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
7Copyright (c) 2004 - 2009, Intel Corporation. All rights reserved.<BR>
8Portitions copyright (c) 2011, Apple Inc. All rights reserved.
9
10This program and the accompanying materials
11are licensed and made available under the terms and conditions of the BSD License
12which accompanies this distribution.  The full text of the license may be found at
13http://opensource.org/licenses/bsd-license.php
14
15THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
16WITHOUT 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')
30typedef 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
63typedef 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**/
79EFI_STATUS
80EmuSnpCreateMapping (
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
110static 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
143EFI_STATUS
144OpenBpfFileDescriptor (
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**/
193EFI_STATUS
194EmuSnpStart (
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
338DeviceErrorExit:
339  Status = EFI_DEVICE_ERROR;
340ErrorExit:
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**/
361EFI_STATUS
362EmuSnpStop (
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**/
425EFI_STATUS
426EmuSnpInitialize (
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**/
475EFI_STATUS
476EmuSnpReset (
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**/
515EFI_STATUS
516EmuSnpShutdown (
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**/
580EFI_STATUS
581EmuSnpReceiveFilters (
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**/
614EFI_STATUS
615EmuSnpStationAddress (
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**/
650EFI_STATUS
651EmuSnpStatistics (
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**/
687EFI_STATUS
688EmuSnpMCastIpToMac (
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**/
723EFI_STATUS
724EmuSnpNvData (
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**/
764EFI_STATUS
765EmuSnpGetStatus (
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**/
820EFI_STATUS
821EmuSnpTransmit (
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**/
896EFI_STATUS
897EmuSnpReceive (
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
985EMU_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
1002EFI_STATUS
1003GetInterfaceMacAddr (
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
1044Exit:
1045  freeifaddrs (IfAddrs);
1046  return Status;
1047}
1048
1049
1050EFI_STATUS
1051EmuSnpThunkOpen (
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
1082EFI_STATUS
1083EmuSnpThunkClose (
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
1101EMU_IO_THUNK_PROTOCOL gSnpThunkIo = {
1102  &gEmuSnpProtocolGuid,
1103  NULL,
1104  NULL,
1105  0,
1106  GasketSnpThunkOpen,
1107  GasketSnpThunkClose,
1108  NULL
1109};
1110
1111#endif
1112