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
cargo add uefi --features logger,panic_handler
to your Cargo.toml. The resulting Cargo.toml should look like that:
[dependencies]
log = "0.4"
# Check crates.io for the latest version.
uefi = { version = "<latest, e.g. 0.36>", features = [ "panic_handler", "logger" ] }
Replace the contents of src/main.rs with this:
#![no_main] #![no_std] use core::time::Duration; use log::info; use uefi::prelude::*; #[entry] fn main() -> Status { uefi::helpers::init().unwrap(); info!("Hello world!"); boot::stall(Duration::from_secs(10)); 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 macros, modules, and types.
#![allow(unused)] fn main() { use core::time::Duration; 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() -> Status {
The main function in a UEFI application takes no arguments and returns
a Status, which is essentially a numeric error (or success) code
defined by UEFI. The main function must be marked with the #[entry]
macro.
The first thing we do inside of main is initialize the helpers
module, which initializes logging:
#![allow(unused)] fn main() { uefi::helpers::init().unwrap(); }
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!"); boot::stall(Duration::from_secs(10)); }
Finally we return Status::SUCCESS indicating that everything completed
successfully:
#![allow(unused)] fn main() { Status::SUCCESS } }