/** @file
TpmNvmeSupport.c -- TPM NVMe Support DXE Driver
This DXE driver is responsible for detecting NVMe device presence on
TPM-connected NVMe controllers and reporting the result through the
AMI BIOS Setup variable. It integrates with the AMI TCG PPI mechanism
and provides a periodic retry mechanism via the UEFI timer services.
Functional overview:
1. On entry (TpmNvmeSupportEntry), the driver initializes all UEFI
globals (ImageHandle, SystemTable, BootServices, RuntimeServices),
locates the HOB list and PCD protocol, maps the PCI Express MMIO
range, and performs a hardware-settling delay.
2. TpmNvmeSupportDxeEntry checks the AMI Setup "TpmNvmeSupport" option.
If enabled, it clears the AMITCGPPIVAR variable and calls
CheckNvmeDevicePresence().
3. CheckNvmeDevicePresence() enumerates all handles with the NVMe
Storage protocol, probes each handle for a live device, and updates
the Setup variable with the presence flag (offset 300).
4. If no device is found immediately, a periodic timer (sub_800) is
registered to retry the check.
5. Debug output is provided through the DebugMode protocol, gated by
a CMOS-based severity filter (CMOS register 0x4B).
Copyright (C) 2025, American Megatrends International LLC.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "TpmNvmeSupport.h"
//
// ---------------------------------------------------------------------------
// GUID Definitions
// ---------------------------------------------------------------------------
//
EFI_GUID gAmiTcgPpiVariableGuid = AMI_TCG_PPI_VARIABLE_GUID;
EFI_GUID gAmiNvmeStorageProtocolGuid = AMI_NVME_STORAGE_PROTOCOL_GUID;
EFI_GUID gAmiSetupVariableGuid = AMI_SETUP_VARIABLE_GUID;
EFI_GUID gDebugModeProtocolGuid = DEBUG_MODE_PROTOCOL_GUID;
EFI_GUID gPcdProtocolGuid = PCD_PROTOCOL_GUID;
EFI_GUID gEfiHobListGuid = {
0x7739f24c, 0x93d7, 0x11d4, { 0x9a, 0x3a, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d }
};
//
// ---------------------------------------------------------------------------
// Global Data
// ---------------------------------------------------------------------------
//
//
// EFI BIOS globals.
//
EFI_HANDLE gImageHandle = NULL;
EFI_SYSTEM_TABLE *gSystemTable = NULL;
EFI_BOOT_SERVICES *gBootServices = NULL;
EFI_RUNTIME_SERVICES *gRuntimeServices = NULL;
//
// Debug mode protocol interface (obtained via LocateProtocol).
//
VOID *gDebugModeProtocol = NULL;
//
// HOB list pointer cached from the system configuration table.
//
VOID *gHobList = NULL;
//
// PCI Express base address from PCD token 5.
//
UINT64 gPciExpressBaseAddress = 0;
//
// PCD protocol interface (obtained via LocateProtocol).
//
VOID *gPcdProtocol = NULL;
//
// ---------------------------------------------------------------------------
// Helper / CPU Intrinsic Functions
// ---------------------------------------------------------------------------
//
/**
Executes the CPU PAUSE instruction.
PAUSE is a hint that the code is in a spin-loop, improving performance
and power consumption on SMT-capable processors.
**/
VOID
CpuPause (
VOID
)
{
_mm_pause ();
}
/**
Reads the Time-Stamp Counter (TSC).
@return The current 64-bit TSC value.
**/
UINT64
ReadTsc (
VOID
)
{
return __rdtsc ();
}
/**
Enables maskable interrupts (STI).
**/
VOID
EnableInterrupts (
VOID
)
{
_enable ();
}
/**
Disables maskable interrupts (CLI).
**/
VOID
DisableInterrupts (
VOID
)
{
_disable ();
}
/**
Reads the current processor EFLAGS register.
@return EFLAGS as a UINTN value.
**/
UINTN
ReadEflags (
VOID
)
{
return __getcallerseflags ();
}
//
// ---------------------------------------------------------------------------
// Protocol Access Helpers
// ---------------------------------------------------------------------------
//
/**
Locates and returns the debug mode protocol interface.
The protocol is resolved once and cached in gDebugModeProtocol.
A size check on the firmware memory allocation (must be >= 16) is
used as a heuristic to detect pre-DXE phases where the protocol
is unavailable.
@return Pointer to the debug protocol interface, or NULL if not found.
**/
DEBUG_MODE_PROTOCOL *
GetDebugProtocol (
VOID
)
{
DEBUG_MODE_PROTOCOL *Protocol;
UINT64 MemorySize;
if (gDebugModeProtocol != NULL) {
return gDebugModeProtocol;
}
//
// Check available memory -- in very early phases this will be small.
//
MemorySize = gBootServices->AllocatePages (AllocateAnyPages, EfiBootServicesData, 31);
gBootServices->FreePages (MemorySize, 31);
if (MemorySize > 0x10) {
//
// DXE phase -- attempt to locate the debug protocol.
//
if (gBootServices->LocateProtocol (
&gDebugModeProtocolGuid,
NULL,
&gDebugModeProtocol
) >= 0) {
return gDebugModeProtocol;
}
}
gDebugModeProtocol = NULL;
return NULL;
}
/**
Locates and returns the PCD protocol interface.
The protocol is resolved once and cached in gPcdProtocol.
ASSERTs if the protocol cannot be located.
@return Pointer to the PCD protocol interface.
**/
PCD_PROTOCOL *
GetPcdProtocol (
VOID
)
{
if (gPcdProtocol == NULL) {
if (gBootServices->LocateProtocol (
&gPcdProtocolGuid,
NULL,
&gPcdProtocol
) < 0) {
DEBUG ((DEBUG_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", EFI_NOT_FOUND));
ASSERT_EFI_ERROR (EFI_NOT_FOUND);
ASSERT (gPcdProtocol != NULL);
}
}
return gPcdProtocol;
}
//
// ---------------------------------------------------------------------------
// Debug Print and Assert Support
// ---------------------------------------------------------------------------
//
/**
Conditionally prints a debug message based on CMOS debug-level filtering.
The function reads the CMOS debug level register (port 0x70 register 0x4B)
and only invokes the debug protocol print function if the requested
ErrorLevel passes the filter mask.
@param[in] ErrorLevel DEBUG severity level for the message.
@param[in] Format Print format string.
@param[in] ... Variable argument list.
**/
VOID
EFIAPI
DebugPrint (
IN UINTN ErrorLevel,
IN CONST CHAR8 *Format,
...
)
{
DEBUG_MODE_PROTOCOL *Protocol;
UINTN DebugLevel;
UINTN FilterMask;
VA_LIST Va;
UINT8 CmosIndex;
UINT8 CmosValue;
Protocol = GetDebugProtocol ();
if (Protocol == NULL) {
return;
}
//
// Read the CMOS debug level register.
//
CmosIndex = IoRead8 (0x70) & 0x80;
IoWrite8 (0x70, CmosIndex | 0x4B);
CmosValue = IoRead8 (0x71);
//
// Decode the CMOS value to get the debug filter mask.
//
if (CmosValue > 3) {
DebugLevel = CmosValue;
if (CmosValue == 0) {
//
// Read hardware debug pin status.
//
DebugLevel = (MmioRead8 (0xFEDAF0490) & 2) | 1;
}
} else {
DebugLevel = CmosValue;
}
DebugLevel--;
if (DebugLevel > 0xFD) {
FilterMask = 0;
} else {
FilterMask = (DebugLevel == 1) ? DEBUG_INFO : DEBUG_ERROR;
}
//
// If the message passes the filter, call the debug protocol print function.
//
if ((FilterMask & ErrorLevel) != 0) {
VA_START (Va, Format);
Protocol->Print (ErrorLevel, Format, Va);
VA_END (Va);
}
}
/**
ASSERT implementation that prints a message and breaks.
@param[in] FileName Source file where the assertion fired.
@param[in] LineNumber Line number of the assertion.
@param[in] Description Descriptive string for the failed assertion.
**/
VOID
EFIAPI
AssertBreak (
IN CONST CHAR8 *FileName,
IN UINTN LineNumber,
IN CONST CHAR8 *Description
)
{
DEBUG_MODE_PROTOCOL *Protocol;
Protocol = GetDebugProtocol ();
if (Protocol != NULL) {
Protocol->AssertBreak (FileName, LineNumber, Description);
}
}
//
// ---------------------------------------------------------------------------
// HOB and PCD Access
// ---------------------------------------------------------------------------
//
/**
Locates and returns the HOB list pointer.
Scans the system configuration table for the gEfiHobListGuid entry
and caches the result in gHobList. ASSERTs on failure.
@return Pointer to the start of the HOB list.
**/
VOID *
GetHobList (
VOID
)
{
UINTN Index;
if (gHobList != NULL) {
return gHobList;
}
gHobList = NULL;
if (gSystemTable->NumberOfTableEntries > 0) {
for (Index = 0; Index < gSystemTable->NumberOfTableEntries; Index++) {
if (CompareGuid (
&gSystemTable->ConfigurationTable[Index].VendorGuid,
&gEfiHobListGuid
)) {
gHobList = gSystemTable->ConfigurationTable[Index].VendorTable;
break;
}
}
if (gHobList == NULL) {
DEBUG ((DEBUG_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", EFI_NOT_FOUND));
ASSERT_EFI_ERROR (EFI_NOT_FOUND);
ASSERT (gHobList != NULL);
}
} else {
DEBUG ((DEBUG_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", EFI_NOT_FOUND));
ASSERT_EFI_ERROR (EFI_NOT_FOUND);
ASSERT (gHobList != NULL);
}
return gHobList;
}
/**
Returns the PCI Express base address from PCD.
Reads PCD token 5 via the PCD protocol (GetPcdProtocol()->Get5())
and caches the result in gPciExpressBaseAddress.
@return The PCI Express MMIO base address.
**/
UINT64
GetPciExpressBaseAddress (
VOID
)
{
PCD_PROTOCOL *PcdProtocol;
if (gPciExpressBaseAddress != 0) {
return gPciExpressBaseAddress;
}
PcdProtocol = GetPcdProtocol ();
//
// Token 5 in the PCD database corresponds to PcdPciExpressBaseAddress.
//
gPciExpressBaseAddress = PcdProtocol->Get5 (PcdToken (PcdPciExpressBaseAddress));
return gPciExpressBaseAddress;
}
//
// ---------------------------------------------------------------------------
// I/O and Memory Access Wrappers
// ---------------------------------------------------------------------------
//
/**
Translates a PCI library address into a full MMIO address for PCI ECAM.
@param[in] Address PCI CF8-style address (only lower 28 bits valid).
@return Full MMIO address in the PCI Express ECAM space.
**/
UINTN
PciExpressLibAddress (
IN UINTN Address
)
{
ASSERT ((Address & ~0xFFFFFFF) == 0);
return Address + gPciExpressBaseAddress;
}
/**
Writes a 16-bit value to an I/O port.
@param[in] Port The I/O port address (must be word-aligned).
@param[in] Value The 16-bit value to write.
**/
VOID
IoWrite16 (
IN UINT16 Port,
IN UINT16 Value
)
{
ASSERT ((Port & 1) == 0);
__outword (Port, Value);
}
/**
Reads a 32-bit value from an I/O port.
@param[in] Port The I/O port address (must be dword-aligned).
@return The 32-bit value read from the port.
**/
UINT32
IoRead32 (
IN UINT16 Port
)
{
ASSERT ((Port & 3) == 0);
return __indword (Port);
}
/**
Reads a 64-bit value from an unaligned buffer.
@param[in] Buffer Pointer to the buffer (must be non-NULL).
@return The 64-bit value at Buffer.
**/
UINT64
ReadUnaligned64 (
IN CONST VOID *Buffer
)
{
ASSERT (Buffer != NULL);
return *(volatile UINT64 *)Buffer;
}
/**
Compares two EFI GUIDs for equality.
@param[in] Guid1 Pointer to the first GUID.
@param[in] Guid2 Pointer to the second GUID.
@retval TRUE The two GUIDs are identical.
@retval FALSE The GUIDs differ.
**/
BOOLEAN
CompareGuid (
IN CONST EFI_GUID *Guid1,
IN CONST EFI_GUID *Guid2
)
{
UINT64 Low1;
UINT64 High1;
UINT64 Low2;
UINT64 High2;
if (Guid1 == NULL || Guid2 == NULL) {
return FALSE;
}
Low1 = ReadUnaligned64 (Guid1);
High1 = ReadUnaligned64 ((UINT8 *)Guid1 + 8);
Low2 = ReadUnaligned64 (Guid2);
High2 = ReadUnaligned64 ((UINT8 *)Guid2 + 8);
return (BOOLEAN)(Low1 == Low2 && High1 == High2);
}
//
// ---------------------------------------------------------------------------
// Core Driver Logic
// ---------------------------------------------------------------------------
//
/**
Driver entry point.
Initializes all UEFI global state:
1. Caches ImageHandle, SystemTable, BootServices, RuntimeServices.
2. Locates the HOB list via the system configuration table.
3. Reads the PCI Express base address from the PCD database.
4. Performs short I/O delay to allow hardware to settle.
5. Enables or disables interrupts based on the prior interrupt flag.
@param[in] ImageHandle The firmware-allocated handle for the driver image.
@param[in] SystemTable A pointer to the UEFI system table.
@return EFI_SUCCESS Global state initialized successfully.
@return EFI_INVALID_PARAMETER One of the required pointers is NULL.
**/
EFI_STATUS
EFIAPI
TpmNvmeSupportEntry (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
UINTN PciBaseAddress;
UINTN DelayEnd;
UINTN StartTsc;
BOOLEAN InterruptsOn;
//
// Save the driver image handle.
//
gImageHandle = ImageHandle;
if (ImageHandle == NULL) {
ASSERT (gImageHandle != NULL);
}
//
// Save the system table.
//
gSystemTable = SystemTable;
if (SystemTable == NULL) {
ASSERT (gSystemTable != NULL);
}
//
// Save the boot services table.
//
gBootServices = SystemTable->BootServices;
if (gBootServices == NULL) {
ASSERT (gBootServices != NULL);
}
//
// Save the runtime services table.
//
gRuntimeServices = SystemTable->RuntimeServices;
if (gRuntimeServices == NULL) {
ASSERT (gRuntimeServices != NULL);
}
//
// Locate the HOB list.
//
GetHobList ();
//
// Obtain the PCI Express base address from PCD.
//
GetPciExpressBaseAddress ();
//
// If the PcdPciExpressBaseAddress token is non-negative, enable
// PCI Express MMIO access by writing the port 0xCF8 command.
//
if ((INT8)PciExpressLibAddress (0xF0004) >= 0) {
PciExpressLibAddress (0xF0000);
IoWrite16 (1280, 0x500);
}
//
// Save the current interrupt state and disable interrupts.
//
InterruptsOn = (ReadEflags () & EFI_FLAGS_IF) != 0;
DisableInterrupts ();
//
// Spin-wait for ~357 TSC ticks (approximately 100 ns on a 3.6 GHz CPU,
// sufficient for I/O posting to complete).
//
StartTsc = (UINTN)IoRead32 (1288);
DelayEnd = (StartTsc + 357) & 0x7FFFFF;
do {
CpuPause ();
} while ((((UINTN)IoRead32 (1288) - DelayEnd) & 0x800000) == 0);
ReadTsc ();
//
// Restore the interrupt state.
//
if (InterruptsOn) {
EnableInterrupts ();
} else {
DisableInterrupts ();
}
return EFI_SUCCESS;
}
/**
DXE entry routine that drives NVMe presence detection.
1. Prints a startup banner.
2. Checks whether the AMI Setup option for TPM NVMe support is enabled.
3. If enabled, clears the AMI TCG PPI variable.
4. Calls CheckNvmeDevicePresence().
5. If presence detection fails, registers a periodic timer event that
retries the check via gBS->SetTimer / gBS->RegisterProtocolNotify.
@return EFI_SUCCESS Detection completed or retry scheduled.
@return EFI_INVALID_PARAMETER Setup variable read failed.
@return others Error from UEFI services.
**/
EFI_STATUS
TpmNvmeSupportDxeEntry (
VOID
)
{
EFI_STATUS Status;
UINTN SetupVarSize;
UINT8 SetupData[299];
UINT8 TpmNvmeSupportEnabled;
UINTN NvmeVarSize;
UINTN VarSize;
//
// Print the module banner.
//
DEBUG ((DEBUG_INFO, " \n TpmNvmeSupportDxeEntry \n "));
//
// Read the AMI Setup variable to check if TPM NVMe support is enabled.
//
SetupVarSize = sizeof (SetupData);
Status = gRuntimeServices->GetVariable (
L"Setup",
&gAmiSetupVariableGuid,
NULL,
&SetupVarSize,
SetupData
);
if (Status == EFI_SUCCESS) {
//
// Field at offset 0x12C (300) in the Setup structure holds the
// TpmNvmeSupport option -- a single byte: 0x01 = enabled.
//
TpmNvmeSupportEnabled = SetupData[300];
if (TpmNvmeSupportEnabled) {
//
// Clear the AMI TCG PPI variable.
//
SetupData[300] = 0;
Status = gRuntimeServices->SetVariable (
L"Setup",
&gAmiSetupVariableGuid,
EFI_VARIABLE_NON_VOLATILE |
EFI_VARIABLE_BOOTSERVICE_ACCESS |
EFI_VARIABLE_RUNTIME_ACCESS,
SetupVarSize,
SetupData
);
//
// Delete the AMITCGPPIVAR variable.
//
VarSize = 0;
gRuntimeServices->SetVariable (
L"AMITCGPPIVAR",
&gAmiTcgPpiVariableGuid,
0,
7,
NULL
);
}
}
//
// Perform the initial NVMe device presence check.
//
Status = CheckNvmeDevicePresence ();
if (EFI_ERROR (Status)) {
//
// Device not found yet -- register a periodic timer to retry.
//
Status = gBootServices->SetTimer (
TimerPeriodic,
TimerRelative,
EFI_TIMER_PERIOD_SECONDS (8)
);
if (!EFI_ERROR (Status)) {
Status = gBootServices->RegisterProtocolNotify (
&gAmiNvmeStorageProtocolGuid,
gDriverBindingProtocol,
&gImageHandle
);
}
}
DEBUG ((DEBUG_INFO, " \n CheckNvmeDevicePresence results = %r \n ", Status));
return Status;
}
/**
Checks for the presence of an NVMe device.
This function:
1. Locates all handles supporting the NVMe storage protocol.
2. Attempts to open the protocol on each handle.
3. If any handle returns success, an NVMe device is considered present.
4. Writes the presence flag into the AMI Setup variable (offset 300).
5. Frees the handle buffer.
@return EFI_SUCCESS Presence detection completed.
@return EFI_NOT_FOUND No NVMe handles located.
@return EFI_INVALID_PARAMETER Invalid argument.
@return others Error from UEFI protocol or variable services.
**/
EFI_STATUS
CheckNvmeDevicePresence (
VOID
)
{
EFI_STATUS Status;
UINTN HandleCount;
EFI_HANDLE *HandleBuffer;
UINTN Index;
UINT8 NvmePresent;
UINTN SetupVarSize;
UINT8 SetupData[848];
UINT32 Attributes;
UINTN VarSize;
HandleCount = 0;
HandleBuffer = NULL;
VarSize = 814;
//
// Locate all handles that support the NVMe storage protocol.
//
Status = gBootServices->LocateHandleBuffer (
ByProtocol,
&gAmiNvmeStorageProtocolGuid,
NULL,
&HandleCount,
&HandleBuffer
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_INFO, "Locate NvmePassThruhandles results = %r\n", Status));
goto Done;
}
//
// Scan all NVMe protocol handles for a live device.
//
NvmePresent = 0;
for (Index = 0; Index < HandleCount; Index++) {
VOID *Interface;
Interface = NULL;
Status = gBootServices->HandleProtocol (
HandleBuffer[Index],
&gAmiNvmeStorageProtocolGuid,
&Interface
);
if (!EFI_ERROR (Status)) {
//
// Found a responsive NVMe device.
//
NvmePresent = 1;
break;
}
DEBUG ((DEBUG_INFO, "gBS->HandleProtocol: Status=%r\n", Status));
}
DEBUG ((
DEBUG_INFO,
" \n CheckNvmeDevicePresence Found = %x \n ",
NvmePresent
));
//
// Write the presence flag into the AMI Setup variable.
//
Attributes = 0;
SetupVarSize = sizeof (SetupData);
Status = gRuntimeServices->GetVariable (
L"Setup",
&gAmiSetupVariableGuid,
&Attributes,
&SetupVarSize,
SetupData
);
if (!EFI_ERROR (Status)) {
//
// Update byte offset 300 in the Setup data (TpmNvmeSupport presence flag).
//
SetupData[300] = NvmePresent;
Status = gRuntimeServices->SetVariable (
L"Setup",
&gAmiSetupVariableGuid,
Attributes,
VarSize,
SetupData
);
}
Done:
if (HandleBuffer != NULL) {
gBootServices->FreePool (HandleBuffer);
}
return Status;
}
/**
Timer notification callback that retries the NVMe device presence check.
Registered as a periodic timer callback when the initial presence check
fails. It calls CheckNvmeDevicePresence() to try again.
@param[in] Event The timer event that triggered this callback.
@param[in] Context Not used (can be NULL).
@return EFI_SUCCESS from CheckNvmeDevicePresence().
**/
EFI_STATUS
EFIAPI
NvmeCheckTimerNotify (
IN EFI_EVENT Event,
IN VOID *Context
)
{
//
// Print the module banner.
//
DEBUG ((DEBUG_INFO, " \n TpmNvmeSupportDxeEntry \n "));
//
// Retry the NVMe device presence check.
//
CheckNvmeDevicePresence ();
//
// Signal the event to indicate completion.
//
gBootServices->SignalEvent (Event);
return EFI_SUCCESS;
}