use crate::data_types::Align;
use crate::{Error, Result, ResultExt, Status};
use ::alloc::alloc::{alloc, dealloc};
use ::alloc::boxed::Box;
use core::alloc::Layout;
use core::fmt::Debug;
use core::slice;
pub(crate) fn make_boxed<
'a,
Data: Align + ?Sized + Debug + 'a,
F: FnMut(&'a mut [u8]) -> Result<&'a mut Data, Option<usize>>,
>(
mut fetch_data_fn: F,
) -> Result<Box<Data>> {
let required_size = match fetch_data_fn(&mut []).map_err(Error::split) {
Err((Status::BUFFER_TOO_SMALL, Some(required_size))) => Ok(required_size),
Err((status, _)) => Err(Error::from(status)),
Ok(_) => {
log::debug!("Got unexpected success status");
Err(Error::from(Status::UNSUPPORTED))
}
}?;
let layout = Layout::from_size_align(required_size, Data::alignment())
.unwrap()
.pad_to_align();
let heap_buf: *mut u8 = {
{
let ptr = unsafe { alloc(layout) };
if ptr.is_null() {
return Err(Status::OUT_OF_RESOURCES.into());
}
ptr
}
};
let data: Result<&mut Data> = {
let buffer = unsafe { slice::from_raw_parts_mut(heap_buf, required_size) };
fetch_data_fn(buffer).discard_errdata()
};
let data: &mut Data = match data {
Ok(data) => data,
Err(err) => {
unsafe { dealloc(heap_buf, layout) };
return Err(err);
}
};
let data = unsafe { Box::from_raw(data) };
Ok(data)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{ResultExt, StatusExt};
#[derive(Debug)]
#[repr(C)]
struct SomeData([u8; 4]);
impl Align for SomeData {
fn alignment() -> usize {
align_of::<Self>()
}
}
#[derive(Debug)]
#[repr(C, align(16))]
struct Align16<T>(T);
type SomeDataAlign16 = Align16<SomeData>;
impl Align for SomeDataAlign16 {
fn alignment() -> usize {
align_of::<Self>()
}
}
fn uefi_function_stub_read<Data: Align>(buf: &mut [u8]) -> Result<&mut Data, Option<usize>> {
let required_size = size_of::<Data>();
if buf.len() < required_size {
return Status::BUFFER_TOO_SMALL.to_result_with(|| panic!(), |_| Some(required_size));
};
assert_eq!(
buf.as_ptr() as usize % Data::alignment(),
0,
"The buffer must be correctly aligned!"
);
buf[0] = 1;
buf[1] = 2;
buf[2] = 3;
buf[3] = 4;
let data = unsafe { &mut *buf.as_mut_ptr().cast::<Data>() };
Ok(data)
}
#[test]
fn test_some_data_type_size_constraints() {
assert_eq!(size_of::<SomeData>(), 4);
assert_eq!(SomeData::alignment(), 1);
assert_eq!(
size_of::<SomeDataAlign16>(),
16,
"The size must be 16 instead of 4, as in Rust the runtime size is a multiple of the alignment."
);
assert_eq!(SomeDataAlign16::alignment(), 16);
}
#[test]
fn test_basic_stub_read() {
assert_eq!(
uefi_function_stub_read::<SomeData>(&mut []).status(),
Status::BUFFER_TOO_SMALL
);
assert_eq!(
*uefi_function_stub_read::<SomeData>(&mut [])
.unwrap_err()
.data(),
Some(4)
);
let mut buf: [u8; 4] = [0; 4];
let data: &mut SomeData = uefi_function_stub_read(&mut buf).unwrap();
assert_eq!(&data.0, &[1, 2, 3, 4]);
let mut buf: Align16<[u8; 16]> = Align16([0; 16]);
let data: &mut SomeDataAlign16 = uefi_function_stub_read(&mut buf.0).unwrap();
assert_eq!(&data.0.0, &[1, 2, 3, 4]);
}
#[test]
fn test_make_boxed_utility() {
let fetch_data_fn = |buf| uefi_function_stub_read(buf);
let data: Box<SomeData> = make_boxed(fetch_data_fn).unwrap();
assert_eq!(&data.0, &[1, 2, 3, 4]);
let fetch_data_fn = |buf| uefi_function_stub_read(buf);
let data: Box<SomeDataAlign16> = make_boxed(fetch_data_fn).unwrap();
assert_eq!(&data.0.0, &[1, 2, 3, 4]);
}
}