Start work on x86_64 boot
This commit is contained in:
parent
a0a26b745f
commit
08291808c7
2
notes.md
2
notes.md
|
@ -19,7 +19,7 @@ Loader must provide modinfo to kernel, a TLV structure
|
||||||
* `MODINFOMD_EFI_FB`: Some structure describing UEFI framebuffer
|
* `MODINFOMD_EFI_FB`: Some structure describing UEFI framebuffer
|
||||||
* `MODINFOMD_KEYBUF`: `struct keybuf` object with cached keys, don't really need it
|
* `MODINFOMD_KEYBUF`: `struct keybuf` object with cached keys, don't really need it
|
||||||
* `MODINFOMD_FW_HANDLE`: physical address of RuntimeServices system table
|
* `MODINFOMD_FW_HANDLE`: physical address of RuntimeServices system table
|
||||||
* `MODINFOMD_MODULEP`: Base physical address of modinfo, probably
|
* `MODINFOMD_MODULEP`: Base physical address of TLV structure, only seems to matter for x86\_64 Xen
|
||||||
* `MODINFOMD_KERNEND`: Last physical address of kernel, should be free after
|
* `MODINFOMD_KERNEND`: Last physical address of kernel, should be free after
|
||||||
* `MODINFOMD_HOWTO`: u32 with a bunch of bitflags that start with `RB_` in `sys/sys/reboot.h`
|
* `MODINFOMD_HOWTO`: u32 with a bunch of bitflags that start with `RB_` in `sys/sys/reboot.h`
|
||||||
* `MODINFOMD_ELFHDR`: copy of the elf header
|
* `MODINFOMD_ELFHDR`: copy of the elf header
|
||||||
|
|
65
src/abi.rs
65
src/abi.rs
|
@ -1,7 +1,10 @@
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
use alloc::string::String;
|
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use uefi::proto::console::gop::{GraphicsOutput, PixelFormat};
|
use alloc::{collections::BTreeMap, string::String};
|
||||||
|
use uefi::{
|
||||||
|
proto::console::gop::{GraphicsOutput, PixelFormat},
|
||||||
|
table::boot::PAGE_SIZE,
|
||||||
|
};
|
||||||
|
|
||||||
fn align_slice(unaligned: &mut [u8], alignment: usize) -> &mut [u8] {
|
fn align_slice(unaligned: &mut [u8], alignment: usize) -> &mut [u8] {
|
||||||
let base_addr = &unaligned[0] as *const u8 as usize;
|
let base_addr = &unaligned[0] as *const u8 as usize;
|
||||||
|
@ -17,17 +20,29 @@ pub trait Serialize {
|
||||||
/// Calculate output size of object, from base address to final byte written
|
/// Calculate output size of object, from base address to final byte written
|
||||||
fn size(&self) -> usize;
|
fn size(&self) -> usize;
|
||||||
|
|
||||||
|
/// Conservative estimate of size to allocate
|
||||||
|
fn alloc_size(&self) -> usize {
|
||||||
|
self.size() + Self::alignment() - 1
|
||||||
|
}
|
||||||
|
|
||||||
/// Serliaze data into a buffer. Buffer must be aligned at alignment and size of size().
|
/// Serliaze data into a buffer. Buffer must be aligned at alignment and size of size().
|
||||||
fn serialize_raw(&self, out: &mut [u8]);
|
fn serialize_raw(&self, out: &mut [u8]);
|
||||||
|
|
||||||
/// Serialize to an unaligned buffer, returning the end address
|
/// Serialize to an unaligned buffer, returning the pointer to the base and the end address as
|
||||||
|
/// a slice
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn serialize_unaligned<'a>(&self, out: &'a mut [u8]) -> &'a mut [u8] {
|
fn serialize_unaligned_base<'a>(&self, out: &'a mut [u8]) -> (*const u8, &'a mut [u8]) {
|
||||||
let aligned = align_slice(out, Self::alignment());
|
let aligned = align_slice(out, Self::alignment());
|
||||||
let size = self.size();
|
let size = self.size();
|
||||||
self.serialize_raw(&mut aligned[..size]);
|
self.serialize_raw(&mut aligned[..size]);
|
||||||
|
|
||||||
&mut aligned[size..]
|
(aligned.as_ptr(), &mut aligned[size..])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Serialize to an unaligned buffer, returning the end address as a slice
|
||||||
|
#[must_use]
|
||||||
|
fn serialize_unaligned<'a>(&self, out: &'a mut [u8]) -> &'a mut [u8] {
|
||||||
|
self.serialize_unaligned_base(out).1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,6 +114,7 @@ impl Serialize for EFIFramebufferParams {
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
#[repr(u32)]
|
#[repr(u32)]
|
||||||
|
#[allow(dead_code)]
|
||||||
/// Tag in the module info TLV
|
/// Tag in the module info TLV
|
||||||
pub enum ModuleInfoType {
|
pub enum ModuleInfoType {
|
||||||
/// Module name (MODINFO_NAME)
|
/// Module name (MODINFO_NAME)
|
||||||
|
@ -123,6 +139,12 @@ pub enum ModuleInfoType {
|
||||||
EndSymbols = 0x8004,
|
EndSymbols = 0x8004,
|
||||||
/// Base virtual address of `PT_DYNAMIC` segment (MODINFOMD_DYNAMIC)
|
/// Base virtual address of `PT_DYNAMIC` segment (MODINFOMD_DYNAMIC)
|
||||||
Dynamic = 0x8005,
|
Dynamic = 0x8005,
|
||||||
|
/// Loader environment variables (MODINFOMD_ENVP)
|
||||||
|
Environment = 0x8006,
|
||||||
|
/// Boot bitflags starting with `RB_` (MODINFOMD_HOWTO)
|
||||||
|
Howto = 0x8007,
|
||||||
|
/// Free virtual address after staging buffer (MODINFOMD_KERNEND)
|
||||||
|
FreeAddress = 0x8008,
|
||||||
/// Section header table (MODINFOMD_SHDR)
|
/// Section header table (MODINFOMD_SHDR)
|
||||||
SectionHeader = 0x8009,
|
SectionHeader = 0x8009,
|
||||||
/// Base virtual address of constructors (MODINFOMD_CTORS_ADDR)
|
/// Base virtual address of constructors (MODINFOMD_CTORS_ADDR)
|
||||||
|
@ -200,7 +222,7 @@ impl core::fmt::Debug for ModuleInfoValue {
|
||||||
Self::String(arg0) => f.debug_tuple("String").field(arg0).finish(),
|
Self::String(arg0) => f.debug_tuple("String").field(arg0).finish(),
|
||||||
Self::Pointer(arg0) => f.debug_tuple("Pointer").field(arg0).finish(),
|
Self::Pointer(arg0) => f.debug_tuple("Pointer").field(arg0).finish(),
|
||||||
Self::Size(arg0) => f.debug_tuple("Size").field(arg0).finish(),
|
Self::Size(arg0) => f.debug_tuple("Size").field(arg0).finish(),
|
||||||
Self::Buffer(_) => write!(f, "Buffer(elided)"),
|
Self::Buffer(arg0) => write!(f, "Buffer(len={})", arg0.len()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -228,3 +250,34 @@ impl Serialize for ModuleInfoItem {
|
||||||
self.value.serialize_raw(&mut out[8..]);
|
self.value.serialize_raw(&mut out[8..]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Environment(pub BTreeMap<String, String>);
|
||||||
|
|
||||||
|
impl Serialize for Environment {
|
||||||
|
fn alignment() -> usize {
|
||||||
|
// FreeBSD aligns to pages, might not matter though
|
||||||
|
PAGE_SIZE
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size(&self) -> usize {
|
||||||
|
self.0
|
||||||
|
.iter()
|
||||||
|
.map(|(k, v)| k.len() + v.len() + 2)
|
||||||
|
.sum::<usize>()
|
||||||
|
+ 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_raw(&self, mut out: &mut [u8]) {
|
||||||
|
// Format is `key1=value1\0key2=value2\0\0`
|
||||||
|
for (key, value) in self.0.iter() {
|
||||||
|
let entry_len = key.len() + 1 + value.len();
|
||||||
|
out[..key.len()].copy_from_slice(key.as_bytes());
|
||||||
|
out[key.len()] = b'=';
|
||||||
|
out[key.len() + 1..entry_len].copy_from_slice(value.as_bytes());
|
||||||
|
out[entry_len] = 0;
|
||||||
|
out = &mut out[entry_len + 1..];
|
||||||
|
}
|
||||||
|
out[out.len() - 1] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
77
src/boot.rs
Normal file
77
src/boot.rs
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
pub use x86_64::boot_kernel;
|
||||||
|
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
mod x86_64 {
|
||||||
|
use core::{arch::global_asm, ptr::null, slice};
|
||||||
|
|
||||||
|
|
||||||
|
use uefi::table::{Boot, SystemTable};
|
||||||
|
|
||||||
|
use crate::{object::Kernel, staging::allocate_code};
|
||||||
|
|
||||||
|
global_asm!(
|
||||||
|
r#"
|
||||||
|
.text
|
||||||
|
.globl trampoline_code
|
||||||
|
trampoline_code:
|
||||||
|
cli
|
||||||
|
mov rsp,rdi
|
||||||
|
push rcx
|
||||||
|
sal rdx, #32
|
||||||
|
push rdx
|
||||||
|
push r8
|
||||||
|
mov cr3,rsi
|
||||||
|
ret
|
||||||
|
|
||||||
|
trampoline_end:
|
||||||
|
.data
|
||||||
|
.globl trampoline_size
|
||||||
|
trampoline_size:
|
||||||
|
.long trampoline_end-trampoline_code
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
|
||||||
|
// use sysv64 because i don't want to pop from the stack
|
||||||
|
extern "sysv64" {
|
||||||
|
static trampoline_size: u32;
|
||||||
|
|
||||||
|
fn trampoline_code(
|
||||||
|
stack: *const u8,
|
||||||
|
pagetable: *const u8,
|
||||||
|
modinfo_base: *const u8,
|
||||||
|
free_ptr: *const u8,
|
||||||
|
entry: *const u8,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
type TrampolineFunction =
|
||||||
|
extern "sysv64" fn(*const u8, *const u8, *const u8, *const u8, *const u8);
|
||||||
|
|
||||||
|
pub fn boot_kernel(
|
||||||
|
system_table: SystemTable<Boot>,
|
||||||
|
kernel: Kernel,
|
||||||
|
kernel_base: *const u8,
|
||||||
|
modinfo_base: *const u8,
|
||||||
|
free_ptr: *const u8,
|
||||||
|
) {
|
||||||
|
let trampoline_code_size = unsafe { trampoline_size } as usize;
|
||||||
|
let misc_buf = allocate_code(system_table.boot_services(), trampoline_code_size + 64);
|
||||||
|
|
||||||
|
let (stack_buf, trampoline_buf) = misc_buf.split_at_mut(64);
|
||||||
|
trampoline_buf[..trampoline_code_size].copy_from_slice(unsafe {
|
||||||
|
slice::from_raw_parts(trampoline_code as *const u8, trampoline_code_size)
|
||||||
|
});
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let trampoline_func: TrampolineFunction = core::mem::transmute(trampoline_buf.as_ptr());
|
||||||
|
trampoline_func(
|
||||||
|
stack_buf.as_ptr(),
|
||||||
|
null(),
|
||||||
|
modinfo_base,
|
||||||
|
free_ptr,
|
||||||
|
kernel.entry_addr(kernel_base),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
77
src/main.rs
77
src/main.rs
|
@ -2,10 +2,14 @@
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
mod abi;
|
mod abi;
|
||||||
|
mod boot;
|
||||||
mod object;
|
mod object;
|
||||||
mod staging;
|
mod staging;
|
||||||
|
|
||||||
use abi::{ModuleInfoItem, Serialize};
|
extern crate alloc;
|
||||||
|
|
||||||
|
use abi::{Environment, ModuleInfoItem, ModuleInfoType, ModuleInfoValue, Serialize};
|
||||||
|
use alloc::{collections::btree_map::BTreeMap, string::ToString};
|
||||||
use log::info;
|
use log::info;
|
||||||
use object::{Kernel, Module};
|
use object::{Kernel, Module};
|
||||||
use uefi::{fs::FileSystem, prelude::*};
|
use uefi::{fs::FileSystem, prelude::*};
|
||||||
|
@ -16,42 +20,61 @@ fn main(image_handle: Handle, mut system_table: SystemTable<Boot>) -> Status {
|
||||||
|
|
||||||
info!("Starting freeloader");
|
info!("Starting freeloader");
|
||||||
|
|
||||||
let mut esp = FileSystem::new(
|
let kernel_data = {
|
||||||
system_table
|
let mut esp = FileSystem::new(
|
||||||
.boot_services()
|
system_table
|
||||||
.get_image_file_system(image_handle)
|
.boot_services()
|
||||||
.expect("Failed to get ESP handle"),
|
.get_image_file_system(image_handle)
|
||||||
);
|
.expect("Failed to get ESP handle"),
|
||||||
|
);
|
||||||
let kernel_data = esp
|
|
||||||
.read(cstr16!("efi\\freebsd\\kernel"))
|
|
||||||
.expect("Failed to read kernel");
|
|
||||||
|
|
||||||
|
esp.read(cstr16!("efi\\freebsd\\kernel"))
|
||||||
|
.expect("Failed to read kernel")
|
||||||
|
};
|
||||||
info!("Read kernel");
|
info!("Read kernel");
|
||||||
|
|
||||||
let kernel = Kernel::from_elf_bytes(&kernel_data);
|
let kernel = Kernel::from_elf_bytes(&kernel_data);
|
||||||
let kernel_metadata = kernel.metadata();
|
let mut kernel_metadata = kernel.metadata();
|
||||||
info!("Kernel metadata is {:x?}", kernel_metadata);
|
info!("Kernel metadata is {:x?}", kernel_metadata);
|
||||||
|
|
||||||
let metadata_size: usize = kernel_metadata
|
let environment = Environment(BTreeMap::from([(
|
||||||
.iter()
|
"kernel".to_string(),
|
||||||
.map(|item| item.size().next_multiple_of(ModuleInfoItem::alignment()))
|
"kernel".to_string(),
|
||||||
.sum();
|
)]));
|
||||||
|
|
||||||
// Allocate an extra 16KiB for items we have to add to metadata
|
// Allocate an extra 16KiB for items we have to add to metadata
|
||||||
let alloc_size = kernel.size() + Kernel::alignment() + metadata_size + 16 * 1024;
|
let metadata_size: usize = kernel_metadata
|
||||||
|
.iter()
|
||||||
|
.map(|item| item.alloc_size())
|
||||||
|
.sum::<usize>()
|
||||||
|
+ 16 * 1024;
|
||||||
|
|
||||||
let staging = staging::allocate_staging(system_table.boot_services(), alloc_size);
|
let alloc_size = kernel.alloc_size() + environment.alloc_size() + metadata_size;
|
||||||
|
let staging = staging::allocate_code(system_table.boot_services(), alloc_size);
|
||||||
|
|
||||||
{
|
let (environ_base, next) = environment.serialize_unaligned_base(staging);
|
||||||
let mut next = staging;
|
|
||||||
next = kernel.serialize_unaligned(next);
|
let (kernel_base, mut next) = kernel.serialize_unaligned_base(next);
|
||||||
for item in kernel_metadata.iter() {
|
|
||||||
next = item.serialize_unaligned(next);
|
kernel_metadata.push(abi::ModuleInfoItem {
|
||||||
}
|
tag: ModuleInfoType::Environment,
|
||||||
|
value: ModuleInfoValue::Pointer(environ_base),
|
||||||
|
});
|
||||||
|
|
||||||
|
let modinfo_base = next.as_ptr();
|
||||||
|
|
||||||
|
let free_ptr = unsafe { next.as_ptr().byte_add(metadata_size) };
|
||||||
|
kernel_metadata.push(ModuleInfoItem {
|
||||||
|
tag: ModuleInfoType::FreeAddress,
|
||||||
|
value: ModuleInfoValue::Pointer(free_ptr),
|
||||||
|
});
|
||||||
|
|
||||||
|
for item in kernel_metadata.iter() {
|
||||||
|
next = item.serialize_unaligned(next);
|
||||||
}
|
}
|
||||||
|
|
||||||
loop {
|
boot::boot_kernel(system_table, kernel, kernel_base, modinfo_base, free_ptr);
|
||||||
system_table.boot_services().stall(10_000_000);
|
|
||||||
}
|
// boot_kernel shouldn't return
|
||||||
|
Status::ABORTED
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,8 +62,8 @@ impl<'a> Kernel<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn entry_addr(&self, load_addr: usize) -> usize {
|
pub fn entry_addr(&self, serialize_addr: *const u8) -> *const u8 {
|
||||||
self.parsed.header.e_entry as usize - self.min_loadaddr + load_addr
|
unsafe { serialize_addr.byte_add(self.parsed.header.e_entry as usize - self.min_loadaddr) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_symbol_headers(parsed: &Elf) -> Option<(SectionHeader, SectionHeader)> {
|
fn find_symbol_headers(parsed: &Elf) -> Option<(SectionHeader, SectionHeader)> {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use log::info;
|
use log::info;
|
||||||
use uefi::table::boot::{AllocateType, BootServices, MemoryType, PAGE_SIZE};
|
use uefi::table::boot::{AllocateType, BootServices, MemoryType, PAGE_SIZE};
|
||||||
|
|
||||||
pub fn allocate_staging(boot_services: &BootServices, size: usize) -> &'static mut [u8] {
|
pub fn allocate_code(boot_services: &BootServices, size: usize) -> &'static mut [u8] {
|
||||||
let allocation_type = if cfg!(target_arch = "x86_64") {
|
let allocation_type = if cfg!(target_arch = "x86_64") {
|
||||||
// Must be under 4GiB because we need to pass a 32-bit pointer
|
// Must be under 4GiB because we need to pass a 32-bit pointer
|
||||||
AllocateType::MaxAddress(4 * 1024 * 1024 * 1024)
|
AllocateType::MaxAddress(4 * 1024 * 1024 * 1024)
|
||||||
|
|
Loading…
Reference in a new issue