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
;
123 Load the persistent platform configuration and translate it to binary form
126 If the platform configuration is missing, then the function fills in a
129 @param[out] MainFormState Binary form/widget state after translation.
131 @retval EFI_SUCCESS Form/widget state ready.
132 @return Error codes from underlying functions.
137 PlatformConfigToFormState (
138 OUT MAIN_FORM_STATE
*MainFormState
142 PLATFORM_CONFIG PlatformConfig
;
143 UINT64 OptionalElements
;
146 ZeroMem (MainFormState
, sizeof *MainFormState
);
148 Status
= PlatformConfigLoad (&PlatformConfig
, &OptionalElements
);
151 if (OptionalElements
& PLATFORM_CONFIG_F_GRAPHICS_RESOLUTION
) {
153 // Format the preferred resolution as text.
155 UnicodeSPrintAsciiFormat (
156 (CHAR16
*)MainFormState
->CurrentPreferredResolution
,
157 sizeof MainFormState
->CurrentPreferredResolution
,
159 (INT64
)PlatformConfig
.HorizontalResolution
,
160 (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
))
171 MainFormState
->NextPreferredResolution
= (UINT32
)ModeNumber
;
180 // fall through otherwise
184 UnicodeSPrintAsciiFormat (
185 (CHAR16
*)MainFormState
->CurrentPreferredResolution
,
186 sizeof MainFormState
->CurrentPreferredResolution
,
199 This function is called by the HII machinery when it fetches the form state.
201 See the precise documentation in the UEFI spec.
203 @param[in] This The Config Access Protocol instance.
205 @param[in] Request A <ConfigRequest> format UCS-2 string describing the
208 @param[out] Progress A pointer into Request on output, identifying the query
209 element where processing failed.
211 @param[out] Results A <MultiConfigAltResp> format UCS-2 string that has
212 all values filled in for the names in the Request
215 @retval EFI_SUCCESS Extraction of form state in <MultiConfigAltResp>
217 @return Status codes from underlying functions.
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
230 MAIN_FORM_STATE MainFormState
;
233 DEBUG ((DEBUG_VERBOSE
, "%a: Request=\"%s\"\n", __FUNCTION__
, Request
));
235 Status
= PlatformConfigToFormState (&MainFormState
);
236 if (EFI_ERROR (Status
)) {
242 // Answer the textual request keying off the binary form state.
244 Status
= gHiiConfigRouting
->BlockToConfig (
247 (VOID
*)&MainFormState
,
248 sizeof MainFormState
,
252 if (EFI_ERROR (Status
)) {
255 "%a: BlockToConfig(): %r, Progress=\"%s\"\n",
258 (Status
== EFI_DEVICE_ERROR
) ? NULL
: *Progress
261 DEBUG ((DEBUG_VERBOSE
, "%a: Results=\"%s\"\n", __FUNCTION__
, *Results
));
268 Interpret the binary form state and save it as persistent platform
271 @param[in] MainFormState Binary form/widget state to verify and save.
273 @retval EFI_SUCCESS Platform configuration saved.
274 @return Error codes from underlying functions.
279 FormStateToPlatformConfig (
280 IN CONST MAIN_FORM_STATE
*MainFormState
284 PLATFORM_CONFIG PlatformConfig
;
285 CONST GOP_MODE
*GopMode
;
288 // There's nothing to do with the textual CurrentPreferredResolution field.
289 // We verify and translate the selection in the drop-down list.
291 if (MainFormState
->NextPreferredResolution
>= mNumGopModes
) {
292 return EFI_INVALID_PARAMETER
;
295 GopMode
= mGopModes
+ MainFormState
->NextPreferredResolution
;
297 ZeroMem (&PlatformConfig
, sizeof PlatformConfig
);
298 PlatformConfig
.HorizontalResolution
= GopMode
->X
;
299 PlatformConfig
.VerticalResolution
= GopMode
->Y
;
301 Status
= PlatformConfigSave (&PlatformConfig
);
306 This function is called by the HII machinery when it wants the driver to
307 interpret and persist the form state.
309 See the precise documentation in the UEFI spec.
311 @param[in] This The Config Access Protocol instance.
313 @param[in] Configuration A <ConfigResp> format UCS-2 string describing the
316 @param[out] Progress A pointer into Configuration on output,
317 identifying the element where processing failed.
319 @retval EFI_SUCCESS Configuration verified, state permanent.
321 @return Status codes from underlying functions.
327 IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL
*This
,
328 IN CONST EFI_STRING Configuration
,
329 OUT EFI_STRING
*Progress
332 MAIN_FORM_STATE MainFormState
;
338 "%a: Configuration=\"%s\"\n",
344 // the "read" step in RMW
346 Status
= PlatformConfigToFormState (&MainFormState
);
347 if (EFI_ERROR (Status
)) {
348 *Progress
= Configuration
;
353 // the "modify" step in RMW
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.)
358 BlockSize
= sizeof MainFormState
;
359 Status
= gHiiConfigRouting
->ConfigToBlock (
362 (VOID
*)&MainFormState
,
366 if (EFI_ERROR (Status
)) {
369 "%a: ConfigToBlock(): %r, Progress=\"%s\"\n",
372 (Status
== EFI_BUFFER_TOO_SMALL
) ? NULL
: *Progress
378 // the "write" step in RMW
380 Status
= FormStateToPlatformConfig (&MainFormState
);
381 if (EFI_ERROR (Status
)) {
382 *Progress
= Configuration
;
392 IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL
*This
,
393 IN EFI_BROWSER_ACTION Action
,
394 IN EFI_QUESTION_ID QuestionId
,
396 IN OUT EFI_IFR_TYPE_VALUE
*Value
,
397 OUT EFI_BROWSER_ACTION_REQUEST
*ActionRequest
402 "%a: Action=0x%Lx QuestionId=%d Type=%d\n",
409 if (Action
!= EFI_BROWSER_ACTION_CHANGED
) {
410 return EFI_UNSUPPORTED
;
413 switch (QuestionId
) {
414 case QUESTION_SAVE_EXIT
:
415 *ActionRequest
= EFI_BROWSER_ACTION_REQUEST_FORM_SUBMIT_EXIT
;
418 case QUESTION_DISCARD_EXIT
:
419 *ActionRequest
= EFI_BROWSER_ACTION_REQUEST_FORM_DISCARD_EXIT
;
430 Query and save all resolutions supported by the GOP.
432 @param[in] Gop The Graphics Output Protocol instance to query.
434 @param[out] NumGopModes The number of modes supported by the GOP. On output,
435 this parameter will be positive.
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.
441 @retval EFI_UNSUPPORTED No modes found.
442 @retval EFI_OUT_OF_RESOURCES Failed to allocate GopModes.
443 @return Error codes from Gop->QueryMode().
450 IN EFI_GRAPHICS_OUTPUT_PROTOCOL
*Gop
,
451 OUT UINTN
*NumGopModes
,
452 OUT GOP_MODE
**GopModes
458 if (Gop
->Mode
->MaxMode
== 0) {
459 return EFI_UNSUPPORTED
;
462 *NumGopModes
= Gop
->Mode
->MaxMode
;
464 *GopModes
= AllocatePool (Gop
->Mode
->MaxMode
* sizeof **GopModes
);
465 if (*GopModes
== NULL
) {
466 return EFI_OUT_OF_RESOURCES
;
469 for (ModeNumber
= 0; ModeNumber
< Gop
->Mode
->MaxMode
; ++ModeNumber
) {
470 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION
*Info
;
473 Status
= Gop
->QueryMode (Gop
, ModeNumber
, &SizeOfInfo
, &Info
);
474 if (EFI_ERROR (Status
)) {
478 (*GopModes
)[ModeNumber
].X
= Info
->HorizontalResolution
;
479 (*GopModes
)[ModeNumber
].Y
= Info
->VerticalResolution
;
486 FreePool (*GopModes
);
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.
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.
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.
505 @param[in] NumGopModes Number of entries in GopModes.
507 @param[in] GopModes Array of resolutions retrieved from the GOP.
509 @retval EFI_SUCESS Opcodes have been successfully produced.
511 @return Status codes from underlying functions. PackageList may
512 have been extended with new strings. OpCodeBuffer is
518 CreateResolutionOptions (
519 IN EFI_HII_HANDLE PackageList
,
520 OUT VOID
**OpCodeBuffer
,
521 IN UINTN NumGopModes
,
522 IN GOP_MODE
*GopModes
529 OutputBuffer
= HiiAllocateOpCodeHandle ();
530 if (OutputBuffer
== NULL
) {
531 return EFI_OUT_OF_RESOURCES
;
534 for (ModeNumber
= 0; ModeNumber
< NumGopModes
; ++ModeNumber
) {
535 CHAR16 Desc
[MAXSIZE_RES_CUR
];
536 EFI_STRING_ID NewString
;
539 UnicodeSPrintAsciiFormat (
543 (INT64
)GopModes
[ModeNumber
].X
,
544 (INT64
)GopModes
[ModeNumber
].Y
546 NewString
= HiiSetString (
550 NULL
/* for all languages */
552 if (NewString
== 0) {
553 Status
= EFI_OUT_OF_RESOURCES
;
554 goto FreeOutputBuffer
;
557 OpCode
= HiiCreateOneOfOptionOpCode (
561 EFI_IFR_NUMERIC_SIZE_4
,
564 if (OpCode
== NULL
) {
565 Status
= EFI_OUT_OF_RESOURCES
;
566 goto FreeOutputBuffer
;
570 *OpCodeBuffer
= OutputBuffer
;
574 HiiFreeOpCodeHandle (OutputBuffer
);
580 Populate the form identified by the (PackageList, FormSetGuid, FormId)
583 The drop down list of video resolutions is generated from (NumGopModes,
586 @retval EFI_SUCESS Form successfully updated.
587 @return Status codes from underlying functions.
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
604 EFI_IFR_GUID_LABEL
*Anchor
;
607 OpCodeBuffer2
= NULL
;
610 // 1. Allocate an empty opcode buffer.
612 OpCodeBuffer
= HiiAllocateOpCodeHandle ();
613 if (OpCodeBuffer
== NULL
) {
614 return EFI_OUT_OF_RESOURCES
;
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.
621 OpCode
= HiiCreateGuidOpCode (
624 NULL
/* optional copy origin */,
627 if (OpCode
== NULL
) {
628 Status
= EFI_OUT_OF_RESOURCES
;
629 goto FreeOpCodeBuffer
;
633 Anchor
->ExtendOpCode
= EFI_IFR_EXTEND_OP_LABEL
;
634 Anchor
->Number
= LABEL_RES_NEXT
;
637 // 3. Create the opcodes inside the buffer that are to be inserted into the
640 // 3.1. Get a list of resolutions.
642 Status
= CreateResolutionOptions (
648 if (EFI_ERROR (Status
)) {
649 goto FreeOpCodeBuffer
;
653 // 3.2. Create a one-of-many question with the above options.
655 OpCode
= HiiCreateOneOfOpCode (
656 OpCodeBuffer
, // create opcode inside this
658 QUESTION_RES_NEXT
, // ID of question,
659 FORMSTATEID_MAIN_FORM
, // identifies form state
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,
668 EFI_IFR_NUMERIC_SIZE_4
, // see sizeof
669 // NextPreferredResolution,
670 OpCodeBuffer2
, // buffer with possible
672 NULL
// DEFAULT opcodes
674 if (OpCode
== NULL
) {
675 Status
= EFI_OUT_OF_RESOURCES
;
676 goto FreeOpCodeBuffer2
;
680 // 4. Update the form with the opcode buffer.
682 Status
= HiiUpdateForm (
686 OpCodeBuffer
, // buffer with head anchor, and new contents to be
688 NULL
// buffer with tail anchor, for deleting old
693 HiiFreeOpCodeHandle (OpCodeBuffer2
);
696 HiiFreeOpCodeHandle (OpCodeBuffer
);
702 Load and execute the platform configuration.
704 @retval EFI_SUCCESS Configuration loaded and executed.
705 @return Status codes from PlatformConfigLoad().
710 ExecutePlatformConfig (
715 PLATFORM_CONFIG PlatformConfig
;
716 UINT64 OptionalElements
;
717 RETURN_STATUS PcdStatus
;
719 Status
= PlatformConfigLoad (&PlatformConfig
, &OptionalElements
);
720 if (EFI_ERROR (Status
)) {
722 (Status
== EFI_NOT_FOUND
) ? DEBUG_VERBOSE
: DEBUG_ERROR
,
723 "%a: failed to load platform config: %r\n",
730 if (OptionalElements
& PLATFORM_CONFIG_F_GRAPHICS_RESOLUTION
) {
732 // Pass the preferred resolution to GraphicsConsoleDxe via dynamic PCDs.
734 PcdStatus
= PcdSet32S (
735 PcdVideoHorizontalResolution
,
736 PlatformConfig
.HorizontalResolution
738 ASSERT_RETURN_ERROR (PcdStatus
);
740 PcdStatus
= PcdSet32S (
741 PcdVideoVerticalResolution
,
742 PlatformConfig
.VerticalResolution
744 ASSERT_RETURN_ERROR (PcdStatus
);
751 Notification callback for GOP interface installation.
753 @param[in] Event Event whose notification function is being invoked.
755 @param[in] Context The pointer to the notification function's context, which
756 is implementation-dependent.
767 EFI_GRAPHICS_OUTPUT_PROTOCOL
*Gop
;
769 ASSERT (Event
== mGopEvent
);
772 // Check further GOPs.
778 Status
= gBS
->LocateProtocol (
779 &gEfiGraphicsOutputProtocolGuid
,
783 if (EFI_ERROR (Status
)) {
787 Status
= QueryGopModes (Gop
, &mNumGopModes
, &mGopModes
);
788 if (EFI_ERROR (Status
)) {
792 Status
= PopulateForm (
794 &gOvmfPlatformConfigGuid
,
799 if (EFI_ERROR (Status
)) {
800 FreePool (mGopModes
);
808 // Success -- so uninstall this callback. Closing the event removes all
809 // pending notifications and all protocol registrations.
811 Status
= gBS
->CloseEvent (mGopEvent
);
812 ASSERT_EFI_ERROR (Status
);
818 Entry point for this driver.
820 @param[in] ImageHandle Image handle of this driver.
821 @param[in] SystemTable Pointer to SystemTable.
823 @retval EFI_SUCESS Driver has loaded successfully.
824 @retval EFI_OUT_OF_RESOURCES Failed to install HII packages.
825 @return Error codes from lower level functions.
831 IN EFI_HANDLE ImageHandle
,
832 IN EFI_SYSTEM_TABLE
*SystemTable
837 ExecutePlatformConfig ();
839 mConfigAccess
.ExtractConfig
= &ExtractConfig
;
840 mConfigAccess
.RouteConfig
= &RouteConfig
;
841 mConfigAccess
.Callback
= &Callback
;
844 // Declare ourselves suitable for HII communication.
846 Status
= gBS
->InstallMultipleProtocolInterfaces (
848 &gEfiDevicePathProtocolGuid
,
850 &gEfiHiiConfigAccessProtocolGuid
,
854 if (EFI_ERROR (Status
)) {
859 // Publish the HII package list to HII Database.
861 mInstalledPackages
= HiiAddPackages (
862 &gEfiCallerIdGuid
, // PackageListGuid
863 ImageHandle
, // associated DeviceHandle
864 PlatformDxeStrings
, // 1st package
865 PlatformFormsBin
, // 2nd package
868 if (mInstalledPackages
== NULL
) {
869 Status
= EFI_OUT_OF_RESOURCES
;
870 goto UninstallProtocols
;
873 Status
= gBS
->CreateEvent (
880 if (EFI_ERROR (Status
)) {
884 Status
= gBS
->RegisterProtocolNotify (
885 &gEfiGraphicsOutputProtocolGuid
,
889 if (EFI_ERROR (Status
)) {
894 // Check already installed GOPs.
896 Status
= gBS
->SignalEvent (mGopEvent
);
897 ASSERT_EFI_ERROR (Status
);
902 gBS
->CloseEvent (mGopEvent
);
905 HiiRemovePackages (mInstalledPackages
);
908 gBS
->UninstallMultipleProtocolInterfaces (
910 &gEfiDevicePathProtocolGuid
,
912 &gEfiHiiConfigAccessProtocolGuid
,
922 @param[in] ImageHandle Handle that identifies the image to evict.
924 @retval EFI_SUCCESS The image has been unloaded.
929 IN EFI_HANDLE ImageHandle
932 if (mGopEvent
== NULL
) {
934 // The GOP callback ran successfully and unregistered itself. Release the
935 // resources allocated there.
937 ASSERT (mGopModes
!= NULL
);
938 FreePool (mGopModes
);
941 // Otherwise we need to unregister the callback.
943 ASSERT (mGopModes
== NULL
);
944 gBS
->CloseEvent (mGopEvent
);
948 // Release resources allocated by the entry point.
950 HiiRemovePackages (mInstalledPackages
);
951 gBS
->UninstallMultipleProtocolInterfaces (
953 &gEfiDevicePathProtocolGuid
,
955 &gEfiHiiConfigAccessProtocolGuid
,