public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
* [PATCH 00/10] UefiCpuPkg: Create CpuPageTableLib for manipulating X86 paging structs
@ 2022-07-18 13:18 Ni, Ray
  2022-07-18 13:18 ` [PATCH 01/10] " Ni, Ray
                   ` (10 more replies)
  0 siblings, 11 replies; 16+ messages in thread
From: Ni, Ray @ 2022-07-18 13:18 UTC (permalink / raw)
  To: devel

Ray Ni (10):
  UefiCpuPkg: Create CpuPageTableLib for manipulating X86 paging structs
  UefiCpuPkg/CpuPageTableLib: Return error on invalid parameters
  CpuPageTableLib: Fix a bug when a bit is 1 in Attribute, 0 in Mask
  CpuPageTableLib: Refactor the logic
  CpuPageTableLib: Split the page entry when LA is aligned but PA is not
  CpuPageTableLib: Avoid treating non-leaf entry as leaf one
  CpuPageTableLib: Fix parent attributes are not inherited properly
  CpuPageTableLib: Fix a bug to avoid unnecessary changing to page table
  CpuPageTableLib: Fix bug that wrongly requires extra size for mapping
  CpuPageTableLib: define IA32_PAGE_LEVEL enum type internally

 UefiCpuPkg/Include/Library/CpuPageTableLib.h  | 129 ++++
 .../Library/CpuPageTableLib/CpuPageTable.h    | 230 ++++++
 .../CpuPageTableLib/CpuPageTableLib.inf       |  35 +
 .../Library/CpuPageTableLib/CpuPageTableMap.c | 690 ++++++++++++++++++
 .../CpuPageTableLib/CpuPageTableParse.c       | 330 +++++++++
 UefiCpuPkg/UefiCpuPkg.dec                     |   3 +
 UefiCpuPkg/UefiCpuPkg.dsc                     |   4 +-
 7 files changed, 1420 insertions(+), 1 deletion(-)
 create mode 100644 UefiCpuPkg/Include/Library/CpuPageTableLib.h
 create mode 100644 UefiCpuPkg/Library/CpuPageTableLib/CpuPageTable.h
 create mode 100644 UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableLib.inf
 create mode 100644 UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c
 create mode 100644 UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableParse.c

-- 
2.35.1.windows.2


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH 01/10] UefiCpuPkg: Create CpuPageTableLib for manipulating X86 paging structs
  2022-07-18 13:18 [PATCH 00/10] UefiCpuPkg: Create CpuPageTableLib for manipulating X86 paging structs Ni, Ray
@ 2022-07-18 13:18 ` Ni, Ray
  2022-07-18 13:49   ` [edk2-devel] " Gerd Hoffmann
  2022-08-15 16:23   ` Lendacky, Thomas
  2022-07-18 13:18 ` [PATCH 02/10] UefiCpuPkg/CpuPageTableLib: Return error on invalid parameters Ni, Ray
                   ` (9 subsequent siblings)
  10 siblings, 2 replies; 16+ messages in thread
From: Ni, Ray @ 2022-07-18 13:18 UTC (permalink / raw)
  To: devel; +Cc: Eric Dong

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>
Cc: Eric Dong <eric.dong@intel.com>
---
 UefiCpuPkg/Include/Library/CpuPageTableLib.h  | 129 +++++
 .../Library/CpuPageTableLib/CpuPageTable.h    | 204 +++++++
 .../CpuPageTableLib/CpuPageTableLib.inf       |  35 ++
 .../Library/CpuPageTableLib/CpuPageTableMap.c | 543 ++++++++++++++++++
 .../CpuPageTableLib/CpuPageTableParse.c       | 330 +++++++++++
 UefiCpuPkg/UefiCpuPkg.dec                     |   3 +
 UefiCpuPkg/UefiCpuPkg.dsc                     |   4 +-
 7 files changed, 1247 insertions(+), 1 deletion(-)
 create mode 100644 UefiCpuPkg/Include/Library/CpuPageTableLib.h
 create mode 100644 UefiCpuPkg/Library/CpuPageTableLib/CpuPageTable.h
 create mode 100644 UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableLib.inf
 create mode 100644 UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c
 create mode 100644 UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableParse.c

diff --git a/UefiCpuPkg/Include/Library/CpuPageTableLib.h b/UefiCpuPkg/Include/Library/CpuPageTableLib.h
new file mode 100644
index 0000000000..2dc9b7d18e
--- /dev/null
+++ b/UefiCpuPkg/Include/Library/CpuPageTableLib.h
@@ -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
diff --git a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTable.h b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTable.h
new file mode 100644
index 0000000000..c041ea3f56
--- /dev/null
+++ b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTable.h
@@ -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
diff --git a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableLib.inf b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableLib.inf
new file mode 100644
index 0000000000..e4ead7441c
--- /dev/null
+++ b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableLib.inf
@@ -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
diff --git a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c
new file mode 100644
index 0000000000..25e13a6f6f
--- /dev/null
+++ b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c
@@ -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        = 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,
+             BufferSize,
+             MaxLevel,
+             MaxLeafLevel,
+             LinearAddress,
+             Length,
+             0,
+             Attribute,
+             Mask
+             );
+  if (!RETURN_ERROR (Status)) {
+    *PageTable = (UINTN)(TopPagingEntry.Uintn & IA32_PE_BASE_ADDRESS_MASK_40);
+  }
+
+  return Status;
+}
diff --git a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableParse.c b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableParse.c
new file mode 100644
index 0000000000..e66961e122
--- /dev/null
+++ b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableParse.c
@@ -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;
+}
diff --git a/UefiCpuPkg/UefiCpuPkg.dec b/UefiCpuPkg/UefiCpuPkg.dec
index 1951eb294c..4fe79cecbf 100644
--- a/UefiCpuPkg/UefiCpuPkg.dec
+++ b/UefiCpuPkg/UefiCpuPkg.dec
@@ -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 }}
diff --git a/UefiCpuPkg/UefiCpuPkg.dsc b/UefiCpuPkg/UefiCpuPkg.dsc
index a0bbde9985..f694b3a77c 100644
--- a/UefiCpuPkg/UefiCpuPkg.dsc
+++ b/UefiCpuPkg/UefiCpuPkg.dsc
@@ -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
-- 
2.35.1.windows.2


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 02/10] UefiCpuPkg/CpuPageTableLib: Return error on invalid parameters
  2022-07-18 13:18 [PATCH 00/10] UefiCpuPkg: Create CpuPageTableLib for manipulating X86 paging structs Ni, Ray
  2022-07-18 13:18 ` [PATCH 01/10] " Ni, Ray
@ 2022-07-18 13:18 ` Ni, Ray
  2022-07-18 13:18 ` [PATCH 03/10] CpuPageTableLib: Fix a bug when a bit is 1 in Attribute, 0 in Mask Ni, Ray
                   ` (8 subsequent siblings)
  10 siblings, 0 replies; 16+ messages in thread
From: Ni, Ray @ 2022-07-18 13:18 UTC (permalink / raw)
  To: devel; +Cc: Zhiguang Liu, Eric Dong

When LinearAddress or Length is not aligned on 4KB, PageTableMap()
should return Invalid Parameter.

Signed-off-by: Zhiguang Liu <zhiguang.liu@intel.com>
Reviewed-by: Ray Ni <ray.ni@intel.com>
Cc: Eric Dong <eric.dong@intel.com>
---
 UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c
index 25e13a6f6f..17bca5e351 100644
--- a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c
+++ b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c
@@ -467,6 +467,13 @@ PageTableMap (
     return RETURN_INVALID_PARAMETER;
   }
 
+  if ((LinearAddress % SIZE_4KB != 0) || (Length % SIZE_4KB != 0)) {
+    //
+    // LinearAddress and Length should be multiple of 4K.
+    //
+    return RETURN_INVALID_PARAMETER;
+  }
+
   if ((*BufferSize != 0) && (Buffer == NULL)) {
     return RETURN_INVALID_PARAMETER;
   }
-- 
2.35.1.windows.2


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 03/10] CpuPageTableLib: Fix a bug when a bit is 1 in Attribute, 0 in Mask
  2022-07-18 13:18 [PATCH 00/10] UefiCpuPkg: Create CpuPageTableLib for manipulating X86 paging structs Ni, Ray
  2022-07-18 13:18 ` [PATCH 01/10] " Ni, Ray
  2022-07-18 13:18 ` [PATCH 02/10] UefiCpuPkg/CpuPageTableLib: Return error on invalid parameters Ni, Ray
@ 2022-07-18 13:18 ` Ni, Ray
  2022-07-18 13:18 ` [PATCH 04/10] CpuPageTableLib: Refactor the logic Ni, Ray
                   ` (7 subsequent siblings)
  10 siblings, 0 replies; 16+ messages in thread
From: Ni, Ray @ 2022-07-18 13:18 UTC (permalink / raw)
  To: devel; +Cc: Zhiguang Liu, Eric Dong

To reproduce the issue:
  UINTN               PageTable;
  VOID                *Buffer;
  UINTN               PageTableBufferSize;
  IA32_MAP_ATTRIBUTE  Attribute;
  IA32_MAP_ATTRIBUTE  Mask;
  RETURN_STATUS       Status;

  Attribute.Uint64       = 0;
  Mask.Uint64            = 0;
  PageTableBufferSize    = 0;
  PageTable              = 0;
  Buffer                 = NULL;
  Attribute.Bits.Present = 1;
  Attribute.Bits.Nx      = 1;
  Mask.Bits.Present      = 1;
  Mask.Uint64            = MAX_UINT64;

  //
  // Create page table to cover [0, 10M)
  //
  Status = PageTableMap (
             &PageTable, PagingMode, Buffer, &PageTableBufferSize,
             0, (UINT64)SIZE_2MB * 5, &Attribute, &Mask
             );
  ASSERT (Status == RETURN_BUFFER_TOO_SMALL);
  Buffer = AllocatePages (EFI_SIZE_TO_PAGES (PageTableBufferSize));
  Status = PageTableMap (
             &PageTable, PagingMode, Buffer, &PageTableBufferSize,
             0, (UINT64)SIZE_2MB * 5, &Attribute, &Mask
             );
  ASSERT (Status == RETURN_SUCCESS);

  //
  // Change the mapping for [0, 4KB)
  // No change actually. Just clear Nx bit in Mask.
  //
  Mask.Bits.Nx        = 0;
  PageTableBufferSize = 0;

  Status = PageTableMap (
             &PageTable, PagingMode, NULL, &PageTableBufferSize,
             0, (UINT64)SIZE_4KB, &Attribute, &Mask
             );
  ASSERT (Status == RETURN_SUCCESS); // FAIL!!

The root cause is when comparing the existing mapping attributes
against the requested one, Mask is not used but it should be used.

Signed-off-by: Zhiguang Liu <zhiguang.liu@intel.com>
Reviewed-by: Ray Ni <ray.ni@intel.com>
Cc: Eric Dong <eric.dong@intel.com>
---
 UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c
index 17bca5e351..429b014b7b 100644
--- a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c
+++ b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c
@@ -308,7 +308,7 @@ PageTableLibMapInLevel (
     //
     PleBAttribute.Uint64 = PageTableLibGetPleBMapAttribute (&ParentPagingEntry->PleB, &NopAttribute);
     if ((IA32_MAP_ATTRIBUTE_ATTRIBUTES (&PleBAttribute) & IA32_MAP_ATTRIBUTE_ATTRIBUTES (Mask))
-        == IA32_MAP_ATTRIBUTE_ATTRIBUTES (Attribute))
+        == (IA32_MAP_ATTRIBUTE_ATTRIBUTES (Attribute) & IA32_MAP_ATTRIBUTE_ATTRIBUTES (Mask)))
     {
       //
       // This function is called when the memory length is less than the region length of the parent level.
-- 
2.35.1.windows.2


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 04/10] CpuPageTableLib: Refactor the logic
  2022-07-18 13:18 [PATCH 00/10] UefiCpuPkg: Create CpuPageTableLib for manipulating X86 paging structs Ni, Ray
                   ` (2 preceding siblings ...)
  2022-07-18 13:18 ` [PATCH 03/10] CpuPageTableLib: Fix a bug when a bit is 1 in Attribute, 0 in Mask Ni, Ray
@ 2022-07-18 13:18 ` Ni, Ray
  2022-07-18 13:18 ` [PATCH 05/10] CpuPageTableLib: Split the page entry when LA is aligned but PA is not Ni, Ray
                   ` (6 subsequent siblings)
  10 siblings, 0 replies; 16+ messages in thread
From: Ni, Ray @ 2022-07-18 13:18 UTC (permalink / raw)
  To: devel; +Cc: Zhiguang Liu, Eric Dong

The patch replaces
  LinearAddress + Offset == RegionStart
with
  ((LinearAddress + Offset) & RegionMask) == 0

The replace should not cause any behavior change.

Because:
1. In first loop of while when LinearAddress + Offset == RegionStart,
  because the lower "BitStart" bits of RegionStart are all-zero,
  all lower "BitStart" bits of (LinearAddress + Offset) are all-zero.
  Because all lower "BitStart" bits of RegionMask is all-one and
  bits are all-zero, ((LinearAddress + Offset) & RegionMask) == 0.

2. In following loops of the while, even RegionStart is increased
  by RegionLength, the lower "BitStart" bits are still all-zero.
  So the two expressions still semantically equal to each other.

Signed-off-by: Ray Ni <ray.ni@intel.com>
Cc: Zhiguang Liu <zhiguang.liu@intel.com>
Cc: Eric Dong <eric.dong@intel.com>
---
 UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c
index 429b014b7b..509fa5f7bd 100644
--- a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c
+++ b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c
@@ -360,7 +360,7 @@ PageTableLibMapInLevel (
   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)) {
+    if ((Level <= MaxLeafLevel) && (((LinearAddress + Offset) & RegionMask) == 0) && (SubLength == RegionLength)) {
       //
       // Create one entry mapping the entire region (1G, 2M or 4K).
       //
-- 
2.35.1.windows.2


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 05/10] CpuPageTableLib: Split the page entry when LA is aligned but PA is not
  2022-07-18 13:18 [PATCH 00/10] UefiCpuPkg: Create CpuPageTableLib for manipulating X86 paging structs Ni, Ray
                   ` (3 preceding siblings ...)
  2022-07-18 13:18 ` [PATCH 04/10] CpuPageTableLib: Refactor the logic Ni, Ray
@ 2022-07-18 13:18 ` Ni, Ray
  2022-07-18 13:18 ` [PATCH 06/10] CpuPageTableLib: Avoid treating non-leaf entry as leaf one Ni, Ray
                   ` (5 subsequent siblings)
  10 siblings, 0 replies; 16+ messages in thread
From: Ni, Ray @ 2022-07-18 13:18 UTC (permalink / raw)
  To: devel; +Cc: Zhiguang Liu, Eric Dong

When PageTableMap() is called to create non 1:1 mapping
such as [0, 1G) to [8K, 1G+8K), it should split the page entry to the
4K page level, but old logic has a bug that it just uses 1G page
entry.

The patch fixes the bug.

Signed-off-by: Zhiguang Liu <zhiguang.liu@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Eric Dong <eric.dong@intel.com>
---
 UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c
index 509fa5f7bd..dc37ca3647 100644
--- a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c
+++ b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c
@@ -360,7 +360,12 @@ PageTableLibMapInLevel (
   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) & RegionMask) == 0) && (SubLength == RegionLength)) {
+    if ((Level <= MaxLeafLevel) &&
+        (((LinearAddress + Offset) & RegionMask) == 0) &&
+        (((IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS (Attribute) + Offset) & RegionMask) == 0) &&
+        (SubLength == RegionLength)
+        )
+    {
       //
       // Create one entry mapping the entire region (1G, 2M or 4K).
       //
-- 
2.35.1.windows.2


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 06/10] CpuPageTableLib: Avoid treating non-leaf entry as leaf one
  2022-07-18 13:18 [PATCH 00/10] UefiCpuPkg: Create CpuPageTableLib for manipulating X86 paging structs Ni, Ray
                   ` (4 preceding siblings ...)
  2022-07-18 13:18 ` [PATCH 05/10] CpuPageTableLib: Split the page entry when LA is aligned but PA is not Ni, Ray
@ 2022-07-18 13:18 ` Ni, Ray
  2022-07-18 13:18 ` [PATCH 07/10] CpuPageTableLib: Fix parent attributes are not inherited properly Ni, Ray
                   ` (4 subsequent siblings)
  10 siblings, 0 replies; 16+ messages in thread
From: Ni, Ray @ 2022-07-18 13:18 UTC (permalink / raw)
  To: devel; +Cc: Zhiguang Liu, Eric Dong

Today's logic wrongly treats the non-leaf entry as leaf entry and
updates its paging attributes.

The patch fixes the bug to only update paging attributes for
non-present entries or leaf entries.

Signed-off-by: Ray Ni <ray.ni@intel.com>
Signed-off-by: Zhiguang Liu <zhiguang.liu@intel.com>
Cc: Eric Dong <eric.dong@intel.com>
---
 UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c
index dc37ca3647..a6aa1a352b 100644
--- a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c
+++ b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c
@@ -248,6 +248,7 @@ PageTableLibMapInLevel (
   UINTN               BitStart;
   UINTN               Index;
   IA32_PAGING_ENTRY   *PagingEntry;
+  IA32_PAGING_ENTRY   *CurrentPagingEntry;
   UINT64              RegionLength;
   UINT64              SubLength;
   UINT64              SubOffset;
@@ -359,18 +360,20 @@ PageTableLibMapInLevel (
   //
   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));
+    CurrentPagingEntry = (!Modify && CreateNew) ? &OneOfPagingEntry : &PagingEntry[Index];
+    SubLength          = MIN (Length - Offset, RegionStart + RegionLength - (LinearAddress + Offset));
     if ((Level <= MaxLeafLevel) &&
         (((LinearAddress + Offset) & RegionMask) == 0) &&
         (((IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS (Attribute) + Offset) & RegionMask) == 0) &&
-        (SubLength == RegionLength)
+        (SubLength == RegionLength) &&
+        ((CurrentPagingEntry->Pce.Present == 0) || IsPle (CurrentPagingEntry, Level))
         )
     {
       //
       // Create one entry mapping the entire region (1G, 2M or 4K).
       //
       if (Modify) {
-        PageTableLibSetPle (Level, &PagingEntry[Index], Offset, Attribute, Mask);
+        PageTableLibSetPle (Level, CurrentPagingEntry, Offset, Attribute, Mask);
       }
     } else {
       //
@@ -382,7 +385,7 @@ PageTableLibMapInLevel (
       //      but the length is SMALLER than the RegionLength.
       //
       Status = PageTableLibMapInLevel (
-                 (!Modify && CreateNew) ? &OneOfPagingEntry : &PagingEntry[Index],
+                 CurrentPagingEntry,
                  Modify,
                  Buffer,
                  BufferSize,
-- 
2.35.1.windows.2


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 07/10] CpuPageTableLib: Fix parent attributes are not inherited properly
  2022-07-18 13:18 [PATCH 00/10] UefiCpuPkg: Create CpuPageTableLib for manipulating X86 paging structs Ni, Ray
                   ` (5 preceding siblings ...)
  2022-07-18 13:18 ` [PATCH 06/10] CpuPageTableLib: Avoid treating non-leaf entry as leaf one Ni, Ray
@ 2022-07-18 13:18 ` Ni, Ray
  2022-07-18 13:18 ` [PATCH 08/10] CpuPageTableLib: Fix a bug to avoid unnecessary changing to page table Ni, Ray
                   ` (3 subsequent siblings)
  10 siblings, 0 replies; 16+ messages in thread
From: Ni, Ray @ 2022-07-18 13:18 UTC (permalink / raw)
  To: devel; +Cc: Zhiguang Liu, Eric Dong

With the following paging structure that maps [0, 2G] with ReadWrite
bit set.
PML4[0] --> PDPTE[0] --> PDE[0-255]
              \-> PDPTE[1] --> PDE[0-255]

If ReadWrite bit is cleared in PML4[0] and PageTableMap() is called
to change [0, 2M] as writable, today's logic doesn't inherit the
parent entry's attributes when determining the child entry's
attributes. It just sets the PDPTE[0].PDE[0].ReadWrite bit.
But since the PML4[0].ReadWrite is 0, [0, 2M] is still read-only.

The change fixes the bug.
If the inheritable attributes in ParentPagingEntry conflicts with the
requested attributes, let the child entries take the parent attributes
and loosen the attribute in the parent entry.

E.g.: when PDPTE[0].ReadWrite = 0 but caller wants to map [0-2MB as
ReadWrite = 1 (PDE[0].ReadWrite = 1), we need to change
PDPTE[0].ReadWrite = 1 and let all PDE[0-255].ReadWrite = 0 first.
Then change PDE[0].ReadWrite = 1.

Signed-off-by: Zhiguang Liu <zhiguang.liu@intel.com>
Signed-off-by: Ray Ni <ray.ni@intel.com>
Cc: Eric Dong <eric.dong@intel.com>
---
 .../Library/CpuPageTableLib/CpuPageTable.h    |  20 ++-
 .../Library/CpuPageTableLib/CpuPageTableMap.c | 144 ++++++++++++++++--
 2 files changed, 147 insertions(+), 17 deletions(-)

diff --git a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTable.h b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTable.h
index c041ea3f56..627f84e4e2 100644
--- a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTable.h
+++ b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTable.h
@@ -21,7 +21,11 @@
 #define REGION_LENGTH(l)  LShiftU64 (1, (l) * 9 + 3)
 
 typedef struct {
-  UINT64    Present : 1;              // 0 = Not present in memory, 1 = Present in memory
+  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    Reserved       : 58;
+  UINT64    Nx             : 1;        // No Execute bit
 } IA32_PAGE_COMMON_ENTRY;
 
 ///
@@ -201,4 +205,18 @@ PageTableLibGetPleBMapAttribute (
   IN IA32_MAP_ATTRIBUTE                 *ParentMapAttribute
   );
 
+/**
+  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
+  );
+
 #endif
diff --git a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c
index a6aa1a352b..ac5d1c79f4 100644
--- a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c
+++ b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c
@@ -183,18 +183,32 @@ PageTableLibSetPle (
 
   @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.
+  @param[in] Mask      The mask of the page directory referenced by the non-leaf.
 **/
 VOID
 PageTableLibSetPnle (
   IN IA32_PAGE_NON_LEAF_ENTRY  *Pnle,
-  IN IA32_MAP_ATTRIBUTE        *Attribute
+  IN IA32_MAP_ATTRIBUTE        *Attribute,
+  IN IA32_MAP_ATTRIBUTE        *Mask
   )
 {
-  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;
+  if (Mask->Bits.Present) {
+    Pnle->Bits.Present = Attribute->Bits.Present;
+  }
+
+  if (Mask->Bits.ReadWrite) {
+    Pnle->Bits.ReadWrite = Attribute->Bits.ReadWrite;
+  }
+
+  if (Mask->Bits.UserSupervisor) {
+    Pnle->Bits.UserSupervisor = Attribute->Bits.UserSupervisor;
+  }
+
+  if (Mask->Bits.Nx) {
+    Pnle->Bits.Nx = Attribute->Bits.Nx;
+  }
+
+  Pnle->Bits.Accessed = 0;
 
   //
   // Set the attributes (WT, CD, A) to 0.
@@ -210,6 +224,7 @@ PageTableLibSetPnle (
   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]      ParentAttribute   The accumulated attribute of all parents' attribute.
   @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.
@@ -232,6 +247,7 @@ PageTableLibSetPnle (
 RETURN_STATUS
 PageTableLibMapInLevel (
   IN     IA32_PAGING_ENTRY   *ParentPagingEntry,
+  IN     IA32_MAP_ATTRIBUTE  *ParentAttribute,
   IN     BOOLEAN             Modify,
   IN     VOID                *Buffer,
   IN OUT INTN                *BufferSize,
@@ -259,6 +275,8 @@ PageTableLibMapInLevel (
   IA32_MAP_ATTRIBUTE  NopAttribute;
   BOOLEAN             CreateNew;
   IA32_PAGING_ENTRY   OneOfPagingEntry;
+  IA32_MAP_ATTRIBUTE  ChildAttribute;
+  IA32_MAP_ATTRIBUTE  ChildMask;
 
   ASSERT (Level != 0);
   ASSERT ((Attribute != NULL) && (Mask != NULL));
@@ -291,7 +309,7 @@ PageTableLibMapInLevel (
       //
       // Set default attribute bits for PML5E/PML4E/PDPTE/PDE.
       //
-      PageTableLibSetPnle (&ParentPagingEntry->Pnle, &NopAttribute);
+      PageTableLibSetPnle (&ParentPagingEntry->Pnle, &NopAttribute, &AllOneMask);
     } else {
       //
       // Just make sure Present and MustBeZero (PageSize) bits are accurate.
@@ -307,7 +325,7 @@ PageTableLibMapInLevel (
     // 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);
+    PleBAttribute.Uint64 = PageTableLibGetPleBMapAttribute (&ParentPagingEntry->PleB, ParentAttribute);
     if ((IA32_MAP_ATTRIBUTE_ATTRIBUTES (&PleBAttribute) & IA32_MAP_ATTRIBUTE_ATTRIBUTES (Mask))
         == (IA32_MAP_ATTRIBUTE_ATTRIBUTES (Attribute) & IA32_MAP_ATTRIBUTE_ATTRIBUTES (Mask)))
     {
@@ -334,7 +352,7 @@ PageTableLibMapInLevel (
       // 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);
+      PageTableLibSetPnle (&ParentPagingEntry->Pnle, &NopAttribute, &AllOneMask);
 
       RegionLength = REGION_LENGTH (Level);
       PagingEntry  = (IA32_PAGING_ENTRY *)(UINTN)IA32_PNLE_PAGE_TABLE_BASE_ADDRESS (&ParentPagingEntry->Pnle);
@@ -343,6 +361,77 @@ PageTableLibMapInLevel (
         SubOffset                += RegionLength;
       }
     }
+  } else {
+    //
+    // It's a non-leaf entry
+    //
+    ChildAttribute.Uint64 = 0;
+    ChildMask.Uint64      = 0;
+
+    //
+    // If the inheritable attributes in the parent entry conflicts with the requested attributes,
+    //   let the child entries take the parent attributes and
+    //   loosen the attribute in the parent entry
+    // E.g.: when PDPTE[0].ReadWrite = 0 but caller wants to map [0-2MB] as ReadWrite = 1 (PDE[0].ReadWrite = 1)
+    //            we need to change PDPTE[0].ReadWrite = 1 and let all PDE[0-255].ReadWrite = 0 in this step.
+    //       when PDPTE[0].Nx = 1 but caller wants to map [0-2MB] as Nx = 0 (PDT[0].Nx = 0)
+    //            we need to change PDPTE[0].Nx = 0 and let all PDE[0-255].Nx = 1 in this step.
+    if ((ParentPagingEntry->Pnle.Bits.Present == 0) && (Mask->Bits.Present == 1) && (Attribute->Bits.Present == 1)) {
+      if (Modify) {
+        ParentPagingEntry->Pnle.Bits.Present = 1;
+      }
+
+      ChildAttribute.Bits.Present = 0;
+      ChildMask.Bits.Present      = 1;
+    }
+
+    if ((ParentPagingEntry->Pnle.Bits.ReadWrite == 0) && (Mask->Bits.ReadWrite == 1) && (Attribute->Bits.ReadWrite == 1)) {
+      if (Modify) {
+        ParentPagingEntry->Pnle.Bits.ReadWrite = 1;
+      }
+
+      ChildAttribute.Bits.ReadWrite = 0;
+      ChildMask.Bits.ReadWrite      = 1;
+    }
+
+    if ((ParentPagingEntry->Pnle.Bits.UserSupervisor == 0) && (Mask->Bits.UserSupervisor == 1) && (Attribute->Bits.UserSupervisor == 1)) {
+      if (Modify) {
+        ParentPagingEntry->Pnle.Bits.UserSupervisor = 1;
+      }
+
+      ChildAttribute.Bits.UserSupervisor = 0;
+      ChildMask.Bits.UserSupervisor      = 1;
+    }
+
+    if ((ParentPagingEntry->Pnle.Bits.Nx == 1) && (Mask->Bits.Nx == 1) && (Attribute->Bits.Nx == 0)) {
+      if (Modify) {
+        ParentPagingEntry->Pnle.Bits.Nx = 0;
+      }
+
+      ChildAttribute.Bits.Nx = 1;
+      ChildMask.Bits.Nx      = 1;
+    }
+
+    if (ChildMask.Uint64 != 0) {
+      if (Modify) {
+        //
+        // Update child entries to use restrictive attribute inherited from parent.
+        // e.g.: Set PDE[0-255].ReadWrite = 0
+        //
+        PagingEntry = (IA32_PAGING_ENTRY *)(UINTN)IA32_PNLE_PAGE_TABLE_BASE_ADDRESS (&ParentPagingEntry->Pnle);
+        for (Index = 0; Index < 512; Index++) {
+          if (PagingEntry[Index].Pce.Present == 0) {
+            continue;
+          }
+
+          if (IsPle (&PagingEntry[Index], Level)) {
+            PageTableLibSetPle (Level - 1, &PagingEntry[Index], 0, &ChildAttribute, &ChildMask);
+          } else {
+            PageTableLibSetPnle (&PagingEntry[Index].Pnle, &ChildAttribute, &ChildMask);
+          }
+        }
+      }
+    }
   }
 
   //
@@ -355,6 +444,8 @@ PageTableLibMapInLevel (
   RegionMask   = RegionLength - 1;
   RegionStart  = (LinearAddress + Offset) & ~RegionMask;
 
+  ParentAttribute->Uint64 = PageTableLibGetPnleMapAttribute (&ParentPagingEntry->Pnle, ParentAttribute);
+
   //
   // Apply the attribute.
   //
@@ -386,6 +477,7 @@ PageTableLibMapInLevel (
       //
       Status = PageTableLibMapInLevel (
                  CurrentPagingEntry,
+                 ParentAttribute,
                  Modify,
                  Buffer,
                  BufferSize,
@@ -449,12 +541,13 @@ PageTableMap (
   IN     IA32_MAP_ATTRIBUTE  *Mask
   )
 {
-  RETURN_STATUS      Status;
-  IA32_PAGING_ENTRY  TopPagingEntry;
-  INTN               RequiredSize;
-  UINT64             MaxLinearAddress;
-  UINTN              MaxLevel;
-  UINTN              MaxLeafLevel;
+  RETURN_STATUS       Status;
+  IA32_PAGING_ENTRY   TopPagingEntry;
+  INTN                RequiredSize;
+  UINT64              MaxLinearAddress;
+  UINTN               MaxLevel;
+  UINTN               MaxLeafLevel;
+  IA32_MAP_ATTRIBUTE  ParentAttribute;
 
   if ((PagingMode == Paging32bit) || (PagingMode == PagingPae) || (PagingMode >= PagingModeMax)) {
     //
@@ -499,15 +592,26 @@ PageTableMap (
 
   TopPagingEntry.Uintn = *PageTable;
   if (TopPagingEntry.Uintn != 0) {
-    TopPagingEntry.Pce.Present = 1;
+    TopPagingEntry.Pce.Present        = 1;
+    TopPagingEntry.Pce.ReadWrite      = 1;
+    TopPagingEntry.Pce.UserSupervisor = 1;
+    TopPagingEntry.Pce.Nx             = 0;
   }
 
+  ParentAttribute.Uint64                    = 0;
+  ParentAttribute.Bits.PageTableBaseAddress = 1;
+  ParentAttribute.Bits.Present              = 1;
+  ParentAttribute.Bits.ReadWrite            = 1;
+  ParentAttribute.Bits.UserSupervisor       = 1;
+  ParentAttribute.Bits.Nx                   = 0;
+
   //
   // Query the required buffer size without modifying the page table.
   //
   RequiredSize = 0;
   Status       = PageTableLibMapInLevel (
                    &TopPagingEntry,
+                   &ParentAttribute,
                    FALSE,
                    NULL,
                    &RequiredSize,
@@ -537,8 +641,16 @@ PageTableMap (
   //
   // Update the page table when the supplied buffer is sufficient.
   //
+  ParentAttribute.Uint64                    = 0;
+  ParentAttribute.Bits.PageTableBaseAddress = 1;
+  ParentAttribute.Bits.Present              = 1;
+  ParentAttribute.Bits.ReadWrite            = 1;
+  ParentAttribute.Bits.UserSupervisor       = 1;
+  ParentAttribute.Bits.Nx                   = 0;
+
   Status = PageTableLibMapInLevel (
              &TopPagingEntry,
+             &ParentAttribute,
              TRUE,
              Buffer,
              BufferSize,
-- 
2.35.1.windows.2


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 08/10] CpuPageTableLib: Fix a bug to avoid unnecessary changing to page table
  2022-07-18 13:18 [PATCH 00/10] UefiCpuPkg: Create CpuPageTableLib for manipulating X86 paging structs Ni, Ray
                   ` (6 preceding siblings ...)
  2022-07-18 13:18 ` [PATCH 07/10] CpuPageTableLib: Fix parent attributes are not inherited properly Ni, Ray
@ 2022-07-18 13:18 ` Ni, Ray
  2022-07-18 13:18 ` [PATCH 09/10] CpuPageTableLib: Fix bug that wrongly requires extra size for mapping Ni, Ray
                   ` (2 subsequent siblings)
  10 siblings, 0 replies; 16+ messages in thread
From: Ni, Ray @ 2022-07-18 13:18 UTC (permalink / raw)
  To: devel; +Cc: Zhiguang Liu, Eric Dong

With the following paging structure that maps [0, 2G] with ReadWrite
bit set.
PML4[0] --> PDPTE[0] --> PDE[0-255]
              \-> PDPTE[1] --> PDE[0-255]

If ReadWrite bit is cleared in PML4[0] and PageTableMap() is called
to change [0, 2M] as read-only, today's logic unnecessarily changes
the paging structure in 2 aspects:
1. When setting PageTableBaseAddress in the entry, the code clears
    all attributes.
2. Even the ReadWrite bit in parent entry is not set, the code clears
    the ReadWrite bit in the leaf entry.

First change is wrong. It should not change other attributes when
setting the PA.
Second change is unnecessary. Because the parent entry already
declares the whole region as read-only, there is no need to clear
ReadWrite bit in the leaf entry again.

Signed-off-by: Zhiguang Liu <zhiguang.liu@intel.com>
Signed-off-by: Ray Ni <ray.ni@intel.com>
Cc: Eric Dong <eric.dong@intel.com>
---
 .../Library/CpuPageTableLib/CpuPageTableMap.c | 41 +++++++++++++++----
 1 file changed, 32 insertions(+), 9 deletions(-)

diff --git a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c
index ac5d1c79f4..1205119fc8 100644
--- a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c
+++ b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c
@@ -27,10 +27,7 @@ PageTableLibSetPte4K (
   )
 {
   if (Mask->Bits.PageTableBaseAddress) {
-    //
-    // Reset all attributes when the physical address is changed.
-    //
-    Pte4K->Uint64 = IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS (Attribute) + Offset;
+    Pte4K->Uint64 = (IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS (Attribute) + Offset) | (Pte4K->Uint64 & ~IA32_PE_BASE_ADDRESS_MASK_40);
   }
 
   if (Mask->Bits.Present) {
@@ -97,10 +94,7 @@ PageTableLibSetPleB (
   )
 {
   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->Uint64 = (IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS (Attribute) + Offset) | (PleB->Uint64 & ~IA32_PE_BASE_ADDRESS_MASK_39);
   }
 
   PleB->Bits.MustBeOne = 1;
@@ -277,6 +271,7 @@ PageTableLibMapInLevel (
   IA32_PAGING_ENTRY   OneOfPagingEntry;
   IA32_MAP_ATTRIBUTE  ChildAttribute;
   IA32_MAP_ATTRIBUTE  ChildMask;
+  IA32_MAP_ATTRIBUTE  CurrentMask;
 
   ASSERT (Level != 0);
   ASSERT ((Attribute != NULL) && (Mask != NULL));
@@ -464,7 +459,35 @@ PageTableLibMapInLevel (
       // Create one entry mapping the entire region (1G, 2M or 4K).
       //
       if (Modify) {
-        PageTableLibSetPle (Level, CurrentPagingEntry, Offset, Attribute, Mask);
+        //
+        // When the inheritable attributes in parent entry could override the child attributes,
+        // e.g.: Present/ReadWrite/UserSupervisor is 0 in parent entry, or
+        //       Nx is 1 in parent entry,
+        // we just skip setting any value to these attributes in child.
+        // We add assertion to make sure the requested settings don't conflict with parent attributes in this case.
+        //
+        CurrentMask.Uint64 = Mask->Uint64;
+        if (ParentAttribute->Bits.Present == 0) {
+          CurrentMask.Bits.Present = 0;
+          ASSERT (CreateNew || (Mask->Bits.Present == 0) || (Attribute->Bits.Present == 0));
+        }
+
+        if (ParentAttribute->Bits.ReadWrite == 0) {
+          CurrentMask.Bits.ReadWrite = 0;
+          ASSERT (CreateNew || (Mask->Bits.ReadWrite == 0) || (Attribute->Bits.ReadWrite == 0));
+        }
+
+        if (ParentAttribute->Bits.UserSupervisor == 0) {
+          CurrentMask.Bits.UserSupervisor = 0;
+          ASSERT (CreateNew || (Mask->Bits.UserSupervisor == 0) || (Attribute->Bits.UserSupervisor == 0));
+        }
+
+        if (ParentAttribute->Bits.Nx == 1) {
+          CurrentMask.Bits.Nx = 0;
+          ASSERT (CreateNew || (Mask->Bits.Nx == 0) || (Attribute->Bits.Nx == 1));
+        }
+
+        PageTableLibSetPle (Level, CurrentPagingEntry, Offset, Attribute, &CurrentMask);
       }
     } else {
       //
-- 
2.35.1.windows.2


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 09/10] CpuPageTableLib: Fix bug that wrongly requires extra size for mapping
  2022-07-18 13:18 [PATCH 00/10] UefiCpuPkg: Create CpuPageTableLib for manipulating X86 paging structs Ni, Ray
                   ` (7 preceding siblings ...)
  2022-07-18 13:18 ` [PATCH 08/10] CpuPageTableLib: Fix a bug to avoid unnecessary changing to page table Ni, Ray
@ 2022-07-18 13:18 ` Ni, Ray
  2022-07-18 13:18 ` [PATCH 10/10] CpuPageTableLib: define IA32_PAGE_LEVEL enum type internally Ni, Ray
  2022-08-09  3:46 ` [edk2-devel] [PATCH 00/10] UefiCpuPkg: Create CpuPageTableLib for manipulating X86 paging structs Dong, Eric
  10 siblings, 0 replies; 16+ messages in thread
From: Ni, Ray @ 2022-07-18 13:18 UTC (permalink / raw)
  To: devel; +Cc: Zhiguang Liu, Eric Dong

With following paging structure to map
  [2M-4K, 2M] as P = 1, RW = 0,
  [2M, 4M]    as P = 1, RW = 1:

PML4[0] -> PDPTE[0] -> PDE[0](RW = 0) -> PTE[255](P = 0, RW = 0)
                    -> PDE[1](RW = 1)

When a new request to map [2M-4K, 2M+4K] as P = 1, RW = 1,
CpuPageTableMap() wrongly requests 4K buffer size for the new mapping
request.

But in fact, for [2M-4K, 2M] request, PTE[255] can be changed in place,
for [2M, 2M+4K], no change is needed because PDE[1].RW = 1 already.

The change fixes the bug.

Signed-off-by: Ray Ni <ray.ni@intel.com>
Signed-off-by: Zhiguang Liu <zhiguang.liu@intel.com>
Cc: Eric Dong <eric.dong@intel.com>
---
 .../Library/CpuPageTableLib/CpuPageTableMap.c       | 13 +++++--------
 1 file changed, 5 insertions(+), 8 deletions(-)

diff --git a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c
index 1205119fc8..16e6697ed4 100644
--- a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c
+++ b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c
@@ -272,6 +272,7 @@ PageTableLibMapInLevel (
   IA32_MAP_ATTRIBUTE  ChildAttribute;
   IA32_MAP_ATTRIBUTE  ChildMask;
   IA32_MAP_ATTRIBUTE  CurrentMask;
+  IA32_MAP_ATTRIBUTE  LocalParentAttribute;
 
   ASSERT (Level != 0);
   ASSERT ((Attribute != NULL) && (Mask != NULL));
@@ -284,6 +285,9 @@ PageTableLibMapInLevel (
   NopAttribute.Bits.ReadWrite      = 1;
   NopAttribute.Bits.UserSupervisor = 1;
 
+  LocalParentAttribute.Uint64 = ParentAttribute->Uint64;
+  ParentAttribute             = &LocalParentAttribute;
+
   //
   // ParentPagingEntry ONLY is deferenced for checking Present and MustBeOne bits
   // when Modify is FALSE.
@@ -420,7 +424,7 @@ PageTableLibMapInLevel (
           }
 
           if (IsPle (&PagingEntry[Index], Level)) {
-            PageTableLibSetPle (Level - 1, &PagingEntry[Index], 0, &ChildAttribute, &ChildMask);
+            PageTableLibSetPle (Level, &PagingEntry[Index], 0, &ChildAttribute, &ChildMask);
           } else {
             PageTableLibSetPnle (&PagingEntry[Index].Pnle, &ChildAttribute, &ChildMask);
           }
@@ -664,13 +668,6 @@ PageTableMap (
   //
   // Update the page table when the supplied buffer is sufficient.
   //
-  ParentAttribute.Uint64                    = 0;
-  ParentAttribute.Bits.PageTableBaseAddress = 1;
-  ParentAttribute.Bits.Present              = 1;
-  ParentAttribute.Bits.ReadWrite            = 1;
-  ParentAttribute.Bits.UserSupervisor       = 1;
-  ParentAttribute.Bits.Nx                   = 0;
-
   Status = PageTableLibMapInLevel (
              &TopPagingEntry,
              &ParentAttribute,
-- 
2.35.1.windows.2


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 10/10] CpuPageTableLib: define IA32_PAGE_LEVEL enum type internally
  2022-07-18 13:18 [PATCH 00/10] UefiCpuPkg: Create CpuPageTableLib for manipulating X86 paging structs Ni, Ray
                   ` (8 preceding siblings ...)
  2022-07-18 13:18 ` [PATCH 09/10] CpuPageTableLib: Fix bug that wrongly requires extra size for mapping Ni, Ray
@ 2022-07-18 13:18 ` Ni, Ray
  2022-08-09  3:46 ` [edk2-devel] [PATCH 00/10] UefiCpuPkg: Create CpuPageTableLib for manipulating X86 paging structs Dong, Eric
  10 siblings, 0 replies; 16+ messages in thread
From: Ni, Ray @ 2022-07-18 13:18 UTC (permalink / raw)
  To: devel; +Cc: Zhiguang Liu, Eric Dong

The change doesn't change functionality behavior.

Signed-off-by: Ray Ni <ray.ni@intel.com>
Cc: Zhiguang Liu <zhiguang.liu@intel.com>
Cc: Eric Dong <eric.dong@intel.com>
---
 UefiCpuPkg/Library/CpuPageTableLib/CpuPageTable.h    |  8 ++++++++
 UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c | 12 ++++++------
 2 files changed, 14 insertions(+), 6 deletions(-)

diff --git a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTable.h b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTable.h
index 627f84e4e2..8d856d7c7e 100644
--- a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTable.h
+++ b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTable.h
@@ -20,6 +20,14 @@
 
 #define REGION_LENGTH(l)  LShiftU64 (1, (l) * 9 + 3)
 
+typedef enum {
+  Pte   = 1,
+  Pde   = 2,
+  Pdpte = 3,
+  Pml4  = 4,
+  Pml5  = 5
+} IA32_PAGE_LEVEL;
+
 typedef struct {
   UINT64    Present        : 1;       // 0 = Not present in memory, 1 = Present in memory
   UINT64    ReadWrite      : 1;       // 0 = Read-Only, 1= Read/Write
diff --git a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c
index 16e6697ed4..d3fdf13429 100644
--- a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c
+++ b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c
@@ -245,8 +245,8 @@ PageTableLibMapInLevel (
   IN     BOOLEAN             Modify,
   IN     VOID                *Buffer,
   IN OUT INTN                *BufferSize,
-  IN     UINTN               Level,
-  IN     UINTN               MaxLeafLevel,
+  IN     IA32_PAGE_LEVEL     Level,
+  IN     IA32_PAGE_LEVEL     MaxLeafLevel,
   IN     UINT64              LinearAddress,
   IN     UINT64              Length,
   IN     UINT64              Offset,
@@ -572,8 +572,8 @@ PageTableMap (
   IA32_PAGING_ENTRY   TopPagingEntry;
   INTN                RequiredSize;
   UINT64              MaxLinearAddress;
-  UINTN               MaxLevel;
-  UINTN               MaxLeafLevel;
+  IA32_PAGE_LEVEL     MaxLevel;
+  IA32_PAGE_LEVEL     MaxLeafLevel;
   IA32_MAP_ATTRIBUTE  ParentAttribute;
 
   if ((PagingMode == Paging32bit) || (PagingMode == PagingPae) || (PagingMode >= PagingModeMax)) {
@@ -606,8 +606,8 @@ PageTableMap (
     return RETURN_INVALID_PARAMETER;
   }
 
-  MaxLeafLevel     = (UINT8)PagingMode;
-  MaxLevel         = (UINT8)(PagingMode >> 8);
+  MaxLeafLevel     = (IA32_PAGE_LEVEL)(UINT8)PagingMode;
+  MaxLevel         = (IA32_PAGE_LEVEL)(UINT8)(PagingMode >> 8);
   MaxLinearAddress = LShiftU64 (1, 12 + MaxLevel * 9);
 
   if ((LinearAddress > MaxLinearAddress) || (Length > MaxLinearAddress - LinearAddress)) {
-- 
2.35.1.windows.2


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [edk2-devel] [PATCH 01/10] UefiCpuPkg: Create CpuPageTableLib for manipulating X86 paging structs
  2022-07-18 13:18 ` [PATCH 01/10] " Ni, Ray
@ 2022-07-18 13:49   ` Gerd Hoffmann
  2022-07-19  8:17     ` Ni, Ray
  2022-08-15 16:23   ` Lendacky, Thomas
  1 sibling, 1 reply; 16+ messages in thread
From: Gerd Hoffmann @ 2022-07-18 13:49 UTC (permalink / raw)
  To: devel, ray.ni; +Cc: Eric Dong

On Mon, Jul 18, 2022 at 09:18:22PM +0800, Ni, Ray wrote:
> 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.

Nice to finally see the paging library arrive.

What is the plan for splitting huge pages?  I remember several places
needed that functionality.  Will the library get functions for that in
the future?  Or is the plan to hide that from callers, i.e. have
PageTableMap() automatically create huge pages if possible and split
them if needed?

thanks,
  Gerd


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [edk2-devel] [PATCH 01/10] UefiCpuPkg: Create CpuPageTableLib for manipulating X86 paging structs
  2022-07-18 13:49   ` [edk2-devel] " Gerd Hoffmann
@ 2022-07-19  8:17     ` Ni, Ray
  0 siblings, 0 replies; 16+ messages in thread
From: Ni, Ray @ 2022-07-19  8:17 UTC (permalink / raw)
  To: devel@edk2.groups.io, kraxel@redhat.com; +Cc: Dong, Eric

It has been in my todo list for years.
I wish this lib could be created earlier so that existing duplicated page table
manipulation logics could be avoided.

The Map() supports different PagingModes (4Level, 4Level1G, 5Level, 5Level1G).
It creates big page entry as the PagingMode allows (e.g.: 4Level1G allows to create PDPTE
entry mapping to 1G physical mem while 4Level only allows to create PDE entry mapping
to 2M physical mem.) All are hidden from the Map() API.




> -----Original Message-----
> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of Gerd
> Hoffmann
> Sent: Monday, July 18, 2022 9:50 PM
> To: devel@edk2.groups.io; Ni, Ray <ray.ni@intel.com>
> Cc: Dong, Eric <eric.dong@intel.com>
> Subject: Re: [edk2-devel] [PATCH 01/10] UefiCpuPkg: Create
> CpuPageTableLib for manipulating X86 paging structs
> 
> On Mon, Jul 18, 2022 at 09:18:22PM +0800, Ni, Ray wrote:
> > 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.
> 
> Nice to finally see the paging library arrive.
> 
> What is the plan for splitting huge pages?  I remember several places
> needed that functionality.  Will the library get functions for that in
> the future?  Or is the plan to hide that from callers, i.e. have
> PageTableMap() automatically create huge pages if possible and split
> them if needed?
> 
> thanks,
>   Gerd
> 
> 
> 
> 
> 


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [edk2-devel] [PATCH 00/10] UefiCpuPkg: Create CpuPageTableLib for manipulating X86 paging structs
  2022-07-18 13:18 [PATCH 00/10] UefiCpuPkg: Create CpuPageTableLib for manipulating X86 paging structs Ni, Ray
                   ` (9 preceding siblings ...)
  2022-07-18 13:18 ` [PATCH 10/10] CpuPageTableLib: define IA32_PAGE_LEVEL enum type internally Ni, Ray
@ 2022-08-09  3:46 ` Dong, Eric
  10 siblings, 0 replies; 16+ messages in thread
From: Dong, Eric @ 2022-08-09  3:46 UTC (permalink / raw)
  To: devel@edk2.groups.io, Ni, Ray

Reviewed-by: Eric Dong <eric.dong@intel.com>

-----Original Message-----
From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of Ni, Ray
Sent: Monday, July 18, 2022 9:18 PM
To: devel@edk2.groups.io
Subject: [edk2-devel] [PATCH 00/10] UefiCpuPkg: Create CpuPageTableLib for manipulating X86 paging structs

Ray Ni (10):
  UefiCpuPkg: Create CpuPageTableLib for manipulating X86 paging structs
  UefiCpuPkg/CpuPageTableLib: Return error on invalid parameters
  CpuPageTableLib: Fix a bug when a bit is 1 in Attribute, 0 in Mask
  CpuPageTableLib: Refactor the logic
  CpuPageTableLib: Split the page entry when LA is aligned but PA is not
  CpuPageTableLib: Avoid treating non-leaf entry as leaf one
  CpuPageTableLib: Fix parent attributes are not inherited properly
  CpuPageTableLib: Fix a bug to avoid unnecessary changing to page table
  CpuPageTableLib: Fix bug that wrongly requires extra size for mapping
  CpuPageTableLib: define IA32_PAGE_LEVEL enum type internally

 UefiCpuPkg/Include/Library/CpuPageTableLib.h  | 129 ++++
 .../Library/CpuPageTableLib/CpuPageTable.h    | 230 ++++++
 .../CpuPageTableLib/CpuPageTableLib.inf       |  35 +
 .../Library/CpuPageTableLib/CpuPageTableMap.c | 690 ++++++++++++++++++
 .../CpuPageTableLib/CpuPageTableParse.c       | 330 +++++++++
 UefiCpuPkg/UefiCpuPkg.dec                     |   3 +
 UefiCpuPkg/UefiCpuPkg.dsc                     |   4 +-
 7 files changed, 1420 insertions(+), 1 deletion(-)  create mode 100644 UefiCpuPkg/Include/Library/CpuPageTableLib.h
 create mode 100644 UefiCpuPkg/Library/CpuPageTableLib/CpuPageTable.h
 create mode 100644 UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableLib.inf
 create mode 100644 UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c
 create mode 100644 UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableParse.c

--
2.35.1.windows.2







^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [edk2-devel] [PATCH 01/10] UefiCpuPkg: Create CpuPageTableLib for manipulating X86 paging structs
  2022-07-18 13:18 ` [PATCH 01/10] " Ni, Ray
  2022-07-18 13:49   ` [edk2-devel] " Gerd Hoffmann
@ 2022-08-15 16:23   ` Lendacky, Thomas
  2022-08-16  2:25     ` Ni, Ray
  1 sibling, 1 reply; 16+ messages in thread
From: Lendacky, Thomas @ 2022-08-15 16:23 UTC (permalink / raw)
  To: devel, ray.ni; +Cc: Eric Dong

On 7/18/22 08:18, Ni, Ray via groups.io wrote:
> 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>
> Cc: Eric Dong <eric.dong@intel.com>

Hi Ray,

How do you envision this library working with SEV and/or TDX which use the 
one of the bits in PageTableBaseAddress to indicate whether the page is 
encrypted or shared? For SEV, the encryption mask is set in the 
PcdPteMemoryEncryptionAddressOrMask PCD, are you planning on using that at 
all or is it expected that caller will set/clear the appropriate bits in 
the PageTableBaseAddress field?

Thanks,
Tom

> ---
>   UefiCpuPkg/Include/Library/CpuPageTableLib.h  | 129 +++++
>   .../Library/CpuPageTableLib/CpuPageTable.h    | 204 +++++++
>   .../CpuPageTableLib/CpuPageTableLib.inf       |  35 ++
>   .../Library/CpuPageTableLib/CpuPageTableMap.c | 543 ++++++++++++++++++
>   .../CpuPageTableLib/CpuPageTableParse.c       | 330 +++++++++++
>   UefiCpuPkg/UefiCpuPkg.dec                     |   3 +
>   UefiCpuPkg/UefiCpuPkg.dsc                     |   4 +-
>   7 files changed, 1247 insertions(+), 1 deletion(-)
>   create mode 100644 UefiCpuPkg/Include/Library/CpuPageTableLib.h
>   create mode 100644 UefiCpuPkg/Library/CpuPageTableLib/CpuPageTable.h
>   create mode 100644 UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableLib.inf
>   create mode 100644 UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c
>   create mode 100644 UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableParse.c
> 
> diff --git a/UefiCpuPkg/Include/Library/CpuPageTableLib.h b/UefiCpuPkg/Include/Library/CpuPageTableLib.h
> new file mode 100644
> index 0000000000..2dc9b7d18e
> --- /dev/null
> +++ b/UefiCpuPkg/Include/Library/CpuPageTableLib.h
> @@ -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
> diff --git a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTable.h b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTable.h
> new file mode 100644
> index 0000000000..c041ea3f56
> --- /dev/null
> +++ b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTable.h
> @@ -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
> diff --git a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableLib.inf b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableLib.inf
> new file mode 100644
> index 0000000000..e4ead7441c
> --- /dev/null
> +++ b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableLib.inf
> @@ -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
> diff --git a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c
> new file mode 100644
> index 0000000000..25e13a6f6f
> --- /dev/null
> +++ b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c
> @@ -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        = 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,
> +             BufferSize,
> +             MaxLevel,
> +             MaxLeafLevel,
> +             LinearAddress,
> +             Length,
> +             0,
> +             Attribute,
> +             Mask
> +             );
> +  if (!RETURN_ERROR (Status)) {
> +    *PageTable = (UINTN)(TopPagingEntry.Uintn & IA32_PE_BASE_ADDRESS_MASK_40);
> +  }
> +
> +  return Status;
> +}
> diff --git a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableParse.c b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableParse.c
> new file mode 100644
> index 0000000000..e66961e122
> --- /dev/null
> +++ b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableParse.c
> @@ -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;
> +}
> diff --git a/UefiCpuPkg/UefiCpuPkg.dec b/UefiCpuPkg/UefiCpuPkg.dec
> index 1951eb294c..4fe79cecbf 100644
> --- a/UefiCpuPkg/UefiCpuPkg.dec
> +++ b/UefiCpuPkg/UefiCpuPkg.dec
> @@ -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 }}
> diff --git a/UefiCpuPkg/UefiCpuPkg.dsc b/UefiCpuPkg/UefiCpuPkg.dsc
> index a0bbde9985..f694b3a77c 100644
> --- a/UefiCpuPkg/UefiCpuPkg.dsc
> +++ b/UefiCpuPkg/UefiCpuPkg.dsc
> @@ -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

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [edk2-devel] [PATCH 01/10] UefiCpuPkg: Create CpuPageTableLib for manipulating X86 paging structs
  2022-08-15 16:23   ` Lendacky, Thomas
@ 2022-08-16  2:25     ` Ni, Ray
  0 siblings, 0 replies; 16+ messages in thread
From: Ni, Ray @ 2022-08-16  2:25 UTC (permalink / raw)
  To: Tom Lendacky, devel@edk2.groups.io; +Cc: Dong, Eric

Tom,
Yes. I expect caller sets/clears the appropriate bits in the PageTableBaseAddress bit-field.

With this way, the PageTableLib can be stable enough and doesn't need to change for
different (Intel/AMD/...) x86 features today (or future) that repurpose some bits in
the page table entry.

To be specific, TDX uses the highest bit as a indicator for shared memory. So the caller of this lib
needs to set the highest bit in PageTableBaseAddress for shared memory in TDX case.

I guess SEV code can use the similar way.

Thanks,
Ray

> -----Original Message-----
> From: Tom Lendacky <thomas.lendacky@amd.com>
> Sent: Tuesday, August 16, 2022 12:24 AM
> To: devel@edk2.groups.io; Ni, Ray <ray.ni@intel.com>
> Cc: Dong, Eric <eric.dong@intel.com>
> Subject: Re: [edk2-devel] [PATCH 01/10] UefiCpuPkg: Create
> CpuPageTableLib for manipulating X86 paging structs
> 
> On 7/18/22 08:18, Ni, Ray via groups.io wrote:
> 
> Hi Ray,
> 
> How do you envision this library working with SEV and/or TDX which use the
> one of the bits in PageTableBaseAddress to indicate whether the page is
> encrypted or shared? For SEV, the encryption mask is set in the
> PcdPteMemoryEncryptionAddressOrMask PCD, are you planning on using
> that at
> all or is it expected that caller will set/clear the appropriate bits in
> the PageTableBaseAddress field?
> 
> Thanks,
> Tom
> 
> > ---
> >   UefiCpuPkg/Include/Library/CpuPageTableLib.h  | 129 +++++
> >   .../Library/CpuPageTableLib/CpuPageTable.h    | 204 +++++++
> >   .../CpuPageTableLib/CpuPageTableLib.inf       |  35 ++
> >   .../Library/CpuPageTableLib/CpuPageTableMap.c | 543
> ++++++++++++++++++
> >   .../CpuPageTableLib/CpuPageTableParse.c       | 330 +++++++++++
> >   UefiCpuPkg/UefiCpuPkg.dec                     |   3 +
> >   UefiCpuPkg/UefiCpuPkg.dsc                     |   4 +-
> >   7 files changed, 1247 insertions(+), 1 deletion(-)
> >   create mode 100644 UefiCpuPkg/Include/Library/CpuPageTableLib.h
> >   create mode 100644
> UefiCpuPkg/Library/CpuPageTableLib/CpuPageTable.h
> >   create mode 100644
> UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableLib.inf
> >   create mode 100644
> UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c
> >   create mode 100644
> UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableParse.c
> >
> > diff --git a/UefiCpuPkg/Include/Library/CpuPageTableLib.h
> b/UefiCpuPkg/Include/Library/CpuPageTableLib.h
> > new file mode 100644
> > index 0000000000..2dc9b7d18e
> > --- /dev/null
> > +++ b/UefiCpuPkg/Include/Library/CpuPageTableLib.h
> > @@ -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
> > diff --git a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTable.h
> b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTable.h
> > new file mode 100644
> > index 0000000000..c041ea3f56
> > --- /dev/null
> > +++ b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTable.h
> > @@ -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
> > diff --git a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableLib.inf
> b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableLib.inf
> > new file mode 100644
> > index 0000000000..e4ead7441c
> > --- /dev/null
> > +++ b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableLib.inf
> > @@ -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
> > diff --git a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c
> b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c
> > new file mode 100644
> > index 0000000000..25e13a6f6f
> > --- /dev/null
> > +++ b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c
> > @@ -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        = 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,
> > +             BufferSize,
> > +             MaxLevel,
> > +             MaxLeafLevel,
> > +             LinearAddress,
> > +             Length,
> > +             0,
> > +             Attribute,
> > +             Mask
> > +             );
> > +  if (!RETURN_ERROR (Status)) {
> > +    *PageTable = (UINTN)(TopPagingEntry.Uintn &
> IA32_PE_BASE_ADDRESS_MASK_40);
> > +  }
> > +
> > +  return Status;
> > +}
> > diff --git a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableParse.c
> b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableParse.c
> > new file mode 100644
> > index 0000000000..e66961e122
> > --- /dev/null
> > +++ b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableParse.c
> > @@ -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;
> > +}
> > diff --git a/UefiCpuPkg/UefiCpuPkg.dec b/UefiCpuPkg/UefiCpuPkg.dec
> > index 1951eb294c..4fe79cecbf 100644
> > --- a/UefiCpuPkg/UefiCpuPkg.dec
> > +++ b/UefiCpuPkg/UefiCpuPkg.dec
> > @@ -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 }}
> > diff --git a/UefiCpuPkg/UefiCpuPkg.dsc b/UefiCpuPkg/UefiCpuPkg.dsc
> > index a0bbde9985..f694b3a77c 100644
> > --- a/UefiCpuPkg/UefiCpuPkg.dsc
> > +++ b/UefiCpuPkg/UefiCpuPkg.dsc
> > @@ -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/Smm
> CpuRendezvousLib.inf
> > +
> CpuPageTableLib|UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableLib.inf
> >
> >   [LibraryClasses.common.SEC]
> >
> PlatformSecLib|UefiCpuPkg/Library/PlatformSecLibNull/PlatformSecLibNull.i
> nf
> > @@ -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

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2022-08-16  2:25 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2022-07-18 13:18 [PATCH 00/10] UefiCpuPkg: Create CpuPageTableLib for manipulating X86 paging structs Ni, Ray
2022-07-18 13:18 ` [PATCH 01/10] " Ni, Ray
2022-07-18 13:49   ` [edk2-devel] " Gerd Hoffmann
2022-07-19  8:17     ` Ni, Ray
2022-08-15 16:23   ` Lendacky, Thomas
2022-08-16  2:25     ` Ni, Ray
2022-07-18 13:18 ` [PATCH 02/10] UefiCpuPkg/CpuPageTableLib: Return error on invalid parameters Ni, Ray
2022-07-18 13:18 ` [PATCH 03/10] CpuPageTableLib: Fix a bug when a bit is 1 in Attribute, 0 in Mask Ni, Ray
2022-07-18 13:18 ` [PATCH 04/10] CpuPageTableLib: Refactor the logic Ni, Ray
2022-07-18 13:18 ` [PATCH 05/10] CpuPageTableLib: Split the page entry when LA is aligned but PA is not Ni, Ray
2022-07-18 13:18 ` [PATCH 06/10] CpuPageTableLib: Avoid treating non-leaf entry as leaf one Ni, Ray
2022-07-18 13:18 ` [PATCH 07/10] CpuPageTableLib: Fix parent attributes are not inherited properly Ni, Ray
2022-07-18 13:18 ` [PATCH 08/10] CpuPageTableLib: Fix a bug to avoid unnecessary changing to page table Ni, Ray
2022-07-18 13:18 ` [PATCH 09/10] CpuPageTableLib: Fix bug that wrongly requires extra size for mapping Ni, Ray
2022-07-18 13:18 ` [PATCH 10/10] CpuPageTableLib: define IA32_PAGE_LEVEL enum type internally Ni, Ray
2022-08-09  3:46 ` [edk2-devel] [PATCH 00/10] UefiCpuPkg: Create CpuPageTableLib for manipulating X86 paging structs Dong, Eric

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox