Skip to content

Commit 1373a2f

Browse files
authored
Rollup merge of #139517 - Ayush1325:uefi-cmd-stdin-null, r=joboet
std: sys: process: uefi: Use NULL stdin by default According to the docs in `Command::output`: > By default, stdout and stderr are captured (and used to provide the resulting output). Stdin is not inherited from the parent and any attempt by the child process to read from the stdin stream will result in the stream immediately closing. This was being violated by UEFI which was inheriting stdin by default. While the docs don't explicitly state that the default should be NULL, the behaviour seems like reading from NULL. UEFI however, has a bit of a problem. The `EFI_SIMPLE_TEXT_INPUT_PROTOCOL` only provides support for reading 1 key press. This means that you either get an error, or it is assumed that the keypress was read successfully. So there is no way to have a successful read of length 0. Currently, I am returning UNSUPPORTED error when trying to read from NULL stdin. On linux however, you will get a read of length 0 for Null stdin. One possible way to get around this is to translate one of the UEFI errors to a read 0 (Maybe unsupported?). It is also possible to have a non-standard error code, but well, not sure if we go that route. Alternatively, if meaning of Stdio::Null is platform dependent, it should be fine to keep the current behaviour of returning an error. cc `@nicholasbishop` `@dvdhrm`
2 parents e2d5382 + d994fef commit 1373a2f

File tree

2 files changed

+120
-7
lines changed

2 files changed

+120
-7
lines changed

Diff for: library/std/src/sys/process/uefi.rs

+114-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use r_efi::protocols::simple_text_output;
1+
use r_efi::protocols::{simple_text_input, simple_text_output};
22

33
use crate::collections::BTreeMap;
44
pub use crate::ffi::OsString as EnvKey;
@@ -23,6 +23,7 @@ pub struct Command {
2323
args: Vec<OsString>,
2424
stdout: Option<Stdio>,
2525
stderr: Option<Stdio>,
26+
stdin: Option<Stdio>,
2627
env: CommandEnv,
2728
}
2829

@@ -48,6 +49,7 @@ impl Command {
4849
args: Vec::new(),
4950
stdout: None,
5051
stderr: None,
52+
stdin: None,
5153
env: Default::default(),
5254
}
5355
}
@@ -64,8 +66,8 @@ impl Command {
6466
panic!("unsupported")
6567
}
6668

67-
pub fn stdin(&mut self, _stdin: Stdio) {
68-
panic!("unsupported")
69+
pub fn stdin(&mut self, stdin: Stdio) {
70+
self.stdin = Some(stdin);
6971
}
7072

7173
pub fn stdout(&mut self, stdout: Stdio) {
@@ -122,6 +124,22 @@ impl Command {
122124
}
123125
}
124126

127+
fn create_stdin(
128+
s: Stdio,
129+
) -> io::Result<Option<helpers::OwnedProtocol<uefi_command_internal::InputProtocol>>> {
130+
match s {
131+
Stdio::Null => unsafe {
132+
helpers::OwnedProtocol::create(
133+
uefi_command_internal::InputProtocol::null(),
134+
simple_text_input::PROTOCOL_GUID,
135+
)
136+
}
137+
.map(Some),
138+
Stdio::Inherit => Ok(None),
139+
Stdio::MakePipe => unsupported(),
140+
}
141+
}
142+
125143
pub fn output(&mut self) -> io::Result<(ExitStatus, Vec<u8>, Vec<u8>)> {
126144
let mut cmd = uefi_command_internal::Image::load_image(&self.prog)?;
127145

@@ -149,6 +167,15 @@ impl Command {
149167
cmd.stderr_inherit()
150168
};
151169

170+
// Setup Stdin
171+
let stdin = self.stdin.unwrap_or(Stdio::Null);
172+
let stdin = Self::create_stdin(stdin)?;
173+
if let Some(con) = stdin {
174+
cmd.stdin_init(con)
175+
} else {
176+
cmd.stdin_inherit()
177+
};
178+
152179
let env = env_changes(&self.env);
153180

154181
// Set any new vars
@@ -334,7 +361,7 @@ impl<'a> fmt::Debug for CommandArgs<'a> {
334361

335362
#[allow(dead_code)]
336363
mod uefi_command_internal {
337-
use r_efi::protocols::{loaded_image, simple_text_output};
364+
use r_efi::protocols::{loaded_image, simple_text_input, simple_text_output};
338365

339366
use crate::ffi::{OsStr, OsString};
340367
use crate::io::{self, const_error};
@@ -350,6 +377,7 @@ mod uefi_command_internal {
350377
handle: NonNull<crate::ffi::c_void>,
351378
stdout: Option<helpers::OwnedProtocol<PipeProtocol>>,
352379
stderr: Option<helpers::OwnedProtocol<PipeProtocol>>,
380+
stdin: Option<helpers::OwnedProtocol<InputProtocol>>,
353381
st: OwnedTable<r_efi::efi::SystemTable>,
354382
args: Option<(*mut u16, usize)>,
355383
}
@@ -384,7 +412,14 @@ mod uefi_command_internal {
384412
helpers::open_protocol(child_handle, loaded_image::PROTOCOL_GUID).unwrap();
385413
let st = OwnedTable::from_table(unsafe { (*loaded_image.as_ptr()).system_table });
386414

387-
Ok(Self { handle: child_handle, stdout: None, stderr: None, st, args: None })
415+
Ok(Self {
416+
handle: child_handle,
417+
stdout: None,
418+
stderr: None,
419+
stdin: None,
420+
st,
421+
args: None,
422+
})
388423
}
389424
}
390425

@@ -445,6 +480,17 @@ mod uefi_command_internal {
445480
}
446481
}
447482

483+
fn set_stdin(
484+
&mut self,
485+
handle: r_efi::efi::Handle,
486+
protocol: *mut simple_text_input::Protocol,
487+
) {
488+
unsafe {
489+
(*self.st.as_mut_ptr()).console_in_handle = handle;
490+
(*self.st.as_mut_ptr()).con_in = protocol;
491+
}
492+
}
493+
448494
pub fn stdout_init(&mut self, protocol: helpers::OwnedProtocol<PipeProtocol>) {
449495
self.set_stdout(
450496
protocol.handle().as_ptr(),
@@ -471,6 +517,19 @@ mod uefi_command_internal {
471517
unsafe { self.set_stderr((*st.as_ptr()).standard_error_handle, (*st.as_ptr()).std_err) }
472518
}
473519

520+
pub(crate) fn stdin_init(&mut self, protocol: helpers::OwnedProtocol<InputProtocol>) {
521+
self.set_stdin(
522+
protocol.handle().as_ptr(),
523+
protocol.as_ref() as *const InputProtocol as *mut simple_text_input::Protocol,
524+
);
525+
self.stdin = Some(protocol);
526+
}
527+
528+
pub(crate) fn stdin_inherit(&mut self) {
529+
let st: NonNull<r_efi::efi::SystemTable> = system_table().cast();
530+
unsafe { self.set_stdin((*st.as_ptr()).console_in_handle, (*st.as_ptr()).con_in) }
531+
}
532+
474533
pub fn stderr(&self) -> io::Result<Vec<u8>> {
475534
match &self.stderr {
476535
Some(stderr) => stderr.as_ref().utf8(),
@@ -722,6 +781,56 @@ mod uefi_command_internal {
722781
}
723782
}
724783

784+
#[repr(C)]
785+
pub(crate) struct InputProtocol {
786+
reset: simple_text_input::ProtocolReset,
787+
read_key_stroke: simple_text_input::ProtocolReadKeyStroke,
788+
wait_for_key: r_efi::efi::Event,
789+
}
790+
791+
impl InputProtocol {
792+
pub(crate) fn null() -> Self {
793+
let evt = helpers::OwnedEvent::new(
794+
r_efi::efi::EVT_NOTIFY_WAIT,
795+
r_efi::efi::TPL_CALLBACK,
796+
Some(Self::empty_notify),
797+
None,
798+
)
799+
.unwrap();
800+
801+
Self {
802+
reset: Self::null_reset,
803+
read_key_stroke: Self::null_read_key,
804+
wait_for_key: evt.into_raw(),
805+
}
806+
}
807+
808+
extern "efiapi" fn null_reset(
809+
_: *mut simple_text_input::Protocol,
810+
_: r_efi::efi::Boolean,
811+
) -> r_efi::efi::Status {
812+
r_efi::efi::Status::SUCCESS
813+
}
814+
815+
extern "efiapi" fn null_read_key(
816+
_: *mut simple_text_input::Protocol,
817+
_: *mut simple_text_input::InputKey,
818+
) -> r_efi::efi::Status {
819+
r_efi::efi::Status::UNSUPPORTED
820+
}
821+
822+
extern "efiapi" fn empty_notify(_: r_efi::efi::Event, _: *mut crate::ffi::c_void) {}
823+
}
824+
825+
impl Drop for InputProtocol {
826+
fn drop(&mut self) {
827+
// Close wait_for_key
828+
unsafe {
829+
let _ = helpers::OwnedEvent::from_raw(self.wait_for_key);
830+
}
831+
}
832+
}
833+
725834
pub fn create_args(prog: &OsStr, args: &[OsString]) -> Box<[u16]> {
726835
const QUOTE: u16 = 0x0022;
727836
const SPACE: u16 = 0x0020;

Diff for: library/std/src/sys/stdio/uefi.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,12 @@ impl io::Write for Stderr {
142142
// UTF-16 character should occupy 4 bytes at most in UTF-8
143143
pub const STDIN_BUF_SIZE: usize = 4;
144144

145-
pub fn is_ebadf(_err: &io::Error) -> bool {
146-
false
145+
pub fn is_ebadf(err: &io::Error) -> bool {
146+
if let Some(x) = err.raw_os_error() {
147+
r_efi::efi::Status::UNSUPPORTED.as_usize() == x
148+
} else {
149+
false
150+
}
147151
}
148152

149153
pub fn panic_output() -> Option<impl io::Write> {

0 commit comments

Comments
 (0)