11// SPDX-License-Identifier: MIT OR Apache-2.0
22
3+ use core:: ops:: DerefMut ;
34use core:: time:: Duration ;
4-
55use uefi:: proto:: network:: MacAddress ;
6- use uefi:: proto:: network:: snp:: { InterruptStatus , ReceiveFlags , SimpleNetwork } ;
6+ use uefi:: proto:: network:: snp:: { InterruptStatus , NetworkState , ReceiveFlags , SimpleNetwork } ;
77use uefi:: { Status , boot} ;
88
9+ const ETHERNET_PROTOCOL_IPV4 : u16 = 0x0800 ;
10+ /// The MAC address configured for the interface.
11+ const EXPECTED_MAC : [ u8 ; 6 ] = [ 0x52 , 0x54 , 0 , 0 , 0 , 0x1 ] ;
12+
13+ /// Receives the next IPv4 packet and prints corresponding metadata.
14+ ///
15+ /// Returns the length of the response.
16+ fn receive ( simple_network : & mut SimpleNetwork , buffer : & mut [ u8 ] ) -> uefi:: Result < usize > {
17+ // Wait for a bit to ensure that the previous packet has been processed.
18+ boot:: stall ( Duration :: from_millis ( 500 ) ) ;
19+
20+ let mut recv_src_mac = MacAddress ( [ 0 ; 32 ] ) ;
21+ let mut recv_dst_mac = MacAddress ( [ 0 ; 32 ] ) ;
22+ let mut recv_ethernet_protocol = 0 ;
23+
24+ let res = simple_network. receive (
25+ buffer,
26+ None ,
27+ Some ( & mut recv_src_mac) ,
28+ Some ( & mut recv_dst_mac) ,
29+ Some ( & mut recv_ethernet_protocol) ,
30+ ) ;
31+
32+ // To simplify debugging when receive an unexpected packet, we print the
33+ // necessary info. This is especially useful if an unexpected IPv4 or ARP
34+ // packet is received, which can easily happen when fiddling around with
35+ // this test.
36+ res. inspect ( |_| {
37+ debug ! ( "Received:" ) ;
38+ debug ! ( " src_mac = {:x?}" , recv_src_mac) ;
39+ debug ! ( " dst_mac = {:x?}" , recv_dst_mac) ;
40+ debug ! ( " ethernet_proto=0x{:x?}" , recv_ethernet_protocol) ;
41+
42+ // Assert the ethernet frame was sent to the expected interface.
43+ {
44+ // UEFI reports proper DST MAC
45+ assert_eq ! ( recv_dst_mac. 0 [ 0 ..6 ] , EXPECTED_MAC ) ;
46+ }
47+
48+ // Ensure that we do not accidentally get an ARP packet, which we
49+ // do not expect in this test.
50+ assert_eq ! ( recv_ethernet_protocol, ETHERNET_PROTOCOL_IPV4 )
51+ } )
52+ }
53+
54+ /// This test sends a simple UDP/IP packet to the `EchoService` (created by
55+ /// `cargo xtask run`) and receives its response.
956pub fn test ( ) {
57+ // Skip the test if the `pxe` feature is not enabled.
58+ if cfg ! ( not( feature = "pxe" ) ) {
59+ return ;
60+ }
61+
1062 info ! ( "Testing the simple network protocol" ) ;
1163
1264 let handles = boot:: find_handles :: < SimpleNetwork > ( ) . unwrap_or_default ( ) ;
1365
66+ // The handle to our specific network device, as the test requires also a
67+ // specific environment. We do not test all possible handles.
68+ let mut simple_network = None ;
69+
70+ // We iterate over all handles until we found the right network device.
1471 for handle in handles {
15- let simple_network = boot:: open_protocol_exclusive :: < SimpleNetwork > ( handle) ;
16- if simple_network. is_err ( ) {
72+ let Ok ( handle) = boot:: open_protocol_exclusive :: < SimpleNetwork > ( handle) else {
1773 continue ;
18- }
19- let simple_network = simple_network. unwrap ( ) ;
20-
21- // Check shutdown
22- let res = simple_network. shutdown ( ) ;
23- assert ! ( res == Ok ( ( ) ) || res == Err ( Status :: NOT_STARTED . into( ) ) ) ;
24-
25- // Check stop
26- let res = simple_network. stop ( ) ;
27- assert ! ( res == Ok ( ( ) ) || res == Err ( Status :: NOT_STARTED . into( ) ) ) ;
28-
29- // Check start
30- simple_network
31- . start ( )
32- . expect ( "Failed to start Simple Network" ) ;
33-
34- // Check initialize
35- simple_network
36- . initialize ( 0 , 0 )
37- . expect ( "Failed to initialize Simple Network" ) ;
38-
39- // edk2 virtio-net driver does not support statistics, so
40- // allow UNSUPPORTED (same for collect_statistics below).
41- let res = simple_network. reset_statistics ( ) ;
42- assert ! ( res == Ok ( ( ) ) || res == Err ( Status :: UNSUPPORTED . into( ) ) ) ;
43-
44- // Reading the interrupt status clears it
45- simple_network. get_interrupt_status ( ) . unwrap ( ) ;
46-
47- // Set receive filters
48- simple_network
49- . receive_filters (
50- ReceiveFlags :: UNICAST | ReceiveFlags :: BROADCAST ,
51- ReceiveFlags :: empty ( ) ,
52- false ,
53- None ,
54- )
55- . expect ( "Failed to set receive filters" ) ;
56-
57- // Check media
58- if !bool:: from ( simple_network. mode ( ) . media_present_supported )
59- || !bool:: from ( simple_network. mode ( ) . media_present )
74+ } ;
75+
76+ // Check media is present
77+ if !bool:: from ( handle. mode ( ) . media_present_supported )
78+ || !bool:: from ( handle. mode ( ) . media_present )
6079 {
6180 continue ;
6281 }
6382
64- let payload = b"\0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \
83+ let has_mac = handle. mode ( ) . current_address . 0 [ 0 ..6 ] == EXPECTED_MAC
84+ && handle. mode ( ) . permanent_address . 0 [ 0 ..6 ] == EXPECTED_MAC ;
85+ if !has_mac {
86+ continue ;
87+ }
88+
89+ simple_network. replace ( handle) ;
90+ }
91+
92+ let mut simple_network = simple_network. unwrap_or_else ( || panic ! (
93+ "Failed to find SNP handle for network device with MAC address {:x}:{:x}:{:x}:{:x}:{:x}:{:x}" ,
94+ EXPECTED_MAC [ 0 ] ,
95+ EXPECTED_MAC [ 1 ] ,
96+ EXPECTED_MAC [ 2 ] ,
97+ EXPECTED_MAC [ 3 ] ,
98+ EXPECTED_MAC [ 4 ] ,
99+ EXPECTED_MAC [ 5 ]
100+ ) ) ;
101+
102+ assert_eq ! (
103+ simple_network. mode( ) . state,
104+ NetworkState :: STOPPED ,
105+ "Should be in stopped state"
106+ ) ;
107+
108+ simple_network
109+ . start ( )
110+ . expect ( "Failed to start Simple Network" ) ;
111+
112+ simple_network
113+ . initialize ( 0 , 0 )
114+ . expect ( "Failed to initialize Simple Network" ) ;
115+
116+ // edk2 virtio-net driver does not support statistics, so
117+ // allow UNSUPPORTED (same for collect_statistics below).
118+ let res = simple_network. reset_statistics ( ) ;
119+ assert ! ( res == Ok ( ( ) ) || res == Err ( Status :: UNSUPPORTED . into( ) ) ) ;
120+
121+ // Reading the interrupt status clears it
122+ simple_network. get_interrupt_status ( ) . unwrap ( ) ;
123+
124+ // Set receive filters
125+ simple_network
126+ . receive_filters (
127+ ReceiveFlags :: UNICAST | ReceiveFlags :: BROADCAST ,
128+ ReceiveFlags :: empty ( ) ,
129+ false ,
130+ None ,
131+ )
132+ . expect ( "Failed to set receive filters" ) ;
133+
134+ // EthernetFrame(IPv4Packet(UDPPacket(Payload))).
135+ // The ethernet frame header will be filled by `transmit()`.
136+ // The UDP packet contains the byte sequence `4, 4, 3, 2, 1`.
137+ //
138+ // The packet is sent to the `EchoService` created by
139+ // `cargo xtask run`. It runs on UDP port 21572.
140+ let payload = b"\0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \
65141 \x45 \x00 \
66142 \x00 \x21 \
67143 \x00 \x01 \
@@ -77,65 +153,59 @@ pub fn test() {
77153 \xa9 \xe4 \
78154 \x04 \x01 \x02 \x03 \x04 ";
79155
80- let dest_addr = MacAddress ( [ 0xffu8 ; 32 ] ) ;
81- assert ! (
82- !simple_network
83- . get_interrupt_status( )
84- . unwrap( )
85- . contains( InterruptStatus :: TRANSMIT )
86- ) ;
87-
88- // Send the frame
89- simple_network
90- . transmit (
91- simple_network. mode ( ) . media_header_size as usize ,
92- payload,
93- None ,
94- Some ( dest_addr) ,
95- Some ( 0x0800 ) ,
96- )
97- . expect ( "Failed to transmit frame" ) ;
98-
99- info ! ( "Waiting for the transmit" ) ;
100- while !simple_network
156+ assert ! (
157+ !simple_network
101158 . get_interrupt_status( )
102159 . unwrap( )
103160 . contains( InterruptStatus :: TRANSMIT )
104- { }
105-
106- // Attempt to receive a frame
107- let mut buffer = [ 0u8 ; 1500 ] ;
108-
109- info ! ( "Waiting for the reception" ) ;
110- if simple_network. receive ( & mut buffer, None , None , None , None )
111- == Err ( Status :: NOT_READY . into ( ) )
112- {
113- boot:: stall ( Duration :: from_secs ( 1 ) ) ;
114-
115- simple_network
116- . receive ( & mut buffer, None , None , None , None )
117- . unwrap ( ) ;
161+ ) ;
162+
163+ // Send the frame
164+ simple_network
165+ . transmit (
166+ simple_network. mode ( ) . media_header_size as usize ,
167+ payload,
168+ None ,
169+ Some ( simple_network. mode ( ) . broadcast_address ) ,
170+ Some ( ETHERNET_PROTOCOL_IPV4 ) ,
171+ )
172+ . expect ( "Failed to transmit frame" ) ;
173+
174+ info ! ( "Waiting for the transmit" ) ;
175+ while !simple_network
176+ . get_interrupt_status ( )
177+ . unwrap ( )
178+ . contains ( InterruptStatus :: TRANSMIT )
179+ { }
180+
181+ // Attempt to receive a frame
182+ let mut buffer = [ 0u8 ; 1500 ] ;
183+
184+ info ! ( "Waiting for the reception" ) ;
185+ let n = receive ( simple_network. deref_mut ( ) , & mut buffer) . unwrap ( ) ;
186+ debug ! ( "Reply has {n} bytes" ) ;
187+
188+ // Check payload in UDP packet that was reversed by our EchoService.
189+ assert_eq ! ( buffer[ 42 ..47 ] , [ 4 , 4 , 3 , 2 , 1 ] ) ;
190+
191+ // Get stats
192+ let res = simple_network. collect_statistics ( ) ;
193+ match res {
194+ Ok ( stats) => {
195+ info ! ( "Stats: {:?}" , stats) ;
196+
197+ // One frame should have been transmitted and one received
198+ assert_eq ! ( stats. tx_total_frames( ) . unwrap( ) , 1 ) ;
199+ assert_eq ! ( stats. rx_total_frames( ) . unwrap( ) , 1 ) ;
118200 }
119-
120- assert_eq ! ( buffer[ 42 ..47 ] , [ 4 , 4 , 3 , 2 , 1 ] ) ;
121-
122- // Get stats
123- let res = simple_network. collect_statistics ( ) ;
124- match res {
125- Ok ( stats) => {
126- info ! ( "Stats: {:?}" , stats) ;
127-
128- // One frame should have been transmitted and one received
129- assert_eq ! ( stats. tx_total_frames( ) . unwrap( ) , 1 ) ;
130- assert_eq ! ( stats. rx_total_frames( ) . unwrap( ) , 1 ) ;
131- }
132- Err ( e) => {
133- if e == Status :: UNSUPPORTED . into ( ) {
134- info ! ( "Stats: unsupported." ) ;
135- } else {
136- panic ! ( "{e}" ) ;
137- }
201+ Err ( e) => {
202+ if e == Status :: UNSUPPORTED . into ( ) {
203+ info ! ( "Stats: unsupported." ) ;
204+ } else {
205+ panic ! ( "{e}" ) ;
138206 }
139207 }
140208 }
209+
210+ simple_network. shutdown ( ) . unwrap ( ) ;
141211}
0 commit comments