Fixes from PR

Signed-off-by: Ayush Singh <ayushsingh1325@gmail.com>
This commit is contained in:
Ayush Singh
2023-01-28 23:37:54 +05:30
committed by Ayush Singh
parent 48c6ae0611
commit 8e56b33d59
6 changed files with 70 additions and 59 deletions

View File

@@ -1,5 +1,7 @@
//! UEFI-specific extensions to the primitives in `std::env` module //! UEFI-specific extensions to the primitives in `std::env` module
#![unstable(feature = "uefi_std", issue = "100499")]
use crate::ffi::c_void; use crate::ffi::c_void;
use crate::ptr::NonNull; use crate::ptr::NonNull;
use crate::sync::atomic::{AtomicPtr, Ordering}; use crate::sync::atomic::{AtomicPtr, Ordering};
@@ -22,21 +24,18 @@ static GLOBALS: OnceLock<(AtomicPtr<c_void>, AtomicPtr<c_void>)> = OnceLock::new
/// standard library is loaded. /// standard library is loaded.
/// ///
/// This function must not be called more than once. /// This function must not be called more than once.
#[unstable(feature = "uefi_std", issue = "100499")]
pub unsafe fn init_globals(handle: NonNull<c_void>, system_table: NonNull<c_void>) { pub unsafe fn init_globals(handle: NonNull<c_void>, system_table: NonNull<c_void>) {
GLOBALS.set((AtomicPtr::new(system_table.as_ptr()), AtomicPtr::new(handle.as_ptr()))).unwrap() GLOBALS.set((AtomicPtr::new(system_table.as_ptr()), AtomicPtr::new(handle.as_ptr()))).unwrap()
} }
/// Get the SystemTable Pointer. /// Get the SystemTable Pointer.
/// Note: This function panics if the System Table and Image Handle is Not initialized /// Note: This function panics if the System Table or Image Handle is not initialized
#[unstable(feature = "uefi_std", issue = "100499")]
pub fn system_table() -> NonNull<c_void> { pub fn system_table() -> NonNull<c_void> {
try_system_table().unwrap() try_system_table().unwrap()
} }
/// Get the SystemHandle Pointer. /// Get the ImageHandle Pointer.
/// Note: This function panics if the System Table and Image Handle is Not initialized /// Note: This function panics if the System Table or Image Handle is not initialized
#[unstable(feature = "uefi_std", issue = "100499")]
pub fn image_handle() -> NonNull<c_void> { pub fn image_handle() -> NonNull<c_void> {
try_image_handle().unwrap() try_image_handle().unwrap()
} }

View File

@@ -13,11 +13,8 @@ unsafe impl GlobalAlloc for System {
Some(x) => x.as_ptr() as *mut _, Some(x) => x.as_ptr() as *mut _,
}; };
if layout.size() > 0 { // The caller must ensure non-0 layout
unsafe { r_efi_alloc::raw::alloc(system_table, layout, MEMORY_TYPE) } 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) { unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
@@ -25,8 +22,7 @@ unsafe impl GlobalAlloc for System {
None => handle_alloc_error(layout), None => handle_alloc_error(layout),
Some(x) => x.as_ptr() as *mut _, Some(x) => x.as_ptr() as *mut _,
}; };
if layout.size() > 0 { // The caller must ensure non-0 layout
unsafe { r_efi_alloc::raw::dealloc(system_table, ptr, layout) } unsafe { r_efi_alloc::raw::dealloc(system_table, ptr, layout) }
} }
}
} }

View File

@@ -1,16 +1,25 @@
//! Contains most of the shared UEFI specific stuff. Some of this might be moved to `std::os::uefi` //! 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 //! if needed but no point in adding extra public API when there is not Std support for UEFI in the
//! first place //! 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 r_efi::efi::Guid;
use crate::io::{self, const_io_error}; use crate::io::{self, const_io_error};
use crate::mem::MaybeUninit; use crate::mem::{size_of, MaybeUninit};
use crate::os::uefi; use crate::os::uefi;
use crate::ptr::NonNull; use crate::ptr::NonNull;
// Locate handles with a particular protocol GUID /// Locate Handles with a particular Protocol GUID
/// Implemented using `EFI_BOOT_SERVICES.LocateHandles()` /// 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<Vec<NonNull<crate::ffi::c_void>>> { pub(crate) fn locate_handles(mut guid: Guid) -> io::Result<Vec<NonNull<crate::ffi::c_void>>> {
fn inner( fn inner(
guid: &mut Guid, guid: &mut Guid,
@@ -34,6 +43,8 @@ pub(crate) fn locate_handles(mut guid: Guid) -> io::Result<Vec<NonNull<crate::ff
let boot_services = boot_services(); let boot_services = boot_services();
let mut buf_len = 0usize; let mut buf_len = 0usize;
// This should always fail since the size of buffer is 0. This call should update the buf_len
// variable with the required buffer length
match inner(&mut guid, boot_services, &mut buf_len, crate::ptr::null_mut()) { match inner(&mut guid, boot_services, &mut buf_len, crate::ptr::null_mut()) {
Ok(()) => unreachable!(), Ok(()) => unreachable!(),
Err(e) => match e.kind() { Err(e) => match e.kind() {
@@ -44,21 +55,23 @@ pub(crate) fn locate_handles(mut guid: Guid) -> io::Result<Vec<NonNull<crate::ff
// The returned buf_len is in bytes // The returned buf_len is in bytes
let mut buf: Vec<r_efi::efi::Handle> = let mut buf: Vec<r_efi::efi::Handle> =
Vec::with_capacity(buf_len / crate::mem::size_of::<r_efi::efi::Handle>()); Vec::with_capacity(buf_len / size_of::<r_efi::efi::Handle>());
match inner(&mut guid, boot_services, &mut buf_len, buf.as_mut_ptr()) { match inner(&mut guid, boot_services, &mut buf_len, buf.as_mut_ptr()) {
Ok(()) => { Ok(()) => {
// SAFETY: This is safe because the call will succeed only if buf_len >= required // This is safe because the call will succeed only if buf_len >= required length.
// length. Also, on success, the `buf_len` is updated with the size of bufferv (in // Also, on success, the `buf_len` is updated with the size of bufferv (in bytes) written
// bytes) written unsafe { buf.set_len(buf_len / size_of::<r_efi::efi::Handle>()) };
unsafe { buf.set_len(buf_len / crate::mem::size_of::<r_efi::efi::Handle>()) }; Ok(buf.into_iter().filter_map(|x| NonNull::new(x)).collect())
Ok(buf.iter().filter_map(|x| NonNull::new(*x)).collect())
} }
Err(e) => Err(e), Err(e) => Err(e),
} }
} }
/// Open Protocol on a handle /// Open Protocol on a handle.
/// Implemented using `EFI_BOOT_SERVICES.OpenProtocol()` /// 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<T>( pub(crate) fn open_protocol<T>(
handle: NonNull<crate::ffi::c_void>, handle: NonNull<crate::ffi::c_void>,
mut protocol_guid: Guid, 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. /// Get the BootServices Pointer.
pub(crate) fn boot_services() -> NonNull<r_efi::efi::BootServices> { pub(crate) fn boot_services() -> NonNull<r_efi::efi::BootServices> {
let system_table: NonNull<r_efi::efi::SystemTable> = uefi::env::system_table().cast(); try_boot_services().unwrap()
let boot_services = unsafe { (*system_table.as_ptr()).boot_services };
NonNull::new(boot_services).unwrap()
} }
/// Get the BootServices Pointer. /// Get the BootServices Pointer.
/// This function is mostly intended for places where panic is not an option /// This function is mostly intended for places where panic is not an option

View File

@@ -47,7 +47,7 @@ pub mod thread_local_key;
#[path = "../unsupported/time.rs"] #[path = "../unsupported/time.rs"]
pub mod time; pub mod time;
pub(crate) mod common; pub(crate) mod helpers;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
@@ -60,18 +60,20 @@ pub mod memchr {
pub use core::slice::memchr::{memchr, memrchr}; pub use core::slice::memchr::{memchr, memrchr};
} }
// SAFETY: must be called only once during runtime initialization. /// # SAFETY
// SAFETY: argc must be 2. /// - must be called only once during runtime initialization.
// SAFETY: argv must be &[Handle, *mut SystemTable]. /// - argc must be 2.
pub unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) { /// - argv must be &[Handle, *mut SystemTable].
pub(crate) unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) {
assert_eq!(argc, 2); assert_eq!(argc, 2);
let image_handle = unsafe { NonNull::new(*argv as *mut crate::ffi::c_void).unwrap() }; 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() }; 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 { crate::os::uefi::env::init_globals(image_handle, system_table) };
} }
// SAFETY: must be called only once during runtime cleanup. /// # SAFETY
// NOTE: this is not guaranteed to run, for example when the program aborts. /// 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() {}
#[inline] #[inline]
@@ -89,7 +91,7 @@ pub fn decode_error_kind(code: i32) -> crate::io::ErrorKind {
use r_efi::efi::Status; use r_efi::efi::Status;
if let Ok(code) = usize::try_from(code) { 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 { } else {
ErrorKind::Uncategorized ErrorKind::Uncategorized
} }
@@ -97,7 +99,7 @@ pub fn decode_error_kind(code: i32) -> crate::io::ErrorKind {
pub fn abort_internal() -> ! { pub fn abort_internal() -> ! {
if let (Some(boot_services), Some(handle)) = 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 { let _ = unsafe {
((*boot_services.as_ptr()).exit)( ((*boot_services.as_ptr()).exit)(
@@ -130,9 +132,9 @@ fn get_random() -> Option<(u64, u64)> {
use r_efi::protocols::rng; use r_efi::protocols::rng;
let mut buf = [0u8; 16]; 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 { for handle in handles {
if let Ok(protocol) = common::open_protocol::<rng::Protocol>(handle, rng::PROTOCOL_GUID) { if let Ok(protocol) = helpers::open_protocol::<rng::Protocol>(handle, rng::PROTOCOL_GUID) {
let r = unsafe { let r = unsafe {
((*protocol.as_ptr()).get_rng)( ((*protocol.as_ptr()).get_rng)(
protocol.as_ptr(), protocol.as_ptr(),

View File

@@ -44,7 +44,6 @@ cfg_if::cfg_if! {
cfg_if::cfg_if! { cfg_if::cfg_if! {
if #[cfg(any(target_os = "l4re", if #[cfg(any(target_os = "l4re",
target_os = "hermit",
target_os = "uefi", target_os = "uefi",
feature = "restricted-std", feature = "restricted-std",
all(target_family = "wasm", not(target_os = "emscripten")), all(target_family = "wasm", not(target_os = "emscripten")),

View File

@@ -19,7 +19,7 @@ Available targets:
## Requirements ## Requirements
All UEFI targets can be used as `no-std` environments via cross-compilation. 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. 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 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 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`: The linker that should be used is `rust-lld`. Here is a sample `config.toml`:
```toml ```toml
[llvm]
download-ci-llvm = false
[rust] [rust]
lld = true lld = true
[target.x86_64-unknown-uefi]
linker = "rust-lld"
``` ```
Then just build using `x.py`: Then just build using `x.py`:
```sh ```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 ### Std Requirements
The current std has a few basic requirements to function: The current std has a few basic requirements to function:
1. Memory Allocation Services (`EFI_BOOT_SERVICES.AllocatePool()` and 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.
`EFI_BOOT_SERVICES.FreePool()`) are available.
If the above requirement is satisfied, the Rust code will reach `main`. 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. 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 #### alloc
- Implemented using `EFI_BOOT_SERVICES.AllocatePool()` and `EFI_BOOT_SERVICES.FreePool()`. - Implemented using `EFI_BOOT_SERVICES.AllocatePool()` and `EFI_BOOT_SERVICES.FreePool()`.
- Passes all the tests. - 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 #### cmath
- Provided by compiler-builtins. - Provided by compiler-builtins.
#### env #### env
- Just some global consants. - Just some global constants.
#### locks #### locks
- Uses `unsupported/locks`. - The provided locks should work on all standard single-threaded UEFI implementations.
- They should work for a platform without threads according to docs.
#### os_str #### 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 ## Example: Hello World With std
The following code is a valid UEFI application showing stdio in UEFI. It also The following code features a valid UEFI application, including stdio and `alloc` (`OsString` and `Vec`):
uses `alloc` type `OsString` and `Vec`.
This example can be compiled as binary crate via `cargo` using the toolchain This example can be compiled as binary crate via `cargo` using the toolchain
compiled from the above source (named custom): compiled from the above source (named custom):
@@ -286,15 +284,21 @@ cargo +custom build --target x86_64-unknown-uefi
```rust,ignore (platform-specific) ```rust,ignore (platform-specific)
use r_efi::efi; use r_efi::efi;
use std::os::uefi::ffi::OsStrExt; use std::{
use std::{ffi::OsString, panic}; ffi::OsString,
os::uefi::{env, ffi::OsStrExt}
};
pub fn main() { 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<u16> = OsString::from("Hello World!\n").encode_wide().collect(); let mut s: Vec<u16> = OsString::from("Hello World!\n").encode_wide().collect();
s.push(0); s.push(0);
let r = 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()) assert!(!r.is_error())
} }
``` ```