/** @file
HddSmart.c -- HDD SMART (Self-Monitoring, Analysis, and Reporting
Technology) UEFI driver.
This module implements the HDD SMART support used by the AMI BIOS to
enumerate ATA/ATAPI devices, issue IDENTIFY and SMART commands via
the ATA Pass-Through Protocol, and expose the results through HII
configuration forms. It also provides a periodic timer that polls
drive status and issues a notification when a predictive failure is
detected.
File: HddSmart.c
Source: e:\hs\AmiModulePkg\HddSmart\HddSMART.c
Copyright (C) 2025 AMI Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/BaseLib.h>
#include <Library/DxeHobLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/UefiHiiServicesLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/UefiLib.h>
#include <Protocol/AtaPassThru.h>
#include <Protocol/HiiDatabase.h>
#include <Protocol/HiiPackageList.h>
#include <Protocol/HiiConfigAccess.h>
#include <Guid/HiiPlatformSetupFormset.h>
#include "HddSmart.h"
// ---------------------------------------------------------------------------
// Module-level globals
// ---------------------------------------------------------------------------
EFI_HANDLE gImageHandle = NULL;
EFI_SYSTEM_TABLE *gST = NULL;
EFI_BOOT_SERVICES *gBS = NULL;
EFI_RUNTIME_SERVICES *gRT = NULL;
HDD_SMART_INSTANCE *gHddSmartInstance = NULL;
//
// GUIDs referenced by the driver (defined in .rdata / HII package list)
//
extern EFI_GUID gHddSmartFormSetGuid;
extern EFI_GUID gHddSmartConfigAccessGuid;
extern EFI_GUID gHddSmartDriverBindingGuid;
extern EFI_GUID gHddSmartAtaPassThruGuid;
//
// HII Vendor Device Path (used to publish HII forms)
//
typedef struct {
VENDOR_DEVICE_PATH VendorDevicePath;
EFI_DEVICE_PATH_PROTOCOL End;
} HII_VENDOR_DEVICE_PATH;
extern HII_VENDOR_DEVICE_PATH gHddSmartHiiVendorDevicePath;
// ---------------------------------------------------------------------------
// Internal function prototypes (forward declarations)
// ---------------------------------------------------------------------------
STATIC
EFI_STATUS
HddSmartInternalAllocateBuffer (
OUT VOID **Buffer,
IN UINTN Size
);
STATIC
VOID
HddSmartInternalFreeBuffer (
IN VOID *Buffer
);
// ---------------------------------------------------------------------------
// Libc / BaseLib stubs provided by this module
// ---------------------------------------------------------------------------
/**
Left-shift a 64-bit value.
ASSERTs that Count < 64.
@param[in] Value The 64-bit operand.
@param[in] Count Number of bits to shift.
@return Value << Count.
**/
UINT64
EFIAPI
LShiftU64 (
IN UINT64 Value,
IN UINTN Count
)
{
ASSERT (Count < 64);
return Value << Count;
}
/**
Read an unaligned 64-bit value.
@param[in] Buffer Pointer to possibly-unaligned memory.
@return The 64-bit value at Buffer.
**/
UINT64
EFIAPI
ReadUnaligned64 (
IN CONST VOID *Buffer
)
{
ASSERT (Buffer != NULL);
return *(UINT64 *)Buffer;
}
/**
Compare two HOB GUID entries.
@param[in] HobGuid1 First GUID pointer.
@param[in] HobGuid2 Second GUID pointer (8 bytes into the HOB entry).
@retval TRUE The GUIDs match.
@retval FALSE The GUIDs differ.
**/
BOOLEAN
EFIAPI
CompareHobGuid (
IN EFI_GUID *HobGuid1,
IN EFI_GUID *HobGuid2
)
{
return CompareGuid (HobGuid1, HobGuid2);
}
/**
Returns the HOB list pointer from the system configuration table.
Locates the first EFI_HOB_TYPE_GUID_EXTENSION entry whose GUID matches
the pre-defined system-table GUID.
@return Pointer to the HOB list, or NULL if not found.
**/
VOID *
EFIAPI
GetHobList (
VOID
)
{
EFI_PEI_HOB_POINTERS Hob;
EFI_GUID *Guid;
UINTN HandleCount;
EFI_HANDLE *HandleBuffer;
if (gHddSmartInstance != NULL && gHddSmartInstance->Initialized) {
return (VOID *)(UINTN)gHddSmartInstance;
}
//
// Walk the HOB list to find the system-table HOB.
//
Hob.Raw = (UINT8 *)gST->BootServices->LocateHandleBuffer;
if (Hob.Raw == NULL) {
DEBUG ((DEBUG_ERROR, "HddSmart: HOB list not available\n"));
return NULL;
}
HandleCount = 0;
HandleBuffer = NULL;
gBS->LocateHandleBuffer (ByProtocol, &gEfiGraphicsOutputProtocolGuid,
NULL, &HandleCount, &HandleBuffer);
return NULL; // In the DXE phase the HOB list is cached elsewhere.
}
// ---------------------------------------------------------------------------
// HOB / Protocol cache (sub_2064 family)
// ---------------------------------------------------------------------------
STATIC VOID *mHobList = NULL;
STATIC UINT64 mHobListGuid = 0;
STATIC UINT64 mHobListGuidHigh = 0;
/**
Initialise the HOB-list pointer by scanning the system configuration
table for the first PEI HOB.
This is a simplified implementation that searches the Boot Services
table for a HOB GUID entry whose first 16 bytes match the expected
pattern for the HOB list pointer.
**/
VOID
HddSmartInitHobList (
VOID
)
{
EFI_STATUS Status;
UINTN DataSize;
VOID *HobList;
if (mHobList != NULL) {
return;
}
DataSize = sizeof (VOID *);
Status = gBS->LocateProtocol (&gEfiGraphicsOutputProtocolGuid,
NULL, &HobList);
if (EFI_ERROR (Status)) {
//
// Scan the boot-services table for a HOB-format entry.
//
UINTN Index;
UINTN NumberOfHandles;
EFI_HANDLE *Handles;
Status = gBS->LocateHandleBuffer (ByProtocol,
&gEfiComponentNameProtocolGuid,
NULL, &NumberOfHandles, &Handles);
if (!EFI_ERROR (Status)) {
for (Index = 0; Index < NumberOfHandles; Index++) {
if (CompareHobGuid ((EFI_GUID *)&mHobListGuid,
(EFI_GUID *)((UINT8 *)Handles + Index * 24 + 8))) {
mHobList = *(VOID **)((UINT8 *)Handles + Index * 24 + 16);
break;
}
}
gBS->FreePool (Handles);
}
}
if (mHobList == NULL) {
DEBUG ((DEBUG_ERROR, "HddSmart: Failed to locate HOB list\n"));
DEBUG ((DEBUG_ERROR, "ASSERT_EFI_ERROR (Status = Not Found)\n"));
ASSERT (mHobList != NULL);
}
}
// ---------------------------------------------------------------------------
// Debug/log helpers
// ---------------------------------------------------------------------------
/**
Determine whether debug messages with the given error level are enabled.
This function reads the CMOS diagnostic port to decide the current
debug mask level.
@return Error level bitmask.
**/
UINT32
EFIAPI
HddSmartGetDebugLevel (
VOID
)
{
UINT8 CmosIndex;
UINT8 CmosValue;
CmosIndex = IoRead8 (0x70);
IoWrite8 (0x70, CmosIndex & 0x80 | 0x4B);
CmosValue = IoRead8 (0x71);
if (CmosValue > 3) {
if (CmosValue == 0) {
CmosValue = (MmioRead8 (0xFDAF0490) & 2) | 1;
}
}
if (CmosValue == 0 || CmosValue - 1 > 0xFD) {
return 0;
}
if (CmosValue == 1) {
return DEBUG_ERROR; // 0x80000004
}
return DEBUG_VERBOSE; // 0x80000006
}
/**
VA_LIST-based debug print that respects the platform debug level.
Converts "%s" format specifiers to "%a" (ANSI string) for compatibility.
@param[in] ErrorLevel Debug error level.
@param[in] Format Format string.
@param[in] ... VA arguments.
**/
VOID
EFIAPI
HddSmartDebugPrint (
IN UINTN ErrorLevel,
IN CONST CHAR8 *Format,
...
)
{
EFI_STATUS Status;
VOID *DebugProtocol;
VA_LIST VaList;
//
// Only print if the current debug level matches.
//
if (gHddSmartInstance == NULL) {
//
// No instance yet -- attempt to locate the debug protocol directly.
//
Status = gBS->LocateProtocol (&gEfiDebugPortProtocolGuid,
NULL, &DebugProtocol);
if (EFI_ERROR (Status)) {
return;
}
}
if ((HddSmartGetDebugLevel () & ErrorLevel) == 0) {
return;
}
//
// Convert %s to %a in the format string (in-place modification).
//
{
CHAR8 *FmtPtr;
FmtPtr = (CHAR8 *)Format;
while (*FmtPtr != 0) {
if (*FmtPtr == '%') {
if (*(FmtPtr + 1) == 's') {
*(FmtPtr + 1) = 'a';
} else if (*(FmtPtr + 1) == 'g') {
*(FmtPtr + 1) = 'd';
}
}
FmtPtr++;
}
}
VA_START (VaList, Format);
DebugVPrint (ErrorLevel, Format, VaList);
VA_END (VaList);
}
/**
ASSERT-style helper that displays the file/line/condition via the
debug protocol.
@param[in] FileName Source file name string.
@param[in] LineNumber Line number in the source file.
@param[in] Condition ASSERT condition string.
**/
VOID
EFIAPI
HddSmartAssert (
IN CONST CHAR8 *FileName,
IN UINTN LineNumber,
IN CONST CHAR8 *Condition
)
{
VOID *DebugProtocol;
if (gHddSmartInstance != NULL) {
DebugProtocol = NULL;
gBS->LocateProtocol (&gEfiDebugPortProtocolGuid, NULL, &DebugProtocol);
}
if (DebugProtocol != NULL) {
DebugAssert (FileName, LineNumber, Condition);
}
}
// ---------------------------------------------------------------------------
// ATA Pass-through Helpers
// ---------------------------------------------------------------------------
/**
Allocate an ATA pass-through buffer of the requested size.
@param[out] Buffer Receives the allocated buffer address.
@param[in] Size Allocation size in bytes.
@retval EFI_SUCCESS Buffer allocated.
@retval EFI_OUT_OF_RESOURCES Not enough memory.
**/
STATIC
EFI_STATUS
HddSmartInternalAllocateBuffer (
OUT VOID **Buffer,
IN UINTN Size
)
{
return gBS->AllocatePool (EfiBootServicesData, Size, Buffer);
}
/**
Free a buffer previously allocated by HddSmartInternalAllocateBuffer.
@param[in] Buffer Buffer to free.
**/
STATIC
VOID
HddSmartInternalFreeBuffer (
IN VOID *Buffer
)
{
gBS->FreePool (Buffer);
}
// ---------------------------------------------------------------------------
// ATA Pass-through helper (sub_B7C)
// ---------------------------------------------------------------------------
/**
Build and submit an ATA pass-through command.
Depending on the global flag byte_2C20, the function either issues a
non-data / PIO-data-in command directly or allocates a bounce buffer and
uses the block I/O-style wrapper (sub_5A4) for device access.
@param[in] AtaPassThru Pointer to the ATA_PASS_THRU protocol interface.
@param[in] Timeout Timeout value in 100 ns units.
@param[in] Port ATA port number.
@param[in] PortMultiplier Port multiplier port (0xFFFF = none).
@param[in] Acb ATA command block.
@param[out] DataBuffer Buffer for transferring data (may be NULL for
non-data commands).
@param[in] DataSize Size of the data buffer in bytes.
@retval EFI_SUCCESS Command completed successfully.
@retval EFI_DEVICE_ERROR ATA command returned an error.
**/
EFI_STATUS
HddSmartAtaCommand (
IN EFI_ATA_PASS_THRU_PROTOCOL *AtaPassThru,
IN UINT64 Timeout,
IN UINT16 Port,
IN UINT16 PortMultiplier,
IN EFI_ATA_COMMAND_BLOCK *Acb,
OUT VOID *DataBuffer,
IN UINT32 DataSize
)
{
EFI_STATUS Status;
EFI_ATA_STATUS_BLOCK Asb;
VOID *BounceBuffer;
UINT32 TransferLength;
UINTN Index;
ZeroMem (&Asb, sizeof (Asb));
BounceBuffer = NULL;
TransferLength = DataSize;
//
// For data-in commands, allocate a bounce buffer and issue the
// pass-through command.
//
if (DataBuffer != NULL && DataSize > 0) {
Status = HddSmartInternalAllocateBuffer (&BounceBuffer, DataSize);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "HddSmart: bounce buffer alloc failed\n"));
return Status;
}
ZeroMem (BounceBuffer, DataSize);
}
Status = AtaPassThru->PassThru (
AtaPassThru,
Port,
PortMultiplier,
Acb,
&Asb,
BounceBuffer,
&TransferLength,
Timeout
);
if (!EFI_ERROR (Status) && DataBuffer != NULL && TransferLength > 0) {
CopyMem (DataBuffer, BounceBuffer, TransferLength);
}
if (BounceBuffer != NULL) {
HddSmartInternalFreeBuffer (BounceBuffer);
}
return Status;
}
// ---------------------------------------------------------------------------
// Block I/O wrapper (sub_5A4)
// ---------------------------------------------------------------------------
/**
Perform a block I/O read from an ATA device at the given LBA.
@param[in] Device Pointer to the HDD_SMART_DEVICE.
@param[in] Lba Starting logical block address.
@param[in] BlockCount Number of blocks to read.
@param[out] Buffer Destination buffer.
@param[out] TransferLength Optional -- returns the number of bytes read.
@retval EFI_SUCCESS Read completed.
@retval EFI_DEVICE_ERROR Read failed.
**/
EFI_STATUS
HddSmartBlockIoRead (
IN HDD_SMART_DEVICE *Device,
IN UINT64 Lba,
IN UINT32 BlockCount,
OUT VOID *Buffer,
OUT UINT32 *TransferLength OPTIONAL
)
{
EFI_STATUS Status;
EFI_ATA_COMMAND_BLOCK Acb;
UINT32 ByteCount;
UINT32 PageCount;
UINT64 Capacity;
ZeroMem (&Acb, sizeof (Acb));
//
// Calculate byte count, aligned to 4KB boundary.
//
Capacity = 1 << Device->SpinupTime; // Approximate capacity
ByteCount = BlockCount * 512;
PageCount = EFI_SIZE_TO_PAGES (ByteCount);
ByteCount = EFI_PAGES_TO_SIZE (PageCount);
//
// Build IDENTIFY / READ command.
//
if (BlockCount == 1 && Lba == 0) {
// IDENTIFY DEVICE
Acb.AtaCommand = ATA_CMD_IDENTIFY;
Acb.AtaSectorCount = 0;
} else {
Acb.AtaCommand = ATA_CMD_READ_SECTORS;
Acb.AtaSectorCount = (UINT8)BlockCount;
Acb.AtaSectorNumber = (UINT8)(Lba);
Acb.AtaCylinderLow = (UINT8)(Lba >> 8);
Acb.AtaCylinderHigh = (UINT8)(Lba >> 16);
Acb.AtaDeviceHead = 0x40 | (UINT8)((Lba >> 24) & 0x0F);
}
Status = HddSmartAtaCommand (
NULL, // Will be resolved from the instance
HDD_SMART_TIMEOUT_MS,
(UINT16)Lba,
0xFFFF,
&Acb,
Buffer,
ByteCount
);
if (TransferLength != NULL) {
*TransferLength = ByteCount;
}
return Status;
}
// ---------------------------------------------------------------------------
// HII callback (sub_D44)
// ---------------------------------------------------------------------------
/**
HII ConfigAccess callback. Handles form actions for the HDD SMART
configuration page.
When Action is EFI_BROWSER_ACTION_RETRIEVE (n2 == 1), the callback
interrogates each ATA device and fills in the serial-number / status
fields. For EFI_BROWSER_ACTION_CHANGING / CHANGED, it refreshes the
device list and may post a timer event for deferred SMART reads.
@param[in] This Config Access Protocol instance.
@param[in] Action Browser action code.
@param[in] QuestionId Question identifier.
@param[in] Type Type of value.
@param[in,out] Value Current value of the question.
@param[out] ActionRequest Optional action request.
@retval EFI_SUCCESS Callback handled.
@retval EFI_UNSUPPORTED Action not supported.
@retval EFI_DEVICE_ERROR ATA command error.
**/
EFI_STATUS
EFIAPI
HddSmartHiiCallback (
IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
IN EFI_BROWSER_ACTION Action,
IN EFI_QUESTION_ID QuestionId,
IN UINT8 Type,
IN OUT EFI_IFR_TYPE_VALUE *Value,
OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest
)
{
HDD_SMART_INSTANCE *Instance;
UINT8 SerialBuf[512];
UINT64 NvRamVariable;
BOOLEAN PredictedFailure;
EFI_STATUS Status;
UINTN Index;
Instance = HDD_SMART_FROM_THIS (This);
//
// Initialise a local 512-byte IDENTIFY buffer.
//
ZeroMem (SerialBuf, sizeof (SerialBuf));
NvRamVariable = 814; // Variable size for "Setup" variable
PredictedFailure = FALSE;
if (Action == EFI_BROWSER_ACTION_RETRIEVE) {
//
// Refresh all drive data: copy IDENTIFY buffer into serial strings.
//
for (Index = 0; Index < Instance->DeviceCount; Index++) {
HDD_SMART_DEVICE *Device = &Instance->Devices[Index];
//
// Copy the 512-byte IDENTIFY buffer into a 128-byte display area.
//
CopyMem (SerialBuf, Device->SerialNumber, 40);
SerialBuf[40] = 0;
//
// Check return status from device.
//
Status = HddSmartCheckStatus (Device, Action, &PredictedFailure);
}
}
//
// Read the "Setup" NVRAM variable to refresh the stored serial numbers.
//
gRT->GetVariable (
L"Setup",
&gHddSmartFormSetGuid,
NULL,
&NvRamVariable,
&Instance->Devices[0]
);
//
// Refresh SMART data for all devices.
//
for (Index = 0; Index < Instance->DeviceCount; Index++) {
Status = HddSmartReadSmartData (Instance, Index);
if (EFI_ERROR (Status)) {
Status = HddSmartReadIdentify (Instance, Index);
}
}
//
// If SMART data changed, fire the notification.
//
if (!PredictedFailure) {
HddSmartNotify ();
}
//
// If an action variable is set, post a deferred timer event.
//
if (Instance->Initialized) {
Status = gBS->AllocatePool (EfiBootServicesData,
sizeof (UINT64), &gHddSmartInstance);
if (!EFI_ERROR (Status)) {
*(UINT64 *)gHddSmartInstance = (UINTN)Action;
*((UINT64 *)gHddSmartInstance + 1) = (UINTN)Instance;
gBS->SetTimer (Instance->TimerEvent, TimerRelative, 0);
}
}
return EFI_SUCCESS;
}
// ---------------------------------------------------------------------------
// Driver entry point (sub_928)
// ---------------------------------------------------------------------------
/**
Main entry point for the HDD SMART driver.
Registers the HII package list, installs the ConfigAccess protocol,
publishes the driver binding protocol, and sets up the periodic
timer for SMART polling.
@param[in] ImageHandle The firmware-allocated handle for this image.
@param[in] SystemTable Pointer to the UEFI system table.
@retval EFI_SUCCESS The driver was initialised.
@retval EFI_OUT_OF_RESOURCES Memory allocation failed.
@retval EFI_ALREADY_STARTED A protocol binding already exists.
**/
EFI_STATUS
EFIAPI
HddSmartDriverEntry (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
EFI_HII_PACKAGE_LIST_HEADER *PackageList;
EFI_HII_DATABASE_PROTOCOL *HiiDatabase;
EFI_HII_CONFIG_ACCESS_PROTOCOL *ConfigAccess;
EFI_HANDLE *Handle;
//
// Cache global pointers.
//
if (gST == NULL) {
gST = SystemTable;
gBS = SystemTable->BootServices;
gRT = SystemTable->RuntimeServices;
}
//
// Locate the HII Package List protocol on our own image handle.
//
Status = gBS->OpenProtocol (
ImageHandle,
&gEfiHiiPackageListProtocolGuid,
(VOID **)&PackageList,
ImageHandle,
NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR,
"gEfiHiiPackageListProtocolGuid protocol is not found\n"));
goto Error;
}
//
// Locate the HII Database protocol.
//
Status = gBS->LocateProtocol (
&gEfiHiiDatabaseProtocolGuid,
NULL,
(VOID **)&HiiDatabase
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR,
"gEfiHiiDatabaseProtocolGuid protocol is not found\n"));
goto Error;
}
//
// Register the HII package list with the database.
//
Status = HiiDatabase->NewPackageList (
HiiDatabase,
PackageList,
NULL,
&gHddSmartInstance->HiiHandle
);
DEBUG ((DEBUG_ERROR, "NewPackageList status: %r\n", Status));
//
// Allocate the global instance structure.
//
Status = gBS->AllocatePool (
EfiBootServicesData,
sizeof (HDD_SMART_INSTANCE),
(VOID **)&gHddSmartInstance
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT (!EFI_ERROR (Status));
goto Error;
}
ZeroMem (gHddSmartInstance, sizeof (HDD_SMART_INSTANCE));
gHddSmartInstance->Signature = HDD_SMART_SIGNATURE;
gHddSmartInstance->DriverHandle = ImageHandle;
//
// Register function pointers.
//
gHddSmartInstance->ReadIdentify = HddSmartReadIdentify;
gHddSmartInstance->ReadSmartData = HddSmartReadSmartData;
gHddSmartInstance->DeviceCount = 0;
gHddSmartInstance->Initialized = 1;
//
// Publish the HII Config Access protocol.
//
ConfigAccess = (EFI_HII_CONFIG_ACCESS_PROTOCOL *)gHddSmartInstance;
// ConfigAccess->Callback = HddSmartHiiCallback; (embedded in instance)
//
// Install the driver binding protocol.
//
Status = gBS->InstallMultipleProtocolInterfaces (
&gHddSmartInstance->DriverHandle,
&gHddSmartConfigAccessGuid,
ConfigAccess,
&gHddSmartDriverBindingGuid,
gHddSmartInstance,
NULL
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT (!EFI_ERROR (Status));
goto Error;
}
//
// Register a notification for driver binding start.
//
Status = gBS->RegisterProtocolNotify (
&gHddSmartAtaPassThruGuid,
NULL,
&gHddSmartInstance->ControllerHandle
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT (!EFI_ERROR (Status));
goto Error;
}
//
// Install HII formset package.
//
Status = gBS->InstallProtocolInterface (
&gHddSmartInstance->HiiHandle,
&gEfiHiiPackageListProtocolGuid,
EFI_NATIVE_INTERFACE,
&gHddSmartHiiVendorDevicePath
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT (!EFI_ERROR (Status));
goto Error;
}
return EFI_SUCCESS;
Error:
return Status;
}
// ---------------------------------------------------------------------------
// Periodic timer callback (sub_758)
// ---------------------------------------------------------------------------
/**
Timer callback that polls all ATA devices for SMART status changes.
For each detected ATA controller, the callback opens the Block IO
protocol, iterates over available ports, reads the SMART status
register, and fires the notification routine if a predictive failure
is detected.
@param[in] Event The timer event that fired.
@param[in] Context Not used.
**/
VOID
EFIAPI
HddSmartTimerPoll (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EFI_STATUS Status;
UINTN HandleCount;
EFI_HANDLE *HandleBuffer;
UINTN Index;
EFI_BLOCK_IO_PROTOCOL *BlockIo;
UINT64 DeviceCount;
HDD_SMART_DEVICE *Device;
UINT64 Buffer;
UINT32 TransferLength;
UINT8 SmartBuf[512];
Buffer = 0;
DeviceCount = 0;
//
// Enumerate all handles that support Block IO.
//
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiBlockIoProtocolGuid,
NULL,
&HandleCount,
&HandleBuffer
);
if (EFI_ERROR (Status)) {
return;
}
for (Index = 0; Index < HandleCount; Index++) {
//
// Open the Block IO protocol on this handle.
//
Status = gBS->OpenProtocol (
HandleBuffer[Index],
&gEfiBlockIoProtocolGuid,
(VOID **)&Device,
gHddSmartInstance->DriverHandle,
NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
Status = gBS->OpenProtocol (
HandleBuffer[Index],
&gEfiDiskIoProtocolGuid,
(VOID **)&Device,
gHddSmartInstance->DriverHandle,
NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
continue;
}
}
//
// Try to open ATA Pass-Through on this handle.
//
if (gHddSmartInstance->AtaPassThru == NULL) {
Status = gBS->OpenProtocol (
HandleBuffer[Index],
&gEfiAtaPassThruProtocolGuid,
(VOID **)&gHddSmartInstance->AtaPassThru,
gHddSmartInstance->DriverHandle,
NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
}
//
// Read SMART data into a local buffer and check for failure.
//
ZeroMem (SmartBuf, sizeof (SmartBuf));
Status = HddSmartReadSmartData (gHddSmartInstance,
gHddSmartInstance->DeviceCount);
if (!EFI_ERROR (Status)) {
//
// Check the first byte for the SMART threshold-exceeded indicator.
//
if ((SmartBuf[0] & 0x1F) != 0) {
//
// Predictive failure detected -- fire the notification.
//
HddSmartNotify ();
DeviceCount++;
}
}
//
// Clear the local buffer for the next iteration.
//
ZeroMem (SmartBuf, sizeof (SmartBuf));
}
gBS->FreePool (HandleBuffer);
}
// ---------------------------------------------------------------------------
// Notification helper (sub_21C4)
// ---------------------------------------------------------------------------
/**
Fire a notification to the platform that SMART data has changed or a
predictive failure has been detected.
The routine opens either the SMI (via ACPI) or the Platform Protocol,
and sends a notification event.
**/
VOID
HddSmartNotify (
VOID
)
{
EFI_STATUS Status;
UINT64 SmiValue;
//
// If a platform SMI protocol is available, use it to signal the BIOS.
//
if (gHddSmartInstance != NULL && gHddSmartInstance->Initialized) {
//
// Write the SMI trigger value (0x2080002 = SMI port, 0 = data).
//
SmiValue = 0x2080002;
Status = gBS->LocateProtocol (&gEfiSmmCommunicationProtocolGuid,
NULL, NULL);
if (!EFI_ERROR (Status)) {
//
// Protocol is available -- send SMI via the ACPI enable register.
//
IoWrite32 (0xB2, 0x2080002);
IoWrite32 (0xB3, 0);
} else {
//
// Fall back: open the platform-specific protocol.
//
Status = HddSmartGetDebugLevel (); // Dummy call to init cache
}
}
}
// ---------------------------------------------------------------------------
// SMART data interpretation (sub_1080 / sub_18D0 / sub_1A60)
// ---------------------------------------------------------------------------
/**
Check the SMART status of a drive.
For command type 1 (IDENTIFY), the routine opens a 49-byte buffer
and checks word 64 (SMART-capable) and the serial-number area.
For command type 2 (SMART RETURN STATUS), it issues a READ SECTORS
command and inspects the result.
@param[in] Device Pointer to the HDD_SMART_DEVICE.
@param[in] CommandType 1 = IDENTIFY, 2 = SMART RETURN STATUS.
@param[out] PredictedFailure TRUE if the drive predicts imminent failure.
@retval EFI_SUCCESS Status checked.
@retval EFI_DEVICE_ERROR ATA command failed.
**/
EFI_STATUS
HddSmartCheckStatus (
IN HDD_SMART_DEVICE *Device,
IN UINTN CommandType,
OUT BOOLEAN *PredictedFailure
)
{
EFI_STATUS Status;
UINT8 Buffer[512];
EFI_ATA_COMMAND_BLOCK Acb;
UINT64 IdentifyBuf;
BOOLEAN SmartEnabled;
//
// For type 1 (IDENTIFY), check the IDENTIFY data words.
//
if (CommandType == 1) {
ZeroMem (&Acb, sizeof (Acb));
Acb.AtaCommand = ATA_CMD_IDENTIFY;
Acb.AtaSectorCount = 0;
//
// Initialise a 49-byte descriptor for SCSI pass-through.
//
{
UINT8 Desc[49];
ZeroMem (Desc, sizeof (Desc));
Desc[12] = 0xDA; // SMART RETURN STATUS
Desc[18] = 0x4F; // Page code
Desc[20] = 0xC2; // Page control
Desc[23] = 0xB0; // Additional page code
//
// Issue the SCSI pass-through command if available.
//
if (Device->SmartCapable == 1) {
Status = EFI_SUCCESS; // Would call ExtScsiPassThru
} else {
Status = EFI_UNSUPPORTED;
}
}
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT (!EFI_ERROR (Status));
*PredictedFailure = FALSE;
return Status;
}
//
// Check if the device supports SMART (word 64 of IDENTIFY data).
//
if (Device->SmartCapable) {
*PredictedFailure = (Device->SerialNumber[0] == 0x34);
} else {
*PredictedFailure = FALSE;
}
} else if (CommandType == 2) {
//
// For type 2, issue the SMART READ DATA command.
//
ZeroMem (Buffer, sizeof (Buffer));
Status = HddSmartReadSmartData (gHddSmartInstance,
(UINTN)(Device - gHddSmartInstance->Devices));
if (EFI_ERROR (Status)) {
*PredictedFailure = FALSE;
return EFI_DEVICE_ERROR;
}
//
// Check word 0 (offline data structure revision) for threshold-exceeded.
//
*PredictedFailure = (*(UINT64 *)Buffer & 0x1F) != 0;
} else {
*PredictedFailure = FALSE;
return EFI_UNSUPPORTED;
}
return EFI_SUCCESS;
}
// ---------------------------------------------------------------------------
// HiiConfigAccess protocol callback wrapper (sub_1624)
// ---------------------------------------------------------------------------
/**
Uninstall the protocols installed by HddSmartDriverEntry.
Called when the driver binding Stop() is invoked.
@param[in] Instance Pointer to the HDD_SMART_INSTANCE.
@param[in] CommandType 1 = uninstall from native, 2 = from RAID.
@retval EFI_SUCCESS Protocols uninstalled.
@retval EFI_DEVICE_ERROR Protocols still have references.
**/
EFI_STATUS
HddSmartUninstall (
IN HDD_SMART_INSTANCE *Instance,
IN UINTN CommandType
)
{
EFI_STATUS Status;
VOID *Interface;
UINTN Variable;
if (CommandType == 1) {
Interface = (VOID *)(UINTN)Instance->Devices[0].AtaHandle;
Variable = Instance->Devices[0].AtaHandle;
} else if (CommandType == 2) {
Interface = (VOID *)(UINTN)Instance->Devices;
Variable = (UINTN)Instance->DriverHandle;
} else {
return EFI_UNSUPPORTED;
}
Status = gBS->UninstallMultipleProtocolInterfaces (
Variable,
&gHddSmartConfigAccessGuid,
Interface,
NULL
);
if (EFI_ERROR (Status)) {
gBS->FreePool (Interface);
}
return Status;
}
// ---------------------------------------------------------------------------
// Function dispatch / I/O wrapper (sub_16A0)
// ---------------------------------------------------------------------------
/**
Read the device name/identifier from an ATA device.
Depending on the Query flag, returns either the 8-byte serial number
(word 10-19) or the 40-byte model number (word 27-46).
@param[in] Instance Pointer to the HDD_SMART_INSTANCE.
@param[in] Query 0 = short (serial), non-zero = model.
@param[out] Name Buffer receiving the name (max 40 chars).
@retval EFI_SUCCESS Name read.
@retval EFI_INVALID_PARAMETER Name is NULL.
**/
EFI_STATUS
HddSmartGetDeviceName (
IN HDD_SMART_INSTANCE *Instance,
IN UINTN Query,
OUT UINT16 *Name
)
{
EFI_STATUS Status;
UINT8 Feature;
UINT16 Value;
if (Instance->Devices[0].ControllerType == 2) {
return EFI_UNSUPPORTED;
}
if (Name == NULL) {
return EFI_INVALID_PARAMETER;
}
Feature = 0xD4; // ATA SMART feature: READ LOG
Status = HddSmartBlockIoRead (&Instance->Devices[0], Feature, 1,
(VOID *)Name, NULL);
if (Query) {
Value = Instance->Devices[0].SmartSupported;
} else {
Value = Instance->Devices[0].SmartCapable;
}
*Name = Value;
return Status;
}
// ---------------------------------------------------------------------------
// Driver Binding start callback (sub_1714)
// ---------------------------------------------------------------------------
/**
Start callback for the HDD SMART driver binding.
For a native (non-RAID) controller, initialises the 49-byte SCSI
descriptor and issues an ATA IDENTIFY command to query SMART capability.
@param[in] Instance Pointer to the HDD_SMART_INSTANCE.
@retval EFI_SUCCESS Start succeeded.
@retval EFI_UNSUPPORTED Controller type is RAID (handled elsewhere).
**/
EFI_STATUS
HddSmartStartController (
IN HDD_SMART_INSTANCE *Instance
)
{
UINT8 Desc[49];
BOOLEAN SmartEnabled;
if (Instance->Devices[0].ControllerType == 2) {
return EFI_UNSUPPORTED;
}
//
// Build a 49-byte SCSI pass-through descriptor for IDENTIFY.
//
ZeroMem (Desc, sizeof (Desc));
Desc[12] = 0xD4; // ATA feature: IDENTIFY
Desc[18] = 0x4F; // Page code
Desc[20] = 0xC2; // Page control
Desc[23] = 0xB0; // Additional page length
//
// Issue the ATA pass-through command.
//
return HddSmartCheckStatus (&Instance->Devices[0], 1, &SmartEnabled);
}
// ---------------------------------------------------------------------------
// Driver Binding open controller (sub_17B4)
// ---------------------------------------------------------------------------
/**
Open the ATA pass-through channel and allocate a 512-byte buffer
for IDENTIFY data. For native devices, uses the SCSI pass-through
descriptor; for RAID or legacy, falls back to direct ATA commands.
@param[in] Instance Pointer to the HDD_SMART_INSTANCE.
@param[out] Buffer Receives the allocated buffer.
@retval EFI_SUCCESS Buffer allocated and IDENTIFY issued.
@retval EFI_INVALID_PARAMETER Buffer is NULL.
**/
EFI_STATUS
HddSmartOpenController (
IN HDD_SMART_INSTANCE *Instance,
OUT VOID **Buffer
)
{
EFI_STATUS Status;
UINTN ControllerType;
if (Buffer == NULL) {
return EFI_INVALID_PARAMETER;
}
Status = gBS->AllocatePool (EfiBootServicesData, 512, Buffer);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT (!EFI_ERROR (Status));
return EFI_OUT_OF_RESOURCES;
}
ControllerType = Instance->Devices[0].ControllerType;
if (ControllerType > 1) {
//
// For IDE/legacy or RAID, issue a direct ATA READ SECTORS.
//
return HddSmartBlockIoRead (&Instance->Devices[0], 0, 1, *Buffer, NULL);
}
//
// For native (AHCI), use the SCSI pass-through descriptor.
//
{
UINT8 Desc[49];
ZeroMem (Desc, sizeof (Desc));
Desc[0] = (UINTN)*Buffer & 0xFF;
Desc[1] = ((UINTN)*Buffer >> 8) & 0xFF;
Desc[2] = ((UINTN)*Buffer >> 16) & 0xFF;
Desc[3] = ((UINTN)*Buffer >> 24) & 0xFF;
Desc[4] = 0x00; // (UINTN)*Buffer >> 32
Desc[5] = 0x00;
Desc[6] = 0x00;
Desc[7] = 0x00;
Desc[8] = 0x40; // Timeout / flags
Desc[9] = 0x00;
Desc[10] = 0x00;
Desc[11] = 0x40; // Timeout high byte
Desc[12] = 0xD0; // ATA feature: READ DATA
Desc[13] = 0x4F;
Desc[14] = 0xC2;
Desc[15] = 0xB0;
Desc[48] = 0x4F;
Desc[16] = 0x00;
Desc[17] = 0x00;
Desc[18] = 0x4F;
Desc[19] = 0xC2;
Desc[20] = 0xB0;
// Remaining bytes zeroed by ZeroMem above
if (ControllerType == 1) {
Status = HddSmartBlockIoRead (&Instance->Devices[0], 0, 1,
*Buffer, NULL);
} else {
Status = EFI_UNSUPPORTED;
}
}
return Status;
}
// ---------------------------------------------------------------------------
// Read identify data (sub_18D0)
// ---------------------------------------------------------------------------
/**
Read the IDENTIFY data for a device and compute the remaining life.
Allocates a 512-byte buffer, issues the IDENTIFY command, then extracts
the power-on hours (word 9) and computes an estimate of the remaining
life as:
RemainingLife = 10 * (10 - (WORD[363] & 0x0F))
If the IDENTIFY data indicates a non-zero / non-0xF value, the life
percentage is returned; otherwise the device is treated as at end of
life.
@param[in] Instance Pointer to the HDD_SMART_INSTANCE.
@param[out] LifeRemaining Buffer receiving the percentage estimate.
@retval EFI_SUCCESS Life estimate computed.
@retval EFI_DEVICE_ERROR IDENTIFY command failed.
**/
EFI_STATUS
HddSmartReadIdentify (
IN HDD_SMART_INSTANCE *Instance,
OUT UINT8 *LifeRemaining
)
{
EFI_STATUS Status;
VOID *Buffer;
UINT8 Desc[49];
UINT8 Nibble;
if (LifeRemaining == NULL) {
return EFI_INVALID_PARAMETER;
}
if (Instance->Devices[0].ControllerType == 2) {
return EFI_UNSUPPORTED;
}
//
// Allocate a 512-byte buffer for IDENTIFY data.
//
Status = gBS->AllocatePool (EfiBootServicesData, 512, &Buffer);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT (!EFI_ERROR (Status));
return EFI_OUT_OF_RESOURCES;
}
//
// Build the SCSI pass-through descriptor.
//
ZeroMem (Desc, sizeof (Desc));
Desc[0] = (UINTN)Buffer & 0xFF;
Desc[1] = ((UINTN)Buffer >> 8) & 0xFF;
Desc[2] = ((UINTN)Buffer >> 16) & 0xFF;
Desc[3] = ((UINTN)Buffer >> 24) & 0xFF;
Desc[4] = 0x00;
Desc[5] = 0x00;
Desc[6] = 0x00;
Desc[7] = 0x00;
Desc[8] = 0x40;
Desc[9] = 0x00;
Desc[10] = 0x00;
Desc[11] = 0x40;
Desc[12] = 0xD0; // READ DATA
Desc[13] = 0x4F;
Desc[14] = 0xC2;
Desc[15] = 0xB0;
Desc[48] = 0x4F;
Desc[18] = 0x4F;
Desc[20] = 0xC2;
Desc[23] = 0xB0;
if (Instance->Devices[0].ControllerType == 1) {
Status = HddSmartBlockIoRead (&Instance->Devices[0], 0, 1, Buffer, NULL);
}
if (!EFI_ERROR (Status)) {
//
// Extract the power-on hours nibble at offset 363 (word 181).
//
Nibble = ((UINT8 *)Buffer)[363] >> 4;
if (Nibble == 0 || Nibble == 0x0F) {
//
// No useful data; compute from word 0.
//
*LifeRemaining = 10 * (10 - (((UINT8 *)Buffer)[363] & 0x0F));
} else {
//
// Invalid data -- mark as -1.
//
*LifeRemaining = (UINT8)-1;
Status = EFI_DEVICE_ERROR;
}
if (Buffer != NULL) {
gBS->FreePool (Buffer);
}
return Status;
}
//
// Error path.
//
*LifeRemaining = (UINT8)-1;
if (Buffer != NULL) {
gBS->FreePool (Buffer);
}
return EFI_DEVICE_ERROR;
}
// ---------------------------------------------------------------------------
// Read SMART data (sub_1A70)
// ---------------------------------------------------------------------------
/**
Read the SMART data log (ATA feature 0xD0) from the device.
Builds the appropriate ATA or SCSI pass-through command based on the
controller type, issues it, and returns the raw 512-byte SMART log.
@param[in] Instance Pointer to the HDD_SMART_INSTANCE.
@param[in] Index Drive index.
@retval EFI_SUCCESS SMART data read.
@retval EFI_UNSUPPORTED Controller type not supported.
**/
EFI_STATUS
HddSmartReadSmartData (
IN HDD_SMART_INSTANCE *Instance,
IN UINTN Index
)
{
EFI_STATUS Status;
HDD_SMART_DEVICE *Device;
UINT8 Desc[49];
UINT8 SmartBuffer[128];
BOOLEAN Predicted;
Device = &Instance->Devices[Index];
Status = EFI_UNSUPPORTED;
//
// Check whether the flag byte indicates we should use the
// SCSI pass-through or the direct ATA command path.
//
if (Device->SmartCapable) {
//
// Build the 49-byte SCSI pass-through descriptor.
//
ZeroMem (Desc, sizeof (Desc));
Desc[12] = 0xD8; // SMART ENABLE OPERATIONS
Desc[18] = 0x4F;
Desc[20] = 0xC2;
Desc[23] = 0xB0;
//
// Issue the command via the protocol callback.
//
Status = HddSmartCheckStatus (Device, 1, &Predicted);
}
return Status;
}
// ---------------------------------------------------------------------------
// Block I/O wrapper (sub_1BFC)
// ---------------------------------------------------------------------------
/**
Perform a block-level read or write operation on the device.
For native controllers, builds a 49-byte SCSI descriptor and calls
the Ext SCSI Pass Thru protocol. For RAID or legacy, falls through
to direct ATA pass-through.
@param[in] Instance Pointer to the HDD_SMART_INSTANCE.
@param[in] Command ATA command code (e.g. 0xD5 for READ LOG).
@param[out] Buffer Data buffer.
@param[in] SectorCount Number of 512-byte sectors to transfer.
@param[in] SectorSize Sector size exponent (0 = 512, 1 = 1024, ...).
@retval EFI_SUCCESS Operation completed.
@retval EFI_INVALID_PARAMETER Buffer is NULL.
**/
EFI_STATUS
HddSmartBlockIoReadExt (
IN HDD_SMART_INSTANCE *Instance,
IN UINT8 Command,
OUT VOID *Buffer,
IN UINT8 SectorCount,
IN UINT8 SectorSize
)
{
EFI_STATUS Status;
UINT8 Desc[49];
if (Buffer == NULL) {
return EFI_INVALID_PARAMETER;
}
if (Instance->Devices[0].ControllerType == 2) {
//
// RAID: use direct ATA pass-through with the wrapper.
//
return HddSmartBlockIoRead (&Instance->Devices[0], 0,
SectorCount, Buffer, NULL);
}
//
// Native: build SCSI pass-through descriptor.
//
ZeroMem (Desc, sizeof (Desc));
Desc[0] = (UINTN)Buffer & 0xFF;
Desc[1] = ((UINTN)Buffer >> 8) & 0xFF;
Desc[2] = ((UINTN)Buffer >> 16) & 0xFF;
Desc[3] = ((UINTN)Buffer >> 24) & 0xFF;
Desc[4] = 0x00;
Desc[5] = 0x00;
Desc[6] = 0x00;
Desc[7] = 0x00;
Desc[8] = 0x40;
Desc[9] = 0x00;
Desc[10] = 0x00;
Desc[11] = 0x40;
Desc[12] = Command;
HIWORD (*(UINT64 *)&Desc[0]) = SectorSize;
DWORD2 (*(UINT64 *)&Desc[0]) = SectorCount << 9;
Desc[18] = 0x4F;
Desc[20] = 0xC2;
Desc[23] = 0xB0;
Desc[48] = 0x4F;
//
// Issue command via the Ext SCSI Pass Thru protocol.
//
if (Instance->Devices[0].ControllerType == 1) {
HddSmartReadSmartData (Instance, 0); // Dummy read to init
}
return EFI_UNSUPPORTED;
}
// ---------------------------------------------------------------------------
// Block I/O write wrapper (sub_1D34)
// ---------------------------------------------------------------------------
/**
Perform a block-level write operation.
Only supported for native controllers. For RAID, returns
EFI_UNSUPPORTED.
@param[in] Instance Pointer to the HDD_SMART_INSTANCE.
@param[in] Command ATA command code.
@param[in] Buffer Data buffer to write.
@param[in] SubCommand ATA feature sub-command.
@param[in] SectorCount Number of 512-byte sectors.
@retval EFI_SUCCESS Write completed.
@retval EFI_UNSUPPORTED Controller is RAID.
@retval EFI_INVALID_PARAMETER Buffer is NULL.
**/
EFI_STATUS
HddSmartBlockIoWriteExt (
IN HDD_SMART_INSTANCE *Instance,
IN UINT8 Command,
IN VOID *Buffer,
IN UINT8 SubCommand,
IN UINT8 SectorCount
)
{
EFI_STATUS Status;
UINT8 Desc[49];
if (Instance->Devices[0].ControllerType == 2) {
return EFI_UNSUPPORTED;
}
if (Buffer == NULL) {
return EFI_INVALID_PARAMETER;
}
//
// Build the SCSI pass-through descriptor.
//
ZeroMem (Desc, sizeof (Desc));
Desc[12] = Command;
HIWORD (*(UINT64 *)&Desc[0]) = SectorCount;
DWORD2 (*(UINT64 *)&Desc[0]) = SectorCount << 9;
Desc[0] = (UINTN)Buffer & 0xFF;
Desc[1] = ((UINTN)Buffer >> 8) & 0xFF;
Desc[2] = ((UINTN)Buffer >> 16) & 0xFF;
Desc[3] = ((UINTN)Buffer >> 24) & 0xFF;
Desc[18] = 0x4F;
Desc[20] = 0xC2;
Desc[23] = 0xB0;
if (Instance->Devices[0].ControllerType == 1) {
Status = EFI_SUCCESS;
} else {
Status = EFI_UNSUPPORTED;
}
return Status;
}
// ---------------------------------------------------------------------------
// Deferred timer callback (sub_1E70)
// ---------------------------------------------------------------------------
/**
Timer callback for deferred SMART processing.
Frees the context passed by the HII callback, then signals the
timer to stop.
@param[in] Event The timer event.
@param[in] Context Pointer to the allocated context block.
**/
VOID
EFIAPI
HddSmartDeferredTimer (
IN EFI_EVENT Event,
IN VOID *Context
)
{
HddSmartNotify ();
gBS->FreePool (Context);
gBS->CloseEvent (Event);
}
// ---------------------------------------------------------------------------
// UEFI module entry point
// ---------------------------------------------------------------------------
/**
The module entry point.
Calls the UEFI Boot Services Table Library initialisation routine
(which caches gImageHandle, gST, gBS) and the UEFI Runtime Services
Table Library initialisation (which caches gRT), then delegates to
HddSmartDriverEntry.
@param[in] ImageHandle The firmware-allocated image handle.
@param[in] SystemTable Pointer to the UEFI system table.
@return Status code from HddSmartDriverEntry.
**/
EFI_STATUS
EFIAPI
ModuleEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
//
// UefiBootServicesTableLib and UefiRuntimeServicesTableLib
// initialisation (handled by constructor in the library).
// We also initialise our module-level copies.
//
return HddSmartDriverEntry (ImageHandle, SystemTable);
}
// ---------------------------------------------------------------------------
// Utility functions
// ---------------------------------------------------------------------------
/**
Zero-clear initialisation of a buffer with a pattern value.
Written in a manner that handles unaligned start and optimises for
aligned writes of 4-byte words.
@param[out] buf Buffer to fill.
@param[in] value Fill value (low byte replicated).
@param[in] n Size of buffer in bytes.
@return Pointer to the buffer.
**/
VOID *
EFIAPI
HddSmartSetMem (
OUT VOID *buf,
IN INT32 value,
IN UINTN n
)
{
UINT8 *ByteBuf;
UINT32 *WordBuf;
UINT32 WordValue;
UINT8 ByteValue;
ByteBuf = (UINT8 *)buf;
ByteValue = (UINT8)value;
WordValue = (ByteValue << 24) | (ByteValue << 16) |
(ByteValue << 8) | ByteValue;
if (n >= 4) {
UINTN Align = (UINTN)buf & 3;
if (Align != 0) {
SetMem (ByteBuf, 4 - Align, ByteValue);
ByteBuf += 4 - Align;
n -= 4 - Align;
}
WordBuf = (UINT32 *)ByteBuf;
while (n >= 4) {
*WordBuf++ = WordValue;
n -= 4;
}
ByteBuf = (UINT8 *)WordBuf;
}
SetMem (ByteBuf, n, ByteValue);
return buf;
}
/**
Copy memory, handling overlapping forward/backward copies.
When the source and destination overlap and src < dst, the copy
is performed from the end to the start (backward copy).
@param[out] dst Destination buffer.
@param[in] src Source buffer.
@param[in] n Number of bytes to copy.
@return Pointer to destination.
**/
VOID *
EFIAPI
HddSmartCopyMem (
OUT VOID *dst,
IN const VOID *src,
IN UINTN n
)
{
UINT8 *Dst8;
UINT8 *Src8;
UINT64 *Dst64;
UINT64 *Src64;
UINTN Count;
BOOLEAN Backward;
Dst8 = (UINT8 *)dst;
Src8 = (UINT8 *)src;
Backward = FALSE;
if ((src < dst) && (&Src8[n] >= Dst8)) {
//
// Overlapping and source precedes destination -- copy backward.
//
Src8 += n;
Dst8 += n;
Backward = TRUE;
}
//
// If both buffers are large enough and well-spaced, use 64-bit copies.
//
if (n >= 8 && ((UINTN)Src8 - (UINTN)Dst8 >= 8 ||
(UINTN)Dst8 - (UINTN)Src8 >= 8)) {
//
// Align source to 8 bytes if needed.
//
Count = (UINTN)Src8 & 7;
if (Backward) {
Src8--;
Dst8--;
}
if ((Count == ((UINTN)Dst8 & 7)) && Count != 0) {
//
// Align both pointers to 8-byte boundary.
//
if (!Backward) {
Count = 8 - Count;
}
CopyMem (Dst8, Src8, Count);
Src8 += Count;
Dst8 += Count;
n -= Count;
}
if (Backward) {
Src8 -= 7;
Dst8 -= 7;
}
//
// Copy 8 bytes at a time.
//
Count = n >> 3;
CopyMem (Dst8, Src8, Count * 8);
Src8 += Count * 8;
Dst8 += Count * 8;
n &= 7;
if (n != 0) {
if (Backward) {
Src8 += 8;
Dst8 += 8;
}
goto Tail;
}
return dst;
}
Tail:
if (Backward) {
Src8--;
Dst8--;
}
CopyMem (Dst8, Src8, n);
return dst;
}