From: Amine Khaldi Date: Fri, 27 Jan 2017 10:11:30 +0000 (+0000) Subject: [STORAHCI] Merge Storport Miniport driver by Aman Priyadarshi in GSoC. X-Git-Tag: ReactOS-0.4.4-FOSDEM2017~1^2~31 X-Git-Url: https://git.reactos.org/?p=reactos.git;a=commitdiff_plain;h=92d1110c006fc6e89b0241a80ce03027993d22d8;hp=8d91d517107f92280f6a98018250c7a002a4c03d [STORAHCI] Merge Storport Miniport driver by Aman Priyadarshi in GSoC. svn path=/trunk/; revision=73604 --- diff --git a/reactos/drivers/storage/CMakeLists.txt b/reactos/drivers/storage/CMakeLists.txt index ff51e59f461..44cd86852bd 100644 --- a/reactos/drivers/storage/CMakeLists.txt +++ b/reactos/drivers/storage/CMakeLists.txt @@ -6,3 +6,4 @@ add_subdirectory(floppy) add_subdirectory(ide) add_subdirectory(port) add_subdirectory(scsiport) +#add_subdirectory(storahci) diff --git a/reactos/drivers/storage/storahci/CMakeLists.txt b/reactos/drivers/storage/storahci/CMakeLists.txt new file mode 100644 index 00000000000..039ed8d0c4e --- /dev/null +++ b/reactos/drivers/storage/storahci/CMakeLists.txt @@ -0,0 +1,11 @@ +add_definitions(-DDEBUG) + +list(APPEND SOURCE + storahci.c) + +add_library(storahci SHARED ${SOURCE} storahci.rc) + +set_module_type(storahci kernelmodedriver) +add_importlibs(storahci storport ntoskrnl hal) +add_cd_file(TARGET storahci DESTINATION reactos/system32/drivers NO_CAB FOR all) +add_registry_inf(storahci.inf) diff --git a/reactos/drivers/storage/storahci/Notes.txt b/reactos/drivers/storage/storahci/Notes.txt new file mode 100644 index 00000000000..cac43532145 --- /dev/null +++ b/reactos/drivers/storage/storahci/Notes.txt @@ -0,0 +1,168 @@ +AhciPortInitialize + Flags + IMPLEMENTED + FULLY_SUPPORTED + TESTED + Comment + NONE + +AhciAllocateResourceForAdapter + Flags + IMPLEMENTED + FULLY_SUPPORTED + TESTED + Comment + NONE + +AhciHwInitialize + Flags + IMPLEMENTED + FULLY_SUPPORTED + TESTED + Comment + NONE + +AhciInterruptHandler + Flags + NOT_IMPLEMENTED + TESTED + Comment + Fatal Error not supported + Error Recovery not supported + Complete Request Routine + +AhciHwInterrupt + Flags + IMPLEMENTED + FULLY_SUPPORTED + TESTED + Comment + NONE + +AhciHwStartIo + Flags + IMPLEMENTED + TESTED + Comment + Adapter based IO request not supported + Need to implement more srb functions + +AhciHwResetBus + Flags + NOT_IMPLEMENTED + Comment + Adapter master bus reset not implemented + +AhciHwFindAdapter + Flags + IMPLEMENTED + FULLY_SUPPORTED + TESTED + Comment + NONE + +DriverEntry + Flags + IMPLEMENTED + FULLY_SUPPORTED + TESTED + Comment + NONE + +AhciATA_CFIS + Flags + IMPLEMENTED + Comment + Need to implement NCQ + +AhciATAPI_CFIS + Flags + NOT_IMPLEMENTED + Comment + Need to configure command table according to Srb function + +AhciBuild_PRDT + Flags + IMPLEMENTED + Comment + NONE + +AhciProcessSrb + Flags + IMPLEMENTED + Comment + Only ATA/ATAPI type CFIS supported + Also I am not sure about FIS alignment in SrbExtension. + +AhciActivatePort + Flags + IMPLEMENTED + Comment + NCQ not supported + +AhciProcessIO + Flags + IMPLEMENTED + FULLY_SUPPORTED + TESTED + Comment + NONE + +DeviceInquiryRequest + Flags + IMPLEMENTED + TESTED + Comment + EVPD is not sending Data buffer for IDENTIFY command. + Need to implement VPD + +AhciAdapterReset + Flags + NOT_IMPLEMENTED + Comment + NONE + +AhciZeroMemory + Flags + IMPLEMENTED + FULLY_SUPPORTED + TESTED + Comment + NONE + +IsPortValid + Flags + IMPLEMENTED + FULLY_SUPPORTED + TESTED + Comment + NONE + +AddQueue + Flags + IMPLEMENTED + FULLY_SUPPORTED + TESTED + Comment + NONE + +RemoveQueue + Flags + IMPLEMENTED + FULLY_SUPPORTED + TESTED + Comment + NONE + +AhciCompleteIssuedSrb + Flags + IMPLEMENTED + FULLY_SUPPORTED + Comment + NONE + +InquiryCompletion + Flags + NOT_IMPLEMENTED + Comment + NONE \ No newline at end of file diff --git a/reactos/drivers/storage/storahci/makefile b/reactos/drivers/storage/storahci/makefile new file mode 100644 index 00000000000..756c29992d8 --- /dev/null +++ b/reactos/drivers/storage/storahci/makefile @@ -0,0 +1,2 @@ +MINIMUM_NT_TARGET_VERSION=0x502 +!INCLUDE $(NTMAKEENV)\makefile.def \ No newline at end of file diff --git a/reactos/drivers/storage/storahci/sources b/reactos/drivers/storage/storahci/sources new file mode 100644 index 00000000000..a1772aeb55f --- /dev/null +++ b/reactos/drivers/storage/storahci/sources @@ -0,0 +1,10 @@ +TARGETNAME = storahci +TARGETTYPE = MINIPORT + +MSC_WARNING_LEVEL=/W4 +TARGETLIBS=$(DDK_LIB_PATH)\storport.lib + +INCLUDES = %BUILD%\inc +LIBS = %BUILD%\lib +SOURCES = storahci.c \ + storahci.rc diff --git a/reactos/drivers/storage/storahci/storahci.c b/reactos/drivers/storage/storahci/storahci.c new file mode 100644 index 00000000000..97138a6a0a4 --- /dev/null +++ b/reactos/drivers/storage/storahci/storahci.c @@ -0,0 +1,2570 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: GNU GPLv2 only as published by the Free Software Foundation + * PURPOSE: To Implement AHCI Miniport driver targeting storport NT 5.2 + * PROGRAMMERS: Aman Priyadarshi (aman.eureka@gmail.com) + */ + +#include "storahci.h" + +/** + * @name AhciPortInitialize + * @implemented + * + * Initialize port by setting up PxCLB & PxFB Registers + * + * @param PortExtension + * + * @return + * Return true if intialization was successful + */ +BOOLEAN +AhciPortInitialize ( + __in PAHCI_PORT_EXTENSION PortExtension + ) +{ + AHCI_PORT_CMD cmd; + PAHCI_MEMORY_REGISTERS abar; + ULONG mappedLength, portNumber, ticks; + PAHCI_ADAPTER_EXTENSION adapterExtension; + STOR_PHYSICAL_ADDRESS commandListPhysical, receivedFISPhysical; + + AhciDebugPrint("AhciPortInitialize()\n"); + + adapterExtension = PortExtension->AdapterExtension; + abar = adapterExtension->ABAR_Address; + portNumber = PortExtension->PortNumber; + + NT_ASSERT(abar != NULL); + NT_ASSERT(portNumber < adapterExtension->PortCount); + + PortExtension->Port = &abar->PortList[portNumber]; + + commandListPhysical = StorPortGetPhysicalAddress(adapterExtension, + NULL, + PortExtension->CommandList, + &mappedLength); + + if ((mappedLength == 0) || ((commandListPhysical.LowPart % 1024) != 0)) + { + AhciDebugPrint("\tcommandListPhysical mappedLength:%d\n", mappedLength); + return FALSE; + } + + receivedFISPhysical = StorPortGetPhysicalAddress(adapterExtension, + NULL, + PortExtension->ReceivedFIS, + &mappedLength); + + if ((mappedLength == 0) || ((receivedFISPhysical.LowPart % 256) != 0)) + { + AhciDebugPrint("\treceivedFISPhysical mappedLength:%d\n", mappedLength); + return FALSE; + } + + // Ensure that the controller is not in the running state by reading and examining each + // implemented port’s PxCMD register. If PxCMD.ST, PxCMD.CR, PxCMD.FRE and + // PxCMD.FR are all cleared, the port is in an idle state. Otherwise, the port is not idle and + // should be placed in the idle state prior to manipulating HBA and port specific registers. + // System software places a port into the idle state by clearing PxCMD.ST and waiting for + // PxCMD.CR to return ‘0’ when read. Software should wait at least 500 milliseconds for + // this to occur. If PxCMD.FRE is set to ‘1’, software should clear it to ‘0’ and wait at least + // 500 milliseconds for PxCMD.FR to return ‘0’ when read. If PxCMD.CR or PxCMD.FR do + // not clear to ‘0’ correctly, then software may attempt a port reset or a full HBA reset to recove + + // TODO: Check if port is in idle state or not, if not then restart port + cmd.Status = StorPortReadRegisterUlong(adapterExtension, &PortExtension->Port->CMD); + if ((cmd.FR != 0) || (cmd.CR != 0) || (cmd.FRE != 0) || (cmd.ST != 0)) + { + cmd.ST = 0; + cmd.FRE = 0; + + ticks = 3; + do + { + StorPortStallExecution(50000); + cmd.Status = StorPortReadRegisterUlong(adapterExtension, &PortExtension->Port->CMD); + if (ticks == 0) + { + AhciDebugPrint("\tAttempt to reset port failed: %x\n", cmd); + return FALSE; + } + ticks--; + } + while(cmd.CR != 0 || cmd.FR != 0); + } + + // 10.1.2 For each implemented port, system software shall allocate memory for and program: + //  PxCLB and PxCLBU (if CAP.S64A is set to ‘1’) + //  PxFB and PxFBU (if CAP.S64A is set to ‘1’) + // Note: Assuming 32bit support only + StorPortWriteRegisterUlong(adapterExtension, &PortExtension->Port->CLB, commandListPhysical.LowPart); + if (IsAdapterCAPS64(adapterExtension->CAP)) + { + StorPortWriteRegisterUlong(adapterExtension, &PortExtension->Port->CLBU, commandListPhysical.HighPart); + } + + StorPortWriteRegisterUlong(adapterExtension, &PortExtension->Port->FB, receivedFISPhysical.LowPart); + if (IsAdapterCAPS64(adapterExtension->CAP)) + { + StorPortWriteRegisterUlong(adapterExtension, &PortExtension->Port->FBU, receivedFISPhysical.HighPart); + } + + PortExtension->IdentifyDeviceDataPhysicalAddress = StorPortGetPhysicalAddress(adapterExtension, + NULL, + PortExtension->IdentifyDeviceData, + &mappedLength); + + // set device power state flag to D0 + PortExtension->DevicePowerState = StorPowerDeviceD0; + + // clear pending interrupts + StorPortWriteRegisterUlong(adapterExtension, &PortExtension->Port->SERR, (ULONG)~0); + StorPortWriteRegisterUlong(adapterExtension, &PortExtension->Port->IS, (ULONG)~0); + StorPortWriteRegisterUlong(adapterExtension, adapterExtension->IS, (1 << PortExtension->PortNumber)); + + return TRUE; +}// -- AhciPortInitialize(); + +/** + * @name AhciAllocateResourceForAdapter + * @implemented + * + * Allocate memory from poll for required pointers + * + * @param AdapterExtension + * @param ConfigInfo + * + * @return + * return TRUE if allocation was successful + */ +BOOLEAN +AhciAllocateResourceForAdapter ( + __in PAHCI_ADAPTER_EXTENSION AdapterExtension, + __in PPORT_CONFIGURATION_INFORMATION ConfigInfo + ) +{ + PCHAR nonCachedExtension, tmp; + ULONG index, NCS, AlignedNCS; + ULONG portCount, portImplemented, nonCachedExtensionSize; + PAHCI_PORT_EXTENSION PortExtension; + + AhciDebugPrint("AhciAllocateResourceForAdapter()\n"); + + NCS = AHCI_Global_Port_CAP_NCS(AdapterExtension->CAP); + AlignedNCS = ROUND_UP(NCS, 8); + + // get port count -- Number of set bits in `AdapterExtension->PortImplemented` + portCount = 0; + portImplemented = AdapterExtension->PortImplemented; + + NT_ASSERT(portImplemented != 0); + for (index = MAXIMUM_AHCI_PORT_COUNT - 1; index > 0; index--) + if ((portImplemented & (1 << index)) != 0) + break; + + portCount = index + 1; + AhciDebugPrint("\tPort Count: %d\n", portCount); + + AdapterExtension->PortCount = portCount; + nonCachedExtensionSize = sizeof(AHCI_COMMAND_HEADER) * AlignedNCS + //should be 1K aligned + sizeof(AHCI_RECEIVED_FIS) + + sizeof(IDENTIFY_DEVICE_DATA); + + // align nonCachedExtensionSize to 1024 + nonCachedExtensionSize = ROUND_UP(nonCachedExtensionSize, 1024); + + AdapterExtension->NonCachedExtension = StorPortGetUncachedExtension(AdapterExtension, + ConfigInfo, + nonCachedExtensionSize * portCount); + + if (AdapterExtension->NonCachedExtension == NULL) + { + AhciDebugPrint("\tadapterExtension->NonCachedExtension == NULL\n"); + return FALSE; + } + + nonCachedExtension = AdapterExtension->NonCachedExtension; + AhciZeroMemory(nonCachedExtension, nonCachedExtensionSize * portCount); + + for (index = 0; index < portCount; index++) + { + PortExtension = &AdapterExtension->PortExtension[index]; + + PortExtension->DeviceParams.IsActive = FALSE; + if ((AdapterExtension->PortImplemented & (1 << index)) != 0) + { + PortExtension->PortNumber = index; + PortExtension->DeviceParams.IsActive = TRUE; + PortExtension->AdapterExtension = AdapterExtension; + PortExtension->CommandList = (PAHCI_COMMAND_HEADER)nonCachedExtension; + + tmp = (PCHAR)(nonCachedExtension + sizeof(AHCI_COMMAND_HEADER) * AlignedNCS); + + PortExtension->ReceivedFIS = (PAHCI_RECEIVED_FIS)tmp; + PortExtension->IdentifyDeviceData = (PIDENTIFY_DEVICE_DATA)(tmp + sizeof(AHCI_RECEIVED_FIS)); + PortExtension->MaxPortQueueDepth = NCS; + nonCachedExtension += nonCachedExtensionSize; + } + } + + return TRUE; +}// -- AhciAllocateResourceForAdapter(); + +/** + * @name AhciStartPort + * @implemented + * + * Try to start the port device + * + * @param AdapterExtension + * @param PortExtension + * + */ +BOOLEAN +AhciStartPort ( + __in PAHCI_PORT_EXTENSION PortExtension + ) +{ + ULONG index; + AHCI_PORT_CMD cmd; + AHCI_TASK_FILE_DATA tfd; + AHCI_INTERRUPT_ENABLE ie; + AHCI_SERIAL_ATA_STATUS ssts; + AHCI_SERIAL_ATA_CONTROL sctl; + PAHCI_ADAPTER_EXTENSION AdapterExtension; + + AhciDebugPrint("AhciStartPort()\n"); + + AdapterExtension = PortExtension->AdapterExtension; + cmd.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->CMD); + + if ((cmd.FR == 1) && (cmd.CR == 1) && (cmd.FRE == 1) && (cmd.ST == 1)) + { + // Already Running + return TRUE; + } + + cmd.SUD = 1; + StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->CMD, cmd.Status); + + if (((cmd.FR == 1) && (cmd.FRE == 0)) || + ((cmd.CR == 1) && (cmd.ST == 0))) + { + AhciDebugPrint("\tCOMRESET\n"); + // perform COMRESET + // section 10.4.2 + + // Software causes a port reset (COMRESET) by writing 1h to the PxSCTL.DET field to invoke a + // COMRESET on the interface and start a re-establishment of Phy layer communications. Software shall + // wait at least 1 millisecond before clearing PxSCTL.DET to 0h; this ensures that at least one COMRESET + // signal is sent over the interface. After clearing PxSCTL.DET to 0h, software should wait for + // communication to be re-established as indicated by PxSSTS.DET being set to 3h. Then software should + // write all 1s to the PxSERR register to clear any bits that were set as part of the port reset. + + sctl.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->SCTL); + sctl.DET = 1; + StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->SCTL, sctl.Status); + + StorPortStallExecution(1000); + + sctl.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->SCTL); + sctl.DET = 0; + StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->SCTL, sctl.Status); + + // Poll DET to verify if a device is attached to the port + index = 0; + do + { + StorPortStallExecution(1000); + ssts.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->SSTS); + + index++; + if (ssts.DET != 0) + { + break; + } + } + while(index < 30); + } + + ssts.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->SSTS); + switch (ssts.DET) + { + case 0x3: + { + NT_ASSERT(cmd.ST == 0); + + // make sure FIS Recieve is enabled (cmd.FRE) + index = 0; + do + { + StorPortStallExecution(10000); + cmd.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->CMD); + cmd.FRE = 1; + StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->CMD, cmd.Status); + index++; + } + while((cmd.FR != 1) && (index < 3)); + + if (cmd.FR != 1) + { + // failed to start FIS DMA engine + // it can crash the driver later + // so better to turn this port off + return FALSE; + } + + // start port channel + // set cmd.ST + + NT_ASSERT(cmd.FRE == 1); + NT_ASSERT(cmd.CR == 0); + + // why assert? well If we face such condition on DET = 0x3 + // then we don't have port in idle state and hence before executing this part of code + // we must have restarted it. + tfd.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->TFD); + + if ((tfd.STS.BSY) || (tfd.STS.DRQ)) + { + AhciDebugPrint("\tUnhandled Case BSY-DRQ\n"); + } + + // clear pending interrupts + StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->SERR, (ULONG)~0); + StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->IS, (ULONG)~0); + StorPortWriteRegisterUlong(AdapterExtension, AdapterExtension->IS, (1 << PortExtension->PortNumber)); + + // set IE + ie.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->IE); + /* Device to Host Register FIS Interrupt Enable */ + ie.DHRE = 1; + /* PIO Setup FIS Interrupt Enable */ + ie.PSE = 1; + /* DMA Setup FIS Interrupt Enable */ + ie.DSE = 1; + /* Set Device Bits FIS Interrupt Enable */ + ie.SDBE = 1; + /* Unknown FIS Interrupt Enable */ + ie.UFE = 0; + /* Descriptor Processed Interrupt Enable */ + ie.DPE = 0; + /* Port Change Interrupt Enable */ + ie.PCE = 1; + /* Device Mechanical Presence Enable */ + ie.DMPE = 0; + /* PhyRdy Change Interrupt Enable */ + ie.PRCE = 1; + /* Incorrect Port Multiplier Enable */ + ie.IPME = 0; + /* Overflow Enable */ + ie.OFE = 1; + /* Interface Non-fatal Error Enable */ + ie.INFE = 1; + /* Interface Fatal Error Enable */ + ie.IFE = 1; + /* Host Bus Data Error Enable */ + ie.HBDE = 1; + /* Host Bus Fatal Error Enable */ + ie.HBFE = 1; + /* Task File Error Enable */ + ie.TFEE = 1; + + cmd.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->CMD); + /* Cold Presence Detect Enable */ + if (cmd.CPD) // does it support CPD? + { + // disable it for now + ie.CPDE = 0; + } + + // should I replace this to single line? + // by directly setting ie.Status? + + StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->IE, ie.Status); + + cmd.ST = 1; + StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->CMD, cmd.Status); + cmd.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->CMD); + + if (cmd.ST != 1) + { + AhciDebugPrint("\tFailed to start Port\n"); + return FALSE; + } + + return TRUE; + } + default: + // unhandled case + AhciDebugPrint("\tDET == %x Unsupported\n", ssts.DET); + return FALSE; + } +}// -- AhciStartPort(); + +/** + * @name AhciCommandCompletionDpcRoutine + * @implemented + * + * Handles Completed Commands + * + * @param Dpc + * @param AdapterExtension + * @param SystemArgument1 + * @param SystemArgument2 + */ +VOID +AhciCommandCompletionDpcRoutine ( + __in PSTOR_DPC Dpc, + __in PAHCI_ADAPTER_EXTENSION AdapterExtension, + __in PAHCI_PORT_EXTENSION PortExtension, + __in PVOID SystemArgument2 + ) +{ + PSCSI_REQUEST_BLOCK Srb; + PAHCI_SRB_EXTENSION SrbExtension; + STOR_LOCK_HANDLE lockhandle = {0}; + PAHCI_COMPLETION_ROUTINE CompletionRoutine; + + UNREFERENCED_PARAMETER(Dpc); + UNREFERENCED_PARAMETER(SystemArgument2); + + AhciDebugPrint("AhciCommandCompletionDpcRoutine()\n"); + + StorPortAcquireSpinLock(AdapterExtension, InterruptLock, NULL, &lockhandle); + Srb = RemoveQueue(&PortExtension->CompletionQueue); + StorPortReleaseSpinLock(AdapterExtension, &lockhandle); + + NT_ASSERT(Srb != NULL); + + if (Srb->SrbStatus == SRB_STATUS_PENDING) + { + Srb->SrbStatus = SRB_STATUS_SUCCESS; + } + else + { + return; + } + + SrbExtension = GetSrbExtension(Srb); + + CompletionRoutine = SrbExtension->CompletionRoutine; + NT_ASSERT(CompletionRoutine != NULL); + + // now it's completion routine responsibility to set SrbStatus + CompletionRoutine(PortExtension, Srb); + + StorPortNotification(RequestComplete, AdapterExtension, Srb); + + return; +}// -- AhciCommandCompletionDpcRoutine(); + +/** + * @name AhciHwPassiveInitialize + * @implemented + * + * initializes the HBA and finds all devices that are of interest to the miniport driver. (at PASSIVE LEVEL) + * + * @param adapterExtension + * + * @return + * return TRUE if intialization was successful + */ +BOOLEAN +AhciHwPassiveInitialize ( + __in PAHCI_ADAPTER_EXTENSION AdapterExtension + ) +{ + ULONG index; + PAHCI_PORT_EXTENSION PortExtension; + + AhciDebugPrint("AhciHwPassiveInitialize()\n"); + + for (index = 0; index < AdapterExtension->PortCount; index++) + { + if ((AdapterExtension->PortImplemented & (0x1 << index)) != 0) + { + PortExtension = &AdapterExtension->PortExtension[index]; + PortExtension->DeviceParams.IsActive = AhciStartPort(PortExtension); + StorPortInitializeDpc(AdapterExtension, &PortExtension->CommandCompletion, AhciCommandCompletionDpcRoutine); + } + } + + return TRUE; +}// -- AhciHwPassiveInitialize(); + +/** + * @name AhciHwInitialize + * @implemented + * + * initializes the HBA and finds all devices that are of interest to the miniport driver. + * + * @param adapterExtension + * + * @return + * return TRUE if intialization was successful + */ +BOOLEAN +AhciHwInitialize ( + __in PAHCI_ADAPTER_EXTENSION AdapterExtension + ) +{ + AHCI_GHC ghc; + + AhciDebugPrint("AhciHwInitialize()\n"); + + AdapterExtension->StateFlags.MessagePerPort = FALSE; + + // First check what type of interrupt/synchronization device is using + ghc.Status = StorPortReadRegisterUlong(AdapterExtension, &AdapterExtension->ABAR_Address->GHC); + + // When set to ‘1’ by hardware, indicates that the HBA requested more than one MSI vector + // but has reverted to using the first vector only. When this bit is cleared to ‘0’, + // the HBA has not reverted to single MSI mode (i.e. hardware is already in single MSI mode, + // software has allocated the number of messages requested + if (ghc.MRSM == 0) + { + AdapterExtension->StateFlags.MessagePerPort = TRUE; + AhciDebugPrint("\tMultiple MSI based message not supported\n"); + } + + StorPortEnablePassiveInitialization(AdapterExtension, AhciHwPassiveInitialize); + + return TRUE; +}// -- AhciHwInitialize(); + +/** + * @name AhciCompleteIssuedSrb + * @implemented + * + * Complete issued Srbs + * + * @param PortExtension + * + */ +VOID +AhciCompleteIssuedSrb ( + __in PAHCI_PORT_EXTENSION PortExtension, + __in ULONG CommandsToComplete + ) +{ + ULONG NCS, i; + PSCSI_REQUEST_BLOCK Srb; + PAHCI_SRB_EXTENSION SrbExtension; + PAHCI_ADAPTER_EXTENSION AdapterExtension; + + AhciDebugPrint("AhciCompleteIssuedSrb()\n"); + + NT_ASSERT(CommandsToComplete != 0); + + AhciDebugPrint("\tCompleted Commands: %d\n", CommandsToComplete); + + AdapterExtension = PortExtension->AdapterExtension; + NCS = AHCI_Global_Port_CAP_NCS(AdapterExtension->CAP); + + for (i = 0; i < NCS; i++) + { + if (((1 << i) & CommandsToComplete) != 0) + { + Srb = PortExtension->Slot[i]; + + if (Srb == NULL) + { + continue; + } + + SrbExtension = GetSrbExtension(Srb); + NT_ASSERT(SrbExtension != NULL); + + if (SrbExtension->CompletionRoutine != NULL) + { + AddQueue(&PortExtension->CompletionQueue, Srb); + StorPortIssueDpc(AdapterExtension, &PortExtension->CommandCompletion, PortExtension, Srb); + } + else + { + NT_ASSERT(Srb->SrbStatus == SRB_STATUS_PENDING); + Srb->SrbStatus = SRB_STATUS_SUCCESS; + StorPortNotification(RequestComplete, AdapterExtension, Srb); + } + } + } + + return; +}// -- AhciCompleteIssuedSrb(); + +/** + * @name AhciInterruptHandler + * @not_implemented + * + * Interrupt Handler for PortExtension + * + * @param PortExtension + * + */ +VOID +AhciInterruptHandler ( + __in PAHCI_PORT_EXTENSION PortExtension + ) +{ + ULONG is, ci, sact, outstanding; + AHCI_INTERRUPT_STATUS PxIS; + AHCI_INTERRUPT_STATUS PxISMasked; + PAHCI_ADAPTER_EXTENSION AdapterExtension; + + AhciDebugPrint("AhciInterruptHandler()\n"); + AhciDebugPrint("\tPort Number: %d\n", PortExtension->PortNumber); + + AdapterExtension = PortExtension->AdapterExtension; + NT_ASSERT(IsPortValid(AdapterExtension, PortExtension->PortNumber)); + + // 5.5.3 + // 1. Software determines the cause of the interrupt by reading the PxIS register. + // It is possible for multiple bits to be set + // 2. Software clears appropriate bits in the PxIS register corresponding to the cause of the interrupt. + // 3. Software clears the interrupt bit in IS.IPS corresponding to the port. + // 4. If executing non-queued commands, software reads the PxCI register, and compares the current value to + // the list of commands previously issued by software that are still outstanding. + // If executing native queued commands, software reads the PxSACT register and compares the current + // value to the list of commands previously issued by software. + // Software completes with success any outstanding command whose corresponding bit has been cleared in + // the respective register. PxCI and PxSACT are volatile registers; software should only use their values + // to determine commands that have completed, not to determine which commands have previously been issued. + // 5. If there were errors, noted in the PxIS register, software performs error recovery actions (see section 6.2.2). + PxISMasked.Status = 0; + PxIS.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->IS); + + // 6.2.2 + // Fatal Error + // signified by the setting of PxIS.HBFS, PxIS.HBDS, PxIS.IFS, or PxIS.TFES + if (PxIS.HBFS || PxIS.HBDS || PxIS.IFS || PxIS.TFES) + { + // In this state, the HBA shall not issue any new commands nor acknowledge DMA Setup FISes to process + // any native command queuing commands. To recover, the port must be restarted + // To detect an error that requires software recovery actions to be performed, + // software should check whether any of the following status bits are set on an interrupt: + // PxIS.HBFS, PxIS.HBDS, PxIS.IFS, and PxIS.TFES. If any of these bits are set, + // software should perform the appropriate error recovery actions based on whether + // non-queued commands were being issued or native command queuing commands were being issued. + + AhciDebugPrint("\tFatal Error: %x\n", PxIS.Status); + } + + // Normal Command Completion + // 3.3.5 + // A D2H Register FIS has been received with the ‘I’ bit set, and has been copied into system memory. + PxISMasked.DHRS = PxIS.DHRS; + // A PIO Setup FIS has been received with the ‘I’ bit set, it has been copied into system memory. + PxISMasked.PSS = PxIS.PSS; + // A DMA Setup FIS has been received with the ‘I’ bit set and has been copied into system memory. + PxISMasked.DSS = PxIS.DSS; + // A Set Device Bits FIS has been received with the ‘I’ bit set and has been copied into system memory/ + PxISMasked.SDBS = PxIS.SDBS; + // A PRD with the ‘I’ bit set has transferred all of its data. + PxISMasked.DPS = PxIS.DPS; + + if (PxISMasked.Status != 0) + { + StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->IS, PxISMasked.Status); + } + + // 10.7.1.1 + // Clear port interrupt + // It is set by the level of the virtual interrupt line being a set, and cleared by a write of ‘1’ from the software. + is = (1 << PortExtension->PortNumber); + StorPortWriteRegisterUlong(AdapterExtension, AdapterExtension->IS, is); + + ci = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->CI); + sact = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->SACT); + + outstanding = ci | sact; // NOTE: Including both non-NCQ and NCQ based commands + if ((PortExtension->CommandIssuedSlots & (~outstanding)) != 0) + { + AhciCompleteIssuedSrb(PortExtension, (PortExtension->CommandIssuedSlots & (~outstanding))); + PortExtension->CommandIssuedSlots &= outstanding; + } + + return; +}// -- AhciInterruptHandler(); + +/** + * @name AhciHwInterrupt + * @implemented + * + * The Storport driver calls the HwStorInterrupt routine after the HBA generates an interrupt request. + * + * @param AdapterExtension + * + * @return + * return TRUE Indicates that an interrupt was pending on adapter. + * return FALSE Indicates the interrupt was not ours. + */ +BOOLEAN +AhciHwInterrupt ( + __in PAHCI_ADAPTER_EXTENSION AdapterExtension + ) +{ + ULONG portPending, nextPort, i, portCount; + + if (AdapterExtension->StateFlags.Removed) + { + return FALSE; + } + + portPending = StorPortReadRegisterUlong(AdapterExtension, AdapterExtension->IS); + + // we process interrupt for implemented ports only + portCount = AdapterExtension->PortCount; + portPending = portPending & AdapterExtension->PortImplemented; + + if (portPending == 0) + { + return FALSE; + } + + for (i = 1; i <= portCount; i++) + { + nextPort = (AdapterExtension->LastInterruptPort + i) % portCount; + if ((portPending & (0x1 << nextPort)) == 0) + continue; + + NT_ASSERT(IsPortValid(AdapterExtension, nextPort)); + + if (AdapterExtension->PortExtension[nextPort].DeviceParams.IsActive == FALSE) + { + continue; + } + + // we can assign this interrupt to this port + AdapterExtension->LastInterruptPort = nextPort; + AhciInterruptHandler(&AdapterExtension->PortExtension[nextPort]); + + portPending &= ~(1 << nextPort); + + // interrupt belongs to this device + // should always return TRUE + return TRUE; + } + + AhciDebugPrint("\tSomething went wrong"); + return FALSE; +}// -- AhciHwInterrupt(); + +/** + * @name AhciHwStartIo + * @not_implemented + * + * The Storport driver calls the HwStorStartIo routine one time for each incoming I/O request. + * + * @param adapterExtension + * @param Srb + * + * @return + * return TRUE if the request was accepted + * return FALSE if the request must be submitted later + */ +BOOLEAN +AhciHwStartIo ( + __in PAHCI_ADAPTER_EXTENSION AdapterExtension, + __in PSCSI_REQUEST_BLOCK Srb + ) +{ + AhciDebugPrint("AhciHwStartIo()\n"); + + if (!IsPortValid(AdapterExtension, Srb->PathId)) + { + Srb->SrbStatus = SRB_STATUS_NO_DEVICE; + StorPortNotification(RequestComplete, AdapterExtension, Srb); + return TRUE; + } + + switch(Srb->Function) + { + case SRB_FUNCTION_PNP: + { + // https://msdn.microsoft.com/windows/hardware/drivers/storage/handling-srb-function-pnp + // If the function member of an SRB is set to SRB_FUNCTION_PNP, + // the SRB is a structure of type SCSI_PNP_REQUEST_BLOCK. + + PSCSI_PNP_REQUEST_BLOCK pnpRequest; + pnpRequest = (PSCSI_PNP_REQUEST_BLOCK)Srb; + if ((pnpRequest->SrbPnPFlags & SRB_PNP_FLAGS_ADAPTER_REQUEST) != 0) + { + switch(pnpRequest->PnPAction) + { + case StorRemoveDevice: + case StorSurpriseRemoval: + { + Srb->SrbStatus = SRB_STATUS_SUCCESS; + AdapterExtension->StateFlags.Removed = 1; + AhciDebugPrint("\tAdapter removed\n"); + } + break; + case StorStopDevice: + { + Srb->SrbStatus = SRB_STATUS_SUCCESS; + AhciDebugPrint("\tRequested to Stop the adapter\n"); + } + break; + default: + Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; + break; + } + } + else + { + Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; + } + } + break; + case SRB_FUNCTION_EXECUTE_SCSI: + { + // https://msdn.microsoft.com/en-us/windows/hardware/drivers/storage/handling-srb-function-execute-scsi + // On receipt of an SRB_FUNCTION_EXECUTE_SCSI request, a miniport driver's HwScsiStartIo + // routine does the following: + // + // - Gets and/or sets up whatever context the miniport driver maintains in its device, + // logical unit, and/or SRB extensions + // For example, a miniport driver might set up a logical unit extension with pointers + // to the SRB itself and the SRB DataBuffer pointer, the SRB DataTransferLength value, + // and a driver-defined value (or CDB SCSIOP_XXX value) indicating the operation to be + // carried out on the HBA. + // + // - Calls an internal routine to program the HBA, as partially directed by the SrbFlags, + // for the requested operation + // For a device I/O operation, such an internal routine generally selects the target device + // and sends the CDB over the bus to the target logical unit. + PCDB cdb = (PCDB)&Srb->Cdb; + if (Srb->CdbLength == 0) + { + AhciDebugPrint("\tOperationCode: %d\n", cdb->CDB10.OperationCode); + Srb->SrbStatus = SRB_STATUS_BAD_FUNCTION; + break; + } + + NT_ASSERT(cdb != NULL); + + switch(cdb->CDB10.OperationCode) + { + case SCSIOP_INQUIRY: + Srb->SrbStatus = DeviceInquiryRequest(AdapterExtension, Srb, cdb); + break; + case SCSIOP_REPORT_LUNS: + Srb->SrbStatus = DeviceReportLuns(AdapterExtension, Srb, cdb); + break; + case SCSIOP_READ_CAPACITY: + Srb->SrbStatus = DeviceRequestCapacity(AdapterExtension, Srb, cdb); + break; + case SCSIOP_TEST_UNIT_READY: + Srb->SrbStatus = DeviceRequestComplete(AdapterExtension, Srb, cdb); + break; + case SCSIOP_MODE_SENSE: + Srb->SrbStatus = DeviceRequestSense(AdapterExtension, Srb, cdb); + break; + case SCSIOP_READ: + case SCSIOP_WRITE: + Srb->SrbStatus = DeviceRequestReadWrite(AdapterExtension, Srb, cdb); + break; + default: + AhciDebugPrint("\tOperationCode: %d\n", cdb->CDB10.OperationCode); + Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; + break; + } + } + break; + default: + AhciDebugPrint("\tUnknown function code recieved: %x\n", Srb->Function); + Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; + break; + } + + if (Srb->SrbStatus != SRB_STATUS_PENDING) + { + StorPortNotification(RequestComplete, AdapterExtension, Srb); + } + else + { + AhciProcessIO(AdapterExtension, Srb->PathId, Srb); + } + return TRUE; +}// -- AhciHwStartIo(); + +/** + * @name AhciHwResetBus + * @not_implemented + * + * The HwStorResetBus routine is called by the port driver to clear error conditions. + * + * @param adapterExtension + * @param PathId + * + * @return + * return TRUE if bus was successfully reset + */ +BOOLEAN +AhciHwResetBus ( + __in PVOID AdapterExtension, + __in ULONG PathId + ) +{ + STOR_LOCK_HANDLE lockhandle = {0}; + PAHCI_ADAPTER_EXTENSION adapterExtension; + + AhciDebugPrint("AhciHwResetBus()\n"); + + adapterExtension = AdapterExtension; + + if (IsPortValid(AdapterExtension, PathId)) + { + // Acquire Lock + StorPortAcquireSpinLock(AdapterExtension, InterruptLock, NULL, &lockhandle); + + // TODO: Perform port reset + + // Release lock + StorPortReleaseSpinLock(AdapterExtension, &lockhandle); + } + + return FALSE; +}// -- AhciHwResetBus(); + +/** + * @name AhciHwFindAdapter + * @implemented + * + * The HwStorFindAdapter routine uses the supplied configuration to determine whether a specific + * HBA is supported and, if it is, to return configuration information about that adapter. + * + * 10.1 Platform Communication + * http://www.intel.in/content/dam/www/public/us/en/documents/technical-specifications/serial-ata-ahci-spec-rev1_2.pdf + + * @param DeviceExtension + * @param HwContext + * @param BusInformation + * @param ArgumentString + * @param ConfigInfo + * @param Reserved3 + * + * @return + * SP_RETURN_FOUND + * Indicates that a supported HBA was found and that the HBA-relevant configuration information was successfully determined and set in the PORT_CONFIGURATION_INFORMATION structure. + * + * SP_RETURN_ERROR + * Indicates that an HBA was found but there was an error obtaining the configuration information. If possible, such an error should be logged with StorPortLogError. + * + * SP_RETURN_BAD_CONFIG + * Indicates that the supplied configuration information was invalid for the adapter. + * + * SP_RETURN_NOT_FOUND + * Indicates that no supported HBA was found for the supplied configuration information. + * + * @remarks Called by Storport. + */ +ULONG +AhciHwFindAdapter ( + __in PVOID AdapterExtension, + __in PVOID HwContext, + __in PVOID BusInformation, + __in PVOID ArgumentString, + __inout PPORT_CONFIGURATION_INFORMATION ConfigInfo, + __in PBOOLEAN Reserved3 + ) +{ + AHCI_GHC ghc; + ULONG index, pci_cfg_len; + PACCESS_RANGE accessRange; + UCHAR pci_cfg_buf[sizeof(PCI_COMMON_CONFIG)]; + + PAHCI_MEMORY_REGISTERS abar; + PPCI_COMMON_CONFIG pciConfigData; + PAHCI_ADAPTER_EXTENSION adapterExtension; + + AhciDebugPrint("AhciHwFindAdapter()\n"); + + UNREFERENCED_PARAMETER(HwContext); + UNREFERENCED_PARAMETER(BusInformation); + UNREFERENCED_PARAMETER(ArgumentString); + UNREFERENCED_PARAMETER(Reserved3); + + adapterExtension = AdapterExtension; + adapterExtension->SlotNumber = ConfigInfo->SlotNumber; + adapterExtension->SystemIoBusNumber = ConfigInfo->SystemIoBusNumber; + + // get PCI configuration header + pci_cfg_len = StorPortGetBusData( + adapterExtension, + PCIConfiguration, + adapterExtension->SystemIoBusNumber, + adapterExtension->SlotNumber, + pci_cfg_buf, + sizeof(PCI_COMMON_CONFIG)); + + if (pci_cfg_len != sizeof(PCI_COMMON_CONFIG)) + { + AhciDebugPrint("\tpci_cfg_len != %d :: %d", sizeof(PCI_COMMON_CONFIG), pci_cfg_len); + return SP_RETURN_ERROR;//Not a valid device at the given bus number + } + + pciConfigData = (PPCI_COMMON_CONFIG)pci_cfg_buf; + adapterExtension->VendorID = pciConfigData->VendorID; + adapterExtension->DeviceID = pciConfigData->DeviceID; + adapterExtension->RevisionID = pciConfigData->RevisionID; + // The last PCI base address register (BAR[5], header offset 0x24) points to the AHCI base memory, it’s called ABAR (AHCI Base Memory Register). + adapterExtension->AhciBaseAddress = pciConfigData->u.type0.BaseAddresses[5] & (0xFFFFFFF0); + + AhciDebugPrint("\tVendorID:%d DeviceID:%d RevisionID:%d\n", adapterExtension->VendorID, + adapterExtension->DeviceID, + adapterExtension->RevisionID); + + // 2.1.11 + abar = NULL; + if (ConfigInfo->NumberOfAccessRanges > 0) + { + accessRange = *(ConfigInfo->AccessRanges); + for (index = 0; index < ConfigInfo->NumberOfAccessRanges; index++) + { + if (accessRange[index].RangeStart.QuadPart == adapterExtension->AhciBaseAddress) + { + abar = StorPortGetDeviceBase(adapterExtension, + ConfigInfo->AdapterInterfaceType, + ConfigInfo->SystemIoBusNumber, + accessRange[index].RangeStart, + accessRange[index].RangeLength, + !accessRange[index].RangeInMemory); + break; + } + } + } + + if (abar == NULL) + { + AhciDebugPrint("\tabar == NULL\n"); + return SP_RETURN_ERROR; // corrupted information supplied + } + + adapterExtension->ABAR_Address = abar; + adapterExtension->CAP = StorPortReadRegisterUlong(adapterExtension, &abar->CAP); + adapterExtension->CAP2 = StorPortReadRegisterUlong(adapterExtension, &abar->CAP2); + adapterExtension->Version = StorPortReadRegisterUlong(adapterExtension, &abar->VS); + adapterExtension->LastInterruptPort = (ULONG)-1; + + // 10.1.2 + // 1. Indicate that system software is AHCI aware by setting GHC.AE to ‘1’. + // 3.1.2 -- AE bit is read-write only if CAP.SAM is '0' + ghc.Status = StorPortReadRegisterUlong(adapterExtension, &abar->GHC); + // AE := Highest Significant bit of GHC + if (ghc.AE != 0)// Hmm, controller was already in power state + { + // reset controller to have it in known state + AhciDebugPrint("\tAE Already set, Reset()\n"); + if (!AhciAdapterReset(adapterExtension)) + { + AhciDebugPrint("\tReset Failed!\n"); + return SP_RETURN_ERROR;// reset failed + } + } + + ghc.Status = 0; + ghc.AE = 1;// only AE=1 + // tell the controller that we know about AHCI + StorPortWriteRegisterUlong(adapterExtension, &abar->GHC, ghc.Status); + + adapterExtension->IS = &abar->IS; + adapterExtension->PortImplemented = StorPortReadRegisterUlong(adapterExtension, &abar->PI); + + if (adapterExtension->PortImplemented == 0) + { + AhciDebugPrint("\tadapterExtension->PortImplemented == 0\n"); + return SP_RETURN_ERROR; + } + + ConfigInfo->Master = TRUE; + ConfigInfo->AlignmentMask = 0x3; + ConfigInfo->ScatterGather = TRUE; + ConfigInfo->DmaWidth = Width32Bits; + ConfigInfo->WmiDataProvider = FALSE; + ConfigInfo->Dma32BitAddresses = TRUE; + + if (IsAdapterCAPS64(adapterExtension->CAP)) + { + ConfigInfo->Dma64BitAddresses = TRUE; + } + + ConfigInfo->MaximumNumberOfTargets = 1; + ConfigInfo->ResetTargetSupported = TRUE; + ConfigInfo->NumberOfPhysicalBreaks = 0x21; + ConfigInfo->MaximumNumberOfLogicalUnits = 1; + ConfigInfo->NumberOfBuses = MAXIMUM_AHCI_PORT_COUNT; + ConfigInfo->MaximumTransferLength = MAXIMUM_TRANSFER_LENGTH; + ConfigInfo->SynchronizationModel = StorSynchronizeFullDuplex; + + // Turn IE -- Interrupt Enabled + ghc.Status = StorPortReadRegisterUlong(adapterExtension, &abar->GHC); + ghc.IE = 1; + StorPortWriteRegisterUlong(adapterExtension, &abar->GHC, ghc.Status); + + // allocate necessary resource for each port + if (!AhciAllocateResourceForAdapter(adapterExtension, ConfigInfo)) + { + NT_ASSERT(FALSE); + return SP_RETURN_ERROR; + } + + for (index = 0; index < adapterExtension->PortCount; index++) + { + if ((adapterExtension->PortImplemented & (0x1 << index)) != 0) + AhciPortInitialize(&adapterExtension->PortExtension[index]); + } + + return SP_RETURN_FOUND; +}// -- AhciHwFindAdapter(); + +/** + * @name DriverEntry + * @implemented + * + * Initial Entrypoint for storahci miniport driver + * + * @param DriverObject + * @param RegistryPath + * + * @return + * NT_STATUS in case of driver loaded successfully. + */ +ULONG +DriverEntry ( + __in PVOID DriverObject, + __in PVOID RegistryPath + ) +{ + ULONG status; + // initialize the hardware data structure + HW_INITIALIZATION_DATA hwInitializationData = {0}; + + // set size of hardware initialization structure + hwInitializationData.HwInitializationDataSize = sizeof(HW_INITIALIZATION_DATA); + + // identity required miniport entry point routines + hwInitializationData.HwStartIo = AhciHwStartIo; + hwInitializationData.HwResetBus = AhciHwResetBus; + hwInitializationData.HwInterrupt = AhciHwInterrupt; + hwInitializationData.HwInitialize = AhciHwInitialize; + hwInitializationData.HwFindAdapter = AhciHwFindAdapter; + + // adapter specific information + hwInitializationData.TaggedQueuing = TRUE; + hwInitializationData.AutoRequestSense = TRUE; + hwInitializationData.MultipleRequestPerLu = TRUE; + hwInitializationData.NeedPhysicalAddresses = TRUE; + + hwInitializationData.NumberOfAccessRanges = 6; + hwInitializationData.AdapterInterfaceType = PCIBus; + hwInitializationData.MapBuffers = STOR_MAP_NON_READ_WRITE_BUFFERS; + + // set required extension sizes + hwInitializationData.SrbExtensionSize = sizeof(AHCI_SRB_EXTENSION); + hwInitializationData.DeviceExtensionSize = sizeof(AHCI_ADAPTER_EXTENSION); + + // register our hw init data + status = StorPortInitialize(DriverObject, + RegistryPath, + &hwInitializationData, + NULL); + + NT_ASSERT(status == STATUS_SUCCESS); + return status; +}// -- DriverEntry(); + +/** + * @name AhciATA_CFIS + * @implemented + * + * create ATA CFIS from Srb + * + * @param PortExtension + * @param Srb + * + * @return + * Number of CFIS fields used in DWORD + */ +ULONG +AhciATA_CFIS ( + __in PAHCI_PORT_EXTENSION PortExtension, + __in PAHCI_SRB_EXTENSION SrbExtension + ) +{ + PAHCI_COMMAND_TABLE cmdTable; + + UNREFERENCED_PARAMETER(PortExtension); + + AhciDebugPrint("AhciATA_CFIS()\n"); + + cmdTable = (PAHCI_COMMAND_TABLE)SrbExtension; + + AhciZeroMemory((PCHAR)cmdTable->CFIS, sizeof(cmdTable->CFIS)); + + cmdTable->CFIS[AHCI_ATA_CFIS_FisType] = FIS_TYPE_REG_H2D; // FIS Type + cmdTable->CFIS[AHCI_ATA_CFIS_PMPort_C] = (1 << 7); // PM Port & C + cmdTable->CFIS[AHCI_ATA_CFIS_CommandReg] = SrbExtension->CommandReg; + + cmdTable->CFIS[AHCI_ATA_CFIS_FeaturesLow] = SrbExtension->FeaturesLow; + cmdTable->CFIS[AHCI_ATA_CFIS_LBA0] = SrbExtension->LBA0; + cmdTable->CFIS[AHCI_ATA_CFIS_LBA1] = SrbExtension->LBA1; + cmdTable->CFIS[AHCI_ATA_CFIS_LBA2] = SrbExtension->LBA2; + cmdTable->CFIS[AHCI_ATA_CFIS_Device] = SrbExtension->Device; + cmdTable->CFIS[AHCI_ATA_CFIS_LBA3] = SrbExtension->LBA3; + cmdTable->CFIS[AHCI_ATA_CFIS_LBA4] = SrbExtension->LBA4; + cmdTable->CFIS[AHCI_ATA_CFIS_LBA5] = SrbExtension->LBA5; + cmdTable->CFIS[AHCI_ATA_CFIS_FeaturesHigh] = SrbExtension->FeaturesHigh; + cmdTable->CFIS[AHCI_ATA_CFIS_SectorCountLow] = SrbExtension->SectorCountLow; + cmdTable->CFIS[AHCI_ATA_CFIS_SectorCountHigh] = SrbExtension->SectorCountHigh; + + return 5; +}// -- AhciATA_CFIS(); + +/** + * @name AhciATAPI_CFIS + * @not_implemented + * + * create ATAPI CFIS from Srb + * + * @param PortExtension + * @param Srb + * + * @return + * Number of CFIS fields used in DWORD + */ +ULONG +AhciATAPI_CFIS ( + __in PAHCI_PORT_EXTENSION PortExtension, + __in PAHCI_SRB_EXTENSION SrbExtension + ) +{ + PAHCI_COMMAND_TABLE cmdTable; + UNREFERENCED_PARAMETER(PortExtension); + + AhciDebugPrint("AhciATAPI_CFIS()\n"); + + cmdTable = (PAHCI_COMMAND_TABLE)SrbExtension; + + NT_ASSERT(SrbExtension->CommandReg == IDE_COMMAND_ATAPI_PACKET); + + AhciZeroMemory((PCHAR)cmdTable->CFIS, sizeof(cmdTable->CFIS)); + + cmdTable->CFIS[AHCI_ATA_CFIS_FisType] = FIS_TYPE_REG_H2D; // FIS Type + cmdTable->CFIS[AHCI_ATA_CFIS_PMPort_C] = (1 << 7); // PM Port & C + cmdTable->CFIS[AHCI_ATA_CFIS_CommandReg] = SrbExtension->CommandReg; + + cmdTable->CFIS[AHCI_ATA_CFIS_FeaturesLow] = SrbExtension->FeaturesLow; + cmdTable->CFIS[AHCI_ATA_CFIS_LBA0] = SrbExtension->LBA0; + cmdTable->CFIS[AHCI_ATA_CFIS_LBA1] = SrbExtension->LBA1; + cmdTable->CFIS[AHCI_ATA_CFIS_LBA2] = SrbExtension->LBA2; + cmdTable->CFIS[AHCI_ATA_CFIS_Device] = SrbExtension->Device; + cmdTable->CFIS[AHCI_ATA_CFIS_LBA3] = SrbExtension->LBA3; + cmdTable->CFIS[AHCI_ATA_CFIS_LBA4] = SrbExtension->LBA4; + cmdTable->CFIS[AHCI_ATA_CFIS_LBA5] = SrbExtension->LBA5; + cmdTable->CFIS[AHCI_ATA_CFIS_FeaturesHigh] = SrbExtension->FeaturesHigh; + cmdTable->CFIS[AHCI_ATA_CFIS_SectorCountLow] = SrbExtension->SectorCountLow; + cmdTable->CFIS[AHCI_ATA_CFIS_SectorCountHigh] = SrbExtension->SectorCountHigh; + + return 5; +}// -- AhciATAPI_CFIS(); + +/** + * @name AhciBuild_PRDT + * @implemented + * + * Build PRDT for data transfer + * + * @param PortExtension + * @param Srb + * + * @return + * Return number of entries in PRDT. + */ +ULONG +AhciBuild_PRDT ( + __in PAHCI_PORT_EXTENSION PortExtension, + __in PAHCI_SRB_EXTENSION SrbExtension + ) +{ + ULONG index; + PAHCI_COMMAND_TABLE cmdTable; + PLOCAL_SCATTER_GATHER_LIST sgl; + PAHCI_ADAPTER_EXTENSION AdapterExtension; + + AhciDebugPrint("AhciBuild_PRDT()\n"); + + sgl = SrbExtension->pSgl; + cmdTable = (PAHCI_COMMAND_TABLE)SrbExtension; + AdapterExtension = PortExtension->AdapterExtension; + + NT_ASSERT(sgl != NULL); + NT_ASSERT(sgl->NumberOfElements < MAXIMUM_AHCI_PRDT_ENTRIES); + + for (index = 0; index < sgl->NumberOfElements; index++) + { + NT_ASSERT(sgl->List[index].Length <= MAXIMUM_TRANSFER_LENGTH); + + cmdTable->PRDT[index].DBA = sgl->List[index].PhysicalAddress.LowPart; + if (IsAdapterCAPS64(AdapterExtension->CAP)) + { + cmdTable->PRDT[index].DBAU = sgl->List[index].PhysicalAddress.HighPart; + } + + // Data Byte Count (DBC): A ‘0’ based value that Indicates the length, in bytes, of the data block. + // A maximum of length of 4MB may exist for any entry. Bit ‘0’ of this field must always be ‘1’ to + // indicate an even byte count. A value of ‘1’ indicates 2 bytes, ‘3’ indicates 4 bytes, etc. + cmdTable->PRDT[index].DBC = sgl->List[index].Length - 1; + } + + return sgl->NumberOfElements; +}// -- AhciBuild_PRDT(); + +/** + * @name AhciProcessSrb + * @implemented + * + * Prepare Srb for IO processing + * + * @param PortExtension + * @param Srb + * @param SlotIndex + * + */ +VOID +AhciProcessSrb ( + __in PAHCI_PORT_EXTENSION PortExtension, + __in PSCSI_REQUEST_BLOCK Srb, + __in ULONG SlotIndex + ) +{ + ULONG prdtlen, sig, length, cfl; + PAHCI_SRB_EXTENSION SrbExtension; + PAHCI_COMMAND_HEADER CommandHeader; + PAHCI_ADAPTER_EXTENSION AdapterExtension; + STOR_PHYSICAL_ADDRESS CommandTablePhysicalAddress; + + AhciDebugPrint("AhciProcessSrb()\n"); + + NT_ASSERT(Srb->PathId == PortExtension->PortNumber); + + SrbExtension = GetSrbExtension(Srb); + AdapterExtension = PortExtension->AdapterExtension; + + NT_ASSERT(SrbExtension != NULL); + NT_ASSERT(SrbExtension->AtaFunction != 0); + + if ((SrbExtension->AtaFunction == ATA_FUNCTION_ATA_IDENTIFY) && + (SrbExtension->CommandReg == IDE_COMMAND_NOT_VALID)) + { + // Here we are safe to check SIG register + sig = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->SIG); + if (sig == 0x101) + { + AhciDebugPrint("\tATA Device Found!\n"); + SrbExtension->CommandReg = IDE_COMMAND_IDENTIFY; + } + else + { + AhciDebugPrint("\tATAPI Device Found!\n"); + SrbExtension->CommandReg = IDE_COMMAND_ATAPI_IDENTIFY; + } + } + + NT_ASSERT(SlotIndex < AHCI_Global_Port_CAP_NCS(AdapterExtension->CAP)); + SrbExtension->SlotIndex = SlotIndex; + + // program the CFIS in the CommandTable + CommandHeader = &PortExtension->CommandList[SlotIndex]; + + cfl = 0; + if (IsAtapiCommand(SrbExtension->AtaFunction)) + { + cfl = AhciATAPI_CFIS(PortExtension, SrbExtension); + } + else if (IsAtaCommand(SrbExtension->AtaFunction)) + { + cfl = AhciATA_CFIS(PortExtension, SrbExtension); + } + else + { + NT_ASSERT(FALSE); + } + + prdtlen = 0; + if (IsDataTransferNeeded(SrbExtension)) + { + prdtlen = AhciBuild_PRDT(PortExtension, SrbExtension); + NT_ASSERT(prdtlen != -1); + } + + // Program the command header + CommandHeader->DI.PRDTL = prdtlen; // number of entries in PRD table + CommandHeader->DI.CFL = cfl; + CommandHeader->DI.A = (SrbExtension->AtaFunction & ATA_FUNCTION_ATAPI_COMMAND) ? 1 : 0; + CommandHeader->DI.W = (SrbExtension->Flags & ATA_FLAGS_DATA_OUT) ? 1 : 0; + CommandHeader->DI.P = 0; // ATA Specifications says so + CommandHeader->DI.PMP = 0; // Port Multiplier + + // Reset -- Manual Configuation + CommandHeader->DI.R = 0; + CommandHeader->DI.B = 0; + CommandHeader->DI.C = 0; + + CommandHeader->PRDBC = 0; + + CommandHeader->Reserved[0] = 0; + CommandHeader->Reserved[1] = 0; + CommandHeader->Reserved[2] = 0; + CommandHeader->Reserved[3] = 0; + + // set CommandHeader CTBA + CommandTablePhysicalAddress = StorPortGetPhysicalAddress(AdapterExtension, + NULL, + SrbExtension, + &length); + + NT_ASSERT(length != 0); + + // command table alignment + NT_ASSERT((CommandTablePhysicalAddress.LowPart % 128) == 0); + + CommandHeader->CTBA = CommandTablePhysicalAddress.LowPart; + + if (IsAdapterCAPS64(AdapterExtension->CAP)) + { + CommandHeader->CTBA_U = CommandTablePhysicalAddress.HighPart; + } + + // mark this slot + PortExtension->Slot[SlotIndex] = Srb; + PortExtension->QueueSlots |= 1 << SlotIndex; + return; +}// -- AhciProcessSrb(); + +/** + * @name AhciActivatePort + * @implemented + * + * Program Port and populate command list + * + * @param PortExtension + * + */ +VOID +AhciActivatePort ( + __in PAHCI_PORT_EXTENSION PortExtension + ) +{ + AHCI_PORT_CMD cmd; + ULONG QueueSlots, slotToActivate, tmp; + PAHCI_ADAPTER_EXTENSION AdapterExtension; + + AhciDebugPrint("AhciActivatePort()\n"); + + AdapterExtension = PortExtension->AdapterExtension; + QueueSlots = PortExtension->QueueSlots; + + if (QueueSlots == 0) + { + return; + } + + // section 3.3.14 + // Bits in this field shall only be set to ‘1’ by software when PxCMD.ST is set to ‘1’ + cmd.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->CMD); + + if (cmd.ST == 0) // PxCMD.ST == 0 + { + return; + } + + // get the lowest set bit + tmp = QueueSlots & (QueueSlots - 1); + + if (tmp == 0) + slotToActivate = QueueSlots; + else + slotToActivate = (QueueSlots & (~tmp)); + + // mark that bit off in QueueSlots + // so we can know we it is really needed to activate port or not + PortExtension->QueueSlots &= ~slotToActivate; + // mark this CommandIssuedSlots + // to validate in completeIssuedCommand + PortExtension->CommandIssuedSlots |= slotToActivate; + + // tell the HBA to issue this Command Slot to the given port + StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->CI, slotToActivate); + + return; +}// -- AhciActivatePort(); + +/** + * @name AhciProcessIO + * @implemented + * + * Acquire Exclusive lock to port, populate pending commands to command List + * program controller's port to process new commands in command list. + * + * @param AdapterExtension + * @param PathId + * @param Srb + * + */ +VOID +AhciProcessIO ( + __in PAHCI_ADAPTER_EXTENSION AdapterExtension, + __in UCHAR PathId, + __in PSCSI_REQUEST_BLOCK Srb + ) +{ + PSCSI_REQUEST_BLOCK tmpSrb; + STOR_LOCK_HANDLE lockhandle = {0}; + PAHCI_PORT_EXTENSION PortExtension; + ULONG commandSlotMask, occupiedSlots, slotIndex, NCS; + + AhciDebugPrint("AhciProcessIO()\n"); + AhciDebugPrint("\tPathId: %d\n", PathId); + + PortExtension = &AdapterExtension->PortExtension[PathId]; + + NT_ASSERT(PathId < AdapterExtension->PortCount); + + // Acquire Lock + StorPortAcquireSpinLock(AdapterExtension, InterruptLock, NULL, &lockhandle); + + // add Srb to queue + AddQueue(&PortExtension->SrbQueue, Srb); + + if (PortExtension->DeviceParams.IsActive == FALSE) + { + // Release Lock + StorPortReleaseSpinLock(AdapterExtension, &lockhandle); + return; // we should wait for device to get active + } + + occupiedSlots = (PortExtension->QueueSlots | PortExtension->CommandIssuedSlots); // Busy command slots for given port + NCS = AHCI_Global_Port_CAP_NCS(AdapterExtension->CAP); + commandSlotMask = (1 << NCS) - 1; // available slots mask + + commandSlotMask = (commandSlotMask & ~occupiedSlots); + if(commandSlotMask != 0) + { + // iterate over HBA port slots + for (slotIndex = 0; slotIndex < NCS; slotIndex++) + { + // find first free slot + if ((commandSlotMask & (1 << slotIndex)) != 0) + { + tmpSrb = RemoveQueue(&PortExtension->SrbQueue); + if (tmpSrb != NULL) + { + NT_ASSERT(tmpSrb->PathId == PathId); + AhciProcessSrb(PortExtension, tmpSrb, slotIndex); + } + else + { + break; + } + } + else + { + break; + } + } + } + + // program HBA port + AhciActivatePort(PortExtension); + + // Release Lock + StorPortReleaseSpinLock(AdapterExtension, &lockhandle); + + return; +}// -- AhciProcessIO(); + +/** + * @name AtapiInquiryCompletion + * @implemented + * + * AtapiInquiryCompletion routine should be called after device signals + * for device inquiry request is completed (through interrupt) -- ATAPI Device only + * + * @param PortExtension + * @param Srb + * + */ +VOID +AtapiInquiryCompletion ( + __in PAHCI_PORT_EXTENSION PortExtension, + __in PSCSI_REQUEST_BLOCK Srb + ) +{ + BOOLEAN status; + PAHCI_ADAPTER_EXTENSION AdapterExtension; + + AhciDebugPrint("AtapiInquiryCompletion()\n"); + + NT_ASSERT(Srb != NULL); + NT_ASSERT(PortExtension != NULL); + + AdapterExtension = PortExtension->AdapterExtension; + + // send queue depth + status = StorPortSetDeviceQueueDepth(PortExtension->AdapterExtension, + Srb->PathId, + Srb->TargetId, + Srb->Lun, + AHCI_Global_Port_CAP_NCS(AdapterExtension->CAP)); + + NT_ASSERT(status == TRUE); + return; +}// -- AtapiInquiryCompletion(); + +/** + * @name InquiryCompletion + * @implemented + * + * InquiryCompletion routine should be called after device signals + * for device inquiry request is completed (through interrupt) + * + * @param PortExtension + * @param Srb + * + */ +VOID +InquiryCompletion ( + __in PAHCI_PORT_EXTENSION PortExtension, + __in PSCSI_REQUEST_BLOCK Srb + ) +{ + PCDB cdb; + BOOLEAN status; + PINQUIRYDATA InquiryData; + PAHCI_SRB_EXTENSION SrbExtension; + PAHCI_ADAPTER_EXTENSION AdapterExtension; + PIDENTIFY_DEVICE_DATA IdentifyDeviceData; + + AhciDebugPrint("InquiryCompletion()\n"); + + NT_ASSERT(Srb != NULL); + NT_ASSERT(PortExtension != NULL); + + cdb = (PCDB)&Srb->Cdb; + InquiryData = Srb->DataBuffer; + SrbExtension = GetSrbExtension(Srb); + AdapterExtension = PortExtension->AdapterExtension; + IdentifyDeviceData = PortExtension->IdentifyDeviceData; + + if (Srb->SrbStatus != SRB_STATUS_SUCCESS) + { + if (Srb->SrbStatus == SRB_STATUS_NO_DEVICE) + { + PortExtension->DeviceParams.DeviceType = AHCI_DEVICE_TYPE_NODEVICE; + } + return; + } + + NT_ASSERT(InquiryData != NULL); + NT_ASSERT(Srb->SrbStatus == SRB_STATUS_SUCCESS); + + // Device specific data + PortExtension->DeviceParams.MaxLba.QuadPart = 0; + + if (SrbExtension->CommandReg == IDE_COMMAND_IDENTIFY) + { + PortExtension->DeviceParams.DeviceType = AHCI_DEVICE_TYPE_ATA; + if (IdentifyDeviceData->GeneralConfiguration.RemovableMedia) + { + PortExtension->DeviceParams.RemovableDevice = 1; + } + + if ((IdentifyDeviceData->CommandSetSupport.BigLba) && (IdentifyDeviceData->CommandSetActive.BigLba)) + { + PortExtension->DeviceParams.Lba48BitMode = 1; + } + + PortExtension->DeviceParams.AccessType = DIRECT_ACCESS_DEVICE; + + /* Device max address lba */ + if (PortExtension->DeviceParams.Lba48BitMode) + { + PortExtension->DeviceParams.MaxLba.LowPart = IdentifyDeviceData->Max48BitLBA[0]; + PortExtension->DeviceParams.MaxLba.HighPart = IdentifyDeviceData->Max48BitLBA[1]; + } + else + { + PortExtension->DeviceParams.MaxLba.LowPart = IdentifyDeviceData->UserAddressableSectors; + } + + /* Bytes Per Logical Sector */ + if (IdentifyDeviceData->PhysicalLogicalSectorSize.LogicalSectorLongerThan256Words) + { + AhciDebugPrint("\tBytesPerLogicalSector != DEVICE_ATA_BLOCK_SIZE\n"); + NT_ASSERT(FALSE); + } + + PortExtension->DeviceParams.BytesPerLogicalSector = DEVICE_ATA_BLOCK_SIZE; + + /* Bytes Per Physical Sector */ + if (IdentifyDeviceData->PhysicalLogicalSectorSize.MultipleLogicalSectorsPerPhysicalSector) + { + AhciDebugPrint("\tBytesPerPhysicalSector != DEVICE_ATA_BLOCK_SIZE\n"); + NT_ASSERT(FALSE); + } + + PortExtension->DeviceParams.BytesPerPhysicalSector = DEVICE_ATA_BLOCK_SIZE; + + // last byte should be NULL + StorPortCopyMemory(PortExtension->DeviceParams.VendorId, IdentifyDeviceData->ModelNumber, sizeof(PortExtension->DeviceParams.VendorId) - 1); + StorPortCopyMemory(PortExtension->DeviceParams.RevisionID, IdentifyDeviceData->FirmwareRevision, sizeof(PortExtension->DeviceParams.RevisionID) - 1); + StorPortCopyMemory(PortExtension->DeviceParams.SerialNumber, IdentifyDeviceData->SerialNumber, sizeof(PortExtension->DeviceParams.SerialNumber) - 1); + + PortExtension->DeviceParams.VendorId[sizeof(PortExtension->DeviceParams.VendorId) - 1] = '\0'; + PortExtension->DeviceParams.RevisionID[sizeof(PortExtension->DeviceParams.RevisionID) - 1] = '\0'; + PortExtension->DeviceParams.SerialNumber[sizeof(PortExtension->DeviceParams.SerialNumber) - 1] = '\0'; + + // TODO: Add other device params + AhciDebugPrint("\tATA Device\n"); + } + else + { + AhciDebugPrint("\tATAPI Device\n"); + PortExtension->DeviceParams.DeviceType = AHCI_DEVICE_TYPE_ATAPI; + PortExtension->DeviceParams.AccessType = READ_ONLY_DIRECT_ACCESS_DEVICE; + } + + // INQUIRYDATABUFFERSIZE = 36 ; Defined in storport.h + if (Srb->DataTransferLength < INQUIRYDATABUFFERSIZE) + { + AhciDebugPrint("\tDataBufferLength < sizeof(INQUIRYDATA), Could crash the driver.\n"); + NT_ASSERT(FALSE); + } + + // update data transfer length + Srb->DataTransferLength = INQUIRYDATABUFFERSIZE; + + // prepare data to send + InquiryData->Versions = 2; + InquiryData->Wide32Bit = 1; + InquiryData->CommandQueue = 0; // NCQ not supported + InquiryData->ResponseDataFormat = 0x2; + InquiryData->DeviceTypeModifier = 0; + InquiryData->DeviceTypeQualifier = DEVICE_CONNECTED; + InquiryData->AdditionalLength = INQUIRYDATABUFFERSIZE - 5; + InquiryData->DeviceType = PortExtension->DeviceParams.AccessType; + InquiryData->RemovableMedia = PortExtension->DeviceParams.RemovableDevice; + + // Fill VendorID, Product Revision Level and other string fields + StorPortCopyMemory(InquiryData->VendorId, PortExtension->DeviceParams.VendorId, sizeof(InquiryData->VendorId) - 1); + StorPortCopyMemory(InquiryData->ProductId, PortExtension->DeviceParams.RevisionID, sizeof(PortExtension->DeviceParams.RevisionID)); + StorPortCopyMemory(InquiryData->ProductRevisionLevel, PortExtension->DeviceParams.SerialNumber, sizeof(InquiryData->ProductRevisionLevel) - 1); + + InquiryData->VendorId[sizeof(InquiryData->VendorId) - 1] = '\0'; + InquiryData->ProductId[sizeof(InquiryData->ProductId) - 1] = '\0'; + InquiryData->ProductRevisionLevel[sizeof(InquiryData->ProductRevisionLevel) - 1] = '\0'; + + // send queue depth + status = StorPortSetDeviceQueueDepth(PortExtension->AdapterExtension, + Srb->PathId, + Srb->TargetId, + Srb->Lun, + AHCI_Global_Port_CAP_NCS(AdapterExtension->CAP)); + + NT_ASSERT(status == TRUE); + return; +}// -- InquiryCompletion(); + + /** + * @name AhciATAPICommand + * @implemented + * + * Handles ATAPI Requests commands + * + * @param AdapterExtension + * @param Srb + * @param Cdb + * + * @return + * return STOR status for AhciATAPICommand + */ +UCHAR +AhciATAPICommand ( + __in PAHCI_ADAPTER_EXTENSION AdapterExtension, + __in PSCSI_REQUEST_BLOCK Srb, + __in PCDB Cdb + ) +{ + ULONG SrbFlags, DataBufferLength; + PAHCI_SRB_EXTENSION SrbExtension; + PAHCI_PORT_EXTENSION PortExtension; + + AhciDebugPrint("AhciATAPICommand()\n"); + + SrbFlags = Srb->SrbFlags; + SrbExtension = GetSrbExtension(Srb); + DataBufferLength = Srb->DataTransferLength; + PortExtension = &AdapterExtension->PortExtension[Srb->PathId]; + + NT_ASSERT(PortExtension->DeviceParams.DeviceType == AHCI_DEVICE_TYPE_ATAPI); + + NT_ASSERT(SrbExtension != NULL); + + SrbExtension->AtaFunction = ATA_FUNCTION_ATAPI_COMMAND; + SrbExtension->Flags = 0; + + if (SrbFlags & SRB_FLAGS_DATA_IN) + { + SrbExtension->Flags |= ATA_FLAGS_DATA_IN; + } + + if (SrbFlags & SRB_FLAGS_DATA_OUT) + { + SrbExtension->Flags |= ATA_FLAGS_DATA_OUT; + } + + SrbExtension->FeaturesLow = 0; + + SrbExtension->CompletionRoutine = NULL; + + NT_ASSERT(Cdb != NULL); + switch(Cdb->CDB10.OperationCode) + { + case SCSIOP_INQUIRY: + SrbExtension->Flags |= ATA_FLAGS_DATA_IN; + SrbExtension->CompletionRoutine = AtapiInquiryCompletion; + break; + case SCSIOP_READ: + SrbExtension->Flags |= ATA_FLAGS_USE_DMA; + SrbExtension->FeaturesLow = 0x5; + break; + case SCSIOP_WRITE: + SrbExtension->Flags |= ATA_FLAGS_USE_DMA; + SrbExtension->FeaturesLow = 0x1; + break; + } + + SrbExtension->CommandReg = IDE_COMMAND_ATAPI_PACKET; + + SrbExtension->LBA0 = 0; + SrbExtension->LBA1 = (UCHAR)(DataBufferLength >> 0); + SrbExtension->LBA2 = (UCHAR)(DataBufferLength >> 8); + SrbExtension->Device = 0; + SrbExtension->LBA3 = 0; + SrbExtension->LBA4 = 0; + SrbExtension->LBA5 = 0; + SrbExtension->FeaturesHigh = 0; + SrbExtension->SectorCountLow = 0; + SrbExtension->SectorCountHigh = 0; + + if ((SrbExtension->Flags & ATA_FLAGS_DATA_IN) || (SrbExtension->Flags & ATA_FLAGS_DATA_OUT)) + { + SrbExtension->pSgl = (PLOCAL_SCATTER_GATHER_LIST)StorPortGetScatterGatherList(AdapterExtension, Srb); + } + + return SRB_STATUS_PENDING; +}// -- AhciATAPICommand(); + +/** + * @name DeviceRequestSense + * @implemented + * + * Handle SCSIOP_MODE_SENSE OperationCode + * + * @param AdapterExtension + * @param Srb + * @param Cdb + * + * @return + * return STOR status for DeviceRequestSense + */ +UCHAR +DeviceRequestSense ( + __in PAHCI_ADAPTER_EXTENSION AdapterExtension, + __in PSCSI_REQUEST_BLOCK Srb, + __in PCDB Cdb + ) +{ + PMODE_PARAMETER_HEADER ModeHeader; + PAHCI_PORT_EXTENSION PortExtension; + + AhciDebugPrint("DeviceRequestSense()\n"); + + NT_ASSERT(IsPortValid(AdapterExtension, Srb->PathId)); + NT_ASSERT(Cdb->CDB10.OperationCode == SCSIOP_MODE_SENSE); + + PortExtension = &AdapterExtension->PortExtension[Srb->PathId]; + + if (PortExtension->DeviceParams.DeviceType == AHCI_DEVICE_TYPE_ATAPI) + { + return AhciATAPICommand(AdapterExtension, Srb, Cdb); + } + + ModeHeader = (PMODE_PARAMETER_HEADER)Srb->DataBuffer; + + NT_ASSERT(ModeHeader != NULL); + + AhciZeroMemory((PCHAR)ModeHeader, Srb->DataTransferLength); + + ModeHeader->ModeDataLength = sizeof(MODE_PARAMETER_HEADER); + ModeHeader->MediumType = 0; + ModeHeader->DeviceSpecificParameter = 0; + ModeHeader->BlockDescriptorLength = 0; + + if (Cdb->MODE_SENSE.PageCode == MODE_SENSE_CURRENT_VALUES) + { + ModeHeader->ModeDataLength = sizeof(MODE_PARAMETER_HEADER) + sizeof(MODE_PARAMETER_BLOCK); + ModeHeader->BlockDescriptorLength = sizeof(MODE_PARAMETER_BLOCK); + } + + return SRB_STATUS_SUCCESS; +}// -- DeviceRequestSense(); + +/** + * @name DeviceRequestReadWrite + * @implemented + * + * Handle SCSIOP_READ SCSIOP_WRITE OperationCode + * + * @param AdapterExtension + * @param Srb + * @param Cdb + * + * @return + * return STOR status for DeviceRequestReadWrite + */ +UCHAR +DeviceRequestReadWrite ( + __in PAHCI_ADAPTER_EXTENSION AdapterExtension, + __in PSCSI_REQUEST_BLOCK Srb, + __in PCDB Cdb + ) +{ + BOOLEAN IsReading; + ULONG64 StartOffset; + PAHCI_SRB_EXTENSION SrbExtension; + PAHCI_PORT_EXTENSION PortExtension; + ULONG DataTransferLength, BytesPerSector, SectorCount; + + AhciDebugPrint("DeviceRequestReadWrite()\n"); + + NT_ASSERT(IsPortValid(AdapterExtension, Srb->PathId)); + NT_ASSERT((Cdb->CDB10.OperationCode == SCSIOP_READ) || (Cdb->CDB10.OperationCode == SCSIOP_WRITE)); + + SrbExtension = GetSrbExtension(Srb); + PortExtension = &AdapterExtension->PortExtension[Srb->PathId]; + + if (PortExtension->DeviceParams.DeviceType == AHCI_DEVICE_TYPE_ATAPI) + { + return AhciATAPICommand(AdapterExtension, Srb, Cdb); + } + + DataTransferLength = Srb->DataTransferLength; + BytesPerSector = PortExtension->DeviceParams.BytesPerLogicalSector; + + NT_ASSERT(BytesPerSector > 0); + + //ROUND_UP(DataTransferLength, BytesPerSector); + + SectorCount = DataTransferLength / BytesPerSector; + + Srb->DataTransferLength = SectorCount * BytesPerSector; + + StartOffset = AhciGetLba(Cdb, Srb->CdbLength); + IsReading = (Cdb->CDB10.OperationCode == SCSIOP_READ); + + NT_ASSERT(SectorCount > 0); + + SrbExtension->AtaFunction = ATA_FUNCTION_ATA_READ; + SrbExtension->Flags |= ATA_FLAGS_USE_DMA; + SrbExtension->CompletionRoutine = NULL; + + if (IsReading) + { + SrbExtension->Flags |= ATA_FLAGS_DATA_IN; + SrbExtension->CommandReg = IDE_COMMAND_READ_DMA; + } + else + { + SrbExtension->Flags |= ATA_FLAGS_DATA_OUT; + SrbExtension->CommandReg = IDE_COMMAND_WRITE_DMA; + } + + SrbExtension->FeaturesLow = 0; + SrbExtension->LBA0 = (StartOffset >> 0) & 0xFF; + SrbExtension->LBA1 = (StartOffset >> 8) & 0xFF; + SrbExtension->LBA2 = (StartOffset >> 16) & 0xFF; + + SrbExtension->Device = (0xA0 | IDE_LBA_MODE); + + if (PortExtension->DeviceParams.Lba48BitMode) + { + SrbExtension->Flags |= ATA_FLAGS_48BIT_COMMAND; + + if (IsReading) + { + SrbExtension->CommandReg = IDE_COMMAND_READ_DMA_EXT; + } + else + { + SrbExtension->CommandReg = IDE_COMMAND_WRITE_DMA_EXT; + } + + SrbExtension->LBA3 = (StartOffset >> 24) & 0xFF; + SrbExtension->LBA4 = (StartOffset >> 32) & 0xFF; + SrbExtension->LBA5 = (StartOffset >> 40) & 0xFF; + } + else + { + NT_ASSERT(FALSE); + } + + SrbExtension->FeaturesHigh = 0; + SrbExtension->SectorCountLow = (SectorCount >> 0) & 0xFF; + SrbExtension->SectorCountHigh = (SectorCount >> 8) & 0xFF; + + NT_ASSERT(SectorCount < 0x100); + + SrbExtension->pSgl = (PLOCAL_SCATTER_GATHER_LIST)StorPortGetScatterGatherList(AdapterExtension, Srb); + + return SRB_STATUS_PENDING; +}// -- DeviceRequestReadWrite(); + +/** + * @name DeviceRequestCapacity + * @implemented + * + * Handle SCSIOP_READ_CAPACITY OperationCode + * + * @param AdapterExtension + * @param Srb + * @param Cdb + * + * @return + * return STOR status for DeviceRequestCapacity + */ +UCHAR +DeviceRequestCapacity ( + __in PAHCI_ADAPTER_EXTENSION AdapterExtension, + __in PSCSI_REQUEST_BLOCK Srb, + __in PCDB Cdb + ) +{ + ULONG MaxLba, BytesPerLogicalSector; + PREAD_CAPACITY_DATA ReadCapacity; + PAHCI_PORT_EXTENSION PortExtension; + + AhciDebugPrint("DeviceRequestCapacity()\n"); + + UNREFERENCED_PARAMETER(AdapterExtension); + UNREFERENCED_PARAMETER(Cdb); + + NT_ASSERT(Srb->DataBuffer != NULL); + NT_ASSERT(IsPortValid(AdapterExtension, Srb->PathId)); + + + PortExtension = &AdapterExtension->PortExtension[Srb->PathId]; + + if (PortExtension->DeviceParams.DeviceType == AHCI_DEVICE_TYPE_ATAPI) + { + return AhciATAPICommand(AdapterExtension, Srb, Cdb); + } + + if (Cdb->CDB10.OperationCode == SCSIOP_READ_CAPACITY) + { + ReadCapacity = (PREAD_CAPACITY_DATA)Srb->DataBuffer; + + BytesPerLogicalSector = PortExtension->DeviceParams.BytesPerLogicalSector; + MaxLba = (ULONG)PortExtension->DeviceParams.MaxLba.QuadPart - 1; + + // I trust you windows :D + NT_ASSERT(Srb->DataTransferLength >= sizeof(READ_CAPACITY_DATA)); + + // I trust you user :D + NT_ASSERT(PortExtension->DeviceParams.MaxLba.QuadPart < (ULONG)-1); + + // Actually I don't trust anyone :p + Srb->DataTransferLength = sizeof(READ_CAPACITY_DATA); + + REVERSE_BYTES(&ReadCapacity->BytesPerBlock, &BytesPerLogicalSector); + REVERSE_BYTES(&ReadCapacity->LogicalBlockAddress, &MaxLba); + } + else + { + AhciDebugPrint("\tSCSIOP_READ_CAPACITY16 not supported\n"); + NT_ASSERT(FALSE); + } + + return SRB_STATUS_SUCCESS; +}// -- DeviceRequestCapacity(); + +/** + * @name DeviceRequestComplete + * @implemented + * + * Handle UnHandled Requests + * + * @param AdapterExtension + * @param Srb + * @param Cdb + * + * @return + * return STOR status for DeviceRequestComplete + */ +UCHAR +DeviceRequestComplete ( + __in PAHCI_ADAPTER_EXTENSION AdapterExtension, + __in PSCSI_REQUEST_BLOCK Srb, + __in PCDB Cdb + ) +{ + AhciDebugPrint("DeviceRequestComplete()\n"); + + UNREFERENCED_PARAMETER(AdapterExtension); + UNREFERENCED_PARAMETER(Cdb); + + Srb->ScsiStatus = SCSISTAT_GOOD; + + return SRB_STATUS_SUCCESS; +}// -- DeviceRequestComplete(); + +/** + * @name DeviceReportLuns + * @implemented + * + * Handle SCSIOP_REPORT_LUNS OperationCode + * + * @param AdapterExtension + * @param Srb + * @param Cdb + * + * @return + * return STOR status for DeviceReportLuns + */ +UCHAR +DeviceReportLuns ( + __in PAHCI_ADAPTER_EXTENSION AdapterExtension, + __in PSCSI_REQUEST_BLOCK Srb, + __in PCDB Cdb + ) +{ + PLUN_LIST LunList; + PAHCI_PORT_EXTENSION PortExtension; + + AhciDebugPrint("DeviceReportLuns()\n"); + + UNREFERENCED_PARAMETER(Cdb); + + PortExtension = &AdapterExtension->PortExtension[Srb->PathId]; + + NT_ASSERT(Srb->DataTransferLength >= sizeof(LUN_LIST)); + NT_ASSERT(Cdb->CDB10.OperationCode == SCSIOP_REPORT_LUNS); + + if (PortExtension->DeviceParams.DeviceType == AHCI_DEVICE_TYPE_ATAPI) + { + return AhciATAPICommand(AdapterExtension, Srb, Cdb); + } + + LunList = (PLUN_LIST)Srb->DataBuffer; + + NT_ASSERT(LunList != NULL); + + AhciZeroMemory((PCHAR)LunList, sizeof(LUN_LIST)); + + LunList->LunListLength[3] = 8; + + Srb->ScsiStatus = SCSISTAT_GOOD; + Srb->DataTransferLength = sizeof(LUN_LIST); + + return SRB_STATUS_SUCCESS; +}// -- DeviceReportLuns(); + +/** + * @name DeviceInquiryRequest + * @implemented + * + * Tells wheather given port is implemented or not + * + * @param AdapterExtension + * @param Srb + * @param Cdb + * + * @return + * return STOR status for DeviceInquiryRequest + * + * @remark + * http://www.seagate.com/staticfiles/support/disc/manuals/Interface%20manuals/100293068c.pdf + */ +UCHAR +DeviceInquiryRequest ( + __in PAHCI_ADAPTER_EXTENSION AdapterExtension, + __in PSCSI_REQUEST_BLOCK Srb, + __in PCDB Cdb + ) +{ + PVOID DataBuffer; + PAHCI_SRB_EXTENSION SrbExtension; + PAHCI_PORT_EXTENSION PortExtension; + PVPD_SUPPORTED_PAGES_PAGE VpdOutputBuffer; + ULONG DataBufferLength, RequiredDataBufferLength; + + AhciDebugPrint("DeviceInquiryRequest()\n"); + + NT_ASSERT(Cdb->CDB10.OperationCode == SCSIOP_INQUIRY); + NT_ASSERT(IsPortValid(AdapterExtension, Srb->PathId)); + + SrbExtension = GetSrbExtension(Srb); + PortExtension = &AdapterExtension->PortExtension[Srb->PathId]; + + if (PortExtension->DeviceParams.DeviceType == AHCI_DEVICE_TYPE_ATAPI) + { + return AhciATAPICommand(AdapterExtension, Srb, Cdb); + } + + if (Srb->Lun != 0) + { + return SRB_STATUS_SELECTION_TIMEOUT; + } + else if (Cdb->CDB6INQUIRY3.EnableVitalProductData == 0) + { + // 3.6.1 + // If the EVPD bit is set to zero, the device server shall return the standard INQUIRY data + AhciDebugPrint("\tEVPD Inquired\n"); + NT_ASSERT(SrbExtension != NULL); + + SrbExtension->AtaFunction = ATA_FUNCTION_ATA_IDENTIFY; + SrbExtension->Flags |= ATA_FLAGS_DATA_IN; + SrbExtension->CompletionRoutine = InquiryCompletion; + SrbExtension->CommandReg = IDE_COMMAND_NOT_VALID; + + // TODO: Should use AhciZeroMemory + SrbExtension->FeaturesLow = 0; + SrbExtension->LBA0 = 0; + SrbExtension->LBA1 = 0; + SrbExtension->LBA2 = 0; + SrbExtension->Device = 0xA0; + SrbExtension->LBA3 = 0; + SrbExtension->LBA4 = 0; + SrbExtension->LBA5 = 0; + SrbExtension->FeaturesHigh = 0; + SrbExtension->SectorCountLow = 0; + SrbExtension->SectorCountHigh = 0; + + SrbExtension->Sgl.NumberOfElements = 1; + SrbExtension->Sgl.List[0].PhysicalAddress.LowPart = PortExtension->IdentifyDeviceDataPhysicalAddress.LowPart; + SrbExtension->Sgl.List[0].PhysicalAddress.HighPart = PortExtension->IdentifyDeviceDataPhysicalAddress.HighPart; + SrbExtension->Sgl.List[0].Length = sizeof(IDENTIFY_DEVICE_DATA); + + SrbExtension->pSgl = &SrbExtension->Sgl; + return SRB_STATUS_PENDING; + } + else + { + AhciDebugPrint("\tVPD Inquired\n"); + + DataBuffer = Srb->DataBuffer; + DataBufferLength = Srb->DataTransferLength; + RequiredDataBufferLength = DataBufferLength; // make the compiler happy :p + + if (DataBuffer == NULL) + { + return SRB_STATUS_INVALID_REQUEST; + } + + AhciZeroMemory(DataBuffer, DataBufferLength); + + switch(Cdb->CDB6INQUIRY3.PageCode) + { + case VPD_SUPPORTED_PAGES: + { + AhciDebugPrint("\tVPD_SUPPORTED_PAGES\n"); + RequiredDataBufferLength = sizeof(VPD_SUPPORTED_PAGES_PAGE) + 1; + + if (DataBufferLength < RequiredDataBufferLength) + { + AhciDebugPrint("\tDataBufferLength: %d Required: %d\n", DataBufferLength, RequiredDataBufferLength); + return SRB_STATUS_INVALID_REQUEST; + } + + VpdOutputBuffer = (PVPD_SUPPORTED_PAGES_PAGE)DataBuffer; + + VpdOutputBuffer->DeviceType = PortExtension->DeviceParams.AccessType; + VpdOutputBuffer->DeviceTypeQualifier = 0; + VpdOutputBuffer->PageCode = VPD_SUPPORTED_PAGES; + VpdOutputBuffer->PageLength = 1; + VpdOutputBuffer->SupportedPageList[0] = VPD_SUPPORTED_PAGES; + //VpdOutputBuffer->SupportedPageList[1] = VPD_SERIAL_NUMBER; + //VpdOutputBuffer->SupportedPageList[2] = VPD_DEVICE_IDENTIFIERS; + + NT_ASSERT(VpdOutputBuffer->DeviceType == DIRECT_ACCESS_DEVICE); + } + break; + case VPD_SERIAL_NUMBER: + { + AhciDebugPrint("\tVPD_SERIAL_NUMBER\n"); + } + break; + case VPD_DEVICE_IDENTIFIERS: + { + AhciDebugPrint("\tVPD_DEVICE_IDENTIFIERS\n"); + } + break; + default: + AhciDebugPrint("\tPageCode: %x\n", Cdb->CDB6INQUIRY3.PageCode); + return SRB_STATUS_INVALID_REQUEST; + } + + Srb->DataTransferLength = RequiredDataBufferLength; + return SRB_STATUS_SUCCESS; + } +}// -- DeviceInquiryRequest(); + +/** + * @name AhciAdapterReset + * @implemented + * + * 10.4.3 HBA Reset + * If the HBA becomes unusable for multiple ports, and a software reset or port reset does not correct the + * problem, software may reset the entire HBA by setting GHC.HR to ‘1’. When software sets the GHC.HR + * bit to ‘1’, the HBA shall perform an internal reset action. The bit shall be cleared to ‘0’ by the HBA when + * the reset is complete. A software write of ‘0’ to GHC.HR shall have no effect. To perform the HBA reset, + * software sets GHC.HR to ‘1’ and may poll until this bit is read to be ‘0’, at which point software knows that + * the HBA reset has completed. + * If the HBA has not cleared GHC.HR to ‘0’ within 1 second of software setting GHC.HR to ‘1’, the HBA is in + * a hung or locked state. + * + * @param AdapterExtension + * + * @return + * TRUE in case AHCI Controller RESTARTED successfully. i.e GHC.HR == 0 + */ +BOOLEAN +AhciAdapterReset ( + __in PAHCI_ADAPTER_EXTENSION AdapterExtension + ) +{ + ULONG ticks; + AHCI_GHC ghc; + PAHCI_MEMORY_REGISTERS abar = NULL; + + AhciDebugPrint("AhciAdapterReset()\n"); + + abar = AdapterExtension->ABAR_Address; + if (abar == NULL) // basic sanity + { + return FALSE; + } + + // HR -- Very first bit (lowest significant) + ghc.HR = 1; + StorPortWriteRegisterUlong(AdapterExtension, &abar->GHC, ghc.Status); + + for (ticks = 0; ticks < 50; ++ticks) + { + ghc.Status = StorPortReadRegisterUlong(AdapterExtension, &abar->GHC); + if (ghc.HR == 0) + { + break; + } + StorPortStallExecution(20000); + } + + if (ticks == 50)// 1 second + { + AhciDebugPrint("\tDevice Timeout\n"); + return FALSE; + } + + return TRUE; +}// -- AhciAdapterReset(); + +/** + * @name AhciZeroMemory + * @implemented + * + * Clear buffer by filling zeros + * + * @param Buffer + * @param BufferSize + */ +__inline +VOID +AhciZeroMemory ( + __out PCHAR Buffer, + __in ULONG BufferSize + ) +{ + ULONG i; + for (i = 0; i < BufferSize; i++) + { + Buffer[i] = 0; + } + + return; +}// -- AhciZeroMemory(); + +/** + * @name IsPortValid + * @implemented + * + * Tells wheather given port is implemented or not + * + * @param AdapterExtension + * @param PathId + * + * @return + * return TRUE if provided port is valid (implemented) or not + */ +__inline +BOOLEAN +IsPortValid ( + __in PAHCI_ADAPTER_EXTENSION AdapterExtension, + __in ULONG pathId + ) +{ + NT_ASSERT(pathId < MAXIMUM_AHCI_PORT_COUNT); + + if (pathId >= AdapterExtension->PortCount) + { + return FALSE; + } + + return AdapterExtension->PortExtension[pathId].DeviceParams.IsActive; +}// -- IsPortValid() + +/** + * @name AddQueue + * @implemented + * + * Add Srb to Queue + * + * @param Queue + * @param Srb + * + * @return + * return TRUE if Srb is successfully added to Queue + * + */ +__inline +BOOLEAN +AddQueue ( + __inout PAHCI_QUEUE Queue, + __in PVOID Srb + ) +{ + NT_ASSERT(Queue->Head < MAXIMUM_QUEUE_BUFFER_SIZE); + NT_ASSERT(Queue->Tail < MAXIMUM_QUEUE_BUFFER_SIZE); + + if (Queue->Tail == ((Queue->Head + 1) % MAXIMUM_QUEUE_BUFFER_SIZE)) + return FALSE; + + Queue->Buffer[Queue->Head++] = Srb; + Queue->Head %= MAXIMUM_QUEUE_BUFFER_SIZE; + + return TRUE; +}// -- AddQueue(); + +/** + * @name RemoveQueue + * @implemented + * + * Remove and return Srb from Queue + * + * @param Queue + * + * @return + * return Srb + * + */ +__inline +PVOID +RemoveQueue ( + __inout PAHCI_QUEUE Queue + ) +{ + PVOID Srb; + + NT_ASSERT(Queue->Head < MAXIMUM_QUEUE_BUFFER_SIZE); + NT_ASSERT(Queue->Tail < MAXIMUM_QUEUE_BUFFER_SIZE); + + if (Queue->Head == Queue->Tail) + return NULL; + + Srb = Queue->Buffer[Queue->Tail++]; + Queue->Tail %= MAXIMUM_QUEUE_BUFFER_SIZE; + + return Srb; +}// -- RemoveQueue(); + +/** + * @name GetSrbExtension + * @implemented + * + * GetSrbExtension from Srb make sure It is properly aligned + * + * @param Srb + * + * @return + * return SrbExtension + * + */ +__inline +PAHCI_SRB_EXTENSION +GetSrbExtension ( + __in PSCSI_REQUEST_BLOCK Srb + ) +{ + ULONG Offset; + ULONG_PTR SrbExtension; + + SrbExtension = (ULONG_PTR)Srb->SrbExtension; + Offset = SrbExtension % 128; + + // CommandTable should be 128 byte aligned + if (Offset != 0) + Offset = 128 - Offset; + + return (PAHCI_SRB_EXTENSION)(SrbExtension + Offset); +}// -- PAHCI_SRB_EXTENSION(); + +/** + * @name AhciGetLba + * @implemented + * + * Find the logical address of demand block from Cdb + * + * @param Srb + * + * @return + * return Logical Address of the block + * + */ +__inline +ULONG64 +AhciGetLba ( + __in PCDB Cdb, + __in ULONG CdbLength + ) +{ + ULONG64 lba = 0; + + NT_ASSERT(Cdb != NULL); + NT_ASSERT(CdbLength != 0); + + if (CdbLength == 0x10) + { + REVERSE_BYTES_QUAD(&lba, Cdb->CDB16.LogicalBlock); + } + else + { + lba |= Cdb->CDB10.LogicalBlockByte3 << 0; + lba |= Cdb->CDB10.LogicalBlockByte2 << 8; + lba |= Cdb->CDB10.LogicalBlockByte1 << 16; + lba |= Cdb->CDB10.LogicalBlockByte0 << 24; + } + + return lba; +}// -- AhciGetLba(); \ No newline at end of file diff --git a/reactos/drivers/storage/storahci/storahci.h b/reactos/drivers/storage/storahci/storahci.h new file mode 100644 index 00000000000..6adcc50aced --- /dev/null +++ b/reactos/drivers/storage/storahci/storahci.h @@ -0,0 +1,711 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: GNU GPLv2 only as published by the Free Software Foundation + * PURPOSE: To Implement AHCI Miniport driver targeting storport NT 5.2 + * PROGRAMMERS: Aman Priyadarshi (aman.eureka@gmail.com) + */ + +#include +#include +#include + +#define DEBUG 1 +#pragma warning(disable:4214) // bit field types other than int +#pragma warning(disable:4201) // nameless struct/union + +#define MAXIMUM_AHCI_PORT_COUNT 32 +#define MAXIMUM_AHCI_PRDT_ENTRIES 32 +#define MAXIMUM_AHCI_PORT_NCS 30 +#define MAXIMUM_QUEUE_BUFFER_SIZE 255 +#define MAXIMUM_TRANSFER_LENGTH (128*1024) // 128 KB + +#define DEVICE_ATA_BLOCK_SIZE 512 + +// device type (DeviceParams) +#define AHCI_DEVICE_TYPE_ATA 1 +#define AHCI_DEVICE_TYPE_ATAPI 2 +#define AHCI_DEVICE_TYPE_NODEVICE 3 + +// section 3.1.2 +#define AHCI_Global_HBA_CAP_S64A (1 << 31) + +// FIS Types : http://wiki.osdev.org/AHCI +#define FIS_TYPE_REG_H2D 0x27 // Register FIS - host to device +#define FIS_TYPE_REG_D2H 0x34 // Register FIS - device to host +#define FIS_TYPE_DMA_ACT 0x39 // DMA activate FIS - device to host +#define FIS_TYPE_DMA_SETUP 0x41 // DMA setup FIS - bidirectional +#define FIS_TYPE_BIST 0x58 // BIST activate FIS - bidirectional +#define FIS_TYPE_PIO_SETUP 0x5F // PIO setup FIS - device to host +#define FIS_TYPE_DEV_BITS 0xA1 // Set device bits FIS - device to host + +#define AHCI_ATA_CFIS_FisType 0 +#define AHCI_ATA_CFIS_PMPort_C 1 +#define AHCI_ATA_CFIS_CommandReg 2 +#define AHCI_ATA_CFIS_FeaturesLow 3 +#define AHCI_ATA_CFIS_LBA0 4 +#define AHCI_ATA_CFIS_LBA1 5 +#define AHCI_ATA_CFIS_LBA2 6 +#define AHCI_ATA_CFIS_Device 7 +#define AHCI_ATA_CFIS_LBA3 8 +#define AHCI_ATA_CFIS_LBA4 9 +#define AHCI_ATA_CFIS_LBA5 10 +#define AHCI_ATA_CFIS_FeaturesHigh 11 +#define AHCI_ATA_CFIS_SectorCountLow 12 +#define AHCI_ATA_CFIS_SectorCountHigh 13 + +// ATA Functions +#define ATA_FUNCTION_ATA_COMMAND 0x100 +#define ATA_FUNCTION_ATA_IDENTIFY 0x101 +#define ATA_FUNCTION_ATA_READ 0x102 + +// ATAPI Functions +#define ATA_FUNCTION_ATAPI_COMMAND 0x200 + +// ATA Flags +#define ATA_FLAGS_DATA_IN (1 << 1) +#define ATA_FLAGS_DATA_OUT (1 << 2) +#define ATA_FLAGS_48BIT_COMMAND (1 << 3) +#define ATA_FLAGS_USE_DMA (1 << 4) + +#define IsAtaCommand(AtaFunction) (AtaFunction & ATA_FUNCTION_ATA_COMMAND) +#define IsAtapiCommand(AtaFunction) (AtaFunction & ATA_FUNCTION_ATAPI_COMMAND) +#define IsDataTransferNeeded(SrbExtension) (SrbExtension->Flags & (ATA_FLAGS_DATA_IN | ATA_FLAGS_DATA_OUT)) +#define IsAdapterCAPS64(CAP) (CAP & AHCI_Global_HBA_CAP_S64A) + +// 3.1.1 NCS = CAP[12:08] -> Align +#define AHCI_Global_Port_CAP_NCS(x) (((x) & 0xF00) >> 8) + +#define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S)) +#define AhciDebugPrint(format, ...) StorPortDebugPrint(0, format, __VA_ARGS__) + +typedef +VOID +(*PAHCI_COMPLETION_ROUTINE) ( + __in PVOID PortExtension, + __in PVOID Srb + ); + +////////////////////////////////////////////////////////////// +// ---- Support Structures --- // +////////////////////////////////////////////////////////////// + +// section 3.3.5 +typedef union _AHCI_INTERRUPT_STATUS +{ + struct + { + ULONG DHRS:1; //Device to Host Register FIS Interrupt + ULONG PSS :1; //PIO Setup FIS Interrupt + ULONG DSS :1; //DMA Setup FIS Interrupt + ULONG SDBS :1; //Set Device Bits Interrupt + ULONG UFS :1; //Unknown FIS Interrupt + ULONG DPS :1; //Descriptor Processed + ULONG PCS :1; //Port Connect Change Status + ULONG DMPS :1; //Device Mechanical Presence Status (DMPS) + ULONG Reserved :14; + ULONG PRCS :1; //PhyRdy Change Status + ULONG IPMS :1; //Incorrect Port Multiplier Status + ULONG OFS :1; //Overflow Status + ULONG Reserved2 :1; + ULONG INFS :1; //Interface Non-fatal Error Status + ULONG IFS :1; //Interface Fatal Error Status + ULONG HBDS :1; //Host Bus Data Error Status + ULONG HBFS :1; //Host Bus Fatal Error Status + ULONG TFES :1; //Task File Error Status + ULONG CPDS :1; //Cold Port Detect Status + }; + + ULONG Status; +} AHCI_INTERRUPT_STATUS; + +typedef struct _AHCI_FIS_DMA_SETUP +{ + ULONG ULONG0_1; // FIS_TYPE_DMA_SETUP + // Port multiplier + // Reserved + // Data transfer direction, 1 - device to host + // Interrupt bit + // Auto-activate. Specifies if DMA Activate FIS is needed + UCHAR Reserved[2]; // Reserved + ULONG DmaBufferLow; // DMA Buffer Identifier. Used to Identify DMA buffer in host memory. SATA Spec says host specific and not in Spec. Trying AHCI spec might work. + ULONG DmaBufferHigh; + ULONG Reserved2; // More reserved + ULONG DmaBufferOffset; // Byte offset into buffer. First 2 bits must be 0 + ULONG TranferCount; // Number of bytes to transfer. Bit 0 must be 0 + ULONG Reserved3; // Reserved +} AHCI_FIS_DMA_SETUP; + +typedef struct _AHCI_PIO_SETUP_FIS +{ + UCHAR FisType; + UCHAR Reserved1 :5; + UCHAR D :1; + UCHAR I :1; + UCHAR Reserved2 :1; + UCHAR Status; + UCHAR Error; + + UCHAR SectorNumber; + UCHAR CylLow; + UCHAR CylHigh; + UCHAR Dev_Head; + + UCHAR SectorNumb_Exp; + UCHAR CylLow_Exp; + UCHAR CylHigh_Exp; + UCHAR Reserved3; + + UCHAR SectorCount; + UCHAR SectorCount_Exp; + UCHAR Reserved4; + UCHAR E_Status; + + USHORT TransferCount; + UCHAR Reserved5[2]; +} AHCI_PIO_SETUP_FIS; + +typedef struct _AHCI_D2H_REGISTER_FIS +{ + UCHAR FisType; + UCHAR Reserved1 :6; + UCHAR I:1; + UCHAR Reserved2 :1; + UCHAR Status; + UCHAR Error; + + UCHAR SectorNumber; + UCHAR CylLow; + UCHAR CylHigh; + UCHAR Dev_Head; + + UCHAR SectorNum_Exp; + UCHAR CylLow_Exp; + UCHAR CylHigh_Exp; + UCHAR Reserved; + + UCHAR SectorCount; + UCHAR SectorCount_Exp; + UCHAR Reserved3[2]; + + UCHAR Reserved4[4]; +} AHCI_D2H_REGISTER_FIS; + +typedef struct _AHCI_SET_DEVICE_BITS_FIS +{ + UCHAR FisType; + + UCHAR PMPort: 4; + UCHAR Reserved1 :2; + UCHAR I :1; + UCHAR N :1; + + UCHAR Status_Lo :3; + UCHAR Reserved2 :1; + UCHAR Status_Hi :3; + UCHAR Reserved3 :1; + + UCHAR Error; + + UCHAR Reserved5[4]; +} AHCI_SET_DEVICE_BITS_FIS; + +typedef struct _AHCI_QUEUE +{ + PVOID Buffer[MAXIMUM_QUEUE_BUFFER_SIZE]; // because Storahci hold Srb queue of 255 size + ULONG Head; + ULONG Tail; +} AHCI_QUEUE, *PAHCI_QUEUE; + +////////////////////////////////////////////////////////////// +// --------------------------- // +////////////////////////////////////////////////////////////// + +typedef union _AHCI_COMMAND_HEADER_DESCRIPTION +{ + struct + { + ULONG CFL : 5; // Command FIS Length + ULONG A : 1; // IsATAPI + ULONG W : 1; // Write + ULONG P : 1; // Prefetchable + + ULONG R : 1; // Reset + ULONG B : 1; // BIST + ULONG C : 1; //Clear Busy upon R_OK + ULONG RSV : 1; + ULONG PMP : 4; //Port Multiplier Port + + ULONG PRDTL : 16; //Physical Region Descriptor Table Length + }; + + ULONG Status; +} AHCI_COMMAND_HEADER_DESCRIPTION; + +typedef union _AHCI_GHC +{ + struct + { + ULONG HR : 1; + ULONG IE : 1; + ULONG MRSM : 1; + ULONG RSV0 : 28; + ULONG AE : 1; + }; + + ULONG Status; +} AHCI_GHC; + +// section 3.3.7 +typedef union _AHCI_PORT_CMD +{ + struct + { + ULONG ST : 1; + ULONG SUD : 1; + ULONG POD : 1; + ULONG CLO : 1; + ULONG FRE : 1; + ULONG RSV0 : 3; + ULONG CCS : 5; + ULONG MPSS : 1; + ULONG FR : 1; + ULONG CR : 1; + ULONG CPS : 1; + ULONG PMA : 1; + ULONG HPCP : 1; + ULONG MPSP : 1; + ULONG CPD : 1; + ULONG ESP : 1; + ULONG FBSCP : 1; + ULONG APSTE : 1; + ULONG ATAPI : 1; + ULONG DLAE : 1; + ULONG ALPE : 1; + ULONG ASP : 1; + ULONG ICC : 4; + }; + + ULONG Status; +} AHCI_PORT_CMD; + +typedef union _AHCI_SERIAL_ATA_CONTROL +{ + struct + { + ULONG DET :4; + ULONG SPD :4; + ULONG IPM :4; + ULONG SPM :4; + ULONG PMP :4; + ULONG DW11_Reserved :12; + }; + + ULONG Status; +} AHCI_SERIAL_ATA_CONTROL; + +typedef union _AHCI_SERIAL_ATA_STATUS +{ + struct + { + ULONG DET :4; + ULONG SPD :4; + ULONG IPM :4; + ULONG RSV0 :20; + }; + + ULONG Status; +} AHCI_SERIAL_ATA_STATUS; + +typedef union _AHCI_TASK_FILE_DATA +{ + struct + { + struct _STS + { + UCHAR ERR : 1; + UCHAR CS1 : 2; + UCHAR DRQ : 1; + UCHAR CS2 : 3; + UCHAR BSY : 1; + } STS; + UCHAR ERR; + USHORT RSV; + }; + + ULONG Status; +} AHCI_TASK_FILE_DATA; + +typedef struct _AHCI_PRDT +{ + ULONG DBA; + ULONG DBAU; + ULONG RSV0; + + ULONG DBC : 22; + ULONG RSV1 : 9; + ULONG I : 1; +} AHCI_PRDT, *PAHCI_PRDT; + +// 4.2.3 Command Table +typedef struct _AHCI_COMMAND_TABLE +{ + // (16 * 32) + 64 + 16 + 48 = 648 + // 128 byte aligned :D + UCHAR CFIS[64]; + UCHAR ACMD[16]; + UCHAR RSV0[48]; + AHCI_PRDT PRDT[MAXIMUM_AHCI_PRDT_ENTRIES]; +} AHCI_COMMAND_TABLE, *PAHCI_COMMAND_TABLE; + +// 4.2.2 Command Header +typedef struct _AHCI_COMMAND_HEADER +{ + AHCI_COMMAND_HEADER_DESCRIPTION DI; // DW 0 + ULONG PRDBC; // DW 1 + ULONG CTBA; // DW 2 + ULONG CTBA_U; // DW 3 + ULONG Reserved[4]; // DW 4-7 +} AHCI_COMMAND_HEADER, *PAHCI_COMMAND_HEADER; + +// Received FIS +typedef struct _AHCI_RECEIVED_FIS +{ + struct _AHCI_FIS_DMA_SETUP DmaSetupFIS; // 0x00 -- DMA Setup FIS + ULONG pad0; // 4 BYTE padding + struct _AHCI_PIO_SETUP_FIS PioSetupFIS; // 0x20 -- PIO Setup FIS + ULONG pad1[3]; // 12 BYTE padding + struct _AHCI_D2H_REGISTER_FIS RegisterFIS; // 0x40 -- Register – Device to Host FIS + ULONG pad2; // 4 BYTE padding + struct _AHCI_SET_DEVICE_BITS_FIS SetDeviceFIS; // 0x58 -- Set Device Bit FIS + ULONG UnknowFIS[16]; // 0x60 -- Unknown FIS + ULONG Reserved[24]; // 0xA0 -- Reserved +} AHCI_RECEIVED_FIS, *PAHCI_RECEIVED_FIS; + +// Holds Port Information +typedef struct _AHCI_PORT +{ + ULONG CLB; // 0x00, command list base address, 1K-byte aligned + ULONG CLBU; // 0x04, command list base address upper 32 bits + ULONG FB; // 0x08, FIS base address, 256-byte aligned + ULONG FBU; // 0x0C, FIS base address upper 32 bits + ULONG IS; // 0x10, interrupt status + ULONG IE; // 0x14, interrupt enable + ULONG CMD; // 0x18, command and status + ULONG RSV0; // 0x1C, Reserved + ULONG TFD; // 0x20, task file data + ULONG SIG; // 0x24, signature + ULONG SSTS; // 0x28, SATA status (SCR0:SStatus) + ULONG SCTL; // 0x2C, SATA control (SCR2:SControl) + ULONG SERR; // 0x30, SATA error (SCR1:SError) + ULONG SACT; // 0x34, SATA active (SCR3:SActive) + ULONG CI; // 0x38, command issue + ULONG SNTF; // 0x3C, SATA notification (SCR4:SNotification) + ULONG FBS; // 0x40, FIS-based switch control + ULONG RSV1[11]; // 0x44 ~ 0x6F, Reserved + ULONG Vendor[4]; // 0x70 ~ 0x7F, vendor specific +} AHCI_PORT, *PAHCI_PORT; + +typedef union _AHCI_INTERRUPT_ENABLE +{ + struct + { + ULONG DHRE :1; + ULONG PSE :1; + ULONG DSE :1; + ULONG SDBE :1; + ULONG UFE :1; + ULONG DPE :1; + ULONG PCE :1; + ULONG DMPE :1; + ULONG DW5_Reserved :14; + ULONG PRCE :1; + ULONG IPME :1; + ULONG OFE :1; + ULONG DW5_Reserved2 :1; + ULONG INFE :1; + ULONG IFE :1; + ULONG HBDE :1; + ULONG HBFE :1; + ULONG TFEE :1; + ULONG CPDE :1; + }; + + ULONG Status; +} AHCI_INTERRUPT_ENABLE; + +typedef struct _AHCI_MEMORY_REGISTERS +{ + // 0x00 - 0x2B, Generic Host Control + ULONG CAP; // 0x00, Host capability + ULONG GHC; // 0x04, Global host control + ULONG IS; // 0x08, Interrupt status + ULONG PI; // 0x0C, Port implemented + ULONG VS; // 0x10, Version + ULONG CCC_CTL; // 0x14, Command completion coalescing control + ULONG CCC_PTS; // 0x18, Command completion coalescing ports + ULONG EM_LOC; // 0x1C, Enclosure management location + ULONG EM_CTL; // 0x20, Enclosure management control + ULONG CAP2; // 0x24, Host capabilities extended + ULONG BOHC; // 0x28, BIOS/OS handoff control and status + ULONG Reserved[0x1d]; // 0x2C - 0x9F, Reserved + ULONG VendorSpecific[0x18]; // 0xA0 - 0xFF, Vendor specific registers + AHCI_PORT PortList[MAXIMUM_AHCI_PORT_COUNT]; +} AHCI_MEMORY_REGISTERS, *PAHCI_MEMORY_REGISTERS; + +// Holds information for each attached attached port to a given adapter. +typedef struct _AHCI_PORT_EXTENSION +{ + ULONG PortNumber; + ULONG QueueSlots; // slots which we have already assigned task (Slot) + ULONG CommandIssuedSlots; // slots which has been programmed + ULONG MaxPortQueueDepth; + + struct + { + UCHAR RemovableDevice; + UCHAR Lba48BitMode; + UCHAR AccessType; + UCHAR DeviceType; + UCHAR IsActive; + LARGE_INTEGER MaxLba; + ULONG BytesPerLogicalSector; + ULONG BytesPerPhysicalSector; + UCHAR VendorId[41]; + UCHAR RevisionID[9]; + UCHAR SerialNumber[21]; + } DeviceParams; + + STOR_DPC CommandCompletion; + PAHCI_PORT Port; // AHCI Port Infomation + AHCI_QUEUE SrbQueue; // pending Srbs + AHCI_QUEUE CompletionQueue; + PSCSI_REQUEST_BLOCK Slot[MAXIMUM_AHCI_PORT_NCS]; // Srbs which has been alloted a port + PAHCI_RECEIVED_FIS ReceivedFIS; + PAHCI_COMMAND_HEADER CommandList; + STOR_DEVICE_POWER_STATE DevicePowerState; // Device Power State + PIDENTIFY_DEVICE_DATA IdentifyDeviceData; + STOR_PHYSICAL_ADDRESS IdentifyDeviceDataPhysicalAddress; + struct _AHCI_ADAPTER_EXTENSION* AdapterExtension; // Port's Adapter Information +} AHCI_PORT_EXTENSION, *PAHCI_PORT_EXTENSION; + +// Holds Adapter Information +typedef struct _AHCI_ADAPTER_EXTENSION +{ + ULONG SystemIoBusNumber; + ULONG SlotNumber; + ULONG AhciBaseAddress; + PULONG IS;// Interrupt Status, In case of MSIM == `1` + ULONG PortImplemented;// bit-mapping of ports which are implemented + ULONG PortCount; + + USHORT VendorID; + USHORT DeviceID; + USHORT RevisionID; + + ULONG Version; + ULONG CAP; + ULONG CAP2; + ULONG LastInterruptPort; + ULONG CurrentCommandSlot; + + PVOID NonCachedExtension; // holds virtual address to noncached buffer allocated for Port Extension + + struct + { + // Message per port or shared port? + ULONG MessagePerPort : 1; + ULONG Removed : 1; + ULONG Reserved : 30; // not in use -- maintain 4 byte alignment + } StateFlags; + + PAHCI_MEMORY_REGISTERS ABAR_Address; + AHCI_PORT_EXTENSION PortExtension[MAXIMUM_AHCI_PORT_COUNT]; +} AHCI_ADAPTER_EXTENSION, *PAHCI_ADAPTER_EXTENSION; + +typedef struct _LOCAL_SCATTER_GATHER_LIST +{ + ULONG NumberOfElements; + ULONG_PTR Reserved; + STOR_SCATTER_GATHER_ELEMENT List[MAXIMUM_AHCI_PRDT_ENTRIES]; +} LOCAL_SCATTER_GATHER_LIST, *PLOCAL_SCATTER_GATHER_LIST; + +typedef struct _AHCI_SRB_EXTENSION +{ + AHCI_COMMAND_TABLE CommandTable; + ULONG AtaFunction; + ULONG Flags; + + UCHAR CommandReg; + UCHAR FeaturesLow; + UCHAR LBA0; + UCHAR LBA1; + UCHAR LBA2; + UCHAR Device; + UCHAR LBA3; + UCHAR LBA4; + UCHAR LBA5; + UCHAR FeaturesHigh; + + UCHAR SectorCountLow; + UCHAR SectorCountHigh; + + ULONG SlotIndex; + LOCAL_SCATTER_GATHER_LIST Sgl; + PLOCAL_SCATTER_GATHER_LIST pSgl; + PAHCI_COMPLETION_ROUTINE CompletionRoutine; + + // for alignment purpose -- 128 byte alignment + // do not try to access (R/W) this field + UCHAR Reserved[128]; +} AHCI_SRB_EXTENSION, *PAHCI_SRB_EXTENSION; + +////////////////////////////////////////////////////////////// +// Declarations // +////////////////////////////////////////////////////////////// + +VOID +AhciProcessIO ( + __in PAHCI_ADAPTER_EXTENSION AdapterExtension, + __in UCHAR PathId, + __in PSCSI_REQUEST_BLOCK Srb + ); + +BOOLEAN +AhciAdapterReset ( + __in PAHCI_ADAPTER_EXTENSION AdapterExtension + ); + +__inline +VOID +AhciZeroMemory ( + __out PCHAR Buffer, + __in ULONG BufferSize + ); + +__inline +BOOLEAN +IsPortValid ( + __in PAHCI_ADAPTER_EXTENSION AdapterExtension, + __in ULONG pathId + ); + +UCHAR DeviceRequestSense ( + __in PAHCI_ADAPTER_EXTENSION AdapterExtension, + __in PSCSI_REQUEST_BLOCK Srb, + __in PCDB Cdb + ); + +UCHAR DeviceRequestReadWrite ( + __in PAHCI_ADAPTER_EXTENSION AdapterExtension, + __in PSCSI_REQUEST_BLOCK Srb, + __in PCDB Cdb + ); + +UCHAR DeviceRequestCapacity ( + __in PAHCI_ADAPTER_EXTENSION AdapterExtension, + __in PSCSI_REQUEST_BLOCK Srb, + __in PCDB Cdb + ); + +UCHAR +DeviceInquiryRequest ( + __in PAHCI_ADAPTER_EXTENSION AdapterExtension, + __in PSCSI_REQUEST_BLOCK Srb, + __in PCDB Cdb + ); + +UCHAR DeviceRequestComplete ( + __in PAHCI_ADAPTER_EXTENSION AdapterExtension, + __in PSCSI_REQUEST_BLOCK Srb, + __in PCDB Cdb + ); + +UCHAR DeviceReportLuns ( + __in PAHCI_ADAPTER_EXTENSION AdapterExtension, + __in PSCSI_REQUEST_BLOCK Srb, + __in PCDB Cdb + ); + +__inline +BOOLEAN +AddQueue ( + __inout PAHCI_QUEUE Queue, + __in PVOID Srb + ); + +__inline +PVOID +RemoveQueue ( + __inout PAHCI_QUEUE Queue + ); + +__inline +PAHCI_SRB_EXTENSION +GetSrbExtension( + __in PSCSI_REQUEST_BLOCK Srb + ); + +__inline +ULONG64 +AhciGetLba ( + __in PCDB Cdb, + __in ULONG CdbLength + ); + +////////////////////////////////////////////////////////////// +// Assertions // +////////////////////////////////////////////////////////////// + +// I assert every silly mistake I can do while coding +// because god never help me debugging the code +// but these asserts do :') + +C_ASSERT(FIELD_OFFSET(AHCI_MEMORY_REGISTERS, CAP) == 0x00); +C_ASSERT(FIELD_OFFSET(AHCI_MEMORY_REGISTERS, GHC) == 0x04); +C_ASSERT(FIELD_OFFSET(AHCI_MEMORY_REGISTERS, IS) == 0x08); +C_ASSERT(FIELD_OFFSET(AHCI_MEMORY_REGISTERS, PI) == 0x0C); +C_ASSERT(FIELD_OFFSET(AHCI_MEMORY_REGISTERS, VS) == 0x10); +C_ASSERT(FIELD_OFFSET(AHCI_MEMORY_REGISTERS, CCC_CTL) == 0x14); +C_ASSERT(FIELD_OFFSET(AHCI_MEMORY_REGISTERS, CCC_PTS) == 0x18); +C_ASSERT(FIELD_OFFSET(AHCI_MEMORY_REGISTERS, EM_LOC) == 0x1C); +C_ASSERT(FIELD_OFFSET(AHCI_MEMORY_REGISTERS, EM_CTL) == 0x20); +C_ASSERT(FIELD_OFFSET(AHCI_MEMORY_REGISTERS, CAP2) == 0x24); +C_ASSERT(FIELD_OFFSET(AHCI_MEMORY_REGISTERS, BOHC) == 0x28); +C_ASSERT(FIELD_OFFSET(AHCI_MEMORY_REGISTERS, Reserved) == 0x2C); +C_ASSERT(FIELD_OFFSET(AHCI_MEMORY_REGISTERS, VendorSpecific) == 0xA0); +C_ASSERT(FIELD_OFFSET(AHCI_MEMORY_REGISTERS, PortList) == 0x100); + +C_ASSERT(FIELD_OFFSET(AHCI_PORT, CLB) == 0x00); +C_ASSERT(FIELD_OFFSET(AHCI_PORT, CLBU) == 0x04); +C_ASSERT(FIELD_OFFSET(AHCI_PORT, FB) == 0x08); +C_ASSERT(FIELD_OFFSET(AHCI_PORT, FBU) == 0x0C); +C_ASSERT(FIELD_OFFSET(AHCI_PORT, IS) == 0x10); +C_ASSERT(FIELD_OFFSET(AHCI_PORT, IE) == 0x14); +C_ASSERT(FIELD_OFFSET(AHCI_PORT, CMD) == 0x18); +C_ASSERT(FIELD_OFFSET(AHCI_PORT, RSV0) == 0x1C); +C_ASSERT(FIELD_OFFSET(AHCI_PORT, TFD) == 0x20); +C_ASSERT(FIELD_OFFSET(AHCI_PORT, SIG) == 0x24); +C_ASSERT(FIELD_OFFSET(AHCI_PORT, SSTS) == 0x28); +C_ASSERT(FIELD_OFFSET(AHCI_PORT, SCTL) == 0x2C); +C_ASSERT(FIELD_OFFSET(AHCI_PORT, SERR) == 0x30); +C_ASSERT(FIELD_OFFSET(AHCI_PORT, SACT) == 0x34); +C_ASSERT(FIELD_OFFSET(AHCI_PORT, CI) == 0x38); +C_ASSERT(FIELD_OFFSET(AHCI_PORT, SNTF) == 0x3C); +C_ASSERT(FIELD_OFFSET(AHCI_PORT, FBS) == 0x40); +C_ASSERT(FIELD_OFFSET(AHCI_PORT, RSV1) == 0x44); +C_ASSERT(FIELD_OFFSET(AHCI_PORT, Vendor) == 0x70); + +C_ASSERT((sizeof(AHCI_COMMAND_TABLE) % 128) == 0); + +C_ASSERT(sizeof(AHCI_GHC) == sizeof(ULONG)); +C_ASSERT(sizeof(AHCI_PORT_CMD) == sizeof(ULONG)); +C_ASSERT(sizeof(AHCI_TASK_FILE_DATA) == sizeof(ULONG)); +C_ASSERT(sizeof(AHCI_INTERRUPT_ENABLE) == sizeof(ULONG)); +C_ASSERT(sizeof(AHCI_SERIAL_ATA_STATUS) == sizeof(ULONG)); +C_ASSERT(sizeof(AHCI_SERIAL_ATA_CONTROL) == sizeof(ULONG)); +C_ASSERT(sizeof(AHCI_COMMAND_HEADER_DESCRIPTION) == sizeof(ULONG)); + +C_ASSERT(FIELD_OFFSET(AHCI_COMMAND_TABLE, CFIS) == 0x00); +C_ASSERT(FIELD_OFFSET(AHCI_COMMAND_TABLE, ACMD) == 0x40); +C_ASSERT(FIELD_OFFSET(AHCI_COMMAND_TABLE, RSV0) == 0x50); +C_ASSERT(FIELD_OFFSET(AHCI_COMMAND_TABLE, PRDT) == 0x80); diff --git a/reactos/drivers/storage/storahci/storahci.inf b/reactos/drivers/storage/storahci/storahci.inf new file mode 100644 index 00000000000..f291c2b9462 --- /dev/null +++ b/reactos/drivers/storage/storahci/storahci.inf @@ -0,0 +1,77 @@ +; +; PROJECT: ROS Kernel +; LICENSE: GNU GPLv2 only as published by the Free Software Foundation +; PURPOSE: Storahci Driver INF +; PROGRAMMERS: Aman Priyadarshi (aman.eureka@gmail.com) +; + +[version] +signature="$Windows NT$" +Class=hdc +ClassGuid={4D36E96A-E325-11CE-BFC1-08002BE10318} +Provider=%ROS% + +[SourceDisksNames] +1 = %DeviceDesc%,,, + +[SourceDisksFiles] +storahci.sys = 1 + +[DestinationDirs] +DefaultDestDir = 12 ; DIRID_DRIVERS + +[Manufacturer] +%ROS%=STORAHCI,NTx86 + +[STORAHCI] + +[STORAHCI.NTx86] +%SATA_AHCI.DeviceDesc%=storahci_Inst, PCI\CC_010601; Standard SATA AHCI Controller + +[ControlFlags] +ExcludeFromSelect = * + +[storahci_Inst] +CopyFiles = storahci_CopyFiles + +[storahci_Inst.HW] +; Enables Storport IPM for this adapter +HKR, "StorPort", "EnableIdlePowerManagement", %REG_DWORD%, 0x01 + +[storahci_Inst.Services] +AddService = storahci, %SPSVCINST_ASSOCSERVICE%, storahci_Service_Inst, Miniport_EventLog_Inst + +[storahci_Service_Inst] +DisplayName = %DeviceDesc% +ServiceType = %SERVICE_KERNEL_DRIVER% +StartType = %SERVICE_BOOT_START% +ErrorControl = %SERVICE_ERROR_CRITICAL% +ServiceBinary = %12%\storahci.sys +LoadOrderGroup = SCSI Miniport +AddReg = ahci_addreg + +[storahci_CopyFiles] +storahci.sys,,,1 + +[ahci_addreg] +HKR, "Parameters\PnpInterface", "5", %REG_DWORD%, 0x00000001 +HKR, "Parameters", "BusType", %REG_DWORD%, 0x0000000B + +[Miniport_EventLog_Inst] +AddReg = Miniport_EventLog_AddReg + +[Miniport_EventLog_AddReg] +HKR,,EventMessageFile,%REG_EXPAND_SZ%,"%%SystemRoot%%\System32\IoLogMsg.dll" +HKR,,TypesSupported,%REG_DWORD%,7 + +[Strings] +ROS = "ReactOS" +DeviceDesc = "AHCI SATA Driver" +SATA_AHCI.DeviceDesc = "Standard SATA AHCI Controller" + +SPSVCINST_ASSOCSERVICE = 0x00000002 +SERVICE_KERNEL_DRIVER = 1 +SERVICE_BOOT_START = 0 +SERVICE_ERROR_CRITICAL = 3 +REG_EXPAND_SZ = 0x00020000 +REG_DWORD = 0x00010001 diff --git a/reactos/drivers/storage/storahci/storahci.rc b/reactos/drivers/storage/storahci/storahci.rc new file mode 100644 index 00000000000..5950ce2d1f1 --- /dev/null +++ b/reactos/drivers/storage/storahci/storahci.rc @@ -0,0 +1,22 @@ +// +// PROJECT: ReactOS Kernel +// LICENSE: GNU GPLv2 only as published by the Free Software Foundation +// PURPOSE: To Implement AHCI Miniport driver targeting storport NT 5.2 +// PROGRAMMERS: Aman Priyadarshi (aman.eureka@gmail.com) +// + +#define VERSION 1 +#define VERSION_STR "1.0" + +#define REACTOS_FILETYPE VFT_DRV +#define REACTOS_FILESUBTYPE VFT2_DRV_SYSTEM +#define REACTOS_FILEVERSION VERSION +#define REACTOS_PRODUCTVERSION VERSION +#define REACTOS_STR_COMPANY_NAME "ReactOS Development Team" +#define REACTOS_STR_FILE_DESCRIPTION "AHCI Storport Miniport Driver" +#define REACTOS_STR_FILE_VERSION VERSION_STR +#define REACTOS_STR_INTERNAL_NAME "storahci.sys" +#define REACTOS_STR_ORIGINAL_FILENAME "storahci.sys" +#define REACTOS_STR_LEGAL_COPYRIGHT "Copyright 2016 ReactOS Team" +#define REACTOS_STR_PRODUCT_NAME "AHCI Driver for ReactOS" +#define REACTOS_STR_PRODUCT_VERSION VERSION_STR