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