X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=UefiCpuPkg%2FCpuDxe%2FCpuPageTable.c;h=c369b44f128e70a3e49e94adf5b484782fecdfb9;hb=a7ef158b07524f9afd0cefa3180aeac0fcb6e436;hp=3ad55f65c58abd5d0ab47c907d87f9f5dfcb6f9a;hpb=4f10654e04601fe67a750c9b5a4242efd4141569;p=mirror_edk2.git diff --git a/UefiCpuPkg/CpuDxe/CpuPageTable.c b/UefiCpuPkg/CpuDxe/CpuPageTable.c index 3ad55f65c5..c369b44f12 100644 --- a/UefiCpuPkg/CpuDxe/CpuPageTable.c +++ b/UefiCpuPkg/CpuDxe/CpuPageTable.c @@ -4,29 +4,31 @@ Copyright (c) 2017, Intel Corporation. All rights reserved.
Copyright (c) 2017, AMD Incorporated. All rights reserved.
- This program and the accompanying materials - are licensed and made available under the terms and conditions of the BSD License - which accompanies this distribution. The full text of the license may be found at - http://opensource.org/licenses/bsd-license.php - - THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, - WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include #include "CpuDxe.h" #include "CpuPageTable.h" +/// +/// Paging registers +/// +#define CR0_WP BIT16 +#define CR0_PG BIT31 +#define CR4_PSE BIT4 +#define CR4_PAE BIT5 + /// /// Page Table Entry /// @@ -62,6 +64,10 @@ #define PAGING_2M_ADDRESS_MASK_64 0x000FFFFFFFE00000ull #define PAGING_1G_ADDRESS_MASK_64 0x000FFFFFC0000000ull +#define MAX_PF_ENTRY_COUNT 10 +#define MAX_DEBUG_MESSAGE_LENGTH 0x100 +#define IA32_PF_EC_ID BIT4 + typedef enum { PageNone, Page4K, @@ -87,70 +93,59 @@ PAGE_ATTRIBUTE_TABLE mPageAttributeTable[] = { {Page1G, SIZE_1GB, PAGING_1G_ADDRESS_MASK_64}, }; -PAGE_TABLE_POOL *mPageTablePool = NULL; +PAGE_TABLE_POOL *mPageTablePool = NULL; +BOOLEAN mPageTablePoolLock = FALSE; +PAGE_TABLE_LIB_PAGING_CONTEXT mPagingContext; +EFI_SMM_BASE2_PROTOCOL *mSmmBase2 = NULL; -/** - Enable write protection function for AP. - - @param[in,out] Buffer The pointer to private data buffer. -**/ -VOID -EFIAPI -SyncCpuEnableWriteProtection ( - IN OUT VOID *Buffer - ) -{ - AsmWriteCr0 (AsmReadCr0 () | BIT16); -} +// +// Record the page fault exception count for one instruction execution. +// +UINTN *mPFEntryCount; +UINT64 *(*mLastPFEntryPointer)[MAX_PF_ENTRY_COUNT]; /** - CpuFlushTlb function for AP. - - @param[in,out] Buffer The pointer to private data buffer. + Check if current execution environment is in SMM mode or not, via + EFI_SMM_BASE2_PROTOCOL. + + This is necessary because of the fact that MdePkg\Library\SmmMemoryAllocationLib + supports to free memory outside SMRAM. The library will call gBS->FreePool() or + gBS->FreePages() and then SetMemorySpaceAttributes interface in turn to change + memory paging attributes during free operation, if some memory related features + are enabled (like Heap Guard). + + This means that SetMemorySpaceAttributes() has chance to run in SMM mode. This + will cause incorrect result because SMM mode always loads its own page tables, + which are usually different from DXE. This function can be used to detect such + situation and help to avoid further misoperations. + + @retval TRUE In SMM mode. + @retval FALSE Not in SMM mode. **/ -VOID -EFIAPI -SyncCpuFlushTlb ( - IN OUT VOID *Buffer +BOOLEAN +IsInSmm ( + VOID ) { - CpuFlushTlb(); -} + BOOLEAN InSmm; -/** - Sync memory page attributes for AP. + InSmm = FALSE; + if (mSmmBase2 == NULL) { + gBS->LocateProtocol (&gEfiSmmBase2ProtocolGuid, NULL, (VOID **)&mSmmBase2); + } + + if (mSmmBase2 != NULL) { + mSmmBase2->InSmm (mSmmBase2, &InSmm); + } - @param[in] Procedure A pointer to the function to be run on enabled APs of - the system. -**/ -VOID -SyncMemoryPageAttributesAp ( - IN EFI_AP_PROCEDURE Procedure - ) -{ - EFI_STATUS Status; - EFI_MP_SERVICES_PROTOCOL *MpService; - - Status = gBS->LocateProtocol ( - &gEfiMpServiceProtocolGuid, - NULL, - (VOID **)&MpService - ); // - // Synchronize the update with all APs + // mSmmBase2->InSmm() can only detect if the caller is running in SMRAM + // or from SMM driver. It cannot tell if the caller is running in SMM mode. + // Check page table base address to guarantee that because SMM mode willl + // load its own page table. // - if (!EFI_ERROR (Status)) { - Status = MpService->StartupAllAPs ( - MpService, // This - Procedure, // Procedure - FALSE, // SingleThread - NULL, // WaitEvent - 0, // TimeoutInMicrosecsond - NULL, // ProcedureArgument - NULL // FailedCpuList - ); - ASSERT (Status == EFI_SUCCESS || Status == EFI_NOT_STARTED || Status == EFI_NOT_READY); - } + return (InSmm && + mPagingContext.ContextData.X64.PageTableBase != (UINT64)AsmReadCr3()); } /** @@ -163,45 +158,60 @@ GetCurrentPagingContext ( IN OUT PAGE_TABLE_LIB_PAGING_CONTEXT *PagingContext ) { - UINT32 RegEax; - UINT32 RegEdx; + UINT32 RegEax; + CPUID_EXTENDED_CPU_SIG_EDX RegEdx; + MSR_IA32_EFER_REGISTER MsrEfer; - ZeroMem(PagingContext, sizeof(*PagingContext)); - if (sizeof(UINTN) == sizeof(UINT64)) { - PagingContext->MachineType = IMAGE_FILE_MACHINE_X64; - } else { - PagingContext->MachineType = IMAGE_FILE_MACHINE_I386; - } - if ((AsmReadCr0 () & BIT31) != 0) { - PagingContext->ContextData.X64.PageTableBase = (AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64); - } else { - PagingContext->ContextData.X64.PageTableBase = 0; - } + // + // Don't retrieve current paging context from processor if in SMM mode. + // + if (!IsInSmm ()) { + ZeroMem (&mPagingContext, sizeof(mPagingContext)); + if (sizeof(UINTN) == sizeof(UINT64)) { + mPagingContext.MachineType = IMAGE_FILE_MACHINE_X64; + } else { + mPagingContext.MachineType = IMAGE_FILE_MACHINE_I386; + } + if ((AsmReadCr0 () & CR0_PG) != 0) { + mPagingContext.ContextData.X64.PageTableBase = (AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64); + } else { + mPagingContext.ContextData.X64.PageTableBase = 0; + } - if ((AsmReadCr4 () & BIT4) != 0) { - PagingContext->ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PSE; - } - if ((AsmReadCr4 () & BIT5) != 0) { - PagingContext->ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE; - } - if ((AsmReadCr0 () & BIT16) != 0) { - PagingContext->ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_WP_ENABLE; - } + if ((AsmReadCr4 () & CR4_PSE) != 0) { + mPagingContext.ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PSE; + } + if ((AsmReadCr4 () & CR4_PAE) != 0) { + mPagingContext.ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE; + } + if ((AsmReadCr0 () & CR0_WP) != 0) { + mPagingContext.ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_WP_ENABLE; + } - AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL); - if (RegEax > 0x80000000) { - AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx); - if ((RegEdx & BIT20) != 0) { - // XD supported - if ((AsmReadMsr64 (0xC0000080) & BIT11) != 0) { - // XD activated - PagingContext->ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_XD_ACTIVATED; + AsmCpuid (CPUID_EXTENDED_FUNCTION, &RegEax, NULL, NULL, NULL); + if (RegEax >= CPUID_EXTENDED_CPU_SIG) { + AsmCpuid (CPUID_EXTENDED_CPU_SIG, NULL, NULL, NULL, &RegEdx.Uint32); + + if (RegEdx.Bits.NX != 0) { + // XD supported + MsrEfer.Uint64 = AsmReadMsr64(MSR_CORE_IA32_EFER); + if (MsrEfer.Bits.NXE != 0) { + // XD activated + mPagingContext.ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_XD_ACTIVATED; + } + } + + if (RegEdx.Bits.Page1GB != 0) { + mPagingContext.ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAGE_1G_SUPPORT; } - } - if ((RegEdx & BIT26) != 0) { - PagingContext->ContextData.Ia32.Attributes |= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAGE_1G_SUPPORT; } } + + // + // This can avoid getting SMM paging context if in SMM mode. We cannot assume + // SMM mode shares the same paging context as DXE. + // + CopyMem (PagingContext, &mPagingContext, sizeof (mPagingContext)); } /** @@ -520,7 +530,7 @@ SplitPage ( ASSERT (SplitAttribute == Page4K); if (SplitAttribute == Page4K) { NewPageEntry = AllocatePagesFunc (1); - DEBUG ((DEBUG_INFO, "Split - 0x%x\n", NewPageEntry)); + DEBUG ((DEBUG_VERBOSE, "Split - 0x%x\n", NewPageEntry)); if (NewPageEntry == NULL) { return RETURN_OUT_OF_RESOURCES; } @@ -528,7 +538,7 @@ SplitPage ( for (Index = 0; Index < SIZE_4KB / sizeof(UINT64); Index++) { NewPageEntry[Index] = (BaseAddress + SIZE_4KB * Index) | AddressEncMask | ((*PageEntry) & PAGE_PROGATE_BITS); } - (*PageEntry) = (UINT64)(UINTN)NewPageEntry | AddressEncMask | ((*PageEntry) & PAGE_PROGATE_BITS); + (*PageEntry) = (UINT64)(UINTN)NewPageEntry | AddressEncMask | ((*PageEntry) & PAGE_ATTRIBUTE_BITS); return RETURN_SUCCESS; } else { return RETURN_UNSUPPORTED; @@ -541,7 +551,7 @@ SplitPage ( ASSERT (SplitAttribute == Page2M || SplitAttribute == Page4K); if ((SplitAttribute == Page2M || SplitAttribute == Page4K)) { NewPageEntry = AllocatePagesFunc (1); - DEBUG ((DEBUG_INFO, "Split - 0x%x\n", NewPageEntry)); + DEBUG ((DEBUG_VERBOSE, "Split - 0x%x\n", NewPageEntry)); if (NewPageEntry == NULL) { return RETURN_OUT_OF_RESOURCES; } @@ -549,7 +559,7 @@ SplitPage ( for (Index = 0; Index < SIZE_4KB / sizeof(UINT64); Index++) { NewPageEntry[Index] = (BaseAddress + SIZE_2MB * Index) | AddressEncMask | IA32_PG_PS | ((*PageEntry) & PAGE_PROGATE_BITS); } - (*PageEntry) = (UINT64)(UINTN)NewPageEntry | AddressEncMask | ((*PageEntry) & PAGE_PROGATE_BITS); + (*PageEntry) = (UINT64)(UINTN)NewPageEntry | AddressEncMask | ((*PageEntry) & PAGE_ATTRIBUTE_BITS); return RETURN_SUCCESS; } else { return RETURN_UNSUPPORTED; @@ -571,21 +581,14 @@ IsReadOnlyPageWriteProtected ( VOID ) { - return ((AsmReadCr0 () & BIT16) != 0); -} - -/** - Disable write protection function for AP. - - @param[in,out] Buffer The pointer to private data buffer. -**/ -VOID -EFIAPI -SyncCpuDisableWriteProtection ( - IN OUT VOID *Buffer - ) -{ - AsmWriteCr0 (AsmReadCr0() & ~BIT16); + // + // To avoid unforseen consequences, don't touch paging settings in SMM mode + // in this driver. + // + if (!IsInSmm ()) { + return ((AsmReadCr0 () & CR0_WP) != 0); + } + return FALSE; } /** @@ -596,8 +599,13 @@ DisableReadOnlyPageWriteProtect ( VOID ) { - AsmWriteCr0 (AsmReadCr0() & ~BIT16); - SyncMemoryPageAttributesAp (SyncCpuDisableWriteProtection); + // + // To avoid unforseen consequences, don't touch paging settings in SMM mode + // in this driver. + // + if (!IsInSmm ()) { + AsmWriteCr0 (AsmReadCr0 () & ~CR0_WP); + } } /** @@ -608,8 +616,13 @@ EnableReadOnlyPageWriteProtect ( VOID ) { - AsmWriteCr0 (AsmReadCr0() | BIT16); - SyncMemoryPageAttributesAp (SyncCpuEnableWriteProtection); + // + // To avoid unforseen consequences, don't touch paging settings in SMM mode + // in this driver. + // + if (!IsInSmm ()) { + AsmWriteCr0 (AsmReadCr0 () | CR0_WP); + } } /** @@ -837,10 +850,13 @@ AssignMemoryPageAttributes ( if (!EFI_ERROR(Status)) { if ((PagingContext == NULL) && IsModified) { // - // Flush TLB as last step + // Flush TLB as last step. + // + // Note: Since APs will always init CR3 register in HLT loop mode or do + // TLB flush in MWAIT loop mode, there's no need to flush TLB for them + // here. // CpuFlushTlb(); - SyncMemoryPageAttributesAp (SyncCpuFlushTlb); } } @@ -983,7 +999,7 @@ RefreshGcdMemoryAttributesFromPaging ( ); ASSERT_EFI_ERROR (Status); DEBUG (( - DEBUG_INFO, + DEBUG_VERBOSE, "Updated memory space attribute: [%lu] %016lx - %016lx (%016lx -> %016lx)\r\n", (UINT64)Index, BaseAddress, BaseAddress + Length - 1, MemorySpaceMap[Index].Attributes, @@ -1025,6 +1041,16 @@ InitializePageTablePool ( VOID *Buffer; BOOLEAN IsModified; + // + // Do not allow re-entrance. + // + if (mPageTablePoolLock) { + return FALSE; + } + + mPageTablePoolLock = TRUE; + IsModified = FALSE; + // // Always reserve at least PAGE_TABLE_POOL_UNIT_PAGES, including one page for // header. @@ -1035,9 +1061,15 @@ InitializePageTablePool ( Buffer = AllocateAlignedPages (PoolPages, PAGE_TABLE_POOL_ALIGNMENT); if (Buffer == NULL) { DEBUG ((DEBUG_ERROR, "ERROR: Out of aligned pages\r\n")); - return FALSE; + goto Done; } + DEBUG (( + DEBUG_INFO, + "Paging: added %lu pages to page table pool\r\n", + (UINT64)PoolPages + )); + // // Link all pools into a list for easier track later. // @@ -1071,7 +1103,9 @@ InitializePageTablePool ( ); ASSERT (IsModified == TRUE); - return TRUE; +Done: + mPageTablePoolLock = FALSE; + return IsModified; } /** @@ -1121,6 +1155,159 @@ AllocatePageTableMemory ( return Buffer; } +/** + Special handler for #DB exception, which will restore the page attributes + (not-present). It should work with #PF handler which will set pages to + 'present'. + + @param ExceptionType Exception type. + @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. + +**/ +VOID +EFIAPI +DebugExceptionHandler ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN EFI_SYSTEM_CONTEXT SystemContext + ) +{ + UINTN CpuIndex; + UINTN PFEntry; + BOOLEAN IsWpEnabled; + + MpInitLibWhoAmI (&CpuIndex); + + // + // Clear last PF entries + // + IsWpEnabled = IsReadOnlyPageWriteProtected (); + if (IsWpEnabled) { + DisableReadOnlyPageWriteProtect (); + } + + for (PFEntry = 0; PFEntry < mPFEntryCount[CpuIndex]; PFEntry++) { + if (mLastPFEntryPointer[CpuIndex][PFEntry] != NULL) { + *mLastPFEntryPointer[CpuIndex][PFEntry] &= ~(UINT64)IA32_PG_P; + } + } + + if (IsWpEnabled) { + EnableReadOnlyPageWriteProtect (); + } + + // + // Reset page fault exception count for next page fault. + // + mPFEntryCount[CpuIndex] = 0; + + // + // Flush TLB + // + CpuFlushTlb (); + + // + // Clear TF in EFLAGS + // + if (mPagingContext.MachineType == IMAGE_FILE_MACHINE_I386) { + SystemContext.SystemContextIa32->Eflags &= (UINT32)~BIT8; + } else { + SystemContext.SystemContextX64->Rflags &= (UINT64)~BIT8; + } +} + +/** + Special handler for #PF exception, which will set the pages which caused + #PF to be 'present'. The attribute of those pages should be restored in + the subsequent #DB handler. + + @param ExceptionType Exception type. + @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. + +**/ +VOID +EFIAPI +PageFaultExceptionHandler ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN EFI_SYSTEM_CONTEXT SystemContext + ) +{ + EFI_STATUS Status; + UINT64 PFAddress; + PAGE_TABLE_LIB_PAGING_CONTEXT PagingContext; + PAGE_ATTRIBUTE PageAttribute; + UINT64 Attributes; + UINT64 *PageEntry; + UINTN Index; + UINTN CpuIndex; + UINTN PageNumber; + BOOLEAN NonStopMode; + + PFAddress = AsmReadCr2 () & ~EFI_PAGE_MASK; + if (PFAddress < BASE_4KB) { + NonStopMode = NULL_DETECTION_NONSTOP_MODE ? TRUE : FALSE; + } else { + NonStopMode = HEAP_GUARD_NONSTOP_MODE ? TRUE : FALSE; + } + + if (NonStopMode) { + MpInitLibWhoAmI (&CpuIndex); + GetCurrentPagingContext (&PagingContext); + // + // Memory operation cross page boundary, like "rep mov" instruction, will + // cause infinite loop between this and Debug Trap handler. We have to make + // sure that current page and the page followed are both in PRESENT state. + // + PageNumber = 2; + while (PageNumber > 0) { + PageEntry = GetPageTableEntry (&PagingContext, PFAddress, &PageAttribute); + ASSERT(PageEntry != NULL); + + if (PageEntry != NULL) { + Attributes = GetAttributesFromPageEntry (PageEntry); + if ((Attributes & EFI_MEMORY_RP) != 0) { + Attributes &= ~EFI_MEMORY_RP; + Status = AssignMemoryPageAttributes (&PagingContext, PFAddress, + EFI_PAGE_SIZE, Attributes, NULL); + if (!EFI_ERROR(Status)) { + Index = mPFEntryCount[CpuIndex]; + // + // Re-retrieve page entry because above calling might update page + // table due to table split. + // + PageEntry = GetPageTableEntry (&PagingContext, PFAddress, &PageAttribute); + mLastPFEntryPointer[CpuIndex][Index++] = PageEntry; + mPFEntryCount[CpuIndex] = Index; + } + } + } + + PFAddress += EFI_PAGE_SIZE; + --PageNumber; + } + } + + // + // Initialize the serial port before dumping. + // + SerialPortInitialize (); + // + // Display ExceptionType, CPU information and Image information + // + DumpCpuContext (ExceptionType, SystemContext); + if (NonStopMode) { + // + // Set TF in EFLAGS + // + if (mPagingContext.MachineType == IMAGE_FILE_MACHINE_I386) { + SystemContext.SystemContextIa32->Eflags |= (UINT32)BIT8; + } else { + SystemContext.SystemContextX64->Rflags |= (UINT64)BIT8; + } + } else { + CpuDeadLoop (); + } +} + /** Initialize the Page Table lib. **/ @@ -1144,6 +1331,15 @@ InitializePageTableLib ( EnableReadOnlyPageWriteProtect (); } + if (HEAP_GUARD_NONSTOP_MODE || NULL_DETECTION_NONSTOP_MODE) { + mPFEntryCount = (UINTN *)AllocateZeroPool (sizeof (UINTN) * mNumberOfProcessors); + ASSERT (mPFEntryCount != NULL); + + mLastPFEntryPointer = (UINT64 *(*)[MAX_PF_ENTRY_COUNT]) + AllocateZeroPool (sizeof (mLastPFEntryPointer[0]) * mNumberOfProcessors); + ASSERT (mLastPFEntryPointer != NULL); + } + DEBUG ((DEBUG_INFO, "CurrentPagingContext:\n", CurrentPagingContext.MachineType)); DEBUG ((DEBUG_INFO, " MachineType - 0x%x\n", CurrentPagingContext.MachineType)); DEBUG ((DEBUG_INFO, " PageTableBase - 0x%x\n", CurrentPagingContext.ContextData.X64.PageTableBase));