diff options
author | 2023-10-10 14:33:42 +0000 | |
---|---|---|
committer | 2023-10-10 14:33:42 +0000 | |
commit | af1a266670d040d2f4083ff309d732d648afba2a (patch) | |
tree | 2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/edk2/OvmfPkg/VirtioNetDxe | |
parent | e02cda008591317b1625707ff8e115a4841aa889 (diff) |
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/edk2/OvmfPkg/VirtioNetDxe')
18 files changed, 3737 insertions, 0 deletions
diff --git a/roms/edk2/OvmfPkg/VirtioNetDxe/ComponentName.c b/roms/edk2/OvmfPkg/VirtioNetDxe/ComponentName.c new file mode 100644 index 000000000..483d4f703 --- /dev/null +++ b/roms/edk2/OvmfPkg/VirtioNetDxe/ComponentName.c @@ -0,0 +1,171 @@ +/** @file
+
+ Component Name code for the virtio-net driver.
+
+ 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 "VirtioNet.h"
+
+STATIC
+EFI_UNICODE_STRING_TABLE mVirtioNetDriverNameTable[] = {
+ { "eng;en", L"Virtio Network Driver" },
+ { NULL, NULL }
+};
+
+STATIC
+EFI_UNICODE_STRING_TABLE mVirtioNetControllerNameTable[] = {
+ { "eng;en", L"Virtio Network Device" },
+ { 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
+VirtioNetGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+{
+ return (Language == NULL || DriverName == NULL) ?
+ EFI_INVALID_PARAMETER :
+ LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mVirtioNetDriverNameTable,
+ DriverName,
+ (BOOLEAN) (This == &gVirtioNetComponentName) // 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
+VirtioNetGetControllerName (
+ 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 VirtIo
+ // Protocol
+ //
+ Status = EfiTestManagedDevice (
+ ControllerHandle,
+ gVirtioNetDriverBinding.DriverBindingHandle,
+ &gVirtioDeviceProtocolGuid
+ );
+ 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,
+ mVirtioNetControllerNameTable,
+ ControllerName,
+ (BOOLEAN) (This == &gVirtioNetComponentName) // Iso639Language
+ );
+}
+
+EFI_COMPONENT_NAME_PROTOCOL gVirtioNetComponentName = {
+ &VirtioNetGetDriverName,
+ &VirtioNetGetControllerName,
+ "eng" // SupportedLanguages, ISO 639-2 language codes
+};
+
+EFI_COMPONENT_NAME2_PROTOCOL gVirtioNetComponentName2 = {
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) &VirtioNetGetDriverName,
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) &VirtioNetGetControllerName,
+ "en" // SupportedLanguages, RFC 4646 language codes
+};
diff --git a/roms/edk2/OvmfPkg/VirtioNetDxe/DriverBinding.c b/roms/edk2/OvmfPkg/VirtioNetDxe/DriverBinding.c new file mode 100644 index 000000000..3c400a604 --- /dev/null +++ b/roms/edk2/OvmfPkg/VirtioNetDxe/DriverBinding.c @@ -0,0 +1,647 @@ +/** @file
+
+ Driver Binding code and its private helpers for the virtio-net driver.
+
+ 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/BaseMemoryLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+#include "VirtioNet.h"
+
+#define RECEIVE_FILTERS_NO_MCAST ((UINT32) ( \
+ EFI_SIMPLE_NETWORK_RECEIVE_UNICAST | \
+ EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST | \
+ EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS \
+ ))
+
+/*
+ Temporarily enable then reset the virtio-net device in order to retrieve
+ configuration values needed by Simple Network Protocol and Simple Network
+ Mode fields.
+
+ Only VirtioNetSnpPopulate() may call this function.
+
+ If the function fails for any reason, the virtio-net device is moved to
+ VSTAT_FAILED instead of being reset. This serves only informative purposes
+ for the host side.
+
+ param[in,out] Dev The VNET_DEV structure being created for
+ the virtio-net device.
+ param[out] MacAddress MAC address configured by the host.
+ param[out] MediaPresentSupported Link status is made available by the host.
+ param[out] MediaPresent If link status is made available by the
+ host, the current link status is stored in
+ *MediaPresent. Otherwise MediaPresent is
+ unused.
+
+ @retval EFI_UNSUPPORTED The host doesn't supply a MAC address.
+ @return Status codes from VirtIo protocol members.
+ @retval EFI_SUCCESS Configuration values retrieved.
+*/
+STATIC
+EFI_STATUS
+EFIAPI
+VirtioNetGetFeatures (
+ IN OUT VNET_DEV *Dev,
+ OUT EFI_MAC_ADDRESS *MacAddress,
+ OUT BOOLEAN *MediaPresentSupported,
+ OUT BOOLEAN *MediaPresent
+ )
+{
+ EFI_STATUS Status;
+ UINT8 NextDevStat;
+ UINT64 Features;
+ UINTN MacIdx;
+ UINT16 LinkStatus;
+
+ //
+ // Interrogate the device for features (virtio-0.9.5, 2.2.1 Device
+ // Initialization Sequence), but don't complete setting it up.
+ //
+ NextDevStat = 0; // step 1 -- reset device
+ Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ NextDevStat |= VSTAT_ACK; // step 2 -- acknowledge device presence
+ Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
+ if (EFI_ERROR (Status)) {
+ goto YieldDevice;
+ }
+
+ NextDevStat |= VSTAT_DRIVER; // step 3 -- we know how to drive it
+ Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
+ if (EFI_ERROR (Status)) {
+ goto YieldDevice;
+ }
+
+ //
+ // step 4a -- retrieve and validate features
+ //
+ Status = Dev->VirtIo->GetDeviceFeatures (Dev->VirtIo, &Features);
+ if (EFI_ERROR (Status)) {
+ goto YieldDevice;
+ }
+
+ //
+ // get MAC address byte-wise
+ //
+ if ((Features & VIRTIO_NET_F_MAC) == 0) {
+ Status = EFI_UNSUPPORTED;
+ goto YieldDevice;
+ }
+ for (MacIdx = 0; MacIdx < SIZE_OF_VNET (Mac); ++MacIdx) {
+ Status = Dev->VirtIo->ReadDevice (Dev->VirtIo,
+ OFFSET_OF_VNET (Mac) + MacIdx, // Offset
+ 1, // FieldSize
+ 1, // BufferSize
+ &MacAddress->Addr[MacIdx] // Buffer
+ );
+ if (EFI_ERROR (Status)) {
+ goto YieldDevice;
+ }
+ }
+
+ //
+ // check if link status is reported, and if so, what the link status is
+ //
+ if ((Features & VIRTIO_NET_F_STATUS) == 0) {
+ *MediaPresentSupported = FALSE;
+ }
+ else {
+ *MediaPresentSupported = TRUE;
+ Status = VIRTIO_CFG_READ (Dev, LinkStatus, &LinkStatus);
+ if (EFI_ERROR (Status)) {
+ goto YieldDevice;
+ }
+ *MediaPresent = (BOOLEAN) ((LinkStatus & VIRTIO_NET_S_LINK_UP) != 0);
+ }
+
+YieldDevice:
+ Dev->VirtIo->SetDeviceStatus (Dev->VirtIo,
+ EFI_ERROR (Status) ? VSTAT_FAILED : 0);
+
+ 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 VirtioNetDriverBindingStart().
+
+ @param[in,out] Dev The VNET_DEV driver instance being created for the
+ virtio-net device.
+
+ @return Status codes from the CreateEvent() boot service or the
+ VirtioNetGetFeatures() function.
+ @retval EFI_SUCCESS Configuration successful.
+*/
+STATIC
+EFI_STATUS
+EFIAPI
+VirtioNetSnpPopulate (
+ IN OUT VNET_DEV *Dev
+ )
+{
+ 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
+ // VirtioNetIsPacketAvailable() 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,
+ &VirtioNetIsPacketAvailable, Dev, &Dev->Snp.WaitForPacket);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Dev->Snp.Revision = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION;
+ Dev->Snp.Start = &VirtioNetStart;
+ Dev->Snp.Stop = &VirtioNetStop;
+ Dev->Snp.Initialize = &VirtioNetInitialize;
+ Dev->Snp.Reset = &VirtioNetReset;
+ Dev->Snp.Shutdown = &VirtioNetShutdown;
+ Dev->Snp.ReceiveFilters = &VirtioNetReceiveFilters;
+ Dev->Snp.StationAddress = &VirtioNetStationAddress;
+ Dev->Snp.Statistics = &VirtioNetStatistics;
+ Dev->Snp.MCastIpToMac = &VirtioNetMcastIpToMac;
+ Dev->Snp.NvData = &VirtioNetNvData;
+ Dev->Snp.GetStatus = &VirtioNetGetStatus;
+ Dev->Snp.Transmit = &VirtioNetTransmit;
+ Dev->Snp.Receive = &VirtioNetReceive;
+ Dev->Snp.Mode = &Dev->Snm;
+
+ Dev->Snm.State = EfiSimpleNetworkStopped;
+ Dev->Snm.HwAddressSize = SIZE_OF_VNET (Mac);
+ Dev->Snm.MediaHeaderSize = SIZE_OF_VNET (Mac) + // dst MAC
+ SIZE_OF_VNET (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 (SIZE_OF_VNET (Mac) <= sizeof (EFI_MAC_ADDRESS));
+
+ Status = VirtioNetGetFeatures (Dev, &Dev->Snm.CurrentAddress,
+ &Dev->Snm.MediaPresentSupported, &Dev->Snm.MediaPresent);
+ if (EFI_ERROR (Status)) {
+ goto CloseWaitForPacket;
+ }
+ CopyMem (&Dev->Snm.PermanentAddress, &Dev->Snm.CurrentAddress,
+ SIZE_OF_VNET (Mac));
+ SetMem (&Dev->Snm.BroadcastAddress, SIZE_OF_VNET (Mac), 0xFF);
+
+ //
+ // VirtioNetExitBoot() is queued by ExitBootServices(); its purpose is to
+ // cancel any pending virtio 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 VirtioNetExitBoot()
+ // could be entered immediately. VirtioNetExitBoot() checks Dev->Snm.State,
+ // so we're safe.
+ //
+ Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK,
+ &VirtioNetExitBoot, 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 VirtioNetSnpPopulate().
+
+ This function may only be called by VirtioNetDriverBindingStart(), when
+ rolling back a partial, failed driver instance creation, and by
+ VirtioNetDriverBindingStop(), when disconnecting a virtio-net device from the
+ driver.
+
+ @param[in,out] Dev The VNET_DEV driver instance being destroyed.
+*/
+STATIC
+VOID
+EFIAPI
+VirtioNetSnpEvacuate (
+ IN OUT VNET_DEV *Dev
+ )
+{
+ //
+ // This function runs either at TPL_CALLBACK already (from
+ // VirtioNetDriverBindingStop()), or it is part of a teardown following
+ // a partial, failed construction in VirtioNetDriverBindingStart(), 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
+VirtioNetDriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE DeviceHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ EFI_STATUS Status;
+ VIRTIO_DEVICE_PROTOCOL *VirtIo;
+
+ //
+ // Attempt to open the device with the VirtIo set of interfaces. On success,
+ // the protocol is "instantiated" for the VirtIo device. Covers duplicate open
+ // attempts (EFI_ALREADY_STARTED).
+ //
+ Status = gBS->OpenProtocol (
+ DeviceHandle, // candidate device
+ &gVirtioDeviceProtocolGuid, // for generic VirtIo access
+ (VOID **)&VirtIo, // handle to instantiate
+ This->DriverBindingHandle, // requestor driver identity
+ DeviceHandle, // ControllerHandle, according to
+ // the UEFI Driver Model
+ EFI_OPEN_PROTOCOL_BY_DRIVER // get exclusive VirtIo access to
+ // the device; to be released
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (VirtIo->SubSystemDeviceId != VIRTIO_SUBSYSTEM_NETWORK_CARD) {
+ Status = EFI_UNSUPPORTED;
+ }
+
+ //
+ // We needed VirtIo access only transitorily, to see whether we support the
+ // device or not.
+ //
+ gBS->CloseProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,
+ This->DriverBindingHandle, DeviceHandle);
+ 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
+VirtioNetDriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE DeviceHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ EFI_STATUS Status;
+ VNET_DEV *Dev;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ MAC_ADDR_DEVICE_PATH MacNode;
+ VOID *ChildVirtIo;
+
+ //
+ // allocate space for the driver instance
+ //
+ Dev = (VNET_DEV *) AllocateZeroPool (sizeof *Dev);
+ if (Dev == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ Dev->Signature = VNET_SIG;
+
+ Status = gBS->OpenProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,
+ (VOID **)&Dev->VirtIo, This->DriverBindingHandle,
+ DeviceHandle, EFI_OPEN_PROTOCOL_BY_DRIVER);
+ if (EFI_ERROR (Status)) {
+ goto FreeVirtioNet;
+ }
+
+ //
+ // now we can run a basic one-shot virtio-net initialization required to
+ // retrieve the MAC address
+ //
+ Status = VirtioNetSnpPopulate (Dev);
+ if (EFI_ERROR (Status)) {
+ goto CloseVirtIo;
+ }
+
+ //
+ // get the device path of the virtio-net device -- one-shot open
+ //
+ Status = gBS->OpenProtocol (DeviceHandle, &gEfiDevicePathProtocolGuid,
+ (VOID **)&DevicePath, This->DriverBindingHandle,
+ DeviceHandle, 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;
+ }
+
+ //
+ // make a note that we keep this device open with VirtIo for the sake of this
+ // child
+ //
+ Status = gBS->OpenProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,
+ &ChildVirtIo, This->DriverBindingHandle,
+ Dev->MacHandle, EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER);
+ if (EFI_ERROR (Status)) {
+ goto UninstallMultiple;
+ }
+
+ return EFI_SUCCESS;
+
+UninstallMultiple:
+ gBS->UninstallMultipleProtocolInterfaces (Dev->MacHandle,
+ &gEfiDevicePathProtocolGuid, Dev->MacDevicePath,
+ &gEfiSimpleNetworkProtocolGuid, &Dev->Snp,
+ NULL);
+
+FreeMacDevicePath:
+ FreePool (Dev->MacDevicePath);
+
+Evacuate:
+ VirtioNetSnpEvacuate (Dev);
+
+CloseVirtIo:
+ gBS->CloseProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,
+ This->DriverBindingHandle, DeviceHandle);
+
+FreeVirtioNet:
+ FreePool (Dev);
+
+ 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
+VirtioNetDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE DeviceHandle,
+ 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;
+ VNET_DEV *Dev;
+ EFI_TPL OldTpl;
+
+ ASSERT (NumberOfChildren == 1);
+
+ Status = gBS->OpenProtocol (ChildHandleBuffer[0],
+ &gEfiSimpleNetworkProtocolGuid, (VOID **)&Snp,
+ This->DriverBindingHandle, DeviceHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ ASSERT_EFI_ERROR (Status);
+ Dev = VIRTIO_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->CloseProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,
+ This->DriverBindingHandle, Dev->MacHandle);
+ gBS->UninstallMultipleProtocolInterfaces (Dev->MacHandle,
+ &gEfiDevicePathProtocolGuid, Dev->MacDevicePath,
+ &gEfiSimpleNetworkProtocolGuid, &Dev->Snp,
+ NULL);
+ FreePool (Dev->MacDevicePath);
+ VirtioNetSnpEvacuate (Dev);
+ FreePool (Dev);
+ }
+
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+ }
+
+ //
+ // release remaining resources, tied directly to the parent handle
+ //
+ gBS->CloseProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,
+ This->DriverBindingHandle, DeviceHandle);
+
+ return EFI_SUCCESS;
+}
+
+
+EFI_DRIVER_BINDING_PROTOCOL gVirtioNetDriverBinding = {
+ &VirtioNetDriverBindingSupported,
+ &VirtioNetDriverBindingStart,
+ &VirtioNetDriverBindingStop,
+ 0x10,
+ NULL,
+ NULL
+};
diff --git a/roms/edk2/OvmfPkg/VirtioNetDxe/EntryPoint.c b/roms/edk2/OvmfPkg/VirtioNetDxe/EntryPoint.c new file mode 100644 index 000000000..95e8bda6d --- /dev/null +++ b/roms/edk2/OvmfPkg/VirtioNetDxe/EntryPoint.c @@ -0,0 +1,44 @@ +/** @file
+
+ This file implements the entry point of the virtio-net driver.
+
+ 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 "VirtioNet.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
+VirtioNetEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ return EfiLibInstallDriverBindingComponentName2 (
+ ImageHandle,
+ SystemTable,
+ &gVirtioNetDriverBinding,
+ ImageHandle,
+ &gVirtioNetComponentName,
+ &gVirtioNetComponentName2
+ );
+}
diff --git a/roms/edk2/OvmfPkg/VirtioNetDxe/Events.c b/roms/edk2/OvmfPkg/VirtioNetDxe/Events.c new file mode 100644 index 000000000..83e96e5e5 --- /dev/null +++ b/roms/edk2/OvmfPkg/VirtioNetDxe/Events.c @@ -0,0 +1,86 @@ +/** @file
+
+ Implements
+ - the SNM.WaitForPacket EVT_NOTIFY_WAIT event,
+ - the EVT_SIGNAL_EXIT_BOOT_SERVICES event
+ for the virtio-net driver.
+
+ 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 "VirtioNet.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
+VirtioNetIsPacketAvailable (
+ 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.
+ //
+ VNET_DEV *Dev;
+ UINT16 RxCurUsed;
+
+ Dev = Context;
+ if (Dev->Snm.State != EfiSimpleNetworkInitialized) {
+ return;
+ }
+
+ //
+ // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device
+ //
+ MemoryFence ();
+ RxCurUsed = *Dev->RxRing.Used.Idx;
+ MemoryFence ();
+
+ if (Dev->RxLastUsed != RxCurUsed) {
+ gBS->SignalEvent (Dev->Snp.WaitForPacket);
+ }
+}
+
+VOID
+EFIAPI
+VirtioNetExitBoot (
+ 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".
+ //
+ VNET_DEV *Dev;
+
+ DEBUG ((DEBUG_VERBOSE, "%a: Context=0x%p\n", __FUNCTION__, Context));
+ Dev = Context;
+ if (Dev->Snm.State == EfiSimpleNetworkInitialized) {
+ Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);
+ }
+}
diff --git a/roms/edk2/OvmfPkg/VirtioNetDxe/SnpGetStatus.c b/roms/edk2/OvmfPkg/VirtioNetDxe/SnpGetStatus.c new file mode 100644 index 000000000..333a522d5 --- /dev/null +++ b/roms/edk2/OvmfPkg/VirtioNetDxe/SnpGetStatus.c @@ -0,0 +1,175 @@ +/** @file
+
+ Implementation of the SNP.GetStatus() function and its private helpers if
+ any.
+
+ 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 "VirtioNet.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
+VirtioNetGetStatus (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ OUT UINT32 *InterruptStatus OPTIONAL,
+ OUT VOID **TxBuf OPTIONAL
+ )
+{
+ VNET_DEV *Dev;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+ UINT16 RxCurUsed;
+ UINT16 TxCurUsed;
+ EFI_PHYSICAL_ADDRESS DeviceAddress;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Dev = VIRTIO_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) {
+ UINT16 LinkStatus;
+
+ Status = VIRTIO_CFG_READ (Dev, LinkStatus, &LinkStatus);
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+ Dev->Snm.MediaPresent =
+ (BOOLEAN) ((LinkStatus & VIRTIO_NET_S_LINK_UP) != 0);
+ }
+
+ //
+ // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device
+ //
+ MemoryFence ();
+ RxCurUsed = *Dev->RxRing.Used.Idx;
+ TxCurUsed = *Dev->TxRing.Used.Idx;
+ MemoryFence ();
+
+ 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->RxLastUsed != RxCurUsed) {
+ *InterruptStatus |= EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT;
+ }
+ if (Dev->TxLastUsed != TxCurUsed) {
+ ASSERT (Dev->TxCurPending > 0);
+ *InterruptStatus |= EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT;
+ }
+ }
+
+ if (TxBuf != NULL) {
+ if (Dev->TxLastUsed == TxCurUsed) {
+ *TxBuf = NULL;
+ }
+ else {
+ UINT16 UsedElemIdx;
+ UINT32 DescIdx;
+
+ //
+ // fetch the first descriptor among those that the hypervisor reports
+ // completed
+ //
+ ASSERT (Dev->TxCurPending > 0);
+ ASSERT (Dev->TxCurPending <= Dev->TxMaxPending);
+
+ UsedElemIdx = Dev->TxLastUsed++ % Dev->TxRing.QueueSize;
+ DescIdx = Dev->TxRing.Used.UsedElem[UsedElemIdx].Id;
+ ASSERT (DescIdx < (UINT32) (2 * Dev->TxMaxPending - 1));
+
+ //
+ // get the device address that has been enqueued for the caller's
+ // transmit buffer
+ //
+ DeviceAddress = Dev->TxRing.Desc[DescIdx + 1].Addr;
+
+ //
+ // now this descriptor can be used again to enqueue a transmit buffer
+ //
+ Dev->TxFreeStack[--Dev->TxCurPending] = (UINT16) DescIdx;
+
+ //
+ // Unmap the device address and perform the reverse mapping to find the
+ // caller buffer address.
+ //
+ Status = VirtioNetUnmapTxBuf (
+ Dev,
+ TxBuf,
+ DeviceAddress
+ );
+ if (EFI_ERROR (Status)) {
+ //
+ // VirtioNetUnmapTxBuf 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/roms/edk2/OvmfPkg/VirtioNetDxe/SnpInitialize.c b/roms/edk2/OvmfPkg/VirtioNetDxe/SnpInitialize.c new file mode 100644 index 000000000..bb3b552d6 --- /dev/null +++ b/roms/edk2/OvmfPkg/VirtioNetDxe/SnpInitialize.c @@ -0,0 +1,654 @@ +/** @file
+
+ Implementation of the SNP.Initialize() function and its private helpers if
+ any.
+
+ Copyright (C) 2013, Red Hat, Inc.
+ Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>
+ Copyright (c) 2017, AMD Inc, 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 "VirtioNet.h"
+
+/**
+ Initialize a virtio ring for a specific transfer direction of the virtio-net
+ device.
+
+ This function may only be called by VirtioNetInitialize().
+
+ @param[in,out] Dev The VNET_DEV driver instance about to enter the
+ EfiSimpleNetworkInitialized state.
+ @param[in] Selector Identifies the transfer direction (virtio queue) of
+ the network device.
+ @param[out] Ring The virtio-ring inside the VNET_DEV structure,
+ corresponding to Selector.
+ @param[out] Mapping A resulting token to pass to VirtioNetUninitRing()
+
+ @retval EFI_UNSUPPORTED The queue size reported by the virtio-net device is
+ too small.
+ @return Status codes from VIRTIO_CFG_WRITE(),
+ VIRTIO_CFG_READ(), VirtioRingInit() and
+ VirtioRingMap().
+ @retval EFI_SUCCESS Ring initialized.
+*/
+
+STATIC
+EFI_STATUS
+EFIAPI
+VirtioNetInitRing (
+ IN OUT VNET_DEV *Dev,
+ IN UINT16 Selector,
+ OUT VRING *Ring,
+ OUT VOID **Mapping
+ )
+{
+ EFI_STATUS Status;
+ UINT16 QueueSize;
+ UINT64 RingBaseShift;
+ VOID *MapInfo;
+
+ //
+ // step 4b -- allocate selected queue
+ //
+ Status = Dev->VirtIo->SetQueueSel (Dev->VirtIo, Selector);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ Status = Dev->VirtIo->GetQueueNumMax (Dev->VirtIo, &QueueSize);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // For each packet (RX and TX alike), we need two descriptors:
+ // one for the virtio-net request header, and another one for the data
+ //
+ if (QueueSize < 2) {
+ return EFI_UNSUPPORTED;
+ }
+ Status = VirtioRingInit (Dev->VirtIo, QueueSize, Ring);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // If anything fails from here on, we must release the ring resources.
+ //
+ Status = VirtioRingMap (Dev->VirtIo, Ring, &RingBaseShift, &MapInfo);
+ if (EFI_ERROR (Status)) {
+ goto ReleaseQueue;
+ }
+
+ //
+ // Additional steps for MMIO: align the queue appropriately, and set the
+ // size. If anything fails from here on, we must unmap the ring resources.
+ //
+ Status = Dev->VirtIo->SetQueueNum (Dev->VirtIo, QueueSize);
+ if (EFI_ERROR (Status)) {
+ goto UnmapQueue;
+ }
+
+ Status = Dev->VirtIo->SetQueueAlign (Dev->VirtIo, EFI_PAGE_SIZE);
+ if (EFI_ERROR (Status)) {
+ goto UnmapQueue;
+ }
+
+ //
+ // step 4c -- report GPFN (guest-physical frame number) of queue
+ //
+ Status = Dev->VirtIo->SetQueueAddress (Dev->VirtIo, Ring, RingBaseShift);
+ if (EFI_ERROR (Status)) {
+ goto UnmapQueue;
+ }
+
+ *Mapping = MapInfo;
+
+ return EFI_SUCCESS;
+
+UnmapQueue:
+ Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, MapInfo);
+
+ReleaseQueue:
+ VirtioRingUninit (Dev->VirtIo, Ring);
+
+ return Status;
+}
+
+
+/**
+ Set up static scaffolding for the VirtioNetTransmit() and
+ VirtioNetGetStatus() SNP methods.
+
+ This function may only be called by VirtioNetInitialize().
+
+ The structures laid out and resources configured include:
+ - fully populate the TX queue with a static pattern of virtio descriptor
+ chains,
+ - tracking of heads of free descriptor chains from the above,
+ - one common virtio-net request header (never modified by the host) for all
+ pending TX packets,
+ - select polling over TX interrupt.
+
+ @param[in,out] Dev The VNET_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
+VirtioNetInitTx (
+ IN OUT VNET_DEV *Dev
+ )
+{
+ UINTN TxSharedReqSize;
+ UINTN PktIdx;
+ EFI_STATUS Status;
+ EFI_PHYSICAL_ADDRESS DeviceAddress;
+ VOID *TxSharedReqBuffer;
+
+ Dev->TxMaxPending = (UINT16) MIN (Dev->TxRing.QueueSize / 2,
+ VNET_MAX_PENDING);
+ Dev->TxCurPending = 0;
+ Dev->TxFreeStack = AllocatePool (Dev->TxMaxPending *
+ sizeof *Dev->TxFreeStack);
+ if (Dev->TxFreeStack == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Dev->TxBufCollection = OrderedCollectionInit (
+ VirtioNetTxBufMapInfoCompare,
+ VirtioNetTxBufDeviceAddressCompare
+ );
+ if (Dev->TxBufCollection == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto FreeTxFreeStack;
+ }
+
+ //
+ // Allocate TxSharedReq header and map with BusMasterCommonBuffer so that it
+ // can be accessed equally by both processor and device.
+ //
+ Status = Dev->VirtIo->AllocateSharedPages (
+ Dev->VirtIo,
+ EFI_SIZE_TO_PAGES (sizeof *Dev->TxSharedReq),
+ &TxSharedReqBuffer
+ );
+ if (EFI_ERROR (Status)) {
+ goto UninitTxBufCollection;
+ }
+
+ ZeroMem (TxSharedReqBuffer, sizeof *Dev->TxSharedReq);
+
+ Status = VirtioMapAllBytesInSharedBuffer (
+ Dev->VirtIo,
+ VirtioOperationBusMasterCommonBuffer,
+ TxSharedReqBuffer,
+ sizeof *(Dev->TxSharedReq),
+ &DeviceAddress,
+ &Dev->TxSharedReqMap
+ );
+ if (EFI_ERROR (Status)) {
+ goto FreeTxSharedReqBuffer;
+ }
+
+ Dev->TxSharedReq = TxSharedReqBuffer;
+
+
+ //
+ // In VirtIo 1.0, the NumBuffers field is mandatory. In 0.9.5, it depends on
+ // VIRTIO_NET_F_MRG_RXBUF, which we never negotiate.
+ //
+ TxSharedReqSize = (Dev->VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) ?
+ sizeof (Dev->TxSharedReq->V0_9_5) :
+ sizeof *Dev->TxSharedReq;
+
+ for (PktIdx = 0; PktIdx < Dev->TxMaxPending; ++PktIdx) {
+ UINT16 DescIdx;
+
+ DescIdx = (UINT16) (2 * PktIdx);
+ Dev->TxFreeStack[PktIdx] = DescIdx;
+
+ //
+ // For each possibly pending packet, lay out the descriptor for the common
+ // (unmodified by the host) virtio-net request header.
+ //
+ Dev->TxRing.Desc[DescIdx].Addr = DeviceAddress;
+ Dev->TxRing.Desc[DescIdx].Len = (UINT32) TxSharedReqSize;
+ Dev->TxRing.Desc[DescIdx].Flags = VRING_DESC_F_NEXT;
+ Dev->TxRing.Desc[DescIdx].Next = (UINT16) (DescIdx + 1);
+
+ //
+ // The second descriptor of each pending TX packet is updated on the fly,
+ // but it always terminates the descriptor chain of the packet.
+ //
+ Dev->TxRing.Desc[DescIdx + 1].Flags = 0;
+ }
+
+ //
+ // virtio-0.9.5, Appendix C, Packet Transmission
+ //
+ Dev->TxSharedReq->V0_9_5.Flags = 0;
+ Dev->TxSharedReq->V0_9_5.GsoType = VIRTIO_NET_HDR_GSO_NONE;
+
+ //
+ // For VirtIo 1.0 only -- the field exists, but it is unused
+ //
+ Dev->TxSharedReq->NumBuffers = 0;
+
+ //
+ // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device
+ //
+ MemoryFence ();
+ Dev->TxLastUsed = *Dev->TxRing.Used.Idx;
+ ASSERT (Dev->TxLastUsed == 0);
+
+ //
+ // want no interrupt when a transmit completes
+ //
+ *Dev->TxRing.Avail.Flags = (UINT16) VRING_AVAIL_F_NO_INTERRUPT;
+
+ return EFI_SUCCESS;
+
+FreeTxSharedReqBuffer:
+ Dev->VirtIo->FreeSharedPages (
+ Dev->VirtIo,
+ EFI_SIZE_TO_PAGES (sizeof *(Dev->TxSharedReq)),
+ TxSharedReqBuffer
+ );
+
+UninitTxBufCollection:
+ OrderedCollectionUninit (Dev->TxBufCollection);
+
+FreeTxFreeStack:
+ FreePool (Dev->TxFreeStack);
+
+ return Status;
+}
+
+
+/**
+ Set up static scaffolding for the VirtioNetReceive() SNP method and enable
+ live device operation.
+
+ This function may only be called as VirtioNetInitialize()'s final step.
+
+ The structures laid out and resources configured include:
+ - destination area for the host to write virtio-net request headers and
+ packet data into,
+ - select polling over RX interrupt,
+ - fully populate the RX queue with a static pattern of virtio descriptor
+ chains.
+
+ @param[in,out] Dev The VNET_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
+VirtioNetInitRx (
+ IN OUT VNET_DEV *Dev
+ )
+{
+ EFI_STATUS Status;
+ UINTN VirtioNetReqSize;
+ UINTN RxBufSize;
+ UINT16 RxAlwaysPending;
+ UINTN PktIdx;
+ UINT16 DescIdx;
+ UINTN NumBytes;
+ EFI_PHYSICAL_ADDRESS RxBufDeviceAddress;
+ VOID *RxBuffer;
+
+ //
+ // In VirtIo 1.0, the NumBuffers field is mandatory. In 0.9.5, it depends on
+ // VIRTIO_NET_F_MRG_RXBUF, which we never negotiate.
+ //
+ VirtioNetReqSize = (Dev->VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) ?
+ sizeof (VIRTIO_NET_REQ) :
+ sizeof (VIRTIO_1_0_NET_REQ);
+
+ //
+ // For each incoming packet we must supply two descriptors:
+ // - the recipient for the virtio-net request header, plus
+ // - the recipient for the network data (which consists of Ethernet header
+ // and Ethernet payload).
+ //
+ RxBufSize = VirtioNetReqSize +
+ (Dev->Snm.MediaHeaderSize + Dev->Snm.MaxPacketSize);
+
+ //
+ // Limit the number of pending RX packets if the queue is big. The division
+ // by two is due to the above "two descriptors per packet" trait.
+ //
+ RxAlwaysPending = (UINT16) MIN (Dev->RxRing.QueueSize / 2, VNET_MAX_PENDING);
+
+ //
+ // 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 = RxAlwaysPending * RxBufSize;
+ Dev->RxBufNrPages = EFI_SIZE_TO_PAGES (NumBytes);
+ Status = Dev->VirtIo->AllocateSharedPages (
+ Dev->VirtIo,
+ Dev->RxBufNrPages,
+ &RxBuffer
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ ZeroMem (RxBuffer, NumBytes);
+
+ Status = VirtioMapAllBytesInSharedBuffer (
+ Dev->VirtIo,
+ VirtioOperationBusMasterCommonBuffer,
+ RxBuffer,
+ NumBytes,
+ &Dev->RxBufDeviceBase,
+ &Dev->RxBufMap
+ );
+ if (EFI_ERROR (Status)) {
+ goto FreeSharedBuffer;
+ }
+
+ Dev->RxBuf = RxBuffer;
+
+ //
+ // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device
+ //
+ MemoryFence ();
+ Dev->RxLastUsed = *Dev->RxRing.Used.Idx;
+ ASSERT (Dev->RxLastUsed == 0);
+
+ //
+ // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device:
+ // the host should not send interrupts, we'll poll in VirtioNetReceive()
+ // and VirtioNetIsPacketAvailable().
+ //
+ *Dev->RxRing.Avail.Flags = (UINT16) VRING_AVAIL_F_NO_INTERRUPT;
+
+ //
+ // now set up a separate, two-part descriptor chain for each RX packet, and
+ // link each chain into (from) the available ring as well
+ //
+ DescIdx = 0;
+ RxBufDeviceAddress = Dev->RxBufDeviceBase;
+ for (PktIdx = 0; PktIdx < RxAlwaysPending; ++PktIdx) {
+ //
+ // virtio-0.9.5, 2.4.1.2 Updating the Available Ring
+ // invisible to the host until we update the Index Field
+ //
+ Dev->RxRing.Avail.Ring[PktIdx] = DescIdx;
+
+ //
+ // virtio-0.9.5, 2.4.1.1 Placing Buffers into the Descriptor Table
+ //
+ Dev->RxRing.Desc[DescIdx].Addr = RxBufDeviceAddress;
+ Dev->RxRing.Desc[DescIdx].Len = (UINT32) VirtioNetReqSize;
+ Dev->RxRing.Desc[DescIdx].Flags = VRING_DESC_F_WRITE | VRING_DESC_F_NEXT;
+ Dev->RxRing.Desc[DescIdx].Next = (UINT16) (DescIdx + 1);
+ RxBufDeviceAddress += Dev->RxRing.Desc[DescIdx++].Len;
+
+ Dev->RxRing.Desc[DescIdx].Addr = RxBufDeviceAddress;
+ Dev->RxRing.Desc[DescIdx].Len = (UINT32) (RxBufSize - VirtioNetReqSize);
+ Dev->RxRing.Desc[DescIdx].Flags = VRING_DESC_F_WRITE;
+ RxBufDeviceAddress += Dev->RxRing.Desc[DescIdx++].Len;
+ }
+
+ //
+ // virtio-0.9.5, 2.4.1.3 Updating the Index Field
+ //
+ MemoryFence ();
+ *Dev->RxRing.Avail.Idx = RxAlwaysPending;
+
+ //
+ // At this point reception may already be running. In order to make it sure,
+ // kick the hypervisor. If we fail to kick it, we must first abort reception
+ // before tearing down anything, because reception may have been already
+ // running even without the kick.
+ //
+ // virtio-0.9.5, 2.4.1.4 Notifying the Device
+ //
+ MemoryFence ();
+ Status = Dev->VirtIo->SetQueueNotify (Dev->VirtIo, VIRTIO_NET_Q_RX);
+ if (EFI_ERROR (Status)) {
+ Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);
+ goto UnmapSharedBuffer;
+ }
+
+ return Status;
+
+UnmapSharedBuffer:
+ Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Dev->RxBufMap);
+
+FreeSharedBuffer:
+ Dev->VirtIo->FreeSharedPages (
+ Dev->VirtIo,
+ 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
+VirtioNetInitialize (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ IN UINTN ExtraRxBufferSize OPTIONAL,
+ IN UINTN ExtraTxBufferSize OPTIONAL
+ )
+{
+ VNET_DEV *Dev;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+ UINT8 NextDevStat;
+ UINT64 Features;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+ if (ExtraRxBufferSize > 0 || ExtraTxBufferSize > 0) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Dev = VIRTIO_NET_FROM_SNP (This);
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+ if (Dev->Snm.State != EfiSimpleNetworkStarted) {
+ Status = EFI_NOT_STARTED;
+ goto InitFailed;
+ }
+
+ //
+ // In the EfiSimpleNetworkStarted state the virtio-net device has status
+ // value 0 (= reset) -- see the state diagram, the full call chain to
+ // the end of VirtioNetGetFeatures() (considering we're here now),
+ // the DeviceFailed label below, and VirtioNetShutdown().
+ //
+ // Accordingly, the below is a subsequence of the steps found in the
+ // virtio-0.9.5 spec, 2.2.1 Device Initialization Sequence.
+ //
+ NextDevStat = VSTAT_ACK; // step 2 -- acknowledge device presence
+ Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
+ if (EFI_ERROR (Status)) {
+ goto InitFailed;
+ }
+
+ NextDevStat |= VSTAT_DRIVER; // step 3 -- we know how to drive it
+ Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
+ if (EFI_ERROR (Status)) {
+ goto DeviceFailed;
+ }
+
+ //
+ // Set Page Size - MMIO VirtIo Specific
+ //
+ Status = Dev->VirtIo->SetPageSize (Dev->VirtIo, EFI_PAGE_SIZE);
+ if (EFI_ERROR (Status)) {
+ goto DeviceFailed;
+ }
+
+ //
+ // step 4a -- retrieve features. Note that we're past validating required
+ // features in VirtioNetGetFeatures().
+ //
+ Status = Dev->VirtIo->GetDeviceFeatures (Dev->VirtIo, &Features);
+ if (EFI_ERROR (Status)) {
+ goto DeviceFailed;
+ }
+
+ ASSERT (Features & VIRTIO_NET_F_MAC);
+ ASSERT (Dev->Snm.MediaPresentSupported ==
+ !!(Features & VIRTIO_NET_F_STATUS));
+
+ Features &= VIRTIO_NET_F_MAC | VIRTIO_NET_F_STATUS | VIRTIO_F_VERSION_1 |
+ VIRTIO_F_IOMMU_PLATFORM;
+
+ //
+ // In virtio-1.0, feature negotiation is expected to complete before queue
+ // discovery, and the device can also reject the selected set of features.
+ //
+ if (Dev->VirtIo->Revision >= VIRTIO_SPEC_REVISION (1, 0, 0)) {
+ Status = Virtio10WriteFeatures (Dev->VirtIo, Features, &NextDevStat);
+ if (EFI_ERROR (Status)) {
+ goto DeviceFailed;
+ }
+ }
+
+ //
+ // step 4b, 4c -- allocate and report virtqueues
+ //
+ Status = VirtioNetInitRing (
+ Dev,
+ VIRTIO_NET_Q_RX,
+ &Dev->RxRing,
+ &Dev->RxRingMap
+ );
+ if (EFI_ERROR (Status)) {
+ goto DeviceFailed;
+ }
+
+ Status = VirtioNetInitRing (
+ Dev,
+ VIRTIO_NET_Q_TX,
+ &Dev->TxRing,
+ &Dev->TxRingMap
+ );
+ if (EFI_ERROR (Status)) {
+ goto ReleaseRxRing;
+ }
+
+ //
+ // step 5 -- keep only the features we want
+ //
+ if (Dev->VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) {
+ Features &= ~(UINT64)(VIRTIO_F_VERSION_1 | VIRTIO_F_IOMMU_PLATFORM);
+ Status = Dev->VirtIo->SetGuestFeatures (Dev->VirtIo, Features);
+ if (EFI_ERROR (Status)) {
+ goto ReleaseTxRing;
+ }
+ }
+
+ //
+ // step 6 -- virtio-net initialization complete
+ //
+ NextDevStat |= VSTAT_DRIVER_OK;
+ Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
+ if (EFI_ERROR (Status)) {
+ goto ReleaseTxRing;
+ }
+
+ Status = VirtioNetInitTx (Dev);
+ if (EFI_ERROR (Status)) {
+ goto AbortDevice;
+ }
+
+ //
+ // start receiving
+ //
+ Status = VirtioNetInitRx (Dev);
+ if (EFI_ERROR (Status)) {
+ goto ReleaseTxAux;
+ }
+
+ Dev->Snm.State = EfiSimpleNetworkInitialized;
+ gBS->RestoreTPL (OldTpl);
+ return EFI_SUCCESS;
+
+ReleaseTxAux:
+ VirtioNetShutdownTx (Dev);
+
+AbortDevice:
+ Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);
+
+ReleaseTxRing:
+ VirtioNetUninitRing (Dev, &Dev->TxRing, Dev->TxRingMap);
+
+ReleaseRxRing:
+ VirtioNetUninitRing (Dev, &Dev->RxRing, Dev->RxRingMap);
+
+DeviceFailed:
+ //
+ // restore device status invariant for the EfiSimpleNetworkStarted state
+ //
+ Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);
+
+InitFailed:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
diff --git a/roms/edk2/OvmfPkg/VirtioNetDxe/SnpMcastIpToMac.c b/roms/edk2/OvmfPkg/VirtioNetDxe/SnpMcastIpToMac.c new file mode 100644 index 000000000..e16c61c6b --- /dev/null +++ b/roms/edk2/OvmfPkg/VirtioNetDxe/SnpMcastIpToMac.c @@ -0,0 +1,102 @@ +/** @file
+
+ Implementation of the SNP.McastIpToMac() function and its private helpers if
+ any.
+
+ 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 "VirtioNet.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
+VirtioNetMcastIpToMac (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ IN BOOLEAN IPv6,
+ IN EFI_IP_ADDRESS *Ip,
+ OUT EFI_MAC_ADDRESS *Mac
+ )
+{
+ VNET_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 = VIRTIO_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/roms/edk2/OvmfPkg/VirtioNetDxe/SnpReceive.c b/roms/edk2/OvmfPkg/VirtioNetDxe/SnpReceive.c new file mode 100644 index 000000000..cdee9a2ae --- /dev/null +++ b/roms/edk2/OvmfPkg/VirtioNetDxe/SnpReceive.c @@ -0,0 +1,185 @@ +/** @file
+
+ Implementation of the SNP.Receive() function and its private helpers if any.
+
+ 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 "VirtioNet.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
+VirtioNetReceive (
+ 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
+ )
+{
+ VNET_DEV *Dev;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+ UINT16 RxCurUsed;
+ UINT16 UsedElemIdx;
+ UINT32 DescIdx;
+ UINT32 RxLen;
+ UINTN OrigBufferSize;
+ UINT8 *RxPtr;
+ UINT16 AvailIdx;
+ EFI_STATUS NotifyStatus;
+ UINTN RxBufOffset;
+
+ if (This == NULL || BufferSize == NULL || Buffer == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Dev = VIRTIO_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;
+ }
+
+ //
+ // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device
+ //
+ MemoryFence ();
+ RxCurUsed = *Dev->RxRing.Used.Idx;
+ MemoryFence ();
+
+ if (Dev->RxLastUsed == RxCurUsed) {
+ Status = EFI_NOT_READY;
+ goto Exit;
+ }
+
+ UsedElemIdx = Dev->RxLastUsed % Dev->RxRing.QueueSize;
+ DescIdx = Dev->RxRing.Used.UsedElem[UsedElemIdx].Id;
+ RxLen = Dev->RxRing.Used.UsedElem[UsedElemIdx].Len;
+
+ //
+ // the virtio-net request header must be complete; we skip it
+ //
+ ASSERT (RxLen >= Dev->RxRing.Desc[DescIdx].Len);
+ RxLen -= Dev->RxRing.Desc[DescIdx].Len;
+ //
+ // the host must not have filled in more data than requested
+ //
+ ASSERT (RxLen <= Dev->RxRing.Desc[DescIdx + 1].Len);
+
+ 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;
+ }
+
+ RxBufOffset = (UINTN)(Dev->RxRing.Desc[DescIdx + 1].Addr -
+ Dev->RxBufDeviceBase);
+ RxPtr = Dev->RxBuf + RxBufOffset;
+ CopyMem (Buffer, RxPtr, RxLen);
+
+ if (DestAddr != NULL) {
+ CopyMem (DestAddr, RxPtr, SIZE_OF_VNET (Mac));
+ }
+ RxPtr += SIZE_OF_VNET (Mac);
+
+ if (SrcAddr != NULL) {
+ CopyMem (SrcAddr, RxPtr, SIZE_OF_VNET (Mac));
+ }
+ RxPtr += SIZE_OF_VNET (Mac);
+
+ if (Protocol != NULL) {
+ *Protocol = (UINT16) ((RxPtr[0] << 8) | RxPtr[1]);
+ }
+ RxPtr += sizeof (UINT16);
+
+ Status = EFI_SUCCESS;
+
+RecycleDesc:
+ ++Dev->RxLastUsed;
+
+ //
+ // virtio-0.9.5, 2.4.1 Supplying Buffers to The Device
+ //
+ AvailIdx = *Dev->RxRing.Avail.Idx;
+ Dev->RxRing.Avail.Ring[AvailIdx++ % Dev->RxRing.QueueSize] =
+ (UINT16) DescIdx;
+
+ MemoryFence ();
+ *Dev->RxRing.Avail.Idx = AvailIdx;
+
+ MemoryFence ();
+ NotifyStatus = Dev->VirtIo->SetQueueNotify (Dev->VirtIo, VIRTIO_NET_Q_RX);
+ if (!EFI_ERROR (Status)) { // earlier error takes precedence
+ Status = NotifyStatus;
+ }
+
+Exit:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
diff --git a/roms/edk2/OvmfPkg/VirtioNetDxe/SnpReceiveFilters.c b/roms/edk2/OvmfPkg/VirtioNetDxe/SnpReceiveFilters.c new file mode 100644 index 000000000..9d5ce29b7 --- /dev/null +++ b/roms/edk2/OvmfPkg/VirtioNetDxe/SnpReceiveFilters.c @@ -0,0 +1,99 @@ +/** @file
+
+ Implementation of the SNP.ReceiveFilters() function and its private helpers
+ if any.
+
+ 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 "VirtioNet.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
+VirtioNetReceiveFilters (
+ 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
+ )
+{
+ VNET_DEV *Dev;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Dev = VIRTIO_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/roms/edk2/OvmfPkg/VirtioNetDxe/SnpSharedHelpers.c b/roms/edk2/OvmfPkg/VirtioNetDxe/SnpSharedHelpers.c new file mode 100644 index 000000000..d1e3ecfa5 --- /dev/null +++ b/roms/edk2/OvmfPkg/VirtioNetDxe/SnpSharedHelpers.c @@ -0,0 +1,303 @@ +/** @file
+
+ Helper functions used by at least two Simple Network Protocol methods.
+
+ Copyright (C) 2013, Red Hat, Inc.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/MemoryAllocationLib.h>
+
+#include "VirtioNet.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 virtio-net SNP driver instance, or to shutting down a fully
+ initialized, running instance.
+
+ They are only callable by the VirtioNetInitialize() and the
+ VirtioNetShutdown() SNP methods. See the state diagram in "VirtioNet.h".
+
+ @param[in,out] Dev The VNET_DEV driver instance being shut down, or whose
+ partial, failed initialization is being rolled back.
+*/
+
+VOID
+EFIAPI
+VirtioNetShutdownRx (
+ IN OUT VNET_DEV *Dev
+ )
+{
+ Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Dev->RxBufMap);
+ Dev->VirtIo->FreeSharedPages (
+ Dev->VirtIo,
+ Dev->RxBufNrPages,
+ Dev->RxBuf
+ );
+}
+
+
+VOID
+EFIAPI
+VirtioNetShutdownTx (
+ IN OUT VNET_DEV *Dev
+ )
+{
+ ORDERED_COLLECTION_ENTRY *Entry, *Entry2;
+ TX_BUF_MAP_INFO *TxBufMapInfo;
+ VOID *UserStruct;
+
+ Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Dev->TxSharedReqMap);
+ Dev->VirtIo->FreeSharedPages (
+ Dev->VirtIo,
+ EFI_SIZE_TO_PAGES (sizeof *(Dev->TxSharedReq)),
+ Dev->TxSharedReq
+ );
+
+ for (Entry = OrderedCollectionMin (Dev->TxBufCollection);
+ Entry != NULL;
+ Entry = Entry2) {
+ Entry2 = OrderedCollectionNext (Entry);
+ OrderedCollectionDelete (Dev->TxBufCollection, Entry, &UserStruct);
+ TxBufMapInfo = UserStruct;
+ Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, TxBufMapInfo->BufMap);
+ FreePool (TxBufMapInfo);
+ }
+ OrderedCollectionUninit (Dev->TxBufCollection);
+
+ FreePool (Dev->TxFreeStack);
+}
+
+/**
+ Release TX and RX VRING resources.
+
+ @param[in,out] Dev The VNET_DEV driver instance which was using
+ the ring.
+ @param[in,out] Ring The virtio ring to clean up.
+ @param[in] RingMap A token return from the VirtioRingMap()
+*/
+VOID
+EFIAPI
+VirtioNetUninitRing (
+ IN OUT VNET_DEV *Dev,
+ IN OUT VRING *Ring,
+ IN VOID *RingMap
+ )
+{
+ Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, RingMap);
+ VirtioRingUninit (Dev->VirtIo, Ring);
+}
+
+
+/**
+ Map Caller-supplied TxBuf buffer to the device-mapped address
+
+ @param[in] Dev The VNET_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
+VirtioNetMapTxBuf (
+ IN VNET_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 = VirtioMapAllBytesInSharedBuffer (
+ Dev->VirtIo,
+ VirtioOperationBusMasterRead,
+ 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->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Mapping);
+
+FreeTxBufMapInfo:
+ FreePool (TxBufMapInfo);
+ return Status;
+}
+
+/**
+ Unmap (aka reverse mapping) device mapped TxBuf buffer to the system
+ physical address
+
+ @param[in] Dev The VNET_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
+VirtioNetUnmapTxBuf (
+ IN VNET_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->VirtIo->UnmapSharedBuffer (Dev->VirtIo, 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
+VirtioNetTxBufMapInfoCompare (
+ 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
+VirtioNetTxBufDeviceAddressCompare (
+ 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/roms/edk2/OvmfPkg/VirtioNetDxe/SnpShutdown.c b/roms/edk2/OvmfPkg/VirtioNetDxe/SnpShutdown.c new file mode 100644 index 000000000..a217b8379 --- /dev/null +++ b/roms/edk2/OvmfPkg/VirtioNetDxe/SnpShutdown.c @@ -0,0 +1,73 @@ +/** @file
+
+ Implementation of the SNP.Shutdown() function and its private helpers if any.
+
+ Copyright (C) 2013, Red Hat, Inc.
+ Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>
+ Copyright (c) 2017, AMD Inc, All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/UefiBootServicesTableLib.h>
+
+#include "VirtioNet.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
+VirtioNetShutdown (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This
+ )
+{
+ VNET_DEV *Dev;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Dev = VIRTIO_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;
+ }
+
+ Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);
+ VirtioNetShutdownRx (Dev);
+ VirtioNetShutdownTx (Dev);
+ VirtioNetUninitRing (Dev, &Dev->TxRing, Dev->TxRingMap);
+ VirtioNetUninitRing (Dev, &Dev->RxRing, Dev->RxRingMap);
+
+ Dev->Snm.State = EfiSimpleNetworkStarted;
+ Status = EFI_SUCCESS;
+
+Exit:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
diff --git a/roms/edk2/OvmfPkg/VirtioNetDxe/SnpStart.c b/roms/edk2/OvmfPkg/VirtioNetDxe/SnpStart.c new file mode 100644 index 000000000..b8161fbe3 --- /dev/null +++ b/roms/edk2/OvmfPkg/VirtioNetDxe/SnpStart.c @@ -0,0 +1,58 @@ +/** @file
+
+ Implementation of the SNP.Start() function and its private helpers if any.
+
+ 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 "VirtioNet.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
+VirtioNetStart (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This
+ )
+{
+ VNET_DEV *Dev;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Dev = VIRTIO_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/roms/edk2/OvmfPkg/VirtioNetDxe/SnpStop.c b/roms/edk2/OvmfPkg/VirtioNetDxe/SnpStop.c new file mode 100644 index 000000000..3dc7bec8f --- /dev/null +++ b/roms/edk2/OvmfPkg/VirtioNetDxe/SnpStop.c @@ -0,0 +1,59 @@ +/** @file
+
+ Implementation of the SNP.Stop() function and its private helpers if any.
+
+ 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 "VirtioNet.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
+VirtioNetStop (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This
+ )
+{
+ VNET_DEV *Dev;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Dev = VIRTIO_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/roms/edk2/OvmfPkg/VirtioNetDxe/SnpTransmit.c b/roms/edk2/OvmfPkg/VirtioNetDxe/SnpTransmit.c new file mode 100644 index 000000000..2218139b3 --- /dev/null +++ b/roms/edk2/OvmfPkg/VirtioNetDxe/SnpTransmit.c @@ -0,0 +1,178 @@ +/** @file
+
+ Implementation of the SNP.Transmit() function and its private helpers if any.
+
+ 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 "VirtioNet.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
+VirtioNetTransmit (
+ 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
+ )
+{
+ VNET_DEV *Dev;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+ UINT16 DescIdx;
+ UINT16 AvailIdx;
+ EFI_PHYSICAL_ADDRESS DeviceAddress;
+
+ if (This == NULL || BufferSize == 0 || Buffer == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Dev = VIRTIO_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 (SIZE_OF_VNET (Mac) <= sizeof (EFI_MAC_ADDRESS));
+
+ CopyMem (Ptr, DestAddr, SIZE_OF_VNET (Mac));
+ Ptr += SIZE_OF_VNET (Mac);
+
+ CopyMem (Ptr,
+ (SrcAddr == NULL) ? &Dev->Snm.CurrentAddress : SrcAddr,
+ SIZE_OF_VNET (Mac));
+ Ptr += SIZE_OF_VNET (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 = VirtioNetMapTxBuf (
+ Dev,
+ Buffer,
+ BufferSize,
+ &DeviceAddress
+ );
+ if (EFI_ERROR (Status)) {
+ Status = EFI_DEVICE_ERROR;
+ goto Exit;
+ }
+
+ //
+ // virtio-0.9.5, 2.4.1 Supplying Buffers to The Device
+ //
+ DescIdx = Dev->TxFreeStack[Dev->TxCurPending++];
+ Dev->TxRing.Desc[DescIdx + 1].Addr = DeviceAddress;
+ Dev->TxRing.Desc[DescIdx + 1].Len = (UINT32) BufferSize;
+
+ //
+ // the available index is never written by the host, we can read it back
+ // without a barrier
+ //
+ AvailIdx = *Dev->TxRing.Avail.Idx;
+ Dev->TxRing.Avail.Ring[AvailIdx++ % Dev->TxRing.QueueSize] = DescIdx;
+
+ MemoryFence ();
+ *Dev->TxRing.Avail.Idx = AvailIdx;
+
+ MemoryFence ();
+ Status = Dev->VirtIo->SetQueueNotify (Dev->VirtIo, VIRTIO_NET_Q_TX);
+
+Exit:
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+}
diff --git a/roms/edk2/OvmfPkg/VirtioNetDxe/SnpUnsupported.c b/roms/edk2/OvmfPkg/VirtioNetDxe/SnpUnsupported.c new file mode 100644 index 000000000..e41cc31b2 --- /dev/null +++ b/roms/edk2/OvmfPkg/VirtioNetDxe/SnpUnsupported.c @@ -0,0 +1,154 @@ +/** @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) 2013, Red Hat, Inc.
+ Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "VirtioNet.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
+VirtioNetReset (
+ 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
+VirtioNetStationAddress (
+ 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
+VirtioNetStatistics (
+ 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
+VirtioNetNvData (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ IN BOOLEAN ReadWrite,
+ IN UINTN Offset,
+ IN UINTN BufferSize,
+ IN OUT VOID *Buffer
+ )
+{
+ return EFI_UNSUPPORTED;
+}
diff --git a/roms/edk2/OvmfPkg/VirtioNetDxe/TechNotes.txt b/roms/edk2/OvmfPkg/VirtioNetDxe/TechNotes.txt new file mode 100644 index 000000000..5529eac03 --- /dev/null +++ b/roms/edk2/OvmfPkg/VirtioNetDxe/TechNotes.txt @@ -0,0 +1,361 @@ +## @file
+#
+# Technical notes for the virtio-net driver.
+#
+# Copyright (C) 2013, Red Hat, Inc.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+Disclaimer
+----------
+
+All statements concerning standards and specifications are informative and not
+normative. They are made in good faith. Corrections are most welcome on the
+edk2-devel mailing list.
+
+The following documents have been perused while writing the driver and this
+document:
+- Unified Extensible Firmware Interface Specification, Version 2.3.1, Errata C;
+ June 27, 2012
+- Driver Writer's Guide for UEFI 2.3.1, 03/08/2012, Version 1.01;
+- Virtio PCI Card Specification, v0.9.5 DRAFT, 2012 May 7.
+
+
+Summary
+-------
+
+The VirtioNetDxe UEFI_DRIVER implements the Simple Network Protocol for
+virtio-net devices. Higher level protocols are automatically installed on top
+of it by the DXE Core / the ConnectController() boot service, enabling for
+virtio-net devices eg. DHCP configuration, TCP transfers with edk2 StdLib
+applications, and PXE booting in OVMF.
+
+
+UEFI driver structure
+---------------------
+
+A driver instance, belonging to a given virtio-net device, can be in one of
+four states at any time. The states stack up as follows below. The state
+transitions are labeled with the primary function (and its important callees
+faithfully indented) that implement the transition.
+
+ | ^
+ | |
+ [DriverBinding.c] | | [DriverBinding.c]
+ VirtioNetDriverBindingStart | | VirtioNetDriverBindingStop
+ VirtioNetSnpPopulate | | VirtioNetSnpEvacuate
+ VirtioNetGetFeatures | |
+ v |
+ +-------------------------+
+ | EfiSimpleNetworkStopped |
+ +-------------------------+
+ | ^
+ [SnpStart.c] | | [SnpStop.c]
+ VirtioNetStart | | VirtioNetStop
+ | |
+ v |
+ +-------------------------+
+ | EfiSimpleNetworkStarted |
+ +-------------------------+
+ | ^
+ [SnpInitialize.c] | | [SnpShutdown.c]
+ VirtioNetInitialize | | VirtioNetShutdown
+ VirtioNetInitRing {Rx, Tx} | | VirtioNetShutdownRx [SnpSharedHelpers.c]
+ VirtioRingInit | | VirtIo->UnmapSharedBuffer
+ VirtioRingMap | | VirtIo->FreeSharedPages
+ VirtioNetInitTx | | VirtioNetShutdownTx [SnpSharedHelpers.c]
+ VirtIo->AllocateShare... | | VirtIo->UnmapSharedBuffer
+ VirtioMapAllBytesInSh... | | VirtIo->FreeSharedPages
+ VirtioNetInitRx | | VirtioNetUninitRing [SnpSharedHelpers.c]
+ VirtIo->AllocateShare... | | {Tx, Rx}
+ VirtioMapAllBytesInSh... | | VirtIo->UnmapSharedBuffer
+ | | VirtioRingUninit
+ v |
+ +-----------------------------+
+ | EfiSimpleNetworkInitialized |
+ +-----------------------------+
+
+The state at the top means "nonexistent" and is hence unnamed on the diagram --
+a driver instance actually doesn't exist at that point. The transition
+functions out of and into that state implement the Driver Binding Protocol.
+
+The lower three states characterize an existent driver instance and are all
+states defined by the Simple Network Protocol. The transition functions between
+them are member functions of the Simple Network Protocol.
+
+Each transition function validates its expected source state and its
+parameters. For example, VirtioNetDriverBindingStop will refuse to disconnect
+from the controller unless it's in EfiSimpleNetworkStopped.
+
+
+Driver instance states (Simple Network Protocol)
+------------------------------------------------
+
+In the EfiSimpleNetworkStopped state, the virtio-net device is (has been)
+re-set. No resources are allocated for networking / traffic purposes. The MAC
+address and other device attributes have been retrieved from the device (this
+is necessary for completing the VirtioNetDriverBindingStart transition).
+
+The EfiSimpleNetworkStarted is completely identical to the
+EfiSimpleNetworkStopped state for virtio-net, in the functional and
+resource-usage sense. This state is mandated / provided by the Simple Network
+Protocol for flexibility that the virtio-net driver doesn't exploit.
+
+In particular, the EfiSimpleNetworkStarted state is the target of the Shutdown
+SNP member function, and must therefore correspond to a hardware configuration
+where "[it] is safe for another driver to initialize". (Clearly another UEFI
+driver could not do that due to the exclusivity of the driver binding that
+VirtioNetDriverBindingStart() installs, but a later OS driver might qualify.)
+
+The EfiSimpleNetworkInitialized state is the live state of the virtio NIC / the
+driver instance. Virtio and other resources required for network traffic have
+been allocated, and the following SNP member functions are available (in
+addition to VirtioNetShutdown which leaves the state):
+
+- VirtioNetReceive [SnpReceive.c]: poll the virtio NIC for an Rx packet that
+ may have arrived asynchronously;
+
+- VirtioNetTransmit [SnpTransmit.c]: queue a Tx packet for asynchronous
+ transmission (meant to be used together with VirtioNetGetStatus);
+
+- VirtioNetGetStatus [SnpGetStatus.c]: query link status and status of pending
+ Tx packets;
+
+- VirtioNetMcastIpToMac [SnpMcastIpToMac.c]: transform a multicast IPv4/IPv6
+ address into a multicast MAC address;
+
+- VirtioNetReceiveFilters [SnpReceiveFilters.c]: emulate unicast / multicast /
+ broadcast filter configuration (not their actual effect -- a more liberal
+ filter setting than requested is allowed by the UEFI specification).
+
+The following SNP member functions are not supported [SnpUnsupported.c]:
+
+- VirtioNetReset: reinitialize the virtio NIC without shutting it down (a loop
+ from/to EfiSimpleNetworkInitialized);
+
+- VirtioNetStationAddress: assign a new MAC address to the virtio NIC,
+
+- VirtioNetStatistics: collect statistics,
+
+- VirtioNetNvData: access non-volatile data on the virtio NIC.
+
+Missing support for these functions is allowed by the UEFI specification and
+doesn't seem to trip up higher level protocols.
+
+
+Events and task priority levels
+-------------------------------
+
+The UEFI specification defines a sophisticated mechanism for asynchronous
+events / callbacks (see "6.1 Event, Timer, and Task Priority Services" for
+details). Such callbacks work like software interrupts, and some notion of
+locking / masking is important to implement critical sections (atomic or
+exclusive access to data or a device). This notion is defined as Task Priority
+Levels.
+
+The virtio-net driver for OVMF must concern itself with events for two reasons:
+
+- The Simple Network Protocol provides its clients with a (non-optional) WAIT
+ type event called WaitForPacket: it allows them to check or wait for Rx
+ packets by polling or blocking on this event. (This functionality overlaps
+ with the Receive member function.) The event is available to clients starting
+ with EfiSimpleNetworkStopped (inclusive).
+
+ The virtio-net driver is informed about such client polling or blockage by
+ receiving an asynchronous callback (a software interrupt). In the callback
+ function the driver must interrogate the driver instance state, and if it is
+ EfiSimpleNetworkInitialized, access the Rx queue and see if any packets are
+ available for consumption. If so, it must signal the WaitForPacket WAIT type
+ event, waking the client.
+
+ For simplicity and safety, all parts of the virtio-net driver that access any
+ bit of the driver instance (data or device) run at the TPL_CALLBACK level.
+ This is the highest level allowed for an SNP implementation, and all code
+ protected in this manner satisfies even stricter non-blocking requirements
+ than what's documented for TPL_CALLBACK.
+
+ The task priority level for the WaitForPacket callback too is set by the
+ driver, the choice is TPL_CALLBACK again. This in effect serializes the
+ WaitForPacket callback (VirtioNetIsPacketAvailable [Events.c]) with "normal"
+ parts of the driver.
+
+- According to the Driver Writer's Guide, a network driver should install a
+ callback function for the global EXIT_BOOT_SERVICES event (a special NOTIFY
+ type event). When the ExitBootServices() boot service has cleaned up internal
+ firmware state and is about to pass control to the OS, any network driver has
+ to stop any in-flight DMA transfers, lest it corrupts OS memory. For this
+ reason EXIT_BOOT_SERVICES is emitted and the network driver must abort
+ in-flight DMA transfers.
+
+ This callback (VirtioNetExitBoot) is synchronized with the rest of the driver
+ code just the same as explained for WaitForPacket. In
+ EfiSimpleNetworkInitialized state it resets the virtio NIC, halting all data
+ transfer. After the callback returns, no further driver code is expected to
+ be scheduled.
+
+
+Virtio internals -- Rx
+----------------------
+
+Requests (Rx and Tx alike) are always submitted by the guest and processed by
+the host. For Tx, processing means transmission. For Rx, processing means
+filling in the request with an incoming packet. Submitted requests exist on the
+"Available Ring", and answered (processed) requests show up on the "Used Ring".
+
+Packet data includes the media (Ethernet) header: destination MAC, source MAC,
+and Ethertype (14 bytes total).
+
+The following structures implement packet reception. Most of them are defined
+in the Virtio specification, the only driver-specific trait here is the static
+pre-configuration of the two-part descriptor chains, in VirtioNetInitRx. The
+diagram is simplified.
+
+ Available Index Available Index
+ last processed incremented
+ by the host by the guest
+ v -------> v
+Available +-------+-------+-------+-------+-------+
+Ring |DescIdx|DescIdx|DescIdx|DescIdx|DescIdx|
+ +-------+-------+-------+-------+-------+
+ =D6 =D2
+
+ D2 D3 D4 D5 D6 D7
+Descr. +----------+----------++----------+----------++----------+----------+
+Table |Adr:Len:Nx|Adr:Len:Nx||Adr:Len:Nx|Adr:Len:Nx||Adr:Len:Nx|Adr:Len:Nx|
+ +----------+----------++----------+----------++----------+----------+
+ =A2 =D3 =A3 =A4 =D5 =A5 =A6 =D7 =A7
+
+
+ A2 A3 A4 A5 A6 A7
+Receive +---------------+---------------+---------------+
+Destination |vnet hdr:packet|vnet hdr:packet|vnet hdr:packet|
+Area +---------------+---------------+---------------+
+
+ Used Index Used Index incremented
+ last processed by the guest by the host
+ v -------> v
+Used +-----------+-----------+-----------+-----------+-----------+
+Ring |DescIdx:Len|DescIdx:Len|DescIdx:Len|DescIdx:Len|DescIdx:Len|
+ +-----------+-----------+-----------+-----------+-----------+
+ =D4
+
+In VirtioNetInitRx, the guest allocates the fixed size Receive Destination
+Area, which accommodates all packets delivered asynchronously by the host. To
+each packet, a slice of this area is dedicated; each slice is further
+subdivided into virtio-net request header and network packet data. The
+(device-physical) addresses of these sub-slices are denoted with A2, A3, A4 and
+so on. Importantly, an even-subscript "A" always belongs to a virtio-net
+request header, while an odd-subscript "A" always belongs to a packet
+sub-slice.
+
+Furthermore, the guest lays out a static pattern in the Descriptor Table. For
+each packet that can be in-flight or already arrived from the host,
+VirtioNetInitRx sets up a separate, two-part descriptor chain. For packet N,
+the Nth descriptor chain is set up as follows:
+
+- the first (=head) descriptor, with even index, points to the fixed-size
+ sub-slice receiving the virtio-net request header,
+
+- the second descriptor (with odd index) points to the fixed (1514 byte) size
+ sub-slice receiving the packet data,
+
+- a link from the first (head) descriptor in the chain is established to the
+ second (tail) descriptor in the chain.
+
+Finally, the guest populates the Available Ring with the indices of the head
+descriptors. All descriptor indices on both the Available Ring and the Used
+Ring are even.
+
+Packet reception occurs as follows:
+
+- The host consumes a descriptor index off the Available Ring. This index is
+ even (=2*N), and fingers the head descriptor of the chain belonging to packet
+ N.
+
+- The host reads the descriptors D(2*N) and -- following the Next link there
+ --- D(2*N+1), and stores the virtio-net request header at A(2*N), and the
+ packet data at A(2*N+1).
+
+- The host places the index of the head descriptor, 2*N, onto the Used Ring,
+ and sets the Len field in the same Used Ring Element to the total number of
+ bytes transferred for the entire descriptor chain. This enables the guest to
+ identify the length of Rx packets.
+
+- VirtioNetReceive polls the Used Ring. If a new Used Ring Element shows up, it
+ copies the data out to the caller, and recycles the index of the head
+ descriptor (ie. 2*N) to the Available Ring.
+
+- Because the host can process (answer) Rx requests in any order theoretically,
+ the order of head descriptor indices on each of the Available Ring and the
+ Used Ring is virtually random. (Except right after the initial population in
+ VirtioNetInitRx, when the Available Ring is full and increasing, and the Used
+ Ring is empty.)
+
+- If the Available Ring is empty, the host is forced to drop packets. If the
+ Used Ring is empty, VirtioNetReceive returns EFI_NOT_READY (no packet
+ available).
+
+
+Virtio internals -- Tx
+----------------------
+
+The transmission structure erected by VirtioNetInitTx is similar, it differs
+in the following:
+
+- There is no Receive Destination Area.
+
+- Each head descriptor, D(2*N), points to a read-only virtio-net request header
+ that is shared by all of the head descriptors. This virtio-net request header
+ is never modified by the host.
+
+- Each tail descriptor is re-pointed to the device-mapped address of the
+ caller-supplied packet buffer whenever VirtioNetTransmit places the
+ corresponding head descriptor on the Available Ring. A reverse mapping, from
+ the device-mapped address to the caller-supplied packet address, is saved in
+ an associative data structure that belongs to the driver instance.
+
+- Per spec, the caller is responsible to hang on to the unmodified packet
+ buffer until it is reported transmitted by VirtioNetGetStatus.
+
+Steps of packet transmission:
+
+- Client code calls VirtioNetTransmit. VirtioNetTransmit tracks free descriptor
+ chains by keeping the indices of their head descriptors in a stack that is
+ private to the driver instance. All elements of the stack are even.
+
+- If the stack is empty (that is, each descriptor chain, in isolation, is
+ either pending transmission, or has been processed by the host but not
+ yet recycled by a VirtioNetGetStatus call), then VirtioNetTransmit returns
+ EFI_NOT_READY.
+
+- Otherwise the index of a free chain's head descriptor is popped from the
+ stack. The linked tail descriptor is re-pointed as discussed above. The head
+ descriptor's index is pushed on the Available Ring.
+
+- The host moves the head descriptor index from the Available Ring to the Used
+ Ring when it transmits the packet.
+
+- Client code calls VirtioNetGetStatus. In case the Used Ring is empty, the
+ function reports no Tx completion. Otherwise, a head descriptor's index is
+ consumed from the Used Ring and recycled to the private stack. The client
+ code's original packet buffer address is calculated by fetching the
+ device-mapped address from the tail descriptor (where it has been stored at
+ VirtioNetTransmit time), and by looking up the device-mapped address in the
+ associative data structure. The reverse-mapped packet buffer address is
+ returned to the caller.
+
+- The Len field of the Used Ring Element is not checked. The host is assumed to
+ have transmitted the entire packet -- VirtioNetTransmit had forced it below
+ 1514 bytes (inclusive). The Virtio specification suggests this packet size is
+ always accepted (and a lower MTU could be encountered on any later hop as
+ well). Additionally, there's no good way to report a short transmit via
+ VirtioNetGetStatus; EFI_DEVICE_ERROR seems too serious from the specification
+ and higher level protocols could interpret it as a fatal condition.
+
+- The host can theoretically reorder head descriptor indices when moving them
+ from the Available Ring to the Used Ring (out of order transmission). Because
+ of this (and the choice of a stack over a list for free descriptor chain
+ tracking) the order of head descriptor indices on either Ring is
+ unpredictable.
diff --git a/roms/edk2/OvmfPkg/VirtioNetDxe/VirtioNet.h b/roms/edk2/OvmfPkg/VirtioNetDxe/VirtioNet.h new file mode 100644 index 000000000..711b7c4cf --- /dev/null +++ b/roms/edk2/OvmfPkg/VirtioNetDxe/VirtioNet.h @@ -0,0 +1,332 @@ +/** @file
+
+ Internal definitions for the virtio-net driver, which produces Simple Network
+ Protocol instances for virtio-net devices.
+
+ Copyright (C) 2013, Red Hat, Inc.
+ Copyright (c) 2017, AMD Inc, All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef _VIRTIO_NET_DXE_H_
+#define _VIRTIO_NET_DXE_H_
+
+#include <IndustryStandard/VirtioNet.h>
+#include <Library/DebugLib.h>
+#include <Library/VirtioLib.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>
+
+#define VNET_SIG SIGNATURE_32 ('V', 'N', 'E', 'T')
+
+//
+// maximum number of pending packets, separately for each direction
+//
+#define VNET_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
+ VIRTIO_DEVICE_PROTOCOL *VirtIo; // 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
+
+ VRING RxRing; // VirtioNetInitRing
+ VOID *RxRingMap; // VirtioRingMap and
+ // VirtioNetInitRing
+ UINT8 *RxBuf; // VirtioNetInitRx
+ UINT16 RxLastUsed; // VirtioNetInitRx
+ UINTN RxBufNrPages; // VirtioNetInitRx
+ EFI_PHYSICAL_ADDRESS RxBufDeviceBase; // VirtioNetInitRx
+ VOID *RxBufMap; // VirtioNetInitRx
+
+ VRING TxRing; // VirtioNetInitRing
+ VOID *TxRingMap; // VirtioRingMap and
+ // VirtioNetInitRing
+ UINT16 TxMaxPending; // VirtioNetInitTx
+ UINT16 TxCurPending; // VirtioNetInitTx
+ UINT16 *TxFreeStack; // VirtioNetInitTx
+ VIRTIO_1_0_NET_REQ *TxSharedReq; // VirtioNetInitTx
+ VOID *TxSharedReqMap; // VirtioNetInitTx
+ UINT16 TxLastUsed; // VirtioNetInitTx
+ ORDERED_COLLECTION *TxBufCollection; // VirtioNetInitTx
+} VNET_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 VIRTIO_NET_FROM_SNP(SnpPointer) \
+ CR (SnpPointer, VNET_DEV, Snp, VNET_SIG)
+
+#define VIRTIO_CFG_WRITE(Dev, Field, Value) ((Dev)->VirtIo->WriteDevice ( \
+ (Dev)->VirtIo, \
+ OFFSET_OF_VNET (Field), \
+ SIZE_OF_VNET (Field), \
+ (Value) \
+ ))
+
+#define VIRTIO_CFG_READ(Dev, Field, Pointer) ((Dev)->VirtIo->ReadDevice ( \
+ (Dev)->VirtIo, \
+ OFFSET_OF_VNET (Field), \
+ SIZE_OF_VNET (Field), \
+ sizeof *(Pointer), \
+ (Pointer) \
+ ))
+
+//
+// component naming
+//
+extern EFI_COMPONENT_NAME_PROTOCOL gVirtioNetComponentName;
+extern EFI_COMPONENT_NAME2_PROTOCOL gVirtioNetComponentName2;
+
+//
+// driver binding
+//
+extern EFI_DRIVER_BINDING_PROTOCOL gVirtioNetDriverBinding;
+
+//
+// member functions implementing the Simple Network Protocol
+//
+EFI_STATUS
+EFIAPI
+VirtioNetStart (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This
+ );
+
+EFI_STATUS
+EFIAPI
+VirtioNetStop (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This
+ );
+
+EFI_STATUS
+EFIAPI
+VirtioNetInitialize (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ IN UINTN ExtraRxBufferSize OPTIONAL,
+ IN UINTN ExtraTxBufferSize OPTIONAL
+ );
+
+EFI_STATUS
+EFIAPI
+VirtioNetReset (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ IN BOOLEAN ExtendedVerification
+ );
+
+EFI_STATUS
+EFIAPI
+VirtioNetShutdown (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This
+ );
+
+EFI_STATUS
+EFIAPI
+VirtioNetReceiveFilters (
+ 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
+VirtioNetStationAddress (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ IN BOOLEAN Reset,
+ IN EFI_MAC_ADDRESS *New OPTIONAL
+ );
+
+EFI_STATUS
+EFIAPI
+VirtioNetStatistics (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ IN BOOLEAN Reset,
+ IN OUT UINTN *StatisticsSize OPTIONAL,
+ OUT EFI_NETWORK_STATISTICS *StatisticsTable OPTIONAL
+ );
+
+EFI_STATUS
+EFIAPI
+VirtioNetMcastIpToMac (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ IN BOOLEAN IPv6,
+ IN EFI_IP_ADDRESS *Ip,
+ OUT EFI_MAC_ADDRESS *Mac
+ );
+
+EFI_STATUS
+EFIAPI
+VirtioNetNvData (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ IN BOOLEAN ReadWrite,
+ IN UINTN Offset,
+ IN UINTN BufferSize,
+ IN OUT VOID *Buffer
+ );
+
+EFI_STATUS
+EFIAPI
+VirtioNetGetStatus (
+ IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
+ OUT UINT32 *InterruptStatus OPTIONAL,
+ OUT VOID **TxBuf OPTIONAL
+ );
+
+EFI_STATUS
+EFIAPI
+VirtioNetTransmit (
+ 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
+VirtioNetReceive (
+ 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
+VirtioNetShutdownRx (
+ IN OUT VNET_DEV *Dev
+ );
+
+VOID
+EFIAPI
+VirtioNetShutdownTx (
+ IN OUT VNET_DEV *Dev
+ );
+
+VOID
+EFIAPI
+VirtioNetUninitRing (
+ IN OUT VNET_DEV *Dev,
+ IN OUT VRING *Ring,
+ IN VOID *RingMap
+ );
+
+//
+// utility functions to map caller-supplied Tx buffer system physical address
+// to a device address and vice versa
+//
+EFI_STATUS
+EFIAPI
+VirtioNetMapTxBuf (
+ IN VNET_DEV *Dev,
+ IN VOID *Buffer,
+ IN UINTN NumberOfBytes,
+ OUT EFI_PHYSICAL_ADDRESS *DeviceAddress
+ );
+
+EFI_STATUS
+EFIAPI
+VirtioNetUnmapTxBuf (
+ IN VNET_DEV *Dev,
+ OUT VOID **Buffer,
+ IN EFI_PHYSICAL_ADDRESS DeviceAddress
+ );
+
+INTN
+EFIAPI
+VirtioNetTxBufMapInfoCompare (
+ IN CONST VOID *UserStruct1,
+ IN CONST VOID *UserStruct2
+ );
+
+INTN
+EFIAPI
+VirtioNetTxBufDeviceAddressCompare (
+ IN CONST VOID *StandaloneKey,
+ IN CONST VOID *UserStruct
+ );
+
+
+//
+// event callbacks
+//
+VOID
+EFIAPI
+VirtioNetIsPacketAvailable (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+VOID
+EFIAPI
+VirtioNetExitBoot (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+#endif // _VIRTIO_NET_DXE_H_
diff --git a/roms/edk2/OvmfPkg/VirtioNetDxe/VirtioNet.inf b/roms/edk2/OvmfPkg/VirtioNetDxe/VirtioNet.inf new file mode 100644 index 000000000..ada84ed55 --- /dev/null +++ b/roms/edk2/OvmfPkg/VirtioNetDxe/VirtioNet.inf @@ -0,0 +1,56 @@ +## @file
+#
+# This driver produces Simple Network Protocol instances for virtio-net
+# devices.
+#
+# Copyright (C) 2013, Red Hat, Inc.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = VirtioNetDxe
+ FILE_GUID = A92CDB4B-82F1-4E0B-A516-8A655D371524
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = VirtioNetEntryPoint
+
+[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
+ VirtioNet.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ BaseMemoryLib
+ DebugLib
+ DevicePathLib
+ MemoryAllocationLib
+ OrderedCollectionLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ UefiLib
+ VirtioLib
+
+[Protocols]
+ gEfiSimpleNetworkProtocolGuid ## BY_START
+ gEfiDevicePathProtocolGuid ## BY_START
+ gVirtioDeviceProtocolGuid ## TO_START
|