├── .gitattributes ├── .github ├── codecov.yml ├── workflows │ ├── nightly_toolchain.toml │ ├── release.yml │ ├── qa.yml │ ├── developer_productivity.yml │ └── book.yml ├── FUNDING.yml ├── PULL_REQUEST_TEMPLATE.md └── renovate.json ├── uefi-test-runner ├── tftp │ └── example-file.txt ├── https │ ├── cacerts.bin │ └── README.md ├── screenshots │ └── gop_test.ppm ├── src │ ├── proto │ │ ├── ata │ │ │ ├── mod.rs │ │ │ └── pass_thru.rs │ │ ├── nvme │ │ │ ├── mod.rs │ │ │ └── pass_thru.rs │ │ ├── scsi │ │ │ └── mod.rs │ │ ├── pci │ │ │ ├── mod.rs │ │ │ └── root_bridge.rs │ │ ├── usb │ │ │ ├── mod.rs │ │ │ └── io.rs │ │ ├── pi │ │ │ └── mod.rs │ │ ├── string │ │ │ └── mod.rs │ │ ├── console │ │ │ ├── mod.rs │ │ │ ├── pointer.rs │ │ │ ├── stdout.rs │ │ │ └── serial.rs │ │ ├── network │ │ │ └── mod.rs │ │ ├── loaded_image.rs │ │ ├── shim.rs │ │ ├── rng.rs │ │ ├── shell_params.rs │ │ ├── misc.rs │ │ └── mod.rs │ ├── runtime │ │ └── mod.rs │ └── bin │ │ └── shell_launcher.rs ├── .gitignore ├── examples │ ├── rustfmt.toml │ ├── README.md │ ├── hello_world.rs │ ├── input.rs │ ├── shell_params.rs │ ├── timestamp.rs │ └── loaded_image.rs ├── Cargo.toml └── README.md ├── .cargo └── config.toml ├── book ├── src │ ├── how_to │ │ ├── introduction.md │ │ ├── building_drivers.md │ │ ├── rust-std.md │ │ └── drawing.md │ ├── tutorial │ │ ├── introduction.md │ │ ├── building.md │ │ ├── vm.md │ │ └── hardware.md │ ├── introduction.md │ ├── reference.md │ ├── concepts │ │ ├── introduction.md │ │ ├── guid.md │ │ ├── device_paths.md │ │ ├── boot_stages.md │ │ ├── tables.md │ │ ├── handles_and_protocols.md │ │ ├── variables.md │ │ └── gpt.md │ └── SUMMARY.md ├── book.toml └── README.md ├── uefi-raw ├── src │ ├── protocol │ │ ├── pci │ │ │ └── mod.rs │ │ ├── network │ │ │ ├── mod.rs │ │ │ ├── tls.rs │ │ │ └── ip4_config2.rs │ │ ├── acpi.rs │ │ ├── tcg.rs │ │ ├── shell_params.rs │ │ ├── memory_protection.rs │ │ ├── loaded_image.rs │ │ ├── string.rs │ │ ├── hii │ │ │ ├── popup.rs │ │ │ ├── string.rs │ │ │ └── form_browser.rs │ │ ├── mod.rs │ │ ├── misc.rs │ │ ├── media.rs │ │ ├── rng.rs │ │ ├── driver.rs │ │ ├── block.rs │ │ ├── tcg │ │ │ ├── v1.rs │ │ │ └── enums.rs │ │ └── usb │ │ │ └── mod.rs │ └── table │ │ ├── mod.rs │ │ ├── configuration.rs │ │ ├── header.rs │ │ └── system.rs ├── README.md ├── Cargo.toml └── LICENSE-MIT ├── template ├── rust-toolchain.toml ├── Cargo.toml ├── src │ └── main.rs └── README.md ├── uefi ├── src │ ├── fs │ │ ├── file_system │ │ │ └── mod.rs │ │ ├── uefi_types.rs │ │ ├── dir_entry_iter.rs │ │ ├── path │ │ │ ├── mod.rs │ │ │ └── validation.rs │ │ └── mod.rs │ ├── proto │ │ ├── driver │ │ │ └── mod.rs │ │ ├── security │ │ │ └── mod.rs │ │ ├── string │ │ │ └── mod.rs │ │ ├── pi │ │ │ └── mod.rs │ │ ├── console │ │ │ ├── text │ │ │ │ └── mod.rs │ │ │ └── mod.rs │ │ ├── hii │ │ │ ├── mod.rs │ │ │ ├── config.rs │ │ │ ├── config_routing.rs │ │ │ └── database.rs │ │ ├── usb │ │ │ └── mod.rs │ │ ├── media │ │ │ ├── mod.rs │ │ │ └── fs.rs │ │ ├── network │ │ │ └── mod.rs │ │ ├── shell_params.rs │ │ ├── tcg │ │ │ └── mod.rs │ │ ├── boot_policy.rs │ │ ├── rng.rs │ │ └── acpi.rs │ ├── helpers │ │ ├── global_allocator.rs │ │ ├── mod.rs │ │ ├── println.rs │ │ └── panic_handler.rs │ ├── prelude.rs │ ├── table │ │ ├── header.rs │ │ └── mod.rs │ ├── mem │ │ ├── memory_map │ │ │ └── iter.rs │ │ └── mod.rs │ ├── data_types │ │ ├── guid.rs │ │ └── opaque.rs │ ├── polyfill.rs │ ├── util.rs │ ├── result │ │ └── error.rs │ └── macros.rs ├── LICENSE-MIT ├── Cargo.toml ├── tests │ └── memory_map.rs └── README.md ├── .envrc ├── uefi-macros ├── tests │ ├── ui │ │ ├── fail │ │ │ ├── entry_bad_return_type.rs │ │ │ ├── entry_bad_arg.rs │ │ │ ├── entry_bad_async.rs │ │ │ ├── entry_bad_const.rs │ │ │ ├── entry_bad_generic.rs │ │ │ ├── entry_bad_abi.rs │ │ │ ├── entry_bad_attr_arg.rs │ │ │ ├── entry_bad_async.stderr │ │ │ ├── entry_bad_const.stderr │ │ │ ├── entry_bad_abi.stderr │ │ │ ├── entry_bad_arg.stderr │ │ │ ├── entry_bad_attr_arg.stderr │ │ │ ├── entry_bad_generic.stderr │ │ │ └── entry_bad_return_type.stderr │ │ └── pass │ │ │ ├── entry.rs │ │ │ ├── entry_unsafe.rs │ │ │ └── entry_docstring.rs │ └── compilation.rs ├── README.md ├── Cargo.toml └── LICENSE-MIT ├── CHANGELOG.md ├── rust-toolchain.toml ├── .gitignore ├── nix └── rust-toolchain.nix ├── .editorconfig ├── uefi-std-example ├── Cargo.toml ├── README.md └── src │ └── main.rs ├── rustfmt.toml ├── xtask ├── src │ ├── platform.rs │ ├── arch.rs │ ├── device_path │ │ └── util.rs │ ├── util.rs │ ├── tpm.rs │ └── net.rs └── Cargo.toml ├── .typos.toml ├── Cargo.toml ├── LICENSE-MIT ├── flake.lock └── flake.nix /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.github/codecov.yml: -------------------------------------------------------------------------------- 1 | comment: false 2 | -------------------------------------------------------------------------------- /uefi-test-runner/tftp/example-file.txt: -------------------------------------------------------------------------------- 1 | Hello world! -------------------------------------------------------------------------------- /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [alias] 2 | xtask = "run --package xtask --" 3 | -------------------------------------------------------------------------------- /book/src/how_to/introduction.md: -------------------------------------------------------------------------------- 1 | # How-to 2 | 3 | This chapter contains practical how-to guides. 4 | -------------------------------------------------------------------------------- /uefi-raw/src/protocol/pci/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | pub mod root_bridge; 4 | -------------------------------------------------------------------------------- /book/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | language = "en" 3 | multilingual = false 4 | src = "src" 5 | title = "Rust UEFI Book" 6 | -------------------------------------------------------------------------------- /uefi-test-runner/https/cacerts.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-osdev/uefi-rs/HEAD/uefi-test-runner/https/cacerts.bin -------------------------------------------------------------------------------- /template/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | targets = ["aarch64-unknown-uefi", "i686-unknown-uefi", "x86_64-unknown-uefi"] 3 | -------------------------------------------------------------------------------- /uefi-test-runner/screenshots/gop_test.ppm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-osdev/uefi-rs/HEAD/uefi-test-runner/screenshots/gop_test.ppm -------------------------------------------------------------------------------- /uefi/src/fs/file_system/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | mod error; 4 | mod fs; 5 | 6 | pub use error::*; 7 | pub use fs::*; 8 | -------------------------------------------------------------------------------- /.envrc: -------------------------------------------------------------------------------- 1 | # If your shell has direnv support, it will automatically open a Nix shell with 2 | # all relevant dependencies to develop this project. 3 | use flake 4 | -------------------------------------------------------------------------------- /.github/workflows/nightly_toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly" 3 | targets = ["aarch64-unknown-uefi", "i686-unknown-uefi", "x86_64-unknown-uefi"] 4 | -------------------------------------------------------------------------------- /uefi-macros/tests/ui/fail/entry_bad_return_type.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use uefi::prelude::*; 4 | 5 | #[entry] 6 | fn main() -> bool { 7 | false 8 | } 9 | -------------------------------------------------------------------------------- /uefi-macros/tests/ui/fail/entry_bad_arg.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use uefi::prelude::*; 4 | 5 | #[entry] 6 | fn main(_x: usize) -> Status { 7 | Status::SUCCESS 8 | } 9 | -------------------------------------------------------------------------------- /uefi-macros/tests/ui/fail/entry_bad_async.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use uefi::prelude::*; 4 | 5 | #[entry] 6 | async fn main() -> Status { 7 | Status::SUCCESS 8 | } 9 | -------------------------------------------------------------------------------- /uefi-macros/tests/ui/fail/entry_bad_const.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use uefi::prelude::*; 4 | 5 | #[entry] 6 | const fn main() -> Status { 7 | Status::SUCCESS 8 | } 9 | -------------------------------------------------------------------------------- /uefi-macros/tests/ui/fail/entry_bad_generic.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use uefi::prelude::*; 4 | 5 | #[entry] 6 | fn main() -> Status { 7 | Status::SUCCESS 8 | } 9 | -------------------------------------------------------------------------------- /uefi-test-runner/src/proto/ata/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | mod pass_thru; 4 | 5 | pub fn test() { 6 | pass_thru::test(); 7 | } 8 | -------------------------------------------------------------------------------- /uefi-test-runner/src/proto/nvme/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | mod pass_thru; 4 | 5 | pub fn test() { 6 | pass_thru::test(); 7 | } 8 | -------------------------------------------------------------------------------- /uefi-test-runner/src/proto/scsi/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | mod pass_thru; 4 | 5 | pub fn test() { 6 | pass_thru::test(); 7 | } 8 | -------------------------------------------------------------------------------- /uefi-macros/tests/ui/fail/entry_bad_abi.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use uefi::prelude::*; 4 | 5 | #[entry] 6 | extern "C" fn main() -> Status { 7 | Status::SUCCESS 8 | } 9 | -------------------------------------------------------------------------------- /uefi-macros/tests/ui/fail/entry_bad_attr_arg.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use uefi::prelude::*; 4 | 5 | #[entry(some_arg)] 6 | fn main() -> Status { 7 | Status::SUCCESS 8 | } 9 | -------------------------------------------------------------------------------- /uefi-test-runner/src/proto/pci/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | pub mod root_bridge; 4 | 5 | pub fn test() { 6 | root_bridge::test(); 7 | } 8 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # If you are a frequent contributor to uefi-rs and would like to be featured in 2 | # the sponsors section, please contact the maintainers. 3 | github: phip1611 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | * [`uefi` changelog](uefi/CHANGELOG.md) 4 | * [`uefi-macros` changelog](uefi-macros/CHANGELOG.md) 5 | * [`uefi-raw` changelog](uefi-raw/CHANGELOG.md) 6 | -------------------------------------------------------------------------------- /uefi/src/proto/driver/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | //! UEFI driver model protocols. 4 | 5 | mod component_name; 6 | 7 | pub use component_name::*; 8 | -------------------------------------------------------------------------------- /uefi-macros/tests/ui/fail/entry_bad_async.stderr: -------------------------------------------------------------------------------- 1 | error: Entry function should not be async 2 | --> tests/ui/fail/entry_bad_async.rs:6:1 3 | | 4 | 6 | async fn main() -> Status { 5 | | ^^^^^ 6 | -------------------------------------------------------------------------------- /uefi-macros/tests/ui/fail/entry_bad_const.stderr: -------------------------------------------------------------------------------- 1 | error: Entry function should not be const 2 | --> tests/ui/fail/entry_bad_const.rs:6:1 3 | | 4 | 6 | const fn main() -> Status { 5 | | ^^^^^ 6 | -------------------------------------------------------------------------------- /uefi/src/helpers/global_allocator.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | use crate::allocator::Allocator; 4 | 5 | #[global_allocator] 6 | static ALLOCATOR: Allocator = Allocator; 7 | -------------------------------------------------------------------------------- /template/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uefi_app" 3 | version = "0.1.0" 4 | edition = "2024" 5 | publish = false 6 | 7 | [dependencies] 8 | uefi = { version = "0.36.1", features = ["panic_handler"] } 9 | -------------------------------------------------------------------------------- /uefi-macros/tests/ui/fail/entry_bad_abi.stderr: -------------------------------------------------------------------------------- 1 | error: Entry function must have no ABI modifier 2 | --> tests/ui/fail/entry_bad_abi.rs:6:1 3 | | 4 | 6 | extern "C" fn main() -> Status { 5 | | ^^^^^^ 6 | -------------------------------------------------------------------------------- /uefi-macros/tests/ui/fail/entry_bad_arg.stderr: -------------------------------------------------------------------------------- 1 | error: Entry function must have no arguments 2 | --> tests/ui/fail/entry_bad_arg.rs:6:9 3 | | 4 | 6 | fn main(_x: usize) -> Status { 5 | | ^^ 6 | -------------------------------------------------------------------------------- /uefi-macros/tests/ui/fail/entry_bad_attr_arg.stderr: -------------------------------------------------------------------------------- 1 | error: Entry attribute accepts no arguments 2 | --> tests/ui/fail/entry_bad_attr_arg.rs:5:9 3 | | 4 | 5 | #[entry(some_arg)] 5 | | ^^^^^^^^ 6 | -------------------------------------------------------------------------------- /uefi-macros/tests/ui/fail/entry_bad_generic.stderr: -------------------------------------------------------------------------------- 1 | error: Entry function should not be generic 2 | --> tests/ui/fail/entry_bad_generic.rs:6:9 3 | | 4 | 6 | fn main() -> Status { 5 | | ^ 6 | -------------------------------------------------------------------------------- /template/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use uefi::prelude::*; 5 | 6 | #[entry] 7 | fn main() -> Status { 8 | uefi::helpers::init().unwrap(); 9 | 10 | Status::SUCCESS 11 | } 12 | -------------------------------------------------------------------------------- /uefi-test-runner/src/proto/usb/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | pub fn test() { 4 | info!("Testing USB protocols"); 5 | 6 | io::test(); 7 | } 8 | 9 | mod io; 10 | -------------------------------------------------------------------------------- /uefi/src/proto/security/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | //! Protocols related to secure technologies. 4 | 5 | mod memory_protection; 6 | pub use memory_protection::MemoryProtection; 7 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "stable" 3 | # cargo, clippy, rustc, rust-docs, rustfmt, rust-std 4 | profile = "default" 5 | targets = ["aarch64-unknown-uefi", "i686-unknown-uefi", "x86_64-unknown-uefi"] 6 | -------------------------------------------------------------------------------- /uefi-macros/tests/ui/pass/entry.rs: -------------------------------------------------------------------------------- 1 | use uefi::{entry, Status}; 2 | 3 | #[entry] 4 | fn efi_main() -> Status { 5 | Status::SUCCESS 6 | } 7 | 8 | // trybuild requires a `main` function. 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /uefi-test-runner/.gitignore: -------------------------------------------------------------------------------- 1 | # Python cache files. 2 | /__pycache__ 3 | 4 | # OVMF images, which provide a UEFI firmware for QEMU. 5 | /OVMF_CODE.fd 6 | /OVMF_VARS.fd 7 | /QEMU_EFI-pflash.raw 8 | /vars-template-pflash.raw 9 | -------------------------------------------------------------------------------- /uefi-macros/tests/ui/pass/entry_unsafe.rs: -------------------------------------------------------------------------------- 1 | use uefi::{entry, Status}; 2 | 3 | #[entry] 4 | unsafe fn efi_main() -> Status { 5 | Status::SUCCESS 6 | } 7 | 8 | // trybuild requires a `main` function. 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /uefi-test-runner/examples/rustfmt.toml: -------------------------------------------------------------------------------- 1 | # The examples in this directory get included in the book. Set a smaller 2 | # width to reduce the amount of horizontal scrolling needed for markdown 3 | # code blocks. 4 | max_width = 80 5 | -------------------------------------------------------------------------------- /uefi-test-runner/src/proto/pi/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | pub fn test() { 4 | info!("Testing Platform Initialization protocols"); 5 | 6 | mp::test(); 7 | } 8 | 9 | mod mp; 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Cargo places all built files here. 2 | /target/ 3 | 4 | # Files generated by mdBook. 5 | /book/book/ 6 | 7 | # Integration test output by QEMU. 8 | integration-test-debugcon.log 9 | ovmf-firmware-debugcon.log 10 | -------------------------------------------------------------------------------- /book/src/tutorial/introduction.md: -------------------------------------------------------------------------------- 1 | # Tutorial 2 | 3 | This tutorial describes the process of creating and running a simple 4 | x86_64 UEFI application in Rust. The application will print "Hello 5 | World", pause for 10 seconds, then exit. 6 | -------------------------------------------------------------------------------- /uefi/src/proto/string/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | //! String protocols. 4 | //! 5 | //! The protocols provide some string operations like 6 | //! lexical comparison. 7 | 8 | pub mod unicode_collation; 9 | -------------------------------------------------------------------------------- /uefi-macros/tests/compilation.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | #[test] 4 | fn ui() { 5 | let t = trybuild::TestCases::new(); 6 | t.compile_fail("tests/ui/fail/*.rs"); 7 | t.pass("tests/ui/pass/*.rs"); 8 | } 9 | -------------------------------------------------------------------------------- /uefi-test-runner/src/proto/string/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | pub fn test() { 4 | info!("Testing String protocols"); 5 | 6 | unicode_collation::test(); 7 | } 8 | 9 | mod unicode_collation; 10 | -------------------------------------------------------------------------------- /uefi/src/proto/pi/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | //! Platform Initialization protocols. 4 | //! 5 | //! Contains protocols defined in UEFI's 6 | //! Platform Initialization (PI) Specification. 7 | 8 | pub mod mp; 9 | -------------------------------------------------------------------------------- /uefi-macros/tests/ui/pass/entry_docstring.rs: -------------------------------------------------------------------------------- 1 | use uefi::{entry, Status}; 2 | 3 | /// Docstring. 4 | #[entry] 5 | fn efi_main() -> Status { 6 | Status::SUCCESS 7 | } 8 | 9 | // trybuild requires a `main` function. 10 | fn main() {} 11 | -------------------------------------------------------------------------------- /uefi-raw/src/protocol/network/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | pub mod dhcp4; 4 | pub mod http; 5 | pub mod ip4; 6 | pub mod ip4_config2; 7 | pub mod pxe; 8 | pub mod snp; 9 | pub mod tcp4; 10 | pub mod tls; 11 | -------------------------------------------------------------------------------- /uefi/src/proto/console/text/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | //! Text I/O. 4 | 5 | mod input; 6 | pub use input::{Input, Key, ScanCode}; 7 | 8 | mod output; 9 | pub use output::{Color, Output, OutputMode}; 10 | -------------------------------------------------------------------------------- /nix/rust-toolchain.nix: -------------------------------------------------------------------------------- 1 | # Returns a Rust toolchain for Nix that matches the one from the toolchain file. 2 | 3 | { 4 | # Comes from rust-overlay 5 | rust-bin, 6 | }: 7 | 8 | # Includes rustc, cargo, rustfmt, etc 9 | rust-bin.fromRustupToolchainFile ../rust-toolchain.toml 10 | -------------------------------------------------------------------------------- /uefi/src/proto/hii/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | //! HII Protocols 4 | 5 | pub mod config; 6 | #[cfg(feature = "alloc")] 7 | pub mod config_routing; 8 | #[cfg(feature = "alloc")] 9 | pub mod config_str; 10 | #[cfg(feature = "alloc")] 11 | pub mod database; 12 | -------------------------------------------------------------------------------- /uefi-raw/src/table/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | //! Standard UEFI tables. 4 | 5 | mod header; 6 | mod revision; 7 | 8 | pub mod boot; 9 | pub mod configuration; 10 | pub mod runtime; 11 | pub mod system; 12 | 13 | pub use header::Header; 14 | pub use revision::Revision; 15 | -------------------------------------------------------------------------------- /book/src/introduction.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | Welcome to the Rust UEFI Book. The focus of this book is how to use 4 | [`uefi-rs`] to build UEFI applications in Rust, but it also describes 5 | some general UEFI concepts, as well as relevant tools such as QEMU. 6 | 7 | [`uefi-rs`]: https://github.com/rust-osdev/uefi-rs 8 | -------------------------------------------------------------------------------- /uefi-test-runner/https/README.md: -------------------------------------------------------------------------------- 1 | https ca cert database in efi signature list format 2 | 3 | Copied over from centos stream 9 where this is available as 4 | /etc/pki/ca-trust/extracted/edk2/cacerts.bin 5 | 6 | It's the Mozilla Foundation CA certificate list, shipped in 7 | ca-certificates.rpm, licensed as "MIT AND GPL-2.0-or-later". 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | max_line_length = 80 7 | 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.{rs,py}] 12 | indent_style = space 13 | indent_size = 4 14 | 15 | [*.{json,md,nix,toml,yml}] 16 | indent_style = space 17 | indent_size = 2 18 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Checklist 4 | - [ ] Sensible git history (for example, squash "typo" or "fix" commits). See the [Rewriting History](https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History) guide for help. 5 | - [ ] Update the changelog (if necessary) 6 | -------------------------------------------------------------------------------- /uefi/src/proto/console/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | //! Console support protocols. 4 | //! 5 | //! The console represents the various input and output methods 6 | //! used by the user to interact with the early boot platform. 7 | 8 | pub mod gop; 9 | pub mod pointer; 10 | pub mod serial; 11 | pub mod text; 12 | -------------------------------------------------------------------------------- /uefi/src/prelude.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | //! This module is used to simplify importing the most common UEFI types. 4 | //! 5 | //! This includes the system table modules, `Status` codes, etc. 6 | 7 | pub use crate::{ 8 | Handle, ResultExt, Status, StatusExt, boot, cstr8, cstr16, entry, runtime, system, 9 | }; 10 | -------------------------------------------------------------------------------- /uefi-test-runner/examples/README.md: -------------------------------------------------------------------------------- 1 | # uefi-test-runner examples 2 | 3 | The examples in this directory get included into the book. 4 | 5 | These examples are compile-tested, but the output is _not_ currently 6 | tested automatically. To manually run one of these examples, use this 7 | command: 8 | 9 | ``` 10 | cargo xtask run --example name_of_example 11 | ``` 12 | -------------------------------------------------------------------------------- /book/src/reference.md: -------------------------------------------------------------------------------- 1 | # Reference 2 | 3 | * [Rust `*-unknown-uefi` targets](https://doc.rust-lang.org/rustc/platform-support/unknown-uefi.html) 4 | * [`uefi` crate reference](https://docs.rs/uefi) 5 | * [`uefi-macros` crate reference](https://docs.rs/uefi-macros) 6 | * [`uefi-raw` crate reference](https://docs.rs/uefi-raw) 7 | * [UEFI Specifications](https://uefi.org/specifications) 8 | -------------------------------------------------------------------------------- /uefi-raw/README.md: -------------------------------------------------------------------------------- 1 | # uefi-raw 2 | 3 | [![Crates.io](https://img.shields.io/crates/v/uefi-raw)](https://crates.io/crates/uefi-raw) 4 | [![Docs.rs](https://docs.rs/uefi-macros/badge.svg)](https://docs.rs/uefi-raw) 5 | 6 | This crate contains raw UEFI types that closely match the definitions in the 7 | [UEFI Specification]. 8 | 9 | [UEFI Specification]: https://uefi.org/specifications 10 | -------------------------------------------------------------------------------- /uefi-std-example/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uefi-std-example" 3 | version = "0.1.0" 4 | authors = ["The Rust OSDev team"] 5 | publish = false 6 | edition = "2024" 7 | 8 | [dependencies] 9 | # Attention: Don't activate the panic_handler feature, as it will clash with 10 | # the one coming from `std`. 11 | uefi = { path = "../uefi", features = ["alloc"], default-features = false } 12 | -------------------------------------------------------------------------------- /book/README.md: -------------------------------------------------------------------------------- 1 | # UEFI Book 2 | 3 | To build the book locally, first install [mdBook]. 4 | 5 | Next, launch a server that will automatically update as changes are made 6 | to the markdown sources: 7 | 8 | ```console 9 | mdbook serve book/ 10 | ``` 11 | 12 | Then open in a web browser. 13 | 14 | [mdBook]: https://rust-lang.github.io/mdBook/guide/installation.html 15 | -------------------------------------------------------------------------------- /uefi-macros/tests/ui/fail/entry_bad_return_type.stderr: -------------------------------------------------------------------------------- 1 | error[E0308]: mismatched types 2 | --> tests/ui/fail/entry_bad_return_type.rs:6:1 3 | | 4 | 6 | fn main() -> bool { 5 | | ^^^^^^^^^^^^^^^^^ expected `Status`, found `bool` 6 | | 7 | = note: expected fn pointer `extern "efiapi" fn(Handle, *const c_void) -> Status` 8 | found fn pointer `extern "efiapi" fn(Handle, *const c_void) -> bool` 9 | -------------------------------------------------------------------------------- /uefi/src/proto/usb/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | //! USB I/O protocols. 4 | //! 5 | //! These protocols can be used to interact with and configure USB devices. 6 | 7 | pub mod io; 8 | 9 | pub use uefi_raw::protocol::usb::{ 10 | AsyncUsbTransferCallback, ConfigDescriptor, DeviceDescriptor, EndpointDescriptor, 11 | InterfaceDescriptor, UsbTransferStatus, 12 | }; 13 | -------------------------------------------------------------------------------- /uefi-macros/README.md: -------------------------------------------------------------------------------- 1 | # uefi-macros 2 | 3 | [![Crates.io](https://img.shields.io/crates/v/uefi-macros)](https://crates.io/crates/uefi-macros) 4 | [![Docs.rs](https://docs.rs/uefi-macros/badge.svg)](https://docs.rs/uefi-macros) 5 | 6 | This crate provides procedural macros used by [the `uefi-rs` project](https://github.com/rust-osdev/uefi-rs/) 7 | to simplify the development of UEFI applications and reduce boilerplate. 8 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | # We keep this file explicitley to ensure that direct invocations of `rustfmt` 2 | # also use the proper style edition. Unlike `cargo fmt`, which forwards the 3 | # Rust edition specified in Cargo.toml to `rustfmt`, `rustfmt` still defaults to 4 | # the 2021 edition and is unaware of `Cargo.toml`. 5 | # 6 | # We use a direct invocation of `rustfmt` in our device path generation code. 7 | 8 | style_edition = "2024" 9 | -------------------------------------------------------------------------------- /uefi-test-runner/src/proto/console/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | use uefi::prelude::*; 4 | 5 | pub fn test() { 6 | info!("Testing console protocols"); 7 | 8 | system::with_stdout(stdout::test); 9 | 10 | unsafe { 11 | serial::test(); 12 | gop::test(); 13 | } 14 | pointer::test(); 15 | } 16 | 17 | mod gop; 18 | mod pointer; 19 | mod serial; 20 | mod stdout; 21 | -------------------------------------------------------------------------------- /uefi-test-runner/src/proto/network/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | pub fn test() { 4 | info!("Testing Network protocols"); 5 | 6 | http::test(); 7 | pxe::test(); 8 | // Currently, we are in the unfortunate situation that the SNP test 9 | // depends on the PXE test, as it assigns an IPv4 address to the 10 | // interface via DHCP. 11 | snp::test(); 12 | } 13 | 14 | mod http; 15 | mod pxe; 16 | mod snp; 17 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:recommended", 5 | "schedule:weekly" 6 | ], 7 | "lockFileMaintenance": { 8 | "enabled": true, 9 | "automerge": true 10 | }, 11 | "rangeStrategy": "update-lockfile", 12 | "packageRules": [ 13 | { 14 | "matchPackageNames": [ 15 | "crate-ci/typos" 16 | ], 17 | "automerge": true 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /book/src/concepts/introduction.md: -------------------------------------------------------------------------------- 1 | # Concepts 2 | 3 | The canonical source of information about UEFI is the [UEFI specification]. 4 | The specification is huge (currently nearly 2500 pages). Much of that 5 | content relates to optional services, understanding of which is not 6 | critical to understanding UEFI as a whole. This chapter summarizes some 7 | of the more important UEFI concepts and links to the relevant `uefi-rs` 8 | documentation. 9 | 10 | [UEFI specification]: https://uefi.org/specifications 11 | -------------------------------------------------------------------------------- /uefi/src/proto/media/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | //! Media access protocols. 4 | //! 5 | //! These protocols can be used to enumerate and access various media devices. 6 | //! They provide both **high-level abstractions** such as **files and partitions**, 7 | //! and **low-level access** such as an **block I/O** or **raw ATA** access protocol. 8 | 9 | pub mod file; 10 | 11 | pub mod block; 12 | pub mod disk; 13 | pub mod disk_info; 14 | pub mod fs; 15 | pub mod load_file; 16 | pub mod partition; 17 | -------------------------------------------------------------------------------- /xtask/src/platform.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | //! Functions to determine the host platform. 4 | //! 5 | //! Use the functions where possible instead of `#[cfg(...)]` so that 6 | //! code for all platforms gets checked at compile time. 7 | 8 | use std::env::consts; 9 | 10 | pub fn is_linux() -> bool { 11 | consts::OS == "linux" 12 | } 13 | 14 | pub fn is_unix() -> bool { 15 | consts::FAMILY == "unix" 16 | } 17 | 18 | pub fn is_windows() -> bool { 19 | consts::FAMILY == "windows" 20 | } 21 | -------------------------------------------------------------------------------- /uefi-raw/src/table/configuration.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | use crate::Guid; 4 | use core::ffi::c_void; 5 | 6 | /// UEFI configuration table. 7 | /// 8 | /// Each table is uniquely identified by a GUID. The type of data pointed to by 9 | /// `vendor_table`, as well as whether that address is physical or virtual, 10 | /// depends on the GUID. 11 | #[derive(Debug, Eq, PartialEq)] 12 | #[repr(C)] 13 | pub struct ConfigurationTable { 14 | pub vendor_guid: Guid, 15 | pub vendor_table: *mut c_void, 16 | } 17 | -------------------------------------------------------------------------------- /uefi/src/fs/uefi_types.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | //! Re-export of low-level UEFI types but prefixed with `Uefi`. This simplifies 4 | //! to differ between high-level and low-level types and interfaces in this 5 | //! module. 6 | 7 | pub use crate::proto::media::file::{ 8 | Directory as UefiDirectoryHandle, File as UefiFileTrait, FileAttribute as UefiFileAttribute, 9 | FileHandle as UefiFileHandle, FileInfo as UefiFileInfo, FileMode as UefiFileMode, 10 | FileType as UefiFileType, 11 | }; 12 | pub use crate::proto::media::fs::SimpleFileSystem as SimpleFileSystemProtocol; 13 | -------------------------------------------------------------------------------- /.typos.toml: -------------------------------------------------------------------------------- 1 | # Configuration for the typos spell checker utility (). 2 | 3 | [files] 4 | extend-exclude = [ 5 | # Generated sources 6 | "uefi/src/proto/device_path/device_path_gen.rs" 7 | ] 8 | 9 | [default] 10 | extend-ignore-identifiers-re = [ 11 | # uefi-raw/src/protocol/device_path.rs 12 | "PnP", 13 | # uefi-raw/src/protocol/network/tcpv4.rs 14 | "ANDed" 15 | ] 16 | 17 | [default.extend-words] 18 | # ./uefi-test-runner/src/proto/device_path.rs:92:14 19 | HD = "HD" 20 | 21 | [default.extend-identifiers] 22 | # We sometimes use "typ" as "type" is a reserved keyword 23 | typ = "typ" 24 | 25 | -------------------------------------------------------------------------------- /uefi-macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uefi-macros" 3 | version = "0.19.0" 4 | readme = "README.md" 5 | description = "Procedural macros for the `uefi` crate." 6 | 7 | authors.workspace = true 8 | categories.workspace = true 9 | edition.workspace = true 10 | keywords.workspace = true 11 | license.workspace = true 12 | repository.workspace = true 13 | rust-version.workspace = true 14 | 15 | [lib] 16 | proc-macro = true 17 | 18 | [dependencies] 19 | proc-macro2 = "1.0.28" 20 | quote = "1.0.9" 21 | syn = { version = "2.0.4", features = ["full"] } 22 | 23 | [dev-dependencies] 24 | trybuild = "1.0.61" 25 | uefi = { path = "../uefi", default-features = false } 26 | -------------------------------------------------------------------------------- /uefi/src/table/header.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | use super::Revision; 4 | 5 | /// All standard UEFI tables begin with a common header. 6 | #[derive(Debug)] 7 | #[repr(C)] 8 | pub struct Header { 9 | /// Unique identifier for this table. 10 | pub signature: u64, 11 | /// Revision of the spec this table conforms to. 12 | pub revision: Revision, 13 | /// The size in bytes of the entire table. 14 | pub size: u32, 15 | /// 32-bit CRC-32-Castagnoli of the entire table, 16 | /// calculated with this field set to 0. 17 | pub crc: u32, 18 | /// Reserved field that must be set to 0. 19 | _reserved: u32, 20 | } 21 | -------------------------------------------------------------------------------- /uefi-test-runner/src/proto/loaded_image.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | use uefi::prelude::*; 4 | use uefi::proto::loaded_image::LoadedImage; 5 | 6 | pub fn test() { 7 | info!("Running loaded image protocol test"); 8 | 9 | let loaded_image = boot::open_protocol_exclusive::(boot::image_handle()) 10 | .expect("Failed to open LoadedImage protocol"); 11 | 12 | let load_options = loaded_image.load_options_as_bytes(); 13 | info!("LoadedImage options: {load_options:?}"); 14 | 15 | let (image_base, image_size) = loaded_image.info(); 16 | info!("LoadedImage image address: {image_base:?}, image size: {image_size} bytes"); 17 | } 18 | -------------------------------------------------------------------------------- /uefi-raw/src/protocol/acpi.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | use crate::{Guid, Status, guid}; 4 | use core::ffi::c_void; 5 | 6 | #[derive(Clone, Copy, Debug)] 7 | #[repr(C)] 8 | pub struct AcpiTableProtocol { 9 | pub install_acpi_table: unsafe extern "efiapi" fn( 10 | this: *const Self, 11 | acpi_table_buffer: *const c_void, 12 | acpi_table_size: usize, 13 | table_key: *mut usize, 14 | ) -> Status, 15 | pub uninstall_acpi_table: 16 | unsafe extern "efiapi" fn(this: *const Self, table_key: usize) -> Status, 17 | } 18 | 19 | impl AcpiTableProtocol { 20 | pub const GUID: Guid = guid!("ffe06bdd-6107-46a6-7bb2-5a9c7ec5275c"); 21 | } 22 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # This workflow runs when commits are pushed to main or a branch starting with 2 | # "version-". It checks if any packages require a new release, and if so, 3 | # creates the corresponding crates.io releases and git tags. 4 | name: Release 5 | on: 6 | push: 7 | branches: 8 | - main 9 | - version-* 10 | permissions: 11 | contents: write 12 | jobs: 13 | release: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v6 17 | - run: | 18 | cargo install auto-release 19 | auto-release --condition body -p uefi-raw -p uefi-macros -p uefi 20 | env: 21 | CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} 22 | -------------------------------------------------------------------------------- /uefi-raw/src/table/header.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | use super::Revision; 4 | 5 | /// The common header that all UEFI tables begin with. 6 | #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] 7 | #[repr(C)] 8 | pub struct Header { 9 | /// Unique identifier for this table. 10 | pub signature: u64, 11 | /// Revision of the spec this table conforms to. 12 | pub revision: Revision, 13 | /// The size in bytes of the entire table. 14 | pub size: u32, 15 | /// 32-bit CRC-32-Castagnoli of the entire table, 16 | /// calculated with this field set to 0. 17 | pub crc: u32, 18 | /// Reserved field that must be set to 0. 19 | pub reserved: u32, 20 | } 21 | -------------------------------------------------------------------------------- /uefi-std-example/README.md: -------------------------------------------------------------------------------- 1 | # Minimal Rust App using `std` and `uefi` 2 | 3 | Minimal example of a "standard Rust application" that showcases how `uefi` can 4 | be utilized and enhance the developers experience, when `std` is available. 5 | 6 | For simplicity, this example is minimal and the documentation is focused on 7 | `x86_64-unknown-uefi`. However, it works similar for other supported UEFI 8 | platforms. 9 | 10 | ## Build 11 | 12 | Build the app using 13 | `$ cargo +nightly build --target x86_64-unknown-uefi`. To build it from the root 14 | directory (the Cargo workspace), append `-p uefi-std-example`. 15 | 16 | ## Run 17 | 18 | The resulting `.efi` file can be found in `target/x86_64-unknown-uefi//uefi-std-example.efi`. 19 | -------------------------------------------------------------------------------- /uefi-test-runner/examples/hello_world.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | // ANCHOR: all 4 | // ANCHOR: features 5 | #![no_main] 6 | #![no_std] 7 | // ANCHOR_END: features 8 | 9 | // ANCHOR: use 10 | use core::time::Duration; 11 | use log::info; 12 | use uefi::prelude::*; 13 | // ANCHOR_END: use 14 | 15 | // ANCHOR: entry 16 | #[entry] 17 | fn main() -> Status { 18 | // ANCHOR_END: entry 19 | // ANCHOR: services 20 | uefi::helpers::init().unwrap(); 21 | // ANCHOR_END: services 22 | // ANCHOR: log 23 | info!("Hello world!"); 24 | boot::stall(Duration::from_secs(10)); 25 | // ANCHOR_END: log 26 | // ANCHOR: return 27 | Status::SUCCESS 28 | } 29 | // ANCHOR_END: return 30 | // ANCHOR_END: all 31 | -------------------------------------------------------------------------------- /uefi-raw/src/protocol/tcg.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | //! [TCG] (Trusted Computing Group) protocols. 4 | //! 5 | //! These protocols provide access to the [TPM][tpm] (Trusted Platform Module). 6 | //! 7 | //! There are two versions of the protocol. The original protocol is in 8 | //! the [`v1`] module. It is used with TPM 1.1 and 1.2 devices. The 9 | //! newer protocol in the [`v2`] module is generally provided for TPM 10 | //! 2.0 devices, although the spec indicates it can be used for older 11 | //! TPM versions as well. 12 | //! 13 | //! [TCG]: https://trustedcomputinggroup.org/ 14 | //! [TPM]: https://en.wikipedia.org/wiki/Trusted_Platform_Module 15 | 16 | pub mod v1; 17 | pub mod v2; 18 | 19 | mod enums; 20 | pub use enums::*; 21 | -------------------------------------------------------------------------------- /uefi/src/proto/network/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | //! Network access protocols. 4 | //! 5 | //! These protocols can be used to interact with network resources. 6 | //! 7 | //! All high-level wrappers will accept [`core::net`] types: 8 | //! - [`IpAddr`] 9 | //! - [`Ipv4Addr`] 10 | //! - [`Ipv6Addr`] 11 | //! 12 | //! The only exception is [`uefi_raw::MacAddress`] which doesn't have a 13 | //! corresponding type in the standard library. It is re-exported as 14 | //! [`EfiMacAddr`]. 15 | //! 16 | //! [`IpAddr`]: core::net::IpAddr 17 | //! [`Ipv4Addr`]: core::net::Ipv4Addr 18 | //! [`Ipv6Addr`]: core::net::Ipv6Addr 19 | 20 | pub mod http; 21 | pub mod ip4config2; 22 | pub mod pxe; 23 | pub mod snp; 24 | 25 | pub use uefi_raw::MacAddress as EfiMacAddr; 26 | -------------------------------------------------------------------------------- /book/src/concepts/guid.md: -------------------------------------------------------------------------------- 1 | # GUID 2 | 3 | GUID is short for Globally Unique Identifier. A GUID is always 16 bytes, 4 | and has a standard string representation format that looks like this: 5 | `313b0d7c-fed4-4de7-99ed-2fe48874a410`. The details of the GUID format 6 | aren't too important, but be aware that the actual byte representation 7 | is not in the same order as the string representation because the first 8 | three fields are little-endian. For the most part you can treat GUIDs as 9 | opaque identifiers. 10 | 11 | The UEFI specification uses GUIDs all over the place. GUIDs are used to 12 | identify protocols, disk partitions, variable groupings, and much 13 | more. In `uefi-rs`, GUIDs are represented by the [`Guid`] type. 14 | 15 | [`Guid`]: https://docs.rs/uefi/latest/uefi/struct.Guid.html 16 | -------------------------------------------------------------------------------- /uefi-test-runner/src/proto/shim.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | use uefi::boot; 4 | use uefi::proto::shim::ShimLock; 5 | 6 | pub fn test() { 7 | info!("Running shim lock protocol test"); 8 | 9 | if let Ok(handle) = boot::get_handle_for_protocol::() { 10 | let shim_lock = boot::open_protocol_exclusive::(handle) 11 | .expect("failed to open shim lock protocol"); 12 | 13 | // An empty buffer should definitely be invalid, so expect 14 | // shim to reject it. 15 | let buffer = []; 16 | shim_lock 17 | .verify(&buffer) 18 | .expect_err("shim failed to reject an invalid application"); 19 | } else { 20 | info!("Shim lock protocol is not supported"); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /uefi-test-runner/src/proto/rng.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | use uefi::boot; 4 | use uefi::proto::rng::{Rng, RngAlgorithmType}; 5 | 6 | pub fn test() { 7 | info!("Running rng protocol test"); 8 | 9 | let handle = boot::get_handle_for_protocol::().expect("No Rng handles"); 10 | 11 | let mut rng = 12 | boot::open_protocol_exclusive::(handle).expect("Failed to open Rng protocol"); 13 | 14 | let mut list = [RngAlgorithmType::EMPTY_ALGORITHM; 4]; 15 | 16 | let list = rng.get_info(&mut list).unwrap(); 17 | info!("Supported rng algorithms : {list:?}"); 18 | 19 | let mut buf = [0u8; 4]; 20 | 21 | rng.get_rng(Some(list[0]), &mut buf).unwrap(); 22 | 23 | assert_ne!([0u8; 4], buf); 24 | info!("Random buffer : {buf:?}"); 25 | } 26 | -------------------------------------------------------------------------------- /uefi-raw/src/protocol/shell_params.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | use crate::{Char16, Guid, guid}; 4 | use core::ffi::c_void; 5 | 6 | pub type ShellFileHandle = *const c_void; 7 | 8 | #[derive(Debug)] 9 | #[repr(C)] 10 | pub struct ShellParametersProtocol { 11 | /// Pointer to a list of arguments. 12 | pub argv: *const *const Char16, 13 | /// Number of arguments. 14 | pub argc: usize, 15 | /// Handle of the standard input. 16 | pub std_in: ShellFileHandle, 17 | /// Handle of the standard output. 18 | pub std_out: ShellFileHandle, 19 | /// Handle of the standard error output. 20 | pub std_err: ShellFileHandle, 21 | } 22 | 23 | impl ShellParametersProtocol { 24 | pub const GUID: Guid = guid!("752f3136-4e16-4fdc-a22a-e5f46812f4ca"); 25 | } 26 | -------------------------------------------------------------------------------- /xtask/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xtask" 3 | version = "0.0.0" 4 | publish = false 5 | 6 | edition.workspace = true 7 | 8 | [dependencies] 9 | anyhow = "1.0.51" 10 | clap = { version = "4.4.0", default-features = false, features = ["derive", "help", "usage", "std", "env"] } 11 | fatfs = { version = "0.3.6", default-features = false, features = ["alloc", "std"] } 12 | fs-err = "3.0.0" 13 | heck = "0.5.0" 14 | itertools = "0.14.0" 15 | log.workspace = true 16 | mbrman = "0.6.0" 17 | nix = { version = "0.30.0", default-features = false, features = ["fs"] } 18 | ovmf-prebuilt = "0.2.3" 19 | proc-macro2 = { version = "1.0.46", features = ["span-locations"] } 20 | quote = "1.0.21" 21 | regex = "1.10.2" 22 | serde_json = "1.0.73" 23 | syn = { version = "2.0.0", features = ["full"] } 24 | tempfile = "3.6.0" 25 | walkdir = "2.4.0" 26 | -------------------------------------------------------------------------------- /uefi-raw/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uefi-raw" 3 | version = "0.13.0" 4 | readme = "README.md" 5 | description = """ 6 | Raw UEFI types and bindings for protocols, boot, and runtime services. This can 7 | serve as base for an UEFI firmware implementation or a high-level wrapper to 8 | access UEFI functionality from an UEFI image. 9 | """ 10 | 11 | authors.workspace = true 12 | categories.workspace = true 13 | edition.workspace = true 14 | keywords.workspace = true 15 | license.workspace = true 16 | repository.workspace = true 17 | # uefi-raw is much less likely to need the latest bleeding-edge features. 18 | # Hence, it is okay to not use the workspace MSRV. 19 | rust-version = "1.85.1" 20 | 21 | [dependencies] 22 | bitflags.workspace = true 23 | uguid.workspace = true 24 | 25 | [package.metadata.docs.rs] 26 | rustdoc-args = ["--cfg", "docsrs"] 27 | -------------------------------------------------------------------------------- /uefi-test-runner/src/proto/shell_params.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | use uefi::boot; 4 | use uefi::proto::shell_params::ShellParameters; 5 | 6 | use alloc::string::ToString; 7 | use alloc::vec::Vec; 8 | 9 | pub fn test() { 10 | info!("Running loaded image protocol test"); 11 | 12 | let image = 13 | boot::get_handle_for_protocol::().expect("No ShellParameters handles"); 14 | let shell_params = boot::open_protocol_exclusive::(image) 15 | .expect("Failed to open ShellParameters protocol"); 16 | 17 | assert_eq!(shell_params.args_len(), 4); 18 | assert_eq!( 19 | shell_params 20 | .args() 21 | .map(|x| x.to_string()) 22 | .collect::>(), 23 | &["shell.efi", "test_runner.efi", "arg1", "arg2"] 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /uefi-test-runner/src/proto/console/pointer.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | use uefi::boot; 4 | use uefi::proto::console::pointer::Pointer; 5 | 6 | pub fn test() { 7 | info!("Running pointer protocol test"); 8 | let handle = boot::get_handle_for_protocol::().expect("missing Pointer protocol"); 9 | let mut pointer = 10 | boot::open_protocol_exclusive::(handle).expect("failed to open pointer protocol"); 11 | 12 | pointer 13 | .reset(false) 14 | .expect("Failed to reset pointer device"); 15 | 16 | let state = pointer 17 | .read_state() 18 | .expect("Failed to retrieve pointer state"); 19 | 20 | if let Some(state) = state { 21 | info!("New pointer State: {state:#?}"); 22 | } else { 23 | info!("Pointer state has not changed since the last query"); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /book/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [Introduction](introduction.md) 4 | - [Tutorial](tutorial/introduction.md) 5 | - [Creating a UEFI Application](tutorial/app.md) 6 | - [Building](tutorial/building.md) 7 | - [Running in a VM](tutorial/vm.md) 8 | - [Running on Hardware](tutorial/hardware.md) 9 | - [How-to](how_to/introduction.md) 10 | - [Using Protocols](how_to/protocols.md) 11 | - [Drawing to the Screen](how_to/drawing.md) 12 | - [Building drivers](how_to/building_drivers.md) 13 | - [Combining Rust `std` with `uefi`](how_to/rust-std.md) 14 | - [Concepts](concepts/introduction.md) 15 | - [Boot Stages](concepts/boot_stages.md) 16 | - [Tables](concepts/tables.md) 17 | - [GUID](concepts/guid.md) 18 | - [Handles and Protocols](concepts/handles_and_protocols.md) 19 | - [Device Paths](concepts/device_paths.md) 20 | - [Variables](concepts/variables.md) 21 | - [GPT](concepts/gpt.md) 22 | - [Reference](reference.md) 23 | -------------------------------------------------------------------------------- /book/src/tutorial/building.md: -------------------------------------------------------------------------------- 1 | # Building 2 | 3 | ## Toolchain 4 | 5 | In order to compile for UEFI, an appropriate target must be installed. Unless 6 | your operating system provides packages for the Rust UEFI targets, the easiest 7 | way to set this up is using a [rustup toolchain file]. In the root of your 8 | repository, add `rust-toolchain.toml`: 9 | 10 | ```toml 11 | [toolchain] 12 | targets = ["aarch64-unknown-uefi", "i686-unknown-uefi", "x86_64-unknown-uefi"] 13 | ``` 14 | 15 | Here we have specified all three of the currently-supported UEFI targets; you 16 | can remove some if you don't need them. 17 | 18 | ## Build the application 19 | 20 | Run this command to build the application: 21 | 22 | ```sh 23 | cargo build --target x86_64-unknown-uefi 24 | ``` 25 | 26 | This will produce an x86-64 executable: 27 | `target/x86_64-unknown-uefi/debug/my-uefi-app.efi`. 28 | 29 | [rustup toolchain file]: https://rust-lang.github.io/rustup/overrides.html#the-toolchain-file 30 | -------------------------------------------------------------------------------- /uefi-test-runner/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uefi-test-runner" 3 | version = "0.2.0" 4 | authors = ["The Rust OSDev team"] 5 | publish = false 6 | edition = "2024" 7 | 8 | [dependencies] 9 | uefi-raw = { path = "../uefi-raw" } 10 | uefi = { path = "../uefi", features = ["alloc", "global_allocator", "panic_handler", "logger", "qemu", "log-debugcon"] } 11 | smoltcp = { version = "0.12.0", default-features = false, features = ["medium-ethernet", "proto-ipv4", "socket-udp"] } 12 | 13 | log.workspace = true 14 | 15 | qemu-exit = "3.0.0" 16 | 17 | [features] 18 | # Enable the debug support protocol test. 19 | debug_support = [] 20 | 21 | # Enable the multiprocessor test. This only works if KVM is enabled. 22 | multi_processor = [] 23 | 24 | # Enable the PXE test. 25 | pxe = [] 26 | 27 | # Enable the `unstable` feature of the `uefi` crate. 28 | unstable = ["uefi/unstable"] 29 | 30 | # Enable the TPM v1 test. 31 | tpm_v1 = [] 32 | 33 | # Enable the TPM v2 test. 34 | tpm_v2 = [] 35 | -------------------------------------------------------------------------------- /book/src/how_to/building_drivers.md: -------------------------------------------------------------------------------- 1 | # Building drivers 2 | 3 | There are [three types][spec-images] of UEFI images: 4 | * Application 5 | * Boot service driver 6 | * Runtime driver 7 | 8 | [By default][target-flag], Rust's UEFI targets produce applications. This can be 9 | changed by passing a [`subsystem`] linker flag in `rustflags` and setting the 10 | value to `efi_boot_service_driver` or `efi_runtime_driver`. 11 | 12 | Example: 13 | 14 | ```rust 15 | // In build.rs 16 | 17 | fn main() { 18 | let target = std::env::var("TARGET").unwrap(); 19 | if target.ends_with("-unknown-uefi") { 20 | println!("cargo::rustc-link-arg=/subsystem:efi_runtime_driver"); 21 | } 22 | } 23 | ``` 24 | 25 | [spec-images]: https://uefi.org/specs/UEFI/2.10/02_Overview.html#uefi-images 26 | [target-flag]: https://github.com/rust-lang/rust/blob/f4d794ea0b845413344621d89f6c945062748485/compiler/rustc_target/src/spec/base/uefi_msvc.rs#L33 27 | [`subsystem`]: https://learn.microsoft.com/en-us/cpp/build/reference/subsystem?view=msvc-170 28 | -------------------------------------------------------------------------------- /xtask/src/arch.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | use std::fmt; 4 | 5 | #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, clap::ValueEnum)] 6 | pub enum UefiArch { 7 | #[value(name = "aarch64")] 8 | AArch64, 9 | 10 | #[value(name = "ia32")] 11 | IA32, 12 | 13 | #[value(name = "x86_64")] 14 | #[default] 15 | X86_64, 16 | } 17 | 18 | impl UefiArch { 19 | fn as_str(self) -> &'static str { 20 | match self { 21 | Self::AArch64 => "aarch64", 22 | Self::IA32 => "ia32", 23 | Self::X86_64 => "x86_64", 24 | } 25 | } 26 | 27 | pub fn as_triple(self) -> &'static str { 28 | match self { 29 | Self::AArch64 => "aarch64-unknown-uefi", 30 | Self::IA32 => "i686-unknown-uefi", 31 | Self::X86_64 => "x86_64-unknown-uefi", 32 | } 33 | } 34 | } 35 | 36 | impl fmt::Display for UefiArch { 37 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 38 | write!(f, "{}", self.as_str()) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ 4 | "template", 5 | "uefi", 6 | "uefi-macros", 7 | "uefi-raw", 8 | "uefi-std-example", 9 | "uefi-test-runner", 10 | "xtask", 11 | ] 12 | 13 | [workspace.package] 14 | authors = ["The Rust OSDev team"] 15 | categories = ["embedded", "no-std", "api-bindings"] 16 | edition = "2024" 17 | keywords = ["uefi", "efi"] 18 | license = "MIT OR Apache-2.0" 19 | repository = "https://github.com/rust-osdev/uefi-rs" 20 | rust-version = "1.85.1" 21 | 22 | [workspace.dependencies] 23 | bitflags = "2.0.0" 24 | log = { version = "0.4.5", default-features = false } 25 | ptr_meta = { version = "0.3.0", default-features = false, features = ["derive"] } 26 | uguid = "2.2.1" 27 | 28 | [patch.crates-io] 29 | uefi = { path = "uefi" } 30 | uefi-macros = { path = "uefi-macros" } 31 | uefi-raw = { path = "uefi-raw" } 32 | 33 | # Enable optimization for xtask itself, not for its dependencies. This speeds up 34 | # OVMF prebuilt decompression without much increase in compilation time. 35 | [profile.dev.package.xtask] 36 | opt-level = 3 37 | -------------------------------------------------------------------------------- /uefi-raw/src/protocol/memory_protection.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | use crate::table::boot::MemoryAttribute; 4 | use crate::{Guid, PhysicalAddress, Status, guid}; 5 | 6 | #[derive(Debug)] 7 | #[repr(C)] 8 | pub struct MemoryAttributeProtocol { 9 | pub get_memory_attributes: unsafe extern "efiapi" fn( 10 | this: *const Self, 11 | base_address: PhysicalAddress, 12 | length: u64, 13 | attributes: *mut MemoryAttribute, 14 | ) -> Status, 15 | 16 | pub set_memory_attributes: unsafe extern "efiapi" fn( 17 | this: *const Self, 18 | base_address: PhysicalAddress, 19 | length: u64, 20 | attributes: MemoryAttribute, 21 | ) -> Status, 22 | 23 | pub clear_memory_attributes: unsafe extern "efiapi" fn( 24 | this: *const Self, 25 | base_address: PhysicalAddress, 26 | length: u64, 27 | attributes: MemoryAttribute, 28 | ) -> Status, 29 | } 30 | 31 | impl MemoryAttributeProtocol { 32 | pub const GUID: Guid = guid!("f4560cf6-40ec-4b4a-a192-bf1d57d0b189"); 33 | } 34 | -------------------------------------------------------------------------------- /uefi/src/mem/memory_map/iter.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | use super::*; 4 | 5 | /// An iterator for [`MemoryMap`]. 6 | /// 7 | /// The underlying memory might contain an invalid/malformed memory map 8 | /// which can't be checked during construction of this type. The iterator 9 | /// might yield unexpected results. 10 | #[derive(Debug, Clone)] 11 | pub struct MemoryMapIter<'a> { 12 | pub(crate) memory_map: &'a dyn MemoryMap, 13 | pub(crate) index: usize, 14 | } 15 | 16 | impl<'a> Iterator for MemoryMapIter<'a> { 17 | type Item = &'a MemoryDescriptor; 18 | 19 | fn next(&mut self) -> Option { 20 | let desc = self.memory_map.get(self.index)?; 21 | 22 | self.index += 1; 23 | 24 | Some(desc) 25 | } 26 | 27 | fn size_hint(&self) -> (usize, Option) { 28 | let sz = self.memory_map.len() - self.index; 29 | 30 | (sz, Some(sz)) 31 | } 32 | } 33 | 34 | impl ExactSizeIterator for MemoryMapIter<'_> { 35 | fn len(&self) -> usize { 36 | self.memory_map.len() 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /uefi-test-runner/src/runtime/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | mod vars; 4 | 5 | use uefi::runtime::{self, Daylight, Time, TimeParams}; 6 | 7 | pub fn test() { 8 | info!("Testing runtime services"); 9 | vars::test(); 10 | test_time(); 11 | } 12 | 13 | fn test_time() { 14 | // Print the current time and time capabilities. 15 | info!( 16 | "Time with caps: {:?}", 17 | runtime::get_time_and_caps().unwrap() 18 | ); 19 | 20 | // Set the time. 21 | let time = Time::new(TimeParams { 22 | year: 2020, 23 | month: 1, 24 | day: 2, 25 | hour: 3, 26 | minute: 4, 27 | second: 5, 28 | nanosecond: 6, 29 | time_zone: None, 30 | daylight: Daylight::ADJUST_DAYLIGHT, 31 | }) 32 | .unwrap(); 33 | unsafe { runtime::set_time(&time).unwrap() }; 34 | 35 | // Print the new time and check that the year was successfully changed. 36 | let now = runtime::get_time().unwrap(); 37 | info!("After setting time: {now}"); 38 | assert_eq!(now.year(), 2020); 39 | } 40 | -------------------------------------------------------------------------------- /uefi-test-runner/examples/input.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | #![no_main] 4 | #![no_std] 5 | 6 | use uefi::proto::console::text::{Input, Key, ScanCode}; 7 | use uefi::{Result, ResultExt, Status, boot, entry, println, system}; 8 | 9 | fn read_keyboard_events(input: &mut Input) -> Result { 10 | loop { 11 | println!("waiting for key press..."); 12 | 13 | // Pause until a keyboard event occurs. 14 | let mut events = [input.wait_for_key_event().unwrap()]; 15 | boot::wait_for_event(&mut events).discard_errdata()?; 16 | 17 | match input.read_key()? { 18 | Some(Key::Printable(key)) => { 19 | println!("key '{key}' was pressed"); 20 | } 21 | 22 | // Exit the loop when the escape key is pressed. 23 | Some(Key::Special(ScanCode::ESCAPE)) => { 24 | break; 25 | } 26 | _ => {} 27 | } 28 | } 29 | 30 | Ok(()) 31 | } 32 | 33 | #[entry] 34 | fn main() -> Status { 35 | system::with_stdin(|input| read_keyboard_events(input).status()) 36 | } 37 | -------------------------------------------------------------------------------- /uefi/src/data_types/guid.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | pub use uguid::Guid; 4 | 5 | /// Several entities in the UEFI specification can be referred to by their GUID, 6 | /// this trait is a building block to interface them in uefi-rs. 7 | /// 8 | /// You should never need to use the `Identify` trait directly, but instead go 9 | /// for more specific traits such as [`Protocol`] or [`FileProtocolInfo`], which 10 | /// indicate in which circumstances an `Identify`-tagged type should be used. 11 | /// 12 | /// For the common case of implementing this trait for a protocol, use 13 | /// the [`unsafe_protocol`] macro. 14 | /// 15 | /// # Safety 16 | /// 17 | /// Implementing `Identify` is unsafe because attaching an incorrect GUID to a 18 | /// type can lead to type unsafety on both the Rust and UEFI side. 19 | /// 20 | /// [`Protocol`]: crate::proto::Protocol 21 | /// [`FileProtocolInfo`]: crate::proto::media::file::FileProtocolInfo 22 | /// [`unsafe_protocol`]: crate::proto::unsafe_protocol 23 | pub unsafe trait Identify { 24 | /// Unique protocol identifier. 25 | const GUID: Guid; 26 | } 27 | -------------------------------------------------------------------------------- /uefi/src/data_types/opaque.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | /// Create an opaque struct suitable for use as an FFI pointer. 4 | /// 5 | /// The internal representation uses the recommendation in the [nomicon]. 6 | /// 7 | /// [nomicon]: https://doc.rust-lang.org/stable/nomicon/ffi.html#representing-opaque-structs 8 | #[macro_export] 9 | macro_rules! opaque_type { 10 | ( 11 | $(#[$struct_attrs:meta])* 12 | $struct_vis:vis struct $struct_name:ident; 13 | ) => { 14 | // Create the struct with the fields recommended by the nomicon. 15 | $(#[$struct_attrs])* 16 | #[repr(C)] 17 | $struct_vis struct $struct_name { 18 | _data: [u8; 0], 19 | _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>, 20 | } 21 | 22 | // Impl Debug, just show the struct name. 23 | impl core::fmt::Debug for $struct_name { 24 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 25 | f.debug_struct(stringify!($struct_name)).finish() 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /uefi-raw/src/protocol/network/tls.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | use crate::{Guid, Status, guid, newtype_enum}; 4 | use core::ffi::c_void; 5 | 6 | newtype_enum! { 7 | pub enum TlsConfigDataType: i32 => { 8 | HOST_PUBLIC_CERT = 0, 9 | HOST_PRIVATE_KEY = 1, 10 | CA_CERTIFICATE = 2, 11 | CERT_REVOCATION_LIST = 3, 12 | MAXIMUM = 4, 13 | } 14 | } 15 | 16 | #[derive(Debug)] 17 | #[repr(C)] 18 | pub struct TlsConfigurationProtocol { 19 | pub set_data: unsafe extern "efiapi" fn( 20 | this: *mut Self, 21 | typ: TlsConfigDataType, 22 | data: *const c_void, 23 | size: usize, 24 | ) -> Status, 25 | 26 | pub get_data: unsafe extern "efiapi" fn( 27 | this: *const Self, 28 | typ: TlsConfigDataType, 29 | data: *mut c_void, 30 | size: *mut usize, 31 | ) -> Status, 32 | } 33 | 34 | impl TlsConfigurationProtocol { 35 | pub const GUID: Guid = guid!("1682fe44-bd7a-4407-b7c7-dca37ca3922d"); 36 | pub const SERVICE_BINDING_GUID: Guid = guid!("952cb795-ff36-48cf-a249-4df486d6ab8d"); 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) The uefi-rs contributors 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /uefi/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) The uefi-rs contributors 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /uefi-macros/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) The uefi-rs contributors 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /uefi-raw/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) The uefi-rs contributors 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /uefi-std-example/src/main.rs: -------------------------------------------------------------------------------- 1 | // Note: In Rust 1.82.0-nightly and before, the `uefi_std` feature is 2 | // required for accessing `std::os::uefi::env::*`. The other default 3 | // functionality doesn't need a nightly toolchain (with Rust 1.80 and later), 4 | // but with that limited functionality you - currently - also can't integrate 5 | // the `uefi` crate. 6 | #![feature(uefi_std)] 7 | 8 | use std::os::uefi as uefi_std; 9 | use uefi::runtime::ResetType; 10 | use uefi::{Handle, Status}; 11 | 12 | /// Performs the necessary setup code for the `uefi` crate. 13 | fn setup_uefi_crate() { 14 | let st = uefi_std::env::system_table(); 15 | let ih = uefi_std::env::image_handle(); 16 | 17 | // Mandatory setup code for `uefi` crate. 18 | unsafe { 19 | uefi::table::set_system_table(st.as_ptr().cast()); 20 | 21 | let ih = Handle::from_ptr(ih.as_ptr().cast()).unwrap(); 22 | uefi::boot::set_image_handle(ih); 23 | } 24 | } 25 | 26 | fn main() { 27 | println!("Hello World from uefi_std"); 28 | setup_uefi_crate(); 29 | println!("UEFI-Version is {}", uefi::system::uefi_revision()); 30 | uefi::runtime::reset(ResetType::SHUTDOWN, Status::SUCCESS, None); 31 | } 32 | -------------------------------------------------------------------------------- /book/src/concepts/device_paths.md: -------------------------------------------------------------------------------- 1 | # Device Paths 2 | 3 | A device path is a very flexible packed data structure for storing paths 4 | to many kinds of device. Note that these device paths are not the same 5 | thing as file system paths, although they can include file system 6 | paths. Like [handles], device paths can be used to uniquely identify 7 | resources such as consoles, mice, disks, partitions, and more. Unlike 8 | [handles], which are essentially opaque pointers, device paths are 9 | variable-length structures that contain parseable information. 10 | 11 | The [`uefi::proto::device_path`] module documentation describes the 12 | details of how device paths are encoded. 13 | 14 | Device paths can also be converted to and from human-readable text 15 | representations that look like this: 16 | ```text 17 | PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x0,0xFFFF,0x0)/HD(1,MBR,0xBE1AFDFA,0x3F,0xFBFC1) 18 | ``` 19 | 20 | See [`uefi::proto::device_path::text`] for details. 21 | 22 | [handles]: handles_and_protocols.md 23 | [`uefi::proto::device_path`]: https://docs.rs/uefi/latest/uefi/proto/device_path/index.html 24 | [`uefi::proto::device_path::text`]: https://docs.rs/uefi/latest/uefi/proto/device_path/text/index.html 25 | -------------------------------------------------------------------------------- /uefi-raw/src/protocol/loaded_image.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | use crate::protocol::device_path::DevicePathProtocol; 4 | use crate::table::boot::MemoryType; 5 | use crate::table::system::SystemTable; 6 | use crate::{Guid, Handle, Status, guid}; 7 | use core::ffi::c_void; 8 | 9 | #[derive(Clone, Copy, Debug)] 10 | #[repr(C)] 11 | pub struct LoadedImageProtocol { 12 | pub revision: u32, 13 | pub parent_handle: Handle, 14 | pub system_table: *const SystemTable, 15 | 16 | // Source location of the image. 17 | pub device_handle: Handle, 18 | pub file_path: *const DevicePathProtocol, 19 | 20 | pub reserved: *const c_void, 21 | 22 | // Image load options. 23 | pub load_options_size: u32, 24 | pub load_options: *const c_void, 25 | 26 | // Location where image was loaded. 27 | pub image_base: *const c_void, 28 | pub image_size: u64, 29 | pub image_code_type: MemoryType, 30 | pub image_data_type: MemoryType, 31 | pub unload: Option Status>, 32 | } 33 | 34 | impl LoadedImageProtocol { 35 | pub const GUID: Guid = guid!("5b1b31a1-9562-11d2-8e3f-00a0c969723b"); 36 | } 37 | -------------------------------------------------------------------------------- /uefi/src/mem/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | //! Types, functions, traits, and other helpers to work with memory in UEFI 4 | //! libraries and applications. 5 | 6 | use crate::boot; 7 | use core::ptr::NonNull; 8 | 9 | pub mod memory_map; 10 | 11 | #[cfg(feature = "alloc")] 12 | pub(crate) mod util; 13 | 14 | #[cfg(feature = "alloc")] 15 | pub(crate) use util::*; 16 | 17 | #[cfg(feature = "alloc")] 18 | mod aligned_buffer; 19 | #[cfg(feature = "alloc")] 20 | pub use aligned_buffer::{AlignedBuffer, AlignmentError}; 21 | 22 | /// Wrapper for memory allocated with UEFI's pool allocator. The memory is freed 23 | /// on drop. 24 | #[derive(Debug)] 25 | pub(crate) struct PoolAllocation(NonNull); 26 | 27 | impl PoolAllocation { 28 | pub(crate) const fn new(ptr: NonNull) -> Self { 29 | Self(ptr) 30 | } 31 | 32 | pub(crate) const fn as_ptr(&self) -> NonNull { 33 | self.0 34 | } 35 | } 36 | 37 | impl Drop for PoolAllocation { 38 | fn drop(&mut self) { 39 | // Ignore errors returned by `free_pool` since we can't propagate them 40 | // from `drop`. 41 | let _ = unsafe { boot::free_pool(self.0) }; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /uefi-raw/src/protocol/string.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | use crate::{Boolean, Char8, Char16, Guid, guid}; 4 | 5 | #[derive(Debug)] 6 | #[repr(C)] 7 | pub struct UnicodeCollationProtocol { 8 | pub stri_coll: 9 | unsafe extern "efiapi" fn(this: *const Self, s1: *const Char16, s2: *const Char16) -> isize, 10 | pub metai_match: unsafe extern "efiapi" fn( 11 | this: *const Self, 12 | string: *const Char16, 13 | pattern: *const Char16, 14 | ) -> Boolean, 15 | pub str_lwr: unsafe extern "efiapi" fn(this: *const Self, s: *mut Char16), 16 | pub str_upr: unsafe extern "efiapi" fn(this: *const Self, s: *mut Char16), 17 | pub fat_to_str: unsafe extern "efiapi" fn( 18 | this: *const Self, 19 | fat_size: usize, 20 | fat: *const Char8, 21 | s: *mut Char16, 22 | ), 23 | pub str_to_fat: unsafe extern "efiapi" fn( 24 | this: *const Self, 25 | s: *const Char16, 26 | fat_size: usize, 27 | fat: *mut Char8, 28 | ) -> Boolean, 29 | pub supported_languages: *const Char8, 30 | } 31 | 32 | impl UnicodeCollationProtocol { 33 | pub const GUID: Guid = guid!("a4c751fc-23ae-4c3e-92e9-4964cf63f349"); 34 | } 35 | -------------------------------------------------------------------------------- /uefi/src/polyfill.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | //! Polyfills for functions in the standard library that are currently gated 4 | //! behind unstable features. 5 | 6 | use core::mem::MaybeUninit; 7 | use core::ptr; 8 | #[cfg(feature = "alloc")] 9 | use {alloc::vec::Vec, core::mem::ManuallyDrop}; 10 | 11 | /// Polyfill for the unstable `MaybeUninit::slice_assume_init_ref` function. 12 | /// 13 | /// See . 14 | pub const unsafe fn maybe_uninit_slice_assume_init_ref(s: &[MaybeUninit]) -> &[T] { 15 | unsafe { &*(ptr::from_ref(s) as *const [T]) } 16 | } 17 | 18 | /// Polyfill for the unstable `MaybeUninit::slice_as_mut_ptr` function. 19 | /// 20 | /// See . 21 | pub const fn maybe_uninit_slice_as_mut_ptr(s: &mut [MaybeUninit]) -> *mut T { 22 | s.as_mut_ptr().cast::() 23 | } 24 | 25 | /// Polyfill for the unstable `Vec::into_raw_parts` function. 26 | /// 27 | /// See . 28 | #[cfg(feature = "alloc")] 29 | pub fn vec_into_raw_parts(v: Vec) -> (*mut T, usize, usize) { 30 | let mut v = ManuallyDrop::new(v); 31 | (v.as_mut_ptr(), v.len(), v.capacity()) 32 | } 33 | -------------------------------------------------------------------------------- /uefi/src/proto/hii/config.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | //! HII Configuration protocols. 4 | 5 | use uefi_macros::unsafe_protocol; 6 | use uefi_raw::protocol::hii::config::{ConfigKeywordHandlerProtocol, HiiConfigAccessProtocol}; 7 | 8 | /// The HII Keyword Handler Protocol. 9 | /// 10 | /// # UEFI Spec Description 11 | /// 12 | /// This protocol provides the mechanism to set and get the values associated 13 | /// with a keyword exposed through a x-UEFI- prefixed configuration language namespace. 14 | #[derive(Debug)] 15 | #[repr(transparent)] 16 | #[unsafe_protocol(ConfigKeywordHandlerProtocol::GUID)] 17 | pub struct ConfigKeywordHandler(ConfigKeywordHandlerProtocol); 18 | 19 | /// The HII Configuration Access Protocol. 20 | /// 21 | /// # UEFI Spec Description 22 | /// 23 | /// This protocol is responsible for facilitating access to configuration data from HII. 24 | /// It is typically invoked by the HII Configuration Routing Protocol for handling 25 | /// configuration requests. Forms browsers also interact with this protocol through 26 | /// the `Callback()` function. 27 | #[derive(Debug)] 28 | #[repr(transparent)] 29 | #[unsafe_protocol(HiiConfigAccessProtocol::GUID)] 30 | pub struct HiiConfigAccess(HiiConfigAccessProtocol); 31 | -------------------------------------------------------------------------------- /book/src/how_to/rust-std.md: -------------------------------------------------------------------------------- 1 | # Combining Rust `std` with `uefi` 2 | 3 | ## TL;DR 4 | 5 | In Mid-2024, we recommend to stick to our normal guide. Use this document as 6 | guide and outlook for the future of UEFI and Rust. 7 | 8 | ## About 9 | 10 | Programs created with the `uefi` crate are typically created with `#![no_std]` 11 | and `#![no_main]`. A `#![no_std]` crate can use the `core` and `alloc` parts of 12 | Rust's standard library, but not `std`. A `#![no_main]` executable does not use 13 | the standard main entry point, and must define its own entry point; `uefi` 14 | provides the `#[entry]` macro for this purpose. 15 | 16 | Rust has added partial support for building UEFI executables without 17 | `#![no_std]` and `#![no_main]`, thus, the standard way. Some functionality 18 | requires a nightly toolchain, they are gated by the `uefi_std` feature (Rust 19 | language feature, not `uefi` crate feature). Follow the 20 | [tracking issue](https://github.com/rust-lang/rust/issues/100499) for details. 21 | 22 | ## Code Example 23 | 24 | Please refer to [`/uefi-std-example`](/uefi-std-example/README.md) to 25 | see a specific example. The relevant `main.rs` looks as follows: 26 | 27 | ```rust 28 | {{#include ../../../uefi-std-example/src/main.rs}} 29 | ``` 30 | -------------------------------------------------------------------------------- /uefi-test-runner/README.md: -------------------------------------------------------------------------------- 1 | # uefi-test-runner 2 | 3 | This package is a UEFI application for running tests. It is intended to 4 | be run in a specially-configured QEMU VM. This allows us to test the 5 | parts of the `uefi` package that depend on a UEFI environment, such as 6 | various boot services and protocols. 7 | 8 | ## Requirements 9 | 10 | - [QEMU](https://www.qemu.org/): the most recent version of QEMU is recommended. 11 | - [Python 3](https://www.python.org): at least version 3.6 is required. 12 | - [OVMF](https://github.com/tianocore/tianocore.github.io/wiki/OVMF): 13 | You need to extract the firmware files into the `uefi-test-runner` directory. 14 | - For x86_64: `OVMF_CODE.fd` and `OVMF_VARS.fd` 15 | - For AArch64: `QEMU_EFI-pflash.raw` and `vars-template-pflash.raw` 16 | Alternatively, install OVMF using your distro's package manager and change the paths in the script file. 17 | **Note**: if your distro's OVMF version is too old / does not provide these files, 18 | you can download [Gerd Hoffmann's builds](https://www.kraxel.org/repos/) and extract them in the local directory. 19 | 20 | ## Build and run in QEMU 21 | 22 | Use `cargo xtask run` to build `uefi-test-runner` and run it in QEMU. See 23 | the top-level [README](../README.md) for more details of `cargo xtask`. 24 | -------------------------------------------------------------------------------- /uefi/src/fs/dir_entry_iter.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | //! Module for directory iteration. See [`UefiDirectoryIter`]. 4 | 5 | use super::*; 6 | use crate::{CStr16, Result, cstr16}; 7 | use alloc::boxed::Box; 8 | 9 | /// Common skip dirs in UEFI/FAT-style file systems. 10 | pub const COMMON_SKIP_DIRS: &[&CStr16] = &[cstr16!("."), cstr16!("..")]; 11 | 12 | /// Iterates over the entries of an UEFI directory. It returns boxed values of 13 | /// type [`UefiFileInfo`]. 14 | /// 15 | /// Note that on UEFI/FAT-style file systems, the root dir usually doesn't 16 | /// return the entries `.` and `..`, whereas sub directories do. 17 | #[derive(Debug)] 18 | pub struct UefiDirectoryIter(UefiDirectoryHandle); 19 | 20 | impl UefiDirectoryIter { 21 | /// Constructor. 22 | #[must_use] 23 | pub const fn new(handle: UefiDirectoryHandle) -> Self { 24 | Self(handle) 25 | } 26 | } 27 | 28 | impl Iterator for UefiDirectoryIter { 29 | type Item = Result, ()>; 30 | 31 | fn next(&mut self) -> Option { 32 | let e = self.0.read_entry_boxed(); 33 | match e { 34 | // no more entries 35 | Ok(None) => None, 36 | Ok(Some(e)) => Some(Ok(e)), 37 | Err(e) => Some(Err(e)), 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /uefi/src/proto/shell_params.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | //! `ShellParams` protocol 4 | 5 | use crate::proto::unsafe_protocol; 6 | use crate::{Char16, data_types}; 7 | use core::slice::from_raw_parts; 8 | use uefi_raw::protocol::shell_params::ShellParametersProtocol; 9 | 10 | use crate::CStr16; 11 | 12 | /// The ShellParameters [`Protocol`]. 13 | /// 14 | /// [`Protocol`]: uefi::proto::Protocol 15 | #[derive(Debug)] 16 | #[repr(transparent)] 17 | #[unsafe_protocol(ShellParametersProtocol::GUID)] 18 | pub struct ShellParameters(ShellParametersProtocol); 19 | 20 | impl ShellParameters { 21 | /// Get the number of shell parameter arguments 22 | #[must_use] 23 | pub const fn args_len(&self) -> usize { 24 | self.0.argc 25 | } 26 | 27 | /// Get an iterator of the shell parameter arguments 28 | pub fn args(&self) -> impl Iterator { 29 | self.args_slice() 30 | .iter() 31 | .map(|x| unsafe { CStr16::from_ptr(*x) }) 32 | } 33 | 34 | /// Get a slice of the args, as Char16 pointers 35 | #[must_use] 36 | const fn args_slice(&self) -> &[*const Char16] { 37 | unsafe { 38 | from_raw_parts( 39 | self.0.argv.cast::<*const data_types::chars::Char16>(), 40 | self.0.argc, 41 | ) 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /uefi-raw/src/protocol/hii/popup.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | //! Popup protocol 4 | 5 | use super::{HiiHandle, StringId}; 6 | use crate::{Guid, Status, guid, newtype_enum}; 7 | 8 | newtype_enum! { 9 | /// EFI_HII_POPUP_STYLE 10 | pub enum HiiPopupStyle: u32 => { 11 | INFO = 0, 12 | WARNING = 1, 13 | ERROR = 2, 14 | } 15 | } 16 | 17 | newtype_enum! { 18 | /// EFI_HII_POPUP_TYPE 19 | pub enum HiiPopupType: u32 => { 20 | OK = 0, 21 | OK_CANCEL = 1, 22 | YES_NO = 2, 23 | YES_NO_CANCEL = 3, 24 | } 25 | } 26 | 27 | newtype_enum! { 28 | /// EFI_HII_POPUP_SELECTION 29 | pub enum HiiPopupSelection: u32 => { 30 | OK = 0, 31 | CANCEL = 1, 32 | YES = 2, 33 | NO = 3, 34 | } 35 | } 36 | 37 | /// EFI_HII_POPUP_PROTOCOL 38 | #[derive(Debug)] 39 | #[repr(C)] 40 | pub struct HiiPopupProtocol { 41 | pub revision: u64, 42 | pub create_popup: unsafe extern "efiapi" fn( 43 | this: *const Self, 44 | popup_style: HiiPopupStyle, 45 | popup_type: HiiPopupType, 46 | hii_handle: HiiHandle, 47 | message: StringId, 48 | user_selection: *mut HiiPopupSelection, 49 | ) -> Status, 50 | } 51 | 52 | impl HiiPopupProtocol { 53 | pub const GUID: Guid = guid!("4311edc0-6054-46d4-9e40-893ea952fccc"); 54 | pub const REVISION: u64 = 1; 55 | } 56 | -------------------------------------------------------------------------------- /xtask/src/device_path/util.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | use anyhow::{Context, Result, bail}; 4 | use std::io::Write; 5 | use std::process::{Command, Stdio}; 6 | use std::thread; 7 | use syn::Attribute; 8 | 9 | /// Returns true if the attribute is a `#[doc = "..."]` attribute, 10 | /// otherwise returns false. 11 | pub fn is_doc_attr(attr: &Attribute) -> bool { 12 | attr.path().is_ident("doc") 13 | } 14 | 15 | /// Run `rustfmt` on the `input` string and return the formatted code. 16 | pub fn rustfmt_string(input: String) -> Result { 17 | let mut child = Command::new("rustfmt") 18 | .args([ 19 | "--config", 20 | // Convert `#[doc = "..."]` to `///` for readability. 21 | "normalize_doc_attributes=true", 22 | ]) 23 | .stdin(Stdio::piped()) 24 | .stdout(Stdio::piped()) 25 | .spawn()?; 26 | 27 | // Write on a separate thread to avoid deadlock. 28 | let mut stdin = child.stdin.take().context("failed to take stdin")?; 29 | thread::spawn(move || { 30 | stdin 31 | .write_all(input.as_bytes()) 32 | .expect("failed to write to stdin"); 33 | }); 34 | 35 | let output = child.wait_with_output()?; 36 | if !output.status.success() { 37 | bail!("rustfmt failed"); 38 | } 39 | 40 | let stdout = String::from_utf8(output.stdout)?; 41 | 42 | Ok(stdout) 43 | } 44 | -------------------------------------------------------------------------------- /book/src/concepts/boot_stages.md: -------------------------------------------------------------------------------- 1 | # Boot Stages 2 | 3 | A UEFI system goes through several distinct phases during the boot process. 4 | 1. **Platform Initialization.** This early-boot phase is mostly outside 5 | the scope of the `uefi` crate. It is described by the [UEFI Platform 6 | Initialization Specification], which is separate from the main UEFI 7 | Specification. 8 | 2. **Boot Services.** This is when UEFI drivers and applications are loaded. 9 | Functions in both the [`boot`] module and [`runtime`] module can be used. 10 | This stage typically culminates in running a bootloader that loads an 11 | operating system. The stage ends when [`boot::exit_boot_services`] is called, 12 | putting the system in Runtime mode. 13 | 3. **Runtime.** This stage is typically active when running an operating system 14 | such as Linux or Windows. UEFI functionality is much more limited in the 15 | Runtime mode. Functions in the [`boot`] module can no longer be used, but 16 | functions in the [`runtime`] module are still available. Once the system is 17 | in Runtime mode, it cannot return to the Boot Services stage until after a 18 | system reset. 19 | 20 | [UEFI Platform Initialization Specification]: https://uefi.org/specifications 21 | [`boot`]: https://docs.rs/uefi/latest/uefi/boot/index.html 22 | [`runtime`]: https://docs.rs/uefi/latest/uefi/runtime/index.html 23 | [`boot::exit_boot_services`]: https://docs.rs/uefi/latest/uefi/boot/fn.exit_boot_services.html 24 | -------------------------------------------------------------------------------- /uefi/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uefi" 3 | version = "0.36.1" 4 | readme = "README.md" 5 | description = """ 6 | This crate makes it easy to develop Rust software that leverages safe, 7 | convenient, and performant abstractions for UEFI functionality. 8 | """ 9 | 10 | authors.workspace = true 11 | categories.workspace = true 12 | edition.workspace = true 13 | keywords.workspace = true 14 | license.workspace = true 15 | repository.workspace = true 16 | rust-version.workspace = true 17 | 18 | # Feature documentation in uefi/lib.rs. 19 | [features] 20 | # KEEP this feature list in sync with doc in uefi/lib.rs! 21 | default = [ ] 22 | alloc = [] 23 | 24 | # Generic gate to code that uses unstable features of Rust, needing a nightly 25 | # toolchain. 26 | unstable = [] 27 | 28 | # Helper features: 29 | logger = [] 30 | global_allocator = [] 31 | panic_handler = [] 32 | # Some convenience when running inside QEMU. 33 | # - dependency log-debugcon: logical, not technical 34 | # - dependency panic_handler: logical, not technical 35 | qemu = ["dep:qemu-exit", "panic_handler", "log-debugcon"] 36 | log-debugcon = [] 37 | 38 | [dependencies] 39 | bitflags.workspace = true 40 | log.workspace = true 41 | ptr_meta.workspace = true 42 | uguid.workspace = true 43 | cfg-if = "1.0.0" 44 | ucs2 = "0.3.3" 45 | uefi-macros = "0.19.0" 46 | uefi-raw = "0.13.0" 47 | qemu-exit = { version = "3.0.2", optional = true } 48 | 49 | [package.metadata.docs.rs] 50 | all-features = true 51 | rustdoc-args = ["--cfg", "docsrs"] 52 | -------------------------------------------------------------------------------- /uefi-test-runner/src/proto/misc.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | use uefi::prelude::*; 4 | use uefi::proto::misc::ResetNotification; 5 | use uefi::runtime::ResetType; 6 | 7 | pub fn test() { 8 | test_reset_notification(); 9 | } 10 | 11 | pub fn test_reset_notification() { 12 | info!("Running loaded ResetNotification protocol test"); 13 | 14 | let handle = boot::get_handle_for_protocol::() 15 | .expect("Failed to get handles for `ResetNotification` protocol"); 16 | 17 | let mut reset_notif_proto = boot::open_protocol_exclusive::(handle) 18 | .expect("Founded ResetNotification Protocol but open failed"); 19 | 20 | // value efi_reset_fn is the type of ResetSystemFn, a function pointer 21 | unsafe extern "efiapi" fn efi_reset_fn( 22 | rt: ResetType, 23 | status: Status, 24 | data_size: usize, 25 | data: *const u8, 26 | ) { 27 | info!("Inside the event callback, hi, efi_reset_fn"); 28 | info!("rt: {rt:?} status: {status:?}"); 29 | info!("size: {data_size:?} data: {data:?}"); 30 | // do what you want 31 | } 32 | 33 | let result = reset_notif_proto.register_reset_notify(efi_reset_fn); 34 | info!("ResetNotification Protocol register efi_reset_fn test: {result:?}"); 35 | 36 | let result = reset_notif_proto.unregister_reset_notify(efi_reset_fn); 37 | info!("ResetNotification Protocol unregister efi_reset_fn test: {result:?}"); 38 | } 39 | -------------------------------------------------------------------------------- /uefi/src/proto/hii/config_routing.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | //! HII Configuration protocols. 4 | 5 | use core::ptr; 6 | 7 | use alloc::string::{String, ToString}; 8 | use uefi_macros::unsafe_protocol; 9 | use uefi_raw::Char16; 10 | use uefi_raw::protocol::hii::config::HiiConfigRoutingProtocol; 11 | 12 | use crate::{CStr16, StatusExt}; 13 | 14 | /// The HII Configuration Routing Protocol. 15 | /// 16 | /// # UEFI Spec Description 17 | /// 18 | /// The EFI HII Configuration Routing Protocol manages the movement of configuration 19 | /// data from drivers to configuration applications. It then serves as the single point 20 | /// to receive configuration information from configuration applications, routing the results 21 | /// to the appropriate drivers. 22 | #[derive(Debug)] 23 | #[repr(transparent)] 24 | #[unsafe_protocol(HiiConfigRoutingProtocol::GUID)] 25 | pub struct HiiConfigRouting(HiiConfigRoutingProtocol); 26 | impl HiiConfigRouting { 27 | /// Request the current configuration for the entirety of the current HII database and 28 | /// return the data as string in multi configuration string format. 29 | /// 30 | /// Use `super::config_str::MultiConfigurationStringIter` to parse the returned `String`. 31 | pub fn export(&self) -> uefi::Result { 32 | unsafe { 33 | let mut results: *const Char16 = ptr::null(); 34 | (self.0.export_config)(&self.0, &mut results) 35 | .to_result_with_val(|| CStr16::from_ptr(results.cast()).to_string()) 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /uefi-test-runner/examples/shell_params.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | // ANCHOR: all 4 | // ANCHOR: features 5 | #![no_main] 6 | #![no_std] 7 | // ANCHOR_END: features 8 | 9 | use log::error; 10 | // ANCHOR: use 11 | use uefi::prelude::*; 12 | use uefi::proto::shell_params::ShellParameters; 13 | use uefi::{boot, println}; 14 | 15 | extern crate alloc; 16 | use alloc::string::{String, ToString}; 17 | use alloc::vec::Vec; 18 | // ANCHOR_END: use 19 | 20 | // ANCHOR: entry 21 | #[entry] 22 | fn main() -> Status { 23 | // ANCHOR_END: entry 24 | // ANCHOR: services 25 | uefi::helpers::init().unwrap(); 26 | // ANCHOR_END: services 27 | 28 | // ANCHOR: params 29 | let shell_params = 30 | boot::open_protocol_exclusive::(boot::image_handle()); 31 | let shell_params = match shell_params { 32 | Ok(s) => s, 33 | Err(e) => { 34 | error!("Failed to get ShellParameters protocol"); 35 | return e.status(); 36 | } 37 | }; 38 | 39 | // Get as Vec of String, only with alloc feature 40 | let args: Vec = 41 | shell_params.args().map(|x| x.to_string()).collect(); 42 | println!("Args: {:?}", args); 43 | 44 | // Or without allocating, get a slice of the pointers 45 | println!("Num args: {}", args.len()); 46 | if shell_params.args_len() > 1 { 47 | println!("First real arg: '{}'", args[1]); 48 | } 49 | // ANCHOR_END: params 50 | 51 | // ANCHOR: return 52 | Status::SUCCESS 53 | } 54 | // ANCHOR_END: return 55 | // ANCHOR_END: all 56 | -------------------------------------------------------------------------------- /uefi-raw/src/protocol/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | //! Protocol definitions. 4 | //! 5 | //! # TL;DR 6 | //! Technically, a protocol is a `C` struct holding functions and/or data, with 7 | //! an associated [`GUID`]. 8 | //! 9 | //! # About 10 | //! UEFI protocols are a structured collection of functions and/or data, 11 | //! identified by a [`GUID`], which defines an interface between components in 12 | //! the UEFI environment, such as between drivers, applications, or firmware 13 | //! services. 14 | //! 15 | //! Protocols are central to UEFI’s handle-based object model, and they provide 16 | //! a clean, extensible way for components to discover and use services from one 17 | //! another. 18 | //! 19 | //! Implementation-wise, a protocol is a `C` struct holding function pointers 20 | //! and/or data. Please note that some protocols may use [`core::ptr::null`] as 21 | //! interface. For example, the device path protocol can be implemented but 22 | //! return `null`. 23 | //! 24 | //! [`GUID`]: crate::Guid 25 | 26 | pub mod acpi; 27 | pub mod ata; 28 | pub mod block; 29 | pub mod console; 30 | pub mod device_path; 31 | pub mod disk; 32 | pub mod driver; 33 | pub mod file_system; 34 | pub mod firmware_management; 35 | pub mod firmware_volume; 36 | pub mod hii; 37 | pub mod iommu; 38 | pub mod loaded_image; 39 | pub mod media; 40 | pub mod memory_protection; 41 | pub mod misc; 42 | pub mod network; 43 | pub mod nvme; 44 | pub mod pci; 45 | pub mod rng; 46 | pub mod scsi; 47 | pub mod shell; 48 | pub mod shell_params; 49 | pub mod string; 50 | pub mod tcg; 51 | pub mod usb; 52 | -------------------------------------------------------------------------------- /book/src/how_to/drawing.md: -------------------------------------------------------------------------------- 1 | # Drawing to the Screen 2 | 3 | This example shows how to draw to the screen using the [graphics output protocol]. 4 | The code will a [Sierpiński triangle] using the "chaos game" method. 5 | 6 | ![screenshot](https://i.imgur.com/0tpjtV6.png) 7 | 8 | The core abstraction used here is a linear buffer: 9 | ```rust 10 | {{#include ../../../uefi-test-runner/examples/sierpinski.rs:buffer}} 11 | ``` 12 | 13 | This `Buffer` type stores a `Vec` of [`BltPixel`]s, which are BGRX 14 | 32-bit pixels (8 bites each for blue, green, and red, followed by 8 15 | unused bits of padding). We use the `pixel` method to alter a single 16 | pixel at a time. This is often not an efficient method; for more complex 17 | graphics you could use a crate like [`embedded-graphics`]. 18 | 19 | The `Buffer::blit` method calls the graphics output protocol's `blt` 20 | method to copy the buffer to the screen. 21 | 22 | Most of the rest of the code is just implementing the algorithm for 23 | drawing the fractal. Here's the full example: 24 | 25 | ```rust 26 | {{#include ../../../uefi-test-runner/examples/sierpinski.rs:all}} 27 | ``` 28 | 29 | You can run this example from the [uefi-rs] repository with: 30 | ```console 31 | cargo xtask run --example sierpinski 32 | ``` 33 | 34 | [Sierpiński triangle]: https://en.wikipedia.org/wiki/Sierpiński_triangle#Chaos_game 35 | [`BltPixel`]: https://docs.rs/uefi/latest/uefi/proto/console/gop/struct.BltPixel.html 36 | [`embedded-graphics`]: https://crates.io/crates/embedded-graphics 37 | [graphics output protocol]: https://docs.rs/uefi/latest/uefi/proto/console/gop/index.html 38 | [uefi-rs]: https://github.com/rust-osdev/uefi-rs 39 | -------------------------------------------------------------------------------- /uefi/src/util.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | use core::ptr::{self, NonNull}; 4 | 5 | /// Copy the bytes of `val` to `ptr`, then advance pointer to just after the 6 | /// newly-copied bytes. 7 | pub const unsafe fn ptr_write_unaligned_and_add(ptr: &mut *mut u8, val: T) { 8 | unsafe { 9 | ptr.cast::().write_unaligned(val); 10 | *ptr = ptr.add(size_of::()); 11 | } 12 | } 13 | 14 | /// Convert from a `u32` to a `usize`. Panic if the input does fit. On typical 15 | /// targets `usize` is at least as big as `u32`, so this should never panic 16 | /// except on unusual targets. 17 | /// 18 | /// Comparison to alternatives: 19 | /// * `val as usize` doesn't check that `val` actually fits in a `usize`. 20 | /// * `usize::try_from(val).unwrap()` doesn't work in a const context. 21 | pub const fn usize_from_u32(val: u32) -> usize { 22 | // This is essentially the same as `usize::try_from(val).unwrap()`, but 23 | // works in a `const` context on stable. 24 | if size_of::() < size_of::() && val < (usize::MAX as u32) { 25 | panic!("value does not fit in a usize"); 26 | } else { 27 | val as usize 28 | } 29 | } 30 | 31 | /// Get the raw pointer from `opt`, defaulting to `null_mut`. 32 | pub fn opt_nonnull_to_ptr(opt: Option>) -> *mut T { 33 | opt.map(NonNull::as_ptr).unwrap_or(ptr::null_mut()) 34 | } 35 | 36 | #[cfg(test)] 37 | mod tests { 38 | use super::*; 39 | 40 | #[test] 41 | fn test_usize_from_u32() { 42 | assert_eq!(usize_from_u32(0), 0usize); 43 | assert_eq!(usize_from_u32(u32::MAX), 4294967295usize); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /.github/workflows/qa.yml: -------------------------------------------------------------------------------- 1 | name: QA 2 | on: [merge_group, push, pull_request] 3 | jobs: 4 | spellcheck: 5 | name: Spellcheck 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v6 9 | # Executes "typos ." 10 | - uses: crate-ci/typos@v1.40.0 11 | lints: 12 | name: Lints 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout sources 16 | uses: actions/checkout@v6 17 | - uses: cachix/install-nix-action@v31 18 | - uses: Swatinem/rust-cache@v2 19 | # Dedicated step to separate all the 20 | # "copying path '/nix/store/...' from 'https://cache.nixos.org'." 21 | # messages from the actual build output. 22 | - name: Prepare Nix Store 23 | run: nix develop --command echo 24 | # A dedicated step removes spam from the actual job. 25 | - name: Build cargo xtask 26 | run: cargo xtask help >/dev/null 27 | # Executing this in a Nix shell ensures that all our checks run as all 28 | # required tooling exists. 29 | - name: Check formatting 30 | run: | 31 | CMD="cargo xtask fmt --check" 32 | nix develop --command bash -c "$CMD" 33 | - name: Run clippy 34 | run: | 35 | rustup component add clippy 36 | cargo xtask clippy --warnings-as-errors 37 | - name: Run cargo doc (without unstable) 38 | run: cargo xtask doc --warnings-as-errors --document-private-items 39 | - name: Verify generated code is up-to-date 40 | run: cargo xtask gen-code --check 41 | - name: Run additional checks on the uefi-raw package 42 | run: cargo xtask check-raw 43 | -------------------------------------------------------------------------------- /uefi/src/proto/tcg/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | //! [TCG] (Trusted Computing Group) protocols. 4 | //! 5 | //! These protocols provide access to the [TPM][tpm] (Trusted Platform Module). 6 | //! 7 | //! There are two versions of the protocol. The original protocol is in 8 | //! the [`v1`] module. It is used with TPM 1.1 and 1.2 devices. The 9 | //! newer protocol in the [`v2`] module is generally provided for TPM 10 | //! 2.0 devices, although the spec indicates it can be used for older 11 | //! TPM versions as well. 12 | //! 13 | //! [TCG]: https://trustedcomputinggroup.org/ 14 | //! [TPM]: https://en.wikipedia.org/wiki/Trusted_Platform_Module 15 | 16 | pub mod v1; 17 | pub mod v2; 18 | 19 | pub use uefi_raw::protocol::tcg::{AlgorithmId, EventType}; 20 | 21 | use bitflags::bitflags; 22 | 23 | /// Platform Configuration Register (PCR) index. 24 | #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] 25 | #[repr(transparent)] 26 | pub struct PcrIndex(pub u32); 27 | 28 | bitflags! { 29 | /// Hash algorithms the protocol can provide. 30 | /// 31 | /// The [`v1`] protocol only supports SHA1. 32 | #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] 33 | #[repr(transparent)] 34 | pub struct HashAlgorithm: u32 { 35 | /// SHA-1 hash. 36 | const SHA1 = 0x0000_0001; 37 | 38 | /// SHA-256 hash. 39 | const SHA256 = 0x0000_0002; 40 | 41 | /// SHA-384 hash. 42 | const SHA384 = 0x0000_0004; 43 | 44 | /// SHA-512 hash. 45 | const SHA512 = 0x0000_0008; 46 | 47 | /// SM3-256 hash. 48 | const SM3_256 = 0x0000_0010; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /uefi-raw/src/protocol/misc.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | use crate::table::runtime; 4 | use crate::{Guid, Status, guid}; 5 | 6 | #[derive(Debug)] 7 | #[repr(C)] 8 | pub struct TimestampProtocol { 9 | pub get_timestamp: unsafe extern "efiapi" fn() -> u64, 10 | pub get_properties: unsafe extern "efiapi" fn(*mut TimestampProperties) -> Status, 11 | } 12 | 13 | impl TimestampProtocol { 14 | pub const GUID: Guid = guid!("afbfde41-2e6e-4262-ba65-62b9236e5495"); 15 | } 16 | 17 | /// Properties of the timestamp counter. 18 | #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)] 19 | #[repr(C)] 20 | pub struct TimestampProperties { 21 | /// Timestamp counter frequency, in Hz. 22 | pub frequency: u64, 23 | 24 | /// The maximum value of the timestamp counter before it rolls over. For 25 | /// example, a 24-bit counter would have an end value of `0xff_ffff`. 26 | pub end_value: u64, 27 | } 28 | 29 | /// Properties of Reset Notification. 30 | #[derive(Debug)] 31 | #[repr(C)] 32 | pub struct ResetNotificationProtocol { 33 | pub register_reset_notify: 34 | unsafe extern "efiapi" fn(this: *mut Self, reset_function: ResetSystemFn) -> Status, 35 | pub unregister_reset_notify: 36 | unsafe extern "efiapi" fn(this: *mut Self, reset_function: ResetSystemFn) -> Status, 37 | } 38 | 39 | impl ResetNotificationProtocol { 40 | pub const GUID: Guid = guid!("9da34ae0-eaf9-4bbf-8ec3-fd60226c44be"); 41 | } 42 | 43 | /// Raw reset notification function, to be called if you register it when a ResetSystem() is executed. 44 | pub type ResetSystemFn = unsafe extern "efiapi" fn( 45 | rt: runtime::ResetType, 46 | status: Status, 47 | data_size: usize, 48 | data: *const u8, 49 | ); 50 | -------------------------------------------------------------------------------- /book/src/concepts/tables.md: -------------------------------------------------------------------------------- 1 | # Tables 2 | 3 | UEFI has a few table structures. These tables are how you get access to UEFI 4 | services. In the specification and C API, `EFI_SYSTEM_TABLE` is the top-level 5 | table that provides access to the other tables, `EFI_BOOT_SERVICES` and 6 | `EFI_RUNTIME_SERVICES`. 7 | 8 | In the `uefi` crate, these tables are modeled as modules rather than structs. The 9 | functions in each module make use of a global pointer to the system table that 10 | is set automatically by the [`entry`] macro. 11 | 12 | * [`uefi::system`] (`EFI_SYSTEM_TABLE` in the specification) provides access to 13 | system information such as the firmware vendor and version. It can also be used 14 | to access stdout/stderr/stdin. 15 | 16 | * [`uefi::boot`] (`EFI_BOOT_SERVICES` in the specification) provides access to a 17 | wide array of services such as memory allocation, executable loading, and 18 | optional extension interfaces called protocols. Functions in this module can 19 | only be used while in the Boot Services stage. After [`exit_boot_services`] has 20 | been called, these functions will panic. 21 | 22 | * [`uefi::runtime`] (`EFI_RUNTIME_SERVICES` in the specification) provides access 23 | to a fairly limited set of services, including variable storage, system time, 24 | and virtual-memory mapping. Functions in this module are accessible during both 25 | the Boot Services and Runtime stages. 26 | 27 | [`entry`]: https://docs.rs/uefi/latest/uefi/attr.entry.html 28 | [`exit_boot_services`]: https://docs.rs/uefi/latest/uefi/boot/fn.exit_boot_services.html 29 | [`uefi::boot`]: https://docs.rs/uefi/latest/uefi/boot/index.html 30 | [`uefi::runtime`]: https://docs.rs/uefi/latest/uefi/runtime/index.html 31 | [`uefi::system`]: https://docs.rs/uefi/latest/uefi/system/index.html 32 | -------------------------------------------------------------------------------- /uefi/tests/memory_map.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | use uefi::mem::memory_map::*; 4 | 5 | /// This test imitates a kernel that receives the UEFI memory map as boot 6 | /// information. 7 | #[test] 8 | fn parse_boot_information_efi_mmap() { 9 | let desc_size = size_of::(); 10 | let mut mmap_source = [ 11 | MemoryDescriptor { 12 | ty: MemoryType::CONVENTIONAL, 13 | phys_start: 0x3000, 14 | virt_start: 0x3000, 15 | page_count: 1, 16 | att: MemoryAttribute::WRITE_BACK, 17 | }, 18 | MemoryDescriptor { 19 | ty: MemoryType::CONVENTIONAL, 20 | phys_start: 0x2000, 21 | virt_start: 0x2000, 22 | page_count: 1, 23 | att: MemoryAttribute::WRITE_BACK, 24 | }, 25 | MemoryDescriptor { 26 | ty: MemoryType::CONVENTIONAL, 27 | phys_start: 0x1000, 28 | virt_start: 0x1000, 29 | page_count: 1, 30 | att: MemoryAttribute::WRITE_BACK, 31 | }, 32 | ]; 33 | let map_size = mmap_source.len() * desc_size; 34 | let meta = MemoryMapMeta { 35 | map_size, 36 | desc_size, 37 | map_key: Default::default(), 38 | desc_version: MemoryDescriptor::VERSION, 39 | }; 40 | let mmap = 41 | unsafe { core::slice::from_raw_parts_mut(mmap_source.as_mut_ptr().cast::(), map_size) }; 42 | 43 | // BOOT INFORMATION END 44 | // 45 | // BEGIN PARSING 46 | // This scenario is similar to what a kernel parsing a boot information 47 | // would do. 48 | 49 | let mmap = MemoryMapRefMut::new(mmap, meta).unwrap(); 50 | assert_eq!(mmap.entries().copied().collect::>(), mmap_source); 51 | } 52 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "nixpkgs": { 4 | "locked": { 5 | "lastModified": 1743814133, 6 | "narHash": "sha256-drDyYyUmjeYGiHmwB9eOPTQRjmrq3Yz26knwmMPLZFk=", 7 | "owner": "nixos", 8 | "repo": "nixpkgs", 9 | "rev": "250b695f41e0e2f5afbf15c6b12480de1fe0001b", 10 | "type": "github" 11 | }, 12 | "original": { 13 | "owner": "nixos", 14 | "ref": "nixpkgs-unstable", 15 | "repo": "nixpkgs", 16 | "type": "github" 17 | } 18 | }, 19 | "nixpkgs_2": { 20 | "locked": { 21 | "lastModified": 1736320768, 22 | "narHash": "sha256-nIYdTAiKIGnFNugbomgBJR+Xv5F1ZQU+HfaBqJKroC0=", 23 | "owner": "NixOS", 24 | "repo": "nixpkgs", 25 | "rev": "4bc9c909d9ac828a039f288cf872d16d38185db8", 26 | "type": "github" 27 | }, 28 | "original": { 29 | "owner": "NixOS", 30 | "ref": "nixpkgs-unstable", 31 | "repo": "nixpkgs", 32 | "type": "github" 33 | } 34 | }, 35 | "root": { 36 | "inputs": { 37 | "nixpkgs": "nixpkgs", 38 | "rust-overlay": "rust-overlay" 39 | } 40 | }, 41 | "rust-overlay": { 42 | "inputs": { 43 | "nixpkgs": "nixpkgs_2" 44 | }, 45 | "locked": { 46 | "lastModified": 1743906877, 47 | "narHash": "sha256-Thah1oU8Vy0gs9bh5QhNcQh1iuQiowMnZPbrkURonZA=", 48 | "owner": "oxalica", 49 | "repo": "rust-overlay", 50 | "rev": "9d00c6b69408dd40d067603012938d9fbe95cfcd", 51 | "type": "github" 52 | }, 53 | "original": { 54 | "owner": "oxalica", 55 | "repo": "rust-overlay", 56 | "type": "github" 57 | } 58 | } 59 | }, 60 | "root": "root", 61 | "version": 7 62 | } 63 | -------------------------------------------------------------------------------- /.github/workflows/developer_productivity.yml: -------------------------------------------------------------------------------- 1 | name: Developer Productivity 2 | on: 3 | merge_group: 4 | push: 5 | pull_request: 6 | jobs: 7 | # Job to run change detection for Nix-related files 8 | changes: 9 | runs-on: ubuntu-latest 10 | # Set job outputs to values from filter step. 11 | outputs: 12 | nix-src: ${{ steps.filter.outputs.nix-src }} 13 | steps: 14 | - name: Checkout sources 15 | uses: actions/checkout@v6 16 | - uses: dorny/paths-filter@v3 17 | id: filter 18 | with: 19 | filters: | 20 | nix-src: 21 | - 'nix/**' 22 | - 'flake.nix' 23 | - 'flake.lock' 24 | # This is a convenience test to verify that the toolchain provided by 25 | # shell.nix is valid and can build + run the integration test. 26 | # 27 | # It only runs if the "nix-src" output of the "changes" job is true. 28 | nix_shell_toolchain: 29 | name: "Nix shell toolchain: `cargo xtask run` works" 30 | needs: changes 31 | if: ${{ needs.changes.outputs.nix-src == 'true' }} 32 | runs-on: ubuntu-latest 33 | steps: 34 | - name: Checkout sources 35 | uses: actions/checkout@v6 36 | - uses: Swatinem/rust-cache@v2 37 | - uses: cachix/install-nix-action@v31 38 | # Dedicated step to separate all the 39 | # "copying path '/nix/store/...' from 'https://cache.nixos.org'." 40 | # messages from the actual build output. 41 | - name: Prepare Nix Store 42 | run: nix develop --command bash -c "cargo --version" 43 | - name: Run VM tests 44 | run: | 45 | COMMAND="cargo xtask run --target x86_64 --headless --ci --tpm=v1" 46 | echo "Executing in Nix shell: $COMMAND" 47 | nix develop --command bash -c "$COMMAND" 48 | -------------------------------------------------------------------------------- /book/src/concepts/handles_and_protocols.md: -------------------------------------------------------------------------------- 1 | # Handles and Protocols 2 | 3 | Handles and protocols are at the core of what makes UEFI 4 | extensible. Together they are the mechanism by which UEFI can adapt to a 5 | wide array of hardware and boot conditions, while still providing a 6 | consistent interface to drivers and applications. 7 | 8 | ### Handles 9 | 10 | Handles represent resources. A resource might be a physical device such 11 | as a disk drive or USB device, or something less tangible like a loaded 12 | executable. 13 | 14 | A [Handle] is an opaque pointer, so you can't do anything with it 15 | directly. To operate on a handle you have to open a protocol. 16 | 17 | ### Protocols 18 | 19 | Protocols are interfaces that provide functions to interact with a 20 | resource. For example, the [BlockIO] protocol provides functions to read 21 | and write to block IO devices. 22 | 23 | Protocols are only available during the Boot Services [stage]; you can't 24 | access them during the Runtime stage. 25 | 26 | The UEFI Specification defines a very large number of protocols. Because 27 | protocols are inherently very diverse, the best place to learn about 28 | individual protocols is the [UEFI Specification]. There are many 29 | chapters covering various protocols. Not all of these protocols are 30 | wrapped by `uefi-rs` yet (contributions welcome!) but many of the most 31 | commonly useful ones are. 32 | 33 | See the [Using Protocols] how-to for details of the `uefi-rs` API for 34 | interacting with protocols. 35 | 36 | [UEFI Specification]: https://uefi.org/specifications 37 | [stage]: boot_stages.md 38 | [Handle]: https://docs.rs/uefi/latest/uefi/data_types/struct.Handle.html 39 | [BlockIO]: https://docs.rs/uefi/latest/uefi/proto/media/block/struct.BlockIO.html 40 | [Using Protocols]: ../how_to/protocols.md 41 | -------------------------------------------------------------------------------- /uefi/src/proto/hii/database.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | //! HII Database protocol. 4 | 5 | use alloc::boxed::Box; 6 | use uefi_macros::unsafe_protocol; 7 | use uefi_raw::protocol::hii::database::HiiDatabaseProtocol; 8 | 9 | use crate::mem::make_boxed; 10 | use crate::{Error, StatusExt}; 11 | 12 | /// The HII Configuration Access Protocol. 13 | /// 14 | /// This protocol grants access to the HII database definition available in every UEFI firmware. 15 | /// This database contains internationalized strings, as well as a description of all 16 | /// supported BIOS settings, together with their logic (e.g.: option A blocks option B if value is `true`). 17 | /// 18 | /// # UEFI Spec Description 19 | /// 20 | /// Database manager for HII-related data structures. 21 | #[derive(Debug)] 22 | #[repr(transparent)] 23 | #[unsafe_protocol(HiiDatabaseProtocol::GUID)] 24 | pub struct HiiDatabase(HiiDatabaseProtocol); 25 | 26 | impl HiiDatabase { 27 | /// Export all package lists as raw byte buffer. 28 | pub fn export_all_raw(&self) -> crate::Result> { 29 | fn fetch_data_fn<'a>( 30 | proto: &HiiDatabase, 31 | buf: &'a mut [u8], 32 | ) -> Result<&'a mut [u8], Error>> { 33 | unsafe { 34 | let mut size = buf.len(); 35 | let status = { 36 | (proto.0.export_package_lists)( 37 | &proto.0, 38 | core::ptr::null_mut(), 39 | &mut size, 40 | buf.as_mut_ptr().cast(), 41 | ) 42 | }; 43 | status.to_result_with_err(|_| Some(size)).map(|_| buf) 44 | } 45 | } 46 | 47 | let buf = make_boxed::<[u8], _>(|buf| fetch_data_fn(self, buf))?; 48 | 49 | Ok(buf) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /uefi-test-runner/examples/timestamp.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | // ANCHOR: all 4 | // ANCHOR: features 5 | #![no_main] 6 | #![no_std] 7 | // ANCHOR_END: features 8 | 9 | extern crate alloc; 10 | 11 | // ANCHOR: use 12 | use core::time::Duration; 13 | use log::{info, warn}; 14 | use uefi::boot; 15 | use uefi::prelude::*; 16 | use uefi::proto::misc::Timestamp; 17 | // ANCHOR_END: use 18 | 19 | // ANCHOR: entry 20 | #[entry] 21 | fn main() -> Status { 22 | // ANCHOR_END: entry 23 | // ANCHOR: services 24 | uefi::helpers::init().unwrap(); 25 | // ANCHOR_END: services 26 | 27 | // ANCHOR: params 28 | test_timestamp(); 29 | // ANCHOR_END: params 30 | 31 | // ANCHOR: stall 32 | boot::stall(Duration::from_secs(10)); 33 | // ANCHOR_END: stall 34 | 35 | // ANCHOR: return 36 | Status::SUCCESS 37 | } 38 | // ANCHOR_END: return 39 | 40 | // ANCHOR: test_timestamp 41 | pub fn test_timestamp() { 42 | // ANCHOR_END: test_timestamp 43 | info!("Running loaded Timestamp Protocol test"); 44 | 45 | let handle = boot::get_handle_for_protocol::(); 46 | 47 | match handle { 48 | Ok(handle) => { 49 | let timestamp_proto = 50 | boot::open_protocol_exclusive::(handle) 51 | .expect("Founded Timestamp Protocol but open failed"); 52 | // ANCHOR: text 53 | let timestamp = timestamp_proto.get_timestamp(); 54 | info!("Timestamp Protocol's timestamp: {timestamp:?}"); 55 | 56 | let properties = timestamp_proto.get_properties(); 57 | info!("Timestamp Protocol's properties: {properties:?}"); 58 | // ANCHOR_END: text 59 | } 60 | Err(err) => { 61 | warn!("Failed to found Timestamp Protocol: {err}"); 62 | } 63 | } 64 | } 65 | // ANCHOR_END: all 66 | -------------------------------------------------------------------------------- /uefi/README.md: -------------------------------------------------------------------------------- 1 | # `uefi` 2 | 3 | Rusty wrapper for the [Unified Extensible Firmware Interface][UEFI]. 4 | 5 | This crate makes it easy to develop Rust software that leverages **safe**, 6 | **convenient**, and **performant** abstractions for [UEFI] functionality. 7 | 8 | [![Crates.io](https://img.shields.io/crates/v/uefi)](https://crates.io/crates/uefi) 9 | [![Docs.rs](https://docs.rs/uefi/badge.svg)](https://docs.rs/uefi) 10 | ![License](https://img.shields.io/github/license/rust-osdev/uefi-rs) 11 | ![Build status](https://github.com/rust-osdev/uefi-rs/workflows/Rust/badge.svg) 12 | ![Stars](https://img.shields.io/github/stars/rust-osdev/uefi-rs) 13 | 14 | ## Value-add and Use Cases 15 | 16 | `uefi` supports writing code for both pre- and post-exit boot services 17 | epochs, but its true strength shines when you create UEFI images that heavily 18 | interact with UEFI boot services. Still, you have the flexibility to just 19 | integrate selected types and abstractions into your project, for example to 20 | parse the UEFI memory map. 21 | 22 | _Note that for producing UEFI images, you also need to use a corresponding 23 | `uefi` compiler target of Rust, such as `x86_64-unknown-uefi`._ 24 | 25 | ## API and User Documentation 26 | 27 | 28 | Please refer to [docs.rs](https://docs.rs/uefi) for comprehensive documentation 29 | of the **latest stable release**. The latest not necessarily yet published 30 | documentation can be found in [`src/lib.rs`](./src/lib.rs), which can also be 31 | locally viewed by running `$ cargo xtask doc --open`. 32 | 33 | For an introduction to the `uefi-rs` project and this repository, please refer 34 | to our main [README](https://github.com/rust-osdev/uefi-rs/blob/main/README.md). 35 | 37 | 38 | 39 | [UEFI]: https://en.wikipedia.org/wiki/Unified_Extensible_Firmware_Interface 40 | -------------------------------------------------------------------------------- /uefi-test-runner/examples/loaded_image.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | // ANCHOR: all 4 | #![no_main] 5 | #![no_std] 6 | 7 | use core::time::Duration; 8 | use log::info; 9 | use uefi::boot::{self, SearchType}; 10 | use uefi::prelude::*; 11 | use uefi::proto::device_path::text::{ 12 | AllowShortcuts, DevicePathToText, DisplayOnly, 13 | }; 14 | use uefi::proto::loaded_image::LoadedImage; 15 | use uefi::{Identify, Result}; 16 | 17 | // ANCHOR: main 18 | #[entry] 19 | fn main() -> Status { 20 | uefi::helpers::init().unwrap(); 21 | 22 | print_image_path().unwrap(); 23 | 24 | boot::stall(Duration::from_secs(10)); 25 | Status::SUCCESS 26 | } 27 | // ANCHOR_END: main 28 | 29 | // ANCHOR: print_image_path 30 | fn print_image_path() -> Result { 31 | // ANCHOR_END: print_image_path 32 | // ANCHOR: loaded_image 33 | let loaded_image = 34 | boot::open_protocol_exclusive::(boot::image_handle())?; 35 | // ANCHOR_END: loaded_image 36 | 37 | // ANCHOR: device_path 38 | let device_path_to_text_handle = *boot::locate_handle_buffer( 39 | SearchType::ByProtocol(&DevicePathToText::GUID), 40 | )? 41 | .first() 42 | .expect("DevicePathToText is missing"); 43 | 44 | let device_path_to_text = boot::open_protocol_exclusive::( 45 | device_path_to_text_handle, 46 | )?; 47 | // ANCHOR_END: device_path 48 | 49 | // ANCHOR: text 50 | let image_device_path = 51 | loaded_image.file_path().expect("File path is not set"); 52 | let image_device_path_text = device_path_to_text 53 | .convert_device_path_to_text( 54 | image_device_path, 55 | DisplayOnly(true), 56 | AllowShortcuts(false), 57 | ) 58 | .expect("convert_device_path_to_text failed"); 59 | 60 | info!("Image path: {}", &*image_device_path_text); 61 | Ok(()) 62 | } 63 | // ANCHOR_END: text 64 | // ANCHOR_END: all 65 | -------------------------------------------------------------------------------- /uefi-raw/src/protocol/hii/string.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | //! Bindings for HII String protocols and data types 4 | 5 | use super::font::FontInfo; 6 | use super::{HiiHandle, StringId}; 7 | use crate::{Char8, Char16, Guid, Status, guid}; 8 | 9 | /// EFI_HII_STRING_PROTOCOL 10 | #[derive(Debug)] 11 | #[repr(C)] 12 | pub struct HiiStringProtocol { 13 | pub new_string: unsafe extern "efiapi" fn( 14 | this: *const Self, 15 | package_list: HiiHandle, 16 | string_id: *mut StringId, 17 | language: *const Char8, 18 | language_name: *const Char16, 19 | string: *const Char16, 20 | string_font_info: *const FontInfo, 21 | ) -> Status, 22 | pub get_string: unsafe extern "efiapi" fn( 23 | this: *const Self, 24 | language: *const Char8, 25 | package_list: HiiHandle, 26 | string_id: StringId, 27 | string: *mut *mut Char16, 28 | string_size: *mut usize, 29 | string_font_info: *mut *mut FontInfo, 30 | ) -> Status, 31 | pub set_string: unsafe extern "efiapi" fn( 32 | this: *const Self, 33 | package_list: HiiHandle, 34 | string_id: StringId, 35 | language: *const Char8, 36 | string: *const Char16, 37 | string_font_info: *const FontInfo, 38 | ) -> Status, 39 | pub get_languages: unsafe extern "efiapi" fn( 40 | this: *const Self, 41 | package_list: HiiHandle, 42 | languages: *mut Char8, 43 | languages_size: *mut usize, 44 | ) -> Status, 45 | pub get_secondary_languages: unsafe extern "efiapi" fn( 46 | this: *const Self, 47 | package_list: HiiHandle, 48 | primary_language: *const Char8, 49 | secondary_languages: *mut Char8, 50 | secondary_languages_size: *mut usize, 51 | ) -> Status, 52 | } 53 | 54 | impl HiiStringProtocol { 55 | pub const GUID: Guid = guid!("0fd96974-23aa-4cdc-b9cb-98d17750322a"); 56 | } 57 | -------------------------------------------------------------------------------- /book/src/concepts/variables.md: -------------------------------------------------------------------------------- 1 | # Variables 2 | 3 | UEFI provides fairly flexible key/value variable storage. 4 | 5 | Each variable is identified by a key consisting of a UCS-2 6 | null-terminated name plus a vendor [GUID]. The vendor GUID serves as a 7 | namespace for variables so that different vendors don't accidentally 8 | overwrite or misinterpret another vendor's variable if they happen to 9 | have the same name. 10 | 11 | The data stored in each variable is an arbitrary byte array. 12 | 13 | ## Attributes 14 | 15 | Each variable has attributes (represented as bit flags) associated with 16 | it that affect how it is stored and how it can be accessed. 17 | 18 | If the `BOOTSERVICE_ACCESS` and `RUNTIME_ACCESS` bits are set, the 19 | variable can be accessed during both the Boot Services and Runtime 20 | [stages]. If only `BOOTSERVICE_ACCESS` is set then the variable can 21 | neither be read nor written to after exiting boot services. 22 | 23 | Another important attribute is the `NON_VOLATILE` bit. If this bit is 24 | _not_ set, the variable will be stored in normal memory and will not 25 | persist across a power cycle. If this bit _is_ set, the variable will be 26 | stored in special non-volatile memory. You should be careful about 27 | writing variables of this type, because the non-volatile storage can be 28 | very limited in size. There have been cases where a vendor's poor UEFI 29 | implementation caused the machine not too boot once the storage became 30 | too full. Even figuring out how much space is in use can be tricky due 31 | to deletion being implemented via garbage collection. Matthew Garret's 32 | article ["Dealing with UEFI non-volatile memory quirks"] has more details. 33 | 34 | Most of the other attributes relate to authenticated variables, which 35 | can be used to prevent changes to a variable by unauthorized programs. 36 | 37 | [GUID]: guid.md 38 | [stages]: boot_stages.md 39 | ["Dealing with UEFI non-volatile memory quirks"]: https://mjg59.dreamwidth.org/25091.html 40 | -------------------------------------------------------------------------------- /uefi-raw/src/protocol/media.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | use crate::protocol::device_path::DevicePathProtocol; 4 | use crate::{Boolean, Guid, Status, guid}; 5 | use core::ffi::c_void; 6 | 7 | #[derive(Debug)] 8 | #[repr(C)] 9 | pub struct LoadFileProtocol { 10 | pub load_file: unsafe extern "efiapi" fn( 11 | this: *mut Self, 12 | file_path: *const DevicePathProtocol, 13 | boot_policy: Boolean, 14 | buffer_size: *mut usize, 15 | buffer: *mut c_void, 16 | ) -> Status, 17 | } 18 | 19 | impl LoadFileProtocol { 20 | pub const GUID: Guid = guid!("56ec3091-954c-11d2-8e3f-00a0c969723b"); 21 | } 22 | 23 | #[derive(Debug)] 24 | #[repr(C)] 25 | pub struct LoadFile2Protocol { 26 | pub load_file: unsafe extern "efiapi" fn( 27 | this: *mut Self, 28 | file_path: *const DevicePathProtocol, 29 | boot_policy: Boolean, 30 | buffer_size: *mut usize, 31 | buffer: *mut c_void, 32 | ) -> Status, 33 | } 34 | 35 | impl LoadFile2Protocol { 36 | pub const GUID: Guid = guid!("4006c0c1-fcb3-403e-996d-4a6c8724e06d"); 37 | } 38 | 39 | #[derive(Debug)] 40 | #[repr(C)] 41 | pub struct StorageSecurityCommandProtocol { 42 | pub receive_data: unsafe extern "efiapi" fn( 43 | this: *mut Self, 44 | media_id: u32, 45 | timeout: u64, 46 | security_protocol: u8, 47 | security_protocol_specific_data: u16, 48 | buffer_size: usize, 49 | buffer: *mut c_void, 50 | transfer_size: *mut usize, 51 | ) -> Status, 52 | 53 | pub send_data: unsafe extern "efiapi" fn( 54 | this: *mut Self, 55 | media_id: u32, 56 | timeout: u64, 57 | security_protocol: u8, 58 | security_protocol_specific_data: u16, 59 | buffer_size: usize, 60 | buffer: *const c_void, 61 | ) -> Status, 62 | } 63 | 64 | impl StorageSecurityCommandProtocol { 65 | pub const GUID: Guid = guid!("c88b0b6d-0dfc-49a7-9cb4-49074b4c3a78"); 66 | } 67 | -------------------------------------------------------------------------------- /uefi/src/fs/path/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | //! This module offers the [`Path`] and [`PathBuf`] abstractions. 4 | //! 5 | //! # Interoperability with Rust strings 6 | //! 7 | //! For the interoperability with Rust strings, i.e., `String` and `str` from 8 | //! the standard library, the API is intended to transform these types first to 9 | //! `CString16` respectively `CStr16`. They do not directly translate to 10 | //! [`Path`] and [`PathBuf`]. 11 | //! 12 | //! # Path Structure 13 | //! 14 | //! Paths use the [`SEPARATOR`] character as separator. Paths are absolute and 15 | //! do not contain `.` or `..` components. However, this can be implemented in 16 | //! the future. 17 | 18 | mod path; 19 | mod pathbuf; 20 | mod validation; 21 | 22 | pub use path::{Components, Path}; 23 | pub use pathbuf::PathBuf; 24 | 25 | use crate::data_types::chars::NUL_16; 26 | use crate::{CStr16, Char16, cstr16}; 27 | pub use validation::PathError; 28 | pub(super) use validation::validate_path; 29 | 30 | /// The default separator for paths. 31 | pub const SEPARATOR: Char16 = unsafe { Char16::from_u16_unchecked('\\' as u16) }; 32 | 33 | /// Stringified version of [`SEPARATOR`]. 34 | pub const SEPARATOR_STR: &CStr16 = cstr16!("\\"); 35 | 36 | /// Deny list of characters for path components. UEFI supports FAT-like file 37 | /// systems. According to , 38 | /// paths should not contain these symbols. 39 | pub const CHARACTER_DENY_LIST: [Char16; 10] = unsafe { 40 | [ 41 | NUL_16, 42 | Char16::from_u16_unchecked('"' as u16), 43 | Char16::from_u16_unchecked('*' as u16), 44 | Char16::from_u16_unchecked('/' as u16), 45 | Char16::from_u16_unchecked(':' as u16), 46 | Char16::from_u16_unchecked('<' as u16), 47 | Char16::from_u16_unchecked('>' as u16), 48 | Char16::from_u16_unchecked('?' as u16), 49 | SEPARATOR, 50 | Char16::from_u16_unchecked('|' as u16), 51 | ] 52 | }; 53 | -------------------------------------------------------------------------------- /.github/workflows/book.yml: -------------------------------------------------------------------------------- 1 | name: Book 2 | on: 3 | push: 4 | branches: [main] 5 | tags: ['*'] 6 | permissions: 7 | contents: write 8 | # Adapted from: 9 | # https://github.com/rust-lang/mdBook/wiki/Automated-Deployment%3A-GitHub-Actions#github-pages-deploy 10 | jobs: 11 | deploy: 12 | if: github.repository == 'rust-osdev/uefi-rs' 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v6 16 | with: 17 | fetch-depth: 0 18 | - name: Install mdbook 19 | run: | 20 | mkdir mdbook 21 | curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.4.21/mdbook-v0.4.21-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook 22 | echo `pwd`/mdbook >> $GITHUB_PATH 23 | - name: Deploy GitHub Pages 24 | run: | 25 | # Configure git user so that `git commit` works. 26 | git config user.name "Deploy from CI" 27 | git config user.email "" 28 | 29 | # Get the highest `uefi` release tag. 30 | highest_tag="$(git tag --list | grep uefi-v | sort -V | tail -1)" 31 | 32 | # Create a worktree for the tag. 33 | git worktree add wt-tag refs/tags/"${highest_tag}" 34 | 35 | # Create a worktree for the `gh-pages` branch. 36 | git worktree add wt-gh-pages gh-pages 37 | 38 | # Delete the ref to avoid keeping history. 39 | git -C wt-gh-pages update-ref -d refs/heads/gh-pages 40 | 41 | # Build the book for the tag. Don't use `--dest-dir` because it will 42 | # delete the destination directory including the worktree checkout's 43 | # ".git". 44 | mdbook build wt-tag/book 45 | # Copy output to the destination directory. Note the "/." is needed at 46 | # the end of the source path so that hidden files are included. 47 | cp -r wt-tag/book/book/. wt-gh-pages 48 | 49 | # Commit and push. 50 | cd wt-gh-pages 51 | git add . 52 | git commit -m "Deploy $GITHUB_SHA to gh-pages" 53 | git push --force 54 | -------------------------------------------------------------------------------- /uefi/src/proto/boot_policy.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | //! Module for the [`BootPolicy`] helper type. 4 | 5 | use uefi_raw::Boolean; 6 | 7 | /// The UEFI boot policy is a property that influences the behaviour of 8 | /// various UEFI functions that load files (typically UEFI images). 9 | /// 10 | /// This type is not ABI compatible. On the ABI level, this corresponds to 11 | /// a [`Boolean`]. 12 | #[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd)] 13 | pub enum BootPolicy { 14 | /// Indicates that the request originates from the boot manager, and that 15 | /// the boot manager is attempting to load the provided `file_path` as a 16 | /// boot selection. 17 | /// 18 | /// Boot selection refers to what a user has chosen in the (GUI) boot menu. 19 | /// 20 | /// This corresponds to the underlying [`Boolean`] being `true`. 21 | BootSelection, 22 | /// The provided `file_path` must match an exact file to be loaded. 23 | /// 24 | /// This corresponds to the underlying [`Boolean`] being `false`. 25 | #[default] 26 | ExactMatch, 27 | } 28 | 29 | impl From for Boolean { 30 | fn from(value: BootPolicy) -> Self { 31 | match value { 32 | BootPolicy::BootSelection => Self::TRUE, 33 | BootPolicy::ExactMatch => Self::FALSE, 34 | } 35 | } 36 | } 37 | 38 | impl From for BootPolicy { 39 | fn from(value: Boolean) -> Self { 40 | let boolean: bool = value.into(); 41 | match boolean { 42 | true => Self::BootSelection, 43 | false => Self::ExactMatch, 44 | } 45 | } 46 | } 47 | 48 | #[cfg(test)] 49 | mod tests { 50 | use super::*; 51 | 52 | #[test] 53 | fn boot_policy() { 54 | assert_eq!(BootPolicy::from(Boolean::TRUE), BootPolicy::BootSelection); 55 | assert_eq!(BootPolicy::from(Boolean::FALSE), BootPolicy::ExactMatch); 56 | assert_eq!(Boolean::from(BootPolicy::BootSelection), Boolean::TRUE); 57 | assert_eq!(Boolean::from(BootPolicy::ExactMatch), Boolean::FALSE); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /template/README.md: -------------------------------------------------------------------------------- 1 | # UEFI application template 2 | 3 | This directory contains a minimal example of a UEFI application. 4 | Copy it to a new directory to get started. 5 | 6 | Check out the [tutorial] for more instructions on how to build and run a 7 | UEFI application developed using `uefi-rs`. 8 | 9 | ## File structure 10 | 11 | - [`rust-toolchain.toml`](rust-toolchain.toml) adds the UEFI targets. 12 | - [`Cargo.toml`](./Cargo.toml) shows the necessary dependencies. 13 | - [`src/main.rs`](./src/main.rs) has a minimal entry point that 14 | initializes recommended helpers and exits successfully. 15 | 16 | ## Building kernels which use UEFI 17 | 18 | This template makes it easy to start building simple applications with UEFI. 19 | However, there are some limitations you should be aware of: 20 | 21 | - The global logger / allocator **can only be set once** per binary. 22 | It is useful when just starting out, but if you're building a real OS you will 23 | want to write your own specific kernel logger and memory allocator. 24 | 25 | - To support advanced features such as [higher half kernel] and [linker scripts] 26 | you will want to build your kernel as an ELF binary. 27 | 28 | In other words, the best way to use this crate is to create a small binary which 29 | wraps your actual kernel, and then use UEFI's convenient functions for loading 30 | it from disk and booting it. 31 | 32 | This is similar to what the Linux kernel's [EFI stub] does: the compressed kernel 33 | is an ELF binary which has little knowledge of how it's booted, and the boot loader 34 | uses UEFI to set up an environment for it. See the [bootloader] crate for an example 35 | of how `uefi-rs` can be used to construct a first-stage bootloader which then 36 | loads and executes an ELF kernel binary. 37 | 38 | [higher half kernel]: https://wiki.osdev.org/Higher_Half_Kernel 39 | [linker scripts]: https://sourceware.org/binutils/docs/ld/Scripts.html 40 | [EFI stub]: https://www.kernel.org/doc/Documentation/efi-stub.txt 41 | [bootloader]: https://github.com/rust-osdev/bootloader 42 | [tutorial]: https://rust-osdev.github.io/uefi-rs/HEAD/tutorial/introduction.html 43 | -------------------------------------------------------------------------------- /uefi-raw/src/protocol/rng.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | //! `Rng` protocol. 4 | 5 | use crate::{Guid, Status, guid, newtype_enum}; 6 | 7 | newtype_enum! { 8 | /// The algorithms listed are optional, not meant to be exhaustive 9 | /// and may be augmented by vendors or other industry standards. 10 | pub enum RngAlgorithmType: Guid => { 11 | /// Indicates a empty algorithm, used to instantiate a buffer 12 | /// for `get_info` 13 | EMPTY_ALGORITHM = guid!("00000000-0000-0000-0000-000000000000"), 14 | 15 | /// The “raw” algorithm, when supported, is intended to provide 16 | /// entropy directly from the source, without it going through 17 | /// some deterministic random bit generator. 18 | ALGORITHM_RAW = guid!("e43176d7-b6e8-4827-b784-7ffdc4b68561"), 19 | 20 | /// ALGORITHM_SP800_90_HASH_256 21 | ALGORITHM_SP800_90_HASH_256 = guid!("a7af67cb-603b-4d42-ba21-70bfb6293f96"), 22 | 23 | /// ALGORITHM_SP800_90_HMAC_256 24 | ALGORITHM_SP800_90_HMAC_256 = guid!("c5149b43-ae85-4f53-9982-b94335d3a9e7"), 25 | 26 | /// ALGORITHM_SP800_90_CTR_256 27 | ALGORITHM_SP800_90_CTR_256 = guid!("44f0de6e-4d8c-4045-a8c7-4dd168856b9e"), 28 | 29 | /// ALGORITHM_X9_31_3DES 30 | ALGORITHM_X9_31_3DES = guid!("63c4785a-ca34-4012-a3c8-0b6a324f5546"), 31 | 32 | /// ALGORITHM_X9_31_AES 33 | ALGORITHM_X9_31_AES = guid!("acd03321-777e-4d3d-b1c8-20cfd88820c9"), 34 | } 35 | } 36 | 37 | /// Rng protocol. 38 | #[derive(Debug)] 39 | #[repr(C)] 40 | pub struct RngProtocol { 41 | pub get_info: unsafe extern "efiapi" fn( 42 | this: *mut Self, 43 | algorithm_list_size: *mut usize, 44 | algorithm_list: *mut RngAlgorithmType, 45 | ) -> Status, 46 | 47 | pub get_rng: unsafe extern "efiapi" fn( 48 | this: *mut Self, 49 | algorithm: *const RngAlgorithmType, 50 | value_length: usize, 51 | value: *mut u8, 52 | ) -> Status, 53 | } 54 | 55 | impl RngProtocol { 56 | pub const GUID: Guid = guid!("3152bca5-eade-433d-862e-c01cdc291f44"); 57 | } 58 | -------------------------------------------------------------------------------- /uefi/src/proto/rng.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | //! `Rng` protocol. 4 | 5 | use crate::proto::unsafe_protocol; 6 | use crate::{Result, Status, StatusExt}; 7 | use core::ptr; 8 | 9 | pub use uefi_raw::protocol::rng::RngAlgorithmType; 10 | 11 | /// Random Number Generator [`Protocol`] (RNG). 12 | /// 13 | /// [`Protocol`]: uefi::proto::Protocol 14 | #[derive(Debug)] 15 | #[repr(transparent)] 16 | #[unsafe_protocol(uefi_raw::protocol::rng::RngProtocol::GUID)] 17 | pub struct Rng(uefi_raw::protocol::rng::RngProtocol); 18 | 19 | impl Rng { 20 | /// Returns information about the random number generation implementation. 21 | pub fn get_info<'buf>( 22 | &mut self, 23 | algorithm_list: &'buf mut [RngAlgorithmType], 24 | ) -> Result<&'buf [RngAlgorithmType], Option> { 25 | let mut algorithm_list_size = size_of_val(algorithm_list); 26 | 27 | unsafe { 28 | (self.0.get_info)( 29 | &mut self.0, 30 | &mut algorithm_list_size, 31 | algorithm_list.as_mut_ptr(), 32 | ) 33 | .to_result_with( 34 | || { 35 | let len = algorithm_list_size / size_of::(); 36 | &algorithm_list[..len] 37 | }, 38 | |status| { 39 | if status == Status::BUFFER_TOO_SMALL { 40 | Some(algorithm_list_size) 41 | } else { 42 | None 43 | } 44 | }, 45 | ) 46 | } 47 | } 48 | 49 | /// Returns the next set of random numbers 50 | pub fn get_rng(&mut self, algorithm: Option, buffer: &mut [u8]) -> Result { 51 | let buffer_length = buffer.len(); 52 | 53 | let algo = match algorithm.as_ref() { 54 | None => ptr::null(), 55 | Some(algo) => algo as *const RngAlgorithmType, 56 | }; 57 | 58 | unsafe { 59 | (self.0.get_rng)(&mut self.0, algo, buffer_length, buffer.as_mut_ptr()).to_result() 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /uefi-test-runner/src/proto/nvme/pass_thru.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | use core::time::Duration; 4 | use uefi::boot; 5 | use uefi::proto::device_path::DevicePath; 6 | use uefi::proto::device_path::text::{AllowShortcuts, DisplayOnly}; 7 | use uefi::proto::media::block::BlockIO; 8 | use uefi::proto::nvme::pass_thru::NvmePassThru; 9 | use uefi::proto::nvme::{NvmeQueueType, NvmeRequestBuilder}; 10 | 11 | pub fn test() { 12 | info!("Running NVMe PassThru tests"); 13 | 14 | assert!(has_nvme_drive()); 15 | } 16 | 17 | fn has_nvme_drive() -> bool { 18 | let block_io_handles = boot::find_handles::().unwrap(); 19 | for handle in block_io_handles { 20 | let Ok(device_path) = boot::open_protocol_exclusive::(handle) else { 21 | continue; 22 | }; 23 | let mut device_path = &*device_path; 24 | 25 | let Ok(nvme_pt_handle) = boot::locate_device_path::(&mut device_path) else { 26 | continue; 27 | }; 28 | let nvme_pt = boot::open_protocol_exclusive::(nvme_pt_handle).unwrap(); 29 | let device_path_str = device_path 30 | .to_string(DisplayOnly(true), AllowShortcuts(false)) 31 | .unwrap(); 32 | info!("- Successfully opened NVMe: {device_path_str}"); 33 | let mut nvme_ctrl = nvme_pt.controller(); 34 | 35 | let request = NvmeRequestBuilder::new(nvme_pt.io_align(), 0x06, NvmeQueueType::ADMIN) 36 | .with_timeout(Duration::from_millis(500)) 37 | .with_cdw10(1) // we want info about controller 38 | .with_transfer_buffer(4096) 39 | .unwrap() 40 | .build(); 41 | let result = nvme_ctrl.execute_command(request); 42 | if let Ok(result) = result { 43 | let bfr = result.transfer_buffer().unwrap(); 44 | let serial = core::str::from_utf8(&bfr[4..24]).unwrap().trim(); 45 | info!("Found NVMe with serial: '{serial}'"); 46 | if serial == "uefi-rsNvmePassThru" { 47 | return true; 48 | } 49 | } 50 | } 51 | 52 | false 53 | } 54 | -------------------------------------------------------------------------------- /uefi/src/helpers/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | //! This module provides miscellaneous opinionated but optional helpers to 4 | //! better integrate your application with the Rust runtime and the Rust 5 | //! ecosystem. 6 | //! 7 | //! For now, this includes: 8 | //! - using [`uefi::allocator::Allocator`] as global allocator (feature `global_allocator`) 9 | //! - an implementation of [`log::Log`] (feature `logger`) which logs to 10 | //! the stdout text protocol of UEFI (as long as boot services were not 11 | //! excited) and to the [debugcon device](https://phip1611.de/blog/how-to-use-qemus-debugcon-feature/) 12 | //! (only on x86) (feature `log-debugcon`). 13 | //! - [`print!`][print_macro] and [`println!`][println_macro] macros defaulting 14 | //! to the uefi boot service stdout stream 15 | //! - default panic handler (feature `panic_handler`) 16 | //! 17 | //! **PLEASE NOTE** that these helpers are meant for the pre exit boot service 18 | //! epoch. 19 | //! 20 | //! [print_macro]: uefi::print! 21 | //! [println_macro]: uefi::println! 22 | 23 | use crate::Result; 24 | #[doc(hidden)] 25 | pub use println::_print; 26 | 27 | #[cfg(feature = "global_allocator")] 28 | mod global_allocator; 29 | #[cfg(feature = "logger")] 30 | mod logger; 31 | #[cfg(feature = "panic_handler")] 32 | mod panic_handler; 33 | mod println; 34 | 35 | /// Initialize all helpers defined in [`uefi::helpers`] whose Cargo features 36 | /// are activated. 37 | /// 38 | /// This must be called as early as possible, before trying to use logging. 39 | /// 40 | /// **PLEASE NOTE** that these helpers are meant for the pre exit boot service 41 | /// epoch. Limited functionality might work after exiting them, such as logging 42 | /// to the debugcon device. 43 | /// 44 | /// # Panics 45 | /// 46 | /// This function may panic if called more than once. 47 | #[allow(clippy::missing_const_for_fn)] 48 | pub fn init() -> Result<()> { 49 | // Set up logging. 50 | #[cfg(feature = "logger")] 51 | unsafe { 52 | logger::init(); 53 | } 54 | 55 | Ok(()) 56 | } 57 | 58 | #[allow(clippy::missing_const_for_fn)] 59 | pub(crate) fn exit() { 60 | #[cfg(feature = "logger")] 61 | logger::disable(); 62 | } 63 | -------------------------------------------------------------------------------- /book/src/tutorial/vm.md: -------------------------------------------------------------------------------- 1 | # Running in a VM 2 | 3 | ## Install dependencies 4 | 5 | Two dependencies are needed: [QEMU], which implements the virtual 6 | machine itself, and [OVMF], which provides UEFI firmware that QEMU can 7 | run. 8 | 9 | The details of how to install QEMU and OVMF will vary depending on your 10 | operating system. 11 | 12 | Debian/Ubuntu: 13 | ```sh 14 | sudo apt-get install qemu ovmf 15 | ``` 16 | 17 | Fedora: 18 | ```sh 19 | sudo dnf install qemu-kvm edk2-ovmf 20 | ``` 21 | 22 | ### Firmware files 23 | 24 | The OVMF package provides two firmware files, one for the executable 25 | code and one for variable storage. (The package may provide multiple 26 | variations of these files; refer to the package's documentation for 27 | details of the files it includes.) 28 | 29 | For ease of access we'll copy the OVMF code and vars files to the 30 | project directory. The location where OVMF is installed depends on your 31 | operating system; for Debian, Ubuntu and Fedora the files are under 32 | `/usr/share/OVMF`. 33 | 34 | Copy the files to your project directory: 35 | ```sh 36 | cp /usr/share/OVMF/OVMF_CODE.fd . 37 | cp /usr/share/OVMF/OVMF_VARS.fd . 38 | ``` 39 | 40 | ## System partition 41 | 42 | Now create a directory structure containing the executable to imitate a 43 | [UEFI System Partition]: 44 | 45 | ```sh 46 | mkdir -p esp/efi/boot 47 | cp target/x86_64-unknown-uefi/debug/my-uefi-app.efi esp/efi/boot/bootx64.efi 48 | ``` 49 | 50 | ## Launch the VM 51 | 52 | Now we can launch QEMU, using [VVFAT] to access the `esp` directory created above. 53 | 54 | ```sh 55 | qemu-system-x86_64 -enable-kvm \ 56 | -drive if=pflash,format=raw,readonly=on,file=OVMF_CODE.fd \ 57 | -drive if=pflash,format=raw,readonly=on,file=OVMF_VARS.fd \ 58 | -drive format=raw,file=fat:rw:esp 59 | ``` 60 | 61 | A QEMU window should appear, and after a few seconds you should see the 62 | log message: 63 | ```text 64 | [ INFO]: src/main.rs@011: Hello world! 65 | ``` 66 | 67 | [QEMU]: https://www.qemu.org 68 | [OVMF]: https://github.com/tianocore/tianocore.github.io/wiki/OVMF 69 | [VVFAT]: https://en.m.wikibooks.org/wiki/QEMU/Devices/Storage#Virtual_FAT_filesystem_(VVFAT) 70 | [UEFI System Partition]: ../concepts/gpt.md#system-partition 71 | -------------------------------------------------------------------------------- /uefi-raw/src/protocol/network/ip4_config2.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | use crate::protocol::network::ip4::Ip4RouteTable; 4 | use crate::{Char16, Event, Guid, Ipv4Address, MacAddress, Status, guid, newtype_enum}; 5 | use core::ffi::c_void; 6 | 7 | newtype_enum! { 8 | pub enum Ip4Config2DataType: i32 => { 9 | INTERFACE_INFO = 0, 10 | POLICY = 1, 11 | MANUAL_ADDRESS = 2, 12 | GATEWAY = 3, 13 | DNS_SERVER = 4, 14 | MAXIMUM = 5, 15 | } 16 | } 17 | 18 | #[derive(Debug)] 19 | #[repr(C)] 20 | pub struct Ip4Config2InterfaceInfo { 21 | pub name: [Char16; 32], 22 | pub if_type: u8, 23 | pub hw_addr_size: u32, 24 | pub hw_addr: MacAddress, 25 | pub station_addr: Ipv4Address, 26 | pub subnet_mask: Ipv4Address, 27 | pub route_table_size: u32, 28 | pub route_table: *mut Ip4RouteTable, 29 | } 30 | 31 | newtype_enum! { 32 | pub enum Ip4Config2Policy: i32 => { 33 | STATIC = 0, 34 | DHCP = 1, 35 | MAX = 2, 36 | } 37 | } 38 | 39 | #[derive(Debug)] 40 | #[repr(C)] 41 | pub struct Ip4Config2ManualAddress { 42 | pub address: Ipv4Address, 43 | pub subnet_mask: Ipv4Address, 44 | } 45 | 46 | #[derive(Debug)] 47 | #[repr(C)] 48 | pub struct Ip4Config2Protocol { 49 | pub set_data: unsafe extern "efiapi" fn( 50 | this: *mut Self, 51 | data_type: Ip4Config2DataType, 52 | data_size: usize, 53 | data: *const c_void, 54 | ) -> Status, 55 | 56 | pub get_data: unsafe extern "efiapi" fn( 57 | this: *mut Self, 58 | data_type: Ip4Config2DataType, 59 | data_size: *mut usize, 60 | data: *mut c_void, 61 | ) -> Status, 62 | 63 | pub register_data_notify: unsafe extern "efiapi" fn( 64 | this: *mut Self, 65 | data_type: Ip4Config2DataType, 66 | event: Event, 67 | ) -> Status, 68 | 69 | pub unregister_data_notify: unsafe extern "efiapi" fn( 70 | this: *mut Self, 71 | data_type: Ip4Config2DataType, 72 | event: Event, 73 | ) -> Status, 74 | } 75 | 76 | impl Ip4Config2Protocol { 77 | pub const GUID: Guid = guid!("5b446ed1-e30b-4faa-871a-3654eca36080"); 78 | } 79 | -------------------------------------------------------------------------------- /xtask/src/util.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | use anyhow::{Result, bail}; 4 | use std::process::Command; 5 | 6 | /// Format a `Command` as a `String. 7 | /// 8 | /// Example: "VAR=val program --arg1 arg2". 9 | pub fn command_to_string(cmd: &Command) -> String { 10 | // Format env vars as "name=val". 11 | let mut parts = cmd 12 | .get_envs() 13 | // Filter out variables that are set or cleared by 14 | // `fix_nested_cargo_env`, as they would clutter the output. 15 | .filter(|(name, val)| { 16 | *name != "PATH" 17 | // Exclude any variables cleared with `Command::env_remove`. 18 | && val.is_some() 19 | }) 20 | .map(|(name, val)| { 21 | format!( 22 | "{}={}", 23 | name.to_string_lossy(), 24 | val.unwrap_or_default().to_string_lossy() 25 | ) 26 | }) 27 | .collect::>(); 28 | 29 | // Add the program name. 30 | parts.push(cmd.get_program().to_string_lossy().to_string()); 31 | 32 | // Add each argument. 33 | parts.extend(cmd.get_args().map(|arg| arg.to_string_lossy().to_string())); 34 | 35 | // Join the vars, program, and arguments into a single string. 36 | parts.into_iter().collect::>().join(" ") 37 | } 38 | 39 | /// Print a `Command` and run it, then check that it completes 40 | /// successfully. 41 | pub fn run_cmd(mut cmd: Command) -> Result<()> { 42 | println!("run_cmd: '{}'", command_to_string(&cmd)); 43 | 44 | let status = cmd.status()?; 45 | if status.success() { 46 | Ok(()) 47 | } else { 48 | bail!("command failed: {}", status); 49 | } 50 | } 51 | 52 | #[cfg(test)] 53 | mod tests { 54 | use super::*; 55 | 56 | #[test] 57 | fn test_command_to_string() { 58 | let mut cmd = Command::new("MyCommand"); 59 | cmd.args(["abc", "123"]) 60 | .envs([("VAR1", "val1"), ("VAR2", "val2"), ("PATH", "pathval")]) 61 | .env_remove("RUSTC") 62 | .env_remove("CARGO"); 63 | assert_eq!( 64 | command_to_string(&cmd), 65 | "VAR1=val1 VAR2=val2 MyCommand abc 123" 66 | ); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /xtask/src/tpm.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | use crate::opt::TpmVersion; 4 | use crate::util::command_to_string; 5 | use anyhow::Result; 6 | use std::process::{Child, Command}; 7 | use tempfile::TempDir; 8 | 9 | // Adapted from https://qemu.readthedocs.io/en/latest/specs/tpm.html 10 | 11 | /// Wrapper for running `swtpm`, a software TPM emulator. 12 | /// 13 | /// 14 | /// 15 | /// The process is killed on drop. 16 | pub struct Swtpm { 17 | tmp_dir: TempDir, 18 | child: Child, 19 | } 20 | 21 | impl Swtpm { 22 | /// Run `swtpm` in a new process. 23 | pub fn spawn(version: TpmVersion) -> Result { 24 | let tmp_dir = TempDir::new()?; 25 | let tmp_path = tmp_dir.path().to_str().unwrap(); 26 | 27 | let mut cmd = Command::new("swtpm"); 28 | cmd.args([ 29 | "socket", 30 | "--tpmstate", 31 | &format!("dir={tmp_path}"), 32 | "--ctrl", 33 | &format!("type=unixio,path={tmp_path}/swtpm-sock"), 34 | // Terminate when the connection drops. If for any reason 35 | // this fails, the process will be killed on drop. 36 | "--terminate", 37 | // Hide some log spam. 38 | "--log", 39 | "file=-", 40 | ]); 41 | 42 | if version == TpmVersion::V2 { 43 | cmd.arg("--tpm2"); 44 | } 45 | 46 | println!("{}", command_to_string(&cmd)); 47 | let child = cmd.spawn()?; 48 | 49 | Ok(Self { tmp_dir, child }) 50 | } 51 | 52 | /// Get the QEMU args needed to connect to the TPM emulator. 53 | pub fn qemu_args(&self) -> Vec { 54 | let socket_path = self.tmp_dir.path().join("swtpm-sock"); 55 | vec![ 56 | "-chardev".into(), 57 | format!("socket,id=chrtpm0,path={}", socket_path.to_str().unwrap()), 58 | "-tpmdev".into(), 59 | "emulator,id=tpm0,chardev=chrtpm0".into(), 60 | "-device".into(), 61 | "tpm-tis,tpmdev=tpm0".into(), 62 | ] 63 | } 64 | } 65 | 66 | impl Drop for Swtpm { 67 | fn drop(&mut self) { 68 | let _ = self.child.kill(); 69 | let _ = self.child.wait(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /uefi/src/helpers/println.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | use crate::{boot, system}; 4 | use core::fmt::Write; 5 | 6 | /// INTERNAL API! Helper for print macros. 7 | #[doc(hidden)] 8 | pub fn _print(args: core::fmt::Arguments) { 9 | if boot::are_boot_services_active() { 10 | system::with_stdout(|stdout| { 11 | stdout.write_fmt(args).expect("Failed to write to stdout"); 12 | }); 13 | } else { 14 | // Ease debugging: Depending on logger, this might write to serial or 15 | // debugcon. 16 | log::debug!("You are using `print!` after the boot services have been exited."); 17 | } 18 | } 19 | 20 | /// Prints to the standard output of the UEFI boot service console. 21 | /// 22 | /// # Usage 23 | /// Use this similar to `print!` from the Rust standard library, but only 24 | /// as long as boot services have not been exited. 25 | /// 26 | /// You should never use this macro in a custom Logger ([`log::Log`] impl) to 27 | /// prevent a circular runtime dependency. 28 | /// 29 | /// # Panics 30 | /// Will panic if the system table's `stdout` is not set, or if writing fails. 31 | /// 32 | /// # Examples 33 | /// ``` 34 | /// print!(""); 35 | /// print!("Hello World\n"); 36 | /// print!("Hello {}", "World"); 37 | /// ``` 38 | #[macro_export] 39 | macro_rules! print { 40 | ($($arg:tt)*) => ($crate::helpers::_print(core::format_args!($($arg)*))); 41 | } 42 | 43 | /// Prints to the standard output of the UEFI boot service console, but with a 44 | /// newline. 45 | /// 46 | /// # Usage 47 | /// Use this similar to `println!` from the Rust standard library, but only 48 | /// as long as boot services have not been exited. 49 | /// 50 | /// You should never use this macro in a custom Logger ([`log::Log`] impl) to 51 | /// prevent a circular runtime dependency. 52 | /// 53 | /// # Panics 54 | /// Will panic if the system table's `stdout` is not set, or if writing fails. 55 | /// 56 | /// # Examples 57 | /// ``` 58 | /// println!(); 59 | /// println!("Hello World"); 60 | /// println!("Hello {}", "World"); 61 | /// ``` 62 | #[macro_export] 63 | macro_rules! println { 64 | () => ($crate::print!("\n")); 65 | ($($arg:tt)*) => ($crate::helpers::_print(core::format_args!("{}{}", core::format_args!($($arg)*), "\n"))); 66 | } 67 | -------------------------------------------------------------------------------- /uefi/src/proto/media/fs.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | //! File system support protocols. 4 | 5 | use super::file::{Directory, FileHandle}; 6 | use crate::proto::unsafe_protocol; 7 | use crate::{Result, StatusExt}; 8 | use core::ptr; 9 | use uefi_raw::protocol::file_system::SimpleFileSystemProtocol; 10 | 11 | #[cfg(doc)] 12 | use crate::Status; 13 | 14 | /// Simple File System [`Protocol`]. Allows access to a FAT-12/16/32 file 15 | /// system. 16 | /// 17 | /// This interface is implemented by some storage devices 18 | /// to allow file access to the contained file systems. 19 | /// 20 | /// # Accessing `SimpleFileSystem` protocol 21 | /// 22 | /// Use [`boot::get_image_file_system`] to retrieve the `SimpleFileSystem` 23 | /// protocol associated with a given image handle. 24 | /// 25 | /// See the [`boot`] documentation for more details of how to open a protocol. 26 | /// 27 | /// [`boot::get_image_file_system`]: crate::boot::get_image_file_system 28 | /// [`boot`]: crate::boot#accessing-protocols 29 | /// [`Protocol`]: uefi::proto::Protocol 30 | #[derive(Debug)] 31 | #[repr(transparent)] 32 | #[unsafe_protocol(SimpleFileSystemProtocol::GUID)] 33 | pub struct SimpleFileSystem(SimpleFileSystemProtocol); 34 | 35 | impl SimpleFileSystem { 36 | /// Open the root directory on a volume. 37 | /// 38 | /// # Errors 39 | /// 40 | /// See section `EFI_SIMPLE_FILE_SYSTEM_PROTOCOL.OpenVolume()` in the UEFI Specification 41 | /// for more details. 42 | /// 43 | /// If you can't find the function definition, try searching for 44 | /// `EFI_SIMPLE_FILE SYSTEM_PROTOCOL.OpenVolume()` (this has a space in between FILE and 45 | /// SYSTEM; it could be a typo in the UEFI spec). 46 | /// 47 | /// * [`Status::UNSUPPORTED`] 48 | /// * [`Status::NO_MEDIA`] 49 | /// * [`Status::DEVICE_ERROR`] 50 | /// * [`Status::VOLUME_CORRUPTED`] 51 | /// * [`Status::ACCESS_DENIED`] 52 | /// * [`Status::OUT_OF_RESOURCES`] 53 | /// * [`Status::MEDIA_CHANGED`] 54 | pub fn open_volume(&mut self) -> Result { 55 | let mut ptr = ptr::null_mut(); 56 | unsafe { (self.0.open_volume)(&mut self.0, &mut ptr) } 57 | .to_result_with_val(|| unsafe { Directory::new(FileHandle::new(ptr.cast())) }) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /book/src/concepts/gpt.md: -------------------------------------------------------------------------------- 1 | # GPT 2 | 3 | [GPT] is short for [GUID] Partition Table. It's a more modern 4 | alternative to MBR (master boot record) partition tables. Although it's 5 | defined in the UEFI specification, it often gets used on non-UEFI 6 | systems too. There are a couple big advantages of using GPT over MBR: 7 | - It has a relatively clear and precise standard, unlike MBR where 8 | implementations often just try to match what other implementations do. 9 | - It supports very large disks and very large numbers of partitions. 10 | 11 | A GPT disk contains a primary header near the beginning of the disk, 12 | followed by a partition entry array. The header and partition entry 13 | array have a secondary copy at the end of the disk for redundancy. The 14 | partition entry arrays contain structures that describe each partition, 15 | including a GUID to identify the individual partition, a partition type 16 | GUID to indicate the purpose of the partition, and start/end block 17 | addresses. In between the entry arrays is the actual partition data. 18 | 19 | ## System partition 20 | 21 | The system partition is UEFI's version of a bootable partition. The 22 | system partition is sometimes called the ESP, or EFI System 23 | Partition. It is identified by a partition type of 24 | `c12a7328-f81f-11d2-ba4b-00a0c93ec93b`. The system partition always 25 | contains a FAT file system. There are various standardized paths that 26 | can exist within the file system, and of particular importance are the 27 | boot files. These are the files that UEFI will try to boot from by 28 | default (in the absence of a different boot configuration set through 29 | special [UEFI variables]). 30 | 31 | Boot files are under `\EFI\BOOT`, and are named `BOOT.efi`, where 32 | `` is a short architecture name. 33 | 34 | |Architecture |File name | 35 | |--------------|----------------| 36 | |Intel 32-bit |BOOTIA32.EFI | 37 | |X86_64 |BOOTX64.EFI | 38 | |Itanium |BOOTIA64.EFI | 39 | |AArch32 |BOOTARM.EFI | 40 | |AArch64 |BOOTAA64.EFI | 41 | |RISC-V 32-bit |BOOTRISCV32.EFI | 42 | |RISC-V 64-bit |BOOTRISCV64.EFI | 43 | |RISC-V 128-bit|BOOTRISCV128.EFI| 44 | 45 | [GPT]: https://en.wikipedia.org/wiki/GUID_Partition_Table 46 | [GUID]: guid.md 47 | [UEFI variables]: variables.md 48 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "uefi-rs"; 3 | 4 | inputs = { 5 | # Use freshest nixpkgs. We don't use master as packages in nixpkgs-unstable 6 | # have already been build by the nixpkgs CI and are available from the 7 | # nixos.org cache. 8 | nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; 9 | rust-overlay.url = "github:oxalica/rust-overlay"; 10 | }; 11 | 12 | outputs = 13 | inputs@{ self, nixpkgs, ... }: 14 | let 15 | # Systems definition for dev shells and exported packages, 16 | # independent of the NixOS configurations and modules defined here. We 17 | # just use "every system" here to not restrict any user. However, it 18 | # likely happens that certain packages don't build for/under certain 19 | # systems. 20 | systems = nixpkgs.lib.systems.flakeExposed; 21 | forAllSystems = 22 | function: nixpkgs.lib.genAttrs systems (system: function nixpkgs.legacyPackages.${system}); 23 | 24 | # We directly instantiate the functionality, without using an 25 | # nixpkgs overlay. 26 | # https://github.com/oxalica/rust-overlay/blob/f4d5a693c18b389f0d58f55b6f7be6ef85af186f/docs/reference.md?plain=1#L26 27 | rustToolchain = 28 | pkgs: 29 | let 30 | rust-bin = (inputs.rust-overlay.lib.mkRustBin { }) pkgs; 31 | rustToolchainBuilder = import ./nix/rust-toolchain.nix; 32 | in 33 | rustToolchainBuilder { inherit rust-bin; }; 34 | in 35 | { 36 | devShells = forAllSystems (pkgs: { 37 | default = pkgs.mkShell { 38 | packages = with pkgs; [ 39 | nixfmt-rfc-style 40 | 41 | # Integration test dependencies 42 | swtpm 43 | qemu 44 | 45 | # Rust toolchain 46 | (rustToolchain pkgs) 47 | 48 | # Other 49 | cargo-llvm-cov 50 | mdbook 51 | yamlfmt 52 | which # used by "cargo xtask fmt" 53 | ]; 54 | 55 | # Set ENV vars. 56 | # OVMF_CODE="${pkgs.OVMF.firmware}"; 57 | # OVMF_VARS="${pkgs.OVMF.variables}"; 58 | # OVMF_SHELL="${pkgs.edk2-uefi-shell}"; 59 | }; 60 | }); 61 | formatter = forAllSystems (pkgs: pkgs.nixfmt-rfc-style); 62 | }; 63 | } 64 | -------------------------------------------------------------------------------- /uefi/src/result/error.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | //! Module for UEFI-specific error encodings. See [`Error`]. 4 | 5 | use super::Status; 6 | use core::fmt::{Debug, Display}; 7 | 8 | /// An UEFI-related error with optionally additional payload data. The error 9 | /// kind is encoded in the `status` field (see [`Status`]). Additional payload 10 | /// may be inside the `data` field. 11 | #[derive(Clone, Debug, PartialEq, Eq)] 12 | pub struct Error { 13 | status: Status, 14 | data: Data, 15 | } 16 | 17 | impl Error { 18 | /// Create an `Error`. 19 | /// 20 | /// # Panics 21 | /// 22 | /// Panics if `status` is [`Status::SUCCESS`]. 23 | pub const fn new(status: Status, data: Data) -> Self { 24 | assert!(!matches!(status, Status::SUCCESS)); 25 | Self { status, data } 26 | } 27 | 28 | /// Get error `Status`. 29 | pub const fn status(&self) -> Status { 30 | self.status 31 | } 32 | 33 | /// Get error data. 34 | pub const fn data(&self) -> &Data { 35 | &self.data 36 | } 37 | 38 | /// Split this error into its inner status and error data 39 | #[allow(clippy::missing_const_for_fn)] 40 | pub fn split(self) -> (Status, Data) { 41 | (self.status, self.data) 42 | } 43 | } 44 | 45 | // Errors without error data can be autogenerated from statuses 46 | 47 | impl From for Error<()> { 48 | fn from(status: Status) -> Self { 49 | Self::new(status, ()) 50 | } 51 | } 52 | 53 | impl Display for Error { 54 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 55 | write!(f, "UEFI Error {}: {:?}", self.status(), self.data()) 56 | } 57 | } 58 | 59 | impl Error { 60 | /// Transforms the generic payload of an error to `()`. This is useful if 61 | /// you want 62 | /// - to retain the erroneous status code, 63 | /// - do not care about the payload, and 64 | /// - refrain from generic type complexity in a higher API level. 65 | pub const fn to_err_without_payload(&self) -> Error<()> { 66 | Error { 67 | status: self.status, 68 | data: (), 69 | } 70 | } 71 | } 72 | 73 | impl core::error::Error for Error {} 74 | -------------------------------------------------------------------------------- /uefi-test-runner/src/proto/ata/pass_thru.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | use uefi::boot; 4 | use uefi::boot::{OpenProtocolAttributes, OpenProtocolParams}; 5 | use uefi::proto::ata::AtaRequestBuilder; 6 | use uefi::proto::ata::pass_thru::AtaPassThru; 7 | 8 | pub fn test() { 9 | info!("Running ATA PassThru tests"); 10 | 11 | assert!(is_testdrive_present()); 12 | } 13 | 14 | const ATACMD_IDENTIFY: u8 = 0xEC; 15 | 16 | fn is_testdrive_present() -> bool { 17 | let ata_ctrl_handles = boot::find_handles::().unwrap(); 18 | assert_eq!(ata_ctrl_handles.len(), 1); 19 | 20 | for handle in ata_ctrl_handles { 21 | let params = OpenProtocolParams { 22 | handle, 23 | agent: boot::image_handle(), 24 | controller: None, 25 | }; 26 | let ata_pt = unsafe { 27 | // don't open exclusive! That would break other tests 28 | boot::open_protocol::(params, OpenProtocolAttributes::GetProtocol).unwrap() 29 | }; 30 | for mut device in ata_pt.iter_devices() { 31 | // ATA IDENTIFY command 32 | let request = AtaRequestBuilder::read_udma(ata_pt.io_align(), ATACMD_IDENTIFY) 33 | .unwrap() 34 | .with_timeout(core::time::Duration::from_millis(500)) 35 | .with_read_buffer(255) 36 | .unwrap() 37 | .build(); 38 | if let Ok(result) = device.execute_command(request) { 39 | let bfr = result.read_buffer().unwrap(); 40 | // ATA uses wchar16 big endian strings for serial numbers 41 | let mut serial_bfr = [0u8; 20]; 42 | bfr[20..40] 43 | .chunks_exact(2) 44 | .zip(serial_bfr.chunks_exact_mut(2)) 45 | .for_each(|(src, dst)| { 46 | dst[0] = src[1]; 47 | dst[1] = src[0]; 48 | }); 49 | let serial = core::str::from_utf8(&serial_bfr).unwrap().trim(); 50 | if serial == "AtaPassThru" { 51 | info!("Found Testdisk at handle: {handle:?}"); 52 | return true; // found our testdrive! 53 | } 54 | } 55 | } 56 | } 57 | 58 | false 59 | } 60 | -------------------------------------------------------------------------------- /uefi-raw/src/table/system.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | use crate::protocol::console::{SimpleTextInputProtocol, SimpleTextOutputProtocol}; 4 | use crate::table::Header; 5 | use crate::table::boot::BootServices; 6 | use crate::table::configuration::ConfigurationTable; 7 | use crate::table::runtime::RuntimeServices; 8 | use crate::{Char16, Handle}; 9 | use core::ptr; 10 | 11 | #[derive(Clone, Debug, Eq, PartialEq)] 12 | #[repr(C)] 13 | pub struct SystemTable { 14 | pub header: Header, 15 | 16 | pub firmware_vendor: *const Char16, 17 | pub firmware_revision: u32, 18 | 19 | pub stdin_handle: Handle, 20 | pub stdin: *mut SimpleTextInputProtocol, 21 | 22 | pub stdout_handle: Handle, 23 | pub stdout: *mut SimpleTextOutputProtocol, 24 | 25 | pub stderr_handle: Handle, 26 | pub stderr: *mut SimpleTextOutputProtocol, 27 | 28 | pub runtime_services: *mut RuntimeServices, 29 | pub boot_services: *mut BootServices, 30 | 31 | pub number_of_configuration_table_entries: usize, 32 | pub configuration_table: *mut ConfigurationTable, 33 | } 34 | 35 | impl SystemTable { 36 | pub const SIGNATURE: u64 = 0x5453_5953_2049_4249; 37 | } 38 | 39 | impl Default for SystemTable { 40 | /// Create a `SystemTable` with most fields set to zero. 41 | /// 42 | /// The only fields not set to zero are: 43 | /// * [`Header::signature`] is set to [`SystemTable::SIGNATURE`]. 44 | /// * [`Header::size`] is set to the size in bytes of `SystemTable`. 45 | fn default() -> Self { 46 | Self { 47 | header: Header { 48 | signature: Self::SIGNATURE, 49 | size: u32::try_from(size_of::()).unwrap(), 50 | ..Header::default() 51 | }, 52 | 53 | firmware_vendor: ptr::null_mut(), 54 | firmware_revision: 0, 55 | 56 | stdin_handle: ptr::null_mut(), 57 | stdin: ptr::null_mut(), 58 | 59 | stdout_handle: ptr::null_mut(), 60 | stdout: ptr::null_mut(), 61 | 62 | stderr_handle: ptr::null_mut(), 63 | stderr: ptr::null_mut(), 64 | 65 | runtime_services: ptr::null_mut(), 66 | boot_services: ptr::null_mut(), 67 | 68 | number_of_configuration_table_entries: 0, 69 | configuration_table: ptr::null_mut(), 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /uefi-test-runner/src/proto/console/stdout.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | use uefi::prelude::*; 4 | use uefi::proto::console::text::{Color, Output}; 5 | 6 | pub fn test(stdout: &mut Output) { 7 | info!("Running text output protocol test"); 8 | 9 | get_current_mode(stdout); 10 | change_text_mode(stdout); 11 | change_color(stdout); 12 | center_text(stdout); 13 | 14 | // Print all modes. 15 | for (index, mode) in stdout.modes().enumerate() { 16 | info!( 17 | "- Text mode #{}: {} rows by {} columns", 18 | index, 19 | mode.rows(), 20 | mode.columns() 21 | ); 22 | } 23 | 24 | // Should clean up after us. 25 | stdout.reset(false).unwrap(); 26 | } 27 | 28 | // Retrieves and prints the current output mode. 29 | fn get_current_mode(stdout: &mut Output) { 30 | let current_mode = stdout.current_mode().unwrap(); 31 | info!("UEFI standard output current mode: {current_mode:?}"); 32 | } 33 | 34 | // Switch to the maximum supported text mode. 35 | fn change_text_mode(stdout: &mut Output) { 36 | let best_mode = stdout.modes().last().unwrap(); 37 | stdout 38 | .set_mode(best_mode) 39 | .expect("Failed to change text mode"); 40 | } 41 | 42 | // Set a new color, and paint the background with it. 43 | fn change_color(stdout: &mut Output) { 44 | stdout 45 | .set_color(Color::White, Color::Blue) 46 | .expect("Failed to change console color"); 47 | stdout.clear().expect("Failed to clear screen"); 48 | } 49 | 50 | // Print a text centered on screen. 51 | fn center_text(stdout: &mut Output) { 52 | // Move the cursor. 53 | // This will make this `info!` line below be (somewhat) centered. 54 | stdout 55 | .enable_cursor(true) 56 | .unwrap_or_else(|e| match e.status() { 57 | Status::UNSUPPORTED => info!("Cursor visibility control unavailable"), 58 | _ => panic!("Failed to show cursor"), 59 | }); 60 | stdout 61 | .set_cursor_position(24, 0) 62 | .expect("Failed to move cursor"); 63 | info!("# uefi-rs test runner"); 64 | stdout 65 | .enable_cursor(false) 66 | .unwrap_or_else(|e| match e.status() { 67 | Status::UNSUPPORTED => info!("Cursor visibility control unavailable"), 68 | _ => panic!("Failed to hide cursor"), 69 | }); 70 | } 71 | -------------------------------------------------------------------------------- /uefi/src/table/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | //! Standard UEFI tables. 4 | 5 | pub mod cfg; 6 | 7 | mod header; 8 | 9 | pub use header::Header; 10 | pub use uefi_raw::table::Revision; 11 | 12 | use core::ptr::{self, NonNull}; 13 | use core::sync::atomic::{AtomicPtr, Ordering}; 14 | 15 | /// Global system table pointer. This is only modified by [`set_system_table`]. 16 | static SYSTEM_TABLE: AtomicPtr = 17 | AtomicPtr::new(ptr::null_mut()); 18 | 19 | /// Get the raw system table pointer. 20 | /// 21 | /// If called before `set_system_table` has been called, this will return `None`. 22 | pub fn system_table_raw() -> Option> { 23 | let ptr = SYSTEM_TABLE.load(Ordering::Acquire); 24 | NonNull::new(ptr) 25 | } 26 | 27 | /// Get the raw system table pointer. This may only be called after 28 | /// `set_system_table` has been used to set the global pointer. 29 | /// 30 | /// # Panics 31 | /// 32 | /// Panics if the global system table pointer is null. 33 | #[track_caller] 34 | pub(crate) fn system_table_raw_panicking() -> NonNull { 35 | system_table_raw().expect("global system table pointer is not set") 36 | } 37 | 38 | /// Update the global system table pointer. 39 | /// 40 | /// This is called automatically in the `main` entry point as part of 41 | /// [`uefi::entry`]. 42 | /// 43 | /// It is also called by [`set_virtual_address_map`] to transition from a 44 | /// physical address to a virtual address. 45 | /// 46 | /// This function should not be called at any other point in time, unless the 47 | /// executable does not use [`uefi::entry`], in which case it should be called 48 | /// once before calling any other API in this crate. 49 | /// 50 | /// # Safety 51 | /// 52 | /// This function should only be called as described above, and the 53 | /// `ptr` must be a valid [`SystemTable`]. 54 | /// 55 | /// [`SystemTable`]: uefi_raw::table::system::SystemTable 56 | /// [`set_virtual_address_map`]: uefi::runtime::set_virtual_address_map 57 | pub unsafe fn set_system_table(ptr: *const uefi_raw::table::system::SystemTable) { 58 | SYSTEM_TABLE.store(ptr.cast_mut(), Ordering::Release); 59 | } 60 | 61 | /// Common trait implemented by all standard UEFI tables. 62 | pub trait Table { 63 | /// A unique number assigned by the UEFI specification 64 | /// to the standard tables. 65 | const SIGNATURE: u64; 66 | } 67 | -------------------------------------------------------------------------------- /uefi-raw/src/protocol/driver.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | use crate::protocol::device_path::DevicePathProtocol; 4 | use crate::{Guid, Handle, Status, guid}; 5 | 6 | #[derive(Debug)] 7 | #[repr(C)] 8 | pub struct DriverBindingProtocol { 9 | pub supported: unsafe extern "efiapi" fn( 10 | this: *const Self, 11 | controller_handle: Handle, 12 | remaining_device_path: *const DevicePathProtocol, 13 | ) -> Status, 14 | pub start: unsafe extern "efiapi" fn( 15 | this: *const Self, 16 | controller_handle: Handle, 17 | remaining_device_path: *const DevicePathProtocol, 18 | ) -> Status, 19 | pub stop: unsafe extern "efiapi" fn( 20 | this: *const Self, 21 | controller_handle: Handle, 22 | number_of_children: usize, 23 | child_handle_buffer: *const Handle, 24 | ) -> Status, 25 | pub version: u32, 26 | pub image_handle: Handle, 27 | pub driver_binding_handle: Handle, 28 | } 29 | 30 | impl DriverBindingProtocol { 31 | pub const GUID: Guid = guid!("18a031ab-b443-4d1a-a5c0-0c09261e9f71"); 32 | } 33 | 34 | #[derive(Debug)] 35 | #[repr(C)] 36 | pub struct ComponentName2Protocol { 37 | pub get_driver_name: unsafe extern "efiapi" fn( 38 | this: *const Self, 39 | language: *const u8, 40 | driver_name: *mut *const u16, 41 | ) -> Status, 42 | pub get_controller_name: unsafe extern "efiapi" fn( 43 | this: *const Self, 44 | controller_handle: Handle, 45 | child_handle: Handle, 46 | language: *const u8, 47 | controller_name: *mut *const u16, 48 | ) -> Status, 49 | pub supported_languages: *const u8, 50 | } 51 | 52 | impl ComponentName2Protocol { 53 | pub const GUID: Guid = guid!("6a7a5cff-e8d9-4f70-bada-75ab3025ce14"); 54 | 55 | /// GUID of the original `EFI_COMPONENT_NAME_PROTOCOL`. This protocol was 56 | /// deprecated in UEFI 2.1 in favor of the new 57 | /// `EFI_COMPONENT_NAME2_PROTOCOL`. The two protocols are identical 58 | /// except the encoding of supported languages changed from ISO 639-2 to RFC 59 | /// 4646. 60 | pub const DEPRECATED_COMPONENT_NAME_GUID: Guid = guid!("107a772c-d5e1-11d4-9a46-0090273fc14d"); 61 | } 62 | 63 | #[derive(Debug)] 64 | #[repr(C)] 65 | pub struct ServiceBindingProtocol { 66 | pub create_child: 67 | unsafe extern "efiapi" fn(this: *mut Self, child_handle: *mut Handle) -> Status, 68 | pub destroy_child: unsafe extern "efiapi" fn(this: *mut Self, child_handle: Handle) -> Status, 69 | } 70 | -------------------------------------------------------------------------------- /uefi/src/fs/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | //! A high-level file system API for UEFI applications close to the `std::fs` 4 | //! module from Rust's standard library. The main export of this module is 5 | //! [`FileSystem`]. 6 | //! 7 | //! # Difference to typical File System Abstractions 8 | //! Users perform actions on dedicated volumes: For example, the boot volume, 9 | //! such as a CD-rom, USB-stick, or any other storage device. 10 | //! 11 | //! Unlike in the API of typical UNIX file system abstractions, there is 12 | //! no virtual file system. Unlike in Windows, there is no way to access volumes 13 | //! by a dedicated name. 14 | //! 15 | //! # Paths 16 | //! All paths are absolute and follow the FAT-like file system conventions for 17 | //! paths. Thus, there is no current working directory and path components 18 | //! like `.` and `..` are not supported. In other words, the current working 19 | //! directory is always `/`, i.e., the root, of the opened volume. 20 | //! 21 | //! Symlinks or hard-links are not supported but only directories and regular 22 | //! files with plain linear paths to them. For more information, see 23 | //! [`Path`] and [`PathBuf`]. 24 | //! 25 | //! ## Use `&str` as Path 26 | //! A `&str` known at compile time can be converted to a [`Path`] using the 27 | //! [`cstr16!`] macro. During runtime, you can create a path like this: 28 | //! 29 | //! ```no_run 30 | //! use uefi::CString16; 31 | //! use uefi::fs::{FileSystem, FileSystemResult}; 32 | //! use uefi::proto::media::fs::SimpleFileSystem; 33 | //! use uefi::boot::{self, ScopedProtocol}; 34 | //! 35 | //! fn read_file(path: &str) -> FileSystemResult> { 36 | //! let path: CString16 = CString16::try_from(path).unwrap(); 37 | //! let fs: ScopedProtocol = boot::get_image_file_system(boot::image_handle()).unwrap(); 38 | //! let mut fs = FileSystem::new(fs); 39 | //! fs.read(path.as_ref()) 40 | //! } 41 | //! ``` 42 | //! 43 | //! # API Hints 44 | //! There is no `File` abstraction as in the Rust `std` library. Instead, it is 45 | //! intended to work with the file system via dedicated functions, similar to 46 | //! the public functions of the `std::fs` module. 47 | //! 48 | //! There is no automatic synchronization of the file system for concurrent 49 | //! accesses. This is in the responsibility of the user. 50 | //! 51 | //! [`cstr16!`]: crate::cstr16 52 | 53 | mod dir_entry_iter; 54 | mod file_system; 55 | mod path; 56 | mod uefi_types; 57 | 58 | pub use dir_entry_iter::*; 59 | pub use file_system::*; 60 | pub use path::*; 61 | 62 | use uefi_types::*; 63 | -------------------------------------------------------------------------------- /xtask/src/net.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | use std::net::UdpSocket; 4 | use std::sync::{Arc, Mutex}; 5 | use std::thread::{self, JoinHandle}; 6 | use std::time::Duration; 7 | 8 | /// Run a simple echo service that listens on UDP port 21572 and 9 | /// reverses the incoming messages. 10 | pub struct EchoService { 11 | stop_requested: Arc>, 12 | 13 | // `JoinHandle::join` consumes the handle, so put it in an option so 14 | // that we can call `join` in `drop`. 15 | join_handle: Option>, 16 | } 17 | 18 | impl Drop for EchoService { 19 | fn drop(&mut self) { 20 | self.stop(); 21 | self.join_handle 22 | .take() 23 | .unwrap() 24 | .join() 25 | .expect("failed to join echo service thread"); 26 | } 27 | } 28 | 29 | impl EchoService { 30 | /// Start the server. 31 | pub fn start() -> Self { 32 | let stop_requested = Arc::new(Mutex::new(false)); 33 | let stop_requested_copy = stop_requested.clone(); 34 | let join_handle = thread::spawn(|| reverse_echo_service(stop_requested_copy)); 35 | Self { 36 | stop_requested, 37 | join_handle: Some(join_handle), 38 | } 39 | } 40 | 41 | /// Request that the server stop. 42 | pub fn stop(&self) { 43 | let mut guard = self.stop_requested.lock().unwrap(); 44 | *guard = true; 45 | } 46 | } 47 | 48 | fn reverse_echo_service(stop_requested: Arc>) { 49 | let socket = UdpSocket::bind(("127.0.0.1", 21572)).expect("failed to bind to UDP socket"); 50 | 51 | // Set a timeout so that the service can periodically check if a 52 | // stop has been requested. 53 | socket 54 | .set_read_timeout(Some(Duration::from_millis(100))) 55 | .expect("failed to set read timeout"); 56 | 57 | let mut buffer = [0; 257]; 58 | loop { 59 | if *stop_requested.lock().unwrap() { 60 | break; 61 | } 62 | 63 | // Receive a packet. 64 | let (len, addr) = match socket.recv_from(&mut buffer) { 65 | Ok((len, addr)) => (len, addr), 66 | Err(_) => continue, 67 | }; 68 | let buffer = &mut buffer[..len]; 69 | 70 | // Extract header information. 71 | let (payload_len, payload) = buffer.split_first_mut().unwrap(); 72 | assert_eq!(usize::from(*payload_len), payload.len()); 73 | 74 | // Simulate processing the data: Reverse the payload. 75 | payload.reverse(); 76 | 77 | // Send a reply. 78 | socket.send_to(buffer, addr).expect("failed to send packet"); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /uefi/src/helpers/panic_handler.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | use core::time::Duration; 4 | 5 | use crate::{boot, println}; 6 | use cfg_if::cfg_if; 7 | 8 | #[panic_handler] 9 | fn panic_handler(info: &core::panic::PanicInfo) -> ! { 10 | println!("[PANIC]: {}", info); 11 | 12 | // Give the user some time to read the message 13 | if boot::are_boot_services_active() { 14 | boot::stall(Duration::from_secs(10)); 15 | } else { 16 | let mut dummy = 0u64; 17 | // FIXME: May need different counter values in debug & release builds 18 | for i in 0..300_000_000 { 19 | unsafe { 20 | core::ptr::write_volatile(&mut dummy, i); 21 | } 22 | } 23 | } 24 | 25 | cfg_if! { 26 | if #[cfg(all(target_arch = "x86_64", feature = "qemu"))] { 27 | // If running in QEMU, use the f4 exit port to signal the error and exit 28 | use qemu_exit::QEMUExit; 29 | let custom_exit_success = 3; 30 | let qemu_exit_handle = qemu_exit::X86::new(0xF4, custom_exit_success); 31 | qemu_exit_handle.exit_failure(); 32 | } else { 33 | // If the system table is available, use UEFI's standard shutdown mechanism 34 | if let Some(st) = crate::table::system_table_raw() { 35 | if !unsafe { st.as_ref().runtime_services }.is_null() { 36 | crate::runtime::reset(crate::runtime::ResetType::SHUTDOWN, crate::Status::ABORTED, None); 37 | } 38 | } 39 | 40 | // If we don't have any shutdown mechanism handy, the best we can do is loop 41 | log::error!("Could not shut down, please power off the system manually..."); 42 | 43 | cfg_if! { 44 | if #[cfg(target_arch = "x86_64")] { 45 | loop { 46 | unsafe { 47 | // Try to at least keep CPU from running at 100% 48 | core::arch::asm!("hlt", options(nomem, nostack)); 49 | } 50 | } 51 | } else if #[cfg(target_arch = "aarch64")] { 52 | loop { 53 | unsafe { 54 | // Try to at least keep CPU from running at 100% 55 | core::arch::asm!("hlt 420", options(nomem, nostack)); 56 | } 57 | } 58 | } else { 59 | loop { 60 | // just run forever dammit how do you return never anyway 61 | } 62 | } 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /uefi/src/macros.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | /// Encode a string literal as a [`&CStr8`]. 4 | /// 5 | /// The encoding is done at compile time, so the result can be used in a 6 | /// `const` item. 7 | /// 8 | /// An empty string containing just a null character can be created with either 9 | /// `cstr8!()` or `cstr8!("")`. 10 | /// 11 | /// # Example 12 | /// 13 | /// ``` 14 | /// use uefi::{CStr8, cstr8}; 15 | /// 16 | /// const S: &CStr8 = cstr8!("abÿ"); 17 | /// assert_eq!(S.as_bytes(), [97, 98, 255, 0]); 18 | /// 19 | /// const EMPTY: &CStr8 = cstr8!(); 20 | /// assert_eq!(EMPTY.as_bytes(), [0]); 21 | /// assert_eq!(cstr8!(""), EMPTY); 22 | /// ``` 23 | /// 24 | /// [`&CStr8`]: crate::CStr8 25 | #[macro_export] 26 | macro_rules! cstr8 { 27 | () => {{ 28 | const S: &[u8] = &[0]; 29 | // SAFETY: `S` is a trivially correct Latin-1 C string. 30 | unsafe { $crate::CStr8::from_bytes_with_nul_unchecked(S) } 31 | }}; 32 | ($s:literal) => {{ 33 | // Use `const` values here to force errors to happen at compile 34 | // time. 35 | 36 | // Add one for the null char. 37 | const NUM_CHARS: usize = $crate::data_types::str_num_latin1_chars($s) + 1; 38 | 39 | const VAL: [u8; NUM_CHARS] = $crate::data_types::str_to_latin1($s); 40 | 41 | // SAFETY: the `str_to_latin1` function always produces a valid Latin-1 42 | // string with a trailing null character. 43 | unsafe { $crate::CStr8::from_bytes_with_nul_unchecked(&VAL) } 44 | }}; 45 | } 46 | 47 | /// Encode a string literal as a [`&CStr16`]. 48 | /// 49 | /// The encoding is done at compile time, so the result can be used in a 50 | /// `const` item. 51 | /// 52 | /// An empty string containing just a null character can be created with either 53 | /// `cstr16!()` or `cstr16!("")`. 54 | /// 55 | /// # Example 56 | /// 57 | /// ``` 58 | /// use uefi::{CStr16, cstr16}; 59 | /// 60 | /// const S: &CStr16 = cstr16!("abc"); 61 | /// assert_eq!(S.to_u16_slice_with_nul(), [97, 98, 99, 0]); 62 | /// 63 | /// const EMPTY: &CStr16 = cstr16!(); 64 | /// assert_eq!(EMPTY.to_u16_slice_with_nul(), [0]); 65 | /// assert_eq!(cstr16!(""), EMPTY); 66 | /// ``` 67 | /// 68 | /// [`&CStr16`]: crate::CStr16 69 | #[macro_export] 70 | macro_rules! cstr16 { 71 | () => {{ 72 | const S: &[u16] = &[0]; 73 | // SAFETY: `S` is a trivially correct UCS-2 C string. 74 | unsafe { $crate::CStr16::from_u16_with_nul_unchecked(S) } 75 | }}; 76 | ($s:literal) => {{ 77 | const S: &[u16] = &$crate::ucs2_cstr!($s); 78 | // SAFETY: the ucs2_cstr macro always produces a valid UCS-2 string with 79 | // a trailing null character. 80 | unsafe { $crate::CStr16::from_u16_with_nul_unchecked(S) } 81 | }}; 82 | } 83 | -------------------------------------------------------------------------------- /uefi/src/proto/acpi.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | //! `AcpiTable` protocol. 4 | 5 | use crate::proto::unsafe_protocol; 6 | use crate::{Result, StatusExt}; 7 | use core::ffi::c_void; 8 | use uefi_raw::protocol::acpi::AcpiTableProtocol; 9 | 10 | /// The AcpiTable protocol. 11 | #[derive(Debug)] 12 | #[repr(transparent)] 13 | #[unsafe_protocol(AcpiTableProtocol::GUID)] 14 | pub struct AcpiTable(AcpiTableProtocol); 15 | 16 | impl AcpiTable { 17 | /// Installs an ACPI table into the RSDT/XSDT. Returns a index 18 | /// that may be used by `uninstall_acpi_table` to remove the ACPI 19 | /// table. 20 | /// 21 | /// # Safety 22 | /// 23 | /// When installing ACPI table, the data pointed to by 24 | /// `acpi_table_ptr` must be a pool allocation of type 25 | /// [`ACPI_RECLAIM`] or other type suitable for data handed off to 26 | /// the OS. 27 | /// 28 | /// [`ACPI_RECLAIM`]: crate::boot::MemoryType::ACPI_RECLAIM 29 | /// 30 | /// # Errors 31 | /// 32 | /// * [`Status::INVALID_PARAMETER`]: `acpi_table_ptr` is null; the 33 | /// `acpi_table_size`, and the size field embedded in the ACPI 34 | /// table are not in sync. 35 | /// 36 | /// * [`Status::OUT_OF_RESOURCES`]: Insufficient resources 37 | /// exist to complete the request. 38 | /// 39 | /// * [`Status::ACCESS_DENIED`]: The table signature matches a 40 | /// table already present in the system and platform policy does 41 | /// not allow duplicate tables of this type. 42 | /// 43 | /// [`Status::INVALID_PARAMETER`]: crate::Status::INVALID_PARAMETER 44 | /// [`Status::OUT_OF_RESOURCES`]: crate::Status::OUT_OF_RESOURCES 45 | /// [`Status::ACCESS_DENIED`]: crate::Status::ACCESS_DENIED 46 | pub unsafe fn install_acpi_table( 47 | &self, 48 | acpi_table_ptr: *const c_void, 49 | acpi_table_size: usize, 50 | ) -> Result { 51 | let mut table_key = 0usize; 52 | let status = unsafe { 53 | (self.0.install_acpi_table)(&self.0, acpi_table_ptr, acpi_table_size, &mut table_key) 54 | }; 55 | status.to_result_with_val(|| table_key) 56 | } 57 | 58 | /// Removes an ACPI table from the RSDT/XSDT. 59 | /// 60 | /// # Errors 61 | /// 62 | /// * [`Status::NOT_FOUND`]: `table_key` does not refer to a 63 | /// valid key for a table entry. 64 | /// 65 | /// * [`Status::OUT_OF_RESOURCES`]: Insufficient resources exist 66 | /// to complete the request. 67 | /// 68 | /// [`Status::NOT_FOUND`]: crate::Status::NOT_FOUND 69 | /// [`Status::OUT_OF_RESOURCES`]: crate::Status::OUT_OF_RESOURCES 70 | pub fn uninstall_acpi_table(&self, table_key: usize) -> Result { 71 | unsafe { (self.0.uninstall_acpi_table)(&self.0, table_key) }.to_result() 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /uefi-test-runner/src/proto/console/serial.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | use crate::reconnect_serial_to_console; 4 | use uefi::proto::console::serial::{ControlBits, Serial}; 5 | use uefi::{Result, ResultExt, Status, boot}; 6 | 7 | // For the duration of this function, the serial device is opened in 8 | // exclusive mode. That means logs will not work, which means we should 9 | // avoid panicking here because the panic log would be hidden. Instead, 10 | // return a result that gets asserted in `test` *after* the logger has 11 | // been restored. 12 | fn serial_test_helper(serial: &mut Serial) -> Result { 13 | let old_ctrl_bits = serial.get_control_bits()?; 14 | let mut ctrl_bits = ControlBits::empty(); 15 | 16 | // For the purposes of testing, we're _not_ going to implement 17 | // software flow control. 18 | ctrl_bits |= ControlBits::HARDWARE_FLOW_CONTROL_ENABLE; 19 | 20 | // Use a loop back device for testing. 21 | ctrl_bits |= ControlBits::SOFTWARE_LOOPBACK_ENABLE; 22 | 23 | serial.set_control_bits(ctrl_bits)?; 24 | 25 | // Keep this message short, we need it to fit in the FIFO. 26 | const OUTPUT: &[u8] = b"Hello world!"; 27 | const MSG_LEN: usize = OUTPUT.len(); 28 | 29 | serial.write(OUTPUT).discard_errdata()?; 30 | 31 | let mut input = [0u8; MSG_LEN]; 32 | serial.read(&mut input).discard_errdata()?; 33 | 34 | // Clean up after ourselves 35 | serial.reset()?; 36 | serial.set_control_bits(old_ctrl_bits & ControlBits::SETTABLE)?; 37 | 38 | if OUTPUT == input { 39 | Ok(()) 40 | } else { 41 | Err(Status::ABORTED.into()) 42 | } 43 | } 44 | 45 | pub unsafe fn test() { 46 | // The serial device under aarch64 doesn't support the software 47 | // loopback feature needed for this test. 48 | if cfg!(target_arch = "aarch64") { 49 | return; 50 | } 51 | 52 | info!("Running serial protocol test"); 53 | let handle = boot::get_handle_for_protocol::().expect("missing Serial protocol"); 54 | 55 | let mut serial = 56 | boot::open_protocol_exclusive::(handle).expect("failed to open serial protocol"); 57 | 58 | // Send the request, but don't check the result yet so that first 59 | // we can reconnect the console output for the logger. 60 | let res = serial_test_helper(&mut serial); 61 | 62 | // Release the serial device and reconnect all controllers to the 63 | // serial handle. This is necessary to restore the connection 64 | // between the console output device used for logging and the serial 65 | // device, which was broken when we opened the protocol in exclusive 66 | // mode above. 67 | drop(serial); 68 | reconnect_serial_to_console(handle); 69 | 70 | if let Err(err) = res { 71 | panic!("serial test failed: {:?}", err.status()); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /book/src/tutorial/hardware.md: -------------------------------------------------------------------------------- 1 | # Running on Hardware 2 | 3 | Prerequisite: To allow loading your unsigned binary on your personal machine, 4 | [secure boot] needs to be disabled in the BIOS setup. 5 | 6 | To run on real hardware you'll need a specially-prepared USB drive. 7 | 8 | ## Preparation 9 | 10 | The general steps to prepare the drive are: 11 | 12 | 1. Partition the drive using [GPT]. 13 | 2. Create a partition. 14 | 3. Set the partition type GUID to 15 | `C12A7328-F81F-11D2-BA4B-00A0C93EC93B`. That marks it as an EFI 16 | System partition. (On many UEFI implementations this is not strictly 17 | necessary, see note below.) 18 | 4. Format the partition as [FAT]. 19 | 5. Mount the partition. 20 | 6. Create the directory path `EFI/BOOT` on the partition. (FAT is case 21 | insensitive, so capitalization doesn't matter.) 22 | 7. Copy your EFI application to a file under `EFI/BOOT`. The file name 23 | is specific to the architecture. For example, on x86_64 the file name 24 | must be `BOOTX64.EFI`. See the [boot files] table for other 25 | architectures. 26 | 27 | The details of exactly how to do these steps will vary depending on your OS. 28 | 29 | Note that most UEFI implementations do not strictly require GPT 30 | partitioning or the EFI System partition GUID; they will look for any 31 | FAT partition with the appropriate directory structure. This is not 32 | required however; the UEFI Specification says "UEFI implementations may 33 | allow the use of conforming FAT partitions which do not use the ESP 34 | GUID." 35 | 36 | ### Example on Linux 37 | 38 | **Warning: these operations are destructive! Do not run these commands 39 | on a disk if you care about the data it contains.** 40 | 41 | ```sh 42 | # Create the GPT, create a 9MB partition starting at 1MB, and set the 43 | # partition type to EFI System. 44 | sgdisk \ 45 | --clear \ 46 | --new=1:1M:10M \ 47 | --typecode=1:C12A7328-F81F-11D2-BA4B-00A0C93EC93B \ 48 | /path/to/disk 49 | 50 | # Format the partition as FAT. 51 | mkfs.fat /path/to/disk_partition 52 | 53 | # Mount the partition. 54 | mkdir esp 55 | mount /path/to/disk_partition esp 56 | 57 | # Create the boot directory. 58 | mkdir esp/EFI/BOOT 59 | 60 | # Copy in the boot executable. 61 | cp /path/to/your-executable.efi esp/EFI/BOOT/BOOTX64.EFI 62 | ``` 63 | 64 | ## Booting the USB 65 | 66 | Insert the USB into the target computer. Reboot the machine, then press 67 | the one-time boot key. Which key to press depends on the vendor. For 68 | example, Dell uses F12, HP uses F9, and on Macs you hold down the Option 69 | key. 70 | 71 | Once the one-time boot menu appears, select your USB drive and press enter. 72 | 73 | [secure boot]: https://en.wikipedia.org/wiki/UEFI#Secure_Boot 74 | [GPT]: https://en.wikipedia.org/wiki/GUID_Partition_Table 75 | [FAT]: https://en.wikipedia.org/wiki/File_Allocation_Table 76 | [boot files]: ../concepts/gpt.html#system-partition 77 | -------------------------------------------------------------------------------- /uefi-test-runner/src/proto/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | use uefi::boot::{self, OpenProtocolParams}; 4 | use uefi::proto::loaded_image::LoadedImage; 5 | use uefi::{Identify, proto}; 6 | 7 | pub fn test() { 8 | info!("Testing various protocols"); 9 | 10 | console::test(); 11 | 12 | find_protocol(); 13 | test_protocols_per_handle(); 14 | test_test_protocol(); 15 | 16 | debug::test(); 17 | device_path::test(); 18 | driver::test(); 19 | load::test(); 20 | loaded_image::test(); 21 | media::test(); 22 | network::test(); 23 | pci::test(); 24 | pi::test(); 25 | rng::test(); 26 | shell_params::test(); 27 | string::test(); 28 | usb::test(); 29 | misc::test(); 30 | 31 | // disable the ATA test on aarch64 for now. The aarch64 UEFI Firmware does not yet seem 32 | // to support SATA controllers (and providing an AtaPassThru protocol instance for them). 33 | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 34 | ata::test(); 35 | scsi::test(); 36 | nvme::test(); 37 | 38 | #[cfg(any( 39 | target_arch = "x86", 40 | target_arch = "x86_64", 41 | target_arch = "arm", 42 | target_arch = "aarch64" 43 | ))] 44 | shim::test(); 45 | shell::test(); 46 | tcg::test(); 47 | } 48 | 49 | fn find_protocol() { 50 | let handles = boot::find_handles::() 51 | .expect("Failed to retrieve list of handles"); 52 | 53 | assert!( 54 | !handles.is_empty(), 55 | "There should be at least one implementation of Simple Text Output (stdout)" 56 | ); 57 | } 58 | 59 | fn test_protocols_per_handle() { 60 | let pph = boot::protocols_per_handle(boot::image_handle()).unwrap(); 61 | info!("Image handle has {} protocols", pph.len()); 62 | // Check that one of the image's protocols is `LoadedImage`. 63 | assert!(pph.iter().any(|guid| **guid == LoadedImage::GUID)); 64 | } 65 | 66 | fn test_test_protocol() { 67 | assert!( 68 | boot::test_protocol::(OpenProtocolParams { 69 | handle: boot::image_handle(), 70 | agent: boot::image_handle(), 71 | controller: None, 72 | }) 73 | .unwrap() 74 | ); 75 | } 76 | 77 | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 78 | mod ata; 79 | mod console; 80 | mod debug; 81 | mod device_path; 82 | mod driver; 83 | mod load; 84 | mod loaded_image; 85 | mod media; 86 | mod misc; 87 | mod network; 88 | mod nvme; 89 | mod pci; 90 | mod pi; 91 | mod rng; 92 | mod scsi; 93 | #[cfg(any( 94 | target_arch = "x86", 95 | target_arch = "x86_64", 96 | target_arch = "arm", 97 | target_arch = "aarch64" 98 | ))] 99 | mod shell; 100 | mod shell_params; 101 | mod shim; 102 | mod string; 103 | mod tcg; 104 | mod usb; 105 | -------------------------------------------------------------------------------- /uefi-raw/src/protocol/block.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | use crate::{Boolean, Event, Guid, Status, guid}; 4 | use core::ffi::c_void; 5 | 6 | /// Logical block address. 7 | pub type Lba = u64; 8 | 9 | /// Media information structure 10 | #[repr(C)] 11 | #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] 12 | pub struct BlockIoMedia { 13 | pub media_id: u32, 14 | pub removable_media: Boolean, 15 | pub media_present: Boolean, 16 | pub logical_partition: Boolean, 17 | pub read_only: Boolean, 18 | pub write_caching: Boolean, 19 | pub block_size: u32, 20 | pub io_align: u32, 21 | pub last_block: Lba, 22 | 23 | // Added in revision 2. 24 | pub lowest_aligned_lba: Lba, 25 | pub logical_blocks_per_physical_block: u32, 26 | 27 | // Added in revision 3. 28 | pub optimal_transfer_length_granularity: u32, 29 | } 30 | 31 | #[derive(Debug)] 32 | #[repr(C)] 33 | pub struct BlockIoProtocol { 34 | pub revision: u64, 35 | pub media: *const BlockIoMedia, 36 | pub reset: unsafe extern "efiapi" fn(this: *mut Self, extended_verification: Boolean) -> Status, 37 | pub read_blocks: unsafe extern "efiapi" fn( 38 | this: *const Self, 39 | media_id: u32, 40 | lba: Lba, 41 | buffer_size: usize, 42 | buffer: *mut c_void, 43 | ) -> Status, 44 | pub write_blocks: unsafe extern "efiapi" fn( 45 | this: *mut Self, 46 | media_id: u32, 47 | lba: Lba, 48 | buffer_size: usize, 49 | buffer: *const c_void, 50 | ) -> Status, 51 | pub flush_blocks: unsafe extern "efiapi" fn(this: *mut Self) -> Status, 52 | } 53 | 54 | impl BlockIoProtocol { 55 | pub const GUID: Guid = guid!("964e5b21-6459-11d2-8e39-00a0c969723b"); 56 | } 57 | 58 | #[repr(C)] 59 | #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] 60 | pub struct BlockIo2Token { 61 | pub event: Event, 62 | pub transaction_status: Status, 63 | } 64 | 65 | #[derive(Debug)] 66 | #[repr(C)] 67 | pub struct BlockIo2Protocol { 68 | pub media: *const BlockIoMedia, 69 | pub reset: unsafe extern "efiapi" fn(this: *mut Self, extended_verification: Boolean) -> Status, 70 | pub read_blocks_ex: unsafe extern "efiapi" fn( 71 | this: *const Self, 72 | media_id: u32, 73 | lba: Lba, 74 | token: *mut BlockIo2Token, 75 | buffer_size: usize, 76 | buffer: *mut c_void, 77 | ) -> Status, 78 | pub write_blocks_ex: unsafe extern "efiapi" fn( 79 | this: *mut Self, 80 | media_id: u32, 81 | lba: Lba, 82 | token: *mut BlockIo2Token, 83 | buffer_size: usize, 84 | buffer: *const c_void, 85 | ) -> Status, 86 | pub flush_blocks_ex: 87 | unsafe extern "efiapi" fn(this: *mut Self, token: *mut BlockIo2Token) -> Status, 88 | } 89 | 90 | impl BlockIo2Protocol { 91 | pub const GUID: Guid = guid!("a77b2472-e282-4e9f-a245-c2c0e27bbcc1"); 92 | } 93 | -------------------------------------------------------------------------------- /uefi-test-runner/src/proto/pci/root_bridge.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | use uefi::Handle; 4 | use uefi::boot::{OpenProtocolAttributes, OpenProtocolParams, ScopedProtocol, image_handle}; 5 | use uefi::proto::ProtocolPointer; 6 | use uefi::proto::pci::root_bridge::PciRootBridgeIo; 7 | 8 | const RED_HAT_PCI_VENDOR_ID: u16 = 0x1AF4; 9 | const MASS_STORAGE_CTRL_CLASS_CODE: u8 = 0x1; 10 | const SATA_CTRL_SUBCLASS_CODE: u8 = 0x6; 11 | 12 | const REG_SIZE: u8 = size_of::() as u8; 13 | 14 | pub fn test() { 15 | let pci_handles = uefi::boot::find_handles::().unwrap(); 16 | 17 | let mut red_hat_dev_cnt = 0; 18 | let mut mass_storage_ctrl_cnt = 0; 19 | let mut sata_ctrl_cnt = 0; 20 | 21 | for pci_handle in pci_handles { 22 | let mut pci_proto = get_open_protocol::(pci_handle); 23 | 24 | let pci_tree = pci_proto.enumerate().unwrap(); 25 | for addr in pci_tree.iter().cloned() { 26 | let Ok(reg0) = pci_proto.pci().read_one::(addr.with_register(0)) else { 27 | continue; 28 | }; 29 | if reg0 == 0xFFFFFFFF { 30 | continue; // not a valid device 31 | } 32 | let reg1 = pci_proto 33 | .pci() 34 | .read_one::(addr.with_register(2 * REG_SIZE)) 35 | .unwrap(); 36 | 37 | let vendor_id = (reg0 & 0xFFFF) as u16; 38 | let device_id = (reg0 >> 16) as u16; 39 | if vendor_id == RED_HAT_PCI_VENDOR_ID { 40 | red_hat_dev_cnt += 1; 41 | } 42 | 43 | let class_code = (reg1 >> 24) as u8; 44 | let subclass_code = ((reg1 >> 16) & 0xFF) as u8; 45 | if class_code == MASS_STORAGE_CTRL_CLASS_CODE { 46 | mass_storage_ctrl_cnt += 1; 47 | 48 | if subclass_code == SATA_CTRL_SUBCLASS_CODE { 49 | sata_ctrl_cnt += 1; 50 | } 51 | } 52 | 53 | let (bus, dev, fun) = (addr.bus, addr.dev, addr.fun); 54 | log::info!( 55 | "PCI Device: [{bus:02x}, {dev:02x}, {fun:02x}]: vendor={vendor_id:04X}, device={device_id:04X}, class={class_code:02X}, subclass={subclass_code:02X}" 56 | ); 57 | for child_bus in pci_tree.child_bus_of_iter(addr) { 58 | log::info!(" |- Bus: {child_bus:02x}"); 59 | } 60 | } 61 | } 62 | 63 | assert!(red_hat_dev_cnt > 0); 64 | assert!(mass_storage_ctrl_cnt > 0); 65 | assert!(sata_ctrl_cnt > 0); 66 | } 67 | 68 | fn get_open_protocol(handle: Handle) -> ScopedProtocol

{ 69 | let open_opts = OpenProtocolParams { 70 | handle, 71 | agent: image_handle(), 72 | controller: None, 73 | }; 74 | let open_attrs = OpenProtocolAttributes::GetProtocol; 75 | unsafe { uefi::boot::open_protocol(open_opts, open_attrs).unwrap() } 76 | } 77 | -------------------------------------------------------------------------------- /uefi-raw/src/protocol/hii/form_browser.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | //! Form Browser protocol 4 | 5 | use super::{FormId, HiiHandle}; 6 | use crate::{Boolean, Char16, Guid, Status, guid, newtype_enum}; 7 | 8 | /// EFI_SCREEN_DESCRIPTOR 9 | #[derive(Debug)] 10 | #[repr(C)] 11 | pub struct ScreenDescriptor { 12 | pub left_column: usize, 13 | pub right_column: usize, 14 | pub top_row: usize, 15 | pub bottom_row: usize, 16 | } 17 | 18 | newtype_enum! { 19 | /// Represents actions requested by the Forms Browser in response to user interactions. 20 | #[derive(Default)] 21 | pub enum BrowserActionRequest: usize => { 22 | /// No special behavior is taken by the Forms Browser. 23 | NONE = 0, 24 | /// The Forms Browser will exit and request the platform to reset. 25 | RESET = 1, 26 | /// The Forms Browser will save all modified question values to storage and exit. 27 | SUBMIT = 2, 28 | /// The Forms Browser will discard all modified question values and exit. 29 | EXIT = 3, 30 | /// The Forms Browser will write all modified question values on the selected form to storage and exit the form. 31 | FORM_SUBMIT_EXIT = 4, 32 | /// The Forms Browser will discard the modified question values on the selected form and exit the form. 33 | FORM_DISCARD_EXIT = 5, 34 | /// The Forms Browser will write all modified current question values on the selected form to storage. 35 | FORM_APPLY = 6, 36 | /// The Forms Browser will discard the current question values on the selected form and replace them with the original values. 37 | FORM_DISCARD = 7, 38 | /// The user performed a hardware or software configuration change, requiring controller reconnection. 39 | /// The Forms Browser calls `DisconnectController()` followed by `ConnectController()`. 40 | RECONNECT = 8, 41 | /// The Forms Browser will write the current modified question value on the selected form to storage. 42 | QUESTION_APPLY = 9, 43 | } 44 | } 45 | 46 | /// EFI_FORM_BROWSER2_PROTOCOL 47 | #[derive(Debug)] 48 | #[repr(C)] 49 | pub struct FormBrowser2Protocol { 50 | pub send_form: unsafe extern "efiapi" fn( 51 | this: *const Self, 52 | handles: *const HiiHandle, 53 | handle_count: usize, 54 | formset_guid: *const Guid, 55 | form_id: FormId, 56 | screen_dimensions: *const ScreenDescriptor, 57 | action_request: *mut BrowserActionRequest, 58 | ) -> Status, 59 | pub browser_callback: unsafe extern "efiapi" fn( 60 | this: *const Self, 61 | results_data_size: *mut usize, 62 | results_data: *mut Char16, 63 | retrieve_data: Boolean, 64 | variable_guid: *const Guid, 65 | variable_name: *const Char16, 66 | ) -> Status, 67 | } 68 | 69 | impl FormBrowser2Protocol { 70 | pub const GUID: Guid = guid!("b9d4c360-bcfb-4f9b-9298-53c136982258"); 71 | } 72 | -------------------------------------------------------------------------------- /uefi/src/fs/path/validation.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | //! Path validation for the purpose of the [`fs`] module. This is decoupled from 4 | //! [`Path`] and [`PathBuf`], as the Rust standard library also does it this 5 | //! way. Instead, the FS implementation is responsible for that. 6 | //! 7 | //! [`PathBuf`]: super::PathBuf 8 | //! [`fs`]: crate::fs 9 | 10 | use super::Path; 11 | use crate::Char16; 12 | use crate::fs::CHARACTER_DENY_LIST; 13 | use core::fmt::{self, Display, Formatter}; 14 | 15 | /// Errors related to file paths. 16 | #[derive(Debug, Clone, Eq, PartialEq)] 17 | pub enum PathError { 18 | /// The path is empty / points to nothing. 19 | Empty, 20 | /// A component of the path is empty, i.e., two separators without content 21 | /// in between were found. 22 | EmptyComponent, 23 | /// There are illegal characters in the path. 24 | IllegalChar(Char16), 25 | } 26 | 27 | impl Display for PathError { 28 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 29 | match self { 30 | Self::Empty => write!(f, "path is empty"), 31 | Self::EmptyComponent => write!(f, "path contains an empty component"), 32 | Self::IllegalChar(c) => { 33 | write!( 34 | f, 35 | "path contains an illegal character (value {})", 36 | u16::from(*c) 37 | ) 38 | } 39 | } 40 | } 41 | } 42 | 43 | impl core::error::Error for PathError {} 44 | 45 | /// Validates a path for the needs of the [`fs`] module. 46 | /// 47 | /// [`fs`]: crate::fs 48 | pub fn validate_path>(path: P) -> Result<(), PathError> { 49 | let path = path.as_ref(); 50 | if path.is_empty() { 51 | return Err(PathError::Empty); 52 | } 53 | for component in path.components() { 54 | if component.is_empty() { 55 | return Err(PathError::EmptyComponent); 56 | } else if let Some(char) = component 57 | .as_slice() 58 | .iter() 59 | .find(|c| CHARACTER_DENY_LIST.contains(c)) 60 | { 61 | return Err(PathError::IllegalChar(*char)); 62 | } 63 | } 64 | Ok(()) 65 | } 66 | 67 | #[cfg(test)] 68 | mod tests { 69 | use super::*; 70 | use crate::fs::PathBuf; 71 | use crate::{CString16, cstr16}; 72 | 73 | #[test] 74 | fn test_validate_path() { 75 | validate_path(cstr16!("hello\\foo\\bar")).unwrap(); 76 | 77 | let err = validate_path(cstr16!("hello\\f>oo\\bar")).unwrap_err(); 78 | assert_eq!(err, PathError::IllegalChar(CHARACTER_DENY_LIST[6])); 79 | 80 | let err = validate_path(cstr16!("hello\\\\bar")).unwrap_err(); 81 | assert_eq!(err, PathError::EmptyComponent); 82 | 83 | let empty_cstring16 = CString16::try_from("").unwrap(); 84 | let path = PathBuf::from(empty_cstring16); 85 | let err = validate_path(path).unwrap_err(); 86 | assert_eq!(err, PathError::Empty) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /uefi-raw/src/protocol/tcg/v1.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | //! [TCG] (Trusted Computing Group) protocol for [TPM] (Trusted Platform 4 | //! Module) 1.1 and 1.2. 5 | //! 6 | //! This protocol is defined in the [TCG EFI Protocol Specification _for 7 | //! TPM Family 1.1 or 1.2_][spec]. 8 | //! 9 | //! [spec]: https://trustedcomputinggroup.org/resource/tcg-efi-protocol-specification/ 10 | //! [TCG]: https://trustedcomputinggroup.org/ 11 | //! [TPM]: https://en.wikipedia.org/wiki/Trusted_Platform_Module 12 | 13 | use crate::{Guid, PhysicalAddress, Status, guid}; 14 | use core::ffi::c_void; 15 | 16 | /// Information about the protocol and the TPM device. 17 | #[repr(C)] 18 | #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd)] 19 | pub struct TcgBootServiceCapability { 20 | pub size: u8, 21 | pub structure_version: TcgVersion, 22 | pub protocol_spec_version: TcgVersion, 23 | pub hash_algorithm_bitmap: u8, 24 | pub tpm_present_flag: u8, 25 | pub tpm_deactivated_flag: u8, 26 | } 27 | 28 | /// Version information. 29 | #[repr(C)] 30 | #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd)] 31 | pub struct TcgVersion { 32 | /// Major version. 33 | pub major: u8, 34 | /// Minor version. 35 | pub minor: u8, 36 | 37 | pub rev_major: u8, 38 | pub rev_minor: u8, 39 | } 40 | 41 | /// Protocol for interacting with TPM 1.1 and 1.2 devices. 42 | #[derive(Debug)] 43 | #[repr(C)] 44 | pub struct TcgProtocol { 45 | pub status_check: unsafe extern "efiapi" fn( 46 | this: *mut Self, 47 | protocol_capability: *mut TcgBootServiceCapability, 48 | feature_flags: *mut u32, 49 | event_log_location: *mut PhysicalAddress, 50 | event_log_last_entry: *mut PhysicalAddress, 51 | ) -> Status, 52 | 53 | pub hash_all: unsafe extern "efiapi" fn( 54 | this: *mut Self, 55 | hash_data: *const u8, 56 | hash_data_len: u64, 57 | algorithm_id: u32, 58 | hashed_data_len: *mut u64, 59 | hashed_data_result: *mut *mut u8, 60 | ) -> Status, 61 | 62 | pub log_event: unsafe extern "efiapi" fn( 63 | this: *mut Self, 64 | event: *const c_void, 65 | event_number: *mut u32, 66 | flags: u32, 67 | ) -> Status, 68 | 69 | pub pass_through_to_tpm: unsafe extern "efiapi" fn( 70 | this: *mut Self, 71 | tpm_input_parameter_block_size: u32, 72 | tpm_input_parameter_block: *const u8, 73 | tpm_output_parameter_block_size: u32, 74 | tpm_output_parameter_block: *mut u8, 75 | ) -> Status, 76 | 77 | pub hash_log_extend_event: unsafe extern "efiapi" fn( 78 | this: *mut Self, 79 | hash_data: PhysicalAddress, 80 | hash_data_len: u64, 81 | algorithm_id: u32, 82 | event: *mut c_void, 83 | event_number: *mut u32, 84 | event_log_last_entry: *mut PhysicalAddress, 85 | ) -> Status, 86 | } 87 | 88 | impl TcgProtocol { 89 | pub const GUID: Guid = guid!("f541796d-a62e-4954-a775-9584f61b9cdd"); 90 | } 91 | -------------------------------------------------------------------------------- /uefi-test-runner/src/proto/usb/io.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | use uefi::proto::usb::DeviceDescriptor; 4 | use uefi::proto::usb::io::{ControlTransfer, UsbIo}; 5 | use uefi::{Status, boot}; 6 | 7 | const DEVICE_TO_HOST: u8 = 1 << 7; 8 | const STANDARD_REQUEST: u8 = 0b00 << 5; 9 | const DEVICE_RECIPIENT: u8 = 0b0_0000; 10 | const GET_DESCRIPTOR_REQUEST: u8 = 6; 11 | const DEVICE_DESCRIPTOR: u8 = 1; 12 | 13 | /// This test iterates through all of the exposed active USB interfaces and 14 | /// performs checks on each to validate that descriptor acquisition and control 15 | /// transfers work correctly. 16 | pub fn test() { 17 | info!("Testing USB I/O protocol"); 18 | 19 | let handles = boot::locate_handle_buffer(boot::SearchType::from_proto::()) 20 | .expect("failed to acquire USB I/O handles"); 21 | 22 | for handle in handles.iter().copied() { 23 | let mut io = boot::open_protocol_exclusive::(handle) 24 | .expect("failed to open USB I/O protocol"); 25 | 26 | let device = io 27 | .device_descriptor() 28 | .expect("failed to acquire USB device descriptor"); 29 | io.config_descriptor() 30 | .expect("failed to acquire USB config descriptor"); 31 | io.interface_descriptor() 32 | .expect("failed to acquire USB interface descriptor"); 33 | 34 | for endpoint_index in 0..16 { 35 | let result = io.endpoint_descriptor(endpoint_index); 36 | if result 37 | .as_ref() 38 | .is_err_and(|error| error.status() == Status::NOT_FOUND) 39 | { 40 | continue; 41 | } 42 | 43 | result.expect("failed to acquire USB endpoint descriptor"); 44 | } 45 | 46 | let supported_languages = io 47 | .supported_languages() 48 | .expect("failed to acquire supported language list"); 49 | let test_language = supported_languages[0]; 50 | 51 | for string_index in 0..=u8::MAX { 52 | let result = io.string_descriptor(test_language, string_index); 53 | if result 54 | .as_ref() 55 | .is_err_and(|error| error.status() == Status::NOT_FOUND) 56 | { 57 | continue; 58 | } 59 | 60 | result.expect("failed to acquire string descriptor"); 61 | } 62 | 63 | let mut buffer = [0u8; size_of::()]; 64 | 65 | io.control_transfer( 66 | DEVICE_TO_HOST | STANDARD_REQUEST | DEVICE_RECIPIENT, 67 | GET_DESCRIPTOR_REQUEST, 68 | u16::from(DEVICE_DESCRIPTOR) << 8, 69 | 0, 70 | ControlTransfer::DataIn(&mut buffer[..size_of::()]), 71 | 0, 72 | ) 73 | .expect("failed control transfer"); 74 | unsafe { 75 | assert_eq!( 76 | device, 77 | buffer.as_ptr().cast::().read_unaligned() 78 | ) 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /uefi-raw/src/protocol/tcg/enums.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | use crate::newtype_enum; 4 | 5 | newtype_enum! { 6 | /// Algorithm identifiers. 7 | /// 8 | /// These values are defined in the [TCG Algorithm Registry]. 9 | /// 10 | /// [TCG Algorithm Registry]: https://trustedcomputinggroup.org/resource/tcg-algorithm-registry/ 11 | pub enum AlgorithmId: u16 => { 12 | ERROR = 0x0000, 13 | RSA = 0x0001, 14 | TDES = 0x0003, 15 | SHA1 = 0x0004, 16 | HMAC = 0x0005, 17 | AES = 0x0006, 18 | MGF1 = 0x0007, 19 | KEYED_HASH = 0x0008, 20 | XOR = 0x000a, 21 | SHA256 = 0x000b, 22 | SHA384 = 0x000c, 23 | SHA512 = 0x000d, 24 | NULL = 0x0010, 25 | SM3_256 = 0x0012, 26 | SM4 = 0x0013, 27 | // TODO: there are a bunch more, but the above list is probably 28 | // more than sufficient for real devices. 29 | } 30 | } 31 | 32 | newtype_enum! { 33 | /// Event types stored in the TPM event log. The event type defines 34 | /// which structure type is stored in the event data. 35 | /// 36 | /// For details of each variant, see the [TCG PC Client Platform 37 | /// Firmware Protocol Specification][spec], in particular the Events 38 | /// table in the Event Logging chapter. 39 | /// 40 | /// [spec]: https://trustedcomputinggroup.org/resource/pc-client-specific-platform-firmware-profile-specification/ 41 | pub enum EventType: u32 => { 42 | PREBOOT_CERT = 0x0000_0000, 43 | POST_CODE = 0x0000_0001, 44 | UNUSED = 0x0000_0002, 45 | NO_ACTION = 0x0000_0003, 46 | SEPARATOR = 0x0000_0004, 47 | ACTION = 0x0000_0005, 48 | EVENT_TAG = 0x0000_0006, 49 | CRTM_CONTENTS = 0x0000_0007, 50 | CRTM_VERSION = 0x0000_0008, 51 | CPU_MICROCODE = 0x0000_0009, 52 | PLATFORM_CONFIG_FLAGS = 0x0000_000a, 53 | TABLE_OF_DEVICES = 0x0000_000b, 54 | COMPACT_HASH = 0x0000_000c, 55 | IPL = 0x0000_000d, 56 | IPL_PARTITION_DATA = 0x0000_000e, 57 | NONHOST_CODE = 0x0000_000f, 58 | NONHOST_CONFIG = 0x0000_0010, 59 | NONHOST_INFO = 0x0000_0011, 60 | OMIT_BOOT_DEVICE_EVENTS = 0x0000_0012, 61 | EFI_EVENT_BASE = 0x8000_0000, 62 | EFI_VARIABLE_DRIVER_CONFIG = 0x8000_0001, 63 | EFI_VARIABLE_BOOT = 0x8000_0002, 64 | EFI_BOOT_SERVICES_APPLICATION = 0x8000_0003, 65 | EFI_BOOT_SERVICES_DRIVER = 0x8000_0004, 66 | EFI_RUNTIME_SERVICES_DRIVER = 0x8000_0005, 67 | EFI_GPT_EVENT = 0x8000_0006, 68 | EFI_ACTION = 0x8000_0007, 69 | EFI_PLATFORM_FIRMWARE_BLOB = 0x8000_0008, 70 | EFI_HANDOFF_TABLES = 0x8000_0009, 71 | EFI_PLATFORM_FIRMWARE_BLOB2 = 0x8000_000a, 72 | EFI_HANDOFF_TABLES2 = 0x8000_000b, 73 | EFI_VARIABLE_BOOT2 = 0x8000_000c, 74 | EFI_HCRTM_EVENT = 0x8000_0010, 75 | EFI_VARIABLE_AUTHORITY = 0x8000_00e0, 76 | EFI_SPDM_FIRMWARE_BLOB = 0x8000_00e1, 77 | EFI_SPDM_FIRMWARE_CONFIG = 0x8000_00e2, 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /uefi-raw/src/protocol/usb/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | use core::ffi; 4 | 5 | use bitflags::bitflags; 6 | 7 | use crate::{Status, newtype_enum}; 8 | 9 | pub mod host_controller; 10 | pub mod io; 11 | 12 | newtype_enum! { 13 | pub enum DataDirection: i32 => { 14 | DATA_IN = 0, 15 | DATA_OUT = 1, 16 | NO_DATA = 2, 17 | } 18 | } 19 | 20 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 21 | #[repr(C)] 22 | pub struct DeviceRequest { 23 | pub request_type: u8, 24 | pub request: u8, 25 | pub value: u16, 26 | pub index: u16, 27 | pub length: u16, 28 | } 29 | 30 | bitflags! { 31 | #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] 32 | #[repr(transparent)] 33 | pub struct UsbTransferStatus: u32 { 34 | const NOT_EXECUTE = 0x0001; 35 | const STALL = 0x0002; 36 | const BUFFER = 0x0004; 37 | const BABBLE = 0x0008; 38 | const NAK = 0x0010; 39 | const CRC = 0x0020; 40 | const TIMEOUT = 0x0040; 41 | const BIT_STUFF = 0x0080; 42 | const SYSTEM = 0x0100; 43 | } 44 | } 45 | 46 | impl UsbTransferStatus { 47 | pub const SUCCESS: Self = Self::empty(); 48 | } 49 | 50 | pub type AsyncUsbTransferCallback = unsafe extern "efiapi" fn( 51 | data: *mut ffi::c_void, 52 | data_length: usize, 53 | context: *mut ffi::c_void, 54 | status: UsbTransferStatus, 55 | ) -> Status; 56 | 57 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 58 | #[repr(C)] 59 | pub struct DeviceDescriptor { 60 | pub length: u8, 61 | pub descriptor_type: u8, 62 | pub bcd_usb: u16, 63 | pub device_class: u8, 64 | pub device_subclass: u8, 65 | pub device_protocol: u8, 66 | pub max_packet_size: u8, 67 | pub id_vendor: u16, 68 | pub id_product: u16, 69 | pub bcd_device: u16, 70 | pub str_manufacturer: u8, 71 | pub str_product: u8, 72 | pub str_serial_number: u8, 73 | pub num_configurations: u8, 74 | } 75 | 76 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 77 | #[repr(C)] 78 | pub struct ConfigDescriptor { 79 | pub length: u8, 80 | pub descriptor_type: u8, 81 | pub total_length: u16, 82 | pub num_interfaces: u8, 83 | pub configuration_value: u8, 84 | pub configuration: u8, 85 | pub attributes: u8, 86 | pub max_power: u8, 87 | } 88 | 89 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 90 | #[repr(C)] 91 | pub struct InterfaceDescriptor { 92 | pub length: u8, 93 | pub descriptor_type: u8, 94 | pub interface_number: u8, 95 | pub alternate_setting: u8, 96 | pub num_endpoints: u8, 97 | pub interface_class: u8, 98 | pub interface_subclass: u8, 99 | pub interface_protocol: u8, 100 | pub interface: u8, 101 | } 102 | 103 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 104 | #[repr(C)] 105 | pub struct EndpointDescriptor { 106 | pub length: u8, 107 | pub descriptor_type: u8, 108 | pub endpoint_address: u8, 109 | pub attributes: u8, 110 | pub max_packet_size: u16, 111 | pub interval: u8, 112 | } 113 | -------------------------------------------------------------------------------- /uefi-test-runner/src/bin/shell_launcher.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | //! This application launches the UEFI shell app and runs the main 4 | //! uefi-test-running app inside that shell. This allows testing of protocols 5 | //! that require the shell. 6 | //! 7 | //! Launching the shell this way (rather than directly making it the boot 8 | //! executable) makes it possible to avoid the shell's built-in five second 9 | //! startup delay. 10 | 11 | #![no_std] 12 | #![no_main] 13 | 14 | extern crate alloc; 15 | 16 | use alloc::vec::Vec; 17 | use log::info; 18 | use uefi::boot::{self, LoadImageSource}; 19 | use uefi::prelude::*; 20 | use uefi::proto::BootPolicy; 21 | use uefi::proto::device_path::build::{self, DevicePathBuilder}; 22 | use uefi::proto::device_path::{DevicePath, DeviceSubType, DeviceType, LoadedImageDevicePath}; 23 | use uefi::proto::loaded_image::LoadedImage; 24 | 25 | /// Get the device path of the shell app. This is the same as the 26 | /// currently-loaded image's device path, but with the file path part changed. 27 | fn get_shell_app_device_path(storage: &mut Vec) -> &DevicePath { 28 | let loaded_image_device_path = 29 | boot::open_protocol_exclusive::(boot::image_handle()) 30 | .expect("failed to open LoadedImageDevicePath protocol"); 31 | 32 | let mut builder = DevicePathBuilder::with_vec(storage); 33 | for node in loaded_image_device_path.node_iter() { 34 | if node.full_type() == (DeviceType::MEDIA, DeviceSubType::MEDIA_FILE_PATH) { 35 | break; 36 | } 37 | builder = builder.push(&node).unwrap(); 38 | } 39 | builder = builder 40 | .push(&build::media::FilePath { 41 | path_name: cstr16!(r"efi\boot\shell.efi"), 42 | }) 43 | .unwrap(); 44 | builder.finalize().unwrap() 45 | } 46 | 47 | #[entry] 48 | fn efi_main() -> Status { 49 | uefi::helpers::init().unwrap(); 50 | 51 | let mut storage = Vec::new(); 52 | let shell_image_path = get_shell_app_device_path(&mut storage); 53 | 54 | // Load the shell app. 55 | let shell_image_handle = boot::load_image( 56 | boot::image_handle(), 57 | LoadImageSource::FromDevicePath { 58 | device_path: shell_image_path, 59 | boot_policy: BootPolicy::ExactMatch, 60 | }, 61 | ) 62 | .expect("failed to load shell app"); 63 | 64 | // Set the command line passed to the shell app so that it will run the 65 | // test-runner app. This automatically turns off the five-second delay. 66 | let mut shell_loaded_image = boot::open_protocol_exclusive::(shell_image_handle) 67 | .expect("failed to open LoadedImage protocol"); 68 | let load_options = cstr16!(r"shell.efi test_runner.efi arg1 arg2"); 69 | unsafe { 70 | shell_loaded_image.set_load_options( 71 | load_options.as_ptr().cast(), 72 | load_options.num_bytes() as u32, 73 | ); 74 | } 75 | 76 | info!("launching the shell app"); 77 | boot::start_image(shell_image_handle).expect("failed to launch the shell app"); 78 | 79 | Status::SUCCESS 80 | } 81 | --------------------------------------------------------------------------------