Modify CpuPageTableLib code to enable PAE paging. In PageTableMap() API: When creating new PAE page table, after creating page table, set all MustBeZero fields of 4 PDPTE to 0. The MustBeZero fields are treated as RW and other attributes by the common map logic. So they might be set to 1. When updating exsiting PAE page table, the special steps are: 1.Prepare 4K-aligned 32bytes memory in stack for 4 temp PDPTE. 2.Copy original 4 PDPTE to the 4 temp PDPTE and set the RW, UserSupervisor to 1 and set Nx of 4 temp PDPTE to 0. 4.After updating the page table, set the MustBeZero fields of 4 temp PDPTE to 0. 5.Copy the temp PDPTE to original PDPTE. In PageTableParse() API, also create 4 temp PDPTE in stack. Copy original 4 PDPTE to the 4 temp PDPTE. Then set the RW, UserSupervisor to 1 and set Nx of 4 temp PDPTE to 0. Finally use the address of temp PDPTE as the page table address. Signed-off-by: Dun Tan <dun.tan@intel.com> Cc: Eric Dong <eric.dong@intel.com> Reviewed-by: Ray Ni <ray.ni@intel.com> Cc: Rahul Kumar <rahul1.kumar@intel.com> Tested-by: Gerd Hoffmann <kraxel@redhat.com> Acked-by: Gerd Hoffmann <kraxel@redhat.com>
		
			
				
	
	
		
			347 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			347 lines
		
	
	
		
			12 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"
 | 
						|
 | 
						|
/**
 | 
						|
  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 IA32_PAGE_LEAF_ENTRY_BIG_PAGESIZE.
 | 
						|
  //
 | 
						|
  MapAttribute.Uint64 = IA32_PLEB_PAGE_TABLE_BASE_ADDRESS (PleB);
 | 
						|
 | 
						|
  MapAttribute.Bits.Present        = ParentMapAttribute->Bits.Present & PleB->Bits.Present;
 | 
						|
  MapAttribute.Bits.ReadWrite      = ParentMapAttribute->Bits.ReadWrite & PleB->Bits.ReadWrite;
 | 
						|
  MapAttribute.Bits.UserSupervisor = ParentMapAttribute->Bits.UserSupervisor & PleB->Bits.UserSupervisor;
 | 
						|
  MapAttribute.Bits.Nx             = ParentMapAttribute->Bits.Nx | PleB->Bits.Nx;
 | 
						|
  MapAttribute.Bits.WriteThrough   = PleB->Bits.WriteThrough;
 | 
						|
  MapAttribute.Bits.CacheDisabled  = PleB->Bits.CacheDisabled;
 | 
						|
  MapAttribute.Bits.Accessed       = PleB->Bits.Accessed;
 | 
						|
 | 
						|
  MapAttribute.Bits.Pat           = PleB->Bits.Pat;
 | 
						|
  MapAttribute.Bits.Dirty         = PleB->Bits.Dirty;
 | 
						|
  MapAttribute.Bits.Global        = PleB->Bits.Global;
 | 
						|
  MapAttribute.Bits.ProtectionKey = 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 = IA32_PTE4K_PAGE_TABLE_BASE_ADDRESS (Pte4K);
 | 
						|
 | 
						|
  MapAttribute.Bits.Present        = ParentMapAttribute->Bits.Present & Pte4K->Bits.Present;
 | 
						|
  MapAttribute.Bits.ReadWrite      = ParentMapAttribute->Bits.ReadWrite & Pte4K->Bits.ReadWrite;
 | 
						|
  MapAttribute.Bits.UserSupervisor = ParentMapAttribute->Bits.UserSupervisor & Pte4K->Bits.UserSupervisor;
 | 
						|
  MapAttribute.Bits.Nx             = ParentMapAttribute->Bits.Nx | Pte4K->Bits.Nx;
 | 
						|
  MapAttribute.Bits.WriteThrough   = Pte4K->Bits.WriteThrough;
 | 
						|
  MapAttribute.Bits.CacheDisabled  = Pte4K->Bits.CacheDisabled;
 | 
						|
  MapAttribute.Bits.Accessed       = Pte4K->Bits.Accessed;
 | 
						|
 | 
						|
  MapAttribute.Bits.Pat           = Pte4K->Bits.Pat;
 | 
						|
  MapAttribute.Bits.Dirty         = Pte4K->Bits.Dirty;
 | 
						|
  MapAttribute.Bits.Global        = Pte4K->Bits.Global;
 | 
						|
  MapAttribute.Bits.ProtectionKey = 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 = Pnle->Uint64;
 | 
						|
 | 
						|
  MapAttribute.Bits.Present        = ParentMapAttribute->Bits.Present & Pnle->Bits.Present;
 | 
						|
  MapAttribute.Bits.ReadWrite      = ParentMapAttribute->Bits.ReadWrite & Pnle->Bits.ReadWrite;
 | 
						|
  MapAttribute.Bits.UserSupervisor = ParentMapAttribute->Bits.UserSupervisor & Pnle->Bits.UserSupervisor;
 | 
						|
  MapAttribute.Bits.Nx             = ParentMapAttribute->Bits.Nx | Pnle->Bits.Nx;
 | 
						|
  MapAttribute.Bits.WriteThrough   = Pnle->Bits.WriteThrough;
 | 
						|
  MapAttribute.Bits.CacheDisabled  = Pnle->Bits.CacheDisabled;
 | 
						|
  MapAttribute.Bits.Accessed       = 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 to 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 == 1) {
 | 
						|
    return TRUE;
 | 
						|
  }
 | 
						|
 | 
						|
  if (((Level == 3) || (Level == 2))) {
 | 
						|
    if (PagingEntry->PleB.Bits.MustBeOne == 1) {
 | 
						|
      return TRUE;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Recursively parse the non-leaf page table entries.
 | 
						|
 | 
						|
  @param[in]      PageTableBaseAddress The base address of the 512 non-leaf 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 region 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 actual number of entries in the Map.
 | 
						|
  @param[in]      MapCapacity          The maximum number of entries the Map can hold.
 | 
						|
  @param[in]      LastEntry            Pointer to last map entry.
 | 
						|
  @param[in]      OneEntry             Pointer to a library internal storage that holds one map entry.
 | 
						|
                                       It's used when Map array is used up.
 | 
						|
**/
 | 
						|
VOID
 | 
						|
PageTableLibParsePnle (
 | 
						|
  IN     UINT64              PageTableBaseAddress,
 | 
						|
  IN     UINTN               Level,
 | 
						|
  IN     UINTN               MaxLevel,
 | 
						|
  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;
 | 
						|
  UINTN               PagingEntryNumber;
 | 
						|
 | 
						|
  ASSERT (OneEntry != NULL);
 | 
						|
 | 
						|
  PagingEntry       = (IA32_PAGING_ENTRY *)(UINTN)PageTableBaseAddress;
 | 
						|
  RegionLength      = REGION_LENGTH (Level);
 | 
						|
  PagingEntryNumber = ((MaxLevel == 3) && (Level == 3)) ? MAX_PAE_PDPTE_NUM : 512;
 | 
						|
 | 
						|
  for (Index = 0; Index < PagingEntryNumber; Index++, RegionStart += RegionLength) {
 | 
						|
    if (PagingEntry[Index].Pce.Present == 0) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    if (IsPle (&PagingEntry[Index], Level)) {
 | 
						|
      ASSERT (Level == 1 || Level == 2 || Level == 3);
 | 
						|
 | 
						|
      if (Level == 1) {
 | 
						|
        MapAttribute.Uint64 = PageTableLibGetPte4KMapAttribute (&PagingEntry[Index].Pte4K, ParentMapAttribute);
 | 
						|
      } else {
 | 
						|
        MapAttribute.Uint64 = PageTableLibGetPleBMapAttribute (&PagingEntry[Index].PleB, ParentMapAttribute);
 | 
						|
      }
 | 
						|
 | 
						|
      if ((*LastEntry != NULL) &&
 | 
						|
          ((*LastEntry)->LinearAddress + (*LastEntry)->Length == RegionStart) &&
 | 
						|
          (IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS (&(*LastEntry)->Attribute) + (*LastEntry)->Length
 | 
						|
           == IA32_MAP_ATTRIBUTE_PAGE_TABLE_BASE_ADDRESS (&MapAttribute)) &&
 | 
						|
          (IA32_MAP_ATTRIBUTE_ATTRIBUTES (&(*LastEntry)->Attribute) == IA32_MAP_ATTRIBUTE_ATTRIBUTES (&MapAttribute))
 | 
						|
          )
 | 
						|
      {
 | 
						|
        //
 | 
						|
        // Extend LastEntry.
 | 
						|
        //
 | 
						|
        (*LastEntry)->Length += RegionLength;
 | 
						|
      } else {
 | 
						|
        if (*MapCount < MapCapacity) {
 | 
						|
          //
 | 
						|
          // LastEntry points to next map entry in the array.
 | 
						|
          //
 | 
						|
          *LastEntry = &Map[*MapCount];
 | 
						|
        } else {
 | 
						|
          //
 | 
						|
          // LastEntry points to library internal map entry.
 | 
						|
          //
 | 
						|
          *LastEntry = OneEntry;
 | 
						|
        }
 | 
						|
 | 
						|
        //
 | 
						|
        // Set LastEntry.
 | 
						|
        //
 | 
						|
        (*LastEntry)->LinearAddress    = RegionStart;
 | 
						|
        (*LastEntry)->Length           = RegionLength;
 | 
						|
        (*LastEntry)->Attribute.Uint64 = MapAttribute.Uint64;
 | 
						|
        (*MapCount)++;
 | 
						|
      }
 | 
						|
    } else {
 | 
						|
      MapAttribute.Uint64 = PageTableLibGetPnleMapAttribute (&PagingEntry[Index].Pnle, ParentMapAttribute);
 | 
						|
      PageTableLibParsePnle (
 | 
						|
        IA32_PNLE_PAGE_TABLE_BASE_ADDRESS (&PagingEntry[Index].Pnle),
 | 
						|
        Level - 1,
 | 
						|
        MaxLevel,
 | 
						|
        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 linear 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;
 | 
						|
  UINTN               Index;
 | 
						|
  IA32_PAGING_ENTRY   BufferInStack[MAX_PAE_PDPTE_NUM];
 | 
						|
 | 
						|
  if ((PagingMode == Paging32bit) || (PagingMode >= PagingModeMax)) {
 | 
						|
    //
 | 
						|
    // 32bit paging is never supported.
 | 
						|
    //
 | 
						|
    return RETURN_UNSUPPORTED;
 | 
						|
  }
 | 
						|
 | 
						|
  if (MapCount == NULL) {
 | 
						|
    return RETURN_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  if ((*MapCount != 0) && (Map == NULL)) {
 | 
						|
    return RETURN_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  if (PageTable == 0) {
 | 
						|
    *MapCount = 0;
 | 
						|
    return RETURN_SUCCESS;
 | 
						|
  }
 | 
						|
 | 
						|
  if (PagingMode == PagingPae) {
 | 
						|
    CopyMem (BufferInStack, (VOID *)PageTable, sizeof (BufferInStack));
 | 
						|
    for (Index = 0; Index < MAX_PAE_PDPTE_NUM; Index++) {
 | 
						|
      BufferInStack[Index].Pnle.Bits.ReadWrite      = 1;
 | 
						|
      BufferInStack[Index].Pnle.Bits.UserSupervisor = 1;
 | 
						|
      BufferInStack[Index].Pnle.Bits.Nx             = 0;
 | 
						|
    }
 | 
						|
 | 
						|
    PageTable = (UINTN)BufferInStack;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Page table layout is as below:
 | 
						|
  //
 | 
						|
  // [IA32_CR3]
 | 
						|
  //     |
 | 
						|
  //     |
 | 
						|
  //     V
 | 
						|
  // [IA32_PML5E]
 | 
						|
  // ...
 | 
						|
  // [IA32_PML5E] --> [IA32_PML4E]
 | 
						|
  //                  ...
 | 
						|
  //                  [IA32_PML4E] --> [IA32_PDPTE_1G] --> 1G aligned physical address
 | 
						|
  //                                   ...
 | 
						|
  //                                   [IA32_PDPTE] --> [IA32_PDE_2M] --> 2M aligned physical address
 | 
						|
  //                                                    ...
 | 
						|
  //                                                    [IA32_PDE] --> [IA32_PTE_4K]  --> 4K aligned physical address
 | 
						|
  //                                                                   ...
 | 
						|
  //                                                                   [IA32_PTE_4K]  --> 4K aligned physical address
 | 
						|
  //
 | 
						|
 | 
						|
  NopAttribute.Uint64              = 0;
 | 
						|
  NopAttribute.Bits.Present        = 1;
 | 
						|
  NopAttribute.Bits.ReadWrite      = 1;
 | 
						|
  NopAttribute.Bits.UserSupervisor = 1;
 | 
						|
 | 
						|
  MaxLevel    = (UINT8)(PagingMode >> 8);
 | 
						|
  MapCapacity = *MapCount;
 | 
						|
  *MapCount   = 0;
 | 
						|
  LastEntry   = NULL;
 | 
						|
  PageTableLibParsePnle ((UINT64)PageTable, MaxLevel, MaxLevel, 0, &NopAttribute, Map, MapCount, MapCapacity, &LastEntry, &OneEntry);
 | 
						|
 | 
						|
  if (*MapCount > MapCapacity) {
 | 
						|
    return RETURN_BUFFER_TOO_SMALL;
 | 
						|
  }
 | 
						|
 | 
						|
  return RETURN_SUCCESS;
 | 
						|
}
 |