EFI: preliminary support for starting the kernel.

Enough to let the kernel to print hello, but not much beyond that.

Signed-off-by: Jessica Hamilton <[email protected]>
This commit is contained in:
Henry Harrington
2016-11-14 00:59:56 +13:00
committed by Jessica Hamilton
parent 2da1cb75a4
commit b3215a6275
7 changed files with 682 additions and 12 deletions
+4
View File
@@ -51,6 +51,10 @@ UsePrivateHeaders shared storage ;
DEFINES +=
BOOT_SUPPORT_ELF32
;
} else {
DEFINES +=
_BOOT_PLATFORM_EFI
;
}
}
}
+30 -1
View File
@@ -101,6 +101,15 @@ typedef ELFLoader<ELF32Class> ELF32Loader;
#ifdef BOOT_SUPPORT_ELF64
#ifdef _BOOT_PLATFORM_EFI
extern "C" status_t
platform_bootloader_address_to_kernel_address(void *address, uint64_t *_result);
extern "C" status_t
platform_kernel_address_to_bootloader_address(uint64_t address, void **_result);
#endif
struct ELF64Class {
static const uint8 kIdentClass = ELFCLASS64;
@@ -119,6 +128,17 @@ struct ELF64Class {
AllocateRegion(AddrType* _address, AddrType size, uint8 protection,
void **_mappedAddress)
{
#ifdef _BOOT_PLATFORM_EFI
void* address = (void*)*_address;
status_t status = platform_allocate_region(&address, size, protection,
false);
if (status != B_OK)
return status;
*_mappedAddress = address;
platform_bootloader_address_to_kernel_address(address, _address);
#else
// Assume the real 64-bit base address is KERNEL_LOAD_BASE_64_BIT and
// the mappings in the loader address space are at KERNEL_LOAD_BASE.
@@ -132,14 +152,23 @@ struct ELF64Class {
*_mappedAddress = address;
*_address = (AddrType)(addr_t)address + KERNEL_LOAD_BASE_64_BIT
- KERNEL_LOAD_BASE;
#endif
return B_OK;
}
static inline void*
Map(AddrType address)
{
#ifdef _BOOT_PLATFORM_EFI
void *result;
if (platform_kernel_address_to_bootloader_address(address, &result) != B_OK) {
panic("Couldn't convert address %#lx", address);
}
return result;
#else
return (void*)(addr_t)(address - KERNEL_LOAD_BASE_64_BIT
+ KERNEL_LOAD_BASE);
#endif
}
};
@@ -154,7 +183,7 @@ ELFLoader<Class>::Create(int fd, preloaded_image** _image)
ImageType* image = (ImageType*)kernel_args_malloc(sizeof(ImageType));
if (image == NULL)
return B_NO_MEMORY;
ssize_t length = read_pos(fd, 0, &image->elf_header, sizeof(EhdrType));
if (length < (ssize_t)sizeof(EhdrType)) {
kernel_args_free(image);
+2 -1
View File
@@ -8,7 +8,7 @@ UseBuildFeatureHeaders gnuefi : headersProtocol ;
UseBuildFeatureHeaders gnuefi : headersArch ;
{
local defines = _BOOT_MODE GNU_EFI_USE_MS_ABI _BOOT_PLATFORM=efi ;
local defines = _BOOT_MODE GNU_EFI_USE_MS_ABI _BOOT_PLATFORM_EFI ;
defines = [ FDefines $(defines) ] ;
SubDirCcFlags $(defines) ;
SubDirC++Flags $(defines) -fno-rtti ;
@@ -24,6 +24,7 @@ local platform_src =
console.cpp
video.cpp
debug.cpp
entry.S
mmu.cpp
heap.cpp
menu.cpp
+66
View File
@@ -0,0 +1,66 @@
/*
* Copyright 2012, Alex Smith, alex@alex-smith.me.uk.
* Copyright 2014, Henry Harrington, henry.harrington@gmail.com.
* Distributed under the terms of the MIT License.
*/
#include <asm_defs.h>
#define __x86_64__
#include <arch/x86/descriptors.h>
#include "mmu.h"
#undef __x86_64__
#define GDT_LIMIT 0x800
.code64
/*! void efi_enter_kernel(uint64 pml4, uint64 entry_point, uint64 stackTop); */
FUNCTION(efi_enter_kernel):
// Point CR3 to the kernel's PML4.
movq %rdi, %cr3
// Load 64-bit enabled GDT
lgdtq long_gdtr(%rip)
/*
// Jump into the 64-bit code segment.
ljmp $KERNEL_CODE_SELECTOR, $.Llmode
.align 8
.code64
.Llmode:
// Set data segments.
mov $KERNEL_DATA_SELECTOR, %ax
mov %ax, %ss
xor %ax, %ax
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
*/
// Set the stack pointer.
movq %rdx, %rsp
// Clear the stack frame/RFLAGS.
xorq %rbp, %rbp
push $2
popf
// Get arguments and call the kernel entry point.
mov %rsi, %rax // entry point
leaq gKernelArgs(%rip), %rdi
xorl %esi, %esi // current cpu
call *%rax
.data
long_gdtr:
.word BOOT_GDT_SEGMENT_COUNT * 8 - 1
SYMBOL(gLongGDT):
.quad 0
+300 -7
View File
@@ -1,31 +1,324 @@
/*
* Copyright 2016 Haiku, Inc. All rights reserved.
* Copyright 2014, Jessica Hamilton, [email protected].
* Copyright 2014, Henry Harrington, [email protected].
* Distributed under the terms of the MIT License.
*/
#include <algorithm>
#include <boot/platform.h>
#include <boot/stage2.h>
#include <kernel/arch/x86/arch_kernel.h>
#include <kernel/kernel.h>
#include "efi_platform.h"
#include "mmu.h"
struct allocated_memory_region {
allocated_memory_region *next;
uint64_t vaddr;
uint64_t paddr;
size_t size;
bool released;
};
static uint64_t next_virtual_address = KERNEL_LOAD_BASE_64_BIT + 32 * 1024 * 1024;
static allocated_memory_region *allocated_memory_regions = NULL;
static uint64_t mmu_allocate_page()
{
EFI_PHYSICAL_ADDRESS addr;
EFI_STATUS s = kBootServices->AllocatePages(AllocateAnyPages, EfiLoaderData, 1, &addr);
if (s != EFI_SUCCESS)
panic("Unabled to allocate memory: %li", s);
return addr;
}
uint64_t
mmu_generate_post_efi_page_tables(UINTN memory_map_size,
EFI_MEMORY_DESCRIPTOR *memory_map, UINTN descriptor_size,
UINTN descriptor_version)
{
// Generate page tables, matching bios_ia32/long.cpp.
uint64_t *pml4;
uint64_t *pdpt;
uint64_t *pageDir;
uint64_t *pageTable;
// Allocate the top level PML4.
pml4 = NULL;
if (platform_allocate_region((void**)&pml4, B_PAGE_SIZE, 0, false) != B_OK)
panic("Failed to allocate PML4.");
gKernelArgs.arch_args.phys_pgdir = (uint32_t)(addr_t)pml4;
memset(pml4, 0, B_PAGE_SIZE);
platform_bootloader_address_to_kernel_address(pml4, &gKernelArgs.arch_args.vir_pgdir);
// Store the virtual memory usage information.
gKernelArgs.virtual_allocated_range[0].start = KERNEL_LOAD_BASE_64_BIT;
gKernelArgs.virtual_allocated_range[0].size = next_virtual_address - KERNEL_LOAD_BASE_64_BIT;
gKernelArgs.num_virtual_allocated_ranges = 1;
gKernelArgs.arch_args.virtual_end = ROUNDUP(KERNEL_LOAD_BASE_64_BIT
+ gKernelArgs.virtual_allocated_range[0].size, 0x200000);
// Find the highest physical memory address. We map all physical memory
// into the kernel address space, so we want to make sure we map everything
// we have available.
uint64 maxAddress = 0;
for (UINTN i = 0; i < memory_map_size / descriptor_size; ++i) {
EFI_MEMORY_DESCRIPTOR *entry = (EFI_MEMORY_DESCRIPTOR *)((addr_t)memory_map + i * descriptor_size);
maxAddress = std::max(maxAddress,
entry->PhysicalStart + entry->NumberOfPages * 4096);
}
// Want to map at least 4GB, there may be stuff other than usable RAM that
// could be in the first 4GB of physical address space.
maxAddress = std::max(maxAddress, (uint64)0x100000000ll);
maxAddress = ROUNDUP(maxAddress, 0x40000000);
// Currently only use 1 PDPT (512GB). This will need to change if someone
// wants to use Haiku on a box with more than 512GB of RAM but that's
// probably not going to happen any time soon.
if (maxAddress / 0x40000000 > 512)
panic("Can't currently support more than 512GB of RAM!");
// Create page tables for the physical map area. Also map this PDPT
// temporarily at the bottom of the address space so that we are identity
// mapped.
pdpt = (uint64*)mmu_allocate_page();
memset(pdpt, 0, B_PAGE_SIZE);
pml4[510] = (addr_t)pdpt | kTableMappingFlags;
pml4[0] = (addr_t)pdpt | kTableMappingFlags;
for (uint64 i = 0; i < maxAddress; i += 0x40000000) {
pageDir = (uint64*)mmu_allocate_page();
memset(pageDir, 0, B_PAGE_SIZE);
pdpt[i / 0x40000000] = (addr_t)pageDir | kTableMappingFlags;
for (uint64 j = 0; j < 0x40000000; j += 0x200000) {
pageDir[j / 0x200000] = (i + j) | kLargePageMappingFlags;
}
}
// Allocate tables for the kernel mappings.
pdpt = (uint64*)mmu_allocate_page();
memset(pdpt, 0, B_PAGE_SIZE);
pml4[511] = (addr_t)pdpt | kTableMappingFlags;
pageDir = (uint64*)mmu_allocate_page();
memset(pageDir, 0, B_PAGE_SIZE);
pdpt[510] = (addr_t)pageDir | kTableMappingFlags;
// We can now allocate page tables and duplicate the mappings across from
// the 32-bit address space to them.
pageTable = NULL; // shush, compiler.
for (uint32 i = 0; i < gKernelArgs.virtual_allocated_range[0].size
/ B_PAGE_SIZE; i++) {
if ((i % 512) == 0) {
pageTable = (uint64*)mmu_allocate_page();
memset(pageTable, 0, B_PAGE_SIZE);
pageDir[i / 512] = (addr_t)pageTable | kTableMappingFlags;
}
// Get the physical address to map.
void *phys;
if (platform_kernel_address_to_bootloader_address(KERNEL_LOAD_BASE_64_BIT + (i * B_PAGE_SIZE),
&phys) != B_OK)
continue;
pageTable[i % 512] = (addr_t)phys | kPageMappingFlags;
}
return (uint64)pml4;
}
// Called after EFI boot services exit.
// Currently assumes that the memory map is sane... Sorted and no overlapping
// regions.
void
mmu_post_efi_setup(UINTN memory_map_size, EFI_MEMORY_DESCRIPTOR *memory_map, UINTN descriptor_size, UINTN descriptor_version)
{
// Add physical memory to the kernel args and update virtual addresses for EFI regions..
addr_t addr = (addr_t)memory_map;
gKernelArgs.num_physical_memory_ranges = 0;
for (UINTN i = 0; i < memory_map_size / descriptor_size; ++i) {
EFI_MEMORY_DESCRIPTOR *entry = (EFI_MEMORY_DESCRIPTOR *)(addr + i * descriptor_size);
switch (entry->Type) {
case EfiLoaderCode:
case EfiLoaderData:
case EfiBootServicesCode:
case EfiBootServicesData:
case EfiConventionalMemory: {
// Usable memory.
// Ignore memory below 1MB and above 512GB.
uint64_t base = entry->PhysicalStart;
uint64_t end = entry->PhysicalStart + entry->NumberOfPages * 4096;
if (base < 0x100000)
base = 0x100000;
if (end > (512ull * 1024 * 1024 * 1024))
end = 512ull * 1024 * 1024 * 1024;
if (base >= end)
break;
uint64_t size = end - base;
insert_physical_memory_range(base, size);
// LoaderData memory is bootloader allocated memory, possibly
// containing the kernel or loaded drivers.
if (entry->Type == EfiLoaderData)
insert_physical_allocated_range(base, size);
break;
}
case EfiACPIReclaimMemory:
// ACPI reclaim -- physical memory we could actually use later
gKernelArgs.ignored_physical_memory += entry->NumberOfPages * 4096;
break;
case EfiRuntimeServicesCode:
case EfiRuntimeServicesData:
entry->VirtualStart = entry->PhysicalStart + 0xFFFFFF0000000000ull;
break;
}
}
// Sort the address ranges.
sort_address_ranges(gKernelArgs.physical_memory_range,
gKernelArgs.num_physical_memory_ranges);
sort_address_ranges(gKernelArgs.physical_allocated_range,
gKernelArgs.num_physical_allocated_ranges);
sort_address_ranges(gKernelArgs.virtual_allocated_range,
gKernelArgs.num_virtual_allocated_ranges);
// Switch EFI to virtual mode, using the kernel pmap.
// Something involving ConvertPointer might need to be done after this?
// http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES#SetVirtualAddressMap.28.29
kRuntimeServices->SetVirtualAddressMap(memory_map_size, descriptor_size, descriptor_version, memory_map);
}
// Platform allocator.
// The bootloader assumes that bootloader address space == kernel address space.
// This is not true until just before the kernel is booted, so an ugly hack is
// used to cover the difference. platform_allocate_region allocates addresses
// in bootloader space, but can convert them to kernel space. The ELF loader
// accesses kernel memory via Mao(), and much later in the boot process,
// addresses in the kernel argument struct are converted from bootloader
// addresses to kernel addresses.
extern "C" status_t
platform_allocate_region(void **_address, size_t size, uint8 /* protection */, bool exactAddress)
{
// We don't have any control over the page tables, give up right away if an
// exactAddress is wanted.
if (exactAddress)
return B_NO_MEMORY;
EFI_PHYSICAL_ADDRESS addr;
size_t aligned_size = ROUNDUP(size, B_PAGE_SIZE);
allocated_memory_region *region = new(std::nothrow) allocated_memory_region;
if (region == NULL)
return B_NO_MEMORY;
EFI_STATUS status = kBootServices->AllocatePages(AllocateAnyPages,
EfiLoaderData, aligned_size / B_PAGE_SIZE, &addr);
if (status != EFI_SUCCESS) {
delete region;
return B_NO_MEMORY;
}
// Addresses above 512GB not supported.
// Memory map regions above 512GB can be ignored, but if EFI returns pages
// above that there's nothing that can be done to fix it.
if (addr + size > (512ull * 1024 * 1024 * 1024))
panic("Can't currently support more than 512GB of RAM!");
region->next = allocated_memory_regions;
allocated_memory_regions = region;
region->vaddr = 0;
region->paddr = addr;
region->size = size;
region->released = false;
if (*_address != NULL) {
region->vaddr = (uint64_t)*_address;
}
//dprintf("Allocated region %#lx (requested %p) %#lx %lu\n", region->vaddr, *_address, region->paddr, region->size);
*_address = (void *)region->paddr;
return B_OK;
}
static allocated_memory_region *
get_region(void *address, size_t size)
{
for (allocated_memory_region *region = allocated_memory_regions; region; region = region->next) {
if (region->paddr == (uint64_t)address && region->size == size) {
return region;
}
}
return 0;
}
extern "C" status_t
platform_allocate_region(void **_virtualAddress, size_t size, uint8 protection,
bool exactAddress)
platform_bootloader_address_to_kernel_address(void *address, uint64_t *_result)
{
if (kBootServices->AllocatePool(EfiLoaderData, size, _virtualAddress) != EFI_SUCCESS)
return B_NO_MEMORY;
uint64_t addr = (uint64_t)address;
return B_OK;
for (allocated_memory_region *region = allocated_memory_regions; region; region = region->next) {
if (region->paddr <= addr && addr < region->paddr + region->size) {
// Lazily allocate virtual memory.
if (region->vaddr == 0) {
region->vaddr = next_virtual_address;
next_virtual_address += ROUNDUP(region->size, B_PAGE_SIZE);
}
*_result = region->vaddr + (addr - region->paddr);
//dprintf("Converted bootloader address %p in region %#lx-%#lx to %#lx\n",
// address, region->paddr, region->paddr + region->size, *_result);
return B_OK;
}
}
return B_ERROR;
}
extern "C" status_t
platform_kernel_address_to_bootloader_address(uint64_t address, void **_result)
{
for (allocated_memory_region *region = allocated_memory_regions; region; region = region->next) {
if (region->vaddr != 0 && region->vaddr <= address && address < region->vaddr + region->size) {
*_result = (void *)(region->paddr + (address - region->vaddr));
//dprintf("Converted kernel address %#lx in region %#lx-%#lx to %p\n",
// address, region->vaddr, region->vaddr + region->size, *_result);
return B_OK;
}
}
return B_ERROR;
}
extern "C" status_t
platform_free_region(void *address, size_t size)
{
if (kBootServices->FreePool(address) != EFI_SUCCESS)
return B_ERROR;
//dprintf("Release region %p %lu\n", address, size);
allocated_memory_region *region = get_region(address, size);
if (!region)
panic("Unknown region??");
kBootServices->FreePages((EFI_PHYSICAL_ADDRESS)address, ROUNDUP(size, B_PAGE_SIZE) / B_PAGE_SIZE);
return B_OK;
}
+70
View File
@@ -0,0 +1,70 @@
/*
* Copyright 2014, Henry Harrington, [email protected].
* Distributed under the terms of the MIT License.
*/
#ifndef MMU_H
#define MMU_H
#include <arch/x86/descriptors.h>
#undef BOOT_GDT_SEGMENT_COUNT
#define BOOT_GDT_SEGMENT_COUNT (USER_DATA_SEGMENT + 1)
#ifndef _ASSEMBLER
#include "efi_platform.h"
#include <util/FixedWidthPointer.h>
extern segment_descriptor gBootGDT[BOOT_GDT_SEGMENT_COUNT];
static const uint32 kDefaultPageFlags = 0x3; // present, R/W
static const uint64 kTableMappingFlags = 0x7; // present, R/W, user
static const uint64 kLargePageMappingFlags = 0x183; // present, R/W, user, global, large
static const uint64 kPageMappingFlags = 0x103; // present, R/W, user, global
#ifdef __cplusplus
extern "C" {
#endif
extern void
mmu_post_efi_setup(UINTN memory_map_size, EFI_MEMORY_DESCRIPTOR *memory_map, UINTN descriptor_size, UINTN descriptor_version);
extern uint64_t
mmu_generate_post_efi_page_tables(UINTN memory_map_size, EFI_MEMORY_DESCRIPTOR *memory_map, UINTN descriptor_size, UINTN descriptor_version);
extern status_t
platform_kernel_address_to_bootloader_address(uint64_t address, void **_result);
extern status_t
platform_bootloader_address_to_kernel_address(void *address, uint64_t *_result);
#ifdef __cplusplus
}
#endif
/*! Convert a 32-bit address to a 64-bit address. */
inline uint64
fix_address(uint64 address)
{
uint64 result;
if (platform_bootloader_address_to_kernel_address((void *)address, &result) != B_OK)
return address;
else
return result;
}
template<typename Type>
inline void
fix_address(FixedWidthPointer<Type>& p)
{
if (p != NULL)
p.SetTo(fix_address(p.Get()));
}
#endif // !_ASSEMBLER
#endif /* MMU_H */
+210 -3
View File
@@ -1,16 +1,26 @@
/*
* Copyright 2014-2016 Haiku, Inc. All rights reserved.
* Copyright 2013 Fredrik Holmqvist, [email protected]. All rights reserved.
* Distributed under the terms of the MIT License.
* Copyright 2013-2014, Fredrik Holmqvist, [email protected].
* Copyright 2014, Henry Harrington, [email protected].
* All rights reserved.
* Distributed under the terms of the Haiku License.
*/
#include <string.h>
#include <KernelExport.h>
#include <arch/cpu.h>
#include <arch/x86/descriptors.h>
#include <boot/platform.h>
#include <boot/stage2.h>
#include <boot/stdio.h>
#include <kernel.h>
#include "console.h"
#include "efi_platform.h"
#include "mmu.h"
extern void (*__ctor_list)(void);
@@ -20,8 +30,18 @@ extern void (*__ctor_end)(void);
const EFI_SYSTEM_TABLE *kSystemTable;
const EFI_BOOT_SERVICES *kBootServices;
const EFI_RUNTIME_SERVICES *kRuntimeServices;
EFI_HANDLE kImage;
static uint32 sBootOptions;
static uint64 gLongKernelEntry;
extern uint64 gLongGDT;
segment_descriptor gBootGDT[BOOT_GDT_SEGMENT_COUNT];
extern "C" int main(stage2_args *args);
extern "C" void _start(void);
extern "C" void efi_enter_kernel(uint64 pml4, uint64 entry_point, uint64 stack);
static void
@@ -41,10 +61,188 @@ platform_boot_options()
}
static void
long_gdt_init()
{
clear_segment_descriptor(&gBootGDT[0]);
// Set up code/data segments (TSS segments set up later in the kernel).
set_segment_descriptor(&gBootGDT[KERNEL_CODE_SEGMENT], DT_CODE_EXECUTE_ONLY,
DPL_KERNEL);
set_segment_descriptor(&gBootGDT[KERNEL_DATA_SEGMENT], DT_DATA_WRITEABLE,
DPL_KERNEL);
set_segment_descriptor(&gBootGDT[USER_CODE_SEGMENT], DT_CODE_EXECUTE_ONLY,
DPL_USER);
set_segment_descriptor(&gBootGDT[USER_DATA_SEGMENT], DT_DATA_WRITEABLE,
DPL_USER);
// Used by long_enter_kernel().
gLongGDT = fix_address((addr_t)gBootGDT);
dprintf("GDT at 0x%lx\n", gLongGDT);
}
static void
convert_preloaded_image(preloaded_elf64_image* image)
{
fix_address(image->next);
fix_address(image->name);
fix_address(image->debug_string_table);
fix_address(image->syms);
fix_address(image->rel);
fix_address(image->rela);
fix_address(image->pltrel);
fix_address(image->debug_symbols);
}
/*! Convert all addresses in kernel_args to 64-bit addresses. */
static void
convert_kernel_args()
{
fix_address(gKernelArgs.boot_volume);
fix_address(gKernelArgs.vesa_modes);
fix_address(gKernelArgs.edid_info);
fix_address(gKernelArgs.debug_output);
fix_address(gKernelArgs.boot_splash);
fix_address(gKernelArgs.arch_args.apic);
fix_address(gKernelArgs.arch_args.hpet);
convert_preloaded_image(static_cast<preloaded_elf64_image*>(
gKernelArgs.kernel_image.Pointer()));
fix_address(gKernelArgs.kernel_image);
// Iterate over the preloaded images. Must save the next address before
// converting, as the next pointer will be converted.
preloaded_image* image = gKernelArgs.preloaded_images;
fix_address(gKernelArgs.preloaded_images);
while (image != NULL) {
preloaded_image* next = image->next;
convert_preloaded_image(static_cast<preloaded_elf64_image*>(image));
image = next;
}
// Set correct kernel args range addresses.
dprintf("kernel args ranges:\n");
for (uint32 i = 0; i < gKernelArgs.num_kernel_args_ranges; i++) {
gKernelArgs.kernel_args_range[i].start = fix_address(
gKernelArgs.kernel_args_range[i].start);
dprintf(" base %#018" B_PRIx64 ", length %#018" B_PRIx64 "\n",
gKernelArgs.kernel_args_range[i].start,
gKernelArgs.kernel_args_range[i].size);
}
// Fix driver settings files.
driver_settings_file* file = gKernelArgs.driver_settings;
fix_address(gKernelArgs.driver_settings);
while (file != NULL) {
driver_settings_file* next = file->next;
fix_address(file->next);
fix_address(file->buffer);
file = next;
}
}
extern "C" void
platform_start_kernel(void)
{
panic("platform_start_kernel not implemented");
if (gKernelArgs.kernel_image->elf_class != ELFCLASS64)
panic("32-bit kernels not supported with EFI");
preloaded_elf64_image *image = static_cast<preloaded_elf64_image *>(
gKernelArgs.kernel_image.Pointer());
long_gdt_init();
convert_kernel_args();
// Save the kernel entry point address.
gLongKernelEntry = image->elf_header.e_entry;
dprintf("kernel entry at %#lx\n", gLongKernelEntry);
// map in a kernel stack
void *stack_address = NULL;
if (platform_allocate_region(&stack_address, KERNEL_STACK_SIZE + KERNEL_STACK_GUARD_PAGES * B_PAGE_SIZE, 0, false) != B_OK) {
panic("Unabled to allocate a stack");
}
gKernelArgs.cpu_kstack[0].start = fix_address((uint64_t)stack_address);
gKernelArgs.cpu_kstack[0].size = KERNEL_STACK_SIZE + KERNEL_STACK_GUARD_PAGES * B_PAGE_SIZE;
dprintf("Kernel stack at %#lx\n", gKernelArgs.cpu_kstack[0].start);
// Prepare to exit EFI boot services.
// Read the memory map.
// First call is to determine the buffer size.
UINTN memory_map_size = 0;
EFI_MEMORY_DESCRIPTOR dummy;
EFI_MEMORY_DESCRIPTOR *memory_map;
UINTN map_key;
UINTN descriptor_size;
UINT32 descriptor_version;
if (kBootServices->GetMemoryMap(&memory_map_size, &dummy, &map_key, &descriptor_size, &descriptor_version) != EFI_BUFFER_TOO_SMALL) {
panic("Unable to determine size of system memory map");
}
// Allocate a buffer twice as large as needed just in case it gets bigger between
// calls to ExitBootServices.
UINTN actual_memory_map_size = memory_map_size * 2;
memory_map = (EFI_MEMORY_DESCRIPTOR *)kernel_args_malloc(actual_memory_map_size);
if (memory_map == NULL)
panic("Unable to allocate memory map.");
// Read (and print) the memory map.
memory_map_size = actual_memory_map_size;
if (kBootServices->GetMemoryMap(&memory_map_size, memory_map, &map_key, &descriptor_size, &descriptor_version) != EFI_SUCCESS) {
panic("Unable to fetch system memory map.");
}
addr_t addr = (addr_t)memory_map;
dprintf("System provided memory map:\n");
for (UINTN i = 0; i < memory_map_size / descriptor_size; ++i) {
EFI_MEMORY_DESCRIPTOR *entry = (EFI_MEMORY_DESCRIPTOR *)(addr + i * descriptor_size);
dprintf(" %#lx-%#lx %#lx %#x %#lx\n",
entry->PhysicalStart, entry->PhysicalStart + entry->NumberOfPages * 4096,
entry->VirtualStart, entry->Type, entry->Attribute);
}
// Generate page tables for use after ExitBootServices.
uint64_t final_pml4 = mmu_generate_post_efi_page_tables(memory_map_size, memory_map, descriptor_size, descriptor_version);
dprintf("Final PML4 at %#lx\n", final_pml4);
// Attempt to fetch the memory map and exit boot services.
// This needs to be done in a loop, as ExitBootServices can change the
// memory map.
// Even better: Only GetMemoryMap and ExitBootServices can be called after
// the first call to ExitBootServices, as the firmware is permitted to
// partially exit. This is why twice as much space was allocated for the
// memory map, as it's impossible to allocate more now.
// A changing memory map shouldn't affect the generated page tables, as
// they only needed to know about the maximum address, not any specific
// entry.
dprintf("Calling ExitBootServices. So long, EFI!\n");
while (true) {
if (kBootServices->ExitBootServices(kImage, map_key) == EFI_SUCCESS) {
break;
}
memory_map_size = actual_memory_map_size;
if (kBootServices->GetMemoryMap(&memory_map_size, memory_map, &map_key, &descriptor_size, &descriptor_version) != EFI_SUCCESS) {
panic("Unable to fetch system memory map.");
}
}
// We're on our own now...
// The console was provided by boot services, disable it.
stdout = NULL;
// Update EFI, generate final kernel physical memory map, etc.
mmu_post_efi_setup(memory_map_size, memory_map, descriptor_size, descriptor_version);
// Enter the kernel!
efi_enter_kernel(final_pml4,
gLongKernelEntry,
gKernelArgs.cpu_kstack[0].start + gKernelArgs.cpu_kstack[0].size);
panic("Shouldn't get here");
}
@@ -65,6 +263,9 @@ efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *systemTable)
{
stage2_args args;
memset(&args, 0, sizeof(stage2_args));
kImage = image;
kSystemTable = systemTable;
kBootServices = systemTable->BootServices;
kRuntimeServices = systemTable->RuntimeServices;
@@ -77,6 +278,12 @@ efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *systemTable)
sBootOptions = console_check_boot_keys();
// disable apm in case we ever load a 32-bit kernel...
gKernelArgs.platform_args.apm.version = 0;
gKernelArgs.num_cpus = 1;
gKernelArgs.arch_args.hpet_phys = 0;
gKernelArgs.arch_args.hpet = NULL;
main(&args);
return EFI_SUCCESS;