From: "Ni, Ray" <ray.ni@intel.com>
To: devel@edk2.groups.io
Cc: Zhiguang Liu <zhiguang.liu@intel.com>, Eric Dong <eric.dong@intel.com>
Subject: [PATCH 07/10] CpuPageTableLib: Fix parent attributes are not inherited properly
Date: Mon, 18 Jul 2022 21:18:28 +0800 [thread overview]
Message-ID: <20220718131831.660-8-ray.ni@intel.com> (raw)
In-Reply-To: <20220718131831.660-1-ray.ni@intel.com>
With the following paging structure that maps [0, 2G] with ReadWrite
bit set.
PML4[0] --> PDPTE[0] --> PDE[0-255]
\-> PDPTE[1] --> PDE[0-255]
If ReadWrite bit is cleared in PML4[0] and PageTableMap() is called
to change [0, 2M] as writable, today's logic doesn't inherit the
parent entry's attributes when determining the child entry's
attributes. It just sets the PDPTE[0].PDE[0].ReadWrite bit.
But since the PML4[0].ReadWrite is 0, [0, 2M] is still read-only.
The change fixes the bug.
If the inheritable attributes in ParentPagingEntry conflicts with the
requested attributes, let the child entries take the parent attributes
and loosen the attribute in the parent entry.
E.g.: when PDPTE[0].ReadWrite = 0 but caller wants to map [0-2MB as
ReadWrite = 1 (PDE[0].ReadWrite = 1), we need to change
PDPTE[0].ReadWrite = 1 and let all PDE[0-255].ReadWrite = 0 first.
Then change PDE[0].ReadWrite = 1.
Signed-off-by: Zhiguang Liu <zhiguang.liu@intel.com>
Signed-off-by: Ray Ni <ray.ni@intel.com>
Cc: Eric Dong <eric.dong@intel.com>
---
.../Library/CpuPageTableLib/CpuPageTable.h | 20 ++-
.../Library/CpuPageTableLib/CpuPageTableMap.c | 144 ++++++++++++++++--
2 files changed, 147 insertions(+), 17 deletions(-)
diff --git a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTable.h b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTable.h
index c041ea3f56..627f84e4e2 100644
--- a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTable.h
+++ b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTable.h
@@ -21,7 +21,11 @@
#define REGION_LENGTH(l) LShiftU64 (1, (l) * 9 + 3)
typedef struct {
- UINT64 Present : 1; // 0 = Not present in memory, 1 = Present in memory
+ UINT64 Present : 1; // 0 = Not present in memory, 1 = Present in memory
+ UINT64 ReadWrite : 1; // 0 = Read-Only, 1= Read/Write
+ UINT64 UserSupervisor : 1; // 0 = Supervisor, 1=User
+ UINT64 Reserved : 58;
+ UINT64 Nx : 1; // No Execute bit
} IA32_PAGE_COMMON_ENTRY;
///
@@ -201,4 +205,18 @@ PageTableLibGetPleBMapAttribute (
IN IA32_MAP_ATTRIBUTE *ParentMapAttribute
);
+/**
+ Return the attribute of a non-leaf page table entry.
+
+ @param[in] Pnle Pointer to a non-leaf page table entry.
+ @param[in] ParentMapAttribute Pointer to the parent attribute.
+
+ @return Attribute of the non-leaf page table entry.
+**/
+UINT64
+PageTableLibGetPnleMapAttribute (
+ IN IA32_PAGE_NON_LEAF_ENTRY *Pnle,
+ IN IA32_MAP_ATTRIBUTE *ParentMapAttribute
+ );
+
#endif
diff --git a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c
index a6aa1a352b..ac5d1c79f4 100644
--- a/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c
+++ b/UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableMap.c
@@ -183,18 +183,32 @@ PageTableLibSetPle (
@param[in] Pnle Pointer to IA32_PML5, IA32_PML4, IA32_PDPTE or IA32_PDE. All share the same structure definition.
@param[in] Attribute The attribute of the page directory referenced by the non-leaf.
+ @param[in] Mask The mask of the page directory referenced by the non-leaf.
**/
VOID
PageTableLibSetPnle (
IN IA32_PAGE_NON_LEAF_ENTRY *Pnle,
- IN IA32_MAP_ATTRIBUTE *Attribute
+ IN IA32_MAP_ATTRIBUTE *Attribute,
+ IN IA32_MAP_ATTRIBUTE *Mask
)
{
- Pnle->Bits.Present = Attribute->Bits.Present;
- Pnle->Bits.ReadWrite = Attribute->Bits.ReadWrite;
- Pnle->Bits.UserSupervisor = Attribute->Bits.UserSupervisor;
- Pnle->Bits.Nx = Attribute->Bits.Nx;
- Pnle->Bits.Accessed = 0;
+ if (Mask->Bits.Present) {
+ Pnle->Bits.Present = Attribute->Bits.Present;
+ }
+
+ if (Mask->Bits.ReadWrite) {
+ Pnle->Bits.ReadWrite = Attribute->Bits.ReadWrite;
+ }
+
+ if (Mask->Bits.UserSupervisor) {
+ Pnle->Bits.UserSupervisor = Attribute->Bits.UserSupervisor;
+ }
+
+ if (Mask->Bits.Nx) {
+ Pnle->Bits.Nx = Attribute->Bits.Nx;
+ }
+
+ Pnle->Bits.Accessed = 0;
//
// Set the attributes (WT, CD, A) to 0.
@@ -210,6 +224,7 @@ PageTableLibSetPnle (
Update page table to map [LinearAddress, LinearAddress + Length) with specified attribute in the specified level.
@param[in] ParentPagingEntry The pointer to the page table entry to update.
+ @param[in] ParentAttribute The accumulated attribute of all parents' attribute.
@param[in] Modify FALSE to indicate Buffer is not used and BufferSize is increased by the required buffer size.
@param[in] Buffer The free buffer to be used for page table creation/updating.
When Modify is TRUE, it's used from the end.
@@ -232,6 +247,7 @@ PageTableLibSetPnle (
RETURN_STATUS
PageTableLibMapInLevel (
IN IA32_PAGING_ENTRY *ParentPagingEntry,
+ IN IA32_MAP_ATTRIBUTE *ParentAttribute,
IN BOOLEAN Modify,
IN VOID *Buffer,
IN OUT INTN *BufferSize,
@@ -259,6 +275,8 @@ PageTableLibMapInLevel (
IA32_MAP_ATTRIBUTE NopAttribute;
BOOLEAN CreateNew;
IA32_PAGING_ENTRY OneOfPagingEntry;
+ IA32_MAP_ATTRIBUTE ChildAttribute;
+ IA32_MAP_ATTRIBUTE ChildMask;
ASSERT (Level != 0);
ASSERT ((Attribute != NULL) && (Mask != NULL));
@@ -291,7 +309,7 @@ PageTableLibMapInLevel (
//
// Set default attribute bits for PML5E/PML4E/PDPTE/PDE.
//
- PageTableLibSetPnle (&ParentPagingEntry->Pnle, &NopAttribute);
+ PageTableLibSetPnle (&ParentPagingEntry->Pnle, &NopAttribute, &AllOneMask);
} else {
//
// Just make sure Present and MustBeZero (PageSize) bits are accurate.
@@ -307,7 +325,7 @@ PageTableLibMapInLevel (
// Use NOP attributes as the attribute of grand-parents because CPU will consider
// the actual attributes of grand-parents when determing the memory type.
//
- PleBAttribute.Uint64 = PageTableLibGetPleBMapAttribute (&ParentPagingEntry->PleB, &NopAttribute);
+ PleBAttribute.Uint64 = PageTableLibGetPleBMapAttribute (&ParentPagingEntry->PleB, ParentAttribute);
if ((IA32_MAP_ATTRIBUTE_ATTRIBUTES (&PleBAttribute) & IA32_MAP_ATTRIBUTE_ATTRIBUTES (Mask))
== (IA32_MAP_ATTRIBUTE_ATTRIBUTES (Attribute) & IA32_MAP_ATTRIBUTE_ATTRIBUTES (Mask)))
{
@@ -334,7 +352,7 @@ PageTableLibMapInLevel (
// Note: Should NOT inherit the attributes from the original entry because a zero RW bit
// will make the entire region read-only even the child entries set the RW bit.
//
- PageTableLibSetPnle (&ParentPagingEntry->Pnle, &NopAttribute);
+ PageTableLibSetPnle (&ParentPagingEntry->Pnle, &NopAttribute, &AllOneMask);
RegionLength = REGION_LENGTH (Level);
PagingEntry = (IA32_PAGING_ENTRY *)(UINTN)IA32_PNLE_PAGE_TABLE_BASE_ADDRESS (&ParentPagingEntry->Pnle);
@@ -343,6 +361,77 @@ PageTableLibMapInLevel (
SubOffset += RegionLength;
}
}
+ } else {
+ //
+ // It's a non-leaf entry
+ //
+ ChildAttribute.Uint64 = 0;
+ ChildMask.Uint64 = 0;
+
+ //
+ // If the inheritable attributes in the parent entry conflicts with the requested attributes,
+ // let the child entries take the parent attributes and
+ // loosen the attribute in the parent entry
+ // E.g.: when PDPTE[0].ReadWrite = 0 but caller wants to map [0-2MB] as ReadWrite = 1 (PDE[0].ReadWrite = 1)
+ // we need to change PDPTE[0].ReadWrite = 1 and let all PDE[0-255].ReadWrite = 0 in this step.
+ // when PDPTE[0].Nx = 1 but caller wants to map [0-2MB] as Nx = 0 (PDT[0].Nx = 0)
+ // we need to change PDPTE[0].Nx = 0 and let all PDE[0-255].Nx = 1 in this step.
+ if ((ParentPagingEntry->Pnle.Bits.Present == 0) && (Mask->Bits.Present == 1) && (Attribute->Bits.Present == 1)) {
+ if (Modify) {
+ ParentPagingEntry->Pnle.Bits.Present = 1;
+ }
+
+ ChildAttribute.Bits.Present = 0;
+ ChildMask.Bits.Present = 1;
+ }
+
+ if ((ParentPagingEntry->Pnle.Bits.ReadWrite == 0) && (Mask->Bits.ReadWrite == 1) && (Attribute->Bits.ReadWrite == 1)) {
+ if (Modify) {
+ ParentPagingEntry->Pnle.Bits.ReadWrite = 1;
+ }
+
+ ChildAttribute.Bits.ReadWrite = 0;
+ ChildMask.Bits.ReadWrite = 1;
+ }
+
+ if ((ParentPagingEntry->Pnle.Bits.UserSupervisor == 0) && (Mask->Bits.UserSupervisor == 1) && (Attribute->Bits.UserSupervisor == 1)) {
+ if (Modify) {
+ ParentPagingEntry->Pnle.Bits.UserSupervisor = 1;
+ }
+
+ ChildAttribute.Bits.UserSupervisor = 0;
+ ChildMask.Bits.UserSupervisor = 1;
+ }
+
+ if ((ParentPagingEntry->Pnle.Bits.Nx == 1) && (Mask->Bits.Nx == 1) && (Attribute->Bits.Nx == 0)) {
+ if (Modify) {
+ ParentPagingEntry->Pnle.Bits.Nx = 0;
+ }
+
+ ChildAttribute.Bits.Nx = 1;
+ ChildMask.Bits.Nx = 1;
+ }
+
+ if (ChildMask.Uint64 != 0) {
+ if (Modify) {
+ //
+ // Update child entries to use restrictive attribute inherited from parent.
+ // e.g.: Set PDE[0-255].ReadWrite = 0
+ //
+ PagingEntry = (IA32_PAGING_ENTRY *)(UINTN)IA32_PNLE_PAGE_TABLE_BASE_ADDRESS (&ParentPagingEntry->Pnle);
+ for (Index = 0; Index < 512; Index++) {
+ if (PagingEntry[Index].Pce.Present == 0) {
+ continue;
+ }
+
+ if (IsPle (&PagingEntry[Index], Level)) {
+ PageTableLibSetPle (Level - 1, &PagingEntry[Index], 0, &ChildAttribute, &ChildMask);
+ } else {
+ PageTableLibSetPnle (&PagingEntry[Index].Pnle, &ChildAttribute, &ChildMask);
+ }
+ }
+ }
+ }
}
//
@@ -355,6 +444,8 @@ PageTableLibMapInLevel (
RegionMask = RegionLength - 1;
RegionStart = (LinearAddress + Offset) & ~RegionMask;
+ ParentAttribute->Uint64 = PageTableLibGetPnleMapAttribute (&ParentPagingEntry->Pnle, ParentAttribute);
+
//
// Apply the attribute.
//
@@ -386,6 +477,7 @@ PageTableLibMapInLevel (
//
Status = PageTableLibMapInLevel (
CurrentPagingEntry,
+ ParentAttribute,
Modify,
Buffer,
BufferSize,
@@ -449,12 +541,13 @@ PageTableMap (
IN IA32_MAP_ATTRIBUTE *Mask
)
{
- RETURN_STATUS Status;
- IA32_PAGING_ENTRY TopPagingEntry;
- INTN RequiredSize;
- UINT64 MaxLinearAddress;
- UINTN MaxLevel;
- UINTN MaxLeafLevel;
+ RETURN_STATUS Status;
+ IA32_PAGING_ENTRY TopPagingEntry;
+ INTN RequiredSize;
+ UINT64 MaxLinearAddress;
+ UINTN MaxLevel;
+ UINTN MaxLeafLevel;
+ IA32_MAP_ATTRIBUTE ParentAttribute;
if ((PagingMode == Paging32bit) || (PagingMode == PagingPae) || (PagingMode >= PagingModeMax)) {
//
@@ -499,15 +592,26 @@ PageTableMap (
TopPagingEntry.Uintn = *PageTable;
if (TopPagingEntry.Uintn != 0) {
- TopPagingEntry.Pce.Present = 1;
+ TopPagingEntry.Pce.Present = 1;
+ TopPagingEntry.Pce.ReadWrite = 1;
+ TopPagingEntry.Pce.UserSupervisor = 1;
+ TopPagingEntry.Pce.Nx = 0;
}
+ ParentAttribute.Uint64 = 0;
+ ParentAttribute.Bits.PageTableBaseAddress = 1;
+ ParentAttribute.Bits.Present = 1;
+ ParentAttribute.Bits.ReadWrite = 1;
+ ParentAttribute.Bits.UserSupervisor = 1;
+ ParentAttribute.Bits.Nx = 0;
+
//
// Query the required buffer size without modifying the page table.
//
RequiredSize = 0;
Status = PageTableLibMapInLevel (
&TopPagingEntry,
+ &ParentAttribute,
FALSE,
NULL,
&RequiredSize,
@@ -537,8 +641,16 @@ PageTableMap (
//
// Update the page table when the supplied buffer is sufficient.
//
+ ParentAttribute.Uint64 = 0;
+ ParentAttribute.Bits.PageTableBaseAddress = 1;
+ ParentAttribute.Bits.Present = 1;
+ ParentAttribute.Bits.ReadWrite = 1;
+ ParentAttribute.Bits.UserSupervisor = 1;
+ ParentAttribute.Bits.Nx = 0;
+
Status = PageTableLibMapInLevel (
&TopPagingEntry,
+ &ParentAttribute,
TRUE,
Buffer,
BufferSize,
--
2.35.1.windows.2
next prev parent reply other threads:[~2022-07-18 13:18 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-07-18 13:18 [PATCH 00/10] UefiCpuPkg: Create CpuPageTableLib for manipulating X86 paging structs Ni, Ray
2022-07-18 13:18 ` [PATCH 01/10] " Ni, Ray
2022-07-18 13:49 ` [edk2-devel] " Gerd Hoffmann
2022-07-19 8:17 ` Ni, Ray
2022-08-15 16:23 ` Lendacky, Thomas
2022-08-16 2:25 ` Ni, Ray
2022-07-18 13:18 ` [PATCH 02/10] UefiCpuPkg/CpuPageTableLib: Return error on invalid parameters Ni, Ray
2022-07-18 13:18 ` [PATCH 03/10] CpuPageTableLib: Fix a bug when a bit is 1 in Attribute, 0 in Mask Ni, Ray
2022-07-18 13:18 ` [PATCH 04/10] CpuPageTableLib: Refactor the logic Ni, Ray
2022-07-18 13:18 ` [PATCH 05/10] CpuPageTableLib: Split the page entry when LA is aligned but PA is not Ni, Ray
2022-07-18 13:18 ` [PATCH 06/10] CpuPageTableLib: Avoid treating non-leaf entry as leaf one Ni, Ray
2022-07-18 13:18 ` Ni, Ray [this message]
2022-07-18 13:18 ` [PATCH 08/10] CpuPageTableLib: Fix a bug to avoid unnecessary changing to page table Ni, Ray
2022-07-18 13:18 ` [PATCH 09/10] CpuPageTableLib: Fix bug that wrongly requires extra size for mapping Ni, Ray
2022-07-18 13:18 ` [PATCH 10/10] CpuPageTableLib: define IA32_PAGE_LEVEL enum type internally Ni, Ray
2022-08-09 3:46 ` [edk2-devel] [PATCH 00/10] UefiCpuPkg: Create CpuPageTableLib for manipulating X86 paging structs Dong, Eric
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-list from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20220718131831.660-8-ray.ni@intel.com \
--to=devel@edk2.groups.io \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox