├── rust-toolchain ├── test-runner ├── rust-toolchain ├── .gitignore ├── Cargo.toml ├── run.sh └── src │ └── main.rs ├── .gitignore ├── bors.toml ├── .cargo └── config.toml ├── src ├── efi │ ├── protocols │ │ ├── mod.rs │ │ ├── simple_text_input.rs │ │ ├── simple_text_output.rs │ │ └── graphics_output.rs │ ├── result.rs │ ├── mod.rs │ ├── handle.rs │ ├── runtime_services.rs │ ├── protocol.rs │ ├── system_table.rs │ ├── memory_map.rs │ └── boot_services.rs ├── console.rs ├── lib.rs └── allocator.rs ├── Cargo.toml ├── LICENSE ├── .github └── workflows │ └── ci.yml └── README.md /rust-toolchain: -------------------------------------------------------------------------------- 1 | nightly 2 | -------------------------------------------------------------------------------- /test-runner/rust-toolchain: -------------------------------------------------------------------------------- 1 | nightly 2 | -------------------------------------------------------------------------------- /test-runner/.gitignore: -------------------------------------------------------------------------------- 1 | Cargo.lock 2 | target 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | *.swp 5 | *.swo 6 | -------------------------------------------------------------------------------- /bors.toml: -------------------------------------------------------------------------------- 1 | status = [ 2 | "build", 3 | "clippy", 4 | "format", 5 | ] 6 | -------------------------------------------------------------------------------- /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "x86_64-unknown-uefi" 3 | 4 | [unstable] 5 | build-std = ["core", "compiler_builtins", "alloc"] 6 | build-std-features = ["compiler-builtins-mem"] 7 | -------------------------------------------------------------------------------- /test-runner/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test-runner" 3 | version = "0.1.0" 4 | authors = ["Richard Wiedenhöft "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | efw = { path = ".." } 9 | -------------------------------------------------------------------------------- /test-runner/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -eu 3 | 4 | pushd $(dirname $0) &> /dev/null 5 | 6 | cargo build --target x86_64-unknown-uefi 7 | uefi-run target/x86_64-unknown-uefi/debug/test-runner.efi 8 | 9 | popd &> /dev/null 10 | -------------------------------------------------------------------------------- /src/efi/protocols/mod.rs: -------------------------------------------------------------------------------- 1 | //! Collection of UEFI protocols 2 | 3 | use super::*; 4 | 5 | mod graphics_output; 6 | pub use graphics_output::*; 7 | 8 | mod simple_text_input; 9 | pub use simple_text_input::*; 10 | 11 | mod simple_text_output; 12 | pub use simple_text_output::*; 13 | -------------------------------------------------------------------------------- /src/efi/result.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | /// EFI result type 4 | pub type Result = core::result::Result; 5 | 6 | /// Wrap a low-level status into a result 7 | pub(crate) fn status_to_result(status: efi::bits::Status) -> Result<()> { 8 | if status == efi::bits::Status::SUCCESS { 9 | Ok(()) 10 | } else { 11 | Err(status) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "efw" 3 | description = "Framework for writing UEFI applications" 4 | version = "0.2.0" 5 | authors = ["Richard Wiedenhöft "] 6 | edition = "2018" 7 | license = "MIT" 8 | repository = "https://github.com/richard-w/efw" 9 | 10 | [lib] 11 | path = "src/lib.rs" 12 | 13 | [dependencies] 14 | late-static = "0.4" 15 | r-efi = "4.0" 16 | ucs2 = "0.3" 17 | -------------------------------------------------------------------------------- /src/efi/mod.rs: -------------------------------------------------------------------------------- 1 | //! Ergonomic wrappers around the UEFI API 2 | use super::*; 3 | 4 | pub use r_efi::efi as bits; 5 | 6 | mod boot_services; 7 | pub use self::boot_services::*; 8 | 9 | mod handle; 10 | pub use self::handle::*; 11 | 12 | mod memory_map; 13 | pub use self::memory_map::*; 14 | 15 | mod protocol; 16 | pub use self::protocol::*; 17 | 18 | pub mod protocols; 19 | 20 | mod result; 21 | pub use self::result::*; 22 | 23 | mod runtime_services; 24 | pub use self::runtime_services::*; 25 | 26 | mod system_table; 27 | pub use self::system_table::*; 28 | -------------------------------------------------------------------------------- /src/efi/protocols/simple_text_input.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | /// Simple Text Input protocol 4 | pub struct SimpleTextInput(*mut bits::protocols::simple_text_input::Protocol); 5 | 6 | impl Protocol for SimpleTextInput { 7 | const PROTOCOL_GUID: bits::Guid = bits::protocols::simple_text_input::PROTOCOL_GUID; 8 | 9 | unsafe fn new(ptr: *mut core::ffi::c_void) -> Self { 10 | SimpleTextInput(&mut *(ptr as *mut bits::protocols::simple_text_input::Protocol)) 11 | } 12 | } 13 | 14 | impl SimpleTextInput { 15 | /// Pointer to the underlying struct as defined by the UEFI spec 16 | pub fn bits(&mut self) -> *mut bits::protocols::simple_text_input::Protocol { 17 | self.0 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/efi/protocols/simple_text_output.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | /// Simple Text Output protocol 4 | pub struct SimpleTextOutput(*mut bits::protocols::simple_text_output::Protocol); 5 | 6 | impl Protocol for SimpleTextOutput { 7 | const PROTOCOL_GUID: bits::Guid = bits::protocols::simple_text_output::PROTOCOL_GUID; 8 | 9 | unsafe fn new(ptr: *mut core::ffi::c_void) -> Self { 10 | SimpleTextOutput(&mut *(ptr as *mut bits::protocols::simple_text_output::Protocol)) 11 | } 12 | } 13 | 14 | impl SimpleTextOutput { 15 | /// Pointer to the underlying struct as defined by the UEFI spec 16 | pub fn bits(&mut self) -> *mut bits::protocols::simple_text_output::Protocol { 17 | self.0 18 | } 19 | 20 | /// Print the UCS2 string in `string` 21 | /// 22 | /// # Safety 23 | /// 24 | /// Safe if `string` points to a valid UCS2 string. 25 | pub unsafe fn output_string(&mut self, string: *mut u16) -> Result<()> { 26 | status_to_result(((*self.0).output_string)(self.0 as _, string)) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/efi/handle.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use late_static::LateStatic; 3 | 4 | /// Handle of an opaque object 5 | #[repr(transparent)] 6 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 7 | pub struct Handle(bits::Handle); 8 | 9 | static SELF_HANDLE: LateStatic = LateStatic::new(); 10 | 11 | impl Handle { 12 | /// Save handle for the current image 13 | pub(crate) unsafe fn init_self_handle(handle: bits::Handle) { 14 | LateStatic::assign(&SELF_HANDLE, Self::new(handle)); 15 | } 16 | 17 | /// Get handle for the current image 18 | /// 19 | /// This is the handle that was originally passed to the entry point. 20 | pub fn get_self_handle() -> Self { 21 | *SELF_HANDLE 22 | } 23 | 24 | /// Wrap a low-level handle 25 | pub fn new(handle: bits::Handle) -> Self { 26 | Handle(handle) 27 | } 28 | 29 | /// Get the low-level handle 30 | pub fn value(&self) -> bits::Handle { 31 | self.0 as _ 32 | } 33 | } 34 | 35 | unsafe impl core::marker::Send for Handle {} 36 | unsafe impl core::marker::Sync for Handle {} 37 | -------------------------------------------------------------------------------- /test-runner/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use efw::prelude::*; 5 | use efw::efi::Protocol; 6 | 7 | #[no_mangle] 8 | fn efw_main() { 9 | println!("Hello, world!"); 10 | 11 | let vector = vec![1, 2, 3]; 12 | println!("Allocated vector: {:?}", vector); 13 | 14 | let text_output_instances = efw::efi::protocols::SimpleTextOutput::find_instances().unwrap(); 15 | println!("Text Output Instances found: {:?}", text_output_instances.len()); 16 | 17 | let memory_map = efw::efi::MemoryMap::get_current().unwrap(); 18 | println!("Memory map ({} entries)", memory_map.iter().count()); 19 | 20 | let mut graphics_output_instances = efw::efi::protocols::GraphicsOutput::find_instances().unwrap(); 21 | println!("Found {} graphics output protocols", graphics_output_instances.len()); 22 | 23 | let graphics = &mut graphics_output_instances[0]; 24 | let current_mode = unsafe { graphics.mode().mode }; 25 | let info = unsafe { graphics.query_mode(current_mode).unwrap() }; 26 | println!("Found {} infos for mode {}", info.len(), current_mode); 27 | } 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Richard Wiedenhöft 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /src/efi/runtime_services.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | /// Runtime services function table 4 | pub struct RuntimeServices(*mut bits::RuntimeServices); 5 | 6 | impl RuntimeServices { 7 | /// Wrap low-level runtime service table 8 | pub(crate) fn new(runtime_services: *mut bits::RuntimeServices) -> Self { 9 | RuntimeServices(runtime_services) 10 | } 11 | 12 | /// Announces virtual address mappings to EFI components so they can continue to work 13 | /// after page tables have been modified 14 | /// 15 | /// Dont use this function. Use `MemoryMap::set_virtual_address_map` instead. 16 | /// 17 | /// # Safety 18 | /// 19 | /// Safe if boot services were terminated and `memory_map` is valid. 20 | pub unsafe fn set_virtual_address_map( 21 | &self, 22 | size: usize, 23 | desc_size: usize, 24 | desc_version: u32, 25 | buffer: *mut bits::MemoryDescriptor, 26 | ) -> Result<()> { 27 | status_to_result(((*self.0).set_virtual_address_map)( 28 | size, 29 | desc_size, 30 | desc_version, 31 | buffer, 32 | )) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/console.rs: -------------------------------------------------------------------------------- 1 | use super::efi; 2 | 3 | struct StdOut; 4 | 5 | impl core::fmt::Write for StdOut { 6 | fn write_str(&mut self, string: &str) -> core::fmt::Result { 7 | let mut con_out = efi::SystemTable::get().con_out(); 8 | ucs2::encode_with(string, |ch| { 9 | let mut buffer: [u16; 2] = [0, 0]; 10 | buffer[0] = ch; 11 | unsafe { 12 | con_out 13 | .output_string(&buffer[0] as *const u16 as _) 14 | .map_err(|_| ucs2::Error::MultiByte) 15 | } 16 | }) 17 | .unwrap(); 18 | Ok(()) 19 | } 20 | } 21 | 22 | #[doc(hidden)] 23 | pub fn _print_stdout(args: core::fmt::Arguments) { 24 | use core::fmt::Write; 25 | StdOut.write_fmt(args).unwrap(); 26 | } 27 | 28 | /// Print to the standard output console 29 | #[macro_export] 30 | macro_rules! print { 31 | ($($arg:tt)*) => ($crate::prelude::_print_stdout(format_args!($($arg)*))); 32 | } 33 | 34 | /// Print a line to the standard output console 35 | #[macro_export] 36 | macro_rules! println { 37 | () => (print!("\r\n")); 38 | ($($arg:tt)*) => ($crate::prelude::print!("{}\r\n", format_args!($($arg)*))); 39 | } 40 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | - staging 8 | - trying 9 | pull_request: 10 | branches: 11 | - master 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Install nightly toolchain 18 | run: rustup toolchain install nightly 19 | 20 | - name: Install rust sources 21 | run: rustup component add --toolchain nightly rust-src 22 | 23 | - name: Checkout 24 | uses: actions/checkout@v2 25 | 26 | - name: Build 27 | run: cargo build 28 | 29 | - name: Run Tests 30 | run: cargo test -Z build-std=std --target x86_64-unknown-linux-gnu 31 | 32 | clippy: 33 | runs-on: ubuntu-latest 34 | steps: 35 | - name: Install nightly toolchain 36 | run: rustup toolchain install nightly 37 | 38 | - name: Install rust sources 39 | run: rustup component add --toolchain nightly rust-src 40 | 41 | - name: Install clippy for nightly 42 | run: rustup component add --toolchain nightly clippy 43 | 44 | - name: Checkout 45 | uses: actions/checkout@v2 46 | 47 | - name: Run clippy 48 | run: cargo clippy --verbose --all 49 | 50 | format: 51 | runs-on: ubuntu-latest 52 | steps: 53 | - name: Checkout 54 | uses: actions/checkout@v2 55 | 56 | - name: Check formatting 57 | run: cargo +stable fmt --all -- --check 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # efw [![Latest Version]][crates.io] [![Build Status]][travis] 2 | 3 | [Build Status]: https://travis-ci.org/Richard-W/efw.svg?branch=master 4 | [travis]: https://travis-ci.org/Richard-W/efw 5 | [Latest Version]: https://img.shields.io/crates/v/efw.svg 6 | [crates.io]: https://crates.io/crates/efw 7 | 8 | **Framework for writing UEFI application** 9 | 10 | --- 11 | 12 | ## State of development 13 | 14 | The types in this crate do not yet wrap the entirety of the UEFI spec. Currently 15 | only a subset of UEFI functions are defined. Some types (`SystemTable`) for 16 | example allow you access lower layers though (via the `bits()` method). 17 | 18 | ## Getting started 19 | 20 | This is a hello world application using efw: 21 | 22 | ```rust 23 | #![no_std] 24 | #![no_main] 25 | 26 | #[macro_use] extern crate efw; 27 | 28 | #[no_mangle] 29 | fn efw_main() { 30 | println!("Hello, world!"); 31 | } 32 | ``` 33 | 34 | `efw` reexports the contents of the `alloc` crate so you can use dynamic memory allocation: 35 | 36 | ```rust 37 | #![no_std] 38 | #![no_main] 39 | 40 | #[macro_use] extern crate efw; 41 | 42 | #[no_mangle] 43 | fn efw_main() { 44 | let vector = vec![1, 2, 3]; 45 | println!("Allocated vector: {:?}", vector); 46 | } 47 | ``` 48 | 49 | To be able to use `cargo build` to build the applications you should add the following to `.cargo/config.toml`: 50 | 51 | ```toml 52 | [unstable] 53 | build-std = ["core", "compiler_builtins", "alloc"] 54 | build-std-features = ["compiler-builtins-mem"] 55 | ``` 56 | 57 | ## Protocol support 58 | 59 | efw provides a set of predefined protocols that are needed for it to function 60 | properly. You can extend the set of protocols though by implementing the 61 | `Protocol` trait. That trait provides methods for finding handles supporting 62 | the protocol. 63 | -------------------------------------------------------------------------------- /src/efi/protocol.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use core::ptr; 3 | 4 | /// EFI component that can be located via its GUID 5 | pub trait Protocol: core::marker::Sized { 6 | const PROTOCOL_GUID: bits::Guid; 7 | 8 | /// Wrap a raw pointer as the protocol 9 | /// 10 | /// # Safety 11 | /// 12 | /// Safe if `ptr` points to the expected protocol. 13 | unsafe fn new(ptr: *mut core::ffi::c_void) -> Self; 14 | 15 | /// Locate handles that support this protocol 16 | fn locate_handles() -> Result> { 17 | let boot_services = SystemTable::get().boot_services(); 18 | let mut guid = Self::PROTOCOL_GUID; 19 | 20 | // Find out required buffer size 21 | let mut buffer_size: usize = 0; 22 | unsafe { 23 | boot_services 24 | .locate_handle( 25 | bits::BY_PROTOCOL, 26 | &mut guid, 27 | ptr::null_mut(), 28 | &mut buffer_size, 29 | ptr::null_mut(), 30 | ) 31 | .ok(); 32 | } 33 | 34 | // Actually get handles 35 | let mut buffer = vec![Handle::new(0 as _); buffer_size / core::mem::size_of::()]; 36 | unsafe { 37 | boot_services 38 | .locate_handle( 39 | bits::BY_PROTOCOL, 40 | &mut guid, 41 | ptr::null_mut(), 42 | &mut buffer_size, 43 | buffer.as_mut_ptr() as *mut _, 44 | ) 45 | .ok(); 46 | } 47 | Ok(buffer) 48 | } 49 | 50 | /// Locate handles that support this protocol and return protocol instances 51 | fn find_instances() -> Result> { 52 | let handles = Self::locate_handles()?; 53 | let instances = handles 54 | .iter() 55 | .map(|handle| unsafe { 56 | Self::new( 57 | SystemTable::get() 58 | .boot_services() 59 | .handle_protocol(*handle, &Self::PROTOCOL_GUID as *const _ as *mut _) 60 | .unwrap(), 61 | ) 62 | }) 63 | .collect(); 64 | Ok(instances) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(test), no_std)] 2 | #![feature(alloc_error_handler)] 3 | #![feature(panic_info_message)] 4 | 5 | extern crate alloc as alloc_crate; 6 | 7 | // Private modules 8 | mod allocator; 9 | mod console; 10 | 11 | // Exported modules 12 | pub use alloc_crate::alloc; 13 | pub use alloc_crate::borrow; 14 | pub use alloc_crate::boxed; 15 | pub use alloc_crate::collections; 16 | pub mod efi; 17 | pub use alloc_crate::fmt; 18 | pub use alloc_crate::format; 19 | pub use alloc_crate::rc; 20 | pub use alloc_crate::slice; 21 | pub use alloc_crate::str; 22 | pub use alloc_crate::string; 23 | pub use alloc_crate::sync; 24 | pub use alloc_crate::task; 25 | pub use alloc_crate::vec; 26 | 27 | /// Commonly used types, traits, and macros 28 | pub mod prelude { 29 | pub use crate::console::*; 30 | pub use crate::{format, print, println, vec}; 31 | // Based on experimental feature alloc_prelude 32 | pub use crate::borrow::ToOwned; 33 | pub use crate::boxed::Box; 34 | pub use crate::string::{String, ToString}; 35 | pub use crate::vec::Vec; 36 | } 37 | use prelude::*; 38 | 39 | extern "C" { 40 | fn efw_main(); 41 | } 42 | 43 | /// Application entry point as defined by the x86_64-unknown-uefi target 44 | /// 45 | /// Saves its arguments and calls into the `efw_main` symbol which should be 46 | /// defined by a dependee of this crate (typically an application). 47 | #[no_mangle] 48 | unsafe extern "C" fn efi_main( 49 | handle: efi::bits::Handle, 50 | system_table: *mut efi::bits::SystemTable, 51 | ) -> efi::bits::Status { 52 | efi::Handle::init_self_handle(handle); 53 | efi::SystemTable::init(system_table); 54 | 55 | efw_main(); 56 | 57 | efi::bits::Status::SUCCESS 58 | } 59 | 60 | /// Panic handler that prints some location information about where the panic occured 61 | #[cfg(not(test))] 62 | #[panic_handler] 63 | fn panic_handler(panic_info: &core::panic::PanicInfo) -> ! { 64 | let message = match panic_info.message() { 65 | Some(s) => *s, 66 | None => format_args!("no message"), 67 | }; 68 | println!("Panic occured: \"{}\"", message); 69 | if let Some(l) = panic_info.location() { 70 | println!(" File: {}", l.file()); 71 | println!(" Line: {}", l.line()); 72 | println!(" Column: {}", l.column()); 73 | } 74 | println!("Halting..."); 75 | loop {} 76 | } 77 | -------------------------------------------------------------------------------- /src/efi/system_table.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use late_static::LateStatic; 3 | 4 | /// Contains pointers to the runtime and boot services provided by UEFI 5 | #[repr(transparent)] 6 | #[derive(Copy, Clone)] 7 | pub struct SystemTable(*mut bits::SystemTable); 8 | 9 | unsafe impl core::marker::Send for SystemTable {} 10 | 11 | static mut SYSTEM_TABLE: LateStatic = LateStatic::new(); 12 | 13 | impl SystemTable { 14 | /// Save a low-level system table pointer 15 | pub(crate) unsafe fn init(system_table: *mut bits::SystemTable) { 16 | LateStatic::assign(&SYSTEM_TABLE, SystemTable(system_table)); 17 | } 18 | 19 | /// Get a reference to the system table 20 | pub fn get() -> SystemTable { 21 | unsafe { *SYSTEM_TABLE } 22 | } 23 | 24 | /// Handle for the active console input device 25 | pub fn console_in_handle(&self) -> Handle { 26 | unsafe { Handle::new((*self.0).console_in_handle) } 27 | } 28 | 29 | /// SimpleTextInput protocol associated with `console_in_handle()` 30 | pub fn con_in(&self) -> protocols::SimpleTextInput { 31 | unsafe { protocols::SimpleTextInput::new((*self.0).con_out as _) } 32 | } 33 | 34 | /// Handle for the active console output device 35 | pub fn console_out_handle(&self) -> Handle { 36 | unsafe { Handle::new((*self.0).console_out_handle) } 37 | } 38 | 39 | /// SimpleTextOutput protocol associated with `console_out_handle()` 40 | pub fn con_out(&self) -> protocols::SimpleTextOutput { 41 | unsafe { protocols::SimpleTextOutput::new((*self.0).con_out as _) } 42 | } 43 | 44 | /// Handle for the active standard error console device 45 | pub fn standard_error_handle(&self) -> Handle { 46 | unsafe { Handle::new((*self.0).standard_error_handle) } 47 | } 48 | 49 | /// SimpleTextOutput protocol associated with `standard_error_handle()` 50 | pub fn std_err(&self) -> protocols::SimpleTextOutput { 51 | unsafe { protocols::SimpleTextOutput::new((*self.0).std_err as _) } 52 | } 53 | 54 | /// Runtime Services table 55 | pub fn runtime_services(&self) -> RuntimeServices { 56 | unsafe { RuntimeServices::new((*self.0).runtime_services) } 57 | } 58 | 59 | /// Boot Services table 60 | pub fn boot_services(&self) -> BootServices { 61 | unsafe { BootServices::new((*self.0).boot_services) } 62 | } 63 | 64 | /// Get a pointer to the underlying system table implementation 65 | pub fn bits(&mut self) -> *mut bits::SystemTable { 66 | self.0 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/efi/protocols/graphics_output.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | use bits::protocols::graphics_output::{BltOperation, BltPixel, Mode, ModeInformation}; 4 | 5 | /// Configures the video hardware and exposes the framebuffer 6 | pub struct GraphicsOutput(*mut bits::protocols::graphics_output::Protocol); 7 | 8 | impl Protocol for GraphicsOutput { 9 | const PROTOCOL_GUID: bits::Guid = bits::protocols::graphics_output::PROTOCOL_GUID; 10 | 11 | unsafe fn new(ptr: *mut core::ffi::c_void) -> Self { 12 | GraphicsOutput(&mut *(ptr as *mut bits::protocols::graphics_output::Protocol)) 13 | } 14 | } 15 | 16 | impl GraphicsOutput { 17 | /// Pointer to the underlying struct as defined by the UEFI spec 18 | pub fn bits(&mut self) -> *mut bits::protocols::graphics_output::Protocol { 19 | self.0 20 | } 21 | 22 | /// Get the mode information struct 23 | /// 24 | /// Contains information about the dimensions of the framebuffer. 25 | /// 26 | /// # Safety 27 | /// 28 | /// Safe if boot services are still running. 29 | pub unsafe fn query_mode(&mut self, mode_number: u32) -> Result<&'static [ModeInformation]> { 30 | let mut size_of_info: usize = 0; 31 | let mut info: *mut ModeInformation = 0x0 as _; 32 | status_to_result(((*self.0).query_mode)( 33 | self.0, 34 | mode_number, 35 | &mut size_of_info as _, 36 | &mut info as _, 37 | ))?; 38 | Ok(core::slice::from_raw_parts(info, size_of_info)) 39 | } 40 | 41 | /// Switch to a different mode 42 | /// 43 | /// # Safety 44 | /// 45 | /// Safe if boot services are still running. 46 | pub unsafe fn set_mode(&mut self, mode_number: u32) -> Result<()> { 47 | status_to_result(((*self.0).set_mode)(self.0, mode_number)) 48 | } 49 | 50 | /// Draw pixels to framebuffer 51 | /// 52 | /// # Safety 53 | /// 54 | /// Safe if boot services are still running. 55 | #[allow(clippy::too_many_arguments)] 56 | pub unsafe fn blt( 57 | &mut self, 58 | blt_buffer: *mut BltPixel, 59 | blt_operation: BltOperation, 60 | source_x: usize, 61 | source_y: usize, 62 | destination_x: usize, 63 | destination_y: usize, 64 | width: usize, 65 | height: usize, 66 | delta: usize, 67 | ) -> Result<()> { 68 | status_to_result(((*self.0).blt)( 69 | self.0, 70 | blt_buffer, 71 | blt_operation, 72 | source_x, 73 | source_y, 74 | destination_x, 75 | destination_y, 76 | width, 77 | height, 78 | delta, 79 | )) 80 | } 81 | 82 | /// Get the current mode 83 | /// 84 | /// # Safety 85 | /// 86 | /// Safe if boot services are still running. 87 | pub unsafe fn mode(&self) -> &'static Mode { 88 | &mut *(*self.0).mode 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/allocator.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use core::alloc::Layout; 3 | use core::ptr; 4 | 5 | pub struct Allocator; 6 | 7 | #[cfg(not(test))] 8 | #[global_allocator] 9 | static ALLOCATOR: allocator::Allocator = allocator::Allocator; 10 | 11 | // Largest power of two lower than u16::MAX 12 | const MAX_ALIGN: usize = 32768; 13 | 14 | unsafe impl core::alloc::GlobalAlloc for Allocator { 15 | unsafe fn alloc(&self, layout: Layout) -> *mut u8 { 16 | let align = layout.align(); 17 | let size = align_worst_case_size(layout.size(), align); 18 | efi::SystemTable::get() 19 | .boot_services() 20 | .allocate_pool(efi::bits::LOADER_DATA, size) 21 | .map(|ptr| core::slice::from_raw_parts_mut(ptr, size)) 22 | .map(|slice| align_slice(slice, align).as_mut_ptr()) 23 | .unwrap_or(ptr::null_mut()) 24 | } 25 | 26 | unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { 27 | let aligned_slice = core::slice::from_raw_parts_mut(ptr, layout.size()); 28 | let unaligned_slice = unalign_slice(aligned_slice); 29 | efi::SystemTable::get() 30 | .boot_services() 31 | .free_pool(unaligned_slice.as_mut_ptr()) 32 | .unwrap(); 33 | } 34 | } 35 | 36 | #[cfg(not(test))] 37 | #[alloc_error_handler] 38 | fn alloc_error_handler(_layout: core::alloc::Layout) -> ! { 39 | panic!("Allocation failed"); 40 | } 41 | 42 | /// Minimum slice of an unaligned slice so that the aligned slice has at least length `length` 43 | fn align_worst_case_size(size: usize, align: usize) -> usize { 44 | size + 2 + (align - 1) 45 | } 46 | 47 | /// Cut a slice so the first element is aligned 48 | fn align_slice(slice: &mut [u8], align: usize) -> &mut [u8] { 49 | assert!(align <= MAX_ALIGN); 50 | let address = slice.as_mut_ptr() as usize; 51 | let spaced = address + 2; 52 | let aligned = (spaced + align - 1) / align * align; 53 | let offset = aligned - address; 54 | let (_, tail) = slice.split_at_mut(offset - 2); 55 | let (offset_buffer, aligned_buffer) = tail.split_at_mut(2); 56 | write_u16(offset_buffer, offset as u16); 57 | aligned_buffer 58 | } 59 | 60 | /// Get the original slice from an aligned slice 61 | /// 62 | /// # Safety 63 | /// 64 | /// Safe if `slice` was returned by `align_slice`. 65 | unsafe fn unalign_slice(slice: &mut [u8]) -> &mut [u8] { 66 | let ptr = slice.as_mut_ptr(); 67 | let offset_buffer = core::slice::from_raw_parts(ptr.offset(-2), 2); 68 | let offset = read_u16(offset_buffer); 69 | let orig_ptr = ptr.offset(-(offset as isize)); 70 | core::slice::from_raw_parts_mut(orig_ptr, slice.len() + offset as usize) 71 | } 72 | 73 | /// Write a `u16` value to a `&[u8]` of length 2 74 | fn write_u16(slice: &mut [u8], value: u16) { 75 | assert_eq!(slice.len(), core::mem::size_of::()); 76 | let ptr = slice.as_mut_ptr() as *mut u16; 77 | unsafe { 78 | ptr.write(value); 79 | } 80 | } 81 | 82 | /// Read a `u16` value from a `&[u8]` of length 2 83 | fn read_u16(slice: &[u8]) -> u16 { 84 | assert_eq!(slice.len(), core::mem::size_of::()); 85 | let ptr = slice.as_ptr() as *const u16; 86 | unsafe { ptr.read() } 87 | } 88 | 89 | #[cfg(test)] 90 | mod tests { 91 | use super::*; 92 | 93 | #[test] 94 | fn align_then_unalign() { 95 | let mut buffer = vec![0; 128]; 96 | let align = 64; 97 | 98 | let aligned = align_slice(&mut buffer, align); 99 | assert_eq!(aligned.as_mut_ptr() as usize % align, 0); 100 | 101 | let unaligned = unsafe { unalign_slice(aligned) }; 102 | assert_eq!(unaligned.len(), buffer.len()); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/efi/memory_map.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use core::ptr; 3 | 4 | /// UEFI Memory Map container 5 | pub struct MemoryMap { 6 | buffer: Vec, 7 | map_key: usize, 8 | desc_size: usize, 9 | desc_version: u32, 10 | } 11 | 12 | impl MemoryMap { 13 | /// Get the current memory map 14 | pub fn get_current() -> Result { 15 | let boot_services = SystemTable::get().boot_services(); 16 | 17 | let mut buffer_size: usize = 0; 18 | let mut map_key: usize = 0; 19 | let mut desc_size: usize = 0; 20 | let mut desc_version: u32 = 0; 21 | 22 | // Get required size of memory map buffer 23 | unsafe { 24 | boot_services 25 | .get_memory_map( 26 | &mut buffer_size, 27 | ptr::null_mut(), 28 | &mut map_key, 29 | &mut desc_size, 30 | &mut desc_version, 31 | ) 32 | .ok(); 33 | } 34 | // Account for an additional allocation 35 | buffer_size += desc_size; 36 | // Allocate buffer 37 | let mut buffer = vec![0u8; buffer_size]; 38 | 39 | // Actually get the memory map 40 | unsafe { 41 | boot_services.get_memory_map( 42 | &mut buffer_size, 43 | buffer.as_mut_ptr() as *mut _, 44 | &mut map_key, 45 | &mut desc_size, 46 | &mut desc_version, 47 | )?; 48 | } 49 | // We could shrink `buffer` now but that would invalidate the memory map we just got 50 | Ok(MemoryMap { 51 | buffer, 52 | map_key, 53 | desc_size, 54 | desc_version, 55 | }) 56 | } 57 | 58 | /// Set the memory map as the virtual address map 59 | /// 60 | /// # Safety 61 | /// 62 | /// Safe when boot services have been terminated. 63 | pub unsafe fn set_virtual_address_map(&self) -> Result<()> { 64 | let runtime_services = SystemTable::get().runtime_services(); 65 | let bytes = self.bytes(); 66 | runtime_services.set_virtual_address_map( 67 | bytes.len(), 68 | self.desc_size(), 69 | self.desc_version(), 70 | bytes.as_ptr() as *mut _, 71 | ) 72 | } 73 | 74 | /// Key of the memory map 75 | pub fn key(&self) -> usize { 76 | self.map_key 77 | } 78 | 79 | /// Size of a memory descriptor 80 | pub fn desc_size(&self) -> usize { 81 | self.desc_size 82 | } 83 | 84 | /// Memory descriptor version 85 | pub fn desc_version(&self) -> u32 { 86 | self.desc_version 87 | } 88 | 89 | /// Get the raw bytes of the memory map 90 | pub fn bytes(&self) -> &[u8] { 91 | self.buffer.as_slice() 92 | } 93 | 94 | /// Get the raw bytes of the memory map mutably 95 | pub fn bytes_mut(&mut self) -> &mut [u8] { 96 | self.buffer.as_mut_slice() 97 | } 98 | 99 | /// Get a constant iterator of memory map entries 100 | pub fn iter(&self) -> impl Iterator { 101 | ConstMemoryMapIterator::new(self) 102 | } 103 | 104 | /// Get a mutable iterator of memory map entries 105 | pub fn iter_mut(&mut self) -> impl Iterator { 106 | MutMemoryMapIterator::new(self) 107 | } 108 | } 109 | 110 | /// Constant iterator type for the `MemoryMap` struct 111 | struct ConstMemoryMapIterator<'a> { 112 | mmap: &'a MemoryMap, 113 | position: usize, 114 | } 115 | 116 | impl<'a> ConstMemoryMapIterator<'a> { 117 | pub(crate) fn new(mmap: &'a MemoryMap) -> Self { 118 | ConstMemoryMapIterator { mmap, position: 0 } 119 | } 120 | } 121 | 122 | impl<'a> core::iter::Iterator for ConstMemoryMapIterator<'a> { 123 | type Item = &'a bits::MemoryDescriptor; 124 | 125 | fn next(&mut self) -> Option { 126 | let item_offset = self.mmap.desc_size * self.position; 127 | let item_end = item_offset + self.mmap.desc_size; 128 | if item_end >= self.mmap.buffer.len() { 129 | None 130 | } else { 131 | let ptr: *const u8 = &self.mmap.buffer.as_slice()[0] as _; 132 | unsafe { 133 | let desc_ptr = ptr.add(item_offset) as *const bits::MemoryDescriptor; 134 | self.position += 1; 135 | Some(&*desc_ptr) 136 | } 137 | } 138 | } 139 | } 140 | 141 | /// Mutable iterator type for the `MemoryMap` struct 142 | struct MutMemoryMapIterator<'a> { 143 | mmap: &'a mut MemoryMap, 144 | position: usize, 145 | } 146 | 147 | impl<'a> MutMemoryMapIterator<'a> { 148 | pub(crate) fn new(mmap: &'a mut MemoryMap) -> Self { 149 | MutMemoryMapIterator { mmap, position: 0 } 150 | } 151 | } 152 | 153 | impl<'a> core::iter::Iterator for MutMemoryMapIterator<'a> { 154 | type Item = &'a mut bits::MemoryDescriptor; 155 | 156 | fn next(&mut self) -> Option { 157 | let item_offset = self.mmap.desc_size * self.position; 158 | let item_end = item_offset + self.mmap.desc_size; 159 | if item_end >= self.mmap.buffer.len() { 160 | None 161 | } else { 162 | let ptr: *mut u8 = &mut self.mmap.buffer.as_mut_slice()[0] as _; 163 | unsafe { 164 | let desc_ptr = ptr.add(item_offset) as *mut bits::MemoryDescriptor; 165 | self.position += 1; 166 | Some(&mut *desc_ptr) 167 | } 168 | } 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/efi/boot_services.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | /// Boot services function table 4 | pub struct BootServices(*mut bits::BootServices); 5 | 6 | impl BootServices { 7 | pub(crate) fn new(boot_services: *mut bits::BootServices) -> Self { 8 | BootServices(boot_services) 9 | } 10 | 11 | /// Allocate `size` bytes of memory 12 | /// 13 | /// Do not use this function for ordinary memory allocations. Use the global allocator instead. 14 | /// 15 | /// # Safety 16 | /// 17 | /// Safe if `exit_boot_services` was not called. 18 | pub unsafe fn allocate_pool( 19 | &self, 20 | pool_type: bits::MemoryType, 21 | size: usize, 22 | ) -> Result<*mut u8> { 23 | let mut buffer: *mut core::ffi::c_void = 0 as _; 24 | status_to_result(((*self.0).allocate_pool)(pool_type, size, &mut buffer as _))?; 25 | Ok(buffer as _) 26 | } 27 | 28 | /// Frees memory allocated by `allocate_pool` 29 | /// 30 | /// Do not use this function for ordinary memory allocations. Use the global allocator instead. 31 | /// 32 | /// # Safety 33 | /// 34 | /// Safe if `exit_boot_services` was not called and `buffer` was allocated by `allocate_pool`. 35 | pub unsafe fn free_pool(&self, buffer: *mut u8) -> Result<()> { 36 | status_to_result(((*self.0).free_pool)(buffer as _)) 37 | } 38 | 39 | /// Allocate `num` consecutive pages of physical memory 40 | /// 41 | /// Do not use this function for ordinary memory allocations. Use the global allocator instead. 42 | /// 43 | /// # Safety 44 | /// 45 | /// Safe if `exit_boot_services` was not called. 46 | pub unsafe fn allocate_pages( 47 | &self, 48 | allocate_type: bits::AllocateType, 49 | memory_type: bits::MemoryType, 50 | num: usize, 51 | ) -> Result<*mut u8> { 52 | let mut result: bits::PhysicalAddress = 0; 53 | status_to_result(((*self.0).allocate_pages)( 54 | allocate_type, 55 | memory_type, 56 | num, 57 | &mut result as _, 58 | ))?; 59 | Ok(result as _) 60 | } 61 | 62 | /// Free `num` consecutive pages of physical memory 63 | /// 64 | /// Do not use this function for ordinary memory allocations. Use the global allocator instead. 65 | /// 66 | /// # Safety 67 | /// 68 | /// Safe if `exit_boot_services` was not called and `memory` was allocated by `allocate_pages`. 69 | pub unsafe fn free_pages(&self, memory: *mut u8, num: usize) -> Result<()> { 70 | status_to_result(((*self.0).free_pages)(memory as _, num)) 71 | } 72 | 73 | /// Get the current memory map 74 | /// 75 | /// Do not use this function. Use `MemoryMap::get_current` instead. 76 | /// 77 | /// # Safety 78 | /// 79 | /// Safe if `exit_boot_services` was not called and pointers refer to valid memory. 80 | pub unsafe fn get_memory_map( 81 | &self, 82 | memory_map_size: *mut usize, 83 | memory_map: *mut bits::MemoryDescriptor, 84 | map_key: *mut usize, 85 | desc_size: *mut usize, 86 | desc_version: *mut u32, 87 | ) -> Result<()> { 88 | status_to_result(((*self.0).get_memory_map)( 89 | memory_map_size, 90 | memory_map, 91 | map_key, 92 | desc_size, 93 | desc_version, 94 | )) 95 | } 96 | 97 | /// Get an array of handles that support a specific protocol 98 | /// 99 | /// Do not use this function to locate protocol handles. Use `Protocol::locate_handles` 100 | /// instead. 101 | /// 102 | /// # Safety 103 | /// 104 | /// Safe if `exit_boot_services` was not called and passed pointer point to valid memory. 105 | pub unsafe fn locate_handle( 106 | &self, 107 | search_type: bits::LocateSearchType, 108 | protocol: *mut bits::Guid, 109 | search_key: *mut core::ffi::c_void, 110 | buffer_size: *mut usize, 111 | buffer: *mut bits::Handle, 112 | ) -> Result<()> { 113 | status_to_result(((*self.0).locate_handle)( 114 | search_type, 115 | protocol, 116 | search_key, 117 | buffer_size, 118 | buffer, 119 | )) 120 | } 121 | 122 | /// Get a pointer to a protocol supported by the handle 123 | /// 124 | /// Do not use this function to handle protocols. Use `Protocol::find_instances` instead. 125 | /// 126 | /// # Safety 127 | /// 128 | /// Safe if `exit_boot_services` was not called and passed pointer point to valid memory. 129 | pub unsafe fn handle_protocol( 130 | &self, 131 | handle: Handle, 132 | protocol: *mut bits::Guid, 133 | ) -> Result<*mut core::ffi::c_void> { 134 | let mut interface: *mut core::ffi::c_void = 0 as _; 135 | status_to_result(((*self.0).handle_protocol)( 136 | handle.value(), 137 | protocol, 138 | &mut interface as _, 139 | ))?; 140 | Ok(interface) 141 | } 142 | 143 | /// Exit the boot services and take control of the machine 144 | /// 145 | /// Most of the safe interfaces of `efw` will not work after calling this function. 146 | /// 147 | /// # Safety 148 | /// 149 | /// Safe if `exit_boot_services` was not called before. 150 | pub unsafe fn exit_boot_services(&self, map_key: usize) -> Result<()> { 151 | status_to_result(((*self.0).exit_boot_services)( 152 | Handle::get_self_handle().value() as _, 153 | map_key, 154 | )) 155 | } 156 | } 157 | --------------------------------------------------------------------------------