diff --git a/Cargo.lock b/Cargo.lock index 6221c51..f13c1e5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2054,9 +2054,9 @@ dependencies = [ [[package]] name = "uefi" -version = "0.31.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "920793ff1148ab68a07ab0b2b866870886cd1e5c0b1b94f451d66158d0ff1a77" +checksum = "91f17ea8502a6bd414acb2bf5194f90ca4c48e33a2d18cb57eab3294d2050d99" dependencies = [ "bitflags 2.5.0", "cfg-if", @@ -2064,15 +2064,15 @@ dependencies = [ "ptr_meta", "ucs2", "uefi-macros", - "uefi-raw 0.7.0", + "uefi-raw 0.8.0", "uguid", ] [[package]] name = "uefi-macros" -version = "0.15.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0732e421268a2d6d53c95c3c0f4496ccc5c08aa7381458c677153d5d77ce39e" +checksum = "c19ee3a01d435eda42cb9931269b349d28a1762f91ddf01c68d276f74b957cc3" dependencies = [ "proc-macro2", "quote", @@ -2092,9 +2092,9 @@ dependencies = [ [[package]] name = "uefi-raw" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e537b93f83150df09588ca6658e881b2784e8b5f9588f1c7b72a85b72ea71ce" +checksum = "b463030b802e1265a3800fab24df95d3229c202c2e408832a206f05b4d1496ca" dependencies = [ "bitflags 2.5.0", "ptr_meta", diff --git a/towboot/Cargo.toml b/towboot/Cargo.toml index e1e9dc9..42b28a0 100644 --- a/towboot/Cargo.toml +++ b/towboot/Cargo.toml @@ -12,7 +12,7 @@ default-target = "i686-unknown-uefi" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -uefi = { version = "0.31", features = ["alloc", "global_allocator", "logger", "panic_handler"] } +uefi = { version = "0.32", features = ["alloc", "global_allocator", "logger", "panic_handler"] } acpi = "5.0" smbios-lib = { git = "https://github.com/hhuOS/smbios-lib.git", branch = "main", default-features = false, features = ["no_std"] } x86 = "0.52" diff --git a/towboot/src/boot/config_tables.rs b/towboot/src/boot/config_tables.rs index f16517d..9ff4876 100644 --- a/towboot/src/boot/config_tables.rs +++ b/towboot/src/boot/config_tables.rs @@ -1,9 +1,12 @@ //! Handle UEFI config tables. use alloc::slice; +use alloc::vec::Vec; + use log::{debug, warn}; use multiboot12::information::InfoBuilder; use acpi::rsdp::Rsdp; use smbioslib::{SMBiosEntryPoint32, SMBiosEntryPoint64}; +use uefi::system::with_config_table; use uefi::table::cfg::{ ConfigTableEntry, ACPI_GUID, ACPI2_GUID, DEBUG_IMAGE_INFO_GUID, DXE_SERVICES_GUID, HAND_OFF_BLOCK_LIST_GUID, LZMA_COMPRESS_GUID, @@ -13,22 +16,25 @@ use uefi::table::cfg::{ /// Go through all of the configuration tables. /// Some of them are interesting for Multiboot2. -pub(super) fn parse_for_multiboot( - config_tables: &[ConfigTableEntry], info_builder: &mut InfoBuilder, -) { +pub(super) fn parse_for_multiboot(info_builder: &mut InfoBuilder) { + // first, copy all config table pointers + // TODO: remove this when with_config_table takes a FnMut + let config_tables: Vec = with_config_table(|s| + s.iter().cloned().collect() + ); debug!("going through configuration tables..."); for table in config_tables { match table.guid { - ACPI_GUID => handle_acpi(table, info_builder), - ACPI2_GUID => handle_acpi(table, info_builder), + ACPI_GUID => handle_acpi(&table, info_builder), + ACPI2_GUID => handle_acpi(&table, info_builder), DEBUG_IMAGE_INFO_GUID => debug!("ignoring image debug info"), DXE_SERVICES_GUID => debug!("ignoring dxe services table"), HAND_OFF_BLOCK_LIST_GUID => debug!("ignoring hand-off block list"), LZMA_COMPRESS_GUID => debug!("ignoring lzma filesystem"), MEMORY_STATUS_CODE_RECORD_GUID => debug!("ignoring early memory info"), MEMORY_TYPE_INFORMATION_GUID => debug!("ignoring early memory info"), - SMBIOS_GUID => handle_smbios(table, info_builder), - SMBIOS3_GUID => handle_smbios(table, info_builder), + SMBIOS_GUID => handle_smbios(&table, info_builder), + SMBIOS3_GUID => handle_smbios(&table, info_builder), guid => debug!("ignoring table {guid}"), } } diff --git a/towboot/src/boot/mod.rs b/towboot/src/boot/mod.rs index 5e74ea1..a24d634 100644 --- a/towboot/src/boot/mod.rs +++ b/towboot/src/boot/mod.rs @@ -21,11 +21,10 @@ use core::arch::asm; use core::ffi::c_void; use core::ptr::NonNull; use uefi::prelude::*; +use uefi::boot::{exit_boot_services, image_handle, memory_map, MemoryType, ScopedProtocol}; use uefi::mem::memory_map::{MemoryMap, MemoryMapMut}; use uefi::proto::console::gop::GraphicsOutput; -use uefi::table::system_table_boot; -use uefi::table::boot::{ScopedProtocol, MemoryType}; -use uefi::table::cfg::ConfigTableEntry; +use uefi::table::system_table_raw; use log::{debug, info, error, warn}; @@ -204,8 +203,8 @@ fn get_kernel_uefi_entry( fn prepare_multiboot_information( entry: &Entry, header: Header, load_base_address: Option, modules: &[Allocation], symbols: Option, - graphics_output: Option>, image: Handle, - config_tables: &[ConfigTableEntry], boot_services_exited: bool, + graphics_output: Option>, + boot_services_exited: bool, ) -> InfoBuilder { let mut info_builder = header.info_builder(); @@ -249,12 +248,12 @@ fn prepare_multiboot_information( // This only has an effect on Multiboot2. // TODO: Does this stay valid when we exit Boot Services? - let systab_ptr = system_table_boot() + let systab_ptr = system_table_raw() .expect("failed to get System Table") .as_ptr(); - let image_handle_ptr = (unsafe { - core::mem::transmute::<_, NonNull>(image) - }).as_ptr(); + let image_handle_ptr = unsafe { + core::mem::transmute::<_, NonNull>(image_handle()) + }.as_ptr(); if cfg!(target_arch = "x86") { info_builder.set_system_table_ia32(Some( (systab_ptr as usize).try_into().unwrap() @@ -273,7 +272,7 @@ fn prepare_multiboot_information( warn!("don't know how to pass the UEFI data on this target"); } - config_tables::parse_for_multiboot(config_tables, &mut info_builder); + config_tables::parse_for_multiboot(&mut info_builder); if !boot_services_exited { info_builder.set_boot_services_not_exited(); @@ -311,10 +310,9 @@ impl<'a> PreparedEntry<'a> { /// The returned `PreparedEntry` can be used to actually boot. /// This is non-destructive and will always return. pub(crate) fn new( - entry: &'a Entry, image: Handle, image_fs_handle: Handle, - systab: &SystemTable, + entry: &'a Entry, image_fs_handle: Handle, ) -> Result, Status> { - let kernel_vec: Vec = File::open(&entry.image, image_fs_handle, systab)?.try_into()?; + let kernel_vec: Vec = File::open(&entry.image, image_fs_handle)?.try_into()?; let header = Header::from_slice(kernel_vec.as_slice()).ok_or_else(|| { error!("invalid Multiboot header"); Status::LOAD_ERROR @@ -326,7 +324,7 @@ impl<'a> PreparedEntry<'a> { // Load all modules, fail completely if one fails to load. // just always use whole pages, that's easier for us let modules_vec: Vec = entry.modules.iter().map(|module| - File::open(&module.image, image_fs_handle, systab) + File::open(&module.image, image_fs_handle) .and_then(|f| f.try_into_allocation(&entry.quirks)) ).collect::, _>>()?; info!("loaded {} modules", modules_vec.len()); @@ -334,12 +332,11 @@ impl<'a> PreparedEntry<'a> { debug!("loaded module {} to {:?}", index, module.as_ptr()); } - let graphics_output = video::setup_video(&header, systab, &entry.quirks); + let graphics_output = video::setup_video(&header, &entry.quirks); let multiboot_information = prepare_multiboot_information( entry, header, loaded_kernel.load_base_address, &modules_vec, - loaded_kernel.symbols_struct(), graphics_output, image, - systab.config_table(), + loaded_kernel.symbols_struct(), graphics_output, !entry.quirks.contains(&Quirk::DontExitBootServices), ); @@ -358,11 +355,11 @@ impl<'a> PreparedEntry<'a> { /// 5. jump! /// /// This function won't return. - pub(crate) fn boot(mut self, systab: SystemTable) -> ! { + pub(crate) fn boot(mut self) -> ! { // Get a memory map. // This won't be completely accurate as we're still going to allocate // and deallocate a bit, but it gives us a rough estimate. - let map = systab.boot_services().memory_map(MemoryType::LOADER_DATA) + let map = memory_map(MemoryType::LOADER_DATA) .expect("failed to get memory map"); // Estimate how many entries there will be and add some. let estimated_count = map.entries().len() + 5; @@ -385,13 +382,10 @@ impl<'a> PreparedEntry<'a> { debug!("passing signature {signature:x} to kernel..."); let mut memory_map = if self.loaded_kernel.should_exit_boot_services { info!("exiting boot services..."); - let (_systab, memory_map) = unsafe { - systab.exit_boot_services(MemoryType::LOADER_DATA) - }; + unsafe { exit_boot_services(MemoryType::LOADER_DATA) } // now, write! won't work anymore. Also, we can't allocate any memory. - memory_map } else { - let memory_map = systab.boot_services().memory_map(MemoryType::LOADER_DATA).unwrap(); + let memory_map = memory_map(MemoryType::LOADER_DATA).unwrap(); debug!("got {} memory areas", memory_map.entries().len()); memory_map }; diff --git a/towboot/src/boot/video.rs b/towboot/src/boot/video.rs index 00332e0..29bda48 100644 --- a/towboot/src/boot/video.rs +++ b/towboot/src/boot/video.rs @@ -4,8 +4,11 @@ use alloc::collections::btree_set::BTreeSet; use alloc::vec::Vec; use uefi::prelude::*; +use uefi::boot::{ + find_handles, image_handle, open_protocol, + OpenProtocolAttributes, OpenProtocolParams, ScopedProtocol, +}; use uefi::proto::console::gop::{GraphicsOutput, Mode, PixelBitmask, PixelFormat}; -use uefi::table::boot::{ScopedProtocol, OpenProtocolParams, OpenProtocolAttributes}; use log::{debug, warn, info, error}; @@ -18,12 +21,13 @@ use towboot_config::Quirk; /// /// If there are multiple GPUs available, simply choose the first one. /// If there is no available mode that matches, just use the one we're already in. -pub fn setup_video<'a>( - header: &Header, systab: &'a SystemTable, quirks: &BTreeSet -) -> Option> { +pub fn setup_video( + header: &Header, quirks: &BTreeSet, +) -> Option> { info!("setting up the video..."); let wanted_resolution = match ( - header.get_preferred_video_mode(), quirks.contains(&Quirk::KeepResolution) + header.get_preferred_video_mode(), + quirks.contains(&Quirk::KeepResolution), ) { (Some(mode), false) => { if mode.is_graphics() { @@ -49,9 +53,7 @@ pub fn setup_video<'a>( _ => None, }; // just get the first one - let handles = systab - .boot_services() - .find_handles::() + let handles = find_handles::() .expect("failed to list available graphics outputs"); let handle = handles.first().or_else(|| { warn!("Failed to find a graphics output. Do you have a graphics card (and a driver)?"); @@ -59,15 +61,15 @@ pub fn setup_video<'a>( })?; // Opening a protocol non-exclusively is unsafe, but otherwise we won't get // to see any new log messages. - let mut output: ScopedProtocol = unsafe { systab.boot_services().open_protocol( + let mut output: ScopedProtocol = unsafe { open_protocol( OpenProtocolParams { handle: *handle, - agent: systab.boot_services().image_handle(), + agent: image_handle(), controller: None, }, OpenProtocolAttributes::GetProtocol, ).ok() }?; - let modes: Vec = output.modes(systab.boot_services()).collect(); + let modes: Vec = output.modes().collect(); debug!( "available video modes: {:?}", modes.iter().map(Mode::info).map(|i| (i.resolution(), i.pixel_format())) diff --git a/towboot/src/config.rs b/towboot/src/config.rs index 2b2b2fa..168b2eb 100644 --- a/towboot/src/config.rs +++ b/towboot/src/config.rs @@ -43,10 +43,10 @@ fn version_info() -> String { /// /// Returns None if just a help text has been displayed. pub fn get( - image_fs_handle: Handle, load_options: &str, systab: &SystemTable + image_fs_handle: Handle, load_options: &str, ) -> Result, Status> { match parse_load_options(load_options, &version_info()) { - Ok(Some(ConfigSource::File(s))) => Ok(Some(read_file(image_fs_handle, &s, systab)?)), + Ok(Some(ConfigSource::File(s))) => Ok(Some(read_file(image_fs_handle, &s)?)), Ok(Some(ConfigSource::Given(c))) => Ok(Some(c)), Ok(None) => Ok(None), Err(()) => Err(Status::INVALID_PARAMETER), @@ -54,8 +54,8 @@ pub fn get( } /// Try to read and parse the configuration from the given file. -fn read_file(image_fs_handle: Handle, file_name: &str, systab: &SystemTable) -> Result { - let text: Vec = File::open(file_name, image_fs_handle, systab)?.try_into()?; +fn read_file(image_fs_handle: Handle, file_name: &str) -> Result { + let text: Vec = File::open(file_name, image_fs_handle)?.try_into()?; let mut config: Config = toml::from_slice(text.as_slice()).expect("failed to parse config file"); config.src = file_name.to_string(); Ok(config) diff --git a/towboot/src/file.rs b/towboot/src/file.rs index 491c357..514b8e9 100644 --- a/towboot/src/file.rs +++ b/towboot/src/file.rs @@ -9,6 +9,7 @@ use alloc::string::ToString; use log::{info, error}; use uefi::prelude::*; +use uefi::boot::{find_handles, open_protocol_exclusive}; use uefi::fs::{Path, PathBuf}; use uefi::data_types::CString16; use uefi::proto::media::fs::SimpleFileSystem; @@ -38,7 +39,7 @@ impl<'a> File<'a> { /// * `Status::NOT_FOUND`: the file does not exist /// * `Status::PROTOCOL_ERROR`: the file name is not a valid string /// * `Status::UNSUPPORTED`: the given path does exist, but it's a directory - pub(crate) fn open(name: &'a str, image_fs_handle: Handle, systab: &SystemTable) -> Result { + pub(crate) fn open(name: &'a str, image_fs_handle: Handle) -> Result { info!("loading file '{name}'..."); let file_name = CString16::try_from(name) .map_err(|e| { @@ -56,9 +57,7 @@ impl<'a> File<'a> { .strip_suffix(':') .unwrap() .strip_prefix("fs") { - let filesystems = systab - .boot_services() - .find_handles::() + let filesystems = find_handles::() .map_err(|e| e.status())?; let fs = filesystems.into_iter().nth( idx.parse::().map_err(|_| { @@ -78,9 +77,7 @@ impl<'a> File<'a> { } else { (image_fs_handle, file_name) }; - let mut fs = systab - .boot_services() - .open_protocol_exclusive::(fs_handle) + let mut fs = open_protocol_exclusive::(fs_handle) .map_err(|e| e.status())?; let file_handle = match fs.open_volume().map_err(|e| e.status())?.open( &file_name, diff --git a/towboot/src/main.rs b/towboot/src/main.rs index 5576a75..f85029e 100644 --- a/towboot/src/main.rs +++ b/towboot/src/main.rs @@ -10,11 +10,11 @@ extern crate alloc; use core::str::FromStr; use alloc::string::ToString; +use uefi::prelude::*; +use uefi::boot::{image_handle, open_protocol_exclusive}; use uefi::fs::PathBuf; use uefi::data_types::CString16; -use uefi::prelude::*; -use uefi::proto::loaded_image::LoadedImage; -use uefi::proto::loaded_image::LoadOptionsError; +use uefi::proto::loaded_image::{LoadedImage, LoadOptionsError}; use log::{debug, info, warn, error}; @@ -27,99 +27,78 @@ mod mem; mod menu; #[entry] -fn efi_main(image: Handle, mut systab: SystemTable) -> Status { - // Putting this comment above the function breaks the entry annotation. - //! This is the main function. - //! Startup happens here. +/// This is the main function. Startup happens here. +fn main() -> Status { uefi::helpers::init().expect("Failed to initialize utilities"); log::set_max_level(log::LevelFilter::Info); - - // blocks are so cool, I wish the borrow checker was real - // - // So what's happening here is that `open_protocol` returns a - // `ScopedProtocol` which requires us to (read this as "enforces that we") - // properly close (eg. drop) all protocols we open before exiting - // Boot Services. `exit_boot_services` also consumes our `SystemTable` and - // spits out a new one with less functionality. - // This closure exists so that we can use protocols inside and they're all - // gone after. - // - // This may seem safe and sound but I'm not sure whether it actually is. - // There's also the global singleton `uefi_services::system_table`, - // but this panics at least if we've exited the Boot Services. - // (That's why we must never hold a reference to its return value!) - let (config, image_fs_handle) = { - // get information about the way we were loaded - // the interesting thing here is the partition handle - let loaded_image = systab - .boot_services() - .open_protocol_exclusive::(image) - .expect("Failed to open loaded image protocol"); - - // get the load options - let load_options = match loaded_image.load_options_as_cstr16() { - Ok(s) => { - debug!("got load options: {s:}"); - Some(s.to_string()) - }, - Err(LoadOptionsError::NotSet) => { - debug!("got no load options"); - None - }, - Err(e) => { - warn!("failed to get load options: {e:?}"); - warn!("assuming there were none"); - None - }, - }; - - // get the filesystem - let image_fs_handle = loaded_image.device().expect("the image to be loaded from a device"); - - let mut config = match config::get( - image_fs_handle, load_options.as_deref().unwrap_or_default(), &systab, - ) { - Ok(Some(c)) => c, - Ok(None) => return Status::SUCCESS, - Err(e) => { - error!("failed to get config: {e:?}"); - return Status::INVALID_PARAMETER - } - }; - if let Some(level) = &config.log_level { - if let Ok(level) = log::LevelFilter::from_str(level) { - log::set_max_level(level); - } else { - warn!("'{level}' is not a valid log level, using default"); - } + + // get information about the way we were loaded + // the interesting thing here is the partition handle + let loaded_image = open_protocol_exclusive::(image_handle()) + .expect("Failed to open loaded image protocol"); + + // get the load options + let load_options = match loaded_image.load_options_as_cstr16() { + Ok(s) => { + debug!("got load options: {s:}"); + Some(s.to_string()) } - // resolve paths relative to the config file itself - if let Some(config_parent) = PathBuf::from( - CString16::try_from(config.src.as_str()) - .expect("paths to be valid strings") - ).parent() { - for path in config.needed_files() { - if path.starts_with('\\') { - continue - } - let mut buf = config_parent.clone(); - buf.push(PathBuf::from(CString16::try_from(path.as_str()) - .expect("paths to be valid strings") - )); - *path = buf.to_string(); - } + Err(LoadOptionsError::NotSet) => { + debug!("got no load options"); + None + } + Err(e) => { + warn!("failed to get load options: {e:?}"); + warn!("assuming there were none"); + None } - debug!("config: {config:?}"); - (config, image_fs_handle) }; - let entry_to_boot = menu::choose(&config, &mut systab); + + // get the filesystem + let image_fs_handle = loaded_image.device().expect("the image to be loaded from a device"); + + let mut config = match config::get( + image_fs_handle, load_options.as_deref().unwrap_or_default(), + ) { + Ok(Some(c)) => c, + Ok(None) => return Status::SUCCESS, + Err(e) => { + error!("failed to get config: {e:?}"); + return Status::INVALID_PARAMETER; + } + }; + if let Some(level) = &config.log_level { + if let Ok(level) = log::LevelFilter::from_str(level) { + log::set_max_level(level); + } else { + warn!("'{level}' is not a valid log level, using default"); + } + } + // resolve paths relative to the config file itself + if let Some(config_parent) = PathBuf::from( + CString16::try_from(config.src.as_str()) + .expect("paths to be valid strings") + ).parent() { + for path in config.needed_files() { + if path.starts_with('\\') { + continue; + } + let mut buf = config_parent.clone(); + buf.push(PathBuf::from(CString16::try_from(path.as_str()) + .expect("paths to be valid strings") + )); + *path = buf.to_string(); + } + } + debug!("config: {config:?}"); + let entry_to_boot = menu::choose(&config); debug!("okay, trying to load {entry_to_boot:?}"); info!("loading {entry_to_boot}..."); - match boot::PreparedEntry::new(entry_to_boot, image, image_fs_handle, &systab) { + match boot::PreparedEntry::new(entry_to_boot, image_fs_handle) { Ok(e) => { info!("booting {entry_to_boot}..."); - e.boot(systab); + e.boot(); }, Err(e) => { error!("failed to prepare the entry: {e:?}"); diff --git a/towboot/src/mem.rs b/towboot/src/mem.rs index ea9e819..ede9cb5 100644 --- a/towboot/src/mem.rs +++ b/towboot/src/mem.rs @@ -8,14 +8,15 @@ //! Also, gathering memory map information for the kernel happens here. use core::mem::size_of; +use core::ptr::NonNull; use alloc::boxed::Box; use alloc::collections::btree_set::BTreeSet; use alloc::vec::Vec; use uefi::prelude::*; +use uefi::boot::{allocate_pages, free_pages, memory_map}; use uefi::mem::memory_map::{MemoryMap, MemoryMapMut}; -use uefi::table::system_table_boot; use uefi::table::boot::{AllocateType, MemoryDescriptor, MemoryType}; use log::{debug, warn, error}; @@ -29,7 +30,7 @@ pub(super) const PAGE_SIZE: usize = 4096; /// Tracks our own allocations. #[derive(Debug)] pub(super) struct Allocation { - ptr: u64, + ptr: NonNull, pub len: usize, pages: usize, /// the address of memory where it should have been allocated @@ -42,12 +43,7 @@ impl Drop for Allocation { fn drop(&mut self) { // We can't free memory after we've exited boot services. // But this only happens in `PreparedEntry::boot` and this function doesn't return. - unsafe { - system_table_boot() - .expect("failed to get System Table") - .boot_services() - .free_pages(self.ptr, self.pages) - } + unsafe { free_pages(self.ptr, self.pages) } // let's just panic if we can't free .expect("failed to free the allocated memory"); } @@ -66,15 +62,11 @@ impl Allocation { /// [`move_to_where_it_should_be`]: struct.Allocation.html#method.move_to_where_it_should_be pub(crate) fn new_at(address: usize, size: usize) -> Result{ let count_pages = Self::calculate_page_count(size); - match system_table_boot() - .expect("failed to get System Table") - .boot_services() - .allocate_pages( - AllocateType::Address(address.try_into().unwrap()), - MemoryType::LOADER_DATA, - count_pages - ) - { + match allocate_pages( + AllocateType::Address(address.try_into().unwrap()), + MemoryType::LOADER_DATA, + count_pages + ) { Ok(ptr) => Ok(Allocation { ptr, len: size, pages: count_pages, should_be_at: None }), Err(e) => { warn!("failed to allocate {size} bytes of memory at {address:x}: {e:?}"); @@ -94,10 +86,7 @@ impl Allocation { /// Note: This will round up to whole pages. pub(crate) fn new_under_4gb(size: usize, quirks: &BTreeSet) -> Result { let count_pages = Self::calculate_page_count(size); - let ptr = system_table_boot() - .expect("failed to get System Table") - .boot_services() - .allocate_pages( + let ptr = allocate_pages( AllocateType::MaxAddress(if quirks.contains(&Quirk::ModulesBelow200Mb) { 200 * 1024 * 1024 } else { @@ -122,12 +111,12 @@ impl Allocation { /// Return a slice that references the associated memory. pub(crate) fn as_mut_slice(&mut self) -> &mut [u8] { - unsafe { core::slice::from_raw_parts_mut(self.ptr as *mut u8, self.pages * PAGE_SIZE) } + unsafe { core::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.pages * PAGE_SIZE) } } /// Get the pointer inside. pub(crate) fn as_ptr(&self) -> *const u8 { - self.ptr as *const u8 + self.ptr.as_ptr() } /// Move to the desired location. @@ -152,9 +141,8 @@ impl Allocation { assert!(filter.next().is_none()); // there shouldn't be another matching entry } let dest: usize = a.try_into().unwrap(); - let src: usize = self.ptr.try_into().unwrap(); - core::ptr::copy(src as *mut u8, dest as *mut u8, self.len); - self.ptr = a; + core::ptr::copy(self.ptr.as_ptr(), dest as *mut u8, self.len); + self.ptr = NonNull::new(a as *mut u8).unwrap(); self.should_be_at = None; } } @@ -163,10 +151,8 @@ impl Allocation { /// Show the current memory map. fn dump_memory_map() { debug!("memory map:"); - let mut memory_map = system_table_boot() - .expect("failed to get System Table") - .boot_services() - .memory_map(MemoryType::LOADER_DATA).expect("failed to get memory map"); + let mut memory_map = memory_map(MemoryType::LOADER_DATA) + .expect("failed to get memory map"); memory_map.sort(); for descriptor in memory_map.entries() { debug!("{descriptor:?}"); diff --git a/towboot/src/menu.rs b/towboot/src/menu.rs index b35e40e..30d3dc5 100644 --- a/towboot/src/menu.rs +++ b/towboot/src/menu.rs @@ -4,7 +4,9 @@ use alloc::collections::btree_map::BTreeMap; use alloc::string::String; use uefi::prelude::*; +use uefi::boot::{create_event, set_timer, wait_for_event}; use uefi::proto::console::text::{Key, ScanCode}; +use uefi::system::{with_stdin, with_stdout}; use uefi::table::boot::{EventType, TimerTrigger, Tpl}; use log::{error, warn}; @@ -21,7 +23,7 @@ use towboot_config::{Config, Entry}; /// If the default entry is missing, it will try to use the first one instead. /// If there are no entries, it will panic. // TODO: perhaps this should return a Result? -pub fn choose<'a>(config: &'a Config, systab: &mut SystemTable) -> &'a Entry { +pub fn choose(config: &Config) -> &Entry { let default_entry = config.entries.get(&config.default).unwrap_or_else(|| { warn!("default entry is missing, trying the first one"); config.entries.values().next().expect("no entries") @@ -29,7 +31,7 @@ pub fn choose<'a>(config: &'a Config, systab: &mut SystemTable) -> &'a Ent if let Some(0) = config.timeout { return default_entry } - match display_menu(config, default_entry, systab) { + match display_menu(config, default_entry) { Ok(entry) => entry, Err(err) => { error!("failed to display menu: {err:?}"); @@ -41,25 +43,25 @@ pub fn choose<'a>(config: &'a Config, systab: &mut SystemTable) -> &'a Ent /// Display the menu. This can fail. fn display_menu<'a>( - config: &'a Config, default_entry: &'a Entry, systab: &mut SystemTable + config: &'a Config, default_entry: &'a Entry, ) -> uefi::Result<&'a Entry> { if let Some(timeout) = config.timeout { - writeln!( - systab.stdout(), + with_stdout(|stdout | writeln!( + stdout, "towboot: booting {} ({}) in {} seconds... (press ESC to change)", config.default, default_entry.name.as_ref().unwrap_or(&config.default), timeout, - ).unwrap(); + )).unwrap(); // This is safe because there is no callback. - let timer = unsafe { systab.boot_services().create_event( + let timer = unsafe { create_event( EventType::TIMER, Tpl::APPLICATION, None, None ) }?; - systab.boot_services().set_timer( + set_timer( &timer, TimerTrigger::Relative(u64::from(timeout) * 10_000_000) )?; - let key_event = systab.stdin().wait_for_key_event() + let key_event = with_stdin(|stdin| stdin.wait_for_key_event()) .expect("to be able to wait for key events"); loop { - match systab.boot_services().wait_for_event( + match wait_for_event( // this is safe because we're never calling close_event &mut [ unsafe { key_event.unsafe_clone() }, @@ -67,7 +69,7 @@ fn display_menu<'a>( ] ).discard_errdata()? { // key - 0 => match systab.stdin().read_key()? { + 0 => match with_stdin(|stdin| stdin.read_key())? { Some(Key::Special(ScanCode::ESCAPE)) => break, _ => (), }, @@ -76,38 +78,40 @@ fn display_menu<'a>( e => warn!("firmware returned invalid event {e}"), } } - systab.boot_services().set_timer(&timer, TimerTrigger::Cancel)?; - } - writeln!(systab.stdout(), "available entries:").unwrap(); - for (index, (key, entry)) in config.entries.iter().enumerate() { - writeln!( - systab.stdout(), "{index}. [{key}] {entry}" - ).unwrap(); + set_timer(&timer, TimerTrigger::Cancel)?; } + with_stdout(|stdout| { + writeln!(stdout, "available entries:").unwrap(); + for (index, (key, entry)) in config.entries.iter().enumerate() { + writeln!(stdout, "{index}. [{key}] {entry}").unwrap(); + } + }); loop { - match select_entry(&config.entries, systab) { + match select_entry(&config.entries) { Ok(entry) => return Ok(entry), Err(err) => { - writeln!(systab.stdout(), "invalid choice: {err:?}").unwrap(); + with_stdout(|stdout| writeln!(stdout, "invalid choice: {err:?}")).unwrap(); } } } } /// Try to select an entry. -fn select_entry<'a>( - entries: &'a BTreeMap, systab: &mut SystemTable -) -> uefi::Result<&'a Entry> { +fn select_entry(entries: &BTreeMap) -> uefi::Result<&Entry> { let mut value = String::new(); - let key_event = systab.stdin().wait_for_key_event() + let key_event = with_stdin(|stdin| stdin.wait_for_key_event()) .expect("to be able to wait for key events"); loop { - write!(systab.stdout(), "\rplease select an entry to boot: {value} ").unwrap(); - systab.boot_services().wait_for_event( + with_stdout(|stdout| write!( + stdout, "\rplease select an entry to boot: {value} ", + )).unwrap(); + wait_for_event( // this is safe because we're never calling close_event &mut [unsafe { key_event.unsafe_clone() }] ).discard_errdata()?; - if let Some(Key::Printable(c)) = systab.stdin().read_key()? { + if let Some(Key::Printable(c)) = with_stdin( + |stdin| stdin.read_key() + )? { match c.into() { '\r' => break, // enter '\u{8}' => {value.pop();}, // backspace @@ -115,7 +119,7 @@ fn select_entry<'a>( } } } - writeln!(systab.stdout(), ).unwrap(); + with_stdout(|stdout| writeln!(stdout,)).unwrap(); // support lookup by both index and key match value.parse::() { Ok(index) => entries.values().nth(index),