ZFI – Zero-cost and safe interface to UEFI firmware
ZFI is a Rust crate for writing a UEFI application with the following goals:
- Provides base APIs that are almost identical to the UEFI specifications.
- Provides additional APIs that build on top of the base APIs.
- Base APIs are zero-cost abstraction over UEFI API.
- Safe and easy to use.
- Work on stable Rust.
ZFI supports only single-thread environment, which is the same as UEFI specifications.
Example
use Box;
use ;
extern crate alloc;
extern "efiapi"
!
static ALLOCATOR: PoolAllocator = zfi:PoolAllocator;
You can use zfi::main macro if you prefer a less boilerplate:
use ;
// zfi::main will not enable the debug writer by default. See its documentation to see how to enable
// the debug writer.
To build the above example you need to add a UEFI target to Rust:
Then build with the following command:
You can grab the EFI file in target/x86_64-unknown-uefi/debug and boot it on a compatible machine.
Integration Testing
ZFI provide zfi-testing crate to help you write the
integration tests.
This crate must be added as a
development dependency,
not a standard dependency. You need to install the following tools before you can run the
integration tests that use zfi-testing:
Once ready create zfi.toml in the root of your package (the same location as Cargo.toml) with
the following content:
[]
= "QEMU_BIN"
= "OVMF_CODE"
= "OVMF_VARS"
This file should not commit to the version control because it is specific to your machine. Replace the following placeholders with the appropriate value:
RUST_TARGET: name of Rust target you want to run on the QEMU (e.g.x86_64-unknown-uefi).QEMU_BIN: path to the QEMU binary to run your tests. The binary must have the same CPU type asRUST_TARGET. You don't need to specify a full path if the binary can be found in thePATHenvironment variable (e.g.qemu-system-x86_64).OVMF_CODE: path toOVMF_CODE.fdfrom OVMF. File must have the same CPU type asRUST_TARGET(e.g./usr/share/edk2/x64/OVMF_CODE.fd).OVMF_VARS: path toOVMF_VARS.fdfrom OVMF. File must have the same CPU type asRUST_TARGET(e.g./usr/share/edk2/x64/OVMF_VARS.fd).
Example:
[]
= "qemu-system-aarch64"
= "/usr/share/AAVMF/AAVMF_CODE.fd"
= "/usr/share/AAVMF/AAVMF_VARS.fd"
[]
= "qemu-system-i386"
= "/usr/share/edk2/ia32/OVMF_CODE.fd"
= "/usr/share/edk2/ia32/OVMF_VARS.fd"
[]
= "qemu-system-x86_64"
= "/usr/share/edk2/x64/OVMF_CODE.fd"
= "/usr/share/edk2/x64/OVMF_VARS.fd"
Writing Tests
To write an integration test to run on QEMU, put zfi_testing::qemu attribute to your integration
test:
use qemu;
The code in the function that has zfi_testing::qemu attribute will run on the QEMU. This test can
be run in the same way as normal integration tests:
Keep in mind that you need to put everything your test needed in the same function because what
qemu attribute does is moving your function body into efi_main and run it on QEMU.
Known Issues
- Any panic (including assertion failed) in your integration test will be show as
src/main.rs:L:C. This is a limitation on stable Rust for now. - rust-analyzer not report any syntax error. The reason is because
qemuattribute replace the whole function body, which mean what rust-analyzer see when running syntax check is the replaced function, not the origial function. Right now there is no way to check if our proc macro being run by rust-analyzer until this issue has been resolved.
Breaking Changes
0.1 to 0.2
Pathis changed from sized type to unsized type. Any code that castPathto a raw pointer need to update otherwise you will get a fat pointer, which is Rust specific. You can get a pointer toEFI_DEVICE_PATH_PROTOCOLviaPath::as_bytes().FileInfois changed from sized type to unsized type in the same way asPath.File::info()now returnBox<FileInfo>instead ofOwned<FileInfo>when success.- The second parameter of
Owned::new()is changed toDtor. SystemTable::current()was replaced withzfi::system_table().Image::current()was replaced withzfi::current_image().- All getters on
SystemTableno longer return a static lifetime. EfiStrandEfiStringto longer implementDisplay. UseEfiStr::display()instead.Displayimplementation ofDebugFileError,FileCreateErrorandFileSetLenErrorno longer print the nested error. Usecore::error::Error::source()to obtains the inner error instead.Pathno longer implementDisplay. UsePath::display()instead.PathNodeno longer implementDisplay. Currently no alternative is provided yet. Please create the issue if you want this feature so I can prioritize it.
License
MIT