]> git.proxmox.com Git - mirror_edk2.git/commitdiff
UefiCpuPkg: Create CpuPageTableLib for manipulating X86 paging structs
authorRay Ni <ray.ni@intel.com>
Thu, 14 Jul 2022 06:33:18 +0000 (14:33 +0800)
committermergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Tue, 9 Aug 2022 07:08:05 +0000 (07:08 +0000)
The lib includes two APIs:
* PageTableMap
  It creates/updates mapping from LA to PA.
  The implementation only supports paging structures used in 64bit
  mode now. PAE paging structure support will be added in future.

* PageTableParse
   It parses the page table and returns the mapping relations in an
  array of IA32_MAP_ENTRY.

It passed some stress tests. These test code will be upstreamed in
other patches following edk2 Unit Test framework.

Signed-off-by: Ray Ni <ray.ni@intel.com>
Reviewed-by: Eric Dong <eric.dong@intel.com>
UefiCpuPkg/Include/Library/CpuPageTableLib.h [new file with mode: 0644]
UefiCpuPkg/Library/CpuPageTableLib/CpuPageTable.h [new file with mode: 0644]
UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableLib.inf [new file with mode: 0644]
UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c [new file with mode: 0644]
UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableParse.c [new file with mode: 0644]
UefiCpuPkg/UefiCpuPkg.dec
UefiCpuPkg/UefiCpuPkg.dsc

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