#include "storahci.h"
-BOOLEAN AhciAdapterReset(
- __in PAHCI_ADAPTER_EXTENSION adapterExtension
-);
-
-VOID AhciZeroMemory(
- __in PCHAR buffer,
- __in ULONG bufferSize
-);
-
/**
* @name AhciPortInitialize
* @implemented
*
* Initialize port by setting up PxCLB & PxFB Registers
*
- * @param portExtension
+ * @param PortExtension
*
* @return
* Return true if intialization was successful
*/
-BOOLEAN AhciPortInitialize(
- __in PAHCI_PORT_EXTENSION portExtension
-)
+BOOLEAN
+AhciPortInitialize (
+ __in PAHCI_PORT_EXTENSION PortExtension
+ )
{
- ULONG mappedLength;
+ AHCI_PORT_CMD cmd;
+ ULONG mappedLength, portNumber;
PAHCI_MEMORY_REGISTERS abar;
PAHCI_ADAPTER_EXTENSION adapterExtension;
STOR_PHYSICAL_ADDRESS commandListPhysical, receivedFISPhysical;
- adapterExtension = portExtension->AdapterExtension;
+ DebugPrint("AhciPortInitialize()\n");
+
+ adapterExtension = PortExtension->AdapterExtension;
abar = adapterExtension->ABAR_Address;
- portExtension->Port = &abar->PortList[portExtension->PortNumber];
+ portNumber = PortExtension->PortNumber;
+
+ NT_ASSERT(abar != NULL);
+ NT_ASSERT(portNumber < adapterExtension->PortCount);
- commandListPhysical = StorPortGetPhysicalAddress(adapterExtension, NULL, portExtension->CommandList, &mappedLength);
- if (mappedLength == 0 || (commandListPhysical.LowPart % 1024) != 0)
+ PortExtension->Port = &abar->PortList[portNumber];
+
+ commandListPhysical = StorPortGetPhysicalAddress(adapterExtension,
+ NULL,
+ PortExtension->CommandList,
+ &mappedLength);
+
+ if ((mappedLength == 0) || ((commandListPhysical.LowPart % 1024) != 0))
+ {
+ DebugPrint("\tcommandListPhysical mappedLength:%d\n", mappedLength);
return FALSE;
+ }
- receivedFISPhysical = StorPortGetPhysicalAddress(adapterExtension, NULL, portExtension->ReceivedFIS, &mappedLength);
- if (mappedLength == 0 || (commandListPhysical.LowPart % 256) != 0)
+ receivedFISPhysical = StorPortGetPhysicalAddress(adapterExtension,
+ NULL,
+ PortExtension->ReceivedFIS,
+ &mappedLength);
+
+ if ((mappedLength == 0) || ((receivedFISPhysical.LowPart % 256) != 0))
+ {
+ DebugPrint("\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))
+ {
+ DebugPrint("\tPort is not idle: %x\n", cmd);
+ }
// 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);
- StorPortWriteRegisterUlong(adapterExtension, &portExtension->Port->FB, receivedFISPhysical.LowPart);
+ // 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();
*
* Allocate memory from poll for required pointers
*
- * @param adapterExtension
+ * @param AdapterExtension
* @param ConfigInfo
*
* @return
* return TRUE if allocation was successful
*/
-BOOLEAN AhciAllocateResourceForAdapter(
- __in PAHCI_ADAPTER_EXTENSION adapterExtension,
- __in PPORT_CONFIGURATION_INFORMATION ConfigInfo
-)
+BOOLEAN
+AhciAllocateResourceForAdapter (
+ __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
+ __in PPORT_CONFIGURATION_INFORMATION ConfigInfo
+ )
{
- PVOID portsExtension = NULL;
- PCHAR nonCachedExtension;
- ULONG portCount, portImplemented, status, index, NCS, AlignedNCS, nonCachedExtensionSize, currentCount;
+ PCHAR nonCachedExtension, tmp;
+ ULONG index, NCS, AlignedNCS;
+ ULONG portCount, portImplemented, nonCachedExtensionSize;
+ PAHCI_PORT_EXTENSION PortExtension;
- // 3.1.1 NCS = CAP[12:08] -> Align
- NCS = (adapterExtension->CAP & 0xF00) >> 8;
- AlignedNCS = ((NCS/8) + 1) * 8;
+ DebugPrint("AhciAllocateResourceForAdapter()\n");
- // get port count -- Number of set bits in `adapterExtension->PortImplemented`
+ 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;
- while(portImplemented > 0)
- {
- portCount++;
- portImplemented &= (portImplemented-1);
- }
+ portImplemented = AdapterExtension->PortImplemented;
- nonCachedExtensionSize = sizeof(AHCI_COMMAND_HEADER) * AlignedNCS + //should be 1K aligned
- sizeof(AHCI_RECEIVED_FIS);
- //align nonCachedExtensionSize to 1K
- nonCachedExtensionSize = (((nonCachedExtensionSize - 1) / 0x400) + 1) * 0x400;
- nonCachedExtensionSize *= portCount;
+ NT_ASSERT(portImplemented != 0);
+ for (index = MAXIMUM_AHCI_PORT_COUNT - 1; index > 0; index--)
+ if ((portImplemented & (1 << index)) != 0)
+ break;
- adapterExtension->NonCachedExtension = StorPortGetUncachedExtension(adapterExtension, ConfigInfo, nonCachedExtensionSize);
- if (adapterExtension->NonCachedExtension == NULL)
- return FALSE;
+ portCount = index + 1;
+ DebugPrint("\tPort Count: %d\n", portCount);
- nonCachedExtension = (PCHAR)adapterExtension->NonCachedExtension;
+ AdapterExtension->PortCount = portCount;
+ nonCachedExtensionSize = sizeof(AHCI_COMMAND_HEADER) * AlignedNCS + //should be 1K aligned
+ sizeof(AHCI_RECEIVED_FIS) +
+ sizeof(IDENTIFY_DEVICE_DATA);
- AhciZeroMemory(nonCachedExtension, nonCachedExtensionSize);
-
+ // align nonCachedExtensionSize to 1024
+ nonCachedExtensionSize = ROUND_UP(nonCachedExtensionSize, 1024);
- // allocate memory for port extension
- status = StorPortAllocatePool(
- adapterExtension,
- portCount * sizeof(AHCI_PORT_EXTENSION),
- AHCI_POOL_TAG,
- (PVOID*)&portsExtension);
+ AdapterExtension->NonCachedExtension = StorPortGetUncachedExtension(AdapterExtension,
+ ConfigInfo,
+ nonCachedExtensionSize * portCount);
- if (status != STOR_STATUS_SUCCESS)
+ if (AdapterExtension->NonCachedExtension == NULL)
+ {
+ DebugPrint("\tadapterExtension->NonCachedExtension == NULL\n");
return FALSE;
+ }
- AhciZeroMemory((PCHAR)portsExtension, portCount * sizeof(AHCI_PORT_EXTENSION));
+ nonCachedExtension = AdapterExtension->NonCachedExtension;
+ AhciZeroMemory(nonCachedExtension, nonCachedExtensionSize * portCount);
- nonCachedExtensionSize /= portCount;
- currentCount = 0;
- for (index = 0; index < 32; index++)
+ for (index = 0; index < portCount; index++)
{
- if ((adapterExtension->PortImplemented & (1<<index)) != 0)
+ PortExtension = &AdapterExtension->PortExtension[index];
+
+ PortExtension->DeviceParams.IsActive = FALSE;
+ if ((AdapterExtension->PortImplemented & (1 << index)) != 0)
{
- adapterExtension->PortExtension[index] = (PAHCI_PORT_EXTENSION)((PCHAR)portsExtension + sizeof(AHCI_PORT_EXTENSION) * currentCount);
+ PortExtension->PortNumber = index;
+ PortExtension->DeviceParams.IsActive = TRUE;
+ PortExtension->AdapterExtension = AdapterExtension;
+ PortExtension->CommandList = (PAHCI_COMMAND_HEADER)nonCachedExtension;
+
+ tmp = (PCHAR)(nonCachedExtension + sizeof(AHCI_COMMAND_HEADER) * AlignedNCS);
- adapterExtension->PortExtension[index]->PortNumber = index;
- adapterExtension->PortExtension[index]->AdapterExtension = adapterExtension;
- adapterExtension->PortExtension[index]->CommandList = (PAHCI_COMMAND_HEADER)(nonCachedExtension + (currentCount*nonCachedExtensionSize));
- adapterExtension->PortExtension[index]->ReceivedFIS = (PAHCI_RECEIVED_FIS)((PCHAR)adapterExtension->PortExtension[index]->CommandList + sizeof(AHCI_COMMAND_HEADER) * AlignedNCS);
- currentCount++;
+ PortExtension->ReceivedFIS = (PAHCI_RECEIVED_FIS)tmp;
+ PortExtension->IdentifyDeviceData = (PIDENTIFY_DEVICE_DATA)(tmp + sizeof(AHCI_RECEIVED_FIS));
+ PortExtension->MaxPortQueueDepth = NCS;
+ nonCachedExtension += nonCachedExtensionSize;
}
}
}// -- AhciAllocateResourceForAdapter();
/**
- * @name AhciFindAdapter
+ * @name AhciStartPort
* @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.
+ * Try to start the port device
*
- * SP_RETURN_NOT_FOUND
- * Indicates that no supported HBA was found for the supplied configuration information.
+ * @param AdapterExtension
+ * @param PortExtension
*
- * @remarks Called by Storport.
*/
-ULONG AhciFindAdapter(
- IN PVOID DeviceExtension,
- __in PVOID HwContext,
- __in PVOID BusInformation,
- __in IN PVOID ArgumentString,
- __inout PPORT_CONFIGURATION_INFORMATION ConfigInfo,
- __in PBOOLEAN Reserved3
-)
+BOOLEAN
+AhciStartPort (
+ __in PAHCI_PORT_EXTENSION PortExtension
+ )
{
- ULONG ghc;
ULONG index;
- ULONG portCount, portImplemented;
- ULONG pci_cfg_len;
- UCHAR pci_cfg_buf[0x30];
-
- PAHCI_MEMORY_REGISTERS abar;
- PPCI_COMMON_CONFIG pciConfigData;
- PAHCI_ADAPTER_EXTENSION adapterExtension;
+ 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;
- adapterExtension = (PAHCI_ADAPTER_EXTENSION)DeviceExtension;
- adapterExtension->SlotNumber = ConfigInfo->SlotNumber;
- adapterExtension->SystemIoBusNumber = ConfigInfo->SystemIoBusNumber;
-
- // get PCI configuration header
- pci_cfg_len = StorPortGetBusData(
- adapterExtension,
- PCIConfiguration,
- adapterExtension->SystemIoBusNumber,
- adapterExtension->SlotNumber,
- (PVOID)pci_cfg_buf,
- (ULONG)0x30);
+ DebugPrint("AhciStartPort()\n");
- if (pci_cfg_len != 0x30)
- return SP_RETURN_ERROR;//Not a valid device at the given bus number
+ AdapterExtension = PortExtension->AdapterExtension;
+ cmd.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->CMD);
- 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);
+ if ((cmd.FR == 1) && (cmd.CR == 1) && (cmd.FRE == 1) && (cmd.ST == 1))
+ {
+ // Already Running
+ return TRUE;
+ }
- // 2.1.11
- abar = NULL;
- if (ConfigInfo->NumberOfAccessRanges > 0)
+ cmd.SUD = 1;
+ StorPortWriteRegisterUlong(AdapterExtension, &PortExtension->Port->CMD, cmd.Status);
+
+ if (((cmd.FR == 1) && (cmd.FRE == 0)) ||
+ ((cmd.CR == 1) && (cmd.ST == 0)))
{
- for (index = 0; index < ConfigInfo->NumberOfAccessRanges; index++)
+ DebugPrint("\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
{
- if ((*(ConfigInfo->AccessRanges))[index].RangeStart.QuadPart == adapterExtension->AhciBaseAddress)
+ StorPortStallExecution(1000);
+ ssts.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->SSTS);
+
+ index++;
+ if (ssts.DET != 0)
{
- abar = (PAHCI_MEMORY_REGISTERS)StorPortGetDeviceBase(
- adapterExtension,
- ConfigInfo->AdapterInterfaceType,
- ConfigInfo->SystemIoBusNumber,
- (*(ConfigInfo->AccessRanges))[index].RangeStart,
- (*(ConfigInfo->AccessRanges))[index].RangeLength,
- (BOOLEAN)!(*(ConfigInfo->AccessRanges))[index].RangeInMemory);
break;
}
}
+ while(index < 30);
}
- if (abar == NULL)
- return SP_RETURN_ERROR; // corrupted information supplied
+ ssts.Status = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->SSTS);
+ switch (ssts.DET)
+ {
+ case 0x3:
+ {
+ NT_ASSERT(cmd.ST == 0);
- adapterExtension->ABAR_Address = abar;
- adapterExtension->CAP = StorPortReadRegisterUlong(adapterExtension, &abar->CAP);
- adapterExtension->CAP2 = StorPortReadRegisterUlong(adapterExtension, &abar->CAP2);
- adapterExtension->Version = StorPortReadRegisterUlong(adapterExtension, &abar->VS);
+ // 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));
- // 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 = StorPortReadRegisterUlong(adapterExtension, &abar->GHC);
- // AE := Highest Significant bit of GHC
- if ((ghc & (0x1<<31)) == 1)//Hmm, controller was already in power state
- {
- // reset controller to have it in know state
- DebugPrint("AhciFindAdapter -> AE Already set, Reset()\n");
- if (!AhciAdapterReset(adapterExtension))
- return SP_RETURN_ERROR;// reset failed
+ 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))
+ {
+ DebugPrint("\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)
+ {
+ DebugPrint("\tFailed to start Port\n");
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+ default:
+ // unhandled case
+ DebugPrint("\tDET == %x Unsupported\n", ssts.DET);
+ return FALSE;
}
- ghc = 0x1<<31;// only AE=1
- StorPortWriteRegisterUlong(adapterExtension, &abar->GHC, ghc);
+}// -- AhciStartPort();
- adapterExtension->IS = abar->IS;
- adapterExtension->PortImplemented = StorPortReadRegisterUlong(adapterExtension, &abar->PI);
+/**
+ * @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;
+ STOR_LOCK_HANDLE lockhandle;
+ PAHCI_SRB_EXTENSION SrbExtension;
+ PAHCI_COMPLETION_ROUTINE CompletionRoutine;
- if (adapterExtension->PortImplemented == 0)
- return SP_RETURN_ERROR;
+ UNREFERENCED_PARAMETER(Dpc);
+ UNREFERENCED_PARAMETER(SystemArgument2);
- ConfigInfo->MaximumTransferLength = 128 * 1024;//128 KB
- ConfigInfo->NumberOfPhysicalBreaks = 0x21;
- ConfigInfo->MaximumNumberOfTargets = 1;
- ConfigInfo->MaximumNumberOfLogicalUnits = 1;
- ConfigInfo->ResetTargetSupported = TRUE;
- ConfigInfo->NumberOfBuses = 32;
- ConfigInfo->SynchronizationModel = StorSynchronizeFullDuplex;
- ConfigInfo->ScatterGather = TRUE;
+ DebugPrint("AhciCommandCompletionDpcRoutine()\n");
- // allocate necessary resource for each port
- if (!AhciAllocateResourceForAdapter(adapterExtension, ConfigInfo))
- return SP_RETURN_ERROR;
+ AhciZeroMemory(&lockhandle, sizeof(lockhandle));
+
+ StorPortAcquireSpinLock(AdapterExtension, InterruptLock, NULL, &lockhandle);
+ Srb = RemoveQueue(&PortExtension->CompletionQueue);
- for (index = 0; index < 32; index++)
+ NT_ASSERT(Srb != NULL);
+
+ if (Srb->SrbStatus == SRB_STATUS_PENDING)
{
- if ((adapterExtension->PortImplemented & (1<<index)) != 0)
- AhciPortInitialize(adapterExtension->PortExtension[index]);
+ Srb->SrbStatus = SRB_STATUS_SUCCESS;
}
- // Turn IE -- Interrupt Enabled
- ghc |= 0x2;
- StorPortWriteRegisterUlong(adapterExtension, &abar->GHC, ghc);
+ SrbExtension = GetSrbExtension(Srb);
+ CompletionRoutine = SrbExtension->CompletionRoutine;
- return SP_RETURN_FOUND;
-}// -- AhciFindAdapter();
+ if (CompletionRoutine != NULL)
+ {
+ // now it's completion routine responsibility to set SrbStatus
+ CompletionRoutine(PortExtension, Srb);
+ }
+ else
+ {
+ Srb->SrbStatus = SRB_STATUS_SUCCESS;
+ StorPortNotification(RequestComplete, AdapterExtension, Srb);
+ }
+
+ StorPortReleaseSpinLock(AdapterExtension, &lockhandle);
+ return;
+}// -- AhciCommandCompletionDpcRoutine();
/**
- * @name DriverEntry
+ * @name AhciHwPassiveInitialize
* @implemented
*
- * Initial Entrypoint for storahci miniport driver
+ * initializes the HBA and finds all devices that are of interest to the miniport driver. (at PASSIVE LEVEL)
*
- * @param DriverObject
- * @param RegistryPath
+ * @param adapterExtension
*
* @return
- * NT_STATUS in case of driver loaded successfully.
+ * return TRUE if intialization was successful
*/
-ULONG DriverEntry(
- IN PVOID DriverObject,
- IN PVOID RegistryPath
-)
+BOOLEAN
+AhciHwPassiveInitialize (
+ __in PAHCI_ADAPTER_EXTENSION AdapterExtension
+ )
{
- HW_INITIALIZATION_DATA hwInitializationData;
- ULONG i, status;
+ ULONG index;
+ PAHCI_PORT_EXTENSION PortExtension;
- DebugPrint("Storahci -> DriverEntry()\n");
+ DebugPrint("AhciHwPassiveInitialize()\n");
- // initialize the hardware data structure
- AhciZeroMemory((PCHAR)&hwInitializationData, sizeof(HW_INITIALIZATION_DATA));
+ 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);
+ }
+ }
- // set size of hardware initialization structure
- hwInitializationData.HwInitializationDataSize = sizeof(HW_INITIALIZATION_DATA);
+ return TRUE;
+}// -- AhciHwPassiveInitialize();
- // identity required miniport entry point routines
- hwInitializationData.HwFindAdapter = AhciFindAdapter;
+/**
+ * @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;
+ MESSAGE_INTERRUPT_INFORMATION messageInfo;
- // adapter specific information
- hwInitializationData.NeedPhysicalAddresses = TRUE;
- hwInitializationData.TaggedQueuing = TRUE;
- hwInitializationData.AutoRequestSense = TRUE;
- hwInitializationData.MultipleRequestPerLu = TRUE;
+ DebugPrint("AhciHwInitialize()\n");
- hwInitializationData.NumberOfAccessRanges = 6;
- hwInitializationData.AdapterInterfaceType = PCIBus;
- hwInitializationData.MapBuffers = STOR_MAP_NON_READ_WRITE_BUFFERS;
+ AdapterExtension->StateFlags.MessagePerPort = FALSE;
- // 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);
+ // First check what type of interrupt/synchronization device is using
+ ghc.Status = StorPortReadRegisterUlong(AdapterExtension, &AdapterExtension->ABAR_Address->GHC);
- return status;
-}// -- DriverEntry();
+ // 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;
+ DebugPrint("\tMultiple MSI based message not supported\n");
+ }
+
+ StorPortEnablePassiveInitialization(AdapterExtension, AhciHwPassiveInitialize);
+
+ return TRUE;
+}// -- AhciHwInitialize();
/**
- * @name AhciAdapterReset
+ * @name AhciCompleteIssuedSrb
* @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.
+ * Complete issued Srbs
*
- * @param adapterExtension
+ * @param PortExtension
*
- * @return
- * TRUE in case AHCI Controller RESTARTED successfully. i.e GHC.HR == 0
*/
-BOOLEAN AhciAdapterReset(
- PAHCI_ADAPTER_EXTENSION adapterExtension
-)
+VOID
+AhciCompleteIssuedSrb (
+ __in PAHCI_PORT_EXTENSION PortExtension,
+ __in ULONG CommandsToComplete
+ )
{
- ULONG ghc, ticks;
- PAHCI_MEMORY_REGISTERS abar = NULL;
+ ULONG NCS, i;
+ PSCSI_REQUEST_BLOCK Srb;
+ PAHCI_ADAPTER_EXTENSION AdapterExtension;
- abar = adapterExtension->ABAR_Address;
+ DebugPrint("AhciCompleteIssuedSrb()\n");
- if (abar == NULL) // basic sanity
- return FALSE;
+ NT_ASSERT(CommandsToComplete != 0);
- // HR -- Very first bit (lowest significant)
- ghc = 1;
- StorPortWriteRegisterUlong(adapterExtension, &abar->GHC, ghc);
+ DebugPrint("\tCompleted Commands: %d\n", CommandsToComplete);
- for (ticks = 0; (ticks < 50) &&
- (StorPortReadRegisterUlong(adapterExtension, &abar->GHC) == 1);
- StorPortStallExecution(20000), ticks++);
+ AdapterExtension = PortExtension->AdapterExtension;
+ NCS = AHCI_Global_Port_CAP_NCS(AdapterExtension->CAP);
- if (ticks == 50)//1 second
- return FALSE;
+ for (i = 0; i < NCS; i++)
+ {
+ if (((1 << i) & CommandsToComplete) != 0)
+ {
+ Srb = PortExtension->Slot[i];
+ NT_ASSERT(Srb != NULL);
- return TRUE;
-}// -- AhciAdapterReset();
+ AddQueue(&PortExtension->CompletionQueue, Srb);
+ StorPortIssueDpc(AdapterExtension, &PortExtension->CommandCompletion, PortExtension, Srb);
+ }
+ }
+
+ return;
+}// -- AhciCompleteIssuedSrb();
/**
- * @name AhciZeroMemory
+ * @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;
+
+ DebugPrint("AhciInterruptHandler()\n");
+ DebugPrint("\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.
+
+ DebugPrint("\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
*
- * Clear buffer by filling zeros
+ * The Storport driver calls the HwStorInterrupt routine after the HBA generates an interrupt request.
+ *
+ * @param AdapterExtension
*
- * @param buffer
+ * @return
+ * return TRUE Indicates that an interrupt was pending on adapter.
+ * return FALSE Indicates the interrupt was not ours.
*/
-VOID AhciZeroMemory(
- __in PCHAR buffer,
- __in ULONG bufferSize
-)
+BOOLEAN
+AhciHwInterrupt (
+ __in PAHCI_ADAPTER_EXTENSION AdapterExtension
+ )
{
- ULONG i;
- for (i = 0; i < bufferSize; i++)
- buffer[i] = 0;
-}// -- AhciZeroMemory();
+ 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;
+ }
+
+ DebugPrint("\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 PVOID AdapterExtension,
+ __in PSCSI_REQUEST_BLOCK Srb
+ )
+{
+ UCHAR function, pathId;
+ PAHCI_ADAPTER_EXTENSION adapterExtension;
+
+ DebugPrint("AhciHwStartIo()\n");
+
+ pathId = Srb->PathId;
+ function = Srb->Function;
+ adapterExtension = AdapterExtension;
+
+ DebugPrint("\tFunction: %x\n", function);
+
+ if (!IsPortValid(adapterExtension, pathId))
+ {
+ Srb->SrbStatus = SRB_STATUS_NO_DEVICE;
+ StorPortNotification(RequestComplete, adapterExtension, Srb);
+ return TRUE;
+ }
+
+ // 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.
+ if (function == SRB_FUNCTION_PNP)
+ {
+ PSCSI_PNP_REQUEST_BLOCK pnpRequest;
+
+ pnpRequest = (PSCSI_PNP_REQUEST_BLOCK)Srb;
+ if ((pnpRequest->SrbPnPFlags & SRB_PNP_FLAGS_ADAPTER_REQUEST) != 0)
+ {
+ if ((pnpRequest->PnPAction == StorRemoveDevice) ||
+ (pnpRequest->PnPAction == StorSurpriseRemoval))
+ {
+ Srb->SrbStatus = SRB_STATUS_SUCCESS;
+ adapterExtension->StateFlags.Removed = 1;
+ DebugPrint("\tAdapter removed\n");
+ }
+ else if (pnpRequest->PnPAction == StorStopDevice)
+ {
+ Srb->SrbStatus = SRB_STATUS_SUCCESS;
+ DebugPrint("\tRequested to Stop the adapter\n");
+ }
+ else
+ {
+ Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
+ }
+
+ StorPortNotification(RequestComplete, adapterExtension, Srb);
+ return TRUE;
+ }
+ }
+
+ if (function == SRB_FUNCTION_EXECUTE_SCSI)
+ {
+ DebugPrint("\tSRB_FUNCTION_EXECUTE_SCSI\n");
+ // 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.
+ if (Srb->CdbLength > 0)
+ {
+ PCDB cdb = (PCDB)&Srb->Cdb;
+ NT_ASSERT(cdb != NULL);
+
+ switch(cdb->CDB10.OperationCode)
+ {
+ case SCSIOP_INQUIRY:
+ Srb->SrbStatus = DeviceInquiryRequest(adapterExtension, Srb, cdb, TRUE);
+ break;
+ case SCSIOP_REPORT_LUNS:
+ Srb->SrbStatus = DeviceInquiryRequest(adapterExtension, Srb, cdb, FALSE);
+ break;
+ default:
+ {
+ DebugPrint("\tOperationCode: %d\n", cdb->CDB10.OperationCode);
+ Srb->SrbStatus = SRB_STATUS_NO_DEVICE;
+ }
+ break;
+ }
+ }
+ else
+ {
+ Srb->SrbStatus = SRB_STATUS_BAD_FUNCTION;
+ }
+
+ StorPortNotification(RequestComplete, adapterExtension, Srb);
+ return TRUE;
+ }
+
+ DebugPrint("\tUnknown function code recieved: %x\n", function);
+ Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
+ StorPortNotification(RequestComplete, adapterExtension, 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;
+ PAHCI_ADAPTER_EXTENSION adapterExtension;
+
+ DebugPrint("AhciHwResetBus()\n");
+
+ adapterExtension = AdapterExtension;
+
+ if (IsPortValid(AdapterExtension, PathId))
+ {
+ AhciZeroMemory(&lockhandle, sizeof(lockhandle));
+
+ // 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;
+
+ DebugPrint("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))
+ {
+ DebugPrint("\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 = 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);
+
+ DebugPrint("\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)
+ {
+ DebugPrint("\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 = -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
+ DebugPrint("\tAE Already set, Reset()\n");
+ if (!AhciAdapterReset(adapterExtension))
+ {
+ DebugPrint("\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)
+ {
+ DebugPrint("\tadapterExtension->PortImplemented == 0\n");
+ return SP_RETURN_ERROR;
+ }
+
+ ConfigInfo->MaximumTransferLength = MAXIMUM_TRANSFER_LENGTH;//128 KB
+ ConfigInfo->NumberOfPhysicalBreaks = 0x21;
+ ConfigInfo->MaximumNumberOfTargets = 1;
+ ConfigInfo->MaximumNumberOfLogicalUnits = 1;
+ ConfigInfo->ResetTargetSupported = TRUE;
+ ConfigInfo->NumberOfBuses = MAXIMUM_AHCI_PORT_COUNT;
+ ConfigInfo->SynchronizationModel = StorSynchronizeFullDuplex;
+ ConfigInfo->ScatterGather = TRUE;
+
+ // 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))
+ {
+ DebugPrint("\tAhciAllocateResourceForAdapter() == FALSE\n");
+ 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;
+ HW_INITIALIZATION_DATA hwInitializationData;
+
+ DebugPrint("Storahci Loaded\n");
+
+ // initialize the hardware data structure
+ AhciZeroMemory(&hwInitializationData, sizeof(HW_INITIALIZATION_DATA));
+
+ // 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.NeedPhysicalAddresses = TRUE;
+ hwInitializationData.TaggedQueuing = TRUE;
+ hwInitializationData.AutoRequestSense = TRUE;
+ hwInitializationData.MultipleRequestPerLu = 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);
+
+ DebugPrint("\tstatus: %x\n", status);
+ 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);
+
+ DebugPrint("AhciATA_CFIS()\n");
+
+ cmdTable = (PAHCI_COMMAND_TABLE)SrbExtension;
+
+ AhciZeroMemory(&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
+ )
+{
+ UNREFERENCED_PARAMETER(PortExtension);
+ UNREFERENCED_PARAMETER(SrbExtension);
+
+ DebugPrint("AhciATAPI_CFIS()\n");
+
+ return 2;
+}// -- 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;
+
+ DebugPrint("AhciBuild_PRDT()\n");
+
+ sgl = &SrbExtension->Sgl;
+ 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;
+
+ DebugPrint("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)
+ {
+ DebugPrint("\tATA Device Found!\n");
+ SrbExtension->CommandReg = IDE_COMMAND_IDENTIFY;
+ }
+ else
+ {
+ DebugPrint("\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 (IsAtaCommand(SrbExtension->AtaFunction))
+ {
+ cfl = AhciATA_CFIS(PortExtension, SrbExtension);
+ }
+ else if (IsAtapiCommand(SrbExtension->AtaFunction))
+ {
+ cfl = AhciATAPI_CFIS(PortExtension, SrbExtension);
+ }
+
+ 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 sact, ci;
+ ULONG QueueSlots, slotToActivate, tmp;
+ PAHCI_ADAPTER_EXTENSION AdapterExtension;
+
+ DebugPrint("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;
+ }
+
+ sact = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->SACT);
+ ci = StorPortReadRegisterUlong(AdapterExtension, &PortExtension->Port->CI);
+
+ // 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
+ )
+{
+ STOR_LOCK_HANDLE lockhandle;
+ PSCSI_REQUEST_BLOCK tmpSrb;
+ PAHCI_PORT_EXTENSION PortExtension;
+ ULONG commandSlotMask, occupiedSlots, slotIndex, NCS;
+
+ DebugPrint("AhciProcessIO()\n");
+ DebugPrint("\tPathId: %d\n", PathId);
+
+ PortExtension = &AdapterExtension->PortExtension[PathId];
+
+ NT_ASSERT(PathId < AdapterExtension->PortCount);
+
+ AhciZeroMemory(&lockhandle, sizeof(lockhandle));
+
+ // 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 InquiryCompletion
+ * @not_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;
+ PLUN_LIST LunList;
+ PAHCI_SRB_EXTENSION SrbExtension;
+ PIDENTIFY_DEVICE_DATA IdentifyDeviceData;
+ ULONG SrbStatus, LunCount, DataBufferLength;
+
+ DebugPrint("InquiryCompletion()\n");
+
+ NT_ASSERT(PortExtension != NULL);
+ NT_ASSERT(Srb != NULL);
+
+ cdb = (PCDB)&Srb->Cdb;
+ SrbStatus = Srb->SrbStatus;
+ SrbExtension = GetSrbExtension(Srb);
+
+ if (SrbStatus == SRB_STATUS_SUCCESS)
+ {
+ IdentifyDeviceData = PortExtension->IdentifyDeviceData;
+
+ if (SrbExtension->CommandReg == IDE_COMMAND_IDENTIFY)
+ {
+ DebugPrint("Device: ATA\n");
+ 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;
+
+ // TODO: Add other device params
+ }
+ else
+ {
+ DebugPrint("Device: ATAPI\n");
+ PortExtension->DeviceParams.DeviceType = AHCI_DEVICE_TYPE_ATAPI;
+
+ PortExtension->DeviceParams.AccessType = READ_ONLY_DIRECT_ACCESS_DEVICE;
+ }
+ }
+ else if (SrbStatus == SRB_STATUS_NO_DEVICE)
+ {
+ DebugPrint("Device: No Device\n");
+ PortExtension->DeviceParams.DeviceType = AHCI_DEVICE_TYPE_NODEVICE;
+ }
+ else
+ {
+ return;
+ }
+
+ if ((cdb != NULL) && (cdb->CDB10.OperationCode == SCSIOP_REPORT_LUNS))
+ {
+ Srb->SrbStatus = SRB_STATUS_SUCCESS;
+ Srb->ScsiStatus = SCSISTAT_GOOD;
+
+ SrbExtension->AtaFunction = 0;
+ DataBufferLength = Srb->DataTransferLength;
+
+ LunList = (PLUN_LIST)Srb->DataBuffer;
+ if (PortExtension->DeviceParams.DeviceType == AHCI_DEVICE_TYPE_NODEVICE)
+ {
+ LunCount = 0;
+ }
+ else
+ {
+ LunCount = 1;
+ }
+
+ if (DataBufferLength < sizeof(LUN_LIST))
+ {
+ DebugPrint("\tSRB_STATUS_DATA_OVERRUN\n");
+ Srb->SrbStatus = SRB_STATUS_DATA_OVERRUN;
+ }
+ else
+ {
+ LunList->LunListLength[0] = 0;
+ LunList->LunListLength[1] = 0;
+ LunList->LunListLength[2] = 0;
+ LunList->LunListLength[3] = 8;
+
+ // followed by 8 entries
+ LunList->Lun[0][0] = 0;
+ LunList->Lun[0][1] = 0;
+ LunList->Lun[0][2] = 0;
+ LunList->Lun[0][3] = 0;
+ LunList->Lun[0][4] = 0;
+ LunList->Lun[0][5] = 0;
+ LunList->Lun[0][6] = 0;
+ LunList->Lun[0][7] = 0;
+ }
+ }
+
+ return;
+}// -- InquiryCompletion();
+
+/**
+ * @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,
+ __in BOOLEAN HasProductDataRequest
+ )
+{
+ PVOID DataBuffer;
+ PAHCI_PORT_EXTENSION PortExtension;
+ PAHCI_SRB_EXTENSION SrbExtension;
+ PVPD_SUPPORTED_PAGES_PAGE VpdOutputBuffer;
+ ULONG DataBufferLength, RequiredDataBufferLength;
+
+ DebugPrint("DeviceInquiryRequest()\n");
+
+ NT_ASSERT(IsPortValid(AdapterExtension, Srb->PathId));
+
+ SrbExtension = GetSrbExtension(Srb);
+ PortExtension = &AdapterExtension->PortExtension[Srb->PathId];
+
+ if (Srb->Lun != 0)
+ {
+ return SRB_STATUS_SELECTION_TIMEOUT;
+ }
+ else if ((HasProductDataRequest == FALSE) || (Cdb->CDB6INQUIRY3.EnableVitalProductData == 0))
+ {
+ // 3.6.1
+ // If the EVPD bit is set to zero, the device server shall return the standard INQUIRY data
+ DebugPrint("\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);
+
+ AhciProcessIO(AdapterExtension, Srb->PathId, Srb);
+ return SRB_STATUS_PENDING;
+ }
+ else if (HasProductDataRequest == TRUE)
+ {
+ DebugPrint("\tVPD Inquired\n");
+
+ DataBuffer = Srb->DataBuffer;
+ DataBufferLength = Srb->DataTransferLength;
+
+ if (DataBuffer == NULL)
+ {
+ return SRB_STATUS_INVALID_REQUEST;
+ }
+
+ AhciZeroMemory(DataBuffer, DataBufferLength);
+
+ switch(Cdb->CDB6INQUIRY3.PageCode)
+ {
+ case VPD_SUPPORTED_PAGES:
+ {
+ DebugPrint("\tVPD_SUPPORTED_PAGES\n");
+ RequiredDataBufferLength = sizeof(VPD_SUPPORTED_PAGES_PAGE);
+ if (DataBufferLength < RequiredDataBufferLength)
+ {
+ DebugPrint("\tDataBufferLength: %d Required: %d\n", DataBufferLength, RequiredDataBufferLength);
+ return SRB_STATUS_INVALID_REQUEST;
+ }
+ else
+ {
+ VpdOutputBuffer = (PVPD_SUPPORTED_PAGES_PAGE)DataBuffer;
+ VpdOutputBuffer->DeviceType = PortExtension->DeviceParams.DeviceType;
+ VpdOutputBuffer->DeviceTypeQualifier = 0;
+ VpdOutputBuffer->PageCode = VPD_SUPPORTED_PAGES;
+ VpdOutputBuffer->PageLength = 1;
+ VpdOutputBuffer->SupportedPageList[0] = VPD_SUPPORTED_PAGES;
+
+ return SRB_STATUS_SUCCESS;
+ }
+ }
+ break;
+ default:
+ DebugPrint("\tPageCode: %x\n", Cdb->CDB6INQUIRY3.PageCode);
+ break;
+ }
+ }
+
+ return SRB_STATUS_INVALID_REQUEST;
+}// -- 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;
+
+ DebugPrint("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
+ {
+ DebugPrint("\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();