1295 lines
27 KiB
C++
1295 lines
27 KiB
C++
//++
|
|
//
|
|
// Copyright (c) Microsoft Corporation
|
|
//
|
|
// Module Name:
|
|
//
|
|
// blmm.cpp
|
|
//
|
|
// Abstract:
|
|
//
|
|
// This module implements memory management for the boot loader.
|
|
//
|
|
//--
|
|
|
|
#include "bl.h"
|
|
|
|
LIST_ENTRY BlMmPhysicalRegionList;
|
|
|
|
struct {
|
|
BL_MM_PHYSICAL_REGION StaticArray[16];
|
|
LIST_ENTRY FreeList;
|
|
} BlMmPhysicalRegionLookaside;
|
|
|
|
GDTR BlMmInitialGdtr;
|
|
|
|
ULONG_PTR BlMmLegacyCr3;
|
|
ULONG_PTR BlMmBootCr3;
|
|
|
|
typedef struct _BL_MM_PAGE_TABLE {
|
|
UINT64 Entry[512];
|
|
} BL_MM_PAGE_TABLE, *PBL_MM_PAGE_TABLE;
|
|
|
|
#if defined(BOOT_X64)
|
|
|
|
__declspec(align(PAGE_SIZE)) BL_MM_PAGE_TABLE BlMmPml4Table[1];
|
|
|
|
#endif
|
|
|
|
__declspec(align(PAGE_SIZE)) BL_MM_PAGE_TABLE BlMmPdpTable[1];
|
|
__declspec(align(PAGE_SIZE)) BL_MM_PAGE_TABLE BlMmPdTable[4];
|
|
__declspec(align(PAGE_SIZE)) BL_MM_PAGE_TABLE BlMmPgTable[1];
|
|
|
|
PVOID BlMmExtendedBiosDataArea;
|
|
|
|
VOID
|
|
BlMmCompactPhysicalRegionList(
|
|
VOID
|
|
)
|
|
|
|
//++
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// This function compacts the physical region list by coalescing adjacent
|
|
// regions of the same type.
|
|
//
|
|
//--
|
|
|
|
{
|
|
PBL_MM_PHYSICAL_REGION Current;
|
|
PLIST_ENTRY Head;
|
|
PBL_MM_PHYSICAL_REGION Next;
|
|
|
|
Head = &BlMmPhysicalRegionList;
|
|
|
|
if (BlRtlIsListEmpty(Head) != FALSE) {
|
|
|
|
return;
|
|
}
|
|
|
|
Current = CONTAINING_RECORD(Head->Flink,
|
|
BL_MM_PHYSICAL_REGION,
|
|
Entry);
|
|
|
|
for (;;) {
|
|
|
|
BLASSERT(Current->Size > 0);
|
|
|
|
BLASSERT(Current->Start + Current->Size == Current->Limit);
|
|
|
|
BLASSERT((Current->Type >= BL_MM_PHYSICAL_REGION_MIN_TYPE) && (Current->Type <= BL_MM_PHYSICAL_REGION_MAX_TYPE));
|
|
|
|
if (Current->Entry.Flink == Head) {
|
|
|
|
break;
|
|
}
|
|
|
|
Next = CONTAINING_RECORD(Current->Entry.Flink,
|
|
BL_MM_PHYSICAL_REGION,
|
|
Entry);
|
|
|
|
BLASSERT(Next->Start >= Current->Limit);
|
|
|
|
if ((Next->Start == Current->Limit) &&
|
|
(Next->Type == Current->Type)) {
|
|
|
|
Current->Limit = Next->Limit;
|
|
Current->Size = Current->Limit - Current->Start;
|
|
|
|
BlRtlRemoveEntryList(&Next->Entry);
|
|
|
|
BlRtlInsertTailList(&BlMmPhysicalRegionLookaside.FreeList, &Next->Entry);
|
|
|
|
continue;
|
|
}
|
|
|
|
Current = Next;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
BlMmInsertPhysicalRegion(
|
|
PBL_MM_PHYSICAL_REGION Region
|
|
)
|
|
|
|
//++
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// This function inserts a new physical region to the physical region list.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Region - Supplies a pointer to the region to insert.
|
|
//
|
|
//--
|
|
|
|
{
|
|
PLIST_ENTRY Entry;
|
|
PLIST_ENTRY Head;
|
|
PBL_MM_PHYSICAL_REGION Next;
|
|
|
|
Head = &BlMmPhysicalRegionList;
|
|
Entry = Head->Flink;
|
|
|
|
while (Entry != Head) {
|
|
|
|
Next = CONTAINING_RECORD(Entry,
|
|
BL_MM_PHYSICAL_REGION,
|
|
Entry);
|
|
|
|
if (Next->Start > Region->Start) {
|
|
|
|
break;
|
|
}
|
|
|
|
Entry = Entry->Flink;
|
|
}
|
|
|
|
BlRtlInsertTailList(Entry, &Region->Entry);
|
|
|
|
BlMmCompactPhysicalRegionList();
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
BlMmCreatePhysicalRegion(
|
|
UINT64 Start,
|
|
UINT64 Size,
|
|
UINT32 Type
|
|
)
|
|
|
|
//++
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// This function creates a physical region descriptor.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Start - Supplies the start address of the region.
|
|
//
|
|
// Size - Supplies the size of the region.
|
|
//
|
|
// Type - Supplies the type of the region.
|
|
//
|
|
//--
|
|
|
|
{
|
|
PLIST_ENTRY Entry;
|
|
PLIST_ENTRY Head;
|
|
UINT64 Limit;
|
|
PBL_MM_PHYSICAL_REGION Region;
|
|
|
|
BLASSERT((Start % PAGE_SIZE) == 0);
|
|
BLASSERT(Size > 0);
|
|
BLASSERT((Size % PAGE_SIZE) == 0);
|
|
BLASSERT(Type >= BL_MM_PHYSICAL_REGION_MIN_TYPE);
|
|
BLASSERT(Type <= BL_MM_PHYSICAL_REGION_MAX_TYPE);
|
|
|
|
Limit = Start + Size;
|
|
|
|
BLASSERT(Limit > Start);
|
|
|
|
Head = &BlMmPhysicalRegionList;
|
|
Entry = Head->Flink;
|
|
|
|
while (Entry != Head) {
|
|
|
|
Region = CONTAINING_RECORD(Entry,
|
|
BL_MM_PHYSICAL_REGION,
|
|
Entry);
|
|
|
|
if ((Start < Region->Limit) && (Limit > Region->Start)) {
|
|
|
|
BlRtlPrintf("MM: Physical region collision!\n");
|
|
BlRtlHalt();
|
|
}
|
|
|
|
Entry = Entry->Flink;
|
|
}
|
|
|
|
Entry = Head->Flink;
|
|
|
|
while (Entry != Head) {
|
|
|
|
Region = CONTAINING_RECORD(Entry,
|
|
BL_MM_PHYSICAL_REGION,
|
|
Entry);
|
|
|
|
if (Start < Region->Start) {
|
|
|
|
break;
|
|
}
|
|
|
|
Entry = Entry->Flink;
|
|
}
|
|
|
|
BLASSERT(BlRtlIsListEmpty(&BlMmPhysicalRegionLookaside.FreeList) == FALSE);
|
|
|
|
Region = CONTAINING_RECORD(BlRtlRemoveHeadList(&BlMmPhysicalRegionLookaside.FreeList),
|
|
BL_MM_PHYSICAL_REGION,
|
|
Entry);
|
|
|
|
Region->Start = Start;
|
|
Region->Size = Size;
|
|
Region->Limit = Limit;
|
|
Region->Type = Type;
|
|
|
|
BlMmInsertPhysicalRegion(Region);
|
|
|
|
return;
|
|
}
|
|
|
|
UINT64
|
|
BlMmAllocatePhysicalRegion(
|
|
UINT32 Size,
|
|
UINT32 Type
|
|
)
|
|
|
|
//++
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// This function allocates a physical region from the lowest available and
|
|
// sufficient free region.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Size - Supplies the size of the region to allocate.
|
|
//
|
|
// Type - Supplies the type of the region to allocate.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// The physical address of the allocated region.
|
|
//
|
|
//--
|
|
|
|
{
|
|
PLIST_ENTRY Entry;
|
|
PBL_MM_PHYSICAL_REGION FreeRegion;
|
|
PLIST_ENTRY Head;
|
|
PBL_MM_PHYSICAL_REGION Region;
|
|
|
|
BLASSERT(Size > 0);
|
|
BLASSERT(Type != BL_MM_PHYSICAL_REGION_FREE);
|
|
|
|
SATISFY_OVERZEALOUS_COMPILER(Region = NULL);
|
|
|
|
Size = ROUND_UP_TO_PAGES(Size);
|
|
|
|
Head = &BlMmPhysicalRegionList;
|
|
Entry = Head->Blink;
|
|
|
|
while (Entry != Head) {
|
|
|
|
Region = CONTAINING_RECORD(Entry,
|
|
BL_MM_PHYSICAL_REGION,
|
|
Entry);
|
|
|
|
if ((Region->Type == BL_MM_PHYSICAL_REGION_FREE) &&
|
|
(Region->Size >= Size) &&
|
|
(Region->Limit < 0x100000000UI64)) {
|
|
|
|
break;
|
|
}
|
|
|
|
Entry = Entry->Blink;
|
|
}
|
|
|
|
if (Entry == Head) {
|
|
|
|
BlRtlPrintf("MM: Unable to allocate %x bytes!\n", Size);
|
|
BlRtlHalt();
|
|
}
|
|
|
|
if (Region->Size == Size) {
|
|
|
|
Region->Type = Type;
|
|
return Region->Start;
|
|
}
|
|
|
|
FreeRegion = Region;
|
|
|
|
BLASSERT(BlRtlIsListEmpty(&BlMmPhysicalRegionLookaside.FreeList) == FALSE);
|
|
|
|
Region = CONTAINING_RECORD(BlRtlRemoveHeadList(&BlMmPhysicalRegionLookaside.FreeList),
|
|
BL_MM_PHYSICAL_REGION,
|
|
Entry);
|
|
|
|
Region->Start = FreeRegion->Limit - Size;
|
|
FreeRegion->Limit -= Size;
|
|
FreeRegion->Size -= Size;
|
|
|
|
Region->Size = Size;
|
|
Region->Limit = Region->Start + Region->Size;
|
|
Region->Type = Type;
|
|
|
|
BlRtlZeroMemory((PVOID) (ULONG_PTR) Region->Start, (ULONG_PTR) (UINT32) Region->Size);
|
|
|
|
BlMmInsertPhysicalRegion(Region);
|
|
|
|
return Region->Start;
|
|
}
|
|
|
|
BOOLEAN
|
|
BlMmAllocateSpecificPhysicalRegion(
|
|
UINT64 Base,
|
|
UINT64 Size,
|
|
UINT32 Type
|
|
)
|
|
|
|
//++
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// This function allocates a specific physical region.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Base - Supplies the base physical address of the region to allocate.
|
|
//
|
|
// Size - Supplies the size of the region to allocate.
|
|
//
|
|
// Type - Supplies the type of the region to allocate.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// TRUE, if allocation was successful.
|
|
// FALSE, otherwise.
|
|
//
|
|
//--
|
|
|
|
{
|
|
UINT64 End;
|
|
PLIST_ENTRY Entry;
|
|
PLIST_ENTRY Head;
|
|
PBL_MM_PHYSICAL_REGION NextRegion;
|
|
PBL_MM_PHYSICAL_REGION PreviousRegion;
|
|
PBL_MM_PHYSICAL_REGION Region;
|
|
UINT64 Start;
|
|
|
|
BLASSERT((Base % PAGE_SIZE) == 0);
|
|
|
|
BLASSERT(Size > 0);
|
|
|
|
BLASSERT((Size % PAGE_SIZE) == 0);
|
|
|
|
BLASSERT(Type != BL_MM_PHYSICAL_REGION_FREE);
|
|
|
|
SATISFY_OVERZEALOUS_COMPILER(Region = NULL);
|
|
|
|
Start = Base;
|
|
End = Start + Size;
|
|
|
|
Head = &BlMmPhysicalRegionList;
|
|
|
|
for (Entry = Head->Flink; Entry != Head; Entry = Entry->Flink) {
|
|
|
|
Region = CONTAINING_RECORD(Entry,
|
|
BL_MM_PHYSICAL_REGION,
|
|
Entry);
|
|
|
|
if ((Start >= Region->Start) && (End <= Region->Limit)) {
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Entry == Head) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
if (Region->Type != BL_MM_PHYSICAL_REGION_FREE) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
PreviousRegion = NULL;
|
|
NextRegion = NULL;
|
|
|
|
if (Region->Start < Start) {
|
|
|
|
BLASSERT(BlRtlIsListEmpty(&BlMmPhysicalRegionLookaside.FreeList) == FALSE);
|
|
|
|
PreviousRegion = CONTAINING_RECORD(BlRtlRemoveHeadList(&BlMmPhysicalRegionLookaside.FreeList),
|
|
BL_MM_PHYSICAL_REGION,
|
|
Entry);
|
|
|
|
PreviousRegion->Start = Region->Start;
|
|
PreviousRegion->Size = Start - Region->Start;
|
|
PreviousRegion->Limit = Start;
|
|
PreviousRegion->Type = BL_MM_PHYSICAL_REGION_FREE;
|
|
}
|
|
|
|
if (Region->Limit > End) {
|
|
|
|
BLASSERT(BlRtlIsListEmpty(&BlMmPhysicalRegionLookaside.FreeList) == FALSE);
|
|
|
|
NextRegion = CONTAINING_RECORD(BlRtlRemoveHeadList(&BlMmPhysicalRegionLookaside.FreeList),
|
|
BL_MM_PHYSICAL_REGION,
|
|
Entry);
|
|
|
|
NextRegion->Start = End;
|
|
NextRegion->Size = Region->Limit - End;
|
|
NextRegion->Limit = Region->Limit;
|
|
NextRegion->Type = BL_MM_PHYSICAL_REGION_FREE;
|
|
}
|
|
|
|
Region->Start = Start;
|
|
Region->Size = Size;
|
|
Region->Limit = End;
|
|
Region->Type = Type;
|
|
|
|
if (PreviousRegion != NULL) {
|
|
|
|
BlMmInsertPhysicalRegion(PreviousRegion);
|
|
}
|
|
|
|
if (NextRegion != NULL) {
|
|
|
|
BlMmInsertPhysicalRegion(NextRegion);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
BlMmFindFreePhysicalRegion(
|
|
PUINT64 Base,
|
|
PUINT64 Size
|
|
)
|
|
|
|
//++
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// This function finds a free physical region.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Base - Receives the base address of the free region.
|
|
//
|
|
// Size - Receives the size of the free region.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// TRUE, if a free region was found.
|
|
// FALSE, otherwise.
|
|
//
|
|
//--
|
|
|
|
{
|
|
PLIST_ENTRY Entry;
|
|
PLIST_ENTRY Head;
|
|
PBL_MM_PHYSICAL_REGION Region;
|
|
|
|
Head = &BlMmPhysicalRegionList;
|
|
|
|
for (Entry = Head->Flink; Entry != Head; Entry = Entry->Flink) {
|
|
|
|
Region = CONTAINING_RECORD(Entry,
|
|
BL_MM_PHYSICAL_REGION,
|
|
Entry);
|
|
|
|
if (Region->Type == BL_MM_PHYSICAL_REGION_FREE) {
|
|
|
|
*Base = Region->Start;
|
|
*Size = Region->Size;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOLEAN
|
|
BlMmGetNextPhysicalRegion(
|
|
PVOID *Handle,
|
|
PUINT64 Base,
|
|
PUINT64 Size,
|
|
PUINT32 Type
|
|
)
|
|
|
|
//++
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// This function is used to enumerate physical regions.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Handle - Supplies a pointer to the last handle (or NULL to start
|
|
// enumeration) on entry.
|
|
// Receives the next handle (if any) on exit.
|
|
//
|
|
// Base - Receives the base address of the next region.
|
|
//
|
|
// Size - Receives the size of the next region.
|
|
//
|
|
// Type - Receives the type of the next region.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// TRUE, if there is a next region.
|
|
// FALSE, otherwise.
|
|
//
|
|
//--
|
|
|
|
{
|
|
PLIST_ENTRY Entry;
|
|
PLIST_ENTRY Head;
|
|
PBL_MM_PHYSICAL_REGION Region;
|
|
|
|
Head = &BlMmPhysicalRegionList;
|
|
|
|
if (*Handle == NULL) {
|
|
|
|
Entry = Head;
|
|
|
|
} else {
|
|
|
|
Entry = (PLIST_ENTRY) *Handle;
|
|
}
|
|
|
|
Entry = Entry->Flink;
|
|
|
|
if (Entry == Head) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
Region = CONTAINING_RECORD(Entry,
|
|
BL_MM_PHYSICAL_REGION,
|
|
Entry);
|
|
|
|
*Handle = &Region->Entry;
|
|
*Base = Region->Start;
|
|
*Size = Region->Size;
|
|
*Type = Region->Type;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
PCHAR
|
|
BlMmPhysicalRegionTypeString(
|
|
UINT32 Type
|
|
)
|
|
|
|
//++
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// This function returns the specified physical region type string.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Type - Supplies the physical region type.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// String representation for the specified type.
|
|
//
|
|
//--
|
|
|
|
{
|
|
|
|
#define CASE(X) case BL_MM_PHYSICAL_REGION_##X: return #X;
|
|
|
|
switch (Type) {
|
|
|
|
CASE(FREE)
|
|
CASE(BIOS)
|
|
CASE(BOOT_LOADER)
|
|
CASE(SMAP_RESERVED)
|
|
CASE(DISTRO)
|
|
CASE(KERNEL_IMAGE)
|
|
CASE(NATIVE_PLATFORM)
|
|
CASE(NATIVE_PROCESSOR)
|
|
CASE(LOG_RECORD)
|
|
CASE(LOG_TEXT)
|
|
CASE(KERNEL_STACK)
|
|
CASE(CONTEXT)
|
|
CASE(TASK)
|
|
CASE(SINGULARITY)
|
|
CASE(BOOT_STACK)
|
|
CASE(SINGULARITY_SMAP)
|
|
}
|
|
|
|
#undef CASE
|
|
|
|
BLASSERT(FALSE);
|
|
return NULL;
|
|
}
|
|
|
|
VOID
|
|
BlMmDumpPhysicalRegionList(
|
|
VOID
|
|
)
|
|
|
|
//++
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// This function dumps the list of physical regions.
|
|
//
|
|
//--
|
|
|
|
{
|
|
PLIST_ENTRY Entry;
|
|
PLIST_ENTRY Head;
|
|
PBL_MM_PHYSICAL_REGION Region;
|
|
|
|
BlRtlPrintf("MM: Physical Region:\n");
|
|
|
|
Head = &BlMmPhysicalRegionList;
|
|
|
|
for (Entry = Head->Flink; Entry != Head; Entry = Entry->Flink) {
|
|
|
|
Region = CONTAINING_RECORD(Entry, BL_MM_PHYSICAL_REGION, Entry);
|
|
|
|
BlRtlPrintf("MM: %016I64x...%016I64x %s\n",
|
|
Region->Start,
|
|
Region->Limit,
|
|
BlMmPhysicalRegionTypeString(Region->Type));
|
|
}
|
|
|
|
BlRtlPrintf("\n");
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// AIFIX: Switch from identity mapping to dynamic mapping.
|
|
//
|
|
|
|
VOID
|
|
BlMmInitializePageTables(
|
|
VOID
|
|
)
|
|
|
|
//++
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// This function initializes boot loader page tables that identity map the first 4GB of memory.
|
|
//
|
|
//--
|
|
|
|
{
|
|
UINT64 Index;
|
|
UINT64 *Pde;
|
|
UINT64 PdtBase;
|
|
UINT64 *Pdpe;
|
|
UINT64 PdptBase;
|
|
|
|
#if defined(BOOT_X64)
|
|
|
|
UINT64 *Pml4e;
|
|
UINT64 Pml4tBase;
|
|
|
|
#endif
|
|
|
|
UINT64 *Pte;
|
|
UINT64 PtBase;
|
|
|
|
#if defined(BOOT_X64)
|
|
|
|
Pml4tBase = (UINT64) (ULONG_PTR) BlMmPml4Table;
|
|
|
|
#endif
|
|
|
|
PdptBase = (UINT64) (ULONG_PTR) BlMmPdpTable;
|
|
PdtBase = (UINT64) (ULONG_PTR) BlMmPdTable;
|
|
PtBase = (UINT64) (ULONG_PTR) BlMmPgTable;
|
|
|
|
#if defined(BOOT_X64)
|
|
|
|
Pml4e = (UINT64 *) (PVOID) Pml4tBase;
|
|
|
|
#endif
|
|
|
|
Pdpe = (UINT64 *) (PVOID) (ULONG_PTR) PdptBase;
|
|
Pde = (UINT64 *) (PVOID) (ULONG_PTR) PdtBase;
|
|
Pte = (UINT64 *) (PVOID) (ULONG_PTR) PtBase;
|
|
|
|
#if defined(BOOT_X64)
|
|
|
|
Pml4e[0] = PdptBase | PAGE_PRESENT | PAGE_WRITEABLE | PAGE_ACCESSED;
|
|
|
|
#endif
|
|
|
|
for (Index = 0; Index < 4; Index += 1) {
|
|
|
|
Pdpe[Index] = (PdtBase + (Index * PAGE_SIZE)) | PAGE_PRESENT;
|
|
|
|
#if defined(BOOT_X64)
|
|
|
|
Pdpe[Index] |= PAGE_WRITEABLE | PAGE_ACCESSED;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
Pde[0] = PtBase | PAGE_PRESENT | PAGE_WRITEABLE | PAGE_ACCESSED;
|
|
|
|
for (Index = 1; Index < 512; Index += 1) {
|
|
|
|
Pte[Index] = (Index << 12) | PAGE_PRESENT | PAGE_WRITEABLE | PAGE_ACCESSED;
|
|
}
|
|
|
|
for (Index = 1; Index < 2048; Index += 1) {
|
|
|
|
Pde[Index] = (Index << 21) | PAGE_PRESENT | PAGE_WRITEABLE | PAGE_ACCESSED | PAGE_2MB;
|
|
}
|
|
|
|
#if defined(BOOT_X86)
|
|
|
|
BlMmBootCr3 = (ULONG_PTR) PdptBase;
|
|
|
|
#elif defined(BOOT_X64)
|
|
|
|
BlMmBootCr3 = Pml4tBase;
|
|
|
|
#endif
|
|
|
|
BlMmSetCr3(BlMmBootCr3);
|
|
|
|
BlGetBeb()->LegacyReturnCr3 = (UINT32) BlMmBootCr3;
|
|
|
|
#if MM_VERBOSE
|
|
|
|
BlRtlPrintf("MM: 4GB identity map [CR3=%p]\n", BlMmBootCr3);
|
|
|
|
#endif
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
BlMmMapVirtualPage(
|
|
PVOID VirtualAddress,
|
|
PVOID PhysicalAddress,
|
|
BOOLEAN Writeable,
|
|
BOOLEAN Cacheable,
|
|
BOOLEAN WriteThrough
|
|
)
|
|
|
|
//++
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// This function maps the specified virtual page.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// VirtualAddress - Supplies the virtual address to map.
|
|
//
|
|
// PhysicalAddress - Supplies the physical address to map to.
|
|
//
|
|
// Writeable - Supplies whether the page is writeable.
|
|
//
|
|
// Cacheable - Supplies whether the page is cacheable.
|
|
//
|
|
// WriteThrough - Supplies whether the page is write-through.
|
|
//
|
|
//--
|
|
|
|
{
|
|
UINT64 Entry;
|
|
UINT32 Index;
|
|
UINT64 LargePageAddress;
|
|
PUINT64 PdBase;
|
|
UINT32 PdIndex;
|
|
PUINT64 PdpBase;
|
|
UINT32 PdpIndex;
|
|
UINT64 PhysicalPageNumber;
|
|
PUINT64 PtBase;
|
|
UINT32 PtIndex;
|
|
ULONG_PTR VirtualPageNumber;
|
|
|
|
BLASSERT((((ULONG_PTR) VirtualAddress) & 0xFFF) == 0);
|
|
|
|
BLASSERT((((ULONG_PTR) PhysicalAddress) & 0xFFF) == 0);
|
|
|
|
#if defined(BOOT_X64)
|
|
|
|
BLASSERT((ULONG_PTR) VirtualAddress < 0x100000000UI64);
|
|
|
|
#endif
|
|
|
|
//
|
|
// Compute virtual page number, page directory pointer, page directory, and page table indices.
|
|
//
|
|
|
|
VirtualPageNumber = ((ULONG_PTR) VirtualAddress) / PAGE_SIZE;
|
|
|
|
PdpIndex = (UINT32) ((VirtualPageNumber >> 18) & 0x1FF);
|
|
PdIndex = (UINT32) ((VirtualPageNumber >> 9) & 0x1FF);
|
|
PtIndex = (UINT32) (VirtualPageNumber & 0x1FF);
|
|
|
|
//
|
|
// Look up page directory base address.
|
|
//
|
|
|
|
PdpBase = &BlMmPdpTable[0].Entry[0];
|
|
|
|
PdBase = (PUINT64) (ULONG_PTR) (PdpBase[PdpIndex] & (~(0xFFFUI64)));
|
|
|
|
//
|
|
// If the specified page is currently being mapped with large pages, then split it into 4K mappings.
|
|
//
|
|
|
|
if ((PdBase[PdIndex] & PAGE_2MB) != 0) {
|
|
|
|
PtBase = (PUINT64) (ULONG_PTR) BlMmAllocatePhysicalRegion(PAGE_SIZE, BL_MM_PHYSICAL_REGION_BOOT_LOADER);
|
|
|
|
LargePageAddress = (PdBase[PdIndex] & (~(0xFFFUI64)));
|
|
|
|
BLASSERT(((LargePageAddress >> 12) & 0x1FF) == 0);
|
|
|
|
//
|
|
// Create page table entries to map the region in 4K pages.
|
|
//
|
|
|
|
for (Index = 0; Index < 512; Index += 1) {
|
|
|
|
PtBase[Index] = (LargePageAddress + (Index * PAGE_SIZE)) | PAGE_PRESENT | PAGE_WRITEABLE | PAGE_ACCESSED;
|
|
}
|
|
|
|
//
|
|
// Update page directory entry.
|
|
//
|
|
|
|
PdBase[PdIndex] = ((UINT64) (ULONG_PTR) PtBase) | PAGE_PRESENT | PAGE_WRITEABLE | PAGE_ACCESSED;
|
|
|
|
//
|
|
// Flush TLB.
|
|
//
|
|
|
|
BlMmSetCr3(BlMmBootCr3);
|
|
}
|
|
|
|
//
|
|
// Update page mapping.
|
|
//
|
|
|
|
PtBase = (PUINT64) (ULONG_PTR) (PdBase[PdIndex] & (~(0xFFFUI64)));
|
|
|
|
PhysicalPageNumber = ((ULONG_PTR) PhysicalAddress) >> 12;
|
|
|
|
Entry = (PhysicalPageNumber << 12) | PAGE_PRESENT;
|
|
|
|
if (Writeable != FALSE) {
|
|
|
|
Entry |= PAGE_WRITEABLE;
|
|
}
|
|
|
|
if (Cacheable == FALSE) {
|
|
|
|
Entry |= PAGE_CACHEDISABLE;
|
|
|
|
} else if (WriteThrough != FALSE) {
|
|
|
|
Entry |= PAGE_WRITETHROUGH;
|
|
}
|
|
|
|
PtBase[PtIndex] = Entry;
|
|
|
|
//
|
|
// Flush TLB.
|
|
//
|
|
|
|
BlMmSetCr3(BlMmBootCr3);
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
BlMmMapVirtualRange(
|
|
PVOID VirtualAddress,
|
|
PVOID PhysicalAddress,
|
|
ULONG_PTR Size,
|
|
BOOLEAN Writeable,
|
|
BOOLEAN Cacheable,
|
|
BOOLEAN WriteThrough
|
|
)
|
|
|
|
//++
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// This function maps the specified virtual range.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// VirtualAddress - Supplies the virtual address to map.
|
|
//
|
|
// PhysicalAddress - Supplies the physical address to map to.
|
|
//
|
|
// Size - Supplies the size of the mapping.
|
|
//
|
|
// Writeable - Supplies whether the page is writeable.
|
|
//
|
|
// Cacheable - Supplies whether the page is cacheable.
|
|
//
|
|
// WriteThrough - Supplies whether the page is write-through.
|
|
//
|
|
//--
|
|
|
|
{
|
|
ULONG_PTR PhysicalNext;
|
|
ULONG_PTR VirtualLimit;
|
|
ULONG_PTR VirtualNext;
|
|
|
|
VirtualNext = (ULONG_PTR) VirtualAddress;
|
|
VirtualLimit = VirtualNext + Size;
|
|
|
|
VirtualNext &= (~((ULONG_PTR) 0xFFF));
|
|
VirtualLimit = ROUND_UP_TO_PAGES(VirtualLimit);
|
|
|
|
PhysicalNext = (ULONG_PTR) PhysicalAddress;
|
|
PhysicalNext &= (~((ULONG_PTR) 0xFFF));
|
|
|
|
while (VirtualNext < VirtualLimit) {
|
|
|
|
BlMmMapVirtualPage((PVOID) VirtualNext,
|
|
(PVOID) PhysicalNext,
|
|
Writeable,
|
|
Cacheable,
|
|
WriteThrough);
|
|
|
|
VirtualNext += PAGE_SIZE;
|
|
PhysicalNext += PAGE_SIZE;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
BlMmInitializeCodeSegment(
|
|
PCODE_SEGMENT CodeSegment
|
|
)
|
|
|
|
//++
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// This function initializes the specified code segment.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// CodeSegment - Supplies a pointer to the code segment to initialize.
|
|
//
|
|
//--
|
|
|
|
{
|
|
BlRtlZeroMemory(CodeSegment, sizeof(CODE_SEGMENT));
|
|
|
|
CodeSegment->Accessed = 1;
|
|
CodeSegment->Readable = 1;
|
|
CodeSegment->Code = 1;
|
|
CodeSegment->S = 1;
|
|
CodeSegment->Present = 1;
|
|
CodeSegment->Long = 1;
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
BlMmInitializeDataSegment(
|
|
PDATA_SEGMENT DataSegment,
|
|
UINT32 Base,
|
|
UINT32 Limit
|
|
)
|
|
|
|
//++
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// This function initializes the specified data segment.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// DataSegment - Supplies a pointer to the data segment to initialize.
|
|
//
|
|
// Base - Supplies the base address of the data segment.
|
|
//
|
|
// Limit - Supplies the limit of the data segment.
|
|
//
|
|
//--
|
|
|
|
{
|
|
BlRtlZeroMemory(DataSegment, sizeof(DATA_SEGMENT));
|
|
|
|
DataSegment->Accessed = 1;
|
|
DataSegment->Writable = 1;
|
|
DataSegment->S = 1;
|
|
DataSegment->Present = 1;
|
|
DataSegment->Big = 1;
|
|
|
|
DataSegment->Base_23_0 = Base & 0xFFFFFF;
|
|
DataSegment->Base_31_24 = Base >> 24;
|
|
|
|
if (Limit <= 0xFFFFF) {
|
|
|
|
DataSegment->Limit_15_0 = Limit & 0xFFFF;
|
|
DataSegment->Limit_19_16 = (Limit >> 16) & 0xF;
|
|
|
|
} else {
|
|
|
|
DataSegment->Granularity = 1;
|
|
DataSegment->Limit_15_0 = (Limit >> 12) & 0xFFFF;
|
|
DataSegment->Limit_19_16 = (Limit >> 28) & 0xF;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
BlMmInitializeSystemSegment(
|
|
PSYSTEM_SEGMENT SystemSegment,
|
|
UINT32 Type,
|
|
ULONG_PTR Base,
|
|
UINT32 Limit
|
|
)
|
|
|
|
//++
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// This function initializes the specified system segment.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// SystemSegment - Supplies a pointer to the system segment to initialize.
|
|
//
|
|
// Type - Supplies the type of the system segment.
|
|
//
|
|
// Base - Supplies the base address of the system segment.
|
|
//
|
|
// Limit - Supplies the limit of the system segment.
|
|
//
|
|
//--
|
|
|
|
{
|
|
BlRtlZeroMemory(SystemSegment, sizeof(SYSTEM_SEGMENT));
|
|
|
|
SystemSegment->Type = (Type & 0xF);
|
|
SystemSegment->Present = 1;
|
|
|
|
SystemSegment->Base_23_0 = Base & 0xFFFFFF;
|
|
SystemSegment->Base_31_24 = (Base >> 24) & 0xFF;
|
|
|
|
#if defined(BOOT_X64)
|
|
|
|
SystemSegment->Base_63_32 = Base >> 32;
|
|
|
|
#endif
|
|
|
|
if (Limit <= 0xFFFFF) {
|
|
|
|
SystemSegment->Limit_15_0 = Limit & 0xFFFF;
|
|
SystemSegment->Limit_19_16 = (Limit >> 16) & 0xF;
|
|
|
|
} else {
|
|
|
|
SystemSegment->Granularity = 1;
|
|
SystemSegment->Limit_15_0 = (Limit >> 12) & 0xFFFF;
|
|
SystemSegment->Limit_19_16 = (Limit >> 28) & 0xF;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
#if !defined(BOOT_PXE)
|
|
VOID
|
|
BlMmEnableA20Gate(
|
|
VOID
|
|
)
|
|
|
|
//++
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// This function enables A20 gate.
|
|
//
|
|
//--
|
|
|
|
{
|
|
BL_KEYBOARD_WRITE_OUTPUT_PORT(BL_KEYBOARD_A20_ENABLE);
|
|
|
|
BL_KEYBOARD_WRITE_COMMAND(BL_KEYBOARD_COMMAND_PULSE_OUTPUT_PORT);
|
|
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
PVOID
|
|
BlMmGetExtendedBiosDataArea(
|
|
VOID
|
|
)
|
|
|
|
//++
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// This function gets the address of the extended BIOS data area.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// Address of the extended BIOS data area.
|
|
//
|
|
//--
|
|
|
|
{
|
|
UINT16 Segment;
|
|
|
|
Segment = *((PUINT16) (ULONG_PTR) 0x40E);
|
|
|
|
return (PVOID) (((ULONG_PTR) Segment) << 4);
|
|
}
|
|
|
|
VOID
|
|
BlMmInitializeSystem(
|
|
VOID
|
|
)
|
|
|
|
//++
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// This function initializes boot loader memory management.
|
|
//
|
|
//--
|
|
|
|
{
|
|
UINT64 Delta;
|
|
UINT32 Index;
|
|
PBL_MM_PHYSICAL_REGION PhysicalRegion;
|
|
PBL_SMAP_ENTRY SmapEntry;
|
|
|
|
#if !defined(BOOT_PXE)
|
|
//
|
|
// Enable A20 gate.
|
|
//
|
|
|
|
BlMmEnableA20Gate();
|
|
#endif
|
|
|
|
//
|
|
// Get extended BIOS data area.
|
|
//
|
|
|
|
BlMmExtendedBiosDataArea = BlMmGetExtendedBiosDataArea();
|
|
|
|
//
|
|
// Get legacy CR3 value;
|
|
//
|
|
|
|
BlMmLegacyCr3 = BlMmGetCr3();
|
|
|
|
//
|
|
// Get initial GDTR.
|
|
//
|
|
|
|
BlMmGetGdtr(&BlMmInitialGdtr);
|
|
|
|
//
|
|
// Initialize memory map.
|
|
//
|
|
|
|
BlSmapInitialize();
|
|
|
|
//
|
|
// Create physical region lookaside.
|
|
//
|
|
|
|
BlRtlInitializeListHead(&BlMmPhysicalRegionList);
|
|
|
|
BlRtlInitializeListHead(&BlMmPhysicalRegionLookaside.FreeList);
|
|
|
|
for (Index = 0; Index < (sizeof(BlMmPhysicalRegionLookaside.StaticArray) / sizeof(BlMmPhysicalRegionLookaside.StaticArray[0])); Index += 1) {
|
|
|
|
BlRtlInsertTailList(&BlMmPhysicalRegionLookaside.FreeList, &BlMmPhysicalRegionLookaside.StaticArray[Index].Entry);
|
|
}
|
|
|
|
//
|
|
// Initialize page tables.
|
|
//
|
|
|
|
BlMmInitializePageTables();
|
|
|
|
//
|
|
// Create reserved BIOS region.
|
|
//
|
|
|
|
BlMmCreatePhysicalRegion(0,
|
|
BL_MM_BIOS_SIZE,
|
|
BL_MM_PHYSICAL_REGION_BIOS);
|
|
|
|
//
|
|
// Create free regions based on SMAP.
|
|
//
|
|
|
|
for (Index = 0; Index < BlSystemMemoryMap.EntryCount; Index += 1) {
|
|
|
|
SmapEntry = &BlSystemMemoryMap.Entry[Index];
|
|
|
|
//
|
|
// Don't use any memory below 1MB (BIOS area) and above 2GB (Singularity uses MSB for marking and such).
|
|
//
|
|
|
|
if ((SmapEntry->Type == BL_SMAP_AVAILABLE) &&
|
|
(SmapEntry->Base >= BL_MM_BIOS_SIZE) &&
|
|
(SmapEntry->Base < 0x80000000UI64)
|
|
) {
|
|
|
|
if ((SmapEntry->Base % PAGE_SIZE) != 0) {
|
|
|
|
Delta = PAGE_SIZE - (SmapEntry->Base % PAGE_SIZE);
|
|
SmapEntry->Base += Delta;
|
|
SmapEntry->Size -= Delta;
|
|
}
|
|
|
|
SmapEntry->Size &= (~(0xFFFUI64));
|
|
|
|
if ((SmapEntry->Base + SmapEntry->Size) > 0x80000000UI64) {
|
|
|
|
SmapEntry->Size = 0x80000000UI64 - SmapEntry->Base;
|
|
}
|
|
|
|
if (SmapEntry->Size > 0) {
|
|
|
|
BlMmCreatePhysicalRegion(SmapEntry->Base,
|
|
SmapEntry->Size,
|
|
BL_MM_PHYSICAL_REGION_FREE);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Initialize pool.
|
|
//
|
|
|
|
BlPoolInitialize();
|
|
|
|
//
|
|
// Add more entries to the physical region lookaside.
|
|
//
|
|
|
|
for (Index = 0; Index < 256; Index += 1) {
|
|
|
|
PhysicalRegion = (PBL_MM_PHYSICAL_REGION)
|
|
BlPoolAllocateBlock(sizeof(BL_MM_PHYSICAL_REGION));
|
|
|
|
BlRtlInsertTailList(&BlMmPhysicalRegionLookaside.FreeList, &PhysicalRegion->Entry);
|
|
}
|
|
|
|
return;
|
|
}
|