Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

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

Lines changed: 2 additions & 0 deletions
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

Lines changed: 158 additions & 22 deletions
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

Lines changed: 1 addition & 1 deletion
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)

AltStyle によって変換されたページ (->オリジナル) /