mirror of
https://github.com/acidanthera/audk.git
synced 2025-08-17 15:48:10 +02:00
This patch is code refactoring and doesn't change any functionality. Remove mInternalCr3 in PiSmmCpuDxe pagetable related code. In previous code, mInternalCr3 is used to pass address of page table which is different from Cr3 register in different level of SetMemoryAttributes function. Now remove it and pass the page table base address from the root function parameter to simplify the code logic. Signed-off-by: Dun Tan <dun.tan@intel.com> Cc: Eric Dong <eric.dong@intel.com> Cc: Rahul Kumar <rahul1.kumar@intel.com> Reviewed-by: Ray Ni <ray.ni@intel.com>
365 lines
9.4 KiB
C
365 lines
9.4 KiB
C
/** @file
|
|
Page table manipulation functions for IA-32 processors
|
|
|
|
Copyright (c) 2009 - 2019, Intel Corporation. All rights reserved.<BR>
|
|
Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
|
|
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
|
|
**/
|
|
|
|
#include "PiSmmCpuDxeSmm.h"
|
|
|
|
/**
|
|
Disable CET.
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
DisableCet (
|
|
VOID
|
|
);
|
|
|
|
/**
|
|
Enable CET.
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
EnableCet (
|
|
VOID
|
|
);
|
|
|
|
/**
|
|
Create PageTable for SMM use.
|
|
|
|
@return PageTable Address
|
|
|
|
**/
|
|
UINT32
|
|
SmmInitPageTable (
|
|
VOID
|
|
)
|
|
{
|
|
UINTN PageFaultHandlerHookAddress;
|
|
IA32_IDT_GATE_DESCRIPTOR *IdtEntry;
|
|
EFI_STATUS Status;
|
|
|
|
//
|
|
// Initialize spin lock
|
|
//
|
|
InitializeSpinLock (mPFLock);
|
|
|
|
mPhysicalAddressBits = 32;
|
|
|
|
if (FeaturePcdGet (PcdCpuSmmProfileEnable) ||
|
|
HEAP_GUARD_NONSTOP_MODE ||
|
|
NULL_DETECTION_NONSTOP_MODE)
|
|
{
|
|
//
|
|
// Set own Page Fault entry instead of the default one, because SMM Profile
|
|
// feature depends on IRET instruction to do Single Step
|
|
//
|
|
PageFaultHandlerHookAddress = (UINTN)PageFaultIdtHandlerSmmProfile;
|
|
IdtEntry = (IA32_IDT_GATE_DESCRIPTOR *)gcSmiIdtr.Base;
|
|
IdtEntry += EXCEPT_IA32_PAGE_FAULT;
|
|
IdtEntry->Bits.OffsetLow = (UINT16)PageFaultHandlerHookAddress;
|
|
IdtEntry->Bits.Reserved_0 = 0;
|
|
IdtEntry->Bits.GateType = IA32_IDT_GATE_TYPE_INTERRUPT_32;
|
|
IdtEntry->Bits.OffsetHigh = (UINT16)(PageFaultHandlerHookAddress >> 16);
|
|
} else {
|
|
//
|
|
// Register SMM Page Fault Handler
|
|
//
|
|
Status = SmmRegisterExceptionHandler (&mSmmCpuService, EXCEPT_IA32_PAGE_FAULT, SmiPFHandler);
|
|
ASSERT_EFI_ERROR (Status);
|
|
}
|
|
|
|
//
|
|
// Additional SMM IDT initialization for SMM stack guard
|
|
//
|
|
if (FeaturePcdGet (PcdCpuSmmStackGuard)) {
|
|
InitializeIDTSmmStackGuard ();
|
|
}
|
|
|
|
return Gen4GPageTable (TRUE);
|
|
}
|
|
|
|
/**
|
|
Page Fault handler for SMM use.
|
|
|
|
**/
|
|
VOID
|
|
SmiDefaultPFHandler (
|
|
VOID
|
|
)
|
|
{
|
|
CpuDeadLoop ();
|
|
}
|
|
|
|
/**
|
|
ThePage Fault handler wrapper for SMM use.
|
|
|
|
@param InterruptType Defines the type of interrupt or exception that
|
|
occurred on the processor.This parameter is processor architecture specific.
|
|
@param SystemContext A pointer to the processor context when
|
|
the interrupt occurred on the processor.
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
SmiPFHandler (
|
|
IN EFI_EXCEPTION_TYPE InterruptType,
|
|
IN EFI_SYSTEM_CONTEXT SystemContext
|
|
)
|
|
{
|
|
UINTN PFAddress;
|
|
UINTN GuardPageAddress;
|
|
UINTN CpuIndex;
|
|
|
|
ASSERT (InterruptType == EXCEPT_IA32_PAGE_FAULT);
|
|
|
|
AcquireSpinLock (mPFLock);
|
|
|
|
PFAddress = AsmReadCr2 ();
|
|
|
|
//
|
|
// If a page fault occurs in SMRAM range, it might be in a SMM stack guard page,
|
|
// or SMM page protection violation.
|
|
//
|
|
if ((PFAddress >= mCpuHotPlugData.SmrrBase) &&
|
|
(PFAddress < (mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize)))
|
|
{
|
|
DumpCpuContext (InterruptType, SystemContext);
|
|
CpuIndex = GetCpuIndex ();
|
|
GuardPageAddress = (mSmmStackArrayBase + EFI_PAGE_SIZE + CpuIndex * mSmmStackSize);
|
|
if ((FeaturePcdGet (PcdCpuSmmStackGuard)) &&
|
|
(PFAddress >= GuardPageAddress) &&
|
|
(PFAddress < (GuardPageAddress + EFI_PAGE_SIZE)))
|
|
{
|
|
DEBUG ((DEBUG_ERROR, "SMM stack overflow!\n"));
|
|
} else {
|
|
if ((SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_ID) != 0) {
|
|
DEBUG ((DEBUG_ERROR, "SMM exception at execution (0x%x)\n", PFAddress));
|
|
DEBUG_CODE (
|
|
DumpModuleInfoByIp (*(UINTN *)(UINTN)SystemContext.SystemContextIa32->Esp);
|
|
);
|
|
} else {
|
|
DEBUG ((DEBUG_ERROR, "SMM exception at access (0x%x)\n", PFAddress));
|
|
DEBUG_CODE (
|
|
DumpModuleInfoByIp ((UINTN)SystemContext.SystemContextIa32->Eip);
|
|
);
|
|
}
|
|
|
|
if (HEAP_GUARD_NONSTOP_MODE) {
|
|
GuardPagePFHandler (SystemContext.SystemContextIa32->ExceptionData);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
CpuDeadLoop ();
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// If a page fault occurs in non-SMRAM range.
|
|
//
|
|
if ((PFAddress < mCpuHotPlugData.SmrrBase) ||
|
|
(PFAddress >= mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize))
|
|
{
|
|
if ((SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_ID) != 0) {
|
|
DumpCpuContext (InterruptType, SystemContext);
|
|
DEBUG ((DEBUG_ERROR, "Code executed on IP(0x%x) out of SMM range after SMM is locked!\n", PFAddress));
|
|
DEBUG_CODE (
|
|
DumpModuleInfoByIp (*(UINTN *)(UINTN)SystemContext.SystemContextIa32->Esp);
|
|
);
|
|
CpuDeadLoop ();
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// If NULL pointer was just accessed
|
|
//
|
|
if (((PcdGet8 (PcdNullPointerDetectionPropertyMask) & BIT1) != 0) &&
|
|
(PFAddress < EFI_PAGE_SIZE))
|
|
{
|
|
DumpCpuContext (InterruptType, SystemContext);
|
|
DEBUG ((DEBUG_ERROR, "!!! NULL pointer access !!!\n"));
|
|
DEBUG_CODE (
|
|
DumpModuleInfoByIp ((UINTN)SystemContext.SystemContextIa32->Eip);
|
|
);
|
|
|
|
if (NULL_DETECTION_NONSTOP_MODE) {
|
|
GuardPagePFHandler (SystemContext.SystemContextIa32->ExceptionData);
|
|
goto Exit;
|
|
}
|
|
|
|
CpuDeadLoop ();
|
|
goto Exit;
|
|
}
|
|
|
|
if (IsSmmCommBufferForbiddenAddress (PFAddress)) {
|
|
DumpCpuContext (InterruptType, SystemContext);
|
|
DEBUG ((DEBUG_ERROR, "Access SMM communication forbidden address (0x%x)!\n", PFAddress));
|
|
DEBUG_CODE (
|
|
DumpModuleInfoByIp ((UINTN)SystemContext.SystemContextIa32->Eip);
|
|
);
|
|
CpuDeadLoop ();
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if (FeaturePcdGet (PcdCpuSmmProfileEnable)) {
|
|
SmmProfilePFHandler (
|
|
SystemContext.SystemContextIa32->Eip,
|
|
SystemContext.SystemContextIa32->ExceptionData
|
|
);
|
|
} else {
|
|
DumpCpuContext (InterruptType, SystemContext);
|
|
SmiDefaultPFHandler ();
|
|
}
|
|
|
|
Exit:
|
|
ReleaseSpinLock (mPFLock);
|
|
}
|
|
|
|
/**
|
|
This function sets memory attribute for page table.
|
|
**/
|
|
VOID
|
|
SetPageTableAttributes (
|
|
VOID
|
|
)
|
|
{
|
|
UINTN Index2;
|
|
UINTN Index3;
|
|
UINT64 *L1PageTable;
|
|
UINT64 *L2PageTable;
|
|
UINT64 *L3PageTable;
|
|
UINTN PageTableBase;
|
|
BOOLEAN IsSplitted;
|
|
BOOLEAN PageTableSplitted;
|
|
BOOLEAN CetEnabled;
|
|
|
|
//
|
|
// Don't mark page table to read-only if heap guard is enabled.
|
|
//
|
|
// BIT2: SMM page guard enabled
|
|
// BIT3: SMM pool guard enabled
|
|
//
|
|
if ((PcdGet8 (PcdHeapGuardPropertyMask) & (BIT3 | BIT2)) != 0) {
|
|
DEBUG ((DEBUG_INFO, "Don't mark page table to read-only as heap guard is enabled\n"));
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Don't mark page table to read-only if SMM profile is enabled.
|
|
//
|
|
if (FeaturePcdGet (PcdCpuSmmProfileEnable)) {
|
|
DEBUG ((DEBUG_INFO, "Don't mark page table to read-only as SMM profile is enabled\n"));
|
|
return;
|
|
}
|
|
|
|
DEBUG ((DEBUG_INFO, "SetPageTableAttributes\n"));
|
|
|
|
//
|
|
// Disable write protection, because we need mark page table to be write protected.
|
|
// We need *write* page table memory, to mark itself to be *read only*.
|
|
//
|
|
CetEnabled = ((AsmReadCr4 () & CR4_CET_ENABLE) != 0) ? TRUE : FALSE;
|
|
if (CetEnabled) {
|
|
//
|
|
// CET must be disabled if WP is disabled.
|
|
//
|
|
DisableCet ();
|
|
}
|
|
|
|
AsmWriteCr0 (AsmReadCr0 () & ~CR0_WP);
|
|
|
|
do {
|
|
DEBUG ((DEBUG_INFO, "Start...\n"));
|
|
PageTableSplitted = FALSE;
|
|
|
|
PageTableBase = AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64;
|
|
L3PageTable = (UINT64 *)PageTableBase;
|
|
|
|
SmmSetMemoryAttributesEx (PageTableBase, FALSE, (EFI_PHYSICAL_ADDRESS)PageTableBase, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted);
|
|
PageTableSplitted = (PageTableSplitted || IsSplitted);
|
|
|
|
for (Index3 = 0; Index3 < 4; Index3++) {
|
|
L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);
|
|
if (L2PageTable == NULL) {
|
|
continue;
|
|
}
|
|
|
|
SmmSetMemoryAttributesEx (PageTableBase, FALSE, (EFI_PHYSICAL_ADDRESS)(UINTN)L2PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted);
|
|
PageTableSplitted = (PageTableSplitted || IsSplitted);
|
|
|
|
for (Index2 = 0; Index2 < SIZE_4KB/sizeof (UINT64); Index2++) {
|
|
if ((L2PageTable[Index2] & IA32_PG_PS) != 0) {
|
|
// 2M
|
|
continue;
|
|
}
|
|
|
|
L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);
|
|
if (L1PageTable == NULL) {
|
|
continue;
|
|
}
|
|
|
|
SmmSetMemoryAttributesEx (PageTableBase, FALSE, (EFI_PHYSICAL_ADDRESS)(UINTN)L1PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted);
|
|
PageTableSplitted = (PageTableSplitted || IsSplitted);
|
|
}
|
|
}
|
|
} while (PageTableSplitted);
|
|
|
|
//
|
|
// Enable write protection, after page table updated.
|
|
//
|
|
AsmWriteCr0 (AsmReadCr0 () | CR0_WP);
|
|
if (CetEnabled) {
|
|
//
|
|
// re-enable CET.
|
|
//
|
|
EnableCet ();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/**
|
|
This function returns with no action for 32 bit.
|
|
|
|
@param[out] *Cr2 Pointer to variable to hold CR2 register value.
|
|
**/
|
|
VOID
|
|
SaveCr2 (
|
|
OUT UINTN *Cr2
|
|
)
|
|
{
|
|
return;
|
|
}
|
|
|
|
/**
|
|
This function returns with no action for 32 bit.
|
|
|
|
@param[in] Cr2 Value to write into CR2 register.
|
|
**/
|
|
VOID
|
|
RestoreCr2 (
|
|
IN UINTN Cr2
|
|
)
|
|
{
|
|
return;
|
|
}
|
|
|
|
/**
|
|
Return whether access to non-SMRAM is restricted.
|
|
|
|
@retval TRUE Access to non-SMRAM is restricted.
|
|
@retval FALSE Access to non-SMRAM is not restricted.
|
|
**/
|
|
BOOLEAN
|
|
IsRestrictedMemoryAccess (
|
|
VOID
|
|
)
|
|
{
|
|
return TRUE;
|
|
}
|