Skip to content

Commit

Permalink
feat: UEFI MVP
Browse files Browse the repository at this point in the history
Co-authored-by: Martin Kröning <[email protected]>
Signed-off-by: Martin Kröning <[email protected]>
  • Loading branch information
sarahspberrypi and mkroening committed May 14, 2024
1 parent 8fb589d commit d4371af
Show file tree
Hide file tree
Showing 7 changed files with 207 additions and 24 deletions.
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ spinning_top = "0.3"

[target.'cfg(target_os = "uefi")'.dependencies]
uefi = { version = "0.28", features = ["alloc", "global_allocator", "panic_handler", "qemu"] }
qemu-exit = "3"

[target.'cfg(target_arch = "riscv64")'.dependencies]
fdt = "0.1"
Expand Down
6 changes: 2 additions & 4 deletions src/arch/x86_64/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ pub use console::Console;

#[cfg(target_os = "none")]
const KERNEL_STACK_SIZE: u64 = 32_768;
#[cfg(target_os = "none")]
const SERIAL_IO_PORT: u16 = 0x3F8;
pub const SERIAL_IO_PORT: u16 = 0x3F8;

#[cfg(target_os = "none")]
unsafe fn map_memory(address: usize, memory_size: usize) -> usize {
Expand All @@ -45,8 +44,7 @@ pub unsafe fn get_memory(memory_size: u64) -> u64 {
unsafe { map_memory(address, memory_size as usize) as u64 }
}

#[cfg(target_os = "none")]
unsafe fn enter_kernel(
pub unsafe fn enter_kernel(
stack: *mut u8,
entry: *const (),
raw_boot_info: &'static hermit_entry::boot_info::RawBootInfo,
Expand Down
4 changes: 0 additions & 4 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@
#![allow(unstable_name_collisions)]
#![allow(clippy::missing_safety_doc)]

#[cfg(not(target_os = "uefi"))]
use ::log::info;
#[cfg(not(target_os = "uefi"))]
use hermit_entry::boot_info::{BootInfo, RawBootInfo};

#[macro_use]
Expand All @@ -23,12 +21,10 @@ mod os;
))]
extern crate alloc;

#[cfg(not(target_os = "uefi"))]
trait BootInfoExt {
fn write(self) -> &'static RawBootInfo;
}

#[cfg(not(target_os = "uefi"))]
impl BootInfoExt for BootInfo {
fn write(self) -> &'static RawBootInfo {
info!("boot_info = {self:#x?}");
Expand Down
36 changes: 36 additions & 0 deletions src/os/uefi/fdt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use alloc::format;
use alloc::vec::Vec;

use vm_fdt::{FdtWriter, FdtWriterNode, FdtWriterResult};

pub struct Fdt {
writer: FdtWriter,
root_node: FdtWriterNode,
}

impl Fdt {
pub fn new() -> FdtWriterResult<Self> {
let mut writer = FdtWriter::new()?;

let root_node = writer.begin_node("")?;
writer.property_string("compatible", "hermit,uefi")?;
writer.property_u32("#address-cells", 0x2)?;
writer.property_u32("#size-cells", 0x2)?;

Ok(Self { writer, root_node })
}

pub fn finish(mut self) -> FdtWriterResult<Vec<u8>> {
self.writer.end_node(self.root_node)?;

self.writer.finish()
}

pub fn rsdp(mut self, rsdp: u64) -> FdtWriterResult<Self> {
let rsdp_node = self.writer.begin_node(&format!("hermit,rsdp@{rsdp:x}"))?;
self.writer.property_array_u64("reg", &[rsdp, 1])?;
self.writer.end_node(rsdp_node)?;

Ok(self)
}
}
171 changes: 163 additions & 8 deletions src/os/uefi/mod.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,63 @@
mod console;
mod fdt;

use alloc::string::String;
use alloc::vec::Vec;
use core::ffi::c_void;
use core::mem::MaybeUninit;
use core::ops::Range;
use core::{fmt, slice};

use align_address::Align;
use hermit_entry::boot_info::{
BootInfo, DeviceTreeAddress, HardwareInfo, PlatformInfo, SerialPortBase,
};
use hermit_entry::elf::{KernelObject, LoadedKernel};
use log::info;
use qemu_exit::QEMUExit;
use sptr::Strict;
use uefi::fs::{FileSystem, Path};
use uefi::prelude::*;
use uefi::table::boot::{
AllocateType, BootServices, MemoryDescriptor, MemoryMap, MemoryType, PAGE_SIZE,
};
use uefi::table::cfg;

pub use self::console::CONSOLE;
use self::fdt::Fdt;
use crate::{arch, BootInfoExt};

// Entry Point of the Uefi Loader
#[entry]
fn loader_main(_handle: Handle, mut system_table: SystemTable<Boot>) -> Status {
uefi::helpers::init(&mut system_table).unwrap();
crate::log::init();
let bs = system_table.boot_services();

let app = read_app(system_table.boot_services());
let kernel_image = read_app(bs);
let kernel = KernelObject::parse(&kernel_image).unwrap();

let string = String::from_utf8(app).unwrap();
println!("{string}");
let kernel_memory = bs.alloc_page_slice(kernel.mem_size()).unwrap();
let kernel_memory = &mut kernel_memory[..kernel.mem_size()];

let custom_exit_success = 3;
let qemu_exit_handle = qemu_exit::X86::new(0xf4, custom_exit_success);
qemu_exit_handle.exit_success()
let kernel_info = kernel.load_kernel(kernel_memory, kernel_memory.as_ptr() as u64);

let rsdp = system_table.rsdp();

drop(kernel_image);

let fdt = Fdt::new()
.unwrap()
.rsdp(u64::try_from(rsdp.expose_addr()).unwrap())
.unwrap()
.finish()
.unwrap();

let (_runtime_system_table, mut memory_map) =
system_table.exit_boot_services(MemoryType::LOADER_DATA);

let desc = largest_free_memory_region(&mut memory_map);
let phys_addr_range = desc.phys_start..desc.phys_start + (desc.page_count * PAGE_SIZE as u64);

unsafe { boot_kernel(kernel_info, fdt, phys_addr_range) }
}

fn read_app(bt: &BootServices) -> Vec<u8> {
Expand All @@ -42,3 +76,124 @@ fn read_app(bt: &BootServices) -> Vec<u8> {

data
}

pub unsafe fn boot_kernel(
kernel_info: LoadedKernel,
fdt: Vec<u8>,
phys_addr_range: Range<u64>,
) -> ! {
let LoadedKernel {
load_info,
entry_point,
} = kernel_info;

let device_tree = DeviceTreeAddress::new(u64::try_from(fdt.leak().as_ptr().addr()).unwrap());

let boot_info = BootInfo {
hardware_info: HardwareInfo {
phys_addr_range,
serial_port_base: SerialPortBase::new(arch::SERIAL_IO_PORT),
device_tree,
},
load_info,
platform_info: PlatformInfo::Fdt,
};

let stack = usize::try_from(boot_info.load_info.kernel_image_addr_range.end)
.unwrap()
.align_down(PAGE_SIZE);
let entry = sptr::from_exposed_addr(entry_point.try_into().unwrap());
let stack = sptr::from_exposed_addr_mut(stack);
let raw_boot_info = boot_info.write();

unsafe { arch::enter_kernel(stack, entry, raw_boot_info) }
}

trait BootServicesExt {
fn alloc_page_slice(&self, size: usize) -> uefi::Result<&'static mut [MaybeUninit<u8>]>;
}

impl BootServicesExt for BootServices {
fn alloc_page_slice(&self, size: usize) -> uefi::Result<&'static mut [MaybeUninit<u8>]> {
let size = size.align_up(PAGE_SIZE);
let phys_addr = self.allocate_pages(
AllocateType::AnyPages,
MemoryType::LOADER_DATA,
size / PAGE_SIZE,
)?;
let ptr = sptr::from_exposed_addr_mut(usize::try_from(phys_addr).unwrap());
Ok(unsafe { slice::from_raw_parts_mut(ptr, size) })
}
}

trait SystemTableBootExt {
/// Returns the RSDP.
///
/// This must be called before exiting boot services.
/// See [5.2.5.2. Finding the RSDP on UEFI Enabled Systems — ACPI Specification 6.5 documentation](https://uefi.org/specs/ACPI/6.5/05_ACPI_Software_Programming_Model.html#finding-the-rsdp-on-uefi-enabled-systems) for details.
fn rsdp(&self) -> *const c_void;
}

impl SystemTableBootExt for SystemTable<Boot> {
fn rsdp(&self) -> *const c_void {
let config_table = self.config_table();
let (rsdp, version) = if let Some(entry) = config_table
.iter()
.find(|entry| entry.guid == cfg::ACPI2_GUID)
{
(entry.address, 2)
} else {
let entry = config_table
.iter()
.find(|entry| entry.guid == cfg::ACPI_GUID)
.unwrap();
(entry.address, 1)
};
info!("Found ACPI {version} RSDP at {rsdp:p}");
rsdp
}
}

fn largest_free_memory_region<'a>(memory_map: &'a mut MemoryMap<'a>) -> &'a MemoryDescriptor {
memory_map.sort();

let max_desc = memory_map
.entries()
.filter(|desc| desc.ty == MemoryType::CONVENTIONAL)
.max_by_key(|desc| desc.page_count)
.unwrap();

info!("Memory map:");
for desc in memory_map.entries() {
if desc == max_desc {
info!("largest free memory region:");
}
info!("{}", desc.display());
}

max_desc
}

trait MemoryDescriptorExt {
fn display(&self) -> MemoryDescriptorDisplay<'_>;
}

impl MemoryDescriptorExt for MemoryDescriptor {
fn display(&self) -> MemoryDescriptorDisplay<'_> {
MemoryDescriptorDisplay { inner: self }
}
}

struct MemoryDescriptorDisplay<'a> {
inner: &'a MemoryDescriptor,
}

impl<'a> fmt::Display for MemoryDescriptorDisplay<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"start: {:#12x}, pages: {:#8x}, type: {:?}",
self.inner.phys_start, self.inner.page_count, self.inner.ty
)
}
}
12 changes: 6 additions & 6 deletions xtask/src/ci/qemu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@ impl Qemu {
if self.build.target() == Target::X86_64Uefi {
sh.create_dir("target/esp/efi/boot")?;
sh.copy_file(self.build.dist_object(), "target/esp/efi/boot/bootx64.efi")?;
sh.write_file("target/esp/efi/boot/hermit-app", "Hello, UEFI!\n")?;
sh.copy_file(
self.build.ci_image(&self.image),
"target/esp/efi/boot/hermit-app",
)?;
}

let target = self.build.target();
Expand Down Expand Up @@ -198,13 +201,10 @@ impl Qemu {
}

fn memory(&self) -> usize {
let mut memory = 32usize;
if self.image == "hello_c" {
memory = memory.max(64);
}
let mut memory = 64usize;
match self.build.target() {
Target::X86_64Uefi => {
memory = memory.max(64);
memory = memory.max(512);
}
Target::Aarch64 => {
memory = memory.max(256);
Expand Down

0 comments on commit d4371af

Please sign in to comment.