From c7e5f3ca085c3adfd285a6d41080ff65a6299bc9 Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Sun, 21 May 2023 14:26:59 +0530 Subject: [PATCH] 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 4753f125540..6c51dffedbf 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 bc0d7287be5..6e097e2caf2 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 1c32b425650..36516978b7a 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 b572347019c..5e3249655b8 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 d1a73503899..41aa0472f52 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 4dd090e22c6..5d082e7c113 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 2e1bcb36944..126661bfc96 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 85d00caf149..9a10395af8e 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 cd489d8f7f9..e6693db68e6 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 019f030eb1c..68cd7fae319 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.