| /** @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; | |
| OneOfPagingEntry.Uint64 = 0; | |
| // | |
| // 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; | |
| } | |
| } 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; | |
| } |