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 - 2010, 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
VirtioNetGetFeatures(IN OUT VNET_DEV * Dev,OUT EFI_MAC_ADDRESS * MacAddress,OUT BOOLEAN * MediaPresentSupported,OUT BOOLEAN * MediaPresent)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   UINT32     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 = !!(LinkStatus & VIRTIO_NET_S_LINK_UP);
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
VirtioNetSnpPopulate(IN OUT VNET_DEV * Dev)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
VirtioNetSnpEvacuate(IN OUT VNET_DEV * Dev)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
VirtioNetDriverBindingSupported(IN EFI_DRIVER_BINDING_PROTOCOL * This,IN EFI_HANDLE DeviceHandle,IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath)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
VirtioNetDriverBindingStart(IN EFI_DRIVER_BINDING_PROTOCOL * This,IN EFI_HANDLE DeviceHandle,IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath)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
VirtioNetDriverBindingStop(IN EFI_DRIVER_BINDING_PROTOCOL * This,IN EFI_HANDLE DeviceHandle,IN UINTN NumberOfChildren,IN EFI_HANDLE * ChildHandleBuffer)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