+/**\r
+ Send SMART Return Status command to check if the execution of SMART cmd is successful or not.\r
+\r
+ @param PciIo The PCI IO protocol instance.\r
+ @param AhciRegisters The pointer to the EFI_AHCI_REGISTERS.\r
+ @param Port The number of port.\r
+ @param PortMultiplier The port multiplier port number.\r
+ @param AtaStatusBlock A pointer to EFI_ATA_STATUS_BLOCK data structure.\r
+\r
+ @retval EFI_SUCCESS Successfully get the return status of S.M.A.R.T command execution.\r
+ @retval Others Fail to get return status data.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciAtaSmartReturnStatusCheck (\r
+ IN EFI_PCI_IO_PROTOCOL *PciIo,\r
+ IN EFI_AHCI_REGISTERS *AhciRegisters,\r
+ IN UINT8 Port,\r
+ IN UINT8 PortMultiplier,\r
+ IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_ATA_COMMAND_BLOCK AtaCommandBlock;\r
+ UINT8 LBAMid;\r
+ UINT8 LBAHigh;\r
+ UINTN FisBaseAddr;\r
+ UINT32 Value;\r
+\r
+ ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK));\r
+\r
+ AtaCommandBlock.AtaCommand = ATA_CMD_SMART;\r
+ AtaCommandBlock.AtaFeatures = ATA_SMART_RETURN_STATUS;\r
+ AtaCommandBlock.AtaCylinderLow = ATA_CONSTANT_4F;\r
+ AtaCommandBlock.AtaCylinderHigh = ATA_CONSTANT_C2;\r
+\r
+ //\r
+ // Send S.M.A.R.T Read Return Status command to device\r
+ //\r
+ Status = AhciNonDataTransfer (\r
+ PciIo,\r
+ AhciRegisters,\r
+ (UINT8)Port,\r
+ (UINT8)PortMultiplier,\r
+ NULL,\r
+ 0,\r
+ &AtaCommandBlock,\r
+ AtaStatusBlock,\r
+ ATA_ATAPI_TIMEOUT,\r
+ NULL\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ REPORT_STATUS_CODE (\r
+ EFI_ERROR_CODE | EFI_ERROR_MINOR,\r
+ (EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_DISABLED)\r
+ );\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ REPORT_STATUS_CODE (\r
+ EFI_PROGRESS_CODE,\r
+ (EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_ENABLE)\r
+ );\r
+\r
+ FisBaseAddr = (UINTN)AhciRegisters->AhciRFis + Port * sizeof (EFI_AHCI_RECEIVED_FIS);\r
+\r
+ Value = *(UINT32 *) (FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET);\r
+\r
+ if ((Value & EFI_AHCI_FIS_TYPE_MASK) == EFI_AHCI_FIS_REGISTER_D2H) {\r
+ LBAMid = ((UINT8 *)(UINTN)(FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET))[5];\r
+ LBAHigh = ((UINT8 *)(UINTN)(FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET))[6];\r
+\r
+ if ((LBAMid == 0x4f) && (LBAHigh == 0xc2)) {\r
+ //\r
+ // The threshold exceeded condition is not detected by the device\r
+ //\r
+ DEBUG ((EFI_D_INFO, "The S.M.A.R.T threshold exceeded condition is not detected\n"));\r
+ REPORT_STATUS_CODE (\r
+ EFI_PROGRESS_CODE,\r
+ (EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_UNDERTHRESHOLD)\r
+ );\r
+ } else if ((LBAMid == 0xf4) && (LBAHigh == 0x2c)) {\r
+ //\r
+ // The threshold exceeded condition is detected by the device\r
+ //\r
+ DEBUG ((EFI_D_INFO, "The S.M.A.R.T threshold exceeded condition is detected\n"));\r
+ REPORT_STATUS_CODE (\r
+ EFI_PROGRESS_CODE,\r
+ (EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_OVERTHRESHOLD)\r
+ );\r
+ }\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Enable SMART command of the disk if supported.\r
+\r
+ @param PciIo The PCI IO protocol instance.\r
+ @param AhciRegisters The pointer to the EFI_AHCI_REGISTERS.\r
+ @param Port The number of port.\r
+ @param PortMultiplier The port multiplier port number.\r
+ @param IdentifyData A pointer to data buffer which is used to contain IDENTIFY data.\r
+ @param AtaStatusBlock A pointer to EFI_ATA_STATUS_BLOCK data structure.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+AhciAtaSmartSupport (\r
+ IN EFI_PCI_IO_PROTOCOL *PciIo,\r
+ IN EFI_AHCI_REGISTERS *AhciRegisters,\r
+ IN UINT8 Port,\r
+ IN UINT8 PortMultiplier,\r
+ IN EFI_IDENTIFY_DATA *IdentifyData,\r
+ IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_ATA_COMMAND_BLOCK AtaCommandBlock;\r
+\r
+ //\r
+ // Detect if the device supports S.M.A.R.T.\r
+ //\r
+ if ((IdentifyData->AtaData.command_set_supported_82 & 0x0001) != 0x0001) {\r
+ //\r
+ // S.M.A.R.T is not supported by the device\r
+ //\r
+ DEBUG ((EFI_D_INFO, "S.M.A.R.T feature is not supported at port [%d] PortMultiplier [%d]!\n",\r
+ Port, PortMultiplier));\r
+ REPORT_STATUS_CODE (\r
+ EFI_ERROR_CODE | EFI_ERROR_MINOR,\r
+ (EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_NOTSUPPORTED)\r
+ );\r
+ } else {\r
+ //\r
+ // Check if the feature is enabled. If not, then enable S.M.A.R.T.\r
+ //\r
+ if ((IdentifyData->AtaData.command_set_feature_enb_85 & 0x0001) != 0x0001) {\r
+\r
+ REPORT_STATUS_CODE (\r
+ EFI_PROGRESS_CODE,\r
+ (EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_DISABLE)\r
+ );\r
+\r
+ ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK));\r
+\r
+ AtaCommandBlock.AtaCommand = ATA_CMD_SMART;\r
+ AtaCommandBlock.AtaFeatures = ATA_SMART_ENABLE_OPERATION;\r
+ AtaCommandBlock.AtaCylinderLow = ATA_CONSTANT_4F;\r
+ AtaCommandBlock.AtaCylinderHigh = ATA_CONSTANT_C2;\r
+\r
+ //\r
+ // Send S.M.A.R.T Enable command to device\r
+ //\r
+ Status = AhciNonDataTransfer (\r
+ PciIo,\r
+ AhciRegisters,\r
+ (UINT8)Port,\r
+ (UINT8)PortMultiplier,\r
+ NULL,\r
+ 0,\r
+ &AtaCommandBlock,\r
+ AtaStatusBlock,\r
+ ATA_ATAPI_TIMEOUT,\r
+ NULL\r
+ );\r
+\r
+\r
+ if (!EFI_ERROR (Status)) {\r
+ //\r
+ // Send S.M.A.R.T AutoSave command to device\r
+ //\r
+ ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK));\r
+\r
+ AtaCommandBlock.AtaCommand = ATA_CMD_SMART;\r
+ AtaCommandBlock.AtaFeatures = 0xD2;\r
+ AtaCommandBlock.AtaSectorCount = 0xF1;\r
+ AtaCommandBlock.AtaCylinderLow = ATA_CONSTANT_4F;\r
+ AtaCommandBlock.AtaCylinderHigh = ATA_CONSTANT_C2;\r
+\r
+ Status = AhciNonDataTransfer (\r
+ PciIo,\r
+ AhciRegisters,\r
+ (UINT8)Port,\r
+ (UINT8)PortMultiplier,\r
+ NULL,\r
+ 0,\r
+ &AtaCommandBlock,\r
+ AtaStatusBlock,\r
+ ATA_ATAPI_TIMEOUT,\r
+ NULL\r
+ );\r
+\r
+ if (!EFI_ERROR (Status)) {\r
+ Status = AhciAtaSmartReturnStatusCheck (\r
+ PciIo,\r
+ AhciRegisters,\r
+ (UINT8)Port,\r
+ (UINT8)PortMultiplier,\r
+ AtaStatusBlock\r
+ );\r
+ }\r
+ }\r
+ }\r
+ DEBUG ((EFI_D_INFO, "Enabled S.M.A.R.T feature at port [%d] PortMultiplier [%d]!\n",\r
+ Port, PortMultiplier));\r
+ }\r
+\r
+ return ;\r
+}\r
+\r