Creating a UEFI application
Install dependencies
Follow the Rust installation instructions to set up Rust.
Create a minimal application
Create an empty application and change to that directory:
cargo new my-uefi-app
cd my-uefi-app
Add a few dependencies:
cargo add log uefi uefi-services
Replace the contents of src/main.rs
with this:
#![no_main] #![no_std] use log::info; use uefi::prelude::*; #[entry] fn main(_image_handle: Handle, mut system_table: SystemTable<Boot>) -> Status { uefi_services::init(&mut system_table).unwrap(); info!("Hello world!"); system_table.boot_services().stall(10_000_000); Status::SUCCESS }
Walkthrough
Let's look a quick look at what each part of the program is doing,
starting with the #![...]
lines at the top:
#![allow(unused)] #![no_main] #![no_std] fn main() { }
This is some boilerplate that all Rust UEFI applications will
need. no_main
is needed because the UEFI application entry point is
different from the standard Rust main
function. no_std
is needed to
turn off the std
library; the core
and alloc
crates can still be
used.
Next up are some use
lines. Nothing too exciting here; the
uefi::prelude
module is intended to be glob-imported, and exports a
number of commonly-used types.
#![allow(unused)] fn main() { use log::info; use uefi::prelude::*; }
Now we get to the UEFI application main
function, and here things look
a little different from a standard Rust program.
#[entry] fn main(_image_handle: Handle, mut system_table: SystemTable<Boot>) -> Status {
The main
function in a Uefi application always takes two arguments,
the image handle and the system table. The image handle represents the
currently-running executable, and the system table provides access to
many different UEFI services. The main
function returns a Status
,
which is essentially a numeric error (or success) code defined by UEFI.
The first thing we do inside of main
is initialize uefi_services
:
#![allow(unused)] fn main() { uefi_services::init(&mut system_table).unwrap(); }
The uefi_services
crate is not strictly required to make a UEFI
application with the uefi
crate, but it makes things much simpler by
setting a simple memory allocator, initializing the logger, and
providing a panic handler.
Next we use the standard log
crate to output "Hello world!". Then we
call stall
to make the system pause for 10 seconds. This just ensures
you have enough time to see the output.
#![allow(unused)] fn main() { info!("Hello world!"); system_table.boot_services().stall(10_000_000); }
Finally we return Status::SUCCESS
indicating that everything completed
successfully:
#![allow(unused)] fn main() { Status::SUCCESS } }