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 if ((Progress
== NULL
) || (Results
== NULL
)) {
236 return EFI_INVALID_PARAMETER
;
239 Status
= PlatformConfigToFormState (&MainFormState
);
240 if (EFI_ERROR (Status
)) {
246 // Answer the textual request keying off the binary form state.
248 Status
= gHiiConfigRouting
->BlockToConfig (
251 (VOID
*)&MainFormState
,
252 sizeof MainFormState
,
256 if (EFI_ERROR (Status
)) {
259 "%a: BlockToConfig(): %r, Progress=\"%s\"\n",
262 (Status
== EFI_DEVICE_ERROR
) ? NULL
: *Progress
265 DEBUG ((DEBUG_VERBOSE
, "%a: Results=\"%s\"\n", __FUNCTION__
, *Results
));
272 Interpret the binary form state and save it as persistent platform
275 @param[in] MainFormState Binary form/widget state to verify and save.
277 @retval EFI_SUCCESS Platform configuration saved.
278 @return Error codes from underlying functions.
283 FormStateToPlatformConfig (
284 IN CONST MAIN_FORM_STATE
*MainFormState
288 PLATFORM_CONFIG PlatformConfig
;
289 CONST GOP_MODE
*GopMode
;
292 // There's nothing to do with the textual CurrentPreferredResolution field.
293 // We verify and translate the selection in the drop-down list.
295 if (MainFormState
->NextPreferredResolution
>= mNumGopModes
) {
296 return EFI_INVALID_PARAMETER
;
299 GopMode
= mGopModes
+ MainFormState
->NextPreferredResolution
;
301 ZeroMem (&PlatformConfig
, sizeof PlatformConfig
);
302 PlatformConfig
.HorizontalResolution
= GopMode
->X
;
303 PlatformConfig
.VerticalResolution
= GopMode
->Y
;
305 Status
= PlatformConfigSave (&PlatformConfig
);
310 This function is called by the HII machinery when it wants the driver to
311 interpret and persist the form state.
313 See the precise documentation in the UEFI spec.
315 @param[in] This The Config Access Protocol instance.
317 @param[in] Configuration A <ConfigResp> format UCS-2 string describing the
320 @param[out] Progress A pointer into Configuration on output,
321 identifying the element where processing failed.
323 @retval EFI_SUCCESS Configuration verified, state permanent.
325 @return Status codes from underlying functions.
331 IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL
*This
,
332 IN CONST EFI_STRING Configuration
,
333 OUT EFI_STRING
*Progress
336 MAIN_FORM_STATE MainFormState
;
342 "%a: Configuration=\"%s\"\n",
347 if (Progress
== NULL
) {
348 return EFI_INVALID_PARAMETER
;
352 // the "read" step in RMW
354 Status
= PlatformConfigToFormState (&MainFormState
);
355 if (EFI_ERROR (Status
)) {
356 *Progress
= Configuration
;
361 // the "modify" step in RMW
363 // (Update the binary form state. This update may be partial, which is why in
364 // general we must pre-load the form state from the platform config.)
366 BlockSize
= sizeof MainFormState
;
367 Status
= gHiiConfigRouting
->ConfigToBlock (
370 (VOID
*)&MainFormState
,
374 if (EFI_ERROR (Status
)) {
377 "%a: ConfigToBlock(): %r, Progress=\"%s\"\n",
380 (Status
== EFI_BUFFER_TOO_SMALL
) ? NULL
: *Progress
386 // the "write" step in RMW
388 Status
= FormStateToPlatformConfig (&MainFormState
);
389 if (EFI_ERROR (Status
)) {
390 *Progress
= Configuration
;
400 IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL
*This
,
401 IN EFI_BROWSER_ACTION Action
,
402 IN EFI_QUESTION_ID QuestionId
,
404 IN OUT EFI_IFR_TYPE_VALUE
*Value
,
405 OUT EFI_BROWSER_ACTION_REQUEST
*ActionRequest
410 "%a: Action=0x%Lx QuestionId=%d Type=%d\n",
417 if (Action
!= EFI_BROWSER_ACTION_CHANGED
) {
418 return EFI_UNSUPPORTED
;
421 switch (QuestionId
) {
422 case QUESTION_SAVE_EXIT
:
423 *ActionRequest
= EFI_BROWSER_ACTION_REQUEST_FORM_SUBMIT_EXIT
;
426 case QUESTION_DISCARD_EXIT
:
427 *ActionRequest
= EFI_BROWSER_ACTION_REQUEST_FORM_DISCARD_EXIT
;
438 Query and save all resolutions supported by the GOP.
440 @param[in] Gop The Graphics Output Protocol instance to query.
442 @param[out] NumGopModes The number of modes supported by the GOP. On output,
443 this parameter will be positive.
445 @param[out] GopModes On output, a dynamically allocated array containing
446 the resolutions returned by the GOP. The caller is
447 responsible for freeing the array after use.
449 @retval EFI_UNSUPPORTED No modes found.
450 @retval EFI_OUT_OF_RESOURCES Failed to allocate GopModes.
451 @return Error codes from Gop->QueryMode().
458 IN EFI_GRAPHICS_OUTPUT_PROTOCOL
*Gop
,
459 OUT UINTN
*NumGopModes
,
460 OUT GOP_MODE
**GopModes
466 if (Gop
->Mode
->MaxMode
== 0) {
467 return EFI_UNSUPPORTED
;
470 *NumGopModes
= Gop
->Mode
->MaxMode
;
472 *GopModes
= AllocatePool (Gop
->Mode
->MaxMode
* sizeof **GopModes
);
473 if (*GopModes
== NULL
) {
474 return EFI_OUT_OF_RESOURCES
;
477 for (ModeNumber
= 0; ModeNumber
< Gop
->Mode
->MaxMode
; ++ModeNumber
) {
478 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION
*Info
;
481 Status
= Gop
->QueryMode (Gop
, ModeNumber
, &SizeOfInfo
, &Info
);
482 if (EFI_ERROR (Status
)) {
486 (*GopModes
)[ModeNumber
].X
= Info
->HorizontalResolution
;
487 (*GopModes
)[ModeNumber
].Y
= Info
->VerticalResolution
;
494 FreePool (*GopModes
);
500 Create a set of "one-of-many" (ie. "drop down list") option IFR opcodes,
501 based on available GOP resolutions, to be placed under a "one-of-many" (ie.
502 "drop down list") opcode.
504 @param[in] PackageList The package list with the formset and form for
505 which the drop down options are produced. Option
506 names are added as new strings to PackageList.
508 @param[out] OpCodeBuffer On output, a dynamically allocated opcode buffer
509 with drop down list options corresponding to GOP
510 resolutions. The caller is responsible for freeing
511 OpCodeBuffer with HiiFreeOpCodeHandle() after use.
513 @param[in] NumGopModes Number of entries in GopModes.
515 @param[in] GopModes Array of resolutions retrieved from the GOP.
517 @retval EFI_SUCESS Opcodes have been successfully produced.
519 @return Status codes from underlying functions. PackageList may
520 have been extended with new strings. OpCodeBuffer is
526 CreateResolutionOptions (
527 IN EFI_HII_HANDLE PackageList
,
528 OUT VOID
**OpCodeBuffer
,
529 IN UINTN NumGopModes
,
530 IN GOP_MODE
*GopModes
537 OutputBuffer
= HiiAllocateOpCodeHandle ();
538 if (OutputBuffer
== NULL
) {
539 return EFI_OUT_OF_RESOURCES
;
542 for (ModeNumber
= 0; ModeNumber
< NumGopModes
; ++ModeNumber
) {
543 CHAR16 Desc
[MAXSIZE_RES_CUR
];
544 EFI_STRING_ID NewString
;
547 UnicodeSPrintAsciiFormat (
551 (INT64
)GopModes
[ModeNumber
].X
,
552 (INT64
)GopModes
[ModeNumber
].Y
554 NewString
= HiiSetString (
558 NULL
/* for all languages */
560 if (NewString
== 0) {
561 Status
= EFI_OUT_OF_RESOURCES
;
562 goto FreeOutputBuffer
;
565 OpCode
= HiiCreateOneOfOptionOpCode (
569 EFI_IFR_NUMERIC_SIZE_4
,
572 if (OpCode
== NULL
) {
573 Status
= EFI_OUT_OF_RESOURCES
;
574 goto FreeOutputBuffer
;
578 *OpCodeBuffer
= OutputBuffer
;
582 HiiFreeOpCodeHandle (OutputBuffer
);
588 Populate the form identified by the (PackageList, FormSetGuid, FormId)
591 The drop down list of video resolutions is generated from (NumGopModes,
594 @retval EFI_SUCESS Form successfully updated.
595 @return Status codes from underlying functions.
602 IN EFI_HII_HANDLE PackageList
,
603 IN EFI_GUID
*FormSetGuid
,
604 IN EFI_FORM_ID FormId
,
605 IN UINTN NumGopModes
,
606 IN GOP_MODE
*GopModes
612 EFI_IFR_GUID_LABEL
*Anchor
;
615 OpCodeBuffer2
= NULL
;
618 // 1. Allocate an empty opcode buffer.
620 OpCodeBuffer
= HiiAllocateOpCodeHandle ();
621 if (OpCodeBuffer
== NULL
) {
622 return EFI_OUT_OF_RESOURCES
;
626 // 2. Create a label opcode (which is a Tiano extension) inside the buffer.
627 // The label's number must match the "anchor" label in the form.
629 OpCode
= HiiCreateGuidOpCode (
632 NULL
/* optional copy origin */,
635 if (OpCode
== NULL
) {
636 Status
= EFI_OUT_OF_RESOURCES
;
637 goto FreeOpCodeBuffer
;
641 Anchor
->ExtendOpCode
= EFI_IFR_EXTEND_OP_LABEL
;
642 Anchor
->Number
= LABEL_RES_NEXT
;
645 // 3. Create the opcodes inside the buffer that are to be inserted into the
648 // 3.1. Get a list of resolutions.
650 Status
= CreateResolutionOptions (
656 if (EFI_ERROR (Status
)) {
657 goto FreeOpCodeBuffer
;
661 // 3.2. Create a one-of-many question with the above options.
663 OpCode
= HiiCreateOneOfOpCode (
664 OpCodeBuffer
, // create opcode inside this
666 QUESTION_RES_NEXT
, // ID of question,
667 FORMSTATEID_MAIN_FORM
, // identifies form state
670 MAIN_FORM_STATE
, // value of question stored
671 NextPreferredResolution
672 ), // at this offset,
673 STRING_TOKEN (STR_RES_NEXT
), // Prompt,
674 STRING_TOKEN (STR_RES_NEXT_HELP
), // Help,
676 EFI_IFR_NUMERIC_SIZE_4
, // see sizeof
677 // NextPreferredResolution,
678 OpCodeBuffer2
, // buffer with possible
680 NULL
// DEFAULT opcodes
682 if (OpCode
== NULL
) {
683 Status
= EFI_OUT_OF_RESOURCES
;
684 goto FreeOpCodeBuffer2
;
688 // 4. Update the form with the opcode buffer.
690 Status
= HiiUpdateForm (
694 OpCodeBuffer
, // buffer with head anchor, and new contents to be
696 NULL
// buffer with tail anchor, for deleting old
701 HiiFreeOpCodeHandle (OpCodeBuffer2
);
704 HiiFreeOpCodeHandle (OpCodeBuffer
);
710 Load and execute the platform configuration.
712 @retval EFI_SUCCESS Configuration loaded and executed.
713 @return Status codes from PlatformConfigLoad().
718 ExecutePlatformConfig (
723 PLATFORM_CONFIG PlatformConfig
;
724 UINT64 OptionalElements
;
725 RETURN_STATUS PcdStatus
;
727 Status
= PlatformConfigLoad (&PlatformConfig
, &OptionalElements
);
728 if (EFI_ERROR (Status
)) {
730 (Status
== EFI_NOT_FOUND
) ? DEBUG_VERBOSE
: DEBUG_ERROR
,
731 "%a: failed to load platform config: %r\n",
738 if (OptionalElements
& PLATFORM_CONFIG_F_GRAPHICS_RESOLUTION
) {
740 // Pass the preferred resolution to GraphicsConsoleDxe via dynamic PCDs.
742 PcdStatus
= PcdSet32S (
743 PcdVideoHorizontalResolution
,
744 PlatformConfig
.HorizontalResolution
746 ASSERT_RETURN_ERROR (PcdStatus
);
748 PcdStatus
= PcdSet32S (
749 PcdVideoVerticalResolution
,
750 PlatformConfig
.VerticalResolution
752 ASSERT_RETURN_ERROR (PcdStatus
);
754 PcdStatus
= PcdSet8S (PcdVideoResolutionSource
, 1);
755 ASSERT_RETURN_ERROR (PcdStatus
);
762 Notification callback for GOP interface installation.
764 @param[in] Event Event whose notification function is being invoked.
766 @param[in] Context The pointer to the notification function's context, which
767 is implementation-dependent.
778 EFI_GRAPHICS_OUTPUT_PROTOCOL
*Gop
;
780 ASSERT (Event
== mGopEvent
);
783 // Check further GOPs.
789 Status
= gBS
->LocateProtocol (
790 &gEfiGraphicsOutputProtocolGuid
,
794 if (EFI_ERROR (Status
)) {
798 Status
= QueryGopModes (Gop
, &mNumGopModes
, &mGopModes
);
799 if (EFI_ERROR (Status
)) {
803 Status
= PopulateForm (
805 &gOvmfPlatformConfigGuid
,
810 if (EFI_ERROR (Status
)) {
811 FreePool (mGopModes
);
819 // Success -- so uninstall this callback. Closing the event removes all
820 // pending notifications and all protocol registrations.
822 Status
= gBS
->CloseEvent (mGopEvent
);
823 ASSERT_EFI_ERROR (Status
);
829 Entry point for this driver.
831 @param[in] ImageHandle Image handle of this driver.
832 @param[in] SystemTable Pointer to SystemTable.
834 @retval EFI_SUCESS Driver has loaded successfully.
835 @retval EFI_OUT_OF_RESOURCES Failed to install HII packages.
836 @return Error codes from lower level functions.
842 IN EFI_HANDLE ImageHandle
,
843 IN EFI_SYSTEM_TABLE
*SystemTable
848 ExecutePlatformConfig ();
850 mConfigAccess
.ExtractConfig
= &ExtractConfig
;
851 mConfigAccess
.RouteConfig
= &RouteConfig
;
852 mConfigAccess
.Callback
= &Callback
;
855 // Declare ourselves suitable for HII communication.
857 Status
= gBS
->InstallMultipleProtocolInterfaces (
859 &gEfiDevicePathProtocolGuid
,
861 &gEfiHiiConfigAccessProtocolGuid
,
865 if (EFI_ERROR (Status
)) {
870 // Publish the HII package list to HII Database.
872 mInstalledPackages
= HiiAddPackages (
873 &gEfiCallerIdGuid
, // PackageListGuid
874 ImageHandle
, // associated DeviceHandle
875 PlatformDxeStrings
, // 1st package
876 PlatformFormsBin
, // 2nd package
879 if (mInstalledPackages
== NULL
) {
880 Status
= EFI_OUT_OF_RESOURCES
;
881 goto UninstallProtocols
;
884 Status
= gBS
->CreateEvent (
891 if (EFI_ERROR (Status
)) {
895 Status
= gBS
->RegisterProtocolNotify (
896 &gEfiGraphicsOutputProtocolGuid
,
900 if (EFI_ERROR (Status
)) {
905 // Check already installed GOPs.
907 Status
= gBS
->SignalEvent (mGopEvent
);
908 ASSERT_EFI_ERROR (Status
);
913 gBS
->CloseEvent (mGopEvent
);
916 HiiRemovePackages (mInstalledPackages
);
919 gBS
->UninstallMultipleProtocolInterfaces (
921 &gEfiDevicePathProtocolGuid
,
923 &gEfiHiiConfigAccessProtocolGuid
,
933 @param[in] ImageHandle Handle that identifies the image to evict.
935 @retval EFI_SUCCESS The image has been unloaded.
940 IN EFI_HANDLE ImageHandle
943 if (mGopEvent
== NULL
) {
945 // The GOP callback ran successfully and unregistered itself. Release the
946 // resources allocated there.
948 ASSERT (mGopModes
!= NULL
);
949 FreePool (mGopModes
);
952 // Otherwise we need to unregister the callback.
954 ASSERT (mGopModes
== NULL
);
955 gBS
->CloseEvent (mGopEvent
);
959 // Release resources allocated by the entry point.
961 HiiRemovePackages (mInstalledPackages
);
962 gBS
->UninstallMultipleProtocolInterfaces (
964 &gEfiDevicePathProtocolGuid
,
966 &gEfiHiiConfigAccessProtocolGuid
,