Skip to content

Commit 87b00fd

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 87b00fd

File tree

3 files changed

+158
-23
lines changed

3 files changed

+158
-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

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

149211
impl Debug for IpAddress {
150212
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()
213+
f.debug_tuple("IpAddress").field(&self.octets()).finish()
155214
}
156215
}
157216

158217
impl Default for IpAddress {
159218
fn default() -> Self {
160219
Self {
220+
// Initialize all fields to zero
161221
_align_helper: [0u32; 4],
162222
}
163223
}
@@ -166,16 +226,51 @@ impl Default for IpAddress {
166226
impl From<IpAddr> for IpAddress {
167227
fn from(t: IpAddr) -> Self {
168228
match t {
169-
IpAddr::V4(ip) => Self {
170-
v4: Ipv4Addr::from(ip),
171-
},
172-
IpAddr::V6(ip) => Self {
173-
v6: Ipv6Addr::from(ip),
174-
},
229+
IpAddr::V4(ip) => Self::new_v4(ip.octets()),
230+
IpAddr::V6(ip) => Self::new_v6(ip.octets()),
231+
}
232+
}
233+
}
234+
235+
impl From<&IpAddr> for IpAddress {
236+
fn from(t: &IpAddr) -> Self {
237+
match t {
238+
IpAddr::V4(ip) => Self::new_v4(ip.octets()),
239+
IpAddr::V6(ip) => Self::new_v6(ip.octets()),
175240
}
176241
}
177242
}
178243

244+
impl From<[u8; 4]> for IpAddress {
245+
fn from(octets: [u8; 4]) -> Self {
246+
Self::new_v4(octets)
247+
}
248+
}
249+
250+
impl From<[u8; 16]> for IpAddress {
251+
fn from(octets: [u8; 16]) -> Self {
252+
Self::new_v6(octets)
253+
}
254+
}
255+
256+
impl From<IpAddress> for [u8; 16] {
257+
fn from(value: IpAddress) -> Self {
258+
value.octets()
259+
}
260+
}
261+
262+
impl From<Ipv4Addr> for IpAddress {
263+
fn from(value: Ipv4Addr) -> Self {
264+
Self::new_v4(value.octets())
265+
}
266+
}
267+
268+
impl From<Ipv6Addr> for IpAddress {
269+
fn from(value: Ipv6Addr) -> Self {
270+
Self::new_v6(value.octets())
271+
}
272+
}
273+
179274
/// UEFI Media Access Control (MAC) address.
180275
///
181276
/// UEFI supports multiple network protocols and hardware types, not just
@@ -240,17 +335,55 @@ mod tests {
240335
assert_eq!(size_of::<Ipv6Addr>(), 16);
241336
assert_eq!(align_of::<Ipv6Addr>(), 1);
242337
}
243-
/// Test conversion from `core::net::IpAddr` to `IpvAddress`.
244-
///
245-
/// Note that conversion in the other direction is not possible.
338+
339+
#[test]
340+
fn ip_ptr() {
341+
let mut ip = IpAddress::new_v4([0; 4]);
342+
let ptr = ip.as_ptr_mut().cast::<u8>();
343+
unsafe {
344+
core::ptr::write(ptr, 192);
345+
core::ptr::write(ptr.add(1), 168);
346+
core::ptr::write(ptr.add(2), 42);
347+
core::ptr::write(ptr.add(3), 73);
348+
}
349+
unsafe { assert_eq!(ip.v4.octets(), [192, 168, 42, 73]) }
350+
}
351+
352+
/// Test conversion from [`IpAddr`] to [`IpAddress`].
246353
#[test]
247354
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);
355+
// Reference: std types
356+
let core_ipv4_v4 = Ipv4Addr::from(TEST_IPV4);
357+
let core_ipv4 = IpAddr::from(core_ipv4_v4);
358+
let core_ipv6_v6 = Ipv6Addr::from(TEST_IPV6);
359+
let core_ipv6 = IpAddr::from(core_ipv6_v6);
360+
361+
// Test From [u8; N] constructors
362+
assert_eq!(IpAddress::from(TEST_IPV4).octets()[0..4], TEST_IPV4);
363+
assert_eq!(IpAddress::from(TEST_IPV6).octets(), TEST_IPV6);
364+
{
365+
let bytes: [u8; 16] = IpAddress::from(TEST_IPV6).into();
366+
assert_eq!(bytes, TEST_IPV6);
367+
}
251368

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);
369+
// Test From::from std type constructors
370+
let efi_ipv4 = IpAddress::from(core_ipv4);
371+
assert_eq!(efi_ipv4.octets()[0..4], TEST_IPV4);
372+
assert_eq!(unsafe { efi_ipv4.as_ipv4().unwrap() }, core_ipv4);
373+
374+
let efi_ipv6 = IpAddress::from(core_ipv6);
375+
assert_eq!(efi_ipv6.octets(), TEST_IPV6);
376+
assert_eq!(unsafe { efi_ipv6.as_ipv4().unwrap_err() }, core_ipv6);
377+
assert_eq!(unsafe { efi_ipv6.as_ipv6() }, core_ipv6);
378+
379+
// Test From::from std type constructors
380+
let efi_ipv4 = IpAddress::from(core_ipv4_v4);
381+
assert_eq!(efi_ipv4.octets()[0..4], TEST_IPV4);
382+
assert_eq!(unsafe { efi_ipv4.as_ipv4().unwrap() }, core_ipv4);
383+
384+
let efi_ipv6 = IpAddress::from(core_ipv6_v6);
385+
assert_eq!(efi_ipv6.octets(), TEST_IPV6);
386+
assert_eq!(unsafe { efi_ipv6.as_ipv4().unwrap_err() }, core_ipv6);
387+
assert_eq!(unsafe { efi_ipv6.as_ipv6() }, core_ipv6);
255388
}
256389
}

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)