rustc/vendor/r-efi-alloc-1.0.0/examples/hello-world.rs
2024-06-24 14:48:22 +02:00

119 lines
4.6 KiB
Rust

// 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<u16>;
// 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)
}
}