rust-osdev/uefi-rs

Question on how to access to stdin, stdout (and stderr) since 0.32

YtvwlD opened this issue · 4 comments

YtvwlD commented

Hi, I'm trying to display a menu. With older versions of uefi-rs, I'd have a function

pub fn choose<'a>(config: &'a Config, systab: &mut SystemTable<Boot>) -> &'a Entry

that accesses systab.stdin() and systab.stdout(). Since that is now deprecated, how would I change this?
The migration document doesn't mention stdin or stdout.

I've thought of this:

pub fn choose<'a>(config: &'a Config, stdin: &mut Input, stdout: &mut Output) -> &'a Entry

but I can't call it with:

let entry_to_boot = with_stdin(|stdin| with_stdout(
    |stdout| menu::choose(&config, stdin, stdout)
));

as this produces the following error:

error[E0596]: cannot borrow `*stdin` as mutable, as it is a captured variable in a `Fn` closure
   --> src/main.rs:117:40
    |
31  | fn efi_main(image: Handle, mut systab: SystemTable<Boot>) -> Status {
    |    --------                                                  ------ change this to return `FnMut` instead of `Fn`
...
117 |         |stdout| menu::choose(&config, stdin, stdout)
    |         -------- in this closure       ^^^^^ cannot borrow as mutable

Do I have to put the calls to with_stdin and with_stdout inside the function?

Good question, thanks for pointing that out. I don't have capacity to look into this in the next days, but perhaps @nicholasbishop can work on it?

Good question indeed, the API here is not making what you want to do as easy as it should be.

I think the simplest way forward for now is to manually stash pointers. Here's an example that compiles:

#![no_std]
#![no_main]

use uefi::proto::console::text::{Output, Input};
use uefi::{Status, entry, system};
use core::ptr;

pub fn choose(_stdin: &mut Input, _stdout: &mut Output) {
    unimplemented!();
}

#[entry]
fn main() -> Status {
    let stdin: *mut Input = system::with_stdin(|stdin| ptr::from_mut(stdin));
    let stdout: *mut Output = system::with_stdout(|stdout| ptr::from_mut(stdout));

    // Safety: in the UEFI spec, section
    // EFI_BOOT_SERVICES.UninstallProtocolInterface(), it says that
    // console I/O protocols can never be removed. So these pointers are
    // always valid, and within this block there is no shared mutable
    // reference to stdin or stdout, so Rust's aliasing guarantees are
    // maintained.
    { 
        let stdin = unsafe { &mut *stdin };
        let stdout = unsafe { &mut *stdout };

        choose(stdin, stdout);
    }

    Status::SUCCESS
}

This is not ideal though, we should think about this API some more.

YtvwlD commented

Can f be FnMut? Would that work?

(This also applies to with_config_table.)

How now system::with_std(|stdin, stdout|)?