]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/PlatformDxe/Platform.c
4bf22712c78f5e6b42b5fb74653e6d98a6639eeb
[mirror_edk2.git] / OvmfPkg / PlatformDxe / Platform.c
1 /** @file
2 This driver effectuates OVMF's platform configuration settings and exposes
3 them via HII.
4
5 Copyright (C) 2014, Red Hat, Inc.
6 Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>
7
8 SPDX-License-Identifier: BSD-2-Clause-Patent
9 **/
10
11 #include <Library/BaseLib.h>
12 #include <Library/BaseMemoryLib.h>
13 #include <Library/DebugLib.h>
14 #include <Library/DevicePathLib.h>
15 #include <Library/HiiLib.h>
16 #include <Library/MemoryAllocationLib.h>
17 #include <Library/PrintLib.h>
18 #include <Library/UefiBootServicesTableLib.h>
19 #include <Library/UefiHiiServicesLib.h>
20 #include <Protocol/DevicePath.h>
21 #include <Protocol/GraphicsOutput.h>
22 #include <Protocol/HiiConfigAccess.h>
23 #include <Guid/MdeModuleHii.h>
24 #include <Guid/OvmfPlatformConfig.h>
25
26 #include "Platform.h"
27 #include "PlatformConfig.h"
28
29 //
30 // The HiiAddPackages() library function requires that any controller (or
31 // image) handle, to be associated with the HII packages under installation, be
32 // "decorated" with a device path. The tradition seems to be a vendor device
33 // path.
34 //
35 // We'd like to associate our HII packages with the driver's image handle. The
36 // first idea is to use the driver image's device path. Unfortunately, loaded
37 // images only come with an EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL (not the
38 // usual EFI_DEVICE_PATH_PROTOCOL), ie. a different GUID. In addition, even the
39 // EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL interface may be NULL, if the image
40 // has been loaded from an "unnamed" memory source buffer.
41 //
42 // Hence let's just stick with the tradition -- use a dedicated vendor device
43 // path, with the driver's FILE_GUID.
44 //
45 #pragma pack(1)
46 typedef struct {
47 VENDOR_DEVICE_PATH VendorDevicePath;
48 EFI_DEVICE_PATH_PROTOCOL End;
49 } PKG_DEVICE_PATH;
50 #pragma pack()
51
52 STATIC PKG_DEVICE_PATH mPkgDevicePath = {
53 {
54 {
55 HARDWARE_DEVICE_PATH,
56 HW_VENDOR_DP,
57 {
58 (UINT8)(sizeof (VENDOR_DEVICE_PATH)),
59 (UINT8)(sizeof (VENDOR_DEVICE_PATH) >> 8)
60 }
61 },
62 EFI_CALLER_ID_GUID
63 },
64 {
65 END_DEVICE_PATH_TYPE,
66 END_ENTIRE_DEVICE_PATH_SUBTYPE,
67 {
68 (UINT8)(END_DEVICE_PATH_LENGTH),
69 (UINT8)(END_DEVICE_PATH_LENGTH >> 8)
70 }
71 }
72 };
73
74 //
75 // The configuration interface between the HII engine (form display etc) and
76 // this driver.
77 //
78 STATIC EFI_HII_CONFIG_ACCESS_PROTOCOL mConfigAccess;
79
80 //
81 // The handle representing our list of packages after installation.
82 //
83 STATIC EFI_HII_HANDLE mInstalledPackages;
84
85 //
86 // The arrays below constitute our HII package list. They are auto-generated by
87 // the VFR compiler and linked into the driver image during the build.
88 //
89 // - The strings package receives its C identifier from the driver's BASE_NAME,
90 // plus "Strings".
91 //
92 // - The forms package receives its C identifier from the VFR file's basename,
93 // plus "Bin".
94 //
95 //
96 extern UINT8 PlatformDxeStrings[];
97 extern UINT8 PlatformFormsBin[];
98
99 //
100 // We want to be notified about GOP installations until we find one GOP
101 // interface that lets us populate the form.
102 //
103 STATIC EFI_EVENT mGopEvent;
104
105 //
106 // The registration record underneath this pointer allows us to iterate through
107 // the GOP instances one by one.
108 //
109 STATIC VOID *mGopTracker;
110
111 //
112 // Cache the resolutions we get from the GOP.
113 //
114 typedef struct {
115 UINT32 X;
116 UINT32 Y;
117 } GOP_MODE;
118
119 STATIC UINTN mNumGopModes;
120 STATIC GOP_MODE *mGopModes;
121
122 /**
123 Load the persistent platform configuration and translate it to binary form
124 state.
125
126 If the platform configuration is missing, then the function fills in a
127 default state.
128
129 @param[out] MainFormState Binary form/widget state after translation.
130
131 @retval EFI_SUCCESS Form/widget state ready.
132 @return Error codes from underlying functions.
133 **/
134 STATIC
135 EFI_STATUS
136 EFIAPI
137 PlatformConfigToFormState (
138 OUT MAIN_FORM_STATE *MainFormState
139 )
140 {
141 EFI_STATUS Status;
142 PLATFORM_CONFIG PlatformConfig;
143 UINT64 OptionalElements;
144 UINTN ModeNumber;
145
146 ZeroMem (MainFormState, sizeof *MainFormState);
147
148 Status = PlatformConfigLoad (&PlatformConfig, &OptionalElements);
149 switch (Status) {
150 case EFI_SUCCESS:
151 if (OptionalElements & PLATFORM_CONFIG_F_GRAPHICS_RESOLUTION) {
152 //
153 // Format the preferred resolution as text.
154 //
155 UnicodeSPrintAsciiFormat (
156 (CHAR16 *)MainFormState->CurrentPreferredResolution,
157 sizeof MainFormState->CurrentPreferredResolution,
158 "%Ldx%Ld",
159 (INT64)PlatformConfig.HorizontalResolution,
160 (INT64)PlatformConfig.VerticalResolution
161 );
162
163 //
164 // Try to locate it in the drop-down list too. This may not succeed, but
165 // that's fine.
166 //
167 for (ModeNumber = 0; ModeNumber < mNumGopModes; ++ModeNumber) {
168 if ((mGopModes[ModeNumber].X == PlatformConfig.HorizontalResolution) &&
169 (mGopModes[ModeNumber].Y == PlatformConfig.VerticalResolution))
170 {
171 MainFormState->NextPreferredResolution = (UINT32)ModeNumber;
172 break;
173 }
174 }
175
176 break;
177 }
178
179 //
180 // fall through otherwise
181 //
182
183 case EFI_NOT_FOUND:
184 UnicodeSPrintAsciiFormat (
185 (CHAR16 *)MainFormState->CurrentPreferredResolution,
186 sizeof MainFormState->CurrentPreferredResolution,
187 "Unset"
188 );
189 break;
190
191 default:
192 return Status;
193 }
194
195 return EFI_SUCCESS;
196 }
197
198 /**
199 This function is called by the HII machinery when it fetches the form state.
200
201 See the precise documentation in the UEFI spec.
202
203 @param[in] This The Config Access Protocol instance.
204
205 @param[in] Request A <ConfigRequest> format UCS-2 string describing the
206 query.
207
208 @param[out] Progress A pointer into Request on output, identifying the query
209 element where processing failed.
210
211 @param[out] Results A <MultiConfigAltResp> format UCS-2 string that has
212 all values filled in for the names in the Request
213 string.
214
215 @retval EFI_SUCCESS Extraction of form state in <MultiConfigAltResp>
216 encoding successful.
217 @return Status codes from underlying functions.
218
219 **/
220 STATIC
221 EFI_STATUS
222 EFIAPI
223 ExtractConfig (
224 IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
225 IN CONST EFI_STRING Request,
226 OUT EFI_STRING *Progress,
227 OUT EFI_STRING *Results
228 )
229 {
230 MAIN_FORM_STATE MainFormState;
231 EFI_STATUS Status;
232
233 DEBUG ((DEBUG_VERBOSE, "%a: Request=\"%s\"\n", __FUNCTION__, Request));
234
235 Status = PlatformConfigToFormState (&MainFormState);
236 if (EFI_ERROR (Status)) {
237 *Progress = Request;
238 return Status;
239 }
240
241 //
242 // Answer the textual request keying off the binary form state.
243 //
244 Status = gHiiConfigRouting->BlockToConfig (
245 gHiiConfigRouting,
246 Request,
247 (VOID *)&MainFormState,
248 sizeof MainFormState,
249 Results,
250 Progress
251 );
252 if (EFI_ERROR (Status)) {
253 DEBUG ((
254 DEBUG_ERROR,
255 "%a: BlockToConfig(): %r, Progress=\"%s\"\n",
256 __FUNCTION__,
257 Status,
258 (Status == EFI_DEVICE_ERROR) ? NULL : *Progress
259 ));
260 } else {
261 DEBUG ((DEBUG_VERBOSE, "%a: Results=\"%s\"\n", __FUNCTION__, *Results));
262 }
263
264 return Status;
265 }
266
267 /**
268 Interpret the binary form state and save it as persistent platform
269 configuration.
270
271 @param[in] MainFormState Binary form/widget state to verify and save.
272
273 @retval EFI_SUCCESS Platform configuration saved.
274 @return Error codes from underlying functions.
275 **/
276 STATIC
277 EFI_STATUS
278 EFIAPI
279 FormStateToPlatformConfig (
280 IN CONST MAIN_FORM_STATE *MainFormState
281 )
282 {
283 EFI_STATUS Status;
284 PLATFORM_CONFIG PlatformConfig;
285 CONST GOP_MODE *GopMode;
286
287 //
288 // There's nothing to do with the textual CurrentPreferredResolution field.
289 // We verify and translate the selection in the drop-down list.
290 //
291 if (MainFormState->NextPreferredResolution >= mNumGopModes) {
292 return EFI_INVALID_PARAMETER;
293 }
294
295 GopMode = mGopModes + MainFormState->NextPreferredResolution;
296
297 ZeroMem (&PlatformConfig, sizeof PlatformConfig);
298 PlatformConfig.HorizontalResolution = GopMode->X;
299 PlatformConfig.VerticalResolution = GopMode->Y;
300
301 Status = PlatformConfigSave (&PlatformConfig);
302 return Status;
303 }
304
305 /**
306 This function is called by the HII machinery when it wants the driver to
307 interpret and persist the form state.
308
309 See the precise documentation in the UEFI spec.
310
311 @param[in] This The Config Access Protocol instance.
312
313 @param[in] Configuration A <ConfigResp> format UCS-2 string describing the
314 form state.
315
316 @param[out] Progress A pointer into Configuration on output,
317 identifying the element where processing failed.
318
319 @retval EFI_SUCCESS Configuration verified, state permanent.
320
321 @return Status codes from underlying functions.
322 **/
323 STATIC
324 EFI_STATUS
325 EFIAPI
326 RouteConfig (
327 IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
328 IN CONST EFI_STRING Configuration,
329 OUT EFI_STRING *Progress
330 )
331 {
332 MAIN_FORM_STATE MainFormState;
333 UINTN BlockSize;
334 EFI_STATUS Status;
335
336 DEBUG ((
337 DEBUG_VERBOSE,
338 "%a: Configuration=\"%s\"\n",
339 __FUNCTION__,
340 Configuration
341 ));
342
343 //
344 // the "read" step in RMW
345 //
346 Status = PlatformConfigToFormState (&MainFormState);
347 if (EFI_ERROR (Status)) {
348 *Progress = Configuration;
349 return Status;
350 }
351
352 //
353 // the "modify" step in RMW
354 //
355 // (Update the binary form state. This update may be partial, which is why in
356 // general we must pre-load the form state from the platform config.)
357 //
358 BlockSize = sizeof MainFormState;
359 Status = gHiiConfigRouting->ConfigToBlock (
360 gHiiConfigRouting,
361 Configuration,
362 (VOID *)&MainFormState,
363 &BlockSize,
364 Progress
365 );
366 if (EFI_ERROR (Status)) {
367 DEBUG ((
368 DEBUG_ERROR,
369 "%a: ConfigToBlock(): %r, Progress=\"%s\"\n",
370 __FUNCTION__,
371 Status,
372 (Status == EFI_BUFFER_TOO_SMALL) ? NULL : *Progress
373 ));
374 return Status;
375 }
376
377 //
378 // the "write" step in RMW
379 //
380 Status = FormStateToPlatformConfig (&MainFormState);
381 if (EFI_ERROR (Status)) {
382 *Progress = Configuration;
383 }
384
385 return Status;
386 }
387
388 STATIC
389 EFI_STATUS
390 EFIAPI
391 Callback (
392 IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
393 IN EFI_BROWSER_ACTION Action,
394 IN EFI_QUESTION_ID QuestionId,
395 IN UINT8 Type,
396 IN OUT EFI_IFR_TYPE_VALUE *Value,
397 OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest
398 )
399 {
400 DEBUG ((
401 DEBUG_VERBOSE,
402 "%a: Action=0x%Lx QuestionId=%d Type=%d\n",
403 __FUNCTION__,
404 (UINT64)Action,
405 QuestionId,
406 Type
407 ));
408
409 if (Action != EFI_BROWSER_ACTION_CHANGED) {
410 return EFI_UNSUPPORTED;
411 }
412
413 switch (QuestionId) {
414 case QUESTION_SAVE_EXIT:
415 *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_SUBMIT_EXIT;
416 break;
417
418 case QUESTION_DISCARD_EXIT:
419 *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_DISCARD_EXIT;
420 break;
421
422 default:
423 break;
424 }
425
426 return EFI_SUCCESS;
427 }
428
429 /**
430 Query and save all resolutions supported by the GOP.
431
432 @param[in] Gop The Graphics Output Protocol instance to query.
433
434 @param[out] NumGopModes The number of modes supported by the GOP. On output,
435 this parameter will be positive.
436
437 @param[out] GopModes On output, a dynamically allocated array containing
438 the resolutions returned by the GOP. The caller is
439 responsible for freeing the array after use.
440
441 @retval EFI_UNSUPPORTED No modes found.
442 @retval EFI_OUT_OF_RESOURCES Failed to allocate GopModes.
443 @return Error codes from Gop->QueryMode().
444
445 **/
446 STATIC
447 EFI_STATUS
448 EFIAPI
449 QueryGopModes (
450 IN EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop,
451 OUT UINTN *NumGopModes,
452 OUT GOP_MODE **GopModes
453 )
454 {
455 EFI_STATUS Status;
456 UINT32 ModeNumber;
457
458 if (Gop->Mode->MaxMode == 0) {
459 return EFI_UNSUPPORTED;
460 }
461
462 *NumGopModes = Gop->Mode->MaxMode;
463
464 *GopModes = AllocatePool (Gop->Mode->MaxMode * sizeof **GopModes);
465 if (*GopModes == NULL) {
466 return EFI_OUT_OF_RESOURCES;
467 }
468
469 for (ModeNumber = 0; ModeNumber < Gop->Mode->MaxMode; ++ModeNumber) {
470 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info;
471 UINTN SizeOfInfo;
472
473 Status = Gop->QueryMode (Gop, ModeNumber, &SizeOfInfo, &Info);
474 if (EFI_ERROR (Status)) {
475 goto FreeGopModes;
476 }
477
478 (*GopModes)[ModeNumber].X = Info->HorizontalResolution;
479 (*GopModes)[ModeNumber].Y = Info->VerticalResolution;
480 FreePool (Info);
481 }
482
483 return EFI_SUCCESS;
484
485 FreeGopModes:
486 FreePool (*GopModes);
487
488 return Status;
489 }
490
491 /**
492 Create a set of "one-of-many" (ie. "drop down list") option IFR opcodes,
493 based on available GOP resolutions, to be placed under a "one-of-many" (ie.
494 "drop down list") opcode.
495
496 @param[in] PackageList The package list with the formset and form for
497 which the drop down options are produced. Option
498 names are added as new strings to PackageList.
499
500 @param[out] OpCodeBuffer On output, a dynamically allocated opcode buffer
501 with drop down list options corresponding to GOP
502 resolutions. The caller is responsible for freeing
503 OpCodeBuffer with HiiFreeOpCodeHandle() after use.
504
505 @param[in] NumGopModes Number of entries in GopModes.
506
507 @param[in] GopModes Array of resolutions retrieved from the GOP.
508
509 @retval EFI_SUCESS Opcodes have been successfully produced.
510
511 @return Status codes from underlying functions. PackageList may
512 have been extended with new strings. OpCodeBuffer is
513 unchanged.
514 **/
515 STATIC
516 EFI_STATUS
517 EFIAPI
518 CreateResolutionOptions (
519 IN EFI_HII_HANDLE PackageList,
520 OUT VOID **OpCodeBuffer,
521 IN UINTN NumGopModes,
522 IN GOP_MODE *GopModes
523 )
524 {
525 EFI_STATUS Status;
526 VOID *OutputBuffer;
527 UINTN ModeNumber;
528
529 OutputBuffer = HiiAllocateOpCodeHandle ();
530 if (OutputBuffer == NULL) {
531 return EFI_OUT_OF_RESOURCES;
532 }
533
534 for (ModeNumber = 0; ModeNumber < NumGopModes; ++ModeNumber) {
535 CHAR16 Desc[MAXSIZE_RES_CUR];
536 EFI_STRING_ID NewString;
537 VOID *OpCode;
538
539 UnicodeSPrintAsciiFormat (
540 Desc,
541 sizeof Desc,
542 "%Ldx%Ld",
543 (INT64)GopModes[ModeNumber].X,
544 (INT64)GopModes[ModeNumber].Y
545 );
546 NewString = HiiSetString (
547 PackageList,
548 0 /* new string */,
549 Desc,
550 NULL /* for all languages */
551 );
552 if (NewString == 0) {
553 Status = EFI_OUT_OF_RESOURCES;
554 goto FreeOutputBuffer;
555 }
556
557 OpCode = HiiCreateOneOfOptionOpCode (
558 OutputBuffer,
559 NewString,
560 0 /* Flags */,
561 EFI_IFR_NUMERIC_SIZE_4,
562 ModeNumber
563 );
564 if (OpCode == NULL) {
565 Status = EFI_OUT_OF_RESOURCES;
566 goto FreeOutputBuffer;
567 }
568 }
569
570 *OpCodeBuffer = OutputBuffer;
571 return EFI_SUCCESS;
572
573 FreeOutputBuffer:
574 HiiFreeOpCodeHandle (OutputBuffer);
575
576 return Status;
577 }
578
579 /**
580 Populate the form identified by the (PackageList, FormSetGuid, FormId)
581 triplet.
582
583 The drop down list of video resolutions is generated from (NumGopModes,
584 GopModes).
585
586 @retval EFI_SUCESS Form successfully updated.
587 @return Status codes from underlying functions.
588
589 **/
590 STATIC
591 EFI_STATUS
592 EFIAPI
593 PopulateForm (
594 IN EFI_HII_HANDLE PackageList,
595 IN EFI_GUID *FormSetGuid,
596 IN EFI_FORM_ID FormId,
597 IN UINTN NumGopModes,
598 IN GOP_MODE *GopModes
599 )
600 {
601 EFI_STATUS Status;
602 VOID *OpCodeBuffer;
603 VOID *OpCode;
604 EFI_IFR_GUID_LABEL *Anchor;
605 VOID *OpCodeBuffer2;
606
607 OpCodeBuffer2 = NULL;
608
609 //
610 // 1. Allocate an empty opcode buffer.
611 //
612 OpCodeBuffer = HiiAllocateOpCodeHandle ();
613 if (OpCodeBuffer == NULL) {
614 return EFI_OUT_OF_RESOURCES;
615 }
616
617 //
618 // 2. Create a label opcode (which is a Tiano extension) inside the buffer.
619 // The label's number must match the "anchor" label in the form.
620 //
621 OpCode = HiiCreateGuidOpCode (
622 OpCodeBuffer,
623 &gEfiIfrTianoGuid,
624 NULL /* optional copy origin */,
625 sizeof *Anchor
626 );
627 if (OpCode == NULL) {
628 Status = EFI_OUT_OF_RESOURCES;
629 goto FreeOpCodeBuffer;
630 }
631
632 Anchor = OpCode;
633 Anchor->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
634 Anchor->Number = LABEL_RES_NEXT;
635
636 //
637 // 3. Create the opcodes inside the buffer that are to be inserted into the
638 // form.
639 //
640 // 3.1. Get a list of resolutions.
641 //
642 Status = CreateResolutionOptions (
643 PackageList,
644 &OpCodeBuffer2,
645 NumGopModes,
646 GopModes
647 );
648 if (EFI_ERROR (Status)) {
649 goto FreeOpCodeBuffer;
650 }
651
652 //
653 // 3.2. Create a one-of-many question with the above options.
654 //
655 OpCode = HiiCreateOneOfOpCode (
656 OpCodeBuffer, // create opcode inside this
657 // opcode buffer,
658 QUESTION_RES_NEXT, // ID of question,
659 FORMSTATEID_MAIN_FORM, // identifies form state
660 // storage,
661 (UINT16)OFFSET_OF (
662 MAIN_FORM_STATE, // value of question stored
663 NextPreferredResolution
664 ), // at this offset,
665 STRING_TOKEN (STR_RES_NEXT), // Prompt,
666 STRING_TOKEN (STR_RES_NEXT_HELP), // Help,
667 0, // QuestionFlags,
668 EFI_IFR_NUMERIC_SIZE_4, // see sizeof
669 // NextPreferredResolution,
670 OpCodeBuffer2, // buffer with possible
671 // choices,
672 NULL // DEFAULT opcodes
673 );
674 if (OpCode == NULL) {
675 Status = EFI_OUT_OF_RESOURCES;
676 goto FreeOpCodeBuffer2;
677 }
678
679 //
680 // 4. Update the form with the opcode buffer.
681 //
682 Status = HiiUpdateForm (
683 PackageList,
684 FormSetGuid,
685 FormId,
686 OpCodeBuffer, // buffer with head anchor, and new contents to be
687 // inserted at it
688 NULL // buffer with tail anchor, for deleting old
689 // contents up to it
690 );
691
692 FreeOpCodeBuffer2:
693 HiiFreeOpCodeHandle (OpCodeBuffer2);
694
695 FreeOpCodeBuffer:
696 HiiFreeOpCodeHandle (OpCodeBuffer);
697
698 return Status;
699 }
700
701 /**
702 Load and execute the platform configuration.
703
704 @retval EFI_SUCCESS Configuration loaded and executed.
705 @return Status codes from PlatformConfigLoad().
706 **/
707 STATIC
708 EFI_STATUS
709 EFIAPI
710 ExecutePlatformConfig (
711 VOID
712 )
713 {
714 EFI_STATUS Status;
715 PLATFORM_CONFIG PlatformConfig;
716 UINT64 OptionalElements;
717 RETURN_STATUS PcdStatus;
718
719 Status = PlatformConfigLoad (&PlatformConfig, &OptionalElements);
720 if (EFI_ERROR (Status)) {
721 DEBUG ((
722 (Status == EFI_NOT_FOUND) ? DEBUG_VERBOSE : DEBUG_ERROR,
723 "%a: failed to load platform config: %r\n",
724 __FUNCTION__,
725 Status
726 ));
727 return Status;
728 }
729
730 if (OptionalElements & PLATFORM_CONFIG_F_GRAPHICS_RESOLUTION) {
731 //
732 // Pass the preferred resolution to GraphicsConsoleDxe via dynamic PCDs.
733 //
734 PcdStatus = PcdSet32S (
735 PcdVideoHorizontalResolution,
736 PlatformConfig.HorizontalResolution
737 );
738 ASSERT_RETURN_ERROR (PcdStatus);
739
740 PcdStatus = PcdSet32S (
741 PcdVideoVerticalResolution,
742 PlatformConfig.VerticalResolution
743 );
744 ASSERT_RETURN_ERROR (PcdStatus);
745
746 PcdStatus = PcdSet8S (PcdVideoResolutionSource, 1);
747 ASSERT_RETURN_ERROR (PcdStatus);
748 }
749
750 return EFI_SUCCESS;
751 }
752
753 /**
754 Notification callback for GOP interface installation.
755
756 @param[in] Event Event whose notification function is being invoked.
757
758 @param[in] Context The pointer to the notification function's context, which
759 is implementation-dependent.
760 **/
761 STATIC
762 VOID
763 EFIAPI
764 GopInstalled (
765 IN EFI_EVENT Event,
766 IN VOID *Context
767 )
768 {
769 EFI_STATUS Status;
770 EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop;
771
772 ASSERT (Event == mGopEvent);
773
774 //
775 // Check further GOPs.
776 //
777 for ( ; ;) {
778 mNumGopModes = 0;
779 mGopModes = NULL;
780
781 Status = gBS->LocateProtocol (
782 &gEfiGraphicsOutputProtocolGuid,
783 mGopTracker,
784 (VOID **)&Gop
785 );
786 if (EFI_ERROR (Status)) {
787 return;
788 }
789
790 Status = QueryGopModes (Gop, &mNumGopModes, &mGopModes);
791 if (EFI_ERROR (Status)) {
792 continue;
793 }
794
795 Status = PopulateForm (
796 mInstalledPackages,
797 &gOvmfPlatformConfigGuid,
798 FORMID_MAIN_FORM,
799 mNumGopModes,
800 mGopModes
801 );
802 if (EFI_ERROR (Status)) {
803 FreePool (mGopModes);
804 continue;
805 }
806
807 break;
808 }
809
810 //
811 // Success -- so uninstall this callback. Closing the event removes all
812 // pending notifications and all protocol registrations.
813 //
814 Status = gBS->CloseEvent (mGopEvent);
815 ASSERT_EFI_ERROR (Status);
816 mGopEvent = NULL;
817 mGopTracker = NULL;
818 }
819
820 /**
821 Entry point for this driver.
822
823 @param[in] ImageHandle Image handle of this driver.
824 @param[in] SystemTable Pointer to SystemTable.
825
826 @retval EFI_SUCESS Driver has loaded successfully.
827 @retval EFI_OUT_OF_RESOURCES Failed to install HII packages.
828 @return Error codes from lower level functions.
829
830 **/
831 EFI_STATUS
832 EFIAPI
833 PlatformInit (
834 IN EFI_HANDLE ImageHandle,
835 IN EFI_SYSTEM_TABLE *SystemTable
836 )
837 {
838 EFI_STATUS Status;
839
840 ExecutePlatformConfig ();
841
842 mConfigAccess.ExtractConfig = &ExtractConfig;
843 mConfigAccess.RouteConfig = &RouteConfig;
844 mConfigAccess.Callback = &Callback;
845
846 //
847 // Declare ourselves suitable for HII communication.
848 //
849 Status = gBS->InstallMultipleProtocolInterfaces (
850 &ImageHandle,
851 &gEfiDevicePathProtocolGuid,
852 &mPkgDevicePath,
853 &gEfiHiiConfigAccessProtocolGuid,
854 &mConfigAccess,
855 NULL
856 );
857 if (EFI_ERROR (Status)) {
858 return Status;
859 }
860
861 //
862 // Publish the HII package list to HII Database.
863 //
864 mInstalledPackages = HiiAddPackages (
865 &gEfiCallerIdGuid, // PackageListGuid
866 ImageHandle, // associated DeviceHandle
867 PlatformDxeStrings, // 1st package
868 PlatformFormsBin, // 2nd package
869 NULL // terminator
870 );
871 if (mInstalledPackages == NULL) {
872 Status = EFI_OUT_OF_RESOURCES;
873 goto UninstallProtocols;
874 }
875
876 Status = gBS->CreateEvent (
877 EVT_NOTIFY_SIGNAL,
878 TPL_CALLBACK,
879 &GopInstalled,
880 NULL /* Context */,
881 &mGopEvent
882 );
883 if (EFI_ERROR (Status)) {
884 goto RemovePackages;
885 }
886
887 Status = gBS->RegisterProtocolNotify (
888 &gEfiGraphicsOutputProtocolGuid,
889 mGopEvent,
890 &mGopTracker
891 );
892 if (EFI_ERROR (Status)) {
893 goto CloseGopEvent;
894 }
895
896 //
897 // Check already installed GOPs.
898 //
899 Status = gBS->SignalEvent (mGopEvent);
900 ASSERT_EFI_ERROR (Status);
901
902 return EFI_SUCCESS;
903
904 CloseGopEvent:
905 gBS->CloseEvent (mGopEvent);
906
907 RemovePackages:
908 HiiRemovePackages (mInstalledPackages);
909
910 UninstallProtocols:
911 gBS->UninstallMultipleProtocolInterfaces (
912 ImageHandle,
913 &gEfiDevicePathProtocolGuid,
914 &mPkgDevicePath,
915 &gEfiHiiConfigAccessProtocolGuid,
916 &mConfigAccess,
917 NULL
918 );
919 return Status;
920 }
921
922 /**
923 Unload the driver.
924
925 @param[in] ImageHandle Handle that identifies the image to evict.
926
927 @retval EFI_SUCCESS The image has been unloaded.
928 **/
929 EFI_STATUS
930 EFIAPI
931 PlatformUnload (
932 IN EFI_HANDLE ImageHandle
933 )
934 {
935 if (mGopEvent == NULL) {
936 //
937 // The GOP callback ran successfully and unregistered itself. Release the
938 // resources allocated there.
939 //
940 ASSERT (mGopModes != NULL);
941 FreePool (mGopModes);
942 } else {
943 //
944 // Otherwise we need to unregister the callback.
945 //
946 ASSERT (mGopModes == NULL);
947 gBS->CloseEvent (mGopEvent);
948 }
949
950 //
951 // Release resources allocated by the entry point.
952 //
953 HiiRemovePackages (mInstalledPackages);
954 gBS->UninstallMultipleProtocolInterfaces (
955 ImageHandle,
956 &gEfiDevicePathProtocolGuid,
957 &mPkgDevicePath,
958 &gEfiHiiConfigAccessProtocolGuid,
959 &mConfigAccess,
960 NULL
961 );
962 return EFI_SUCCESS;
963 }