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