From f66d801b23d5870c4af36df194b1b41cbde4c912 Mon Sep 17 00:00:00 2001 From: Kun Qin Date: Mon, 26 Oct 2026 13:57:41 -0700 Subject: [PATCH 1/3] [SQUASH on REBASE] Revert "UefiCpuPkg: Fixing page table handling for non-present pages. [202405] (#1081)" This reverts commit b78533887c51e7f92ddb32ba3c111b266b6312e7. --- .../Library/CpuPageTableLib/CpuPageTableMap.c | 35 ++++++------------- 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c index c488825e336..b3a47566312 100644 --- a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c +++ b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c @@ -390,16 +390,6 @@ PageTableLibMapInLevel ( if (RETURN_ERROR (Status)) { return Status; } - - // MU_CHANGE Start - Populate base address bits for non present pages - OneOfPagingEntry.Pnle.Uint64 = 0; - if ((Level != 1) && (Level != 2) && (Level != 3)) { - PageTableLibSetPnle (&OneOfPagingEntry.Pnle, &PleBAttribute, &AllOneMask); - } else { - PageTableLibSetPle (Level, &OneOfPagingEntry, 0, &PleBAttribute, &AllOneMask); - } - - // MU_CHANGE End - Populate base address bits for non present pages } else { PageTableLibSetPle (Level, &OneOfPagingEntry, 0, &PleBAttribute, &AllOneMask); } @@ -436,17 +426,16 @@ PageTableLibMapInLevel ( PagingEntry = (IA32_PAGING_ENTRY *)((UINTN)Buffer + *BufferSize); ZeroMem (PagingEntry, SIZE_4KB); - // MU_CHANGE Start - Populate contents for both present and non-present pages - // - // Create 512 child-level entries that map to 2M/4K. - // - for (SubOffset = 0, Index = 0; Index < 512; Index++) { - PagingEntry[Index].Uint64 = OneOfPagingEntry.Uint64 + SubOffset; - SubOffset += RegionLength; + if (ParentPagingEntry->Pce.Present) { + // + // Create 512 child-level entries that map to 2M/4K. + // + for (SubOffset = 0, Index = 0; Index < 512; Index++) { + PagingEntry[Index].Uint64 = OneOfPagingEntry.Uint64 + SubOffset; + SubOffset += RegionLength; + } } - // MU_CHANGE End - Populate contents for both present and non-present pages - // // Set NOP attributes // Note: Should NOT inherit the attributes from the original entry because a zero RW bit @@ -530,11 +519,9 @@ PageTableLibMapInLevel ( // e.g.: Set PDE[0-255].ReadWrite = 0 // for (Index = 0; Index < 512; Index++) { - // MU_CHANGE Start - Count the space for non-present pages as well. - // if (PagingEntry[Index].Pce.Present == 0) { - // continue; - // } - // MU_CHANGE End - Count the space for non-present pages as well. + if (PagingEntry[Index].Pce.Present == 0) { + continue; + } if (IsPle (&PagingEntry[Index], Level)) { PageTableLibSetPle (Level, &PagingEntry[Index], 0, &ChildAttribute, &ChildMask); From 5a3004e0b4b5c2f1897c69a3bb27a01d0924361f Mon Sep 17 00:00:00 2001 From: Aaron <105021049+apop5@users.noreply.github.com> Date: Thu, 1 Aug 2024 11:52:48 -0700 Subject: [PATCH 2/3] [CHERRY-PICK] UefiCpuPkg: CpuPageTableLib: Fix page table handling for non-present pages Current page table logic skips the content population when the attribute is RP. This is problematic when a smaller page is marked present from a big non-present page. The remainder of the non-present pages will drop their address and attribute bits. Page table consumers treat such zero entries as unmapped. For example, GetMemoryAttributes of `EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL` in PiSmmCpuDxeSmm returns `EFI_UNSUPPORTED` for those ranges instead of reporting EFI_MEMORY_RP. This change adds logic to populate the address and propagate attribute bits for the non-present pages. This was tested on QEMU Q35 platform and booted to OS. Co-authored-by: Kun Qin Signed-off-by: Kun Qin (cherry picked from commit 0d6d87f99485a1a3cfa7e27c74941f0f13beee66) --- .../Library/CpuPageTableLib/CpuPageTableMap.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c index b3a47566312..fc08420cd05 100644 --- a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c +++ b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c @@ -390,7 +390,14 @@ PageTableLibMapInLevel ( if (RETURN_ERROR (Status)) { return Status; } - } else { + } + + // + // A present parent leaf is split into child leaf entries to preserve the existing mapping. + // A non-present parent is split into child leaf entries only when Level can be a leaf. + // Otherwise, the child entries are non-leaf entries and are left zeroed. + // + if ((ParentPagingEntry->Pce.Present == 1) || (Level <= MaxLeafLevel)) { PageTableLibSetPle (Level, &OneOfPagingEntry, 0, &PleBAttribute, &AllOneMask); } @@ -426,9 +433,9 @@ PageTableLibMapInLevel ( PagingEntry = (IA32_PAGING_ENTRY *)((UINTN)Buffer + *BufferSize); ZeroMem (PagingEntry, SIZE_4KB); - if (ParentPagingEntry->Pce.Present) { + if ((ParentPagingEntry->Pce.Present == 1) || (Level <= MaxLeafLevel)) { // - // Create 512 child-level entries that map to 2M/4K. + // Create 512 child-level entries that map to 1G/2M/4K. // for (SubOffset = 0, Index = 0; Index < 512; Index++) { PagingEntry[Index].Uint64 = OneOfPagingEntry.Uint64 + SubOffset; @@ -519,10 +526,6 @@ PageTableLibMapInLevel ( // e.g.: Set PDE[0-255].ReadWrite = 0 // for (Index = 0; Index < 512; Index++) { - if (PagingEntry[Index].Pce.Present == 0) { - continue; - } - if (IsPle (&PagingEntry[Index], Level)) { PageTableLibSetPle (Level, &PagingEntry[Index], 0, &ChildAttribute, &ChildMask); } else { From 337f5799186debae9edca3136d737b9d51956206 Mon Sep 17 00:00:00 2001 From: Kun Qin Date: Tue, 15 Sep 2026 12:43:43 -0700 Subject: [PATCH 3/3] [CHERRY-PICK] UefiCpuPkg: CpuPageTableLib: Test for splitting large non-present table This change adds a unit test for mapping a small page from a large non- present page. The expected operation is that the test should trigger the large page table entry to split and marking the newly mapped entry properly while the rest of the smaller entries remain unmapped. Signed-off-by: Kun Qin (cherry picked from commit 71e8a50308b8d7bde0d103a955d877b0e6e447a8) --- .../UnitTest/CpuPageTableLibUnitTestHost.c | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/UefiCpuPkg/Library/CpuPageTableLib/UnitTest/CpuPageTableLibUnitTestHost.c b/UefiCpuPkg/Library/CpuPageTableLib/UnitTest/CpuPageTableLibUnitTestHost.c index 00f89b62412..76d32bbebe4 100644 --- a/UefiCpuPkg/Library/CpuPageTableLib/UnitTest/CpuPageTableLibUnitTestHost.c +++ b/UefiCpuPkg/Library/CpuPageTableLib/UnitTest/CpuPageTableLibUnitTestHost.c @@ -2,6 +2,7 @@ Unit tests of the CpuPageTableLib instance of the CpuPageTableLib class Copyright (c) 2022 - 2023, Intel Corporation. All rights reserved.
+ Copyright (c) Microsoft Corporation. SPDX-License-Identifier: BSD-2-Clause-Patent **/ @@ -821,6 +822,87 @@ TestCaseToCheckMapMaskAndAttr ( return UNIT_TEST_PASSED; } +/** + Check that splitting a non-present large page populates all small page + entries. + + @param[in] Context Unit test context. + + @retval UNIT_TEST_PASSED The unit test completed successfully. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion failed. +**/ +UNIT_TEST_STATUS +EFIAPI +TestCaseSplitNonPresentLargePage ( + IN UNIT_TEST_CONTEXT Context + ) +{ + UINTN PageTable; + PAGING_MODE PagingMode; + VOID *Buffer; + UINTN PageTableBufferSize; + IA32_MAP_ATTRIBUTE MapAttribute; + IA32_MAP_ATTRIBUTE MapMask; + RETURN_STATUS Status; + IA32_PAGING_ENTRY *PagingEntry; + UINTN Index; + + PagingMode = Paging4Level; + PageTableBufferSize = 0; + PageTable = 0; + MapAttribute.Uint64 = 0; + MapAttribute.Bits.Present = 1; + MapAttribute.Bits.Nx = 1; + MapMask.Uint64 = MAX_UINT64; + + // Start with a 2MB XP page. + Status = PageTableMap (&PageTable, PagingMode, NULL, &PageTableBufferSize, 0, SIZE_2MB, &MapAttribute, &MapMask, NULL); + UT_ASSERT_EQUAL (Status, RETURN_BUFFER_TOO_SMALL); + Buffer = AllocatePages (EFI_SIZE_TO_PAGES (PageTableBufferSize)); + Status = PageTableMap (&PageTable, PagingMode, Buffer, &PageTableBufferSize, 0, SIZE_2MB, &MapAttribute, &MapMask, NULL); + UT_ASSERT_EQUAL (Status, RETURN_SUCCESS); + + // Mark the 2MB page as RP. + MapAttribute.Uint64 = 0; + MapMask.Uint64 = 0; + MapMask.Bits.Present = 1; + Status = PageTableMap (&PageTable, PagingMode, NULL, &PageTableBufferSize, 0, SIZE_2MB, &MapAttribute, &MapMask, NULL); + UT_ASSERT_EQUAL (Status, RETURN_SUCCESS); + + // Map the first 4 KB page as R/W/X. + MapAttribute.Uint64 = 0; + MapAttribute.Bits.Present = 1; + MapAttribute.Bits.ReadWrite = 1; + MapMask.Uint64 = MAX_UINT64; + Status = PageTableMap (&PageTable, PagingMode, NULL, &PageTableBufferSize, 0, SIZE_4KB, &MapAttribute, &MapMask, NULL); + + // The above should trigger splitting, and thus needs more allocation. + UT_ASSERT_EQUAL (Status, RETURN_BUFFER_TOO_SMALL); + Buffer = AllocatePages (EFI_SIZE_TO_PAGES (PageTableBufferSize)); + Status = PageTableMap (&PageTable, PagingMode, Buffer, &PageTableBufferSize, 0, SIZE_4KB, &MapAttribute, &MapMask, NULL); + UT_ASSERT_EQUAL (Status, RETURN_SUCCESS); + + PagingEntry = (IA32_PAGING_ENTRY *)(UINTN)PageTable; + PagingEntry = (IA32_PAGING_ENTRY *)(UINTN)IA32_PNLE_PAGE_TABLE_BASE_ADDRESS (PagingEntry); + PagingEntry = (IA32_PAGING_ENTRY *)(UINTN)IA32_PNLE_PAGE_TABLE_BASE_ADDRESS (PagingEntry); + PagingEntry = (IA32_PAGING_ENTRY *)(UINTN)IA32_PNLE_PAGE_TABLE_BASE_ADDRESS (PagingEntry); + + // The newly mapped first 4 KB page should be present, R/W, and executable. + UT_ASSERT_EQUAL (PagingEntry[0].Pte4K.Bits.Present, 1); + UT_ASSERT_EQUAL (PagingEntry[0].Pte4K.Bits.ReadWrite, 1); + UT_ASSERT_EQUAL (PagingEntry[0].Pte4K.Bits.Nx, 0); + + // The rest should remain RP and XP. + for (Index = 1; Index < 512; Index++) { + UT_ASSERT_EQUAL (PagingEntry[Index].Pte4K.Bits.Present, 0); + UT_ASSERT_EQUAL (PagingEntry[Index].Pte4K.Bits.Nx, 1); + UT_ASSERT_EQUAL (PagingEntry[Index].Pte4K.Bits.PageTableBaseAddressLow, Index); + UT_ASSERT_EQUAL (PagingEntry[Index].Pte4K.Bits.PageTableBaseAddressHigh, 0); + } + + return UNIT_TEST_PASSED; +} + /** Initialize the unit test framework, suite, and unit tests for the sample unit tests and run the unit tests. @@ -872,6 +954,7 @@ UefiTestMain ( AddTestCase (ManualTestCase, "Check if the parent entry has different Nx attribute", "Manual Test Case6", TestCaseManualChangeNx, NULL, NULL, NULL); AddTestCase (ManualTestCase, "Check if the needed size is expected", "Manual Test Case7", TestCaseManualSizeNotMatch, NULL, NULL, NULL); AddTestCase (ManualTestCase, "Check MapMask when creating new page table or mapping not-present range", "Manual Test Case8", TestCaseToCheckMapMaskAndAttr, NULL, NULL, NULL); + AddTestCase (ManualTestCase, "Check splitting a non-present large page populates small page entries", "Manual Test Case9", TestCaseSplitNonPresentLargePage, NULL, NULL, NULL); // // Populate the Random Test Cases. //