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:
committed by
Jessica Hamilton
parent
2da1cb75a4
commit
b3215a6275
@@ -51,6 +51,10 @@ UsePrivateHeaders shared storage ;
|
||||
DEFINES +=
|
||||
BOOT_SUPPORT_ELF32
|
||||
;
|
||||
} else {
|
||||
DEFINES +=
|
||||
_BOOT_PLATFORM_EFI
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user