]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/VirtioGpuDxe/DriverBinding.c
OvmfPkg/PlatformBootManagerLib: rejuvenate old-style function comments
[mirror_edk2.git] / OvmfPkg / VirtioGpuDxe / DriverBinding.c
CommitLineData
a2a4fa66
LE
1/** @file\r
2\r
3 Implement the Driver Binding Protocol and the Component Name 2 Protocol for\r
4 the Virtio GPU hybrid driver.\r
5\r
6 Copyright (C) 2016, Red Hat, Inc.\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
a2a4fa66
LE
18#include <Library/DevicePathLib.h>\r
19#include <Library/MemoryAllocationLib.h>\r
20#include <Library/PrintLib.h>\r
21#include <Library/UefiBootServicesTableLib.h>\r
22#include <Library/UefiLib.h>\r
23#include <Protocol/ComponentName2.h>\r
24#include <Protocol/DevicePath.h>\r
25#include <Protocol/DriverBinding.h>\r
26#include <Protocol/PciIo.h>\r
27\r
28#include "VirtioGpu.h"\r
29\r
a2a4fa66
LE
30//\r
31// The device path node that describes the Video Output Device Attributes for\r
32// the single head (UEFI child handle) that we support.\r
33//\r
34// The ACPI_DISPLAY_ADR() macro corresponds to Table B-2, section "B.4.2 _DOD"\r
35// in the ACPI 3.0b spec, or more recently, to Table B-379, section "B.3.2\r
36// _DOD" in the ACPI 6.0 spec.\r
37//\r
38STATIC CONST ACPI_ADR_DEVICE_PATH mAcpiAdr = {\r
39 { // Header\r
40 ACPI_DEVICE_PATH, // Type\r
41 ACPI_ADR_DP, // SubType\r
42 { sizeof mAcpiAdr, 0 }, // Length\r
43 },\r
44 ACPI_DISPLAY_ADR ( // ADR\r
45 1, // DeviceIdScheme: use the ACPI\r
46 // bit-field definitions\r
47 0, // HeadId\r
48 0, // NonVgaOutput\r
49 1, // BiosCanDetect\r
50 0, // VendorInfo\r
51 ACPI_ADR_DISPLAY_TYPE_EXTERNAL_DIGITAL, // Type\r
52 0, // Port\r
53 0 // Index\r
54 )\r
55};\r
56\r
57//\r
58// Component Name 2 Protocol implementation.\r
59//\r
60STATIC CONST EFI_UNICODE_STRING_TABLE mDriverNameTable[] = {\r
61 { "en", L"Virtio GPU Driver" },\r
62 { NULL, NULL }\r
63};\r
64\r
65STATIC\r
66EFI_STATUS\r
67EFIAPI\r
68VirtioGpuGetDriverName (\r
69 IN EFI_COMPONENT_NAME2_PROTOCOL *This,\r
70 IN CHAR8 *Language,\r
71 OUT CHAR16 **DriverName\r
72 )\r
73{\r
74 return LookupUnicodeString2 (Language, This->SupportedLanguages,\r
75 mDriverNameTable, DriverName, FALSE /* Iso639Language */);\r
76}\r
77\r
78STATIC\r
79EFI_STATUS\r
80EFIAPI\r
81VirtioGpuGetControllerName (\r
82 IN EFI_COMPONENT_NAME2_PROTOCOL *This,\r
83 IN EFI_HANDLE ControllerHandle,\r
84 IN EFI_HANDLE ChildHandle OPTIONAL,\r
85 IN CHAR8 *Language,\r
86 OUT CHAR16 **ControllerName\r
87 )\r
88{\r
89 EFI_STATUS Status;\r
90 VGPU_DEV *VgpuDev;\r
91\r
92 //\r
93 // Look up the VGPU_DEV "protocol interface" on ControllerHandle.\r
94 //\r
95 Status = gBS->OpenProtocol (ControllerHandle, &gEfiCallerIdGuid,\r
96 (VOID **)&VgpuDev, gImageHandle, ControllerHandle,\r
97 EFI_OPEN_PROTOCOL_GET_PROTOCOL);\r
98 if (EFI_ERROR (Status)) {\r
99 return Status;\r
100 }\r
101 //\r
102 // Sanity check: if we found gEfiCallerIdGuid on ControllerHandle, then we\r
103 // keep its Virtio Device Protocol interface open BY_DRIVER.\r
104 //\r
105 ASSERT_EFI_ERROR (EfiTestManagedDevice (ControllerHandle, gImageHandle,\r
106 &gVirtioDeviceProtocolGuid));\r
107\r
108 if (ChildHandle == NULL) {\r
109 //\r
110 // The caller is querying the name of the VGPU_DEV controller.\r
111 //\r
112 return LookupUnicodeString2 (Language, This->SupportedLanguages,\r
113 VgpuDev->BusName, ControllerName, FALSE /* Iso639Language */);\r
114 }\r
115\r
116 //\r
117 // Otherwise, the caller is looking for the name of the GOP child controller.\r
118 // Check if it is asking about the GOP child controller that we manage. (The\r
119 // condition below covers the case when we haven't produced the GOP child\r
120 // controller yet, or we've destroyed it since.)\r
121 //\r
122 if (VgpuDev->Child == NULL || ChildHandle != VgpuDev->Child->GopHandle) {\r
123 return EFI_UNSUPPORTED;\r
124 }\r
125 //\r
126 // Sanity check: our GOP child controller keeps the VGPU_DEV controller's\r
127 // Virtio Device Protocol interface open BY_CHILD_CONTROLLER.\r
128 //\r
129 ASSERT_EFI_ERROR (EfiTestChildHandle (ControllerHandle, ChildHandle,\r
130 &gVirtioDeviceProtocolGuid));\r
131\r
132 return LookupUnicodeString2 (Language, This->SupportedLanguages,\r
133 VgpuDev->Child->GopName, ControllerName,\r
134 FALSE /* Iso639Language */);\r
135}\r
136\r
137STATIC CONST EFI_COMPONENT_NAME2_PROTOCOL mComponentName2 = {\r
138 VirtioGpuGetDriverName,\r
139 VirtioGpuGetControllerName,\r
140 "en" // SupportedLanguages (RFC 4646)\r
141};\r
142\r
143//\r
144// Helper functions for the Driver Binding Protocol Implementation.\r
145//\r
146/**\r
147 Format the VGPU_DEV controller name, to be looked up and returned by\r
148 VirtioGpuGetControllerName().\r
149\r
150 @param[in] ControllerHandle The handle that identifies the VGPU_DEV\r
151 controller.\r
152\r
153 @param[in] AgentHandle The handle of the agent that will attempt to\r
154 temporarily open the PciIo protocol. This is the\r
155 DriverBindingHandle member of the\r
156 EFI_DRIVER_BINDING_PROTOCOL whose Start()\r
157 function is calling this function.\r
158\r
159 @param[in] DevicePath The device path that is installed on\r
160 ControllerHandle.\r
161\r
162 @param[out] ControllerName A dynamically allocated unicode string that\r
163 unconditionally says "Virtio GPU Device", with a\r
164 PCI Segment:Bus:Device.Function location\r
165 optionally appended. The latter part is only\r
166 produced if DevicePath contains at least one\r
167 PciIo node; in that case, the most specific such\r
168 node is used for retrieving the location info.\r
169 The caller is responsible for freeing\r
170 ControllerName after use.\r
171\r
172 @retval EFI_SUCCESS ControllerName has been formatted.\r
173\r
174 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for ControllerName.\r
175**/\r
176STATIC\r
177EFI_STATUS\r
178FormatVgpuDevName (\r
179 IN EFI_HANDLE ControllerHandle,\r
180 IN EFI_HANDLE AgentHandle,\r
181 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,\r
182 OUT CHAR16 **ControllerName\r
183 )\r
184{\r
185 EFI_HANDLE PciIoHandle;\r
186 EFI_PCI_IO_PROTOCOL *PciIo;\r
187 UINTN Segment, Bus, Device, Function;\r
188 STATIC CONST CHAR16 ControllerNameStem[] = L"Virtio GPU Device";\r
189 UINTN ControllerNameSize;\r
190\r
191 if (EFI_ERROR (gBS->LocateDevicePath (&gEfiPciIoProtocolGuid, &DevicePath,\r
192 &PciIoHandle)) ||\r
193 EFI_ERROR (gBS->OpenProtocol (PciIoHandle, &gEfiPciIoProtocolGuid,\r
194 (VOID **)&PciIo, AgentHandle, ControllerHandle,\r
195 EFI_OPEN_PROTOCOL_GET_PROTOCOL)) ||\r
196 EFI_ERROR (PciIo->GetLocation (PciIo, &Segment, &Bus, &Device,\r
197 &Function))) {\r
198 //\r
199 // Failed to retrieve location info, return verbatim copy of static string.\r
200 //\r
201 *ControllerName = AllocateCopyPool (sizeof ControllerNameStem,\r
202 ControllerNameStem);\r
203 return (*ControllerName == NULL) ? EFI_OUT_OF_RESOURCES : EFI_SUCCESS;\r
204 }\r
205 //\r
206 // Location info available, format ControllerName dynamically.\r
207 //\r
208 ControllerNameSize = sizeof ControllerNameStem + // includes L'\0'\r
209 sizeof (CHAR16) * (1 + 4 + // Segment\r
210 1 + 2 + // Bus\r
211 1 + 2 + // Device\r
212 1 + 1 // Function\r
213 );\r
214 *ControllerName = AllocatePool (ControllerNameSize);\r
215 if (*ControllerName == NULL) {\r
216 return EFI_OUT_OF_RESOURCES;\r
217 }\r
218\r
219 UnicodeSPrintAsciiFormat (*ControllerName, ControllerNameSize,\r
220 "%s %04x:%02x:%02x.%x", ControllerNameStem, (UINT32)Segment, (UINT32)Bus,\r
221 (UINT32)Device, (UINT32)Function);\r
222 return EFI_SUCCESS;\r
223}\r
224\r
225/**\r
226 Dynamically allocate and initialize the VGPU_GOP child object within an\r
227 otherwise configured parent VGPU_DEV object.\r
228\r
229 This function adds a BY_CHILD_CONTROLLER reference to ParentBusController's\r
230 VIRTIO_DEVICE_PROTOCOL interface.\r
231\r
232 @param[in,out] ParentBus The pre-initialized VGPU_DEV object that the\r
233 newly created VGPU_GOP object will be the\r
234 child of.\r
235\r
236 @param[in] ParentDevicePath The device path protocol instance that is\r
237 installed on ParentBusController.\r
238\r
239 @param[in] ParentBusController The UEFI controller handle on which the\r
240 ParentBus VGPU_DEV object and the\r
241 ParentDevicePath device path protocol are\r
242 installed.\r
243\r
244 @param[in] DriverBindingHandle The DriverBindingHandle member of\r
245 EFI_DRIVER_BINDING_PROTOCOL whose Start()\r
246 function is calling this function. It is\r
247 passed as AgentHandle to gBS->OpenProtocol()\r
248 when creating the BY_CHILD_CONTROLLER\r
249 reference.\r
250\r
251 @retval EFI_SUCCESS ParentBus->Child has been created and\r
252 populated, and ParentBus->Child->GopHandle now\r
253 references ParentBusController->VirtIo\r
254 BY_CHILD_CONTROLLER.\r
255\r
256 @retval EFI_OUT_OF_RESOURCES Memory allocation failed.\r
257\r
258 @return Error codes from underlying functions.\r
259**/\r
260STATIC\r
261EFI_STATUS\r
262InitVgpuGop (\r
263 IN OUT VGPU_DEV *ParentBus,\r
264 IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath,\r
265 IN EFI_HANDLE ParentBusController,\r
266 IN EFI_HANDLE DriverBindingHandle\r
267 )\r
268{\r
269 VGPU_GOP *VgpuGop;\r
270 EFI_STATUS Status;\r
271 CHAR16 *ParentBusName;\r
272 STATIC CONST CHAR16 NameSuffix[] = L" Head #0";\r
273 UINTN NameSize;\r
274 CHAR16 *Name;\r
275 EFI_TPL OldTpl;\r
276 VOID *ParentVirtIo;\r
277\r
278 VgpuGop = AllocateZeroPool (sizeof *VgpuGop);\r
279 if (VgpuGop == NULL) {\r
280 return EFI_OUT_OF_RESOURCES;\r
281 }\r
282\r
283 VgpuGop->Signature = VGPU_GOP_SIG;\r
284 VgpuGop->ParentBus = ParentBus;\r
285\r
286 //\r
287 // Format a human-readable controller name for VGPU_GOP, and stash it for\r
288 // VirtioGpuGetControllerName() to look up. We simply append NameSuffix to\r
289 // ParentBus->BusName.\r
290 //\r
291 Status = LookupUnicodeString2 ("en", mComponentName2.SupportedLanguages,\r
292 ParentBus->BusName, &ParentBusName, FALSE /* Iso639Language */);\r
293 ASSERT_EFI_ERROR (Status);\r
294 NameSize = StrSize (ParentBusName) - sizeof (CHAR16) + sizeof NameSuffix;\r
295 Name = AllocatePool (NameSize);\r
296 if (Name == NULL) {\r
297 Status = EFI_OUT_OF_RESOURCES;\r
298 goto FreeVgpuGop;\r
299 }\r
300 UnicodeSPrintAsciiFormat (Name, NameSize, "%s%s", ParentBusName, NameSuffix);\r
301 Status = AddUnicodeString2 ("en", mComponentName2.SupportedLanguages,\r
302 &VgpuGop->GopName, Name, FALSE /* Iso639Language */);\r
303 FreePool (Name);\r
304 if (EFI_ERROR (Status)) {\r
305 goto FreeVgpuGop;\r
306 }\r
307\r
308 //\r
309 // Create the child device path.\r
310 //\r
311 VgpuGop->GopDevicePath = AppendDevicePathNode (ParentDevicePath,\r
312 &mAcpiAdr.Header);\r
313 if (VgpuGop->GopDevicePath == NULL) {\r
314 Status = EFI_OUT_OF_RESOURCES;\r
315 goto FreeVgpuGopName;\r
316 }\r
317\r
318 //\r
319 // Mask protocol notify callbacks until we're done.\r
320 //\r
321 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
322\r
323 //\r
324 // Create the child handle with the child device path.\r
325 //\r
326 Status = gBS->InstallProtocolInterface (&VgpuGop->GopHandle,\r
327 &gEfiDevicePathProtocolGuid, EFI_NATIVE_INTERFACE,\r
328 VgpuGop->GopDevicePath);\r
329 if (EFI_ERROR (Status)) {\r
330 goto FreeDevicePath;\r
331 }\r
332\r
333 //\r
334 // The child handle must present a reference to the parent handle's Virtio\r
335 // Device Protocol interface.\r
336 //\r
337 Status = gBS->OpenProtocol (ParentBusController, &gVirtioDeviceProtocolGuid,\r
338 &ParentVirtIo, DriverBindingHandle, VgpuGop->GopHandle,\r
339 EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER);\r
340 if (EFI_ERROR (Status)) {\r
341 goto UninstallDevicePath;\r
342 }\r
343 ASSERT (ParentVirtIo == ParentBus->VirtIo);\r
344\r
345 //\r
346 // Initialize our Graphics Output Protocol.\r
347 //\r
8731debe
LE
348 // Fill in the function members of VgpuGop->Gop from the template, then set\r
349 // up the rest of the GOP infrastructure by calling SetMode() right now.\r
a2a4fa66 350 //\r
8731debe
LE
351 CopyMem (&VgpuGop->Gop, &mGopTemplate, sizeof mGopTemplate);\r
352 Status = VgpuGop->Gop.SetMode (&VgpuGop->Gop, 0);\r
a2a4fa66
LE
353 if (EFI_ERROR (Status)) {\r
354 goto CloseVirtIoByChild;\r
355 }\r
356\r
357 //\r
358 // Install the Graphics Output Protocol on the child handle.\r
359 //\r
360 Status = gBS->InstallProtocolInterface (&VgpuGop->GopHandle,\r
8731debe 361 &gEfiGraphicsOutputProtocolGuid, EFI_NATIVE_INTERFACE,\r
a2a4fa66
LE
362 &VgpuGop->Gop);\r
363 if (EFI_ERROR (Status)) {\r
364 goto UninitGop;\r
365 }\r
366\r
367 //\r
368 // We're done.\r
369 //\r
370 gBS->RestoreTPL (OldTpl);\r
371 ParentBus->Child = VgpuGop;\r
372 return EFI_SUCCESS;\r
373\r
374UninitGop:\r
8731debe 375 ReleaseGopResources (VgpuGop, TRUE /* DisableHead */);\r
a2a4fa66
LE
376\r
377CloseVirtIoByChild:\r
378 gBS->CloseProtocol (ParentBusController, &gVirtioDeviceProtocolGuid,\r
379 DriverBindingHandle, VgpuGop->GopHandle);\r
380\r
381UninstallDevicePath:\r
382 gBS->UninstallProtocolInterface (VgpuGop->GopHandle,\r
383 &gEfiDevicePathProtocolGuid, VgpuGop->GopDevicePath);\r
384\r
385FreeDevicePath:\r
386 gBS->RestoreTPL (OldTpl);\r
387 FreePool (VgpuGop->GopDevicePath);\r
388\r
389FreeVgpuGopName:\r
390 FreeUnicodeStringTable (VgpuGop->GopName);\r
391\r
392FreeVgpuGop:\r
393 FreePool (VgpuGop);\r
394\r
395 return Status;\r
396}\r
397\r
398/**\r
399 Tear down and release the VGPU_GOP child object within the VGPU_DEV parent\r
400 object.\r
401\r
402 This function removes the BY_CHILD_CONTROLLER reference from\r
403 ParentBusController's VIRTIO_DEVICE_PROTOCOL interface.\r
404\r
405 @param[in,out] ParentBus The VGPU_DEV object that the VGPU_GOP child\r
406 object will be removed from.\r
407\r
408 @param[in] ParentBusController The UEFI controller handle on which the\r
409 ParentBus VGPU_DEV object is installed.\r
410\r
411 @param[in] DriverBindingHandle The DriverBindingHandle member of\r
412 EFI_DRIVER_BINDING_PROTOCOL whose Stop()\r
413 function is calling this function. It is\r
414 passed as AgentHandle to gBS->CloseProtocol()\r
415 when removing the BY_CHILD_CONTROLLER\r
416 reference.\r
417**/\r
418STATIC\r
419VOID\r
420UninitVgpuGop (\r
421 IN OUT VGPU_DEV *ParentBus,\r
422 IN EFI_HANDLE ParentBusController,\r
423 IN EFI_HANDLE DriverBindingHandle\r
424 )\r
425{\r
426 VGPU_GOP *VgpuGop;\r
427 EFI_STATUS Status;\r
428\r
429 VgpuGop = ParentBus->Child;\r
430 Status = gBS->UninstallProtocolInterface (VgpuGop->GopHandle,\r
8731debe 431 &gEfiGraphicsOutputProtocolGuid, &VgpuGop->Gop);\r
a2a4fa66
LE
432 ASSERT_EFI_ERROR (Status);\r
433\r
434 //\r
435 // Uninitialize VgpuGop->Gop.\r
436 //\r
8731debe 437 ReleaseGopResources (VgpuGop, TRUE /* DisableHead */);\r
a2a4fa66
LE
438\r
439 Status = gBS->CloseProtocol (ParentBusController, &gVirtioDeviceProtocolGuid,\r
440 DriverBindingHandle, VgpuGop->GopHandle);\r
441 ASSERT_EFI_ERROR (Status);\r
442\r
443 Status = gBS->UninstallProtocolInterface (VgpuGop->GopHandle,\r
444 &gEfiDevicePathProtocolGuid, VgpuGop->GopDevicePath);\r
445 ASSERT_EFI_ERROR (Status);\r
446\r
447 FreePool (VgpuGop->GopDevicePath);\r
448 FreeUnicodeStringTable (VgpuGop->GopName);\r
449 FreePool (VgpuGop);\r
450\r
451 ParentBus->Child = NULL;\r
452}\r
453\r
454//\r
455// Driver Binding Protocol Implementation.\r
456//\r
457STATIC\r
458EFI_STATUS\r
459EFIAPI\r
460VirtioGpuDriverBindingSupported (\r
461 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
462 IN EFI_HANDLE ControllerHandle,\r
463 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
464 )\r
465{\r
466 EFI_STATUS Status;\r
467 VIRTIO_DEVICE_PROTOCOL *VirtIo;\r
468\r
469 //\r
470 // - If RemainingDevicePath is NULL: the caller is interested in creating all\r
471 // child handles.\r
472 // - If RemainingDevicePath points to an end node: the caller is not\r
473 // interested in creating any child handle.\r
474 // - Otherwise, the caller would like to create the one child handle\r
475 // specified in RemainingDevicePath. In this case we have to see if the\r
476 // requested device path is supportable.\r
477 //\r
478 if (RemainingDevicePath != NULL &&\r
479 !IsDevicePathEnd (RemainingDevicePath) &&\r
480 (DevicePathNodeLength (RemainingDevicePath) != sizeof mAcpiAdr ||\r
481 CompareMem (RemainingDevicePath, &mAcpiAdr, sizeof mAcpiAdr) != 0)) {\r
482 return EFI_UNSUPPORTED;\r
483 }\r
484\r
485 //\r
486 // Open the Virtio Device Protocol interface on the controller, BY_DRIVER.\r
487 //\r
488 Status = gBS->OpenProtocol (ControllerHandle, &gVirtioDeviceProtocolGuid,\r
489 (VOID **)&VirtIo, This->DriverBindingHandle,\r
490 ControllerHandle, EFI_OPEN_PROTOCOL_BY_DRIVER);\r
491 if (EFI_ERROR (Status)) {\r
492 //\r
493 // If this fails, then by default we cannot support ControllerHandle. There\r
494 // is one exception: we've already bound the device, have not produced any\r
495 // GOP child controller, and now the caller wants us to produce the child\r
496 // controller (either specifically or as part of "all children"). That's\r
497 // allowed.\r
498 //\r
499 if (Status == EFI_ALREADY_STARTED) {\r
500 EFI_STATUS Status2;\r
501 VGPU_DEV *VgpuDev;\r
502\r
503 Status2 = gBS->OpenProtocol (ControllerHandle, &gEfiCallerIdGuid,\r
504 (VOID **)&VgpuDev, This->DriverBindingHandle,\r
505 ControllerHandle, EFI_OPEN_PROTOCOL_GET_PROTOCOL);\r
506 ASSERT_EFI_ERROR (Status2);\r
507\r
508 if (VgpuDev->Child == NULL &&\r
509 (RemainingDevicePath == NULL ||\r
510 !IsDevicePathEnd (RemainingDevicePath))) {\r
511 Status = EFI_SUCCESS;\r
512 }\r
513 }\r
514\r
515 return Status;\r
516 }\r
517\r
518 //\r
519 // First BY_DRIVER open; check the VirtIo revision and subsystem.\r
520 //\r
521 if (VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0) ||\r
522 VirtIo->SubSystemDeviceId != VIRTIO_SUBSYSTEM_GPU_DEVICE) {\r
523 Status = EFI_UNSUPPORTED;\r
524 goto CloseVirtIo;\r
525 }\r
526\r
527 //\r
528 // We'll need the device path of the VirtIo device both for formatting\r
529 // VGPU_DEV.BusName and for populating VGPU_GOP.GopDevicePath.\r
530 //\r
531 Status = gBS->OpenProtocol (ControllerHandle, &gEfiDevicePathProtocolGuid,\r
532 NULL, This->DriverBindingHandle, ControllerHandle,\r
533 EFI_OPEN_PROTOCOL_TEST_PROTOCOL);\r
534\r
535CloseVirtIo:\r
536 gBS->CloseProtocol (ControllerHandle, &gVirtioDeviceProtocolGuid,\r
537 This->DriverBindingHandle, ControllerHandle);\r
538\r
539 return Status;\r
540}\r
541\r
542STATIC\r
543EFI_STATUS\r
544EFIAPI\r
545VirtioGpuDriverBindingStart (\r
546 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
547 IN EFI_HANDLE ControllerHandle,\r
548 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
549 )\r
550{\r
551 EFI_STATUS Status;\r
552 VIRTIO_DEVICE_PROTOCOL *VirtIo;\r
553 BOOLEAN VirtIoBoundJustNow;\r
554 VGPU_DEV *VgpuDev;\r
555 EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
556\r
557 //\r
558 // Open the Virtio Device Protocol.\r
559 //\r
560 // The result of this operation, combined with the checks in\r
561 // VirtioGpuDriverBindingSupported(), uniquely tells us whether we are\r
562 // binding the VirtIo controller on this call (with or without creating child\r
563 // controllers), or else we're *only* creating child controllers.\r
564 //\r
565 Status = gBS->OpenProtocol (ControllerHandle, &gVirtioDeviceProtocolGuid,\r
566 (VOID **)&VirtIo, This->DriverBindingHandle,\r
567 ControllerHandle, EFI_OPEN_PROTOCOL_BY_DRIVER);\r
568 if (EFI_ERROR (Status)) {\r
569 //\r
570 // The assertions below are based on the success of\r
571 // VirtioGpuDriverBindingSupported(): we bound ControllerHandle earlier,\r
572 // without producing child handles, and now we're producing the GOP child\r
573 // handle only.\r
574 //\r
575 ASSERT (Status == EFI_ALREADY_STARTED);\r
576\r
577 Status = gBS->OpenProtocol (ControllerHandle, &gEfiCallerIdGuid,\r
578 (VOID **)&VgpuDev, This->DriverBindingHandle,\r
579 ControllerHandle, EFI_OPEN_PROTOCOL_GET_PROTOCOL);\r
580 ASSERT_EFI_ERROR (Status);\r
581\r
582 ASSERT (VgpuDev->Child == NULL);\r
583 ASSERT (\r
584 RemainingDevicePath == NULL || !IsDevicePathEnd (RemainingDevicePath));\r
585\r
586 VirtIoBoundJustNow = FALSE;\r
587 } else {\r
588 VirtIoBoundJustNow = TRUE;\r
589\r
590 //\r
591 // Allocate the private structure.\r
592 //\r
593 VgpuDev = AllocateZeroPool (sizeof *VgpuDev);\r
594 if (VgpuDev == NULL) {\r
595 Status = EFI_OUT_OF_RESOURCES;\r
596 goto CloseVirtIo;\r
597 }\r
598 VgpuDev->VirtIo = VirtIo;\r
599 }\r
600\r
601 //\r
602 // Grab the VirtIo controller's device path. This is necessary regardless of\r
603 // VirtIoBoundJustNow.\r
604 //\r
605 Status = gBS->OpenProtocol (ControllerHandle, &gEfiDevicePathProtocolGuid,\r
606 (VOID **)&DevicePath, This->DriverBindingHandle,\r
607 ControllerHandle, EFI_OPEN_PROTOCOL_GET_PROTOCOL);\r
608 if (EFI_ERROR (Status)) {\r
609 goto FreeVgpuDev;\r
610 }\r
611\r
612 //\r
613 // Create VGPU_DEV if we've bound the VirtIo controller right now (that is,\r
614 // if we aren't *only* creating child handles).\r
615 //\r
616 if (VirtIoBoundJustNow) {\r
617 CHAR16 *VgpuDevName;\r
618\r
619 //\r
620 // Format a human-readable controller name for VGPU_DEV, and stash it for\r
621 // VirtioGpuGetControllerName() to look up.\r
622 //\r
623 Status = FormatVgpuDevName (ControllerHandle, This->DriverBindingHandle,\r
624 DevicePath, &VgpuDevName);\r
625 if (EFI_ERROR (Status)) {\r
626 goto FreeVgpuDev;\r
627 }\r
628 Status = AddUnicodeString2 ("en", mComponentName2.SupportedLanguages,\r
629 &VgpuDev->BusName, VgpuDevName, FALSE /* Iso639Language */);\r
630 FreePool (VgpuDevName);\r
631 if (EFI_ERROR (Status)) {\r
632 goto FreeVgpuDev;\r
633 }\r
634\r
c5f235bb
LE
635 Status = VirtioGpuInit (VgpuDev);\r
636 if (EFI_ERROR (Status)) {\r
637 goto FreeVgpuDevBusName;\r
638 }\r
639\r
640 Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK,\r
641 VirtioGpuExitBoot, VgpuDev /* NotifyContext */,\r
642 &VgpuDev->ExitBoot);\r
643 if (EFI_ERROR (Status)) {\r
644 goto UninitGpu;\r
645 }\r
646\r
a2a4fa66
LE
647 //\r
648 // Install the VGPU_DEV "protocol interface" on ControllerHandle.\r
649 //\r
650 Status = gBS->InstallProtocolInterface (&ControllerHandle,\r
651 &gEfiCallerIdGuid, EFI_NATIVE_INTERFACE, VgpuDev);\r
652 if (EFI_ERROR (Status)) {\r
c5f235bb 653 goto CloseExitBoot;\r
a2a4fa66
LE
654 }\r
655\r
656 if (RemainingDevicePath != NULL && IsDevicePathEnd (RemainingDevicePath)) {\r
657 //\r
658 // No child handle should be produced; we're done.\r
659 //\r
660 DEBUG ((EFI_D_INFO, "%a: bound VirtIo=%p without producing GOP\n",\r
661 __FUNCTION__, (VOID *)VgpuDev->VirtIo));\r
662 return EFI_SUCCESS;\r
663 }\r
664 }\r
665\r
666 //\r
667 // Below we'll produce our single child handle: the caller requested it\r
668 // either specifically, or as part of all child handles.\r
669 //\r
670 ASSERT (VgpuDev->Child == NULL);\r
671 ASSERT (\r
672 RemainingDevicePath == NULL || !IsDevicePathEnd (RemainingDevicePath));\r
673\r
674 Status = InitVgpuGop (VgpuDev, DevicePath, ControllerHandle,\r
675 This->DriverBindingHandle);\r
676 if (EFI_ERROR (Status)) {\r
677 goto UninstallVgpuDev;\r
678 }\r
679\r
680 //\r
681 // We're done.\r
682 //\r
683 DEBUG ((EFI_D_INFO, "%a: produced GOP %a VirtIo=%p\n", __FUNCTION__,\r
684 VirtIoBoundJustNow ? "while binding" : "for pre-bound",\r
685 (VOID *)VgpuDev->VirtIo));\r
686 return EFI_SUCCESS;\r
687\r
688UninstallVgpuDev:\r
689 if (VirtIoBoundJustNow) {\r
690 gBS->UninstallProtocolInterface (ControllerHandle, &gEfiCallerIdGuid,\r
691 VgpuDev);\r
692 }\r
693\r
c5f235bb
LE
694CloseExitBoot:\r
695 if (VirtIoBoundJustNow) {\r
696 gBS->CloseEvent (VgpuDev->ExitBoot);\r
697 }\r
698\r
699UninitGpu:\r
700 if (VirtIoBoundJustNow) {\r
701 VirtioGpuUninit (VgpuDev);\r
702 }\r
703\r
a2a4fa66
LE
704FreeVgpuDevBusName:\r
705 if (VirtIoBoundJustNow) {\r
706 FreeUnicodeStringTable (VgpuDev->BusName);\r
707 }\r
708\r
709FreeVgpuDev:\r
710 if (VirtIoBoundJustNow) {\r
711 FreePool (VgpuDev);\r
712 }\r
713\r
714CloseVirtIo:\r
715 if (VirtIoBoundJustNow) {\r
716 gBS->CloseProtocol (ControllerHandle, &gVirtioDeviceProtocolGuid,\r
717 This->DriverBindingHandle, ControllerHandle);\r
718 }\r
719\r
720 return Status;\r
721}\r
722\r
723STATIC\r
724EFI_STATUS\r
725EFIAPI\r
726VirtioGpuDriverBindingStop (\r
727 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
728 IN EFI_HANDLE ControllerHandle,\r
729 IN UINTN NumberOfChildren,\r
730 IN EFI_HANDLE *ChildHandleBuffer OPTIONAL\r
731 )\r
732{\r
733 EFI_STATUS Status;\r
734 VGPU_DEV *VgpuDev;\r
735\r
736 //\r
737 // Look up the VGPU_DEV "protocol interface" on ControllerHandle.\r
738 //\r
739 Status = gBS->OpenProtocol (ControllerHandle, &gEfiCallerIdGuid,\r
740 (VOID **)&VgpuDev, This->DriverBindingHandle,\r
741 ControllerHandle, EFI_OPEN_PROTOCOL_GET_PROTOCOL);\r
742 if (EFI_ERROR (Status)) {\r
743 return Status;\r
744 }\r
745 //\r
746 // Sanity check: if we found gEfiCallerIdGuid on ControllerHandle, then we\r
747 // keep its Virtio Device Protocol interface open BY_DRIVER.\r
748 //\r
749 ASSERT_EFI_ERROR (EfiTestManagedDevice (ControllerHandle,\r
750 This->DriverBindingHandle, &gVirtioDeviceProtocolGuid));\r
751\r
752 switch (NumberOfChildren) {\r
753 case 0:\r
754 //\r
755 // The caller wants us to unbind the VirtIo controller.\r
756 //\r
757 if (VgpuDev->Child != NULL) {\r
758 //\r
759 // We still have the GOP child.\r
760 //\r
761 Status = EFI_DEVICE_ERROR;\r
762 break;\r
763 }\r
764\r
765 DEBUG ((EFI_D_INFO, "%a: unbinding GOP-less VirtIo=%p\n", __FUNCTION__,\r
766 (VOID *)VgpuDev->VirtIo));\r
767\r
768 Status = gBS->UninstallProtocolInterface (ControllerHandle,\r
769 &gEfiCallerIdGuid, VgpuDev);\r
770 ASSERT_EFI_ERROR (Status);\r
771\r
c5f235bb
LE
772 Status = gBS->CloseEvent (VgpuDev->ExitBoot);\r
773 ASSERT_EFI_ERROR (Status);\r
774\r
775 VirtioGpuUninit (VgpuDev);\r
a2a4fa66
LE
776 FreeUnicodeStringTable (VgpuDev->BusName);\r
777 FreePool (VgpuDev);\r
778\r
779 Status = gBS->CloseProtocol (ControllerHandle, &gVirtioDeviceProtocolGuid,\r
780 This->DriverBindingHandle, ControllerHandle);\r
781 ASSERT_EFI_ERROR (Status);\r
782 break;\r
783\r
784 case 1:\r
785 //\r
786 // The caller wants us to destroy our child GOP controller.\r
787 //\r
788 if (VgpuDev->Child == NULL ||\r
789 ChildHandleBuffer[0] != VgpuDev->Child->GopHandle) {\r
790 //\r
791 // We have no child controller at the moment, or it differs from the one\r
792 // the caller wants us to destroy. I.e., we don't own the child\r
793 // controller passed in.\r
794 //\r
795 Status = EFI_DEVICE_ERROR;\r
796 break;\r
797 }\r
798 //\r
799 // Sanity check: our GOP child controller keeps the VGPU_DEV controller's\r
800 // Virtio Device Protocol interface open BY_CHILD_CONTROLLER.\r
801 //\r
802 ASSERT_EFI_ERROR (EfiTestChildHandle (ControllerHandle,\r
803 VgpuDev->Child->GopHandle,\r
804 &gVirtioDeviceProtocolGuid));\r
805\r
806 DEBUG ((EFI_D_INFO, "%a: destroying GOP under VirtIo=%p\n", __FUNCTION__,\r
807 (VOID *)VgpuDev->VirtIo));\r
808 UninitVgpuGop (VgpuDev, ControllerHandle, This->DriverBindingHandle);\r
809 break;\r
810\r
811 default:\r
812 //\r
813 // Impossible, we never produced more than one child.\r
814 //\r
815 Status = EFI_DEVICE_ERROR;\r
816 break;\r
817 }\r
818 return Status;\r
819}\r
820\r
821STATIC EFI_DRIVER_BINDING_PROTOCOL mDriverBinding = {\r
822 VirtioGpuDriverBindingSupported,\r
823 VirtioGpuDriverBindingStart,\r
824 VirtioGpuDriverBindingStop,\r
825 0x10, // Version\r
826 NULL, // ImageHandle, overwritten in entry point\r
827 NULL // DriverBindingHandle, ditto\r
828};\r
829\r
830//\r
831// Entry point of the driver.\r
832//\r
833EFI_STATUS\r
834EFIAPI\r
835VirtioGpuEntryPoint (\r
836 IN EFI_HANDLE ImageHandle,\r
837 IN EFI_SYSTEM_TABLE *SystemTable\r
838 )\r
839{\r
840 return EfiLibInstallDriverBindingComponentName2 (ImageHandle, SystemTable,\r
841 &mDriverBinding, ImageHandle, NULL /* ComponentName */,\r
842 &mComponentName2);\r
843}\r