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