r_efi/lib.rs
1//! UEFI Reference Specification Protocol Constants and Definitions
2//!
3//! This project provides protocol constants and definitions as defined in the UEFI Reference
4//! Specification. The aim is to provide all these constants as C-ABI compatible imports to rust.
5//! Safe rust abstractions over the UEFI API are out of scope of this project. That is, the
6//! purpose is really just to extract all the bits and pieces from the specification and provide
7//! them as rust types and constants.
8//!
9//! While we strongly recommend using safe abstractions to interact with UEFI systems, this
10//! project serves both as base to write those abstractions, but also as last resort if you have
11//! to deal with quirks and peculiarities of UEFI systems directly. Therefore, several examples
12//! are included, which show how to interact with UEFI systems from rust. These serve both as
13//! documentation for anyone interested in how the system works, but also as base for anyone
14//! implementing safe abstractions on top.
15//!
16//! # Target Configuration
17//!
18//! Rust code can be compiled natively for UEFI systems. However, you are quite unlikely to have a
19//! rust compiler running in an UEFI environment. Therefore, you will most likely want to cross
20//! compile your rust code for UEFI systems. To do this, you need a target-configuration for UEFI
21//! systems. As of rust-1.61, upstream rust includes the following UEFI targets:
22//!
23//! * `aarch64-unknown-uefi`: A native UEFI target for aarch64 systems (64bit ARM).
24//! * `i686-unknown-uefi`: A native UEFI target for i686 systems (32bit Intel x86).
25//! * `x86_64-unknown-uefi`: A native UEFI target for x86-64 systems (64bit Intel x86).
26//!
27//! If none of these targets match your architecture, you have to create the target specification
28//! yourself. Feel free to contact the `r-efi` project for help.
29//!
30//! # Transpose Guidelines
31//!
32//! The UEFI specification provides C language symbols and definitions of all
33//! its protocols and features. Those are integral parts of the specification
34//! and UEFI programming is often tightly coupled with the C language. For
35//! better compatibility to existing UEFI documentation, all the rust symbols
36//! are transposed from C following strict rules, aiming for close similarity
37//! to specification. This section gives a rationale on some of the less
38//! obvious choices and tries to describe as many of those rules as possible.
39//!
40//! * `no enums`: Rust enums do not allow random discriminant values. However,
41//! many UEFI enumerations use reserved ranges for vendor defined values.
42//! These cannot be represented with rust enums in an efficient manner.
43//! Hence, any enumerations are turned into rust constants with an
44//! accompanying type alias.
45//!
46//! A detailed discussion can be found in:
47//!
48//! ```gitlog
49//! commit 401a91901e860a5c0cd0f92b75dda0a72cf65322
50//! Author: David Rheinsberg <david.rheinsberg@gmail.com>
51//! Date: Wed Apr 21 12:07:07 2021 +0200
52//!
53//! r-efi: convert enums to constants
54//! ```
55//!
56//! * `no incomplete types`: Several structures use incomplete structure types
57//! by using an unbound array as last member. While rust can easily
58//! represent those within its type-system, such structures become DSTs,
59//! hence even raw pointers to them become fat-pointers, and would thus
60//! violate the UEFI ABI.
61//!
62//! Instead, we use const-generics to allow compile-time adjustment of the
63//! variable-sized structures, with a default value of 0. This allows
64//! computing different sizes of the structures without any runtime overhead.
65//!
66//! * `nullable callbacks as Option`: Rust has no raw function pointers, but
67//! just normal Rust function pointers. Those, however, have no valid null
68//! value. The Rust ABI guarantees that `Option<fn ...>` is an C-ABI
69//! compatible replacement for nullable function pointers, with `None` being
70//! mapped to `NULL`. Hence, whenever UEFI APIs require nullable function
71//! pointers, we use `Option<fn ...>`.
72//!
73//! * `prefer *mut over *const`: Whenever we transpose pointers from the
74//! specification into Rust, we default to `*mut`. So far, there is no
75//! reason to use `*const`. We prefer `*mut T`, because:
76//!
77//! * it is invariant over `T`, unlike `*const T`, which is covariant over
78//! `T`. This variance is useful when treating the raw pointer like a
79//! shared reference (which is also covariant over `T`). However, if the
80//! raw pointer can be aliases by mutable pointers somewhere in the UEFI
81//! stack, the variance might no longer apply. Using `*mut T` avoids this
82//! default covariance, and requires callers to introduce manually if
83//! desired.
84//! * it cannot be automatically coerced from shared references. This
85//! coercion is not necessarily correct, given that `const T *` pointers
86//! in C do not share the immutability guarantee of Rust shared
87//! references. By using `*mut T` an explicit pointer cast is required,
88//! which we definitely want.
89//! * it correctly conveys mutability to Miri Stacked Borrows. While Tree
90//! Borrows do not distinguish raw pointers, Stacked Borrows do, and they
91//! require a pointer to originate from a mutable reference if mutability
92//! is desired.
93//!
94//! Lastly, note that `*mut` and `*const` can be `as`-casted in both
95//! directions without violating any Rust guarantees. Any UB concerns always
96//! stem from the safety guarantees of the surrounding code, not of the
97//! raw-pointer handling.
98//!
99//! * `default to unsafe fn`: Always use `unsafe fn` for function prototypes.
100//! UEFI makes no guarantees about global state, as such any implementation
101//! of any UEFI prototype might introduce unsafety. Use `unsafe fn`
102//! unconditionally, so the prototypes can be used for externally provided
103//! code.
104//!
105//! # Specification Details
106//!
107//! This section lists errata of, and general comments on, the UEFI
108//! specification relevant to the development of `r-efi`:
109//!
110//! * The `Unload` function-pointer of the LoadedImageProtocol can be `NULL`,
111//! despite the protocol documentation lacking any mention of this. Other
112//! parts of the specification refer to images lacking an unload function,
113//! but there is no explicit documentation how this manifests in the
114//! protocol structure. EDK2 assumes `NULL` indicates a lack of unload
115//! function, and an errata has been submitted to the UEFI forum.
116//!
117//! * The specification mandates an 8-byte alignment for the `GUID` structure
118//! However, all widespread implementations (including EDK2) use a 4-byte
119//! alignment. An errata has been reported to EDK2 (still pending).
120//!
121//! # Examples
122//!
123//! To write free-standing UEFI applications, you need to disable the entry-point provided by rust
124//! and instead provide your own. Most target-configurations look for a function called `efi_main`
125//! during linking and set it as entry point. If you use the target-configurations provided with
126//! upstream rust, they will pick the function called `efi_main` as entry-point.
127//!
128//! The following example shows a minimal UEFI application, which simply returns success upon
129//! invocation. Note that you must provide your own panic-handler when running without `libstd`.
130//! In our case, we use a trivial implementation that simply loops forever.
131//!
132//! ```ignore
133//! #![no_main]
134//! #![no_std]
135//!
136//! use r_efi::efi;
137//!
138//! #[panic_handler]
139//! fn panic_handler(_info: &core::panic::PanicInfo) -> ! {
140//! loop {}
141//! }
142//!
143//! #[export_name = "efi_main"]
144//! pub extern fn main(_h: efi::Handle, _st: *mut efi::SystemTable) -> efi::Status {
145//! efi::Status::SUCCESS
146//! }
147//! ```
148
149// Mark this crate as `no_std`. We have no std::* dependencies (and we better don't have them),
150// so no reason to require it. This does not mean that you cannot use std::* with UEFI. You have
151// to port it to UEFI first, though.
152//
153// In case of unit-test compilation, we pull in `std` and drop the `no_std` marker. This allows
154// basic unit-tests on the compilation host. For integration tests, we have separate compilation
155// units, so they will be unaffected by this.
156#![cfg_attr(not(test), no_std)]
157
158// Import the different core modules. We separate them into different modules to make it easier to
159// work on them and describe what each part implements. This is different to the reference
160// implementation, which uses a flat namespace due to its origins in the C language. For
161// compatibility, we provide this flat namespace as well. See the `efi` submodule.
162#[macro_use]
163pub mod base;
164#[macro_use]
165pub mod hii;
166#[macro_use]
167pub mod system;
168
169// Import the protocols. Each protocol is separated into its own module, readily imported by the
170// meta `protocols` module. Note that this puts all symbols into their respective protocol
171// namespace, thus clearly separating them (unlike the UEFI Specification, which more often than
172// not violates its own namespacing).
173pub mod protocols;
174
175// Import vendor protocols. They are just like protocols in `protocols`, but
176// separated for better namespacing.
177pub mod vendor;
178
179/// Flat EFI Namespace
180///
181/// The EFI namespace re-exports all symbols in a single, flat namespace. This allows mirroring
182/// the entire EFI namespace as given in the specification and makes it easier to refer to them
183/// with the same names as the reference C implementation.
184///
185/// Note that the individual protocols are put into submodules. The specification does this in
186/// most parts as well (by prefixing all symbols). This is not true in all cases, as the
187/// specification suffers from lack of namespaces in the reference C language. However, we decided
188/// to namespace the remaining bits as well, for better consistency throughout the API. This
189/// should be self-explanatory in nearly all cases.
190pub mod efi {
191 pub use crate::base::*;
192 pub use crate::system::*;
193
194 pub use crate::hii;
195 pub use crate::protocols;
196 pub use crate::vendor;
197}