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