Establish the full stack of conversions when modifying the platform
configuration:
       ConfigResp            -- form engine / HII communication
            |
     [ConfigToBlock]
            |
            v
     MAIN_FORM_STATE         -- binary representation of form/widget state
            |
[FormStateToPlatformConfig]
            |
            v
     PLATFORM_CONFIG         -- accessible to DXE and UEFI drivers
            |
   [PlatformConfigSave]
            |
            v
  UEFI non-volatile variable -- accessible to external utilities
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>
git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@15375 6f19259b-4bc3-4df7-8a09-765794883524
		
	
		
			
				
	
	
		
			872 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			872 lines
		
	
	
		
			25 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>
 | 
						|
 | 
						|
  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;
 | 
						|
 | 
						|
  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.
 | 
						|
    //
 | 
						|
    PcdSet32 (PcdVideoHorizontalResolution,
 | 
						|
      PlatformConfig.HorizontalResolution);
 | 
						|
    PcdSet32 (PcdVideoVerticalResolution,
 | 
						|
      PlatformConfig.VerticalResolution);
 | 
						|
  }
 | 
						|
 | 
						|
  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;
 | 
						|
}
 |