summaryrefslogtreecommitdiffhomepage
path: root/dev/Kernel/HALKit/AMD64/Storage
diff options
context:
space:
mode:
authorAmlal <amlal.elmahrouss@icloud.com>2025-01-24 10:38:36 +0100
committerAmlal <amlal.elmahrouss@icloud.com>2025-01-24 10:38:36 +0100
commit7b4bd3577a31d0f0adc7371840642791ae1567f4 (patch)
tree1a8afc973aaa739d0d763315cad2fd376d1cea9c /dev/Kernel/HALKit/AMD64/Storage
ADD: Open version, with important changes kept out.
Signed-off-by: Amlal <amlal.elmahrouss@icloud.com>
Diffstat (limited to 'dev/Kernel/HALKit/AMD64/Storage')
-rw-r--r--dev/Kernel/HALKit/AMD64/Storage/ATA-DMA.cc38
-rw-r--r--dev/Kernel/HALKit/AMD64/Storage/ATA-PIO.cc195
-rw-r--r--dev/Kernel/HALKit/AMD64/Storage/SATA-DMA.cc316
3 files changed, 549 insertions, 0 deletions
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..5f765d03
--- /dev/null
+++ b/dev/Kernel/HALKit/AMD64/Storage/ATA-DMA.cc
@@ -0,0 +1,38 @@
+/* -------------------------------------------
+
+ Copyright (C) 2024, Amlal EL Mahrouss, all rights reserved.
+
+------------------------------------------- */
+
+/**
+ * @file ATA-DMA.cc
+ * @author Amlal EL Mahrouss (amlalelmahrouss@icloud.com)
+ * @brief ATA driver (DMA mode).
+ * @version 0.1
+ * @date 2024-02-02
+ *
+ * @copyright Copyright (c) Amlal EL Mahrouss
+ *
+ */
+
+#include <StorageKit/PRDT.h>
+
+#include <Mod/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..6e523cb7
--- /dev/null
+++ b/dev/Kernel/HALKit/AMD64/Storage/ATA-PIO.cc
@@ -0,0 +1,195 @@
+/* -------------------------------------------
+
+ Copyright (C) 2024, Amlal EL Mahrouss, all rights reserved.
+
+------------------------------------------- */
+
+/**
+ * @file ATA-PIO.cc
+ * @author Amlal EL Mahrouss (amlalelmahrouss@icloud.com)
+ * @brief ATA driver (PIO mode).
+ * @version 0.1
+ * @date 2024-02-02
+ *
+ * @copyright Copyright (c) Amlal EL Mahrouss
+ *
+ */
+
+#include <Mod/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 rdy = rt_in8(IO + ATA_REG_STATUS);
+
+ if ((rdy & ATA_SR_BSY))
+ goto ATAWaitForIO_Retry;
+
+ATAWaitForIO_Retry2:
+ rdy = rt_in8(IO + ATA_REG_STATUS);
+
+ if (rdy & ATA_SR_ERR)
+ return false;
+
+ if (!(rdy & 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 rdy = rt_in8(IO + ATA_REG_STATUS);
+
+ if (rdy & ATA_SR_ERR)
+ {
+ kcout << "ATA Error, aborting...\r";
+
+ return false;
+ }
+
+ if ((rdy & ATA_SR_BSY))
+ {
+ kcout << "Retrying as controller is busy...\r";
+ 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;
+
+ return YES;
+}
+
+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__ */
diff --git a/dev/Kernel/HALKit/AMD64/Storage/SATA-DMA.cc b/dev/Kernel/HALKit/AMD64/Storage/SATA-DMA.cc
new file mode 100644
index 00000000..74afcde3
--- /dev/null
+++ b/dev/Kernel/HALKit/AMD64/Storage/SATA-DMA.cc
@@ -0,0 +1,316 @@
+/* -------------------------------------------
+
+ Copyright (C) 2024, Amlal EL Mahrouss, all rights reserved.
+
+------------------------------------------- */
+
+/**
+ * @file AHCI.cc
+ * @author Amlal EL Mahrouss (amlalelmahrouss@icloud.com)
+ * @brief AHCI driver.
+ * @version 0.1
+ * @date 2024-02-02
+ *
+ * @Copyright (C) 2024, Amlal EL Mahrouss, all rights reserved.
+ *
+ */
+
+#include <KernelKit/UserProcessScheduler.h>
+#include <KernelKit/LPC.h>
+
+#include <Mod/ATA/ATA.h>
+#include <Mod/AHCI/AHCI.h>
+#include <KernelKit/PCI/Iterator.h>
+#include <NewKit/Utils.h>
+#include <KernelKit/LockDelegate.h>
+
+#ifdef __AHCI__
+
+#define kHBAErrTaskFile (1 << 30)
+#define kHBAPxCmdST 0x0001
+#define kHBAPxCmdFre 0x0010
+#define kHBAPxCmdFR 0x4000
+#define kHBAPxCmdCR 0x8000
+
+#define kSataLBAMode (1 << 6)
+
+#define kAhciSRBsy (0x80)
+#define kAhciSRDrq (0x08)
+
+#define kAhciPortCnt 32
+
+enum
+{
+ kSATAProgIfAHCI = 0x01,
+ kSATASubClass = 0x06,
+ kSATABar5 = 0x24,
+};
+
+STATIC Kernel::PCI::Device kPCIDevice;
+STATIC HbaMem* kSATAPort = nullptr;
+STATIC Kernel::SizeT kSATAPortIdx = 0UL;
+STATIC Kernel::Lba kCurrentDiskSectorCount = 0UL;
+
+template <BOOL Write, BOOL CommandOrCTRL, BOOL Identify>
+static Kernel::Void drv_std_input_output(Kernel::UInt64 lba, Kernel::UInt8* buffer, Kernel::SizeT sector_sz, Kernel::SizeT size_buffer) noexcept;
+
+static Kernel::Int32 drv_find_cmd_slot(HbaPort* port) noexcept;
+
+static Kernel::Void drv_calculate_disk_geometry() noexcept;
+
+static Kernel::Void drv_calculate_disk_geometry() noexcept
+{
+ kCurrentDiskSectorCount = 0UL;
+
+ Kernel::UInt8 identify_data[kib_cast(4)] = {};
+
+ drv_std_input_output<NO, YES, YES>(0, identify_data, 0, kib_cast(8));
+
+ kCurrentDiskSectorCount = (identify_data[61] << 16) | identify_data[60];
+
+ kcout << "Disk Size: " << Kernel::number(drv_get_size()) << endl;
+ kcout << "Highest LBA: " << Kernel::number(kCurrentDiskSectorCount) << endl;
+}
+
+/// @brief Initializes an AHCI disk.
+/// @param PortsImplemented the amount of kSATAPort 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)
+ {
+ kPCIDevice = iterator[device_index].Leak(); // And then leak the reference.
+
+ // if SATA and then interface is AHCI...
+ if (kPCIDevice.Subclass() == kSATASubClass &&
+ kPCIDevice.ProgIf() == kSATAProgIfAHCI)
+ {
+ kPCIDevice.EnableMmio(0x24); // Enable the memory index_byte/o for this ahci device.
+ kPCIDevice.BecomeBusMaster(0x24); // Become bus master for this ahci device, so that we can control it.
+
+ HbaMem* mem_ahci = (HbaMem*)kPCIDevice.Bar(0x24);
+
+ Kernel::UInt32 ports_implemented = mem_ahci->Pi;
+ Kernel::UInt16 ahci_index = 0;
+
+ const Kernel::UInt16 kMaxPortsImplemented = kAhciPortCnt;
+ 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.\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 SATA.\r";
+
+ kSATAPortIdx = ahci_index;
+ kSATAPort = mem_ahci;
+
+ kSATAPort->Ports[kSATAPortIdx].Cmd &= ~kHBAPxCmdST;
+ kSATAPort->Ports[kSATAPortIdx].Cmd &= ~kHBAPxCmdFre;
+
+ while (YES)
+ {
+ if (kSATAPort->Ports[kSATAPortIdx].Cmd & kHBAPxCmdFR)
+ continue;
+ if (kSATAPort->Ports[kSATAPortIdx].Cmd & kHBAPxCmdCR)
+ continue;
+
+ break;
+ }
+
+ const auto kAHCIBaseAddress = mib_cast(4);
+
+ kSATAPort->Ports[kSATAPortIdx].Clb = kAHCIBaseAddress + (kSATAPortIdx << 10);
+ kSATAPort->Ports[kSATAPortIdx].Clbu = 0;
+
+ rt_set_memory((VoidPtr)((UIntPtr)kSATAPort->Ports[kSATAPortIdx].Clb), 0, 1024);
+
+ kSATAPort->Ports[kSATAPortIdx].Fb = kAHCIBaseAddress + (32 << 10) + (kSATAPortIdx << 8);
+ kSATAPort->Ports[kSATAPortIdx].Fbu = 0;
+
+ rt_set_memory((VoidPtr)((UIntPtr)kSATAPort->Ports[kSATAPortIdx].Fb), 0, 256);
+
+ HbaCmdHeader* cmdheader = (HbaCmdHeader*)((UIntPtr)kSATAPort->Ports[kSATAPortIdx].Clb);
+
+ for (int i = 0; i < 32; i++)
+ {
+ cmdheader[i].Prdtl = 8;
+
+ cmdheader[i].Ctba = kAHCIBaseAddress + (40 << 10) + (kSATAPortIdx << 13) + (i << 8);
+ cmdheader[i].Ctbau = 0;
+
+ rt_set_memory((VoidPtr)(UIntPtr)cmdheader[i].Ctba, 0, 256);
+ }
+
+ while (kSATAPort->Ports[kSATAPortIdx].Cmd & kHBAPxCmdCR)
+ {
+ }
+
+ kSATAPort->Ports[kSATAPortIdx].Cmd |= kHBAPxCmdFre;
+ kSATAPort->Ports[kSATAPortIdx].Cmd |= kHBAPxCmdST;
+
+ drv_calculate_disk_geometry();
+
+ detected = YES;
+
+ break;
+ }
+ }
+
+ ports_implemented >>= 1;
+ ++ahci_index;
+ }
+
+ return detected;
+ }
+ }
+
+ return No;
+}
+
+Kernel::Boolean drv_std_detected(Kernel::Void)
+{
+ return kPCIDevice.DeviceId() != 0xFFFF;
+}
+
+Kernel::Void drv_std_write(Kernel::UInt64 lba, Kernel::Char* buffer, Kernel::SizeT sector_sz, Kernel::SizeT size_buffer)
+{
+ drv_std_input_output<YES, YES, NO>(lba, (Kernel::UInt8*)buffer, sector_sz, size_buffer);
+}
+
+Kernel::Void drv_std_read(Kernel::UInt64 lba, Kernel::Char* buffer, Kernel::SizeT sector_sz, Kernel::SizeT size_buffer)
+{
+ drv_std_input_output<NO, YES, NO>(lba, (Kernel::UInt8*)buffer, sector_sz, size_buffer);
+}
+
+static Kernel::Int32 drv_find_cmd_slot(HbaPort* port) noexcept
+{
+ if (port == nullptr)
+ return -1;
+
+ Kernel::UInt32 slots = (kSATAPort->Ports[kSATAPortIdx].Sact | kSATAPort->Ports[kSATAPortIdx].Ci);
+
+ for (Kernel::Int32 i = 0; i < ((kSATAPort->Cap & 0x1F) + 1); i++)
+ {
+ if ((slots & 1) == 0)
+ return i;
+
+ slots >>= 1;
+ }
+
+ return -1;
+}
+
+template <BOOL Write, BOOL CommandOrCTRL, BOOL Identify>
+static Kernel::Void drv_std_input_output(Kernel::UInt64 lba, Kernel::UInt8* buffer, Kernel::SizeT sector_sz, Kernel::SizeT size_buffer) noexcept
+{
+ if (!CommandOrCTRL)
+ return;
+
+ kSATAPort->Ports[kSATAPortIdx].Is = -1;
+
+ auto slot = 0L;
+
+ slot = drv_find_cmd_slot(&kSATAPort->Ports[kSATAPortIdx]);
+
+ if (slot == -1)
+ return;
+
+ volatile HbaCmdHeader* command_header = ((volatile HbaCmdHeader*)((Kernel::UInt64)kSATAPort->Ports[kSATAPortIdx].Clb + kSATAPort->Ports[kSATAPortIdx].Clbu));
+
+ command_header += slot;
+
+ MUST_PASS(command_header);
+
+ command_header->Cfl = sizeof(FisRegH2D) / sizeof(Kernel::UInt32);
+ command_header->Write = Write;
+ command_header->Prdtl = (Kernel::UInt16)((size_buffer - 1) >> 4) + 1;
+
+ volatile HbaCmdTbl* command_table = (volatile HbaCmdTbl*)((Kernel::UInt64)command_header->Ctba + command_header->Ctbau);
+
+ MUST_PASS(command_table);
+
+ for (Kernel::SizeT i = 0; i < (command_header->Prdtl - 1); i++)
+ {
+ command_table->PrdtEntries[i].Dba = ((Kernel::UInt32)(Kernel::UInt64)buffer & 0xFFFFFFFF);
+ command_table->PrdtEntries[i].Dbau = (((Kernel::UInt64)buffer >> 32) & 0xFFFFFFFF);
+ command_table->PrdtEntries[i].Dbc = (size_buffer - 1);
+ command_table->PrdtEntries[i].InterruptBit = YES;
+
+ size_buffer -= 16;
+ buffer += kib_cast(4);
+ }
+
+ volatile FisRegH2D* h2d_fis = (volatile FisRegH2D*)((Kernel::UInt64)command_table->Cfis);
+
+ h2d_fis->FisType = kFISTypeRegH2D;
+
+ h2d_fis->CmdOrCtrl = YES;
+
+ h2d_fis->Command = Write ? kAHCICmdWriteDmaEx : kAHCICmdReadDmaEx;
+
+ if (Identify)
+ h2d_fis->Command = kAHCICmdIdentify;
+
+ h2d_fis->Lba0 = lba;
+ h2d_fis->Lba1 = lba >> 8;
+ h2d_fis->Lba2 = lba >> 16;
+ h2d_fis->Lba3 = lba >> 24;
+
+ h2d_fis->Device = kSataLBAMode;
+
+ h2d_fis->CountLow = sector_sz & 0xFF;
+ h2d_fis->CountHigh = (sector_sz >> 8) & 0xFF;
+
+ while ((kSATAPort->Ports[kSATAPortIdx].Tfd & (kAhciSRBsy | kAhciSRDrq)))
+ {
+ kcout << "waiting for slot to be ready\r\n";
+ }
+
+ kSATAPort->Is = -1;
+ kSATAPort->Ports[kSATAPortIdx].Ci |= 1 << slot;
+
+ while (kSATAPort->Ports[kSATAPortIdx].Ci & (1 << slot))
+ {
+ kcout << Kernel::number(slot) << endl;
+
+ if (kSATAPort->Is & kHBAErrTaskFile) // check for task file error.
+ {
+ Kernel::ke_panic(RUNTIME_CHECK_BAD_BEHAVIOR, "AHCI Read disk failure, faulty component.");
+ 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__