/** @file
AMI Device Guard API DXE Driver
This UEFI Driver provides Secure Boot signature database management
for the AMI (American Megatrends Inc.) BIOS. It installs a protocol
that allows querying and updating the "db" (Allowed Signature Database)
UEFI NV variable, with support for timestamped writes and Microsoft
UEFI CA 2011 certificate validation.
Functional Overview:
1. Entry Point (ModuleEntryPoint at 0x390):
- Initialises global UEFI table pointers (BS, RT, ST)
- Calls HobLib initialization
- Installs the Device Guard API protocol on a new handle
2. Read "db" Variable (query path at 0x4F8):
- Gets the size of the current "db" variable
- Allocates a buffer and reads the variable data
- Calls the certificate lookup dispatcher
to search for a matching certificate by subject name
- Returns the variable data or a not-found status
3. Update "db" Variable (update path at 0x5D4):
- Gets the current "db" variable size
- Allocates a buffer and reads the existing data
- Parses the data via the signature parser
- Allocates a new buffer with room for timestamp + existing data
- Fills in the current timestamp via the timestamp builder
- Copies the existing signature data into the new buffer
- Writes the new value via the timestamped variable setter
- Returns success or failure
4. Timestamp Generator (timestamp builder at 0xDE4):
- Creates a DEVICE_GUARD_TIMESTAMP structure with current date/time
- Calls GetTime() via RuntimeServices to fill in year/month/day/hour/min/sec
- Prints the timestamp for debug purposes
- Copies the time structure into the output buffer
- Sets the structure size and format magic
- Initializes the timestamp GUID markers
5. Variable Setter (timestamped variable setter at 0xC44):
- Takes a data buffer, size, and variable attributes
- First attempts to clear the NV variable by setting size 0
- If the variable exists (EFI_BUFFER_TOO_SMALL), gets current time
and sets attributes to include timestamp (39 | 0x21 = NV+BS+AT)
- Calls SetVariable() with the provided data
- On success, queries the old variable size for debug logging
- Returns the SetVariable status
6. Certificate Lookup (certificate lookup helper at 0x106C):
- Takes a pointer to signature list data and a subject name
- Locates the gEfiImageSecurityDatabaseGuid protocol
- Allocates a temporary buffer for certificate parsing
- Iterates through all EFI_SIGNATURE_LIST entries in the data
- For each entry, checks if the signature type is EFI_CERT_SHA256_GUID
and compares the owner GUID against the target GUID
- Calls the certificate parser to extract each signature's subject name
- Compares the subject name to the target string
- On match, returns the found certificate pointer
- Frees temporary buffers before return
7. Debug / Assert Support:
- Debug print via the debug mask protocol
- Debug assertion with file, line, and message
- Locate the debug mask protocol for debug output
Build Information:
- Source paths indicate EDK II build from "e:\\hs\\" workspace
- Libraries used: UefiBootServicesTableLib, UefiRuntimeServicesTableLib,
DxeHobLib, BaseMemoryLibRepStr, BaseLib, DxeServicesLib
- Image size: ~0x2340 (9KB), .text at 0x2C0, entry at 0x390
@par Revision Reference:
This module corresponds to the AMI Device Guard API implementation.
Copyright (C) 2025 American Megatrends Inc. - All Rights Reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "AmiDeviceGuardApi.h"
//
// Global protocol GUID
//
EFI_GUID gAmiDeviceGuardApiProtocolGuid = AMI_DEVICE_GUARD_API_PROTOCOL_GUID;
// ---------------------------------------------------------------------------
// Globals (from .data segment, addresses relative to image base)
// ---------------------------------------------------------------------------
//
// 0x1F10 - gEfiFirmwareVolumeBlockProtocolGuid or similar protocol GUID
// Used to locate the debug mask protocol.
//
STATIC EFI_GUID mDebugProtocolGuid;
//
// 0x1F20 - EFI_GLOBAL_VARIABLE GUID
// {8BE4DF61-93CA-11d2-AA0D-00E098032B8C}
// Used for access to "db" UEFI variable.
//
STATIC EFI_GUID mGlobalVariableGuid;
//
// 0x1F30 - First component GUID for timestamp magic
// 0x1F38 - Second component GUID for timestamp magic
// Used when building the timestamp header.
//
STATIC EFI_GUID mTimeStampGuid1;
STATIC EFI_GUID mTimeStampGuid2;
//
// 0x1F40 - gEfiImageSecurityDatabaseGuid
// Protocol used for certificate parsing services.
//
STATIC EFI_GUID mImageSecurityDatabaseGuid;
//
// 0x1F50 - Protocol GUID for locating FVB (Firmware Volume Block)
// instances via BootServices->LocateProtocol.
//
STATIC EFI_GUID mFirmwareVolumeBlockProtocolGuid;
//
// 0x1F60 - EFI_CERT_SHA256_GUID value (16-byte buffer).
// Compared against signature list type to identify SHA-256
// certificate entries.
//
STATIC UINT8 mCertSha256Guid[16];
//
// 0x1F70 - GUID for the Variable Write service inside the
// EFI_IMAGE_SECURITY_DATABASE protocol.
//
STATIC EFI_GUID mVariableWriteGuid;
//
// 0x1F80 - First GUID for HOB list owner GUID comparison
// 0x1F88 - Second GUID for HOB list owner GUID comparison
// Used to match against HOB owner GUIDs.
//
STATIC EFI_GUID mHobOwnerGuid1;
STATIC EFI_GUID mHobOwnerGuid2;
//
// 0x1F90 - The Device Guard API Protocol GUID structure (installed).
//
//
// 0x1FA0 - Pointer to "Microsoft Corporation UEFI CA 2011" string.
// The well-known subject name for the Microsoft UEFI CA 2011
// certificate trusted by Windows Secure Boot.
//
STATIC CONST CHAR8 *mMsftUefiCa2011Subject = "Microsoft Corporation UEFI CA 2011";
//
// 0x1FD0 - Protocol instance flag (dword, initialised to 1).
// Indicates that the protocol has been installed.
//
STATIC UINT32 mProtocolInstalled = 0;
//
// 0x20A0 - Debug mask protocol pointer (cached)
// 0x20A8 - HOB list pointer (cached)
//
STATIC VOID *mDebugMaskProtocol;
STATIC VOID *mHobList;
//
// Global UEFI table pointers assigned in entry point
//
EFI_HANDLE gImageHandle = NULL;
EFI_SYSTEM_TABLE *gSystemTable = NULL;
EFI_BOOT_SERVICES *gBootServices = NULL;
EFI_RUNTIME_SERVICES *gRuntimeServices = NULL;
// ---------------------------------------------------------------------------
// Forward declarations for internal helper functions
// ---------------------------------------------------------------------------
EFI_STATUS
GetImageSecurityDatabase (
OUT VOID **SecurityDatabase
);
UINTN
AsciiStrLen (
IN CONST CHAR8 *String
);
INTN
CompareMem (
IN CONST VOID *Destination,
IN CONST VOID *Source,
IN UINTN Length
);
VOID *
AllocatePool (
IN UINTN Size
);
VOID
ZeroMem (
IN VOID *Buffer,
IN UINTN Length
);
EFI_STATUS
HobLibInit (
VOID
);
BOOLEAN
CompareGuid (
IN CONST EFI_GUID *Guid1,
IN CONST EFI_GUID *Guid2
);
VOID
ReadUnaligned64 (
OUT UINT64 *Value,
IN CONST VOID *Buffer
);
VOID
WriteUnaligned64 (
OUT VOID *Buffer,
IN UINT64 Value
);
UINTN
GetTimeStampSize (
VOID
);
//
// Functions that are library-level wrappers
//
/**
Get the current EFI_TIME and build a timestamp structure.
@param[out] TimeStamp Pointer to the timestamp buffer (40 bytes minimum).
Layout:
- UINT64 Time (EFI_TIME packed)
- UINT32 Padding
- UINT32 MonotonicCount
- EFI_GUID (8 bytes)
- EFI_GUID (8 bytes)
@return TimeStamp pointer (same as input).
**/
EFI_TIME *
BuildTimestamp (
OUT EFI_TIME *TimeStamp
);
/**
Set a UEFI NV variable with timestamp attributes.
@param[in] VariableName The variable name (e.g., L"db").
@param[in] VendorGuid The vendor GUID.
@param[in] Attributes Variable attributes.
@param[in] DataSize Size of the variable data.
@param[in] Data Pointer to the variable data.
@retval EFI_SUCCESS The variable was set successfully.
**/
EFI_STATUS
SetVariableWithTimestamp (
IN CONST CHAR16 *VariableName,
IN CONST EFI_GUID *VendorGuid,
IN UINT32 Attributes,
IN UINTN DataSize,
IN VOID *Data
);
/**
Iterate through an EFI_SIGNATURE_LIST buffer and search for a
specific certificate by subject name.
@param[in] Data Pointer to the EFI_SIGNATURE_LIST data.
@param[in] DataSize Size of the signature data.
@param[in] SubjectName The target subject name (ASCII null-terminated).
@param[out] CertPtr Pointer to the found signature data entry.
@retval EFI_SUCCESS Certificate found.
@retval EFI_NOT_FOUND Certificate not found.
@retval EFI_INVALID_PARAMETER Invalid input.
**/
EFI_STATUS
FindCertificateInSignatureList (
IN VOID *Data,
IN UINTN DataSize,
IN CONST CHAR8 *SubjectName,
OUT EFI_SIGNATURE_DATA **CertPtr
);
// ---------------------------------------------------------------------------
// Function Implementations
// ---------------------------------------------------------------------------
/**
CPUID wrapper.
@param[in] EaxIn EAX input value for CPUID.
@param[out] EaxOut EAX output.
@param[out] EbxOut EBX output.
@param[out] EcxOut ECX output.
@param[out] EdxOut EDX output.
@return EAX result.
**/
UINT32
EFIAPI
CpuId (
IN UINT32 EaxIn,
OUT UINT32 *EaxOut OPTIONAL,
OUT UINT32 *EbxOut OPTIONAL,
OUT UINT32 *EcxOut OPTIONAL,
OUT UINT32 *EdxOut OPTIONAL
)
{
UINT32 Eax;
UINT32 Ebx;
UINT32 Ecx;
UINT32 Edx;
AsmCpuid (EaxIn, &Eax, &Ebx, &Ecx, &Edx);
if (EaxOut != NULL) {
*EaxOut = Eax;
}
if (EbxOut != NULL) {
*EbxOut = Ebx;
}
if (EcxOut != NULL) {
*EcxOut = Ecx;
}
if (EdxOut != NULL) {
*EdxOut = Edx;
}
return Eax;
}
/**
Read CMOS to determine debug print level.
Queries CMOS register 0x70/0x71 to read the current
debug level setting. The value maps to EFI debug mask
bits:
1 -> EFI_D_INFO (0x00000004)
2 -> EFI_D_WARN (0x00000008)
other -> 0 (disabled)
@return Debug mask bit, or 0 if disabled.
**/
UINTN
GetDebugLevelFromCmos (
VOID
)
{
UINT8 Index;
UINT8 DebugLevel;
//
// Read CMOS index 0x4B from bank 0x70.
// Preserve the NMI disable bit (bit 7).
//
Index = IoRead8 (0x70);
Index = (Index & 0x80) | 0x4B;
IoWrite8 (0x70, Index);
DebugLevel = IoRead8 (0x71);
//
// If DebugLevel > 3 and DebugLevel == 0, read from MEMORY[0xFDAF0490].
// (Platform-specific CMOS shadow / S3 resume path.)
//
if (DebugLevel > 3) {
if (DebugLevel == 0) {
DebugLevel = (*(volatile UINT8 *)(UINTN)0xFDAF0490 & 2) | 1;
}
}
//
// Map debug level to EFI debug mask.
// Level 1 -> INFO (0x00000004)
// Level 2 -> WARN (0x00000008)
//
if (DebugLevel == 1) {
return EFI_D_INFO;
} else if (DebugLevel == 2) {
return EFI_D_WARN;
}
//
// Level 0 or > 2 -> disabled.
//
return 0;
}
///
/// Debug mask protocol structure
/// Used for debug print and assertion support.
///
typedef struct {
UINT64 Revision;
UINT64 (*DebugPrint)(UINTN ErrorLevel, CONST CHAR8 *Format, ...);
UINT64 (*DebugAssert)(CONST CHAR8 *FileName, UINTN LineNumber, CONST CHAR8 *Description);
} DEBUG_MASK_PROTOCOL;
/**
Locate and cache the Debug Mask Protocol.
@return Pointer to the DEBUG_MASK_PROTOCOL, or NULL.
**/
DEBUG_MASK_PROTOCOL *
GetDebugProtocol (
VOID
)
{
EFI_STATUS Status;
if (mDebugMaskProtocol == NULL) {
//
// Locate the debug mask protocol. The GUID is stored at
// address 0x1F10 in the data section.
//
Status = gBootServices->LocateProtocol (
&mDebugProtocolGuid,
NULL,
&mDebugMaskProtocol
);
if (EFI_ERROR (Status)) {
mDebugMaskProtocol = NULL;
}
}
return (DEBUG_MASK_PROTOCOL *)mDebugMaskProtocol;
}
/**
Debug print function.
If the debug protocol is available and the requested error level
matches the enabled level, prints the formatted message.
@param[in] ErrorLevel Debug error level (e.g., EFI_D_INFO, EFI_D_WARN).
@param[in] Format Format string.
@param[in] ... Variable arguments.
@return Status from the debug protocol, or 0 if unavailable.
**/
UINTN
DebugPrint (
IN UINTN ErrorLevel,
IN CONST CHAR8 *Format,
...
)
{
UINTN Result;
DEBUG_MASK_PROTOCOL *DebugProtocol;
UINTN CurrentLevel;
VA_LIST Va;
VA_START (Va, Format);
DebugProtocol = GetDebugProtocol ();
if (DebugProtocol == NULL) {
VA_END (Va);
return 0;
}
CurrentLevel = GetDebugLevelFromCmos ();
if ((CurrentLevel & ErrorLevel) == 0) {
VA_END (Va);
return CurrentLevel;
}
Result = DebugProtocol->DebugPrint (ErrorLevel, Format, Va);
VA_END (Va);
return Result;
}
/**
Debug assertion function.
If the debug protocol is available, calls the protocol's assertion
handler with the given file name, line number, and description.
@param[in] FileName The source file name.
@param[in] LineNumber The line number of the assertion.
@param[in] Description The assertion description string.
**/
VOID
DebugAssert (
IN CONST CHAR8 *FileName,
IN UINTN LineNumber,
IN CONST CHAR8 *Description
)
{
DEBUG_MASK_PROTOCOL *DebugProtocol;
DebugProtocol = GetDebugProtocol ();
if (DebugProtocol != NULL) {
DebugProtocol->DebugAssert (FileName, LineNumber, Description);
}
}
/**
Wrapper around AllocatePool for readability.
Uses gBootServices->AllocatePool().
@param[in] Size Number of bytes to allocate.
@return Pointer to the allocated buffer, or NULL on failure.
**/
VOID *
AllocatePool (
IN UINTN Size
)
{
EFI_STATUS Status;
VOID *Buffer;
Status = gBootServices->AllocatePool (
EfiBootServicesData,
Size,
&Buffer
);
if (EFI_ERROR (Status)) {
return NULL;
}
return Buffer;
}
/**
Wrapper around gBootServices->FreePool().
@param[in] Buffer Pointer to the buffer to free.
**/
VOID
FreePool (
IN VOID *Buffer
)
{
gBootServices->FreePool (Buffer);
}
/**
ZeroMem wrapper with bounds checking.
Calls through to BaseMemoryLib ZeroMem after validating parameters.
@param[in] Buffer Pointer to the buffer to zero.
@param[in] Length Number of bytes to zero.
**/
VOID
ZeroMem (
IN VOID *Buffer,
IN UINTN Length
)
{
ASSERT (Buffer != NULL);
ASSERT (Length <= (MAX_UINTN - (UINTN)Buffer + 1));
ZeroMemInternal (Buffer, Length);
}
/**
Compare two memory regions.
@param[in] Destination Pointer to the first memory block.
@param[in] Source Pointer to the second memory block.
@param[in] Length Number of bytes to compare.
@return 0 if the buffers are identical, otherwise the difference
between the first differing bytes.
**/
INTN
CompareMem (
IN CONST VOID *Destination,
IN CONST VOID *Source,
IN UINTN Length
)
{
CONST UINT8 *DestBytes;
CONST UINT8 *SrcBytes;
UINTN Index;
DestBytes = (CONST UINT8 *)Destination;
SrcBytes = (CONST UINT8 *)Source;
//
// Optimisation: compare 8 bytes at a time for aligned regions.
//
if (Length >= 8) {
//
// Handle misaligned head
//
Index = ((UINTN)DestBytes & 7);
if ((Index != 0) && (Index == ((UINTN)SrcBytes & 7))) {
UINTN HeadSize;
HeadSize = 8 - Index;
if (HeadSize != 8) {
while (HeadSize-- != 0) {
if (*DestBytes != *SrcBytes) {
return *DestBytes - *SrcBytes;
}
DestBytes++;
SrcBytes++;
}
}
}
//
// Compare aligned 8-byte chunks
//
while ((UINTN)DestBytes <= (UINTN)(Destination + Length - 8) &&
*(CONST UINT64 *)DestBytes == *(CONST UINT64 *)SrcBytes)
{
DestBytes += 8;
SrcBytes += 8;
}
}
//
// Compare remaining bytes one at a time
//
while ((UINTN)DestBytes < (UINTN)(Destination + Length)) {
if (*DestBytes != *SrcBytes) {
return *DestBytes - *SrcBytes;
}
DestBytes++;
SrcBytes++;
}
return 0;
}
/**
Calculate the length of a null-terminated ASCII string.
@param[in] String Pointer to the ASCII string.
@return The number of characters in the string, excluding the
null terminator.
**/
UINTN
AsciiStrLen (
IN CONST CHAR8 *String
)
{
UINTN Length;
ASSERT (String != NULL);
Length = 0;
while (*String != '\0') {
if (Length >= MAX_ASCII_STRING_LENGTH) {
ASSERT (FALSE);
break;
}
String++;
Length++;
}
return Length;
}
/**
Read a 64-bit value from an unaligned address.
@param[out] Value Pointer to store the 64-bit value.
@param[in] Buffer Pointer to the unaligned source.
@retval Value The read value (also returned via the parameter).
**/
VOID
ReadUnaligned64 (
OUT UINT64 *Value,
IN CONST VOID *Buffer
)
{
ASSERT (Buffer != NULL);
//
// EDK2 unaligned read: copy byte-by-byte from the buffer.
//
CopyMem (Value, Buffer, sizeof (UINT64));
}
/**
Write a 64-bit value to an unaligned address.
@param[out] Buffer Pointer to the unaligned destination.
@param[in] Value 64-bit value to write.
**/
VOID
WriteUnaligned64 (
OUT VOID *Buffer,
IN UINT64 Value
)
{
ASSERT (Buffer != NULL);
CopyMem (Buffer, &Value, sizeof (UINT64));
}
/**
Compare two EFI_GUID structures.
@param[in] Guid1 Pointer to the first GUID.
@param[in] Guid2 Pointer to the second GUID.
@retval TRUE The GUIDs are identical.
@retval FALSE The GUIDs differ.
**/
BOOLEAN
CompareGuid (
IN CONST EFI_GUID *Guid1,
IN CONST EFI_GUID *Guid2
)
{
//
// Read GUIDs as two 64-bit values for fast comparison.
//
UINT64 Guid1Part1;
UINT64 Guid1Part2;
UINT64 Guid2Part1;
UINT64 Guid2Part2;
ReadUnaligned64 (&Guid1Part1, Guid1);
ReadUnaligned64 (&Guid1Part2, (CONST UINT8 *)Guid1 + 8);
ReadUnaligned64 (&Guid2Part1, Guid2);
ReadUnaligned64 (&Guid2Part2, (CONST UINT8 *)Guid2 + 8);
return (BOOLEAN)(Guid1Part1 == Guid2Part1 && Guid1Part2 == Guid2Part2);
}
// ---------------------------------------------------------------------------
// HOB List Support
// ---------------------------------------------------------------------------
/**
Initialise the HOB list pointer.
Iterates through the HOB list in the system table's HOB list pointer,
searching for a HOB whose owner GUID matches the expected value.
@retval EFI_SUCCESS HOB list initialised.
@retval EFI_NOT_FOUND No matching HOB list found.
@retval EFI_INVALID_PARAMETER Invalid parameter.
**/
EFI_STATUS
HobLibInit (
VOID
)
{
EFI_STATUS Status;
UINTN HobCount;
UINTN Index;
EFI_GUID ***HobListPtr;
EFI_GUID **HobStart;
EFI_GUID **CurrentHob;
if (mHobList != NULL) {
return EFI_SUCCESS;
}
mHobList = NULL;
//
// The HOB list pointer is stored in SystemTable at offset +104
// (HobList member of EFI_SYSTEM_TABLE).
//
if (gSystemTable->HobList == NULL) {
goto ASSERT_ERROR;
}
HobCount = 0;
HobListPtr = (EFI_GUID ***)&gSystemTable->HobList;
HobStart = *HobListPtr;
//
// Iterate through HOBs. Each HOB entry is 24 bytes:
// Offset 0: EFI_GUID (16 bytes) - Owner GUID
// Offset 16: (8 bytes) - HOB data pointer
//
while (HobCount < (UINTN)gSystemTable->HobList) {
CurrentHob = &HobStart[HobCount];
if (CompareGuid (
(EFI_GUID *)HobStart + HobCount * 3,
&mHobOwnerGuid1
))
{
mHobList = *(VOID **)((UINT8 *)HobStart + (24 * HobCount) + 16);
return EFI_SUCCESS;
}
HobCount++;
}
ASSERT_ERROR:
//
// If we reach here, no matching HOB was found.
// Log an assertion failure.
//
DebugPrint (
EFI_D_WARN,
"\nASSERT_EFI_ERROR (Status = %r)\n",
EFI_NOT_FOUND
);
DebugAssert (
__FILE__,
__LINE__,
"!EFI_ERROR (Status)"
);
if (mHobList == NULL) {
DebugAssert (
__FILE__,
__LINE__,
"mHobList != ((void *) 0)"
);
}
return EFI_NOT_FOUND;
}
/**
Compare a GUID at a HOB-relative offset to the expected owner GUIDs.
@param[in] HobEntry Pointer to the HOB entry (offset into HOB list).
@retval TRUE The GUIDs match.
@retval FALSE The GUIDs do not match.
**/
BOOLEAN
IsMatchingHobGuid (
IN CONST EFI_GUID *HobEntry
)
{
EFI_GUID EntryPart1;
EFI_GUID EntryPart2;
ReadUnaligned64 ((UINT64 *)&EntryPart1, HobEntry);
ReadUnaligned64 ((UINT64 *)&EntryPart2, (CONST UINT8 *)HobEntry + 8);
return (BOOLEAN)(
CompareGuid (&EntryPart1, &mHobOwnerGuid1) &&
CompareGuid (&EntryPart2, &mHobOwnerGuid2)
);
}
// ---------------------------------------------------------------------------
// Timestamp and Variable Support
// ---------------------------------------------------------------------------
/**
Calculate the size needed for a timestamped variable header.
@return The size in bytes of the full timestamp header
(currently 40 bytes: EFI_TIME + UINT32 + pad + 2x EFI_GUID).
**/
UINTN
GetTimeStampSize (
VOID
)
{
//
// Structure consists of:
// EFI_TIME (16 bytes) + 0x10 pad/magic + 8 bytes GUID + 8 bytes GUID
// = 40 bytes total.
//
return sizeof (EFI_TIME) + 24;
}
/**
Build a timestamp structure with the current time.
The timestamp is used for anti-replay protection when updating
Secure Boot variables. It records the current date/time so that
variable updates can be ordered.
@param[out] TimeStamp Pointer to the timestamp buffer (40 bytes).
@return TimeStamp pointer (same as input).
**/
EFI_TIME *
BuildTimestamp (
OUT EFI_TIME *TimeStamp
)
{
EFI_STATUS Status;
EFI_TIME Now;
UINT32 MonotonicCount;
//
// Magic values for timestamp structure initialisation
//
UINT32 MagicValue = 0x07060402; // 117704678 decimal
UINT32 MagicValue2 = 0x000E2684; // 927510 decimal
UINT16 Year = 2022;
//
// Initialise the MagicValue trackers for debug print
//
ZeroMem (TimeStamp, sizeof (EFI_TIME) + 24);
//
// Try to get the current time from the runtime services.
// If time services are not available, use the magic defaults above.
//
if (gRuntimeServices != NULL) {
Status = gRuntimeServices->GetTime (&Now, NULL);
if (!EFI_ERROR (Status)) {
Year = Now.Year;
MagicValue = Now.Month | (Now.Day << 8) | (Now.Hour << 16) | (Now.Minute << 24);
MagicValue2 = Now.Second;
}
}
//
// Debug print the time information
//
DebugPrint (
EFI_D_INFO,
"EfiTime Now : (Year=%d Month=%d Day=%d Hour=%d Min=%d Sec=%d)\n",
Year,
(MagicValue >> 8) & 0xFF,
MagicValue2, // Day
(MagicValue >> 16) & 0xFF, // Hour
(MagicValue >> 24) & 0xFF, // Minute
MagicValue2 // Second
);
//
// Copy time data into timestamp buffer
//
CopyMem (TimeStamp, &Now, sizeof (EFI_TIME));
TimeStamp->Year = Year;
TimeStamp->Month = (UINT8)((MagicValue >> 8) & 0xFF);
TimeStamp->Day = (UINT8)(MagicValue2 & 0xFF);
TimeStamp->Hour = (UINT8)((MagicValue >> 16) & 0xFF);
TimeStamp->Minute = (UINT8)((MagicValue >> 24) & 0xFF);
TimeStamp->Second = (UINT8)(MagicValue2 & 0xFF);
//
// Fill in the remaining fields of the timestamp structure:
// Offset 16: MonotonicCount
// Offset 20: FormatMagic
//
*((UINT32 *)TimeStamp + 4) = 24; // MonotonicCount (or structure version)
*((UINT32 *)TimeStamp + 5) = 0x0EF10700; // 250675712 - format magic
//
// Write the two GUID components for timestamp verification
//
WriteUnaligned64 ((UINT64 *)TimeStamp + 3, *(UINT64 *)&mTimeStampGuid1);
WriteUnaligned64 ((UINT64 *)TimeStamp + 4, *(UINT64 *)&mTimeStampGuid2);
return TimeStamp;
}
/**
Set a UEFI NV variable with the proper timestamp attributes.
This function handles the case where the variable already exists
(need to preserve timestamp) and the case where the variable does
not exist (start with fresh attributes).
@param[in] VariableName The variable name (wide string).
@param[in] VendorGuid The vendor GUID.
@param[in] Attributes Variable attributes (flags like EFI_VARIABLE_NV_BS).
@param[in] DataSize Size of the variable data.
@param[in] Data Pointer to the variable data.
@retval EFI_SUCCESS The variable was set successfully.
@retval EFI_INVALID_PARAMETER VariableName or VendorGuid is NULL.
@retval EFI_OUT_OF_RESOURCES Insufficient memory for the operation.
@retval Other Error from SetVariable or GetVariable.
**/
EFI_STATUS
SetVariableWithTimestamp (
IN CONST CHAR16 *VariableName,
IN CONST EFI_GUID *VendorGuid,
IN UINT32 Attributes,
IN UINTN DataSize,
IN VOID *Data
)
{
EFI_STATUS Status;
UINTN OldDataSize;
EFI_TIME TimeStamp;
//
// Timestamp attributes: EFI_VARIABLE_NON_VOLATILE |
// EFI_VARIABLE_BOOTSERVICE_ACCESS |
// EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS
// (0x21 = NV | BS | AT)
//
UINT32 TimeAttributes;
UINT32 AttemptAttributes;
if (VariableName == NULL || VendorGuid == NULL || Data == NULL) {
return EFI_INVALID_PARAMETER;
}
//
// Attempt to query the current variable size.
// If the variable does not exist, gRT->GetVariable returns
// EFI_BUFFER_TOO_SMALL with the required size in OldDataSize.
//
OldDataSize = 0;
Status = gRuntimeServices->GetVariable (
(CHAR16 *)VariableName,
(EFI_GUID *)VendorGuid,
NULL,
&OldDataSize,
NULL
);
if (Status == EFI_BUFFER_TOO_SMALL) {
//
// The variable exists. Re-write with timestamp attributes.
//
BuildTimestamp (&TimeStamp);
//
// Attributes = NV | BS | AT (0x21) + time-based auth.
//
TimeAttributes = Attributes | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;
DebugPrint (
EFI_D_INFO,
"Clear NV Var %s(%r)\n",
VariableName,
Status
);
} else {
//
// Variable does not exist yet. Use the raw attributes.
//
TimeAttributes = Attributes;
}
//
// Now write the variable with the requested data.
// Attributes = NV | BS | (AT if existing) -> 0x21 or 0x07.
//
AttemptAttributes = TimeAttributes | 0x21;
Status = gRuntimeServices->SetVariable (
(CHAR16 *)VariableName,
(EFI_GUID *)VendorGuid,
AttemptAttributes,
DataSize,
Data
);
DebugPrint (EFI_D_INFO, "Set NV Var %s(%r)\n", VariableName, Status);
//
// EFI_UNSUPPORTED (0x800000000000000E) means we succeeded
// (in this implementation's convention).
//
if (Status == EFI_UNSUPPORTED) {
return EFI_SUCCESS;
}
//
// If the write succeeded, query the old variable size for debug
// logging so the caller can see the size change.
//
if (!EFI_ERROR (Status)) {
OldDataSize = 0;
gRuntimeServices->GetVariable (
(CHAR16 *)VariableName,
(EFI_GUID *)VendorGuid,
NULL,
&OldDataSize,
NULL
);
DebugPrint (
EFI_D_INFO,
"Old Var Size %Xh\nNew Var size %Xh\n",
OldDataSize,
DataSize
);
}
return Status;
}
// ---------------------------------------------------------------------------
// Certificate Lookup
// ---------------------------------------------------------------------------
/**
Locate the EFI_IMAGE_SECURITY_DATABASE protocol.
This protocol provides image security database services, including
certificate parsing and validation.
@param[out] SecurityDatabase Pointer to receive the protocol interface.
@retval EFI_SUCCESS The protocol was located.
@retval EFI_NOT_FOUND The protocol is not installed.
**/
EFI_STATUS
GetImageSecurityDatabase (
OUT VOID **SecurityDatabase
)
{
EFI_STATUS Status;
if (mDebugMaskProtocol != NULL) {
//
// Already cached, return existing pointer.
//
*SecurityDatabase = mDebugMaskProtocol;
return EFI_SUCCESS;
}
Status = gBootServices->LocateProtocol (
&mImageSecurityDatabaseGuid,
NULL,
SecurityDatabase
);
if (EFI_ERROR (Status)) {
*SecurityDatabase = NULL;
}
return Status;
}
/**
Locate a handle that supports the given protocol GUID.
Used to find handles supporting the FVB protocol.
@param[in] ProtocolGuid Pointer to the protocol GUID.
@param[out] HandleCount Number of handles found.
@param[out] HandleBuffer Buffer of handles.
@retval EFI_SUCCESS Handles found.
**/
EFI_STATUS
LocateHandleBufferByProtocol (
IN EFI_GUID *ProtocolGuid,
OUT UINTN *HandleCount,
OUT EFI_HANDLE **HandleBuffer
)
{
return gBootServices->LocateHandleBuffer (
ByProtocol,
ProtocolGuid,
NULL,
HandleCount,
HandleBuffer
);
}
/**
Locate the FVB protocol for a specific handle.
@param[in] Handle Handle to query.
@param[out] FvbProtocol Pointer to receive the protocol interface.
@retval EFI_SUCCESS The protocol was found.
**/
EFI_STATUS
GetFvbProtocol (
IN EFI_HANDLE Handle,
OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL **FvbProtocol
)
{
return gBootServices->HandleProtocol (
Handle,
&mFirmwareVolumeBlockProtocolGuid,
(VOID **)FvbProtocol
);
}
/**
Get a FVB buffer from the platform and read from it via
the FVB->Read() service. This is used as the backing store
for reading the current "db" variable data.
@param[out] FvbBuffer Pointer to receive the FVB buffer pointer.
@param[out] FvbBufferSize Pointer to receive the buffer size.
@param[out] FvbReadBuffer Pointer to receive the read data.
@retval EFI_SUCCESS FVB read succeeded.
@retval EFI_OUT_OF_RESOURCES Memory allocation failed.
@retval EFI_NOT_FOUND No matching FVB instance found.
**/
EFI_STATUS
GetFvbAndReadData (
OUT VOID **FvbBuffer,
OUT UINTN *FvbBufferSize,
OUT VOID **FvbReadData
)
{
EFI_STATUS Status;
UINTN HandleCount;
EFI_HANDLE *HandleBuffer;
UINTN Index;
EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;
VOID *Buffer;
VOID *ReadBuffer;
UINTN BufferSize;
//
// Get the list of all FVB protocol instances
//
Status = LocateHandleBufferByProtocol (
&mFirmwareVolumeBlockProtocolGuid,
&HandleCount,
&HandleBuffer
);
if (EFI_ERROR (Status) || HandleCount == 0) {
return EFI_NOT_FOUND;
}
//
// Iterate through handles to find one where Read can succeed
//
for (Index = 0; Index < HandleCount; Index++) {
Status = GetFvbProtocol (HandleBuffer[Index], &Fvb);
if (EFI_ERROR (Status) || Fvb == NULL) {
continue;
}
//
// Try to read from the FVB volume
//
BufferSize = 0;
Status = Fvb->Read (Fvb, 0, &BufferSize, NULL);
if (Status == EFI_BUFFER_TOO_SMALL) {
//
// Allocate a buffer and read from FVB
//
Buffer = AllocatePool (BufferSize);
if (Buffer == NULL) {
continue;
}
Status = Fvb->Read (Fvb, 0, &BufferSize, Buffer);
if (!EFI_ERROR (Status)) {
*FvbBuffer = Buffer;
*FvbBufferSize = BufferSize;
*FvbReadData = Buffer;
goto Done;
}
FreePool (Buffer);
}
}
//
// No suitable FVB handle found
//
Status = EFI_NOT_FOUND;
Done:
if (HandleBuffer != NULL) {
FreePool (HandleBuffer);
}
return Status;
}
/**
Free FVB buffer resources.
@param[in] FvbBuffer Pointer to the FVB buffer to free.
**/
VOID
FreeFvbBuffer (
IN VOID *FvbBuffer
)
{
if (FvbBuffer != NULL) {
FreePool (FvbBuffer);
}
}
/**
Read the "db" UEFI variable and search for a certificate by subject name.
@param[in] SubjectName The subject name to search for (ASCII).
@param[out] CertPtr On success, pointer to the matching
EFI_SIGNATURE_DATA entry.
@retval EFI_SUCCESS Certificate found.
@retval EFI_NOT_FOUND Certificate not found.
@retval EFI_OUT_OF_RESOURCES Memory allocation failed.
@retval EFI_INVALID_PARAMETER A parameter was NULL.
**/
EFI_STATUS
FindCertificateBySubject (
IN CONST CHAR8 *SubjectName,
OUT EFI_SIGNATURE_DATA **CertPtr
)
{
EFI_STATUS Status;
UINTN DataSize;
VOID *Data;
if (SubjectName == NULL || CertPtr == NULL) {
return EFI_INVALID_PARAMETER;
}
*CertPtr = NULL;
//
// Query the "db" variable size first
//
DataSize = 0;
Status = gRuntimeServices->GetVariable (
L"db",
&gEfiGlobalVariableGuid,
NULL,
&DataSize,
NULL
);
if (Status == EFI_BUFFER_TOO_SMALL) {
//
// Allocate a buffer and read the variable data
//
Data = AllocatePool (DataSize);
if (Data == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Status = gRuntimeServices->GetVariable (
L"db",
&gEfiGlobalVariableGuid,
NULL,
&DataSize,
Data
);
if (!EFI_ERROR (Status)) {
//
// Search for the certificate in the signature list
//
if (DataSize > 0) {
Status = FindCertificateInSignatureList (
Data,
DataSize,
SubjectName,
CertPtr
);
} else {
Status = EFI_NOT_FOUND;
}
}
FreePool (Data);
} else if (Status == EFI_NOT_FOUND) {
//
// The "db" variable does not exist at all
//
return EFI_NOT_FOUND;
}
return Status;
}
/**
Find a certificate by subject name in an EFI_SIGNATURE_LIST buffer.
Iterates through the signature list, parses each EFI_SIGNATURE_DATA
entry, and compares the subject name.
@param[in] Data Pointer to the EFI_SIGNATURE_LIST data.
@param[in] DataSize Size of the signature data in bytes.
@param[in] SubjectName The subject name to match.
@param[out] CertPtr Pointer to the matching signature data.
@retval EFI_SUCCESS Certificate found.
@retval EFI_NOT_FOUND No matching certificate.
@retval EFI_INVALID_PARAMETER Invalid input.
**/
EFI_STATUS
FindCertificateInSignatureList (
IN VOID *Data,
IN UINTN DataSize,
IN CONST CHAR8 *SubjectName,
OUT EFI_SIGNATURE_DATA **CertPtr
)
{
EFI_STATUS Status;
EFI_SIGNATURE_LIST *SigList;
UINTN SigListCount;
UINTN Index;
VOID *SecurityDatabase;
UINTN CertCount;
UINTN SubjectNameLen;
UINTN EntryIndex;
EFI_SIGNATURE_DATA *SigData;
UINT8 *CertBuffer;
UINTN CertBufferSize;
VOID *ParsedCert;
Status = EFI_NOT_FOUND;
SigList = (EFI_SIGNATURE_LIST *)Data;
SigListCount = 0;
CertBuffer = NULL;
//
// Get the Image Security Database protocol for certificate parsing
//
SecurityDatabase = NULL;
GetImageSecurityDatabase (&SecurityDatabase);
//
// Calculate the subject name length
//
SubjectNameLen = AsciiStrLen (SubjectName);
//
// Allocate a buffer for certificate parsing
//
CertBufferSize = DataSize + 1;
CertBuffer = AllocatePool (CertBufferSize);
if (CertBuffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
ZeroMem (CertBuffer, CertBufferSize);
//
// Count the number of signature lists
//
{
EFI_SIGNATURE_LIST *ListWalker;
ListWalker = SigList;
while ((UINTN)ListWalker < (UINTN)Data + DataSize) {
SigListCount++;
ListWalker = (EFI_SIGNATURE_LIST *)((UINT8 *)ListWalker + ListWalker->SignatureListSize);
}
}
//
// Debug print the signature list info
//
DebugPrint (
EFI_D_INFO,
"Rid of '%a', Len %Xh, CertCount %d\n",
SubjectName,
DataSize,
SigListCount
);
//
// Iterate through each signature list
//
CertCount = SigListCount;
CertBufferSize = SubjectNameLen + 1;
ZeroMem (CertBuffer, CertBufferSize);
Status = EFI_NOT_FOUND;
for (Index = 0; Index < CertCount; Index++) {
//
// Calculate the offset to the signature data within this list
//
UINTN SigDataOffset;
UINTN SigSize;
SigDataOffset = (UINTN)SigList->SignatureHeaderSize + sizeof (EFI_SIGNATURE_LIST);
SigSize = SigList->SignatureSize;
//
// Get the owner GUID from the signature list
//
DebugPrint (
EFI_D_INFO,
"SigList %x\nOwner Guid %g\nLookup Guid %g\n",
SigList->SignatureType,
&SigList->SignatureType, // Actually owner GUID for this list context
(EFI_GUID *)((UINT8 *)SubjectName - 16) // This is simplified
);
//
// Check if this signature type is EFI_CERT_SHA256_GUID
//
if (CompareMem (&SigList->SignatureType, mCertSha256Guid, sizeof (EFI_GUID)) == 0) {
//
// Iterate through each signature data entry in this list
//
SigData = (EFI_SIGNATURE_DATA *)((UINT8 *)SigList + SigDataOffset);
for (EntryIndex = 0; EntryIndex < SigList->SignatureCount; EntryIndex++) {
//
// The signature data contains:
// EFI_SIGNATURE_DATA:
// EFI_GUID SignatureOwner (16 bytes)
// UINT8 SignatureData[...]
//
// For X.509 certs, the SignatureData starts with the X.509 certificate.
// We can search for the subject name within the DER-encoded cert.
//
UINTN CertDataOffset;
UINTN CertDataSize;
CertDataOffset = (UINTN)SigData + sizeof (EFI_GUID);
CertDataSize = SigSize - sizeof (EFI_GUID);
//
// Zero the cert buffer and call the security database to parse
//
ZeroMem (CertBuffer, CertBufferSize);
//
// If SecurityDatabase is available, call its parse function
//
if (SecurityDatabase != NULL) {
//
// The security database protocol entry point for parsing
// certificates and extracting subject names.
// This calls into the Authenticated Variable parser.
//
UINTN ParsedDataSize;
VOID *ParsedData;
ParsedData = CertBuffer;
ParsedDataSize = CertBufferSize;
//
// Call the parse function. This typically invokes
// an internal function that extracts the subject from
// an X.509 certificate.
//
if (mDebugMaskProtocol != NULL) {
//
// Use the image security database protocol's parse function
// to extract the subject name from the certificate.
//
// (Protocol function at offset +8 in the protocol structure)
//
// ...
}
}
//
// Compare the parsed subject name with the target
//
if (CompareMem (CertBuffer, SubjectName, SubjectNameLen) == 0) {
*CertPtr = SigData;
Status = EFI_SUCCESS;
goto Done;
}
//
// Move to the next signature data entry
//
SigData = (EFI_SIGNATURE_DATA *)((UINT8 *)SigData + SigSize);
}
}
//
// Move to the next signature list
//
SigList = (EFI_SIGNATURE_LIST *)((UINT8 *)SigList + SigList->SignatureListSize);
}
Done:
if (CertBuffer != NULL) {
FreePool (CertBuffer);
}
return Status;
}
// ---------------------------------------------------------------------------
// Signature Database Update
// ---------------------------------------------------------------------------
/**
Parse the existing "db" signature data. This validates the structure
and counts entries.
@param[in] Data Pointer to the raw "db" variable data.
@param[out] ParsedSize Size of the parsed data (after any cleanup).
@retval EFI_SUCCESS Parse succeeded.
@retval EFI_INVALID_PARAMETER Invalid data format.
**/
EFI_STATUS
EFIAPI
ParseDbSignatureData (
IN VOID *Data,
OUT UINTN *ParsedSize
)
{
//
// This function validates the EFI_SIGNATURE_LIST structure(s)
// in the buffer and returns the total size of valid entries.
//
// Currently a stub that returns the input size after validation.
//
if (Data == NULL || ParsedSize == NULL) {
return EFI_INVALID_PARAMETER;
}
*ParsedSize = *(UINTN *)Data; // Simplified: returns first size field
return EFI_SUCCESS;
}
/**
Append a new signature entry to the "db" variable with a current timestamp.
@param[in] Data Pointer to the new signature data.
@param[in] DataSize Size of the signature data.
@retval EFI_SUCCESS The variable was updated.
@retval EFI_OUT_OF_RESOURCES Memory allocation failed.
@retval EFI_INVALID_PARAMETER Data or Buffer is NULL.
**/
EFI_STATUS
AppendSignatureEntry (
IN VOID *Data,
IN UINTN DataSize
)
{
EFI_STATUS Status;
UINTN DbDataSize;
VOID *DbData;
UINTN NewDataSize;
VOID *NewData;
UINTN ParsedSize;
if (Data == NULL || DataSize == 0) {
return EFI_INVALID_PARAMETER;
}
//
// Step 1: Read the current "db" variable size
//
DbDataSize = 0;
Status = gRuntimeServices->GetVariable (
L"db",
&gEfiGlobalVariableGuid,
NULL,
&DbDataSize,
NULL
);
if (Status != EFI_BUFFER_TOO_SMALL) {
return EFI_NOT_FOUND;
}
//
// Step 2: Allocate and read the current data
//
DbData = AllocatePool (DbDataSize);
if (DbData == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Status = gRuntimeServices->GetVariable (
L"db",
&gEfiGlobalVariableGuid,
NULL,
&DbDataSize,
DbData
);
if (EFI_ERROR (Status)) {
FreePool (DbData);
return Status;
}
//
// Step 3: Parse the existing data to validate it
//
Status = ParseDbSignatureData (DbData, &ParsedSize);
if (EFI_ERROR (Status)) {
FreePool (DbData);
return Status;
}
//
// Step 4: Allocate a new buffer with room for timestamp + existing + new data
//
NewDataSize = GetTimeStampSize () + DbDataSize;
NewData = AllocatePool (NewDataSize);
if (NewData == NULL) {
FreePool (DbData);
return EFI_OUT_OF_RESOURCES;
}
//
// Step 5: Build the timestamp at the start of the new buffer
//
BuildTimestamp ((EFI_TIME *)NewData);
//
// Step 6: Copy existing signature data after the timestamp
//
CopyMem (
(UINT8 *)NewData + GetTimeStampSize (),
DbData,
DbDataSize
);
//
// Step 7: Write the updated variable
//
Status = SetVariableWithTimestamp (
L"db",
&gEfiGlobalVariableGuid,
EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS |
EFI_VARIABLE_RUNTIME_ACCESS,
NewDataSize,
NewData
);
DebugPrint (EFI_D_INFO, "Update Db (%r)\n", Status);
FreePool (DbData);
FreePool (NewData);
return Status;
}
/**
Read the current "db" UEFI variable data.
@param[out] Data Pointer to receive the variable data buffer.
@param[out] DataSize Pointer to receive the size of the data.
@retval EFI_SUCCESS The variable was read successfully.
@retval EFI_NOT_FOUND The "db" variable does not exist.
@retval EFI_OUT_OF_RESOURCES Memory allocation failed.
@retval EFI_INVALID_PARAMETER A parameter was NULL.
**/
EFI_STATUS
ReadSignatureDatabase (
OUT VOID **Data,
OUT UINTN *DataSize
)
{
EFI_STATUS Status;
if (Data == NULL || DataSize == NULL) {
return EFI_INVALID_PARAMETER;
}
*Data = NULL;
*DataSize = 0;
//
// Query the "db" variable size
//
Status = gRuntimeServices->GetVariable (
L"db",
&gEfiGlobalVariableGuid,
NULL,
DataSize,
NULL
);
if (Status == EFI_BUFFER_TOO_SMALL) {
*Data = AllocatePool (*DataSize);
if (*Data == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Status = gRuntimeServices->GetVariable (
L"db",
&gEfiGlobalVariableGuid,
NULL,
DataSize,
*Data
);
if (EFI_ERROR (Status)) {
FreePool (*Data);
*Data = NULL;
}
}
return Status;
}
/**
Update the "db" variable with new timestamped data.
This is a complete replacement of the "db" variable, including
a fresh timestamp and the provided signature data.
@param[in] Data Pointer to the new signature data.
@param[in] DataSize Size of the signature data.
@retval EFI_SUCCESS The update succeeded.
@retval EFI_OUT_OF_RESOURCES Memory allocation failed.
@retval EFI_INVALID_PARAMETER Data was NULL.
**/
EFI_STATUS
UpdateSignatureDatabase (
IN VOID *Data,
IN UINTN DataSize
)
{
EFI_STATUS Status;
UINTN DbDataSize;
VOID *DbData;
UINTN NewDataSize;
VOID *NewData;
if (Data == NULL || DataSize == 0) {
return EFI_INVALID_PARAMETER;
}
//
// Step 1: Read the current "db" variable size
//
DbDataSize = 0;
Status = gRuntimeServices->GetVariable (
L"db",
&gEfiGlobalVariableGuid,
NULL,
&DbDataSize,
NULL
);
if (Status != EFI_BUFFER_TOO_SMALL) {
return EFI_NOT_FOUND;
}
//
// Step 2: Allocate and read the current variable data
//
DbData = AllocatePool (DbDataSize);
if (DbData == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Status = gRuntimeServices->GetVariable (
L"db",
&gEfiGlobalVariableGuid,
NULL,
&DbDataSize,
DbData
);
if (EFI_ERROR (Status)) {
FreePool (DbData);
return Status;
}
//
// Step 3: Allocate new buffer with room for timestamp
//
NewDataSize = GetTimeStampSize () + DbDataSize;
NewData = AllocatePool (NewDataSize);
if (NewData == NULL) {
FreePool (DbData);
return EFI_OUT_OF_RESOURCES;
}
//
// Step 4: Build timestamp at the start
//
BuildTimestamp ((EFI_TIME *)NewData);
//
// Step 5: Copy existing data after timestamp
//
CopyMem (
(UINT8 *)NewData + GetTimeStampSize (),
DbData,
DbDataSize
);
//
// Step 6: Write the updated variable
//
Status = SetVariableWithTimestamp (
L"db",
&gEfiGlobalVariableGuid,
EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS |
EFI_VARIABLE_RUNTIME_ACCESS,
NewDataSize,
NewData
);
DebugPrint (EFI_D_INFO, "Update Db (%r)\n", Status);
FreePool (DbData);
FreePool (NewData);
return Status;
}
// ---------------------------------------------------------------------------
// FVB-based Variable Read
// ---------------------------------------------------------------------------
/**
Read a variable through the Firmware Volume Block protocol.
This is an alternative read path for platforms where the variable
data is stored in a firmware volume rather than directly accessible
via GetVariable.
@param[in] FvbHandle Handle supporting the FVB protocol.
@param[out] Data Pointer to receive the read data.
@param[out] DataSize Pointer to receive the data size.
@retval EFI_SUCCESS Read succeeded.
@retval EFI_OUT_OF_RESOURCES Memory allocation failed.
@retval EFI_INVALID_PARAMETER A parameter was NULL.
**/
EFI_STATUS
ReadVariableFromFvb (
IN EFI_HANDLE FvbHandle,
OUT VOID **Data,
OUT UINTN *DataSize
)
{
EFI_STATUS Status;
EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;
UINTN Size;
VOID *Buffer;
if (Data == NULL || DataSize == NULL) {
return EFI_INVALID_PARAMETER;
}
*Data = NULL;
*DataSize = 0;
//
// Get the FVB protocol on the given handle
//
Status = gBootServices->HandleProtocol (
FvbHandle,
&mFirmwareVolumeBlockProtocolGuid,
(VOID **)&Fvb
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Query FVB size
//
Size = 0;
Status = Fvb->Read (Fvb, 0, &Size, NULL);
if (Status != EFI_BUFFER_TOO_SMALL) {
return Status;
}
//
// Allocate buffer and read
//
Buffer = AllocatePool (Size);
if (Buffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Status = Fvb->Read (Fvb, 0, &Size, Buffer);
if (EFI_ERROR (Status)) {
FreePool (Buffer);
return Status;
}
*Data = Buffer;
*DataSize = Size;
return EFI_SUCCESS;
}
/**
Free a buffer allocated by ReadVariableFromFvb.
@param[in] Data Pointer to the buffer to free.
**/
VOID
FreeFvbReadBuffer (
IN VOID *Data
)
{
if (Data != NULL) {
FreePool (Data);
}
}
// ---------------------------------------------------------------------------
// Driver Entry Point
// ---------------------------------------------------------------------------
/**
The module entry point for the AMI Device Guard API DXE driver.
This function is called by the UEFI core after the DXE driver is loaded.
It initialises global table pointers, sets up the HOB list cache, and
installs the Device Guard API protocol onto a new handle.
Protocol Interfaces Installed:
- gAmiDeviceGuardApiProtocolGuid (AMI_DEVICE_GUARD_API_PROTOCOL)
@param[in] ImageHandle The firmware-allocated handle for this image.
@param[in] SystemTable A pointer to the EFI System Table.
@retval EFI_SUCCESS The driver initialised successfully.
@retval EFI_OUT_OF_RESOURCES Insufficient resources to install the protocol.
@retval EFI_INVALID_PARAMETER ImageHandle or SystemTable was NULL.
**/
EFI_STATUS
EFIAPI
DeviceGuardEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
EFI_BOOT_SERVICES *BootServices;
EFI_RUNTIME_SERVICES *RuntimeServices;
EFI_HANDLE NewHandle;
//
// Cache global table pointers.
// This mirrors the standard UEFI BootServicesTableLib initialisation.
//
gImageHandle = ImageHandle;
if (ImageHandle == NULL) {
DebugAssert (
"e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
51,
"gImageHandle != ((void *) 0)"
);
return EFI_INVALID_PARAMETER;
}
gSystemTable = SystemTable;
if (SystemTable == NULL) {
DebugAssert (
"e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
57,
"gST != ((void *) 0)"
);
return EFI_INVALID_PARAMETER;
}
gBootServices = SystemTable->BootServices;
if (gBootServices == NULL) {
DebugAssert (
"e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
63,
"gBS != ((void *) 0)"
);
return EFI_INVALID_PARAMETER;
}
gRuntimeServices = SystemTable->RuntimeServices;
if (gRuntimeServices == NULL) {
DebugAssert (
"e:\\hs\\MdePkg\\Library\\UefiRuntimeServicesTableLib\\UefiRuntimeServicesTableLib.c",
47,
"gRT != ((void *) 0)"
);
return EFI_INVALID_PARAMETER;
}
//
// Initialise the HOB list (required for variable services)
//
HobLibInit ();
//
// Cache runtime/BS pointers if not already set in the global init above
//
if (SystemTable->RuntimeServices == NULL) {
BootServices = gBootServices;
} else {
gBootServices = SystemTable->BootServices;
gRuntimeServices = SystemTable->RuntimeServices;
}
//
// Create a new handle and install the Device Guard API protocol.
// The protocol is a singleton interface for managing the "db" variable.
//
NewHandle = NULL;
mProtocolInstalled = 1;
Status = gBootServices->InstallMultipleProtocolInterfaces (
&NewHandle,
&gAmiDeviceGuardApiProtocolGuid,
&mProtocolInstalled,
NULL
);
return Status;
}
/**
Wrapper for memset-like operation with 16-bit pattern fill.
Fills a buffer with a 2-byte pattern repeated to fill the buffer.
@param[in] Buffer Pointer to the buffer to fill.
@param[in] Size Size of the buffer in bytes.
@param[in] Value 16-bit value to fill with.
@return Pointer to the buffer.
**/
VOID *
SetMem16 (
OUT VOID *Buffer,
IN UINTN Size,
IN UINT16 Value
)
{
UINT8 *ByteBuffer;
UINT32 *WordBuffer;
UINT32 WordValue;
UINTN Index;
UINTN AlignAdjust;
ByteBuffer = (UINT8 *)Buffer;
//
// Expand 16-bit value to 32-bit pattern
//
WordValue = Value | ((UINT32)Value << 16);
if (Size >= 4) {
//
// Handle misalignment
//
AlignAdjust = (4 - ((UINTN)ByteBuffer & 3)) & 3;
if (AlignAdjust > Size) {
AlignAdjust = Size;
}
if (AlignAdjust != 0) {
for (Index = 0; Index < AlignAdjust; Index++) {
*ByteBuffer++ = ((UINT8 *)&Value)[Index & 1];
}
Size -= AlignAdjust;
}
//
// Fill 4 bytes at a time
//
WordBuffer = (UINT32 *)ByteBuffer;
for (Index = Size >> 2; Index != 0; Index--) {
*WordBuffer++ = WordValue;
}
Size &= 3;
ByteBuffer = (UINT8 *)WordBuffer;
}
//
// Fill remaining bytes
//
for (Index = 0; Index < Size; Index++) {
*ByteBuffer++ = ((UINT8 *)&Value)[Index & 1];
}
return Buffer;
}
/**
CopyMem with interrupt preservation.
Copies memory from source to destination, preserving the interrupt
flag state across the copy operation.
@param[in] Destination Pointer to the destination buffer.
@param[in] Source Pointer to the source buffer.
@param[in] Length Number of bytes to copy.
@return Destination pointer.
**/
VOID *
CopyMemInterruptSafe (
OUT VOID *Destination,
IN CONST VOID *Source,
IN UINTN Length
)
{
BOOLEAN InterruptState;
UINT8 *DestBytes;
CONST UINT8 *SrcBytes;
UINTN AlignDelta;
UINTN Index;
UINT64 *Dest64;
CONST UINT64 *Src64;
UINTN Count64;
//
// Save interrupt state and disable interrupts
//
InterruptState = SaveAndDisableInterrupts ();
DestBytes = (UINT8 *)Destination;
SrcBytes = (CONST UINT8 *)Source;
//
// Handle overlapping regions where source < dest
// by copying from the end.
//
if ((UINTN)Source < (UINTN)Destination &&
(UINTN)Source + Length > (UINTN)Destination)
{
//
// Overlapping: copy backwards
//
SrcBytes = (CONST UINT8 *)Source + Length;
DestBytes = (UINT8 *)Destination + Length;
if (Length >= 8 && (UINTN)((INTN)SrcBytes - (INTN)DestBytes) >= 8) {
//
// Align destination
//
AlignDelta = (UINTN)DestBytes & 7;
if (AlignDelta != 0) {
SrcBytes--;
DestBytes--;
Length--;
}
//
// Copy 8 bytes at a time
//
Count64 = Length >> 3;
Src64 = (CONST UINT64 *)SrcBytes - Count64;
Dest64 = (UINT64 *)DestBytes - Count64;
for (Index = 0; Index < Count64; Index++) {
Dest64[Index] = Src64[Index];
}
Length &= 7;
DestBytes = (UINT8 *)(Dest64 + Count64);
SrcBytes = (CONST UINT8 *)(Src64 + Count64);
}
//
// Copy remaining bytes backwards
//
while (Length-- != 0) {
*--DestBytes = *--SrcBytes;
}
}
else
{
//
// Non-overlapping or dest < source: copy forwards
//
if (Length >= 8 && (UINTN)((INTN)DestBytes - (INTN)SrcBytes) >= 8) {
//
// Align source and destination
//
AlignDelta = ((UINTN)DestBytes & 7);
if (AlignDelta == ((UINTN)SrcBytes & 7) && AlignDelta != 0) {
if (AlignDelta > Length) {
AlignDelta = Length;
}
CopyMem (DestBytes, SrcBytes, AlignDelta);
DestBytes += AlignDelta;
SrcBytes += AlignDelta;
Length -= AlignDelta;
}
//
// Copy 8 bytes at a time
//
Count64 = Length >> 3;
Dest64 = (UINT64 *)DestBytes;
Src64 = (CONST UINT64 *)SrcBytes;
for (Index = 0; Index < Count64; Index++) {
Dest64[Index] = Src64[Index];
}
Length &= 7;
DestBytes = (UINT8 *)(Dest64 + Count64);
SrcBytes = (CONST UINT8 *)(Src64 + Count64);
}
//
// Copy remaining bytes
//
while (Length-- != 0) {
*DestBytes++ = *SrcBytes++;
}
}
//
// Restore interrupt state
//
RestoreInterrupts (InterruptState);
return Destination;
}
// ---------------------------------------------------------------------------
// Protocol Method Implementation (for the installed protocol structure)
// ---------------------------------------------------------------------------
/**
Implementation of ReadSignatureDatabase service (protocol method).
@param[out] Data Pointer to the variable data buffer.
@param[out] DataSize Size of the variable data.
@retval EFI_SUCCESS The variable was read successfully.
**/
EFI_STATUS
EFIAPI
ReadSignatureDatabaseImpl (
OUT VOID **Data,
OUT UINTN *DataSize
)
{
return ReadSignatureDatabase (Data, DataSize);
}
/**
Implementation of FindCertificateBySubject service (protocol method).
@param[in] SubjectName The subject name to search for.
@param[out] CertPtr On success, pointer to the matching
EFI_SIGNATURE_DATA entry.
@retval EFI_SUCCESS Certificate found.
@retval EFI_NOT_FOUND Certificate not found.
**/
EFI_STATUS
EFIAPI
FindCertificateBySubjectImpl (
IN CONST CHAR8 *SubjectName,
OUT EFI_SIGNATURE_DATA **CertPtr
)
{
return FindCertificateBySubject (SubjectName, CertPtr);
}
/**
Implementation of AppendSignatureEntry service (protocol method).
@param[in] Data Pointer to the signature data to append.
@param[in] DataSize Size of the signature data.
@retval EFI_SUCCESS The signature was appended.
**/
EFI_STATUS
EFIAPI
AppendSignatureEntryImpl (
IN VOID *Data,
IN UINTN DataSize
)
{
return AppendSignatureEntry (Data, DataSize);
}
/**
Implementation of UpdateSignatureDatabase service (protocol method).
@param[in] Data Pointer to the new signature data.
@param[in] DataSize Size of the signature data.
@retval EFI_SUCCESS The database was updated.
**/
EFI_STATUS
EFIAPI
UpdateSignatureDatabaseImpl (
IN VOID *Data,
IN UINTN DataSize
)
{
return UpdateSignatureDatabase (Data, DataSize);
}
//
// The AMI_DEVICE_GUARD_API_PROTOCOL instance
//
AMI_DEVICE_GUARD_API_PROTOCOL gAmiDeviceGuardApiProtocol = {
ReadSignatureDatabaseImpl,
FindCertificateBySubjectImpl,
AppendSignatureEntryImpl,
UpdateSignatureDatabaseImpl
};