Skip to content

Commit 217ad0c

Browse files
committed
Stdio for UEFI
- Uses Simple Text Output Protocol and Simple Text Input Protocol - Reading is done one character at a time - Writing is done with max 4096 characters Signed-off-by: Ayush Singh <[email protected]>
1 parent af68593 commit 217ad0c

File tree

3 files changed

+163
-2
lines changed

3 files changed

+163
-2
lines changed

library/std/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@
259259
all(target_vendor = "fortanix", target_env = "sgx"),
260260
feature(slice_index_methods, coerce_unsized, sgx_platform)
261261
)]
262-
#![cfg_attr(windows, feature(round_char_boundary))]
262+
#![cfg_attr(any(windows, target_os = "uefi"), feature(round_char_boundary))]
263263
#![cfg_attr(target_os = "xous", feature(slice_ptr_len))]
264264
//
265265
// Language features:

library/std/src/sys/uefi/mod.rs

-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ pub mod path;
3636
pub mod pipe;
3737
#[path = "../unsupported/process.rs"]
3838
pub mod process;
39-
#[path = "../unsupported/stdio.rs"]
4039
pub mod stdio;
4140
#[path = "../unsupported/thread.rs"]
4241
pub mod thread;

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

+162
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
use crate::io;
2+
use crate::iter::Iterator;
3+
use crate::mem::MaybeUninit;
4+
use crate::os::uefi;
5+
use crate::ptr::NonNull;
6+
7+
const MAX_BUFFER_SIZE: usize = 8192;
8+
9+
pub struct Stdin;
10+
pub struct Stdout;
11+
pub struct Stderr;
12+
13+
impl Stdin {
14+
pub const fn new() -> Stdin {
15+
Stdin
16+
}
17+
}
18+
19+
impl io::Read for Stdin {
20+
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
21+
let st: NonNull<r_efi::efi::SystemTable> = uefi::env::system_table().cast();
22+
let stdin = unsafe { (*st.as_ptr()).con_in };
23+
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()))?
31+
}
32+
Err(e) => {
33+
return Err(io::Error::from_raw_os_error(e.as_usize()));
34+
}
35+
};
36+
// SAFETY: Iterator will have only 1 character.
37+
// SAFETY: This character will always be UCS-2 and thus no surrogates.
38+
let ch: char = char::decode_utf16([inp]).next().unwrap().unwrap();
39+
if ch.len_utf8() > buf.len() {
40+
return Ok(0);
41+
}
42+
43+
ch.encode_utf8(buf);
44+
reset(stdin)?;
45+
46+
Ok(ch.len_utf8())
47+
}
48+
}
49+
50+
impl Stdout {
51+
pub const fn new() -> Stdout {
52+
Stdout
53+
}
54+
}
55+
56+
impl io::Write for Stdout {
57+
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
58+
let st: NonNull<r_efi::efi::SystemTable> = uefi::env::system_table().cast();
59+
let stdout = unsafe { (*st.as_ptr()).con_out };
60+
61+
write(stdout, buf)
62+
}
63+
64+
fn flush(&mut self) -> io::Result<()> {
65+
Ok(())
66+
}
67+
}
68+
69+
impl Stderr {
70+
pub const fn new() -> Stderr {
71+
Stderr
72+
}
73+
}
74+
75+
impl io::Write for Stderr {
76+
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
77+
let st: NonNull<r_efi::efi::SystemTable> = uefi::env::system_table().cast();
78+
let stderr = unsafe { (*st.as_ptr()).std_err };
79+
80+
write(stderr, buf)
81+
}
82+
83+
fn flush(&mut self) -> io::Result<()> {
84+
Ok(())
85+
}
86+
}
87+
88+
// UCS-2 character should occupy 3 bytes at most in UTF-8
89+
pub const STDIN_BUF_SIZE: usize = 3;
90+
91+
pub fn is_ebadf(_err: &io::Error) -> bool {
92+
true
93+
}
94+
95+
pub fn panic_output() -> Option<impl io::Write> {
96+
uefi::env::try_system_table().map(|_| Stderr::new())
97+
}
98+
99+
fn write(
100+
protocol: *mut r_efi::protocols::simple_text_output::Protocol,
101+
buf: &[u8],
102+
) -> io::Result<usize> {
103+
let mut utf16 = [0; MAX_BUFFER_SIZE / 2];
104+
105+
// Get valid UTF-8 buffer
106+
let utf8 = match crate::str::from_utf8(buf) {
107+
Ok(x) => x,
108+
Err(e) => unsafe { crate::str::from_utf8_unchecked(&buf[..e.valid_up_to()]) },
109+
};
110+
// Clip UTF-8 buffer to max UTF-16 buffer we support
111+
let utf8 = &utf8[..utf8.floor_char_boundary(utf16.len() - 1)];
112+
113+
for (i, ch) in utf8.encode_utf16().enumerate() {
114+
utf16[i] = ch;
115+
}
116+
117+
unsafe { simple_text_output(protocol, &mut utf16) }?;
118+
119+
Ok(utf8.len())
120+
}
121+
122+
unsafe fn simple_text_output(
123+
protocol: *mut r_efi::protocols::simple_text_output::Protocol,
124+
buf: &mut [u16],
125+
) -> io::Result<()> {
126+
let res = unsafe { ((*protocol).output_string)(protocol, buf.as_mut_ptr()) };
127+
if res.is_error() { Err(io::Error::from_raw_os_error(res.as_usize())) } else { Ok(()) }
128+
}
129+
130+
fn wait_stdin(stdin: *mut r_efi::protocols::simple_text_input::Protocol) -> io::Result<()> {
131+
let boot_services: NonNull<r_efi::efi::BootServices> =
132+
uefi::env::boot_services().unwrap().cast();
133+
let wait_for_event = unsafe { (*boot_services.as_ptr()).wait_for_event };
134+
let wait_for_key_event = unsafe { (*stdin).wait_for_key };
135+
136+
let r = {
137+
let mut x: usize = 0;
138+
(wait_for_event)(1, [wait_for_key_event].as_mut_ptr(), &mut x)
139+
};
140+
if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) }
141+
}
142+
143+
fn reset(stdin: *mut r_efi::protocols::simple_text_input::Protocol) -> io::Result<()> {
144+
let r = unsafe { ((*stdin).reset)(stdin, r_efi::efi::Boolean::FALSE) };
145+
if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) }
146+
}
147+
148+
fn read_key_stroke(
149+
stdin: *mut r_efi::protocols::simple_text_input::Protocol,
150+
) -> Result<u16, r_efi::efi::Status> {
151+
let mut input_key: MaybeUninit<r_efi::protocols::simple_text_input::InputKey> =
152+
MaybeUninit::uninit();
153+
154+
let r = unsafe { ((*stdin).read_key_stroke)(stdin, input_key.as_mut_ptr()) };
155+
156+
if r.is_error() {
157+
Err(r)
158+
} else {
159+
let input_key = unsafe { input_key.assume_init() };
160+
Ok(input_key.unicode_char)
161+
}
162+
}

0 commit comments

Comments
 (0)