mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-12-01 12:53:27 +01:00
1083 lines
29 KiB
C
1083 lines
29 KiB
C
/** @file
|
|
This driver effectuates OVMF's platform configuration settings and exposes
|
|
them via HII.
|
|
|
|
Copyright (C) 2014, Red Hat, Inc.
|
|
Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>
|
|
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
**/
|
|
|
|
#include <Library/BaseLib.h>
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <Library/DevicePathLib.h>
|
|
#include <Library/HiiLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Library/PrintLib.h>
|
|
#include <Library/UefiBootServicesTableLib.h>
|
|
#include <Library/UefiHiiServicesLib.h>
|
|
#include <Protocol/DevicePath.h>
|
|
#include <Protocol/GraphicsOutput.h>
|
|
#include <Protocol/HiiConfigAccess.h>
|
|
#include <Guid/MdeModuleHii.h>
|
|
#include <Guid/OvmfPlatformConfig.h>
|
|
|
|
#include "Platform.h"
|
|
#include "PlatformConfig.h"
|
|
|
|
//
|
|
// The HiiAddPackages() library function requires that any controller (or
|
|
// image) handle, to be associated with the HII packages under installation, be
|
|
// "decorated" with a device path. The tradition seems to be a vendor device
|
|
// path.
|
|
//
|
|
// We'd like to associate our HII packages with the driver's image handle. The
|
|
// first idea is to use the driver image's device path. Unfortunately, loaded
|
|
// images only come with an EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL (not the
|
|
// usual EFI_DEVICE_PATH_PROTOCOL), ie. a different GUID. In addition, even the
|
|
// EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL interface may be NULL, if the image
|
|
// has been loaded from an "unnamed" memory source buffer.
|
|
//
|
|
// Hence let's just stick with the tradition -- use a dedicated vendor device
|
|
// path, with the driver's FILE_GUID.
|
|
//
|
|
#pragma pack(1)
|
|
typedef struct {
|
|
VENDOR_DEVICE_PATH VendorDevicePath;
|
|
EFI_DEVICE_PATH_PROTOCOL End;
|
|
} PKG_DEVICE_PATH;
|
|
#pragma pack()
|
|
|
|
STATIC PKG_DEVICE_PATH mPkgDevicePath = {
|
|
{
|
|
{
|
|
HARDWARE_DEVICE_PATH,
|
|
HW_VENDOR_DP,
|
|
{
|
|
(UINT8)(sizeof (VENDOR_DEVICE_PATH)),
|
|
(UINT8)(sizeof (VENDOR_DEVICE_PATH) >> 8)
|
|
}
|
|
},
|
|
EFI_CALLER_ID_GUID
|
|
},
|
|
{
|
|
END_DEVICE_PATH_TYPE,
|
|
END_ENTIRE_DEVICE_PATH_SUBTYPE,
|
|
{
|
|
(UINT8)(END_DEVICE_PATH_LENGTH),
|
|
(UINT8)(END_DEVICE_PATH_LENGTH >> 8)
|
|
}
|
|
}
|
|
};
|
|
|
|
//
|
|
// The configuration interface between the HII engine (form display etc) and
|
|
// this driver.
|
|
//
|
|
STATIC EFI_HII_CONFIG_ACCESS_PROTOCOL mConfigAccess;
|
|
|
|
//
|
|
// The handle representing our list of packages after installation.
|
|
//
|
|
STATIC EFI_HII_HANDLE mInstalledPackages;
|
|
|
|
//
|
|
// The arrays below constitute our HII package list. They are auto-generated by
|
|
// the VFR compiler and linked into the driver image during the build.
|
|
//
|
|
// - The strings package receives its C identifier from the driver's BASE_NAME,
|
|
// plus "Strings".
|
|
//
|
|
// - The forms package receives its C identifier from the VFR file's basename,
|
|
// plus "Bin".
|
|
//
|
|
//
|
|
extern UINT8 PlatformDxeStrings[];
|
|
extern UINT8 PlatformFormsBin[];
|
|
|
|
//
|
|
// We want to be notified about GOP installations until we find one GOP
|
|
// interface that lets us populate the form.
|
|
//
|
|
STATIC EFI_EVENT mGopEvent;
|
|
|
|
//
|
|
// The registration record underneath this pointer allows us to iterate through
|
|
// the GOP instances one by one.
|
|
//
|
|
STATIC VOID *mGopTracker;
|
|
|
|
//
|
|
// The driver image handle, used to obtain the device path for <ConfigHdr>.
|
|
//
|
|
STATIC EFI_HANDLE mImageHandle;
|
|
|
|
//
|
|
// Cache the resolutions we get from the GOP.
|
|
//
|
|
typedef struct {
|
|
UINT32 X;
|
|
UINT32 Y;
|
|
} GOP_MODE;
|
|
|
|
STATIC UINTN mNumGopModes;
|
|
STATIC GOP_MODE *mGopModes;
|
|
|
|
/**
|
|
Load the persistent platform configuration and translate it to binary form
|
|
state.
|
|
|
|
If the platform configuration is missing, then the function fills in a
|
|
default state.
|
|
|
|
@param[out] MainFormState Binary form/widget state after translation.
|
|
|
|
@retval EFI_SUCCESS Form/widget state ready.
|
|
@return Error codes from underlying functions.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
PlatformConfigToFormState (
|
|
OUT MAIN_FORM_STATE *MainFormState
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
PLATFORM_CONFIG PlatformConfig;
|
|
UINT64 OptionalElements;
|
|
UINTN ModeNumber;
|
|
|
|
ZeroMem (MainFormState, sizeof *MainFormState);
|
|
|
|
Status = PlatformConfigLoad (&PlatformConfig, &OptionalElements);
|
|
switch (Status) {
|
|
case EFI_SUCCESS:
|
|
if (OptionalElements & PLATFORM_CONFIG_F_GRAPHICS_RESOLUTION) {
|
|
//
|
|
// Format the preferred resolution as text.
|
|
//
|
|
UnicodeSPrintAsciiFormat (
|
|
(CHAR16 *)MainFormState->CurrentPreferredResolution,
|
|
sizeof MainFormState->CurrentPreferredResolution,
|
|
"%Ldx%Ld",
|
|
(INT64)PlatformConfig.HorizontalResolution,
|
|
(INT64)PlatformConfig.VerticalResolution
|
|
);
|
|
|
|
//
|
|
// Try to locate it in the drop-down list too. This may not succeed, but
|
|
// that's fine.
|
|
//
|
|
for (ModeNumber = 0; ModeNumber < mNumGopModes; ++ModeNumber) {
|
|
if ((mGopModes[ModeNumber].X == PlatformConfig.HorizontalResolution) &&
|
|
(mGopModes[ModeNumber].Y == PlatformConfig.VerticalResolution))
|
|
{
|
|
MainFormState->NextPreferredResolution = (UINT32)ModeNumber;
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// fall through otherwise
|
|
//
|
|
|
|
case EFI_NOT_FOUND:
|
|
UnicodeSPrintAsciiFormat (
|
|
(CHAR16 *)MainFormState->CurrentPreferredResolution,
|
|
sizeof MainFormState->CurrentPreferredResolution,
|
|
"Unset"
|
|
);
|
|
break;
|
|
|
|
default:
|
|
return Status;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
This function is called by the HII machinery when it fetches the form state.
|
|
|
|
See the precise documentation in the UEFI spec.
|
|
|
|
@param[in] This The Config Access Protocol instance.
|
|
|
|
@param[in] Request A <ConfigRequest> format UCS-2 string describing the
|
|
query.
|
|
|
|
@param[out] Progress A pointer into Request on output, identifying the query
|
|
element where processing failed.
|
|
|
|
@param[out] Results A <MultiConfigAltResp> format UCS-2 string that has
|
|
all values filled in for the names in the Request
|
|
string.
|
|
|
|
@retval EFI_SUCCESS Extraction of form state in <MultiConfigAltResp>
|
|
encoding successful.
|
|
@return Status codes from underlying functions.
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
ExtractConfig (
|
|
IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
|
|
IN CONST EFI_STRING Request,
|
|
OUT EFI_STRING *Progress,
|
|
OUT EFI_STRING *Results
|
|
)
|
|
{
|
|
MAIN_FORM_STATE MainFormState;
|
|
EFI_STATUS Status;
|
|
EFI_STRING ConfigRequestHdr;
|
|
EFI_STRING ConfigRequest;
|
|
UINTN Size;
|
|
BOOLEAN AllocatedRequest;
|
|
|
|
DEBUG ((DEBUG_VERBOSE, "%a: Request=\"%s\"\n", __func__, Request));
|
|
|
|
if ((Progress == NULL) || (Results == NULL)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
ConfigRequestHdr = NULL;
|
|
ConfigRequest = NULL;
|
|
Size = 0;
|
|
AllocatedRequest = FALSE;
|
|
|
|
//
|
|
// Check if <ConfigHdr> matches the GUID and name
|
|
//
|
|
*Progress = Request;
|
|
if ((Request != NULL) &&
|
|
!HiiIsConfigHdrMatch (
|
|
Request,
|
|
&gOvmfPlatformConfigGuid,
|
|
mHiiFormName
|
|
)
|
|
)
|
|
{
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
Status = PlatformConfigToFormState (&MainFormState);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
if ((Request == NULL) || (StrStr (Request, L"OFFSET") == NULL)) {
|
|
//
|
|
// Request has no <RequestElement>, so construct full request string.
|
|
// Allocate and fill a buffer large enough to hold <ConfigHdr>
|
|
// followed by "&OFFSET=0&WIDTH=WWWWWWWWWWWWWWWW" followed by a
|
|
// null terminator.
|
|
//
|
|
ConfigRequestHdr = HiiConstructConfigHdr (
|
|
&gOvmfPlatformConfigGuid,
|
|
mHiiFormName,
|
|
mImageHandle
|
|
);
|
|
if (ConfigRequestHdr == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
Size = (StrLen (ConfigRequestHdr) + 32 + 1) * sizeof (CHAR16);
|
|
ConfigRequest = AllocateZeroPool (Size);
|
|
AllocatedRequest = TRUE;
|
|
if (ConfigRequest == NULL) {
|
|
FreePool (ConfigRequestHdr);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
UnicodeSPrint (
|
|
ConfigRequest,
|
|
Size,
|
|
L"%s&OFFSET=0&WIDTH=%016LX",
|
|
ConfigRequestHdr,
|
|
sizeof MainFormState
|
|
);
|
|
FreePool (ConfigRequestHdr);
|
|
} else {
|
|
ConfigRequest = Request;
|
|
}
|
|
|
|
//
|
|
// Answer the textual request keying off the binary form state.
|
|
//
|
|
Status = gHiiConfigRouting->BlockToConfig (
|
|
gHiiConfigRouting,
|
|
ConfigRequest,
|
|
(VOID *)&MainFormState,
|
|
sizeof MainFormState,
|
|
Results,
|
|
Progress
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((
|
|
DEBUG_ERROR,
|
|
"%a: BlockToConfig(): %r, Progress=\"%s\"\n",
|
|
__func__,
|
|
Status,
|
|
(Status == EFI_DEVICE_ERROR) ? NULL : *Progress
|
|
));
|
|
} else {
|
|
DEBUG ((DEBUG_VERBOSE, "%a: Results=\"%s\"\n", __func__, *Results));
|
|
}
|
|
|
|
//
|
|
// If we used a newly allocated ConfigRequest, update Progress to point to
|
|
// original Request instead of ConfigRequest.
|
|
//
|
|
if (Request == NULL) {
|
|
*Progress = NULL;
|
|
} else if (StrStr (Request, L"OFFSET") == NULL) {
|
|
if (EFI_ERROR (Status)) {
|
|
//
|
|
// Since we constructed ConfigRequest, failure can only occur if there
|
|
// is not enough memory. In this case, we point Progress to the first
|
|
// character of Request.
|
|
//
|
|
*Progress = Request;
|
|
} else {
|
|
//
|
|
// In case of success, we point Progress to the null terminator of
|
|
// Request.
|
|
//
|
|
*Progress = Request + StrLen (Request);
|
|
}
|
|
}
|
|
|
|
if (AllocatedRequest) {
|
|
FreePool (ConfigRequest);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Interpret the binary form state and save it as persistent platform
|
|
configuration.
|
|
|
|
@param[in] MainFormState Binary form/widget state to verify and save.
|
|
|
|
@retval EFI_SUCCESS Platform configuration saved.
|
|
@return Error codes from underlying functions.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
FormStateToPlatformConfig (
|
|
IN CONST MAIN_FORM_STATE *MainFormState
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
PLATFORM_CONFIG PlatformConfig;
|
|
CONST GOP_MODE *GopMode;
|
|
|
|
//
|
|
// There's nothing to do with the textual CurrentPreferredResolution field.
|
|
// We verify and translate the selection in the drop-down list.
|
|
//
|
|
if (MainFormState->NextPreferredResolution >= mNumGopModes) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
GopMode = mGopModes + MainFormState->NextPreferredResolution;
|
|
|
|
ZeroMem (&PlatformConfig, sizeof PlatformConfig);
|
|
PlatformConfig.HorizontalResolution = GopMode->X;
|
|
PlatformConfig.VerticalResolution = GopMode->Y;
|
|
|
|
Status = PlatformConfigSave (&PlatformConfig);
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
This function is called by the HII machinery when it wants the driver to
|
|
interpret and persist the form state.
|
|
|
|
See the precise documentation in the UEFI spec.
|
|
|
|
@param[in] This The Config Access Protocol instance.
|
|
|
|
@param[in] Configuration A <ConfigResp> format UCS-2 string describing the
|
|
form state.
|
|
|
|
@param[out] Progress A pointer into Configuration on output,
|
|
identifying the element where processing failed.
|
|
|
|
@retval EFI_SUCCESS Configuration verified, state permanent.
|
|
|
|
@return Status codes from underlying functions.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
RouteConfig (
|
|
IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
|
|
IN CONST EFI_STRING Configuration,
|
|
OUT EFI_STRING *Progress
|
|
)
|
|
{
|
|
MAIN_FORM_STATE MainFormState;
|
|
UINTN BlockSize;
|
|
EFI_STATUS Status;
|
|
|
|
DEBUG ((
|
|
DEBUG_VERBOSE,
|
|
"%a: Configuration=\"%s\"\n",
|
|
__func__,
|
|
Configuration
|
|
));
|
|
|
|
if ((Progress == NULL) || (Configuration == NULL)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Check if <ConfigHdr> matches the GUID and name
|
|
//
|
|
*Progress = Configuration;
|
|
if ((Configuration != NULL) &&
|
|
!HiiIsConfigHdrMatch (
|
|
Configuration,
|
|
&gOvmfPlatformConfigGuid,
|
|
mHiiFormName
|
|
)
|
|
)
|
|
{
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
//
|
|
// the "read" step in RMW
|
|
//
|
|
Status = PlatformConfigToFormState (&MainFormState);
|
|
if (EFI_ERROR (Status)) {
|
|
*Progress = Configuration;
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// the "modify" step in RMW
|
|
//
|
|
// (Update the binary form state. This update may be partial, which is why in
|
|
// general we must pre-load the form state from the platform config.)
|
|
//
|
|
BlockSize = sizeof MainFormState;
|
|
Status = gHiiConfigRouting->ConfigToBlock (
|
|
gHiiConfigRouting,
|
|
Configuration,
|
|
(VOID *)&MainFormState,
|
|
&BlockSize,
|
|
Progress
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((
|
|
DEBUG_ERROR,
|
|
"%a: ConfigToBlock(): %r, Progress=\"%s\"\n",
|
|
__func__,
|
|
Status,
|
|
(Status == EFI_BUFFER_TOO_SMALL) ? NULL : *Progress
|
|
));
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// the "write" step in RMW
|
|
//
|
|
Status = FormStateToPlatformConfig (&MainFormState);
|
|
if (EFI_ERROR (Status)) {
|
|
*Progress = Configuration;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
Callback (
|
|
IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
|
|
IN EFI_BROWSER_ACTION Action,
|
|
IN EFI_QUESTION_ID QuestionId,
|
|
IN UINT8 Type,
|
|
IN OUT EFI_IFR_TYPE_VALUE *Value,
|
|
OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest
|
|
)
|
|
{
|
|
DEBUG ((
|
|
DEBUG_VERBOSE,
|
|
"%a: Action=0x%Lx QuestionId=%d Type=%d\n",
|
|
__func__,
|
|
(UINT64)Action,
|
|
QuestionId,
|
|
Type
|
|
));
|
|
|
|
if (Action != EFI_BROWSER_ACTION_CHANGED) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
switch (QuestionId) {
|
|
case QUESTION_SAVE_EXIT:
|
|
*ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_SUBMIT_EXIT;
|
|
break;
|
|
|
|
case QUESTION_DISCARD_EXIT:
|
|
*ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_DISCARD_EXIT;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Query and save all resolutions supported by the GOP.
|
|
|
|
@param[in] Gop The Graphics Output Protocol instance to query.
|
|
|
|
@param[out] NumGopModes The number of modes supported by the GOP. On output,
|
|
this parameter will be positive.
|
|
|
|
@param[out] GopModes On output, a dynamically allocated array containing
|
|
the resolutions returned by the GOP. The caller is
|
|
responsible for freeing the array after use.
|
|
|
|
@retval EFI_UNSUPPORTED No modes found.
|
|
@retval EFI_OUT_OF_RESOURCES Failed to allocate GopModes.
|
|
@return Error codes from Gop->QueryMode().
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
QueryGopModes (
|
|
IN EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop,
|
|
OUT UINTN *NumGopModes,
|
|
OUT GOP_MODE **GopModes
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT32 ModeNumber;
|
|
|
|
if (Gop->Mode->MaxMode == 0) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
*NumGopModes = Gop->Mode->MaxMode;
|
|
|
|
*GopModes = AllocatePool (Gop->Mode->MaxMode * sizeof **GopModes);
|
|
if (*GopModes == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
for (ModeNumber = 0; ModeNumber < Gop->Mode->MaxMode; ++ModeNumber) {
|
|
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info;
|
|
UINTN SizeOfInfo;
|
|
|
|
Status = Gop->QueryMode (Gop, ModeNumber, &SizeOfInfo, &Info);
|
|
if (EFI_ERROR (Status)) {
|
|
goto FreeGopModes;
|
|
}
|
|
|
|
(*GopModes)[ModeNumber].X = Info->HorizontalResolution;
|
|
(*GopModes)[ModeNumber].Y = Info->VerticalResolution;
|
|
FreePool (Info);
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
FreeGopModes:
|
|
FreePool (*GopModes);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Create a set of "one-of-many" (ie. "drop down list") option IFR opcodes,
|
|
based on available GOP resolutions, to be placed under a "one-of-many" (ie.
|
|
"drop down list") opcode.
|
|
|
|
@param[in] PackageList The package list with the formset and form for
|
|
which the drop down options are produced. Option
|
|
names are added as new strings to PackageList.
|
|
|
|
@param[out] OpCodeBuffer On output, a dynamically allocated opcode buffer
|
|
with drop down list options corresponding to GOP
|
|
resolutions. The caller is responsible for freeing
|
|
OpCodeBuffer with HiiFreeOpCodeHandle() after use.
|
|
|
|
@param[in] NumGopModes Number of entries in GopModes.
|
|
|
|
@param[in] GopModes Array of resolutions retrieved from the GOP.
|
|
|
|
@retval EFI_SUCESS Opcodes have been successfully produced.
|
|
|
|
@return Status codes from underlying functions. PackageList may
|
|
have been extended with new strings. OpCodeBuffer is
|
|
unchanged.
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
CreateResolutionOptions (
|
|
IN EFI_HII_HANDLE PackageList,
|
|
OUT VOID **OpCodeBuffer,
|
|
IN UINTN NumGopModes,
|
|
IN GOP_MODE *GopModes
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
VOID *OutputBuffer;
|
|
UINTN ModeNumber;
|
|
|
|
OutputBuffer = HiiAllocateOpCodeHandle ();
|
|
if (OutputBuffer == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
for (ModeNumber = 0; ModeNumber < NumGopModes; ++ModeNumber) {
|
|
CHAR16 Desc[MAXSIZE_RES_CUR];
|
|
EFI_STRING_ID NewString;
|
|
VOID *OpCode;
|
|
|
|
UnicodeSPrintAsciiFormat (
|
|
Desc,
|
|
sizeof Desc,
|
|
"%Ldx%Ld",
|
|
(INT64)GopModes[ModeNumber].X,
|
|
(INT64)GopModes[ModeNumber].Y
|
|
);
|
|
NewString = HiiSetString (
|
|
PackageList,
|
|
0 /* new string */,
|
|
Desc,
|
|
NULL /* for all languages */
|
|
);
|
|
if (NewString == 0) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto FreeOutputBuffer;
|
|
}
|
|
|
|
OpCode = HiiCreateOneOfOptionOpCode (
|
|
OutputBuffer,
|
|
NewString,
|
|
0 /* Flags */,
|
|
EFI_IFR_NUMERIC_SIZE_4,
|
|
ModeNumber
|
|
);
|
|
if (OpCode == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto FreeOutputBuffer;
|
|
}
|
|
}
|
|
|
|
*OpCodeBuffer = OutputBuffer;
|
|
return EFI_SUCCESS;
|
|
|
|
FreeOutputBuffer:
|
|
HiiFreeOpCodeHandle (OutputBuffer);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Populate the form identified by the (PackageList, FormSetGuid, FormId)
|
|
triplet.
|
|
|
|
The drop down list of video resolutions is generated from (NumGopModes,
|
|
GopModes).
|
|
|
|
@retval EFI_SUCESS Form successfully updated.
|
|
@return Status codes from underlying functions.
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
PopulateForm (
|
|
IN EFI_HII_HANDLE PackageList,
|
|
IN EFI_GUID *FormSetGuid,
|
|
IN EFI_FORM_ID FormId,
|
|
IN UINTN NumGopModes,
|
|
IN GOP_MODE *GopModes
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
VOID *OpCodeBuffer;
|
|
VOID *OpCode;
|
|
EFI_IFR_GUID_LABEL *Anchor;
|
|
VOID *OpCodeBuffer2;
|
|
|
|
OpCodeBuffer2 = NULL;
|
|
|
|
//
|
|
// 1. Allocate an empty opcode buffer.
|
|
//
|
|
OpCodeBuffer = HiiAllocateOpCodeHandle ();
|
|
if (OpCodeBuffer == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// 2. Create a label opcode (which is a Tiano extension) inside the buffer.
|
|
// The label's number must match the "anchor" label in the form.
|
|
//
|
|
OpCode = HiiCreateGuidOpCode (
|
|
OpCodeBuffer,
|
|
&gEfiIfrTianoGuid,
|
|
NULL /* optional copy origin */,
|
|
sizeof *Anchor
|
|
);
|
|
if (OpCode == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto FreeOpCodeBuffer;
|
|
}
|
|
|
|
Anchor = OpCode;
|
|
Anchor->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
|
|
Anchor->Number = LABEL_RES_NEXT;
|
|
|
|
//
|
|
// 3. Create the opcodes inside the buffer that are to be inserted into the
|
|
// form.
|
|
//
|
|
// 3.1. Get a list of resolutions.
|
|
//
|
|
Status = CreateResolutionOptions (
|
|
PackageList,
|
|
&OpCodeBuffer2,
|
|
NumGopModes,
|
|
GopModes
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto FreeOpCodeBuffer;
|
|
}
|
|
|
|
//
|
|
// 3.2. Create a one-of-many question with the above options.
|
|
//
|
|
OpCode = HiiCreateOneOfOpCode (
|
|
OpCodeBuffer, // create opcode inside this
|
|
// opcode buffer,
|
|
QUESTION_RES_NEXT, // ID of question,
|
|
FORMSTATEID_MAIN_FORM, // identifies form state
|
|
// storage,
|
|
(UINT16)OFFSET_OF (
|
|
MAIN_FORM_STATE, // value of question stored
|
|
NextPreferredResolution
|
|
), // at this offset,
|
|
STRING_TOKEN (STR_RES_NEXT), // Prompt,
|
|
STRING_TOKEN (STR_RES_NEXT_HELP), // Help,
|
|
0, // QuestionFlags,
|
|
EFI_IFR_NUMERIC_SIZE_4, // see sizeof
|
|
// NextPreferredResolution,
|
|
OpCodeBuffer2, // buffer with possible
|
|
// choices,
|
|
NULL // DEFAULT opcodes
|
|
);
|
|
if (OpCode == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto FreeOpCodeBuffer2;
|
|
}
|
|
|
|
//
|
|
// 4. Update the form with the opcode buffer.
|
|
//
|
|
Status = HiiUpdateForm (
|
|
PackageList,
|
|
FormSetGuid,
|
|
FormId,
|
|
OpCodeBuffer, // buffer with head anchor, and new contents to be
|
|
// inserted at it
|
|
NULL // buffer with tail anchor, for deleting old
|
|
// contents up to it
|
|
);
|
|
|
|
FreeOpCodeBuffer2:
|
|
HiiFreeOpCodeHandle (OpCodeBuffer2);
|
|
|
|
FreeOpCodeBuffer:
|
|
HiiFreeOpCodeHandle (OpCodeBuffer);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Load and execute the platform configuration.
|
|
|
|
@retval EFI_SUCCESS Configuration loaded and executed.
|
|
@return Status codes from PlatformConfigLoad().
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
ExecutePlatformConfig (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
PLATFORM_CONFIG PlatformConfig;
|
|
UINT64 OptionalElements;
|
|
RETURN_STATUS PcdStatus;
|
|
|
|
Status = PlatformConfigLoad (&PlatformConfig, &OptionalElements);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((
|
|
(Status == EFI_NOT_FOUND) ? DEBUG_VERBOSE : DEBUG_ERROR,
|
|
"%a: failed to load platform config: %r\n",
|
|
__func__,
|
|
Status
|
|
));
|
|
return Status;
|
|
}
|
|
|
|
if (OptionalElements & PLATFORM_CONFIG_F_GRAPHICS_RESOLUTION) {
|
|
//
|
|
// Pass the preferred resolution to GraphicsConsoleDxe via dynamic PCDs.
|
|
//
|
|
PcdStatus = PcdSet32S (
|
|
PcdVideoHorizontalResolution,
|
|
PlatformConfig.HorizontalResolution
|
|
);
|
|
ASSERT_RETURN_ERROR (PcdStatus);
|
|
|
|
PcdStatus = PcdSet32S (
|
|
PcdVideoVerticalResolution,
|
|
PlatformConfig.VerticalResolution
|
|
);
|
|
ASSERT_RETURN_ERROR (PcdStatus);
|
|
|
|
PcdStatus = PcdSet8S (PcdVideoResolutionSource, 1);
|
|
ASSERT_RETURN_ERROR (PcdStatus);
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Notification callback for GOP interface installation.
|
|
|
|
@param[in] Event Event whose notification function is being invoked.
|
|
|
|
@param[in] Context The pointer to the notification function's context, which
|
|
is implementation-dependent.
|
|
**/
|
|
STATIC
|
|
VOID
|
|
EFIAPI
|
|
GopInstalled (
|
|
IN EFI_EVENT Event,
|
|
IN VOID *Context
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop;
|
|
|
|
ASSERT (Event == mGopEvent);
|
|
|
|
//
|
|
// Check further GOPs.
|
|
//
|
|
for ( ; ;) {
|
|
mNumGopModes = 0;
|
|
mGopModes = NULL;
|
|
|
|
Status = gBS->LocateProtocol (
|
|
&gEfiGraphicsOutputProtocolGuid,
|
|
mGopTracker,
|
|
(VOID **)&Gop
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return;
|
|
}
|
|
|
|
Status = QueryGopModes (Gop, &mNumGopModes, &mGopModes);
|
|
if (EFI_ERROR (Status)) {
|
|
continue;
|
|
}
|
|
|
|
Status = PopulateForm (
|
|
mInstalledPackages,
|
|
&gOvmfPlatformConfigGuid,
|
|
FORMID_MAIN_FORM,
|
|
mNumGopModes,
|
|
mGopModes
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
FreePool (mGopModes);
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Success -- so uninstall this callback. Closing the event removes all
|
|
// pending notifications and all protocol registrations.
|
|
//
|
|
Status = gBS->CloseEvent (mGopEvent);
|
|
ASSERT_EFI_ERROR (Status);
|
|
mGopEvent = NULL;
|
|
mGopTracker = NULL;
|
|
}
|
|
|
|
/**
|
|
Entry point for this driver.
|
|
|
|
@param[in] ImageHandle Image handle of this driver.
|
|
@param[in] SystemTable Pointer to SystemTable.
|
|
|
|
@retval EFI_SUCESS Driver has loaded successfully.
|
|
@retval EFI_OUT_OF_RESOURCES Failed to install HII packages.
|
|
@return Error codes from lower level functions.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
PlatformInit (
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
ExecutePlatformConfig ();
|
|
|
|
mConfigAccess.ExtractConfig = &ExtractConfig;
|
|
mConfigAccess.RouteConfig = &RouteConfig;
|
|
mConfigAccess.Callback = &Callback;
|
|
|
|
//
|
|
// Declare ourselves suitable for HII communication.
|
|
//
|
|
Status = gBS->InstallMultipleProtocolInterfaces (
|
|
&ImageHandle,
|
|
&gEfiDevicePathProtocolGuid,
|
|
&mPkgDevicePath,
|
|
&gEfiHiiConfigAccessProtocolGuid,
|
|
&mConfigAccess,
|
|
NULL
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Save the driver image handle.
|
|
//
|
|
mImageHandle = ImageHandle;
|
|
|
|
//
|
|
// Publish the HII package list to HII Database.
|
|
//
|
|
mInstalledPackages = HiiAddPackages (
|
|
&gEfiCallerIdGuid, // PackageListGuid
|
|
ImageHandle, // associated DeviceHandle
|
|
PlatformDxeStrings, // 1st package
|
|
PlatformFormsBin, // 2nd package
|
|
NULL // terminator
|
|
);
|
|
if (mInstalledPackages == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto UninstallProtocols;
|
|
}
|
|
|
|
Status = gBS->CreateEvent (
|
|
EVT_NOTIFY_SIGNAL,
|
|
TPL_CALLBACK,
|
|
&GopInstalled,
|
|
NULL /* Context */,
|
|
&mGopEvent
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto RemovePackages;
|
|
}
|
|
|
|
Status = gBS->RegisterProtocolNotify (
|
|
&gEfiGraphicsOutputProtocolGuid,
|
|
mGopEvent,
|
|
&mGopTracker
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto CloseGopEvent;
|
|
}
|
|
|
|
//
|
|
// Check already installed GOPs.
|
|
//
|
|
Status = gBS->SignalEvent (mGopEvent);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
CloseGopEvent:
|
|
gBS->CloseEvent (mGopEvent);
|
|
|
|
RemovePackages:
|
|
HiiRemovePackages (mInstalledPackages);
|
|
|
|
UninstallProtocols:
|
|
gBS->UninstallMultipleProtocolInterfaces (
|
|
ImageHandle,
|
|
&gEfiDevicePathProtocolGuid,
|
|
&mPkgDevicePath,
|
|
&gEfiHiiConfigAccessProtocolGuid,
|
|
&mConfigAccess,
|
|
NULL
|
|
);
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Unload the driver.
|
|
|
|
@param[in] ImageHandle Handle that identifies the image to evict.
|
|
|
|
@retval EFI_SUCCESS The image has been unloaded.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
PlatformUnload (
|
|
IN EFI_HANDLE ImageHandle
|
|
)
|
|
{
|
|
if (mGopEvent == NULL) {
|
|
//
|
|
// The GOP callback ran successfully and unregistered itself. Release the
|
|
// resources allocated there.
|
|
//
|
|
ASSERT (mGopModes != NULL);
|
|
FreePool (mGopModes);
|
|
} else {
|
|
//
|
|
// Otherwise we need to unregister the callback.
|
|
//
|
|
ASSERT (mGopModes == NULL);
|
|
gBS->CloseEvent (mGopEvent);
|
|
}
|
|
|
|
//
|
|
// Release resources allocated by the entry point.
|
|
//
|
|
HiiRemovePackages (mInstalledPackages);
|
|
gBS->UninstallMultipleProtocolInterfaces (
|
|
ImageHandle,
|
|
&gEfiDevicePathProtocolGuid,
|
|
&mPkgDevicePath,
|
|
&gEfiHiiConfigAccessProtocolGuid,
|
|
&mConfigAccess,
|
|
NULL
|
|
);
|
|
return EFI_SUCCESS;
|
|
}
|