mirror of https://github.com/acidanthera/audk.git
1238 lines
40 KiB
C
1238 lines
40 KiB
C
/** @file
|
|
This module produce main entry for BDS phase - BdsEntry.
|
|
When this module was dispatched by DxeCore, gEfiBdsArchProtocolGuid will be installed
|
|
which contains interface of BdsEntry.
|
|
After DxeCore finish DXE phase, gEfiBdsArchProtocolGuid->BdsEntry will be invoked
|
|
to enter BDS phase.
|
|
|
|
Copyright (c) 2004 - 2017, Intel Corporation. All rights reserved.<BR>
|
|
(C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
|
|
(C) Copyright 2015 Hewlett-Packard Development Company, L.P.<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 "Bds.h"
|
|
#include "Language.h"
|
|
#include "HwErrRecSupport.h"
|
|
|
|
#define SET_BOOT_OPTION_SUPPORT_KEY_COUNT(a, c) { \
|
|
(a) = ((a) & ~EFI_BOOT_OPTION_SUPPORT_COUNT) | (((c) << LowBitSet32 (EFI_BOOT_OPTION_SUPPORT_COUNT)) & EFI_BOOT_OPTION_SUPPORT_COUNT); \
|
|
}
|
|
|
|
///
|
|
/// BDS arch protocol instance initial value.
|
|
///
|
|
EFI_BDS_ARCH_PROTOCOL gBds = {
|
|
BdsEntry
|
|
};
|
|
|
|
//
|
|
// gConnectConInEvent - Event which is signaled when ConIn connection is required
|
|
//
|
|
EFI_EVENT gConnectConInEvent = NULL;
|
|
|
|
///
|
|
/// The read-only variables defined in UEFI Spec.
|
|
///
|
|
CHAR16 *mReadOnlyVariables[] = {
|
|
EFI_PLATFORM_LANG_CODES_VARIABLE_NAME,
|
|
EFI_LANG_CODES_VARIABLE_NAME,
|
|
EFI_BOOT_OPTION_SUPPORT_VARIABLE_NAME,
|
|
EFI_HW_ERR_REC_SUPPORT_VARIABLE_NAME,
|
|
EFI_OS_INDICATIONS_SUPPORT_VARIABLE_NAME
|
|
};
|
|
|
|
CHAR16 *mBdsLoadOptionName[] = {
|
|
L"Driver",
|
|
L"SysPrep",
|
|
L"Boot",
|
|
L"PlatformRecovery"
|
|
};
|
|
|
|
/**
|
|
Event to Connect ConIn.
|
|
|
|
@param Event Event whose notification function is being invoked.
|
|
@param Context Pointer to the notification function's context,
|
|
which is implementation-dependent.
|
|
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
BdsDxeOnConnectConInCallBack (
|
|
IN EFI_EVENT Event,
|
|
IN VOID *Context
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
//
|
|
// When Osloader call ReadKeyStroke to signal this event
|
|
// no driver dependency is assumed existing. So use a non-dispatch version
|
|
//
|
|
Status = EfiBootManagerConnectConsoleVariable (ConIn);
|
|
if (EFI_ERROR (Status)) {
|
|
//
|
|
// Should not enter this case, if enter, the keyboard will not work.
|
|
// May need platfrom policy to connect keyboard.
|
|
//
|
|
DEBUG ((EFI_D_WARN, "[Bds] Connect ConIn failed - %r!!!\n", Status));
|
|
}
|
|
}
|
|
/**
|
|
Notify function for event group EFI_EVENT_GROUP_READY_TO_BOOT. This is used to
|
|
check whether there is remaining deferred load images.
|
|
|
|
@param[in] Event The Event that is being processed.
|
|
@param[in] Context The Event Context.
|
|
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
CheckDeferredLoadImageOnReadyToBoot (
|
|
IN EFI_EVENT Event,
|
|
IN VOID *Context
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_DEFERRED_IMAGE_LOAD_PROTOCOL *DeferredImage;
|
|
UINTN HandleCount;
|
|
EFI_HANDLE *Handles;
|
|
UINTN Index;
|
|
UINTN ImageIndex;
|
|
EFI_DEVICE_PATH_PROTOCOL *ImageDevicePath;
|
|
VOID *Image;
|
|
UINTN ImageSize;
|
|
BOOLEAN BootOption;
|
|
CHAR16 *DevicePathStr;
|
|
|
|
//
|
|
// Find all the deferred image load protocols.
|
|
//
|
|
HandleCount = 0;
|
|
Handles = NULL;
|
|
Status = gBS->LocateHandleBuffer (
|
|
ByProtocol,
|
|
&gEfiDeferredImageLoadProtocolGuid,
|
|
NULL,
|
|
&HandleCount,
|
|
&Handles
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return;
|
|
}
|
|
|
|
for (Index = 0; Index < HandleCount; Index++) {
|
|
Status = gBS->HandleProtocol (Handles[Index], &gEfiDeferredImageLoadProtocolGuid, (VOID **) &DeferredImage);
|
|
if (EFI_ERROR (Status)) {
|
|
continue;
|
|
}
|
|
|
|
for (ImageIndex = 0; ; ImageIndex++) {
|
|
//
|
|
// Load all the deferred images in this protocol instance.
|
|
//
|
|
Status = DeferredImage->GetImageInfo (
|
|
DeferredImage,
|
|
ImageIndex,
|
|
&ImageDevicePath,
|
|
(VOID **) &Image,
|
|
&ImageSize,
|
|
&BootOption
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
break;
|
|
}
|
|
DevicePathStr = ConvertDevicePathToText (ImageDevicePath, FALSE, FALSE);
|
|
DEBUG ((DEBUG_LOAD, "[Bds] Image was deferred but not loaded: %s.\n", DevicePathStr));
|
|
if (DevicePathStr != NULL) {
|
|
FreePool (DevicePathStr);
|
|
}
|
|
}
|
|
}
|
|
if (Handles != NULL) {
|
|
FreePool (Handles);
|
|
}
|
|
}
|
|
|
|
/**
|
|
|
|
Install Boot Device Selection Protocol
|
|
|
|
@param ImageHandle The image handle.
|
|
@param SystemTable The system table.
|
|
|
|
@retval EFI_SUCEESS BDS has finished initializing.
|
|
Return the dispatcher and recall BDS.Entry
|
|
@retval Other Return status from AllocatePool() or gBS->InstallProtocolInterface
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
BdsInitialize (
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_HANDLE Handle;
|
|
//
|
|
// Install protocol interface
|
|
//
|
|
Handle = NULL;
|
|
Status = gBS->InstallMultipleProtocolInterfaces (
|
|
&Handle,
|
|
&gEfiBdsArchProtocolGuid, &gBds,
|
|
NULL
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
DEBUG_CODE (
|
|
EFI_EVENT Event;
|
|
//
|
|
// Register notify function to check deferred images on ReadyToBoot Event.
|
|
//
|
|
Status = gBS->CreateEventEx (
|
|
EVT_NOTIFY_SIGNAL,
|
|
TPL_CALLBACK,
|
|
CheckDeferredLoadImageOnReadyToBoot,
|
|
NULL,
|
|
&gEfiEventReadyToBootGuid,
|
|
&Event
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
);
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Function waits for a given event to fire, or for an optional timeout to expire.
|
|
|
|
@param Event The event to wait for
|
|
@param Timeout An optional timeout value in 100 ns units.
|
|
|
|
@retval EFI_SUCCESS Event fired before Timeout expired.
|
|
@retval EFI_TIME_OUT Timout expired before Event fired..
|
|
|
|
**/
|
|
EFI_STATUS
|
|
BdsWaitForSingleEvent (
|
|
IN EFI_EVENT Event,
|
|
IN UINT64 Timeout OPTIONAL
|
|
)
|
|
{
|
|
UINTN Index;
|
|
EFI_STATUS Status;
|
|
EFI_EVENT TimerEvent;
|
|
EFI_EVENT WaitList[2];
|
|
|
|
if (Timeout != 0) {
|
|
//
|
|
// Create a timer event
|
|
//
|
|
Status = gBS->CreateEvent (EVT_TIMER, 0, NULL, NULL, &TimerEvent);
|
|
if (!EFI_ERROR (Status)) {
|
|
//
|
|
// Set the timer event
|
|
//
|
|
gBS->SetTimer (
|
|
TimerEvent,
|
|
TimerRelative,
|
|
Timeout
|
|
);
|
|
|
|
//
|
|
// Wait for the original event or the timer
|
|
//
|
|
WaitList[0] = Event;
|
|
WaitList[1] = TimerEvent;
|
|
Status = gBS->WaitForEvent (2, WaitList, &Index);
|
|
ASSERT_EFI_ERROR (Status);
|
|
gBS->CloseEvent (TimerEvent);
|
|
|
|
//
|
|
// If the timer expired, change the return to timed out
|
|
//
|
|
if (Index == 1) {
|
|
Status = EFI_TIMEOUT;
|
|
}
|
|
}
|
|
} else {
|
|
//
|
|
// No timeout... just wait on the event
|
|
//
|
|
Status = gBS->WaitForEvent (1, &Event, &Index);
|
|
ASSERT (!EFI_ERROR (Status));
|
|
ASSERT (Index == 0);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
The function reads user inputs.
|
|
|
|
**/
|
|
VOID
|
|
BdsReadKeys (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_INPUT_KEY Key;
|
|
|
|
if (PcdGetBool (PcdConInConnectOnDemand)) {
|
|
return;
|
|
}
|
|
|
|
while (gST->ConIn != NULL) {
|
|
|
|
Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
//
|
|
// No more keys.
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
The function waits for the boot manager timeout expires or hotkey is pressed.
|
|
|
|
It calls PlatformBootManagerWaitCallback each second.
|
|
|
|
@param HotkeyTriggered Input hotkey event.
|
|
**/
|
|
VOID
|
|
BdsWait (
|
|
IN EFI_EVENT HotkeyTriggered
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT16 TimeoutRemain;
|
|
|
|
DEBUG ((EFI_D_INFO, "[Bds]BdsWait ...Zzzzzzzzzzzz...\n"));
|
|
|
|
TimeoutRemain = PcdGet16 (PcdPlatformBootTimeOut);
|
|
while (TimeoutRemain != 0) {
|
|
DEBUG ((EFI_D_INFO, "[Bds]BdsWait(%d)..Zzzz...\n", (UINTN) TimeoutRemain));
|
|
PlatformBootManagerWaitCallback (TimeoutRemain);
|
|
|
|
BdsReadKeys (); // BUGBUG: Only reading can signal HotkeyTriggered
|
|
// Can be removed after all keyboard drivers invoke callback in timer callback.
|
|
|
|
if (HotkeyTriggered != NULL) {
|
|
Status = BdsWaitForSingleEvent (HotkeyTriggered, EFI_TIMER_PERIOD_SECONDS (1));
|
|
if (!EFI_ERROR (Status)) {
|
|
break;
|
|
}
|
|
} else {
|
|
gBS->Stall (1000000);
|
|
}
|
|
|
|
//
|
|
// 0xffff means waiting forever
|
|
// BDS with no hotkey provided and 0xffff as timeout will "hang" in the loop
|
|
//
|
|
if (TimeoutRemain != 0xffff) {
|
|
TimeoutRemain--;
|
|
}
|
|
}
|
|
DEBUG ((EFI_D_INFO, "[Bds]Exit the waiting!\n"));
|
|
}
|
|
|
|
/**
|
|
Attempt to boot each boot option in the BootOptions array.
|
|
|
|
@param BootOptions Input boot option array.
|
|
@param BootOptionCount Input boot option count.
|
|
@param BootManagerMenu Input boot manager menu.
|
|
|
|
@retval TRUE Successfully boot one of the boot options.
|
|
@retval FALSE Failed boot any of the boot options.
|
|
**/
|
|
BOOLEAN
|
|
BootBootOptions (
|
|
IN EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions,
|
|
IN UINTN BootOptionCount,
|
|
IN EFI_BOOT_MANAGER_LOAD_OPTION *BootManagerMenu OPTIONAL
|
|
)
|
|
{
|
|
UINTN Index;
|
|
|
|
//
|
|
// Report Status Code to indicate BDS starts attempting booting from the UEFI BootOrder list.
|
|
//
|
|
REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_ATTEMPT_BOOT_ORDER_EVENT));
|
|
|
|
//
|
|
// Attempt boot each boot option
|
|
//
|
|
for (Index = 0; Index < BootOptionCount; Index++) {
|
|
//
|
|
// According to EFI Specification, if a load option is not marked
|
|
// as LOAD_OPTION_ACTIVE, the boot manager will not automatically
|
|
// load the option.
|
|
//
|
|
if ((BootOptions[Index].Attributes & LOAD_OPTION_ACTIVE) == 0) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Boot#### load options with LOAD_OPTION_CATEGORY_APP are executables which are not
|
|
// part of the normal boot processing. Boot options with reserved category values will be
|
|
// ignored by the boot manager.
|
|
//
|
|
if ((BootOptions[Index].Attributes & LOAD_OPTION_CATEGORY) != LOAD_OPTION_CATEGORY_BOOT) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// All the driver options should have been processed since
|
|
// now boot will be performed.
|
|
//
|
|
EfiBootManagerBoot (&BootOptions[Index]);
|
|
|
|
//
|
|
// If the boot via Boot#### returns with a status of EFI_SUCCESS, platform firmware
|
|
// supports boot manager menu, and if firmware is configured to boot in an
|
|
// interactive mode, the boot manager will stop processing the BootOrder variable and
|
|
// present a boot manager menu to the user.
|
|
//
|
|
if ((BootManagerMenu != NULL) && (BootOptions[Index].Status == EFI_SUCCESS)) {
|
|
EfiBootManagerBoot (BootManagerMenu);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return (BOOLEAN) (Index < BootOptionCount);
|
|
}
|
|
|
|
/**
|
|
The function will load and start every Driver####, SysPrep#### or PlatformRecovery####.
|
|
|
|
@param LoadOptions Load option array.
|
|
@param LoadOptionCount Load option count.
|
|
**/
|
|
VOID
|
|
ProcessLoadOptions (
|
|
IN EFI_BOOT_MANAGER_LOAD_OPTION *LoadOptions,
|
|
IN UINTN LoadOptionCount
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN Index;
|
|
BOOLEAN ReconnectAll;
|
|
EFI_BOOT_MANAGER_LOAD_OPTION_TYPE LoadOptionType;
|
|
|
|
ReconnectAll = FALSE;
|
|
LoadOptionType = LoadOptionTypeMax;
|
|
|
|
//
|
|
// Process the driver option
|
|
//
|
|
for (Index = 0; Index < LoadOptionCount; Index++) {
|
|
//
|
|
// All the load options in the array should be of the same type.
|
|
//
|
|
if (Index == 0) {
|
|
LoadOptionType = LoadOptions[Index].OptionType;
|
|
}
|
|
ASSERT (LoadOptionType == LoadOptions[Index].OptionType);
|
|
ASSERT (LoadOptionType != LoadOptionTypeBoot);
|
|
|
|
Status = EfiBootManagerProcessLoadOption (&LoadOptions[Index]);
|
|
|
|
//
|
|
// Status indicates whether the load option is loaded and executed
|
|
// LoadOptions[Index].Status is what the load option returns
|
|
//
|
|
if (!EFI_ERROR (Status)) {
|
|
//
|
|
// Stop processing if any PlatformRecovery#### returns success.
|
|
//
|
|
if ((LoadOptions[Index].Status == EFI_SUCCESS) &&
|
|
(LoadOptionType == LoadOptionTypePlatformRecovery)) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Only set ReconnectAll flag when the load option executes successfully.
|
|
//
|
|
if (!EFI_ERROR (LoadOptions[Index].Status) &&
|
|
(LoadOptions[Index].Attributes & LOAD_OPTION_FORCE_RECONNECT) != 0) {
|
|
ReconnectAll = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If a driver load option is marked as LOAD_OPTION_FORCE_RECONNECT,
|
|
// then all of the EFI drivers in the system will be disconnected and
|
|
// reconnected after the last driver load option is processed.
|
|
//
|
|
if (ReconnectAll && LoadOptionType == LoadOptionTypeDriver) {
|
|
EfiBootManagerDisconnectAll ();
|
|
EfiBootManagerConnectAll ();
|
|
}
|
|
}
|
|
|
|
/**
|
|
|
|
Validate input console variable data.
|
|
|
|
If found the device path is not a valid device path, remove the variable.
|
|
|
|
@param VariableName Input console variable name.
|
|
|
|
**/
|
|
VOID
|
|
BdsFormalizeConsoleVariable (
|
|
IN CHAR16 *VariableName
|
|
)
|
|
{
|
|
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
|
|
UINTN VariableSize;
|
|
EFI_STATUS Status;
|
|
|
|
GetEfiGlobalVariable2 (VariableName, (VOID **) &DevicePath, &VariableSize);
|
|
if ((DevicePath != NULL) && !IsDevicePathValid (DevicePath, VariableSize)) {
|
|
Status = gRT->SetVariable (
|
|
VariableName,
|
|
&gEfiGlobalVariableGuid,
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
|
|
0,
|
|
NULL
|
|
);
|
|
//
|
|
// Deleting variable with current variable implementation shouldn't fail.
|
|
//
|
|
ASSERT_EFI_ERROR (Status);
|
|
}
|
|
|
|
if (DevicePath != NULL) {
|
|
FreePool (DevicePath);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Formalize OsIndication related variables.
|
|
|
|
For OsIndicationsSupported, Create a BS/RT/UINT64 variable to report caps
|
|
Delete OsIndications variable if it is not NV/BS/RT UINT64.
|
|
|
|
Item 3 is used to solve case when OS corrupts OsIndications. Here simply delete this NV variable.
|
|
|
|
Create a boot option for BootManagerMenu if it hasn't been created yet
|
|
|
|
**/
|
|
VOID
|
|
BdsFormalizeOSIndicationVariable (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT64 OsIndicationSupport;
|
|
UINT64 OsIndication;
|
|
UINTN DataSize;
|
|
UINT32 Attributes;
|
|
EFI_BOOT_MANAGER_LOAD_OPTION BootManagerMenu;
|
|
|
|
//
|
|
// OS indicater support variable
|
|
//
|
|
Status = EfiBootManagerGetBootManagerMenu (&BootManagerMenu);
|
|
if (Status != EFI_NOT_FOUND) {
|
|
OsIndicationSupport = EFI_OS_INDICATIONS_BOOT_TO_FW_UI | EFI_OS_INDICATIONS_START_PLATFORM_RECOVERY;
|
|
EfiBootManagerFreeLoadOption (&BootManagerMenu);
|
|
} else {
|
|
OsIndicationSupport = EFI_OS_INDICATIONS_START_PLATFORM_RECOVERY;
|
|
}
|
|
|
|
Status = gRT->SetVariable (
|
|
EFI_OS_INDICATIONS_SUPPORT_VARIABLE_NAME,
|
|
&gEfiGlobalVariableGuid,
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
|
|
sizeof(UINT64),
|
|
&OsIndicationSupport
|
|
);
|
|
//
|
|
// Platform needs to make sure setting volatile variable before calling 3rd party code shouldn't fail.
|
|
//
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
//
|
|
// If OsIndications is invalid, remove it.
|
|
// Invalid case
|
|
// 1. Data size != UINT64
|
|
// 2. OsIndication value inconsistence
|
|
// 3. OsIndication attribute inconsistence
|
|
//
|
|
OsIndication = 0;
|
|
Attributes = 0;
|
|
DataSize = sizeof(UINT64);
|
|
Status = gRT->GetVariable (
|
|
EFI_OS_INDICATIONS_VARIABLE_NAME,
|
|
&gEfiGlobalVariableGuid,
|
|
&Attributes,
|
|
&DataSize,
|
|
&OsIndication
|
|
);
|
|
if (Status == EFI_NOT_FOUND) {
|
|
return;
|
|
}
|
|
|
|
if ((DataSize != sizeof (OsIndication)) ||
|
|
((OsIndication & ~OsIndicationSupport) != 0) ||
|
|
(Attributes != (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE))
|
|
){
|
|
|
|
DEBUG ((EFI_D_ERROR, "[Bds] Unformalized OsIndications variable exists. Delete it\n"));
|
|
Status = gRT->SetVariable (
|
|
EFI_OS_INDICATIONS_VARIABLE_NAME,
|
|
&gEfiGlobalVariableGuid,
|
|
0,
|
|
0,
|
|
NULL
|
|
);
|
|
//
|
|
// Deleting variable with current variable implementation shouldn't fail.
|
|
//
|
|
ASSERT_EFI_ERROR(Status);
|
|
}
|
|
}
|
|
|
|
/**
|
|
|
|
Validate variables.
|
|
|
|
**/
|
|
VOID
|
|
BdsFormalizeEfiGlobalVariable (
|
|
VOID
|
|
)
|
|
{
|
|
//
|
|
// Validate Console variable.
|
|
//
|
|
BdsFormalizeConsoleVariable (EFI_CON_IN_VARIABLE_NAME);
|
|
BdsFormalizeConsoleVariable (EFI_CON_OUT_VARIABLE_NAME);
|
|
BdsFormalizeConsoleVariable (EFI_ERR_OUT_VARIABLE_NAME);
|
|
|
|
//
|
|
// Validate OSIndication related variable.
|
|
//
|
|
BdsFormalizeOSIndicationVariable ();
|
|
}
|
|
|
|
/**
|
|
|
|
Allocate a block of memory that will contain performance data to OS.
|
|
|
|
**/
|
|
VOID
|
|
BdsAllocateMemoryForPerformanceData (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_PHYSICAL_ADDRESS AcpiLowMemoryBase;
|
|
EDKII_VARIABLE_LOCK_PROTOCOL *VariableLock;
|
|
|
|
AcpiLowMemoryBase = 0x0FFFFFFFFULL;
|
|
|
|
//
|
|
// Allocate a block of memory that will contain performance data to OS.
|
|
//
|
|
Status = gBS->AllocatePages (
|
|
AllocateMaxAddress,
|
|
EfiReservedMemoryType,
|
|
EFI_SIZE_TO_PAGES (PERF_DATA_MAX_LENGTH),
|
|
&AcpiLowMemoryBase
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
//
|
|
// Save the pointer to variable for use in S3 resume.
|
|
//
|
|
Status = BdsDxeSetVariableAndReportStatusCodeOnError (
|
|
L"PerfDataMemAddr",
|
|
&gPerformanceProtocolGuid,
|
|
EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
|
|
sizeof (EFI_PHYSICAL_ADDRESS),
|
|
&AcpiLowMemoryBase
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((EFI_D_ERROR, "[Bds] PerfDataMemAddr (%08x) cannot be saved to NV storage.\n", AcpiLowMemoryBase));
|
|
}
|
|
//
|
|
// Mark L"PerfDataMemAddr" variable to read-only if the Variable Lock protocol exists
|
|
// Still lock it even the variable cannot be saved to prevent it's set by 3rd party code.
|
|
//
|
|
Status = gBS->LocateProtocol (&gEdkiiVariableLockProtocolGuid, NULL, (VOID **) &VariableLock);
|
|
if (!EFI_ERROR (Status)) {
|
|
Status = VariableLock->RequestToLock (VariableLock, L"PerfDataMemAddr", &gPerformanceProtocolGuid);
|
|
ASSERT_EFI_ERROR (Status);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Enter an infinite loop of calling the Boot Manager Menu.
|
|
|
|
This is a last resort alternative to BdsEntry() giving up for good. This
|
|
function never returns.
|
|
|
|
@param[in] BootManagerMenu The EFI_BOOT_MANAGER_LOAD_OPTION located and/or
|
|
created by the EfiBootManagerGetBootManagerMenu()
|
|
call in BdsEntry().
|
|
**/
|
|
VOID
|
|
BdsBootManagerMenuLoop (
|
|
IN EFI_BOOT_MANAGER_LOAD_OPTION *BootManagerMenu
|
|
)
|
|
{
|
|
EFI_INPUT_KEY Key;
|
|
|
|
//
|
|
// Normally BdsDxe does not print anything to the system console, but this is
|
|
// a last resort -- the end-user will likely not see any DEBUG messages
|
|
// logged in this situation.
|
|
//
|
|
// AsciiPrint() will NULL-check gST->ConOut internally. We check gST->ConIn
|
|
// here to see if it makes sense to request and wait for a keypress.
|
|
//
|
|
if (gST->ConIn != NULL) {
|
|
AsciiPrint (
|
|
"%a: No bootable option or device was found.\n"
|
|
"%a: Press any key to enter the Boot Manager Menu.\n",
|
|
gEfiCallerBaseName,
|
|
gEfiCallerBaseName
|
|
);
|
|
BdsWaitForSingleEvent (gST->ConIn->WaitForKey, 0);
|
|
|
|
//
|
|
// Drain any queued keys.
|
|
//
|
|
while (!EFI_ERROR (gST->ConIn->ReadKeyStroke (gST->ConIn, &Key))) {
|
|
//
|
|
// just throw away Key
|
|
//
|
|
}
|
|
}
|
|
|
|
for (;;) {
|
|
EfiBootManagerBoot (BootManagerMenu);
|
|
}
|
|
}
|
|
|
|
/**
|
|
|
|
Service routine for BdsInstance->Entry(). Devices are connected, the
|
|
consoles are initialized, and the boot options are tried.
|
|
|
|
@param This Protocol Instance structure.
|
|
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
BdsEntry (
|
|
IN EFI_BDS_ARCH_PROTOCOL *This
|
|
)
|
|
{
|
|
EFI_BOOT_MANAGER_LOAD_OPTION *LoadOptions;
|
|
UINTN LoadOptionCount;
|
|
CHAR16 *FirmwareVendor;
|
|
EFI_EVENT HotkeyTriggered;
|
|
UINT64 OsIndication;
|
|
UINTN DataSize;
|
|
EFI_STATUS Status;
|
|
UINT32 BootOptionSupport;
|
|
UINT16 BootTimeOut;
|
|
EDKII_VARIABLE_LOCK_PROTOCOL *VariableLock;
|
|
UINTN Index;
|
|
EFI_BOOT_MANAGER_LOAD_OPTION LoadOption;
|
|
UINT16 *BootNext;
|
|
CHAR16 BootNextVariableName[sizeof ("Boot####")];
|
|
EFI_BOOT_MANAGER_LOAD_OPTION BootManagerMenu;
|
|
BOOLEAN BootFwUi;
|
|
BOOLEAN PlatformRecovery;
|
|
BOOLEAN BootSuccess;
|
|
EFI_DEVICE_PATH_PROTOCOL *FilePath;
|
|
EFI_STATUS BootManagerMenuStatus;
|
|
|
|
HotkeyTriggered = NULL;
|
|
Status = EFI_SUCCESS;
|
|
BootSuccess = FALSE;
|
|
|
|
//
|
|
// Insert the performance probe
|
|
//
|
|
PERF_END (NULL, "DXE", NULL, 0);
|
|
PERF_START (NULL, "BDS", NULL, 0);
|
|
DEBUG ((EFI_D_INFO, "[Bds] Entry...\n"));
|
|
|
|
PERF_CODE (
|
|
BdsAllocateMemoryForPerformanceData ();
|
|
);
|
|
|
|
//
|
|
// Fill in FirmwareVendor and FirmwareRevision from PCDs
|
|
//
|
|
FirmwareVendor = (CHAR16 *) PcdGetPtr (PcdFirmwareVendor);
|
|
gST->FirmwareVendor = AllocateRuntimeCopyPool (StrSize (FirmwareVendor), FirmwareVendor);
|
|
ASSERT (gST->FirmwareVendor != NULL);
|
|
gST->FirmwareRevision = PcdGet32 (PcdFirmwareRevision);
|
|
|
|
//
|
|
// Fixup Tasble CRC after we updated Firmware Vendor and Revision
|
|
//
|
|
gST->Hdr.CRC32 = 0;
|
|
gBS->CalculateCrc32 ((VOID *) gST, sizeof (EFI_SYSTEM_TABLE), &gST->Hdr.CRC32);
|
|
|
|
//
|
|
// Validate Variable.
|
|
//
|
|
BdsFormalizeEfiGlobalVariable ();
|
|
|
|
//
|
|
// Mark the read-only variables if the Variable Lock protocol exists
|
|
//
|
|
Status = gBS->LocateProtocol (&gEdkiiVariableLockProtocolGuid, NULL, (VOID **) &VariableLock);
|
|
DEBUG ((EFI_D_INFO, "[BdsDxe] Locate Variable Lock protocol - %r\n", Status));
|
|
if (!EFI_ERROR (Status)) {
|
|
for (Index = 0; Index < ARRAY_SIZE (mReadOnlyVariables); Index++) {
|
|
Status = VariableLock->RequestToLock (VariableLock, mReadOnlyVariables[Index], &gEfiGlobalVariableGuid);
|
|
ASSERT_EFI_ERROR (Status);
|
|
}
|
|
}
|
|
|
|
InitializeHwErrRecSupport ();
|
|
|
|
//
|
|
// Initialize L"Timeout" EFI global variable.
|
|
//
|
|
BootTimeOut = PcdGet16 (PcdPlatformBootTimeOut);
|
|
if (BootTimeOut != 0xFFFF) {
|
|
//
|
|
// If time out value equal 0xFFFF, no need set to 0xFFFF to variable area because UEFI specification
|
|
// define same behavior between no value or 0xFFFF value for L"Timeout".
|
|
//
|
|
BdsDxeSetVariableAndReportStatusCodeOnError (
|
|
EFI_TIME_OUT_VARIABLE_NAME,
|
|
&gEfiGlobalVariableGuid,
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
|
|
sizeof (UINT16),
|
|
&BootTimeOut
|
|
);
|
|
}
|
|
|
|
//
|
|
// Initialize L"BootOptionSupport" EFI global variable.
|
|
// Lazy-ConIn implictly disables BDS hotkey.
|
|
//
|
|
BootOptionSupport = EFI_BOOT_OPTION_SUPPORT_APP | EFI_BOOT_OPTION_SUPPORT_SYSPREP;
|
|
if (!PcdGetBool (PcdConInConnectOnDemand)) {
|
|
BootOptionSupport |= EFI_BOOT_OPTION_SUPPORT_KEY;
|
|
SET_BOOT_OPTION_SUPPORT_KEY_COUNT (BootOptionSupport, 3);
|
|
}
|
|
Status = gRT->SetVariable (
|
|
EFI_BOOT_OPTION_SUPPORT_VARIABLE_NAME,
|
|
&gEfiGlobalVariableGuid,
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
|
|
sizeof (BootOptionSupport),
|
|
&BootOptionSupport
|
|
);
|
|
//
|
|
// Platform needs to make sure setting volatile variable before calling 3rd party code shouldn't fail.
|
|
//
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
//
|
|
// Cache the "BootNext" NV variable before calling any PlatformBootManagerLib APIs
|
|
// This could avoid the "BootNext" set by PlatformBootManagerLib be consumed in this boot.
|
|
//
|
|
GetEfiGlobalVariable2 (EFI_BOOT_NEXT_VARIABLE_NAME, (VOID **) &BootNext, &DataSize);
|
|
if (DataSize != sizeof (UINT16)) {
|
|
if (BootNext != NULL) {
|
|
FreePool (BootNext);
|
|
}
|
|
BootNext = NULL;
|
|
}
|
|
|
|
//
|
|
// Initialize the platform language variables
|
|
//
|
|
InitializeLanguage (TRUE);
|
|
|
|
//
|
|
// System firmware must include a PlatformRecovery#### variable specifying
|
|
// a short-form File Path Media Device Path containing the platform default
|
|
// file path for removable media
|
|
//
|
|
FilePath = FileDevicePath (NULL, EFI_REMOVABLE_MEDIA_FILE_NAME);
|
|
Status = EfiBootManagerInitializeLoadOption (
|
|
&LoadOption,
|
|
LoadOptionNumberUnassigned,
|
|
LoadOptionTypePlatformRecovery,
|
|
LOAD_OPTION_ACTIVE,
|
|
L"Default PlatformRecovery",
|
|
FilePath,
|
|
NULL,
|
|
0
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
LoadOptions = EfiBootManagerGetLoadOptions (&LoadOptionCount, LoadOptionTypePlatformRecovery);
|
|
if (EfiBootManagerFindLoadOption (&LoadOption, LoadOptions, LoadOptionCount) == -1) {
|
|
for (Index = 0; Index < LoadOptionCount; Index++) {
|
|
//
|
|
// The PlatformRecovery#### options are sorted by OptionNumber.
|
|
// Find the the smallest unused number as the new OptionNumber.
|
|
//
|
|
if (LoadOptions[Index].OptionNumber != Index) {
|
|
break;
|
|
}
|
|
}
|
|
LoadOption.OptionNumber = Index;
|
|
Status = EfiBootManagerLoadOptionToVariable (&LoadOption);
|
|
ASSERT_EFI_ERROR (Status);
|
|
}
|
|
EfiBootManagerFreeLoadOption (&LoadOption);
|
|
FreePool (FilePath);
|
|
EfiBootManagerFreeLoadOptions (LoadOptions, LoadOptionCount);
|
|
|
|
//
|
|
// Report Status Code to indicate connecting drivers will happen
|
|
//
|
|
REPORT_STATUS_CODE (
|
|
EFI_PROGRESS_CODE,
|
|
(EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_BEGIN_CONNECTING_DRIVERS)
|
|
);
|
|
|
|
//
|
|
// Initialize ConnectConIn event before calling platform code.
|
|
//
|
|
if (PcdGetBool (PcdConInConnectOnDemand)) {
|
|
Status = gBS->CreateEventEx (
|
|
EVT_NOTIFY_SIGNAL,
|
|
TPL_CALLBACK,
|
|
BdsDxeOnConnectConInCallBack,
|
|
NULL,
|
|
&gConnectConInEventGuid,
|
|
&gConnectConInEvent
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
gConnectConInEvent = NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Do the platform init, can be customized by OEM/IBV
|
|
// Possible things that can be done in PlatformBootManagerBeforeConsole:
|
|
// > Update console variable: 1. include hot-plug devices; 2. Clear ConIn and add SOL for AMT
|
|
// > Register new Driver#### or Boot####
|
|
// > Register new Key####: e.g.: F12
|
|
// > Signal ReadyToLock event
|
|
// > Authentication action: 1. connect Auth devices; 2. Identify auto logon user.
|
|
//
|
|
PERF_START (NULL, "PlatformBootManagerBeforeConsole", "BDS", 0);
|
|
PlatformBootManagerBeforeConsole ();
|
|
PERF_END (NULL, "PlatformBootManagerBeforeConsole", "BDS", 0);
|
|
|
|
//
|
|
// Initialize hotkey service
|
|
//
|
|
EfiBootManagerStartHotkeyService (&HotkeyTriggered);
|
|
|
|
//
|
|
// Execute Driver Options
|
|
//
|
|
LoadOptions = EfiBootManagerGetLoadOptions (&LoadOptionCount, LoadOptionTypeDriver);
|
|
ProcessLoadOptions (LoadOptions, LoadOptionCount);
|
|
EfiBootManagerFreeLoadOptions (LoadOptions, LoadOptionCount);
|
|
|
|
//
|
|
// Connect consoles
|
|
//
|
|
PERF_START (NULL, "EfiBootManagerConnectAllDefaultConsoles", "BDS", 0);
|
|
if (PcdGetBool (PcdConInConnectOnDemand)) {
|
|
EfiBootManagerConnectConsoleVariable (ConOut);
|
|
EfiBootManagerConnectConsoleVariable (ErrOut);
|
|
//
|
|
// Do not connect ConIn devices when lazy ConIn feature is ON.
|
|
//
|
|
} else {
|
|
EfiBootManagerConnectAllDefaultConsoles ();
|
|
}
|
|
PERF_END (NULL, "EfiBootManagerConnectAllDefaultConsoles", "BDS", 0);
|
|
|
|
//
|
|
// Do the platform specific action after the console is ready
|
|
// Possible things that can be done in PlatformBootManagerAfterConsole:
|
|
// > Console post action:
|
|
// > Dynamically switch output mode from 100x31 to 80x25 for certain senarino
|
|
// > Signal console ready platform customized event
|
|
// > Run diagnostics like memory testing
|
|
// > Connect certain devices
|
|
// > Dispatch aditional option roms
|
|
// > Special boot: e.g.: USB boot, enter UI
|
|
//
|
|
PERF_START (NULL, "PlatformBootManagerAfterConsole", "BDS", 0);
|
|
PlatformBootManagerAfterConsole ();
|
|
PERF_END (NULL, "PlatformBootManagerAfterConsole", "BDS", 0);
|
|
//
|
|
// Boot to Boot Manager Menu when EFI_OS_INDICATIONS_BOOT_TO_FW_UI is set. Skip HotkeyBoot
|
|
//
|
|
DataSize = sizeof (UINT64);
|
|
Status = gRT->GetVariable (
|
|
EFI_OS_INDICATIONS_VARIABLE_NAME,
|
|
&gEfiGlobalVariableGuid,
|
|
NULL,
|
|
&DataSize,
|
|
&OsIndication
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
OsIndication = 0;
|
|
}
|
|
|
|
DEBUG_CODE (
|
|
EFI_BOOT_MANAGER_LOAD_OPTION_TYPE LoadOptionType;
|
|
DEBUG ((EFI_D_INFO, "[Bds]OsIndication: %016x\n", OsIndication));
|
|
DEBUG ((EFI_D_INFO, "[Bds]=============Begin Load Options Dumping ...=============\n"));
|
|
for (LoadOptionType = 0; LoadOptionType < LoadOptionTypeMax; LoadOptionType++) {
|
|
DEBUG ((
|
|
EFI_D_INFO, " %s Options:\n",
|
|
mBdsLoadOptionName[LoadOptionType]
|
|
));
|
|
LoadOptions = EfiBootManagerGetLoadOptions (&LoadOptionCount, LoadOptionType);
|
|
for (Index = 0; Index < LoadOptionCount; Index++) {
|
|
DEBUG ((
|
|
EFI_D_INFO, " %s%04x: %s \t\t 0x%04x\n",
|
|
mBdsLoadOptionName[LoadOptionType],
|
|
LoadOptions[Index].OptionNumber,
|
|
LoadOptions[Index].Description,
|
|
LoadOptions[Index].Attributes
|
|
));
|
|
}
|
|
EfiBootManagerFreeLoadOptions (LoadOptions, LoadOptionCount);
|
|
}
|
|
DEBUG ((EFI_D_INFO, "[Bds]=============End Load Options Dumping=============\n"));
|
|
);
|
|
|
|
//
|
|
// BootManagerMenu doesn't contain the correct information when return status is EFI_NOT_FOUND.
|
|
//
|
|
BootManagerMenuStatus = EfiBootManagerGetBootManagerMenu (&BootManagerMenu);
|
|
|
|
BootFwUi = (BOOLEAN) ((OsIndication & EFI_OS_INDICATIONS_BOOT_TO_FW_UI) != 0);
|
|
PlatformRecovery = (BOOLEAN) ((OsIndication & EFI_OS_INDICATIONS_START_PLATFORM_RECOVERY) != 0);
|
|
//
|
|
// Clear EFI_OS_INDICATIONS_BOOT_TO_FW_UI to acknowledge OS
|
|
//
|
|
if (BootFwUi || PlatformRecovery) {
|
|
OsIndication &= ~((UINT64) (EFI_OS_INDICATIONS_BOOT_TO_FW_UI | EFI_OS_INDICATIONS_START_PLATFORM_RECOVERY));
|
|
Status = gRT->SetVariable (
|
|
EFI_OS_INDICATIONS_VARIABLE_NAME,
|
|
&gEfiGlobalVariableGuid,
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
|
|
sizeof(UINT64),
|
|
&OsIndication
|
|
);
|
|
//
|
|
// Changing the content without increasing its size with current variable implementation shouldn't fail.
|
|
//
|
|
ASSERT_EFI_ERROR (Status);
|
|
}
|
|
|
|
//
|
|
// Launch Boot Manager Menu directly when EFI_OS_INDICATIONS_BOOT_TO_FW_UI is set. Skip HotkeyBoot
|
|
//
|
|
if (BootFwUi && (BootManagerMenuStatus != EFI_NOT_FOUND)) {
|
|
//
|
|
// Follow generic rule, Call BdsDxeOnConnectConInCallBack to connect ConIn before enter UI
|
|
//
|
|
if (PcdGetBool (PcdConInConnectOnDemand)) {
|
|
BdsDxeOnConnectConInCallBack (NULL, NULL);
|
|
}
|
|
|
|
//
|
|
// Directly enter the setup page.
|
|
//
|
|
EfiBootManagerBoot (&BootManagerMenu);
|
|
}
|
|
|
|
if (!PlatformRecovery) {
|
|
//
|
|
// Execute SysPrep####
|
|
//
|
|
LoadOptions = EfiBootManagerGetLoadOptions (&LoadOptionCount, LoadOptionTypeSysPrep);
|
|
ProcessLoadOptions (LoadOptions, LoadOptionCount);
|
|
EfiBootManagerFreeLoadOptions (LoadOptions, LoadOptionCount);
|
|
|
|
//
|
|
// Execute Key####
|
|
//
|
|
PERF_START (NULL, "BdsWait", "BDS", 0);
|
|
BdsWait (HotkeyTriggered);
|
|
PERF_END (NULL, "BdsWait", "BDS", 0);
|
|
|
|
//
|
|
// BdsReadKeys() can be removed after all keyboard drivers invoke callback in timer callback.
|
|
//
|
|
BdsReadKeys ();
|
|
|
|
EfiBootManagerHotkeyBoot ();
|
|
|
|
if (BootNext != NULL) {
|
|
//
|
|
// Delete "BootNext" NV variable before transferring control to it to prevent loops.
|
|
//
|
|
Status = gRT->SetVariable (
|
|
EFI_BOOT_NEXT_VARIABLE_NAME,
|
|
&gEfiGlobalVariableGuid,
|
|
0,
|
|
0,
|
|
NULL
|
|
);
|
|
//
|
|
// Deleting NV variable shouldn't fail unless it doesn't exist.
|
|
//
|
|
ASSERT (Status == EFI_SUCCESS || Status == EFI_NOT_FOUND);
|
|
|
|
//
|
|
// Boot to "BootNext"
|
|
//
|
|
UnicodeSPrint (BootNextVariableName, sizeof (BootNextVariableName), L"Boot%04x", *BootNext);
|
|
Status = EfiBootManagerVariableToLoadOption (BootNextVariableName, &LoadOption);
|
|
if (!EFI_ERROR (Status)) {
|
|
EfiBootManagerBoot (&LoadOption);
|
|
EfiBootManagerFreeLoadOption (&LoadOption);
|
|
if ((LoadOption.Status == EFI_SUCCESS) &&
|
|
(BootManagerMenuStatus != EFI_NOT_FOUND) &&
|
|
(LoadOption.OptionNumber != BootManagerMenu.OptionNumber)) {
|
|
//
|
|
// Boot to Boot Manager Menu upon EFI_SUCCESS
|
|
// Exception: Do not boot again when the BootNext points to Boot Manager Menu.
|
|
//
|
|
EfiBootManagerBoot (&BootManagerMenu);
|
|
}
|
|
}
|
|
}
|
|
|
|
do {
|
|
//
|
|
// Retry to boot if any of the boot succeeds
|
|
//
|
|
LoadOptions = EfiBootManagerGetLoadOptions (&LoadOptionCount, LoadOptionTypeBoot);
|
|
BootSuccess = BootBootOptions (LoadOptions, LoadOptionCount, (BootManagerMenuStatus != EFI_NOT_FOUND) ? &BootManagerMenu : NULL);
|
|
EfiBootManagerFreeLoadOptions (LoadOptions, LoadOptionCount);
|
|
} while (BootSuccess);
|
|
}
|
|
|
|
if (!BootSuccess) {
|
|
LoadOptions = EfiBootManagerGetLoadOptions (&LoadOptionCount, LoadOptionTypePlatformRecovery);
|
|
ProcessLoadOptions (LoadOptions, LoadOptionCount);
|
|
EfiBootManagerFreeLoadOptions (LoadOptions, LoadOptionCount);
|
|
}
|
|
|
|
//
|
|
// If BootManagerMenu is available, fall back to it indefinitely.
|
|
//
|
|
if (BootManagerMenuStatus != EFI_NOT_FOUND) {
|
|
BdsBootManagerMenuLoop (&BootManagerMenu);
|
|
}
|
|
|
|
DEBUG ((EFI_D_ERROR, "[Bds] Unable to boot!\n"));
|
|
CpuDeadLoop ();
|
|
}
|
|
|
|
/**
|
|
Set the variable and report the error through status code upon failure.
|
|
|
|
@param VariableName A Null-terminated string that is the name of the vendor's variable.
|
|
Each VariableName is unique for each VendorGuid. VariableName must
|
|
contain 1 or more characters. If VariableName is an empty string,
|
|
then EFI_INVALID_PARAMETER is returned.
|
|
@param VendorGuid A unique identifier for the vendor.
|
|
@param Attributes Attributes bitmask to set for the variable.
|
|
@param DataSize The size in bytes of the Data buffer. Unless the EFI_VARIABLE_APPEND_WRITE,
|
|
or EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS attribute is set, a size of zero
|
|
causes the variable to be deleted. When the EFI_VARIABLE_APPEND_WRITE attribute is
|
|
set, then a SetVariable() call with a DataSize of zero will not cause any change to
|
|
the variable value (the timestamp associated with the variable may be updated however
|
|
even if no new data value is provided,see the description of the
|
|
EFI_VARIABLE_AUTHENTICATION_2 descriptor below. In this case the DataSize will not
|
|
be zero since the EFI_VARIABLE_AUTHENTICATION_2 descriptor will be populated).
|
|
@param Data The contents for the variable.
|
|
|
|
@retval EFI_SUCCESS The firmware has successfully stored the variable and its data as
|
|
defined by the Attributes.
|
|
@retval EFI_INVALID_PARAMETER An invalid combination of attribute bits, name, and GUID was supplied, or the
|
|
DataSize exceeds the maximum allowed.
|
|
@retval EFI_INVALID_PARAMETER VariableName is an empty string.
|
|
@retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the variable and its data.
|
|
@retval EFI_DEVICE_ERROR The variable could not be retrieved due to a hardware error.
|
|
@retval EFI_WRITE_PROTECTED The variable in question is read-only.
|
|
@retval EFI_WRITE_PROTECTED The variable in question cannot be deleted.
|
|
@retval EFI_SECURITY_VIOLATION The variable could not be written due to EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACESS
|
|
being set, but the AuthInfo does NOT pass the validation check carried out by the firmware.
|
|
|
|
@retval EFI_NOT_FOUND The variable trying to be updated or deleted was not found.
|
|
**/
|
|
EFI_STATUS
|
|
BdsDxeSetVariableAndReportStatusCodeOnError (
|
|
IN CHAR16 *VariableName,
|
|
IN EFI_GUID *VendorGuid,
|
|
IN UINT32 Attributes,
|
|
IN UINTN DataSize,
|
|
IN VOID *Data
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EDKII_SET_VARIABLE_STATUS *SetVariableStatus;
|
|
UINTN NameSize;
|
|
|
|
Status = gRT->SetVariable (
|
|
VariableName,
|
|
VendorGuid,
|
|
Attributes,
|
|
DataSize,
|
|
Data
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
NameSize = StrSize (VariableName);
|
|
SetVariableStatus = AllocatePool (sizeof (EDKII_SET_VARIABLE_STATUS) + NameSize + DataSize);
|
|
if (SetVariableStatus != NULL) {
|
|
CopyGuid (&SetVariableStatus->Guid, VendorGuid);
|
|
SetVariableStatus->NameSize = NameSize;
|
|
SetVariableStatus->DataSize = DataSize;
|
|
SetVariableStatus->SetStatus = Status;
|
|
SetVariableStatus->Attributes = Attributes;
|
|
CopyMem (SetVariableStatus + 1, VariableName, NameSize);
|
|
CopyMem (((UINT8 *) (SetVariableStatus + 1)) + NameSize, Data, DataSize);
|
|
|
|
REPORT_STATUS_CODE_EX (
|
|
EFI_ERROR_CODE,
|
|
PcdGet32 (PcdErrorCodeSetVariable),
|
|
0,
|
|
NULL,
|
|
&gEdkiiStatusCodeDataTypeVariableGuid,
|
|
SetVariableStatus,
|
|
sizeof (EDKII_SET_VARIABLE_STATUS) + NameSize + DataSize
|
|
);
|
|
|
|
FreePool (SetVariableStatus);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|