Skip to content

Commit b712e74

Browse files
committed
uefi: process: Add support to capture stdout
Use a custom simple_text_output protocol to capture output. Signed-off-by: Ayush Singh <[email protected]>
1 parent e6eeb4e commit b712e74

File tree

2 files changed

+258
-8
lines changed

2 files changed

+258
-8
lines changed

Diff for: std/src/sys/pal/uefi/helpers.rs

+74-2
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@
1212
use r_efi::efi::{self, Guid};
1313
use r_efi::protocols::{device_path, device_path_to_text};
1414

15-
use crate::ffi::{OsString, OsStr};
15+
use crate::ffi::{OsStr, OsString};
1616
use crate::io::{self, const_io_error};
1717
use crate::mem::{size_of, MaybeUninit};
18-
use crate::os::uefi::{self, env::boot_services, ffi::OsStringExt};
18+
use crate::os::uefi::{self, env::boot_services, ffi::OsStrExt, ffi::OsStringExt};
1919
use crate::ptr::NonNull;
2020
use crate::slice;
2121
use crate::sync::atomic::{AtomicPtr, Ordering};
@@ -291,3 +291,75 @@ impl Drop for DevicePath {
291291
}
292292
}
293293
}
294+
295+
pub(crate) struct Protocol<T> {
296+
guid: r_efi::efi::Guid,
297+
handle: NonNull<crate::ffi::c_void>,
298+
protocol: Box<T>,
299+
}
300+
301+
impl<T> Protocol<T> {
302+
const fn new(
303+
guid: r_efi::efi::Guid,
304+
handle: NonNull<crate::ffi::c_void>,
305+
protocol: Box<T>,
306+
) -> Self {
307+
Self { guid, handle, protocol }
308+
}
309+
310+
pub(crate) fn create(protocol: T, mut guid: r_efi::efi::Guid) -> io::Result<Self> {
311+
let boot_services: NonNull<r_efi::efi::BootServices> =
312+
boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?.cast();
313+
let mut protocol = Box::new(protocol);
314+
let mut handle: r_efi::efi::Handle = crate::ptr::null_mut();
315+
316+
let r = unsafe {
317+
((*boot_services.as_ptr()).install_protocol_interface)(
318+
&mut handle,
319+
&mut guid,
320+
r_efi::efi::NATIVE_INTERFACE,
321+
protocol.as_mut() as *mut T as *mut crate::ffi::c_void,
322+
)
323+
};
324+
325+
if r.is_error() {
326+
return Err(crate::io::Error::from_raw_os_error(r.as_usize()));
327+
};
328+
329+
let handle = NonNull::new(handle)
330+
.ok_or(io::const_io_error!(io::ErrorKind::Uncategorized, "found null handle"))?;
331+
332+
Ok(Self::new(guid, handle, protocol))
333+
}
334+
335+
pub(crate) fn handle(&self) -> NonNull<crate::ffi::c_void> {
336+
self.handle
337+
}
338+
}
339+
340+
impl<T> Drop for Protocol<T> {
341+
fn drop(&mut self) {
342+
if let Some(bt) = boot_services() {
343+
let bt: NonNull<r_efi::efi::BootServices> = bt.cast();
344+
unsafe {
345+
((*bt.as_ptr()).uninstall_protocol_interface)(
346+
self.handle.as_ptr(),
347+
&mut self.guid,
348+
self.protocol.as_mut() as *mut T as *mut crate::ffi::c_void,
349+
)
350+
};
351+
}
352+
}
353+
}
354+
355+
impl<T> AsRef<T> for Protocol<T> {
356+
fn as_ref(&self) -> &T {
357+
&self.protocol
358+
}
359+
}
360+
361+
impl<T> AsMut<T> for Protocol<T> {
362+
fn as_mut(&mut self) -> &mut T {
363+
&mut self.protocol
364+
}
365+
}

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

+184-6
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,11 @@ impl Command {
9191
}
9292

9393
pub fn output(&mut self) -> io::Result<(ExitStatus, Vec<u8>, Vec<u8>)> {
94-
let cmd = uefi_command_internal::Command::load_image(&self.prog)?;
94+
let mut cmd = uefi_command_internal::Command::load_image(&self.prog)?;
95+
cmd.stdout_init()?;
9596
let stat = cmd.start_image()?;
96-
Ok((ExitStatus(stat), Vec::new(), Vec::new()))
97+
let stdout = cmd.stdout()?;
98+
Ok((ExitStatus(stat), stdout, Vec::new()))
9799
}
98100
}
99101

@@ -246,20 +248,30 @@ impl<'a> fmt::Debug for CommandArgs<'a> {
246248
}
247249

248250
mod uefi_command_internal {
251+
use r_efi::protocols::{loaded_image, simple_text_output};
252+
249253
use super::super::helpers;
250-
use crate::ffi::OsStr;
254+
use crate::ffi::{OsStr, OsString};
251255
use crate::io::{self, const_io_error};
252256
use crate::mem::MaybeUninit;
253257
use crate::os::uefi::env::{boot_services, image_handle};
258+
use crate::os::uefi::ffi::OsStringExt;
254259
use crate::ptr::NonNull;
260+
use crate::slice;
261+
use crate::sys_common::wstr::WStrUnits;
255262

256263
pub struct Command {
257264
handle: NonNull<crate::ffi::c_void>,
265+
stdout: Option<helpers::Protocol<PipeProtocol>>,
266+
st: Box<r_efi::efi::SystemTable>,
258267
}
259268

260269
impl Command {
261-
const fn new(handle: NonNull<crate::ffi::c_void>) -> Self {
262-
Self { handle }
270+
const fn new(
271+
handle: NonNull<crate::ffi::c_void>,
272+
st: Box<r_efi::efi::SystemTable>,
273+
) -> Self {
274+
Self { handle, stdout: None, st }
263275
}
264276

265277
pub fn load_image(p: &OsStr) -> io::Result<Self> {
@@ -286,7 +298,17 @@ mod uefi_command_internal {
286298
} else {
287299
let child_handle = unsafe { child_handle.assume_init() };
288300
let child_handle = NonNull::new(child_handle).unwrap();
289-
Ok(Self::new(child_handle))
301+
302+
let loaded_image: NonNull<loaded_image::Protocol> =
303+
helpers::open_protocol(child_handle, loaded_image::PROTOCOL_GUID).unwrap();
304+
let mut st: Box<r_efi::efi::SystemTable> =
305+
Box::new(unsafe { crate::ptr::read((*loaded_image.as_ptr()).system_table) });
306+
307+
unsafe {
308+
(*loaded_image.as_ptr()).system_table = st.as_mut();
309+
}
310+
311+
Ok(Self::new(child_handle, st))
290312
}
291313
}
292314

@@ -313,6 +335,32 @@ mod uefi_command_internal {
313335

314336
Ok(r)
315337
}
338+
339+
pub fn stdout_init(&mut self) -> io::Result<()> {
340+
let mut protocol =
341+
helpers::Protocol::create(PipeProtocol::new(), simple_text_output::PROTOCOL_GUID)?;
342+
343+
self.st.console_out_handle = protocol.handle().as_ptr();
344+
self.st.con_out =
345+
protocol.as_mut() as *mut PipeProtocol as *mut simple_text_output::Protocol;
346+
347+
self.stdout = Some(protocol);
348+
349+
Ok(())
350+
}
351+
352+
pub fn stdout(&self) -> io::Result<Vec<u8>> {
353+
if let Some(stdout) = &self.stdout {
354+
stdout
355+
.as_ref()
356+
.utf8()
357+
.into_string()
358+
.map_err(|_| const_io_error!(io::ErrorKind::Other, "utf8 conversion failed"))
359+
.map(Into::into)
360+
} else {
361+
Err(const_io_error!(io::ErrorKind::NotFound, "stdout not found"))
362+
}
363+
}
316364
}
317365

318366
impl Drop for Command {
@@ -325,4 +373,134 @@ mod uefi_command_internal {
325373
}
326374
}
327375
}
376+
377+
#[repr(C)]
378+
struct PipeProtocol {
379+
reset: simple_text_output::ProtocolReset,
380+
output_string: simple_text_output::ProtocolOutputString,
381+
test_string: simple_text_output::ProtocolTestString,
382+
query_mode: simple_text_output::ProtocolQueryMode,
383+
set_mode: simple_text_output::ProtocolSetMode,
384+
set_attribute: simple_text_output::ProtocolSetAttribute,
385+
clear_screen: simple_text_output::ProtocolClearScreen,
386+
set_cursor_position: simple_text_output::ProtocolSetCursorPosition,
387+
enable_cursor: simple_text_output::ProtocolEnableCursor,
388+
mode: *mut simple_text_output::Mode,
389+
_mode: Box<simple_text_output::Mode>,
390+
_buffer: Vec<u16>,
391+
}
392+
393+
impl PipeProtocol {
394+
fn new() -> Self {
395+
let mut mode = Box::new(simple_text_output::Mode {
396+
max_mode: 0,
397+
mode: 0,
398+
attribute: 0,
399+
cursor_column: 0,
400+
cursor_row: 0,
401+
cursor_visible: r_efi::efi::Boolean::FALSE,
402+
});
403+
Self {
404+
reset: Self::reset,
405+
output_string: Self::output_string,
406+
test_string: Self::test_string,
407+
query_mode: Self::query_mode,
408+
set_mode: Self::set_mode,
409+
set_attribute: Self::set_attribute,
410+
clear_screen: Self::clear_screen,
411+
set_cursor_position: Self::set_cursor_position,
412+
enable_cursor: Self::enable_cursor,
413+
mode: mode.as_mut(),
414+
_mode: mode,
415+
_buffer: Vec::new(),
416+
}
417+
}
418+
419+
fn utf8(&self) -> OsString {
420+
OsString::from_wide(&self._buffer)
421+
}
422+
423+
extern "efiapi" fn reset(
424+
proto: *mut simple_text_output::Protocol,
425+
_: r_efi::efi::Boolean,
426+
) -> r_efi::efi::Status {
427+
let proto: *mut PipeProtocol = proto.cast();
428+
unsafe {
429+
(*proto)._buffer.clear();
430+
}
431+
r_efi::efi::Status::SUCCESS
432+
}
433+
434+
extern "efiapi" fn output_string(
435+
proto: *mut simple_text_output::Protocol,
436+
buf: *mut r_efi::efi::Char16,
437+
) -> r_efi::efi::Status {
438+
let proto: *mut PipeProtocol = proto.cast();
439+
let buf_len = unsafe {
440+
if let Some(x) = WStrUnits::new(buf) {
441+
x.count()
442+
} else {
443+
return r_efi::efi::Status::INVALID_PARAMETER;
444+
}
445+
};
446+
let buf_slice = unsafe { slice::from_raw_parts(buf, buf_len) };
447+
448+
unsafe {
449+
(*proto)._buffer.extend_from_slice(buf_slice);
450+
};
451+
452+
r_efi::efi::Status::SUCCESS
453+
}
454+
455+
extern "efiapi" fn test_string(
456+
_: *mut simple_text_output::Protocol,
457+
_: *mut r_efi::efi::Char16,
458+
) -> r_efi::efi::Status {
459+
r_efi::efi::Status::SUCCESS
460+
}
461+
462+
extern "efiapi" fn query_mode(
463+
_: *mut simple_text_output::Protocol,
464+
_: usize,
465+
_: *mut usize,
466+
_: *mut usize,
467+
) -> r_efi::efi::Status {
468+
r_efi::efi::Status::UNSUPPORTED
469+
}
470+
471+
extern "efiapi" fn set_mode(
472+
_: *mut simple_text_output::Protocol,
473+
_: usize,
474+
) -> r_efi::efi::Status {
475+
r_efi::efi::Status::UNSUPPORTED
476+
}
477+
478+
extern "efiapi" fn set_attribute(
479+
_: *mut simple_text_output::Protocol,
480+
_: usize,
481+
) -> r_efi::efi::Status {
482+
r_efi::efi::Status::UNSUPPORTED
483+
}
484+
485+
extern "efiapi" fn clear_screen(
486+
_: *mut simple_text_output::Protocol,
487+
) -> r_efi::efi::Status {
488+
r_efi::efi::Status::UNSUPPORTED
489+
}
490+
491+
extern "efiapi" fn set_cursor_position(
492+
_: *mut simple_text_output::Protocol,
493+
_: usize,
494+
_: usize,
495+
) -> r_efi::efi::Status {
496+
r_efi::efi::Status::UNSUPPORTED
497+
}
498+
499+
extern "efiapi" fn enable_cursor(
500+
_: *mut simple_text_output::Protocol,
501+
_: r_efi::efi::Boolean,
502+
) -> r_efi::efi::Status {
503+
r_efi::efi::Status::UNSUPPORTED
504+
}
505+
}
328506
}

0 commit comments

Comments
 (0)