From 5fb3476b43b19432b8ebe6043cf3ed5f408b02a0 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sat, 14 Jun 2025 21:24:08 +0200 Subject: [PATCH 1/6] uefi-raw: move types to net module We have enough network-related types to justify a dedicated module: - IpAddress - Ipv4Address - Ipv6Address - MacAddress And lots of corresponding impl{} blocks, tests, etc. --- uefi-raw/src/lib.rs | 162 +--------------- uefi-raw/src/net.rs | 176 ++++++++++++++++++ .../protocol/device_path/device_path_gen.rs | 3 +- uefi-raw/src/protocol/network/dhcp4.rs | 3 +- uefi-raw/src/protocol/network/http.rs | 3 +- uefi-raw/src/protocol/network/ip4.rs | 2 +- uefi-raw/src/protocol/network/ip4_config2.rs | 3 +- uefi-raw/src/protocol/network/pxe.rs | 3 +- uefi-raw/src/protocol/network/snp.rs | 3 +- uefi/src/proto/network/ip4config2.rs | 2 +- uefi/src/proto/network/mod.rs | 8 +- uefi/src/proto/network/pxe.rs | 4 +- xtask/src/device_path/mod.rs | 3 +- 13 files changed, 199 insertions(+), 176 deletions(-) create mode 100644 uefi-raw/src/net.rs diff --git a/uefi-raw/src/lib.rs b/uefi-raw/src/lib.rs index a67ac564d..95b36210d 100644 --- a/uefi-raw/src/lib.rs +++ b/uefi-raw/src/lib.rs @@ -27,6 +27,7 @@ mod enums; pub mod capsule; pub mod firmware_storage; +pub mod net; pub mod protocol; pub mod table; pub mod time; @@ -37,7 +38,6 @@ 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; @@ -106,140 +106,10 @@ impl From for bool { } } -/// 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 for Ipv4Address { - fn from(ip: core::net::Ipv4Addr) -> Self { - Self(ip.octets()) - } -} - -impl From 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 for Ipv6Address { - fn from(ip: core::net::Ipv6Addr) -> Self { - Self(ip.octets()) - } -} - -impl From 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 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 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. @@ -257,34 +127,4 @@ mod tests { 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); - } } diff --git a/uefi-raw/src/net.rs b/uefi-raw/src/net.rs new file mode 100644 index 000000000..4b28fc410 --- /dev/null +++ b/uefi-raw/src/net.rs @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 + +//! UEFI network types. + +use core::fmt; +use core::fmt::{Debug, Formatter}; + +/// 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 for Ipv4Address { + fn from(ip: core::net::Ipv4Addr) -> Self { + Self(ip.octets()) + } +} + +impl From 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 for Ipv6Address { + fn from(ip: core::net::Ipv6Addr) -> Self { + Self(ip.octets()) + } +} + +impl From 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 { + /// An IPv4 internet protocol address. + pub v4: Ipv4Address, + + /// An IPv6 internet protocol address. + pub v6: Ipv6Address, + + /// This member serves to align the whole type to 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 _align_helper: [u32; 4], +} + +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 { + _align_helper: [0u32; 4], + } + } +} + +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), + }, + } + } +} + +/// UEFI Media Access Control (MAC) address. +/// +/// UEFI supports multiple network protocols and hardware types, not just +/// Ethernet. Some of them may use MAC addresses longer than 6 bytes. To be +/// protocol-agnostic and future-proof, the UEFI spec chooses a maximum size +/// that can hold any supported media access control address. +/// +/// In most cases, this is just a typical `[u8; 6]` Ethernet style MAC +/// address with the rest of the bytes being zero. +#[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.copy_from_slice(&octets); + Self(buffer) + } +} + +impl From 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 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); + } +} diff --git a/uefi-raw/src/protocol/device_path/device_path_gen.rs b/uefi-raw/src/protocol/device_path/device_path_gen.rs index 825a27929..f31289d87 100644 --- a/uefi-raw/src/protocol/device_path/device_path_gen.rs +++ b/uefi-raw/src/protocol/device_path/device_path_gen.rs @@ -8,9 +8,10 @@ // See `/xtask/src/device_path/README.md` for more details. #![allow(clippy::missing_const_for_fn)] #![allow(missing_debug_implementations)] +use crate::net::IpAddress; use crate::protocol::device_path; use crate::table::boot::MemoryType; -use crate::{Guid, IpAddress, guid}; +use crate::{Guid, guid}; use bitflags::bitflags; use device_path::DevicePathProtocol as DevicePathHeader; #[cfg(doc)] diff --git a/uefi-raw/src/protocol/network/dhcp4.rs b/uefi-raw/src/protocol/network/dhcp4.rs index c8ee47098..0e15b991b 100644 --- a/uefi-raw/src/protocol/network/dhcp4.rs +++ b/uefi-raw/src/protocol/network/dhcp4.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -use crate::{Boolean, Char8, Event, Guid, Ipv4Address, MacAddress, Status, guid}; +use crate::net::{Ipv4Address, MacAddress}; +use crate::{Boolean, Char8, Event, Guid, Status, guid}; use core::ffi::c_void; newtype_enum! { diff --git a/uefi-raw/src/protocol/network/http.rs b/uefi-raw/src/protocol/network/http.rs index 115f5fe18..260e64371 100644 --- a/uefi-raw/src/protocol/network/http.rs +++ b/uefi-raw/src/protocol/network/http.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -use crate::{Boolean, Char8, Char16, Event, Guid, Ipv4Address, Ipv6Address, Status, guid}; +use crate::net::{Ipv4Address, Ipv6Address}; +use crate::{Boolean, Char8, Char16, Event, Guid, Status, guid}; use core::ffi::c_void; use core::fmt::{self, Debug, Formatter}; use core::ptr; diff --git a/uefi-raw/src/protocol/network/ip4.rs b/uefi-raw/src/protocol/network/ip4.rs index 88fb04d65..fe3723dd4 100644 --- a/uefi-raw/src/protocol/network/ip4.rs +++ b/uefi-raw/src/protocol/network/ip4.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -use crate::Ipv4Address; +use crate::net::Ipv4Address; #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] #[repr(C)] diff --git a/uefi-raw/src/protocol/network/ip4_config2.rs b/uefi-raw/src/protocol/network/ip4_config2.rs index 2bed6937d..3d852c5fd 100644 --- a/uefi-raw/src/protocol/network/ip4_config2.rs +++ b/uefi-raw/src/protocol/network/ip4_config2.rs @@ -1,7 +1,8 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 +use crate::net::{Ipv4Address, MacAddress}; use crate::protocol::network::ip4::Ip4RouteTable; -use crate::{Char16, Event, Guid, Ipv4Address, MacAddress, Status, guid}; +use crate::{Char16, Event, Guid, Status, guid}; use core::ffi::c_void; newtype_enum! { diff --git a/uefi-raw/src/protocol/network/pxe.rs b/uefi-raw/src/protocol/network/pxe.rs index 15d1a39b6..fc917718b 100644 --- a/uefi-raw/src/protocol/network/pxe.rs +++ b/uefi-raw/src/protocol/network/pxe.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -use crate::{Boolean, Char8, Guid, IpAddress, MacAddress, Status, guid}; +use crate::net::{IpAddress, MacAddress}; +use crate::{Boolean, Char8, Guid, Status, guid}; use bitflags::bitflags; use core::ffi::c_void; use core::fmt::{self, Debug, Formatter}; diff --git a/uefi-raw/src/protocol/network/snp.rs b/uefi-raw/src/protocol/network/snp.rs index 744cacd50..9a0d21695 100644 --- a/uefi-raw/src/protocol/network/snp.rs +++ b/uefi-raw/src/protocol/network/snp.rs @@ -4,7 +4,8 @@ use core::ffi; use bitflags::bitflags; -use crate::{Boolean, Event, Guid, IpAddress, MacAddress, Status, guid}; +use crate::net::{IpAddress, MacAddress}; +use crate::{Boolean, Event, Guid, Status, guid}; #[derive(Debug)] #[repr(C)] diff --git a/uefi/src/proto/network/ip4config2.rs b/uefi/src/proto/network/ip4config2.rs index 66b99bfae..8323de15b 100644 --- a/uefi/src/proto/network/ip4config2.rs +++ b/uefi/src/proto/network/ip4config2.rs @@ -13,7 +13,7 @@ use uefi::boot::ScopedProtocol; use uefi::prelude::*; use uefi::proto::unsafe_protocol; use uefi::{print, println}; -use uefi_raw::Ipv4Address; +use uefi_raw::net::Ipv4Address; use uefi_raw::protocol::network::ip4_config2::{ Ip4Config2DataType, Ip4Config2InterfaceInfo, Ip4Config2Policy, Ip4Config2Protocol, }; diff --git a/uefi/src/proto/network/mod.rs b/uefi/src/proto/network/mod.rs index 925287f2c..aab57114d 100644 --- a/uefi/src/proto/network/mod.rs +++ b/uefi/src/proto/network/mod.rs @@ -9,7 +9,7 @@ pub mod ip4config2; pub mod pxe; pub mod snp; -pub use uefi_raw::MacAddress; +pub use uefi_raw::net::MacAddress; /// Represents an IPv4/v6 address. /// @@ -42,7 +42,7 @@ impl IpAddress { /// /// `is_ipv6` must accurately reflect how the union was initialized. #[must_use] - const unsafe fn from_raw(ip_addr: uefi_raw::IpAddress, is_ipv6: bool) -> Self { + const unsafe fn from_raw(ip_addr: uefi_raw::net::IpAddress, is_ipv6: bool) -> Self { if is_ipv6 { Self::new_v6(unsafe { ip_addr.v6.0 }) } else { @@ -51,14 +51,14 @@ impl IpAddress { } #[must_use] - const fn as_raw_ptr(&self) -> *const uefi_raw::IpAddress { + const fn as_raw_ptr(&self) -> *const uefi_raw::net::IpAddress { // The uefi-raw type is defined differently, but the layout is // compatible. self.0.as_ptr().cast() } #[must_use] - const fn as_raw_ptr_mut(&mut self) -> *mut uefi_raw::IpAddress { + const fn as_raw_ptr_mut(&mut self) -> *mut uefi_raw::net::IpAddress { // The uefi-raw type is defined differently, but the layout is // compatible. self.0.as_mut_ptr().cast() diff --git a/uefi/src/proto/network/pxe.rs b/uefi/src/proto/network/pxe.rs index a46162151..77fd25a0f 100644 --- a/uefi/src/proto/network/pxe.rs +++ b/uefi/src/proto/network/pxe.rs @@ -559,12 +559,12 @@ fn opt_bool_to_ptr(arg: &Option) -> *const Boolean { } /// Convert an `Option<&IpAddress>` to a `*const uefi_raw::IpAddress`. -fn opt_ip_addr_to_ptr(arg: Option<&IpAddress>) -> *const uefi_raw::IpAddress { +fn opt_ip_addr_to_ptr(arg: Option<&IpAddress>) -> *const uefi_raw::net::IpAddress { arg.map(|arg| arg.as_raw_ptr()).unwrap_or_else(null) } /// Convert an `Option<&mut IpAddress>` to a `*mut uefi_raw::IpAddress`. -fn opt_ip_addr_to_ptr_mut(arg: Option<&mut IpAddress>) -> *mut uefi_raw::IpAddress { +fn opt_ip_addr_to_ptr_mut(arg: Option<&mut IpAddress>) -> *mut uefi_raw::net::IpAddress { arg.map(|arg| arg.as_raw_ptr_mut()).unwrap_or_else(null_mut) } diff --git a/xtask/src/device_path/mod.rs b/xtask/src/device_path/mod.rs index 821a705b6..740477018 100644 --- a/xtask/src/device_path/mod.rs +++ b/xtask/src/device_path/mod.rs @@ -54,7 +54,8 @@ fn gen_uefi_raw_code_as_string(groups: &[NodeGroup]) -> Result { use bitflags::bitflags; use crate::protocol::device_path; use crate::table::boot::MemoryType; - use crate::{Guid, IpAddress, guid}; + use crate::{Guid, guid}; + use crate::net::IpAddress; use device_path::DevicePathProtocol as DevicePathHeader; #[cfg(doc)] use device_path::DeviceType; From c5c0b63bc185f72c5c5f8e40522dbe05f5d0a570 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sun, 15 Jun 2025 20:29:48 +0200 Subject: [PATCH 2/6] uefi-raw: net: improve convenience and doc --- uefi-raw/src/net.rs | 371 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 329 insertions(+), 42 deletions(-) diff --git a/uefi-raw/src/net.rs b/uefi-raw/src/net.rs index 4b28fc410..ee7355d72 100644 --- a/uefi-raw/src/net.rs +++ b/uefi-raw/src/net.rs @@ -1,50 +1,108 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 //! UEFI network types. +//! +//! The main exports of this module are: +//! - [`MacAddress`] +//! - [`IpAddress`] +//! - [`Ipv4Address`] +//! - [`Ipv6Address`] +//! +//! ## Relation to Rust Standard Library Net Types +//! +//! Some of these types may overlap with those in the Rust standard library +//! ([`core::net`]). To ensure a streamlined API and maintain ABI compatibility, +//! this results in some necessary duplication. +//! +//! All types are tightly integrated with the corresponding [`core::net`] types +//! and other convenient conversions using [`From`] implementations: +//! - `[u8; 4]` -> [`Ipv4Address`], [`IpAddress`] +//! - `[u8; 16]` -> [`Ipv6Address`], [`IpAddress`] +//! - [`core::net::Ipv4Addr`] -> [`Ipv4Address`], [`IpAddress`] +//! - [`core::net::Ipv6Addr`] -> [`Ipv6Address`], [`IpAddress`] +//! - [`core::net::IpAddr`] -> [`IpAddress`] +//! - [`Ipv4Address`] -> [`core::net::Ipv4Addr`] +//! - [`Ipv6Address`] -> [`core::net::Ipv6Addr`] +//! +//! Further, these [`From`] implementations exist: +//! - `[u8; 6]` -> [`MacAddress`] +//! - `[u8; 32]` -> [`MacAddress`] -use core::fmt; use core::fmt::{Debug, Formatter}; +use core::net::{IpAddr as StdIpAddr, Ipv4Addr as StdIpv4Addr, Ipv6Addr as StdIpv6Addr}; +use core::{fmt, mem}; /// An IPv4 internet protocol address. +/// +/// See the [module documentation] to get an overview over the relation to the +/// types from [`core::net`]. +/// +/// [module documentation]: self #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)] #[repr(transparent)] pub struct Ipv4Address(pub [u8; 4]); -impl From for Ipv4Address { - fn from(ip: core::net::Ipv4Addr) -> Self { +impl Ipv4Address { + /// Returns the octets of the IP address. + #[must_use] + pub const fn octets(self) -> [u8; 4] { + self.0 + } +} + +impl From for Ipv4Address { + fn from(ip: StdIpv4Addr) -> Self { Self(ip.octets()) } } -impl From for core::net::Ipv4Addr { +impl From for StdIpv4Addr { fn from(ip: Ipv4Address) -> Self { Self::from(ip.0) } } /// An IPv6 internet protocol address. +/// +/// See the [module documentation] to get an overview over the relation to the +/// types from [`core::net`]. +/// +/// [module documentation]: self #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)] #[repr(transparent)] pub struct Ipv6Address(pub [u8; 16]); -impl From for Ipv6Address { - fn from(ip: core::net::Ipv6Addr) -> Self { +impl Ipv6Address { + /// Returns the octets of the IP address. + #[must_use] + pub const fn octets(self) -> [u8; 16] { + self.0 + } +} + +impl From for Ipv6Address { + fn from(ip: StdIpv6Addr) -> Self { Self(ip.octets()) } } -impl From for core::net::Ipv6Addr { +impl From for StdIpv6Addr { fn from(ip: Ipv6Address) -> Self { Self::from(ip.0) } } -/// An IPv4 or IPv6 internet protocol address. +/// EFI ABI-compatible union of 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. +/// +/// See the [module documentation] to get an overview over the relation to the +/// types from [`core::net`]. +/// +/// [module documentation]: self #[derive(Clone, Copy)] #[repr(C)] pub union IpAddress { @@ -57,57 +115,171 @@ pub union IpAddress { /// This member serves to align the whole type to 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 _align_helper: [u32; 4], + align_helper: [u32; 4], } impl IpAddress { + /// Construct a new zeroed address. + #[must_use] + pub const fn new_zeroed() -> Self { + // SAFETY: All bit patterns are valid. + unsafe { mem::zeroed() } + } + /// Construct a new IPv4 address. + /// + /// The type won't know that it is an IPv6 address and additional context + /// is needed. #[must_use] - pub const fn new_v4(ip_addr: [u8; 4]) -> Self { - Self { - v4: Ipv4Address(ip_addr), - } + pub const fn new_v4(octets: [u8; 4]) -> Self { + // Initialize all bytes to zero first. + let mut obj = Self::new_zeroed(); + obj.v4 = Ipv4Address(octets); + obj } /// 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(octets: [u8; 16]) -> Self { + // Initialize all bytes to zero first. + let mut obj = Self::new_zeroed(); + obj.v6 = Ipv6Address(octets); + obj + } + + /// Returns the octets of the union. Without additional context, it is not + /// clear whether the octets represent an IPv4 or IPv6 address. #[must_use] - pub const fn new_v6(ip_addr: [u8; 16]) -> Self { - Self { - v6: Ipv6Address(ip_addr), + pub const fn octets(&self) -> [u8; 16] { + unsafe { self.v6.octets() } + } + + /// Returns a raw pointer to the IP address. + #[must_use] + pub const fn as_ptr(&self) -> *const Self { + core::ptr::addr_of!(*self) + } + + /// Returns a raw mutable pointer to the IP address. + #[must_use] + pub const fn as_ptr_mut(&mut self) -> *mut Self { + core::ptr::addr_of_mut!(*self) + } + + /// Transforms this EFI type to the Rust standard library's type. + /// + /// # Arguments + /// - `is_ipv6`: Whether the internal data should be interpreted as IPv6 or + /// IPv4 address. + /// + /// # Panics + /// Panics if `is_ipv6` is false but there are additional bytes + /// indicating it's a IPv6 address. + #[must_use] + pub fn to_std_ip_addr(self, is_ipv6: bool) -> StdIpAddr { + if is_ipv6 { + StdIpAddr::V6(StdIpv6Addr::from(unsafe { self.v6.octets() })) + } else { + let has_extra_bytes = self.octets()[4..].iter().any(|&x| x != 0); + assert!(!has_extra_bytes); + StdIpAddr::V4(StdIpv4Addr::from(unsafe { self.v4.octets() })) } } + + /// Returns the underlying data as [`Ipv4Address`], if only the first four + /// octets are used. + /// + /// # Safety + /// This function is not unsafe memory-wise but callers need to ensure with + /// additional context that the IP is indeed an IPv4 address. + pub unsafe fn try_as_ipv4(&self) -> Result { + let extra = self.octets()[4..].iter().any(|&x| x != 0); + if !extra { + let octets: [u8; 4] = self.octets()[..4].try_into().unwrap(); + Ok(Ipv4Address(octets)) + } else { + Err(Ipv6Address(self.octets())) + } + } + + /// Returns the underlying data as [`Ipv6Address`]. + /// + /// # Safety + /// This function is not unsafe memory-wise but callers need to ensure with + /// additional context that the IP is indeed an IPv6 address. + #[must_use] + pub unsafe fn as_ipv6(&self) -> Ipv6Address { + Ipv6Address::from(unsafe { self.v6.octets() }) + } } 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() + f.debug_struct("IpAddress") + // SAFETY: All constructors ensure that all bytes are always + // initialized. + .field("v4", &unsafe { self.v4 }) + // SAFETY: All constructors ensure that all bytes are always + // initialized. + .field("v6", &unsafe { self.v6 }) + .finish() } } impl Default for IpAddress { fn default() -> Self { - Self { - _align_helper: [0u32; 4], - } + Self::new_zeroed() } } -impl From for IpAddress { - fn from(t: core::net::IpAddr) -> Self { +impl From for IpAddress { + fn from(t: StdIpAddr) -> Self { match t { - core::net::IpAddr::V4(ip) => Self { - v4: Ipv4Address::from(ip), - }, - core::net::IpAddr::V6(ip) => Self { - v6: Ipv6Address::from(ip), - }, + StdIpAddr::V4(ip) => Self::new_v4(ip.octets()), + StdIpAddr::V6(ip) => Self::new_v6(ip.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) + } +} + +impl From<[u8; 4]> for Ipv4Address { + fn from(octets: [u8; 4]) -> Self { + Self(octets) + } +} + +impl From<[u8; 16]> for Ipv6Address { + fn from(octets: [u8; 16]) -> Self { + Self(octets) + } +} + +impl From for IpAddress { + fn from(value: StdIpv4Addr) -> Self { + Self::new_v4(value.octets()) + } +} + +impl From for IpAddress { + fn from(value: StdIpv6Addr) -> Self { + Self::new_v6(value.octets()) + } +} + /// UEFI Media Access Control (MAC) address. /// /// UEFI supports multiple network protocols and hardware types, not just @@ -121,17 +293,36 @@ impl From for IpAddress { #[repr(transparent)] pub struct MacAddress(pub [u8; 32]); +impl MacAddress { + /// Returns the octets of the MAC address. + #[must_use] + 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_as_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()) + } + } +} + impl From<[u8; 6]> for MacAddress { fn from(octets: [u8; 6]) -> Self { let mut buffer = [0; 32]; - buffer.copy_from_slice(&octets); + buffer[..6].copy_from_slice(&octets); Self(buffer) } } -impl From for [u8; 6] { - fn from(MacAddress(o): MacAddress) -> Self { - [o[0], o[1], o[2], o[3], o[4], o[5]] +impl From<[u8; 32]> for MacAddress { + fn from(octets: [u8; 32]) -> Self { + Self(octets) } } @@ -144,33 +335,129 @@ mod tests { 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, ]; - /// Test round-trip conversion between `Ipv4Address` and `core::net::Ipv4Addr`. + /// Test round-trip conversion between `Ipv4Address` and `StdIpv4Addr`. #[test] fn test_ip_addr4_conversion() { let uefi_addr = Ipv4Address(TEST_IPV4); - let core_addr = core::net::Ipv4Addr::from(uefi_addr); + let core_addr = StdIpv4Addr::from(uefi_addr); assert_eq!(uefi_addr, Ipv4Address::from(core_addr)); } - /// Test round-trip conversion between `Ipv6Address` and `core::net::Ipv6Addr`. + /// Test round-trip conversion between [`Ipv6Address`] and [`StdIpv6Addr`]. #[test] fn test_ip_addr6_conversion() { let uefi_addr = Ipv6Address(TEST_IPV6); - let core_addr = core::net::Ipv6Addr::from(uefi_addr); + let core_addr = StdIpv6Addr::from(uefi_addr); assert_eq!(uefi_addr, Ipv6Address::from(core_addr)); } - /// Test conversion from `core::net::IpAddr` to `IpvAddress`. + /// Test conversion from [`StdIpAddr`] 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 core_addr = StdIpAddr::V4(StdIpv4Addr::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 core_addr = StdIpAddr::V6(StdIpv6Addr::from(TEST_IPV6)); let uefi_addr = IpAddress::from(core_addr); assert_eq!(unsafe { uefi_addr.v6.0 }, TEST_IPV6); } + + /// Tests the From-impls as described in the module description. + #[test] + fn test_module_description_from_impls() { + { + let octets = [0_u8, 1, 2, 3]; + assert_eq!(Ipv4Address::from(octets), Ipv4Address(octets)); + let uefi_addr = IpAddress::from(octets); + assert_eq!(&octets, &uefi_addr.octets()[0..4]); + } + { + 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, &uefi_addr.octets()); + } + { + let octets = [7, 5, 3, 1]; + let core_ipv4_addr = StdIpv4Addr::from(octets); + assert_eq!(Ipv4Address::from(core_ipv4_addr).octets(), octets); + assert_eq!(IpAddress::from(core_ipv4_addr).octets()[0..4], octets); + } + { + let octets = [7, 5, 3, 1, 6, 3, 8, 5, 2, 5, 2, 7, 3, 5, 2, 6]; + let core_ipv6_addr = StdIpv6Addr::from(octets); + assert_eq!(Ipv6Address::from(core_ipv6_addr).octets(), octets); + assert_eq!(IpAddress::from(core_ipv6_addr).octets(), octets); + } + { + let octets = [8, 8, 2, 6]; + let core_ip_addr = StdIpAddr::from(octets); + assert_eq!(IpAddress::from(core_ip_addr).octets()[0..4], octets); + } + { + let octets = [8, 8, 2, 6, 6, 7]; + let uefi_mac_addr = MacAddress::from(octets); + assert_eq!(uefi_mac_addr.octets()[0..6], octets); + } + { + 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); + } + } + + /// Tests that all bytes are initialized and that the Debug print doesn't + /// produce errors, when Miri executes this. + #[test] + fn test_ip_address_debug_memory_safe() { + let uefi_addr = IpAddress::new_v6(TEST_IPV6); + std::eprintln!("{uefi_addr:#?}"); + } + + /// 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) { + // 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) -> StdIpAddr { + let mut efi_ip_addr = IpAddress::new_zeroed(); + efi_retrieve_efi_ip_addr(&mut efi_ip_addr, is_ipv6); + efi_ip_addr.to_std_ip_addr(is_ipv6) + } + + let ipv4_addr = high_level_retrieve_ip(false); + let ipv4_addr: StdIpv4Addr = match ipv4_addr { + StdIpAddr::V4(ipv4_addr) => ipv4_addr, + StdIpAddr::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: StdIpv6Addr = match ipv6_addr { + StdIpAddr::V6(ipv6_addr) => ipv6_addr, + StdIpAddr::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 e03ebdabdd5ad76e67512506e4a382e2f99703ca Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sun, 15 Jun 2025 20:29:55 +0200 Subject: [PATCH 3/6] uefi-raw: update changelog --- uefi-raw/CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/uefi-raw/CHANGELOG.md b/uefi-raw/CHANGELOG.md index 000e7590c..e2a97a57a 100644 --- a/uefi-raw/CHANGELOG.md +++ b/uefi-raw/CHANGELOG.md @@ -5,7 +5,14 @@ - Added `PciRootBridgeIoProtocol`. - Added `ConfigKeywordHandlerProtocol`. - Added `HiiConfigAccessProtocol`. +- Added lots of convenient methods and `From` implementations to better + integrate the `uefi_raw::net::*` types with the types from `core::net::*`. + Further, more associated functions for an improved convenience have been + added to the types of that module. +## Changed +- Types `MacAddress`, `IpAddress`, `Ipv4Address`, and `Ipv6Address` were moved + from `uefi_raw::*` to `uefi_raw::net::*`. # uefi-raw - 0.11.0 (2025-05-04) From d23e9944c560b396abb727c236760ae31116e38d Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sun, 15 Jun 2025 20:55:51 +0200 Subject: [PATCH 4/6] uefi-raw: net: covert union to normal type The union makes some things hard to work with, and we can simply remove it. We get multiple advantages from that: - no more chance to have uninitialized bytes - We can `Debug`-print the type - Some implementations become simpler --- uefi-raw/src/net.rs | 114 +++++++++++++--------------------- uefi/src/proto/network/mod.rs | 4 +- 2 files changed, 46 insertions(+), 72 deletions(-) diff --git a/uefi-raw/src/net.rs b/uefi-raw/src/net.rs index ee7355d72..13fdb1b06 100644 --- a/uefi-raw/src/net.rs +++ b/uefi-raw/src/net.rs @@ -28,9 +28,7 @@ //! - `[u8; 6]` -> [`MacAddress`] //! - `[u8; 32]` -> [`MacAddress`] -use core::fmt::{Debug, Formatter}; use core::net::{IpAddr as StdIpAddr, Ipv4Addr as StdIpv4Addr, Ipv6Addr as StdIpv6Addr}; -use core::{fmt, mem}; /// An IPv4 internet protocol address. /// @@ -92,38 +90,27 @@ impl From for StdIpv6Addr { } } -/// EFI ABI-compatible union of 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. +/// EFI ABI-compatible union of an IPv4 or IPv6 internet protocol address, +/// corresponding to `EFI_IP_ADDRESS` type in the UEFI specification. /// /// See the [module documentation] to get an overview over the relation to the /// types from [`core::net`]. /// +/// # UEFI Information +/// Corresponds to the `EFI_IP_ADDRESS` type in the UEFI specification. Instead +/// of using an untagged C union, we use this more rusty type. ABI-wise it is +/// the same but less cumbersome to work with in Rust. +/// /// [module documentation]: self -#[derive(Clone, Copy)] -#[repr(C)] -pub union IpAddress { - /// An IPv4 internet protocol address. - pub v4: Ipv4Address, - - /// An IPv6 internet protocol address. - pub v6: Ipv6Address, - - /// This member serves to align the whole type to 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. - align_helper: [u32; 4], -} +#[derive(Debug, Clone, Copy)] +#[repr(C, align(4))] +pub struct IpAddress(pub [u8; 16]); impl IpAddress { /// Construct a new zeroed address. #[must_use] pub const fn new_zeroed() -> Self { - // SAFETY: All bit patterns are valid. - unsafe { mem::zeroed() } + Self([0; 16]) } /// Construct a new IPv4 address. @@ -132,10 +119,9 @@ impl IpAddress { /// is needed. #[must_use] pub const fn new_v4(octets: [u8; 4]) -> Self { - // Initialize all bytes to zero first. - let mut obj = Self::new_zeroed(); - obj.v4 = Ipv4Address(octets); - obj + Self([ + octets[0], octets[1], octets[2], octets[3], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]) } /// Construct a new IPv6 address. @@ -144,17 +130,14 @@ impl IpAddress { /// is needed. #[must_use] pub const fn new_v6(octets: [u8; 16]) -> Self { - // Initialize all bytes to zero first. - let mut obj = Self::new_zeroed(); - obj.v6 = Ipv6Address(octets); - obj + Self(octets) } /// Returns the octets of the union. Without additional context, it is not /// clear whether the octets represent an IPv4 or IPv6 address. #[must_use] pub const fn octets(&self) -> [u8; 16] { - unsafe { self.v6.octets() } + self.0 } /// Returns a raw pointer to the IP address. @@ -181,11 +164,12 @@ impl IpAddress { #[must_use] pub fn to_std_ip_addr(self, is_ipv6: bool) -> StdIpAddr { if is_ipv6 { - StdIpAddr::V6(StdIpv6Addr::from(unsafe { self.v6.octets() })) + StdIpAddr::V6(StdIpv6Addr::from(self.octets())) } else { let has_extra_bytes = self.octets()[4..].iter().any(|&x| x != 0); assert!(!has_extra_bytes); - StdIpAddr::V4(StdIpv4Addr::from(unsafe { self.v4.octets() })) + let octets: [u8; 4] = self.octets()[..4].try_into().unwrap(); + StdIpAddr::V4(StdIpv4Addr::from(octets)) } } @@ -212,20 +196,7 @@ impl IpAddress { /// additional context that the IP is indeed an IPv6 address. #[must_use] pub unsafe fn as_ipv6(&self) -> Ipv6Address { - Ipv6Address::from(unsafe { self.v6.octets() }) - } -} - -impl Debug for IpAddress { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_struct("IpAddress") - // SAFETY: All constructors ensure that all bytes are always - // initialized. - .field("v4", &unsafe { self.v4 }) - // SAFETY: All constructors ensure that all bytes are always - // initialized. - .field("v6", &unsafe { self.v6 }) - .finish() + Ipv6Address::from(self.octets()) } } @@ -335,6 +306,18 @@ mod tests { 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, ]; + /// Ensures ABI-compatibility, as described here: + /// + #[test] + fn test_abi() { + assert_eq!(size_of::(), 16); + assert_eq!(align_of::(), 4); + assert_eq!(size_of::(), 4); + assert_eq!(align_of::(), 1); + assert_eq!(size_of::(), 16); + assert_eq!(align_of::(), 1); + } + /// Test round-trip conversion between `Ipv4Address` and `StdIpv4Addr`. #[test] fn test_ip_addr4_conversion() { @@ -358,11 +341,14 @@ mod tests { fn test_ip_addr_conversion() { let core_addr = StdIpAddr::V4(StdIpv4Addr::from(TEST_IPV4)); let uefi_addr = IpAddress::from(core_addr); - assert_eq!(unsafe { uefi_addr.v4.0 }, TEST_IPV4); + assert_eq!( + unsafe { uefi_addr.try_as_ipv4().unwrap() }.octets(), + TEST_IPV4 + ); let core_addr = StdIpAddr::V6(StdIpv6Addr::from(TEST_IPV6)); let uefi_addr = IpAddress::from(core_addr); - assert_eq!(unsafe { uefi_addr.v6.0 }, TEST_IPV6); + assert_eq!(unsafe { uefi_addr.as_ipv6() }.octets(), TEST_IPV6); } /// Tests the From-impls as described in the module description. @@ -412,30 +398,18 @@ mod tests { } } - /// Tests that all bytes are initialized and that the Debug print doesn't - /// produce errors, when Miri executes this. - #[test] - fn test_ip_address_debug_memory_safe() { - let uefi_addr = IpAddress::new_v6(TEST_IPV6); - std::eprintln!("{uefi_addr:#?}"); - } - /// 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) { - // 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; - } + addr.0[0] = 42; + addr.0[1] = 42; + addr.0[2] = 42; + addr.0[3] = 42; + if is_ipv6 { - unsafe { - addr.v6.0[14] = 42; - addr.v6.0[15] = 42; - } + addr.0[14] = 42; + addr.0[15] = 42; } } diff --git a/uefi/src/proto/network/mod.rs b/uefi/src/proto/network/mod.rs index aab57114d..7bf0ecc2e 100644 --- a/uefi/src/proto/network/mod.rs +++ b/uefi/src/proto/network/mod.rs @@ -44,9 +44,9 @@ impl IpAddress { #[must_use] const unsafe fn from_raw(ip_addr: uefi_raw::net::IpAddress, is_ipv6: bool) -> Self { if is_ipv6 { - Self::new_v6(unsafe { ip_addr.v6.0 }) + Self::new_v6(ip_addr.0) } else { - Self::new_v4(unsafe { ip_addr.v4.0 }) + Self::new_v4([ip_addr.0[0], ip_addr.0[1], ip_addr.0[2], ip_addr.0[3]]) } } From 8e4c873e570a278863718d3ec51bffc277661e6e Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sun, 15 Jun 2025 21:06:57 +0200 Subject: [PATCH 5/6] xtask: improved error output for "wrong" repr This helps to better understand why something fails. --- xtask/src/check_raw.rs | 45 +++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/xtask/src/check_raw.rs b/xtask/src/check_raw.rs index 0930e639a..1162c1e6f 100644 --- a/xtask/src/check_raw.rs +++ b/xtask/src/check_raw.rs @@ -45,7 +45,7 @@ enum ErrorKind { ForbiddenAbi, ForbiddenAttr, ForbiddenItemKind(ItemKind), - ForbiddenRepr, + ForbiddenRepr(Vec), ForbiddenType, MalformedAttrs, MissingPub, @@ -57,25 +57,26 @@ enum ErrorKind { impl Display for ErrorKind { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!( - f, - "{}", - match self { - Self::ForbiddenAbi => "forbidden ABI", - Self::ForbiddenAttr => "forbidden attribute", - Self::ForbiddenItemKind(ItemKind::Enum) => - "forbidden use of enum; use the `newtype_enum!` macro instead", - Self::ForbiddenItemKind(_) => "forbidden type of item", - Self::ForbiddenRepr => "forbidden repr", - Self::ForbiddenType => "forbidden type", - Self::MalformedAttrs => "malformed attribute contents", - Self::MissingPub => "missing pub", - Self::MissingRepr => "missing repr", - Self::MissingUnsafe => "missing unsafe", - Self::UnderscoreField => "field name starts with `_`", - Self::UnknownRepr => "unknown repr", - } - ) + match self { + Self::ForbiddenAbi => write!(f, "forbidden ABI"), + Self::ForbiddenAttr => write!(f, "forbidden attribute"), + Self::ForbiddenItemKind(ItemKind::Enum) => write!( + f, + "forbidden use of enum; use the `newtype_enum!` macro instead" + ), + Self::ForbiddenItemKind(_) => write!(f, "forbidden type of item"), + Self::ForbiddenRepr(reprs) => write!( + f, + "the following combination of repr attributes is forbidden: {reprs:?}" + ), + Self::ForbiddenType => write!(f, "forbidden type"), + Self::MalformedAttrs => write!(f, "malformed attribute contents"), + Self::MissingPub => write!(f, "missing pub"), + Self::MissingRepr => write!(f, "missing repr"), + Self::MissingUnsafe => write!(f, "missing unsafe"), + Self::UnderscoreField => write!(f, "field name starts with `_`"), + Self::UnknownRepr => write!(f, "unknown repr"), + } } } @@ -290,7 +291,7 @@ fn check_type_attrs(attrs: &[Attribute], spanned: &dyn Spanned, src: &Path) -> R } else if ALLOWED_REPRS.contains(&reprs.as_slice()) { Ok(()) } else { - Err(Error::new(ErrorKind::ForbiddenRepr, src, spanned)) + Err(Error::new(ErrorKind::ForbiddenRepr(reprs), src, spanned)) } } @@ -347,7 +348,7 @@ fn check_macro(item: &ItemMacro, src: &Path) -> Result<(), Error> { let reprs = get_reprs(&attrs); let allowed_reprs: &[&[Repr]] = &[&[Repr::Transparent]]; if !allowed_reprs.contains(&reprs.as_slice()) { - return Err(Error::new(ErrorKind::ForbiddenRepr, src, mac)); + return Err(Error::new(ErrorKind::ForbiddenRepr(reprs), src, mac)); } } From 85838cec5a894e0edd195095c7b0066a691e3765 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sun, 15 Jun 2025 21:17:46 +0200 Subject: [PATCH 6/6] xtask: fix check-raw check-raw is very strict. Relaxing it gives uefi-raw more freedom, which is especially needed for the more high-level IpAddress type changes. --- xtask/src/check_raw.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/xtask/src/check_raw.rs b/xtask/src/check_raw.rs index 1162c1e6f..5a415d7f6 100644 --- a/xtask/src/check_raw.rs +++ b/xtask/src/check_raw.rs @@ -280,7 +280,12 @@ fn check_fields(fields: &Punctuated, src: &Path) -> Result<(), Err } /// List with allowed combinations of representations (see [`Repr`]). -const ALLOWED_REPRS: &[&[Repr]] = &[&[Repr::C], &[Repr::C, Repr::Packed], &[Repr::Transparent]]; +const ALLOWED_REPRS: &[&[Repr]] = &[ + &[Repr::C], + &[Repr::C, Repr::Packed], + &[Repr::Transparent], + &[Repr::Align(4), Repr::C], +]; fn check_type_attrs(attrs: &[Attribute], spanned: &dyn Spanned, src: &Path) -> Result<(), Error> { let attrs = parse_attrs(attrs, src)?; @@ -482,7 +487,7 @@ mod tests { } } }, - ErrorKind::ForbiddenRepr, + ErrorKind::ForbiddenRepr(vec![Repr::C]), ); } @@ -614,7 +619,7 @@ mod tests { pub f: u32, } }, - ErrorKind::ForbiddenRepr, + ErrorKind::ForbiddenRepr(vec![Repr::Rust]), ); // Forbidden attr.