blob: 7f8050b5659fc3c226f347edd90f60286b20061c [file] [log] [blame] [edit]
// SPDX-License-Identifier: MIT OR Apache-2.0
//! Raw interface for working with UEFI.
//!
//! This crate is intended for implementing UEFI services. It is also used for
//! implementing the [`uefi`] crate, which provides a safe wrapper around UEFI.
//!
//! For creating UEFI applications and drivers, consider using the [`uefi`]
//! crate instead of `uefi-raw`.
//!
//! [`uefi`]: https://crates.io/crates/uefi
#![no_std]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![deny(
clippy::all,
clippy::missing_const_for_fn,
clippy::must_use_candidate,
clippy::ptr_as_ptr,
clippy::use_self,
missing_debug_implementations,
unused
)]
#[macro_use]
mod enums;
pub mod capsule;
pub mod firmware_storage;
pub mod protocol;
pub mod table;
pub mod time;
mod status;
pub use status::Status;
pub use uguid::{guid, Guid};
use core::ffi::c_void;
use core::fmt::{self, Debug, Formatter};
/// Handle to an event structure.
pub type Event = *mut c_void;
/// Handle to a UEFI entity (protocol, image, etc).
pub type Handle = *mut c_void;
/// One-byte character.
///
/// Most strings in UEFI use [`Char16`], but a few places use one-byte
/// characters. Unless otherwise noted, these are encoded as 8-bit ASCII using
/// the ISO-Latin-1 character set.
pub type Char8 = u8;
/// Two-byte character.
///
/// Unless otherwise noted, the encoding is UCS-2. The UCS-2 encoding was
/// defined by Unicode 2.1 and ISO/IEC 10646 standards, but is no longer part of
/// the modern Unicode standards. It is essentially UTF-16 without support for
/// surrogate pairs.
pub type Char16 = u16;
/// Physical memory address. This is always a 64-bit value, regardless
/// of target platform.
pub type PhysicalAddress = u64;
/// Virtual memory address. This is always a 64-bit value, regardless
/// of target platform.
pub type VirtualAddress = u64;
/// ABI-compatible UEFI boolean.
///
/// This is similar to a `bool`, but allows values other than 0 or 1 to be
/// stored without it being undefined behavior.
///
/// Any non-zero value is treated as logically `true`.
#[derive(Copy, Clone, Debug, Default, PartialEq, Ord, PartialOrd, Eq, Hash)]
#[repr(transparent)]
pub struct Boolean(pub u8);
impl Boolean {
/// [`Boolean`] representing `true`.
pub const TRUE: Self = Self(1);
/// [`Boolean`] representing `false`.
pub const FALSE: Self = Self(0);
}
impl From<bool> for Boolean {
fn from(value: bool) -> Self {
match value {
true => Self(1),
false => Self(0),
}
}
}
impl From<Boolean> for bool {
#[allow(clippy::match_like_matches_macro)]
fn from(value: Boolean) -> Self {
// We handle it as in C: Any bit-pattern != 0 equals true
match value.0 {
0 => false,
_ => true,
}
}
}
/// An IPv4 internet protocol address.
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(transparent)]
pub struct Ipv4Address(pub [u8; 4]);
impl From<core::net::Ipv4Addr> for Ipv4Address {
fn from(ip: core::net::Ipv4Addr) -> Self {
Self(ip.octets())
}
}
impl From<Ipv4Address> for core::net::Ipv4Addr {
fn from(ip: Ipv4Address) -> Self {
Self::from(ip.0)
}
}
/// An IPv6 internet protocol address.
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(transparent)]
pub struct Ipv6Address(pub [u8; 16]);
impl From<core::net::Ipv6Addr> for Ipv6Address {
fn from(ip: core::net::Ipv6Addr) -> Self {
Self(ip.octets())
}
}
impl From<Ipv6Address> for core::net::Ipv6Addr {
fn from(ip: Ipv6Address) -> Self {
Self::from(ip.0)
}
}
/// An IPv4 or IPv6 internet protocol address.
///
/// Corresponds to the `EFI_IP_ADDRESS` type in the UEFI specification. This
/// type is defined in the same way as edk2 for compatibility with C code. Note
/// that this is an untagged union, so there's no way to tell which type of
/// address an `IpAddress` value contains without additional context.
#[derive(Clone, Copy)]
#[repr(C)]
pub union IpAddress {
/// This member serves to align the whole type to a 4 bytes as required by
/// the spec. Note that this is slightly different from `repr(align(4))`,
/// which would prevent placing this type in a packed structure.
pub addr: [u32; 4],
/// An IPv4 internet protocol address.
pub v4: Ipv4Address,
/// An IPv6 internet protocol address.
pub v6: Ipv6Address,
}
impl IpAddress {
/// Construct a new IPv4 address.
#[must_use]
pub const fn new_v4(ip_addr: [u8; 4]) -> Self {
Self {
v4: Ipv4Address(ip_addr),
}
}
/// Construct a new IPv6 address.
#[must_use]
pub const fn new_v6(ip_addr: [u8; 16]) -> Self {
Self {
v6: Ipv6Address(ip_addr),
}
}
}
impl Debug for IpAddress {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
// The type is an untagged union, so we don't know whether it contains
// an IPv4 or IPv6 address. It's also not safe to just print the whole
// 16 bytes, since they might not all be initialized.
f.debug_struct("IpAddress").finish()
}
}
impl Default for IpAddress {
fn default() -> Self {
Self { addr: [0u32; 4] }
}
}
impl From<core::net::IpAddr> for IpAddress {
fn from(t: core::net::IpAddr) -> Self {
match t {
core::net::IpAddr::V4(ip) => Self {
v4: Ipv4Address::from(ip),
},
core::net::IpAddr::V6(ip) => Self {
v6: Ipv6Address::from(ip),
},
}
}
}
/// A Media Access Control (MAC) address.
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(transparent)]
pub struct MacAddress(pub [u8; 32]);
impl From<[u8; 6]> for MacAddress {
fn from(octets: [u8; 6]) -> Self {
let mut buffer = [0; 32];
buffer[0] = octets[0];
buffer[1] = octets[1];
buffer[2] = octets[2];
buffer[3] = octets[3];
buffer[4] = octets[4];
buffer[5] = octets[5];
Self(buffer)
}
}
impl From<MacAddress> for [u8; 6] {
fn from(MacAddress(o): MacAddress) -> Self {
[o[0], o[1], o[2], o[3], o[4], o[5]]
}
}
#[cfg(test)]
mod tests {
use super::*;
const TEST_IPV4: [u8; 4] = [91, 92, 93, 94];
const TEST_IPV6: [u8; 16] = [
101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116,
];
#[test]
/// Test the properties promised in [0]. This also applies for the other
/// architectures.
///
/// [0] https://github.com/tianocore/edk2/blob/b0f43dd3fdec2363e3548ec31eb455dc1c4ac761/MdePkg/Include/X64/ProcessorBind.h#L192
fn test_boolean_abi() {
assert_eq!(size_of::<Boolean>(), 1);
assert_eq!(Boolean::from(true).0, 1);
assert_eq!(Boolean::from(false).0, 0);
assert_eq!(Boolean::TRUE.0, 1);
assert_eq!(Boolean::FALSE.0, 0);
assert!(!bool::from(Boolean(0b0)));
assert!(bool::from(Boolean(0b1)));
// We do it as in C: Every bit pattern not 0 is equal to true.
assert!(bool::from(Boolean(0b11111110)));
assert!(bool::from(Boolean(0b11111111)));
}
/// Test round-trip conversion between `Ipv4Address` and `core::net::Ipv4Addr`.
#[test]
fn test_ip_addr4_conversion() {
let uefi_addr = Ipv4Address(TEST_IPV4);
let core_addr = core::net::Ipv4Addr::from(uefi_addr);
assert_eq!(uefi_addr, Ipv4Address::from(core_addr));
}
/// Test round-trip conversion between `Ipv6Address` and `core::net::Ipv6Addr`.
#[test]
fn test_ip_addr6_conversion() {
let uefi_addr = Ipv6Address(TEST_IPV6);
let core_addr = core::net::Ipv6Addr::from(uefi_addr);
assert_eq!(uefi_addr, Ipv6Address::from(core_addr));
}
/// Test conversion from `core::net::IpAddr` to `IpvAddress`.
///
/// Note that conversion in the other direction is not possible.
#[test]
fn test_ip_addr_conversion() {
let core_addr = core::net::IpAddr::V4(core::net::Ipv4Addr::from(TEST_IPV4));
let uefi_addr = IpAddress::from(core_addr);
assert_eq!(unsafe { uefi_addr.v4.0 }, TEST_IPV4);
let core_addr = core::net::IpAddr::V6(core::net::Ipv6Addr::from(TEST_IPV6));
let uefi_addr = IpAddress::from(core_addr);
assert_eq!(unsafe { uefi_addr.v6.0 }, TEST_IPV6);
}
}