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