summaryrefslogtreecommitdiffhomepage
path: root/dev/kernel/HALKit/AMD64/Storage
diff options
context:
space:
mode:
authorAmlal El Mahrouss <amlal.elmahrouss@icloud.com>2025-03-23 19:13:48 +0100
committerAmlal El Mahrouss <amlal.elmahrouss@icloud.com>2025-03-23 19:15:17 +0100
commita13e1c0911c0627184bc38f18c7fdda64447b3ad (patch)
tree073a62c09bf216e85a3f310376640fa1805147f9 /dev/kernel/HALKit/AMD64/Storage
parent149fa096eb306d03686b3b67e813cf1a78e08cd0 (diff)
meta(kernel): Reworked repository's filesystem structure.
Removing useless parts of the project too. Signed-off-by: Amlal El Mahrouss <amlal.elmahrouss@icloud.com>
Diffstat (limited to 'dev/kernel/HALKit/AMD64/Storage')
-rw-r--r--dev/kernel/HALKit/AMD64/Storage/AHCI+Generic.cc424
-rw-r--r--dev/kernel/HALKit/AMD64/Storage/DMA+Generic.cc253
-rw-r--r--dev/kernel/HALKit/AMD64/Storage/PIO+Generic.cc191
3 files changed, 868 insertions, 0 deletions
diff --git a/dev/kernel/HALKit/AMD64/Storage/AHCI+Generic.cc b/dev/kernel/HALKit/AMD64/Storage/AHCI+Generic.cc
new file mode 100644
index 00000000..002266de
--- /dev/null
+++ b/dev/kernel/HALKit/AMD64/Storage/AHCI+Generic.cc
@@ -0,0 +1,424 @@
+/* -------------------------------------------
+
+ Copyright (C) 2024-2025, Amlal El Mahrouss Corporation, 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-2025, Amlal El Mahrouss, all rights reserved.
+ *
+ */
+
+#include <KernelKit/DeviceMgr.h>
+#include <KernelKit/DriveMgr.h>
+#include <KernelKit/UserProcessScheduler.h>
+#include <KernelKit/LPC.h>
+
+#include <FirmwareKit/EPM.h>
+
+#include <modules/ATA/ATA.h>
+#include <modules/AHCI/AHCI.h>
+#include <KernelKit/PCI/Iterator.h>
+#include <NewKit/Utils.h>
+#include <KernelKit/LockDelegate.h>
+
+#include <StorageKit/AHCI.h>
+
+#define kHBAErrTaskFile (1 << 30)
+#define kHBAPxCmdST (0x0001)
+#define kHBAPxCmdFre (0x0010)
+#define kHBAPxCmdFR (0x4000)
+#define kHBAPxCmdCR (0x8000)
+
+#define kSATALBAMode (1 << 6)
+
+#define kSATASRBsy (0x80)
+#define kSATASRDrq (0x08)
+
+#define kHBABohcBiosOwned (1 << 0)
+#define kHBABohcOSOwned (1 << 1)
+
+#define kSATAPortCnt (0x20)
+
+#define kSATASig (0x00000101)
+#define kSATAPISig (0xEB140101)
+
+#define kSATAProgIfAHCI (0x01)
+#define kSATASubClass (0x06)
+#define kSATABar5 (0x24)
+
+using namespace NeOS;
+
+STATIC PCI::Device kSATADev;
+STATIC HbaMem* kSATAHba;
+STATIC Lba kSATASectorCount = 0UL;
+STATIC UInt16 kSATAIndex = 0U;
+STATIC Char kCurrentDiskModel[50] = {"UNKNOWN AHCI DRIVE"};
+STATIC UInt16 kSATAPortsImplemented = 0U;
+
+template <BOOL Write, BOOL CommandOrCTRL, BOOL Identify>
+STATIC Void drv_std_input_output(UInt64 lba, UInt8* buffer, SizeT sector_sz, SizeT size_buffer) noexcept;
+
+STATIC Int32 drv_find_cmd_slot(HbaPort* port) noexcept;
+
+STATIC Void drv_compute_disk_ahci() noexcept;
+
+STATIC Void drv_compute_disk_ahci() noexcept
+{
+ kSATASectorCount = 0UL;
+
+ const UInt16 kSzIdent = kib_cast(1);
+
+ UInt8 identify_data[kSzIdent] = {0};
+
+ rt_set_memory(identify_data, 0, kSzIdent);
+
+ drv_std_input_output<NO, YES, YES>(0, identify_data, kAHCISectorSize, kSzIdent);
+
+ kSATASectorCount = (identify_data[61] << 16) | identify_data[60];
+
+ kout << "Disk Model: " << kCurrentDiskModel << kendl;
+ kout << "Disk Size: " << number(drv_get_size()) << kendl;
+ kout << "Disk Sector Count: " << number(kSATASectorCount) << kendl;
+}
+
+STATIC Int32 drv_find_cmd_slot(HbaPort* port) noexcept
+{
+ UInt32 slots = (port->Sact | port->Ci);
+
+ for (Int32 i = 0; i < kSATAPortCnt; ++i)
+ {
+ if ((slots & 1) == 0)
+ return i;
+
+ slots >>= 1;
+ }
+
+ return ~0;
+}
+
+template <BOOL Write, BOOL CommandOrCTRL, BOOL Identify>
+STATIC Void drv_std_input_output(UInt64 lba, UInt8* buffer, SizeT sector_sz, SizeT size_buffer) noexcept
+{
+ UIntPtr slot = 0UL;
+
+ slot = drv_find_cmd_slot(&kSATAHba->Ports[kSATAIndex]);
+
+ if (slot == ~0)
+ return;
+
+ volatile HbaCmdHeader* command_header = ((HbaCmdHeader*)(((UInt64)kSATAHba->Ports[kSATAIndex].Clb)));
+
+ command_header += slot;
+
+ MUST_PASS(command_header);
+
+ command_header->Cfl = sizeof(FisRegH2D) / sizeof(UInt32);
+ command_header->Write = Write;
+ command_header->Prdtl = (UInt16)((size_buffer - 1) >> 4) + 1;
+
+ HbaCmdTbl* command_table = (HbaCmdTbl*)((VoidPtr)((UInt64)command_header->Ctba));
+
+ rt_set_memory(command_table, 0, sizeof(HbaCmdTbl) + (command_header->Prdtl - 1) * sizeof(HbaPrdtEntry));
+
+ MUST_PASS(command_table);
+
+ UIntPtr buffer_phys = HAL::hal_get_phys_address(buffer);
+
+ UInt16 prd_i = 0;
+
+ for (; prd_i < (command_header->Prdtl - 1); prd_i++)
+ {
+ command_table->Prdt[prd_i].Dbc = ((size_buffer / command_header->Prdtl - 1) - 1);
+ command_table->Prdt[prd_i].Dba = ((UInt32)(UInt64)buffer_phys + (prd_i * command_table->Prdt[prd_i].Dbc));
+ command_table->Prdt[prd_i].Dbau = (((UInt64)(buffer_phys) >> 32) + (prd_i * command_table->Prdt[prd_i].Dbc));
+ command_table->Prdt[prd_i].Ie = YES;
+ }
+
+ command_table->Prdt[prd_i].Dba = ((UInt32)(UInt64)buffer_phys + (prd_i * command_table->Prdt[prd_i].Dbc));
+ command_table->Prdt[prd_i].Dbau = (((UInt64)(buffer_phys) >> 32) + (prd_i * command_table->Prdt[prd_i].Dbc));
+ command_table->Prdt[prd_i].Dbc = ((size_buffer / command_header->Prdtl - 1) - 1);
+ command_table->Prdt[prd_i].Ie = YES;
+
+ FisRegH2D* h2d_fis = (FisRegH2D*)(&command_table->Cfis);
+
+ h2d_fis->FisType = kFISTypeRegH2D;
+ h2d_fis->CmdOrCtrl = CommandOrCTRL;
+ h2d_fis->Command = Write ? kAHCICmdWriteDmaEx : kAHCICmdReadDmaEx;
+
+ if (Identify)
+ h2d_fis->Command = kAHCICmdIdentify;
+
+ h2d_fis->Lba0 = (lba)&0xFF;
+ h2d_fis->Lba1 = (lba >> 8) & 0xFF;
+ h2d_fis->Lba2 = (lba >> 16) & 0xFF;
+
+ h2d_fis->Device = Identify ? 0U : kSATALBAMode;
+
+ h2d_fis->Lba3 = (lba >> 24) & 0xFF;
+ h2d_fis->Lba4 = (lba >> 32) & 0xFF;
+ h2d_fis->Lba5 = (lba >> 40) & 0xFF;
+
+ h2d_fis->CountLow = (size_buffer)&0xFF;
+ h2d_fis->CountHigh = (size_buffer >> 8) & 0xFF;
+
+ while (kSATAHba->Ports[kSATAIndex].Tfd & (kSATASRBsy | kSATASRDrq))
+ {
+ ;
+ }
+
+ kSATAHba->Ports[kSATAIndex].Ci = (1 << slot);
+
+ while (YES)
+ {
+ if (!(kSATAHba->Ports[kSATAIndex].Ci & (1 << slot)))
+ break;
+
+ if (kSATAHba->Is & kHBAErrTaskFile)
+ {
+ err_global_get() = kErrorDiskIsCorrupted;
+ return;
+ }
+ }
+
+ if (kSATAHba->Is & kHBAErrTaskFile)
+ {
+ err_global_get() = kErrorDiskIsCorrupted;
+ return;
+ }
+}
+
+/***
+ @brief Gets the number of sectors inside the drive.
+ @return Sector size in bytes.
+ */
+SizeT drv_get_sector_count_ahci()
+{
+ return kSATASectorCount;
+}
+
+/// @brief Get the drive size.
+/// @return Disk size in bytes.
+SizeT drv_get_size_ahci()
+{
+ return drv_get_sector_count() * kAHCISectorSize;
+}
+
+/// @brief Enable Host and probe using the IDENTIFY command.
+STATIC Void ahci_enable_and_probe()
+{
+ if (kSATAHba->Bohc & kHBABohcBiosOwned)
+ {
+ kSATAHba->Bohc |= kHBABohcOSOwned;
+
+ while (kSATAHba->Bohc & kHBABohcBiosOwned)
+ {
+ ;
+ }
+ }
+
+ // Poll until ready.
+ while (kSATAHba->Ports[kSATAIndex].Cmd & kHBAPxCmdCR)
+ {
+ ;
+ }
+
+ kSATAHba->Ports[kSATAIndex].Cmd |= kHBAPxCmdFre;
+ kSATAHba->Ports[kSATAIndex].Cmd |= kHBAPxCmdST;
+
+ drv_compute_disk_ahci();
+}
+
+/// @brief Initializes an AHCI disk.
+/// @param pi the amount of ports that have been detected.
+/// @param atapi reference value, tells whether we should detect ATAPI instead of SATA.
+/// @return if the disk was successfully initialized or not.
+STATIC Bool drv_std_init_ahci(UInt16& pi, BOOL& atapi)
+{
+ PCI::Iterator iterator(Types::PciDeviceKind::MassStorageController);
+
+ for (SizeT device_index = 0; device_index < NE_BUS_COUNT; ++device_index)
+ {
+ kSATADev = iterator[device_index].Leak(); // Leak device.
+
+ if (kSATADev.Subclass() == kSATASubClass &&
+ kSATADev.ProgIf() == kSATAProgIfAHCI)
+ {
+ HbaMem* mem_ahci = (HbaMem*)kSATADev.Bar(kSATABar5);
+
+ kSATADev.EnableMmio((UIntPtr)mem_ahci);
+ kSATADev.BecomeBusMaster((UIntPtr)mem_ahci);
+
+ UInt32 ports_implemented = mem_ahci->Pi;
+ UInt16 ahci_index = 0;
+
+ pi = ports_implemented;
+
+ const UInt16 kMaxPortsImplemented = kSATAPortCnt;
+ const UInt32 kSATASignature = kSATASig;
+ const UInt32 kSATAPISignature = kSATAPISig;
+ const UInt8 kSATAPresent = 0x03;
+ const UInt8 kSATAIPMActive = 0x01;
+
+ kSATAHba = mem_ahci;
+
+ while (ports_implemented)
+ {
+ UInt8 ipm = (mem_ahci->Ports[ahci_index].Ssts >> 8) & 0x0F;
+ UInt8 det = mem_ahci->Ports[ahci_index].Ssts & 0x0F;
+
+ if (det != kSATAPresent || ipm != kSATAIPMActive)
+ continue;
+
+ if (mem_ahci->Ports[ahci_index].Sig == kSATASignature)
+ {
+ kout << "Detect: /dev/sat" << number(ahci_index) << kendl;
+
+ kSATAIndex = ahci_index;
+ kSATAHba = mem_ahci;
+
+ ahci_enable_and_probe();
+
+ break;
+ }
+ else if (atapi && kSATAPISignature == mem_ahci->Ports[ahci_index].Sig)
+ {
+ kout << "Detect: /dev/atp" << number(ahci_index) << kendl;
+
+ kSATAIndex = ahci_index;
+ kSATAHba = mem_ahci;
+
+ ahci_enable_and_probe();
+
+ break;
+ }
+
+ ports_implemented >>= 1;
+ ++ahci_index;
+ }
+
+ return YES;
+ }
+ }
+
+ return NO;
+}
+
+Bool drv_std_detected_ahci()
+{
+ return kSATADev.DeviceId() != (UShort)PCI::PciConfigKind::Invalid && kSATADev.Bar(kSATABar5) != 0;
+}
+
+namespace NeOS
+{
+ UInt16 sk_init_ahci_device(BOOL atapi)
+ {
+ UInt16 pi = 0;
+ return drv_std_init_ahci(pi, atapi);
+
+ kSATAPortsImplemented = pi;
+
+ return pi;
+ }
+
+ namespace Detail
+ {
+ /// @brief Read AHCI device.
+ /// @param self device
+ /// @param mnt mounted disk.
+ STATIC Void sk_io_read_ahci(IDeviceObject<MountpointInterface*>* self, MountpointInterface* mnt)
+ {
+ AHCIDeviceInterface* dev = (AHCIDeviceInterface*)self;
+
+ if (!dev)
+ return;
+
+ auto disk = mnt->GetAddressOf(dev->GetIndex());
+
+ if (!disk)
+ return;
+
+ drv_std_input_output<NO, YES, NO>(disk->fPacket.fPacketLba, (UInt8*)disk->fPacket.fPacketContent, kAHCISectorSize, disk->fPacket.fPacketSize);
+ }
+
+ /// @brief Write AHCI device.
+ /// @param self device
+ /// @param mnt mounted disk.
+ STATIC Void sk_io_write_ahci(IDeviceObject<MountpointInterface*>* self, MountpointInterface* mnt)
+ {
+ AHCIDeviceInterface* dev = (AHCIDeviceInterface*)self;
+
+ if (!dev)
+ return;
+
+ auto disk = mnt->GetAddressOf(dev->GetIndex());
+
+ if (!disk)
+ return;
+
+ drv_std_input_output<YES, YES, NO>(disk->fPacket.fPacketLba, (UInt8*)disk->fPacket.fPacketContent, kAHCISectorSize, disk->fPacket.fPacketSize);
+ }
+ } // namespace Detail
+
+ ErrorOr<AHCIDeviceInterface> sk_acquire_ahci_device(Int32 drv_index)
+ {
+ if (!drv_std_detected_ahci())
+ return ErrorOr<AHCIDeviceInterface>(kErrorDisk);
+
+ AHCIDeviceInterface device(Detail::sk_io_read_ahci,
+ Detail::sk_io_write_ahci,
+ nullptr);
+
+ device.SetPortsImplemented(kSATAPortsImplemented);
+ device.SetIndex(drv_index);
+
+ return ErrorOr<AHCIDeviceInterface>(device);
+ }
+} // namespace NeOS
+
+#ifdef __AHCI__
+
+Void drv_std_write(UInt64 lba, Char* buffer, SizeT sector_sz, SizeT size_buffer)
+{
+ drv_std_input_output<YES, YES, NO>(lba, (UInt8*)buffer, sector_sz, size_buffer);
+}
+
+Void drv_std_read(UInt64 lba, Char* buffer, SizeT sector_sz, SizeT size_buffer)
+{
+ drv_std_input_output<NO, YES, NO>(lba, (UInt8*)buffer, sector_sz, size_buffer);
+}
+
+Bool drv_std_init(UInt16& pi)
+{
+ BOOL atapi = NO;
+ return drv_std_init_ahci(pi, atapi);
+}
+
+Bool drv_std_detected(Void)
+{
+ return drv_std_detected_ahci();
+}
+
+/***
+ @brief Gets the number of sectors inside the drive.
+ @return Sector size in bytes.
+ */
+SizeT drv_get_sector_count()
+{
+ return drv_get_sector_count_ahci();
+}
+
+/// @brief Get the drive size.
+/// @return Disk size in bytes.
+SizeT drv_get_size()
+{
+ return drv_get_size_ahci();
+}
+
+#endif // ifdef __AHCI__
diff --git a/dev/kernel/HALKit/AMD64/Storage/DMA+Generic.cc b/dev/kernel/HALKit/AMD64/Storage/DMA+Generic.cc
new file mode 100644
index 00000000..e025d24d
--- /dev/null
+++ b/dev/kernel/HALKit/AMD64/Storage/DMA+Generic.cc
@@ -0,0 +1,253 @@
+/* -------------------------------------------
+
+ Copyright (C) 2024-2025, 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 <modules/ATA/ATA.h>
+#include <ArchKit/ArchKit.h>
+#include <KernelKit/PCI/Iterator.h>
+
+#if defined(__ATA_DMA__)
+
+#define kATADataLen (256)
+
+using namespace NeOS;
+using namespace NeOS::HAL;
+
+/// BUGS: 0
+
+STATIC Boolean kATADetected = false;
+STATIC Int32 kATADeviceType = kATADeviceCount;
+STATIC Char kATAData[kATADataLen] = {0};
+STATIC NeOS::PCI::Device kATADevice;
+STATIC Char kCurrentDiskModel[50] = {"UNKNOWN DMA DRIVE"};
+
+Boolean drv_std_wait_io(UInt16 IO)
+{
+ for (int i = 0; i < 400; i++)
+ rt_in8(IO + ATA_REG_STATUS);
+
+ATAWaitForIO_Retry:
+ auto status_rdy = rt_in8(IO + ATA_REG_STATUS);
+
+ if ((status_rdy & ATA_SR_BSY))
+ goto ATAWaitForIO_Retry;
+
+ATAWaitForIO_Retry2:
+ status_rdy = rt_in8(IO + ATA_REG_STATUS);
+
+ if (status_rdy & ATA_SR_ERR)
+ return false;
+
+ if (!(status_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)
+{
+ PCI::Iterator iterator(Types::PciDeviceKind::MassStorageController);
+
+ for (SizeT device_index = 0; device_index < NE_BUS_COUNT; ++device_index)
+ {
+ kATADevice = iterator[device_index].Leak(); // And then leak the reference.
+
+ // if SATA and then interface is AHCI...
+ if (kATADevice.Subclass() == 0x01)
+ {
+ UInt16 IO = Bus;
+
+ drv_std_select(IO);
+
+ // Bus init, NEIN bit.
+ rt_out8(IO + ATA_REG_NEIN, 1);
+
+ // identify until it's good.
+ ATAInit_Retry:
+ auto status_rdy = rt_in8(IO + ATA_REG_STATUS);
+
+ if (status_rdy & ATA_SR_ERR)
+ {
+ return false;
+ }
+
+ if ((status_rdy & ATA_SR_BSY))
+ 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 i = 0ul; i < kATADataLen; ++i)
+ {
+ drv_std_wait_io(IO);
+ kATAData[i] = NeOS::HAL::rt_in16(IO + ATA_REG_DATA);
+ drv_std_wait_io(IO);
+ }
+
+ for (SizeT i = 0; i < 40; i += 2)
+ {
+ kCurrentDiskModel[i * 2] = kATAData[27 + i * 2] >> 8;
+ kCurrentDiskModel[i * 2 + 1] = kATAData[27 + i * 2] & 0xFF;
+ }
+
+ kCurrentDiskModel[40] = '\0';
+
+ kout << "Drive Model: " << kCurrentDiskModel << kendl;
+
+ OutBus = (Bus == ATA_PRIMARY_IO) ? ATA_PRIMARY_IO : ATA_SECONDARY_IO;
+ OutMaster = (Bus == ATA_PRIMARY_IO) ? ATA_MASTER : ATA_SLAVE;
+
+ return YES;
+ }
+ }
+
+ ke_panic(RUNTIME_CHECK_BOOTSTRAP, "Invalid ATA DMA driver, not detected");
+
+ return NO;
+}
+
+namespace Detail
+{
+ using namespace NeOS;
+
+ struct PRDEntry
+ {
+ UInt32 mAddress;
+ UInt16 mByteCount;
+ UInt16 mFlags;
+ };
+} // namespace Detail
+
+static UIntPtr kReadAddr = mib_cast(2);
+static UIntPtr kWriteAddr = mib_cast(4);
+
+Void drv_std_read(UInt64 Lba, UInt16 IO, UInt8 Master, Char* Buf, SizeT SectorSz, SizeT Size)
+{
+ Lba /= SectorSz;
+
+ if (Size > kib_cast(64))
+ ke_panic(RUNTIME_CHECK_FAILED, "ATA-DMA only supports < 64kb DMA transfers.");
+
+ UInt8 Command = ((!Master) ? 0xE0 : 0xF0);
+
+ rt_copy_memory((VoidPtr)Buf, (VoidPtr)kReadAddr, Size);
+
+ drv_std_select(IO);
+
+ rt_out8(IO + ATA_REG_HDDEVSEL, (Command) | (((Lba) >> 24) & 0x0F));
+
+ rt_out8(IO + ATA_REG_SEC_COUNT0, ((Size + SectorSz - 1) / 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);
+
+ Detail::PRDEntry* prd = (Detail::PRDEntry*)(kATADevice.Bar(0x20) + 4); // The PRDEntry is not correct.
+
+ prd->mAddress = (UInt32)(UIntPtr)kReadAddr;
+ prd->mByteCount = Size - 1;
+ prd->mFlags = 0x8000; // indicate the end of prd.
+
+ rt_out32(kATADevice.Bar(0x20) + 0x04, (UInt32)(UIntPtr)prd);
+
+ rt_out8(kATADevice.Bar(0x20) + ATA_REG_COMMAND, ATA_CMD_READ_DMA);
+
+ rt_out8(kATADevice.Bar(0x20) + 0x00, 0x09); // Start DMA engine
+
+ while (rt_in8(kATADevice.Bar(0x20) + ATA_REG_STATUS) & 0x01)
+ ;
+
+ rt_out8(kATADevice.Bar(0x20) + 0x00, 0x00); // Stop DMA engine
+
+ rt_copy_memory((VoidPtr)kReadAddr, (VoidPtr)Buf, Size);
+
+ delete prd;
+ prd = nullptr;
+}
+
+Void drv_std_write(UInt64 Lba, UInt16 IO, UInt8 Master, Char* Buf, SizeT SectorSz, SizeT Size)
+{
+ Lba /= SectorSz;
+
+ if (Size > kib_cast(64))
+ ke_panic(RUNTIME_CHECK_FAILED, "ATA-DMA only supports < 64kb DMA transfers.");
+
+ UInt8 Command = ((!Master) ? 0xE0 : 0xF0);
+
+ rt_copy_memory((VoidPtr)Buf, (VoidPtr)kWriteAddr, Size);
+
+ rt_out8(IO + ATA_REG_HDDEVSEL, (Command) | (((Lba) >> 24) & 0x0F));
+
+ rt_out8(IO + ATA_REG_SEC_COUNT0, ((Size + (SectorSz - 1)) / 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);
+
+ Detail::PRDEntry* prd = (Detail::PRDEntry*)(kATADevice.Bar(0x20) + 4);
+ prd->mAddress = (UInt32)(UIntPtr)kWriteAddr;
+ prd->mByteCount = Size - 1;
+ prd->mFlags = 0x8000;
+
+ rt_out32(kATADevice.Bar(0x20) + 0x04, (UInt32)(UIntPtr)prd);
+ rt_out8(kATADevice.Bar(0x20) + ATA_REG_COMMAND, ATA_CMD_WRITE_DMA);
+
+ rt_out8(IO + 0x00, 0x09); // Start DMA engine
+
+ while (rt_in8(kATADevice.Bar(0x20) + ATA_REG_STATUS) & 0x01)
+ ;
+
+ rt_out8(kATADevice.Bar(0x20) + 0x00, 0x00); // Stop DMA engine
+
+ delete prd;
+ prd = nullptr;
+}
+
+/// @brief is ATA detected?
+Boolean drv_std_detected(Void)
+{
+ return kATADetected;
+}
+
+/***
+ @brief Getter, gets the number of sectors inside the drive.
+*/
+NeOS::SizeT drv_get_sector_count()
+{
+ return (kATAData[61] << 16) | kATAData[60];
+}
+
+/// @brief Get the drive size.
+NeOS::SizeT drv_get_size()
+{
+ return (drv_get_sector_count()) * kATASectorSize;
+}
+
+#endif /* ifdef __ATA_DMA__ */
diff --git a/dev/kernel/HALKit/AMD64/Storage/PIO+Generic.cc b/dev/kernel/HALKit/AMD64/Storage/PIO+Generic.cc
new file mode 100644
index 00000000..cb629478
--- /dev/null
+++ b/dev/kernel/HALKit/AMD64/Storage/PIO+Generic.cc
@@ -0,0 +1,191 @@
+/* -------------------------------------------
+
+ Copyright (C) 2024-2025, Amlal EL Mahrouss, all rights reserved.
+
+------------------------------------------- */
+
+/**
+ * @file 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 <modules/ATA/ATA.h>
+#include <ArchKit/ArchKit.h>
+
+#ifdef __ATA_PIO__
+
+using namespace NeOS;
+using namespace NeOS::HAL;
+
+/// BUGS: 0
+
+#define kATADataLen 512
+
+STATIC Boolean kATADetected = false;
+STATIC Int32 kATADeviceType = kATADeviceCount;
+STATIC Char kATAData[kATADataLen] = {0};
+STATIC Char kCurrentDiskModel[50] = {"UNKNOWN PIO DRIVE"};
+
+Boolean drv_std_wait_io(UInt16 IO)
+{
+ for (int i = 0; i < 400; i++)
+ rt_in8(IO + ATA_REG_STATUS);
+
+ATAWaitForIO_Retry:
+ auto stat_rdy = rt_in8(IO + ATA_REG_STATUS);
+
+ if ((stat_rdy & ATA_SR_BSY))
+ goto ATAWaitForIO_Retry;
+
+ATAWaitForIO_Retry2:
+ stat_rdy = rt_in8(IO + ATA_REG_STATUS);
+
+ if (stat_rdy & ATA_SR_ERR)
+ return false;
+
+ if (!(stat_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);
+
+ // Bus init, NEIN bit.
+ rt_out8(IO + ATA_REG_NEIN, 1);
+
+ // identify until it's good.
+ATAInit_Retry:
+ auto stat_rdy = rt_in8(IO + ATA_REG_STATUS);
+
+ if (stat_rdy & ATA_SR_ERR)
+ {
+ return false;
+ }
+
+ if ((stat_rdy & ATA_SR_BSY))
+ goto ATAInit_Retry;
+
+ OutBus = (Bus == ATA_PRIMARY_IO) ? ATA_PRIMARY_IO : ATA_SECONDARY_IO;
+ OutMaster = (Bus == ATA_PRIMARY_IO) ? ATA_MASTER : ATA_SLAVE;
+
+ rt_out8(OutBus + ATA_REG_COMMAND, ATA_CMD_IDENTIFY);
+
+ drv_std_wait_io(IO);
+
+ /// fetch serial info
+ /// model, speed, number of sectors...
+
+ for (SizeT i = 0ul; i < kATADataLen; ++i)
+ {
+ kATAData[i] = HAL::rt_in16(OutBus + ATA_REG_DATA);
+ }
+
+ for (Int32 i = 0; i < 20; i++)
+ {
+ kCurrentDiskModel[i * 2] = kATAData[27 + i] >> 8;
+ kCurrentDiskModel[i * 2 + 1] = kATAData[27 + i + 1] & 0xFF;
+ }
+
+ kCurrentDiskModel[40] = '\0';
+
+ kout << "Detect: /dev/ata0" << kendl;
+
+ kout << "Drive Model: " << kCurrentDiskModel << kendl;
+
+ 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);
+
+ for (SizeT IndexOff = 0; IndexOff < Size; ++IndexOff)
+ {
+ drv_std_wait_io(IO);
+ Buf[IndexOff] = HAL::rt_in16(IO + ATA_REG_DATA);
+ }
+}
+
+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);
+
+ for (SizeT IndexOff = 0; IndexOff < Size; ++IndexOff)
+ {
+ drv_std_wait_io(IO);
+ rt_out16(IO + ATA_REG_DATA, Buf[IndexOff]);
+ }
+}
+
+/// @brief is ATA detected?
+Boolean drv_std_detected(Void)
+{
+ return kATADetected;
+}
+
+/***
+ @brief Getter, gets the number of sectors inside the drive.
+ */
+SizeT drv_get_sector_count()
+{
+ return (kATAData[61] << 16) | kATAData[60];
+}
+
+/// @brief Get the drive size.
+SizeT drv_get_size()
+{
+ return (drv_get_sector_count()) * kATASectorSize;
+}
+
+#endif /* ifdef __ATA_PIO__ */ \ No newline at end of file