> {
+ // Search by protocol.
+ let search_type = SearchType::from_proto::();
+
+ // Determine how much we need to allocate.
+ let num_handles = match locate_handle(search_type, &mut []) {
+ Err(err) => {
+ if err.status() == Status::BUFFER_TOO_SMALL {
+ err.data().expect("error data is missing")
+ } else {
+ return Err(err.to_err_without_payload());
+ }
+ }
+ // This should never happen: if no handles match the search then a
+ // `NOT_FOUND` error should be returned.
+ Ok(_) => panic!("locate_handle should not return success with empty buffer"),
+ };
+
+ // Allocate a large enough buffer without pointless initialization.
+ let mut handles = Vec::with_capacity(num_handles);
+
+ // Perform the search.
+ let num_handles = locate_handle(search_type, handles.spare_capacity_mut())
+ .discard_errdata()?
+ .len();
+
+ // Mark the returned number of elements as initialized.
+ unsafe {
+ handles.set_len(num_handles);
+ }
+
+ // Emit output, with warnings
+ Ok(handles)
+}
+
+/// Find an arbitrary handle that supports a particular [`Protocol`]. Returns
+/// [`NOT_FOUND`] if no handles support the protocol.
+///
+/// This method is a convenient wrapper around [`locate_handle_buffer`] for
+/// getting just one handle. This is useful when you don't care which handle the
+/// protocol is opened on. For example, [`DevicePathToText`] isn't tied to a
+/// particular device, so only a single handle is expected to exist.
+///
+/// [`NOT_FOUND`]: Status::NOT_FOUND
+/// [`DevicePathToText`]: uefi::proto::device_path::text::DevicePathToText
+///
+/// # Example
+///
+/// ```
+/// use uefi::proto::device_path::text::DevicePathToText;
+/// use uefi::{boot, Handle};
+/// # use uefi::Result;
+///
+/// # fn get_fake_val() -> T { todo!() }
+/// # fn test() -> Result {
+/// # let image_handle: Handle = get_fake_val();
+/// let handle = boot::get_handle_for_protocol::()?;
+/// let device_path_to_text = boot::open_protocol_exclusive::(handle)?;
+/// # Ok(())
+/// # }
+/// ```
+///
+/// # Errors
+///
+/// * [`Status::NOT_FOUND`]: no matching handle.
+/// * [`Status::OUT_OF_RESOURCES`]: out of memory.
+pub fn get_handle_for_protocol() -> Result {
+ locate_handle_buffer(SearchType::ByProtocol(&P::GUID))?
+ .first()
+ .cloned()
+ .ok_or_else(|| Status::NOT_FOUND.into())
+}
+
+/// Opens a protocol interface for a handle.
+///
+/// See also [`open_protocol_exclusive`], which provides a safe subset of this
+/// functionality.
+///
+/// This function attempts to get the protocol implementation of a handle, based
+/// on the [protocol GUID].
+///
+/// See [`OpenProtocolParams`] and [`OpenProtocolAttributes`] for details of the
+/// input parameters.
+///
+/// If successful, a [`ScopedProtocol`] is returned that will automatically
+/// close the protocol interface when dropped.
+///
+/// [protocol GUID]: uefi::data_types::Identify::GUID
+///
+/// # Safety
+///
+/// This function is unsafe because it can be used to open a protocol in ways
+/// that don't get tracked by the UEFI implementation. This could allow the
+/// protocol to be removed from a handle, or for the handle to be deleted
+/// entirely, while a reference to the protocol is still active. The caller is
+/// responsible for ensuring that the handle and protocol remain valid until the
+/// `ScopedProtocol` is dropped.
+///
+/// # Errors
+///
+/// * [`Status::INVALID_PARAMETER`]: an invalid combination of `params` and
+/// `attributes` was provided.
+/// * [`Status::UNSUPPORTED`]: the handle does not support the protocol.
+/// * [`Status::ACCESS_DENIED`] or [`Status::ALREADY_STARTED`]: the protocol is
+/// already open in a way that is incompatible with the new request.
+pub unsafe fn open_protocol(
+ params: OpenProtocolParams,
+ attributes: OpenProtocolAttributes,
+) -> Result> {
+ let bt = boot_services_raw_panicking();
+ let bt = unsafe { bt.as_ref() };
+
+ let mut interface = ptr::null_mut();
+ (bt.open_protocol)(
+ params.handle.as_ptr(),
+ &P::GUID,
+ &mut interface,
+ params.agent.as_ptr(),
+ Handle::opt_to_ptr(params.controller),
+ attributes as u32,
+ )
+ .to_result_with_val(|| ScopedProtocol {
+ interface: NonNull::new(P::mut_ptr_from_ffi(interface)),
+ open_params: params,
+ })
+}
+
+/// Opens a protocol interface for a handle in exclusive mode.
+///
+/// If successful, a [`ScopedProtocol`] is returned that will automatically
+/// close the protocol interface when dropped.
+///
+/// # Errors
+///
+/// * [`Status::UNSUPPORTED`]: the handle does not support the protocol.
+/// * [`Status::ACCESS_DENIED`]: the protocol is already open in a way that is
+/// incompatible with the new request.
+pub fn open_protocol_exclusive(
+ handle: Handle,
+) -> Result> {
+ // Safety: opening in exclusive mode with the correct agent
+ // handle set ensures that the protocol cannot be modified or
+ // removed while it is open, so this usage is safe.
+ unsafe {
+ open_protocol::(
+ OpenProtocolParams {
+ handle,
+ agent: image_handle(),
+ controller: None,
+ },
+ OpenProtocolAttributes::Exclusive,
+ )
+ }
+}
+
+/// Tests whether a handle supports a protocol.
+///
+/// Returns `Ok(true)` if the handle supports the protocol, `Ok(false)` if not.
+///
+/// # Errors
+///
+/// * [`Status::INVALID_PARAMETER`]: one of the handles in `params` is invalid.
+pub fn test_protocol(params: OpenProtocolParams) -> Result {
+ const TEST_PROTOCOL: u32 = 0x04;
+
+ let bt = boot_services_raw_panicking();
+ let bt = unsafe { bt.as_ref() };
+
+ let mut interface = ptr::null_mut();
+ let status = unsafe {
+ (bt.open_protocol)(
+ params.handle.as_ptr(),
+ &P::GUID,
+ &mut interface,
+ params.agent.as_ptr(),
+ Handle::opt_to_ptr(params.controller),
+ TEST_PROTOCOL,
+ )
+ };
+
+ match status {
+ Status::SUCCESS => Ok(true),
+ Status::UNSUPPORTED => Ok(false),
+ _ => Err(Error::from(status)),
+ }
+}
+
+/// Loads a UEFI image into memory and return a [`Handle`] to the image.
+///
+/// There are two ways to load the image: by copying raw image data
+/// from a source buffer, or by loading the image via the
+/// [`SimpleFileSystem`] protocol. See [`LoadImageSource`] for more
+/// details of the `source` parameter.
+///
+/// The `parent_image_handle` is used to initialize the
+/// `parent_handle` field of the [`LoadedImage`] protocol for the
+/// image.
+///
+/// If the image is successfully loaded, a [`Handle`] supporting the
+/// [`LoadedImage`] and [`LoadedImageDevicePath`] protocols is returned. The
+/// image can be started with [`start_image`] and unloaded with
+/// [`unload_image`].
+///
+/// # Errors
+///
+/// * [`Status::INVALID_PARAMETER`]: `source` contains an invalid value.
+/// * [`Status::UNSUPPORTED`]: the image type is not supported.
+/// * [`Status::OUT_OF_RESOURCES`]: insufficient resources to load the image.
+/// * [`Status::LOAD_ERROR`]: the image is invalid.
+/// * [`Status::DEVICE_ERROR`]: failed to load image due to a read error.
+/// * [`Status::ACCESS_DENIED`]: failed to load image due to a security policy.
+/// * [`Status::SECURITY_VIOLATION`]: a security policy specifies that the image
+/// should not be started.
+pub fn load_image(parent_image_handle: Handle, source: LoadImageSource) -> Result {
+ let bt = boot_services_raw_panicking();
+ let bt = unsafe { bt.as_ref() };
+
+ let (boot_policy, device_path, source_buffer, source_size) = source.to_ffi_params();
+
+ let mut image_handle = ptr::null_mut();
+ unsafe {
+ (bt.load_image)(
+ boot_policy.into(),
+ parent_image_handle.as_ptr(),
+ device_path.cast(),
+ source_buffer,
+ source_size,
+ &mut image_handle,
+ )
+ .to_result_with_val(
+ // OK to unwrap: image handle is non-null for Status::SUCCESS.
+ || Handle::from_ptr(image_handle).unwrap(),
+ )
+ }
+}
+
+/// Unloads a UEFI image.
+///
+/// # Errors
+///
+/// * [`Status::UNSUPPORTED`]: the image has been started, and does not support unload.
+/// * [`Status::INVALID_PARAMETER`]: `image_handle` is not valid.
+pub fn unload_image(image_handle: Handle) -> Result {
+ let bt = boot_services_raw_panicking();
+ let bt = unsafe { bt.as_ref() };
+
+ unsafe { (bt.unload_image)(image_handle.as_ptr()) }.to_result()
+}
+
+/// Transfers control to a loaded image's entry point.
+///
+/// # Errors
+///
+/// * [`Status::INVALID_PARAMETER`]: `image_handle` is not valid, or the image
+/// has already been initialized with `start_image`.
+/// * [`Status::SECURITY_VIOLATION`]: a security policy specifies that the image
+/// should not be started.
+pub fn start_image(image_handle: Handle) -> Result {
+ let bt = boot_services_raw_panicking();
+ let bt = unsafe { bt.as_ref() };
+
+ // TODO: implement returning exit data to the caller.
+ let mut exit_data_size: usize = 0;
+ let mut exit_data: *mut u16 = ptr::null_mut();
+
+ unsafe {
+ (bt.start_image)(image_handle.as_ptr(), &mut exit_data_size, &mut exit_data).to_result()
+ }
+}
+
+/// Exits the UEFI application and returns control to the UEFI component
+/// that started the UEFI application.
+///
+/// # Safety
+///
+/// The caller must ensure that resources owned by the application are properly
+/// cleaned up.
+///
+/// Note that event callbacks installed by the application are not automatically
+/// uninstalled. If such a callback is invoked after exiting the application,
+/// the function's code may no longer be loaded in memory, leading to a crash or
+/// other unexpected behavior.
+pub unsafe fn exit(
+ image_handle: Handle,
+ exit_status: Status,
+ exit_data_size: usize,
+ exit_data: *mut Char16,
+) -> ! {
+ let bt = boot_services_raw_panicking();
+ let bt = unsafe { bt.as_ref() };
+
+ (bt.exit)(
+ image_handle.as_ptr(),
+ exit_status,
+ exit_data_size,
+ exit_data.cast(),
+ )
+}
+
+/// Get the current memory map and exit boot services.
+unsafe fn get_memory_map_and_exit_boot_services(buf: &mut [u8]) -> Result {
+ let bt = boot_services_raw_panicking();
+ let bt = unsafe { bt.as_ref() };
+
+ // Get the memory map.
+ let memory_map = get_memory_map(buf)?;
+
+ // Try to exit boot services using the memory map key. Note that after
+ // the first call to `exit_boot_services`, there are restrictions on
+ // what boot services functions can be called. In UEFI 2.8 and earlier,
+ // only `get_memory_map` and `exit_boot_services` are allowed. Starting
+ // in UEFI 2.9 other memory allocation functions may also be called.
+ (bt.exit_boot_services)(image_handle().as_ptr(), memory_map.map_key.0)
+ .to_result_with_val(|| memory_map)
+}
+
+/// Exit UEFI boot services.
+///
+/// After this function completes, UEFI hands over control of the hardware
+/// to the executing OS loader, which implies that the UEFI boot services
+/// are shut down and cannot be used anymore. Only UEFI configuration tables
+/// and run-time services can be used.
+///
+/// The memory map at the time of exiting boot services returned. The map is
+/// backed by a pool allocation of the given `memory_type`. Since the boot
+/// services function to free that memory is no longer available after calling
+/// `exit_boot_services`, the allocation will not be freed on drop.
+///
+/// Note that once the boot services are exited, associated loggers and
+/// allocators can't use the boot services anymore. For the corresponding
+/// abstractions provided by this crate (see the [`helpers`] module),
+/// invoking this function will automatically disable them. If the
+/// `global_allocator` feature is enabled, attempting to use the allocator
+/// after exiting boot services will panic.
+///
+/// # Safety
+///
+/// The caller is responsible for ensuring that no references to
+/// boot-services data remain. A non-exhaustive list of resources to check:
+///
+/// * All protocols will be invalid after exiting boot services. This
+/// includes the [`Output`] protocols attached to stdout/stderr. The
+/// caller must ensure that no protocol references remain.
+/// * The pool allocator is not usable after exiting boot services. Types
+/// such as [`PoolString`] which call [`free_pool`] on drop
+/// must be cleaned up before calling `exit_boot_services`, or leaked to
+/// avoid drop ever being called.
+/// * All data in the memory map marked as
+/// [`MemoryType::BOOT_SERVICES_CODE`] and
+/// [`MemoryType::BOOT_SERVICES_DATA`] will become free memory.
+///
+/// # Errors
+///
+/// This function will fail if it is unable to allocate memory for
+/// the memory map, if it fails to retrieve the memory map, or if
+/// exiting boot services fails (with up to one retry).
+///
+/// All errors are treated as unrecoverable because the system is
+/// now in an undefined state. Rather than returning control to the
+/// caller, the system will be reset.
+///
+/// [`helpers`]: crate::helpers
+/// [`Output`]: crate::proto::console::text::Output
+/// [`PoolString`]: crate::proto::device_path::text::PoolString
+#[must_use]
+pub unsafe fn exit_boot_services(memory_type: MemoryType) -> MemoryMapOwned {
+ crate::helpers::exit();
+
+ let mut buf = MemoryMapBackingMemory::new(memory_type).expect("Failed to allocate memory");
+
+ // Calling `exit_boot_services` can fail if the memory map key is not
+ // current. Retry a second time if that occurs. This matches the
+ // behavior of the Linux kernel:
+ // https://github.com/torvalds/linux/blob/e544a0743/drivers/firmware/efi/libstub/efi-stub-helper.c#L375
+ let mut status = Status::ABORTED;
+ for _ in 0..2 {
+ match unsafe { get_memory_map_and_exit_boot_services(buf.as_mut_slice()) } {
+ Ok(memory_map) => {
+ return MemoryMapOwned::from_initialized_mem(buf, memory_map);
+ }
+ Err(err) => {
+ log::error!("Error retrieving the memory map for exiting the boot services");
+ status = err.status()
+ }
+ }
+ }
+
+ // Failed to exit boot services.
+ log::warn!("Resetting the machine");
+ runtime::reset(ResetType::COLD, status, None);
+}
+
+/// Adds, updates, or removes a configuration table entry
+/// from the EFI System Table.
+///
+/// # Safety
+///
+/// When installing or updating a configuration table, the data pointed to by
+/// `table_ptr` must be a pool allocation of type
+/// [`RUNTIME_SERVICES_DATA`]. Once this table has been installed, the caller
+/// should not modify or free the data.
+///
+/// [`RUNTIME_SERVICES_DATA`]: MemoryType::RUNTIME_SERVICES_DATA
+///
+/// # Errors
+///
+/// * [`Status::NOT_FOUND`]: tried to delete a nonexistent entry.
+/// * [`Status::OUT_OF_RESOURCES`]: out of memory.
+pub unsafe fn install_configuration_table(
+ guid_entry: &'static Guid,
+ table_ptr: *const c_void,
+) -> Result {
+ let bt = boot_services_raw_panicking();
+ let bt = unsafe { bt.as_ref() };
+
+ (bt.install_configuration_table)(guid_entry, table_ptr).to_result()
+}
+
+/// Sets the watchdog timer.
+///
+/// UEFI will start a 5-minute countdown after an UEFI image is loaded. The
+/// image must either successfully load an OS and exit boot services in that
+/// time, or disable the watchdog.
+///
+/// Otherwise, the firmware will log the event using the provided numeric
+/// code and data, then reset the system.
+///
+/// This function allows you to change the watchdog timer's timeout to a
+/// certain amount of seconds or to disable the watchdog entirely. It also
+/// allows you to change what will be logged when the timer expires.
+///
+/// The watchdog codes from 0 to 0xffff (65535) are reserved for internal
+/// firmware use. Higher values can be used freely by applications.
+///
+/// If provided, the watchdog data must be a null-terminated string optionally
+/// followed by other binary data.
+///
+/// # Errors
+///
+/// * [`Status::INVALID_PARAMETER`]: `watchdog_code` is invalid.
+/// * [`Status::UNSUPPORTED`]: the system does not have a watchdog timer.
+/// * [`Status::DEVICE_ERROR`]: the watchdog timer could not be set due to a
+/// hardware error.
+pub fn set_watchdog_timer(
+ timeout_in_seconds: usize,
+ watchdog_code: u64,
+ data: Option<&mut [u16]>,
+) -> Result {
+ let bt = boot_services_raw_panicking();
+ let bt = unsafe { bt.as_ref() };
+
+ let (data_len, data) = data
+ .map(|d| {
+ assert!(
+ d.contains(&0),
+ "Watchdog data must start with a null-terminated string"
+ );
+ (d.len(), d.as_mut_ptr())
+ })
+ .unwrap_or((0, ptr::null_mut()));
+
+ unsafe { (bt.set_watchdog_timer)(timeout_in_seconds, watchdog_code, data_len, data) }
+ .to_result()
+}
+
+/// Stalls execution for the given number of microseconds.
+pub fn stall(microseconds: usize) {
+ let bt = boot_services_raw_panicking();
+ let bt = unsafe { bt.as_ref() };
+
+ unsafe {
+ // No error conditions are defined in the spec for this function, so
+ // ignore the status.
+ let _ = (bt.stall)(microseconds);
+ }
+}
+
+/// Retrieves a [`SimpleFileSystem`] protocol associated with the device the given
+/// image was loaded from.
+///
+/// # Errors
+///
+/// This function can return errors from [`open_protocol_exclusive`] and
+/// [`locate_device_path`]. See those functions for more details.
+///
+/// * [`Status::INVALID_PARAMETER`]
+/// * [`Status::UNSUPPORTED`]
+/// * [`Status::ACCESS_DENIED`]
+/// * [`Status::ALREADY_STARTED`]
+/// * [`Status::NOT_FOUND`]
+pub fn get_image_file_system(image_handle: Handle) -> Result> {
+ let loaded_image = open_protocol_exclusive::(image_handle)?;
+
+ let device_handle = loaded_image
+ .device()
+ .ok_or(Error::new(Status::UNSUPPORTED, ()))?;
+ let device_path = open_protocol_exclusive::(device_handle)?;
+
+ let device_handle = locate_device_path::(&mut &*device_path)?;
+
+ open_protocol_exclusive(device_handle)
+}
+
+/// Protocol interface [`Guids`][Guid] that are installed on a [`Handle`] as
+/// returned by [`protocols_per_handle`].
+#[derive(Debug)]
+pub struct ProtocolsPerHandle {
+ protocols: NonNull<*const Guid>,
+ count: usize,
+}
+
+impl Drop for ProtocolsPerHandle {
+ fn drop(&mut self) {
+ let _ = unsafe { free_pool(self.protocols.cast::()) };
+ }
+}
+
+impl Deref for ProtocolsPerHandle {
+ type Target = [&'static Guid];
+
+ fn deref(&self) -> &Self::Target {
+ let ptr: *const &'static Guid = self.protocols.as_ptr().cast();
+
+ // SAFETY:
+ //
+ // * The firmware is assumed to provide a correctly-aligned pointer and
+ // array length.
+ // * The firmware is assumed to provide valid GUID pointers.
+ // * Protocol GUIDs should be constants or statics, so a 'static
+ // lifetime (of the individual pointers, not the overall slice) can be
+ // assumed.
+ unsafe { slice::from_raw_parts(ptr, self.count) }
+ }
+}
+
+/// A buffer returned by [`locate_handle_buffer`] that contains an array of
+/// [`Handle`]s that support the requested protocol.
+#[derive(Debug, Eq, PartialEq)]
+pub struct HandleBuffer {
+ count: usize,
+ buffer: NonNull,
+}
+
+impl Drop for HandleBuffer {
+ fn drop(&mut self) {
+ let _ = unsafe { free_pool(self.buffer.cast::()) };
+ }
+}
+
+impl Deref for HandleBuffer {
+ type Target = [Handle];
+
+ fn deref(&self) -> &Self::Target {
+ unsafe { slice::from_raw_parts(self.buffer.as_ptr(), self.count) }
+ }
+}
+
+/// An open protocol interface. Automatically closes the protocol
+/// interface on drop.
+///
+/// Most protocols have interface data associated with them. `ScopedProtocol`
+/// implements [`Deref`] and [`DerefMut`] to access this data. A few protocols
+/// (such as [`DevicePath`] and [`LoadedImageDevicePath`]) may be installed with
+/// null interface data, in which case [`Deref`] and [`DerefMut`] will
+/// panic. The [`get`] and [`get_mut`] methods may be used to access the
+/// optional interface data without panicking.
+///
+/// [`DevicePath`]: crate::proto::device_path::DevicePath
+/// [`LoadedImageDevicePath`]: crate::proto::device_path::LoadedImageDevicePath
+/// [`get`]: ScopedProtocol::get
+/// [`get_mut`]: ScopedProtocol::get_mut
+#[derive(Debug)]
+pub struct ScopedProtocol {
+ /// The protocol interface.
+ interface: Option>,
+ open_params: OpenProtocolParams,
+}
+
+impl Drop for ScopedProtocol {
+ fn drop(&mut self) {
+ let bt = boot_services_raw_panicking();
+ let bt = unsafe { bt.as_ref() };
+
+ let status = unsafe {
+ (bt.close_protocol)(
+ self.open_params.handle.as_ptr(),
+ &P::GUID,
+ self.open_params.agent.as_ptr(),
+ Handle::opt_to_ptr(self.open_params.controller),
+ )
+ };
+ // All of the error cases for close_protocol boil down to
+ // calling it with a different set of parameters than what was
+ // passed to open_protocol. The public API prevents such errors,
+ // and the error can't be propagated out of drop anyway, so just
+ // assert success.
+ assert_eq!(status, Status::SUCCESS);
+ }
+}
+
+impl Deref for ScopedProtocol {
+ type Target = P;
+
+ #[track_caller]
+ fn deref(&self) -> &Self::Target {
+ unsafe { self.interface.unwrap().as_ref() }
+ }
+}
+
+impl DerefMut for ScopedProtocol {
+ #[track_caller]
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ unsafe { self.interface.unwrap().as_mut() }
+ }
+}
+
+impl ScopedProtocol {
+ /// Get the protocol interface data, or `None` if the open protocol's
+ /// interface is null.
+ #[must_use]
+ pub fn get(&self) -> Option<&P> {
+ self.interface.map(|p| unsafe { p.as_ref() })
+ }
+
+ /// Get the protocol interface data, or `None` if the open protocol's
+ /// interface is null.
+ #[must_use]
+ pub fn get_mut(&mut self) -> Option<&mut P> {
+ self.interface.map(|mut p| unsafe { p.as_mut() })
+ }
+}
+
+/// RAII guard for task priority level changes.
+///
+/// Will automatically restore the former task priority level when dropped.
+#[derive(Debug)]
+pub struct TplGuard {
+ old_tpl: Tpl,
+}
+
+impl Drop for TplGuard {
+ fn drop(&mut self) {
+ let bt = boot_services_raw_panicking();
+ let bt = unsafe { bt.as_ref() };
+
+ unsafe {
+ (bt.restore_tpl)(self.old_tpl);
+ }
+ }
+}
diff --git a/uefi/src/data_types/chars.rs b/uefi/src/data_types/chars.rs
index 60671514b..c2f5bd63f 100644
--- a/uefi/src/data_types/chars.rs
+++ b/uefi/src/data_types/chars.rs
@@ -35,19 +35,19 @@ impl TryFrom for Char8 {
}
impl From for char {
- fn from(char: Char8) -> char {
- char::from(char.0)
+ fn from(char: Char8) -> Self {
+ Self::from(char.0)
}
}
impl From for Char8 {
fn from(value: u8) -> Self {
- Char8(value)
+ Self(value)
}
}
impl From for u8 {
- fn from(char: Char8) -> u8 {
+ fn from(char: Char8) -> Self {
char.0
}
}
@@ -107,7 +107,7 @@ impl TryFrom for Char16 {
}
impl From for char {
- fn from(char: Char16) -> char {
+ fn from(char: Char16) -> Self {
u32::from(char.0).try_into().unwrap()
}
}
@@ -127,7 +127,7 @@ impl TryFrom for Char16 {
}
impl From for u16 {
- fn from(char: Char16) -> u16 {
+ fn from(char: Char16) -> Self {
char.0
}
}
diff --git a/uefi/src/data_types/mod.rs b/uefi/src/data_types/mod.rs
index 74239c025..33cd201ff 100644
--- a/uefi/src/data_types/mod.rs
+++ b/uefi/src/data_types/mod.rs
@@ -39,7 +39,7 @@ impl Handle {
/// Get the underlying raw pointer.
#[must_use]
- pub fn as_ptr(&self) -> *mut c_void {
+ pub const fn as_ptr(&self) -> *mut c_void {
self.0.as_ptr()
}
@@ -78,7 +78,7 @@ impl Event {
/// Get the underlying raw pointer.
#[must_use]
- pub fn as_ptr(&self) -> *mut c_void {
+ pub const fn as_ptr(&self) -> *mut c_void {
self.0.as_ptr()
}
}
@@ -137,6 +137,12 @@ pub trait Align {
}
}
+impl Align for [u8] {
+ fn alignment() -> usize {
+ 1
+ }
+}
+
mod guid;
pub use guid::{Guid, Identify};
diff --git a/uefi/src/data_types/owned_strs.rs b/uefi/src/data_types/owned_strs.rs
index cf11f43ae..6fe29a6bd 100644
--- a/uefi/src/data_types/owned_strs.rs
+++ b/uefi/src/data_types/owned_strs.rs
@@ -113,7 +113,7 @@ impl CString16 {
impl Default for CString16 {
fn default() -> Self {
- CString16::new()
+ Self::new()
}
}
@@ -141,7 +141,7 @@ impl TryFrom<&str> for CString16 {
// Add trailing nul.
output.push(NUL_16);
- Ok(CString16(output))
+ Ok(Self(output))
}
}
@@ -175,7 +175,7 @@ impl<'a> TryFrom<&UnalignedSlice<'a, u16>> for CString16 {
fn try_from(input: &UnalignedSlice) -> Result {
let v = input.to_vec();
- CString16::try_from(v)
+ Self::try_from(v)
}
}
@@ -189,7 +189,7 @@ impl From<&CStr16> for CString16 {
impl From<&CString16> for String {
fn from(value: &CString16) -> Self {
let slice: &CStr16 = value.as_ref();
- String::from(slice)
+ Self::from(slice)
}
}
diff --git a/uefi/src/data_types/strs.rs b/uefi/src/data_types/strs.rs
index 2855e8802..bb01120d1 100644
--- a/uefi/src/data_types/strs.rs
+++ b/uefi/src/data_types/strs.rs
@@ -457,7 +457,7 @@ impl CStr16 {
pub fn from_unaligned_slice<'buf>(
src: &UnalignedSlice<'_, u16>,
buf: &'buf mut [MaybeUninit],
- ) -> Result<&'buf CStr16, UnalignedCStr16Error> {
+ ) -> Result<&'buf Self, UnalignedCStr16Error> {
// The input `buf` might be longer than needed, so get a
// subslice of the required length.
let buf = buf
@@ -469,7 +469,7 @@ impl CStr16 {
// Safety: `copy_buf` fully initializes the slice.
maybe_uninit_slice_assume_init_ref(buf)
};
- CStr16::from_u16_with_nul(buf).map_err(|e| match e {
+ Self::from_u16_with_nul(buf).map_err(|e| match e {
FromSliceWithNulError::InvalidChar(v) => UnalignedCStr16Error::InvalidChar(v),
FromSliceWithNulError::InteriorNul(v) => UnalignedCStr16Error::InteriorNul(v),
FromSliceWithNulError::NotNulTerminated => UnalignedCStr16Error::NotNulTerminated,
@@ -524,7 +524,7 @@ impl CStr16 {
/// Returns if the string is empty. This ignores the null character.
#[must_use]
- pub fn is_empty(&self) -> bool {
+ pub const fn is_empty(&self) -> bool {
self.num_chars() == 0
}
@@ -566,7 +566,7 @@ impl CStr16 {
/// Returns the underlying bytes as slice including the terminating null
/// character.
#[must_use]
- pub fn as_bytes(&self) -> &[u8] {
+ pub const fn as_bytes(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self.0.as_ptr().cast(), self.num_bytes()) }
}
}
@@ -593,7 +593,7 @@ impl From<&CStr16> for alloc::string::String {
.map(u16::from)
.map(u32::from)
.map(|int| char::from_u32(int).expect("Should be encodable as UTF-8"))
- .collect::()
+ .collect::()
}
}
@@ -615,8 +615,8 @@ impl + ?Sized> EqStrUntilNul for CStr16 {
}
}
-impl AsRef for CStr16 {
- fn as_ref(&self) -> &CStr16 {
+impl AsRef for CStr16 {
+ fn as_ref(&self) -> &Self {
self
}
}
diff --git a/uefi/src/data_types/unaligned_slice.rs b/uefi/src/data_types/unaligned_slice.rs
index 45f1eaa0c..a77fff6ce 100644
--- a/uefi/src/data_types/unaligned_slice.rs
+++ b/uefi/src/data_types/unaligned_slice.rs
@@ -28,7 +28,7 @@ impl<'a, T: Copy> UnalignedSlice<'a, T> {
/// The `data` pointer must point to a packed array of at least
/// `len` elements of type `T`. The pointer must remain valid for as
/// long as the `'a` lifetime.
- pub unsafe fn new(data: *const T, len: usize) -> Self {
+ pub const unsafe fn new(data: *const T, len: usize) -> Self {
Self {
data,
len,
diff --git a/uefi/src/fs/dir_entry_iter.rs b/uefi/src/fs/dir_entry_iter.rs
index 334825e2e..dc82477a9 100644
--- a/uefi/src/fs/dir_entry_iter.rs
+++ b/uefi/src/fs/dir_entry_iter.rs
@@ -18,7 +18,7 @@ pub struct UefiDirectoryIter(UefiDirectoryHandle);
impl UefiDirectoryIter {
/// Constructor.
#[must_use]
- pub fn new(handle: UefiDirectoryHandle) -> Self {
+ pub const fn new(handle: UefiDirectoryHandle) -> Self {
Self(handle)
}
}
diff --git a/uefi/src/fs/file_system/fs.rs b/uefi/src/fs/file_system/fs.rs
index 2d57fc442..8925131b9 100644
--- a/uefi/src/fs/file_system/fs.rs
+++ b/uefi/src/fs/file_system/fs.rs
@@ -26,7 +26,7 @@ pub struct FileSystem<'a>(ScopedProtocol<'a, SimpleFileSystemProtocol>);
impl<'a> FileSystem<'a> {
/// Constructor.
#[must_use]
- pub fn new(proto: ScopedProtocol<'a, SimpleFileSystemProtocol>) -> Self {
+ pub const fn new(proto: ScopedProtocol<'a, SimpleFileSystemProtocol>) -> Self {
Self(proto)
}
diff --git a/uefi/src/fs/path/path.rs b/uefi/src/fs/path/path.rs
index efa5019d5..950c344e2 100644
--- a/uefi/src/fs/path/path.rs
+++ b/uefi/src/fs/path/path.rs
@@ -21,7 +21,7 @@ impl Path {
/// Returns the underlying string.
#[must_use]
- pub fn to_cstr16(&self) -> &CStr16 {
+ pub const fn to_cstr16(&self) -> &CStr16 {
&self.0
}
@@ -79,7 +79,7 @@ impl Path {
/// Returns of the path is empty.
#[must_use]
- pub fn is_empty(&self) -> bool {
+ pub const fn is_empty(&self) -> bool {
self.to_cstr16().is_empty()
}
}
diff --git a/uefi/src/helpers/logger.rs b/uefi/src/helpers/logger.rs
index b8d8cfa41..cb01a1ae4 100644
--- a/uefi/src/helpers/logger.rs
+++ b/uefi/src/helpers/logger.rs
@@ -19,14 +19,12 @@ use core::ptr;
use core::sync::atomic::{AtomicPtr, Ordering};
/// Global logger object
-#[cfg(feature = "logger")]
static LOGGER: Logger = Logger::new();
/// Set up logging
///
/// This is unsafe because you must arrange for the logger to be reset with
/// disable() on exit from UEFI boot services.
-#[cfg(feature = "logger")]
pub unsafe fn init(st: &mut SystemTable) {
// Connect the logger to stdout.
LOGGER.set_output(st.stdout());
@@ -60,7 +58,7 @@ impl core::fmt::Write for DebugconWriter {
fn write_str(&mut self, s: &str) -> fmt::Result {
for &byte in s.as_bytes() {
unsafe {
- core::arch::asm!("outb %al, %dx", in("al") byte, in("dx") DebugconWriter::IO_PORT, options(att_syntax))
+ core::arch::asm!("outb %al, %dx", in("al") byte, in("dx") Self::IO_PORT, options(att_syntax))
};
}
Ok(())
@@ -85,7 +83,7 @@ impl Logger {
/// [`set_output`]: Self::set_output
#[must_use]
pub const fn new() -> Self {
- Logger {
+ Self {
writer: AtomicPtr::new(ptr::null_mut()),
}
}
diff --git a/uefi/src/helpers/mod.rs b/uefi/src/helpers/mod.rs
index e7afe9a6e..650720966 100644
--- a/uefi/src/helpers/mod.rs
+++ b/uefi/src/helpers/mod.rs
@@ -54,23 +54,26 @@ pub fn system_table() -> SystemTable {
/// **PLEASE NOTE** that these helpers are meant for the pre exit boot service
/// epoch. Limited functionality might work after exiting them, such as logging
/// to the debugcon device.
-#[allow(unused_variables)] // `st` is unused if logger and allocator are disabled
-pub fn init(st: &mut SystemTable) -> Result<()> {
+#[allow(clippy::missing_const_for_fn)]
+pub fn init() -> Result<()> {
// Setup logging and memory allocation
#[cfg(feature = "logger")]
unsafe {
- logger::init(st);
+ let mut st = table::system_table_boot().expect("boot services are not active");
+ logger::init(&mut st);
}
#[cfg(feature = "global_allocator")]
unsafe {
- crate::allocator::init(st);
+ let mut st = table::system_table_boot().expect("boot services are not active");
+ crate::allocator::init(&mut st);
}
Ok(())
}
+#[allow(clippy::missing_const_for_fn)]
pub(crate) fn exit() {
#[cfg(feature = "logger")]
logger::disable();
diff --git a/uefi/src/helpers/println.rs b/uefi/src/helpers/println.rs
index 2a30eb1d5..2b9903e92 100644
--- a/uefi/src/helpers/println.rs
+++ b/uefi/src/helpers/println.rs
@@ -4,14 +4,25 @@ use core::fmt::Write;
/// INTERNAL API! Helper for print macros.
#[doc(hidden)]
pub fn _print(args: core::fmt::Arguments) {
- system_table_boot()
- .expect("boot services are not active")
- .stdout()
- .write_fmt(args)
- .expect("Failed to write to stdout");
+ if let Some(mut bs) = system_table_boot() {
+ bs.stdout()
+ .write_fmt(args)
+ .expect("Failed to write to stdout");
+ } else {
+ // Ease debugging: Depending on logger, this might write to serial or
+ // debugcon.
+ log::debug!("You are using `print!` after the boot services have been exited.");
+ }
}
-/// Prints to the standard output.
+/// Prints to the standard output of the UEFI boot service console.
+///
+/// # Usage
+/// Use this similar to `print!` from the Rust standard library, but only
+/// as long as boot services have not been exited.
+///
+/// You should never use this macro in a custom Logger ([`log::Log`] impl) to
+/// prevent a circular runtime dependency.
///
/// # Panics
/// Will panic if `SYSTEM_TABLE` is `None` (Before [`uefi::helpers::init()`] and
@@ -28,7 +39,15 @@ macro_rules! print {
($($arg:tt)*) => ($crate::helpers::_print(core::format_args!($($arg)*)));
}
-/// Prints to the standard output, with a newline.
+/// Prints to the standard output of the UEFI boot service console, but with a
+/// newline.
+///
+/// # Usage
+/// Use this similar to `println!` from the Rust standard library, but only
+/// as long as boot services have not been exited.
+///
+/// You should never use this macro in a custom Logger ([`log::Log`] impl) to
+/// prevent a circular runtime dependency.
///
/// # Panics
/// Will panic if `SYSTEM_TABLE` is `None` (Before [`uefi::helpers::init()`] and
diff --git a/uefi/src/lib.rs b/uefi/src/lib.rs
index fb658d7c3..851dd74a7 100644
--- a/uefi/src/lib.rs
+++ b/uefi/src/lib.rs
@@ -1,17 +1,73 @@
//! Rusty wrapper for the [Unified Extensible Firmware Interface][UEFI].
//!
+//! This crate makes it easy to develop Rust software that leverages **safe**,
+//! **convenient**, and **performant** abstractions for [UEFI] functionality.
+//!
//! See the [Rust UEFI Book] for a tutorial, how-tos, and overviews of some
//! important UEFI concepts. For more details of UEFI, see the latest [UEFI
//! Specification][spec].
//!
-//! Feel free to file bug reports and questions in our [issue tracker], and [PR
-//! contributions][contributing] are also welcome!
+//! # Minimal Example
+//!
+//! Minimal example for an UEFI application using functionality of the
+//! `uefi` crate:
+//!
+//! ```ignore
+//! #![no_main]
+//! #![no_std]
+//!
+//! use uefi::prelude::*;
+//!
+//! #[entry]
+//! fn main(_handle: Handle, system_table: SystemTable) -> Status {
+//! uefi::helpers::init().unwrap();
+//!
+//! Status::SUCCESS
+//! }
+//! ```
+//!
+//! Please find more info in our [Rust UEFI Book].
+//!
+//! # Value-add and Use Cases
+//!
+//! `uefi` supports writing code for both pre- and post-exit boot services
+//! epochs, but its true strength shines when you create UEFI images that heavily
+//! interact with UEFI boot services. Still, you have the flexibility to just
+//! integrate selected types and abstractions into your project, for example to
+//! parse the UEFI memory map.
+//!
+//! _Note that for producing UEFI images, you also need to use a corresponding
+//! `uefi` compiler target of Rust, such as `x86_64-unknown-uefi`._
//!
-//! # Interaction with uefi services
+//! ## Example Use Cases
//!
-//! With this crate you can write code for the pre- and post-exit boot services
-//! epochs. However, the `uefi` crate unfolds its true potential when
-//! interacting with UEFI boot services.
+//! This library significantly simplifies the process of creating **UEFI images**
+//! by abstracting away much of the UEFI API complexity and by providing
+//! convenient wrappers. When we mention UEFI images, we are talking about UEFI
+//! applications, UEFI boot service drivers, and EFI runtime service drivers,
+//! which typically have the `.efi` file extension. For instance, an UEFI
+//! application could be an OS-specific loader, similar to _GRUB_ or _Limine_.
+//!
+//! Additionally, you can use this crate in non-UEFI images (such as a kernel
+//! in ELF format) to perform tasks like parsing the UEFI memory map embedded in
+//! the boot information provided by a bootloader. It also enables access to
+//! UEFI runtime services from a non-UEFI image kernel.
+//!
+//! # Supported Compiler Versions and Architectures
+//!
+//! `uefi` works with stable Rust, but additional nightly-only features are
+//! gated behind the `unstable` Cargo feature. Please find more information
+//! about additional crate features below.
+//!
+//! `uefi` is compatible with all platforms that both the Rust compiler and
+//! UEFI support, such as `i686`, `x86_64`, and `aarch64`. Please note that we
+//! can't test all possible hardware/firmware/platform combinations in CI.
+//!
+//! ## MSRV
+//!
+//!
+//! The minimum supported Rust version is currently 1.70.
+//! Our policy is to support at least the past two stable releases.
//!
//! # Crate organisation
//!
@@ -42,7 +98,7 @@
//! protocol, and see the [`proto`] module for protocol implementations. New
//! protocols can be defined with the [`unsafe_protocol`] macro.
//!
-//! ## Optional crate features
+//! ## Optional Cargo crate features
//!
//! - `alloc`: Enable functionality requiring the [`alloc`] crate from
//! the Rust standard library. For example, methods that return a
@@ -59,8 +115,6 @@
//! that prints output to the UEFI console. No buffering is done; this
//! is not a high-performance logger.
//! - `panic_handler`: Add a default panic handler that logs to `stdout`.
-//! - `panic-on-logger-errors` (enabled by default): Panic if a text
-//! output error occurs in the logger.
//! - `unstable`: Enable functionality that depends on [unstable
//! features] in the nightly compiler.
//! As example, in conjunction with the `alloc`-feature, this gate allows
@@ -73,70 +127,122 @@
//! only unfold their potential when you invoke `uefi::helpers::init` as soon
//! as possible in your application.
//!
+//! # Discuss and Contribute
+//!
+//! For general discussions, feel free to join us in our [Zulip] and ask
+//! your questions there.
+//!
+//! Further, you can submit bugs and also ask questions in our [issue tracker].
+//! Contributions in the form of a PR are also highly welcome. Check our
+//! [contributing guide][contributing] for details.
+//!
+//! # Comparison to other Projects in the Ecosystem
+//!
+//! ## Rust `std` implementation
+//!
+//! There is an ongoing effort for a [`std` implementation][rustc-uefi-std] of
+//! the Rust standard library, which allows you to write UEFI programs that look
+//! very similar to normal Rust programs running on top of an OS.
+//!
+//! It is still under development. You can track the progress in the
+//! corresponding [tracking issue][uefi-std-tr-issue].
+//!
+//! Using the `std` implementation simplifies the overall process of producing
+//! the binary. For example, our [`#[entry]`][entry-macro] macro won't be
+//! required any longer. As the `std` implementation evolves over time, you'll
+//! need fewer and fewer abstractions of this crate. For everything not covered
+//! by the `std` implementation, you can obtain relevant structures to work with
+//! our crate via:
+//! - `std::os::uefi::env::boot_services()`
+//! - `std::os::uefi::env::get_system_handle()`
+//! - `std::os::uefi::env::get_system_table()`
+//!
+//! ## `r-efi`
+//!
+//! [`r-efi`] provides Raw UEFI bindings without high-level convenience similar
+//! to our `uefi-raw` crate, which is part of this project, but more
+//! feature-complete. It targets a lower-level than our `uefi` crate does.
+//!
+//! # License
+//!
+//!
+//! The code in this repository is licensed under the Mozilla Public License 2.
+//! This license allows you to use the crate in proprietary programs, but any
+//! modifications to the files must be open-sourced.
+//!
+//! The full text of the license is available in the [license file][LICENSE].
+//!
+//! # Terminology
+//!
+//! Both "EFI" and "UEFI" can be used interchangeably, such as "UEFI image" or
+//! "EFI image". We prefer "UEFI" in our crate and its documentation.
+//!
+//! [LICENSE]: https://github.com/rust-osdev/uefi-rs/blob/main/uefi/LICENSE
//! [Rust UEFI Book]: https://rust-osdev.github.io/uefi-rs/HEAD/
//! [UEFI]: https://uefi.org/
+//! [Zulip]: https://rust-osdev.zulipchat.com
//! [`BootServices`]: table::boot::BootServices
//! [`GlobalAlloc`]: alloc::alloc::GlobalAlloc
//! [`SystemTable`]: table::SystemTable
+//! [`r-efi`]: https://crates.io/crates/r-efi
+//! [`entry-macro`]: uefi_macros::entry
//! [`unsafe_protocol`]: proto::unsafe_protocol
//! [contributing]: https://github.com/rust-osdev/uefi-rs/blob/main/CONTRIBUTING.md
//! [issue tracker]: https://github.com/rust-osdev/uefi-rs/issues
//! [spec]: https://uefi.org/specifications
//! [unstable features]: https://doc.rust-lang.org/unstable-book/
+//! [rustc-uefi-std]: https://doc.rust-lang.org/nightly/rustc/platform-support/unknown-uefi.html
+//! [uefi-std-tr-issue]: https://github.com/rust-lang/rust/issues/100499
#![cfg_attr(all(feature = "unstable", feature = "alloc"), feature(allocator_api))]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![no_std]
-// Enable some additional warnings and lints.
-#![warn(clippy::ptr_as_ptr, missing_docs, unused)]
-#![deny(clippy::all)]
-#![deny(clippy::must_use_candidate)]
-#![deny(missing_debug_implementations)]
+#![deny(
+ clippy::all,
+ clippy::missing_const_for_fn,
+ clippy::must_use_candidate,
+ clippy::ptr_as_ptr,
+ clippy::use_self,
+ missing_debug_implementations,
+ missing_docs,
+ unused
+)]
#[cfg(feature = "alloc")]
extern crate alloc;
-
// allow referring to self as ::uefi for macros to work universally (from this crate and from others)
// see https://github.com/rust-lang/rust/issues/54647
extern crate self as uefi;
-
-/// Re-export ucs2_cstr so that it can be used in the implementation of the
-/// cstr16 macro. It is hidden since it's not intended to be used directly.
-#[doc(hidden)]
-pub use ucs2::ucs2_cstr;
-
#[macro_use]
extern crate uefi_raw;
#[macro_use]
pub mod data_types;
-#[cfg(feature = "alloc")]
-pub use data_types::CString16;
-pub use data_types::{CStr16, CStr8, Char16, Char8, Event, Guid, Handle, Identify};
-pub use uefi_macros::entry;
-pub use uguid::guid;
-
-mod result;
-pub use result::{Error, Result, ResultExt, Status, StatusExt};
-
-pub mod table;
-
-pub mod proto;
-
-pub mod prelude;
-
pub mod allocator;
-
+pub mod boot;
#[cfg(feature = "alloc")]
pub mod fs;
-
-// As long as this is behind "alloc", we can simplify cfg-feature attributes in this module.
-#[cfg(feature = "alloc")]
-pub(crate) mod mem;
+pub mod helpers;
+pub mod mem;
+pub mod prelude;
+pub mod proto;
+pub mod runtime;
+pub mod system;
+pub mod table;
pub(crate) mod polyfill;
-pub mod helpers;
-
mod macros;
+mod result;
mod util;
+
+#[cfg(feature = "alloc")]
+pub use data_types::CString16;
+pub use data_types::{CStr16, CStr8, Char16, Char8, Event, Guid, Handle, Identify};
+pub use result::{Error, Result, ResultExt, Status, StatusExt};
+/// Re-export ucs2_cstr so that it can be used in the implementation of the
+/// cstr16 macro. It is hidden since it's not intended to be used directly.
+#[doc(hidden)]
+pub use ucs2::ucs2_cstr;
+pub use uefi_macros::entry;
+pub use uguid::guid;
diff --git a/uefi/src/mem/memory_map/api.rs b/uefi/src/mem/memory_map/api.rs
new file mode 100644
index 000000000..960967d48
--- /dev/null
+++ b/uefi/src/mem/memory_map/api.rs
@@ -0,0 +1,123 @@
+//! Module for the traits [`MemoryMap`] and [`MemoryMapMut`].
+
+use super::*;
+use core::fmt::Debug;
+use core::ops::{Index, IndexMut};
+
+/// An accessory to the UEFI memory map and associated metadata that can be
+/// either iterated or indexed like an array.
+///
+/// A [`MemoryMap`] is always associated with the unique [`MemoryMapKey`]
+/// bundled with the map.
+///
+/// To iterate over the entries, call [`MemoryMap::entries`].
+///
+/// ## UEFI pitfalls
+/// Note that a MemoryMap can quickly become outdated, as soon as any explicit
+/// or hidden allocation happens.
+///
+/// As soon as boot services are excited, all previous obtained memory maps must
+/// be considered as outdated, except if the [`MemoryMapKey`] equals the one
+/// returned by `exit_boot_services()`.
+///
+/// **Please note** that when working with memory maps, the `entry_size` is
+/// usually larger than `size_of:: {
+ /// Returns the associated [`MemoryMapMeta`].
+ #[must_use]
+ fn meta(&self) -> MemoryMapMeta;
+
+ /// Returns the associated [`MemoryMapKey`]. Note that this isn't
+ /// necessarily the key of the latest valid UEFI memory map.
+ #[must_use]
+ fn key(&self) -> MemoryMapKey;
+
+ /// Returns the number of keys in the map.
+ #[must_use]
+ fn len(&self) -> usize;
+
+ /// Returns if the memory map is empty.
+ #[must_use]
+ fn is_empty(&self) -> bool {
+ self.len() == 0
+ }
+
+ /// Returns a reference to the [`MemoryDescriptor`] at the given index, if
+ /// present.
+ #[must_use]
+ fn get(&self, index: usize) -> Option<&MemoryDescriptor> {
+ if index >= self.len() {
+ None
+ } else {
+ let offset = index * self.meta().desc_size;
+ unsafe {
+ self.buffer()
+ .as_ptr()
+ .add(offset)
+ .cast::()
+ .as_ref()
+ }
+ }
+ }
+
+ /// Returns a reference to the underlying memory.
+ #[must_use]
+ fn buffer(&self) -> &[u8];
+
+ /// Returns an Iterator of type [`MemoryMapIter`].
+ #[must_use]
+ fn entries(&self) -> MemoryMapIter<'_>;
+
+ /// Returns if the underlying memory map is sorted regarding the physical
+ /// address start.
+ #[must_use]
+ fn is_sorted(&self) -> bool {
+ let iter = self.entries();
+ let iter = iter.clone().zip(iter.skip(1));
+
+ for (curr, next) in iter {
+ if next.phys_start < curr.phys_start {
+ log::debug!("next.phys_start < curr.phys_start: curr={curr:?}, next={next:?}");
+ return false;
+ }
+ }
+ true
+ }
+}
+
+/// Extension to [`MemoryMap`] that adds mutable operations. This also includes
+/// the ability to sort the memory map.
+pub trait MemoryMapMut: MemoryMap + IndexMut {
+ /// Returns a mutable reference to the [`MemoryDescriptor`] at the given
+ /// index, if present.
+ #[must_use]
+ fn get_mut(&mut self, index: usize) -> Option<&mut MemoryDescriptor> {
+ if index >= self.len() {
+ None
+ } else {
+ let offset = index * self.meta().desc_size;
+ unsafe {
+ self.buffer_mut()
+ .as_mut_ptr()
+ .add(offset)
+ .cast::()
+ .as_mut()
+ }
+ }
+ }
+
+ /// Sorts the memory map by physical address in place. This operation is
+ /// optional and should be invoked only once.
+ fn sort(&mut self);
+
+ /// Returns a reference to the underlying memory.
+ ///
+ /// # Safety
+ ///
+ /// This is unsafe as there is a potential to create invalid entries.
+ unsafe fn buffer_mut(&mut self) -> &mut [u8];
+}
diff --git a/uefi/src/mem/memory_map/impl_.rs b/uefi/src/mem/memory_map/impl_.rs
new file mode 100644
index 000000000..a0fd463dc
--- /dev/null
+++ b/uefi/src/mem/memory_map/impl_.rs
@@ -0,0 +1,532 @@
+//! Module for [`MemoryMapOwned`], [`MemoryMapRef`], and [`MemoryMapRefMut`],
+//! as well as relevant helper types, such as [`MemoryMapBackingMemory`].
+
+use super::*;
+use crate::table::system_table_boot;
+use core::fmt::{Debug, Display, Formatter};
+use core::ops::{Index, IndexMut};
+use core::ptr::NonNull;
+use core::{mem, ptr};
+use uefi_raw::PhysicalAddress;
+
+/// Errors that may happen when constructing a [`MemoryMapRef`] or
+/// [`MemoryMapRefMut`].
+#[derive(Copy, Clone, Debug)]
+pub enum MemoryMapError {
+ /// The buffer is not 8-byte aligned.
+ Misaligned,
+ /// The memory map size is invalid.
+ InvalidSize,
+}
+
+impl Display for MemoryMapError {
+ fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
+ Debug::fmt(self, f)
+ }
+}
+
+#[cfg(feature = "unstable")]
+impl core::error::Error for MemoryMapError {}
+
+/// Implementation of [`MemoryMap`] for the given buffer.
+#[derive(Debug)]
+pub struct MemoryMapRef<'a> {
+ buf: &'a [u8],
+ meta: MemoryMapMeta,
+ len: usize,
+}
+
+impl<'a> MemoryMapRef<'a> {
+ /// Constructs a new [`MemoryMapRef`].
+ ///
+ /// The underlying memory might contain an invalid/malformed memory map
+ /// which can't be checked during construction of this type. The entry
+ /// iterator might yield unexpected results.
+ pub fn new(buffer: &'a [u8], meta: MemoryMapMeta) -> Result {
+ if buffer.as_ptr().align_offset(8) != 0 {
+ return Err(MemoryMapError::Misaligned);
+ }
+ if buffer.len() < meta.map_size {
+ return Err(MemoryMapError::InvalidSize);
+ }
+ Ok(Self {
+ buf: buffer,
+ meta,
+ len: meta.entry_count(),
+ })
+ }
+}
+
+impl<'a> MemoryMap for MemoryMapRef<'a> {
+ fn meta(&self) -> MemoryMapMeta {
+ self.meta
+ }
+
+ fn key(&self) -> MemoryMapKey {
+ self.meta.map_key
+ }
+
+ fn len(&self) -> usize {
+ self.len
+ }
+
+ fn buffer(&self) -> &[u8] {
+ self.buf
+ }
+
+ fn entries(&self) -> MemoryMapIter<'_> {
+ MemoryMapIter {
+ memory_map: self,
+ index: 0,
+ }
+ }
+}
+
+impl Index for MemoryMapRef<'_> {
+ type Output = MemoryDescriptor;
+
+ fn index(&self, index: usize) -> &Self::Output {
+ self.get(index).unwrap()
+ }
+}
+
+/// Implementation of [`MemoryMapMut`] for the given buffer.
+#[derive(Debug)]
+pub struct MemoryMapRefMut<'a> {
+ buf: &'a mut [u8],
+ meta: MemoryMapMeta,
+ len: usize,
+}
+
+impl<'a> MemoryMapRefMut<'a> {
+ /// Constructs a new [`MemoryMapRefMut`].
+ ///
+ /// The underlying memory might contain an invalid/malformed memory map
+ /// which can't be checked during construction of this type. The entry
+ /// iterator might yield unexpected results.
+ pub fn new(buffer: &'a mut [u8], meta: MemoryMapMeta) -> Result {
+ if buffer.as_ptr().align_offset(8) != 0 {
+ return Err(MemoryMapError::Misaligned);
+ }
+ if buffer.len() < meta.map_size {
+ return Err(MemoryMapError::InvalidSize);
+ }
+ Ok(Self {
+ buf: buffer,
+ meta,
+ len: meta.entry_count(),
+ })
+ }
+}
+
+impl<'a> MemoryMap for MemoryMapRefMut<'a> {
+ fn meta(&self) -> MemoryMapMeta {
+ self.meta
+ }
+
+ fn key(&self) -> MemoryMapKey {
+ self.meta.map_key
+ }
+
+ fn len(&self) -> usize {
+ self.len
+ }
+
+ fn buffer(&self) -> &[u8] {
+ self.buf
+ }
+
+ fn entries(&self) -> MemoryMapIter<'_> {
+ MemoryMapIter {
+ memory_map: self,
+ index: 0,
+ }
+ }
+}
+
+impl<'a> MemoryMapMut for MemoryMapRefMut<'a> {
+ fn sort(&mut self) {
+ unsafe {
+ self.qsort(0, self.len - 1);
+ }
+ }
+
+ unsafe fn buffer_mut(&mut self) -> &mut [u8] {
+ self.buf
+ }
+}
+
+impl<'a> MemoryMapRefMut<'a> {
+ /// Hoare partition scheme for quicksort.
+ /// Must be called with `low` and `high` being indices within bounds.
+ unsafe fn qsort(&mut self, low: usize, high: usize) {
+ if low >= high {
+ return;
+ }
+
+ let p = self.partition(low, high);
+ self.qsort(low, p);
+ self.qsort(p + 1, high);
+ }
+
+ unsafe fn partition(&mut self, low: usize, high: usize) -> usize {
+ let pivot = self.get_element_phys_addr(low + (high - low) / 2);
+
+ let mut left_index = low.wrapping_sub(1);
+ let mut right_index = high.wrapping_add(1);
+
+ loop {
+ while {
+ left_index = left_index.wrapping_add(1);
+
+ self.get_element_phys_addr(left_index) < pivot
+ } {}
+
+ while {
+ right_index = right_index.wrapping_sub(1);
+
+ self.get_element_phys_addr(right_index) > pivot
+ } {}
+
+ if left_index >= right_index {
+ return right_index;
+ }
+
+ self.swap(left_index, right_index);
+ }
+ }
+
+ /// Indices must be smaller than len.
+ unsafe fn swap(&mut self, index1: usize, index2: usize) {
+ if index1 == index2 {
+ return;
+ }
+
+ let base = self.buf.as_mut_ptr();
+
+ unsafe {
+ ptr::swap_nonoverlapping(
+ base.add(index1 * self.meta.desc_size),
+ base.add(index2 * self.meta.desc_size),
+ self.meta.desc_size,
+ );
+ }
+ }
+
+ fn get_element_phys_addr(&self, index: usize) -> PhysicalAddress {
+ let offset = index.checked_mul(self.meta.desc_size).unwrap();
+ let elem = unsafe { &*self.buf.as_ptr().add(offset).cast::() };
+ elem.phys_start
+ }
+}
+
+impl Index for MemoryMapRefMut<'_> {
+ type Output = MemoryDescriptor;
+
+ fn index(&self, index: usize) -> &Self::Output {
+ self.get(index).unwrap()
+ }
+}
+
+impl IndexMut for MemoryMapRefMut<'_> {
+ fn index_mut(&mut self, index: usize) -> &mut Self::Output {
+ self.get_mut(index).unwrap()
+ }
+}
+
+/// The backing memory for the UEFI memory app on the UEFI heap, allocated using
+/// the UEFI boot services allocator. This occupied memory will also be
+/// reflected in the memory map itself.
+///
+/// Although untyped, it is similar to the `Box` type in terms of heap
+/// allocation and deallocation, as well as ownership of the corresponding
+/// memory. Apart from that, this type only has the semantics of a buffer.
+///
+/// The memory is untyped, which is necessary due to the nature of the UEFI
+/// spec. It still ensures a correct alignment to hold [`MemoryDescriptor`]. The
+/// size of the buffer is sufficient to hold the memory map at the point in time
+/// where this is created. Note that due to (not obvious or asynchronous)
+/// allocations/deallocations in your environment, this might be outdated at the
+/// time you store the memory map in it.
+///
+/// Note that due to the nature of the UEFI memory app, this buffer might
+/// hold (a few) bytes more than necessary. The `map_size` reported by
+/// `get_memory_map` tells the actual size.
+///
+/// When this type is dropped and boot services are not exited yet, the memory
+/// is freed.
+///
+/// # Usage
+/// The type is intended to be used like this:
+/// 1. create it using [`MemoryMapBackingMemory::new`]
+/// 2. pass it to [`BootServices::get_memory_map`]
+/// 3. construct a [`MemoryMapOwned`] from it
+///
+/// [`BootServices::get_memory_map`]: crate::table::boot::BootServices::get_memory_map
+#[derive(Debug)]
+#[allow(clippy::len_without_is_empty)] // this type is never empty
+pub(crate) struct MemoryMapBackingMemory(NonNull<[u8]>);
+
+impl MemoryMapBackingMemory {
+ /// Constructs a new [`MemoryMapBackingMemory`].
+ ///
+ /// # Parameters
+ /// - `memory_type`: The memory type for the memory map allocation.
+ /// Typically, [`MemoryType::LOADER_DATA`] for regular UEFI applications.
+ pub(crate) fn new(memory_type: MemoryType) -> crate::Result {
+ let st = system_table_boot().expect("Should have boot services activated");
+ let bs = st.boot_services();
+
+ let memory_map_meta = bs.memory_map_size();
+ let len = Self::safe_allocation_size_hint(memory_map_meta);
+ let ptr = bs.allocate_pool(memory_type, len)?.as_ptr();
+
+ // Should be fine as UEFI always has allocations with a guaranteed
+ // alignment of 8 bytes.
+ assert_eq!(ptr.align_offset(mem::align_of::()), 0);
+
+ // If this panics, the UEFI implementation is broken.
+ assert_eq!(memory_map_meta.map_size % memory_map_meta.desc_size, 0);
+
+ unsafe { Ok(Self::from_raw(ptr, len)) }
+ }
+
+ unsafe fn from_raw(ptr: *mut u8, len: usize) -> Self {
+ assert_eq!(ptr.align_offset(mem::align_of::()), 0);
+
+ let ptr = NonNull::new(ptr).expect("UEFI should never return a null ptr. An error should have been reflected via an Err earlier.");
+ let slice = NonNull::slice_from_raw_parts(ptr, len);
+
+ Self(slice)
+ }
+
+ /// INTERNAL, for unit tests.
+ ///
+ /// Creates an instance from the provided memory, which is not necessarily
+ /// on the UEFI heap.
+ #[cfg(test)]
+ pub(crate) fn from_slice(buffer: &mut [u8]) -> Self {
+ let len = buffer.len();
+ unsafe { Self::from_raw(buffer.as_mut_ptr(), len) }
+ }
+
+ /// Returns a "safe" best-effort size hint for the memory map size with
+ /// some additional bytes in buffer compared to the [`MemoryMapMeta`]. This
+ /// takes into account that, as you go, more (small) allocations might
+ /// happen.
+ #[must_use]
+ const fn safe_allocation_size_hint(mmm: MemoryMapMeta) -> usize {
+ // Allocate space for extra entries beyond the current size of the
+ // memory map. The value of 8 matches the value in the Linux kernel:
+ // https://github.com/torvalds/linux/blob/e544a07438/drivers/firmware/efi/libstub/efistub.h#L173
+ const EXTRA_ENTRIES: usize = 8;
+
+ let extra_size = mmm.desc_size * EXTRA_ENTRIES;
+ mmm.map_size + extra_size
+ }
+
+ /// Returns a slice to the underlying memory.
+ #[must_use]
+ pub fn as_slice(&self) -> &[u8] {
+ unsafe { self.0.as_ref() }
+ }
+
+ /// Returns a mutable slice to the underlying memory.
+ #[must_use]
+ pub fn as_mut_slice(&mut self) -> &mut [u8] {
+ unsafe { self.0.as_mut() }
+ }
+}
+
+// Don't drop when we use this in unit tests.
+#[cfg(not(test))]
+impl Drop for MemoryMapBackingMemory {
+ fn drop(&mut self) {
+ if let Some(bs) = system_table_boot() {
+ let res = unsafe { bs.boot_services().free_pool(self.0.as_ptr().cast()) };
+ if let Err(e) = res {
+ log::error!("Failed to deallocate memory map: {e:?}");
+ }
+ } else {
+ #[cfg(test)]
+ log::debug!("Boot services are not available in unit tests.");
+
+ #[cfg(not(test))]
+ log::debug!("Boot services are excited. Memory map won't be freed using the UEFI boot services allocator.");
+ }
+ }
+}
+
+/// Implementation of [`MemoryMapMut`] that owns the buffer on the UEFI heap.
+#[derive(Debug)]
+pub struct MemoryMapOwned {
+ /// Backing memory, properly initialized at this point.
+ pub(crate) buf: MemoryMapBackingMemory,
+ pub(crate) meta: MemoryMapMeta,
+ pub(crate) len: usize,
+}
+
+impl MemoryMapOwned {
+ /// Creates a [`MemoryMapOwned`] from the given **initialized** memory map
+ /// (stored inside the provided buffer) and the corresponding
+ /// [`MemoryMapMeta`].
+ pub(crate) fn from_initialized_mem(buf: MemoryMapBackingMemory, meta: MemoryMapMeta) -> Self {
+ assert!(meta.desc_size >= mem::size_of::());
+ let len = meta.entry_count();
+ Self { buf, meta, len }
+ }
+}
+
+impl MemoryMap for MemoryMapOwned {
+ fn meta(&self) -> MemoryMapMeta {
+ self.meta
+ }
+
+ fn key(&self) -> MemoryMapKey {
+ self.meta.map_key
+ }
+
+ fn len(&self) -> usize {
+ self.len
+ }
+
+ fn buffer(&self) -> &[u8] {
+ self.buf.as_slice()
+ }
+
+ fn entries(&self) -> MemoryMapIter<'_> {
+ MemoryMapIter {
+ memory_map: self,
+ index: 0,
+ }
+ }
+}
+
+impl MemoryMapMut for MemoryMapOwned {
+ fn sort(&mut self) {
+ let mut reference = MemoryMapRefMut {
+ buf: self.buf.as_mut_slice(),
+ meta: self.meta,
+ len: self.len,
+ };
+ reference.sort();
+ }
+
+ unsafe fn buffer_mut(&mut self) -> &mut [u8] {
+ self.buf.as_mut_slice()
+ }
+}
+
+impl Index for MemoryMapOwned {
+ type Output = MemoryDescriptor;
+
+ fn index(&self, index: usize) -> &Self::Output {
+ self.get(index).unwrap()
+ }
+}
+
+impl IndexMut for MemoryMapOwned {
+ fn index_mut(&mut self, index: usize) -> &mut Self::Output {
+ self.get_mut(index).unwrap()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use alloc::vec::Vec;
+ use core::mem::size_of;
+
+ const BASE_MMAP_UNSORTED: [MemoryDescriptor; 3] = [
+ MemoryDescriptor {
+ ty: MemoryType::CONVENTIONAL,
+ phys_start: 0x3000,
+ virt_start: 0x3000,
+ page_count: 1,
+ att: MemoryAttribute::WRITE_BACK,
+ },
+ MemoryDescriptor {
+ ty: MemoryType::CONVENTIONAL,
+ phys_start: 0x2000,
+ virt_start: 0x2000,
+ page_count: 1,
+ att: MemoryAttribute::WRITE_BACK,
+ },
+ MemoryDescriptor {
+ ty: MemoryType::CONVENTIONAL,
+ phys_start: 0x1000,
+ virt_start: 0x1000,
+ page_count: 1,
+ att: MemoryAttribute::WRITE_BACK,
+ },
+ ];
+
+ /// Returns a copy of [`BASE_MMAP_UNSORTED`] owned on the stack.
+ fn new_mmap_memory() -> [MemoryDescriptor; 3] {
+ BASE_MMAP_UNSORTED
+ }
+
+ fn mmap_raw<'a>(memory: &mut [MemoryDescriptor]) -> (&'a mut [u8], MemoryMapMeta) {
+ let desc_size = size_of::();
+ let len = memory.len() * desc_size;
+ let ptr = memory.as_mut_ptr().cast::();
+ let slice = unsafe { core::slice::from_raw_parts_mut(ptr, len) };
+ let meta = MemoryMapMeta {
+ map_size: len,
+ desc_size,
+ map_key: Default::default(),
+ desc_version: MemoryDescriptor::VERSION,
+ };
+ (slice, meta)
+ }
+
+ /// Basic sanity checks for the type [`MemoryMapRef`].
+ #[test]
+ fn memory_map_ref() {
+ let mut memory = new_mmap_memory();
+ let (mmap, meta) = mmap_raw(&mut memory);
+ let mmap = MemoryMapRef::new(mmap, meta).unwrap();
+
+ assert_eq!(mmap.entries().count(), 3);
+ assert_eq!(
+ mmap.entries().copied().collect::>().as_slice(),
+ &BASE_MMAP_UNSORTED
+ );
+ assert!(!mmap.is_sorted());
+ }
+
+ /// Basic sanity checks for the type [`MemoryMapRefMut`].
+ #[test]
+ fn memory_map_ref_mut() {
+ let mut memory = new_mmap_memory();
+ let (mmap, meta) = mmap_raw(&mut memory);
+ let mut mmap = MemoryMapRefMut::new(mmap, meta).unwrap();
+
+ assert_eq!(mmap.entries().count(), 3);
+ assert_eq!(
+ mmap.entries().copied().collect::>().as_slice(),
+ &BASE_MMAP_UNSORTED
+ );
+ assert!(!mmap.is_sorted());
+ mmap.sort();
+ assert!(mmap.is_sorted());
+ }
+
+ /// Basic sanity checks for the type [`MemoryMapOwned`].
+ #[test]
+ fn memory_map_owned() {
+ let mut memory = new_mmap_memory();
+ let (mmap, meta) = mmap_raw(&mut memory);
+ let mmap = MemoryMapBackingMemory::from_slice(mmap);
+ let mut mmap = MemoryMapOwned::from_initialized_mem(mmap, meta);
+
+ assert_eq!(mmap.entries().count(), 3);
+ assert_eq!(
+ mmap.entries().copied().collect::>().as_slice(),
+ &BASE_MMAP_UNSORTED
+ );
+ assert!(!mmap.is_sorted());
+ mmap.sort();
+ assert!(mmap.is_sorted());
+ }
+}
diff --git a/uefi/src/mem/memory_map/iter.rs b/uefi/src/mem/memory_map/iter.rs
new file mode 100644
index 000000000..049b52ddd
--- /dev/null
+++ b/uefi/src/mem/memory_map/iter.rs
@@ -0,0 +1,36 @@
+use super::*;
+
+/// An iterator for [`MemoryMap`].
+///
+/// The underlying memory might contain an invalid/malformed memory map
+/// which can't be checked during construction of this type. The iterator
+/// might yield unexpected results.
+#[derive(Debug, Clone)]
+pub struct MemoryMapIter<'a> {
+ pub(crate) memory_map: &'a dyn MemoryMap,
+ pub(crate) index: usize,
+}
+
+impl<'a> Iterator for MemoryMapIter<'a> {
+ type Item = &'a MemoryDescriptor;
+
+ fn next(&mut self) -> Option {
+ let desc = self.memory_map.get(self.index)?;
+
+ self.index += 1;
+
+ Some(desc)
+ }
+
+ fn size_hint(&self) -> (usize, Option) {
+ let sz = self.memory_map.len() - self.index;
+
+ (sz, Some(sz))
+ }
+}
+
+impl ExactSizeIterator for MemoryMapIter<'_> {
+ fn len(&self) -> usize {
+ self.memory_map.len()
+ }
+}
diff --git a/uefi/src/mem/memory_map/mod.rs b/uefi/src/mem/memory_map/mod.rs
new file mode 100644
index 000000000..45d66de41
--- /dev/null
+++ b/uefi/src/mem/memory_map/mod.rs
@@ -0,0 +1,375 @@
+//! Bundles all relevant types and helpers to work with the UEFI memory map.
+//!
+//! To work with the memory map, you should use one of the structs
+//! [`MemoryMapOwned`], [`MemoryMapRef`], or [`MemoryMapRefMut`] - depending on
+//! your use-case. The traits [`MemoryMap`] and [`MemoryMapMut`] mainly exist
+//! to guarantee a streamlined API across these types. We recommend to work with
+//! the specific implementation.
+//!
+//! # Usecase: Obtain UEFI Memory Map
+//!
+//! You can use [`SystemTable::exit_boot_services`] or
+//! [`BootServices::memory_map`], which returns an properly initialized
+//! [`MemoryMapOwned`].
+//!
+//! # Usecase: Parse Memory Slice as UEFI Memory Map
+//!
+//! If you have a chunk of memory and want to parse it as UEFI memory map, which
+//! might be the case if a bootloader such as GRUB or Limine passes its boot
+//! information, you can use [`MemoryMapRef`] or [`MemoryMapRefMut`].
+//!
+//! # All relevant exports:
+//!
+//! - the traits [`MemoryMap`] and [`MemoryMapMut`],
+//! - the trait implementations [`MemoryMapOwned`], [`MemoryMapRef`], and
+//! [`MemoryMapRefMut`],
+//! - the iterator [`MemoryMapIter`]
+//! - various associated helper types, such as [`MemoryMapKey`] and
+//! [`MemoryMapMeta`],
+//! - re-exports [`MemoryDescriptor`], [`MemoryType`], and [`MemoryAttribute`].
+//!
+//! [`SystemTable::exit_boot_services`]: uefi::table::SystemTable::exit_boot_services
+//! [`BootServices::memory_map`]: uefi::table::boot::BootServices::memory_map
+
+mod api;
+mod impl_;
+mod iter;
+
+pub use api::*;
+pub use impl_::*;
+pub use iter::*;
+pub use uefi_raw::table::boot::{MemoryAttribute, MemoryDescriptor, MemoryType};
+
+use crate::data_types::Align;
+use core::mem;
+
+impl Align for MemoryDescriptor {
+ fn alignment() -> usize {
+ mem::align_of::()
+ }
+}
+
+/// A unique identifier of a UEFI memory map, used to tell the firmware that one
+/// has the latest valid memory map when exiting boot services.
+///
+/// If the memory map changes, due to any allocation or deallocation, this value
+/// is no longer valid, and exiting boot services will fail.
+#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
+#[repr(C)]
+pub struct MemoryMapKey(pub(crate) usize);
+
+/// A structure containing the meta attributes associated with a call to
+/// `GetMemoryMap` of UEFI. Note that all values refer to the time this was
+/// called. All following invocations (hidden, subtle, and asynchronous ones)
+/// will likely invalidate this.
+#[derive(Copy, Clone, Debug)]
+pub struct MemoryMapMeta {
+ /// The actual size of the map.
+ pub map_size: usize,
+ /// The reported memory descriptor size. Note that this is the reference
+ /// and never `size_of::()`!
+ pub desc_size: usize,
+ /// A unique memory key bound to a specific memory map version/state.
+ pub map_key: MemoryMapKey,
+ /// The version of the descriptor struct.
+ pub desc_version: u32,
+}
+
+impl MemoryMapMeta {
+ /// Returns the amount of entries in the map.
+ #[must_use]
+ pub fn entry_count(&self) -> usize {
+ assert_eq!(self.map_size % self.desc_size, 0);
+ self.map_size / self.desc_size
+ }
+
+ /// Runs some sanity assertions.
+ pub fn assert_sanity_checks(&self) {
+ assert!(self.desc_size > 0);
+ // Although very unlikely, this might fail if the memory descriptor is
+ // extended by a future UEFI revision by a significant amount, we
+ // update the struct, but an old UEFI implementation reports a small
+ // size.
+ assert!(self.desc_size >= mem::size_of::());
+ assert!(self.map_size > 0);
+
+ // Ensure the mmap size is (somehow) sane.
+ const ONE_GB: usize = 1024 * 1024 * 1024;
+ assert!(self.map_size <= ONE_GB);
+ }
+}
+
+/// Comprehensive unit test of the memory map functionality with the simplified
+/// data. Here, `desc_size` equals `size_of:: MemoryMapRefMut {
+ let mmap_len = size_of_val(buffer);
+ let mmap = {
+ unsafe { core::slice::from_raw_parts_mut(buffer.as_mut_ptr() as *mut u8, mmap_len) }
+ };
+
+ MemoryMapRefMut::new(
+ mmap,
+ MemoryMapMeta {
+ map_size: mmap_len,
+ desc_size: size_of::(),
+ map_key: Default::default(),
+ desc_version: MemoryDescriptor::VERSION,
+ },
+ )
+ .unwrap()
+ }
+
+ #[test]
+ fn mem_map_sorting() {
+ // Doesn't matter what type it is.
+ const TY: MemoryType = MemoryType::RESERVED;
+
+ const BASE: MemoryDescriptor = MemoryDescriptor {
+ ty: TY,
+ phys_start: 0,
+ virt_start: 0,
+ page_count: 0,
+ att: MemoryAttribute::empty(),
+ };
+
+ let mut buffer = [
+ MemoryDescriptor {
+ phys_start: 2000,
+ ..BASE
+ },
+ MemoryDescriptor {
+ phys_start: 3000,
+ ..BASE
+ },
+ BASE,
+ MemoryDescriptor {
+ phys_start: 1000,
+ ..BASE
+ },
+ ];
+
+ let mut mem_map = buffer_to_map(&mut buffer);
+
+ mem_map.sort();
+
+ if !is_sorted(&mem_map.entries()) {
+ panic!("mem_map is not sorted: {:?}", mem_map);
+ }
+ }
+
+ #[test]
+ fn mem_map_get() {
+ // Doesn't matter what type it is.
+ const TY: MemoryType = MemoryType::RESERVED;
+
+ const BASE: MemoryDescriptor = MemoryDescriptor {
+ ty: TY,
+ phys_start: 0,
+ virt_start: 0,
+ page_count: 0,
+ att: MemoryAttribute::empty(),
+ };
+
+ const BUFFER: [MemoryDescriptor; 4] = [
+ MemoryDescriptor {
+ phys_start: 2000,
+ ..BASE
+ },
+ MemoryDescriptor {
+ phys_start: 3000,
+ ..BASE
+ },
+ BASE,
+ MemoryDescriptor {
+ phys_start: 1000,
+ ..BASE
+ },
+ ];
+
+ let mut buffer = BUFFER;
+
+ let mut mem_map = buffer_to_map(&mut buffer);
+
+ for index in 0..3 {
+ assert_eq!(mem_map.get(index), BUFFER.get(index));
+
+ // Test Index impl
+ assert_eq!(Some(&mem_map[index]), BUFFER.get(index));
+ }
+
+ let mut_desc = mem_map.get_mut(2).unwrap();
+
+ mut_desc.phys_start = 300;
+
+ let desc = mem_map.get(2).unwrap();
+
+ assert_ne!(*desc, BUFFER[2]);
+ }
+
+ fn is_sorted(iter: &MemoryMapIter) -> bool {
+ let mut iter = iter.clone();
+ let mut curr_start;
+
+ if let Some(val) = iter.next() {
+ curr_start = val.phys_start;
+ } else {
+ return true;
+ }
+
+ for desc in iter {
+ if desc.phys_start <= curr_start {
+ return false;
+ }
+ curr_start = desc.phys_start
+ }
+ true
+ }
+}
+
+/// Comprehensive unit test of the memory map functionality with the data from a
+/// real UEFI memory map. The important property that we test here is that
+/// the reported `desc_size` doesn't equal `size_of::(),
+ desc_size: 48,
+ map_key: MemoryMapKey(0),
+ desc_version: 1,
+ };
+ /// Sample with 10 entries of a real UEFI memory map extracted from our
+ /// UEFI test runner.
+ const MMAP_RAW: [u64; 60] = [
+ 3, 0, 0, 1, 15, 0, 7, 4096, 0, 134, 15, 0, 4, 552960, 0, 1, 15, 0, 7, 557056, 0, 24, 15, 0,
+ 7, 1048576, 0, 1792, 15, 0, 10, 8388608, 0, 8, 15, 0, 7, 8421376, 0, 3, 15, 0, 10, 8433664,
+ 0, 1, 15, 0, 7, 8437760, 0, 4, 15, 0, 10, 8454144, 0, 240, 15, 0,
+ ];
+
+ #[test]
+ fn basic_functionality() {
+ let mut buf = MMAP_RAW;
+ let buf =
+ unsafe { slice::from_raw_parts_mut(buf.as_mut_ptr().cast::(), MMAP_META.map_size) };
+ let mut mmap = MemoryMapRefMut::new(buf, MMAP_META).unwrap();
+
+ assert!(mmap.is_sorted());
+ mmap.sort();
+ assert!(mmap.is_sorted());
+
+ let entries = mmap.entries().copied().collect::>();
+
+ let expected = [
+ MemoryDescriptor {
+ ty: MemoryType::BOOT_SERVICES_CODE,
+ phys_start: 0x0,
+ virt_start: 0x0,
+ page_count: 0x1,
+ att: MemoryAttribute::UNCACHEABLE
+ | MemoryAttribute::WRITE_COMBINE
+ | MemoryAttribute::WRITE_THROUGH
+ | MemoryAttribute::WRITE_BACK,
+ },
+ MemoryDescriptor {
+ ty: MemoryType::CONVENTIONAL,
+ phys_start: 0x1000,
+ virt_start: 0x0,
+ page_count: 0x86,
+ att: MemoryAttribute::UNCACHEABLE
+ | MemoryAttribute::WRITE_COMBINE
+ | MemoryAttribute::WRITE_THROUGH
+ | MemoryAttribute::WRITE_BACK,
+ },
+ MemoryDescriptor {
+ ty: MemoryType::BOOT_SERVICES_DATA,
+ phys_start: 0x87000,
+ virt_start: 0x0,
+ page_count: 0x1,
+ att: MemoryAttribute::UNCACHEABLE
+ | MemoryAttribute::WRITE_COMBINE
+ | MemoryAttribute::WRITE_THROUGH
+ | MemoryAttribute::WRITE_BACK,
+ },
+ MemoryDescriptor {
+ ty: MemoryType::CONVENTIONAL,
+ phys_start: 0x88000,
+ virt_start: 0x0,
+ page_count: 0x18,
+ att: MemoryAttribute::UNCACHEABLE
+ | MemoryAttribute::WRITE_COMBINE
+ | MemoryAttribute::WRITE_THROUGH
+ | MemoryAttribute::WRITE_BACK,
+ },
+ MemoryDescriptor {
+ ty: MemoryType::CONVENTIONAL,
+ phys_start: 0x100000,
+ virt_start: 0x0,
+ page_count: 0x700,
+ att: MemoryAttribute::UNCACHEABLE
+ | MemoryAttribute::WRITE_COMBINE
+ | MemoryAttribute::WRITE_THROUGH
+ | MemoryAttribute::WRITE_BACK,
+ },
+ MemoryDescriptor {
+ ty: MemoryType::ACPI_NON_VOLATILE,
+ phys_start: 0x800000,
+ virt_start: 0x0,
+ page_count: 0x8,
+ att: MemoryAttribute::UNCACHEABLE
+ | MemoryAttribute::WRITE_COMBINE
+ | MemoryAttribute::WRITE_THROUGH
+ | MemoryAttribute::WRITE_BACK,
+ },
+ MemoryDescriptor {
+ ty: MemoryType::CONVENTIONAL,
+ phys_start: 0x808000,
+ virt_start: 0x0,
+ page_count: 0x3,
+ att: MemoryAttribute::UNCACHEABLE
+ | MemoryAttribute::WRITE_COMBINE
+ | MemoryAttribute::WRITE_THROUGH
+ | MemoryAttribute::WRITE_BACK,
+ },
+ MemoryDescriptor {
+ ty: MemoryType::ACPI_NON_VOLATILE,
+ phys_start: 0x80b000,
+ virt_start: 0x0,
+ page_count: 0x1,
+ att: MemoryAttribute::UNCACHEABLE
+ | MemoryAttribute::WRITE_COMBINE
+ | MemoryAttribute::WRITE_THROUGH
+ | MemoryAttribute::WRITE_BACK,
+ },
+ MemoryDescriptor {
+ ty: MemoryType::CONVENTIONAL,
+ phys_start: 0x80c000,
+ virt_start: 0x0,
+ page_count: 0x4,
+ att: MemoryAttribute::UNCACHEABLE
+ | MemoryAttribute::WRITE_COMBINE
+ | MemoryAttribute::WRITE_THROUGH
+ | MemoryAttribute::WRITE_BACK,
+ },
+ MemoryDescriptor {
+ ty: MemoryType::ACPI_NON_VOLATILE,
+ phys_start: 0x810000,
+ virt_start: 0x0,
+ page_count: 0xf0,
+ att: MemoryAttribute::UNCACHEABLE
+ | MemoryAttribute::WRITE_COMBINE
+ | MemoryAttribute::WRITE_THROUGH
+ | MemoryAttribute::WRITE_BACK,
+ },
+ ];
+ assert_eq!(entries.as_slice(), &expected);
+ }
+}
diff --git a/uefi/src/mem/mod.rs b/uefi/src/mem/mod.rs
new file mode 100644
index 000000000..f3087cd8e
--- /dev/null
+++ b/uefi/src/mem/mod.rs
@@ -0,0 +1,9 @@
+//! Types, functions, traits, and other helpers to work with memory in UEFI
+//! libraries and applications.
+
+pub mod memory_map;
+#[cfg(feature = "alloc")]
+pub(crate) mod util;
+
+#[cfg(feature = "alloc")]
+pub(crate) use util::*;
diff --git a/uefi/src/mem.rs b/uefi/src/mem/util.rs
similarity index 98%
rename from uefi/src/mem.rs
rename to uefi/src/mem/util.rs
index 1d0d66d41..6df38d916 100644
--- a/uefi/src/mem.rs
+++ b/uefi/src/mem/util.rs
@@ -54,7 +54,10 @@ pub(crate) fn make_boxed<
// Propagate any other error.
Err((status, _)) => Err(Error::from(status)),
// Success is unexpected, return an error.
- Ok(_) => Err(Error::from(Status::UNSUPPORTED)),
+ Ok(_) => {
+ log::debug!("Got unexpected success status");
+ Err(Error::from(Status::UNSUPPORTED))
+ }
}?;
// We add trailing padding because the size of a rust structure must
diff --git a/uefi/src/proto/boot_policy.rs b/uefi/src/proto/boot_policy.rs
new file mode 100644
index 000000000..d46edf2ff
--- /dev/null
+++ b/uefi/src/proto/boot_policy.rs
@@ -0,0 +1,108 @@
+//! Module for the [`BootPolicy`] helper type.
+
+use core::fmt::{Display, Formatter};
+
+/// Errors that can happen when working with [`BootPolicy`].
+#[derive(Debug, Copy, Clone, PartialOrd, PartialEq, Eq, Ord)]
+pub enum BootPolicyError {
+ /// Only `0` and `1` are valid integers, all other values are undefined.
+ InvalidInteger(u8),
+}
+
+impl Display for BootPolicyError {
+ fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
+ let s = match self {
+ Self::InvalidInteger(_) => {
+ "Only `0` and `1` are valid integers, all other values are undefined."
+ }
+ };
+ f.write_str(s)
+ }
+}
+
+#[cfg(feature = "unstable")]
+impl core::error::Error for BootPolicyError {}
+
+/// The UEFI boot policy is a property that influences the behaviour of
+/// various UEFI functions that load files (typically UEFI images).
+///
+/// This type is not ABI compatible. On the ABI level, this is an UEFI
+/// boolean.
+#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
+pub enum BootPolicy {
+ /// Indicates that the request originates from the boot manager, and that
+ /// the boot manager is attempting to load the provided `file_path` as a
+ /// boot selection.
+ ///
+ /// Boot selection refers to what a user has chosen in the (GUI) boot menu.
+ ///
+ /// This corresponds to the `TRUE` value in the UEFI spec.
+ BootSelection,
+ /// The provided `file_path` must match an exact file to be loaded.
+ ///
+ /// This corresponds to the `FALSE` value in the UEFI spec.
+ #[default]
+ ExactMatch,
+}
+
+impl From for bool {
+ fn from(value: BootPolicy) -> Self {
+ match value {
+ BootPolicy::BootSelection => true,
+ BootPolicy::ExactMatch => false,
+ }
+ }
+}
+
+impl From for BootPolicy {
+ fn from(value: bool) -> Self {
+ match value {
+ true => Self::BootSelection,
+ false => Self::ExactMatch,
+ }
+ }
+}
+
+impl From for u8 {
+ fn from(value: BootPolicy) -> Self {
+ match value {
+ BootPolicy::BootSelection => 1,
+ BootPolicy::ExactMatch => 0,
+ }
+ }
+}
+
+impl TryFrom for BootPolicy {
+ type Error = BootPolicyError;
+ fn try_from(value: u8) -> Result {
+ match value {
+ 0 => Ok(Self::ExactMatch),
+ 1 => Ok(Self::BootSelection),
+ err => Err(Self::Error::InvalidInteger(err)),
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn boot_policy() {
+ assert_eq!(bool::from(BootPolicy::ExactMatch), false);
+ assert_eq!(bool::from(BootPolicy::BootSelection), true);
+
+ assert_eq!(BootPolicy::from(false), BootPolicy::ExactMatch);
+ assert_eq!(BootPolicy::from(true), BootPolicy::BootSelection);
+
+ assert_eq!(u8::from(BootPolicy::ExactMatch), 0);
+ assert_eq!(u8::from(BootPolicy::BootSelection), 1);
+
+ assert_eq!(BootPolicy::try_from(0), Ok(BootPolicy::ExactMatch));
+ assert_eq!(BootPolicy::try_from(1), Ok(BootPolicy::BootSelection));
+ assert_eq!(
+ BootPolicy::try_from(2),
+ Err(BootPolicyError::InvalidInteger(2))
+ );
+ }
+}
diff --git a/uefi/src/proto/console/gop.rs b/uefi/src/proto/console/gop.rs
index 5ae81596d..0676df547 100644
--- a/uefi/src/proto/console/gop.rs
+++ b/uefi/src/proto/console/gop.rs
@@ -102,7 +102,7 @@ impl GraphicsOutput {
/// Returns a [`ModeIter`].
#[must_use]
- pub fn modes<'a>(&'a self, bs: &'a BootServices) -> ModeIter {
+ pub const fn modes<'a>(&'a self, bs: &'a BootServices) -> ModeIter {
ModeIter {
gop: self,
bs,
diff --git a/uefi/src/proto/console/text/input.rs b/uefi/src/proto/console/text/input.rs
index 20bb2db83..e681c97cf 100644
--- a/uefi/src/proto/console/text/input.rs
+++ b/uefi/src/proto/console/text/input.rs
@@ -98,11 +98,11 @@ pub enum Key {
}
impl From for Key {
- fn from(k: InputKey) -> Key {
+ fn from(k: InputKey) -> Self {
if k.scan_code == ScanCode::NULL.0 {
- Key::Printable(Char16::try_from(k.unicode_char).unwrap())
+ Self::Printable(Char16::try_from(k.unicode_char).unwrap())
} else {
- Key::Special(ScanCode(k.scan_code))
+ Self::Special(ScanCode(k.scan_code))
}
}
}
diff --git a/uefi/src/proto/debug/exception.rs b/uefi/src/proto/debug/exception.rs
index 4298a54b6..b2d453842 100644
--- a/uefi/src/proto/debug/exception.rs
+++ b/uefi/src/proto/debug/exception.rs
@@ -5,27 +5,27 @@ pub struct ExceptionType(isize);
impl ExceptionType {
/// Undefined Exception
- pub const EXCEPT_EBC_UNDEFINED: ExceptionType = ExceptionType(0);
+ pub const EXCEPT_EBC_UNDEFINED: Self = Self(0);
/// Divide-by-zero Error
- pub const EXCEPT_EBC_DIVIDE_ERROR: ExceptionType = ExceptionType(1);
+ pub const EXCEPT_EBC_DIVIDE_ERROR: Self = Self(1);
/// Debug Exception
- pub const EXCEPT_EBC_DEBUG: ExceptionType = ExceptionType(2);
+ pub const EXCEPT_EBC_DEBUG: Self = Self(2);
/// Breakpoint
- pub const EXCEPT_EBC_BREAKPOINT: ExceptionType = ExceptionType(3);
+ pub const EXCEPT_EBC_BREAKPOINT: Self = Self(3);
/// Overflow
- pub const EXCEPT_EBC_OVERFLOW: ExceptionType = ExceptionType(4);
+ pub const EXCEPT_EBC_OVERFLOW: Self = Self(4);
/// Invalid Opcode
- pub const EXCEPT_EBC_INVALID_OPCODE: ExceptionType = ExceptionType(5);
+ pub const EXCEPT_EBC_INVALID_OPCODE: Self = Self(5);
/// Stack-Segment Fault
- pub const EXCEPT_EBC_STACK_FAULT: ExceptionType = ExceptionType(6);
+ pub const EXCEPT_EBC_STACK_FAULT: Self = Self(6);
/// Alignment Check
- pub const EXCEPT_EBC_ALIGNMENT_CHECK: ExceptionType = ExceptionType(7);
+ pub const EXCEPT_EBC_ALIGNMENT_CHECK: Self = Self(7);
/// Instruction Encoding Exception
- pub const EXCEPT_EBC_INSTRUCTION_ENCODING: ExceptionType = ExceptionType(8);
+ pub const EXCEPT_EBC_INSTRUCTION_ENCODING: Self = Self(8);
/// Bad Breakpoint Exception
- pub const EXCEPT_EBC_BAD_BREAK: ExceptionType = ExceptionType(9);
+ pub const EXCEPT_EBC_BAD_BREAK: Self = Self(9);
/// Single Step Exception
- pub const EXCEPT_EBC_SINGLE_STEP: ExceptionType = ExceptionType(10);
+ pub const EXCEPT_EBC_SINGLE_STEP: Self = Self(10);
}
#[cfg(target_arch = "x86")]
@@ -69,39 +69,39 @@ impl ExceptionType {
#[cfg(target_arch = "x86_64")]
impl ExceptionType {
/// Divide-by-zero Error
- pub const EXCEPT_X64_DIVIDE_ERROR: ExceptionType = ExceptionType(0);
+ pub const EXCEPT_X64_DIVIDE_ERROR: Self = Self(0);
/// Debug Exception
- pub const EXCEPT_X64_DEBUG: ExceptionType = ExceptionType(1);
+ pub const EXCEPT_X64_DEBUG: Self = Self(1);
/// Non-maskable Interrupt
- pub const EXCEPT_X64_NMI: ExceptionType = ExceptionType(2);
+ pub const EXCEPT_X64_NMI: Self = Self(2);
/// Breakpoint
- pub const EXCEPT_X64_BREAKPOINT: ExceptionType = ExceptionType(3);
+ pub const EXCEPT_X64_BREAKPOINT: Self = Self(3);
/// Overflow
- pub const EXCEPT_X64_OVERFLOW: ExceptionType = ExceptionType(4);
+ pub const EXCEPT_X64_OVERFLOW: Self = Self(4);
/// Bound Range Exceeded
- pub const EXCEPT_X64_BOUND: ExceptionType = ExceptionType(5);
+ pub const EXCEPT_X64_BOUND: Self = Self(5);
/// Invalid Opcode
- pub const EXCEPT_X64_INVALID_OPCODE: ExceptionType = ExceptionType(6);
+ pub const EXCEPT_X64_INVALID_OPCODE: Self = Self(6);
/// Double Fault
- pub const EXCEPT_X64_DOUBLE_FAULT: ExceptionType = ExceptionType(8);
+ pub const EXCEPT_X64_DOUBLE_FAULT: Self = Self(8);
/// Invalid TSS
- pub const EXCEPT_X64_INVALID_TSS: ExceptionType = ExceptionType(10);
+ pub const EXCEPT_X64_INVALID_TSS: Self = Self(10);
/// Segment Not Present
- pub const EXCEPT_X64_SEG_NOT_PRESENT: ExceptionType = ExceptionType(11);
+ pub const EXCEPT_X64_SEG_NOT_PRESENT: Self = Self(11);
/// Stack-Segment Fault
- pub const EXCEPT_X64_STACK_FAULT: ExceptionType = ExceptionType(12);
+ pub const EXCEPT_X64_STACK_FAULT: Self = Self(12);
/// General Protection Fault
- pub const EXCEPT_X64_GP_FAULT: ExceptionType = ExceptionType(13);
+ pub const EXCEPT_X64_GP_FAULT: Self = Self(13);
/// Page Fault
- pub const EXCEPT_X64_PAGE_FAULT: ExceptionType = ExceptionType(14);
+ pub const EXCEPT_X64_PAGE_FAULT: Self = Self(14);
/// x87 Floating-Point Exception
- pub const EXCEPT_X64_FP_ERROR: ExceptionType = ExceptionType(16);
+ pub const EXCEPT_X64_FP_ERROR: Self = Self(16);
/// Alignment Check
- pub const EXCEPT_X64_ALIGNMENT_CHECK: ExceptionType = ExceptionType(17);
+ pub const EXCEPT_X64_ALIGNMENT_CHECK: Self = Self(17);
/// Machine Check
- pub const EXCEPT_X64_MACHINE_CHECK: ExceptionType = ExceptionType(18);
+ pub const EXCEPT_X64_MACHINE_CHECK: Self = Self(18);
/// SIMD Floating-Point Exception
- pub const EXCEPT_X64_SIMD: ExceptionType = ExceptionType(19);
+ pub const EXCEPT_X64_SIMD: Self = Self(19);
}
#[cfg(target_arch = "arm")]
diff --git a/uefi/src/proto/device_path/device_path_gen.rs b/uefi/src/proto/device_path/device_path_gen.rs
index cbd855a16..9dc098c1c 100644
--- a/uefi/src/proto/device_path/device_path_gen.rs
+++ b/uefi/src/proto/device_path/device_path_gen.rs
@@ -4,14 +4,15 @@
// `cargo xtask gen-code`
//
// See `/xtask/src/device_path/README.md` for more details.
+#![allow(clippy::missing_const_for_fn)]
use crate::data_types::UnalignedSlice;
+use crate::mem::memory_map::MemoryType;
use crate::polyfill::maybe_uninit_slice_as_mut_ptr;
use crate::proto::device_path::{
DevicePathHeader, DevicePathNode, DeviceSubType, DeviceType, NodeConversionError,
};
use crate::proto::network::IpAddress;
-use crate::table::boot::MemoryType;
use crate::{guid, Guid};
use bitflags::bitflags;
use core::mem::{size_of, size_of_val};
diff --git a/uefi/src/proto/device_path/mod.rs b/uefi/src/proto/device_path/mod.rs
index 52ce35417..feae09d97 100644
--- a/uefi/src/proto/device_path/mod.rs
+++ b/uefi/src/proto/device_path/mod.rs
@@ -171,7 +171,7 @@ impl DevicePathNode {
/// remain valid for the lifetime `'a`, and cannot be mutated during
/// that lifetime.
#[must_use]
- pub unsafe fn from_ffi_ptr<'a>(ptr: *const FfiDevicePath) -> &'a DevicePathNode {
+ pub unsafe fn from_ffi_ptr<'a>(ptr: *const FfiDevicePath) -> &'a Self {
let header = *ptr.cast::();
let data_len = usize::from(header.length) - mem::size_of::();
@@ -217,7 +217,7 @@ impl DevicePathNode {
/// Returns the payload data of this node.
#[must_use]
- pub fn data(&self) -> &[u8] {
+ pub const fn data(&self) -> &[u8] {
&self.data
}
@@ -339,7 +339,7 @@ impl PartialEq for DevicePathInstance {
#[cfg(feature = "alloc")]
impl ToOwned for DevicePathInstance {
- type Owned = Box;
+ type Owned = Box;
fn to_owned(&self) -> Self::Owned {
self.to_boxed()
@@ -433,7 +433,7 @@ impl DevicePath {
/// remain valid for the lifetime `'a`, and cannot be mutated during
/// that lifetime.
#[must_use]
- pub unsafe fn from_ffi_ptr<'a>(ptr: *const FfiDevicePath) -> &'a DevicePath {
+ pub unsafe fn from_ffi_ptr<'a>(ptr: *const FfiDevicePath) -> &'a Self {
&*Self::ptr_from_ffi(ptr.cast::())
}
@@ -527,7 +527,7 @@ impl<'a> TryFrom<&'a [u8]> for &'a DevicePath {
#[cfg(feature = "alloc")]
impl ToOwned for DevicePath {
- type Owned = Box;
+ type Owned = Box;
fn to_owned(&self) -> Self::Owned {
self.to_boxed()
@@ -678,118 +678,118 @@ pub struct DeviceSubType(pub u8);
impl DeviceSubType {
/// PCI Device Path.
- pub const HARDWARE_PCI: DeviceSubType = DeviceSubType(1);
+ pub const HARDWARE_PCI: Self = Self(1);
/// PCCARD Device Path.
- pub const HARDWARE_PCCARD: DeviceSubType = DeviceSubType(2);
+ pub const HARDWARE_PCCARD: Self = Self(2);
/// Memory-mapped Device Path.
- pub const HARDWARE_MEMORY_MAPPED: DeviceSubType = DeviceSubType(3);
+ pub const HARDWARE_MEMORY_MAPPED: Self = Self(3);
/// Vendor-Defined Device Path.
- pub const HARDWARE_VENDOR: DeviceSubType = DeviceSubType(4);
+ pub const HARDWARE_VENDOR: Self = Self(4);
/// Controller Device Path.
- pub const HARDWARE_CONTROLLER: DeviceSubType = DeviceSubType(5);
+ pub const HARDWARE_CONTROLLER: Self = Self(5);
/// BMC Device Path.
- pub const HARDWARE_BMC: DeviceSubType = DeviceSubType(6);
+ pub const HARDWARE_BMC: Self = Self(6);
/// ACPI Device Path.
- pub const ACPI: DeviceSubType = DeviceSubType(1);
+ pub const ACPI: Self = Self(1);
/// Expanded ACPI Device Path.
- pub const ACPI_EXPANDED: DeviceSubType = DeviceSubType(2);
+ pub const ACPI_EXPANDED: Self = Self(2);
/// ACPI _ADR Device Path.
- pub const ACPI_ADR: DeviceSubType = DeviceSubType(3);
+ pub const ACPI_ADR: Self = Self(3);
/// NVDIMM Device Path.
- pub const ACPI_NVDIMM: DeviceSubType = DeviceSubType(4);
+ pub const ACPI_NVDIMM: Self = Self(4);
/// ATAPI Device Path.
- pub const MESSAGING_ATAPI: DeviceSubType = DeviceSubType(1);
+ pub const MESSAGING_ATAPI: Self = Self(1);
/// SCSI Device Path.
- pub const MESSAGING_SCSI: DeviceSubType = DeviceSubType(2);
+ pub const MESSAGING_SCSI: Self = Self(2);
/// Fibre Channel Device Path.
- pub const MESSAGING_FIBRE_CHANNEL: DeviceSubType = DeviceSubType(3);
+ pub const MESSAGING_FIBRE_CHANNEL: Self = Self(3);
/// 1394 Device Path.
- pub const MESSAGING_1394: DeviceSubType = DeviceSubType(4);
+ pub const MESSAGING_1394: Self = Self(4);
/// USB Device Path.
- pub const MESSAGING_USB: DeviceSubType = DeviceSubType(5);
+ pub const MESSAGING_USB: Self = Self(5);
/// I2O Device Path.
- pub const MESSAGING_I2O: DeviceSubType = DeviceSubType(6);
+ pub const MESSAGING_I2O: Self = Self(6);
/// Infiniband Device Path.
- pub const MESSAGING_INFINIBAND: DeviceSubType = DeviceSubType(9);
+ pub const MESSAGING_INFINIBAND: Self = Self(9);
/// Vendor-Defined Device Path.
- pub const MESSAGING_VENDOR: DeviceSubType = DeviceSubType(10);
+ pub const MESSAGING_VENDOR: Self = Self(10);
/// MAC Address Device Path.
- pub const MESSAGING_MAC_ADDRESS: DeviceSubType = DeviceSubType(11);
+ pub const MESSAGING_MAC_ADDRESS: Self = Self(11);
/// IPV4 Device Path.
- pub const MESSAGING_IPV4: DeviceSubType = DeviceSubType(12);
+ pub const MESSAGING_IPV4: Self = Self(12);
/// IPV6 Device Path.
- pub const MESSAGING_IPV6: DeviceSubType = DeviceSubType(13);
+ pub const MESSAGING_IPV6: Self = Self(13);
/// UART Device Path.
- pub const MESSAGING_UART: DeviceSubType = DeviceSubType(14);
+ pub const MESSAGING_UART: Self = Self(14);
/// USB Class Device Path.
- pub const MESSAGING_USB_CLASS: DeviceSubType = DeviceSubType(15);
+ pub const MESSAGING_USB_CLASS: Self = Self(15);
/// USB WWID Device Path.
- pub const MESSAGING_USB_WWID: DeviceSubType = DeviceSubType(16);
+ pub const MESSAGING_USB_WWID: Self = Self(16);
/// Device Logical Unit.
- pub const MESSAGING_DEVICE_LOGICAL_UNIT: DeviceSubType = DeviceSubType(17);
+ pub const MESSAGING_DEVICE_LOGICAL_UNIT: Self = Self(17);
/// SATA Device Path.
- pub const MESSAGING_SATA: DeviceSubType = DeviceSubType(18);
+ pub const MESSAGING_SATA: Self = Self(18);
/// iSCSI Device Path node (base information).
- pub const MESSAGING_ISCSI: DeviceSubType = DeviceSubType(19);
+ pub const MESSAGING_ISCSI: Self = Self(19);
/// VLAN Device Path node.
- pub const MESSAGING_VLAN: DeviceSubType = DeviceSubType(20);
+ pub const MESSAGING_VLAN: Self = Self(20);
/// Fibre Channel Ex Device Path.
- pub const MESSAGING_FIBRE_CHANNEL_EX: DeviceSubType = DeviceSubType(21);
+ pub const MESSAGING_FIBRE_CHANNEL_EX: Self = Self(21);
/// Serial Attached SCSI (SAS) Ex Device Path.
- pub const MESSAGING_SCSI_SAS_EX: DeviceSubType = DeviceSubType(22);
+ pub const MESSAGING_SCSI_SAS_EX: Self = Self(22);
/// NVM Express Namespace Device Path.
- pub const MESSAGING_NVME_NAMESPACE: DeviceSubType = DeviceSubType(23);
+ pub const MESSAGING_NVME_NAMESPACE: Self = Self(23);
/// Uniform Resource Identifiers (URI) Device Path.
- pub const MESSAGING_URI: DeviceSubType = DeviceSubType(24);
+ pub const MESSAGING_URI: Self = Self(24);
/// UFS Device Path.
- pub const MESSAGING_UFS: DeviceSubType = DeviceSubType(25);
+ pub const MESSAGING_UFS: Self = Self(25);
/// SD (Secure Digital) Device Path.
- pub const MESSAGING_SD: DeviceSubType = DeviceSubType(26);
+ pub const MESSAGING_SD: Self = Self(26);
/// Bluetooth Device Path.
- pub const MESSAGING_BLUETOOTH: DeviceSubType = DeviceSubType(27);
+ pub const MESSAGING_BLUETOOTH: Self = Self(27);
/// Wi-Fi Device Path.
- pub const MESSAGING_WIFI: DeviceSubType = DeviceSubType(28);
+ pub const MESSAGING_WIFI: Self = Self(28);
/// eMMC (Embedded Multi-Media Card) Device Path.
- pub const MESSAGING_EMMC: DeviceSubType = DeviceSubType(29);
+ pub const MESSAGING_EMMC: Self = Self(29);
/// BluetoothLE Device Path.
- pub const MESSAGING_BLUETOOTH_LE: DeviceSubType = DeviceSubType(30);
+ pub const MESSAGING_BLUETOOTH_LE: Self = Self(30);
/// DNS Device Path.
- pub const MESSAGING_DNS: DeviceSubType = DeviceSubType(31);
+ pub const MESSAGING_DNS: Self = Self(31);
/// NVDIMM Namespace Device Path.
- pub const MESSAGING_NVDIMM_NAMESPACE: DeviceSubType = DeviceSubType(32);
+ pub const MESSAGING_NVDIMM_NAMESPACE: Self = Self(32);
/// REST Service Device Path.
- pub const MESSAGING_REST_SERVICE: DeviceSubType = DeviceSubType(33);
+ pub const MESSAGING_REST_SERVICE: Self = Self(33);
/// NVME over Fabric (NVMe-oF) Namespace Device Path.
- pub const MESSAGING_NVME_OF_NAMESPACE: DeviceSubType = DeviceSubType(34);
+ pub const MESSAGING_NVME_OF_NAMESPACE: Self = Self(34);
/// Hard Drive Media Device Path.
- pub const MEDIA_HARD_DRIVE: DeviceSubType = DeviceSubType(1);
+ pub const MEDIA_HARD_DRIVE: Self = Self(1);
/// CD-ROM Media Device Path.
- pub const MEDIA_CD_ROM: DeviceSubType = DeviceSubType(2);
+ pub const MEDIA_CD_ROM: Self = Self(2);
/// Vendor-Defined Media Device Path.
- pub const MEDIA_VENDOR: DeviceSubType = DeviceSubType(3);
+ pub const MEDIA_VENDOR: Self = Self(3);
/// File Path Media Device Path.
- pub const MEDIA_FILE_PATH: DeviceSubType = DeviceSubType(4);
+ pub const MEDIA_FILE_PATH: Self = Self(4);
/// Media Protocol Device Path.
- pub const MEDIA_PROTOCOL: DeviceSubType = DeviceSubType(5);
+ pub const MEDIA_PROTOCOL: Self = Self(5);
/// PIWG Firmware File.
- pub const MEDIA_PIWG_FIRMWARE_FILE: DeviceSubType = DeviceSubType(6);
+ pub const MEDIA_PIWG_FIRMWARE_FILE: Self = Self(6);
/// PIWG Firmware Volume.
- pub const MEDIA_PIWG_FIRMWARE_VOLUME: DeviceSubType = DeviceSubType(7);
+ pub const MEDIA_PIWG_FIRMWARE_VOLUME: Self = Self(7);
/// Relative Offset Range.
- pub const MEDIA_RELATIVE_OFFSET_RANGE: DeviceSubType = DeviceSubType(8);
+ pub const MEDIA_RELATIVE_OFFSET_RANGE: Self = Self(8);
/// RAM Disk Device Path.
- pub const MEDIA_RAM_DISK: DeviceSubType = DeviceSubType(9);
+ pub const MEDIA_RAM_DISK: Self = Self(9);
/// BIOS Boot Specification Device Path.
- pub const BIOS_BOOT_SPECIFICATION: DeviceSubType = DeviceSubType(1);
+ pub const BIOS_BOOT_SPECIFICATION: Self = Self(1);
/// End this instance of a Device Path and start a new one.
- pub const END_INSTANCE: DeviceSubType = DeviceSubType(0x01);
+ pub const END_INSTANCE: Self = Self(0x01);
/// End entire Device Path.
- pub const END_ENTIRE: DeviceSubType = DeviceSubType(0xff);
+ pub const END_ENTIRE: Self = Self(0xff);
}
/// Error returned when attempting to convert from a `&[u8]` to a
diff --git a/uefi/src/proto/loaded_image.rs b/uefi/src/proto/loaded_image.rs
index 0070b668d..7e1bf2071 100644
--- a/uefi/src/proto/loaded_image.rs
+++ b/uefi/src/proto/loaded_image.rs
@@ -1,9 +1,9 @@
//! `LoadedImage` protocol.
use crate::data_types::FromSliceWithNulError;
+use crate::mem::memory_map::MemoryType;
use crate::proto::device_path::DevicePath;
use crate::proto::unsafe_protocol;
-use crate::table::boot::MemoryType;
use crate::util::usize_from_u32;
use crate::{CStr16, Handle, Status};
use core::ffi::c_void;
@@ -185,7 +185,7 @@ impl LoadedImage {
/// - `MemoryType::BOOT_SERVICES_CODE` for UEFI boot drivers
/// - `MemoryType::RUNTIME_SERVICES_CODE` for UEFI runtime drivers
#[must_use]
- pub fn code_type(&self) -> MemoryType {
+ pub const fn code_type(&self) -> MemoryType {
self.0.image_code_type
}
@@ -196,7 +196,7 @@ impl LoadedImage {
/// - `MemoryType::BOOT_SERVICES_DATA` for UEFI boot drivers
/// - `MemoryType::RUNTIME_SERVICES_DATA` for UEFI runtime drivers
#[must_use]
- pub fn data_type(&self) -> MemoryType {
+ pub const fn data_type(&self) -> MemoryType {
self.0.image_data_type
}
}
diff --git a/uefi/src/proto/media/file/dir.rs b/uefi/src/proto/media/file/dir.rs
index 1ba826963..9ab3b49c2 100644
--- a/uefi/src/proto/media/file/dir.rs
+++ b/uefi/src/proto/media/file/dir.rs
@@ -22,7 +22,7 @@ impl Directory {
/// This function should only be called on files which ARE directories,
/// doing otherwise is unsafe.
#[must_use]
- pub unsafe fn new(handle: FileHandle) -> Self {
+ pub const unsafe fn new(handle: FileHandle) -> Self {
Self(RegularFile::new(handle))
}
diff --git a/uefi/src/proto/media/file/info.rs b/uefi/src/proto/media/file/info.rs
index 847c02d95..adce16c4d 100644
--- a/uefi/src/proto/media/file/info.rs
+++ b/uefi/src/proto/media/file/info.rs
@@ -248,13 +248,13 @@ impl FileInfo {
/// Returns if the file is a directory.
#[must_use]
- pub fn is_directory(&self) -> bool {
+ pub const fn is_directory(&self) -> bool {
self.attribute.contains(FileAttribute::DIRECTORY)
}
/// Returns if the file is a regular file.
#[must_use]
- pub fn is_regular_file(&self) -> bool {
+ pub const fn is_regular_file(&self) -> bool {
!self.is_directory()
}
}
diff --git a/uefi/src/proto/media/file/regular.rs b/uefi/src/proto/media/file/regular.rs
index c3f23a5d8..1656b9006 100644
--- a/uefi/src/proto/media/file/regular.rs
+++ b/uefi/src/proto/media/file/regular.rs
@@ -19,7 +19,7 @@ impl RegularFile {
/// This function should only be called on handles which ARE NOT directories,
/// doing otherwise is unsafe.
#[must_use]
- pub unsafe fn new(handle: FileHandle) -> Self {
+ pub const unsafe fn new(handle: FileHandle) -> Self {
Self(handle)
}
diff --git a/uefi/src/proto/media/load_file.rs b/uefi/src/proto/media/load_file.rs
new file mode 100644
index 000000000..6f5d74634
--- /dev/null
+++ b/uefi/src/proto/media/load_file.rs
@@ -0,0 +1,160 @@
+//! LoadFile and LoadFile2 protocols.
+
+use crate::proto::unsafe_protocol;
+#[cfg(all(feature = "alloc", feature = "unstable"))]
+use alloc::alloc::Global;
+use uefi_raw::protocol::media::{LoadFile2Protocol, LoadFileProtocol};
+#[cfg(feature = "alloc")]
+use {
+ crate::{mem::make_boxed, proto::device_path::DevicePath, Result, StatusExt},
+ alloc::boxed::Box,
+ uefi::proto::BootPolicy,
+};
+
+/// Load File Protocol.
+///
+/// Used to obtain files, that are primarily boot options, from arbitrary
+/// devices.
+///
+/// # UEFI Spec Description
+/// The EFI_LOAD_FILE_PROTOCOL is a simple protocol used to obtain files from
+/// arbitrary devices.
+///
+/// When the firmware is attempting to load a file, it first attempts to use the
+/// device’s Simple File System protocol to read the file. If the file system
+/// protocol is found, the firmware implements the policy of interpreting the
+/// File Path value of the file being loaded. If the device does not support the
+/// file system protocol, the firmware then attempts to read the file via the
+/// EFI_LOAD_FILE_PROTOCOL and the LoadFile() function. In this case the
+/// LoadFile() function implements the policy of interpreting the File Path
+/// value.
+#[derive(Debug)]
+#[repr(transparent)]
+#[unsafe_protocol(LoadFileProtocol::GUID)]
+pub struct LoadFile(LoadFileProtocol);
+
+impl LoadFile {
+ /// Causes the driver to load a specified file.
+ ///
+ /// # Parameters
+ /// - `file_path` The device specific path of the file to load.
+ /// - `boot_policy` The [`BootPolicy`] to use.
+ ///
+ /// # Errors
+ /// - `uefi::status::EFI_SUCCESS` The file was loaded.
+ /// - `uefi::status::EFI_UNSUPPORTED` The device does not support the
+ /// provided BootPolicy.
+ /// - `uefi::status::EFI_INVALID_PARAMETER` FilePath is not a valid device
+ /// path, or BufferSize is NULL.
+ /// - `uefi::status::EFI_NO_MEDIA` No medium was present to load the file.
+ /// - `uefi::status::EFI_DEVICE_ERROR` The file was not loaded due to a
+ /// device error.
+ /// - `uefi::status::EFI_NO_RESPONSE` The remote system did not respond.
+ /// - `uefi::status::EFI_NOT_FOUND` The file was not found.
+ /// - `uefi::status::EFI_ABORTED` The file load process was manually
+ /// cancelled.
+ /// - `uefi::status::EFI_BUFFER_TOO_SMALL` The BufferSize is too small to
+ /// read the current directory entry. BufferSize has been updated with the
+ /// size needed to complete the request.
+ /// - `uefi::status::EFI_WARN_FILE_SYSTEM` The resulting Buffer contains
+ /// UEFI-compliant file system.
+ ///
+ /// [`BootPolicy`]: uefi::proto::BootPolicy
+ #[cfg(feature = "alloc")]
+ #[allow(clippy::extra_unused_lifetimes)] // false positive, it is used
+ pub fn load_file<'a>(
+ &mut self,
+ file_path: &DevicePath,
+ boot_policy: BootPolicy,
+ ) -> Result> {
+ let this = core::ptr::addr_of_mut!(*self).cast();
+
+ let fetch_data_fn = |buf: &'a mut [u8]| {
+ let mut size = buf.len();
+ let status = unsafe {
+ (self.0.load_file)(
+ this,
+ file_path.as_ffi_ptr().cast(),
+ boot_policy.into(),
+ &mut size,
+ buf.as_mut_ptr().cast(),
+ )
+ };
+ status.to_result_with_err(|_| Some(size)).map(|_| buf)
+ };
+
+ #[cfg(not(feature = "unstable"))]
+ let file: Box<[u8]> = make_boxed::<[u8], _>(fetch_data_fn)?;
+
+ #[cfg(feature = "unstable")]
+ let file = make_boxed::<[u8], _, _>(fetch_data_fn, Global)?;
+
+ Ok(file)
+ }
+}
+
+/// Load File2 Protocol.
+///
+/// The Load File2 protocol is used to obtain files from arbitrary devices that
+/// are not boot options.
+///
+/// # UEFI Spec Description
+///
+/// The EFI_LOAD_FILE2_PROTOCOL is a simple protocol used to obtain files from
+/// arbitrary devices that are not boot options. It is used by LoadImage() when
+/// its BootOption parameter is FALSE and the FilePath does not have an instance
+/// of the EFI_SIMPLE_FILE_SYSTEM_PROTOCOL.
+#[derive(Debug)]
+#[repr(transparent)]
+#[unsafe_protocol(LoadFile2Protocol::GUID)]
+pub struct LoadFile2(LoadFile2Protocol);
+
+impl LoadFile2 {
+ /// Causes the driver to load a specified file.
+ ///
+ /// # Parameters
+ /// - `file_path` The device specific path of the file to load.
+ ///
+ /// # Errors
+ /// - `uefi::status::EFI_SUCCESS` The file was loaded.
+ /// - `uefi::status::EFI_UNSUPPORTED` BootPolicy is TRUE.
+ /// - `uefi::status::EFI_INVALID_PARAMETER` FilePath is not a valid device
+ /// path, or BufferSize is NULL.
+ /// - `uefi::status::EFI_NO_MEDIA` No medium was present to load the file.
+ /// - `uefi::status::EFI_DEVICE_ERROR` The file was not loaded due to a
+ /// device error.
+ /// - `uefi::status::EFI_NO_RESPONSE` The remote system did not respond.
+ /// - `uefi::status::EFI_NOT_FOUND` The file was not found.
+ /// - `uefi::status::EFI_ABORTED` The file load process was manually
+ /// cancelled.
+ /// - `uefi::status::EFI_BUFFER_TOO_SMALL` The BufferSize is too small to
+ /// read the current directory entry. BufferSize has been updated with the
+ /// size needed to complete the request.
+ #[cfg(feature = "alloc")]
+ #[allow(clippy::extra_unused_lifetimes)] // false positive, it is used
+ pub fn load_file<'a>(&mut self, file_path: &DevicePath) -> Result> {
+ let this = core::ptr::addr_of_mut!(*self).cast();
+
+ let fetch_data_fn = |buf: &'a mut [u8]| {
+ let mut size = buf.len();
+ let status = unsafe {
+ (self.0.load_file)(
+ this,
+ file_path.as_ffi_ptr().cast(),
+ false, /* always false - see spec */
+ &mut size,
+ buf.as_mut_ptr().cast(),
+ )
+ };
+ status.to_result_with_err(|_| Some(size)).map(|_| buf)
+ };
+
+ #[cfg(not(feature = "unstable"))]
+ let file: Box<[u8]> = make_boxed::<[u8], _>(fetch_data_fn)?;
+
+ #[cfg(feature = "unstable")]
+ let file = make_boxed::<[u8], _, _>(fetch_data_fn, Global)?;
+
+ Ok(file)
+ }
+}
diff --git a/uefi/src/proto/media/mod.rs b/uefi/src/proto/media/mod.rs
index 6750875a6..cd1473a73 100644
--- a/uefi/src/proto/media/mod.rs
+++ b/uefi/src/proto/media/mod.rs
@@ -9,4 +9,5 @@ pub mod file;
pub mod block;
pub mod disk;
pub mod fs;
+pub mod load_file;
pub mod partition;
diff --git a/uefi/src/proto/mod.rs b/uefi/src/proto/mod.rs
index 5919b7afc..218f3ffb0 100644
--- a/uefi/src/proto/mod.rs
+++ b/uefi/src/proto/mod.rs
@@ -9,6 +9,27 @@
//!
//! [`BootServices`]: crate::table::boot::BootServices#accessing-protocols
+pub mod console;
+pub mod debug;
+pub mod device_path;
+pub mod driver;
+pub mod loaded_image;
+pub mod media;
+pub mod misc;
+pub mod network;
+pub mod pi;
+pub mod rng;
+pub mod security;
+pub mod shell_params;
+pub mod shim;
+pub mod string;
+pub mod tcg;
+
+mod boot_policy;
+
+pub use boot_policy::{BootPolicy, BootPolicyError};
+pub use uefi_macros::unsafe_protocol;
+
use crate::Identify;
use core::ffi::c_void;
@@ -63,21 +84,3 @@ where
ptr.cast::()
}
}
-
-pub use uefi_macros::unsafe_protocol;
-
-pub mod console;
-pub mod debug;
-pub mod device_path;
-pub mod driver;
-pub mod loaded_image;
-pub mod media;
-pub mod misc;
-pub mod network;
-pub mod pi;
-pub mod rng;
-pub mod security;
-pub mod shell_params;
-pub mod shim;
-pub mod string;
-pub mod tcg;
diff --git a/uefi/src/proto/network/snp.rs b/uefi/src/proto/network/snp.rs
index ee9c2a999..05a7ffd7f 100644
--- a/uefi/src/proto/network/snp.rs
+++ b/uefi/src/proto/network/snp.rs
@@ -261,13 +261,13 @@ impl SimpleNetwork {
/// On QEMU, this event seems to never fire; it is suggested to verify that your implementation
/// of UEFI properly implements this event before using it.
#[must_use]
- pub fn wait_for_packet(&self) -> &Event {
+ pub const fn wait_for_packet(&self) -> &Event {
&self.wait_for_packet
}
/// Returns a reference to the Simple Network mode.
#[must_use]
- pub fn mode(&self) -> &NetworkMode {
+ pub const fn mode(&self) -> &NetworkMode {
unsafe { &*self.mode }
}
}
@@ -347,14 +347,14 @@ pub struct NetworkStats {
impl NetworkStats {
/// Any statistic value of -1 is not available
- fn available(&self, stat: u64) -> bool {
+ const fn available(&self, stat: u64) -> bool {
stat as i64 != -1
}
/// Takes a statistic and converts it to an option
///
/// When the statistic is not available, `None` is returned
- fn to_option(&self, stat: u64) -> Option {
+ const fn to_option(&self, stat: u64) -> Option {
match self.available(stat) {
true => Some(stat),
false => None,
@@ -364,175 +364,175 @@ impl NetworkStats {
/// The total number of frames received, including error frames
/// and dropped frames
#[must_use]
- pub fn rx_total_frames(&self) -> Option {
+ pub const fn rx_total_frames(&self) -> Option {
self.to_option(self.rx_total_frames)
}
/// The total number of good frames received and copied
/// into receive buffers
#[must_use]
- pub fn rx_good_frames(&self) -> Option {
+ pub const fn rx_good_frames(&self) -> Option {
self.to_option(self.rx_good_frames)
}
/// The number of frames below the minimum length for the
/// communications device
#[must_use]
- pub fn rx_undersize_frames(&self) -> Option {
+ pub const fn rx_undersize_frames(&self) -> Option {
self.to_option(self.rx_undersize_frames)
}
/// The number of frames longer than the maximum length for
/// the communications length device
#[must_use]
- pub fn rx_oversize_frames(&self) -> Option {
+ pub const fn rx_oversize_frames(&self) -> Option {
self.to_option(self.rx_oversize_frames)
}
/// The number of valid frames that were dropped because
/// the receive buffers were full
#[must_use]
- pub fn rx_dropped_frames(&self) -> Option {
+ pub const fn rx_dropped_frames(&self) -> Option {
self.to_option(self.rx_dropped_frames)
}
/// The number of valid unicast frames received and not dropped
#[must_use]
- pub fn rx_unicast_frames(&self) -> Option {
+ pub const fn rx_unicast_frames(&self) -> Option {
self.to_option(self.rx_unicast_frames)
}
/// The number of valid broadcast frames received and not dropped
#[must_use]
- pub fn rx_broadcast_frames(&self) -> Option {
+ pub const fn rx_broadcast_frames(&self) -> Option {
self.to_option(self.rx_broadcast_frames)
}
/// The number of valid multicast frames received and not dropped
#[must_use]
- pub fn rx_multicast_frames(&self) -> Option {
+ pub const fn rx_multicast_frames(&self) -> Option {
self.to_option(self.rx_multicast_frames)
}
/// Number of frames with CRC or alignment errors
#[must_use]
- pub fn rx_crc_error_frames(&self) -> Option {
+ pub const fn rx_crc_error_frames(&self) -> Option {
self.to_option(self.rx_crc_error_frames)
}
/// The total number of bytes received including frames with errors
/// and dropped frames
#[must_use]
- pub fn rx_total_bytes(&self) -> Option {
+ pub const fn rx_total_bytes(&self) -> Option {
self.to_option(self.rx_total_bytes)
}
/// The total number of frames transmitted including frames
/// with errors and dropped frames
#[must_use]
- pub fn tx_total_frames(&self) -> Option {
+ pub const fn tx_total_frames(&self) -> Option {
self.to_option(self.tx_total_frames)
}
/// The total number of valid frames transmitted and copied
/// into receive buffers
#[must_use]
- pub fn tx_good_frames(&self) -> Option {
+ pub const fn tx_good_frames(&self) -> Option {
self.to_option(self.tx_good_frames)
}
/// The number of frames below the minimum length for
/// the media. This would be less than 64 for Ethernet
#[must_use]
- pub fn tx_undersize_frames(&self) -> Option {
+ pub const fn tx_undersize_frames(&self) -> Option {
self.to_option(self.tx_undersize_frames)
}
/// The number of frames longer than the maximum length for
/// the media. This would be 1500 for Ethernet
#[must_use]
- pub fn tx_oversize_frames(&self) -> Option {
+ pub const fn tx_oversize_frames(&self) -> Option {
self.to_option(self.tx_oversize_frames)
}
/// The number of valid frames that were dropped because
/// received buffers were full
#[must_use]
- pub fn tx_dropped_frames(&self) -> Option {
+ pub const fn tx_dropped_frames(&self) -> Option {
self.to_option(self.tx_dropped_frames)
}
/// The number of valid unicast frames transmitted and not
/// dropped
#[must_use]
- pub fn tx_unicast_frames(&self) -> Option {
+ pub const fn tx_unicast_frames(&self) -> Option {
self.to_option(self.tx_unicast_frames)
}
/// The number of valid broadcast frames transmitted and
/// not dropped
#[must_use]
- pub fn tx_broadcast_frames(&self) -> Option {
+ pub const fn tx_broadcast_frames(&self) -> Option {
self.to_option(self.tx_broadcast_frames)
}
/// The number of valid multicast frames transmitted
/// and not dropped
#[must_use]
- pub fn tx_multicast_frames(&self) -> Option {
+ pub const fn tx_multicast_frames(&self) -> Option {
self.to_option(self.tx_multicast_frames)
}
/// The number of transmitted frames with CRC or
/// alignment errors
#[must_use]
- pub fn tx_crc_error_frames(&self) -> Option {
+ pub const fn tx_crc_error_frames(&self) -> Option {
self.to_option(self.tx_crc_error_frames)
}
/// The total number of bytes transmitted including
/// error frames and dropped frames
#[must_use]
- pub fn tx_total_bytes(&self) -> Option {
+ pub const fn tx_total_bytes(&self) -> Option {
self.to_option(self.tx_total_bytes)
}
/// The number of collisions detected on this subnet
#[must_use]
- pub fn collisions(&self) -> Option