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