Skip to content

Commit

Permalink
Add Minimal Std implementation for UEFI
Browse files Browse the repository at this point in the history
Implemented modules:
1. alloc
2. os_str
3. env
4. math

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

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.

Signed-off-by: Ayush Singh <ayushsingh1325@gmail.com>
  • Loading branch information
Ayush1325 committed Sep 4, 2023
1 parent ce798a5 commit 581adb0
Show file tree
Hide file tree
Showing 24 changed files with 718 additions and 18 deletions.
3 changes: 3 additions & 0 deletions .nlsp-settings/rust_analyzer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"rust-analyzer.cargo.target": "x86_64-unknown-uefi"
}
23 changes: 23 additions & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2924,6 +2924,27 @@ dependencies = [
"proc-macro2",
]

[[package]]
name = "r-efi"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e7345c622833c6745e7b027a28aa95618813dc1f3c3de396206410267dce6f3"
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 @@ -4930,6 +4951,8 @@ dependencies = [
"panic_abort",
"panic_unwind",
"profiler_builtins",
"r-efi",
"r-efi-alloc",
"rand",
"rand_xorshift",
"rustc-demangle",
Expand Down
35 changes: 28 additions & 7 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
// `Status efi_main(Handle hd, SystemTable *st)` 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,18 @@ 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_i8p(), 2), Align::ONE);
bx.store(param_handle, arg_argv, Align::ONE);
let arg_argv_el1 =
bx.gep(cx.type_ptr_to(cx.type_i8()), 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 Expand Up @@ -549,7 +566,11 @@ pub fn allocator_kind_for_codegen(tcx: TyCtxt<'_>) -> Option<AllocatorKind> {
use rustc_middle::middle::dependency_format::Linkage;
list.iter().any(|&linkage| linkage == Linkage::Dynamic)
});
if any_dynamic_crate { None } else { tcx.allocator_kind(()) }
if any_dynamic_crate {
None
} else {
tcx.allocator_kind(())
}
}

pub fn codegen_crate<B: ExtraBackendMethods>(
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
1 change: 1 addition & 0 deletions library/panic_abort/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ pub unsafe fn __rust_start_panic(_payload: &mut dyn BoxMeUp) -> u32 {
libc::abort();
}
} else if #[cfg(any(target_os = "hermit",
target_os = "uefi",
all(target_vendor = "fortanix", target_env = "sgx")
))] {
unsafe fn abort() -> ! {
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.1.0", features = ['rustc-dep-of-std', 'efiapi']}
r-efi-alloc = { version = "1.0.0", features = ['rustc-dep-of-std']}

[features]
backtrace = [
"gimli-symbolize",
Expand Down
3 changes: 2 additions & 1 deletion library/std/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ fn main() {
|| target.contains("nintendo-3ds")
|| target.contains("vita")
|| target.contains("nto")
|| target.contains("uefi")
// See src/bootstrap/synthetic_targets.rs
|| env::var("RUSTC_BOOTSTRAP_SYNTHETIC_TARGET").is_ok()
{
Expand All @@ -49,7 +50,7 @@ fn main() {
// - mipsel-sony-psp
// - nvptx64-nvidia-cuda
// - arch=avr
// - uefi (x86_64-unknown-uefi, i686-unknown-uefi)
// - tvos (aarch64-apple-tvos, x86_64-apple-tvos)
// - JSON targets
// - Any new targets that have not been explicitly added above.
println!("cargo:rustc-cfg=feature=\"restricted-std\"");
Expand Down
1 change: 0 additions & 1 deletion library/std/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,6 @@
#![cfg_attr(windows, feature(round_char_boundary))]
//
// Language features:
// tidy-alphabetical-start
#![feature(alloc_error_handler)]
#![feature(allocator_internals)]
#![feature(allow_internal_unsafe)]
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 @@ -140,6 +140,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
54 changes: 54 additions & 0 deletions library/std/src/os/uefi/env.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//! UEFI-specific extensions to the primitives in `std::env` module

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

// Position 0 = SystemTable
// Position 1 = ImageHandle
static GLOBALS: OnceLock<(AtomicPtr<c_void>, AtomicPtr<c_void>)> = OnceLock::new();

/// 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.
/// 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.
///
/// This function must not be called more than once.
#[unstable(feature = "uefi_std", issue = "100499")]
pub unsafe fn init_globals(handle: NonNull<c_void>, system_table: NonNull<c_void>) {
GLOBALS.set((AtomicPtr::new(system_table.as_ptr()), AtomicPtr::new(handle.as_ptr()))).unwrap()
}

/// Get the SystemTable Pointer.
/// Note: This function panics if the System Table and Image Handle is Not initialized
#[unstable(feature = "uefi_std", issue = "100499")]
pub fn system_table() -> NonNull<c_void> {
try_system_table().unwrap()
}

/// Get the SystemHandle Pointer.
/// Note: This function panics if the System Table and Image Handle is Not initialized
#[unstable(feature = "uefi_std", issue = "100499")]
pub fn image_handle() -> NonNull<c_void> {
try_image_handle().unwrap()
}

/// 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<crate::ffi::c_void>> {
NonNull::new(GLOBALS.get()?.0.load(Ordering::Acquire))
}

/// Get the SystemHandle Pointer.
/// This function is mostly intended for places where panic is not an option
pub(crate) fn try_image_handle() -> Option<NonNull<crate::ffi::c_void>> {
NonNull::new(GLOBALS.get()?.1.load(Ordering::Acquire))
}
7 changes: 7 additions & 0 deletions library/std/src/os/uefi/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
//! Platform-specific extensions to `std` for UEFI.

#![unstable(feature = "uefi_std", issue = "100499")]

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
3 changes: 3 additions & 0 deletions library/std/src/sys/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ cfg_if::cfg_if! {
} else if #[cfg(target_family = "wasm")] {
mod wasm;
pub use self::wasm::*;
} 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
32 changes: 32 additions & 0 deletions library/std/src/sys/uefi/alloc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//! Global Allocator for UEFI.
//! Uses [r-efi-alloc](https://crates.io/crates/r-efi-alloc)

use crate::alloc::{handle_alloc_error, 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 {
let system_table = match crate::os::uefi::env::try_system_table() {
None => return crate::ptr::null_mut(),
Some(x) => x.as_ptr() as *mut _,
};

if layout.size() > 0 {
unsafe { r_efi_alloc::raw::alloc(system_table, layout, MEMORY_TYPE) }
} else {
layout.dangling().as_ptr()
}
}

unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
let system_table = match crate::os::uefi::env::try_system_table() {
None => handle_alloc_error(layout),
Some(x) => x.as_ptr() as *mut _,
};
if layout.size() > 0 {
unsafe { r_efi_alloc::raw::dealloc(system_table, ptr, layout) }
}
}
}
Loading

0 comments on commit 581adb0

Please sign in to comment.