From ff34fcbe86182f4acafd034e955243ecd4d286a5 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Wed, 14 Aug 2024 20:27:01 +0200 Subject: [PATCH 1/6] uefi: handle: add constructor This is a prerequisite for the next step. --- uefi/CHANGELOG.md | 2 ++ uefi/src/data_types/mod.rs | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/uefi/CHANGELOG.md b/uefi/CHANGELOG.md index 32429126e..f7b2db0c2 100644 --- a/uefi/CHANGELOG.md +++ b/uefi/CHANGELOG.md @@ -1,5 +1,7 @@ # uefi - [Unreleased] +## Added +- Added `Handle::new` # uefi - 0.31.0 (2024-08-21) diff --git a/uefi/src/data_types/mod.rs b/uefi/src/data_types/mod.rs index 33cd201ff..463fa7ff7 100644 --- a/uefi/src/data_types/mod.rs +++ b/uefi/src/data_types/mod.rs @@ -13,6 +13,17 @@ use core::ptr::{self, NonNull}; pub struct Handle(NonNull); impl Handle { + /// Creates a new [`Handle`]. + /// + /// # Safety + /// This function is unsafe because the caller must be sure that the pointer + /// is valid. Otherwise, further operations on the object might result in + /// undefined behaviour, even if the methods aren't marked as unsafe. + #[must_use] + pub const unsafe fn new(ptr: NonNull) -> Self { + Self(ptr) + } + /// Creates a new [`Handle`] from a raw address. The address might /// come from the Multiboot2 information structure or something similar. /// From 47aaec7b822c848ce77e8e21babf164248af29ba Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Thu, 15 Aug 2024 12:23:19 +0200 Subject: [PATCH 2/6] workspace: add uefi-std-example member --- Cargo.lock | 7 +++++++ Cargo.toml | 1 + README.md | 2 ++ uefi-std-example/Cargo.toml | 11 +++++++++++ uefi-std-example/README.md | 18 ++++++++++++++++++ uefi-std-example/src/main.rs | 31 +++++++++++++++++++++++++++++++ 6 files changed, 70 insertions(+) create mode 100644 uefi-std-example/Cargo.toml create mode 100644 uefi-std-example/README.md create mode 100644 uefi-std-example/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index c40e334fb..de598e93b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -903,6 +903,13 @@ dependencies = [ "uguid", ] +[[package]] +name = "uefi-std-example" +version = "0.1.0" +dependencies = [ + "uefi", +] + [[package]] name = "uefi-test-runner" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index 89e9533f5..57263c679 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ members = [ "uefi", "uefi-macros", "uefi-raw", + "uefi-std-example", "uefi-test-runner", "xtask", ] diff --git a/README.md b/README.md index f352b7632..01c656cf2 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,8 @@ This project contains multiple sub-crates: Specification. Safe wrappers for these types are provided by the `uefi` crate. The raw types are suitable for implementing UEFI firmware. +- `uefi-std-example`: Example UEFI app but as Rust standard binary. + - `uefi-test-runner`: a UEFI application that runs unit / integration tests. [log]: https://github.com/rust-lang-nursery/log diff --git a/uefi-std-example/Cargo.toml b/uefi-std-example/Cargo.toml new file mode 100644 index 000000000..5a2e3e293 --- /dev/null +++ b/uefi-std-example/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "uefi-std-example" +version = "0.1.0" +authors = ["The Rust OSDev team"] +publish = false +edition = "2021" + +[dependencies] +# Attention: Don't activate the panic_handler feature, as it will clash with +# the one coming from `std`. +uefi = { path = "../uefi", features = ["alloc"], default-features = false } diff --git a/uefi-std-example/README.md b/uefi-std-example/README.md new file mode 100644 index 000000000..dc57c41bd --- /dev/null +++ b/uefi-std-example/README.md @@ -0,0 +1,18 @@ +# Minimal Rust App using `std` and `uefi` + +Minimal example of a "standard Rust application" that showcases how `uefi` can +be utilized and enhance the developers experience, when `std` is available. + +For simplicity, this example is minimal and the documentation is focused on +`x86_64-unknown-uefi`. However, it works similar for other supported UEFI +platforms. + +## Build + +Build the app using +`$ cargo +nightly build --target x86_64-unknown-uefi`. To build it from the root +directory (the Cargo workspace), append `-p uefi-std-example`. + +## Run + +The resulting `.efi` file can be found in `target/x86_64-unknown-uefi//uefi-std-example.efi`. diff --git a/uefi-std-example/src/main.rs b/uefi-std-example/src/main.rs new file mode 100644 index 000000000..6bb17c61c --- /dev/null +++ b/uefi-std-example/src/main.rs @@ -0,0 +1,31 @@ +// Note: In Rust 1.82.0-nightly and before, the `uefi_std` feature is +// required for accessing `std::os::uefi::env::*`. The other default +// functionality doesn't need a nightly toolchain (with Rust 1.80 and later), +// but with that limited functionality you - currently - also can't integrate +// the `uefi` crate. +#![feature(uefi_std)] + +use std::os::uefi as uefi_std; +use uefi::runtime::ResetType; +use uefi::{Handle, Status}; + +/// Performs the necessary setup code for the `uefi` crate. +fn setup_uefi_crate() { + let st = uefi_std::env::system_table(); + let ih = uefi_std::env::image_handle(); + + // Mandatory setup code for `uefi` crate. + unsafe { + uefi::table::set_system_table(st.as_ptr().cast()); + + let ih = Handle::from_ptr(ih.as_ptr().cast()).unwrap(); + uefi::boot::set_image_handle(ih); + } +} + +fn main() { + println!("Hello World from uefi_std"); + setup_uefi_crate(); + println!("UEFI-Version is {}", uefi::system::uefi_revision()); + uefi::runtime::reset(ResetType::SHUTDOWN, Status::SUCCESS, None); +} From 547e53b05dd6cbe33e3c2b58be64513382b8a7b6 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Wed, 14 Aug 2024 20:27:24 +0200 Subject: [PATCH 3/6] ci: build uefi-std-example workspace member --- .github/workflows/rust.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 0f09f2701..dfb4abe74 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -170,3 +170,16 @@ jobs: run: | rustup component add miri cargo xtask miri + # Builds a Rust standard binary using the `std` impl for UEFI, rather than + # creating a `no_std` + `no_main` binary. + build_standard_uefi_binary: + name: Build Standard Binary (nightly) + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v4 + - name: Set toolchain + run: cp .github/workflows/nightly_toolchain.toml rust-toolchain.toml + - uses: Swatinem/rust-cache@v2 + - name: Build + run: cargo +nightly build --target x86_64-unknown-uefi --verbose -p uefi-std-example From 7212462a9985eb39614eb110db9412a8b07f654f Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Thu, 15 Aug 2024 12:24:16 +0200 Subject: [PATCH 4/6] doc: add rust-std chapter to book --- book/src/SUMMARY.md | 1 + book/src/how_to/rust-std.md | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 book/src/how_to/rust-std.md diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index bf8972087..bcdd27a65 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -10,6 +10,7 @@ - [Using Protocols](how_to/protocols.md) - [Drawing to the Screen](how_to/drawing.md) - [Building drivers](how_to/building_drivers.md) + - [Combining Rust `std` with `uefi`](how_to/rust-std.md) - [Concepts](concepts/introduction.md) - [Boot Stages](concepts/boot_stages.md) - [Tables](concepts/tables.md) diff --git a/book/src/how_to/rust-std.md b/book/src/how_to/rust-std.md new file mode 100644 index 000000000..5d3284298 --- /dev/null +++ b/book/src/how_to/rust-std.md @@ -0,0 +1,29 @@ +# Combining Rust `std` with `uefi` + +## TL;DR + +In Mid-2024, we recommend to stick to our normal guide. Use this document as +guide and outlook for the future of UEFI and Rust. + +## About + +Programs created with the `uefi` crate are typically created with `#![no_std]` +and `#![no_main]`. A `#![no_std]` crate can use the `core` and `alloc` parts of +Rust's standard library, but not `std`. A `#![no_main]` executable does not use +the standard main entry point, and must define its own entry point; `uefi` +provides the `#[entry]` macro for this purpose. + +Rust has added partial support for building UEFI executables without +`#![no_std]` and `#![no_main]`, thus, the standard way. Some functionality +requires a nightly toolchain, they are gated by the `uefi_std` feature (Rust +language feature, not `uefi` crate feature). Follow the +[tracking issue](https://github.com/rust-lang/rust/issues/100499) for details. + +## Code Example + +Please refer to [`/uefi-std-example`](/uefi-std-example/README.md) to +see a specific example. The relevant `main.rs` looks as follows: + +```rust +{{#include ../../../uefi-std-example/src/main.rs}} +``` From 2e661655866580f15d7fb3082920634954fd02b8 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Thu, 15 Aug 2024 12:24:25 +0200 Subject: [PATCH 5/6] nix: add mdbook tool to locally build and serve the book --- shell.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/shell.nix b/shell.nix index f16ba337c..72d5f9328 100644 --- a/shell.nix +++ b/shell.nix @@ -21,6 +21,7 @@ pkgs.mkShell { rustToolchain # Other + mdbook yamlfmt which # used by "cargo xtask fmt" ]; From 8b09be32f968b7bd7882fc002388138a2069d29b Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Thu, 15 Aug 2024 12:21:56 +0200 Subject: [PATCH 6/6] uefi: update CHANGELOG --- uefi/CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/uefi/CHANGELOG.md b/uefi/CHANGELOG.md index f7b2db0c2..a451e1fdc 100644 --- a/uefi/CHANGELOG.md +++ b/uefi/CHANGELOG.md @@ -1,5 +1,9 @@ # uefi - [Unreleased] +We added documentation to `lib.rs` and the [uefi-rs book] about how +`uefi` compares to "standard Rust binaries" for UEFI (those using `std`), and +how to integrate the `uefi` crate into them. + ## Added - Added `Handle::new` @@ -605,3 +609,6 @@ Rust 1.68 or higher. truncated and could result in out-of-bounds reads. - Fixed size check for file info types so that alignment padding is taken into account. This fixes potential out-of-bounds writes. + + +[uefi-rs book]: https://rust-osdev.github.io/uefi-rs/HEAD