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

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: 155 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,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

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 によって変換されたページ (->オリジナル) /