summaryrefslogtreecommitdiffhomepage
path: root/dev/Kernel/HALKit/AMD64/Storage
diff options
context:
space:
mode:
authorAmlal <amlalelmahrouss@icloud.com>2024-12-21 21:59:13 +0100
committerAmlal <amlalelmahrouss@icloud.com>2024-12-21 21:59:45 +0100
commit610f91d87152cbe48d3054fcf437d8239da6ef35 (patch)
treea386f7047ab73d088169ab2371ddc6ffe8020f1c /dev/Kernel/HALKit/AMD64/Storage
parent509fcca5986651c8ba712fb395f8498f2dea4109 (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.cc319
-rw-r--r--dev/Kernel/HALKit/AMD64/Storage/ATA-DMA.cc38
-rw-r--r--dev/Kernel/HALKit/AMD64/Storage/ATA-PIO.cc197
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__ */