Giter Club home page Giter Club logo

uefi-rs's Introduction

uefi-rs

Crates.io Docs.rs License Build status Stars

Description

UEFI started as the successor firmware to the BIOS in x86 space and developed to a universal firmware specification for various platforms, such as ARM. It provides an early boot environment with a variety of specified ready-to-use "high-level" functionality, such as accessing disks or the network. EFI images, the files that can be loaded by an UEFI environment, can leverage these abstractions to extend the functionality in form of additional drivers, OS-specific bootloaders, or different kind of low-level applications.

Our mission is to provide safe and performant wrappers for UEFI interfaces, and allow developers to write idiomatic Rust code.

This repository provides various crates:

  • uefi-raw: Raw Rust UEFI bindings for basic structures and functions.
  • uefi: High-level wrapper around various low-level UEFI APIs.
    Offers various optional features for typical Rust convenience, such as a Logger and an Allocator. (This is what you are usually looking for!)
  • uefi-macros: Helper macros. Used by uefi.

You can use the abstractions for example to:

  • create OS-specific loaders and leverage UEFI boot service
  • access UEFI runtime services from an OS

All crates are compatible with all platforms that both the Rust compiler and UEFI support, such as i686, x86_64, and aarch64). Please note that we can't test all possible hardware/firmware/platform combinations.

UEFI App running in QEMU Screenshot of an application running in QEMU on an UEFI firmware that leverages our Rust library.

User Documentation

For a quick start, please check out the UEFI application template.

The uefi-rs book contains a tutorial, how-tos, and overviews of some important UEFI concepts. Reference documentation for the various crates can be found on docs.rs:

For additional information, refer to the UEFI specification.

MSRV

See the uefi package's README.

Developer Guide

Project structure

This project contains multiple sub-crates:

  • uefi: defines the standard UEFI tables / interfaces. The objective is to stay unopinionated and safely wrap most interfaces. Additional opinionated features (such as a Logger) are feature-gated.

  • uefi-macros: procedural macros that are used to derive some traits in uefi.

  • uefi-raw: raw types that closely match the definitions in the UEFI Specification. Safe wrappers for these types are provided by the uefi crate. The raw types are suitable for implementing UEFI firmware.

  • uefi-test-runner: a UEFI application that runs unit / integration tests.

Building and testing uefi-rs

Use the cargo xtask command to build and test the crate.

Available commands:

  • build: build all the UEFI packages
    • --release: build in release mode
    • --target {x86_64,ia32,aarch64}: choose target UEFI arch
  • clippy: run clippy on all the packages
    • --target {x86_64,ia32,aarch64}: choose target UEFI arch
    • --warnings-as-errors: treat warnings as errors
  • doc: build the docs for the UEFI packages
    • --open: open the docs in a browser
    • --warnings-as-errors: treat warnings as errors
  • run: build uefi-test-runner and run it in QEMU
    • --ci: disable some tests that don't work in the CI
    • --disable-kvm: disable hardware accelerated virtualization support in QEMU. Especially useful if you want to run the tests under WSL on Windows.
    • --example <NAME>: run an example instead of the main binary.
    • --headless: run QEMU without a GUI
    • --ovmf-code <PATH>: path of an OVMF code file
    • --ovmf-vars <PATH>: path of an OVMF vars file
    • --release: build in release mode
    • --target {x86_64,ia32,aarch64}: choose target UEFI arch
  • test: run unit tests and doctests on the host

The uefi-test-runner directory contains a sample UEFI app which exercises most of the library's functionality.

Check out the testing project's README.md for prerequisites for running the tests.

Contributing

We welcome issues and pull requests! For instructions on how to set up a development environment and how to add new protocols, check out CONTRIBUTING.md.

License

The code in this repository is licensed under the Mozilla Public License 2. This license allows you to use the crate in proprietary programs, but any modifications to the files must be open-sourced.

The full text of the license is available in the license file.

uefi-rs's People

Contributors

adriandanis avatar atsukitak avatar blitz avatar ctrlaltmilk avatar d-sonuga avatar dependabot-preview[bot] avatar dependabot[bot] avatar e820 avatar edigaryev avatar foxcob avatar fredrikaleksander avatar gabrielmajeri avatar gelven4sec avatar gil0mendes avatar hadrieng2 avatar imtsuki avatar isaacwoods avatar johnazoidberg avatar josephlr avatar medhefgo avatar necauqua avatar nicholasbishop avatar phil-opp avatar phip1611 avatar raccog avatar renovate[bot] avatar sky5454 avatar supdrewin avatar timrobertsdev avatar toku-sa-n avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

uefi-rs's Issues

Clarify lifetime of the pointers returned by the API

The uefi-rs API returns pointers in a couple of place (most prominent being the NonNull returned by protocol queries). It would be nice if the docs could specify how long these pointers will be valid when the spec tells us. I'll try to add such docs as I progress through the UEFI spec.

Could not find `alloc` in `{{root}}`

Just tried giving uefi-rs a spin again after a hiatus from low-level dev, and I get these strange build errors on my first try at running the tests:

uefi-rs/uefi-test-runner> ./build.py run
   Compiling uefi-exts v0.1.0 (/home/hadrien/Bureau/Programmation/uefi-rs/uefi-exts)                                                                                                                                                                                                                                         
error[E0433]: failed to resolve. Could not find `alloc` in `{{root}}`                                                                                                                                                                                                                                                        
 --> uefi-exts/src/boot.rs:5:5                                                                                                                                                                                                                                                                                               
  |                                                                                                                                                                                                                                                                                                                          
5 | use alloc::vec::Vec;                                                                                                                                                                                                                                                                                                     
  |     ^^^^^ Could not find `alloc` in `{{root}}`                                                                                                                                                                                                                                                                           
                                                                                                                                                                                                                                                                                                                             
error[E0433]: failed to resolve. Use of undeclared type or module `Vec`                                                                                                                                                                                                                                                      
  --> uefi-exts/src/boot.rs:26:26                                                                                                                                                                                                                                                                                            
   |                                                                                                                                                                                                                                                                                                                         
26 |         let mut buffer = Vec::with_capacity(buffer_size);                                                                                                                                                                                                                                                               
   |                          ^^^ Use of undeclared type or module `Vec`                                                                                                                                                                                                                                                     
                                                                                                                                                                                                                                                                                                                             
error[E0412]: cannot find type `Vec` in this scope                                                                                                                                                                                                                                                                           
  --> uefi-exts/src/boot.rs:11:51                                                                                                                                                                                                                                                                                            
   |                                                                                                                                                                                                                                                                                                                         
11 |     fn find_handles<P: Protocol>(&self) -> Result<Vec<Handle>>;                                                                                                                                                                                                                                                         
   |                                                   ^^^ not found in this scope                                                                                                                                                                                                                                           
                                                                                                                                                                                                                                                                                                                             
error[E0412]: cannot find type `Vec` in this scope                                                                                                                                                                                                                                                                           
  --> uefi-exts/src/boot.rs:18:51                                                                                                                                                                                                                                                                                            
   |                                                                                                                                                                                                                                                                                                                         
18 |     fn find_handles<P: Protocol>(&self) -> Result<Vec<Handle>> {                                                                                                                                                                                                                                                        
   |                                                   ^^^ not found in this scope                                                                                                                                                                                                                                           
                                                                                                                                                                                                                                                                                                                             
warning: unused import: `alloc::vec::Vec`                                                                                                                                                                                                                                                                                    
 --> uefi-exts/src/boot.rs:5:5                                                                                                                                                                                                                                                                                               
  |                                                                                                                                                                                                                                                                                                                          
5 | use alloc::vec::Vec;                                                                                                                                                                                                                                                                                                     
  |     ^^^^^^^^^^^^^^^                                                                                                                                                                                                                                                                                                      
  |                                                                                                                                                                                                                                                                                                                          
  = note: #[warn(unused_imports)] on by default                                                                                                                                                                                                                                                                              
                                                                                                                                                                                                                                                                                                                             
error: aborting due to 4 previous errors                                                                                                                                                                                                                                                                                     
                                                                                                                                                                                                                                                                                                                             
Some errors occurred: E0412, E0433.                                                                                                                                                                                                                                                                                          
For more information about an error, try `rustc --explain E0412`.                                                                                                                                                                                                                                                            
error: Could not compile `uefi-exts`.                                                                                                                                                                                                                                                                                        

To learn more, run the command again with --verbose.
Subprocess cargo exited with error code 101

I guess that for some reason, my toolchain does not find the standard alloc crate. Ever got this one and know how to handle it?

Investigate efiapi!{} macro

In Rust, I think resolving #38 entails generating different extern "<something>" statements depending on the target architecture. The pointer wrapping discussed in #40, if it ends up being implemented, could also use a bit of code generation to automatically turn *const T into EfiPtr<T> and *mut T into EfiPtrMut<T>.

A general solution to handle this kind of EFI API boilerplate would be to borrow the EFIAPI design from the UEFI spec and turn it into a Rust macro. For example, this code...

efiapi! { fn(u64, usize) -> Status }

...would generate this code on x86_64:

extern "win64" fn(u64, usize) -> Status

...and this code on i386:

extern "cdecl" fn(u64, usize) -> Status

There are limitations to what a declarative macro can generate in Rust (IIRC they can't generate types), and we probably don't want to go for procedural macros on a task this simple, so the final design may be modified to accomodate these limitations. But I think the general idea could portably address the issue of EFI calling conventions without bringing too much boilerplate in the declaration of each individual API entry point.

Implement Debug trait for CStr16

The custom CStr16 wrapper type should have a Debug trait implementation which tries to print its characters one by one, replacing invalid/unsupported characters with some replacement char (or just panic).

exit_boot_services should consume the host BootServices

Since the boot services are invalidated upon calling exit_boot_services, they should not remain available in the safe API. So I would propose having BootServices::exit_boot_services take a self value as a parameter, rather than a &self reference.

One point to be resolved, though, is what should happen if exit_boot_services fails, for example if the mmap_key is invalid. In this case, perhaps the BootServices value, or a subset thereof (to account for partial shutdown), should be returned in order to let the user try again?

Study other enums in uefi-rs

It is well possible that #48 was just the tip of the iceberg, and that we need to rethink the way we handle enums in FFI. To check if this is the case, we should take a tour of the various enums in uefi-rs and determine if...

  • The spec might add new enum variants in the future and expose them to us without us asking for it.
  • Implementations are allowed to add new enum variants if they feel so inclined.

If the answer to either of these questions is "yes" or even "I don't know", then the enum must be replaced with an integer newtype as was done for Status.

Misleading comment in the code

The uefi::alloc::Allocator is only valid while the UEFI boot services are still available, right? It then seems that this comment is wrong, as it says it's valid for as long as the UEFI runtime services are available.

Add filesystem code to uefi-exts

The API for get_info() would greatly benefit from an allocating wrapper around it. Ideally a method like:

pub fn get_boxed_info<Info: FileProtocolInfo>(&mut self) -> Result<Box<Info>> {
    // Implement me!        
}

Update documentation on BootServices::locate_handle

BootServices::locate_handle doesn't currently work according to the comments and the test. There should be a mention that you need to set buffer.set_len after the first call locate_handle, not just after the second. The reason is because the code uses the length of the buffer * handle_size to get the buffer size the second time, and it will be 0, so the call will fail with BufferTooSmall, and the buffer will contain invalid pointers.

It is pretty easy to reproduce. If looping through the results in the test, the handles will all be bad pointers (on OVMF 0xAFAFAFAFAFAFAFAF aka EFI_BAD_POINTER)

Memory map queries do not enforce correct alignment

Both rustc and UEFI expect pointers to be correctly aligned for the target type. However, we do not enforce this when querying a memory map, as we take an unrestricted &mut [u8], cast it to *mut MemoryDescriptor, and pass it down to UEFI. This is undefined behavior waiting to happen.

Since type alignments are small and bytes are cheap in the archs targeted by UEFI, the right thing to do in this case is probably to skip the first few bytes of the input slice in order to reach a part which is properly aligned, and do the write there.

I solved a very similar problem as part of #69. As I expect such "take a buffer as input and write into it" patterns to be common in UEFI's low-level environment, it might be worthwhile to take the alignment correction code that I wrote there and generalize it.

Here is a tentative API mockup:

/// Best API most of the time
fn realign_for<T: Sized>(storage: &mut [u8]) -> &mut [u8];

/// Lower-level API that is sometimes needed because mem::align_of does not support DSTs
fn realign(storage: &mut [u8], alignment: usize) -> &mut [u8];

Better DST support could be provided with a custom derive, that computes the alignment automatically instead of having people compute it by hand, and errors out on dyn Trait. But I would only do this at a later point in uefi-rs' evolution, if we encounter the use case sufficiently often to justify the significant code complexity of that infrastructure.

Improve &str <-> &CStr16 lifecycle

When interfacing Rust with UEFI, one of the API mismatches that we need to take care of is that Rust uses UTF-8 Pascal-style strings while UEFI uses UCS-2 null-terminated strings.

Currently, we have...

  • A little bit of code duplication on UTF-8 to UCS-2 conversions.
  • The std-inspired CStr16 type for handling borrowed UEFI strings more conveniently and safely.
  • Some API entry points that expect &CStr16.
  • Others that expect &str and silently convert to UCS-2.
    • In one occurence (File::open), this restricts possible inputs due to finite buffer size.
    • In all cases, this makes the API more confusing by mixing char conversions with other concerns.
    • In a few cases like logging, we actually don't have any other choice.

I expect interoperability with UCS-2 to be a common problem in realistic UEFI application, so we may want to improve upon this situation by 1/standardizing conversions between UTF-8 and UCS-2 and 2/eliminating "hidden" conversions between the two whenever possible.


Coming from this perspective, here are some ideas at the core uefi-rs layer...

  • One utility fn that takes an &str and an &mut [u16] as an input, and returns a &CStr16, a char conversion error, or a "buffer full" error as an output.
  • One reciprocal utility fn that goes from &CStr16 + &mut [u8] to &str or similar errors as above.

...and more ideas for the convenience uefi-exts layer, where allocations are allowed:

  • A TryFrom<&CStr16> implementation for String.
  • A CString16 type with a TryFrom<&str> implementation.

Once we have that, automagic conversion from &str to UCS-2 should be eliminated whenever possible, in favor of taking a &CStr16.

If the user has enabled uefi-services conveniences, then converting from &str to &CStr16 is just a .try_from() away. And if the user wants to stick with the core crate, calling the underlying conversion utilities is more involved, but not terribly difficult.

I do not understand BltPixel's definition

The current spec has the following to say about the Blt buffer's pixel format (pp. 489)

Each software Blt buffer entry represents a pixel that is comprised of a 32-bit quantity. Byte zero of
the Blt buffer entry represents the Red component of the pixel. Byte one of the Blt buffer entry
represents the Green component of the pixel. Byte two of the Blt buffer entry represents the Blue
component of the pixel. Byte three of the Blt buffer entry is reserved and must be zero.

I would expect that definition to translate into this struct:

#[repr(C, packed)]
pub struct BltPixel {
    pub red: u8,    // "Byte zero represents the Red component"
    pub green: u8,  // "Byte one represents the Green component"
    pub blue: u8,   // "Byte two represents the Blue component"
    _reserved: u8,  // "Byte three is reserved and must be zero"
}

Or maybe this one, if I badly misunderstood UEFI's endianness:

#[repr(C, packed)]
pub struct BltPixel {
    _reserved: u8,  // "Byte three is reserved and must be zero"
    pub blue: u8,   // "Byte two represents the Blue component"
    pub green: u8,  // "Byte one represents the Green component"
    pub red: u8,    // "Byte zero represents the Red component"
}

However, what uefi-rs does is this:

#[repr(C, packed)]
pub struct BltPixel {
    pub blue: u8,   // "Byte two represents the Blue component"
    pub green: u8,  // "Byte one represents the Green component"
    pub red: u8,    // "Byte zero represents the Red component"
    _reserved: u8,  // "Byte three is reserved and must be zero"
}

There is no question that it works, and that the other interpretations don't (just gave them a try). But I don't understand why. It feels like there is a mismatch between what the spec says and what the implementation (here OVMF) is doing. Can you enlighten me?

Publish uefi-services (and uefi-exts) on crates.io

I don't think it is currently possible to use the uefi-services crate from something outside of the workspace in this project. It would be nice to just be able to add uefi and uefi-services to a projects dependencies and then define an efi_main and start building. Because efi-services isn't published as its own crate, this doesn't work at the moment.

The rust book mentions of workspaces that "If you publish the crates in the workspace to crates.io, each crate in the workspace will need to be published separately". So I don't think there is any way round this besides publishing it as a separate crate.

Slow logging with max resolution and scrolling

It seems that scrolling logging (trace!, debug! etc.) messages can become a bottleneck on bigger screens when it's scrolling and the Output protocol is set to the maximum resolution. But maybe I'm just doing something wrong.

I have the following function:

fn setup_output(st: &SystemTable<Boot>) {
    if let Ok(stdout) = st.boot_services().locate_protocol::<Output>() {
        let stdout = stdout.expect("Warnings encountered while opening Output");
        let stdout = unsafe { &mut *stdout.get() };
        let best_mode = stdout
            .modes()
            .last()
            .unwrap()
            .expect("Warnings encountered while querying text mode");
        stdout
            .set_mode(best_mode)
            .expect_success("Failed to change text mode");
    } else {
        warn!("UEFI Output Protocol is not supported.");
    }
}

When called during initialization, logging/adding new lines is now a bottleneck (but screen resolution is high), and when not called logging/scrolling is fast again (but screen resolution is low). This happens on real hardware.

Panic hook seems to be optimized out in release builds

On my system, building the test runners with the --release flag results in panics being ignored, most likely because the panic handler is optimized out. This is probably not what we want, especially as returning from a ! function is undefined behavior in Rust.

Random crashes with SimpleTextOutput Protocol on VirtualBox

Lately, I am experiencing random crashes with uefi-rs running on VirtualBox while writing to stdout. The panic message is like the following:

Panic in /Users/tsuki/.cargo/registry/src/mirrors.ustc.edu.cn-61ef6e0cd06fb9b8/uefi-0.4.3/src/logger.rs at (59, 13):
ERROR: called `Result::unwrap()` on an `Err` value: Error

After investigation, it turns out the following code causes this problem:

self.output_string(text)
.warning_as_error()
.map_err(|_| fmt::Error)

Where the UEFI firmware shipped with VirtualBox will report EFI_DEVICE_ERROR when we're outputting too fast, probably, I'm not sure.

Personally I think this is not a fatal error and since there is no extra error message returned by output_string(), I'll submit a PR that ignores EFI_DEVICE_ERROR here.

"Memory does not start at address 0" => Always?

In the test of the memory map, there is an assertion that ensures that the first physical address in the UEFI memory map is not zero.

As far as I know, there is nothing which guarantees that this will be the case. The EFI_MEMORY_DESCRIPTOR spec does not dictate it, and there is actually something at physical address 0 on x86, even though the EFI firmware may not show it (not to mention other CPU architectures where there is nothing special about a null pointer).

So spontaneously, I would replace this assertion with one that the number of pages of the first map entry is nonzero, which is guaranteed by the UEFI spec.

x64 General Protection Fault in Blog

Walking through https://medium.com/@gil0mendes/an-efi-app-a-bit-rusty-82c36b745f49, it seems that the API has changed, but the old API doesn't throw any type errors and compiles fine. When I go through and run within QEMU I get :
!!!! X64 Exception Type - 0D(#GP - General Protection) CPU Apic ID - 00000000 !!!! ExceptionData - 0000000000000000 RIP - 00000000057EE99C, CS - 0000000000000038, RFLAGS - 0000000000000206 RAX - 5453595320494249, RCX - 0000000007BEE018, RDX - 0000000007BEE000 RBX - 0000000007200498, RSP - 0000000007F0C3E0, RBP - 0000000007F0C5A8 RSI - 0000000000000009, RDI - 0000000007200498 R8 - 0000000000000000, R9 - 0000000007200318, R10 - 0000000007276098 R11 - 000000000000000A, R12 - 0000000000000000, R13 - 0000000007200B18 R14 - 0000000000000000, R15 - 0000000006504598 DS - 0000000000000030, ES - 0000000000000030, FS - 0000000000000030 GS - 0000000000000030, SS - 0000000000000030 CR0 - 0000000080010033, CR2 - 0000000000000000, CR3 - 0000000007C01000 CR4 - 0000000000000668, CR8 - 0000000000000000 DR0 - 0000000000000000, DR1 - 0000000000000000, DR2 - 0000000000000000 DR3 - 0000000000000000, DR6 - 00000000FFFF0FF0, DR7 - 0000000000000400 GDTR - 0000000007BEE698 0000000000000047, LDTR - 0000000000000000 IDTR - 00000000076F1018 0000000000000FFF, TR - 0000000000000000 FXSAVE_STATE - 0000000007F0C040 !!!! Find image based on IP(0x57EE99C) (snip) (ImageBase=00000000057EB000, EntryPoint=00000000057EC000) !!!!

This seems to stem from the uefi_services::init. Essentially it boils down to trying to dereference RAX which is obviously ASCII, but there doesn't seem to be cross references to the offending function. I didn't dive too deep down the rabbit hole beyond that. I swapped everything over to the new API seen in the test-runner and it also compiles file, but this actually works without faulting. I haven't hunted down where the actual issue is, but thought it might be useful to either re-document or maybe even add a macro that will enforce the new API.

Ensure ABI is correct for all targets

All the UEFI functions are declared with the extern "C" calling convention, which matches the specification.

However, this raises an issue regarding the ABI on x64: extern "C" refers to MS's ABI when compiling for Windows-like targets, but refers to the System-V ABI when compiling for UNIX targets.

This is OK when building with the example target.json, since it tells LLVM to compile for the x86_64-pc-windows-gnu target, which makes it use the MS ABI.

This can cause an issue if the calling executable does not use the MS ABI: if an OS kernel were to be built as an ELF executable, and use this crate for calling UEFI runtime services, it would lead to undefined behavior, since the ELF ABI, gnuabi, is not the same as UEFI's ABI, msabi.

With C compilers we could make a distinction with __attribute__((msabi)) and __attribute__((gnuabi)), but Rust doesn't seem to make that kind of distinction.

Basically, we need to find a way to have functions declared as extern "C" when compiling for all targets except x86_64, where we'll have to use extern "win64" (because otherwise extern "C" becomes extern "sysv64", which is incorrect).

Release master as 0.4.2 on crates.io?

I think due to the dependency of uefi-services on uefi I can't easily use uefi from master with uefi-services from crates.io.
(If you know a good work-around let me know)

provide a way to discover the size of the memory map before calling get_memory_map

The current rust implementation of get_memory_map doesn't provide a way to figure out the size of the provided output buffer. The UEFI spec says the function returns the required size of the memory map buffer when a buffer of the wrong size is passed, which allows users of the C API to pass a zero-sized buffer to the function to discover the required size (see this reference implementation for example).

I can see a couple solutions to this -

  1. Change the API of get_memory_map to return the required size of the memory map buffer if the provided buffer is too small
  2. Provide an additional function that returns the size of the memory map by calling the underlying memory_map function with a zero sized buffer and returning the resulting value in map_size.

With either of those solutions, a higher level function could be provided that does the allocation in uefi-utils.

I would be happy to help out with the implementation if you have an idea for the approach you want to take for this (I'll even try compiling it this time too!). Alternatively, if I'm just totally missing something that already exists, that would be great too.

linker error on latest nightly

I just updated to the latest nightly today and I got this error -

$ ./build.py build
   Compiling uefi-test-runner v0.1.0 (file:///home/demos/projects/uefi-rs/uefi-test-runner)
error: linking with `lld` failed: exit code: 1===================>      ] 9/10     
  |
  = note: "lld" "-flavor" "link" "/Subsystem:EFI_Application" "/Entry:uefi_start" "/LIBPATH:/home/demos/.xargo/lib/rustlib/x86_64-uefi/lib" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.114xigf1yyw2gcw0.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.16u6js6g0l3k1ic6.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.181cuta0v63atwcm.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.1c4sbqhvukbgthag.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.1dqvxks6k2bzkxe.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.1im38lueib99jsk0.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.1mvmz58owquyropc.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.1y16o1qfye96o7m0.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.23tqyymcb18u96mb.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.2fipfjjg6nxsyp4t.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.2imnh2vhxcqrizhm.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.2jqywn86b2gsqohu.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.2lyh15q6cjwzy18c.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.2mb8rq5da3exkx2g.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.2oiwwqohid6mabhc.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.2q5257pdh5222n7q.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.33j957i3px7pj5vz.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.35c7yhz90tqp9m9a.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.37huh28k6g3px6ck.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.38ps4pa181wsnsy9.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.391nabwpj8wu7mr0.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.3ayaeypdcro9d6yk.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.3s96syg39ip5dg5w.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.436dotimmrgzkwfa.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.43v6g0y2xsxoggnt.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.47chcpo31ood8q32.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.48721dc4k5qxei0u.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.49a7n47po4ttqjl7.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.4ahv1jxw4hkr9dtv.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.4ezmh1vbs95c5ack.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.4lmbw51bfoes4lkp.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.4xq48u46a1pwiqn7.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.4yh8x2b62dcih00t.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.4ypvbwho0bu5tnww.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.56dly8q07ws8ucdq.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.57k06xfugllsc526.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.5by9xggibmun5kn9.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.7p53qlves2rwxx0.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.8hvrv8qpny8dbel.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.8xzrsc1ux72v29j.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.9elsx31vb4it187.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.9fcb3syd3ne5k0n.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.bxp0med4do8g2u9.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.rzsapsfex8xntj4.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.y08g5q2x813c4wx.rcgu.o" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.z9ox7biyn1otfln.rcgu.o" "/OUT:/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.efi" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/uefi_test_runner-7bc0d1f33800b032.crate.allocator.rcgu.o" "/OPT:REF,NOICF" "/DEBUG" "/LIBPATH:/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps" "/LIBPATH:/home/demos/projects/uefi-rs/target/debug/deps" "/LIBPATH:/home/demos/.xargo/lib/rustlib/x86_64-uefi/lib" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/libuefi_utils-8050474b59705aeb.rlib" "/home/demos/.xargo/lib/rustlib/x86_64-uefi/lib/liballoc-fecacabd009468bc.rlib" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/libuefi_services-b91c7ff6cea21318.rlib" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/libuefi_alloc-0be210fde2ecf984.rlib" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/libuefi_logger-399a6cab2dc38960.rlib" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/liblog-d266a9b7833cdac5.rlib" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/libcfg_if-fe0d654391b4e273.rlib" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/librlibc-e741ceffc6c2dba7.rlib" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/libuefi-5ccfc5e76190e324.rlib" "/home/demos/projects/uefi-rs/target/x86_64-uefi/debug/deps/libbitflags-f75c9383c243bedd.rlib" "/home/demos/.xargo/lib/rustlib/x86_64-uefi/lib/libcore-d722c004db30ce6b.rlib" "/home/demos/.xargo/lib/rustlib/x86_64-uefi/lib/libcompiler_builtins-7fa11aff09ec40fa.rlib"
  = note: lld: error: libcore-d722c004db30ce6b.rlib(core-d722c004db30ce6b.core14-11a635b2ecf559281af1ed28854a3d65.rs.rcgu.o): undefined symbol: cosf
          lld: error: libcore-d722c004db30ce6b.rlib(core-d722c004db30ce6b.core14-11a635b2ecf559281af1ed28854a3d65.rs.rcgu.o): undefined symbol: cos
          lld: error: libcore-d722c004db30ce6b.rlib(core-d722c004db30ce6b.core14-11a635b2ecf559281af1ed28854a3d65.rs.rcgu.o): undefined symbol: sinf
          lld: error: libcore-d722c004db30ce6b.rlib(core-d722c004db30ce6b.core14-11a635b2ecf559281af1ed28854a3d65.rs.rcgu.o): undefined symbol: sin
          

error: aborting due to previous error

error: Could not compile `uefi-test-runner`.                                       

To learn more, run the command again with --verbose.
Subprocess xargo exited with error code 101

Interestingly, it also required me to actually install lld with my package manager, whereas before it just seemed to work (apparently without it?). I'm not sure what is causing this, but I'm having the same issue with my project as well now, since I use the same target spec.

Study alternate designs for warnings

As discussed in #51 , I think EFI warnings should be exposed as part of regular results, rather than as errors. We should, however, make them must_use to hint the user into checking them.

Another option would be to replace core::Result with a more elaborate enum that uses ternary logic:

enum EfiResult<T> {
    Ok(T),
    Warn(T, Status),
    Err(Status)
}

But we would then need to duplicate a small amount of Result code and API and to think about how this will integrate with the Try (?) operator. I think a promising avenue for the latter would be to print a warning to the logs as part of into_result.

In any case, a problem to be resolved is that EFI purposely does not define which operations may throw a warning and which may not. So any solution must be generalizable to the whole EFI API surface with minimal boilerplate.

Run tests in CI

  • Since commit 4148e36 we now build the code on Travis.

  • Thanks to PR #19, QEMU will exit with a return code on a panic.

  • Using -serial stdio makes QEMU redirect all output to the console.

  • -nographic hides the QEMU window.

Things left to do:

  • Install QEMU in Travis
  • Print the failed tests / the panic message
  • Run the tests with QEMU in Travis

Improve ergonomics of defining GUIDs

A lot of UEFI interfaces / objects rely on GUIDs for identification, but they are long and unwieldy. This issue is for tracking improvements to the way we encode GUIDs in our code.

Current solution:

  • The GUIDs are encoded as associated constants, either directly or using a macro

Ideas:

FileMode and FileAttribute not exported

Use euclid for graphics types

The euclid crate provides useful 2D graphics structures like Point or Rectangle. I wanted to use it for GOP when I first implemented it, but it wasn't working in bare metal environments, so the API currently uses tuples.

Since then I got a PR merged for adding no_std support, but I forgot about it, and I think it should be possible to use it now.

Add rustfmt build command + associated check in the CI

Not every editor has good integration with rustfmt, and even when it's there it's often not enabled by default. So it sounds like a good idea to add a formatting check to Travis and provide an easy way to fix the formatting locally when the check fails.

Custom macro for UEFI entrypoint

Currently, the user has to manually define a uefi_start executable entry point, and the Rust compiler does no type checking to ensure it is correct.

It would be safer to have a custom attribute like #[uefi_entry] or similar which would use a procedural macro to check the types of the entry point.

Does OutputWriter belong in uefi-exts?

I think the usefulness of OutputWriter extends beyond just uefi-logger: anyone who uses the SystemTable's stdout is likely to eventually need that kind of functionality.

Therefore, mind if I move it to uefi-exts and make uefi-logger depend on that?

Linker error caused by `info!(...)` macro.

Recently tried to build the crate, it seems newer versions of Rust / LLD break the build.

The warning / error is as follows:

lld: warning: libtests.a(tests.rcgu.o): undefined symbol: ___chkstk_ms
error: link failed

I've narrowed down the issue to the usage of the info! macro for logging:

info!("UEFI {}.{}.{}", major, minor / 10, minor % 10);

The issue might somehow be related to stack checks, considering the name of the missing function name.

FileSystemInfoHeader is misalligned

Per the EDK2 source for EFI_FILE_SYSTEM_INFO, our FileSystemInfo struct should have the following layout (as a rust structure):

#[repr(C)]
pub struct FileSystemInfo {
    size: u64,
    read_only: bool,
    volume_size: u64,
    free_space: u64,
    block_size: u32,
    name: [Char16],
}

However, as the uefi-rs code uses separate structs for the header and name components. This causes the name field to be misalligned, and the unsafe pointer arithmetic in the module to be wrong.

The result of this is that using volume_label() field with FileSystemInfo can perform an out of bounds read if the length is empty (unfortunately this is the default for mkfs.fat), and it always returns an incorrect value for the label. Tested and confirmed on qemu-edk2 and a real system.

Maybe uefi::Result should allow for custom errors

Providing extra data about an error has been needed a couple of times now. Often, the error data is only available for specific error types, so it should be optional to fill it out for other error types. It would be nice if we didn't need to fallback to core::result::Result whenever that happens, because in doing so we lose ergonomics conveniences, treat warnings as errors, and confuse users by giving them two different Result types.

Here are some rough ideas:

// Directly maps the semantics, but implementing a custom error requires work
type Result<T, E: From<Status>> = core::result::Result<Completion<T>, E>;

// We could ease this work by providing something like this
impl<ErrData> From<Status> for (Status, Option<ErrData>) { ... }

// Or, if we feel the Option will always be good enough, flatten the abstraction
type Result<T, ErrData> = core::result::Result<Completion<T>, (Status, Option<ErrData>)>;

// The question is, do we sometimes want this, which is closest to today's practice ?
type Result<T, ErrData: Default> = core::result::Result<Completion<T>, (Status, ErrData)>;

Tag a release

I'd like to package a released version for Arch Linux. We can also do a -git package, but I'd also like to maintain a package with the stable release.

Let's discuss UEFI's pointer conventions

The "Calling conventions" section of the the "Overview" chapter of the UEFI spec starts with an interesting read about what can be expected of UEFI when passing pointers to the API, which may or may not influence our FFI interface (the "extern" functions that we use internally), unsafe APIs (those that ingest pointers), and what we consider to be safe APIs.

This is from page 20 of UEFI spec 2.7A, with numbering added to ease discussion:

When passing pointer arguments to Boot Services, Runtime Services, and Protocol Interfaces, the
caller has the following responsibilities:

  1. It is the caller’s responsibility to pass pointer parameters that reference physical memory
    locations. If a pointer is passed that does not point to a physical memory location (i.e., a
    memory mapped I/O region), the results are unpredictable and the system may halt.
  2. It is the caller’s responsibility to pass pointer parameters with correct alignment. If an
    unaligned pointer is passed to a function, the results are unpredictable and the system may
    halt.
  3. It is the caller’s responsibility to not pass in a NULL parameter to a function unless it is explicitly
    allowed. If a NULL pointer is passed to a function, the results are unpredictable and the system
    may hang.
  4. Unless otherwise stated, a caller should not make any assumptions regarding the state of
    pointer parameters if the function returns with an error.

Requirement 2 (correct alignment) is in line with Unsafe Rust's normal expectations: unaligned pointers are very special in Rust, and may only be used when one has special permission to do so (here, the answer is "never in the public API"). So we don't need to do anything about it.

Requirement 3 (no NULLs unless given permission) is also common in Unsafe Rust. If we wanted to encode the non-nullness requirement in the type system, we could use NonNulls (which are repr(transparent) and therefore ABI-compatible with C pointers), but a lot of unsafe Rust APIs don't bother and we probably don't need to either.

Requirement 1 (only physical memory, no MMIO and such) is where things become more interesting. The concern is very specific to low-level code, and this is not a normal expectation of an unsafe Rust API. It is also prohibitively expensive to check in software (you basically need to take a memory map and walk through it). Therefore, it could be a contract which we want to expose in the FFI, or in unsafe APIs that consume pointers like memmove/memset. Since every UEFI entry point is concerned by this contract, a way to move it into the API would be to take inspiration from NonNull and build our own wrapper type (e.g. "EfiPtr") which encodes this requirement.

Finally, requirement 4 (EFI may freely garble pointer parameters on error) is I think the most interesting from the perspective of Rust's safety guarantees. A pessimistic interpretation of this sentence would mean that every EFI function is unsafe with an unknown contract, since an out-of-bounds pointer access can corrupt everything, and so the only thing to do on error would be to abort immediately. Not something very pleasant to build upon. But if we disregard the spec's advice and make the IMO reasonable assumption that all invalid pointer accesses during erronerous execution will be in bounds, then it becomes something that we can build safe APIs upon. The only thing we need to take care of is that any function which takes as input a mutable pointer parameter to a type which has safety invariants must be kept unsafe with a "may corrupt input on error" contract.

I'll try to see if I can encode these requirements in the unsafe APIs and check the safe APIs against them somehow.

Tests for the file system protocols

Currently, we have no test coverage for the file system related protocols.

@HadrienG2 on #54:

To prevent filesystem tests from having a long-lasting impact on the testing environment (even if there's a bug in them), it would be good to have them operate on a throwaway ramdisk which QEMU discards at the end.

CI improvements

This issue tracks some extra nice-to-have features we could add to CI:

  • Test other targets, such as UEFI for AArch64 Implemented in 5bcf9a2.

  • Some nightlies lack certain binaries, meaning we sometimes have to wait a few days before CI works again. Should be possible to fall back to a slightly older nightly toolchain in those cases. Doesn't seem to be an issue anymore.

UB caused by Status enum

The status codes returned on error are currently defined as an enum, but as @HadrienG2 pointed out, UEFI functions are allowed to return implementation-specific error codes, or error codes not listed in a function's specification. This could trigger undefined behavior.

Revisit decision to ignore I/O errors in logging

In the face of a VirtualBox UEFI implementation bug, #122 had to pick the lesser of two poisons. But silently dropping logs is in general a very dangerous thing to do in low-level settings like OS bootloaders, where logs are often the only available information for failure diagnosis.

Therefore, we may want to revisit this design later on, as uefi-rs matures and starts being used for production-grade applications in addition to being a plaform for general Rust UEFI experimentation.

As a first step, I've opened rust-lang/log#382 to discuss whether the log crate itself should provide support for faillible logging.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.