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_KEYBUF`: `struct keybuf` object with cached keys, don't really need it
|
||||
* `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_HOWTO`: u32 with a bunch of bitflags that start with `RB_` in `sys/sys/reboot.h`
|
||||
* `MODINFOMD_ELFHDR`: copy of the elf header
|
||||
|
|
65
src/abi.rs
65
src/abi.rs
|
@ -1,7 +1,10 @@
|
|||
extern crate alloc;
|
||||
use alloc::string::String;
|
||||
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] {
|
||||
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
|
||||
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().
|
||||
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]
|
||||
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 size = self.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)]
|
||||
#[repr(u32)]
|
||||
#[allow(dead_code)]
|
||||
/// Tag in the module info TLV
|
||||
pub enum ModuleInfoType {
|
||||
/// Module name (MODINFO_NAME)
|
||||
|
@ -123,6 +139,12 @@ pub enum ModuleInfoType {
|
|||
EndSymbols = 0x8004,
|
||||
/// Base virtual address of `PT_DYNAMIC` segment (MODINFOMD_DYNAMIC)
|
||||
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)
|
||||
SectionHeader = 0x8009,
|
||||
/// 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::Pointer(arg0) => f.debug_tuple("Pointer").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..]);
|
||||
}
|
||||
}
|
||||
|
||||
#[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),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
61
src/main.rs
61
src/main.rs
|
@ -2,10 +2,14 @@
|
|||
#![no_std]
|
||||
|
||||
mod abi;
|
||||
mod boot;
|
||||
mod object;
|
||||
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 object::{Kernel, Module};
|
||||
use uefi::{fs::FileSystem, prelude::*};
|
||||
|
@ -16,6 +20,7 @@ fn main(image_handle: Handle, mut system_table: SystemTable<Boot>) -> Status {
|
|||
|
||||
info!("Starting freeloader");
|
||||
|
||||
let kernel_data = {
|
||||
let mut esp = FileSystem::new(
|
||||
system_table
|
||||
.boot_services()
|
||||
|
@ -23,35 +28,53 @@ fn main(image_handle: Handle, mut system_table: SystemTable<Boot>) -> Status {
|
|||
.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");
|
||||
|
||||
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);
|
||||
|
||||
let metadata_size: usize = kernel_metadata
|
||||
.iter()
|
||||
.map(|item| item.size().next_multiple_of(ModuleInfoItem::alignment()))
|
||||
.sum();
|
||||
let environment = Environment(BTreeMap::from([(
|
||||
"kernel".to_string(),
|
||||
"kernel".to_string(),
|
||||
)]));
|
||||
|
||||
// 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 (kernel_base, mut next) = kernel.serialize_unaligned_base(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),
|
||||
});
|
||||
|
||||
{
|
||||
let mut next = staging;
|
||||
next = kernel.serialize_unaligned(next);
|
||||
for item in kernel_metadata.iter() {
|
||||
next = item.serialize_unaligned(next);
|
||||
}
|
||||
}
|
||||
|
||||
loop {
|
||||
system_table.boot_services().stall(10_000_000);
|
||||
}
|
||||
boot::boot_kernel(system_table, kernel, kernel_base, modinfo_base, free_ptr);
|
||||
|
||||
// boot_kernel shouldn't return
|
||||
Status::ABORTED
|
||||
}
|
||||
|
|
|
@ -62,8 +62,8 @@ impl<'a> Kernel<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn entry_addr(&self, load_addr: usize) -> usize {
|
||||
self.parsed.header.e_entry as usize - self.min_loadaddr + load_addr
|
||||
pub fn entry_addr(&self, serialize_addr: *const u8) -> *const u8 {
|
||||
unsafe { serialize_addr.byte_add(self.parsed.header.e_entry as usize - self.min_loadaddr) }
|
||||
}
|
||||
|
||||
fn find_symbol_headers(parsed: &Elf) -> Option<(SectionHeader, SectionHeader)> {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use log::info;
|
||||
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") {
|
||||
// Must be under 4GiB because we need to pass a 32-bit pointer
|
||||
AllocateType::MaxAddress(4 * 1024 * 1024 * 1024)
|
||||
|
|
Loading…
Reference in a new issue