Skip to content

Commit

Permalink
Auto merge of #105861 - Ayush1325:uefi-std-minimial, r=workingjubilee
Browse files Browse the repository at this point in the history
Add Minimal Std implementation for UEFI

# Implemented modules:
1. alloc
2. os_str
3. env
4. math

# Related Links
Tracking Issue: #100499
API Change Proposal: rust-lang/libs-team#87

# Additional Information
This was originally part of #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 <[email protected]>
  • Loading branch information
bors committed Sep 24, 2023
2 parents 44bd31c + 984ecef commit c7224e3
Show file tree
Hide file tree
Showing 25 changed files with 957 additions and 21 deletions.
23 changes: 23 additions & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3006,6 +3006,27 @@ dependencies = [
"proc-macro2",
]

[[package]]
name = "r-efi"
version = "4.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "575fc2d9b3da54adbdfaddf6eca48fec256d977c8630a1750b8991347d1ac911"
dependencies = [
"compiler_builtins",
"rustc-std-workspace-core",
]

[[package]]
name = "r-efi-alloc"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31d6f09fe2b6ad044bc3d2c34ce4979796581afd2f1ebc185837e02421e02fd7"
dependencies = [
"compiler_builtins",
"r-efi",
"rustc-std-workspace-core",
]

[[package]]
name = "rand"
version = "0.8.5"
Expand Down Expand Up @@ -5012,6 +5033,8 @@ dependencies = [
"panic_abort",
"panic_unwind",
"profiler_builtins",
"r-efi",
"r-efi-alloc",
"rand",
"rand_xorshift",
"rustc-demangle",
Expand Down
28 changes: 22 additions & 6 deletions compiler/rustc_codegen_ssa/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -420,9 +420,11 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
rust_main_def_id: DefId,
entry_type: EntryFnType,
) -> Bx::Function {
// The entry function is either `int main(void)` or `int main(int argc, char **argv)`,
// depending on whether the target needs `argc` and `argv` to be passed in.
let llfty = if cx.sess().target.main_needs_argc_argv {
// The entry function is either `int main(void)` or `int main(int argc, char **argv)`, or
// `usize efi_main(void *handle, void *system_table)` depending on the target.
let llfty = if cx.sess().target.os.contains("uefi") {
cx.type_func(&[cx.type_ptr(), cx.type_ptr()], cx.type_isize())
} else if cx.sess().target.main_needs_argc_argv {
cx.type_func(&[cx.type_int(), cx.type_ptr()], cx.type_int())
} else {
cx.type_func(&[], cx.type_int())
Expand Down Expand Up @@ -485,8 +487,12 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
};

let result = bx.call(start_ty, None, None, start_fn, &args, None);
let cast = bx.intcast(result, cx.type_int(), true);
bx.ret(cast);
if cx.sess().target.os.contains("uefi") {
bx.ret(result);
} else {
let cast = bx.intcast(result, cx.type_int(), true);
bx.ret(cast);
}

llfn
}
Expand All @@ -497,7 +503,17 @@ fn get_argc_argv<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
cx: &'a Bx::CodegenCx,
bx: &mut Bx,
) -> (Bx::Value, Bx::Value) {
if cx.sess().target.main_needs_argc_argv {
if cx.sess().target.os.contains("uefi") {
// Params for UEFI
let param_handle = bx.get_param(0);
let param_system_table = bx.get_param(1);
let arg_argc = bx.const_int(cx.type_isize(), 2);
let arg_argv = bx.alloca(cx.type_array(cx.type_ptr(), 2), Align::ONE);
bx.store(param_handle, arg_argv, Align::ONE);
let arg_argv_el1 = bx.gep(cx.type_ptr(), arg_argv, &[bx.const_int(cx.type_int(), 1)]);
bx.store(param_system_table, arg_argv_el1, Align::ONE);
(arg_argc, arg_argv)
} else if cx.sess().target.main_needs_argc_argv {
// Params from native `main()` used as args for rust start function
let param_argc = bx.get_param(0);
let param_argv = bx.get_param(1);
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_target/src/spec/uefi_msvc_base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ pub fn opts() -> TargetOptions {
stack_probes: StackProbeType::Call,
singlethread: true,
linker: Some("rust-lld".into()),
entry_name: "efi_main".into(),
..base
}
}
3 changes: 2 additions & 1 deletion compiler/rustc_target/src/spec/x86_64_unknown_uefi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@
// The win64 ABI is used. It differs from the sysv64 ABI, so we must use a windows target with
// LLVM. "x86_64-unknown-windows" is used to get the minimal subset of windows-specific features.

use crate::spec::Target;
use crate::{abi::call::Conv, spec::Target};

pub fn target() -> Target {
let mut base = super::uefi_msvc_base::opts();
base.cpu = "x86-64".into();
base.plt_by_default = false;
base.max_atomic_width = Some(64);
base.entry_abi = Conv::X86_64Win64;

// We disable MMX and SSE for now, even though UEFI allows using them. Problem is, you have to
// enable these CPU features explicitly before their first use, otherwise their instructions
Expand Down
3 changes: 2 additions & 1 deletion library/panic_abort/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ pub unsafe fn __rust_start_panic(_payload: &mut dyn PanicPayload) -> u32 {
}
} else if #[cfg(any(target_os = "hermit",
all(target_vendor = "fortanix", target_env = "sgx"),
target_os = "xous"
target_os = "xous",
target_os = "uefi",
))] {
unsafe fn abort() -> ! {
// call std::sys::abort_internal
Expand Down
4 changes: 4 additions & 0 deletions library/std/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ hermit-abi = { version = "0.3.2", features = ['rustc-dep-of-std'], public = true
[target.'cfg(target_os = "wasi")'.dependencies]
wasi = { version = "0.11.0", features = ['rustc-dep-of-std'], default-features = false }

[target.'cfg(target_os = "uefi")'.dependencies]
r-efi = { version = "4.2.0", features = ['rustc-dep-of-std']}
r-efi-alloc = { version = "1.0.0", features = ['rustc-dep-of-std']}

[features]
backtrace = [
"gimli-symbolize",
Expand Down
2 changes: 1 addition & 1 deletion library/std/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ fn main() {
|| target.contains("nto")
|| target.contains("xous")
|| target.contains("hurd")
|| target.contains("uefi")
// See src/bootstrap/synthetic_targets.rs
|| env::var("RUSTC_BOOTSTRAP_SYNTHETIC_TARGET").is_ok()
{
Expand All @@ -51,7 +52,6 @@ fn main() {
// - mipsel-sony-psp
// - nvptx64-nvidia-cuda
// - arch=avr
// - uefi (x86_64-unknown-uefi, i686-unknown-uefi)
// - JSON targets
// - Any new targets that have not been explicitly added above.
println!("cargo:rustc-cfg=feature=\"restricted-std\"");
Expand Down
8 changes: 4 additions & 4 deletions library/std/src/io/error.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
#[cfg(test)]
mod tests;

#[cfg(target_pointer_width = "64")]
#[cfg(all(target_pointer_width = "64", not(target_os = "uefi")))]
mod repr_bitpacked;
#[cfg(target_pointer_width = "64")]
#[cfg(all(target_pointer_width = "64", not(target_os = "uefi")))]
use repr_bitpacked::Repr;

#[cfg(not(target_pointer_width = "64"))]
#[cfg(any(not(target_pointer_width = "64"), target_os = "uefi"))]
mod repr_unpacked;
#[cfg(not(target_pointer_width = "64"))]
#[cfg(any(not(target_pointer_width = "64"), target_os = "uefi"))]
use repr_unpacked::Repr;

use crate::error;
Expand Down
2 changes: 2 additions & 0 deletions library/std/src/os/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ pub mod solid;
#[cfg(target_os = "tvos")]
#[path = "ios/mod.rs"]
pub(crate) mod tvos;
#[cfg(target_os = "uefi")]
pub mod uefi;
#[cfg(target_os = "vita")]
pub mod vita;
#[cfg(target_os = "vxworks")]
Expand Down
92 changes: 92 additions & 0 deletions library/std/src/os/uefi/env.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
//! UEFI-specific extensions to the primitives in `std::env` module
#![unstable(feature = "uefi_std", issue = "100499")]

use crate::sync::atomic::{AtomicBool, AtomicPtr, Ordering};
use crate::{ffi::c_void, ptr::NonNull};

static SYSTEM_TABLE: AtomicPtr<c_void> = AtomicPtr::new(crate::ptr::null_mut());
static IMAGE_HANDLE: AtomicPtr<c_void> = AtomicPtr::new(crate::ptr::null_mut());
// Flag to check if BootServices are still valid.
// Start with assuming that they are not available
static BOOT_SERVICES_FLAG: AtomicBool = AtomicBool::new(false);

/// Initializes the global System Table and Image Handle pointers.
///
/// The standard library requires access to the UEFI System Table and the Application Image Handle
/// to operate. Those are provided to UEFI Applications via their application entry point. By
/// calling `init_globals()`, those pointers are retained by the standard library for future use.
/// Thus this function must be called before any of the standard library services are used.
///
/// The pointers are never exposed to any entity outside of this application and it is guaranteed
/// that, once the application exited, these pointers are never dereferenced again.
///
/// Callers are required to ensure the pointers are valid for the entire lifetime of this
/// application. In particular, UEFI Boot Services must not be exited while an application with the
/// standard library is loaded.
///
/// # SAFETY
/// Calling this function more than once will panic
pub(crate) unsafe fn init_globals(handle: NonNull<c_void>, system_table: NonNull<c_void>) {
IMAGE_HANDLE
.compare_exchange(
crate::ptr::null_mut(),
handle.as_ptr(),
Ordering::Release,
Ordering::Acquire,
)
.unwrap();
SYSTEM_TABLE
.compare_exchange(
crate::ptr::null_mut(),
system_table.as_ptr(),
Ordering::Release,
Ordering::Acquire,
)
.unwrap();
BOOT_SERVICES_FLAG.store(true, Ordering::Release)
}

/// Get the SystemTable Pointer.
/// If you want to use `BootServices` then please use [`boot_services`] as it performs some
/// additional checks.
///
/// Note: This function panics if the System Table or Image Handle is not initialized
pub fn system_table() -> NonNull<c_void> {
try_system_table().unwrap()
}

/// Get the ImageHandle Pointer.
///
/// Note: This function panics if the System Table or Image Handle is not initialized
pub fn image_handle() -> NonNull<c_void> {
try_image_handle().unwrap()
}

/// Get the BootServices Pointer.
/// This function also checks if `ExitBootServices` has already been called.
pub fn boot_services() -> Option<NonNull<c_void>> {
if BOOT_SERVICES_FLAG.load(Ordering::Acquire) {
let system_table: NonNull<r_efi::efi::SystemTable> = try_system_table()?.cast();
let boot_services = unsafe { (*system_table.as_ptr()).boot_services };
NonNull::new(boot_services).map(|x| x.cast())
} else {
None
}
}

/// Get the SystemTable Pointer.
/// This function is mostly intended for places where panic is not an option
pub(crate) fn try_system_table() -> Option<NonNull<c_void>> {
NonNull::new(SYSTEM_TABLE.load(Ordering::Acquire))
}

/// Get the SystemHandle Pointer.
/// This function is mostly intended for places where panicking is not an option
pub(crate) fn try_image_handle() -> Option<NonNull<c_void>> {
NonNull::new(IMAGE_HANDLE.load(Ordering::Acquire))
}

pub(crate) fn disable_boot_services() {
BOOT_SERVICES_FLAG.store(false, Ordering::Release)
}
8 changes: 8 additions & 0 deletions library/std/src/os/uefi/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//! Platform-specific extensions to `std` for UEFI.
#![unstable(feature = "uefi_std", issue = "100499")]
#![doc(cfg(target_os = "uefi"))]

pub mod env;
#[path = "../windows/ffi.rs"]
pub mod ffi;
2 changes: 1 addition & 1 deletion library/std/src/sys/common/thread_local/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// "static" is for single-threaded platforms where a global static is sufficient.

cfg_if::cfg_if! {
if #[cfg(all(target_family = "wasm", not(target_feature = "atomics")))] {
if #[cfg(any(all(target_family = "wasm", not(target_feature = "atomics")), target_os = "uefi"))] {
#[doc(hidden)]
mod static_local;
#[doc(hidden)]
Expand Down
4 changes: 4 additions & 0 deletions library/std/src/sys/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ cfg_if::cfg_if! {
} else if #[cfg(target_os = "xous")] {
mod xous;
pub use self::xous::*;
} else if #[cfg(target_os = "uefi")] {
mod uefi;
pub use self::uefi::*;
} else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] {
mod sgx;
pub use self::sgx::*;
Expand Down Expand Up @@ -114,4 +117,5 @@ pub fn log_wrapper<F: Fn(f64) -> f64>(n: f64, log_fn: F) -> f64 {
log_fn(n)
}

#[cfg(not(target_os = "uefi"))]
pub type RawOsError = i32;
33 changes: 33 additions & 0 deletions library/std/src/sys/uefi/alloc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//! Global Allocator for UEFI.
//! Uses [r-efi-alloc](https://crates.io/crates/r-efi-alloc)
use crate::alloc::{GlobalAlloc, Layout, System};

const MEMORY_TYPE: u32 = r_efi::efi::LOADER_DATA;

#[stable(feature = "alloc_system_type", since = "1.28.0")]
unsafe impl GlobalAlloc for System {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
// Return null pointer if boot services are not available
if crate::os::uefi::env::boot_services().is_none() {
return crate::ptr::null_mut();
}

// If boot services is valid then SystemTable is not null.
let system_table = crate::os::uefi::env::system_table().as_ptr().cast();
// The caller must ensure non-0 layout
unsafe { r_efi_alloc::raw::alloc(system_table, layout, MEMORY_TYPE) }
}

unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
// Do nothing if boot services are not available
if crate::os::uefi::env::boot_services().is_none() {
return;
}

// If boot services is valid then SystemTable is not null.
let system_table = crate::os::uefi::env::system_table().as_ptr().cast();
// The caller must ensure non-0 layout
unsafe { r_efi_alloc::raw::dealloc(system_table, ptr, layout) }
}
}
9 changes: 9 additions & 0 deletions library/std/src/sys/uefi/env.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
pub mod os {
pub const FAMILY: &str = "";
pub const OS: &str = "uefi";
pub const DLL_PREFIX: &str = "";
pub const DLL_SUFFIX: &str = "";
pub const DLL_EXTENSION: &str = "";
pub const EXE_SUFFIX: &str = ".efi";
pub const EXE_EXTENSION: &str = "efi";
}
Loading

0 comments on commit c7224e3

Please sign in to comment.