diff --git a/Disk/AHCI/AHCI.cpp b/Disk/AHCI/AHCI.cpp index 6dc2adf..45e5f27 100644 --- a/Disk/AHCI/AHCI.cpp +++ b/Disk/AHCI/AHCI.cpp @@ -31,6 +31,145 @@ KernelAPI *KAPI; /* --------------------------------------------------------------------------------------------------------- */ +#define ATA_DEV_BUSY 0x80 +#define ATA_DEV_DRQ 0x08 +#define ATA_CMD_WRITE_DMA_EX 0x35 +#define ATA_CMD_READ_DMA_EX 0x25 +#define HBA_PxIS_TFES (1 << 30) + +#define HBA_PORT_DEV_PRESENT 0x3 +#define HBA_PORT_IPM_ACTIVE 0x1 +#define SATA_SIG_ATAPI 0xEB140101 +#define SATA_SIG_ATA 0x00000101 +#define SATA_SIG_SEMB 0xC33C0101 +#define SATA_SIG_PM 0x96690101 + +#define HBA_PxCMD_CR 0x8000 +#define HBA_PxCMD_FRE 0x0010 +#define HBA_PxCMD_ST 0x0001 +#define HBA_PxCMD_FR 0x4000 + +enum PortType +{ + None = 0, + SATA = 1, + SEMB = 2, + PM = 3, + SATAPI = 4, +}; + +enum FIS_TYPE +{ + FIS_TYPE_REG_H2D = 0x27, + FIS_TYPE_REG_D2H = 0x34, + FIS_TYPE_DMA_ACT = 0x39, + FIS_TYPE_DMA_SETUP = 0x41, + FIS_TYPE_DATA = 0x46, + FIS_TYPE_BIST = 0x58, + FIS_TYPE_PIO_SETUP = 0x5F, + FIS_TYPE_DEV_BITS = 0xA1, +}; + +struct HBAPort +{ + uint32_t CommandListBase; + uint32_t CommandListBaseUpper; + uint32_t FISBaseAddress; + uint32_t FISBaseAddressUpper; + uint32_t InterruptStatus; + uint32_t InterruptEnable; + uint32_t CommandStatus; + uint32_t Reserved0; + uint32_t TaskFileData; + uint32_t Signature; + uint32_t SataStatus; + uint32_t SataControl; + uint32_t SataError; + uint32_t SataActive; + uint32_t CommandIssue; + uint32_t SataNotification; + uint32_t FISSwitchControl; + uint32_t Reserved1[11]; + uint32_t Vendor[4]; +}; + +struct HBAMemory +{ + uint32_t HostCapability; + uint32_t GlobalHostControl; + uint32_t InterruptStatus; + uint32_t PortsImplemented; + uint32_t Version; + uint32_t CCCControl; + uint32_t CCCPorts; + uint32_t EnclosureManagementLocation; + uint32_t EnclosureManagementControl; + uint32_t HostCapabilitiesExtended; + uint32_t BIOSHandoffControlStatus; + uint8_t Reserved0[0x74]; + uint8_t Vendor[0x60]; + HBAPort Ports[1]; +}; + +struct HBACommandHeader +{ + uint8_t CommandFISLength : 5; + uint8_t ATAPI : 1; + uint8_t Write : 1; + uint8_t Prefetchable : 1; + uint8_t Reset : 1; + uint8_t BIST : 1; + uint8_t ClearBusy : 1; + uint8_t Reserved0 : 1; + uint8_t PortMultiplier : 4; + uint16_t PRDTLength; + uint32_t PRDBCount; + uint32_t CommandTableBaseAddress; + uint32_t CommandTableBaseAddressUpper; + uint32_t Reserved1[4]; +}; + +struct HBAPRDTEntry +{ + uint32_t DataBaseAddress; + uint32_t DataBaseAddressUpper; + uint32_t Reserved0; + uint32_t ByteCount : 22; + uint32_t Reserved1 : 9; + uint32_t InterruptOnCompletion : 1; +}; + +struct HBACommandTable +{ + uint8_t CommandFIS[64]; + uint8_t ATAPICommand[16]; + uint8_t Reserved[48]; + HBAPRDTEntry PRDTEntry[]; +}; + +struct FIS_REG_H2D +{ + uint8_t FISType; + uint8_t PortMultiplier : 4; + uint8_t Reserved0 : 3; + uint8_t CommandControl : 1; + uint8_t Command; + uint8_t FeatureLow; + uint8_t LBA0; + uint8_t LBA1; + uint8_t LBA2; + uint8_t DeviceRegister; + uint8_t LBA3; + uint8_t LBA4; + uint8_t LBA5; + uint8_t FeatureHigh; + uint8_t CountLow; + uint8_t CountHigh; + uint8_t ISOCommandCompletion; + uint8_t Control; + uint8_t Reserved1[4]; +}; + struct BARData { uint8_t Type; @@ -38,8 +177,194 @@ struct BARData uint64_t MemoryBase; }; +void *operator new(size_t Size) { return KAPI->Memory.RequestPage(Size / KAPI->Memory.PageSize + 1); } + +class Port +{ +public: + HBAPort *HBAPortPtr; + PortType AHCIPortType; + uint8_t *Buffer; + uint8_t PortNumber; + + void StartCMD() + { + while (HBAPortPtr->CommandStatus & HBA_PxCMD_CR) + ; + HBAPortPtr->CommandStatus |= HBA_PxCMD_FRE; + HBAPortPtr->CommandStatus |= HBA_PxCMD_ST; + } + + void StopCMD() + { + HBAPortPtr->CommandStatus &= ~HBA_PxCMD_ST; + HBAPortPtr->CommandStatus &= ~HBA_PxCMD_FRE; + while (true) + { + if (HBAPortPtr->CommandStatus & HBA_PxCMD_FR) + continue; + if (HBAPortPtr->CommandStatus & HBA_PxCMD_CR) + continue; + break; + } + } + + void Configure() + { + StopCMD(); + void *NewBase = KAPI->Memory.RequestPage(1); + HBAPortPtr->CommandListBase = (uint32_t)(uint64_t)NewBase; + HBAPortPtr->CommandListBaseUpper = (uint32_t)((uint64_t)NewBase >> 32); + KAPI->Util.memset(reinterpret_cast(HBAPortPtr->CommandListBase), 0, 1024); + + void *FISBase = KAPI->Memory.RequestPage(1); + HBAPortPtr->FISBaseAddress = (uint32_t)(uint64_t)FISBase; + HBAPortPtr->FISBaseAddressUpper = (uint32_t)((uint64_t)FISBase >> 32); + KAPI->Util.memset(FISBase, 0, 256); + + HBACommandHeader *CommandHeader = (HBACommandHeader *)((uint64_t)HBAPortPtr->CommandListBase + ((uint64_t)HBAPortPtr->CommandListBaseUpper << 32)); + for (int i = 0; i < 32; i++) + { + CommandHeader[i].PRDTLength = 8; + void *CommandTableAddress = KAPI->Memory.RequestPage(1); + uint64_t Address = (uint64_t)CommandTableAddress + (i << 8); + CommandHeader[i].CommandTableBaseAddress = (uint32_t)(uint64_t)Address; + CommandHeader[i].CommandTableBaseAddressUpper = (uint32_t)((uint64_t)Address >> 32); + KAPI->Util.memset(CommandTableAddress, 0, 256); + } + StartCMD(); + } + + bool ReadWrite(uint64_t Sector, uint32_t SectorCount, uint8_t *Buffer, bool Write) + { + if (this->PortNumber == PortType::SATAPI && Write) + { + // err("SATAPI port does not support write."); + KAPI->Util.DebugPrint(((char *)"SATAPI port does not support write." + KAPI->Info.Offset), KAPI->Info.DriverUID); + return false; + } + + uint32_t SectorL = (uint32_t)Sector; + uint32_t SectorH = (uint32_t)(Sector >> 32); + + HBAPortPtr->InterruptStatus = (uint32_t)-1; // Clear pending interrupt bits + + HBACommandHeader *CommandHeader = reinterpret_cast(HBAPortPtr->CommandListBase); + CommandHeader->CommandFISLength = sizeof(FIS_REG_H2D) / sizeof(uint32_t); + if (Write) + CommandHeader->Write = 1; + else + CommandHeader->Write = 0; + CommandHeader->PRDTLength = 1; + + HBACommandTable *CommandTable = reinterpret_cast(CommandHeader->CommandTableBaseAddress); + KAPI->Util.memset(CommandTable, 0, sizeof(HBACommandTable) + (CommandHeader->PRDTLength - 1) * sizeof(HBAPRDTEntry)); + + CommandTable->PRDTEntry[0].DataBaseAddress = (uint32_t)(uint64_t)Buffer; + CommandTable->PRDTEntry[0].DataBaseAddressUpper = (uint32_t)((uint64_t)Buffer >> 32); + CommandTable->PRDTEntry[0].ByteCount = (SectorCount << 9) - 1; // 512 bytes per sector + CommandTable->PRDTEntry[0].InterruptOnCompletion = 1; + + FIS_REG_H2D *CommandFIS = (FIS_REG_H2D *)(&CommandTable->CommandFIS); + + CommandFIS->FISType = FIS_TYPE_REG_H2D; + CommandFIS->CommandControl = 1; + if (Write) + CommandFIS->Command = ATA_CMD_WRITE_DMA_EX; + else + CommandFIS->Command = ATA_CMD_READ_DMA_EX; + + CommandFIS->LBA0 = (uint8_t)SectorL; + CommandFIS->LBA1 = (uint8_t)(SectorL >> 8); + CommandFIS->LBA2 = (uint8_t)(SectorL >> 16); + CommandFIS->LBA3 = (uint8_t)SectorH; + CommandFIS->LBA4 = (uint8_t)(SectorH >> 8); + CommandFIS->LBA5 = (uint8_t)(SectorH >> 16); + + CommandFIS->DeviceRegister = 1 << 6; // LBA mode + CommandFIS->CountLow = SectorCount & 0xFF; + CommandFIS->CountHigh = (SectorCount >> 8) & 0xFF; + + uint64_t Spin = 0; + + while ((HBAPortPtr->TaskFileData & (ATA_DEV_BUSY | ATA_DEV_DRQ)) && Spin < 1000000) + Spin++; + if (Spin == 1000000) + { + // err("Port not responding."); + KAPI->Util.DebugPrint(((char *)"Port not responding." + KAPI->Info.Offset), KAPI->Info.DriverUID); + return false; + } + + HBAPortPtr->CommandIssue = 1; + + Spin = 0; + int TryCount = 0; + + while (true) + { + if (Spin > 100000000) + { + // err("Port %d not responding. (%d)", this->PortNumber, TryCount); + KAPI->Util.DebugPrint(((char *)"Port not responding." + KAPI->Info.Offset), KAPI->Info.DriverUID); + Spin = 0; + TryCount++; + if (TryCount > 10) + return false; + } + if ((HBAPortPtr->CommandIssue == 0)) + break; + Spin++; + if (HBAPortPtr->InterruptStatus & HBA_PxIS_TFES) + { + // err("Error reading/writing (%d).", Write); + KAPI->Util.DebugPrint(((char *)"Error reading/writing." + KAPI->Info.Offset), KAPI->Info.DriverUID); + return false; + } + } + + return true; + } +}; + +HBAMemory *ABAR; +Port *Ports[32]; +uint8_t PortCount = 0; + PCIDeviceHeader *PCIBaseAddress; +const char *PortTypeName[] = {"None", + "SATA", + "SEMB", + "PM", + "SATAPI"}; + +PortType CheckPortType(HBAPort *Port) +{ + uint32_t SataStatus = Port->SataStatus; + uint8_t InterfacePowerManagement = (SataStatus >> 8) & 0b111; + uint8_t DeviceDetection = SataStatus & 0b111; + + if (DeviceDetection != HBA_PORT_DEV_PRESENT) + return PortType::None; + if (InterfacePowerManagement != HBA_PORT_IPM_ACTIVE) + return PortType::None; + + switch (Port->Signature) + { + case SATA_SIG_ATAPI: + return PortType::SATAPI; + case SATA_SIG_ATA: + return PortType::SATA; + case SATA_SIG_PM: + return PortType::PM; + case SATA_SIG_SEMB: + return PortType::SEMB; + default: + return PortType::None; + } +} + int DriverEntry(KernelAPI *Data) { if (!Data) @@ -63,14 +388,45 @@ int CallbackHandler(KernelCallback *Data) { KAPI->Util.DebugPrint(((char *)"Kernel received configuration data." + KAPI->Info.Offset), KAPI->Info.DriverUID); PCIBaseAddress = reinterpret_cast(Data->RawPtr); - break; - } - case InterruptReason: - { + ABAR = reinterpret_cast(((PCIHeader0 *)PCIBaseAddress)->BAR5); + KAPI->Memory.Map((void *)ABAR, (void *)ABAR, (1 << 1)); + + uint32_t PortsImplemented = ABAR->PortsImplemented; + for (int i = 0; i < 32; i++) + { + if (PortsImplemented & (1 << i)) + { + PortType portType = CheckPortType(&ABAR->Ports[i]); + if (portType == PortType::SATA || portType == PortType::SATAPI) + { + // trace("%s drive found at port %d", PortTypeName[portType], i); + KAPI->Util.DebugPrint(((char *)"SATA drive found." + KAPI->Info.Offset), KAPI->Info.DriverUID); + Ports[PortCount] = new Port; + Ports[PortCount]->AHCIPortType = portType; + Ports[PortCount]->HBAPortPtr = &ABAR->Ports[i]; + Ports[PortCount]->PortNumber = PortCount; + Ports[PortCount]->Buffer = static_cast(KAPI->Memory.RequestPage(1)); + PortCount++; + } + else + { + if (portType != PortType::None) + KAPI->Util.DebugPrint(((char *)"Unsupported port type found." + KAPI->Info.Offset), KAPI->Info.DriverUID); + // warn("Unsupported drive type %s found at port %d", PortTypeName[portType], i); + } + } + } + + for (int i = 0; i < PortCount; i++) + Ports[i]->Configure(); break; } case SendReason: { + Ports[Data->DiskCallback.Port]->ReadWrite(Data->DiskCallback.Sector, + Data->DiskCallback.SectorCount, + Data->DiskCallback.Buffer, + Data->DiskCallback.Write); break; } default: