Sample Code
Windows Driver Samples/ StorAhci StorPort Miniport Driver/ C++/ src/ pnppower.c/
/*++ Copyright (C) Microsoft Corporation, 2009 Module Name: pnppower.c Abstract: This file contains function of pnp and power process of the AHCI miniport. Notes: Revision History: --*/ #pragma warning(push) #pragma warning(disable:26015) //26015: "Potential overflow using expression 'outParams->DriverStatus.bDriverError'. Buffer access is apparently unbounded by the buffer size. //Output buffer cannot be checked for size. ATAport provides this validation check as the input buffer size and output buffer size are 2 of the 4 parameters passed in on the SMART IRP. Storport doesn�t do this and the miniport doesn�t get the IRP so it cannot do this for itself. This is just the condition of a legacy IOCTL. //26015: "Potential overflow using expression 'nRB->NRBStatus' Buffer access is apparently unbounded by the buffer size. //The same is true for the NVCache IOCTL. Instead of the output buffer, this time it is the NVCache_Request_Block. #pragma warning(disable:4214) // bit field types other than int #pragma warning(disable:4201) // nameless struct/union #include "generic.h" #include "acpiioct.h" //_DSM for Link Power is uniquely identified by the following UUID: // E4DB149B-FCFE-425B-A6D8-92357D78FC7F static const GUID LINK_POWER_ACPI_DSM_GUID = { 0xE4DB149B, 0xFCFE, 0x425B, { 0xA6,0xD8,0x92,0x35,0x7D,0x78,0xFC,0x7F } }; VOID LogPageDiscoveryCompletion ( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb ); BOOLEAN AhciPortInitialize( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension ) { /*++ This function is used to start an AHCI port It assumes: Called By: HwFindAdapter Affected Variables/Registers: ChannelExtension->StateFlags ChannelExtension->CommandList ChannelExtension->ReceivedFIS PX.CLB,PX.CLBU PX.FB,PX.FBU CMD.ST It performs: (overview) 1. Start with some defensive structure checking and variable initialization 2. Initialize the ChannelConfiguration structure (final steps) 3. Enable the AHCI interface as per AHCI 1.1 section 10.1.2 (final steps) 4. Allocate memory for the CommandList, the Receive FIS buffer, and SRB Extension. 5. Start the channel processing commands. (details) 1.1 Initialize variables 1.2 Verify the Channel Configuration 2.1 Initialize the ChannelConfiguration structure 2.2 Initialize the Channel's base address and the controller's Interrupt Status register address 3.1 Enable the AHCI interface AHCI 1.1 Section 10.1.2 - 5. "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�) It is good practice for system software to �zero-out� the memory allocated and referenced by PxCLB and PxFB. After setting PxFB and PxFBU to the physical address of the FIS receive area, system software shall set PxCMD.FRE to �1�." 3.2 Enable Interrupts on the Channel AHCI 1.1 Section 10.1.2 - 7. "Determine which events should cause an interrupt, and set each implemented port�s PxIE register with the appropriate enables." Note: Due to the multi-tiered nature of the AHCI HBA�s interrupt architecture, system software must always ensure that the PxIS (clear this first) and IS.IPS (clear this second) registers are cleared to �0� before programming the PxIE and GHC.IE registers. This will prevent any residual bits set in these registers from causing an interrupt to be asserted. 4.1 Allocate memory for the CommandList, the Receive FIS buffer and SRB Extension Now is the time to allocate memory that will be used for controller and per request structures. In AHCI, the controller structures are both the command header list and the received FIS buffer. The per request structure will be recieved through the SRB and will be used to make a Command Table The mechanism for requesting all of this memory is AtaPortGetUnCachedExtension. NOTE! AtaPortGetUnCachedExtension can only be called while processing a HwControl IdeStart. Also NOTE! In order to perform crashdump/hibernate the UncachedExtensionSize cannot be larger than 30K. The call to AtaPortGetUnCachedExtension is complicated by alignment restrictions that an AHCI controller has so here are the rules: - Command List Base Addresses must be 1K aligned, and the Command list is (sizeof (AHCI_COMMAND_HEADER) * cap.NCS), which is some multiple of 32 bytes in length. - The FIS Base Address must be 256 aligned, and the FIS Receive buffer is sizeof (AHCI_RECEIVED_FIS), 256 bytes in length. - The Command Table must be 128 aligned, and is sizeof(AHCI_COMMAND_TABLE), 1280 bytes in length thanks to some padding in the AHCI_COMMAND_TABLE structure. The alignment of the addresses (virtual and physical) returned by the function follow these rules - the address returned by AtaPortGetUnCachedExtension will have both its virtual and physical addresses page aligned - the memory received through the SRB will either be physically and virtually 4K aligned or SRBExtensionSize aligned. The first allocation will be on a 4K boundry the address of the second will be SRBExtensionSize larger than the first, the third will be SRBExtensionSize larger than the second, etc. Since the Command Header must be 1K aligned and the uncached extension starts 4K aligned, this works. However, the Command Header is variable and must be padded so the Received FIS is on a 256 boundry. Therefore the number of Command Headers must be 256/32 = 8. Round cap.NCS to the next multiple of 8 4.2 Setup the CommandList Although the pointer returned from AtaPortGetUnCachedExtension is useful to this driver, it does the controller no good and can't be used in CLB. The VIRTUAL address must be translated into the PHYSICAL address before being written to the CLB register as the controller doesn't have the CPU's virtual address translation tables. AtaPortGetPhysicalAddress Returns the physical address for the given Va. The va has to be an offset into any one of the following buffers. - SRB's data buffer - SRB's SrbExtension - Miniport's uncached extension 4.3 Setup the Receive FIS buffer Handle the Receive FIS buffer the same as 4.2 Command List 4.4 Setup the Local SRB Extension 5.1 Enable Command Processing 5.2 Initialize the ChannelConfiguration structure ChannelConfiguration->ChannelNumber and ChannelConfiguration->ChannelResources are kept default values. If it is found that CI and/or SACT can be changed from a 1 to 0, Number of overlapped requests becomes 1. Number of overlapped requests is a 1 based number (1=1, 2=2, etc.), CAP.NCS is a 0 based number. 5.3 START COMMAND PROCESSING Return Values: The miniport driver returns TRUE if it successfully exectute the whole function. Any errors causes the function to return FALSE and prevents the channel from loading. This ultimately causes a yellow '!' to show up on the channel in device manager. NOTE: as this routine is invoked from FindAdapter where the adapter might not be fully initialized, do not retrieve registry information. --*/ PAHCI_ADAPTER_EXTENSION adapterExtension = NULL; PAHCI_MEMORY_REGISTERS abar = NULL; //these are throw away variables ULONG mappedLength = 0; //1.1 Initialize variables adapterExtension = ChannelExtension->AdapterExtension; if (LogExecuteFullDetail(adapterExtension->LogFlags)) { RecordExecutionHistory(ChannelExtension, 0x00000024);//AhciPortInitialize } ChannelExtension->CurrentCommandSlot = 1; //slot 0 is reserved for internal commands, ChannelExtension->StateFlags.IgnoreHotplugInterrupt = TRUE; abar = (PAHCI_MEMORY_REGISTERS)adapterExtension->ABAR_Address; ChannelExtension->Px = &abar->PortList[ChannelExtension->PortNumber]; // NonCachedExtension is for CommandList, Receive FIS, SRB Extension for Local SRB and Sense SRB., READ_LOG/IDENTIFY buffer // (sizeof(AHCI_COMMAND_HEADER) * paddedNCS) + sizeof(AHCI_RECEIVED_FIS) + 2*sizeof(AHCI_SRB_EXTENSION) + sizeof(AHCI_READ_LOG_EXT_DATA); //4.2 Setup the CommandList ChannelExtension->CommandListPhysicalAddress = StorPortGetPhysicalAddress(adapterExtension, NULL, (PVOID)ChannelExtension->CommandList, &mappedLength); if (ChannelExtension->CommandListPhysicalAddress.QuadPart == 0) { RecordExecutionHistory(ChannelExtension, 0xff02);//Command List Failed return FALSE; } //3.1.1 PxCLB and PxCLBU (AHCI 1.1 Section 10.1.2 - 5) if ( (ChannelExtension->CommandListPhysicalAddress.LowPart % 1024) == 0 ) { // validate the alignment is fine StorPortWriteRegisterUlong(adapterExtension, &ChannelExtension->Px->CLB.AsUlong, ChannelExtension->CommandListPhysicalAddress.LowPart); }else{ RecordExecutionHistory(ChannelExtension, 0xff03);//Command List alignment failed return FALSE; } if (adapterExtension->CAP.S64A) { //If the controller supports 64 bits, write the high part too StorPortWriteRegisterUlong(adapterExtension, &ChannelExtension->Px->CLBU, ChannelExtension->CommandListPhysicalAddress.HighPart); } //4.3 Setup the Receive FIS buffer ChannelExtension->ReceivedFisPhysicalAddress = StorPortGetPhysicalAddress(adapterExtension, NULL, (PVOID)ChannelExtension->ReceivedFIS, &mappedLength); if (ChannelExtension->ReceivedFisPhysicalAddress.QuadPart == 0) { RecordExecutionHistory(ChannelExtension, 0xff04);//Receive FIS failed return FALSE; } //3.1.2 PxFB and PxFBU (AHCI 1.1 Section 10.1.2 - 5) if ((ChannelExtension->ReceivedFisPhysicalAddress.LowPart % 256) == 0) { // validate the alignment is fine StorPortWriteRegisterUlong(adapterExtension, &ChannelExtension->Px->FB.AsUlong, ChannelExtension->ReceivedFisPhysicalAddress.LowPart); } else { RecordExecutionHistory(ChannelExtension, 0xff05);//Receive FIS alignment failed return FALSE; } if (adapterExtension->CAP.S64A) { //If the controller supports 64 bits, write the high part too StorPortWriteRegisterUlong(adapterExtension, &ChannelExtension->Px->FBU, ChannelExtension->ReceivedFisPhysicalAddress.HighPart); } //4.4 Setup the Local SRB Extension ChannelExtension->Local.SrbExtensionPhysicalAddress = StorPortGetPhysicalAddress(adapterExtension, NULL, (PVOID)ChannelExtension->Local.SrbExtension, &mappedLength); ChannelExtension->Sense.SrbExtensionPhysicalAddress = StorPortGetPhysicalAddress(adapterExtension, NULL, (PVOID)ChannelExtension->Sense.SrbExtension, &mappedLength); //4.6 Setup Device Identify Data and Inquiry Data buffers ChannelExtension->DeviceExtension[0].IdentifyDataPhysicalAddress = StorPortGetPhysicalAddress(adapterExtension, NULL, (PVOID)ChannelExtension->DeviceExtension[0].IdentifyDeviceData, &mappedLength); ChannelExtension->DeviceExtension[0].InquiryDataPhysicalAddress = StorPortGetPhysicalAddress(adapterExtension, NULL, (PVOID)ChannelExtension->DeviceExtension[0].InquiryData, &mappedLength); //4.8 Setup STOR_ADDRESS for the device. StorAHCI uses Bus/Target/Lun addressing model, thus uses STOR_ADDRESS_TYPE_BTL8. // Port - not need to be set by miniport, Storport has this knowledge. miniport can get the value by calling StorPortGetSystemPortNumber(). // Path - StorAHCI reports (highest implemented port number + 1) as bus number, thus "port number" will be "Path" value. // Target - StorAHCI only supports single device on each port, the "Target" value will be 0. // Lun - StorAHCI only supports single device on each port, the "Lun" value will be 0. ChannelExtension->DeviceExtension[0].DeviceAddress.Type = STOR_ADDRESS_TYPE_BTL8; ChannelExtension->DeviceExtension[0].DeviceAddress.Port = 0; ChannelExtension->DeviceExtension[0].DeviceAddress.AddressLength = STOR_ADDR_BTL8_ADDRESS_LENGTH; ChannelExtension->DeviceExtension[0].DeviceAddress.Path = (UCHAR)ChannelExtension->PortNumber; ChannelExtension->DeviceExtension[0].DeviceAddress.Target = 0; ChannelExtension->DeviceExtension[0].DeviceAddress.Lun = 0; // // Inititalize device power state to D0. // ChannelExtension->DevicePowerState = StorPowerDeviceD0; //3.2 Clear Enable Interrupts on the Channel (AHCI 1.1 Section 10.1.2 - 7) //We will enable interrupt after channel started PortClearPendingInterrupt(ChannelExtension); //5.1 Enable Command Processing ChannelExtension->StateFlags.Initialized = TRUE; if (adapterExtension->CAP.NCS > 0) { //this leaves one emergency slot free if possible, as CAP.NCS is 0-based. ChannelExtension->MaxPortQueueDepth = (UCHAR)adapterExtension->CAP.NCS; } else { ChannelExtension->MaxPortQueueDepth = 1; } if ( IsSingleIoDevice(adapterExtension) || IsDumpMode(adapterExtension) ) { ChannelExtension->MaxPortQueueDepth = 1; } ChannelExtension->LastActiveSlot = 0; ChannelExtension->DeviceExtension[0].DeviceParameters.MaxDeviceQueueDepth = ChannelExtension->MaxPortQueueDepth; if (!IsDumpMode(adapterExtension)) { if (AdapterResetInInit(adapterExtension)) { P_NotRunning(ChannelExtension, ChannelExtension->Px); AhciCOMRESET(ChannelExtension, ChannelExtension->Px); } } RecordExecutionHistory(ChannelExtension, 0x10000024);//Exit AhciPortInitialize return TRUE; } BOOLEAN AhciAdapterPowerUp( _In_ PAHCI_ADAPTER_EXTENSION AdapterExtension ) /*++ Indicates that the adapter is being powered up. Anything that doesn't persist across a power cycle needs to be done here. It assumes: PCI Ensures the HBA is in D0 (Offset PMCAP + 4h: PMCS[0,1]) Called by: AhciAdapterControl It performs: Enables the AHCI Interface and global Interrupts Affected Variables/Registers: GHC.AE, GHC.IE Return Values: TRUE always. --*/ //Used to enable the AHCI interface { ULONG i; AHCI_Global_HBA_CONTROL ghc; PAHCI_MEMORY_REGISTERS abar = (PAHCI_MEMORY_REGISTERS)AdapterExtension->ABAR_Address; AdapterExtension->StateFlags.PowerDown = 0; // adapter is on its way power up. there will be no power down request coming in before this function finishes. // thus there is no need to call AdapterAcquireActiveReference; ghc.AsUlong = StorPortReadRegisterUlong(AdapterExtension, &abar->GHC.AsUlong); if (ghc.AE == 0) { ghc.AsUlong = 0; ghc.AE = 1; StorPortWriteRegisterUlong(AdapterExtension, &abar->GHC.AsUlong, ghc.AsUlong); } if (ghc.IE == 0) { ghc.IE = 1; StorPortWriteRegisterUlong(AdapterExtension, &abar->GHC.AsUlong, ghc.AsUlong); } // Try to power up all ports, otherwise only ports with device connected will be powered up by D0 to the device. // There is protection method in AhciPortPowerUp() to only allow it run once. for (i = 0; i <= AdapterExtension->HighestPort; i++) { if ( (AdapterExtension->PortExtension[i] != NULL) && (AdapterExtension->PortExtension[i]->StateFlags.PowerDown == TRUE) ) { AhciPortPowerUp(AdapterExtension->PortExtension[i]); } } return TRUE; } BOOLEAN AhciAdapterPowerDown( _In_ PAHCI_ADAPTER_EXTENSION AdapterExtension ) /*++ Indicates that the adapter is being powered down. It assumes: PCI powers down the HBA after this function returns: D3 Offset PMCAP + 4h: PMCS[0,1] Called by: AhciAdapterControl It performs: 1. Clear GHC.IE AHCI 1.1 Section 8.3.3 "Software must disable interrupts (GHC.IE must be cleared to �0�) prior to requesting a transition of the HBA to the D3 state. This precaution by software avoids an interrupt storm if an interrupt occurs during the transition to the D3 state." Affected Variables/Registers: GHC.IE Return Values: TRUE always. --*/ { ULONG i; AHCI_Global_HBA_CONTROL ghc; PAHCI_MEMORY_REGISTERS abar = (PAHCI_MEMORY_REGISTERS)AdapterExtension->ABAR_Address; // Try to power down all ports, otherwise only ports with device connected will be powered down by D3 to the device. for (i = 0; i <= AdapterExtension->HighestPort; i++) { if ( (AdapterExtension->PortExtension[i] != NULL) && (AdapterExtension->PortExtension[i]->StateFlags.PowerDown == FALSE) ) { AhciPortPowerDown(AdapterExtension->PortExtension[i]); } } ghc.AsUlong = StorPortReadRegisterUlong(AdapterExtension, &abar->GHC.AsUlong); ghc.IE = 0; StorPortWriteRegisterUlong(AdapterExtension, &abar->GHC.AsUlong, ghc.AsUlong); AdapterExtension->StateFlags.PowerDown = 1; return TRUE; } VOID AhciPortStop( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension ) { /*++ The miniport driver should stop using the resources allocated for this port. It assumes: AhciAdapterStop is called after all the ports are stopped. StartIo spin lock must be held before this function is invoked. Note: Currently this function has only one caller - AdapterStop, which has the following callers. Every respective code path has StartIo spin lock acquired. 1. AhciAdapterStop. It has one caller - AhciHwStartIo. Storport has already acquired StartIo spin lock before calling AhciHwStartIo. 2. AhciHwAdapterControl. AhciHwAdapterControl has already acquired StartIo spin lock before calling AdapterStop. Called by: It performs: (overview) 1. Stop the channel 2. Undefine all references to anything within the Uncached Extension Affected Variables/Registers: CMD.ST, CMD.CR, CMD.FRE, CMD.FR Return Values: TRUE if the function excecuted completely. FALSE if the channel could not be stopped. --*/ if (LogExecuteFullDetail(ChannelExtension->AdapterExtension->LogFlags)) { RecordExecutionHistory(ChannelExtension, 0x00000025);//AhciPortStop } //1. Stop the channel if ( !P_NotRunning(ChannelExtension, ChannelExtension->Px) ) { // don't need RESET, the port will be tried to start when processing start device request RecordExecutionHistory(ChannelExtension, 0xff08); //AhciPortStop Failed } //2. Disable Interrupt and disconnect with Port resources StorPortWriteRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->IE.AsUlong, 0); //disabling interrupts PortClearPendingInterrupt(ChannelExtension); ChannelExtension->Px = 0; ChannelExtension->StateFlags.Initialized = FALSE; ChannelExtension->StateFlags.NoMoreIO = FALSE; RecordExecutionHistory(ChannelExtension, 0x10000025);//Exit AhciPortStop return; } VOID AhciPortPowerUp( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension ) { /*++ Indicates that the channel is being powered up. Called by: AhciHwStartIo It performs: (overview) 1. Start the Port. 2. If APM is supported, make sure the Link is Active as defined in AHCI1.0 8.3.1.2. 3. If Port Multiplier is supported, powered it up. (details) 1.1 Reload the CLB,CBU,FLB,FBU stored in the channel extension 1.3 Reinitialize the StateFlags 1.4 Start the channel Affected Variables/Registers: PxCMD.ST, PxCMD.ICC PxCLB,PxCLBU,PxFB,PxFBU PxIE Return Values: none --*/ AHCI_LPM_POWER_SETTINGS userLpmSettings; BOOLEAN portPowerDown; RecordExecutionHistory(ChannelExtension, 0x00000026);//Enter AhciPortPowerUp //1.0 Reinitialize the StateFlags. e.g. ChannelExtension->StateFlags.PowerDown = FALSE; portPowerDown = InterlockedBitTestAndReset((LONG*)&ChannelExtension->StateFlags, 12); //StateFlags.PownDown field is at bit 12 if (portPowerDown == FALSE) { RecordExecutionHistory(ChannelExtension, 0x00010026);//AhciPortPowerUp: port already powered up. return; } //1.1 Reload the CLB,CBU,FLB,FBU StorPortWriteRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CLB.AsUlong, ChannelExtension->CommandListPhysicalAddress.LowPart); if (ChannelExtension->AdapterExtension->CAP.S64A) { //If the controller supports 64 bits, write high part StorPortWriteRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CLBU, ChannelExtension->CommandListPhysicalAddress.HighPart); } StorPortWriteRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->FB.AsUlong, ChannelExtension->ReceivedFisPhysicalAddress.LowPart); if (ChannelExtension->AdapterExtension->CAP.S64A) { //If the controller supports 64 bits, write high part StorPortWriteRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->FBU, ChannelExtension->ReceivedFisPhysicalAddress.HighPart); } // // If D3 Cold is enabled and we are being powered up from D3, we need to be // a bit heavy-handed with powering up the port due to loss of context. // if (ChannelExtension->DevicePowerState == StorPowerDeviceD3 && IsPortD3ColdEnabled(ChannelExtension)) { // 1.4.1 Re-issue init commands (this will also restore preserved settings). AhciPortIssueInitCommands(ChannelExtension); // 1.4.2 Restore LPM settings userLpmSettings.AsUlong = ChannelExtension->LastUserLpmPowerSetting; AhciLpmSettingsModes(ChannelExtension, userLpmSettings); //ignore the returned value, IO will be restarted anyway. // 1.5 Start the channel by issuing a reset to restore PHY communication. AhciPortReset(ChannelExtension, FALSE); } else { // 1.4.1 Restore Perserved Settings if (NeedToSetTransferMode(ChannelExtension)) { RestorePreservedSettings(ChannelExtension, FALSE); } // 1.5 Start the channel P_Running_StartAttempt(ChannelExtension, FALSE); } RecordExecutionHistory(ChannelExtension, 0x10000026);//Exit AhciPortPowerUp return; } VOID AhciPortPowerDown( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension ) { /*++ Indicates that the channel is being powered down. It assumes: the device has been powered down through ATA commands All outstanding IO will be complete before the first power request is sent to the miniport Called by: AhciHwStartIo It performs: Then each port must be stopped. PxCMD.ST If APM is supported, the Link need to be put into Slumber as defined in AHCI 1.1 Section 8.3.1.2 If Port Multiplier is support, it would need to be powered down next. Affected Variables/Registers: none Return Values: TRUE if the function excecuted completely. FALSE if the channel could not be stopped for Power Down. Neither return value is used by ATAport. --*/ ChannelExtension->StateFlags.PowerDown = TRUE; // // Cancel the StartPortTimer since we're going into a lower power state. // StorPortRequestTimer(ChannelExtension->AdapterExtension, ChannelExtension->StartPortTimer, P_Running_Callback, ChannelExtension, 0, 0); if (ChannelExtension->StateFlags.PoFxEnabled == 1) { if (IsPortD3ColdEnabled(ChannelExtension)) { // the link will be inactive, ignore the hot plug noise. ChannelExtension->StateFlags.IgnoreHotplugInterrupt = TRUE; } } RecordExecutionHistory(ChannelExtension, 0x10000027);//Exit AhciPortPowerDown } VOID ReportLunsComplete( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb ) { //Port start completed. Prepare device list. ULONG lunCount; ULONG lunLength; PLUN_LIST lunList; ULONG i; PAHCI_SRB_EXTENSION srbExtension; ULONG srbDataBufferLength = SrbGetDataTransferLength(Srb); srbExtension = GetSrbExtension(Srb); // clean up callback fields so that the SRB can be completed. srbExtension->AtaFunction = 0; srbExtension->CompletionRoutine = NULL; // report error back so that Storport may retry the command. // tolerate failure from IDE_COMMAND_READ_LOG_EXT during device enumeration as it's not part of device enumeration commands. if ( (srbExtension->TaskFile.Current.bCommandReg != IDE_COMMAND_READ_LOG_EXT) && (Srb->SrbStatus != SRB_STATUS_PENDING) && (Srb->SrbStatus != SRB_STATUS_SUCCESS) && (Srb->SrbStatus != SRB_STATUS_NO_DEVICE) ) { return; } Srb->SrbStatus = SRB_STATUS_SUCCESS; SrbSetScsiStatus(Srb, SCSISTAT_GOOD); lunList = (PLUN_LIST)SrbGetDataBuffer(Srb); if ( ChannelExtension->DeviceExtension->DeviceParameters.AtaDeviceType == DeviceNotExist ) { lunCount = 0; } else { //lunCount = ChannelExtension->DeviceExtension->DeviceParameters.MaximumLun + 1; lunCount = 1; } lunLength = lunCount * 8; if ( srbDataBufferLength < (sizeof(LUN_LIST) + lunLength) ) { Srb->SrbStatus = SRB_STATUS_DATA_OVERRUN; if (srbDataBufferLength >= sizeof(ULONG)) { //fill in required buffer size lunList->LunListLength[0] = (UCHAR)(lunLength >> (8*3)); lunList->LunListLength[1] = (UCHAR)(lunLength >> (8*2)); lunList->LunListLength[2] = (UCHAR)(lunLength >> (8*1)); lunList->LunListLength[3] = (UCHAR)(lunLength >> (8*0)); } } else { lunList->LunListLength[0] = (UCHAR)(lunLength >> (8*3)); lunList->LunListLength[1] = (UCHAR)(lunLength >> (8*2)); lunList->LunListLength[2] = (UCHAR)(lunLength >> (8*1)); lunList->LunListLength[3] = (UCHAR)(lunLength >> (8*0)); for (i = 0; i < lunCount; i++) { lunList->Lun[i][0] = 0; lunList->Lun[i][1] = (UCHAR)i; lunList->Lun[i][2] = 0; lunList->Lun[i][3] = 0; lunList->Lun[i][4] = 0; lunList->Lun[i][5] = 0; lunList->Lun[i][6] = 0; lunList->Lun[i][7] = 0; } } return; } VOID InitQueryLogPages ( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension ) /*++ Initialize Log Pages to Read. Note that this functions should only be called after Identify Device Data is retrieved. Parameters: ChannelExtension - port that log-pages-to-query should be inited. Return Values: None --*/ { PUSHORT index = &ChannelExtension->DeviceExtension->QueryLogPages.TotalPageCount; // // Log Page only applies to ATA device; General Purpose Logging feature should be supported; // 48bit command should be supported as READ LOG EXT is a 48bit command. // if (!IsDeviceGeneralPurposeLoggingSupported(ChannelExtension)) { return; } AhciZeroMemory((PCHAR)&ChannelExtension->DeviceExtension->QueryLogPages, sizeof(ATA_GPL_PAGES_TO_QUERY)); // read Log Directory ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].Query = TRUE; ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].LogAddress = IDE_GP_LOG_DIRECTORY_ADDRESS; ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].PageNumber = 0; ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].BlockCount = 1; ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].FeatureField = 0; *index = *index + 1; // read Device Statistics log - supported page ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].Query = TRUE; ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].LogAddress = IDE_GP_LOG_DEVICE_STATISTICS_ADDRESS; ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].PageNumber = IDE_GP_LOG_SUPPORTED_PAGES; ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].BlockCount = 1; ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].FeatureField = 0; *index = *index + 1; // read Device Statistics log - general page ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].Query = TRUE; ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].LogAddress = IDE_GP_LOG_DEVICE_STATISTICS_ADDRESS; ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].PageNumber = IDE_GP_LOG_DEVICE_STATISTICS_GENERAL_PAGE; ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].BlockCount = 1; ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].FeatureField = 0; *index = *index + 1; // read Identify Device Data log - supported page ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].Query = TRUE; ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].LogAddress = IDE_GP_LOG_IDENTIFY_DEVICE_DATA_ADDRESS; ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].PageNumber = IDE_GP_LOG_SUPPORTED_PAGES; ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].BlockCount = 1; ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].FeatureField = 0; *index = *index + 1; // read Identify Device Data log - SATA page ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].Query = TRUE; ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].LogAddress = IDE_GP_LOG_IDENTIFY_DEVICE_DATA_ADDRESS; ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].PageNumber = IDE_GP_LOG_IDENTIFY_DEVICE_DATA_SATA_PAGE; ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].BlockCount = 1; ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].FeatureField = 0; *index = *index + 1; // read Saved Device Internal log ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].Query = TRUE; ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].LogAddress = IDE_GP_LOG_SAVED_DEVICE_INTERNAL_STATUS; ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].PageNumber = 0; ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].BlockCount = 1; ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].FeatureField = 0; *index = *index + 1; // read NCQ non-Data log if ((ChannelExtension->DeviceExtension->IdentifyDeviceData->SerialAtaCapabilities.NCQ == 1) && (ChannelExtension->DeviceExtension->IdentifyDeviceData->SerialAtaCapabilities.NcqQueueMgmt == 1)) { ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].Query = TRUE; } else { ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].Query = FALSE; } ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].LogAddress = IDE_GP_LOG_NCQ_NON_DATA_ADDRESS; ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].PageNumber = 0; ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].BlockCount = 1; ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].FeatureField = 0; *index = *index + 1; // read NCQ Send Receive log if ((ChannelExtension->DeviceExtension->IdentifyDeviceData->SerialAtaCapabilities.NCQ == 1) && (ChannelExtension->DeviceExtension->IdentifyDeviceData->SerialAtaCapabilities.NcqReceiveSend == 1)) { ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].Query = TRUE; } else { ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].Query = FALSE; } ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].LogAddress = IDE_GP_LOG_NCQ_SEND_RECEIVE_ADDRESS; ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].PageNumber = 0; ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].BlockCount = 1; ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].FeatureField = 0; *index = *index + 1; NT_ASSERT(*index <= ATA_GPL_PAGES_QUERY_COUNT); return; } VOID IssueReadLogExtCommand( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb, _In_ UCHAR LogAddress, _In_ USHORT PageNumber, _In_ USHORT BlockCount, _In_ USHORT FeatureField, _In_ PSTOR_PHYSICAL_ADDRESS PhysicalAddress, _In_ PVOID DataBuffer, _In_opt_ PSRB_COMPLETION_ROUTINE CompletionRoutine ) /*++ Issue READ LOG EXT command to device Parameters: ChannelExtension - port that the command should be sent to Srb - Srb that carries READ LOG EXT command in SrbExtension LogAddress - Log address PageNumber - Page# of the log BlockCount - How many blocks (in 512 bytes) FeatureField - log specific value PhysicalAddress - Buffer physical address DataBuffer - Buffer CompletionRoutine - Routine that needs to be executed after READ LOG EXT command completed Return Values: None --*/ { PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); UNREFERENCED_PARAMETER(ChannelExtension); //1 Fills in the local SRB srbExtension->AtaFunction = ATA_FUNCTION_ATA_COMMAND; srbExtension->Flags |= ATA_FLAGS_DATA_IN ; srbExtension->Flags |= ATA_FLAGS_48BIT_COMMAND ; srbExtension->CompletionRoutine = CompletionRoutine; //setup TaskFile srbExtension->TaskFile.Current.bFeaturesReg = (UCHAR)(FeatureField & 0xFF); //FeatureField, low part srbExtension->TaskFile.Current.bSectorCountReg = (UCHAR)(BlockCount & 0xFF); //Number of blocks to read, low part srbExtension->TaskFile.Current.bSectorNumberReg = LogAddress; //Log address srbExtension->TaskFile.Current.bCylLowReg = (UCHAR)(PageNumber & 0xFF); //Page#, low part srbExtension->TaskFile.Current.bCylHighReg = 0; srbExtension->TaskFile.Current.bDriveHeadReg = 0xA0 | IDE_LBA_MODE; srbExtension->TaskFile.Current.bCommandReg = IDE_COMMAND_READ_LOG_EXT; srbExtension->TaskFile.Current.bReserved = 0; srbExtension->TaskFile.Previous.bFeaturesReg = (UCHAR)((FeatureField >> 8) & 0xFF); //FeatureField, high part srbExtension->TaskFile.Previous.bSectorCountReg = (UCHAR)((BlockCount >> 8) & 0xFF); //Number of blocks to read, high part srbExtension->TaskFile.Previous.bSectorNumberReg = 0; srbExtension->TaskFile.Previous.bCylLowReg = (UCHAR)((PageNumber >> 8) & 0xFF); //Page#, high part srbExtension->TaskFile.Previous.bCylHighReg = 0; srbExtension->TaskFile.Previous.bDriveHeadReg = 0; srbExtension->TaskFile.Previous.bCommandReg = 0; srbExtension->TaskFile.Previous.bReserved = 0; Srb->SrbStatus = SRB_STATUS_PENDING; srbExtension->DataBuffer = DataBuffer; //setup SGL if ( PhysicalAddress ) { srbExtension->LocalSgl.NumberOfElements = 1; srbExtension->LocalSgl.List[0].PhysicalAddress.LowPart = PhysicalAddress->LowPart; srbExtension->LocalSgl.List[0].PhysicalAddress.HighPart = PhysicalAddress->HighPart; srbExtension->LocalSgl.List[0].Length = ATA_BLOCK_SIZE * BlockCount; srbExtension->Sgl = &srbExtension->LocalSgl; srbExtension->DataTransferLength = ATA_BLOCK_SIZE * BlockCount; } return; } __inline USHORT GetNextQueryLogPageIndex ( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension ) /*++ Get an Index value that log page should be queried. Parameters: ChannelExtension Return Values: USHORT --*/ { USHORT i; USHORT index = ChannelExtension->DeviceExtension->QueryLogPages.CurrentPageIndex; if (index >= ChannelExtension->DeviceExtension->QueryLogPages.TotalPageCount) { return ATA_GPL_PAGES_INVALID_INDEX; } for (i = index; i < ChannelExtension->DeviceExtension->QueryLogPages.TotalPageCount; i++) { if (ChannelExtension->DeviceExtension->QueryLogPages.LogPage[i].Query) { ChannelExtension->DeviceExtension->QueryLogPages.CurrentPageIndex = i; return i; } } return ATA_GPL_PAGES_INVALID_INDEX; } __inline VOID ReadQueryLogPage ( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb, _In_ USHORT Index ) { IssueReadLogExtCommand( ChannelExtension, Srb, ChannelExtension->DeviceExtension->QueryLogPages.LogPage[Index].LogAddress, ChannelExtension->DeviceExtension->QueryLogPages.LogPage[Index].PageNumber, ChannelExtension->DeviceExtension->QueryLogPages.LogPage[Index].BlockCount, ChannelExtension->DeviceExtension->QueryLogPages.LogPage[Index].FeatureField, &ChannelExtension->DeviceExtension->ReadLogExtPageDataPhysicalAddress, (PVOID)ChannelExtension->DeviceExtension->ReadLogExtPageData, LogPageDiscoveryCompletion ); } __inline VOID UpdateQueryLogPageSupportive ( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ UCHAR LogAddress, _In_ USHORT PageNumber, _In_ BOOLEAN Supported ) /*++ Get an Index value that log page should be queried. Parameters: ChannelExtension - port that log-page-to-query should be updated. LogAddress - Log address PageNumber - Page# of the log Supported - log page is supported or not by device Return Values: None --*/ { USHORT i; for (i = 0; i < ChannelExtension->DeviceExtension->QueryLogPages.TotalPageCount; i++) { if ((ChannelExtension->DeviceExtension->QueryLogPages.LogPage[i].LogAddress == LogAddress) && (ChannelExtension->DeviceExtension->QueryLogPages.LogPage[i].PageNumber == PageNumber)) { ChannelExtension->DeviceExtension->QueryLogPages.LogPage[i].Query = Supported; return; } } return; } VOID LogPageDiscoveryCompletion ( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb ) /* This process is to discover all needed information from general log pages. The process is initiated by reading log directory when Identify Device command completes. */ { USHORT nextPageIndex = ATA_GPL_PAGES_INVALID_INDEX; UCHAR completedLogAddress = 0; USHORT completedPageNumber = 0; PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); // get the Log Address and Log Page just received completedLogAddress = srbExtension->TaskFile.Current.bSectorNumberReg; completedPageNumber = ((USHORT)srbExtension->TaskFile.Previous.bCylLowReg << 8) | srbExtension->TaskFile.Current.bCylLowReg; if ( (completedLogAddress == IDE_GP_LOG_DIRECTORY_ADDRESS) && (completedPageNumber == 0) ) { // the issued command was for getting Log Directory // ACS8-3, 4.12.1 Devices that report support for the NCQ feature set shall also report support for the GPL feature set (see 4.9), // the General Purpose Log Directory log and the NCQ Command Error log. NT_ASSERT( (Srb->SrbStatus == SRB_STATUS_SUCCESS) || (ChannelExtension->DeviceExtension->IdentifyDeviceData->SerialAtaCapabilities.NCQ == 0) ); if ( (Srb->SrbStatus == SRB_STATUS_SUCCESS) && (ChannelExtension->DeviceExtension->ReadLogExtPageData[0] == IDE_GP_LOG_VERSION) ) { // per ACS spec: The value of the General Purpose Logging Version word shall be 0001h // preserve the log address supportive information ChannelExtension->DeviceExtension->SupportedGPLPages.DeviceStatistics.LogAddressSupported = (ChannelExtension->DeviceExtension->ReadLogExtPageData[IDE_GP_LOG_DEVICE_STATISTICS_ADDRESS] > 0) ? 1 : 0; if (ChannelExtension->DeviceExtension->SupportedGPLPages.DeviceStatistics.LogAddressSupported == 0) { UpdateQueryLogPageSupportive(ChannelExtension, IDE_GP_LOG_DEVICE_STATISTICS_ADDRESS, IDE_GP_LOG_SUPPORTED_PAGES, FALSE); UpdateQueryLogPageSupportive(ChannelExtension, IDE_GP_LOG_DEVICE_STATISTICS_ADDRESS, IDE_GP_LOG_DEVICE_STATISTICS_GENERAL_PAGE, FALSE); } ChannelExtension->DeviceExtension->SupportedGPLPages.IdentifyDeviceData.LogAddressSupported = (ChannelExtension->DeviceExtension->ReadLogExtPageData[IDE_GP_LOG_IDENTIFY_DEVICE_DATA_ADDRESS] > 0) ? 1 : 0; if (ChannelExtension->DeviceExtension->SupportedGPLPages.IdentifyDeviceData.LogAddressSupported == 0) { UpdateQueryLogPageSupportive(ChannelExtension, IDE_GP_LOG_IDENTIFY_DEVICE_DATA_ADDRESS, IDE_GP_LOG_SUPPORTED_PAGES, FALSE); UpdateQueryLogPageSupportive(ChannelExtension, IDE_GP_LOG_IDENTIFY_DEVICE_DATA_ADDRESS, IDE_GP_LOG_IDENTIFY_DEVICE_DATA_SATA_PAGE, FALSE); } ChannelExtension->DeviceExtension->SupportedGPLPages.SinglePage.NcqCommandError = (ChannelExtension->DeviceExtension->ReadLogExtPageData[IDE_GP_LOG_NCQ_COMMAND_ERROR_ADDRESS] > 0) ? 1 : 0; ChannelExtension->DeviceExtension->SupportedGPLPages.SinglePage.NcqNonData = (ChannelExtension->DeviceExtension->ReadLogExtPageData[IDE_GP_LOG_NCQ_NON_DATA_ADDRESS] > 0) ? 1 : 0; if (ChannelExtension->DeviceExtension->SupportedGPLPages.SinglePage.NcqNonData == 0) { UpdateQueryLogPageSupportive(ChannelExtension, IDE_GP_LOG_NCQ_NON_DATA_ADDRESS, 0, FALSE); } ChannelExtension->DeviceExtension->SupportedGPLPages.SinglePage.NcqSendReceive = (ChannelExtension->DeviceExtension->ReadLogExtPageData[IDE_GP_LOG_NCQ_SEND_RECEIVE_ADDRESS] > 0) ? 1 : 0; if (ChannelExtension->DeviceExtension->SupportedGPLPages.SinglePage.NcqSendReceive == 0) { UpdateQueryLogPageSupportive(ChannelExtension, IDE_GP_LOG_NCQ_SEND_RECEIVE_ADDRESS, 0, FALSE); } ChannelExtension->DeviceExtension->SupportedGPLPages.SinglePage.HybridInfo = (ChannelExtension->DeviceExtension->ReadLogExtPageData[IDE_GP_LOG_HYBRID_INFO_ADDRESS] > 0) ? 1 : 0; } else { // Log Directory can be optional. Preset supportive info, they will be updated if the actual command fails later. // In case of the disk doesn't support NCQ and doesn't support Log Directory, still try to discover some log pages. // don't query all other log pages. UpdateQueryLogPageSupportive(ChannelExtension, IDE_GP_LOG_DEVICE_STATISTICS_ADDRESS, IDE_GP_LOG_SUPPORTED_PAGES, FALSE); UpdateQueryLogPageSupportive(ChannelExtension, IDE_GP_LOG_DEVICE_STATISTICS_ADDRESS, IDE_GP_LOG_DEVICE_STATISTICS_GENERAL_PAGE, FALSE); UpdateQueryLogPageSupportive(ChannelExtension, IDE_GP_LOG_IDENTIFY_DEVICE_DATA_ADDRESS, IDE_GP_LOG_SUPPORTED_PAGES, FALSE); UpdateQueryLogPageSupportive(ChannelExtension, IDE_GP_LOG_IDENTIFY_DEVICE_DATA_ADDRESS, IDE_GP_LOG_IDENTIFY_DEVICE_DATA_SATA_PAGE, FALSE); UpdateQueryLogPageSupportive(ChannelExtension, IDE_GP_LOG_NCQ_NON_DATA_ADDRESS, 0, FALSE); UpdateQueryLogPageSupportive(ChannelExtension, IDE_GP_LOG_NCQ_SEND_RECEIVE_ADDRESS, 0, FALSE); } } else if ( (completedLogAddress == IDE_GP_LOG_DEVICE_STATISTICS_ADDRESS) && (completedPageNumber == IDE_GP_LOG_SUPPORTED_PAGES) ) { // the issued command was for getting supported log pages of device statistics log if (Srb->SrbStatus == SRB_STATUS_SUCCESS) { PDEVICE_STATISTICS_LOG_PAGE_HEADER pageHeader = (PDEVICE_STATISTICS_LOG_PAGE_HEADER)ChannelExtension->DeviceExtension->ReadLogExtPageData; PUCHAR pageSupported = (PUCHAR)ChannelExtension->DeviceExtension->ReadLogExtPageData; // first byte after header is how many entries in following list. UCHAR pageCount = *(pageSupported + sizeof(DEVICE_STATISTICS_LOG_PAGE_HEADER)); // The value of revision number word shall be 0001h. The first supported page shall be 00h. if ( (pageHeader->RevisionNumber == IDE_GP_LOG_VERSION) && (pageHeader->PageNumber == IDE_GP_LOG_SUPPORTED_PAGES) && (pageCount > 1) ) { int i; for (i = 1; i <= pageCount; i++) { // if the page number is shown in supported list, mark it's supported. if (*(pageSupported + sizeof(DEVICE_STATISTICS_LOG_PAGE_HEADER) + i) == IDE_GP_LOG_DEVICE_STATISTICS_GENERAL_PAGE) { ChannelExtension->DeviceExtension->SupportedGPLPages.DeviceStatistics.GeneralStatistics = 1; break; } } } if (ChannelExtension->DeviceExtension->SupportedGPLPages.DeviceStatistics.GeneralStatistics == 0) { UpdateQueryLogPageSupportive(ChannelExtension, IDE_GP_LOG_DEVICE_STATISTICS_ADDRESS, IDE_GP_LOG_DEVICE_STATISTICS_GENERAL_PAGE, FALSE); } } else { ChannelExtension->DeviceExtension->SupportedGPLPages.DeviceStatistics.LogAddressSupported = 0; UpdateQueryLogPageSupportive(ChannelExtension, IDE_GP_LOG_DEVICE_STATISTICS_ADDRESS, IDE_GP_LOG_DEVICE_STATISTICS_GENERAL_PAGE, FALSE); } } else if ( (completedLogAddress == IDE_GP_LOG_DEVICE_STATISTICS_ADDRESS) && (completedPageNumber == IDE_GP_LOG_DEVICE_STATISTICS_GENERAL_PAGE) ) { // the issued command was for getting General Statistics page of Device Statistics Log if (Srb->SrbStatus == SRB_STATUS_SUCCESS) { PGP_LOG_GENERAL_STATISTICS generalStatistics = (PGP_LOG_GENERAL_STATISTICS)ChannelExtension->DeviceExtension->ReadLogExtPageData; // The value of revision number word shall be 0002h. if ( (generalStatistics->Header.RevisionNumber == 0x0002) && (generalStatistics->Header.PageNumber == IDE_GP_LOG_DEVICE_STATISTICS_GENERAL_PAGE) ) { if (generalStatistics->DateAndTime.Supported == 1) { ChannelExtension->DeviceExtension->SupportedCommands.SetDateAndTime = 1; } } } else { ChannelExtension->DeviceExtension->SupportedGPLPages.DeviceStatistics.GeneralStatistics = 0; } } else if ( (completedLogAddress == IDE_GP_LOG_IDENTIFY_DEVICE_DATA_ADDRESS) && (completedPageNumber == IDE_GP_LOG_SUPPORTED_PAGES) ) { // the issued command was for getting supported log pages of identify device data log if (Srb->SrbStatus == SRB_STATUS_SUCCESS) { PIDENTIFY_DEVICE_DATA_LOG_PAGE_HEADER pageHeader = (PIDENTIFY_DEVICE_DATA_LOG_PAGE_HEADER)ChannelExtension->DeviceExtension->ReadLogExtPageData; PUCHAR pageSupported = (PUCHAR)ChannelExtension->DeviceExtension->ReadLogExtPageData; // first byte after header is how many entries in following list. UCHAR pageCount = *(pageSupported + sizeof(IDENTIFY_DEVICE_DATA_LOG_PAGE_HEADER)); // The value of revision number word shall be 0001h. The first supported page shall be 00h. if ( (pageHeader->RevisionNumber == IDE_GP_LOG_VERSION) && (pageHeader->PageNumber == IDE_GP_LOG_SUPPORTED_PAGES) && (pageCount > 1) ) { int i; for (i = 1; i <= pageCount; i++) { // if the page number is shown in supported list, mark it's supported. if (*(pageSupported + sizeof(IDENTIFY_DEVICE_DATA_LOG_PAGE_HEADER) + i) == IDE_GP_LOG_IDENTIFY_DEVICE_DATA_SATA_PAGE) { ChannelExtension->DeviceExtension->SupportedGPLPages.IdentifyDeviceData.SATA = 1; break; } } } if (ChannelExtension->DeviceExtension->SupportedGPLPages.IdentifyDeviceData.SATA == 0) { UpdateQueryLogPageSupportive(ChannelExtension, IDE_GP_LOG_IDENTIFY_DEVICE_DATA_ADDRESS, IDE_GP_LOG_IDENTIFY_DEVICE_DATA_SATA_PAGE, FALSE); } } else { ChannelExtension->DeviceExtension->SupportedGPLPages.IdentifyDeviceData.LogAddressSupported = 0; UpdateQueryLogPageSupportive(ChannelExtension, IDE_GP_LOG_IDENTIFY_DEVICE_DATA_ADDRESS, IDE_GP_LOG_IDENTIFY_DEVICE_DATA_SATA_PAGE, FALSE); } } else if ( (completedLogAddress == IDE_GP_LOG_IDENTIFY_DEVICE_DATA_ADDRESS) && (completedPageNumber == IDE_GP_LOG_IDENTIFY_DEVICE_DATA_SATA_PAGE) ) { // the issued command was for getting SATA log page of identify device data log if (Srb->SrbStatus == SRB_STATUS_SUCCESS) { } else { ChannelExtension->DeviceExtension->SupportedGPLPages.IdentifyDeviceData.SATA = 0; } } else if ( (completedLogAddress == IDE_GP_LOG_SAVED_DEVICE_INTERNAL_STATUS) && (completedPageNumber == 0) ) { // the issued command was for getting saved device internal data log if (Srb->SrbStatus != SRB_STATUS_SUCCESS) { ChannelExtension->DeviceExtension->SupportedGPLPages.SinglePage.SavedDeviceInternalStatusData = 0; } } else if ( (completedLogAddress == IDE_GP_LOG_NCQ_NON_DATA_ADDRESS) && (completedPageNumber == 0) ) { // the issued command was for getting ncq non-data log if (Srb->SrbStatus == SRB_STATUS_SUCCESS) { PGP_LOG_NCQ_NON_DATA ncqNonData = (PGP_LOG_NCQ_NON_DATA)ChannelExtension->DeviceExtension->ReadLogExtPageData; ChannelExtension->DeviceExtension->SupportedCommands.HybridDemoteBySize = ncqNonData->SubCmd2.HybridDemoteBySize; ChannelExtension->DeviceExtension->SupportedCommands.HybridChangeByLbaRange = ncqNonData->SubCmd3.HybridChangeByLbaRange; ChannelExtension->DeviceExtension->SupportedCommands.HybridControl = ncqNonData->SubCmd4.HybridControl; } else { NT_ASSERT(FALSE); } } else if ( (completedLogAddress == IDE_GP_LOG_NCQ_SEND_RECEIVE_ADDRESS) && (completedPageNumber == 0) ) { // the issued command was for getting ncq send receive log if (Srb->SrbStatus == SRB_STATUS_SUCCESS) { PGP_LOG_NCQ_SEND_RECEIVE ncqSendReceive = (PGP_LOG_NCQ_SEND_RECEIVE)ChannelExtension->DeviceExtension->ReadLogExtPageData; ChannelExtension->DeviceExtension->SupportedCommands.HybridEvict = ncqSendReceive->SubCmd.HybridEvict; } else { NT_ASSERT(FALSE); } } else { // all log addresses and log pages in log page discovery process should be covered in above conditions. NT_ASSERT(FALSE); } // // Move index to the next one and check if there is any log pages pending to read. // ChannelExtension->DeviceExtension->QueryLogPages.CurrentPageIndex++; nextPageIndex = GetNextQueryLogPageIndex(ChannelExtension); if (nextPageIndex != ATA_GPL_PAGES_INVALID_INDEX) { ReadQueryLogPage(ChannelExtension, Srb, nextPageIndex); } else { ReportLunsComplete(ChannelExtension, Srb); } return; } VOID AhciPortIdentifyDevice( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb ) { PAHCI_SRB_EXTENSION srbExtension; PCDB cdb = SrbGetCdb(Srb); srbExtension = GetSrbExtension(Srb); if (Srb->SrbStatus == SRB_STATUS_BUS_RESET) { return; } if (Srb->SrbStatus == SRB_STATUS_SUCCESS) { if (srbExtension->TaskFile.Current.bCommandReg == IDE_COMMAND_ATAPI_IDENTIFY) { ChannelExtension->DeviceExtension->DeviceParameters.AtaDeviceType = DeviceIsAtapi; } else { ChannelExtension->DeviceExtension->DeviceParameters.AtaDeviceType = DeviceIsAta; } // identify completes, digest identify data / inquiry data UpdateDeviceParameters(ChannelExtension); // Initialize port properties ChannelExtension->PortProperties = 0; if (IsExternalPort(ChannelExtension)) { SETMASK(ChannelExtension->PortProperties, PORT_PROPERTIES_EXTERNAL_PORT); } } else if (Srb->SrbStatus == SRB_STATUS_NO_DEVICE) { // command failed consider as no device ChannelExtension->DeviceExtension->DeviceParameters.AtaDeviceType = DeviceNotExist; } // Identify Device can only be triggered from REPORT LUNS command or // INQUIRY command (for disk in dump environment) if ((cdb != NULL) && (cdb->CDB10.OperationCode == SCSIOP_REPORT_LUNS)) { // clear previous values as they are going to be retrieved again. AhciZeroMemory((PCHAR)&ChannelExtension->DeviceExtension->SupportedGPLPages, sizeof(ATA_SUPPORTED_GPL_PAGES)); AhciZeroMemory((PCHAR)&ChannelExtension->DeviceExtension->SupportedCommands, sizeof(ATA_COMMAND_SUPPORTED)); if ( (Srb->SrbStatus == SRB_STATUS_SUCCESS) && IsDeviceGeneralPurposeLoggingSupported(ChannelExtension) ) { // READ LOG EXT command is supported. USHORT index; InitQueryLogPages(ChannelExtension); index = GetNextQueryLogPageIndex(ChannelExtension); NT_ASSERT(index == 0); if (index != ATA_GPL_PAGES_INVALID_INDEX) { //First page should be log directory. Read it to get pages supported by device. ReadQueryLogPage(ChannelExtension, Srb, index); } else { ReportLunsComplete(ChannelExtension, Srb); } } else { ReportLunsComplete(ChannelExtension, Srb); } } else if (IsDumpMode(ChannelExtension->AdapterExtension) && (cdb != NULL) && (cdb->CDB10.OperationCode == SCSIOP_INQUIRY)) { if (IsDumpResumeMode(ChannelExtension->AdapterExtension) && (Srb->SrbStatus == SRB_STATUS_SUCCESS) && IsDeviceGeneralPurposeLoggingSupported(ChannelExtension) && IsDeviceHybridInfoSupported(ChannelExtension)) { // // Read Hybrid Informaton log during resume, so that disk can stop self-pinning. // In normal stack, suerfetch sends down HYBRID_FUNCTION_GET_INFO triggers the log to be read. // IssueReadLogExtCommand( ChannelExtension, Srb, IDE_GP_LOG_HYBRID_INFO_ADDRESS, 0, 1, 0, // feature field &ChannelExtension->DeviceExtension->ReadLogExtPageDataPhysicalAddress, (PVOID)ChannelExtension->DeviceExtension->ReadLogExtPageData, (PSRB_COMPLETION_ROUTINE)InquiryComplete ); } else { InquiryComplete(ChannelExtension, Srb); } } return; } VOID AhciPortNVCacheCompletion( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb ) { PSRB_IO_CONTROL srbControl; PNVCACHE_REQUEST_BLOCK nRB; PATA_TASK_FILE TaskFile; PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); UNREFERENCED_PARAMETER(ChannelExtension); srbControl = (PSRB_IO_CONTROL)SrbGetDataBuffer(Srb); nRB = ((PNVCACHE_REQUEST_BLOCK) ( (PSRB_IO_CONTROL) srbControl + 1) ); //26015: "Potential overflow using expression 'nRB->NRBStatus' Buffer access is apparently unbounded by the buffer size. // Return status sucess indicating that the request was handled by the device. nRB->NRBStatus = NRB_SUCCESS; TaskFile = (PATA_TASK_FILE)srbExtension->ResultBuffer; if ( TaskFile != NULL ) { nRB->NVCacheStatus = TaskFile->Current.bCommandReg; if (TaskFile->Current.bCommandReg & 1) { // command failed nRB->NVCacheSubStatus = TaskFile->Current.bFeaturesReg; } nRB->Count = (TaskFile->Current.bSectorCountReg << 8) + (TaskFile->Previous.bSectorCountReg); nRB->LBA = (ULONGLONG) TaskFile->Previous.bCylHighReg; nRB->LBA <<= 8; nRB->LBA += (ULONGLONG) TaskFile->Previous.bCylLowReg; nRB->LBA <<= 8; nRB->LBA += (ULONGLONG) TaskFile->Previous.bSectorNumberReg; nRB->LBA <<= 8; nRB->LBA += (ULONGLONG) TaskFile->Current.bCylHighReg; nRB->LBA <<= 8; nRB->LBA += (ULONGLONG) TaskFile->Current.bCylLowReg; nRB->LBA <<= 8; nRB->LBA += (ULONGLONG) TaskFile->Current.bSectorNumberReg; // // Free the buffer allocated as mode sense info buffer , holding task file // AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, srbExtension->ResultBufferLength, TaskFile); } else { // in case TaskFile is not returned in SenseInfoBuffer, use cached ATA Status and Error register values. if (Srb->SrbStatus == SRB_STATUS_SUCCESS) { // command succeeded nRB->NVCacheStatus = 0; nRB->NVCacheSubStatus = 0; } else { // command failed nRB->NVCacheStatus = srbExtension->AtaStatus; nRB->NVCacheSubStatus = srbExtension->AtaError; } } return; } VOID AhciPortSmartCompletion( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb ) { PSENDCMDOUTPARAMS outParams; PAHCI_SRB_EXTENSION srbExtension; PUCHAR buffer;//to make the pointer arithmatic easier UNREFERENCED_PARAMETER(ChannelExtension); buffer = (PUCHAR)SrbGetDataBuffer(Srb) + sizeof(SRB_IO_CONTROL); outParams = (PSENDCMDOUTPARAMS) buffer; //26015: "Potential overflow using expression 'outParams->DriverStatus.bDriverError' Buffer access is apparently unbounded by the buffer size. srbExtension = GetSrbExtension(Srb); Srb->SrbStatus &= ~SRB_STATUS_AUTOSENSE_VALID; // remove this flag as there is no data copy back to original Sense Buffer if (Srb->SrbStatus == SRB_STATUS_SUCCESS) { outParams->DriverStatus.bDriverError = 0; outParams->DriverStatus.bIDEError = 0; } else { // command failed outParams->DriverStatus.bDriverError = SMART_IDE_ERROR; outParams->DriverStatus.bIDEError = srbExtension->AtaStatus; } return; } __inline VOID BuildLocalCommand( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PATA_TASK_FILE TaskFile, _In_opt_ PSRB_COMPLETION_ROUTINE CompletionRountine ) /*++ It assumes: nothing Called by: IssuePreservedSettingCommands It performs: 1 Fills in the local SRB with the ATA command Affected Variables/Registers: none --*/ { PSCSI_REQUEST_BLOCK srb; PAHCI_SRB_EXTENSION srbExtension; // // Local Srb still uses SCSI_REQUEST_BLOCK type. // do not touch field "srb->NextSrb". It should be only touched in queue related operations. // srb = &ChannelExtension->Local.Srb; srb->SrbStatus = SRB_STATUS_PENDING; srb->SrbExtension = (PVOID)ChannelExtension->Local.SrbExtension; srb->TimeOutValue = 1; //as it's sent by miniport, no one monitors the timeout value. // Fills in the local SRB with the SetFeatures command srbExtension = ChannelExtension->Local.SrbExtension; AhciZeroMemory((PCHAR)srbExtension, sizeof(AHCI_SRB_EXTENSION)); srbExtension->AtaFunction = ATA_FUNCTION_ATA_COMMAND; srbExtension->CompletionRoutine = CompletionRountine; //setup TaskFile StorPortCopyMemory(&srbExtension->TaskFile, TaskFile, sizeof(ATA_TASK_FILE)); return; } VOID IssuePreservedSettingCommands( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_opt_ PSTORAGE_REQUEST_BLOCK Srb ) /*++ Uses the local SRB to send down the next Preserved Setting It assumes: Local SRB is only used for restoring preserved settings Called by: RestorePreservedSettings, IssueInitCommands, Itself indirectly through local SRB callback It performs: 1 Verify local SRB is not in use 2 Find the next Preserved Setting 3 Send it Affected Variables/Registers: none --*/ { UCHAR i; ULONG allocated; ATA_TASK_FILE taskFile = {0}; UNREFERENCED_PARAMETER(Srb); //1 Verify local SRB is not in use allocated = GetOccupiedSlots(ChannelExtension); if ((allocated & (1 << 0)) > 0) { // Already restoring preserved Settings return; } //2 find the next command to send for (i = 0; i < MAX_SETTINGS_PRESERVED; i++) { if ( (ChannelExtension->PersistentSettings.SlotsToSend & (1 << i)) > 0 ) { ChannelExtension->PersistentSettings.SlotsToSend &= ~(1 << i); break; } } //perhaps there is none. Done. if ( i >= MAX_SETTINGS_PRESERVED) { // release active reference for process of restore preserved settings if (ChannelExtension->StateFlags.RestorePreservedSettingsActiveReferenced == 1) { PortReleaseActiveReference(ChannelExtension, NULL); ChannelExtension->StateFlags.RestorePreservedSettingsActiveReferenced = 0; } InterlockedBitTestAndReset((LONG*)&ChannelExtension->StateFlags, 3); //ReservedSlotInUse field is at bit 3 return; } //3 Otherwise use the LocalSRB to send the command. When it is done, call this routine again taskFile.Current.bFeaturesReg = ChannelExtension->PersistentSettings.CommandParams[i].Features; taskFile.Current.bSectorCountReg = ChannelExtension->PersistentSettings.CommandParams[i].SectorCount; taskFile.Current.bDriveHeadReg = 0xA0; taskFile.Current.bCommandReg = IDE_COMMAND_SET_FEATURE; BuildLocalCommand(ChannelExtension, &taskFile, IssuePreservedSettingCommands); return; } VOID IssueInitCommands( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_opt_ PSTORAGE_REQUEST_BLOCK Srb ) /*++ Uses the local SRB to send down the next Init Command or Preserved Setting Command It assumes: Local SRB is only used for restoring preseved settings Called by: AhciIssueInitCommands, Itself indirectly through local SRB callback It performs: 1 Verify local SRB is not in use 2 Find the next Init Command or Preserved Setting Command 3 Send it Affected Variables/Registers: none --*/ { ULONG allocated; PATA_TASK_FILE taskFile; UNREFERENCED_PARAMETER(Srb); // Verify local SRB is not in use allocated = GetOccupiedSlots(ChannelExtension); if ((allocated & (1 << 0)) > 0) { // Already restoring preserved Settings return; } // if all Init commands have been sent, send Preserved Setting Commands if (ChannelExtension->DeviceInitCommands.CommandToSend >= ChannelExtension->DeviceInitCommands.ValidCommandCount) { ChannelExtension->PersistentSettings.SlotsToSend = ChannelExtension->PersistentSettings.Slots; IssuePreservedSettingCommands(ChannelExtension, NULL); return; } // find the next command to send taskFile = ChannelExtension->DeviceInitCommands.CommandTaskFile + ChannelExtension->DeviceInitCommands.CommandToSend; taskFile->Current.bDriveHeadReg = 0xA0; BuildLocalCommand(ChannelExtension, taskFile, IssueInitCommands); ChannelExtension->DeviceInitCommands.CommandToSend++; return; } VOID SetDateAndTimeCompletion( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb ) { PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); UNREFERENCED_PARAMETER(ChannelExtension); AhciZeroMemory((PCHAR)srbExtension, sizeof(AHCI_SRB_EXTENSION)); srbExtension->AtaFunction = ATA_FUNCTION_ATA_COMMAND; srbExtension->CompletionRoutine = NULL; SetCommandReg((&srbExtension->TaskFile.Current), IDE_COMMAND_STANDBY_IMMEDIATE); return; } VOID BuildSetDateAndTimeTaskFile( _In_ PATA_TASK_FILE TaskFile ) { LARGE_INTEGER temp; ULONGLONG now; AhciZeroMemory((PCHAR)TaskFile, sizeof(ATA_TASK_FILE)); //setup TaskFile StorPortQuerySystemTime(&temp); now = (ULONGLONG) temp.QuadPart; now /= 10000; //4 orders of magnatude // 2) subtract 369 years in seconds. // Number of milliseconds in a Julian year = 31,557,600,000 (1millisecond * 1000second * 60minute * 60hour * 24day * 365.25year) // 369 * 31,557,600,000 = 11,644,754,400,000 (0xA97 4173 1300) now -= 0xA9741731300; // Example 2010-09-29 10 am = 0x1cb5ffd`22c1bf5e // 0x1cb5ffd`22c1bf5e/10000 = 0xbc2`8f496393 (12930255512467 or 12930255512467.8494) milliseconds // 0xbc2`8f496393 - 0xa97'41731300 = 0x012b`4dd65093 // NOTE: this number won't roll over for another ~8700 years. TaskFile->Current.bSectorNumberReg = (UCHAR) (0xFF & now); now >>= 8; TaskFile->Current.bCylLowReg = (UCHAR) (0xFF & now); now >>= 8; TaskFile->Current.bCylHighReg = (UCHAR) (0xFF & now); now >>= 8; TaskFile->Previous.bSectorNumberReg = (UCHAR) (0xFF & now); now >>= 8; TaskFile->Previous.bCylLowReg = (UCHAR) (0xFF & now); now >>= 8; TaskFile->Previous.bCylHighReg = (UCHAR) (0xFF & now); TaskFile->Current.bDriveHeadReg = 0xA0; TaskFile->Current.bCommandReg = IDE_COMMAND_SET_DATE_AND_TIME; return; } VOID IssueSetDateAndTimeCommand( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _Inout_ PSCSI_REQUEST_BLOCK Srb, _In_ BOOLEAN SendStandBy ) /*++ It assumes: Srb is not the local SRB. Called by: AhciHwStartIo with SRB_FUNCTION_SHUTDOWN It performs: 1 Builds a Set Date & Time taskfile and associates it with the provided Srb. Affected Variables/Registers: none --*/ { PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension((PSTORAGE_REQUEST_BLOCK)Srb); NT_ASSERT(Srb != &ChannelExtension->Local.Srb); UNREFERENCED_PARAMETER(ChannelExtension); //setup TaskFile BuildSetDateAndTimeTaskFile(&srbExtension->TaskFile); srbExtension->AtaFunction = ATA_FUNCTION_ATA_COMMAND; srbExtension->CompletionRoutine = (SendStandBy ? SetDateAndTimeCompletion : NULL); } BOOLEAN AhciDeviceInitialize ( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension ) { STOR_LOCK_HANDLE lockhandle = {0}; RecordExecutionHistory(ChannelExtension, 0x00000007); //AhciDeviceInitialize //1 update preserved commands per device needs. if (IsAtaDevice(&ChannelExtension->DeviceExtension->DeviceParameters)) { if (NeedToSetTransferMode(ChannelExtension)) { //1.1 Set DMA mode to this device UpdateSetFeatureCommands(ChannelExtension, IDE_FEATURE_INVALID, IDE_FEATURE_SET_TRANSFER_MODE, 0, 0x44); } //1.2 Persist Write Cache UpdateSetFeatureCommands(ChannelExtension, IDE_FEATURE_INVALID, IDE_FEATURE_ENABLE_WRITE_CACHE, 0, 0); } else if (IsAtapiDevice(&ChannelExtension->DeviceExtension->DeviceParameters)) { //2.1 Persist SATA transfer mode for some SATAI/PATAPI bridge chips UpdateSetFeatureCommands(ChannelExtension, IDE_FEATURE_INVALID, IDE_FEATURE_SET_TRANSFER_MODE, 0, 0x42); if ( IsDeviceSupportsAN(ChannelExtension->DeviceExtension->IdentifyPacketData) && !IsDeviceEnabledAN(ChannelExtension->DeviceExtension->IdentifyPacketData) ) { //2.2 Enable Asynchronous Notification if supported UpdateSetFeatureCommands(ChannelExtension, IDE_FEATURE_INVALID, IDE_FEATURE_ENABLE_SATA_FEATURE, 0, IDE_SATA_FEATURE_ASYNCHRONOUS_NOTIFICATION); } } //3.1 evaluate ACPI _SDD method informing information about device connected. AhciPortEvaluateSDDMethod(ChannelExtension); //3.2 retrieve _GTF commands and add needed commands in list. AhciPortGetInitCommands(ChannelExtension); //5.1 Configure device with init commands and persistent configuration commands AhciPortIssueInitCommands(ChannelExtension); StorPortAcquireSpinLock(ChannelExtension->AdapterExtension, InterruptLock, NULL, &lockhandle); ActivateQueue(ChannelExtension, TRUE); StorPortReleaseSpinLock(ChannelExtension->AdapterExtension, &lockhandle); RecordExecutionHistory(ChannelExtension, 0x10000007);//Exit AhciDeviceInitialize return TRUE; } VOID AhciDeviceStart ( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension ) /* Running at PASSIVE_LEVEL This function is called when IRP_MN_START_DEVICE is being processed. device registry access, ACPI calls can be processed in this function. */ { if (ChannelExtension == NULL) { return; } AhciDeviceInitialize(ChannelExtension); return; } __inline BOOLEAN IsLpmModeSetting( _In_ PSTOR_POWER_SETTING_INFO PowerInfo ) { if (PowerInfo->PowerSettingGuid.Data1 == 0x0b2d69d7) { if (PowerInfo->PowerSettingGuid.Data2 == 0xa2a1){ if (PowerInfo->PowerSettingGuid.Data3 == 0x449c){ if (PowerInfo->PowerSettingGuid.Data4[0] == 0x96){ if (PowerInfo->PowerSettingGuid.Data4[1] == 0x80){ if (PowerInfo->PowerSettingGuid.Data4[2] == 0xf9){ if (PowerInfo->PowerSettingGuid.Data4[3] == 0x1c){ if (PowerInfo->PowerSettingGuid.Data4[4] == 0x70){ if (PowerInfo->PowerSettingGuid.Data4[5] == 0x52){ if (PowerInfo->PowerSettingGuid.Data4[6] == 0x1c){ if (PowerInfo->PowerSettingGuid.Data4[7] == 0x60){ return TRUE; } } } } } } } } } } } return FALSE; } __inline BOOLEAN IsLpmAdaptiveSetting( _In_ PSTOR_POWER_SETTING_INFO PowerInfo ) { if (PowerInfo->PowerSettingGuid.Data1 == 0xDAB60367) { if (PowerInfo->PowerSettingGuid.Data2 == 0x53FE){ if (PowerInfo->PowerSettingGuid.Data3 == 0x4fbc){ if (PowerInfo->PowerSettingGuid.Data4[0] == 0x82){ if (PowerInfo->PowerSettingGuid.Data4[1] == 0x5E){ if (PowerInfo->PowerSettingGuid.Data4[2] == 0x52){ if (PowerInfo->PowerSettingGuid.Data4[3] == 0x1D){ if (PowerInfo->PowerSettingGuid.Data4[4] == 0x06){ if (PowerInfo->PowerSettingGuid.Data4[5] == 0x9D){ if (PowerInfo->PowerSettingGuid.Data4[6] == 0x24){ if (PowerInfo->PowerSettingGuid.Data4[7] == 0x56){ return TRUE; } } } } } } } } } } } return FALSE; } UCHAR SetAllowedLpmStates( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension ) // Return Value: disabled modes; { UCHAR lpm; AHCI_SERIAL_ATA_CONTROL sctl; // 0h No interface restrictions // 1h Transitions to the Partial state disabled // 2h Transitions to the Slumber state disabled // 3h Transitions to both Partial and Slumber states disabled // disable LPM for eSATA port as hot-plug cannot be detected in partial or slumber state. if ((ChannelExtension->LastUserLpmPowerSetting == 0) || !IsLPMCapablePort(ChannelExtension)) { lpm = 0x03; // slumber and partial disallowed } else { AHCI_COMMAND cmd; cmd.AsUlong = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CMD.AsUlong); if ((ChannelExtension->AutoPartialToSlumberInterval == 0) && // No software auto partial to slumber ( (ChannelExtension->AdapterExtension->CAP2.APST == 0) || // Host auto Partial to Slumber is not supported. (cmd.APSTE == 0) ) ) { // Host auto Partial to Slumber is not enabled. lpm = 0x02; // partial allowed; slumber disallowed } else { lpm = 0x00; // partial allowed; slumber allowed } } if (ChannelExtension->AdapterExtension->CAP.SSC == 0) { // disable Slumber if controller does not support it. lpm |= 0x02; } if (ChannelExtension->AdapterExtension->CAP.PSC == 0) { // storahci LPM is to put device into partial, then transit into slumber according to defined interval value. // do not enable LPM if partial is not supported. // the case of device supporting slumber but not partial is very rare. lpm = 0x03; } //Set PxSCTL.IPM to 3h to restrict slumber and partial interface power management state transitions. sctl.AsUlong = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SCTL.AsUlong); sctl.IPM = lpm; StorPortWriteRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SCTL.AsUlong, sctl.AsUlong); return lpm; } BOOLEAN AhciLpmSettingsModes( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ AHCI_LPM_POWER_SETTINGS LpmMode ) /* NOTE: this routine may prepared command in Local.Srb. Caller of this routine should try to start IO process. Return Value: TRUE: the caller should start IO process */ { AHCI_COMMAND cmd; BOOLEAN needStartIo = FALSE; //Make sure the configuration supports LPM, otherwise, don't touch anything. if (NoLpmSupport(ChannelExtension) || !IsLPMCapablePort(ChannelExtension)) { return needStartIo; } ChannelExtension->LastUserLpmPowerSetting = (UCHAR)LpmMode.AsUlong; if (LpmMode.AsUlong == 0) { // Active Mode. //Turn LPM off as Active is chosen if (ChannelExtension->AdapterExtension->CAP.SALP == 1) { cmd.AsUlong = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CMD.AsUlong); if (cmd.ALPE != 0) { cmd.ALPE = 0; cmd.ASP = 0; StorPortWriteRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CMD.AsUlong, cmd.AsUlong); } } //Set PxSCTL.IPM to 3h to restrict slumber and partial interface power management state transitions. SetAllowedLpmStates(ChannelExtension); // Disable DIPM if (IsDeviceSupportsDIPM(ChannelExtension->DeviceExtension[0].IdentifyDeviceData)) { //The enable/disable state for device initiated power management shall persist across software reset. //The enable/disable state shall be reset to its default disabled state upon COMRESET. UpdateSetFeatureCommands(ChannelExtension, IDE_FEATURE_ENABLE_SATA_FEATURE, IDE_FEATURE_DISABLE_SATA_FEATURE, IDE_SATA_FEATURE_DEVICE_INITIATED_POWER_MANAGEMENT, IDE_SATA_FEATURE_DEVICE_INITIATED_POWER_MANAGEMENT); //Configure device with persistent configuration commands RestorePreservedSettings(ChannelExtension, FALSE); needStartIo = TRUE; } } else { // link power management is allowed. //Set PxSCTL.IPM for LPM allowed states. UCHAR sctlIpm = SetAllowedLpmStates(ChannelExtension); // Setting HIPM if it's enabled. if ( (LpmMode.HipmEnabled > 0) && (sctlIpm != 0x03) && (ChannelExtension->AdapterExtension->CAP.SALP == 1) && IsDeviceSupportsHIPM(ChannelExtension->DeviceExtension[0].IdentifyDeviceData) ) { // If Partial is capable and device supports HIPM. // Turn on LPM and set it for Partial cmd.AsUlong = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CMD.AsUlong); cmd.ALPE = 1; cmd.ASP = 0; //0 = partial, 1 = slumber StorPortWriteRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CMD.AsUlong, cmd.AsUlong); } else if (ChannelExtension->AdapterExtension->CAP.SALP == 1) { //Turn off HIPM if it's supported cmd.AsUlong = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CMD.AsUlong); if (cmd.ALPE != 0) { cmd.ALPE = 0; cmd.ASP = 0; StorPortWriteRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CMD.AsUlong, cmd.AsUlong); } } // Setting DIPM if it's enabled. if ((LpmMode.DipmEnabled > 0) && (sctlIpm != 0x03)) { // Enable DIPM feature. if (IsDeviceSupportsDIPM(ChannelExtension->DeviceExtension[0].IdentifyDeviceData)) { //The enable/disable state for device initiated power management shall persist across software reset. //The enable/disable state shall be reset to its default disabled state upon COMRESET. UpdateSetFeatureCommands(ChannelExtension, IDE_FEATURE_DISABLE_SATA_FEATURE, IDE_FEATURE_ENABLE_SATA_FEATURE, IDE_SATA_FEATURE_DEVICE_INITIATED_POWER_MANAGEMENT, IDE_SATA_FEATURE_DEVICE_INITIATED_POWER_MANAGEMENT); //Configure device with persistent configuration commands RestorePreservedSettings(ChannelExtension, FALSE); needStartIo = TRUE; } } else { // Disable DIPM feature. if (IsDeviceSupportsDIPM(ChannelExtension->DeviceExtension[0].IdentifyDeviceData)) { //The enable/disable state for device initiated power management shall persist across software reset. //The enable/disable state shall be reset to its default disabled state upon COMRESET. UpdateSetFeatureCommands(ChannelExtension, IDE_FEATURE_ENABLE_SATA_FEATURE, IDE_FEATURE_DISABLE_SATA_FEATURE, IDE_SATA_FEATURE_DEVICE_INITIATED_POWER_MANAGEMENT, IDE_SATA_FEATURE_DEVICE_INITIATED_POWER_MANAGEMENT); //Configure device with persistent configuration commands RestorePreservedSettings(ChannelExtension, FALSE); needStartIo = TRUE; } } } return needStartIo; } BOOLEAN AhciPortPowerSettingNotification( IN PAHCI_CHANNEL_EXTENSION ChannelExtension, IN PSTOR_POWER_SETTING_INFO PowerInfo ) { // do nothing if there is no device connected if ( (ChannelExtension->StartState.ChannelNextStartState == StartFailed) || (ChannelExtension->DeviceExtension->DeviceParameters.AtaDeviceType == DeviceNotExist) ) { return FALSE; } //Make sure the configuration supports LPM, otherwise, don't touch anything. if (NoLpmSupport(ChannelExtension) || !IsLPMCapablePort(ChannelExtension)) { return FALSE; } // Validate input LPM data from the Power Manager if (PowerInfo->ValueLength != sizeof(ULONG)) { return FALSE; } if (!IsLpmModeSetting(PowerInfo) && !IsLpmAdaptiveSetting(PowerInfo)) { // invalid power policy. return FALSE; } if (IsLpmAdaptiveSetting(PowerInfo)) { // max allowed value: 5 minutes (in ms) ULONG interval = (ULONG)*((PULONG)PowerInfo->Value); if (interval <= 300000) { ChannelExtension->AutoPartialToSlumberInterval = interval; //Set PxSCTL.IPM register for LPM allowed states. SetAllowedLpmStates(ChannelExtension); } } else if (IsLpmModeSetting(PowerInfo)) { BOOLEAN needRestartIo; AHCI_LPM_POWER_SETTINGS userLpmPowerSettings; userLpmPowerSettings.AsUlong = (ULONG)*((PULONG)PowerInfo->Value); needRestartIo = AhciLpmSettingsModes(ChannelExtension, userLpmPowerSettings); if (needRestartIo) { STOR_LOCK_HANDLE lockhandle = {0}; StorPortAcquireSpinLock(ChannelExtension->AdapterExtension, InterruptLock, NULL, &lockhandle); ActivateQueue(ChannelExtension, TRUE); StorPortReleaseSpinLock(ChannelExtension->AdapterExtension, &lockhandle); } } return TRUE; } VOID AhciAutoPartialToSlumber( _In_ PVOID AdapterExtension, _In_opt_ PVOID ChannelExtension ) /* NOTE: input parameter - Context is required as this is a callback function. But it's not used by this function. */ { PAHCI_CHANNEL_EXTENSION channelExtension = (PAHCI_CHANNEL_EXTENSION)ChannelExtension; AHCI_SERIAL_ATA_STATUS ssts; AHCI_COMMAND cmd; ULONG ci; ULONG sact; if (channelExtension == NULL) { NT_ASSERT(FALSE); return; } if (channelExtension->Px == NULL) { // The port has been stopped. Do not touch its registers. // There is no need to transit the link power state to Slumber state. // // Note: // Px is set to NULL in AhciPortStop function. StartIo spin lock is utilized to // prevent race condition with AhciPortStop function. StartIo spin lock is acquired // before AhciPortStop is called. When we are here in AhciAutoPartialToSlumber, because // it is a timer callback function, StartIo spin lock is already held - Storport holds // StartIo spin lock before invoking miniport timer callback function. // return; } NT_ASSERT(AdapterExtension == (PVOID)(channelExtension->AdapterExtension)); UNREFERENCED_PARAMETER(AdapterExtension); // 1.1 check the Link Power State should be enabled. cmd.AsUlong = StorPortReadRegisterUlong(channelExtension->AdapterExtension, &channelExtension->Px->CMD.AsUlong); ci = StorPortReadRegisterUlong(AdapterExtension, &channelExtension->Px->CI); sact = StorPortReadRegisterUlong(AdapterExtension, &channelExtension->Px->SACT); if (!PartialToSlumberTransitionIsAllowed(channelExtension, cmd, ci, sact)) { // validate again in case any condition changed that not allowing StorAHCI to perform Partial to Slumber transition. StorPortDebugPrint(3, "StorAHCI - LPM: Port %02d - Transit into Slumber from Partial - bailed out, request outstanding: CI: 0x%08X, SACT: 0x%08X \n", channelExtension->PortNumber, ci, sact); return; } ssts.AsUlong = StorPortReadRegisterUlong(channelExtension->AdapterExtension, &channelExtension->Px->SSTS.AsUlong); // 1.3 check the Link Power State, should be Partial (value 2). if (ssts.IPM != 2) { StorPortDebugPrint(3, "StorAHCI - LPM: Port %02d - Transit into Slumber from Partial - bailed out, current link state is not Partial: %1x \n", channelExtension->PortNumber, ssts.IPM); return; } // 2. Change LPM State. // Link should be in idle state (able to accept new interface commands). if (cmd.ICC == 0) { ULONG waitTime; ULONG waitTimeLimit = AHCI_LINK_POWER_STATE_CHANGE_TIMEOUT_US; UCHAR iccAttempts = 0; AhciUlongIncrement(&(channelExtension->AutoPartialToSlumberDbgStats.InterfaceReady)); // // Attempt to transition the link to Active. // By spec, Partial to Active transition should be completed in 10us. Reading register already takes sometime. // Poll for a little bit to give the link some time to go Active. // cmd.ICC = 1; StorPortWriteRegisterUlong(channelExtension->AdapterExtension, &channelExtension->Px->CMD.AsUlong, cmd.AsUlong); ssts.AsUlong = StorPortReadRegisterUlong(channelExtension->AdapterExtension, &channelExtension->Px->SSTS.AsUlong); for (waitTime = 0; (waitTime < waitTimeLimit) && (ssts.IPM != 1); waitTime += 10) { // // Make a few attempts to program ICC if we haven't transitioned yet. // if (iccAttempts++ < 3) { cmd.ICC = 1; StorPortWriteRegisterUlong(channelExtension->AdapterExtension, &channelExtension->Px->CMD.AsUlong, cmd.AsUlong); } StorPortStallExecution(10); //10 microseconds ssts.AsUlong = StorPortReadRegisterUlong(channelExtension->AdapterExtension, &channelExtension->Px->SSTS.AsUlong); } if (ssts.IPM != 1) { AhciUlongIncrement(&(channelExtension->AutoPartialToSlumberDbgStats.ActiveFailCount)); StorPortDebugPrint(3, "StorAHCI - LPM: Port %02d - Transit into Slumber from Partial - Failed to go to Active, SSTS.IPM = %u \n", channelExtension->PortNumber, ssts.IPM); return; } AhciUlongIncrement(&(channelExtension->AutoPartialToSlumberDbgStats.ActiveSuccessCount)); // // Attempt to transition the link to Slumber. // Poll for a little bit to give the link some time to go Slumber. // iccAttempts = 0; cmd.ICC = 6; StorPortWriteRegisterUlong(channelExtension->AdapterExtension, &channelExtension->Px->CMD.AsUlong, cmd.AsUlong); ssts.AsUlong = StorPortReadRegisterUlong(channelExtension->AdapterExtension, &channelExtension->Px->SSTS.AsUlong); for (waitTime = 0; (waitTime < waitTimeLimit) && (ssts.IPM != 6); waitTime += 10) { // // Make a few attempts to program ICC if we haven't transitioned yet. // if (iccAttempts++ < 3) { cmd.ICC = 6; StorPortWriteRegisterUlong(channelExtension->AdapterExtension, &channelExtension->Px->CMD.AsUlong, cmd.AsUlong); } StorPortStallExecution(10); //10 microseconds ssts.AsUlong = StorPortReadRegisterUlong(channelExtension->AdapterExtension, &channelExtension->Px->SSTS.AsUlong); } if (ssts.IPM == 6) { AhciUlongIncrement(&(channelExtension->AutoPartialToSlumberDbgStats.SlumberSuccessCount)); StorPortDebugPrint(3, "StorAHCI - LPM: Port %02d - Transit into Slumber from Partial - Succeeded \n", channelExtension->PortNumber); } else { AhciUlongIncrement(&(channelExtension->AutoPartialToSlumberDbgStats.SlumberFailCount)); StorPortDebugPrint(3, "StorAHCI - LPM: Port %02d - Transit into Slumber from Partial - Failed, SSTS.IPM = %u \n", channelExtension->PortNumber, ssts.IPM); } } else { AhciUlongIncrement(&(channelExtension->AutoPartialToSlumberDbgStats.InterfaceNotReady)); } return; } BOOLEAN AhciAdapterPowerSettingNotification( _In_ PAHCI_ADAPTER_EXTENSION AdapterExtension, _In_ PSTOR_POWER_SETTING_INFO PowerSettingInfo ) { ULONG i; for (i = 0; i <= AdapterExtension->HighestPort; i++) { if (AdapterExtension->PortExtension[i] != NULL) { AhciPortPowerSettingNotification(AdapterExtension->PortExtension[i], PowerSettingInfo); } } return TRUE; } VOID AhciPortGetInitCommands( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension ) { // Read _GTF from ACPI ULONG status = STOR_STATUS_SUCCESS; ACPI_EVAL_INPUT_BUFFER inputData = {0}; PACPI_EVAL_OUTPUT_BUFFER acpiData = NULL; PACPI_METHOD_ARGUMENT argument = NULL; ULONG acpiDataSize = 256; // initial size, should be good enough for most cases ULONG returnedLength = 0; UCHAR gtfCommandCount = 0; // send SECURE_FREEZE_LOCK by default BOOLEAN sendSecureFreezeLock = TRUE; // clear Init Commands area. need to do this for device removed previously (StorAHCI only knows about adapter removal, not device removal) ChannelExtension->DeviceInitCommands.CommandCount = 0; ChannelExtension->DeviceInitCommands.ValidCommandCount = 0; ChannelExtension->DeviceInitCommands.CommandToSend = 0; if (ChannelExtension->DeviceInitCommands.CommandTaskFile != NULL) { StorPortFreePool(ChannelExtension->AdapterExtension, (PVOID)ChannelExtension->DeviceInitCommands.CommandTaskFile); ChannelExtension->DeviceInitCommands.CommandTaskFile = NULL; } inputData.Signature = ACPI_EVAL_INPUT_BUFFER_SIGNATURE; inputData.MethodNameAsUlong = ACPI_METHOD_GTF; status = StorPortAllocatePool(ChannelExtension->AdapterExtension, acpiDataSize, AHCI_POOL_TAG, (PVOID*)&acpiData); if (acpiData != NULL) { // call API to get required buffer size status = StorPortInvokeAcpiMethod(ChannelExtension->AdapterExtension, (PSTOR_ADDRESS)&ChannelExtension->DeviceExtension[0].DeviceAddress, ACPI_METHOD_GTF, &inputData, sizeof(ACPI_EVAL_INPUT_BUFFER), (PVOID)acpiData, acpiDataSize, &returnedLength ); // in case of the alloate buffer is too small, re-allocate buffer and retry the call if ( (status == STOR_STATUS_BUFFER_TOO_SMALL) && (acpiData->Length > acpiDataSize) ) { acpiDataSize = acpiData->Length; StorPortFreePool(ChannelExtension->AdapterExtension, (PVOID)acpiData); acpiData = NULL; // re-allocate a bigger buffer status = StorPortAllocatePool(ChannelExtension->AdapterExtension, acpiDataSize, AHCI_POOL_TAG, (PVOID*)&acpiData); if (acpiData != NULL) { status = StorPortInvokeAcpiMethod(ChannelExtension->AdapterExtension, (PSTOR_ADDRESS)&ChannelExtension->DeviceExtension[0].DeviceAddress, ACPI_METHOD_GTF, &inputData, sizeof(ACPI_EVAL_INPUT_BUFFER), (PVOID)acpiData, acpiDataSize, &returnedLength ); } } } // get _GTF commands count if ( (status == STOR_STATUS_SUCCESS) && (acpiData != NULL) && (acpiData->Signature == ACPI_EVAL_OUTPUT_BUFFER_SIGNATURE) && (acpiData->Count == 1) ) { argument = acpiData->Argument; if (argument->Type == ACPI_METHOD_ARGUMENT_BUFFER) { NT_ASSERT ((argument->DataLength % sizeof(ACPI_GTF_IDE_REGISTERS)) == 0); gtfCommandCount = (UCHAR)(argument->DataLength / sizeof(ACPI_GTF_IDE_REGISTERS)); } else { NT_ASSERT(argument->Type == ACPI_METHOD_ARGUMENT_BUFFER); } } // Get Init Command count for devices // // calculate possible command count for memory allocation // an IDE_FEATURE_DISABLE_REVERT_TO_POWER_ON command will be sent to device anyway. // ChannelExtension->DeviceInitCommands.CommandCount = gtfCommandCount + 1; if (IsAtaDevice(&ChannelExtension->DeviceExtension[0].DeviceParameters)) { if (ChannelExtension->DeviceExtension->SupportedCommands.SetDateAndTime == 0x1) { ChannelExtension->DeviceInitCommands.CommandCount++; } if (sendSecureFreezeLock) { ChannelExtension->DeviceInitCommands.CommandCount++; } } // copy _GTF commands into buffer if (ChannelExtension->DeviceInitCommands.CommandCount > 0) { PATA_TASK_FILE taskFile; ULONG i; status = StorPortAllocatePool(ChannelExtension->AdapterExtension, ChannelExtension->DeviceInitCommands.CommandCount * sizeof(ATA_TASK_FILE), AHCI_POOL_TAG, (PVOID*)&ChannelExtension->DeviceInitCommands.CommandTaskFile); if ( (status != STOR_STATUS_SUCCESS) || (ChannelExtension->DeviceInitCommands.CommandTaskFile == NULL) ) { goto exit; } AhciZeroMemory((PCHAR)ChannelExtension->DeviceInitCommands.CommandTaskFile, ChannelExtension->DeviceInitCommands.CommandCount * sizeof(ATA_TASK_FILE)); for (i = 0; i < gtfCommandCount; i++) { StorPortCopyMemory(ChannelExtension->DeviceInitCommands.CommandTaskFile + i, argument->Data + (i * sizeof(ACPI_GTF_IDE_REGISTERS)), sizeof(ACPI_GTF_IDE_REGISTERS) ); } // add IDE_FEATURE_DISABLE_REVERT_TO_POWER_ON for all devices if ((ChannelExtension->DeviceInitCommands.CommandCount - i) >= 1) { taskFile = ChannelExtension->DeviceInitCommands.CommandTaskFile + i; taskFile->Current.bFeaturesReg = IDE_FEATURE_DISABLE_REVERT_TO_POWER_ON; taskFile->Current.bCommandReg = IDE_COMMAND_SET_FEATURE; i++; } // add more commands for ATA devices if (IsAtaDevice(&ChannelExtension->DeviceExtension[0].DeviceParameters)) { if (sendSecureFreezeLock) { if ((ChannelExtension->DeviceInitCommands.CommandCount - i) >= 1) { taskFile = ChannelExtension->DeviceInitCommands.CommandTaskFile + i; taskFile->Current.bCommandReg = IDE_COMMAND_SECURITY_FREEZE_LOCK; i++; } } if ((ChannelExtension->DeviceInitCommands.CommandCount - i) >= 1) { if (ChannelExtension->DeviceExtension->SupportedCommands.SetDateAndTime == 0x1) { taskFile = ChannelExtension->DeviceInitCommands.CommandTaskFile + i; //setup TaskFile BuildSetDateAndTimeTaskFile(taskFile); i++; } } } ChannelExtension->DeviceInitCommands.ValidCommandCount = (UCHAR)i; } exit: if (acpiData != NULL) { StorPortFreePool(ChannelExtension->AdapterExtension, (PVOID)acpiData); acpiData = NULL; } return; } VOID AhciPortEvaluateSDDMethod( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension ) { ULONG status = STOR_STATUS_SUCCESS; ULONG returnedLength = 0; PACPI_EVAL_INPUT_BUFFER_COMPLEX inputData; PACPI_METHOD_ARGUMENT argument; ULONG inputDataSize; // get the memory we need inputDataSize = sizeof(ACPI_EVAL_INPUT_BUFFER_COMPLEX) + sizeof(IDENTIFY_DEVICE_DATA); status = StorPortAllocatePool(ChannelExtension->AdapterExtension, inputDataSize, AHCI_POOL_TAG, (PVOID*)&inputData); if ( (status != STOR_STATUS_SUCCESS) || (inputData == NULL) ) { goto Exit; } AhciZeroMemory((PCHAR)inputData, inputDataSize); inputData->Signature = ACPI_EVAL_INPUT_BUFFER_COMPLEX_SIGNATURE; inputData->MethodNameAsUlong = ACPI_METHOD_SDD; inputData->Size = inputDataSize; inputData->ArgumentCount = 1; argument = inputData->Argument; argument->Type = ACPI_METHOD_ARGUMENT_BUFFER; argument->DataLength = sizeof(IDENTIFY_DEVICE_DATA); StorPortCopyMemory(argument->Data, ChannelExtension->DeviceExtension[0].IdentifyDeviceData, sizeof(IDENTIFY_DEVICE_DATA)); status = StorPortInvokeAcpiMethod(ChannelExtension->AdapterExtension, (PSTOR_ADDRESS)&ChannelExtension->DeviceExtension[0].DeviceAddress, ACPI_METHOD_SDD, (PVOID)inputData, inputDataSize, NULL, 0, &returnedLength ); Exit: // we don't care about the return status UNREFERENCED_PARAMETER(status); if (inputData != NULL) { StorPortFreePool(ChannelExtension->AdapterExtension, inputData); } return; } VOID AhciAdapterEvaluateDSMMethod( _In_ PAHCI_ADAPTER_EXTENSION AdapterExtension ) { ULONG status = STOR_STATUS_SUCCESS; ULONG returnedLength = 0; PACPI_METHOD_ARGUMENT argument; PACPI_EVAL_INPUT_BUFFER_COMPLEX inputData = NULL; ULONG inputDataSize; PACPI_EVAL_OUTPUT_BUFFER outputData = NULL; ULONG outputDataSize; // 0. get output buffer ready, make sure the buffer is big enough. outputDataSize = FIELD_OFFSET(ACPI_EVAL_OUTPUT_BUFFER, Argument) + AHCI_MAX_PORT_COUNT * ACPI_METHOD_ARGUMENT_LENGTH(sizeof(ULONG)); status = StorPortAllocatePool(AdapterExtension, outputDataSize, AHCI_POOL_TAG, (PVOID*)&outputData); if ( (status != STOR_STATUS_SUCCESS) || (outputData == NULL) ) { goto Exit; } AhciZeroMemory((PCHAR)outputData, outputDataSize); // 1. check if Link Power Management is supported in ACPI // get the memory we need // N.B. 4 arguments are stored in the ACPI_EVAL_INPUT_BUFFER_COMPLEX // and passed to acpi.sys to eval _DSM. // // ACPI_EVAL_INPUT_BUFFER_COMPLEX with 4 arguments. // 0 - GUID // 1 - ULONG (revison id) // 2 - ULONG (function index) // 3 - unused (package) // inputDataSize = FIELD_OFFSET(ACPI_EVAL_INPUT_BUFFER_COMPLEX, Argument) + ACPI_METHOD_ARGUMENT_LENGTH(sizeof(GUID)) + ACPI_METHOD_ARGUMENT_LENGTH(sizeof(ULONG)) + ACPI_METHOD_ARGUMENT_LENGTH(sizeof(ULONG)) + ACPI_METHOD_ARGUMENT_LENGTH(sizeof(ULONG)); status = StorPortAllocatePool(AdapterExtension, inputDataSize, AHCI_POOL_TAG, (PVOID*)&inputData); if ( (status != STOR_STATUS_SUCCESS) || (inputData == NULL) ) { goto Exit; } AhciZeroMemory((PCHAR)inputData, inputDataSize); inputData->Signature = ACPI_EVAL_INPUT_BUFFER_COMPLEX_SIGNATURE; inputData->MethodNameAsUlong = ACPI_METHOD_DSM; inputData->Size = inputDataSize; inputData->ArgumentCount = 4; // argument 0 - Interface GUID argument = &inputData->Argument[0]; argument->Type = ACPI_METHOD_ARGUMENT_BUFFER; argument->DataLength = sizeof(GUID); StorPortCopyMemory(&argument->Data[0], &LINK_POWER_ACPI_DSM_GUID, sizeof(GUID)); // argument 1 - Revision number argument = ACPI_METHOD_NEXT_ARGUMENT(argument); ACPI_METHOD_SET_ARGUMENT_INTEGER(argument, ACPI_METHOD_DSM_LINKPOWER_REVISION); // argument 2 - Function Index argument = ACPI_METHOD_NEXT_ARGUMENT(argument); ACPI_METHOD_SET_ARGUMENT_INTEGER(argument, ACPI_METHOD_DSM_LINKPOWER_FUNCTION_SUPPORT); // argument 3 - Function-dependent package. not used for ACPI_METHOD_DSM_LINKPOWER_FUNCTION_SUPPORT argument = ACPI_METHOD_NEXT_ARGUMENT(argument); ACPI_METHOD_SET_ARGUMENT_INTEGER(argument, 0); argument->Type = ACPI_METHOD_ARGUMENT_PACKAGE; status = StorPortInvokeAcpiMethod(AdapterExtension, NULL, // NULL for Address field means the request is for Adapter ACPI_METHOD_DSM, (PVOID)inputData, inputDataSize, outputData, outputDataSize, &returnedLength ); if ( (status != STOR_STATUS_SUCCESS) || (returnedLength < (FIELD_OFFSET(ACPI_EVAL_OUTPUT_BUFFER, Argument) + ACPI_METHOD_ARGUMENT_LENGTH(sizeof(ULONG)))) ) { goto Exit; } argument = outputData->Argument; if ( ((argument->Argument & ACPI_METHOD_DSM_LINKPOWER_FUNCTION_QUERY) != 0) && ((argument->Argument & ACPI_METHOD_DSM_LINKPOWER_FUNCTION_CONTROL) != 0) ) { // If ACPI_METHOD_DSM_LINKPOWER_FUNCTION_CONTROL is supported, it supports value (-1) that apply action to all ports. // Mark adapter supports _DSM. This indicates that it has capability to power on all ports and connected devices. if (AdapterExtension->StateFlags.SupportsAcpiDSM != TRUE) { AdapterExtension->StateFlags.SupportsAcpiDSM = TRUE; } } Exit: if (inputData != NULL) { StorPortFreePool(AdapterExtension, inputData); } if (outputData != NULL) { StorPortFreePool(AdapterExtension, outputData); } return; } VOID AhciPortAcpiDSMControl( _In_ PAHCI_ADAPTER_EXTENSION AdapterExtension, _In_ ULONG PortNumber, _In_ BOOLEAN Sleep ) { ULONG status = STOR_STATUS_SUCCESS; ULONG returnedLength = 0; PACPI_EVAL_INPUT_BUFFER_COMPLEX inputData; PACPI_METHOD_ARGUMENT argument; ULONG inputDataSize; // get the memory we need inputDataSize = FIELD_OFFSET(ACPI_EVAL_INPUT_BUFFER_COMPLEX, Argument) + ACPI_METHOD_ARGUMENT_LENGTH(sizeof(GUID)) + ACPI_METHOD_ARGUMENT_LENGTH(sizeof(ULONG)) + ACPI_METHOD_ARGUMENT_LENGTH(sizeof(ULONG)) + FIELD_OFFSET(ACPI_METHOD_ARGUMENT, Argument) + 2* ACPI_METHOD_ARGUMENT_LENGTH(sizeof(ULONG)); status = StorPortAllocatePool(AdapterExtension, inputDataSize, AHCI_POOL_TAG, (PVOID*)&inputData); if ( (status != STOR_STATUS_SUCCESS) || (inputData == NULL) ) { goto Exit; } AhciZeroMemory((PCHAR)inputData, inputDataSize); inputData->Signature = ACPI_EVAL_INPUT_BUFFER_COMPLEX_SIGNATURE; inputData->MethodNameAsUlong = ACPI_METHOD_DSM; inputData->Size = inputDataSize; inputData->ArgumentCount = 4; // argument 0 - Interface GUID argument = &inputData->Argument[0]; argument->Type = ACPI_METHOD_ARGUMENT_BUFFER; argument->DataLength = sizeof(GUID); StorPortCopyMemory(&argument->Data[0], &LINK_POWER_ACPI_DSM_GUID, sizeof(GUID)); // argument 1 - Revision number argument = ACPI_METHOD_NEXT_ARGUMENT(argument); ACPI_METHOD_SET_ARGUMENT_INTEGER(argument, ACPI_METHOD_DSM_LINKPOWER_REVISION); // argument 2 - Function Index argument = ACPI_METHOD_NEXT_ARGUMENT(argument); ACPI_METHOD_SET_ARGUMENT_INTEGER(argument, ACPI_METHOD_DSM_LINKPOWER_FUNCTION_CONTROL); // argument 3 - Function-dependent package. argument = ACPI_METHOD_NEXT_ARGUMENT(argument); argument->Type = ACPI_METHOD_ARGUMENT_PACKAGE_EX; argument->DataLength = 2 * ACPI_METHOD_ARGUMENT_LENGTH(sizeof(ULONG)); // argument 3 - Package entry 0 argument = (PACPI_METHOD_ARGUMENT)argument->Data; if (PortNumber == (ULONG)-1) { // all 1s indicates power on operation is for all ports/devices NT_ASSERT(Sleep == FALSE); ACPI_METHOD_SET_ARGUMENT_INTEGER(argument, PortNumber); } else { // convert PortNumber to be ACPI format of Address ACPI_METHOD_SET_ARGUMENT_INTEGER(argument, (PortNumber << 16) | 0xFFFF); } // argument 3 - Package entry 1 argument = ACPI_METHOD_NEXT_ARGUMENT(argument); ACPI_METHOD_SET_ARGUMENT_INTEGER(argument, Sleep ? 0 : 1); status = StorPortInvokeAcpiMethod(AdapterExtension, NULL, // NULL for Address field means the request is for Adapter ACPI_METHOD_DSM, (PVOID)inputData, inputDataSize, NULL, 0, &returnedLength ); Exit: UNREFERENCED_PARAMETER(status); if (inputData != NULL) { StorPortFreePool(AdapterExtension, inputData); } return; } #pragma warning(pop) // un-sets any local warning changes
Our Services
-
What our customers say about us?
Read our customer testimonials to find out why our clients keep returning for their projects.
View Testimonials