blob: 23d750c0c522d9234227dd4cc0b072b36ba63c7d [file] [log] [blame]
// Copyright 2024, The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Rust wrapper for `EFI_IMAGE_LOADING_PROTOCOL`.
use crate::defs::{
EfiGuid, EfiImageLoadingProtocol, GblImageBuffer, GblImageInfo, GblPartitionName,
PARTITION_NAME_LEN_U16,
};
use crate::protocol::{Protocol, ProtocolInfo};
use crate::{
efi_call, map_efi_err, EfiError, EfiResult, EFI_STATUS_ALREADY_STARTED,
EFI_STATUS_BUFFER_TOO_SMALL, EFI_STATUS_INVALID_PARAMETER, EFI_STATUS_NOT_FOUND,
};
use arrayvec::ArrayVec;
use core::mem::size_of;
use core::ptr::null_mut;
use spin::Mutex;
/// GBL_IMAGE_LOADING_PROTOCOL
pub struct GblImageLoadingProtocol;
impl ProtocolInfo for GblImageLoadingProtocol {
type InterfaceType = EfiImageLoadingProtocol;
const GUID: EfiGuid =
EfiGuid::new(0x26f4418b, 0x6cf1, 0x4543, [0x90, 0xbb, 0x55, 0xdd, 0x2c, 0x17, 0x57, 0x9c]);
}
/// Max length of partition name in UTF8 in bytes.
pub const PARTITION_NAME_LEN_U8: usize = size_of::<char>() * PARTITION_NAME_LEN_U16;
impl GblPartitionName {
/// Decodes the UCS2 GblPartitionName using buffer, and returns &str of UTF8 representation.
///
/// Buffer must be big enough to contain UTF8 representation of the UCS2 partition name.
///
/// Maximum partition name as UCS2 is PARTITION_NAME_LEN_U16.
/// And [PARTITION_NAME_LEN_U8] bytes is maximum buffer size needed for UTF8 representation.
///
/// # Result
/// Ok(&str) - On success return UTF8 representation of the partition name
/// Err(EfiError::EFI_STATUS_BUFFER_TOO_SMALL) if provided buffer is too small
/// Err(err) - if error occurred during decoding
pub fn get_str<'a>(&self, buffer_utf8: &'a mut [u8]) -> EfiResult<&'a str> {
let mut index = 0;
for c in char::decode_utf16(self.StrUtf16.iter().copied())
.map(|c_res| c_res.unwrap_or(char::REPLACEMENT_CHARACTER))
.take_while(|c| *c != '\0')
{
if c.len_utf8() <= buffer_utf8[index..].len() {
index += c.encode_utf8(&mut buffer_utf8[index..]).len();
} else {
return Err(EfiError::from(EFI_STATUS_BUFFER_TOO_SMALL));
}
}
// SAFETY:
// _unchecked should be OK here since we wrote each utf8 byte ourselves,
// but it's just an optimization, checked version would be fine also.
unsafe { Ok(core::str::from_utf8_unchecked(&buffer_utf8[..index])) }
}
}
const MAX_ARRAY_SIZE: usize = 100;
static RETURNED_BUFFERS: Mutex<ArrayVec<usize, MAX_ARRAY_SIZE>> = Mutex::new(ArrayVec::new_const());
/// Wrapper class for buffer received with [get_buffer] function.
///
/// Helps to keep track of allocated memory and avoid getting same buffer more than once.
#[derive(Debug, PartialEq)]
pub struct ImageBuffer<'a> {
buffer: &'a mut [u8],
}
impl ImageBuffer<'_> {
// SAFETY:
// `gbl_buffer` must represent valid buffer.
// If `gbl_buffer.Memory` is NULL function will return None.
//
// # Return
// Err(EFI_STATUS_INVALID_PARAMETER) - If `gbl_buffer.Memory` == NULL
// Err(EFI_STATUS_ALREADY_STARTED) - Requested buffer was already returned and is still in use.
// Err(err) - on error
// Ok(_) - on success
unsafe fn new(gbl_buffer: GblImageBuffer) -> EfiResult<ImageBuffer<'static>> {
if gbl_buffer.Memory.is_null() {
return Err(EfiError::from(EFI_STATUS_INVALID_PARAMETER));
}
let addr = gbl_buffer.Memory as usize;
let mut returned_buffers = RETURNED_BUFFERS.lock();
if returned_buffers.contains(&addr) {
return Err(EfiError::from(EFI_STATUS_ALREADY_STARTED));
}
returned_buffers.push(addr);
// SAFETY:
// `gbl_buffer.Memory` is guarantied to be not null
// This code is relying on EFI protocol implementation to provide valid buffer pointer
// to memory region of size `gbl_buffer.SizeBytes`.
Ok(ImageBuffer {
buffer: unsafe {
core::slice::from_raw_parts_mut(gbl_buffer.Memory as *mut u8, gbl_buffer.SizeBytes)
},
})
}
}
impl AsRef<[u8]> for ImageBuffer<'_> {
fn as_ref(&self) -> &[u8] {
&self.buffer
}
}
impl AsMut<[u8]> for ImageBuffer<'_> {
fn as_mut(&mut self) -> &mut [u8] {
&mut self.buffer
}
}
impl Drop for ImageBuffer<'_> {
fn drop(&mut self) {
let mut returned_buffers = RETURNED_BUFFERS.lock();
if let Some(pos) =
returned_buffers.iter().position(|&val| val == self.buffer.as_ptr() as usize)
{
returned_buffers.swap_remove(pos);
}
}
}
// Protocol interface wrappers.
impl Protocol<'_, GblImageLoadingProtocol> {
/// Wrapper of `GBL_IMAGE_LOADING_PROTOCOL.get_buffer()`
///
/// # Return
/// Ok(Some(ImageBuffer)) if buffer was successfully provided,
/// Ok(None) if buffer was not provided
/// Err(EfiError::EFI_STATUS_BUFFER_TOO_SMALL) if provided buffer is too small
/// Err(EfiError::EFI_STATUS_INVALID_PARAMETER) if received buffer is NULL
/// Err(EfiError::EFI_STATUS_ALREADY_STARTED) buffer was already returned and is still in use.
/// Err(err) if `err` occurred
pub fn get_buffer(&self, gbl_image_info: &GblImageInfo) -> EfiResult<ImageBuffer> {
let mut gbl_buffer: GblImageBuffer = Default::default();
// SAFETY:
// `self.interface()?` guarantees self.interface is non-null and points to a valid object
// established by `Protocol::new()`.
// `self.interface` and `gbl_buffer` are input/output parameters, outlive the call and
// will not be retained.
// `gbl_buffer` returned by this call must not overlap, and will be checked by `ImageBuffer`
unsafe {
efi_call!(
self.interface()?.get_buffer,
self.interface,
gbl_image_info,
&mut gbl_buffer
)?;
}
if gbl_buffer.SizeBytes < gbl_image_info.SizeBytes {
return Err(EfiError::from(EFI_STATUS_BUFFER_TOO_SMALL));
}
// SAFETY:
// `gbl_buffer.Memory` must be not null. This checked in `new()` call
// `gbl_buffer.Size` must be valid size of the buffer.
// This protocol is relying on EFI protocol implementation to provide valid buffer pointer
// to memory region of size `gbl_buffer.SizeBytes`.
let image_buffer = unsafe { ImageBuffer::new(gbl_buffer)? };
Ok(image_buffer)
}
/// Wrapper of `GBL_IMAGE_LOADING_PROTOCOL.get_verify_partitions()` special case to get
/// partition_names expected in [get_verify_partitions] call.
///
/// It is a helper function that indicates expected GblPartitionName slice size for
/// [get_verify_partitions] call.
///
/// # Result
/// Err(err) - if error occurred.
/// Ok(len) - will return maximum number of `GblPartitionName`s that will copied in
/// [get_verify_partitions]. Indicating expected GblPartitionName slice size.
pub fn get_verify_partitions_count(&self) -> EfiResult<usize> {
let mut partition_count = 0usize;
// SAFETY:
// `self.interface()?` guarantees self.interface is non-null and points to a valid object
// established by `Protocol::new()`.
// `self.interface` is input parameter, outlives the call, and will not be retained.
// `partition_count` must be set to valid length of `partition_names` array after the call.
// `partition_names` must be valid array of length `partition_count` after the call.
unsafe {
efi_call!(
self.interface()?.get_verify_partitions,
self.interface,
&mut partition_count,
null_mut(),
)?;
}
Ok(partition_count)
}
/// Wrapper of `GBL_IMAGE_LOADING_PROTOCOL.get_verify_partitions()`
///
/// [get_verify_partitions_count] can be used to get expected length for partition_names.
///
/// # Result
/// Err(err) - if error occurred.
/// Ok(len) - will return number of `GblPartitionName`s copied to `partition_names` slice.
pub fn get_verify_partitions(
&self,
partition_names: &mut [GblPartitionName],
) -> EfiResult<usize> {
let partition_count_in: usize = partition_names.len();
let mut partition_count: usize = partition_count_in;
// SAFETY:
// `self.interface()?` guarantees self.interface is non-null and points to a valid object
// established by `Protocol::new()`.
// `self.interface` is input parameter, outlives the call, and will not be retained.
// `partition_count` must be set to valid length of `partition_names` array after the call.
// `partition_names` must be valid array of length `partition_count` after the call.
unsafe {
efi_call!(
self.interface()?.get_verify_partitions,
self.interface,
&mut partition_count,
partition_names.as_mut_ptr(),
)?;
}
Ok(partition_count)
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::{
protocol::image_loading, test::run_test, DeviceHandle, EfiEntry, EfiStatus,
EFI_STATUS_BAD_BUFFER_SIZE, EFI_STATUS_INVALID_PARAMETER, EFI_STATUS_SUCCESS,
};
use core::{ffi::c_void, iter::zip, ptr::null_mut};
const UCS2_STR: [u16; 8] = [0x2603, 0x0073, 0x006e, 0x006f, 0x0077, 0x006d, 0x0061, 0x006e];
const UTF8_STR: &str = "☃snowman";
const PARTITIONS_MAX: usize = 128;
fn get_buffer_utf8() -> [[u8; PARTITION_NAME_LEN_U8]; PARTITIONS_MAX] {
[[0; PARTITION_NAME_LEN_U8]; PARTITIONS_MAX]
}
fn get_printable_utf16() -> Vec<u16> {
(0x0021..0x007e).collect::<Vec<u16>>()
}
fn get_printable_string() -> String {
String::from_utf8((0x21..0x7e).collect::<Vec<u8>>()).unwrap()
}
impl<const N: usize> From<[u16; N]> for GblPartitionName {
fn from(value: [u16; N]) -> Self {
value[..].into()
}
}
impl From<&[u16]> for GblPartitionName {
fn from(value: &[u16]) -> Self {
let mut res: GblPartitionName = Default::default();
res.StrUtf16[..value.len()].copy_from_slice(value);
res
}
}
#[test]
fn test_partition_name_get_str() {
let mut buffer = [0u8; 100];
// empty string
assert_eq!(GblPartitionName::from([0u16]).get_str(&mut buffer).unwrap(), "");
assert_eq!(GblPartitionName::from([0u16]).get_str(&mut buffer).unwrap(), "");
assert_eq!(GblPartitionName::from([0x0000]).get_str(&mut buffer[..0]).unwrap(), "");
// Special characters
assert_eq!(GblPartitionName::from(UCS2_STR).get_str(&mut buffer).unwrap(), UTF8_STR);
// Null character in the middle
assert_eq!(
GblPartitionName::from([0x006d, 0x0075, 0x0000, 0x0073, 0x0069, 0x0063])
.get_str(&mut buffer),
Ok("mu")
);
// Null character at the end
assert_eq!(
GblPartitionName::from([0x006d, 0x0075, 0x0073, 0x0069, 0x0063, 0x0000])
.get_str(&mut buffer),
Ok("music")
);
// exact buffer size
assert_eq!(
GblPartitionName::from([0x006d, 0x0075, 0x0073, 0x0069, 0x0063])
.get_str(&mut buffer[..5]),
Ok("music")
);
assert_eq!(
GblPartitionName::from([0x006d, 0x0075, 0x0000, 0x0073, 0x0069, 0x0063])
.get_str(&mut buffer[..2]),
Ok("mu")
);
}
#[test]
fn test_partition_name_get_str_small_buffer() {
let mut buffer = [0u8; 8];
let partition_name: GblPartitionName = UCS2_STR.into();
assert_eq!(
partition_name.get_str(&mut buffer),
Err(EfiError::from(EFI_STATUS_BUFFER_TOO_SMALL))
);
}
fn generate_protocol<'a, P: ProtocolInfo>(
efi_entry: &'a EfiEntry,
proto: &'a mut P::InterfaceType,
) -> Protocol<'a, P> {
// SAFETY:
// proto is a valid pointer and lasts at least as long as efi_entry.
unsafe { Protocol::<'a, P>::new(DeviceHandle::new(null_mut()), proto, efi_entry) }
}
#[test]
fn test_proto_get_partitions_count() {
unsafe extern "C" fn get_verify_partitions(
_: *mut EfiImageLoadingProtocol,
number_of_partitions: *mut usize,
_: *mut GblPartitionName,
) -> EfiStatus {
assert!(!number_of_partitions.is_null());
// SAFETY
// `number_of_partitions` must be valid pointer to usize
let number_of_partitions = unsafe { number_of_partitions.as_mut() }.unwrap();
*number_of_partitions = 1;
EFI_STATUS_SUCCESS
}
run_test(|image_handle, systab_ptr| {
let mut image_loading = EfiImageLoadingProtocol {
get_verify_partitions: Some(get_verify_partitions),
..Default::default()
};
let efi_entry = EfiEntry { image_handle, systab_ptr };
let protocol = generate_protocol::<image_loading::GblImageLoadingProtocol>(
&efi_entry,
&mut image_loading,
);
let partitions_count = protocol.get_verify_partitions_count().unwrap();
assert_eq!(partitions_count, 1);
});
}
#[test]
fn test_proto_get_partitions_count_error() {
unsafe extern "C" fn get_verify_partitions(
_: *mut EfiImageLoadingProtocol,
_: *mut usize,
_: *mut GblPartitionName,
) -> EfiStatus {
EFI_STATUS_INVALID_PARAMETER
}
run_test(|image_handle, systab_ptr| {
let mut image_loading = EfiImageLoadingProtocol {
get_verify_partitions: Some(get_verify_partitions),
..Default::default()
};
let efi_entry = EfiEntry { image_handle, systab_ptr };
let protocol = generate_protocol::<image_loading::GblImageLoadingProtocol>(
&efi_entry,
&mut image_loading,
);
assert!(protocol.get_verify_partitions_count().is_err());
});
}
#[test]
fn test_proto_get_partitions_len_and_value() {
unsafe extern "C" fn get_verify_partitions(
_: *mut EfiImageLoadingProtocol,
number_of_partitions: *mut usize,
partitions: *mut GblPartitionName,
) -> EfiStatus {
// SAFETY
// `number_of_partitions` must be valid pointer to usize
let number_of_partitions = unsafe { number_of_partitions.as_mut() }.unwrap();
match *number_of_partitions {
0 => *number_of_partitions = 1,
_ => {
// SAFETY
// `partitions` must be valid array of size `number_of_partitions`
let partitions = unsafe {
core::slice::from_raw_parts_mut(partitions, *number_of_partitions)
};
*number_of_partitions = 1;
partitions[0].StrUtf16[..UCS2_STR.len()].copy_from_slice(&UCS2_STR);
}
}
EFI_STATUS_SUCCESS
}
run_test(|image_handle, systab_ptr| {
let mut buffer_utf8 = get_buffer_utf8();
let mut image_loading = EfiImageLoadingProtocol {
get_verify_partitions: Some(get_verify_partitions),
..Default::default()
};
let efi_entry = EfiEntry { image_handle, systab_ptr };
let protocol = generate_protocol::<image_loading::GblImageLoadingProtocol>(
&efi_entry,
&mut image_loading,
);
let mut partitions: [GblPartitionName; 2] = Default::default();
let verify_partitions_len = protocol.get_verify_partitions_count().unwrap();
assert_eq!(verify_partitions_len, 1);
let verify_partitions_len = protocol.get_verify_partitions(&mut partitions).unwrap();
assert_eq!(verify_partitions_len, 1);
assert_eq!(partitions[0].get_str(&mut buffer_utf8[0]), Ok(UTF8_STR));
});
}
#[test]
fn test_proto_get_partitions_zero_len() {
unsafe extern "C" fn get_verify_partitions(
_: *mut EfiImageLoadingProtocol,
number_of_partitions: *mut usize,
_: *mut GblPartitionName,
) -> EfiStatus {
// SAFETY
// `number_of_partitions` must be valid pointer to usize
let number_of_partitions = unsafe { number_of_partitions.as_mut() }.unwrap();
assert_eq!(*number_of_partitions, 0);
*number_of_partitions = 1;
EFI_STATUS_SUCCESS
}
run_test(|image_handle, systab_ptr| {
let mut image_loading = EfiImageLoadingProtocol {
get_verify_partitions: Some(get_verify_partitions),
..Default::default()
};
let efi_entry = EfiEntry { image_handle, systab_ptr };
let protocol = generate_protocol::<image_loading::GblImageLoadingProtocol>(
&efi_entry,
&mut image_loading,
);
let mut partitions: [GblPartitionName; 0] = Default::default();
let verify_partitions_len = protocol.get_verify_partitions(&mut partitions).unwrap();
assert_eq!(verify_partitions_len, 1);
});
}
#[test]
fn test_proto_get_partitions_less_than_buffer() {
unsafe extern "C" fn get_verify_partitions(
_: *mut EfiImageLoadingProtocol,
number_of_partitions: *mut usize,
partitions: *mut GblPartitionName,
) -> EfiStatus {
assert!(!partitions.is_null());
// SAFETY
// `number_of_partitions` must be valid pointer to usize
let number_of_partitions = unsafe { number_of_partitions.as_mut() }.unwrap();
assert!(*number_of_partitions > 0);
// SAFETY
// `partitions` must be valid array of size `number_of_partitions`
let partitions =
unsafe { core::slice::from_raw_parts_mut(partitions, *number_of_partitions) };
partitions[0].StrUtf16[..UCS2_STR.len()].copy_from_slice(&UCS2_STR);
*number_of_partitions = 1;
EFI_STATUS_SUCCESS
}
run_test(|image_handle, systab_ptr| {
let mut buffer_utf8 = get_buffer_utf8();
let mut image_loading = EfiImageLoadingProtocol {
get_verify_partitions: Some(get_verify_partitions),
..Default::default()
};
let efi_entry = EfiEntry { image_handle, systab_ptr };
let protocol = generate_protocol::<image_loading::GblImageLoadingProtocol>(
&efi_entry,
&mut image_loading,
);
let mut partitions: [GblPartitionName; 2] = Default::default();
let verify_partitions_len = protocol.get_verify_partitions(&mut partitions).unwrap();
assert_eq!(verify_partitions_len, 1);
assert_eq!(partitions[0].get_str(&mut buffer_utf8[0]), Ok(UTF8_STR));
});
}
#[test]
fn test_proto_get_partitions_name_max() {
unsafe extern "C" fn get_verify_partitions(
_: *mut EfiImageLoadingProtocol,
number_of_partitions: *mut usize,
partitions: *mut GblPartitionName,
) -> EfiStatus {
let printable_utf16 = get_printable_utf16();
assert!(!partitions.is_null());
// SAFETY
// `number_of_partitions` must be valid pointer to usize
let number_of_partitions = unsafe { number_of_partitions.as_mut() }.unwrap();
assert!(*number_of_partitions > 0);
// SAFETY
// `partitions` must be valid array of size `number_of_partitions`
let partitions =
unsafe { core::slice::from_raw_parts_mut(partitions, *number_of_partitions) };
let partition_names: [GblPartitionName; PARTITIONS_MAX] = (0..PARTITION_NAME_LEN_U16)
.cycle()
.take(PARTITIONS_MAX)
.map(|i| printable_utf16[i..i + PARTITION_NAME_LEN_U16].into())
.collect::<Vec<_>>()
.try_into()
.unwrap();
*number_of_partitions = partition_names.len();
for (p_out, p_gen) in zip(partitions.iter_mut(), partition_names.iter()) {
*p_out = *p_gen;
}
EFI_STATUS_SUCCESS
}
run_test(|image_handle, systab_ptr| {
let mut buffer_utf8 = get_buffer_utf8();
let printable_str = get_printable_string();
let mut image_loading = EfiImageLoadingProtocol {
get_verify_partitions: Some(get_verify_partitions),
..Default::default()
};
let efi_entry = EfiEntry { image_handle, systab_ptr };
let protocol = generate_protocol::<image_loading::GblImageLoadingProtocol>(
&efi_entry,
&mut image_loading,
);
let mut partitions = [GblPartitionName::default(); PARTITIONS_MAX];
let expected_strs: Vec<&str> = (0..PARTITION_NAME_LEN_U16)
.cycle()
.take(PARTITIONS_MAX)
.map(|i| &printable_str[i..i + PARTITION_NAME_LEN_U16])
.collect();
let verify_partitions_len = protocol.get_verify_partitions(&mut partitions).unwrap();
assert_eq!(verify_partitions_len, PARTITIONS_MAX);
assert!(zip(partitions.iter(), expected_strs.iter())
.all(|(p, expected_str)| { p.get_str(&mut buffer_utf8[0]) == Ok(*expected_str) }));
});
}
#[test]
fn test_proto_get_partitions() {
unsafe extern "C" fn get_verify_partitions(
_: *mut EfiImageLoadingProtocol,
number_of_partitions: *mut usize,
partitions: *mut GblPartitionName,
) -> EfiStatus {
assert!(!partitions.is_null());
// SAFETY
// `number_of_partitions` must be valid pointer to usize
let number_of_partitions = unsafe { number_of_partitions.as_mut() }.unwrap();
assert!(*number_of_partitions > 0);
// SAFETY
// `partitions` must be valid array of size `number_of_partitions`
let partitions =
unsafe { core::slice::from_raw_parts_mut(partitions, *number_of_partitions) };
partitions[0].StrUtf16[..UCS2_STR.len()].copy_from_slice(&UCS2_STR);
partitions[1].StrUtf16[..UCS2_STR.len() - 1].copy_from_slice(&UCS2_STR[1..]);
*number_of_partitions = 2;
EFI_STATUS_SUCCESS
}
run_test(|image_handle, systab_ptr| {
let mut buffer_utf8 = get_buffer_utf8();
let mut image_loading = EfiImageLoadingProtocol {
get_verify_partitions: Some(get_verify_partitions),
..Default::default()
};
let efi_entry = EfiEntry { image_handle, systab_ptr };
let protocol = generate_protocol::<image_loading::GblImageLoadingProtocol>(
&efi_entry,
&mut image_loading,
);
let mut partitions: [GblPartitionName; 2] = Default::default();
let verify_partitions_len = protocol.get_verify_partitions(&mut partitions).unwrap();
assert_eq!(verify_partitions_len, 2);
let mut char_idx = UTF8_STR.char_indices();
char_idx.next();
let (next_char_pos, _) = char_idx.next().unwrap();
assert_eq!(partitions[0].get_str(&mut buffer_utf8[0]), Ok(UTF8_STR));
assert_eq!(partitions[1].get_str(&mut buffer_utf8[1]), Ok(&UTF8_STR[next_char_pos..]));
});
}
#[test]
fn test_proto_get_partitions_empty() {
unsafe extern "C" fn get_verify_partitions(
_: *mut EfiImageLoadingProtocol,
number_of_partitions: *mut usize,
partitions: *mut GblPartitionName,
) -> EfiStatus {
assert!(!partitions.is_null());
// SAFETY
// `number_of_partitions` must be valid pointer to usize
let number_of_partitions = unsafe { number_of_partitions.as_mut() }.unwrap();
assert!(*number_of_partitions > 0);
*number_of_partitions = 0;
EFI_STATUS_SUCCESS
}
run_test(|image_handle, systab_ptr| {
let mut image_loading = EfiImageLoadingProtocol {
get_verify_partitions: Some(get_verify_partitions),
..Default::default()
};
let efi_entry = EfiEntry { image_handle, systab_ptr };
let protocol = generate_protocol::<image_loading::GblImageLoadingProtocol>(
&efi_entry,
&mut image_loading,
);
let mut partitions: [GblPartitionName; 2] = Default::default();
let verify_partitions_len = protocol.get_verify_partitions(&mut partitions).unwrap();
assert_eq!(verify_partitions_len, 0);
});
}
#[test]
fn test_proto_get_partitions_error() {
unsafe extern "C" fn get_verify_partitions(
_: *mut EfiImageLoadingProtocol,
_: *mut usize,
_: *mut GblPartitionName,
) -> EfiStatus {
EFI_STATUS_BAD_BUFFER_SIZE
}
run_test(|image_handle, systab_ptr| {
let mut image_loading = EfiImageLoadingProtocol {
get_verify_partitions: Some(get_verify_partitions),
..Default::default()
};
let efi_entry = EfiEntry { image_handle, systab_ptr };
let protocol = generate_protocol::<image_loading::GblImageLoadingProtocol>(
&efi_entry,
&mut image_loading,
);
let mut partitions: [GblPartitionName; 1] = Default::default();
assert!(protocol.get_verify_partitions(&mut partitions).is_err());
});
}
#[test]
fn test_proto_get_buffer_error() {
unsafe extern "C" fn get_buffer(
_: *mut EfiImageLoadingProtocol,
_: *const GblImageInfo,
_: *mut GblImageBuffer,
) -> EfiStatus {
EFI_STATUS_INVALID_PARAMETER
}
run_test(|image_handle, systab_ptr| {
let gbl_image_info: GblImageInfo = Default::default();
let mut image_loading =
EfiImageLoadingProtocol { get_buffer: Some(get_buffer), ..Default::default() };
let efi_entry = EfiEntry { image_handle, systab_ptr };
let protocol = generate_protocol::<image_loading::GblImageLoadingProtocol>(
&efi_entry,
&mut image_loading,
);
assert!(protocol.get_buffer(&gbl_image_info).is_err());
});
}
#[test]
fn test_proto_get_buffer_not_provided() {
unsafe extern "C" fn get_buffer(
_: *mut EfiImageLoadingProtocol,
image_info: *const GblImageInfo,
buffer: *mut GblImageBuffer,
) -> EfiStatus {
assert!(!image_info.is_null());
assert!(!buffer.is_null());
// SAFETY
// `buffer` must be valid pointer to `GblImageBuffer`
let buffer = unsafe { buffer.as_mut() }.unwrap();
buffer.Memory = null_mut();
buffer.SizeBytes = 10;
EFI_STATUS_SUCCESS
}
run_test(|image_handle, systab_ptr| {
let gbl_image_info: GblImageInfo = Default::default();
let mut image_loading =
EfiImageLoadingProtocol { get_buffer: Some(get_buffer), ..Default::default() };
let efi_entry = EfiEntry { image_handle, systab_ptr };
let protocol = generate_protocol::<image_loading::GblImageLoadingProtocol>(
&efi_entry,
&mut image_loading,
);
let buffer_res = protocol.get_buffer(&gbl_image_info);
assert_eq!(buffer_res, Err(EfiError::from(EFI_STATUS_INVALID_PARAMETER)));
});
}
fn get_memory() -> Box<[u8; 100]> {
Box::new([0; 100])
}
#[test]
fn test_proto_get_buffer_zero_size() {
unsafe extern "C" fn get_buffer(
_: *mut EfiImageLoadingProtocol,
image_info: *const GblImageInfo,
buffer: *mut GblImageBuffer,
) -> EfiStatus {
assert!(!image_info.is_null());
assert!(!buffer.is_null());
// SAFETY
// `buffer` must be valid pointer to `GblImageBuffer`
let buffer = unsafe { buffer.as_mut() }.unwrap();
buffer.Memory = Box::leak(get_memory()).as_mut_ptr() as *mut c_void;
buffer.SizeBytes = 0;
EFI_STATUS_SUCCESS
}
run_test(|image_handle, systab_ptr| {
let gbl_image_info: GblImageInfo = Default::default();
let mut image_loading =
EfiImageLoadingProtocol { get_buffer: Some(get_buffer), ..Default::default() };
let efi_entry = EfiEntry { image_handle, systab_ptr };
let protocol = generate_protocol::<image_loading::GblImageLoadingProtocol>(
&efi_entry,
&mut image_loading,
);
let buffer = protocol.get_buffer(&gbl_image_info).unwrap();
assert!(buffer.as_ref().is_empty());
});
}
#[test]
fn test_proto_get_buffer_small() {
unsafe extern "C" fn get_buffer(
_: *mut EfiImageLoadingProtocol,
image_info: *const GblImageInfo,
buffer: *mut GblImageBuffer,
) -> EfiStatus {
assert!(!image_info.is_null());
// SAFETY
// `image_info` must be valid pointer to `GblImageInfo`
let image_info = unsafe { image_info.as_ref() }.unwrap();
assert!(!buffer.is_null());
// SAFETY
// `buffer` must be valid pointer to `GblImageBuffer`
let buffer = unsafe { buffer.as_mut() }.unwrap();
buffer.Memory = Box::leak(get_memory()).as_mut_ptr() as *mut c_void;
buffer.SizeBytes = image_info.SizeBytes - 1;
EFI_STATUS_SUCCESS
}
run_test(|image_handle, systab_ptr| {
let gbl_image_info: GblImageInfo =
GblImageInfo { ImageType: [0; PARTITION_NAME_LEN_U16], SizeBytes: 10 };
let mut image_loading =
EfiImageLoadingProtocol { get_buffer: Some(get_buffer), ..Default::default() };
let efi_entry = EfiEntry { image_handle, systab_ptr };
let protocol = generate_protocol::<image_loading::GblImageLoadingProtocol>(
&efi_entry,
&mut image_loading,
);
let res = protocol.get_buffer(&gbl_image_info);
assert_eq!(res, Err(EfiError::from(EFI_STATUS_BUFFER_TOO_SMALL)));
});
}
#[test]
fn test_proto_get_buffer() {
unsafe extern "C" fn get_buffer(
_: *mut EfiImageLoadingProtocol,
image_info: *const GblImageInfo,
buffer: *mut GblImageBuffer,
) -> EfiStatus {
assert!(!image_info.is_null());
assert!(!buffer.is_null());
// SAFETY
// `buffer` must be valid pointer to `GblImageBuffer`
let buffer = unsafe { buffer.as_mut() }.unwrap();
let mem = get_memory();
buffer.SizeBytes = mem.len();
buffer.Memory = Box::leak(mem).as_mut_ptr() as *mut c_void;
EFI_STATUS_SUCCESS
}
run_test(|image_handle, systab_ptr| {
let gbl_image_info: GblImageInfo =
GblImageInfo { ImageType: [0; PARTITION_NAME_LEN_U16], SizeBytes: 100 };
let mut image_loading =
EfiImageLoadingProtocol { get_buffer: Some(get_buffer), ..Default::default() };
let efi_entry = EfiEntry { image_handle, systab_ptr };
let protocol = generate_protocol::<image_loading::GblImageLoadingProtocol>(
&efi_entry,
&mut image_loading,
);
let buf = protocol.get_buffer(&gbl_image_info).unwrap();
assert_ne!(buf.as_ref().as_ptr(), null_mut());
assert_eq!(buf.as_ref().len(), 100);
});
}
#[test]
fn test_proto_get_buffer_image_type() {
const IMAGE_TYPE_STR: &'static str = "test";
unsafe extern "C" fn get_buffer(
_: *mut EfiImageLoadingProtocol,
image_info: *const GblImageInfo,
buffer: *mut GblImageBuffer,
) -> EfiStatus {
assert!(!image_info.is_null());
// SAFETY
// `image_info` must be valid pointer to `GblImageInfo`
let image_info = unsafe { image_info.as_ref() }.unwrap();
assert!(!buffer.is_null());
// SAFETY
// `buffer` must be valid pointer to `GblImageBuffer`
let buffer = unsafe { buffer.as_mut() }.unwrap();
let mut buffer_utf8 = [0u8; 100];
assert_eq!(
GblPartitionName::from(image_info.ImageType).get_str(&mut buffer_utf8).unwrap(),
IMAGE_TYPE_STR
);
let mem = get_memory();
buffer.SizeBytes = mem.len();
buffer.Memory = Box::leak(mem).as_mut_ptr() as *mut c_void;
EFI_STATUS_SUCCESS
}
run_test(|image_handle, systab_ptr| {
let mut image_type = [0u16; PARTITION_NAME_LEN_U16];
image_type[..4].copy_from_slice(&IMAGE_TYPE_STR.encode_utf16().collect::<Vec<u16>>());
let gbl_image_info: GblImageInfo =
GblImageInfo { ImageType: image_type, SizeBytes: 100 };
let mut image_loading =
EfiImageLoadingProtocol { get_buffer: Some(get_buffer), ..Default::default() };
let efi_entry = EfiEntry { image_handle, systab_ptr };
let protocol = generate_protocol::<image_loading::GblImageLoadingProtocol>(
&efi_entry,
&mut image_loading,
);
let res = protocol.get_buffer(&gbl_image_info);
assert!(res.is_ok());
});
}
#[test]
fn test_proto_get_buffer_double_call() {
unsafe extern "C" fn get_buffer(
_: *mut EfiImageLoadingProtocol,
image_info: *const GblImageInfo,
buffer: *mut GblImageBuffer,
) -> EfiStatus {
assert!(!image_info.is_null());
assert!(!buffer.is_null());
// SAFETY
// `buffer` must be valid pointer to `GblImageBuffer`
let buffer = unsafe { buffer.as_mut() }.unwrap();
let mem = get_memory();
buffer.SizeBytes = mem.len();
buffer.Memory = 0x1000 as *mut c_void;
EFI_STATUS_SUCCESS
}
run_test(|image_handle, systab_ptr| {
let gbl_image_info: GblImageInfo =
GblImageInfo { ImageType: [0; PARTITION_NAME_LEN_U16], SizeBytes: 100 };
let mut image_loading =
EfiImageLoadingProtocol { get_buffer: Some(get_buffer), ..Default::default() };
let efi_entry = EfiEntry { image_handle, systab_ptr };
let protocol = generate_protocol::<image_loading::GblImageLoadingProtocol>(
&efi_entry,
&mut image_loading,
);
let _buf = protocol.get_buffer(&gbl_image_info).unwrap();
assert_eq!(
protocol.get_buffer(&gbl_image_info),
Err(EfiError::from(EFI_STATUS_ALREADY_STARTED))
);
});
}
#[test]
fn test_proto_get_buffer_double_call_after_drop() {
unsafe extern "C" fn get_buffer(
_: *mut EfiImageLoadingProtocol,
image_info: *const GblImageInfo,
buffer: *mut GblImageBuffer,
) -> EfiStatus {
assert!(!image_info.is_null());
assert!(!buffer.is_null());
// SAFETY
// `buffer` must be valid pointer to `GblImageBuffer`
let buffer = unsafe { buffer.as_mut() }.unwrap();
let mem = get_memory();
buffer.SizeBytes = mem.len();
buffer.Memory = 0x2000 as *mut c_void;
EFI_STATUS_SUCCESS
}
run_test(|image_handle, systab_ptr| {
let gbl_image_info: GblImageInfo =
GblImageInfo { ImageType: [0; PARTITION_NAME_LEN_U16], SizeBytes: 100 };
let mut image_loading =
EfiImageLoadingProtocol { get_buffer: Some(get_buffer), ..Default::default() };
let efi_entry = EfiEntry { image_handle, systab_ptr };
let protocol = generate_protocol::<image_loading::GblImageLoadingProtocol>(
&efi_entry,
&mut image_loading,
);
protocol.get_buffer(&gbl_image_info).unwrap();
protocol.get_buffer(&gbl_image_info).unwrap();
});
}
#[test]
#[should_panic]
fn test_proto_get_buffer_too_many_times() {
unsafe extern "C" fn get_buffer(
_: *mut EfiImageLoadingProtocol,
image_info: *const GblImageInfo,
buffer: *mut GblImageBuffer,
) -> EfiStatus {
assert!(!image_info.is_null());
assert!(!buffer.is_null());
// SAFETY
// `buffer` must be valid pointer to `GblImageBuffer`
let buffer = unsafe { buffer.as_mut() }.unwrap();
let mem = get_memory();
buffer.SizeBytes = mem.len();
// Make sure to return different memory
buffer.Memory = Box::leak(get_memory()).as_mut_ptr() as *mut c_void;
EFI_STATUS_SUCCESS
}
run_test(|image_handle, systab_ptr| {
let gbl_image_info: GblImageInfo =
GblImageInfo { ImageType: [0; PARTITION_NAME_LEN_U16], SizeBytes: 100 };
let mut image_loading =
EfiImageLoadingProtocol { get_buffer: Some(get_buffer), ..Default::default() };
let efi_entry = EfiEntry { image_handle, systab_ptr };
let protocol = generate_protocol::<image_loading::GblImageLoadingProtocol>(
&efi_entry,
&mut image_loading,
);
let mut keep_alive: Vec<ImageBuffer> = vec![];
for _ in 1..=MAX_ARRAY_SIZE + 1 {
keep_alive.push(protocol.get_buffer(&gbl_image_info).unwrap());
}
});
}
}