From 8e56b33d59593a99fdef6f73f0c1a09a012ca907 Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Sat, 28 Jan 2023 23:37:54 +0530 Subject: [PATCH] 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 479ff5f6975..71bf0ff8ff3 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 6a14ee76e92..54d86d0c8a7 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 6316a74d367..e83adabaf16 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 193d4ecc16f..fed44b4f49d 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 d65d68337d8..e18638f2a5f 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 8d4d60e12fb..1a4cbc9f092 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()) } ```