1
1
// SPDX-License-Identifier: MIT OR Apache-2.0
2
2
3
+ use core:: ops:: DerefMut ;
3
4
use core:: time:: Duration ;
4
-
5
5
use uefi:: proto:: network:: MacAddress ;
6
- use uefi:: proto:: network:: snp:: { InterruptStatus , ReceiveFlags , SimpleNetwork } ;
6
+ use uefi:: proto:: network:: snp:: { InterruptStatus , NetworkState , ReceiveFlags , SimpleNetwork } ;
7
7
use uefi:: { Status , boot} ;
8
8
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.
9
56
pub fn test ( ) {
57
+ // Skip the test if the `pxe` feature is not enabled.
58
+ if cfg ! ( not( feature = "pxe" ) ) {
59
+ return ;
60
+ }
61
+
10
62
info ! ( "Testing the simple network protocol" ) ;
11
63
12
64
let handles = boot:: find_handles :: < SimpleNetwork > ( ) . unwrap_or_default ( ) ;
13
65
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.
14
71
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 {
17
73
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 )
60
79
{
61
80
continue ;
62
81
}
63
82
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 \
65
141
\x45 \x00 \
66
142
\x00 \x21 \
67
143
\x00 \x01 \
@@ -77,65 +153,59 @@ pub fn test() {
77
153
\xa9 \xe4 \
78
154
\x04 \x01 \x02 \x03 \x04 ";
79
155
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
101
158
. get_interrupt_status( )
102
159
. unwrap( )
103
160
. 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 ) ;
118
200
}
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}" ) ;
138
206
}
139
207
}
140
208
}
209
+
210
+ simple_network. shutdown ( ) . unwrap ( ) ;
141
211
}
0 commit comments