Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /**@file
- Linux Tap Network implementation of the EMU_SNP_PROTOCOL that allows the
- emulator to get on real networks.
- Copyright (c) 2004 - 2009, Intel Corporation. All rights reserved.<BR>
- Portions copyright (c) 2011, Apple Inc. All rights reserved.
- Most implementation copyright (c) 2021, Mara Sophie Grosch <littlefox@lf-net.org>
- SPDX-License-Identifier: BSD-2-Clause-Patent
- **/
- #ifdef __linux__
- #define _GNU_SOURCE
- #include <fcntl.h>
- #include <unistd.h>
- #include <pthread.h>
- #include <poll.h>
- #include <arpa/inet.h>
- #include <linux/if_tun.h>
- #include "Host.h"
- typedef struct {
- UINT8 *Buffer;
- UINT64 Size;
- UINTN Enq;
- UINTN Deq;
- pthread_mutex_t Mutex;
- int DoorbellFd;
- } TAP_RING_BUFFER;
- static
- EFI_STATUS
- TapRingBufferPush (
- IN TAP_RING_BUFFER *Ring,
- IN VOID *Buffer,
- IN UINTN BufferSize
- )
- {
- pthread_mutex_lock (&Ring->Mutex);
- if ((Ring->Size - Ring->Enq) + Ring->Deq < BufferSize + 2) {
- pthread_mutex_unlock (&Ring->Mutex);
- return EFI_NOT_READY;
- }
- else if (!Buffer) {
- pthread_mutex_unlock (&Ring->Mutex);
- return EFI_INVALID_PARAMETER;
- }
- // we store the size of the packet right before the buffer
- // so we can read whole packets. Ethernet frames do not
- // include a size field themself.
- ((UINT8*)Ring->Buffer)[Ring->Enq++] = BufferSize >> 8;
- ((UINT8*)Ring->Buffer)[Ring->Enq++ % Ring->Size] = BufferSize & 0xFF;
- UINTN rightChunkSize = (Ring->Size - Ring->Enq);
- if (rightChunkSize > BufferSize) {
- rightChunkSize = BufferSize;
- }
- memcpy (&Ring->Buffer[Ring->Enq], Buffer, rightChunkSize);
- Ring->Enq += rightChunkSize;
- if (Ring->Enq == Ring->Size) {
- Ring->Enq = 0;
- UINTN leftChunkSize = Ring->Deq - 1;
- if (leftChunkSize > BufferSize - rightChunkSize) {
- leftChunkSize = BufferSize - rightChunkSize;
- }
- memcpy (&Ring->Buffer[Ring->Enq], Buffer + rightChunkSize, leftChunkSize);
- Ring->Enq += leftChunkSize;
- }
- if (Ring->DoorbellFd >= 0) {
- if (write (Ring->DoorbellFd, "P", 1) != 1) {
- printf (
- "SNP: error ringing doorbell: %s (%d).\n",
- strerror(errno), errno
- );
- }
- }
- pthread_mutex_unlock (&Ring->Mutex);
- return EFI_SUCCESS;
- }
- static
- EFI_STATUS
- TapRingBufferPop (
- IN TAP_RING_BUFFER *Ring,
- OUT VOID *Buffer,
- IN OUT UINTN *BufferSize
- )
- {
- if (!BufferSize || !Ring) {
- return EFI_INVALID_PARAMETER;
- }
- pthread_mutex_lock (&Ring->Mutex);
- UINTN sizeInBuffer = Ring->Enq - Ring->Deq;
- if (sizeInBuffer >= 2) {
- UINT16 packetSize = (UINT16)Ring->Buffer[ Ring->Deq ] << 8
- | Ring->Buffer[(Ring->Deq + 1) % Ring->Size];
- if (packetSize > *BufferSize) {
- *BufferSize = packetSize;
- pthread_mutex_unlock (&Ring->Mutex);
- return EFI_BUFFER_TOO_SMALL;
- }
- else if (!Buffer) {
- pthread_mutex_unlock (&Ring->Mutex);
- return EFI_INVALID_PARAMETER;
- }
- // we have checked everything, lets increase Deq by the packet size
- // do not add failure-returns here without caring for Deq!
- Ring->Deq += 2;
- UINTN rightChunkSize = Ring->Deq < Ring->Enq
- ? Ring->Enq - Ring->Deq
- : Ring->Size - Ring->Deq;
- if (rightChunkSize > packetSize) {
- rightChunkSize = packetSize;
- }
- memcpy (Buffer, &Ring->Buffer[Ring->Deq], rightChunkSize);
- Ring->Deq += rightChunkSize;
- if (Ring->Deq == Ring->Size) {
- Ring->Deq = 0;
- UINTN leftChunkSize = Ring->Enq;
- if (leftChunkSize > packetSize - rightChunkSize) {
- leftChunkSize = packetSize - rightChunkSize;
- }
- if (leftChunkSize) {
- memcpy (Buffer + rightChunkSize, Ring->Buffer, leftChunkSize);
- Ring->Deq += leftChunkSize;
- }
- }
- pthread_mutex_unlock (&Ring->Mutex);
- return EFI_SUCCESS;
- }
- else {
- pthread_mutex_unlock (&Ring->Mutex);
- return EFI_NOT_READY;
- }
- }
- #define EMU_SNP_PRIVATE_SIGNATURE SIGNATURE_32('L', 'T', 's', 'n') // Linux Tap simple network
- typedef struct {
- UINTN Signature;
- EMU_IO_THUNK_PROTOCOL *Thunk;
- EMU_SNP_PROTOCOL EmuSnp;
- EFI_SIMPLE_NETWORK_MODE *Mode;
- int TapFd;
- int TapWorkerDoorbell[2];
- pthread_t WorkerThread;
- TAP_RING_BUFFER TxBuffer;
- TAP_RING_BUFFER RxBuffer;
- } EMU_SNP_PRIVATE;
- #define EMU_SNP_PRIVATE_DATA_FROM_THIS(a) \
- CR(a, EMU_SNP_PRIVATE, EmuSnp, EMU_SNP_PRIVATE_SIGNATURE)
- static
- VOID*
- TapWorker (
- IN VOID* PrivatePtr
- )
- {
- // first we have to disable the alarm signal,
- // UEFI timers will be executed on this thread otherwise
- sigset_t signals;
- sigemptyset (&signals);
- sigaddset (&signals, SIGALRM);
- pthread_sigmask (SIG_BLOCK, &signals, NULL);
- EMU_SNP_PRIVATE *Private = (EMU_SNP_PRIVATE*)PrivatePtr;
- int packetsToTransmit = 0;
- // packets to and from the tap interface are stored here
- // so we don't have to enqueue them in the same loop
- UINT8 *readPacket = 0, *writePacket = 0;
- UINTN readPacketSize = 0, writePacketSize = 0;
- struct pollfd pfds[] = {
- { .fd = Private->TapFd, .events = POLLIN | POLLOUT },
- { .fd = Private->TapWorkerDoorbell[0], .events = POLLIN },
- };
- while(1) {
- int error = poll(pfds, sizeof(pfds) / sizeof(struct pollfd), -1);
- if(error < 0) {
- printf (
- "SNP: received an error while polling for work: %s (%d).\n",
- strerror(errno), errno
- );
- }
- if (pfds[1].revents & POLLIN || 1) {
- char command;
- while(read (Private->TapWorkerDoorbell[0], &command, 1) == 1) {
- switch (command) {
- case 'E':
- return NULL;
- case 'P':
- ++packetsToTransmit;
- break;
- }
- }
- }
- if (pfds[0].revents & POLLIN && !readPacket) {
- readPacketSize = 9000;
- readPacket = malloc(readPacketSize);
- ssize_t size = read (Private->TapFd, readPacket, readPacketSize);
- if (size < 0) {
- printf (
- "SNP: error read()ing packet: %s (%d).\n",
- strerror(errno), errno
- );
- }
- else {
- readPacketSize = size;
- }
- }
- if (pfds[0].revents & POLLOUT && writePacket) {
- ssize_t sendSize = write (Private->TapFd, writePacket, writePacketSize);
- if (sendSize < 0) {
- printf (
- "SNP: error write()ing packet: %s (%d).\n",
- strerror(errno), errno
- );
- }
- else {
- if (sendSize < writePacketSize) {
- printf (
- "SNP: write() packet shorter (%ld B) than the size we wanted to send(%llu B).\n",
- sendSize, writePacketSize
- );
- }
- free(writePacket);
- writePacket = 0;
- writePacketSize = 0;
- }
- }
- EFI_STATUS status = TapRingBufferPush (&Private->RxBuffer, readPacket, readPacketSize);
- if (status == EFI_SUCCESS) {
- free (readPacket);
- readPacket = 0;
- readPacketSize = 0;
- }
- if (packetsToTransmit && !writePacket) {
- status = TapRingBufferPop (&Private->TxBuffer, NULL, &writePacketSize);
- if (status == EFI_BUFFER_TOO_SMALL) {
- writePacket = malloc (writePacketSize);
- status = TapRingBufferPop (&Private->TxBuffer, writePacket, &writePacketSize);
- if (status == EFI_SUCCESS) {
- --packetsToTransmit;
- }
- else {
- printf (
- "SNP: error dequeueing packet from Tx buffer: %llu.\n",
- status
- );
- }
- }
- }
- }
- }
- /**
- Register storage for SNP Mode.
- @param This Protocol instance pointer.
- @param Mode SimpleNetworkProtocol Mode structure passed into driver.
- @retval EFI_SUCCESS The network interface was started.
- @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
- **/
- EFI_STATUS
- EmuSnpCreateMapping (
- IN EMU_SNP_PROTOCOL *This,
- IN EFI_SIMPLE_NETWORK_MODE *Mode
- )
- {
- EMU_SNP_PRIVATE *Private;
- Private = EMU_SNP_PRIVATE_DATA_FROM_THIS (This);
- char* ifname = "uefiEmu";
- Private->TapFd = open ("/dev/net/tun", O_RDWR | O_NONBLOCK);
- if (Private->TapFd < 0) {
- printf (
- "SNP: Could not open '/dev/net/tun': %s (%d). Trying older kernel interface.\n",
- strerror(-Private->TapFd), -Private->TapFd
- );
- char fname[IFNAMSIZ];
- snprintf (fname, IFNAMSIZ - 1, "/dev/%s", ifname);
- Private->TapFd = open (fname, O_RDWR | O_NONBLOCK);
- if (Private->TapFd < 0) {
- printf (
- "SNP: Could not open '%s': %s (%d).\n",
- fname,
- strerror(-Private->TapFd), -Private->TapFd
- );
- return EFI_DEVICE_ERROR;
- }
- }
- else {
- struct ifreq ifr;
- ZeroMem (&ifr, sizeof(ifr));
- ifr.ifr_flags = IFF_TAP;
- strncpy (ifr.ifr_name, ifname, IFNAMSIZ);
- int err = ioctl (Private->TapFd, TUNSETIFF, &ifr);
- if(err < 0) {
- close (Private->TapFd);
- printf (
- "SNP: Could not open tap interface '%s': %s (%d).\n",
- ifname,
- strerror (-err),- err
- );
- return EFI_DEVICE_ERROR;
- }
- }
- struct ifreq ifr;
- ZeroMem (&ifr, sizeof (ifr));
- int error = ioctl (Private->TapFd, SIOCGIFHWADDR, &ifr);
- if(error < 0) {
- printf (
- "SNP: error retrieving interface info: %s (%d). Will continue with dummy mac.\n",
- strerror (errno), errno
- );
- CopyMem(&ifr.ifr_addr.sa_data, (UINT8[]){ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }, ETH_ALEN);
- }
- else {
- // prevent MAC conflict: use Host-side of TAP + 1
- ifr.ifr_addr.sa_data[ETH_ALEN - 1]++;
- }
- // error = pipe2(Private->TapWorkerDoorbell, O_DIRECT);
- //
- // if (error) {
- // printf(
- // "SNP: Failed to create doorbell pipe: %s (%d).\n",
- // strerror(errno), errno
- // );
- //
- // return EFI_DEVICE_ERROR;
- // }
- //
- // Private->TxBuffer.DoorbellFd = Private->TapWorkerDoorbell[1];
- // Private->RxBuffer.DoorbellFd = -1;
- //
- printf (
- "SNP: opened tap interface '%s' as endpoint for emulator network device.\n",
- ifname
- );
- Private->Mode = Mode;
- Private->Mode->HwAddressSize = ETH_ALEN;
- SetMem (&Private->Mode->BroadcastAddress, ETH_ALEN, 0xFF);
- CopyMem (&Private->Mode->CurrentAddress, &ifr.ifr_addr.sa_data, ETH_ALEN);
- CopyMem (&Private->Mode->PermanentAddress, &ifr.ifr_addr.sa_data, ETH_ALEN);
- Private->TxBuffer.Size = Private->TxBuffer.Enq = Private->TxBuffer.Deq = 0;
- Private->RxBuffer.Size = Private->RxBuffer.Enq = Private->RxBuffer.Deq = 0;
- // if ((error = pthread_mutex_init(&Private->TxBuffer.Mutex, NULL)) != 0) {
- // printf (
- // "SNP: error initializing mutex for tx buffer: %s (%d).\n",
- // strerror(error), error
- // );
- // }
- //
- // if ((error = pthread_mutex_init(&Private->RxBuffer.Mutex, NULL)) != 0) {
- // printf (
- // "SNP: error initializing mutex for rx buffer: %s (%d).\n",
- // strerror(error), error
- // );
- // }
- return EFI_SUCCESS;
- }
- /**
- Changes the state of a network interface from "stopped" to "started".
- @param This Protocol instance pointer.
- @retval EFI_SUCCESS The network interface was started.
- @retval EFI_ALREADY_STARTED The network interface is already in the started state.
- @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
- @retval EFI_DEVICE_ERROR The command could not be sent to the network interface.
- @retval EFI_UNSUPPORTED This function is not supported by the network interface.
- **/
- EFI_STATUS
- EmuSnpStart (
- IN EMU_SNP_PROTOCOL *This
- )
- {
- EMU_SNP_PRIVATE *Private;
- Private = EMU_SNP_PRIVATE_DATA_FROM_THIS (This);
- if(Private->Mode->State != EfiSimpleNetworkStopped) {
- return EFI_ALREADY_STARTED;
- }
- struct ifreq ifr;
- ZeroMem (&ifr, sizeof (ifr));
- ifr.ifr_flags = IFF_UP;
- int error = ioctl (Private->TapFd, TUNSETLINK, &ifr);
- // EBUSY means it's already up, maybe from a previous run
- if (error == -1 && errno != EBUSY) {
- printf (
- "SNP: failed to set link up: %s (%d)",
- strerror (errno), errno
- );
- return EFI_DEVICE_ERROR;
- }
- // error = pthread_create (&Private->WorkerThread, NULL, TapWorker, Private);
- //
- // if(error) {
- // printf (
- // "SNP: failed to start worker thread: %s (%d)",
- // strerror (error), error
- // );
- //
- // return EFI_DEVICE_ERROR;
- // }
- Private->Mode->State = EfiSimpleNetworkStarted;
- return EFI_SUCCESS;
- }
- /**
- Changes the state of a network interface from "started" to "stopped".
- @param This Protocol instance pointer.
- @retval EFI_SUCCESS The network interface was stopped.
- @retval EFI_ALREADY_STARTED The network interface is already in the stopped state.
- @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
- @retval EFI_DEVICE_ERROR The command could not be sent to the network interface.
- @retval EFI_UNSUPPORTED This function is not supported by the network interface.
- **/
- EFI_STATUS
- EmuSnpStop (
- IN EMU_SNP_PROTOCOL *This
- )
- {
- EMU_SNP_PRIVATE *Private;
- Private = EMU_SNP_PRIVATE_DATA_FROM_THIS (This);
- if(Private->Mode->State == EfiSimpleNetworkStopped) {
- return EFI_ALREADY_STARTED;
- }
- struct ifreq ifr;
- ZeroMem (&ifr, sizeof (ifr));
- int error = ioctl (Private->TapFd, TUNSETLINK, &ifr);
- if (error == -1) {
- printf (
- "SNP: failed to set link down: %s (%d)",
- strerror (errno), errno
- );
- return EFI_DEVICE_ERROR;
- }
- // if (write (Private->TapWorkerDoorbell[1], "E", 1) != 1) {
- // printf (
- // "SNP: error ringing for stop: %s (%d).\n",
- // strerror(errno), errno
- // );
- // }
- //
- // pthread_join (Private->WorkerThread, NULL);
- //
- // Private->TxBuffer.Size = Private->TxBuffer.Enq = Private->TxBuffer.Deq = 0;
- // Private->RxBuffer.Size = Private->RxBuffer.Enq = Private->RxBuffer.Deq = 0;
- //
- Private->Mode->State = EfiSimpleNetworkStopped;
- return EFI_SUCCESS;
- }
- /**
- Resets a network adapter and allocates the transmit and receive buffers
- required by the network interface; optionally, also requests allocation
- of additional transmit and receive buffers.
- @param This The protocol instance pointer.
- @param ExtraRxBufferSize The size, in bytes, of the extra receive buffer space
- that the driver should allocate for the network interface.
- Some network interfaces will not be able to use the extra
- buffer, and the caller will not know if it is actually
- being used.
- @param ExtraTxBufferSize The size, in bytes, of the extra transmit buffer space
- that the driver should allocate for the network interface.
- Some network interfaces will not be able to use the extra
- buffer, and the caller will not know if it is actually
- being used.
- @retval EFI_SUCCESS The network interface was initialized.
- @retval EFI_NOT_STARTED The network interface has not been started.
- @retval EFI_OUT_OF_RESOURCES There was not enough memory for the transmit and
- receive buffers.
- @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
- @retval EFI_DEVICE_ERROR The command could not be sent to the network interface.
- @retval EFI_UNSUPPORTED This function is not supported by the network interface.
- **/
- EFI_STATUS
- EmuSnpInitialize (
- IN EMU_SNP_PROTOCOL *This,
- IN UINTN ExtraRxBufferSize OPTIONAL,
- IN UINTN ExtraTxBufferSize OPTIONAL
- )
- {
- EMU_SNP_PRIVATE *Private;
- Private = EMU_SNP_PRIVATE_DATA_FROM_THIS (This);
- switch(Private->Mode->State) {
- case EfiSimpleNetworkStarted:
- break;
- case EfiSimpleNetworkStopped:
- return EFI_NOT_STARTED;
- break;
- default:
- return EFI_DEVICE_ERROR;
- break;
- }
- // Private->TxBuffer.Size = Private->TxBuffer.Enq = Private->TxBuffer.Deq = 0;
- // Private->RxBuffer.Size = Private->RxBuffer.Enq = Private->RxBuffer.Deq = 0;
- //
- // static const UINTN bufferSizeMin = 65536;
- //
- // if (ExtraRxBufferSize < bufferSizeMin) {
- // ExtraRxBufferSize = bufferSizeMin;
- // }
- //
- // if (ExtraTxBufferSize < bufferSizeMin) {
- // ExtraTxBufferSize = bufferSizeMin;
- // }
- //
- // Private->TxBuffer.Buffer = malloc (ExtraTxBufferSize);
- // Private->TxBuffer.Size = ExtraTxBufferSize;
- //
- // Private->RxBuffer.Buffer = malloc (ExtraRxBufferSize);
- // Private->RxBuffer.Size = ExtraRxBufferSize;
- Private->Mode->State = EfiSimpleNetworkInitialized;
- return EFI_SUCCESS;
- }
- /**
- Resets a network adapter and re-initializes it with the parameters that were
- provided in the previous call to Initialize().
- @param This The protocol instance pointer.
- @param ExtendedVerification Indicates that the driver may perform a more
- exhaustive verification operation of the device
- during reset.
- @retval EFI_SUCCESS The network interface was reset.
- @retval EFI_NOT_STARTED The network interface has not been started.
- @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
- @retval EFI_DEVICE_ERROR The command could not be sent to the network interface.
- @retval EFI_UNSUPPORTED This function is not supported by the network interface.
- **/
- EFI_STATUS
- EmuSnpReset (
- IN EMU_SNP_PROTOCOL *This,
- IN BOOLEAN ExtendedVerification
- )
- {
- EMU_SNP_PRIVATE *Private;
- Private = EMU_SNP_PRIVATE_DATA_FROM_THIS (This);
- switch(Private->Mode->State) {
- case EfiSimpleNetworkInitialized:
- break;
- case EfiSimpleNetworkStopped:
- return EFI_NOT_STARTED;
- break;
- default:
- return EFI_DEVICE_ERROR;
- break;
- }
- // Private->TxBuffer.Deq = Private->TxBuffer.Enq = 0;
- // Private->RxBuffer.Deq = Private->RxBuffer.Enq = 0;
- //
- // ZeroMem(Private->TxBuffer.Buffer, Private->TxBuffer.Size);
- // ZeroMem(Private->RxBuffer.Buffer, Private->RxBuffer.Size);
- //
- Private->Mode->State = EfiSimpleNetworkStarted;
- return EFI_SUCCESS;
- }
- /**
- Resets a network adapter and leaves it in a state that is safe for
- another driver to initialize.
- @param This Protocol instance pointer.
- @retval EFI_SUCCESS The network interface was shutdown.
- @retval EFI_NOT_STARTED The network interface has not been started.
- @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
- @retval EFI_DEVICE_ERROR The command could not be sent to the network interface.
- @retval EFI_UNSUPPORTED This function is not supported by the network interface.
- **/
- EFI_STATUS
- EmuSnpShutdown (
- IN EMU_SNP_PROTOCOL *This
- )
- {
- EMU_SNP_PRIVATE *Private;
- Private = EMU_SNP_PRIVATE_DATA_FROM_THIS (This);
- switch(Private->Mode->State) {
- case EfiSimpleNetworkInitialized:
- break;
- case EfiSimpleNetworkStopped:
- return EFI_NOT_STARTED;
- break;
- default:
- return EFI_DEVICE_ERROR;
- break;
- }
- // if (write (Private->TapWorkerDoorbell[1], "E", 1) != 1) {
- // printf (
- // "SNP: error ringing for stop: %s (%d).\n",
- // strerror(errno), errno
- // );
- // }
- //
- // pthread_join (Private->WorkerThread, NULL);
- //
- // Private->TxBuffer.Size = Private->TxBuffer.Deq = Private->TxBuffer.Enq = 0;
- // Private->RxBuffer.Size = Private->RxBuffer.Deq = Private->RxBuffer.Enq = 0;
- //
- // free (Private->TxBuffer.Buffer);
- // free (Private->RxBuffer.Buffer);
- Private->Mode->ReceiveFilterSetting = 0;
- Private->Mode->MCastFilterCount = 0;
- ZeroMem (Private->Mode->MCastFilter, sizeof (Private->Mode->MCastFilter));
- Private->Mode->State = EfiSimpleNetworkStarted;
- return EFI_SUCCESS;
- }
- /**
- Manages the multicast receive filters of a network interface.
- @param This The protocol instance pointer.
- @param Enable A bit mask of receive filters to enable on the network interface.
- @param Disable A bit mask of receive filters to disable on the network interface.
- @param ResetMCastFilter Set to TRUE to reset the contents of the multicast receive
- filters on the network interface to their default values.
- @param McastFilterCnt Number of multicast HW MAC addresses in the new
- MCastFilter list. This value must be less than or equal to
- the MCastFilterCnt field of EMU_SNP_MODE. This
- field is optional if ResetMCastFilter is TRUE.
- @param MCastFilter A pointer to a list of new multicast receive filter HW MAC
- addresses. This list will replace any existing multicast
- HW MAC address list. This field is optional if
- ResetMCastFilter is TRUE.
- @retval EFI_SUCCESS The multicast receive filter list was updated.
- @retval EFI_NOT_STARTED The network interface has not been started.
- @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
- @retval EFI_DEVICE_ERROR The command could not be sent to the network interface.
- @retval EFI_UNSUPPORTED This function is not supported by the network interface.
- **/
- 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
- )
- {
- EMU_SNP_PRIVATE *Private;
- Private = EMU_SNP_PRIVATE_DATA_FROM_THIS (This);
- return EFI_SUCCESS;
- }
- /**
- Modifies or resets the current station address, if supported.
- @param This The protocol instance pointer.
- @param Reset Flag used to reset the station address to the network interfaces
- permanent address.
- @param New The new station address to be used for the network interface.
- @retval EFI_SUCCESS The network interfaces station address was updated.
- @retval EFI_NOT_STARTED The network interface has not been started.
- @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
- @retval EFI_DEVICE_ERROR The command could not be sent to the network interface.
- @retval EFI_UNSUPPORTED This function is not supported by the network interface.
- **/
- EFI_STATUS
- EmuSnpStationAddress (
- IN EMU_SNP_PROTOCOL *This,
- IN BOOLEAN Reset,
- IN EFI_MAC_ADDRESS *New OPTIONAL
- )
- {
- EMU_SNP_PRIVATE *Private;
- Private = EMU_SNP_PRIVATE_DATA_FROM_THIS (This);
- switch(Private->Mode->State) {
- case EfiSimpleNetworkInitialized:
- break;
- case EfiSimpleNetworkStopped:
- return EFI_NOT_STARTED;
- break;
- default:
- return EFI_DEVICE_ERROR;
- break;
- }
- struct ifreq ifr;
- SetMem (&ifr, sizeof (ifr), 0);
- if ((New && Reset) || (!New && !Reset)) {
- return EFI_INVALID_PARAMETER;
- }
- else if (New) {
- CopyMem(&ifr.ifr_hwaddr, New, ETH_ALEN);
- }
- else if (Reset) {
- CopyMem(&ifr.ifr_hwaddr, &Private->Mode->PermanentAddress, ETH_ALEN);
- }
- int error = ioctl (Private->TapFd, SIOCSIFHWADDR, &ifr);
- if(error) {
- return EFI_DEVICE_ERROR;
- }
- return EFI_SUCCESS;
- }
- /**
- Resets or collects the statistics on a network interface.
- @param This Protocol instance pointer.
- @param Reset Set to TRUE to reset the statistics for the network interface.
- @param StatisticsSize On input the size, in bytes, of StatisticsTable. On
- output the size, in bytes, of the resulting table of
- statistics.
- @param StatisticsTable A pointer to the EFI_NETWORK_STATISTICS structure that
- contains the statistics.
- @retval EFI_SUCCESS The statistics were collected from the network interface.
- @retval EFI_NOT_STARTED The network interface has not been started.
- @retval EFI_BUFFER_TOO_SMALL The Statistics buffer was too small. The current buffer
- size needed to hold the statistics is returned in
- StatisticsSize.
- @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
- @retval EFI_DEVICE_ERROR The command could not be sent to the network interface.
- @retval EFI_UNSUPPORTED This function is not supported by the network interface.
- **/
- EFI_STATUS
- EmuSnpStatistics (
- IN EMU_SNP_PROTOCOL *This,
- IN BOOLEAN Reset,
- IN OUT UINTN *StatisticsSize OPTIONAL,
- OUT EFI_NETWORK_STATISTICS *StatisticsTable OPTIONAL
- )
- {
- EMU_SNP_PRIVATE *Private;
- Private = EMU_SNP_PRIVATE_DATA_FROM_THIS (This);
- return EFI_UNSUPPORTED;
- }
- /**
- Converts a multicast IP address to a multicast HW MAC address.
- @param This The protocol instance pointer.
- @param IPv6 Set to TRUE if the multicast IP address is IPv6 [RFC 2460]. Set
- to FALSE if the multicast IP address is IPv4 [RFC 791].
- @param IP The multicast IP address that is to be converted to a multicast
- HW MAC address.
- @param MAC The multicast HW MAC address that is to be generated from IP.
- @retval EFI_SUCCESS The multicast IP address was mapped to the multicast
- HW MAC address.
- @retval EFI_NOT_STARTED The network interface has not been started.
- @retval EFI_BUFFER_TOO_SMALL The Statistics buffer was too small. The current buffer
- size needed to hold the statistics is returned in
- StatisticsSize.
- @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
- @retval EFI_DEVICE_ERROR The command could not be sent to the network interface.
- @retval EFI_UNSUPPORTED This function is not supported by the network interface.
- **/
- EFI_STATUS
- EmuSnpMCastIpToMac (
- IN EMU_SNP_PROTOCOL *This,
- IN BOOLEAN IPv6,
- IN EFI_IP_ADDRESS *IP,
- OUT EFI_MAC_ADDRESS *MAC
- )
- {
- EMU_SNP_PRIVATE *Private;
- Private = EMU_SNP_PRIVATE_DATA_FROM_THIS (This);
- return EFI_UNSUPPORTED;
- }
- /**
- Performs read and write operations on the NVRAM device attached to a
- network interface.
- @param This The protocol instance pointer.
- @param ReadWrite TRUE for read operations, FALSE for write operations.
- @param Offset Byte offset in the NVRAM device at which to start the read or
- write operation. This must be a multiple of NvRamAccessSize and
- less than NvRamSize.
- @param BufferSize The number of bytes to read or write from the NVRAM device.
- This must also be a multiple of NvramAccessSize.
- @param Buffer A pointer to the data buffer.
- @retval EFI_SUCCESS The NVRAM access was performed.
- @retval EFI_NOT_STARTED The network interface has not been started.
- @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
- @retval EFI_DEVICE_ERROR The command could not be sent to the network interface.
- @retval EFI_UNSUPPORTED This function is not supported by the network interface.
- **/
- EFI_STATUS
- EmuSnpNvData (
- IN EMU_SNP_PROTOCOL *This,
- IN BOOLEAN ReadWrite,
- IN UINTN Offset,
- IN UINTN BufferSize,
- IN OUT VOID *Buffer
- )
- {
- EMU_SNP_PRIVATE *Private;
- Private = EMU_SNP_PRIVATE_DATA_FROM_THIS (This);
- return EFI_UNSUPPORTED;
- }
- /**
- Reads the current interrupt status and recycled transmit buffer status from
- a network interface.
- @param This The protocol instance pointer.
- @param InterruptStatus A pointer to the bit mask of the currently active interrupts
- If this is NULL, the interrupt status will not be read from
- the device. If this is not NULL, the interrupt status will
- be read from the device. When the interrupt status is read,
- it will also be cleared. Clearing the transmit interrupt
- does not empty the recycled transmit buffer array.
- @param TxBuf Recycled transmit buffer address. The network interface will
- not transmit if its internal recycled transmit buffer array
- is full. Reading the transmit buffer does not clear the
- transmit interrupt. If this is NULL, then the transmit buffer
- status will not be read. If there are no transmit buffers to
- recycle and TxBuf is not NULL, * TxBuf will be set to NULL.
- @retval EFI_SUCCESS The status of the network interface was retrieved.
- @retval EFI_NOT_STARTED The network interface has not been started.
- @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
- @retval EFI_DEVICE_ERROR The command could not be sent to the network interface.
- @retval EFI_UNSUPPORTED This function is not supported by the network interface.
- **/
- EFI_STATUS
- EmuSnpGetStatus (
- IN EMU_SNP_PROTOCOL *This,
- OUT UINT32 *InterruptStatus OPTIONAL,
- OUT VOID **TxBuf OPTIONAL
- )
- {
- EMU_SNP_PRIVATE *Private;
- Private = EMU_SNP_PRIVATE_DATA_FROM_THIS (This);
- if ( InterruptStatus != NULL ) {
- *InterruptStatus = EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT;
- }
- return EFI_SUCCESS;
- }
- /**
- Places a packet in the transmit queue of a network interface.
- @param This The protocol instance pointer.
- @param HeaderSize The size, in bytes, of the media header to be filled in by
- the Transmit() function. If HeaderSize is non-zero, then it
- must be equal to This->Mode->MediaHeaderSize and the DestAddr
- and Protocol parameters must not be NULL.
- @param BufferSize The size, in bytes, of the entire packet (media header and
- data) to be transmitted through the network interface.
- @param Buffer A pointer to the packet (media header followed by data) to be
- transmitted. This parameter cannot be NULL. If HeaderSize is zero,
- then the media header in Buffer must already be filled in by the
- caller. If HeaderSize is non-zero, then the media header will be
- filled in by the Transmit() function.
- @param SrcAddr The source HW MAC address. If HeaderSize is zero, then this parameter
- is ignored. If HeaderSize is non-zero and SrcAddr is NULL, then
- This->Mode->CurrentAddress is used for the source HW MAC address.
- @param DestAddr The destination HW MAC address. If HeaderSize is zero, then this
- parameter is ignored.
- @param Protocol The type of header to build. If HeaderSize is zero, then this
- parameter is ignored. See RFC 1700, section "Ether Types", for
- examples.
- @retval EFI_SUCCESS The packet was placed on the transmit queue.
- @retval EFI_NOT_STARTED The network interface has not been started.
- @retval EFI_NOT_READY The network interface is too busy to accept this transmit request.
- @retval EFI_BUFFER_TOO_SMALL The BufferSize parameter is too small.
- @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
- @retval EFI_DEVICE_ERROR The command could not be sent to the network interface.
- @retval EFI_UNSUPPORTED This function is not supported by the network interface.
- **/
- 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
- )
- {
- EMU_SNP_PRIVATE *Private;
- Private = EMU_SNP_PRIVATE_DATA_FROM_THIS (This);
- if (Private->Mode->State < EfiSimpleNetworkStarted) {
- return EFI_NOT_STARTED;
- }
- if ( HeaderSize != 0 ) {
- if ((DestAddr == NULL) || (Protocol == NULL) || (HeaderSize != Private->Mode->MediaHeaderSize)) {
- return EFI_INVALID_PARAMETER;
- }
- if (SrcAddr == NULL) {
- SrcAddr = &Private->Mode->CurrentAddress;
- }
- struct ethhdr *header = (struct ethhdr*)Buffer;
- CopyMem (header->h_dest, DestAddr, ETH_ALEN);
- CopyMem (header->h_source, SrcAddr, ETH_ALEN);
- header->h_proto = htons(*Protocol);
- }
- VOID* buffer = malloc (BufferSize + 4);
- ZeroMem(buffer, BufferSize + 4);
- CopyMem(buffer + 4, Buffer, BufferSize);
- ssize_t writeSize = write (Private->TapFd, buffer, BufferSize + 4);
- if (writeSize < 0) {
- printf (
- "SNP: error writing to tap device: %s (%d).\n",
- strerror(errno), errno
- );
- return EFI_DEVICE_ERROR;
- }
- else if (writeSize < BufferSize + 4) {
- printf (
- "SNP: short write to tap device, trying to transmit whats left.\n",
- strerror(errno), errno
- );
- ssize_t writeSize2 = write (Private->TapFd, buffer + writeSize, BufferSize + 4 - writeSize);
- if (writeSize2 + writeSize < BufferSize + 4) {
- if (writeSize2 < 0) {
- printf (
- "SNP: error writing to tap device (while recovering from a short write): %s (%d).\n",
- strerror(errno), errno
- );
- // no error return here since we wrote some ...
- }
- if (writeSize2 + writeSize < BufferSize + 4) {
- printf (
- "SNP: short write on second try, giving up.\n",
- strerror(errno), errno
- );
- }
- free(buffer);
- return EFI_WARN_WRITE_FAILURE;
- }
- }
- free(buffer);
- return EFI_SUCCESS;
- //return TapRingBufferPush (&Private->TxBuffer, Buffer, BufferSize);
- }
- /**
- Receives a packet from a network interface.
- @param This The protocol instance pointer.
- @param HeaderSize The size, in bytes, of the media header received on the network
- interface. If this parameter is NULL, then the media header size
- will not be returned.
- @param BufferSize On entry, the size, in bytes, of Buffer. On exit, the size, in
- bytes, of the packet that was received on the network interface.
- @param Buffer A pointer to the data buffer to receive both the media header and
- the data.
- @param SrcAddr The source HW MAC address. If this parameter is NULL, the
- HW MAC source address will not be extracted from the media
- header.
- @param DestAddr The destination HW MAC address. If this parameter is NULL,
- the HW MAC destination address will not be extracted from the
- media header.
- @param Protocol The media header type. If this parameter is NULL, then the
- protocol will not be extracted from the media header. See
- RFC 1700 section "Ether Types" for examples.
- @retval EFI_SUCCESS The received data was stored in Buffer, and BufferSize has
- been updated to the number of bytes received.
- @retval EFI_NOT_STARTED The network interface has not been started.
- @retval EFI_NOT_READY The network interface is too busy to accept this transmit
- request.
- @retval EFI_BUFFER_TOO_SMALL The BufferSize parameter is too small.
- @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value.
- @retval EFI_DEVICE_ERROR The command could not be sent to the network interface.
- @retval EFI_UNSUPPORTED This function is not supported by the network interface.
- **/
- 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
- )
- {
- EMU_SNP_PRIVATE *Private;
- EFI_STATUS status;
- Private = EMU_SNP_PRIVATE_DATA_FROM_THIS (This);
- if (!BufferSize) {
- return EFI_INVALID_PARAMETER;
- }
- static UINTN packetSize = 0;
- static UINT8 *packet = 0;
- if (!packetSize) {
- packetSize = 9000;
- packet = malloc (9000);
- ssize_t readSize = read(Private->TapFd, packet, packetSize);
- if (readSize < 0) {
- free(packet);
- packet = 0;
- packetSize = 0;
- if (errno == EAGAIN || errno == EINTR) {
- return EFI_NOT_READY;
- }
- printf (
- "SNP: error reading from tap device: %s (%d).\n",
- strerror(errno), errno
- );
- return EFI_DEVICE_ERROR;
- }
- packetSize = readSize;
- }
- if (packetSize) {
- if (packetSize - 4 <= *BufferSize) {
- *BufferSize = packetSize - 4;
- if (!Buffer) {
- return EFI_INVALID_PARAMETER;
- }
- else {
- CopyMem(Buffer, packet + 4, packetSize - 4);
- free(packet);
- packetSize = 0;
- struct ethhdr *header = (struct ethhdr*)Buffer;
- printf("received packet from %02x:%02x:%02x:%02x:%02x:%02x\n",
- header->h_source[0], header->h_source[1], header->h_source[2],
- header->h_source[3], header->h_source[4], header->h_source[5]
- );
- UINTN headerSize;
- // ethernet frames have two possible sizes, depending on
- // if there is a VLAN tag - we check for the VLAN tag here
- if (header->h_proto == htons(0x8100)) {
- headerSize = 18;
- }
- else {
- headerSize = 14;
- }
- if (HeaderSize) {
- *HeaderSize = headerSize;
- }
- if (SrcAddr) {
- CopyMem(SrcAddr, header->h_source, ETH_ALEN);
- }
- if (DestAddr) {
- CopyMem(SrcAddr, header->h_source, ETH_ALEN);
- }
- if (Protocol) {
- if (headerSize == 14) {
- *Protocol = ntohs(header->h_proto);
- }
- else {
- *Protocol = ntohs(*(&header->h_proto + 2));
- }
- }
- return EFI_SUCCESS;
- }
- }
- else {
- *BufferSize = packetSize - 4;
- return EFI_BUFFER_TOO_SMALL;
- }
- }
- else {
- free(packet);
- packet = 0;
- return EFI_NOT_READY;
- }
- // UINTN packetSize = 0;
- // status = TapRingBufferPop(&Private->RxBuffer, NULL, &packetSize);
- //
- // if (status == EFI_BUFFER_TOO_SMALL) {
- // if (packetSize > *BufferSize) {
- // return status;
- // }
- // }
- // else {
- // return status;
- // }
- //
- // *BufferSize = packetSize;
- // status = TapRingBufferPop(&Private->RxBuffer, Buffer, BufferSize);
- //
- }
- EMU_SNP_PROTOCOL gEmuSnpProtocol = {
- GasketSnpCreateMapping,
- GasketSnpStart,
- GasketSnpStop,
- GasketSnpInitialize,
- GasketSnpReset,
- GasketSnpShutdown,
- GasketSnpReceiveFilters,
- GasketSnpStationAddress,
- GasketSnpStatistics,
- GasketSnpMCastIpToMac,
- GasketSnpNvData,
- GasketSnpGetStatus,
- GasketSnpTransmit,
- GasketSnpReceive
- };
- EFI_STATUS
- EmuSnpThunkOpen (
- IN EMU_IO_THUNK_PROTOCOL *This
- )
- {
- EMU_SNP_PRIVATE *Private;
- if (This->Private != NULL) {
- return EFI_ALREADY_STARTED;
- }
- if (!CompareGuid (This->Protocol, &gEmuSnpProtocolGuid)) {
- return EFI_UNSUPPORTED;
- }
- Private = malloc (sizeof (EMU_SNP_PRIVATE));
- if (Private == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
- Private->Signature = EMU_SNP_PRIVATE_SIGNATURE;
- Private->Thunk = This;
- CopyMem (&Private->EmuSnp, &gEmuSnpProtocol, sizeof (gEmuSnpProtocol));
- This->Interface = &Private->EmuSnp;
- This->Private = Private;
- return EFI_SUCCESS;
- }
- EFI_STATUS
- EmuSnpThunkClose (
- IN EMU_IO_THUNK_PROTOCOL *This
- )
- {
- EMU_SNP_PRIVATE *Private;
- if (!CompareGuid (This->Protocol, &gEmuSnpProtocolGuid)) {
- return EFI_UNSUPPORTED;
- }
- Private = This->Private;
- free (Private);
- return EFI_SUCCESS;
- }
- EMU_IO_THUNK_PROTOCOL gSnpThunkIo = {
- &gEmuSnpProtocolGuid,
- NULL,
- NULL,
- 0,
- GasketSnpThunkOpen,
- GasketSnpThunkClose,
- NULL
- };
- #endif
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement