diff --git a/Zenith-latest-2021-05-26-03_17_44.iso b/Zenith-latest-2021-05-26-21_25_20.iso similarity index 99% rename from Zenith-latest-2021-05-26-03_17_44.iso rename to Zenith-latest-2021-05-26-21_25_20.iso index 63c1c9f..1bcf273 100644 Binary files a/Zenith-latest-2021-05-26-03_17_44.iso and b/Zenith-latest-2021-05-26-21_25_20.iso differ diff --git a/src/Home/AHCI/ahci.CC b/src/Home/AHCI/ahci.CC index 77171c7..59f6673 100755 --- a/src/Home/AHCI/ahci.CC +++ b/src/Home/AHCI/ahci.CC @@ -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); } diff --git a/src/Home/AHCI/ahcifunctions.CC b/src/Home/AHCI/ahcifunctions.CC new file mode 100644 index 0000000..f7baba0 --- /dev/null +++ b/src/Home/AHCI/ahcifunctions.CC @@ -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; \ No newline at end of file diff --git a/src/Home/AHCI/notes.DD b/src/Home/AHCI/notes.DD index 8d2e64b..0575d4a 100755 --- a/src/Home/AHCI/notes.DD +++ b/src/Home/AHCI/notes.DD @@ -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$ diff --git a/src/Home/satarep.CC b/src/Home/satarep.CC new file mode 100644 index 0000000..9555ebb --- /dev/null +++ b/src/Home/satarep.CC @@ -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; \ No newline at end of file diff --git a/src/Kernel/BlkDev/DiskAHCI.CC b/src/Kernel/BlkDev/DiskAHCI.CC new file mode 100644 index 0000000..fcae9a0 --- /dev/null +++ b/src/Kernel/BlkDev/DiskAHCI.CC @@ -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; +*/ \ No newline at end of file diff --git a/src/Kernel/BlkDev/DiskATAId.CC b/src/Kernel/BlkDev/DiskATAId.CC index 5a7f664..c2beda6 100755 --- a/src/Kernel/BlkDev/DiskATAId.CC +++ b/src/Kernel/BlkDev/DiskATAId.CC @@ -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++) { diff --git a/src/Kernel/BlkDev/DiskAddDev.CC b/src/Kernel/BlkDev/DiskAddDev.CC index 6afafe8..442152b 100755 --- a/src/Kernel/BlkDev/DiskAddDev.CC +++ b/src/Kernel/BlkDev/DiskAddDev.CC @@ -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;"); diff --git a/src/Kernel/BlkDev/MakeBlkDev.CC b/src/Kernel/BlkDev/MakeBlkDev.CC index 9018126..343432a 100755 --- a/src/Kernel/BlkDev/MakeBlkDev.CC +++ b/src/Kernel/BlkDev/MakeBlkDev.CC @@ -1,6 +1,7 @@ #exe {Cd(__DIR__);}; #include "DiskStrA" #include "DiskCache" +#include "DiskAHCI" #include "DiskATA" #include "DiskATAId" #include "DiskBlkDev" diff --git a/src/Kernel/KernelA.HH b/src/Kernel/KernelA.HH index cd67057..793da9e 100755 --- a/src/Kernel/KernelA.HH +++ b/src/Kernel/KernelA.HH @@ -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]; }; diff --git a/src/Kernel/KernelC.HH b/src/Kernel/KernelC.HH index 41e7592..eca10da 100755 --- a/src/Kernel/KernelC.HH +++ b/src/Kernel/KernelC.HH @@ -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"