diff options
Diffstat (limited to 'src/kernel/HALKit/AMD64/Storage')
| -rw-r--r-- | src/kernel/HALKit/AMD64/Storage/AHCI+Generic.cc | 600 | ||||
| -rw-r--r-- | src/kernel/HALKit/AMD64/Storage/DMA+Generic.cc | 199 | ||||
| -rw-r--r-- | src/kernel/HALKit/AMD64/Storage/NVME+Generic.cc | 9 | ||||
| -rw-r--r-- | src/kernel/HALKit/AMD64/Storage/PIO+Generic.cc | 278 | ||||
| -rw-r--r-- | src/kernel/HALKit/AMD64/Storage/SCSI+Generic.cc | 13 |
5 files changed, 1099 insertions, 0 deletions
diff --git a/src/kernel/HALKit/AMD64/Storage/AHCI+Generic.cc b/src/kernel/HALKit/AMD64/Storage/AHCI+Generic.cc new file mode 100644 index 00000000..14b40b98 --- /dev/null +++ b/src/kernel/HALKit/AMD64/Storage/AHCI+Generic.cc @@ -0,0 +1,600 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +/** + * @file AHCI+Generic.cc + * @author Amlal El Mahrouss (amlal@nekernel.org) + * @brief AHCI Generic driver. + * @version 0.1 + * @date 2024-02-02 + * + * @copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + * + */ + +#include <DmaKit/DmaPool.h> +#include <FirmwareKit/EPM.h> +#include <KernelKit/DeviceMgr.h> +#include <KernelKit/DriveMgr.h> +#include <KernelKit/KPC.h> +#include <KernelKit/LockDelegate.h> +#include <KernelKit/PCI/Iterator.h> +#include <KernelKit/ProcessScheduler.h> +#include <KernelKit/Timer.h> +#include <NeKit/Utils.h> +#include <StorageKit/AHCI.h> +#include <modules/AHCI/AHCI.h> +#include <modules/ATA/ATA.h> + +#define kSATAErrTaskFile (1 << 30) +#define kSATAPxCmdST (0x0001) +#define kSATAPxCmdFre (0x0010) +#define kSATAPxCmdFR (0x4000) +#define kSATAPxCmdCR (0x8000) + +#define kSATALBAMode (1 << 6) + +#define kSATASRBsy (0x80) +#define kSATASRDrq (0x08) + +#define kSATABohcBiosOwned (1 << 0) +#define kSATABohcOSOwned (1 << 1) + +#define kSATAPortCnt (0x20) + +#define kSATASig (0x00000101) +#define kSATAPISig (0xEB140101) + +#define kSATAProgIfAHCI (0x01) +#define kSATASubClass (0x06) +#define kSATABar5 (0x24) + +using namespace Kernel; + +STATIC PCI::Device kSATADev; +STATIC HbaMemRef kSATAHba; +STATIC Lba kSATASectorCount = 0UL; +STATIC UInt16 kSATAIndex = 0U; +STATIC Char kCurrentDiskModel[50] = {"GENERIC SATA"}; +STATIC UInt16 kSATAPortsImplemented = 0U; +STATIC ALIGN(kib_cast(4)) UInt8 kIdentifyData[kAHCISectorSize] = {0}; + +template <BOOL Write, BOOL CommandOrCTRL, BOOL Identify> +STATIC Void drv_std_input_output_ahci(UInt64 lba, UInt8* buffer, SizeT sector_sz, + SizeT size_buffer) noexcept; + +STATIC Int32 drv_find_cmd_slot_ahci(HbaPort* port) noexcept; + +STATIC Void drv_compute_disk_ahci() noexcept; + +STATIC SizeT drv_get_size_ahci(); + +STATIC SizeT drv_get_sector_count_ahci(); + +/***********************************************************************************/ +/// @brief Identify device and read LBA info, Disk OEM vendor. +/***********************************************************************************/ +STATIC Void drv_compute_disk_ahci() noexcept { + kSATASectorCount = 0UL; + + rt_set_memory(kIdentifyData, 0, kAHCISectorSize); + + drv_std_input_output_ahci<NO, YES, YES>(0, kIdentifyData, kAHCISectorSize, kAHCISectorSize); + + // --> Reinterpret the 512-byte buffer as an array of 256 UInt16 words + UInt16* identify_words = reinterpret_cast<UInt16*>(kIdentifyData); + + /// Extract 48-bit LBA. + UInt64 lba48_sectors = 0UL; + lba48_sectors |= (UInt64) identify_words[100]; + lba48_sectors |= (UInt64) identify_words[101] << 16; + lba48_sectors |= (UInt64) identify_words[102] << 32; + + if (lba48_sectors == 0) + kSATASectorCount = (identify_words[61] << 16) | identify_words[60]; + else + kSATASectorCount = lba48_sectors; + + for (Int32 i = 0; i < 20; i++) { + kCurrentDiskModel[i * 2] = (identify_words[27 + i] >> 8) & 0xFF; + kCurrentDiskModel[i * 2 + 1] = identify_words[27 + i] & 0xFF; + } + + kCurrentDiskModel[40] = '\0'; + + (Void)(kout << "SATA Sector Count: " << hex_number(kSATASectorCount) << kendl); + (Void)(kout << "SATA Disk Model: " << kCurrentDiskModel << kendl); +} + +/***********************************************************************************/ +/// @brief Finds a command slot for a HBA port. +/// @param port The port to search on. +/// @return The slot, or -1. +/***********************************************************************************/ +STATIC Int32 drv_find_cmd_slot_ahci(HbaPort* port) noexcept { + UInt32 slots = port->Sact | port->Ci; + + for (Int32 i = 0; i < kSATAPortCnt; ++i) // AHCI supports up to 32 slots + { + if ((slots & (1U << i)) == 0) return i; + } + + return -1; // no free slot found +} + +/***********************************************************************************/ +/// @brief Send an AHCI command, according to the template parameters. +/// @param lba Logical Block Address to look for. +/// @param buffer The data buffer to transfer. +/// @param sector_sz The disk's sector size (unused) +/// @param size_buffer The size of the **buffer** parameter. +/***********************************************************************************/ +template <BOOL Write, BOOL CommandOrCTRL, BOOL Identify> +STATIC Void drv_std_input_output_ahci(UInt64 lba, UInt8* buffer, SizeT sector_sz, + SizeT size_buffer) noexcept { + if (sector_sz == 0) { + kout << "ahci: Invalid sector size.\r"; + err_global_get() = kErrorDisk; + return; + } + + lba /= sector_sz; + + if (!buffer || size_buffer == 0) { + kout << "ahci: Invalid buffer for AHCI I/O.\r"; + err_global_get() = kErrorDisk; + return; + } + + UIntPtr slot = drv_find_cmd_slot_ahci(&kSATAHba->Ports[kSATAIndex]); + + UInt16 timeout = 0; + + constexpr static UInt16 kTimeout = 0x8000; + + while (slot == ~0UL) { + if (timeout > kTimeout) { + kout << "ahci: No free command slot found, AHCI disk is busy!\r"; + + err_global_get() = kErrorDisk; + return; + } + + slot = drv_find_cmd_slot_ahci(&kSATAHba->Ports[kSATAIndex]); + ++timeout; + } + + volatile HbaCmdHeader* command_header = + (volatile HbaCmdHeader*) ((UInt64) kSATAHba->Ports[kSATAIndex].Clb); + + command_header += slot; + + MUST_PASS(command_header); + + // Clear old command table memory + volatile HbaCmdTbl* command_table = + (volatile HbaCmdTbl*) (((UInt64) command_header->Ctbau << 32) | command_header->Ctba); + + MUST_PASS(command_table); + + rt_set_memory((VoidPtr) command_table, 0, sizeof(HbaCmdTbl)); + + VoidPtr ptr = rtl_dma_alloc(size_buffer, kib_cast(4)); + + rtl_dma_flush(ptr, size_buffer); + + if (Write) { + rt_copy_memory(buffer, ptr, size_buffer); + } + + rtl_dma_flush(ptr, size_buffer); + + // Build the PRD table. + SizeT bytes_remaining = size_buffer; + SizeT prdt_index = 0; + UIntPtr buffer_phys = (UIntPtr) ptr; + + while (bytes_remaining > 0) { + SizeT chunk_size = bytes_remaining; + + if (chunk_size > kib_cast(32)) chunk_size = kib_cast(32); + + command_table->Prdt[prdt_index].Dba = (UInt32) (buffer_phys & 0xFFFFFFFF); + command_table->Prdt[prdt_index].Dbau = (UInt32) (buffer_phys >> 32); + command_table->Prdt[prdt_index].Dbc = (UInt32) (chunk_size - 1); + command_table->Prdt[prdt_index].Ie = NO; + + buffer_phys += chunk_size; + bytes_remaining -= chunk_size; + + ++prdt_index; + } + + // Mark the last PRD entry, for the FIS to process the table. + command_table->Prdt[prdt_index - 1].Ie = YES; + + if (bytes_remaining > 0) { + kout << "ahci: AHCI PRDT overflow, cannot map full buffer.\r"; + err_global_get() = kErrorDisk; + rtl_dma_free(size_buffer); + + return; + } + + command_header->Prdtl = prdt_index; + command_header->HbaFlags.Struct.Cfl = sizeof(FisRegH2D) / sizeof(UInt32); + command_header->HbaFlags.Struct.Write = Write; + + volatile FisRegH2D* h2d_fis = (volatile FisRegH2D*) (&command_table->Cfis[0]); + + h2d_fis->FisType = kFISTypeRegH2D; + h2d_fis->CmdOrCtrl = CommandOrCTRL; + h2d_fis->Command = + (Identify ? kAHCICmdIdentify : (Write ? kAHCICmdWriteDmaEx : kAHCICmdReadDmaEx)); + + h2d_fis->Lba0 = (lba >> 0) & 0xFF; + h2d_fis->Lba1 = (lba >> 8) & 0xFF; + h2d_fis->Lba2 = (lba >> 16) & 0xFF; + h2d_fis->Lba3 = (lba >> 24) & 0xFF; + h2d_fis->Lba4 = (lba >> 32) & 0xFF; + h2d_fis->Lba5 = (lba >> 40) & 0xFF; + + h2d_fis->Device = 0; + + if (Identify) { + h2d_fis->CountLow = 1; + h2d_fis->CountHigh = 0; + } else { + h2d_fis->Device = kSATALBAMode; + h2d_fis->CountLow = (size_buffer / kAHCISectorSize) & 0xFF; + h2d_fis->CountHigh = ((size_buffer / kAHCISectorSize) >> 8) & 0xFF; + } + + rtl_dma_flush(ptr, size_buffer); + + // Issue command + kSATAHba->Ports[kSATAIndex].Ci = (1 << slot); + + timeout = 0UL; + + while (YES) { + if (timeout > kTimeout) { + kout << "ahci: disk-hangup, corrupted-disk.\r"; + err_global_get() = kErrorDiskIsCorrupted; + rtl_dma_free(size_buffer); + + return; + } + + ++timeout; + + if (!(kSATAHba->Ports[kSATAIndex].Ci & (1 << slot))) break; + } + + rtl_dma_flush(ptr, size_buffer); + + if (kSATAHba->Is & kSATAErrTaskFile) { + kout << "ahci: Task File Error during I/O.\r"; + + rtl_dma_free(size_buffer); + err_global_get() = kErrorDiskIsCorrupted; + + return; + } else { + if (!Write) { + rtl_dma_flush(ptr, size_buffer); + rt_copy_memory(ptr, buffer, size_buffer); + rtl_dma_flush(ptr, size_buffer); + } + + if ((kSATAHba->Ports[kSATAIndex].Tfd & (kSATASRBsy | kSATASRDrq)) == 0) { + goto ahci_io_end; + } else { + kout << "ahci: Disk still busy after command completion!\r"; + while (kSATAHba->Ports[kSATAIndex].Tfd & (kSATASRBsy | kSATASRDrq)) + ; + } + + ahci_io_end: + rtl_dma_free(size_buffer); + err_global_get() = kErrorSuccess; + } +} + +/*** + @brief Gets the number of sectors inside the drive. + @return Sector size in bytes. + */ +STATIC ATTRIBUTE(unused) SizeT drv_get_sector_count_ahci() { + return kSATASectorCount; +} + +/// @brief Get the drive size. +/// @return Disk size in bytes. +STATIC ATTRIBUTE(unused) SizeT drv_get_size_ahci() { + return drv_std_get_sector_count() * kAHCISectorSize; +} + +/// @brief Enable Host and probe using the IDENTIFY command. +STATIC BOOL ahci_enable_and_probe() { + if (kSATAHba->Cap == 0x0) return NO; + + kSATAHba->Ports[kSATAIndex].Cmd &= ~kSATAPxCmdFre; + kSATAHba->Ports[kSATAIndex].Cmd &= ~kSATAPxCmdST; + + while (YES) { + if (kSATAHba->Ports[kSATAIndex].Cmd & kSATAPxCmdCR) continue; + + if (kSATAHba->Ports[kSATAIndex].Cmd & kSATAPxCmdFR) continue; + + break; + } + + // Now we are ready. + + kSATAHba->Ports[kSATAIndex].Cmd |= kSATAPxCmdFre; + kSATAHba->Ports[kSATAIndex].Cmd |= kSATAPxCmdST; + + if (kSATAHba->Bohc & kSATABohcBiosOwned) { + kSATAHba->Bohc |= kSATABohcOSOwned; + + while (kSATAHba->Bohc & kSATABohcBiosOwned) { + ; + } + } + + drv_compute_disk_ahci(); + + return YES; +} + +STATIC Bool drv_init_command_structures_ahci() { + // Allocate 4KiB for Command List (32 headers) + VoidPtr clb_mem = rtl_dma_alloc(4096, 1024); + if (!clb_mem) { + kout << "Failed to allocate CLB memory!\r"; + return NO; + } + + UIntPtr clb_phys = HAL::mm_get_page_addr(clb_mem); + + kSATAHba->Ports[kSATAIndex].Clb = (UInt32) (clb_phys & 0xFFFFFFFF); + kSATAHba->Ports[kSATAIndex].Clbu = (UInt32) (clb_phys >> 32); + + // Clear it + rt_set_memory(clb_mem, 0, kib_cast(4)); + + // For each command slot (up to 32) + volatile HbaCmdHeader* header = (volatile HbaCmdHeader*) clb_mem; + + for (Int32 i = 0; i < 32; ++i) { + // Allocate 4KiB for Command Table + VoidPtr ct_mem = rtl_dma_alloc(4096, 128); + if (!ct_mem) { + (Void)(kout << "Failed to allocate CTB memory for slot " << hex_number(i)); + kout << "!\r"; + return NO; + } + + UIntPtr ct_phys = HAL::mm_get_page_addr(ct_mem); + + header[i].Ctba = (UInt32) (ct_phys & 0xFFFFFFFF); + header[i].Ctbau = (UInt32) (ct_phys >> 32); + + // Clear the command table + rt_set_memory((VoidPtr) ct_mem, 0, 4096); + } + + return YES; +} + +/// @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) { + /// AMLALE: TODO: Iterator is good enough, but we need to expand it. + PCI::Iterator iterator(Types::PciDeviceKind::MassStorageController, 0x00); + + 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) { + kSATADev.EnableMmio(); + kSATADev.BecomeBusMaster(); + + HbaMem* mem_ahci = (HbaMem*) kSATADev.Bar(kSATABar5); + + HAL::mm_map_page( + (VoidPtr) mem_ahci, (VoidPtr) mem_ahci, + HAL::kMMFlagsPresent | HAL::kMMFlagsWr | HAL::kMMFlagsPCD | HAL::kMMFlagsPwt); + + UInt32 ports_implemented = mem_ahci->Pi; + UInt16 ahci_index = 0; + + pi = ports_implemented; + + const UInt16 kSATAMaxPortsImplemented = ports_implemented; + const UInt32 kSATASignature = kSATASig; + const UInt32 kSATAPISignature = kSATAPISig; + const UInt8 kSATAPresent = 0x03; + const UInt8 kSATAIPMActive = 0x01; + + if (kSATAMaxPortsImplemented < 1) continue; + + 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) || + (atapi && kSATAPISignature == mem_ahci->Ports[ahci_index].Sig)) { + kSATAIndex = ahci_index; + kSATAHba = mem_ahci; + + if (!drv_init_command_structures_ahci()) { + err_global_get() = kErrorDisk; + } + + goto success_hba_fetch; + } + + ports_implemented >>= 1; + ++ahci_index; + } + } + } + + err_global_get() = kErrorDisk; + + return NO; + +success_hba_fetch: + if (ahci_enable_and_probe()) { + err_global_get() = kErrorSuccess; + } + + return err_global_get() == kErrorSuccess; +} + +/// @brief Checks if an AHCI device is detected. +/// @return Either if detected, or not found. +Bool drv_std_detected_ahci() { + return kSATADev.DeviceId() != (UShort) PCI::PciConfigKind::Invalid && + kSATADev.Bar(kSATABar5) != 0; +} + +// ================================================================================================ + +// +/// @note This applies only if we compile with AHCI as a default disk driver. +// + +// ================================================================================================ + +#ifdef __AHCI__ + +//////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////// +Void drv_std_write(UInt64 lba, Char* buffer, SizeT sector_sz, SizeT size_buffer) { + drv_std_input_output_ahci<YES, YES, NO>(lba, reinterpret_cast<UInt8*>(buffer), sector_sz, + size_buffer); +} + +//////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////// +Void drv_std_read(UInt64 lba, Char* buffer, SizeT sector_sz, SizeT size_buffer) { + drv_std_input_output_ahci<NO, YES, NO>(lba, reinterpret_cast<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_std_get_sector_count() { + return drv_get_sector_count_ahci(); +} + +//////////////////////////////////////////////////// +/// @brief Get the drive size. +/// @return Disk size in bytes. +//////////////////////////////////////////////////// +SizeT drv_std_get_size() { + return drv_get_size_ahci(); +} + +#endif // ifdef __AHCI__ + +namespace Kernel { +/// @brief Initialize an AHCI device (StorageKit) +UInt16 sk_init_ahci_device(BOOL atapi) { + UInt16 pi = 0; + + if (drv_std_init_ahci(pi, atapi)) kSATAPortsImplemented = pi; + + return pi; +} + +/// @brief Implementation details namespace. +namespace Detail { + /// @brief Read AHCI device. + /// @param self device + /// @param mnt mounted disk. + STATIC Void sk_io_read_ahci(DeviceInterface<IMountpoint*>* self, IMountpoint* mnt) { + AHCIDeviceInterface* dev = (AHCIDeviceInterface*) self; + + err_global_get() = kErrorDisk; + + if (!dev) return; + + auto disk = mnt->GetAddressOf(dev->GetIndex()); + + if (!disk) return; + + err_global_get() = kErrorSuccess; + + drv_std_input_output_ahci<NO, YES, NO>(disk->fPacket.fPacketLba / kAHCISectorSize, + (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(DeviceInterface<IMountpoint*>* self, IMountpoint* mnt) { + AHCIDeviceInterface* dev = (AHCIDeviceInterface*) self; + + err_global_get() = kErrorDisk; + + if (!dev) return; + + auto disk = mnt->GetAddressOf(dev->GetIndex()); + + if (!disk) return; + + err_global_get() = kErrorSuccess; + + drv_std_input_output_ahci<YES, YES, NO>(disk->fPacket.fPacketLba / kAHCISectorSize, + (UInt8*) disk->fPacket.fPacketContent, kAHCISectorSize, + disk->fPacket.fPacketSize); + } +} // namespace Detail + +/// @brief Acquires a new AHCI device with drv_index in mind. +/// @param drv_index The drive index to assign. +/// @return A wrapped device interface if successful, or error code. +ErrorOr<AHCIDeviceInterface> sk_acquire_ahci_device(UInt32 drv_index) { + if (!drv_std_detected_ahci()) return ErrorOr<AHCIDeviceInterface>(kErrorDisk); + + AHCIDeviceInterface device(Detail::sk_io_read_ahci, Detail::sk_io_write_ahci); + + device.SetPortsImplemented(kSATAPortsImplemented); + device.SetIndex(drv_index); + + return ErrorOr<AHCIDeviceInterface>(device); +} +} // namespace Kernel diff --git a/src/kernel/HALKit/AMD64/Storage/DMA+Generic.cc b/src/kernel/HALKit/AMD64/Storage/DMA+Generic.cc new file mode 100644 index 00000000..cf6147d9 --- /dev/null +++ b/src/kernel/HALKit/AMD64/Storage/DMA+Generic.cc @@ -0,0 +1,199 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +/** + * @file DMA+Generic.cc + * @author Amlal El Mahrouss (amlal@nekernel.org) + * @brief ATA driver (DMA mode). + * @version 0.1 + * @date 2024-02-02 + * + * @copyright Copyright (c) Amlal El Mahrouss + * + */ + +#include <ArchKit/ArchKit.h> +#include <KernelKit/PCI/Iterator.h> +#include <modules/ATA/ATA.h> + +#if defined(__ATA_DMA__) + +#define kATADataLen (256) + +using namespace Kernel; +using namespace Kernel::HAL; + +/// BUGS: 0 + +STATIC Boolean kATADetected = false; +STATIC Int32 kATADeviceType ATTRIBUTE(unused) = kATADeviceCount; +STATIC UInt16 kATAIdentifyData[kATADataLen] = {0}; +STATIC Kernel::PCI::Device kATADevice; +STATIC Char kATADiskModel[50] ATTRIBUTE(unused) = {"GENERIC DMA"}; + +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) { + NE_UNUSED(Bus); + NE_UNUSED(Drive); + NE_UNUSED(OutBus); + NE_UNUSED(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. + + /// IDE interface + if (kATADevice.Subclass() == 0x01) { + break; + } + } + + return NO; +} + +namespace Kernel::Detail { +struct PRDEntry final { + UInt32 mAddress; + UInt16 mByteCount; + UInt16 mFlags; /// @param PRD flags, set to 0x8000 to indicate end of prd. +}; +} // namespace Kernel::Detail + +static UIntPtr kReadAddr = mib_cast(2); +static UIntPtr kWriteAddr = mib_cast(2) + kib_cast(64); + +Void drv_std_read(UInt64 Lba, UInt16 IO, UInt8 Master, Char* Buf, SizeT SectorSz, SizeT Size) { + Lba /= SectorSz; + + if (Size > kib_cast(64)) return; + + 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); + + Kernel::Detail::PRDEntry* prd = + (Kernel::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)) return; + + 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); + + Kernel::Detail::PRDEntry* prd = (Kernel::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 Gets the number of sectors inside the drive. + @return Number of sectors, or zero. +*/ +/***********************************************************************************/ +Kernel::SizeT drv_std_get_sector_count() { + return (kATAIdentifyData[61] << 16) | kATAIdentifyData[60]; +} + +/***********************************************************************************/ +/// @brief Get the size of the current drive. +/***********************************************************************************/ +Kernel::SizeT drv_std_get_size() { + return (drv_std_get_sector_count()) * kATASectorSize; +} + +#endif /* ifdef __ATA_DMA__ */ diff --git a/src/kernel/HALKit/AMD64/Storage/NVME+Generic.cc b/src/kernel/HALKit/AMD64/Storage/NVME+Generic.cc new file mode 100644 index 00000000..88f95a86 --- /dev/null +++ b/src/kernel/HALKit/AMD64/Storage/NVME+Generic.cc @@ -0,0 +1,9 @@ +/* ======================================== + + Copyright (C) 2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include <modules/NVME/NVME.h> + +using namespace Kernel; diff --git a/src/kernel/HALKit/AMD64/Storage/PIO+Generic.cc b/src/kernel/HALKit/AMD64/Storage/PIO+Generic.cc new file mode 100644 index 00000000..0516be39 --- /dev/null +++ b/src/kernel/HALKit/AMD64/Storage/PIO+Generic.cc @@ -0,0 +1,278 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +/** + * @file PIO+Generic.cc + * @author Amlal El Mahrouss (amlal@nekernel.org) + * @brief ATA driver (PIO mode). + * @version 0.1 + * @date 2024-02-02 + * + * @copyright Copyright (c) Amlal El Mahrouss + * + */ + +#include <ArchKit/ArchKit.h> +#include <KernelKit/DriveMgr.h> +#include <StorageKit/ATA.h> +#include <modules/ATA/ATA.h> + +using namespace Kernel; +using namespace Kernel::HAL; + +/// BUGS: 0 + +#define kATADataLen 256 + +STATIC Boolean kATADetected = false; +STATIC UInt16 kATAIdentifyData[kATADataLen] = {0}; +STATIC Char kATADiskModel[50] = {"GENERIC PIO"}; + +static Boolean drv_pio_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; +} + +STATIC Void drv_pio_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_pio_std_init(UInt16 Bus, UInt8 Drive, UInt16& OutBus, UInt8& OutMaster) { + UInt16 IO = Bus; + + NE_UNUSED(Drive); + + drv_pio_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; + + drv_pio_std_select(IO); + + rt_out8(OutBus + ATA_REG_COMMAND, ATA_CMD_IDENTIFY); + + while (!(rt_in8(IO + ATA_REG_STATUS) & ATA_SR_DRQ)) + ; + + /// fetch serial info + /// model, speed, number of sectors... + + for (SizeT i = 0ul; i < kATADataLen; ++i) { + kATAIdentifyData[i] = rt_in16(OutBus + ATA_REG_DATA); + } + + for (Int32 i = 0; i < 20; i++) { + kATADiskModel[i * 2] = (kATAIdentifyData[27 + i] >> 8) & 0xFF; + kATADiskModel[i * 2 + 1] = kATAIdentifyData[27 + i] & 0xFF; + } + + kATADiskModel[40] = '\0'; + + (Void)(kout << "Drive Model: " << kATADiskModel << kendl); + + return true; +} + +Void drv_pio_std_read(UInt64 Lba, UInt16 IO, UInt8 Master, Char* Buf, SizeT SectorSz, SizeT Size) { + Lba /= SectorSz; + + UInt8 Command = ((!Master) ? 0xE0 : 0xF0); + + drv_pio_std_wait_io(IO); + drv_pio_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); + + while (!(rt_in8(IO + ATA_REG_STATUS) & ATA_SR_DRQ)) + ; + + for (SizeT IndexOff = 0; IndexOff < Size; IndexOff += 2) { + drv_pio_std_wait_io(IO); + + auto in = rt_in16(IO + ATA_REG_DATA); + + Buf[IndexOff] = in & 0xFF; + Buf[IndexOff + 1] = (in >> 8) & 0xFF; + } +} + +Void drv_pio_std_write(UInt64 Lba, UInt16 IO, UInt8 Master, Char* Buf, SizeT SectorSz, SizeT Size) { + Lba /= SectorSz; + + UInt8 Command = ((!Master) ? 0xE0 : 0xF0); + + drv_pio_std_wait_io(IO); + drv_pio_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); + + while (!(rt_in8(IO + ATA_REG_STATUS) & ATA_SR_DRQ)) + ; + + for (SizeT IndexOff = 0; IndexOff < Size; IndexOff += 2) { + drv_pio_std_wait_io(IO); + + UInt8 low = (UInt8) Buf[IndexOff]; + UInt8 high = (IndexOff + 1 < Size) ? (UInt8) Buf[IndexOff + 1] : 0; + UInt16 packed = (high << 8) | low; + + rt_out16(IO + ATA_REG_DATA, packed); + } +} + +/// @brief is ATA detected? +Boolean drv_pio_std_detected(Void) { + return kATADetected; +} + +/*** + @brief Getter, gets the number of sectors inside the drive. + */ +SizeT drv_pio_get_sector_count() { + return (kATAIdentifyData[61] << 16) | kATAIdentifyData[60]; +} + +/// @brief Get the drive size. +SizeT drv_pio_get_size() { + return (drv_pio_get_sector_count()) * kATASectorSize; +} + +namespace Kernel { +/// @brief Initialize an PIO device (StorageKit function) +/// @param is_master is the current PIO master? +/// @return [io:master] for PIO device. +BOOL sk_init_ata_device(BOOL is_master, UInt16& io, UInt8& master) { + return drv_pio_std_init(ATA_SECONDARY_IO, is_master, io, master); +} + +/// @brief Implementation details namespace. +namespace Detail { + /// @brief Read PIO device. + /// @param self device + /// @param mnt mounted disk. + STATIC Void sk_io_read_pio(DeviceInterface<IMountpoint*>* self, IMountpoint* mnt) { + ATADeviceInterface* dev = (ATADeviceInterface*) self; + + err_global_get() = kErrorDisk; + + if (!dev) return; + + auto disk = mnt->GetAddressOf(dev->GetIndex()); + + if (!disk) return; + + err_global_get() = kErrorSuccess; + + drv_pio_std_read(disk->fPacket.fPacketLba, dev->GetIO(), dev->GetMaster(), + (Char*) disk->fPacket.fPacketContent, kATASectorSize, + disk->fPacket.fPacketSize); + } + + /// @brief Write PIO device. + /// @param self device + /// @param mnt mounted disk. + STATIC Void sk_io_write_pio(DeviceInterface<IMountpoint*>* self, IMountpoint* mnt) { + ATADeviceInterface* dev = (ATADeviceInterface*) self; + + err_global_get() = kErrorDisk; + + if (!dev) return; + + auto disk = mnt->GetAddressOf(dev->GetIndex()); + + if (!disk) return; + + err_global_get() = kErrorSuccess; + + drv_pio_std_write(disk->fPacket.fPacketLba, dev->GetIO(), dev->GetMaster(), + (Char*) disk->fPacket.fPacketContent, kATASectorSize, + disk->fPacket.fPacketSize); + } +} // namespace Detail + +/// @brief Acquires a new PIO device with drv_index in mind. +/// @param drv_index The drive index to assign. +/// @return A wrapped device interface if successful, or error code. +ErrorOr<ATADeviceInterface> sk_acquire_ata_device(Int32 drv_index) { + /// here we don't check if we probed ATA, since we'd need to grab IO after that. + ATADeviceInterface device(Detail::sk_io_read_pio, Detail::sk_io_write_pio); + + device.SetIndex(drv_index); + + return ErrorOr<ATADeviceInterface>(device); +} +} // namespace Kernel + +#ifdef __ATA_PIO__ + +Void drv_std_read(UInt64 Lba, UInt16 IO, UInt8 Master, Char* Buf, SizeT SectorSz, SizeT Size) { + drv_pio_std_read(Lba, IO, Master, Buf, SectorSz, Size); +} + +Void drv_std_write(UInt64 Lba, UInt16 IO, UInt8 Master, Char* Buf, SizeT SectorSz, SizeT Size) { + drv_pio_std_write(Lba, IO, Master, Buf, SectorSz, Size); +} + +SizeT drv_std_get_size() { + return drv_pio_get_size(); +} + +SizeT drv_std_get_sector_count() { + return drv_pio_get_sector_count(); +} + +Boolean drv_std_init(UInt16 Bus, UInt8 Drive, UInt16& OutBus, UInt8& OutMaster) { + return drv_pio_std_init(Bus, Drive, OutBus, OutMaster); +} + +#endif
\ No newline at end of file diff --git a/src/kernel/HALKit/AMD64/Storage/SCSI+Generic.cc b/src/kernel/HALKit/AMD64/Storage/SCSI+Generic.cc new file mode 100644 index 00000000..0200ec5a --- /dev/null +++ b/src/kernel/HALKit/AMD64/Storage/SCSI+Generic.cc @@ -0,0 +1,13 @@ +/* ======================================== + + Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license. + +======================================== */ + +#include <modules/SCSI/SCSI.h> + +using namespace Kernel; + +///! @brief ATAPI SCSI packet. +const ATTRIBUTE(unused) scsi_packet_type_12 kCDRomPacketTemplate = {0x43, 0, 1, 0, 0, 0, + 0, 12, 0x40, 0, 0}; |
