Skip to content

std: sys: io: io_slice: Add UEFI types #144350

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions library/std/src/sys/io/io_slice/uefi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
//! A buffer type used with `Write::write_vectored` for UEFI Networking APIs. Vectored writing to
//! File is not supported as of UEFI Spec 2.11.

use crate::marker::PhantomData;
use crate::slice;

#[derive(Copy, Clone)]
#[repr(C)]
pub struct IoSlice<'a> {
len: u32,
data: *const u8,
_p: PhantomData<&'a [u8]>,
}

impl<'a> IoSlice<'a> {
#[inline]
pub fn new(buf: &'a [u8]) -> IoSlice<'a> {
let len = buf.len().try_into().unwrap();
Self { len, data: buf.as_ptr(), _p: PhantomData }
}

#[inline]
pub fn advance(&mut self, n: usize) {
self.len = u32::try_from(n)
.ok()
.and_then(|n| self.len.checked_sub(n))
.expect("advancing IoSlice beyond its length");
unsafe { self.data = self.data.add(n) };
}

#[inline]
pub const fn as_slice(&self) -> &'a [u8] {
unsafe { slice::from_raw_parts(self.data, self.len as usize) }
}
}

#[repr(C)]
pub struct IoSliceMut<'a> {
len: u32,
data: *mut u8,
_p: PhantomData<&'a mut [u8]>,
}

impl<'a> IoSliceMut<'a> {
#[inline]
pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> {
let len = buf.len().try_into().unwrap();
Self { len, data: buf.as_mut_ptr(), _p: PhantomData }
}

#[inline]
pub fn advance(&mut self, n: usize) {
self.len = u32::try_from(n)
.ok()
.and_then(|n| self.len.checked_sub(n))
.expect("advancing IoSlice beyond its length");
unsafe { self.data = self.data.add(n) };
}

#[inline]
pub fn as_slice(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self.data, self.len as usize) }
}

#[inline]
pub const fn into_slice(self) -> &'a mut [u8] {
unsafe { slice::from_raw_parts_mut(self.data, self.len as usize) }
}

#[inline]
pub fn as_mut_slice(&mut self) -> &mut [u8] {
unsafe { slice::from_raw_parts_mut(self.data, self.len as usize) }
}
}
3 changes: 3 additions & 0 deletions library/std/src/sys/io/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ mod io_slice {
} else if #[cfg(target_os = "wasi")] {
mod wasi;
pub use wasi::*;
} else if #[cfg(target_os = "uefi")] {
mod uefi;
pub use uefi::*;
} else {
mod unsupported;
pub use unsupported::*;
Expand Down
119 changes: 119 additions & 0 deletions library/std/src/sys/pal/uefi/tests.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use super::alloc::*;
use super::time::*;
use crate::io::{IoSlice, IoSliceMut};
use crate::time::Duration;

#[test]
Expand Down Expand Up @@ -39,3 +40,121 @@ fn epoch() {
};
assert_eq!(system_time_internal::uefi_time_to_duration(t), Duration::new(0, 0));
}

// UEFI IoSlice and IoSliceMut Tests
//
// Strictly speaking, vectored read/write types for UDP4, UDP6, TCP4, TCP6 are defined
// separately in the UEFI Spec. However, they have the same signature. These tests just ensure
// that `IoSlice` and `IoSliceMut` are compatible with the vectored types for all the
// networking protocols.

unsafe fn to_slice<T>(val: &T) -> &[u8] {
let len = size_of_val(val);
unsafe { crate::slice::from_raw_parts(crate::ptr::from_ref(val).cast(), len) }
}

#[test]
fn io_slice_single() {
let mut data = [0, 1, 2, 3, 4];

let tcp4_frag = r_efi::protocols::tcp4::FragmentData {
fragment_length: data.len().try_into().unwrap(),
fragment_buffer: data.as_mut_ptr().cast(),
};
let tcp6_frag = r_efi::protocols::tcp6::FragmentData {
fragment_length: data.len().try_into().unwrap(),
fragment_buffer: data.as_mut_ptr().cast(),
};
let udp4_frag = r_efi::protocols::udp4::FragmentData {
fragment_length: data.len().try_into().unwrap(),
fragment_buffer: data.as_mut_ptr().cast(),
};
let udp6_frag = r_efi::protocols::udp6::FragmentData {
fragment_length: data.len().try_into().unwrap(),
fragment_buffer: data.as_mut_ptr().cast(),
};
let io_slice = IoSlice::new(&data);

unsafe {
assert_eq!(to_slice(&io_slice), to_slice(&tcp4_frag));
assert_eq!(to_slice(&io_slice), to_slice(&tcp6_frag));
assert_eq!(to_slice(&io_slice), to_slice(&udp4_frag));
assert_eq!(to_slice(&io_slice), to_slice(&udp6_frag));
}
}

#[test]
fn io_slice_mut_single() {
let mut data = [0, 1, 2, 3, 4];

let tcp4_frag = r_efi::protocols::tcp4::FragmentData {
fragment_length: data.len().try_into().unwrap(),
fragment_buffer: data.as_mut_ptr().cast(),
};
let tcp6_frag = r_efi::protocols::tcp6::FragmentData {
fragment_length: data.len().try_into().unwrap(),
fragment_buffer: data.as_mut_ptr().cast(),
};
let udp4_frag = r_efi::protocols::udp4::FragmentData {
fragment_length: data.len().try_into().unwrap(),
fragment_buffer: data.as_mut_ptr().cast(),
};
let udp6_frag = r_efi::protocols::udp6::FragmentData {
fragment_length: data.len().try_into().unwrap(),
fragment_buffer: data.as_mut_ptr().cast(),
};
let io_slice_mut = IoSliceMut::new(&mut data);

unsafe {
assert_eq!(to_slice(&io_slice_mut), to_slice(&tcp4_frag));
assert_eq!(to_slice(&io_slice_mut), to_slice(&tcp6_frag));
assert_eq!(to_slice(&io_slice_mut), to_slice(&udp4_frag));
assert_eq!(to_slice(&io_slice_mut), to_slice(&udp6_frag));
}
}

#[test]
fn io_slice_multi() {
let mut data = [0, 1, 2, 3, 4];

let tcp4_frag = r_efi::protocols::tcp4::FragmentData {
fragment_length: data.len().try_into().unwrap(),
fragment_buffer: data.as_mut_ptr().cast(),
};
let rhs =
[tcp4_frag.clone(), tcp4_frag.clone(), tcp4_frag.clone(), tcp4_frag.clone(), tcp4_frag];
let lhs = [
IoSlice::new(&data),
IoSlice::new(&data),
IoSlice::new(&data),
IoSlice::new(&data),
IoSlice::new(&data),
];

unsafe {
assert_eq!(to_slice(&lhs), to_slice(&rhs));
}
}

#[test]
fn io_slice_basic() {
let data = [0, 1, 2, 3, 4];
let mut io_slice = IoSlice::new(&data);

assert_eq!(data, io_slice.as_slice());
io_slice.advance(2);
assert_eq!(&data[2..], io_slice.as_slice());
}

#[test]
fn io_slice_mut_basic() {
let data = [0, 1, 2, 3, 4];
let mut data_clone = [0, 1, 2, 3, 4];
let mut io_slice_mut = IoSliceMut::new(&mut data_clone);

assert_eq!(data, io_slice_mut.as_slice());
assert_eq!(data, io_slice_mut.as_mut_slice());

io_slice_mut.advance(2);
assert_eq!(&data[2..], io_slice_mut.into_slice());
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be good to add a test or two for advance/as_slice/into_slice/as_mut_slice

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added

Loading