Rollup merge of #123196 - Ayush1325:uefi-process, r=joboet
Add Process support for UEFI UEFI does not have an actual process. However, it does provide methods to launch and execute another UEFI image. Having process support is important since it is possible to run rust test suit using `Command::output` and is the first step towards being able to run it for UEFI. Here is an overview of how the support is implemented. - We create a copy of the SystemTable. This is required since at least OVMF seems to crash if the original system table is modified. - Stdout and Stderr pipe works by registering a new `simple_text_output` Protocol and pointing the child system table to use those. - `Stdio::Inherit` just points the console to the current running image console which seems to work with even 3 levels of process. - `spawn` is left unimplemented since it does not make sense for UEFI architecture. Additionally, since https://github.com/rust-lang/rust/pull/105458 was merged, the `spawn` and `output` implementations are completely independent.
This commit is contained in:
commit
bc86893a1a
5 changed files with 888 additions and 4 deletions
|
@ -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]
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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) };
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
689
library/std/src/sys/pal/uefi/process.rs
Normal file
689
library/std/src/sys/pal/uefi/process.rs
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue