| /** @file | |
| Page table manipulation functions for IA-32 processors | |
| Copyright (c) 2009 - 2023, 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" | |
| /** | |
| 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; | |
| mPagingMode = PagingPae; | |
| 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 GenSmmPageTable (PagingPae, mPhysicalAddressBits); | |
| } | |
| /** | |
| 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 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; | |
| } |