Skip to content

Instantly share code, notes, and snippets.

@dvdhrm
Created December 10, 2018 09:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dvdhrm/eff3a1b2776a2cd4e7f20a7a46bb75b7 to your computer and use it in GitHub Desktop.
Save dvdhrm/eff3a1b2776a2cd4e7f20a7a46bb75b7 to your computer and use it in GitHub Desktop.
//
// Example: Hello World!
//
// This is an example UEFI application that prints "Hello World!", then waits for key input before
// it exits. It serves as base example how to write UEFI applications without any helper modules
// other than the UEFI protocol definitions.
//
// The `efi_main` function serves as entry-point. Depending on your target-configuration, this
// entry point might be called differently. If you use the target-configuration shipped with
// r-efi, then `efi_main` is the selected PE/COFF entry point.
//
// Additionally, a panic handler is provided. This is executed by rust on panic. For simplicity,
// we simply end up in an infinite loop. For real applications, this method should probably call
// into `SystemTable->boot_services->exit()` to exit the UEFI application. Note, however, that
// UEFI applications are likely to run in the same address space as the entire firmware. Hence,
// halting the machine might be a viable alternative. All that is out-of-scope of this example,
// though.
//
// Lastly, note that UEFI uses UTF-16 strings. Since rust literals are UTF-8, we have to use an
// open-coded, zero-terminated, UTF-16 array as argument to `output_string()`. Similarly to the
// panic handler, real applications should rather use UTF-16 modules.
//
#![feature(alloc)]
#![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();
#[panic_handler]
fn rust_panic_handler(_info: &core::panic::PanicInfo) -> ! {
loop {}
}
#[alloc_error_handler]
fn foo(_layout: core::alloc::Layout) -> ! {
panic!();
}
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.
s = String::from("Hello World!\n");
v = s.encode_utf16().collect();
v.push(0);
// Print "Hello World!".
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.
//
// We call `drop` explicitly on the attachment to guarantee it is kept alive until after the
// efi_run() function.
//
// XXX: We really should figure out how to guarantee @attachment stays alive. Is the compiler
// allowed to re-order? Is deinitialization guaranteed to be at the *end* of a block? Does
// anybody know whether such guarantees are given by the rust ABI?
#[no_mangle]
pub extern fn efi_main(h: efi::Handle, st: *mut efi::SystemTable) -> efi::Status {
let r;
unsafe {
let mut allocator = r_efi_alloc::alloc::Allocator::from_system_table(
st,
efi::MemoryType::LoaderData,
);
let attachment = GLOBAL_ALLOCATOR.attach(&mut allocator);
r = efi_run(h, st);
drop(attachment);
}
r
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment