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