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/examples/freestanding.rs | 34 +++++ vendor/r-efi/examples/gop-query.rs | 188 ++++++++++++++++++++++++++++ vendor/r-efi/examples/hello-world.rs | 55 ++++++++ vendor/r-efi/examples/uefi-cross-compile.rs | 41 ++++++ 4 files changed, 318 insertions(+) create mode 100644 vendor/r-efi/examples/freestanding.rs create mode 100644 vendor/r-efi/examples/gop-query.rs create mode 100644 vendor/r-efi/examples/hello-world.rs create mode 100644 vendor/r-efi/examples/uefi-cross-compile.rs (limited to 'vendor/r-efi/examples') diff --git a/vendor/r-efi/examples/freestanding.rs b/vendor/r-efi/examples/freestanding.rs new file mode 100644 index 000000000..1c4e3aaf4 --- /dev/null +++ b/vendor/r-efi/examples/freestanding.rs @@ -0,0 +1,34 @@ +// Example: Freestanding +// +// This example is a plain UEFI application without any external requirements +// but `core`. It immediately returns control to the caller upon execution, +// yielding the exit code 0. +// +// The `main` function serves as entry-point. Depending on your +// target-configuration, it must be exported with a pre-configured name so the +// linker will correctly mark it as entry-point. The target configurations +// shipped with upstream rust-lang use `efi_main` as symbol name. +// +// 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 for this example, though. +// +// Note that as of rust-1.31.0, all features used here are stabilized. No +// unstable features are required, nor do we rely on nightly compilers. + +#![no_main] +#![no_std] + +#[panic_handler] +fn panic_handler(_info: &core::panic::PanicInfo) -> ! { + loop {} +} + +#[export_name = "efi_main"] +pub extern "C" fn main(_h: *mut core::ffi::c_void, _st: *mut core::ffi::c_void) -> usize { + 0 +} diff --git a/vendor/r-efi/examples/gop-query.rs b/vendor/r-efi/examples/gop-query.rs new file mode 100644 index 000000000..b4ab4c370 --- /dev/null +++ b/vendor/r-efi/examples/gop-query.rs @@ -0,0 +1,188 @@ +// Example: Graphics Query +// +// This is a slightly more complex UEFI application than `hello-world`. It +// locates the graphics-output-protocol, queries its current mode and prints +// the current resolution to the UEFI console. +// +// This example should make everyone aware that UEFI programing in Rust really +// asks for helper layers. While the C/FFI/Spec API can be used directly, it +// is quite cumbersome. Especially the error handling is overly difficult. +// +// Nevertheless, this example shows how to find UEFI protocol and invoke +// their member functions. +// +// Like all the other r-efi examples, it is a standalone example. That is, no +// UTF-16 helpers are pulled in, nor any allocators or panic frameworks. For +// real world scenarios, you really should choose such helpers. + +#![no_main] +#![no_std] + +use r_efi::efi; + +#[panic_handler] +fn panic_handler(_info: &core::panic::PanicInfo) -> ! { + loop {} +} + +fn fail(_r: efi::Status) -> ! { + panic!(); +} + +// A simple `itoa()`-ish function that takes a u32 and turns it into a UTF-16 +// string. It always prints exactly 10 characters, so leading zeroes are used +// for small numbers. +fn utoa(mut u: u32, a: &mut [u16]) { + for i in 0..10 { + a[9 - i] = 0x0030u16 + ((u % 10) as u16); + u = u / 10; + } +} + +// A simple helper that takes two integers and prints them to the UEFI console +// with a short prefix. It uses a UTF-16 buffer and fills in the numbers before +// printing the entire buffer. +fn print_xy(st: *mut efi::SystemTable, x: u32, y: u32) { + let mut s = [ + 0x0058u16, 0x0059u16, 0x003au16, // "XY:" + 0x0020u16, // " " + 0x0020u16, 0x0020u16, 0x0020u16, 0x0020u16, // " " + 0x0020u16, 0x0020u16, 0x0020u16, 0x0020u16, // " " + 0x0020u16, 0x0020u16, // " " + 0x0078u16, // "x" + 0x0020u16, 0x0020u16, 0x0020u16, 0x0020u16, // " " + 0x0020u16, 0x0020u16, 0x0020u16, 0x0020u16, // " " + 0x0020u16, 0x0020u16, // " " + 0x000au16, // "\n" + 0x0000u16, // NUL + ]; + + utoa(x, &mut s[4..14]); + utoa(y, &mut s[15..25]); + + unsafe { + let r = ((*(*st).con_out).output_string)((*st).con_out, s.as_ptr() as *mut efi::Char16); + if r.is_error() { + fail(r); + } + } +} + +// This function locates singleton UEFI protocols. Those protocols do not +// require to register listener handles, but are globally available to all +// UEFI applications. It takes a GUID of the protocol to locate and returns +// the protocol pointer on success. +fn locate_singleton( + st: *mut efi::SystemTable, + guid: *const efi::Guid, +) -> Result<*mut core::ffi::c_void, efi::Status> { + let mut interface: *mut core::ffi::c_void = core::ptr::null_mut(); + let mut handles: *mut efi::Handle = core::ptr::null_mut(); + let mut n_handles: usize = 0; + let mut r: efi::Status; + + // Use `locate_handle_buffer()` to find all handles that support the + // specified protocol. + unsafe { + if (*st).hdr.revision < efi::SYSTEM_TABLE_REVISION_1_10 { + // We use `LocateHandleBuffer`, which was introduced in 1.10. + return Err(efi::Status::UNSUPPORTED); + } + + let r = ((*(*st).boot_services).locate_handle_buffer)( + efi::BY_PROTOCOL, + guid as *mut _, + core::ptr::null_mut(), + &mut n_handles, + &mut handles, + ); + match r { + efi::Status::SUCCESS => {} + efi::Status::NOT_FOUND => return Err(r), + efi::Status::OUT_OF_RESOURCES => return Err(r), + _ => panic!(), + }; + } + + // Now that we have all handles with the specified protocol, query it for + // the protocol interface. We loop here, even though every item should + // succeed. Lets be on the safe side. + // Secondly, we use `handle_protocol()` here, but really should be using + // `open_protocol()`. But for singleton protocols, this does not matter, + // so lets use the simple path for now. + unsafe { + r = efi::Status::NOT_FOUND; + for i in 0..n_handles { + r = ((*(*st).boot_services).handle_protocol)( + *handles.offset(core::convert::TryFrom::::try_from(i).unwrap()), + guid as *mut _, + &mut interface, + ); + match r { + efi::Status::SUCCESS => break, + efi::Status::UNSUPPORTED => continue, + _ => panic!(), + }; + } + } + + // Free the allocated buffer memory of `handles`. This was allocated on the + // pool by `locate_handle_buffer()`. + unsafe { + let r = ((*(*st).boot_services).free_pool)(handles as *mut core::ffi::c_void); + assert!(!r.is_error()); + } + + // In case we found nothing, return `NOT_FOUND`, otherwise return the + // interface identifier. + match r { + efi::Status::SUCCESS => Ok(interface), + _ => Err(efi::Status::NOT_FOUND), + } +} + +// A simple helper that queries the current mode of the GraphicsOutputProtocol +// and returns the x and y dimensions on success. +fn query_gop( + gop: *mut efi::protocols::graphics_output::Protocol, +) -> Result<(u32, u32), efi::Status> { + let mut info: *mut efi::protocols::graphics_output::ModeInformation = core::ptr::null_mut(); + let mut z_info: usize = 0; + + unsafe { + // We could just look at `gop->mode->info`, but lets query the mode + // instead to show how to query other modes than the active one. + let r = ((*gop).query_mode)(gop, (*(*gop).mode).mode, &mut z_info, &mut info); + match r { + efi::Status::SUCCESS => {} + efi::Status::DEVICE_ERROR => return Err(r), + _ => panic!(), + }; + if z_info < core::mem::size_of_val(&*info) { + return Err(efi::Status::UNSUPPORTED); + } + + Ok(((*info).horizontal_resolution, (*info).vertical_resolution)) + } +} + +// This is the UEFI application entrypoint. We use it to locate the GOP +// pointer, query the current mode, and then print it to the system console. +#[export_name = "efi_main"] +pub extern "C" fn main(_h: efi::Handle, st: *mut efi::SystemTable) -> efi::Status { + let r = locate_singleton(st, &efi::protocols::graphics_output::PROTOCOL_GUID); + let gop = match r { + Ok(v) => v, + Err(r) => fail(r), + }; + + let r = query_gop(gop as _); + let v = match r { + Ok(v) => v, + Err(r) => fail(r), + }; + + print_xy(st, v.0, v.1); + + efi::Status::SUCCESS +} diff --git a/vendor/r-efi/examples/hello-world.rs b/vendor/r-efi/examples/hello-world.rs new file mode 100644 index 000000000..25b5243ac --- /dev/null +++ b/vendor/r-efi/examples/hello-world.rs @@ -0,0 +1,55 @@ +// 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. +// +// This example builds upon the `freestanding.rs` example, using the same setup +// and rust integration. See there for details on the panic-handler and entry +// point configuration. +// +// 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. + +#![no_main] +#![no_std] + +use r_efi::efi; + +#[panic_handler] +fn panic_handler(_info: &core::panic::PanicInfo) -> ! { + loop {} +} + +#[export_name = "efi_main"] +pub extern "C" fn main(_h: efi::Handle, st: *mut efi::SystemTable) -> efi::Status { + let s = [ + 0x0048u16, 0x0065u16, 0x006cu16, 0x006cu16, 0x006fu16, // "Hello" + 0x0020u16, // " " + 0x0057u16, 0x006fu16, 0x0072u16, 0x006cu16, 0x0064u16, // "World" + 0x0021u16, // "!" + 0x000au16, // "\n" + 0x0000u16, // NUL + ]; + + // Print "Hello World!". + let r = + unsafe { ((*(*st).con_out).output_string)((*st).con_out, s.as_ptr() as *mut efi::Char16) }; + 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 +} diff --git a/vendor/r-efi/examples/uefi-cross-compile.rs b/vendor/r-efi/examples/uefi-cross-compile.rs new file mode 100644 index 000000000..f99870a21 --- /dev/null +++ b/vendor/r-efi/examples/uefi-cross-compile.rs @@ -0,0 +1,41 @@ +// Bare UEFI Compilation +// +// This is a copy of a cross-compile test from the upstream rust repository. It +// is used to verify cross-compilation of UEFI targets works. It is included +// verbatim here to make sure we can run tests against it and get notifications +// when it breaks. + +// ----------------------------------------------------------------------------- +// COPY FROM `rust-lang/rust/src/test/codegen/uefi-cross-compile.rs` +// ----------------------------------------------------------------------------- + +// Checks whether UEFI targets cross-compile successfully. +// +// This test contains a simple UEFI program that simply exits with return code +// 0. It can be easily run from the UEFI shell (but any other UEFI environment +// works as well). This program is not run as part of the test. The test merely +// verifies the cross-compilation does not fail and an entry-point is emitted. +// +// The imported definitions from the UEFI specification are intentionally left +// incomplete. Only the bits that are actually used by this test are defined. + +// min-llvm-version 9.0 + +// compile-flags: --target x86_64-unknown-uefi + +#![feature(lang_items, no_core)] +#![no_core] +#![no_main] + +#[lang = "sized"] +pub trait Sized {} +#[lang = "freeze"] +pub trait Freeze {} +#[lang = "copy"] +pub trait Copy {} + +// CHECK: define win64cc i64 @efi_main{{.*}} +#[export_name = "efi_main"] +pub extern "efiapi" fn main(_h: *mut usize, _st: *mut usize) -> usize { + return 0; +} -- cgit v1.2.3