Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit c7224e3

Browse files
committedSep 24, 2023
Auto merge of rust-lang#105861 - Ayush1325:uefi-std-minimial, r=workingjubilee
Add Minimal Std implementation for UEFI # Implemented modules: 1. alloc 2. os_str 3. env 4. math # Related Links Tracking Issue: rust-lang#100499 API Change Proposal: rust-lang/libs-team#87 # Additional Information This was originally part of rust-lang#100316. Since that PR was becoming too unwieldy and cluttered, and with suggestion from `@dvdhrm,` I have extracted a minimal std implementation to this PR. The example in `src/doc/rustc/src/platform-support/unknown-uefi.md` has been tested for `x86_64-unknown-uefi` and `i686-unknown-uefi` in OVMF. It would be great if someone more familiar with AARCH64 can help with testing for that target. Signed-off-by: Ayush Singh <ayushsingh1325@gmail.com>
2 parents 44bd31c + 984ecef commit c7224e3

File tree

25 files changed

+957
-21
lines changed

25 files changed

+957
-21
lines changed
 

‎Cargo.lock

+23
Original file line numberDiff line numberDiff line change
@@ -3006,6 +3006,27 @@ dependencies = [
30063006
"proc-macro2",
30073007
]
30083008

3009+
[[package]]
3010+
name = "r-efi"
3011+
version = "4.2.0"
3012+
source = "registry+https://github.com/rust-lang/crates.io-index"
3013+
checksum = "575fc2d9b3da54adbdfaddf6eca48fec256d977c8630a1750b8991347d1ac911"
3014+
dependencies = [
3015+
"compiler_builtins",
3016+
"rustc-std-workspace-core",
3017+
]
3018+
3019+
[[package]]
3020+
name = "r-efi-alloc"
3021+
version = "1.0.0"
3022+
source = "registry+https://github.com/rust-lang/crates.io-index"
3023+
checksum = "31d6f09fe2b6ad044bc3d2c34ce4979796581afd2f1ebc185837e02421e02fd7"
3024+
dependencies = [
3025+
"compiler_builtins",
3026+
"r-efi",
3027+
"rustc-std-workspace-core",
3028+
]
3029+
30093030
[[package]]
30103031
name = "rand"
30113032
version = "0.8.5"
@@ -5012,6 +5033,8 @@ dependencies = [
50125033
"panic_abort",
50135034
"panic_unwind",
50145035
"profiler_builtins",
5036+
"r-efi",
5037+
"r-efi-alloc",
50155038
"rand",
50165039
"rand_xorshift",
50175040
"rustc-demangle",

‎compiler/rustc_codegen_ssa/src/base.rs

+22-6
Original file line numberDiff line numberDiff line change
@@ -420,9 +420,11 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
420420
rust_main_def_id: DefId,
421421
entry_type: EntryFnType,
422422
) -> Bx::Function {
423-
// The entry function is either `int main(void)` or `int main(int argc, char **argv)`,
424-
// depending on whether the target needs `argc` and `argv` to be passed in.
425-
let llfty = if cx.sess().target.main_needs_argc_argv {
423+
// The entry function is either `int main(void)` or `int main(int argc, char **argv)`, or
424+
// `usize efi_main(void *handle, void *system_table)` depending on the target.
425+
let llfty = if cx.sess().target.os.contains("uefi") {
426+
cx.type_func(&[cx.type_ptr(), cx.type_ptr()], cx.type_isize())
427+
} else if cx.sess().target.main_needs_argc_argv {
426428
cx.type_func(&[cx.type_int(), cx.type_ptr()], cx.type_int())
427429
} else {
428430
cx.type_func(&[], cx.type_int())
@@ -485,8 +487,12 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
485487
};
486488

487489
let result = bx.call(start_ty, None, None, start_fn, &args, None);
488-
let cast = bx.intcast(result, cx.type_int(), true);
489-
bx.ret(cast);
490+
if cx.sess().target.os.contains("uefi") {
491+
bx.ret(result);
492+
} else {
493+
let cast = bx.intcast(result, cx.type_int(), true);
494+
bx.ret(cast);
495+
}
490496

491497
llfn
492498
}
@@ -497,7 +503,17 @@ fn get_argc_argv<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
497503
cx: &'a Bx::CodegenCx,
498504
bx: &mut Bx,
499505
) -> (Bx::Value, Bx::Value) {
500-
if cx.sess().target.main_needs_argc_argv {
506+
if cx.sess().target.os.contains("uefi") {
507+
// Params for UEFI
508+
let param_handle = bx.get_param(0);
509+
let param_system_table = bx.get_param(1);
510+
let arg_argc = bx.const_int(cx.type_isize(), 2);
511+
let arg_argv = bx.alloca(cx.type_array(cx.type_ptr(), 2), Align::ONE);
512+
bx.store(param_handle, arg_argv, Align::ONE);
513+
let arg_argv_el1 = bx.gep(cx.type_ptr(), arg_argv, &[bx.const_int(cx.type_int(), 1)]);
514+
bx.store(param_system_table, arg_argv_el1, Align::ONE);
515+
(arg_argc, arg_argv)
516+
} else if cx.sess().target.main_needs_argc_argv {
501517
// Params from native `main()` used as args for rust start function
502518
let param_argc = bx.get_param(0);
503519
let param_argv = bx.get_param(1);

‎compiler/rustc_target/src/spec/uefi_msvc_base.rs

+1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ pub fn opts() -> TargetOptions {
4646
stack_probes: StackProbeType::Call,
4747
singlethread: true,
4848
linker: Some("rust-lld".into()),
49+
entry_name: "efi_main".into(),
4950
..base
5051
}
5152
}

‎compiler/rustc_target/src/spec/x86_64_unknown_uefi.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@
55
// The win64 ABI is used. It differs from the sysv64 ABI, so we must use a windows target with
66
// LLVM. "x86_64-unknown-windows" is used to get the minimal subset of windows-specific features.
77

8-
use crate::spec::Target;
8+
use crate::{abi::call::Conv, spec::Target};
99

1010
pub fn target() -> Target {
1111
let mut base = super::uefi_msvc_base::opts();
1212
base.cpu = "x86-64".into();
1313
base.plt_by_default = false;
1414
base.max_atomic_width = Some(64);
15+
base.entry_abi = Conv::X86_64Win64;
1516

1617
// We disable MMX and SSE for now, even though UEFI allows using them. Problem is, you have to
1718
// enable these CPU features explicitly before their first use, otherwise their instructions

‎library/panic_abort/src/lib.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ pub unsafe fn __rust_start_panic(_payload: &mut dyn PanicPayload) -> u32 {
4444
}
4545
} else if #[cfg(any(target_os = "hermit",
4646
all(target_vendor = "fortanix", target_env = "sgx"),
47-
target_os = "xous"
47+
target_os = "xous",
48+
target_os = "uefi",
4849
))] {
4950
unsafe fn abort() -> ! {
5051
// call std::sys::abort_internal

‎library/std/Cargo.toml

+4
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ hermit-abi = { version = "0.3.2", features = ['rustc-dep-of-std'], public = true
4848
[target.'cfg(target_os = "wasi")'.dependencies]
4949
wasi = { version = "0.11.0", features = ['rustc-dep-of-std'], default-features = false }
5050

51+
[target.'cfg(target_os = "uefi")'.dependencies]
52+
r-efi = { version = "4.2.0", features = ['rustc-dep-of-std']}
53+
r-efi-alloc = { version = "1.0.0", features = ['rustc-dep-of-std']}
54+
5155
[features]
5256
backtrace = [
5357
"gimli-symbolize",

‎library/std/build.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ fn main() {
3939
|| target.contains("nto")
4040
|| target.contains("xous")
4141
|| target.contains("hurd")
42+
|| target.contains("uefi")
4243
// See src/bootstrap/synthetic_targets.rs
4344
|| env::var("RUSTC_BOOTSTRAP_SYNTHETIC_TARGET").is_ok()
4445
{
@@ -51,7 +52,6 @@ fn main() {
5152
// - mipsel-sony-psp
5253
// - nvptx64-nvidia-cuda
5354
// - arch=avr
54-
// - uefi (x86_64-unknown-uefi, i686-unknown-uefi)
5555
// - JSON targets
5656
// - Any new targets that have not been explicitly added above.
5757
println!("cargo:rustc-cfg=feature=\"restricted-std\"");

‎library/std/src/io/error.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
#[cfg(test)]
22
mod tests;
33

4-
#[cfg(target_pointer_width = "64")]
4+
#[cfg(all(target_pointer_width = "64", not(target_os = "uefi")))]
55
mod repr_bitpacked;
6-
#[cfg(target_pointer_width = "64")]
6+
#[cfg(all(target_pointer_width = "64", not(target_os = "uefi")))]
77
use repr_bitpacked::Repr;
88

9-
#[cfg(not(target_pointer_width = "64"))]
9+
#[cfg(any(not(target_pointer_width = "64"), target_os = "uefi"))]
1010
mod repr_unpacked;
11-
#[cfg(not(target_pointer_width = "64"))]
11+
#[cfg(any(not(target_pointer_width = "64"), target_os = "uefi"))]
1212
use repr_unpacked::Repr;
1313

1414
use crate::error;

‎library/std/src/os/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,8 @@ pub mod solid;
142142
#[cfg(target_os = "tvos")]
143143
#[path = "ios/mod.rs"]
144144
pub(crate) mod tvos;
145+
#[cfg(target_os = "uefi")]
146+
pub mod uefi;
145147
#[cfg(target_os = "vita")]
146148
pub mod vita;
147149
#[cfg(target_os = "vxworks")]

‎library/std/src/os/uefi/env.rs

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
//! UEFI-specific extensions to the primitives in `std::env` module
2+
3+
#![unstable(feature = "uefi_std", issue = "100499")]
4+
5+
use crate::sync::atomic::{AtomicBool, AtomicPtr, Ordering};
6+
use crate::{ffi::c_void, ptr::NonNull};
7+
8+
static SYSTEM_TABLE: AtomicPtr<c_void> = AtomicPtr::new(crate::ptr::null_mut());
9+
static IMAGE_HANDLE: AtomicPtr<c_void> = AtomicPtr::new(crate::ptr::null_mut());
10+
// Flag to check if BootServices are still valid.
11+
// Start with assuming that they are not available
12+
static BOOT_SERVICES_FLAG: AtomicBool = AtomicBool::new(false);
13+
14+
/// Initializes the global System Table and Image Handle pointers.
15+
///
16+
/// The standard library requires access to the UEFI System Table and the Application Image Handle
17+
/// to operate. Those are provided to UEFI Applications via their application entry point. By
18+
/// calling `init_globals()`, those pointers are retained by the standard library for future use.
19+
/// Thus this function must be called before any of the standard library services are used.
20+
///
21+
/// The pointers are never exposed to any entity outside of this application and it is guaranteed
22+
/// that, once the application exited, these pointers are never dereferenced again.
23+
///
24+
/// Callers are required to ensure the pointers are valid for the entire lifetime of this
25+
/// application. In particular, UEFI Boot Services must not be exited while an application with the
26+
/// standard library is loaded.
27+
///
28+
/// # SAFETY
29+
/// Calling this function more than once will panic
30+
pub(crate) unsafe fn init_globals(handle: NonNull<c_void>, system_table: NonNull<c_void>) {
31+
IMAGE_HANDLE
32+
.compare_exchange(
33+
crate::ptr::null_mut(),
34+
handle.as_ptr(),
35+
Ordering::Release,
36+
Ordering::Acquire,
37+
)
38+
.unwrap();
39+
SYSTEM_TABLE
40+
.compare_exchange(
41+
crate::ptr::null_mut(),
42+
system_table.as_ptr(),
43+
Ordering::Release,
44+
Ordering::Acquire,
45+
)
46+
.unwrap();
47+
BOOT_SERVICES_FLAG.store(true, Ordering::Release)
48+
}
49+
50+
/// Get the SystemTable Pointer.
51+
/// If you want to use `BootServices` then please use [`boot_services`] as it performs some
52+
/// additional checks.
53+
///
54+
/// Note: This function panics if the System Table or Image Handle is not initialized
55+
pub fn system_table() -> NonNull<c_void> {
56+
try_system_table().unwrap()
57+
}
58+
59+
/// Get the ImageHandle Pointer.
60+
///
61+
/// Note: This function panics if the System Table or Image Handle is not initialized
62+
pub fn image_handle() -> NonNull<c_void> {
63+
try_image_handle().unwrap()
64+
}
65+
66+
/// Get the BootServices Pointer.
67+
/// This function also checks if `ExitBootServices` has already been called.
68+
pub fn boot_services() -> Option<NonNull<c_void>> {
69+
if BOOT_SERVICES_FLAG.load(Ordering::Acquire) {
70+
let system_table: NonNull<r_efi::efi::SystemTable> = try_system_table()?.cast();
71+
let boot_services = unsafe { (*system_table.as_ptr()).boot_services };
72+
NonNull::new(boot_services).map(|x| x.cast())
73+
} else {
74+
None
75+
}
76+
}
77+
78+
/// Get the SystemTable Pointer.
79+
/// This function is mostly intended for places where panic is not an option
80+
pub(crate) fn try_system_table() -> Option<NonNull<c_void>> {
81+
NonNull::new(SYSTEM_TABLE.load(Ordering::Acquire))
82+
}
83+
84+
/// Get the SystemHandle Pointer.
85+
/// This function is mostly intended for places where panicking is not an option
86+
pub(crate) fn try_image_handle() -> Option<NonNull<c_void>> {
87+
NonNull::new(IMAGE_HANDLE.load(Ordering::Acquire))
88+
}
89+
90+
pub(crate) fn disable_boot_services() {
91+
BOOT_SERVICES_FLAG.store(false, Ordering::Release)
92+
}

‎library/std/src/os/uefi/mod.rs

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
//! Platform-specific extensions to `std` for UEFI.
2+
3+
#![unstable(feature = "uefi_std", issue = "100499")]
4+
#![doc(cfg(target_os = "uefi"))]
5+
6+
pub mod env;
7+
#[path = "../windows/ffi.rs"]
8+
pub mod ffi;

‎library/std/src/sys/common/thread_local/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
// "static" is for single-threaded platforms where a global static is sufficient.
77

88
cfg_if::cfg_if! {
9-
if #[cfg(all(target_family = "wasm", not(target_feature = "atomics")))] {
9+
if #[cfg(any(all(target_family = "wasm", not(target_feature = "atomics")), target_os = "uefi"))] {
1010
#[doc(hidden)]
1111
mod static_local;
1212
#[doc(hidden)]

‎library/std/src/sys/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ cfg_if::cfg_if! {
4747
} else if #[cfg(target_os = "xous")] {
4848
mod xous;
4949
pub use self::xous::*;
50+
} else if #[cfg(target_os = "uefi")] {
51+
mod uefi;
52+
pub use self::uefi::*;
5053
} else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] {
5154
mod sgx;
5255
pub use self::sgx::*;
@@ -114,4 +117,5 @@ pub fn log_wrapper<F: Fn(f64) -> f64>(n: f64, log_fn: F) -> f64 {
114117
log_fn(n)
115118
}
116119

120+
#[cfg(not(target_os = "uefi"))]
117121
pub type RawOsError = i32;

‎library/std/src/sys/uefi/alloc.rs

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//! Global Allocator for UEFI.
2+
//! Uses [r-efi-alloc](https://crates.io/crates/r-efi-alloc)
3+
4+
use crate::alloc::{GlobalAlloc, Layout, System};
5+
6+
const MEMORY_TYPE: u32 = r_efi::efi::LOADER_DATA;
7+
8+
#[stable(feature = "alloc_system_type", since = "1.28.0")]
9+
unsafe impl GlobalAlloc for System {
10+
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
11+
// Return null pointer if boot services are not available
12+
if crate::os::uefi::env::boot_services().is_none() {
13+
return crate::ptr::null_mut();
14+
}
15+
16+
// If boot services is valid then SystemTable is not null.
17+
let system_table = crate::os::uefi::env::system_table().as_ptr().cast();
18+
// The caller must ensure non-0 layout
19+
unsafe { r_efi_alloc::raw::alloc(system_table, layout, MEMORY_TYPE) }
20+
}
21+
22+
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
23+
// Do nothing if boot services are not available
24+
if crate::os::uefi::env::boot_services().is_none() {
25+
return;
26+
}
27+
28+
// If boot services is valid then SystemTable is not null.
29+
let system_table = crate::os::uefi::env::system_table().as_ptr().cast();
30+
// The caller must ensure non-0 layout
31+
unsafe { r_efi_alloc::raw::dealloc(system_table, ptr, layout) }
32+
}
33+
}

‎library/std/src/sys/uefi/env.rs

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
pub mod os {
2+
pub const FAMILY: &str = "";
3+
pub const OS: &str = "uefi";
4+
pub const DLL_PREFIX: &str = "";
5+
pub const DLL_SUFFIX: &str = "";
6+
pub const DLL_EXTENSION: &str = "";
7+
pub const EXE_SUFFIX: &str = ".efi";
8+
pub const EXE_EXTENSION: &str = "efi";
9+
}
There was a problem loading the remainder of the diff.

0 commit comments

Comments
 (0)
Failed to load comments.