diff options
| author | Amlal <amlalelmahrouss@icloud.com> | 2024-12-21 21:59:13 +0100 |
|---|---|---|
| committer | Amlal <amlalelmahrouss@icloud.com> | 2024-12-21 21:59:45 +0100 |
| commit | 610f91d87152cbe48d3054fcf437d8239da6ef35 (patch) | |
| tree | a386f7047ab73d088169ab2371ddc6ffe8020f1c /dev/Kernel/HALKit/AMD64/Storage | |
| parent | 509fcca5986651c8ba712fb395f8498f2dea4109 (diff) | |
IMP: :boom: Breaking changes some checks are needed to be done.
Signed-off-by: Amlal <amlalelmahrouss@icloud.com>
Diffstat (limited to 'dev/Kernel/HALKit/AMD64/Storage')
| -rw-r--r-- | dev/Kernel/HALKit/AMD64/Storage/AHCI-DMA.cc | 319 | ||||
| -rw-r--r-- | dev/Kernel/HALKit/AMD64/Storage/ATA-DMA.cc | 38 | ||||
| -rw-r--r-- | dev/Kernel/HALKit/AMD64/Storage/ATA-PIO.cc | 197 |
3 files changed, 554 insertions, 0 deletions
diff --git a/dev/Kernel/HALKit/AMD64/Storage/AHCI-DMA.cc b/dev/Kernel/HALKit/AMD64/Storage/AHCI-DMA.cc new file mode 100644 index 00000000..e86bfde7 --- /dev/null +++ b/dev/Kernel/HALKit/AMD64/Storage/AHCI-DMA.cc @@ -0,0 +1,319 @@ +/* ------------------------------------------- + + Copyright (C) 2024, TQ B.V, all rights reserved. + +------------------------------------------- */ + +/** + * @file AHCI.cc + * @author TQ B.V (amlalelmahrouss@icloud.com) + * @brief AHCI driver. + * @version 0.1 + * @date 2024-02-02 + * + * @Copyright (C) 2024, TQ B.V, all rights reserved. + * + */ + +#include <KernelKit/UserProcessScheduler.h> +#include <KernelKit/LPC.h> + +#include <Modules/ATA/ATA.h> +#include <Modules/AHCI/AHCI.h> +#include <KernelKit/PCI/Iterator.h> +#include <NewKit/Utils.h> +#include <KernelKit/LockDelegate.h> + +#ifdef __AHCI__ + +#define HBA_ERR_TFE (1 << 30) +#define HBA_PxCMD_ST 0x0001 +#define HBA_PxCMD_FRE 0x0010 +#define HBA_PxCMD_FR 0x4000 +#define HBA_PxCMD_CR 0x8000 + +#define kAhciStartAddress mib_cast(4) + +#define kAhciLBAMode (1 << 6) + +#define kAhciMaxPoll (100000U) + +#define kCmdOrCtrlCmd 1 +#define kCmdOrCtrlCtrl 0 + +#define kAhciSRBsy 0x80 +#define kAhciSRDrq 0x08 + +enum +{ + kSATAProgIfAHCI = 0x01, + kSATASubClass = 0x06, + kSATABar5 = 0x24, +}; + +STATIC Kernel::PCI::Device kAhciDevice; +STATIC HbaPort* kAhciPort = nullptr; +STATIC Kernel::Lba kCurrentDiskSectorCount = 0UL; + +Kernel::Void drv_calculate_disk_geometry() +{ + kCurrentDiskSectorCount = 0UL; + kcout << "Highest AHCI LBA (48-bit): " << Kernel::number(kCurrentDiskSectorCount) << endl; +} + +/// @brief Initializes an AHCI disk. +/// @param PortsImplemented the amount of kAhciPort that have been detected. +/// @return if the disk was successfully initialized or not. +Kernel::Boolean drv_std_init(Kernel::UInt16& PortsImplemented) +{ + using namespace Kernel; + + PCI::Iterator iterator(Types::PciDeviceKind::MassStorageController); + + for (SizeT device_index = 0; device_index < ZKA_BUS_COUNT; ++device_index) + { + kAhciDevice = iterator[device_index].Leak(); // And then leak the reference. + + // if SATA and then interface is AHCI... + if (kAhciDevice.Subclass() == kSATASubClass && + kAhciDevice.ProgIf() == kSATAProgIfAHCI) + { + kAhciDevice.EnableMmio(0x24); // Enable the memory index_byte/o for this ahci device. + kAhciDevice.BecomeBusMaster(0x24); // Become bus master for this ahci device, so that we can control it. + + HbaMem* mem_ahci = (HbaMem*)kAhciDevice.Bar(0x24); + + Kernel::UInt32 ports_implemented = mem_ahci->Pi; + Kernel::UInt16 ahci_index = 0; + + const Kernel::UInt16 kMaxPortsImplemented = 32; + const Kernel::UInt32 kSATASignature = 0x00000101; + const Kernel::UInt8 kAhciPresent = 0x03; + const Kernel::UInt8 kAhciIPMActive = 0x01; + + Kernel::Boolean detected = false; + + while (ahci_index < kMaxPortsImplemented) + { + if (ports_implemented) + { + kcout << "Port is implemented by host.\r"; + + Kernel::UInt8 ipm = (mem_ahci->Ports[ahci_index].Ssts >> 8) & 0x0F; + Kernel::UInt8 det = mem_ahci->Ports[ahci_index].Ssts & 0x0F; + + if (mem_ahci->Ports[ahci_index].Sig == kSATASignature) + { + kcout << "Port is AHCI controller.\r"; + + detected = true; + + kAhciPort = &mem_ahci->Ports[ahci_index]; + + kAhciPort->Cmd &= ~HBA_PxCMD_FRE; + + // Clear FRE (bit4) + kAhciPort->Cmd &= ~HBA_PxCMD_ST; + + // Wait until FR (bit14), CR (bit15) are cleared + while (YES) + { + if (kAhciPort->Cmd & HBA_PxCMD_CR) + continue; + + if (kAhciPort->Cmd & HBA_PxCMD_FR) + continue; + break; + } + + // when it's stopped. + + // do in-between + + kAhciPort->Clb = kAhciStartAddress + (ahci_index << 10); + kAhciPort->Clbu = 0; + rt_set_memory((Kernel::VoidPtr)((Kernel::UIntPtr)kAhciPort->Clb + kAhciPort->Clbu), 0, 1024); + + // FIS offset: 32K+256*ahci_index + // FIS entry size = 256 bytes per port + kAhciPort->Fb = kAhciStartAddress + (32 << 10) + (ahci_index << 8); + kAhciPort->Fbu = 0; + rt_set_memory((Kernel::VoidPtr)((Kernel::UIntPtr)kAhciPort->Fb + kAhciPort->Fbu), 0, 256); + + // Command table offset: 40K + 8K*ahci_index + // Command table size = 256*32 = 8K per port + HbaCmdHeader* cmd_header = (HbaCmdHeader*)((Kernel::UIntPtr)kAhciPort->Clb + kAhciPort->Clbu); + + for (SizeT i = 0; i < 32; i++) + { + cmd_header[i].Prdtl = 8; // 8 prdt entries per command table + // 256 bytes per command table, 64+16+48+16*8 + // Command table offset: 40K + 8K*ahci_index + cmdheader_index*256 + cmd_header[i].Ctba = kAhciStartAddress + (40 << 10) + (ahci_index << 13) + (i << 8); + cmd_header[i].Ctbau = 0; + + rt_set_memory((VoidPtr)((Kernel::UIntPtr)cmd_header[i].Ctba + cmd_header[i].Ctbau), 0, 256); + } + + // when it's starting + + // check for bits again, to start it again. + while (YES) + { + if (kAhciPort->Cmd & HBA_PxCMD_FR) + continue; + + break; + } + + kAhciPort->Cmd |= HBA_PxCMD_FRE; + kAhciPort->Cmd |= HBA_PxCMD_ST; + + drv_calculate_disk_geometry(); + + break; + } + } + + ports_implemented >>= 1; + ++ahci_index; + } + + return detected; + } + } + + return No; +} + +Kernel::Boolean drv_std_detected(Kernel::Void) +{ + return kAhciDevice.DeviceId() != 0xFFFF; +} + +Kernel::Void drv_std_write(Kernel::UInt64 lba, Kernel::Char* buffer, Kernel::SizeT sector_cnt, Kernel::SizeT size_buffer) +{ +} + +Kernel::Void drv_std_read(Kernel::UInt64 lba, Kernel::Char* buffer, Kernel::SizeT sector_cnt, Kernel::SizeT size_buffer) +{ + kAhciPort->Is = -1; + + Kernel::SizeT port = 0; + Kernel::UInt32 slots = (kAhciPort->Sact | kAhciPort->Ci); + + for (; port < slots; ++port) + { + if ((slots & 1) == 0) + break; + + slots >>= 1; + } + + HbaCmdHeader* cmd_hdr = (HbaCmdHeader*)((Kernel::UIntPtr)kAhciPort->Clb + kAhciPort->Clbu); + + cmd_hdr += port; + cmd_hdr->Cfl = sizeof(FisRegH2D) / sizeof(Kernel::UInt32); + cmd_hdr->Write = NO; + cmd_hdr->Prdtl = (Kernel::UInt16)((sector_cnt - 1) >> 4) + 1; + + HbaCmdTbl* cmd_tbl = (HbaCmdTbl*)((Kernel::UIntPtr)cmd_hdr->Ctba + cmd_hdr->Ctbau); + Kernel::rt_set_memory(cmd_tbl, 0, (cmd_hdr->Prdtl - 1) * sizeof(HbaPrdtEntry)); + + Kernel::SizeT i = 0; + + for (Kernel::SizeT i = 0; i < cmd_hdr->Prdtl - 1; i++) + { + cmd_tbl->PrdtEntries[i].Dba = (Kernel::UInt32)(Kernel::UInt64)buffer; + cmd_tbl->PrdtEntries[i].Dbau = (Kernel::UInt32)((Kernel::UInt64)(buffer) >> 32); + cmd_tbl->PrdtEntries[i].Dbc = size_buffer - 1; // 8K bytes (this value should always be set to 1 less than the actual value) + cmd_tbl->PrdtEntries[i].InterruptBit = 1; + } + + cmd_tbl->PrdtEntries[i].Dba = (Kernel::UInt32)(Kernel::UInt64)buffer; + cmd_tbl->PrdtEntries[i].Dbau = (Kernel::UInt32)((Kernel::UInt64)(buffer) >> 32); + cmd_tbl->PrdtEntries[i].Dbc = size_buffer - 1; // 8K bytes (this value should always be set to 1 less than the actual value) + cmd_tbl->PrdtEntries[i].InterruptBit = 1; + + FisRegH2D* cmd_fis = (FisRegH2D*)(&cmd_tbl->Cfis); + + cmd_fis->FisType = kFISTypeRegH2D; + cmd_fis->CmdOrCtrl = YES; // Command + cmd_fis->Command = kAHCICmdReadDmaEx; + + cmd_fis->Lba0 = (Kernel::UInt8)(Kernel::UInt32)lba & 0xFF; + cmd_fis->Lba1 = (Kernel::UInt8)((Kernel::UInt32)lba >> 8); + cmd_fis->Lba2 = (Kernel::UInt8)((Kernel::UInt32)lba >> 16); + cmd_fis->Device = kAhciLBAMode; // LBA mode + + cmd_fis->Lba3 = (Kernel::UInt8)((Kernel::UInt32)lba >> 24); + cmd_fis->Lba4 = (Kernel::UInt8)(lba >> 32); + cmd_fis->Lba5 = (Kernel::UInt8)((lba >> 32) >> 8); + + cmd_fis->CountLow = sector_cnt & 0xFF; + cmd_fis->CountHigh = (sector_cnt >> 8) & 0xFF; + + Kernel::UInt64 spin = 0UL; + + // The below loop waits until the port is no longer busy before issuing a new command + while ((kAhciPort->Tfd & (kAhciSRBsy | kAhciSRDrq)) && spin < kAhciMaxPoll) + { + spin++; + } + if (spin == 1000000) + { + kcout << "AHCI: Port is hung.\r"; + return; + } + + kAhciPort->Ci = 1 << port; // Issue command + + // Wait for completion + while (YES) + { + // In some longer duration reads, it may be helpful to spin on the DPS bit + // in the PxIS port field as well (1 << 5) + if ((kAhciPort->Ci & (1 << port)) == 0) + break; + if (kAhciPort->Is & HBA_ERR_TFE) // Task file error + { + using namespace Kernel; + kcout << ("AHCI: Read disk error.\r"); + + err_global_get() = kErrorUnrecoverableDisk; + + return; + } + } + + // Check again for the last time. + if (kAhciPort->Is & HBA_ERR_TFE) // task file error status + { + using namespace Kernel; + + kcout << ("AHCI: Read disk error.\r"); + *buffer = 0; + + err_global_get() = kErrorUnrecoverableDisk; + + return; + } +} + +/*** + @brief Gets the number of sectors inside the drive. + @return Sector size in bytes. + */ +Kernel::SizeT drv_get_sector_count() +{ + return kCurrentDiskSectorCount; +} + +/// @brief Get the drive size. +/// @return Disk size in bytes. +Kernel::SizeT drv_get_size() +{ + return drv_get_sector_count() * kAHCISectorSize; +} + +#endif // ifdef __AHCI__ diff --git a/dev/Kernel/HALKit/AMD64/Storage/ATA-DMA.cc b/dev/Kernel/HALKit/AMD64/Storage/ATA-DMA.cc new file mode 100644 index 00000000..d1751105 --- /dev/null +++ b/dev/Kernel/HALKit/AMD64/Storage/ATA-DMA.cc @@ -0,0 +1,38 @@ +/* ------------------------------------------- + + Copyright (C) 2024, TQ B.V, all rights reserved. + +------------------------------------------- */ + +/** + * @file ATA-DMA.cc + * @author TQ B.V (amlalelmahrouss@icloud.com) + * @brief ATA driver (DMA mode). + * @version 0.1 + * @date 2024-02-02 + * + * @copyright Copyright (c) TQ B.V + * + */ + +#include <StorageKit/PRDT.h> + +#include <Modules/ATA/ATA.h> +#include <ArchKit/ArchKit.h> + +using namespace Kernel; + +EXTERN_C Int32 kPRDTTransferStatus; +STATIC PRDT kPRDT; + +#ifdef __ATA_DMA__ + +#ifdef __ATA_PIO__ +#error !!! You cant have both PIO and DMA enabled! !!! +#endif /* ifdef __ATA_PIO__ */ + +#ifdef __AHCI__ +#error !!! You cant have both ATA and AHCI enabled! !!! +#endif /* ifdef __AHCI__ */ + +#endif /* ifdef __ATA_DMA__ */ diff --git a/dev/Kernel/HALKit/AMD64/Storage/ATA-PIO.cc b/dev/Kernel/HALKit/AMD64/Storage/ATA-PIO.cc new file mode 100644 index 00000000..678d65b7 --- /dev/null +++ b/dev/Kernel/HALKit/AMD64/Storage/ATA-PIO.cc @@ -0,0 +1,197 @@ +/* ------------------------------------------- + + Copyright (C) 2024, TQ B.V, all rights reserved. + +------------------------------------------- */ + +/** + * @file ATA-PIO.cc + * @author TQ B.V (amlalelmahrouss@icloud.com) + * @brief ATA driver (PIO mode). + * @version 0.1 + * @date 2024-02-02 + * + * @copyright Copyright (c) TQ B.V + * + */ + +#include <Modules/ATA/ATA.h> +#include <ArchKit/ArchKit.h> + +#ifdef __ATA_PIO__ + +using namespace Kernel; +using namespace Kernel::HAL; + +/// bugs: 0 + +#define kATADataLen 256 + +STATIC Boolean kATADetected = false; +STATIC Int32 kATADeviceType = kATADeviceCount; +STATIC Char kATAData[kATADataLen] = {0}; + +Boolean drv_std_wait_io(UInt16 IO) +{ + for (int i = 0; i < 400; i++) + rt_in8(IO + ATA_REG_STATUS); + +ATAWaitForIO_Retry: + auto statRdy = rt_in8(IO + ATA_REG_STATUS); + + if ((statRdy & ATA_SR_BSY)) + goto ATAWaitForIO_Retry; + +ATAWaitForIO_Retry2: + statRdy = rt_in8(IO + ATA_REG_STATUS); + + if (statRdy & ATA_SR_ERR) + return false; + + if (!(statRdy & ATA_SR_DRDY)) + goto ATAWaitForIO_Retry2; + + return true; +} + +Void drv_std_select(UInt16 Bus) +{ + if (Bus == ATA_PRIMARY_IO) + rt_out8(Bus + ATA_REG_HDDEVSEL, ATA_PRIMARY_SEL); + else + rt_out8(Bus + ATA_REG_HDDEVSEL, ATA_SECONDARY_SEL); +} + +Boolean drv_std_init(UInt16 Bus, UInt8 Drive, UInt16& OutBus, UInt8& OutMaster) +{ + UInt16 IO = Bus; + + drv_std_select(IO); + +ATAInit_Retry: + // Bus init, NEIN bit. + rt_out8(IO + ATA_REG_NEIN, 1); + + // identify until it's good + + auto statRdy = rt_in8(IO + ATA_REG_STATUS); + + if (statRdy & ATA_SR_ERR) + { + kcout << "ERROR: ATA DRIVE RETURNED ERROR BIT! ABORTING...\r"; + + return false; + } + + if ((statRdy & ATA_SR_BSY)) + { + kcout << "Retrying..."; + goto ATAInit_Retry; + } + + rt_out8(IO + ATA_REG_COMMAND, ATA_CMD_IDENTIFY); + + /// fetch serial info + /// model, speed, number of sectors... + + drv_std_wait_io(IO); + + for (SizeT indexData = 0ul; indexData < kATADataLen; ++indexData) + { + kATAData[indexData] = rt_in16(IO + ATA_REG_DATA); + } + + OutBus = (Bus == ATA_PRIMARY_IO) ? ATA_PRIMARY_IO : ATA_SECONDARY_IO; + + OutMaster = (Bus == ATA_PRIMARY_IO) ? ATA_MASTER : ATA_SLAVE; + + kcout << "INFO: INITIALIZED ATA DRIVE!\r"; + + return true; +} + +Void drv_std_read(UInt64 Lba, UInt16 IO, UInt8 Master, Char* Buf, SizeT SectorSz, SizeT Size) +{ + Lba /= SectorSz; + + UInt8 Command = ((!Master) ? 0xE0 : 0xF0); + + drv_std_wait_io(IO); + drv_std_select(IO); + + rt_out8(IO + ATA_REG_HDDEVSEL, (Command) | (((Lba) >> 24) & 0x0F)); + + rt_out8(IO + ATA_REG_SEC_COUNT0, ((Size + SectorSz) / SectorSz)); + + rt_out8(IO + ATA_REG_LBA0, (Lba)&0xFF); + rt_out8(IO + ATA_REG_LBA1, (Lba) >> 8); + rt_out8(IO + ATA_REG_LBA2, (Lba) >> 16); + rt_out8(IO + ATA_REG_LBA3, (Lba) >> 24); + + rt_out8(IO + ATA_REG_COMMAND, ATA_CMD_READ_PIO); + + drv_std_wait_io(IO); + + for (SizeT IndexOff = 0; IndexOff < Size; ++IndexOff) + { + drv_std_wait_io(IO); + Buf[IndexOff] = rt_in16(IO + ATA_REG_DATA); + drv_std_wait_io(IO); + } + + drv_std_wait_io(IO); +} + +Void drv_std_write(UInt64 Lba, UInt16 IO, UInt8 Master, Char* Buf, SizeT SectorSz, SizeT Size) +{ + Lba /= SectorSz; + + UInt8 Command = ((!Master) ? 0xE0 : 0xF0); + + drv_std_wait_io(IO); + drv_std_select(IO); + + rt_out8(IO + ATA_REG_HDDEVSEL, (Command) | (((Lba) >> 24) & 0x0F)); + + rt_out8(IO + ATA_REG_SEC_COUNT0, ((Size + (SectorSz)) / SectorSz)); + + rt_out8(IO + ATA_REG_LBA0, (Lba)&0xFF); + rt_out8(IO + ATA_REG_LBA1, (Lba) >> 8); + rt_out8(IO + ATA_REG_LBA2, (Lba) >> 16); + rt_out8(IO + ATA_REG_LBA3, (Lba) >> 24); + + rt_out8(IO + ATA_REG_COMMAND, ATA_CMD_WRITE_PIO); + + drv_std_wait_io(IO); + + for (SizeT IndexOff = 0; IndexOff < Size; ++IndexOff) + { + drv_std_wait_io(IO); + rt_out16(IO + ATA_REG_DATA, Buf[IndexOff]); + drv_std_wait_io(IO); + } + + drv_std_wait_io(IO); +} + +/// @brief is ATA detected? +Boolean drv_std_detected(Void) +{ + return kATADetected; +} + +/*** + @brief Getter, gets the number of sectors inside the drive. +*/ +Kernel::SizeT drv_get_sector_count() +{ + return (kATAData[61] << 16) | kATAData[60]; +} + +/// @brief Get the drive size. +Kernel::SizeT drv_get_size() +{ + return drv_get_sector_count() * kATASectorSize; +} + +#endif /* ifdef __ATA_PIO__ */ |
