mirror of
https://github.com/justinian/jsix.git
synced 2025-12-10 00:14:32 -08:00
[boot] Virtualize memory in the bootloader
Finish updating the page tables, call UEFI's `set_virtual_address_map` and jump to the kernel!
This commit is contained in:
@@ -189,12 +189,15 @@ efi_main(uefi::handle image_handle, uefi::system_table *st)
|
|||||||
kernel::args::header *args =
|
kernel::args::header *args =
|
||||||
bootloader_main_uefi(image_handle, st, con, &kentry);
|
bootloader_main_uefi(image_handle, st, con, &kentry);
|
||||||
|
|
||||||
size_t map_key = memory::build_kernel_mem_map(args, st->boot_services);
|
memory::efi_mem_map map =
|
||||||
|
memory::build_kernel_mem_map(args, st->boot_services);
|
||||||
|
|
||||||
try_or_raise(
|
try_or_raise(
|
||||||
st->boot_services->exit_boot_services(image_handle, map_key),
|
st->boot_services->exit_boot_services(image_handle, map.key),
|
||||||
L"Failed to exit boot services");
|
L"Failed to exit boot services");
|
||||||
|
|
||||||
|
memory::virtualize(args->pml4, map, st->runtime_services);
|
||||||
|
|
||||||
kentry(args);
|
kentry(args);
|
||||||
debug_break();
|
debug_break();
|
||||||
return uefi::status::unsupported;
|
return uefi::status::unsupported;
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <uefi/types.h>
|
#include <uefi/types.h>
|
||||||
|
|
||||||
|
#include "kernel_memory.h"
|
||||||
|
|
||||||
#include "console.h"
|
#include "console.h"
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
|
#include "paging.h"
|
||||||
|
|
||||||
namespace boot {
|
namespace boot {
|
||||||
namespace memory {
|
namespace memory {
|
||||||
@@ -85,51 +88,6 @@ mark_pointer_fixup(void **p)
|
|||||||
fixup_pointers[fixup_pointer_index++] = p;
|
fixup_pointers[fixup_pointer_index++] = p;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
void
|
|
||||||
virtualize(paging::page_table *pml4, uefi::runtime_services *rs, efi_mem_map *map)
|
|
||||||
{
|
|
||||||
// Get the pointer to the start of PML4
|
|
||||||
uint64_t* cr3 = 0;
|
|
||||||
__asm__ __volatile__ ( "mov %%cr3, %0" : "=r" (cr3) );
|
|
||||||
|
|
||||||
// PML4 is indexed with bits 39:47 of the virtual address
|
|
||||||
uint64_t offset = (KERNEL_VIRT_ADDRESS >> 39) & 0x1ff;
|
|
||||||
|
|
||||||
// Double map the lower half pages that are present into the higher half
|
|
||||||
for (unsigned i = 0; i < offset; ++i) {
|
|
||||||
if (cr3[i] & 0x1)
|
|
||||||
new_pml4[i] = new_pml4[offset+i] = cr3[i];
|
|
||||||
else
|
|
||||||
new_pml4[i] = new_pml4[offset+i] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write our new PML4 pointer back to CR3
|
|
||||||
__asm__ __volatile__ ( "mov %0, %%cr3" :: "r" (new_pml4) );
|
|
||||||
|
|
||||||
EFI_MEMORY_DESCRIPTOR *end = INCREMENT_DESC(map->entries, map->length);
|
|
||||||
EFI_MEMORY_DESCRIPTOR *d = map->entries;
|
|
||||||
while (d < end) {
|
|
||||||
switch (d->Type) {
|
|
||||||
case memtype_kernel:
|
|
||||||
case memtype_data:
|
|
||||||
case memtype_initrd:
|
|
||||||
case memtype_scratch:
|
|
||||||
d->Attribute |= EFI_MEMORY_RUNTIME;
|
|
||||||
d->VirtualStart = d->PhysicalStart + KERNEL_VIRT_ADDRESS;
|
|
||||||
|
|
||||||
default:
|
|
||||||
if (d->Attribute & EFI_MEMORY_RUNTIME) {
|
|
||||||
d->VirtualStart = d->PhysicalStart + KERNEL_VIRT_ADDRESS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
d = INCREMENT_DESC(d, map->size);
|
|
||||||
}
|
|
||||||
|
|
||||||
runsvc->SetVirtualAddressMap(map->length, map->size, map->version, map->entries);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
can_merge(mem_entry &prev, mem_type type, uefi::memory_descriptor *next)
|
can_merge(mem_entry &prev, mem_type type, uefi::memory_descriptor *next)
|
||||||
{
|
{
|
||||||
@@ -165,7 +123,7 @@ get_uefi_mappings(efi_mem_map *map, bool allocate, uefi::boot_services *bs)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t
|
efi_mem_map
|
||||||
build_kernel_mem_map(kernel::args::header *args, uefi::boot_services *bs)
|
build_kernel_mem_map(kernel::args::header *args, uefi::boot_services *bs)
|
||||||
{
|
{
|
||||||
status_line(L"Creating kernel memory map");
|
status_line(L"Creating kernel memory map");
|
||||||
@@ -286,7 +244,25 @@ build_kernel_mem_map(kernel::args::header *args, uefi::boot_services *bs)
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
return efi_map.key;
|
return efi_map;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
virtualize(void *pml4, efi_mem_map &map, uefi::runtime_services *rs)
|
||||||
|
{
|
||||||
|
paging::add_current_mappings(reinterpret_cast<paging::page_table*>(pml4));
|
||||||
|
|
||||||
|
for (auto desc : map)
|
||||||
|
desc->virtual_start = desc->physical_start + ::memory::page_offset;
|
||||||
|
|
||||||
|
// Write our new PML4 pointer to CR3
|
||||||
|
asm volatile ( "mov %0, %%cr3" :: "r" (pml4) );
|
||||||
|
__sync_synchronize();
|
||||||
|
|
||||||
|
try_or_raise(
|
||||||
|
rs->set_virtual_address_map(
|
||||||
|
map.length, map.size, map.version, map.entries),
|
||||||
|
L"Error setting virtual address map");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -81,9 +81,17 @@ struct efi_mem_map
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// Add the kernel's memory map as a module to the kernel args.
|
/// Add the kernel's memory map as a module to the kernel args.
|
||||||
/// \returns The uefi memory map key for the version used to build
|
/// \returns The uefi memory map used to build the kernel map
|
||||||
/// this map
|
efi_mem_map build_kernel_mem_map(kernel::args::header *args, uefi::boot_services *bs);
|
||||||
size_t build_kernel_mem_map(kernel::args::header *args, uefi::boot_services *bs);
|
|
||||||
|
/// Activate the given memory mappings. Sets the given page tables live as well
|
||||||
|
/// as informs UEFI runtime services of the new mappings.
|
||||||
|
/// \arg pml4 The root page table for the new mappings
|
||||||
|
/// \arg map The UEFI memory map, used to update runtime services
|
||||||
|
void virtualize(
|
||||||
|
void *pml4,
|
||||||
|
efi_mem_map &map,
|
||||||
|
uefi::runtime_services *rs);
|
||||||
|
|
||||||
} // namespace boot
|
} // namespace boot
|
||||||
} // namespace memory
|
} // namespace memory
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#include "kernel_memory.h"
|
||||||
|
|
||||||
#include "console.h"
|
#include "console.h"
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
#include "loader.h"
|
#include "loader.h"
|
||||||
@@ -123,7 +125,46 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
void allocate_tables(kernel::args::header *args, uefi::boot_services *bs)
|
static void
|
||||||
|
add_offset_mappings(page_table *pml4, void *&page_cache, uint32_t &num_pages)
|
||||||
|
{
|
||||||
|
uintptr_t phys = 0;
|
||||||
|
uintptr_t virt = ::memory::page_offset; // Start of offset-mapped area
|
||||||
|
size_t pages = 64 * 1024; // 64 TiB of 1 GiB pages
|
||||||
|
constexpr size_t GiB = 0x40000000ull;
|
||||||
|
|
||||||
|
page_entry_iterator<2> iterator{
|
||||||
|
virt, pml4,
|
||||||
|
page_cache,
|
||||||
|
num_pages};
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
*iterator = phys | huge_page_flags;
|
||||||
|
if (--pages == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
iterator.increment();
|
||||||
|
phys += GiB;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
add_current_mappings(page_table *new_pml4)
|
||||||
|
{
|
||||||
|
// Get the pointer to the current PML4
|
||||||
|
page_table *old_pml4 = 0;
|
||||||
|
asm volatile ( "mov %%cr3, %0" : "=r" (old_pml4) );
|
||||||
|
|
||||||
|
// Only copy mappings in the lower half
|
||||||
|
for (int i = 0; i < 256; ++i) {
|
||||||
|
uint64_t entry = old_pml4->entries[i];
|
||||||
|
if (entry & 1)
|
||||||
|
new_pml4->entries[i] = entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
allocate_tables(kernel::args::header *args, uefi::boot_services *bs)
|
||||||
{
|
{
|
||||||
status_line status(L"Allocating initial page tables");
|
status_line status(L"Allocating initial page tables");
|
||||||
|
|
||||||
@@ -151,25 +192,7 @@ void allocate_tables(kernel::args::header *args, uefi::boot_services *bs)
|
|||||||
args->page_table_cache = offset_ptr<void>(addr, page_size);
|
args->page_table_cache = offset_ptr<void>(addr, page_size);
|
||||||
|
|
||||||
page_table *pml4 = reinterpret_cast<page_table*>(addr);
|
page_table *pml4 = reinterpret_cast<page_table*>(addr);
|
||||||
|
add_offset_mappings(pml4, args->page_table_cache, args->num_free_tables);
|
||||||
uintptr_t phys = 0;
|
|
||||||
uintptr_t virt = 0xffffc00000000000ull; // Start of offset-mapped area
|
|
||||||
size_t pages = 64 * 1024; // 64 TiB of 1 GiB pages
|
|
||||||
constexpr size_t GiB = 0x40000000ull;
|
|
||||||
|
|
||||||
page_entry_iterator<2> iterator{
|
|
||||||
virt, pml4,
|
|
||||||
args->page_table_cache,
|
|
||||||
args->num_free_tables};
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
*iterator = phys | huge_page_flags;
|
|
||||||
if (--pages == 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
iterator.increment();
|
|
||||||
phys += GiB;
|
|
||||||
}
|
|
||||||
|
|
||||||
console::print(L" Set up initial mappings, %d spare tables.\r\n", args->num_free_tables);
|
console::print(L" Set up initial mappings, %d spare tables.\r\n", args->num_free_tables);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,11 @@ void allocate_tables(
|
|||||||
kernel::args::header *args,
|
kernel::args::header *args,
|
||||||
uefi::boot_services *bs);
|
uefi::boot_services *bs);
|
||||||
|
|
||||||
|
/// Copy existing page table entries to a new page table. Does not do a deep
|
||||||
|
/// copy - the new PML4 is updated to point to the existing next-level page
|
||||||
|
/// tables in the current PML4.
|
||||||
|
void add_current_mappings(page_table *new_pml4);
|
||||||
|
|
||||||
/// Map a physical address to a virtual address in the given page tables.
|
/// Map a physical address to a virtual address in the given page tables.
|
||||||
/// \arg pml4 The root of the set of page tables to be updated
|
/// \arg pml4 The root of the set of page tables to be updated
|
||||||
/// \arg args The kernel args header, used for the page table cache
|
/// \arg args The kernel args header, used for the page table cache
|
||||||
|
|||||||
@@ -11,10 +11,10 @@ namespace memory {
|
|||||||
static const size_t frame_size = 0x1000;
|
static const size_t frame_size = 0x1000;
|
||||||
|
|
||||||
/// Start of kernel memory.
|
/// Start of kernel memory.
|
||||||
static const uintptr_t kernel_offset = 0xffffff0000000000;
|
static const uintptr_t kernel_offset = 0xffff800000000000;
|
||||||
|
|
||||||
/// Offset from physical where page tables are mapped.
|
/// Offset from physical where page tables are mapped.
|
||||||
static const uintptr_t page_offset = 0xffffff8000000000;
|
static const uintptr_t page_offset = 0xffffc00000000000;
|
||||||
|
|
||||||
/// Initial process thread's stack address
|
/// Initial process thread's stack address
|
||||||
static const uintptr_t initial_stack = 0x0000800000000000;
|
static const uintptr_t initial_stack = 0x0000800000000000;
|
||||||
|
|||||||
Reference in New Issue
Block a user