alder_lake_bios/Insyde/InsydeModulePkg/Universal/StatusCode/SmartStatusCodeDxe/SmartStatusCodeDxe.c

870 lines
26 KiB
C

/** @file
BootService DXE driver for SMART Report Status Code
;******************************************************************************
;* Copyright (c) 2020 - 2021, Insyde Software Corp. All Rights Reserved.
;*
;* You may not reproduce, distribute, publish, display, perform, modify, adapt,
;* transmit, broadcast, present, recite, release, license or otherwise exploit
;* any part of this publication in any form, by any means, without the prior
;* written permission of Insyde Software Corporation.
;*
;******************************************************************************
*/
#include <Protocol/AtaPassThru.h>
#include <Protocol/NvmExpressPassthru.h>
#include <Protocol/DevicePath.h>
#include <Protocol/BlockIo.h>
#include <Protocol/PciIo.h>
#include <Guid/EventGroup.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/ReportStatusCodeLib.h>
#include <Library/PrintLib.h>
#include <Library/PcdLib.h>
#include <Library/UefiLib.h>
#include <Library/UefiDriverEntryPoint.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/DevicePathLib.h>
#include <Library/H2OCpLib.h>
#include <Library/H2OLib.h>
#include <IndustryStandard/Atapi.h>
#include <IndustryStandard/Nvme.h>
STATIC NVME_SMART_HEALTH_INFO_LOG mNvmeHealthBuffer;
STATIC EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET mNvmeCommandPacket;
STATIC EFI_NVM_EXPRESS_COMMAND mNvmeCommand;
STATIC EFI_NVM_EXPRESS_COMPLETION mNvmeCompletion;
STATIC NVME_ADMIN_CONTROLLER_DATA mNvmeControllerDataBuffer;
STATIC EFI_ATA_COMMAND_BLOCK mAcb;
STATIC EFI_ATA_STATUS_BLOCK mAsb;
STATIC ATA_IDENTIFY_DATA mIdentifyData;
STATIC EFI_ATA_PASS_THRU_COMMAND_PACKET mAtaPassThruCmdPacket;
#define SMART_STATUS_CHECKPOINT_NOTIFY_TPL H2O_CP_MEDIUM_LOW
#define ATA_IDENTIFY_CMD 0xEC
#define NVME_ADMIN_QUEUE 0x00
#define NVME_GENERIC_TIMEOUT EFI_TIMER_PERIOD_SECONDS (5)
#define NVME_CONTROLLER_ID 0
#define NVME_ALL_NAMESPACES 0xFFFFFFFF
/**
Find NVMe namespaceid from devicepath
@param[in] DevicePath NVMe devicepath
@param[out] Nsid NamespaceId
@retval EFI_SUCCESS Find the namespaceid
@retval Others Failured
**/
BOOLEAN
FindNvmeNsid (
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
OUT UINT32 *Nsid
)
{
NVME_NAMESPACE_DEVICE_PATH *NVMeDevPath;
while (!IsDevicePathEnd (DevicePath)) {
if (DevicePath->Type == MESSAGING_DEVICE_PATH && DevicePath->SubType == MSG_NVME_NAMESPACE_DP) {
NVMeDevPath = (NVME_NAMESPACE_DEVICE_PATH *) DevicePath;
*Nsid = (UINT8)NVMeDevPath->NamespaceId;
return TRUE;
}
DevicePath = NextDevicePathNode (DevicePath);
}
return FALSE;
}
/**
Find SATA port from device path
@param[in] DevicePath SATA device path
@param[out] Port SATA port number
@param[out] PortMultiplier SATA portmultiplier number
@retval EFI_SUCCESS find the SATA device path
@retval EFI_NOT_FOUND Cannot find SATA device path
**/
BOOLEAN
FindSataDevicePort (
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
OUT UINT16 *Port,
OUT UINT16 *PortMultiplier
)
{
SATA_DEVICE_PATH *SataDevPath;
while (!IsDevicePathEnd (DevicePath)) {
if (DevicePath->Type == MESSAGING_DEVICE_PATH && DevicePath->SubType == MSG_SATA_DP) {
SataDevPath = (SATA_DEVICE_PATH *) DevicePath;
*Port = SataDevPath->HBAPortNumber;
*PortMultiplier = SataDevPath->PortMultiplierPortNumber;
return TRUE;
}
DevicePath = NextDevicePathNode (DevicePath);
}
return FALSE;
}
/**
Check if the device is a RAID volume
@param[in] DevicePath SATA device path
@retval TRUE It is a RAID volume
@retval FALSE Not a RAID volume
**/
BOOLEAN
IsRaidVolume (
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
)
{
UINT32 NsidMap;
UINT16 PortMultiplier;
UINT16 PortMap;
UINT16 Count;
if (FindSataDevicePort (DevicePath, &PortMap, &PortMultiplier)) {
for (Count = 0;PortMap != 0; PortMap >>= 1) {
if (PortMap & BIT0) {
Count++;
}
}
if (Count > 1) {
return TRUE;
}
}
if (FindNvmeNsid (DevicePath, &NsidMap)) {
for (Count = 0; NsidMap != 0; NsidMap >>= 1) {
if (NsidMap & BIT0) {
Count++;
}
}
if (Count > 1) {
return TRUE;
}
}
return FALSE;
}
/**
Check if the controller is a Intel Raid controller
@param[in] DevicePath SATA device path
@retval TRUE It is a RAID volume
@retval FALSE Not a RAID volume
**/
BOOLEAN
IsIntelRaidController (
IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath
)
{
EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
EFI_STATUS AtaPassthruStatus;
EFI_STATUS NvmePassthruStatus;
EFI_STATUS PciIoStatus;
EFI_HANDLE ControllerHandle;
TempDevicePath = ParentDevicePath;
AtaPassthruStatus = gBS->LocateDevicePath(&gEfiAtaPassThruProtocolGuid, &TempDevicePath, &ControllerHandle);
TempDevicePath = ParentDevicePath;
NvmePassthruStatus = gBS->LocateDevicePath(&gEfiNvmExpressPassThruProtocolGuid, &TempDevicePath, &ControllerHandle);
TempDevicePath = ParentDevicePath;
PciIoStatus = gBS->LocateDevicePath(&gEfiPciIoProtocolGuid, &TempDevicePath, &ControllerHandle);
if (!EFI_ERROR(PciIoStatus) && !EFI_ERROR(NvmePassthruStatus) && !EFI_ERROR(AtaPassthruStatus)) {
return TRUE;
}
return FALSE;
}
/**
compare device path between controller and device
@param[in] ParentDevicePath parent controller's device path
@param[in] DevicePath Raid device path
@param[in] SourceNsid Nsid extract from passthru for compare
@param[in] SourcePort SATA port extract from passthru for compare
@retval TRUE It is a device belong to the controller
@retval FALSE it belongs to different controller
**/
BOOLEAN
CompareDp (
IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath,
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
IN UINT32 SourceNsid,
IN UINT16 SourcePort
)
{
UINT32 Nsid;
UINT32 NsidMap;
UINT16 Port;
UINT16 PortMultiplier;
UINT16 PortMap;
//
// Check if it is the right controller first
//
if (CompareMem(DevicePath, ParentDevicePath, GetDevicePathSize(ParentDevicePath) - END_DEVICE_PATH_LENGTH)){
return FALSE;
}
//
// Check if it is the right device
//
if (FindSataDevicePort (DevicePath, &Port, &PortMultiplier)) {
if (!IsIntelRaidController (ParentDevicePath)) {
if (SourcePort == Port) {
return TRUE;
}
}
//
// treat port that extract from devicepath as bit map
//
PortMap = Port;
for (Port = 0; PortMap != 0; Port++, PortMap >>= 1);
Port--;
if (SourcePort == Port) {
return TRUE;
}
}
if (FindNvmeNsid (DevicePath, &Nsid)) {
if (!IsIntelRaidController (ParentDevicePath)) {
if (SourceNsid == Nsid) {
return TRUE;
}
}
//
// treat Nsid that extract from devicepath as bit map
//
NsidMap= Nsid;
for (Nsid = 0; NsidMap != 0; Nsid++, NsidMap >>= 1);
if (SourceNsid == Nsid) {
return TRUE;
}
}
return FALSE;
}
/**
Get NVMe SMART log page attribute from controller identify structure
@param[in] NvmeDevice Nvmexpresspassthru
@param[in] Nsid NnamespaceId
@param[out] Lpa Log page attribute
@param[out] IsNsidValid Check if Nsid parameter is usde as Nsid
@retval EFI_SUCCESS Get the attribute successfully.
@retval Others Failure to get the attribute.
**/
EFI_STATUS
EFIAPI
NvmeGetLogAttribute (
IN EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL *NvmeDevice,
IN UINT32 Nsid,
OUT UINT8 *Lpa,
OUT BOOLEAN *IsNsidFromDevicePathValid
)
{
EFI_STATUS Status;
ZeroMem (&mNvmeCommandPacket, sizeof(EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));
ZeroMem (&mNvmeCommand, sizeof(EFI_NVM_EXPRESS_COMMAND));
ZeroMem (&mNvmeCompletion, sizeof(EFI_NVM_EXPRESS_COMPLETION));
ZeroMem (&mNvmeControllerDataBuffer, sizeof(NVME_ADMIN_CONTROLLER_DATA));
mNvmeCommand.Cdw0.Opcode = NVME_ADMIN_IDENTIFY_CMD;
//
// According to Nvm Express 1.1 spec Figure 38, When not used, the field shall be cleared to 0h.
// For the Identify command, the Namespace Identifier is only used for the Namespace data structure.
//
mNvmeCommand.Nsid = 0;
mNvmeCommandPacket.NvmeCmd = &mNvmeCommand;
mNvmeCommandPacket.NvmeCompletion = &mNvmeCompletion;
mNvmeCommandPacket.TransferBuffer = (VOID*)&mNvmeControllerDataBuffer;
mNvmeCommandPacket.TransferLength = sizeof (NVME_ADMIN_CONTROLLER_DATA);
mNvmeCommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;
mNvmeCommandPacket.QueueType = NVME_ADMIN_QUEUE;
//
// Set bit 0 (Cns bit) to 1 to identify a controller
//
mNvmeCommand.Cdw10 = 1;
mNvmeCommand.Flags = CDW10_VALID;
*IsNsidFromDevicePathValid = TRUE;
Status = NvmeDevice->PassThru (
NvmeDevice,
0,
&mNvmeCommandPacket,
NULL
);
if (EFI_ERROR(Status)) {
//
// namespace used as controller index here
//
Status = NvmeDevice->PassThru (
NvmeDevice,
Nsid,
&mNvmeCommandPacket,
NULL
);
if (EFI_ERROR(Status)) {
return EFI_UNSUPPORTED;
}
*IsNsidFromDevicePathValid = FALSE;
}
if (!EFI_ERROR(Status)) {
*Lpa = mNvmeControllerDataBuffer.Lpa;
return Status;
}
return Status;
}
/**
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 port multiplier port number.
@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.
and underthreshold
@retval EFI_UNSUPPORTED Can't determine the error, report SMART not support
@retval EFI_DEVICE_ERROR Overthreshold warning happened
**/
EFI_STATUS
EFIAPI
NvmeSmartReturnStatusCheck (
IN EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL *NvmeDevice,
IN UINT32 Nsid,
IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath
)
{
EFI_STATUS Status;
UINT8 Lpa;
NVME_SMART_HEALTH_INFO_LOG *tempLog;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
EFI_HANDLE *HandleBuffer;
UINTN HandleCount;
UINTN HandleIndex;
BOOLEAN IsNsidFromDevicePathValid;
Lpa = 0;
DevicePath = NULL;
Status = NvmeGetLogAttribute(NvmeDevice, Nsid, &Lpa, &IsNsidFromDevicePathValid);
if (EFI_ERROR(Status)) {
return EFI_NOT_FOUND;
}
ZeroMem (&mNvmeCommandPacket, sizeof(EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));
ZeroMem (&mNvmeCommand, sizeof(EFI_NVM_EXPRESS_COMMAND));
ZeroMem (&mNvmeCompletion, sizeof(EFI_NVM_EXPRESS_COMPLETION));
ZeroMem (&mNvmeHealthBuffer, sizeof (NVME_SMART_HEALTH_INFO_LOG));
mNvmeCommand.Cdw0.Opcode = NVME_ADMIN_GET_LOG_PAGE_CMD;
//
// According to Nvm Express 1.1 spec Figure 38, When not used, the field shall be cleared to 0h.
// For the Identify command, the Namespace Identifier is only used for the Namespace data structure.
//
mNvmeCommand.Nsid = (Lpa & BIT0) ? Nsid : 0xffffffff;
mNvmeCommandPacket.NvmeCmd = &mNvmeCommand;
mNvmeCommandPacket.NvmeCompletion = &mNvmeCompletion;
mNvmeCommandPacket.TransferBuffer = (VOID*)&mNvmeHealthBuffer;
mNvmeCommandPacket.TransferLength = sizeof (NVME_SMART_HEALTH_INFO_LOG);
mNvmeCommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;
mNvmeCommandPacket.QueueType = NVME_ADMIN_QUEUE;
//
// Set log identifier to 2 for SMART/Health information
//
mNvmeCommand.Cdw10 = 0x02 | (((sizeof (NVME_SMART_HEALTH_INFO_LOG) / 4) - 1) << 16);
mNvmeCommand.Flags = CDW10_VALID;
if (IsNsidFromDevicePathValid) {
Status = NvmeDevice->PassThru (
NvmeDevice,
mNvmeCommand.Nsid,
&mNvmeCommandPacket,
NULL
);
if (EFI_ERROR(Status)) {
return EFI_UNSUPPORTED;
}
} else {
//
// namespace used as controller index here
//
Status = NvmeDevice->PassThru (
NvmeDevice,
Nsid,
&mNvmeCommandPacket,
NULL
);
}
if (EFI_ERROR(Status)) {
return EFI_UNSUPPORTED;
}
DEBUG ((DEBUG_INFO, "NVME SMART command with mNVMeCommand.Nsid:%r\n", Status));
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiBlockIoProtocolGuid,
NULL,
&HandleCount,
&HandleBuffer
);
if (EFI_ERROR(Status)) {
return EFI_UNSUPPORTED;
}
for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) {
Status = gBS->HandleProtocol (
HandleBuffer[HandleIndex],
&gEfiDevicePathProtocolGuid,
(VOID **)&DevicePath
);
if (EFI_ERROR(Status)) {
continue;
}
if (IsIntelRaidController(ParentDevicePath) && IsRaidVolume (DevicePath)) {
continue;
}
if (CompareDp (ParentDevicePath, DevicePath, Nsid, 0xffff)) {
break;
}
}
if (HandleIndex == HandleCount){
return EFI_NOT_FOUND;
}
if (EFI_ERROR(Status)) {
REPORT_STATUS_CODE_WITH_DEVICE_PATH (
EFI_PROGRESS_CODE,
(EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_NOTSUPPORTED),
DevicePath
);
return EFI_UNSUPPORTED;
}
tempLog = &mNvmeHealthBuffer;
if (
tempLog->CriticalWarningAvailableSpare ||
tempLog->CriticalWarningTemperature ||
tempLog->CriticalWarningReliability ||
tempLog->CriticalWarningMediaReadOnly ||
tempLog->CriticalWarningVolatileBackup
) {
REPORT_STATUS_CODE_WITH_DEVICE_PATH (
EFI_PROGRESS_CODE,
(EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_OVERTHRESHOLD),
DevicePath
);
return EFI_DEVICE_ERROR;
}
REPORT_STATUS_CODE_WITH_DEVICE_PATH (
EFI_PROGRESS_CODE,
(EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_UNDERTHRESHOLD),
DevicePath
);
return EFI_SUCCESS;
}
/**
Send SMART Return Status command to check if the execution of SMART cmd is successful or not.
@param AtaPassThru Atapassthru instance
@param Port The number of port.
@param PortMultiplier The port multiplier port number.
@param DevicePath devicepath of the controller
@retval EFI_SUCCESS Successfully get the return status of S.M.A.R.T command execution.
and underthreshold
@retval EFI_UNSUPPORTED Can't determine the error, report SMART not support
@retval EFI_DEVICE_ERROR Overthreshold warning happened
@retval EFI_INVALID_PARAMETER Support SMART but disabled
**/
EFI_STATUS
EFIAPI
AtaSmartReturnStatusCheck (
IN EFI_ATA_PASS_THRU_PROTOCOL *AtaPassThru,
IN UINT16 Port,
IN UINT16 PortMultiplier,
IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath
)
{
EFI_STATUS Status;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
ATA_IDENTIFY_DATA *IdentifyData;
EFI_ATA_PASS_THRU_COMMAND_PACKET *AtaPassThruCmdPacket;
EFI_HANDLE *HandleBuffer;
UINTN HandleCount;
UINTN HandleIndex;
ZeroMem (&mAcb, sizeof (EFI_ATA_COMMAND_BLOCK));
ZeroMem (&mAtaPassThruCmdPacket, sizeof (EFI_ATA_PASS_THRU_COMMAND_PACKET));
ZeroMem (&mAsb, sizeof (EFI_ATA_STATUS_BLOCK));
mAtaPassThruCmdPacket.Acb = &mAcb;
mAtaPassThruCmdPacket.Asb = &mAsb;
IdentifyData = &mIdentifyData;
AtaPassThruCmdPacket = &mAtaPassThruCmdPacket;
mAtaPassThruCmdPacket.Acb->AtaCommand = ATA_IDENTIFY_CMD;
mAtaPassThruCmdPacket.InDataBuffer = (VOID *)&mIdentifyData;
mAtaPassThruCmdPacket.InTransferLength = sizeof(ATA_IDENTIFY_DATA);
mAtaPassThruCmdPacket.Protocol = EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_IN;
mAtaPassThruCmdPacket.Length = EFI_ATA_PASS_THRU_LENGTH_BYTES;
mAtaPassThruCmdPacket.Timeout = EFI_TIMER_PERIOD_SECONDS (3);
Status = AtaPassThru->PassThru (
AtaPassThru,
Port,
PortMultiplier,
&mAtaPassThruCmdPacket,
NULL
);
if (EFI_ERROR(Status)) {
return RETURN_ABORTED;
}
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiBlockIoProtocolGuid,
NULL,
&HandleCount,
&HandleBuffer
);
if (EFI_ERROR(Status)) {
return EFI_UNSUPPORTED;
}
//
// Find devicepath
//
for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) {
Status = gBS->HandleProtocol (
HandleBuffer[HandleIndex],
&gEfiDevicePathProtocolGuid,
(VOID **)&DevicePath
);
if (EFI_ERROR(Status)) {
continue;
}
//
// VMD RAID volume is not supported
//
if (IsIntelRaidController(ParentDevicePath) && IsRaidVolume (DevicePath)) {
continue;
}
if (CompareDp (ParentDevicePath, DevicePath, 0xffffffff, Port)) {
break;
}
}
if (HandleIndex == HandleCount){
return EFI_NOT_FOUND;
}
if (!(mIdentifyData.command_set_supported_82 & BIT0)) {
REPORT_STATUS_CODE_WITH_DEVICE_PATH (
EFI_PROGRESS_CODE,
(EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_NOTSUPPORTED),
DevicePath
);
if (!(mIdentifyData.command_set_feature_enb_85 & BIT0)) {
REPORT_STATUS_CODE_WITH_DEVICE_PATH (
EFI_PROGRESS_CODE,
(EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_DISABLE),
DevicePath
);
return EFI_INVALID_PARAMETER;
}
return EFI_UNSUPPORTED;
}
mAcb.AtaCommand = ATA_CMD_SMART;
mAcb.AtaFeatures = ATA_SMART_RETURN_STATUS;
mAcb.AtaCylinderLow = ATA_CONSTANT_4F;
mAcb.AtaCylinderHigh = ATA_CONSTANT_C2;
mAtaPassThruCmdPacket.Protocol = EFI_ATA_PASS_THRU_PROTOCOL_ATA_NON_DATA;
mAtaPassThruCmdPacket.Timeout = EFI_TIMER_PERIOD_SECONDS (3);
mAtaPassThruCmdPacket.Acb = &mAcb;
mAtaPassThruCmdPacket.Asb = &mAsb;
Status = AtaPassThru->PassThru (
AtaPassThru,
Port,
PortMultiplier,
&mAtaPassThruCmdPacket,
NULL
);
if (!EFI_ERROR (Status) &&
(mAsb.AtaCylinderLow == 0xf4) && (mAsb.AtaCylinderHigh == 0x2c)) {
REPORT_STATUS_CODE_WITH_DEVICE_PATH (
EFI_PROGRESS_CODE,
(EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_OVERTHRESHOLD),
DevicePath
);
return EFI_DEVICE_ERROR;
}
REPORT_STATUS_CODE_WITH_DEVICE_PATH (
EFI_PROGRESS_CODE,
(EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_UNDERTHRESHOLD),
DevicePath
);
return EFI_SUCCESS;
}
/**
Check ATA SMART error and report status
**/
VOID
CheckAtaSmartErrorAndReportStatus (
VOID
)
{
EFI_STATUS Status;
UINTN HandleNum;
EFI_HANDLE *AtaPassThruHandles;
UINT8 Index;
EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
EFI_ATA_PASS_THRU_PROTOCOL *AtaDevice;
UINT16 Port;
UINT16 PortMultiplierPort;
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiAtaPassThruProtocolGuid,
NULL,
&HandleNum,
&AtaPassThruHandles
);
if (EFI_ERROR (Status)) {
return;
}
for (Index = 0; Index < HandleNum; Index++) {
Status = gBS->HandleProtocol (
AtaPassThruHandles[Index],
&gEfiAtaPassThruProtocolGuid,
(VOID **) &AtaDevice
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_INFO, "gBS->HandleProtocol : Status=%r\n", Status));
continue;
}
Status = gBS->HandleProtocol (
AtaPassThruHandles[Index],
&gEfiDevicePathProtocolGuid,
(VOID **) &ParentDevicePath
);
//
//Go through all of the ports and portmultiplierports and try SE if a device is present
//
Port = 0xFFFF;
while (TRUE) {
Status = AtaDevice->GetNextPort (AtaDevice, &Port);
if (EFI_ERROR (Status)) {
break;
}
PortMultiplierPort = 0xFFFF;
while (TRUE) {
Status = AtaDevice->GetNextDevice (AtaDevice, Port, &PortMultiplierPort);
if (EFI_ERROR (Status)) {
break;
}
Status = AtaSmartReturnStatusCheck(AtaDevice, Port, PortMultiplierPort, ParentDevicePath);
}
}
}
H2OFreePool((VOID**)&AtaPassThruHandles);
}
/**
Check ATA SMART error and report status
**/
VOID
CheckNvmeSmartErrorAndReportStatus (
VOID
)
{
EFI_STATUS Status;
UINTN HandleNum;
EFI_HANDLE *NvmePassThruHandles;
UINTN Index;
EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL *NvmeDevice;
UINT32 NamespaceId;
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiNvmExpressPassThruProtocolGuid,
NULL,
&HandleNum,
&NvmePassThruHandles
);
if (EFI_ERROR (Status)) {
return;
}
for (Index = 0; Index < HandleNum; Index++) {
Status = gBS->HandleProtocol (
NvmePassThruHandles[Index],
&gEfiNvmExpressPassThruProtocolGuid,
(VOID **) &NvmeDevice
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_INFO, "gBS->HandleProtocol: Status=%r\n", Status));
continue;
}
Status = gBS->HandleProtocol (
NvmePassThruHandles[Index],
&gEfiDevicePathProtocolGuid,
(VOID **) &ParentDevicePath
);
if (EFI_ERROR(Status)) {
continue;
}
NamespaceId = NVME_ALL_NAMESPACES;
while (TRUE) {
Status = NvmeDevice->GetNextNamespace (NvmeDevice, &NamespaceId);
if (EFI_ERROR (Status)) {
break;
}
Status = NvmeSmartReturnStatusCheck(NvmeDevice, NamespaceId, ParentDevicePath);
}
}
H2OFreePool((VOID**)&NvmePassThruHandles);
}
/**
Check SMART status and report status for NVMe and SATA
@param[in] Event Pointer to this event
@param[in] Handle The handle associated with a previously registered checkpoint handler.
**/
VOID
H2OBdsCpBootDeviceSelectCallback (
IN EFI_EVENT Event,
IN H2O_CP_HANDLE Handle
)
{
H2OCpUnregisterHandler (Handle);
CheckAtaSmartErrorAndReportStatus ();
CheckNvmeSmartErrorAndReportStatus ();
}
/**
Entry point of Smart Status Code Driver.
This function is the entry point of this DXE Status Code Driver.
It initializes registers status code handlers, and registers event for EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE.
@param ImageHandle The firmware allocated handle for the EFI image.
@param SystemTable A pointer to the EFI System Table.
@retval EFI_SUCCESS The entry point is executed successfully.
**/
EFI_STATUS
EFIAPI
SmartStatusCodeDxeEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
if (FeaturePcdGet (PcdH2OBdsCpBootDeviceSelectSupported)) {
H2O_CP_HANDLE H2OBdsCpBootDeviceSelectHandle;
//
// Register for callback on Storage Security Protocol publication
//
H2OBdsCpBootDeviceSelectHandle = NULL;
Status = H2OCpRegisterHandler (
&gH2OBdsCpBootDeviceSelectGuid,
H2OBdsCpBootDeviceSelectCallback,
SMART_STATUS_CHECKPOINT_NOTIFY_TPL,
&H2OBdsCpBootDeviceSelectHandle
);
if (EFI_ERROR (Status)) {
DEBUG_CP ((DEBUG_ERROR, "Checkpoint Register Fail: %g (%r)\n", &gH2OBdsCpBootDeviceSelectGuid, Status));
return Status;
}
DEBUG_CP ((DEBUG_INFO, "Checkpoint Registered: %g (%r)\n", &gH2OBdsCpBootDeviceSelectGuid, Status));
}
return EFI_SUCCESS;
}