reactos/boot/freeldr/freeldr/ntldr/arch/amd64/winldr.c
Mark Harmstone dd6c1c8843 [FREELDR] Allow Freeloader to boot Vista revamp of PR #1905 (#6479)
[FREELDR] Add "WindowsVista" boot type
[FREELDR] Set GDT correctly for Vista
[FREELDR] Map first page of memory, this is an observed behavior, and
also increases stability boot Checked windows 2003 SP2 ntoskrnl with
freeloader.
[SDK] Don't assert on big keys in bootloader

Co-authored-by: Justin Miller <justin.miller@reactos.org>
2024-04-18 09:28:54 -07:00

434 lines
11 KiB
C

/*
* PROJECT: EFI Windows Loader
* LICENSE: GPL - See COPYING in the top level directory
* FILE: boot/freeldr/freeldr/arch/amd64/winldr.c
* PURPOSE: Memory related routines
* PROGRAMMERS: Timo Kreuzer (timo.kreuzer@reactos.org)
*/
/* INCLUDES ***************************************************************/
#include <freeldr.h>
#include <ndk/asm.h>
#include <internal/amd64/intrin_i.h>
#include "../../winldr.h"
#include <debug.h>
DBG_DEFAULT_CHANNEL(WINDOWS);
//extern ULONG LoaderPagesSpanned;
/* GLOBALS ***************************************************************/
PHARDWARE_PTE PxeBase;
//PHARDWARE_PTE HalPageTable;
PVOID GdtIdt;
ULONG_PTR PcrBasePage;
ULONG_PTR TssBasePage;
/* FUNCTIONS **************************************************************/
static
BOOLEAN
MempAllocatePageTables(VOID)
{
TRACE(">>> MempAllocatePageTables\n");
/* Allocate a page for the PML4 */
PxeBase = MmAllocateMemoryWithType(PAGE_SIZE, LoaderMemoryData);
if (!PxeBase)
{
ERR("failed to allocate PML4\n");
return FALSE;
}
// FIXME: Physical PTEs = FirmwareTemporary ?
/* Zero the PML4 */
RtlZeroMemory(PxeBase, PAGE_SIZE);
/* The page tables are located at 0xfffff68000000000
* We create a recursive self mapping through all 4 levels at
* virtual address 0xfffff6fb7dbedf68 */
PxeBase[VAtoPXI(PXE_BASE)].Valid = 1;
PxeBase[VAtoPXI(PXE_BASE)].Write = 1;
PxeBase[VAtoPXI(PXE_BASE)].PageFrameNumber = PtrToPfn(PxeBase);
// FIXME: map PDE's for hals memory mapping
TRACE(">>> leave MempAllocatePageTables\n");
return TRUE;
}
static
PHARDWARE_PTE
MempGetOrCreatePageDir(PHARDWARE_PTE PdeBase, ULONG Index)
{
PHARDWARE_PTE SubDir;
if (!PdeBase)
return NULL;
if (!PdeBase[Index].Valid)
{
SubDir = MmAllocateMemoryWithType(PAGE_SIZE, LoaderMemoryData);
if (!SubDir)
return NULL;
RtlZeroMemory(SubDir, PAGE_SIZE);
PdeBase[Index].PageFrameNumber = PtrToPfn(SubDir);
PdeBase[Index].Valid = 1;
PdeBase[Index].Write = 1;
}
else
{
SubDir = (PVOID)((ULONG64)(PdeBase[Index].PageFrameNumber) * PAGE_SIZE);
}
return SubDir;
}
static
BOOLEAN
MempMapSinglePage(ULONG64 VirtualAddress, ULONG64 PhysicalAddress)
{
PHARDWARE_PTE PpeBase, PdeBase, PteBase;
ULONG Index;
PpeBase = MempGetOrCreatePageDir(PxeBase, VAtoPXI(VirtualAddress));
PdeBase = MempGetOrCreatePageDir(PpeBase, VAtoPPI(VirtualAddress));
PteBase = MempGetOrCreatePageDir(PdeBase, VAtoPDI(VirtualAddress));
if (!PteBase)
{
ERR("!!!No Dir %p, %p, %p, %p\n", PxeBase, PpeBase, PdeBase, PteBase);
return FALSE;
}
Index = VAtoPTI(VirtualAddress);
if (PteBase[Index].Valid)
{
ERR("!!!Already mapped %ld\n", Index);
return FALSE;
}
PteBase[Index].Valid = 1;
PteBase[Index].Write = 1;
PteBase[Index].PageFrameNumber = PhysicalAddress / PAGE_SIZE;
return TRUE;
}
BOOLEAN
MempIsPageMapped(PVOID VirtualAddress)
{
PHARDWARE_PTE PpeBase, PdeBase, PteBase;
ULONG Index;
Index = VAtoPXI(VirtualAddress);
if (!PxeBase[Index].Valid)
return FALSE;
PpeBase = (PVOID)((ULONG64)(PxeBase[Index].PageFrameNumber) * PAGE_SIZE);
Index = VAtoPPI(VirtualAddress);
if (!PpeBase[Index].Valid)
return FALSE;
PdeBase = (PVOID)((ULONG64)(PpeBase[Index].PageFrameNumber) * PAGE_SIZE);
Index = VAtoPDI(VirtualAddress);
if (!PdeBase[Index].Valid)
return FALSE;
PteBase = (PVOID)((ULONG64)(PdeBase[Index].PageFrameNumber) * PAGE_SIZE);
Index = VAtoPTI(VirtualAddress);
if (!PteBase[Index].Valid)
return FALSE;
return TRUE;
}
static
PFN_NUMBER
MempMapRangeOfPages(ULONG64 VirtualAddress, ULONG64 PhysicalAddress, PFN_NUMBER cPages)
{
PFN_NUMBER i;
for (i = 0; i < cPages; i++)
{
if (!MempMapSinglePage(VirtualAddress, PhysicalAddress))
{
ERR("Failed to map page %ld from %p to %p\n",
i, (PVOID)VirtualAddress, (PVOID)PhysicalAddress);
return i;
}
VirtualAddress += PAGE_SIZE;
PhysicalAddress += PAGE_SIZE;
}
return i;
}
BOOLEAN
MempSetupPaging(IN PFN_NUMBER StartPage,
IN PFN_NUMBER NumberOfPages,
IN BOOLEAN KernelMapping)
{
TRACE(">>> MempSetupPaging(0x%lx, %ld, %p)\n",
StartPage, NumberOfPages, StartPage * PAGE_SIZE + KSEG0_BASE);
/* Identity mapping */
if (MempMapRangeOfPages(StartPage * PAGE_SIZE,
StartPage * PAGE_SIZE,
NumberOfPages) != NumberOfPages)
{
ERR("Failed to map pages %ld, %ld\n",
StartPage, NumberOfPages);
return FALSE;
}
/* Kernel mapping */
if (KernelMapping)
{
if (MempMapRangeOfPages(StartPage * PAGE_SIZE + KSEG0_BASE,
StartPage * PAGE_SIZE,
NumberOfPages) != NumberOfPages)
{
ERR("Failed to map pages %ld, %ld\n",
StartPage, NumberOfPages);
return FALSE;
}
}
return TRUE;
}
VOID
MempUnmapPage(PFN_NUMBER Page)
{
// TRACE(">>> MempUnmapPage\n");
}
static
VOID
WinLdrpMapApic(VOID)
{
BOOLEAN LocalAPIC;
LARGE_INTEGER MsrValue;
ULONG CpuInfo[4];
ULONG64 APICAddress;
TRACE(">>> WinLdrpMapApic\n");
/* Check if we have a local APIC */
__cpuid((int*)CpuInfo, 1);
LocalAPIC = (((CpuInfo[3] >> 9) & 1) != 0);
/* If there is no APIC, just return */
if (!LocalAPIC)
{
WARN("No APIC found.\n");
return;
}
/* Read the APIC Address */
MsrValue.QuadPart = __readmsr(0x1B);
APICAddress = (MsrValue.LowPart & 0xFFFFF000);
TRACE("Local APIC detected at address 0x%x\n",
APICAddress);
/* Map it */
MempMapSinglePage(APIC_BASE, APICAddress);
}
static
BOOLEAN
WinLdrMapSpecialPages(VOID)
{
PHARDWARE_PTE PpeBase, PdeBase;
/* Map the PCR page */
if (!MempMapSinglePage(KIP0PCRADDRESS, PcrBasePage * PAGE_SIZE))
{
ERR("Could not map PCR @ %lx\n", PcrBasePage);
return FALSE;
}
/* Map KI_USER_SHARED_DATA */
if (!MempMapSinglePage(KI_USER_SHARED_DATA, (PcrBasePage+1) * PAGE_SIZE))
{
ERR("Could not map KI_USER_SHARED_DATA\n");
return FALSE;
}
/* Map the APIC page */
WinLdrpMapApic();
/* Map the page tables for 4 MB HAL address space. */
PpeBase = MempGetOrCreatePageDir(PxeBase, VAtoPXI(MM_HAL_VA_START));
PdeBase = MempGetOrCreatePageDir(PpeBase, VAtoPPI(MM_HAL_VA_START));
MempGetOrCreatePageDir(PdeBase, VAtoPDI(MM_HAL_VA_START));
MempGetOrCreatePageDir(PdeBase, VAtoPDI(MM_HAL_VA_START + 2 * 1024 * 1024));
return TRUE;
}
static
VOID
Amd64SetupGdt(PVOID GdtBase, ULONG64 TssBase)
{
PKGDTENTRY64 Entry;
KDESCRIPTOR GdtDesc;
TRACE("Amd64SetupGdt(GdtBase = %p, TssBase = %p)\n", GdtBase, TssBase);
/* Setup KGDT64_NULL */
Entry = KiGetGdtEntry(GdtBase, KGDT64_NULL);
*(PULONG64)Entry = 0x0000000000000000ULL;
/* Setup KGDT64_R0_CODE */
Entry = KiGetGdtEntry(GdtBase, KGDT64_R0_CODE);
*(PULONG64)Entry = 0x00209b0000000000ULL;
/* Setup KGDT64_R0_DATA */
Entry = KiGetGdtEntry(GdtBase, KGDT64_R0_DATA);
*(PULONG64)Entry = 0x00cf93000000ffffULL;
/* Setup KGDT64_R3_CMCODE */
Entry = KiGetGdtEntry(GdtBase, KGDT64_R3_CMCODE);
*(PULONG64)Entry = 0x00cffb000000ffffULL;
/* Setup KGDT64_R3_DATA */
Entry = KiGetGdtEntry(GdtBase, KGDT64_R3_DATA);
*(PULONG64)Entry = 0x00cff3000000ffffULL;
/* Setup KGDT64_R3_CODE */
Entry = KiGetGdtEntry(GdtBase, KGDT64_R3_CODE);
*(PULONG64)Entry = 0x0020fb0000000000ULL;
/* Setup KGDT64_R3_CMTEB */
Entry = KiGetGdtEntry(GdtBase, KGDT64_R3_CMTEB);
*(PULONG64)Entry = 0xff40f3fd50003c00ULL;
/* Setup TSS entry */
Entry = KiGetGdtEntry(GdtBase, KGDT64_SYS_TSS);
KiInitGdtEntry(Entry, TssBase, sizeof(KTSS), I386_TSS, 0);
/* Setup GDT descriptor */
GdtDesc.Base = GdtBase;
GdtDesc.Limit = NUM_GDT * sizeof(KGDTENTRY) - 1;
/* Set the new Gdt */
__lgdt(&GdtDesc.Limit);
TRACE("Leave Amd64SetupGdt()\n");
}
static
VOID
Amd64SetupIdt(PVOID IdtBase)
{
KDESCRIPTOR IdtDesc, OldIdt;
//ULONG Size;
TRACE("Amd64SetupIdt(IdtBase = %p)\n", IdtBase);
/* Get old IDT */
__sidt(&OldIdt.Limit);
/* Copy the old IDT */
//Size = min(OldIdt.Limit + 1, NUM_IDT * sizeof(KIDTENTRY));
//RtlCopyMemory(IdtBase, (PVOID)OldIdt.Base, Size);
/* Setup the new IDT descriptor */
IdtDesc.Base = IdtBase;
IdtDesc.Limit = NUM_IDT * sizeof(KIDTENTRY) - 1;
/* Set the new IDT */
__lidt(&IdtDesc.Limit);
TRACE("Leave Amd64SetupIdt()\n");
}
VOID
WinLdrSetProcessorContext(
_In_ USHORT OperatingSystemVersion)
{
TRACE("WinLdrSetProcessorContext\n");
/* Disable Interrupts */
_disable();
/* Re-initialize EFLAGS */
__writeeflags(0);
/* Set the new PML4 */
__writecr3((ULONG64)PxeBase);
/* Get kernel mode address of gdt / idt */
GdtIdt = (PVOID)((ULONG64)GdtIdt + KSEG0_BASE);
/* Create gdt entries and load gdtr */
Amd64SetupGdt(GdtIdt, KSEG0_BASE | (TssBasePage << MM_PAGE_SHIFT));
/* Copy old Idt and set idtr */
Amd64SetupIdt((PVOID)((ULONG64)GdtIdt + NUM_GDT * sizeof(KGDTENTRY)));
/* LDT is unused */
// __lldt(0);
/* Load TSR */
__ltr(KGDT64_SYS_TSS);
TRACE("leave WinLdrSetProcessorContext\n");
}
void WinLdrSetupMachineDependent(PLOADER_PARAMETER_BLOCK LoaderBlock)
{
ULONG_PTR Pcr = 0;
ULONG_PTR Tss = 0;
ULONG BlockSize, NumPages;
LoaderBlock->u.I386.CommonDataArea = (PVOID)DbgPrint; // HACK
LoaderBlock->u.I386.MachineType = MACHINE_TYPE_ISA;
/* Allocate 2 pages for PCR */
Pcr = (ULONG_PTR)MmAllocateMemoryWithType(2 * MM_PAGE_SIZE, LoaderStartupPcrPage);
PcrBasePage = Pcr >> MM_PAGE_SHIFT;
if (Pcr == 0)
{
UiMessageBox("Can't allocate PCR.");
return;
}
RtlZeroMemory((PVOID)Pcr, 2 * MM_PAGE_SIZE);
/* Allocate TSS */
BlockSize = (sizeof(KTSS) + MM_PAGE_SIZE) & ~(MM_PAGE_SIZE - 1);
Tss = (ULONG_PTR)MmAllocateMemoryWithType(BlockSize, LoaderMemoryData);
TssBasePage = Tss >> MM_PAGE_SHIFT;
/* Allocate space for new GDT + IDT */
BlockSize = NUM_GDT * sizeof(KGDTENTRY) + NUM_IDT * sizeof(KIDTENTRY);
NumPages = (BlockSize + MM_PAGE_SIZE - 1) >> MM_PAGE_SHIFT;
GdtIdt = (PKGDTENTRY)MmAllocateMemoryWithType(NumPages * MM_PAGE_SIZE, LoaderMemoryData);
if (GdtIdt == NULL)
{
UiMessageBox("Can't allocate pages for GDT+IDT!");
return;
}
/* Zero newly prepared GDT+IDT */
RtlZeroMemory(GdtIdt, NumPages << MM_PAGE_SHIFT);
// Before we start mapping pages, create a block of memory, which will contain
// PDE and PTEs
if (MempAllocatePageTables() == FALSE)
{
// FIXME: bugcheck
}
/* Map stuff like PCR, KI_USER_SHARED_DATA and Apic */
WinLdrMapSpecialPages();
}
VOID
MempDump(VOID)
{
}