diff --git a/Cargo.lock b/Cargo.lock index 226556d3..5f448b7b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -303,7 +303,6 @@ dependencies = [ "multiboot", "naked-function", "one-shot-mutex", - "qemu-exit", "riscv", "sbi-rt", "spinning_top", diff --git a/Cargo.toml b/Cargo.toml index 72748a8e..f796af8e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/src/arch/x86_64/mod.rs b/src/arch/x86_64/mod.rs index 3fece3a1..90acc585 100644 --- a/src/arch/x86_64/mod.rs +++ b/src/arch/x86_64/mod.rs @@ -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 { @@ -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, diff --git a/src/main.rs b/src/main.rs index 0eb7b14d..a953980a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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] @@ -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?}"); diff --git a/src/os/uefi/fdt.rs b/src/os/uefi/fdt.rs new file mode 100644 index 00000000..cfc10049 --- /dev/null +++ b/src/os/uefi/fdt.rs @@ -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 { + 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> { + self.writer.end_node(self.root_node)?; + + self.writer.finish() + } + + pub fn rsdp(mut self, rsdp: u64) -> FdtWriterResult { + 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) + } +} diff --git a/src/os/uefi/mod.rs b/src/os/uefi/mod.rs index 9c0978c9..ca252ee9 100644 --- a/src/os/uefi/mod.rs +++ b/src/os/uefi/mod.rs @@ -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) -> 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 { @@ -42,3 +76,124 @@ fn read_app(bt: &BootServices) -> Vec { data } + +pub unsafe fn boot_kernel( + kernel_info: LoadedKernel, + fdt: Vec, + phys_addr_range: Range, +) -> ! { + 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]>; +} + +impl BootServicesExt for BootServices { + fn alloc_page_slice(&self, size: usize) -> uefi::Result<&'static mut [MaybeUninit]> { + 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 { + 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 + ) + } +} diff --git a/xtask/src/ci/qemu.rs b/xtask/src/ci/qemu.rs index a525e705..9429aa4d 100644 --- a/xtask/src/ci/qemu.rs +++ b/xtask/src/ci/qemu.rs @@ -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(); @@ -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);