From c23a457e72abe608715ac76f076f47dc42af07a5 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 30 May 2024 20:31:44 +0200 Subject: Merging upstream version 1.74.1+dfsg1. Signed-off-by: Daniel Baumann --- vendor/r-efi-alloc/examples/hello-world.rs | 118 +++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 vendor/r-efi-alloc/examples/hello-world.rs (limited to 'vendor/r-efi-alloc/examples/hello-world.rs') diff --git a/vendor/r-efi-alloc/examples/hello-world.rs b/vendor/r-efi-alloc/examples/hello-world.rs new file mode 100644 index 000000000..e1265f868 --- /dev/null +++ b/vendor/r-efi-alloc/examples/hello-world.rs @@ -0,0 +1,118 @@ +// Example: Hello World! +// +// This example extends the Hello-World example from the `r-efi` crate. The +// main entry-point now installs a global rust allocator and calls into the +// `efi_run()` function. The latter then prints the string "Hello World!\n" to +// console-out, waits for any key-input, and returns. +// +// Unlike the original example, here we make use of the global allocator by +// using the types from rust's `alloc::*` crate. These needs dynamic +// allocations, and we can now serve them by providing the allocator from +// `r-efi-alloc`. +// +// To integrate the allocator with rust, we need to provide a global variable +// annotated as `#[global_allocator]`. It must implement the `GlobalAlloc` +// trait. We use the `Bridge` type from our crate to serve this. Furthermore, +// we need to define a callback to be invoked in out-of-memory situations. We +// simply make it forward the error to our panic-handler, which we already +// provided in the previous example. +// +// The error-handler required by the `alloc::*` objects is unstable as well, +// so the `alloc_error_handler` feature is required. + +#![feature(alloc_error_handler)] +#![no_main] +#![no_std] + +extern crate alloc; + +use alloc::string::String; +use alloc::vec::Vec; +use r_efi::efi; + +#[global_allocator] +static GLOBAL_ALLOCATOR: r_efi_alloc::global::Bridge = r_efi_alloc::global::Bridge::new(); + +#[alloc_error_handler] +fn rust_oom_handler(_layout: core::alloc::Layout) -> ! { + panic!(); +} + +#[panic_handler] +fn rust_panic_handler(_info: &core::panic::PanicInfo) -> ! { + loop {} +} + +// This is the wrapped entry-point of this Hello-World UEFI Application. The +// caller provides us with an extended environment, by guaranteeing us a global +// rust allocator. Hence, we can now make use of all the `alloc::*` objects. +// +// Similar to the Hello-World example from `r-efi`, this example just prints +// "Hello World!\n" to standard-output, waits for any key input, then exits. +// +// With `alloc::*` to our disposal, we use normal rust strings, and convert +// them to UTF-16 vectors before passing them to UEFI. +pub fn efi_run(_h: efi::Handle, st: *mut efi::SystemTable) -> efi::Status { + let s: String; + let mut v: Vec; + + // Create string and convert to UTF-16. We need a terminating NUL, since + // UEFI uses C-String style wide-strings. + s = String::from("Hello World!\n"); + v = s.encode_utf16().collect(); + v.push(0); + + // Print the string on console-out. + let r = + unsafe { ((*(*st).con_out).output_string)((*st).con_out, v.as_mut_slice().as_mut_ptr()) }; + if r.is_error() { + return r; + } + + // Wait for key input, by waiting on the `wait_for_key` event hook. + let r = unsafe { + let mut x: usize = 0; + ((*(*st).boot_services).wait_for_event)(1, &mut (*(*st).con_in).wait_for_key, &mut x) + }; + if r.is_error() { + return r; + } + + efi::Status::SUCCESS +} + +// This is the main UEFI entry point, called by the UEFI environment when the +// application is spawned. We use it to create an allocator and attach it to +// the global allocator bridge. Then we invoke the `efi_run()` function as if +// it was the main entry-point. Since the attachment is dropped after +// `efi_run()` returns, the allocator is available throughout the entire +// runtime. +// +// Note that both calls here require unsafe code: +// +// * Allocator::from_system_table(): We must guarantee `SystemTable` survives +// longer than the allocator object we create. This is trivially true +// here, since we pass in the system-table from the UEFI core, which is +// guaranteed to outlive us. However, we must make sure not to call +// ExitBootServices() and friends, obviously. +// +// * Bridge::attach(): This function is unsafe, since it requires the caller +// to guarantee that all memory allocations are released before it is +// detached. Since we do not perform allocations ourselves here, we know +// that they must be released before `efi_run()` returns. Hence, we are +// safe as well. +// +// Lastly, we use the `LoaderData` annotation for all memory allocations. +// Depending on your UEFI application type you might want different allocators +// for different operations. The rust global allocator is a fixed type, so you +// need to use custom-allocators for all allocations that need to be put in +// different memory regions. +#[no_mangle] +pub extern "C" fn efi_main(h: efi::Handle, st: *mut efi::SystemTable) -> efi::Status { + unsafe { + let mut allocator = r_efi_alloc::alloc::Allocator::from_system_table(st, efi::LOADER_DATA); + let _attachment = GLOBAL_ALLOCATOR.attach(&mut allocator); + + efi_run(h, st) + } +} -- cgit v1.2.3