mirror of https://github.com/acidanthera/audk.git
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:
parent
f1688ec9da
commit
75e3c2435c
|
@ -0,0 +1,129 @@
|
|||
/** @file
|
||||
Public include file for PageTableLib library.
|
||||
|
||||
Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
|
||||
SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
|
||||
**/
|
||||
|
||||
#ifndef PAGE_TABLE_LIB_H_
|
||||
#define PAGE_TABLE_LIB_H_
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
UINT64 Present : 1; // 0 = Not present in memory, 1 = Present in memory
|
||||
UINT64 ReadWrite : 1; // 0 = Read-Only, 1= Read/Write
|
||||
UINT64 UserSupervisor : 1; // 0 = Supervisor, 1=User
|
||||
UINT64 WriteThrough : 1; // 0 = Write-Back caching, 1=Write-Through caching
|
||||
UINT64 CacheDisabled : 1; // 0 = Cached, 1=Non-Cached
|
||||
UINT64 Accessed : 1; // 0 = Not accessed, 1 = Accessed (set by CPU)
|
||||
UINT64 Dirty : 1; // 0 = Not dirty, 1 = Dirty (set by CPU)
|
||||
UINT64 Pat : 1; // PAT
|
||||
|
||||
UINT64 Global : 1; // 0 = Not global, 1 = Global (if CR4.PGE = 1)
|
||||
UINT64 Reserved1 : 3; // Ignored
|
||||
|
||||
UINT64 PageTableBaseAddress : 40; // Page Table Base Address
|
||||
UINT64 Reserved2 : 7; // Ignored
|
||||
UINT64 ProtectionKey : 4; // Protection key
|
||||
UINT64 Nx : 1; // No Execute bit
|
||||
} Bits;
|
||||
UINT64 Uint64;
|
||||
} IA32_MAP_ATTRIBUTE;
|
||||
|
||||
#define IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS_MASK 0xFFFFFFFFFF000ull
|
||||
#define IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS(pa) ((pa)->Uint64 & IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS_MASK)
|
||||
#define IA32_MAP_ATTRIBUTE_ATTRIBUTES(pa) ((pa)->Uint64 & ~IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS_MASK)
|
||||
|
||||
//
|
||||
// Below enum follows "4.1.1 Four Paging Modes" in Chapter 4 Paging of SDM Volume 3.
|
||||
// Page1GB is only supported in 4-level and 5-level.
|
||||
//
|
||||
typedef enum {
|
||||
Paging32bit,
|
||||
|
||||
//
|
||||
// High byte in paging mode indicates the max levels of the page table.
|
||||
// Low byte in paging mode indicates the max level that can be a leaf entry.
|
||||
//
|
||||
PagingPae = 0x0302,
|
||||
|
||||
Paging4Level = 0x0402,
|
||||
Paging4Level1GB = 0x0403,
|
||||
|
||||
Paging5Level = 0x0502,
|
||||
Paging5Level1GB = 0x0503,
|
||||
|
||||
PagingModeMax
|
||||
} PAGING_MODE;
|
||||
|
||||
/**
|
||||
Create or update page table to map [LinearAddress, LinearAddress + Length) with specified attribute.
|
||||
|
||||
@param[in, out] PageTable The pointer to the page table to update, or pointer to NULL if a new page table is to be created.
|
||||
@param[in] PagingMode The paging mode.
|
||||
@param[in] Buffer The free buffer to be used for page table creation/updating.
|
||||
@param[in, out] BufferSize The buffer size.
|
||||
On return, the remaining buffer size.
|
||||
The free buffer is used from the end so caller can supply the same Buffer pointer with an updated
|
||||
BufferSize in the second call to this API.
|
||||
@param[in] LinearAddress The start of the linear address range.
|
||||
@param[in] Length The length of the linear address range.
|
||||
@param[in] Attribute The attribute of the linear address range.
|
||||
All non-reserved fields in IA32_MAP_ATTRIBUTE are supported to set in the page table.
|
||||
Page table entries that map the linear address range are reset to 0 before set to the new attribute
|
||||
when a new physical base address is set.
|
||||
@param[in] Mask The mask used for attribute. The corresponding field in Attribute is ignored if that in Mask is 0.
|
||||
|
||||
@retval RETURN_UNSUPPORTED PagingMode is not supported.
|
||||
@retval RETURN_INVALID_PARAMETER PageTable, BufferSize, Attribute or Mask is NULL.
|
||||
@retval RETURN_INVALID_PARAMETER *BufferSize is not multiple of 4KB.
|
||||
@retval RETURN_BUFFER_TOO_SMALL The buffer is too small for page table creation/updating.
|
||||
BufferSize is updated to indicate the expected buffer size.
|
||||
Caller may still get RETURN_BUFFER_TOO_SMALL with the new BufferSize.
|
||||
@retval RETURN_SUCCESS PageTable is created/updated successfully.
|
||||
**/
|
||||
RETURN_STATUS
|
||||
EFIAPI
|
||||
PageTableMap (
|
||||
IN OUT UINTN *PageTable OPTIONAL,
|
||||
IN PAGING_MODE PagingMode,
|
||||
IN VOID *Buffer,
|
||||
IN OUT UINTN *BufferSize,
|
||||
IN UINT64 LinearAddress,
|
||||
IN UINT64 Length,
|
||||
IN IA32_MAP_ATTRIBUTE *Attribute,
|
||||
IN IA32_MAP_ATTRIBUTE *Mask
|
||||
);
|
||||
|
||||
typedef struct {
|
||||
UINT64 LinearAddress;
|
||||
UINT64 Length;
|
||||
IA32_MAP_ATTRIBUTE Attribute;
|
||||
} IA32_MAP_ENTRY;
|
||||
|
||||
/**
|
||||
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,
|
||||
IN IA32_MAP_ENTRY *Map,
|
||||
IN OUT UINTN *MapCount
|
||||
);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,204 @@
|
|||
/** @file
|
||||
Internal header for CpuPageTableLib.
|
||||
|
||||
Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
|
||||
SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
|
||||
**/
|
||||
|
||||
#ifndef CPU_PAGE_TABLE_H_
|
||||
#define CPU_PAGE_TABLE_H_
|
||||
|
||||
#include <Base.h>
|
||||
#include <Library/BaseLib.h>
|
||||
#include <Library/BaseMemoryLib.h>
|
||||
#include <Library/DebugLib.h>
|
||||
#include <Library/CpuPageTableLib.h>
|
||||
|
||||
#define IA32_PE_BASE_ADDRESS_MASK_40 0xFFFFFFFFFF000ull
|
||||
#define IA32_PE_BASE_ADDRESS_MASK_39 0xFFFFFFFFFE000ull
|
||||
|
||||
#define REGION_LENGTH(l) LShiftU64 (1, (l) * 9 + 3)
|
||||
|
||||
typedef struct {
|
||||
UINT64 Present : 1; // 0 = Not present in memory, 1 = Present in memory
|
||||
} IA32_PAGE_COMMON_ENTRY;
|
||||
|
||||
///
|
||||
/// Format of a non-leaf entry that references a page table entry
|
||||
///
|
||||
typedef union {
|
||||
struct {
|
||||
UINT64 Present : 1; // 0 = Not present in memory, 1 = Present in memory
|
||||
UINT64 ReadWrite : 1; // 0 = Read-Only, 1= Read/Write
|
||||
UINT64 UserSupervisor : 1; // 0 = Supervisor, 1=User
|
||||
UINT64 WriteThrough : 1; // 0 = Write-Back caching, 1=Write-Through caching
|
||||
UINT64 CacheDisabled : 1; // 0 = Cached, 1=Non-Cached
|
||||
UINT64 Accessed : 1; // 0 = Not accessed, 1 = Accessed (set by CPU)
|
||||
UINT64 Available0 : 1; // Ignored
|
||||
UINT64 MustBeZero : 1; // Must Be Zero
|
||||
|
||||
UINT64 Available2 : 4; // Ignored
|
||||
|
||||
UINT64 PageTableBaseAddress : 40; // Page Table Base Address
|
||||
UINT64 Available3 : 11; // Ignored
|
||||
UINT64 Nx : 1; // No Execute bit
|
||||
} Bits;
|
||||
UINT64 Uint64;
|
||||
} IA32_PAGE_NON_LEAF_ENTRY;
|
||||
|
||||
#define IA32_PNLE_PAGE_TABLE_BASE_ADDRESS(pa) ((pa)->Uint64 & IA32_PE_BASE_ADDRESS_MASK_40)
|
||||
|
||||
///
|
||||
/// Format of a PML5 Entry (PML5E) that References a PML4 Table
|
||||
///
|
||||
typedef IA32_PAGE_NON_LEAF_ENTRY IA32_PML5E;
|
||||
|
||||
///
|
||||
/// Format of a PML4 Entry (PML4E) that References a Page-Directory-Pointer Table
|
||||
///
|
||||
typedef IA32_PAGE_NON_LEAF_ENTRY IA32_PML4E;
|
||||
|
||||
///
|
||||
/// Format of a Page-Directory-Pointer-Table Entry (PDPTE) that References a Page Directory
|
||||
///
|
||||
typedef IA32_PAGE_NON_LEAF_ENTRY IA32_PDPTE;
|
||||
|
||||
///
|
||||
/// Format of a Page-Directory Entry that References a Page Table
|
||||
///
|
||||
typedef IA32_PAGE_NON_LEAF_ENTRY IA32_PDE;
|
||||
|
||||
///
|
||||
/// Format of a leaf entry that Maps a 1-Gbyte or 2-MByte Page
|
||||
///
|
||||
typedef union {
|
||||
struct {
|
||||
UINT64 Present : 1; // 0 = Not present in memory, 1 = Present in memory
|
||||
UINT64 ReadWrite : 1; // 0 = Read-Only, 1= Read/Write
|
||||
UINT64 UserSupervisor : 1; // 0 = Supervisor, 1=User
|
||||
UINT64 WriteThrough : 1; // 0 = Write-Back caching, 1=Write-Through caching
|
||||
UINT64 CacheDisabled : 1; // 0 = Cached, 1=Non-Cached
|
||||
UINT64 Accessed : 1; // 0 = Not accessed, 1 = Accessed (set by CPU)
|
||||
UINT64 Dirty : 1; // 0 = Not dirty, 1 = Dirty (set by CPU)
|
||||
UINT64 MustBeOne : 1; // Page Size. Must Be One
|
||||
|
||||
UINT64 Global : 1; // 0 = Not global, 1 = Global (if CR4.PGE = 1)
|
||||
UINT64 Available1 : 3; // Ignored
|
||||
UINT64 Pat : 1; // PAT
|
||||
|
||||
UINT64 PageTableBaseAddress : 39; // Page Table Base Address
|
||||
UINT64 Available3 : 7; // Ignored
|
||||
UINT64 ProtectionKey : 4; // Protection key
|
||||
UINT64 Nx : 1; // No Execute bit
|
||||
} Bits;
|
||||
UINT64 Uint64;
|
||||
} IA32_PAGE_LEAF_ENTRY_BIG_PAGESIZE;
|
||||
#define IA32_PLEB_PAGE_TABLE_BASE_ADDRESS(pa) ((pa)->Uint64 & IA32_PE_BASE_ADDRESS_MASK_39)
|
||||
|
||||
///
|
||||
/// Format of a Page-Directory Entry that Maps a 2-MByte Page
|
||||
///
|
||||
typedef IA32_PAGE_LEAF_ENTRY_BIG_PAGESIZE IA32_PDE_2M;
|
||||
|
||||
///
|
||||
/// Format of a Page-Directory-Pointer-Table Entry (PDPTE) that Maps a 1-GByte Page
|
||||
///
|
||||
typedef IA32_PAGE_LEAF_ENTRY_BIG_PAGESIZE IA32_PDPTE_1G;
|
||||
|
||||
///
|
||||
/// Format of a Page-Table Entry that Maps a 4-KByte Page
|
||||
///
|
||||
typedef union {
|
||||
struct {
|
||||
UINT64 Present : 1; // 0 = Not present in memory, 1 = Present in memory
|
||||
UINT64 ReadWrite : 1; // 0 = Read-Only, 1= Read/Write
|
||||
UINT64 UserSupervisor : 1; // 0 = Supervisor, 1=User
|
||||
UINT64 WriteThrough : 1; // 0 = Write-Back caching, 1=Write-Through caching
|
||||
UINT64 CacheDisabled : 1; // 0 = Cached, 1=Non-Cached
|
||||
UINT64 Accessed : 1; // 0 = Not accessed, 1 = Accessed (set by CPU)
|
||||
UINT64 Dirty : 1; // 0 = Not dirty, 1 = Dirty (set by CPU)
|
||||
UINT64 Pat : 1; // PAT
|
||||
|
||||
UINT64 Global : 1; // 0 = Not global, 1 = Global (if CR4.PGE = 1)
|
||||
UINT64 Available1 : 3; // Ignored
|
||||
|
||||
UINT64 PageTableBaseAddress : 40; // Page Table Base Address
|
||||
UINT64 Available3 : 7; // Ignored
|
||||
UINT64 ProtectionKey : 4; // Protection key
|
||||
UINT64 Nx : 1; // No Execute bit
|
||||
} Bits;
|
||||
UINT64 Uint64;
|
||||
} IA32_PTE_4K;
|
||||
#define IA32_PTE4K_PAGE_TABLE_BASE_ADDRESS(pa) ((pa)->Uint64 & IA32_PE_BASE_ADDRESS_MASK_40)
|
||||
|
||||
///
|
||||
/// Format of a Page-Directory-Pointer-Table Entry (PDPTE) that References a Page Directory (32bit PAE specific)
|
||||
///
|
||||
typedef union {
|
||||
struct {
|
||||
UINT64 Present : 1; // 0 = Not present in memory, 1 = Present in memory
|
||||
UINT64 MustBeZero : 2; // Must Be Zero
|
||||
UINT64 WriteThrough : 1; // 0 = Write-Back caching, 1=Write-Through caching
|
||||
UINT64 CacheDisabled : 1; // 0 = Cached, 1=Non-Cached
|
||||
UINT64 MustBeZero2 : 4; // Must Be Zero
|
||||
|
||||
UINT64 Available : 3; // Ignored
|
||||
|
||||
UINT64 PageTableBaseAddress : 40; // Page Table Base Address
|
||||
UINT64 MustBeZero3 : 12; // Must Be Zero
|
||||
} Bits;
|
||||
UINT64 Uint64;
|
||||
} IA32_PDPTE_PAE;
|
||||
|
||||
typedef union {
|
||||
IA32_PAGE_NON_LEAF_ENTRY Pnle; // To access Pml5, Pml4, Pdpte and Pde.
|
||||
IA32_PML5E Pml5;
|
||||
IA32_PML4E Pml4;
|
||||
IA32_PDPTE Pdpte;
|
||||
IA32_PDE Pde;
|
||||
|
||||
IA32_PAGE_LEAF_ENTRY_BIG_PAGESIZE PleB; // to access Pdpte1G and Pde2M.
|
||||
IA32_PDPTE_1G Pdpte1G;
|
||||
IA32_PDE_2M Pde2M;
|
||||
|
||||
IA32_PTE_4K Pte4K;
|
||||
|
||||
IA32_PDPTE_PAE PdptePae;
|
||||
IA32_PAGE_COMMON_ENTRY Pce; // To access all common bits in above entries.
|
||||
|
||||
UINT64 Uint64;
|
||||
UINTN Uintn;
|
||||
} IA32_PAGING_ENTRY;
|
||||
|
||||
/**
|
||||
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
|
||||
);
|
||||
|
||||
/**
|
||||
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
|
||||
);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,35 @@
|
|||
## @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
|
||||
#
|
||||
##
|
||||
|
||||
[Defines]
|
||||
INF_VERSION = 0x00010005
|
||||
BASE_NAME = CpuPageTableLib
|
||||
FILE_GUID = 524ed6a1-f661-451b-929b-b54d755c914a
|
||||
MODULE_TYPE = BASE
|
||||
VERSION_STRING = 1.0
|
||||
LIBRARY_CLASS = CpuPageTableLib
|
||||
|
||||
#
|
||||
# The following information is for reference only and not required by the build tools.
|
||||
#
|
||||
# VALID_ARCHITECTURES = IA32 X64
|
||||
#
|
||||
|
||||
[Sources]
|
||||
CpuPageTableMap.c
|
||||
CpuPageTableParse.c
|
||||
CpuPageTable.h
|
||||
|
||||
[Packages]
|
||||
MdePkg/MdePkg.dec
|
||||
UefiCpuPkg/UefiCpuPkg.dec
|
||||
|
||||
[LibraryClasses]
|
||||
BaseLib
|
||||
BaseMemoryLib
|
||||
DebugLib
|
|
@ -0,0 +1,543 @@
|
|||
/** @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"
|
||||
|
||||
/**
|
||||
Set the IA32_PTE_4K.
|
||||
|
||||
@param[in] Pte4K Pointer to IA32_PTE_4K.
|
||||
@param[in] Offset The offset within the linear address range.
|
||||
@param[in] Attribute The attribute of the linear address range.
|
||||
All non-reserved fields in IA32_MAP_ATTRIBUTE are supported to set in the page table.
|
||||
Page table entry is reset to 0 before set to the new attribute when a new physical base address is set.
|
||||
@param[in] Mask The mask used for attribute. The corresponding field in Attribute is ignored if that in Mask is 0.
|
||||
**/
|
||||
VOID
|
||||
PageTableLibSetPte4K (
|
||||
IN IA32_PTE_4K *Pte4K,
|
||||
IN UINT64 Offset,
|
||||
IN IA32_MAP_ATTRIBUTE *Attribute,
|
||||
IN IA32_MAP_ATTRIBUTE *Mask
|
||||
)
|
||||
{
|
||||
if (Mask->Bits.PageTableBaseAddress) {
|
||||
//
|
||||
// Reset all attributes when the physical address is changed.
|
||||
//
|
||||
Pte4K->Uint64 = IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS (Attribute) + Offset;
|
||||
}
|
||||
|
||||
if (Mask->Bits.Present) {
|
||||
Pte4K->Bits.Present = Attribute->Bits.Present;
|
||||
}
|
||||
|
||||
if (Mask->Bits.ReadWrite) {
|
||||
Pte4K->Bits.ReadWrite = Attribute->Bits.ReadWrite;
|
||||
}
|
||||
|
||||
if (Mask->Bits.UserSupervisor) {
|
||||
Pte4K->Bits.UserSupervisor = Attribute->Bits.UserSupervisor;
|
||||
}
|
||||
|
||||
if (Mask->Bits.WriteThrough) {
|
||||
Pte4K->Bits.WriteThrough = Attribute->Bits.WriteThrough;
|
||||
}
|
||||
|
||||
if (Mask->Bits.CacheDisabled) {
|
||||
Pte4K->Bits.CacheDisabled = Attribute->Bits.CacheDisabled;
|
||||
}
|
||||
|
||||
if (Mask->Bits.Accessed) {
|
||||
Pte4K->Bits.Accessed = Attribute->Bits.Accessed;
|
||||
}
|
||||
|
||||
if (Mask->Bits.Dirty) {
|
||||
Pte4K->Bits.Dirty = Attribute->Bits.Dirty;
|
||||
}
|
||||
|
||||
if (Mask->Bits.Pat) {
|
||||
Pte4K->Bits.Pat = Attribute->Bits.Pat;
|
||||
}
|
||||
|
||||
if (Mask->Bits.Global) {
|
||||
Pte4K->Bits.Global = Attribute->Bits.Global;
|
||||
}
|
||||
|
||||
if (Mask->Bits.ProtectionKey) {
|
||||
Pte4K->Bits.ProtectionKey = Attribute->Bits.ProtectionKey;
|
||||
}
|
||||
|
||||
if (Mask->Bits.Nx) {
|
||||
Pte4K->Bits.Nx = Attribute->Bits.Nx;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Set the IA32_PDPTE_1G or IA32_PDE_2M.
|
||||
|
||||
@param[in] PleB Pointer to PDPTE_1G or PDE_2M. Both share the same structure definition.
|
||||
@param[in] Offset The offset within the linear address range.
|
||||
@param[in] Attribute The attribute of the linear address range.
|
||||
All non-reserved fields in IA32_MAP_ATTRIBUTE are supported to set in the page table.
|
||||
Page table entry is reset to 0 before set to the new attribute when a new physical base address is set.
|
||||
@param[in] Mask The mask used for attribute. The corresponding field in Attribute is ignored if that in Mask is 0.
|
||||
**/
|
||||
VOID
|
||||
PageTableLibSetPleB (
|
||||
IN IA32_PAGE_LEAF_ENTRY_BIG_PAGESIZE *PleB,
|
||||
IN UINT64 Offset,
|
||||
IN IA32_MAP_ATTRIBUTE *Attribute,
|
||||
IN IA32_MAP_ATTRIBUTE *Mask
|
||||
)
|
||||
{
|
||||
if (Mask->Bits.PageTableBaseAddress) {
|
||||
//
|
||||
// Reset all attributes when the physical address is changed.
|
||||
//
|
||||
PleB->Uint64 = IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS (Attribute) + Offset;
|
||||
}
|
||||
|
||||
PleB->Bits.MustBeOne = 1;
|
||||
|
||||
if (Mask->Bits.Present) {
|
||||
PleB->Bits.Present = Attribute->Bits.Present;
|
||||
}
|
||||
|
||||
if (Mask->Bits.ReadWrite) {
|
||||
PleB->Bits.ReadWrite = Attribute->Bits.ReadWrite;
|
||||
}
|
||||
|
||||
if (Mask->Bits.UserSupervisor) {
|
||||
PleB->Bits.UserSupervisor = Attribute->Bits.UserSupervisor;
|
||||
}
|
||||
|
||||
if (Mask->Bits.WriteThrough) {
|
||||
PleB->Bits.WriteThrough = Attribute->Bits.WriteThrough;
|
||||
}
|
||||
|
||||
if (Mask->Bits.CacheDisabled) {
|
||||
PleB->Bits.CacheDisabled = Attribute->Bits.CacheDisabled;
|
||||
}
|
||||
|
||||
if (Mask->Bits.Accessed) {
|
||||
PleB->Bits.Accessed = Attribute->Bits.Accessed;
|
||||
}
|
||||
|
||||
if (Mask->Bits.Dirty) {
|
||||
PleB->Bits.Dirty = Attribute->Bits.Dirty;
|
||||
}
|
||||
|
||||
if (Mask->Bits.Pat) {
|
||||
PleB->Bits.Pat = Attribute->Bits.Pat;
|
||||
}
|
||||
|
||||
if (Mask->Bits.Global) {
|
||||
PleB->Bits.Global = Attribute->Bits.Global;
|
||||
}
|
||||
|
||||
if (Mask->Bits.ProtectionKey) {
|
||||
PleB->Bits.ProtectionKey = Attribute->Bits.ProtectionKey;
|
||||
}
|
||||
|
||||
if (Mask->Bits.Nx) {
|
||||
PleB->Bits.Nx = Attribute->Bits.Nx;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Set the IA32_PDPTE_1G, IA32_PDE_2M or IA32_PTE_4K.
|
||||
|
||||
@param[in] Level 3, 2 or 1.
|
||||
@param[in] Ple Pointer to PDPTE_1G, PDE_2M or IA32_PTE_4K, depending on the Level.
|
||||
@param[in] Offset The offset within the linear address range.
|
||||
@param[in] Attribute The attribute of the linear address range.
|
||||
All non-reserved fields in IA32_MAP_ATTRIBUTE are supported to set in the page table.
|
||||
Page table entry is reset to 0 before set to the new attribute when a new physical base address is set.
|
||||
@param[in] Mask The mask used for attribute. The corresponding field in Attribute is ignored if that in Mask is 0.
|
||||
**/
|
||||
VOID
|
||||
PageTableLibSetPle (
|
||||
IN UINTN Level,
|
||||
IN IA32_PAGING_ENTRY *Ple,
|
||||
IN UINT64 Offset,
|
||||
IN IA32_MAP_ATTRIBUTE *Attribute,
|
||||
IN IA32_MAP_ATTRIBUTE *Mask
|
||||
)
|
||||
{
|
||||
if (Level == 1) {
|
||||
PageTableLibSetPte4K (&Ple->Pte4K, Offset, Attribute, Mask);
|
||||
} else {
|
||||
ASSERT (Level == 2 || Level == 3);
|
||||
PageTableLibSetPleB (&Ple->PleB, Offset, Attribute, Mask);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Set the IA32_PML5, IA32_PML4, IA32_PDPTE or IA32_PDE.
|
||||
|
||||
@param[in] Pnle Pointer to IA32_PML5, IA32_PML4, IA32_PDPTE or IA32_PDE. All share the same structure definition.
|
||||
@param[in] Attribute The attribute of the page directory referenced by the non-leaf.
|
||||
**/
|
||||
VOID
|
||||
PageTableLibSetPnle (
|
||||
IN IA32_PAGE_NON_LEAF_ENTRY *Pnle,
|
||||
IN IA32_MAP_ATTRIBUTE *Attribute
|
||||
)
|
||||
{
|
||||
Pnle->Bits.Present = Attribute->Bits.Present;
|
||||
Pnle->Bits.ReadWrite = Attribute->Bits.ReadWrite;
|
||||
Pnle->Bits.UserSupervisor = Attribute->Bits.UserSupervisor;
|
||||
Pnle->Bits.Nx = Attribute->Bits.Nx;
|
||||
Pnle->Bits.Accessed = 0;
|
||||
|
||||
//
|
||||
// Set the attributes (WT, CD, A) to 0.
|
||||
// WT and CD determin the memory type used to access the 4K page directory referenced by this entry.
|
||||
// So, it implictly requires PAT[0] is Write Back.
|
||||
// Create a new parameter if caller requires to use a different memory type for accessing page directories.
|
||||
//
|
||||
Pnle->Bits.WriteThrough = 0;
|
||||
Pnle->Bits.CacheDisabled = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
Update page table to map [LinearAddress, LinearAddress + Length) with specified attribute in the specified level.
|
||||
|
||||
@param[in] ParentPagingEntry The pointer to the page table entry to update.
|
||||
@param[in] Modify FALSE to indicate Buffer is not used and BufferSize is increased by the required buffer size.
|
||||
@param[in] Buffer The free buffer to be used for page table creation/updating.
|
||||
When Modify is TRUE, it's used from the end.
|
||||
When Modify is FALSE, it's ignored.
|
||||
@param[in, out] BufferSize The available buffer size.
|
||||
Return the remaining buffer size.
|
||||
@param[in] Level Page table level. Could be 5, 4, 3, 2, or 1.
|
||||
@param[in] MaxLeafLevel Maximum level that can be a leaf entry. Could be 1, 2 or 3 (if Page 1G is supported).
|
||||
@param[in] LinearAddress The start of the linear address range.
|
||||
@param[in] Length The length of the linear address range.
|
||||
@param[in] Offset The offset within the linear address range.
|
||||
@param[in] Attribute The attribute of the linear address range.
|
||||
All non-reserved fields in IA32_MAP_ATTRIBUTE are supported to set in the page table.
|
||||
Page table entries that map the linear address range are reset to 0 before set to the new attribute
|
||||
when a new physical base address is set.
|
||||
@param[in] Mask The mask used for attribute. The corresponding field in Attribute is ignored if that in Mask is 0.
|
||||
|
||||
@retval RETURN_SUCCESS PageTable is created/updated successfully.
|
||||
**/
|
||||
RETURN_STATUS
|
||||
PageTableLibMapInLevel (
|
||||
IN IA32_PAGING_ENTRY *ParentPagingEntry,
|
||||
IN BOOLEAN Modify,
|
||||
IN VOID *Buffer,
|
||||
IN OUT INTN *BufferSize,
|
||||
IN UINTN Level,
|
||||
IN UINTN MaxLeafLevel,
|
||||
IN UINT64 LinearAddress,
|
||||
IN UINT64 Length,
|
||||
IN UINT64 Offset,
|
||||
IN IA32_MAP_ATTRIBUTE *Attribute,
|
||||
IN IA32_MAP_ATTRIBUTE *Mask
|
||||
)
|
||||
{
|
||||
RETURN_STATUS Status;
|
||||
UINTN BitStart;
|
||||
UINTN Index;
|
||||
IA32_PAGING_ENTRY *PagingEntry;
|
||||
UINT64 RegionLength;
|
||||
UINT64 SubLength;
|
||||
UINT64 SubOffset;
|
||||
UINT64 RegionMask;
|
||||
UINT64 RegionStart;
|
||||
IA32_MAP_ATTRIBUTE AllOneMask;
|
||||
IA32_MAP_ATTRIBUTE PleBAttribute;
|
||||
IA32_MAP_ATTRIBUTE NopAttribute;
|
||||
BOOLEAN CreateNew;
|
||||
IA32_PAGING_ENTRY OneOfPagingEntry;
|
||||
|
||||
ASSERT (Level != 0);
|
||||
ASSERT ((Attribute != NULL) && (Mask != NULL));
|
||||
|
||||
CreateNew = FALSE;
|
||||
AllOneMask.Uint64 = ~0ull;
|
||||
|
||||
NopAttribute.Uint64 = 0;
|
||||
NopAttribute.Bits.Present = 1;
|
||||
NopAttribute.Bits.ReadWrite = 1;
|
||||
NopAttribute.Bits.UserSupervisor = 1;
|
||||
|
||||
//
|
||||
// ParentPagingEntry ONLY is deferenced for checking Present and MustBeOne bits
|
||||
// when Modify is FALSE.
|
||||
//
|
||||
|
||||
if (ParentPagingEntry->Pce.Present == 0) {
|
||||
//
|
||||
// The parent entry is CR3 or PML5E/PML4E/PDPTE/PDE.
|
||||
// It does NOT point to an existing page directory.
|
||||
//
|
||||
ASSERT (Buffer == NULL || *BufferSize >= SIZE_4KB);
|
||||
CreateNew = TRUE;
|
||||
*BufferSize -= SIZE_4KB;
|
||||
|
||||
if (Modify) {
|
||||
ParentPagingEntry->Uintn = (UINTN)Buffer + *BufferSize;
|
||||
ZeroMem ((VOID *)ParentPagingEntry->Uintn, SIZE_4KB);
|
||||
//
|
||||
// Set default attribute bits for PML5E/PML4E/PDPTE/PDE.
|
||||
//
|
||||
PageTableLibSetPnle (&ParentPagingEntry->Pnle, &NopAttribute);
|
||||
} else {
|
||||
//
|
||||
// Just make sure Present and MustBeZero (PageSize) bits are accurate.
|
||||
//
|
||||
OneOfPagingEntry.Pnle.Uint64 = 0;
|
||||
}
|
||||
} else if (IsPle (ParentPagingEntry, Level + 1)) {
|
||||
//
|
||||
// The parent entry is a PDPTE_1G or PDE_2M. Split to 2M or 4K pages.
|
||||
// Note: it's impossible the parent entry is a PTE_4K.
|
||||
//
|
||||
//
|
||||
// Use NOP attributes as the attribute of grand-parents because CPU will consider
|
||||
// the actual attributes of grand-parents when determing the memory type.
|
||||
//
|
||||
PleBAttribute.Uint64 = PageTableLibGetPleBMapAttribute (&ParentPagingEntry->PleB, &NopAttribute);
|
||||
if ((IA32_MAP_ATTRIBUTE_ATTRIBUTES (&PleBAttribute) & IA32_MAP_ATTRIBUTE_ATTRIBUTES (Mask))
|
||||
== IA32_MAP_ATTRIBUTE_ATTRIBUTES (Attribute))
|
||||
{
|
||||
//
|
||||
// This function is called when the memory length is less than the region length of the parent level.
|
||||
// No need to split the page when the attributes equal.
|
||||
//
|
||||
return RETURN_SUCCESS;
|
||||
}
|
||||
|
||||
ASSERT (Buffer == NULL || *BufferSize >= SIZE_4KB);
|
||||
CreateNew = TRUE;
|
||||
*BufferSize -= SIZE_4KB;
|
||||
PageTableLibSetPle (Level, &OneOfPagingEntry, 0, &PleBAttribute, &AllOneMask);
|
||||
if (Modify) {
|
||||
//
|
||||
// Create 512 child-level entries that map to 2M/4K.
|
||||
//
|
||||
ParentPagingEntry->Uintn = (UINTN)Buffer + *BufferSize;
|
||||
ZeroMem ((VOID *)ParentPagingEntry->Uintn, SIZE_4KB);
|
||||
|
||||
//
|
||||
// Set NOP attributes
|
||||
// Note: Should NOT inherit the attributes from the original entry because a zero RW bit
|
||||
// will make the entire region read-only even the child entries set the RW bit.
|
||||
//
|
||||
PageTableLibSetPnle (&ParentPagingEntry->Pnle, &NopAttribute);
|
||||
|
||||
RegionLength = REGION_LENGTH (Level);
|
||||
PagingEntry = (IA32_PAGING_ENTRY *)(UINTN)IA32_PNLE_PAGE_TABLE_BASE_ADDRESS (&ParentPagingEntry->Pnle);
|
||||
for (SubOffset = 0, Index = 0; Index < 512; Index++) {
|
||||
PagingEntry[Index].Uint64 = OneOfPagingEntry.Uint64 + SubOffset;
|
||||
SubOffset += RegionLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// RegionLength: 256T (1 << 48) 512G (1 << 39), 1G (1 << 30), 2M (1 << 21) or 4K (1 << 12).
|
||||
// RegionStart: points to the linear address that's aligned on RegionLength and lower than (LinearAddress + Offset).
|
||||
//
|
||||
BitStart = 12 + (Level - 1) * 9;
|
||||
Index = (UINTN)BitFieldRead64 (LinearAddress + Offset, BitStart, BitStart + 9 - 1);
|
||||
RegionLength = LShiftU64 (1, BitStart);
|
||||
RegionMask = RegionLength - 1;
|
||||
RegionStart = (LinearAddress + Offset) & ~RegionMask;
|
||||
|
||||
//
|
||||
// Apply the attribute.
|
||||
//
|
||||
PagingEntry = (IA32_PAGING_ENTRY *)(UINTN)IA32_PNLE_PAGE_TABLE_BASE_ADDRESS (&ParentPagingEntry->Pnle);
|
||||
while (Offset < Length && Index < 512) {
|
||||
SubLength = MIN (Length - Offset, RegionStart + RegionLength - (LinearAddress + Offset));
|
||||
if ((Level <= MaxLeafLevel) && (LinearAddress + Offset == RegionStart) && (SubLength == RegionLength)) {
|
||||
//
|
||||
// Create one entry mapping the entire region (1G, 2M or 4K).
|
||||
//
|
||||
if (Modify) {
|
||||
PageTableLibSetPle (Level, &PagingEntry[Index], Offset, Attribute, Mask);
|
||||
}
|
||||
} else {
|
||||
//
|
||||
// Recursively call to create page table.
|
||||
// There are 3 cases:
|
||||
// a. Level cannot be a leaf entry which points to physical memory.
|
||||
// a. Level can be a leaf entry but (LinearAddress + Offset) is NOT aligned on the RegionStart.
|
||||
// b. Level can be a leaf entry and (LinearAddress + Offset) is aligned on RegionStart,
|
||||
// but the length is SMALLER than the RegionLength.
|
||||
//
|
||||
Status = PageTableLibMapInLevel (
|
||||
(!Modify && CreateNew) ? &OneOfPagingEntry : &PagingEntry[Index],
|
||||
Modify,
|
||||
Buffer,
|
||||
BufferSize,
|
||||
Level - 1,
|
||||
MaxLeafLevel,
|
||||
LinearAddress,
|
||||
Length,
|
||||
Offset,
|
||||
Attribute,
|
||||
Mask
|
||||
);
|
||||
if (RETURN_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
}
|
||||
|
||||
Offset += SubLength;
|
||||
RegionStart += RegionLength;
|
||||
Index++;
|
||||
}
|
||||
|
||||
return RETURN_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
Create or update page table to map [LinearAddress, LinearAddress + Length) with specified attribute.
|
||||
|
||||
@param[in, out] PageTable The pointer to the page table to update, or pointer to NULL if a new page table is to be created.
|
||||
@param[in] PagingMode The paging mode.
|
||||
@param[in] Buffer The free buffer to be used for page table creation/updating.
|
||||
@param[in, out] BufferSize The buffer size.
|
||||
On return, the remaining buffer size.
|
||||
The free buffer is used from the end so caller can supply the same Buffer pointer with an updated
|
||||
BufferSize in the second call to this API.
|
||||
@param[in] LinearAddress The start of the linear address range.
|
||||
@param[in] Length The length of the linear address range.
|
||||
@param[in] Attribute The attribute of the linear address range.
|
||||
All non-reserved fields in IA32_MAP_ATTRIBUTE are supported to set in the page table.
|
||||
Page table entries that map the linear address range are reset to 0 before set to the new attribute
|
||||
when a new physical base address is set.
|
||||
@param[in] Mask The mask used for attribute. The corresponding field in Attribute is ignored if that in Mask is 0.
|
||||
|
||||
@retval RETURN_UNSUPPORTED PagingMode is not supported.
|
||||
@retval RETURN_INVALID_PARAMETER PageTable, BufferSize, Attribute or Mask is NULL.
|
||||
@retval RETURN_INVALID_PARAMETER *BufferSize is not multiple of 4KB.
|
||||
@retval RETURN_BUFFER_TOO_SMALL The buffer is too small for page table creation/updating.
|
||||
BufferSize is updated to indicate the expected buffer size.
|
||||
Caller may still get RETURN_BUFFER_TOO_SMALL with the new BufferSize.
|
||||
@retval RETURN_SUCCESS PageTable is created/updated successfully.
|
||||
**/
|
||||
RETURN_STATUS
|
||||
EFIAPI
|
||||
PageTableMap (
|
||||
IN OUT UINTN *PageTable OPTIONAL,
|
||||
IN PAGING_MODE PagingMode,
|
||||
IN VOID *Buffer,
|
||||
IN OUT UINTN *BufferSize,
|
||||
IN UINT64 LinearAddress,
|
||||
IN UINT64 Length,
|
||||
IN IA32_MAP_ATTRIBUTE *Attribute,
|
||||
IN IA32_MAP_ATTRIBUTE *Mask
|
||||
)
|
||||
{
|
||||
RETURN_STATUS Status;
|
||||
IA32_PAGING_ENTRY TopPagingEntry;
|
||||
INTN RequiredSize;
|
||||
UINT64 MaxLinearAddress;
|
||||
UINTN MaxLevel;
|
||||
UINTN MaxLeafLevel;
|
||||
|
||||
if ((PagingMode == Paging32bit) || (PagingMode == PagingPae) || (PagingMode >= PagingModeMax)) {
|
||||
//
|
||||
// 32bit paging is never supported.
|
||||
// PAE paging will be supported later.
|
||||
//
|
||||
return RETURN_UNSUPPORTED;
|
||||
}
|
||||
|
||||
if ((PageTable == NULL) || (BufferSize == NULL) || (Attribute == NULL) || (Mask == NULL)) {
|
||||
return RETURN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (*BufferSize % SIZE_4KB != 0) {
|
||||
//
|
||||
// BufferSize should be multiple of 4K.
|
||||
//
|
||||
return RETURN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if ((*BufferSize != 0) && (Buffer == NULL)) {
|
||||
return RETURN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
MaxLeafLevel = (UINT8)PagingMode;
|
||||
MaxLevel = (UINT8)(PagingMode >> 8);
|
||||
MaxLinearAddress = LShiftU64 (1, 12 + MaxLevel * 9);
|
||||
|
||||
if ((LinearAddress > MaxLinearAddress) || (Length > MaxLinearAddress - LinearAddress)) {
|
||||
//
|
||||
// Maximum linear address is (1 << 48) or (1 << 57)
|
||||
//
|
||||
return RETURN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
TopPagingEntry.Uintn = *PageTable;
|
||||
if (TopPagingEntry.Uintn != 0) {
|
||||
TopPagingEntry.Pce.Present = 1;
|
||||
}
|
||||
|
||||
//
|
||||
// Query the required buffer size without modifying the page table.
|
||||
//
|
||||
RequiredSize = 0;
|
||||
Status = PageTableLibMapInLevel (
|
||||
&TopPagingEntry,
|
||||
FALSE,
|
||||
NULL,
|
||||
&RequiredSize,
|
||||
MaxLevel,
|
||||
MaxLeafLevel,
|
||||
LinearAddress,
|
||||
Length,
|
||||
0,
|
||||
Attribute,
|
||||
Mask
|
||||
);
|
||||
if (RETURN_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
|
||||
RequiredSize = -RequiredSize;
|
||||
|
||||
if ((UINTN)RequiredSize > *BufferSize) {
|
||||
*BufferSize = RequiredSize;
|
||||
return RETURN_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
if ((RequiredSize != 0) && (Buffer == NULL)) {
|
||||
return RETURN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
//
|
||||
// Update the page table when the supplied buffer is sufficient.
|
||||
//
|
||||
Status = PageTableLibMapInLevel (
|
||||
&TopPagingEntry,
|
||||
TRUE,
|
||||
Buffer,
|
||||
(INTN *)BufferSize,
|
||||
MaxLevel,
|
||||
MaxLeafLevel,
|
||||
LinearAddress,
|
||||
Length,
|
||||
0,
|
||||
Attribute,
|
||||
Mask
|
||||
);
|
||||
if (!RETURN_ERROR (Status)) {
|
||||
*PageTable = (UINTN)(TopPagingEntry.Uintn & IA32_PE_BASE_ADDRESS_MASK_40);
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -62,6 +62,9 @@
|
|||
## @libraryclass Provides function for loading microcode.
|
||||
MicrocodeLib|Include/Library/MicrocodeLib.h
|
||||
|
||||
## @libraryclass Provides function for manipulating x86 paging structures.
|
||||
CpuPageTableLib|Include/Library/CpuPageTableLib.h
|
||||
|
||||
[Guids]
|
||||
gUefiCpuPkgTokenSpaceGuid = { 0xac05bf33, 0x995a, 0x4ed4, { 0xaa, 0xb8, 0xef, 0x7a, 0xe8, 0xf, 0x5c, 0xb0 }}
|
||||
gMsegSmramGuid = { 0x5802bce4, 0xeeee, 0x4e33, { 0xa1, 0x30, 0xeb, 0xad, 0x27, 0xf0, 0xe4, 0x39 }}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
## @file
|
||||
# UefiCpuPkg Package
|
||||
#
|
||||
# Copyright (c) 2007 - 2021, Intel Corporation. All rights reserved.<BR>
|
||||
# Copyright (c) 2007 - 2022, Intel Corporation. All rights reserved.<BR>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||
#
|
||||
|
@ -62,6 +62,7 @@
|
|||
VmgExitLib|UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf
|
||||
MicrocodeLib|UefiCpuPkg/Library/MicrocodeLib/MicrocodeLib.inf
|
||||
SmmCpuRendezvousLib|UefiCpuPkg/Library/SmmCpuRendezvousLib/SmmCpuRendezvousLib.inf
|
||||
CpuPageTableLib|UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableLib.inf
|
||||
|
||||
[LibraryClasses.common.SEC]
|
||||
PlatformSecLib|UefiCpuPkg/Library/PlatformSecLibNull/PlatformSecLibNull.inf
|
||||
|
@ -175,6 +176,7 @@
|
|||
UefiCpuPkg/Universal/Acpi/S3Resume2Pei/S3Resume2Pei.inf
|
||||
UefiCpuPkg/ResetVector/Vtf0/Bin/ResetVector.inf
|
||||
UefiCpuPkg/Library/SmmCpuRendezvousLib/SmmCpuRendezvousLib.inf
|
||||
UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableLib.inf
|
||||
|
||||
[BuildOptions]
|
||||
*_*_*_CC_FLAGS = -D DISABLE_NEW_DEPRECATED_INTERFACES
|
||||
|
|
Loading…
Reference in New Issue