Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit ebbe0e4

Browse files
committedOct 29, 2024··
Initial fs implementation for UEFI
- Only implementing basic file stuff for now. - Not implementing path::absolute right now since absolute UEFI paths will not be recognized as absolute path anyway. - Supports both UEFI shell mapping and device path text representation. - Follows closely UEFI shell file implementations. Signed-off-by: Ayush Singh <[email protected]>
1 parent b596184 commit ebbe0e4

File tree

6 files changed

+1063
-14
lines changed

6 files changed

+1063
-14
lines changed
 

‎library/std/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,7 @@
356356
#![feature(prelude_2024)]
357357
#![feature(ptr_as_uninit)]
358358
#![feature(ptr_mask)]
359+
#![feature(ptr_metadata)]
359360
#![feature(random)]
360361
#![feature(slice_internals)]
361362
#![feature(slice_ptr_get)]

‎library/std/src/sys/pal/uefi/fs.rs

Lines changed: 803 additions & 0 deletions
Large diffs are not rendered by default.

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

Lines changed: 250 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ use r_efi::protocols::{device_path, device_path_to_text, shell};
1414

1515
use crate::ffi::{OsStr, OsString};
1616
use crate::io::{self, const_io_error};
17+
use crate::iter::Iterator;
18+
use crate::marker::PhantomData;
1719
use crate::mem::{MaybeUninit, size_of};
1820
use crate::os::uefi::env::boot_services;
1921
use crate::os::uefi::ffi::{OsStrExt, OsStringExt};
@@ -22,6 +24,7 @@ use crate::ptr::NonNull;
2224
use crate::slice;
2325
use crate::sync::atomic::{AtomicPtr, Ordering};
2426
use crate::sys_common::wstr::WStrUnits;
27+
use crate::time::Duration;
2528

2629
type BootInstallMultipleProtocolInterfaces =
2730
unsafe extern "efiapi" fn(_: *mut r_efi::efi::Handle, _: ...) -> r_efi::efi::Status;
@@ -162,11 +165,11 @@ pub(crate) fn image_handle_protocol<T>(protocol_guid: Guid) -> io::Result<NonNul
162165
open_protocol(system_handle, protocol_guid)
163166
}
164167

165-
pub(crate) fn device_path_to_text(path: NonNull<device_path::Protocol>) -> io::Result<OsString> {
168+
fn device_path_to_text_raw(path: NonNull<device_path::Protocol>) -> io::Result<Box<[u16]>> {
166169
fn path_to_text(
167170
protocol: NonNull<device_path_to_text::Protocol>,
168171
path: NonNull<device_path::Protocol>,
169-
) -> io::Result<OsString> {
172+
) -> io::Result<Box<[u16]>> {
170173
let path_ptr: *mut r_efi::efi::Char16 = unsafe {
171174
((*protocol.as_ptr()).convert_device_path_to_text)(
172175
path.as_ptr(),
@@ -177,6 +180,56 @@ pub(crate) fn device_path_to_text(path: NonNull<device_path::Protocol>) -> io::R
177180
)
178181
};
179182

183+
owned_uefi_string_from_raw(path_ptr)
184+
.ok_or(io::const_io_error!(io::ErrorKind::InvalidData, "Invalid path"))
185+
}
186+
187+
static LAST_VALID_HANDLE: AtomicPtr<crate::ffi::c_void> =
188+
AtomicPtr::new(crate::ptr::null_mut());
189+
190+
if let Some(handle) = NonNull::new(LAST_VALID_HANDLE.load(Ordering::Acquire)) {
191+
if let Ok(protocol) = open_protocol::<device_path_to_text::Protocol>(
192+
handle,
193+
device_path_to_text::PROTOCOL_GUID,
194+
) {
195+
return path_to_text(protocol, path);
196+
}
197+
}
198+
199+
let device_path_to_text_handles = locate_handles(device_path_to_text::PROTOCOL_GUID)?;
200+
for handle in device_path_to_text_handles {
201+
if let Ok(protocol) = open_protocol::<device_path_to_text::Protocol>(
202+
handle,
203+
device_path_to_text::PROTOCOL_GUID,
204+
) {
205+
LAST_VALID_HANDLE.store(handle.as_ptr(), Ordering::Release);
206+
return path_to_text(protocol, path);
207+
}
208+
}
209+
210+
Err(io::const_io_error!(io::ErrorKind::NotFound, "No device path to text protocol found"))
211+
}
212+
213+
pub(crate) fn device_path_to_text(path: NonNull<device_path::Protocol>) -> io::Result<OsString> {
214+
let p = device_path_to_text_raw(path)?;
215+
Ok(OsString::from_wide(&p))
216+
}
217+
218+
fn device_node_to_text(path: NonNull<device_path::Protocol>) -> io::Result<OsString> {
219+
fn node_to_text(
220+
protocol: NonNull<device_path_to_text::Protocol>,
221+
path: NonNull<device_path::Protocol>,
222+
) -> io::Result<OsString> {
223+
let path_ptr: *mut r_efi::efi::Char16 = unsafe {
224+
((*protocol.as_ptr()).convert_device_node_to_text)(
225+
path.as_ptr(),
226+
// DisplayOnly
227+
r_efi::efi::Boolean::FALSE,
228+
// AllowShortcuts
229+
r_efi::efi::Boolean::FALSE,
230+
)
231+
};
232+
180233
let path = os_string_from_raw(path_ptr)
181234
.ok_or(io::const_io_error!(io::ErrorKind::InvalidData, "Invalid path"))?;
182235

@@ -198,7 +251,7 @@ pub(crate) fn device_path_to_text(path: NonNull<device_path::Protocol>) -> io::R
198251
handle,
199252
device_path_to_text::PROTOCOL_GUID,
200253
) {
201-
return path_to_text(protocol, path);
254+
return node_to_text(protocol, path);
202255
}
203256
}
204257

@@ -209,7 +262,7 @@ pub(crate) fn device_path_to_text(path: NonNull<device_path::Protocol>) -> io::R
209262
device_path_to_text::PROTOCOL_GUID,
210263
) {
211264
LAST_VALID_HANDLE.store(handle.as_ptr(), Ordering::Release);
212-
return path_to_text(protocol, path);
265+
return node_to_text(protocol, path);
213266
}
214267
}
215268

@@ -224,14 +277,14 @@ pub(crate) fn runtime_services() -> Option<NonNull<r_efi::efi::RuntimeServices>>
224277
NonNull::new(runtime_services)
225278
}
226279

227-
pub(crate) struct DevicePath(NonNull<r_efi::protocols::device_path::Protocol>);
280+
pub(crate) struct OwnedDevicePath(pub(crate) NonNull<r_efi::protocols::device_path::Protocol>);
228281

229-
impl DevicePath {
282+
impl OwnedDevicePath {
230283
pub(crate) fn from_text(p: &OsStr) -> io::Result<Self> {
231284
fn inner(
232285
p: &OsStr,
233286
protocol: NonNull<r_efi::protocols::device_path_from_text::Protocol>,
234-
) -> io::Result<DevicePath> {
287+
) -> io::Result<OwnedDevicePath> {
235288
let path_vec = p.encode_wide().chain(Some(0)).collect::<Vec<u16>>();
236289
if path_vec[..path_vec.len() - 1].contains(&0) {
237290
return Err(const_io_error!(
@@ -243,7 +296,7 @@ impl DevicePath {
243296
let path =
244297
unsafe { ((*protocol.as_ptr()).convert_text_to_device_path)(path_vec.as_ptr()) };
245298

246-
NonNull::new(path).map(DevicePath).ok_or_else(|| {
299+
NonNull::new(path).map(OwnedDevicePath).ok_or_else(|| {
247300
const_io_error!(io::ErrorKind::InvalidFilename, "Invalid Device Path")
248301
})
249302
}
@@ -277,12 +330,16 @@ impl DevicePath {
277330
))
278331
}
279332

280-
pub(crate) fn as_ptr(&self) -> *mut r_efi::protocols::device_path::Protocol {
333+
pub(crate) const fn as_ptr(&self) -> *mut r_efi::protocols::device_path::Protocol {
281334
self.0.as_ptr()
282335
}
336+
337+
pub(crate) const fn borrow<'a>(&'a self) -> BorrowedDevicePath<'a> {
338+
BorrowedDevicePath::new(self.0)
339+
}
283340
}
284341

285-
impl Drop for DevicePath {
342+
impl Drop for OwnedDevicePath {
286343
fn drop(&mut self) {
287344
if let Some(bt) = boot_services() {
288345
let bt: NonNull<r_efi::efi::BootServices> = bt.cast();
@@ -293,6 +350,136 @@ impl Drop for DevicePath {
293350
}
294351
}
295352

353+
impl crate::fmt::Debug for OwnedDevicePath {
354+
fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result {
355+
let p = device_path_to_text(self.0).unwrap();
356+
p.fmt(f)
357+
}
358+
}
359+
360+
pub(crate) struct BorrowedDevicePath<'a> {
361+
protocol: NonNull<r_efi::protocols::device_path::Protocol>,
362+
phantom: PhantomData<&'a r_efi::protocols::device_path::Protocol>,
363+
}
364+
365+
impl<'a> BorrowedDevicePath<'a> {
366+
pub(crate) const fn new(protocol: NonNull<r_efi::protocols::device_path::Protocol>) -> Self {
367+
Self { protocol, phantom: PhantomData }
368+
}
369+
370+
pub(crate) const fn iter(&'a self) -> DevicePathIterator<'a> {
371+
DevicePathIterator::new(DevicePathNode::new(self.protocol))
372+
}
373+
374+
pub(crate) fn to_text_raw(&self) -> io::Result<Box<[u16]>> {
375+
device_path_to_text_raw(self.protocol)
376+
}
377+
378+
pub(crate) fn to_text(&self) -> io::Result<OsString> {
379+
device_path_to_text(self.protocol)
380+
}
381+
}
382+
383+
impl<'a> crate::fmt::Debug for BorrowedDevicePath<'a> {
384+
fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result {
385+
let p = self.to_text().unwrap();
386+
p.fmt(f)
387+
}
388+
}
389+
390+
pub(crate) struct DevicePathIterator<'a>(Option<DevicePathNode<'a>>);
391+
392+
impl<'a> DevicePathIterator<'a> {
393+
const fn new(node: DevicePathNode<'a>) -> Self {
394+
if node.is_end() { Self(None) } else { Self(Some(node)) }
395+
}
396+
}
397+
398+
impl<'a> Iterator for DevicePathIterator<'a> {
399+
type Item = DevicePathNode<'a>;
400+
401+
fn next(&mut self) -> Option<Self::Item> {
402+
let cur_node = self.0?;
403+
404+
let next_node = unsafe { cur_node.next_node() };
405+
self.0 = if next_node.is_end() { None } else { Some(next_node) };
406+
407+
Some(cur_node)
408+
}
409+
}
410+
411+
#[derive(Copy, Clone)]
412+
pub(crate) struct DevicePathNode<'a> {
413+
protocol: NonNull<r_efi::protocols::device_path::Protocol>,
414+
phantom: PhantomData<&'a r_efi::protocols::device_path::Protocol>,
415+
}
416+
417+
impl<'a> DevicePathNode<'a> {
418+
pub(crate) const fn new(protocol: NonNull<r_efi::protocols::device_path::Protocol>) -> Self {
419+
Self { protocol, phantom: PhantomData }
420+
}
421+
422+
pub(crate) const fn length(&self) -> u16 {
423+
let len = unsafe { (*self.protocol.as_ptr()).length };
424+
u16::from_le_bytes(len)
425+
}
426+
427+
pub(crate) const fn node_type(&self) -> u8 {
428+
unsafe { (*self.protocol.as_ptr()).r#type }
429+
}
430+
431+
pub(crate) const fn sub_type(&self) -> u8 {
432+
unsafe { (*self.protocol.as_ptr()).sub_type }
433+
}
434+
435+
pub(crate) const fn is_end(&self) -> bool {
436+
self.node_type() == r_efi::protocols::device_path::TYPE_END
437+
&& self.sub_type() == r_efi::protocols::device_path::End::SUBTYPE_ENTIRE
438+
}
439+
440+
pub(crate) const fn is_end_instance(&self) -> bool {
441+
self.node_type() == r_efi::protocols::device_path::TYPE_END
442+
&& self.sub_type() == r_efi::protocols::device_path::End::SUBTYPE_INSTANCE
443+
}
444+
445+
pub(crate) unsafe fn next_node(&self) -> Self {
446+
let node = unsafe {
447+
self.protocol
448+
.cast::<u8>()
449+
.add(self.length().into())
450+
.cast::<r_efi::protocols::device_path::Protocol>()
451+
};
452+
Self::new(node)
453+
}
454+
455+
pub(crate) fn to_path(&'a self) -> BorrowedDevicePath<'a> {
456+
BorrowedDevicePath::new(self.protocol)
457+
}
458+
}
459+
460+
impl<'a> PartialEq for DevicePathNode<'a> {
461+
fn eq(&self, other: &Self) -> bool {
462+
let self_len = self.length();
463+
let other_len = other.length();
464+
465+
self_len == other_len
466+
&& unsafe {
467+
compiler_builtins::mem::memcmp(
468+
self.protocol.as_ptr().cast(),
469+
other.protocol.as_ptr().cast(),
470+
usize::from(self_len),
471+
) == 0
472+
}
473+
}
474+
}
475+
476+
impl<'a> crate::fmt::Debug for DevicePathNode<'a> {
477+
fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result {
478+
let p = device_node_to_text(self.protocol).unwrap();
479+
p.fmt(f)
480+
}
481+
}
482+
296483
pub(crate) struct OwnedProtocol<T> {
297484
guid: r_efi::efi::Guid,
298485
handle: NonNull<crate::ffi::c_void>,
@@ -413,6 +600,15 @@ impl<T> Drop for OwnedTable<T> {
413600
}
414601
}
415602

603+
/// Create an Owned UEFI string from raw pointer. Allows string allocations and conversions
604+
///
605+
/// SAFETY: This function assumes that Rust has ownership over this string
606+
fn owned_uefi_string_from_raw(ptr: *mut r_efi::efi::Char16) -> Option<Box<[r_efi::efi::Char16]>> {
607+
let str_len = unsafe { WStrUnits::new(ptr)?.count() };
608+
let str_slice = crate::ptr::slice_from_raw_parts_mut(ptr.cast(), str_len);
609+
Some(unsafe { Box::from_raw(str_slice) })
610+
}
611+
416612
/// Create OsString from a pointer to NULL terminated UTF-16 string
417613
pub(crate) fn os_string_from_raw(ptr: *mut r_efi::efi::Char16) -> Option<OsString> {
418614
let path_len = unsafe { WStrUnits::new(ptr)?.count() };
@@ -445,3 +641,47 @@ pub(crate) fn open_shell() -> Option<NonNull<shell::Protocol>> {
445641

446642
None
447643
}
644+
645+
// This algorithm is taken from: http://howardhinnant.github.io/date_algorithms.html
646+
pub const fn uefi_time_from_duration(
647+
dur: Duration,
648+
daylight: u8,
649+
timezone: i16,
650+
) -> r_efi::system::Time {
651+
const SECS_IN_MINUTE: u64 = 60;
652+
const SECS_IN_HOUR: u64 = SECS_IN_MINUTE * 60;
653+
const SECS_IN_DAY: u64 = SECS_IN_HOUR * 24;
654+
655+
let secs = dur.as_secs();
656+
657+
let days = secs / SECS_IN_DAY;
658+
let remaining_secs = secs % SECS_IN_DAY;
659+
660+
let z = days + 719468;
661+
let era = z / 146097;
662+
let doe = z - (era * 146097);
663+
let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
664+
let mut y = yoe + era * 400;
665+
let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
666+
let mp = (5 * doy + 2) / 153;
667+
let d = doy - (153 * mp + 2) / 5 + 1;
668+
let m = if mp < 10 { mp + 3 } else { mp - 9 };
669+
670+
if m <= 2 {
671+
y += 1;
672+
}
673+
674+
r_efi::system::Time {
675+
year: y as u16,
676+
month: m as u8,
677+
day: d as u8,
678+
hour: (remaining_secs / SECS_IN_HOUR) as u8,
679+
minute: ((remaining_secs % SECS_IN_HOUR) / SECS_IN_MINUTE) as u8,
680+
second: ((remaining_secs % SECS_IN_HOUR) % SECS_IN_MINUTE) as u8,
681+
pad1: 0,
682+
nanosecond: dur.subsec_nanos(),
683+
timezone,
684+
daylight,
685+
pad2: 0,
686+
}
687+
}

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515

1616
pub mod args;
1717
pub mod env;
18-
#[path = "../unsupported/fs.rs"]
1918
pub mod fs;
2019
pub mod helpers;
2120
#[path = "../unsupported/io.rs"]

‎library/std/src/sys/pal/uefi/process.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ mod uefi_command_internal {
320320

321321
impl Image {
322322
pub fn load_image(p: &OsStr) -> io::Result<Self> {
323-
let path = helpers::DevicePath::from_text(p)?;
323+
let path = helpers::OwnedDevicePath::from_text(p)?;
324324
let boot_services: NonNull<r_efi::efi::BootServices> = boot_services()
325325
.ok_or_else(|| const_io_error!(io::ErrorKind::NotFound, "Boot Services not found"))?
326326
.cast();

‎library/std/src/sys/pal/uefi/time.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const SECS_IN_DAY: u64 = SECS_IN_HOUR * 24;
88
pub struct Instant(Duration);
99

1010
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
11-
pub struct SystemTime(Duration);
11+
pub struct SystemTime(pub(crate) Duration);
1212

1313
pub const UNIX_EPOCH: SystemTime = SystemTime(Duration::from_secs(0));
1414

@@ -40,6 +40,12 @@ impl Instant {
4040
}
4141

4242
impl SystemTime {
43+
pub(crate) const ZERO: SystemTime = SystemTime(Duration::ZERO);
44+
45+
pub(crate) const fn new(t: r_efi::efi::Time) -> Self {
46+
Self(system_time_internal::uefi_time_to_duration(t))
47+
}
48+
4349
pub fn now() -> SystemTime {
4450
system_time_internal::now()
4551
.unwrap_or_else(|| panic!("time not implemented on this platform"))
@@ -79,7 +85,7 @@ pub(crate) mod system_time_internal {
7985

8086
let t = unsafe { t.assume_init() };
8187

82-
Some(SystemTime(uefi_time_to_duration(t)))
88+
Some(SystemTime::new(t))
8389
}
8490

8591
// This algorithm is based on the one described in the post

0 commit comments

Comments
 (0)
Please sign in to comment.