UefiCpuPkg: Create CpuPageTableLib for manipulating X86 paging structs
The lib includes two APIs: * PageTableMap It creates/updates mapping from LA to PA. The implementation only supports paging structures used in 64bit mode now. PAE paging structure support will be added in future. * PageTableParse It parses the page table and returns the mapping relations in an array of IA32_MAP_ENTRY. It passed some stress tests. These test code will be upstreamed in other patches following edk2 Unit Test framework. Signed-off-by: Ray Ni <ray.ni@intel.com> Reviewed-by: Eric Dong <eric.dong@intel.com>
This commit is contained in:
330
UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableParse.c
Normal file
330
UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableParse.c
Normal file
@@ -0,0 +1,330 @@
|
||||
/** @file
|
||||
This library implements CpuPageTableLib that are generic for IA32 family CPU.
|
||||
|
||||
Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
|
||||
SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
|
||||
**/
|
||||
|
||||
#include "CpuPageTable.h"
|
||||
|
||||
/**
|
||||
Return the attribute of a 2M/1G page table entry.
|
||||
|
||||
@param[in] PleB Pointer to a 2M/1G page table entry.
|
||||
@param[in] ParentMapAttribute Pointer to the parent attribute.
|
||||
|
||||
@return Attribute of the 2M/1G page table entry.
|
||||
**/
|
||||
UINT64
|
||||
PageTableLibGetPleBMapAttribute (
|
||||
IN IA32_PAGE_LEAF_ENTRY_BIG_PAGESIZE *PleB,
|
||||
IN IA32_MAP_ATTRIBUTE *ParentMapAttribute
|
||||
)
|
||||
{
|
||||
IA32_MAP_ATTRIBUTE MapAttribute;
|
||||
|
||||
//
|
||||
// PageTableBaseAddress cannot be assigned field to field
|
||||
// because their bit positions are different in IA32_MAP_ATTRIBUTE and IA32_PAGE_LEAF_ENTRY_BIG_PAGESIZE.
|
||||
//
|
||||
MapAttribute.Uint64 = IA32_PLEB_PAGE_TABLE_BASE_ADDRESS (PleB);
|
||||
|
||||
MapAttribute.Bits.Present = ParentMapAttribute->Bits.Present & PleB->Bits.Present;
|
||||
MapAttribute.Bits.ReadWrite = ParentMapAttribute->Bits.ReadWrite & PleB->Bits.ReadWrite;
|
||||
MapAttribute.Bits.UserSupervisor = ParentMapAttribute->Bits.UserSupervisor & PleB->Bits.UserSupervisor;
|
||||
MapAttribute.Bits.Nx = ParentMapAttribute->Bits.Nx | PleB->Bits.Nx;
|
||||
MapAttribute.Bits.WriteThrough = PleB->Bits.WriteThrough;
|
||||
MapAttribute.Bits.CacheDisabled = PleB->Bits.CacheDisabled;
|
||||
MapAttribute.Bits.Accessed = PleB->Bits.Accessed;
|
||||
|
||||
MapAttribute.Bits.Pat = PleB->Bits.Pat;
|
||||
MapAttribute.Bits.Dirty = PleB->Bits.Dirty;
|
||||
MapAttribute.Bits.Global = PleB->Bits.Global;
|
||||
MapAttribute.Bits.ProtectionKey = PleB->Bits.ProtectionKey;
|
||||
|
||||
return MapAttribute.Uint64;
|
||||
}
|
||||
|
||||
/**
|
||||
Return the attribute of a 4K page table entry.
|
||||
|
||||
@param[in] Pte4K Pointer to a 4K page table entry.
|
||||
@param[in] ParentMapAttribute Pointer to the parent attribute.
|
||||
|
||||
@return Attribute of the 4K page table entry.
|
||||
**/
|
||||
UINT64
|
||||
PageTableLibGetPte4KMapAttribute (
|
||||
IN IA32_PTE_4K *Pte4K,
|
||||
IN IA32_MAP_ATTRIBUTE *ParentMapAttribute
|
||||
)
|
||||
{
|
||||
IA32_MAP_ATTRIBUTE MapAttribute;
|
||||
|
||||
MapAttribute.Uint64 = IA32_PTE4K_PAGE_TABLE_BASE_ADDRESS (Pte4K);
|
||||
|
||||
MapAttribute.Bits.Present = ParentMapAttribute->Bits.Present & Pte4K->Bits.Present;
|
||||
MapAttribute.Bits.ReadWrite = ParentMapAttribute->Bits.ReadWrite & Pte4K->Bits.ReadWrite;
|
||||
MapAttribute.Bits.UserSupervisor = ParentMapAttribute->Bits.UserSupervisor & Pte4K->Bits.UserSupervisor;
|
||||
MapAttribute.Bits.Nx = ParentMapAttribute->Bits.Nx | Pte4K->Bits.Nx;
|
||||
MapAttribute.Bits.WriteThrough = Pte4K->Bits.WriteThrough;
|
||||
MapAttribute.Bits.CacheDisabled = Pte4K->Bits.CacheDisabled;
|
||||
MapAttribute.Bits.Accessed = Pte4K->Bits.Accessed;
|
||||
|
||||
MapAttribute.Bits.Pat = Pte4K->Bits.Pat;
|
||||
MapAttribute.Bits.Dirty = Pte4K->Bits.Dirty;
|
||||
MapAttribute.Bits.Global = Pte4K->Bits.Global;
|
||||
MapAttribute.Bits.ProtectionKey = Pte4K->Bits.ProtectionKey;
|
||||
|
||||
return MapAttribute.Uint64;
|
||||
}
|
||||
|
||||
/**
|
||||
Return the attribute of a non-leaf page table entry.
|
||||
|
||||
@param[in] Pnle Pointer to a non-leaf page table entry.
|
||||
@param[in] ParentMapAttribute Pointer to the parent attribute.
|
||||
|
||||
@return Attribute of the non-leaf page table entry.
|
||||
**/
|
||||
UINT64
|
||||
PageTableLibGetPnleMapAttribute (
|
||||
IN IA32_PAGE_NON_LEAF_ENTRY *Pnle,
|
||||
IN IA32_MAP_ATTRIBUTE *ParentMapAttribute
|
||||
)
|
||||
{
|
||||
IA32_MAP_ATTRIBUTE MapAttribute;
|
||||
|
||||
MapAttribute.Uint64 = Pnle->Uint64;
|
||||
|
||||
MapAttribute.Bits.Present = ParentMapAttribute->Bits.Present & Pnle->Bits.Present;
|
||||
MapAttribute.Bits.ReadWrite = ParentMapAttribute->Bits.ReadWrite & Pnle->Bits.ReadWrite;
|
||||
MapAttribute.Bits.UserSupervisor = ParentMapAttribute->Bits.UserSupervisor & Pnle->Bits.UserSupervisor;
|
||||
MapAttribute.Bits.Nx = ParentMapAttribute->Bits.Nx | Pnle->Bits.Nx;
|
||||
MapAttribute.Bits.WriteThrough = Pnle->Bits.WriteThrough;
|
||||
MapAttribute.Bits.CacheDisabled = Pnle->Bits.CacheDisabled;
|
||||
MapAttribute.Bits.Accessed = Pnle->Bits.Accessed;
|
||||
return MapAttribute.Uint64;
|
||||
}
|
||||
|
||||
/**
|
||||
Return TRUE when the page table entry is a leaf entry that points to the physical address memory.
|
||||
Return FALSE when the page table entry is a non-leaf entry that points to the page table entries.
|
||||
|
||||
@param[in] PagingEntry Pointer to the page table entry.
|
||||
@param[in] Level Page level where the page table entry resides in.
|
||||
|
||||
@retval TRUE It's a leaf entry.
|
||||
@retval FALSE It's a non-leaf entry.
|
||||
**/
|
||||
BOOLEAN
|
||||
IsPle (
|
||||
IN IA32_PAGING_ENTRY *PagingEntry,
|
||||
IN UINTN Level
|
||||
)
|
||||
{
|
||||
//
|
||||
// PML5E and PML4E are always non-leaf entries.
|
||||
//
|
||||
if (Level == 1) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (((Level == 3) || (Level == 2))) {
|
||||
if (PagingEntry->PleB.Bits.MustBeOne == 1) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
Recursively parse the non-leaf page table entries.
|
||||
|
||||
@param[in] PageTableBaseAddress The base address of the 512 non-leaf page table entries in the specified level.
|
||||
@param[in] Level Page level. Could be 5, 4, 3, 2, 1.
|
||||
@param[in] RegionStart The base linear address of the region covered by the non-leaf page table entries.
|
||||
@param[in] ParentMapAttribute The mapping attribute of the parent entries.
|
||||
@param[in, out] Map Pointer to an array that describes multiple linear address ranges.
|
||||
@param[in, out] MapCount Pointer to a UINTN that hold the actual number of entries in the Map.
|
||||
@param[in] MapCapacity The maximum number of entries the Map can hold.
|
||||
@param[in] LastEntry Pointer to last map entry.
|
||||
@param[in] OneEntry Pointer to a library internal storage that holds one map entry.
|
||||
It's used when Map array is used up.
|
||||
**/
|
||||
VOID
|
||||
PageTableLibParsePnle (
|
||||
IN UINT64 PageTableBaseAddress,
|
||||
IN UINTN Level,
|
||||
IN UINT64 RegionStart,
|
||||
IN IA32_MAP_ATTRIBUTE *ParentMapAttribute,
|
||||
IN OUT IA32_MAP_ENTRY *Map,
|
||||
IN OUT UINTN *MapCount,
|
||||
IN UINTN MapCapacity,
|
||||
IN IA32_MAP_ENTRY **LastEntry,
|
||||
IN IA32_MAP_ENTRY *OneEntry
|
||||
)
|
||||
{
|
||||
IA32_PAGING_ENTRY *PagingEntry;
|
||||
UINTN Index;
|
||||
IA32_MAP_ATTRIBUTE MapAttribute;
|
||||
UINT64 RegionLength;
|
||||
|
||||
ASSERT (OneEntry != NULL);
|
||||
|
||||
PagingEntry = (IA32_PAGING_ENTRY *)(UINTN)PageTableBaseAddress;
|
||||
RegionLength = REGION_LENGTH (Level);
|
||||
|
||||
for (Index = 0; Index < 512; Index++, RegionStart += RegionLength) {
|
||||
if (PagingEntry[Index].Pce.Present == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (IsPle (&PagingEntry[Index], Level)) {
|
||||
ASSERT (Level == 1 || Level == 2 || Level == 3);
|
||||
|
||||
if (Level == 1) {
|
||||
MapAttribute.Uint64 = PageTableLibGetPte4KMapAttribute (&PagingEntry[Index].Pte4K, ParentMapAttribute);
|
||||
} else {
|
||||
MapAttribute.Uint64 = PageTableLibGetPleBMapAttribute (&PagingEntry[Index].PleB, ParentMapAttribute);
|
||||
}
|
||||
|
||||
if ((*LastEntry != NULL) &&
|
||||
((*LastEntry)->LinearAddress + (*LastEntry)->Length == RegionStart) &&
|
||||
(IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS (&(*LastEntry)->Attribute) + (*LastEntry)->Length
|
||||
== IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS (&MapAttribute)) &&
|
||||
(IA32_MAP_ATTRIBUTE_ATTRIBUTES (&(*LastEntry)->Attribute) == IA32_MAP_ATTRIBUTE_ATTRIBUTES (&MapAttribute))
|
||||
)
|
||||
{
|
||||
//
|
||||
// Extend LastEntry.
|
||||
//
|
||||
(*LastEntry)->Length += RegionLength;
|
||||
} else {
|
||||
if (*MapCount < MapCapacity) {
|
||||
//
|
||||
// LastEntry points to next map entry in the array.
|
||||
//
|
||||
*LastEntry = &Map[*MapCount];
|
||||
} else {
|
||||
//
|
||||
// LastEntry points to library internal map entry.
|
||||
//
|
||||
*LastEntry = OneEntry;
|
||||
}
|
||||
|
||||
//
|
||||
// Set LastEntry.
|
||||
//
|
||||
(*LastEntry)->LinearAddress = RegionStart;
|
||||
(*LastEntry)->Length = RegionLength;
|
||||
(*LastEntry)->Attribute.Uint64 = MapAttribute.Uint64;
|
||||
(*MapCount)++;
|
||||
}
|
||||
} else {
|
||||
MapAttribute.Uint64 = PageTableLibGetPnleMapAttribute (&PagingEntry[Index].Pnle, ParentMapAttribute);
|
||||
PageTableLibParsePnle (
|
||||
IA32_PNLE_PAGE_TABLE_BASE_ADDRESS (&PagingEntry[Index].Pnle),
|
||||
Level - 1,
|
||||
RegionStart,
|
||||
&MapAttribute,
|
||||
Map,
|
||||
MapCount,
|
||||
MapCapacity,
|
||||
LastEntry,
|
||||
OneEntry
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Parse page table.
|
||||
|
||||
@param[in] PageTable Pointer to the page table.
|
||||
@param[in] PagingMode The paging mode.
|
||||
@param[out] Map Return an array that describes multiple linear address ranges.
|
||||
@param[in, out] MapCount On input, the maximum number of entries that Map can hold.
|
||||
On output, the number of entries in Map.
|
||||
|
||||
@retval RETURN_UNSUPPORTED PageLevel is not 5 or 4.
|
||||
@retval RETURN_INVALID_PARAMETER MapCount is NULL.
|
||||
@retval RETURN_INVALID_PARAMETER *MapCount is not 0 but Map is NULL.
|
||||
@retval RETURN_BUFFER_TOO_SMALL *MapCount is too small.
|
||||
@retval RETURN_SUCCESS Page table is parsed successfully.
|
||||
**/
|
||||
RETURN_STATUS
|
||||
EFIAPI
|
||||
PageTableParse (
|
||||
IN UINTN PageTable,
|
||||
IN PAGING_MODE PagingMode,
|
||||
OUT IA32_MAP_ENTRY *Map,
|
||||
IN OUT UINTN *MapCount
|
||||
)
|
||||
{
|
||||
UINTN MapCapacity;
|
||||
IA32_MAP_ATTRIBUTE NopAttribute;
|
||||
IA32_MAP_ENTRY *LastEntry;
|
||||
IA32_MAP_ENTRY OneEntry;
|
||||
UINTN MaxLevel;
|
||||
|
||||
if ((PagingMode == Paging32bit) || (PagingMode == PagingPae) || (PagingMode >= PagingModeMax)) {
|
||||
//
|
||||
// 32bit paging is never supported.
|
||||
// PAE paging will be supported later.
|
||||
//
|
||||
return RETURN_UNSUPPORTED;
|
||||
}
|
||||
|
||||
if (MapCount == NULL) {
|
||||
return RETURN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if ((*MapCount != 0) && (Map == NULL)) {
|
||||
return RETURN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (PageTable == 0) {
|
||||
*MapCount = 0;
|
||||
return RETURN_SUCCESS;
|
||||
}
|
||||
|
||||
//
|
||||
// Page table layout is as below:
|
||||
//
|
||||
// [IA32_CR3]
|
||||
// |
|
||||
// |
|
||||
// V
|
||||
// [IA32_PML5E]
|
||||
// ...
|
||||
// [IA32_PML5E] --> [IA32_PML4E]
|
||||
// ...
|
||||
// [IA32_PML4E] --> [IA32_PDPTE_1G] --> 1G aligned physical address
|
||||
// ...
|
||||
// [IA32_PDPTE] --> [IA32_PDE_2M] --> 2M aligned physical address
|
||||
// ...
|
||||
// [IA32_PDE] --> [IA32_PTE_4K] --> 4K aligned physical address
|
||||
// ...
|
||||
// [IA32_PTE_4K] --> 4K aligned physical address
|
||||
//
|
||||
|
||||
NopAttribute.Uint64 = 0;
|
||||
NopAttribute.Bits.Present = 1;
|
||||
NopAttribute.Bits.ReadWrite = 1;
|
||||
NopAttribute.Bits.UserSupervisor = 1;
|
||||
|
||||
MaxLevel = (UINT8)(PagingMode >> 8);
|
||||
MapCapacity = *MapCount;
|
||||
*MapCount = 0;
|
||||
LastEntry = NULL;
|
||||
PageTableLibParsePnle ((UINT64)PageTable, MaxLevel, 0, &NopAttribute, Map, MapCount, MapCapacity, &LastEntry, &OneEntry);
|
||||
|
||||
if (*MapCount > MapCapacity) {
|
||||
return RETURN_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
return RETURN_SUCCESS;
|
||||
}
|
Reference in New Issue
Block a user