2828//! - `[u8; 6]` -> [`MacAddress`]
2929//! - `[u8; 32]` -> [`MacAddress`]
3030
31- use core:: fmt:: { Debug , Formatter } ;
3231use core:: net:: { IpAddr as StdIpAddr , Ipv4Addr as StdIpv4Addr , Ipv6Addr as StdIpv6Addr } ;
33- use core:: { fmt, mem} ;
3432
3533/// An IPv4 internet protocol address.
3634///
@@ -92,38 +90,27 @@ impl From<Ipv6Address> for StdIpv6Addr {
9290 }
9391}
9492
95- /// EFI ABI-compatible union of an IPv4 or IPv6 internet protocol address.
96- ///
97- /// Corresponds to the `EFI_IP_ADDRESS` type in the UEFI specification. This
98- /// type is defined in the same way as edk2 for compatibility with C code. Note
99- /// that this is an **untagged union**, so there's no way to tell which type of
100- /// address an `IpAddress` value contains without additional context.
93+ /// EFI ABI-compatible union of an IPv4 or IPv6 internet protocol address,
94+ /// corresponding to `EFI_IP_ADDRESS` type in the UEFI specification.
10195///
10296/// See the [module documentation] to get an overview over the relation to the
10397/// types from [`core::net`].
10498///
99+ /// # UEFI Information
100+ /// Corresponds to the `EFI_IP_ADDRESS` type in the UEFI specification. Instead
101+ /// of using an untagged C union, we use this more rusty type. ABI-wise it is
102+ /// the same but less cumbersome to work with in Rust.
103+ ///
105104/// [module documentation]: self
106- #[ derive( Clone , Copy ) ]
107- #[ repr( C ) ]
108- pub union IpAddress {
109- /// An IPv4 internet protocol address.
110- pub v4 : Ipv4Address ,
111-
112- /// An IPv6 internet protocol address.
113- pub v6 : Ipv6Address ,
114-
115- /// This member serves to align the whole type to 4 bytes as required by
116- /// the spec. Note that this is slightly different from `repr(align(4))`,
117- /// which would prevent placing this type in a packed structure.
118- align_helper : [ u32 ; 4 ] ,
119- }
105+ #[ derive( Debug , Clone , Copy ) ]
106+ #[ repr( C , align( 4 ) ) ]
107+ pub struct IpAddress ( pub [ u8 ; 16 ] ) ;
120108
121109impl IpAddress {
122110 /// Construct a new zeroed address.
123111 #[ must_use]
124112 pub const fn new_zeroed ( ) -> Self {
125- // SAFETY: All bit patterns are valid.
126- unsafe { mem:: zeroed ( ) }
113+ Self ( [ 0 ; 16 ] )
127114 }
128115
129116 /// Construct a new IPv4 address.
@@ -132,10 +119,9 @@ impl IpAddress {
132119 /// is needed.
133120 #[ must_use]
134121 pub const fn new_v4 ( octets : [ u8 ; 4 ] ) -> Self {
135- // Initialize all bytes to zero first.
136- let mut obj = Self :: new_zeroed ( ) ;
137- obj. v4 = Ipv4Address ( octets) ;
138- obj
122+ Self ( [
123+ octets[ 0 ] , octets[ 1 ] , octets[ 2 ] , octets[ 3 ] , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
124+ ] )
139125 }
140126
141127 /// Construct a new IPv6 address.
@@ -144,17 +130,14 @@ impl IpAddress {
144130 /// is needed.
145131 #[ must_use]
146132 pub const fn new_v6 ( octets : [ u8 ; 16 ] ) -> Self {
147- // Initialize all bytes to zero first.
148- let mut obj = Self :: new_zeroed ( ) ;
149- obj. v6 = Ipv6Address ( octets) ;
150- obj
133+ Self ( octets)
151134 }
152135
153136 /// Returns the octets of the union. Without additional context, it is not
154137 /// clear whether the octets represent an IPv4 or IPv6 address.
155138 #[ must_use]
156139 pub const fn octets ( & self ) -> [ u8 ; 16 ] {
157- unsafe { self . v6 . octets ( ) }
140+ self . 0
158141 }
159142
160143 /// Returns a raw pointer to the IP address.
@@ -181,11 +164,12 @@ impl IpAddress {
181164 #[ must_use]
182165 pub fn to_std_ip_addr ( self , is_ipv6 : bool ) -> StdIpAddr {
183166 if is_ipv6 {
184- StdIpAddr :: V6 ( StdIpv6Addr :: from ( unsafe { self . v6 . octets ( ) } ) )
167+ StdIpAddr :: V6 ( StdIpv6Addr :: from ( self . octets ( ) ) )
185168 } else {
186169 let has_extra_bytes = self . octets ( ) [ 4 ..] . iter ( ) . any ( |& x| x != 0 ) ;
187170 assert ! ( !has_extra_bytes) ;
188- StdIpAddr :: V4 ( StdIpv4Addr :: from ( unsafe { self . v4 . octets ( ) } ) )
171+ let octets: [ u8 ; 4 ] = self . octets ( ) [ ..4 ] . try_into ( ) . unwrap ( ) ;
172+ StdIpAddr :: V4 ( StdIpv4Addr :: from ( octets) )
189173 }
190174 }
191175
@@ -212,20 +196,7 @@ impl IpAddress {
212196 /// additional context that the IP is indeed an IPv6 address.
213197 #[ must_use]
214198 pub unsafe fn as_ipv6 ( & self ) -> Ipv6Address {
215- Ipv6Address :: from ( unsafe { self . v6 . octets ( ) } )
216- }
217- }
218-
219- impl Debug for IpAddress {
220- fn fmt ( & self , f : & mut Formatter < ' _ > ) -> fmt:: Result {
221- f. debug_struct ( "IpAddress" )
222- // SAFETY: All constructors ensure that all bytes are always
223- // initialized.
224- . field ( "v4" , & unsafe { self . v4 } )
225- // SAFETY: All constructors ensure that all bytes are always
226- // initialized.
227- . field ( "v6" , & unsafe { self . v6 } )
228- . finish ( )
199+ Ipv6Address :: from ( self . octets ( ) )
229200 }
230201}
231202
@@ -337,6 +308,18 @@ mod tests {
337308 101 , 102 , 103 , 104 , 105 , 106 , 107 , 108 , 109 , 110 , 111 , 112 , 113 , 114 , 115 , 116 ,
338309 ] ;
339310
311+ /// Ensures ABI-compatibility, as described here:
312+ /// <https://github.com/tianocore/edk2/blob/b1887152024c7eb0cc7de735b0b57febd6099bf9/MdePkg/Include/Uefi/UefiBaseType.h#L100>
313+ #[ test]
314+ fn test_abi ( ) {
315+ assert_eq ! ( size_of:: <IpAddress >( ) , 16 ) ;
316+ assert_eq ! ( align_of:: <IpAddress >( ) , 4 ) ;
317+ assert_eq ! ( size_of:: <Ipv4Address >( ) , 4 ) ;
318+ assert_eq ! ( align_of:: <Ipv6Address >( ) , 1 ) ;
319+ assert_eq ! ( size_of:: <Ipv6Address >( ) , 16 ) ;
320+ assert_eq ! ( align_of:: <Ipv6Address >( ) , 1 ) ;
321+ }
322+
340323 /// Test round-trip conversion between `Ipv4Address` and `StdIpv4Addr`.
341324 #[ test]
342325 fn test_ip_addr4_conversion ( ) {
@@ -360,11 +343,14 @@ mod tests {
360343 fn test_ip_addr_conversion ( ) {
361344 let core_addr = StdIpAddr :: V4 ( StdIpv4Addr :: from ( TEST_IPV4 ) ) ;
362345 let uefi_addr = IpAddress :: from ( core_addr) ;
363- assert_eq ! ( unsafe { uefi_addr. v4. 0 } , TEST_IPV4 ) ;
346+ assert_eq ! (
347+ unsafe { uefi_addr. try_as_ipv4( ) . unwrap( ) } . octets( ) ,
348+ TEST_IPV4
349+ ) ;
364350
365351 let core_addr = StdIpAddr :: V6 ( StdIpv6Addr :: from ( TEST_IPV6 ) ) ;
366352 let uefi_addr = IpAddress :: from ( core_addr) ;
367- assert_eq ! ( unsafe { uefi_addr. v6 . 0 } , TEST_IPV6 ) ;
353+ assert_eq ! ( unsafe { uefi_addr. as_ipv6 ( ) } . octets ( ) , TEST_IPV6 ) ;
368354 }
369355
370356 /// Tests the From-impls as described in the module description.
@@ -414,30 +400,18 @@ mod tests {
414400 }
415401 }
416402
417- /// Tests that all bytes are initialized and that the Debug print doesn't
418- /// produce errors, when Miri executes this.
419- #[ test]
420- fn test_ip_address_debug_memory_safe ( ) {
421- let uefi_addr = IpAddress :: new_v6 ( TEST_IPV6 ) ;
422- std:: eprintln!( "{uefi_addr:#?}" ) ;
423- }
424-
425403 /// Tests the expected flow of types in a higher-level UEFI API.
426404 #[ test]
427405 fn test_uefi_flow ( ) {
428406 fn efi_retrieve_efi_ip_addr ( addr : & mut IpAddress , is_ipv6 : bool ) {
429- // SAFETY: Alignment is guaranteed and memory is initialized.
430- unsafe {
431- addr. v4 . 0 [ 0 ] = 42 ;
432- addr. v4 . 0 [ 1 ] = 42 ;
433- addr. v4 . 0 [ 2 ] = 42 ;
434- addr. v4 . 0 [ 3 ] = 42 ;
435- }
407+ addr. 0 [ 0 ] = 42 ;
408+ addr. 0 [ 1 ] = 42 ;
409+ addr. 0 [ 2 ] = 42 ;
410+ addr. 0 [ 3 ] = 42 ;
411+
436412 if is_ipv6 {
437- unsafe {
438- addr. v6 . 0 [ 14 ] = 42 ;
439- addr. v6 . 0 [ 15 ] = 42 ;
440- }
413+ addr. 0 [ 14 ] = 42 ;
414+ addr. 0 [ 15 ] = 42 ;
441415 }
442416 }
443417
0 commit comments