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