Skip to content

Commit 452bfb8

Browse files
authored
Rollup merge of rust-lang#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 rust-lang#105458 was merged, the `spawn` and `output` implementations are completely independent.
2 parents ff4b398 + e290398 commit 452bfb8

File tree

5 files changed

+888
-4
lines changed

5 files changed

+888
-4
lines changed

library/std/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ hermit-abi = { version = "0.4.0", features = ['rustc-dep-of-std'], public = true
5656
wasi = { version = "0.11.0", features = ['rustc-dep-of-std'], default-features = false }
5757

5858
[target.'cfg(target_os = "uefi")'.dependencies]
59-
r-efi = { version = "4.2.0", features = ['rustc-dep-of-std'] }
59+
r-efi = { version = "4.5.0", features = ['rustc-dep-of-std'] }
6060
r-efi-alloc = { version = "1.0.0", features = ['rustc-dep-of-std'] }
6161

6262
[features]

library/std/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,7 @@
293293
#![feature(doc_masked)]
294294
#![feature(doc_notable_trait)]
295295
#![feature(dropck_eyepatch)]
296+
#![feature(extended_varargs_abi_support)]
296297
#![feature(f128)]
297298
#![feature(f16)]
298299
#![feature(if_let_guard)]

library/std/src/sys/pal/uefi/helpers.rs

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

15-
use crate::ffi::OsString;
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};
2222
use crate::sys_common::wstr::WStrUnits;
2323

24+
type BootInstallMultipleProtocolInterfaces =
25+
unsafe extern "efiapi" fn(_: *mut r_efi::efi::Handle, _: ...) -> r_efi::efi::Status;
26+
27+
type BootUninstallMultipleProtocolInterfaces =
28+
unsafe extern "efiapi" fn(_: r_efi::efi::Handle, _: ...) -> r_efi::efi::Status;
29+
2430
const BOOT_SERVICES_UNAVAILABLE: io::Error =
2531
const_io_error!(io::ErrorKind::Other, "Boot Services are no longer available");
2632

@@ -221,3 +227,192 @@ pub(crate) fn runtime_services() -> Option<NonNull<r_efi::efi::RuntimeServices>>
221227
let runtime_services = unsafe { (*system_table.as_ptr()).runtime_services };
222228
NonNull::new(runtime_services)
223229
}
230+
231+
pub(crate) struct DevicePath(NonNull<r_efi::protocols::device_path::Protocol>);
232+
233+
impl DevicePath {
234+
pub(crate) fn from_text(p: &OsStr) -> io::Result<Self> {
235+
fn inner(
236+
p: &OsStr,
237+
protocol: NonNull<r_efi::protocols::device_path_from_text::Protocol>,
238+
) -> io::Result<DevicePath> {
239+
let path_vec = p.encode_wide().chain(Some(0)).collect::<Vec<u16>>();
240+
if path_vec[..path_vec.len() - 1].contains(&0) {
241+
return Err(const_io_error!(
242+
io::ErrorKind::InvalidInput,
243+
"strings passed to UEFI cannot contain NULs",
244+
));
245+
}
246+
247+
let path =
248+
unsafe { ((*protocol.as_ptr()).convert_text_to_device_path)(path_vec.as_ptr()) };
249+
250+
NonNull::new(path).map(DevicePath).ok_or_else(|| {
251+
const_io_error!(io::ErrorKind::InvalidFilename, "Invalid Device Path")
252+
})
253+
}
254+
255+
static LAST_VALID_HANDLE: AtomicPtr<crate::ffi::c_void> =
256+
AtomicPtr::new(crate::ptr::null_mut());
257+
258+
if let Some(handle) = NonNull::new(LAST_VALID_HANDLE.load(Ordering::Acquire)) {
259+
if let Ok(protocol) = open_protocol::<r_efi::protocols::device_path_from_text::Protocol>(
260+
handle,
261+
r_efi::protocols::device_path_from_text::PROTOCOL_GUID,
262+
) {
263+
return inner(p, protocol);
264+
}
265+
}
266+
267+
let handles = locate_handles(r_efi::protocols::device_path_from_text::PROTOCOL_GUID)?;
268+
for handle in handles {
269+
if let Ok(protocol) = open_protocol::<r_efi::protocols::device_path_from_text::Protocol>(
270+
handle,
271+
r_efi::protocols::device_path_from_text::PROTOCOL_GUID,
272+
) {
273+
LAST_VALID_HANDLE.store(handle.as_ptr(), Ordering::Release);
274+
return inner(p, protocol);
275+
}
276+
}
277+
278+
io::Result::Err(const_io_error!(
279+
io::ErrorKind::NotFound,
280+
"DevicePathFromText Protocol not found"
281+
))
282+
}
283+
284+
pub(crate) fn as_ptr(&self) -> *mut r_efi::protocols::device_path::Protocol {
285+
self.0.as_ptr()
286+
}
287+
}
288+
289+
impl Drop for DevicePath {
290+
fn drop(&mut self) {
291+
if let Some(bt) = boot_services() {
292+
let bt: NonNull<r_efi::efi::BootServices> = bt.cast();
293+
unsafe {
294+
((*bt.as_ptr()).free_pool)(self.0.as_ptr() as *mut crate::ffi::c_void);
295+
}
296+
}
297+
}
298+
}
299+
300+
pub(crate) struct OwnedProtocol<T> {
301+
guid: r_efi::efi::Guid,
302+
handle: NonNull<crate::ffi::c_void>,
303+
protocol: *mut T,
304+
}
305+
306+
impl<T> OwnedProtocol<T> {
307+
// FIXME: Consider using unsafe trait for matching protocol with guid
308+
pub(crate) unsafe fn create(protocol: T, mut guid: r_efi::efi::Guid) -> io::Result<Self> {
309+
let bt: NonNull<r_efi::efi::BootServices> =
310+
boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?.cast();
311+
let protocol: *mut T = Box::into_raw(Box::new(protocol));
312+
let mut handle: r_efi::efi::Handle = crate::ptr::null_mut();
313+
314+
// FIXME: Move into r-efi once extended_varargs_abi_support is stablized
315+
let func: BootInstallMultipleProtocolInterfaces =
316+
unsafe { crate::mem::transmute((*bt.as_ptr()).install_multiple_protocol_interfaces) };
317+
318+
let r = unsafe {
319+
func(
320+
&mut handle,
321+
&mut guid as *mut _ as *mut crate::ffi::c_void,
322+
protocol as *mut crate::ffi::c_void,
323+
crate::ptr::null_mut() as *mut crate::ffi::c_void,
324+
)
325+
};
326+
327+
if r.is_error() {
328+
drop(unsafe { Box::from_raw(protocol) });
329+
return Err(crate::io::Error::from_raw_os_error(r.as_usize()));
330+
};
331+
332+
let handle = NonNull::new(handle)
333+
.ok_or(io::const_io_error!(io::ErrorKind::Uncategorized, "found null handle"))?;
334+
335+
Ok(Self { guid, handle, protocol })
336+
}
337+
338+
pub(crate) fn handle(&self) -> NonNull<crate::ffi::c_void> {
339+
self.handle
340+
}
341+
}
342+
343+
impl<T> Drop for OwnedProtocol<T> {
344+
fn drop(&mut self) {
345+
// Do not deallocate a runtime protocol
346+
if let Some(bt) = boot_services() {
347+
let bt: NonNull<r_efi::efi::BootServices> = bt.cast();
348+
// FIXME: Move into r-efi once extended_varargs_abi_support is stablized
349+
let func: BootUninstallMultipleProtocolInterfaces = unsafe {
350+
crate::mem::transmute((*bt.as_ptr()).uninstall_multiple_protocol_interfaces)
351+
};
352+
let status = unsafe {
353+
func(
354+
self.handle.as_ptr(),
355+
&mut self.guid as *mut _ as *mut crate::ffi::c_void,
356+
self.protocol as *mut crate::ffi::c_void,
357+
crate::ptr::null_mut() as *mut crate::ffi::c_void,
358+
)
359+
};
360+
361+
// Leak the protocol in case uninstall fails
362+
if status == r_efi::efi::Status::SUCCESS {
363+
let _ = unsafe { Box::from_raw(self.protocol) };
364+
}
365+
}
366+
}
367+
}
368+
369+
impl<T> AsRef<T> for OwnedProtocol<T> {
370+
fn as_ref(&self) -> &T {
371+
unsafe { self.protocol.as_ref().unwrap() }
372+
}
373+
}
374+
375+
pub(crate) struct OwnedTable<T> {
376+
layout: crate::alloc::Layout,
377+
ptr: *mut T,
378+
}
379+
380+
impl<T> OwnedTable<T> {
381+
pub(crate) fn from_table_header(hdr: &r_efi::efi::TableHeader) -> Self {
382+
let header_size = hdr.header_size as usize;
383+
let layout = crate::alloc::Layout::from_size_align(header_size, 8).unwrap();
384+
let ptr = unsafe { crate::alloc::alloc(layout) as *mut T };
385+
Self { layout, ptr }
386+
}
387+
388+
pub(crate) const fn as_ptr(&self) -> *const T {
389+
self.ptr
390+
}
391+
392+
pub(crate) const fn as_mut_ptr(&self) -> *mut T {
393+
self.ptr
394+
}
395+
}
396+
397+
impl OwnedTable<r_efi::efi::SystemTable> {
398+
pub(crate) fn from_table(tbl: *const r_efi::efi::SystemTable) -> Self {
399+
let hdr = unsafe { (*tbl).hdr };
400+
401+
let owned_tbl = Self::from_table_header(&hdr);
402+
unsafe {
403+
crate::ptr::copy_nonoverlapping(
404+
tbl as *const u8,
405+
owned_tbl.as_mut_ptr() as *mut u8,
406+
hdr.header_size as usize,
407+
)
408+
};
409+
410+
owned_tbl
411+
}
412+
}
413+
414+
impl<T> Drop for OwnedTable<T> {
415+
fn drop(&mut self) {
416+
unsafe { crate::alloc::dealloc(self.ptr as *mut u8, self.layout) };
417+
}
418+
}

library/std/src/sys/pal/uefi/mod.rs

-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ pub mod net;
2525
pub mod os;
2626
#[path = "../unsupported/pipe.rs"]
2727
pub mod pipe;
28-
#[path = "../unsupported/process.rs"]
2928
pub mod process;
3029
pub mod stdio;
3130
pub mod thread;

0 commit comments

Comments
 (0)