2 This driver effectuates OVMF's platform configuration settings and exposes
5 Copyright (C) 2014, Red Hat, Inc.
6 Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>
8 SPDX-License-Identifier: BSD-2-Clause-Patent
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>
27 #include "PlatformConfig.h"
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
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.
42 // Hence let's just stick with the tradition -- use a dedicated vendor device
43 // path, with the driver's FILE_GUID.
47 VENDOR_DEVICE_PATH VendorDevicePath
;
48 EFI_DEVICE_PATH_PROTOCOL End
;
52 STATIC PKG_DEVICE_PATH mPkgDevicePath
= {
58 (UINT8
) (sizeof (VENDOR_DEVICE_PATH
) ),
59 (UINT8
) (sizeof (VENDOR_DEVICE_PATH
) >> 8)
66 END_ENTIRE_DEVICE_PATH_SUBTYPE
,
68 (UINT8
) (END_DEVICE_PATH_LENGTH
),
69 (UINT8
) (END_DEVICE_PATH_LENGTH
>> 8)
75 // The configuration interface between the HII engine (form display etc) and
78 STATIC EFI_HII_CONFIG_ACCESS_PROTOCOL mConfigAccess
;
81 // The handle representing our list of packages after installation.
83 STATIC EFI_HII_HANDLE mInstalledPackages
;
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.
89 // - The strings package receives its C identifier from the driver's BASE_NAME,
92 // - The forms package receives its C identifier from the VFR file's basename,
96 extern UINT8 PlatformDxeStrings
[];
97 extern UINT8 PlatformFormsBin
[];
100 // We want to be notified about GOP installations until we find one GOP
101 // interface that lets us populate the form.
103 STATIC EFI_EVENT mGopEvent
;
106 // The registration record underneath this pointer allows us to iterate through
107 // the GOP instances one by one.
109 STATIC VOID
*mGopTracker
;
112 // Cache the resolutions we get from the GOP.
119 STATIC UINTN mNumGopModes
;
120 STATIC GOP_MODE
*mGopModes
;
124 Load the persistent platform configuration and translate it to binary form
127 If the platform configuration is missing, then the function fills in a
130 @param[out] MainFormState Binary form/widget state after translation.
132 @retval EFI_SUCCESS Form/widget state ready.
133 @return Error codes from underlying functions.
138 PlatformConfigToFormState (
139 OUT MAIN_FORM_STATE
*MainFormState
143 PLATFORM_CONFIG PlatformConfig
;
144 UINT64 OptionalElements
;
147 ZeroMem (MainFormState
, sizeof *MainFormState
);
149 Status
= PlatformConfigLoad (&PlatformConfig
, &OptionalElements
);
152 if (OptionalElements
& PLATFORM_CONFIG_F_GRAPHICS_RESOLUTION
) {
154 // Format the preferred resolution as text.
156 UnicodeSPrintAsciiFormat (
157 (CHAR16
*) MainFormState
->CurrentPreferredResolution
,
158 sizeof MainFormState
->CurrentPreferredResolution
,
160 (INT64
) PlatformConfig
.HorizontalResolution
,
161 (INT64
) PlatformConfig
.VerticalResolution
);
164 // Try to locate it in the drop-down list too. This may not succeed, but
167 for (ModeNumber
= 0; ModeNumber
< mNumGopModes
; ++ModeNumber
) {
168 if (mGopModes
[ModeNumber
].X
== PlatformConfig
.HorizontalResolution
&&
169 mGopModes
[ModeNumber
].Y
== PlatformConfig
.VerticalResolution
) {
170 MainFormState
->NextPreferredResolution
= (UINT32
) ModeNumber
;
178 // fall through otherwise
182 UnicodeSPrintAsciiFormat (
183 (CHAR16
*) MainFormState
->CurrentPreferredResolution
,
184 sizeof MainFormState
->CurrentPreferredResolution
,
197 This function is called by the HII machinery when it fetches the form state.
199 See the precise documentation in the UEFI spec.
201 @param[in] This The Config Access Protocol instance.
203 @param[in] Request A <ConfigRequest> format UCS-2 string describing the
206 @param[out] Progress A pointer into Request on output, identifying the query
207 element where processing failed.
209 @param[out] Results A <MultiConfigAltResp> format UCS-2 string that has
210 all values filled in for the names in the Request
213 @retval EFI_SUCCESS Extraction of form state in <MultiConfigAltResp>
215 @return Status codes from underlying functions.
222 IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL
*This
,
223 IN CONST EFI_STRING Request
,
224 OUT EFI_STRING
*Progress
,
225 OUT EFI_STRING
*Results
228 MAIN_FORM_STATE MainFormState
;
231 DEBUG ((EFI_D_VERBOSE
, "%a: Request=\"%s\"\n", __FUNCTION__
, Request
));
233 Status
= PlatformConfigToFormState (&MainFormState
);
234 if (EFI_ERROR (Status
)) {
240 // Answer the textual request keying off the binary form state.
242 Status
= gHiiConfigRouting
->BlockToConfig (gHiiConfigRouting
, Request
,
243 (VOID
*) &MainFormState
, sizeof MainFormState
,
245 if (EFI_ERROR (Status
)) {
246 DEBUG ((EFI_D_ERROR
, "%a: BlockToConfig(): %r, Progress=\"%s\"\n",
247 __FUNCTION__
, Status
, (Status
== EFI_DEVICE_ERROR
) ? NULL
: *Progress
));
249 DEBUG ((EFI_D_VERBOSE
, "%a: Results=\"%s\"\n", __FUNCTION__
, *Results
));
256 Interpret the binary form state and save it as persistent platform
259 @param[in] MainFormState Binary form/widget state to verify and save.
261 @retval EFI_SUCCESS Platform configuration saved.
262 @return Error codes from underlying functions.
267 FormStateToPlatformConfig (
268 IN CONST MAIN_FORM_STATE
*MainFormState
272 PLATFORM_CONFIG PlatformConfig
;
273 CONST GOP_MODE
*GopMode
;
276 // There's nothing to do with the textual CurrentPreferredResolution field.
277 // We verify and translate the selection in the drop-down list.
279 if (MainFormState
->NextPreferredResolution
>= mNumGopModes
) {
280 return EFI_INVALID_PARAMETER
;
282 GopMode
= mGopModes
+ MainFormState
->NextPreferredResolution
;
284 ZeroMem (&PlatformConfig
, sizeof PlatformConfig
);
285 PlatformConfig
.HorizontalResolution
= GopMode
->X
;
286 PlatformConfig
.VerticalResolution
= GopMode
->Y
;
288 Status
= PlatformConfigSave (&PlatformConfig
);
294 This function is called by the HII machinery when it wants the driver to
295 interpret and persist the form state.
297 See the precise documentation in the UEFI spec.
299 @param[in] This The Config Access Protocol instance.
301 @param[in] Configuration A <ConfigResp> format UCS-2 string describing the
304 @param[out] Progress A pointer into Configuration on output,
305 identifying the element where processing failed.
307 @retval EFI_SUCCESS Configuration verified, state permanent.
309 @return Status codes from underlying functions.
315 IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL
*This
,
316 IN CONST EFI_STRING Configuration
,
317 OUT EFI_STRING
*Progress
320 MAIN_FORM_STATE MainFormState
;
324 DEBUG ((EFI_D_VERBOSE
, "%a: Configuration=\"%s\"\n", __FUNCTION__
,
328 // the "read" step in RMW
330 Status
= PlatformConfigToFormState (&MainFormState
);
331 if (EFI_ERROR (Status
)) {
332 *Progress
= Configuration
;
337 // the "modify" step in RMW
339 // (Update the binary form state. This update may be partial, which is why in
340 // general we must pre-load the form state from the platform config.)
342 BlockSize
= sizeof MainFormState
;
343 Status
= gHiiConfigRouting
->ConfigToBlock (gHiiConfigRouting
, Configuration
,
344 (VOID
*) &MainFormState
, &BlockSize
, Progress
);
345 if (EFI_ERROR (Status
)) {
346 DEBUG ((EFI_D_ERROR
, "%a: ConfigToBlock(): %r, Progress=\"%s\"\n",
347 __FUNCTION__
, Status
,
348 (Status
== EFI_BUFFER_TOO_SMALL
) ? NULL
: *Progress
));
353 // the "write" step in RMW
355 Status
= FormStateToPlatformConfig (&MainFormState
);
356 if (EFI_ERROR (Status
)) {
357 *Progress
= Configuration
;
367 IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL
*This
,
368 IN EFI_BROWSER_ACTION Action
,
369 IN EFI_QUESTION_ID QuestionId
,
371 IN OUT EFI_IFR_TYPE_VALUE
*Value
,
372 OUT EFI_BROWSER_ACTION_REQUEST
*ActionRequest
375 DEBUG ((EFI_D_VERBOSE
, "%a: Action=0x%Lx QuestionId=%d Type=%d\n",
376 __FUNCTION__
, (UINT64
) Action
, QuestionId
, Type
));
378 if (Action
!= EFI_BROWSER_ACTION_CHANGED
) {
379 return EFI_UNSUPPORTED
;
382 switch (QuestionId
) {
383 case QUESTION_SAVE_EXIT
:
384 *ActionRequest
= EFI_BROWSER_ACTION_REQUEST_FORM_SUBMIT_EXIT
;
387 case QUESTION_DISCARD_EXIT
:
388 *ActionRequest
= EFI_BROWSER_ACTION_REQUEST_FORM_DISCARD_EXIT
;
400 Query and save all resolutions supported by the GOP.
402 @param[in] Gop The Graphics Output Protocol instance to query.
404 @param[out] NumGopModes The number of modes supported by the GOP. On output,
405 this parameter will be positive.
407 @param[out] GopModes On output, a dynamically allocated array containing
408 the resolutions returned by the GOP. The caller is
409 responsible for freeing the array after use.
411 @retval EFI_UNSUPPORTED No modes found.
412 @retval EFI_OUT_OF_RESOURCES Failed to allocate GopModes.
413 @return Error codes from Gop->QueryMode().
420 IN EFI_GRAPHICS_OUTPUT_PROTOCOL
*Gop
,
421 OUT UINTN
*NumGopModes
,
422 OUT GOP_MODE
**GopModes
428 if (Gop
->Mode
->MaxMode
== 0) {
429 return EFI_UNSUPPORTED
;
431 *NumGopModes
= Gop
->Mode
->MaxMode
;
433 *GopModes
= AllocatePool (Gop
->Mode
->MaxMode
* sizeof **GopModes
);
434 if (*GopModes
== NULL
) {
435 return EFI_OUT_OF_RESOURCES
;
438 for (ModeNumber
= 0; ModeNumber
< Gop
->Mode
->MaxMode
; ++ModeNumber
) {
439 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION
*Info
;
442 Status
= Gop
->QueryMode (Gop
, ModeNumber
, &SizeOfInfo
, &Info
);
443 if (EFI_ERROR (Status
)) {
447 (*GopModes
)[ModeNumber
].X
= Info
->HorizontalResolution
;
448 (*GopModes
)[ModeNumber
].Y
= Info
->VerticalResolution
;
455 FreePool (*GopModes
);
462 Create a set of "one-of-many" (ie. "drop down list") option IFR opcodes,
463 based on available GOP resolutions, to be placed under a "one-of-many" (ie.
464 "drop down list") opcode.
466 @param[in] PackageList The package list with the formset and form for
467 which the drop down options are produced. Option
468 names are added as new strings to PackageList.
470 @param[out] OpCodeBuffer On output, a dynamically allocated opcode buffer
471 with drop down list options corresponding to GOP
472 resolutions. The caller is responsible for freeing
473 OpCodeBuffer with HiiFreeOpCodeHandle() after use.
475 @param[in] NumGopModes Number of entries in GopModes.
477 @param[in] GopModes Array of resolutions retrieved from the GOP.
479 @retval EFI_SUCESS Opcodes have been successfully produced.
481 @return Status codes from underlying functions. PackageList may
482 have been extended with new strings. OpCodeBuffer is
488 CreateResolutionOptions (
489 IN EFI_HII_HANDLE
*PackageList
,
490 OUT VOID
**OpCodeBuffer
,
491 IN UINTN NumGopModes
,
492 IN GOP_MODE
*GopModes
499 OutputBuffer
= HiiAllocateOpCodeHandle ();
500 if (OutputBuffer
== NULL
) {
501 return EFI_OUT_OF_RESOURCES
;
504 for (ModeNumber
= 0; ModeNumber
< NumGopModes
; ++ModeNumber
) {
505 CHAR16 Desc
[MAXSIZE_RES_CUR
];
506 EFI_STRING_ID NewString
;
509 UnicodeSPrintAsciiFormat (Desc
, sizeof Desc
, "%Ldx%Ld",
510 (INT64
) GopModes
[ModeNumber
].X
, (INT64
) GopModes
[ModeNumber
].Y
);
511 NewString
= HiiSetString (PackageList
, 0 /* new string */, Desc
,
512 NULL
/* for all languages */);
513 if (NewString
== 0) {
514 Status
= EFI_OUT_OF_RESOURCES
;
515 goto FreeOutputBuffer
;
517 OpCode
= HiiCreateOneOfOptionOpCode (OutputBuffer
, NewString
,
518 0 /* Flags */, EFI_IFR_NUMERIC_SIZE_4
, ModeNumber
);
519 if (OpCode
== NULL
) {
520 Status
= EFI_OUT_OF_RESOURCES
;
521 goto FreeOutputBuffer
;
525 *OpCodeBuffer
= OutputBuffer
;
529 HiiFreeOpCodeHandle (OutputBuffer
);
536 Populate the form identified by the (PackageList, FormSetGuid, FormId)
539 The drop down list of video resolutions is generated from (NumGopModes,
542 @retval EFI_SUCESS Form successfully updated.
543 @return Status codes from underlying functions.
550 IN EFI_HII_HANDLE
*PackageList
,
551 IN EFI_GUID
*FormSetGuid
,
552 IN EFI_FORM_ID FormId
,
553 IN UINTN NumGopModes
,
554 IN GOP_MODE
*GopModes
560 EFI_IFR_GUID_LABEL
*Anchor
;
563 OpCodeBuffer2
= NULL
;
566 // 1. Allocate an empty opcode buffer.
568 OpCodeBuffer
= HiiAllocateOpCodeHandle ();
569 if (OpCodeBuffer
== NULL
) {
570 return EFI_OUT_OF_RESOURCES
;
574 // 2. Create a label opcode (which is a Tiano extension) inside the buffer.
575 // The label's number must match the "anchor" label in the form.
577 OpCode
= HiiCreateGuidOpCode (OpCodeBuffer
, &gEfiIfrTianoGuid
,
578 NULL
/* optional copy origin */, sizeof *Anchor
);
579 if (OpCode
== NULL
) {
580 Status
= EFI_OUT_OF_RESOURCES
;
581 goto FreeOpCodeBuffer
;
584 Anchor
->ExtendOpCode
= EFI_IFR_EXTEND_OP_LABEL
;
585 Anchor
->Number
= LABEL_RES_NEXT
;
588 // 3. Create the opcodes inside the buffer that are to be inserted into the
591 // 3.1. Get a list of resolutions.
593 Status
= CreateResolutionOptions (PackageList
, &OpCodeBuffer2
,
594 NumGopModes
, GopModes
);
595 if (EFI_ERROR (Status
)) {
596 goto FreeOpCodeBuffer
;
600 // 3.2. Create a one-of-many question with the above options.
602 OpCode
= HiiCreateOneOfOpCode (
603 OpCodeBuffer
, // create opcode inside this
605 QUESTION_RES_NEXT
, // ID of question,
606 FORMSTATEID_MAIN_FORM
, // identifies form state
608 (UINT16
) OFFSET_OF (MAIN_FORM_STATE
, // value of question stored
609 NextPreferredResolution
), // at this offset,
610 STRING_TOKEN (STR_RES_NEXT
), // Prompt,
611 STRING_TOKEN (STR_RES_NEXT_HELP
), // Help,
613 EFI_IFR_NUMERIC_SIZE_4
, // see sizeof
614 // NextPreferredResolution,
615 OpCodeBuffer2
, // buffer with possible
617 NULL
// DEFAULT opcodes
619 if (OpCode
== NULL
) {
620 Status
= EFI_OUT_OF_RESOURCES
;
621 goto FreeOpCodeBuffer2
;
625 // 4. Update the form with the opcode buffer.
627 Status
= HiiUpdateForm (PackageList
, FormSetGuid
, FormId
,
628 OpCodeBuffer
, // buffer with head anchor, and new contents to be
630 NULL
// buffer with tail anchor, for deleting old
635 HiiFreeOpCodeHandle (OpCodeBuffer2
);
638 HiiFreeOpCodeHandle (OpCodeBuffer
);
645 Load and execute the platform configuration.
647 @retval EFI_SUCCESS Configuration loaded and executed.
648 @return Status codes from PlatformConfigLoad().
653 ExecutePlatformConfig (
658 PLATFORM_CONFIG PlatformConfig
;
659 UINT64 OptionalElements
;
660 RETURN_STATUS PcdStatus
;
662 Status
= PlatformConfigLoad (&PlatformConfig
, &OptionalElements
);
663 if (EFI_ERROR (Status
)) {
664 DEBUG (((Status
== EFI_NOT_FOUND
) ? EFI_D_VERBOSE
: EFI_D_ERROR
,
665 "%a: failed to load platform config: %r\n", __FUNCTION__
, Status
));
669 if (OptionalElements
& PLATFORM_CONFIG_F_GRAPHICS_RESOLUTION
) {
671 // Pass the preferred resolution to GraphicsConsoleDxe via dynamic PCDs.
673 PcdStatus
= PcdSet32S (PcdVideoHorizontalResolution
,
674 PlatformConfig
.HorizontalResolution
);
675 ASSERT_RETURN_ERROR (PcdStatus
);
677 PcdStatus
= PcdSet32S (PcdVideoVerticalResolution
,
678 PlatformConfig
.VerticalResolution
);
679 ASSERT_RETURN_ERROR (PcdStatus
);
687 Notification callback for GOP interface installation.
689 @param[in] Event Event whose notification function is being invoked.
691 @param[in] Context The pointer to the notification function's context, which
692 is implementation-dependent.
703 EFI_GRAPHICS_OUTPUT_PROTOCOL
*Gop
;
705 ASSERT (Event
== mGopEvent
);
708 // Check further GOPs.
714 Status
= gBS
->LocateProtocol (&gEfiGraphicsOutputProtocolGuid
, mGopTracker
,
716 if (EFI_ERROR (Status
)) {
720 Status
= QueryGopModes (Gop
, &mNumGopModes
, &mGopModes
);
721 if (EFI_ERROR (Status
)) {
725 Status
= PopulateForm (mInstalledPackages
, &gOvmfPlatformConfigGuid
,
726 FORMID_MAIN_FORM
, mNumGopModes
, mGopModes
);
727 if (EFI_ERROR (Status
)) {
728 FreePool (mGopModes
);
736 // Success -- so uninstall this callback. Closing the event removes all
737 // pending notifications and all protocol registrations.
739 Status
= gBS
->CloseEvent (mGopEvent
);
740 ASSERT_EFI_ERROR (Status
);
747 Entry point for this driver.
749 @param[in] ImageHandle Image handle of this driver.
750 @param[in] SystemTable Pointer to SystemTable.
752 @retval EFI_SUCESS Driver has loaded successfully.
753 @retval EFI_OUT_OF_RESOURCES Failed to install HII packages.
754 @return Error codes from lower level functions.
760 IN EFI_HANDLE ImageHandle
,
761 IN EFI_SYSTEM_TABLE
*SystemTable
766 ExecutePlatformConfig ();
768 mConfigAccess
.ExtractConfig
= &ExtractConfig
;
769 mConfigAccess
.RouteConfig
= &RouteConfig
;
770 mConfigAccess
.Callback
= &Callback
;
773 // Declare ourselves suitable for HII communication.
775 Status
= gBS
->InstallMultipleProtocolInterfaces (&ImageHandle
,
776 &gEfiDevicePathProtocolGuid
, &mPkgDevicePath
,
777 &gEfiHiiConfigAccessProtocolGuid
, &mConfigAccess
,
779 if (EFI_ERROR (Status
)) {
784 // Publish the HII package list to HII Database.
786 mInstalledPackages
= HiiAddPackages (
787 &gEfiCallerIdGuid
, // PackageListGuid
788 ImageHandle
, // associated DeviceHandle
789 PlatformDxeStrings
, // 1st package
790 PlatformFormsBin
, // 2nd package
793 if (mInstalledPackages
== NULL
) {
794 Status
= EFI_OUT_OF_RESOURCES
;
795 goto UninstallProtocols
;
798 Status
= gBS
->CreateEvent (EVT_NOTIFY_SIGNAL
, TPL_CALLBACK
, &GopInstalled
,
799 NULL
/* Context */, &mGopEvent
);
800 if (EFI_ERROR (Status
)) {
804 Status
= gBS
->RegisterProtocolNotify (&gEfiGraphicsOutputProtocolGuid
,
805 mGopEvent
, &mGopTracker
);
806 if (EFI_ERROR (Status
)) {
811 // Check already installed GOPs.
813 Status
= gBS
->SignalEvent (mGopEvent
);
814 ASSERT_EFI_ERROR (Status
);
819 gBS
->CloseEvent (mGopEvent
);
822 HiiRemovePackages (mInstalledPackages
);
825 gBS
->UninstallMultipleProtocolInterfaces (ImageHandle
,
826 &gEfiDevicePathProtocolGuid
, &mPkgDevicePath
,
827 &gEfiHiiConfigAccessProtocolGuid
, &mConfigAccess
,
835 @param[in] ImageHandle Handle that identifies the image to evict.
837 @retval EFI_SUCCESS The image has been unloaded.
842 IN EFI_HANDLE ImageHandle
845 if (mGopEvent
== NULL
) {
847 // The GOP callback ran successfully and unregistered itself. Release the
848 // resources allocated there.
850 ASSERT (mGopModes
!= NULL
);
851 FreePool (mGopModes
);
854 // Otherwise we need to unregister the callback.
856 ASSERT (mGopModes
== NULL
);
857 gBS
->CloseEvent (mGopEvent
);
861 // Release resources allocated by the entry point.
863 HiiRemovePackages (mInstalledPackages
);
864 gBS
->UninstallMultipleProtocolInterfaces (ImageHandle
,
865 &gEfiDevicePathProtocolGuid
, &mPkgDevicePath
,
866 &gEfiHiiConfigAccessProtocolGuid
, &mConfigAccess
,