From 48c6ae0611ec6fb4094a24610982ddefde16e20f Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Sun, 18 Dec 2022 09:54:54 +0530 Subject: [PATCH 1/8] Add Minimal Std implementation for UEFI Implemented modules: 1. alloc 2. os_str 3. env 4. math Tracking Issue: https://github.com/rust-lang/rust/issues/100499 API Change Proposal: https://github.com/rust-lang/libs-team/issues/87 This was originally part of https://github.com/rust-lang/rust/pull/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 --- .nlsp-settings/rust_analyzer.json | 3 + Cargo.lock | 23 ++ compiler/rustc_codegen_ssa/src/base.rs | 35 ++- .../rustc_target/src/spec/uefi_msvc_base.rs | 1 + .../src/spec/x86_64_unknown_uefi.rs | 3 +- library/panic_abort/src/lib.rs | 1 + library/std/Cargo.toml | 4 + library/std/build.rs | 3 +- library/std/src/lib.rs | 1 - library/std/src/os/mod.rs | 2 + library/std/src/os/uefi/env.rs | 54 ++++ library/std/src/os/uefi/mod.rs | 7 + .../std/src/sys/common/thread_local/mod.rs | 2 +- library/std/src/sys/mod.rs | 3 + library/std/src/sys/uefi/alloc.rs | 32 +++ library/std/src/sys/uefi/common.rs | 269 ++++++++++++++++++ library/std/src/sys/uefi/env.rs | 9 + library/std/src/sys/uefi/mod.rs | 155 ++++++++++ library/std/src/sys/uefi/path.rs | 25 ++ library/std/src/sys/uefi/tests.rs | 21 ++ library/std/src/sys_common/mod.rs | 2 + src/bootstrap/config.rs | 6 +- .../src/platform-support/unknown-uefi.md | 72 ++++- src/tools/tidy/src/deps.rs | 3 + 24 files changed, 718 insertions(+), 18 deletions(-) create mode 100644 .nlsp-settings/rust_analyzer.json create mode 100644 library/std/src/os/uefi/env.rs create mode 100644 library/std/src/os/uefi/mod.rs create mode 100644 library/std/src/sys/uefi/alloc.rs create mode 100644 library/std/src/sys/uefi/common.rs create mode 100644 library/std/src/sys/uefi/env.rs create mode 100644 library/std/src/sys/uefi/mod.rs create mode 100644 library/std/src/sys/uefi/path.rs create mode 100644 library/std/src/sys/uefi/tests.rs diff --git a/.nlsp-settings/rust_analyzer.json b/.nlsp-settings/rust_analyzer.json new file mode 100644 index 0000000000000..eddf84afb2a3d --- /dev/null +++ b/.nlsp-settings/rust_analyzer.json @@ -0,0 +1,3 @@ +{ + "rust-analyzer.cargo.target": "x86_64-unknown-uefi" +} diff --git a/Cargo.lock b/Cargo.lock index d9aaedb85443f..f84387f30be61 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3006,6 +3006,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" @@ -5012,6 +5033,8 @@ dependencies = [ "panic_abort", "panic_unwind", "profiler_builtins", + "r-efi", + "r-efi-alloc", "rand", "rand_xorshift", "rustc-demangle", diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index cd19885faa0e1..4753f12554065 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -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()) @@ -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 } @@ -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); @@ -549,7 +566,11 @@ pub fn allocator_kind_for_codegen(tcx: TyCtxt<'_>) -> Option { 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( diff --git a/compiler/rustc_target/src/spec/uefi_msvc_base.rs b/compiler/rustc_target/src/spec/uefi_msvc_base.rs index 8968d3c8fc100..a50a55ad7e028 100644 --- a/compiler/rustc_target/src/spec/uefi_msvc_base.rs +++ b/compiler/rustc_target/src/spec/uefi_msvc_base.rs @@ -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 } } diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_uefi.rs b/compiler/rustc_target/src/spec/x86_64_unknown_uefi.rs index 67664a74710a3..41ba768068a35 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_uefi.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_uefi.rs @@ -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 diff --git a/library/panic_abort/src/lib.rs b/library/panic_abort/src/lib.rs index d675696f13f1a..bc0d7287be505 100644 --- a/library/panic_abort/src/lib.rs +++ b/library/panic_abort/src/lib.rs @@ -45,6 +45,7 @@ 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 = "uefi", ))] { unsafe fn abort() -> ! { // call std::sys::abort_internal diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index e8f642586cd7c..1908dfaa33db0 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -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", diff --git a/library/std/build.rs b/library/std/build.rs index e5509504b8495..1c32b42565011 100644 --- a/library/std/build.rs +++ b/library/std/build.rs @@ -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() { @@ -51,7 +52,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\""); diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 5e3249655b838..b572347019caf 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -263,7 +263,6 @@ #![cfg_attr(target_os = "xous", feature(slice_ptr_len))] // // Language features: -// tidy-alphabetical-start #![feature(alloc_error_handler)] #![feature(allocator_internals)] #![feature(allow_internal_unsafe)] diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs index 24d16e64c8636..11ad21515fdec 100644 --- a/library/std/src/os/mod.rs +++ b/library/std/src/os/mod.rs @@ -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")] diff --git a/library/std/src/os/uefi/env.rs b/library/std/src/os/uefi/env.rs new file mode 100644 index 0000000000000..479ff5f69757e --- /dev/null +++ b/library/std/src/os/uefi/env.rs @@ -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, AtomicPtr)> = 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, system_table: NonNull) { + 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 { + 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 { + 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::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::new(GLOBALS.get()?.1.load(Ordering::Acquire)) +} diff --git a/library/std/src/os/uefi/mod.rs b/library/std/src/os/uefi/mod.rs new file mode 100644 index 0000000000000..fc0468f25fc8e --- /dev/null +++ b/library/std/src/os/uefi/mod.rs @@ -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; diff --git a/library/std/src/sys/common/thread_local/mod.rs b/library/std/src/sys/common/thread_local/mod.rs index 975509bd412b0..8b2c839f837d4 100644 --- a/library/std/src/sys/common/thread_local/mod.rs +++ b/library/std/src/sys/common/thread_local/mod.rs @@ -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)] diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index 457eb782ccc66..a03501e6a1357 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -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::*; diff --git a/library/std/src/sys/uefi/alloc.rs b/library/std/src/sys/uefi/alloc.rs new file mode 100644 index 0000000000000..6a14ee76e92ce --- /dev/null +++ b/library/std/src/sys/uefi/alloc.rs @@ -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) } + } + } +} diff --git a/library/std/src/sys/uefi/common.rs b/library/std/src/sys/uefi/common.rs new file mode 100644 index 0000000000000..6316a74d3670c --- /dev/null +++ b/library/std/src/sys/uefi/common.rs @@ -0,0 +1,269 @@ +//! Contains most of the shared UEFI specific stuff. Some of this might be moved to `std::os::uefi` +//! if needed but no point in adding extra public API when there is not Std support for UEFI in the +//! first place + +use r_efi::efi::Guid; + +use crate::io::{self, const_io_error}; +use crate::mem::MaybeUninit; +use crate::os::uefi; +use crate::ptr::NonNull; + +// Locate handles with a particular protocol GUID +/// Implemented using `EFI_BOOT_SERVICES.LocateHandles()` +pub(crate) fn locate_handles(mut guid: Guid) -> io::Result>> { + fn inner( + guid: &mut Guid, + boot_services: NonNull, + buf_size: &mut usize, + buf: *mut r_efi::efi::Handle, + ) -> io::Result<()> { + let r = unsafe { + ((*boot_services.as_ptr()).locate_handle)( + r_efi::efi::BY_PROTOCOL, + guid, + crate::ptr::null_mut(), + buf_size, + buf, + ) + }; + + if r.is_error() { Err(status_to_io_error(r)) } else { Ok(()) } + } + + let boot_services = boot_services(); + let mut buf_len = 0usize; + + match inner(&mut guid, boot_services, &mut buf_len, crate::ptr::null_mut()) { + Ok(()) => unreachable!(), + Err(e) => match e.kind() { + io::ErrorKind::FileTooLarge => {} + _ => return Err(e), + }, + } + + // The returned buf_len is in bytes + let mut buf: Vec = + Vec::with_capacity(buf_len / crate::mem::size_of::()); + match inner(&mut guid, boot_services, &mut buf_len, buf.as_mut_ptr()) { + Ok(()) => { + // SAFETY: This is safe because the call will succeed only if buf_len >= required + // length. Also, on success, the `buf_len` is updated with the size of bufferv (in + // bytes) written + unsafe { buf.set_len(buf_len / crate::mem::size_of::()) }; + Ok(buf.iter().filter_map(|x| NonNull::new(*x)).collect()) + } + Err(e) => Err(e), + } +} + +/// Open Protocol on a handle +/// Implemented using `EFI_BOOT_SERVICES.OpenProtocol()` +pub(crate) fn open_protocol( + handle: NonNull, + mut protocol_guid: Guid, +) -> io::Result> { + let boot_services = boot_services(); + let system_handle = uefi::env::image_handle(); + let mut protocol: MaybeUninit<*mut T> = MaybeUninit::uninit(); + + let r = unsafe { + ((*boot_services.as_ptr()).open_protocol)( + handle.as_ptr(), + &mut protocol_guid, + protocol.as_mut_ptr().cast(), + system_handle.as_ptr(), + crate::ptr::null_mut(), + r_efi::system::OPEN_PROTOCOL_GET_PROTOCOL, + ) + }; + + if r.is_error() { + Err(status_to_io_error(r)) + } else { + NonNull::new(unsafe { protocol.assume_init() }) + .ok_or(const_io_error!(io::ErrorKind::Other, "null protocol")) + } +} + +pub(crate) fn status_to_io_error(s: r_efi::efi::Status) -> io::Error { + use io::ErrorKind; + use r_efi::efi::Status; + + // Keep the List in Alphabetical Order + // The Messages are taken from UEFI Specification Appendix D - Status Codes + match s { + Status::ABORTED => { + const_io_error!(ErrorKind::ConnectionAborted, "The operation was aborted.") + } + Status::ACCESS_DENIED => { + const_io_error!(ErrorKind::PermissionDenied, "Access was denied.") + } + Status::ALREADY_STARTED => { + const_io_error!(ErrorKind::Other, "The protocol has already been started.") + } + Status::BAD_BUFFER_SIZE => { + const_io_error!( + ErrorKind::InvalidData, + "The buffer was not the proper size for the request." + ) + } + Status::BUFFER_TOO_SMALL => { + const_io_error!( + ErrorKind::FileTooLarge, + "The buffer is not large enough to hold the requested data. The required buffer size is returned in the appropriate parameter when this error occurs." + ) + } + Status::COMPROMISED_DATA => { + const_io_error!( + ErrorKind::Other, + "The security status of the data is unknown or compromised and the data must be updated or replaced to restore a valid security status." + ) + } + Status::CONNECTION_FIN => { + const_io_error!( + ErrorKind::Other, + "The receiving operation fails because the communication peer has closed the connection and there is no more data in the receive buffer of the instance." + ) + } + Status::CONNECTION_REFUSED => { + const_io_error!( + ErrorKind::ConnectionRefused, + "The receiving or transmission operation fails because this connection is refused." + ) + } + Status::CONNECTION_RESET => { + const_io_error!( + ErrorKind::ConnectionReset, + "The connect fails because the connection is reset either by instance itself or the communication peer." + ) + } + Status::CRC_ERROR => const_io_error!(ErrorKind::Other, "A CRC error was detected."), + Status::DEVICE_ERROR => const_io_error!( + ErrorKind::Other, + "The physical device reported an error while attempting the operation." + ), + Status::END_OF_FILE => { + const_io_error!(ErrorKind::UnexpectedEof, "The end of the file was reached.") + } + Status::END_OF_MEDIA => { + const_io_error!(ErrorKind::Other, "Beginning or end of media was reached") + } + Status::HOST_UNREACHABLE => { + const_io_error!(ErrorKind::HostUnreachable, "The remote host is not reachable.") + } + Status::HTTP_ERROR => { + const_io_error!(ErrorKind::Other, "A HTTP error occurred during the network operation.") + } + Status::ICMP_ERROR => { + const_io_error!( + ErrorKind::Other, + "An ICMP error occurred during the network operation." + ) + } + Status::INCOMPATIBLE_VERSION => { + const_io_error!( + ErrorKind::Other, + "The function encountered an internal version that was incompatible with a version requested by the caller." + ) + } + Status::INVALID_LANGUAGE => { + const_io_error!(ErrorKind::InvalidData, "The language specified was invalid.") + } + Status::INVALID_PARAMETER => { + const_io_error!(ErrorKind::InvalidInput, "A parameter was incorrect.") + } + Status::IP_ADDRESS_CONFLICT => { + const_io_error!(ErrorKind::AddrInUse, "There is an address conflict address allocation") + } + Status::LOAD_ERROR => { + const_io_error!(ErrorKind::Other, "The image failed to load.") + } + Status::MEDIA_CHANGED => { + const_io_error!( + ErrorKind::Other, + "The medium in the device has changed since the last access." + ) + } + Status::NETWORK_UNREACHABLE => { + const_io_error!( + ErrorKind::NetworkUnreachable, + "The network containing the remote host is not reachable." + ) + } + Status::NO_MAPPING => { + const_io_error!(ErrorKind::Other, "A mapping to a device does not exist.") + } + Status::NO_MEDIA => { + const_io_error!( + ErrorKind::Other, + "The device does not contain any medium to perform the operation." + ) + } + Status::NO_RESPONSE => { + const_io_error!( + ErrorKind::HostUnreachable, + "The server was not found or did not respond to the request." + ) + } + Status::NOT_FOUND => const_io_error!(ErrorKind::NotFound, "The item was not found."), + Status::NOT_READY => { + const_io_error!(ErrorKind::ResourceBusy, "There is no data pending upon return.") + } + Status::NOT_STARTED => { + const_io_error!(ErrorKind::Other, "The protocol has not been started.") + } + Status::OUT_OF_RESOURCES => { + const_io_error!(ErrorKind::OutOfMemory, "A resource has run out.") + } + Status::PROTOCOL_ERROR => { + const_io_error!( + ErrorKind::Other, + "A protocol error occurred during the network operation." + ) + } + Status::PROTOCOL_UNREACHABLE => { + const_io_error!(ErrorKind::Other, "An ICMP protocol unreachable error is received.") + } + Status::SECURITY_VIOLATION => { + const_io_error!( + ErrorKind::PermissionDenied, + "The function was not performed due to a security violation." + ) + } + Status::TFTP_ERROR => { + const_io_error!(ErrorKind::Other, "A TFTP error occurred during the network operation.") + } + Status::TIMEOUT => const_io_error!(ErrorKind::TimedOut, "The timeout time expired."), + Status::UNSUPPORTED => { + const_io_error!(ErrorKind::Unsupported, "The operation is not supported.") + } + Status::VOLUME_FULL => { + const_io_error!(ErrorKind::StorageFull, "There is no more space on the file system.") + } + Status::VOLUME_CORRUPTED => { + const_io_error!( + ErrorKind::Other, + "An inconstancy was detected on the file system causing the operating to fail." + ) + } + Status::WRITE_PROTECTED => { + const_io_error!(ErrorKind::ReadOnlyFilesystem, "The device cannot be written to.") + } + _ => io::Error::new(ErrorKind::Uncategorized, format!("Status: {}", s.as_usize())), + } +} + +/// Get the BootServices Pointer. +pub(crate) fn boot_services() -> NonNull { + let system_table: NonNull = uefi::env::system_table().cast(); + let boot_services = unsafe { (*system_table.as_ptr()).boot_services }; + NonNull::new(boot_services).unwrap() +} +/// Get the BootServices Pointer. +/// This function is mostly intended for places where panic is not an option +pub(crate) fn try_boot_services() -> Option> { + let system_table: NonNull = uefi::env::try_system_table()?.cast(); + let boot_services = unsafe { (*system_table.as_ptr()).boot_services }; + NonNull::new(boot_services) +} diff --git a/library/std/src/sys/uefi/env.rs b/library/std/src/sys/uefi/env.rs new file mode 100644 index 0000000000000..c106d5fed3e1d --- /dev/null +++ b/library/std/src/sys/uefi/env.rs @@ -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"; +} diff --git a/library/std/src/sys/uefi/mod.rs b/library/std/src/sys/uefi/mod.rs new file mode 100644 index 0000000000000..193d4ecc16fa7 --- /dev/null +++ b/library/std/src/sys/uefi/mod.rs @@ -0,0 +1,155 @@ +//! Platform-specific extensions to `std` for UEFI platforms. +//! +//! Provides access to platform-level information on UEFI platforms, and +//! exposes UEFI-specific functions that would otherwise be inappropriate as +//! part of the core `std` library. +//! +//! It exposes more ways to deal with platform-specific strings ([`OsStr`], +//! [`OsString`]), allows to set permissions more granularly, extract low-level +//! file descriptors from files and sockets, and has platform-specific helpers +//! for spawning processes. +//! +//! [`OsStr`]: crate::ffi::OsStr +//! [`OsString`]: crate::ffi::OsString + +#![deny(unsafe_op_in_unsafe_fn)] +pub mod alloc; +#[path = "../unsupported/args.rs"] +pub mod args; +#[path = "../unix/cmath.rs"] +pub mod cmath; +pub mod env; +#[path = "../unsupported/fs.rs"] +pub mod fs; +#[path = "../unsupported/io.rs"] +pub mod io; +#[path = "../unsupported/locks/mod.rs"] +pub mod locks; +#[path = "../unsupported/net.rs"] +pub mod net; +#[path = "../unsupported/once.rs"] +pub mod once; +#[path = "../unsupported/os.rs"] +pub mod os; +#[path = "../windows/os_str.rs"] +pub mod os_str; +pub mod path; +#[path = "../unsupported/pipe.rs"] +pub mod pipe; +#[path = "../unsupported/process.rs"] +pub mod process; +#[path = "../unsupported/stdio.rs"] +pub mod stdio; +#[path = "../unsupported/thread.rs"] +pub mod thread; +#[path = "../unsupported/thread_local_key.rs"] +pub mod thread_local_key; +#[path = "../unsupported/time.rs"] +pub mod time; + +pub(crate) mod common; + +#[cfg(test)] +mod tests; + +use crate::io as std_io; +use crate::os::uefi; +use crate::ptr::NonNull; + +pub mod memchr { + pub use core::slice::memchr::{memchr, memrchr}; +} + +// SAFETY: must be called only once during runtime initialization. +// SAFETY: argc must be 2. +// SAFETY: argv must be &[Handle, *mut SystemTable]. +pub unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) { + assert_eq!(argc, 2); + let image_handle = unsafe { NonNull::new(*argv as *mut crate::ffi::c_void).unwrap() }; + let system_table = unsafe { NonNull::new(*argv.add(1) as *mut crate::ffi::c_void).unwrap() }; + unsafe { crate::os::uefi::env::init_globals(image_handle, system_table) }; +} + +// SAFETY: must be called only once during runtime cleanup. +// NOTE: this is not guaranteed to run, for example when the program aborts. +pub unsafe fn cleanup() {} + +#[inline] +pub const fn unsupported() -> std_io::Result { + Err(unsupported_err()) +} + +#[inline] +pub const fn unsupported_err() -> std_io::Error { + std_io::const_io_error!(std_io::ErrorKind::Unsupported, "operation not supported on UEFI",) +} + +pub fn decode_error_kind(code: i32) -> crate::io::ErrorKind { + use crate::io::ErrorKind; + use r_efi::efi::Status; + + if let Ok(code) = usize::try_from(code) { + common::status_to_io_error(Status::from_usize(code)).kind() + } else { + ErrorKind::Uncategorized + } +} + +pub fn abort_internal() -> ! { + if let (Some(boot_services), Some(handle)) = + (common::try_boot_services(), uefi::env::try_image_handle()) + { + let _ = unsafe { + ((*boot_services.as_ptr()).exit)( + handle.as_ptr(), + r_efi::efi::Status::ABORTED, + 0, + crate::ptr::null_mut(), + ) + }; + } + + // In case SystemTable and ImageHandle cannot be reached, use `core::intrinsics::abort` + core::intrinsics::abort(); +} + +// This function is needed by the panic runtime. The symbol is named in +// pre-link args for the target specification, so keep that in sync. +#[cfg(not(test))] +#[no_mangle] +pub extern "C" fn __rust_abort() { + abort_internal(); +} + +#[inline] +pub fn hashmap_random_keys() -> (u64, u64) { + get_random().unwrap() +} + +fn get_random() -> Option<(u64, u64)> { + use r_efi::protocols::rng; + + let mut buf = [0u8; 16]; + let handles = common::locate_handles(rng::PROTOCOL_GUID).ok()?; + for handle in handles { + if let Ok(protocol) = common::open_protocol::(handle, rng::PROTOCOL_GUID) { + let r = unsafe { + ((*protocol.as_ptr()).get_rng)( + protocol.as_ptr(), + crate::ptr::null_mut(), + buf.len(), + buf.as_mut_ptr(), + ) + }; + if r.is_error() { + continue; + } else { + return Some(( + u64::from_le_bytes(buf[..8].try_into().ok()?), + u64::from_le_bytes(buf[8..].try_into().ok()?), + )); + } + } + } + None +} diff --git a/library/std/src/sys/uefi/path.rs b/library/std/src/sys/uefi/path.rs new file mode 100644 index 0000000000000..106682eee56b6 --- /dev/null +++ b/library/std/src/sys/uefi/path.rs @@ -0,0 +1,25 @@ +use super::unsupported; +use crate::ffi::OsStr; +use crate::io; +use crate::path::{Path, PathBuf, Prefix}; + +pub const MAIN_SEP_STR: &str = "\\"; +pub const MAIN_SEP: char = '\\'; + +#[inline] +pub fn is_sep_byte(b: u8) -> bool { + b == b'\\' +} + +#[inline] +pub fn is_verbatim_sep(b: u8) -> bool { + b == b'\\' +} + +pub fn parse_prefix(_p: &OsStr) -> Option> { + None +} + +pub(crate) fn absolute(_path: &Path) -> io::Result { + unsupported() +} diff --git a/library/std/src/sys/uefi/tests.rs b/library/std/src/sys/uefi/tests.rs new file mode 100644 index 0000000000000..8806eda3ac0a6 --- /dev/null +++ b/library/std/src/sys/uefi/tests.rs @@ -0,0 +1,21 @@ +use super::alloc::*; + +#[test] +fn align() { + // UEFI ABI specifies that allocation alignment minimum is always 8. So this can be + // statically verified. + assert_eq!(POOL_ALIGNMENT, 8); + + // Loop over allocation-request sizes from 0-256 and alignments from 1-128, and verify + // that in case of overalignment there is at least space for one additional pointer to + // store in the allocation. + for i in 0..256 { + for j in &[1, 2, 4, 8, 16, 32, 64, 128] { + if *j <= 8 { + assert_eq!(align_size(i, *j), i); + } else { + assert!(align_size(i, *j) > i + std::mem::size_of::<*mut ()>()); + } + } + } +} diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs index f7d821750635d..d65d68337d811 100644 --- a/library/std/src/sys_common/mod.rs +++ b/library/std/src/sys_common/mod.rs @@ -44,6 +44,8 @@ cfg_if::cfg_if! { cfg_if::cfg_if! { if #[cfg(any(target_os = "l4re", + target_os = "hermit", + target_os = "uefi", feature = "restricted-std", all(target_family = "wasm", not(target_os = "emscripten")), target_os = "xous", diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 176ef8e92dbec..836328f94ef81 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -532,11 +532,7 @@ pub struct Target { impl Target { pub fn from_triple(triple: &str) -> Self { let mut target: Self = Default::default(); - if triple.contains("-none") - || triple.contains("nvptx") - || triple.contains("switch") - || triple.contains("-uefi") - { + if triple.contains("-none") || triple.contains("nvptx") || triple.contains("switch") { target.no_std = true; } target diff --git a/src/doc/rustc/src/platform-support/unknown-uefi.md b/src/doc/rustc/src/platform-support/unknown-uefi.md index 03fa284620e4c..8d4d60e12fb1a 100644 --- a/src/doc/rustc/src/platform-support/unknown-uefi.md +++ b/src/doc/rustc/src/platform-support/unknown-uefi.md @@ -19,8 +19,8 @@ Available targets: ## Requirements All UEFI targets can be used as `no-std` environments via cross-compilation. -Support for `std` is missing, but actively worked on. `alloc` is supported if -an allocator is provided by the user. No host tools are supported. +Support for `std` is present, but incomplete and extreamly new. `alloc` is supported if +an allocator is provided by the user or if using std. No host tools are supported. The UEFI environment resembles the environment for Microsoft Windows, with some minor differences. Therefore, cross-compiling for UEFI works with the same @@ -230,3 +230,71 @@ pub extern "C" fn main(_h: efi::Handle, st: *mut efi::SystemTable) -> efi::Statu efi::Status::SUCCESS } ``` + +## Rust std for UEFI +This section contains information on how to use std on UEFI. + +### Build std +The building std part is pretty much the same as the official [docs](https://rustc-dev-guide.rust-lang.org/getting-started.html). +The linker that should be used is `rust-lld`. Here is a sample `config.toml`: +```toml +[llvm] +download-ci-llvm = false +[rust] +lld = true +[target.x86_64-unknown-uefi] +linker = "rust-lld" +``` +Then just build using `x.py`: +```sh +./x.py build --target x86_64-unknown-uefi +``` + +### Std Requirements +The current std has a few basic requirements to function: +1. Memory Allocation Services (`EFI_BOOT_SERVICES.AllocatePool()` and + `EFI_BOOT_SERVICES.FreePool()`) are available. +If the above requirement is satisfied, the Rust code will reach `main`. +Now we will discuss what the different modules of std use in UEFI. + +### Implemented features +#### alloc +- Implemented using `EFI_BOOT_SERVICES.AllocatePool()` and `EFI_BOOT_SERVICES.FreePool()`. +- Passes all the tests. +- Some Quirks: + - Currently uses `EfiLoaderData` as the `EFI_ALLOCATE_POOL->PoolType`. +#### cmath +- Provided by compiler-builtins. +#### env +- Just some global consants. +#### locks +- Uses `unsupported/locks`. +- They should work for a platform without threads according to docs. +#### os_str +- Uses WTF-8 from windows. + +## Example: Hello World With std +The following code is a valid UEFI application showing stdio in UEFI. It also +uses `alloc` type `OsString` and `Vec`. + +This example can be compiled as binary crate via `cargo` using the toolchain +compiled from the above source (named custom): + +```sh +cargo +custom build --target x86_64-unknown-uefi +``` + +```rust,ignore (platform-specific) +use r_efi::efi; +use std::os::uefi::ffi::OsStrExt; +use std::{ffi::OsString, panic}; + +pub fn main() { + let st = std::os::uefi::env::system_table().as_ptr() as *mut efi::SystemTable; + let mut s: Vec = OsString::from("Hello World!\n").encode_wide().collect(); + s.push(0); + let r = + unsafe { ((*(*st).con_out).output_string)((*st).con_out, s.as_ptr() as *mut efi::Char16) }; + assert!(!r.is_error()) +} +``` diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 843ffe2c4c332..daa9cb8d22ed5 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -18,6 +18,7 @@ const LICENSES: &[&str] = &[ "Apache-2.0/MIT", "ISC", "MIT / Apache-2.0", + "MIT OR Apache-2.0 OR LGPL-2.1-or-later", // r-efi, r-efi-alloc "MIT OR Apache-2.0 OR Zlib", // tinyvec_macros "MIT OR Apache-2.0", "MIT OR Zlib OR Apache-2.0", // miniz_oxide @@ -217,6 +218,8 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "pulldown-cmark", "punycode", "quote", + "r-efi", + "r-efi-alloc", "rand", "rand_chacha", "rand_core", From 8e56b33d59593a99fdef6f73f0c1a09a012ca907 Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Sat, 28 Jan 2023 23:37:54 +0530 Subject: [PATCH 2/8] Fixes from PR Signed-off-by: Ayush Singh --- library/std/src/os/uefi/env.rs | 11 +++-- library/std/src/sys/uefi/alloc.rs | 12 ++--- .../src/sys/uefi/{common.rs => helpers.rs} | 37 ++++++++++------ library/std/src/sys/uefi/mod.rs | 24 +++++----- library/std/src/sys_common/mod.rs | 1 - .../src/platform-support/unknown-uefi.md | 44 ++++++++++--------- 6 files changed, 70 insertions(+), 59 deletions(-) rename library/std/src/sys/uefi/{common.rs => helpers.rs} (86%) diff --git a/library/std/src/os/uefi/env.rs b/library/std/src/os/uefi/env.rs index 479ff5f69757e..71bf0ff8ff3f5 100644 --- a/library/std/src/os/uefi/env.rs +++ b/library/std/src/os/uefi/env.rs @@ -1,5 +1,7 @@ //! UEFI-specific extensions to the primitives in `std::env` module +#![unstable(feature = "uefi_std", issue = "100499")] + use crate::ffi::c_void; use crate::ptr::NonNull; use crate::sync::atomic::{AtomicPtr, Ordering}; @@ -22,21 +24,18 @@ static GLOBALS: OnceLock<(AtomicPtr, AtomicPtr)> = OnceLock::new /// 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, system_table: NonNull) { 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")] +/// Note: This function panics if the System Table or Image Handle is not initialized pub fn system_table() -> NonNull { 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")] +/// Get the ImageHandle Pointer. +/// Note: This function panics if the System Table or Image Handle is not initialized pub fn image_handle() -> NonNull { try_image_handle().unwrap() } diff --git a/library/std/src/sys/uefi/alloc.rs b/library/std/src/sys/uefi/alloc.rs index 6a14ee76e92ce..54d86d0c8a75a 100644 --- a/library/std/src/sys/uefi/alloc.rs +++ b/library/std/src/sys/uefi/alloc.rs @@ -13,11 +13,8 @@ unsafe impl GlobalAlloc for System { 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() - } + // 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) { @@ -25,8 +22,7 @@ unsafe impl GlobalAlloc for System { 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) } - } + // The caller must ensure non-0 layout + unsafe { r_efi_alloc::raw::dealloc(system_table, ptr, layout) } } } diff --git a/library/std/src/sys/uefi/common.rs b/library/std/src/sys/uefi/helpers.rs similarity index 86% rename from library/std/src/sys/uefi/common.rs rename to library/std/src/sys/uefi/helpers.rs index 6316a74d3670c..e83adabaf1622 100644 --- a/library/std/src/sys/uefi/common.rs +++ b/library/std/src/sys/uefi/helpers.rs @@ -1,16 +1,25 @@ //! Contains most of the shared UEFI specific stuff. Some of this might be moved to `std::os::uefi` //! if needed but no point in adding extra public API when there is not Std support for UEFI in the //! first place +//! +//! Some Nomenclature +//! * Protocol: +//! - Protocols serve to enable communication between separately built modules, including drivers. +//! - Every protocol has a GUID associated with it. The GUID serves as the name for the protocol. +//! - Protocols are produced and consumed. +//! - More information about protocols can be found [here](https://edk2-docs.gitbook.io/edk-ii-uefi-driver-writer-s-guide/3_foundation/36_protocols_and_handles) use r_efi::efi::Guid; use crate::io::{self, const_io_error}; -use crate::mem::MaybeUninit; +use crate::mem::{size_of, MaybeUninit}; use crate::os::uefi; use crate::ptr::NonNull; -// Locate handles with a particular protocol GUID +/// Locate Handles with a particular Protocol GUID /// Implemented using `EFI_BOOT_SERVICES.LocateHandles()` +/// +/// Returns an array of [Handles](r_efi::efi::Handle) that support a specified protocol. pub(crate) fn locate_handles(mut guid: Guid) -> io::Result>> { fn inner( guid: &mut Guid, @@ -34,6 +43,8 @@ pub(crate) fn locate_handles(mut guid: Guid) -> io::Result unreachable!(), Err(e) => match e.kind() { @@ -44,21 +55,23 @@ pub(crate) fn locate_handles(mut guid: Guid) -> io::Result = - Vec::with_capacity(buf_len / crate::mem::size_of::()); + Vec::with_capacity(buf_len / size_of::()); match inner(&mut guid, boot_services, &mut buf_len, buf.as_mut_ptr()) { Ok(()) => { - // SAFETY: This is safe because the call will succeed only if buf_len >= required - // length. Also, on success, the `buf_len` is updated with the size of bufferv (in - // bytes) written - unsafe { buf.set_len(buf_len / crate::mem::size_of::()) }; - Ok(buf.iter().filter_map(|x| NonNull::new(*x)).collect()) + // This is safe because the call will succeed only if buf_len >= required length. + // Also, on success, the `buf_len` is updated with the size of bufferv (in bytes) written + unsafe { buf.set_len(buf_len / size_of::()) }; + Ok(buf.into_iter().filter_map(|x| NonNull::new(x)).collect()) } Err(e) => Err(e), } } -/// Open Protocol on a handle -/// Implemented using `EFI_BOOT_SERVICES.OpenProtocol()` +/// Open Protocol on a handle. +/// Internally just a call to `EFI_BOOT_SERVICES.OpenProtocol()`. +/// +/// Queries a handle to determine if it supports a specified protocol. If the protocol is +/// supported by the handle, it opens the protocol on behalf of the calling agent. pub(crate) fn open_protocol( handle: NonNull, mut protocol_guid: Guid, @@ -256,9 +269,7 @@ pub(crate) fn status_to_io_error(s: r_efi::efi::Status) -> io::Error { /// Get the BootServices Pointer. pub(crate) fn boot_services() -> NonNull { - let system_table: NonNull = uefi::env::system_table().cast(); - let boot_services = unsafe { (*system_table.as_ptr()).boot_services }; - NonNull::new(boot_services).unwrap() + try_boot_services().unwrap() } /// Get the BootServices Pointer. /// This function is mostly intended for places where panic is not an option diff --git a/library/std/src/sys/uefi/mod.rs b/library/std/src/sys/uefi/mod.rs index 193d4ecc16fa7..fed44b4f49dac 100644 --- a/library/std/src/sys/uefi/mod.rs +++ b/library/std/src/sys/uefi/mod.rs @@ -47,7 +47,7 @@ pub mod thread_local_key; #[path = "../unsupported/time.rs"] pub mod time; -pub(crate) mod common; +pub(crate) mod helpers; #[cfg(test)] mod tests; @@ -60,18 +60,20 @@ pub mod memchr { pub use core::slice::memchr::{memchr, memrchr}; } -// SAFETY: must be called only once during runtime initialization. -// SAFETY: argc must be 2. -// SAFETY: argv must be &[Handle, *mut SystemTable]. -pub unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) { +/// # SAFETY +/// - must be called only once during runtime initialization. +/// - argc must be 2. +/// - argv must be &[Handle, *mut SystemTable]. +pub(crate) unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) { assert_eq!(argc, 2); let image_handle = unsafe { NonNull::new(*argv as *mut crate::ffi::c_void).unwrap() }; let system_table = unsafe { NonNull::new(*argv.add(1) as *mut crate::ffi::c_void).unwrap() }; unsafe { crate::os::uefi::env::init_globals(image_handle, system_table) }; } -// SAFETY: must be called only once during runtime cleanup. -// NOTE: this is not guaranteed to run, for example when the program aborts. +/// # SAFETY +/// this is not guaranteed to run, for example when the program aborts. +/// - must be called only once during runtime cleanup. pub unsafe fn cleanup() {} #[inline] @@ -89,7 +91,7 @@ pub fn decode_error_kind(code: i32) -> crate::io::ErrorKind { use r_efi::efi::Status; if let Ok(code) = usize::try_from(code) { - common::status_to_io_error(Status::from_usize(code)).kind() + helpers::status_to_io_error(Status::from_usize(code)).kind() } else { ErrorKind::Uncategorized } @@ -97,7 +99,7 @@ pub fn decode_error_kind(code: i32) -> crate::io::ErrorKind { pub fn abort_internal() -> ! { if let (Some(boot_services), Some(handle)) = - (common::try_boot_services(), uefi::env::try_image_handle()) + (helpers::try_boot_services(), uefi::env::try_image_handle()) { let _ = unsafe { ((*boot_services.as_ptr()).exit)( @@ -130,9 +132,9 @@ fn get_random() -> Option<(u64, u64)> { use r_efi::protocols::rng; let mut buf = [0u8; 16]; - let handles = common::locate_handles(rng::PROTOCOL_GUID).ok()?; + let handles = helpers::locate_handles(rng::PROTOCOL_GUID).ok()?; for handle in handles { - if let Ok(protocol) = common::open_protocol::(handle, rng::PROTOCOL_GUID) { + if let Ok(protocol) = helpers::open_protocol::(handle, rng::PROTOCOL_GUID) { let r = unsafe { ((*protocol.as_ptr()).get_rng)( protocol.as_ptr(), diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs index d65d68337d811..e18638f2a5f54 100644 --- a/library/std/src/sys_common/mod.rs +++ b/library/std/src/sys_common/mod.rs @@ -44,7 +44,6 @@ cfg_if::cfg_if! { cfg_if::cfg_if! { if #[cfg(any(target_os = "l4re", - target_os = "hermit", target_os = "uefi", feature = "restricted-std", all(target_family = "wasm", not(target_os = "emscripten")), diff --git a/src/doc/rustc/src/platform-support/unknown-uefi.md b/src/doc/rustc/src/platform-support/unknown-uefi.md index 8d4d60e12fb1a..1a4cbc9f092f0 100644 --- a/src/doc/rustc/src/platform-support/unknown-uefi.md +++ b/src/doc/rustc/src/platform-support/unknown-uefi.md @@ -19,7 +19,7 @@ Available targets: ## Requirements All UEFI targets can be used as `no-std` environments via cross-compilation. -Support for `std` is present, but incomplete and extreamly new. `alloc` is supported if +Support for `std` is present, but incomplete and extremely new. `alloc` is supported if an allocator is provided by the user or if using std. No host tools are supported. The UEFI environment resembles the environment for Microsoft Windows, with some @@ -238,22 +238,22 @@ This section contains information on how to use std on UEFI. The building std part is pretty much the same as the official [docs](https://rustc-dev-guide.rust-lang.org/getting-started.html). The linker that should be used is `rust-lld`. Here is a sample `config.toml`: ```toml -[llvm] -download-ci-llvm = false [rust] lld = true -[target.x86_64-unknown-uefi] -linker = "rust-lld" ``` Then just build using `x.py`: ```sh -./x.py build --target x86_64-unknown-uefi +./x.py build --target x86_64-unknown-uefi --stage 1 +``` +Alternatively, it is possible to use the `build-std` feature. However, you must use a toolchain which has the UEFI std patches. +Then just build the project using the following command: +```sh +cargo build --target x86_64-unknown-uefi -Zbuild-std=std,panic_abort ``` ### Std Requirements The current std has a few basic requirements to function: -1. Memory Allocation Services (`EFI_BOOT_SERVICES.AllocatePool()` and - `EFI_BOOT_SERVICES.FreePool()`) are available. +1. Memory Allocation Services (`EFI_BOOT_SERVICES.AllocatePool()` and `EFI_BOOT_SERVICES.FreePool()`) are available. This should be true in in the Driver Execution Environment or later. If the above requirement is satisfied, the Rust code will reach `main`. Now we will discuss what the different modules of std use in UEFI. @@ -261,21 +261,19 @@ Now we will discuss what the different modules of std use in UEFI. #### alloc - Implemented using `EFI_BOOT_SERVICES.AllocatePool()` and `EFI_BOOT_SERVICES.FreePool()`. - Passes all the tests. -- Some Quirks: - - Currently uses `EfiLoaderData` as the `EFI_ALLOCATE_POOL->PoolType`. +- Currently uses `EfiLoaderData` as the `EFI_ALLOCATE_POOL->PoolType`. #### cmath - Provided by compiler-builtins. #### env -- Just some global consants. +- Just some global constants. #### locks -- Uses `unsupported/locks`. -- They should work for a platform without threads according to docs. +- The provided locks should work on all standard single-threaded UEFI implementations. #### os_str -- Uses WTF-8 from windows. +- While the strings in UEFI should be valid UCS-2, in practice, many implementations just do not care and use UTF-16 strings. +- Thus, the current implementation supports full UTF-16 strings. ## Example: Hello World With std -The following code is a valid UEFI application showing stdio in UEFI. It also -uses `alloc` type `OsString` and `Vec`. +The following code features a valid UEFI application, including stdio and `alloc` (`OsString` and `Vec`): This example can be compiled as binary crate via `cargo` using the toolchain compiled from the above source (named custom): @@ -286,15 +284,21 @@ cargo +custom build --target x86_64-unknown-uefi ```rust,ignore (platform-specific) use r_efi::efi; -use std::os::uefi::ffi::OsStrExt; -use std::{ffi::OsString, panic}; +use std::{ + ffi::OsString, + os::uefi::{env, ffi::OsStrExt} +}; pub fn main() { - let st = std::os::uefi::env::system_table().as_ptr() as *mut efi::SystemTable; + let st = env::system_table().as_ptr() as *mut efi::SystemTable; let mut s: Vec = OsString::from("Hello World!\n").encode_wide().collect(); s.push(0); let r = - unsafe { ((*(*st).con_out).output_string)((*st).con_out, s.as_ptr() as *mut efi::Char16) }; + unsafe { + let con_out: *mut simple_text_output::Protocol = (*st).con_out; + let output_string: extern "efiapi" fn(_: *mut simple_text_output::Protocol, *mut u16) = (*con_out).output_string; + output_string(con_out, s.as_ptr() as *mut efi::Char16) + }; assert!(!r.is_error()) } ``` From 032e3766d54fe1b91ed13ff12652f91354d019cb Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Sat, 25 Mar 2023 14:20:49 +0530 Subject: [PATCH 3/8] Handle ExitBootServices - Make BootServices unavailable if ExitBootServices event is signaled. - Use thread locals for SystemTable and ImageHandle Signed-off-by: Ayush Singh --- library/std/src/os/uefi/env.rs | 43 +++++++++++++++++++------ library/std/src/sys/uefi/alloc.rs | 10 ++++++ library/std/src/sys/uefi/helpers.rs | 50 +++++++++++++++++++++-------- library/std/src/sys/uefi/mod.rs | 39 ++++++++++++++++++++-- 4 files changed, 116 insertions(+), 26 deletions(-) diff --git a/library/std/src/os/uefi/env.rs b/library/std/src/os/uefi/env.rs index 71bf0ff8ff3f5..e62441596f3a7 100644 --- a/library/std/src/os/uefi/env.rs +++ b/library/std/src/os/uefi/env.rs @@ -2,14 +2,17 @@ #![unstable(feature = "uefi_std", issue = "100499")] -use crate::ffi::c_void; -use crate::ptr::NonNull; -use crate::sync::atomic::{AtomicPtr, Ordering}; -use crate::sync::OnceLock; +use crate::{cell::Cell, ffi::c_void, ptr::NonNull}; -// Position 0 = SystemTable -// Position 1 = ImageHandle -static GLOBALS: OnceLock<(AtomicPtr, AtomicPtr)> = OnceLock::new(); +// Since UEFI is single-threaded, making the global variables thread local should be safe. +thread_local! { + // Flag to check if BootServices are still valid. + // Start with assuming that they are not available + static BOOT_SERVICES_FLAG: Cell = Cell::new(false); + // Position 0 = SystemTable + // Position 1 = ImageHandle + static GLOBALS: Cell, NonNull)>> = Cell::new(None); +} /// Initializes the global System Table and Image Handle pointers. /// @@ -25,7 +28,7 @@ static GLOBALS: OnceLock<(AtomicPtr, AtomicPtr)> = OnceLock::new /// /// This function must not be called more than once. pub unsafe fn init_globals(handle: NonNull, system_table: NonNull) { - GLOBALS.set((AtomicPtr::new(system_table.as_ptr()), AtomicPtr::new(handle.as_ptr()))).unwrap() + GLOBALS.set(Some((system_table, handle))); } /// Get the SystemTable Pointer. @@ -43,11 +46,31 @@ pub fn image_handle() -> NonNull { /// 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::new(GLOBALS.get()?.0.load(Ordering::Acquire)) + GLOBALS.get().map(|x| x.0) } /// 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::new(GLOBALS.get()?.1.load(Ordering::Acquire)) + GLOBALS.get().map(|x| x.1) +} + +/// Get the BootServices Pointer. +/// This function also checks if `ExitBootServices` has already been called. +pub(crate) fn boot_services() -> Option> { + if BOOT_SERVICES_FLAG.get() { + let system_table: NonNull = try_system_table()?.cast(); + let boot_services = unsafe { (*system_table.as_ptr()).boot_services }; + NonNull::new(boot_services) + } else { + None + } +} + +pub(crate) fn enable_boot_services() { + BOOT_SERVICES_FLAG.set(true); +} + +pub(crate) fn disable_boot_services() { + BOOT_SERVICES_FLAG.set(false); } diff --git a/library/std/src/sys/uefi/alloc.rs b/library/std/src/sys/uefi/alloc.rs index 54d86d0c8a75a..d6eb371033ce6 100644 --- a/library/std/src/sys/uefi/alloc.rs +++ b/library/std/src/sys/uefi/alloc.rs @@ -8,6 +8,11 @@ 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(); + } + let system_table = match crate::os::uefi::env::try_system_table() { None => return crate::ptr::null_mut(), Some(x) => x.as_ptr() as *mut _, @@ -18,6 +23,11 @@ unsafe impl GlobalAlloc for System { } 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; + } + let system_table = match crate::os::uefi::env::try_system_table() { None => handle_alloc_error(layout), Some(x) => x.as_ptr() as *mut _, diff --git a/library/std/src/sys/uefi/helpers.rs b/library/std/src/sys/uefi/helpers.rs index e83adabaf1622..28a8239422688 100644 --- a/library/std/src/sys/uefi/helpers.rs +++ b/library/std/src/sys/uefi/helpers.rs @@ -9,12 +9,18 @@ //! - Protocols are produced and consumed. //! - More information about protocols can be found [here](https://edk2-docs.gitbook.io/edk-ii-uefi-driver-writer-s-guide/3_foundation/36_protocols_and_handles) -use r_efi::efi::Guid; +use r_efi::efi::{self, Guid}; -use crate::io::{self, const_io_error}; use crate::mem::{size_of, MaybeUninit}; use crate::os::uefi; use crate::ptr::NonNull; +use crate::{ + io::{self, const_io_error}, + os::uefi::env::boot_services, +}; + +const BOOT_SERVICES_UNAVAILABLE: io::Error = + const_io_error!(io::ErrorKind::Other, "Boot Services are no longer available"); /// Locate Handles with a particular Protocol GUID /// Implemented using `EFI_BOOT_SERVICES.LocateHandles()` @@ -40,7 +46,7 @@ pub(crate) fn locate_handles(mut guid: Guid) -> io::Result( handle: NonNull, mut protocol_guid: Guid, ) -> io::Result> { - let boot_services = boot_services(); + let boot_services = boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?; let system_handle = uefi::env::image_handle(); let mut protocol: MaybeUninit<*mut T> = MaybeUninit::uninit(); @@ -267,14 +273,32 @@ pub(crate) fn status_to_io_error(s: r_efi::efi::Status) -> io::Error { } } -/// Get the BootServices Pointer. -pub(crate) fn boot_services() -> NonNull { - try_boot_services().unwrap() +pub(crate) fn create_event( + signal: u32, + tpl: efi::Tpl, + handler: Option, + context: *mut crate::ffi::c_void, +) -> io::Result> { + let boot_services = boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?; + let mut exit_boot_service_event: r_efi::efi::Event = crate::ptr::null_mut(); + let r = unsafe { + let create_event = (*boot_services.as_ptr()).create_event; + (create_event)(signal, tpl, handler, context, &mut exit_boot_service_event) + }; + if r.is_error() { + Err(status_to_io_error(r)) + } else { + NonNull::new(exit_boot_service_event) + .ok_or(const_io_error!(io::ErrorKind::Other, "null protocol")) + } } -/// Get the BootServices Pointer. -/// This function is mostly intended for places where panic is not an option -pub(crate) fn try_boot_services() -> Option> { - let system_table: NonNull = uefi::env::try_system_table()?.cast(); - let boot_services = unsafe { (*system_table.as_ptr()).boot_services }; - NonNull::new(boot_services) + +pub(crate) fn close_event(evt: NonNull) -> io::Result<()> { + let boot_services = boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?; + let r = unsafe { + let close_event = (*boot_services.as_ptr()).close_event; + (close_event)(evt.as_ptr()) + }; + + if r.is_error() { Err(status_to_io_error(r)) } else { Ok(()) } } diff --git a/library/std/src/sys/uefi/mod.rs b/library/std/src/sys/uefi/mod.rs index fed44b4f49dac..fa33b681a19d5 100644 --- a/library/std/src/sys/uefi/mod.rs +++ b/library/std/src/sys/uefi/mod.rs @@ -52,6 +52,7 @@ pub(crate) mod helpers; #[cfg(test)] mod tests; +use crate::cell::Cell; use crate::io as std_io; use crate::os::uefi; use crate::ptr::NonNull; @@ -60,6 +61,10 @@ pub mod memchr { pub use core::slice::memchr::{memchr, memrchr}; } +thread_local! { + static EXIT_BOOT_SERVICE_EVENT: Cell>> = Cell::new(None); +} + /// # SAFETY /// - must be called only once during runtime initialization. /// - argc must be 2. @@ -68,13 +73,32 @@ pub(crate) unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) { assert_eq!(argc, 2); let image_handle = unsafe { NonNull::new(*argv as *mut crate::ffi::c_void).unwrap() }; let system_table = unsafe { NonNull::new(*argv.add(1) as *mut crate::ffi::c_void).unwrap() }; - unsafe { crate::os::uefi::env::init_globals(image_handle, system_table) }; + unsafe { uefi::env::init_globals(image_handle, system_table) }; + // Enable boot services once GLOBALS are initialized + uefi::env::enable_boot_services(); + + // Register exit boot services handler + match helpers::create_event( + r_efi::efi::EVT_SIGNAL_EXIT_BOOT_SERVICES, + r_efi::efi::TPL_NOTIFY, + Some(exit_boot_service_handler), + crate::ptr::null_mut(), + ) { + Ok(x) => { + EXIT_BOOT_SERVICE_EVENT.set(Some(x)); + } + Err(_) => abort_internal(), + } } /// # SAFETY /// this is not guaranteed to run, for example when the program aborts. /// - must be called only once during runtime cleanup. -pub unsafe fn cleanup() {} +pub unsafe fn cleanup() { + if let Some(exit_boot_service_event) = EXIT_BOOT_SERVICE_EVENT.take() { + let _ = helpers::close_event(exit_boot_service_event); + } +} #[inline] pub const fn unsupported() -> std_io::Result { @@ -98,8 +122,12 @@ pub fn decode_error_kind(code: i32) -> crate::io::ErrorKind { } pub fn abort_internal() -> ! { + if let Some(exit_boot_service_event) = EXIT_BOOT_SERVICE_EVENT.take() { + let _ = helpers::close_event(exit_boot_service_event); + } + if let (Some(boot_services), Some(handle)) = - (helpers::try_boot_services(), uefi::env::try_image_handle()) + (uefi::env::boot_services(), uefi::env::try_image_handle()) { let _ = unsafe { ((*boot_services.as_ptr()).exit)( @@ -155,3 +183,8 @@ fn get_random() -> Option<(u64, u64)> { } None } + +/// Disable access to BootServices if `EVT_SIGNAL_EXIT_BOOT_SERVICES` is signaled +extern "efiapi" fn exit_boot_service_handler(_e: r_efi::efi::Event, _ctx: *mut crate::ffi::c_void) { + uefi::env::disable_boot_services(); +} From 5df24d18b6cdb8abcede6b658cf066360a219c12 Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Mon, 27 Mar 2023 19:53:53 +0530 Subject: [PATCH 4/8] Add support for building `std::os::uefi` docs Signed-off-by: Ayush Singh --- library/std/src/os/mod.rs | 1 + library/std/src/os/uefi/env.rs | 32 +++++++++++-------- library/std/src/os/uefi/mod.rs | 1 + library/std/src/sys/uefi/alloc.rs | 15 +++------ library/std/src/sys/uefi/helpers.rs | 11 ++++--- library/std/src/sys/uefi/mod.rs | 1 + .../src/platform-support/unknown-uefi.md | 11 +++---- 7 files changed, 38 insertions(+), 34 deletions(-) diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs index 11ad21515fdec..d1a735038992c 100644 --- a/library/std/src/os/mod.rs +++ b/library/std/src/os/mod.rs @@ -143,6 +143,7 @@ pub mod solid; #[path = "ios/mod.rs"] pub(crate) mod tvos; #[cfg(target_os = "uefi")] +#[cfg(any(target_os = "uefi", doc))] pub mod uefi; #[cfg(target_os = "vita")] pub mod vita; diff --git a/library/std/src/os/uefi/env.rs b/library/std/src/os/uefi/env.rs index e62441596f3a7..41dc7ccdb6ade 100644 --- a/library/std/src/os/uefi/env.rs +++ b/library/std/src/os/uefi/env.rs @@ -32,41 +32,45 @@ pub unsafe fn init_globals(handle: NonNull, system_table: NonNull NonNull { 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 { 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> { - GLOBALS.get().map(|x| x.0) -} - -/// Get the SystemHandle Pointer. -/// This function is mostly intended for places where panic is not an option -pub(crate) fn try_image_handle() -> Option> { - GLOBALS.get().map(|x| x.1) -} - /// Get the BootServices Pointer. /// This function also checks if `ExitBootServices` has already been called. -pub(crate) fn boot_services() -> Option> { +pub fn boot_services() -> Option> { if BOOT_SERVICES_FLAG.get() { let system_table: NonNull = try_system_table()?.cast(); let boot_services = unsafe { (*system_table.as_ptr()).boot_services }; - NonNull::new(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> { + GLOBALS.get().map(|x| x.0) +} + +/// Get the SystemHandle Pointer. +/// This function is mostly intended for places where panic is not an option +pub(crate) fn try_image_handle() -> Option> { + GLOBALS.get().map(|x| x.1) +} + pub(crate) fn enable_boot_services() { BOOT_SERVICES_FLAG.set(true); } diff --git a/library/std/src/os/uefi/mod.rs b/library/std/src/os/uefi/mod.rs index fc0468f25fc8e..8ef05eee1f4e7 100644 --- a/library/std/src/os/uefi/mod.rs +++ b/library/std/src/os/uefi/mod.rs @@ -1,6 +1,7 @@ //! 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"] diff --git a/library/std/src/sys/uefi/alloc.rs b/library/std/src/sys/uefi/alloc.rs index d6eb371033ce6..789e3cbd81ade 100644 --- a/library/std/src/sys/uefi/alloc.rs +++ b/library/std/src/sys/uefi/alloc.rs @@ -1,7 +1,7 @@ //! Global Allocator for UEFI. //! Uses [r-efi-alloc](https://crates.io/crates/r-efi-alloc) -use crate::alloc::{handle_alloc_error, GlobalAlloc, Layout, System}; +use crate::alloc::{GlobalAlloc, Layout, System}; const MEMORY_TYPE: u32 = r_efi::efi::LOADER_DATA; @@ -13,11 +13,8 @@ unsafe impl GlobalAlloc for System { return crate::ptr::null_mut(); } - 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 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) } } @@ -28,10 +25,8 @@ unsafe impl GlobalAlloc for System { return; } - let system_table = match crate::os::uefi::env::try_system_table() { - None => handle_alloc_error(layout), - Some(x) => x.as_ptr() as *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::dealloc(system_table, ptr, layout) } } diff --git a/library/std/src/sys/uefi/helpers.rs b/library/std/src/sys/uefi/helpers.rs index 28a8239422688..2108484a40c3d 100644 --- a/library/std/src/sys/uefi/helpers.rs +++ b/library/std/src/sys/uefi/helpers.rs @@ -46,7 +46,7 @@ pub(crate) fn locate_handles(mut guid: Guid) -> io::Result( handle: NonNull, mut protocol_guid: Guid, ) -> io::Result> { - let boot_services = boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?; + let boot_services: NonNull = + boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?.cast(); let system_handle = uefi::env::image_handle(); let mut protocol: MaybeUninit<*mut T> = MaybeUninit::uninit(); @@ -279,7 +280,8 @@ pub(crate) fn create_event( handler: Option, context: *mut crate::ffi::c_void, ) -> io::Result> { - let boot_services = boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?; + let boot_services: NonNull = + boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?.cast(); let mut exit_boot_service_event: r_efi::efi::Event = crate::ptr::null_mut(); let r = unsafe { let create_event = (*boot_services.as_ptr()).create_event; @@ -294,7 +296,8 @@ pub(crate) fn create_event( } pub(crate) fn close_event(evt: NonNull) -> io::Result<()> { - let boot_services = boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?; + let boot_services: NonNull = + boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?.cast(); let r = unsafe { let close_event = (*boot_services.as_ptr()).close_event; (close_event)(evt.as_ptr()) diff --git a/library/std/src/sys/uefi/mod.rs b/library/std/src/sys/uefi/mod.rs index fa33b681a19d5..6fd2c5f21cb4e 100644 --- a/library/std/src/sys/uefi/mod.rs +++ b/library/std/src/sys/uefi/mod.rs @@ -129,6 +129,7 @@ pub fn abort_internal() -> ! { if let (Some(boot_services), Some(handle)) = (uefi::env::boot_services(), uefi::env::try_image_handle()) { + let boot_services: NonNull = boot_services.cast(); let _ = unsafe { ((*boot_services.as_ptr()).exit)( handle.as_ptr(), diff --git a/src/doc/rustc/src/platform-support/unknown-uefi.md b/src/doc/rustc/src/platform-support/unknown-uefi.md index 1a4cbc9f092f0..e2c09d67bedab 100644 --- a/src/doc/rustc/src/platform-support/unknown-uefi.md +++ b/src/doc/rustc/src/platform-support/unknown-uefi.md @@ -251,12 +251,6 @@ Then just build the project using the following command: cargo build --target x86_64-unknown-uefi -Zbuild-std=std,panic_abort ``` -### Std Requirements -The current std has a few basic requirements to function: -1. Memory Allocation Services (`EFI_BOOT_SERVICES.AllocatePool()` and `EFI_BOOT_SERVICES.FreePool()`) are available. This should be true in in the Driver Execution Environment or later. -If the above requirement is satisfied, the Rust code will reach `main`. -Now we will discuss what the different modules of std use in UEFI. - ### Implemented features #### alloc - Implemented using `EFI_BOOT_SERVICES.AllocatePool()` and `EFI_BOOT_SERVICES.FreePool()`. @@ -302,3 +296,8 @@ pub fn main() { assert!(!r.is_error()) } ``` + +### BootServices +The current implementation of std make `BootServices` unavailable once `ExitBootServices` is called. Refer to [Runtime Drivers](https://edk2-docs.gitbook.io/edk-ii-uefi-driver-writer-s-guide/7_driver_entry_point/711_runtime_drivers) for more information regarding how to handle switching from using physical addresses to using virtual addresses. + +Note: It should be noted that it is upto the user to drop all allocated memory before `ExitBootServices` is called. From 7a956441a15e6056448233d79f2d03581ce0ccfc Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Tue, 28 Mar 2023 20:14:33 +0530 Subject: [PATCH 5/8] Fixes from PR - Some comment fixes. - Make some functions unsafe. - Make helpers module private. - Rebase on master - Update r-efi to v4.2.0 Signed-off-by: Ayush Singh --- .nlsp-settings/rust_analyzer.json | 3 --- Cargo.lock | 4 ++-- library/std/Cargo.toml | 2 +- library/std/src/os/uefi/env.rs | 2 +- library/std/src/sys/uefi/helpers.rs | 4 +++- library/std/src/sys/uefi/mod.rs | 6 +++--- src/doc/rustc/src/platform-support/unknown-uefi.md | 2 +- 7 files changed, 11 insertions(+), 12 deletions(-) delete mode 100644 .nlsp-settings/rust_analyzer.json diff --git a/.nlsp-settings/rust_analyzer.json b/.nlsp-settings/rust_analyzer.json deleted file mode 100644 index eddf84afb2a3d..0000000000000 --- a/.nlsp-settings/rust_analyzer.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "rust-analyzer.cargo.target": "x86_64-unknown-uefi" -} diff --git a/Cargo.lock b/Cargo.lock index f84387f30be61..a2025e58a8d1d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3008,9 +3008,9 @@ dependencies = [ [[package]] name = "r-efi" -version = "4.1.0" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e7345c622833c6745e7b027a28aa95618813dc1f3c3de396206410267dce6f3" +checksum = "575fc2d9b3da54adbdfaddf6eca48fec256d977c8630a1750b8991347d1ac911" dependencies = [ "compiler_builtins", "rustc-std-workspace-core", diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 1908dfaa33db0..965132bdedb91 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -49,7 +49,7 @@ hermit-abi = { version = "0.3.2", features = ['rustc-dep-of-std'], public = true 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 = { version = "4.2.0", features = ['rustc-dep-of-std']} r-efi-alloc = { version = "1.0.0", features = ['rustc-dep-of-std']} [features] diff --git a/library/std/src/os/uefi/env.rs b/library/std/src/os/uefi/env.rs index 41dc7ccdb6ade..4dd090e22c620 100644 --- a/library/std/src/os/uefi/env.rs +++ b/library/std/src/os/uefi/env.rs @@ -66,7 +66,7 @@ pub(crate) fn try_system_table() -> Option> { } /// Get the SystemHandle Pointer. -/// This function is mostly intended for places where panic is not an option +/// This function is mostly intended for places where panicking is not an option pub(crate) fn try_image_handle() -> Option> { GLOBALS.get().map(|x| x.1) } diff --git a/library/std/src/sys/uefi/helpers.rs b/library/std/src/sys/uefi/helpers.rs index 2108484a40c3d..ea1c68a90ef83 100644 --- a/library/std/src/sys/uefi/helpers.rs +++ b/library/std/src/sys/uefi/helpers.rs @@ -295,7 +295,9 @@ pub(crate) fn create_event( } } -pub(crate) fn close_event(evt: NonNull) -> io::Result<()> { +/// # SAFETY +/// - The supplied event must be valid +pub(crate) unsafe fn close_event(evt: NonNull) -> io::Result<()> { let boot_services: NonNull = boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?.cast(); let r = unsafe { diff --git a/library/std/src/sys/uefi/mod.rs b/library/std/src/sys/uefi/mod.rs index 6fd2c5f21cb4e..27f76f04c05aa 100644 --- a/library/std/src/sys/uefi/mod.rs +++ b/library/std/src/sys/uefi/mod.rs @@ -47,7 +47,7 @@ pub mod thread_local_key; #[path = "../unsupported/time.rs"] pub mod time; -pub(crate) mod helpers; +mod helpers; #[cfg(test)] mod tests; @@ -96,7 +96,7 @@ pub(crate) unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) { /// - must be called only once during runtime cleanup. pub unsafe fn cleanup() { if let Some(exit_boot_service_event) = EXIT_BOOT_SERVICE_EVENT.take() { - let _ = helpers::close_event(exit_boot_service_event); + let _ = unsafe { helpers::close_event(exit_boot_service_event) }; } } @@ -123,7 +123,7 @@ pub fn decode_error_kind(code: i32) -> crate::io::ErrorKind { pub fn abort_internal() -> ! { if let Some(exit_boot_service_event) = EXIT_BOOT_SERVICE_EVENT.take() { - let _ = helpers::close_event(exit_boot_service_event); + let _ = unsafe { helpers::close_event(exit_boot_service_event) }; } if let (Some(boot_services), Some(handle)) = diff --git a/src/doc/rustc/src/platform-support/unknown-uefi.md b/src/doc/rustc/src/platform-support/unknown-uefi.md index e2c09d67bedab..019f030eb1c10 100644 --- a/src/doc/rustc/src/platform-support/unknown-uefi.md +++ b/src/doc/rustc/src/platform-support/unknown-uefi.md @@ -300,4 +300,4 @@ pub fn main() { ### BootServices The current implementation of std make `BootServices` unavailable once `ExitBootServices` is called. Refer to [Runtime Drivers](https://edk2-docs.gitbook.io/edk-ii-uefi-driver-writer-s-guide/7_driver_entry_point/711_runtime_drivers) for more information regarding how to handle switching from using physical addresses to using virtual addresses. -Note: It should be noted that it is upto the user to drop all allocated memory before `ExitBootServices` is called. +Note: It should be noted that it is upto the user to drop all allocated memory before `ExitBootServices` is called. From 40c3dacc767a4fbf42ea5dfa686a54acf9ded5fb Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Thu, 11 May 2023 11:35:15 +0530 Subject: [PATCH 6/8] Use RawOsError for UEFI Some changes from this commit will probably be converted to its own PR. Signed-off-by: Ayush Singh --- library/std/src/sys/mod.rs | 1 + library/std/src/sys/uefi/helpers.rs | 176 +---------------------- library/std/src/sys/uefi/mod.rs | 49 ++++++- library/std/src/sys/uefi/os.rs | 213 ++++++++++++++++++++++++++++ 4 files changed, 261 insertions(+), 178 deletions(-) create mode 100644 library/std/src/sys/uefi/os.rs diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index a03501e6a1357..159ffe7ac9635 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -117,4 +117,5 @@ pub fn log_wrapper f64>(n: f64, log_fn: F) -> f64 { log_fn(n) } +#[cfg(not(target_os = "uefi"))] pub type RawOsError = i32; diff --git a/library/std/src/sys/uefi/helpers.rs b/library/std/src/sys/uefi/helpers.rs index ea1c68a90ef83..2e1bcb369447e 100644 --- a/library/std/src/sys/uefi/helpers.rs +++ b/library/std/src/sys/uefi/helpers.rs @@ -43,7 +43,7 @@ pub(crate) fn locate_handles(mut guid: Guid) -> io::Result( }; if r.is_error() { - Err(status_to_io_error(r)) + Err(crate::io::Error::from_raw_os_error(r.as_usize())) } else { NonNull::new(unsafe { protocol.assume_init() }) .ok_or(const_io_error!(io::ErrorKind::Other, "null protocol")) } } -pub(crate) fn status_to_io_error(s: r_efi::efi::Status) -> io::Error { - use io::ErrorKind; - use r_efi::efi::Status; - - // Keep the List in Alphabetical Order - // The Messages are taken from UEFI Specification Appendix D - Status Codes - match s { - Status::ABORTED => { - const_io_error!(ErrorKind::ConnectionAborted, "The operation was aborted.") - } - Status::ACCESS_DENIED => { - const_io_error!(ErrorKind::PermissionDenied, "Access was denied.") - } - Status::ALREADY_STARTED => { - const_io_error!(ErrorKind::Other, "The protocol has already been started.") - } - Status::BAD_BUFFER_SIZE => { - const_io_error!( - ErrorKind::InvalidData, - "The buffer was not the proper size for the request." - ) - } - Status::BUFFER_TOO_SMALL => { - const_io_error!( - ErrorKind::FileTooLarge, - "The buffer is not large enough to hold the requested data. The required buffer size is returned in the appropriate parameter when this error occurs." - ) - } - Status::COMPROMISED_DATA => { - const_io_error!( - ErrorKind::Other, - "The security status of the data is unknown or compromised and the data must be updated or replaced to restore a valid security status." - ) - } - Status::CONNECTION_FIN => { - const_io_error!( - ErrorKind::Other, - "The receiving operation fails because the communication peer has closed the connection and there is no more data in the receive buffer of the instance." - ) - } - Status::CONNECTION_REFUSED => { - const_io_error!( - ErrorKind::ConnectionRefused, - "The receiving or transmission operation fails because this connection is refused." - ) - } - Status::CONNECTION_RESET => { - const_io_error!( - ErrorKind::ConnectionReset, - "The connect fails because the connection is reset either by instance itself or the communication peer." - ) - } - Status::CRC_ERROR => const_io_error!(ErrorKind::Other, "A CRC error was detected."), - Status::DEVICE_ERROR => const_io_error!( - ErrorKind::Other, - "The physical device reported an error while attempting the operation." - ), - Status::END_OF_FILE => { - const_io_error!(ErrorKind::UnexpectedEof, "The end of the file was reached.") - } - Status::END_OF_MEDIA => { - const_io_error!(ErrorKind::Other, "Beginning or end of media was reached") - } - Status::HOST_UNREACHABLE => { - const_io_error!(ErrorKind::HostUnreachable, "The remote host is not reachable.") - } - Status::HTTP_ERROR => { - const_io_error!(ErrorKind::Other, "A HTTP error occurred during the network operation.") - } - Status::ICMP_ERROR => { - const_io_error!( - ErrorKind::Other, - "An ICMP error occurred during the network operation." - ) - } - Status::INCOMPATIBLE_VERSION => { - const_io_error!( - ErrorKind::Other, - "The function encountered an internal version that was incompatible with a version requested by the caller." - ) - } - Status::INVALID_LANGUAGE => { - const_io_error!(ErrorKind::InvalidData, "The language specified was invalid.") - } - Status::INVALID_PARAMETER => { - const_io_error!(ErrorKind::InvalidInput, "A parameter was incorrect.") - } - Status::IP_ADDRESS_CONFLICT => { - const_io_error!(ErrorKind::AddrInUse, "There is an address conflict address allocation") - } - Status::LOAD_ERROR => { - const_io_error!(ErrorKind::Other, "The image failed to load.") - } - Status::MEDIA_CHANGED => { - const_io_error!( - ErrorKind::Other, - "The medium in the device has changed since the last access." - ) - } - Status::NETWORK_UNREACHABLE => { - const_io_error!( - ErrorKind::NetworkUnreachable, - "The network containing the remote host is not reachable." - ) - } - Status::NO_MAPPING => { - const_io_error!(ErrorKind::Other, "A mapping to a device does not exist.") - } - Status::NO_MEDIA => { - const_io_error!( - ErrorKind::Other, - "The device does not contain any medium to perform the operation." - ) - } - Status::NO_RESPONSE => { - const_io_error!( - ErrorKind::HostUnreachable, - "The server was not found or did not respond to the request." - ) - } - Status::NOT_FOUND => const_io_error!(ErrorKind::NotFound, "The item was not found."), - Status::NOT_READY => { - const_io_error!(ErrorKind::ResourceBusy, "There is no data pending upon return.") - } - Status::NOT_STARTED => { - const_io_error!(ErrorKind::Other, "The protocol has not been started.") - } - Status::OUT_OF_RESOURCES => { - const_io_error!(ErrorKind::OutOfMemory, "A resource has run out.") - } - Status::PROTOCOL_ERROR => { - const_io_error!( - ErrorKind::Other, - "A protocol error occurred during the network operation." - ) - } - Status::PROTOCOL_UNREACHABLE => { - const_io_error!(ErrorKind::Other, "An ICMP protocol unreachable error is received.") - } - Status::SECURITY_VIOLATION => { - const_io_error!( - ErrorKind::PermissionDenied, - "The function was not performed due to a security violation." - ) - } - Status::TFTP_ERROR => { - const_io_error!(ErrorKind::Other, "A TFTP error occurred during the network operation.") - } - Status::TIMEOUT => const_io_error!(ErrorKind::TimedOut, "The timeout time expired."), - Status::UNSUPPORTED => { - const_io_error!(ErrorKind::Unsupported, "The operation is not supported.") - } - Status::VOLUME_FULL => { - const_io_error!(ErrorKind::StorageFull, "There is no more space on the file system.") - } - Status::VOLUME_CORRUPTED => { - const_io_error!( - ErrorKind::Other, - "An inconstancy was detected on the file system causing the operating to fail." - ) - } - Status::WRITE_PROTECTED => { - const_io_error!(ErrorKind::ReadOnlyFilesystem, "The device cannot be written to.") - } - _ => io::Error::new(ErrorKind::Uncategorized, format!("Status: {}", s.as_usize())), - } -} - pub(crate) fn create_event( signal: u32, tpl: efi::Tpl, @@ -288,7 +120,7 @@ pub(crate) fn create_event( (create_event)(signal, tpl, handler, context, &mut exit_boot_service_event) }; if r.is_error() { - Err(status_to_io_error(r)) + Err(crate::io::Error::from_raw_os_error(r.as_usize())) } else { NonNull::new(exit_boot_service_event) .ok_or(const_io_error!(io::ErrorKind::Other, "null protocol")) @@ -305,5 +137,5 @@ pub(crate) unsafe fn close_event(evt: NonNull) -> io::Result (close_event)(evt.as_ptr()) }; - if r.is_error() { Err(status_to_io_error(r)) } else { Ok(()) } + if r.is_error() { Err(crate::io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } } diff --git a/library/std/src/sys/uefi/mod.rs b/library/std/src/sys/uefi/mod.rs index 27f76f04c05aa..85d00caf14911 100644 --- a/library/std/src/sys/uefi/mod.rs +++ b/library/std/src/sys/uefi/mod.rs @@ -29,7 +29,6 @@ pub mod locks; pub mod net; #[path = "../unsupported/once.rs"] pub mod once; -#[path = "../unsupported/os.rs"] pub mod os; #[path = "../windows/os_str.rs"] pub mod os_str; @@ -52,6 +51,8 @@ mod helpers; #[cfg(test)] mod tests; +pub type RawOsError = usize; + use crate::cell::Cell; use crate::io as std_io; use crate::os::uefi; @@ -110,14 +111,50 @@ pub const fn unsupported_err() -> std_io::Error { std_io::const_io_error!(std_io::ErrorKind::Unsupported, "operation not supported on UEFI",) } -pub fn decode_error_kind(code: i32) -> crate::io::ErrorKind { +pub fn decode_error_kind(code: RawOsError) -> crate::io::ErrorKind { use crate::io::ErrorKind; use r_efi::efi::Status; - if let Ok(code) = usize::try_from(code) { - helpers::status_to_io_error(Status::from_usize(code)).kind() - } else { - ErrorKind::Uncategorized + match r_efi::efi::Status::from_usize(code) { + Status::ALREADY_STARTED + | Status::COMPROMISED_DATA + | Status::CONNECTION_FIN + | Status::CRC_ERROR + | Status::DEVICE_ERROR + | Status::END_OF_MEDIA + | Status::HTTP_ERROR + | Status::ICMP_ERROR + | Status::INCOMPATIBLE_VERSION + | Status::LOAD_ERROR + | Status::MEDIA_CHANGED + | Status::NO_MAPPING + | Status::NO_MEDIA + | Status::NOT_STARTED + | Status::PROTOCOL_ERROR + | Status::PROTOCOL_UNREACHABLE + | Status::TFTP_ERROR + | Status::VOLUME_CORRUPTED => ErrorKind::Other, + Status::BAD_BUFFER_SIZE | Status::INVALID_LANGUAGE => ErrorKind::InvalidData, + Status::ABORTED => ErrorKind::ConnectionAborted, + Status::ACCESS_DENIED => ErrorKind::PermissionDenied, + Status::BUFFER_TOO_SMALL => ErrorKind::FileTooLarge, + Status::CONNECTION_REFUSED => ErrorKind::ConnectionRefused, + Status::CONNECTION_RESET => ErrorKind::ConnectionReset, + Status::END_OF_FILE => ErrorKind::UnexpectedEof, + Status::HOST_UNREACHABLE => ErrorKind::HostUnreachable, + Status::INVALID_PARAMETER => ErrorKind::InvalidInput, + Status::IP_ADDRESS_CONFLICT => ErrorKind::AddrInUse, + Status::NETWORK_UNREACHABLE => ErrorKind::NetworkUnreachable, + Status::NO_RESPONSE => ErrorKind::HostUnreachable, + Status::NOT_FOUND => ErrorKind::NotFound, + Status::NOT_READY => ErrorKind::ResourceBusy, + Status::OUT_OF_RESOURCES => ErrorKind::OutOfMemory, + Status::SECURITY_VIOLATION => ErrorKind::PermissionDenied, + Status::TIMEOUT => ErrorKind::TimedOut, + Status::UNSUPPORTED => ErrorKind::Unsupported, + Status::VOLUME_FULL => ErrorKind::StorageFull, + Status::WRITE_PROTECTED => ErrorKind::ReadOnlyFilesystem, + _ => ErrorKind::Uncategorized, } } diff --git a/library/std/src/sys/uefi/os.rs b/library/std/src/sys/uefi/os.rs new file mode 100644 index 0000000000000..cd489d8f7f9c6 --- /dev/null +++ b/library/std/src/sys/uefi/os.rs @@ -0,0 +1,213 @@ +use super::{unsupported, RawOsError}; +use crate::error::Error as StdError; +use crate::ffi::{OsStr, OsString}; +use crate::fmt; +use crate::io; +use crate::marker::PhantomData; +use crate::path::{self, PathBuf}; + +pub fn errno() -> RawOsError { + 0 +} + +pub fn error_string(errno: RawOsError) -> String { + use r_efi::efi::Status; + + // Keep the List in Alphabetical Order + // The Messages are taken from UEFI Specification Appendix D - Status Codes + match r_efi::efi::Status::from_usize(errno) { + Status::ABORTED => "The operation was aborted.".to_owned(), + Status::ACCESS_DENIED => "Access was denied.".to_owned(), + Status::ALREADY_STARTED => "The protocol has already been started.".to_owned(), + Status::BAD_BUFFER_SIZE => "The buffer was not the proper size for the request.".to_owned(), + Status::BUFFER_TOO_SMALL => { + "The buffer is not large enough to hold the requested data. The required buffer size is returned in the appropriate parameter when this error occurs.".to_owned() + } + Status::COMPROMISED_DATA => { + "The security status of the data is unknown or compromised and the data must be updated or replaced to restore a valid security status.".to_owned() + } + Status::CONNECTION_FIN => { + "The receiving operation fails because the communication peer has closed the connection and there is no more data in the receive buffer of the instance.".to_owned() + } + Status::CONNECTION_REFUSED => { + "The receiving or transmission operation fails because this connection is refused.".to_owned() + } + Status::CONNECTION_RESET => { + "The connect fails because the connection is reset either by instance itself or the communication peer.".to_owned() + } + Status::CRC_ERROR => "A CRC error was detected.".to_owned(), + Status::DEVICE_ERROR => "The physical device reported an error while attempting the operation.".to_owned() + , + Status::END_OF_FILE => { + "The end of the file was reached.".to_owned() + } + Status::END_OF_MEDIA => { + "Beginning or end of media was reached".to_owned() + } + Status::HOST_UNREACHABLE => { + "The remote host is not reachable.".to_owned() + } + Status::HTTP_ERROR => { + "A HTTP error occurred during the network operation.".to_owned() + } + Status::ICMP_ERROR => { + "An ICMP error occurred during the network operation.".to_owned() + } + Status::INCOMPATIBLE_VERSION => { + "The function encountered an internal version that was incompatible with a version requested by the caller.".to_owned() + } + Status::INVALID_LANGUAGE => { + "The language specified was invalid.".to_owned() + } + Status::INVALID_PARAMETER => { + "A parameter was incorrect.".to_owned() + } + Status::IP_ADDRESS_CONFLICT => { + "There is an address conflict address allocation".to_owned() + } + Status::LOAD_ERROR => { + "The image failed to load.".to_owned() + } + Status::MEDIA_CHANGED => { + "The medium in the device has changed since the last access.".to_owned() + } + Status::NETWORK_UNREACHABLE => { + "The network containing the remote host is not reachable.".to_owned() + } + Status::NO_MAPPING => { + "A mapping to a device does not exist.".to_owned() + } + Status::NO_MEDIA => { + "The device does not contain any medium to perform the operation.".to_owned() + } + Status::NO_RESPONSE => { + "The server was not found or did not respond to the request.".to_owned() + } + Status::NOT_FOUND => "The item was not found.".to_owned(), + Status::NOT_READY => { + "There is no data pending upon return.".to_owned() + } + Status::NOT_STARTED => { + "The protocol has not been started.".to_owned() + } + Status::OUT_OF_RESOURCES => { + "A resource has run out.".to_owned() + } + Status::PROTOCOL_ERROR => { + "A protocol error occurred during the network operation.".to_owned() + } + Status::PROTOCOL_UNREACHABLE => { + "An ICMP protocol unreachable error is received.".to_owned() + } + Status::SECURITY_VIOLATION => { + "The function was not performed due to a security violation.".to_owned() + } + Status::TFTP_ERROR => { + "A TFTP error occurred during the network operation.".to_owned() + } + Status::TIMEOUT => "The timeout time expired.".to_owned(), + Status::UNSUPPORTED => { + "The operation is not supported.".to_owned() + } + Status::VOLUME_FULL => { + "There is no more space on the file system.".to_owned() + } + Status::VOLUME_CORRUPTED => { + "An inconstancy was detected on the file system causing the operating to fail.".to_owned() + } + Status::WRITE_PROTECTED => { + "The device cannot be written to.".to_owned() + } + _ => format!("Status: {}", errno), + } +} + +pub fn getcwd() -> io::Result { + unsupported() +} + +pub fn chdir(_: &path::Path) -> io::Result<()> { + unsupported() +} + +pub struct SplitPaths<'a>(!, PhantomData<&'a ()>); + +pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> { + panic!("unsupported") +} + +impl<'a> Iterator for SplitPaths<'a> { + type Item = PathBuf; + fn next(&mut self) -> Option { + self.0 + } +} + +#[derive(Debug)] +pub struct JoinPathsError; + +pub fn join_paths(_paths: I) -> Result +where + I: Iterator, + T: AsRef, +{ + Err(JoinPathsError) +} + +impl fmt::Display for JoinPathsError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + "not supported on this platform yet".fmt(f) + } +} + +impl StdError for JoinPathsError { + #[allow(deprecated)] + fn description(&self) -> &str { + "not supported on this platform yet" + } +} + +pub fn current_exe() -> io::Result { + unsupported() +} + +pub struct Env(!); + +impl Iterator for Env { + type Item = (OsString, OsString); + fn next(&mut self) -> Option<(OsString, OsString)> { + self.0 + } +} + +pub fn env() -> Env { + panic!("not supported on this platform") +} + +pub fn getenv(_: &OsStr) -> Option { + None +} + +pub fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { + Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) +} + +pub fn unsetenv(_: &OsStr) -> io::Result<()> { + Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform")) +} + +pub fn temp_dir() -> PathBuf { + panic!("no filesystem on this platform") +} + +pub fn home_dir() -> Option { + None +} + +pub fn exit(_code: i32) -> ! { + crate::intrinsics::abort() +} + +pub fn getpid() -> u32 { + panic!("no pids on this platform") +} From c7e5f3ca085c3adfd285a6d41080ff65a6299bc9 Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Sun, 21 May 2023 14:26:59 +0530 Subject: [PATCH 7/8] Rebase to master - Update Example - Add thread_parking to sys::uefi - Fix unsafe in unsafe errors - Improve docs - Improve os/exit - Some asserts - Switch back to atomics Signed-off-by: Ayush Singh --- compiler/rustc_codegen_ssa/src/base.rs | 13 ++--- library/panic_abort/src/lib.rs | 2 +- library/std/build.rs | 1 - library/std/src/lib.rs | 1 + library/std/src/os/mod.rs | 1 - library/std/src/os/uefi/env.rs | 54 +++++++++++-------- library/std/src/sys/uefi/helpers.rs | 14 ++--- library/std/src/sys/uefi/mod.rs | 36 +++++++++---- library/std/src/sys/uefi/os.rs | 42 +++++++++++---- .../src/platform-support/unknown-uefi.md | 10 ++-- 10 files changed, 111 insertions(+), 63 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 4753f12554065..6c51dffedbfad 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -421,7 +421,7 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( entry_type: EntryFnType, ) -> Bx::Function { // 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. + // `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 { @@ -508,10 +508,9 @@ fn get_argc_argv<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( 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); + 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_to(cx.type_i8()), arg_argv, &[bx.const_int(cx.type_int(), 1)]); + 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 { @@ -566,11 +565,7 @@ pub fn allocator_kind_for_codegen(tcx: TyCtxt<'_>) -> Option { 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( diff --git a/library/panic_abort/src/lib.rs b/library/panic_abort/src/lib.rs index bc0d7287be505..6e097e2caf24b 100644 --- a/library/panic_abort/src/lib.rs +++ b/library/panic_abort/src/lib.rs @@ -44,7 +44,7 @@ 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() -> ! { diff --git a/library/std/build.rs b/library/std/build.rs index 1c32b42565011..36516978b7a09 100644 --- a/library/std/build.rs +++ b/library/std/build.rs @@ -52,7 +52,6 @@ fn main() { // - mipsel-sony-psp // - nvptx64-nvidia-cuda // - arch=avr - // - 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\""); diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index b572347019caf..5e3249655b838 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -263,6 +263,7 @@ #![cfg_attr(target_os = "xous", feature(slice_ptr_len))] // // Language features: +// tidy-alphabetical-start #![feature(alloc_error_handler)] #![feature(allocator_internals)] #![feature(allow_internal_unsafe)] diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs index d1a735038992c..41aa0472f526a 100644 --- a/library/std/src/os/mod.rs +++ b/library/std/src/os/mod.rs @@ -142,7 +142,6 @@ pub mod solid; #[cfg(target_os = "tvos")] #[path = "ios/mod.rs"] pub(crate) mod tvos; -#[cfg(target_os = "uefi")] #[cfg(any(target_os = "uefi", doc))] pub mod uefi; #[cfg(target_os = "vita")] diff --git a/library/std/src/os/uefi/env.rs b/library/std/src/os/uefi/env.rs index 4dd090e22c620..5d082e7c11388 100644 --- a/library/std/src/os/uefi/env.rs +++ b/library/std/src/os/uefi/env.rs @@ -2,23 +2,22 @@ #![unstable(feature = "uefi_std", issue = "100499")] -use crate::{cell::Cell, ffi::c_void, ptr::NonNull}; +use crate::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; +use crate::{ffi::c_void, ptr::NonNull}; -// Since UEFI is single-threaded, making the global variables thread local should be safe. -thread_local! { - // Flag to check if BootServices are still valid. - // Start with assuming that they are not available - static BOOT_SERVICES_FLAG: Cell = Cell::new(false); - // Position 0 = SystemTable - // Position 1 = ImageHandle - static GLOBALS: Cell, NonNull)>> = Cell::new(None); -} +static SYSTEM_TABLE: AtomicPtr = AtomicPtr::new(crate::ptr::null_mut()); +static IMAGE_HANDLE: AtomicPtr = 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. /// @@ -26,9 +25,26 @@ thread_local! { /// 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. -pub unsafe fn init_globals(handle: NonNull, system_table: NonNull) { - GLOBALS.set(Some((system_table, handle))); +/// # SAFETY +/// Calling this function more than once will panic +pub(crate) unsafe fn init_globals(handle: NonNull, system_table: NonNull) { + 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. @@ -50,7 +66,7 @@ pub fn image_handle() -> NonNull { /// Get the BootServices Pointer. /// This function also checks if `ExitBootServices` has already been called. pub fn boot_services() -> Option> { - if BOOT_SERVICES_FLAG.get() { + if BOOT_SERVICES_FLAG.load(Ordering::Acquire) { let system_table: NonNull = try_system_table()?.cast(); let boot_services = unsafe { (*system_table.as_ptr()).boot_services }; NonNull::new(boot_services).map(|x| x.cast()) @@ -62,19 +78,15 @@ pub fn boot_services() -> Option> { /// Get the SystemTable Pointer. /// This function is mostly intended for places where panic is not an option pub(crate) fn try_system_table() -> Option> { - GLOBALS.get().map(|x| x.0) + 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> { - GLOBALS.get().map(|x| x.1) -} - -pub(crate) fn enable_boot_services() { - BOOT_SERVICES_FLAG.set(true); + NonNull::new(IMAGE_HANDLE.load(Ordering::Acquire)) } pub(crate) fn disable_boot_services() { - BOOT_SERVICES_FLAG.set(false); + BOOT_SERVICES_FLAG.store(false, Ordering::Release) } diff --git a/library/std/src/sys/uefi/helpers.rs b/library/std/src/sys/uefi/helpers.rs index 2e1bcb369447e..126661bfc961c 100644 --- a/library/std/src/sys/uefi/helpers.rs +++ b/library/std/src/sys/uefi/helpers.rs @@ -60,13 +60,14 @@ pub(crate) fn locate_handles(mut guid: Guid) -> io::Result = - Vec::with_capacity(buf_len / size_of::()); + assert_eq!(buf_len % size_of::(), 0); + let num_of_handles = buf_len / size_of::(); + let mut buf: Vec = Vec::with_capacity(num_of_handles); match inner(&mut guid, boot_services, &mut buf_len, buf.as_mut_ptr()) { Ok(()) => { // This is safe because the call will succeed only if buf_len >= required length. // Also, on success, the `buf_len` is updated with the size of bufferv (in bytes) written - unsafe { buf.set_len(buf_len / size_of::()) }; + unsafe { buf.set_len(num_of_handles) }; Ok(buf.into_iter().filter_map(|x| NonNull::new(x)).collect()) } Err(e) => Err(e), @@ -114,16 +115,15 @@ pub(crate) fn create_event( ) -> io::Result> { let boot_services: NonNull = boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?.cast(); - let mut exit_boot_service_event: r_efi::efi::Event = crate::ptr::null_mut(); + let mut event: r_efi::efi::Event = crate::ptr::null_mut(); let r = unsafe { let create_event = (*boot_services.as_ptr()).create_event; - (create_event)(signal, tpl, handler, context, &mut exit_boot_service_event) + (create_event)(signal, tpl, handler, context, &mut event) }; if r.is_error() { Err(crate::io::Error::from_raw_os_error(r.as_usize())) } else { - NonNull::new(exit_boot_service_event) - .ok_or(const_io_error!(io::ErrorKind::Other, "null protocol")) + NonNull::new(event).ok_or(const_io_error!(io::ErrorKind::Other, "null protocol")) } } diff --git a/library/std/src/sys/uefi/mod.rs b/library/std/src/sys/uefi/mod.rs index 85d00caf14911..9a10395af8e31 100644 --- a/library/std/src/sys/uefi/mod.rs +++ b/library/std/src/sys/uefi/mod.rs @@ -12,7 +12,6 @@ //! [`OsStr`]: crate::ffi::OsStr //! [`OsString`]: crate::ffi::OsString -#![deny(unsafe_op_in_unsafe_fn)] pub mod alloc; #[path = "../unsupported/args.rs"] pub mod args; @@ -43,6 +42,8 @@ pub mod stdio; pub mod thread; #[path = "../unsupported/thread_local_key.rs"] pub mod thread_local_key; +#[path = "../unsupported/thread_parking.rs"] +pub mod thread_parking; #[path = "../unsupported/time.rs"] pub mod time; @@ -53,18 +54,17 @@ mod tests; pub type RawOsError = usize; -use crate::cell::Cell; use crate::io as std_io; use crate::os::uefi; use crate::ptr::NonNull; +use crate::sync::atomic::{AtomicPtr, Ordering}; pub mod memchr { pub use core::slice::memchr::{memchr, memrchr}; } -thread_local! { - static EXIT_BOOT_SERVICE_EVENT: Cell>> = Cell::new(None); -} +static EXIT_BOOT_SERVICE_EVENT: AtomicPtr = + AtomicPtr::new(crate::ptr::null_mut()); /// # SAFETY /// - must be called only once during runtime initialization. @@ -75,8 +75,6 @@ pub(crate) unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) { let image_handle = unsafe { NonNull::new(*argv as *mut crate::ffi::c_void).unwrap() }; let system_table = unsafe { NonNull::new(*argv.add(1) as *mut crate::ffi::c_void).unwrap() }; unsafe { uefi::env::init_globals(image_handle, system_table) }; - // Enable boot services once GLOBALS are initialized - uefi::env::enable_boot_services(); // Register exit boot services handler match helpers::create_event( @@ -86,7 +84,17 @@ pub(crate) unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) { crate::ptr::null_mut(), ) { Ok(x) => { - EXIT_BOOT_SERVICE_EVENT.set(Some(x)); + if EXIT_BOOT_SERVICE_EVENT + .compare_exchange( + crate::ptr::null_mut(), + x.as_ptr(), + Ordering::Release, + Ordering::Acquire, + ) + .is_err() + { + abort_internal(); + }; } Err(_) => abort_internal(), } @@ -96,7 +104,9 @@ pub(crate) unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) { /// this is not guaranteed to run, for example when the program aborts. /// - must be called only once during runtime cleanup. pub unsafe fn cleanup() { - if let Some(exit_boot_service_event) = EXIT_BOOT_SERVICE_EVENT.take() { + if let Some(exit_boot_service_event) = + NonNull::new(EXIT_BOOT_SERVICE_EVENT.swap(crate::ptr::null_mut(), Ordering::Acquire)) + { let _ = unsafe { helpers::close_event(exit_boot_service_event) }; } } @@ -159,7 +169,9 @@ pub fn decode_error_kind(code: RawOsError) -> crate::io::ErrorKind { } pub fn abort_internal() -> ! { - if let Some(exit_boot_service_event) = EXIT_BOOT_SERVICE_EVENT.take() { + if let Some(exit_boot_service_event) = + NonNull::new(EXIT_BOOT_SERVICE_EVENT.load(Ordering::Acquire)) + { let _ = unsafe { helpers::close_event(exit_boot_service_event) }; } @@ -226,3 +238,7 @@ fn get_random() -> Option<(u64, u64)> { extern "efiapi" fn exit_boot_service_handler(_e: r_efi::efi::Event, _ctx: *mut crate::ffi::c_void) { uefi::env::disable_boot_services(); } + +pub fn is_interrupted(_code: RawOsError) -> bool { + false +} diff --git a/library/std/src/sys/uefi/os.rs b/library/std/src/sys/uefi/os.rs index cd489d8f7f9c6..e6693db68e641 100644 --- a/library/std/src/sys/uefi/os.rs +++ b/library/std/src/sys/uefi/os.rs @@ -4,15 +4,16 @@ use crate::ffi::{OsStr, OsString}; use crate::fmt; use crate::io; use crate::marker::PhantomData; +use crate::os::uefi; use crate::path::{self, PathBuf}; +use crate::ptr::NonNull; +use r_efi::efi::Status; pub fn errno() -> RawOsError { 0 } pub fn error_string(errno: RawOsError) -> String { - use r_efi::efi::Status; - // Keep the List in Alphabetical Order // The Messages are taken from UEFI Specification Appendix D - Status Codes match r_efi::efi::Status::from_usize(errno) { @@ -160,12 +161,7 @@ impl fmt::Display for JoinPathsError { } } -impl StdError for JoinPathsError { - #[allow(deprecated)] - fn description(&self) -> &str { - "not supported on this platform yet" - } -} +impl StdError for JoinPathsError {} pub fn current_exe() -> io::Result { unsupported() @@ -173,6 +169,14 @@ pub fn current_exe() -> io::Result { pub struct Env(!); +impl Env { + // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. + pub fn str_debug(&self) -> impl fmt::Debug + '_ { + let Self(inner) = self; + match *inner {} + } +} + impl Iterator for Env { type Item = (OsString, OsString); fn next(&mut self) -> Option<(OsString, OsString)> { @@ -180,6 +184,13 @@ impl Iterator for Env { } } +impl fmt::Debug for Env { + fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self(inner) = self; + match *inner {} + } +} + pub fn env() -> Env { panic!("not supported on this platform") } @@ -204,7 +215,20 @@ pub fn home_dir() -> Option { None } -pub fn exit(_code: i32) -> ! { +pub fn exit(code: i32) -> ! { + if let (Some(boot_services), Some(handle)) = + (uefi::env::boot_services(), uefi::env::try_image_handle()) + { + let boot_services: NonNull = boot_services.cast(); + let _ = unsafe { + ((*boot_services.as_ptr()).exit)( + handle.as_ptr(), + Status::from_usize(code as usize), + 0, + crate::ptr::null_mut(), + ) + }; + } crate::intrinsics::abort() } diff --git a/src/doc/rustc/src/platform-support/unknown-uefi.md b/src/doc/rustc/src/platform-support/unknown-uefi.md index 019f030eb1c10..68cd7fae31971 100644 --- a/src/doc/rustc/src/platform-support/unknown-uefi.md +++ b/src/doc/rustc/src/platform-support/unknown-uefi.md @@ -277,7 +277,9 @@ cargo +custom build --target x86_64-unknown-uefi ``` ```rust,ignore (platform-specific) -use r_efi::efi; +#![feature(uefi_std)] + +use r_efi::{efi, protocols::simple_text_output}; use std::{ ffi::OsString, os::uefi::{env, ffi::OsStrExt} @@ -290,7 +292,7 @@ pub fn main() { let r = unsafe { let con_out: *mut simple_text_output::Protocol = (*st).con_out; - let output_string: extern "efiapi" fn(_: *mut simple_text_output::Protocol, *mut u16) = (*con_out).output_string; + let output_string: extern "efiapi" fn(_: *mut simple_text_output::Protocol, *mut u16) -> efi::Status = (*con_out).output_string; output_string(con_out, s.as_ptr() as *mut efi::Char16) }; assert!(!r.is_error()) @@ -298,6 +300,6 @@ pub fn main() { ``` ### BootServices -The current implementation of std make `BootServices` unavailable once `ExitBootServices` is called. Refer to [Runtime Drivers](https://edk2-docs.gitbook.io/edk-ii-uefi-driver-writer-s-guide/7_driver_entry_point/711_runtime_drivers) for more information regarding how to handle switching from using physical addresses to using virtual addresses. +The current implementation of std makes `BootServices` unavailable once `ExitBootServices` is called. Refer to [Runtime Drivers](https://edk2-docs.gitbook.io/edk-ii-uefi-driver-writer-s-guide/7_driver_entry_point/711_runtime_drivers) for more information regarding how to handle switching from using physical addresses to using virtual addresses. -Note: It should be noted that it is upto the user to drop all allocated memory before `ExitBootServices` is called. +Note: It should be noted that it is up to the user to drop all allocated memory before `ExitBootServices` is called. From 984ecefed8a12fc31200f5307f07acd7ce6b18c9 Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Thu, 21 Sep 2023 18:59:43 +0530 Subject: [PATCH 8/8] Fixes from PR - Hide Docs - Use repr_unpacked error Signed-off-by: Ayush Singh --- library/std/src/io/error.rs | 8 ++++---- library/std/src/os/mod.rs | 2 +- src/tools/tidy/src/pal.rs | 1 + 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index f63142ff01fc0..5966416e32a19 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -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; diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs index 41aa0472f526a..11ad21515fdec 100644 --- a/library/std/src/os/mod.rs +++ b/library/std/src/os/mod.rs @@ -142,7 +142,7 @@ pub mod solid; #[cfg(target_os = "tvos")] #[path = "ios/mod.rs"] pub(crate) mod tvos; -#[cfg(any(target_os = "uefi", doc))] +#[cfg(target_os = "uefi")] pub mod uefi; #[cfg(target_os = "vita")] pub mod vita; diff --git a/src/tools/tidy/src/pal.rs b/src/tools/tidy/src/pal.rs index 3a4d9c53d7bc4..5f6b63a67fda7 100644 --- a/src/tools/tidy/src/pal.rs +++ b/src/tools/tidy/src/pal.rs @@ -56,6 +56,7 @@ const EXCEPTION_PATHS: &[&str] = &[ "library/std/src/path.rs", "library/std/src/sys_common", // Should only contain abstractions over platforms "library/std/src/net/test.rs", // Utility helpers for tests + "library/std/src/io/error.rs", // Repr unpacked needed for UEFI ]; pub fn check(path: &Path, bad: &mut bool) {