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 :
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;
}
-2
View File
@@ -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
+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 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",
+34 -27
View File
@@ -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
+10 -5
View File
@@ -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
+6 -88
View File
@@ -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) }
}