Skip to content

Commit 07000dc

Browse files
committed
Improve UEFI stdio
- Do not drop any character while reading - eabdf == Unsupported status - loop untill read character or error encountered Signed-off-by: Ayush Singh <[email protected]>
1 parent 32ec40c commit 07000dc

File tree

1 file changed

+36
-20
lines changed

1 file changed

+36
-20
lines changed

library/std/src/sys/pal/uefi/stdio.rs

+36-20
Original file line numberDiff line numberDiff line change
@@ -6,43 +6,49 @@ use crate::ptr::NonNull;
66

77
const MAX_BUFFER_SIZE: usize = 8192;
88

9-
pub struct Stdin;
9+
pub struct Stdin {
10+
pending: Option<char>,
11+
}
12+
1013
pub struct Stdout;
1114
pub struct Stderr;
1215

1316
impl Stdin {
1417
pub const fn new() -> Stdin {
15-
Stdin
18+
Stdin { pending: None }
1619
}
1720
}
1821

1922
impl io::Read for Stdin {
20-
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
23+
fn read(&mut self, mut buf: &mut [u8]) -> io::Result<usize> {
2124
let st: NonNull<r_efi::efi::SystemTable> = uefi::env::system_table().cast();
2225
let stdin = unsafe { (*st.as_ptr()).con_in };
2326

24-
// Try reading any pending data
25-
let inp = match read_key_stroke(stdin) {
26-
Ok(x) => x,
27-
Err(e) if e == r_efi::efi::Status::NOT_READY => {
28-
// Wait for keypress for new data
29-
wait_stdin(stdin)?;
30-
read_key_stroke(stdin).map_err(|x| io::Error::from_raw_os_error(x.as_usize()))?
27+
// Write any pending character
28+
if let Some(ch) = self.pending {
29+
if ch.len_utf8() > buf.len() {
30+
return Ok(0);
3131
}
32-
Err(e) => {
33-
return Err(io::Error::from_raw_os_error(e.as_usize()));
34-
}
35-
};
32+
ch.encode_utf8(buf);
33+
buf = &mut buf[ch.len_utf8()..];
34+
self.pending = None;
35+
}
36+
37+
// Try reading any pending data
38+
let inp = read(stdin)?;
3639

3740
// Check if the key is printiable character
38-
if inp.scan_code != 0x00 {
41+
if inp == 0x00 {
3942
return Err(io::const_io_error!(io::ErrorKind::Interrupted, "Special Key Press"));
4043
}
4144

42-
// SAFETY: Iterator will have only 1 character since we are reading only 1 Key
43-
// SAFETY: This character will always be UCS-2 and thus no surrogates.
44-
let ch: char = char::decode_utf16([inp.unicode_char]).next().unwrap().unwrap();
45+
// The option unwrap is safe since iterator will have 1 element.
46+
let ch: char = char::decode_utf16([inp])
47+
.next()
48+
.unwrap()
49+
.map_err(|_| io::const_io_error!(io::ErrorKind::InvalidInput, "Invalid Input"))?;
4550
if ch.len_utf8() > buf.len() {
51+
self.pending = Some(ch);
4652
return Ok(0);
4753
}
4854

@@ -93,8 +99,8 @@ impl io::Write for Stderr {
9399
// UCS-2 character should occupy 3 bytes at most in UTF-8
94100
pub const STDIN_BUF_SIZE: usize = 3;
95101

96-
pub fn is_ebadf(_err: &io::Error) -> bool {
97-
true
102+
pub fn is_ebadf(err: &io::Error) -> bool {
103+
err.raw_os_error() == Some(r_efi::efi::Status::UNSUPPORTED.as_usize())
98104
}
99105

100106
pub fn panic_output() -> Option<impl io::Write> {
@@ -132,6 +138,16 @@ unsafe fn simple_text_output(
132138
if res.is_error() { Err(io::Error::from_raw_os_error(res.as_usize())) } else { Ok(()) }
133139
}
134140

141+
fn read(stdin: *mut r_efi::protocols::simple_text_input::Protocol) -> io::Result<u16> {
142+
loop {
143+
match read_key_stroke(stdin) {
144+
Ok(x) => return Ok(x.unicode_char),
145+
Err(e) if e == r_efi::efi::Status::NOT_READY => wait_stdin(stdin)?,
146+
Err(e) => return Err(io::Error::from_raw_os_error(e.as_usize())),
147+
}
148+
}
149+
}
150+
135151
fn wait_stdin(stdin: *mut r_efi::protocols::simple_text_input::Protocol) -> io::Result<()> {
136152
let boot_services: NonNull<r_efi::efi::BootServices> =
137153
uefi::env::boot_services().unwrap().cast();

0 commit comments

Comments
 (0)