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