1 | /* |
---|
2 | * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>. |
---|
3 | * |
---|
4 | * This program is free software; you can redistribute it and/or |
---|
5 | * modify it under the terms of the GNU General Public License as |
---|
6 | * published by the Free Software Foundation; either version 2 of the |
---|
7 | * License, or any later version. |
---|
8 | * |
---|
9 | * This program is distributed in the hope that it will be useful, but |
---|
10 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
---|
12 | * General Public License for more details. |
---|
13 | * |
---|
14 | * You should have received a copy of the GNU General Public License |
---|
15 | * along with this program; if not, write to the Free Software |
---|
16 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
---|
17 | */ |
---|
18 | |
---|
19 | FILE_LICENCE ( GPL2_OR_LATER ); |
---|
20 | |
---|
21 | #include <stdlib.h> |
---|
22 | #include <string.h> |
---|
23 | #include <errno.h> |
---|
24 | #include <assert.h> |
---|
25 | #include <byteswap.h> |
---|
26 | #include <gpxe/netdevice.h> |
---|
27 | #include <gpxe/iobuf.h> |
---|
28 | #include <gpxe/in.h> |
---|
29 | #include <gpxe/pci.h> |
---|
30 | #include <gpxe/efi/efi.h> |
---|
31 | #include <gpxe/efi/Protocol/DriverBinding.h> |
---|
32 | #include <gpxe/efi/Protocol/PciIo.h> |
---|
33 | #include <gpxe/efi/Protocol/SimpleNetwork.h> |
---|
34 | #include <gpxe/efi/Protocol/ComponentName2.h> |
---|
35 | #include <gpxe/efi/Protocol/NetworkInterfaceIdentifier.h> |
---|
36 | #include <config/general.h> |
---|
37 | |
---|
38 | /** @file |
---|
39 | * |
---|
40 | * gPXE EFI SNP interface |
---|
41 | * |
---|
42 | */ |
---|
43 | |
---|
44 | /** An SNP device */ |
---|
45 | struct efi_snp_device { |
---|
46 | /** The underlying gPXE network device */ |
---|
47 | struct net_device *netdev; |
---|
48 | /** EFI device handle */ |
---|
49 | EFI_HANDLE handle; |
---|
50 | /** The SNP structure itself */ |
---|
51 | EFI_SIMPLE_NETWORK_PROTOCOL snp; |
---|
52 | /** The SNP "mode" (parameters) */ |
---|
53 | EFI_SIMPLE_NETWORK_MODE mode; |
---|
54 | /** Outstanding TX packet count (via "interrupt status") |
---|
55 | * |
---|
56 | * Used in order to generate TX completions. |
---|
57 | */ |
---|
58 | unsigned int tx_count_interrupts; |
---|
59 | /** Outstanding TX packet count (via "recycled tx buffers") |
---|
60 | * |
---|
61 | * Used in order to generate TX completions. |
---|
62 | */ |
---|
63 | unsigned int tx_count_txbufs; |
---|
64 | /** Outstanding RX packet count (via "interrupt status") */ |
---|
65 | unsigned int rx_count_interrupts; |
---|
66 | /** Outstanding RX packet count (via WaitForPacket event) */ |
---|
67 | unsigned int rx_count_events; |
---|
68 | /** The network interface identifier */ |
---|
69 | EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL nii; |
---|
70 | /** Device name */ |
---|
71 | wchar_t name[ sizeof ( ( ( struct net_device * ) NULL )->name ) ]; |
---|
72 | /** The device path |
---|
73 | * |
---|
74 | * This field is variable in size and must appear at the end |
---|
75 | * of the structure. |
---|
76 | */ |
---|
77 | EFI_DEVICE_PATH_PROTOCOL path; |
---|
78 | }; |
---|
79 | |
---|
80 | /** EFI simple network protocol GUID */ |
---|
81 | static EFI_GUID efi_simple_network_protocol_guid |
---|
82 | = EFI_SIMPLE_NETWORK_PROTOCOL_GUID; |
---|
83 | |
---|
84 | /** EFI driver binding protocol GUID */ |
---|
85 | static EFI_GUID efi_driver_binding_protocol_guid |
---|
86 | = EFI_DRIVER_BINDING_PROTOCOL_GUID; |
---|
87 | |
---|
88 | /** EFI component name protocol GUID */ |
---|
89 | static EFI_GUID efi_component_name2_protocol_guid |
---|
90 | = EFI_COMPONENT_NAME2_PROTOCOL_GUID; |
---|
91 | |
---|
92 | /** EFI device path protocol GUID */ |
---|
93 | static EFI_GUID efi_device_path_protocol_guid |
---|
94 | = EFI_DEVICE_PATH_PROTOCOL_GUID; |
---|
95 | |
---|
96 | /** EFI network interface identifier GUID */ |
---|
97 | static EFI_GUID efi_nii_protocol_guid |
---|
98 | = EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL_GUID; |
---|
99 | |
---|
100 | /** EFI network interface identifier GUID (extra special version) */ |
---|
101 | static EFI_GUID efi_nii31_protocol_guid = { |
---|
102 | /* At some point, it seems that someone decided to change the |
---|
103 | * GUID. Current EFI builds ignore the older GUID, older EFI |
---|
104 | * builds ignore the newer GUID, so we have to expose both. |
---|
105 | */ |
---|
106 | 0x1ACED566, 0x76ED, 0x4218, |
---|
107 | { 0xBC, 0x81, 0x76, 0x7F, 0x1F, 0x97, 0x7A, 0x89 } |
---|
108 | }; |
---|
109 | |
---|
110 | /** EFI PCI I/O protocol GUID */ |
---|
111 | static EFI_GUID efi_pci_io_protocol_guid |
---|
112 | = EFI_PCI_IO_PROTOCOL_GUID; |
---|
113 | |
---|
114 | /** |
---|
115 | * Set EFI SNP mode based on gPXE net device parameters |
---|
116 | * |
---|
117 | * @v snp SNP interface |
---|
118 | */ |
---|
119 | static void efi_snp_set_mode ( struct efi_snp_device *snpdev ) { |
---|
120 | struct net_device *netdev = snpdev->netdev; |
---|
121 | EFI_SIMPLE_NETWORK_MODE *mode = &snpdev->mode; |
---|
122 | struct ll_protocol *ll_protocol = netdev->ll_protocol; |
---|
123 | unsigned int ll_addr_len = ll_protocol->ll_addr_len; |
---|
124 | |
---|
125 | mode->HwAddressSize = ll_addr_len; |
---|
126 | mode->MediaHeaderSize = ll_protocol->ll_header_len; |
---|
127 | mode->MaxPacketSize = netdev->max_pkt_len; |
---|
128 | mode->ReceiveFilterMask = ( EFI_SIMPLE_NETWORK_RECEIVE_UNICAST | |
---|
129 | EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST | |
---|
130 | EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST ); |
---|
131 | assert ( ll_addr_len <= sizeof ( mode->CurrentAddress ) ); |
---|
132 | memcpy ( &mode->CurrentAddress, netdev->ll_addr, ll_addr_len ); |
---|
133 | memcpy ( &mode->BroadcastAddress, netdev->ll_broadcast, ll_addr_len ); |
---|
134 | ll_protocol->init_addr ( netdev->hw_addr, &mode->PermanentAddress ); |
---|
135 | mode->IfType = ntohs ( ll_protocol->ll_proto ); |
---|
136 | mode->MacAddressChangeable = TRUE; |
---|
137 | mode->MediaPresentSupported = TRUE; |
---|
138 | mode->MediaPresent = ( netdev_link_ok ( netdev ) ? TRUE : FALSE ); |
---|
139 | } |
---|
140 | |
---|
141 | /** |
---|
142 | * Poll net device and count received packets |
---|
143 | * |
---|
144 | * @v snpdev SNP device |
---|
145 | */ |
---|
146 | static void efi_snp_poll ( struct efi_snp_device *snpdev ) { |
---|
147 | struct io_buffer *iobuf; |
---|
148 | unsigned int before = 0; |
---|
149 | unsigned int after = 0; |
---|
150 | unsigned int arrived; |
---|
151 | |
---|
152 | /* We have to report packet arrivals, and this is the easiest |
---|
153 | * way to fake it. |
---|
154 | */ |
---|
155 | list_for_each_entry ( iobuf, &snpdev->netdev->rx_queue, list ) |
---|
156 | before++; |
---|
157 | netdev_poll ( snpdev->netdev ); |
---|
158 | list_for_each_entry ( iobuf, &snpdev->netdev->rx_queue, list ) |
---|
159 | after++; |
---|
160 | arrived = ( after - before ); |
---|
161 | |
---|
162 | snpdev->rx_count_interrupts += arrived; |
---|
163 | snpdev->rx_count_events += arrived; |
---|
164 | } |
---|
165 | |
---|
166 | /** |
---|
167 | * Change SNP state from "stopped" to "started" |
---|
168 | * |
---|
169 | * @v snp SNP interface |
---|
170 | * @ret efirc EFI status code |
---|
171 | */ |
---|
172 | static EFI_STATUS EFIAPI |
---|
173 | efi_snp_start ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) { |
---|
174 | struct efi_snp_device *snpdev = |
---|
175 | container_of ( snp, struct efi_snp_device, snp ); |
---|
176 | |
---|
177 | DBGC2 ( snpdev, "SNPDEV %p START\n", snpdev ); |
---|
178 | |
---|
179 | snpdev->mode.State = EfiSimpleNetworkStarted; |
---|
180 | return 0; |
---|
181 | } |
---|
182 | |
---|
183 | /** |
---|
184 | * Change SNP state from "started" to "stopped" |
---|
185 | * |
---|
186 | * @v snp SNP interface |
---|
187 | * @ret efirc EFI status code |
---|
188 | */ |
---|
189 | static EFI_STATUS EFIAPI |
---|
190 | efi_snp_stop ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) { |
---|
191 | struct efi_snp_device *snpdev = |
---|
192 | container_of ( snp, struct efi_snp_device, snp ); |
---|
193 | |
---|
194 | DBGC2 ( snpdev, "SNPDEV %p STOP\n", snpdev ); |
---|
195 | |
---|
196 | snpdev->mode.State = EfiSimpleNetworkStopped; |
---|
197 | return 0; |
---|
198 | } |
---|
199 | |
---|
200 | /** |
---|
201 | * Open the network device |
---|
202 | * |
---|
203 | * @v snp SNP interface |
---|
204 | * @v extra_rx_bufsize Extra RX buffer size, in bytes |
---|
205 | * @v extra_tx_bufsize Extra TX buffer size, in bytes |
---|
206 | * @ret efirc EFI status code |
---|
207 | */ |
---|
208 | static EFI_STATUS EFIAPI |
---|
209 | efi_snp_initialize ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, |
---|
210 | UINTN extra_rx_bufsize, UINTN extra_tx_bufsize ) { |
---|
211 | struct efi_snp_device *snpdev = |
---|
212 | container_of ( snp, struct efi_snp_device, snp ); |
---|
213 | int rc; |
---|
214 | |
---|
215 | DBGC2 ( snpdev, "SNPDEV %p INITIALIZE (%ld extra RX, %ld extra TX)\n", |
---|
216 | snpdev, ( ( unsigned long ) extra_rx_bufsize ), |
---|
217 | ( ( unsigned long ) extra_tx_bufsize ) ); |
---|
218 | |
---|
219 | if ( ( rc = netdev_open ( snpdev->netdev ) ) != 0 ) { |
---|
220 | DBGC ( snpdev, "SNPDEV %p could not open %s: %s\n", |
---|
221 | snpdev, snpdev->netdev->name, strerror ( rc ) ); |
---|
222 | return RC_TO_EFIRC ( rc ); |
---|
223 | } |
---|
224 | |
---|
225 | snpdev->mode.State = EfiSimpleNetworkInitialized; |
---|
226 | return 0; |
---|
227 | } |
---|
228 | |
---|
229 | /** |
---|
230 | * Reset the network device |
---|
231 | * |
---|
232 | * @v snp SNP interface |
---|
233 | * @v ext_verify Extended verification required |
---|
234 | * @ret efirc EFI status code |
---|
235 | */ |
---|
236 | static EFI_STATUS EFIAPI |
---|
237 | efi_snp_reset ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN ext_verify ) { |
---|
238 | struct efi_snp_device *snpdev = |
---|
239 | container_of ( snp, struct efi_snp_device, snp ); |
---|
240 | int rc; |
---|
241 | |
---|
242 | DBGC2 ( snpdev, "SNPDEV %p RESET (%s extended verification)\n", |
---|
243 | snpdev, ( ext_verify ? "with" : "without" ) ); |
---|
244 | |
---|
245 | netdev_close ( snpdev->netdev ); |
---|
246 | snpdev->mode.State = EfiSimpleNetworkStarted; |
---|
247 | |
---|
248 | if ( ( rc = netdev_open ( snpdev->netdev ) ) != 0 ) { |
---|
249 | DBGC ( snpdev, "SNPDEV %p could not reopen %s: %s\n", |
---|
250 | snpdev, snpdev->netdev->name, strerror ( rc ) ); |
---|
251 | return RC_TO_EFIRC ( rc ); |
---|
252 | } |
---|
253 | |
---|
254 | snpdev->mode.State = EfiSimpleNetworkInitialized; |
---|
255 | return 0; |
---|
256 | } |
---|
257 | |
---|
258 | /** |
---|
259 | * Shut down the network device |
---|
260 | * |
---|
261 | * @v snp SNP interface |
---|
262 | * @ret efirc EFI status code |
---|
263 | */ |
---|
264 | static EFI_STATUS EFIAPI |
---|
265 | efi_snp_shutdown ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) { |
---|
266 | struct efi_snp_device *snpdev = |
---|
267 | container_of ( snp, struct efi_snp_device, snp ); |
---|
268 | |
---|
269 | DBGC2 ( snpdev, "SNPDEV %p SHUTDOWN\n", snpdev ); |
---|
270 | |
---|
271 | netdev_close ( snpdev->netdev ); |
---|
272 | snpdev->mode.State = EfiSimpleNetworkStarted; |
---|
273 | return 0; |
---|
274 | } |
---|
275 | |
---|
276 | /** |
---|
277 | * Manage receive filters |
---|
278 | * |
---|
279 | * @v snp SNP interface |
---|
280 | * @v enable Receive filters to enable |
---|
281 | * @v disable Receive filters to disable |
---|
282 | * @v mcast_reset Reset multicast filters |
---|
283 | * @v mcast_count Number of multicast filters |
---|
284 | * @v mcast Multicast filters |
---|
285 | * @ret efirc EFI status code |
---|
286 | */ |
---|
287 | static EFI_STATUS EFIAPI |
---|
288 | efi_snp_receive_filters ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, UINT32 enable, |
---|
289 | UINT32 disable, BOOLEAN mcast_reset, |
---|
290 | UINTN mcast_count, EFI_MAC_ADDRESS *mcast ) { |
---|
291 | struct efi_snp_device *snpdev = |
---|
292 | container_of ( snp, struct efi_snp_device, snp ); |
---|
293 | unsigned int i; |
---|
294 | |
---|
295 | DBGC2 ( snpdev, "SNPDEV %p RECEIVE_FILTERS %08x&~%08x%s %ld mcast\n", |
---|
296 | snpdev, enable, disable, ( mcast_reset ? " reset" : "" ), |
---|
297 | ( ( unsigned long ) mcast_count ) ); |
---|
298 | for ( i = 0 ; i < mcast_count ; i++ ) { |
---|
299 | DBGC2_HDA ( snpdev, i, &mcast[i], |
---|
300 | snpdev->netdev->ll_protocol->ll_addr_len ); |
---|
301 | } |
---|
302 | |
---|
303 | /* Lie through our teeth, otherwise MNP refuses to accept us */ |
---|
304 | return 0; |
---|
305 | } |
---|
306 | |
---|
307 | /** |
---|
308 | * Set station address |
---|
309 | * |
---|
310 | * @v snp SNP interface |
---|
311 | * @v reset Reset to permanent address |
---|
312 | * @v new New station address |
---|
313 | * @ret efirc EFI status code |
---|
314 | */ |
---|
315 | static EFI_STATUS EFIAPI |
---|
316 | efi_snp_station_address ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN reset, |
---|
317 | EFI_MAC_ADDRESS *new ) { |
---|
318 | struct efi_snp_device *snpdev = |
---|
319 | container_of ( snp, struct efi_snp_device, snp ); |
---|
320 | struct ll_protocol *ll_protocol = snpdev->netdev->ll_protocol; |
---|
321 | |
---|
322 | DBGC2 ( snpdev, "SNPDEV %p STATION_ADDRESS %s\n", snpdev, |
---|
323 | ( reset ? "reset" : ll_protocol->ntoa ( new ) ) ); |
---|
324 | |
---|
325 | /* Set the MAC address */ |
---|
326 | if ( reset ) |
---|
327 | new = &snpdev->mode.PermanentAddress; |
---|
328 | memcpy ( snpdev->netdev->ll_addr, new, ll_protocol->ll_addr_len ); |
---|
329 | |
---|
330 | /* MAC address changes take effect only on netdev_open() */ |
---|
331 | if ( snpdev->netdev->state & NETDEV_OPEN ) { |
---|
332 | DBGC ( snpdev, "SNPDEV %p MAC address changed while net " |
---|
333 | "devive open\n", snpdev ); |
---|
334 | } |
---|
335 | |
---|
336 | return 0; |
---|
337 | } |
---|
338 | |
---|
339 | /** |
---|
340 | * Get (or reset) statistics |
---|
341 | * |
---|
342 | * @v snp SNP interface |
---|
343 | * @v reset Reset statistics |
---|
344 | * @v stats_len Size of statistics table |
---|
345 | * @v stats Statistics table |
---|
346 | * @ret efirc EFI status code |
---|
347 | */ |
---|
348 | static EFI_STATUS EFIAPI |
---|
349 | efi_snp_statistics ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN reset, |
---|
350 | UINTN *stats_len, EFI_NETWORK_STATISTICS *stats ) { |
---|
351 | struct efi_snp_device *snpdev = |
---|
352 | container_of ( snp, struct efi_snp_device, snp ); |
---|
353 | EFI_NETWORK_STATISTICS stats_buf; |
---|
354 | |
---|
355 | DBGC2 ( snpdev, "SNPDEV %p STATISTICS%s", snpdev, |
---|
356 | ( reset ? " reset" : "" ) ); |
---|
357 | |
---|
358 | /* Gather statistics */ |
---|
359 | memset ( &stats_buf, 0, sizeof ( stats_buf ) ); |
---|
360 | stats_buf.TxGoodFrames = snpdev->netdev->tx_stats.good; |
---|
361 | stats_buf.TxDroppedFrames = snpdev->netdev->tx_stats.bad; |
---|
362 | stats_buf.TxTotalFrames = ( snpdev->netdev->tx_stats.good + |
---|
363 | snpdev->netdev->tx_stats.bad ); |
---|
364 | stats_buf.RxGoodFrames = snpdev->netdev->rx_stats.good; |
---|
365 | stats_buf.RxDroppedFrames = snpdev->netdev->rx_stats.bad; |
---|
366 | stats_buf.RxTotalFrames = ( snpdev->netdev->rx_stats.good + |
---|
367 | snpdev->netdev->rx_stats.bad ); |
---|
368 | if ( *stats_len > sizeof ( stats_buf ) ) |
---|
369 | *stats_len = sizeof ( stats_buf ); |
---|
370 | if ( stats ) |
---|
371 | memcpy ( stats, &stats_buf, *stats_len ); |
---|
372 | |
---|
373 | /* Reset statistics if requested to do so */ |
---|
374 | if ( reset ) { |
---|
375 | memset ( &snpdev->netdev->tx_stats, 0, |
---|
376 | sizeof ( snpdev->netdev->tx_stats ) ); |
---|
377 | memset ( &snpdev->netdev->rx_stats, 0, |
---|
378 | sizeof ( snpdev->netdev->rx_stats ) ); |
---|
379 | } |
---|
380 | |
---|
381 | return 0; |
---|
382 | } |
---|
383 | |
---|
384 | /** |
---|
385 | * Convert multicast IP address to MAC address |
---|
386 | * |
---|
387 | * @v snp SNP interface |
---|
388 | * @v ipv6 Address is IPv6 |
---|
389 | * @v ip IP address |
---|
390 | * @v mac MAC address |
---|
391 | * @ret efirc EFI status code |
---|
392 | */ |
---|
393 | static EFI_STATUS EFIAPI |
---|
394 | efi_snp_mcast_ip_to_mac ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN ipv6, |
---|
395 | EFI_IP_ADDRESS *ip, EFI_MAC_ADDRESS *mac ) { |
---|
396 | struct efi_snp_device *snpdev = |
---|
397 | container_of ( snp, struct efi_snp_device, snp ); |
---|
398 | struct ll_protocol *ll_protocol = snpdev->netdev->ll_protocol; |
---|
399 | const char *ip_str; |
---|
400 | int rc; |
---|
401 | |
---|
402 | ip_str = ( ipv6 ? "(IPv6)" /* FIXME when we have inet6_ntoa() */ : |
---|
403 | inet_ntoa ( *( ( struct in_addr * ) ip ) ) ); |
---|
404 | DBGC2 ( snpdev, "SNPDEV %p MCAST_IP_TO_MAC %s\n", snpdev, ip_str ); |
---|
405 | |
---|
406 | /* Try to hash the address */ |
---|
407 | if ( ( rc = ll_protocol->mc_hash ( ( ipv6 ? AF_INET6 : AF_INET ), |
---|
408 | ip, mac ) ) != 0 ) { |
---|
409 | DBGC ( snpdev, "SNPDEV %p could not hash %s: %s\n", |
---|
410 | snpdev, ip_str, strerror ( rc ) ); |
---|
411 | return RC_TO_EFIRC ( rc ); |
---|
412 | } |
---|
413 | |
---|
414 | return 0; |
---|
415 | } |
---|
416 | |
---|
417 | /** |
---|
418 | * Read or write non-volatile storage |
---|
419 | * |
---|
420 | * @v snp SNP interface |
---|
421 | * @v read Operation is a read |
---|
422 | * @v offset Starting offset within NVRAM |
---|
423 | * @v len Length of data buffer |
---|
424 | * @v data Data buffer |
---|
425 | * @ret efirc EFI status code |
---|
426 | */ |
---|
427 | static EFI_STATUS EFIAPI |
---|
428 | efi_snp_nvdata ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN read, |
---|
429 | UINTN offset, UINTN len, VOID *data ) { |
---|
430 | struct efi_snp_device *snpdev = |
---|
431 | container_of ( snp, struct efi_snp_device, snp ); |
---|
432 | |
---|
433 | DBGC2 ( snpdev, "SNPDEV %p NVDATA %s %lx+%lx\n", snpdev, |
---|
434 | ( read ? "read" : "write" ), ( ( unsigned long ) offset ), |
---|
435 | ( ( unsigned long ) len ) ); |
---|
436 | if ( ! read ) |
---|
437 | DBGC2_HDA ( snpdev, offset, data, len ); |
---|
438 | |
---|
439 | return EFI_UNSUPPORTED; |
---|
440 | } |
---|
441 | |
---|
442 | /** |
---|
443 | * Read interrupt status and TX recycled buffer status |
---|
444 | * |
---|
445 | * @v snp SNP interface |
---|
446 | * @v interrupts Interrupt status, or NULL |
---|
447 | * @v txbufs Recycled transmit buffer address, or NULL |
---|
448 | * @ret efirc EFI status code |
---|
449 | */ |
---|
450 | static EFI_STATUS EFIAPI |
---|
451 | efi_snp_get_status ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, |
---|
452 | UINT32 *interrupts, VOID **txbufs ) { |
---|
453 | struct efi_snp_device *snpdev = |
---|
454 | container_of ( snp, struct efi_snp_device, snp ); |
---|
455 | |
---|
456 | DBGC2 ( snpdev, "SNPDEV %p GET_STATUS", snpdev ); |
---|
457 | |
---|
458 | /* Poll the network device */ |
---|
459 | efi_snp_poll ( snpdev ); |
---|
460 | |
---|
461 | /* Interrupt status. In practice, this seems to be used only |
---|
462 | * to detect TX completions. |
---|
463 | */ |
---|
464 | if ( interrupts ) { |
---|
465 | *interrupts = 0; |
---|
466 | /* Report TX completions once queue is empty; this |
---|
467 | * avoids having to add hooks in the net device layer. |
---|
468 | */ |
---|
469 | if ( snpdev->tx_count_interrupts && |
---|
470 | list_empty ( &snpdev->netdev->tx_queue ) ) { |
---|
471 | *interrupts |= EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT; |
---|
472 | snpdev->tx_count_interrupts--; |
---|
473 | } |
---|
474 | /* Report RX */ |
---|
475 | if ( snpdev->rx_count_interrupts ) { |
---|
476 | *interrupts |= EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT; |
---|
477 | snpdev->rx_count_interrupts--; |
---|
478 | } |
---|
479 | DBGC2 ( snpdev, " INTS:%02x", *interrupts ); |
---|
480 | } |
---|
481 | |
---|
482 | /* TX completions. It would be possible to design a more |
---|
483 | * idiotic scheme for this, but it would be a challenge. |
---|
484 | * According to the UEFI header file, txbufs will be filled in |
---|
485 | * with a list of "recycled transmit buffers" (i.e. completed |
---|
486 | * TX buffers). Observant readers may care to note that |
---|
487 | * *txbufs is a void pointer. Precisely how a list of |
---|
488 | * completed transmit buffers is meant to be represented as an |
---|
489 | * array of voids is left as an exercise for the reader. |
---|
490 | * |
---|
491 | * The only users of this interface (MnpDxe/MnpIo.c and |
---|
492 | * PxeBcDxe/Bc.c within the EFI dev kit) both just poll until |
---|
493 | * seeing a non-NULL result return in txbufs. This is valid |
---|
494 | * provided that they do not ever attempt to transmit more |
---|
495 | * than one packet concurrently (and that TX never times out). |
---|
496 | */ |
---|
497 | if ( txbufs ) { |
---|
498 | if ( snpdev->tx_count_txbufs && |
---|
499 | list_empty ( &snpdev->netdev->tx_queue ) ) { |
---|
500 | *txbufs = "Which idiot designed this API?"; |
---|
501 | snpdev->tx_count_txbufs--; |
---|
502 | } else { |
---|
503 | *txbufs = NULL; |
---|
504 | } |
---|
505 | DBGC2 ( snpdev, " TX:%s", ( *txbufs ? "some" : "none" ) ); |
---|
506 | } |
---|
507 | |
---|
508 | DBGC2 ( snpdev, "\n" ); |
---|
509 | return 0; |
---|
510 | } |
---|
511 | |
---|
512 | /** |
---|
513 | * Start packet transmission |
---|
514 | * |
---|
515 | * @v snp SNP interface |
---|
516 | * @v ll_header_len Link-layer header length, if to be filled in |
---|
517 | * @v len Length of data buffer |
---|
518 | * @v data Data buffer |
---|
519 | * @v ll_src Link-layer source address, if specified |
---|
520 | * @v ll_dest Link-layer destination address, if specified |
---|
521 | * @v net_proto Network-layer protocol (in host order) |
---|
522 | * @ret efirc EFI status code |
---|
523 | */ |
---|
524 | static EFI_STATUS EFIAPI |
---|
525 | efi_snp_transmit ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, |
---|
526 | UINTN ll_header_len, UINTN len, VOID *data, |
---|
527 | EFI_MAC_ADDRESS *ll_src, EFI_MAC_ADDRESS *ll_dest, |
---|
528 | UINT16 *net_proto ) { |
---|
529 | struct efi_snp_device *snpdev = |
---|
530 | container_of ( snp, struct efi_snp_device, snp ); |
---|
531 | struct ll_protocol *ll_protocol = snpdev->netdev->ll_protocol; |
---|
532 | struct io_buffer *iobuf; |
---|
533 | int rc; |
---|
534 | EFI_STATUS efirc; |
---|
535 | |
---|
536 | DBGC2 ( snpdev, "SNPDEV %p TRANSMIT %p+%lx", snpdev, data, |
---|
537 | ( ( unsigned long ) len ) ); |
---|
538 | if ( ll_header_len ) { |
---|
539 | if ( ll_src ) { |
---|
540 | DBGC2 ( snpdev, " src %s", |
---|
541 | ll_protocol->ntoa ( ll_src ) ); |
---|
542 | } |
---|
543 | if ( ll_dest ) { |
---|
544 | DBGC2 ( snpdev, " dest %s", |
---|
545 | ll_protocol->ntoa ( ll_dest ) ); |
---|
546 | } |
---|
547 | if ( net_proto ) { |
---|
548 | DBGC2 ( snpdev, " proto %04x", *net_proto ); |
---|
549 | } |
---|
550 | } |
---|
551 | DBGC2 ( snpdev, "\n" ); |
---|
552 | |
---|
553 | /* Sanity checks */ |
---|
554 | if ( ll_header_len ) { |
---|
555 | if ( ll_header_len != ll_protocol->ll_header_len ) { |
---|
556 | DBGC ( snpdev, "SNPDEV %p TX invalid header length " |
---|
557 | "%ld\n", snpdev, |
---|
558 | ( ( unsigned long ) ll_header_len ) ); |
---|
559 | efirc = EFI_INVALID_PARAMETER; |
---|
560 | goto err_sanity; |
---|
561 | } |
---|
562 | if ( len < ll_header_len ) { |
---|
563 | DBGC ( snpdev, "SNPDEV %p invalid packet length %ld\n", |
---|
564 | snpdev, ( ( unsigned long ) len ) ); |
---|
565 | efirc = EFI_BUFFER_TOO_SMALL; |
---|
566 | goto err_sanity; |
---|
567 | } |
---|
568 | if ( ! ll_dest ) { |
---|
569 | DBGC ( snpdev, "SNPDEV %p TX missing destination " |
---|
570 | "address\n", snpdev ); |
---|
571 | efirc = EFI_INVALID_PARAMETER; |
---|
572 | goto err_sanity; |
---|
573 | } |
---|
574 | if ( ! net_proto ) { |
---|
575 | DBGC ( snpdev, "SNPDEV %p TX missing network " |
---|
576 | "protocol\n", snpdev ); |
---|
577 | efirc = EFI_INVALID_PARAMETER; |
---|
578 | goto err_sanity; |
---|
579 | } |
---|
580 | if ( ! ll_src ) |
---|
581 | ll_src = &snpdev->mode.CurrentAddress; |
---|
582 | } |
---|
583 | |
---|
584 | /* Allocate buffer */ |
---|
585 | iobuf = alloc_iob ( len ); |
---|
586 | if ( ! iobuf ) { |
---|
587 | DBGC ( snpdev, "SNPDEV %p TX could not allocate %ld-byte " |
---|
588 | "buffer\n", snpdev, ( ( unsigned long ) len ) ); |
---|
589 | efirc = EFI_DEVICE_ERROR; |
---|
590 | goto err_alloc_iob; |
---|
591 | } |
---|
592 | memcpy ( iob_put ( iobuf, len ), data, len ); |
---|
593 | |
---|
594 | /* Create link-layer header, if specified */ |
---|
595 | if ( ll_header_len ) { |
---|
596 | iob_pull ( iobuf, ll_header_len ); |
---|
597 | if ( ( rc = ll_protocol->push ( snpdev->netdev, |
---|
598 | iobuf, ll_dest, ll_src, |
---|
599 | htons ( *net_proto ) )) != 0 ){ |
---|
600 | DBGC ( snpdev, "SNPDEV %p TX could not construct " |
---|
601 | "header: %s\n", snpdev, strerror ( rc ) ); |
---|
602 | efirc = RC_TO_EFIRC ( rc ); |
---|
603 | goto err_ll_push; |
---|
604 | } |
---|
605 | } |
---|
606 | |
---|
607 | /* Transmit packet */ |
---|
608 | if ( ( rc = netdev_tx ( snpdev->netdev, iob_disown ( iobuf ) ) ) != 0){ |
---|
609 | DBGC ( snpdev, "SNPDEV %p TX could not transmit: %s\n", |
---|
610 | snpdev, strerror ( rc ) ); |
---|
611 | efirc = RC_TO_EFIRC ( rc ); |
---|
612 | goto err_tx; |
---|
613 | } |
---|
614 | |
---|
615 | /* Record transmission as outstanding */ |
---|
616 | snpdev->tx_count_interrupts++; |
---|
617 | snpdev->tx_count_txbufs++; |
---|
618 | |
---|
619 | return 0; |
---|
620 | |
---|
621 | err_tx: |
---|
622 | err_ll_push: |
---|
623 | free_iob ( iobuf ); |
---|
624 | err_alloc_iob: |
---|
625 | err_sanity: |
---|
626 | return efirc; |
---|
627 | } |
---|
628 | |
---|
629 | /** |
---|
630 | * Receive packet |
---|
631 | * |
---|
632 | * @v snp SNP interface |
---|
633 | * @v ll_header_len Link-layer header length, if to be filled in |
---|
634 | * @v len Length of data buffer |
---|
635 | * @v data Data buffer |
---|
636 | * @v ll_src Link-layer source address, if specified |
---|
637 | * @v ll_dest Link-layer destination address, if specified |
---|
638 | * @v net_proto Network-layer protocol (in host order) |
---|
639 | * @ret efirc EFI status code |
---|
640 | */ |
---|
641 | static EFI_STATUS EFIAPI |
---|
642 | efi_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, |
---|
643 | UINTN *ll_header_len, UINTN *len, VOID *data, |
---|
644 | EFI_MAC_ADDRESS *ll_src, EFI_MAC_ADDRESS *ll_dest, |
---|
645 | UINT16 *net_proto ) { |
---|
646 | struct efi_snp_device *snpdev = |
---|
647 | container_of ( snp, struct efi_snp_device, snp ); |
---|
648 | struct ll_protocol *ll_protocol = snpdev->netdev->ll_protocol; |
---|
649 | struct io_buffer *iobuf; |
---|
650 | const void *iob_ll_dest; |
---|
651 | const void *iob_ll_src; |
---|
652 | uint16_t iob_net_proto; |
---|
653 | int rc; |
---|
654 | EFI_STATUS efirc; |
---|
655 | |
---|
656 | DBGC2 ( snpdev, "SNPDEV %p RECEIVE %p(+%lx)", snpdev, data, |
---|
657 | ( ( unsigned long ) *len ) ); |
---|
658 | |
---|
659 | /* Poll the network device */ |
---|
660 | efi_snp_poll ( snpdev ); |
---|
661 | |
---|
662 | /* Dequeue a packet, if one is available */ |
---|
663 | iobuf = netdev_rx_dequeue ( snpdev->netdev ); |
---|
664 | if ( ! iobuf ) { |
---|
665 | DBGC2 ( snpdev, "\n" ); |
---|
666 | efirc = EFI_NOT_READY; |
---|
667 | goto out_no_packet; |
---|
668 | } |
---|
669 | DBGC2 ( snpdev, "+%zx\n", iob_len ( iobuf ) ); |
---|
670 | |
---|
671 | /* Return packet to caller */ |
---|
672 | memcpy ( data, iobuf->data, iob_len ( iobuf ) ); |
---|
673 | *len = iob_len ( iobuf ); |
---|
674 | |
---|
675 | /* Attempt to decode link-layer header */ |
---|
676 | if ( ( rc = ll_protocol->pull ( snpdev->netdev, iobuf, &iob_ll_dest, |
---|
677 | &iob_ll_src, &iob_net_proto ) ) != 0 ){ |
---|
678 | DBGC ( snpdev, "SNPDEV %p could not parse header: %s\n", |
---|
679 | snpdev, strerror ( rc ) ); |
---|
680 | efirc = RC_TO_EFIRC ( rc ); |
---|
681 | goto out_bad_ll_header; |
---|
682 | } |
---|
683 | |
---|
684 | /* Return link-layer header parameters to caller, if required */ |
---|
685 | if ( ll_header_len ) |
---|
686 | *ll_header_len = ll_protocol->ll_header_len; |
---|
687 | if ( ll_src ) |
---|
688 | memcpy ( ll_src, iob_ll_src, ll_protocol->ll_addr_len ); |
---|
689 | if ( ll_dest ) |
---|
690 | memcpy ( ll_dest, iob_ll_dest, ll_protocol->ll_addr_len ); |
---|
691 | if ( net_proto ) |
---|
692 | *net_proto = ntohs ( iob_net_proto ); |
---|
693 | |
---|
694 | efirc = 0; |
---|
695 | |
---|
696 | out_bad_ll_header: |
---|
697 | free_iob ( iobuf ); |
---|
698 | out_no_packet: |
---|
699 | return efirc; |
---|
700 | } |
---|
701 | |
---|
702 | /** |
---|
703 | * Poll event |
---|
704 | * |
---|
705 | * @v event Event |
---|
706 | * @v context Event context |
---|
707 | */ |
---|
708 | static VOID EFIAPI efi_snp_wait_for_packet ( EFI_EVENT event, |
---|
709 | VOID *context ) { |
---|
710 | EFI_BOOT_SERVICES *bs = efi_systab->BootServices; |
---|
711 | struct efi_snp_device *snpdev = context; |
---|
712 | |
---|
713 | DBGCP ( snpdev, "SNPDEV %p WAIT_FOR_PACKET\n", snpdev ); |
---|
714 | |
---|
715 | /* Do nothing unless the net device is open */ |
---|
716 | if ( ! ( snpdev->netdev->state & NETDEV_OPEN ) ) |
---|
717 | return; |
---|
718 | |
---|
719 | /* Poll the network device */ |
---|
720 | efi_snp_poll ( snpdev ); |
---|
721 | |
---|
722 | /* Fire event if packets have been received */ |
---|
723 | if ( snpdev->rx_count_events != 0 ) { |
---|
724 | DBGC2 ( snpdev, "SNPDEV %p firing WaitForPacket event\n", |
---|
725 | snpdev ); |
---|
726 | bs->SignalEvent ( event ); |
---|
727 | snpdev->rx_count_events--; |
---|
728 | } |
---|
729 | } |
---|
730 | |
---|
731 | /** SNP interface */ |
---|
732 | static EFI_SIMPLE_NETWORK_PROTOCOL efi_snp_device_snp = { |
---|
733 | .Revision = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION, |
---|
734 | .Start = efi_snp_start, |
---|
735 | .Stop = efi_snp_stop, |
---|
736 | .Initialize = efi_snp_initialize, |
---|
737 | .Reset = efi_snp_reset, |
---|
738 | .Shutdown = efi_snp_shutdown, |
---|
739 | .ReceiveFilters = efi_snp_receive_filters, |
---|
740 | .StationAddress = efi_snp_station_address, |
---|
741 | .Statistics = efi_snp_statistics, |
---|
742 | .MCastIpToMac = efi_snp_mcast_ip_to_mac, |
---|
743 | .NvData = efi_snp_nvdata, |
---|
744 | .GetStatus = efi_snp_get_status, |
---|
745 | .Transmit = efi_snp_transmit, |
---|
746 | .Receive = efi_snp_receive, |
---|
747 | }; |
---|
748 | |
---|
749 | /** |
---|
750 | * Locate net device corresponding to EFI device |
---|
751 | * |
---|
752 | * @v driver EFI driver |
---|
753 | * @v device EFI device |
---|
754 | * @ret netdev Net device, or NULL if not found |
---|
755 | */ |
---|
756 | static struct net_device * |
---|
757 | efi_snp_netdev ( EFI_DRIVER_BINDING_PROTOCOL *driver, EFI_HANDLE device ) { |
---|
758 | EFI_BOOT_SERVICES *bs = efi_systab->BootServices; |
---|
759 | union { |
---|
760 | EFI_PCI_IO_PROTOCOL *pci; |
---|
761 | void *interface; |
---|
762 | } u; |
---|
763 | UINTN pci_segment, pci_bus, pci_dev, pci_fn; |
---|
764 | unsigned int pci_busdevfn; |
---|
765 | struct net_device *netdev = NULL; |
---|
766 | EFI_STATUS efirc; |
---|
767 | |
---|
768 | /* See if device is a PCI device */ |
---|
769 | if ( ( efirc = bs->OpenProtocol ( device, |
---|
770 | &efi_pci_io_protocol_guid, |
---|
771 | &u.interface, |
---|
772 | driver->DriverBindingHandle, |
---|
773 | device, |
---|
774 | EFI_OPEN_PROTOCOL_BY_DRIVER )) !=0 ){ |
---|
775 | DBGCP ( driver, "SNPDRV %p device %p is not a PCI device\n", |
---|
776 | driver, device ); |
---|
777 | goto out_no_pci_io; |
---|
778 | } |
---|
779 | |
---|
780 | /* Get PCI bus:dev.fn address */ |
---|
781 | if ( ( efirc = u.pci->GetLocation ( u.pci, &pci_segment, &pci_bus, |
---|
782 | &pci_dev, &pci_fn ) ) != 0 ) { |
---|
783 | DBGC ( driver, "SNPDRV %p device %p could not get PCI " |
---|
784 | "location: %s\n", |
---|
785 | driver, device, efi_strerror ( efirc ) ); |
---|
786 | goto out_no_pci_location; |
---|
787 | } |
---|
788 | DBGCP ( driver, "SNPDRV %p device %p is PCI %04lx:%02lx:%02lx.%lx\n", |
---|
789 | driver, device, ( ( unsigned long ) pci_segment ), |
---|
790 | ( ( unsigned long ) pci_bus ), ( ( unsigned long ) pci_dev ), |
---|
791 | ( ( unsigned long ) pci_fn ) ); |
---|
792 | |
---|
793 | /* Look up corresponding network device */ |
---|
794 | pci_busdevfn = PCI_BUSDEVFN ( pci_bus, PCI_DEVFN ( pci_dev, pci_fn ) ); |
---|
795 | if ( ( netdev = find_netdev_by_location ( BUS_TYPE_PCI, |
---|
796 | pci_busdevfn ) ) == NULL ) { |
---|
797 | DBGCP ( driver, "SNPDRV %p device %p is not a gPXE network " |
---|
798 | "device\n", driver, device ); |
---|
799 | goto out_no_netdev; |
---|
800 | } |
---|
801 | DBGC ( driver, "SNPDRV %p device %p is %s\n", |
---|
802 | driver, device, netdev->name ); |
---|
803 | |
---|
804 | out_no_netdev: |
---|
805 | out_no_pci_location: |
---|
806 | bs->CloseProtocol ( device, &efi_pci_io_protocol_guid, |
---|
807 | driver->DriverBindingHandle, device ); |
---|
808 | out_no_pci_io: |
---|
809 | return netdev; |
---|
810 | } |
---|
811 | |
---|
812 | /** |
---|
813 | * Locate SNP corresponding to EFI device |
---|
814 | * |
---|
815 | * @v driver EFI driver |
---|
816 | * @v device EFI device |
---|
817 | * @ret snp EFI SNP, or NULL if not found |
---|
818 | */ |
---|
819 | static struct efi_snp_device * |
---|
820 | efi_snp_snpdev ( EFI_DRIVER_BINDING_PROTOCOL *driver, EFI_HANDLE device ) { |
---|
821 | EFI_BOOT_SERVICES *bs = efi_systab->BootServices; |
---|
822 | union { |
---|
823 | EFI_SIMPLE_NETWORK_PROTOCOL *snp; |
---|
824 | void *interface; |
---|
825 | } u; |
---|
826 | struct efi_snp_device *snpdev = NULL; |
---|
827 | EFI_STATUS efirc; |
---|
828 | |
---|
829 | if ( ( efirc = bs->OpenProtocol ( device, |
---|
830 | &efi_simple_network_protocol_guid, |
---|
831 | &u.interface, |
---|
832 | driver->DriverBindingHandle, |
---|
833 | device, |
---|
834 | EFI_OPEN_PROTOCOL_GET_PROTOCOL))!=0){ |
---|
835 | DBGC ( driver, "SNPDRV %p device %p could not locate SNP: " |
---|
836 | "%s\n", driver, device, efi_strerror ( efirc ) ); |
---|
837 | goto err_no_snp; |
---|
838 | } |
---|
839 | |
---|
840 | snpdev = container_of ( u.snp, struct efi_snp_device, snp ); |
---|
841 | DBGCP ( driver, "SNPDRV %p device %p is SNPDEV %p\n", |
---|
842 | driver, device, snpdev ); |
---|
843 | |
---|
844 | bs->CloseProtocol ( device, &efi_simple_network_protocol_guid, |
---|
845 | driver->DriverBindingHandle, device ); |
---|
846 | err_no_snp: |
---|
847 | return snpdev; |
---|
848 | } |
---|
849 | |
---|
850 | /** |
---|
851 | * Check to see if driver supports a device |
---|
852 | * |
---|
853 | * @v driver EFI driver |
---|
854 | * @v device EFI device |
---|
855 | * @v child Path to child device, if any |
---|
856 | * @ret efirc EFI status code |
---|
857 | */ |
---|
858 | static EFI_STATUS EFIAPI |
---|
859 | efi_snp_driver_supported ( EFI_DRIVER_BINDING_PROTOCOL *driver, |
---|
860 | EFI_HANDLE device, |
---|
861 | EFI_DEVICE_PATH_PROTOCOL *child ) { |
---|
862 | struct net_device *netdev; |
---|
863 | |
---|
864 | DBGCP ( driver, "SNPDRV %p DRIVER_SUPPORTED %p (%p)\n", |
---|
865 | driver, device, child ); |
---|
866 | |
---|
867 | netdev = efi_snp_netdev ( driver, device ); |
---|
868 | return ( netdev ? 0 : EFI_UNSUPPORTED ); |
---|
869 | } |
---|
870 | |
---|
871 | /** |
---|
872 | * Attach driver to device |
---|
873 | * |
---|
874 | * @v driver EFI driver |
---|
875 | * @v device EFI device |
---|
876 | * @v child Path to child device, if any |
---|
877 | * @ret efirc EFI status code |
---|
878 | */ |
---|
879 | static EFI_STATUS EFIAPI |
---|
880 | efi_snp_driver_start ( EFI_DRIVER_BINDING_PROTOCOL *driver, |
---|
881 | EFI_HANDLE device, |
---|
882 | EFI_DEVICE_PATH_PROTOCOL *child ) { |
---|
883 | EFI_BOOT_SERVICES *bs = efi_systab->BootServices; |
---|
884 | EFI_DEVICE_PATH_PROTOCOL *path; |
---|
885 | EFI_DEVICE_PATH_PROTOCOL *subpath; |
---|
886 | MAC_ADDR_DEVICE_PATH *macpath; |
---|
887 | struct efi_snp_device *snpdev; |
---|
888 | struct net_device *netdev; |
---|
889 | size_t subpath_len; |
---|
890 | size_t path_prefix_len = 0; |
---|
891 | unsigned int i; |
---|
892 | EFI_STATUS efirc; |
---|
893 | |
---|
894 | DBGCP ( driver, "SNPDRV %p DRIVER_START %p (%p)\n", |
---|
895 | driver, device, child ); |
---|
896 | |
---|
897 | /* Determine device path prefix length */ |
---|
898 | if ( ( efirc = bs->OpenProtocol ( device, |
---|
899 | &efi_device_path_protocol_guid, |
---|
900 | ( void * ) &path, |
---|
901 | driver->DriverBindingHandle, |
---|
902 | device, |
---|
903 | EFI_OPEN_PROTOCOL_BY_DRIVER )) !=0 ){ |
---|
904 | DBGCP ( driver, "SNPDRV %p device %p has no device path\n", |
---|
905 | driver, device ); |
---|
906 | goto err_no_device_path; |
---|
907 | } |
---|
908 | subpath = path; |
---|
909 | while ( subpath->Type != END_DEVICE_PATH_TYPE ) { |
---|
910 | subpath_len = ( ( subpath->Length[1] << 8 ) | |
---|
911 | subpath->Length[0] ); |
---|
912 | path_prefix_len += subpath_len; |
---|
913 | subpath = ( ( ( void * ) subpath ) + subpath_len ); |
---|
914 | } |
---|
915 | |
---|
916 | /* Allocate the SNP device */ |
---|
917 | snpdev = zalloc ( sizeof ( *snpdev ) + path_prefix_len + |
---|
918 | sizeof ( *macpath ) ); |
---|
919 | if ( ! snpdev ) { |
---|
920 | efirc = EFI_OUT_OF_RESOURCES; |
---|
921 | goto err_alloc_snp; |
---|
922 | } |
---|
923 | |
---|
924 | /* Identify the net device */ |
---|
925 | netdev = efi_snp_netdev ( driver, device ); |
---|
926 | if ( ! netdev ) { |
---|
927 | DBGC ( snpdev, "SNPDEV %p cannot find netdev for device %p\n", |
---|
928 | snpdev, device ); |
---|
929 | efirc = EFI_UNSUPPORTED; |
---|
930 | goto err_no_netdev; |
---|
931 | } |
---|
932 | snpdev->netdev = netdev_get ( netdev ); |
---|
933 | |
---|
934 | /* Sanity check */ |
---|
935 | if ( netdev->ll_protocol->ll_addr_len > sizeof ( EFI_MAC_ADDRESS ) ) { |
---|
936 | DBGC ( snpdev, "SNPDEV %p cannot support link-layer address " |
---|
937 | "length %d for %s\n", snpdev, |
---|
938 | netdev->ll_protocol->ll_addr_len, netdev->name ); |
---|
939 | efirc = EFI_INVALID_PARAMETER; |
---|
940 | goto err_ll_addr_len; |
---|
941 | } |
---|
942 | |
---|
943 | /* Populate the SNP structure */ |
---|
944 | memcpy ( &snpdev->snp, &efi_snp_device_snp, sizeof ( snpdev->snp ) ); |
---|
945 | snpdev->snp.Mode = &snpdev->mode; |
---|
946 | if ( ( efirc = bs->CreateEvent ( EVT_NOTIFY_WAIT, TPL_NOTIFY, |
---|
947 | efi_snp_wait_for_packet, snpdev, |
---|
948 | &snpdev->snp.WaitForPacket ) ) != 0 ){ |
---|
949 | DBGC ( snpdev, "SNPDEV %p could not create event: %s\n", |
---|
950 | snpdev, efi_strerror ( efirc ) ); |
---|
951 | goto err_create_event; |
---|
952 | } |
---|
953 | |
---|
954 | /* Populate the SNP mode structure */ |
---|
955 | snpdev->mode.State = EfiSimpleNetworkStopped; |
---|
956 | efi_snp_set_mode ( snpdev ); |
---|
957 | |
---|
958 | /* Populate the NII structure */ |
---|
959 | snpdev->nii.Revision = |
---|
960 | EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL_REVISION; |
---|
961 | strncpy ( snpdev->nii.StringId, "gPXE", |
---|
962 | sizeof ( snpdev->nii.StringId ) ); |
---|
963 | |
---|
964 | /* Populate the device name */ |
---|
965 | for ( i = 0 ; i < sizeof ( netdev->name ) ; i++ ) { |
---|
966 | /* Damn Unicode names */ |
---|
967 | assert ( i < ( sizeof ( snpdev->name ) / |
---|
968 | sizeof ( snpdev->name[0] ) ) ); |
---|
969 | snpdev->name[i] = netdev->name[i]; |
---|
970 | } |
---|
971 | |
---|
972 | /* Populate the device path */ |
---|
973 | memcpy ( &snpdev->path, path, path_prefix_len ); |
---|
974 | macpath = ( ( ( void * ) &snpdev->path ) + path_prefix_len ); |
---|
975 | subpath = ( ( void * ) ( macpath + 1 ) ); |
---|
976 | memset ( macpath, 0, sizeof ( *macpath ) ); |
---|
977 | macpath->Header.Type = MESSAGING_DEVICE_PATH; |
---|
978 | macpath->Header.SubType = MSG_MAC_ADDR_DP; |
---|
979 | macpath->Header.Length[0] = sizeof ( *macpath ); |
---|
980 | memcpy ( &macpath->MacAddress, netdev->ll_addr, |
---|
981 | sizeof ( macpath->MacAddress ) ); |
---|
982 | macpath->IfType = ntohs ( netdev->ll_protocol->ll_proto ); |
---|
983 | memset ( subpath, 0, sizeof ( *subpath ) ); |
---|
984 | subpath->Type = END_DEVICE_PATH_TYPE; |
---|
985 | subpath->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; |
---|
986 | subpath->Length[0] = sizeof ( *subpath ); |
---|
987 | |
---|
988 | /* Install the SNP */ |
---|
989 | if ( ( efirc = bs->InstallMultipleProtocolInterfaces ( |
---|
990 | &snpdev->handle, |
---|
991 | &efi_simple_network_protocol_guid, &snpdev->snp, |
---|
992 | &efi_device_path_protocol_guid, &snpdev->path, |
---|
993 | &efi_nii_protocol_guid, &snpdev->nii, |
---|
994 | &efi_nii31_protocol_guid, &snpdev->nii, |
---|
995 | NULL ) ) != 0 ) { |
---|
996 | DBGC ( snpdev, "SNPDEV %p could not install protocols: " |
---|
997 | "%s\n", snpdev, efi_strerror ( efirc ) ); |
---|
998 | goto err_install_protocol_interface; |
---|
999 | } |
---|
1000 | |
---|
1001 | DBGC ( snpdev, "SNPDEV %p installed for %s as device %p\n", |
---|
1002 | snpdev, netdev->name, snpdev->handle ); |
---|
1003 | return 0; |
---|
1004 | |
---|
1005 | bs->UninstallMultipleProtocolInterfaces ( |
---|
1006 | snpdev->handle, |
---|
1007 | &efi_simple_network_protocol_guid, &snpdev->snp, |
---|
1008 | &efi_device_path_protocol_guid, &snpdev->path, |
---|
1009 | &efi_nii_protocol_guid, &snpdev->nii, |
---|
1010 | &efi_nii31_protocol_guid, &snpdev->nii, |
---|
1011 | NULL ); |
---|
1012 | err_install_protocol_interface: |
---|
1013 | bs->CloseEvent ( snpdev->snp.WaitForPacket ); |
---|
1014 | err_create_event: |
---|
1015 | err_ll_addr_len: |
---|
1016 | netdev_put ( netdev ); |
---|
1017 | err_no_netdev: |
---|
1018 | free ( snpdev ); |
---|
1019 | err_alloc_snp: |
---|
1020 | bs->CloseProtocol ( device, &efi_device_path_protocol_guid, |
---|
1021 | driver->DriverBindingHandle, device ); |
---|
1022 | err_no_device_path: |
---|
1023 | return efirc; |
---|
1024 | } |
---|
1025 | |
---|
1026 | /** |
---|
1027 | * Detach driver from device |
---|
1028 | * |
---|
1029 | * @v driver EFI driver |
---|
1030 | * @v device EFI device |
---|
1031 | * @v num_children Number of child devices |
---|
1032 | * @v children List of child devices |
---|
1033 | * @ret efirc EFI status code |
---|
1034 | */ |
---|
1035 | static EFI_STATUS EFIAPI |
---|
1036 | efi_snp_driver_stop ( EFI_DRIVER_BINDING_PROTOCOL *driver, |
---|
1037 | EFI_HANDLE device, |
---|
1038 | UINTN num_children, |
---|
1039 | EFI_HANDLE *children ) { |
---|
1040 | EFI_BOOT_SERVICES *bs = efi_systab->BootServices; |
---|
1041 | struct efi_snp_device *snpdev; |
---|
1042 | |
---|
1043 | DBGCP ( driver, "SNPDRV %p DRIVER_STOP %p (%ld %p)\n", |
---|
1044 | driver, device, ( ( unsigned long ) num_children ), children ); |
---|
1045 | |
---|
1046 | /* Locate SNP device */ |
---|
1047 | snpdev = efi_snp_snpdev ( driver, device ); |
---|
1048 | if ( ! snpdev ) { |
---|
1049 | DBGC ( driver, "SNPDRV %p device %p could not find SNPDEV\n", |
---|
1050 | driver, device ); |
---|
1051 | return EFI_DEVICE_ERROR; |
---|
1052 | } |
---|
1053 | |
---|
1054 | /* Uninstall the SNP */ |
---|
1055 | bs->UninstallMultipleProtocolInterfaces ( |
---|
1056 | snpdev->handle, |
---|
1057 | &efi_simple_network_protocol_guid, &snpdev->snp, |
---|
1058 | &efi_device_path_protocol_guid, &snpdev->path, |
---|
1059 | &efi_nii_protocol_guid, &snpdev->nii, |
---|
1060 | &efi_nii31_protocol_guid, &snpdev->nii, |
---|
1061 | NULL ); |
---|
1062 | bs->CloseEvent ( snpdev->snp.WaitForPacket ); |
---|
1063 | netdev_put ( snpdev->netdev ); |
---|
1064 | free ( snpdev ); |
---|
1065 | bs->CloseProtocol ( device, &efi_device_path_protocol_guid, |
---|
1066 | driver->DriverBindingHandle, device ); |
---|
1067 | return 0; |
---|
1068 | } |
---|
1069 | |
---|
1070 | /** EFI SNP driver binding */ |
---|
1071 | static EFI_DRIVER_BINDING_PROTOCOL efi_snp_binding = { |
---|
1072 | efi_snp_driver_supported, |
---|
1073 | efi_snp_driver_start, |
---|
1074 | efi_snp_driver_stop, |
---|
1075 | 0x10, |
---|
1076 | NULL, |
---|
1077 | NULL |
---|
1078 | }; |
---|
1079 | |
---|
1080 | /** |
---|
1081 | * Look up driver name |
---|
1082 | * |
---|
1083 | * @v wtf Component name protocol |
---|
1084 | * @v language Language to use |
---|
1085 | * @v driver_name Driver name to fill in |
---|
1086 | * @ret efirc EFI status code |
---|
1087 | */ |
---|
1088 | static EFI_STATUS EFIAPI |
---|
1089 | efi_snp_get_driver_name ( EFI_COMPONENT_NAME2_PROTOCOL *wtf __unused, |
---|
1090 | CHAR8 *language __unused, CHAR16 **driver_name ) { |
---|
1091 | |
---|
1092 | *driver_name = L"" PRODUCT_SHORT_NAME " Driver"; |
---|
1093 | return 0; |
---|
1094 | } |
---|
1095 | |
---|
1096 | /** |
---|
1097 | * Look up controller name |
---|
1098 | * |
---|
1099 | * @v wtf Component name protocol |
---|
1100 | * @v device Device |
---|
1101 | * @v child Child device, or NULL |
---|
1102 | * @v language Language to use |
---|
1103 | * @v driver_name Device name to fill in |
---|
1104 | * @ret efirc EFI status code |
---|
1105 | */ |
---|
1106 | static EFI_STATUS EFIAPI |
---|
1107 | efi_snp_get_controller_name ( EFI_COMPONENT_NAME2_PROTOCOL *wtf __unused, |
---|
1108 | EFI_HANDLE device __unused, |
---|
1109 | EFI_HANDLE child __unused, |
---|
1110 | CHAR8 *language __unused, |
---|
1111 | CHAR16 **controller_name __unused ) { |
---|
1112 | |
---|
1113 | /* Just let EFI use the default Device Path Name */ |
---|
1114 | return EFI_UNSUPPORTED; |
---|
1115 | } |
---|
1116 | |
---|
1117 | /** EFI SNP component name protocol */ |
---|
1118 | static EFI_COMPONENT_NAME2_PROTOCOL efi_snp_name = { |
---|
1119 | efi_snp_get_driver_name, |
---|
1120 | efi_snp_get_controller_name, |
---|
1121 | "en" |
---|
1122 | }; |
---|
1123 | |
---|
1124 | /** |
---|
1125 | * Install EFI SNP driver |
---|
1126 | * |
---|
1127 | * @ret rc Return status code |
---|
1128 | */ |
---|
1129 | int efi_snp_install ( void ) { |
---|
1130 | EFI_BOOT_SERVICES *bs = efi_systab->BootServices; |
---|
1131 | EFI_DRIVER_BINDING_PROTOCOL *driver = &efi_snp_binding; |
---|
1132 | EFI_STATUS efirc; |
---|
1133 | |
---|
1134 | driver->ImageHandle = efi_image_handle; |
---|
1135 | if ( ( efirc = bs->InstallMultipleProtocolInterfaces ( |
---|
1136 | &driver->DriverBindingHandle, |
---|
1137 | &efi_driver_binding_protocol_guid, driver, |
---|
1138 | &efi_component_name2_protocol_guid, &efi_snp_name, |
---|
1139 | NULL ) ) != 0 ) { |
---|
1140 | DBGC ( driver, "SNPDRV %p could not install protocols: " |
---|
1141 | "%s\n", driver, efi_strerror ( efirc ) ); |
---|
1142 | return EFIRC_TO_RC ( efirc ); |
---|
1143 | } |
---|
1144 | |
---|
1145 | DBGC ( driver, "SNPDRV %p driver binding installed as %p\n", |
---|
1146 | driver, driver->DriverBindingHandle ); |
---|
1147 | return 0; |
---|
1148 | } |
---|