Add DiskAHCI.CC to Kernel, add ATAPI capacity read

Export read/write and reset AHCI functions
Create SATARep()
This commit is contained in:
VoidNV
2021-05-26 21:27:49 -05:00
parent 1d1c564760
commit b974147dbf
11 changed files with 636 additions and 56 deletions
+6 -3
View File
@@ -90,7 +90,7 @@ U0 AHCIPortIdentify(CBlkDev *bd)
port->interrupt_status = port->interrupt_status; //TODO: Why?
cmd_header = *&port->cmd_list_base(I64 *); //Read cmd_list_base and cmd_list_base_upper as one full I64 value.
cmd_header = *&port->cmd_list_base(I64 *);
cmd_header += cmd_slot; //Move up pointer to the slot we have in the command list.
//Write Command FIS Length (CFL, a fixed size) in bits 4:0 of the desc. Takes size in U32s.
@@ -513,6 +513,8 @@ U0 AHCIPortInit(CBlkDev *bd, CAHCIPort *port, I64 port_num)
for (i = 0; i < blkdev.cmd_slot_count; i++)
{
cmd_header = &port->cmd_list_base(CPortCmdHeader *)[i];
//Write Command FIS Length (CFL, a fixed size) in bits 4:0 of the desc. Takes size in U32s.
cmd_header->desc = sizeof(CFisH2D) / sizeof(U32);
if (blkdev.ahci64)
{
@@ -613,8 +615,9 @@ U0 Test()
"$$PURPLE$$Byte count: %X$$FG$$\n", AHCIAtapiBlksRead(bd, buf2, 240, BLKS); //read
// "Capacity %d\n", AHCIAtapiCapacityRead(bd);
D(buf2 + (BLKS - 5) * DVD_BLK_SIZE, 5 * DVD_BLK_SIZE); //Dump last 5 blocks
// D(buf2, DVD_BLK_SIZE);
D(bd->dev_id_record, 512);
// D(buf2 + (BLKS - 5) * DVD_BLK_SIZE, 5 * DVD_BLK_SIZE); //Dump last 5 blocks
D(buf2, DVD_BLK_SIZE);
Free(buf);
Free(buf2);
}
+11
View File
@@ -0,0 +1,11 @@
#define BLKS 40
U0 Test()
{
CBlkDev *ata_bd = CAlloc(sizeof(CBlkDev));
CBlkDev *atapi_bd = CAlloc(sizeof(CBlkDev));
AHCIPortInit(ata_bd, &blkdev.ahci_hba->ports[0], 0);
AHCIPortInit(atapi_bd, &blkdev.ahci_hba->ports[1], 1);
}
Test;
+2 -2
View File
@@ -1,10 +1,10 @@
$WW$
- Pushing back internal_buf by doing $FG,2$internal_buf - buf_size$FG$ instead of using a second variable $FG,2$tmp_buf$FG$ might prove dangerous.
- Perhaps make more references to spec in comments
- ATAPI RW
- Remove Buffer alignment check and just do it on every call
$FG,7$ZenithOS IDE DVD Boot function order list and summary$FG$
+24
View File
@@ -0,0 +1,24 @@
I64 SATARep()
{
I64 bdf;
CAHCIPort *port;
CPCIDev *pci;
"AHCI version %X.%1X%1X\n", blkdev.ahci_hba->version >> 16, (blkdev.ahci_hba->version & 0xFF00) >> 8, blkdev.ahci_hba->version & 0xFF;
if (dev.pci_head)
{
pci = PCIDevFind(PCIC_STORAGE, PCISC_AHCI);
ClassRep(pci);
"Bus:0x%02X, Dev:0x%02X, Fun:0x%02X\n\n", pci->bus, pci->dev, pci->fun;
"$$PURPLE$$Vendor$$FG$$: $$BLACK$$%s$$FG$$\n", pci->vendor_str;
"$$PURPLE$$Device$$FG$$: $$BLACK$$%s$$FG$$\n", pci->dev_id_str;
}
else
{
bdf = PCIClassFind(PCIC_STORAGE << 16 | PCISC_AHCI << 8 + 1, 0);
"Bus:%02X, Dev:%02X, Fun:%02X", bdf.u8[2], bdf.u8[1], bdf.u8[0];
"HBA Base Address: 0x%X", PCIReadU32(bdf.u8[2], bdf.u8[1], bdf.u8[0], PCIR_BASE5) & ~0x1F;
}
return 0;
}
SATARep;
+582
View File
@@ -0,0 +1,582 @@
I64 AHCILBA48CapacityGet(U16 *id_record)
{//Get capacity of drive, in LBA blocks.
//Capacity is stored in a U64, so we take the shortcut
//and access the U16-indexed ID record as U64 indexed. Zero-based value.
return (id_record)(U64 *)[ATA_IDENT_LBA48_CAPACITY / 4] - 1;
}
I64 AHCIPortCmdSlotGet(I64 port_num)
{//Get next free command slot in port; if none, throw error.
I64 i;
CAHCIPort *port = &blkdev.ahci_hba->ports[port_num];
U32 slots = port->sata_active | port->cmd_issue;
for (i = 0; i < blkdev.cmd_slot_count; i++)
{
if (!(slots & 1))
return i;
slots >>= 1;
}
ZenithErr("AHCI: No empty command slots on port %d!\n", port_num);
throw('AHCI');
}
Bool AHCIPortIsIdle(I64 port_num)
{//Check if the command engine is running on port.
return !(blkdev.ahci_hba->ports[port_num].command & (AHCI_PxCMDF_ST | AHCI_PxCMDF_CR | AHCI_PxCMDF_FR | AHCI_PxCMDF_FRE));
}
U0 AHCIPortCmdStop(I64 port_num)
{//Stop command engine on port.
CAHCIPort *port = &blkdev.ahci_hba->ports[port_num];
Btr(&port->command, AHCI_PxCMDf_ST);
Btr(&port->command, AHCI_PxCMDf_FRE);
// while (port->command & (AHCI_PxCMDF_CR | AHCI_PxCMDF_FR));
while (Bt(&port->command, AHCI_PxCMDf_CR) || Bt(&port->command, AHCI_PxCMDf_FR));
}
U0 AHCIPortCmdStart(I64 port_num)
{//Start command engine on port.
CAHCIPort *port = &blkdev.ahci_hba->ports[port_num];
while (Bt(&port->command, AHCI_PxCMDf_CR));
Bts(&port->command, AHCI_PxCMDf_FRE);
Bts(&port->command, AHCI_PxCMDf_ST);
}
Bool AHCIPortWait(I64 port_num, F64 timeout, Bool throwing=TRUE)
{//Wait until DRQ & BSY are clear in port task file.
CAHCIPort *port = &blkdev.ahci_hba->ports[port_num];
do
{
if (!(port->task_file_data & (ATAS_DRQ | ATAS_BSY)))
return TRUE;
}
while (timeout > tS);
if (throwing)
{ ZenithErr("AHCI: Port %d hung.\n", port_num);
throw('AHCI');
}
return FALSE;
}
U0 AHCIPortReset(I64 port_num)
{//Software reset of port. Port command engine must be started after this.
//If port is not responsive we do a full reset.
CAHCIPort *port = &blkdev.ahci_hba->ports[port_num];
AHCIPortCmdStop(port_num);
port->interrupt_status = port->interrupt_status; //Acknowledge all interrupt statuses.
if (!AHCIPortWait(port_num, tS + 1))
{//Perform 'more intrusive' HBA<->Port comm reset (sec. 10.4.2 of spec).
port->sata_ctrl = AHCI_PxSCTLF_DET_INIT;
Sleep(2); //Spec says 1 millisecond
port->sata_ctrl = 0;
}
while (port->sata_status & 0xF != AHCI_PxSSTSF_DET_PRESENT);
port->sata_error = ~0; //Write all 1s to sata error register.
}
CPortCmdHeader *AHCIPortActiveHeaderGet(I64 port_num, I64 cmd_slot)
{//Get current command slot header on port.
CAHCIPort *port = &blkdev.ahci_hba->ports[port_num];
CPortCmdHeader *cmd_header = port->cmd_list_base;
return cmd_header + cmd_slot; //Move up pointer to the slot we have in the command list.
}
U0 AHCIPortCmdWait(I64 port_num, I64 cmd_slot)
{//Wait on command completion after command issue, and double check any error.
CAHCIPort *port = &blkdev.ahci_hba->ports[port_num];
while (TRUE)
{
if (!Bt(&port->cmd_issue, cmd_slot)) //When command has been processed
break;
if (Bt(&port->interrupt_status, AHCI_PxIf_TFE)) //Task File Error ($LK,"ATAS_ERR",A="MN:ATAS_ERR"$)
{
error:
ZenithErr("AHCI: Port %d: Command failed!\n", port_num);
throw('AHCI');
}
}
if (Bt(&port->interrupt_status, AHCI_PxIf_TFE)) //Second safety check
goto error;
}
I64 AHCIAtapiCapacityGet(CBlkDev *bd)
{
CPortCmdTable *cmd_table;
CFisH2D *cmd_fis;
CAHCIPort *port = bd->ahci_port;
I64 cmd_slot = AHCIPortCmdSlotGet(bd->port_num);
CPortCmdHeader *cmd_header = AHCIPortActiveHeaderGet(bd->port_num, cmd_slot);
if (port->signature != AHCI_PxSIG_ATAPI)
{
ZenithErr("AHCI: Drive is not an ATAPI drive!\n");
throw('AHCI');
}
U32 *buf = CAlloc(8, Fs->code_heap);
Bts(&cmd_header->desc, AHCI_CH_DESCf_A);
cmd_table = cmd_header->cmd_table_base;
MemSet(cmd_table, 0, sizeof(CPortCmdTable));
//Set up single PRD
cmd_table->prdt[0].data_base = buf;
cmd_table->prdt[0].data_byte_count = DVD_BLK_SIZE - 1; //Zero-based value
cmd_header->prdt_len = 1;
cmd_fis = &cmd_table->cmd_fis;
cmd_fis->type = FISt_H2D;
Bts(&cmd_fis->desc, AHCI_CF_DESCf_C); //Set Command bit in H2D FIS.
cmd_fis->command = ATA_PACKET;
cmd_table->acmd[0] = ATAPI_READ_CAPACITY >> 8;
AHCIPortWait(bd->port_num, tS + 2);
Bts(&port->cmd_issue, cmd_slot); //Issue the command.
try
AHCIPortCmdWait(bd->port_num, cmd_slot);
catch
{
Fs->catch_except = TRUE;
return 0;
}
return EndianU32(buf[0]);
}
U0 AHCIPortIdentify(CBlkDev *bd)
{//Perform ATA_IDENTIFY command on ATA/ATAPI drive and store capacity and id record.
CPortCmdTable *cmd_table;
CFisH2D *cmd_fis;
CAHCIPort *port = bd->ahci_port;
I64 cmd_slot = AHCIPortCmdSlotGet(bd->port_num);
CPortCmdHeader *cmd_header = AHCIPortActiveHeaderGet(bd->port_num, cmd_slot);
port->interrupt_status = port->interrupt_status; //TODO: Why?
//Using the code heap for this alloc to stay under 32-bit address space.
U16 *dev_id_record = CAlloc(512, Fs->code_heap);
cmd_table = cmd_header->cmd_table_base;
MemSet(cmd_table, 0, sizeof(CPortCmdTable));
//Set up single PRD
cmd_table->prdt[0].data_base = dev_id_record;
cmd_table->prdt[0].data_base_upper = 0;
cmd_table->prdt[0].data_byte_count = 512 - 1; //Zero-based value
cmd_header->prdt_len = 1; //1 PRD, as described above, which contains the address to put the ID record.
//Setup command FIS
cmd_fis = &cmd_table->cmd_fis;
cmd_fis->type = FISt_H2D;
Bts(&cmd_fis->desc, AHCI_CF_DESCf_C); //Set Command bit in H2D FIS.
if (port->signature == AHCI_PxSIG_ATAPI)
cmd_fis->command = ATA_IDENTIFY_PACKET;
else
cmd_fis->command = ATA_IDENTIFY;
cmd_fis->device = 0; //No bits need to be set in the device register.
//Wait on previous command to complete.
AHCIPortWait(bd->port_num, tS + 2);
Bts(&port->cmd_issue, cmd_slot); //Issue the command.
AHCIPortCmdWait(bd->port_num, cmd_slot);
Free(bd->dev_id_record);
bd->dev_id_record = dev_id_record;
if (port->signature == AHCI_PxSIG_ATA)
{
bd->max_blk = AHCILBA48CapacityGet(dev_id_record);
"MAX BLOCK %d, disk size %d MiB\n", bd->max_blk, bd->max_blk * BLK_SIZE / 1024 / 1024;
}
else
{
bd->max_blk = AHCIAtapiCapacityGet(bd);
"MAX BLOCK %d, disk size %d MiB\n", bd->max_blk, bd->max_blk * DVD_BLK_SIZE / 1024 / 1024;
}
}
U8 *AHCIBufferAlign(CBlkDev *bd, U8 *user_buf, I64 buf_size, Bool write)
{//Make sure buffer is accessible by HBA controller.
//Controller requires a U16 aligned buffer and in 32-bit address space (We are not using 64-bit capabilities).
//MAlloc provides U64-aligned addresses, and can allocate in the code heap ( <4GB ).
//In the case of an inadequate buffer address being passed in, we will use a MAlloced internal buffer.
if(user_buf + buf_size > U32_MAX || user_buf & 1)
{//if the buffer is not within 32-bit address space or not U16-aligned
"Aligning buffer under 32-bit range\n";
Free(bd->prd_buf);
bd->prd_buf = MAlloc(buf_size, Fs->code_heap);
Bts(&bd->flags, BDf_INTERNAL_BUF);
if (write)
MemCopy(bd->prd_buf, user_buf, buf_size);
return bd->prd_buf;
}
Btr(&bd->flags, BDF_INTERNAL_BUF);
return user_buf;
}
I64 AHCIAtaBlksRW(CBlkDev *bd, U8 *buf, I64 blk, I64 count, Bool write)
{//Read/Write ATA disk blocks. Returns number of bytes transferred between system and disk.
//Don't use this, use the AHCIAtaBlksRead and AHCIAtaBlksWrite functions.
CPortCmdTable *cmd_table;
CFisH2D *cmd_fis;
CAHCIPort *port = bd->ahci_port;
I64 i, buf_size, buf_size_tmp, byte_count, prdt_len, cmd_slot = AHCIPortCmdSlotGet(bd->port_num);
CPortCmdHeader *cmd_header = AHCIPortActiveHeaderGet(bd->port_num, cmd_slot);
U8 *internal_buf_tmp, *internal_buf;
if (port->signature != AHCI_PxSIG_ATA)
{
ZenithErr("AHCI: Drive is not an ATA drive!\n");
throw('AHCI');
}
if (count < 1) return 0;
if (count > AHCI_PRDT_MAX_BLOCKS)
{
ZenithErr("AHCI: Block count %d max allowed in one command (%d)", count, AHCI_PRDT_MAX_BLOCKS);
throw('AHCI');
}
//Determine buffer size and PRDT length.
buf_size = buf_size_tmp = count * BLK_SIZE;
prdt_len = (buf_size - 1) / AHCI_PRD_MAX_BYTES + 1;
"PRDT Length:\t%d\n", prdt_len;
"Count:\t\t\t%d\n", count;
"Buffer size:\t%X\n", buf_size;
cmd_header->prdt_len = prdt_len; //Set PRD table length in cmd header.
//Set 'write' bit depending on 'write' argument.
BEqual(&cmd_header->desc, AHCI_CH_DESCf_W, write);
internal_buf = internal_buf_tmp = AHCIBufferAlign(bd, buf, buf_size, write);
"Buffer:\t\t\t0x%X\n", internal_buf;
if (!internal_buf) throw('AHCI'); //Will probably never happen.
//Obtain command table and zero it. This contains the command FIS and the PRDT.
cmd_table = cmd_header->cmd_table_base;
MemSet(cmd_table, 0, sizeof(CPortCmdTable));
//Create 'prdt_len' amount of PRD entries in the command table
for (i = 0; i < prdt_len; i++)
{//Use max PRD size until the remaining buffer is smaller than max size.
if (buf_size_tmp > AHCI_PRD_MAX_BYTES)
byte_count = AHCI_PRD_MAX_BYTES;
else
byte_count = buf_size_tmp;
"prdt[%d].data_base_addr = 0x%X\n" , i, internal_buf_tmp;
"prdt[%d].data_byte_count = 0x%X\n\n", i, byte_count;
cmd_table->prdt[i].data_base = internal_buf_tmp;
cmd_table->prdt[i].data_byte_count = byte_count - 1; //Zero-based value
buf_size_tmp -= byte_count;
internal_buf_tmp += byte_count;
}
//Setup the command FIS.
cmd_fis = &cmd_table->cmd_fis;
cmd_fis->type = FISt_H2D;
Bts(&cmd_fis->desc, AHCI_CF_DESCf_C); //Set Command bit in H2D FIS
if (write) //Assumed support for LBA48.
cmd_fis->command = ATA_WRITE_DMA_EXT;
else
cmd_fis->command = ATA_READ_DMA_EXT;
//Fill in the rest of the command FIS.
cmd_fis->lba0 = blk.u8[0];
cmd_fis->lba1 = blk.u8[1];
cmd_fis->lba2 = blk.u8[2];
cmd_fis->device = 1 << 6; //Required as per ATA8-ACS section 7.25.3
cmd_fis->lba3 = blk.u8[3];
cmd_fis->lba4 = blk.u8[4];
cmd_fis->lba5 = blk.u8[5];
cmd_fis->count = count;
//Wait on previous command to complete.
AHCIPortWait(bd->port_num, tS + 2);
//Issue the command.
Bts(&port->cmd_issue, cmd_slot);
//Wait on command to finish.
AHCIPortCmdWait(bd->port_num, cmd_slot);
if (!write) //If internal_buf was created it back to argument buf.
if (bd->flags & BDF_INTERNAL_BUF)
{
"Writing back internal buffer\n";
MemCopy(buf, internal_buf, buf_size);
}
return cmd_header->prd_byte_count;
}
I64 AHCIAtaBlksRead(CBlkDev *bd, U8 *buf, I64 blk, I64 count)
{//Read 'blk' amount of blocks from AHCI disk device. Returns num of bytes transferred between disk and memory.
I64 byte_count = 0;
if (!count)
return 0;
if (count <= AHCI_PRDT_MAX_BLOCKS)
{
"$$GREEN$$READ less than MAX_BLOCKS$$FG$$\n";
return AHCIAtaBlksRW(bd, buf, blk, count, FALSE);
}
else
{
"$$GREEN$$READ greater than MAX_BLOCKS\n";
"read count: %d\n$$FG$$", count;
while (count > AHCI_PRDT_MAX_BLOCKS)
{
byte_count += AHCIAtaBlksRW(bd, buf, blk, AHCI_PRDT_MAX_BLOCKS, FALSE);
count -= AHCI_PRDT_MAX_BLOCKS;
blk += AHCI_PRDT_MAX_BLOCKS;
buf += AHCI_PRDT_MAX_BLOCKS * BLK_SIZE;
"$$GREEN$$read count: %d\n$$FG$$", count;
}$ER$
byte_count += AHCIAtaBlksRW(bd, buf, blk, count, FALSE);
}
return byte_count;
}
I64 AHCIAtaBlksWrite(CBlkDev *bd, U8 *buf, I64 blk, I64 count)
{//Write 'blk' amount of blocks to AHCI disk device. Returns num of bytes transferred between memory and disk.
I64 byte_count = 0;
if (!count)
return 0;
if (count <= AHCI_PRDT_MAX_BLOCKS)
{
"$$GREEN$$WRITE less than MAX_BLOCKS$$FG$$\n";
return AHCIAtaBlksRW(bd, buf, blk, count, TRUE);
}
else
{
"$$GREEN$$WRITE greater than MAX_BLOCKS\n";
"write count: %d$$FG$$\n", count;
while (count > AHCI_PRDT_MAX_BLOCKS)
{
byte_count += AHCIAtaBlksRW(bd, buf, blk, AHCI_PRDT_MAX_BLOCKS, TRUE);
count -= AHCI_PRDT_MAX_BLOCKS;
blk += AHCI_PRDT_MAX_BLOCKS;
buf += AHCI_PRDT_MAX_BLOCKS * BLK_SIZE;
"$$GREEN$$write count: %d\n$$FG$$\n", count;
}
byte_count += AHCIAtaBlksRW(bd, buf, blk, count, TRUE);
}
return byte_count;
}
I64 AHCIAtapiBlksRead(CBlkDev *bd, U8 *buf, I64 blk, I64 count)
{//Read 'blk' amount of blocks from from AHCI ATAPI device. Returns num of bytes transferred.
CPortCmdTable *cmd_table;
CFisH2D *cmd_fis;
CAHCIPort *port = bd->ahci_port;
I64 i, byte_count, buf_size, buf_size_tmp, prdt_len, cmd_slot = AHCIPortCmdSlotGet(bd->port_num);
CPortCmdHeader *cmd_header = AHCIPortActiveHeaderGet(bd->port_num, cmd_slot);
U8 *internal_buf, *internal_buf_tmp;
if (port->signature != AHCI_PxSIG_ATAPI)
{
ZenithErr("AHCI: Drive is not an ATAPI drive!\n");
throw('AHCI');
}
if (count < 1)
return 0;
buf_size = buf_size_tmp = count * DVD_BLK_SIZE;
prdt_len = (buf_size - 1) / AHCI_PRD_MAX_BYTES + 1;
"PRDT Length:\t%d\n", prdt_len;
"Count:\t\t\t%d\n", count;
"Buffer size:\t%X\n", buf_size;
cmd_header->prdt_len = prdt_len;
internal_buf = internal_buf_tmp = AHCIBufferAlign(bd, buf, buf_size, FALSE);
"Buffer:\t\t\t0x%X\n", internal_buf;
if (!internal_buf) throw('AHCI');
Bts(&cmd_header->desc, AHCI_CH_DESCf_A); //Set ATAPI flag in command header
cmd_table = cmd_header->cmd_table_base;
MemSet(cmd_table, 0, sizeof(CPortCmdTable));
for (i = 0; i < prdt_len; i++)
{
if (buf_size_tmp > AHCI_PRD_MAX_BYTES) //$BK,1$SHOULD PROBABLY BE ATAPI MAX BYTES$BK,0$
byte_count = AHCI_PRD_MAX_BYTES;
else
byte_count = buf_size_tmp;
"prdt[%d].data_base_addr = 0x%X\n" , i, internal_buf_tmp;
"prdt[%d].data_byte_count = 0x%X\n\n", i, byte_count;
cmd_table->prdt[i].data_base = internal_buf_tmp;
cmd_table->prdt[i].data_byte_count = byte_count - 1; //Zero-based value
buf_size_tmp -= byte_count;
internal_buf_tmp += byte_count;
}
cmd_fis = &cmd_table->cmd_fis;
MemSet(cmd_fis, 0, sizeof(CFisH2D));
cmd_fis->type = FISt_H2D;
Bts(&cmd_fis->desc, AHCI_CF_DESCf_C); //Set Command bit in H2D FIS
cmd_fis->feature_low = 1; //Necessary?
cmd_fis->command = ATA_PACKET;
CAtapiReadCmd read_cmd;
MemSet(&read_cmd, 0, sizeof(CAtapiReadCmd));
read_cmd.command = ATAPI_READ >> 8; //$BK,1$FIX$BK,0$
read_cmd.lba = EndianU32(blk);
read_cmd.count = EndianU32(count);
MemCopy(&cmd_table->acmd, &read_cmd, 16);
cmd_fis->count = count; //Necessary?
AHCIPortWait(bd->port_num, tS + 2);
Bts(&port->cmd_issue, cmd_slot);
// Sleep(1000);
AHCIPortCmdWait(bd->port_num, cmd_slot);
if (bd->flags & BDF_INTERNAL_BUF)
{
"Writing back internal buffer\n";
MemCopy(buf, internal_buf, buf_size);
}
return cmd_header->prd_byte_count;
}
U0 AHCIPortInit(CBlkDev *bd, CAHCIPort *port, I64 port_num)
{//Initialize base addresses for command list and FIS receive area and start command execution on port.
CPortCmdHeader *cmd_header;
I64 i;
bd->ahci_port = port;
bd->port_num = port_num;
AHCIPortReset(port_num);
AHCIPortCmdStart(port_num);
//Spin up, power on device. If the capability isn't suppport the bits will be read-only and this won't do anything.
port->command |= AHCI_PxCMDF_POD | AHCI_PxCMDF_SUD;
Sleep(100); //Why?
AHCIPortCmdStop(port_num);
//'1K-byte' align as per SATA spec.
port->cmd_list_base = CAllocAligned(sizeof(CPortCmdHeader) * blkdev.cmd_slot_count, 1024, Fs->code_heap);
port->cmd_list_base_upper = 0;
//Alloc where received FISes will be copied to. '256-byte' align as per spec.
port->fis_base = CAllocAligned(sizeof(CFisReceived), 256, Fs->code_heap);
port->fis_base_upper = 0;
for (i = 0; i < blkdev.cmd_slot_count; i++)
{
cmd_header = &port->cmd_list_base(CPortCmdHeader *)[i];
//Write Command FIS Length (CFL, a fixed size) in bits 4:0 of the desc. Takes size in U32s.
cmd_header->desc = sizeof(CFisH2D) / sizeof(U32);
//'128-byte' align as per SATA spec, minus 1 since length is 1-based.
cmd_header->cmd_table_base = CAllocAligned(sizeof(CPortCmdTable) + sizeof(CPrdtEntry) * (AHCI_PRDT_MAX_LEN - 1), 128, Fs->code_heap);
cmd_header->cmd_table_base_upper = 0;
}
AHCIPortCmdStart(port_num);
AHCIPortIdentify(bd);
}
U0 AHCIHbaReset()
{
Bts(&blkdev.ahci_hba->ghc, AHCI_GHCf_HBA_RESET);
while (Bt(&blkdev.ahci_hba->ghc, AHCI_GHCf_HBA_RESET));
Bts(&blkdev.ahci_hba->ghc, AHCI_GHCf_AHCI_ENABLE);
}
U0 AHCIInit()
{
CAHCIHba *hba;
CAHCIPort *port;
I64 i, bdf = PCIClassFind(PCIC_STORAGE << 16 | PCISC_AHCI << 8 + 1, 0); //0x010601, last byte prog_if, AHCI version 1.0
if (bdf == -1)
{
"No AHCI controller found.\n";
return;
}
"AHCI: controller found\n";
hba = dev.uncached_alias + PCIReadU32(bdf.u8[2], bdf.u8[1], bdf.u8[0], PCIR_BASE5) & ~0x1F; //Last 4 bits not part of address.
blkdev.ahci_hba = hba;
AHCIHbaReset;
"AHCI: HBA reset\n";
//Transferring ownership from BIOS if supported.
if (Bt(&hba->caps_ext, AHCI_CAPSEXTf_BOH))
{
Bt(&hba->bohc, AHCI_BOHCf_OOS);
while (Bt(&hba->bohc, AHCI_BOHCf_BOS));
Sleep(25);
if (Bt(&hba->bohc, AHCI_BOHCf_BB)) //if Bios Busy is still set after 25 mS, wait 2 seconds.
Sleep(2000);
}
blkdev.cmd_slot_count = (hba->caps & 0x1F00) >> 8;
"AHCI: cmd slot count: %d\n", blkdev.cmd_slot_count;
for (i = 0; i < AHCI_MAX_PORTS; i++)
{
if (Bt(&hba->ports_implemented, i))
{//$BK,1$Make ports idle?$BK,0$
port = &hba->ports[i];
if (port->signature == AHCI_PxSIG_ATA || port->signature == AHCI_PxSIG_ATAPI)
{
"AHCI: port on %d\n", i;
if (port->signature == AHCI_PxSIG_ATAPI)
Bts(&port->command, AHCI_PxCMDf_ATAPI);
if (!AHCIPortIsIdle(i))
{
"AHCI: Port not idle\n";
AHCIPortCmdStop(i);
}
}
}
}
}
/*
AHCIInit;
#define BLKS 65537
U0 rw()
{
U8 *buf = MAlloc(BLKS * BLK_SIZE);
U8 *buf2 = MAlloc(BLKS * DVD_BLK_SIZE);
U8 *buf3 = CAlloc(BLKS * DVD_BLK_SIZE);
MemSet(buf, 0xFF, BLKS * BLK_SIZE);
CBlkDev *bd = CAlloc(sizeof(CBlkDev));
CBlkDev *bd2 = CAlloc(sizeof(CBlkDev));
// AHCIPortInit(bd, &blkdev.ahci_hba->ports[0], 0);
AHCIPortInit(bd2, &blkdev.ahci_hba->ports[1], 1);
// "$$PURPLE$$Byte count: %X$$FG$$\n", AHCIAtaBlksWrite(bd, buf, 0, BLKS); //write
// "$$PURPLE$$Byte count: %X$$FG$$\n", AHCIAtaBlksRead(bd, buf2, 0, BLKS);//read
// "$$PURPLE$$Byte count: %X$$FG$$\n", AHCIAtapiBlksRead(bd2, buf3, 0, BLKS);//read
// D(buf2 + (BLKS - 5) * BLK_SIZE, 5 * BLK_SIZE);
"Capacity: %d\n", AHCIAtapiCapacityGet(bd2);
// D(buf3 + DVD_BLK_SIZE * (BLKS - 5), 5 * DVD_BLK_SIZE);
Free(buf);
Free(buf2);
Free(buf3);
Free(bd);
Free(bd2);
}
rw;
*/
-50
View File
@@ -38,56 +38,6 @@ Bool BootDVDProbeAll(CBlkDev *bd)
{
I64 d1, d2, i, j, k;
/* AHCI Prototyping */
CAHCIHba *hba;
CAHCIPort *port;
#exe {Option(OPTf_WARN_DUP_TYPES, OFF);};
I64 bdf = PCIClassFind(PCIC_STORAGE << 16 | PCISC_AHCI << 8 + 1, 0);
#exe {Option(OPTf_WARN_DUP_TYPES, ON);};
if (bdf != -1)
{
"\nAHCI is not yet supported.\n\n";
hba = dev.uncached_alias + PCIReadU32(bdf.u8[2], bdf.u8[1], bdf.u8[0], PCIR_BASE5) & ~0x1F;
Bts(&hba->ghc, AHCI_GHCf_HBA_RESET);
while (Bt(&hba->ghc, AHCI_GHCf_HBA_RESET));
Bts(&hba->ghc, AHCI_GHCf_AHCI_ENABLE);
if (Bt(&hba->caps_ext, AHCI_CAPSEXTf_BOH))
{
"Transferring AHCI ownership from BIOS.\n\n";
Bt(&hba->bohc, AHCI_BOHCf_OOS);
while (Bt(&hba->bohc, AHCI_BOHCf_BOS));
Sleep(25);
if (Bt(&hba->bohc, AHCI_BOHCf_BB)) //if Bios Busy is still set after 25 mS, wait 2 seconds.
Sleep(2000);
}
blkdev.ahci64 = Bt(&hba->caps, AHCI_CAPSf_S64A);
blkdev.cmd_slot_count = (hba->caps & 0x1F00) >> 8;
blkdev.ahci_hba = hba;
"ahci64: %Z\n", blkdev.ahci64, "ST_FALSE_TRUE";
"ahci cmd slots: %d\n\n", blkdev.cmd_slot_count;
for (i = 0; i < AHCI_MAX_PORTS; i++)
{
if (Bt(&hba->ports_implemented, i))
{
"AHCI Port %d implemented.\n", i;
port = &hba->ports[i];
"Port Signature: 0x%08X\n", port->signature;
if (port->signature == AHCI_PxSIG_ATAPI)
"ATAPI drive detected on AHCI Port %d.\n", i;
else if (port->signature == AHCI_PxSIG_ATA)
"ATA drive detected on AHCI Port %d.\n", i;
"\n";
}
}
"Reboot with IDE Compatibility mode set in the BIOS.\n";
SysHlt;
}
/********************/
bd->base1 = 0;
for (k = 0; k < 256; k++)
{
+2
View File
@@ -266,6 +266,8 @@ U0 BlkDevsInitAll()
for (i = 0; i < DRIVES_NUM; i++)
blkdev.let_to_drive[i] = &blkdev.drvs[i];
AHCIInit;
#exe {
if (kernel_config->opts[CONFIG_MOUNT_IDE_AUTO])
StreamPrint("MountIDEAuto;");
+1
View File
@@ -1,6 +1,7 @@
#exe {Cd(__DIR__);};
#include "DiskStrA"
#include "DiskCache"
#include "DiskAHCI"
#include "DiskATA"
#include "DiskATAId"
#include "DiskBlkDev"
+1 -1
View File
@@ -3398,8 +3398,8 @@ public class CBlkDevGlobals
ins_base0,
ins_base1; //Install cd/dvd controller.
Bool dvd_boot_is_good,
ins_unit,
ahci64,
ins_unit,
pad[2];
};
+7
View File
@@ -359,6 +359,13 @@ extern I64 BlkDevAdd( CBlkDev *bd, I64 prt_num=I64_MIN, Bool whole_drive
extern U0 DiskCacheInit(I64 size_in_U8s);
public extern U0 DiskCacheInvalidate(CDrive *drive);
public extern I64 MountIDEAuto();
extern U0 AHCIHbaReset();
extern U0 AHCIPortReset(I64 port_num);
extern U0 AHCIPortInit(CBlkDev *bd, CAHCIPort *port, I64 port_num);
extern I64 AHCIAtaBlksRW( CBlkDev *bd, U8 *buf, I64 blk, I64 count, Bool write);
extern I64 AHCIAtaBlksRead( CBlkDev *bd, U8 *buf, I64 blk, I64 count);
extern I64 AHCIAtaBlksWrite( CBlkDev *bd, U8 *buf, I64 blk, I64 count);
extern I64 AHCIAtapiBlksRead(CBlkDev *bd, U8 *buf, I64 blk, I64 count);
public extern CBlkDevGlobals blkdev;
#help_index "Graphics/Color"