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 <ayushdevel1325@gmail.com>
This commit is contained in:
Ayush Singh 2023-05-21 14:26:59 +05:30 committed by Ayush Singh
parent 40c3dacc76
commit c7e5f3ca08
No known key found for this signature in database
GPG Key ID: 05CEF5C789E55A74
10 changed files with 111 additions and 63 deletions

View File

@ -421,7 +421,7 @@ fn create_entry_fn<'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<AllocatorKind> {
use rustc_middle::middle::dependency_format::Linkage;
list.iter().any(|&linkage| linkage == Linkage::Dynamic)
});
if any_dynamic_crate {
None
} else {
tcx.allocator_kind(())
}
if any_dynamic_crate { None } else { tcx.allocator_kind(()) }
}
pub fn codegen_crate<B: ExtraBackendMethods>(

View File

@ -44,7 +44,7 @@ unsafe fn abort() -> ! {
}
} 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() -> ! {

View File

@ -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\"");

View File

@ -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)]

View File

@ -142,7 +142,6 @@ pub mod windows {}
#[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")]

View File

@ -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<bool> = Cell::new(false);
// Position 0 = SystemTable
// Position 1 = ImageHandle
static GLOBALS: Cell<Option<(NonNull<c_void>, NonNull<c_void>)>> = Cell::new(None);
}
static SYSTEM_TABLE: AtomicPtr<c_void> = AtomicPtr::new(crate::ptr::null_mut());
static IMAGE_HANDLE: AtomicPtr<c_void> = 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 @@
/// 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<c_void>, system_table: NonNull<c_void>) {
GLOBALS.set(Some((system_table, handle)));
/// # SAFETY
/// Calling this function more than once will panic
pub(crate) unsafe fn init_globals(handle: NonNull<c_void>, system_table: NonNull<c_void>) {
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<c_void> {
/// Get the BootServices Pointer.
/// This function also checks if `ExitBootServices` has already been called.
pub fn boot_services() -> Option<NonNull<c_void>> {
if BOOT_SERVICES_FLAG.get() {
if BOOT_SERVICES_FLAG.load(Ordering::Acquire) {
let system_table: NonNull<r_efi::efi::SystemTable> = 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<NonNull<c_void>> {
/// 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<c_void>> {
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<NonNull<c_void>> {
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)
}

View File

@ -60,13 +60,14 @@ fn inner(
}
// The returned buf_len is in bytes
let mut buf: Vec<r_efi::efi::Handle> =
Vec::with_capacity(buf_len / size_of::<r_efi::efi::Handle>());
assert_eq!(buf_len % size_of::<r_efi::efi::Handle>(), 0);
let num_of_handles = buf_len / size_of::<r_efi::efi::Handle>();
let mut buf: Vec<r_efi::efi::Handle> = 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::<r_efi::efi::Handle>()) };
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<NonNull<crate::ffi::c_void>> {
let boot_services: NonNull<efi::BootServices> =
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"))
}
}

View File

@ -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 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 @@
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<Option<NonNull<crate::ffi::c_void>>> = Cell::new(None);
}
static EXIT_BOOT_SERVICE_EVENT: AtomicPtr<crate::ffi::c_void> =
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
}

View File

@ -4,15 +4,16 @@
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 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
}
}
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<PathBuf> {
unsupported()
@ -173,6 +169,14 @@ pub fn current_exe() -> io::Result<PathBuf> {
pub struct Env(!);
impl Env {
// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::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 @@ fn next(&mut self) -> Option<(OsString, OsString)> {
}
}
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<PathBuf> {
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<r_efi::efi::BootServices> = 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()
}

View File

@ -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.