From nobody Sun Feb 8 17:21:23 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of groups.io designates 66.175.222.108 as permitted sender) client-ip=66.175.222.108; envelope-from=bounce+27952+91449+1787277+3901457@groups.io; helo=mail02.groups.io; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of groups.io designates 66.175.222.108 as permitted sender) smtp.mailfrom=bounce+27952+91449+1787277+3901457@groups.io; dmarc=fail(p=none dis=none) header.from=intel.com ARC-Seal: i=1; a=rsa-sha256; t=1658150327; cv=none; d=zohomail.com; s=zohoarc; b=HQ+EDTP6DF2Nhy1H/HD2mvEA1p6/FiZMmF7+62HkoZBB7pW8kWJvWwQKX5URmERjux5EmEzL+LnyeRaGfsxPdaeXda0J9TPNcf309xiTiYrR0mWmBCM1TtXsd3oGvuHfg6E4AOxOUGFbUTnSKGGg0NodcaPcnOYHxa0adL5lxYE= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1658150327; h=Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:References:Sender:Subject:To; bh=MBEjrzkMaNPVLiVJS8nz3SwKWpA2+NW7eSghuu+bBg8=; b=FeDwN/khlqCA1Y5jZNz5Mu7TjzfW/Q3cefGBHJUXvvCqhDDbdm81BX2Y6drp0Vx0+hnw/TLzIjwnzGN5+Q+xbqktfqMwE4yOBMrv0lKHT214/KW6E74dfCyuwCfHVMVfn023r8N2SbNEHYE2UbC4IDjWrv7A5IGN5uH5jn+q9bQ= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of groups.io designates 66.175.222.108 as permitted sender) smtp.mailfrom=bounce+27952+91449+1787277+3901457@groups.io; dmarc=fail header.from= (p=none dis=none) Received: from mail02.groups.io (mail02.groups.io [66.175.222.108]) by mx.zohomail.com with SMTPS id 165815032740176.74836566823365; Mon, 18 Jul 2022 06:18:47 -0700 (PDT) Return-Path: X-Received: by 127.0.0.2 with SMTP id 2cj4YY1788612xq9LPcEpJFL; Mon, 18 Jul 2022 06:18:47 -0700 X-Received: from mga03.intel.com (mga03.intel.com [134.134.136.65]) by mx.groups.io with SMTP id smtpd.web12.27079.1658150325580236005 for ; Mon, 18 Jul 2022 06:18:45 -0700 X-IronPort-AV: E=McAfee;i="6400,9594,10411"; a="287363886" X-IronPort-AV: E=Sophos;i="5.92,281,1650956400"; d="scan'208";a="287363886" X-Received: from orsmga008.jf.intel.com ([10.7.209.65]) by orsmga103.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 18 Jul 2022 06:18:40 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.92,281,1650956400"; d="scan'208";a="624724945" X-Received: from shwdeopenlab706.ccr.corp.intel.com ([10.239.183.102]) by orsmga008.jf.intel.com with ESMTP; 18 Jul 2022 06:18:39 -0700 From: "Ni, Ray" To: devel@edk2.groups.io Cc: Eric Dong Subject: [edk2-devel] [PATCH 01/10] UefiCpuPkg: Create CpuPageTableLib for manipulating X86 paging structs Date: Mon, 18 Jul 2022 21:18:22 +0800 Message-Id: <20220718131831.660-2-ray.ni@intel.com> In-Reply-To: <20220718131831.660-1-ray.ni@intel.com> References: <20220718131831.660-1-ray.ni@intel.com> MIME-Version: 1.0 Precedence: Bulk List-Unsubscribe: List-Subscribe: List-Help: Sender: devel@edk2.groups.io List-Id: Mailing-List: list devel@edk2.groups.io; contact devel+owner@edk2.groups.io Reply-To: devel@edk2.groups.io,ray.ni@intel.com X-Gm-Message-State: 9OrY0YpLp7BbCO73HuzroELux1787277AA= Content-Transfer-Encoding: quoted-printable DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=groups.io; q=dns/txt; s=20140610; t=1658150327; bh=R2el/dOZ9s2lAhcVYwUoExabZktTm6mIFdw6DyBM6ao=; h=Cc:Date:From:Reply-To:Subject:To; b=VMljbiGLdTjeGU5u4eQ5w1NpobvgdMAxB5H9gU4QZILS5HA/tG6yncTmue2Vq51ja6U BSP5pkEkro1cX4pJQ/urcYr4Atwi9uqK/AwRI1cUsAazURcIm7Yu+Rmqcnz+z8HW10F3b 3xlrfgRsgtDcVctqsdBzONjzyeH4KaaHLWY= X-ZohoMail-DKIM: pass (identity @groups.io) X-ZM-MESSAGEID: 1658150329601100012 Content-Type: text/plain; charset="utf-8" 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 Cc: Eric Dong --- UefiCpuPkg/Include/Library/CpuPageTableLib.h | 129 +++++ .../Library/CpuPageTableLib/CpuPageTable.h | 204 +++++++ .../CpuPageTableLib/CpuPageTableLib.inf | 35 ++ .../Library/CpuPageTableLib/CpuPageTableMap.c | 543 ++++++++++++++++++ .../CpuPageTableLib/CpuPageTableParse.c | 330 +++++++++++ UefiCpuPkg/UefiCpuPkg.dec | 3 + UefiCpuPkg/UefiCpuPkg.dsc | 4 +- 7 files changed, 1247 insertions(+), 1 deletion(-) create mode 100644 UefiCpuPkg/Include/Library/CpuPageTableLib.h create mode 100644 UefiCpuPkg/Library/CpuPageTableLib/CpuPageTable.h create mode 100644 UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableLib.inf create mode 100644 UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c create mode 100644 UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableParse.c diff --git a/UefiCpuPkg/Include/Library/CpuPageTableLib.h b/UefiCpuPkg/Incl= ude/Library/CpuPageTableLib.h new file mode 100644 index 0000000000..2dc9b7d18e --- /dev/null +++ b/UefiCpuPkg/Include/Library/CpuPageTableLib.h @@ -0,0 +1,129 @@ +/** @file + Public include file for PageTableLib library. + + Copyright (c) 2022, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef PAGE_TABLE_LIB_H_ +#define PAGE_TABLE_LIB_H_ + +typedef union { + struct { + UINT64 Present : 1; // 0 =3D Not present in memory, 1 = =3D Present in memory + UINT64 ReadWrite : 1; // 0 =3D Read-Only, 1=3D Read/Write + UINT64 UserSupervisor : 1; // 0 =3D Supervisor, 1=3DUser + UINT64 WriteThrough : 1; // 0 =3D Write-Back caching, 1=3DW= rite-Through caching + UINT64 CacheDisabled : 1; // 0 =3D Cached, 1=3DNon-Cached + UINT64 Accessed : 1; // 0 =3D Not accessed, 1 =3D Acces= sed (set by CPU) + UINT64 Dirty : 1; // 0 =3D Not dirty, 1 =3D Dirty (s= et by CPU) + UINT64 Pat : 1; // PAT + + UINT64 Global : 1; // 0 =3D Not global, 1 =3D Global = (if CR4.PGE =3D 1) + UINT64 Reserved1 : 3; // Ignored + + UINT64 PageTableBaseAddress : 40; // Page Table Base Address + UINT64 Reserved2 : 7; // Ignored + UINT64 ProtectionKey : 4; // Protection key + UINT64 Nx : 1; // No Execute bit + } Bits; + UINT64 Uint64; +} IA32_MAP_ATTRIBUTE; + +#define IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS_MASK 0xFFFFFFFFFF000ull +#define IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS(pa) ((pa)->Uint64 & IA= 32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS_MASK) +#define IA32_MAP_ATTRIBUTE_ATTRIBUTES(pa) ((pa)->Uint64 & ~I= A32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS_MASK) + +// +// Below enum follows "4.1.1 Four Paging Modes" in Chapter 4 Paging of SDM= Volume 3. +// Page1GB is only supported in 4-level and 5-level. +// +typedef enum { + Paging32bit, + + // + // High byte in paging mode indicates the max levels of the page table. + // Low byte in paging mode indicates the max level that can be a leaf en= try. + // + PagingPae =3D 0x0302, + + Paging4Level =3D 0x0402, + Paging4Level1GB =3D 0x0403, + + Paging5Level =3D 0x0502, + Paging5Level1GB =3D 0x0503, + + PagingModeMax +} PAGING_MODE; + +/** + Create or update page table to map [LinearAddress, LinearAddress + Lengt= h) with specified attribute. + + @param[in, out] PageTable The pointer to the page table to update, = or pointer to NULL if a new page table is to be created. + @param[in] PagingMode The paging mode. + @param[in] Buffer The free buffer to be used for page table= creation/updating. + @param[in, out] BufferSize The buffer size. + On return, the remaining buffer size. + The free buffer is used from the end so c= aller can supply the same Buffer pointer with an updated + BufferSize in the second call to this API. + @param[in] LinearAddress The start of the linear address range. + @param[in] Length The length of the linear address range. + @param[in] Attribute The attribute of the linear address range. + All non-reserved fields in IA32_MAP_ATTRI= BUTE are supported to set in the page table. + Page table entries that map the linear ad= dress range are reset to 0 before set to the new attribute + when a new physical base address is set. + @param[in] Mask The mask used for attribute. The correspo= nding field in Attribute is ignored if that in Mask is 0. + + @retval RETURN_UNSUPPORTED PagingMode is not supported. + @retval RETURN_INVALID_PARAMETER PageTable, BufferSize, Attribute or Ma= sk is NULL. + @retval RETURN_INVALID_PARAMETER *BufferSize is not multiple of 4KB. + @retval RETURN_BUFFER_TOO_SMALL The buffer is too small for page table= creation/updating. + BufferSize is updated to indicate the = expected buffer size. + Caller may still get RETURN_BUFFER_TOO= _SMALL with the new BufferSize. + @retval RETURN_SUCCESS PageTable is created/updated successfu= lly. +**/ +RETURN_STATUS +EFIAPI +PageTableMap ( + IN OUT UINTN *PageTable OPTIONAL, + IN PAGING_MODE PagingMode, + IN VOID *Buffer, + IN OUT UINTN *BufferSize, + IN UINT64 LinearAddress, + IN UINT64 Length, + IN IA32_MAP_ATTRIBUTE *Attribute, + IN IA32_MAP_ATTRIBUTE *Mask + ); + +typedef struct { + UINT64 LinearAddress; + UINT64 Length; + IA32_MAP_ATTRIBUTE Attribute; +} IA32_MAP_ENTRY; + +/** + Parse page table. + + @param[in] PageTable Pointer to the page table. + @param[in] PagingMode The paging mode. + @param[out] Map Return an array that describes multiple linea= r address ranges. + @param[in, out] MapCount On input, the maximum number of entries that = Map can hold. + On output, the number of entries in Map. + + @retval RETURN_UNSUPPORTED PageLevel is not 5 or 4. + @retval RETURN_INVALID_PARAMETER MapCount is NULL. + @retval RETURN_INVALID_PARAMETER *MapCount is not 0 but Map is NULL. + @retval RETURN_BUFFER_TOO_SMALL *MapCount is too small. + @retval RETURN_SUCCESS Page table is parsed successfully. +**/ +RETURN_STATUS +EFIAPI +PageTableParse ( + IN UINTN PageTable, + IN PAGING_MODE PagingMode, + IN IA32_MAP_ENTRY *Map, + IN OUT UINTN *MapCount + ); + +#endif diff --git a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTable.h b/UefiCpuPkg= /Library/CpuPageTableLib/CpuPageTable.h new file mode 100644 index 0000000000..c041ea3f56 --- /dev/null +++ b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTable.h @@ -0,0 +1,204 @@ +/** @file + Internal header for CpuPageTableLib. + + Copyright (c) 2022, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef CPU_PAGE_TABLE_H_ +#define CPU_PAGE_TABLE_H_ + +#include +#include +#include +#include +#include + +#define IA32_PE_BASE_ADDRESS_MASK_40 0xFFFFFFFFFF000ull +#define IA32_PE_BASE_ADDRESS_MASK_39 0xFFFFFFFFFE000ull + +#define REGION_LENGTH(l) LShiftU64 (1, (l) * 9 + 3) + +typedef struct { + UINT64 Present : 1; // 0 =3D Not present in memory, 1 = =3D Present in memory +} IA32_PAGE_COMMON_ENTRY; + +/// +/// Format of a non-leaf entry that references a page table entry +/// +typedef union { + struct { + UINT64 Present : 1; // 0 =3D Not present in memory, 1 = =3D Present in memory + UINT64 ReadWrite : 1; // 0 =3D Read-Only, 1=3D Read/Write + UINT64 UserSupervisor : 1; // 0 =3D Supervisor, 1=3DUser + UINT64 WriteThrough : 1; // 0 =3D Write-Back caching, 1=3DW= rite-Through caching + UINT64 CacheDisabled : 1; // 0 =3D Cached, 1=3DNon-Cached + UINT64 Accessed : 1; // 0 =3D Not accessed, 1 =3D Acces= sed (set by CPU) + UINT64 Available0 : 1; // Ignored + UINT64 MustBeZero : 1; // Must Be Zero + + UINT64 Available2 : 4; // Ignored + + UINT64 PageTableBaseAddress : 40; // Page Table Base Address + UINT64 Available3 : 11; // Ignored + UINT64 Nx : 1; // No Execute bit + } Bits; + UINT64 Uint64; +} IA32_PAGE_NON_LEAF_ENTRY; + +#define IA32_PNLE_PAGE_TABLE_BASE_ADDRESS(pa) ((pa)->Uint64 & IA32_PE_BAS= E_ADDRESS_MASK_40) + +/// +/// Format of a PML5 Entry (PML5E) that References a PML4 Table +/// +typedef IA32_PAGE_NON_LEAF_ENTRY IA32_PML5E; + +/// +/// Format of a PML4 Entry (PML4E) that References a Page-Directory-Pointe= r Table +/// +typedef IA32_PAGE_NON_LEAF_ENTRY IA32_PML4E; + +/// +/// Format of a Page-Directory-Pointer-Table Entry (PDPTE) that References= a Page Directory +/// +typedef IA32_PAGE_NON_LEAF_ENTRY IA32_PDPTE; + +/// +/// Format of a Page-Directory Entry that References a Page Table +/// +typedef IA32_PAGE_NON_LEAF_ENTRY IA32_PDE; + +/// +/// Format of a leaf entry that Maps a 1-Gbyte or 2-MByte Page +/// +typedef union { + struct { + UINT64 Present : 1; // 0 =3D Not present in memory, 1 = =3D Present in memory + UINT64 ReadWrite : 1; // 0 =3D Read-Only, 1=3D Read/Write + UINT64 UserSupervisor : 1; // 0 =3D Supervisor, 1=3DUser + UINT64 WriteThrough : 1; // 0 =3D Write-Back caching, 1=3DW= rite-Through caching + UINT64 CacheDisabled : 1; // 0 =3D Cached, 1=3DNon-Cached + UINT64 Accessed : 1; // 0 =3D Not accessed, 1 =3D Acces= sed (set by CPU) + UINT64 Dirty : 1; // 0 =3D Not dirty, 1 =3D Dirty (s= et by CPU) + UINT64 MustBeOne : 1; // Page Size. Must Be One + + UINT64 Global : 1; // 0 =3D Not global, 1 =3D Global = (if CR4.PGE =3D 1) + UINT64 Available1 : 3; // Ignored + UINT64 Pat : 1; // PAT + + UINT64 PageTableBaseAddress : 39; // Page Table Base Address + UINT64 Available3 : 7; // Ignored + UINT64 ProtectionKey : 4; // Protection key + UINT64 Nx : 1; // No Execute bit + } Bits; + UINT64 Uint64; +} IA32_PAGE_LEAF_ENTRY_BIG_PAGESIZE; +#define IA32_PLEB_PAGE_TABLE_BASE_ADDRESS(pa) ((pa)->Uint64 & IA32_PE_BAS= E_ADDRESS_MASK_39) + +/// +/// Format of a Page-Directory Entry that Maps a 2-MByte Page +/// +typedef IA32_PAGE_LEAF_ENTRY_BIG_PAGESIZE IA32_PDE_2M; + +/// +/// Format of a Page-Directory-Pointer-Table Entry (PDPTE) that Maps a 1-G= Byte Page +/// +typedef IA32_PAGE_LEAF_ENTRY_BIG_PAGESIZE IA32_PDPTE_1G; + +/// +/// Format of a Page-Table Entry that Maps a 4-KByte Page +/// +typedef union { + struct { + UINT64 Present : 1; // 0 =3D Not present in memory, 1 = =3D Present in memory + UINT64 ReadWrite : 1; // 0 =3D Read-Only, 1=3D Read/Write + UINT64 UserSupervisor : 1; // 0 =3D Supervisor, 1=3DUser + UINT64 WriteThrough : 1; // 0 =3D Write-Back caching, 1=3DW= rite-Through caching + UINT64 CacheDisabled : 1; // 0 =3D Cached, 1=3DNon-Cached + UINT64 Accessed : 1; // 0 =3D Not accessed, 1 =3D Acces= sed (set by CPU) + UINT64 Dirty : 1; // 0 =3D Not dirty, 1 =3D Dirty (s= et by CPU) + UINT64 Pat : 1; // PAT + + UINT64 Global : 1; // 0 =3D Not global, 1 =3D Global = (if CR4.PGE =3D 1) + UINT64 Available1 : 3; // Ignored + + UINT64 PageTableBaseAddress : 40; // Page Table Base Address + UINT64 Available3 : 7; // Ignored + UINT64 ProtectionKey : 4; // Protection key + UINT64 Nx : 1; // No Execute bit + } Bits; + UINT64 Uint64; +} IA32_PTE_4K; +#define IA32_PTE4K_PAGE_TABLE_BASE_ADDRESS(pa) ((pa)->Uint64 & IA32_PE_BA= SE_ADDRESS_MASK_40) + +/// +/// Format of a Page-Directory-Pointer-Table Entry (PDPTE) that References= a Page Directory (32bit PAE specific) +/// +typedef union { + struct { + UINT64 Present : 1; // 0 =3D Not present in memory, 1 = =3D Present in memory + UINT64 MustBeZero : 2; // Must Be Zero + UINT64 WriteThrough : 1; // 0 =3D Write-Back caching, 1=3DW= rite-Through caching + UINT64 CacheDisabled : 1; // 0 =3D Cached, 1=3DNon-Cached + UINT64 MustBeZero2 : 4; // Must Be Zero + + UINT64 Available : 3; // Ignored + + UINT64 PageTableBaseAddress : 40; // Page Table Base Address + UINT64 MustBeZero3 : 12; // Must Be Zero + } Bits; + UINT64 Uint64; +} IA32_PDPTE_PAE; + +typedef union { + IA32_PAGE_NON_LEAF_ENTRY Pnle; // To access Pml5, Pml4, Pdpt= e and Pde. + IA32_PML5E Pml5; + IA32_PML4E Pml4; + IA32_PDPTE Pdpte; + IA32_PDE Pde; + + IA32_PAGE_LEAF_ENTRY_BIG_PAGESIZE PleB; // to access Pdpte1G and Pde2= M. + IA32_PDPTE_1G Pdpte1G; + IA32_PDE_2M Pde2M; + + IA32_PTE_4K Pte4K; + + IA32_PDPTE_PAE PdptePae; + IA32_PAGE_COMMON_ENTRY Pce; // To access all common bits i= n above entries. + + UINT64 Uint64; + UINTN Uintn; +} IA32_PAGING_ENTRY; + +/** + Return TRUE when the page table entry is a leaf entry that points to the= physical address memory. + Return FALSE when the page table entry is a non-leaf entry that points t= o the page table entries. + + @param[in] PagingEntry Pointer to the page table entry. + @param[in] Level Page level where the page table entry resides in. + + @retval TRUE It's a leaf entry. + @retval FALSE It's a non-leaf entry. +**/ +BOOLEAN +IsPle ( + IN IA32_PAGING_ENTRY *PagingEntry, + IN UINTN Level + ); + +/** + Return the attribute of a 2M/1G page table entry. + + @param[in] PleB Pointer to a 2M/1G page table entry. + @param[in] ParentMapAttribute Pointer to the parent attribute. + + @return Attribute of the 2M/1G page table entry. +**/ +UINT64 +PageTableLibGetPleBMapAttribute ( + IN IA32_PAGE_LEAF_ENTRY_BIG_PAGESIZE *PleB, + IN IA32_MAP_ATTRIBUTE *ParentMapAttribute + ); + +#endif diff --git a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableLib.inf b/UefiC= puPkg/Library/CpuPageTableLib/CpuPageTableLib.inf new file mode 100644 index 0000000000..e4ead7441c --- /dev/null +++ b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableLib.inf @@ -0,0 +1,35 @@ +## @file +# This library implements CpuPageTableLib that are generic for IA32 famil= y CPU. +# +# Copyright (c) 2022, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION =3D 0x00010005 + BASE_NAME =3D CpuPageTableLib + FILE_GUID =3D 524ed6a1-f661-451b-929b-b54d755c914a + MODULE_TYPE =3D BASE + VERSION_STRING =3D 1.0 + LIBRARY_CLASS =3D CpuPageTableLib + +# +# The following information is for reference only and not required by the = build tools. +# +# VALID_ARCHITECTURES =3D IA32 X64 +# + +[Sources] + CpuPageTableMap.c + CpuPageTableParse.c + CpuPageTable.h + +[Packages] + MdePkg/MdePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib diff --git a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c b/UefiCpu= Pkg/Library/CpuPageTableLib/CpuPageTableMap.c new file mode 100644 index 0000000000..25e13a6f6f --- /dev/null +++ b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c @@ -0,0 +1,543 @@ +/** @file + This library implements CpuPageTableLib that are generic for IA32 family= CPU. + + Copyright (c) 2022, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CpuPageTable.h" + +/** + Set the IA32_PTE_4K. + + @param[in] Pte4K Pointer to IA32_PTE_4K. + @param[in] Offset The offset within the linear address range. + @param[in] Attribute The attribute of the linear address range. + All non-reserved fields in IA32_MAP_ATTRIBUTE are s= upported to set in the page table. + Page table entry is reset to 0 before set to the ne= w attribute when a new physical base address is set. + @param[in] Mask The mask used for attribute. The corresponding fiel= d in Attribute is ignored if that in Mask is 0. +**/ +VOID +PageTableLibSetPte4K ( + IN IA32_PTE_4K *Pte4K, + IN UINT64 Offset, + IN IA32_MAP_ATTRIBUTE *Attribute, + IN IA32_MAP_ATTRIBUTE *Mask + ) +{ + if (Mask->Bits.PageTableBaseAddress) { + // + // Reset all attributes when the physical address is changed. + // + Pte4K->Uint64 =3D IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS (Attribut= e) + Offset; + } + + if (Mask->Bits.Present) { + Pte4K->Bits.Present =3D Attribute->Bits.Present; + } + + if (Mask->Bits.ReadWrite) { + Pte4K->Bits.ReadWrite =3D Attribute->Bits.ReadWrite; + } + + if (Mask->Bits.UserSupervisor) { + Pte4K->Bits.UserSupervisor =3D Attribute->Bits.UserSupervisor; + } + + if (Mask->Bits.WriteThrough) { + Pte4K->Bits.WriteThrough =3D Attribute->Bits.WriteThrough; + } + + if (Mask->Bits.CacheDisabled) { + Pte4K->Bits.CacheDisabled =3D Attribute->Bits.CacheDisabled; + } + + if (Mask->Bits.Accessed) { + Pte4K->Bits.Accessed =3D Attribute->Bits.Accessed; + } + + if (Mask->Bits.Dirty) { + Pte4K->Bits.Dirty =3D Attribute->Bits.Dirty; + } + + if (Mask->Bits.Pat) { + Pte4K->Bits.Pat =3D Attribute->Bits.Pat; + } + + if (Mask->Bits.Global) { + Pte4K->Bits.Global =3D Attribute->Bits.Global; + } + + if (Mask->Bits.ProtectionKey) { + Pte4K->Bits.ProtectionKey =3D Attribute->Bits.ProtectionKey; + } + + if (Mask->Bits.Nx) { + Pte4K->Bits.Nx =3D Attribute->Bits.Nx; + } +} + +/** + Set the IA32_PDPTE_1G or IA32_PDE_2M. + + @param[in] PleB Pointer to PDPTE_1G or PDE_2M. Both share the same = structure definition. + @param[in] Offset The offset within the linear address range. + @param[in] Attribute The attribute of the linear address range. + All non-reserved fields in IA32_MAP_ATTRIBUTE are s= upported to set in the page table. + Page table entry is reset to 0 before set to the ne= w attribute when a new physical base address is set. + @param[in] Mask The mask used for attribute. The corresponding fiel= d in Attribute is ignored if that in Mask is 0. +**/ +VOID +PageTableLibSetPleB ( + IN IA32_PAGE_LEAF_ENTRY_BIG_PAGESIZE *PleB, + IN UINT64 Offset, + IN IA32_MAP_ATTRIBUTE *Attribute, + IN IA32_MAP_ATTRIBUTE *Mask + ) +{ + if (Mask->Bits.PageTableBaseAddress) { + // + // Reset all attributes when the physical address is changed. + // + PleB->Uint64 =3D IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS (Attribute= ) + Offset; + } + + PleB->Bits.MustBeOne =3D 1; + + if (Mask->Bits.Present) { + PleB->Bits.Present =3D Attribute->Bits.Present; + } + + if (Mask->Bits.ReadWrite) { + PleB->Bits.ReadWrite =3D Attribute->Bits.ReadWrite; + } + + if (Mask->Bits.UserSupervisor) { + PleB->Bits.UserSupervisor =3D Attribute->Bits.UserSupervisor; + } + + if (Mask->Bits.WriteThrough) { + PleB->Bits.WriteThrough =3D Attribute->Bits.WriteThrough; + } + + if (Mask->Bits.CacheDisabled) { + PleB->Bits.CacheDisabled =3D Attribute->Bits.CacheDisabled; + } + + if (Mask->Bits.Accessed) { + PleB->Bits.Accessed =3D Attribute->Bits.Accessed; + } + + if (Mask->Bits.Dirty) { + PleB->Bits.Dirty =3D Attribute->Bits.Dirty; + } + + if (Mask->Bits.Pat) { + PleB->Bits.Pat =3D Attribute->Bits.Pat; + } + + if (Mask->Bits.Global) { + PleB->Bits.Global =3D Attribute->Bits.Global; + } + + if (Mask->Bits.ProtectionKey) { + PleB->Bits.ProtectionKey =3D Attribute->Bits.ProtectionKey; + } + + if (Mask->Bits.Nx) { + PleB->Bits.Nx =3D Attribute->Bits.Nx; + } +} + +/** + Set the IA32_PDPTE_1G, IA32_PDE_2M or IA32_PTE_4K. + + @param[in] Level 3, 2 or 1. + @param[in] Ple Pointer to PDPTE_1G, PDE_2M or IA32_PTE_4K, dependi= ng on the Level. + @param[in] Offset The offset within the linear address range. + @param[in] Attribute The attribute of the linear address range. + All non-reserved fields in IA32_MAP_ATTRIBUTE are s= upported to set in the page table. + Page table entry is reset to 0 before set to the ne= w attribute when a new physical base address is set. + @param[in] Mask The mask used for attribute. The corresponding fiel= d in Attribute is ignored if that in Mask is 0. +**/ +VOID +PageTableLibSetPle ( + IN UINTN Level, + IN IA32_PAGING_ENTRY *Ple, + IN UINT64 Offset, + IN IA32_MAP_ATTRIBUTE *Attribute, + IN IA32_MAP_ATTRIBUTE *Mask + ) +{ + if (Level =3D=3D 1) { + PageTableLibSetPte4K (&Ple->Pte4K, Offset, Attribute, Mask); + } else { + ASSERT (Level =3D=3D 2 || Level =3D=3D 3); + PageTableLibSetPleB (&Ple->PleB, Offset, Attribute, Mask); + } +} + +/** + Set the IA32_PML5, IA32_PML4, IA32_PDPTE or IA32_PDE. + + @param[in] Pnle Pointer to IA32_PML5, IA32_PML4, IA32_PDPTE or IA32= _PDE. All share the same structure definition. + @param[in] Attribute The attribute of the page directory referenced by t= he non-leaf. +**/ +VOID +PageTableLibSetPnle ( + IN IA32_PAGE_NON_LEAF_ENTRY *Pnle, + IN IA32_MAP_ATTRIBUTE *Attribute + ) +{ + Pnle->Bits.Present =3D Attribute->Bits.Present; + Pnle->Bits.ReadWrite =3D Attribute->Bits.ReadWrite; + Pnle->Bits.UserSupervisor =3D Attribute->Bits.UserSupervisor; + Pnle->Bits.Nx =3D Attribute->Bits.Nx; + Pnle->Bits.Accessed =3D 0; + + // + // Set the attributes (WT, CD, A) to 0. + // WT and CD determin the memory type used to access the 4K page directo= ry referenced by this entry. + // So, it implictly requires PAT[0] is Write Back. + // Create a new parameter if caller requires to use a different memory t= ype for accessing page directories. + // + Pnle->Bits.WriteThrough =3D 0; + Pnle->Bits.CacheDisabled =3D 0; +} + +/** + Update page table to map [LinearAddress, LinearAddress + Length) with sp= ecified attribute in the specified level. + + @param[in] ParentPagingEntry The pointer to the page table entry to= update. + @param[in] Modify FALSE to indicate Buffer is not used a= nd BufferSize is increased by the required buffer size. + @param[in] Buffer The free buffer to be used for page ta= ble creation/updating. + When Modify is TRUE, it's used from th= e end. + When Modify is FALSE, it's ignored. + @param[in, out] BufferSize The available buffer size. + Return the remaining buffer size. + @param[in] Level Page table level. Could be 5, 4, 3, 2,= or 1. + @param[in] MaxLeafLevel Maximum level that can be a leaf entry= . Could be 1, 2 or 3 (if Page 1G is supported). + @param[in] LinearAddress The start of the linear address range. + @param[in] Length The length of the linear address range. + @param[in] Offset The offset within the linear address r= ange. + @param[in] Attribute The attribute of the linear address ra= nge. + All non-reserved fields in IA32_MAP_AT= TRIBUTE are supported to set in the page table. + Page table entries that map the linear= address range are reset to 0 before set to the new attribute + when a new physical base address is se= t. + @param[in] Mask The mask used for attribute. The corre= sponding field in Attribute is ignored if that in Mask is 0. + + @retval RETURN_SUCCESS PageTable is created/updated successfu= lly. +**/ +RETURN_STATUS +PageTableLibMapInLevel ( + IN IA32_PAGING_ENTRY *ParentPagingEntry, + IN BOOLEAN Modify, + IN VOID *Buffer, + IN OUT INTN *BufferSize, + IN UINTN Level, + IN UINTN MaxLeafLevel, + IN UINT64 LinearAddress, + IN UINT64 Length, + IN UINT64 Offset, + IN IA32_MAP_ATTRIBUTE *Attribute, + IN IA32_MAP_ATTRIBUTE *Mask + ) +{ + RETURN_STATUS Status; + UINTN BitStart; + UINTN Index; + IA32_PAGING_ENTRY *PagingEntry; + UINT64 RegionLength; + UINT64 SubLength; + UINT64 SubOffset; + UINT64 RegionMask; + UINT64 RegionStart; + IA32_MAP_ATTRIBUTE AllOneMask; + IA32_MAP_ATTRIBUTE PleBAttribute; + IA32_MAP_ATTRIBUTE NopAttribute; + BOOLEAN CreateNew; + IA32_PAGING_ENTRY OneOfPagingEntry; + + ASSERT (Level !=3D 0); + ASSERT ((Attribute !=3D NULL) && (Mask !=3D NULL)); + + CreateNew =3D FALSE; + AllOneMask.Uint64 =3D ~0ull; + + NopAttribute.Uint64 =3D 0; + NopAttribute.Bits.Present =3D 1; + NopAttribute.Bits.ReadWrite =3D 1; + NopAttribute.Bits.UserSupervisor =3D 1; + + // + // ParentPagingEntry ONLY is deferenced for checking Present and MustBeO= ne bits + // when Modify is FALSE. + // + + if (ParentPagingEntry->Pce.Present =3D=3D 0) { + // + // The parent entry is CR3 or PML5E/PML4E/PDPTE/PDE. + // It does NOT point to an existing page directory. + // + ASSERT (Buffer =3D=3D NULL || *BufferSize >=3D SIZE_4KB); + CreateNew =3D TRUE; + *BufferSize -=3D SIZE_4KB; + + if (Modify) { + ParentPagingEntry->Uintn =3D (UINTN)Buffer + *BufferSize; + ZeroMem ((VOID *)ParentPagingEntry->Uintn, SIZE_4KB); + // + // Set default attribute bits for PML5E/PML4E/PDPTE/PDE. + // + PageTableLibSetPnle (&ParentPagingEntry->Pnle, &NopAttribute); + } else { + // + // Just make sure Present and MustBeZero (PageSize) bits are accurat= e. + // + OneOfPagingEntry.Pnle.Uint64 =3D 0; + } + } else if (IsPle (ParentPagingEntry, Level + 1)) { + // + // The parent entry is a PDPTE_1G or PDE_2M. Split to 2M or 4K pages. + // Note: it's impossible the parent entry is a PTE_4K. + // + // + // Use NOP attributes as the attribute of grand-parents because CPU wi= ll consider + // the actual attributes of grand-parents when determing the memory ty= pe. + // + PleBAttribute.Uint64 =3D PageTableLibGetPleBMapAttribute (&ParentPagin= gEntry->PleB, &NopAttribute); + if ((IA32_MAP_ATTRIBUTE_ATTRIBUTES (&PleBAttribute) & IA32_MAP_ATTRIBU= TE_ATTRIBUTES (Mask)) + =3D=3D IA32_MAP_ATTRIBUTE_ATTRIBUTES (Attribute)) + { + // + // This function is called when the memory length is less than the r= egion length of the parent level. + // No need to split the page when the attributes equal. + // + return RETURN_SUCCESS; + } + + ASSERT (Buffer =3D=3D NULL || *BufferSize >=3D SIZE_4KB); + CreateNew =3D TRUE; + *BufferSize -=3D SIZE_4KB; + PageTableLibSetPle (Level, &OneOfPagingEntry, 0, &PleBAttribute, &AllO= neMask); + if (Modify) { + // + // Create 512 child-level entries that map to 2M/4K. + // + ParentPagingEntry->Uintn =3D (UINTN)Buffer + *BufferSize; + ZeroMem ((VOID *)ParentPagingEntry->Uintn, SIZE_4KB); + + // + // Set NOP attributes + // Note: Should NOT inherit the attributes from the original entry b= ecause a zero RW bit + // will make the entire region read-only even the child entrie= s set the RW bit. + // + PageTableLibSetPnle (&ParentPagingEntry->Pnle, &NopAttribute); + + RegionLength =3D REGION_LENGTH (Level); + PagingEntry =3D (IA32_PAGING_ENTRY *)(UINTN)IA32_PNLE_PAGE_TABLE_BA= SE_ADDRESS (&ParentPagingEntry->Pnle); + for (SubOffset =3D 0, Index =3D 0; Index < 512; Index++) { + PagingEntry[Index].Uint64 =3D OneOfPagingEntry.Uint64 + SubOffset; + SubOffset +=3D RegionLength; + } + } + } + + // + // RegionLength: 256T (1 << 48) 512G (1 << 39), 1G (1 << 30), 2M (1 << 2= 1) or 4K (1 << 12). + // RegionStart: points to the linear address that's aligned on RegionLe= ngth and lower than (LinearAddress + Offset). + // + BitStart =3D 12 + (Level - 1) * 9; + Index =3D BitFieldRead64 (LinearAddress + Offset, BitStart, BitSt= art + 9 - 1); + RegionLength =3D LShiftU64 (1, BitStart); + RegionMask =3D RegionLength - 1; + RegionStart =3D (LinearAddress + Offset) & ~RegionMask; + + // + // Apply the attribute. + // + PagingEntry =3D (IA32_PAGING_ENTRY *)(UINTN)IA32_PNLE_PAGE_TABLE_BASE_AD= DRESS (&ParentPagingEntry->Pnle); + while (Offset < Length && Index < 512) { + SubLength =3D MIN (Length - Offset, RegionStart + RegionLength - (Line= arAddress + Offset)); + if ((Level <=3D MaxLeafLevel) && (LinearAddress + Offset =3D=3D Region= Start) && (SubLength =3D=3D RegionLength)) { + // + // Create one entry mapping the entire region (1G, 2M or 4K). + // + if (Modify) { + PageTableLibSetPle (Level, &PagingEntry[Index], Offset, Attribute,= Mask); + } + } else { + // + // Recursively call to create page table. + // There are 3 cases: + // a. Level cannot be a leaf entry which points to physical memory. + // a. Level can be a leaf entry but (LinearAddress + Offset) is NO= T aligned on the RegionStart. + // b. Level can be a leaf entry and (LinearAddress + Offset) is al= igned on RegionStart, + // but the length is SMALLER than the RegionLength. + // + Status =3D PageTableLibMapInLevel ( + (!Modify && CreateNew) ? &OneOfPagingEntry : &PagingEntry= [Index], + Modify, + Buffer, + BufferSize, + Level - 1, + MaxLeafLevel, + LinearAddress, + Length, + Offset, + Attribute, + Mask + ); + if (RETURN_ERROR (Status)) { + return Status; + } + } + + Offset +=3D SubLength; + RegionStart +=3D RegionLength; + Index++; + } + + return RETURN_SUCCESS; +} + +/** + Create or update page table to map [LinearAddress, LinearAddress + Lengt= h) with specified attribute. + + @param[in, out] PageTable The pointer to the page table to update, = or pointer to NULL if a new page table is to be created. + @param[in] PagingMode The paging mode. + @param[in] Buffer The free buffer to be used for page table= creation/updating. + @param[in, out] BufferSize The buffer size. + On return, the remaining buffer size. + The free buffer is used from the end so c= aller can supply the same Buffer pointer with an updated + BufferSize in the second call to this API. + @param[in] LinearAddress The start of the linear address range. + @param[in] Length The length of the linear address range. + @param[in] Attribute The attribute of the linear address range. + All non-reserved fields in IA32_MAP_ATTRI= BUTE are supported to set in the page table. + Page table entries that map the linear ad= dress range are reset to 0 before set to the new attribute + when a new physical base address is set. + @param[in] Mask The mask used for attribute. The correspo= nding field in Attribute is ignored if that in Mask is 0. + + @retval RETURN_UNSUPPORTED PagingMode is not supported. + @retval RETURN_INVALID_PARAMETER PageTable, BufferSize, Attribute or Ma= sk is NULL. + @retval RETURN_INVALID_PARAMETER *BufferSize is not multiple of 4KB. + @retval RETURN_BUFFER_TOO_SMALL The buffer is too small for page table= creation/updating. + BufferSize is updated to indicate the = expected buffer size. + Caller may still get RETURN_BUFFER_TOO= _SMALL with the new BufferSize. + @retval RETURN_SUCCESS PageTable is created/updated successfu= lly. +**/ +RETURN_STATUS +EFIAPI +PageTableMap ( + IN OUT UINTN *PageTable OPTIONAL, + IN PAGING_MODE PagingMode, + IN VOID *Buffer, + IN OUT UINTN *BufferSize, + IN UINT64 LinearAddress, + IN UINT64 Length, + IN IA32_MAP_ATTRIBUTE *Attribute, + IN IA32_MAP_ATTRIBUTE *Mask + ) +{ + RETURN_STATUS Status; + IA32_PAGING_ENTRY TopPagingEntry; + INTN RequiredSize; + UINT64 MaxLinearAddress; + UINTN MaxLevel; + UINTN MaxLeafLevel; + + if ((PagingMode =3D=3D Paging32bit) || (PagingMode =3D=3D PagingPae) || = (PagingMode >=3D PagingModeMax)) { + // + // 32bit paging is never supported. + // PAE paging will be supported later. + // + return RETURN_UNSUPPORTED; + } + + if ((PageTable =3D=3D NULL) || (BufferSize =3D=3D NULL) || (Attribute = =3D=3D NULL) || (Mask =3D=3D NULL)) { + return RETURN_INVALID_PARAMETER; + } + + if (*BufferSize % SIZE_4KB !=3D 0) { + // + // BufferSize should be multiple of 4K. + // + return RETURN_INVALID_PARAMETER; + } + + if ((*BufferSize !=3D 0) && (Buffer =3D=3D NULL)) { + return RETURN_INVALID_PARAMETER; + } + + MaxLeafLevel =3D (UINT8)PagingMode; + MaxLevel =3D (UINT8)(PagingMode >> 8); + MaxLinearAddress =3D LShiftU64 (1, 12 + MaxLevel * 9); + + if ((LinearAddress > MaxLinearAddress) || (Length > MaxLinearAddress - L= inearAddress)) { + // + // Maximum linear address is (1 << 48) or (1 << 57) + // + return RETURN_INVALID_PARAMETER; + } + + TopPagingEntry.Uintn =3D *PageTable; + if (TopPagingEntry.Uintn !=3D 0) { + TopPagingEntry.Pce.Present =3D 1; + } + + // + // Query the required buffer size without modifying the page table. + // + RequiredSize =3D 0; + Status =3D PageTableLibMapInLevel ( + &TopPagingEntry, + FALSE, + NULL, + &RequiredSize, + MaxLevel, + MaxLeafLevel, + LinearAddress, + Length, + 0, + Attribute, + Mask + ); + if (RETURN_ERROR (Status)) { + return Status; + } + + RequiredSize =3D -RequiredSize; + + if ((UINTN)RequiredSize > *BufferSize) { + *BufferSize =3D RequiredSize; + return RETURN_BUFFER_TOO_SMALL; + } + + if ((RequiredSize !=3D 0) && (Buffer =3D=3D NULL)) { + return RETURN_INVALID_PARAMETER; + } + + // + // Update the page table when the supplied buffer is sufficient. + // + Status =3D PageTableLibMapInLevel ( + &TopPagingEntry, + TRUE, + Buffer, + BufferSize, + MaxLevel, + MaxLeafLevel, + LinearAddress, + Length, + 0, + Attribute, + Mask + ); + if (!RETURN_ERROR (Status)) { + *PageTable =3D (UINTN)(TopPagingEntry.Uintn & IA32_PE_BASE_ADDRESS_MAS= K_40); + } + + return Status; +} diff --git a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableParse.c b/UefiC= puPkg/Library/CpuPageTableLib/CpuPageTableParse.c new file mode 100644 index 0000000000..e66961e122 --- /dev/null +++ b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableParse.c @@ -0,0 +1,330 @@ +/** @file + This library implements CpuPageTableLib that are generic for IA32 family= CPU. + + Copyright (c) 2022, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CpuPageTable.h" + +/** + Return the attribute of a 2M/1G page table entry. + + @param[in] PleB Pointer to a 2M/1G page table entry. + @param[in] ParentMapAttribute Pointer to the parent attribute. + + @return Attribute of the 2M/1G page table entry. +**/ +UINT64 +PageTableLibGetPleBMapAttribute ( + IN IA32_PAGE_LEAF_ENTRY_BIG_PAGESIZE *PleB, + IN IA32_MAP_ATTRIBUTE *ParentMapAttribute + ) +{ + IA32_MAP_ATTRIBUTE MapAttribute; + + // + // PageTableBaseAddress cannot be assigned field to field + // because their bit positions are different in IA32_MAP_ATTRIBUTE and I= A32_PAGE_LEAF_ENTRY_BIG_PAGESIZE. + // + MapAttribute.Uint64 =3D IA32_PLEB_PAGE_TABLE_BASE_ADDRESS (PleB); + + MapAttribute.Bits.Present =3D ParentMapAttribute->Bits.Present & = PleB->Bits.Present; + MapAttribute.Bits.ReadWrite =3D ParentMapAttribute->Bits.ReadWrite = & PleB->Bits.ReadWrite; + MapAttribute.Bits.UserSupervisor =3D ParentMapAttribute->Bits.UserSuperv= isor & PleB->Bits.UserSupervisor; + MapAttribute.Bits.Nx =3D ParentMapAttribute->Bits.Nx | PleB-= >Bits.Nx; + MapAttribute.Bits.WriteThrough =3D PleB->Bits.WriteThrough; + MapAttribute.Bits.CacheDisabled =3D PleB->Bits.CacheDisabled; + MapAttribute.Bits.Accessed =3D PleB->Bits.Accessed; + + MapAttribute.Bits.Pat =3D PleB->Bits.Pat; + MapAttribute.Bits.Dirty =3D PleB->Bits.Dirty; + MapAttribute.Bits.Global =3D PleB->Bits.Global; + MapAttribute.Bits.ProtectionKey =3D PleB->Bits.ProtectionKey; + + return MapAttribute.Uint64; +} + +/** + Return the attribute of a 4K page table entry. + + @param[in] Pte4K Pointer to a 4K page table entry. + @param[in] ParentMapAttribute Pointer to the parent attribute. + + @return Attribute of the 4K page table entry. +**/ +UINT64 +PageTableLibGetPte4KMapAttribute ( + IN IA32_PTE_4K *Pte4K, + IN IA32_MAP_ATTRIBUTE *ParentMapAttribute + ) +{ + IA32_MAP_ATTRIBUTE MapAttribute; + + MapAttribute.Uint64 =3D IA32_PTE4K_PAGE_TABLE_BASE_ADDRESS (Pte4K); + + MapAttribute.Bits.Present =3D ParentMapAttribute->Bits.Present & = Pte4K->Bits.Present; + MapAttribute.Bits.ReadWrite =3D ParentMapAttribute->Bits.ReadWrite = & Pte4K->Bits.ReadWrite; + MapAttribute.Bits.UserSupervisor =3D ParentMapAttribute->Bits.UserSuperv= isor & Pte4K->Bits.UserSupervisor; + MapAttribute.Bits.Nx =3D ParentMapAttribute->Bits.Nx | Pte4K= ->Bits.Nx; + MapAttribute.Bits.WriteThrough =3D Pte4K->Bits.WriteThrough; + MapAttribute.Bits.CacheDisabled =3D Pte4K->Bits.CacheDisabled; + MapAttribute.Bits.Accessed =3D Pte4K->Bits.Accessed; + + MapAttribute.Bits.Pat =3D Pte4K->Bits.Pat; + MapAttribute.Bits.Dirty =3D Pte4K->Bits.Dirty; + MapAttribute.Bits.Global =3D Pte4K->Bits.Global; + MapAttribute.Bits.ProtectionKey =3D Pte4K->Bits.ProtectionKey; + + return MapAttribute.Uint64; +} + +/** + Return the attribute of a non-leaf page table entry. + + @param[in] Pnle Pointer to a non-leaf page table entry. + @param[in] ParentMapAttribute Pointer to the parent attribute. + + @return Attribute of the non-leaf page table entry. +**/ +UINT64 +PageTableLibGetPnleMapAttribute ( + IN IA32_PAGE_NON_LEAF_ENTRY *Pnle, + IN IA32_MAP_ATTRIBUTE *ParentMapAttribute + ) +{ + IA32_MAP_ATTRIBUTE MapAttribute; + + MapAttribute.Uint64 =3D Pnle->Uint64; + + MapAttribute.Bits.Present =3D ParentMapAttribute->Bits.Present & = Pnle->Bits.Present; + MapAttribute.Bits.ReadWrite =3D ParentMapAttribute->Bits.ReadWrite = & Pnle->Bits.ReadWrite; + MapAttribute.Bits.UserSupervisor =3D ParentMapAttribute->Bits.UserSuperv= isor & Pnle->Bits.UserSupervisor; + MapAttribute.Bits.Nx =3D ParentMapAttribute->Bits.Nx | Pnle-= >Bits.Nx; + MapAttribute.Bits.WriteThrough =3D Pnle->Bits.WriteThrough; + MapAttribute.Bits.CacheDisabled =3D Pnle->Bits.CacheDisabled; + MapAttribute.Bits.Accessed =3D Pnle->Bits.Accessed; + return MapAttribute.Uint64; +} + +/** + Return TRUE when the page table entry is a leaf entry that points to the= physical address memory. + Return FALSE when the page table entry is a non-leaf entry that points t= o the page table entries. + + @param[in] PagingEntry Pointer to the page table entry. + @param[in] Level Page level where the page table entry resides in. + + @retval TRUE It's a leaf entry. + @retval FALSE It's a non-leaf entry. +**/ +BOOLEAN +IsPle ( + IN IA32_PAGING_ENTRY *PagingEntry, + IN UINTN Level + ) +{ + // + // PML5E and PML4E are always non-leaf entries. + // + if (Level =3D=3D 1) { + return TRUE; + } + + if (((Level =3D=3D 3) || (Level =3D=3D 2))) { + if (PagingEntry->PleB.Bits.MustBeOne =3D=3D 1) { + return TRUE; + } + } + + return FALSE; +} + +/** + Recursively parse the non-leaf page table entries. + + @param[in] PageTableBaseAddress The base address of the 512 non-lea= f page table entries in the specified level. + @param[in] Level Page level. Could be 5, 4, 3, 2, 1. + @param[in] RegionStart The base linear address of the regi= on covered by the non-leaf page table entries. + @param[in] ParentMapAttribute The mapping attribute of the parent= entries. + @param[in, out] Map Pointer to an array that describes = multiple linear address ranges. + @param[in, out] MapCount Pointer to a UINTN that hold the ac= tual number of entries in the Map. + @param[in] MapCapacity The maximum number of entries the M= ap can hold. + @param[in] LastEntry Pointer to last map entry. + @param[in] OneEntry Pointer to a library internal stora= ge that holds one map entry. + It's used when Map array is used up. +**/ +VOID +PageTableLibParsePnle ( + IN UINT64 PageTableBaseAddress, + IN UINTN Level, + IN UINT64 RegionStart, + IN IA32_MAP_ATTRIBUTE *ParentMapAttribute, + IN OUT IA32_MAP_ENTRY *Map, + IN OUT UINTN *MapCount, + IN UINTN MapCapacity, + IN IA32_MAP_ENTRY **LastEntry, + IN IA32_MAP_ENTRY *OneEntry + ) +{ + IA32_PAGING_ENTRY *PagingEntry; + UINTN Index; + IA32_MAP_ATTRIBUTE MapAttribute; + UINT64 RegionLength; + + ASSERT (OneEntry !=3D NULL); + + PagingEntry =3D (IA32_PAGING_ENTRY *)(UINTN)PageTableBaseAddress; + RegionLength =3D REGION_LENGTH (Level); + + for (Index =3D 0; Index < 512; Index++, RegionStart +=3D RegionLength) { + if (PagingEntry[Index].Pce.Present =3D=3D 0) { + continue; + } + + if (IsPle (&PagingEntry[Index], Level)) { + ASSERT (Level =3D=3D 1 || Level =3D=3D 2 || Level =3D=3D 3); + + if (Level =3D=3D 1) { + MapAttribute.Uint64 =3D PageTableLibGetPte4KMapAttribute (&PagingE= ntry[Index].Pte4K, ParentMapAttribute); + } else { + MapAttribute.Uint64 =3D PageTableLibGetPleBMapAttribute (&PagingEn= try[Index].PleB, ParentMapAttribute); + } + + if ((*LastEntry !=3D NULL) && + ((*LastEntry)->LinearAddress + (*LastEntry)->Length =3D=3D Regio= nStart) && + (IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS (&(*LastEntry)->Attr= ibute) + (*LastEntry)->Length + =3D=3D IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS (&MapAttribut= e)) && + (IA32_MAP_ATTRIBUTE_ATTRIBUTES (&(*LastEntry)->Attribute) =3D=3D= IA32_MAP_ATTRIBUTE_ATTRIBUTES (&MapAttribute)) + ) + { + // + // Extend LastEntry. + // + (*LastEntry)->Length +=3D RegionLength; + } else { + if (*MapCount < MapCapacity) { + // + // LastEntry points to next map entry in the array. + // + *LastEntry =3D &Map[*MapCount]; + } else { + // + // LastEntry points to library internal map entry. + // + *LastEntry =3D OneEntry; + } + + // + // Set LastEntry. + // + (*LastEntry)->LinearAddress =3D RegionStart; + (*LastEntry)->Length =3D RegionLength; + (*LastEntry)->Attribute.Uint64 =3D MapAttribute.Uint64; + (*MapCount)++; + } + } else { + MapAttribute.Uint64 =3D PageTableLibGetPnleMapAttribute (&PagingEntr= y[Index].Pnle, ParentMapAttribute); + PageTableLibParsePnle ( + IA32_PNLE_PAGE_TABLE_BASE_ADDRESS (&PagingEntry[Index].Pnle), + Level - 1, + RegionStart, + &MapAttribute, + Map, + MapCount, + MapCapacity, + LastEntry, + OneEntry + ); + } + } +} + +/** + Parse page table. + + @param[in] PageTable Pointer to the page table. + @param[in] PagingMode The paging mode. + @param[out] Map Return an array that describes multiple linea= r address ranges. + @param[in, out] MapCount On input, the maximum number of entries that = Map can hold. + On output, the number of entries in Map. + + @retval RETURN_UNSUPPORTED PageLevel is not 5 or 4. + @retval RETURN_INVALID_PARAMETER MapCount is NULL. + @retval RETURN_INVALID_PARAMETER *MapCount is not 0 but Map is NULL. + @retval RETURN_BUFFER_TOO_SMALL *MapCount is too small. + @retval RETURN_SUCCESS Page table is parsed successfully. +**/ +RETURN_STATUS +EFIAPI +PageTableParse ( + IN UINTN PageTable, + IN PAGING_MODE PagingMode, + OUT IA32_MAP_ENTRY *Map, + IN OUT UINTN *MapCount + ) +{ + UINTN MapCapacity; + IA32_MAP_ATTRIBUTE NopAttribute; + IA32_MAP_ENTRY *LastEntry; + IA32_MAP_ENTRY OneEntry; + UINTN MaxLevel; + + if ((PagingMode =3D=3D Paging32bit) || (PagingMode =3D=3D PagingPae) || = (PagingMode >=3D PagingModeMax)) { + // + // 32bit paging is never supported. + // PAE paging will be supported later. + // + return RETURN_UNSUPPORTED; + } + + if (MapCount =3D=3D NULL) { + return RETURN_INVALID_PARAMETER; + } + + if ((*MapCount !=3D 0) && (Map =3D=3D NULL)) { + return RETURN_INVALID_PARAMETER; + } + + if (PageTable =3D=3D 0) { + *MapCount =3D 0; + return RETURN_SUCCESS; + } + + // + // Page table layout is as below: + // + // [IA32_CR3] + // | + // | + // V + // [IA32_PML5E] + // ... + // [IA32_PML5E] --> [IA32_PML4E] + // ... + // [IA32_PML4E] --> [IA32_PDPTE_1G] --> 1G aligned phys= ical address + // ... + // [IA32_PDPTE] --> [IA32_PDE_2M] --> = 2M aligned physical address + // ... + // [IA32_PDE] --> [IA= 32_PTE_4K] --> 4K aligned physical address + // ... + // [IA= 32_PTE_4K] --> 4K aligned physical address + // + + NopAttribute.Uint64 =3D 0; + NopAttribute.Bits.Present =3D 1; + NopAttribute.Bits.ReadWrite =3D 1; + NopAttribute.Bits.UserSupervisor =3D 1; + + MaxLevel =3D (UINT8)(PagingMode >> 8); + MapCapacity =3D *MapCount; + *MapCount =3D 0; + LastEntry =3D NULL; + PageTableLibParsePnle ((UINT64)PageTable, MaxLevel, 0, &NopAttribute, Ma= p, MapCount, MapCapacity, &LastEntry, &OneEntry); + + if (*MapCount > MapCapacity) { + return RETURN_BUFFER_TOO_SMALL; + } + + return RETURN_SUCCESS; +} diff --git a/UefiCpuPkg/UefiCpuPkg.dec b/UefiCpuPkg/UefiCpuPkg.dec index 1951eb294c..4fe79cecbf 100644 --- a/UefiCpuPkg/UefiCpuPkg.dec +++ b/UefiCpuPkg/UefiCpuPkg.dec @@ -62,6 +62,9 @@ ## @libraryclass Provides function for loading microcode. MicrocodeLib|Include/Library/MicrocodeLib.h =20 + ## @libraryclass Provides function for manipulating x86 paging structu= res. + CpuPageTableLib|Include/Library/CpuPageTableLib.h + [Guids] gUefiCpuPkgTokenSpaceGuid =3D { 0xac05bf33, 0x995a, 0x4ed4, { 0xaa,= 0xb8, 0xef, 0x7a, 0xe8, 0xf, 0x5c, 0xb0 }} gMsegSmramGuid =3D { 0x5802bce4, 0xeeee, 0x4e33, { 0xa1,= 0x30, 0xeb, 0xad, 0x27, 0xf0, 0xe4, 0x39 }} diff --git a/UefiCpuPkg/UefiCpuPkg.dsc b/UefiCpuPkg/UefiCpuPkg.dsc index a0bbde9985..f694b3a77c 100644 --- a/UefiCpuPkg/UefiCpuPkg.dsc +++ b/UefiCpuPkg/UefiCpuPkg.dsc @@ -1,7 +1,7 @@ ## @file # UefiCpuPkg Package # -# Copyright (c) 2007 - 2021, Intel Corporation. All rights reserved.
+# Copyright (c) 2007 - 2022, Intel Corporation. All rights reserved.
# # SPDX-License-Identifier: BSD-2-Clause-Patent # @@ -62,6 +62,7 @@ VmgExitLib|UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf MicrocodeLib|UefiCpuPkg/Library/MicrocodeLib/MicrocodeLib.inf SmmCpuRendezvousLib|UefiCpuPkg/Library/SmmCpuRendezvousLib/SmmCpuRendezv= ousLib.inf + CpuPageTableLib|UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableLib.inf =20 [LibraryClasses.common.SEC] PlatformSecLib|UefiCpuPkg/Library/PlatformSecLibNull/PlatformSecLibNull.= inf @@ -175,6 +176,7 @@ UefiCpuPkg/Universal/Acpi/S3Resume2Pei/S3Resume2Pei.inf UefiCpuPkg/ResetVector/Vtf0/Bin/ResetVector.inf UefiCpuPkg/Library/SmmCpuRendezvousLib/SmmCpuRendezvousLib.inf + UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableLib.inf =20 [BuildOptions] *_*_*_CC_FLAGS =3D -D DISABLE_NEW_DEPRECATED_INTERFACES --=20 2.35.1.windows.2 -=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#91449): https://edk2.groups.io/g/devel/message/91449 Mute This Topic: https://groups.io/mt/92458154/1787277 Group Owner: devel+owner@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [importer@patchew.org] -=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-