diff --git a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTable.h b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTable.h index 2c67ecb469..24da0ffb30 100644 --- a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTable.h +++ b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTable.h @@ -1,7 +1,7 @@ /** @file Internal header for CpuPageTableLib. - Copyright (c) 2022, Intel Corporation. All rights reserved.
+ Copyright (c) 2022 - 2023, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ @@ -20,6 +20,8 @@ #define REGION_LENGTH(l) LShiftU64 (1, (l) * 9 + 3) +#define MAX_PAE_PDPTE_NUM 4 + typedef enum { Pte = 1, Pde = 2, diff --git a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c index 0d576ce39a..eff02619fa 100644 --- a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c +++ b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c @@ -671,15 +671,17 @@ PageTableMap ( IA32_PAGE_LEVEL MaxLeafLevel; IA32_MAP_ATTRIBUTE ParentAttribute; BOOLEAN LocalIsModified; + UINTN Index; + IA32_PAGING_ENTRY *PagingEntry; + UINT8 BufferInStack[SIZE_4KB - 1 + MAX_PAE_PDPTE_NUM * sizeof (IA32_PAGING_ENTRY)]; if (Length == 0) { return RETURN_SUCCESS; } - if ((PagingMode == Paging32bit) || (PagingMode == PagingPae) || (PagingMode >= PagingModeMax)) { + if ((PagingMode == Paging32bit) || (PagingMode >= PagingModeMax)) { // // 32bit paging is never supported. - // PAE paging will be supported later. // return RETURN_UNSUPPORTED; } @@ -716,17 +718,32 @@ PageTableMap ( MaxLeafLevel = (IA32_PAGE_LEVEL)(UINT8)PagingMode; MaxLevel = (IA32_PAGE_LEVEL)(UINT8)(PagingMode >> 8); - MaxLinearAddress = LShiftU64 (1, 12 + MaxLevel * 9); + MaxLinearAddress = (PagingMode == PagingPae) ? LShiftU64 (1, 32) : LShiftU64 (1, 12 + MaxLevel * 9); if ((LinearAddress > MaxLinearAddress) || (Length > MaxLinearAddress - LinearAddress)) { // - // Maximum linear address is (1 << 48) or (1 << 57) + // Maximum linear address is (1 << 32), (1 << 48) or (1 << 57) // return RETURN_INVALID_PARAMETER; } TopPagingEntry.Uintn = *PageTable; if (TopPagingEntry.Uintn != 0) { + if (PagingMode == PagingPae) { + // + // Create 4 temporary PDPTE at a 4k-aligned address. + // Copy the original PDPTE content and set ReadWrite, UserSupervisor to 1, set Nx to 0. + // + TopPagingEntry.Uintn = ALIGN_VALUE ((UINTN)BufferInStack, BASE_4KB); + PagingEntry = (IA32_PAGING_ENTRY *)(TopPagingEntry.Uintn); + CopyMem (PagingEntry, (VOID *)(*PageTable), MAX_PAE_PDPTE_NUM * sizeof (IA32_PAGING_ENTRY)); + for (Index = 0; Index < MAX_PAE_PDPTE_NUM; Index++) { + PagingEntry[Index].Pnle.Bits.ReadWrite = 1; + PagingEntry[Index].Pnle.Bits.UserSupervisor = 1; + PagingEntry[Index].Pnle.Bits.Nx = 0; + } + } + TopPagingEntry.Pce.Present = 1; TopPagingEntry.Pce.ReadWrite = 1; TopPagingEntry.Pce.UserSupervisor = 1; @@ -801,7 +818,33 @@ PageTableMap ( ); if (!RETURN_ERROR (Status)) { - *PageTable = (UINTN)(TopPagingEntry.Uintn & IA32_PE_BASE_ADDRESS_MASK_40); + PagingEntry = (IA32_PAGING_ENTRY *)(UINTN)(TopPagingEntry.Uintn & IA32_PE_BASE_ADDRESS_MASK_40); + + if (PagingMode == PagingPae) { + // + // These MustBeZero fields are treated as RW and other attributes by the common map logic. So they might be set to 1. + // + for (Index = 0; Index < MAX_PAE_PDPTE_NUM; Index++) { + PagingEntry[Index].PdptePae.Bits.MustBeZero = 0; + PagingEntry[Index].PdptePae.Bits.MustBeZero2 = 0; + PagingEntry[Index].PdptePae.Bits.MustBeZero3 = 0; + } + + if (*PageTable != 0) { + // + // Copy temp PDPTE to original PDPTE. + // + CopyMem ((VOID *)(*PageTable), PagingEntry, MAX_PAE_PDPTE_NUM * sizeof (IA32_PAGING_ENTRY)); + } + } + + if (*PageTable == 0) { + // + // Do not assign the *PageTable when it's an existing page table. + // If it's an existing PAE page table, PagingEntry is the temp buffer in stack. + // + *PageTable = (UINTN)PagingEntry; + } } return Status; diff --git a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableParse.c b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableParse.c index 65490751ab..37466e4e10 100644 --- a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableParse.c +++ b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableParse.c @@ -1,7 +1,7 @@ /** @file This library implements CpuPageTableLib that are generic for IA32 family CPU. - Copyright (c) 2022, Intel Corporation. All rights reserved.
+ Copyright (c) 2022 - 2023, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ @@ -158,6 +158,7 @@ VOID PageTableLibParsePnle ( IN UINT64 PageTableBaseAddress, IN UINTN Level, + IN UINTN MaxLevel, IN UINT64 RegionStart, IN IA32_MAP_ATTRIBUTE *ParentMapAttribute, IN OUT IA32_MAP_ENTRY *Map, @@ -171,13 +172,15 @@ PageTableLibParsePnle ( UINTN Index; IA32_MAP_ATTRIBUTE MapAttribute; UINT64 RegionLength; + UINTN PagingEntryNumber; ASSERT (OneEntry != NULL); - PagingEntry = (IA32_PAGING_ENTRY *)(UINTN)PageTableBaseAddress; - RegionLength = REGION_LENGTH (Level); + PagingEntry = (IA32_PAGING_ENTRY *)(UINTN)PageTableBaseAddress; + RegionLength = REGION_LENGTH (Level); + PagingEntryNumber = ((MaxLevel == 3) && (Level == 3)) ? MAX_PAE_PDPTE_NUM : 512; - for (Index = 0; Index < 512; Index++, RegionStart += RegionLength) { + for (Index = 0; Index < PagingEntryNumber; Index++, RegionStart += RegionLength) { if (PagingEntry[Index].Pce.Present == 0) { continue; } @@ -228,6 +231,7 @@ PageTableLibParsePnle ( PageTableLibParsePnle ( IA32_PNLE_PAGE_TABLE_BASE_ADDRESS (&PagingEntry[Index].Pnle), Level - 1, + MaxLevel, RegionStart, &MapAttribute, Map, @@ -269,6 +273,8 @@ PageTableParse ( IA32_MAP_ENTRY *LastEntry; IA32_MAP_ENTRY OneEntry; UINTN MaxLevel; + UINTN Index; + IA32_PAGING_ENTRY BufferInStack[MAX_PAE_PDPTE_NUM]; if ((PagingMode == Paging32bit) || (PagingMode >= PagingModeMax)) { // @@ -290,6 +296,17 @@ PageTableParse ( return RETURN_SUCCESS; } + if (PagingMode == PagingPae) { + CopyMem (BufferInStack, (VOID *)PageTable, sizeof (BufferInStack)); + for (Index = 0; Index < MAX_PAE_PDPTE_NUM; Index++) { + BufferInStack[Index].Pnle.Bits.ReadWrite = 1; + BufferInStack[Index].Pnle.Bits.UserSupervisor = 1; + BufferInStack[Index].Pnle.Bits.Nx = 0; + } + + PageTable = (UINTN)BufferInStack; + } + // // Page table layout is as below: // @@ -319,7 +336,7 @@ PageTableParse ( MapCapacity = *MapCount; *MapCount = 0; LastEntry = NULL; - PageTableLibParsePnle ((UINT64)PageTable, MaxLevel, 0, &NopAttribute, Map, MapCount, MapCapacity, &LastEntry, &OneEntry); + PageTableLibParsePnle ((UINT64)PageTable, MaxLevel, MaxLevel, 0, &NopAttribute, Map, MapCount, MapCapacity, &LastEntry, &OneEntry); if (*MapCount > MapCapacity) { return RETURN_BUFFER_TOO_SMALL;