diff options
-rw-r--r-- | OvmfPkg/VirtioNetDxe/DriverBinding.c | 674 |
1 files changed, 674 insertions, 0 deletions
diff --git a/OvmfPkg/VirtioNetDxe/DriverBinding.c b/OvmfPkg/VirtioNetDxe/DriverBinding.c new file mode 100644 index 0000000000..d9c0096d08 --- /dev/null +++ b/OvmfPkg/VirtioNetDxe/DriverBinding.c @@ -0,0 +1,674 @@ +/** @file
+
+ Driver Binding code and its private helpers for the virtio-net driver.
+
+ Copyright (C) 2013, Red Hat, Inc.
+ Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials are licensed and made available
+ under the terms and conditions of the BSD License which accompanies this
+ distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
+ WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include <IndustryStandard/Pci.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+#include "VirtioNet.h"
+
+#define RECEIVE_FILTERS_NO_MCAST ((UINT32) ( \
+ EFI_SIMPLE_NETWORK_RECEIVE_UNICAST | \
+ EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST | \
+ EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS \
+ ))
+
+/*
+ Temporarily enable then reset the virtio-net device in order to retrieve
+ configuration values needed by Simple Network Protocol and Simple Network
+ Mode fields.
+
+ Only VirtioNetSnpPopulate() may call this function.
+
+ If the function fails for any reason, the virtio-net device is moved to
+ VSTAT_FAILED instead of being reset. This serves only informative purposes
+ for the host side.
+
+ param[in,out] Dev The VNET_DEV structure being created for
+ the virtio-net device.
+ param[out] MacAddress MAC address configured by the host.
+ param[out] MediaPresentSupported Link status is made available by the host.
+ param[out] MediaPresent If link status is made available by the
+ host, the current link status is stored in
+ *MediaPresent. Otherwise MediaPresent is
+ unused.
+
+ @retval EFI_UNSUPPORTED The host doesn't supply a MAC address.
+ @return Status codes from Dev->PciIo->Io.Read(),
+ VIRTIO_CFG_READ() and VIRTIO_CFG_WRITE().
+ @retval EFI_SUCCESS Configuration values retrieved.
+*/
+STATIC
+EFI_STATUS
+EFIAPI
+VirtioNetGetFeatures (
+ IN OUT VNET_DEV *Dev,
+ OUT EFI_MAC_ADDRESS *MacAddress,
+ OUT BOOLEAN *MediaPresentSupported,
+ OUT BOOLEAN *MediaPresent
+ )
+{
+ EFI_STATUS Status;
+ UINT8 NextDevStat;
+ UINT32 Features;
+ UINT16 LinkStatus;
+
+ //
+ // Interrogate the device for features (virtio-0.9.5, 2.2.1 Device
+ // Initialization Sequence), but don't complete setting it up.
+ //
+ NextDevStat = 0; // step 1 -- reset device
+ Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrDeviceStatus, NextDevStat);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ NextDevStat |= VSTAT_ACK; // step 2 -- acknowledge device presence
+ Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrDeviceStatus, NextDevStat);
+ if (EFI_ERROR (Status)) {
+ goto YieldDevice;
+ }
+
+ NextDevStat |= VSTAT_DRIVER; // step 3 -- we know how to drive it
+ Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrDeviceStatus, NextDevStat);
+ if (EFI_ERROR (Status)) {
+ goto YieldDevice;
+ }
+
+ //
+ // step 4a -- retrieve and validate features
+ //
+ Status = VIRTIO_CFG_READ (Dev, Generic.VhdrDeviceFeatureBits, &Features);
+ if (EFI_ERROR (Status)) {
+ goto YieldDevice;
+ }
+
+ //
+ // get MAC address byte-wise
+ //
+ if ((Features & VIRTIO_NET_F_MAC) == 0) {
+ Status = EFI_UNSUPPORTED;
+ goto YieldDevice;
+ }
+ Status = Dev->PciIo->Io.Read (Dev->PciIo, // PciIo
+ EfiPciIoWidthUint8, // Width
+ PCI_BAR_IDX0, // BarIndex
+ OFFSET_OF_VNET (VhdrMac), // Offset
+ SIZE_OF_VNET (VhdrMac), // Count
+ MacAddress // Buffer
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto YieldDevice;
+ }
+
+ //
+ // check if link status is reported, and if so, what the link status is
+ //
+ if ((Features & VIRTIO_NET_F_STATUS) == 0) {
+ *MediaPresentSupported = FALSE;
+ }
+ else {
+ *MediaPresentSupported = TRUE;
+ Status = VIRTIO_CFG_READ (Dev, VhdrLinkStatus, &LinkStatus);
+ if (EFI_ERROR (Status)) {
+ goto YieldDevice;
+ }
+ *MediaPresent = !!(LinkStatus & VIRTIO_NET_S_LINK_UP);
+ }
+
+YieldDevice:
+ VIRTIO_CFG_WRITE (Dev, Generic.VhdrDeviceStatus,
+ EFI_ERROR (Status) ? VSTAT_FAILED : 0);
+
+ return Status;
+}
+
+
+/**
+ Set up the Simple Network Protocol fields, the Simple Network Mode fields,
+ and the Exit Boot Services Event of the virtio-net driver instance.
+
+ This function may only be called by VirtioNetDriverBindingStart().
+
+ @param[in,out] Dev The VNET_DEV driver instance being created for the
+ virtio-net device.
+
+ @return Status codes from the CreateEvent() boot service or the
+ VirtioNetGetFeatures() function.
+ @retval EFI_SUCCESS Configuration successful.
+*/
+STATIC
+EFI_STATUS
+EFIAPI
+VirtioNetSnpPopulate (
+ IN OUT VNET_DEV *Dev
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // We set up a function here that is asynchronously callable by an
+ // external application to check if there are any packets available for
+ // reception. The least urgent task priority level we can specify for such a
+ // "software interrupt" is TPL_CALLBACK.
+ //
+ // TPL_CALLBACK is also the maximum TPL an SNP implementation is allowed to
+ // run at (see 6.1 Event, Timer, and Task Priority Services in the UEFI
+ // Specification 2.3.1+errC).
+ //
+ // Since we raise our TPL to TPL_CALLBACK in every single function that
+ // accesses the device, and the external application also queues its interest
+ // for received packets at the same TPL_CALLBACK, in effect the
+ // VirtioNetIsPacketAvailable() function will never interrupt any
+ // device-accessing driver function, it will be scheduled in isolation.
+ //
+ // TPL_CALLBACK (which basically this entire driver runs at) is allowed
+ // for "[l]ong term operations (such as file system operations and disk
+ // I/O)". Because none of our functions block, we'd satisfy an even stronger
+ // requirement.
+ //
+ Status = gBS->CreateEvent (EVT_NOTIFY_WAIT, TPL_CALLBACK,
+ &VirtioNetIsPacketAvailable, Dev, &Dev->Snp.WaitForPacket);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Dev->Snp.Revision = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION;
+ Dev->Snp.Start = &VirtioNetStart;
+ Dev->Snp.Stop = &VirtioNetStop;
+ Dev->Snp.Initialize = &VirtioNetInitialize;
+ Dev->Snp.Reset = &VirtioNetReset;
+ Dev->Snp.Shutdown = &VirtioNetShutdown;
+ Dev->Snp.ReceiveFilters = &VirtioNetReceiveFilters;
+ Dev->Snp.StationAddress = &VirtioNetStationAddress;
+ Dev->Snp.Statistics = &VirtioNetStatistics;
+ Dev->Snp.MCastIpToMac = &VirtioNetMcastIpToMac;
+ Dev->Snp.NvData = &VirtioNetNvData;
+ Dev->Snp.GetStatus = &VirtioNetGetStatus;
+ Dev->Snp.Transmit = &VirtioNetTransmit;
+ Dev->Snp.Receive = &VirtioNetReceive;
+ Dev->Snp.Mode = &Dev->Snm;
+
+ Dev->Snm.State = EfiSimpleNetworkStopped;
+ Dev->Snm.HwAddressSize = SIZE_OF_VNET (VhdrMac);
+ Dev->Snm.MediaHeaderSize = SIZE_OF_VNET (VhdrMac) + // dst MAC
+ SIZE_OF_VNET (VhdrMac) + // src MAC
+ 2; // Ethertype
+ Dev->Snm.MaxPacketSize = 1500;
+ Dev->Snm.NvRamSize = 0;
+ Dev->Snm.NvRamAccessSize = 0;
+ Dev->Snm.ReceiveFilterMask = RECEIVE_FILTERS_NO_MCAST;
+ Dev->Snm.ReceiveFilterSetting = RECEIVE_FILTERS_NO_MCAST;
+ Dev->Snm.MaxMCastFilterCount = 0;
+ Dev->Snm.MCastFilterCount = 0;
+ Dev->Snm.IfType = 1; // ethernet
+ Dev->Snm.MacAddressChangeable = FALSE;
+ Dev->Snm.MultipleTxSupported = TRUE;
+
+ ASSERT (SIZE_OF_VNET (VhdrMac) <= sizeof (EFI_MAC_ADDRESS));
+
+ Status = VirtioNetGetFeatures (Dev, &Dev->Snm.CurrentAddress,
+ &Dev->Snm.MediaPresentSupported, &Dev->Snm.MediaPresent);
+ if (EFI_ERROR (Status)) {
+ goto CloseWaitForPacket;
+ }
+ CopyMem (&Dev->Snm.PermanentAddress, &Dev->Snm.CurrentAddress,
+ SIZE_OF_VNET (VhdrMac));
+ SetMem (&Dev->Snm.BroadcastAddress, SIZE_OF_VNET (VhdrMac), 0xFF);
+
+ //
+ // VirtioNetExitBoot() is queued by ExitBootServices(); its purpose is to
+ // cancel any pending virtio requests. The TPL_CALLBACK reasoning is
+ // identical to the one above. There's one difference: this kind of
+ // event is "globally visible", which means it can be signalled as soon as
+ // we create it. We haven't raised our TPL here, hence VirtioNetExitBoot()
+ // could be entered immediately. VirtioNetExitBoot() checks Dev->Snm.State,
+ // so we're safe.
+ //
+ Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK,
+ &VirtioNetExitBoot, Dev, &Dev->ExitBoot);
+ if (EFI_ERROR (Status)) {
+ goto CloseWaitForPacket;
+ }
+
+ return EFI_SUCCESS;
+
+CloseWaitForPacket:
+ gBS->CloseEvent (Dev->Snp.WaitForPacket);
+ return Status;
+}
+
+
+/**
+ Release any resources allocated by VirtioNetSnpPopulate().
+
+ This function may only be called by VirtioNetDriverBindingStart(), when
+ rolling back a partial, failed driver instance creation, and by
+ VirtioNetDriverBindingStop(), when disconnecting a virtio-net device from the
+ driver.
+
+ @param[in,out] Dev The VNET_DEV driver instance being destroyed.
+*/
+STATIC
+VOID
+EFIAPI
+VirtioNetSnpEvacuate (
+ IN OUT VNET_DEV *Dev
+ )
+{
+ //
+ // This function runs either at TPL_CALLBACK already (from
+ // VirtioNetDriverBindingStop()), or it is part of a teardown following
+ // a partial, failed construction in VirtioNetDriverBindingStart(), when
+ // WaitForPacket was never accessible to the world.
+ //
+ gBS->CloseEvent (Dev->ExitBoot);
+ gBS->CloseEvent (Dev->Snp.WaitForPacket);
+}
+
+
+/**
+ Tests to see if this driver supports a given controller. If a child device is
+ provided, it further tests to see if this driver supports creating a handle
+ for the specified child device.
+
+ This function checks to see if the driver specified by This supports the
+ device specified by ControllerHandle. Drivers will typically use the device
+ path attached to ControllerHandle and/or the services from the bus I/O
+ abstraction attached to ControllerHandle to determine if the driver supports
+ ControllerHandle. This function may be called many times during platform
+ initialization. In order to reduce boot times, the tests performed by this
+ function must be very small, and take as little time as possible to execute.
+ This function must not change the state of any hardware devices, and this
+ function must be aware that the device specified by ControllerHandle may
+ already be managed by the same driver or a different driver. This function
+ must match its calls to AllocatePages() with FreePages(), AllocatePool() with
+ FreePool(), and OpenProtocol() with CloseProtocol(). Because ControllerHandle
+ may have been previously started by the same driver, if a protocol is already
+ in the opened state, then it must not be closed with CloseProtocol(). This is
+ required to guarantee the state of ControllerHandle is not modified by this
+ function.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL
+ instance.
+ @param[in] ControllerHandle The handle of the controller to test. This
+ handle must support a protocol interface
+ that supplies an I/O abstraction to the
+ driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a
+ device path. This parameter is ignored by
+ device drivers, and is optional for bus
+ drivers. For bus drivers, if this parameter
+ is not NULL, then the bus driver must
+ determine if the bus controller specified by
+ ControllerHandle and the child controller
+ specified by RemainingDevicePath are both
+ supported by this bus driver.
+
+ @retval EFI_SUCCESS The device specified by ControllerHandle and
+ RemainingDevicePath is supported by the
+ driver specified by This.
+ @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed
+ by the driver specified by This.
+ @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed
+ by a different driver or an application that
+ requires exclusive access. Currently not
+ implemented.
+ @retval EFI_UNSUPPORTED The device specified by ControllerHandle and
+ RemainingDevicePath is not supported by the
+ driver specified by This.
+**/
+
+STATIC
+EFI_STATUS
+EFIAPI
+VirtioNetDriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE DeviceHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ EFI_STATUS Status;
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ PCI_TYPE00 Pci;
+
+ Status = gBS->OpenProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,
+ (VOID **)&PciIo, This->DriverBindingHandle, DeviceHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, 0,
+ sizeof Pci / sizeof (UINT32), &Pci);
+
+ //
+ // virtio-0.9.5, 2.1 PCI Discovery:
+ // the network device has Subsystem Device ID 1
+ //
+ if (Status == EFI_SUCCESS) {
+ Status = (Pci.Hdr.VendorId == 0x1AF4 &&
+ Pci.Hdr.DeviceId >= 0x1000 && Pci.Hdr.DeviceId <= 0x103F &&
+ Pci.Hdr.RevisionID == 0x00 &&
+ Pci.Device.SubsystemID == 0x01) ? EFI_SUCCESS : EFI_UNSUPPORTED;
+ }
+
+ gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,
+ This->DriverBindingHandle, DeviceHandle);
+ return Status;
+}
+
+
+/**
+ Starts a device controller or a bus controller.
+
+ The Start() function is designed to be invoked from the EFI boot service
+ ConnectController(). As a result, much of the error checking on the
+ parameters to Start() has been moved into this common boot service. It is
+ legal to call Start() from other locations, but the following calling
+ restrictions must be followed, or the system behavior will not be
+ deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE.
+ 2. If RemainingDevicePath is not NULL, then it must be a pointer to a
+ naturally aligned EFI_DEVICE_PATH_PROTOCOL.
+ 3. Prior to calling Start(), the Supported() function for the driver
+ specified by This must have been called with the same calling parameters,
+ and Supported() must have returned EFI_SUCCESS.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL
+ instance.
+ @param[in] ControllerHandle The handle of the controller to start. This
+ handle must support a protocol interface
+ that supplies an I/O abstraction to the
+ driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a
+ device path. This parameter is ignored by
+ device drivers, and is optional for bus
+ drivers. For a bus driver, if this parameter
+ is NULL, then handles for all the children
+ of Controller are created by this driver.
+ If this parameter is not NULL and the first
+ Device Path Node is not the End of Device
+ Path Node, then only the handle for the
+ child device specified by the first Device
+ Path Node of RemainingDevicePath is created
+ by this driver. If the first Device Path
+ Node of RemainingDevicePath is the End of
+ Device Path Node, no child handle is created
+ by this driver.
+
+ @retval EFI_SUCCESS The device was started.
+ @retval EFI_DEVICE_ERROR The device could not be started due to a
+ device error.Currently not implemented.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a
+ lack of resources.
+ @retval Others The driver failded to start the device.
+
+**/
+
+STATIC
+EFI_STATUS
+EFIAPI
+VirtioNetDriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE DeviceHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ EFI_STATUS Status;
+ VNET_DEV *Dev;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ MAC_ADDR_DEVICE_PATH MacNode;
+ VOID *ChildPciIo;
+
+ //
+ // allocate space for the driver instance
+ //
+ Dev = (VNET_DEV *) AllocateZeroPool (sizeof *Dev);
+ if (Dev == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ Dev->Signature = VNET_SIG;
+
+ //
+ // get PCI access to the device and keep it open
+ //
+ Status = gBS->OpenProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,
+ (VOID **)&Dev->PciIo, This->DriverBindingHandle,
+ DeviceHandle, EFI_OPEN_PROTOCOL_BY_DRIVER);
+ if (EFI_ERROR (Status)) {
+ goto FreeVirtioNet;
+ }
+
+ //
+ // save original PCI attributes and enable IO space access
+ //
+ Status = Dev->PciIo->Attributes (Dev->PciIo, EfiPciIoAttributeOperationGet,
+ 0, &Dev->OrigPciAttributes);
+ if (EFI_ERROR (Status)) {
+ goto ClosePciIo;
+ }
+
+ Status = Dev->PciIo->Attributes (Dev->PciIo,
+ EfiPciIoAttributeOperationEnable,
+ EFI_PCI_IO_ATTRIBUTE_IO, NULL);
+ if (EFI_ERROR (Status)) {
+ goto ClosePciIo;
+ }
+
+ //
+ // now we can run a basic one-shot virtio-net initialization required to
+ // retrieve the MAC address
+ //
+ Status = VirtioNetSnpPopulate (Dev);
+ if (EFI_ERROR (Status)) {
+ goto RestorePciAttributes;
+ }
+
+ //
+ // get the device path of the virtio-net PCI device -- one-shot open
+ //
+ Status = gBS->OpenProtocol (DeviceHandle, &gEfiDevicePathProtocolGuid,
+ (VOID **)&DevicePath, This->DriverBindingHandle,
+ DeviceHandle, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ if (EFI_ERROR (Status)) {
+ goto Evacuate;
+ }
+
+ //
+ // create another device path that has the MAC address appended
+ //
+ MacNode.Header.Type = MESSAGING_DEVICE_PATH;
+ MacNode.Header.SubType = MSG_MAC_ADDR_DP;
+ SetDevicePathNodeLength (&MacNode, sizeof MacNode);
+ CopyMem (&MacNode.MacAddress, &Dev->Snm.CurrentAddress,
+ sizeof (EFI_MAC_ADDRESS));
+ MacNode.IfType = Dev->Snm.IfType;
+
+ Dev->MacDevicePath = AppendDevicePathNode (DevicePath, &MacNode.Header);
+ if (Dev->MacDevicePath == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Evacuate;
+ }
+
+ //
+ // create a child handle with the Simple Network Protocol and the new
+ // device path installed on it
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (&Dev->MacHandle,
+ &gEfiSimpleNetworkProtocolGuid, &Dev->Snp,
+ &gEfiDevicePathProtocolGuid, Dev->MacDevicePath,
+ NULL);
+ if (EFI_ERROR (Status)) {
+ goto FreeMacDevicePath;
+ }
+
+ //
+ // make a note that we keep this device open with PciIo for the sake of this
+ // child
+ //
+ Status = gBS->OpenProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,
+ &ChildPciIo, This->DriverBindingHandle,
+ Dev->MacHandle, EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER);
+ if (EFI_ERROR (Status)) {
+ goto UninstallMultiple;
+ }
+
+ return EFI_SUCCESS;
+
+UninstallMultiple:
+ gBS->UninstallMultipleProtocolInterfaces (Dev->MacHandle,
+ &gEfiDevicePathProtocolGuid, Dev->MacDevicePath,
+ &gEfiSimpleNetworkProtocolGuid, &Dev->Snp,
+ NULL);
+
+FreeMacDevicePath:
+ FreePool (Dev->MacDevicePath);
+
+Evacuate:
+ VirtioNetSnpEvacuate (Dev);
+
+RestorePciAttributes:
+ Dev->PciIo->Attributes (Dev->PciIo, EfiPciIoAttributeOperationSet,
+ Dev->OrigPciAttributes, NULL);
+
+ClosePciIo:
+ gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,
+ This->DriverBindingHandle, DeviceHandle);
+
+FreeVirtioNet:
+ FreePool (Dev);
+
+ return Status;
+}
+
+
+/**
+ Stops a device controller or a bus controller.
+
+ The Stop() function is designed to be invoked from the EFI boot service
+ DisconnectController(). As a result, much of the error checking on the
+ parameters to Stop() has been moved into this common boot service. It is
+ legal to call Stop() from other locations, but the following calling
+ restrictions must be followed, or the system behavior will not be
+ deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous
+ call to this same driver's Start() function.
+ 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a
+ valid EFI_HANDLE. In addition, all of these handles must have been created
+ in this driver's Start() function, and the Start() function must have
+ called OpenProtocol() on ControllerHandle with an Attribute of
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL
+ instance.
+ @param[in] ControllerHandle A handle to the device being stopped. The
+ handle must support a bus specific I/O
+ protocol for the driver to use to stop the
+ device.
+ @param[in] NumberOfChildren The number of child device handles in
+ ChildHandleBuffer.
+ @param[in] ChildHandleBuffer An array of child handles to be freed. May be
+ NULL if NumberOfChildren is 0.
+
+ @retval EFI_SUCCESS The device was stopped.
+ @retval EFI_DEVICE_ERROR The device could not be stopped due to a device
+ error.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+VirtioNetDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE DeviceHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ )
+{
+ if (NumberOfChildren > 0) {
+ //
+ // free all resources for whose access we need the child handle, because
+ // the child handle is going away
+ //
+ EFI_STATUS Status;
+ EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
+ VNET_DEV *Dev;
+ EFI_TPL OldTpl;
+
+ ASSERT (NumberOfChildren == 1);
+
+ Status = gBS->OpenProtocol (ChildHandleBuffer[0],
+ &gEfiSimpleNetworkProtocolGuid, (VOID **)&Snp,
+ This->DriverBindingHandle, DeviceHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ ASSERT_EFI_ERROR (Status);
+ Dev = VIRTIO_NET_FROM_SNP (Snp);
+
+ //
+ // prevent any interference with WaitForPacket
+ //
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ ASSERT (Dev->MacHandle == ChildHandleBuffer[0]);
+ if (Dev->Snm.State != EfiSimpleNetworkStopped) {
+ //
+ // device in use, cannot stop driver instance
+ //
+ Status = EFI_DEVICE_ERROR;
+ }
+ else {
+ gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,
+ This->DriverBindingHandle, Dev->MacHandle);
+ gBS->UninstallMultipleProtocolInterfaces (Dev->MacHandle,
+ &gEfiDevicePathProtocolGuid, Dev->MacDevicePath,
+ &gEfiSimpleNetworkProtocolGuid, &Dev->Snp,
+ NULL);
+ FreePool (Dev->MacDevicePath);
+ VirtioNetSnpEvacuate (Dev);
+ Dev->PciIo->Attributes (Dev->PciIo, EfiPciIoAttributeOperationSet,
+ Dev->OrigPciAttributes, NULL);
+ FreePool (Dev);
+ }
+
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+ }
+
+ //
+ // release remaining resources, tied directly to the parent handle
+ //
+ gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,
+ This->DriverBindingHandle, DeviceHandle);
+
+ return EFI_SUCCESS;
+}
+
+
+EFI_DRIVER_BINDING_PROTOCOL gVirtioNetDriverBinding = {
+ &VirtioNetDriverBindingSupported,
+ &VirtioNetDriverBindingStart,
+ &VirtioNetDriverBindingStop,
+ 0x10,
+ NULL,
+ NULL
+};
|