Writing UEFI application in Rust from scratch
This is an English version of my blog post. The original version is here (Japanese).
x86_64-unknown-uefi target was added into Rust mainline (https://github.com/rust-lang/rust/pull/56769).
So, I tried to write UEFI application with this update.
There exists an awesome crate, uefi-rs, which provides Rust interface for UEFI application.
Rust wrapper for UEFI. Contribute to rust-osdev/uefi-rs development by creating an account on GitHub.github.com
However, this is my first time to write UEFI application, so to understand what happens in it, I didn’t use any existing crate.
What is UEFI?
UEFI is a software interface for platform firmware and replaces BIOS. For more detail, please refer Wikipedia, or there are a lot of existing examples developing in C.
The latest specification is available here: Unified Extensible Firmware Interface Forum
I followed version 2.7.
The target architecture here is x86_64. First, we need to use nigtly Rust compiler including
x86_64-unknown-uefi target. Also, to include sysroot libraries, cargo-xbuild is necessary.
$ rustup default nightly-2019-03-23
$ cargo install cargo-xbuild
To run your UEFI application in your local machine, you need to get
qemu-system-x86_64 and its UEFI firmware OVMF.
To get OVMF, it is better to download the built image from https://www.kraxel.org/repos/. (You can build it from source code as well, but it will take a long time). You need to get
OVMF_CODE-pure-efi.fd from the rpm for x64 in
Here is my final result for hello world application on UEFI.
Contribute to garasubo/uefi-practice development by creating an account on GitHub.github.com
To run this application on qemu, you need to put
OVMF_CODE.fd on the project root directory. Then, you can start the UEFI shell by following commands:
$ cargo xbuild --target x86_64-unknown-uefi
$ sh qemu-run.sh
And, you can start the application by following commands:
Shell > fs0:
FS0: \> uefi-practice.efi
Now, you will see “Hello World”.
How it works
efi_main function is the entry point. It correspond to
EFI_IMAGE_ENTRY_POINT in the UEFI specification. This function takes two arguments and return
EFI_STATUS. Please refer to the specification for more detail.
There are code snippets written in C to declare the interface types. We need to write down them in Rust. However, Rust and C ABI are different. So, we need to attach
repr attribute to make sure the compatiblity. Rustonomicon provides the detail for it in Data Layout section.
My goal was to display “hello world”, so I use
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL to put text. This protocol includes
output_string functions, so I used these. However, this protocol and other structs include more interface, actually. I declared only what I needed, and declared as
usize for unused functions. It is hard to write down all interfaces.
In UEFI, we need to encode all string as UCS-2, however, in Rust the default string is encoded as UFT-8 and there is no support to convert it to UCS-2. Each character in UCS-2 is 16bit, so I prepared some
u16 array buffer and convert the string on it (It is the same way as
Finally, we need to provide
It is too hard to implement all UEFI specification. So, let’s use