--- /dev/null
+/** @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
--- /dev/null
+/** @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
--- /dev/null
+## @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
--- /dev/null
+/** @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
--- /dev/null
+/** @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
## @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
## @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
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