Home | History | Annotate | Download | only in VirtioNetDxe
      1 /** @file
      2 
      3   Driver Binding code and its private helpers for the virtio-net driver.
      4 
      5   Copyright (C) 2013, Red Hat, Inc.
      6   Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
      7 
      8   This program and the accompanying materials are licensed and made available
      9   under the terms and conditions of the BSD License which accompanies this
     10   distribution. The full text of the license may be found at
     11   http://opensource.org/licenses/bsd-license.php
     12 
     13   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
     14   WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     15 
     16 **/
     17 
     18 #include <Library/BaseMemoryLib.h>
     19 #include <Library/DevicePathLib.h>
     20 #include <Library/MemoryAllocationLib.h>
     21 #include <Library/UefiBootServicesTableLib.h>
     22 
     23 #include "VirtioNet.h"
     24 
     25 #define RECEIVE_FILTERS_NO_MCAST ((UINT32) (       \
     26           EFI_SIMPLE_NETWORK_RECEIVE_UNICAST     | \
     27           EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST   | \
     28           EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS   \
     29           ))
     30 
     31 /*
     32   Temporarily enable then reset the virtio-net device in order to retrieve
     33   configuration values needed by Simple Network Protocol and Simple Network
     34   Mode fields.
     35 
     36   Only VirtioNetSnpPopulate() may call this function.
     37 
     38   If the function fails for any reason, the virtio-net device is moved to
     39   VSTAT_FAILED instead of being reset. This serves only informative purposes
     40   for the host side.
     41 
     42   param[in,out] Dev                 The VNET_DEV structure being created for
     43                                     the virtio-net device.
     44   param[out] MacAddress             MAC address configured by the host.
     45   param[out] MediaPresentSupported  Link status is made available by the host.
     46   param[out] MediaPresent           If link status is made available by the
     47                                     host, the current link status is stored in
     48                                     *MediaPresent. Otherwise MediaPresent is
     49                                     unused.
     50 
     51   @retval EFI_UNSUPPORTED           The host doesn't supply a MAC address.
     52   @return                           Status codes from VirtIo protocol members.
     53   @retval EFI_SUCCESS               Configuration values retrieved.
     54 */
     55 STATIC
     56 EFI_STATUS
     57 EFIAPI
     58 VirtioNetGetFeatures (
     59   IN OUT  VNET_DEV        *Dev,
     60   OUT     EFI_MAC_ADDRESS *MacAddress,
     61   OUT     BOOLEAN         *MediaPresentSupported,
     62   OUT     BOOLEAN         *MediaPresent
     63   )
     64 {
     65   EFI_STATUS Status;
     66   UINT8      NextDevStat;
     67   UINT64     Features;
     68   UINTN      MacIdx;
     69   UINT16     LinkStatus;
     70 
     71   //
     72   // Interrogate the device for features (virtio-0.9.5, 2.2.1 Device
     73   // Initialization Sequence), but don't complete setting it up.
     74   //
     75   NextDevStat = 0;             // step 1 -- reset device
     76   Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
     77   if (EFI_ERROR (Status)) {
     78     return Status;
     79   }
     80 
     81   NextDevStat |= VSTAT_ACK;    // step 2 -- acknowledge device presence
     82   Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
     83   if (EFI_ERROR (Status)) {
     84     goto YieldDevice;
     85   }
     86 
     87   NextDevStat |= VSTAT_DRIVER; // step 3 -- we know how to drive it
     88   Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
     89   if (EFI_ERROR (Status)) {
     90     goto YieldDevice;
     91   }
     92 
     93   //
     94   // step 4a -- retrieve and validate features
     95   //
     96   Status = Dev->VirtIo->GetDeviceFeatures (Dev->VirtIo, &Features);
     97   if (EFI_ERROR (Status)) {
     98     goto YieldDevice;
     99   }
    100 
    101   //
    102   // get MAC address byte-wise
    103   //
    104   if ((Features & VIRTIO_NET_F_MAC) == 0) {
    105     Status = EFI_UNSUPPORTED;
    106     goto YieldDevice;
    107   }
    108   for (MacIdx = 0; MacIdx < SIZE_OF_VNET (Mac); ++MacIdx) {
    109     Status = Dev->VirtIo->ReadDevice (Dev->VirtIo,
    110                             OFFSET_OF_VNET (Mac) + MacIdx, // Offset
    111                             1,                             // FieldSize
    112                             1,                             // BufferSize
    113                             &MacAddress->Addr[MacIdx]      // Buffer
    114                             );
    115     if (EFI_ERROR (Status)) {
    116       goto YieldDevice;
    117     }
    118   }
    119 
    120   //
    121   // check if link status is reported, and if so, what the link status is
    122   //
    123   if ((Features & VIRTIO_NET_F_STATUS) == 0) {
    124     *MediaPresentSupported = FALSE;
    125   }
    126   else {
    127     *MediaPresentSupported = TRUE;
    128     Status = VIRTIO_CFG_READ (Dev, LinkStatus, &LinkStatus);
    129     if (EFI_ERROR (Status)) {
    130       goto YieldDevice;
    131     }
    132     *MediaPresent = (BOOLEAN) ((LinkStatus & VIRTIO_NET_S_LINK_UP) != 0);
    133   }
    134 
    135 YieldDevice:
    136   Dev->VirtIo->SetDeviceStatus (Dev->VirtIo,
    137     EFI_ERROR (Status) ? VSTAT_FAILED : 0);
    138 
    139   return Status;
    140 }
    141 
    142 
    143 /**
    144   Set up the Simple Network Protocol fields, the Simple Network Mode fields,
    145   and the Exit Boot Services Event of the virtio-net driver instance.
    146 
    147   This function may only be called by VirtioNetDriverBindingStart().
    148 
    149   @param[in,out] Dev  The VNET_DEV driver instance being created for the
    150                       virtio-net device.
    151 
    152   @return              Status codes from the CreateEvent() boot service or the
    153                        VirtioNetGetFeatures() function.
    154   @retval EFI_SUCCESS  Configuration successful.
    155 */
    156 STATIC
    157 EFI_STATUS
    158 EFIAPI
    159 VirtioNetSnpPopulate (
    160   IN OUT VNET_DEV *Dev
    161   )
    162 {
    163   EFI_STATUS Status;
    164 
    165   //
    166   // We set up a function here that is asynchronously callable by an
    167   // external application to check if there are any packets available for
    168   // reception. The least urgent task priority level we can specify for such a
    169   // "software interrupt" is TPL_CALLBACK.
    170   //
    171   // TPL_CALLBACK is also the maximum TPL an SNP implementation is allowed to
    172   // run at (see 6.1 Event, Timer, and Task Priority Services in the UEFI
    173   // Specification 2.3.1+errC).
    174   //
    175   // Since we raise our TPL to TPL_CALLBACK in every single function that
    176   // accesses the device, and the external application also queues its interest
    177   // for received packets at the same TPL_CALLBACK, in effect the
    178   // VirtioNetIsPacketAvailable() function will never interrupt any
    179   // device-accessing driver function, it will be scheduled in isolation.
    180   //
    181   // TPL_CALLBACK (which basically this entire driver runs at) is allowed
    182   // for "[l]ong term operations (such as file system operations and disk
    183   // I/O)". Because none of our functions block, we'd satisfy an even stronger
    184   // requirement.
    185   //
    186   Status = gBS->CreateEvent (EVT_NOTIFY_WAIT, TPL_CALLBACK,
    187                   &VirtioNetIsPacketAvailable, Dev, &Dev->Snp.WaitForPacket);
    188   if (EFI_ERROR (Status)) {
    189     return Status;
    190   }
    191 
    192   Dev->Snp.Revision       = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION;
    193   Dev->Snp.Start          = &VirtioNetStart;
    194   Dev->Snp.Stop           = &VirtioNetStop;
    195   Dev->Snp.Initialize     = &VirtioNetInitialize;
    196   Dev->Snp.Reset          = &VirtioNetReset;
    197   Dev->Snp.Shutdown       = &VirtioNetShutdown;
    198   Dev->Snp.ReceiveFilters = &VirtioNetReceiveFilters;
    199   Dev->Snp.StationAddress = &VirtioNetStationAddress;
    200   Dev->Snp.Statistics     = &VirtioNetStatistics;
    201   Dev->Snp.MCastIpToMac   = &VirtioNetMcastIpToMac;
    202   Dev->Snp.NvData         = &VirtioNetNvData;
    203   Dev->Snp.GetStatus      = &VirtioNetGetStatus;
    204   Dev->Snp.Transmit       = &VirtioNetTransmit;
    205   Dev->Snp.Receive        = &VirtioNetReceive;
    206   Dev->Snp.Mode           = &Dev->Snm;
    207 
    208   Dev->Snm.State                 = EfiSimpleNetworkStopped;
    209   Dev->Snm.HwAddressSize         = SIZE_OF_VNET (Mac);
    210   Dev->Snm.MediaHeaderSize       = SIZE_OF_VNET (Mac) + // dst MAC
    211                                    SIZE_OF_VNET (Mac) + // src MAC
    212                                    2;                       // Ethertype
    213   Dev->Snm.MaxPacketSize         = 1500;
    214   Dev->Snm.NvRamSize             = 0;
    215   Dev->Snm.NvRamAccessSize       = 0;
    216   Dev->Snm.ReceiveFilterMask     = RECEIVE_FILTERS_NO_MCAST;
    217   Dev->Snm.ReceiveFilterSetting  = RECEIVE_FILTERS_NO_MCAST;
    218   Dev->Snm.MaxMCastFilterCount   = 0;
    219   Dev->Snm.MCastFilterCount      = 0;
    220   Dev->Snm.IfType                = 1; // ethernet
    221   Dev->Snm.MacAddressChangeable  = FALSE;
    222   Dev->Snm.MultipleTxSupported   = TRUE;
    223 
    224   ASSERT (SIZE_OF_VNET (Mac) <= sizeof (EFI_MAC_ADDRESS));
    225 
    226   Status = VirtioNetGetFeatures (Dev, &Dev->Snm.CurrentAddress,
    227              &Dev->Snm.MediaPresentSupported, &Dev->Snm.MediaPresent);
    228   if (EFI_ERROR (Status)) {
    229     goto CloseWaitForPacket;
    230   }
    231   CopyMem (&Dev->Snm.PermanentAddress, &Dev->Snm.CurrentAddress,
    232     SIZE_OF_VNET (Mac));
    233   SetMem (&Dev->Snm.BroadcastAddress, SIZE_OF_VNET (Mac), 0xFF);
    234 
    235   //
    236   // VirtioNetExitBoot() is queued by ExitBootServices(); its purpose is to
    237   // cancel any pending virtio requests. The TPL_CALLBACK reasoning is
    238   // identical to the one above. There's one difference: this kind of
    239   // event is "globally visible", which means it can be signalled as soon as
    240   // we create it. We haven't raised our TPL here, hence VirtioNetExitBoot()
    241   // could be entered immediately. VirtioNetExitBoot() checks Dev->Snm.State,
    242   // so we're safe.
    243   //
    244   Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK,
    245                   &VirtioNetExitBoot, Dev, &Dev->ExitBoot);
    246   if (EFI_ERROR (Status)) {
    247     goto CloseWaitForPacket;
    248   }
    249 
    250   return EFI_SUCCESS;
    251 
    252 CloseWaitForPacket:
    253   gBS->CloseEvent (Dev->Snp.WaitForPacket);
    254   return Status;
    255 }
    256 
    257 
    258 /**
    259   Release any resources allocated by VirtioNetSnpPopulate().
    260 
    261   This function may only be called by VirtioNetDriverBindingStart(), when
    262   rolling back a partial, failed driver instance creation, and by
    263   VirtioNetDriverBindingStop(), when disconnecting a virtio-net device from the
    264   driver.
    265 
    266   @param[in,out] Dev  The VNET_DEV driver instance being destroyed.
    267 */
    268 STATIC
    269 VOID
    270 EFIAPI
    271 VirtioNetSnpEvacuate (
    272   IN OUT VNET_DEV *Dev
    273   )
    274 {
    275   //
    276   // This function runs either at TPL_CALLBACK already (from
    277   // VirtioNetDriverBindingStop()), or it is part of a teardown following
    278   // a partial, failed construction in VirtioNetDriverBindingStart(), when
    279   // WaitForPacket was never accessible to the world.
    280   //
    281   gBS->CloseEvent (Dev->ExitBoot);
    282   gBS->CloseEvent (Dev->Snp.WaitForPacket);
    283 }
    284 
    285 
    286 /**
    287   Tests to see if this driver supports a given controller. If a child device is
    288   provided, it further tests to see if this driver supports creating a handle
    289   for the specified child device.
    290 
    291   This function checks to see if the driver specified by This supports the
    292   device specified by ControllerHandle. Drivers will typically use the device
    293   path attached to ControllerHandle and/or the services from the bus I/O
    294   abstraction attached to ControllerHandle to determine if the driver supports
    295   ControllerHandle. This function may be called many times during platform
    296   initialization. In order to reduce boot times, the tests performed by this
    297   function must be very small, and take as little time as possible to execute.
    298   This function must not change the state of any hardware devices, and this
    299   function must be aware that the device specified by ControllerHandle may
    300   already be managed by the same driver or a different driver. This function
    301   must match its calls to AllocatePages() with FreePages(), AllocatePool() with
    302   FreePool(), and OpenProtocol() with CloseProtocol(). Because ControllerHandle
    303   may have been previously started by the same driver, if a protocol is already
    304   in the opened state, then it must not be closed with CloseProtocol(). This is
    305   required to guarantee the state of ControllerHandle is not modified by this
    306   function.
    307 
    308   @param[in]  This                 A pointer to the EFI_DRIVER_BINDING_PROTOCOL
    309                                    instance.
    310   @param[in]  ControllerHandle     The handle of the controller to test. This
    311                                    handle must support a protocol interface
    312                                    that supplies an I/O abstraction to the
    313                                    driver.
    314   @param[in]  RemainingDevicePath  A pointer to the remaining portion of a
    315                                    device path.  This parameter is ignored by
    316                                    device drivers, and is optional for bus
    317                                    drivers. For bus drivers, if this parameter
    318                                    is not NULL, then the bus driver must
    319                                    determine if the bus controller specified by
    320                                    ControllerHandle and the child controller
    321                                    specified by RemainingDevicePath are both
    322                                    supported by this bus driver.
    323 
    324   @retval EFI_SUCCESS              The device specified by ControllerHandle and
    325                                    RemainingDevicePath is supported by the
    326                                    driver specified by This.
    327   @retval EFI_ALREADY_STARTED      The device specified by ControllerHandle and
    328                                    RemainingDevicePath is already being managed
    329                                    by the driver specified by This.
    330   @retval EFI_ACCESS_DENIED        The device specified by ControllerHandle and
    331                                    RemainingDevicePath is already being managed
    332                                    by a different driver or an application that
    333                                    requires exclusive access. Currently not
    334                                    implemented.
    335   @retval EFI_UNSUPPORTED          The device specified by ControllerHandle and
    336                                    RemainingDevicePath is not supported by the
    337                                    driver specified by This.
    338 **/
    339 
    340 STATIC
    341 EFI_STATUS
    342 EFIAPI
    343 VirtioNetDriverBindingSupported (
    344   IN EFI_DRIVER_BINDING_PROTOCOL *This,
    345   IN EFI_HANDLE                  DeviceHandle,
    346   IN EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath
    347   )
    348 {
    349   EFI_STATUS          Status;
    350   VIRTIO_DEVICE_PROTOCOL *VirtIo;
    351 
    352   //
    353   // Attempt to open the device with the VirtIo set of interfaces. On success,
    354   // the protocol is "instantiated" for the VirtIo device. Covers duplicate open
    355   // attempts (EFI_ALREADY_STARTED).
    356   //
    357   Status = gBS->OpenProtocol (
    358                   DeviceHandle,               // candidate device
    359                   &gVirtioDeviceProtocolGuid, // for generic VirtIo access
    360                   (VOID **)&VirtIo,           // handle to instantiate
    361                   This->DriverBindingHandle,  // requestor driver identity
    362                   DeviceHandle,               // ControllerHandle, according to
    363                                               // the UEFI Driver Model
    364                   EFI_OPEN_PROTOCOL_BY_DRIVER // get exclusive VirtIo access to
    365                                               // the device; to be released
    366                   );
    367   if (EFI_ERROR (Status)) {
    368     return Status;
    369   }
    370 
    371   if (VirtIo->SubSystemDeviceId != VIRTIO_SUBSYSTEM_NETWORK_CARD) {
    372     Status = EFI_UNSUPPORTED;
    373   }
    374 
    375   //
    376   // We needed VirtIo access only transitorily, to see whether we support the
    377   // device or not.
    378   //
    379   gBS->CloseProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,
    380          This->DriverBindingHandle, DeviceHandle);
    381   return Status;
    382 }
    383 
    384 
    385 /**
    386   Starts a device controller or a bus controller.
    387 
    388   The Start() function is designed to be invoked from the EFI boot service
    389   ConnectController(). As a result, much of the error checking on the
    390   parameters to Start() has been moved into this  common boot service. It is
    391   legal to call Start() from other locations,  but the following calling
    392   restrictions must be followed, or the system behavior will not be
    393   deterministic.
    394   1. ControllerHandle must be a valid EFI_HANDLE.
    395   2. If RemainingDevicePath is not NULL, then it must be a pointer to a
    396      naturally aligned EFI_DEVICE_PATH_PROTOCOL.
    397   3. Prior to calling Start(), the Supported() function for the driver
    398      specified by This must have been called with the same calling parameters,
    399      and Supported() must have returned EFI_SUCCESS.
    400 
    401   @param[in]  This                 A pointer to the EFI_DRIVER_BINDING_PROTOCOL
    402                                    instance.
    403   @param[in]  ControllerHandle     The handle of the controller to start. This
    404                                    handle  must support a protocol interface
    405                                    that supplies  an I/O abstraction to the
    406                                    driver.
    407   @param[in]  RemainingDevicePath  A pointer to the remaining portion of a
    408                                    device path.  This  parameter is ignored by
    409                                    device drivers, and is optional for bus
    410                                    drivers. For a bus driver, if this parameter
    411                                    is NULL, then handles  for all the children
    412                                    of Controller are created by this driver.
    413                                    If this parameter is not NULL and the first
    414                                    Device Path Node is  not the End of Device
    415                                    Path Node, then only the handle for the
    416                                    child device specified by the first Device
    417                                    Path Node of  RemainingDevicePath is created
    418                                    by this driver. If the first Device Path
    419                                    Node of RemainingDevicePath is  the End of
    420                                    Device Path Node, no child handle is created
    421                                    by this driver.
    422 
    423   @retval EFI_SUCCESS              The device was started.
    424   @retval EFI_DEVICE_ERROR         The device could not be started due to a
    425                                    device error.Currently not implemented.
    426   @retval EFI_OUT_OF_RESOURCES     The request could not be completed due to a
    427                                    lack of resources.
    428   @retval Others                   The driver failded to start the device.
    429 
    430 **/
    431 
    432 STATIC
    433 EFI_STATUS
    434 EFIAPI
    435 VirtioNetDriverBindingStart (
    436   IN EFI_DRIVER_BINDING_PROTOCOL *This,
    437   IN EFI_HANDLE                  DeviceHandle,
    438   IN EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath
    439   )
    440 {
    441   EFI_STATUS               Status;
    442   VNET_DEV                 *Dev;
    443   EFI_DEVICE_PATH_PROTOCOL *DevicePath;
    444   MAC_ADDR_DEVICE_PATH     MacNode;
    445   VOID                     *ChildVirtIo;
    446 
    447   //
    448   // allocate space for the driver instance
    449   //
    450   Dev = (VNET_DEV *) AllocateZeroPool (sizeof *Dev);
    451   if (Dev == NULL) {
    452     return EFI_OUT_OF_RESOURCES;
    453   }
    454   Dev->Signature = VNET_SIG;
    455 
    456   Status = gBS->OpenProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,
    457                   (VOID **)&Dev->VirtIo, This->DriverBindingHandle,
    458                   DeviceHandle, EFI_OPEN_PROTOCOL_BY_DRIVER);
    459   if (EFI_ERROR (Status)) {
    460     goto FreeVirtioNet;
    461   }
    462 
    463   //
    464   // now we can run a basic one-shot virtio-net initialization required to
    465   // retrieve the MAC address
    466   //
    467   Status = VirtioNetSnpPopulate (Dev);
    468   if (EFI_ERROR (Status)) {
    469     goto CloseVirtIo;
    470   }
    471 
    472   //
    473   // get the device path of the virtio-net device -- one-shot open
    474   //
    475   Status = gBS->OpenProtocol (DeviceHandle, &gEfiDevicePathProtocolGuid,
    476                   (VOID **)&DevicePath, This->DriverBindingHandle,
    477                   DeviceHandle, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
    478   if (EFI_ERROR (Status)) {
    479     goto Evacuate;
    480   }
    481 
    482   //
    483   // create another device path that has the MAC address appended
    484   //
    485   MacNode.Header.Type    = MESSAGING_DEVICE_PATH;
    486   MacNode.Header.SubType = MSG_MAC_ADDR_DP;
    487   SetDevicePathNodeLength (&MacNode, sizeof MacNode);
    488   CopyMem (&MacNode.MacAddress, &Dev->Snm.CurrentAddress,
    489     sizeof (EFI_MAC_ADDRESS));
    490   MacNode.IfType         = Dev->Snm.IfType;
    491 
    492   Dev->MacDevicePath = AppendDevicePathNode (DevicePath, &MacNode.Header);
    493   if (Dev->MacDevicePath == NULL) {
    494     Status = EFI_OUT_OF_RESOURCES;
    495     goto Evacuate;
    496   }
    497 
    498   //
    499   // create a child handle with the Simple Network Protocol and the new
    500   // device path installed on it
    501   //
    502   Status = gBS->InstallMultipleProtocolInterfaces (&Dev->MacHandle,
    503                   &gEfiSimpleNetworkProtocolGuid, &Dev->Snp,
    504                   &gEfiDevicePathProtocolGuid,    Dev->MacDevicePath,
    505                   NULL);
    506   if (EFI_ERROR (Status)) {
    507     goto FreeMacDevicePath;
    508   }
    509 
    510   //
    511   // make a note that we keep this device open with VirtIo for the sake of this
    512   // child
    513   //
    514   Status = gBS->OpenProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,
    515                   &ChildVirtIo, This->DriverBindingHandle,
    516                   Dev->MacHandle, EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER);
    517   if (EFI_ERROR (Status)) {
    518     goto UninstallMultiple;
    519   }
    520 
    521   return EFI_SUCCESS;
    522 
    523 UninstallMultiple:
    524   gBS->UninstallMultipleProtocolInterfaces (Dev->MacHandle,
    525          &gEfiDevicePathProtocolGuid,    Dev->MacDevicePath,
    526          &gEfiSimpleNetworkProtocolGuid, &Dev->Snp,
    527          NULL);
    528 
    529 FreeMacDevicePath:
    530   FreePool (Dev->MacDevicePath);
    531 
    532 Evacuate:
    533   VirtioNetSnpEvacuate (Dev);
    534 
    535 CloseVirtIo:
    536   gBS->CloseProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,
    537          This->DriverBindingHandle, DeviceHandle);
    538 
    539 FreeVirtioNet:
    540   FreePool (Dev);
    541 
    542   return Status;
    543 }
    544 
    545 
    546 /**
    547   Stops a device controller or a bus controller.
    548 
    549   The Stop() function is designed to be invoked from the EFI boot service
    550   DisconnectController().  As a result, much of the error checking on the
    551   parameters to Stop() has been moved  into this common boot service. It is
    552   legal to call Stop() from other locations,  but the following calling
    553   restrictions must be followed, or the system behavior will not be
    554   deterministic.
    555   1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous
    556      call to this same driver's Start() function.
    557   2. The first NumberOfChildren handles of ChildHandleBuffer must all be a
    558      valid EFI_HANDLE. In addition, all of these handles must have been created
    559      in this driver's Start() function, and the Start() function must have
    560      called OpenProtocol() on ControllerHandle with an Attribute of
    561      EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.
    562 
    563   @param[in]  This              A pointer to the EFI_DRIVER_BINDING_PROTOCOL
    564                                 instance.
    565   @param[in]  ControllerHandle  A handle to the device being stopped. The
    566                                 handle must  support a bus specific I/O
    567                                 protocol for the driver  to use to stop the
    568                                 device.
    569   @param[in]  NumberOfChildren  The number of child device handles in
    570                                 ChildHandleBuffer.
    571   @param[in]  ChildHandleBuffer An array of child handles to be freed. May be
    572                                 NULL  if NumberOfChildren is 0.
    573 
    574   @retval EFI_SUCCESS           The device was stopped.
    575   @retval EFI_DEVICE_ERROR      The device could not be stopped due to a device
    576                                 error.
    577 
    578 **/
    579 STATIC
    580 EFI_STATUS
    581 EFIAPI
    582 VirtioNetDriverBindingStop (
    583   IN EFI_DRIVER_BINDING_PROTOCOL *This,
    584   IN EFI_HANDLE                  DeviceHandle,
    585   IN UINTN                       NumberOfChildren,
    586   IN EFI_HANDLE                  *ChildHandleBuffer
    587   )
    588 {
    589   if (NumberOfChildren > 0) {
    590     //
    591     // free all resources for whose access we need the child handle, because
    592     // the child handle is going away
    593     //
    594     EFI_STATUS                  Status;
    595     EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
    596     VNET_DEV                    *Dev;
    597     EFI_TPL                     OldTpl;
    598 
    599     ASSERT (NumberOfChildren == 1);
    600 
    601     Status = gBS->OpenProtocol (ChildHandleBuffer[0],
    602                     &gEfiSimpleNetworkProtocolGuid, (VOID **)&Snp,
    603                     This->DriverBindingHandle, DeviceHandle,
    604                     EFI_OPEN_PROTOCOL_GET_PROTOCOL);
    605     ASSERT_EFI_ERROR (Status);
    606     Dev = VIRTIO_NET_FROM_SNP (Snp);
    607 
    608     //
    609     // prevent any interference with WaitForPacket
    610     //
    611     OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
    612 
    613     ASSERT (Dev->MacHandle == ChildHandleBuffer[0]);
    614     if (Dev->Snm.State != EfiSimpleNetworkStopped) {
    615       //
    616       // device in use, cannot stop driver instance
    617       //
    618       Status = EFI_DEVICE_ERROR;
    619     }
    620     else {
    621       gBS->CloseProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,
    622              This->DriverBindingHandle, Dev->MacHandle);
    623       gBS->UninstallMultipleProtocolInterfaces (Dev->MacHandle,
    624              &gEfiDevicePathProtocolGuid,    Dev->MacDevicePath,
    625              &gEfiSimpleNetworkProtocolGuid, &Dev->Snp,
    626              NULL);
    627       FreePool (Dev->MacDevicePath);
    628       VirtioNetSnpEvacuate (Dev);
    629       FreePool (Dev);
    630     }
    631 
    632     gBS->RestoreTPL (OldTpl);
    633     return Status;
    634   }
    635 
    636   //
    637   // release remaining resources, tied directly to the parent handle
    638   //
    639   gBS->CloseProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,
    640          This->DriverBindingHandle, DeviceHandle);
    641 
    642   return EFI_SUCCESS;
    643 }
    644 
    645 
    646 EFI_DRIVER_BINDING_PROTOCOL gVirtioNetDriverBinding = {
    647   &VirtioNetDriverBindingSupported,
    648   &VirtioNetDriverBindingStart,
    649   &VirtioNetDriverBindingStop,
    650   0x10,
    651   NULL,
    652   NULL
    653 };
    654