about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--library/std/Cargo.toml2
-rw-r--r--library/std/src/lib.rs1
-rw-r--r--library/std/src/sys/pal/uefi/helpers.rs199
-rw-r--r--library/std/src/sys/pal/uefi/mod.rs1
-rw-r--r--library/std/src/sys/pal/uefi/process.rs689
5 files changed, 888 insertions, 4 deletions
diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml
index b991b1cf22d..3db2f113860 100644
--- a/library/std/Cargo.toml
+++ b/library/std/Cargo.toml
@@ -56,7 +56,7 @@ hermit-abi = { version = "0.4.0", features = ['rustc-dep-of-std'], public = true
 wasi = { version = "0.11.0", features = ['rustc-dep-of-std'], default-features = false }
 
 [target.'cfg(target_os = "uefi")'.dependencies]
-r-efi = { version = "4.2.0", features = ['rustc-dep-of-std'] }
+r-efi = { version = "4.5.0", features = ['rustc-dep-of-std'] }
 r-efi-alloc = { version = "1.0.0", features = ['rustc-dep-of-std'] }
 
 [features]
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index f0a73a308a4..eb30a3355e5 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -293,6 +293,7 @@
 #![feature(doc_masked)]
 #![feature(doc_notable_trait)]
 #![feature(dropck_eyepatch)]
+#![feature(extended_varargs_abi_support)]
 #![feature(f128)]
 #![feature(f16)]
 #![feature(if_let_guard)]
diff --git a/library/std/src/sys/pal/uefi/helpers.rs b/library/std/src/sys/pal/uefi/helpers.rs
index 23aa4da14a7..29984a915b8 100644
--- a/library/std/src/sys/pal/uefi/helpers.rs
+++ b/library/std/src/sys/pal/uefi/helpers.rs
@@ -12,15 +12,21 @@
 use r_efi::efi::{self, Guid};
 use r_efi::protocols::{device_path, device_path_to_text};
 
-use crate::ffi::OsString;
+use crate::ffi::{OsStr, OsString};
 use crate::io::{self, const_io_error};
 use crate::mem::{size_of, MaybeUninit};
-use crate::os::uefi::{self, env::boot_services, ffi::OsStringExt};
+use crate::os::uefi::{self, env::boot_services, ffi::OsStrExt, ffi::OsStringExt};
 use crate::ptr::NonNull;
 use crate::slice;
 use crate::sync::atomic::{AtomicPtr, Ordering};
 use crate::sys_common::wstr::WStrUnits;
 
+type BootInstallMultipleProtocolInterfaces =
+    unsafe extern "efiapi" fn(_: *mut r_efi::efi::Handle, _: ...) -> r_efi::efi::Status;
+
+type BootUninstallMultipleProtocolInterfaces =
+    unsafe extern "efiapi" fn(_: r_efi::efi::Handle, _: ...) -> r_efi::efi::Status;
+
 const BOOT_SERVICES_UNAVAILABLE: io::Error =
     const_io_error!(io::ErrorKind::Other, "Boot Services are no longer available");
 
@@ -221,3 +227,192 @@ pub(crate) fn runtime_services() -> Option<NonNull<r_efi::efi::RuntimeServices>>
     let runtime_services = unsafe { (*system_table.as_ptr()).runtime_services };
     NonNull::new(runtime_services)
 }
+
+pub(crate) struct DevicePath(NonNull<r_efi::protocols::device_path::Protocol>);
+
+impl DevicePath {
+    pub(crate) fn from_text(p: &OsStr) -> io::Result<Self> {
+        fn inner(
+            p: &OsStr,
+            protocol: NonNull<r_efi::protocols::device_path_from_text::Protocol>,
+        ) -> io::Result<DevicePath> {
+            let path_vec = p.encode_wide().chain(Some(0)).collect::<Vec<u16>>();
+            if path_vec[..path_vec.len() - 1].contains(&0) {
+                return Err(const_io_error!(
+                    io::ErrorKind::InvalidInput,
+                    "strings passed to UEFI cannot contain NULs",
+                ));
+            }
+
+            let path =
+                unsafe { ((*protocol.as_ptr()).convert_text_to_device_path)(path_vec.as_ptr()) };
+
+            NonNull::new(path).map(DevicePath).ok_or_else(|| {
+                const_io_error!(io::ErrorKind::InvalidFilename, "Invalid Device Path")
+            })
+        }
+
+        static LAST_VALID_HANDLE: AtomicPtr<crate::ffi::c_void> =
+            AtomicPtr::new(crate::ptr::null_mut());
+
+        if let Some(handle) = NonNull::new(LAST_VALID_HANDLE.load(Ordering::Acquire)) {
+            if let Ok(protocol) = open_protocol::<r_efi::protocols::device_path_from_text::Protocol>(
+                handle,
+                r_efi::protocols::device_path_from_text::PROTOCOL_GUID,
+            ) {
+                return inner(p, protocol);
+            }
+        }
+
+        let handles = locate_handles(r_efi::protocols::device_path_from_text::PROTOCOL_GUID)?;
+        for handle in handles {
+            if let Ok(protocol) = open_protocol::<r_efi::protocols::device_path_from_text::Protocol>(
+                handle,
+                r_efi::protocols::device_path_from_text::PROTOCOL_GUID,
+            ) {
+                LAST_VALID_HANDLE.store(handle.as_ptr(), Ordering::Release);
+                return inner(p, protocol);
+            }
+        }
+
+        io::Result::Err(const_io_error!(
+            io::ErrorKind::NotFound,
+            "DevicePathFromText Protocol not found"
+        ))
+    }
+
+    pub(crate) fn as_ptr(&self) -> *mut r_efi::protocols::device_path::Protocol {
+        self.0.as_ptr()
+    }
+}
+
+impl Drop for DevicePath {
+    fn drop(&mut self) {
+        if let Some(bt) = boot_services() {
+            let bt: NonNull<r_efi::efi::BootServices> = bt.cast();
+            unsafe {
+                ((*bt.as_ptr()).free_pool)(self.0.as_ptr() as *mut crate::ffi::c_void);
+            }
+        }
+    }
+}
+
+pub(crate) struct OwnedProtocol<T> {
+    guid: r_efi::efi::Guid,
+    handle: NonNull<crate::ffi::c_void>,
+    protocol: *mut T,
+}
+
+impl<T> OwnedProtocol<T> {
+    // FIXME: Consider using unsafe trait for matching protocol with guid
+    pub(crate) unsafe fn create(protocol: T, mut guid: r_efi::efi::Guid) -> io::Result<Self> {
+        let bt: NonNull<r_efi::efi::BootServices> =
+            boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?.cast();
+        let protocol: *mut T = Box::into_raw(Box::new(protocol));
+        let mut handle: r_efi::efi::Handle = crate::ptr::null_mut();
+
+        // FIXME: Move into r-efi once extended_varargs_abi_support is stablized
+        let func: BootInstallMultipleProtocolInterfaces =
+            unsafe { crate::mem::transmute((*bt.as_ptr()).install_multiple_protocol_interfaces) };
+
+        let r = unsafe {
+            func(
+                &mut handle,
+                &mut guid as *mut _ as *mut crate::ffi::c_void,
+                protocol as *mut crate::ffi::c_void,
+                crate::ptr::null_mut() as *mut crate::ffi::c_void,
+            )
+        };
+
+        if r.is_error() {
+            drop(unsafe { Box::from_raw(protocol) });
+            return Err(crate::io::Error::from_raw_os_error(r.as_usize()));
+        };
+
+        let handle = NonNull::new(handle)
+            .ok_or(io::const_io_error!(io::ErrorKind::Uncategorized, "found null handle"))?;
+
+        Ok(Self { guid, handle, protocol })
+    }
+
+    pub(crate) fn handle(&self) -> NonNull<crate::ffi::c_void> {
+        self.handle
+    }
+}
+
+impl<T> Drop for OwnedProtocol<T> {
+    fn drop(&mut self) {
+        // Do not deallocate a runtime protocol
+        if let Some(bt) = boot_services() {
+            let bt: NonNull<r_efi::efi::BootServices> = bt.cast();
+            // FIXME: Move into r-efi once extended_varargs_abi_support is stablized
+            let func: BootUninstallMultipleProtocolInterfaces = unsafe {
+                crate::mem::transmute((*bt.as_ptr()).uninstall_multiple_protocol_interfaces)
+            };
+            let status = unsafe {
+                func(
+                    self.handle.as_ptr(),
+                    &mut self.guid as *mut _ as *mut crate::ffi::c_void,
+                    self.protocol as *mut crate::ffi::c_void,
+                    crate::ptr::null_mut() as *mut crate::ffi::c_void,
+                )
+            };
+
+            // Leak the protocol in case uninstall fails
+            if status == r_efi::efi::Status::SUCCESS {
+                let _ = unsafe { Box::from_raw(self.protocol) };
+            }
+        }
+    }
+}
+
+impl<T> AsRef<T> for OwnedProtocol<T> {
+    fn as_ref(&self) -> &T {
+        unsafe { self.protocol.as_ref().unwrap() }
+    }
+}
+
+pub(crate) struct OwnedTable<T> {
+    layout: crate::alloc::Layout,
+    ptr: *mut T,
+}
+
+impl<T> OwnedTable<T> {
+    pub(crate) fn from_table_header(hdr: &r_efi::efi::TableHeader) -> Self {
+        let header_size = hdr.header_size as usize;
+        let layout = crate::alloc::Layout::from_size_align(header_size, 8).unwrap();
+        let ptr = unsafe { crate::alloc::alloc(layout) as *mut T };
+        Self { layout, ptr }
+    }
+
+    pub(crate) const fn as_ptr(&self) -> *const T {
+        self.ptr
+    }
+
+    pub(crate) const fn as_mut_ptr(&self) -> *mut T {
+        self.ptr
+    }
+}
+
+impl OwnedTable<r_efi::efi::SystemTable> {
+    pub(crate) fn from_table(tbl: *const r_efi::efi::SystemTable) -> Self {
+        let hdr = unsafe { (*tbl).hdr };
+
+        let owned_tbl = Self::from_table_header(&hdr);
+        unsafe {
+            crate::ptr::copy_nonoverlapping(
+                tbl as *const u8,
+                owned_tbl.as_mut_ptr() as *mut u8,
+                hdr.header_size as usize,
+            )
+        };
+
+        owned_tbl
+    }
+}
+
+impl<T> Drop for OwnedTable<T> {
+    fn drop(&mut self) {
+        unsafe { crate::alloc::dealloc(self.ptr as *mut u8, self.layout) };
+    }
+}
diff --git a/library/std/src/sys/pal/uefi/mod.rs b/library/std/src/sys/pal/uefi/mod.rs
index 4d50d9e8c3d..c54e9477bfc 100644
--- a/library/std/src/sys/pal/uefi/mod.rs
+++ b/library/std/src/sys/pal/uefi/mod.rs
@@ -25,7 +25,6 @@ pub mod net;
 pub mod os;
 #[path = "../unsupported/pipe.rs"]
 pub mod pipe;
-#[path = "../unsupported/process.rs"]
 pub mod process;
 pub mod stdio;
 pub mod thread;
diff --git a/library/std/src/sys/pal/uefi/process.rs b/library/std/src/sys/pal/uefi/process.rs
new file mode 100644
index 00000000000..5c7c8415ee2
--- /dev/null
+++ b/library/std/src/sys/pal/uefi/process.rs
@@ -0,0 +1,689 @@
+use r_efi::protocols::simple_text_output;
+
+use crate::ffi::OsStr;
+use crate::ffi::OsString;
+use crate::fmt;
+use crate::io;
+use crate::num::NonZero;
+use crate::num::NonZeroI32;
+use crate::path::Path;
+use crate::sys::fs::File;
+use crate::sys::pipe::AnonPipe;
+use crate::sys::unsupported;
+use crate::sys_common::process::{CommandEnv, CommandEnvs};
+
+pub use crate::ffi::OsString as EnvKey;
+
+use super::helpers;
+
+////////////////////////////////////////////////////////////////////////////////
+// Command
+////////////////////////////////////////////////////////////////////////////////
+
+#[derive(Debug)]
+pub struct Command {
+    prog: OsString,
+    stdout: Option<Stdio>,
+    stderr: Option<Stdio>,
+}
+
+// passed back to std::process with the pipes connected to the child, if any
+// were requested
+pub struct StdioPipes {
+    pub stdin: Option<AnonPipe>,
+    pub stdout: Option<AnonPipe>,
+    pub stderr: Option<AnonPipe>,
+}
+
+#[derive(Copy, Clone, Debug)]
+pub enum Stdio {
+    Inherit,
+    Null,
+    MakePipe,
+}
+
+impl Command {
+    pub fn new(program: &OsStr) -> Command {
+        Command { prog: program.to_os_string(), stdout: None, stderr: None }
+    }
+
+    // FIXME: Implement arguments as reverse of parsing algorithm
+    pub fn arg(&mut self, _arg: &OsStr) {
+        panic!("unsupported")
+    }
+
+    pub fn env_mut(&mut self) -> &mut CommandEnv {
+        panic!("unsupported")
+    }
+
+    pub fn cwd(&mut self, _dir: &OsStr) {
+        panic!("unsupported")
+    }
+
+    pub fn stdin(&mut self, _stdin: Stdio) {
+        panic!("unsupported")
+    }
+
+    pub fn stdout(&mut self, stdout: Stdio) {
+        self.stdout = Some(stdout);
+    }
+
+    pub fn stderr(&mut self, stderr: Stdio) {
+        self.stderr = Some(stderr);
+    }
+
+    pub fn get_program(&self) -> &OsStr {
+        self.prog.as_ref()
+    }
+
+    pub fn get_args(&self) -> CommandArgs<'_> {
+        panic!("unsupported")
+    }
+
+    pub fn get_envs(&self) -> CommandEnvs<'_> {
+        panic!("unsupported")
+    }
+
+    pub fn get_current_dir(&self) -> Option<&Path> {
+        None
+    }
+
+    pub fn spawn(
+        &mut self,
+        _default: Stdio,
+        _needs_stdin: bool,
+    ) -> io::Result<(Process, StdioPipes)> {
+        unsupported()
+    }
+
+    fn create_pipe(
+        s: Stdio,
+    ) -> io::Result<Option<helpers::OwnedProtocol<uefi_command_internal::PipeProtocol>>> {
+        match s {
+            Stdio::MakePipe => unsafe {
+                helpers::OwnedProtocol::create(
+                    uefi_command_internal::PipeProtocol::new(),
+                    simple_text_output::PROTOCOL_GUID,
+                )
+            }
+            .map(Some),
+            Stdio::Null => unsafe {
+                helpers::OwnedProtocol::create(
+                    uefi_command_internal::PipeProtocol::null(),
+                    simple_text_output::PROTOCOL_GUID,
+                )
+            }
+            .map(Some),
+            Stdio::Inherit => Ok(None),
+        }
+    }
+
+    pub fn output(&mut self) -> io::Result<(ExitStatus, Vec<u8>, Vec<u8>)> {
+        let mut cmd = uefi_command_internal::Image::load_image(&self.prog)?;
+
+        // Setup Stdout
+        let stdout = self.stdout.unwrap_or(Stdio::MakePipe);
+        let stdout = Self::create_pipe(stdout)?;
+        if let Some(con) = stdout {
+            cmd.stdout_init(con)
+        } else {
+            cmd.stdout_inherit()
+        };
+
+        // Setup Stderr
+        let stderr = self.stderr.unwrap_or(Stdio::MakePipe);
+        let stderr = Self::create_pipe(stderr)?;
+        if let Some(con) = stderr {
+            cmd.stderr_init(con)
+        } else {
+            cmd.stderr_inherit()
+        };
+
+        let stat = cmd.start_image()?;
+
+        let stdout = cmd.stdout()?;
+        let stderr = cmd.stderr()?;
+
+        Ok((ExitStatus(stat), stdout, stderr))
+    }
+}
+
+impl From<AnonPipe> for Stdio {
+    fn from(pipe: AnonPipe) -> Stdio {
+        pipe.diverge()
+    }
+}
+
+impl From<io::Stdout> for Stdio {
+    fn from(_: io::Stdout) -> Stdio {
+        // FIXME: This is wrong.
+        // Instead, the Stdio we have here should be a unit struct.
+        panic!("unsupported")
+    }
+}
+
+impl From<io::Stderr> for Stdio {
+    fn from(_: io::Stderr) -> Stdio {
+        // FIXME: This is wrong.
+        // Instead, the Stdio we have here should be a unit struct.
+        panic!("unsupported")
+    }
+}
+
+impl From<File> for Stdio {
+    fn from(_file: File) -> Stdio {
+        // FIXME: This is wrong.
+        // Instead, the Stdio we have here should be a unit struct.
+        panic!("unsupported")
+    }
+}
+
+#[derive(PartialEq, Eq, Clone, Copy, Debug)]
+#[non_exhaustive]
+pub struct ExitStatus(r_efi::efi::Status);
+
+impl ExitStatus {
+    pub fn exit_ok(&self) -> Result<(), ExitStatusError> {
+        if self.0 == r_efi::efi::Status::SUCCESS { Ok(()) } else { Err(ExitStatusError(self.0)) }
+    }
+
+    pub fn code(&self) -> Option<i32> {
+        Some(self.0.as_usize() as i32)
+    }
+}
+
+impl fmt::Display for ExitStatus {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let err_str = super::os::error_string(self.0.as_usize());
+        write!(f, "{}", err_str)
+    }
+}
+
+impl Default for ExitStatus {
+    fn default() -> Self {
+        ExitStatus(r_efi::efi::Status::SUCCESS)
+    }
+}
+
+#[derive(Clone, Copy, PartialEq, Eq)]
+pub struct ExitStatusError(r_efi::efi::Status);
+
+impl fmt::Debug for ExitStatusError {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let err_str = super::os::error_string(self.0.as_usize());
+        write!(f, "{}", err_str)
+    }
+}
+
+impl Into<ExitStatus> for ExitStatusError {
+    fn into(self) -> ExitStatus {
+        ExitStatus(self.0)
+    }
+}
+
+impl ExitStatusError {
+    pub fn code(self) -> Option<NonZero<i32>> {
+        NonZeroI32::new(self.0.as_usize() as i32)
+    }
+}
+
+#[derive(PartialEq, Eq, Clone, Copy, Debug)]
+pub struct ExitCode(bool);
+
+impl ExitCode {
+    pub const SUCCESS: ExitCode = ExitCode(false);
+    pub const FAILURE: ExitCode = ExitCode(true);
+
+    pub fn as_i32(&self) -> i32 {
+        self.0 as i32
+    }
+}
+
+impl From<u8> for ExitCode {
+    fn from(code: u8) -> Self {
+        match code {
+            0 => Self::SUCCESS,
+            1..=255 => Self::FAILURE,
+        }
+    }
+}
+
+pub struct Process(!);
+
+impl Process {
+    pub fn id(&self) -> u32 {
+        self.0
+    }
+
+    pub fn kill(&mut self) -> io::Result<()> {
+        self.0
+    }
+
+    pub fn wait(&mut self) -> io::Result<ExitStatus> {
+        self.0
+    }
+
+    pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
+        self.0
+    }
+}
+
+pub struct CommandArgs<'a> {
+    iter: crate::slice::Iter<'a, OsString>,
+}
+
+impl<'a> Iterator for CommandArgs<'a> {
+    type Item = &'a OsStr;
+
+    fn next(&mut self) -> Option<&'a OsStr> {
+        self.iter.next().map(|x| x.as_ref())
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        self.iter.size_hint()
+    }
+}
+
+impl<'a> ExactSizeIterator for CommandArgs<'a> {
+    fn len(&self) -> usize {
+        self.iter.len()
+    }
+
+    fn is_empty(&self) -> bool {
+        self.iter.is_empty()
+    }
+}
+
+impl<'a> fmt::Debug for CommandArgs<'a> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_list().entries(self.iter.clone()).finish()
+    }
+}
+
+#[allow(dead_code)]
+mod uefi_command_internal {
+    use r_efi::protocols::{loaded_image, simple_text_output};
+
+    use super::super::helpers;
+    use crate::ffi::{OsStr, OsString};
+    use crate::io::{self, const_io_error};
+    use crate::mem::MaybeUninit;
+    use crate::os::uefi::env::{boot_services, image_handle, system_table};
+    use crate::os::uefi::ffi::{OsStrExt, OsStringExt};
+    use crate::ptr::NonNull;
+    use crate::slice;
+    use crate::sys::pal::uefi::helpers::OwnedTable;
+    use crate::sys_common::wstr::WStrUnits;
+
+    pub struct Image {
+        handle: NonNull<crate::ffi::c_void>,
+        stdout: Option<helpers::OwnedProtocol<PipeProtocol>>,
+        stderr: Option<helpers::OwnedProtocol<PipeProtocol>>,
+        st: OwnedTable<r_efi::efi::SystemTable>,
+        args: Option<Vec<u16>>,
+    }
+
+    impl Image {
+        pub fn load_image(p: &OsStr) -> io::Result<Self> {
+            let path = helpers::DevicePath::from_text(p)?;
+            let boot_services: NonNull<r_efi::efi::BootServices> = boot_services()
+                .ok_or_else(|| const_io_error!(io::ErrorKind::NotFound, "Boot Services not found"))?
+                .cast();
+            let mut child_handle: MaybeUninit<r_efi::efi::Handle> = MaybeUninit::uninit();
+            let image_handle = image_handle();
+
+            let r = unsafe {
+                ((*boot_services.as_ptr()).load_image)(
+                    r_efi::efi::Boolean::FALSE,
+                    image_handle.as_ptr(),
+                    path.as_ptr(),
+                    crate::ptr::null_mut(),
+                    0,
+                    child_handle.as_mut_ptr(),
+                )
+            };
+
+            if r.is_error() {
+                Err(io::Error::from_raw_os_error(r.as_usize()))
+            } else {
+                let child_handle = unsafe { child_handle.assume_init() };
+                let child_handle = NonNull::new(child_handle).unwrap();
+
+                let loaded_image: NonNull<loaded_image::Protocol> =
+                    helpers::open_protocol(child_handle, loaded_image::PROTOCOL_GUID).unwrap();
+                let st = OwnedTable::from_table(unsafe { (*loaded_image.as_ptr()).system_table });
+
+                Ok(Self { handle: child_handle, stdout: None, stderr: None, st, args: None })
+            }
+        }
+
+        pub fn start_image(&mut self) -> io::Result<r_efi::efi::Status> {
+            self.update_st_crc32()?;
+
+            // Use our system table instead of the default one
+            let loaded_image: NonNull<loaded_image::Protocol> =
+                helpers::open_protocol(self.handle, loaded_image::PROTOCOL_GUID).unwrap();
+            unsafe {
+                (*loaded_image.as_ptr()).system_table = self.st.as_mut_ptr();
+            }
+
+            let boot_services: NonNull<r_efi::efi::BootServices> = boot_services()
+                .ok_or_else(|| const_io_error!(io::ErrorKind::NotFound, "Boot Services not found"))?
+                .cast();
+            let mut exit_data_size: usize = 0;
+            let mut exit_data: MaybeUninit<*mut u16> = MaybeUninit::uninit();
+
+            let r = unsafe {
+                ((*boot_services.as_ptr()).start_image)(
+                    self.handle.as_ptr(),
+                    &mut exit_data_size,
+                    exit_data.as_mut_ptr(),
+                )
+            };
+
+            // Drop exitdata
+            if exit_data_size != 0 {
+                unsafe {
+                    let exit_data = exit_data.assume_init();
+                    ((*boot_services.as_ptr()).free_pool)(exit_data as *mut crate::ffi::c_void);
+                }
+            }
+
+            Ok(r)
+        }
+
+        fn set_stdout(
+            &mut self,
+            handle: r_efi::efi::Handle,
+            protocol: *mut simple_text_output::Protocol,
+        ) {
+            unsafe {
+                (*self.st.as_mut_ptr()).console_out_handle = handle;
+                (*self.st.as_mut_ptr()).con_out = protocol;
+            }
+        }
+
+        fn set_stderr(
+            &mut self,
+            handle: r_efi::efi::Handle,
+            protocol: *mut simple_text_output::Protocol,
+        ) {
+            unsafe {
+                (*self.st.as_mut_ptr()).standard_error_handle = handle;
+                (*self.st.as_mut_ptr()).std_err = protocol;
+            }
+        }
+
+        pub fn stdout_init(&mut self, protocol: helpers::OwnedProtocol<PipeProtocol>) {
+            self.set_stdout(
+                protocol.handle().as_ptr(),
+                protocol.as_ref() as *const PipeProtocol as *mut simple_text_output::Protocol,
+            );
+            self.stdout = Some(protocol);
+        }
+
+        pub fn stdout_inherit(&mut self) {
+            let st: NonNull<r_efi::efi::SystemTable> = system_table().cast();
+            unsafe { self.set_stdout((*st.as_ptr()).console_out_handle, (*st.as_ptr()).con_out) }
+        }
+
+        pub fn stderr_init(&mut self, protocol: helpers::OwnedProtocol<PipeProtocol>) {
+            self.set_stderr(
+                protocol.handle().as_ptr(),
+                protocol.as_ref() as *const PipeProtocol as *mut simple_text_output::Protocol,
+            );
+            self.stderr = Some(protocol);
+        }
+
+        pub fn stderr_inherit(&mut self) {
+            let st: NonNull<r_efi::efi::SystemTable> = system_table().cast();
+            unsafe { self.set_stderr((*st.as_ptr()).standard_error_handle, (*st.as_ptr()).std_err) }
+        }
+
+        pub fn stderr(&self) -> io::Result<Vec<u8>> {
+            match &self.stderr {
+                Some(stderr) => stderr.as_ref().utf8(),
+                None => Ok(Vec::new()),
+            }
+        }
+
+        pub fn stdout(&self) -> io::Result<Vec<u8>> {
+            match &self.stdout {
+                Some(stdout) => stdout.as_ref().utf8(),
+                None => Ok(Vec::new()),
+            }
+        }
+
+        pub fn set_args(&mut self, args: &OsStr) {
+            let loaded_image: NonNull<loaded_image::Protocol> =
+                helpers::open_protocol(self.handle, loaded_image::PROTOCOL_GUID).unwrap();
+
+            let mut args = args.encode_wide().collect::<Vec<u16>>();
+            let args_size = (crate::mem::size_of::<u16>() * args.len()) as u32;
+
+            unsafe {
+                (*loaded_image.as_ptr()).load_options =
+                    args.as_mut_ptr() as *mut crate::ffi::c_void;
+                (*loaded_image.as_ptr()).load_options_size = args_size;
+            }
+
+            self.args = Some(args);
+        }
+
+        fn update_st_crc32(&mut self) -> io::Result<()> {
+            let bt: NonNull<r_efi::efi::BootServices> = boot_services().unwrap().cast();
+            let st_size = unsafe { (*self.st.as_ptr()).hdr.header_size as usize };
+            let mut crc32: u32 = 0;
+
+            // Set crc to 0 before calcuation
+            unsafe {
+                (*self.st.as_mut_ptr()).hdr.crc32 = 0;
+            }
+
+            let r = unsafe {
+                ((*bt.as_ptr()).calculate_crc32)(
+                    self.st.as_mut_ptr() as *mut crate::ffi::c_void,
+                    st_size,
+                    &mut crc32,
+                )
+            };
+
+            if r.is_error() {
+                Err(io::Error::from_raw_os_error(r.as_usize()))
+            } else {
+                unsafe {
+                    (*self.st.as_mut_ptr()).hdr.crc32 = crc32;
+                }
+                Ok(())
+            }
+        }
+    }
+
+    impl Drop for Image {
+        fn drop(&mut self) {
+            if let Some(bt) = boot_services() {
+                let bt: NonNull<r_efi::efi::BootServices> = bt.cast();
+                unsafe {
+                    ((*bt.as_ptr()).unload_image)(self.handle.as_ptr());
+                }
+            }
+        }
+    }
+
+    #[repr(C)]
+    pub struct PipeProtocol {
+        reset: simple_text_output::ProtocolReset,
+        output_string: simple_text_output::ProtocolOutputString,
+        test_string: simple_text_output::ProtocolTestString,
+        query_mode: simple_text_output::ProtocolQueryMode,
+        set_mode: simple_text_output::ProtocolSetMode,
+        set_attribute: simple_text_output::ProtocolSetAttribute,
+        clear_screen: simple_text_output::ProtocolClearScreen,
+        set_cursor_position: simple_text_output::ProtocolSetCursorPosition,
+        enable_cursor: simple_text_output::ProtocolEnableCursor,
+        mode: *mut simple_text_output::Mode,
+        _buffer: Vec<u16>,
+    }
+
+    impl PipeProtocol {
+        pub fn new() -> Self {
+            let mode = Box::new(simple_text_output::Mode {
+                max_mode: 0,
+                mode: 0,
+                attribute: 0,
+                cursor_column: 0,
+                cursor_row: 0,
+                cursor_visible: r_efi::efi::Boolean::FALSE,
+            });
+            Self {
+                reset: Self::reset,
+                output_string: Self::output_string,
+                test_string: Self::test_string,
+                query_mode: Self::query_mode,
+                set_mode: Self::set_mode,
+                set_attribute: Self::set_attribute,
+                clear_screen: Self::clear_screen,
+                set_cursor_position: Self::set_cursor_position,
+                enable_cursor: Self::enable_cursor,
+                mode: Box::into_raw(mode),
+                _buffer: Vec::new(),
+            }
+        }
+
+        pub fn null() -> Self {
+            let mode = Box::new(simple_text_output::Mode {
+                max_mode: 0,
+                mode: 0,
+                attribute: 0,
+                cursor_column: 0,
+                cursor_row: 0,
+                cursor_visible: r_efi::efi::Boolean::FALSE,
+            });
+            Self {
+                reset: Self::reset_null,
+                output_string: Self::output_string_null,
+                test_string: Self::test_string,
+                query_mode: Self::query_mode,
+                set_mode: Self::set_mode,
+                set_attribute: Self::set_attribute,
+                clear_screen: Self::clear_screen,
+                set_cursor_position: Self::set_cursor_position,
+                enable_cursor: Self::enable_cursor,
+                mode: Box::into_raw(mode),
+                _buffer: Vec::new(),
+            }
+        }
+
+        pub fn utf8(&self) -> io::Result<Vec<u8>> {
+            OsString::from_wide(&self._buffer)
+                .into_string()
+                .map(Into::into)
+                .map_err(|_| const_io_error!(io::ErrorKind::Other, "utf8 conversion failed"))
+        }
+
+        extern "efiapi" fn reset(
+            proto: *mut simple_text_output::Protocol,
+            _: r_efi::efi::Boolean,
+        ) -> r_efi::efi::Status {
+            let proto: *mut PipeProtocol = proto.cast();
+            unsafe {
+                (*proto)._buffer.clear();
+            }
+            r_efi::efi::Status::SUCCESS
+        }
+
+        extern "efiapi" fn reset_null(
+            _: *mut simple_text_output::Protocol,
+            _: r_efi::efi::Boolean,
+        ) -> r_efi::efi::Status {
+            r_efi::efi::Status::SUCCESS
+        }
+
+        extern "efiapi" fn output_string(
+            proto: *mut simple_text_output::Protocol,
+            buf: *mut r_efi::efi::Char16,
+        ) -> r_efi::efi::Status {
+            let proto: *mut PipeProtocol = proto.cast();
+            let buf_len = unsafe {
+                if let Some(x) = WStrUnits::new(buf) {
+                    x.count()
+                } else {
+                    return r_efi::efi::Status::INVALID_PARAMETER;
+                }
+            };
+            let buf_slice = unsafe { slice::from_raw_parts(buf, buf_len) };
+
+            unsafe {
+                (*proto)._buffer.extend_from_slice(buf_slice);
+            };
+
+            r_efi::efi::Status::SUCCESS
+        }
+
+        extern "efiapi" fn output_string_null(
+            _: *mut simple_text_output::Protocol,
+            _: *mut r_efi::efi::Char16,
+        ) -> r_efi::efi::Status {
+            r_efi::efi::Status::SUCCESS
+        }
+
+        extern "efiapi" fn test_string(
+            _: *mut simple_text_output::Protocol,
+            _: *mut r_efi::efi::Char16,
+        ) -> r_efi::efi::Status {
+            r_efi::efi::Status::SUCCESS
+        }
+
+        extern "efiapi" fn query_mode(
+            _: *mut simple_text_output::Protocol,
+            _: usize,
+            _: *mut usize,
+            _: *mut usize,
+        ) -> r_efi::efi::Status {
+            r_efi::efi::Status::UNSUPPORTED
+        }
+
+        extern "efiapi" fn set_mode(
+            _: *mut simple_text_output::Protocol,
+            _: usize,
+        ) -> r_efi::efi::Status {
+            r_efi::efi::Status::UNSUPPORTED
+        }
+
+        extern "efiapi" fn set_attribute(
+            _: *mut simple_text_output::Protocol,
+            _: usize,
+        ) -> r_efi::efi::Status {
+            r_efi::efi::Status::UNSUPPORTED
+        }
+
+        extern "efiapi" fn clear_screen(
+            _: *mut simple_text_output::Protocol,
+        ) -> r_efi::efi::Status {
+            r_efi::efi::Status::UNSUPPORTED
+        }
+
+        extern "efiapi" fn set_cursor_position(
+            _: *mut simple_text_output::Protocol,
+            _: usize,
+            _: usize,
+        ) -> r_efi::efi::Status {
+            r_efi::efi::Status::UNSUPPORTED
+        }
+
+        extern "efiapi" fn enable_cursor(
+            _: *mut simple_text_output::Protocol,
+            _: r_efi::efi::Boolean,
+        ) -> r_efi::efi::Status {
+            r_efi::efi::Status::UNSUPPORTED
+        }
+    }
+
+    impl Drop for PipeProtocol {
+        fn drop(&mut self) {
+            unsafe {
+                let _ = Box::from_raw(self.mode);
+            }
+        }
+    }
+}