uefi_raw/
lib.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3//! Raw interface for working with UEFI.
4//!
5//! This crate is intended for implementing UEFI services. It is also used for
6//! implementing the [`uefi`] crate, which provides a safe wrapper around UEFI.
7//!
8//! For creating UEFI applications and drivers, consider using the [`uefi`]
9//! crate instead of `uefi-raw`.
10//!
11//! [`uefi`]: https://crates.io/crates/uefi
12
13#![no_std]
14#![cfg_attr(docsrs, feature(doc_auto_cfg))]
15#![deny(
16    clippy::all,
17    clippy::missing_const_for_fn,
18    clippy::must_use_candidate,
19    clippy::ptr_as_ptr,
20    clippy::use_self,
21    missing_debug_implementations,
22    unused
23)]
24
25#[macro_use]
26mod enums;
27
28pub mod capsule;
29pub mod firmware_storage;
30pub mod protocol;
31pub mod table;
32pub mod time;
33
34mod status;
35
36pub use status::Status;
37pub use uguid::{guid, Guid};
38
39use core::ffi::c_void;
40use core::fmt::{self, Debug, Formatter};
41
42/// Handle to an event structure.
43pub type Event = *mut c_void;
44
45/// Handle to a UEFI entity (protocol, image, etc).
46pub type Handle = *mut c_void;
47
48/// One-byte character.
49///
50/// Most strings in UEFI use [`Char16`], but a few places use one-byte
51/// characters. Unless otherwise noted, these are encoded as 8-bit ASCII using
52/// the ISO-Latin-1 character set.
53pub type Char8 = u8;
54
55/// Two-byte character.
56///
57/// Unless otherwise noted, the encoding is UCS-2. The UCS-2 encoding was
58/// defined by Unicode 2.1 and ISO/IEC 10646 standards, but is no longer part of
59/// the modern Unicode standards. It is essentially UTF-16 without support for
60/// surrogate pairs.
61pub type Char16 = u16;
62
63/// Physical memory address. This is always a 64-bit value, regardless
64/// of target platform.
65pub type PhysicalAddress = u64;
66
67/// Virtual memory address. This is always a 64-bit value, regardless
68/// of target platform.
69pub type VirtualAddress = u64;
70
71/// ABI-compatible UEFI boolean.
72///
73/// This is similar to a `bool`, but allows values other than 0 or 1 to be
74/// stored without it being undefined behavior.
75///
76/// Any non-zero value is treated as logically `true`.
77#[derive(Copy, Clone, Debug, Default, PartialEq, Ord, PartialOrd, Eq, Hash)]
78#[repr(transparent)]
79pub struct Boolean(pub u8);
80
81impl Boolean {
82    /// [`Boolean`] representing `true`.
83    pub const TRUE: Self = Self(1);
84
85    /// [`Boolean`] representing `false`.
86    pub const FALSE: Self = Self(0);
87}
88
89impl From<bool> for Boolean {
90    fn from(value: bool) -> Self {
91        match value {
92            true => Self(1),
93            false => Self(0),
94        }
95    }
96}
97
98impl From<Boolean> for bool {
99    #[allow(clippy::match_like_matches_macro)]
100    fn from(value: Boolean) -> Self {
101        // We handle it as in C: Any bit-pattern != 0 equals true
102        match value.0 {
103            0 => false,
104            _ => true,
105        }
106    }
107}
108
109/// An IPv4 internet protocol address.
110#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
111#[repr(transparent)]
112pub struct Ipv4Address(pub [u8; 4]);
113
114impl From<core::net::Ipv4Addr> for Ipv4Address {
115    fn from(ip: core::net::Ipv4Addr) -> Self {
116        Self(ip.octets())
117    }
118}
119
120impl From<Ipv4Address> for core::net::Ipv4Addr {
121    fn from(ip: Ipv4Address) -> Self {
122        Self::from(ip.0)
123    }
124}
125
126/// An IPv6 internet protocol address.
127#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
128#[repr(transparent)]
129pub struct Ipv6Address(pub [u8; 16]);
130
131impl From<core::net::Ipv6Addr> for Ipv6Address {
132    fn from(ip: core::net::Ipv6Addr) -> Self {
133        Self(ip.octets())
134    }
135}
136
137impl From<Ipv6Address> for core::net::Ipv6Addr {
138    fn from(ip: Ipv6Address) -> Self {
139        Self::from(ip.0)
140    }
141}
142
143/// An IPv4 or IPv6 internet protocol address.
144///
145/// Corresponds to the `EFI_IP_ADDRESS` type in the UEFI specification. This
146/// type is defined in the same way as edk2 for compatibility with C code. Note
147/// that this is an untagged union, so there's no way to tell which type of
148/// address an `IpAddress` value contains without additional context.
149#[derive(Clone, Copy)]
150#[repr(C)]
151pub union IpAddress {
152    /// This member serves to align the whole type to a 4 bytes as required by
153    /// the spec. Note that this is slightly different from `repr(align(4))`,
154    /// which would prevent placing this type in a packed structure.
155    pub addr: [u32; 4],
156
157    /// An IPv4 internet protocol address.
158    pub v4: Ipv4Address,
159
160    /// An IPv6 internet protocol address.
161    pub v6: Ipv6Address,
162}
163
164impl IpAddress {
165    /// Construct a new IPv4 address.
166    #[must_use]
167    pub const fn new_v4(ip_addr: [u8; 4]) -> Self {
168        Self {
169            v4: Ipv4Address(ip_addr),
170        }
171    }
172
173    /// Construct a new IPv6 address.
174    #[must_use]
175    pub const fn new_v6(ip_addr: [u8; 16]) -> Self {
176        Self {
177            v6: Ipv6Address(ip_addr),
178        }
179    }
180}
181
182impl Debug for IpAddress {
183    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
184        // The type is an untagged union, so we don't know whether it contains
185        // an IPv4 or IPv6 address. It's also not safe to just print the whole
186        // 16 bytes, since they might not all be initialized.
187        f.debug_struct("IpAddress").finish()
188    }
189}
190
191impl Default for IpAddress {
192    fn default() -> Self {
193        Self { addr: [0u32; 4] }
194    }
195}
196
197impl From<core::net::IpAddr> for IpAddress {
198    fn from(t: core::net::IpAddr) -> Self {
199        match t {
200            core::net::IpAddr::V4(ip) => Self {
201                v4: Ipv4Address::from(ip),
202            },
203            core::net::IpAddr::V6(ip) => Self {
204                v6: Ipv6Address::from(ip),
205            },
206        }
207    }
208}
209
210/// A Media Access Control (MAC) address.
211#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
212#[repr(transparent)]
213pub struct MacAddress(pub [u8; 32]);
214
215impl From<[u8; 6]> for MacAddress {
216    fn from(octets: [u8; 6]) -> Self {
217        let mut buffer = [0; 32];
218        buffer[0] = octets[0];
219        buffer[1] = octets[1];
220        buffer[2] = octets[2];
221        buffer[3] = octets[3];
222        buffer[4] = octets[4];
223        buffer[5] = octets[5];
224        Self(buffer)
225    }
226}
227
228impl From<MacAddress> for [u8; 6] {
229    fn from(MacAddress(o): MacAddress) -> Self {
230        [o[0], o[1], o[2], o[3], o[4], o[5]]
231    }
232}
233
234#[cfg(test)]
235mod tests {
236    use super::*;
237
238    const TEST_IPV4: [u8; 4] = [91, 92, 93, 94];
239    const TEST_IPV6: [u8; 16] = [
240        101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116,
241    ];
242
243    #[test]
244    /// Test the properties promised in [0]. This also applies for the other
245    /// architectures.
246    ///
247    /// [0] https://github.com/tianocore/edk2/blob/b0f43dd3fdec2363e3548ec31eb455dc1c4ac761/MdePkg/Include/X64/ProcessorBind.h#L192
248    fn test_boolean_abi() {
249        assert_eq!(size_of::<Boolean>(), 1);
250        assert_eq!(Boolean::from(true).0, 1);
251        assert_eq!(Boolean::from(false).0, 0);
252        assert_eq!(Boolean::TRUE.0, 1);
253        assert_eq!(Boolean::FALSE.0, 0);
254        assert!(!bool::from(Boolean(0b0)));
255        assert!(bool::from(Boolean(0b1)));
256        // We do it as in C: Every bit pattern not 0 is equal to true.
257        assert!(bool::from(Boolean(0b11111110)));
258        assert!(bool::from(Boolean(0b11111111)));
259    }
260
261    /// Test round-trip conversion between `Ipv4Address` and `core::net::Ipv4Addr`.
262    #[test]
263    fn test_ip_addr4_conversion() {
264        let uefi_addr = Ipv4Address(TEST_IPV4);
265        let core_addr = core::net::Ipv4Addr::from(uefi_addr);
266        assert_eq!(uefi_addr, Ipv4Address::from(core_addr));
267    }
268
269    /// Test round-trip conversion between `Ipv6Address` and `core::net::Ipv6Addr`.
270    #[test]
271    fn test_ip_addr6_conversion() {
272        let uefi_addr = Ipv6Address(TEST_IPV6);
273        let core_addr = core::net::Ipv6Addr::from(uefi_addr);
274        assert_eq!(uefi_addr, Ipv6Address::from(core_addr));
275    }
276
277    /// Test conversion from `core::net::IpAddr` to `IpvAddress`.
278    ///
279    /// Note that conversion in the other direction is not possible.
280    #[test]
281    fn test_ip_addr_conversion() {
282        let core_addr = core::net::IpAddr::V4(core::net::Ipv4Addr::from(TEST_IPV4));
283        let uefi_addr = IpAddress::from(core_addr);
284        assert_eq!(unsafe { uefi_addr.v4.0 }, TEST_IPV4);
285
286        let core_addr = core::net::IpAddr::V6(core::net::Ipv6Addr::from(TEST_IPV6));
287        let uefi_addr = IpAddress::from(core_addr);
288        assert_eq!(unsafe { uefi_addr.v6.0 }, TEST_IPV6);
289    }
290}