From 12873d57666d0beff41959a1fb8f9062016f0983 Mon Sep 17 00:00:00 2001 From: erictian Date: Thu, 28 Oct 2010 07:44:11 +0000 Subject: S.M.A.R.T feature enable git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@10985 6f19259b-4bc3-4df7-8a09-765794883524 --- MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AhciMode.c | 199 +++++++++++++++++++++++ MdeModulePkg/Bus/Ata/AtaAtapiPassThru/IdeMode.c | 177 ++++++++++++++++++++ 2 files changed, 376 insertions(+) diff --git a/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AhciMode.c b/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AhciMode.c index 06b280a001..92449f40c6 100644 --- a/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AhciMode.c +++ b/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AhciMode.c @@ -1249,6 +1249,191 @@ AhciReset ( return EFI_SUCCESS; } +/** + Send SMART Return Status command to check if the execution of SMART cmd is successful or not. + + @param PciIo The PCI IO protocol instance. + @param AhciRegisters The pointer to the EFI_AHCI_REGISTERS. + @param Port The number of port. + @param PortMultiplier The timeout value of stop. + @param AtaStatusBlock A pointer to EFI_ATA_STATUS_BLOCK data structure. + + @retval EFI_SUCCESS Successfully get the return status of S.M.A.R.T command execution. + @retval Others Fail to get return status data. + +**/ +EFI_STATUS +EFIAPI +AhciAtaSmartReturnStatusCheck ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN EFI_AHCI_REGISTERS *AhciRegisters, + IN UINT8 Port, + IN UINT8 PortMultiplier, + IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock + ) +{ + EFI_STATUS Status; + EFI_ATA_COMMAND_BLOCK AtaCommandBlock; + UINT8 LBAMid; + UINT8 LBAHigh; + UINTN FisBaseAddr; + UINT32 Value; + + ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK)); + + AtaCommandBlock.AtaCommand = ATA_CMD_SMART; + AtaCommandBlock.AtaFeatures = ATA_SMART_RETURN_STATUS; + AtaCommandBlock.AtaCylinderLow = ATA_CONSTANT_4F; + AtaCommandBlock.AtaCylinderHigh = ATA_CONSTANT_C2; + + // + // Send S.M.A.R.T Read Return Status command to device + // + Status = AhciNonDataTransfer ( + PciIo, + AhciRegisters, + (UINT8)Port, + (UINT8)PortMultiplier, + NULL, + 0, + &AtaCommandBlock, + AtaStatusBlock, + ATA_ATAPI_TIMEOUT + ); + + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + FisBaseAddr = (UINTN)AhciRegisters->AhciRFis + Port * sizeof (EFI_AHCI_RECEIVED_FIS); + + Value = *(UINT32 *) (FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET); + + if ((Value & EFI_AHCI_FIS_TYPE_MASK) == EFI_AHCI_FIS_REGISTER_D2H) { + LBAMid = ((UINT8 *)(UINTN)(FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET))[5]; + LBAHigh = ((UINT8 *)(UINTN)(FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET))[6]; + + if ((LBAMid == 0x4f) && (LBAHigh == 0xc2)) { + // + // The threshold exceeded condition is not detected by the device + // + DEBUG ((EFI_D_INFO, "The S.M.A.R.T threshold exceeded condition is not detected\n")); + + } else if ((LBAMid == 0xf4) && (LBAHigh == 0x2c)) { + // + // The threshold exceeded condition is detected by the device + // + DEBUG ((EFI_D_INFO, "The S.M.A.R.T threshold exceeded condition is detected\n")); + } + } + + return EFI_SUCCESS; +} + +/** + Enable SMART command of the disk if supported. + + @param PciIo The PCI IO protocol instance. + @param AhciRegisters The pointer to the EFI_AHCI_REGISTERS. + @param Port The number of port. + @param PortMultiplier The timeout value of stop. + @param IdentifyData A pointer to data buffer which is used to contain IDENTIFY data. + @param AtaStatusBlock A pointer to EFI_ATA_STATUS_BLOCK data structure. + +**/ +VOID +EFIAPI +AhciAtaSmartSupport ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN EFI_AHCI_REGISTERS *AhciRegisters, + IN UINT8 Port, + IN UINT8 PortMultiplier, + IN EFI_IDENTIFY_DATA *IdentifyData, + IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock + ) +{ + EFI_STATUS Status; + EFI_ATA_COMMAND_BLOCK AtaCommandBlock; + + // + // Detect if the device supports S.M.A.R.T. + // + if ((IdentifyData->AtaData.command_set_supported_82 & 0x0001) != 0x0001) { + // + // S.M.A.R.T is not supported by the device + // + DEBUG ((EFI_D_INFO, "S.M.A.R.T feature is not supported at port [%d] PortMultiplier [%d]!\n", + Port, PortMultiplier)); + } else { + // + // Check if the feature is enabled. If not, then enable S.M.A.R.T. + // + if ((IdentifyData->AtaData.command_set_feature_enb_85 & 0x0001) != 0x0001) { + ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK)); + + AtaCommandBlock.AtaCommand = ATA_CMD_SMART; + AtaCommandBlock.AtaFeatures = ATA_SMART_ENABLE_OPERATION; + AtaCommandBlock.AtaCylinderLow = ATA_CONSTANT_4F; + AtaCommandBlock.AtaCylinderHigh = ATA_CONSTANT_C2; + + // + // Send S.M.A.R.T Enable command to device + // + Status = AhciNonDataTransfer ( + PciIo, + AhciRegisters, + (UINT8)Port, + (UINT8)PortMultiplier, + NULL, + 0, + &AtaCommandBlock, + AtaStatusBlock, + ATA_ATAPI_TIMEOUT + ); + + + if (!EFI_ERROR (Status)) { + // + // Send S.M.A.R.T AutoSave command to device + // + ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK)); + + AtaCommandBlock.AtaCommand = ATA_CMD_SMART; + AtaCommandBlock.AtaFeatures = 0xD2; + AtaCommandBlock.AtaSectorCount = 0xF1; + AtaCommandBlock.AtaCylinderLow = ATA_CONSTANT_4F; + AtaCommandBlock.AtaCylinderHigh = ATA_CONSTANT_C2; + + Status = AhciNonDataTransfer ( + PciIo, + AhciRegisters, + (UINT8)Port, + (UINT8)PortMultiplier, + NULL, + 0, + &AtaCommandBlock, + AtaStatusBlock, + ATA_ATAPI_TIMEOUT + ); + + if (!EFI_ERROR (Status)) { + Status = AhciAtaSmartReturnStatusCheck ( + PciIo, + AhciRegisters, + (UINT8)Port, + (UINT8)PortMultiplier, + AtaStatusBlock + ); + } + } + } + DEBUG ((EFI_D_INFO, "Enabled S.M.A.R.T feature at port [%d] PortMultiplier [%d]!\n", + Port, PortMultiplier)); + } + + return ; +} + /** Send Buffer cmd to specific device. @@ -1947,6 +2132,20 @@ AhciModeInitialization ( DEBUG ((EFI_D_INFO, "port [%d] port mulitplier [%d] has a [%a]\n", Port, 0, DeviceType == EfiIdeCdrom ? "cdrom" : "harddisk")); + // + // If the device is a hard disk, then try to enable S.M.A.R.T feature + // + if (DeviceType == EfiIdeHarddisk) { + AhciAtaSmartSupport ( + PciIo, + AhciRegisters, + Port, + 0, + &Buffer, + NULL + ); + } + // // Submit identify data to IDE controller init driver // diff --git a/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/IdeMode.c b/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/IdeMode.c index bdbf3ab450..50b95a6713 100644 --- a/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/IdeMode.c +++ b/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/IdeMode.c @@ -2090,6 +2090,170 @@ SetDriveParameters ( return Status; } +/** + Send SMART Return Status command to check if the execution of SMART cmd is successful or not. + + @param Instance A pointer to ATA_ATAPI_PASS_THRU_INSTANCE data structure. + @param Channel The channel number of device. + @param Device The device number of device. + @param AtaStatusBlock A pointer to EFI_ATA_STATUS_BLOCK data structure. + + @retval EFI_SUCCESS Successfully get the return status of S.M.A.R.T command execution. + @retval Others Fail to get return status data. + +**/ +EFI_STATUS +EFIAPI +IdeAtaSmartReturnStatusCheck ( + IN ATA_ATAPI_PASS_THRU_INSTANCE *Instance, + IN UINT8 Channel, + IN UINT8 Device, + IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock + ) +{ + EFI_STATUS Status; + EFI_ATA_COMMAND_BLOCK AtaCommandBlock; + UINT8 LBAMid; + UINT8 LBAHigh; + + ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK)); + + AtaCommandBlock.AtaCommand = ATA_CMD_SMART; + AtaCommandBlock.AtaFeatures = ATA_SMART_RETURN_STATUS; + AtaCommandBlock.AtaCylinderLow = ATA_CONSTANT_4F; + AtaCommandBlock.AtaCylinderHigh = ATA_CONSTANT_C2; + AtaCommandBlock.AtaDeviceHead = (UINT8) ((Device << 0x4) | 0xe0); + + // + // Send S.M.A.R.T Read Return Status command to device + // + Status = AtaNonDataCommandIn ( + Instance->PciIo, + &Instance->IdeRegisters[Channel], + &AtaCommandBlock, + AtaStatusBlock, + ATA_ATAPI_TIMEOUT + ); + + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + LBAMid = IdeReadPortB (Instance->PciIo, Instance->IdeRegisters[Channel].CylinderLsb); + LBAHigh = IdeReadPortB (Instance->PciIo, Instance->IdeRegisters[Channel].CylinderMsb); + + if ((LBAMid == 0x4f) && (LBAHigh == 0xc2)) { + // + // The threshold exceeded condition is not detected by the device + // + DEBUG ((EFI_D_INFO, "The S.M.A.R.T threshold exceeded condition is not detected\n")); + + } else if ((LBAMid == 0xf4) && (LBAHigh == 0x2c)) { + // + // The threshold exceeded condition is detected by the device + // + DEBUG ((EFI_D_INFO, "The S.M.A.R.T threshold exceeded condition is detected\n")); + } + + return EFI_SUCCESS; +} + +/** + Enable SMART command of the disk if supported. + + @param Instance A pointer to ATA_ATAPI_PASS_THRU_INSTANCE data structure. + @param Channel The channel number of device. + @param Device The device number of device. + @param IdentifyData A pointer to data buffer which is used to contain IDENTIFY data. + @param AtaStatusBlock A pointer to EFI_ATA_STATUS_BLOCK data structure. + +**/ +VOID +EFIAPI +IdeAtaSmartSupport ( + IN ATA_ATAPI_PASS_THRU_INSTANCE *Instance, + IN UINT8 Channel, + IN UINT8 Device, + IN EFI_IDENTIFY_DATA *IdentifyData, + IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock + ) +{ + EFI_STATUS Status; + EFI_ATA_COMMAND_BLOCK AtaCommandBlock; + + // + // Detect if the device supports S.M.A.R.T. + // + if ((IdentifyData->AtaData.command_set_supported_82 & 0x0001) != 0x0001) { + // + // S.M.A.R.T is not supported by the device + // + DEBUG ((EFI_D_INFO, "S.M.A.R.T feature is not supported at [%a] channel [%a] device!\n", + (Channel == 1) ? "secondary" : "primary", (Device == 1) ? "slave" : "master")); + } else { + // + // Check if the feature is enabled. If not, then enable S.M.A.R.T. + // + if ((IdentifyData->AtaData.command_set_feature_enb_85 & 0x0001) != 0x0001) { + + ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK)); + + AtaCommandBlock.AtaCommand = ATA_CMD_SMART; + AtaCommandBlock.AtaFeatures = ATA_SMART_ENABLE_OPERATION; + AtaCommandBlock.AtaCylinderLow = ATA_CONSTANT_4F; + AtaCommandBlock.AtaCylinderHigh = ATA_CONSTANT_C2; + AtaCommandBlock.AtaDeviceHead = (UINT8) ((Device << 0x4) | 0xe0); + + // + // Send S.M.A.R.T Enable command to device + // + Status = AtaNonDataCommandIn ( + Instance->PciIo, + &Instance->IdeRegisters[Channel], + &AtaCommandBlock, + AtaStatusBlock, + ATA_ATAPI_TIMEOUT + ); + + if (!EFI_ERROR (Status)) { + // + // Send S.M.A.R.T AutoSave command to device + // + ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK)); + + AtaCommandBlock.AtaCommand = ATA_CMD_SMART; + AtaCommandBlock.AtaFeatures = 0xD2; + AtaCommandBlock.AtaSectorCount = 0xF1; + AtaCommandBlock.AtaCylinderLow = ATA_CONSTANT_4F; + AtaCommandBlock.AtaCylinderHigh = ATA_CONSTANT_C2; + AtaCommandBlock.AtaDeviceHead = (UINT8) ((Device << 0x4) | 0xe0); + + Status = AtaNonDataCommandIn ( + Instance->PciIo, + &Instance->IdeRegisters[Channel], + &AtaCommandBlock, + AtaStatusBlock, + ATA_ATAPI_TIMEOUT + ); + if (!EFI_ERROR (Status)) { + Status = IdeAtaSmartReturnStatusCheck ( + Instance, + Channel, + Device, + AtaStatusBlock + ); + } + } + } + + DEBUG ((EFI_D_INFO, "Enabled S.M.A.R.T feature at [%a] channel [%a] device!\n", + (Channel == 1) ? "secondary" : "primary", (Device == 1) ? "slave" : "master")); + + } + + return ; +} + /** Sends out an ATA Identify Command to the specified device. @@ -2333,6 +2497,19 @@ DetectAndConfigIdeDevice ( (IdeChannel == 1) ? "secondary" : "primary ", (IdeDevice == 1) ? "slave " : "master", DeviceType == EfiIdeCdrom ? "cdrom " : "harddisk")); + // + // If the device is a hard disk, then try to enable S.M.A.R.T feature + // + if (DeviceType == EfiIdeHarddisk) { + IdeAtaSmartSupport ( + Instance, + IdeChannel, + IdeDevice, + &Buffer, + NULL + ); + } + // // Submit identify data to IDE controller init driver // -- cgit v1.2.3