diff --git a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c index c488825e336..fc08420cd05 100644 --- a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c +++ b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c @@ -390,17 +390,14 @@ 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 { + // + // 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); } @@ -436,17 +433,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 == 1) || (Level <= MaxLeafLevel)) { + // + // 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; + 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,12 +526,6 @@ 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 (IsPle (&PagingEntry[Index], Level)) { PageTableLibSetPle (Level, &PagingEntry[Index], 0, &ChildAttribute, &ChildMask); } else { 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. //