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