diff --git a/flake.nix b/flake.nix index 6c6b214..3895b6c 100644 --- a/flake.nix +++ b/flake.nix @@ -34,7 +34,10 @@ in { default = pkgs.mkShell { - packages = [ rust-uefi ]; + packages = [ + rust-uefi + pkgs.lldb + ]; RUST_SRC_PATH = "${rust-uefi}/lib/rustlib/src/rust/library"; }; diff --git a/src/abi.rs b/src/abi.rs index 061665e..cabfdea 100644 --- a/src/abi.rs +++ b/src/abi.rs @@ -2,8 +2,7 @@ extern crate alloc; use alloc::vec::Vec; use alloc::{collections::BTreeMap, string::String}; use bitflags::bitflags; -use uefi::prelude::BootServices; -use uefi::table::boot::MemoryType; +use uefi::table::boot::MemoryMap; use uefi::{ proto::console::gop::{GraphicsOutput, PixelFormat}, table::boot::PAGE_SIZE, @@ -123,53 +122,33 @@ struct EFIMemoryMapHeader { descriptor_version: u32, } -#[derive(Debug, Clone)] -pub struct EFIMemoryMapParams { - header: EFIMemoryMapHeader, - map: Vec, -} +impl Serialize for MemoryMap { + fn alignment() -> usize { + 4 + } -impl EFIMemoryMapParams { - pub fn from_boot_services(bs: &BootServices) -> Self { - let map = bs - .memory_map(MemoryType::LOADER_DATA) - .expect("Failed to get EFI memory map"); + fn size(&self) -> usize { + core::mem::size_of::() + self.as_raw().0.len() + } - let (raw, meta) = map.as_raw(); + fn serialize_raw(&self, out: &mut [u8]) { + let (raw, meta) = self.as_raw(); let header = EFIMemoryMapHeader { memory_size: meta.map_size, descriptor_size: meta.desc_size, descriptor_version: meta.desc_version, }; - - Self { - header, - map: raw.to_vec(), - } - } -} - -impl Serialize for EFIMemoryMapParams { - fn alignment() -> usize { - 4 - } - - fn size(&self) -> usize { - core::mem::size_of::() + self.map.len() - } - - fn serialize_raw(&self, out: &mut [u8]) { - let header_size = core::mem::size_of_val(&self.header); + let header_size = core::mem::size_of_val(&header); let header_bytes = unsafe { core::slice::from_raw_parts( - &self.header as *const EFIMemoryMapHeader as *const u8, + &header as *const EFIMemoryMapHeader as *const u8, header_size, ) }; out[..header_size].copy_from_slice(header_bytes); - out[header_size..].copy_from_slice(&self.map); + out[header_size..].copy_from_slice(raw); } } @@ -279,7 +258,6 @@ pub enum ModuleInfoType { } /// Value in the module info TLV -#[derive(Clone)] pub enum ModuleInfoValue { StaticString(&'static str), String(String), @@ -288,7 +266,7 @@ pub enum ModuleInfoValue { Buffer(Vec), Howto(Howto), EFIFrameBuffer(EFIFramebufferParams), - EFIMemoryMap(EFIMemoryMapParams), + EFIMemoryMap(MemoryMap), } impl ModuleInfoValue { @@ -349,7 +327,7 @@ impl core::fmt::Debug for ModuleInfoValue { /// Key and value in the module info TLV /// TODO: figure out how to make this properly typesafe -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct ModuleInfoItem { pub tag: ModuleInfoType, pub value: ModuleInfoValue, diff --git a/src/boot.rs b/src/boot.rs index 824bb7a..8745afa 100644 --- a/src/boot.rs +++ b/src/boot.rs @@ -5,13 +5,25 @@ pub use x86_64::boot_kernel; mod x86_64 { use core::{arch::global_asm, slice}; - use uefi::table::{boot::PAGE_SIZE, Boot, SystemTable}; + use log::warn; + use uefi::{ + prelude::BootServices, + proto::console::gop::GraphicsOutput, + table::{ + boot::{MemoryType, PAGE_SIZE}, + Boot, SystemTable, + }, + }; use x86_64::{ structures::paging::{PageTable, PageTableFlags}, PhysAddr, }; - use crate::{object::Kernel, staging::allocate_code}; + use crate::{ + abi::{EFIFramebufferParams, ModuleInfoItem, ModuleInfoType, ModuleInfoValue, Serialize}, + object::Kernel, + staging::allocate_code, + }; global_asm!( r#" @@ -67,6 +79,7 @@ trampoline_size: kernel_base: *const u8, modinfo_base: *const u8, free_ptr: *const u8, + staging: &mut [u8], ) { let misc_alloc = allocate_code( system_table.boot_services(), @@ -113,6 +126,9 @@ trampoline_size: ); unsafe { + // Serialize some final metadata. Calls exit boot services. + add_efi_metadata(system_table, staging); + let trampoline_func: TrampolineFunction = core::mem::transmute(misc_buf.trampoline.as_ptr()); trampoline_func( @@ -124,4 +140,50 @@ trampoline_size: ); } } + + unsafe fn add_efi_metadata(boot_system_table: SystemTable, mut staging: &mut [u8]) { + staging = add_efi_framebuffer_metadata(boot_system_table.boot_services(), staging); + + let (runtime_system_table, memory_map) = + boot_system_table.exit_boot_services(MemoryType::LOADER_DATA); + let memory_map_item = ModuleInfoItem { + tag: ModuleInfoType::EFIMemoryMap, + value: ModuleInfoValue::EFIMemoryMap(memory_map), + }; + staging = memory_map_item.serialize_unaligned(staging); + + let firmware_handle_item = ModuleInfoItem { + tag: ModuleInfoType::FirmwareHandle, + value: ModuleInfoValue::Pointer(runtime_system_table.as_ptr() as *const u8), + }; + let _staging = firmware_handle_item.serialize_unaligned(staging); + } + + fn add_efi_framebuffer_metadata<'a>( + boot_services: &BootServices, + staging: &'a mut [u8], + ) -> &'a mut [u8] { + let params = match parse_efi_framebuffer_params(boot_services) { + Ok(params) => params, + Err(err) => { + warn!("Unable to parse framebuffer parameters: {}", err); + return staging; + } + }; + + let item = ModuleInfoItem { + tag: ModuleInfoType::EFIFrameBuffer, + value: ModuleInfoValue::EFIFrameBuffer(params), + }; + + item.serialize_unaligned(staging) + } + fn parse_efi_framebuffer_params<'a>( + boot_services: &BootServices, + ) -> Result { + let gop_handle = boot_services.get_handle_for_protocol::()?; + let mut gop = boot_services.open_protocol_exclusive::(gop_handle)?; + EFIFramebufferParams::from_graphics_output(&mut gop) + .ok_or_else(|| uefi::Error::new(uefi::Status::INVALID_PARAMETER, ())) + } } diff --git a/src/main.rs b/src/main.rs index 22afb6d..53a39dd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,11 +8,11 @@ mod staging; extern crate alloc; -use abi::{Environment, ModuleInfoItem, ModuleInfoType, ModuleInfoValue, Serialize}; +use abi::{Environment, Howto, ModuleInfoItem, ModuleInfoType, ModuleInfoValue, Serialize}; use alloc::{collections::btree_map::BTreeMap, string::ToString}; use log::info; use object::{Kernel, Module}; -use uefi::{fs::FileSystem, prelude::*}; +use uefi::{fs::FileSystem, prelude::*, proto::loaded_image::LoadedImage}; #[entry] fn main(image_handle: Handle, mut system_table: SystemTable) -> Status { @@ -20,6 +20,16 @@ fn main(image_handle: Handle, mut system_table: SystemTable) -> Status { info!("Starting freeloader"); + { + let loaded_image_protocol = system_table + .boot_services() + .open_protocol_exclusive::(image_handle) + .expect("Failed to open LoadedImage protocol"); + let (base, size) = loaded_image_protocol.info(); + log::debug!("Image base: {:#x}, size: {:#x}", base as usize, size); + log::debug!("Main is at {:#x}", &main as *const _ as usize) + } + let kernel_data = { let mut esp = FileSystem::new( system_table @@ -42,12 +52,12 @@ fn main(image_handle: Handle, mut system_table: SystemTable) -> Status { "kernel".to_string(), )])); - // Allocate an extra 16KiB for items we have to add to metadata + // Allocate an extra 1 MiB for items we have to add to metadata, like memory map let metadata_size: usize = kernel_metadata .iter() .map(|item| item.alloc_size()) .sum::() - + 16 * 1024; + + 1024 * 1024; let alloc_size = kernel.alloc_size() + environment.alloc_size() + metadata_size; let staging = staging::allocate_code(system_table.boot_services(), alloc_size); @@ -61,7 +71,10 @@ fn main(image_handle: Handle, mut system_table: SystemTable) -> Status { value: ModuleInfoValue::Pointer(environ_base), }); - let modinfo_base = next.as_ptr(); + kernel_metadata.push(abi::ModuleInfoItem { + tag: ModuleInfoType::Howto, + value: ModuleInfoValue::Howto(Howto::VERBOSE | Howto::SERIAL), + }); let free_ptr = unsafe { next.as_ptr().byte_add(metadata_size) }; kernel_metadata.push(ModuleInfoItem { @@ -69,11 +82,19 @@ fn main(image_handle: Handle, mut system_table: SystemTable) -> Status { value: ModuleInfoValue::Pointer(free_ptr), }); + let modinfo_base = next.as_ptr(); for item in kernel_metadata.iter() { next = item.serialize_unaligned(next); } - boot::boot_kernel(system_table, kernel, kernel_base, modinfo_base, free_ptr); + boot::boot_kernel( + system_table, + kernel, + kernel_base, + modinfo_base, + free_ptr, + next, + ); // boot_kernel shouldn't return Status::ABORTED diff --git a/src/object.rs b/src/object.rs index aa2c569..b2dbb56 100644 --- a/src/object.rs +++ b/src/object.rs @@ -91,7 +91,7 @@ impl<'a> Kernel<'a> { .copy_from_slice(&self.elf[in_offset..in_offset + copy_size]); debug!( - "kernel: {:x}@{:x} -> {:x}", + "kernel: {:#x}@{:#x} -> {:#x}", copy_size, in_offset, (&out[out_offset] as *const u8) as usize