]> git.proxmox.com Git - mirror_edk2.git/blob - EmulatorPkg/Unix/Host/BerkeleyPacketFilter.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / EmulatorPkg / Unix / Host / BerkeleyPacketFilter.c
1 /**@file
2 Berkeley Packet Filter implementation of the EMU_SNP_PROTOCOL that allows the
3 emulator to get on real networks.
4
5 Tested on Mac OS X.
6
7 Copyright (c) 2004 - 2019, Intel Corporation. All rights reserved.<BR>
8 Portions copyright (c) 2011, Apple Inc. All rights reserved.
9
10 SPDX-License-Identifier: BSD-2-Clause-Patent
11
12 **/
13
14 #include "Host.h"
15
16 #ifdef __APPLE__
17
18 #include <Library/NetLib.h>
19
20 #define EMU_SNP_PRIVATE_SIGNATURE SIGNATURE_32('E', 'M', 's', 'n')
21 typedef struct {
22 UINTN Signature;
23
24 EMU_IO_THUNK_PROTOCOL *Thunk;
25 EMU_SNP_PROTOCOL EmuSnp;
26 EFI_SIMPLE_NETWORK_MODE *Mode;
27
28 int BpfFd;
29 char *InterfaceName;
30 EFI_MAC_ADDRESS MacAddress;
31 u_int ReadBufferSize;
32 VOID *ReadBuffer;
33
34 //
35 // Two walking pointers to manage the multiple packets that can be returned
36 // in a single read.
37 //
38 VOID *CurrentReadPointer;
39 VOID *EndReadPointer;
40
41 UINT32 ReceivedPackets;
42 UINT32 DroppedPackets;
43 } EMU_SNP_PRIVATE;
44
45 #define EMU_SNP_PRIVATE_DATA_FROM_THIS(a) \
46 CR(a, EMU_SNP_PRIVATE, EmuSnp, EMU_SNP_PRIVATE_SIGNATURE)
47
48 //
49 // Strange, but there doesn't appear to be any structure for the Ethernet header in edk2...
50 //
51
52 typedef struct {
53 UINT8 DstAddr[NET_ETHER_ADDR_LEN];
54 UINT8 SrcAddr[NET_ETHER_ADDR_LEN];
55 UINT16 Type;
56 } ETHERNET_HEADER;
57
58 /**
59 Register storage for SNP Mode.
60
61 @param This Protocol instance pointer.
62 @param Mode SimpleNetworkProtocol Mode structure passed into driver.
63
64 @retval EFI_SUCCESS The network interface was started.
65 @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
66
67 **/
68 EFI_STATUS
69 EmuSnpCreateMapping (
70 IN EMU_SNP_PROTOCOL *This,
71 IN EFI_SIMPLE_NETWORK_MODE *Mode
72 )
73 {
74 EMU_SNP_PRIVATE *Private;
75
76 Private = EMU_SNP_PRIVATE_DATA_FROM_THIS (This);
77
78 Private->Mode = Mode;
79
80 //
81 // Set the broadcast address.
82 //
83 SetMem (&Mode->BroadcastAddress, sizeof (EFI_MAC_ADDRESS), 0xFF);
84
85 CopyMem (&Mode->CurrentAddress, &Private->MacAddress, sizeof (EFI_MAC_ADDRESS));
86 CopyMem (&Mode->PermanentAddress, &Private->MacAddress, sizeof (EFI_MAC_ADDRESS));
87
88 //
89 // Since the fake SNP is based on a real NIC, to avoid conflict with the host NIC
90 // network stack, we use a different MAC address.
91 // So just change the last byte of the MAC address for the real NIC.
92 //
93 Mode->CurrentAddress.Addr[NET_ETHER_ADDR_LEN - 1]++;
94
95 return EFI_SUCCESS;
96 }
97
98 static struct bpf_insn mFilterInstructionTemplate[] = {
99 // Load 4 bytes from the destination MAC address.
100 BPF_STMT (BPF_LD + BPF_W + BPF_ABS, OFFSET_OF (ETHERNET_HEADER, DstAddr[0])),
101
102 // Compare to first 4 bytes of fake MAC address.
103 BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, 0x12345678, 0, 3),
104
105 // Load remaining 2 bytes from the destination MAC address.
106 BPF_STMT (BPF_LD + BPF_H + BPF_ABS, OFFSET_OF (ETHERNET_HEADER, DstAddr[4])),
107
108 // Compare to remaining 2 bytes of fake MAC address.
109 BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, 0x9ABC, 5, 0),
110
111 // Load 4 bytes from the destination MAC address.
112 BPF_STMT (BPF_LD + BPF_W + BPF_ABS, OFFSET_OF (ETHERNET_HEADER, DstAddr[0])),
113
114 // Compare to first 4 bytes of broadcast MAC address.
115 BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, 0xFFFFFFFF, 0, 2),
116
117 // Load remaining 2 bytes from the destination MAC address.
118 BPF_STMT (BPF_LD + BPF_H + BPF_ABS, OFFSET_OF (ETHERNET_HEADER, DstAddr[4])),
119
120 // Compare to remaining 2 bytes of broadcast MAC address.
121 BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, 0xFFFF, 1, 0),
122
123 // Reject packet.
124 BPF_STMT (BPF_RET + BPF_K, 0),
125
126 // Receive entire packet.
127 BPF_STMT (BPF_RET + BPF_K, -1)
128 };
129
130 EFI_STATUS
131 OpenBpfFileDescriptor (
132 IN EMU_SNP_PRIVATE *Private,
133 OUT int *Fd
134 )
135 {
136 char BfpDeviceName[256];
137 int Index;
138
139 //
140 // Open a Berkeley Packet Filter device. This must be done as root, so this is probably
141 // the place which is most likely to fail...
142 //
143 for (Index = 0; TRUE; Index++ ) {
144 snprintf (BfpDeviceName, sizeof (BfpDeviceName), "/dev/bpf%d", Index);
145
146 *Fd = open (BfpDeviceName, O_RDWR, 0);
147 if ( *Fd >= 0 ) {
148 return EFI_SUCCESS;
149 }
150
151 if (errno == EACCES) {
152 printf (
153 "SNP: Permissions on '%s' are incorrect. Fix with 'sudo chmod 666 %s'.\n",
154 BfpDeviceName,
155 BfpDeviceName
156 );
157 }
158
159 if (errno != EBUSY) {
160 break;
161 }
162 }
163
164 return EFI_OUT_OF_RESOURCES;
165 }
166
167 /**
168 Changes the state of a network interface from "stopped" to "started".
169
170 @param This Protocol instance pointer.
171
172 @retval EFI_SUCCESS The network interface was started.
173 @retval EFI_ALREADY_STARTED The network interface is already in the started state.
174 @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
175 @retval EFI_DEVICE_ERROR The command could not be sent to the network interface.
176 @retval EFI_UNSUPPORTED This function is not supported by the network interface.
177
178 **/
179 EFI_STATUS
180 EmuSnpStart (
181 IN EMU_SNP_PROTOCOL *This
182 )
183 {
184 EFI_STATUS Status;
185 EMU_SNP_PRIVATE *Private;
186 struct ifreq BoundIf;
187 struct bpf_program BpfProgram;
188 struct bpf_insn *FilterProgram;
189 u_int Value;
190 u_int ReadBufferSize;
191 UINT16 Temp16;
192 UINT32 Temp32;
193
194 Private = EMU_SNP_PRIVATE_DATA_FROM_THIS (This);
195
196 switch (Private->Mode->State) {
197 case EfiSimpleNetworkStopped:
198 break;
199
200 case EfiSimpleNetworkStarted:
201 case EfiSimpleNetworkInitialized:
202 return EFI_ALREADY_STARTED;
203 break;
204
205 default:
206 return EFI_DEVICE_ERROR;
207 break;
208 }
209
210 Status = EFI_SUCCESS;
211 Private->ReadBuffer = NULL;
212 if (Private->BpfFd == 0) {
213 Status = OpenBpfFileDescriptor (Private, &Private->BpfFd);
214 if (EFI_ERROR (Status)) {
215 goto DeviceErrorExit;
216 }
217
218 //
219 // Get the read buffer size.
220 //
221 if (ioctl (Private->BpfFd, BIOCGBLEN, &ReadBufferSize) < 0) {
222 goto DeviceErrorExit;
223 }
224
225 //
226 // Default value from BIOCGBLEN is usually too small, so use a much larger size, if necessary.
227 //
228 if (ReadBufferSize < FixedPcdGet32 (PcdNetworkPacketFilterSize)) {
229 ReadBufferSize = FixedPcdGet32 (PcdNetworkPacketFilterSize);
230 if (ioctl (Private->BpfFd, BIOCSBLEN, &ReadBufferSize) < 0) {
231 goto DeviceErrorExit;
232 }
233 }
234
235 //
236 // Associate our interface with this BPF file descriptor.
237 //
238 AsciiStrCpyS (BoundIf.ifr_name, sizeof (BoundIf.ifr_name), Private->InterfaceName);
239 if (ioctl (Private->BpfFd, BIOCSETIF, &BoundIf) < 0) {
240 goto DeviceErrorExit;
241 }
242
243 //
244 // Enable immediate mode.
245 //
246 Value = 1;
247 if (ioctl (Private->BpfFd, BIOCIMMEDIATE, &Value) < 0) {
248 goto DeviceErrorExit;
249 }
250
251 //
252 // Enable non-blocking I/O.
253 //
254 if (fcntl (Private->BpfFd, F_GETFL, 0) == -1) {
255 goto DeviceErrorExit;
256 }
257
258 Value |= O_NONBLOCK;
259
260 if (fcntl (Private->BpfFd, F_SETFL, Value) == -1) {
261 goto DeviceErrorExit;
262 }
263
264 //
265 // Disable "header complete" flag. This means the supplied source MAC address is
266 // what goes on the wire.
267 //
268 Value = 1;
269 if (ioctl (Private->BpfFd, BIOCSHDRCMPLT, &Value) < 0) {
270 goto DeviceErrorExit;
271 }
272
273 //
274 // Allocate read buffer.
275 //
276 Private->ReadBufferSize = ReadBufferSize;
277 Private->ReadBuffer = malloc (Private->ReadBufferSize);
278 if (Private->ReadBuffer == NULL) {
279 goto ErrorExit;
280 }
281
282 Private->CurrentReadPointer = Private->EndReadPointer = Private->ReadBuffer;
283
284 //
285 // Install our packet filter: successful reads should only produce broadcast or unicast
286 // packets directed to our fake MAC address.
287 //
288 FilterProgram = malloc (sizeof (mFilterInstructionTemplate));
289 if ( FilterProgram == NULL ) {
290 goto ErrorExit;
291 }
292
293 CopyMem (FilterProgram, &mFilterInstructionTemplate, sizeof (mFilterInstructionTemplate));
294
295 //
296 // Insert out fake MAC address into the filter. The data has to be host endian.
297 //
298 CopyMem (&Temp32, &Private->Mode->CurrentAddress.Addr[0], sizeof (UINT32));
299 FilterProgram[1].k = NTOHL (Temp32);
300 CopyMem (&Temp16, &Private->Mode->CurrentAddress.Addr[4], sizeof (UINT16));
301 FilterProgram[3].k = NTOHS (Temp16);
302
303 BpfProgram.bf_len = sizeof (mFilterInstructionTemplate) / sizeof (struct bpf_insn);
304 BpfProgram.bf_insns = FilterProgram;
305
306 if (ioctl (Private->BpfFd, BIOCSETF, &BpfProgram) < 0) {
307 goto DeviceErrorExit;
308 }
309
310 free (FilterProgram);
311
312 //
313 // Enable promiscuous mode.
314 //
315 if (ioctl (Private->BpfFd, BIOCPROMISC, 0) < 0) {
316 goto DeviceErrorExit;
317 }
318
319 Private->Mode->State = EfiSimpleNetworkStarted;
320 }
321
322 return Status;
323
324 DeviceErrorExit:
325 Status = EFI_DEVICE_ERROR;
326 ErrorExit:
327 if (Private->ReadBuffer != NULL) {
328 free (Private->ReadBuffer);
329 Private->ReadBuffer = NULL;
330 }
331
332 return Status;
333 }
334
335 /**
336 Changes the state of a network interface from "started" to "stopped".
337
338 @param This Protocol instance pointer.
339
340 @retval EFI_SUCCESS The network interface was stopped.
341 @retval EFI_ALREADY_STARTED The network interface is already in the stopped state.
342 @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
343 @retval EFI_DEVICE_ERROR The command could not be sent to the network interface.
344 @retval EFI_UNSUPPORTED This function is not supported by the network interface.
345
346 **/
347 EFI_STATUS
348 EmuSnpStop (
349 IN EMU_SNP_PROTOCOL *This
350 )
351 {
352 EMU_SNP_PRIVATE *Private;
353
354 Private = EMU_SNP_PRIVATE_DATA_FROM_THIS (This);
355
356 switch ( Private->Mode->State ) {
357 case EfiSimpleNetworkStarted:
358 break;
359
360 case EfiSimpleNetworkStopped:
361 return EFI_NOT_STARTED;
362 break;
363
364 default:
365 return EFI_DEVICE_ERROR;
366 break;
367 }
368
369 if (Private->BpfFd != 0) {
370 close (Private->BpfFd);
371 Private->BpfFd = 0;
372 }
373
374 if (Private->ReadBuffer != NULL) {
375 free (Private->ReadBuffer);
376 Private->CurrentReadPointer = Private->EndReadPointer = Private->ReadBuffer = NULL;
377 }
378
379 Private->Mode->State = EfiSimpleNetworkStopped;
380
381 return EFI_SUCCESS;
382 }
383
384 /**
385 Resets a network adapter and allocates the transmit and receive buffers
386 required by the network interface; optionally, also requests allocation
387 of additional transmit and receive buffers.
388
389 @param This The protocol instance pointer.
390 @param ExtraRxBufferSize The size, in bytes, of the extra receive buffer space
391 that the driver should allocate for the network interface.
392 Some network interfaces will not be able to use the extra
393 buffer, and the caller will not know if it is actually
394 being used.
395 @param ExtraTxBufferSize The size, in bytes, of the extra transmit buffer space
396 that the driver should allocate for the network interface.
397 Some network interfaces will not be able to use the extra
398 buffer, and the caller will not know if it is actually
399 being used.
400
401 @retval EFI_SUCCESS The network interface was initialized.
402 @retval EFI_NOT_STARTED The network interface has not been started.
403 @retval EFI_OUT_OF_RESOURCES There was not enough memory for the transmit and
404 receive buffers.
405 @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
406 @retval EFI_DEVICE_ERROR The command could not be sent to the network interface.
407 @retval EFI_UNSUPPORTED This function is not supported by the network interface.
408
409 **/
410 EFI_STATUS
411 EmuSnpInitialize (
412 IN EMU_SNP_PROTOCOL *This,
413 IN UINTN ExtraRxBufferSize OPTIONAL,
414 IN UINTN ExtraTxBufferSize OPTIONAL
415 )
416 {
417 EMU_SNP_PRIVATE *Private;
418
419 Private = EMU_SNP_PRIVATE_DATA_FROM_THIS (This);
420
421 switch ( Private->Mode->State ) {
422 case EfiSimpleNetworkStarted:
423 break;
424
425 case EfiSimpleNetworkStopped:
426 return EFI_NOT_STARTED;
427 break;
428
429 default:
430 return EFI_DEVICE_ERROR;
431 break;
432 }
433
434 Private->Mode->MCastFilterCount = 0;
435 Private->Mode->ReceiveFilterSetting = 0;
436 ZeroMem (Private->Mode->MCastFilter, sizeof (Private->Mode->MCastFilter));
437
438 Private->Mode->State = EfiSimpleNetworkInitialized;
439
440 return EFI_SUCCESS;
441 }
442
443 /**
444 Resets a network adapter and re-initializes it with the parameters that were
445 provided in the previous call to Initialize().
446
447 @param This The protocol instance pointer.
448 @param ExtendedVerification Indicates that the driver may perform a more
449 exhaustive verification operation of the device
450 during reset.
451
452 @retval EFI_SUCCESS The network interface was reset.
453 @retval EFI_NOT_STARTED The network interface has not been started.
454 @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
455 @retval EFI_DEVICE_ERROR The command could not be sent to the network interface.
456 @retval EFI_UNSUPPORTED This function is not supported by the network interface.
457
458 **/
459 EFI_STATUS
460 EmuSnpReset (
461 IN EMU_SNP_PROTOCOL *This,
462 IN BOOLEAN ExtendedVerification
463 )
464 {
465 EMU_SNP_PRIVATE *Private;
466
467 Private = EMU_SNP_PRIVATE_DATA_FROM_THIS (This);
468
469 switch ( Private->Mode->State ) {
470 case EfiSimpleNetworkInitialized:
471 break;
472
473 case EfiSimpleNetworkStopped:
474 return EFI_NOT_STARTED;
475 break;
476
477 default:
478 return EFI_DEVICE_ERROR;
479 break;
480 }
481
482 return EFI_SUCCESS;
483 }
484
485 /**
486 Resets a network adapter and leaves it in a state that is safe for
487 another driver to initialize.
488
489 @param This Protocol instance pointer.
490
491 @retval EFI_SUCCESS The network interface was shutdown.
492 @retval EFI_NOT_STARTED The network interface has not been started.
493 @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
494 @retval EFI_DEVICE_ERROR The command could not be sent to the network interface.
495 @retval EFI_UNSUPPORTED This function is not supported by the network interface.
496
497 **/
498 EFI_STATUS
499 EmuSnpShutdown (
500 IN EMU_SNP_PROTOCOL *This
501 )
502 {
503 EMU_SNP_PRIVATE *Private;
504
505 Private = EMU_SNP_PRIVATE_DATA_FROM_THIS (This);
506
507 switch ( Private->Mode->State ) {
508 case EfiSimpleNetworkInitialized:
509 break;
510
511 case EfiSimpleNetworkStopped:
512 return EFI_NOT_STARTED;
513 break;
514
515 default:
516 return EFI_DEVICE_ERROR;
517 break;
518 }
519
520 Private->Mode->State = EfiSimpleNetworkStarted;
521
522 Private->Mode->ReceiveFilterSetting = 0;
523 Private->Mode->MCastFilterCount = 0;
524 ZeroMem (Private->Mode->MCastFilter, sizeof (Private->Mode->MCastFilter));
525
526 if (Private->BpfFd != 0) {
527 close (Private->BpfFd);
528 Private->BpfFd = 0;
529 }
530
531 if (Private->ReadBuffer != NULL) {
532 free (Private->ReadBuffer);
533 Private->CurrentReadPointer = Private->EndReadPointer = Private->ReadBuffer = NULL;
534 }
535
536 return EFI_SUCCESS;
537 }
538
539 /**
540 Manages the multicast receive filters of a network interface.
541
542 @param This The protocol instance pointer.
543 @param Enable A bit mask of receive filters to enable on the network interface.
544 @param Disable A bit mask of receive filters to disable on the network interface.
545 @param ResetMCastFilter Set to TRUE to reset the contents of the multicast receive
546 filters on the network interface to their default values.
547 @param McastFilterCnt Number of multicast HW MAC addresses in the new
548 MCastFilter list. This value must be less than or equal to
549 the MCastFilterCnt field of EMU_SNP_MODE. This
550 field is optional if ResetMCastFilter is TRUE.
551 @param MCastFilter A pointer to a list of new multicast receive filter HW MAC
552 addresses. This list will replace any existing multicast
553 HW MAC address list. This field is optional if
554 ResetMCastFilter is TRUE.
555
556 @retval EFI_SUCCESS The multicast receive filter list was updated.
557 @retval EFI_NOT_STARTED The network interface has not been started.
558 @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
559 @retval EFI_DEVICE_ERROR The command could not be sent to the network interface.
560 @retval EFI_UNSUPPORTED This function is not supported by the network interface.
561
562 **/
563 EFI_STATUS
564 EmuSnpReceiveFilters (
565 IN EMU_SNP_PROTOCOL *This,
566 IN UINT32 Enable,
567 IN UINT32 Disable,
568 IN BOOLEAN ResetMCastFilter,
569 IN UINTN MCastFilterCnt OPTIONAL,
570 IN EFI_MAC_ADDRESS *MCastFilter OPTIONAL
571 )
572 {
573 EMU_SNP_PRIVATE *Private;
574
575 Private = EMU_SNP_PRIVATE_DATA_FROM_THIS (This);
576
577 // For now, just succeed...
578 return EFI_SUCCESS;
579 }
580
581 /**
582 Modifies or resets the current station address, if supported.
583
584 @param This The protocol instance pointer.
585 @param Reset Flag used to reset the station address to the network interfaces
586 permanent address.
587 @param New The new station address to be used for the network interface.
588
589 @retval EFI_SUCCESS The network interfaces station address was updated.
590 @retval EFI_NOT_STARTED The network interface has not been started.
591 @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
592 @retval EFI_DEVICE_ERROR The command could not be sent to the network interface.
593 @retval EFI_UNSUPPORTED This function is not supported by the network interface.
594
595 **/
596 EFI_STATUS
597 EmuSnpStationAddress (
598 IN EMU_SNP_PROTOCOL *This,
599 IN BOOLEAN Reset,
600 IN EFI_MAC_ADDRESS *New OPTIONAL
601 )
602 {
603 EMU_SNP_PRIVATE *Private;
604
605 Private = EMU_SNP_PRIVATE_DATA_FROM_THIS (This);
606
607 return EFI_UNSUPPORTED;
608 }
609
610 /**
611 Resets or collects the statistics on a network interface.
612
613 @param This Protocol instance pointer.
614 @param Reset Set to TRUE to reset the statistics for the network interface.
615 @param StatisticsSize On input the size, in bytes, of StatisticsTable. On
616 output the size, in bytes, of the resulting table of
617 statistics.
618 @param StatisticsTable A pointer to the EFI_NETWORK_STATISTICS structure that
619 contains the statistics.
620
621 @retval EFI_SUCCESS The statistics were collected from the network interface.
622 @retval EFI_NOT_STARTED The network interface has not been started.
623 @retval EFI_BUFFER_TOO_SMALL The Statistics buffer was too small. The current buffer
624 size needed to hold the statistics is returned in
625 StatisticsSize.
626 @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
627 @retval EFI_DEVICE_ERROR The command could not be sent to the network interface.
628 @retval EFI_UNSUPPORTED This function is not supported by the network interface.
629
630 **/
631 EFI_STATUS
632 EmuSnpStatistics (
633 IN EMU_SNP_PROTOCOL *This,
634 IN BOOLEAN Reset,
635 IN OUT UINTN *StatisticsSize OPTIONAL,
636 OUT EFI_NETWORK_STATISTICS *StatisticsTable OPTIONAL
637 )
638 {
639 EMU_SNP_PRIVATE *Private;
640
641 Private = EMU_SNP_PRIVATE_DATA_FROM_THIS (This);
642
643 return EFI_UNSUPPORTED;
644 }
645
646 /**
647 Converts a multicast IP address to a multicast HW MAC address.
648
649 @param This The protocol instance pointer.
650 @param IPv6 Set to TRUE if the multicast IP address is IPv6 [RFC 2460]. Set
651 to FALSE if the multicast IP address is IPv4 [RFC 791].
652 @param IP The multicast IP address that is to be converted to a multicast
653 HW MAC address.
654 @param MAC The multicast HW MAC address that is to be generated from IP.
655
656 @retval EFI_SUCCESS The multicast IP address was mapped to the multicast
657 HW MAC address.
658 @retval EFI_NOT_STARTED The network interface has not been started.
659 @retval EFI_BUFFER_TOO_SMALL The Statistics buffer was too small. The current buffer
660 size needed to hold the statistics is returned in
661 StatisticsSize.
662 @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
663 @retval EFI_DEVICE_ERROR The command could not be sent to the network interface.
664 @retval EFI_UNSUPPORTED This function is not supported by the network interface.
665
666 **/
667 EFI_STATUS
668 EmuSnpMCastIpToMac (
669 IN EMU_SNP_PROTOCOL *This,
670 IN BOOLEAN IPv6,
671 IN EFI_IP_ADDRESS *IP,
672 OUT EFI_MAC_ADDRESS *MAC
673 )
674 {
675 EMU_SNP_PRIVATE *Private;
676
677 Private = EMU_SNP_PRIVATE_DATA_FROM_THIS (This);
678
679 return EFI_UNSUPPORTED;
680 }
681
682 /**
683 Performs read and write operations on the NVRAM device attached to a
684 network interface.
685
686 @param This The protocol instance pointer.
687 @param ReadWrite TRUE for read operations, FALSE for write operations.
688 @param Offset Byte offset in the NVRAM device at which to start the read or
689 write operation. This must be a multiple of NvRamAccessSize and
690 less than NvRamSize.
691 @param BufferSize The number of bytes to read or write from the NVRAM device.
692 This must also be a multiple of NvramAccessSize.
693 @param Buffer A pointer to the data buffer.
694
695 @retval EFI_SUCCESS The NVRAM access was performed.
696 @retval EFI_NOT_STARTED The network interface has not been started.
697 @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
698 @retval EFI_DEVICE_ERROR The command could not be sent to the network interface.
699 @retval EFI_UNSUPPORTED This function is not supported by the network interface.
700
701 **/
702 EFI_STATUS
703 EmuSnpNvData (
704 IN EMU_SNP_PROTOCOL *This,
705 IN BOOLEAN ReadWrite,
706 IN UINTN Offset,
707 IN UINTN BufferSize,
708 IN OUT VOID *Buffer
709 )
710 {
711 EMU_SNP_PRIVATE *Private;
712
713 Private = EMU_SNP_PRIVATE_DATA_FROM_THIS (This);
714
715 return EFI_UNSUPPORTED;
716 }
717
718 /**
719 Reads the current interrupt status and recycled transmit buffer status from
720 a network interface.
721
722 @param This The protocol instance pointer.
723 @param InterruptStatus A pointer to the bit mask of the currently active interrupts
724 If this is NULL, the interrupt status will not be read from
725 the device. If this is not NULL, the interrupt status will
726 be read from the device. When the interrupt status is read,
727 it will also be cleared. Clearing the transmit interrupt
728 does not empty the recycled transmit buffer array.
729 @param TxBuf Recycled transmit buffer address. The network interface will
730 not transmit if its internal recycled transmit buffer array
731 is full. Reading the transmit buffer does not clear the
732 transmit interrupt. If this is NULL, then the transmit buffer
733 status will not be read. If there are no transmit buffers to
734 recycle and TxBuf is not NULL, * TxBuf will be set to NULL.
735
736 @retval EFI_SUCCESS The status of the network interface was retrieved.
737 @retval EFI_NOT_STARTED The network interface has not been started.
738 @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
739 @retval EFI_DEVICE_ERROR The command could not be sent to the network interface.
740 @retval EFI_UNSUPPORTED This function is not supported by the network interface.
741
742 **/
743 EFI_STATUS
744 EmuSnpGetStatus (
745 IN EMU_SNP_PROTOCOL *This,
746 OUT UINT32 *InterruptStatus OPTIONAL,
747 OUT VOID **TxBuf OPTIONAL
748 )
749 {
750 EMU_SNP_PRIVATE *Private;
751
752 Private = EMU_SNP_PRIVATE_DATA_FROM_THIS (This);
753
754 if ( InterruptStatus != NULL ) {
755 *InterruptStatus = EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT;
756 }
757
758 return EFI_SUCCESS;
759 }
760
761 /**
762 Places a packet in the transmit queue of a network interface.
763
764 @param This The protocol instance pointer.
765 @param HeaderSize The size, in bytes, of the media header to be filled in by
766 the Transmit() function. If HeaderSize is non-zero, then it
767 must be equal to This->Mode->MediaHeaderSize and the DestAddr
768 and Protocol parameters must not be NULL.
769 @param BufferSize The size, in bytes, of the entire packet (media header and
770 data) to be transmitted through the network interface.
771 @param Buffer A pointer to the packet (media header followed by data) to be
772 transmitted. This parameter cannot be NULL. If HeaderSize is zero,
773 then the media header in Buffer must already be filled in by the
774 caller. If HeaderSize is non-zero, then the media header will be
775 filled in by the Transmit() function.
776 @param SrcAddr The source HW MAC address. If HeaderSize is zero, then this parameter
777 is ignored. If HeaderSize is non-zero and SrcAddr is NULL, then
778 This->Mode->CurrentAddress is used for the source HW MAC address.
779 @param DestAddr The destination HW MAC address. If HeaderSize is zero, then this
780 parameter is ignored.
781 @param Protocol The type of header to build. If HeaderSize is zero, then this
782 parameter is ignored. See RFC 1700, section "Ether Types", for
783 examples.
784
785 @retval EFI_SUCCESS The packet was placed on the transmit queue.
786 @retval EFI_NOT_STARTED The network interface has not been started.
787 @retval EFI_NOT_READY The network interface is too busy to accept this transmit request.
788 @retval EFI_BUFFER_TOO_SMALL The BufferSize parameter is too small.
789 @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
790 @retval EFI_DEVICE_ERROR The command could not be sent to the network interface.
791 @retval EFI_UNSUPPORTED This function is not supported by the network interface.
792
793 **/
794 EFI_STATUS
795 EmuSnpTransmit (
796 IN EMU_SNP_PROTOCOL *This,
797 IN UINTN HeaderSize,
798 IN UINTN BufferSize,
799 IN VOID *Buffer,
800 IN EFI_MAC_ADDRESS *SrcAddr OPTIONAL,
801 IN EFI_MAC_ADDRESS *DestAddr OPTIONAL,
802 IN UINT16 *Protocol OPTIONAL
803 )
804 {
805 EMU_SNP_PRIVATE *Private;
806 ETHERNET_HEADER *EnetHeader;
807
808 Private = EMU_SNP_PRIVATE_DATA_FROM_THIS (This);
809
810 if (Private->Mode->State < EfiSimpleNetworkStarted) {
811 return EFI_NOT_STARTED;
812 }
813
814 if ( HeaderSize != 0 ) {
815 if ((DestAddr == NULL) || (Protocol == NULL) || (HeaderSize != Private->Mode->MediaHeaderSize)) {
816 return EFI_INVALID_PARAMETER;
817 }
818
819 if (SrcAddr == NULL) {
820 SrcAddr = &Private->Mode->CurrentAddress;
821 }
822
823 EnetHeader = (ETHERNET_HEADER *)Buffer;
824
825 CopyMem (EnetHeader->DstAddr, DestAddr, NET_ETHER_ADDR_LEN);
826 CopyMem (EnetHeader->SrcAddr, SrcAddr, NET_ETHER_ADDR_LEN);
827
828 EnetHeader->Type = HTONS (*Protocol);
829 }
830
831 if (write (Private->BpfFd, Buffer, BufferSize) < 0) {
832 return EFI_DEVICE_ERROR;
833 }
834
835 return EFI_SUCCESS;
836 }
837
838 /**
839 Receives a packet from a network interface.
840
841 @param This The protocol instance pointer.
842 @param HeaderSize The size, in bytes, of the media header received on the network
843 interface. If this parameter is NULL, then the media header size
844 will not be returned.
845 @param BufferSize On entry, the size, in bytes, of Buffer. On exit, the size, in
846 bytes, of the packet that was received on the network interface.
847 @param Buffer A pointer to the data buffer to receive both the media header and
848 the data.
849 @param SrcAddr The source HW MAC address. If this parameter is NULL, the
850 HW MAC source address will not be extracted from the media
851 header.
852 @param DestAddr The destination HW MAC address. If this parameter is NULL,
853 the HW MAC destination address will not be extracted from the
854 media header.
855 @param Protocol The media header type. If this parameter is NULL, then the
856 protocol will not be extracted from the media header. See
857 RFC 1700 section "Ether Types" for examples.
858
859 @retval EFI_SUCCESS The received data was stored in Buffer, and BufferSize has
860 been updated to the number of bytes received.
861 @retval EFI_NOT_STARTED The network interface has not been started.
862 @retval EFI_NOT_READY The network interface is too busy to accept this transmit
863 request.
864 @retval EFI_BUFFER_TOO_SMALL The BufferSize parameter is too small.
865 @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
866 @retval EFI_DEVICE_ERROR The command could not be sent to the network interface.
867 @retval EFI_UNSUPPORTED This function is not supported by the network interface.
868
869 **/
870 EFI_STATUS
871 EmuSnpReceive (
872 IN EMU_SNP_PROTOCOL *This,
873 OUT UINTN *HeaderSize OPTIONAL,
874 IN OUT UINTN *BufferSize,
875 OUT VOID *Buffer,
876 OUT EFI_MAC_ADDRESS *SrcAddr OPTIONAL,
877 OUT EFI_MAC_ADDRESS *DestAddr OPTIONAL,
878 OUT UINT16 *Protocol OPTIONAL
879 )
880 {
881 EMU_SNP_PRIVATE *Private;
882 struct bpf_hdr *BpfHeader;
883 struct bpf_stat BpfStats;
884 ETHERNET_HEADER *EnetHeader;
885 ssize_t Result;
886
887 Private = EMU_SNP_PRIVATE_DATA_FROM_THIS (This);
888
889 if (Private->Mode->State < EfiSimpleNetworkStarted) {
890 return EFI_NOT_STARTED;
891 }
892
893 ZeroMem (&BpfStats, sizeof (BpfStats));
894
895 if (ioctl (Private->BpfFd, BIOCGSTATS, &BpfStats) == 0) {
896 Private->ReceivedPackets += BpfStats.bs_recv;
897 if (BpfStats.bs_drop > Private->DroppedPackets) {
898 printf (
899 "SNP: STATS: RCVD = %d DROPPED = %d. Probably need to increase BPF PcdNetworkPacketFilterSize?\n",
900 BpfStats.bs_recv,
901 BpfStats.bs_drop - Private->DroppedPackets
902 );
903 Private->DroppedPackets = BpfStats.bs_drop;
904 }
905 }
906
907 //
908 // Do we have any remaining packets from the previous read?
909 //
910 if (Private->CurrentReadPointer >= Private->EndReadPointer) {
911 Result = read (Private->BpfFd, Private->ReadBuffer, Private->ReadBufferSize);
912 if (Result < 0) {
913 // EAGAIN means that there's no I/O outstanding against this file descriptor.
914 return (errno == EAGAIN) ? EFI_NOT_READY : EFI_DEVICE_ERROR;
915 }
916
917 if (Result == 0) {
918 return EFI_NOT_READY;
919 }
920
921 Private->CurrentReadPointer = Private->ReadBuffer;
922 Private->EndReadPointer = Private->CurrentReadPointer + Result;
923 }
924
925 BpfHeader = Private->CurrentReadPointer;
926 EnetHeader = Private->CurrentReadPointer + BpfHeader->bh_hdrlen;
927
928 if (BpfHeader->bh_caplen > *BufferSize) {
929 *BufferSize = BpfHeader->bh_caplen;
930 return EFI_BUFFER_TOO_SMALL;
931 }
932
933 CopyMem (Buffer, EnetHeader, BpfHeader->bh_caplen);
934 *BufferSize = BpfHeader->bh_caplen;
935
936 if (HeaderSize != NULL) {
937 *HeaderSize = sizeof (ETHERNET_HEADER);
938 }
939
940 if (DestAddr != NULL) {
941 ZeroMem (DestAddr, sizeof (EFI_MAC_ADDRESS));
942 CopyMem (DestAddr, EnetHeader->DstAddr, NET_ETHER_ADDR_LEN);
943 }
944
945 if (SrcAddr != NULL) {
946 ZeroMem (SrcAddr, sizeof (EFI_MAC_ADDRESS));
947 CopyMem (SrcAddr, EnetHeader->SrcAddr, NET_ETHER_ADDR_LEN);
948 }
949
950 if (Protocol != NULL) {
951 *Protocol = NTOHS (EnetHeader->Type);
952 }
953
954 Private->CurrentReadPointer += BPF_WORDALIGN (BpfHeader->bh_hdrlen + BpfHeader->bh_caplen);
955 return EFI_SUCCESS;
956 }
957
958 EMU_SNP_PROTOCOL gEmuSnpProtocol = {
959 GasketSnpCreateMapping,
960 GasketSnpStart,
961 GasketSnpStop,
962 GasketSnpInitialize,
963 GasketSnpReset,
964 GasketSnpShutdown,
965 GasketSnpReceiveFilters,
966 GasketSnpStationAddress,
967 GasketSnpStatistics,
968 GasketSnpMCastIpToMac,
969 GasketSnpNvData,
970 GasketSnpGetStatus,
971 GasketSnpTransmit,
972 GasketSnpReceive
973 };
974
975 EFI_STATUS
976 GetInterfaceMacAddr (
977 EMU_SNP_PRIVATE *Private
978 )
979 {
980 EFI_STATUS Status;
981 struct ifaddrs *IfAddrs;
982 struct ifaddrs *If;
983 struct sockaddr_dl *IfSdl;
984
985 if (getifaddrs (&IfAddrs) != 0) {
986 return EFI_UNSUPPORTED;
987 }
988
989 //
990 // Convert the interface name to ASCII so we can find it.
991 //
992 Private->InterfaceName = malloc (StrSize (Private->Thunk->ConfigString));
993 if (Private->InterfaceName == NULL) {
994 Status = EFI_OUT_OF_RESOURCES;
995 goto Exit;
996 }
997
998 UnicodeStrToAsciiStrS (
999 Private->Thunk->ConfigString,
1000 Private->InterfaceName,
1001 StrSize (Private->Thunk->ConfigString)
1002 );
1003
1004 Status = EFI_NOT_FOUND;
1005 If = IfAddrs;
1006 while (If != NULL) {
1007 IfSdl = (struct sockaddr_dl *)If->ifa_addr;
1008
1009 if (IfSdl->sdl_family == AF_LINK) {
1010 if (!AsciiStrCmp (Private->InterfaceName, If->ifa_name)) {
1011 CopyMem (&Private->MacAddress, LLADDR (IfSdl), NET_ETHER_ADDR_LEN);
1012
1013 Status = EFI_SUCCESS;
1014 break;
1015 }
1016 }
1017
1018 If = If->ifa_next;
1019 }
1020
1021 Exit:
1022 freeifaddrs (IfAddrs);
1023 return Status;
1024 }
1025
1026 EFI_STATUS
1027 EmuSnpThunkOpen (
1028 IN EMU_IO_THUNK_PROTOCOL *This
1029 )
1030 {
1031 EMU_SNP_PRIVATE *Private;
1032
1033 if (This->Private != NULL) {
1034 return EFI_ALREADY_STARTED;
1035 }
1036
1037 if (!CompareGuid (This->Protocol, &gEmuSnpProtocolGuid)) {
1038 return EFI_UNSUPPORTED;
1039 }
1040
1041 Private = malloc (sizeof (EMU_SNP_PRIVATE));
1042 if (Private == NULL) {
1043 return EFI_OUT_OF_RESOURCES;
1044 }
1045
1046 Private->Signature = EMU_SNP_PRIVATE_SIGNATURE;
1047 Private->Thunk = This;
1048 CopyMem (&Private->EmuSnp, &gEmuSnpProtocol, sizeof (gEmuSnpProtocol));
1049 GetInterfaceMacAddr (Private);
1050
1051 This->Interface = &Private->EmuSnp;
1052 This->Private = Private;
1053 return EFI_SUCCESS;
1054 }
1055
1056 EFI_STATUS
1057 EmuSnpThunkClose (
1058 IN EMU_IO_THUNK_PROTOCOL *This
1059 )
1060 {
1061 EMU_SNP_PRIVATE *Private;
1062
1063 if (!CompareGuid (This->Protocol, &gEmuSnpProtocolGuid)) {
1064 return EFI_UNSUPPORTED;
1065 }
1066
1067 Private = This->Private;
1068 free (Private);
1069
1070 return EFI_SUCCESS;
1071 }
1072
1073 EMU_IO_THUNK_PROTOCOL gSnpThunkIo = {
1074 &gEmuSnpProtocolGuid,
1075 NULL,
1076 NULL,
1077 0,
1078 GasketSnpThunkOpen,
1079 GasketSnpThunkClose,
1080 NULL
1081 };
1082
1083 #endif