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 <ayushsingh1325@gmail.com>
This commit is contained in:
parent
5a4e47ebed
commit
48c6ae0611
24 changed files with 718 additions and 18 deletions
3
.nlsp-settings/rust_analyzer.json
Normal file
3
.nlsp-settings/rust_analyzer.json
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"rust-analyzer.cargo.target": "x86_64-unknown-uefi"
|
||||
}
|
23
Cargo.lock
23
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",
|
||||
|
|
|
@ -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<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>(
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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\"");
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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")]
|
||||
|
|
54
library/std/src/os/uefi/env.rs
Normal file
54
library/std/src/os/uefi/env.rs
Normal file
|
@ -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<c_void>, AtomicPtr<c_void>)> = 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<c_void>, system_table: NonNull<c_void>) {
|
||||
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<c_void> {
|
||||
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<c_void> {
|
||||
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<crate::ffi::c_void>> {
|
||||
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<crate::ffi::c_void>> {
|
||||
NonNull::new(GLOBALS.get()?.1.load(Ordering::Acquire))
|
||||
}
|
7
library/std/src/os/uefi/mod.rs
Normal file
7
library/std/src/os/uefi/mod.rs
Normal file
|
@ -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;
|
|
@ -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)]
|
||||
|
|
|
@ -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::*;
|
||||
|
|
32
library/std/src/sys/uefi/alloc.rs
Normal file
32
library/std/src/sys/uefi/alloc.rs
Normal file
|
@ -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) }
|
||||
}
|
||||
}
|
||||
}
|
269
library/std/src/sys/uefi/common.rs
Normal file
269
library/std/src/sys/uefi/common.rs
Normal file
|
@ -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<Vec<NonNull<crate::ffi::c_void>>> {
|
||||
fn inner(
|
||||
guid: &mut Guid,
|
||||
boot_services: NonNull<r_efi::efi::BootServices>,
|
||||
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<r_efi::efi::Handle> =
|
||||
Vec::with_capacity(buf_len / crate::mem::size_of::<r_efi::efi::Handle>());
|
||||
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::<r_efi::efi::Handle>()) };
|
||||
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<T>(
|
||||
handle: NonNull<crate::ffi::c_void>,
|
||||
mut protocol_guid: Guid,
|
||||
) -> io::Result<NonNull<T>> {
|
||||
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<r_efi::efi::BootServices> {
|
||||
let system_table: NonNull<r_efi::efi::SystemTable> = 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<NonNull<r_efi::efi::BootServices>> {
|
||||
let system_table: NonNull<r_efi::efi::SystemTable> = uefi::env::try_system_table()?.cast();
|
||||
let boot_services = unsafe { (*system_table.as_ptr()).boot_services };
|
||||
NonNull::new(boot_services)
|
||||
}
|
9
library/std/src/sys/uefi/env.rs
Normal file
9
library/std/src/sys/uefi/env.rs
Normal file
|
@ -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";
|
||||
}
|
155
library/std/src/sys/uefi/mod.rs
Normal file
155
library/std/src/sys/uefi/mod.rs
Normal file
|
@ -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<T>() -> std_io::Result<T> {
|
||||
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::<rng::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
|
||||
}
|
25
library/std/src/sys/uefi/path.rs
Normal file
25
library/std/src/sys/uefi/path.rs
Normal file
|
@ -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<Prefix<'_>> {
|
||||
None
|
||||
}
|
||||
|
||||
pub(crate) fn absolute(_path: &Path) -> io::Result<PathBuf> {
|
||||
unsupported()
|
||||
}
|
21
library/std/src/sys/uefi/tests.rs
Normal file
21
library/std/src/sys/uefi/tests.rs
Normal file
|
@ -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 ()>());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<u16> = 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())
|
||||
}
|
||||
```
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Add table
Reference in a new issue