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:
Alexander von Gluck IV
2020-02-21 14:29:22 +00:00
committed by Alex von Gluck IV
parent 35b4b2373a
commit 04f1baa771
21 changed files with 655 additions and 304 deletions
+2 -2
View File
@@ -302,8 +302,8 @@ rule KernelArchitectureSetup architecture
} }
case riscv64 : case riscv64 :
HAIKU_KERNEL_PLATFORM ?= u-boot ; HAIKU_KERNEL_PLATFORM ?= efi ;
HAIKU_BOOT_TARGETS += u-boot ; HAIKU_BOOT_TARGETS += efi ;
HAIKU_BOOT_SDIMAGE_SIZE ?= 128 ; HAIKU_BOOT_SDIMAGE_SIZE ?= 128 ;
# SOC's like allwinner need an offset to skip the hardcoded initial loader # 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 # error This file is included from <boot/kernel_args.h> only
#endif #endif
#include <util/FixedWidthPointer.h>
#define _PACKED __attribute__((packed)) #define _PACKED __attribute__((packed))
#define MAX_VIRTUAL_RANGES_TO_KEEP 32 #define MAX_VIRTUAL_RANGES_TO_KEEP 32
@@ -21,9 +25,14 @@ typedef struct {
uint64 vir_pgdir; uint64 vir_pgdir;
uint64 next_pagetable; uint64 next_pagetable;
uint64 virtual_end;
// The virtual ranges we want to keep in the kernel. // The virtual ranges we want to keep in the kernel.
uint32 num_virtual_ranges_to_keep; uint32 num_virtual_ranges_to_keep;
addr_range virtual_ranges_to_keep[MAX_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 */ #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, arch_debug_get_instruction_pointer(debug_context *context, thread_id thread,
void **ip, void **stackFrameAddress) void **ip, void **stackFrameAddress)
{ {
#warning TODO RISCV64 get instruction pointer
#if 0
// get the CPU state // get the CPU state
debug_cpu_state cpuState; debug_cpu_state cpuState;
status_t error = debug_get_cpu_state(context, thread, NULL, &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; *ip = (void*)cpuState.rip;
*stackFrameAddress = (void*)cpuState.rbp; *stackFrameAddress = (void*)cpuState.rbp;
#endif
return B_OK; return B_OK;
} }
@@ -37,6 +40,8 @@ status_t
arch_debug_get_stack_frame(debug_context *context, void *stackFrameAddress, arch_debug_get_stack_frame(debug_context *context, void *stackFrameAddress,
debug_stack_frame_info *stackFrameInfo) debug_stack_frame_info *stackFrameInfo)
{ {
#warning TODO RISCV64 get stack frame
#if 0
stack_frame stackFrame; stack_frame stackFrame;
ssize_t bytesRead = debug_read_memory(context, stackFrameAddress, ssize_t bytesRead = debug_read_memory(context, stackFrameAddress,
&stackFrame, sizeof(stackFrame)); &stackFrame, sizeof(stackFrame));
@@ -48,5 +53,7 @@ arch_debug_get_stack_frame(debug_context *context, void *stackFrameAddress,
stackFrameInfo->frame = stackFrameAddress; stackFrameInfo->frame = stackFrameAddress;
stackFrameInfo->parent_frame = stackFrame.previous; stackFrameInfo->parent_frame = stackFrame.previous;
stackFrameInfo->return_address = stackFrame.return_address; stackFrameInfo->return_address = stackFrame.return_address;
#endif
return B_OK; return B_OK;
} }
-2
View File
@@ -29,8 +29,6 @@ local platform_src =
quirks.cpp quirks.cpp
smp.cpp smp.cpp
serial.cpp serial.cpp
smp_trampoline.S
support.S
; ;
local platform ; 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 <kallisti5@unixzen.com>
*/
#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, dustin.howett@gmail.com. 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 <agraf@suse.de>
Copyright (C) 2014 Linaro Ltd. <ard.biesheuvel@linaro.org>
Copyright (C) 1999 Hewlett-Packard Co.
Contributed by David Mosberger <davidm@hpl.hp.com>.
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 platform ] ;
UsePrivateHeaders [ FDirName kernel boot platform efi ] ; 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 = local arch_src =
crt0-efi-$(TARGET_ARCH).S crt0-efi-$(TARGET_ARCH).S
entry.S entry.S
smp_trampoline.S
support.S
relocation_func.cpp relocation_func.cpp
arch_start.cpp
arch_smp.cpp arch_smp.cpp
arch_mmu.cpp arch_mmu.cpp
arch_timer.cpp arch_timer.cpp
@@ -7,9 +7,24 @@
*/ */
#include <algorithm>
#include <kernel.h>
#include <arch_kernel.h>
#include <boot/platform.h> #include <boot/platform.h>
#include <boot/stage2.h>
#include <arch/x86/descriptors.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 gLongGDT;
extern uint64 gLongGDTR; 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 void
arch_mmu_init() arch_mmu_init()
{ {
@@ -0,0 +1,115 @@
/*
* Copyright 2014-2020 Haiku, Inc. All rights reserved.
* Copyright 2013-2014, Fredrik Holmqvist, fredrik.holmqvist@gmail.com.
* Copyright 2014, Henry Harrington, henry.harrington@gmail.com.
* All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Alexander von Gluck IV <kallisti5@unixzen.com>
*/
#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 .code64
/*! void efi_enter_kernel(uint64 pml4, uint64 entry_point, uint64 stackTop); */ /*! void arch_enter_kernel(uint64 pml4, uint64 entry_point, uint64 stackTop); */
FUNCTION(efi_enter_kernel): FUNCTION(arch_enter_kernel):
// Point CR3 to the kernel's PML4. // Point CR3 to the kernel's PML4.
movq %rdi, %cr3 movq %rdi, %cr3
+12 -165
View File
@@ -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 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_physical_addr addr;
efi_status s = kBootServices->AllocatePages(AllocateAnyPages, EfiLoaderData, 1, &addr); efi_status s = kBootServices->AllocatePages(AllocateAnyPages, EfiLoaderData, 1, &addr);
@@ -42,172 +43,19 @@ static uint64_t mmu_allocate_page()
} }
uint64_t extern "C" addr_t
mmu_generate_post_efi_page_tables(size_t memory_map_size, get_next_virtual_address(size_t size)
efi_memory_descriptor *memory_map, size_t descriptor_size,
uint32_t descriptor_version)
{ {
// Generate page tables, matching bios_ia32/long.cpp. addr_t address = sNextVirtualAddress;
uint64_t *pml4; sNextVirtualAddress += ROUNDUP(size, B_PAGE_SIZE);
uint64_t *pdpt; return address;
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;
} }
// Called after EFI boot services exit. extern "C" addr_t
// Currently assumes that the memory map is sane... Sorted and no overlapping get_current_virtual_address()
// regions.
void
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.. return sNextVirtualAddress;
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
} }
@@ -364,8 +212,7 @@ platform_bootloader_address_to_kernel_address(void *address, uint64_t *_result)
if (region->paddr <= addr && addr < region->paddr + region->size) { if (region->paddr <= addr && addr < region->paddr + region->size) {
// Lazily allocate virtual memory. // Lazily allocate virtual memory.
if (region->vaddr == 0) { if (region->vaddr == 0) {
region->vaddr = next_virtual_address; region->vaddr = get_next_virtual_address(region->size);
next_virtual_address += ROUNDUP(region->size, B_PAGE_SIZE);
} }
*_result = region->vaddr + (addr - region->paddr); *_result = region->vaddr + (addr - region->paddr);
//dprintf("Converted bootloader address %p in region %#lx-%#lx to %#lx\n", //dprintf("Converted bootloader address %p in region %#lx-%#lx to %#lx\n",
+26 -19
View File
@@ -1,24 +1,23 @@
/* /*
* Copyright 2014, Henry Harrington, henry.harrington@gmail.com. * Copyright 2014, Henry Harrington, henry.harrington@gmail.com.
* Copyright 2019-2020, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
*/ */
#ifndef MMU_H #ifndef MMU_H
#define 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 #ifndef _ASSEMBLER
#include "efi_platform.h" #include "efi_platform.h"
#include <util/FixedWidthPointer.h> #include <util/FixedWidthPointer.h>
extern segment_descriptor gBootGDT[BOOT_GDT_SEGMENT_COUNT]; #ifdef __cplusplus
extern "C" {
#endif
static const uint32 kDefaultPageFlags = 0x3; static const uint32 kDefaultPageFlags = 0x3;
// present, R/W // present, R/W
@@ -30,28 +29,36 @@ static const uint64 kPageMappingFlags = 0x103;
// present, R/W, user, global // present, R/W, user, global
#ifdef __cplusplus extern addr_t get_next_virtual_address(size_t size);
extern "C" { extern addr_t get_current_virtual_address();
#endif
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 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_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, extern status_t platform_kernel_address_to_bootloader_address(uint64_t address,
void **_result); void **_result);
extern status_t platform_bootloader_address_to_kernel_address(void *address, extern status_t platform_bootloader_address_to_kernel_address(void *address,
uint64_t *_result); 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 #ifdef __cplusplus
} }
#endif #endif
+7 -2
View File
@@ -50,12 +50,15 @@ serial_putc(char ch)
if (sSerialUsesEFI) { if (sSerialUsesEFI) {
size_t bufSize = 1; size_t bufSize = 1;
sSerial->Write(sSerial, &bufSize, &ch); sSerial->Write(sSerial, &bufSize, &ch);
} else { return;
}
#if defined(__x86__) || defined(__x86_64__)
while ((in8(sSerialBasePort + SERIAL_LINE_STATUS) & 0x20) == 0) while ((in8(sSerialBasePort + SERIAL_LINE_STATUS) & 0x20) == 0)
asm volatile ("pause;"); asm volatile ("pause;");
out8(ch, sSerialBasePort + SERIAL_TRANSMIT_BUFFER); out8(ch, sSerialBasePort + SERIAL_TRANSMIT_BUFFER);
} #endif
} }
@@ -115,6 +118,7 @@ serial_init(void)
} }
#if defined(__x86__) || defined(__x86_64__)
extern "C" void extern "C" void
serial_switch_to_legacy(void) serial_switch_to_legacy(void)
{ {
@@ -135,3 +139,4 @@ serial_switch_to_legacy(void)
out8(3, sSerialBasePort + SERIAL_LINE_CONTROL); out8(3, sSerialBasePort + SERIAL_LINE_CONTROL);
// 8N1 // 8N1
} }
#endif
+6 -88
View File
@@ -20,7 +20,7 @@
#include <boot/stage2.h> #include <boot/stage2.h>
#include <boot/stdio.h> #include <boot/stdio.h>
#include "arch_mmu.h" #include "arch_start.h"
#include "acpi.h" #include "acpi.h"
#include "console.h" #include "console.h"
#include "efi_platform.h" #include "efi_platform.h"
@@ -42,7 +42,6 @@ efi_handle kImage;
static uint32 sBootOptions; static uint32 sBootOptions;
static uint64 gLongKernelEntry;
extern "C" int main(stage2_args *args); extern "C" int main(stage2_args *args);
@@ -136,8 +135,8 @@ platform_start_kernel(void)
convert_kernel_args(); convert_kernel_args();
// Save the kernel entry point address. // Save the kernel entry point address.
gLongKernelEntry = image->elf_header.e_entry; addr_t kernelEntry = image->elf_header.e_entry;
dprintf("kernel entry at %#lx\n", gLongKernelEntry); dprintf("kernel entry at %#lx\n", kernelEntry);
// map in a kernel stack // map in a kernel stack
void *stack_address = NULL; void *stack_address = NULL;
@@ -154,91 +153,10 @@ platform_start_kernel(void)
// Apply any weird EFI quirks // Apply any weird EFI quirks
quirks_init(); quirks_init();
// Prepare to exit EFI boot services. // Begin architecture-centric kernel entry.
// Read the memory map. arch_start_kernel(kernelEntry);
// 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 panic("Shouldn't get here!");
// 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");
} }
@@ -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) }
}