Contributed-under: TianoCore Contribution Agreement 1.0

Signed-off-by: Laszlo Ersek <ler...@redhat.com>
---
 OvmfPkg/VirtioNetDxe/DriverBinding.c |  676 ++++++++++++++++++++++++++++++++++
 1 files changed, 676 insertions(+), 0 deletions(-)
 create mode 100644 OvmfPkg/VirtioNetDxe/DriverBinding.c

diff --git a/OvmfPkg/VirtioNetDxe/DriverBinding.c 
b/OvmfPkg/VirtioNetDxe/DriverBinding.c
new file mode 100644
index 0000000..d9cbe73
--- /dev/null
+++ b/OvmfPkg/VirtioNetDxe/DriverBinding.c
@@ -0,0 +1,676 @@
+/** @file
+
+  Driver Binding code and its private helpers for the virtio-net driver.
+
+  Copyright (C) 2013, Red Hat, Inc.
+
+  UEFI API documentation:
+  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
+};
-- 
1.7.1



------------------------------------------------------------------------------
AlienVault Unified Security Management (USM) platform delivers complete
security visibility with the essential security capabilities. Easily and
efficiently configure, manage, and operate all of your security controls
from a single console and one unified framework. Download a free trial.
http://p.sf.net/sfu/alienvault_d2d
_______________________________________________
edk2-devel mailing list
edk2-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/edk2-devel

Reply via email to