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