Skip to content

Commit

Permalink
Merge pull request #1331 from phip1611/uefi-std
Browse files Browse the repository at this point in the history
uefi std: add example and add book chapter
  • Loading branch information
nicholasbishop authored Aug 21, 2024
2 parents 93e864e + 8b09be3 commit fa36299
Show file tree
Hide file tree
Showing 12 changed files with 134 additions and 0 deletions.
13 changes: 13 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ members = [
"uefi",
"uefi-macros",
"uefi-raw",
"uefi-std-example",
"uefi-test-runner",
"xtask",
]
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
29 changes: 29 additions & 0 deletions book/src/how_to/rust-std.md
Original file line number Diff line number Diff line change
@@ -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 [`<repo>/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}}
```
1 change: 1 addition & 0 deletions shell.nix
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pkgs.mkShell {
rustToolchain

# Other
mdbook
yamlfmt
which # used by "cargo xtask fmt"
];
Expand Down
11 changes: 11 additions & 0 deletions uefi-std-example/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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 }
18 changes: 18 additions & 0 deletions uefi-std-example/README.md
Original file line number Diff line number Diff line change
@@ -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/<debug|release>/uefi-std-example.efi`.
31 changes: 31 additions & 0 deletions uefi-std-example/src/main.rs
Original file line number Diff line number Diff line change
@@ -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);
}
9 changes: 9 additions & 0 deletions uefi/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# 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`

# uefi - 0.31.0 (2024-08-21)

Expand Down Expand Up @@ -603,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
11 changes: 11 additions & 0 deletions uefi/src/data_types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,17 @@ use core::ptr::{self, NonNull};
pub struct Handle(NonNull<c_void>);

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<c_void>) -> Self {
Self(ptr)
}

/// Creates a new [`Handle`] from a raw address. The address might
/// come from the Multiboot2 information structure or something similar.
///
Expand Down

0 comments on commit fa36299

Please sign in to comment.