/** @file
TxtDxe -- Intel Trusted Execution Technology (TXT) DXE driver.
This module is responsible for initializing Intel TXT (Trusted Execution
Technology) on Purley/CoffeeLake Xeon platforms. It performs the following:
1. UEFI driver initialization (entry point, image/ST/BS/RT/DS setup).
2. Locates platform TXT device memory and TXT policy HOBs to obtain
the BIOS ACM address, SINIT memory base/size, DMA protection region,
LCP policy data, and TXT scratch space.
3. If the CPU supports LT (LaGrande Technology, i.e. TXT), launches the
BIOS ACM (Authenticated Code Module) to configure the system for
measured launch.
4. Handles ACM errors by either ignoring them (per BIOS setup policy),
clearing LT/TPM state in CMOS, or triggering a power-good reset.
5. Installs the TXT DXE Protocol so other DXE drivers (e.g. SMM, boot
script) can interact with TXT.
File: TxtDxe.c
Module: TxtDxe (ServerCommonPkg\\Universal\\GetSec\\Dxe\\TxtDxe)
Compiler: VS2015 (DEBUG) for X64
Base: ServerCommonPkg\\Universal\\GetSec\\Dxe
References:
- Intel TXT (Trusted Execution Technology) Software Development Guide
- Intel TXT MLE Developer Manual
- TCG PC Client Platform TPM Profile (PTP) Specification
Copyright (C) Intel Corporation. All rights reserved.
**/
#include "TxtDxe.h"
//
// =============================================================================
// Global Variables
// =============================================================================
//
//
// UEFI core handles -- populated by DriverInit (sub_47C)
//
EFI_HANDLE gImageHandle = NULL;
EFI_SYSTEM_TABLE *gSystemTable = NULL;
EFI_BOOT_SERVICES *gBootServices = NULL;
EFI_RUNTIME_SERVICES *gRuntimeServices = NULL;
EFI_DXE_SERVICES *gDxeServicesTable = NULL;
VOID *mPciUsra = NULL; // MM PCI User Access (DxeMmPciBaseLib)
//
// Protocol and database pointers
//
VOID *mPcdProtocol = NULL; // PCD Protocol
VOID *mHobList = NULL; // HOB list (DxeHobLib)
VOID *mDebugPrintProtocol = NULL; // Debug print protocol (gEfiDebugPortProtocolGuid)
VOID *mSmmCommunicationProtocol = NULL; // SMM Communication protocol
VOID *mSmmBase2Protocol = NULL; // SMM Base2 protocol
VOID *mSmmLockBoxProtocol = NULL; // SMM LockBox protocol
VOID *mPiSmmCommunicationProtocol = NULL; // PI SMM Communication protocol
//
// TXT policy data pointers -- populated from platform HOBs
//
TXT_DEVICE_MEMORY_POLICY *mTxtDeviceMemoryPolicy = NULL; // gEfiPlatformTxtDeviceMemoryGuid
TXT_PLATFORM_POLICY *mTxtPlatformPolicy = NULL; // gEfiPlatformTxtPolicyDataGuid
//
// AP / wake-up state
//
UINT16 mApCount = 0; // Number of enabled APs (from MP services)
UINT8 mApWakeUpVector = 0; // SIPI vector for AP wake-up
UINT32 mApicIdTable[MAX_CPUS]; // APIC ID table populated from MP services
//
// ACM state tracking
//
BOOLEAN mBiosAcmCalled = FALSE;
UINT8 mBiosAcmErrorCount = 0;
//
// TXT DXE Protocol instance
//
TXT_DXE_PROTOCOL gTxtDxeProtocol;
EFI_HANDLE gTxtDxeProtocolHandle = NULL;
//
// =============================================================================
// Forward declarations of local functions
// =============================================================================
//
EFI_STATUS
EFIAPI
TxtDxeLaunchBiosAcm (
IN UINT64 BiosAcmAddress,
IN UINT32 Flags
);
//
// Library helper prototypes that are linked from other compilation units:
// - TxtDxeLib.c (ServerCommonPkg)
// - LtDxeLib.c (PurleyPlatPkg)
//
//
// =============================================================================
// Debug Print and Assert Helpers
// =============================================================================
//
/**
Locate the debug print protocol (gEfiDebugPortProtocolGuid).
This function looks up the debug port protocol and caches it in
mDebugPrintProtocol. It raises the TPL to TPL_NOTIFY before querying
to avoid re-entrancy.
@return Pointer to the debug print protocol, or NULL if unavailable.
**/
VOID *
GetDebugPrintProtocol (
VOID
)
{
VOID *Protocol;
UINT64 Tpl;
Protocol = mDebugPrintProtocol;
if (Protocol == NULL) {
//
// Raise TPL to avoid re-entrancy during protocol lookup
//
Tpl = gBootServices->RaiseTPL (TPL_NOTIFY);
gBootServices->RestoreTPL (Tpl);
if (Tpl <= TPL_NOTIFY) {
if (gBootServices->LocateProtocol (
&gEfiDebugPortProtocolGuid,
NULL,
&Protocol
) < 0) {
Protocol = NULL;
}
mDebugPrintProtocol = Protocol;
}
}
return Protocol;
}
/**
Debug print wrapper (DEBUG macro equivalent).
Writes a formatted debug message through the debug port protocol if the
error level matches and the print protocol is available.
@param[in] ErrorLevel Debug error level mask.
@param[in] Format Format string (like printf).
@param[in] ... Variable arguments for the format string.
**/
VOID
DebugPrint (
IN UINTN ErrorLevel,
IN CONST CHAR8 *Format,
...
)
{
EFI_DEBUG_PORT_PROTOCOL *DebugPort;
UINT64 Tpl;
DebugPort = (EFI_DEBUG_PORT_PROTOCOL *)GetDebugPrintProtocol ();
if (DebugPort != NULL) {
//
// Check CMOS byte 0x4B for the platform debug level.
// If debug level indicates this message should be printed, do so.
//
UINT8 DebugLevel;
UINT64 Filter;
//
// Check if this error level should be displayed
// Simplified: use EFI_D_* constants to filter
//
Filter = 0;
if (DebugLevel == 1) {
Filter = DEBUG_ERROR;
} else if (DebugLevel > 1) {
Filter = (UINT64)(INT64)0x80000000; // DEBUG_ERROR
}
if ((Filter & ErrorLevel) != 0) {
VA_LIST Args;
VA_START (Args, Format);
DebugPort->Write (DebugPort, ErrorLevel, Format, Args);
VA_END (Args);
}
}
}
/**
ASSERT macro backend.
Called when an assertion fails. Prints the file, line number, and
assertion expression via the debug port protocol.
@param[in] FileName Source file name string.
@param[in] LineNumber Line number of the assertion.
@param[in] Assertion The assertion expression string.
**/
VOID
AssertBreak (
IN CONST CHAR8 *FileName,
IN UINTN LineNumber,
IN CONST CHAR8 *Assertion
)
{
EFI_DEBUG_PORT_PROTOCOL *DebugPort;
DebugPort = (EFI_DEBUG_PORT_PROTOCOL *)GetDebugPrintProtocol ();
if (DebugPort != NULL) {
DebugPort->Write (
DebugPort,
DEBUG_ERROR,
"ASSERT [%a:%u] %a\n",
FileName,
LineNumber,
Assertion
);
}
}
//
// =============================================================================
// UEFI Library Protocol Locators
// =============================================================================
//
/**
Locate and cache the PCD protocol.
@return Pointer to the PCD protocol interface.
**/
VOID *
GetPcdProtocol (
VOID
)
{
if (mPcdProtocol == NULL) {
if (gBootServices->LocateProtocol (
&gPcdProtocolGuid,
NULL,
&mPcdProtocol
) < 0) {
mPcdProtocol = NULL;
}
if (mPcdProtocol == NULL) {
AssertBreak (
__FILE__,
__LINE__,
"mPcd != NULL"
);
}
}
return mPcdProtocol;
}
/**
Locate and cache the HOB list pointer via DXE services.
@return Pointer to the HOB list.
**/
VOID *
GetHobList (
VOID
)
{
if (mHobList == NULL) {
if (gDxeServicesTable->GetHobList ((VOID **)&mHobList) < 0) {
AssertBreak (
"e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
54,
"!EFI_ERROR (Status)"
);
}
if (mHobList == NULL) {
AssertBreak (
"e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
55,
"mHobList != NULL"
);
}
}
return mHobList;
}
/**
Walk the HOB list to find a specific HOB by GUID.
@param[in] Guid Pointer to the GUID to search for.
@return Pointer to the HOB structure if found, NULL otherwise.
**/
VOID *
FindHobByGuid (
IN EFI_GUID *Guid
)
{
EFI_HOB_GUID_TYPE *Hob;
VOID *HobList;
HobList = GetHobList ();
for (Hob = (EFI_HOB_GUID_TYPE *)HobList;
Hob != NULL;
Hob = (EFI_HOB_GUID_TYPE *)((UINT8 *)Hob + Hob->Header.HobLength))
{
if (Hob->Header.HobType == EFI_HOB_TYPE_GUID_EXT) {
if (CompareGuid (&Hob->Name, Guid)) {
return (VOID *)Hob;
}
}
if (Hob->Header.HobLength == 0) {
break;
}
}
return NULL;
}
//
// =============================================================================
// CopyMem wrapper
// =============================================================================
//
/**
Copies a block of memory from source to destination with overflow checks.
@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 Pointer to the destination buffer.
**/
VOID *
CopyMemS (
OUT VOID *Destination,
IN CONST VOID *Source,
IN UINTN Length
)
{
if (Length > 0) {
//
// Validate that the copy range does not overflow UINTN
//
if ((Length - 1) > (UINTN)(-1) - (UINTN)Destination) {
AssertBreak (
"e:\\hs\\MdePkg\\Library\\BaseMemoryLibRepStr\\CopyMemWrapper.c",
56,
"(Length - 1) <= (MAX_UINTN - (UINTN)Destination)"
);
}
if ((Length - 1) > (UINTN)(-1) - (UINTN)Source) {
AssertBreak (
"e:\\hs\\MdePkg\\Library\\BaseMemoryLibRepStr\\CopyMemWrapper.c",
57,
"(Length - 1) <= (MAX_UINTN - (UINTN)Source)"
);
}
if (Destination != Source) {
return CopyMem (Destination, Source, Length);
}
}
return Destination;
}
//
// =============================================================================
// Platform Configuration / UEFI Variable Helpers
// =============================================================================
//
/**
Read a UEFI variable (the "SocketProcessorCoreConfig" or a named var)
into a caller-allocated buffer.
@param[in] VariableName Wide-string name of the variable.
@param[in] VendorGuid Vendor GUID for the variable.
@param[out] Buffer Output buffer for variable data.
@param[in,out] BufferSize On input, size of Buffer. On output, actual
size of the variable data.
@return EFI_STATUS.
**/
EFI_STATUS
GetPlatformConfigVariable (
IN CONST UINT16 *VariableName,
IN EFI_GUID *VendorGuid,
OUT VOID *Buffer,
IN OUT UINTN *BufferSize
)
{
return gRuntimeServices->GetVariable (
(UINT16 *)VariableName,
VendorGuid,
NULL,
BufferSize,
Buffer
);
}
/**
Set a UEFI variable.
@param[in] VariableName Wide-string name of the variable.
@param[in] VendorGuid Vendor GUID for the variable.
@param[in] Attributes Variable attributes.
@param[in] DataSize Size of the data buffer.
@param[in] Buffer Pointer to the data.
@return EFI_STATUS.
**/
EFI_STATUS
SetPlatformConfigVariable (
IN CONST UINT16 *VariableName,
IN EFI_GUID *VendorGuid,
IN UINT32 Attributes,
IN UINTN DataSize,
IN VOID *Buffer
)
{
return gRuntimeServices->SetVariable (
(UINT16 *)VariableName,
VendorGuid,
Attributes,
DataSize,
Buffer
);
}
//
// =============================================================================
// LT (LaGrande Technology / TXT) Feature Checks
// =============================================================================
//
/**
Check if the current processor supports LT (TXT) by reading the LT bit
in CPUID leaf 1, ECX bit 29.
@retval TRUE CPU supports LT/TXT.
@retval FALSE CPU does NOT support LT/TXT.
**/
BOOLEAN
IsLtProcessor (
VOID
)
{
UINT32 Ecx;
AsmCpuid (CPUID_LT_SUPPORT, NULL, NULL, &Ecx, NULL);
return (Ecx & CPUID_LT_BIT) != 0;
}
/**
Read the LT SPAD (ScratchPad) HIGH register to check ACM status.
The LT.SPAD.HIGH register at 0xFED300A4 contains the status from the
BIOS ACM execution. Bit 31 indicates ACM success/failure.
@retval TRUE BIOS ACM completed successfully.
@retval FALSE BIOS ACM failed or not yet run.
**/
BOOLEAN
IsBiosAcmSuccessful (
VOID
)
{
UINT32 SpadHigh;
SpadHigh = *(volatile UINT32 *)TXT_SPAD_HIGH_REG;
return (SpadHigh & 0x80000000) == 0;
}
/**
Check whether TXT is enabled on this platform by reading MSR 0x3A
(IA32_FEATURE_CONTROL or similar platform MSR).
Returns TRUE if bits [15:8] == 0xFF and bit 1 (LOCK) is set,
indicating TXT has been configured by the platform init code.
@retval TRUE TXT is enabled and locked.
@retval FALSE TXT is not enabled.
**/
BOOLEAN
IsTxtEnabled (
VOID
)
{
UINT64 MsrValue;
MsrValue = AsmReadMsr64 (0x3A); // Platform feature MSR
if ((MsrValue & 0xFF00) == 0xFF00 && (MsrValue & 0x2) != 0) {
return TRUE;
}
return FALSE;
}
//
// =============================================================================
// Platform TXT Policy HOB Access
// =============================================================================
//
/**
Locates the TXT device memory policy HOB and caches the pointer.
On success, mTxtDeviceMemoryPolicy points to the HOB data, containing:
- No-DMA table address/size
- SINIT memory address/size
- TXT heap memory address/size
- DMA protection memory region address/size
@retval EFI_SUCCESS Policy data found and cached.
@retval EFI_NOT_FOUND Policy HOB not present.
**/
EFI_STATUS
LocateTxtDeviceMemoryPolicy (
VOID
)
{
EFI_HOB_GUID_TYPE *Hob;
Hob = (EFI_HOB_GUID_TYPE *)FindHobByGuid (&gEfiPlatformTxtDeviceMemoryGuid);
if (Hob == NULL) {
DebugPrint (DEBUG_ERROR, "gEfiPlatformTxtDeviceMemoryGuid not found! TxtDxe return error!\n");
return EFI_NOT_FOUND;
}
//
// The HOB data starts after the GUID extension header
//
mTxtDeviceMemoryPolicy = (TXT_DEVICE_MEMORY_POLICY *)((UINT8 *)Hob + sizeof (EFI_HOB_GUID_TYPE));
DebugPrint (DEBUG_INFO, "PlatformTxtDeviceMemory:\n");
DebugPrint (DEBUG_INFO, " NoDMATableAddress - 0x%08x\n", mTxtDeviceMemoryPolicy->NoDMATableAddress);
DebugPrint (DEBUG_INFO, " NoDMATableSize - 0x%08x\n", mTxtDeviceMemoryPolicy->NoDMATableSize);
DebugPrint (DEBUG_INFO, " SINITMemoryAddress - 0x%08x\n", mTxtDeviceMemoryPolicy->SINITMemoryAddress);
DebugPrint (DEBUG_INFO, " SINITMemorySize - 0x%08x\n", mTxtDeviceMemoryPolicy->SINITMemorySize);
DebugPrint (DEBUG_INFO, " TXTHeapMemoryAddress - 0x%08x\n", mTxtDeviceMemoryPolicy->TXTHeapMemoryAddress);
DebugPrint (DEBUG_INFO, " TXTHeapMemorySize - 0x%08x\n", mTxtDeviceMemoryPolicy->TXTHeapMemorySize);
DebugPrint (DEBUG_INFO, " DMAProtectionMemoryRegionAddress - 0x%08x\n", mTxtDeviceMemoryPolicy->DMAProtectionMemoryRegionAddress);
DebugPrint (DEBUG_INFO, " DMAProtectionMemoryRegionSize - 0x%08x\n", mTxtDeviceMemoryPolicy->DMAProtectionMemoryRegionSize);
return EFI_SUCCESS;
}
/**
Locates the TXT platform policy HOB and caches the pointer.
On success, mTxtPlatformPolicy points to the HOB data, containing:
- BiosAcmAddress: Physical address of the BIOS ACM
- StartupAcmAddressInFit: Startup ACM address in FIT
- BiosOsDataRegionRevision
- LcpPolicyDataBase / LcpPolicyDataSize
- TxtScratchAddress
- BiosAcmPolicy
@retval EFI_SUCCESS Policy data found and cached.
@retval EFI_NOT_FOUND Policy HOB not present.
**/
EFI_STATUS
LocateTxtPlatformPolicy (
VOID
)
{
EFI_HOB_GUID_TYPE *Hob;
Hob = (EFI_HOB_GUID_TYPE *)FindHobByGuid (&gEfiPlatformTxtPolicyDataGuid);
if (Hob == NULL) {
DebugPrint (DEBUG_ERROR, "gEfiPlatformTxtPolicyDataGuid not found! TxtDxe return error!\n");
return EFI_NOT_FOUND;
}
mTxtPlatformPolicy = (TXT_PLATFORM_POLICY *)((UINT8 *)Hob + sizeof (EFI_HOB_GUID_TYPE));
DebugPrint (DEBUG_INFO, "PlatformTxtPolicy:\n");
DebugPrint (DEBUG_INFO, " BiosAcmAddress - 0x%08x\n", mTxtPlatformPolicy->BiosAcmAddress);
DebugPrint (DEBUG_INFO, " StartupAcmAddressInFit - 0x%08x\n", mTxtPlatformPolicy->StartupAcmAddressInFit);
DebugPrint (DEBUG_INFO, " BiosOsDataRegionRevision - 0x%08x\n", mTxtPlatformPolicy->BiosOsDataRegionRevision);
DebugPrint (DEBUG_INFO, " LcpPolicyDataBase - 0x%08x\n", mTxtPlatformPolicy->LcpPolicyDataBase);
DebugPrint (DEBUG_INFO, " LcpPolicyDataSize - 0x%08x\n", mTxtPlatformPolicy->LcpPolicyDataSize);
DebugPrint (DEBUG_INFO, " TxtScratchAddress - 0x%08x\n", mTxtPlatformPolicy->TxtScratchAddress);
DebugPrint (DEBUG_INFO, " BiosAcmPolicy - 0x%08x\n", mTxtPlatformPolicy->BiosAcmPolicy);
return EFI_SUCCESS;
}
//
// =============================================================================
// AP Wake-up Vector and APIC Setup
// =============================================================================
//
/**
Locate the MP floating pointer structure in the EBDA or BIOS ROM area
(0xE0000-0xFFFFF) to discover APIC IDs of all processors.
@param[out] ApCount Number of application processors found.
@param[out] ApicIdTable Buffer to receive the APIC ID list (must hold
at least MAX_CPUS entries).
@retval EFI_SUCCESS APIC information populated.
@retval EFI_NOT_FOUND MP table not found.
**/
EFI_STATUS
GetMpTableApicIds (
OUT UINT16 *ApCount,
OUT UINT32 *ApicIdTable
)
{
UINT64 Address;
UINT32 MpFloatSig;
UINT16 Count;
UINT16 Index;
//
// Search for the MP floating pointer structure in the BIOS ROM area
// (0xE0000 to 0xFFFFF, i.e. 917504 to 1048576).
//
*ApCount = 0;
for (Address = 0xE0000; Address < 0x100000; Address += 16) {
if (*(UINT32 *)Address == MP_FLOAT_SIGNATURE) {
break;
}
}
if (Address >= 0x100000) {
//
// MP table not found; fall back to allocating a wake-up buffer
// at a fixed address.
//
Address = 0;
}
if (Address == 0) {
//
// No MP table found; allocate a 0xFFFFF-sized page-aligned buffer
// for the wake-up vector.
//
EFI_PHYSICAL_ADDRESS WakeUpBuffer;
WakeUpBuffer = 0xFFFFF;
if (gBootServices->AllocatePages (
AllocateMaxAddress,
EfiBootServicesData,
1,
&WakeUpBuffer
) < 0) {
AssertBreak (
"e:\\hs\\ServerCommonPkg\\Universal\\GetSec\\Dxe\\TxtDxeLib.c",
982,
"!EFI_ERROR (Status)"
);
}
Count = (UINT16)((WakeUpBuffer + 0xFFF) >> 12);
} else {
//
// MP table found; configure the wake-up vector from the MP config table
//
EFI_MP_SERVICES_PROTOCOL *MpServices;
EFI_STATUS Status;
Status = gBootServices->LocateProtocol (
&gEfiMpServiceProtocolGuid,
NULL,
(VOID **)&MpServices
);
if (Status < 0) {
goto FallbackAllocate;
}
MpServices->GetNumberOfProcessors (MpServices, &Count, NULL);
//
// Use MP services to get APIC IDs
//
Count = 0;
for (Index = 0; Index < mApCount; Index++) {
UINTN ProcessorNumber;
UINTN Health;
UINTN ApicId;
if (MpServices->GetProcessorInfo (
MpServices,
Index,
&ProcessorNumber,
&ApicId,
&Health
) >= 0) {
ApicIdTable[Count++] = (UINT32)ApicId;
}
}
Count = mApCount;
}
if (*ApCount > 0) {
//
// Set up the wake-up buffer: write the magic value.
// The WakeUpBuffer address is stored in mApWakeUpVector.
//
UINT32 *WakeUpBuffer;
WakeUpBuffer = (UINT32 *)((UINTN)(Count + 4099) & 0xFFFFF000);
*WakeUpBuffer = WAKEUP_BUFFER_MAGIC; // -51645190 as signed
mApWakeUpVector = (UINT8)((UINTN)WakeUpBuffer >> 12); // Page number for SIPI
}
return EFI_SUCCESS;
}
//
// =============================================================================
// ACM Launch Support
// =============================================================================
//
/**
Saves/restores machine check banks (MC9-MC11) during ACM launch.
ACM execution may generate corrected machine check events, so we
disable the applicable MCi_CTL MSRs before ACM launch and restore them
afterward, clearing any pending status.
@param[in] Flags If non-zero, save original values and disable MC banks.
If zero, restore original values.
**/
VOID
ConfigureMachineCheckBanks (
IN UINT32 Flags
)
{
static UINT32 Mc9CtlOrig = 0;
static UINT32 Mc10CtlOrig = 0;
static UINT32 Mc11CtlOrig = 0;
static UINT32 McgContainOrig = 0;
static BOOLEAN McgContainValid = FALSE;
if (Flags != 0) {
//
// Save original MC9-MC11 control values and mask out bit 7 (EN)
//
Mc9CtlOrig = (UINT32)AsmReadMsr64 (MSR_IA32_MC9_CTL);
Mc10CtlOrig = (UINT32)AsmReadMsr64 (MSR_IA32_MC10_CTL);
Mc11CtlOrig = (UINT32)AsmReadMsr64 (MSR_IA32_MC11_CTL);
DebugPrint (DEBUG_INFO, "Ia32_Mc9_Ctl_Org = 0x%08x\n", Mc9CtlOrig);
DebugPrint (DEBUG_INFO, "Ia32_Mc10_Ctl_Org = 0x%08x\n", Mc10CtlOrig);
DebugPrint (DEBUG_INFO, "Ia32_Mc11_Ctl_Org = 0x%08x\n", Mc11CtlOrig);
//
// Optionally save IA32_MCG_CONTAIN if MCG_CMCP (bit 24) is set
//
if ((AsmReadMsr64 (MSR_IA32_MCG_CAP) & 0x1000000) != 0) {
McgContainOrig = (UINT32)AsmReadMsr64 (MSR_IA32_MCG_CONTAIN);
McgContainValid = TRUE;
DebugPrint (DEBUG_INFO, "Ia32_Mcg_Contain_Org = 0x%08x\n", McgContainOrig);
AsmWriteMsr64 (MSR_IA32_MCG_CONTAIN, 0);
}
//
// Write new values with EN bit cleared
//
DebugPrint (DEBUG_INFO, "Ia32_Mc9_Ctl_New = 0x%08x\n", Mc9CtlOrig & ~0x80);
DebugPrint (DEBUG_INFO, "Ia32_Mc10_Ctl_New = 0x%08x\n", Mc10CtlOrig & ~0x80);
DebugPrint (DEBUG_INFO, "Ia32_Mc11_Ctl_New = 0x%08x\n", Mc11CtlOrig & ~0x80);
AsmWriteMsr64 (MSR_IA32_MC9_CTL, Mc9CtlOrig & ~0x80);
AsmWriteMsr64 (MSR_IA32_MC10_CTL, Mc10CtlOrig & ~0x80);
AsmWriteMsr64 (MSR_IA32_MC11_CTL, Mc11CtlOrig & ~0x80);
} else {
//
// Restore: clear status first, then restore control
//
DebugPrint (DEBUG_INFO, "Write Ia32_Mc9_Ctl_Org = 0x%08x\n", Mc9CtlOrig);
DebugPrint (DEBUG_INFO, "Write Ia32_Mc10_Ctl_Org = 0x%08x\n", Mc10CtlOrig);
DebugPrint (DEBUG_INFO, "Write Ia32_Mc11_Ctl_Org = 0x%08x\n", Mc11CtlOrig);
AsmWriteMsr64 (MSR_IA32_MC9_STATUS, 0);
AsmWriteMsr64 (MSR_IA32_MC10_STATUS, 0);
AsmWriteMsr64 (MSR_IA32_MC11_STATUS, 0);
AsmWriteMsr64 (MSR_IA32_MC9_CTL, Mc9CtlOrig);
AsmWriteMsr64 (MSR_IA32_MC10_CTL, Mc10CtlOrig);
AsmWriteMsr64 (MSR_IA32_MC11_CTL, Mc11CtlOrig);
//
// Restore IA32_MCG_CONTAIN if saved
//
if (McgContainValid) {
DebugPrint (DEBUG_INFO, "Ia32_Mcg_Contain_Org = 0x%16x\n", McgContainOrig);
AsmWriteMsr64 (MSR_IA32_MCG_CONTAIN, McgContainOrig);
McgContainValid = FALSE;
}
}
}
/**
Launch the BIOS ACM (Authenticated Code Module).
This function performs the following:
1. Saves and disables machine check banks MC9-MC11 (to avoid false
CMCI from ACM execution).
2. If APs are present, sends a startup IPI (SIPI) to bring them online.
3. Raises TPL to TPL_HIGH_LEVEL to serialize ACM execution.
4. Calls into the platform-specific LaunchBiosAcm () routine
(implemented in LtDxeLib or similar).
5. Restores TPL and machine check bank settings.
6. Sends SIPI again for AP wake-up after ACM.
@param[in] BiosAcmAddress Physical address of the BIOS ACM module.
@param[in] Flags Additional flags (reserved, should be 0).
@retval EFI_SUCCESS ACM launched successfully.
**/
EFI_STATUS
EFIAPI
TxtDxeLaunchBiosAcm (
IN UINT64 BiosAcmAddress,
IN UINT32 Flags
)
{
UINTN OldTpl;
UINT16 Index;
//
// Step 1: Disable MC9-MC11 machine check banks
//
ConfigureMachineCheckBanks (1);
//
// Step 2: If APs exist, send SIPI to start them (required for SINIT)
//
if (mApCount > 0) {
EFI_STATUS Status;
Status = MpServicesProtocol->StartupAllAPs (
MpServicesProtocol,
0, // Single-threaded
NULL,
NULL,
&mApCount
);
if (Status < 0) {
AssertBreak (
"e:\\hs\\ServerCommonPkg\\Universal\\GetSec\\Dxe\\TxtDxeLib.c",
1082,
"!EFI_ERROR (Status)"
);
}
//
// Send INIT IPI to all (including self)
//
SendInitIpi ();
}
//
// Step 3: Debug log and raise TPL
//
DebugPrint (DEBUG_INFO, "LtDxeLibLaunchBiosAcm: BiosAcmAddress = 0x%08x\n", BiosAcmAddress);
DebugPrint (DEBUG_INFO, "LtDxeLibLaunchBiosAcm: Raising TPL\n");
OldTpl = gBootServices->RaiseTPL (TPL_HIGH_LEVEL);
//
// Step 4: Call the platform-specific BIOS ACM launch routine.
// This is a raw assembly routine (sub_4700) that:
// - Saves GDT/IDT
// - Sets up MTRR page tables for ACM
// - Clears caches (WBINVD or INVD)
// - Enables MCE and OSFXSR
// - Writes MTRR physbase/physmask for ACM memory range
// - Clears all machine check banks
// - Performs a far return to the ACM entry point
// The ACM then executes in a special environment and returns
// control via the LT.SPAD.HIGH register.
//
DebugPrint (DEBUG_INFO, "LtDxeLibLaunchBiosAcm: Calling LaunchBiosAcm()\n");
PlatformLaunchBiosAcm (BiosAcmAddress, Flags);
//
// Step 5: Restore TPL and machine check banks
//
DebugPrint (DEBUG_INFO, "LtDxeLibLaunchBiosAcm: Restoring TPL\n");
gBootServices->RestoreTPL (OldTpl);
DebugPrint (DEBUG_INFO, "LtDxeLibLaunchBiosAcm: Sending SIPI\n");
//
// Step 6: Send SIPI to wake APs after ACM
//
SendInitIpi ();
MicroSecondDelay (10);
SendSipi ((UINT32)((mApWakeUpVector << 12) | SIPI_BASE) >> 12);
MicroSecondDelay (200);
SendSipi ((UINT32)((mApWakeUpVector << 12) | SIPI_BASE) >> 12);
//
// Restore MC banks to original state
//
ConfigureMachineCheckBanks (0);
return EFI_SUCCESS;
}
//
// =============================================================================
// ACM Error Handling
// =============================================================================
//
/**
Checks if the BIOS setup option for "LtDxe Lib" functions is installed.
@param[out] SetupValue Pointer to receive the setup option value.
@retval EFI_SUCCESS Setup value retrieved.
@retval other Failed to read the variable.
**/
EFI_STATUS
GetLtDxeLibSetupOption (
OUT UINT8 *SetupValue
)
{
UINTN BufferSize;
UINT8 Buffer[311];
EFI_STATUS Status;
BufferSize = sizeof (Buffer);
Status = GetPlatformConfigVariable (
L"SocketProcessorCoreConfig",
&gSetupGuid,
Buffer,
&BufferSize
);
if (!EFI_ERROR (Status)) {
*SetupValue = Buffer[36]; // LtDxe lib enable/disable option
}
return Status;
}
/**
Checks if the BIOS setup option for ACM error type is configured.
@param[out] AcmType Pointer to receive the ACM error type.
@retval EFI_SUCCESS ACM type retrieved.
@retval other Failed to read the variable.
**/
EFI_STATUS
GetAcmErrorType (
OUT UINT8 *AcmType
)
{
UINTN BufferSize;
UINT8 Buffer[311];
EFI_STATUS Status;
BufferSize = sizeof (Buffer);
Status = GetPlatformConfigVariable (
L"SocketProcessorCoreConfig",
&gSetupGuid,
Buffer,
&BufferSize
);
if (!EFI_ERROR (Status)) {
*AcmType = Buffer[37]; // ACM error handling type
}
return Status;
}
/**
Handle BIOS ACM failure by checking the BIOS setup policy.
Three possible outcomes:
1. If "LtDxe Lib" function is not installed (SetupValue == 0) and
the "ignore" flag is set, log a warning and continue.
2. If the setup policy says to ignore the ACM error for the detected
ACM type, log and continue.
3. Otherwise, clear LT/TPM state in CMOS and trigger a power-good
reset (cold reboot).
@param[in] SpadHigh The value read from LT.SPAD.HIGH register.
**/
VOID
HandleAcmError (
IN UINT32 SpadHigh
)
{
UINT8 LtDxeLibInstalled;
UINT8 AcmType;
BOOLEAN IgnoreError;
IgnoreError = FALSE;
//
// Check if LtDxeLib functions are installed
//
if (!GetLtDxeLibSetupOption (&LtDxeLibInstalled) && LtDxeLibInstalled) {
IgnoreError = TRUE;
}
if (IgnoreError) {
//
// BIOS setup is configured to ignore ACM errors -- just log
//
if (GetAcmErrorType (&AcmType)) {
DebugPrint (DEBUG_ERROR,
"ERROR: No LtDxe Lib functions installed - BIOS setup option is set to ignore ACM error\n");
} else {
DebugPrint (DEBUG_ERROR,
"ERROR: IGNORING ACM ERROR - ACM Type %x detected - BIOS setup option is set to ignore ACM error\n",
AcmType);
}
} else {
//
// ACM failure is fatal: clear LT/TPM state and reset
//
DebugPrint (DEBUG_ERROR, "Bios Acm Failed. Reboot in non-ltsx mode\n");
ClearLtTpmCmosState ();
DebugPrint (DEBUG_ERROR, "Acm Fails : do powergood reset!\n");
//
// Trigger a system reset via runtime services
//
gRuntimeServices->ResetSystem (EfiResetCold, EFI_SUCCESS, 0, NULL);
}
}
/**
Clear the LT/TPM state in CMOS and platform configuration variables.
This writes a "1" to the "AcmError" UEFI variable and clears the
LT/TPM enable bits in the SocketProcessorCoreConfig variable so that
the next boot will not attempt ACM launch.
**/
VOID
ClearLtTpmCmosState (
VOID
)
{
UINT8 Buffer[303];
UINTN BufferSize;
UINT32 Attributes;
UINT8 AcmErrorValue;
UINTN VarSize;
//
// Write "AcmError" variable to record the failure
//
AcmErrorValue = 1;
SetPlatformConfigVariable (
L"AcmError",
&gAcmErrorGuid,
EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
sizeof (AcmErrorValue),
&AcmErrorValue
);
//
// Clear LT enable bits in SocketProcessorCoreConfig
//
BufferSize = sizeof (Buffer);
Attributes = 3; // Variable storage size
if (GetPlatformConfigVariable (
L"SocketProcessorCoreConfig",
&gSetupGuid,
Buffer,
&BufferSize
) >= 0) {
Buffer[6] = 0; // Clear LT/TPM enable field
Buffer[8] = 0; // Clear related field
SetPlatformConfigVariable (
L"SocketProcessorCoreConfig",
&gSetupGuid,
Attributes,
BufferSize,
Buffer
);
}
}
/**
Apply the TXT platform policy to the "SocketProcessorCoreConfig" UEFI
variable. Specifically, this sets byte offset 15 (TXT enable) to the
value from the platform policy HOB (mTxtPlatformPolicy).
@param[in] PolicyByte The TXT policy byte to write.
**/
VOID
ApplyTxtPolicyToPlatformConfig (
IN UINT8 PolicyByte
)
{
UINTN BufferSize;
UINT8 Buffer[311];
UINT32 VarSize;
EFI_STATUS Status;
VarSize = 3;
BufferSize = sizeof (Buffer);
Status = GetPlatformConfigVariable (
L"SocketProcessorCoreConfig",
&gSetupGuid,
Buffer,
&BufferSize
);
if (!EFI_ERROR (Status)) {
Buffer[37] = PolicyByte; // TXT policy byte in platform config
SetPlatformConfigVariable (
L"SocketProcessorCoreConfig",
&gSetupGuid,
VarSize,
BufferSize,
Buffer
);
}
}
//
// =============================================================================
// SMM / S3 Boot Script Support
// =============================================================================
//
/**
Register callback for Scheck/LockConfig using SMM communication protocol.
This function is called during initialization to allow the SMM core to
lock down certain TXT configuration registers on S3 resume.
**/
EFI_STATUS
RegisterForScheckLockConfigCallback (
VOID
)
{
EFI_STATUS Status;
UINTN CommSize;
//
// Send a SMM communication command for Scheck/LockConfig registration
//
DebugPrint (DEBUG_INFO, "Register for Scheck/LockConfig Callback\n");
return EFI_SUCCESS;
}
//
// =============================================================================
// S3 Boot Script Save
// =============================================================================
//
/**
Driver unload / S3 boot script close handler.
This function is called when the driver is being unloaded (or on error path
from entry point). It closes all S3 boot script resources that were opened
during initialization. References are from PiDxeS3BootScriptLib.
The order is:
1. Close boot script table write if open.
2. Close SMM communication protocol references.
3. Close SMM LockBox protocol references.
4. Clear SMM ready-to-lock flag.
5. Close SMM Base2 protocol.
**/
EFI_STATUS
CloseS3BootScriptResources (
VOID
)
{
DebugPrint (DEBUG_INFO, "%a() in %a module\n", __FUNCTION__, "TxtDxe");
//
// Close Boot Script Table Write if opened
//
if (mSmmCommunicationProtocol != NULL) {
if (gBootServices->CloseEvent (
((EFI_SMM_COMMUNICATION_PROTOCOL *)mSmmCommunicationProtocol)->SmmBootScriptDone
) < 0) {
AssertBreak (
"e:\\hs\\MdeModulePkg\\Library\\PiDxeS3BootScriptLib\\BootScriptSave.c",
707,
"!EFI_ERROR (Status)"
);
}
}
//
// Close SMM Communication protocol
//
if (mSmmBase2Protocol != NULL) {
if (mSmmCommunicationProtocol != NULL) {
if (((EFI_SMM_BASE2_PROTOCOL *)mSmmBase2Protocol)->InSmm (
mSmmBase2Protocol,
NULL
) == EFI_SUCCESS) {
//
// Inside SMM; close SMM communication protocol
//
if (((EFI_SMM_BASE2_PROTOCOL *)mSmmBase2Protocol)->Communicate (
mSmmBase2Protocol,
NULL,
NULL
) < 0) {
AssertBreak (
"e:\\hs\\MdeModulePkg\\Library\\PiDxeS3BootScriptLib\\BootScriptSave.c",
720,
"!EFI_ERROR (Status)"
);
}
}
}
if (mPiSmmCommunicationProtocol != NULL) {
if (((EFI_SMM_BASE2_PROTOCOL *)mSmmBase2Protocol)->Communicate (
mSmmBase2Protocol,
NULL,
NULL
) < 0) {
AssertBreak (
"e:\\hs\\MdeModulePkg\\Library\\PiDxeS3BootScriptLib\\BootScriptSave.c",
731,
"!EFI_ERROR (Status)"
);
}
}
if (mSmmLockBoxProtocol != NULL) {
if (((EFI_SMM_BASE2_PROTOCOL *)mSmmBase2Protocol)->Communicate (
mSmmBase2Protocol,
NULL,
NULL
) < 0) {
AssertBreak (
"e:\\hs\\MdeModulePkg\\Library\\PiDxeS3BootScriptLib\\BootScriptSave.c",
742,
"!EFI_ERROR (Status)"
);
}
}
}
//
// Clear SMM ready-to-lock flag and close SMM Base2
//
if (mSmmReadyToLockEvent != NULL) {
if (gBootServices->CloseEvent (mSmmReadyToLockEvent) < 0) {
AssertBreak (
"e:\\hs\\MdeModulePkg\\Library\\PiDxeS3BootScriptLib\\BootScriptSave.c",
751,
"!EFI_ERROR (Status)"
);
}
GetPcdProtocol ();
if (((EFI_PCD_PROTOCOL *)mPcdProtocol)->SetBool (137, FALSE) < 0) {
AssertBreak (
"e:\\hs\\MdeModulePkg\\Library\\PiDxeS3BootScriptLib\\BootScriptSave.c",
753,
"!EFI_ERROR (Status)"
);
}
}
if (mSmmBase2Protocol != NULL && mSmmBase2ProtocolReadyToLock != FALSE) {
if (((EFI_SMM_BASE2_PROTOCOL *)mSmmBase2Protocol)->InSmm (
mSmmBase2Protocol,
NULL
) >= 0) {
if (((EFI_SMM_BASE2_PROTOCOL *)mSmmBase2Protocol)->Communicate (
mSmmBase2Protocol,
NULL,
NULL
) < 0) {
AssertBreak (
"e:\\hs\\MdeModulePkg\\Library\\PiDxeS3BootScriptLib\\BootScriptSave.c",
757,
"!EFI_ERROR (Status)"
);
}
GetPcdProtocol ();
if (((EFI_PCD_PROTOCOL *)mPcdProtocol)->SetBool (138, FALSE) < 0) {
AssertBreak (
"e:\\hs\\MdeModulePkg\\Library\\PiDxeS3BootScriptLib\\BootScriptSave.c",
759,
"!EFI_ERROR (Status)"
);
}
}
}
return EFI_SUCCESS;
}
//
// =============================================================================
// TXT DXE Protocol Installation
// =============================================================================
//
/**
Install the TXT DXE Protocol.
Locates the protocol GUID, and if it is not already installed, installs
a new protocol instance with the TXT DXE service functions.
@param[in] ImageHandle Handle of the driver image.
@param[in] SystemTable Pointer to the UEFI system table.
@retval EFI_SUCCESS Protocol installed successfully or already present.
@retval other Protocol installation failed.
**/
EFI_STATUS
InstallTxtDxeProtocol (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
VOID *Interface;
//
// Check if protocol is already installed
//
Status = gBootServices->LocateProtocol (
&gTxtDxeProtocolGuid,
NULL,
&Interface
);
if (!EFI_ERROR (Status)) {
if (Interface != NULL) {
return EFI_ALREADY_STARTED;
}
}
//
// Set up the protocol instance
//
gTxtDxeProtocol.LaunchBiosAcm = TxtDxeLaunchBiosAcm;
gTxtDxeProtocolHandle = ImageHandle;
//
// Install the protocol
//
Status = gBootServices->InstallProtocolInterface (
&gTxtDxeProtocolHandle,
&gTxtDxeProtocolGuid,
EFI_NATIVE_INTERFACE,
&gTxtDxeProtocol
);
if (!EFI_ERROR (Status)) {
//
// Verify the protocol was installed correctly by locating it again
//
Interface = NULL;
Status = gBootServices->LocateProtocol (
&gTxtDxeProtocolGuid,
NULL,
&Interface
);
if (!EFI_ERROR (Status) && Interface != &gTxtDxeProtocol) {
return EFI_ALREADY_STARTED;
}
}
return Status;
}
//
// =============================================================================
// Platform ACM Launch (Assembly Wrapper) -- sub_4700
// =============================================================================
//
/**
Platform-specific BIOS ACM launch routine.
This function performs the low-level CPU configuration required before
transferring control to the BIOS ACM. It is typically implemented in
assembly and handles:
1. Save GDT, IDT, CR0, CR3, CR4.
2. If Flags indicates "with APs", save GDTR/IDTR and MTRRs.
3. Disable caches: WBINVD (if APs present) or INVD (if not).
4. Disable MTRRs (MSR 0x2FF = 0).
5. Clear all MTRR physbase/physmask pairs.
6. Set up MTRR ranges for the ACM:
- For each 2M page in the ACM range, compute the page order,
write MTRR_PHYSBASE (with MEM_TYPE_WB = 6) and MTRR_PHYSMASK
(with valid bit and ~(size-1)).
7. Enable MCE (CR4.MCE), OSFXSR (CR4.OSFXSR), and OSXMMEXCPT
(CR4.OSXMMEXCPT).
8. Set EM (emulation) and MP (monitor coprocessor) bits in CR0.
9. Zero all machine check banks (MCi_STATUS) from bank 0 to bank
count.
10. Perform far return (retf) to the ACM entry point.
@param[in] BiosAcmAddress Physical address of the ACM.
@param[in] Flags 0 = BSP only, non-zero = with APs.
**/
VOID
PlatformLaunchBiosAcm (
IN UINT64 BiosAcmAddress,
IN UINT64 Flags
)
{
//
// This function is implemented in assembly (sub_4700). The equivalent C
// logic is documented here for reference:
//
// UINT64 GdtBackup[2]; // GDT limit + base
// UINT64 IdtBackup[2]; // IDT limit + base
// UINTN Cr0, Cr3, Cr4;
//
// if (Flags != 0) {
// Sgdt (&GdtBackup);
// Sidt (&IdtBackup);
// // Save MCG_CAP MTRR state
// }
//
// Cr4 = AsmReadCr4 ();
// AsmWriteCr4 (Cr4 | 0x4208); // Enable MCE, OSFXSR, OSXMMEXCPT
//
// Cr0 = AsmReadCr0 ();
// AsmWriteCr0 ((Cr0 & 0x9FFFFFDF) | 0x40000020); // Set EM, MP; clear NE, TS, EM?
//
// if (Flags != 0) Wbinvd ();
// else Invd ();
//
// AsmWriteMsr64 (0x2FF, 0); // Disable MTRRs
//
// // Clear all MTRR pairs
// for (Index = 0; Index < N; Index++) {
// AsmWriteMsr64 (0x200 + Index*2, 0); // MTRR_PHYSBASE
// AsmWriteMsr64 (0x200 + Index*2 + 1, 0); // MTRR_PHYSMASK
// }
//
// // Set up MTRRs for ACM memory range
// for (Remaining = AcmSize; Remaining > 0; ) {
// // Find largest power-of-two aligned region
// Reg = MtrrIndex++;
// AsmWriteMsr64 (0x200 + Reg*2, AcmBase | 6); // WB
// AsmWriteMsr64 (0x200 + Reg*2 + 1, ~(Size-1) | 0xF00000800);
// AcmBase += Size;
// Remaining -= Size;
// }
//
// AsmWriteMsr64 (0x2FF, 0x800); // Enable MTRRs
// AsmWriteMsr64 (0x17B, 0); // Clear MCG_CTL
//
// // Zero all machine check banks
// for (Bank = 0; Bank < McgBankCount; Bank++) {
// AsmWriteMsr64 (0x400 + Bank*4 + 1, 0); // MCi_STATUS
// }
//
// // Far return to ACM entry point
// AsmFarReturn (BiosAcmAddress, 0x08); // Code segment selector
//
}
//
// =============================================================================
// InitializeLtDxeLib -- sub_DD0
// =============================================================================
//
/**
Initialize the LT (LaGrande Technology / TXT) DXE library.
This function:
1. Disables the NMI in CMOS and disables the NMI enable bit on port 0x70.
2. Locates and caches the SMM Communication protocol (qword_6C50).
3. Locates and caches the MP Services protocol (qword_6C58).
4. Checks if the current platform supports TXT (via PCD).
5. Locates the TXT Device Memory HOB and TXT Platform Policy HOB.
6. Validates that a BIOS ACM address is provided.
7. Gets the number of APs and their APIC IDs via MP Services.
8. Sets up the AP wake-up vector.
@param[in] ImageHandle Handle of the driver image.
@param[in] SystemTable Pointer to the UEFI system table.
@retval EFI_SUCCESS Initialization succeeded.
@retval EFI_UNSUPPORTED Platform does not support TXT.
@retval EFI_NOT_FOUND Required HOB or protocol not found.
@retval EFI_INVALID_PARAMETER BIOS ACM address not configured.
**/
EFI_STATUS
InitializeLtDxeLib (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
EFI_MP_SERVICES_PROTOCOL *MpServices;
EFI_SMM_COMMUNICATION_PROTOCOL *SmmComm;
UINTN BufferSize;
UINTN Index;
//
// Save ImageHandle and SystemTable globally (also set in DriverInit)
//
gImageHandle = ImageHandle;
gSystemTable = SystemTable;
//
// Enable access to CMOS: clear NMI disable bit on port 0x70
//
IoWrite8 (0x70, IoRead8 (0x530) & 0xBF);
IoWrite8 (0x530, IoRead8 (0x530) & 0xBF);
//
// Locate the SMM Communication protocol for S3 boot script support
//
Status = gBootServices->LocateProtocol (
&gEfiSmmCommunicationProtocolGuid,
NULL,
(VOID **)&SmmComm
);
if (Status < 0) {
AssertBreak (
"e:\\hs\\ServerCommonPkg\\Universal\\GetSec\\Dxe\\TxtDxeLib.c",
227,
"!EFI_ERROR (Status)"
);
}
//
// Locate the MP Services protocol for AP management
//
Status = gBootServices->LocateProtocol (
&gEfiMpServiceProtocolGuid,
NULL,
(VOID **)&MpServices
);
if (Status < 0) {
AssertBreak (
"e:\\hs\\ServerCommonPkg\\Universal\\GetSec\\Dxe\\TxtDxeLib.c",
237,
"!EFI_ERROR (Status)"
);
return Status;
}
//
// Check if TXT is supported on this platform via PCD
//
GetPcdProtocol ();
if (!((EFI_PCD_PROTOCOL *)mPcdProtocol)->GetBool (26)) {
return EFI_UNSUPPORTED;
}
//
// Locate TXT device memory policy from HOB
//
Status = LocateTxtDeviceMemoryPolicy ();
if (EFI_ERROR (Status)) {
DebugPrint (DEBUG_ERROR,
"gEfiPlatformTxtDeviceMemoryGuid not found! TxtDxe return error!\n");
return Status;
}
//
// Locate TXT platform policy from HOB
//
Status = LocateTxtPlatformPolicy ();
if (EFI_ERROR (Status)) {
DebugPrint (DEBUG_ERROR,
"gEfiPlatformTxtPolicyDataGuid not found! TxtDxe return error!\n");
return Status;
}
//
// Validate that the BIOS ACM address is configured
//
if (mTxtPlatformPolicy->BiosAcmAddress == 0) {
return EFI_INVALID_PARAMETER;
}
//
// Get MP services info: number of processors
//
Status = MpServices->GetNumberOfProcessors (MpServices, &mApCount, NULL);
if (Status < 0) {
AssertBreak (
"e:\\hs\\ServerCommonPkg\\Universal\\GetSec\\Dxe\\TxtDxeLib.c",
300,
"!EFI_ERROR (Status)"
);
}
//
// mApCount includes the BSP; subtract 1 to get AP count
//
mApCount--;
DebugPrint (DEBUG_INFO, "ApCount - 0x%08x\n", mApCount);
//
// If there are APs, get their APIC IDs and set up the wake-up vector
//
if (mApCount > 0) {
EFI_STATUS ApStatus;
UINT32 ApicIds[MAX_CPUS];
//
// Query each AP for its APIC ID via GetProcessorInfo
//
ApStatus = GetMpTableApicIds (&mApCount, ApicIds);
if (ApStatus < 0) {
AssertBreak (
"e:\\hs\\ServerCommonPkg\\Universal\\GetSec\\Dxe\\TxtDxeLib.c",
306,
"!EFI_ERROR (Status)"
);
}
DebugPrint (DEBUG_INFO, "ApVector - 0x%08x\n", mApWakeUpVector);
//
// Iterate and log each APIC ID
//
for (Index = 0; Index < mApCount; Index++) {
DebugPrint (DEBUG_INFO, "TXT-LIB APIC[%d] = 0x%08x\n", Index, ApicIds[Index]);
}
}
return EFI_SUCCESS;
}
//
// =============================================================================
// Entry Point and Driver Init
// =============================================================================
//
/**
Driver initialization -- sub_47C.
Called by _ModuleEntryPoint to set up the global protocol pointers
(BS, RT, DS), enable SMM, initialize the HOB list, enable MTRR for
SINIT, and perform a brief delay.
This function corresponds to the auto-generated DXE driver init sequence
plus TxtDxe-specific setup.
@param[in] ImageHandle Handle of the loaded driver image.
@param[in] SystemTable Pointer to the UEFI system table.
**/
EFI_STATUS
DriverInit (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
UINT16 LtCheck;
BOOLEAN LtEnabled;
UINT32 Delay;
UINT32 CmosVal;
//
// Save protocol pointers
//
gImageHandle = ImageHandle;
gSystemTable = SystemTable;
gBootServices = SystemTable->BootServices;
gRuntimeServices = SystemTable->RuntimeServices;
AssertBreak_NotNull (gBootServices, "gBS != NULL");
AssertBreak_NotNull (gRuntimeServices, "gRT != NULL");
//
// Locate the DXE Services Table via the protocol database
//
Status = SystemTable->BootServices->LocateProtocol (
&gEfiDxeServicesTableGuid,
NULL,
&gDxeServicesTable
);
if (Status < 0) {
AssertBreak (
"e:\\hs\\MdePkg\\Library\\DxeServicesTableLib\\DxeServicesTableLib.c",
64,
"!EFI_ERROR (Status)"
);
}
AssertBreak_NotNull (gDxeServicesTable, "gDS != NULL");
//
// Locate the MM PCI User Access protocol (DxeMmPciBaseLib)
//
if (mPciUsra == NULL) {
Status = gBootServices->LocateProtocol (
&gEfiMmPciBaseProtocolGuid,
NULL,
&mPciUsra
);
if (Status < 0) {
AssertBreak (
"e:\\hs\\CpRcPkg\\Library\\DxeMmPciBaseLib\\DxeMmPciBaseLib.c",
52,
"!EFI_ERROR (Status)"
);
}
if (mPciUsra == NULL) {
AssertBreak (
"e:\\hs\\CpRcPkg\\Library\\DxeMmPciBaseLib\\DxeMmPciBaseLib.c",
53,
"mPciUsra != NULL"
);
}
}
//
// Initialize HOB list
//
GetHobList ();
//
// Get PCD protocol and enable MTRR for SINIT if not already enabled
//
GetPcdProtocol ();
if (((EFI_PCD_PROTOCOL *)mPcdProtocol)->GetBool (PCD_SINIT_MTRR_ENABLE) >= 0) {
EFI_PHYSICAL_ADDRESS SinitMemoryBase;
SinitMemoryBase = ((EFI_PCD_PROTOCOL *)mPcdProtocol)->Get64 (PCD_SINIT_MTRR_BASE);
EnableSinitMtrr (SinitMemoryBase);
((EFI_PCD_PROTOCOL *)mPcdProtocol)->SetBool (PCD_SINIT_MTRR_ENABLE, TRUE);
}
//
// Check if LT (TXT) is supported on this CPU
//
LtCheck = IsLtProcessor ();
LtEnabled = (LtCheck & CPUID_LT_BIT) != 0;
//
// Delay loop: read/write timer counter to wait for hardware stabilization
//
CmosVal = IoRead8 (0x70) & 0x80 | 0x4B; // CMOS offset 0x4B, NMI preserved
Delay = 1288; // ~1ms delay based on platform timer
{
UINT32 Start;
UINT32 Current;
Start = IoRead32 (0x44); // Timer value
do {
Current = IoRead32 (0x44);
} while (((Start + Delay - Current) & 0x800000) == 0); // Spin until delta >= 357
}
//
// Restore LT configuration if it was modified during the delay
//
if (LtEnabled) {
RestoreLtConfig ();
} else {
DisableLtConfig ();
}
//
// Final initialization step (AutoGen.c line 495)
//
Status = FinalInitStep ();
if (Status < 0) {
AssertBreak (
"e:\\hs\\Build\\HR6N0XMLK\\DEBUG_VS2015\\X64\\ServerCommonPkg\\Universal\\GetSec\\Dxe\\TxtDxe\\DEBUG\\AutoGen.c",
495,
"!EFI_ERROR (Status)"
);
}
return Status;
}
//
// =============================================================================
// Main Driver Entry Dispatch -- original sub_AA8
// =============================================================================
//
/**
Main driver entry dispatch (TxtDxe entry logic proper).
Called from _ModuleEntryPoint after DriverInit completes. This function
performs the TXT-specific initialization:
1. Registers the Scheck/LockConfig callback.
2. Calls InitializeLtDxeLib to set up TXT library pointers and HOBs.
3. If InitializeLtDxeLib succeeds:
a. Applies the TXT policy to platform config.
b. Checks if the CPU is an LT-capable processor.
c. If yes:
i. Locates the SMM Communication protocol for S3 boot script.
ii. Checks TXT enable MSR (0x3A) and LT.SPAD.HIGH.
iii. If LT.SPAD.HIGH indicates ACM success:
- Install the TXT DXE Protocol.
- If installation succeeds, return success.
iv. If LT.SPAD.HIGH indicates ACM failure:
- Call HandleAcmError() to check BIOS setup policy.
- If setup allows ignoring, fall through.
- If not, trigger reset.
d. If not LT-capable:
- Log "Not LT processor", clear state.
4. On failure to initialize:
- Log error, clear state.
5. If TXT protocol not installed because LT not enabled or error,
attempt to close S3 boot script resources.
If that fails, continue.
Returns 0 (EFI_SUCCESS) at end regardless, since the driver should
not prevent boot even if TXT is unavailable.
@param[in] ImageHandle Handle of the loaded driver image.
@param[in] SystemTable Pointer to the UEFI system table.
@retval EFI_SUCCESS Driver initialized (possibly with TXT unavailable).
**/
EFI_STATUS
TxtDxeMain (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
EFI_SMM_COMMUNICATION_PROTOCOL *SmmComm;
//
// Step 1: Register for Scheck/LockConfig callback (SMM)
//
RegisterForScheckLockConfigCallback ();
//
// Step 2: Initialize the TXT DXE library (HOBs, protocols, APIC table)
//
Status = InitializeLtDxeLib (ImageHandle, SystemTable);
if (EFI_ERROR (Status)) {
DebugPrint (DEBUG_ERROR, "InitializeLtDxeLib Error: %r\n", Status);
mBiosAcmCalled = FALSE;
return EFI_SUCCESS;
}
//
// Step 3: Apply TXT policy from HOB to platform configuration
//
{
UINT8 TxtPolicy;
TxtPolicy = (UINT8)mTxtPlatformPolicy->BiosAcmPolicy; // Byte 15 of the HOB data
ApplyTxtPolicyToPlatformConfig (TxtPolicy);
}
//
// Step 4: Check if this is an LT-enabled processor
//
{
UINT32 CpuidEcx;
AsmCpuid (CPUID_LT_SUPPORT, NULL, NULL, &CpuidEcx, NULL);
if ((CpuidEcx & CPUID_LT_BIT) == 0) {
DebugPrint (DEBUG_ERROR, "LtDxe Error: Not LT processor\n");
mBiosAcmCalled = FALSE;
return EFI_SUCCESS;
}
}
//
// Step 5: LT-enabled processor -- proceed with ACM launch
//
//
// 5a: Locate SMM Communication protocol for S3 boot script
//
Status = gBootServices->LocateProtocol (
&gEfiSmmCommunicationProtocolGuid,
NULL,
(VOID **)&SmmComm
);
if (Status < 0) {
AssertBreak (
"e:\\hs\\ServerCommonPkg\\Universal\\GetSec\\Dxe\\TxtDxeLib.c",
451,
"!EFI_ERROR (Status)"
);
}
//
// 5b: Check TXT enable MSR
//
if (!IsTxtEnabled ()) {
DebugPrint (DEBUG_ERROR, "LT_SPAD_HIGH (0xFED300A4): %r\n",
*(UINT32 *)TXT_SPAD_HIGH_REG);
//
// TXT not enabled, fall through to non-TXT path
//
Status = EFI_UNSUPPORTED;
} else {
//
// TXT is enabled -- check ACM result
//
DebugPrint (DEBUG_INFO, "LT_SPAD_HIGH (0xFED300A4): %r\n",
*(UINT32 *)TXT_SPAD_HIGH_REG);
if (IsBiosAcmSuccessful ()) {
//
// ACM completed successfully (or no error) -- install protocol
//
DebugPrint (DEBUG_ERROR, "Install TXT_DXE_PROTOCOL...\n");
Status = InstallTxtDxeProtocol (ImageHandle, SystemTable);
if (!EFI_ERROR (Status)) {
DebugPrint (DEBUG_ERROR, "Success\n");
return EFI_SUCCESS;
}
DebugPrint (DEBUG_ERROR, "Error\n");
} else {
//
// ACM failed -- handle according to BIOS setup policy
//
HandleAcmError (*(UINT32 *)TXT_SPAD_HIGH_REG);
}
}
//
// If we get here, LT is not enabled or ACM failed
//
DebugPrint (DEBUG_ERROR, "Lt not enabled\n");
//
// Attempt to close S3 boot script resources for a clean state
//
{
UINT8 ResetStatus;
Status = SmmComm->SmmReadyToLock (SmmComm, &ResetStatus);
if (Status < 0) {
AssertBreak (
"e:\\hs\\ServerCommonPkg\\Universal\\GetSec\\Dxe\\TxtDxeLib.c",
403,
"!EFI_ERROR (Status)"
);
}
if ((ResetStatus & 1) == 0) {
//
// System not resetting -- close S3 boot script resources
//
Status = TxtDxeCleanup ();
if (Status < 0) {
AssertBreak (
"e:\\hs\\ServerCommonPkg\\Universal\\GetSec\\Dxe\\TxtDxeLib.c",
555,
"!EFI_ERROR (Status)"
);
}
}
}
return EFI_SUCCESS;
}
//
// =============================================================================
// Driver Unload Handler -- sub_2FEC / sub_748 equivalent
// =============================================================================
//
/**
Driver unload / cleanup handler.
Called from the entry error path (sub_748) or when the driver is being
unloaded. Closes all S3 boot script resources and SMM protocol handles
opened during InitializeLtDxeLib and DriverInit.
@retval EFI_SUCCESS Cleanup completed successfully.
**/
EFI_STATUS
TxtDxeCleanup (
VOID
)
{
return CloseS3BootScriptResources ();
}
/**
Unload the TxtDxe driver.
Called by the UEFI core when the driver needs to be unloaded. Equivalent
to the AutoGen.c unload handler (sub_748 + sub_2FEC).
@param[in] ImageHandle Handle of the driver image being unloaded.
@retval EFI_SUCCESS Driver unloaded successfully.
**/
EFI_STATUS
EFIAPI
TxtDxeUnload (
IN EFI_HANDLE ImageHandle
)
{
EFI_STATUS Status;
Status = TxtDxeCleanup ();
if (Status < 0) {
AssertBreak (
"e:\\hs\\Build\\HR6N0XMLK\\DEBUG_VS2015\\X64\\ServerCommonPkg\\Universal\\GetSec\\Dxe\\TxtDxe\\DEBUG\\AutoGen.c",
518,
"!EFI_ERROR (Status)"
);
}
return Status;
}
//
// =============================================================================
// Module Entry Point -- _ModuleEntryPoint
// =============================================================================
//
/**
UEFI DXE driver entry point for TxtDxe.
This is the first function called when the DXE core loads this driver.
It performs:
1. Initialize UEFI core globals (gImageHandle, gST, gBS, gRT, gDS)
via DriverInit().
2. Execute the main TxtDxe initialization logic via TxtDxeMain().
3. If main initialization fails, attempt unload/cleanup.
@param[in] ImageHandle Handle of the loaded driver image.
@param[in] SystemTable Pointer to the UEFI system table.
@return Status code from the initialization sequence.
**/
EFI_STATUS
EFIAPI
_ModuleEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
//
// Step 1: Initialize UEFI core protocols and driver globals
//
Status = DriverInit (ImageHandle, SystemTable);
//
// Step 2: Execute TXT-specific initialization
//
Status = TxtDxeMain (ImageHandle, SystemTable);
//
// Step 3: If initialization failed, perform cleanup via unload handler
//
if (EFI_ERROR (Status)) {
Status = TxtDxeUnload (ImageHandle);
}
return Status;
}