/** @file
Copyright (c) 2020, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include
#include
#include
#include
#include
#define PDE_PG_LVL 2
#define PDP_PG_LVL 3
/**
Get shift bit number from address to page index
@param[in] Level Page level.
@retval Bit to shift to get specific page level index.
**/
UINT32
GetPageShiftBits (
IN UINT8 Level
)
{
if (Level == PDE_PG_LVL) {
return IsLongModeEnabled() ? 21 : 22;
} else {
return IsLongModeEnabled() ? 30 : 31;
}
}
/**
Map a memory range from the current paging table.
@param[in] Ranges Memory range info to map.
@param[in] PageBuffer Page table root pointer.
@retval EFI_SUCCESS Map completed successfully.
@retval EFI_INVALID_PARAMETER Invalid PageBuffer or Ranges.
**/
EFI_STATUS
EFIAPI
MapMemoryRange (
IN MAP_RANGE Ranges[1],
IN VOID *PageBuffer
)
{
UINTN Idx;
UINTN End;
UINTN EntryNum;
UINTN *PageTable1G;
UINTN *PageTable2M;
UINTN *PageTable4K;
UINTN PageBase;
UINT32 Attribute;
UINTN Address;
UINT32 PteOffset;
UINT32 PdeOffset;
UINT32 PdpOffset;
UINTN Alignment;
UINT32 PageBits;
Attribute = IA32_PG_P | IA32_PG_RW | IA32_PG_AC;
Alignment = Ranges[0].PageSize - 1;
// Check for Start and Limit PageSize alignment
if (((Ranges[0].Start & Alignment) != 0) || ((Ranges[0].Limit & Alignment) != Alignment)) {
return EFI_INVALID_PARAMETER;
}
// Check for Mapping PageSize alignment
if ( (Ranges[0].Mapping & Alignment) != 0) {
return EFI_INVALID_PARAMETER;
}
switch (Ranges[0].PageSize) {
case SIZE_4KB:
PageBits = GetPageShiftBits (PDE_PG_LVL);
if (PageBits == 21) {
// 64 bit mode
PteOffset = 6 * EFI_PAGE_SIZE;
PdeOffset = 2 * EFI_PAGE_SIZE;
EntryNum = 512;
} else {
// 32 bit mode
PteOffset = 1 * EFI_PAGE_SIZE;
PdeOffset = 0;
EntryNum = 1024;
}
PageTable4K = (UINTN *)((UINTN)PageBuffer + PteOffset);
PageTable2M = (UINTN *)((UINTN)PageBuffer + PdeOffset);
Alignment = (UINTN) LShiftU64 (1, PageBits) - 1;
PageBase = Ranges[0].Start & ~Alignment;
// Check if the limit is in the same page
if ((Ranges[0].Limit & ~Alignment) != PageBase) {
return EFI_INVALID_PARAMETER;
}
Address = PageBase;
// Create 4KB pages and remap the CAR region into a different memory location
for (Idx = 0; Idx < EntryNum; Idx++) {
if ((Address >= Ranges[0].Start) && (Address <= Ranges[0].Limit)) {
PageTable4K[Idx] = Ranges[0].Mapping + (Attribute | IA32_PG_PD);
Ranges[0].Mapping += SIZE_4KB;
} else {
PageTable4K[Idx] = Address + (Attribute | IA32_PG_PD);
}
Address += SIZE_4KB;
}
// Split the 2MB page containing the CAR region into 4KB pages
Idx = (UINT32) RShiftU64 (PageBase, PageBits);
PageTable2M[Idx] = (UINTN)PageTable4K + Attribute;
break;
case SIZE_1GB:
ASSERT (Ranges[0].Limit < SIZE_512GB);
PageBits = GetPageShiftBits (PDP_PG_LVL);
PdpOffset = 1 * EFI_PAGE_SIZE;
EntryNum = 512;
PageTable1G = (UINTN *)((UINTN)PageBuffer + PdpOffset);
Alignment = (UINTN) LShiftU64 (1, PageBits) - 1;
PageBase = Ranges[0].Start & ~Alignment;
// Get the start and ending index into 1G table and add 1G entries
Address = Ranges[0].Mapping;
Idx = (UINT32) RShiftU64 (PageBase, PageBits);
End = (UINT32) RShiftU64 (Ranges[0].Limit + 1, PageBits);
for (; Idx < End; Idx++) {
PageTable1G[Idx] = Address + (Attribute | IA32_PG_PD);
Address += SIZE_1GB;
}
break;
default:
return EFI_INVALID_PARAMETER;
}
return EFI_SUCCESS;
}
/**
Unmap a memory range from the current paging table.
@param[in] Ranges Memory range info to un map.
@param[in] PageBuffer Page table root pointer.
@retval EFI_SUCCESS Unmap completed successfully.
@retval EFI_INVALID_PARAMETER Invalid PageBuffer or Ranges.
**/
EFI_STATUS
EFIAPI
UnmapMemoryRange (
IN MAP_RANGE Ranges[1],
IN VOID *PageBuffer
)
{
UINTN *PageTable;
UINTN Idx;
UINTN Start;
UINTN End;
UINT32 PageBits;
UINT32 PdeOffset;
UINT32 Attribute;
if (PageBuffer == NULL) {
return EFI_INVALID_PARAMETER;
}
// Check if range is 4KB page aligned
if (((Ranges[0].Start & 0xFFF) != 0) || ((Ranges[0].Limit & 0xFFF) != 0xFFF)) {
return EFI_INVALID_PARAMETER;
}
Attribute = IA32_PG_P | IA32_PG_RW | IA32_PG_AC;
// Remove 4KB pages from PDE
PageBits = GetPageShiftBits (PDE_PG_LVL);
if (PageBits == 21) {
// 64 bit mode
PdeOffset = 2 * EFI_PAGE_SIZE;
} else {
// 32 bit mode
PdeOffset = 0;
}
PageTable = (UINTN *)((UINTN)PageBuffer + PdeOffset);
Start = (UINTN) RShiftU64 (Ranges[0].Start, PageBits);
End = (UINTN) RShiftU64 (Ranges[0].Limit, PageBits);
for (Idx = Start; Idx < End + 1; Idx++) {
PageTable[Idx] = (UINTN)LShiftU64 (Idx, PageBits) | Attribute | IA32_PG_PD;
}
return EFI_SUCCESS;
}