From ff424b56ac3e8687f044ba9cd8535e94e4ee6f56 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sat, 23 Aug 2025 14:40:39 +0200 Subject: [PATCH 1/5] uefi-raw: small code improvements for IpAddress --- uefi-raw/CHANGELOG.md | 1 + uefi-raw/src/net.rs | 33 ++++++++++++++++++++------------- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/uefi-raw/CHANGELOG.md b/uefi-raw/CHANGELOG.md index 8468d9e0d..99f1535f8 100644 --- a/uefi-raw/CHANGELOG.md +++ b/uefi-raw/CHANGELOG.md @@ -7,6 +7,7 @@ - Added `HiiConfigAccessProtocol`. - Added `::octets()` for `Ipv4Address`, `Ipv6Address`, and `MacAddress` to streamline the API with `core::net`. +- Added `::ZERO` constant for `IpAddress` ## Changed - **Breaking:** The MSRV is now 1.85.1 and the crate uses the Rust 2024 edition. diff --git a/uefi-raw/src/net.rs b/uefi-raw/src/net.rs index 773dd2e8f..c13f4e9b8 100644 --- a/uefi-raw/src/net.rs +++ b/uefi-raw/src/net.rs @@ -8,8 +8,7 @@ //! - [`Ipv4Address`] //! - [`Ipv6Address`] -use core::fmt; -use core::fmt::{Debug, Formatter}; +use core::fmt::{self, Debug, Formatter}; /// An IPv4 internet protocol address. #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)] @@ -83,19 +82,31 @@ pub union IpAddress { } impl IpAddress { + /// Zeroed variant where all bytes are guaranteed to be initialized to zero. + pub const ZERO: Self = Self { addr: [0; 4] }; + /// Construct a new IPv4 address. + /// + /// The type won't know that it is an IPv6 address and additional context + /// is needed. + /// + /// # Safety + /// The constructor only initializes the bytes needed for IPv4 addresses. #[must_use] - pub const fn new_v4(ip_addr: [u8; 4]) -> Self { + pub const fn new_v4(octets: [u8; 4]) -> Self { Self { - v4: Ipv4Address(ip_addr), + v4: Ipv4Address(octets), } } /// Construct a new IPv6 address. + /// + /// The type won't know that it is an IPv6 address and additional context + /// is needed. #[must_use] - pub const fn new_v6(ip_addr: [u8; 16]) -> Self { + pub const fn new_v6(octets: [u8; 16]) -> Self { Self { - v6: Ipv6Address(ip_addr), + v6: Ipv6Address(octets), } } } @@ -111,19 +122,15 @@ impl Debug for IpAddress { impl Default for IpAddress { fn default() -> Self { - Self { addr: [0u32; 4] } + Self::ZERO } } impl From 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), - }, + core::net::IpAddr::V4(ip) => Self::new_v4(ip.octets()), + core::net::IpAddr::V6(ip) => Self::new_v6(ip.octets()), } } } From 9fa22d9a05ab30baabf14dd0b465ff0e40117c5b Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sat, 23 Aug 2025 14:41:39 +0200 Subject: [PATCH 2/5] uefi-raw: add convenient From impls + integration with core::net - added missing conversions between core::net types and the uefi-raw net types - added missing conversions for "typical byte arrays" --- uefi-raw/CHANGELOG.md | 8 +++ uefi-raw/src/net.rs | 133 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 141 insertions(+) diff --git a/uefi-raw/CHANGELOG.md b/uefi-raw/CHANGELOG.md index 99f1535f8..39c8e5629 100644 --- a/uefi-raw/CHANGELOG.md +++ b/uefi-raw/CHANGELOG.md @@ -8,6 +8,14 @@ - Added `::octets()` for `Ipv4Address`, `Ipv6Address`, and `MacAddress` to streamline the API with `core::net`. - Added `::ZERO` constant for `IpAddress` +- Added comprehensive integration with `core::net::{IpAddr, Ipv4Addr, Ipv6Addr}` + via `From` impls to better integrate uefi-raw types `IpAddress`, + `Ipv4Address`, and `Ipv6Address` with the Rust ecosystem. +- Added convenient `From` impls: + - `[u8; 6]` --> `MacAddress` + - `[u8; 32]` --> `MacAddress` + - `[u8; 4]` --> `Ipv4Address`, `IpAddress` + - `[u8; 16]` --> `Ipv6Address`, `IpAddress` ## Changed - **Breaking:** The MSRV is now 1.85.1 and the crate uses the Rust 2024 edition. diff --git a/uefi-raw/src/net.rs b/uefi-raw/src/net.rs index c13f4e9b8..6d91aced3 100644 --- a/uefi-raw/src/net.rs +++ b/uefi-raw/src/net.rs @@ -11,6 +11,13 @@ use core::fmt::{self, Debug, Formatter}; /// An IPv4 internet protocol address. +/// +/// # Conversions and Relation to [`core::net`] +/// +/// The following [`From`] implementations exist: +/// - `[u8; 4]` -> [`Ipv4Address`] +/// - [`core::net::Ipv4Addr`] -> [`Ipv4Address`] +/// - [`core::net::IpAddr`] -> [`Ipv4Address`] #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)] #[repr(transparent)] pub struct Ipv4Address(pub [u8; 4]); @@ -35,7 +42,20 @@ impl From for core::net::Ipv4Addr { } } +impl From<[u8; 4]> for Ipv4Address { + fn from(octets: [u8; 4]) -> Self { + Self(octets) + } +} + /// An IPv6 internet protocol address. +/// +/// # Conversions and Relation to [`core::net`] +/// +/// The following [`From`] implementations exist: +/// - `[u8; 16]` -> [`Ipv6Address`] +/// - [`core::net::Ipv6Addr`] -> [`Ipv6Address`] +/// - [`core::net::IpAddr`] -> [`Ipv6Address`] #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)] #[repr(transparent)] pub struct Ipv6Address(pub [u8; 16]); @@ -60,12 +80,27 @@ impl From for core::net::Ipv6Addr { } } +impl From<[u8; 16]> for Ipv6Address { + fn from(octets: [u8; 16]) -> Self { + Self(octets) + } +} + /// An IPv4 or IPv6 internet protocol address that is ABI compatible with EFI. /// /// 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. +/// +/// # Conversions and Relation to [`core::net`] +/// +/// The following [`From`] implementations exist: +/// - `[u8; 4]` -> [`IpAddress`] +/// - `[u8; 16]` -> [`IpAddress`] +/// - [`core::net::Ipv4Addr`] -> [`IpAddress`] +/// - [`core::net::Ipv6Addr`] -> [`IpAddress`] +/// - [`core::net::IpAddr`] -> [`IpAddress`] #[derive(Clone, Copy)] #[repr(C)] pub union IpAddress { @@ -135,6 +170,30 @@ impl From for IpAddress { } } +impl From for IpAddress { + fn from(value: core::net::Ipv4Addr) -> Self { + Self::new_v4(value.octets()) + } +} + +impl From for IpAddress { + fn from(value: core::net::Ipv6Addr) -> Self { + Self::new_v6(value.octets()) + } +} + +impl From<[u8; 4]> for IpAddress { + fn from(octets: [u8; 4]) -> Self { + Self::new_v4(octets) + } +} + +impl From<[u8; 16]> for IpAddress { + fn from(octets: [u8; 16]) -> Self { + Self::new_v6(octets) + } +} + /// UEFI Media Access Control (MAC) address. /// /// UEFI supports multiple network protocols and hardware types, not just @@ -144,6 +203,13 @@ impl From for IpAddress { /// /// In most cases, this is just a typical `[u8; 6]` Ethernet style MAC /// address with the rest of the bytes being zero. +/// +/// # Conversions and Relation to [`core::net`] +/// +/// There is no matching type in [`core::net`] but the following [`From`] +/// implementations exist: +/// - `[u8; 6]` -> [`MacAddress`] +/// - `[u8; 32]` -> [`MacAddress`] #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)] #[repr(transparent)] pub struct MacAddress(pub [u8; 32]); @@ -171,6 +237,13 @@ impl From for [u8; 6] { } } +// UEFI MAC addresses. +impl From<[u8; 32]> for MacAddress { + fn from(octets: [u8; 32]) -> Self { + Self(octets) + } +} + #[cfg(test)] mod tests { use super::*; @@ -223,4 +296,64 @@ mod tests { assert_eq!(align_of::>(), 1); assert_eq!(size_of::>(), 16); } + + /// Tests the From-impls from the documentation. + #[test] + fn test_promised_from_impls() { + // octets -> Ipv4Address + { + let octets = [0_u8, 1, 2, 3]; + assert_eq!(Ipv4Address::from(octets), Ipv4Address(octets)); + let uefi_addr = IpAddress::from(octets); + assert_eq!(&octets, &unsafe { uefi_addr.v4.octets() }); + } + // octets -> Ipv6Address + { + let octets = [0_u8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; + assert_eq!(Ipv6Address::from(octets), Ipv6Address(octets)); + let uefi_addr = IpAddress::from(octets); + assert_eq!(&octets, &unsafe { uefi_addr.v6.octets() }); + } + // StdIpv4Addr -> Ipv4Address + { + let octets = [7, 5, 3, 1]; + let core_ipv4_addr = core::net::Ipv4Addr::from(octets); + assert_eq!(Ipv4Address::from(core_ipv4_addr).octets(), octets); + assert_eq!( + unsafe { IpAddress::from(core_ipv4_addr).v4.octets() }, + octets + ); + } + // StdIpv6Addr -> Ipv6Address + { + let octets = [7, 5, 3, 1, 6, 3, 8, 5, 2, 5, 2, 7, 3, 5, 2, 6]; + let core_ipv6_addr = core::net::Ipv6Addr::from(octets); + assert_eq!(Ipv6Address::from(core_ipv6_addr).octets(), octets); + assert_eq!( + unsafe { IpAddress::from(core_ipv6_addr).v6.octets() }, + octets + ); + } + // StdIpAddr -> IpAddress + { + let octets = [8, 8, 2, 6]; + let core_ip_addr = core::net::IpAddr::from(octets); + assert_eq!(unsafe { IpAddress::from(core_ip_addr).v4.octets() }, octets); + } + // octets -> MacAddress + { + let octets = [8, 8, 2, 6, 6, 7]; + let uefi_mac_addr = MacAddress::from(octets); + assert_eq!(uefi_mac_addr.octets()[0..6], octets); + } + // octets -> MacAddress + { + let octets = [ + 8_u8, 8, 2, 6, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 7, 0, 0, 0, + 0, 0, 0, 0, 42, + ]; + let uefi_mac_addr = MacAddress::from(octets); + assert_eq!(uefi_mac_addr.octets(), octets); + } + } } From a984cca7cb1394e02ca766780b6eb1740d9ad02d Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sat, 23 Aug 2025 14:41:19 +0200 Subject: [PATCH 3/5] uefi-raw: add convenient into_* helpers --- uefi-raw/CHANGELOG.md | 2 ++ uefi-raw/src/net.rs | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/uefi-raw/CHANGELOG.md b/uefi-raw/CHANGELOG.md index 39c8e5629..faa8db90f 100644 --- a/uefi-raw/CHANGELOG.md +++ b/uefi-raw/CHANGELOG.md @@ -16,6 +16,8 @@ - `[u8; 32]` --> `MacAddress` - `[u8; 4]` --> `Ipv4Address`, `IpAddress` - `[u8; 16]` --> `Ipv6Address`, `IpAddress` +- Added `::into_core_ip_addr()` for `IpAddress` +- Added `::try_into_ethernet_mac_addr()` for `MacAddress` ## Changed - **Breaking:** The MSRV is now 1.85.1 and the crate uses the Rust 2024 edition. diff --git a/uefi-raw/src/net.rs b/uefi-raw/src/net.rs index 6d91aced3..35176277d 100644 --- a/uefi-raw/src/net.rs +++ b/uefi-raw/src/net.rs @@ -144,6 +144,27 @@ impl IpAddress { v6: Ipv6Address(octets), } } + + /// Transforms this EFI type to the Rust standard library's type + /// [`core::net::IpAddr`]. + /// + /// # Arguments + /// - `is_ipv6`: Whether the internal data should be interpreted as IPv6 or + /// IPv4 address. + /// + /// # Safety + /// Callers must ensure that the `v4` field is valid if `is_ipv6` is false, + /// and that the `v6` field is valid if `is_ipv6` is true + #[must_use] + pub unsafe fn into_core_ip_addr(self, is_ipv6: bool) -> core::net::IpAddr { + if is_ipv6 { + // SAFETY: Caller assumes that the underlying data is initialized. + core::net::IpAddr::V6(core::net::Ipv6Addr::from(unsafe { self.v6.octets() })) + } else { + // SAFETY: Caller assumes that the underlying data is initialized. + core::net::IpAddr::V4(core::net::Ipv4Addr::from(unsafe { self.v4.octets() })) + } + } } impl Debug for IpAddress { @@ -220,6 +241,17 @@ impl MacAddress { pub const fn octets(self) -> [u8; 32] { self.0 } + + /// Tries to interpret the MAC address as normal 6-byte MAC address, as used + /// in ethernet. + pub fn try_into_ethernet_mac_addr(self) -> Result<[u8; 6], [u8; 32]> { + let extra = self.octets()[4..].iter().any(|&x| x != 0); + if extra { + Err(self.0) + } else { + Ok(self.octets()[..4].try_into().unwrap()) + } + } } // Normal/typical MAC addresses, such as in Ethernet. From 8fc20970dd141f63064d43e5a0dded774208a7d1 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Tue, 19 Aug 2025 10:50:21 +0200 Subject: [PATCH 4/5] uefi-raw: add unit test for typical high-level net API usage --- uefi-raw/src/net.rs | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/uefi-raw/src/net.rs b/uefi-raw/src/net.rs index 35176277d..800ac8cbd 100644 --- a/uefi-raw/src/net.rs +++ b/uefi-raw/src/net.rs @@ -388,4 +388,46 @@ mod tests { assert_eq!(uefi_mac_addr.octets(), octets); } } + + /// Tests the expected flow of types in a higher-level UEFI API. + #[test] + fn test_uefi_flow() { + fn efi_retrieve_efi_ip_addr(addr: *mut IpAddress, is_ipv6: bool) { + let addr = unsafe { addr.as_mut().unwrap() }; + // SAFETY: Alignment is guaranteed and memory is initialized. + unsafe { + addr.v4.0[0] = 42; + addr.v4.0[1] = 42; + addr.v4.0[2] = 42; + addr.v4.0[3] = 42; + } + if is_ipv6 { + unsafe { + addr.v6.0[14] = 42; + addr.v6.0[15] = 42; + } + } + } + + fn high_level_retrieve_ip(is_ipv6: bool) -> core::net::IpAddr { + let mut efi_ip_addr = IpAddress::ZERO; + efi_retrieve_efi_ip_addr(&mut efi_ip_addr, is_ipv6); + unsafe { efi_ip_addr.into_core_ip_addr(is_ipv6) } + } + + let ipv4_addr = high_level_retrieve_ip(false); + let ipv4_addr: core::net::Ipv4Addr = match ipv4_addr { + core::net::IpAddr::V4(ipv4_addr) => ipv4_addr, + core::net::IpAddr::V6(_) => panic!("should not happen"), + }; + assert_eq!(ipv4_addr.octets(), [42, 42, 42, 42]); + + let ipv6_addr = high_level_retrieve_ip(true); + let ipv6_addr: core::net::Ipv6Addr = match ipv6_addr { + core::net::IpAddr::V6(ipv6_addr) => ipv6_addr, + core::net::IpAddr::V4(_) => panic!("should not happen"), + }; + let expected = [42, 42, 42, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 42]; + assert_eq!(ipv6_addr.octets(), expected); + } } From cede8218f74dbb441d8f7c9381aceb93e589a59b Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Tue, 16 Sep 2025 08:38:09 +0200 Subject: [PATCH 5/5] uefi-raw: implement Display for Ipv4Address and Ipv6Address --- uefi-raw/src/net.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/uefi-raw/src/net.rs b/uefi-raw/src/net.rs index 800ac8cbd..5161dc42a 100644 --- a/uefi-raw/src/net.rs +++ b/uefi-raw/src/net.rs @@ -8,7 +8,7 @@ //! - [`Ipv4Address`] //! - [`Ipv6Address`] -use core::fmt::{self, Debug, Formatter}; +use core::fmt::{self, Debug, Display, Formatter}; /// An IPv4 internet protocol address. /// @@ -48,6 +48,13 @@ impl From<[u8; 4]> for Ipv4Address { } } +impl Display for Ipv4Address { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let ip = core::net::Ipv4Addr::from(*self); + write!(f, "{}", ip) + } +} + /// An IPv6 internet protocol address. /// /// # Conversions and Relation to [`core::net`] @@ -86,6 +93,13 @@ impl From<[u8; 16]> for Ipv6Address { } } +impl Display for Ipv6Address { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let ip = core::net::Ipv6Addr::from(*self); + write!(f, "{}", ip) + } +} + /// An IPv4 or IPv6 internet protocol address that is ABI compatible with EFI. /// /// Corresponds to the `EFI_IP_ADDRESS` type in the UEFI specification. This