mirror of https://github.com/acidanthera/audk.git
852 lines
33 KiB
C
852 lines
33 KiB
C
/** @file
|
|
This library implements CpuPageTableLib that are generic for IA32 family CPU.
|
|
|
|
Copyright (c) 2022 - 2023, Intel Corporation. All rights reserved.<BR>
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
|
|
**/
|
|
|
|
#include "CpuPageTable.h"
|
|
|
|
/**
|
|
Set the IA32_PTE_4K.
|
|
|
|
@param[in] Pte4K Pointer to IA32_PTE_4K.
|
|
@param[in] Offset The offset within the linear address range.
|
|
@param[in] Attribute The attribute of the linear address range.
|
|
All non-reserved fields in IA32_MAP_ATTRIBUTE are supported to set in the page table.
|
|
Page table entry is reset to 0 before set to the new attribute when a new physical base address is set.
|
|
@param[in] Mask The mask used for attribute. The corresponding field in Attribute is ignored if that in Mask is 0.
|
|
**/
|
|
VOID
|
|
PageTableLibSetPte4K (
|
|
IN IA32_PTE_4K *Pte4K,
|
|
IN UINT64 Offset,
|
|
IN IA32_MAP_ATTRIBUTE *Attribute,
|
|
IN IA32_MAP_ATTRIBUTE *Mask
|
|
)
|
|
{
|
|
if (Mask->Bits.PageTableBaseAddressLow || Mask->Bits.PageTableBaseAddressHigh) {
|
|
Pte4K->Uint64 = (IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS (Attribute) + Offset) | (Pte4K->Uint64 & ~IA32_PE_BASE_ADDRESS_MASK_40);
|
|
}
|
|
|
|
if (Mask->Bits.Present) {
|
|
Pte4K->Bits.Present = Attribute->Bits.Present;
|
|
}
|
|
|
|
if (Mask->Bits.ReadWrite) {
|
|
Pte4K->Bits.ReadWrite = Attribute->Bits.ReadWrite;
|
|
}
|
|
|
|
if (Mask->Bits.UserSupervisor) {
|
|
Pte4K->Bits.UserSupervisor = Attribute->Bits.UserSupervisor;
|
|
}
|
|
|
|
if (Mask->Bits.WriteThrough) {
|
|
Pte4K->Bits.WriteThrough = Attribute->Bits.WriteThrough;
|
|
}
|
|
|
|
if (Mask->Bits.CacheDisabled) {
|
|
Pte4K->Bits.CacheDisabled = Attribute->Bits.CacheDisabled;
|
|
}
|
|
|
|
if (Mask->Bits.Accessed) {
|
|
Pte4K->Bits.Accessed = Attribute->Bits.Accessed;
|
|
}
|
|
|
|
if (Mask->Bits.Dirty) {
|
|
Pte4K->Bits.Dirty = Attribute->Bits.Dirty;
|
|
}
|
|
|
|
if (Mask->Bits.Pat) {
|
|
Pte4K->Bits.Pat = Attribute->Bits.Pat;
|
|
}
|
|
|
|
if (Mask->Bits.Global) {
|
|
Pte4K->Bits.Global = Attribute->Bits.Global;
|
|
}
|
|
|
|
if (Mask->Bits.ProtectionKey) {
|
|
Pte4K->Bits.ProtectionKey = Attribute->Bits.ProtectionKey;
|
|
}
|
|
|
|
if (Mask->Bits.Nx) {
|
|
Pte4K->Bits.Nx = Attribute->Bits.Nx;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Set the IA32_PDPTE_1G or IA32_PDE_2M.
|
|
|
|
@param[in] PleB Pointer to PDPTE_1G or PDE_2M. Both share the same structure definition.
|
|
@param[in] Offset The offset within the linear address range.
|
|
@param[in] Attribute The attribute of the linear address range.
|
|
All non-reserved fields in IA32_MAP_ATTRIBUTE are supported to set in the page table.
|
|
Page table entry is reset to 0 before set to the new attribute when a new physical base address is set.
|
|
@param[in] Mask The mask used for attribute. The corresponding field in Attribute is ignored if that in Mask is 0.
|
|
**/
|
|
VOID
|
|
PageTableLibSetPleB (
|
|
IN IA32_PAGE_LEAF_ENTRY_BIG_PAGESIZE *PleB,
|
|
IN UINT64 Offset,
|
|
IN IA32_MAP_ATTRIBUTE *Attribute,
|
|
IN IA32_MAP_ATTRIBUTE *Mask
|
|
)
|
|
{
|
|
if (Mask->Bits.PageTableBaseAddressLow || Mask->Bits.PageTableBaseAddressHigh) {
|
|
PleB->Uint64 = (IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS (Attribute) + Offset) | (PleB->Uint64 & ~IA32_PE_BASE_ADDRESS_MASK_39);
|
|
}
|
|
|
|
PleB->Bits.MustBeOne = 1;
|
|
|
|
if (Mask->Bits.Present) {
|
|
PleB->Bits.Present = Attribute->Bits.Present;
|
|
}
|
|
|
|
if (Mask->Bits.ReadWrite) {
|
|
PleB->Bits.ReadWrite = Attribute->Bits.ReadWrite;
|
|
}
|
|
|
|
if (Mask->Bits.UserSupervisor) {
|
|
PleB->Bits.UserSupervisor = Attribute->Bits.UserSupervisor;
|
|
}
|
|
|
|
if (Mask->Bits.WriteThrough) {
|
|
PleB->Bits.WriteThrough = Attribute->Bits.WriteThrough;
|
|
}
|
|
|
|
if (Mask->Bits.CacheDisabled) {
|
|
PleB->Bits.CacheDisabled = Attribute->Bits.CacheDisabled;
|
|
}
|
|
|
|
if (Mask->Bits.Accessed) {
|
|
PleB->Bits.Accessed = Attribute->Bits.Accessed;
|
|
}
|
|
|
|
if (Mask->Bits.Dirty) {
|
|
PleB->Bits.Dirty = Attribute->Bits.Dirty;
|
|
}
|
|
|
|
if (Mask->Bits.Pat) {
|
|
PleB->Bits.Pat = Attribute->Bits.Pat;
|
|
}
|
|
|
|
if (Mask->Bits.Global) {
|
|
PleB->Bits.Global = Attribute->Bits.Global;
|
|
}
|
|
|
|
if (Mask->Bits.ProtectionKey) {
|
|
PleB->Bits.ProtectionKey = Attribute->Bits.ProtectionKey;
|
|
}
|
|
|
|
if (Mask->Bits.Nx) {
|
|
PleB->Bits.Nx = Attribute->Bits.Nx;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Set the IA32_PDPTE_1G, IA32_PDE_2M or IA32_PTE_4K.
|
|
|
|
@param[in] Level 3, 2 or 1.
|
|
@param[in] Ple Pointer to PDPTE_1G, PDE_2M or IA32_PTE_4K, depending on the Level.
|
|
@param[in] Offset The offset within the linear address range.
|
|
@param[in] Attribute The attribute of the linear address range.
|
|
All non-reserved fields in IA32_MAP_ATTRIBUTE are supported to set in the page table.
|
|
Page table entry is reset to 0 before set to the new attribute when a new physical base address is set.
|
|
@param[in] Mask The mask used for attribute. The corresponding field in Attribute is ignored if that in Mask is 0.
|
|
**/
|
|
VOID
|
|
PageTableLibSetPle (
|
|
IN UINTN Level,
|
|
IN IA32_PAGING_ENTRY *Ple,
|
|
IN UINT64 Offset,
|
|
IN IA32_MAP_ATTRIBUTE *Attribute,
|
|
IN IA32_MAP_ATTRIBUTE *Mask
|
|
)
|
|
{
|
|
if (Level == 1) {
|
|
PageTableLibSetPte4K (&Ple->Pte4K, Offset, Attribute, Mask);
|
|
} else {
|
|
ASSERT (Level == 2 || Level == 3);
|
|
PageTableLibSetPleB (&Ple->PleB, Offset, Attribute, Mask);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Set the IA32_PML5, IA32_PML4, IA32_PDPTE or IA32_PDE.
|
|
|
|
@param[in] Pnle Pointer to IA32_PML5, IA32_PML4, IA32_PDPTE or IA32_PDE. All share the same structure definition.
|
|
@param[in] Attribute The attribute of the page directory referenced by the non-leaf.
|
|
@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 *Mask
|
|
)
|
|
{
|
|
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;
|
|
Pnle->Bits.MustBeZero = 0;
|
|
|
|
//
|
|
// Set the attributes (WT, CD, A) to 0.
|
|
// WT and CD determin the memory type used to access the 4K page directory referenced by this entry.
|
|
// So, it implictly requires PAT[0] is Write Back.
|
|
// Create a new parameter if caller requires to use a different memory type for accessing page directories.
|
|
//
|
|
Pnle->Bits.WriteThrough = 0;
|
|
Pnle->Bits.CacheDisabled = 0;
|
|
}
|
|
|
|
/**
|
|
Check if the combination for Attribute and Mask is valid for non-present entry.
|
|
1.Mask.Present is 0 but some other attributes is provided. This case should be invalid.
|
|
2.Map non-present range to present. In this case, all attributes should be provided.
|
|
|
|
@param[in] Attribute The attribute of the linear address range.
|
|
@param[in] Mask The mask used for attribute to check.
|
|
|
|
@retval RETURN_INVALID_PARAMETER For non-present range, Mask->Bits.Present is 0 but some other attributes are provided.
|
|
@retval RETURN_INVALID_PARAMETER For non-present range, Mask->Bits.Present is 1, Attribute->Bits.Present is 1 but some other attributes are not provided.
|
|
@retval RETURN_SUCCESS The combination for Attribute and Mask is valid.
|
|
**/
|
|
RETURN_STATUS
|
|
IsAttributesAndMaskValidForNonPresentEntry (
|
|
IN IA32_MAP_ATTRIBUTE *Attribute,
|
|
IN IA32_MAP_ATTRIBUTE *Mask
|
|
)
|
|
{
|
|
if ((Mask->Bits.Present == 1) && (Attribute->Bits.Present == 1)) {
|
|
//
|
|
// Creating new page table or remapping non-present range to present.
|
|
//
|
|
if ((Mask->Bits.ReadWrite == 0) || (Mask->Bits.UserSupervisor == 0) || (Mask->Bits.WriteThrough == 0) || (Mask->Bits.CacheDisabled == 0) ||
|
|
(Mask->Bits.Accessed == 0) || (Mask->Bits.Dirty == 0) || (Mask->Bits.Pat == 0) || (Mask->Bits.Global == 0) ||
|
|
((Mask->Bits.PageTableBaseAddressLow == 0) && (Mask->Bits.PageTableBaseAddressHigh == 0)) || (Mask->Bits.ProtectionKey == 0) || (Mask->Bits.Nx == 0))
|
|
{
|
|
return RETURN_INVALID_PARAMETER;
|
|
}
|
|
} else if ((Mask->Bits.Present == 0) && (Mask->Uint64 > 1)) {
|
|
//
|
|
// Only change other attributes for non-present range is not permitted.
|
|
//
|
|
return RETURN_INVALID_PARAMETER;
|
|
}
|
|
|
|
return RETURN_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
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.
|
|
When Modify is FALSE, it's ignored.
|
|
@param[in, out] BufferSize The available buffer size.
|
|
Return the remaining buffer size.
|
|
@param[in] Level Page table level. Could be 5, 4, 3, 2, or 1.
|
|
@param[in] MaxLeafLevel Maximum level that can be a leaf entry. Could be 1, 2 or 3 (if Page 1G is supported).
|
|
@param[in] LinearAddress The start of the linear address range.
|
|
@param[in] Length The length of the linear address range.
|
|
@param[in] Offset The offset within the linear address range.
|
|
@param[in] Attribute The attribute of the linear address range.
|
|
All non-reserved fields in IA32_MAP_ATTRIBUTE are supported to set in the page table.
|
|
Page table entries that map the linear address range are reset to 0 before set to the new attribute
|
|
when a new physical base address is set.
|
|
@param[in] Mask The mask used for attribute. The corresponding field in Attribute is ignored if that in Mask is 0.
|
|
@param[out] IsModified TRUE means page table is modified. FALSE means page table is not modified.
|
|
|
|
@retval RETURN_INVALID_PARAMETER For non-present range, Mask->Bits.Present is 0 but some other attributes are provided.
|
|
@retval RETURN_INVALID_PARAMETER For non-present range, Mask->Bits.Present is 1, Attribute->Bits.Present is 1 but some other attributes are not provided.
|
|
@retval RETURN_SUCCESS PageTable is created/updated successfully.
|
|
**/
|
|
RETURN_STATUS
|
|
PageTableLibMapInLevel (
|
|
IN IA32_PAGING_ENTRY *ParentPagingEntry,
|
|
IN IA32_MAP_ATTRIBUTE *ParentAttribute,
|
|
IN BOOLEAN Modify,
|
|
IN VOID *Buffer,
|
|
IN OUT INTN *BufferSize,
|
|
IN IA32_PAGE_LEVEL Level,
|
|
IN IA32_PAGE_LEVEL MaxLeafLevel,
|
|
IN UINT64 LinearAddress,
|
|
IN UINT64 Length,
|
|
IN UINT64 Offset,
|
|
IN IA32_MAP_ATTRIBUTE *Attribute,
|
|
IN IA32_MAP_ATTRIBUTE *Mask,
|
|
OUT BOOLEAN *IsModified
|
|
)
|
|
{
|
|
RETURN_STATUS Status;
|
|
UINTN BitStart;
|
|
UINTN Index;
|
|
IA32_PAGING_ENTRY *PagingEntry;
|
|
UINTN PagingEntryIndex;
|
|
UINTN PagingEntryIndexEnd;
|
|
IA32_PAGING_ENTRY *CurrentPagingEntry;
|
|
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;
|
|
IA32_MAP_ATTRIBUTE ChildAttribute;
|
|
IA32_MAP_ATTRIBUTE ChildMask;
|
|
IA32_MAP_ATTRIBUTE CurrentMask;
|
|
IA32_MAP_ATTRIBUTE LocalParentAttribute;
|
|
UINT64 PhysicalAddrInEntry;
|
|
UINT64 PhysicalAddrInAttr;
|
|
IA32_PAGING_ENTRY OriginalParentPagingEntry;
|
|
IA32_PAGING_ENTRY OriginalCurrentPagingEntry;
|
|
|
|
ASSERT (Level != 0);
|
|
ASSERT ((Attribute != NULL) && (Mask != NULL));
|
|
|
|
CreateNew = FALSE;
|
|
AllOneMask.Uint64 = ~0ull;
|
|
|
|
NopAttribute.Uint64 = 0;
|
|
NopAttribute.Bits.Present = 1;
|
|
NopAttribute.Bits.ReadWrite = 1;
|
|
NopAttribute.Bits.UserSupervisor = 1;
|
|
|
|
LocalParentAttribute.Uint64 = ParentAttribute->Uint64;
|
|
ParentAttribute = &LocalParentAttribute;
|
|
|
|
OriginalParentPagingEntry.Uint64 = ParentPagingEntry->Uint64;
|
|
|
|
//
|
|
// RegionLength: 256T (1 << 48) 512G (1 << 39), 1G (1 << 30), 2M (1 << 21) or 4K (1 << 12).
|
|
//
|
|
BitStart = 12 + (Level - 1) * 9;
|
|
PagingEntryIndex = (UINTN)BitFieldRead64 (LinearAddress + Offset, BitStart, BitStart + 9 - 1);
|
|
RegionLength = REGION_LENGTH (Level);
|
|
RegionMask = RegionLength - 1;
|
|
|
|
//
|
|
// ParentPagingEntry ONLY is deferenced for checking Present and MustBeOne bits
|
|
// when Modify is FALSE.
|
|
//
|
|
if ((ParentPagingEntry->Pce.Present == 0) || IsPle (ParentPagingEntry, Level + 1)) {
|
|
//
|
|
// When ParentPagingEntry is non-present, parent entry is CR3 or PML5E/PML4E/PDPTE/PDE.
|
|
// It does NOT point to an existing page directory.
|
|
// When ParentPagingEntry is present, parent entry is leaf PDPTE_1G or PDE_2M. Split to 2M or 4K pages.
|
|
// Note: it's impossible the parent entry is a PTE_4K.
|
|
//
|
|
PleBAttribute.Uint64 = PageTableLibGetPleBMapAttribute (&ParentPagingEntry->PleB, ParentAttribute);
|
|
if (ParentPagingEntry->Pce.Present == 0) {
|
|
//
|
|
// [LinearAddress, LinearAddress + Length] contains non-present range.
|
|
//
|
|
Status = IsAttributesAndMaskValidForNonPresentEntry (Attribute, Mask);
|
|
if (RETURN_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
OneOfPagingEntry.Pnle.Uint64 = 0;
|
|
} else {
|
|
PageTableLibSetPle (Level, &OneOfPagingEntry, 0, &PleBAttribute, &AllOneMask);
|
|
}
|
|
|
|
//
|
|
// Check if the attribute, the physical address calculated by ParentPagingEntry is equal to
|
|
// the attribute, the physical address calculated by input Attribue and Mask.
|
|
//
|
|
if ((IA32_MAP_ATTRIBUTE_ATTRIBUTES (&PleBAttribute) & IA32_MAP_ATTRIBUTE_ATTRIBUTES (Mask))
|
|
== (IA32_MAP_ATTRIBUTE_ATTRIBUTES (Attribute) & IA32_MAP_ATTRIBUTE_ATTRIBUTES (Mask)))
|
|
{
|
|
if ((Mask->Bits.PageTableBaseAddressLow == 0) && (Mask->Bits.PageTableBaseAddressHigh == 0)) {
|
|
return RETURN_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Non-present entry won't reach there since:
|
|
// 1.When map non-present entry to present, the attribute must be different.
|
|
// 2.When still map non-present entry to non-present, PageTableBaseAddressLow and High in Mask must be 0.
|
|
//
|
|
ASSERT (ParentPagingEntry->Pce.Present == 1);
|
|
PhysicalAddrInEntry = IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS (&PleBAttribute) + MultU64x32 (RegionLength, (UINT32)PagingEntryIndex);
|
|
PhysicalAddrInAttr = (IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS (Attribute) + Offset) & (~RegionMask);
|
|
if (PhysicalAddrInEntry == PhysicalAddrInAttr) {
|
|
return RETURN_SUCCESS;
|
|
}
|
|
}
|
|
|
|
ASSERT (Buffer == NULL || *BufferSize >= SIZE_4KB);
|
|
CreateNew = TRUE;
|
|
*BufferSize -= SIZE_4KB;
|
|
|
|
if (Modify) {
|
|
PagingEntry = (IA32_PAGING_ENTRY *)((UINTN)Buffer + *BufferSize);
|
|
ZeroMem (PagingEntry, SIZE_4KB);
|
|
|
|
if (ParentPagingEntry->Pce.Present) {
|
|
//
|
|
// Create 512 child-level entries that map to 2M/4K.
|
|
//
|
|
for (SubOffset = 0, Index = 0; Index < 512; Index++) {
|
|
PagingEntry[Index].Uint64 = OneOfPagingEntry.Uint64 + SubOffset;
|
|
SubOffset += RegionLength;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set NOP attributes
|
|
// Note: Should NOT inherit the attributes from the original entry because a zero RW bit
|
|
// will make the entire region read-only even the child entries set the RW bit.
|
|
//
|
|
// Non-leaf entry doesn't have PAT bit. So use ~IA32_PE_BASE_ADDRESS_MASK_40 is to make sure PAT bit
|
|
// (bit12) in original big-leaf entry is not assigned to PageTableBaseAddress field of non-leaf entry.
|
|
//
|
|
PageTableLibSetPnle (&ParentPagingEntry->Pnle, &NopAttribute, &AllOneMask);
|
|
ParentPagingEntry->Uint64 = ((UINTN)(VOID *)PagingEntry) | (ParentPagingEntry->Uint64 & (~IA32_PE_BASE_ADDRESS_MASK_40));
|
|
}
|
|
} else {
|
|
//
|
|
// If (LinearAddress + Length - 1) is not in the same ParentPagingEntry with (LinearAddress + Offset), then the remaining child PagingEntry
|
|
// starting from PagingEntryIndex of ParentPagingEntry is all covered by [LinearAddress + Offset, LinearAddress + Length - 1].
|
|
//
|
|
PagingEntryIndexEnd = (BitFieldRead64 (LinearAddress + Length - 1, BitStart + 9, 63) != BitFieldRead64 (LinearAddress + Offset, BitStart + 9, 63)) ? 511 :
|
|
(UINTN)BitFieldRead64 (LinearAddress + Length - 1, BitStart, BitStart + 9 - 1);
|
|
PagingEntry = (IA32_PAGING_ENTRY *)(UINTN)IA32_PNLE_PAGE_TABLE_BASE_ADDRESS (&ParentPagingEntry->Pnle);
|
|
for (Index = PagingEntryIndex; Index <= PagingEntryIndexEnd; Index++) {
|
|
if (PagingEntry[Index].Pce.Present == 0) {
|
|
//
|
|
// [LinearAddress, LinearAddress + Length] contains non-present range.
|
|
//
|
|
Status = IsAttributesAndMaskValidForNonPresentEntry (Attribute, Mask);
|
|
if (RETURN_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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.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
|
|
//
|
|
for (Index = 0; Index < 512; Index++) {
|
|
if (PagingEntry[Index].Pce.Present == 0) {
|
|
continue;
|
|
}
|
|
|
|
if (IsPle (&PagingEntry[Index], Level)) {
|
|
PageTableLibSetPle (Level, &PagingEntry[Index], 0, &ChildAttribute, &ChildMask);
|
|
} else {
|
|
PageTableLibSetPnle (&PagingEntry[Index].Pnle, &ChildAttribute, &ChildMask);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// RegionStart: points to the linear address that's aligned on RegionLength and lower than (LinearAddress + Offset).
|
|
//
|
|
Index = PagingEntryIndex;
|
|
RegionStart = (LinearAddress + Offset) & ~RegionMask;
|
|
ParentAttribute->Uint64 = PageTableLibGetPnleMapAttribute (&ParentPagingEntry->Pnle, ParentAttribute);
|
|
|
|
//
|
|
// Apply the attribute.
|
|
//
|
|
PagingEntry = (IA32_PAGING_ENTRY *)(UINTN)IA32_PNLE_PAGE_TABLE_BASE_ADDRESS (&ParentPagingEntry->Pnle);
|
|
while (Offset < Length && Index < 512) {
|
|
CurrentPagingEntry = (!Modify && CreateNew) ? &OneOfPagingEntry : &PagingEntry[Index];
|
|
SubLength = MIN (Length - Offset, RegionStart + RegionLength - (LinearAddress + Offset));
|
|
if ((Level <= MaxLeafLevel) &&
|
|
(((LinearAddress + Offset) & RegionMask) == 0) &&
|
|
(((IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS (Attribute) + Offset) & RegionMask) == 0) &&
|
|
(SubLength == RegionLength) &&
|
|
((CurrentPagingEntry->Pce.Present == 0) || IsPle (CurrentPagingEntry, Level))
|
|
)
|
|
{
|
|
//
|
|
// Create one entry mapping the entire region (1G, 2M or 4K).
|
|
//
|
|
if (Modify) {
|
|
//
|
|
// When the inheritable attributes in parent entry could override the child attributes,
|
|
// e.g.: Present/ReadWrite/UserSupervisor is 0 in parent entry, or
|
|
// Nx is 1 in parent entry,
|
|
// we just skip setting any value to these attributes in child.
|
|
// We add assertion to make sure the requested settings don't conflict with parent attributes in this case.
|
|
//
|
|
CurrentMask.Uint64 = Mask->Uint64;
|
|
if (ParentAttribute->Bits.Present == 0) {
|
|
CurrentMask.Bits.Present = 0;
|
|
ASSERT (CreateNew || (Mask->Bits.Present == 0) || (Attribute->Bits.Present == 0));
|
|
}
|
|
|
|
if (ParentAttribute->Bits.ReadWrite == 0) {
|
|
CurrentMask.Bits.ReadWrite = 0;
|
|
ASSERT (CreateNew || (Mask->Bits.ReadWrite == 0) || (Attribute->Bits.ReadWrite == 0));
|
|
}
|
|
|
|
if (ParentAttribute->Bits.UserSupervisor == 0) {
|
|
CurrentMask.Bits.UserSupervisor = 0;
|
|
ASSERT (CreateNew || (Mask->Bits.UserSupervisor == 0) || (Attribute->Bits.UserSupervisor == 0));
|
|
}
|
|
|
|
if (ParentAttribute->Bits.Nx == 1) {
|
|
CurrentMask.Bits.Nx = 0;
|
|
ASSERT (CreateNew || (Mask->Bits.Nx == 0) || (Attribute->Bits.Nx == 1));
|
|
}
|
|
|
|
//
|
|
// Check if any leaf PagingEntry is modified.
|
|
//
|
|
OriginalCurrentPagingEntry.Uint64 = CurrentPagingEntry->Uint64;
|
|
PageTableLibSetPle (Level, CurrentPagingEntry, Offset, Attribute, &CurrentMask);
|
|
|
|
if (OriginalCurrentPagingEntry.Uint64 != CurrentPagingEntry->Uint64) {
|
|
*IsModified = TRUE;
|
|
}
|
|
}
|
|
} else {
|
|
//
|
|
// Recursively call to create page table.
|
|
// There are 3 cases:
|
|
// a. Level cannot be a leaf entry which points to physical memory.
|
|
// a. Level can be a leaf entry but (LinearAddress + Offset) is NOT aligned on the RegionStart.
|
|
// b. Level can be a leaf entry and (LinearAddress + Offset) is aligned on RegionStart,
|
|
// but the length is SMALLER than the RegionLength.
|
|
//
|
|
Status = PageTableLibMapInLevel (
|
|
CurrentPagingEntry,
|
|
ParentAttribute,
|
|
Modify,
|
|
Buffer,
|
|
BufferSize,
|
|
Level - 1,
|
|
MaxLeafLevel,
|
|
LinearAddress,
|
|
Length,
|
|
Offset,
|
|
Attribute,
|
|
Mask,
|
|
IsModified
|
|
);
|
|
if (RETURN_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
Offset += SubLength;
|
|
RegionStart += RegionLength;
|
|
Index++;
|
|
}
|
|
|
|
//
|
|
// Check if ParentPagingEntry entry is modified here is enough. Except the changes happen in leaf PagingEntry during
|
|
// the while loop, if there is any other change happens in page table, the ParentPagingEntry must has been modified.
|
|
//
|
|
if (OriginalParentPagingEntry.Uint64 != ParentPagingEntry->Uint64) {
|
|
*IsModified = TRUE;
|
|
}
|
|
|
|
return RETURN_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Create or update page table to map [LinearAddress, LinearAddress + Length) with specified attribute.
|
|
|
|
@param[in, out] PageTable The pointer to the page table to update, or pointer to NULL if a new page table is to be created.
|
|
@param[in] PagingMode The paging mode.
|
|
@param[in] Buffer The free buffer to be used for page table creation/updating.
|
|
@param[in, out] BufferSize The buffer size.
|
|
On return, the remaining buffer size.
|
|
The free buffer is used from the end so caller can supply the same Buffer pointer with an updated
|
|
BufferSize in the second call to this API.
|
|
@param[in] LinearAddress The start of the linear address range.
|
|
@param[in] Length The length of the linear address range.
|
|
@param[in] Attribute The attribute of the linear address range.
|
|
All non-reserved fields in IA32_MAP_ATTRIBUTE are supported to set in the page table.
|
|
Page table entries that map the linear address range are reset to 0 before set to the new attribute
|
|
when a new physical base address is set.
|
|
@param[in] Mask The mask used for attribute. The corresponding field in Attribute is ignored if that in Mask is 0.
|
|
@param[out] IsModified TRUE means page table is modified. FALSE means page table is not modified.
|
|
|
|
@retval RETURN_UNSUPPORTED PagingMode is not supported.
|
|
@retval RETURN_INVALID_PARAMETER PageTable, BufferSize, Attribute or Mask is NULL.
|
|
@retval RETURN_INVALID_PARAMETER For non-present range, Mask->Bits.Present is 0 but some other attributes are provided.
|
|
@retval RETURN_INVALID_PARAMETER For non-present range, Mask->Bits.Present is 1, Attribute->Bits.Present is 1 but some other attributes are not provided.
|
|
@retval RETURN_INVALID_PARAMETER For non-present range, Mask->Bits.Present is 1, Attribute->Bits.Present is 0 but some other attributes are provided.
|
|
@retval RETURN_INVALID_PARAMETER For present range, Mask->Bits.Present is 1, Attribute->Bits.Present is 0 but some other attributes are provided.
|
|
@retval RETURN_INVALID_PARAMETER *BufferSize is not multiple of 4KB.
|
|
@retval RETURN_BUFFER_TOO_SMALL The buffer is too small for page table creation/updating.
|
|
BufferSize is updated to indicate the expected buffer size.
|
|
Caller may still get RETURN_BUFFER_TOO_SMALL with the new BufferSize.
|
|
@retval RETURN_SUCCESS PageTable is created/updated successfully or the input Length is 0.
|
|
**/
|
|
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,
|
|
OUT BOOLEAN *IsModified OPTIONAL
|
|
)
|
|
{
|
|
RETURN_STATUS Status;
|
|
IA32_PAGING_ENTRY TopPagingEntry;
|
|
INTN RequiredSize;
|
|
UINT64 MaxLinearAddress;
|
|
IA32_PAGE_LEVEL MaxLevel;
|
|
IA32_PAGE_LEVEL MaxLeafLevel;
|
|
IA32_MAP_ATTRIBUTE ParentAttribute;
|
|
BOOLEAN LocalIsModified;
|
|
UINTN Index;
|
|
IA32_PAGING_ENTRY *PagingEntry;
|
|
UINT8 BufferInStack[SIZE_4KB - 1 + MAX_PAE_PDPTE_NUM * sizeof (IA32_PAGING_ENTRY)];
|
|
|
|
if (Length == 0) {
|
|
return RETURN_SUCCESS;
|
|
}
|
|
|
|
if ((PagingMode == Paging32bit) || (PagingMode >= PagingModeMax)) {
|
|
//
|
|
// 32bit paging is never supported.
|
|
//
|
|
return RETURN_UNSUPPORTED;
|
|
}
|
|
|
|
if ((PageTable == NULL) || (BufferSize == NULL) || (Attribute == NULL) || (Mask == NULL)) {
|
|
return RETURN_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (*BufferSize % SIZE_4KB != 0) {
|
|
//
|
|
// BufferSize should be multiple of 4K.
|
|
//
|
|
return RETURN_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (((UINTN)LinearAddress % SIZE_4KB != 0) || ((UINTN)Length % SIZE_4KB != 0)) {
|
|
//
|
|
// LinearAddress and Length should be multiple of 4K.
|
|
//
|
|
return RETURN_INVALID_PARAMETER;
|
|
}
|
|
|
|
if ((*BufferSize != 0) && (Buffer == NULL)) {
|
|
return RETURN_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// If to map [LinearAddress, LinearAddress + Length] as non-present,
|
|
// all attributes except Present should not be provided.
|
|
//
|
|
if ((Attribute->Bits.Present == 0) && (Mask->Bits.Present == 1) && (Mask->Uint64 > 1)) {
|
|
return RETURN_INVALID_PARAMETER;
|
|
}
|
|
|
|
MaxLeafLevel = (IA32_PAGE_LEVEL)(UINT8)PagingMode;
|
|
MaxLevel = (IA32_PAGE_LEVEL)(UINT8)(PagingMode >> 8);
|
|
MaxLinearAddress = (PagingMode == PagingPae) ? LShiftU64 (1, 32) : LShiftU64 (1, 12 + MaxLevel * 9);
|
|
|
|
if ((LinearAddress > MaxLinearAddress) || (Length > MaxLinearAddress - LinearAddress)) {
|
|
//
|
|
// Maximum linear address is (1 << 32), (1 << 48) or (1 << 57)
|
|
//
|
|
return RETURN_INVALID_PARAMETER;
|
|
}
|
|
|
|
TopPagingEntry.Uintn = *PageTable;
|
|
if (TopPagingEntry.Uintn != 0) {
|
|
if (PagingMode == PagingPae) {
|
|
//
|
|
// Create 4 temporary PDPTE at a 4k-aligned address.
|
|
// Copy the original PDPTE content and set ReadWrite, UserSupervisor to 1, set Nx to 0.
|
|
//
|
|
TopPagingEntry.Uintn = ALIGN_VALUE ((UINTN)BufferInStack, BASE_4KB);
|
|
PagingEntry = (IA32_PAGING_ENTRY *)(TopPagingEntry.Uintn);
|
|
CopyMem (PagingEntry, (VOID *)(*PageTable), MAX_PAE_PDPTE_NUM * sizeof (IA32_PAGING_ENTRY));
|
|
for (Index = 0; Index < MAX_PAE_PDPTE_NUM; Index++) {
|
|
PagingEntry[Index].Pnle.Bits.ReadWrite = 1;
|
|
PagingEntry[Index].Pnle.Bits.UserSupervisor = 1;
|
|
PagingEntry[Index].Pnle.Bits.Nx = 0;
|
|
}
|
|
}
|
|
|
|
TopPagingEntry.Pce.Present = 1;
|
|
TopPagingEntry.Pce.ReadWrite = 1;
|
|
TopPagingEntry.Pce.UserSupervisor = 1;
|
|
TopPagingEntry.Pce.Nx = 0;
|
|
}
|
|
|
|
if (IsModified == NULL) {
|
|
IsModified = &LocalIsModified;
|
|
}
|
|
|
|
*IsModified = FALSE;
|
|
|
|
ParentAttribute.Uint64 = 0;
|
|
ParentAttribute.Bits.PageTableBaseAddressLow = 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,
|
|
MaxLevel,
|
|
MaxLeafLevel,
|
|
LinearAddress,
|
|
Length,
|
|
0,
|
|
Attribute,
|
|
Mask,
|
|
IsModified
|
|
);
|
|
ASSERT (*IsModified == FALSE);
|
|
if (RETURN_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
RequiredSize = -RequiredSize;
|
|
|
|
if ((UINTN)RequiredSize > *BufferSize) {
|
|
*BufferSize = RequiredSize;
|
|
return RETURN_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
if ((RequiredSize != 0) && (Buffer == NULL)) {
|
|
return RETURN_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Update the page table when the supplied buffer is sufficient.
|
|
//
|
|
Status = PageTableLibMapInLevel (
|
|
&TopPagingEntry,
|
|
&ParentAttribute,
|
|
TRUE,
|
|
Buffer,
|
|
(INTN *)BufferSize,
|
|
MaxLevel,
|
|
MaxLeafLevel,
|
|
LinearAddress,
|
|
Length,
|
|
0,
|
|
Attribute,
|
|
Mask,
|
|
IsModified
|
|
);
|
|
|
|
if (!RETURN_ERROR (Status)) {
|
|
PagingEntry = (IA32_PAGING_ENTRY *)(UINTN)(TopPagingEntry.Uintn & IA32_PE_BASE_ADDRESS_MASK_40);
|
|
|
|
if (PagingMode == PagingPae) {
|
|
//
|
|
// These MustBeZero fields are treated as RW and other attributes by the common map logic. So they might be set to 1.
|
|
//
|
|
for (Index = 0; Index < MAX_PAE_PDPTE_NUM; Index++) {
|
|
PagingEntry[Index].PdptePae.Bits.MustBeZero = 0;
|
|
PagingEntry[Index].PdptePae.Bits.MustBeZero2 = 0;
|
|
PagingEntry[Index].PdptePae.Bits.MustBeZero3 = 0;
|
|
}
|
|
|
|
if (*PageTable != 0) {
|
|
//
|
|
// Copy temp PDPTE to original PDPTE.
|
|
//
|
|
CopyMem ((VOID *)(*PageTable), PagingEntry, MAX_PAE_PDPTE_NUM * sizeof (IA32_PAGING_ENTRY));
|
|
}
|
|
}
|
|
|
|
if (*PageTable == 0) {
|
|
//
|
|
// Do not assign the *PageTable when it's an existing page table.
|
|
// If it's an existing PAE page table, PagingEntry is the temp buffer in stack.
|
|
//
|
|
*PageTable = (UINTN)PagingEntry;
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|