EFI: Make our haiku_loader architecture agnostic
* This is the bulk of the work. Anything else should be minor cleanups and tweaking. * riscv64 isn't a viable EFI platform yet.. just acting as a stand-in to test a non-x86 EFI haiku_loader Change-Id: Ib03de81e2b562e693987b86d7b4318209fb1c792 Reviewed-on: https://review.haiku-os.org/c/haiku/+/2256 Reviewed-by: Alex von Gluck IV <[email protected]> Reviewed-by: Adrien Destugues <[email protected]>
This commit is contained in:
committed by
Alex von Gluck IV
parent
35b4b2373a
commit
04f1baa771
@@ -302,8 +302,8 @@ rule KernelArchitectureSetup architecture
|
||||
}
|
||||
|
||||
case riscv64 :
|
||||
HAIKU_KERNEL_PLATFORM ?= u-boot ;
|
||||
HAIKU_BOOT_TARGETS += u-boot ;
|
||||
HAIKU_KERNEL_PLATFORM ?= efi ;
|
||||
HAIKU_BOOT_TARGETS += efi ;
|
||||
|
||||
HAIKU_BOOT_SDIMAGE_SIZE ?= 128 ;
|
||||
# SOC's like allwinner need an offset to skip the hardcoded initial loader
|
||||
|
||||
@@ -9,6 +9,10 @@
|
||||
# error This file is included from <boot/kernel_args.h> only
|
||||
#endif
|
||||
|
||||
|
||||
#include <util/FixedWidthPointer.h>
|
||||
|
||||
|
||||
#define _PACKED __attribute__((packed))
|
||||
|
||||
#define MAX_VIRTUAL_RANGES_TO_KEEP 32
|
||||
@@ -21,9 +25,14 @@ typedef struct {
|
||||
uint64 vir_pgdir;
|
||||
uint64 next_pagetable;
|
||||
|
||||
uint64 virtual_end;
|
||||
|
||||
// The virtual ranges we want to keep in the kernel.
|
||||
uint32 num_virtual_ranges_to_keep;
|
||||
addr_range virtual_ranges_to_keep[MAX_VIRTUAL_RANGES_TO_KEEP];
|
||||
} arch_kernel_args;
|
||||
|
||||
// needed for UEFI, otherwise kernel acpi support can't find ACPI root
|
||||
FixedWidthPointer<void> acpi_root;
|
||||
} _PACKED arch_kernel_args;
|
||||
|
||||
#endif /* KERNEL_ARCH_RISCV64_KERNEL_ARGS_H */
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2019 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef KERNEL_BOOT_PLATFORM_EFI_ARCH_MMU_H
|
||||
#define KERNEL_BOOT_PLATFORM_EFI_ARCH_MMU_H
|
||||
|
||||
|
||||
void arch_mmu_init();
|
||||
|
||||
|
||||
#endif /* KERNEL_BOOT_PLATFORM_EFI_ARCH_MMU_H */
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Copyright 2019-2020, Haiku, Inc. All rights reserved.
|
||||
* Released under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef __ARCH_START_H
|
||||
#define __ARCH_START_H
|
||||
|
||||
|
||||
void arch_start_kernel(addr_t kernelEntry);
|
||||
|
||||
|
||||
#endif /* __ARCH_START_H */
|
||||
@@ -20,6 +20,8 @@ status_t
|
||||
arch_debug_get_instruction_pointer(debug_context *context, thread_id thread,
|
||||
void **ip, void **stackFrameAddress)
|
||||
{
|
||||
#warning TODO RISCV64 get instruction pointer
|
||||
#if 0
|
||||
// get the CPU state
|
||||
debug_cpu_state cpuState;
|
||||
status_t error = debug_get_cpu_state(context, thread, NULL, &cpuState);
|
||||
@@ -28,6 +30,7 @@ arch_debug_get_instruction_pointer(debug_context *context, thread_id thread,
|
||||
|
||||
*ip = (void*)cpuState.rip;
|
||||
*stackFrameAddress = (void*)cpuState.rbp;
|
||||
#endif
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
@@ -37,6 +40,8 @@ status_t
|
||||
arch_debug_get_stack_frame(debug_context *context, void *stackFrameAddress,
|
||||
debug_stack_frame_info *stackFrameInfo)
|
||||
{
|
||||
#warning TODO RISCV64 get stack frame
|
||||
#if 0
|
||||
stack_frame stackFrame;
|
||||
ssize_t bytesRead = debug_read_memory(context, stackFrameAddress,
|
||||
&stackFrame, sizeof(stackFrame));
|
||||
@@ -48,5 +53,7 @@ arch_debug_get_stack_frame(debug_context *context, void *stackFrameAddress,
|
||||
stackFrameInfo->frame = stackFrameAddress;
|
||||
stackFrameInfo->parent_frame = stackFrame.previous;
|
||||
stackFrameInfo->return_address = stackFrame.return_address;
|
||||
#endif
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
@@ -29,8 +29,6 @@ local platform_src =
|
||||
quirks.cpp
|
||||
smp.cpp
|
||||
serial.cpp
|
||||
smp_trampoline.S
|
||||
support.S
|
||||
;
|
||||
|
||||
local platform ;
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
SubDir HAIKU_TOP src system boot platform efi arch riscv64 ;
|
||||
|
||||
SubDirHdrs $(HAIKU_TOP) src system boot platform efi ;
|
||||
|
||||
UsePrivateHeaders [ FDirName kernel platform ] ;
|
||||
UsePrivateHeaders [ FDirName kernel boot platform efi ] ;
|
||||
|
||||
local arch_src =
|
||||
crt0-efi-$(TARGET_ARCH).S
|
||||
#entry.S
|
||||
relocation_func.cpp
|
||||
#arch_smp.cpp
|
||||
#arch_mmu.cpp
|
||||
#arch_timer.cpp
|
||||
;
|
||||
|
||||
BootMergeObject boot_platform_efi_riscv64.o :
|
||||
$(arch_src)
|
||||
;
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright, 2019, Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Alexander von Gluck IV <[email protected]>
|
||||
*/
|
||||
|
||||
|
||||
#include "arch_timer.h"
|
||||
|
||||
#include <KernelExport.h>
|
||||
|
||||
#include <kernel.h>
|
||||
#include <safemode.h>
|
||||
#include <boot/stage2.h>
|
||||
#include <boot/menu.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
//#define TRACE_TIMER
|
||||
#ifdef TRACE_TIMER
|
||||
# define TRACE(x) dprintf x
|
||||
#else
|
||||
# define TRACE(x) ;
|
||||
#endif
|
||||
|
||||
|
||||
void
|
||||
arch_timer_init(void)
|
||||
{
|
||||
// Stub
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright 2008, Dustin Howett, [email protected]. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef HPET_H
|
||||
#define HPET_H
|
||||
|
||||
#include <SupportDefs.h>
|
||||
#include <arch/x86/arch_hpet.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void arch_timer_init(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* HPET_H */
|
||||
@@ -0,0 +1,101 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* reloc_riscv.c - position independent ELF shared object relocator
|
||||
Copyright (C) 2018 Alexander Graf <[email protected]>
|
||||
Copyright (C) 2014 Linaro Ltd. <[email protected]>
|
||||
Copyright (C) 1999 Hewlett-Packard Co.
|
||||
Contributed by David Mosberger <[email protected]>.
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials
|
||||
provided with the distribution.
|
||||
* Neither the name of Hewlett-Packard Co. nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
|
||||
BE LIABLE FOR ANYDIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <efi/types.h>
|
||||
#include <efi/system-table.h>
|
||||
|
||||
#include <elf.h>
|
||||
|
||||
#if __riscv_xlen == 64
|
||||
#define Elf_Dyn Elf64_Dyn
|
||||
#define Elf_Rela Elf64_Rela
|
||||
#define ELF_R_TYPE ELF64_R_TYPE
|
||||
#else
|
||||
#define Elf_Dyn Elf32_Dyn
|
||||
#define Elf_Rela Elf32_Rela
|
||||
#define ELF_R_TYPE ELF32_R_TYPE
|
||||
#endif
|
||||
|
||||
|
||||
efi_status _relocate(long ldbase, Elf_Dyn *dyn,
|
||||
efi_handle image __attribute__((__unused__)),
|
||||
efi_system_table *systab __attribute__((__unused__)))
|
||||
{
|
||||
long relsz = 0, relent = 0;
|
||||
Elf_Rela *rel = 0;
|
||||
unsigned long *addr;
|
||||
int i;
|
||||
|
||||
for (i = 0; dyn[i].d_tag != DT_NULL; ++i) {
|
||||
switch (dyn[i].d_tag) {
|
||||
case DT_RELA:
|
||||
rel = (Elf_Rela *)((ulong)dyn[i].d_un.d_ptr + ldbase);
|
||||
break;
|
||||
case DT_RELASZ:
|
||||
relsz = dyn[i].d_un.d_val;
|
||||
break;
|
||||
case DT_RELAENT:
|
||||
relent = dyn[i].d_un.d_val;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!rel && relent == 0)
|
||||
return EFI_SUCCESS;
|
||||
|
||||
if (!rel || relent == 0)
|
||||
return EFI_LOAD_ERROR;
|
||||
|
||||
while (relsz > 0) {
|
||||
/* apply the relocs */
|
||||
switch (ELF_R_TYPE(rel->r_info)) {
|
||||
case R_RISCV_RELATIVE:
|
||||
addr = (ulong *)(ldbase + rel->r_offset);
|
||||
*addr = ldbase + rel->r_addend;
|
||||
break;
|
||||
default:
|
||||
/* Panic */
|
||||
while (1) ;
|
||||
}
|
||||
rel = (Elf_Rela *)((char *)rel + relent);
|
||||
relsz -= relent;
|
||||
}
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
@@ -5,10 +5,20 @@ SubDirHdrs $(HAIKU_TOP) src system boot platform efi ;
|
||||
UsePrivateHeaders [ FDirName kernel platform ] ;
|
||||
UsePrivateHeaders [ FDirName kernel boot platform efi ] ;
|
||||
|
||||
{
|
||||
local defines = _BOOT_MODE _BOOT_PLATFORM_EFI ;
|
||||
defines = [ FDefines $(defines) ] ;
|
||||
SubDirCcFlags $(defines) ;
|
||||
SubDirC++Flags $(defines) -fno-rtti ;
|
||||
}
|
||||
|
||||
local arch_src =
|
||||
crt0-efi-$(TARGET_ARCH).S
|
||||
entry.S
|
||||
smp_trampoline.S
|
||||
support.S
|
||||
relocation_func.cpp
|
||||
arch_start.cpp
|
||||
arch_smp.cpp
|
||||
arch_mmu.cpp
|
||||
arch_timer.cpp
|
||||
|
||||
@@ -7,9 +7,24 @@
|
||||
*/
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <kernel.h>
|
||||
#include <arch_kernel.h>
|
||||
#include <boot/platform.h>
|
||||
#include <boot/stage2.h>
|
||||
#include <arch/x86/descriptors.h>
|
||||
|
||||
#include <efi/types.h>
|
||||
#include <efi/boot-services.h>
|
||||
|
||||
#include "mmu.h"
|
||||
#include "efi_platform.h"
|
||||
|
||||
|
||||
#undef BOOT_GDT_SEGMENT_COUNT
|
||||
#define BOOT_GDT_SEGMENT_COUNT (USER_DATA_SEGMENT + 1)
|
||||
|
||||
|
||||
extern uint64 gLongGDT;
|
||||
extern uint64 gLongGDTR;
|
||||
@@ -37,6 +52,181 @@ long_gdt_init()
|
||||
}
|
||||
|
||||
|
||||
// Called after EFI boot services exit.
|
||||
// Currently assumes that the memory map is sane... Sorted and no overlapping
|
||||
// regions.
|
||||
void
|
||||
arch_mmu_post_efi_setup(size_t memory_map_size,
|
||||
efi_memory_descriptor *memory_map, size_t descriptor_size,
|
||||
uint32_t 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 (size_t 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;
|
||||
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
|
||||
kRuntimeServices->SetVirtualAddressMap(memory_map_size, descriptor_size,
|
||||
descriptor_version, memory_map);
|
||||
|
||||
// Important. Make sure supervisor threads can fault on read only pages...
|
||||
asm("mov %%rax, %%cr0" : : "a" ((1 << 31) | (1 << 16) | (1 << 5) | 1));
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint64_t
|
||||
arch_mmu_generate_post_efi_page_tables(size_t memory_map_size,
|
||||
efi_memory_descriptor *memory_map, size_t descriptor_size,
|
||||
uint32_t 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
|
||||
= get_current_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 (size_t 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;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
arch_mmu_init()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright 2014-2020 Haiku, Inc. All rights reserved.
|
||||
* Copyright 2013-2014, Fredrik Holmqvist, [email protected].
|
||||
* Copyright 2014, Henry Harrington, [email protected].
|
||||
* All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Alexander von Gluck IV <[email protected]>
|
||||
*/
|
||||
|
||||
|
||||
#include <boot/platform.h>
|
||||
#include <boot/stage2.h>
|
||||
#include <boot/stdio.h>
|
||||
|
||||
#include "mmu.h"
|
||||
#include "serial.h"
|
||||
#include "smp.h"
|
||||
#include "efi_platform.h"
|
||||
|
||||
|
||||
// From entry.S
|
||||
extern "C" void arch_enter_kernel(uint64 pml4, uint64 entry_point,
|
||||
uint64 stackTop);
|
||||
|
||||
|
||||
void
|
||||
arch_start_kernel(addr_t kernelEntry)
|
||||
{
|
||||
// Prepare to exit EFI boot services.
|
||||
// Read the memory map.
|
||||
// First call is to determine the buffer size.
|
||||
size_t memory_map_size = 0;
|
||||
efi_memory_descriptor dummy;
|
||||
efi_memory_descriptor *memory_map;
|
||||
size_t map_key;
|
||||
size_t descriptor_size;
|
||||
uint32_t 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.
|
||||
size_t 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 (size_t 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 = arch_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) {
|
||||
// The console was provided by boot services, disable it.
|
||||
stdout = NULL;
|
||||
stderr = NULL;
|
||||
// Also switch to legacy serial output
|
||||
// (may not work on all systems)
|
||||
serial_switch_to_legacy();
|
||||
dprintf("Switched to legacy serial output\n");
|
||||
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.");
|
||||
}
|
||||
}
|
||||
|
||||
// Update EFI, generate final kernel physical memory map, etc.
|
||||
arch_mmu_post_efi_setup(memory_map_size, memory_map,
|
||||
descriptor_size, descriptor_version);
|
||||
|
||||
smp_boot_other_cpus(final_pml4, kernelEntry);
|
||||
|
||||
// Enter the kernel!
|
||||
arch_enter_kernel(final_pml4, kernelEntry,
|
||||
gKernelArgs.cpu_kstack[0].start + gKernelArgs.cpu_kstack[0].size);
|
||||
}
|
||||
@@ -16,8 +16,8 @@
|
||||
.code64
|
||||
|
||||
|
||||
/*! void efi_enter_kernel(uint64 pml4, uint64 entry_point, uint64 stackTop); */
|
||||
FUNCTION(efi_enter_kernel):
|
||||
/*! void arch_enter_kernel(uint64 pml4, uint64 entry_point, uint64 stackTop); */
|
||||
FUNCTION(arch_enter_kernel):
|
||||
// Point CR3 to the kernel's PML4.
|
||||
movq %rdi, %cr3
|
||||
|
||||
|
||||
@@ -27,11 +27,12 @@ struct allocated_memory_region {
|
||||
};
|
||||
|
||||
|
||||
static uint64_t next_virtual_address = KERNEL_LOAD_BASE_64_BIT + 32 * 1024 * 1024;
|
||||
static addr_t sNextVirtualAddress = KERNEL_LOAD_BASE_64_BIT + 32 * 1024 * 1024;
|
||||
static allocated_memory_region *allocated_memory_regions = NULL;
|
||||
|
||||
|
||||
static uint64_t mmu_allocate_page()
|
||||
extern "C" uint64_t
|
||||
mmu_allocate_page()
|
||||
{
|
||||
efi_physical_addr addr;
|
||||
efi_status s = kBootServices->AllocatePages(AllocateAnyPages, EfiLoaderData, 1, &addr);
|
||||
@@ -42,172 +43,19 @@ static uint64_t mmu_allocate_page()
|
||||
}
|
||||
|
||||
|
||||
uint64_t
|
||||
mmu_generate_post_efi_page_tables(size_t memory_map_size,
|
||||
efi_memory_descriptor *memory_map, size_t descriptor_size,
|
||||
uint32_t descriptor_version)
|
||||
extern "C" addr_t
|
||||
get_next_virtual_address(size_t size)
|
||||
{
|
||||
// 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 (size_t 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;
|
||||
addr_t address = sNextVirtualAddress;
|
||||
sNextVirtualAddress += ROUNDUP(size, B_PAGE_SIZE);
|
||||
return address;
|
||||
}
|
||||
|
||||
|
||||
// Called after EFI boot services exit.
|
||||
// Currently assumes that the memory map is sane... Sorted and no overlapping
|
||||
// regions.
|
||||
void
|
||||
mmu_post_efi_setup(size_t memory_map_size, efi_memory_descriptor *memory_map, size_t descriptor_size, uint32_t descriptor_version)
|
||||
extern "C" addr_t
|
||||
get_current_virtual_address()
|
||||
{
|
||||
// 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 (size_t 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;
|
||||
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);
|
||||
|
||||
// Important. Make sure supervisor threads can fault on read only pages...
|
||||
#if defined(__x86_64__) || defined(__x86__)
|
||||
asm("mov %%rax, %%cr0" : : "a" ((1 << 31) | (1 << 16) | (1 << 5) | 1));
|
||||
#else
|
||||
#error Ensure supervisor threads can fault on read-only pages on this architecture!
|
||||
#endif
|
||||
return sNextVirtualAddress;
|
||||
}
|
||||
|
||||
|
||||
@@ -364,8 +212,7 @@ platform_bootloader_address_to_kernel_address(void *address, uint64_t *_result)
|
||||
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);
|
||||
region->vaddr = get_next_virtual_address(region->size);
|
||||
}
|
||||
*_result = region->vaddr + (addr - region->paddr);
|
||||
//dprintf("Converted bootloader address %p in region %#lx-%#lx to %#lx\n",
|
||||
|
||||
@@ -1,57 +1,64 @@
|
||||
/*
|
||||
* Copyright 2014, Henry Harrington, [email protected].
|
||||
* Copyright 2019-2020, Haiku, Inc. All rights reserved.
|
||||
* 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 addr_t mmu_map_physical_memory(addr_t physicalAddress,
|
||||
size_t size, uint32 flags);
|
||||
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
|
||||
|
||||
|
||||
extern addr_t get_next_virtual_address(size_t size);
|
||||
extern addr_t get_current_virtual_address();
|
||||
|
||||
extern void mmu_init();
|
||||
|
||||
extern uint64_t mmu_allocate_page();
|
||||
|
||||
extern addr_t mmu_map_physical_memory(addr_t physicalAddress, size_t size,
|
||||
uint32 flags);
|
||||
|
||||
extern void mmu_free(void *virtualAddress, size_t size);
|
||||
|
||||
extern void mmu_post_efi_setup(size_t memory_map_size,
|
||||
efi_memory_descriptor *memory_map, size_t descriptor_size,
|
||||
uint32_t descriptor_version);
|
||||
|
||||
extern uint64_t mmu_generate_post_efi_page_tables(size_t memory_map_size,
|
||||
efi_memory_descriptor *memory_map, size_t descriptor_size,
|
||||
uint32_t 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);
|
||||
|
||||
// Architecture dependant
|
||||
|
||||
extern void arch_mmu_post_efi_setup(size_t memory_map_size,
|
||||
efi_memory_descriptor *memory_map, size_t descriptor_size,
|
||||
uint32_t descriptor_version);
|
||||
|
||||
extern uint64_t arch_mmu_generate_post_efi_page_tables(size_t memory_map_size,
|
||||
efi_memory_descriptor *memory_map, size_t descriptor_size,
|
||||
uint32_t descriptor_version);
|
||||
|
||||
void arch_mmu_init();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -50,12 +50,15 @@ serial_putc(char ch)
|
||||
if (sSerialUsesEFI) {
|
||||
size_t bufSize = 1;
|
||||
sSerial->Write(sSerial, &bufSize, &ch);
|
||||
} else {
|
||||
while ((in8(sSerialBasePort + SERIAL_LINE_STATUS) & 0x20) == 0)
|
||||
asm volatile ("pause;");
|
||||
|
||||
out8(ch, sSerialBasePort + SERIAL_TRANSMIT_BUFFER);
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(__x86__) || defined(__x86_64__)
|
||||
while ((in8(sSerialBasePort + SERIAL_LINE_STATUS) & 0x20) == 0)
|
||||
asm volatile ("pause;");
|
||||
|
||||
out8(ch, sSerialBasePort + SERIAL_TRANSMIT_BUFFER);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -115,6 +118,7 @@ serial_init(void)
|
||||
}
|
||||
|
||||
|
||||
#if defined(__x86__) || defined(__x86_64__)
|
||||
extern "C" void
|
||||
serial_switch_to_legacy(void)
|
||||
{
|
||||
@@ -135,3 +139,4 @@ serial_switch_to_legacy(void)
|
||||
out8(3, sSerialBasePort + SERIAL_LINE_CONTROL);
|
||||
// 8N1
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#include <boot/stage2.h>
|
||||
#include <boot/stdio.h>
|
||||
|
||||
#include "arch_mmu.h"
|
||||
#include "arch_start.h"
|
||||
#include "acpi.h"
|
||||
#include "console.h"
|
||||
#include "efi_platform.h"
|
||||
@@ -42,7 +42,6 @@ efi_handle kImage;
|
||||
|
||||
|
||||
static uint32 sBootOptions;
|
||||
static uint64 gLongKernelEntry;
|
||||
|
||||
|
||||
extern "C" int main(stage2_args *args);
|
||||
@@ -136,8 +135,8 @@ platform_start_kernel(void)
|
||||
convert_kernel_args();
|
||||
|
||||
// Save the kernel entry point address.
|
||||
gLongKernelEntry = image->elf_header.e_entry;
|
||||
dprintf("kernel entry at %#lx\n", gLongKernelEntry);
|
||||
addr_t kernelEntry = image->elf_header.e_entry;
|
||||
dprintf("kernel entry at %#lx\n", kernelEntry);
|
||||
|
||||
// map in a kernel stack
|
||||
void *stack_address = NULL;
|
||||
@@ -154,91 +153,10 @@ platform_start_kernel(void)
|
||||
// Apply any weird EFI quirks
|
||||
quirks_init();
|
||||
|
||||
// Prepare to exit EFI boot services.
|
||||
// Read the memory map.
|
||||
// First call is to determine the buffer size.
|
||||
size_t memory_map_size = 0;
|
||||
efi_memory_descriptor dummy;
|
||||
efi_memory_descriptor *memory_map;
|
||||
size_t map_key;
|
||||
size_t descriptor_size;
|
||||
uint32_t 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");
|
||||
}
|
||||
// Begin architecture-centric kernel entry.
|
||||
arch_start_kernel(kernelEntry);
|
||||
|
||||
// Allocate a buffer twice as large as needed just in case it gets bigger between
|
||||
// calls to ExitBootServices.
|
||||
size_t 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 (size_t 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) {
|
||||
// The console was provided by boot services, disable it.
|
||||
stdout = NULL;
|
||||
stderr = NULL;
|
||||
// Also switch to legacy serial output (may not work on all systems)
|
||||
serial_switch_to_legacy();
|
||||
dprintf("Switched to legacy serial output\n");
|
||||
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.");
|
||||
}
|
||||
}
|
||||
|
||||
// Update EFI, generate final kernel physical memory map, etc.
|
||||
mmu_post_efi_setup(memory_map_size, memory_map,
|
||||
descriptor_size, descriptor_version);
|
||||
|
||||
smp_boot_other_cpus(final_pml4, gLongKernelEntry);
|
||||
|
||||
// Enter the kernel!
|
||||
efi_enter_kernel(final_pml4, gLongKernelEntry,
|
||||
gKernelArgs.cpu_kstack[0].start + gKernelArgs.cpu_kstack[0].size);
|
||||
|
||||
panic("Shouldn't get here");
|
||||
panic("Shouldn't get here!");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* U-Boot riscv64 EFI linker script
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Modified from arch/arm/lib/elf_aarch64_efi.lds
|
||||
*/
|
||||
|
||||
OUTPUT_FORMAT("elf64-littleriscv", "elf64-littleriscv", "elf64-littleriscv")
|
||||
OUTPUT_ARCH(riscv)
|
||||
ENTRY(_start)
|
||||
SECTIONS
|
||||
{
|
||||
.text 0x0 : {
|
||||
_text = .;
|
||||
*(.text.head)
|
||||
*(.text)
|
||||
*(.text.*)
|
||||
*(.gnu.linkonce.t.*)
|
||||
*(.srodata)
|
||||
*(.rodata*)
|
||||
. = ALIGN(16);
|
||||
}
|
||||
_etext = .;
|
||||
_text_size = . - _text;
|
||||
.dynamic : { *(.dynamic) }
|
||||
.data : {
|
||||
_data = .;
|
||||
*(.sdata)
|
||||
*(.data)
|
||||
*(.data1)
|
||||
*(.data.*)
|
||||
*(.got.plt)
|
||||
*(.got)
|
||||
|
||||
/*
|
||||
* The EFI loader doesn't seem to like a .bss section, so we
|
||||
* stick it all into .data:
|
||||
*/
|
||||
. = ALIGN(16);
|
||||
_bss = .;
|
||||
*(.sbss)
|
||||
*(.scommon)
|
||||
*(.dynbss)
|
||||
*(.bss)
|
||||
*(.bss.*)
|
||||
*(COMMON)
|
||||
. = ALIGN(16);
|
||||
_bss_end = .;
|
||||
_edata = .;
|
||||
}
|
||||
.rela.dyn : { *(.rela.dyn) }
|
||||
.rela.plt : { *(.rela.plt) }
|
||||
.rela.got : { *(.rela.got) }
|
||||
.rela.data : { *(.rela.data) *(.rela.data*) }
|
||||
_data_size = . - _etext;
|
||||
|
||||
. = ALIGN(4096);
|
||||
.dynsym : { *(.dynsym) }
|
||||
. = ALIGN(4096);
|
||||
.dynstr : { *(.dynstr) }
|
||||
. = ALIGN(4096);
|
||||
.note.gnu.build-id : { *(.note.gnu.build-id) }
|
||||
/DISCARD/ : {
|
||||
*(.rel.reloc)
|
||||
*(.eh_frame)
|
||||
*(.note.GNU-stack)
|
||||
}
|
||||
.comment 0 : { *(.comment) }
|
||||
}
|
||||
Reference in New Issue
Block a user