mirror of https://github.com/acidanthera/audk.git
433 lines
15 KiB
C
433 lines
15 KiB
C
/** @file
|
|
x64 Virtual Memory Management Services in the form of an IA-32 driver.
|
|
Used to establish a 1:1 Virtual to Physical Mapping that is required to
|
|
enter Long Mode (x64 64-bit mode).
|
|
|
|
While we make a 1:1 mapping (identity mapping) for all physical pages
|
|
we still need to use the MTRR's to ensure that the cachability attributes
|
|
for all memory regions is correct.
|
|
|
|
The basic idea is to use 2MB page table entries where ever possible. If
|
|
more granularity of cachability is required then 4K page tables are used.
|
|
|
|
References:
|
|
1) IA-32 Intel(R) Architecture Software Developer's Manual Volume 1:Basic Architecture, Intel
|
|
2) IA-32 Intel(R) Architecture Software Developer's Manual Volume 2:Instruction Set Reference, Intel
|
|
3) IA-32 Intel(R) Architecture Software Developer's Manual Volume 3:System Programmer's Guide, Intel
|
|
|
|
Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
|
|
Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
|
|
|
|
This program and the accompanying materials
|
|
are licensed and made available under the terms and conditions of the BSD License
|
|
which accompanies this distribution. The full text of the license may be found at
|
|
http://opensource.org/licenses/bsd-license.php
|
|
|
|
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
|
|
**/
|
|
|
|
#include "DxeIpl.h"
|
|
#include "VirtualMemory.h"
|
|
|
|
/**
|
|
Clear legacy memory located at the first 4K-page, if available.
|
|
|
|
This function traverses the whole HOB list to check if memory from 0 to 4095
|
|
exists and has not been allocated, and then clear it if so.
|
|
|
|
@param HoStart The start of HobList passed to DxeCore.
|
|
|
|
**/
|
|
VOID
|
|
ClearFirst4KPage (
|
|
IN VOID *HobStart
|
|
)
|
|
{
|
|
EFI_PEI_HOB_POINTERS RscHob;
|
|
EFI_PEI_HOB_POINTERS MemHob;
|
|
BOOLEAN DoClear;
|
|
|
|
RscHob.Raw = HobStart;
|
|
MemHob.Raw = HobStart;
|
|
DoClear = FALSE;
|
|
|
|
//
|
|
// Check if page 0 exists and free
|
|
//
|
|
while ((RscHob.Raw = GetNextHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR,
|
|
RscHob.Raw)) != NULL) {
|
|
if (RscHob.ResourceDescriptor->ResourceType == EFI_RESOURCE_SYSTEM_MEMORY &&
|
|
RscHob.ResourceDescriptor->PhysicalStart == 0) {
|
|
DoClear = TRUE;
|
|
//
|
|
// Make sure memory at 0-4095 has not been allocated.
|
|
//
|
|
while ((MemHob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION,
|
|
MemHob.Raw)) != NULL) {
|
|
if (MemHob.MemoryAllocation->AllocDescriptor.MemoryBaseAddress
|
|
< EFI_PAGE_SIZE) {
|
|
DoClear = FALSE;
|
|
break;
|
|
}
|
|
MemHob.Raw = GET_NEXT_HOB (MemHob);
|
|
}
|
|
break;
|
|
}
|
|
RscHob.Raw = GET_NEXT_HOB (RscHob);
|
|
}
|
|
|
|
if (DoClear) {
|
|
DEBUG ((DEBUG_INFO, "Clearing first 4K-page!\r\n"));
|
|
SetMem (NULL, EFI_PAGE_SIZE, 0);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
BOOLEAN
|
|
IsNullDetectionEnabled (
|
|
VOID
|
|
)
|
|
{
|
|
return ((PcdGet8 (PcdNullPointerDetectionPropertyMask) & BIT0) != 0);
|
|
}
|
|
|
|
/**
|
|
Enable Execute Disable Bit.
|
|
|
|
**/
|
|
VOID
|
|
EnableExecuteDisableBit (
|
|
VOID
|
|
)
|
|
{
|
|
UINT64 MsrRegisters;
|
|
|
|
MsrRegisters = AsmReadMsr64 (0xC0000080);
|
|
MsrRegisters |= BIT11;
|
|
AsmWriteMsr64 (0xC0000080, MsrRegisters);
|
|
}
|
|
|
|
/**
|
|
Split 2M page to 4K.
|
|
|
|
@param[in] PhysicalAddress Start physical address the 2M page covered.
|
|
@param[in, out] PageEntry2M Pointer to 2M page entry.
|
|
@param[in] StackBase Stack base address.
|
|
@param[in] StackSize Stack size.
|
|
|
|
**/
|
|
VOID
|
|
Split2MPageTo4K (
|
|
IN EFI_PHYSICAL_ADDRESS PhysicalAddress,
|
|
IN OUT UINT64 *PageEntry2M,
|
|
IN EFI_PHYSICAL_ADDRESS StackBase,
|
|
IN UINTN StackSize
|
|
)
|
|
{
|
|
EFI_PHYSICAL_ADDRESS PhysicalAddress4K;
|
|
UINTN IndexOfPageTableEntries;
|
|
PAGE_TABLE_4K_ENTRY *PageTableEntry;
|
|
UINT64 AddressEncMask;
|
|
|
|
//
|
|
// Make sure AddressEncMask is contained to smallest supported address field
|
|
//
|
|
AddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask) & PAGING_1G_ADDRESS_MASK_64;
|
|
|
|
PageTableEntry = AllocatePages (1);
|
|
ASSERT (PageTableEntry != NULL);
|
|
|
|
//
|
|
// Fill in 2M page entry.
|
|
//
|
|
*PageEntry2M = (UINT64) (UINTN) PageTableEntry | AddressEncMask | IA32_PG_P | IA32_PG_RW;
|
|
|
|
PhysicalAddress4K = PhysicalAddress;
|
|
for (IndexOfPageTableEntries = 0; IndexOfPageTableEntries < 512; IndexOfPageTableEntries++, PageTableEntry++, PhysicalAddress4K += SIZE_4KB) {
|
|
//
|
|
// Fill in the Page Table entries
|
|
//
|
|
PageTableEntry->Uint64 = (UINT64) PhysicalAddress4K | AddressEncMask;
|
|
PageTableEntry->Bits.ReadWrite = 1;
|
|
|
|
if (IsNullDetectionEnabled () && PhysicalAddress4K == 0) {
|
|
PageTableEntry->Bits.Present = 0;
|
|
} else {
|
|
PageTableEntry->Bits.Present = 1;
|
|
}
|
|
|
|
if (PcdGetBool (PcdSetNxForStack)
|
|
&& (PhysicalAddress4K >= StackBase)
|
|
&& (PhysicalAddress4K < StackBase + StackSize)) {
|
|
//
|
|
// Set Nx bit for stack.
|
|
//
|
|
PageTableEntry->Bits.Nx = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Split 1G page to 2M.
|
|
|
|
@param[in] PhysicalAddress Start physical address the 1G page covered.
|
|
@param[in, out] PageEntry1G Pointer to 1G page entry.
|
|
@param[in] StackBase Stack base address.
|
|
@param[in] StackSize Stack size.
|
|
|
|
**/
|
|
VOID
|
|
Split1GPageTo2M (
|
|
IN EFI_PHYSICAL_ADDRESS PhysicalAddress,
|
|
IN OUT UINT64 *PageEntry1G,
|
|
IN EFI_PHYSICAL_ADDRESS StackBase,
|
|
IN UINTN StackSize
|
|
)
|
|
{
|
|
EFI_PHYSICAL_ADDRESS PhysicalAddress2M;
|
|
UINTN IndexOfPageDirectoryEntries;
|
|
PAGE_TABLE_ENTRY *PageDirectoryEntry;
|
|
UINT64 AddressEncMask;
|
|
|
|
//
|
|
// Make sure AddressEncMask is contained to smallest supported address field
|
|
//
|
|
AddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask) & PAGING_1G_ADDRESS_MASK_64;
|
|
|
|
PageDirectoryEntry = AllocatePages (1);
|
|
ASSERT (PageDirectoryEntry != NULL);
|
|
|
|
//
|
|
// Fill in 1G page entry.
|
|
//
|
|
*PageEntry1G = (UINT64) (UINTN) PageDirectoryEntry | AddressEncMask | IA32_PG_P | IA32_PG_RW;
|
|
|
|
PhysicalAddress2M = PhysicalAddress;
|
|
for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectoryEntry++, PhysicalAddress2M += SIZE_2MB) {
|
|
if ((IsNullDetectionEnabled () && PhysicalAddress2M == 0)
|
|
|| (PcdGetBool (PcdSetNxForStack)
|
|
&& (PhysicalAddress2M < StackBase + StackSize)
|
|
&& ((PhysicalAddress2M + SIZE_2MB) > StackBase))) {
|
|
//
|
|
// Need to split this 2M page that covers NULL or stack range.
|
|
//
|
|
Split2MPageTo4K (PhysicalAddress2M, (UINT64 *) PageDirectoryEntry, StackBase, StackSize);
|
|
} else {
|
|
//
|
|
// Fill in the Page Directory entries
|
|
//
|
|
PageDirectoryEntry->Uint64 = (UINT64) PhysicalAddress2M | AddressEncMask;
|
|
PageDirectoryEntry->Bits.ReadWrite = 1;
|
|
PageDirectoryEntry->Bits.Present = 1;
|
|
PageDirectoryEntry->Bits.MustBe1 = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Allocates and fills in the Page Directory and Page Table Entries to
|
|
establish a 1:1 Virtual to Physical mapping.
|
|
|
|
@param[in] StackBase Stack base address.
|
|
@param[in] StackSize Stack size.
|
|
|
|
@return The address of 4 level page map.
|
|
|
|
**/
|
|
UINTN
|
|
CreateIdentityMappingPageTables (
|
|
IN EFI_PHYSICAL_ADDRESS StackBase,
|
|
IN UINTN StackSize
|
|
)
|
|
{
|
|
UINT32 RegEax;
|
|
UINT32 RegEdx;
|
|
UINT8 PhysicalAddressBits;
|
|
EFI_PHYSICAL_ADDRESS PageAddress;
|
|
UINTN IndexOfPml4Entries;
|
|
UINTN IndexOfPdpEntries;
|
|
UINTN IndexOfPageDirectoryEntries;
|
|
UINT32 NumberOfPml4EntriesNeeded;
|
|
UINT32 NumberOfPdpEntriesNeeded;
|
|
PAGE_MAP_AND_DIRECTORY_POINTER *PageMapLevel4Entry;
|
|
PAGE_MAP_AND_DIRECTORY_POINTER *PageMap;
|
|
PAGE_MAP_AND_DIRECTORY_POINTER *PageDirectoryPointerEntry;
|
|
PAGE_TABLE_ENTRY *PageDirectoryEntry;
|
|
UINTN TotalPagesNum;
|
|
UINTN BigPageAddress;
|
|
VOID *Hob;
|
|
BOOLEAN Page1GSupport;
|
|
PAGE_TABLE_1G_ENTRY *PageDirectory1GEntry;
|
|
UINT64 AddressEncMask;
|
|
|
|
//
|
|
// Make sure AddressEncMask is contained to smallest supported address field
|
|
//
|
|
AddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask) & PAGING_1G_ADDRESS_MASK_64;
|
|
|
|
Page1GSupport = FALSE;
|
|
if (PcdGetBool(PcdUse1GPageTable)) {
|
|
AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
|
|
if (RegEax >= 0x80000001) {
|
|
AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
|
|
if ((RegEdx & BIT26) != 0) {
|
|
Page1GSupport = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get physical address bits supported.
|
|
//
|
|
Hob = GetFirstHob (EFI_HOB_TYPE_CPU);
|
|
if (Hob != NULL) {
|
|
PhysicalAddressBits = ((EFI_HOB_CPU *) Hob)->SizeOfMemorySpace;
|
|
} else {
|
|
AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
|
|
if (RegEax >= 0x80000008) {
|
|
AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);
|
|
PhysicalAddressBits = (UINT8) RegEax;
|
|
} else {
|
|
PhysicalAddressBits = 36;
|
|
}
|
|
}
|
|
|
|
//
|
|
// IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.
|
|
//
|
|
ASSERT (PhysicalAddressBits <= 52);
|
|
if (PhysicalAddressBits > 48) {
|
|
PhysicalAddressBits = 48;
|
|
}
|
|
|
|
//
|
|
// Calculate the table entries needed.
|
|
//
|
|
if (PhysicalAddressBits <= 39 ) {
|
|
NumberOfPml4EntriesNeeded = 1;
|
|
NumberOfPdpEntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 30));
|
|
} else {
|
|
NumberOfPml4EntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 39));
|
|
NumberOfPdpEntriesNeeded = 512;
|
|
}
|
|
|
|
//
|
|
// Pre-allocate big pages to avoid later allocations.
|
|
//
|
|
if (!Page1GSupport) {
|
|
TotalPagesNum = (NumberOfPdpEntriesNeeded + 1) * NumberOfPml4EntriesNeeded + 1;
|
|
} else {
|
|
TotalPagesNum = NumberOfPml4EntriesNeeded + 1;
|
|
}
|
|
BigPageAddress = (UINTN) AllocatePages (TotalPagesNum);
|
|
ASSERT (BigPageAddress != 0);
|
|
|
|
//
|
|
// By architecture only one PageMapLevel4 exists - so lets allocate storage for it.
|
|
//
|
|
PageMap = (VOID *) BigPageAddress;
|
|
BigPageAddress += SIZE_4KB;
|
|
|
|
PageMapLevel4Entry = PageMap;
|
|
PageAddress = 0;
|
|
for (IndexOfPml4Entries = 0; IndexOfPml4Entries < NumberOfPml4EntriesNeeded; IndexOfPml4Entries++, PageMapLevel4Entry++) {
|
|
//
|
|
// Each PML4 entry points to a page of Page Directory Pointer entires.
|
|
// So lets allocate space for them and fill them in in the IndexOfPdpEntries loop.
|
|
//
|
|
PageDirectoryPointerEntry = (VOID *) BigPageAddress;
|
|
BigPageAddress += SIZE_4KB;
|
|
|
|
//
|
|
// Make a PML4 Entry
|
|
//
|
|
PageMapLevel4Entry->Uint64 = (UINT64)(UINTN)PageDirectoryPointerEntry | AddressEncMask;
|
|
PageMapLevel4Entry->Bits.ReadWrite = 1;
|
|
PageMapLevel4Entry->Bits.Present = 1;
|
|
|
|
if (Page1GSupport) {
|
|
PageDirectory1GEntry = (VOID *) PageDirectoryPointerEntry;
|
|
|
|
for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectory1GEntry++, PageAddress += SIZE_1GB) {
|
|
if ((IsNullDetectionEnabled () && PageAddress == 0)
|
|
|| (PcdGetBool (PcdSetNxForStack)
|
|
&& (PageAddress < StackBase + StackSize)
|
|
&& ((PageAddress + SIZE_1GB) > StackBase))) {
|
|
Split1GPageTo2M (PageAddress, (UINT64 *) PageDirectory1GEntry, StackBase, StackSize);
|
|
} else {
|
|
//
|
|
// Fill in the Page Directory entries
|
|
//
|
|
PageDirectory1GEntry->Uint64 = (UINT64)PageAddress | AddressEncMask;
|
|
PageDirectory1GEntry->Bits.ReadWrite = 1;
|
|
PageDirectory1GEntry->Bits.Present = 1;
|
|
PageDirectory1GEntry->Bits.MustBe1 = 1;
|
|
}
|
|
}
|
|
} else {
|
|
for (IndexOfPdpEntries = 0; IndexOfPdpEntries < NumberOfPdpEntriesNeeded; IndexOfPdpEntries++, PageDirectoryPointerEntry++) {
|
|
//
|
|
// Each Directory Pointer entries points to a page of Page Directory entires.
|
|
// So allocate space for them and fill them in in the IndexOfPageDirectoryEntries loop.
|
|
//
|
|
PageDirectoryEntry = (VOID *) BigPageAddress;
|
|
BigPageAddress += SIZE_4KB;
|
|
|
|
//
|
|
// Fill in a Page Directory Pointer Entries
|
|
//
|
|
PageDirectoryPointerEntry->Uint64 = (UINT64)(UINTN)PageDirectoryEntry | AddressEncMask;
|
|
PageDirectoryPointerEntry->Bits.ReadWrite = 1;
|
|
PageDirectoryPointerEntry->Bits.Present = 1;
|
|
|
|
for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectoryEntry++, PageAddress += SIZE_2MB) {
|
|
if ((IsNullDetectionEnabled () && PageAddress == 0)
|
|
|| (PcdGetBool (PcdSetNxForStack)
|
|
&& (PageAddress < StackBase + StackSize)
|
|
&& ((PageAddress + SIZE_2MB) > StackBase))) {
|
|
//
|
|
// Need to split this 2M page that covers NULL or stack range.
|
|
//
|
|
Split2MPageTo4K (PageAddress, (UINT64 *) PageDirectoryEntry, StackBase, StackSize);
|
|
} else {
|
|
//
|
|
// Fill in the Page Directory entries
|
|
//
|
|
PageDirectoryEntry->Uint64 = (UINT64)PageAddress | AddressEncMask;
|
|
PageDirectoryEntry->Bits.ReadWrite = 1;
|
|
PageDirectoryEntry->Bits.Present = 1;
|
|
PageDirectoryEntry->Bits.MustBe1 = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (; IndexOfPdpEntries < 512; IndexOfPdpEntries++, PageDirectoryPointerEntry++) {
|
|
ZeroMem (
|
|
PageDirectoryPointerEntry,
|
|
sizeof(PAGE_MAP_AND_DIRECTORY_POINTER)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// For the PML4 entries we are not using fill in a null entry.
|
|
//
|
|
for (; IndexOfPml4Entries < 512; IndexOfPml4Entries++, PageMapLevel4Entry++) {
|
|
ZeroMem (
|
|
PageMapLevel4Entry,
|
|
sizeof (PAGE_MAP_AND_DIRECTORY_POINTER)
|
|
);
|
|
}
|
|
|
|
if (PcdGetBool (PcdSetNxForStack)) {
|
|
EnableExecuteDisableBit ();
|
|
}
|
|
|
|
return (UINTN)PageMap;
|
|
}
|
|
|