From dbadff83eed662c244d5a6fae48baa840fb543b8 Mon Sep 17 00:00:00 2001 From: aktsuda Date: Thu, 25 Jun 2020 05:52:49 +0900 Subject: [PATCH] Merged current inbox code except for DDKSPLIT (#474) * Merged current inbox code except for DDKSPLIT * 19H1 inbox code except for DDKSPLIT and storport_p.h * Removed internal code --- storage/miniports/storahci/src/ahci.h | 107 +- storage/miniports/storahci/src/common.c | 1633 ++++++++++++++--- storage/miniports/storahci/src/common.h | 137 +- storage/miniports/storahci/src/data.h | 96 + storage/miniports/storahci/src/entrypts.c | 874 ++++++--- storage/miniports/storahci/src/entrypts.h | 159 +- storage/miniports/storahci/src/generic.h | 2 - storage/miniports/storahci/src/hbastat.c | 595 ++++-- storage/miniports/storahci/src/hbastat.h | 1 - .../storahci/src/inbox/storahci.vcxproj | 37 +- storage/miniports/storahci/src/io.c | 639 ++++--- storage/miniports/storahci/src/io.h | 1 - storage/miniports/storahci/src/pnppower.c | 733 ++++++-- storage/miniports/storahci/src/pnppower.h | 4 +- storage/miniports/storahci/src/util.c | 331 +++- storage/miniports/storahci/src/util.h | 423 ++++- 16 files changed, 4549 insertions(+), 1223 deletions(-) create mode 100644 storage/miniports/storahci/src/data.h diff --git a/storage/miniports/storahci/src/ahci.h b/storage/miniports/storahci/src/ahci.h index 29e29f655..b11c24fef 100644 --- a/storage/miniports/storahci/src/ahci.h +++ b/storage/miniports/storahci/src/ahci.h @@ -322,9 +322,9 @@ typedef struct _AHCI_COMMAND_TABLE { UCHAR CFIS_Padding[44]; UCHAR ACMD[16]; //AHCI1.0 section 4.2.3.2 ATAPI Command (ACMD): This is a software constructed region of 12 or 16 bytes in length that contains the ATAPI command to transmit if the ?A? bit is set in the command header. The ATAPI command must be either 12 or 16 bytes in length. The length transmitted by the HBA is determined by the PIO setup FIS that is sent by the device requesting the ATAPI command. UCHAR Reserved1[48]; - AHCI_PRDT PRDT[33]; //elements in a sglist are 4K (except for maybe the first and last one). STORAHCI limits transfer size cap to be 128K, therefore no more than 33 PRD entries. - //if the PRD Table had 32 entries this structure would come out to 640 which has nice 128 byte alignment (required for all instances of these structures), - UCHAR Reserved2[112];//but since it has 33 entries it comes out to 656. another 112 bytes at the end is needed to restore 128 byte alignment and make the structure 768 bytes + AHCI_PRDT PRDT[257]; //elements in a sglist are 4K (except for maybe the first and last one). STORAHCI limits transfer size cap default to be 128K, max to 1M (configure registry MaxTransferSize to 1024), therefore no more than 257 PRD entries. + //if the PRD Table had 256 entries this structure would come out to 4224 which has nice 128 byte alignment (required for all instances of these structures), + UCHAR Reserved2[112];//but since it has 257 entries it comes out to 4240. another 112 bytes at the end is needed to restore 128 byte alignment and make the structure 4352 bytes } AHCI_COMMAND_TABLE, *PAHCI_COMMAND_TABLE; @@ -439,7 +439,7 @@ typedef union _AHCI_INTERRUPT_STATUS { ULONG UFS :1; //Unknown FIS Interrupt: When set to '1 , indicates that an unknown FIS was received and has been copied into system memory. This bit is cleared to ?0 by software clearing the PxSERR.DIAG.F bit to ?0 . Note that this bit does not directly reflect the PxSERR.DIAG.F bit. PxSERR.DIAG.F is set immediately when an unknown FIS is detected, whereas this bit is set when that FIS is posted to memory. Software should wait to act on an unknown FIS until this bit is set to ?1 or the two bits may become out of sync. ULONG DPS :1; //Descriptor Processed: A PRD with the 'I' bit set has transferred all of its data. Refer to section 5.3.2. ULONG PCS :1; //Port Connect Change Status: 1=Change in Current Connect Status. 0=No change in Current Connect Status. This bit reflects the state of PxSERR.DIAG.X. This bit is only cleared when PxSERR.DIAG.X is cleared. - ULONG DMPS :1; //Device Mechanical Presence Status (DMPS): When set, indicates that a mechanical presence switch attached to this port has been opened or closed, which may lead to a change in the connection state of the device. This bit is only valid if both CAP.SMPS and P0CMD.MPSP are set to ‘1’. + ULONG DMPS :1; //Device Mechanical Presence Status (DMPS): When set, indicates that a mechanical presence switch attached to this port has been opened or closed, which may lead to a change in the connection state of the device. This bit is only valid if both CAP.SMPS and P0CMD.MPSP are set to '1'. ULONG DW4_Reserved :14; ULONG PRCS :1; //PhyRdy Change Status: When set to '1 indicates the internal PhyRdy signal changed state. This bit reflects the state of P0SERR.DIAG.N. To clear this bit, software must clear P0SERR.DIAG.N to ?0 . ULONG IPMS :1; //Incorrect Port Multiplier Status: Indicates that the HBA received a FIS from a device whose Port Multiplier field did not match what was expected. The IPMS bit may be set during enumeration of devices on a Port Multiplier due to the normal Port Multiplier enumeration process. It is recommended that IPMS only be used after enumeration is complete on the Port Multiplier. @@ -470,7 +470,7 @@ typedef union _AHCI_INTERRUPT_ENABLE { ULONG UFE :1; // Unknown FIS Interrupt Enable: When set, GHC.IE is set, and P0IS.UFS is set to ?1 , the HBA shall generate an interrupt. ULONG DPE :1; // Descriptor Processed Interrupt Enable: When set, GHC.IE is set, and P0IS.DPS is set, the HBA shall generate an interrupt. ULONG PCE :1; // Port Change Interrupt Enable: When set, GHC.IE is set, and P0IS.PCS is set, the HBA shall generate an interrupt. - ULONG DMPE :1; //Device Mechanical Presence Enable (DMPE): When set, and GHC.IE is set to ‘1’, and P0IS.DMPS is set, the HBA shall generate an interrupt. For systems that do not support a mechanical presence switch, this bit shall be a read-only ‘0’. + ULONG DMPE :1; //Device Mechanical Presence Enable (DMPE): When set, and GHC.IE is set to '1', and P0IS.DMPS is set, the HBA shall generate an interrupt. For systems that do not support a mechanical presence switch, this bit shall be a read-only '0'. ULONG DW5_Reserved :14; // ULONG PRCE :1; // PhyRdy Change Interrupt Enable: When set to ?1 , and GHC.IE is set to ?1 , and P0IS.PRCS is set to ?1 , the HBA shall generate an interrupt. ULONG IPME :1; // Incorrect Port Multiplier Enable: When set, and GHC.IE and P0IS.IPMS are set, the HBA shall generate an interrupt. @@ -494,8 +494,8 @@ typedef union _AHCI_COMMAND { struct { //LSB - ULONG ST :1; // Start: When set, the HBA may process the command list. When cleared, the HBA may not process the command list. Whenever this bit is changed from a ?0 to a, the HBA starts processing the command list at entry ?0 . Whenever this bit is changed from a ?1 to a ?0 , the PxCI register is cleared by the HBA upon the HBA putting the controller into an idle state. This bit shall only be set to ‘1’ by software after PxCMD.FRE has been set to ‘1’. Refer to section 10.3.1 for important restrictions on when ST can be set to ?1 . - ULONG SUD :1; // Spin-Up Device: This bit is read/write for HBAs that support staggered spin-up via CAP.SSS. This bit is read only ?1 for HBAs that do not support staggered spin-up. On an edge detect from ?0 to ?1 , the HBA shall start a COMRESET initialization sequence to the device. Clearing this bit to ‘0’ does not cause any OOB signal to be sent on the interface. When this bit is cleared to ‘0’ and PxSCTL.DET=0h, the HBA will enter listen mode as detailed in section 10.9.1. + ULONG ST :1; // Start: When set, the HBA may process the command list. When cleared, the HBA may not process the command list. Whenever this bit is changed from a ?0 to a, the HBA starts processing the command list at entry ?0 . Whenever this bit is changed from a ?1 to a ?0 , the PxCI register is cleared by the HBA upon the HBA putting the controller into an idle state. This bit shall only be set to '1' by software after PxCMD.FRE has been set to '1'. Refer to section 10.3.1 for important restrictions on when ST can be set to ?1 . + ULONG SUD :1; // Spin-Up Device: This bit is read/write for HBAs that support staggered spin-up via CAP.SSS. This bit is read only ?1 for HBAs that do not support staggered spin-up. On an edge detect from ?0 to ?1 , the HBA shall start a COMRESET initialization sequence to the device. Clearing this bit to '0' does not cause any OOB signal to be sent on the interface. When this bit is cleared to '0' and PxSCTL.DET=0h, the HBA will enter listen mode as detailed in section 10.9.1. ULONG POD :1; // Power On Device: This bit is read/write for HBAs that support cold presence detection on this port as indicated by PxCMD.CPD set to ?1 . This bit is read only ?1 for HBAs that do not support cold presence detect. When set, the HBA sets the state of a pin on the HBA to ?1 so that it may be used to provide power to a cold-presence detectable port. ULONG CLO :1; // Command List Override: Setting this bit to ?1 causes PxTFD.STS.BSY and PxTFD.STS.DRQ to be cleared to ?0 . This allows a software reset to be transmitted to the device regardless of whether the BSY and DRQ bits are still set in the PxTFD.STS register. The HBA sets this bit to ?0 when PxTFD.STS.BSY and PxTFD.STS.DRQ have been cleared to ?0 . A write to this register with a value of ?0 shall have no effect. This bit shall only be set to ?1 immediately prior to setting the PxCMD.ST bit to ?1 from previous value of ?0 . Setting this bit to ?1 at any other time is not supported and will result in indeterminate behavior. ULONG FRE :1; // FIS Receive Enable: When set, the HBA may post received FISes into the FIS receive area pointed to by PxFB (and for 64-bit HBAs, PxFBU). When cleared, received FISes are not accepted by the HBA, except for the first D2H register FIS after the initialization sequence, and no FISes are posted to the FIS receive area. System software must not set this bit until PxFB (PxFBU) have been programmed with valid pointer to the FIS receive area, and if software wishes to move the base, this bit must first be cleared, and software must wait for the FR bit in this register to be cleared. Refer to section 10.3.2 for important restrictions on when FRE can be set and cleared. @@ -508,17 +508,17 @@ typedef union _AHCI_COMMAND { ULONG CPS :1; // Cold Presence State: The CPS bit reports whether a device is currently detected on this port. If CPS is set to ?1 , then the HBA detects via cold presence that a device is attached to this port. If CPS is cleared to ?0 , then the HBA detects via cold presence that there is no device attached to this port. ULONG PMA :1; // Port Multiplier Attached: This bit is read/write for HBAs that support a Port Multiplier (CAP.SPM = ?1 ). This bit is read-only for HBAs that do not support a port Multiplier (CAP.SPM = ?0 ). When set to ?1 by software, a Port Multiplier is attached to the HBA for this port. When cleared to ?0 by software, a Port Multiplier is not attached to the HBA for this port. Software is responsible for detecting whether a Port Multiplier is present; hardware does not auto-detect the presence of a Port Multiplier. - ULONG HPCP :1; // Hot Plug Capable Port: When set to ‘1’, indicates that this port’s signal and power connectors are externally accessible via a joint signal and power connector for blindmate device hot plug. When cleared to ‘0’, indicates that this port’s signal and power connectors are not externally accessible via a joint signal and power connector. + ULONG HPCP :1; // Hot Plug Capable Port: When set to '1', indicates that this port's signal and power connectors are externally accessible via a joint signal and power connector for blindmate device hot plug. When cleared to '0', indicates that this port's signal and power connectors are not externally accessible via a joint signal and power connector. ULONG MPSP :1; // Mechanical Presence Switch Attached to Port : If set to ?1 , the platform supports an interlocked switch attached to this port. If cleared to ?0 , the platform does not support an interlocked switch attached to this port. When this bit is set to ?1 , P0CMD.HPCP should also be set to ?1 . ULONG CPD :1; // Cold Presence Detection: If set to ?1 , the platform supports cold presence detection on this port. If cleared to ?0 , the platform does not support cold presence detection on this port. When this bit is set to ?1 , P0CMD.HPCP should also be set to ?1 . - ULONG ESP :1; //AHCI 1.1 External SATA Port: When set to '1', indicates that this port’s signal connector is externally accessible on a signal only connector. When set to '1', CAP.SXS shall be set to '1'. When cleared to ‘0’, indicates that this port’s signal connector is not externally accessible on a signal only connector. ESP is mutually exclusive with the HPCP bit in this register. - ULONG FBSCP :1; // FIS-based Switching Capable Port (FBSCP): When set to ‘1’, indicates that this port supports Port Multiplier FIS-based switching. When cleared to ‘0’, indicates that this port does not support FIS-based switching. This bit may only be set to ‘1’ if both CAP.SPM and CAP.FBSS are set to ‘1’. - ULONG APSTE :1; // Automatic Partial to Slumber Transitions Enabled (APSTE): When set to ‘1’, the HBA may perform Automatic Partial to Slumber Transitions. When cleared to ‘0’ the port shall not perform Automatic Partial to Slumber Transitions. Software shall only set this bit to ‘1’ if CAP2.APST is set to ‘1’; if CAP2.APST is cleared to ‘0’ software shall treat this bit as reserved. + ULONG ESP :1; //AHCI 1.1 External SATA Port: When set to '1', indicates that this port's signal connector is externally accessible on a signal only connector. When set to '1', CAP.SXS shall be set to '1'. When cleared to '0', indicates that this port's signal connector is not externally accessible on a signal only connector. ESP is mutually exclusive with the HPCP bit in this register. + ULONG FBSCP :1; // FIS-based Switching Capable Port (FBSCP): When set to '1', indicates that this port supports Port Multiplier FIS-based switching. When cleared to '0', indicates that this port does not support FIS-based switching. This bit may only be set to '1' if both CAP.SPM and CAP.FBSS are set to '1'. + ULONG APSTE :1; // Automatic Partial to Slumber Transitions Enabled (APSTE): When set to '1', the HBA may perform Automatic Partial to Slumber Transitions. When cleared to '0' the port shall not perform Automatic Partial to Slumber Transitions. Software shall only set this bit to '1' if CAP2.APST is set to '1'; if CAP2.APST is cleared to '0' software shall treat this bit as reserved. ULONG ATAPI :1; // Device is ATAPI: When set, the connected device is an ATAPI device. This bit is used by the HBA to control whether or not to generate the desktop LED when commands are active. See section 10.10 for details on the activity LED. ULONG DLAE :1; // Drive LED on ATAPI Enable: When set, the HBA shall drive the LED pin active for commands regardless of the state of P0CMD.ATAPI. When cleared, the HBA shall only drive the LED pin active for commands if P0CMD.ATAPI set to ?0 . See section 10.10 for details on the activity LED. - ULONG ALPE :1; // Aggressive Link Power Management Enable: When set to '1', the HBA shall aggressively enter a lower link power state (Partial or Slumber) based upon the setting of the ASP bit. Software shall only set this bit to ‘1’ if CAP.SALP is set to ‘1’; if CAP.SALP is cleared to ‘0’ software shall treat this bit as reserved. See section 8.3.1.3 for details. - ULONG ASP :1; // Aggressive Slumber / Partial: When set to '1', and ALPE is set, the HBA shall aggressively enter the Slumber state when it clears the PxCI register and the PxSACT register is cleared or when it clears the PxSACT register and PxCI is cleared. When cleared, and ALPE is set, the HBA shall aggressively enter the Partial state when it clears the PxCI register and the PxSACT register is cleared or when it clears the PxSACT register and PxCI is cleared. If CAP.SALP is cleared to ‘0’ software shall treat this bit as reserved. See section 8.3.1.3 for details. + ULONG ALPE :1; // Aggressive Link Power Management Enable: When set to '1', the HBA shall aggressively enter a lower link power state (Partial or Slumber) based upon the setting of the ASP bit. Software shall only set this bit to '1' if CAP.SALP is set to '1'; if CAP.SALP is cleared to '0' software shall treat this bit as reserved. See section 8.3.1.3 for details. + ULONG ASP :1; // Aggressive Slumber / Partial: When set to '1', and ALPE is set, the HBA shall aggressively enter the Slumber state when it clears the PxCI register and the PxSACT register is cleared or when it clears the PxSACT register and PxCI is cleared. When cleared, and ALPE is set, the HBA shall aggressively enter the Partial state when it clears the PxCI register and the PxSACT register is cleared or when it clears the PxSACT register and PxCI is cleared. If CAP.SALP is cleared to '0' software shall treat this bit as reserved. See section 8.3.1.3 for details. ULONG ICC :4; // Interface Communication Control: This field is used to control power management states of the interface. If the Link layer is currently in the L_IDLE state, writes to this field shall cause the HBA to initiate a transition to the interface power management state requested. If the Link layer is not currently in the L_IDLE state, writes to this field shall have no effect. //MSB }; @@ -532,12 +532,12 @@ typedef union _AHCI_DEVICE_SLEEP { struct { //LSB - ULONG ADSE :1; // Aggressive Device Sleep Enable (ADSE): When this bit is set to ‘1’, the HBA shall assert the DEVSLP signal after the port has been idle (PxCI = 0h and PxSACT = 0h) for the amount of time specified by the PxDEVSLP.DITO register. When this bit is set to ‘1’ and CAP2.DESO is set to ‘1’, the HBA shall assert the DEVSLP signal after the port has been idle (PxCI = 0h and PxSACT = 0h) for the amount of time specified by PxDEVSLP.DITO and the interface is in Slumber (PxSSTS.IPM = 6h).When this bit is cleared to ‘0’, the HBA does not enter DevSleep unless software directed via PxCMD.ICC. This bit shall only be set to ‘1’ if PxDEVSLP.DSP is set to ‘1’. - ULONG DSP :1; // Device Sleep Present (DSP): If set to ‘1’, the platform supports Device Sleep on this port. If cleared to ‘0’, the platform does not support Device Sleep on this port. This bit may only be set to ‘1’ if CAP2.SDS is set to ‘1’. DSP is mutually exclusive with the PxCMD.HPCP bit and PxCMD.ESP bit. + ULONG ADSE :1; // Aggressive Device Sleep Enable (ADSE): When this bit is set to '1', the HBA shall assert the DEVSLP signal after the port has been idle (PxCI = 0h and PxSACT = 0h) for the amount of time specified by the PxDEVSLP.DITO register. When this bit is set to '1' and CAP2.DESO is set to '1', the HBA shall assert the DEVSLP signal after the port has been idle (PxCI = 0h and PxSACT = 0h) for the amount of time specified by PxDEVSLP.DITO and the interface is in Slumber (PxSSTS.IPM = 6h).When this bit is cleared to '0', the HBA does not enter DevSleep unless software directed via PxCMD.ICC. This bit shall only be set to '1' if PxDEVSLP.DSP is set to '1'. + ULONG DSP :1; // Device Sleep Present (DSP): If set to '1', the platform supports Device Sleep on this port. If cleared to '0', the platform does not support Device Sleep on this port. This bit may only be set to '1' if CAP2.SDS is set to '1'. DSP is mutually exclusive with the PxCMD.HPCP bit and PxCMD.ESP bit. ULONG DETO :8; // Device Sleep Exit Timeout (DETO): This field specifies the maximum duration (in approximate 1ms granularity) from DEVSLP de-assertion until the device is ready to accept OOB. The nominal value is 20ms while the max value is 255ms depending on device identification information. ULONG MDAT :5; // Minimum Device Sleep Assertion Time (MDAT): This field specifies the minimum amount of time (in 1ms granularity) that the HBA must assert the DEVSLP signal before it may be de-asserted. The nominal value is 10ms and the minimum is 1ms depending on device identification information. - ULONG DITO :10; // Device Sleep Idle Timeout (DITO): This field specifies the amount of the time (in approximate 1ms granularity) that the HBA shall wait before driving the DEVSLP signal. Refer to section 8.5.1.1.1 for details. Hardware reloads its port specific Device Sleep timer with this value each time the port transitions out of the DEVSLP state. For example: from DevSleep to active or PxDEVSLP.ADSE transitions from ‘0’ to a ‘1’. - ULONG DM :4; // DITO Multiplier (DM): 0’s based value that specifies the DITO multiplier that the HBA applies to the specified DITO value, effectively extending the range of DITO from 1ms to 16368ms. A value of 0h indicates a multiplier of 1. A maximum multiplier of 16 may be applied. The HBA computes the total idle timeout as a product of DM and DITO (i.e. DITOactual = DITO * (DM+1)). + ULONG DITO :10; // Device Sleep Idle Timeout (DITO): This field specifies the amount of the time (in approximate 1ms granularity) that the HBA shall wait before driving the DEVSLP signal. Refer to section 8.5.1.1.1 for details. Hardware reloads its port specific Device Sleep timer with this value each time the port transitions out of the DEVSLP state. For example: from DevSleep to active or PxDEVSLP.ADSE transitions from '0' to a '1'. + ULONG DM :4; // DITO Multiplier (DM): 0's based value that specifies the DITO multiplier that the HBA applies to the specified DITO value, effectively extending the range of DITO from 1ms to 16368ms. A value of 0h indicates a multiplier of 1. A maximum multiplier of 16 may be applied. The HBA computes the total idle timeout as a product of DM and DITO (i.e. DITOactual = DITO * (DM+1)). ULONG Reserved :3; //MSB @@ -733,10 +733,10 @@ typedef struct _AHCI_PORT { AHCI_SERIAL_ATA_ERROR SERR; //Serial ATA Active as defined in AHCI1.0 section 3.3.13 - ULONG SACT; //Device Status: Device Status (DS): This field is bit significant. Each bit corresponds to the TAG and command slot of a native queued command, where bit 0 corresponds to TAG 0 and command slot 0. This field is set by software prior to issuing a native queued command for a particular command slot. Prior to writing PxCI[TAG] to ‘1’, software will set DS[TAG] to ‘1’ to indicate that a command with that TAG is outstanding. The device clears bits in this field by sending a Set Device Bits FIS to the host. The HBA clears bits in this field that are set to ‘1’ in the SActive field of the Set Device Bits FIS. The HBA only clears bits that correspond to native queued commands that have completed successfully. Software should only write this field when PxCMD.ST is set to ‘1’. This field is cleared when PxCMD.ST is written from a ‘1’ to a ‘0’ by software. This field is not cleared by a COMRESET or a software reset. + ULONG SACT; //Device Status: Device Status (DS): This field is bit significant. Each bit corresponds to the TAG and command slot of a native queued command, where bit 0 corresponds to TAG 0 and command slot 0. This field is set by software prior to issuing a native queued command for a particular command slot. Prior to writing PxCI[TAG] to '1', software will set DS[TAG] to '1' to indicate that a command with that TAG is outstanding. The device clears bits in this field by sending a Set Device Bits FIS to the host. The HBA clears bits in this field that are set to '1' in the SActive field of the Set Device Bits FIS. The HBA only clears bits that correspond to native queued commands that have completed successfully. Software should only write this field when PxCMD.ST is set to '1'. This field is cleared when PxCMD.ST is written from a '1' to a '0' by software. This field is not cleared by a COMRESET or a software reset. //Command Issue as defined in AHCI1.0 section 3.3.14 - ULONG CI; //Commands Issued: This field is bit significant. Each bit corresponds to a command slot, where bit 0 corresponds to command slot 0. This field is set by software to indicate to the HBA that a command has been built in system memory for a command slot and may be sent to the device. When the HBA receives a FIS which clears the BSY, DRQ, and ERR bits for the command, it clears the corresponding bit in this register for that command slot. Bits in this field shall only be set to ‘1’ by software when PxCMD.ST is set to ‘1’. This field is also cleared when PxCMD.ST is written from a ‘1’ to a ‘0’ by software. + ULONG CI; //Commands Issued: This field is bit significant. Each bit corresponds to a command slot, where bit 0 corresponds to command slot 0. This field is set by software to indicate to the HBA that a command has been built in system memory for a command slot and may be sent to the device. When the HBA receives a FIS which clears the BSY, DRQ, and ERR bits for the command, it clears the corresponding bit in this register for that command slot. Bits in this field shall only be set to '1' by software when PxCMD.ST is set to '1'. This field is also cleared when PxCMD.ST is written from a '1' to a '0' by software. AHCI_SNOTIFICATION SNTF; @@ -757,24 +757,24 @@ typedef union _AHCI_HBA_CAPABILITIES { struct { //LSB ULONG NP :5; //Number of Ports: 0 s based value indicating the maximum number of ports supported by the HBA silicon. A maximum of 32 ports can be supported. A value of ?0h?, indicating one port, is the minimum requirement. Note that the number of ports indicated in this field may be more than the number of ports indicated in the GHC.PI register. - ULONG SXS :1; //AHCI 1.1 Supports External SATA: When set to ‘1’, indicates that the HBA has one or more Serial ATA ports that has a signal only connector that is externally accessible. If this bit is set, software may refer to the PxCMD.ESP bit to determine whether a specific port has its signal connector externally accessible as a signal only connector (i.e. power is not part of that connector). When the bit is cleared to ‘0’, indicates that the HBA has no Serial ATA ports that have a signal only connector externally accessible. - ULONG EMS :1; //AHCI 1.1 Enclosure Management Supported: When set to ‘1’, indicates that the HBA supports enclosure management as defined in section 12. When enclosure management is supported, the HBA has implemented the EM_LOC and EM_CTL global HBA registers. When cleared to ‘0’, indicates that the HBA does not support enclosure management and the EM_LOC and EM_CTL global HBA registers are not implemented. - ULONG CCCS :1; //AHCI 1.1 Command Completion Coalescing Supported: When set to ‘1’, indicates that the HBA supports command completion coalescing as defined in section 11. When command completion coalescing is supported, the HBA has implemented the CCC_CTL and the CCC_PORTS global HBA registers. When cleared to ‘0’, indicates that the HBA does not support command completion coalescing and the CCC_CTL and CCC_PORTS global HBA registers are not implemented. + ULONG SXS :1; //AHCI 1.1 Supports External SATA: When set to '1', indicates that the HBA has one or more Serial ATA ports that has a signal only connector that is externally accessible. If this bit is set, software may refer to the PxCMD.ESP bit to determine whether a specific port has its signal connector externally accessible as a signal only connector (i.e. power is not part of that connector). When the bit is cleared to '0', indicates that the HBA has no Serial ATA ports that have a signal only connector externally accessible. + ULONG EMS :1; //AHCI 1.1 Enclosure Management Supported: When set to '1', indicates that the HBA supports enclosure management as defined in section 12. When enclosure management is supported, the HBA has implemented the EM_LOC and EM_CTL global HBA registers. When cleared to '0', indicates that the HBA does not support enclosure management and the EM_LOC and EM_CTL global HBA registers are not implemented. + ULONG CCCS :1; //AHCI 1.1 Command Completion Coalescing Supported: When set to '1', indicates that the HBA supports command completion coalescing as defined in section 11. When command completion coalescing is supported, the HBA has implemented the CCC_CTL and the CCC_PORTS global HBA registers. When cleared to '0', indicates that the HBA does not support command completion coalescing and the CCC_CTL and CCC_PORTS global HBA registers are not implemented. ULONG NCS :5; //Number of Command Slots: 0 s based value indicating the number of command slots supported by this HBA. A minimum of 1 and maximum of 32 slots can be supported. ULONG PSC :1; //Partial State Capable: Indicates whether the HBA can support transitions to the Partial state. When cleared to ?0 , software must not allow the HBA to initiate transitions to the Partial state via aggressive link power management nor the PxCMD.ICC field in each port, and the PxSCTL.IPM field in each port must be programmed to disallow device initiated Partial requests. When set to ?1 , HBA and device initiated Partial requests can be supported. ULONG SSC :1; //Slumber State Capable: Indicates whether the HBA can support transitions to the Slumber state. When cleared to ?0 , software must not allow the HBA to initiate transitions to the Slumber state via aggressive link power management nor the PxCMD.ICC field in each port, and the PxSCTL.IPM field in each port must be programmed to disallow device initiated Slumber requests. When set to ?1 , HBA and device initiated Slumber requests can be supported. - ULONG PMD :1; //PIO Multiple DRQ Block: If set to ‘1 , the HBA supports multiple DRQ block data transfers for the PIO command protocol. If cleared to ?0 the HBA only supports single DRQ block data transfers for the PIO command protocol. - ULONG FBSS :1; //AHCI 1.1 FIS-based Switching Supported: When set to ‘1’, indicates that the HBA supports Port Multiplier FIS-based switching. When cleared to ‘0’, indicates that the HBA does not support FIS-based switching. AHCI 1.0 and 1.1 HBAs shall have this bit cleared to ‘0’. + ULONG PMD :1; //PIO Multiple DRQ Block: If set to '1 , the HBA supports multiple DRQ block data transfers for the PIO command protocol. If cleared to ?0 the HBA only supports single DRQ block data transfers for the PIO command protocol. + ULONG FBSS :1; //AHCI 1.1 FIS-based Switching Supported: When set to '1', indicates that the HBA supports Port Multiplier FIS-based switching. When cleared to '0', indicates that the HBA does not support FIS-based switching. AHCI 1.0 and 1.1 HBAs shall have this bit cleared to '0'. ULONG SPM :1; //Supports Port Multiplier: Indicates whether the HBA can support a Port Multiplier. When set, a Port Multiplier using command-based switching is supported. When cleared to ?0 , a Port Multiplier is not supported, and a Port Multiplier may not be attached to this HBA. ULONG SAM :1; //Supports AHCI mode only: The SATA controller may optionally support AHCI access mechanisms only. A value of '0' indicates that in addition to the native AHCI mechanism (via ABAR), the SATA controller implements a legacy, task-file based register interface such as SFF-8038i. A value of '1' indicates that the SATA controller does not implement a legacy, task-file based register interface. - ULONG SNZO :1; //Supports Non-Zero DMA Offsets: When set to ‘1 , indicates that the HBA can support non-zero DMA offsets for DMA Setup FISes. This bit is reserved for future AHCI enhancements. AHCI 1.0 HBAs must have this bit cleared to ?0 . + ULONG SNZO :1; //Supports Non-Zero DMA Offsets: When set to '1 , indicates that the HBA can support non-zero DMA offsets for DMA Setup FISes. This bit is reserved for future AHCI enhancements. AHCI 1.0 HBAs must have this bit cleared to ?0 . ULONG ISS :4; //Interface Speed Support: Indicates the maximum speed the HBA can support on its ports. These encodings match the PxSCTL.DET.SPD field, which is programmable by system software. Values are: - ULONG SCLO :1; //Supports Command List Override: When set to ‘1 , indicates that the HBA supports the PxCMD.CLO bit and its associated function. When cleared to ?0 , the HBA is not capable of clearing the BSY and DRQ bits in the Status register in order to issue a software reset if these bits are still set from a previous operation. - ULONG SAL :1; //Supports Activity LED: When set to ‘1 , indicates that the HBA supports a single output pin which indicates activity. This pin can be connected to an LED on the platform to indicate device activity on any drive. See section 10.10 for more information. + ULONG SCLO :1; //Supports Command List Override: When set to '1 , indicates that the HBA supports the PxCMD.CLO bit and its associated function. When cleared to ?0 , the HBA is not capable of clearing the BSY and DRQ bits in the Status register in order to issue a software reset if these bits are still set from a previous operation. + ULONG SAL :1; //Supports Activity LED: When set to '1 , indicates that the HBA supports a single output pin which indicates activity. This pin can be connected to an LED on the platform to indicate device activity on any drive. See section 10.10 for more information. ULONG SALP :1; //Supports Aggressive Link Power Management: When set to ?1 , indicates that the HBA can support auto-generating link requests to the Partial or Slumber states when there are no commands to process. Refer to section 8.3.1.3. - ULONG SSS :1; //Supports Staggered Spin-up: When set to ‘1 , indicates that the HBA supports staggered spin-up on its ports, for use in balancing power spikes. This value is loaded by the BIOS prior to OS initialization. - ULONG SMPS :1; //Supports Mechanical Presence Switch: ): When set to ‘1’, the HBA supports mechanical presence switches on its ports for use in hot plug operations. When cleared to ‘0’, this function is not supported. This value is loaded by the BIOS prior to OS initialization. - ULONG SSNTF :1; //AHCI 1.1 Supports SNotification Register: When set to ‘1’, indicates that the HBA supports the PxSNTF (SNotification) register and its associated functionality. When cleared to ‘0’, the HBA does not support the PxSNTF (SNotification) register and its associated functionality. Refer to section 10.10.1. + ULONG SSS :1; //Supports Staggered Spin-up: When set to '1 , indicates that the HBA supports staggered spin-up on its ports, for use in balancing power spikes. This value is loaded by the BIOS prior to OS initialization. + ULONG SMPS :1; //Supports Mechanical Presence Switch: ): When set to '1', the HBA supports mechanical presence switches on its ports for use in hot plug operations. When cleared to '0', this function is not supported. This value is loaded by the BIOS prior to OS initialization. + ULONG SSNTF :1; //AHCI 1.1 Supports SNotification Register: When set to '1', indicates that the HBA supports the PxSNTF (SNotification) register and its associated functionality. When cleared to '0', the HBA does not support the PxSNTF (SNotification) register and its associated functionality. Refer to section 10.10.1. ULONG SNCQ :1; //Supports Native Command Queuing: Indicates whether the HBA supports Serial ATA native command queuing. If set to ?1 , an HBA shall handle DMA Setup FISes natively, and shall handle the auto-activate optimization through that FIS. If cleared to ?0 , native command queuing is not supported and software should not issue any native command queuing commands. ULONG S64A :1; //Supports 64-bit Addressing: Indicates whether the HBA can access 64-bit data structures. If true, the HBA shall make the 32-bit upper bits of the port DMA Descriptor, the PRD Base, and each PRD entry read/write. If cleared, these are read- only and treated as ?0 by the HBA. //MSB @@ -791,7 +791,7 @@ typedef union _AHCI_Global_HBA_CONTROL { //LSB ULONG HR :1; //HBA Reset: When set by SW, this bit causes an internal reset of the HBA. All state machines that relate to data transfers and queuing shall return to an idle condition, and all ports shall be re-initialized via COMRESET (if staggered spin-up is not supported). If staggered spin-up is supported, then it is the responsibility of software to spin-up each port after the reset has completed. When the HBA has performed the reset action, it shall reset this bit to ?0 . A software write of ?0 shall have no effect. For a description on which bits are reset when this bit is set, see section 10.4.3. ULONG IE :1; //Interrupt Enable: This global bit enables interrupts from the HBA. When cleared (reset default), all interrupt sources from all ports are disabled. When set, interrupts are enabled. - ULONG MRSM :1; //MSI Revert to Single Message: 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, or hardware is sharing interrupt vectors if MC.MME < MC.MMC). + ULONG MRSM :1; //MSI Revert to Single Message: 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, or hardware is sharing interrupt vectors if MC.MME < MC.MMC). ULONG Reserved :28; //3-30 ULONG AE :1; //AHCI Enable: When set, indicates that communication to the HBA shall be via AHCI mechanisms. This can be used by an HBA that supports both legacy mechanisms (such as SFF-8038i) and AHCI to know when the HBA is running under an AHCI driver. When set, software shall only communicate with the HBA using AHCI. When cleared, software shall only communicate with the HBA using legacy mechanisms. When cleared FISes are not posted to memory and no commands are sent via AHCI mechanisms. Software shall set this bit to ?1 before accessing other AHCI registers. The implementation of this bit is dependent upon the value of the CAP.SAM bit. If CAP.SAM is '0', then GHC.AE shall be read-write and shall have a reset value of '0'. If CAP.SAM is '1', then AE shall be read-only and shall have a reset value of '1'. //MSB @@ -820,11 +820,11 @@ typedef union _AHCI_COMMAND_COMPLETION_COALESCING_CONTROL { struct { //LSB - ULONG EN: 1; //Enable: When cleared to ‘0’, the command completion coalescing feature is disabled and no CCC interrupts are generated. When set to ‘1’, the command completion coalescing feature is enabled and CCC interrupts may be generated based on timeout or command completion conditions. Software shall only change the contents of the TV and CC fields when EN is cleared to ‘0’. On transition of this bit from ‘0’ to ‘1’, any updated values for the TV and CC fields shall take effect. + ULONG EN: 1; //Enable: When cleared to '0', the command completion coalescing feature is disabled and no CCC interrupts are generated. When set to '1', the command completion coalescing feature is enabled and CCC interrupts may be generated based on timeout or command completion conditions. Software shall only change the contents of the TV and CC fields when EN is cleared to '0'. On transition of this bit from '0' to '1', any updated values for the TV and CC fields shall take effect. ULONG Reserved: 2; - ULONG INT: 5; //Interrupt: Specifies the interrupt used by the CCC feature. This interrupt must be marked as unused in the Ports Implemented (PI) register by the corresponding bit being set to ‘0’. Thus, the CCC interrupt corresponds to the interrupt for an unimplemented port on the controller. When a CCC interrupt occurs, the IS.IPS[INT] bit shall be asserted to ‘1’. This field also specifies the interrupt vector used for MSI. - ULONG CC: 8; //Command Completions: Specifies the number of command completions that are necessary to cause a CCC interrupt. The HBA has an internal command completion counter, hCccComplete. hCccComplete is incremented by one each time a selected port has a command completion. When hCccComplete is equal to the command completions value, a CCC interrupt is signaled. The internal command completion counter is reset to ‘0’ on the assertion of each CCC interrupt. A value of ‘0’ for this field shall disable CCC interrupts being generated based on the number of commands completed, i.e. CCC interrupts are only generated based on the timer in this case. - ULONG TV: 16; //Timeout Value: The timeout value is specified in 1 millisecond intervals. The timer accuracy shall be within 5%. hCccTimer is loaded with this timeout value. hCccTimer is only decremented when commands are outstanding on selected ports, as defined in section 11.2. The HBA will signal a CCC interrupt when hCccTimer has decremented to ‘0’. hCccTimer is reset to the timeout value on the assertion of each CCC interrupt. A timeout value of ‘0’ is reserved. + ULONG INT: 5; //Interrupt: Specifies the interrupt used by the CCC feature. This interrupt must be marked as unused in the Ports Implemented (PI) register by the corresponding bit being set to '0'. Thus, the CCC interrupt corresponds to the interrupt for an unimplemented port on the controller. When a CCC interrupt occurs, the IS.IPS[INT] bit shall be asserted to '1'. This field also specifies the interrupt vector used for MSI. + ULONG CC: 8; //Command Completions: Specifies the number of command completions that are necessary to cause a CCC interrupt. The HBA has an internal command completion counter, hCccComplete. hCccComplete is incremented by one each time a selected port has a command completion. When hCccComplete is equal to the command completions value, a CCC interrupt is signaled. The internal command completion counter is reset to '0' on the assertion of each CCC interrupt. A value of '0' for this field shall disable CCC interrupts being generated based on the number of commands completed, i.e. CCC interrupts are only generated based on the timer in this case. + ULONG TV: 16; //Timeout Value: The timeout value is specified in 1 millisecond intervals. The timer accuracy shall be within 5%. hCccTimer is loaded with this timeout value. hCccTimer is only decremented when commands are outstanding on selected ports, as defined in section 11.2. The HBA will signal a CCC interrupt when hCccTimer has decremented to '0'. hCccTimer is reset to the timeout value on the assertion of each CCC interrupt. A timeout value of '0' is reserved. //MSB }; @@ -837,7 +837,7 @@ typedef union _AHCI_ENCLOSURE_MANAGEMENT_LOCATION { struct { //LSB - ULONG SZ :16; //Buffer Size: Specifies the size of the transmit message buffer area in Dwords. If both transmit and receive buffers are supported, then the transmit buffer begins at ABAR[EM_LOC.OFST*4] and the receive buffer directly follows it. If both transmit and receive buffers are supported, both buffers are of the size indicated in the Buffer Size field. A value of ‘0’ is invalid. + ULONG SZ :16; //Buffer Size: Specifies the size of the transmit message buffer area in Dwords. If both transmit and receive buffers are supported, then the transmit buffer begins at ABAR[EM_LOC.OFST*4] and the receive buffer directly follows it. If both transmit and receive buffers are supported, both buffers are of the size indicated in the Buffer Size field. A value of '0' is invalid. ULONG OFST :16; //Offset: The offset of the message buffer in Dwords from the beginning of the ABAR. //MSB }; @@ -851,20 +851,20 @@ typedef union _AHCI_ENCLOSURE_MANAGEMENT_CONTROL { struct { //LSB - ULONG STS_MR :1; //Message Received: The HBA sets this bit to a ‘1’ when a message is completely received into the message buffer. When software detects this bit is a ‘1’, software should read the message and perform any actions necessary. When software is finished reading the message in the buffer, software writes a ‘1’ to this bit in order to clear it. A write of ‘0’ to this bit by software shall have no effect. + ULONG STS_MR :1; //Message Received: The HBA sets this bit to a '1' when a message is completely received into the message buffer. When software detects this bit is a '1', software should read the message and perform any actions necessary. When software is finished reading the message in the buffer, software writes a '1' to this bit in order to clear it. A write of '0' to this bit by software shall have no effect. ULONG Reserved0 :7; - ULONG CTL_TM :1; //Transmit Message: When set to ‘1’ by software, the HBA shall transmit the message contained in the message buffer. When the message is completely sent, the HBA shall clear this bit to ‘0’. A write of ‘0’ to this bit by software shall have no effect. Software shall not change the contents of the message buffer while CTL.TM is set to ‘1’. - ULONG CTL_RST :1; //Reset: When set to ‘1’ by software, the HBA shall reset all enclosure management message logic and the attached enclosure processor (if applicable) and take all appropriate reset actions to ensure messages can be transmitted/received after the reset. After the HBA completes the reset operation, the HBA shall set the value to ‘0’. A write of ‘0’ by software to this field shall have no effect. + ULONG CTL_TM :1; //Transmit Message: When set to '1' by software, the HBA shall transmit the message contained in the message buffer. When the message is completely sent, the HBA shall clear this bit to '0'. A write of '0' to this bit by software shall have no effect. Software shall not change the contents of the message buffer while CTL.TM is set to '1'. + ULONG CTL_RST :1; //Reset: When set to '1' by software, the HBA shall reset all enclosure management message logic and the attached enclosure processor (if applicable) and take all appropriate reset actions to ensure messages can be transmitted/received after the reset. After the HBA completes the reset operation, the HBA shall set the value to '0'. A write of '0' by software to this field shall have no effect. ULONG Reserved1 :6; - ULONG SUPP_LED :1; //LED Message Types: If set to ‘1’, the HBA supports the LED message type defined in section 12.2.1. - ULONG SUPP_SAFTE :1; //SAF-TE Enclosure Management Messages: If set to ‘1’, the HBA supports the SAF-TE message type. - ULONG SUPP_SES2 :1; //SES-2 Enclosure Management Messages: If set to ‘1’, the HBA supports the SES-2 message type. - ULONG SUPP_SGPIO :1; //SGPIO Enclosure Management Messages: If set to ‘1’, the HBA supports the SGPIO register interface message type. + ULONG SUPP_LED :1; //LED Message Types: If set to '1', the HBA supports the LED message type defined in section 12.2.1. + ULONG SUPP_SAFTE :1; //SAF-TE Enclosure Management Messages: If set to '1', the HBA supports the SAF-TE message type. + ULONG SUPP_SES2 :1; //SES-2 Enclosure Management Messages: If set to '1', the HBA supports the SES-2 message type. + ULONG SUPP_SGPIO :1; //SGPIO Enclosure Management Messages: If set to '1', the HBA supports the SGPIO register interface message type. ULONG Reserved2 :4; - ULONG ATTR_SMB :1; //Single Message Buffer: If set to ‘1’, the HBA has one message buffer that is shared for messages to transmit and messages received. In this case, unsolicited receive messages are not supported and it is software’s responsibility to manage access to this buffer. If cleared to ‘0’, there are separate receive and transmit buffers such that unsolicited messages could be supported. - ULONG ATTR_XMT :1; //Transmit Only: If set to ‘1’, the HBA only supports transmitting messages and does not support receiving messages. If cleared to ‘0’, the HBA supports transmitting and receiving messages. - ULONG ATTR_ALHD :1; //Activity LED Hardware Driven: If set to ‘1’, the HBA drives the activity LED for the LED message type in hardware and does not utilize software settings for this LED. The HBA does not begin transmitting the hardware based activity signal until after software has written CTL.TM=1 after a reset condition. - ULONG ATTR_PM :1; //Port Multiplier Support: If set to ‘1’, the HBA supports enclosure management messages for devices attached via a Port Multiplier. If cleared to ‘0’, the HBA does not support enclosure management messages for devices attached via a Port Multiplier. When cleared to ‘0’, software should use the Serial ATA enclosure management bridge that is built into many Port Multipliers for enclosure services with these devices. For more information on Serial ATA enclosure management bridges, refer to the Serial ATA Revision 2.5 specification. + ULONG ATTR_SMB :1; //Single Message Buffer: If set to '1', the HBA has one message buffer that is shared for messages to transmit and messages received. In this case, unsolicited receive messages are not supported and it is software's responsibility to manage access to this buffer. If cleared to '0', there are separate receive and transmit buffers such that unsolicited messages could be supported. + ULONG ATTR_XMT :1; //Transmit Only: If set to '1', the HBA only supports transmitting messages and does not support receiving messages. If cleared to '0', the HBA supports transmitting and receiving messages. + ULONG ATTR_ALHD :1; //Activity LED Hardware Driven: If set to '1', the HBA drives the activity LED for the LED message type in hardware and does not utilize software settings for this LED. The HBA does not begin transmitting the hardware based activity signal until after software has written CTL.TM=1 after a reset condition. + ULONG ATTR_PM :1; //Port Multiplier Support: If set to '1', the HBA supports enclosure management messages for devices attached via a Port Multiplier. If cleared to '0', the HBA does not support enclosure management messages for devices attached via a Port Multiplier. When cleared to '0', software should use the Serial ATA enclosure management bridge that is built into many Port Multipliers for enclosure services with these devices. For more information on Serial ATA enclosure management bridges, refer to the Serial ATA Revision 2.5 specification. ULONG Reserved3 :4; //MSB }; @@ -878,12 +878,12 @@ typedef union _AHCI_HBA_CAPABILITIES2 { struct { //LSB - ULONG BOH :1; //RO -- BIOS/OS Handoff: When set to ‘1’, the HBA supports the BIOS/OS handoff mechanism defined in section TBD. When cleared to ‘0’, the HBA does not support the BIOS/OS handoff mechanism. - ULONG NVMP :1; //RO -- NVMHCI Present: When set to '1', the HBA includes support for NVMHCI and the registers at offset 60h-9Fh are valid. When cleared to ‘0’, the HBA does not support NVMHCI. - ULONG APST :1; //RO -- Automatic Partial to Slumber Transitions (APST): When set to ‘1’, the HBA supports Automatic Partial to Slumber Transitions. When cleared to ‘0’, Automatic Partial to Slumber Transitions are not supported. - ULONG SDS :1; //RO -- Supports Device Sleep (SDS): When set to ‘1’, the HBA supports the Device Sleep feature. When cleared to ‘0’, DEVSLP is not supported and software shall not set PxCMD.ICC to ‘8h’ - ULONG SADM :1; //RO -- Supports Aggressive Device Sleep Management (SADM): When set to ‘1’, the HBA supports hardware assertion of the DEVSLP signal after the idle timeout expires. When cleared to ‘0’, this function is not supported and software shall treat the PxDEVSLP.ADSE field as reserved. Refer to section 8.5.1. - ULONG DESO :1; //RO -- DevSleep Entrance from Slumber Only (DESO): This field specifies that the HBA shall only assert the DEVSLP signal if the interface is in Slumber. When this bit is set to ‘1’, the HBA shall ignore software directed entrance to DevSleep via PxCMD.ICC unless PxSSTS.IPM = 6h. When this bit is cleared to ‘0’, the HBA may enter DevSleep from any link state (active, Partial, or Slumber). + ULONG BOH :1; //RO -- BIOS/OS Handoff: When set to '1', the HBA supports the BIOS/OS handoff mechanism defined in section TBD. When cleared to '0', the HBA does not support the BIOS/OS handoff mechanism. + ULONG NVMP :1; //RO -- NVMHCI Present: When set to '1', the HBA includes support for NVMHCI and the registers at offset 60h-9Fh are valid. When cleared to '0', the HBA does not support NVMHCI. + ULONG APST :1; //RO -- Automatic Partial to Slumber Transitions (APST): When set to '1', the HBA supports Automatic Partial to Slumber Transitions. When cleared to '0', Automatic Partial to Slumber Transitions are not supported. + ULONG SDS :1; //RO -- Supports Device Sleep (SDS): When set to '1', the HBA supports the Device Sleep feature. When cleared to '0', DEVSLP is not supported and software shall not set PxCMD.ICC to '8h' + ULONG SADM :1; //RO -- Supports Aggressive Device Sleep Management (SADM): When set to '1', the HBA supports hardware assertion of the DEVSLP signal after the idle timeout expires. When cleared to '0', this function is not supported and software shall treat the PxDEVSLP.ADSE field as reserved. Refer to section 8.5.1. + ULONG DESO :1; //RO -- DevSleep Entrance from Slumber Only (DESO): This field specifies that the HBA shall only assert the DEVSLP signal if the interface is in Slumber. When this bit is set to '1', the HBA shall ignore software directed entrance to DevSleep via PxCMD.ICC unless PxSSTS.IPM = 6h. When this bit is cleared to '0', the HBA may enter DevSleep from any link state (active, Partial, or Slumber). ULONG Reserved :26; //MSB @@ -918,4 +918,3 @@ typedef struct _AHCI_MEMORY_REGISTERS{ #pragma warning(default:4214) #pragma warning(default:4201) #endif - diff --git a/storage/miniports/storahci/src/common.c b/storage/miniports/storahci/src/common.c index 2c1dd9913..b8aa8667b 100644 --- a/storage/miniports/storahci/src/common.c +++ b/storage/miniports/storahci/src/common.c @@ -91,10 +91,58 @@ AtaAlwaysSuccessRequestCompletion ( _In_ PSTORAGE_REQUEST_BLOCK Srb ); +__inline +BOOLEAN +IsExceptionCommand( + _In_ PCDB Cdb + ) +/* + Return true for enumeration and power transition related command. +*/ +{ + return ((Cdb->CDB10.OperationCode == SCSIOP_REPORT_LUNS) || + (Cdb->CDB10.OperationCode == SCSIOP_INQUIRY) || + (Cdb->CDB10.OperationCode == SCSIOP_START_STOP_UNIT) || + (Cdb->CDB10.OperationCode == SCSIOP_SECURITY_PROTOCOL_OUT) || + (Cdb->CDB10.OperationCode == SCSIOP_SECURITY_PROTOCOL_IN) || + (Cdb->CDB10.OperationCode == SCSIOP_SYNCHRONIZE_CACHE)); +} + +__inline +BOOLEAN +RetryCommand( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_ PCDB Cdb + ) +/* + If initialization triggered by power up in progress, return false; + else if port start/init/restore preserved settings command outstanding, and + command(e.g. enumeration and power transition related) is not in exception + list, return true; + Otherwise, return false. +*/ +{ + // + // If power up in progress, do not retry anything. + // + if (ChannelExtension->StateFlags.PowerUpInitializationInProgress) { + return FALSE; + } + + if ((ChannelExtension->StateFlags.ReservedSlotInUse || + ((ChannelExtension->StartState.ChannelNextStartState != StartComplete) && + (ChannelExtension->StartState.ChannelNextStartState != StartFailed))) && + (!IsExceptionCommand(Cdb))) { + return TRUE; + } + + return FALSE; +} + ULONG SCSItoATA( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, - _In_ PSTORAGE_REQUEST_BLOCK Srb + _In_ PSTORAGE_REQUEST_BLOCK Srb ) /* Note: If there is a need to send a command to device, @@ -103,9 +151,9 @@ SCSItoATA( Not setting this field can cause the Srb being completed earlier than expected. */ { - ULONG status; - ULONG cdbLength = 0; - PCDB cdb = RequestGetSrbScsiData(Srb, &cdbLength, NULL, NULL, NULL); + ULONG status; + ULONG cdbLength = 0; + PCDB cdb = RequestGetSrbScsiData(Srb, &cdbLength, NULL, NULL, NULL); if (cdb != NULL) { if (IsAtapiDevice(&ChannelExtension->DeviceExtension->DeviceParameters)) { @@ -114,8 +162,17 @@ SCSItoATA( } else if (IsAtaDevice(&ChannelExtension->DeviceExtension->DeviceParameters) || (cdb->CDB10.OperationCode == SCSIOP_REPORT_LUNS) || (cdb->CDB10.OperationCode == SCSIOP_INQUIRY) ) { - // Ata command, or device enumeration commands. - status = SrbConvertToATACommand(ChannelExtension, Srb, cdb, cdbLength); + // + // Fail SRB with busy status to storport if port start is in progress or init/restore + // preserved settings command outstanding. Storport will retry these IOs later. + // + if (RetryCommand(ChannelExtension, cdb)) { + Srb->SrbStatus = SRB_STATUS_BUSY; + status = STOR_STATUS_BUSY; + } else { + // Ata command, or device enumeration commands. + status = SrbConvertToATACommand(ChannelExtension, Srb, cdb, cdbLength); + } } else { Srb->SrbStatus = SRB_STATUS_NO_DEVICE; status = STOR_STATUS_INVALID_DEVICE_REQUEST; @@ -306,7 +363,7 @@ Return value: if (Srb->SrbStatus == SRB_STATUS_BUS_RESET) { if (srbExtension->DataBuffer != NULL) { - AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, (ULONG_PTR)srbExtension->CompletionContext, srbExtension->DataBuffer); + AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, (ULONG_PTR)srbExtension->CompletionContext, srbExtension->DataBuffer, srbExtension->DataBufferPhysicalAddress); srbExtension->DataBuffer = NULL; } return; @@ -317,7 +374,7 @@ Return value: (srbExtension->DataTransferLength < sizeof(MODE_PARAMETER_HEADER)) ) { // free the memory and mark the Srb with error status. if (srbExtension->DataBuffer != NULL) { - AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, (ULONG_PTR)srbExtension->CompletionContext, srbExtension->DataBuffer); + AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, (ULONG_PTR)srbExtension->CompletionContext, srbExtension->DataBuffer, srbExtension->DataBufferPhysicalAddress); srbExtension->DataBuffer = NULL; } @@ -370,7 +427,7 @@ Return value: } if (srbExtension->DataBuffer != NULL) { - AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, (ULONG_PTR)srbExtension->CompletionContext, srbExtension->DataBuffer); + AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, (ULONG_PTR)srbExtension->CompletionContext, srbExtension->DataBuffer, srbExtension->DataBufferPhysicalAddress); srbExtension->DataBuffer = NULL; } @@ -388,9 +445,8 @@ AtapiModeSenseRequest ( { ULONG status; ULONG modeSenseBufferSize; - ULONG tempLength; PMODE_PARAMETER_HEADER10 modeSenseBuffer; - STOR_PHYSICAL_ADDRESS modeSensePhysialAddress; + STOR_PHYSICAL_ADDRESS modeSensePhysicalAddress = {0}; PUCHAR origBuffer; UCHAR bytesAdjust; @@ -432,7 +488,7 @@ AtapiModeSenseRequest ( } // We need to allocate a new data buffer since the size is different - status = AhciAllocateDmaBuffer(ChannelExtension->AdapterExtension, modeSenseBufferSize, (PVOID*)&modeSenseBuffer); + status = AhciAllocateDmaBuffer(ChannelExtension->AdapterExtension, modeSenseBufferSize, (PVOID*)&modeSenseBuffer, &modeSensePhysicalAddress); if ( (status != STOR_STATUS_SUCCESS) || (modeSenseBuffer == NULL) ) { // memory allocation failed @@ -442,7 +498,6 @@ AtapiModeSenseRequest ( } AhciZeroMemory((PCHAR)modeSenseBuffer, modeSenseBufferSize); - modeSensePhysialAddress = StorPortGetPhysicalAddress(ChannelExtension->AdapterExtension, NULL, (PVOID)modeSenseBuffer, &tempLength); // fill information in the new MODE_SENSE10 header allocationLength = Cdb->MODE_SENSE.AllocationLength; @@ -472,13 +527,14 @@ AtapiModeSenseRequest ( srbExtension->Flags |= ATA_FLAGS_NEW_CDB; srbExtension->Flags |= ATA_FLAGS_DATA_IN; srbExtension->DataBuffer = modeSenseBuffer; + srbExtension->DataBufferPhysicalAddress.QuadPart = modeSensePhysicalAddress.QuadPart; srbExtension->DataTransferLength = modeSenseBufferSize; srbExtension->CompletionRoutine = AtapiModeCommandRequestCompletion; srbExtension->CompletionContext = (PVOID)modeSenseBufferSize; // preserve the buffer size, it's needed for freeing the memory srbExtension->LocalSgl.NumberOfElements = 1; - srbExtension->LocalSgl.List[0].PhysicalAddress.LowPart = modeSensePhysialAddress.LowPart; - srbExtension->LocalSgl.List[0].PhysicalAddress.HighPart = modeSensePhysialAddress.HighPart; + srbExtension->LocalSgl.List[0].PhysicalAddress.LowPart = modeSensePhysicalAddress.LowPart; + srbExtension->LocalSgl.List[0].PhysicalAddress.HighPart = modeSensePhysicalAddress.HighPart; srbExtension->LocalSgl.List[0].Length = modeSenseBufferSize; srbExtension->Sgl = &srbExtension->LocalSgl; @@ -486,7 +542,7 @@ AtapiModeSenseRequest ( if (status != STOR_STATUS_SUCCESS) { if (modeSenseBuffer != NULL) { - AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, modeSenseBufferSize, modeSenseBuffer); + AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, modeSenseBufferSize, modeSenseBuffer, modeSensePhysicalAddress); } srbExtension->DataBuffer = NULL; } @@ -508,8 +564,7 @@ AtapiModeSelectRequest ( ULONG status; PVOID modeSelectBuffer; ULONG modeSelectBufferSize; - ULONG tempLength; - STOR_PHYSICAL_ADDRESS modeSelectPhysialAddress; + STOR_PHYSICAL_ADDRESS modeSelectPhysicalAddress = {0}; UCHAR bytesToSkip; PUCHAR origBuffer; UCHAR bytesAdjust; @@ -556,7 +611,7 @@ AtapiModeSelectRequest ( modeSelectBufferSize = srbDataBufferLength + bytesAdjust - header->BlockDescriptorLength; // allocate buffer for the new cdb - status = AhciAllocateDmaBuffer(ChannelExtension->AdapterExtension, modeSelectBufferSize, (PVOID*)&modeSelectBuffer); + status = AhciAllocateDmaBuffer(ChannelExtension->AdapterExtension, modeSelectBufferSize, (PVOID*)&modeSelectBuffer, &modeSelectPhysicalAddress); if ( (status != STOR_STATUS_SUCCESS) || (modeSelectBuffer == NULL) ) { @@ -567,7 +622,6 @@ AtapiModeSelectRequest ( } AhciZeroMemory((PCHAR)modeSelectBuffer, modeSelectBufferSize); - modeSelectPhysialAddress = StorPortGetPhysicalAddress(ChannelExtension->AdapterExtension, NULL, (PVOID)modeSelectBuffer, &tempLength); header10 = (PMODE_PARAMETER_HEADER10)modeSelectBuffer; @@ -610,6 +664,7 @@ AtapiModeSelectRequest ( srbExtension->Flags |= ATA_FLAGS_NEW_CDB; srbExtension->Flags |= ATA_FLAGS_DATA_OUT; srbExtension->DataBuffer = modeSelectBuffer; + srbExtension->DataBufferPhysicalAddress.QuadPart = modeSelectPhysicalAddress.QuadPart; srbExtension->DataTransferLength = modeSelectBufferSize; srbExtension->CompletionRoutine = AtapiModeCommandRequestCompletion; srbExtension->CompletionContext = (PVOID)modeSelectBufferSize; // preserve the buffer size, it's needed for freeing the memory @@ -626,8 +681,8 @@ AtapiModeSelectRequest ( cdb->MODE_SELECT10.Control = Cdb->MODE_SELECT.Control; srbExtension->LocalSgl.NumberOfElements = 1; - srbExtension->LocalSgl.List[0].PhysicalAddress.LowPart = modeSelectPhysialAddress.LowPart; - srbExtension->LocalSgl.List[0].PhysicalAddress.HighPart = modeSelectPhysialAddress.HighPart; + srbExtension->LocalSgl.List[0].PhysicalAddress.LowPart = modeSelectPhysicalAddress.LowPart; + srbExtension->LocalSgl.List[0].PhysicalAddress.HighPart = modeSelectPhysicalAddress.HighPart; srbExtension->LocalSgl.List[0].Length = modeSelectBufferSize; // preserve the buffer size, it's needed for freeing the memory srbExtension->Sgl = &srbExtension->LocalSgl; @@ -635,7 +690,7 @@ AtapiModeSelectRequest ( if (status != STOR_STATUS_SUCCESS) { if (modeSelectBuffer != NULL) { - AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, modeSelectBufferSize, modeSelectBuffer); + AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, modeSelectBufferSize, modeSelectBuffer, modeSelectPhysicalAddress); } srbExtension->DataBuffer = NULL; } @@ -660,7 +715,7 @@ SrbConvertToATACommand( Not setting this field can cause the Srb being completed earlier than expected. */ { - ULONG status; + ULONG status = STOR_STATUS_INVALID_DEVICE_REQUEST; // translate the cdb to task file register values switch (Cdb->CDB10.OperationCode) { @@ -693,11 +748,29 @@ SrbConvertToATACommand( break; case SCSIOP_READ_CAPACITY: - case SCSIOP_READ_CAPACITY16: status = AtaReadCapacityRequest(ChannelExtension, Srb, Cdb, CdbLength); break; + case SCSIOP_READ_CAPACITY16: + + // + // Get physical element status & Remove element and truncate command share the same operation code 0x9e + // with read capacity 16 command. + // + + if (Cdb->READ_CAPACITY16.ServiceAction == SERVICE_ACTION_READ_CAPACITY16) { + status = AtaReadCapacityRequest(ChannelExtension, Srb, Cdb, CdbLength); + } else if (Cdb->GET_PHYSICAL_ELEMENT_STATUS.ServiceAction == SERVICE_ACTION_GET_PHYSICAL_ELEMENT_STATUS) { + status = AtaGetPhysicalElementStatusRequest(ChannelExtension, Srb, Cdb); + } else if (Cdb->REMOVE_ELEMENT_AND_TRUNCATE.ServiceAction == SERVICE_ACTION_REMOVE_ELEMENT_AND_TRUNCATE) { + status = AtaRemoveElementAndTruncateRequest(ChannelExtension, Srb, Cdb); + } else { + Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; + } + + break; + case SCSIOP_INQUIRY: status = AtaInquiryRequest(ChannelExtension, Srb, Cdb); @@ -746,14 +819,62 @@ SrbConvertToATACommand( status = AtaWriteBufferRequest(ChannelExtension, Srb, Cdb); break; + case SCSIOP_READ_DATA_BUFF16: + + if (Cdb->READ_BUFFER_16.Mode == READ_BUFFER_MODE_ERROR_HISTORY) { + if (Cdb->READ_BUFFER_16.BufferId == BUFFER_ID_RETURN_ERROR_HISTORY_DIRECTORY) { + status = AtaGetDeviceCurrentInternalStatusDataHeader(ChannelExtension, Srb, Cdb); + } else if((Cdb->READ_BUFFER_16.BufferId >= BUFFER_ID_RETURN_ERROR_HISTORY_MINIMUM_THRESHOLD) && + (Cdb->READ_BUFFER_16.BufferId <= BUFFER_ID_RETURN_ERROR_HISTORY_MAXIMUM_THRESHOLD)) { + status = AtaGetDeviceCurrentInternalStatusData(ChannelExtension, Srb, Cdb); + } else { + Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; + } + } else { + Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; + } + + break; default: Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; - status = STOR_STATUS_INVALID_DEVICE_REQUEST; break; } + if (status != STOR_STATUS_SUCCESS) { + + } else { + PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); + AHCI_H2D_REGISTER_FIS cfis = srbExtension->Cfis; // Size is 20 bytes (shares first 16 with TaskFile) + // Convert the Cfis to an array of ULONGs to log the struct in pieces + PULONG cfisAsUlong = (PULONG)&cfis; // Length is 5 ULONGs + StorPortEtwEvent8(ChannelExtension->AdapterExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + AhciEtwEventBuildIO, + L"SrbConvertToATACommand finished", + STORPORT_ETW_EVENT_KEYWORD_COMMAND_TRACE, + StorportEtwLevelInformational, + StorportEtwEventOpcodeInfo, + (PSCSI_REQUEST_BLOCK)Srb, + L"AtaFunction", + srbExtension->AtaFunction, + L"Cfis 0-7", + ((ULONGLONG)cfisAsUlong[0] << 32) + cfisAsUlong[1], + L"Cfis 8-15", + ((ULONGLONG)cfisAsUlong[2] << 32) + cfisAsUlong[3], + L"Cfis 16-20", + ((ULONGLONG)cfisAsUlong[4] << 32), + L"CDB 0-7", + ((ULONGLONG)Cdb->AsUlong[0] << 32) + Cdb->AsUlong[1], + L"CDB 8-15", + ((ULONGLONG)Cdb->AsUlong[2] << 32) + Cdb->AsUlong[3], + NULL, + 0, + NULL, + 0); + } + return status; } @@ -852,7 +973,7 @@ HybridWriteThroughEvictCompletion( // // Free DMA buffer that allocated for EVICT command. // - AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, ATA_BLOCK_SIZE, srbExtension->DataBuffer); + AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, ATA_BLOCK_SIZE, srbExtension->DataBuffer, srbExtension->DataBufferPhysicalAddress); return; } @@ -880,7 +1001,6 @@ Return Value: --*/ { ULONG status; - ULONG i; PATA_LBA_RANGE buffer = NULL; // DMA buffer allocated for EVICT command STOR_PHYSICAL_ADDRESS bufferPhysicalAddress; PAHCI_SRB_EXTENSION srbExtension; @@ -897,7 +1017,7 @@ Return Value: // // allocate DMA buffer, this buffer will be used to store the ATA LBA Range for EVICT command // - status = AhciAllocateDmaBuffer((PVOID)ChannelExtension->AdapterExtension, ATA_BLOCK_SIZE, (PVOID*)&buffer); + status = AhciAllocateDmaBuffer((PVOID)ChannelExtension->AdapterExtension, ATA_BLOCK_SIZE, (PVOID*)&buffer, &bufferPhysicalAddress); if ( (status != STOR_STATUS_SUCCESS) || (buffer == NULL) ) { Srb->SrbStatus = SRB_STATUS_ERROR; @@ -925,9 +1045,9 @@ Return Value: srbExtension->AtaFunction = ATA_FUNCTION_ATA_CFIS_PAYLOAD; srbExtension->Flags |= ATA_FLAGS_DATA_OUT; srbExtension->DataBuffer = buffer; + srbExtension->DataBufferPhysicalAddress.QuadPart = bufferPhysicalAddress.QuadPart; srbExtension->DataTransferLength = ATA_BLOCK_SIZE; - bufferPhysicalAddress = StorPortGetPhysicalAddress(ChannelExtension->AdapterExtension, NULL, buffer, &i); srbExtension->LocalSgl.NumberOfElements = 1; srbExtension->LocalSgl.List[0].PhysicalAddress.LowPart = bufferPhysicalAddress.LowPart; srbExtension->LocalSgl.List[0].PhysicalAddress.HighPart = bufferPhysicalAddress.HighPart; @@ -1164,7 +1284,6 @@ Return Value: } } - return; } @@ -1741,9 +1860,9 @@ AtaReadCapacityCompletion ( if (srbDataBuffer != NULL) { if (cdbLength == 0x10) { - // 16 byte CDB + // 16 byte CDB, set return data length default to sizeof(READ_CAPACITY_DATA_EX). PREAD_CAPACITY16_DATA readCap16 = (PREAD_CAPACITY16_DATA)srbDataBuffer; - ULONG returnDataLength = 12; //default to sizeof(READ_CAPACITY_DATA_EX) + ULONG returnDataLength = sizeof(READ_CAPACITY_DATA_EX); if (srbDataBufferLength < sizeof(READ_CAPACITY_DATA_EX)) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; @@ -1780,7 +1899,11 @@ AtaReadCapacityCompletion ( readCap16->LBPRZ = 0; } - returnDataLength = FIELD_OFFSET(READ_CAPACITY16_DATA, Reserved3); + if (srbDataBufferLength >= sizeof(READ_CAPACITY16_DATA)) { + returnDataLength = sizeof(READ_CAPACITY16_DATA); + } else { + returnDataLength = (ULONG)FIELD_OFFSET(READ_CAPACITY16_DATA, Reserved3); + } } SrbSetDataTransferLength(Srb, returnDataLength); @@ -1939,7 +2062,7 @@ AtaGenerateInquiryData ( AhciZeroMemory((PCHAR)InquiryData, ATA_INQUIRYDATA_SIZE); - InquiryData->DeviceType = ChannelExtension->DeviceExtension->DeviceParameters.ScsiDeviceType; //DIRECT_ACCESS_DEVICE; + InquiryData->DeviceType = ChannelExtension->DeviceExtension->DeviceParameters.ScsiDeviceType; InquiryData->RemovableMedia = IsRemovableMedia(&ChannelExtension->DeviceExtension->DeviceParameters) ? 1 : 0; InquiryData->ResponseDataFormat = 0x2; //data format is defined in SPC standard InquiryData->CommandQueue = 1; //support NCQ for AHCI controller @@ -2143,7 +2266,11 @@ AtaInquiryRequest( _In_ PCDB Cdb ) /* - NOTE: the command should be completed after calling this function as no real command will be sent to device. + NOTE: + In most cases the command should be completed after calling this function as + no real command will be sent to device. The exceptions being if we're on the + dump stack or if a firmware activate was just performed and the identify data + needs to be refreshed. */ { ULONG status = STOR_STATUS_SUCCESS; @@ -2157,18 +2284,17 @@ AtaInquiryRequest( status = STOR_STATUS_INVALID_PARAMETER; } else if (Cdb->CDB6INQUIRY3.EnableVitalProductData == 0) { - if (IsDumpMode(ChannelExtension->AdapterExtension) && !DeviceIdentificationComplete(ChannelExtension->AdapterExtension)) { + if (IsDumpMode(ChannelExtension->AdapterExtension) && !DeviceIdentificationCompletedSuccess(ChannelExtension->AdapterExtension)) { // the enumeration command from dump stack. IssueIdentifyCommand(ChannelExtension, Srb); - } else if (ChannelExtension->DeviceExtension->DeviceParameters.StateFlags.NeedUpdateIdentifyDeviceData == 1) { - ChannelExtension->DeviceExtension->DeviceParameters.StateFlags.NeedUpdateIdentifyDeviceData = 0; + } else if (ChannelExtension->DeviceExtension->DeviceParameters.StateFlags.NeedUpdateIdentifyDeviceData == 1) { IssueIdentifyCommand(ChannelExtension, Srb); } else { status = InquiryComplete(ChannelExtension, Srb); } } else { - PVOID srbDataBuffer = SrbGetDataBuffer(Srb); - ULONG srbDataBufferLength = SrbGetDataTransferLength(Srb); + PVOID srbDataBuffer = SrbGetDataBuffer(Srb); + ULONG srbDataBufferLength = SrbGetDataTransferLength(Srb); if (srbDataBuffer == NULL) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; @@ -2184,7 +2310,12 @@ AtaInquiryRequest( // // Input buffer should be at least the size of page header plus count of supported pages, each page needs one byte. // - ULONG requiredBufferSize = sizeof(VPD_SUPPORTED_PAGES_PAGE) + 6; + + ULONG requiredBufferSize = 0; + UCHAR supportedPageCount = 8; + ULONG i = 0; + + requiredBufferSize = sizeof(VPD_SUPPORTED_PAGES_PAGE) + supportedPageCount; if (srbDataBufferLength < requiredBufferSize) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; @@ -2192,16 +2323,26 @@ AtaInquiryRequest( } else { PVPD_SUPPORTED_PAGES_PAGE outputBuffer = (PVPD_SUPPORTED_PAGES_PAGE)srbDataBuffer; - outputBuffer->DeviceType = ChannelExtension->DeviceExtension->DeviceParameters.ScsiDeviceType; //DIRECT_ACCESS_DEVICE; ; + // + // Report supported page in ascending order beginning with page code 00h. + // + outputBuffer->DeviceType = ChannelExtension->DeviceExtension->DeviceParameters.ScsiDeviceType; outputBuffer->DeviceTypeQualifier = 0; outputBuffer->PageCode = VPD_SUPPORTED_PAGES; - outputBuffer->PageLength = 0x06; // supports 6 VPD pages - outputBuffer->SupportedPageList[0] = VPD_SUPPORTED_PAGES; - outputBuffer->SupportedPageList[1] = VPD_SERIAL_NUMBER; - outputBuffer->SupportedPageList[2] = VPD_ATA_INFORMATION; - outputBuffer->SupportedPageList[3] = VPD_BLOCK_LIMITS; - outputBuffer->SupportedPageList[4] = VPD_BLOCK_DEVICE_CHARACTERISTICS; - outputBuffer->SupportedPageList[5] = VPD_LOGICAL_BLOCK_PROVISIONING; + outputBuffer->PageLength = supportedPageCount; + outputBuffer->SupportedPageList[i++] = VPD_SUPPORTED_PAGES; + outputBuffer->SupportedPageList[i++] = VPD_SERIAL_NUMBER; + // Support device identification page(VPD: 0x83) to resolve PCS test failure. + outputBuffer->SupportedPageList[i++] = VPD_DEVICE_IDENTIFIERS; + // According to SAT4, if the SATL supports the Read Buffer command with mode field set to 0x1C(Error History mode), + // then the SATL shall support the Extended Inquiry Data VPD page. + outputBuffer->SupportedPageList[i++] = VPD_EXTENDED_INQUIRY_DATA; + outputBuffer->SupportedPageList[i++] = VPD_ATA_INFORMATION; + outputBuffer->SupportedPageList[i++] = VPD_BLOCK_LIMITS; + outputBuffer->SupportedPageList[i++] = VPD_BLOCK_DEVICE_CHARACTERISTICS; + outputBuffer->SupportedPageList[i++] = VPD_LOGICAL_BLOCK_PROVISIONING; + + NT_ASSERT(supportedPageCount == i); SrbSetDataTransferLength(Srb, requiredBufferSize); @@ -2211,13 +2352,19 @@ AtaInquiryRequest( break; } case VPD_BLOCK_LIMITS: { - if (srbDataBufferLength < 0x14) { - Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; - status = STOR_STATUS_INVALID_PARAMETER; + if (srbDataBufferLength < sizeof(VPD_BLOCK_LIMITS_PAGE)) { + SrbSetSrbStatus(Srb, SRB_STATUS_DATA_OVERRUN); + SrbSetDataTransferLength(Srb, 0); + status = STOR_STATUS_BUFFER_TOO_SMALL; } else { PVPD_BLOCK_LIMITS_PAGE outputBuffer = (PVPD_BLOCK_LIMITS_PAGE)srbDataBuffer; - outputBuffer->DeviceType = ChannelExtension->DeviceExtension->DeviceParameters.ScsiDeviceType; //DIRECT_ACCESS_DEVICE; ; + // + // Resetting the VPD page buffer + // + AhciZeroMemory((PCHAR)outputBuffer, sizeof(VPD_BLOCK_LIMITS_PAGE)); + + outputBuffer->DeviceType = ChannelExtension->DeviceExtension->DeviceParameters.ScsiDeviceType; outputBuffer->DeviceTypeQualifier = 0; outputBuffer->PageCode = VPD_BLOCK_LIMITS; @@ -2225,9 +2372,8 @@ AtaInquiryRequest( // leave outputBuffer->Descriptors[0 : 15] as '0' indicating 'not supported' for those fields. // - if ( (srbDataBufferLength >= 0x24) && - IsDeviceSupportsTrim(ChannelExtension) ) { - // not worry about multiply overflow as max of DsmCapBlockCount is min(AHCI_MAX_TRANSFER_LENGTH / ATA_BLOCK_SIZE, 0xFFFF) + if ( IsDeviceSupportsTrim(ChannelExtension) ) { + // not worry about multiply overflow as max of DsmCapBlockCount is min(AHCI_MAX_TRANSFER_LENGTH_DEFAULT / ATA_BLOCK_SIZE, 0xFFFF) // calculate how many LBA ranges can be associated with one DSM - Trim command ULONG maxLbaRangeEntryCountPerCmd = ChannelExtension->DeviceExtension[0].DeviceParameters.DsmCapBlockCount * (ATA_BLOCK_SIZE / sizeof(ATA_LBA_RANGE)); // calculate how many LBA can be associated with one DSM - Trim command @@ -2250,8 +2396,9 @@ AtaInquiryRequest( // keep original 'Srb->DataTransferLength' value. } else { - outputBuffer->PageLength[1] = 0x10; - SrbSetDataTransferLength(Srb, 0x14); + outputBuffer->PageLength[1] = 0x3C; + + // keep original 'Srb->DataTransferLength' value. } Srb->SrbStatus = SRB_STATUS_SUCCESS; @@ -2266,7 +2413,7 @@ AtaInquiryRequest( } else { PVPD_BLOCK_DEVICE_CHARACTERISTICS_PAGE outputBuffer = (PVPD_BLOCK_DEVICE_CHARACTERISTICS_PAGE)srbDataBuffer; - outputBuffer->DeviceType = ChannelExtension->DeviceExtension->DeviceParameters.ScsiDeviceType; //DIRECT_ACCESS_DEVICE; ; + outputBuffer->DeviceType = ChannelExtension->DeviceExtension->DeviceParameters.ScsiDeviceType; outputBuffer->DeviceTypeQualifier = 0; outputBuffer->PageCode = VPD_BLOCK_DEVICE_CHARACTERISTICS; outputBuffer->PageLength = 0x3C; // must be 0x3C per spec @@ -2286,7 +2433,7 @@ AtaInquiryRequest( } else { PVPD_LOGICAL_BLOCK_PROVISIONING_PAGE outputBuffer = (PVPD_LOGICAL_BLOCK_PROVISIONING_PAGE)srbDataBuffer; - outputBuffer->DeviceType = ChannelExtension->DeviceExtension->DeviceParameters.ScsiDeviceType; //DIRECT_ACCESS_DEVICE; ; + outputBuffer->DeviceType = ChannelExtension->DeviceExtension->DeviceParameters.ScsiDeviceType; outputBuffer->DeviceTypeQualifier = 0; outputBuffer->PageCode = VPD_LOGICAL_BLOCK_PROVISIONING; outputBuffer->PageLength[1] = 0x04; // 8 bytes data in total @@ -2319,7 +2466,7 @@ AtaInquiryRequest( PVPD_SERIAL_NUMBER_PAGE outputBuffer = (PVPD_SERIAL_NUMBER_PAGE)srbDataBuffer; UCHAR i; - outputBuffer->DeviceType = ChannelExtension->DeviceExtension->DeviceParameters.ScsiDeviceType; //DIRECT_ACCESS_DEVICE; ; + outputBuffer->DeviceType = ChannelExtension->DeviceExtension->DeviceParameters.ScsiDeviceType; outputBuffer->DeviceTypeQualifier = 0; outputBuffer->PageCode = VPD_SERIAL_NUMBER; outputBuffer->PageLength = 20; // 24 bytes data in total @@ -2347,7 +2494,7 @@ AtaInquiryRequest( AhciZeroMemory((PCHAR)outputBuffer, sizeof(VPD_ATA_INFORMATION_PAGE)); - outputBuffer->DeviceType = ChannelExtension->DeviceExtension->DeviceParameters.ScsiDeviceType; //DIRECT_ACCESS_DEVICE; ; + outputBuffer->DeviceType = ChannelExtension->DeviceExtension->DeviceParameters.ScsiDeviceType; outputBuffer->DeviceTypeQualifier = 0; outputBuffer->PageCode = VPD_ATA_INFORMATION; outputBuffer->PageLength[0] = 0x02; @@ -2400,6 +2547,40 @@ AtaInquiryRequest( break; } + case VPD_EXTENDED_INQUIRY_DATA: { + if (srbDataBufferLength < RTL_SIZEOF_THROUGH_FIELD(VPD_EXTENDED_INQUIRY_DATA_PAGE, MaxSupportedSenseDataLength)) { + Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; + status = STOR_STATUS_INVALID_PARAMETER; + } else { + PVPD_EXTENDED_INQUIRY_DATA_PAGE outputBuffer = (PVPD_EXTENDED_INQUIRY_DATA_PAGE)srbDataBuffer; + + AhciZeroMemory((PCHAR)outputBuffer, srbDataBufferLength); + + // Below fields are required for Read Buffer command(Error history mode) support in SAT-4. + outputBuffer->DeviceType = ChannelExtension->DeviceExtension->DeviceParameters.ScsiDeviceType; + outputBuffer->DeviceTypeQualifier = 0; + outputBuffer->PageCode = VPD_EXTENDED_INQUIRY_DATA; + outputBuffer->PageLength[0] = 0; + outputBuffer->PageLength[1] = 0x3C; // must be 0x3C per spec. + outputBuffer->HssRelef = 1; + + // Populate below fields according to SAT-2. + if (ChannelExtension->DeviceExtension->IdentifyDeviceData->NVCacheCapabilities.NVCachePowerModeEnabled == 1 || + ChannelExtension->DeviceExtension->IdentifyDeviceData->AdditionalSupported.NonVolatileWriteCache == 1) { + outputBuffer->NvSup = 1; + } + if (ChannelExtension->DeviceExtension->IdentifyDeviceData->CommandSetActive.WriteCache || + ChannelExtension->DeviceExtension->IdentifyDeviceData->CommandSetActive.LookAhead) { + outputBuffer->VSup = 1; + } + + Srb->SrbStatus = SRB_STATUS_SUCCESS; + status = STOR_STATUS_SUCCESS; + } + + break; + } + default: Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; status = STOR_STATUS_INVALID_PARAMETER; @@ -2905,7 +3086,7 @@ Return Value: } else { if (buffer != NULL) { - AhciFreeDmaBuffer((PVOID)ChannelExtension->AdapterExtension, trimContext->AllocatedBufferLength, buffer); + AhciFreeDmaBuffer((PVOID)ChannelExtension->AdapterExtension, trimContext->AllocatedBufferLength, buffer, srbExtension->DataBufferPhysicalAddress); } if (trimContext != NULL) { @@ -2938,6 +3119,7 @@ AtaUnmapRequest ( PVOID srbDataBuffer = SrbGetDataBuffer(Srb); ULONG srbDataBufferLength = SrbGetDataTransferLength(Srb); + STOR_PHYSICAL_ADDRESS bufferPhysicalAddress = {0}; unmapList = (PUNMAP_LIST_HEADER)srbDataBuffer; @@ -2957,8 +3139,6 @@ AtaUnmapRequest ( } else { // some preparation work before actually starting to process the request ULONG i = 0; - ULONG length = 0; - STOR_PHYSICAL_ADDRESS bufferPhysicalAddress; status = StorPortAllocatePool(ChannelExtension->AdapterExtension, sizeof(ATA_TRIM_CONTEXT), AHCI_POOL_TAG, (PVOID*)&trimContext); if ( (status != STOR_STATUS_SUCCESS) || (trimContext == NULL) ) { @@ -3007,7 +3187,7 @@ AtaUnmapRequest ( } // 1.4 allocate buffer, this buffer will be used to store ATA LBA Ranges for DSM command - status = AhciAllocateDmaBuffer((PVOID)ChannelExtension->AdapterExtension, trimContext->AllocatedBufferLength, (PVOID*)&buffer); + status = AhciAllocateDmaBuffer((PVOID)ChannelExtension->AdapterExtension, trimContext->AllocatedBufferLength, (PVOID*)&buffer, &bufferPhysicalAddress); if ( (status != STOR_STATUS_SUCCESS) || (buffer == NULL) ) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; @@ -3021,10 +3201,10 @@ AtaUnmapRequest ( srbExtension->AtaFunction = ATA_FUNCTION_ATA_COMMAND; srbExtension->Flags |= ATA_FLAGS_DATA_OUT; srbExtension->DataBuffer = buffer; + srbExtension->DataBufferPhysicalAddress.QuadPart = bufferPhysicalAddress.QuadPart; srbExtension->DataTransferLength = trimContext->AllocatedBufferLength; srbExtension->CompletionContext = (PVOID)trimContext; - bufferPhysicalAddress = StorPortGetPhysicalAddress(ChannelExtension->AdapterExtension, NULL, buffer, &length); srbExtension->LocalSgl.NumberOfElements = 1; srbExtension->LocalSgl.List[0].PhysicalAddress.LowPart = bufferPhysicalAddress.LowPart; srbExtension->LocalSgl.List[0].PhysicalAddress.HighPart = bufferPhysicalAddress.HighPart; @@ -3039,7 +3219,7 @@ AtaUnmapRequest ( // the process failed before DSM command can be sent. Free allocated resources. if (status != STOR_STATUS_SUCCESS) { if (buffer != NULL) { - AhciFreeDmaBuffer((PVOID)ChannelExtension->AdapterExtension, trimContext->AllocatedBufferLength, buffer); + AhciFreeDmaBuffer((PVOID)ChannelExtension->AdapterExtension, trimContext->AllocatedBufferLength, buffer, bufferPhysicalAddress); } if (trimContext != NULL) { @@ -3120,176 +3300,1095 @@ AtaSecurityProtocolRequest ( return STOR_STATUS_SUCCESS; } - + +VOID UpdateFirmwareIoctlReturnCode( + _In_ PSTORAGE_REQUEST_BLOCK Srb + ) +{ + PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); + PSRB_IO_CONTROL srbControl = (PSRB_IO_CONTROL)SrbGetDataBuffer(Srb); + + if (srbControl == NULL) { + return; + } + + if (srbExtension->AtaError & IDE_ERROR_CRC_ERROR) { + + // bit 7: Interface CRC error. + srbControl->ReturnCode = FIRMWARE_STATUS_INTERFACE_CRC_ERROR; + + } else if (srbExtension->AtaError & IDE_ERROR_DATA_ERROR) { + + // bit 6: Uncorrectable Error. + srbControl->ReturnCode = FIRMWARE_STATUS_DEVICE_ERROR; + + } else if (srbExtension->AtaError & IDE_ERROR_MEDIA_CHANGE) { + + // bit 5: Media Changed (legacy). + srbControl->ReturnCode = FIRMWARE_STATUS_MEDIA_CHANGE; + + } else if (srbExtension->AtaError & IDE_ERROR_ID_NOT_FOUND) { + + // bit 4: ID Not Found. + srbControl->ReturnCode = FIRMWARE_STATUS_ID_NOT_FOUND; + + } else if (srbExtension->AtaError & IDE_ERROR_MEDIA_CHANGE_REQ) { + + // bit 3: Media Change Request (legacy). + srbControl->ReturnCode = FIRMWARE_STATUS_MEDIA_CHANGE_REQUEST; + + } else if (srbExtension->AtaError & IDE_ERROR_COMMAND_ABORTED) { + + // bit 2: Command Aborted. + srbControl->ReturnCode = FIRMWARE_STATUS_COMMAND_ABORT; + + } else if (srbExtension->AtaError & IDE_ERROR_END_OF_MEDIA) { + + // bit 1: End of Media (legacy). + srbControl->ReturnCode = FIRMWARE_STATUS_END_OF_MEDIA; + + } else if (srbExtension->AtaError & IDE_ERROR_ILLEGAL_LENGTH) { + + // bit 0: Media Error (legacy). + srbControl->ReturnCode = FIRMWARE_STATUS_ILLEGAL_LENGTH; + + } else { + + // Should not be here, need investigation if get here. + NT_ASSERT(FALSE); + srbControl->ReturnCode = FIRMWARE_STATUS_ERROR; + } + + return; +} + + +VOID +AtaFirmwareDownloadCompletion ( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_ PSTORAGE_REQUEST_BLOCK Srb + ) +{ + if (Srb->SrbStatus != SRB_STATUS_SUCCESS) { + + PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); + PSRB_IO_CONTROL srbControl = (PSRB_IO_CONTROL)SrbGetDataBuffer(Srb); + + UpdateFirmwareIoctlReturnCode(Srb); + + StorPortEtwEvent8(ChannelExtension->AdapterExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + AhciEtwEventUnitFirmwareDownloadComplete, + L"Firmware Download Completion", + STORPORT_ETW_EVENT_KEYWORD_IO, + StorportEtwLevelError, + StorportEtwEventOpcodeInfo, + (PSCSI_REQUEST_BLOCK)Srb, + L"SrbStatus", + Srb->SrbStatus, + L"ReturnCode", + (srbControl != NULL) ? (srbControl->ReturnCode) : (0), + L"FeaturesReg", + srbExtension->TaskFile.Current.bFeaturesReg, + L"SectorCountReg", + srbExtension->TaskFile.Current.bSectorCountReg, + L"DriveHeadReg", + srbExtension->TaskFile.Current.bDriveHeadReg, + L"CommandReg", + srbExtension->TaskFile.Current.bCommandReg, + NULL, + 0, + NULL, + 0); + + } + + return; +} + +VOID +AtaWriteBufferFirmwareDownload ( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_ PSTORAGE_REQUEST_BLOCK Srb, + _In_ PCDB Cdb + ) +{ + PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); + PSRB_IO_CONTROL srbControl = NULL; + ULONG bufferOffset = 0; + ULONG dataLength = 0; + UCHAR commandReg = 0; + BOOLEAN invalidCommand = FALSE; + + srbControl = (PSRB_IO_CONTROL)SrbGetDataBuffer(Srb); + + bufferOffset = (ULONG)Cdb->WRITE_BUFFER.BufferOffset[0] << 16 | + (ULONG)Cdb->WRITE_BUFFER.BufferOffset[1] << 8 | + (ULONG)Cdb->WRITE_BUFFER.BufferOffset[2]; + + dataLength = (ULONG)Cdb->WRITE_BUFFER.ParameterListLength[0] << 16 | + (ULONG)Cdb->WRITE_BUFFER.ParameterListLength[1] << 8 | + (ULONG)Cdb->WRITE_BUFFER.ParameterListLength[2]; + + if (((bufferOffset % ATA_BLOCK_SIZE) != 0) || + ((dataLength % ATA_BLOCK_SIZE) != 0)) { + // Report invalid command. + AhciSetSenseData(Srb, SRB_STATUS_INVALID_REQUEST, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ADSENSE_INVALID_CDB, 0); + srbControl->ReturnCode = FIRMWARE_STATUS_INVALID_PARAMETER; + invalidCommand = TRUE; + goto exit; + } + + // Convert offset and data length using unit of Block Size. + bufferOffset /= ATA_BLOCK_SIZE; + dataLength /= ATA_BLOCK_SIZE; + + if ((dataLength > ChannelExtension->DeviceExtension->FirmwareUpdate.DmMaxTransferBlocks) || + (dataLength < ChannelExtension->DeviceExtension->FirmwareUpdate.DmMinTransferBlocks)) { + // Report invalid command. + AhciSetSenseData(Srb, SRB_STATUS_INVALID_REQUEST, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ADSENSE_INVALID_CDB, 0); + srbControl->ReturnCode = FIRMWARE_STATUS_INVALID_PARAMETER; + invalidCommand = TRUE; + goto exit; + } + + if (Cdb->WRITE_BUFFER.Mode == 0x0D) { + if (((Cdb->WRITE_BUFFER.ModeSpecific & SCSI_WRITE_BUFFER_MODE_0D_MODE_SPECIFIC_PO_ACT) == 0) || + ((Cdb->WRITE_BUFFER.ModeSpecific & SCSI_WRITE_BUFFER_MODE_0D_MODE_SPECIFIC_HR_ACT) != 0)) { + // Report invalid command. + AhciSetSenseData(Srb, SRB_STATUS_INVALID_REQUEST, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ADSENSE_INVALID_CDB, 0); + srbControl->ReturnCode = FIRMWARE_STATUS_INVALID_PARAMETER; + invalidCommand = TRUE; + goto exit; + } + } + + NT_ASSERT((SrbGetSrbFlags(Srb) & SRB_FLAGS_UNSPECIFIED_DIRECTION) != 0); + + commandReg = GetFirmwareUpdateCommand(ChannelExtension); + NT_ASSERT(commandReg != IDE_COMMAND_NOT_VALID); + + if (commandReg == IDE_COMMAND_DOWNLOAD_MICROCODE_DMA) { + srbExtension->Flags |= ATA_FLAGS_USE_DMA; + } + + srbExtension->Flags |= ATA_FLAGS_DATA_OUT; + + // Set up taskfile in SrbExtension. + srbExtension->AtaFunction = ATA_FUNCTION_ATA_COMMAND; + + srbExtension->CompletionRoutine = AtaFirmwareDownloadCompletion; + + SetCommandReg((&srbExtension->TaskFile.Current), commandReg); + + SetFeaturesReg((&srbExtension->TaskFile.Current), 0x0E); + + SetSectorCount((&srbExtension->TaskFile.Current), (UCHAR)dataLength); // low byte of transfer length + SetSectorNumber((&srbExtension->TaskFile.Current), (UCHAR)(dataLength >> 8)); // high byte of transfer length + + SetCylinderLow((&srbExtension->TaskFile.Current), (UCHAR)bufferOffset); // low byte of buffer offset + SetCylinderHigh((&srbExtension->TaskFile.Current), (UCHAR)(bufferOffset >> 8)); // high byte of buffer offset + +exit: + + // If the ATA command completes without error and the ATA device returns a COUNT field set to 01h, then the + // SATL should send additional ATA download commands. + + // If the ATA command completes without error and the ATA device returns a COUNT field set to 03h, then the + // SATL shall remember that the new microcode shall be activated by whichever event in table 48 occurs first. + + if (invalidCommand) { + + StorPortEtwEvent8(ChannelExtension->AdapterExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + AhciEtwEventUnitFirmwareDownload, + L"Firmware Download", + STORPORT_ETW_EVENT_KEYWORD_IO, + StorportEtwLevelError, + StorportEtwEventOpcodeInfo, + (PSCSI_REQUEST_BLOCK)Srb, + L"BufferOffset", + bufferOffset, + L"DataLength", + dataLength, + L"MaxTransBlocks", + ChannelExtension->DeviceExtension->FirmwareUpdate.DmMaxTransferBlocks, + L"MinTransBlocks", + ChannelExtension->DeviceExtension->FirmwareUpdate.DmMinTransferBlocks, + L"Mode", + Cdb->WRITE_BUFFER.Mode, + L"ModeSpecific", + Cdb->WRITE_BUFFER.ModeSpecific, + L"CommandReg", + commandReg, + L"ReturnCode", + srbControl->ReturnCode); + + } + + + + return; +} + +VOID +AtaFirmwareActivateCompletion ( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_ PSTORAGE_REQUEST_BLOCK Srb + ) +{ + // + // When a new firmware is activated successfully, the next INQUIRY command should trigger + // the cached Identify Device Data to be updated. + // + if (Srb->SrbStatus == SRB_STATUS_SUCCESS) { + ChannelExtension->DeviceExtension->DeviceParameters.StateFlags.NeedUpdateIdentifyDeviceData = 1; + } else { + + PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); + PSRB_IO_CONTROL srbControl = (PSRB_IO_CONTROL)SrbGetDataBuffer(Srb); + + UpdateFirmwareIoctlReturnCode(Srb); + + StorPortEtwEvent8(ChannelExtension->AdapterExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + AhciEtwEventUnitFirmwareActivateComplete, + L"Firmware Activate Completion", + STORPORT_ETW_EVENT_KEYWORD_IO, + StorportEtwLevelError, + StorportEtwEventOpcodeInfo, + (PSCSI_REQUEST_BLOCK)Srb, + L"SrbStatus", + Srb->SrbStatus, + L"ReturnCode", + (srbControl != NULL) ? (srbControl->ReturnCode) : (0), + L"FeaturesReg", + srbExtension->TaskFile.Current.bFeaturesReg, + L"SectorCountReg", + srbExtension->TaskFile.Current.bSectorCountReg, + L"DriveHeadReg", + srbExtension->TaskFile.Current.bDriveHeadReg, + L"CommandReg", + srbExtension->TaskFile.Current.bCommandReg, + NULL, + 0, + NULL, + 0); + + } + + return; +} + + +VOID +AtaWriteBufferFirmwareActivate ( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_ PSTORAGE_REQUEST_BLOCK Srb, + _In_ PCDB Cdb + ) +{ + PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); + UCHAR commandReg; + PSRB_IO_CONTROL srbControl = NULL; + + srbControl = (PSRB_IO_CONTROL)SrbGetDataBuffer(Srb); + + if ((Cdb->WRITE_BUFFER.ModeSpecific != 0) || + (Cdb->WRITE_BUFFER.BufferID != 0) || + (Cdb->WRITE_BUFFER.BufferOffset[0] != 0) || + (Cdb->WRITE_BUFFER.BufferOffset[1] != 0) || + (Cdb->WRITE_BUFFER.BufferOffset[2] != 0) || + (Cdb->WRITE_BUFFER.ParameterListLength[0] != 0) || + (Cdb->WRITE_BUFFER.ParameterListLength[1] != 0) || + (Cdb->WRITE_BUFFER.ParameterListLength[2] != 0)) { + // Report invalid command. + AhciSetSenseData(Srb, SRB_STATUS_INVALID_REQUEST, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ADSENSE_INVALID_CDB, 0); + + srbControl->ReturnCode = FIRMWARE_STATUS_INVALID_PARAMETER; + + StorPortEtwEvent8(ChannelExtension->AdapterExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + AhciEtwEventUnitFirmwareActivate, + L"Firmware Activate", + STORPORT_ETW_EVENT_KEYWORD_IO, + StorportEtwLevelError, + StorportEtwEventOpcodeInfo, + (PSCSI_REQUEST_BLOCK)Srb, + L"Operation Code", + Cdb->WRITE_BUFFER.ModeSpecific, + L"Buffer ID", + Cdb->WRITE_BUFFER.BufferID, + L"Buffer Offset0", + Cdb->WRITE_BUFFER.BufferOffset[0], + L"Buffer Offset1", + Cdb->WRITE_BUFFER.BufferOffset[1], + L"Buffer Offset2", + Cdb->WRITE_BUFFER.BufferOffset[2], + L"ParaList Len0", + Cdb->WRITE_BUFFER.ParameterListLength[0], + L"ParaList Len1", + Cdb->WRITE_BUFFER.ParameterListLength[1], + L"ParaList Len2", + Cdb->WRITE_BUFFER.ParameterListLength[2]); + + } else { + NT_ASSERT((SrbGetSrbFlags(Srb) & SRB_FLAGS_UNSPECIFIED_DIRECTION) == 0); + + commandReg = GetFirmwareUpdateCommand(ChannelExtension); + NT_ASSERT(commandReg != IDE_COMMAND_NOT_VALID); + + if (commandReg == IDE_COMMAND_DOWNLOAD_MICROCODE_DMA) { + srbExtension->Flags |= ATA_FLAGS_USE_DMA; + } + + // Set up taskfile in SrbExtension. + srbExtension->AtaFunction = ATA_FUNCTION_ATA_COMMAND; + srbExtension->CompletionRoutine = AtaFirmwareActivateCompletion; + + SetCommandReg((&srbExtension->TaskFile.Current), commandReg); + SetFeaturesReg((&srbExtension->TaskFile.Current), 0x0F); + } + + return; +} + + +ULONG +AtaWriteBufferRequest ( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_ PSTORAGE_REQUEST_BLOCK Srb, + _In_ PCDB Cdb + ) +{ + if (!IsFirmwareUpdateSupported(ChannelExtension)) { + //the Subcommand 0Eh and 0Fh are supported bit of the DOWNLOAD MICROCODE Capabilities field of ATA Identify Device Data log page 03h (DOWNLOAD MICROCODE OFFSETS + // DEFERRED SUPPORTED bit) is set to zero), + // IDENTIFY DEVICE data log (Log Address 30h) - 03h Supported Capabilities (see A.11.5) A.11.5.3 DOWNLOAD MICROCODE Capabilities + + // then the SATL shall terminate the command with CHECK CONDITION status with the sense key set to ILLEGAL REQUST with the additional sense code set to INVALID FIELD IN CDB. + AhciSetSenseData(Srb, SRB_STATUS_INVALID_REQUEST, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ADSENSE_INVALID_CDB, 0); + + } else if ((Cdb->WRITE_BUFFER.Mode == 0x0D) || + (Cdb->WRITE_BUFFER.Mode == 0x0E)) { + AtaWriteBufferFirmwareDownload(ChannelExtension, Srb, Cdb); + } else if (Cdb->WRITE_BUFFER.Mode == 0x0F) { + AtaWriteBufferFirmwareActivate(ChannelExtension, Srb, Cdb); + } else { + AhciSetSenseData(Srb, SRB_STATUS_INVALID_REQUEST, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ADSENSE_INVALID_CDB, 0); + } + + return STOR_STATUS_SUCCESS; +} + +VOID +AtaGetPhysicalElementStatusCompletion ( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_ PSTORAGE_REQUEST_BLOCK Srb + ) +/*++ + This is the completion point of Get Physical Element Status command for ATA devices. + + This routine translates an ATA output of Get Physical Element Status command into + SCSI format if the command is successful. + +Arguments: + + ChannelExtension + Srb + +Return Value: + None + +--*/ +{ + PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); + + if (Srb->SrbStatus == SRB_STATUS_SUCCESS) { + + ULONG srbDataBufferLength = SrbGetDataTransferLength(Srb); + ULONG transferLength = RequestGetDataTransferLength(Srb); + ULONG tempScsiBufferLength = 0; + ULONG tempAtaBufferLength = 0; + + PATA_GET_PHYSICAL_ELEMENT_STATUS_PARAMETER_DATA ataDataBuffer = (PATA_GET_PHYSICAL_ELEMENT_STATUS_PARAMETER_DATA)srbExtension->DataBuffer; + PPHYSICAL_ELEMENT_STATUS_PARAMETER_DATA scsiDataBuffer = (PPHYSICAL_ELEMENT_STATUS_PARAMETER_DATA)SrbGetDataBuffer(Srb); + + NT_ASSERT((srbExtension->DataBuffer != NULL) && (transferLength >= sizeof(ATA_GET_PHYSICAL_ELEMENT_STATUS_PARAMETER_DATA))); + + // + // Translate ATA get physical element status data to SCSI format. + // + tempScsiBufferLength = sizeof(PHYSICAL_ELEMENT_STATUS_PARAMETER_DATA); + tempAtaBufferLength = sizeof(ATA_GET_PHYSICAL_ELEMENT_STATUS_PARAMETER_DATA); + + REVERSE_BYTES(scsiDataBuffer->DescriptorCount, &ataDataBuffer->NumberOfDescriptors); + REVERSE_BYTES(scsiDataBuffer->ReturnedDescriptorCount, &ataDataBuffer->NumberOfDescriptorsReturned); + REVERSE_BYTES(scsiDataBuffer->ElementIdentifierBeingDepoped, &ataDataBuffer->ElementIdentifierBeingDepoped); + + for (ULONG i = 0; i < ataDataBuffer->NumberOfDescriptorsReturned; ++i) { + + if ((tempScsiBufferLength > srbDataBufferLength) || (tempAtaBufferLength > transferLength)) { + goto Exit; + } + + scsiDataBuffer->Descriptors[i].PhysicalElementType = ataDataBuffer->Descriptors[i].PhysicalElementType; + scsiDataBuffer->Descriptors[i].PhysicalElementHealth = ataDataBuffer->Descriptors[i].PhysicalElementHealth; + REVERSE_BYTES(scsiDataBuffer->Descriptors[i].ElementIdentifier, &ataDataBuffer->Descriptors[i].ElementIdentifier); + REVERSE_BYTES_QUAD(scsiDataBuffer->Descriptors[i].AssociatedCapacity, &ataDataBuffer->Descriptors[i].AssociatedCapacity); + + tempScsiBufferLength += sizeof(PHYSICAL_ELEMENT_STATUS_DATA_DESCRIPTOR); + tempAtaBufferLength += sizeof(ATA_PHYSICAL_ELEMENT_STATUS_DESCRIPTOR); + } + } + +Exit: + + if (srbExtension->DataBuffer != NULL) { + + // srbExtension->CompletionContext contains the allocated buffer size. + AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, (ULONG_PTR)srbExtension->CompletionContext, srbExtension->DataBuffer, srbExtension->DataBufferPhysicalAddress); + srbExtension->DataBuffer = NULL; + } + + if (Srb->SrbStatus != SRB_STATUS_SUCCESS) { + StorPortEtwEvent8(ChannelExtension->AdapterExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + AhciEtwEventUnitGetPhysicalElementStatusComplete, + L"Get Physical Element status", + STORPORT_ETW_EVENT_KEYWORD_IO, + StorportEtwLevelError, + StorportEtwEventOpcodeInfo, + (PSCSI_REQUEST_BLOCK)Srb, + L"SrbStatus", + Srb->SrbStatus, + L"ErrorReg", + ChannelExtension->ReceivedFIS->D2hRegisterFis.Error, + L"StatusReg", + ChannelExtension->ReceivedFIS->D2hRegisterFis.Status, + NULL, + 0, + NULL, + 0, + NULL, + 0, + NULL, + 0, + NULL, + 0 + ); + } + + return; +} + +ULONG +AtaGetPhysicalElementStatusRequest ( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_ PSTORAGE_REQUEST_BLOCK Srb, + _In_ PCDB Cdb + ) +/*++ + This routine translates a SCSI - Get Physical Element Status command into + ATA - Get Physical Element Status command. + +Arguments: + + ChannelExtension + Srb + Cdb + +Return Value: + ULONG - status + +--*/ +{ + ULONG status = STOR_STATUS_SUCCESS; + PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); + PVOID srbDataBuffer = SrbGetDataBuffer(Srb); + ULONG srbDataBufferLength = SrbGetDataTransferLength(Srb); + PVOID physicalElementStatusBuffer = NULL; + ULONG physicalElementStatusBufferSize = 0; + STOR_PHYSICAL_ADDRESS physicalElementStatusPhysicalAddress = {0}; + PAHCI_H2D_REGISTER_FIS cfis = &srbExtension->Cfis; + ULONG tempUlong = 0; + + if ((srbDataBuffer == NULL) || + (srbDataBufferLength < sizeof(PHYSICAL_ELEMENT_STATUS_PARAMETER_DATA))) { + + Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; + status = STOR_STATUS_INVALID_PARAMETER; + goto Done; + } + + srbExtension->AtaFunction = ATA_FUNCTION_ATA_CFIS_PAYLOAD; + srbExtension->Flags |= (ATA_FLAGS_48BIT_COMMAND | ATA_FLAGS_DATA_IN | ATA_FLAGS_USE_DMA); + srbExtension->CompletionRoutine = AtaGetPhysicalElementStatusCompletion; + + // + // Set up ATA command. + // + cfis->Command = IDE_COMMAND_GET_PHYSICAL_ELEMENT_STATUS; + + cfis->Feature7_0 = 0; + cfis->Feature15_8 = (Cdb->GET_PHYSICAL_ELEMENT_STATUS.Filter << 6) | (Cdb->GET_PHYSICAL_ELEMENT_STATUS.ReportType); + + REVERSE_BYTES(&tempUlong, Cdb->GET_PHYSICAL_ELEMENT_STATUS.AllocationLength); + + tempUlong = (tempUlong + (ATA_BLOCK_SIZE - 1)) / ATA_BLOCK_SIZE; + physicalElementStatusBufferSize = tempUlong * ATA_BLOCK_SIZE; + + status = AhciAllocateDmaBuffer(ChannelExtension->AdapterExtension, physicalElementStatusBufferSize, (PVOID*)&physicalElementStatusBuffer, &physicalElementStatusPhysicalAddress); + + if ((status != STOR_STATUS_SUCCESS) || (physicalElementStatusBuffer == NULL) ) { + + Srb->SrbStatus = SRB_STATUS_ERROR; + status = STOR_STATUS_INSUFFICIENT_RESOURCES; + goto Done; + } + + AhciZeroMemory((PCHAR)physicalElementStatusBuffer, physicalElementStatusBufferSize); + + srbExtension->DataBuffer = physicalElementStatusBuffer; + srbExtension->DataBufferPhysicalAddress.QuadPart = physicalElementStatusPhysicalAddress.QuadPart; + srbExtension->DataTransferLength = physicalElementStatusBufferSize; + srbExtension->CompletionContext = (PVOID)physicalElementStatusBufferSize; // preserve the buffer size, it's needed for freeing the memory + + srbExtension->LocalSgl.NumberOfElements = 1; + srbExtension->LocalSgl.List[0].PhysicalAddress.LowPart = physicalElementStatusPhysicalAddress.LowPart; + srbExtension->LocalSgl.List[0].PhysicalAddress.HighPart = physicalElementStatusPhysicalAddress.HighPart; + srbExtension->LocalSgl.List[0].Length = physicalElementStatusBufferSize; // preserve the buffer size, it's needed for freeing the memory + srbExtension->Sgl = &srbExtension->LocalSgl; + + cfis->Count7_0 = (UCHAR)tempUlong; + cfis->Count15_8 = (UCHAR)(tempUlong >> 8); + + REVERSE_BYTES(&tempUlong, Cdb->GET_PHYSICAL_ELEMENT_STATUS.StartingElement); + cfis->LBA7_0 = (UCHAR)((tempUlong & 0x000000ff) >> 0); + cfis->LBA15_8 = (UCHAR)((tempUlong & 0x0000ff00) >> 8); + cfis->LBA23_16 = (UCHAR)((tempUlong & 0x00ff0000) >> 16); + cfis->LBA31_24 = (UCHAR)((tempUlong & 0xff000000) >> 24); + +Done: + + if (status != STOR_STATUS_SUCCESS) { + if (physicalElementStatusBuffer != NULL) { + AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, physicalElementStatusBufferSize, physicalElementStatusBuffer, physicalElementStatusPhysicalAddress); + } + srbExtension->DataBuffer = NULL; + } + + return status; +} + +VOID +AtaRemoveElementAndTruncateCompletion ( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_ PSTORAGE_REQUEST_BLOCK Srb + ) +/*++ + This is the completion point of Remove Element And Truncate command for ATA devices. + +Arguments: + + ChannelExtension + Srb + +Return Value: + None + +--*/ +{ + if (Srb->SrbStatus != SRB_STATUS_SUCCESS) { + + StorPortEtwEvent8(ChannelExtension->AdapterExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + AhciEtwEventUnitRemoveElementAndTruncateComplete, + L"Remove Element And Truncate", + STORPORT_ETW_EVENT_KEYWORD_IO, + StorportEtwLevelError, + StorportEtwEventOpcodeInfo, + (PSCSI_REQUEST_BLOCK)Srb, + L"SrbStatus", + Srb->SrbStatus, + L"ErrorReg", + ChannelExtension->ReceivedFIS->D2hRegisterFis.Error, + L"StatusReg", + ChannelExtension->ReceivedFIS->D2hRegisterFis.Status, + NULL, + 0, + NULL, + 0, + NULL, + 0, + NULL, + 0, + NULL, + 0 + ); + } + + return; +} + +ULONG +AtaRemoveElementAndTruncateRequest ( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_ PSTORAGE_REQUEST_BLOCK Srb, + _In_ PCDB Cdb + ) +/*++ + This routine translates a SCSI - Remove Element and Truncate command into + ATA - Remove Element and Truncate command. + +Arguments: + + ChannelExtension + Srb + Cdb + +Return Value: + ULONG - status + +--*/ +{ + PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); + PAHCI_H2D_REGISTER_FIS cfis = &srbExtension->Cfis; + ULONGLONG tempUlonglong = 0; + + UNREFERENCED_PARAMETER(ChannelExtension); + + // + // According to SAT-5 Repurposing Depopulation Translation: + // - If requested capacity is 0, SATL shall set the ATA REQUESTED MAX field to 0; + // - If requested capacity is 1, remove element and truncate command shall be terminated with a checked condition status + // with the sense key set to illegal request and additional sense code set to INVALID FIELD IN CDB; + // - If requested capacity is greater than 1, SATL shall set ATA REQUESTED MAX field in the ATA REMOVE ELEMENT AND + // TRUNCATE command to the value of the REQUESTED CAPACITY field minus 1. + // + REVERSE_BYTES_QUAD(&tempUlonglong, Cdb->REMOVE_ELEMENT_AND_TRUNCATE.RequestedCapacity); + if (tempUlonglong == 1) { + + AhciSetSenseData(Srb, SRB_STATUS_INVALID_REQUEST, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ADSENSE_INVALID_CDB, 0); + return STOR_STATUS_INVALID_DEVICE_REQUEST; + } + + tempUlonglong = (tempUlonglong == 0) ? (0) : (tempUlonglong - 1); + cfis->LBA7_0 = (UCHAR)((tempUlonglong & 0x00000000000000ff) >> 0); + cfis->LBA15_8 = (UCHAR)((tempUlonglong & 0x000000000000ff00) >> 8); + cfis->LBA23_16 = (UCHAR)((tempUlonglong & 0x0000000000ff0000) >> 16); + cfis->LBA31_24 = (UCHAR)((tempUlonglong & 0x00000000ff000000) >> 24); + cfis->LBA39_32 = (UCHAR)((tempUlonglong & 0x000000ff00000000) >> 32); + cfis->LBA47_40 = (UCHAR)((tempUlonglong & 0x0000ff0000000000) >> 40); + + srbExtension->AtaFunction = ATA_FUNCTION_ATA_CFIS_PAYLOAD; + srbExtension->Flags |= ATA_FLAGS_48BIT_COMMAND; + srbExtension->CompletionRoutine = AtaRemoveElementAndTruncateCompletion; + + // + // Set up ATA command. + // + cfis->Command = IDE_COMMAND_REMOVE_ELEMENT_AND_TRUNCATE; + + cfis->Feature7_0 = Cdb->REMOVE_ELEMENT_AND_TRUNCATE.ElementIdentifier[1]; + cfis->Feature15_8 = Cdb->REMOVE_ELEMENT_AND_TRUNCATE.ElementIdentifier[0]; + cfis->Count7_0 = Cdb->REMOVE_ELEMENT_AND_TRUNCATE.ElementIdentifier[3]; + cfis->Count15_8 = Cdb->REMOVE_ELEMENT_AND_TRUNCATE.ElementIdentifier[2]; + + return STOR_STATUS_SUCCESS; +} + +VOID +AtaGetDeviceCurrentInternalStatusDataHeaderCompletion( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_ PSTORAGE_REQUEST_BLOCK Srb + ) +/*++ + This is the completion point of Read Log Ext command that is used to read device + internal data header for ATA devices. + +Arguments: + + ChannelExtension - Channel Extension. + + Srb - Supplies a pointer to the srb request. + +Return Value: + + None. + +--*/ +{ + if (Srb->SrbStatus == SRB_STATUS_SUCCESS) { + + PCURRENT_DEVICE_INTERNAL_STATUS_LOG ataCurrentDeviceInternalStatusDataHeader = NULL; + PERROR_HISTORY_DIRECTORY scsiErrorHistoryDirectory = (PERROR_HISTORY_DIRECTORY)SrbGetDataBuffer(Srb); + + ULONG transferLength = RequestGetDataTransferLength(Srb); + UCHAR tmpT10VendorId[] = "ATA "; + + NT_ASSERT((scsiErrorHistoryDirectory != NULL) && + (transferLength >= sizeof(CURRENT_DEVICE_INTERNAL_STATUS_LOG)) && + (SrbGetDataTransferLength(Srb) >= sizeof(ERROR_HISTORY_DIRECTORY))); + + AhciZeroMemory((PVOID)scsiErrorHistoryDirectory, sizeof(ERROR_HISTORY_DIRECTORY)); + + if (transferLength < sizeof(CURRENT_DEVICE_INTERNAL_STATUS_LOG)) { + NT_ASSERT(FALSE); + return; + } + + // + // Parse current device internal status data header(page 0) content. + // + ataCurrentDeviceInternalStatusDataHeader = (PCURRENT_DEVICE_INTERNAL_STATUS_LOG)ChannelExtension->DeviceExtension->ReadLogExtPageData; + NT_ASSERT(ataCurrentDeviceInternalStatusDataHeader->LogAddress == CURRENT_DEVICE_INTERNAL_STATUS_DATA_LOG_ADDRESS); + + NT_ASSERT(sizeof(tmpT10VendorId) >= SCSI_VENDOR_ID_LENGTH); + StorPortCopyMemory(scsiErrorHistoryDirectory->T10VendorId, tmpT10VendorId, SCSI_VENDOR_ID_LENGTH); + + if (ataCurrentDeviceInternalStatusDataHeader->Area3LastLogPage > 0) { + + USHORT errorhistoryDirectoryEntryLength = (USHORT)sizeof(ERROR_HISTORY_DIRECTORY_ENTRY); + ULONG maxAvailableLength = (ataCurrentDeviceInternalStatusDataHeader->Area3LastLogPage + 1UL) * IDE_GP_LOG_SECTOR_SIZE; + REVERSE_BYTES_SHORT(scsiErrorHistoryDirectory->DirectoryLength, &errorhistoryDirectoryEntryLength); + scsiErrorHistoryDirectory->ErrorHistoryDirectoryList[0].SupportedBufferId = BUFFER_ID_RETURN_ERROR_HISTORY_MINIMUM_THRESHOLD; + scsiErrorHistoryDirectory->ErrorHistoryDirectoryList[0].BufferFormat = BUFFER_FORMAT_CURRENT_INTERNAL_STATUS_DATA; + scsiErrorHistoryDirectory->ErrorHistoryDirectoryList[0].BufferSource = BUFFER_SOURCE_CREATED_DUE_TO_CURRENT_COMMAND; + REVERSE_BYTES(scsiErrorHistoryDirectory->ErrorHistoryDirectoryList[0].MaxAvailableLength, &maxAvailableLength); + } + + SrbSetDataTransferLength(Srb, sizeof(ERROR_HISTORY_DIRECTORY)); + + } else { + + PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); + + StorPortEtwEvent8(ChannelExtension->AdapterExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + AhciEtwEventUnitGetInternalStatusDataHeaderComplete, + L"Internal Status Data Header", + STORPORT_ETW_EVENT_KEYWORD_IO, + StorportEtwLevelError, + StorportEtwEventOpcodeInfo, + (PSCSI_REQUEST_BLOCK)Srb, + L"SrbStatus", + Srb->SrbStatus, + L"ErrorReg", + srbExtension->TaskFile.Current.bFeaturesReg, + L"StatusReg", + srbExtension->TaskFile.Current.bCommandReg, + NULL, + 0, + NULL, + 0, + NULL, + 0, + NULL, + 0, + NULL, + 0 + ); + } + + return; +} + +ULONG +AtaGetDeviceCurrentInternalStatusDataHeader( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_ PSTORAGE_REQUEST_BLOCK Srb, + _In_ PCDB Cdb + ) +/*++ + This routine translates a SCSI - Read Buffer command(Mode: 0x1c Error History Directory) + into ATA - Read Log Ext command to read device current internal status data header. + +Arguments: + + ChannelExtension - Channel Extension. + + Srb - Supplies a pointer to the srb request. + + Cdb - SCSI Command Descriptor Block carried by Srb. + +Return Value: + + ULONG - status. + +--*/ +{ + PVOID srbDataBuffer = SrbGetDataBuffer(Srb); + ULONG srbDataBufferLength = SrbGetDataTransferLength(Srb); + + UNREFERENCED_PARAMETER(Cdb); + + if ((srbDataBuffer == NULL) || (srbDataBufferLength < sizeof(ERROR_HISTORY_DIRECTORY))) { + + Srb->SrbStatus = SRB_STATUS_BAD_SRB_BLOCK_LENGTH; + return STOR_STATUS_INVALID_PARAMETER; + } + + IssueReadLogExtCommand(ChannelExtension, + Srb, + IDE_GP_LOG_CURRENT_DEVICE_INTERNAL_STATUS, + 0, // Starting page number + 1, // Number of sectors + 1, // Features field set to 1 + &ChannelExtension->DeviceExtension->ReadLogExtPageDataPhysicalAddress, + (PVOID)ChannelExtension->DeviceExtension->ReadLogExtPageData, + AtaGetDeviceCurrentInternalStatusDataHeaderCompletion + ); + + return STOR_STATUS_SUCCESS; +} + VOID -AtaWriteBufferFirmwareDownload ( +AtaGetDeviceCurrentInternalStatusDataCompletion( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, - _In_ PSTORAGE_REQUEST_BLOCK Srb, - _In_ PCDB Cdb - ) -{ + _In_ PSTORAGE_REQUEST_BLOCK Srb + ) +/*++ + This is the completion point of Read Log Ext command that is used to read device + internal data for ATA devices. + +Arguments: + + ChannelExtension - Channel Extension. + + Srb - Supplies a pointer to the srb request. + +Return Value: + + None. +--*/ +{ PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); - ULONG bufferOffset = 0; - ULONG dataLength = 0; - UCHAR commandReg; + ULONG_PTR currentLogPageOffset = (ULONG_PTR)srbExtension->CompletionContext; + BOOLEAN requestFailed = FALSE; - bufferOffset = (ULONG)Cdb->WRITE_BUFFER.BufferOffset[0] << 16 | - (ULONG)Cdb->WRITE_BUFFER.BufferOffset[1] << 8 | - (ULONG)Cdb->WRITE_BUFFER.BufferOffset[2]; + if (Srb->SrbStatus == SRB_STATUS_SUCCESS) { - dataLength = (ULONG)Cdb->WRITE_BUFFER.ParameterListLength[0] << 16 | - (ULONG)Cdb->WRITE_BUFFER.ParameterListLength[1] << 8 | - (ULONG)Cdb->WRITE_BUFFER.ParameterListLength[2]; + PCURRENT_DEVICE_INTERNAL_STATUS_LOG ataCurrentDeviceInternalStatusDataHeader = NULL; + PCURRENT_INTERNAL_STATUS_PARAMETER_DATA scsiCurrentInternalStatusData = (PCURRENT_INTERNAL_STATUS_PARAMETER_DATA)SrbGetDataBuffer(Srb); - if (((bufferOffset % ATA_BLOCK_SIZE) != 0) || - ((dataLength % ATA_BLOCK_SIZE) != 0)) { - // Report invalid command. - AhciSetSenseData(Srb, SRB_STATUS_INVALID_REQUEST, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ADSENSE_INVALID_CDB, 0); - goto exit; - } + ULONG srbDataBufferLength = SrbGetDataTransferLength(Srb); + ULONG transferLength = RequestGetDataTransferLength(Srb); - // Convert offset and data length using unit of Block Size. - bufferOffset /= ATA_BLOCK_SIZE; - dataLength /= ATA_BLOCK_SIZE; + NT_ASSERT((srbExtension->DataBuffer != NULL) && + (srbExtension->DataTransferLength >= sizeof(CURRENT_DEVICE_INTERNAL_STATUS_LOG)) && + (scsiCurrentInternalStatusData != NULL) && + (srbDataBufferLength >= transferLength)); - if ((dataLength > ChannelExtension->DeviceExtension->FirmwareUpdate.DmMaxTransferBlocks) || - (dataLength < ChannelExtension->DeviceExtension->FirmwareUpdate.DmMinTransferBlocks)) { - // Report invalid command. - AhciSetSenseData(Srb, SRB_STATUS_INVALID_REQUEST, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ADSENSE_INVALID_CDB, 0); - goto exit; - } + NT_ASSERT((transferLength % IDE_GP_LOG_SECTOR_SIZE) == 0); - if (Cdb->WRITE_BUFFER.Mode == 0x0D) { - if (((Cdb->WRITE_BUFFER.ModeSpecific & SCSI_WRITE_BUFFER_MODE_0D_MODE_SPECIFIC_PO_ACT) == 0) || - ((Cdb->WRITE_BUFFER.ModeSpecific & SCSI_WRITE_BUFFER_MODE_0D_MODE_SPECIFIC_HR_ACT) != 0)) { - // Report invalid command. - AhciSetSenseData(Srb, SRB_STATUS_INVALID_REQUEST, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ADSENSE_INVALID_CDB, 0); - goto exit; + if (!IsDumpMode(ChannelExtension->AdapterExtension)) { + AhciZeroMemory((PVOID)scsiCurrentInternalStatusData, srbDataBufferLength); } - } - NT_ASSERT((SrbGetSrbFlags(Srb) & SRB_FLAGS_UNSPECIFIED_DIRECTION) != 0); + // + // Parse current device internal status data header(page 0) and/or copy other pages back to SRB databuffer. + // + ataCurrentDeviceInternalStatusDataHeader = (PCURRENT_DEVICE_INTERNAL_STATUS_LOG)srbExtension->DataBuffer; - commandReg = GetFirmwareUpdateCommand(ChannelExtension); - NT_ASSERT(commandReg != IDE_COMMAND_NOT_VALID); + if (currentLogPageOffset == 0) { - if (commandReg == IDE_COMMAND_DOWNLOAD_MICROCODE_DMA) { - srbExtension->Flags |= ATA_FLAGS_USE_DMA; - } + if (transferLength >= sizeof(CURRENT_DEVICE_INTERNAL_STATUS_LOG)) { - srbExtension->Flags |= ATA_FLAGS_DATA_OUT; + NT_ASSERT(ataCurrentDeviceInternalStatusDataHeader->LogAddress == CURRENT_DEVICE_INTERNAL_STATUS_DATA_LOG_ADDRESS); + NT_ASSERT((ataCurrentDeviceInternalStatusDataHeader->Area2LastLogPage >= ataCurrentDeviceInternalStatusDataHeader->Area1LastLogPage) && + (ataCurrentDeviceInternalStatusDataHeader->Area3LastLogPage >= ataCurrentDeviceInternalStatusDataHeader->Area2LastLogPage)); - // Set up taskfile in SrbExtension. - srbExtension->AtaFunction = ATA_FUNCTION_ATA_COMMAND; + if (!IsDumpMode(ChannelExtension->AdapterExtension)) { - SetCommandReg((&srbExtension->TaskFile.Current), commandReg); + REVERSE_BYTES(scsiCurrentInternalStatusData->IEEECompanyId, &ataCurrentDeviceInternalStatusDataHeader->OrganizationID); + REVERSE_BYTES_SHORT(scsiCurrentInternalStatusData->CurrentInternalStatusDataSetOneLength, &ataCurrentDeviceInternalStatusDataHeader->Area1LastLogPage); + REVERSE_BYTES_SHORT(scsiCurrentInternalStatusData->CurrentInternalStatusDataSetTwoLength, &ataCurrentDeviceInternalStatusDataHeader->Area2LastLogPage); + REVERSE_BYTES_SHORT(scsiCurrentInternalStatusData->CurrentInternalStatusDataSetThreeLength, &ataCurrentDeviceInternalStatusDataHeader->Area3LastLogPage); + scsiCurrentInternalStatusData->NewSavedDataAvailable = ataCurrentDeviceInternalStatusDataHeader->SavedDataAvailable; + scsiCurrentInternalStatusData->SavedDataGenerationNumber = ataCurrentDeviceInternalStatusDataHeader->SavedDataGenerationNumber; + StorPortCopyMemory(scsiCurrentInternalStatusData->CurrentReasonIdentifier, + ataCurrentDeviceInternalStatusDataHeader->ReasonIdentifier, + sizeof(scsiCurrentInternalStatusData->CurrentReasonIdentifier) + ); + } else { - SetFeaturesReg((&srbExtension->TaskFile.Current), 0x0E); + // + // Reorder in place if this is dump mode. + // + REVERSE_LONG(&ataCurrentDeviceInternalStatusDataHeader->OrganizationID); + REVERSE_SHORT(&ataCurrentDeviceInternalStatusDataHeader->Area1LastLogPage); + REVERSE_SHORT(&ataCurrentDeviceInternalStatusDataHeader->Area2LastLogPage); + REVERSE_SHORT(&ataCurrentDeviceInternalStatusDataHeader->Area3LastLogPage); + } + } else { - SetSectorCount((&srbExtension->TaskFile.Current), (UCHAR)dataLength); // low byte of transfer length - SetSectorNumber((&srbExtension->TaskFile.Current), (UCHAR)(dataLength >> 8)); // high byte of transfer length + NT_ASSERT(FALSE); + requestFailed = TRUE; + goto Exit; + } - SetCylinderLow((&srbExtension->TaskFile.Current), (UCHAR)bufferOffset); // low byte of buffer offset - SetCylinderHigh((&srbExtension->TaskFile.Current), (UCHAR)(bufferOffset >> 8)); // high byte of buffer offset + // + // Assume that size of this log hadn't changed since we read the directory. + // + NT_ASSERT(transferLength <= + (sizeof(CURRENT_DEVICE_INTERNAL_STATUS_LOG) + (ULONG)(ataCurrentDeviceInternalStatusDataHeader->Area3LastLogPage) * IDE_GP_LOG_SECTOR_SIZE)); + + if ((transferLength > sizeof(CURRENT_DEVICE_INTERNAL_STATUS_LOG)) && + (srbDataBufferLength >= transferLength) && + (!IsDumpMode(ChannelExtension->AdapterExtension))) { + StorPortCopyMemory(scsiCurrentInternalStatusData->CurrentInternalStatusData, + (PUCHAR)(ataCurrentDeviceInternalStatusDataHeader + 1), + (transferLength - IDE_GP_LOG_SECTOR_SIZE)); + } + } else { + if ((transferLength > sizeof(CURRENT_DEVICE_INTERNAL_STATUS_LOG)) && + (srbDataBufferLength >= transferLength) && + (!IsDumpMode(ChannelExtension->AdapterExtension))) { + StorPortCopyMemory((PUCHAR)scsiCurrentInternalStatusData, + (PUCHAR)ataCurrentDeviceInternalStatusDataHeader, + transferLength); + } + } -exit: + SrbSetDataTransferLength(Srb, transferLength); + } - // If the ATA command completes without error and the ATA device returns a COUNT field set to 01h, then the - // SATL should send additional ATA download commands. +Exit: - // If the ATA command completes without error and the ATA device returns a COUNT field set to 03h, then the - // SATL shall remember that the new microcode shall be activated by whichever event in table 48 occurs first. + if (requestFailed) { - return; -} + StorPortEtwEvent8(ChannelExtension->AdapterExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + AhciEtwEventUnitGetInternalStatusDataComplete, + L"Internal Status Data", + STORPORT_ETW_EVENT_KEYWORD_IO, + StorportEtwLevelError, + StorportEtwEventOpcodeInfo, + (PSCSI_REQUEST_BLOCK)Srb, + L"SrbStatus", + Srb->SrbStatus, + L"ErrorReg", + srbExtension->TaskFile.Current.bFeaturesReg, + L"StatusReg", + srbExtension->TaskFile.Current.bCommandReg, + NULL, + 0, + NULL, + 0, + NULL, + 0, + NULL, + 0, + NULL, + 0 + ); + } -VOID -AtaFirmwareActivateCompletion ( - _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, - _In_ PSTORAGE_REQUEST_BLOCK Srb - ) -{ - // - // When a new firmware is activated successfully, the next INQUIRY command should trigger - // the cached Identify Device Data to be updated. - // - if (Srb->SrbStatus == SRB_STATUS_SUCCESS) { - ChannelExtension->DeviceExtension->DeviceParameters.StateFlags.NeedUpdateIdentifyDeviceData = 1; + if ((srbExtension->DataBuffer != NULL) && !IsDumpMode(ChannelExtension->AdapterExtension)) { + + AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, (ULONG_PTR)srbExtension->DataTransferLength, srbExtension->DataBuffer, srbExtension->DataBufferPhysicalAddress); + srbExtension->DataBuffer = NULL; } return; } - -VOID -AtaWriteBufferFirmwareActivate ( +ULONG +AtaGetDeviceCurrentInternalStatusData( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb, _In_ PCDB Cdb ) +/*++ + This routine translates a SCSI - Read Buffer command(Mode: 0x1c Error History) + into ATA - Read Log Ext command to read device current internal status data. + +Arguments: + + ChannelExtension - Channel Extension. + + Srb - Supplies a pointer to the srb request. + + Cdb - SCSI Command Descriptor Block carried by Srb. + +Return Value: + + ULONG - status. + +--*/ { + ULONG status = STOR_STATUS_SUCCESS; + PVOID srbDataBuffer = SrbGetDataBuffer(Srb); + ULONG srbDataBufferLength = SrbGetDataTransferLength(Srb); + PVOID internalStatusBuffer = NULL; + STOR_PHYSICAL_ADDRESS internalStatusBufferPhysicalAddress = {0}; + ULONG bufferSize = 0; + ULONG bufferSizeDumpMode = 0; + LONGLONG bufferOffset = 0; + ULONG tempUlong = 0; PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); - UCHAR commandReg; - if ((Cdb->WRITE_BUFFER.ModeSpecific != 0) || - (Cdb->WRITE_BUFFER.BufferID != 0) || - (Cdb->WRITE_BUFFER.BufferOffset[0] != 0) || - (Cdb->WRITE_BUFFER.BufferOffset[1] != 0) || - (Cdb->WRITE_BUFFER.BufferOffset[2] != 0) || - (Cdb->WRITE_BUFFER.ParameterListLength[0] != 0) || - (Cdb->WRITE_BUFFER.ParameterListLength[1] != 0) || - (Cdb->WRITE_BUFFER.ParameterListLength[2] != 0)) { - // Report invalid command. - AhciSetSenseData(Srb, SRB_STATUS_INVALID_REQUEST, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ADSENSE_INVALID_CDB, 0); + if ((srbDataBuffer == NULL) || (srbDataBufferLength < (ULONG)FIELD_OFFSET(CURRENT_INTERNAL_STATUS_PARAMETER_DATA, CurrentInternalStatusData))) { - } else { - NT_ASSERT((SrbGetSrbFlags(Srb) & SRB_FLAGS_UNSPECIFIED_DIRECTION) == 0); + Srb->SrbStatus = SRB_STATUS_BAD_SRB_BLOCK_LENGTH; + status = STOR_STATUS_INVALID_PARAMETER; + goto Done; + } - commandReg = GetFirmwareUpdateCommand(ChannelExtension); - NT_ASSERT(commandReg != IDE_COMMAND_NOT_VALID); + REVERSE_BYTES(&tempUlong, Cdb->READ_BUFFER_16.AllocationLength); + bufferSize = tempUlong * IDE_GP_LOG_SECTOR_SIZE; + REVERSE_BYTES_QUAD(&bufferOffset, Cdb->READ_BUFFER_16.BufferOffset); - if (commandReg == IDE_COMMAND_DOWNLOAD_MICROCODE_DMA) { - srbExtension->Flags |= ATA_FLAGS_USE_DMA; - } + if (!IsDumpMode(ChannelExtension->AdapterExtension)) { + status = AhciAllocateDmaBuffer(ChannelExtension->AdapterExtension, bufferSize, &internalStatusBuffer, &internalStatusBufferPhysicalAddress); + if ((status != STOR_STATUS_SUCCESS) || (internalStatusBuffer == NULL) ) { - // Set up taskfile in SrbExtension. - srbExtension->AtaFunction = ATA_FUNCTION_ATA_COMMAND; - srbExtension->CompletionRoutine = AtaFirmwareActivateCompletion; + Srb->SrbStatus = SRB_STATUS_ERROR; + status = STOR_STATUS_INSUFFICIENT_RESOURCES; + goto Done; + } + } else { + internalStatusBufferPhysicalAddress = StorPortGetPhysicalAddress(ChannelExtension->AdapterExtension, NULL, srbDataBuffer, &bufferSizeDumpMode); + if ((internalStatusBufferPhysicalAddress.QuadPart == 0) || (bufferSizeDumpMode == 0)) { - SetCommandReg((&srbExtension->TaskFile.Current), commandReg); - SetFeaturesReg((&srbExtension->TaskFile.Current), 0x0F); + Srb->SrbStatus = SRB_STATUS_ERROR; + status = STOR_STATUS_INSUFFICIENT_RESOURCES; + goto Done; + } + internalStatusBuffer = srbDataBuffer; + bufferSize = min(bufferSize, bufferSizeDumpMode); + bufferSize = ALIGN_DOWN_BY(bufferSize, IDE_GP_LOG_SECTOR_SIZE); } - return; -} + AhciZeroMemory((PCHAR)internalStatusBuffer, bufferSize); + AhciZeroMemory((PCHAR)srbExtension, sizeof(AHCI_SRB_EXTENSION)); + srbExtension->DataTransferLength = bufferSize; + srbExtension->CompletionContext = (PVOID)((ULONG)(bufferOffset / IDE_GP_LOG_SECTOR_SIZE)); -ULONG -AtaWriteBufferRequest ( - _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, - _In_ PSTORAGE_REQUEST_BLOCK Srb, - _In_ PCDB Cdb - ) -{ - if (!IsFirmwareUpdateSupported(ChannelExtension)) { - //the Subcommand 0Eh and 0Fh are supported bit of the DOWNLOAD MICROCODE Capabilities field of ATA Identify Device Data log page 03h (DOWNLOAD MICROCODE OFFSETS - // DEFERRED SUPPORTED bit) is set to zero), - // IDENTIFY DEVICE data log (Log Address 30h) - 03h Supported Capabilities (see A.11.5) A.11.5.3 DOWNLOAD MICROCODE Capabilities + IssueReadLogExtCommand(ChannelExtension, + Srb, + IDE_GP_LOG_CURRENT_DEVICE_INTERNAL_STATUS, + (USHORT)(bufferOffset / IDE_GP_LOG_SECTOR_SIZE), // Starting page number + (USHORT)tempUlong, // Number of sectors + 1, // Features field set to 1 + &internalStatusBufferPhysicalAddress, + (PVOID)internalStatusBuffer, + AtaGetDeviceCurrentInternalStatusDataCompletion + ); - // then the SATL shall terminate the command with CHECK CONDITION status with the sense key set to ILLEGAL REQUST with the additional sense code set to INVALID FIELD IN CDB. - AhciSetSenseData(Srb, SRB_STATUS_INVALID_REQUEST, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ADSENSE_INVALID_CDB, 0); + return status; - } else if ((Cdb->WRITE_BUFFER.Mode == 0x0D) || - (Cdb->WRITE_BUFFER.Mode == 0x0E)) { - AtaWriteBufferFirmwareDownload(ChannelExtension, Srb, Cdb); - } else if (Cdb->WRITE_BUFFER.Mode == 0x0F) { - AtaWriteBufferFirmwareActivate(ChannelExtension, Srb, Cdb); - } else { - AhciSetSenseData(Srb, SRB_STATUS_INVALID_REQUEST, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ADSENSE_INVALID_CDB, 0); +Done: + + if ((internalStatusBuffer != NULL) && !IsDumpMode(ChannelExtension->AdapterExtension)) { + AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, bufferSize, internalStatusBuffer, internalStatusBufferPhysicalAddress); + internalStatusBuffer = NULL; } - return STOR_STATUS_SUCCESS; + return status; } @@ -3884,6 +4983,7 @@ Return Value: deviceParameters->MaximumLun = 0; deviceParameters->ScsiDeviceType = DIRECT_ACCESS_DEVICE; + deviceParameters->StateFlags.RemovableMedia = identifyDeviceData->GeneralConfiguration.RemovableMedia; deviceParameters->MaxDeviceQueueDepth = min(ChannelExtension->MaxPortQueueDepth, (UCHAR)identifyDeviceData->QueueDepth); @@ -3937,15 +5037,15 @@ Return Value: // Fill some firmware support information. // if (identifyDeviceData->MinBlocksPerDownloadMicrocodeMode03 > 0) { - ChannelExtension->DeviceExtension->FirmwareUpdate.DmMinTransferBlocks = (USHORT)min(identifyDeviceData->MinBlocksPerDownloadMicrocodeMode03, AHCI_MAX_TRANSFER_LENGTH / ATA_BLOCK_SIZE); + ChannelExtension->DeviceExtension->FirmwareUpdate.DmMinTransferBlocks = (USHORT)min(identifyDeviceData->MinBlocksPerDownloadMicrocodeMode03, AHCI_MAX_TRANSFER_LENGTH_DEFAULT / ATA_BLOCK_SIZE); } else { ChannelExtension->DeviceExtension->FirmwareUpdate.DmMinTransferBlocks = 1; } if (identifyDeviceData->MaxBlocksPerDownloadMicrocodeMode03 > 0) { - ChannelExtension->DeviceExtension->FirmwareUpdate.DmMaxTransferBlocks = (USHORT)min(identifyDeviceData->MaxBlocksPerDownloadMicrocodeMode03, AHCI_MAX_TRANSFER_LENGTH / ATA_BLOCK_SIZE); + ChannelExtension->DeviceExtension->FirmwareUpdate.DmMaxTransferBlocks = (USHORT)min(identifyDeviceData->MaxBlocksPerDownloadMicrocodeMode03, AHCI_MAX_TRANSFER_LENGTH_DEFAULT / ATA_BLOCK_SIZE); } else { - ChannelExtension->DeviceExtension->FirmwareUpdate.DmMaxTransferBlocks = AHCI_MAX_TRANSFER_LENGTH / ATA_BLOCK_SIZE; + ChannelExtension->DeviceExtension->FirmwareUpdate.DmMaxTransferBlocks = AHCI_MAX_TRANSFER_LENGTH_DEFAULT / ATA_BLOCK_SIZE; } } @@ -4062,7 +5162,7 @@ IOCTLtoATA( nvCacheRequest = (PNVCACHE_REQUEST_BLOCK)(srbControl + 1); if ( ((srbDataBufferLength - sizeof(SRB_IO_CONTROL) - sizeof(NVCACHE_REQUEST_BLOCK)) < nvCacheRequest->DataBufSize) || - (nvCacheRequest->DataBufSize > AHCI_MAX_TRANSFER_LENGTH) ) { + (nvCacheRequest->DataBufSize > AHCI_MAX_TRANSFER_LENGTH_DEFAULT) ) { nvCacheRequest->NRBStatus = NRB_INVALID_PARAMETER; Srb->SrbStatus = SRB_STATUS_BAD_SRB_BLOCK_LENGTH; @@ -4073,7 +5173,6 @@ IOCTLtoATA( // process NVCACHE request according to function switch (nvCacheRequest->Function) { - default: status = NVCacheGeneric (ChannelExtension, Srb); break; @@ -4083,8 +5182,6 @@ IOCTLtoATA( break; } - - case IOCTL_SCSI_MINIPORT_DSM_GENERAL: status = DsmGeneralIoctlProcess(ChannelExtension, Srb); break; @@ -4129,6 +5226,19 @@ IOCTLtoATA( break; } + StorPortEtwEvent2(ChannelExtension->AdapterExtension, + (PSTOR_ADDRESS) &(ChannelExtension->DeviceExtension->DeviceAddress), + AhciEtwEventBuildIO, + L"IOCTLtoATA finished", + STORPORT_ETW_EVENT_KEYWORD_COMMAND_TRACE, + StorportEtwLevelInformational, + StorportEtwEventOpcodeInfo, + (PSCSI_REQUEST_BLOCK)Srb, + L"SrbStatus", + Srb->SrbStatus, + L"SrbControl", + srbControl->ControlCode); + return status; } @@ -4491,6 +5601,7 @@ NVCacheGeneric( ULONG srbDataBufferLength = SrbGetDataTransferLength(Srb); ULONG srbFlags = SrbGetSrbFlags(Srb); PVOID resultBuffer = NULL; + PHYSICAL_ADDRESS resultBufferPhysicalAddress = {0}; srbExtension = GetSrbExtension(Srb); srbControl = (PSRB_IO_CONTROL)SrbGetDataBuffer(Srb); @@ -4586,7 +5697,8 @@ NVCacheGeneric( // status = AhciAllocateDmaBuffer(ChannelExtension->AdapterExtension, sizeof(ATA_TASK_FILE), - &resultBuffer); + &resultBuffer, + &resultBufferPhysicalAddress); if ( (status != STOR_STATUS_SUCCESS) || (resultBuffer == NULL) ) { @@ -4599,6 +5711,7 @@ NVCacheGeneric( AhciZeroMemory((PCHAR)resultBuffer, sizeof(ATA_TASK_FILE)); srbExtension->ResultBuffer = resultBuffer; + srbExtension->ResultBufferPhysicalAddress.QuadPart = resultBufferPhysicalAddress.QuadPart; srbExtension->ResultBufferLength = sizeof(ATA_TASK_FILE); // @@ -4697,7 +5810,7 @@ HybridInfoCompletion( 0); } - AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, ATA_BLOCK_SIZE, srbExtension->DataBuffer); + AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, ATA_BLOCK_SIZE, srbExtension->DataBuffer, srbExtension->DataBufferPhysicalAddress); StorPortDebugPrint(3, "StorAHCI - Hybrid: Port %02d - Hybrid Info log read failed. \n", ChannelExtension->PortNumber); return; } @@ -4815,7 +5928,7 @@ HybridInfoCompletion( hybridInfo->Priorities.DirtyThresholdHigh); } - AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, ATA_BLOCK_SIZE, srbExtension->DataBuffer); + AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, ATA_BLOCK_SIZE, srbExtension->DataBuffer, srbExtension->DataBufferPhysicalAddress); StorPortDebugPrint(3, "StorAHCI - Hybrid: Port %02d - Hybrid Info log read successfully. \n", ChannelExtension->PortNumber); @@ -4897,13 +6010,12 @@ Return Value: } else { PVOID logPageBuffer = NULL; - STOR_PHYSICAL_ADDRESS logPagePhysialAddress; - ULONG tempLength; + STOR_PHYSICAL_ADDRESS logPagePhysicalAddress; // // We need to allocate a new data buffer for log page // - status = AhciAllocateDmaBuffer(ChannelExtension->AdapterExtension, ATA_BLOCK_SIZE, &logPageBuffer); + status = AhciAllocateDmaBuffer(ChannelExtension->AdapterExtension, ATA_BLOCK_SIZE, &logPageBuffer, &logPagePhysicalAddress); if ( (status != STOR_STATUS_SUCCESS) || (logPageBuffer == NULL) ) { // @@ -4916,7 +6028,6 @@ Return Value: } AhciZeroMemory((PCHAR)logPageBuffer, ATA_BLOCK_SIZE); - logPagePhysialAddress = StorPortGetPhysicalAddress(ChannelExtension->AdapterExtension, NULL, (PVOID)logPageBuffer, &tempLength); // // Note that logPageBuffer will be released in completion routine. @@ -4927,7 +6038,7 @@ Return Value: 0, 1, 0, // feature field - &logPagePhysialAddress, + &logPagePhysicalAddress, logPageBuffer, HybridInfoCompletion ); @@ -6480,7 +7591,7 @@ Return Value: } if (buffer != NULL) { - AhciFreeDmaBuffer((PVOID)ChannelExtension->AdapterExtension, evictContext->AllocatedBufferLength, buffer); + AhciFreeDmaBuffer((PVOID)ChannelExtension->AdapterExtension, evictContext->AllocatedBufferLength, buffer, srbExtension->DataBufferPhysicalAddress); } if (evictContext != NULL) { @@ -6517,7 +7628,7 @@ HybridEvict( ULONG i; PVOID buffer = NULL; // DMA buffer allocated for EVICT command - STOR_PHYSICAL_ADDRESS bufferPhysicalAddress; + STOR_PHYSICAL_ADDRESS bufferPhysicalAddress = {0}; logicalSectorSize = BytesPerLogicalSector(&ChannelExtension->DeviceExtension->DeviceParameters); @@ -6644,7 +7755,7 @@ HybridEvict( // // allocate DMA buffer, this buffer will be used to store ATA LBA Ranges for EVICT command // - status = AhciAllocateDmaBuffer((PVOID)ChannelExtension->AdapterExtension, evictContext->AllocatedBufferLength, &buffer); + status = AhciAllocateDmaBuffer((PVOID)ChannelExtension->AdapterExtension, evictContext->AllocatedBufferLength, &buffer, &bufferPhysicalAddress); if ( (status != STOR_STATUS_SUCCESS) || (buffer == NULL) ) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; @@ -6660,10 +7771,10 @@ HybridEvict( srbExtension->AtaFunction = ATA_FUNCTION_ATA_CFIS_PAYLOAD; srbExtension->Flags |= ATA_FLAGS_DATA_OUT; srbExtension->DataBuffer = buffer; + srbExtension->DataBufferPhysicalAddress.QuadPart = bufferPhysicalAddress.QuadPart; srbExtension->DataTransferLength = evictContext->AllocatedBufferLength; srbExtension->CompletionContext = (PVOID)evictContext; - bufferPhysicalAddress = StorPortGetPhysicalAddress(ChannelExtension->AdapterExtension, NULL, buffer, &i); srbExtension->LocalSgl.NumberOfElements = 1; srbExtension->LocalSgl.List[0].PhysicalAddress.LowPart = bufferPhysicalAddress.LowPart; srbExtension->LocalSgl.List[0].PhysicalAddress.HighPart = bufferPhysicalAddress.HighPart; @@ -6703,7 +7814,7 @@ HybridEvict( // if (status != STOR_STATUS_SUCCESS) { if (buffer != NULL) { - AhciFreeDmaBuffer((PVOID)ChannelExtension->AdapterExtension, evictContext->AllocatedBufferLength, buffer); + AhciFreeDmaBuffer((PVOID)ChannelExtension->AdapterExtension, evictContext->AllocatedBufferLength, buffer, bufferPhysicalAddress); } if (evictContext != NULL) { @@ -6736,9 +7847,9 @@ Return Value: --*/ { ULONG status = STOR_STATUS_SUCCESS; - PSRB_IO_CONTROL srbControl; - PFIRMWARE_REQUEST_BLOCK firmwareRequest; - PSTORAGE_FIRMWARE_INFO_V2 firmwareInfo; + PSRB_IO_CONTROL srbControl = NULL; + PFIRMWARE_REQUEST_BLOCK firmwareRequest = NULL; + PSTORAGE_FIRMWARE_INFO_V2 firmwareInfo = NULL; srbControl = (PSRB_IO_CONTROL)SrbGetDataBuffer(Srb); @@ -6748,7 +7859,7 @@ Return Value: srbControl->ReturnCode = FIRMWARE_STATUS_INVALID_PARAMETER; Srb->SrbStatus = SRB_STATUS_BAD_SRB_BLOCK_LENGTH; status = STOR_STATUS_INVALID_PARAMETER; - return status; + goto Exit; } // @@ -6764,7 +7875,7 @@ Return Value: srbControl->ReturnCode = FIRMWARE_STATUS_INVALID_PARAMETER; Srb->SrbStatus = SRB_STATUS_BAD_SRB_BLOCK_LENGTH; status = STOR_STATUS_INVALID_PARAMETER; - return status; + goto Exit; } AhciZeroMemory((PCHAR)firmwareInfo, firmwareRequest->DataBufferLength); @@ -6807,6 +7918,37 @@ Return Value: status = STOR_STATUS_SUCCESS; } +Exit: + + if (status != STOR_STATUS_SUCCESS) { + + StorPortEtwEvent8(ChannelExtension->AdapterExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + AhciEtwEventUnitFirmwareInfo, + L"Get Firmware Info", + STORPORT_ETW_EVENT_KEYWORD_IO, + StorportEtwLevelError, + StorportEtwEventOpcodeInfo, + (PSCSI_REQUEST_BLOCK)Srb, + L"SrbStatus", + Srb->SrbStatus, + L"Status", + status, + L"DataBufferLen", + firmwareRequest->DataBufferLength, + L"ReturnCode", + srbControl->ReturnCode, + L"Version", + (firmwareInfo == NULL) ? (0) : (firmwareInfo->Version), + L"Size", + (firmwareInfo == NULL) ? (0) : (firmwareInfo->Size), + L"UpgradeSupport", + (firmwareInfo == NULL) ? (0) : (firmwareInfo->UpgradeSupport), + NULL, + 0); + + } + return status; } @@ -6830,10 +7972,10 @@ Return Value: --*/ { - ULONG status; + ULONG status = STOR_STATUS_SUCCESS; ULONG srbDataBufferLength = 0; - PSRB_IO_CONTROL srbControl; - PFIRMWARE_REQUEST_BLOCK firmwareRequest; + PSRB_IO_CONTROL srbControl = NULL; + PFIRMWARE_REQUEST_BLOCK firmwareRequest = NULL; srbControl = (PSRB_IO_CONTROL)SrbGetDataBuffer(Srb); srbDataBufferLength = SrbGetDataTransferLength(Srb); @@ -6842,8 +7984,10 @@ Return Value: // A Firmware request must have at least SRB_IO_CONTROL and FIRMWARE_REQUEST_BLOCK in input buffer. // if (srbDataBufferLength < (sizeof(SRB_IO_CONTROL) + sizeof(FIRMWARE_REQUEST_BLOCK))) { + srbControl->ReturnCode = FIRMWARE_STATUS_INVALID_PARAMETER; Srb->SrbStatus = SRB_STATUS_BAD_SRB_BLOCK_LENGTH; - return STOR_STATUS_INVALID_PARAMETER; + status = STOR_STATUS_INVALID_PARAMETER; + goto Exit; } firmwareRequest = (PFIRMWARE_REQUEST_BLOCK)(srbControl + 1); @@ -6851,13 +7995,15 @@ Return Value: if ((ULONGLONG)(srbDataBufferLength) < ((ULONGLONG)firmwareRequest->DataBufferOffset + firmwareRequest->DataBufferLength)) { srbControl->ReturnCode = FIRMWARE_STATUS_INVALID_PARAMETER; Srb->SrbStatus = SRB_STATUS_BAD_SRB_BLOCK_LENGTH; - return STOR_STATUS_INVALID_PARAMETER; + status = STOR_STATUS_INVALID_PARAMETER; + goto Exit; } if (firmwareRequest->Version < FIRMWARE_REQUEST_BLOCK_STRUCTURE_VERSION) { srbControl->ReturnCode = FIRMWARE_STATUS_INVALID_PARAMETER; Srb->SrbStatus = SRB_STATUS_BAD_SRB_BLOCK_LENGTH; - return STOR_STATUS_INVALID_PARAMETER; + status = STOR_STATUS_INVALID_PARAMETER; + goto Exit; } // @@ -6866,7 +8012,8 @@ Return Value: if (firmwareRequest->DataBufferOffset < ALIGN_UP(sizeof(SRB_IO_CONTROL) + sizeof(FIRMWARE_REQUEST_BLOCK), PVOID)) { srbControl->ReturnCode = FIRMWARE_STATUS_INVALID_PARAMETER; Srb->SrbStatus = SRB_STATUS_BAD_SRB_BLOCK_LENGTH; - return STOR_STATUS_INVALID_PARAMETER; + status = STOR_STATUS_INVALID_PARAMETER; + goto Exit; } // @@ -6887,6 +8034,37 @@ Return Value: } +Exit: + + if (status != STOR_STATUS_SUCCESS) { + + StorPortEtwEvent8(ChannelExtension->AdapterExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + AhciEtwEventUnitFirmwareIoctl, + L"Firmware Ioctl", + STORPORT_ETW_EVENT_KEYWORD_IO, + StorportEtwLevelError, + StorportEtwEventOpcodeInfo, + (PSCSI_REQUEST_BLOCK)Srb, + L"SrbDataBufLen", + srbDataBufferLength, + L"SrbStatus", + Srb->SrbStatus, + L"ReturnCode", + srbControl->ReturnCode, + L"FWReqVersion", + (firmwareRequest == NULL) ? (0) : (firmwareRequest->Version), + L"FWReqFunction", + (firmwareRequest == NULL) ? (0) : (firmwareRequest->Function), + L"FWBufOffset", + (firmwareRequest == NULL) ? (0) : (firmwareRequest->DataBufferOffset), + L"Status", + status, + NULL, + 0); + + } + return status; } @@ -7543,7 +8721,10 @@ QueryTemperatureInfoCompletion( NT_ASSERT(FALSE); } - srbExtension->DataBuffer = NULL; + if (srbExtension->DataBuffer != NULL) { + AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, IDE_GP_LOG_SECTOR_SIZE, srbExtension->DataBuffer, srbExtension->DataBufferPhysicalAddress); + srbExtension->DataBuffer = NULL; + } srbExtension->CompletionContext = NULL; srbExtension->CompletionRoutine = NULL; srbExtension->AtaFunction = 0; @@ -7581,7 +8762,6 @@ Return Value: PVOID buffer = NULL; STOR_PHYSICAL_ADDRESS bufferPhysicalAddress = {0}; - ULONG tempLength = 0; srbControl = (PSRB_IO_CONTROL)SrbGetDataBuffer(Srb); srbDataBufferLength = SrbGetDataTransferLength(Srb); @@ -7616,7 +8796,7 @@ Return Value: // // allocate DMA buffer to retrieve temperature statistics log. // - status = AhciAllocateDmaBuffer((PVOID)ChannelExtension->AdapterExtension, IDE_GP_LOG_SECTOR_SIZE, (PVOID*)&buffer); + status = AhciAllocateDmaBuffer((PVOID)ChannelExtension->AdapterExtension, IDE_GP_LOG_SECTOR_SIZE, (PVOID*)&buffer, &bufferPhysicalAddress); if ( (status != STOR_STATUS_SUCCESS) || (buffer == NULL) ) { Srb->SrbStatus = SRB_STATUS_ERROR; @@ -7626,8 +8806,6 @@ Return Value: AhciZeroMemory((PCHAR)buffer, IDE_GP_LOG_SECTOR_SIZE); - bufferPhysicalAddress = StorPortGetPhysicalAddress(ChannelExtension->AdapterExtension, NULL, buffer, &tempLength); - IssueReadLogExtCommand(ChannelExtension, Srb, IDE_GP_LOG_DEVICE_STATISTICS_ADDRESS, @@ -7911,8 +9089,6 @@ Return Value: return status; } - - #if _MSC_VER >= 1200 #pragma warning(pop) #else @@ -7920,4 +9096,3 @@ Return Value: #pragma warning(default:4201) #pragma warning(default:26015) #endif - diff --git a/storage/miniports/storahci/src/common.h b/storage/miniports/storahci/src/common.h index 3b0661315..7a7b8d9cd 100644 --- a/storage/miniports/storahci/src/common.h +++ b/storage/miniports/storahci/src/common.h @@ -44,6 +44,7 @@ Revision History: #define IDE_FEATURE_INVALID 0xFF +#define SCSI_VENDOR_ID_LENGTH (8) // // ATA function code @@ -84,7 +85,6 @@ Revision History: #define ATA_FLAGS_ACTIVE_REFERENCE (1 << 10) // indicates Active Reference needs to be acquired before processing the Srb and released after processing the Srb #define ATA_FLAGS_SENSEDATA_SET (1 << 11) // indicates sense data has been set to the Srb - // // helper macros // @@ -114,9 +114,23 @@ typedef enum _AHCI_ETW_EVENT_IDS { AhciEtwEventUnitHybridEvict = 11, AhciEtwEventUnitHybridSetDirtyThreshold = 12, AhciEtwEventUnitHybridWriteThrough = 13, + AhciEtwEventUnitFirmwareIoctl = 14, + AhciEtwEventUnitFirmwareInfo = 15, + AhciEtwEventUnitFirmwareDownload = 16, + AhciEtwEventUnitFirmwareDownloadComplete = 17, + AhciEtwEventUnitFirmwareActivate = 18, + AhciEtwEventUnitFirmwareActivateComplete = 19, + AhciEtwEventUnitGetPhysicalElementStatusComplete = 20, + AhciEtwEventUnitRemoveElementAndTruncateComplete = 21, + AhciEtwEventUnitGetInternalStatusDataHeaderComplete = 22, + AhciEtwEventUnitGetInternalStatusDataComplete = 23, + AhciEtwEventBuildIO = 24, + AhciEtwEventStartIO = 25, + AhciEtwEventHandleInterrupt = 26, + AhciEtwEventPortReset = 27, + AhciEtwEventIOCompletion = 28 } AHCI_ETW_EVENT_IDS, *PAHCI_ETW_EVENT_IDS; - // // task file register contents // @@ -312,6 +326,14 @@ typedef struct _HYBRID_EVICT_CONTEXT { } HYBRID_EVICT_CONTEXT, *PHYBRID_EVICT_CONTEXT; +__inline +BOOLEAN +IsUnknownDevice( + _In_ PATA_DEVICE_PARAMETERS DeviceParameters + ) +{ + return (DeviceParameters->AtaDeviceType == DeviceUnknown); +} __inline BOOLEAN @@ -601,7 +623,8 @@ __inline AhciAllocateDmaBuffer ( _In_ PVOID AdapterExtension, _In_ ULONG BufferLength, - _Post_writable_byte_size_(BufferLength) PVOID* Buffer + _Post_writable_byte_size_(BufferLength) PVOID* Buffer, + _Out_ PSTOR_PHYSICAL_ADDRESS PhysicalAddress ) { ULONG status; @@ -613,15 +636,16 @@ AhciAllocateDmaBuffer ( maxPhysicalAddress.QuadPart = 0x7FFFFFFF; // (2GB - 1) boundaryPhysicalAddress.QuadPart = 0; + status = StorPortAllocateDmaMemory(AdapterExtension, + BufferLength, + minPhysicalAddress, + maxPhysicalAddress, + boundaryPhysicalAddress, + MmCached, + MM_ANY_NODE_OK, + Buffer, + PhysicalAddress); - status = StorPortAllocateContiguousMemorySpecifyCacheNode(AdapterExtension, - BufferLength, - minPhysicalAddress, - maxPhysicalAddress, - boundaryPhysicalAddress, - MmCached, - MM_ANY_NODE_OK, - Buffer); return status; } @@ -631,14 +655,18 @@ __inline AhciFreeDmaBuffer ( _In_ PVOID AdapterExtension, _In_ ULONG_PTR BufferLength, - _In_reads_bytes_(BufferLength) _Post_invalid_ PVOID Buffer + _In_reads_bytes_(BufferLength) _Post_invalid_ PVOID Buffer, + _In_opt_ STOR_PHYSICAL_ADDRESS PhysicalAddress ) { - ULONG status; - status = StorPortFreeContiguousMemorySpecifyCache(AdapterExtension, - Buffer, - BufferLength, - MmCached); + ULONG status; + + status = StorPortFreeDmaMemory(AdapterExtension, + Buffer, + BufferLength, + MmCached, + PhysicalAddress); + return status; } @@ -862,6 +890,33 @@ AtaReportLunsCommand( _In_ PVOID Context ); +ULONG +AtaGetPhysicalElementStatusRequest ( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_ PSTORAGE_REQUEST_BLOCK Srb, + _In_ PCDB Cdb + ); + +ULONG +AtaRemoveElementAndTruncateRequest ( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_ PSTORAGE_REQUEST_BLOCK Srb, + _In_ PCDB Cdb + ); + +ULONG +AtaGetDeviceCurrentInternalStatusData( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_ PSTORAGE_REQUEST_BLOCK Srb, + _In_ PCDB Cdb + ); + +ULONG +AtaGetDeviceCurrentInternalStatusDataHeader( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_ PSTORAGE_REQUEST_BLOCK Srb, + _In_ PCDB Cdb + ); UCHAR AtaMapError( @@ -965,6 +1020,53 @@ FirmwareIoctlProcess( ); +// +// AHCI Telemetry event related. +// +#define AHCI_TELEMETRY_EVENT_VERSION 0x1 +#define AHCI_TELEMETRY_DRIVER_VERSION 0x1 + +#define AHCI_TELEMETRY_FLAG_NOT_SUPPRESS_LOGGING 0x1 + +typedef enum _AHCI_TELEMETRY_EVENT_ID { + AhciTelemetryEventIdGeneral = 0, + AhciTelemetryEventIdPortReset = 1, + AhciTelemetryEventIdPortRunningStartFail = 2, + AhciTelemetryEventIdPortErrorRecovery = 3, + AhciTelemetryEventIdNonqueuedErrorRecovery = 4, + AhciTelemetryEventIdNCQErrorRecovery = 5, + AhciTelemetryEventIdNCQErrorRecoveryComplete = 6, + AhciTelemetryEventIdResetBus = 7, + AhciTelemetryEventIdResetDeviceRequest = 8, + AhciTelemetryEventIdSurpriseRemove = 9, + AhciTelemetryEventIdLpmAdaptiveSetting = 10, + AhciTelemetryEventIdLpmSettingsModes = 11, + AhciTelemetryEventIdPortStartSuccess = 12, + AhciTelemetryEventIdReservedSlotStuck = 13, + AhciTelemetryEventIdMax = 256 +} AHCI_TELEMETRY_EVENT_ID, *PAHCI_TELEMETRY_EVENT_ID; + +// +// AHCI mark device failure related. +// +#define AHCI_BUS_CHANGE_WARNING_THROTTLE_MASK (0x1 << 0) +#define AHCI_BUS_CHANGE_COUNT_WARNING_THRESHOLD (20) + +#define AHCI_NCQ_ERROR_WARNING_THROTTLE_MASK (0x1 << 1) +#define AHCI_NCQ_ERROR_COUNT_WARNING_THRESHOLD (100) + +#define AHCI_NON_QUEUED_ERROR_WARNING_THROTTLE_MASK (0x1 << 2) +#define AHCI_NON_QUEUED_ERROR_COUNT_WARNING_THRESHOLD (100) + +#define AHCI_DEVICE_STUCK_WARNING_THROTTLE_MASK (0x1 << 3) + +typedef enum _AHCI_DEVICE_FAILURE_REASON { + AhciDeviceFailureUnspecific = 0, + AhciDeviceFailureTooManyBusChange = 1, + AhciDeviceFailureTooManyNCQError = 2, + AhciDeviceFailureTooManyNonQueuedError = 3, + AhciDeviceFailureDeviceStuck = 4 +} AHCI_DEVICE_FAILURE_REASON, *PAHCI_DEVICE_FAILURE_REASON; #if _MSC_VER >= 1200 #pragma warning(pop) @@ -972,4 +1074,3 @@ FirmwareIoctlProcess( #pragma warning(default:4214) #pragma warning(default:4201) #endif - diff --git a/storage/miniports/storahci/src/data.h b/storage/miniports/storahci/src/data.h new file mode 100644 index 000000000..6bec0934a --- /dev/null +++ b/storage/miniports/storahci/src/data.h @@ -0,0 +1,96 @@ +/*++ + +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + data.h + +Abstract: + + Definitions needed by storahci log/diagnostic function. + +Author: + +--*/ + +#pragma once + +// +// Diagnostic data definitions for STORAHCI +// + +#define MAX_EXECUTION_HISTORY_ENTRY_COUNT 200 // 200 entries take up 20KB + +typedef struct _SLOT_MANAGER { + + ULONG HighPriorityAttribute; + + ULONG NCQueueSlice; + ULONG NormalQueueSlice; + ULONG SingleIoSlice; + + ULONG CommandsIssued; + ULONG CommandsToComplete; + + // + // These issued slices are used to determine the type of command + // being programmed to adapter. + // They are used instead of reading PxCI and PxSACT. + // + ULONG NCQueueSliceIssued; + ULONG NormalQueueSliceIssued; + ULONG SingleIoSliceIssued; + + ULONG Reserved; + +} SLOT_MANAGER, *PSLOT_MANAGER; + +typedef struct _EXECUTION_HISTORY { + + ULONG Function; + ULONG IS; + SLOT_MANAGER SlotManager; //SLOT_MANAGER from _AHCI_CHANNEL_EXTENSION + ULONG Px[0x10]; //Px registers value, end to AHCI_SNOTIFICATION -- SNTF + LARGE_INTEGER TimeStamp; + +} EXECUTION_HISTORY, *PEXECUTION_HISTORY; + +typedef struct _ATA_IO_RECORD { + + ULONG SuccessCount; + + ULONG CrcErrorCount; + ULONG MediaErrorCount; + ULONG EndofMediaCount; + ULONG IllegalCommandCount; + ULONG AbortedCommandCount; + ULONG DeviceFaultCount; + + ULONG OtherErrorCount; + + ULONG NcqReadLogErrorCount; // used to record the READ LOG EXT command error count when used for NCQ Error Recovery. + + ULONG PortDriverResetCount; + ULONG TotalResetCount; + +} ATA_IO_RECORD, *PATA_IO_RECORD; + +typedef struct _STORAGE_DIAGNOSTIC_AHCI_EXECUTION_HISTORY { + + ULONG ExecutionHistoryCount; + ULONG ExecutionHistoryIndex; + EXECUTION_HISTORY History[MAX_EXECUTION_HISTORY_ENTRY_COUNT]; + +} STORAGE_DIAGNOSTIC_AHCI_EXECUTION_HISTORY, *PSTORAGE_DIAGNOSTIC_AHCI_EXECUTION_HISTORY; + +typedef struct _STORAGE_DIAGNOSTIC_AHCI_DATA { + + ULONG Version; + ULONG Count; + STORAGE_DIAGNOSTIC_AHCI_EXECUTION_HISTORY ExecutionHistory; + AHCI_PORT Registers; + ATA_IO_RECORD IoRecord; + +} STORAGE_DIAGNOSTIC_AHCI_DATA, *PSTORAGE_DIAGNOSTIC_AHCI_DATA; + diff --git a/storage/miniports/storahci/src/entrypts.c b/storage/miniports/storahci/src/entrypts.c index e9c070c87..efbe22794 100644 --- a/storage/miniports/storahci/src/entrypts.c +++ b/storage/miniports/storahci/src/entrypts.c @@ -21,11 +21,10 @@ Revision History: #pragma warning(push) #endif -#pragma warning(disable:4152) // nonstandard extension, function/data pointer conversion in expression +#pragma warning(disable:4152) // nonstandard extension, function/data pointer conversion in expression #pragma warning(disable:4214) // bit field types other than int #pragma warning(disable:4201) // nameless struct/union - #include "generic.h" @@ -102,12 +101,12 @@ Return Value: hwInitializationData.NumberOfAccessRanges = NUM_ACCESS_RANGES; // - // TODO : It's not a given that SATA on ARM64 will only ever be ACPI enumerated - // This is just for the on-board controller. We will have to come up with a - // solution for PCI bus enuerated on ARM64 + // Support both PCI/ACPI enumerations on ARM64 platform for SATA device. + // Miniport uses flag to indicate storport to query/override the interface type reported here. // -#if defined (_ARM64_) - hwInitializationData.AdapterInterfaceType = ACPIBus; +#if defined(_ARM_) || defined(_ARM64_) + hwInitializationData.FeatureSupport |= STOR_FEATURE_SET_ADAPTER_INTERFACE_TYPE; + hwInitializationData.AdapterInterfaceType = InterfaceTypeUndefined; #else hwInitializationData.AdapterInterfaceType = PCIBus; #endif @@ -122,6 +121,7 @@ Return Value: hwInitializationData.FeatureSupport |= STOR_FEATURE_DEVICE_DESCRIPTOR_FROM_ATA_INFO_VPD; // indicating that port driver forms STORAGE_DEVICE_DESCRIPTOR from ATA Information VPD page rather than INQUIRY data hwInitializationData.FeatureSupport |= STOR_FEATURE_EXTRA_IO_INFORMATION; // Indicating that miniport driver wants SRBEX_DATA_IO_INFO in a SRBEX if available hwInitializationData.FeatureSupport |= STOR_FEATURE_ADAPTER_NOT_REQUIRE_IO_PORT; // Indicating that miniport driver doesn't require IO Port resource for its adapter. + hwInitializationData.FeatureSupport |= STOR_FEATURE_DUMP_INFO; // Indicating that the miniport driver supports the dump info SRBs. // Set required extension sizes. hwInitializationData.DeviceExtensionSize = sizeof(AHCI_ADAPTER_EXTENSION); @@ -206,7 +206,6 @@ Return Values: AdapterExtension->StateFlags.InterruptMessagePerPort = 0; } - StorPortDebugPrint(3, "StorAHCI - Interrupt Mode: Adapter 0x%04X-0x%04X --- %s\n", AdapterExtension->VendorID, AdapterExtension->DeviceID, @@ -242,11 +241,16 @@ AllocateResourcesForAdapter( Therefore the number of Command Headers must be 256/32 = 8. Round cap.NCS to the next multiple of 8 */ { + ULONG alignment = 0x400; // 1K + BOOLEAN dumpMode = IsDumpMode(AdapterExtension); ULONG paddedNCS = 0; ULONG paddedSrbExtensionSize = 0; ULONG nonCachedExtensionSize = 0; + ULONG channelExtensionSize = sizeof(AHCI_CHANNEL_EXTENSION); PVOID portsChannelExtension = NULL; - PCHAR portsUncachedExtension = NULL; + ULONG executionHistorySize = sizeof(EXECUTION_HISTORY) * MAX_EXECUTION_HISTORY_ENTRY_COUNT; + PVOID portsExecutionHistory = NULL; + ULONG_PTR left = 0; ULONG i = 0; ULONG j = 0; @@ -268,33 +272,40 @@ AllocateResourcesForAdapter( sizeof(IDENTIFY_DEVICE_DATA) + // 512 bytes ATA_BLOCK_SIZE + // ReadLogExtPageData --- 512 bytes INQUIRYDATABUFFERSIZE; // Inquiry Data + // round up to KiloBytes if it's not dump mode. this makes sure that NonCachedExtension for next port can align to 1K. - if (!IsDumpMode(AdapterExtension)) { - nonCachedExtensionSize = ((nonCachedExtensionSize - 1) / 0x400 + 1) * 0x400; - // total size for all ports - nonCachedExtensionSize *= PortCount; + if (!dumpMode) { + nonCachedExtensionSize = ((nonCachedExtensionSize - 1) / alignment + 1) * alignment; + + AdapterExtension->NonCachedExtension = StorPortGetUncachedExtension(AdapterExtension, + ConfigInfo, + nonCachedExtensionSize * PortCount); } else { // dump mode, address returned from StorPortGetUncachedExtension() is not guaranteed align with 1K. // adding 1K into length so that we can start from 1K alignment safely. // NOTE: StorPortAllocatePool is not supported in dump stack, so we allocate ChannelExtension from UnCachedExtension as work around. - nonCachedExtensionSize += 0x400 + sizeof(AHCI_CHANNEL_EXTENSION); - } + nonCachedExtensionSize += alignment; - AdapterExtension->NonCachedExtension = StorPortGetUncachedExtension(AdapterExtension, ConfigInfo, nonCachedExtensionSize); + AdapterExtension->NonCachedExtension = StorPortGetUncachedExtension(AdapterExtension, + ConfigInfo, + (nonCachedExtensionSize + channelExtensionSize) * PortCount); + } if (AdapterExtension->NonCachedExtension == NULL) { - // we cannot continue if cannot get nonCachedMemory for Channels. + + // we cannot continue if cannot get nonCachedMemory for Channels. + NT_ASSERT(FALSE); return FALSE; } - AhciZeroMemory((PCHAR)AdapterExtension->NonCachedExtension, nonCachedExtensionSize); + AhciZeroMemory((PCHAR)AdapterExtension->NonCachedExtension, nonCachedExtensionSize * PortCount); // 2.2 allocate resources for Ports which need AHCI_CHANNEL_EXTENSION for each of them. - if (!IsDumpMode(AdapterExtension)) { + if (!dumpMode) { ULONG status = STOR_STATUS_SUCCESS; // allocate pool and zero the content status = StorPortAllocatePool(AdapterExtension, - PortCount * sizeof(AHCI_CHANNEL_EXTENSION), + channelExtensionSize * PortCount, AHCI_POOL_TAG, (PVOID*)&portsChannelExtension); @@ -302,36 +313,49 @@ AllocateResourcesForAdapter( // we cannot continue if cannot get memory for ChannelExtension. return FALSE; } - AhciZeroMemory((PCHAR)portsChannelExtension, PortCount * sizeof(AHCI_CHANNEL_EXTENSION)); - //get the starting pointer - portsUncachedExtension = (PCHAR)AdapterExtension->NonCachedExtension; - } else { - ULONG_PTR left = 0; - // get channelExtension - portsChannelExtension = (PCHAR)AdapterExtension->NonCachedExtension + nonCachedExtensionSize - sizeof(AHCI_CHANNEL_EXTENSION); - //get the starting pointer; align the starting location to 1K. - left = ((ULONG_PTR)AdapterExtension->NonCachedExtension) % 1024; + status = StorPortAllocatePool(AdapterExtension, + (ULONG)(executionHistorySize * PortCount), + AHCI_POOL_TAG, + (PVOID*)&portsExecutionHistory); - if (left > 0) { - portsUncachedExtension = (PCHAR)AdapterExtension->NonCachedExtension + 1024 - left; - } else { - portsUncachedExtension = (PCHAR)AdapterExtension->NonCachedExtension; + if ((status != STOR_STATUS_SUCCESS) || (portsExecutionHistory == NULL)) { + // Continue if cannot get memory for ExecutionHistory. + NT_ASSERT(FALSE); } + } else { + // get channelExtension from UnCachedExtension + portsChannelExtension = (PCHAR)AdapterExtension->NonCachedExtension + (nonCachedExtensionSize * PortCount); } - // reset nonCachedExtensionSize to be the size for one Port, it works for dump case also as PortCount is '1'. - nonCachedExtensionSize /= PortCount; + AhciZeroMemory((PCHAR)portsChannelExtension, channelExtensionSize * PortCount); - // assign allocated memory and uncachedExension into ChannelExtensions + // assign allocated uncachedExension into ChannelExtensions for (i = 0; i <= AdapterExtension->HighestPort; i++) { if ( (AdapterExtension->PortImplemented & (1 << i)) != 0 ) { + PCHAR portsUncachedExtension = (PCHAR)AdapterExtension->NonCachedExtension + nonCachedExtensionSize * j; + // this port is implemented, allocate and initialize extension for the port - AdapterExtension->PortExtension[i] = (PAHCI_CHANNEL_EXTENSION)((PCHAR)portsChannelExtension + sizeof(AHCI_CHANNEL_EXTENSION) * j); + AdapterExtension->PortExtension[i] = (PAHCI_CHANNEL_EXTENSION)((PCHAR)portsChannelExtension + channelExtensionSize * j); AdapterExtension->PortExtension[i]->AdapterExtension = AdapterExtension; AdapterExtension->PortExtension[i]->PortNumber = i; // set ChannelExtension fields that use NonCachedExtension - AdapterExtension->PortExtension[i]->CommandList = (PAHCI_COMMAND_HEADER)(portsUncachedExtension + nonCachedExtensionSize * j); + if (!dumpMode) { + AdapterExtension->PortExtension[i]->CommandList = (PAHCI_COMMAND_HEADER)(portsUncachedExtension); + if (portsExecutionHistory != NULL) { + AdapterExtension->PortExtension[i]->ExecutionHistory = (PEXECUTION_HISTORY)((PCHAR)portsExecutionHistory + executionHistorySize * j); + } + } else { + //get the starting pointer; align the starting location to 1K. + left = ((ULONG_PTR)portsUncachedExtension) % alignment; + + if (left > 0) { + AdapterExtension->PortExtension[i]->CommandList = (PAHCI_COMMAND_HEADER)(portsUncachedExtension + alignment - left); + } else { + AdapterExtension->PortExtension[i]->CommandList = (PAHCI_COMMAND_HEADER)(portsUncachedExtension); + } + } + AdapterExtension->PortExtension[i]->ReceivedFIS = (PAHCI_RECEIVED_FIS)((PCHAR)AdapterExtension->PortExtension[i]->CommandList + sizeof(AHCI_COMMAND_HEADER) * paddedNCS); AdapterExtension->PortExtension[i]->Local.SrbExtension = (PAHCI_SRB_EXTENSION)((PCHAR)AdapterExtension->PortExtension[i]->ReceivedFIS + sizeof(AHCI_RECEIVED_FIS)); AdapterExtension->PortExtension[i]->Sense.SrbExtension = (PAHCI_SRB_EXTENSION)((PCHAR)AdapterExtension->PortExtension[i]->Local.SrbExtension + paddedSrbExtensionSize); @@ -377,7 +401,7 @@ It performs: 2.2 Initialize adapterExtension with version & cap values 3.1 Turn on AE, reset the controller if AE is already set AHCI 1.1 Section 10.1.2 - 1. - "Indicate that system software is AHCI aware by setting GHC.AE to ‘1’." + "Indicate that system software is AHCI aware by setting GHC.AE to '1'." 3.2 Determine which ports are implemented by the HBA AHCI 1.1 Section 10.1.2 - 2. "Determine which ports are implemented by the HBA, by reading the PI register. This bitmap value will aid software in determining how many ports are available and which port registers need to be initialized." @@ -387,10 +411,11 @@ It performs: 4.1 Turn on IE, pending interrupts will be cleared when port starts This has to be done after 3.2 because we need to know the number of channels before we check each PxIS. Verify that none of the PxIS registers are loaded, but take no action - 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. + 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. However, the interrupt handler hasn't been hooked up by StorPort yet, so no interrupts will be handled by software until that happens. - 4.2 Allocate resources for both DMA use and all Channel/Port extensions. - 4.3 Initialize ports and start them. Dump stack will do this when receiving the INQUIRY command + 4.2 Initialize the remaining port configuration settings after getting registry settings. + 4.3 Allocate resources for both DMA use and all Channel/Port extensions. + 4.4 Initialize ports and start them. Dump stack will do this when receiving the INQUIRY command Affected Variables/Registers: AdapterExtension->ABAR_Address @@ -399,7 +424,7 @@ Affected Variables/Registers: AdapterExtension->Version GHC.AE, GHC.IE, GHC.HR - IS and all fields in the HBA’s register memory space except PxFB/PxFBU/PxCLB/PxCLBU that are not HwInit + IS and all fields in the HBA's register memory space except PxFB/PxFBU/PxCLB/PxCLBU that are not HwInit Return Values: The miniport driver returns TRUE if it successfully execute the whole function. @@ -421,11 +446,12 @@ Return Values: ULONG piMask = 0; UCHAR numberOfHighestPort = 0; ULONG portCount = 0; + ULONG portNumber; //Used to enable the AHCI interface AHCI_Global_HBA_CONTROL ghc = {0}; //guids GUID powerSettingChangeGuids[2] = {0}; - + BOOLEAN dumpMode = FALSE; PAHCI_DUMP_CONTEXT dumpContext = (PAHCI_DUMP_CONTEXT)ConfigInfo->MiniportDumpData; UNREFERENCED_PARAMETER(HwContext); @@ -447,48 +473,54 @@ Return Values: adapterExtension->SystemIoBusNumber = ConfigInfo->SystemIoBusNumber; adapterExtension->SlotNumber = ConfigInfo->SlotNumber; - //1.1 Get dump mode + //1.1 Get dump mode adapterExtension->DumpMode = ConfigInfo->DumpMode; + dumpMode = IsDumpMode(adapterExtension); - if (IsDumpMode(adapterExtension)) { + if (dumpMode) { if (dumpContext != NULL) { - // In dump/hibernation mode, need to mark ConfigInfo->MiniportDumpData and any embedded memory buffer(s) in MiniportDumpData - StorPortMarkDumpMemory(AdapterExtension, dumpContext, sizeof(AHCI_DUMP_CONTEXT), 0); + // In dump/hibernation mode, need to mark ConfigInfo->MiniportDumpData + // and any embedded memory buffer(s) in MiniportDumpData + StorPortMarkDumpMemory(AdapterExtension, + dumpContext, + FIELD_OFFSET(AHCI_DUMP_CONTEXT, Ports) + + (dumpContext->PortCount * sizeof(AHCI_DUMP_PORT_CONTEXT)), + 0); } else { NT_ASSERT(FALSE); return SP_RETURN_ERROR; } } - //1.2 Gather Vendor,Device,Revision IDs from PCI - if (!IsDumpMode(adapterExtension)) { + //1.2 Gather Vendor,Device,Revision IDs from PCI + if (!dumpMode) { - // - // TODO : Ensure that we can enumerate using PCI as well as ACPI - // right now this code is for ACPI only. - // -#if !defined(_ARM64_) - ULONG pcicfgLen = 0; - UCHAR pcicfgBuffer[0x30] = {0}; - - pcicfgLen = StorPortGetBusData(adapterExtension, - PCIConfiguration, - ConfigInfo->SystemIoBusNumber, - (ULONG)ConfigInfo->SlotNumber, - (PVOID)pcicfgBuffer, - (ULONG)0x30); - if (pcicfgLen == 0x30) { - PPCI_COMMON_CONFIG pciConfigData = (PPCI_COMMON_CONFIG)pcicfgBuffer; - adapterExtension->VendorID = pciConfigData->VendorID; - adapterExtension->DeviceID = pciConfigData->DeviceID; - adapterExtension->RevisionID = pciConfigData->RevisionID; - // on PCI bus, AHCI Base Address is BAR5. Bits 0-3 defined for other usages, not part of address value. - adapterExtension->AhciBaseAddress = pciConfigData->u.type0.BaseAddresses[5] & (0xFFFFFFF0); - } else { - NT_ASSERT(FALSE); - return SP_RETURN_ERROR; + // + // Enumeration may be via either PCI or ACPI. Bus type is set by storport/miniport. + // Check if we are enumerating via PCI, and retrieve the address appropriately. + // + if (ConfigInfo->AdapterInterfaceType == PCIBus) { + ULONG pcicfgLen = 0; + UCHAR pcicfgBuffer[0x30] = {0}; + + pcicfgLen = StorPortGetBusData(adapterExtension, + PCIConfiguration, + ConfigInfo->SystemIoBusNumber, + (ULONG)ConfigInfo->SlotNumber, + (PVOID)pcicfgBuffer, + (ULONG)0x30); + if (pcicfgLen == 0x30) { + PPCI_COMMON_CONFIG pciConfigData = (PPCI_COMMON_CONFIG)pcicfgBuffer; + adapterExtension->VendorID = pciConfigData->VendorID; + adapterExtension->DeviceID = pciConfigData->DeviceID; + adapterExtension->RevisionID = pciConfigData->RevisionID; + // on PCI bus, AHCI Base Address is BAR5. Bits 0-3 defined for other usages, not part of address value. + adapterExtension->AhciBaseAddress = pciConfigData->u.type0.BaseAddresses[5] & (0xFFFFFFF0); + } else { + NT_ASSERT(FALSE); + return SP_RETURN_ERROR; + } } -#endif } else { adapterExtension->VendorID = dumpContext->VendorID; adapterExtension->DeviceID = dumpContext->DeviceID; @@ -499,22 +531,22 @@ Return Values: adapterExtension->LogFlags = dumpContext->LogFlags; } - //2.1 Initialize adapterExtension with AHCI abar + //2.1 Initialize adapterExtension with AHCI abar abar = GetABARAddress(adapterExtension, ConfigInfo); - //2.1.1 If abar is still NULL after all of that, malformed resources. We aren't going to get very far. + //2.1.1 If abar is still NULL after all of that, malformed resources. We aren't going to get very far. if (abar == NULL) { return SP_RETURN_ERROR; } else { adapterExtension->ABAR_Address = abar; } - //2.2 Initialize adapterExtension with version & cap values + //2.2 Initialize adapterExtension with version & cap values adapterExtension->Version.AsUlong = StorPortReadRegisterUlong(adapterExtension, &abar->VS.AsUlong); adapterExtension->CAP.AsUlong = StorPortReadRegisterUlong(adapterExtension, &abar->CAP.AsUlong); adapterExtension->CAP2.AsUlong = StorPortReadRegisterUlong(adapterExtension, &abar->CAP2.AsUlong); - //3.1 Turn on AE (AHCI 1.1 Section 10.1.2 - 1) + //3.1 Turn on AE (AHCI 1.1 Section 10.1.2 - 1) ghc.AsUlong = StorPortReadRegisterUlong(adapterExtension, &abar->GHC.AsUlong); if (ghc.AE == 1) { if (!AhciAdapterReset(adapterExtension)) { @@ -527,40 +559,54 @@ Return Values: adapterExtension->IS = &abar->IS; - //3.2 Determine which ports are implemented by the HBA (AHCI 1.1 Section 10.1.2 - 2) - adapterExtension->PortImplemented = StorPortReadRegisterUlong(adapterExtension, &abar->PI); + //3.2 Determine which ports are implemented by the HBA + if (!dumpMode) { + // Get implemented ports from PI register (AHCI 1.1 Section 10.1.2 - 2) + adapterExtension->PortImplemented = StorPortReadRegisterUlong(adapterExtension, &abar->PI); + } else { + // Get implemented ports from the dump context + adapterExtension->PortImplemented = 0; - // - // AHCI specification requires that at least one bit is set in PI register. - // In other words, at least one port must be implemented. - // + for (i = 0; i < dumpContext->PortCount; i++) { + adapterExtension->PortImplemented |= 1 << dumpContext->Ports[i].PortNumber; + } + } + + // At least one port must be implemented if (adapterExtension->PortImplemented == 0) { return SP_RETURN_ERROR; } - //3.3 Get biggest port number value and implemented port count. - //3.3.1 get biggest port number value - numberOfHighestPort = AHCI_MAX_PORT_COUNT; - //Check from highest bit to lowest bit for the first highest bit set - for (piMask = (ULONG)(1 << (AHCI_MAX_PORT_COUNT - 1)); piMask != 0; piMask = (ULONG)(piMask >> 1)){ - numberOfHighestPort--; - if ( (adapterExtension->PortImplemented & piMask) != 0) { - break; + //3.3 Get biggest port number value and implemented port count + if (!dumpMode) { + numberOfHighestPort = AHCI_MAX_PORT_COUNT; + //Check from highest bit to lowest bit for the first highest bit set + for (piMask = (ULONG)(1 << (AHCI_MAX_PORT_COUNT - 1)); piMask != 0; piMask = (ULONG)(piMask >> 1)){ + numberOfHighestPort--; + if ((adapterExtension->PortImplemented & piMask) != 0) { + break; + } } - } //numberOfHighestPort now holds the correct value - //3.3.2 get implemented port count - if (!IsDumpMode(adapterExtension)) { for (i = 0; i <= numberOfHighestPort; i++) { - if ( (adapterExtension->PortImplemented & (1 << i)) != 0 ) { + if ((adapterExtension->PortImplemented & (1 << i)) != 0) { portCount++; } } } else { - // in dump environment, only use the desired port. - portCount = 1; + numberOfHighestPort = 0; + for (i = 0; i < dumpContext->PortCount; i++) { + if (dumpContext->Ports[i].PortNumber > numberOfHighestPort) { + numberOfHighestPort = (UCHAR)dumpContext->Ports[i].PortNumber; + } + } + + portCount = dumpContext->PortCount; } + NT_ASSERT(numberOfHighestPort < AHCI_MAX_PORT_COUNT); + adapterExtension->HighestPort = numberOfHighestPort; + // // AHCI specification requires that implemented port count (number of bit set in PI register) // is less than or equal to CAP.NP + 1 @@ -570,8 +616,8 @@ Return Values: // NT_ASSERT(portCount > 0 && portCount <= (adapterExtension->CAP.NP + 1)); - //3.4 Initializing the rest of PORT_CONFIGURATION_INFORMATION - ConfigInfo->MaximumTransferLength = AHCI_MAX_TRANSFER_LENGTH; + //3.4 Initializing the rest of PORT_CONFIGURATION_INFORMATION + ConfigInfo->MaximumTransferLength = AHCI_MAX_TRANSFER_LENGTH_DEFAULT; ConfigInfo->NumberOfPhysicalBreaks = 0x21; // Since "NumberOfPhysicalBreaks" has been used in storage stack as the count of entries, use value of physical breaks plus one. ConfigInfo->AlignmentMask = 1; // ATA devices need WORD alignment ConfigInfo->ScatterGather = TRUE; @@ -584,33 +630,10 @@ Return Values: ConfigInfo->BusResetHoldTime = 0; // StorAHCI wait RESET to be completed by itself, no need for port driver to wait. ConfigInfo->MaxNumberOfIO = portCount * adapterExtension->CAP.NCS; - if (adapterExtension->CAP.S64A) { -#if defined (_ARM64_) - ConfigInfo->Dma64BitAddresses = SCSI_DMA64_MINIPORT_FULL64BIT_NO_BOUNDARY_REQ_SUPPORTED; -#else - ConfigInfo->Dma64BitAddresses = SCSI_DMA64_MINIPORT_SUPPORTED; -#endif - // increase size of SrbExtension to accommodate 64-bit move commands - // and 1 extra Scripts instruction (turn off 64-bit mode) - ConfigInfo->SrbExtensionSize += (ULONG)((ConfigInfo->NumberOfPhysicalBreaks + 2) * 4); - } - ConfigInfo->FeatureSupport |= STOR_ADAPTER_FEATURE_STOP_UNIT_DURING_POWER_DOWN; ConfigInfo->FeatureSupport |= STOR_ADAPTER_FEATURE_RICH_TEMPERATURE_THRESHOLD; - - // 3.4.2 update PortImplemented, HighestPort and portCount if necessary - if (!IsDumpMode(adapterExtension)) { - adapterExtension->HighestPort = numberOfHighestPort; - } else { - - if (dumpContext->DumpPortNumber < AHCI_MAX_PORT_COUNT) { - adapterExtension->PortImplemented = 1 << dumpContext->DumpPortNumber; - adapterExtension->HighestPort = dumpContext->DumpPortNumber; - } else { - NT_ASSERT(FALSE); - return SP_RETURN_ERROR; - } - } + ConfigInfo->FeatureSupport |= STOR_ADAPTER_FEATURE_DEVICE_TELEMETRY; + ConfigInfo->FeatureSupport |= STOR_ADAPTER_DMA_V3_PREFERRED; // // Set interrupt related entry points. @@ -625,7 +648,7 @@ Return Values: // // Allocate MessageGroupAffinity buffer for later use. // - if (!IsDumpMode(adapterExtension)) { + if (!dumpMode) { StorPortAllocatePool(AdapterExtension, sizeof(GROUP_AFFINITY) * (adapterExtension->HighestPort + 1), AHCI_POOL_TAG, @@ -636,8 +659,8 @@ Return Values: ConfigInfo->InterruptSynchronizationMode = InterruptSynchronizeAll; } - //3.5 Register Power Setting Change Notification Guids - if (!IsDumpMode(adapterExtension)) { + //3.5 Register Power Setting Change Notification Guids + if (!dumpMode) { /* 0b2d69d7-a2a1-449c-9680-f91c70521c60 -DIPM/HIPM */ powerSettingChangeGuids[0].Data1 = 0x0b2d69d7; powerSettingChangeGuids[0].Data2 = 0xa2a1; @@ -667,13 +690,25 @@ Return Values: StorPortSetPowerSettingNotificationGuids(AdapterExtension, 2, powerSettingChangeGuids); } - //4.1 Turn on IE, pending interrupts will be cleared when port starts + //4.1 Turn on IE, pending interrupts will be cleared when port starts adapterExtension->LastInterruptedPort = (ULONG)(-1); ghc.IE = 1; StorPortWriteRegisterUlong(adapterExtension, &abar->GHC.AsUlong, ghc.AsUlong); + //4.2.2 Configure DMA 64 bit support settings. + if (adapterExtension->CAP.S64A) { +#if defined (_ARM64_) + ConfigInfo->Dma64BitAddresses = SCSI_DMA64_MINIPORT_FULL64BIT_NO_BOUNDARY_REQ_SUPPORTED; +#else + ConfigInfo->Dma64BitAddresses = SCSI_DMA64_MINIPORT_SUPPORTED; +#endif + + // increase size of SrbExtension to accommodate 64-bit move commands + // and 1 extra Scripts instruction (turn off 64-bit mode) + ConfigInfo->SrbExtensionSize += (ULONG)((ConfigInfo->NumberOfPhysicalBreaks + 2) * 4); + } - //4.2 allocate resources, implemented port information needs to be ready before calling the following routine. + //4.3 allocate resources, implemented port information needs to be ready before calling the following routine. if (adapterExtension->StateFlags.StoppedState == 0) { if (!AllocateResourcesForAdapter(adapterExtension, ConfigInfo, portCount)) { return SP_RETURN_ERROR; @@ -693,8 +728,8 @@ Return Values: // // 3. Currently WorkerTimer is used only for PartialToSlumber during interrupt servicing (not applicable in dump mode) // - if (!IsDumpMode(adapterExtension)) - { + if (!dumpMode) { + for (i = 0; i <= adapterExtension->HighestPort; i++) { if (adapterExtension->PortExtension[i] != NULL) { @@ -726,8 +761,8 @@ Return Values: // reset "StoppedState". NOTE: This field should not be referenced anymore in this function after following line. adapterExtension->StateFlags.StoppedState = 0; - // 4.3 initialize ports and start them. - //4.3.1 initialize all AHCI ports + //4.4 initialize ports and start them. + //4.4.1 initialize all AHCI ports for (i = 0; i <= adapterExtension->HighestPort; i++) { if (adapterExtension->PortExtension[i] != NULL) { // in case of PortInitialize fails, ChannelExtension->StateFlags.Initialized will remain as 'FALSE'. there will be not attempt to start the Port. @@ -735,35 +770,37 @@ Return Values: } } - if (IsDumpMode(adapterExtension)) { + if (dumpMode) { // // In dump mode copy registry flags and telemetry configuration from the dump context. If telemetry extends to more than one // device the logic should cover migration of per device settings, initially we are limited to a single boot device // - ULONG Index; -#pragma warning (suppress: 6385) // dumpContext->DumpPortNumber is guaranteed to be in-bound earlier - if (adapterExtension->PortExtension[dumpContext->DumpPortNumber] != NULL) { + for (i = 0; i < dumpContext->PortCount; i++) { + portNumber = dumpContext->Ports[i].PortNumber; - adapterExtension->PortExtension[dumpContext->DumpPortNumber]->RegistryFlags = dumpContext->PortRegistryFlags; + if (adapterExtension->PortExtension[portNumber] != NULL) { - // - // If the table in dump context is empty (not filled) - keep static data intact - // - if (dumpContext->PublicGPLogTableAddresses[0]) { - for(Index=0; - Index < sizeof(AhciPublicGPLogTableAddresses) / sizeof(AhciPublicGPLogTableAddresses[0] ); - Index++) { - AhciPublicGPLogTableAddresses[Index] = dumpContext->PublicGPLogTableAddresses[Index]; - } + adapterExtension->PortExtension[portNumber]->RegistryFlags = dumpContext->Ports[i].PortRegistryFlags; - AhciGPLogPageIntoPrivate = dumpContext->PrivateGPLogPageAddress; - } + // + // If the table in dump context is empty (not filled) - keep static data intact + // + /*if (dumpContext->PublicGPLogTableAddresses[0]) { + for(Index=0; + Index < sizeof(AhciPublicGPLogTableAddresses) / sizeof(AhciPublicGPLogTableAddresses[0] ); + Index++) { + AhciPublicGPLogTableAddresses[Index] = dumpContext->PublicGPLogTableAddresses[Index]; + } + + AhciGPLogPageIntoPrivate = dumpContext->PrivateGPLogPageAddress; + }*/ - StorPortMoveMemory((PVOID)&adapterExtension->PortExtension[dumpContext->DumpPortNumber]->DeviceExtension->HybridInfo, - (PVOID)&dumpContext->HybridInfo, - sizeof(GP_LOG_HYBRID_INFORMATION_HEADER)); + StorPortMoveMemory((PVOID)&adapterExtension->PortExtension[portNumber]->DeviceExtension->HybridInfo, + (PVOID)&dumpContext->Ports[i].HybridInfo, + sizeof(GP_LOG_HYBRID_INFORMATION_HEADER)); + } } } @@ -797,8 +834,6 @@ AhciHwInitialize ( // // Turn on DPC Redirection if it's supported. // - // TODO : Revisit this : Disable these perf optimizations for now during bringup. -#if !defined (_ARM64_) if ((status == STOR_STATUS_SUCCESS) && ((perfConfigData.Flags & STOR_PERF_DPC_REDIRECTION) != 0)) { @@ -841,7 +876,6 @@ AhciHwInitialize ( NT_ASSERT(status == STOR_STATUS_SUCCESS); } -#endif // // async process to get all ports into running state @@ -876,7 +910,6 @@ AhciHwPassiveInitialize ( // 2. check if ACPI supports turning off power on link AhciAdapterEvaluateDSMMethod(adapterExtension); - // // Get and cache D3Cold support. // @@ -886,8 +919,7 @@ AhciHwPassiveInitialize ( if (status == STOR_STATUS_SUCCESS) { adapterExtension->StateFlags.D3ColdSupported = d3ColdSupported; } - - + if (reportF1State) { bufferSize += STOR_POFX_COMPONENT_IDLE_STATE_SIZE; } @@ -911,8 +943,11 @@ AhciHwPassiveInitialize ( if (IsD3ColdAllowed(adapterExtension)) { adapterExtension->PoFxDevice->Flags = STOR_POFX_DEVICE_FLAG_ENABLE_D3_COLD; } + + // Indicate miniport opt-in adapter D3 wake support. + adapterExtension->PoFxDevice->Flags |= STOR_POFX_DEVICE_FLAG_ADAPTER_D3_WAKE; - // indicate dump miniport can't bring adapter to active + // Indicate dump miniport can't bring adapter to active adapterExtension->PoFxDevice->Flags |= STOR_POFX_DEVICE_FLAG_NO_DUMP_ACTIVE; adapterExtension->PoFxDevice->Components[0].Version = STOR_POFX_COMPONENT_VERSION_V1; @@ -948,7 +983,6 @@ AhciHwPassiveInitialize ( goto Exit; } - // register success adapterExtension->StateFlags.PoFxEnabled = TRUE; adapterExtension->StateFlags.PoFxActive = TRUE; @@ -983,7 +1017,6 @@ Return Value: AhciPortAcpiDSMControl(AdapterExtension, (ULONG)-1, FALSE); } - return; } @@ -1078,8 +1111,9 @@ AhciAdapterRemoval ( Release resources allocated for the adapter and its managed ports/devices */ { - ULONG i; - PVOID bufferToFree = NULL; + ULONG i; + PVOID bufferToFree = NULL; + PVOID executionHistoryToFree = NULL; if (IsDumpMode(AdapterExtension)) { return; @@ -1129,6 +1163,13 @@ AhciAdapterRemoval ( AdapterExtension->PortExtension[i]->StateFlags.PoFxActive = FALSE; } + if ((executionHistoryToFree == NULL) && (AdapterExtension->PortExtension[i]->ExecutionHistory != NULL)) { + executionHistoryToFree = AdapterExtension->PortExtension[i]->ExecutionHistory; + } + + if (AdapterExtension->PortExtension[i]->ExecutionHistory != NULL) { + AdapterExtension->PortExtension[i]->ExecutionHistory = NULL; + } AdapterExtension->PortExtension[i] = NULL; } @@ -1138,6 +1179,10 @@ AhciAdapterRemoval ( StorPortFreePool(AdapterExtension, bufferToFree); } + if (executionHistoryToFree != NULL) { + StorPortFreePool(AdapterExtension, executionHistoryToFree); + } + return; } @@ -1274,7 +1319,8 @@ Return Value: NT_ASSERT(adapterPower != NULL); - StorPortDebugPrint(3, "StorAHCI - LPM: Adapter - %s\n", adapterPower->PowerState == StorPowerDeviceD0 ? "D0" : "D3"); + StorPortDebugPrint(3, "StorAHCI - LPM: Adapter SystemIoBusNumber:%d - %s\n", + adapterExtension->SystemIoBusNumber, adapterPower->PowerState == StorPowerDeviceD0 ? "D0" : "D3"); if (adapterPower->PowerState == StorPowerDeviceD0) { AhciAdapterPowerUp(adapterExtension); //power up @@ -1296,12 +1342,12 @@ Return Value: if (powerHints->Size >= sizeof(STOR_SYSTEM_POWER_HINTS)) { - StorPortDebugPrint(3, "StorAHCI - LPM: Adapter - System Power Hint - State: %u - Latency: %u ms \n", powerHints->SystemPower, powerHints->ResumeLatencyMSec); + StorPortDebugPrint(3, "StorAHCI - LPM: Adapter SystemIoBusNumber:%d - System Power Hint - State: %u - Latency: %u ms\n", + adapterExtension->SystemIoBusNumber, powerHints->SystemPower, powerHints->ResumeLatencyMSec); adapterExtension->SystemPowerHintState = powerHints->SystemPower; adapterExtension->SystemPowerResumeLatencyMSec = powerHints->ResumeLatencyMSec; - if (adapterExtension->TracingEnabled) { StorPortEtwEvent2(AdapterExtension, NULL, @@ -1361,6 +1407,15 @@ AhciHwResetBus ( status = AhciPortReset(adapterExtension->PortExtension[PathId], TRUE); AhciInterruptSpinlockRelease(adapterExtension, MAXULONG, &lockhandle); adapterExtension->PortExtension[PathId]->DeviceExtension[0].IoRecord.PortDriverResetCount++; + + AhciTelemetryLogResetErrorRecovery(adapterExtension->PortExtension[PathId], + (PSTOR_ADDRESS)&(adapterExtension->PortExtension[PathId]->DeviceExtension->DeviceAddress), + AhciTelemetryEventIdResetBus, + "AhciHwResetBus", + 0, + "PortResetStatus", + status + ); } return status; @@ -1390,7 +1445,9 @@ AhciHwBuildIo ( NT_ASSERT(Srb->SrbStatus == SRB_STATUS_PENDING); // SrbExtension is not Null-ed by Storport, so do it here. - AhciZeroMemory((PCHAR)srbExtension, sizeof(AHCI_SRB_EXTENSION)); + // Zero PRDT later when elements number in the SGL is available. + AhciZeroMemory((PCHAR)(&(srbExtension->AtaFunction)), (sizeof(AHCI_SRB_EXTENSION) - FIELD_OFFSET(AHCI_SRB_EXTENSION, AtaFunction))); + AhciZeroMemory((PCHAR)srbExtension, FIELD_OFFSET(AHCI_COMMAND_TABLE, PRDT)); channelExtension = adapterExtension->PortExtension[pathId]; @@ -1409,11 +1466,63 @@ AhciHwBuildIo ( if ( !IsPortValid(adapterExtension, pathId) ) { Srb->SrbStatus = SRB_STATUS_NO_DEVICE; + StorPortEtwChannelEvent2(adapterExtension, + NULL, + StorportEtwEventOperational, + AhciEtwEventBuildIO, + L"No device", + STORPORT_ETW_EVENT_KEYWORD_COMMAND_TRACE, + StorportEtwLevelError, + StorportEtwEventOpcodeInfo, + Srb, + L"function", + function, + L"srbFlags", + srbFlags); + goto exit; + } + + // + // If the adapter was removed, fail this request. + // We first check to see if the adapter is removable so that we can + // avoid incurring a register access if it is not removable. + // + if (IsAdapterRemovable(adapterExtension) && + IsAdapterRemoved(adapterExtension)) { + Srb->SrbStatus = SRB_STATUS_NO_DEVICE; + StorPortEtwChannelEvent2(adapterExtension, + (channelExtension != NULL) ? ((PSTOR_ADDRESS) &(channelExtension->DeviceExtension->DeviceAddress)) : (NULL), + StorportEtwEventOperational, + AhciEtwEventBuildIO, + L"Adapter removed during BuildIo", + STORPORT_ETW_EVENT_KEYWORD_COMMAND_TRACE, + StorportEtwLevelError, + StorportEtwEventOpcodeInfo, + Srb, + L"function", + function, + L"srbFlags", + srbFlags); goto exit; } } switch (function) { + // + // Get the flag from Storport that tells us if the adapter is removable or not. + // + case SRB_FUNCTION_PNP: { + PSRBEX_DATA_PNP pnpData = (PSRBEX_DATA_PNP)SrbGetSrbExDataByType((PSTORAGE_REQUEST_BLOCK)Srb, SrbExDataTypePnP); + if (((pnpData->SrbPnPFlags & SRB_PNP_FLAGS_ADAPTER_REQUEST)) && + (pnpData->PnPAction == StorQueryCapabilities) && + (SrbGetDataTransferLength(Srb) >= sizeof(STOR_DEVICE_CAPABILITIES_EX))) { + PSTOR_DEVICE_CAPABILITIES_EX storCapabilities = (PSTOR_DEVICE_CAPABILITIES_EX)SrbGetDataBuffer(Srb); + adapterExtension->StateFlags.Removable = storCapabilities->Removable; + Srb->SrbStatus = SRB_STATUS_SUCCESS; + } + break; + } + case SRB_FUNCTION_IO_CONTROL: { if ((srbFlags & SRB_IOCTL_FLAGS_ADAPTER_REQUEST) == 0) { @@ -1424,6 +1533,10 @@ AhciHwBuildIo ( if ( ( srbExtension->Sgl == NULL ) && ( IsDataTransferNeeded((PSTORAGE_REQUEST_BLOCK)Srb) ) ) { srbExtension->Sgl = (PLOCAL_SCATTER_GATHER_LIST)StorPortGetScatterGatherList(adapterExtension, Srb); } + if (srbExtension->Sgl != NULL) { + // Zero PRDT according to SGL elements number to avoid unnecessary CPU usage. + AhciZeroMemory((PCHAR)(srbExtension->CommandTable.PRDT), (srbExtension->Sgl->NumberOfElements * sizeof(AHCI_PRDT))); + } } } @@ -1438,6 +1551,10 @@ AhciHwBuildIo ( if ( ( srbExtension->Sgl == NULL ) && ( IsDataTransferNeeded((PSTORAGE_REQUEST_BLOCK)Srb) ) ) { srbExtension->Sgl = (PLOCAL_SCATTER_GATHER_LIST)StorPortGetScatterGatherList(adapterExtension, Srb); } + if (srbExtension->Sgl != NULL) { + // Zero PRDT according to SGL elements number to avoid unnecessary CPU usage. + AhciZeroMemory((PCHAR)(srbExtension->CommandTable.PRDT), (srbExtension->Sgl->NumberOfElements * sizeof(AHCI_PRDT))); + } } break; @@ -1475,7 +1592,7 @@ AhciHwStartIo ( ) /* 1. Process Adapter request - 2. Bail out if it’s adapter request + 2. Bail out if it's adapter request 3. Validate Port Number, if not valid, bail out. 4. Process Device/Port request @@ -1488,7 +1605,7 @@ AhciHwStartIo ( BOOLEAN adapterRequest = FALSE; BOOLEAN processIO = FALSE; - //1 Work on Adapter requests + //1 Work on Adapter requests switch (function) { case SRB_FUNCTION_PNP: { PSRBEX_DATA_PNP pnpData = (PSRBEX_DATA_PNP)SrbGetSrbExDataByType((PSTORAGE_REQUEST_BLOCK)Srb, SrbExDataTypePnP); @@ -1509,6 +1626,21 @@ AhciHwStartIo ( } else { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; } + + StorPortEtwChannelEvent2(adapterExtension, + NULL, + (Srb->SrbStatus == SRB_STATUS_SUCCESS) ? StorportEtwEventDiagnostic : StorportEtwEventOperational, + AhciEtwEventStartIO, + L"Processing PNP", + STORPORT_ETW_EVENT_KEYWORD_COMMAND_TRACE, + (Srb->SrbStatus == SRB_STATUS_SUCCESS) ? StorportEtwLevelInformational : StorportEtwLevelError, + StorportEtwEventOpcodeInfo, + Srb, + L"PnPAction", + pnpData->PnPAction, + L"SrbStatus", + Srb->SrbStatus); + //complete all Adapter PnP request StorPortNotification(RequestComplete, AdapterExtension, Srb); } @@ -1563,21 +1695,62 @@ AhciHwStartIo ( // 2.1 All requests reach here should be for devices if ( !IsPortValid(adapterExtension, pathId) ) { Srb->SrbStatus = SRB_STATUS_NO_DEVICE; + + StorPortEtwChannelEvent8(adapterExtension, + NULL, + StorportEtwEventOperational, + AhciEtwEventStartIO, + L"Port has no device", + STORPORT_ETW_EVENT_KEYWORD_COMMAND_TRACE, + StorportEtwLevelError, + StorportEtwEventOpcodeInfo, + Srb, + L"AdapterNumber", + adapterExtension->AdapterNumber, + L"SystemIoBusNumber", + adapterExtension->SystemIoBusNumber, + L"SlotNumber", + adapterExtension->SlotNumber, + L"AhciBaseAddress", + adapterExtension->AhciBaseAddress, + L"PathId", + pathId, + L"Function", + Srb->Function, + L"SrbStatus", + Srb->SrbStatus, + NULL, + 0); + StorPortNotification(RequestComplete, AdapterExtension, Srb); return TRUE; } - // 2.2 work on device requests + // 2.2 work on device requests + // Get channelExtension and storAddress to use for ETW Event logging + PAHCI_CHANNEL_EXTENSION channelExtension = adapterExtension->PortExtension[pathId]; + PSTOR_ADDRESS storAddress = (PSTOR_ADDRESS)&(channelExtension->DeviceExtension->DeviceAddress); + switch (function) { case SRB_FUNCTION_RESET_BUS: // this one may come from class driver, not port driver. same as AhciHwResetBus case SRB_FUNCTION_RESET_DEVICE: case SRB_FUNCTION_RESET_LOGICAL_UNIT: { + // these reset requests target to Port AhciInterruptSpinlockAcquire(adapterExtension, pathId, &lockhandle); Srb->SrbStatus = AhciPortReset(adapterExtension->PortExtension[pathId], TRUE) ? SRB_STATUS_SUCCESS : SRB_STATUS_ERROR; StorPortNotification(RequestComplete, AdapterExtension, Srb); AhciInterruptSpinlockRelease(adapterExtension, pathId, &lockhandle); adapterExtension->PortExtension[pathId]->DeviceExtension[0].IoRecord.PortDriverResetCount++; + + AhciTelemetryLogResetErrorRecovery(adapterExtension->PortExtension[pathId], + (PSTOR_ADDRESS)&(adapterExtension->PortExtension[pathId]->DeviceExtension->DeviceAddress), + AhciTelemetryEventIdResetDeviceRequest, + "AhciHwStartIo RESET Request", + 0, + "PortResetStatus", + Srb->SrbStatus + ); break; } @@ -1612,6 +1785,21 @@ AhciHwStartIo ( Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; StorPortNotification(RequestComplete, AdapterExtension, Srb); } + + StorPortEtwChannelEvent2(adapterExtension, + storAddress, + (Srb->SrbStatus == SRB_STATUS_SUCCESS) ? StorportEtwEventDiagnostic : StorportEtwEventOperational, + AhciEtwEventStartIO, + L"Completed PnP request", + STORPORT_ETW_EVENT_KEYWORD_COMMAND_TRACE, + (Srb->SrbStatus == SRB_STATUS_SUCCESS) ? StorportEtwLevelInformational : StorportEtwLevelError, + StorportEtwEventOpcodeInfo, + Srb, + L"SrbPnPFlags", + pnpData->SrbPnPFlags, + L"PnPAction", + pnpData->PnPAction); + break; } @@ -1623,20 +1811,38 @@ AhciHwStartIo ( BOOLEAN sendStandby = (IsDumpHiberMode(adapterExtension) || IsDumpCrashMode(adapterExtension)); deviceParameters->StateFlags.SystemPoweringDown = TRUE; + + USHORT *eventDescription; if (adapterExtension->PortExtension[pathId]->DeviceExtension->SupportedCommands.SetDateAndTime == 0x1) { IssueSetDateAndTimeCommand(adapterExtension->PortExtension[pathId], Srb, sendStandby); processIO = TRUE; + eventDescription = L"Shutdown set date and time"; } else if (sendStandby) { //in dump mode, this is the last Srb sent after SYNC CACHE, spin down the disk PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension((PSTORAGE_REQUEST_BLOCK)Srb); srbExtension->AtaFunction = ATA_FUNCTION_ATA_COMMAND; SetCommandReg((&srbExtension->TaskFile.Current), IDE_COMMAND_STANDBY_IMMEDIATE); processIO = TRUE; + eventDescription = L"Shutdown standby immediate"; } else { Srb->SrbStatus = SRB_STATUS_SUCCESS; + eventDescription = L"Shutdown"; StorPortNotification(RequestComplete, AdapterExtension, Srb); } + StorPortEtwEvent2(adapterExtension, + storAddress, + AhciEtwEventStartIO, + eventDescription, + STORPORT_ETW_EVENT_KEYWORD_COMMAND_TRACE, + StorportEtwLevelInformational, + StorportEtwEventOpcodeInfo, + Srb, + NULL, + 0, + NULL, + 0); + break; } @@ -1648,38 +1854,56 @@ AhciHwStartIo ( case SRB_FUNCTION_DUMP_POINTERS: { ULONG status = STOR_STATUS_SUCCESS; + ULONG size = 0; PAHCI_DUMP_CONTEXT dumpContext = NULL; PMINIPORT_DUMP_POINTERS dumpPointers = (PMINIPORT_DUMP_POINTERS)SrbGetDataBuffer(Srb); + USHORT *eventDescription = L"Completed dump pointers"; + if ( (dumpPointers == NULL) || (SrbGetDataTransferLength(Srb) < RTL_SIZEOF_THROUGH_FIELD(MINIPORT_DUMP_POINTERS, MiniportPrivateDumpData)) ) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; + eventDescription = L"Invalid dump pointers request"; } else { + size = sizeof(AHCI_DUMP_CONTEXT); + // allocate pool and zero the content status = StorPortAllocatePool(AdapterExtension, - sizeof(AHCI_DUMP_CONTEXT), + size, AHCI_POOL_TAG, (PVOID*)&dumpContext); if ((status != STOR_STATUS_SUCCESS) || (dumpContext == NULL)) { // we cannot continue if cannot get memory for ChannelExtension. Srb->SrbStatus = SRB_STATUS_ERROR; + eventDescription = L"Allocate dump context failed"; } else { dumpPointers->Version = DUMP_MINIPORT_VERSION_1; dumpPointers->Size = sizeof(MINIPORT_DUMP_POINTERS); - dumpPointers->MiniportPrivateDumpData = (PVOID)dumpContext; + AhciZeroMemory((PCHAR)dumpContext, size); - AhciZeroMemory((PCHAR)dumpContext, sizeof(AHCI_DUMP_CONTEXT)); dumpContext->VendorID = adapterExtension->VendorID; dumpContext->DeviceID = adapterExtension->DeviceID; dumpContext->RevisionID = adapterExtension->RevisionID; dumpContext->AhciBaseAddress = adapterExtension->AhciBaseAddress; dumpContext->LogFlags = adapterExtension->LogFlags; dumpContext->AdapterRegistryFlags = adapterExtension->RegistryFlags; - dumpContext->DumpPortNumber = adapterExtension->PortExtension[pathId]->PortNumber; - dumpContext->PortRegistryFlags = adapterExtension->PortExtension[pathId]->RegistryFlags; + + dumpContext->PortCount = 1; + + dumpContext->Ports[0].PortNumber = adapterExtension->PortExtension[pathId]->PortNumber; + dumpContext->Ports[0].PortRegistryFlags = adapterExtension->PortExtension[pathId]->RegistryFlags; + + // + // Preserve Hybrid Disk Information. + // + if (IsDeviceHybridInfoSupported(adapterExtension->PortExtension[pathId])) { + StorPortMoveMemory((PVOID)&dumpContext->Ports[0].HybridInfo, + (PVOID)&adapterExtension->PortExtension[pathId]->DeviceExtension->HybridInfo, + sizeof(GP_LOG_HYBRID_INFORMATION_HEADER)); + } // // Fill telemetry collection context - shared with diskdump.sys stack @@ -1698,18 +1922,26 @@ AhciHwStartIo ( dumpContext->PrivateGPLogPageAddress = IDE_GP_LOG_CURRENT_DEVICE_INTERNAL_STATUS; } - // - // Preserve Hybrid Disk Information. - // - if (IsDeviceHybridInfoSupported(adapterExtension->PortExtension[pathId])) { - StorPortMoveMemory((PVOID)&dumpContext->HybridInfo, - (PVOID)&adapterExtension->PortExtension[pathId]->DeviceExtension->HybridInfo, - sizeof(GP_LOG_HYBRID_INFORMATION_HEADER)); - } + dumpPointers->MiniportPrivateDumpData = (PVOID)dumpContext; Srb->SrbStatus = SRB_STATUS_SUCCESS; } } + + StorPortEtwChannelEvent2(adapterExtension, + storAddress, + (Srb->SrbStatus == SRB_STATUS_SUCCESS) ? StorportEtwEventDiagnostic : StorportEtwEventOperational, + AhciEtwEventStartIO, + eventDescription, + STORPORT_ETW_EVENT_KEYWORD_COMMAND_TRACE, + (Srb->SrbStatus == SRB_STATUS_SUCCESS) ? StorportEtwLevelInformational : StorportEtwLevelError, + StorportEtwEventOpcodeInfo, + Srb, + L"SrbStatus", + Srb->SrbStatus, + L"TransferLen", + SrbGetDataTransferLength(Srb)); + StorPortNotification(RequestComplete, AdapterExtension, Srb); break; } @@ -1725,6 +1957,21 @@ AhciHwStartIo ( status = StorPortFreePool(AdapterExtension, dumpPointers->MiniportPrivateDumpData); Srb->SrbStatus = (status == STOR_STATUS_SUCCESS) ? SRB_STATUS_SUCCESS : SRB_STATUS_ERROR; } + + StorPortEtwChannelEvent2(adapterExtension, + storAddress, + (Srb->SrbStatus == SRB_STATUS_SUCCESS) ? StorportEtwEventDiagnostic : StorportEtwEventOperational, + AhciEtwEventStartIO, + L"Completed free dump pointers", + STORPORT_ETW_EVENT_KEYWORD_COMMAND_TRACE, + (Srb->SrbStatus == SRB_STATUS_SUCCESS) ? StorportEtwLevelInformational : StorportEtwLevelError, + StorportEtwEventOpcodeInfo, + Srb, + L"SrbStatus", + Srb->SrbStatus, + L"TransferLen", + SrbGetDataTransferLength(Srb)); + StorPortNotification(RequestComplete, AdapterExtension, Srb); break; } @@ -1732,6 +1979,20 @@ AhciHwStartIo ( default: { // for unsupported SRB: complete with status: SRB_STATUS_INVALID_REQUEST Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; + + StorPortEtwEvent2(adapterExtension, + storAddress, + AhciEtwEventStartIO, + L"Invalid request", + STORPORT_ETW_EVENT_KEYWORD_COMMAND_TRACE, + StorportEtwLevelInformational, + StorportEtwEventOpcodeInfo, + Srb, + L"Function", + function, + NULL, + 0); + StorPortNotification(RequestComplete, AdapterExtension, Srb); break; } @@ -1749,7 +2010,6 @@ AhciHwStartIo ( AhciProcessIo(adapterExtension->PortExtension[pathId], (PSTORAGE_REQUEST_BLOCK)Srb, FALSE); ActivateQueue(adapterExtension->PortExtension[pathId], FALSE); - } return TRUE; @@ -1769,7 +2029,7 @@ Routine Description: This routine must attempt one clear the interrupt on the HBA before it returns TRUE. NOTE: - The following StorPort routines shall not be called from the AhciHwInterrupt routine – StorPortCompleteAllRequests and StorPortDeviceBusy. + The following StorPort routines shall not be called from the AhciHwInterrupt routine - StorPortCompleteAllRequests and StorPortDeviceBusy. The miniport could however request for a worker routine and make the calls in the worker routine. Called by: @@ -1823,7 +2083,6 @@ It performs: "If there were errors, noted in the PxIS register, software performs error recovery actions (see section 6.2.2)." AHCI 1.1 Section 6.2.2.1 Non-Queued Error Recovery (this may take a while, better queue a DPC) Complete further processing in the worker routine and enable interrupts on the channel - 6.1 Partial to Slumber auto transit Affected Variables/Registers: @@ -1872,12 +2131,36 @@ Return Values: //call the correct error handling based on current hw queue workload type sact = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SACT); - - if ((sact == MAXULONG) && IsAdapterRemoved(ChannelExtension)) { + if ((sact == MAXULONG) && IsAdapterRemoved(ChannelExtension->AdapterExtension)) { // controller has been surprise removed + StorPortEtwChannelEvent8(ChannelExtension->AdapterExtension, + (ChannelExtension != NULL) ? (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress) : (NULL), + StorportEtwEventOperational, + AhciEtwEventHandleInterrupt, + L"Adapter surprise removed", + STORPORT_ETW_EVENT_KEYWORD_COMMAND_TRACE, + StorportEtwLevelWarning, + StorportEtwEventOpcodeInfo, + NULL, + L"pxis", + pxis.AsUlong, + L"ssts", + ssts.AsUlong, + L"serr", + serr.AsUlong, + L"cmd", + cmd.AsUlong, + L"sact", + sact, + NULL, + 0, + NULL, + 0, + NULL, + 0); return; } else if (sact != 0) { - //5.1 NCQ, Handle error processing + //5.1 NCQ, Handle error processing ChannelExtension->StateFlags.CallAhciNcqErrorRecovery = 1; //Give NCQ one chance @@ -1903,7 +2186,32 @@ Return Values: if (pxis.DMPS || pxis.PCS) { cmd.AsUlong = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CMD.AsUlong); - if ((cmd.AsUlong == MAXULONG) && IsAdapterRemoved(ChannelExtension)) { + if ((cmd.AsUlong == MAXULONG) && IsAdapterRemoved(ChannelExtension->AdapterExtension)) { + StorPortEtwChannelEvent8(ChannelExtension->AdapterExtension, + (ChannelExtension != NULL) ? (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress) : (NULL), + StorportEtwEventOperational, + AhciEtwEventHandleInterrupt, + L"Adapter surprise removed", + STORPORT_ETW_EVENT_KEYWORD_COMMAND_TRACE, + StorportEtwLevelWarning, + StorportEtwEventOpcodeInfo, + NULL, + L"pxis", + pxis.AsUlong, + L"ssts", + ssts.AsUlong, + L"serr", + serr.AsUlong, + L"cmd", + cmd.AsUlong, + L"sact", + sact, + NULL, + 0, + NULL, + 0, + NULL, + 0); return; } @@ -1937,13 +2245,13 @@ Return Values: // PhyRdy Change Status if (pxis.PRCS) { //Hot plug removals are detected via the PxIS.PRCS bit that directly reflects the PxSERR.DIAG.N bit. - //Note that PxSERR.DIAG.N is also set to ‘1’ on insertions and during interface power management entry/exit. + //Note that PxSERR.DIAG.N is also set to '1' on insertions and during interface power management entry/exit. serrMask.DIAG.N = 1; StorPortWriteRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SERR.AsUlong, serrMask.AsUlong); ssts.AsUlong = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SSTS.AsUlong); - if ((ssts.AsUlong == MAXULONG) && IsAdapterRemoved(ChannelExtension)) { + if ((ssts.AsUlong == MAXULONG) && IsAdapterRemoved(ChannelExtension->AdapterExtension)) { // controller has been surprise removed return; } @@ -2053,7 +2361,7 @@ Return Values: if (pxis.SDBS) { pxisMask.SDBS = 1; } - // Descriptor Processed (A PRD with the ‘I’ bit set has transferred all of its data) + // Descriptor Processed (A PRD with the 'I' bit set has transferred all of its data) if (pxis.DPS) { pxisMask.DPS = 1; } @@ -2066,8 +2374,33 @@ Return Values: // preserve taskfile for using in command completion process ChannelExtension->TaskFileData.AsUlong = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->TFD.AsUlong); - if ((ChannelExtension->TaskFileData.AsUlong == MAXULONG) && IsAdapterRemoved(ChannelExtension)) { + if ((ChannelExtension->TaskFileData.AsUlong == MAXULONG) && IsAdapterRemoved(ChannelExtension->AdapterExtension)) { // controller has been surprise removed + StorPortEtwChannelEvent8(ChannelExtension->AdapterExtension, + (ChannelExtension != NULL) ? (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress) : (NULL), + StorportEtwEventOperational, + AhciEtwEventHandleInterrupt, + L"Adapter surprise removed", + STORPORT_ETW_EVENT_KEYWORD_COMMAND_TRACE, + StorportEtwLevelWarning, + StorportEtwEventOpcodeInfo, + NULL, + L"pxis", + pxis.AsUlong, + L"ssts", + ssts.AsUlong, + L"serr", + serr.AsUlong, + L"cmd", + cmd.AsUlong, + L"sact", + sact, + L"StateFlags", + *(ULONGLONG *)&(ChannelExtension->StateFlags), + NULL, + 0, + NULL, + 0); return; } @@ -2086,8 +2419,33 @@ Return Values: ci = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CI); sact = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SACT); - if (((ci == MAXULONG) || (sact == MAXULONG)) && IsAdapterRemoved(ChannelExtension)) { + if (((ci == MAXULONG) || (sact == MAXULONG)) && IsAdapterRemoved(ChannelExtension->AdapterExtension)) { // controller has been surprise removed + StorPortEtwChannelEvent8(ChannelExtension->AdapterExtension, + (ChannelExtension != NULL) ? (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress) : (NULL), + StorportEtwEventOperational, + AhciEtwEventHandleInterrupt, + L"Adapter removed: complete cmd", + STORPORT_ETW_EVENT_KEYWORD_COMMAND_TRACE, + StorportEtwLevelWarning, + StorportEtwEventOpcodeInfo, + NULL, + L"pxis", + pxis.AsUlong, + L"ssts", + ssts.AsUlong, + L"serr", + serr.AsUlong, + L"cmd", + cmd.AsUlong, + L"is", + is, + L"ci", + ci, + L"sact", + sact, + NULL, + 0); return; } @@ -2111,20 +2469,6 @@ Return Values: RecordInterruptHistory(ChannelExtension, pxis.AsUlong, ssts.AsUlong, serr.AsUlong, ci, sact, 0x20010005); //AhciHwInterrupt No IO completed } - //6.1 Partial to Slumber auto transit - if ((outstanding == 0) && - PartialToSlumberTransitionIsAllowed(ChannelExtension, &cmd)) { - - ULONG status; - - // convert interval value from ms to us. allow 20ms of coalescing with other timers - status = StorPortRequestTimer(ChannelExtension->AdapterExtension, ChannelExtension->WorkerTimer, AhciAutoPartialToSlumber, ChannelExtension, ChannelExtension->AutoPartialToSlumberInterval * 1000, 20000); - - if (status == STOR_STATUS_SUCCESS) { - StorPortDebugPrint(3, "StorAHCI - LPM: Port %02d - Transit into Slumber from Partial - Scheduled \n", ChannelExtension->PortNumber); - } - } - if (LogExecuteFullDetail(ChannelExtension->AdapterExtension->LogFlags)) { RecordExecutionHistory(ChannelExtension, 0x10000005);//Exit AhciInterruptHandler } @@ -2356,7 +2700,8 @@ Return Value: if (IsPortValid(adapterExtension, storAddrBtl8->Path)) { PAHCI_CHANNEL_EXTENSION channelExtension = adapterExtension->PortExtension[storAddrBtl8->Path]; - StorPortDebugPrint(3, "StorAHCI - LPM: Port %02d - %s\n", storAddrBtl8->Path, unitControlPower->PowerState == StorPowerDeviceD0 ? "D0" : "D3"); + StorPortDebugPrint(3, "StorAHCI - LPM: SystemIoBusNumber:%d Port:%02d - %s\n", + adapterExtension->SystemIoBusNumber, storAddrBtl8->Path, unitControlPower->PowerState == StorPowerDeviceD0 ? "D0" : "D3"); if (unitControlPower->PowerState == StorPowerDeviceD0) { AhciPortPowerUp(channelExtension); @@ -2379,16 +2724,15 @@ Return Value: } case ScsiUnitPoFxPowerInfo: { - PSTOR_POFX_UNIT_POWER_INFO unitPowerInfo = (PSTOR_POFX_UNIT_POWER_INFO)Parameters; - PSTOR_ADDR_BTL8 storAddrBtl8 = (PSTOR_ADDR_BTL8)unitPowerInfo->Header.Address; - BOOLEAN d3ColdEnabled = FALSE; + PSTOR_POFX_UNIT_POWER_INFO unitPowerInfo = (PSTOR_POFX_UNIT_POWER_INFO)Parameters; + PSTOR_ADDR_BTL8 storAddrBtl8 = (PSTOR_ADDR_BTL8)unitPowerInfo->Header.Address; + BOOLEAN d3ColdEnabled = FALSE; if (IsPortValid(adapterExtension, storAddrBtl8->Path)) { - ULONG storStatus = STOR_STATUS_SUCCESS; - ULONG bufferLength = STOR_POFX_DEVICE_V3_SIZE + STOR_POFX_COMPONENT_V2_SIZE + STOR_POFX_COMPONENT_IDLE_STATE_SIZE; + ULONG storStatus = STOR_STATUS_SUCCESS; + ULONG bufferLength = STOR_POFX_DEVICE_V3_SIZE + STOR_POFX_COMPONENT_V2_SIZE + STOR_POFX_COMPONENT_IDLE_STATE_SIZE; PAHCI_CHANNEL_EXTENSION channelExtension = adapterExtension->PortExtension[storAddrBtl8->Path]; - BOOLEAN reportF1State = FALSE; - + BOOLEAN reportF1State = FALSE; // // IdlePowerEnabled == TRUE indicates this unit is being @@ -2417,7 +2761,6 @@ Return Value: bufferLength, AHCI_POOL_TAG, (PVOID*)&channelExtension->PoFxDevice); - } if (storStatus == STOR_STATUS_SUCCESS) { @@ -2436,10 +2779,34 @@ Return Value: channelExtension->PoFxDevice->Flags |= STOR_POFX_DEVICE_FLAG_NO_DUMP_ACTIVE; // - // If this is a drive with rotational media then enable adaptive D3 idle timeout. + // Disable idle debouncing if it's possible for us to + // initiate a Slumber transition. + // We set the Slumber timer when we get the Idle + // condition callback and cancel it when we get the + // Active condition callback. The slumber timer is + // typically set to 100ms and idle debouncing can add + // a bias of up to 1s. Significantly delaying Slumber + // entry can have a negative impact on power so we + // disable idle debouncing. + // CAP.SSC indicates if the HBA supports Slumber. + // CAP.SALP indicates if the HBA can automatically + // transition to Partial when idle, which is needed for + // us to subsequently initiate a Slumber transition. + // + if ((channelExtension->AdapterExtension->CAP.SSC) && + (channelExtension->AdapterExtension->CAP.SALP) && + IsDeviceSupportsHIPM(channelExtension->DeviceExtension[0].IdentifyDeviceData)) { + channelExtension->PoFxDevice->Flags |= STOR_POFX_DEVICE_FLAG_NO_IDLE_DEBOUNCE; + } + + // + // If this is a drive with rotational media then enable adaptive D3 idle timeout, else if + // this is a SSD on an AOAC system, set the idle timeout flag and provide a minimum unit idle timeout. // if (DeviceIncursSeekPenalty(channelExtension)) { channelExtension->PoFxDevice->Flags |= STOR_POFX_DEVICE_FLAG_ADAPTIVE_D3_IDLE_TIMEOUT; + } else if (IsReceivingSystemPowerHints(adapterExtension) && DeviceIncursNoSeekPenalty(channelExtension)) { + channelExtension->PoFxDevice->Flags |= STOR_POFX_DEVICE_FLAG_IDLE_TIMEOUT; } component->Version = STOR_POFX_COMPONENT_VERSION_V2; @@ -2457,7 +2824,6 @@ Return Value: component->FStates[0].ResidencyRequirement = 0; component->FStates[0].NominalPower = STOR_POFX_UNKNOWN_POWER; - // registry runtime power management for Unit storStatus = StorPortInitializePoFxPower(AdapterExtension, (PSTOR_ADDRESS)storAddrBtl8, @@ -2504,19 +2870,30 @@ Return Value: case ScsiUnitPoFxPowerActive: { PSTOR_POFX_ACTIVE_CONTEXT activeContext = (PSTOR_POFX_ACTIVE_CONTEXT)Parameters; - PSTOR_ADDR_BTL8 storAddrBtl8 = (PSTOR_ADDR_BTL8)activeContext->Header.Address; + PSTOR_ADDR_BTL8 storAddrBtl8 = (PSTOR_ADDR_BTL8)activeContext->Header.Address; - if ( IsPortValid(adapterExtension, storAddrBtl8->Path) && PortPoFxEnabled(adapterExtension->PortExtension[storAddrBtl8->Path]) ) { + if (IsPortValid(adapterExtension, storAddrBtl8->Path) && PortPoFxEnabled(adapterExtension->PortExtension[storAddrBtl8->Path])) { PAHCI_CHANNEL_EXTENSION channelExtension = adapterExtension->PortExtension[storAddrBtl8->Path]; channelExtension->StateFlags.PoFxActive = activeContext->Active ? 1 : 0; - StorPortDebugPrint(3, "StorAHCI - LPM: Port %02d - %s\n", channelExtension->PortNumber, activeContext->Active ? "ACTIVE" : "IDLE"); + StorPortDebugPrint(3, "StorAHCI - LPM: SystemIoBusNumber:%d Port:%02d - %s\n", + adapterExtension->SystemIoBusNumber, channelExtension->PortNumber, activeContext->Active ? "ACTIVE" : "IDLE"); if (activeContext->Active) { - ULONG busChangePending; - ULONG restorePreservedSettingsPending; + ULONG busChangePending; + ULONG restorePreservedSettingsPending; + // + // Cancel the Slumber timer. + // + StorPortRequestTimer(channelExtension->AdapterExtension, + channelExtension->WorkerTimer, + AhciAutoPartialToSlumber, + channelExtension, + 0, 0); + StorPortDebugPrint(3, "StorAHCI - LPM: SystemIoBusNumber:%d Port:%02d - Transit into Slumber from Partial - Canceled\n", + adapterExtension->SystemIoBusNumber, channelExtension->PortNumber); busChangePending = InterlockedBitTestAndReset((LONG*)&channelExtension->PoFxPendingWork, 1); //BusChange is at bit 1 restorePreservedSettingsPending = InterlockedBitTestAndReset((LONG*)&channelExtension->PoFxPendingWork, 0); //RestorePreservedSettings is at bit 0 @@ -2529,6 +2906,7 @@ Return Value: if (restorePreservedSettingsPending == 1) { // continue on processing RestorePreservedSettings + RecordExecutionHistory(channelExtension, 0x10050041); // RestorePreservedSettings continue in Unit Active // Start the first command IssuePreservedSettingCommands(channelExtension, NULL); @@ -2538,10 +2916,30 @@ Return Value: PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension((PSTORAGE_REQUEST_BLOCK)&channelExtension->Local.Srb); if (srbExtension->AtaFunction != 0) { AhciProcessIo(channelExtension, (PSTORAGE_REQUEST_BLOCK)&channelExtension->Local.Srb, FALSE); + ActivateQueue(channelExtension, FALSE); } } } } else { + // + // Attempt to start the Slumber timer now that we're idle. + // Use a tolerable delay of 20ms to allow coalescing with + // other timers. + // + if (PartialToSlumberTransitionIsAllowed(channelExtension, NULL)) { + + status = StorPortRequestTimer(channelExtension->AdapterExtension, + channelExtension->WorkerTimer, + AhciAutoPartialToSlumber, + channelExtension, + channelExtension->AutoPartialToSlumberInterval * 1000, 20000); + + if (status == STOR_STATUS_SUCCESS) { + StorPortDebugPrint(3, "StorAHCI - LPM: SystemIoBusNumber:%d Port:%02d - Transit into Slumber from Partial - Scheduled\n", + adapterExtension->SystemIoBusNumber, channelExtension->PortNumber); + } + } + } } else { status = ScsiUnitControlUnsuccessful; @@ -2552,14 +2950,13 @@ Return Value: case ScsiUnitPoFxPowerSetFState: { PSTOR_POFX_FSTATE_CONTEXT fStateContext = (PSTOR_POFX_FSTATE_CONTEXT)Parameters; - PSTOR_ADDR_BTL8 storAddrBtl8 = (PSTOR_ADDR_BTL8)fStateContext->Header.Address; - PAHCI_CHANNEL_EXTENSION channelExtension = adapterExtension->PortExtension[storAddrBtl8->Path]; + PSTOR_ADDR_BTL8 storAddrBtl8 = (PSTOR_ADDR_BTL8)fStateContext->Header.Address; + PAHCI_CHANNEL_EXTENSION channelExtension = adapterExtension->PortExtension[storAddrBtl8->Path]; - if ( IsPortValid(adapterExtension, storAddrBtl8->Path) && PortPoFxEnabled(channelExtension) ) { + if (IsPortValid(adapterExtension, storAddrBtl8->Path) && PortPoFxEnabled(channelExtension)) { - StorPortDebugPrint(3, "StorAHCI - LPM: Port %02d - Transition from F%u to F%u\n", channelExtension->PortNumber, - channelExtension->PoFxFState, - fStateContext->FState); + StorPortDebugPrint(3, "StorAHCI - LPM: SystemIoBusNumber:%d Port:%02d - Transition from F%u to F%u\n", + adapterExtension->SystemIoBusNumber, channelExtension->PortNumber, channelExtension->PoFxFState, fStateContext->FState); channelExtension->PoFxFState = (UCHAR)fStateContext->FState; @@ -2607,6 +3004,16 @@ Return Value: AhciPortFailAllIos(channelExtension, SRB_STATUS_NO_DEVICE, TRUE); AhciInterruptSpinlockRelease(adapterExtension, channelExtension->PortNumber, &lockhandle); + + ++(channelExtension->TotalCountSurpriseRemove); + AhciTelemetryLogResetErrorRecovery(channelExtension, + (PSTOR_ADDRESS)&(channelExtension->DeviceExtension->DeviceAddress), + AhciTelemetryEventIdSurpriseRemove, + "ScsiUnitSurpriseRemoval", + AHCI_TELEMETRY_FLAG_NOT_SUPPRESS_LOGGING, + NULL, + 0 + ); } break; @@ -2629,4 +3036,3 @@ Return Value: #pragma warning(default:4201) #endif - diff --git a/storage/miniports/storahci/src/entrypts.h b/storage/miniports/storahci/src/entrypts.h index 56adcb0ec..df1aa9eae 100644 --- a/storage/miniports/storahci/src/entrypts.h +++ b/storage/miniports/storahci/src/entrypts.h @@ -23,6 +23,7 @@ Module Name: #pragma warning(disable:4214) // bit field types other than int #pragma warning(disable:4201) // nameless struct/union +#include "data.h" #define AHCI_POOL_TAG 'ichA' // "Ahci" - StorAHCI miniport driver @@ -34,10 +35,10 @@ Module Name: #define AHCI_MAX_NCQ_REQUEST_COUNT 32 #define KB (1024) -#define AHCI_MAX_TRANSFER_LENGTH (128 * KB) +#define AHCI_MAX_TRANSFER_LENGTH_DEFAULT (128 * KB) +#define AHCI_MAX_TRANSFER_LENGTH (1024 * KB) #define MAX_SETTINGS_PRESERVED 32 #define MAX_CRB_LOG_INDEX 64 -#define MAX_EXECUTION_HISTORY_ENTRY_COUNT 100 #define INQUIRYDATABUFFERSIZE 36 @@ -71,22 +72,18 @@ Module Name: // registry flags apply to port or device typedef struct _CHANNEL_REGISTRY_FLAGS { - ULONG Reserved : 20; - } CHANNEL_REGISTRY_FLAGS, *PCHANNEL_REGISTRY_FLAGS; // registry flags apply to the whole adapter typedef struct _ADAPTER_REGISTRY_FLAGS { - ULONG Reserved2 : 16; - + ULONG Reserved2 : 13; } ADAPTER_REGISTRY_FLAGS, *PADAPTER_REGISTRY_FLAGS; typedef struct _CHANNEL_STATE_FLAGS { - ULONG Initialized : 1; ULONG NoMoreIO : 1; ULONG QueuePaused : 1; @@ -112,31 +109,14 @@ typedef struct _CHANNEL_STATE_FLAGS { ULONG NcqErrorRecoveryInProcess : 1; ULONG PuisEnabled : 1; - ULONG Reserved0 : 12; + ULONG IdentifyDeviceSuccess : 1; + ULONG NeedQDR : 1; + ULONG PowerUpInitializationInProgress : 1; //Note: this field indicates that init/preserved settings commands(invoked by power up) are not completed yet. + ULONG Reserved0 : 9; ULONG Reserved1; } CHANNEL_STATE_FLAGS, *PCHANNEL_STATE_FLAGS; -typedef struct _ATA_IO_RECORD { - - ULONG SuccessCount; - - ULONG CrcErrorCount; - ULONG MediaErrorCount; - ULONG EndofMediaCount; - ULONG IllegalCommandCount; - ULONG AbortedCommandCount; - ULONG DeviceFaultCount; - - ULONG OtherErrorCount; - - ULONG NcqReadLogErrorCount; // used to record the READ LOG EXT command error count when used for NCQ Error Recovery. - - ULONG PortDriverResetCount; - ULONG TotalResetCount; - -} ATA_IO_RECORD, *PATA_IO_RECORD; - // // Data structure to retrieve a Log Page // @@ -234,6 +214,7 @@ typedef struct _AHCI_DEVICE_EXTENSION { ATA_SUPPORTED_GPL_PAGES SupportedGPLPages; ATA_COMMAND_SUPPORTED SupportedCommands; + GP_LOG_HYBRID_INFORMATION_HEADER HybridInfo; LONG HybridCachingMediumEnableRefs; @@ -242,8 +223,21 @@ typedef struct _AHCI_DEVICE_EXTENSION { PUSHORT ReadLogExtPageData; STOR_PHYSICAL_ADDRESS ReadLogExtPageDataPhysicalAddress; + BOOLEAN UpdateCachedLogPageInfo; + UCHAR Reserved[3]; } AHCI_DEVICE_EXTENSION, *PAHCI_DEVICE_EXTENSION; +typedef struct _AHCI_DEVICE_LOG_PAGE_INFO { + + ATA_GPL_PAGES_TO_QUERY QueryLogPages; + + ATA_SUPPORTED_GPL_PAGES SupportedGPLPages; + ATA_COMMAND_SUPPORTED SupportedCommands; + + DOWNLOAD_MICROCODE_CAPABILITIES FirmwareUpdate; + +} AHCI_DEVICE_LOG_PAGE_INFO, *PAHCI_DEVICE_LOG_PAGE_INFO; + typedef struct _COMMAND_HISTORY { union { //0x10 bytes ATA_TASK_FILE InitialTaskFile; @@ -258,36 +252,6 @@ typedef struct _COMMAND_HISTORY { ULONG SrbStatus; } COMMAND_HISTORY, *PCOMMAND_HISTORY; -typedef struct _SLOT_MANAGER { - ULONG HighPriorityAttribute; - - ULONG NCQueueSlice; - ULONG NormalQueueSlice; - ULONG SingleIoSlice; - - ULONG CommandsIssued; - ULONG CommandsToComplete; - - // - // These issued slices are used to determine the type of command - // being programmed to adapter. - // They are used instead of reading PxCI and PxSACT. - // - ULONG NCQueueSliceIssued; - ULONG NormalQueueSliceIssued; - ULONG SingleIoSliceIssued; - - ULONG Reserved; - -} SLOT_MANAGER, *PSLOT_MANAGER; - -typedef struct _EXECUTION_HISTORY { - ULONG Function; - ULONG IS; - SLOT_MANAGER SlotManager; //SLOT_MANAGER from _AHCI_CHANNEL_EXTENSION - ULONG Px[0x10]; //Px registers value, end to AHCI_SNOTIFICATION -- SNTF -} EXECUTION_HISTORY, *PEXECUTION_HISTORY; - typedef struct _SLOT_STATE_FLAGS { UCHAR FUA :1; UCHAR Reserved :7; @@ -356,9 +320,13 @@ typedef struct _LOCAL_SCATTER_GATHER_LIST { ULONG NumberOfElements; ULONG_PTR Reserved; _Field_size_(NumberOfElements) - STOR_SCATTER_GATHER_ELEMENT List[33]; + STOR_SCATTER_GATHER_ELEMENT List[257]; } LOCAL_SCATTER_GATHER_LIST, *PLOCAL_SCATTER_GATHER_LIST; +// +// Note: When adding new members to AHCI SRB extension, make sure the uncached extension allocation +// is successful in dump mode because there is MAX size limitation. +// typedef struct _AHCI_SRB_EXTENSION { AHCI_COMMAND_TABLE CommandTable; // this field MUST to be the first one as it's asked to be 128 aligned USHORT AtaFunction; // if this field is 0, it means the command does not need to be sent to device @@ -371,6 +339,7 @@ typedef struct _AHCI_SRB_EXTENSION { AHCI_H2D_REGISTER_FIS Cfis; // info related to Host to Device FIS (0x27) }; PVOID DataBuffer; // go with Cdb field when needed. + STOR_PHYSICAL_ADDRESS DataBufferPhysicalAddress; // Physical address of DataBuffer. ULONG DataTransferLength; // go with Cdb field when needed. PLOCAL_SCATTER_GATHER_LIST Sgl; // pointer to the local or port provided SGL LOCAL_SCATTER_GATHER_LIST LocalSgl; // local SGL @@ -381,6 +350,7 @@ typedef struct _AHCI_SRB_EXTENSION { ULONGLONG StartTime; PVOID ResultBuffer; // for requests marked with ATA_FLAGS_RETURN_RESULTS + STOR_PHYSICAL_ADDRESS ResultBufferPhysicalAddress; // Physical address of ResultBuffer. ULONG ResultBufferLength; } AHCI_SRB_EXTENSION, *PAHCI_SRB_EXTENSION; @@ -411,10 +381,16 @@ typedef struct _STORAHCI_QUEUE { ULONG DeepestDepth; ULONG DepthHistoryIndex; ULONG DepthHistory[100]; + LARGE_INTEGER LastTimeStampAddQueue; + LARGE_INTEGER LastTimeStampRemoveQueue; } STORAHCI_QUEUE, *PSTORAHCI_QUEUE; typedef struct _AHCI_ADAPTER_EXTENSION AHCI_ADAPTER_EXTENSION, *PAHCI_ADAPTER_EXTENSION; +// +// Note: When adding new members to AHCI channel extension, make sure the uncached extension allocation +// is successful in dump mode because there is MAX size limitation. +// typedef struct _AHCI_CHANNEL_EXTENSION { //Adapter Characteristics PAHCI_ADAPTER_EXTENSION AdapterExtension; @@ -458,6 +434,7 @@ typedef struct _AHCI_CHANNEL_EXTENSION { } AutoPartialToSlumberDbgStats; + struct { ULONG RestorePreservedSettings :1; //NOTE: this field is accessed in InterlockedBitTestAndReset, bit position (currently: 0) is used there. ULONG BusChange :1; //NOTE: this field is accessed in InterlockedBitTestAndReset, bit position (currently: 1) is used there. @@ -498,8 +475,29 @@ typedef struct _AHCI_CHANNEL_EXTENSION { //Logging UCHAR CommandHistoryNextAvailableIndex; COMMAND_HISTORY CommandHistory[64]; + UCHAR ExecutionHistoryNextAvailableIndex; - EXECUTION_HISTORY ExecutionHistory[MAX_EXECUTION_HISTORY_ENTRY_COUNT]; + PEXECUTION_HISTORY ExecutionHistory; // For non-dump mode, contains MAX_EXECUTION_HISTORY_ENTRY_COUNT elements. + +//Statistics counters for telemetry event. + ULONG TotalCountPortReset; + ULONG TotalCountRunningStartFailed; + ULONG TotalCountPortErrorRecovery; + ULONG TotalCountNonQueuedErrorRecovery; + ULONG TotalCountNCQError; + ULONG TotalCountNCQErrorRecoveryComplete; + ULONG TotalCountSurpriseRemove; + ULONG TotalCountPowerSettingNotification; + ULONGLONG LastLogResetErrorRecoveryTime; + ULONG TotalCountPowerUp; + ULONG TotalCountPowerDown; + ULONG TotalPortStartTime; + ULONG TotalCountBusChange; + ULONG DeviceFailureThrottleFlag; + +//Statistics for tracking DPC. + LARGE_INTEGER LastTimeStampDpcStart; + LARGE_INTEGER LastTimeStampDpcCompletion; } AHCI_CHANNEL_EXTENSION, *PAHCI_CHANNEL_EXTENSION; @@ -512,13 +510,15 @@ typedef struct _ADAPTER_STATE_FLAGS { ULONG SupportsAcpiDSM : 1; // indicates if the system has _DSM method implemented to control port/device power. when the value is 1, the _DSM method at least supports powering on all connected devices. ULONG Removed : 1; + ULONG InterruptMessagePerPort : 1; ULONG D3ColdSupported : 1; ULONG D3ColdEnabled : 1; ULONG UseAdapterF1InsteadOfD3 : 1; + ULONG Removable : 1; - ULONG Reserved : 21; + ULONG Reserved : 20; } ADAPTER_STATE_FLAGS, *PADAPTER_STATE_FLAGS; @@ -595,6 +595,19 @@ typedef struct _AHCI_ADAPTER_EXTENSION { } AHCI_ADAPTER_EXTENSION, *PAHCI_ADAPTER_EXTENSION; // information that will be transferred to dump/hibernate environment + +typedef struct _AHCI_DUMP_PORT_CONTEXT { + + ULONG PortNumber; + CHANNEL_REGISTRY_FLAGS PortRegistryFlags; + + // + // Hybrid Disk Information Log + // + GP_LOG_HYBRID_INFORMATION_HEADER HybridInfo; + +} AHCI_DUMP_PORT_CONTEXT, *PAHCI_DUMP_PORT_CONTEXT; + typedef struct _AHCI_DUMP_CONTEXT { // adapter information USHORT VendorID; @@ -607,10 +620,6 @@ typedef struct _AHCI_DUMP_CONTEXT { ADAPTER_LOG_FLAGS LogFlags; // internal log levels ADAPTER_REGISTRY_FLAGS AdapterRegistryFlags; - // port information - ULONG DumpPortNumber; - CHANNEL_REGISTRY_FLAGS PortRegistryFlags; - // // Device telemetry descriptors for the boot device // @@ -618,12 +627,25 @@ typedef struct _AHCI_DUMP_CONTEXT { ULONG PrivateGPLogPageAddress; // - // Hybrid Disk Information Log + // Port information of dump devices // - GP_LOG_HYBRID_INFORMATION_HEADER HybridInfo; + ULONG PortCount; + AHCI_DUMP_PORT_CONTEXT Ports[1]; } AHCI_DUMP_CONTEXT, *PAHCI_DUMP_CONTEXT; +typedef struct _ATA_COMMAND_ERROR_LOG { + ULONG Version; + ULONG Size; + CHANNEL_STATE_FLAGS StateFlags; + CHANNEL_START_STATE StartState; + STORAGE_REQUEST_BLOCK Srb; + CDB Cdb; // To differentiate SRBs if the struct is reused + UCHAR Command; // ATA taksfile/AHCI FIS payload + UCHAR AtaStatus; + UCHAR AtaError; +} ATA_COMMAND_ERROR, *PATA_COMMAND_ERROR; + // Storport miniport driver entry routines, with prefix: "AhciHw" sp_DRIVER_INITIALIZE DriverEntry; @@ -669,8 +691,6 @@ AhciDeviceStart ( extern ULONG AhciPublicGPLogTableAddresses[TC_PUBLIC_DEVICEDUMP_CONTENT_GPLOG_MAX]; extern ULONG AhciGPLogPageIntoPrivate; - - #if _MSC_VER >= 1200 #pragma warning(pop) #else @@ -678,4 +698,3 @@ extern ULONG AhciGPLogPageIntoPrivate; #pragma warning(default:4201) #endif - diff --git a/storage/miniports/storahci/src/generic.h b/storage/miniports/storahci/src/generic.h index 4595820b7..1deedeaf0 100644 --- a/storage/miniports/storahci/src/generic.h +++ b/storage/miniports/storahci/src/generic.h @@ -28,7 +28,6 @@ Revision History: #include "srbhelper.h" - // storahci header files #include "common.h" #include "ahci.h" @@ -168,4 +167,3 @@ typedef struct _SENDCMDOUTPARAMS { #define DISABLE_SMART 0xD9 #define RETURN_SMART_STATUS 0xDA #define ENABLE_DISABLE_AUTO_OFFLINE 0xDB - diff --git a/storage/miniports/storahci/src/hbastat.c b/storage/miniports/storahci/src/hbastat.c index fc518675b..21c502d57 100644 --- a/storage/miniports/storahci/src/hbastat.c +++ b/storage/miniports/storahci/src/hbastat.c @@ -1,4 +1,4 @@ -/*++ +ο»Ώ/*++ Copyright (C) Microsoft Corporation, 2009 @@ -12,7 +12,7 @@ Module Name: Notes: -Revision History: +Revision History: --*/ @@ -92,11 +92,11 @@ Return value: } //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. + //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. ghc.HR = 1; StorPortWriteRegisterUlong(AdapterExtension, &abar->GHC.AsUlong, ghc.AsUlong); - //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. + //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. ghc.AsUlong = StorPortReadRegisterUlong(AdapterExtension, &abar->GHC.AsUlong); //5.2.2.1 H:Init //5.2.2.2 H:WaitForAhciEnable @@ -105,13 +105,13 @@ Return value: ghc.AsUlong = StorPortReadRegisterUlong(AdapterExtension, &abar->GHC.AsUlong); } - //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. + //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. if(i == 50) { AdapterExtension->ErrorFlags = (1 << 29); return FALSE; } - //When GHC.HR is set to ‘1’, GHC.AE, GHC.IE, the IS register, and all port register fields (except PxFB/PxFBU/PxCLB/PxCLBU) that are not HwInit in the HBA’s register memory space are reset. The HBA’s configuration space and all other global registers/bits are not affected by setting GHC.HR to ‘1’. Any HwInit bits in the port specific registers are not affected by setting GHC.HR to ‘1’. The port specific registers PxFB, PxFBU, PxCLB, and PxCLBU are not affected by setting GHC.HR to ‘1’. If the HBA supports staggered spin-up, the PxCMD.SUD bit will be reset to ‘0’; software is responsible for setting the PxCMD.SUD and PxSCTL.DET fields appropriately such that communication can be established on the Serial ATA link. If the HBA does not support staggered spin-up, the HBA reset shall cause a COMRESET to be sent on the port + //When GHC.HR is set to β€˜1’, GHC.AE, GHC.IE, the IS register, and all port register fields (except PxFB/PxFBU/PxCLB/PxCLBU) that are not HwInit in the HBA’s register memory space are reset. The HBA’s configuration space and all other global registers/bits are not affected by setting GHC.HR to β€˜1’. Any HwInit bits in the port specific registers are not affected by setting GHC.HR to β€˜1’. The port specific registers PxFB, PxFBU, PxCLB, and PxCLBU are not affected by setting GHC.HR to β€˜1’. If the HBA supports staggered spin-up, the PxCMD.SUD bit will be reset to β€˜0’; software is responsible for setting the PxCMD.SUD and PxSCTL.DET fields appropriately such that communication can be established on the Serial ATA link. If the HBA does not support staggered spin-up, the HBA reset shall cause a COMRESET to be sent on the port return TRUE; } @@ -124,10 +124,10 @@ AhciCOMRESET( ) /* PHY Reset:COMRESET - SCTL.DET Controls the HBA’s device detection and interface initialization. + SCTL.DET Controls the HBA’s device detection and interface initialization. DET=1 Performs interface communication initialization sequence to establish communication. This is functionally equivalent to a hard reset and results in the interface being reset and communications reinitialized. While this field is 1h, COMRESET is transmitted on the interface. Software should leave the DET field set to 1h for a minimum of 1 millisecond to ensure that a COMRESET is sent on the interface. - since we are in 5.3.2.3 P:NotRunning and PxCMD.SUD = ‘0’ does this still take us to P:StartComm? + since we are in 5.3.2.3 P:NotRunning and PxCMD.SUD = β€˜0’ does this still take us to P:StartComm? Called By: AhciPortReset,AhciNonQueuedErrorRecovery,P_Running_WaitWhileDET1,AhciHwControlIdeStart It assumes: @@ -191,7 +191,7 @@ Return Values: StorPortWriteRegisterUlong(ChannelExtension->AdapterExtension, &Px->SCTL.AsUlong, sctl.AsUlong); //2.2 Wait for DET to be set - // AHCI 10.4.2 After clearing PxSCTL.DET to 0h, software should wait for communication to be re-established as indicated by bit 0 of PxSSTS.DET being set to ‘1’. + // AHCI 10.4.2 After clearing PxSCTL.DET to 0h, software should wait for communication to be re-established as indicated by bit 0 of PxSSTS.DET being set to β€˜1’. // typically, it will be done in 10ms. max wait 30ms to be safe StorPortStallExecution(50); ssts.AsUlong = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &Px->SSTS.AsUlong); @@ -279,8 +279,8 @@ Return value: //1.1 Clear CMD.ST cmd.AsUlong = StorPortReadRegisterUlong(adapterExtension, &Px->CMD.AsUlong); //AHCI 10.3.2 on FRE it says: - //Software shall not clear this [FRE] bit while PxCMD.ST remains set to ‘1’." - //System software places a port into the idle state by clearing PxCMD.ST and waiting for PxCMD.CR to return ‘0’ when read. + //Software shall not clear this [FRE] bit while PxCMD.ST remains set to β€˜1’." + //System software places a port into the idle state by clearing PxCMD.ST and waiting for PxCMD.CR to return β€˜0’ when read. cmd.ST = 0; StorPortWriteRegisterUlong(adapterExtension, &Px->CMD.AsUlong, cmd.AsUlong); @@ -304,7 +304,7 @@ Return value: tfd.AsUlong = StorPortReadRegisterUlong(adapterExtension, &ChannelExtension->Px->TFD.AsUlong); if (tfd.STS.BSY) { - // AHCI 3.3.7 Command List Override (CLO): Setting this bit to ‘1’ causes PxTFD.STS.BSY and PxTFD.STS.DRQ to be cleared to ‘0’. + // AHCI 3.3.7 Command List Override (CLO): Setting this bit to β€˜1’ causes PxTFD.STS.BSY and PxTFD.STS.DRQ to be cleared to β€˜0’. // This allows a software reset to be transmitted to the device regardless of whether the BSY and DRQ bits are still set in the PxTFD.STS register. // Do this to make sure the port can stop @@ -323,9 +323,9 @@ Return value: StorPortStallExecution(5000); //5 milliseconds } - //AHCI 10.4.2 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 recover. + //AHCI 10.4.2 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 recover. if (i == 101) { - RecordExecutionHistory(ChannelExtension, 0x10350011); // P_NotRunning, PxCMD.CR or PxCMD.FR do not clear to ‘0’ correctly + RecordExecutionHistory(ChannelExtension, 0x10350011); // P_NotRunning, PxCMD.CR or PxCMD.FR do not clear to β€˜0’ correctly return FALSE; } @@ -339,17 +339,17 @@ Return value: StorPortStallExecution(50); //50 microseconds } - //If CI does not clear to ‘0’ correctly abort the stop + //If CI does not clear to β€˜0’ correctly abort the stop if (i == 101) { - RecordExecutionHistory(ChannelExtension, 0x10360011); // P_NotRunning, CI does not clear to ‘0’ + RecordExecutionHistory(ChannelExtension, 0x10360011); // P_NotRunning, CI does not clear to β€˜0’ return FALSE; } //2.1 Clear CMD.FRE cmd.AsUlong = StorPortReadRegisterUlong(adapterExtension, &Px->CMD.AsUlong); if( (cmd.FRE | cmd.FR) != 0 ) { - //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. - //AHCI 10.3.2 Software shall not clear this bit while PxCMD.ST or PxCMD.CR is set to ‘1’. + //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. + //AHCI 10.3.2 Software shall not clear this bit while PxCMD.ST or PxCMD.CR is set to β€˜1’. cmd.FRE = 0; StorPortWriteRegisterUlong(adapterExtension, &Px->CMD.AsUlong, cmd.AsUlong); @@ -371,18 +371,18 @@ Return value: StorPortStallExecution(5000); // 5 milliseconds } - if ( i == 101 ) { //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 recover. + if ( i == 101 ) { //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 recover. if (supportsCLO) { - RecordExecutionHistory(ChannelExtension, 0x10380011); // P_NotRunning, PxCMD.CR or PxCMD.FR do not clear to ‘0’, CLO enabled + RecordExecutionHistory(ChannelExtension, 0x10380011); // P_NotRunning, PxCMD.CR or PxCMD.FR do not clear to β€˜0’, CLO enabled } else { - RecordExecutionHistory(ChannelExtension, 0x10370011); // P_NotRunning, PxCMD.CR or PxCMD.FR do not clear to ‘0’, CLO not enabled + RecordExecutionHistory(ChannelExtension, 0x10370011); // P_NotRunning, PxCMD.CR or PxCMD.FR do not clear to β€˜0’, CLO not enabled } return FALSE; } } //2.2 wait for CLO to clear - // AHCI 3.3.7 Software must wait for CLO to be cleared to ‘0’ before setting PxCMD.ST to ‘1’. + // AHCI 3.3.7 Software must wait for CLO to be cleared to β€˜0’ before setting PxCMD.ST to β€˜1’. // register bit CLO might be set in this function, and it should have been cleared before function exit. if ( supportsCLO && (cmd.CLO == 0) ) { for (i = 1; i < 101; i++) { @@ -394,7 +394,7 @@ Return value: } if ( i == 101 ) { - RecordExecutionHistory(ChannelExtension, 0x10390011); // P_NotRunning, PxCMD.CLO not clear to ‘0’, CLO enabled + RecordExecutionHistory(ChannelExtension, 0x10390011); // P_NotRunning, PxCMD.CLO not clear to β€˜0’, CLO enabled return FALSE; } } @@ -464,6 +464,14 @@ RunNextPort( } else { //starting all ports process is completed. adapterExtension->InRunningPortsProcess = FALSE; + + // + // Note: If we are at DIRQL when get here, it implies previous port start finishes quickly since no + // timer scheduled during port start. Then there is no need to log the port start time. + // + if (!AtDIRQL) { + AhciPortStartTelemetry(adapterExtension); + } } return; @@ -588,7 +596,7 @@ It performs: 1.2 Next, is the port somehow already running? 1.3 Make sure the device knows it is supposed to be spun up 1.4 Check to make sure that FR and CR are both 0. Attempt to bring the controller into a consistent state by stopping the controller. - 1.5 CMD.ST has to be set, when that happens check to see that PxSSTS.DET is not ‘4h’ + 1.5 CMD.ST has to be set, when that happens check to see that PxSSTS.DET is not β€˜4h’ 2.1 Dispatch to the current state (states are responsible for selecting the next state) @@ -633,7 +641,6 @@ Return Values: return TRUE; } - //1.3 Make sure the device knows it is supposed to be spun up cmd.SUD = 1; StorPortWriteRegisterUlong(adapterExtension, &px->CMD.AsUlong, cmd.AsUlong); @@ -662,7 +669,7 @@ Return Values: } } - //1.5 CMD.ST has to be set, when that happens check to see that PxSSTS.DET is not ‘4h’ + //1.5 CMD.ST has to be set, when that happens check to see that PxSSTS.DET is not β€˜4h’ ssts.AsUlong = StorPortReadRegisterUlong(adapterExtension, &px->SSTS.AsUlong); if( ssts.DET == 0x4) { P_Running_StartFailed(ChannelExtension, TimerCallbackProcess); @@ -1088,10 +1095,10 @@ Affected Variables/Registers: */ { - AHCI_COMMAND cmd; - AHCI_TASK_FILE_DATA tfd; - AHCI_SERIAL_ATA_ERROR serr; - USHORT totalstarttime; + AHCI_COMMAND cmd; + AHCI_TASK_FILE_DATA tfd; + AHCI_SERIAL_ATA_ERROR serr; + USHORT totalstarttime; PAHCI_ADAPTER_EXTENSION adapterExtension = ChannelExtension->AdapterExtension; @@ -1099,7 +1106,7 @@ Affected Variables/Registers: RecordExecutionHistory(ChannelExtension, 0x0000001a);//P_Running_WaitOnBSYDRQ tfd.AsUlong = StorPortReadRegisterUlong(adapterExtension, &ChannelExtension->Px->TFD.AsUlong); - //1.1 Enable BSY and DRQ to go to 0 + //1.1 Enable BSY and DRQ to go to 0 if ( (tfd.STS.BSY) || (tfd.STS.DRQ) ) { //When [serr.DIAG.X is] set to one this bit indicates a COMINIT signal was received. This bit is reflected in the P0IS.PCS bit. //to allow the TFD to be updated serr.DIAG.X must be cleared. @@ -1107,10 +1114,10 @@ Affected Variables/Registers: serr.DIAG.X = 1; StorPortWriteRegisterUlong(adapterExtension, &ChannelExtension->Px->SERR.AsUlong, serr.AsUlong); } - //3.1 Set ST to 1 + //3.1 Set ST to 1 if ( ( tfd.STS.BSY == 0) && ( tfd.STS.DRQ == 0) ) { - STOR_LOCK_HANDLE lockhandle = {InterruptLock, 0}; - BOOLEAN needSpinLock; + STOR_LOCK_HANDLE lockhandle = { InterruptLock, 0 }; + BOOLEAN needSpinLock; if ( TimerCallbackProcess && (ChannelExtension->StartState.DirectStartInProcess == 1) ) { //This is timer callback and a direct port start process has been started separately, bail out this one. @@ -1124,11 +1131,11 @@ Affected Variables/Registers: AhciInterruptSpinlockAcquire(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockhandle); } - //3.2 Enable Interrupts on the channel (AHCI 1.1 Section 10.1.2 - 7) + //3.2 Enable Interrupts on the channel (AHCI 1.1 Section 10.1.2 - 7) PortClearPendingInterrupt(ChannelExtension); Set_PxIE(ChannelExtension, &ChannelExtension->Px->IE); - //We made it! Set ST and start the IO we have collected! + //We made it! Set ST and start the IO we have collected! cmd.AsUlong = StorPortReadRegisterUlong(adapterExtension, &ChannelExtension->Px->CMD.AsUlong); cmd.ST = 1; StorPortWriteRegisterUlong(adapterExtension, &ChannelExtension->Px->CMD.AsUlong, cmd.AsUlong); @@ -1138,7 +1145,19 @@ Affected Variables/Registers: StorPortDebugPrint(3, "StorAHCI - LPM: Port %02d - Port Started\n", ChannelExtension->PortNumber); - //Start requests on this Port + // + // Note: If ChannelStateBSYDRQCount is larger than 50, which implies the other counts are + // all cleared to 0 according to port start state machine implementation. But typically + // WaitOnBSYDRQ is always the most significant time consuming phase of port start, so the + // total port start time here is still able to reflect the rough port start status. + // + ChannelExtension->TotalPortStartTime = (ULONG)(ChannelExtension->StartState.ChannelStateDETCount + + ChannelExtension->StartState.ChannelStateDET1Count + + ChannelExtension->StartState.ChannelStateDET3Count + + ChannelExtension->StartState.ChannelStateFRECount + + (ChannelExtension->StartState.ChannelStateBSYDRQCount * 2)) * 10; + + //Start requests on this Port ActivateQueue(ChannelExtension, TRUE); ChannelExtension->StartState.DirectStartInProcess = 0; @@ -1149,16 +1168,20 @@ Affected Variables/Registers: AhciInterruptSpinlockRelease(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockhandle); } + if (ChannelExtension->StateFlags.NeedQDR) { + StorPortIssueDpc(ChannelExtension->AdapterExtension, &(ChannelExtension->BusChangeDpc), ChannelExtension, NULL); + ChannelExtension->StateFlags.NeedQDR = FALSE; + } + RunNextPort(ChannelExtension, (!TimerCallbackProcess && ChannelExtension->StartState.AtDIRQL)); return; } else { - //2.3 After waiting for the remainder of the 60 second maximum Channel Start time for BSY and DRQ to clear, give up - //Some big HDDs takes close to 20 second to spin up + //2.3 After waiting for the remainder of the 60 second maximum Channel Start time for BSY and DRQ to clear, give up + //Some big HDDs takes close to 20 second to spin up ULONG portStartTimeoutIn10MS = (AHCI_PORT_START_TIMEOUT_IN_SECONDS * 100); - // calculate the total time in unit of 10 ms. totalstarttime = ChannelExtension->StartState.ChannelStateDETCount + ChannelExtension->StartState.ChannelStateDET1Count + @@ -1170,7 +1193,7 @@ Affected Variables/Registers: P_Running_StartFailed(ChannelExtension, TimerCallbackProcess); RecordExecutionHistory(ChannelExtension, 0x00ff001a);//P_Running_WaitOnBSYDRQ timed out return; - //2.2 After a total amount of 1 second working on the start COMRESET + //2.2 After a total amount of 1 second working on the start COMRESET } else if ( ChannelExtension->StartState.ChannelStateBSYDRQCount == 50 ){ //Stop FRE,FR in preparation for RESET if ( !P_NotRunning(ChannelExtension, ChannelExtension->Px) ){ @@ -1196,7 +1219,7 @@ Affected Variables/Registers: RecordExecutionHistory(ChannelExtension, 0x00fd001a);//P_Running_WaitOnBSYDRQ crossing 1 second. COMRESET done, going to WaitOnDET3 P_Running_WaitOnDET3(ChannelExtension, TimerCallbackProcess); return; - //2.1 Wait for the rest of the 4 seconds for BSY and DRQ to clear + //2.1 Wait for the rest of the 4 seconds for BSY and DRQ to clear } else { ChannelExtension->StartState.ChannelStateBSYDRQCount++; RecordExecutionHistory(ChannelExtension, 0x0008001a);//P_Running_WaitOnBSYDRQ still waiting @@ -1244,8 +1267,21 @@ Affected Variables/Registers: none */ { - STOR_LOCK_HANDLE lockhandle = {InterruptLock, 0}; - BOOLEAN needSpinLock; + STOR_LOCK_HANDLE lockhandle = { InterruptLock, 0 }; + BOOLEAN needSpinLock; + + ++(ChannelExtension->TotalCountRunningStartFailed); + // + // Note: If ChannelStateBSYDRQCount is larger than 50, which implies the other counts are + // all cleared to 0 according to port start state machine implementation. But typically + // WaitOnBSYDRQ is always the most significant time consuming phase of port start, so the + // total port start time here is still able to reflect the rough port start status. + // + ChannelExtension->TotalPortStartTime = (ULONG)(ChannelExtension->StartState.ChannelStateDETCount + + ChannelExtension->StartState.ChannelStateDET1Count + + ChannelExtension->StartState.ChannelStateDET3Count + + ChannelExtension->StartState.ChannelStateFRECount + + (ChannelExtension->StartState.ChannelStateBSYDRQCount * 2)) * 10; if ( TimerCallbackProcess && (ChannelExtension->StartState.DirectStartInProcess == 1) ) { //This is timer callback and a direct port start process has been started separately, bail out this one. @@ -1259,7 +1295,7 @@ Affected Variables/Registers: AhciInterruptSpinlockAcquire(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockhandle); } - //1.1 update state machines + //1.1 update state machines ChannelExtension->StartState.ChannelNextStartState = StartFailed; ChannelExtension->StateFlags.IgnoreHotplugInterrupt = FALSE; ChannelExtension->StateFlags.D3ColdEnabled = FALSE; //this flag may be set to "TRUE" for last connected device, clear it when no device connected. @@ -1281,7 +1317,7 @@ Affected Variables/Registers: AhciZeroMemory((PCHAR)&ChannelExtension->DeviceExtension->SupportedGPLPages, sizeof(ATA_SUPPORTED_GPL_PAGES)); AhciZeroMemory((PCHAR)&ChannelExtension->DeviceExtension->SupportedCommands, sizeof(ATA_COMMAND_SUPPORTED)); - //1.2 clear out the programmed slots + //1.2 clear out the programmed slots ChannelExtension->SlotManager.CommandsToComplete = GetOccupiedSlots(ChannelExtension); ChannelExtension->SlotManager.CommandsIssued = 0; ChannelExtension->SlotManager.NCQueueSliceIssued = 0; @@ -1292,11 +1328,11 @@ Affected Variables/Registers: ChannelExtension->SlotManager.SingleIoSlice = 0; ChannelExtension->SlotManager.HighPriorityAttribute = 0; - //1.3 Enable Interrupts on the Channel (AHCI 1.1 Section 10.1.2 - 7) + //1.3 Enable Interrupts on the Channel (AHCI 1.1 Section 10.1.2 - 7) PortClearPendingInterrupt(ChannelExtension); Set_PxIE(ChannelExtension, &ChannelExtension->Px->IE); - //2.1 Call AhciPortFailAllIos to complete all outstanding commands now that ChannelNextStartState is StartFailed + //2.1 Call AhciPortFailAllIos to complete all outstanding commands now that ChannelNextStartState is StartFailed AhciPortFailAllIos(ChannelExtension, SRB_STATUS_NO_DEVICE, TRUE); ChannelExtension->StartState.DirectStartInProcess = 0; @@ -1320,11 +1356,11 @@ AHCI 1.1 Section 6.2.2.1 "The flow for system software to recover from an error when non-queued commands are issued is as follows: Reads PxCI to see which commands are still outstanding Reads PxCMD.CCS to determine the slot that the HBA was processing when the error occurred - Clears PxCMD.ST to ‘0’ to reset the PxCI register, waits for PxCMD.CR to clear to ‘0’ + Clears PxCMD.ST to β€˜0’ to reset the PxCI register, waits for PxCMD.CR to clear to β€˜0’ Clears any error bits in PxSERR to enable capturing new errors. Clears status bits in PxIS as appropriate - If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to ‘1’, issue a COMRESET to the device to put it in an idle state - Sets PxCMD.ST to ‘1’ to enable issuing new commands + If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to β€˜1’, issue a COMRESET to the device to put it in an idle state + Sets PxCMD.ST to β€˜1’ to enable issuing new commands It assumes: Called asynchronously @@ -1343,10 +1379,10 @@ It performs: 1.1 Initialize 1.2 Reads PxCI to see which commands are still outstanding 1.3 Reads PxCMD.CCS to determine the slot that the HBA was processing when the error occurred - 1.4.1 Clears PxCMD.ST to ‘0’ to reset the PxCI register, waits for PxCMD.CR to clear to ‘0’ + 1.4.1 Clears PxCMD.ST to β€˜0’ to reset the PxCI register, waits for PxCMD.CR to clear to β€˜0’ 1.5 Clears any error bits in PxSERR to enable capturing new errors. 1.6 Clears status bits in PxIS as appropriate - 1.4.2 If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to ‘1’, issue a COMRESET to the device to put it in an idle state + 1.4.2 If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to β€˜1’, issue a COMRESET to the device to put it in an idle state 1.4.3 If a COMRESET was issued, restore Preserved Settings 1.4.4 Start the channel @@ -1374,6 +1410,16 @@ Affected Variables/Registers: //1.1 Initialize variables RecordExecutionHistory(ChannelExtension, 0x00000013);//AhciNonQueuedErrorRecovery + ++(ChannelExtension->TotalCountNonQueuedErrorRecovery); + AhciTelemetryLogResetErrorRecovery(ChannelExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + AhciTelemetryEventIdNonqueuedErrorRecovery, + "AhciNonQueuedErrorRecovery", + 0, + NULL, + 0 + ); + senseSrb = NULL; performedCOMRESET = FALSE; @@ -1383,7 +1429,7 @@ Affected Variables/Registers: //1.3 Reads PxCMD.CCS to determine the slot that the HBA was processing when the error occurred cmd.AsUlong = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CMD.AsUlong); - //1.4 Clears PxCMD.ST to ‘0’ to reset the PxCI register, waits for PxCMD.CR to clear to ‘0’ + //1.4 Clears PxCMD.ST to β€˜0’ to reset the PxCI register, waits for PxCMD.CR to clear to β€˜0’ if ( !P_NotRunning(ChannelExtension, ChannelExtension->Px) ){ //This clears PxCI RecordExecutionHistory(ChannelExtension, 0x10160013);//AhciNonQueuedErrorRecovery, Port Stop Failed AhciPortReset(ChannelExtension, FALSE); @@ -1434,6 +1480,10 @@ Affected Variables/Registers: // Keep in mind although Px.CMD.ST was just shut off, we are working with the previous snapshot of ST. if (cmd.ST == 1) { + + ULONG storStatus = STOR_STATUS_NOT_IMPLEMENTED; + STOR_REQUEST_INFO requestInfo = {0}; + srbExtension = GetSrbExtension(ChannelExtension->Slot[failingCommand].Srb); // Remove the errant command from the Commands Issued. ci &= ~(1 << failingCommand); @@ -1448,7 +1498,16 @@ Affected Variables/Registers: // Fill in the status of the command srbExtension->AtaStatus = ChannelExtension->TaskFileData.STS.AsUchar; srbExtension->AtaError = ChannelExtension->TaskFileData.ERR; - ChannelExtension->Slot[failingCommand].Srb->SrbStatus = SRB_STATUS_ERROR; + + // Check whether the failed command is paging IO, if so, return busy status to indicate storport to retry it. + storStatus = StorPortGetRequestInfo(ChannelExtension, (PSCSI_REQUEST_BLOCK)ChannelExtension->Slot[failingCommand].Srb, &requestInfo); + if ((storStatus == STOR_STATUS_SUCCESS) && + (requestInfo.Flags & REQUEST_INFO_PAGING_IO_FLAG)) { + ChannelExtension->Slot[failingCommand].Srb->SrbStatus = SRB_STATUS_BUSY; + } else { + ChannelExtension->Slot[failingCommand].Srb->SrbStatus = SRB_STATUS_ERROR; + } + // Handle Request Sense if needed if ( NeedRequestSense(ChannelExtension->Slot[failingCommand].Srb) ) { // This is for ATAPI device only @@ -1476,7 +1535,7 @@ Affected Variables/Registers: //1.6 Clears status bits in PxIS as appropriate //Handled in the Interrupt routine - //1.4.2 If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to ‘1’, issue a COMRESET to the device to put it in an idle state + //1.4.2 If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to β€˜1’, issue a COMRESET to the device to put it in an idle state tfd.AsUlong = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->TFD.AsUlong); if(tfd.STS.BSY || tfd.STS.DRQ) { @@ -1527,12 +1586,22 @@ NcqErrorRecoveryCompletion ( */ { PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); - ULONG issuedCommands = (ULONG)(ULONG_PTR)srbExtension->CompletionContext; - STOR_LOCK_HANDLE lockhandle = {InterruptLock, 0}; - BOOLEAN fallBacktoReset = FALSE; + ULONG issuedCommands = (ULONG)(ULONG_PTR)srbExtension->CompletionContext; + STOR_LOCK_HANDLE lockhandle = {InterruptLock, 0}; + BOOLEAN fallBacktoReset = FALSE; RecordExecutionHistory(ChannelExtension, 0x0000001b); //Enter NcqErrorRecoveryCompletion + ++(ChannelExtension->TotalCountNCQErrorRecoveryComplete); + AhciTelemetryLogResetErrorRecovery(ChannelExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + AhciTelemetryEventIdNCQErrorRecoveryComplete, + "NcqErrorRecoveryCompletion", + 0, + NULL, + 0 + ); + // // In case of reset happened when Read Log command was executing, all requests have been returned back to port driver. // There is no more recovery work this routine needs to do. @@ -1553,9 +1622,9 @@ NcqErrorRecoveryCompletion ( // Other commands will be put back into NCQ slices and will be issued again to adapter. // PGP_LOG_NCQ_COMMAND_ERROR ncqErrorLog = (PGP_LOG_NCQ_COMMAND_ERROR)ChannelExtension->DeviceExtension->ReadLogExtPageData; - UCHAR failingCommand; - PSTORAGE_REQUEST_BLOCK failingSrb = NULL; - PAHCI_SRB_EXTENSION failingSrbExtension = NULL; + UCHAR failingCommand; + PSTORAGE_REQUEST_BLOCK failingSrb = NULL; + PAHCI_SRB_EXTENSION failingSrbExtension = NULL; NT_ASSERT(ncqErrorLog->NonQueuedCmd == 0); NT_ASSERT(ncqErrorLog->NcqTag != 0); @@ -1569,71 +1638,89 @@ NcqErrorRecoveryCompletion ( if (((1 << failingCommand) & issuedCommands) != 0) { failingSrb = ChannelExtension->Slot[failingCommand].Srb; - NT_ASSERT(failingSrb != NULL); - - failingSrbExtension = GetSrbExtension(failingSrb); // - // Record error and status - // - failingSrbExtension->AtaStatus = ncqErrorLog->Status; - failingSrbExtension->AtaError = ncqErrorLog->Error; + // If the surprise remove happens just after the NCQ error handling, then + // there is chance that the failingSrb is NULL here. + // + if (failingSrb != NULL) { + failingSrbExtension = GetSrbExtension(failingSrb); - if( IsReturnResults(failingSrbExtension->Flags) ) { - SetReturnRegisterValues(ChannelExtension, failingSrb, NULL); - } + // + // Record error and status + // + failingSrbExtension->AtaStatus = ncqErrorLog->Status; + failingSrbExtension->AtaError = ncqErrorLog->Error; - // - // Get Sense Data from log, or from translation. - // - if ((ChannelExtension->DeviceExtension->IdentifyDeviceData->SerialAtaFeaturesSupported.NCQAutosense == 1) && - (ncqErrorLog->SenseKey != SCSI_SENSE_NO_SENSE)) { + if( IsReturnResults(failingSrbExtension->Flags) ) { + SetReturnRegisterValues(ChannelExtension, failingSrb, NULL); + } - StorPortDebugPrint(3, "StorAHCI - Port %02d - NCQ Error Recovery: NCQ Error Log read successfully. Sense Data returned: %02X/%02X/%02X\n", - ChannelExtension->PortNumber, ncqErrorLog->SenseKey, ncqErrorLog->ASC, ncqErrorLog->ASCQ); + // + // Get Sense Data from log, or from translation. + // + if ((ChannelExtension->DeviceExtension->IdentifyDeviceData->SerialAtaFeaturesSupported.NCQAutosense == 1) && + (ncqErrorLog->SenseKey != SCSI_SENSE_NO_SENSE)) { + + StorPortDebugPrint(3, "StorAHCI - Port %02d - NCQ Error Recovery: NCQ Error Log read successfully. Sense Data returned: %02X/%02X/%02X\n", + ChannelExtension->PortNumber, ncqErrorLog->SenseKey, ncqErrorLog->ASC, ncqErrorLog->ASCQ); + + AhciSetSenseData(failingSrb, SRB_STATUS_ERROR, ncqErrorLog->SenseKey, ncqErrorLog->ASC, ncqErrorLog->ASCQ); + } else { + // + // Set SrbStatus indicates it's an error condition. + // Sense data will be translated from Error register in ReleaseSlottedCommand() + // + StorPortDebugPrint(3, "StorAHCI - Port %02d - NCQ Error Recovery: NCQ Error Log read successfully. No Sense Data returned\n", ChannelExtension->PortNumber); + + failingSrb->SrbStatus = SRB_STATUS_ERROR; + AtaMapError(ChannelExtension, failingSrb, ChannelExtension->Slot[failingCommand].StateFlags.FUA); + } - AhciSetSenseData(failingSrb, SRB_STATUS_ERROR, ncqErrorLog->SenseKey, ncqErrorLog->ASC, ncqErrorLog->ASCQ); - } else { // - // Set SrbStatus indicates it's an error condition. - // Sense data will be translated from Error register in ReleaseSlottedCommand() + // Inform ReleaseSlottedCommand() function that sense data has been set. // - StorPortDebugPrint(3, "StorAHCI - Port %02d - NCQ Error Recovery: NCQ Error Log read successfully. No Sense Data returned\n", ChannelExtension->PortNumber); + SETMASK(failingSrbExtension->Flags, ATA_FLAGS_SENSEDATA_SET); - failingSrb->SrbStatus = SRB_STATUS_ERROR; - AtaMapError(ChannelExtension, failingSrb, ChannelExtension->Slot[failingCommand].StateFlags.FUA); - } + // + // Acquire spinlock before touching Slots. + // + AhciInterruptSpinlockAcquire(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockhandle); - // - // Inform ReleaseSlottedCommand() function that sense data has been set. - // - SETMASK(failingSrbExtension->Flags, ATA_FLAGS_SENSEDATA_SET); + // + // Remove the failing command from NCQ Slice. + // Put back other previous outstanding commands. + // + ChannelExtension->SlotManager.NCQueueSlice &= ~(1 << failingCommand); - // - // Acquire spinlock before touching Slots. - // - AhciInterruptSpinlockAcquire(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockhandle); + // + // Clear the NCQ Error Recovery state flag, so that queue will be resumed in ReleaseSlottedCommand(). + // + ChannelExtension->StateFlags.NcqErrorRecoveryInProcess = 0; - // - // Remove the failing command from NCQ Slice. - // Put back other previous outstanding commands. - // - ChannelExtension->SlotManager.NCQueueSlice &= ~(1 << failingCommand); + // + // Complete the failing command. + // + ChannelExtension->SlotManager.CommandsToComplete |= (1 << failingCommand); + ReleaseSlottedCommand(ChannelExtension, (UCHAR)failingCommand, TRUE); - // - // Clear the NCQ Error Recovery state flag, so that queue will be resumed in ReleaseSlottedCommand(). - // - ChannelExtension->StateFlags.NcqErrorRecoveryInProcess = 0; + ActivateQueue(ChannelExtension, TRUE); - // - // Complete the failing command. - // - ChannelExtension->SlotManager.CommandsToComplete |= (1 << failingCommand); - ReleaseSlottedCommand(ChannelExtension, (UCHAR)failingCommand, TRUE); + AhciInterruptSpinlockRelease(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockhandle); - ActivateQueue(ChannelExtension, TRUE); + } else { + // + // If failingSrb is NULL, clear the NCQ Error Recovery state flag. + // + ChannelExtension->StateFlags.NcqErrorRecoveryInProcess = 0; - AhciInterruptSpinlockRelease(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockhandle); + StorPortDebugPrint(3, "StorAHCI - Port %02d - NCQ Error Recovery: NCQ Error Log read successfully, but the failing srb is NULL!\n", ChannelExtension->PortNumber); + + // + // Capture the scenarios that failing srb is NULL, need investigate cases other than surprise remove. + // + NT_ASSERT(failingSrb != NULL); + } } else { StorPortDebugPrint(3, "StorAHCI - Port %02d - NCQ Error Recovery: NCQ Tag doesn't belong to issued commands.\n", ChannelExtension->PortNumber); @@ -1695,13 +1782,13 @@ AHCI 1.1 Section 6.2.2.2 Native Command Queuing Error Recovery The flow for system software to recover from an error when native command queuing commands are issued is as follows: - • Reads PxSACT to see which commands have not yet completed - • Clears PxCMD.ST to ‘0’ to reset the PxCI and PxSACT registers, waits for PxCMD.CR to clear to ‘0’ - • Clears any error bits in PxSERR to enable capturing new errors. - • Clears status bits in PxIS as appropriate - • If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to ‘1’, issue a COMRESET to the device to put it in an idle state - • Sets PxCMD.ST to ‘1’ to enable issuing new commands - • Issue READ LOG EXT to determine the cause of the error condition if software did not have to perform a reset (COMRESET or software reset) as part of the error recovery + β€’ Reads PxSACT to see which commands have not yet completed + β€’ Clears PxCMD.ST to β€˜0’ to reset the PxCI and PxSACT registers, waits for PxCMD.CR to clear to β€˜0’ + β€’ Clears any error bits in PxSERR to enable capturing new errors. + β€’ Clears status bits in PxIS as appropriate + β€’ If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to β€˜1’, issue a COMRESET to the device to put it in an idle state + β€’ Sets PxCMD.ST to β€˜1’ to enable issuing new commands + β€’ Issue READ LOG EXT to determine the cause of the error condition if software did not have to perform a reset (COMRESET or software reset) as part of the error recovery Software then either completes commands that did not finish with error to higher level software, or reissues them to the device. @@ -1733,6 +1820,15 @@ Called by: // RecordExecutionHistory(ChannelExtension, 0x00000014); //AhciNcqErrorRecovery + AhciTelemetryLogResetErrorRecovery(ChannelExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + AhciTelemetryEventIdNCQErrorRecovery, + "AhciNcqErrorRecovery Enter", + 0, + NULL, + 0 + ); + // // Reads PxSACT and PxCI to see which commands are still outstanding // @@ -1742,7 +1838,7 @@ Called by: #endif // - // Clears PxCMD.ST to ‘0’, waits for PxCMD.CR to clear to ‘0’ + // Clears PxCMD.ST to β€˜0’, waits for PxCMD.CR to clear to β€˜0’ // If it doesn't succeed, issue COMRESET to recover. // if ( !P_NotRunning(ChannelExtension, ChannelExtension->Px) ) { @@ -1761,7 +1857,7 @@ Called by: if (needCOMRESET == FALSE) { // - // If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to ‘1’, issue a COMRESET to the device to put it in an idle state + // If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to β€˜1’, issue a COMRESET to the device to put it in an idle state // ChannelExtension->TaskFileData has been read from register in ISR. // if ((ChannelExtension->TaskFileData.STS.BSY != 0) || (ChannelExtension->TaskFileData.STS.DRQ != 0)) { @@ -1863,6 +1959,15 @@ Called by: RecordExecutionHistory(ChannelExtension, 0x10000014); //Exit AhciNcqErrorRecovery + AhciTelemetryLogResetErrorRecovery(ChannelExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + AhciTelemetryEventIdNCQErrorRecovery, + "AhciNcqErrorRecovery Exit", + 0, + NULL, + 0 + ); + return; } @@ -1886,6 +1991,44 @@ Return Values: None */ { + ++(ChannelExtension->TotalCountPortErrorRecovery); + AhciTelemetryLogResetErrorRecovery(ChannelExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + AhciTelemetryEventIdPortErrorRecovery, + "AhciPortErrorRecovery", + 0, + NULL, + 0 + ); + + // Log the StateFlags + StorPortEtwChannelEvent8(ChannelExtension->AdapterExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + StorportEtwEventOperational, + AhciEtwEventHandleInterrupt, + L"AhciPortErrorRecovery", + STORPORT_ETW_EVENT_KEYWORD_COMMAND_TRACE, + StorportEtwLevelError, + StorportEtwEventOpcodeInfo, + NULL, + L"StateFlags", + *(ULONGLONG *)&(ChannelExtension->StateFlags), + L"PortErrRecovery", + ChannelExtension->TotalCountPortErrorRecovery, + L"StartState", + *(ULONGLONG *)&(ChannelExtension->StartState), + L"PortReset", + ChannelExtension->TotalCountPortReset, + L"RunningStrtFail", + ChannelExtension->TotalCountRunningStartFailed, + L"NonQ'dErrRecov", + ChannelExtension->TotalCountNonQueuedErrorRecovery, + L"NCQError|NCQErrorComplete", + (((ULONGLONG)ChannelExtension->TotalCountNCQError << 32) | + ((ULONGLONG)ChannelExtension->TotalCountNCQErrorRecoveryComplete)), + L"BusChange", + ChannelExtension->TotalCountBusChange); + if (ChannelExtension->StateFlags.CallAhciReportBusChange == 1) { // Handle AHCI 6.2.2.3 Recovery of Unsolicited COMINIT and hot plug // AhciPortBusChangeDpcRoutine() will issue RESET, ignore other error recovery marks. @@ -1894,6 +2037,11 @@ Return Values: ChannelExtension->StateFlags.CallAhciReset = 0; ChannelExtension->StateFlags.CallAhciNcqErrorRecovery = 0; + ++(ChannelExtension->TotalCountBusChange); + if (ChannelExtension->TotalCountBusChange >= AHCI_BUS_CHANGE_COUNT_WARNING_THRESHOLD) { + AhciPortMarkDeviceFailed(ChannelExtension, AhciDeviceFailureTooManyBusChange, 0, TRUE); + } + if (!IsDumpMode(ChannelExtension->AdapterExtension)) { StorPortIssueDpc(ChannelExtension->AdapterExtension, &ChannelExtension->BusChangeDpc, ChannelExtension, NULL); } else { @@ -1910,6 +2058,11 @@ Return Values: ChannelExtension->StateFlags.CallAhciReset = 0; ChannelExtension->StateFlags.CallAhciNcqErrorRecovery = 0; + ++(ChannelExtension->TotalCountNCQError); + if (ChannelExtension->TotalCountNCQError >= AHCI_NCQ_ERROR_COUNT_WARNING_THRESHOLD) { + AhciPortMarkDeviceFailed(ChannelExtension, AhciDeviceFailureTooManyNCQError, 0, TRUE); + } + if (SupportsEnhancedNcqErrorRecovery(ChannelExtension)) { AhciNcqErrorRecovery(ChannelExtension); } else { @@ -1932,6 +2085,10 @@ Return Values: // Handle AHCI 6.2.2.1 Non-Queued Error Recovery ChannelExtension->StateFlags.CallAhciNonQueuedErrorRecovery = 0; + if (ChannelExtension->TotalCountNonQueuedErrorRecovery >= AHCI_NON_QUEUED_ERROR_COUNT_WARNING_THRESHOLD) { + AhciPortMarkDeviceFailed(ChannelExtension, AhciDeviceFailureTooManyNonQueuedError, 0, TRUE); + } + AhciNonQueuedErrorRecovery(ChannelExtension); } @@ -1939,16 +2096,16 @@ Return Values: } BOOLEAN -AhciPortReset ( +AhciPortReset( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ BOOLEAN CompleteAllRequests ) /*++ -AhciPortReset can be called even if the miniport driver is not ready for another request. -The miniport driver should complete all pending requests and must reset the given channel. + AhciPortReset can be called even if the miniport driver is not ready for another request. + The miniport driver should complete all pending requests and must reset the given channel. It assumes: - nothing + DIRQL Called by: @@ -1970,16 +2127,106 @@ Return Values: If the reset failed the routine must return FALSE. --*/ { - ULONG commandsToCompleteCount; + ULONG commandsToCompleteCount = 0; + BOOLEAN isPortStartInProgress = FALSE; + BOOLEAN isCompletionDPCQueued = FALSE; + BOOLEAN isCompletionDPCInterrupted = FALSE; + + //1.1 Initialize Variables + ++(ChannelExtension->TotalCountPortReset); - //1.1 Initialize Variables - RecordExecutionHistory(ChannelExtension, 0x00000050);//AhciPortReset + if ((CompleteAllRequests) && + (ChannelExtension->StartState.ChannelNextStartState != StartComplete)) { + + NT_ASSERT(FALSE); + + isPortStartInProgress = TRUE; + RecordExecutionHistory(ChannelExtension, 0x10040050); //AhciPortReset, Port Start still in progress, device probably hang + + } else { + RecordExecutionHistory(ChannelExtension, 0x00000050); //AhciPortReset + } + + AhciTelemetryLogResetErrorRecovery(ChannelExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + AhciTelemetryEventIdPortReset, + (isPortStartInProgress ? "PortStartTakeTooLong" : "AhciPortReset"), + (isPortStartInProgress ? AHCI_TELEMETRY_FLAG_NOT_SUPPRESS_LOGGING : 0), + NULL, + 0 + ); + + if (ChannelExtension->CompletionQueue.LastTimeStampAddQueue.QuadPart > ChannelExtension->LastTimeStampDpcStart.QuadPart) { + isCompletionDPCQueued = TRUE; + } + + if (ChannelExtension->LastTimeStampDpcStart.QuadPart > ChannelExtension->LastTimeStampDpcCompletion.QuadPart) { + isCompletionDPCInterrupted = TRUE; + } + + if (isCompletionDPCQueued || isCompletionDPCInterrupted) { + + LARGE_INTEGER currentTimeStamp; + StorPortQuerySystemTime(¤tTimeStamp); + ULONGLONG duration = 0; + + NT_ASSERT(FALSE); + + if (isCompletionDPCQueued && isCompletionDPCInterrupted) { + if (ChannelExtension->LastTimeStampDpcStart.QuadPart < ChannelExtension->CompletionQueue.LastTimeStampRemoveQueue.QuadPart) { + duration = (ULONGLONG)(currentTimeStamp.QuadPart - ChannelExtension->CompletionQueue.LastTimeStampRemoveQueue.QuadPart); + } else { + duration = (ULONGLONG)(currentTimeStamp.QuadPart - ChannelExtension->LastTimeStampDpcStart.QuadPart); + } + } else if (isCompletionDPCQueued) { + duration = (ULONGLONG)(currentTimeStamp.QuadPart - ChannelExtension->CompletionQueue.LastTimeStampAddQueue.QuadPart); + } else if (isCompletionDPCInterrupted) { + duration = (ULONGLONG)(currentTimeStamp.QuadPart - ChannelExtension->LastTimeStampDpcStart.QuadPart); + } - //2.1 Stop the channel + RecordExecutionHistory(ChannelExtension, 0x10050050); //AhciPortReset, DPC queued or interrupted + + AhciTelemetryLogResetErrorRecovery(ChannelExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + AhciTelemetryEventIdPortReset, + "DPC queued or interrupted", + AHCI_TELEMETRY_FLAG_NOT_SUPPRESS_LOGGING, + "Duration(in 100ns)", + duration + ); + } + + StorPortEtwChannelEvent8(ChannelExtension->AdapterExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + StorportEtwEventOperational, + AhciEtwEventPortReset, + L"Port reset", + STORPORT_ETW_EVENT_KEYWORD_COMMAND_TRACE, + StorportEtwLevelInformational, + StorportEtwEventOpcodeInfo, + NULL, + L"PortReset", + ChannelExtension->TotalCountPortReset, + L"RunningStrtFail", + ChannelExtension->TotalCountRunningStartFailed, + L"PortErrRecovery", + ChannelExtension->TotalCountPortErrorRecovery, + L"NonQ'dErrRecov", + ChannelExtension->TotalCountNonQueuedErrorRecovery, + L"NCQErrRecovery", + ChannelExtension->TotalCountNCQError, + L"NCQErrRecovCmpl", + ChannelExtension->TotalCountNCQErrorRecoveryComplete, + L"SurpriseRemove", + ChannelExtension->TotalCountSurpriseRemove, + L"StateFlags", + *(ULONGLONG *)&(ChannelExtension->StateFlags)); + + //2.1 Stop the channel P_NotRunning(ChannelExtension, ChannelExtension->Px); - //2.2 Perform the COMRESET - // AHCI 10.1.2 - 3: 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 recover. + //2.2 Perform the COMRESET + // AHCI 10.1.2 - 3: 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 recover. AhciCOMRESET(ChannelExtension, ChannelExtension->Px); // @@ -1990,14 +2237,89 @@ Return Values: ChannelExtension->StateFlags.QueuePaused = 0; } - //2.3 If either Init Command or PreservedSettings Command is being processed, reset the command index to start from the first one again. - // The process will be continued by the Srb's completion routine. + if ((CompleteAllRequests) && + (ChannelExtension->StateFlags.QueuePaused == 1) && + (ChannelExtension->SlotManager.CommandsIssued == 0)) { + + // + // This should never happen. Resetting state. + // + + NT_ASSERT(FALSE); + + ChannelExtension->StateFlags.QueuePaused = 0; + + RecordExecutionHistory(ChannelExtension, 0x10010050); //AhciPortReset, Unpausing queue + AhciTelemetryLogResetErrorRecovery(ChannelExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + AhciTelemetryEventIdPortReset, + "Unpausing queue", + AHCI_TELEMETRY_FLAG_NOT_SUPPRESS_LOGGING, + NULL, + 0 + ); + } + + //2.3 If either Init Command or PreservedSettings Command is being processed, reset the command index to start from the first one again. + // The process will be continued by the Srb's completion routine. if (ChannelExtension->StateFlags.ReservedSlotInUse == 1) { ChannelExtension->DeviceInitCommands.CommandToSend = 0; ChannelExtension->PersistentSettings.SlotsToSend = ChannelExtension->PersistentSettings.Slots; } - //3.1 Complete all issued commands + if ((CompleteAllRequests) && + (ChannelExtension->StateFlags.ReservedSlotInUse == 1) && + (ChannelExtension->SlotManager.SingleIoSliceIssued == 0) && + ((ChannelExtension->SlotManager.SingleIoSlice & 0x1) == 0)) { + + NT_ASSERT(FALSE); + + // + // This could happen if init/preserved setting command completed by device, but the completion DPC is queued(or interrupted) for long time. + // If there is other scenario, it is needed to further triage. + // + // Note: Force clear ReservedSlotInUse flag only if corresponding registry key is set. + // Otherwise, there maybe add queue issue. E.g. Buggy device repeatedly generate bus change interrupt, + // which could result in add queue bugcheck eventually. + // + + if (!(isCompletionDPCQueued || isCompletionDPCInterrupted)) { + RecordExecutionHistory(ChannelExtension, 0x10020050); //AhciPortReset, ReservedSlotInUse flag maybe stuck + // + // There maybe possibility that init/restore preserved settings command stuck in device and never complete. + // + AhciPortMarkDeviceFailed(ChannelExtension, AhciDeviceFailureDeviceStuck, 0, TRUE); + } else { + RecordExecutionHistory(ChannelExtension, 0x10060050); //AhciPortReset, ReservedSlotInUse flag outstanding + } + + } + + if (((ChannelExtension->SlotManager.SingleIoSliceIssued & 0x1) != 0) && + (ChannelExtension->Slot[0].Srb != NULL) && + ((PSTORAGE_REQUEST_BLOCK)&ChannelExtension->Local.Srb == ChannelExtension->Slot[0].Srb)) { + + if ((ChannelExtension->Local.SrbExtension != NULL) && + ((ChannelExtension->Local.SrbExtension->CompletionRoutine == IssuePreservedSettingCommands) || + (ChannelExtension->Local.SrbExtension->CompletionRoutine == IssueInitCommands))) { + + BOOLEAN isInitCommand = (ChannelExtension->Local.SrbExtension->CompletionRoutine == IssueInitCommands) ? (TRUE) : (FALSE); + + NT_ASSERT(FALSE); + + RecordExecutionHistory(ChannelExtension, 0x10030050); //AhciPortReset, Init/Preserved Setting command timeout + AhciTelemetryLogResetErrorRecovery(ChannelExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + AhciTelemetryEventIdPortReset, + "Slot 0 command timeout", + AHCI_TELEMETRY_FLAG_NOT_SUPPRESS_LOGGING, + (isInitCommand ? "Init Command" : "Preserved Setting Command"), + 0 + ); + } + } + + //3.1 Complete all issued commands ChannelExtension->SlotManager.CommandsToComplete = ChannelExtension->SlotManager.CommandsIssued; ChannelExtension->SlotManager.CommandsIssued = 0; ChannelExtension->SlotManager.NCQueueSliceIssued = 0; @@ -2030,17 +2352,17 @@ Return Values: AhciCompleteIssuedSRBs(ChannelExtension, completeStatus, TRUE); //AhciPortReset is under Interrupt spinlock } - //3.2 Complete all other commands miniport own for this device, when it's necessary or when the Reset is requested by Storport + //3.2 Complete all other commands miniport own for this device, when it's necessary or when the Reset is requested by Storport if (CompleteAllRequests) { AhciPortFailAllIos(ChannelExtension, SRB_STATUS_BUS_RESET, TRUE); } - //4.1 Restore device configuration + //4.1 Restore device configuration if (ChannelExtension->StateFlags.ReservedSlotInUse == 0) { RestorePreservedSettings(ChannelExtension, TRUE); } - //2.3 Start the channel + //2.3 Start the channel P_Running_StartAttempt(ChannelExtension, TRUE); //AhciPortReset is under Interrupt spinlock // record that the channel is reset. @@ -2057,4 +2379,3 @@ Return Values: #pragma warning(default:4214) #pragma warning(default:4201) #endif - diff --git a/storage/miniports/storahci/src/hbastat.h b/storage/miniports/storahci/src/hbastat.h index 409866684..b401a81e4 100644 --- a/storage/miniports/storahci/src/hbastat.h +++ b/storage/miniports/storahci/src/hbastat.h @@ -107,4 +107,3 @@ AhciPortErrorRecovery( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension ); - diff --git a/storage/miniports/storahci/src/inbox/storahci.vcxproj b/storage/miniports/storahci/src/inbox/storahci.vcxproj index 7e6948795..eb2e4f82a 100644 --- a/storage/miniports/storahci/src/inbox/storahci.vcxproj +++ b/storage/miniports/storahci/src/inbox/storahci.vcxproj @@ -76,11 +76,6 @@ - - true - true - TracePrint((LEVEL,FLAGS,MSG,...)) - true true @@ -186,5 +181,37 @@ + + + true + true + TracePrint((LEVEL,FLAGS,MSG,...)) + + + true + true + TracePrint((LEVEL,FLAGS,MSG,...)) + + + true + true + TracePrint((LEVEL,FLAGS,MSG,...)) + + + true + true + TracePrint((LEVEL,FLAGS,MSG,...)) + + + true + true + TracePrint((LEVEL,FLAGS,MSG,...)) + + + true + true + TracePrint((LEVEL,FLAGS,MSG,...)) + + \ No newline at end of file diff --git a/storage/miniports/storahci/src/io.c b/storage/miniports/storahci/src/io.c index 3a5178545..44913b555 100644 --- a/storage/miniports/storahci/src/io.c +++ b/storage/miniports/storahci/src/io.c @@ -44,7 +44,6 @@ Affected Variables/Registers: Return Value: --*/ { - UCHAR activeCount = 0; UCHAR emptyCount; UCHAR requestCount; @@ -52,26 +51,27 @@ Return Value: ULONG slotToActivate = 0; UCHAR i; - //1 Device's queue depth is smaller than Controller's + // 1. Device's queue depth is smaller than Controller's NT_ASSERT(ChannelExtension->DeviceExtension[0].DeviceParameters.MaxDeviceQueueDepth <= ChannelExtension->AdapterExtension->CAP.NCS); - //count the number of slots already in use + // Count the number of slots already in use if (ChannelExtension->SlotManager.CommandsIssued > 0) { activeCount = NumberOfSetBits(ChannelExtension->SlotManager.CommandsIssued); } - //1.1 Check if all slots are active. + + // 1.1 Check if all slots are active. if (activeCount >= ChannelExtension->DeviceExtension[0].DeviceParameters.MaxDeviceQueueDepth) { - //if all possible slots are full, no matter what, return no work (0) + // If all possible slots are full, no matter what, return no work (0) return 0; } - //2 Look for any entry from last active slot + // 2. Look for any entry from last active slot requestCount = NumberOfSetBits(TargetSlots); lastActiveSlot = ChannelExtension->LastActiveSlot; emptyCount = ChannelExtension->DeviceExtension[0].DeviceParameters.MaxDeviceQueueDepth - activeCount; - //3.1 Look for any entry from last active slot + // 3.1 Look for any entry from last active slot for (i = lastActiveSlot; i <= ChannelExtension->AdapterExtension->CAP.NCS; i++) { if ((TargetSlots & (1 << i)) > 0) { slotToActivate |= (1 << i); @@ -84,8 +84,8 @@ Return Value: } } - //3.2 Look for any entry from beginning to last active slot - //Slot 0 is reserved for internal command + // 3.2 Look for any entry from beginning to last active slot + // Slot 0 is reserved for internal command for (i = 1 ; i <= lastActiveSlot; i++) { if ((TargetSlots & (1 << i)) > 0) { slotToActivate |= (1 << i); @@ -138,23 +138,23 @@ Return Value: PAHCI_ADAPTER_EXTENSION adapterExtension = ChannelExtension->AdapterExtension; - //1.1 Initialize variables + // 1.1 Initialize variables limit = ChannelExtension->CurrentCommandSlot; - // if there is internal request pending, always get it first. - if ( (ChannelExtension->SlotManager.SingleIoSlice & 1) > 0 ) { + // if there is internal request pending, always get it first. + if ((ChannelExtension->SlotManager.SingleIoSlice & 1) > 0) { return 0; } - //2.1 Chose the slot circularly starting with CCS + // 2.1 Chose the slot circularly starting with CCS for (i = limit; i <= adapterExtension->CAP.NCS; i++) { - if ( (ChannelExtension->SlotManager.SingleIoSlice & (1 << i)) > 0){ + if ((ChannelExtension->SlotManager.SingleIoSlice & (1 << i)) > 0) { return i; } } for (i = 0; i < limit; i++) { - if ( (ChannelExtension->SlotManager.SingleIoSlice & (1 << i)) > 0){ + if ((ChannelExtension->SlotManager.SingleIoSlice & (1 << i)) > 0) { return i; } } @@ -163,7 +163,7 @@ Return Value: } VOID -AddQueue ( +AddQueue( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _Inout_ PSTORAHCI_QUEUE Queue, _In_ PSTORAGE_REQUEST_BLOCK Srb, @@ -179,10 +179,13 @@ Input Parameters: Tag: bit 31 ~ 24, Queue->CurrentDepth: bit 23 ~ 0 */ { - PVOID tempTail, foundSrb; - ULONG srbsFound; + PVOID tempTail, foundSrb; + ULONG srbsFound; + UNREFERENCED_PARAMETER(ChannelExtension); + StorPortQuerySystemTime(&(Queue->LastTimeStampAddQueue)); + if (Queue->Tail == NULL) { Queue->Tail = (PVOID) Srb; Queue->Head = (PVOID) Srb; @@ -190,11 +193,14 @@ Input Parameters: tempTail = Queue->Tail; if (SrbGetNextSrb(tempTail) == NULL) { //Verify SRBs are not about to be severed from the Queue SrbSetNextSrb(tempTail, (PVOID)Srb); + } Queue->Tail = (PVOID)Srb; + } Queue->CurrentDepth++; if (Queue->Head == NULL) { //It is impossible for Head to be NULL here + srbsFound = 0; } else { foundSrb = Queue->Head; @@ -209,6 +215,7 @@ Input Parameters: Queue->DepthHistoryIndex++; Queue->DepthHistoryIndex %= 100; Queue->DepthHistory[Queue->DepthHistoryIndex] = Signature; + if (Queue->CurrentDepth > Queue->DeepestDepth) { Queue->DeepestDepth = Queue->CurrentDepth; } @@ -217,23 +224,25 @@ Input Parameters: } PSTORAGE_REQUEST_BLOCK -RemoveQueue ( +RemoveQueue( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _Inout_ PSTORAHCI_QUEUE Queue, _In_ ULONG Signature, _In_ UCHAR Tag ) { - PVOID nextSrb, foundSrb; - ULONG srbsFound; + PVOID nextSrb, foundSrb; + ULONG srbsFound; UNREFERENCED_PARAMETER(ChannelExtension); - //Check to see if the queue is empty + // Check to see if the queue is empty if (Queue->Head == NULL) { return NULL; } + StorPortQuerySystemTime(&(Queue->LastTimeStampRemoveQueue)); + //if it is not empty, pop nextSrb = Queue->Head; Queue->Head = SrbGetNextSrb(nextSrb); @@ -258,6 +267,7 @@ RemoveQueue ( Queue->DepthHistoryIndex++; Queue->DepthHistoryIndex %= 100; Queue->DepthHistory[Queue->DepthHistoryIndex] = Signature; + if (Queue->CurrentDepth > Queue->DeepestDepth) { Queue->DeepestDepth = Queue->CurrentDepth; } @@ -299,20 +309,20 @@ Affected Variables/Registers: Return Values: --*/ { - BOOLEAN status = TRUE; - ULONG slotsToActivate = 0; - BOOLEAN activateNcq = FALSE; - int i; + BOOLEAN status = TRUE; + ULONG slotsToActivate = 0; + BOOLEAN activateNcq = FALSE; + int i; PAHCI_ADAPTER_EXTENSION adapterExtension = ChannelExtension->AdapterExtension; - STOR_LOCK_HANDLE lockhandle = {InterruptLock, 0}; + STOR_LOCK_HANDLE lockhandle = { InterruptLock, 0 }; - //1.1 Initialize variables + // 1.1 Initialize variables if (LogExecuteFullDetail(adapterExtension->LogFlags)) { RecordExecutionHistory(ChannelExtension, 0x00000022);//ActivateQueue } - // If the programming should not happen now, leave + // If the programming should not happen now, leave if (ChannelExtension->StartState.ChannelNextStartState != StartComplete) { RecordExecutionHistory(ChannelExtension, 0x10010022);//ActivateQueue, Channel Not Yet Started status = FALSE; @@ -325,13 +335,13 @@ Return Values: return status; } - if ( !IsPortStartCapable(ChannelExtension) ) { + if (!IsPortStartCapable(ChannelExtension)) { RecordExecutionHistory(ChannelExtension, 0x10030022);//ActivateQueue, Channel Not Start Capable status = FALSE; return status; } - if ( ErrorRecoveryIsPending(ChannelExtension) ) { + if (ErrorRecoveryIsPending(ChannelExtension)) { RecordExecutionHistory(ChannelExtension, 0x10070022);//ActivateQueue, Error Recovery is pending. status = FALSE; return status; @@ -341,10 +351,10 @@ Return Values: AhciInterruptSpinlockAcquire(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockhandle); } - //1.1.1 If there is no command to program, leave - if ( (ChannelExtension->SlotManager.SingleIoSlice == 0) && - (ChannelExtension->SlotManager.NormalQueueSlice == 0) && - (ChannelExtension->SlotManager.NCQueueSlice == 0) ) { + // 1.1.1 If there is no command to program, leave + if ((ChannelExtension->SlotManager.SingleIoSlice == 0) && + (ChannelExtension->SlotManager.NormalQueueSlice == 0) && + (ChannelExtension->SlotManager.NCQueueSlice == 0)) { if (LogExecuteFullDetail(adapterExtension->LogFlags)) { RecordExecutionHistory(ChannelExtension, 0x10040022);//ActivateQueue, No Commands to program @@ -363,26 +373,60 @@ Return Values: goto Exit; } - //2.1 Choose the Queue with which to program the controller - //2.1.2 Single IO SRBs have highest priority. - if(ChannelExtension->SlotManager.SingleIoSlice != 0) { - if ( (ChannelExtension->SlotManager.NCQueueSliceIssued | ChannelExtension->SlotManager.NormalQueueSliceIssued | ChannelExtension->SlotManager.SingleIoSliceIssued) == 0 ) { - //Safely get Single IO in round robin fashion + // 2.1 Choose the Queue with which to program the controller + // 2.1.1 Single IO SRBs have highest priority. + if (ChannelExtension->SlotManager.SingleIoSlice != 0) { + if ((ChannelExtension->SlotManager.NCQueueSliceIssued | ChannelExtension->SlotManager.NormalQueueSliceIssued | ChannelExtension->SlotManager.SingleIoSliceIssued) == 0) { + // Safely get Single IO in round robin fashion i = GetSingleIo(ChannelExtension); if (i != 0xff) { + PSLOT_CONTENT slotContent = &ChannelExtension->Slot[i]; + slotsToActivate = (1 << i); ChannelExtension->SlotManager.SingleIoSlice &= ~slotsToActivate; ChannelExtension->SlotManager.SingleIoSliceIssued |= slotsToActivate; ChannelExtension->StateFlags.QueuePaused = TRUE; //and pause the queue so no other IO get programmed + + if (LogExecuteFullDetail(adapterExtension->LogFlags)) { + RecordExecutionHistory(ChannelExtension, 0x10090022); //ActivateQueue, Single IO pause queue + } + + // 2.1.1.1 Update the Identify command for enumeration. This needs to be here for PxSIG to be accessible. + // Accessing PxSIG register here to avoid invalid values from time when the port is being reset or not started, or other scenarios. + if (slotContent->Srb != NULL) { + + PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(slotContent->Srb); + PAHCI_COMMAND_TABLE cmdTable = (PAHCI_COMMAND_TABLE)srbExtension; + + if ((srbExtension->AtaFunction == ATA_FUNCTION_ATA_IDENTIFY) && + (srbExtension->CompletionRoutine == AhciPortIdentifyDevice) && + (srbExtension->TaskFile.Current.bCommandReg == IDE_COMMAND_NOT_VALID) && + (cmdTable->CFIS.Command == IDE_COMMAND_NOT_VALID) ) { + + // For enumeration command, it's safe now to check the PxSIG register as starting port has been executed. + ULONG sig = 0; + sig = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SIG.AsUlong); + + if (sig == ATA_DEVICE_SIGNATURE_ATA) { //ATA + srbExtension->TaskFile.Current.bCommandReg = IDE_COMMAND_IDENTIFY; + cmdTable->CFIS.Command = IDE_COMMAND_IDENTIFY; + } else { //ATAPI + srbExtension->TaskFile.Current.bCommandReg = IDE_COMMAND_ATAPI_IDENTIFY; + cmdTable->CFIS.Command = IDE_COMMAND_ATAPI_IDENTIFY; + } + } + } else { + NT_ASSERT(FALSE); + } } } - //2.1.2 When there are no Single IO commands, Normal IO get the next highest priority + // 2.1.2 When there are no Single IO commands, Normal IO get the next highest priority } else if (ChannelExtension->SlotManager.NormalQueueSlice != 0) { // Normal commands can not be sent when NCQ commands are outstanding. When the NCQ commands complete ActivateQueue will get called again. if (ChannelExtension->SlotManager.NCQueueSliceIssued == 0) { - //Grab the High Priority Normal IO before the Low Priority Normal IO + // Grab the High Priority Normal IO before the Low Priority Normal IO slotsToActivate = ChannelExtension->SlotManager.HighPriorityAttribute & ChannelExtension->SlotManager.NormalQueueSlice; - //If there aren't any High Priority, grab everything else + // If there aren't any High Priority, grab everything else if (slotsToActivate > 0) { ChannelExtension->SlotManager.NormalQueueSlice &= ~slotsToActivate; } else { @@ -391,41 +435,41 @@ Return Values: } ChannelExtension->SlotManager.NormalQueueSliceIssued |= slotsToActivate; } - //2.1.3 When there are no Single or Normal commands, NCQ commands get the next highest priority + // 2.1.3 When there are no Single or Normal commands, NCQ commands get the next highest priority } else if ((ChannelExtension->SlotManager.NCQueueSlice != 0) && (ChannelExtension->StateFlags.NcqErrorRecoveryInProcess == 0)) { // NCQ commands can not be sent when Normal commands are outstanding. When the Normal commands complete, Activate Queue will get called again. - if ( ( ChannelExtension->SlotManager.SingleIoSliceIssued | ChannelExtension->SlotManager.NormalQueueSliceIssued ) != 0 ) { + if ((ChannelExtension->SlotManager.SingleIoSliceIssued | ChannelExtension->SlotManager.NormalQueueSliceIssued ) != 0) { slotsToActivate = 0; } else { - //Grab the High Priority NCQ IO before the Low Priority NCQ IO + // Grab the High Priority NCQ IO before the Low Priority NCQ IO slotsToActivate = ChannelExtension->SlotManager.HighPriorityAttribute & ChannelExtension->SlotManager.NCQueueSlice; - //If there aren't any High Priority, grab everything else + // If there aren't any High Priority, grab everything else if (slotsToActivate == 0) { slotsToActivate = ChannelExtension->SlotManager.NCQueueSlice; } - //and apply any device outstanding IO limits to filter down which IO to activate + // And apply any device outstanding IO limits to filter down which IO to activate if (slotsToActivate > 0) { - if (ChannelExtension->DeviceExtension[0].DeviceParameters.MaxDeviceQueueDepth < ChannelExtension->MaxPortQueueDepth ) { - // get allowed slots if the device queue depth is less than the port can support. + if (ChannelExtension->DeviceExtension[0].DeviceParameters.MaxDeviceQueueDepth < ChannelExtension->MaxPortQueueDepth) { + // Get allowed slots if the device queue depth is less than the port can support. slotsToActivate = GetSlotToActivate(ChannelExtension, slotsToActivate); } if (slotsToActivate > 0) { - //and if there are any IO still selected, clear them from the NCQueue + // And if there are any IO still selected, clear them from the NCQueue activateNcq = TRUE; //Remember to program SACT for these commands ChannelExtension->SlotManager.NCQueueSlice &= ~slotsToActivate; ChannelExtension->SlotManager.NCQueueSliceIssued |= slotsToActivate; - //the selected IO will be activated at the end of this function + // The selected IO will be activated at the end of this function } } } - //2.1.4 In the case that no IO is present in any Slices, program nothing + // 2.1.4 In the case that no IO is present in any Slices, program nothing } - //2.1 Program all the IO from the chosen queue into the controller + // 2.1 Program all the IO from the chosen queue into the controller if (slotsToActivate != 0) { - //2.2 Get command start time - if(adapterExtension->TracingEnabled) { + // 2.2 Get command start time + if (adapterExtension->TracingEnabled) { LARGE_INTEGER perfCounter = {0}; ULONG pendingProgrammingCommands = slotsToActivate; @@ -434,9 +478,11 @@ Return Values: StorPortQueryPerformanceCounter((PVOID)adapterExtension, NULL, &perfCounter); while (pendingProgrammingCommands) { - if (pendingProgrammingCommands & 1) { + // TODO: there is potential race condition that causes slot flags mismatch and Srb is NULL here. + if ((pendingProgrammingCommands & 1) && (ChannelExtension->Slot[i].Srb != NULL)) { PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(ChannelExtension->Slot[i].Srb); srbExtension->StartTime = perfCounter.QuadPart; + } i++; @@ -446,7 +492,7 @@ Return Values: ChannelExtension->SlotManager.CommandsIssued |= slotsToActivate; - // program registers + // Program registers if (activateNcq) { StorPortWriteRegisterUlong(adapterExtension, &ChannelExtension->Px->SACT, slotsToActivate); } @@ -486,21 +532,21 @@ CalculateTimeDurationIn100ns ( { ULONGLONG timeIn100ns = 0; - // as the counter can hold more than 20,000 years for the same power cycle, + // As the counter can hold more than 20,000 years for the same power cycle, // the situation about the counter start over again is considered an error case of inputs. if (CounterFrequency > 0) { - // difference between performance counters, needs to convert to 100ns. + // Difference between performance counters, needs to convert to 100ns. ULONGLONG countersDiff = TimeDuration; - // get seconds + // Get seconds timeIn100ns = countersDiff / CounterFrequency; - // get milliseconds + // Get milliseconds countersDiff = (countersDiff % CounterFrequency) * 1000; timeIn100ns *= 1000; timeIn100ns += countersDiff / CounterFrequency; - // get 100 nanoseconds + // Get 100 nanoseconds countersDiff = (countersDiff % CounterFrequency) * 10000; timeIn100ns *= 10000; timeIn100ns += countersDiff / CounterFrequency; @@ -546,35 +592,34 @@ Affected Variables/Registers: --*/ { - PSLOT_CONTENT slotContent; - UCHAR i; + PSLOT_CONTENT slotContent; + UCHAR i; PAHCI_ADAPTER_EXTENSION adapterExtension; - PAHCI_SRB_EXTENSION srbExtension; - - LARGE_INTEGER perfCounter = {0}; - LARGE_INTEGER perfFrequency = {0}; + PAHCI_SRB_EXTENSION srbExtension; + LARGE_INTEGER perfCounter = { 0 }; + LARGE_INTEGER perfFrequency = { 0 }; - //1.1 Initialize variables + // 1.1 Initialize variables adapterExtension = ChannelExtension->AdapterExtension; if (LogExecuteFullDetail(adapterExtension->LogFlags)) { RecordExecutionHistory(ChannelExtension, 0x00000046);//AhciCompleteIssuedSRBs } - if( adapterExtension->TracingEnabled && (ChannelExtension->SlotManager.CommandsToComplete) ) { + if (adapterExtension->TracingEnabled && (ChannelExtension->SlotManager.CommandsToComplete)) { StorPortQueryPerformanceCounter((PVOID)adapterExtension, &perfFrequency, &perfCounter); } - //2.1 For every command marked as completed + // 2.1 For every command marked as completed for (i = 0; i <= (adapterExtension->CAP.NCS); i++) { - if( ( ChannelExtension->SlotManager.CommandsToComplete & (1 << i) ) > 0) { + if ((ChannelExtension->SlotManager.CommandsToComplete & (1 << i)) > 0) { slotContent = &ChannelExtension->Slot[i]; if (slotContent->Srb == NULL) { - //This shall never happen. - //The completed slot has no SRB so it can not be completed back to Storport. - //Give back the empty slot + // This shall never happen. + // The completed slot has no SRB so it can not be completed back to Storport. + // Give back the empty slot NT_ASSERT(FALSE); ChannelExtension->SlotManager.CommandsToComplete &= ~(1 << i); ChannelExtension->SlotManager.HighPriorityAttribute &= ~(1 << i); @@ -582,12 +627,12 @@ Affected Variables/Registers: } if (slotContent->CmdHeader == NULL) { - //This shall never happen. - //Give back the empty slot + // This shall never happen. + // Give back the empty slot NT_ASSERT(FALSE); ChannelExtension->SlotManager.CommandsToComplete &= ~(1 << i); ChannelExtension->SlotManager.HighPriorityAttribute &= ~(1 << i); - //It is now impossible to determine if a data transfer completed correctly + // It is now impossible to determine if a data transfer completed correctly slotContent->Srb->SrbStatus = SRB_STATUS_ABORTED; AhciCompleteRequest(ChannelExtension, slotContent->Srb, AtDIRQL); continue; @@ -595,46 +640,45 @@ Affected Variables/Registers: srbExtension = GetSrbExtension(slotContent->Srb); - //2.1.2 Log command execution time, if it's allowed - if ( adapterExtension->TracingEnabled && - (srbExtension->StartTime != 0) && - (perfCounter.QuadPart != 0) && - !IsMiniportInternalSrb(ChannelExtension, slotContent->Srb) ) { + // 2.1.2 Log command execution time, if it's allowed + if (adapterExtension->TracingEnabled && + (srbExtension->StartTime != 0) && + (perfCounter.QuadPart != 0) && + !IsMiniportInternalSrb(ChannelExtension, slotContent->Srb)) { ULONGLONG durationTime = CalculateTimeDurationIn100ns((perfCounter.QuadPart - srbExtension->StartTime), perfFrequency.QuadPart); StorPortNotification(IoTargetRequestServiceTime, (PVOID)adapterExtension, durationTime, slotContent->Srb); } - //2.2 Set the status - if( (SrbStatus == SRB_STATUS_SUCCESS) && + // 2.2 Set the status + if ((SrbStatus == SRB_STATUS_SUCCESS) && (!IsRequestSenseSrb(srbExtension->AtaFunction)) && (srbExtension->AtaFunction != ATA_FUNCTION_ATA_SMART) && (!IsNCQCommand(srbExtension)) && - (slotContent->CmdHeader->PRDBC != RequestGetDataTransferLength(slotContent->Srb)) ) { - // + (slotContent->CmdHeader->PRDBC != RequestGetDataTransferLength(slotContent->Srb))) { if (slotContent->CmdHeader->PRDBC < RequestGetDataTransferLength(slotContent->Srb)) { - //buffer underrun, + // Buffer underrun, RequestSetDataTransferLength(slotContent->Srb, slotContent->CmdHeader->PRDBC); slotContent->Srb->SrbStatus = SrbStatus; } else { - //buffer overrun, return error + // Buffer overrun, return error NT_ASSERT(FALSE); slotContent->Srb->SrbStatus = SRB_STATUS_DATA_OVERRUN; } } else { - //If anything has set a STATUS on this SRB, honor that over the one passed in + // If anything has set a STATUS on this SRB, honor that over the one passed in if (slotContent->Srb->SrbStatus == SRB_STATUS_PENDING) { slotContent->Srb->SrbStatus = SrbStatus; } } - //2.3 Monitor to see that any NCQ commands are completing - if ( (slotContent->Srb->SrbStatus == SRB_STATUS_SUCCESS) && - (IsNCQCommand(srbExtension)) ) { + // 2.3 Monitor to see that any NCQ commands are completing + if ((slotContent->Srb->SrbStatus == SRB_STATUS_SUCCESS) && + (IsNCQCommand(srbExtension))) { ChannelExtension->StateFlags.NCQ_Succeeded = TRUE; } - //2.4 Give the slot back + // 2.4 Give the slot back ReleaseSlottedCommand(ChannelExtension, i, AtDIRQL); // Request sense is handled here. if (LogExecuteFullDetail(adapterExtension->LogFlags)) { @@ -643,7 +687,7 @@ Affected Variables/Registers: } } - //3.1 Start the next IO(s) if any + // 3.1 Start the next IO(s) if any ActivateQueue(ChannelExtension, AtDIRQL); return; @@ -677,21 +721,23 @@ Affected Variables/Registers: UNREFERENCED_PARAMETER(ChannelExtension); - //1.1 Map SRB fields to CFIS fields + // 1.1 Map SRB fields to CFIS fields cmdTable->CFIS.FisType = 0x27; cmdTable->CFIS.PMPort = 0; // StorAHCI doesn't support Port Multiplier cmdTable->CFIS.Reserved1 = 0; cmdTable->CFIS.C = 1; cmdTable->CFIS.Command = srbExtension->TaskFile.Current.bCommandReg; - //1.2 Special case mapping of NCQ - if( IsNCQCommand(srbExtension) ){ + + // 1.2 Special case mapping of NCQ + if (IsNCQCommand(srbExtension)) { + cmdTable->CFIS.Features = srbExtension->TaskFile.Current.bSectorCountReg; cmdTable->CFIS.Features_Exp = srbExtension->TaskFile.Previous.bSectorCountReg; cmdTable->CFIS.SectorCount = (srbExtension->QueueTag << 3); cmdTable->CFIS.Dev_Head = 0xF & srbExtension->TaskFile.Current.bDriveHeadReg; cmdTable->CFIS.Dev_Head |= (1 << 6); - if( SlotContent->StateFlags.FUA ){ + if (SlotContent->StateFlags.FUA) { cmdTable->CFIS.Dev_Head |= ATA_NCQ_FUA_BIT; } else { cmdTable->CFIS.Dev_Head &= ~ATA_NCQ_FUA_BIT; @@ -705,7 +751,7 @@ Affected Variables/Registers: cmdTable->CFIS.Dev_Head = srbExtension->TaskFile.Current.bDriveHeadReg; } - //1.1 Map SRB fields to CFIS fields + // 1.1 Map SRB fields to CFIS fields cmdTable->CFIS.SectorNumber = srbExtension->TaskFile.Current.bSectorNumberReg; cmdTable->CFIS.SecNum_Exp = srbExtension->TaskFile.Previous.bSectorNumberReg; @@ -749,11 +795,11 @@ Affected Variables/Registers: Command Table --*/ { - //These are used for copying a CDB to ACMD on ATAPI commands. + // These are used for copying a CDB to ACMD on ATAPI commands. ULONG dataLength; PVOID cdb; - //1.1 Memcopy CDB into ACMD + // 1.1 Memcopy CDB into ACMD PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(SlotContent->Srb); PAHCI_COMMAND_TABLE cmdTable = (PAHCI_COMMAND_TABLE)srbExtension; @@ -768,37 +814,37 @@ Affected Variables/Registers: NT_ASSERT(cdb != NULL); } - //2.1 Put the PACKET Command in the CFIS + // 2.1 Put the PACKET Command in the CFIS cmdTable->CFIS.FisType = 0x27; cmdTable->CFIS.PMPort = 0; // StorAHCI doesn't support Port Multiplier cmdTable->CFIS.Reserved1 = 0; cmdTable->CFIS.C = 1; cmdTable->CFIS.Command = IDE_COMMAND_ATAPI_PACKET; //A0 is the PACKET command - //2.2 Populate DMA bit properly, only use it for data transfer + // 2.2 Populate DMA bit properly, only use it for data transfer if (IsDmaCommand(srbExtension->Flags)) { cmdTable->CFIS.Features = 0x1; - //If word 62 bit 15 is set to one, then the DMADIR bit in the PACKET command is required by the device for - //PACKET commands using the DMA data transfer protocol and: + // If word 62 bit 15 is set to one, then the DMADIR bit in the PACKET command is required by the device for + // PACKET commands using the DMA data transfer protocol and: // a) word 63 bits (2:0); -- MultiWordDMASupport & 0x7 == 0 // b) word 49 bit 15; -- Capabilities.InterleavedDmaSupported // c) word 49 bit 8; and -- Capabilities.DmaSupported // d) word 88 bits (6:0), -- UltraDMASupport & 0x7F == 0 //shall be cleared to zero. - if ( (ChannelExtension->DeviceExtension[0].IdentifyPacketData->DMADIR.DMADIRBitRequired != 0) && - (ChannelExtension->DeviceExtension[0].IdentifyPacketData->Capabilities.DmaSupported != 0) && - (ChannelExtension->DeviceExtension[0].IdentifyPacketData->Capabilities.InterleavedDmaSupported == 0) && - ((ChannelExtension->DeviceExtension[0].IdentifyPacketData->MultiWordDMASupport & 0x7) == 0) && - ((ChannelExtension->DeviceExtension[0].IdentifyPacketData->UltraDMASupport & 0x7F) == 0) ) { - //bit 0 is DMA, bit 2 is DMADIR. (0 = transfer to the device; 4 = transfer to the host). + if ((ChannelExtension->DeviceExtension[0].IdentifyPacketData->DMADIR.DMADIRBitRequired != 0) && + (ChannelExtension->DeviceExtension[0].IdentifyPacketData->Capabilities.DmaSupported != 0) && + (ChannelExtension->DeviceExtension[0].IdentifyPacketData->Capabilities.InterleavedDmaSupported == 0) && + ((ChannelExtension->DeviceExtension[0].IdentifyPacketData->MultiWordDMASupport & 0x7) == 0) && + ((ChannelExtension->DeviceExtension[0].IdentifyPacketData->UltraDMASupport & 0x7F) == 0)) { + // Bit 0 is DMA, bit 2 is DMADIR. (0 = transfer to the device; 4 = transfer to the host). cmdTable->CFIS.Features |= (srbExtension->Flags & ATA_FLAGS_DATA_IN) ? 4 : 0; } } else { cmdTable->CFIS.Features = 0x0; } - //2.1 Put the PACKET Command in the CFIS + // 2.1 Put the PACKET Command in the CFIS cmdTable->CFIS.SectorNumber = 0; cmdTable->CFIS.CylLow = (UCHAR)dataLength; //put the low byte of the in CylLow dataLength >>= 8; @@ -841,8 +887,8 @@ Affected Variables/Registers: Command Table --*/ { - PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(SlotContent->Srb); - PAHCI_COMMAND_TABLE cmdTable = (PAHCI_COMMAND_TABLE)srbExtension; + PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(SlotContent->Srb); + PAHCI_COMMAND_TABLE cmdTable = (PAHCI_COMMAND_TABLE)srbExtension; UNREFERENCED_PARAMETER(ChannelExtension); @@ -865,7 +911,7 @@ Affected Variables/Registers: // // Set common data fields. // - if( IsNCQCommand(srbExtension) ) { + if (IsNCQCommand(srbExtension)) { cmdTable->CFIS.Count7_0 = (srbExtension->QueueTag << 3); } cmdTable->CFIS.FisType = 0x27; @@ -880,7 +926,6 @@ Affected Variables/Registers: cmdTable->CFIS.Control = 0; // Device control consists of the 48bit HighOrderByte, SRST and nIEN. None apply here. } - ULONG SRBtoPRDT( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, @@ -910,39 +955,39 @@ Return Values: If the value returned is -1 the PRDT could not be built. --*/ { - ULONG i; - PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(SlotContent->Srb); - PAHCI_COMMAND_TABLE cmdTable = (PAHCI_COMMAND_TABLE)srbExtension; - PLOCAL_SCATTER_GATHER_LIST sgl = srbExtension->Sgl; + ULONG i; + PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(SlotContent->Srb); + PAHCI_COMMAND_TABLE cmdTable = (PAHCI_COMMAND_TABLE)srbExtension; + PLOCAL_SCATTER_GATHER_LIST sgl = srbExtension->Sgl; if (sgl == NULL) { - //return as invalid request in case of cannot get scatter gather list. + // Return as invalid request in case of cannot get scatter gather list. NT_ASSERT(FALSE); return (ULONG)-1; } for (i = 0; i < sgl->NumberOfElements; i++) { - //1.2 Verify that the DataBuffer is properly aligned - if ( (sgl->List[i].PhysicalAddress.LowPart & 0x1) == 0) { - //2.1 Map SGL entries into PRDT entries + // 1.2 Verify that the DataBuffer is properly aligned + if ((sgl->List[i].PhysicalAddress.LowPart & 0x1) == 0) { + // 2.1 Map SGL entries into PRDT entries if (sgl->List[i].Length != 0x20000) { cmdTable->PRDT[i].DBA.AsUlong = sgl->List[i].PhysicalAddress.LowPart; - if( ChannelExtension->AdapterExtension->CAP.S64A) {//If the controller supports 64 bits, write the high part too + if (ChannelExtension->AdapterExtension->CAP.S64A) {//If the controller supports 64 bits, write the high part too cmdTable->PRDT[i].DBAU = sgl->List[i].PhysicalAddress.HighPart; } - //2.2 Break up a 128K single entry IO into 2 64K IO entries (128K is max transfer so there can be only 1 in any SGL) - // although one entry can represent at max 4M length IO, some adapters cannot handle a DBC >= 128K. + // 2.2 Break up a 128K single entry IO into 2 64K IO entries (128K is max transfer so there can be only 1 in any SGL) + // although one entry can represent at max 4M length IO, some adapters cannot handle a DBC >= 128K. } else { // Entry 0 cmdTable->PRDT[0].DBA.AsUlong = sgl->List[0].PhysicalAddress.LowPart; - if( ChannelExtension->AdapterExtension->CAP.S64A) {//If the controller supports 64 bits, write the high part too + if (ChannelExtension->AdapterExtension->CAP.S64A) {//If the controller supports 64 bits, write the high part too cmdTable->PRDT[0].DBAU = sgl->List[0].PhysicalAddress.HighPart; } cmdTable->PRDT[0].DI.DBC = (0x10000 - 1); // Entry 1 cmdTable->PRDT[1].DBA.AsUlong = (sgl->List[0].PhysicalAddress.LowPart + 0x10000); - if( ChannelExtension->AdapterExtension->CAP.S64A) {//If the controller supports 64 bits, write the high part too - if ( (sgl->List[0].PhysicalAddress.LowPart + 0x10000) < sgl->List[0].PhysicalAddress.LowPart) { + if (ChannelExtension->AdapterExtension->CAP.S64A) {//If the controller supports 64 bits, write the high part too + if ((sgl->List[0].PhysicalAddress.LowPart + 0x10000) < sgl->List[0].PhysicalAddress.LowPart) { cmdTable->PRDT[1].DBAU = (sgl->List[0].PhysicalAddress.HighPart + 1); //add 1 to the highpart if adding 0x10000 caused a rollover } else { cmdTable->PRDT[1].DBAU = sgl->List[0].PhysicalAddress.HighPart; @@ -956,13 +1001,13 @@ Return Values: return (ULONG)-1; } - //1.3 Verify that the DataLength is even - // all SATA transfers must be even - // DBC is a 0 based number (i.e. 0 is 1, 1 is 2, etc. - // sgl->Elements.Length is not (i.e. 0 is 0, 1 is 1, etc. - if( (sgl->List[i].Length & 1) == 0 ) { //therefore length must be even here - //2.3 Set Datalength in the PRDT entries - cmdTable->PRDT[i].DI.DBC = sgl->List[i].Length - 1; //but it must be odd here + // 1.3 Verify that the DataLength is even + // all SATA transfers must be even + // DBC is a 0 based number (i.e. 0 is 1, 1 is 2, etc. + // sgl->Elements.Length is not (i.e. 0 is 0, 1 is 1, etc. + if ((sgl->List[i].Length & 1) == 0) { //therefore length must be even here + // 2.3 Set Datalength in the PRDT entries + cmdTable->PRDT[i].DI.DBC = sgl->List[i].Length - 1; //but it must be odd here } else if (sgl->List[i].Length <= RequestGetDataTransferLength(SlotContent->Srb)) { // Storport may send down SCSI commands with odd number of data transfer length, and it builds SGL using that transfer length value. // we use the length -1 to get as much data as we can. If the data length is over (length - 1), buffer overrun will be reported when the command is completed. @@ -973,6 +1018,7 @@ Return Values: return (ULONG)-1; } } + return sgl->NumberOfElements; } @@ -994,32 +1040,32 @@ Affected Variables/Registers: Command Header --*/ { - PAHCI_COMMAND_HEADER cmdHeader = SlotContent->CmdHeader; - PSTORAGE_REQUEST_BLOCK srb = SlotContent->Srb; - PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(srb); + PAHCI_COMMAND_HEADER cmdHeader = SlotContent->CmdHeader; + PSTORAGE_REQUEST_BLOCK srb = SlotContent->Srb; + PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(srb); UNREFERENCED_PARAMETER(ChannelExtension); -// a. PRDTL containing the number of entries in the PRD table + // a. PRDTL containing the number of entries in the PRD table cmdHeader->DI.PRDTL = Length; -// b. CFL set to the length of the command in the CFIS area + // b. CFL set to the length of the command in the CFIS area cmdHeader->DI.CFL = 5; -// c. A bit set if it is an ATAPI command + // c. A bit set if it is an ATAPI command cmdHeader->DI.A = (srbExtension->AtaFunction & ATA_FUNCTION_ATAPI_COMMAND) ? 1 : 0; -// d. W (Write) bit set if data is going to the device + // d. W (Write) bit set if data is going to the device cmdHeader->DI.W = (srbExtension->Flags & ATA_FLAGS_DATA_OUT) ? 1 : 0; -// e. P (Prefetch) bit optionally set (see rules in section 5.5.2) + // e. P (Prefetch) bit optionally set (see rules in section 5.5.2) //Some controllers have problems if P is set. cmdHeader->DI.P = 0; -// f. If a Port Multiplier is attached, the PMP field set to the correct Port Multiplier port. + // f. If a Port Multiplier is attached, the PMP field set to the correct Port Multiplier port. cmdHeader->DI.PMP = 0; - //Reset + // Reset cmdHeader->DI.R = Reset; cmdHeader->DI.B = 0; cmdHeader->DI.C = Reset; - //initialize the PRD byte count + // Initialize the PRD byte count cmdHeader->PRDBC = 0; cmdHeader->Reserved[0] = 0; @@ -1046,14 +1092,14 @@ Note: This routine can be called even the Port is stopped. */ { PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); - UCHAR pathId = 0; - UCHAR targetId = 0; - UCHAR lun = 0; - STOR_LOCK_HANDLE lockHandle = {InterruptLock, 0}; + UCHAR pathId = 0; + UCHAR targetId = 0; + UCHAR lun = 0; + STOR_LOCK_HANDLE lockHandle = { InterruptLock, 0 }; SrbGetPathTargetLun(Srb, &pathId, &targetId, &lun); - //1.0 complete Srb if no command should be sent to device. + // 1.0 complete Srb if no command should be sent to device. if (srbExtension->AtaFunction == 0) { NT_ASSERT(FALSE); // should investigate if this ASSERT fires Srb->SrbStatus = SRB_STATUS_ERROR; @@ -1062,7 +1108,7 @@ Note: This routine can be called even the Port is stopped. return TRUE; } - //1.1 Check if command processing should happen + // 1.1 Check if command processing should happen if (ChannelExtension->StartState.ChannelNextStartState == StartFailed) { Srb->SrbStatus = SRB_STATUS_NO_DEVICE; MarkSrbToBeCompleted(Srb); @@ -1070,42 +1116,81 @@ Note: This routine can be called even the Port is stopped. return TRUE; } - //2. central place for command special handling. this part cannot be put in command translation layer, as there might be internal command also needs special handling. - //2.1 central place for special handling of Enable/Disable WRITE CACHE, update persistent settings command. - if ( (srbExtension->AtaFunction == ATA_FUNCTION_ATA_COMMAND) && - (srbExtension->TaskFile.Current.bCommandReg == IDE_COMMAND_SET_FEATURE) ) { - //// + // 2. central place for command special handling. this part cannot be put in command translation layer, as there might be internal command also needs special handling. + // 2.1 central place for special handling of Enable/Disable WRITE CACHE, update persistent settings command. + if ((srbExtension->AtaFunction == ATA_FUNCTION_ATA_COMMAND) && + (srbExtension->TaskFile.Current.bCommandReg == IDE_COMMAND_SET_FEATURE)) { + if (srbExtension->TaskFile.Current.bFeaturesReg == IDE_FEATURE_DISABLE_WRITE_CACHE) { - //IDE_FEATURE_DISABLE_WRITE_CACHE => Modify Persistent Settings + // IDE_FEATURE_DISABLE_WRITE_CACHE => Modify Persistent Settings UpdateSetFeatureCommands(ChannelExtension, IDE_FEATURE_ENABLE_WRITE_CACHE, IDE_FEATURE_DISABLE_WRITE_CACHE, 0, 0); } + if (srbExtension->TaskFile.Current.bFeaturesReg == IDE_FEATURE_ENABLE_WRITE_CACHE) { - //IDE_FEATURE_ENABLE_WRITE_CACHE => Modify Persistent Settings + // IDE_FEATURE_ENABLE_WRITE_CACHE => Modify Persistent Settings UpdateSetFeatureCommands(ChannelExtension, IDE_FEATURE_DISABLE_WRITE_CACHE, IDE_FEATURE_ENABLE_WRITE_CACHE, 0, 0); } } - //2.2 update the Identify command for enumeration. This needs to be here waiting for PxSIG to be accessible. - if ( (srbExtension->AtaFunction == ATA_FUNCTION_ATA_IDENTIFY) && - (srbExtension->CompletionRoutine == AhciPortIdentifyDevice) && - (srbExtension->TaskFile.Current.bCommandReg == IDE_COMMAND_NOT_VALID) ) { - // for enumeration command, it's safe now to check the PxSIG register as starting port has been executed. - ULONG sig = 0; + // 2.2 update the Identify command for enumeration. This needs to be here waiting for PxSIG to be accessible. + if ((srbExtension->AtaFunction == ATA_FUNCTION_ATA_IDENTIFY) && + (srbExtension->CompletionRoutine == AhciPortIdentifyDevice) && + (srbExtension->TaskFile.Current.bCommandReg == IDE_COMMAND_NOT_VALID)) { + // For enumeration command, it's safe now to check the PxSIG register as starting port has been executed. + // Note: there is chance that the signature register is FFFFFFFFh when accessing it here because reset is in progress. + // Handle this special case in the follow checking device type code. + ULONG sig = 0; sig = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SIG.AsUlong); - if (sig == 0x101) { //ATA + if (sig == ATA_DEVICE_SIGNATURE_ATA) { srbExtension->TaskFile.Current.bCommandReg = IDE_COMMAND_IDENTIFY; - } else { //ATAPI + ChannelExtension->DeviceExtension[0].DeviceParameters.AtaDeviceType = DeviceIsAta; + } else if (sig == ATA_DEVICE_SIGNATURE_ATAPI) { srbExtension->TaskFile.Current.bCommandReg = IDE_COMMAND_ATAPI_IDENTIFY; + ChannelExtension->DeviceExtension[0].DeviceParameters.AtaDeviceType = DeviceIsAtapi; + } else { + + ULONG ssts = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SSTS.AsUlong); + RecordExecutionHistory(ChannelExtension, 0x10090020); // Unexpected signature register value + + if ((sig == 0) || ((sig == (ULONG)~0) && (ssts != (ULONG)~0))) { + + // Case 1: A value of 0 is not a valid signature. Some devices + // initially report a valid signature but soon after the PxSIG + // register becomes 0. + // Case 2: A value of FFFFFFFFh is set to signature register during reset according to AHCI 1.3.1 spec, + // while the value maybe also FFFFFFFFh when device is removed, because PCI configuration space that mapped with AHCI registers + // are FFFFFFFFh. To differentiate whether it is reset or device removed, check Px.SSTS register value(it is 0 during reset). + // + // If above situations happen, just set the identify command according to the currently known device type. + switch (ChannelExtension->DeviceExtension[0].DeviceParameters.AtaDeviceType) { + case DeviceIsAta: + srbExtension->TaskFile.Current.bCommandReg = IDE_COMMAND_IDENTIFY; + break; + + case DeviceIsAtapi: + default: + srbExtension->TaskFile.Current.bCommandReg = IDE_COMMAND_ATAPI_IDENTIFY; + break; + } + + } else { + + // Other devices types, such as with signature ATA_DEVICE_SIGNATURE_ENCLOSURE, or + // ATA_DEVICE_SIGNATURE_PORT_MULTIPLIER, are not supported. + // IDENTIFY PACKET DEVICE command will fail on these devices. + srbExtension->TaskFile.Current.bCommandReg = IDE_COMMAND_ATAPI_IDENTIFY; + ChannelExtension->DeviceExtension[0].DeviceParameters.AtaDeviceType = DeviceUnknown; + } } } - //2.3 this is central place to update this register value in case it's wrongly set. + // 2.3 this is central place to update this register value in case it's wrongly set. if (IsAtaCommand(srbExtension->AtaFunction)) { SetDeviceReg((&srbExtension->TaskFile.Current), 0); //make sure set to device0 } - //3 Find an available slot/tag (AHCI 1.1 Section 5.5.1) + // 3 Find an available slot/tag (AHCI 1.1 Section 5.5.1) if (!AtDIRQL) { AhciInterruptSpinlockAcquire(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockHandle); } @@ -1113,11 +1198,11 @@ Note: This routine can be called even the Port is stopped. GetAvailableSlot(ChannelExtension, Srb); //srbExtension->QueueTag will be set // 3.1 If no tag is available, reject the command to be retried later - if ( srbExtension->QueueTag > ChannelExtension->AdapterExtension->CAP.NCS ) { - //wait for 8 IO (random picked number) being completed before re-starting sending IO to miniport for this device. + if (srbExtension->QueueTag > ChannelExtension->AdapterExtension->CAP.NCS) { + // Wait for 8 IO (random picked number) being completed before re-starting sending IO to miniport for this device. // if filled up, if (!IsMiniportInternalSrb(ChannelExtension, Srb)) { - // report busy only for IOs from Storport + // Report busy only for IOs from Storport StorPortDeviceBusy(ChannelExtension->AdapterExtension, pathId, targetId, lun, min(8, ChannelExtension->AdapterExtension->CAP.NCS)); } Srb->SrbStatus = SRB_STATUS_BUSY; @@ -1129,19 +1214,19 @@ Note: This routine can be called even the Port is stopped. #ifdef DBG { - ULONG ci; - ULONG sact; + ULONG ci; + ULONG sact; ci = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CI); sact = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SACT); // 3.2 if the tag returned can't be used, something is seriously wrong. Reject the command to be retried later - if( ( ( (1 << srbExtension->QueueTag) & ci) > 0) || ( ( (1 << srbExtension->QueueTag) & sact) > 0) ) { + if ((( (1 << srbExtension->QueueTag) & ci) > 0) || (((1 << srbExtension->QueueTag) & sact) > 0)) { if (!IsMiniportInternalSrb(ChannelExtension, Srb)) { // pause device only for IOs from Storport StorPortPauseDevice(ChannelExtension->AdapterExtension, pathId, targetId, lun, 1); //pause for 1 seconds. } // One reason why this can happen is if the adapter was removed. - if (IsAdapterRemoved(ChannelExtension)) { + if (IsAdapterRemoved(ChannelExtension->AdapterExtension)) { Srb->SrbStatus = SRB_STATUS_NO_DEVICE; } else { NT_ASSERT(FALSE); //can this happen? catch it. @@ -1179,32 +1264,32 @@ AhciFormIo( Caller: only AhciProcessIo() */ { - PAHCI_SRB_EXTENSION srbExtension; - PSLOT_CONTENT slotContent; - PAHCI_COMMAND_TABLE cmdTable; - PAHCI_COMMAND_HEADER cmdHeader; + PAHCI_SRB_EXTENSION srbExtension; + PSLOT_CONTENT slotContent; + PAHCI_COMMAND_TABLE cmdTable; + PAHCI_COMMAND_HEADER cmdHeader; STOR_PHYSICAL_ADDRESS cmdTablePhysicalAddress; - ULONG prdtLength = 0; - PCDB cdb = SrbGetCdb(Srb); + ULONG prdtLength = 0; + PCDB cdb = SrbGetCdb(Srb); srbExtension = GetSrbExtension(Srb); - // command header is allocated in ChannelExtension for all command slots, get the correct one. + // Command header is allocated in ChannelExtension for all command slots, get the correct one. cmdHeader = ChannelExtension->CommandList; cmdHeader += srbExtension->QueueTag; slotContent = &ChannelExtension->Slot[srbExtension->QueueTag]; - // 1. setup slot content and log history + // 1. setup slot content and log history slotContent->Srb = Srb; slotContent->CmdHeader = cmdHeader; - //1.1 Update FUA tracking flag if needed - if( !IsReturnResults(srbExtension->Flags) && + // 1.1 Update FUA tracking flag if needed + if (!IsReturnResults(srbExtension->Flags) && (cdb != NULL) && ((cdb->CDB10.OperationCode == SCSIOP_WRITE) || (cdb->CDB10.OperationCode == SCSIOP_WRITE16)) && (cdb->CDB10.ForceUnitAccess == 1) && - IsFuaSupported(ChannelExtension) ) { + IsFuaSupported(ChannelExtension)) { // Keep track of FUA to add it back in when the command is put in the FIS slotContent->StateFlags.FUA = TRUE; } @@ -1245,18 +1330,17 @@ AhciFormIo( cmdHistory->SrbStatus = 0; } - //already get a slot, after this point, any request completion effort needs to release the slot. + // Already get a slot, after this point, any request completion effort needs to release the slot. // just like what's done in: ReleaseSlottedCommand() - //2. Program the CFIS in the CommandTable (allocated in srbExtension) + // 2. Program the CFIS in the CommandTable (allocated in srbExtension) cmdTable = (PAHCI_COMMAND_TABLE)srbExtension; - AhciZeroMemory((PCHAR)cmdTable, sizeof(AHCI_COMMAND_TABLE)); - if ( IsAtapiCommand(srbExtension->AtaFunction) ) { + if (IsAtapiCommand(srbExtension->AtaFunction)) { SRBtoATAPI_CFIS(ChannelExtension, slotContent); - } else if ( IsAtaCfisPayload(srbExtension->AtaFunction) ) { + } else if (IsAtaCfisPayload(srbExtension->AtaFunction)) { CfistoATA_CFIS(ChannelExtension, slotContent); - } else if ( IsAtaCommand(srbExtension->AtaFunction) ) { + } else if (IsAtaCommand(srbExtension->AtaFunction)) { SRBtoATA_CFIS(ChannelExtension, slotContent); } else { NT_ASSERT(FALSE); @@ -1266,10 +1350,8 @@ AhciFormIo( return TRUE; } - - - //3. Build the PRD Table in CommandTable. - if( IsDataTransferNeeded(slotContent->Srb) ) { + // 3. Build the PRD Table in CommandTable. + if (IsDataTransferNeeded(slotContent->Srb)) { prdtLength = SRBtoPRDT(ChannelExtension, slotContent); if (prdtLength == -1) { NT_ASSERT(FALSE); @@ -1280,22 +1362,21 @@ AhciFormIo( } } - //4. Program the Command Header (allocated in ChannelExtension for all command slots) + // 4. Program the Command Header (allocated in ChannelExtension for all command slots) SRBtoCmdHeader(ChannelExtension, slotContent, prdtLength, FALSE); - //4.1. Get the Command Table's physical address to verify the alignment and program cmdHeader->CTBA + // 4.1. Get the Command Table's physical address to verify the alignment and program cmdHeader->CTBA if ((PSTORAGE_REQUEST_BLOCK)&ChannelExtension->Local.Srb == Srb) { cmdTablePhysicalAddress = ChannelExtension->Local.SrbExtensionPhysicalAddress; } else if ((PSTORAGE_REQUEST_BLOCK)&ChannelExtension->Sense.Srb == Srb) { cmdTablePhysicalAddress = ChannelExtension->Sense.SrbExtensionPhysicalAddress; } else { ULONG length; - cmdTablePhysicalAddress = StorPortGetPhysicalAddress(ChannelExtension->AdapterExtension, - NULL, - srbExtension, - &length); + // SRB is needed for StorPortGetPhysicalAddress to calculate logical address when DMAR is enabled. + cmdTablePhysicalAddress = StorPortGetPhysicalAddress(ChannelExtension->AdapterExtension, (PSCSI_REQUEST_BLOCK)Srb, srbExtension, &length); } - if ((cmdTablePhysicalAddress.LowPart % 128) == 0 ) { + + if ((cmdTablePhysicalAddress.LowPart % 128) == 0) { cmdHeader->CTBA.AsUlong = cmdTablePhysicalAddress.LowPart; } else { slotContent->Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; @@ -1303,28 +1384,29 @@ AhciFormIo( RecordExecutionHistory(ChannelExtension, 0x10080020);//Invalid PhyscialAddress alignment return TRUE; } - //If the controller supports 64 bits, write the high part too - if (ChannelExtension->AdapterExtension->CAP.S64A) { + + // If the controller supports 64 bits, write the high part too + if (ChannelExtension->AdapterExtension->CAP.S64A) { cmdHeader->CTBAU = cmdTablePhysicalAddress.HighPart; } - //5. Sort it, put request in one of IO Slices. - //5.1. Keep track of high priority outside of the Slices - if ( IsHighPriorityCommand(srbExtension->Flags) ){ + // 5. Sort it, put request in one of IO Slices. + // 5.1. Keep track of high priority outside of the Slices + if (IsHighPriorityCommand(srbExtension->Flags)) { ChannelExtension->SlotManager.HighPriorityAttribute |= ( 1 << srbExtension->QueueTag); } - //5.2. Put the slot content in the correct Slice - if ( IsNCQCommand(srbExtension) ) { + + // 5.2. Put the slot content in the correct Slice + if (IsNCQCommand(srbExtension)) { ChannelExtension->SlotManager.NCQueueSlice |= ( 1 << srbExtension->QueueTag); - } else if ( IsReturnResults(srbExtension->Flags) ) { + } else if (IsReturnResults(srbExtension->Flags)) { ChannelExtension->SlotManager.SingleIoSlice |= ( 1 << srbExtension->QueueTag); - } else if ( IsNormalCommand(slotContent->Srb) ) { + } else if (IsNormalCommand(slotContent->Srb)) { ChannelExtension->SlotManager.NormalQueueSlice |= ( 1 << srbExtension->QueueTag); } else { ChannelExtension->SlotManager.SingleIoSlice |= ( 1 << srbExtension->QueueTag); } - if (LogExecuteFullDetail(ChannelExtension->AdapterExtension->LogFlags)) { RecordExecutionHistory(ChannelExtension, 0x10000020);//Exit AhciHwStartIo } @@ -1340,19 +1422,19 @@ BuildRequestSenseSrb( _In_ PSTORAGE_REQUEST_BLOCK FailingSrb ) { - STOR_PHYSICAL_ADDRESS dataBufferPhysicalAddress; - ULONG length; - PCDB cdb; - PSCSI_REQUEST_BLOCK senseSrb = &ChannelExtension->Sense.Srb; - PAHCI_SRB_EXTENSION srbExtension = ChannelExtension->Sense.SrbExtension; - PVOID srbSenseBuffer = NULL; - UCHAR srbSenseBufferLength = 0; + STOR_PHYSICAL_ADDRESS dataBufferPhysicalAddress; + ULONG length; + PCDB cdb; + PSCSI_REQUEST_BLOCK senseSrb = &ChannelExtension->Sense.Srb; + PAHCI_SRB_EXTENSION srbExtension = ChannelExtension->Sense.SrbExtension; + PVOID srbSenseBuffer = NULL; + UCHAR srbSenseBufferLength = 0; RequestGetSrbScsiData(FailingSrb, NULL, NULL, &srbSenseBuffer, &srbSenseBufferLength); - //1. validate if senseSrb should be used. - if ( (srbExtension == NULL) || (srbExtension->AtaFunction != 0) || - (srbSenseBuffer == NULL) || (srbSenseBufferLength == 0) ) { + // 1. Validate if senseSrb should be used. + if ((srbExtension == NULL) || (srbExtension->AtaFunction != 0) || + (srbSenseBuffer == NULL) || (srbSenseBufferLength == 0)) { // Request Sense is for ATAPI commands which are single IOs. At the same time there should be only one command failed and needs this Srb. NT_ASSERT(srbExtension && srbExtension->AtaFunction == 0); return NULL; @@ -1362,11 +1444,11 @@ BuildRequestSenseSrb( AhciZeroMemory((PCHAR)srbSenseBuffer, srbSenseBufferLength); } - //2. initialize Srb and SrbExtension structures. + // 2. Initialize Srb and SrbExtension structures. AhciZeroMemory((PCHAR)senseSrb, sizeof(SCSI_REQUEST_BLOCK)); AhciZeroMemory((PCHAR)srbExtension, sizeof(AHCI_SRB_EXTENSION)); - //3. setup Srb and CDB. Note that Sense Srb uses SCSI_REQUEST_BLOCK type. + // 3. Setup Srb and CDB. Note that Sense Srb uses SCSI_REQUEST_BLOCK type. senseSrb->Length = sizeof(SCSI_REQUEST_BLOCK); senseSrb->Function = SRB_FUNCTION_EXECUTE_SCSI; senseSrb->PathId = (UCHAR)ChannelExtension->PortNumber; @@ -1383,11 +1465,12 @@ BuildRequestSenseSrb( cdb->CDB6INQUIRY.OperationCode = SCSIOP_REQUEST_SENSE; cdb->CDB6INQUIRY.AllocationLength = (UCHAR)senseSrb->DataTransferLength; - //4. fill in the srbExtension fields + // 4. Fill in the srbExtension fields srbExtension->AtaFunction = ATA_FUNCTION_REQUEST_SENSE; srbExtension->Flags = ATA_FLAGS_DATA_IN; - dataBufferPhysicalAddress = StorPortGetPhysicalAddress(ChannelExtension->AdapterExtension, NULL, srbSenseBuffer, &length); + // SRB is needed for StorPortGetPhysicalAddress to calculate logical address when DMAR is enabled. + dataBufferPhysicalAddress = StorPortGetPhysicalAddress(ChannelExtension->AdapterExtension, (PSCSI_REQUEST_BLOCK)FailingSrb, srbSenseBuffer, &length); if (dataBufferPhysicalAddress.QuadPart == 0) { NT_ASSERT(FALSE); //Shall Not Pass return NULL; @@ -1411,7 +1494,7 @@ AhciPortFailAllIos( { UCHAR i; - // complete all requests in slots + // Complete all requests in slots for (i = 0; i <= ChannelExtension->AdapterExtension->CAP.NCS; i++) { if (ChannelExtension->Slot[i].Srb != NULL) { ChannelExtension->Slot[i].Srb->SrbStatus = SrbStatus; @@ -1419,7 +1502,7 @@ AhciPortFailAllIos( } } - // requests in completion queue will be completed by AhciPortSrbCompletionDpcRoutine. + // Requests in completion queue will be completed by AhciPortSrbCompletionDpcRoutine. return; } @@ -1433,12 +1516,13 @@ AhciPortSrbCompletionDpcRoutine( ) { PAHCI_CHANNEL_EXTENSION channelExtension = (PAHCI_CHANNEL_EXTENSION)SystemArgument1; - STOR_LOCK_HANDLE lockhandle = {InterruptLock, 0}; - PSTORAGE_REQUEST_BLOCK srb = NULL; + STOR_LOCK_HANDLE lockhandle = { InterruptLock, 0 }; + PSTORAGE_REQUEST_BLOCK srb = NULL; PSRB_COMPLETION_ROUTINE completionRoutine = NULL; - BOOLEAN reservedSlotInUse = FALSE; - BOOLEAN sendCommand = FALSE; - ULONGLONG count = 0; // record the number of Srb being completed in this routine. It's for diagnostic purpose. + BOOLEAN reservedSlotInUse = FALSE; + BOOLEAN sendCommand = FALSE; + BOOLEAN sendCommandForLocalSrb = FALSE; + ULONGLONG count = 0; // record the number of Srb being completed in this routine. It's for diagnostic purpose. UNREFERENCED_PARAMETER(Dpc); UNREFERENCED_PARAMETER(SystemArgument2); @@ -1452,6 +1536,8 @@ AhciPortSrbCompletionDpcRoutine( RecordExecutionHistory(channelExtension, 0x0000001e);//Enter AhciPortSrbCompletionDpcRoutine } + StorPortQuerySystemTime(&(channelExtension->LastTimeStampDpcStart)); + // // Preserve the use status about Reserved Slot. // @@ -1463,7 +1549,7 @@ AhciPortSrbCompletionDpcRoutine( AhciInterruptSpinlockRelease(channelExtension->AdapterExtension, channelExtension->PortNumber, &lockhandle); if (srb != NULL) { - PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(srb); + PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(srb); BOOLEAN completeSrb = TRUE; @@ -1476,35 +1562,85 @@ AhciPortSrbCompletionDpcRoutine( completionRoutine(channelExtension, srb); - // a request with STATUS_BUS_RESET should be completed after running completion routine. - if ( (srbExtension->AtaFunction != 0) && - (!SrbShouldBeCompleted(srbExtension->Flags)) && - (srb->SrbStatus != SRB_STATUS_BUS_RESET) ) { + // A request with STATUS_BUS_RESET should be completed after running completion routine. + if ((srbExtension->AtaFunction != 0) && + (!SrbShouldBeCompleted(srbExtension->Flags)) && + (srb->SrbStatus != SRB_STATUS_BUS_RESET)) { // new command associated needs to be processed, do not complete the request. AhciProcessIo(channelExtension, srb, FALSE); - // this Srb should not be completed yet + // This Srb should not be completed yet completeSrb = FALSE; sendCommand = TRUE; + if (srb == (PSTORAGE_REQUEST_BLOCK)&channelExtension->Local.Srb) { + sendCommandForLocalSrb = TRUE; + } + } else if (SrbShouldBeCompleted(srbExtension->Flags)) { - // clear the flag + // Clear the flag CLRMASK(srbExtension->Flags, ATA_FLAGS_COMPLETE_SRB); } - } else { - // a Srb without completion routine should be completed. + // An Srb without completion routine should be completed. + } + + if ((srb == (PSTORAGE_REQUEST_BLOCK)&channelExtension->Local.Srb) && + reservedSlotInUse && + channelExtension->StateFlags.ReservedSlotInUse && + (!sendCommandForLocalSrb)) { + + // + // This should never happen. ReservedSlotInUse flag will stuck. + // + + NT_ASSERT(FALSE); + + RecordExecutionHistory(channelExtension, 0x1001001e); //AhciPortSrbCompletionDpcRoutine, ReservedSlotInUse starts to stuck + + AhciTelemetryLog(channelExtension->AdapterExtension, + (PSTOR_ADDRESS)&(channelExtension->DeviceExtension->DeviceAddress), + AHCI_TELEMETRY_DRIVER_VERSION, + AhciTelemetryEventIdReservedSlotStuck, + "Reserved Slot Stuck", + AHCI_TELEMETRY_EVENT_VERSION, + 0, + 0, + NULL, + "CommandsIssued", + channelExtension->SlotManager.CommandsIssued, + "NCQueueSlice", + channelExtension->SlotManager.NCQueueSlice, + "NormalQueueSlice", + channelExtension->SlotManager.NormalQueueSlice, + "SingleIoSlice", + channelExtension->SlotManager.SingleIoSlice, + "CommandsToComplete", + channelExtension->SlotManager.CommandsToComplete, + "StateFlags", + *(ULONGLONG *)&(channelExtension->StateFlags), + "StartState", + *(ULONGLONG *)&(channelExtension->StartState), + "Flags|Function|Status", + (ULONGLONG)((((ULONGLONG)srbExtension->Flags) << 32) | + (((ULONGLONG)srbExtension->AtaFunction) << 16) | + (ULONGLONG)(srb->SrbStatus)) + ); } if (completeSrb) { - // release active reference for port/device and adapter. + // Release active reference for port/device and adapter. if ((srbExtension->Flags & ATA_FLAGS_ACTIVE_REFERENCE) != 0) { PortReleaseActiveReference(channelExtension, srb); } if (!IsMiniportInternalSrb(channelExtension, srb)) { + if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) { + + } + NT_ASSERT(srb->SrbStatus != SRB_STATUS_PENDING); StorPortNotification(RequestComplete, AdapterExtension, srb); } @@ -1523,9 +1659,11 @@ AhciPortSrbCompletionDpcRoutine( } if (sendCommand) { - ActivateQueue(channelExtension, TRUE); + ActivateQueue(channelExtension, (IsDumpMode(channelExtension->AdapterExtension) ? (TRUE) : (FALSE))); } + StorPortQuerySystemTime(&(channelExtension->LastTimeStampDpcCompletion)); + if (LogExecuteFullDetail(channelExtension->AdapterExtension->LogFlags)) { RecordExecutionHistory(channelExtension, 0x1000001e);//Exit AhciPortSrbCompletionDpcRoutine } @@ -1539,4 +1677,3 @@ AhciPortSrbCompletionDpcRoutine( #pragma warning(default:4214) #pragma warning(default:4201) #endif - diff --git a/storage/miniports/storahci/src/io.h b/storage/miniports/storahci/src/io.h index 00b3a44fb..8e9df9b5f 100644 --- a/storage/miniports/storahci/src/io.h +++ b/storage/miniports/storahci/src/io.h @@ -122,4 +122,3 @@ HW_DPC_ROUTINE AhciPortSrbCompletionDpcRoutine; HW_DPC_ROUTINE AhciPortBusChangeDpcRoutine; - diff --git a/storage/miniports/storahci/src/pnppower.c b/storage/miniports/storahci/src/pnppower.c index ca9b6ff65..a6b5e3d68 100644 --- a/storage/miniports/storahci/src/pnppower.c +++ b/storage/miniports/storahci/src/pnppower.c @@ -1,4 +1,4 @@ -/*++ +ο»Ώ/*++ Copyright (C) Microsoft Corporation, 2009 @@ -19,7 +19,7 @@ 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. + //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 @@ -79,13 +79,13 @@ It performs: 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’." + β€’ 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. + "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. @@ -193,7 +193,6 @@ NOTE: as this routine is invoked from FindAdapter where the adapter might not be 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. @@ -261,11 +260,11 @@ Affected Variables/Registers: GHC.AE, GHC.IE Return Values: TRUE always. ---*/ //Used to enable the AHCI interface +--*/ { - ULONG i; + ULONG i; AHCI_Global_HBA_CONTROL ghc; - PAHCI_MEMORY_REGISTERS abar = (PAHCI_MEMORY_REGISTERS)AdapterExtension->ABAR_Address; + PAHCI_MEMORY_REGISTERS abar = (PAHCI_MEMORY_REGISTERS)AdapterExtension->ABAR_Address; AdapterExtension->StateFlags.PowerDown = 0; @@ -286,9 +285,9 @@ Return Values: // Power up all ports that don't have a device present. // 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) && - (AdapterExtension->PortExtension[i]->DeviceExtension[0].DeviceParameters.AtaDeviceType == DeviceNotExist)) { + if ((AdapterExtension->PortExtension[i] != NULL) && + (AdapterExtension->PortExtension[i]->StateFlags.PowerDown == TRUE) && + (AdapterExtension->PortExtension[i]->DeviceExtension[0].DeviceParameters.AtaDeviceType == DeviceNotExist)) { AhciPortPowerUp(AdapterExtension->PortExtension[i]); } } @@ -310,22 +309,22 @@ Called by: 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." + "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; + ULONG i; AHCI_Global_HBA_CONTROL ghc; - PAHCI_MEMORY_REGISTERS abar = (PAHCI_MEMORY_REGISTERS)AdapterExtension->ABAR_Address; + PAHCI_MEMORY_REGISTERS abar = (PAHCI_MEMORY_REGISTERS)AdapterExtension->ABAR_Address; // Power down all ports that don't have a device present. for (i = 0; i <= AdapterExtension->HighestPort; i++) { - if ( (AdapterExtension->PortExtension[i] != NULL) && - (AdapterExtension->PortExtension[i]->StateFlags.PowerDown == FALSE) && - (AdapterExtension->PortExtension[i]->DeviceExtension[0].DeviceParameters.AtaDeviceType == DeviceNotExist)) { + if ((AdapterExtension->PortExtension[i] != NULL) && + (AdapterExtension->PortExtension[i]->StateFlags.PowerDown == FALSE) && + (AdapterExtension->PortExtension[i]->DeviceExtension[0].DeviceParameters.AtaDeviceType == DeviceNotExist)) { AhciPortPowerDown(AdapterExtension->PortExtension[i]); } } @@ -382,13 +381,14 @@ Return Values: if (LogExecuteFullDetail(ChannelExtension->AdapterExtension->LogFlags)) { RecordExecutionHistory(ChannelExtension, 0x00000025);//AhciPortStop } - //1. Stop the channel + + //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 + //2. Disable Interrupt and disconnect with Port resources StorPortWriteRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->IE.AsUlong, 0); //disabling interrupts PortClearPendingInterrupt(ChannelExtension); @@ -432,19 +432,25 @@ Return Values: --*/ AHCI_LPM_POWER_SETTINGS userLpmSettings; - BOOLEAN portPowerDown; + BOOLEAN portPowerDown; + AHCI_INTERRUPT_STATUS pxis = { 0 }; RecordExecutionHistory(ChannelExtension, 0x00000026);//Enter AhciPortPowerUp - //1.0 Reinitialize the StateFlags. e.g. ChannelExtension->StateFlags.PowerDown = FALSE; + ++(ChannelExtension->TotalCountPowerUp); + + pxis.AsUlong = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->IS.AsUlong); + + // 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 + // 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); @@ -454,26 +460,46 @@ Return Values: 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)) { - + + STOR_LOCK_HANDLE lockhandle = { InterruptLock, 0 }; + BOOLEAN powerUpInitializationInProgress = 0; + // 1.4.1 Re-issue init commands (this will also restore preserved settings). AhciPortIssueInitCommands(ChannelExtension); - + + // Set PowerUpInitializationInProgress flag, which will be cleared when preserved settings command done. + powerUpInitializationInProgress = InterlockedBitTestAndSet((LONG*)&ChannelExtension->StateFlags, 22); //PowerUpInitializationInProgress field is at bit 22 + + if (powerUpInitializationInProgress == 1) { + + // PowerUpInitializationInProgress flag was not cleared properly, which is not expected to happen, but not a fatal issue. + RecordExecutionHistory(ChannelExtension, 0x10020026); // AhciPortPowerUp: PowerUpInitializationInProgress flag was not cleared properly. + } + // 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. + AhciInterruptSpinlockAcquire(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockhandle); AhciPortReset(ChannelExtension, FALSE); - + AhciInterruptSpinlockRelease(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockhandle); + } else { + // + // If there is change in Current Connect Status(PCS:1), then QDR is needed. + // + if ((pxis.PCS == 1) && + (ChannelExtension->StartState.ChannelNextStartState == StartFailed)) { + ChannelExtension->StateFlags.NeedQDR = TRUE; + } + // 1.4.1 Restore Preserved Settings if (NeedToSetTransferMode(ChannelExtension)) { RestorePreservedSettings(ChannelExtension, FALSE); @@ -484,6 +510,7 @@ Return Values: } RecordExecutionHistory(ChannelExtension, 0x10000026);//Exit AhciPortPowerUp + return; } @@ -514,6 +541,8 @@ Return Values: --*/ ChannelExtension->StateFlags.PowerDown = TRUE; + ++(ChannelExtension->TotalCountPowerDown); + // // Cancel the StartPortTimer since we're going into a lower power state. // @@ -528,6 +557,7 @@ Return Values: // the link will be inactive, ignore the hot plug noise. ChannelExtension->StateFlags.IgnoreHotplugInterrupt = TRUE; } + } RecordExecutionHistory(ChannelExtension, 0x10000027);//Exit AhciPortPowerDown @@ -564,7 +594,6 @@ ReportLunsComplete( return; } - Srb->SrbStatus = SRB_STATUS_SUCCESS; SrbSetScsiStatus(Srb, SCSISTAT_GOOD); @@ -609,24 +638,181 @@ ReportLunsComplete( return; } +__inline +VOID +GetLogInfoRegValueName( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _Out_writes_(ValueNameLength) PCHAR ValueName, + _In_ ULONG ValueNameLength + ) +/*++ + +Routine Description: + + This function append Port Number to "LogPageInfo" as name of registry value. + This is a work around as StorAHCI doesn't have access to RtlStringCbPrintfA(). + +Arguments: + + ChannelExtension - Pointer to the device extension for channel. + + ValueName - Registry name to be written. + + ValueNameLength - Registry name length. + +Return Value: + + None. + +--*/ +{ + NT_ASSERT(ChannelExtension->PortNumber <= 255); + + if (ValueNameLength >= 14) { + + ULONG portNumber = ChannelExtension->PortNumber; + ULONG remainder = 0; + + StorPortCopyMemory(ValueName, "LogPageInfo", 12); + ValueName[13] = '\0'; + + remainder = portNumber % 16; // use HEX value, base is 16. + ValueName[12] = (CHAR)((remainder < 10) ? (remainder + '0') : (remainder - 10 + 'A')); + + portNumber /= 16; + remainder = portNumber % 16; + ValueName[11] = (CHAR)((remainder < 10) ? (remainder + '0') : (remainder - 10 + 'A')); + } + + return; +} + +_Function_class_(HW_WORKITEM) +VOID +AhciRegistryWriteWorker( + _In_ PAHCI_ADAPTER_EXTENSION AdapterExtension, + _In_ PVOID Context, + _In_ PVOID WorkItem + ) +/*++ + +Routine Description: + + This function runs in the context of a work item. It performs writting registry with data + from Log Pages that needs to be cached. + +Arguments: + + AdapterExtension - Pointer to the device extension for adapter. + + Context - The AHCI_CHANNEL_EXTENSION we specified when we queued this work item. + + WorkItem - The work item object. + +Return Value: + + None. + +--*/ +{ + PAHCI_CHANNEL_EXTENSION channelExtension = (PAHCI_CHANNEL_EXTENSION)Context; + ULONG storStatus; + CHAR valueName[16] = { 0 }; + AHCI_DEVICE_LOG_PAGE_INFO logPageInfo = { 0 }; + + NT_ASSERT(Context != NULL); + NT_ASSERT(WorkItem != NULL); + + GetLogInfoRegValueName(channelExtension, valueName, sizeof(valueName)); + + StorPortCopyMemory((PVOID)&logPageInfo.QueryLogPages, &channelExtension->DeviceExtension[0].QueryLogPages, sizeof(ATA_GPL_PAGES_TO_QUERY)); + StorPortCopyMemory((PVOID)&logPageInfo.SupportedGPLPages, &channelExtension->DeviceExtension[0].SupportedGPLPages, sizeof(ATA_SUPPORTED_GPL_PAGES)); + StorPortCopyMemory((PVOID)&logPageInfo.SupportedCommands, &channelExtension->DeviceExtension[0].SupportedCommands, sizeof(ATA_COMMAND_SUPPORTED)); + StorPortCopyMemory((PVOID)&logPageInfo.FirmwareUpdate, &channelExtension->DeviceExtension[0].FirmwareUpdate, sizeof(DOWNLOAD_MICROCODE_CAPABILITIES)); + + storStatus = StorPortRegistryWriteAdapterKey(AdapterExtension, + (PUCHAR)"StorAHCI", + (PUCHAR)valueName, + MINIPORT_REG_BINARY, + &logPageInfo, + sizeof(AHCI_DEVICE_LOG_PAGE_INFO)); + + if (storStatus == STOR_STATUS_SUCCESS) { + channelExtension->DeviceExtension[0].UpdateCachedLogPageInfo = FALSE; + } else { + NT_ASSERT(FALSE); + } + + // + // Call the callback routine if there is one. + // + + if (WorkItem != NULL) { + StorPortFreeWorker(AdapterExtension, WorkItem); + } + + return; +} + +VOID +PreserveLogPageInformation( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension + ) +/*++ + + This routine allocates a work item and schedule it for saving information retrieved from Log Pages. + +Parameters: + + ChannelExtension - port and device that log pages are retrieved from. + +Return Values: + + None + +--*/ +{ + ULONG storStatus; + PVOID workItem = NULL; + + storStatus = StorPortInitializeWorker(ChannelExtension->AdapterExtension, &workItem); + + if (storStatus == STOR_STATUS_SUCCESS) { + storStatus = StorPortQueueWorkItem(ChannelExtension->AdapterExtension, AhciRegistryWriteWorker, workItem, ChannelExtension); + } + + NT_ASSERT(storStatus == STOR_STATUS_SUCCESS); + + if ((storStatus != STOR_STATUS_SUCCESS) && (workItem != NULL)) { + // Free the work item as it cannot be scheduled to run. + StorPortFreeWorker(ChannelExtension->AdapterExtension, workItem); + } + + return; +} + VOID -InitQueryLogPages ( +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. + + 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. @@ -637,7 +823,7 @@ Return Values: AhciZeroMemory((PCHAR)&ChannelExtension->DeviceExtension->QueryLogPages, sizeof(ATA_GPL_PAGES_TO_QUERY)); - // read Log Directory + // 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; @@ -645,7 +831,7 @@ Return Values: ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].FeatureField = 0; *index = *index + 1; - // read Device Statistics log - supported page + // 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; @@ -653,7 +839,7 @@ Return Values: ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].FeatureField = 0; *index = *index + 1; - // read Device Statistics log - general page + // 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; @@ -661,7 +847,7 @@ Return Values: ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].FeatureField = 0; *index = *index + 1; - // read Identify Device Data log - supported page + // 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; @@ -669,7 +855,7 @@ Return Values: ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].FeatureField = 0; *index = *index + 1; - // read Identify Device Data log - Supported Capabilities page + // Read Identify Device Data log - Supported Capabilities 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_SUPPORTED_CAPABILITIES_PAGE; @@ -677,7 +863,7 @@ Return Values: ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].FeatureField = 0; *index = *index + 1; - // read Identify Device Data log - SATA page + // 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; @@ -685,7 +871,7 @@ Return Values: ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].FeatureField = 0; *index = *index + 1; - // read Saved Device Internal log + // 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; @@ -693,7 +879,7 @@ Return Values: ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].FeatureField = 0; *index = *index + 1; - // read NCQ non-Data log + // 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; @@ -706,7 +892,7 @@ Return Values: ChannelExtension->DeviceExtension->QueryLogPages.LogPage[*index].FeatureField = 0; *index = *index + 1; - // read NCQ Send Receive log + // 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; @@ -788,18 +974,20 @@ Return Values: Srb->SrbStatus = SRB_STATUS_PENDING; srbExtension->DataBuffer = DataBuffer; + if ( PhysicalAddress ) { + srbExtension->DataBufferPhysicalAddress.QuadPart = PhysicalAddress->QuadPart; + } //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->LocalSgl.List[0].Length = ATA_BLOCK_SIZE * (ULONG)BlockCount; srbExtension->Sgl = &srbExtension->LocalSgl; - srbExtension->DataTransferLength = ATA_BLOCK_SIZE * BlockCount; + srbExtension->DataTransferLength = ATA_BLOCK_SIZE * (ULONG)BlockCount; } - return; } @@ -845,15 +1033,15 @@ ReadQueryLogPage ( ) { 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 - ); + 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 + ); } @@ -893,6 +1081,168 @@ Return Values: return; } +VOID +UpdateDownloadMicrocodeSupport( + _Inout_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_ PIDENTIFY_DEVICE_DATA_LOG_PAGE_SUPPORTED_CAPABILITIES Capabilities +) +/*++ + +Routine Description: + + Takes the given Supported Capabilities log page and updates the firmware + update (download microcode) support information that is cached in the given + Channel Extension. + +Arguments: + + Channel Extension + Supported Capabilities + +--*/ +{ + NT_ASSERT(Capabilities->Header.RevisionNumber == IDE_GP_LOG_VERSION); + NT_ASSERT(Capabilities->Header.PageNumber == IDE_GP_LOG_IDENTIFY_DEVICE_DATA_SUPPORTED_CAPABILITIES_PAGE); + + // + // Set it to unsupported by default. + // + ChannelExtension->DeviceExtension->FirmwareUpdate.DmOffsetsDeferredSupported = 0; + + // + // If the Download Microcode capabilites are valid then fill in our cached copy. + // + if (Capabilities->DownloadMicrocodeCapabilities.Valid == 1) { + + if ((ChannelExtension->DeviceExtension->IdentifyDeviceData->AdditionalSupported.DownloadMicrocodeDmaSupported == 1) || + (ChannelExtension->DeviceExtension->IdentifyDeviceData->CommandSetSupport.DownloadMicrocode == 1)) { + + ChannelExtension->DeviceExtension->FirmwareUpdate.DmOffsetsDeferredSupported = (Capabilities->DownloadMicrocodeCapabilities.DmOffsetsDeferredSupported == 1); + + if (ChannelExtension->DeviceExtension->FirmwareUpdate.DmOffsetsDeferredSupported) { + + if ((Capabilities->DownloadMicrocodeCapabilities.DmMinTransferSize > 0) && + (Capabilities->DownloadMicrocodeCapabilities.DmMinTransferSize < 0xFFFF)) { + ChannelExtension->DeviceExtension->FirmwareUpdate.DmMinTransferBlocks = (USHORT)min(Capabilities->DownloadMicrocodeCapabilities.DmMinTransferSize, AHCI_MAX_TRANSFER_LENGTH_DEFAULT / ATA_BLOCK_SIZE); + } else { + ChannelExtension->DeviceExtension->FirmwareUpdate.DmMinTransferBlocks = 1; + } + + if ((Capabilities->DownloadMicrocodeCapabilities.DmMaxTransferSize > 0) && + (Capabilities->DownloadMicrocodeCapabilities.DmMaxTransferSize < 0xFFFF)) { + ChannelExtension->DeviceExtension->FirmwareUpdate.DmMaxTransferBlocks = (USHORT)min(Capabilities->DownloadMicrocodeCapabilities.DmMaxTransferSize, AHCI_MAX_TRANSFER_LENGTH_DEFAULT / ATA_BLOCK_SIZE); + } else { + ChannelExtension->DeviceExtension->FirmwareUpdate.DmMaxTransferBlocks = AHCI_MAX_TRANSFER_LENGTH_DEFAULT / ATA_BLOCK_SIZE; + } + } + } + } +} + +VOID +GetDownloadMicrocodeSupportCompletion( + _Inout_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_ PSTORAGE_REQUEST_BLOCK Srb +) +/*++ + +Routine Description: + + Called when the Supported Capabilities log page has been obtained from the + device. The log page is parsed for the Download Microcode support and the + cached copy of this data is updated in the ChannelExtension. + +Arguments: + + Channel Extension + SRB + +--*/ +{ + PAHCI_SRB_EXTENSION srbExtension; + + srbExtension = GetSrbExtension(Srb); + + // + // If the request succeeded then update the cached copy of the Download + // Microcode support information. + // + if (Srb->SrbStatus == SRB_STATUS_SUCCESS) { + UpdateDownloadMicrocodeSupport(ChannelExtension, srbExtension->DataBuffer); + } + + AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, IDE_GP_LOG_SECTOR_SIZE, srbExtension->DataBuffer, srbExtension->DataBufferPhysicalAddress); + + // + // The SRB will be completed after this completion routine returns so + // there's no need to do it here. + // +} + +ULONG +GetDownloadMicrocodeSupport( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_ PSTORAGE_REQUEST_BLOCK Srb +) +/*++ +Routine Description: + + Queries the Download Microcode support from the Identify Log Page and + updates our cached copy of it. + +Arguments: + ChannelExtension + SRB + +Return Value: + + Status code. + +--*/ +{ + ULONG status = STOR_STATUS_SUCCESS; + PVOID buffer = NULL; + STOR_PHYSICAL_ADDRESS bufferPhysicalAddress = { 0 }; + + // + // Fail the request if it's not supported by device. + // + if (IsDeviceGeneralPurposeLoggingSupported(ChannelExtension) == FALSE) { + Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; + return STOR_STATUS_INVALID_PARAMETER; + } + + // + // Allocate DMA buffer to the log page. + // + status = AhciAllocateDmaBuffer((PVOID)ChannelExtension->AdapterExtension, IDE_GP_LOG_SECTOR_SIZE, (PVOID*)&buffer, &bufferPhysicalAddress); + + if ((status != STOR_STATUS_SUCCESS) || (buffer == NULL)) { + if (buffer != NULL) { + AhciFreeDmaBuffer((PVOID)ChannelExtension->AdapterExtension, IDE_GP_LOG_SECTOR_SIZE, buffer, bufferPhysicalAddress); + } + Srb->SrbStatus = SRB_STATUS_ERROR; + return STOR_STATUS_INSUFFICIENT_RESOURCES; + } + + AhciZeroMemory((PCHAR)buffer, IDE_GP_LOG_SECTOR_SIZE); + + // + // Issue the command. The completion routine will update the cached + // firmware update information with the info from the log page. + // The completion routine will also free the DMA buffer. + // + IssueReadLogExtCommand(ChannelExtension, + Srb, + IDE_GP_LOG_IDENTIFY_DEVICE_DATA_ADDRESS, + IDE_GP_LOG_IDENTIFY_DEVICE_DATA_SUPPORTED_CAPABILITIES_PAGE, + 1, + 0, // feature field + &bufferPhysicalAddress, + buffer, + GetDownloadMicrocodeSupportCompletion); + return status; +} VOID LogPageDiscoveryCompletion ( @@ -903,7 +1253,6 @@ LogPageDiscoveryCompletion ( 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; @@ -957,7 +1306,6 @@ LogPageDiscoveryCompletion ( 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. @@ -1070,31 +1418,10 @@ LogPageDiscoveryCompletion ( // The value of revision number word shall be 0001h. if ((supportedCapabilities->Header.RevisionNumber == IDE_GP_LOG_VERSION) && - (supportedCapabilities->Header.PageNumber == IDE_GP_LOG_IDENTIFY_DEVICE_DATA_SUPPORTED_CAPABILITIES_PAGE) && - (supportedCapabilities->DownloadMicrocodeCapabilities.Valid == 1)) { - - if ((ChannelExtension->DeviceExtension->IdentifyDeviceData->AdditionalSupported.DownloadMicrocodeDmaSupported == 1) || - (ChannelExtension->DeviceExtension->IdentifyDeviceData->CommandSetSupport.DownloadMicrocode == 1)) { - - ChannelExtension->DeviceExtension->FirmwareUpdate.DmOffsetsDeferredSupported = (supportedCapabilities->DownloadMicrocodeCapabilities.DmOffsetsDeferredSupported == 1); - - if (ChannelExtension->DeviceExtension->FirmwareUpdate.DmOffsetsDeferredSupported) { + (supportedCapabilities->Header.PageNumber == IDE_GP_LOG_IDENTIFY_DEVICE_DATA_SUPPORTED_CAPABILITIES_PAGE)) { - if ((supportedCapabilities->DownloadMicrocodeCapabilities.DmMinTransferSize > 0) && - (supportedCapabilities->DownloadMicrocodeCapabilities.DmMinTransferSize < 0xFFFF)) { - ChannelExtension->DeviceExtension->FirmwareUpdate.DmMinTransferBlocks = (USHORT)min(supportedCapabilities->DownloadMicrocodeCapabilities.DmMinTransferSize, AHCI_MAX_TRANSFER_LENGTH / ATA_BLOCK_SIZE); - } else { - ChannelExtension->DeviceExtension->FirmwareUpdate.DmMinTransferBlocks = 1; - } - - if ((supportedCapabilities->DownloadMicrocodeCapabilities.DmMaxTransferSize > 0) && - (supportedCapabilities->DownloadMicrocodeCapabilities.DmMaxTransferSize < 0xFFFF)) { - ChannelExtension->DeviceExtension->FirmwareUpdate.DmMaxTransferBlocks = (USHORT)min(supportedCapabilities->DownloadMicrocodeCapabilities.DmMaxTransferSize, AHCI_MAX_TRANSFER_LENGTH / ATA_BLOCK_SIZE); - } else { - ChannelExtension->DeviceExtension->FirmwareUpdate.DmMaxTransferBlocks = AHCI_MAX_TRANSFER_LENGTH / ATA_BLOCK_SIZE; - } - } - } + UpdateDownloadMicrocodeSupport(ChannelExtension, supportedCapabilities); + } } else { @@ -1105,6 +1432,7 @@ LogPageDiscoveryCompletion ( // 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; } @@ -1153,13 +1481,60 @@ LogPageDiscoveryCompletion ( if (nextPageIndex != ATA_GPL_PAGES_INVALID_INDEX) { ReadQueryLogPage(ChannelExtension, Srb, nextPageIndex); } else { + ReportLunsComplete(ChannelExtension, Srb); + + // Use a work itme to preserve information from Log Pages into registry. + PreserveLogPageInformation(ChannelExtension); } return; } +VOID +UpdateDeviceType( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension + ) +/*++ + +Routine Description: + + Update device type information after device enumeration. + + Assumption - PxSIG is ready to be accessed. + +Arguments: + + ChannelExtension + +Return Value: + + None. + +--*/ +{ + PATA_DEVICE_PARAMETERS deviceParameters = &ChannelExtension->DeviceExtension->DeviceParameters; + + // There is chance that during device enumeration, port is not started yet and signature register is 0xffffffff, + // which will result in the AtaDeviceType is set to DeviceUnknown. + // Update it here if above situation is true. + if (IsUnknownDevice(deviceParameters)) { + + ULONG sig = 0; + sig = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SIG.AsUlong); + + if (sig == ATA_DEVICE_SIGNATURE_ATA) { + ChannelExtension->DeviceExtension[0].DeviceParameters.AtaDeviceType = DeviceIsAta; + } else if (sig == ATA_DEVICE_SIGNATURE_ATAPI) { + ChannelExtension->DeviceExtension[0].DeviceParameters.AtaDeviceType = DeviceIsAtapi; + } else { + NT_ASSERT(FALSE); + } + } +} + + VOID AhciPortIdentifyDevice( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, @@ -1175,18 +1550,16 @@ AhciPortIdentifyDevice( 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; - } - } else if (Srb->SrbStatus == SRB_STATUS_NO_DEVICE) { + if (Srb->SrbStatus == SRB_STATUS_NO_DEVICE) { // command failed consider as no device ChannelExtension->DeviceExtension->DeviceParameters.AtaDeviceType = DeviceNotExist; } if (Srb->SrbStatus == SRB_STATUS_SUCCESS) { + + // Update device ata type if it isn't initialized correctly. + UpdateDeviceType(ChannelExtension); + //Re-initialize device specific information to avoid the values being reused after device switched. ChannelExtension->StateFlags.NCQ_Activated = 0; ChannelExtension->StateFlags.NCQ_Succeeded = 0; @@ -1201,7 +1574,6 @@ AhciPortIdentifyDevice( // identify completes, digest identify data / inquiry data UpdateDeviceParameters(ChannelExtension); - // // Cache if PUIS is enabled or not. We'll look at this later when // powering up the port to determine if we need to send the spin up @@ -1212,34 +1584,85 @@ AhciPortIdentifyDevice( // Initialize port properties ChannelExtension->PortProperties = 0; + if (ChannelExtension->StateFlags.IdentifyDeviceSuccess == 0) { + ChannelExtension->StateFlags.IdentifyDeviceSuccess = 1; + } + if (IsExternalPort(ChannelExtension)) { SETMASK(ChannelExtension->PortProperties, PORT_PROPERTIES_EXTERNAL_PORT); } } // Identify Device can only be triggered from REPORT LUNS command or - // INQUIRY command (for disk in dump environment) + // INQUIRY command (for disk in dump environment) if ((cdb != NULL) && (cdb->CDB10.OperationCode == SCSIOP_REPORT_LUNS)) { - if ( (Srb->SrbStatus == SRB_STATUS_SUCCESS) && - IsDeviceGeneralPurposeLoggingSupported(ChannelExtension) ) { - - // READ LOG EXT command is supported. - USHORT index; + BOOLEAN cachedLogPageInfoUsable = FALSE; - InitQueryLogPages(ChannelExtension); + if ((Srb->SrbStatus == SRB_STATUS_SUCCESS) && + IsDeviceGeneralPurposeLoggingSupported(ChannelExtension)) { - index = GetNextQueryLogPageIndex(ChannelExtension); + ULONG storStatus; - NT_ASSERT(index == 0); + // + // Check to determine whether use cached device settings. + // + storStatus = StorPortIsDeviceOperationAllowed(ChannelExtension->AdapterExtension, + NULL, + &STORPORT_DEVICEOPERATION_CACHED_SETTINGS_INIT_GUID, + (PULONG)(&cachedLogPageInfoUsable)); - 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); + cachedLogPageInfoUsable = (cachedLogPageInfoUsable && (ChannelExtension->DeviceExtension[0].UpdateCachedLogPageInfo == FALSE)); + + if (cachedLogPageInfoUsable) { + // In case this is not a newly plugged in device and it's allowed to use cached Log Page Information. + CHAR valueName[16] = { 0 }; + AHCI_DEVICE_LOG_PAGE_INFO logPageInfo = { 0 }; + PVOID dataBuffer = &logPageInfo; + ULONG dataLength = sizeof(AHCI_DEVICE_LOG_PAGE_INFO); + + GetLogInfoRegValueName(ChannelExtension, valueName, sizeof(valueName)); + + storStatus = StorPortRegistryReadAdapterKey(ChannelExtension->AdapterExtension, + (PUCHAR)"StorAHCI", + (PUCHAR)valueName, + MINIPORT_REG_BINARY, + &dataBuffer, + &dataLength); + + if ((storStatus == STOR_STATUS_SUCCESS) && (dataLength == sizeof(AHCI_DEVICE_LOG_PAGE_INFO))) { + + StorPortCopyMemory((PVOID)&ChannelExtension->DeviceExtension[0].QueryLogPages, &logPageInfo.QueryLogPages, sizeof(ATA_GPL_PAGES_TO_QUERY)); + StorPortCopyMemory((PVOID)&ChannelExtension->DeviceExtension[0].SupportedGPLPages, &logPageInfo.SupportedGPLPages, sizeof(ATA_SUPPORTED_GPL_PAGES)); + StorPortCopyMemory((PVOID)&ChannelExtension->DeviceExtension[0].SupportedCommands, &logPageInfo.SupportedCommands, sizeof(ATA_COMMAND_SUPPORTED)); + StorPortCopyMemory((PVOID)&ChannelExtension->DeviceExtension[0].FirmwareUpdate, &logPageInfo.FirmwareUpdate, sizeof(DOWNLOAD_MICROCODE_CAPABILITIES)); + + ReportLunsComplete(ChannelExtension, Srb); + } else { + cachedLogPageInfoUsable = FALSE; + } } + // + // Start log page discovery process if decide not use cached log page data. + // + if (!cachedLogPageInfoUsable) { + + 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); } @@ -1267,7 +1690,28 @@ AhciPortIdentifyDevice( } else { InquiryComplete(ChannelExtension, Srb); } + } else if ((ChannelExtension->DeviceExtension->DeviceParameters.StateFlags.NeedUpdateIdentifyDeviceData == 1) && + (cdb != NULL) && (cdb->CDB10.OperationCode == SCSIOP_INQUIRY)) { + + // + // We are refreshing Identify information because of a firmware update. + // The firmware update support information is contained in the Supported + // Capabilities log page so we need to make sure we query that page as well. + // + ChannelExtension->DeviceExtension->DeviceParameters.StateFlags.NeedUpdateIdentifyDeviceData = 0; + + // + // Finish processing the Inquiry command before re-using the SRB to get + // the Supported Capabilities log page. + // + InquiryComplete(ChannelExtension, Srb); + + if (IsDeviceGeneralPurposeLoggingSupported(ChannelExtension)) { + GetDownloadMicrocodeSupport(ChannelExtension, Srb); + } + } + return; } @@ -1317,7 +1761,7 @@ AhciPortNVCacheCompletion( // // Free the buffer allocated as mode sense info buffer , holding task file // - AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, srbExtension->ResultBufferLength, TaskFile); + AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, srbExtension->ResultBufferLength, TaskFile, srbExtension->ResultBufferPhysicalAddress); } else { // in case TaskFile is not returned in SenseInfoBuffer, use cached ATA Status and Error register values. @@ -1408,7 +1852,7 @@ Affected Variables/Registers: 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 + // Fills in the local SRB with the SetFeatures command srbExtension = ChannelExtension->Local.SrbExtension; AhciZeroMemory((PCHAR)srbExtension, sizeof(AHCI_SRB_EXTENSION)); @@ -1448,21 +1892,25 @@ Affected Variables/Registers: none --*/ { - UCHAR i; - ULONG allocated; - ATA_TASK_FILE taskFile = {0}; + UCHAR i; + ULONG allocated; + ATA_TASK_FILE taskFile = {0}; UNREFERENCED_PARAMETER(Srb); - //1 Verify local SRB is not in use + RecordExecutionHistory(ChannelExtension, 0x00000043); // IssuePreservedSettingCommands + + //1 Verify local SRB is not in use allocated = GetOccupiedSlots(ChannelExtension); if ((allocated & (1 << 0)) > 0) { + // Already restoring preserved Settings + RecordExecutionHistory(ChannelExtension, 0x10010043); // IssuePreservedSettingCommands Slot 0 in use return; } - //2 find the next command to send + //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); @@ -1470,20 +1918,23 @@ Affected Variables/Registers: } } - //perhaps there is none. Done. + // Perhaps there is none. Done. if ( i >= MAX_SETTINGS_PRESERVED) { - // release active reference for process of restore preserved settings + // 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 + RecordExecutionHistory(ChannelExtension, 0x10020043); // IssuePreservedSettingCommands done and clear flag + + InterlockedBitTestAndReset((LONG*)&ChannelExtension->StateFlags, 3); //ReservedSlotInUse field is at bit 3 + InterlockedBitTestAndReset((LONG*)&ChannelExtension->StateFlags, 22); //PowerUpInitializationInProgress field is at bit 22 return; } - //3 Otherwise use the LocalSRB to send the command. When it is done, call this routine again + //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; @@ -1521,22 +1972,29 @@ Affected Variables/Registers: UNREFERENCED_PARAMETER(Srb); - // Verify local SRB is not in use + RecordExecutionHistory(ChannelExtension, 0x00000044); // IssueInitCommands + + // Verify local SRB is not in use allocated = GetOccupiedSlots(ChannelExtension); if ((allocated & (1 << 0)) > 0) { + // Already restoring preserved Settings + RecordExecutionHistory(ChannelExtension, 0x10010044); // IssueInitCommands slot 0 in use return; } - // if all Init commands have been sent, send Preserved Setting Commands + // if all Init commands have been sent, send Preserved Setting Commands if (ChannelExtension->DeviceInitCommands.CommandToSend >= ChannelExtension->DeviceInitCommands.ValidCommandCount) { + + RecordExecutionHistory(ChannelExtension, 0x10020044); // IssueInitCommands init commands done, send Preserved Setting Commands + ChannelExtension->PersistentSettings.SlotsToSend = ChannelExtension->PersistentSettings.Slots; IssuePreservedSettingCommands(ChannelExtension, NULL); return; } - // find the next command to send + // find the next command to send taskFile = ChannelExtension->DeviceInitCommands.CommandTaskFile + ChannelExtension->DeviceInitCommands.CommandToSend; taskFile->Current.bDriveHeadReg = 0xA0; @@ -1824,7 +2282,6 @@ SetAllowedLpmStates( 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; @@ -1846,14 +2303,16 @@ Return Value: */ { AHCI_COMMAND cmd; - BOOLEAN needStartIo = FALSE; + BOOLEAN needStartIo = FALSE; + UCHAR sctlIpm = 0; + + RecordExecutionHistory(ChannelExtension, 0x00000047); // AhciLpmSettingsModes //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) { @@ -1869,7 +2328,7 @@ Return Value: } //Set PxSCTL.IPM to 3h to restrict slumber and partial interface power management state transitions. - SetAllowedLpmStates(ChannelExtension); + sctlIpm = SetAllowedLpmStates(ChannelExtension); // Disable DIPM if (IsDeviceSupportsDIPM(ChannelExtension->DeviceExtension[0].IdentifyDeviceData)) { @@ -1890,7 +2349,7 @@ Return Value: // link power management is allowed. //Set PxSCTL.IPM for LPM allowed states. - UCHAR sctlIpm = SetAllowedLpmStates(ChannelExtension); + sctlIpm = SetAllowedLpmStates(ChannelExtension); // Setting HIPM if it's enabled. if ( (LpmMode.HipmEnabled > 0) && @@ -1954,6 +2413,18 @@ Return Value: } + ++(ChannelExtension->TotalCountPowerSettingNotification); + AhciTelemetryLogPowerSettingChange(ChannelExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + AhciTelemetryEventIdLpmSettingsModes, + "AhciLpmSettingsModes", + 0, + sctlIpm, + LpmMode.AsUlong + ); + + RecordExecutionHistory(ChannelExtension, 0x10000047); // AhciLpmSettingsModes Exit + return needStartIo; } @@ -1963,7 +2434,7 @@ 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) ) { @@ -1991,10 +2462,22 @@ AhciPortPowerSettingNotification( ULONG interval = (ULONG)*((PULONG)PowerInfo->Value); if (interval <= 300000) { + UCHAR sctlIpm = 0; + ChannelExtension->AutoPartialToSlumberInterval = interval; //Set PxSCTL.IPM register for LPM allowed states. - SetAllowedLpmStates(ChannelExtension); + sctlIpm = SetAllowedLpmStates(ChannelExtension); + + ++(ChannelExtension->TotalCountPowerSettingNotification); + AhciTelemetryLogPowerSettingChange(ChannelExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + AhciTelemetryEventIdLpmAdaptiveSetting, + "LpmAdaptiveSetting", + 0, + sctlIpm, + ChannelExtension->AutoPartialToSlumberInterval + ); } } else if (IsLpmModeSetting(PowerInfo)) { @@ -2184,7 +2667,6 @@ AhciPortGetInitCommands( // 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; @@ -2258,7 +2740,6 @@ AhciPortGetInitCommands( // 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. @@ -2529,6 +3010,7 @@ AhciAdapterEvaluateDSMMethod( AdapterExtension->StateFlags.SupportsAcpiDSM = TRUE; } + } Exit: @@ -2639,4 +3121,3 @@ AhciPortAcpiDSMControl( #pragma warning(pop) // un-sets any local warning changes - diff --git a/storage/miniports/storahci/src/pnppower.h b/storage/miniports/storahci/src/pnppower.h index 366c6bb5b..ad5e1253c 100644 --- a/storage/miniports/storahci/src/pnppower.h +++ b/storage/miniports/storahci/src/pnppower.h @@ -41,6 +41,8 @@ Revision History: #define ACPI_METHOD_DSM_LINKPOWER_REMOVE_POWER 0x00 #define ACPI_METHOD_DSM_LINKPOWER_APPLY_POWER 0x01 +#define AHCI_DEVICE_IO_COALESCING_IDLE_TIMEOUT_MS (1000) // 1s by default + // // When waiting for the link to change power states, don't wait more // than 100 microseconds. @@ -130,7 +132,6 @@ AhciPortAcpiDSMControl( _In_ BOOLEAN Sleep ); - VOID IssuePreservedSettingCommands( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, @@ -178,4 +179,3 @@ AhciLpmSettingsModes( #pragma warning(default:4214) #pragma warning(default:4201) #endif - diff --git a/storage/miniports/storahci/src/util.c b/storage/miniports/storahci/src/util.c index e7ed0cbaa..b95c682c9 100644 --- a/storage/miniports/storahci/src/util.c +++ b/storage/miniports/storahci/src/util.c @@ -1,4 +1,4 @@ -/*++ +ο»Ώ/*++ Copyright (C) Microsoft Corporation, 2009 @@ -55,14 +55,23 @@ Affected Variables/Registers: abar = NULL; - // loop over resource entries + // loop over resource entries if (ConfigInfo->NumberOfAccessRanges > 0) { for (i = 0; i < ConfigInfo->NumberOfAccessRanges; i++) { -#if defined (_ARM_) || defined (_ARM64_) - if ((*(ConfigInfo->AccessRanges))[i].RangeStart.QuadPart != 0) { -#else - if ((*(ConfigInfo->AccessRanges))[i].RangeStart.QuadPart == AdapterExtension->AhciBaseAddress) { -#endif + BOOLEAN bAccessRangeFound = FALSE; + + // Test to see if this is the needed resource entry, depending on the interface type + if (ConfigInfo->AdapterInterfaceType == PCIBus) { + if ((*(ConfigInfo->AccessRanges))[i].RangeStart.QuadPart == AdapterExtension->AhciBaseAddress) { + bAccessRangeFound = TRUE; + } + } else { + if ((*(ConfigInfo->AccessRanges))[i].RangeStart.QuadPart != 0) { + bAccessRangeFound = TRUE; + } + } + + if (bAccessRangeFound) { abar = (PAHCI_MEMORY_REGISTERS)StorPortGetDeviceBase(AdapterExtension, ConfigInfo->AdapterInterfaceType, ConfigInfo->SystemIoBusNumber, @@ -104,9 +113,11 @@ Return Value: none --*/ { - int i; + if (ChannelExtension->ExecutionHistory == NULL) { + return; + } - //1.1 Select the next available index + //1.1 Select the next available index // Actually, ExecutionHistoryNextAvailableIndex is used for current index. if (ChannelExtension->ExecutionHistoryNextAvailableIndex >= (MAX_EXECUTION_HISTORY_ENTRY_COUNT - 1)) { ChannelExtension->ExecutionHistoryNextAvailableIndex = 0; @@ -114,10 +125,12 @@ Return Value: ChannelExtension->ExecutionHistoryNextAvailableIndex++; } - //2.1 Copy over the Channel Extension header + StorPortQuerySystemTime(&(ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].TimeStamp)); + + //2.1 Copy over the Channel Extension header ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].Function = Function; - if(ChannelExtension->AdapterExtension->IS) { + if (ChannelExtension->AdapterExtension->IS) { // Keep using the old field "IS" to save StateFlags information. StorPortCopyMemory(&ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].IS, &ChannelExtension->StateFlags, sizeof(ULONG)); } else { @@ -126,8 +139,8 @@ Return Value: StorPortCopyMemory(&ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].SlotManager, &ChannelExtension->SlotManager, sizeof(SLOT_MANAGER)); - //2.2 Copy over the AHCI registers - if(ChannelExtension->Px) { + //2.2 Copy over the AHCI registers + if (ChannelExtension->Px) { ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].Px[0] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CLB.AsUlong); ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].Px[1] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CLBU); ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].Px[2] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->FB.AsUlong); @@ -149,7 +162,7 @@ Return Value: ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].Px[15] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SNTF.AsUlong); } else { - for(i=0; i<0x10; i++) { + for(int i = 0; i < 0x10; i++) { ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].Px[i] = (ULONG)~0; } } @@ -178,7 +191,11 @@ Return Value: none --*/ { - //1. Select the next available index + if (ChannelExtension->ExecutionHistory == NULL) { + return; + } + + //1. Select the next available index // Actually, ExecutionHistoryNextAvailableIndex is used for current index. if (ChannelExtension->ExecutionHistoryNextAvailableIndex >= (MAX_EXECUTION_HISTORY_ENTRY_COUNT - 1)) { ChannelExtension->ExecutionHistoryNextAvailableIndex = 0; @@ -186,7 +203,9 @@ Return Value: ChannelExtension->ExecutionHistoryNextAvailableIndex++; } - //2. Copy data + StorPortQuerySystemTime(&(ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].TimeStamp)); + + //2. Copy data ChannelExtension->ExecutionHistory[ChannelExtension->ExecutionHistoryNextAvailableIndex].Function = Function; if (ChannelExtension->AdapterExtension->IS) { @@ -223,22 +242,22 @@ Called by: PAHCI_ADAPTER_EXTENSION adapterExtension = ChannelExtension->AdapterExtension; ie.AsUlong = StorPortReadRegisterUlong(adapterExtension, &IE->AsUlong); - ie.DHRE = 1; //Device to Host Register FIS Interrupt (DHRS): A D2H Register FIS has been received with the ‘I’ bit set, and has been copied into system memory. - ie.PSE = 1; //PIO Setup FIS Interrupt (PSS): A PIO Setup FIS has been received with the ‘I’ bit set, it has been copied into system memory, and the data related to that FIS has been transferred. This bit shall be set even if the data transfer resulted in an error. - ie.DSE = 1; //DMA Setup FIS Interrupt (DSS): A DMA Setup FIS has been received with the ‘I’ bit set and has been copied into system memory. - ie.SDBE = 1; //Set Device Bits Interrupt (SDBS): A Set Device Bits FIS has been received with the ‘I’ bit set and has been copied into system memory. + ie.DHRE = 1; //Device to Host Register FIS Interrupt (DHRS): A D2H Register FIS has been received with the οΏ½IοΏ½ bit set, and has been copied into system memory. + ie.PSE = 1; //PIO Setup FIS Interrupt (PSS): A PIO Setup FIS has been received with the οΏ½IοΏ½ bit set, it has been copied into system memory, and the data related to that FIS has been transferred. This bit shall be set even if the data transfer resulted in an error. + ie.DSE = 1; //DMA Setup FIS Interrupt (DSS): A DMA Setup FIS has been received with the οΏ½IοΏ½ bit set and has been copied into system memory. + ie.SDBE = 1; //Set Device Bits Interrupt (SDBS): A Set Device Bits FIS has been received with the οΏ½IοΏ½ bit set and has been copied into system memory. - ie.UFE = 0; //Unknown FIS Interrupt (UFS): When set to ‘1’, indicates that an unknown FIS was received and has been copied into system memory. This bit is cleared to ‘0’ by software clearing the PxSERR.DIAG.F bit to ‘0’. Note that this bit does not directly reflect the PxSERR.DIAG.F bit. PxSERR.DIAG.F is set immediately when an unknown FIS is detected, whereas this bit is set when that FIS is posted to memory. Software should wait to act on an unknown FIS until this bit is set to ‘1’ or the two bits may become out of sync. - ie.DPE = 0; //Descriptor Processed (DPS): A PRD with the ‘I’ bit set has transferred all of its data. Refer to section 5.4.2. + ie.UFE = 0; //Unknown FIS Interrupt (UFS): When set to οΏ½1οΏ½, indicates that an unknown FIS was received and has been copied into system memory. This bit is cleared to οΏ½0οΏ½ by software clearing the PxSERR.DIAG.F bit to οΏ½0οΏ½. Note that this bit does not directly reflect the PxSERR.DIAG.F bit. PxSERR.DIAG.F is set immediately when an unknown FIS is detected, whereas this bit is set when that FIS is posted to memory. Software should wait to act on an unknown FIS until this bit is set to οΏ½1οΏ½ or the two bits may become out of sync. + ie.DPE = 0; //Descriptor Processed (DPS): A PRD with the οΏ½IοΏ½ bit set has transferred all of its data. Refer to section 5.4.2. ie.PCE = 1; //Port Connect Change Status (PCS): 1=Change in Current Connect Status. 0=No change in Current Connect Status. This bit reflects the state of PxSERR.DIAG.X. This bit is only cleared when PxSERR.DIAG.X is cleared. if(adapterExtension->CAP.SMPS) { - ie.DMPE = 1; //Device Mechanical Presence Status (DMPS): When set, indicates that a mechanical presence switch attached to this port has been opened or closed, which may lead to a change in the connection state of the device. This bit is only valid if both CAP.SMPS and P0CMD.MPSP are set to ‘1’. + ie.DMPE = 1; //Device Mechanical Presence Status (DMPS): When set, indicates that a mechanical presence switch attached to this port has been opened or closed, which may lead to a change in the connection state of the device. This bit is only valid if both CAP.SMPS and P0CMD.MPSP are set to οΏ½1οΏ½. } else { ie.DMPE = 0; } //Reserved :14; - ie.PRCE = 1; //PhyRdy Change Status (PRCS): When set to ‘1’ indicates the internal PhyRdy signal changed state. This bit reflects the state of P0SERR.DIAG.N. To clear this bit, software must clear P0SERR.DIAG.N to ‘0’. + ie.PRCE = 1; //PhyRdy Change Status (PRCS): When set to οΏ½1οΏ½ indicates the internal PhyRdy signal changed state. This bit reflects the state of P0SERR.DIAG.N. To clear this bit, software must clear P0SERR.DIAG.N to οΏ½0οΏ½. ie.IPME = 0; //Incorrect Port Multiplier Status (IPMS): Indicates that the HBA received a FIS from a device whose Port Multiplier field did not match what was expected. The IPMS bit may be set during enumeration of devices on a Port Multiplier due to the normal Port Multiplier enumeration process. It is recommended that IPMS only be used after enumeration is complete on the Port Multiplier. ie.OFE = 1; //Overflow Status (OFS): Indicates that the HBA received more bytes from a device than was specified in the PRD table for the command. @@ -250,8 +269,8 @@ Called by: ie.HBFE = 1; //Host Bus Fatal Error Status (HBFS): Indicates that the HBA encountered a host bus error that it cannot recover from, such as a bad software pointer. In PCI, such an indication would be a target or master abort. ie.TFEE = 1; //Task File Error Status (TFES): This bit is set whenever the status register is updated by the device and the error bit (bit 0) is set. cmd.AsUlong = StorPortReadRegisterUlong(adapterExtension, &ChannelExtension->Px->CMD.AsUlong); - if(cmd.CPD) { //check for PxCMD.CPD set to ‘1’ before setting CPDE - ie.CPDE = 1; //Cold Port Detect Status (CPDS): When set, a device status has changed as detected by the cold presence detect logic. This bit can either be set due to a non-connected port receiving a device, or a connected port having its device removed. This bit is only valid if the port supports cold presence detect as indicated by PxCMD.CPD set to ‘1’. + if(cmd.CPD) { //check for PxCMD.CPD set to οΏ½1οΏ½ before setting CPDE + ie.CPDE = 1; //Cold Port Detect Status (CPDS): When set, a device status has changed as detected by the cold presence detect logic. This bit can either be set due to a non-connected port receiving a device, or a connected port having its device removed. This bit is only valid if the port supports cold presence detect as indicated by PxCMD.CPD set to οΏ½1οΏ½. } else { ie.CPDE = 0; } @@ -451,7 +470,6 @@ Return Value: return matches; } - VOID AhciBusChangeCallback( _In_ PVOID AdapterExtension, @@ -494,38 +512,41 @@ PortBusChangeProcess ( ) { AHCI_SERIAL_ATA_CONTROL sctl; - STOR_LOCK_HANDLE lockhandle = {InterruptLock, 0}; - ULONG status = STOR_STATUS_UNSUCCESSFUL; + STOR_LOCK_HANDLE lockhandle = { InterruptLock, 0 }; + ULONG status = STOR_STATUS_UNSUCCESSFUL; - //1 if link speed was limited, restore the supported value. + // 1 if link speed was limited, restore the supported value. sctl.AsUlong = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SCTL.AsUlong); - if ( (sctl.SPD != 0) && - (sctl.SPD != ChannelExtension->AdapterExtension->CAP.ISS) ) { + if ((sctl.SPD != 0) && + (sctl.SPD != ChannelExtension->AdapterExtension->CAP.ISS)) { sctl.SPD = 0; //AHCI 3.3.11 --- 0h No speed negotiation restrictions StorPortWriteRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SCTL.AsUlong, sctl.AsUlong); } - //2 Kicks off the Start Channel state machine + // 2 Kicks off the Start Channel state machine AhciInterruptSpinlockAcquire(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockhandle); AhciPortReset(ChannelExtension, TRUE); // all requests should be completed AhciInterruptSpinlockRelease(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockhandle); - //3 Requests rescan on this port. Using STATE_CHANGE_TARGET to make sure a REPORT LUNS command coming from Storport. + // Mark that cached Log Page information need to be updated. + ChannelExtension->DeviceExtension[0].UpdateCachedLogPageInfo = TRUE; + + // 3 Requests rescan on this port. Using STATE_CHANGE_TARGET to make sure a REPORT LUNS command coming from Storport. status = StorPortStateChangeDetected(ChannelExtension->AdapterExtension, - STATE_CHANGE_TARGET, - (PSTOR_ADDRESS)&ChannelExtension->DeviceExtension[0].DeviceAddress, - 0, - AhciBusChangeCallback, - NULL); + STATE_CHANGE_TARGET, + (PSTOR_ADDRESS)&ChannelExtension->DeviceExtension[0].DeviceAddress, + 0, + AhciBusChangeCallback, + NULL); - // if the state change notification fails, set a timer to retry the notification after a delay + // If the state change notification fails, set a timer to retry the notification after a delay if (status == STOR_STATUS_UNSUCCESSFUL) { if (ChannelExtension->BusChangeTimer == NULL) { - StorPortInitializeTimer(ChannelExtension->AdapterExtension, &ChannelExtension->BusChangeTimer); } + status = StorPortRequestTimer(ChannelExtension->AdapterExtension, ChannelExtension->BusChangeTimer, AhciBusChangeTimerCallback, @@ -533,22 +554,19 @@ PortBusChangeProcess ( 100000, // 100 milliseconds 0); - if ( status != STOR_STATUS_SUCCESS ) { + if (status != STOR_STATUS_SUCCESS) { PortReleaseActiveReference(ChannelExtension, NULL); } - } else { if (ChannelExtension->BusChangeTimer != NULL) { StorPortFreeTimer(ChannelExtension->AdapterExtension, ChannelExtension->BusChangeTimer); ChannelExtension->BusChangeTimer = NULL; - } - + } + PortReleaseActiveReference(ChannelExtension, NULL); - } - } VOID @@ -676,6 +694,7 @@ Affected Variables/Registers: PSTORAGE_REQUEST_BLOCK srbToRelease; BOOLEAN isSenseSrb; BOOLEAN retrySrb = FALSE; + BOOLEAN isReadLogExtSrbIssuedInNCQErrorRecovery = FALSE; slotContent = &ChannelExtension->Slot[SlotNumber]; if (slotContent->Srb == NULL) { @@ -685,6 +704,12 @@ Affected Variables/Registers: srbExtension = GetSrbExtension(slotContent->Srb); isSenseSrb = IsRequestSenseSrb(srbExtension->AtaFunction); + if ((slotContent->Srb == (PSTORAGE_REQUEST_BLOCK)&ChannelExtension->Sense.Srb) && + (ChannelExtension->StateFlags.NcqErrorRecoveryInProcess == 1)) { + + isReadLogExtSrbIssuedInNCQErrorRecovery = TRUE; + } + if (LogExecuteFullDetail(ChannelExtension->AdapterExtension->LogFlags)) { RecordExecutionHistory(ChannelExtension, 0x10000053);//ReleaseSlottedCommand } @@ -766,7 +791,6 @@ Affected Variables/Registers: } } - //3.1 Make the slot available again slotContent->CmdHeader = NULL; slotContent->CommandHistoryIndex = 0; @@ -796,6 +820,21 @@ Affected Variables/Registers: } } + // + // There is a bad timing will break the error recovery and cause IO hang eventually, typically happen in SSD but not limited to. + // 1. NCQ error happen first, which will issue the ReadLogExtCommand; + // 2. Then Nonqueued error happen before the ReadLogExtCommand complete. + // In ReleaseSlottedCommand, ReadLogExtCommand may got chance to be retried, which will result in SingleIoSlice set to 1, + // and SingleIoSlice will skip the PreservedSettingCommand(the ReservedSlotInUse is already set). + // 3. Then ReservedSlotInUse never got chance to be cleared because it relies on PreservedSettingCommand execution. + // ReservedSlotInUse will block the queue activation, finally the IO will hang and AhciPortReset will be repeatedly issued by upper layer. + // + // Do not retry ReadLogExtCommand when NCQ error recovery in process, which makes sense since ReadLogExtCommand is sense-like command as well. + // + if (retrySrb && isReadLogExtSrbIssuedInNCQErrorRecovery) { + retrySrb = FALSE; + } + if (retrySrb) { srbToRelease->SrbStatus = SRB_STATUS_PENDING; srbExtension->RetryCount++; @@ -1128,30 +1167,32 @@ Affected Variables/Registers: { BOOLEAN reservedSlotInUse = 0; - if (LogExecuteFullDetail(ChannelExtension->AdapterExtension->LogFlags)) { - RecordExecutionHistory(ChannelExtension, 0x00000041); //RestorePreservedSettings - } + RecordExecutionHistory(ChannelExtension, 0x00000041); // RestorePreservedSettings - //1 Reinitialize all the commands to send + //1 Reinitialize all the commands to send ChannelExtension->PersistentSettings.SlotsToSend = ChannelExtension->PersistentSettings.Slots; reservedSlotInUse = InterlockedBitTestAndSet((LONG*)&ChannelExtension->StateFlags, 3); //ReservedSlotInUse field is at bit 3 if (reservedSlotInUse == 1) { + // the process has been started. + RecordExecutionHistory(ChannelExtension, 0x10010041); // RestorePreservedSettings process already started return; } - // acquire active reference for process of restore preserved settings + // acquire active reference for process of restore preserved settings if (!AtDIRQL) { - // this is only needed if function is called at lower level than DIRQL. - // the places calling this function at DIRQL already made sure the unit is active. + // this is only needed if function is called at lower level than DIRQL. + // the places calling this function at DIRQL already made sure the unit is active. ULONG restorePreservedSettingsInProcess; BOOLEAN portIdle = FALSE; restorePreservedSettingsInProcess = InterlockedBitTestAndSet((LONG*)&ChannelExtension->PoFxPendingWork, 0); //RestorePreservedSettings is at bit 0 if (restorePreservedSettingsInProcess == 1) { + // it's already in process, return from here + RecordExecutionHistory(ChannelExtension, 0x10020041); // RestorePreservedSettings already in process return; } @@ -1160,21 +1201,24 @@ Affected Variables/Registers: } if (portIdle) { + // Unit Active process will continue on RestorePreservedSettings process + RecordExecutionHistory(ChannelExtension, 0x10030041); // RestorePreservedSettings will continue in Unit Active return; } else { ULONG restorePreservedSettingsPending; restorePreservedSettingsPending = InterlockedBitTestAndReset((LONG*)&ChannelExtension->PoFxPendingWork, 0); //RestorePreservedSettings is at bit 0 if (restorePreservedSettingsPending == 0) { + // Unit Active process already started on RestorePreservedSettings process + RecordExecutionHistory(ChannelExtension, 0x10040041); // RestorePreservedSettings already started in Unit Active return; } } } - //2 Start the first command IssuePreservedSettingCommands(ChannelExtension, NULL); @@ -1209,29 +1253,29 @@ Affected Variables/Registers: { BOOLEAN reservedSlotInUse = 0; - if (LogExecuteFullDetail(ChannelExtension->AdapterExtension->LogFlags)) { - RecordExecutionHistory(ChannelExtension, 0x00000042); //AhciPortIssueInitCommands - } + RecordExecutionHistory(ChannelExtension, 0x00000042); // AhciPortIssueInitCommands - //1 Reinitialize all the commands to send + //1 Reinitialize all the commands to send ChannelExtension->DeviceInitCommands.CommandToSend = 0; reservedSlotInUse = InterlockedBitTestAndSet((LONG*)&ChannelExtension->StateFlags, 3); //ReservedSlotInUse field is at bit 3 if (reservedSlotInUse == 1) { + // the process has been started. + RecordExecutionHistory(ChannelExtension, 0x10010042); // AhciPortIssueInitCommands process already started return; } - // acquire active reference for process of issuing init commands - // note: Port should be in Active state during device start. + // acquire active reference for process of issuing init commands + // note: Port should be in Active state during device start. if (PortAcquireActiveReference(ChannelExtension, NULL, NULL)) { ChannelExtension->StateFlags.RestorePreservedSettingsActiveReferenced = 1; } - //2 Start the first command + //2 Start the first command IssueInitCommands(ChannelExtension, NULL); - //3 Starts processing the command. Only need to do the first command if it exists. all others will be done by processing completion routine. + //3 Starts processing the command. Only need to do the first command if it exists. all others will be done by processing completion routine. if (ChannelExtension->Local.Srb.SrbExtension != NULL) { PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension((PSTORAGE_REQUEST_BLOCK)&ChannelExtension->Local.Srb); if (srbExtension->AtaFunction != 0) { @@ -1242,6 +1286,164 @@ Affected Variables/Registers: return; } +VOID +AhciPortStartTelemetry( + _In_ PAHCI_ADAPTER_EXTENSION AdapterExtension + ) +/*++ + +Routine Description: + + This function logs telemetry for AHCI port start status. + +Arguments: + + AdapterExtension - Pointer to the device extension for adapter. + +Return Value: + + None. + +--*/ +{ + NT_ASSERT(AdapterExtension->InRunningPortsProcess == FALSE); + + for (ULONG i = 0; i <= AdapterExtension->HighestPort; ++i) { + PAHCI_CHANNEL_EXTENSION channelExtension = AdapterExtension->PortExtension[i]; + if (channelExtension != NULL) { + BOOLEAN portStartSuccess = (channelExtension->StartState.ChannelNextStartState == StartComplete) ? (TRUE) : (FALSE); + AhciTelemetryLogPortStart(channelExtension, + (PSTOR_ADDRESS)&(channelExtension->DeviceExtension->DeviceAddress), + ((portStartSuccess) ? (AhciTelemetryEventIdPortStartSuccess) : (AhciTelemetryEventIdPortRunningStartFail)), + ((portStartSuccess) ? ("Port Start Success") : ("Port Start Failed")), + ((portStartSuccess) ? (0) : (AHCI_TELEMETRY_FLAG_NOT_SUPPRESS_LOGGING)), + NULL, + 0 + ); + } + } + + return; +} + +VOID +AhciPortMarkDeviceFailed( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_ AHCI_DEVICE_FAILURE_REASON FailureReason, + _In_ ULONG Flags, + _In_ BOOLEAN SuppressFailure + ) +/*++ + +Routine Description: + + This function will call storport API to mark failed device. + + When caller passes TRUE for SuppressFailure, this function will + only call storport API to mark failed device once; otherwise, + this function will always call storport API to mark failed device. + +Arguments: + + ChannelExtension - Pointer to the device extension for channel. + + FailureReason - Indicates device failure reason. + + Flags - Indicates whether remove failed device. + + SuppressFailure - Indicates whether suppress failures. + +Return Value: + + None. + +--*/ +{ + NT_ASSERT(ChannelExtension != NULL); + + // + // Ignore Non-ATA device failures. + // + if (!IsAtaDevice(&ChannelExtension->DeviceExtension->DeviceParameters)) { + return; + } + + switch (FailureReason) { + + case AhciDeviceFailureTooManyBusChange: { + if ((!SuppressFailure) || + ((ChannelExtension->DeviceFailureThrottleFlag & AHCI_BUS_CHANGE_WARNING_THROTTLE_MASK) == 0)) { + StorPortMarkDeviceFailed(ChannelExtension->AdapterExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + Flags, + L"Too many bus change"); + } + + if (SuppressFailure && + ((ChannelExtension->DeviceFailureThrottleFlag & AHCI_BUS_CHANGE_WARNING_THROTTLE_MASK) == 0)) { + ChannelExtension->DeviceFailureThrottleFlag |= AHCI_BUS_CHANGE_WARNING_THROTTLE_MASK; + } + + break; + } + + case AhciDeviceFailureTooManyNCQError: { + if ((!SuppressFailure) || + ((ChannelExtension->DeviceFailureThrottleFlag & AHCI_NCQ_ERROR_WARNING_THROTTLE_MASK) == 0)) { + StorPortMarkDeviceFailed(ChannelExtension->AdapterExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + Flags, + L"Too many NCQ error"); + } + + if (SuppressFailure && + ((ChannelExtension->DeviceFailureThrottleFlag & AHCI_NCQ_ERROR_WARNING_THROTTLE_MASK) == 0)) { + ChannelExtension->DeviceFailureThrottleFlag |= AHCI_NCQ_ERROR_WARNING_THROTTLE_MASK; + } + + break; + } + + case AhciDeviceFailureTooManyNonQueuedError: { + if ((!SuppressFailure) || + ((ChannelExtension->DeviceFailureThrottleFlag & AHCI_NON_QUEUED_ERROR_WARNING_THROTTLE_MASK) == 0)) { + StorPortMarkDeviceFailed(ChannelExtension->AdapterExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + Flags, + L"Too many NonQueue error"); + } + + if (SuppressFailure && + ((ChannelExtension->DeviceFailureThrottleFlag & AHCI_NON_QUEUED_ERROR_WARNING_THROTTLE_MASK) == 0)) { + ChannelExtension->DeviceFailureThrottleFlag |= AHCI_NON_QUEUED_ERROR_WARNING_THROTTLE_MASK; + } + + break; + } + + case AhciDeviceFailureDeviceStuck: { + if ((!SuppressFailure) || + ((ChannelExtension->DeviceFailureThrottleFlag & AHCI_DEVICE_STUCK_WARNING_THROTTLE_MASK) == 0)) { + StorPortMarkDeviceFailed(ChannelExtension->AdapterExtension, + (PSTOR_ADDRESS)&(ChannelExtension->DeviceExtension->DeviceAddress), + 0, + L"Device may stuck"); + } + + if (SuppressFailure && + ((ChannelExtension->DeviceFailureThrottleFlag & AHCI_DEVICE_STUCK_WARNING_THROTTLE_MASK) == 0)) { + ChannelExtension->DeviceFailureThrottleFlag |= AHCI_DEVICE_STUCK_WARNING_THROTTLE_MASK; + } + + break; + } + + default: { + NT_ASSERT(FALSE); + break; + } + } +} #if _MSC_VER >= 1200 #pragma warning(pop) @@ -1251,4 +1453,3 @@ Affected Variables/Registers: #pragma warning(default:4214) #pragma warning(default:4201) #endif - diff --git a/storage/miniports/storahci/src/util.h b/storage/miniports/storahci/src/util.h index c05339b1f..249b75476 100644 --- a/storage/miniports/storahci/src/util.h +++ b/storage/miniports/storahci/src/util.h @@ -54,6 +54,21 @@ AhciFillMemory( } } +__inline +VOID +AhciCopyMemory( + _Out_writes_(Length) PCHAR Destination, + _In_reads_(Length) PCHAR Source, + _In_ ULONG Length + ) +{ + ULONG i; + + for (i = 0; i < Length; i++) { + Destination[i] = Source[i]; + } +} + __inline BOOLEAN IsPortValid( @@ -191,7 +206,7 @@ IsMsnEnabled( _In_ PAHCI_DEVICE_EXTENSION DeviceExtension ) { - return ( (DeviceExtension->DeviceParameters.AtaDeviceType == DeviceIsAta) && + return ( IsAtaDevice(&DeviceExtension->DeviceParameters) && (DeviceExtension->IdentifyDeviceData->MsnSupport == 1) ); } @@ -201,7 +216,7 @@ IsRmfEnabled( _In_ PAHCI_DEVICE_EXTENSION DeviceExtension ) { - return ( (DeviceExtension->DeviceParameters.AtaDeviceType == DeviceIsAta) && + return ( IsAtaDevice(&DeviceExtension->DeviceParameters) && (DeviceExtension->IdentifyDeviceData->CommandSetSupport.RemovableMediaFeature == 1) && (DeviceExtension->IdentifyDeviceData->GeneralConfiguration.RemovableMedia == 1) ); } @@ -212,7 +227,7 @@ NoFlushDevice( _In_ PAHCI_DEVICE_EXTENSION DeviceExtension ) { - return ( (DeviceExtension->DeviceParameters.AtaDeviceType == DeviceIsAta) && + return ( IsAtaDevice(&DeviceExtension->DeviceParameters) && (DeviceExtension->IdentifyDeviceData->CommandSetSupport.WriteCache == 0) && (DeviceExtension->IdentifyDeviceData->CommandSetSupport.FlushCache == 0) && (DeviceExtension->IdentifyDeviceData->CommandSetSupport.FlushCacheExt == 0) ); @@ -234,7 +249,7 @@ IsSmartFeatureSupported( _In_ PAHCI_DEVICE_EXTENSION DeviceExtension ) { - return ( (DeviceExtension->DeviceParameters.AtaDeviceType == DeviceIsAta) && + return ( IsAtaDevice(&DeviceExtension->DeviceParameters) && (DeviceExtension->IdentifyDeviceData->CommandSetSupport.SmartCommands == 1) ); } @@ -264,7 +279,7 @@ IsExtendedPowerConditionsFeatureSupported( _In_ PAHCI_DEVICE_EXTENSION DeviceExtension ) { - return ( (DeviceExtension->DeviceParameters.AtaDeviceType == DeviceIsAta) && + return ( IsAtaDevice(&DeviceExtension->DeviceParameters) && (DeviceExtension->IdentifyDeviceData->CommandSetSupportExt.ExtendedPowerConditions == 1) ); } @@ -274,7 +289,7 @@ IsStreamingFeatureSupported( _In_ PAHCI_DEVICE_EXTENSION DeviceExtension ) { - return ( (DeviceExtension->DeviceParameters.AtaDeviceType == DeviceIsAta) && + return ( IsAtaDevice(&DeviceExtension->DeviceParameters) && (DeviceExtension->IdentifyDeviceData->CommandSetSupport.StreamingFeature == 1) ); } @@ -284,7 +299,7 @@ IsNcqFeatureSupported( _In_ PAHCI_DEVICE_EXTENSION DeviceExtension ) { - return ( (DeviceExtension->DeviceParameters.AtaDeviceType == DeviceIsAta) && + return ( IsAtaDevice(&DeviceExtension->DeviceParameters) && (DeviceExtension->IdentifyDeviceData->SerialAtaCapabilities.NCQ == 1) ); } @@ -533,6 +548,7 @@ IsExternalPort( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension ) { + if ( (ChannelExtension->Px->CMD.HPCP) || ((ChannelExtension->AdapterExtension->CAP.SXS) && (ChannelExtension->Px->CMD.ESP)) || ((ChannelExtension->AdapterExtension->CAP.SMPS) && (ChannelExtension->Px->CMD.MPSP)) ) { @@ -551,6 +567,7 @@ IsLPMCapablePort( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension ) { + // if a port is marked eSATA port but not having mechanical switch nor supporting Cold Presence Detection, don't turn on LPM on it. if ( (ChannelExtension->Px->CMD.HPCP) || ((ChannelExtension->AdapterExtension->CAP.SXS) && (ChannelExtension->Px->CMD.ESP)) ) { @@ -570,7 +587,8 @@ IsDeviceHybridInfoSupported( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension ) { - if ( (ChannelExtension->DeviceExtension[0].DeviceParameters.AtaDeviceType == DeviceIsAta) && + + if ( IsAtaDevice(&ChannelExtension->DeviceExtension[0].DeviceParameters) && (ChannelExtension->DeviceExtension[0].IdentifyDeviceData->SerialAtaFeaturesSupported.HybridInformation == 1) ) { return TRUE; @@ -627,6 +645,7 @@ _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension // return TRUE; } + } return FALSE; } @@ -647,6 +666,7 @@ _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension // return TRUE; } + } return FALSE; } @@ -661,7 +681,6 @@ IsD3ColdAllowed( UNREFERENCED_PARAMETER(AdapterExtension); - return allowed; } @@ -671,6 +690,7 @@ IsPortD3ColdEnabled( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension ) { + // if device is D3Cold capable, return TRUE. if (ChannelExtension->StateFlags.D3ColdEnabled == 1) { return TRUE; @@ -688,13 +708,6 @@ IsReceivingSystemPowerHints( return (AdapterExtension->SystemPowerHintState != RaidSystemPowerUnknown); } - - - - - - - __inline BOOLEAN AdapterPoFxEnabled ( @@ -827,15 +840,23 @@ PortReleaseActiveReference ( __inline BOOLEAN -DeviceIdentificationComplete( +DeviceIdentificationCompletedSuccess( _In_ PAHCI_ADAPTER_EXTENSION AdapterExtension ) { ULONG i; + // + // Note: There is possibility that 1st inquiry/identify will fail, then identify device + // data is invalid, but AtaDeviceType is initialized correctly during IO process. + // So it is also needed to check IdentifyDeviceSuccess flag to see whether identify + // command success. + // + for (i = 0; i <= AdapterExtension->HighestPort; i++) { - if ( (AdapterExtension->PortExtension[i] != NULL) && - (AdapterExtension->PortExtension[i]->DeviceExtension->DeviceParameters.AtaDeviceType == DeviceUnknown) ) { + if ((AdapterExtension->PortExtension[i] != NULL) && + ((AdapterExtension->PortExtension[i]->DeviceExtension->DeviceParameters.AtaDeviceType == DeviceUnknown) || + (AdapterExtension->PortExtension[i]->StateFlags.IdentifyDeviceSuccess == 0))) { return FALSE; } } @@ -1018,6 +1039,7 @@ IsFuaSupported( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension ) { + return (ChannelExtension->DeviceExtension->DeviceParameters.StateFlags.FuaSupported == 1); } @@ -1045,6 +1067,7 @@ NoLpmSupport( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension ) { + if (ChannelExtension->AdapterExtension->StateFlags.StoppedState == 1) { // adapter is stopped, Px registers are not accessible. return TRUE; @@ -1067,6 +1090,7 @@ NeedToSetTransferMode( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension ) { + UNREFERENCED_PARAMETER(ChannelExtension); // make WDK sample build clean return FALSE; } @@ -1077,6 +1101,7 @@ IsSingleIoDevice( _In_ PAHCI_ADAPTER_EXTENSION AdapterExtension ) { + UNREFERENCED_PARAMETER(AdapterExtension); // make WDK sample build clean return FALSE; } @@ -1087,6 +1112,7 @@ AdapterResetInInit( _In_ PAHCI_ADAPTER_EXTENSION AdapterExtension ) { + UNREFERENCED_PARAMETER(AdapterExtension); // make WDK sample build clean return FALSE; } @@ -1097,6 +1123,7 @@ IgnoreHotPlug( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension ) { + return (ChannelExtension->StateFlags.IgnoreHotplugInterrupt == 1); } @@ -1106,6 +1133,7 @@ AdapterNoNonQueuedErrorRecovery( _In_ PAHCI_ADAPTER_EXTENSION AdapterExtension ) { + UNREFERENCED_PARAMETER(AdapterExtension); // make WDK sample build clean return FALSE; } @@ -1116,6 +1144,7 @@ CloResetEnabled( _In_ PAHCI_ADAPTER_EXTENSION AdapterExtension ) { + UNREFERENCED_PARAMETER(AdapterExtension); // make WDK sample build clean return (AdapterExtension->CAP.SCLO == 1); } @@ -1126,6 +1155,7 @@ IsNCQSupported( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension ) { + if ( IsAtaDevice(&ChannelExtension->DeviceExtension->DeviceParameters) && (ChannelExtension->AdapterExtension->CAP.SNCQ == 1) && (ChannelExtension->DeviceExtension->IdentifyDeviceData->SerialAtaCapabilities.NCQ == 1) && @@ -1145,6 +1175,7 @@ IsFirmwareUpdateSupported( BOOLEAN support; + if (ChannelExtension->DeviceExtension->FirmwareUpdate.DmOffsetsDeferredSupported) { support = TRUE; } else { @@ -1252,7 +1283,6 @@ PartialToSlumberTransitionIsAllowed ( return FALSE; } - if ( ((ChannelExtension->LastUserLpmPowerSetting & 0x2) != 0) && (ChannelExtension->DeviceExtension->IdentifyDeviceData->SerialAtaCapabilities.DeviceAutoPS == 1) && (ChannelExtension->DeviceExtension->IdentifyDeviceData->SerialAtaFeaturesEnabled.DeviceAutoPS == 1) ) { @@ -1296,6 +1326,7 @@ GetHybridMaxLbaRangeCountForChangeLba ( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension ) { + // // If CacheBehavior is supported, disk doesn't move data from primary medium to caching medium when // executes HybridChangeByLba command. Use 64 as default MaxLbaRangeCountForChangeLba value. @@ -1314,17 +1345,52 @@ SupportsEnhancedNcqErrorRecovery ( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension ) { + if ((ChannelExtension->DeviceExtension->SupportedGPLPages.SinglePage.NcqCommandError == 1) && (ChannelExtension->DeviceExtension->IoRecord.NcqReadLogErrorCount <= 5)) { return TRUE; + } else { return FALSE; } } +__inline +BOOLEAN +IsAtaPioDataInCmd( + _In_ PSTORAGE_REQUEST_BLOCK Srb + ) +{ + PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); + UCHAR command = IDE_COMMAND_NOT_VALID; + + if (IsAtaCfisPayload(srbExtension->AtaFunction)) { + command = srbExtension->Cfis.Command; + } else if (IsAtaCommand(srbExtension->AtaFunction)) { + command = srbExtension->TaskFile.Current.bCommandReg; + } + + // + // Only put together some major PIO Data-In commands here, which should be good enough + // for the PIO Data-In command return status mismatch issue. + // + switch (command) { + case IDE_COMMAND_READ: + case IDE_COMMAND_READ_EXT: + case IDE_COMMAND_READ_MULTIPLE_EXT: + case IDE_COMMAND_READ_LOG_EXT: + case IDE_COMMAND_ATAPI_IDENTIFY: + case IDE_COMMAND_READ_MULTIPLE: + case IDE_COMMAND_IDENTIFY: + return TRUE; + default: + return FALSE; + } +} + __inline VOID -SetReturnRegisterValues ( +SetReturnRegisterValues( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb, _In_opt_ PGP_LOG_NCQ_COMMAND_ERROR NcqErrorLog @@ -1332,6 +1398,7 @@ SetReturnRegisterValues ( { PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); PCDB cdb = SrbGetCdb(Srb); + BOOLEAN pioDataInCmd = IsAtaPioDataInCmd(Srb); if ((IsReturnResults(srbExtension->Flags) == FALSE) || (srbExtension->ResultBuffer == NULL)) { return; @@ -1354,6 +1421,20 @@ SetReturnRegisterValues ( ataStatus->Header.AdditionalLength = 0x0C; ataStatus->Extend = Is48BitCommand(srbExtension->Flags) ? 1 : 0; + // For PIO Data In command, if it is successful, then the device will not send RD2H FIS. + // Only update the ATA status/error without getting the stale copy of information in D2H FIS. + if (pioDataInCmd && (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SUCCESS)) { + // According to SATA 3.0 specification 11.7: + // - Device will not send a RD2H FIS when PIO data in command success. + // According to AHCI 1.3.1 specification 5.3.9: + // - Device will report the ATA Status/Error register contents in the E_Status/Error field of the PIO Setup FIS. + // For ATA status return descriptor, refer to ATA Pass-Through specification 13.2.4. + ataStatus->Status = ChannelExtension->ReceivedFIS->PioSetupFis.E_Status; + ataStatus->Error = ChannelExtension->ReceivedFIS->PioSetupFis.Error; + + goto Exit; + } + if (NcqErrorLog == NULL) { ataStatus->Error = ChannelExtension->ReceivedFIS->D2hRegisterFis.Error; ataStatus->SectorCount7_0 = ChannelExtension->ReceivedFIS->D2hRegisterFis.SectorCount; @@ -1432,6 +1513,7 @@ SetReturnRegisterValues ( } } +Exit: // set flag SRB_STATUS_AUTOSENSE_VALID so that Storport will copy it back to original Sense Buffer Srb->SrbStatus |= SRB_STATUS_AUTOSENSE_VALID; @@ -1509,20 +1591,29 @@ AtaFormFactorToStorageFormFactor( return formFactor; } +__inline +BOOLEAN +IsAdapterRemovable( + _In_ PAHCI_ADAPTER_EXTENSION AdapterExtension + ) +{ + return (BOOLEAN)AdapterExtension->StateFlags.Removable; +} + __inline BOOLEAN IsAdapterRemoved( - _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension + _In_ PAHCI_ADAPTER_EXTENSION AdapterExtension ) { // - // When the adapter is removed, its registers are read as - // all 0xF's by the PCI bus. We use the PxSSTS register because - // it has several reserved bits which will unlikely be defined - // in the lifetime of the spec. + // When the adapter is removed, its registers are read as all 0xFs by the + // PCI bus. We use the Version register because all 0xFs is not a valid + // value and it is unlikely to ever be a valid value for the lifetime of + // the AHCI spec. // - ULONG ssts = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SSTS.AsUlong); - return (ssts == MAXULONG); + ULONG version = StorPortReadRegisterUlong(AdapterExtension, &AdapterExtension->ABAR_Address->VS.AsUlong); + return (version == MAXULONG); } @@ -1586,6 +1677,19 @@ AhciPortIssueInitCommands( PAHCI_CHANNEL_EXTENSION ChannelExtension ); +VOID +AhciPortStartTelemetry( + _In_ PAHCI_ADAPTER_EXTENSION AdapterExtension + ); + +VOID +AhciPortMarkDeviceFailed( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_ AHCI_DEVICE_FAILURE_REASON FailureReason, + _In_ ULONG Flags, + _In_ BOOLEAN SuppressFailure + ); + _Success_(return != FALSE) BOOLEAN CompareId ( @@ -1614,3 +1718,266 @@ AhciUlongIncrement( } } +VOID +FillAtaCommandErrorStruct( + _Out_ PATA_COMMAND_ERROR DataBuffer, + _In_ PSTORAGE_REQUEST_BLOCK Srb, + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension + ); + + +VOID +FORCEINLINE +AhciTelemetryLog( + _In_ PVOID HwDeviceExtension, + _In_opt_ PSTOR_ADDRESS Address, + _In_ ULONG DriverVersion, + _In_ ULONG EventId, + _In_reads_or_z_(EVENT_NAME_MAX_LENGTH) PSTR EventName, + _In_ ULONG EventVersion, + _In_ ULONG Flags, + _In_ ULONG EventBufferLength, + _In_reads_bytes_opt_(EventBufferLength) PUCHAR EventBuffer, + _In_reads_or_z_opt_(EVENT_MAX_PARAM_NAME_LEN) PSTR Parameter0Name, + _In_ ULONGLONG Parameter0Value, + _In_reads_or_z_opt_(EVENT_MAX_PARAM_NAME_LEN) PSTR Parameter1Name, + _In_ ULONGLONG Parameter1Value, + _In_reads_or_z_opt_(EVENT_MAX_PARAM_NAME_LEN) PSTR Parameter2Name, + _In_ ULONGLONG Parameter2Value, + _In_reads_or_z_opt_(EVENT_MAX_PARAM_NAME_LEN) PSTR Parameter3Name, + _In_ ULONGLONG Parameter3Value, + _In_reads_or_z_opt_(EVENT_MAX_PARAM_NAME_LEN) PSTR Parameter4Name, + _In_ ULONGLONG Parameter4Value, + _In_reads_or_z_opt_(EVENT_MAX_PARAM_NAME_LEN) PSTR Parameter5Name, + _In_ ULONGLONG Parameter5Value, + _In_reads_or_z_opt_(EVENT_MAX_PARAM_NAME_LEN) PSTR Parameter6Name, + _In_ ULONGLONG Parameter6Value, + _In_reads_or_z_opt_(EVENT_MAX_PARAM_NAME_LEN) PSTR Parameter7Name, + _In_ ULONGLONG Parameter7Value + ) +{ + STORPORT_TELEMETRY_EVENT event = {0}; + + event.DriverVersion = DriverVersion; + event.EventId = EventId; + StorPortCopyMemory(event.EventName, EventName, GetStringLength(EventName, EVENT_NAME_MAX_LENGTH)); + event.EventVersion = EventVersion; + event.Flags = Flags; + + if ((EventBufferLength > 0) && (EventBuffer != NULL)) { + event.EventBuffer = EventBuffer; + event.EventBufferLength = min(EventBufferLength, EVENT_BUFFER_MAX_LENGTH); + } + + if (Parameter0Name != NULL) { + StorPortCopyMemory(event.ParameterName0, Parameter0Name, GetStringLength(Parameter0Name, EVENT_MAX_PARAM_NAME_LEN)); + event.ParameterValue0 = Parameter0Value; + } + + if (Parameter1Name != NULL) { + StorPortCopyMemory(event.ParameterName1, Parameter1Name, GetStringLength(Parameter1Name, EVENT_MAX_PARAM_NAME_LEN)); + event.ParameterValue1 = Parameter1Value; + } + + if (Parameter2Name != NULL) { + StorPortCopyMemory(event.ParameterName2, Parameter2Name, GetStringLength(Parameter2Name, EVENT_MAX_PARAM_NAME_LEN)); + event.ParameterValue2 = Parameter2Value; + } + + if (Parameter3Name != NULL) { + StorPortCopyMemory(event.ParameterName3, Parameter3Name, GetStringLength(Parameter3Name, EVENT_MAX_PARAM_NAME_LEN)); + event.ParameterValue3 = Parameter3Value; + } + + if (Parameter4Name != NULL) { + StorPortCopyMemory(event.ParameterName4, Parameter4Name, GetStringLength(Parameter4Name, EVENT_MAX_PARAM_NAME_LEN)); + event.ParameterValue4 = Parameter4Value; + } + + if (Parameter5Name != NULL) { + StorPortCopyMemory(event.ParameterName5, Parameter5Name, GetStringLength(Parameter5Name, EVENT_MAX_PARAM_NAME_LEN)); + event.ParameterValue5 = Parameter5Value; + } + + if (Parameter6Name != NULL) { + StorPortCopyMemory(event.ParameterName6, Parameter6Name, GetStringLength(Parameter6Name, EVENT_MAX_PARAM_NAME_LEN)); + event.ParameterValue6 = Parameter6Value; + } + + if (Parameter7Name != NULL) { + StorPortCopyMemory(event.ParameterName7, Parameter7Name, GetStringLength(Parameter7Name, EVENT_MAX_PARAM_NAME_LEN)); + event.ParameterValue7 = Parameter7Value; + } + + StorPortLogTelemetry(HwDeviceExtension, Address, &event); + + return; +} + +VOID +FORCEINLINE +AhciTelemetryLogResetErrorRecovery( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_opt_ PSTOR_ADDRESS Address, + _In_ ULONG EventId, + _In_reads_or_z_(EVENT_NAME_MAX_LENGTH) PSTR EventName, + _In_ ULONG Flags, + _In_reads_or_z_(EVENT_MAX_PARAM_NAME_LEN) PSTR AdditionalParameterName, + _In_ ULONGLONG AdditionalParameterValue + ) +{ + LARGE_INTEGER time; + ULONGLONG elapsedTime; + + if (ChannelExtension != NULL) { + + StorPortQuerySystemTime(&time); + elapsedTime = time.QuadPart - ChannelExtension->LastLogResetErrorRecoveryTime; + + // + // If AHCI_TELEMETRY_FLAG_NOT_SUPPRESS_LOGGING is set, always log event; + // otherwise, if this is the first time logging or if it has been 1 hour + // since the last log, log the current counts of reset and error recovery. + // + if (((Flags & AHCI_TELEMETRY_FLAG_NOT_SUPPRESS_LOGGING) != 0) || + (ChannelExtension->LastLogResetErrorRecoveryTime == 0) || + (elapsedTime >= MS_TO_100NS(60*60*1000))) { + + ChannelExtension->LastLogResetErrorRecoveryTime = time.QuadPart; + + AhciTelemetryLog(ChannelExtension->AdapterExtension, + Address, + AHCI_TELEMETRY_DRIVER_VERSION, + EventId, + EventName, + AHCI_TELEMETRY_EVENT_VERSION, + Flags, + 0, + NULL, + "PortReset|PortErrorRecovery", + (((ULONGLONG)ChannelExtension->TotalCountPortReset << 32) | + ((ULONGLONG)ChannelExtension->TotalCountPortErrorRecovery)), + "StartFail|NonQErrorRecovery", + (((ULONGLONG)ChannelExtension->TotalCountRunningStartFailed << 32) | + ((ULONGLONG)ChannelExtension->TotalCountNonQueuedErrorRecovery)), + "NCQError|NCQErrorComplete", + (((ULONGLONG)ChannelExtension->TotalCountNCQError << 32) | + ((ULONGLONG)ChannelExtension->TotalCountNCQErrorRecoveryComplete)), + "PortDriverResetCount", + ChannelExtension->DeviceExtension[0].IoRecord.PortDriverResetCount, + "StateFlags", + *(ULONGLONG *)&(ChannelExtension->StateFlags), + "StartState", + *(ULONGLONG *)&(ChannelExtension->StartState), + "TotalCountSurpriseRemove", + ChannelExtension->TotalCountSurpriseRemove, + AdditionalParameterName, + AdditionalParameterValue + ); + } + } + + return; +} + +VOID +FORCEINLINE +AhciTelemetryLogPowerSettingChange( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_opt_ PSTOR_ADDRESS Address, + _In_ ULONG EventId, + _In_reads_or_z_(EVENT_NAME_MAX_LENGTH) PSTR EventName, + _In_ ULONG Flags, + _In_ ULONG Ipm, + _In_ ULONG LpmMode + ) +{ + if (ChannelExtension != NULL) { + + AhciTelemetryLog(ChannelExtension->AdapterExtension, + Address, + AHCI_TELEMETRY_DRIVER_VERSION, + EventId, + EventName, + AHCI_TELEMETRY_EVENT_VERSION, + Flags, + 0, + NULL, + "PowerSettingChangeCount", + ChannelExtension->TotalCountPowerSettingNotification, + "StateFlags", + *(ULONGLONG *)&(ChannelExtension->StateFlags), + "LastUserLpmPowerSetting", + ChannelExtension->LastUserLpmPowerSetting, + "CAP", + ChannelExtension->AdapterExtension->CAP.AsUlong, + "CAP2", + ChannelExtension->AdapterExtension->CAP2.AsUlong, + "PxCMD", + StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CMD.AsUlong), + "IPM", + Ipm, + "LpmMode|SlumberInterval", + LpmMode + ); + } + + return; +} + +VOID +FORCEINLINE +AhciTelemetryLogPortStart( + _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, + _In_opt_ PSTOR_ADDRESS Address, + _In_ ULONG EventId, + _In_reads_or_z_(EVENT_NAME_MAX_LENGTH) PSTR EventName, + _In_ ULONG Flags, + _In_reads_or_z_(EVENT_MAX_PARAM_NAME_LEN) PSTR AdditionalParameterName, + _In_ ULONGLONG AdditionalParameterValue + ) +{ + if (ChannelExtension != NULL) { + // + // Log the telemetry event only when at least one of below conditions is true: + // 1. AHCI_TELEMETRY_FLAG_NOT_SUPPRESS_LOGGING flag is set; + // 2. Current port start time is no less than 1s. + // + if (((Flags && AHCI_TELEMETRY_FLAG_NOT_SUPPRESS_LOGGING) != 0) || + (ChannelExtension->TotalPortStartTime >= 1000)) { + + AhciTelemetryLog(ChannelExtension->AdapterExtension, + Address, + AHCI_TELEMETRY_DRIVER_VERSION, + EventId, + EventName, + AHCI_TELEMETRY_EVENT_VERSION, + Flags, + 0, + NULL, + "PortReset|PortErrorRecovery", + (((ULONGLONG)ChannelExtension->TotalCountPortReset << 32) | + ((ULONGLONG)ChannelExtension->TotalCountPortErrorRecovery)), + "StartFail|NonQErrorRecovery", + (((ULONGLONG)ChannelExtension->TotalCountRunningStartFailed << 32) | + ((ULONGLONG)ChannelExtension->TotalCountNonQueuedErrorRecovery)), + "NCQError|NCQErrorComplete", + (((ULONGLONG)ChannelExtension->TotalCountNCQError << 32) | + ((ULONGLONG)ChannelExtension->TotalCountNCQErrorRecoveryComplete)), + "PortDriverResetCount", + ChannelExtension->DeviceExtension[0].IoRecord.PortDriverResetCount, + "StateFlags", + *(ULONGLONG *)&(ChannelExtension->StateFlags), + "StartState", + *(ULONGLONG *)&(ChannelExtension->StartState), + "PortStartTime(ms)", + ChannelExtension->TotalPortStartTime, + AdditionalParameterName, + AdditionalParameterValue + ); + } + } + + return; +} +