-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- 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]>
- Loading branch information
Showing
3 changed files
with
163 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
use crate::io; | ||
use crate::iter::Iterator; | ||
use crate::mem::MaybeUninit; | ||
use crate::os::uefi; | ||
use crate::ptr::NonNull; | ||
|
||
const MAX_BUFFER_SIZE: usize = 8192; | ||
|
||
pub struct Stdin; | ||
pub struct Stdout; | ||
pub struct Stderr; | ||
|
||
impl Stdin { | ||
pub const fn new() -> Stdin { | ||
Stdin | ||
} | ||
} | ||
|
||
impl io::Read for Stdin { | ||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | ||
let st: NonNull<r_efi::efi::SystemTable> = uefi::env::system_table().cast(); | ||
let stdin = unsafe { (*st.as_ptr()).con_in }; | ||
|
||
// Try reading any pending data | ||
let inp = match read_key_stroke(stdin) { | ||
Ok(x) => x, | ||
Err(e) if e == r_efi::efi::Status::NOT_READY => { | ||
// Wait for keypress for new data | ||
wait_stdin(stdin)?; | ||
read_key_stroke(stdin).map_err(|x| io::Error::from_raw_os_error(x.as_usize()))? | ||
} | ||
Err(e) => { | ||
return Err(io::Error::from_raw_os_error(e.as_usize())); | ||
} | ||
}; | ||
// SAFETY: Iterator will have only 1 character. | ||
// SAFETY: This character will always be UCS-2 and thus no surrogates. | ||
let ch: char = char::decode_utf16([inp]).next().unwrap().unwrap(); | ||
if ch.len_utf8() > buf.len() { | ||
return Ok(0); | ||
} | ||
|
||
ch.encode_utf8(buf); | ||
reset(stdin)?; | ||
|
||
Ok(ch.len_utf8()) | ||
} | ||
} | ||
|
||
impl Stdout { | ||
pub const fn new() -> Stdout { | ||
Stdout | ||
} | ||
} | ||
|
||
impl io::Write for Stdout { | ||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> { | ||
let st: NonNull<r_efi::efi::SystemTable> = uefi::env::system_table().cast(); | ||
let stdout = unsafe { (*st.as_ptr()).con_out }; | ||
|
||
write(stdout, buf) | ||
} | ||
|
||
fn flush(&mut self) -> io::Result<()> { | ||
Ok(()) | ||
} | ||
} | ||
|
||
impl Stderr { | ||
pub const fn new() -> Stderr { | ||
Stderr | ||
} | ||
} | ||
|
||
impl io::Write for Stderr { | ||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> { | ||
let st: NonNull<r_efi::efi::SystemTable> = uefi::env::system_table().cast(); | ||
let stderr = unsafe { (*st.as_ptr()).std_err }; | ||
|
||
write(stderr, buf) | ||
} | ||
|
||
fn flush(&mut self) -> io::Result<()> { | ||
Ok(()) | ||
} | ||
} | ||
|
||
// UCS-2 character should occupy 3 bytes at most in UTF-8 | ||
pub const STDIN_BUF_SIZE: usize = 3; | ||
|
||
pub fn is_ebadf(_err: &io::Error) -> bool { | ||
true | ||
} | ||
|
||
pub fn panic_output() -> Option<impl io::Write> { | ||
uefi::env::try_system_table().map(|_| Stderr::new()) | ||
} | ||
|
||
fn write( | ||
protocol: *mut r_efi::protocols::simple_text_output::Protocol, | ||
buf: &[u8], | ||
) -> io::Result<usize> { | ||
let mut utf16 = [0; MAX_BUFFER_SIZE / 2]; | ||
|
||
// Get valid UTF-8 buffer | ||
let utf8 = match crate::str::from_utf8(buf) { | ||
Ok(x) => x, | ||
Err(e) => unsafe { crate::str::from_utf8_unchecked(&buf[..e.valid_up_to()]) }, | ||
}; | ||
// Clip UTF-8 buffer to max UTF-16 buffer we support | ||
let utf8 = &utf8[..utf8.floor_char_boundary(utf16.len() - 1)]; | ||
|
||
for (i, ch) in utf8.encode_utf16().enumerate() { | ||
utf16[i] = ch; | ||
} | ||
|
||
unsafe { simple_text_output(protocol, &mut utf16) }?; | ||
|
||
Ok(utf8.len()) | ||
} | ||
|
||
unsafe fn simple_text_output( | ||
protocol: *mut r_efi::protocols::simple_text_output::Protocol, | ||
buf: &mut [u16], | ||
) -> io::Result<()> { | ||
let res = unsafe { ((*protocol).output_string)(protocol, buf.as_mut_ptr()) }; | ||
if res.is_error() { Err(io::Error::from_raw_os_error(res.as_usize())) } else { Ok(()) } | ||
} | ||
|
||
fn wait_stdin(stdin: *mut r_efi::protocols::simple_text_input::Protocol) -> io::Result<()> { | ||
let boot_services: NonNull<r_efi::efi::BootServices> = | ||
uefi::env::boot_services().unwrap().cast(); | ||
let wait_for_event = unsafe { (*boot_services.as_ptr()).wait_for_event }; | ||
let wait_for_key_event = unsafe { (*stdin).wait_for_key }; | ||
|
||
let r = { | ||
let mut x: usize = 0; | ||
(wait_for_event)(1, [wait_for_key_event].as_mut_ptr(), &mut x) | ||
}; | ||
if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } | ||
} | ||
|
||
fn reset(stdin: *mut r_efi::protocols::simple_text_input::Protocol) -> io::Result<()> { | ||
let r = unsafe { ((*stdin).reset)(stdin, r_efi::efi::Boolean::FALSE) }; | ||
if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } | ||
} | ||
|
||
fn read_key_stroke( | ||
stdin: *mut r_efi::protocols::simple_text_input::Protocol, | ||
) -> Result<u16, r_efi::efi::Status> { | ||
let mut input_key: MaybeUninit<r_efi::protocols::simple_text_input::InputKey> = | ||
MaybeUninit::uninit(); | ||
|
||
let r = unsafe { ((*stdin).read_key_stroke)(stdin, input_key.as_mut_ptr()) }; | ||
|
||
if r.is_error() { | ||
Err(r) | ||
} else { | ||
let input_key = unsafe { input_key.assume_init() }; | ||
Ok(input_key.unicode_char) | ||
} | ||
} |