summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 16:49:04 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 16:49:04 +0000
commit16f504a9dca3fe3b70568f67b7d41241ae485288 (patch)
treec60f36ada0496ba928b7161059ba5ab1ab224f9d /src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe
parentInitial commit. (diff)
downloadvirtualbox-upstream.tar.xz
virtualbox-upstream.zip
Adding upstream version 7.0.6-dfsg.upstream/7.0.6-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe')
-rw-r--r--src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/ComponentName.c172
-rw-r--r--src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/DriverBinding.c666
-rw-r--r--src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/E1kHwIo.c182
-rw-r--r--src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/E1kNet.h350
-rw-r--r--src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/E1kNet.inf60
-rw-r--r--src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/E1kNetHw.h105
-rw-r--r--src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/EntryPoint.c45
-rw-r--r--src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/Events.c82
-rw-r--r--src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpGetStatus.c160
-rw-r--r--src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpInitialize.c321
-rw-r--r--src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpMcastIpToMac.c103
-rw-r--r--src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpReceive.c159
-rw-r--r--src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpReceiveFilters.c100
-rw-r--r--src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpSharedHelpers.c281
-rw-r--r--src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpShutdown.c74
-rw-r--r--src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpStart.c59
-rw-r--r--src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpStop.c60
-rw-r--r--src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpTransmit.c170
-rw-r--r--src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpUnsupported.c155
19 files changed, 3304 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/ComponentName.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/ComponentName.c
new file mode 100644
index 00000000..8a716487
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/ComponentName.c
@@ -0,0 +1,172 @@
+/** @file
+
+ Component Name code for the virtio-net driver.
+
+ Copyright (c) 2021, Oracle and/or its affiliates.
+ Copyright (C) 2013, Red Hat, Inc.
+ Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/UefiLib.h>
+
+#include "E1kNet.h"
+
+STATIC
+EFI_UNICODE_STRING_TABLE mE1kNetDriverNameTable[] = {
+ { "eng;en", L"E1000 network interface card Driver" },
+ { NULL, NULL }
+};
+
+STATIC
+EFI_UNICODE_STRING_TABLE mE1kNetControllerNameTable[] = {
+ { "eng;en", L"E1000 network interface card Driver" },
+ { NULL, NULL }
+};
+
+/**
+ Retrieves a Unicode string that is the user-readable name of the EFI Driver.
+
+ @param This A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+ @param Language A pointer to a three-character ISO 639-2 language
+ identifier. This is the language of the driver name that
+ that the caller is requesting, and it must match one of
+ the languages specified in SupportedLanguages. The number
+ of languages supported by a driver is up to the driver
+ writer.
+ @param DriverName A pointer to the Unicode string to return. This Unicode
+ string is the name of the driver specified by This in the
+ language specified by Language.
+
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by
+ This and the language specified by Language was
+ returned in DriverName.
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+ @retval EFI_INVALID_PARAMETER DriverName is NULL.
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+
+STATIC
+EFI_STATUS
+EFIAPI
+E1kNetGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+{
+ return (Language == NULL || DriverName == NULL) ?
+ EFI_INVALID_PARAMETER :
+ LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mE1kNetDriverNameTable,
+ DriverName,
+ (BOOLEAN) (This == &gE1kNetComponentName) // Iso639Language
+ );
+}
+
+
+/**
+ Retrieves a Unicode string that is the user readable name of the controller
+ that is being managed by an EFI Driver.
+
+ @param This A pointer to the EFI_COMPONENT_NAME_PROTOCOL
+ instance.
+ @param ControllerHandle The handle of a controller that the driver specified
+ by This is managing. This handle specifies the
+ controller whose name is to be returned.
+ @param ChildHandle The handle of the child controller to retrieve the
+ name of. This is an optional parameter that may be
+ NULL. It will be NULL for device drivers. It will
+ also be NULL for a bus drivers that wish to retrieve
+ the name of the bus controller. It will not be NULL
+ for a bus driver that wishes to retrieve the name of
+ a child controller.
+ @param Language A pointer to a three character ISO 639-2 language
+ identifier. This is the language of the controller
+ name that the caller is requesting, and it must
+ match one of the languages specified in
+ SupportedLanguages. The number of languages
+ supported by a driver is up to the driver writer.
+ @param ControllerName A pointer to the Unicode string to return. This
+ Unicode string is the name of the controller
+ specified by ControllerHandle and ChildHandle in the
+ language specified by Language, from the point of
+ view of the driver specified by This.
+
+ @retval EFI_SUCCESS The Unicode string for the user-readable name
+ in the language specified by Language for the
+ driver specified by This was returned in
+ DriverName.
+ @retval EFI_INVALID_PARAMETER ControllerHandle is NULL.
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid
+ EFI_HANDLE.
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+ @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently
+ managing the controller specified by
+ ControllerHandle and ChildHandle.
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+
+STATIC
+EFI_STATUS
+EFIAPI
+E1kNetGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+{
+ EFI_STATUS Status;
+
+ if (ControllerHandle == NULL || Language == NULL || ControllerName == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // confirm that the device is managed by this driver, using the PciIo
+ // Protocol
+ //
+ Status = EfiTestManagedDevice (
+ ControllerHandle,
+ gE1kNetDriverBinding.DriverBindingHandle,
+ &gEfiPciIoProtocolGuid
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // we don't give different names to the bus (= parent) handle and the
+ // child (= MAC) handle
+ //
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mE1kNetControllerNameTable,
+ ControllerName,
+ (BOOLEAN) (This == &gE1kNetComponentName) // Iso639Language
+ );
+}
+
+EFI_COMPONENT_NAME_PROTOCOL gE1kNetComponentName = {
+ &E1kNetGetDriverName,
+ &E1kNetGetControllerName,
+ "eng" // SupportedLanguages, ISO 639-2 language codes
+};
+
+EFI_COMPONENT_NAME2_PROTOCOL gE1kNetComponentName2 = {
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) &E1kNetGetDriverName,
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) &E1kNetGetControllerName,
+ "en" // SupportedLanguages, RFC 4646 language codes
+};
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/DriverBinding.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/DriverBinding.c
new file mode 100644
index 00000000..7ab92e6b
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/DriverBinding.c
@@ -0,0 +1,666 @@
+/** @file
+
+ Driver Binding code and its private helpers for the virtio-net driver.
+
+ Copyright (c) 2021, Oracle and/or its affiliates.
+ Copyright (C) 2013, Red Hat, Inc.
+ Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <IndustryStandard/Pci.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+#include "E1kNet.h"
+
+#define RECEIVE_FILTERS_NO_MCAST ((UINT32) ( \
+ EFI_SIMPLE_NETWORK_RECEIVE_UNICAST | \
+ EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST | \
+ EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS \
+ ))
+
+STATIC
+EFI_STATUS
+E1kNetEepromRead (
+ IN E1K_NET_DEV *Dev,
+ IN UINT8 Offset,
+ OUT UINT16 *Data
+ )
+{
+ EFI_STATUS Status;
+ UINT32 RegEerd = 0;
+
+ Status = E1kNetRegWrite32(Dev, E1K_REG_EERD, ((UINT32)Offset << 8) | E1K_REG_EERD_START);
+ if (EFI_ERROR (Status))
+ return Status;
+
+ // Wait for the read to complete
+ while ( !EFI_ERROR (Status)
+ && !(RegEerd & E1K_REG_EERD_DONE)) {
+ gBS->Stall(1);
+ Status = E1kNetRegRead32(Dev, E1K_REG_EERD, &RegEerd);
+ }
+
+ if (!EFI_ERROR(Status))
+ *Data = E1K_REG_EERD_DATA_GET(RegEerd);
+
+ return Status;
+}
+
+STATIC
+EFI_STATUS
+E1kNetMacAddrRead (
+ IN E1K_NET_DEV *Dev
+ )
+{
+ EFI_STATUS Status;
+ UINT8 i;
+
+ for (i = 0; i < 3; i++)
+ {
+ UINT16 MacAddr;
+ Status = E1kNetEepromRead (Dev, i, &MacAddr);
+ if (EFI_ERROR (Status))
+ return Status;
+
+ Dev->Snm.CurrentAddress.Addr[i * 2] = MacAddr & 0xff;
+ Dev->Snm.CurrentAddress.Addr[i * 2 + 1] = (MacAddr >> 8) & 0xff;
+ }
+
+ return Status;
+}
+
+/**
+ Set up the Simple Network Protocol fields, the Simple Network Mode fields,
+ and the Exit Boot Services Event of the virtio-net driver instance.
+
+ This function may only be called by E1kNetDriverBindingStart().
+
+ @param[in,out] Dev The E1K_NET_DEV driver instance being created for the
+ e1000 device.
+
+ @return Status codes from the CreateEvent().
+ @retval EFI_SUCCESS Configuration successful.
+*/
+STATIC
+EFI_STATUS
+EFIAPI
+E1kNetSnpPopulate (
+ IN OUT E1K_NET_DEV *Dev
+ )
+{
+ UINT32 RegSts;
+ EFI_STATUS Status;
+
+ //
+ // We set up a function here that is asynchronously callable by an
+ // external application to check if there are any packets available for
+ // reception. The least urgent task priority level we can specify for such a
+ // "software interrupt" is TPL_CALLBACK.
+ //
+ // TPL_CALLBACK is also the maximum TPL an SNP implementation is allowed to
+ // run at (see 6.1 Event, Timer, and Task Priority Services in the UEFI
+ // Specification 2.3.1+errC).
+ //
+ // Since we raise our TPL to TPL_CALLBACK in every single function that
+ // accesses the device, and the external application also queues its interest
+ // for received packets at the same TPL_CALLBACK, in effect the
+ // E1kNetIsPacketAvailable() function will never interrupt any
+ // device-accessing driver function, it will be scheduled in isolation.
+ //
+ // TPL_CALLBACK (which basically this entire driver runs at) is allowed
+ // for "[l]ong term operations (such as file system operations and disk
+ // I/O)". Because none of our functions block, we'd satisfy an even stronger
+ // requirement.
+ //
+ Status = gBS->CreateEvent (EVT_NOTIFY_WAIT, TPL_CALLBACK,
+ &E1kNetIsPacketAvailable, Dev, &Dev->Snp.WaitForPacket);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Dev->Snp.Revision = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION;
+ Dev->Snp.Start = &E1kNetStart;
+ Dev->Snp.Stop = &E1kNetStop;
+ Dev->Snp.Initialize = &E1kNetInitialize;
+ Dev->Snp.Reset = &E1kNetReset;
+ Dev->Snp.Shutdown = &E1kNetShutdown;
+ Dev->Snp.ReceiveFilters = &E1kNetReceiveFilters;
+ Dev->Snp.StationAddress = &E1kNetStationAddress;
+ Dev->Snp.Statistics = &E1kNetStatistics;
+ Dev->Snp.MCastIpToMac = &E1kNetMcastIpToMac;
+ Dev->Snp.NvData = &E1kNetNvData;
+ Dev->Snp.GetStatus = &E1kNetGetStatus;
+ Dev->Snp.Transmit = &E1kNetTransmit;
+ Dev->Snp.Receive = &E1kNetReceive;
+ Dev->Snp.Mode = &Dev->Snm;
+
+ Dev->Snm.State = EfiSimpleNetworkStopped;
+ Dev->Snm.HwAddressSize = sizeof (E1K_NET_MAC);
+ Dev->Snm.MediaHeaderSize = sizeof (E1K_NET_MAC) + // dst MAC
+ sizeof (E1K_NET_MAC) + // src MAC
+ 2; // Ethertype
+ Dev->Snm.MaxPacketSize = 1500;
+ Dev->Snm.NvRamSize = 0;
+ Dev->Snm.NvRamAccessSize = 0;
+ Dev->Snm.ReceiveFilterMask = RECEIVE_FILTERS_NO_MCAST;
+ Dev->Snm.ReceiveFilterSetting = RECEIVE_FILTERS_NO_MCAST;
+ Dev->Snm.MaxMCastFilterCount = 0;
+ Dev->Snm.MCastFilterCount = 0;
+ Dev->Snm.IfType = 1; // ethernet
+ Dev->Snm.MacAddressChangeable = FALSE;
+ Dev->Snm.MultipleTxSupported = TRUE;
+
+ ASSERT (sizeof (E1K_NET_MAC) <= sizeof (EFI_MAC_ADDRESS));
+
+ Dev->Snm.MediaPresentSupported = TRUE;
+ Status = E1kNetRegRead32(Dev, E1K_REG_STATUS, &RegSts);
+ if (EFI_ERROR (Status)) {
+ goto CloseWaitForPacket;
+ }
+
+ Dev->Snm.MediaPresent = (BOOLEAN)((RegSts & E1K_REG_STATUS_LU) != 0);
+
+ Status = E1kNetMacAddrRead(Dev);
+ CopyMem (&Dev->Snm.PermanentAddress, &Dev->Snm.CurrentAddress,
+ sizeof (E1K_NET_MAC));
+ SetMem (&Dev->Snm.BroadcastAddress, sizeof (E1K_NET_MAC), 0xFF);
+
+ //
+ // E1kNetExitBoot() is queued by ExitBootServices(); its purpose is to
+ // cancel any pending requests. The TPL_CALLBACK reasoning is
+ // identical to the one above. There's one difference: this kind of
+ // event is "globally visible", which means it can be signalled as soon as
+ // we create it. We haven't raised our TPL here, hence E1kNetExitBoot()
+ // could be entered immediately. E1kNetExitBoot() checks Dev->Snm.State,
+ // so we're safe.
+ //
+ Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK,
+ &E1kNetExitBoot, Dev, &Dev->ExitBoot);
+ if (EFI_ERROR (Status)) {
+ goto CloseWaitForPacket;
+ }
+
+ return EFI_SUCCESS;
+
+CloseWaitForPacket:
+ gBS->CloseEvent (Dev->Snp.WaitForPacket);
+ return Status;
+}
+
+
+/**
+ Release any resources allocated by E1kNetSnpPopulate().
+
+ This function may only be called by E1kNetDriverBindingStart(), when
+ rolling back a partial, failed driver instance creation, and by
+ E1kNetDriverBindingStop(), when disconnecting a virtio-net device from the
+ driver.
+
+ @param[in,out] Dev The E1K_NET_DEV driver instance being destroyed.
+*/
+STATIC
+VOID
+EFIAPI
+E1kNetSnpEvacuate (
+ IN OUT E1K_NET_DEV *Dev
+ )
+{
+ //
+ // This function runs either at TPL_CALLBACK already (from
+ // E1kNetDriverBindingStop()), or it is part of a teardown following
+ // a partial, failed construction in E1kNetDriverBindingStart(), when
+ // WaitForPacket was never accessible to the world.
+ //
+ gBS->CloseEvent (Dev->ExitBoot);
+ gBS->CloseEvent (Dev->Snp.WaitForPacket);
+}
+
+
+/**
+ Tests to see if this driver supports a given controller. If a child device is
+ provided, it further tests to see if this driver supports creating a handle
+ for the specified child device.
+
+ This function checks to see if the driver specified by This supports the
+ device specified by ControllerHandle. Drivers will typically use the device
+ path attached to ControllerHandle and/or the services from the bus I/O
+ abstraction attached to ControllerHandle to determine if the driver supports
+ ControllerHandle. This function may be called many times during platform
+ initialization. In order to reduce boot times, the tests performed by this
+ function must be very small, and take as little time as possible to execute.
+ This function must not change the state of any hardware devices, and this
+ function must be aware that the device specified by ControllerHandle may
+ already be managed by the same driver or a different driver. This function
+ must match its calls to AllocatePages() with FreePages(), AllocatePool() with
+ FreePool(), and OpenProtocol() with CloseProtocol(). Because ControllerHandle
+ may have been previously started by the same driver, if a protocol is already
+ in the opened state, then it must not be closed with CloseProtocol(). This is
+ required to guarantee the state of ControllerHandle is not modified by this
+ function.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL
+ instance.
+ @param[in] ControllerHandle The handle of the controller to test. This
+ handle must support a protocol interface
+ that supplies an I/O abstraction to the
+ driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a
+ device path. This parameter is ignored by
+ device drivers, and is optional for bus
+ drivers. For bus drivers, if this parameter
+ is not NULL, then the bus driver must
+ determine if the bus controller specified by
+ ControllerHandle and the child controller
+ specified by RemainingDevicePath are both
+ supported by this bus driver.
+
+ @retval EFI_SUCCESS The device specified by ControllerHandle and
+ RemainingDevicePath is supported by the
+ driver specified by This.
+ @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed
+ by the driver specified by This.
+ @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed
+ by a different driver or an application that
+ requires exclusive access. Currently not
+ implemented.
+ @retval EFI_UNSUPPORTED The device specified by ControllerHandle and
+ RemainingDevicePath is not supported by the
+ driver specified by This.
+**/
+
+STATIC
+EFI_STATUS
+EFIAPI
+E1kNetDriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ PCI_TYPE00 Pci;
+
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiPciIoProtocolGuid,
+ (VOID **)&PciIo,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = PciIo->Pci.Read (
+ PciIo,
+ EfiPciIoWidthUint32,
+ 0,
+ sizeof (Pci) / sizeof (UINT32),
+ &Pci
+ );
+ if (EFI_ERROR (Status)) {
+ goto Done;
+ }
+
+ if (Pci.Hdr.VendorId == INTEL_PCI_VENDOR_ID &&
+ (Pci.Hdr.DeviceId == INTEL_82540EM_PCI_DEVICE_ID ||
+ Pci.Hdr.DeviceId == INTEL_82543GC_PCI_DEVICE_ID ||
+ Pci.Hdr.DeviceId == INTEL_82545EM_PCI_DEVICE_ID)) {
+ Status = EFI_SUCCESS;
+ } else {
+ Status = EFI_UNSUPPORTED;
+ }
+
+Done:
+ gBS->CloseProtocol (
+ ControllerHandle,
+ &gEfiPciIoProtocolGuid,
+ This->DriverBindingHandle,
+ ControllerHandle
+ );
+ return Status;
+}
+
+
+/**
+ Starts a device controller or a bus controller.
+
+ The Start() function is designed to be invoked from the EFI boot service
+ ConnectController(). As a result, much of the error checking on the
+ parameters to Start() has been moved into this common boot service. It is
+ legal to call Start() from other locations, but the following calling
+ restrictions must be followed, or the system behavior will not be
+ deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE.
+ 2. If RemainingDevicePath is not NULL, then it must be a pointer to a
+ naturally aligned EFI_DEVICE_PATH_PROTOCOL.
+ 3. Prior to calling Start(), the Supported() function for the driver
+ specified by This must have been called with the same calling parameters,
+ and Supported() must have returned EFI_SUCCESS.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL
+ instance.
+ @param[in] ControllerHandle The handle of the controller to start. This
+ handle must support a protocol interface
+ that supplies an I/O abstraction to the
+ driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a
+ device path. This parameter is ignored by
+ device drivers, and is optional for bus
+ drivers. For a bus driver, if this parameter
+ is NULL, then handles for all the children
+ of Controller are created by this driver.
+ If this parameter is not NULL and the first
+ Device Path Node is not the End of Device
+ Path Node, then only the handle for the
+ child device specified by the first Device
+ Path Node of RemainingDevicePath is created
+ by this driver. If the first Device Path
+ Node of RemainingDevicePath is the End of
+ Device Path Node, no child handle is created
+ by this driver.
+
+ @retval EFI_SUCCESS The device was started.
+ @retval EFI_DEVICE_ERROR The device could not be started due to a
+ device error.Currently not implemented.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a
+ lack of resources.
+ @retval Others The driver failed to start the device.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+E1kNetDriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ E1K_NET_DEV *Dev;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ MAC_ADDR_DEVICE_PATH MacNode;
+
+ DEBUG((DEBUG_INFO, "E1kNetControllerStart:\n"));
+
+ Dev = AllocateZeroPool (sizeof (*Dev));
+ if (Dev == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Dev->Signature = E1K_NET_DEV_SIGNATURE;
+
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiPciIoProtocolGuid,
+ (VOID **)&Dev->PciIo,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ goto FreePool;
+ }
+
+ Status = Dev->PciIo->Attributes (
+ Dev->PciIo,
+ EfiPciIoAttributeOperationGet,
+ 0,
+ &Dev->OriginalPciAttributes
+ );
+ if (EFI_ERROR (Status)) {
+ goto CloseProtocol;
+ }
+
+ //
+ // Enable I/O Space & Bus-Mastering
+ //
+ Status = Dev->PciIo->Attributes (
+ Dev->PciIo,
+ EfiPciIoAttributeOperationEnable,
+ (EFI_PCI_IO_ATTRIBUTE_IO |
+ EFI_PCI_IO_ATTRIBUTE_BUS_MASTER),
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ goto CloseProtocol;
+ }
+
+ //
+ // Signal device supports 64-bit DMA addresses
+ //
+ Status = Dev->PciIo->Attributes (
+ Dev->PciIo,
+ EfiPciIoAttributeOperationEnable,
+ EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ //
+ // Warn user that device will only be using 32-bit DMA addresses.
+ //
+ // Note that this does not prevent the device/driver from working
+ // and therefore we only warn and continue as usual.
+ //
+ DEBUG ((
+ DEBUG_WARN,
+ "%a: failed to enable 64-bit DMA addresses\n",
+ __FUNCTION__
+ ));
+ }
+
+ DEBUG((DEBUG_INFO, "E1kNetControllerStart: Resetting NIC\n"));
+ Status = E1kNetDevReset (Dev);
+ if (EFI_ERROR (Status)) {
+ goto RestoreAttributes;
+ }
+
+ //
+ // now we can run a basic one-shot e1000 initialization required to
+ // retrieve the MAC address
+ //
+ DEBUG((DEBUG_INFO, "E1kNetControllerStart: Populating SNP interface\n"));
+ Status = E1kNetSnpPopulate (Dev);
+ if (EFI_ERROR (Status)) {
+ goto UninitDev;
+ }
+
+ //
+ // get the device path of the e1000 device -- one-shot open
+ //
+ Status = gBS->OpenProtocol (ControllerHandle, &gEfiDevicePathProtocolGuid,
+ (VOID **)&DevicePath, This->DriverBindingHandle,
+ ControllerHandle, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ if (EFI_ERROR (Status)) {
+ goto Evacuate;
+ }
+
+ //
+ // create another device path that has the MAC address appended
+ //
+ MacNode.Header.Type = MESSAGING_DEVICE_PATH;
+ MacNode.Header.SubType = MSG_MAC_ADDR_DP;
+ SetDevicePathNodeLength (&MacNode, sizeof MacNode);
+ CopyMem (&MacNode.MacAddress, &Dev->Snm.CurrentAddress,
+ sizeof (EFI_MAC_ADDRESS));
+ MacNode.IfType = Dev->Snm.IfType;
+
+ Dev->MacDevicePath = AppendDevicePathNode (DevicePath, &MacNode.Header);
+ if (Dev->MacDevicePath == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Evacuate;
+ }
+
+ //
+ // create a child handle with the Simple Network Protocol and the new
+ // device path installed on it
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (&Dev->MacHandle,
+ &gEfiSimpleNetworkProtocolGuid, &Dev->Snp,
+ &gEfiDevicePathProtocolGuid, Dev->MacDevicePath,
+ NULL);
+ if (EFI_ERROR (Status)) {
+ goto FreeMacDevicePath;
+ }
+
+ DEBUG((DEBUG_INFO, "E1kNetControllerStart: returns EFI_SUCCESS\n"));
+ return EFI_SUCCESS;
+
+FreeMacDevicePath:
+ FreePool (Dev->MacDevicePath);
+
+Evacuate:
+ E1kNetSnpEvacuate (Dev);
+
+UninitDev:
+ E1kNetDevReset (Dev);
+
+RestoreAttributes:
+ Dev->PciIo->Attributes (
+ Dev->PciIo,
+ EfiPciIoAttributeOperationSet,
+ Dev->OriginalPciAttributes,
+ NULL
+ );
+
+CloseProtocol:
+ gBS->CloseProtocol (
+ ControllerHandle,
+ &gEfiPciIoProtocolGuid,
+ This->DriverBindingHandle,
+ ControllerHandle
+ );
+
+FreePool:
+ FreePool (Dev);
+
+ DEBUG((DEBUG_INFO, "E1kNetControllerStart: returns %u\n", Status));
+ return Status;
+}
+
+/**
+ Stops a device controller or a bus controller.
+
+ The Stop() function is designed to be invoked from the EFI boot service
+ DisconnectController(). As a result, much of the error checking on the
+ parameters to Stop() has been moved into this common boot service. It is
+ legal to call Stop() from other locations, but the following calling
+ restrictions must be followed, or the system behavior will not be
+ deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous
+ call to this same driver's Start() function.
+ 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a
+ valid EFI_HANDLE. In addition, all of these handles must have been created
+ in this driver's Start() function, and the Start() function must have
+ called OpenProtocol() on ControllerHandle with an Attribute of
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL
+ instance.
+ @param[in] ControllerHandle A handle to the device being stopped. The
+ handle must support a bus specific I/O
+ protocol for the driver to use to stop the
+ device.
+ @param[in] NumberOfChildren The number of child device handles in
+ ChildHandleBuffer.
+ @param[in] ChildHandleBuffer An array of child handles to be freed. May be
+ NULL if NumberOfChildren is 0.
+
+ @retval EFI_SUCCESS The device was stopped.
+ @retval EFI_DEVICE_ERROR The device could not be stopped due to a device
+ error.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+E1kNetDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ )
+{
+ if (NumberOfChildren > 0) {
+ //
+ // free all resources for whose access we need the child handle, because
+ // the child handle is going away
+ //
+ EFI_STATUS Status;
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
+ E1K_NET_DEV *Dev;
+ EFI_TPL OldTpl;
+
+ ASSERT (NumberOfChildren == 1);
+
+ Status = gBS->OpenProtocol (ChildHandleBuffer[0],
+ &gEfiSimpleNetworkProtocolGuid, (VOID **)&Snp,
+ This->DriverBindingHandle, ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ ASSERT_EFI_ERROR (Status);
+ Dev = E1K_NET_FROM_SNP (Snp);
+
+ //
+ // prevent any interference with WaitForPacket
+ //
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ ASSERT (Dev->MacHandle == ChildHandleBuffer[0]);
+ if (Dev->Snm.State != EfiSimpleNetworkStopped) {
+ //
+ // device in use, cannot stop driver instance
+ //
+ Status = EFI_DEVICE_ERROR;
+ }
+ else {
+ gBS->UninstallMultipleProtocolInterfaces (Dev->MacHandle,
+ &gEfiDevicePathProtocolGuid, Dev->MacDevicePath,
+ &gEfiSimpleNetworkProtocolGuid, &Dev->Snp,
+ NULL);
+ FreePool (Dev->MacDevicePath);
+ E1kNetSnpEvacuate (Dev);
+
+ Dev->PciIo->Attributes (
+ Dev->PciIo,
+ EfiPciIoAttributeOperationSet,
+ Dev->OriginalPciAttributes,
+ NULL
+ );
+
+ gBS->CloseProtocol (
+ ControllerHandle,
+ &gEfiPciIoProtocolGuid,
+ This->DriverBindingHandle,
+ ControllerHandle
+ );
+
+ FreePool (Dev);
+ }
+
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+EFI_DRIVER_BINDING_PROTOCOL gE1kNetDriverBinding = {
+ &E1kNetDriverBindingSupported,
+ &E1kNetDriverBindingStart,
+ &E1kNetDriverBindingStop,
+ 0x10,
+ NULL,
+ NULL
+};
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/E1kHwIo.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/E1kHwIo.c
new file mode 100644
index 00000000..f203048b
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/E1kHwIo.c
@@ -0,0 +1,182 @@
+/** @file
+
+ This file implements the hardware register access functions of the e1000 driver.
+
+ Copyright (c) 2021, Oracle and/or its affiliates.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <IndustryStandard/Pci.h>
+#include <Library/UefiLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+#include "E1kNet.h"
+
+EFI_STATUS
+EFIAPI
+E1kNetRegWrite32 (
+ IN E1K_NET_DEV *Dev,
+ IN UINT32 Addr,
+ IN UINT32 Data
+ )
+{
+ EFI_STATUS Status;
+
+ Status = Dev->PciIo->Io.Write (
+ Dev->PciIo,
+ EfiPciIoWidthUint32,
+ PCI_BAR_IDX2,
+ 0, // IOADDR
+ 1,
+ &Addr
+ );
+ if (!EFI_ERROR (Status))
+ {
+ Status = Dev->PciIo->Io.Write (
+ Dev->PciIo,
+ EfiPciIoWidthUint32,
+ PCI_BAR_IDX2,
+ 4, // IODATA
+ 1,
+ &Data
+ );
+ }
+
+ return Status;
+}
+
+EFI_STATUS
+EFIAPI
+E1kNetRegRead32 (
+ IN E1K_NET_DEV *Dev,
+ IN UINT32 Addr,
+ OUT UINT32 *Data
+ )
+{
+ EFI_STATUS Status;
+
+ Status = Dev->PciIo->Io.Write (
+ Dev->PciIo,
+ EfiPciIoWidthUint32,
+ PCI_BAR_IDX2,
+ 0, // IOADDR
+ 1,
+ &Addr
+ );
+ if (!EFI_ERROR (Status))
+ {
+ return Dev->PciIo->Io.Read (
+ Dev->PciIo,
+ EfiPciIoWidthUint32,
+ PCI_BAR_IDX2,
+ 4, // IODATA
+ 1,
+ Data
+ );
+ }
+
+ return Status;
+}
+
+EFI_STATUS
+EFIAPI
+E1kNetRegSet32 (
+ IN E1K_NET_DEV *Dev,
+ IN UINT32 Addr,
+ IN UINT32 Set)
+{
+ UINT32 Reg;
+ EFI_STATUS Status;
+
+ Status = E1kNetRegRead32 (Dev, Addr, &Reg);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Reg |= Set;
+ Status = E1kNetRegWrite32 (Dev, Addr, Reg);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+E1kNetRegClear32 (
+ IN E1K_NET_DEV *Dev,
+ IN UINT32 Addr,
+ IN UINT32 Clear)
+{
+ UINT32 Reg;
+ EFI_STATUS Status;
+
+ Status = E1kNetRegRead32 (Dev, Addr, &Reg);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Reg &= ~Clear;
+ Status = E1kNetRegWrite32 (Dev, Addr, Reg);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+E1kNetDevReset (
+ IN E1K_NET_DEV *Dev
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Reset hardware
+ //
+ Status = E1kNetRegSet32 (Dev, E1K_REG_CTRL, E1K_REG_CTRL_RST);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Wait for the reset to complete
+ //
+ for (;;)
+ {
+ UINT32 Ctrl;
+
+ Status = E1kNetRegRead32 (Dev, E1K_REG_CTRL, &Ctrl);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ /// @todo Timeout?
+ if (!(Ctrl & E1K_REG_CTRL_RST))
+ break;
+ }
+
+ //
+ // Reset the PHY.
+ //
+ Status = E1kNetRegSet32 (Dev, E1K_REG_CTRL, E1K_REG_CTRL_PHY_RST);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Wait for the specified amount of 3us and de-assert the PHY reset signal.
+ //
+ gBS->Stall(3);
+ Status = E1kNetRegClear32 (Dev, E1K_REG_CTRL, E1K_REG_CTRL_PHY_RST);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/E1kNet.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/E1kNet.h
new file mode 100644
index 00000000..ea224eff
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/E1kNet.h
@@ -0,0 +1,350 @@
+/** @file
+
+ Internal definitions for the virtio-net driver, which produces Simple Network
+ Protocol instances for virtio-net devices.
+
+ Copyright (c) 2021, Oracle and/or its affiliates.
+ Copyright (c) 2017, AMD Inc, All rights reserved.
+ Copyright (C) 2013, Red Hat, Inc.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef _E1K_NET_DXE_H_
+#define _E1K_NET_DXE_H_
+
+#include <Library/DebugLib.h>
+#include <Protocol/PciIo.h>
+#include <Protocol/PciRootBridgeIo.h>
+#include <Protocol/ComponentName.h>
+#include <Protocol/ComponentName2.h>
+#include <Protocol/DevicePath.h>
+#include <Protocol/DriverBinding.h>
+#include <Protocol/SimpleNetwork.h>
+#include <Library/OrderedCollectionLib.h>
+
+#include "E1kNetHw.h"
+
+#define E1K_NET_DEV_SIGNATURE SIGNATURE_32 ('E','1','K','N')
+
+//
+// maximum number of pending packets, separately for each direction
+//
+#define E1K_NET_MAX_PENDING 64
+
+//
+// State diagram:
+//
+// | ^
+// | |
+// BindingStart BindingStop
+// +SnpPopulate |
+// ++GetFeatures |
+// | |
+// v |
+// +---------+ virtio-net device is reset, no resources are
+// | stopped | allocated for traffic, but MAC address has
+// +---------+ been retrieved
+// | ^
+// | |
+// SNP.Start SNP.Stop
+// | |
+// v |
+// +---------+
+// | started | functionally identical to stopped
+// +---------+
+// | ^
+// | |
+// SNP.Initialize SNP.Shutdown
+// | |
+// v |
+// +-------------+ Virtio-net setup complete, including DRIVER_OK
+// | initialized | bit. The receive queue is populated with
+// +-------------+ requests; McastIpToMac, GetStatus, Transmit,
+// Receive are callable.
+//
+
+typedef struct {
+ //
+ // Parts of this structure are initialized / torn down in various functions
+ // at various call depths. The table to the right should make it easier to
+ // track them.
+ //
+ // field init function
+ // ------------------ ------------------------------
+ UINT32 Signature; // VirtioNetDriverBindingStart
+ EFI_PCI_IO_PROTOCOL *PciIo; // VirtioNetDriverBindingStart
+ UINT64 OriginalPciAttributes; // VirtioNetDriverBindingStart
+ EFI_SIMPLE_NETWORK_PROTOCOL Snp; // VirtioNetSnpPopulate
+ EFI_SIMPLE_NETWORK_MODE Snm; // VirtioNetSnpPopulate
+ EFI_EVENT ExitBoot; // VirtioNetSnpPopulate
+ EFI_DEVICE_PATH_PROTOCOL *MacDevicePath; // VirtioNetDriverBindingStart
+ EFI_HANDLE MacHandle; // VirtioNetDriverBindingStart
+
+ E1K_RX_DESC *RxRing; // VirtioNetInitRing
+ UINT8 *RxBuf; // E1kNetInitRx
+ UINT32 RdhLastSeen; // E1kNetInitRx
+ UINTN RxBufNrPages; // E1kNetInitRx
+ EFI_PHYSICAL_ADDRESS RxBufDeviceBase; // E1kNetInitRx
+ EFI_PHYSICAL_ADDRESS RxDeviceBase; // E1kNetInitRx
+ VOID *RxMap; // E1kNetInitRx
+
+ UINT16 TxMaxPending; // E1kNetInitTx
+ UINT16 TxCurPending; // E1kNetInitTx
+ E1K_TX_DESC *TxRing; // E1kNetInitTx
+ VOID *TxRingMap; // E1kNetInitTx
+ UINT16 TxLastUsed; // E1kNetInitTx
+ UINT32 TdhLastSeen; // E1kNetInitTx
+ ORDERED_COLLECTION *TxBufCollection; // E1kNetInitTx
+} E1K_NET_DEV;
+
+
+//
+// In order to avoid duplication of interface documentation, please find all
+// leading comments near the respective function / variable definitions (not
+// the declarations here), which is where your code editor of choice takes you
+// anyway when jumping to a function.
+//
+
+//
+// utility macros
+//
+#define E1K_NET_FROM_SNP(SnpPointer) \
+ CR (SnpPointer, E1K_NET_DEV, Snp, E1K_NET_DEV_SIGNATURE)
+
+//
+// component naming
+//
+extern EFI_COMPONENT_NAME_PROTOCOL gE1kNetComponentName;
+extern EFI_COMPONENT_NAME2_PROTOCOL gE1kNetComponentName2;
+
+//
+// driver binding
+//
+extern EFI_DRIVER_BINDING_PROTOCOL gE1kNetDriverBinding;
+
+//
+// member functions implementing the Simple Network Protocol
+//
+EFI_STATUS
+EFIAPI
+E1kNetStart (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This
+ );
+
+EFI_STATUS
+EFIAPI
+E1kNetStop (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This
+ );
+
+EFI_STATUS
+EFIAPI
+E1kNetInitialize (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ IN UINTN ExtraRxBufferSize OPTIONAL,
+ IN UINTN ExtraTxBufferSize OPTIONAL
+ );
+
+EFI_STATUS
+EFIAPI
+E1kNetReset (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ IN BOOLEAN ExtendedVerification
+ );
+
+EFI_STATUS
+EFIAPI
+E1kNetShutdown (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This
+ );
+
+EFI_STATUS
+EFIAPI
+E1kNetReceiveFilters (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ IN UINT32 Enable,
+ IN UINT32 Disable,
+ IN BOOLEAN ResetMCastFilter,
+ IN UINTN MCastFilterCnt OPTIONAL,
+ IN EFI_MAC_ADDRESS *MCastFilter OPTIONAL
+ );
+
+EFI_STATUS
+EFIAPI
+E1kNetStationAddress (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ IN BOOLEAN Reset,
+ IN EFI_MAC_ADDRESS *New OPTIONAL
+ );
+
+EFI_STATUS
+EFIAPI
+E1kNetStatistics (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ IN BOOLEAN Reset,
+ IN OUT UINTN *StatisticsSize OPTIONAL,
+ OUT EFI_NETWORK_STATISTICS *StatisticsTable OPTIONAL
+ );
+
+EFI_STATUS
+EFIAPI
+E1kNetMcastIpToMac (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ IN BOOLEAN IPv6,
+ IN EFI_IP_ADDRESS *Ip,
+ OUT EFI_MAC_ADDRESS *Mac
+ );
+
+EFI_STATUS
+EFIAPI
+E1kNetNvData (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ IN BOOLEAN ReadWrite,
+ IN UINTN Offset,
+ IN UINTN BufferSize,
+ IN OUT VOID *Buffer
+ );
+
+EFI_STATUS
+EFIAPI
+E1kNetGetStatus (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ OUT UINT32 *InterruptStatus OPTIONAL,
+ OUT VOID **TxBuf OPTIONAL
+ );
+
+EFI_STATUS
+EFIAPI
+E1kNetTransmit (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ IN UINTN HeaderSize,
+ IN UINTN BufferSize,
+ IN /* +OUT! */ VOID *Buffer,
+ IN EFI_MAC_ADDRESS *SrcAddr OPTIONAL,
+ IN EFI_MAC_ADDRESS *DestAddr OPTIONAL,
+ IN UINT16 *Protocol OPTIONAL
+ );
+
+EFI_STATUS
+EFIAPI
+E1kNetReceive (
+ IN EFI_SIMPLE_NETWORK_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
+ );
+
+//
+// utility functions shared by various SNP member functions
+//
+VOID
+EFIAPI
+E1kNetShutdownRx (
+ IN OUT E1K_NET_DEV *Dev
+ );
+
+VOID
+EFIAPI
+E1kNetShutdownTx (
+ IN OUT E1K_NET_DEV *Dev
+ );
+
+//
+// utility functions to map caller-supplied Tx buffer system physical address
+// to a device address and vice versa
+//
+EFI_STATUS
+EFIAPI
+E1kNetMapTxBuf (
+ IN E1K_NET_DEV *Dev,
+ IN VOID *Buffer,
+ IN UINTN NumberOfBytes,
+ OUT EFI_PHYSICAL_ADDRESS *DeviceAddress
+ );
+
+EFI_STATUS
+EFIAPI
+E1kNetUnmapTxBuf (
+ IN E1K_NET_DEV *Dev,
+ OUT VOID **Buffer,
+ IN EFI_PHYSICAL_ADDRESS DeviceAddress
+ );
+
+INTN
+EFIAPI
+E1kNetTxBufMapInfoCompare (
+ IN CONST VOID *UserStruct1,
+ IN CONST VOID *UserStruct2
+ );
+
+INTN
+EFIAPI
+E1kNetTxBufDeviceAddressCompare (
+ IN CONST VOID *StandaloneKey,
+ IN CONST VOID *UserStruct
+ );
+
+
+//
+// event callbacks
+//
+VOID
+EFIAPI
+E1kNetIsPacketAvailable (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+VOID
+EFIAPI
+E1kNetExitBoot (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+//
+// Hardware I/O functions.
+//
+EFI_STATUS
+EFIAPI
+E1kNetRegWrite32 (
+ IN E1K_NET_DEV *Dev,
+ IN UINT32 Addr,
+ IN UINT32 Data
+ );
+
+EFI_STATUS
+EFIAPI
+E1kNetRegRead32 (
+ IN E1K_NET_DEV *Dev,
+ IN UINT32 Addr,
+ OUT UINT32 *Data
+ );
+
+EFI_STATUS
+EFIAPI
+E1kNetRegSet32 (
+ IN E1K_NET_DEV *Dev,
+ IN UINT32 Addr,
+ IN UINT32 Set
+ );
+
+EFI_STATUS
+EFIAPI
+E1kNetRegClear32 (
+ IN E1K_NET_DEV *Dev,
+ IN UINT32 Addr,
+ IN UINT32 Clear
+ );
+
+EFI_STATUS
+EFIAPI
+E1kNetDevReset (
+ IN E1K_NET_DEV *Dev
+ );
+
+#endif // _E1K_NET_DXE_H_
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/E1kNet.inf b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/E1kNet.inf
new file mode 100644
index 00000000..7c9ba681
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/E1kNet.inf
@@ -0,0 +1,60 @@
+
+## @file
+#
+# This driver produces Simple Network Protocol instances for e1000 based
+# devices.
+#
+# Copyright (C) 2021, Oracle and/or its affiliates.
+# Copyright (C) 2013, Red Hat, Inc.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = E1kNetDxe
+ FILE_GUID = FFCC3DF0-C1EC-11EB-BA2F-0800200C9A66
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = E1kNetEntryPoint
+
+[Sources]
+ ComponentName.c
+ DriverBinding.c
+ EntryPoint.c
+ Events.c
+ SnpGetStatus.c
+ SnpInitialize.c
+ SnpMcastIpToMac.c
+ SnpReceive.c
+ SnpReceiveFilters.c
+ SnpSharedHelpers.c
+ SnpShutdown.c
+ SnpStart.c
+ SnpStop.c
+ SnpTransmit.c
+ SnpUnsupported.c
+ E1kHwIo.c
+ E1kNet.h
+ E1kNetHw.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+ VBoxPkg/VBoxPkg.dec
+
+[LibraryClasses]
+ BaseMemoryLib
+ DebugLib
+ DevicePathLib
+ MemoryAllocationLib
+ OrderedCollectionLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ UefiLib
+
+[Protocols]
+ gEfiSimpleNetworkProtocolGuid ## BY_START
+ gEfiDevicePathProtocolGuid ## BY_START
+ gEfiPciIoProtocolGuid ## TO_START
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/E1kNetHw.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/E1kNetHw.h
new file mode 100644
index 00000000..90afcf8a
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/E1kNetHw.h
@@ -0,0 +1,105 @@
+/** @file
+
+ E1000 hardware interface definitions.
+
+ Copyright (c) 2021, Oracle and/or its affiliates.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef _E1K_NET_HW_H_
+#define _E1K_NET_HW_H_
+
+#define INTEL_PCI_VENDOR_ID 0x8086
+#define INTEL_82540EM_PCI_DEVICE_ID 0x100e
+#define INTEL_82543GC_PCI_DEVICE_ID 0x1004
+#define INTEL_82545EM_PCI_DEVICE_ID 0x100f
+
+//
+// Receive descriptor.
+//
+typedef struct {
+ UINT32 AddrBufferLow;
+ UINT32 AddrBufferHigh;
+ UINT16 BufferLength;
+ UINT16 Checksum;
+ UINT8 Status;
+ UINT8 Errors;
+ UINT16 Special;
+} E1K_RX_DESC;
+
+#define E1K_RX_STATUS_DONE BIT0
+#define E1K_RX_STATUS_EOP BIT1
+
+#define E1K_RX_ERROR_CE BIT0
+#define E1K_RX_ERROR_SEQ BIT2
+#define E1K_RX_ERROR_CXE BIT4
+#define E1K_RX_ERROR_RXE BIT7
+
+//
+// Transmit descriptor.
+//
+typedef struct {
+ UINT32 AddrBufferLow;
+ UINT32 AddrBufferHigh;
+ UINT16 BufferLength;
+ UINT8 ChecksumOffset;
+ UINT8 Command;
+ UINT8 Status;
+ UINT8 ChecksumStart;
+ UINT16 Special;
+} E1K_TX_DESC;
+
+#define E1K_TX_CMD_EOP BIT0
+#define E1K_TX_CMD_FCS BIT1
+#define E1K_TX_CMD_RS BIT3
+
+#define E1K_REG_CTRL 0x00000000
+# define E1K_REG_CTRL_ASDE BIT5
+# define E1K_REG_CTRL_SLU BIT6
+# define E1K_REG_CTRL_RST BIT26
+# define E1K_REG_CTRL_PHY_RST BIT31
+#define E1K_REG_STATUS 0x00000008
+# define E1K_REG_STATUS_LU BIT1
+#define E1K_REG_EECD 0x00000010
+#define E1K_REG_EERD 0x00000014
+# define E1K_REG_EERD_START BIT0
+# define E1K_REG_EERD_DONE BIT4
+# define E1K_REG_EERD_DATA_GET(x) (((x) >> 16) & 0xffff)
+#define E1K_REG_ICR 0x000000c0
+#define E1K_REG_ITR 0x000000c4
+#define E1K_REG_ICS 0x000000c8
+#define E1K_REG_IMS 0x000000d0
+#define E1K_REG_IMC 0x000000d8
+#define E1K_REG_RCTL 0x00000100
+# define E1K_REG_RCTL_EN BIT1
+# define E1K_REG_RCTL_MPE BIT4
+# define E1K_REG_RCTL_BSIZE_MASK 0x00030000
+#define E1K_REG_RDBAL 0x00002800
+#define E1K_REG_RDBAH 0x00002804
+#define E1K_REG_RDLEN 0x00002808
+#define E1K_REG_RDH 0x00002810
+#define E1K_REG_RDT 0x00002818
+#define E1K_REG_RDTR 0x00002820
+#define E1K_REG_TCTL 0x00000400
+# define E1K_REG_TCTL_EN BIT1
+# define E1K_REG_TCTL_PSP BIT3
+#define E1K_REG_TIPG 0x00000410
+#define E1K_REG_TDBAL 0x00003800
+#define E1K_REG_TDBAH 0x00003804
+#define E1K_REG_TDLEN 0x00003808
+#define E1K_REG_TDH 0x00003810
+#define E1K_REG_TDT 0x00003818
+#define E1K_REG_RAL 0x00005400
+#define E1K_REG_RAH 0x00005404
+# define E1K_REG_RAH_AV BIT31
+
+//
+// MAC address.
+//
+typedef struct
+{
+ UINT8 Mac[6];
+} E1K_NET_MAC;
+
+#endif // _E1K_NET_HW_H_
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/EntryPoint.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/EntryPoint.c
new file mode 100644
index 00000000..d4cf35fd
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/EntryPoint.c
@@ -0,0 +1,45 @@
+/** @file
+
+ This file implements the entry point of the e1000 driver.
+
+ Copyright (c) 2021, Oracle and/or its affiliates.
+ Copyright (C) 2013, Red Hat, Inc.
+ Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/UefiLib.h>
+
+#include "E1kNet.h"
+
+/**
+ This is the declaration of an EFI image entry point. This entry point is the
+ same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including both
+ device drivers and bus drivers.
+
+ @param ImageHandle The firmware allocated handle for the UEFI
+ image.
+ @param SystemTable A pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval Others An unexpected error occurred.
+**/
+
+EFI_STATUS
+EFIAPI
+E1kNetEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ return EfiLibInstallDriverBindingComponentName2 (
+ ImageHandle,
+ SystemTable,
+ &gE1kNetDriverBinding,
+ ImageHandle,
+ &gE1kNetComponentName,
+ &gE1kNetComponentName2
+ );
+}
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/Events.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/Events.c
new file mode 100644
index 00000000..e51aee58
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/Events.c
@@ -0,0 +1,82 @@
+/** @file
+
+ Implements
+ - the SNM.WaitForPacket EVT_NOTIFY_WAIT event,
+ - the EVT_SIGNAL_EXIT_BOOT_SERVICES event
+ for the e1000 driver.
+
+ Copyright (c) 2021, Oracle and/or its affiliates.
+ Copyright (C) 2013, Red Hat, Inc.
+ Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/BaseLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+#include "E1kNet.h"
+
+/**
+ Invoke a notification event
+
+ @param Event Event whose notification function is being
+ invoked.
+ @param Context The pointer to the notification function's
+ context, which is implementation-dependent.
+
+**/
+
+VOID
+EFIAPI
+E1kNetIsPacketAvailable (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ //
+ // This callback has been enqueued by an external application and is
+ // running at TPL_CALLBACK already.
+ //
+ // The WaitForPacket logic is similar to that of WaitForKey. The former has
+ // almost no documentation in either the UEFI-2.3.1+errC spec or the
+ // DWG-2.3.1, but WaitForKey does have some.
+ //
+ E1K_NET_DEV *Dev;
+ UINT32 RdhCur;
+
+ Dev = Context;
+ if (Dev->Snm.State != EfiSimpleNetworkInitialized) {
+ return;
+ }
+
+ E1kNetRegRead32(Dev, E1K_REG_RDH, &RdhCur);
+
+ if (Dev->RdhLastSeen != RdhCur) {
+ gBS->SignalEvent (Dev->Snp.WaitForPacket);
+ }
+}
+
+VOID
+EFIAPI
+E1kNetExitBoot (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ //
+ // This callback has been enqueued by ExitBootServices() and is running at
+ // TPL_CALLBACK already.
+ //
+ // Shut down pending transfers according to DWG-2.3.1, "25.5.1 Exit Boot
+ // Services Event".
+ //
+ E1K_NET_DEV *Dev;
+
+ DEBUG ((DEBUG_VERBOSE, "%a: Context=0x%p\n", __FUNCTION__, Context));
+ Dev = Context;
+ if (Dev->Snm.State == EfiSimpleNetworkInitialized) {
+ E1kNetDevReset (Dev);
+ }
+}
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpGetStatus.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpGetStatus.c
new file mode 100644
index 00000000..6927b46e
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpGetStatus.c
@@ -0,0 +1,160 @@
+/** @file
+
+ Implementation of the SNP.GetStatus() function and its private helpers if
+ any.
+
+ Copyright (c) 2021, Oracle and/or its affiliates.
+ Copyright (C) 2013, Red Hat, Inc.
+ Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/BaseLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+#include "E1kNet.h"
+
+/**
+ 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
+EFIAPI
+E1kNetGetStatus (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ OUT UINT32 *InterruptStatus OPTIONAL,
+ OUT VOID **TxBuf OPTIONAL
+ )
+{
+ E1K_NET_DEV *Dev;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+ UINT32 TdhCur;
+ UINT32 RdhCur;
+ EFI_PHYSICAL_ADDRESS DeviceAddress;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Dev = E1K_NET_FROM_SNP (This);
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ switch (Dev->Snm.State) {
+ case EfiSimpleNetworkStopped:
+ Status = EFI_NOT_STARTED;
+ goto Exit;
+ case EfiSimpleNetworkStarted:
+ Status = EFI_DEVICE_ERROR;
+ goto Exit;
+ default:
+ break;
+ }
+
+ //
+ // update link status
+ //
+ if (Dev->Snm.MediaPresentSupported) {
+ UINT32 RegSts;
+
+ Status = E1kNetRegRead32(Dev, E1K_REG_STATUS, &RegSts);
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ Dev->Snm.MediaPresent = (BOOLEAN)((RegSts & E1K_REG_STATUS_LU) != 0);
+ }
+
+ E1kNetRegRead32(Dev, E1K_REG_TDH, &TdhCur);
+ E1kNetRegRead32(Dev, E1K_REG_RDH, &RdhCur);
+
+
+ if (InterruptStatus != NULL) {
+ //
+ // report the receive interrupt if there is data available for reception,
+ // report the transmit interrupt if we have transmitted at least one buffer
+ //
+ *InterruptStatus = 0;
+ if (Dev->RdhLastSeen != RdhCur) {
+ *InterruptStatus |= EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT;
+ }
+ if (Dev->TdhLastSeen != TdhCur) {
+ ASSERT (Dev->TxCurPending > 0);
+ *InterruptStatus |= EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT;
+ }
+ }
+
+ if (TxBuf != NULL) {
+ if (Dev->TdhLastSeen == TdhCur) {
+ *TxBuf = NULL;
+ }
+ else {
+ ASSERT (Dev->TxCurPending > 0);
+ ASSERT (Dev->TxCurPending <= Dev->TxMaxPending);
+
+ //
+ // get the device address that has been enqueued for the caller's
+ // transmit buffer
+ //
+ DeviceAddress = Dev->TxRing[Dev->TdhLastSeen].AddrBufferLow;
+ DeviceAddress |= LShiftU64(Dev->TxRing[Dev->TdhLastSeen].AddrBufferHigh, 32);
+
+ Dev->TdhLastSeen = (Dev->TdhLastSeen + 1) % E1K_NET_MAX_PENDING;
+ Dev->TxCurPending--;
+
+ //
+ // Unmap the device address and perform the reverse mapping to find the
+ // caller buffer address.
+ //
+ Status = E1kNetUnmapTxBuf (
+ Dev,
+ TxBuf,
+ DeviceAddress
+ );
+ if (EFI_ERROR (Status)) {
+ //
+ // E1kNetUnmapTxBuf should never fail, if we have reached here
+ // that means our internal state has been corrupted
+ //
+ ASSERT (FALSE);
+ Status = EFI_DEVICE_ERROR;
+ goto Exit;
+ }
+ }
+ }
+
+ Status = EFI_SUCCESS;
+
+Exit:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpInitialize.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpInitialize.c
new file mode 100644
index 00000000..1fec91b4
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpInitialize.c
@@ -0,0 +1,321 @@
+/** @file
+
+ Implementation of the SNP.Initialize() function and its private helpers if
+ any.
+
+ Copyright (c) 2021, Oracle and/or its affiliates.
+ Copyright (c) 2017, AMD Inc, All rights reserved.
+ Copyright (C) 2013, Red Hat, Inc.
+ Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+#include "E1kNet.h"
+
+/**
+ Set up static scaffolding for the E1kNetTransmit() and
+ E1kNetGetStatus() SNP methods.
+
+ This function may only be called by E1kNetInitialize().
+
+ @param[in,out] Dev The E1K_NET_DEV driver instance about to enter the
+ EfiSimpleNetworkInitialized state.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate the stack to track the heads
+ of free descriptor chains or failed to init
+ TxBufCollection.
+ @return Status codes from VIRTIO_DEVICE_PROTOCOL.
+ AllocateSharedPages() or
+ VirtioMapAllBytesInSharedBuffer()
+ @retval EFI_SUCCESS TX setup successful.
+*/
+
+STATIC
+EFI_STATUS
+EFIAPI
+E1kNetInitTx (
+ IN OUT E1K_NET_DEV *Dev
+ )
+{
+ UINTN TxRingSize;
+ EFI_STATUS Status;
+ EFI_PHYSICAL_ADDRESS DeviceAddress;
+ VOID *TxRingBuffer;
+
+ Dev->TxMaxPending = E1K_NET_MAX_PENDING;
+ Dev->TxCurPending = 0;
+ Dev->TxBufCollection = OrderedCollectionInit (
+ E1kNetTxBufMapInfoCompare,
+ E1kNetTxBufDeviceAddressCompare
+ );
+ if (Dev->TxBufCollection == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Exit;
+ }
+
+ //
+ // Allocate TxRing header and map with BusMasterCommonBuffer so that it
+ // can be accessed equally by both processor and device.
+ //
+ TxRingSize = Dev->TxMaxPending * sizeof (*Dev->TxRing);
+ Status = Dev->PciIo->AllocateBuffer (
+ Dev->PciIo,
+ AllocateAnyPages,
+ EfiBootServicesData,
+ EFI_SIZE_TO_PAGES (TxRingSize),
+ &TxRingBuffer,
+ EFI_PCI_ATTRIBUTE_MEMORY_CACHED
+ );
+ if (EFI_ERROR (Status)) {
+ goto UninitTxBufCollection;
+ }
+
+ ZeroMem (TxRingBuffer, TxRingSize);
+
+ Status = Dev->PciIo->Map (
+ Dev->PciIo,
+ EfiPciIoOperationBusMasterCommonBuffer,
+ TxRingBuffer,
+ &TxRingSize,
+ &DeviceAddress,
+ &Dev->TxRingMap
+ );
+ if (EFI_ERROR (Status)) {
+ goto FreeTxRingBuffer;
+ }
+
+ Dev->TxRing = TxRingBuffer;
+ Dev->TdhLastSeen = 0;
+ Dev->TxLastUsed = 0;
+
+ // Program the transmit engine.
+ MemoryFence ();
+ E1kNetRegWrite32(Dev, E1K_REG_TDBAL, (UINT32)DeviceAddress);
+ E1kNetRegWrite32(Dev, E1K_REG_TDBAH, (UINT32)(RShiftU64 (DeviceAddress, 32)));
+ E1kNetRegWrite32(Dev, E1K_REG_TDLEN, (UINT32)TxRingSize);
+ E1kNetRegWrite32(Dev, E1K_REG_TDH, 0);
+ E1kNetRegWrite32(Dev, E1K_REG_TDT, 0);
+ E1kNetRegWrite32(Dev, E1K_REG_TCTL, E1K_REG_TCTL_EN | E1K_REG_TCTL_PSP);
+
+ return EFI_SUCCESS;
+
+FreeTxRingBuffer:
+ Dev->PciIo->FreeBuffer (
+ Dev->PciIo,
+ EFI_SIZE_TO_PAGES (TxRingSize),
+ TxRingBuffer
+ );
+
+UninitTxBufCollection:
+ OrderedCollectionUninit (Dev->TxBufCollection);
+
+Exit:
+ return Status;
+}
+
+
+/**
+ Set up static scaffolding for the E1kNetReceive() SNP method and enable
+ live device operation.
+
+ This function may only be called as E1kNetInitialize()'s final step.
+
+ @param[in,out] Dev The E1K_NET_DEV driver instance about to enter the
+ EfiSimpleNetworkInitialized state.
+
+ @return Status codes from VIRTIO_CFG_WRITE() or
+ VIRTIO_DEVICE_PROTOCOL.AllocateSharedPages or
+ VirtioMapAllBytesInSharedBuffer().
+ @retval EFI_SUCCESS RX setup successful. The device is live and may
+ already be writing to the receive area.
+*/
+
+STATIC
+EFI_STATUS
+EFIAPI
+E1kNetInitRx (
+ IN OUT E1K_NET_DEV *Dev
+ )
+{
+ EFI_STATUS Status;
+ UINTN RxBufSize;
+ UINTN PktIdx;
+ UINTN NumBytes;
+ EFI_PHYSICAL_ADDRESS RxBufDeviceAddress;
+ VOID *RxBuffer;
+
+ //
+ // For each incoming packet we must supply two buffers:
+ // - the recipient for the RX descriptor, plus
+ // - the recipient for the network data (which consists of Ethernet header
+ // and Ethernet payload) which is a 2KB buffer.
+ //
+ RxBufSize = sizeof(*Dev->RxRing) + 2048;
+
+ //
+ // The RxBuf is shared between guest and hypervisor, use
+ // AllocateSharedPages() to allocate this memory region and map it with
+ // BusMasterCommonBuffer so that it can be accessed by both guest and
+ // hypervisor.
+ //
+ NumBytes = E1K_NET_MAX_PENDING * RxBufSize;
+ Dev->RxBufNrPages = EFI_SIZE_TO_PAGES (NumBytes);
+ Status = Dev->PciIo->AllocateBuffer (
+ Dev->PciIo,
+ AllocateAnyPages,
+ EfiBootServicesData,
+ Dev->RxBufNrPages,
+ &RxBuffer,
+ EFI_PCI_ATTRIBUTE_MEMORY_CACHED
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ ZeroMem (RxBuffer, NumBytes);
+
+ Status = Dev->PciIo->Map (
+ Dev->PciIo,
+ EfiPciIoOperationBusMasterCommonBuffer,
+ RxBuffer,
+ &NumBytes,
+ &Dev->RxDeviceBase,
+ &Dev->RxMap
+ );
+ if (EFI_ERROR (Status)) {
+ goto FreeSharedBuffer;
+ }
+
+ Dev->RxRing = RxBuffer;
+ Dev->RxBuf = (UINT8 *)RxBuffer + sizeof(*Dev->RxRing) * E1K_NET_MAX_PENDING;
+ Dev->RdhLastSeen = 0;
+
+ // Set up the RX descriptors.
+ Dev->RxBufDeviceBase = Dev->RxDeviceBase + sizeof(*Dev->RxRing) * E1K_NET_MAX_PENDING;
+ RxBufDeviceAddress = Dev->RxBufDeviceBase;
+ for (PktIdx = 0; PktIdx < E1K_NET_MAX_PENDING; ++PktIdx) {
+ Dev->RxRing[PktIdx].AddrBufferLow = (UINT32)RxBufDeviceAddress;
+ Dev->RxRing[PktIdx].AddrBufferHigh = (UINT32)RShiftU64(RxBufDeviceAddress, 32);
+ Dev->RxRing[PktIdx].BufferLength = 2048;
+
+ RxBufDeviceAddress += Dev->RxRing[PktIdx].BufferLength;
+ }
+
+ // Program the receive engine.
+ MemoryFence ();
+ E1kNetRegWrite32(Dev, E1K_REG_RDBAL, (UINT32)Dev->RxDeviceBase);
+ E1kNetRegWrite32(Dev, E1K_REG_RDBAH, (UINT32)(RShiftU64 (Dev->RxDeviceBase, 32)));
+ E1kNetRegWrite32(Dev, E1K_REG_RDLEN, sizeof(*Dev->RxRing) * E1K_NET_MAX_PENDING);
+ E1kNetRegWrite32(Dev, E1K_REG_RDH, 0);
+ E1kNetRegWrite32(Dev, E1K_REG_RDT, E1K_NET_MAX_PENDING - 1);
+ E1kNetRegClear32(Dev, E1K_REG_RCTL, E1K_REG_RCTL_BSIZE_MASK);
+ E1kNetRegSet32(Dev, E1K_REG_RCTL, E1K_REG_RCTL_EN | E1K_REG_RCTL_MPE);
+
+ return EFI_SUCCESS;
+
+FreeSharedBuffer:
+ Dev->PciIo->FreeBuffer (
+ Dev->PciIo,
+ Dev->RxBufNrPages,
+ RxBuffer
+ );
+ return Status;
+}
+
+/**
+ 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
+EFIAPI
+E1kNetInitialize (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ IN UINTN ExtraRxBufferSize OPTIONAL,
+ IN UINTN ExtraTxBufferSize OPTIONAL
+ )
+{
+ E1K_NET_DEV *Dev;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+
+ DEBUG((DEBUG_INFO, "E1kNetInitialize:\n"));
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+ if (ExtraRxBufferSize > 0 || ExtraTxBufferSize > 0) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Dev = E1K_NET_FROM_SNP (This);
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ if (Dev->Snm.State != EfiSimpleNetworkStarted) {
+ Status = EFI_NOT_STARTED;
+ goto InitFailed;
+ }
+
+ // Program the first Receive Address Low/High register.
+ E1kNetRegSet32(Dev, E1K_REG_CTRL, E1K_REG_CTRL_ASDE | E1K_REG_CTRL_SLU);
+ E1kNetRegWrite32(Dev, E1K_REG_RAL, *(UINT32 *)&Dev->Snm.CurrentAddress.Addr[0]);
+ E1kNetRegWrite32(Dev, E1K_REG_RAH, (*(UINT32 *)&Dev->Snm.CurrentAddress.Addr[4]) | E1K_REG_RAH_AV);
+
+ Status = E1kNetInitTx (Dev);
+ if (EFI_ERROR (Status)) {
+ goto AbortDevice;
+ }
+
+ //
+ // start receiving
+ //
+ Status = E1kNetInitRx (Dev);
+ if (EFI_ERROR (Status)) {
+ goto ReleaseTxAux;
+ }
+
+ Dev->Snm.State = EfiSimpleNetworkInitialized;
+ gBS->RestoreTPL (OldTpl);
+ return EFI_SUCCESS;
+
+ReleaseTxAux:
+ E1kNetShutdownTx (Dev);
+
+AbortDevice:
+ E1kNetDevReset(Dev);
+
+InitFailed:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpMcastIpToMac.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpMcastIpToMac.c
new file mode 100644
index 00000000..01508ee9
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpMcastIpToMac.c
@@ -0,0 +1,103 @@
+/** @file
+
+ Implementation of the SNP.McastIpToMac() function and its private helpers if
+ any.
+
+ Copyright (c) 2021, Oracle and/or its affiliates.<BR>
+ Copyright (C) 2013, Red Hat, Inc.
+ Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/UefiBootServicesTableLib.h>
+
+#include "E1kNet.h"
+
+/**
+ 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
+EFIAPI
+E1kNetMcastIpToMac (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ IN BOOLEAN IPv6,
+ IN EFI_IP_ADDRESS *Ip,
+ OUT EFI_MAC_ADDRESS *Mac
+ )
+{
+ E1K_NET_DEV *Dev;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+
+ //
+ // http://en.wikipedia.org/wiki/Multicast_address
+ //
+ if (This == NULL || Ip == NULL || Mac == NULL ||
+ ( IPv6 && (Ip->v6.Addr[0] ) != 0xFF) || // invalid IPv6 mcast addr
+ (!IPv6 && (Ip->v4.Addr[0] & 0xF0) != 0xE0) // invalid IPv4 mcast addr
+ ) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Dev = E1K_NET_FROM_SNP (This);
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ switch (Dev->Snm.State) {
+ case EfiSimpleNetworkStopped:
+ Status = EFI_NOT_STARTED;
+ goto Exit;
+ case EfiSimpleNetworkStarted:
+ Status = EFI_DEVICE_ERROR;
+ goto Exit;
+ default:
+ break;
+ }
+
+ //
+ // http://en.wikipedia.org/wiki/IP_multicast#Layer_2_delivery
+ //
+ if (IPv6) {
+ Mac->Addr[0] = 0x33;
+ Mac->Addr[1] = 0x33;
+ Mac->Addr[2] = Ip->v6.Addr[12];
+ Mac->Addr[3] = Ip->v6.Addr[13];
+ Mac->Addr[4] = Ip->v6.Addr[14];
+ Mac->Addr[5] = Ip->v6.Addr[15];
+ }
+ else {
+ Mac->Addr[0] = 0x01;
+ Mac->Addr[1] = 0x00;
+ Mac->Addr[2] = 0x5E;
+ Mac->Addr[3] = Ip->v4.Addr[1] & 0x7F;
+ Mac->Addr[4] = Ip->v4.Addr[2];
+ Mac->Addr[5] = Ip->v4.Addr[3];
+ }
+ Status = EFI_SUCCESS;
+
+Exit:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpReceive.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpReceive.c
new file mode 100644
index 00000000..0b8b0cbe
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpReceive.c
@@ -0,0 +1,159 @@
+/** @file
+
+ Implementation of the SNP.Receive() function and its private helpers if any.
+
+ Copyright (c) 2021, Oracle and/or its affiliates.
+ Copyright (C) 2013, Red Hat, Inc.
+ Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+#include "E1kNet.h"
+
+/**
+ 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
+EFIAPI
+E1kNetReceive (
+ IN EFI_SIMPLE_NETWORK_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
+ )
+{
+ E1K_NET_DEV *Dev;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+ UINT32 RdhCur;
+ UINT32 RxLen;
+ UINTN OrigBufferSize;
+ EFI_PHYSICAL_ADDRESS BufferAddress;
+ UINT8 *RxPtr;
+ UINTN RxBufOffset;
+
+ DEBUG((DEBUG_INFO, "E1kNetReceive: HeaderSize=%p BufferSize=%u Buffer=%p\n",
+ HeaderSize, *BufferSize, Buffer));
+
+ if (This == NULL || BufferSize == NULL || Buffer == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Dev = E1K_NET_FROM_SNP (This);
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ switch (Dev->Snm.State) {
+ case EfiSimpleNetworkStopped:
+ Status = EFI_NOT_STARTED;
+ goto Exit;
+ case EfiSimpleNetworkStarted:
+ Status = EFI_DEVICE_ERROR;
+ goto Exit;
+ default:
+ break;
+ }
+
+ E1kNetRegRead32(Dev, E1K_REG_RDH, &RdhCur);
+
+ if (Dev->RdhLastSeen == RdhCur) {
+ Status = EFI_NOT_READY;
+ goto Exit;
+ }
+
+ RxLen = Dev->RxRing[Dev->RdhLastSeen].BufferLength;
+ //
+ // the host must not have filled in more data than requested
+ //
+ ASSERT (RxLen <= 2048);
+
+ OrigBufferSize = *BufferSize;
+ *BufferSize = RxLen;
+
+ if (OrigBufferSize < RxLen) {
+ Status = EFI_BUFFER_TOO_SMALL;
+ goto Exit; // keep the packet
+ }
+
+ if (RxLen < Dev->Snm.MediaHeaderSize) {
+ Status = EFI_DEVICE_ERROR;
+ goto RecycleDesc; // drop useless short packet
+ }
+
+ if (HeaderSize != NULL) {
+ *HeaderSize = Dev->Snm.MediaHeaderSize;
+ }
+
+ BufferAddress = Dev->RxRing[Dev->RdhLastSeen].AddrBufferLow;
+ BufferAddress |= LShiftU64(Dev->RxRing[Dev->RdhLastSeen].AddrBufferHigh, 32);
+ RxBufOffset = (UINTN)(BufferAddress - Dev->RxBufDeviceBase);
+ RxPtr = Dev->RxBuf + RxBufOffset;
+ CopyMem (Buffer, RxPtr, RxLen);
+
+ if (DestAddr != NULL) {
+ CopyMem (DestAddr, RxPtr, sizeof (E1K_NET_MAC));
+ }
+ RxPtr += sizeof (E1K_NET_MAC);
+
+ if (SrcAddr != NULL) {
+ CopyMem (SrcAddr, RxPtr, sizeof (E1K_NET_MAC));
+ }
+ RxPtr += sizeof (E1K_NET_MAC);
+
+ if (Protocol != NULL) {
+ *Protocol = (UINT16) ((RxPtr[0] << 8) | RxPtr[1]);
+ }
+ RxPtr += sizeof (UINT16);
+
+ Status = EFI_SUCCESS;
+
+RecycleDesc:
+ Dev->RdhLastSeen = (Dev->RdhLastSeen + 1) % E1K_NET_MAX_PENDING;
+ E1kNetRegWrite32(Dev, E1K_REG_RDT, Dev->RdhLastSeen);
+
+Exit:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpReceiveFilters.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpReceiveFilters.c
new file mode 100644
index 00000000..ce559f40
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpReceiveFilters.c
@@ -0,0 +1,100 @@
+/** @file
+
+ Implementation of the SNP.ReceiveFilters() function and its private helpers
+ if any.
+
+ Copyright (c) 2021, Oracle and/or its affiliates.
+ Copyright (C) 2013, Red Hat, Inc.
+ Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/UefiBootServicesTableLib.h>
+
+#include "E1kNet.h"
+
+/**
+ 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
+ EFI_SIMPLE_NETWORK_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
+EFIAPI
+E1kNetReceiveFilters (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ IN UINT32 Enable,
+ IN UINT32 Disable,
+ IN BOOLEAN ResetMCastFilter,
+ IN UINTN MCastFilterCnt OPTIONAL,
+ IN EFI_MAC_ADDRESS *MCastFilter OPTIONAL
+ )
+{
+ E1K_NET_DEV *Dev;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Dev = E1K_NET_FROM_SNP (This);
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ switch (Dev->Snm.State) {
+ case EfiSimpleNetworkStopped:
+ Status = EFI_NOT_STARTED;
+ goto Exit;
+ case EfiSimpleNetworkStarted:
+ Status = EFI_DEVICE_ERROR;
+ goto Exit;
+ default:
+ break;
+ }
+
+ //
+ // MNP apparently fails to initialize on top of us if we simply return
+ // EFI_UNSUPPORTED in this function.
+ //
+ // Hence we openly refuse multicast functionality, and fake the rest by
+ // selecting a no stricter filter setting than whatever is requested. The
+ // UEFI-2.3.1+errC spec allows this. In practice we don't change our current
+ // (default) filter. Additionally, receiving software is responsible for
+ // discarding any packets getting through the filter.
+ //
+ Status = (
+ ((Enable | Disable) & ~Dev->Snm.ReceiveFilterMask) != 0 ||
+ (!ResetMCastFilter && MCastFilterCnt > Dev->Snm.MaxMCastFilterCount)
+ ) ? EFI_INVALID_PARAMETER : EFI_SUCCESS;
+
+Exit:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpSharedHelpers.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpSharedHelpers.c
new file mode 100644
index 00000000..01a02d7a
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpSharedHelpers.c
@@ -0,0 +1,281 @@
+/** @file
+
+ Helper functions used by at least two Simple Network Protocol methods.
+
+ Copyright (c) 2021, Oracle and/or its affiliates.
+ Copyright (C) 2013, Red Hat, Inc.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/MemoryAllocationLib.h>
+
+#include "E1kNet.h"
+
+//
+// The user structure for the ordered collection that will track the mapping
+// info of the packets queued in TxRing
+//
+typedef struct {
+ VOID *Buffer;
+ EFI_PHYSICAL_ADDRESS DeviceAddress; // lookup key for reverse mapping
+ VOID *BufMap;
+} TX_BUF_MAP_INFO;
+
+/**
+ Release RX and TX resources on the boundary of the
+ EfiSimpleNetworkInitialized state.
+
+ These functions contribute to rolling back a partial, failed initialization
+ of the e1000 SNP driver instance, or to shutting down a fully
+ initialized, running instance.
+
+ They are only callable by the E1kNetInitialize() and the
+ E1kNetShutdown() SNP methods. See the state diagram in "E1kNet.h".
+
+ @param[in,out] Dev The E1K_NET_DEV driver instance being shut down, or whose
+ partial, failed initialization is being rolled back.
+*/
+
+VOID
+EFIAPI
+E1kNetShutdownRx (
+ IN OUT E1K_NET_DEV *Dev
+ )
+{
+ Dev->PciIo->Unmap (Dev->PciIo, Dev->RxMap);
+ Dev->PciIo->FreeBuffer (
+ Dev->PciIo,
+ Dev->RxBufNrPages,
+ Dev->RxRing
+ );
+}
+
+
+VOID
+EFIAPI
+E1kNetShutdownTx (
+ IN OUT E1K_NET_DEV *Dev
+ )
+{
+ ORDERED_COLLECTION_ENTRY *Entry, *Entry2;
+ TX_BUF_MAP_INFO *TxBufMapInfo;
+ VOID *UserStruct;
+
+ Dev->PciIo->Unmap (Dev->PciIo, Dev->TxRingMap);
+ Dev->PciIo->FreeBuffer (
+ Dev->PciIo,
+ EFI_SIZE_TO_PAGES (Dev->TxMaxPending * sizeof (*Dev->TxRing)),
+ Dev->TxRing
+ );
+
+ for (Entry = OrderedCollectionMin (Dev->TxBufCollection);
+ Entry != NULL;
+ Entry = Entry2) {
+ Entry2 = OrderedCollectionNext (Entry);
+ OrderedCollectionDelete (Dev->TxBufCollection, Entry, &UserStruct);
+ TxBufMapInfo = UserStruct;
+ Dev->PciIo->Unmap (Dev->PciIo, TxBufMapInfo->BufMap);
+ FreePool (TxBufMapInfo);
+ }
+ OrderedCollectionUninit (Dev->TxBufCollection);
+}
+
+/**
+ Map Caller-supplied TxBuf buffer to the device-mapped address
+
+ @param[in] Dev The E1K_NET_DEV driver instance which wants to
+ map the Tx packet.
+ @param[in] Buffer The system physical address of TxBuf
+ @param[in] NumberOfBytes Number of bytes to map
+ @param[out] DeviceAddress The resulting device address for the bus
+ master access.
+
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to
+ a lack of resources.
+ @return Status codes from
+ VirtioMapAllBytesInSharedBuffer()
+ @retval EFI_SUCCESS Caller-supplied buffer is successfully mapped.
+*/
+EFI_STATUS
+EFIAPI
+E1kNetMapTxBuf (
+ IN E1K_NET_DEV *Dev,
+ IN VOID *Buffer,
+ IN UINTN NumberOfBytes,
+ OUT EFI_PHYSICAL_ADDRESS *DeviceAddress
+ )
+{
+ EFI_STATUS Status;
+ TX_BUF_MAP_INFO *TxBufMapInfo;
+ EFI_PHYSICAL_ADDRESS Address;
+ VOID *Mapping;
+
+ TxBufMapInfo = AllocatePool (sizeof (*TxBufMapInfo));
+ if (TxBufMapInfo == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = Dev->PciIo->Map (
+ Dev->PciIo,
+ EfiPciIoOperationBusMasterRead,
+ Buffer,
+ &NumberOfBytes,
+ &Address,
+ &Mapping
+ );
+ if (EFI_ERROR (Status)) {
+ goto FreeTxBufMapInfo;
+ }
+
+ TxBufMapInfo->Buffer = Buffer;
+ TxBufMapInfo->DeviceAddress = Address;
+ TxBufMapInfo->BufMap = Mapping;
+
+ Status = OrderedCollectionInsert (
+ Dev->TxBufCollection,
+ NULL,
+ TxBufMapInfo
+ );
+ switch (Status) {
+ case EFI_OUT_OF_RESOURCES:
+ goto UnmapTxBuf;
+ case EFI_ALREADY_STARTED:
+ //
+ // This should never happen: it implies
+ //
+ // - an identity-mapping VIRTIO_DEVICE_PROTOCOL.MapSharedBuffer()
+ // implementation -- which is fine,
+ //
+ // - and an SNP client that queues multiple instances of the exact same
+ // buffer address with SNP.Transmit() -- which is undefined behavior,
+ // based on the TxBuf language in UEFI-2.7,
+ // EFI_SIMPLE_NETWORK.GetStatus().
+ //
+ ASSERT (FALSE);
+ Status = EFI_INVALID_PARAMETER;
+ goto UnmapTxBuf;
+ default:
+ ASSERT_EFI_ERROR (Status);
+ break;
+ }
+
+ *DeviceAddress = Address;
+ return EFI_SUCCESS;
+
+UnmapTxBuf:
+ Dev->PciIo->Unmap (Dev->PciIo, Mapping);
+
+FreeTxBufMapInfo:
+ FreePool (TxBufMapInfo);
+ return Status;
+}
+
+/**
+ Unmap (aka reverse mapping) device mapped TxBuf buffer to the system
+ physical address
+
+ @param[in] Dev The E1K_NET_DEV driver instance which wants to
+ reverse- and unmap the Tx packet.
+ @param[out] Buffer The system physical address of TxBuf
+ @param[in] DeviceAddress The device address for the TxBuf
+
+ @retval EFI_INVALID_PARAMETER The DeviceAddress is not mapped
+ @retval EFI_SUCCESS The TxBuf at DeviceAddress has been unmapped,
+ and Buffer has been set to TxBuf's system
+ physical address.
+
+*/
+EFI_STATUS
+EFIAPI
+E1kNetUnmapTxBuf (
+ IN E1K_NET_DEV *Dev,
+ OUT VOID **Buffer,
+ IN EFI_PHYSICAL_ADDRESS DeviceAddress
+ )
+{
+ ORDERED_COLLECTION_ENTRY *Entry;
+ TX_BUF_MAP_INFO *TxBufMapInfo;
+ VOID *UserStruct;
+
+ Entry = OrderedCollectionFind (Dev->TxBufCollection, &DeviceAddress);
+ if (Entry == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OrderedCollectionDelete (Dev->TxBufCollection, Entry, &UserStruct);
+
+ TxBufMapInfo = UserStruct;
+
+ *Buffer = TxBufMapInfo->Buffer;
+ Dev->PciIo->Unmap (Dev->PciIo, TxBufMapInfo->BufMap);
+ FreePool (TxBufMapInfo);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Comparator function for two TX_BUF_MAP_INFO objects.
+
+ @param[in] UserStruct1 Pointer to the first TX_BUF_MAP_INFO object.
+
+ @param[in] UserStruct2 Pointer to the second TX_BUF_MAP_INFO object.
+
+ @retval <0 If UserStruct1 compares less than UserStruct2.
+
+ @retval 0 If UserStruct1 compares equal to UserStruct2.
+
+ @retval >0 If UserStruct1 compares greater than UserStruct2.
+*/
+INTN
+EFIAPI
+E1kNetTxBufMapInfoCompare (
+ IN CONST VOID *UserStruct1,
+ IN CONST VOID *UserStruct2
+ )
+{
+ CONST TX_BUF_MAP_INFO *MapInfo1;
+ CONST TX_BUF_MAP_INFO *MapInfo2;
+
+ MapInfo1 = UserStruct1;
+ MapInfo2 = UserStruct2;
+
+ return MapInfo1->DeviceAddress < MapInfo2->DeviceAddress ? -1 :
+ MapInfo1->DeviceAddress > MapInfo2->DeviceAddress ? 1 :
+ 0;
+}
+
+/**
+ Compare a standalone DeviceAddress against a TX_BUF_MAP_INFO object
+ containing an embedded DeviceAddress.
+
+ @param[in] StandaloneKey Pointer to DeviceAddress, which has type
+ EFI_PHYSICAL_ADDRESS.
+
+ @param[in] UserStruct Pointer to the TX_BUF_MAP_INFO object with the
+ embedded DeviceAddress.
+
+ @retval <0 If StandaloneKey compares less than UserStruct's key.
+
+ @retval 0 If StandaloneKey compares equal to UserStruct's key.
+
+ @retval >0 If StandaloneKey compares greater than UserStruct's key.
+**/
+INTN
+EFIAPI
+E1kNetTxBufDeviceAddressCompare (
+ IN CONST VOID *StandaloneKey,
+ IN CONST VOID *UserStruct
+ )
+{
+ CONST EFI_PHYSICAL_ADDRESS *DeviceAddress;
+ CONST TX_BUF_MAP_INFO *MapInfo;
+
+ DeviceAddress = StandaloneKey;
+ MapInfo = UserStruct;
+
+ return *DeviceAddress < MapInfo->DeviceAddress ? -1 :
+ *DeviceAddress > MapInfo->DeviceAddress ? 1 :
+ 0;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpShutdown.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpShutdown.c
new file mode 100644
index 00000000..d4dede01
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpShutdown.c
@@ -0,0 +1,74 @@
+/** @file
+
+ Implementation of the SNP.Shutdown() function and its private helpers if any.
+
+ Copyright (c) 2021, Oracle and/or its affiliates.
+ Copyright (c) 2017, AMD Inc, All rights reserved.
+ Copyright (C) 2013, Red Hat, Inc.
+ Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/UefiBootServicesTableLib.h>
+
+#include "E1kNet.h"
+
+/**
+ 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
+EFIAPI
+E1kNetShutdown (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This
+ )
+{
+ E1K_NET_DEV *Dev;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+
+ DEBUG((DEBUG_INFO, "E1kNetShutdown:\n"));
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Dev = E1K_NET_FROM_SNP (This);
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ switch (Dev->Snm.State) {
+ case EfiSimpleNetworkStopped:
+ Status = EFI_NOT_STARTED;
+ goto Exit;
+ case EfiSimpleNetworkStarted:
+ Status = EFI_DEVICE_ERROR;
+ goto Exit;
+ default:
+ break;
+ }
+
+ E1kNetDevReset(Dev);
+ E1kNetShutdownRx (Dev);
+ E1kNetShutdownTx (Dev);
+
+ Dev->Snm.State = EfiSimpleNetworkStarted;
+ Status = EFI_SUCCESS;
+
+Exit:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpStart.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpStart.c
new file mode 100644
index 00000000..7dd48081
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpStart.c
@@ -0,0 +1,59 @@
+/** @file
+
+ Implementation of the SNP.Start() function and its private helpers if any.
+
+ Copyright (c) 2021, Oracle and/or its affiliates.
+ Copyright (C) 2013, Red Hat, Inc.
+ Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/UefiBootServicesTableLib.h>
+
+#include "E1kNet.h"
+
+/**
+ 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
+EFIAPI
+E1kNetStart (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This
+ )
+{
+ E1K_NET_DEV *Dev;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Dev = E1K_NET_FROM_SNP (This);
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ if (Dev->Snm.State != EfiSimpleNetworkStopped) {
+ Status = EFI_ALREADY_STARTED;
+ }
+ else {
+ Dev->Snm.State = EfiSimpleNetworkStarted;
+ Status = EFI_SUCCESS;
+ }
+
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpStop.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpStop.c
new file mode 100644
index 00000000..8f564ebb
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpStop.c
@@ -0,0 +1,60 @@
+/** @file
+
+ Implementation of the SNP.Stop() function and its private helpers if any.
+
+ Copyright (c) 2021, Oracle and/or its affiliates.
+ Copyright (C) 2013, Red Hat, Inc.
+ Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/UefiBootServicesTableLib.h>
+
+#include "E1kNet.h"
+
+/**
+ 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
+EFIAPI
+E1kNetStop (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This
+ )
+{
+ E1K_NET_DEV *Dev;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Dev = E1K_NET_FROM_SNP (This);
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ if (Dev->Snm.State != EfiSimpleNetworkStarted) {
+ Status = EFI_NOT_STARTED;
+ }
+ else {
+ Dev->Snm.State = EfiSimpleNetworkStopped;
+ Status = EFI_SUCCESS;
+ }
+
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpTransmit.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpTransmit.c
new file mode 100644
index 00000000..6dead2b0
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpTransmit.c
@@ -0,0 +1,170 @@
+/** @file
+
+ Implementation of the SNP.Transmit() function and its private helpers if any.
+
+ Copyright (c) 2021, Oracle and/or its affiliates.
+ Copyright (C) 2013, Red Hat, Inc.
+ Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+#include "E1kNet.h"
+
+/**
+ 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
+EFIAPI
+E1kNetTransmit (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ IN UINTN HeaderSize,
+ IN UINTN BufferSize,
+ IN /* +OUT! */ VOID *Buffer,
+ IN EFI_MAC_ADDRESS *SrcAddr OPTIONAL,
+ IN EFI_MAC_ADDRESS *DestAddr OPTIONAL,
+ IN UINT16 *Protocol OPTIONAL
+ )
+{
+ E1K_NET_DEV *Dev;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+ EFI_PHYSICAL_ADDRESS DeviceAddress;
+
+ DEBUG((DEBUG_INFO, "E1kNetTransmit: HeaderSize=%u BufferSize=%u Buffer=%p\n",
+ HeaderSize, BufferSize, Buffer));
+
+ if (This == NULL || BufferSize == 0 || Buffer == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Dev = E1K_NET_FROM_SNP (This);
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ switch (Dev->Snm.State) {
+ case EfiSimpleNetworkStopped:
+ Status = EFI_NOT_STARTED;
+ goto Exit;
+ case EfiSimpleNetworkStarted:
+ Status = EFI_DEVICE_ERROR;
+ goto Exit;
+ default:
+ break;
+ }
+
+ if (BufferSize < Dev->Snm.MediaHeaderSize) {
+ Status = EFI_BUFFER_TOO_SMALL;
+ goto Exit;
+ }
+ if (BufferSize > Dev->Snm.MediaHeaderSize + Dev->Snm.MaxPacketSize) {
+ Status = EFI_INVALID_PARAMETER;
+ goto Exit;
+ }
+
+ //
+ // check if we have room for transmission
+ //
+ ASSERT (Dev->TxCurPending <= Dev->TxMaxPending);
+ if (Dev->TxCurPending == Dev->TxMaxPending) {
+ Status = EFI_NOT_READY;
+ goto Exit;
+ }
+
+ //
+ // the caller may want us to fill in the media header:
+ // dst MAC, src MAC, Ethertype
+ //
+ if (HeaderSize != 0) {
+ UINT8 *Ptr;
+
+ if (HeaderSize != Dev->Snm.MediaHeaderSize ||
+ DestAddr == NULL || Protocol == NULL) {
+ Status = EFI_INVALID_PARAMETER;
+ goto Exit;
+ }
+ Ptr = Buffer;
+ ASSERT (sizeof (E1K_NET_MAC) <= sizeof (EFI_MAC_ADDRESS));
+
+ CopyMem (Ptr, DestAddr, sizeof (E1K_NET_MAC));
+ Ptr += sizeof (E1K_NET_MAC);
+
+ CopyMem (Ptr,
+ (SrcAddr == NULL) ? &Dev->Snm.CurrentAddress : SrcAddr,
+ sizeof (E1K_NET_MAC));
+ Ptr += sizeof (E1K_NET_MAC);
+
+ *Ptr++ = (UINT8) (*Protocol >> 8);
+ *Ptr++ = (UINT8) *Protocol;
+
+ ASSERT ((UINTN) (Ptr - (UINT8 *) Buffer) == Dev->Snm.MediaHeaderSize);
+ }
+
+ //
+ // Map the transmit buffer system physical address to device address.
+ //
+ Status = E1kNetMapTxBuf (
+ Dev,
+ Buffer,
+ BufferSize,
+ &DeviceAddress
+ );
+ if (EFI_ERROR (Status)) {
+ Status = EFI_DEVICE_ERROR;
+ goto Exit;
+ }
+
+ Dev->TxCurPending++;
+ Dev->TxRing[Dev->TxLastUsed].AddrBufferLow = (UINT32)DeviceAddress;
+ Dev->TxRing[Dev->TxLastUsed].AddrBufferHigh = (UINT32)RShiftU64(DeviceAddress, 32);
+ Dev->TxRing[Dev->TxLastUsed].BufferLength = (UINT16)BufferSize;
+ Dev->TxRing[Dev->TxLastUsed].Status = 0;
+ Dev->TxRing[Dev->TxLastUsed].Command = E1K_TX_CMD_EOP | E1K_TX_CMD_FCS | E1K_TX_CMD_RS;
+
+ Dev->TxLastUsed = (Dev->TxLastUsed + 1) % E1K_NET_MAX_PENDING;
+ E1kNetRegWrite32(Dev, E1K_REG_TDT, Dev->TxLastUsed);
+
+Exit:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpUnsupported.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpUnsupported.c
new file mode 100644
index 00000000..d66594e2
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/E1kNetDxe/SnpUnsupported.c
@@ -0,0 +1,155 @@
+/** @file
+
+ Empty implementation of the SNP methods that dependent protocols don't
+ absolutely need and the UEFI-2.3.1+errC specification allows us not to
+ support.
+
+ Copyright (c) 2021, Oracle and/or its affiliates.
+ Copyright (C) 2013, Red Hat, Inc.
+ Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "E1kNet.h"
+
+/**
+ 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
+EFIAPI
+E1kNetReset (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ IN BOOLEAN ExtendedVerification
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+
+/**
+ 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
+EFIAPI
+E1kNetStationAddress (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ IN BOOLEAN Reset,
+ IN EFI_MAC_ADDRESS *New OPTIONAL
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+
+/**
+ 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
+EFIAPI
+E1kNetStatistics (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ IN BOOLEAN Reset,
+ IN OUT UINTN *StatisticsSize OPTIONAL,
+ OUT EFI_NETWORK_STATISTICS *StatisticsTable OPTIONAL
+ )
+{
+ 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
+EFIAPI
+E1kNetNvData (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ IN BOOLEAN ReadWrite,
+ IN UINTN Offset,
+ IN UINTN BufferSize,
+ IN OUT VOID *Buffer
+ )
+{
+ return EFI_UNSUPPORTED;
+}