]> git.lizzy.rs Git - rust.git/blob - src/doc/rustc/src/platform-support/unknown-uefi.md
295dec0f0e488aeee419db6e29e380c5d534fb42
[rust.git] / src / doc / rustc / src / platform-support / unknown-uefi.md
1 # `*-unknown-uefi`
2
3 **Tier: 3**
4
5 Unified Extensible Firmware Interface (UEFI) targets for application, driver,
6 and core UEFI binaries.
7
8 Available targets:
9
10 - `aarch64-unknown-uefi`
11 - `i686-unknown-uefi`
12 - `x86_64-unknown-uefi`
13
14 ## Target maintainers
15
16 - David Rheinsberg ([@dvdhrm](https://github.com/dvdhrm))
17 - Nicholas Bishop ([@nicholasbishop](https://github.com/nicholasbishop))
18
19 ## Requirements
20
21 All UEFI targets can be used as `no-std` environments via cross-compilation.
22 Support for `std` is missing, but actively worked on. `alloc` is supported if
23 an allocator is provided by the user. No host tools are supported.
24
25 The UEFI environment resembles the environment for Microsoft Windows, with some
26 minor differences. Therefore, cross-compiling for UEFI works with the same
27 tools as cross-compiling for Windows. The target binaries are PE32+ encoded,
28 the calling convention is different for each architecture, but matches what
29 Windows uses (if the architecture is supported by Windows). The special
30 `efiapi` Rust calling-convention chooses the right ABI for the target platform
31 (`extern "C"` is incorrect on Intel targets at least). The specification has an
32 elaborate section on the different supported calling-conventions, if more
33 details are desired.
34
35 MMX, SSE, and other FP-units are disabled by default, to allow for compilation
36 of core UEFI code that runs before they are set up. This can be overridden for
37 individual compilations via rustc command-line flags. Not all firmwares
38 correctly configure those units, though, so careful inspection is required.
39
40 As native to PE32+, binaries are position-dependent, but can be relocated at
41 runtime if their desired location is unavailable. The code must be statically
42 linked. Dynamic linking is not supported. Code is shared via UEFI interfaces,
43 rather than dynamic linking. Additionally, UEFI forbids running code on
44 anything but the boot CPU/thread, nor is interrupt-usage allowed (apart from
45 the timer interrupt). Device drivers are required to use polling methods.
46
47 UEFI uses a single address-space to run all code in. Multiple applications can
48 be loaded simultaneously and are dispatched via cooperative multitasking on a
49 single stack.
50
51 By default, the UEFI targets use the `link`-flavor of the LLVM linker `lld` to
52 link binaries into the final PE32+ file suffixed with `*.efi`. The PE subsystem
53 is set to `EFI_APPLICATION`, but can be modified by passing `/subsystem:<...>`
54 to the linker. Similarly, the entry-point is to to `efi_main` but can be
55 changed via `/entry:<...>`. The panic-strategy is set to `abort`,
56
57 The UEFI specification is available online for free:
58 [UEFI Specification Directory](https://uefi.org/specifications)
59
60 ## Building rust for UEFI targets
61
62 Rust can be built for the UEFI targets by enabling them in the `rustc` build
63 configuration. Note that you can only build the standard libraries. The
64 compiler and host tools currently cannot be compiled for UEFI targets. A sample
65 configuration would be:
66
67 ```toml
68 [build]
69 build-stage = 1
70 target = ["x86_64-unknown-uefi"]
71 ```
72
73 ## Building Rust programs
74
75 Rust does not yet ship pre-compiled artifacts for this target. To compile for
76 this target, you will either need to build Rust with the target enabled (see
77 "Building rust for UEFI targets" above), or build your own copy of `core` by
78 using `build-std`, `cargo-buildx`, or similar.
79
80 A native build with the unstable `build-std`-feature can be achieved via:
81
82 ```sh
83 cargo +nightly build \
84     -Zbuild-std=core,compiler_builtins \
85     -Zbuild-std-features=compiler-builtins-mem \
86     --target x86_64-unknown-uefi
87 ```
88
89 Alternatively, you can install `cargo-xbuild` via
90 `cargo install --force cargo-xbuild` and build for the UEFI targets via:
91
92 ```sh
93 cargo \
94     +nightly \
95     xbuild \
96     --target x86_64-unknown-uefi
97 ```
98
99 ## Testing
100
101 UEFI applications can be copied into the ESP on any UEFI system and executed
102 via the firmware boot menu. The qemu suite allows emulating UEFI systems and
103 executing UEFI applications as well. See its documentation for details.
104
105 The [uefi-run](https://github.com/Richard-W/uefi-run) rust tool is a simple
106 wrapper around `qemu` that can spawn UEFI applications in qemu. You can install
107 it via `cargo install uefi-run` and execute qemu applications as
108 `uefi-run ./application.efi`.
109
110 ## Cross-compilation toolchains and C code
111
112 There are 3 common ways to compile native C code for UEFI targets:
113
114 - Use the official SDK by Intel:
115   [Tianocore/EDK2](https://github.com/tianocore/edk2). This supports a
116   multitude of platforms, comes with the full specification transposed into C,
117   lots of examples and build-system integrations. This is also the only
118   officially supported platform by Intel, and is used by many major firmware
119   implementations. Any code compiled via the SDK is compatible to rust binaries
120   compiled for the UEFI targets. You can link them directly into your rust
121   binaries, or call into each other via UEFI protocols.
122 - Use the **GNU-EFI** suite. This approach is used by many UEFI applications
123   in the Linux/OSS ecosystem. The GCC compiler is used to compile ELF binaries,
124   and linked with a pre-loader that converts the ELF binary to PE32+
125   **at runtime**. You can combine such binaries with the rust UEFI targets only
126   via UEFI protocols. Linking both into the same executable will fail, since
127   one is an ELF executable, and one a PE32+. If linking to **GNU-EFI**
128   executables is desired, you must compile your rust code natively for the same
129   GNU target as **GNU-EFI** and use their pre-loader. This requires careful
130   consideration about which calling-convention to use when calling into native
131   UEFI protocols, or calling into linked **GNU-EFI** code (similar to how these
132   differences need to be accounted for when writing **GNU-EFI** C code).
133 - Use native Windows targets. This means compiling your C code for the Windows
134   platform as if it was the UEFI platform. This works for static libraries, but
135   needs adjustments when linking into an UEFI executable. You can, however,
136   link such static libraries seamlessly into rust code compiled for UEFI
137   targets. Be wary of any includes that are not specifically suitable for UEFI
138   targets (especially the C standard library includes are not always
139   compatible). Freestanding compilations are recommended to avoid
140   incompatibilites.
141
142 ## Ecosystem
143
144 The rust language has a long history of supporting UEFI targets. Many crates
145 have been developed to provide access to UEFI protocols and make UEFI
146 programming more ergonomic in rust. The following list is a short overview (in
147 alphabetical ordering):
148
149 - **efi**: *Ergonomic Rust bindings for writing UEFI applications*. Provides
150   _rustified_ access to UEFI protocols, implements allocators and a safe
151   environment to write UEFI applications.
152 - **r-efi**: *UEFI Reference Specification Protocol Constants and Definitions*.
153   A pure transpose of the UEFI specification into rust. This provides the raw
154   definitions from the specification, without any extended helpers or
155   _rustification_. It serves as baseline to implement any more elaborate rust
156   UEFI layers.
157 - **uefi-rs**: *Safe and easy-to-use wrapper for building UEFI apps*. An
158   elaborate library providing safe abstractions for UEFI protocols and
159   features. It implements allocators and provides an execution environment to
160   UEFI applications written in rust.
161 - **uefi-run**: *Run UEFI applications*. A small wrapper around _qemu_ to spawn
162   UEFI applications in an emulated `x86_64` machine.
163
164 ## Example: Freestanding
165
166 The following code is a valid UEFI application returning immediately upon
167 execution with an exit code of 0. A panic handler is provided. This is executed
168 by rust on panic. For simplicity, we simply end up in an infinite loop.
169
170 Note that as of rust-1.31.0, all features used here are stabilized. No unstable
171 features are required, nor do we rely on nightly compilers. However, if you do
172 not compile rustc for the UEFI targets, you need a nightly compiler to support
173 the `-Z build-std` flag.
174
175 This example can be compiled as binary crate via `cargo`:
176
177 ```sh
178 cargo +nightly build \
179     -Zbuild-std=core,compiler_builtins \
180     -Zbuild-std-features=compiler-builtins-mem \
181     --target x86_64-unknown-uefi
182 ```
183
184 ```rust,ignore (platform-specific,eh-personality-is-unstable)
185 #![no_main]
186 #![no_std]
187
188 #[panic_handler]
189 fn panic_handler(_info: &core::panic::PanicInfo) -> ! {
190     loop {}
191 }
192
193 #[export_name = "efi_main"]
194 pub extern "C" fn main(_h: *mut core::ffi::c_void, _st: *mut core::ffi::c_void) -> usize {
195     0
196 }
197 ```
198
199 ## Example: Hello World
200
201 This is an example UEFI application that prints "Hello World!", then waits for
202 key input before it exits. It serves as base example how to write UEFI
203 applications without any helper modules other than the standalone UEFI protocol
204 definitions provided by the `r-efi` crate.
205
206 This extends the "Freestanding" example and builds upon its setup. See there
207 for instruction how to compile this as binary crate.
208
209 Note that UEFI uses UTF-16 strings. Since rust literals are UTF-8, we have to
210 use an open-coded, zero-terminated, UTF-16 array as argument to
211 `output_string()`. Similarly to the panic handler, real applications should
212 rather use UTF-16 modules.
213
214 ```rust,ignore (platform-specific,eh-personality-is-unstable)
215 #![no_main]
216 #![no_std]
217
218 use r_efi::efi;
219
220 #[panic_handler]
221 fn panic_handler(_info: &core::panic::PanicInfo) -> ! {
222     loop {}
223 }
224
225 #[export_name = "efi_main"]
226 pub extern "C" fn main(_h: efi::Handle, st: *mut efi::SystemTable) -> efi::Status {
227     let s = [
228         0x0048u16, 0x0065u16, 0x006cu16, 0x006cu16, 0x006fu16, // "Hello"
229         0x0020u16, //                                             " "
230         0x0057u16, 0x006fu16, 0x0072u16, 0x006cu16, 0x0064u16, // "World"
231         0x0021u16, //                                             "!"
232         0x000au16, //                                             "\n"
233         0x0000u16, //                                             NUL
234     ];
235
236     // Print "Hello World!".
237     let r =
238         unsafe { ((*(*st).con_out).output_string)((*st).con_out, s.as_ptr() as *mut efi::Char16) };
239     if r.is_error() {
240         return r;
241     }
242
243     // Wait for key input, by waiting on the `wait_for_key` event hook.
244     let r = unsafe {
245         let mut x: usize = 0;
246         ((*(*st).boot_services).wait_for_event)(1, &mut (*(*st).con_in).wait_for_key, &mut x)
247     };
248     if r.is_error() {
249         return r;
250     }
251
252     efi::Status::SUCCESS
253 }
254 ```