Skip to content

Commit 5ff3165

Browse files
committed
uefi-raw: add convenience for type IpAddress
This tightly integrates the type with the Rust standard IpAddr type. It allows the convenient usage in `uefi` in a later commit.
1 parent d960acd commit 5ff3165

File tree

3 files changed

+161
-23
lines changed

3 files changed

+161
-23
lines changed

uefi-raw/CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
- Added `UsbIoProtocol`.
1616
- Added `Usb2HostControllerProtocol`.
1717
- Added `DevicePathProtocol::length()` properly constructing the `u16` value
18+
- Type `IpAddress` is now tightly integrated with `core::net::IpAddr`, e.g.,
19+
various `From` implementations are available.
1820

1921
## Changed
2022
- **Breaking:** Types `Ipv4Address` and `Ipv6Address` have been removed. They

uefi-raw/src/lib.rs

+158-22
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,12 @@ impl From<Boolean> for bool {
113113
/// type is defined in the same way as edk2 for compatibility with C code. Note
114114
/// that this is an **untagged union**, so there's no way to tell which type of
115115
/// address an `IpAddress` value contains without additional context.
116+
///
117+
/// For convenience, this type is tightly integrated with the Rust standard
118+
/// library types [`IpAddr`], [`Ipv4Addr`], and [`Ipv6Addr`].
119+
///
120+
/// The constructors ensure that all unused bytes of these type are always
121+
/// initialized to zero.
116122
#[derive(Clone, Copy)]
117123
#[repr(C)]
118124
pub union IpAddress {
@@ -132,9 +138,10 @@ impl IpAddress {
132138
/// Construct a new IPv4 address.
133139
#[must_use]
134140
pub fn new_v4(ip_addr: [u8; 4]) -> Self {
135-
Self {
136-
v4: Ipv4Addr::from(ip_addr),
137-
}
141+
// Initialize all bytes to zero first.
142+
let mut obj = Self::default();
143+
obj.v4 = Ipv4Addr::from(ip_addr);
144+
obj
138145
}
139146

140147
/// Construct a new IPv6 address.
@@ -144,20 +151,76 @@ impl IpAddress {
144151
v6: Ipv6Addr::from(ip_addr),
145152
}
146153
}
154+
155+
/// Returns the octets of the union. Without additional context, it is not
156+
/// clear whether the octets represent an IPv4 or IPv6 address.
157+
#[must_use]
158+
pub const fn octets(&self) -> [u8; 16] {
159+
unsafe { self.v6.octets() }
160+
}
161+
162+
/// Returns a raw pointer to the IP address.
163+
#[must_use]
164+
pub const fn as_ptr(&self) -> *const Self {
165+
core::ptr::addr_of!(*self)
166+
}
167+
168+
/// Returns a raw mutable pointer to the IP address.
169+
#[must_use]
170+
pub fn as_ptr_mut(&mut self) -> *mut Self {
171+
core::ptr::addr_of_mut!(*self)
172+
}
173+
174+
/// Transforms this EFI type to the Rust standard libraries type.
175+
///
176+
/// # Arguments
177+
/// - `is_ipv6`: Whether the internal data should be interpreted as IPv6 or
178+
/// IPv4 address.
179+
#[must_use]
180+
pub fn to_ip_addr(self, is_ipv6: bool) -> IpAddr {
181+
if is_ipv6 {
182+
IpAddr::V6(Ipv6Addr::from(unsafe { self.v6.octets() }))
183+
} else {
184+
IpAddr::V4(Ipv4Addr::from(unsafe { self.v4.octets() }))
185+
}
186+
}
187+
188+
/// Returns the underlying data as [`Ipv4Addr`], if only the first four
189+
/// octets are used.
190+
///
191+
/// # Safety
192+
/// This function is not unsafe memory-wise but callers need to ensure with
193+
/// additional context that the IP is indeed an IPv4 address.
194+
pub unsafe fn as_ipv4(&self) -> Result<Ipv4Addr, Ipv6Addr> {
195+
let extra = self.octets()[4..].iter().any(|&x| x != 0);
196+
if !extra {
197+
Ok(Ipv4Addr::from(unsafe { self.v4.octets() }))
198+
} else {
199+
Err(Ipv6Addr::from(unsafe { self.v6.octets() }))
200+
}
201+
}
202+
203+
/// Returns the underlying data as [`Ipv6Addr`].
204+
///
205+
/// # Safety
206+
/// This function is not unsafe memory-wise but callers need to ensure with
207+
/// additional context that the IP is indeed an IPv6 address.
208+
#[must_use]
209+
pub unsafe fn as_ipv6(&self) -> Ipv6Addr {
210+
Ipv6Addr::from(unsafe { self.v6.octets() })
211+
}
147212
}
148213

149214
impl Debug for IpAddress {
150215
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
151-
// The type is an untagged union, so we don't know whether it contains
152-
// an IPv4 or IPv6 address. It's also not safe to just print the whole
153-
// 16 bytes, since they might not all be initialized.
154-
f.debug_struct("IpAddress").finish()
216+
f.debug_tuple("IpAddress").field(&self.octets()).finish()
155217
}
156218
}
157219

158220
impl Default for IpAddress {
159221
fn default() -> Self {
160222
Self {
223+
// Initialize all fields to zero
161224
_align_helper: [0u32; 4],
162225
}
163226
}
@@ -166,16 +229,51 @@ impl Default for IpAddress {
166229
impl From<IpAddr> for IpAddress {
167230
fn from(t: IpAddr) -> Self {
168231
match t {
169-
IpAddr::V4(ip) => Self {
170-
v4: Ipv4Addr::from(ip),
171-
},
172-
IpAddr::V6(ip) => Self {
173-
v6: Ipv6Addr::from(ip),
174-
},
232+
IpAddr::V4(ip) => Self::new_v4(ip.octets()),
233+
IpAddr::V6(ip) => Self::new_v6(ip.octets()),
175234
}
176235
}
177236
}
178237

238+
impl From<&IpAddr> for IpAddress {
239+
fn from(t: &IpAddr) -> Self {
240+
match t {
241+
IpAddr::V4(ip) => Self::new_v4(ip.octets()),
242+
IpAddr::V6(ip) => Self::new_v6(ip.octets()),
243+
}
244+
}
245+
}
246+
247+
impl From<[u8; 4]> for IpAddress {
248+
fn from(octets: [u8; 4]) -> Self {
249+
Self::new_v4(octets)
250+
}
251+
}
252+
253+
impl From<[u8; 16]> for IpAddress {
254+
fn from(octets: [u8; 16]) -> Self {
255+
Self::new_v6(octets)
256+
}
257+
}
258+
259+
impl From<IpAddress> for [u8; 16] {
260+
fn from(value: IpAddress) -> Self {
261+
value.octets()
262+
}
263+
}
264+
265+
impl From<Ipv4Addr> for IpAddress {
266+
fn from(value: Ipv4Addr) -> Self {
267+
Self::new_v4(value.octets())
268+
}
269+
}
270+
271+
impl From<Ipv6Addr> for IpAddress {
272+
fn from(value: Ipv6Addr) -> Self {
273+
Self::new_v6(value.octets())
274+
}
275+
}
276+
179277
/// UEFI Media Access Control (MAC) address.
180278
///
181279
/// UEFI supports multiple network protocols and hardware types, not just
@@ -240,17 +338,55 @@ mod tests {
240338
assert_eq!(size_of::<Ipv6Addr>(), 16);
241339
assert_eq!(align_of::<Ipv6Addr>(), 1);
242340
}
243-
/// Test conversion from `core::net::IpAddr` to `IpvAddress`.
244-
///
245-
/// Note that conversion in the other direction is not possible.
341+
342+
#[test]
343+
fn ip_ptr() {
344+
let mut ip = IpAddress::new_v4([0; 4]);
345+
let ptr = ip.as_ptr_mut().cast::<u8>();
346+
unsafe {
347+
core::ptr::write(ptr, 192);
348+
core::ptr::write(ptr.add(1), 168);
349+
core::ptr::write(ptr.add(2), 42);
350+
core::ptr::write(ptr.add(3), 73);
351+
}
352+
unsafe { assert_eq!(ip.v4.octets(), [192, 168, 42, 73]) }
353+
}
354+
355+
/// Test conversion from [`IpAddr`] to [`IpAddress`].
246356
#[test]
247357
fn test_ip_addr_conversion() {
248-
let core_addr = IpAddr::V4(core::net::Ipv4Addr::from(TEST_IPV4));
249-
let uefi_addr = IpAddress::from(core_addr);
250-
assert_eq!(unsafe { uefi_addr.v4.octets() }, TEST_IPV4);
358+
// Reference: std types
359+
let core_ipv4_v4 = Ipv4Addr::from(TEST_IPV4);
360+
let core_ipv4 = IpAddr::from(core_ipv4_v4);
361+
let core_ipv6_v6 = Ipv6Addr::from(TEST_IPV6);
362+
let core_ipv6 = IpAddr::from(core_ipv6_v6);
363+
364+
// Test From [u8; N] constructors
365+
assert_eq!(IpAddress::from(TEST_IPV4).octets()[0..4], TEST_IPV4);
366+
assert_eq!(IpAddress::from(TEST_IPV6).octets(), TEST_IPV6);
367+
{
368+
let bytes: [u8; 16] = IpAddress::from(TEST_IPV6).into();
369+
assert_eq!(bytes, TEST_IPV6);
370+
}
251371

252-
let core_addr = IpAddr::V6(core::net::Ipv6Addr::from(TEST_IPV6));
253-
let uefi_addr = IpAddress::from(core_addr);
254-
assert_eq!(unsafe { uefi_addr.v6.octets() }, TEST_IPV6);
372+
// Test From::from std type constructors
373+
let efi_ipv4 = IpAddress::from(core_ipv4);
374+
assert_eq!(efi_ipv4.octets()[0..4], TEST_IPV4);
375+
assert_eq!(unsafe { efi_ipv4.as_ipv4().unwrap() }, core_ipv4);
376+
377+
let efi_ipv6 = IpAddress::from(core_ipv6);
378+
assert_eq!(efi_ipv6.octets(), TEST_IPV6);
379+
assert_eq!(unsafe { efi_ipv6.as_ipv4().unwrap_err() }, core_ipv6);
380+
assert_eq!(unsafe { efi_ipv6.as_ipv6() }, core_ipv6);
381+
382+
// Test From::from std type constructors
383+
let efi_ipv4 = IpAddress::from(core_ipv4_v4);
384+
assert_eq!(efi_ipv4.octets()[0..4], TEST_IPV4);
385+
assert_eq!(unsafe { efi_ipv4.as_ipv4().unwrap() }, core_ipv4);
386+
387+
let efi_ipv6 = IpAddress::from(core_ipv6_v6);
388+
assert_eq!(efi_ipv6.octets(), TEST_IPV6);
389+
assert_eq!(unsafe { efi_ipv6.as_ipv4().unwrap_err() }, core_ipv6);
390+
assert_eq!(unsafe { efi_ipv6.as_ipv6() }, core_ipv6);
255391
}
256392
}

uefi/CHANGELOG.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
- The `Display` impl for `CStr8` now excludes the trailing null character.
4242
- `VariableKeys` initializes with a larger name buffer to work around firmware
4343
bugs on some devices.
44-
- The UEFI `allocator::Allocator` has been optimized for page-aligned
44+
- The UEFI `allocator::Allocator` has been optimized for page-aligned
4545
allocations.
4646

4747

0 commit comments

Comments
 (0)