/** @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> | |
This program and the accompanying materials are licensed and made available | |
under the terms and conditions of the BSD License which accompanies this | |
distribution. The full text of the license may be found at | |
http://opensource.org/licenses/bsd-license.php | |
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT | |
WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
**/ | |
#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; | |
// | |
// 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; | |
DEBUG ((EFI_D_VERBOSE, "%a: Request=\"%s\"\n", __FUNCTION__, Request)); | |
Status = PlatformConfigToFormState (&MainFormState); | |
if (EFI_ERROR (Status)) { | |
*Progress = Request; | |
return Status; | |
} | |
// | |
// Answer the textual request keying off the binary form state. | |
// | |
Status = gHiiConfigRouting->BlockToConfig (gHiiConfigRouting, Request, | |
(VOID *) &MainFormState, sizeof MainFormState, | |
Results, Progress); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((EFI_D_ERROR, "%a: BlockToConfig(): %r, Progress=\"%s\"\n", | |
__FUNCTION__, Status, (Status == EFI_DEVICE_ERROR) ? NULL : *Progress)); | |
} else { | |
DEBUG ((EFI_D_VERBOSE, "%a: Results=\"%s\"\n", __FUNCTION__, *Results)); | |
} | |
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 ((EFI_D_VERBOSE, "%a: Configuration=\"%s\"\n", __FUNCTION__, | |
Configuration)); | |
// | |
// 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 ((EFI_D_ERROR, "%a: ConfigToBlock(): %r, Progress=\"%s\"\n", | |
__FUNCTION__, 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 ((EFI_D_VERBOSE, "%a: Action=0x%Lx QuestionId=%d Type=%d\n", | |
__FUNCTION__, (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) ? EFI_D_VERBOSE : EFI_D_ERROR, | |
"%a: failed to load platform config: %r\n", __FUNCTION__, 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); | |
} | |
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; | |
} | |
// | |
// 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; | |
} |