mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-12-04 13:23:26 +01:00
349 lines
10 KiB
C
349 lines
10 KiB
C
|
/** @file
|
||
|
Register a status code handler for printing the Boot Manager's LoadImage()
|
||
|
and StartImage() preparations, and return codes, to the UEFI console.
|
||
|
|
||
|
This feature enables users that are not accustomed to analyzing the firmware
|
||
|
log to glean some information about UEFI boot option processing (loading and
|
||
|
starting).
|
||
|
|
||
|
This library instance filters out (ignores) status codes that are not
|
||
|
reported by the containing firmware module. The intent is to link this
|
||
|
library instance into BdsDxe via PlatformBootManagerLib (which BdsDxe depends
|
||
|
upon), then catch only those status codes that BdsDxe reports (which happens
|
||
|
via UefiBootManagerLib). Status codes reported by other modules (such as
|
||
|
UiApp), via UefiBootManagerLib or otherwise, are meant to be ignored.
|
||
|
|
||
|
Copyright (C) 2019, Red Hat, Inc.
|
||
|
|
||
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
||
|
**/
|
||
|
|
||
|
#include <Library/BaseMemoryLib.h>
|
||
|
#include <Library/DebugLib.h>
|
||
|
#include <Library/DevicePathLib.h>
|
||
|
#include <Library/MemoryAllocationLib.h>
|
||
|
#include <Library/PcdLib.h>
|
||
|
#include <Library/PrintLib.h>
|
||
|
#include <Library/UefiBootManagerLib.h>
|
||
|
#include <Library/UefiBootServicesTableLib.h>
|
||
|
#include <Library/UefiLib.h>
|
||
|
#include <Library/UefiRuntimeServicesTableLib.h>
|
||
|
|
||
|
#include <Protocol/ReportStatusCodeHandler.h>
|
||
|
|
||
|
#include <Guid/GlobalVariable.h>
|
||
|
#include <Guid/StatusCodeDataTypeId.h>
|
||
|
|
||
|
#include <Pi/PiStatusCode.h>
|
||
|
|
||
|
//
|
||
|
// Convenience variables for the status codes that are relevant for LoadImage()
|
||
|
// and StartImage() preparations and return codes.
|
||
|
//
|
||
|
STATIC EFI_STATUS_CODE_VALUE mLoadPrep;
|
||
|
STATIC EFI_STATUS_CODE_VALUE mLoadFail;
|
||
|
STATIC EFI_STATUS_CODE_VALUE mStartPrep;
|
||
|
STATIC EFI_STATUS_CODE_VALUE mStartFail;
|
||
|
|
||
|
/**
|
||
|
Handle status codes reported through ReportStatusCodeLib /
|
||
|
EFI_STATUS_CODE_PROTOCOL.ReportStatusCode(). Format matching status codes to
|
||
|
the system console.
|
||
|
|
||
|
The highest TPL at which this handler can be registered with
|
||
|
EFI_RSC_HANDLER_PROTOCOL.Register() is TPL_CALLBACK. That's because
|
||
|
HandleStatusCode() uses the UEFI variable services.
|
||
|
|
||
|
The parameter list of this function precisely matches that of
|
||
|
EFI_STATUS_CODE_PROTOCOL.ReportStatusCode().
|
||
|
|
||
|
The return status of this function is ignored by the caller, but the function
|
||
|
still returns sensible codes:
|
||
|
|
||
|
@retval EFI_SUCCESS The status code has been processed; either
|
||
|
as a no-op, due to filtering, or by
|
||
|
formatting it to the system console.
|
||
|
|
||
|
@retval EFI_INVALID_PARAMETER Unknown or malformed contents have been
|
||
|
detected in Data.
|
||
|
|
||
|
@retval EFI_INCOMPATIBLE_VERSION Unexpected UEFI variable behavior has been
|
||
|
encountered.
|
||
|
|
||
|
@return Error codes propagated from underlying
|
||
|
services.
|
||
|
**/
|
||
|
STATIC
|
||
|
EFI_STATUS
|
||
|
EFIAPI
|
||
|
HandleStatusCode (
|
||
|
IN EFI_STATUS_CODE_TYPE CodeType,
|
||
|
IN EFI_STATUS_CODE_VALUE Value,
|
||
|
IN UINT32 Instance,
|
||
|
IN EFI_GUID *CallerId,
|
||
|
IN EFI_STATUS_CODE_DATA *Data
|
||
|
)
|
||
|
{
|
||
|
UINTN VariableSize;
|
||
|
UINT16 BootCurrent;
|
||
|
EFI_STATUS Status;
|
||
|
CHAR16 BootOptionName[ARRAY_SIZE (L"Boot####")];
|
||
|
EFI_BOOT_MANAGER_LOAD_OPTION BmBootOption;
|
||
|
BOOLEAN DevPathStringIsDynamic;
|
||
|
CHAR16 *DevPathString;
|
||
|
|
||
|
//
|
||
|
// Ignore all status codes that are irrelevant for LoadImage() and
|
||
|
// StartImage() preparations and return codes.
|
||
|
//
|
||
|
if ((Value != mLoadPrep) && (Value != mLoadFail) &&
|
||
|
(Value != mStartPrep) && (Value != mStartFail))
|
||
|
{
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Ignore status codes that are not reported by the same containing module.
|
||
|
//
|
||
|
if (!CompareGuid (CallerId, &gEfiCallerIdGuid)) {
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Sanity-check Data in case of failure reports.
|
||
|
//
|
||
|
if (((Value == mLoadFail) || (Value == mStartFail)) &&
|
||
|
((Data == NULL) ||
|
||
|
(Data->HeaderSize != sizeof *Data) ||
|
||
|
(Data->Size != sizeof (EFI_RETURN_STATUS_EXTENDED_DATA) - sizeof *Data) ||
|
||
|
!CompareGuid (&Data->Type, &gEfiStatusCodeSpecificDataGuid)))
|
||
|
{
|
||
|
DEBUG ((
|
||
|
DEBUG_ERROR,
|
||
|
"%a:%a: malformed Data\n",
|
||
|
gEfiCallerBaseName,
|
||
|
__func__
|
||
|
));
|
||
|
return EFI_INVALID_PARAMETER;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Get the number of the Boot#### option that the status code applies to.
|
||
|
//
|
||
|
VariableSize = sizeof BootCurrent;
|
||
|
Status = gRT->GetVariable (
|
||
|
EFI_BOOT_CURRENT_VARIABLE_NAME,
|
||
|
&gEfiGlobalVariableGuid,
|
||
|
NULL /* Attributes */,
|
||
|
&VariableSize,
|
||
|
&BootCurrent
|
||
|
);
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
DEBUG ((
|
||
|
DEBUG_ERROR,
|
||
|
"%a:%a: failed to get %g:\"%s\": %r\n",
|
||
|
gEfiCallerBaseName,
|
||
|
__func__,
|
||
|
&gEfiGlobalVariableGuid,
|
||
|
EFI_BOOT_CURRENT_VARIABLE_NAME,
|
||
|
Status
|
||
|
));
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
if (VariableSize != sizeof BootCurrent) {
|
||
|
DEBUG ((
|
||
|
DEBUG_ERROR,
|
||
|
"%a:%a: got %Lu bytes for %g:\"%s\", expected %Lu\n",
|
||
|
gEfiCallerBaseName,
|
||
|
__func__,
|
||
|
(UINT64)VariableSize,
|
||
|
&gEfiGlobalVariableGuid,
|
||
|
EFI_BOOT_CURRENT_VARIABLE_NAME,
|
||
|
(UINT64)sizeof BootCurrent
|
||
|
));
|
||
|
return EFI_INCOMPATIBLE_VERSION;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Get the Boot#### option that the status code applies to.
|
||
|
//
|
||
|
UnicodeSPrint (
|
||
|
BootOptionName,
|
||
|
sizeof BootOptionName,
|
||
|
L"Boot%04x",
|
||
|
BootCurrent
|
||
|
);
|
||
|
Status = EfiBootManagerVariableToLoadOption (BootOptionName, &BmBootOption);
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
DEBUG ((
|
||
|
DEBUG_ERROR,
|
||
|
"%a:%a: EfiBootManagerVariableToLoadOption(\"%s\"): %r\n",
|
||
|
gEfiCallerBaseName,
|
||
|
__func__,
|
||
|
BootOptionName,
|
||
|
Status
|
||
|
));
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Format the device path.
|
||
|
//
|
||
|
DevPathStringIsDynamic = TRUE;
|
||
|
DevPathString = ConvertDevicePathToText (
|
||
|
BmBootOption.FilePath,
|
||
|
FALSE, // DisplayOnly
|
||
|
FALSE // AllowShortcuts
|
||
|
);
|
||
|
if (DevPathString == NULL) {
|
||
|
DevPathStringIsDynamic = FALSE;
|
||
|
DevPathString = L"<out of memory while formatting device path>";
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Print the message to the console.
|
||
|
//
|
||
|
if ((Value == mLoadPrep) || (Value == mStartPrep)) {
|
||
|
Print (
|
||
|
L"%a: %a %s \"%s\" from %s\n",
|
||
|
gEfiCallerBaseName,
|
||
|
Value == mLoadPrep ? "loading" : "starting",
|
||
|
BootOptionName,
|
||
|
BmBootOption.Description,
|
||
|
DevPathString
|
||
|
);
|
||
|
} else {
|
||
|
Print (
|
||
|
L"%a: failed to %a %s \"%s\" from %s: %r\n",
|
||
|
gEfiCallerBaseName,
|
||
|
Value == mLoadFail ? "load" : "start",
|
||
|
BootOptionName,
|
||
|
BmBootOption.Description,
|
||
|
DevPathString,
|
||
|
((EFI_RETURN_STATUS_EXTENDED_DATA *)Data)->ReturnStatus
|
||
|
);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Done.
|
||
|
//
|
||
|
if (DevPathStringIsDynamic) {
|
||
|
FreePool (DevPathString);
|
||
|
}
|
||
|
|
||
|
EfiBootManagerFreeLoadOption (&BmBootOption);
|
||
|
return EFI_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Unregister HandleStatusCode() at ExitBootServices().
|
||
|
|
||
|
(See EFI_RSC_HANDLER_PROTOCOL in Volume 3 of the Platform Init spec.)
|
||
|
|
||
|
@param[in] Event Event whose notification function is being invoked.
|
||
|
|
||
|
@param[in] Context Pointer to EFI_RSC_HANDLER_PROTOCOL, originally looked up
|
||
|
when HandleStatusCode() was registered.
|
||
|
**/
|
||
|
STATIC
|
||
|
VOID
|
||
|
EFIAPI
|
||
|
UnregisterAtExitBootServices (
|
||
|
IN EFI_EVENT Event,
|
||
|
IN VOID *Context
|
||
|
)
|
||
|
{
|
||
|
EFI_RSC_HANDLER_PROTOCOL *StatusCodeRouter;
|
||
|
|
||
|
StatusCodeRouter = Context;
|
||
|
StatusCodeRouter->Unregister (HandleStatusCode);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Register a status code handler for printing the Boot Manager's LoadImage()
|
||
|
and StartImage() preparations, and return codes, to the UEFI console.
|
||
|
|
||
|
@retval EFI_SUCCESS The status code handler has been successfully
|
||
|
registered.
|
||
|
|
||
|
@return Error codes propagated from boot services and from
|
||
|
EFI_RSC_HANDLER_PROTOCOL.
|
||
|
**/
|
||
|
EFI_STATUS
|
||
|
EFIAPI
|
||
|
PlatformBmPrintScRegisterHandler (
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
EFI_STATUS Status;
|
||
|
EFI_RSC_HANDLER_PROTOCOL *StatusCodeRouter;
|
||
|
EFI_EVENT ExitBootEvent;
|
||
|
|
||
|
Status = gBS->LocateProtocol (
|
||
|
&gEfiRscHandlerProtocolGuid,
|
||
|
NULL /* Registration */,
|
||
|
(VOID **)&StatusCodeRouter
|
||
|
);
|
||
|
ASSERT_EFI_ERROR (Status);
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Set the EFI_STATUS_CODE_VALUE convenience variables.
|
||
|
//
|
||
|
mLoadPrep = PcdGet32 (PcdProgressCodeOsLoaderLoad);
|
||
|
mLoadFail = (EFI_SOFTWARE_DXE_BS_DRIVER |
|
||
|
EFI_SW_DXE_BS_EC_BOOT_OPTION_LOAD_ERROR);
|
||
|
mStartPrep = PcdGet32 (PcdProgressCodeOsLoaderStart);
|
||
|
mStartFail = (EFI_SOFTWARE_DXE_BS_DRIVER |
|
||
|
EFI_SW_DXE_BS_EC_BOOT_OPTION_FAILED);
|
||
|
|
||
|
//
|
||
|
// Register the handler callback.
|
||
|
//
|
||
|
Status = StatusCodeRouter->Register (HandleStatusCode, TPL_CALLBACK);
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
DEBUG ((
|
||
|
DEBUG_ERROR,
|
||
|
"%a:%a: failed to register status code handler: %r\n",
|
||
|
gEfiCallerBaseName,
|
||
|
__func__,
|
||
|
Status
|
||
|
));
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Status code reporting and routing/handling extend into OS runtime. Since
|
||
|
// we don't want our handler to survive the BDS phase, we have to unregister
|
||
|
// the callback at ExitBootServices(). (See EFI_RSC_HANDLER_PROTOCOL in
|
||
|
// Volume 3 of the Platform Init spec.)
|
||
|
//
|
||
|
Status = gBS->CreateEvent (
|
||
|
EVT_SIGNAL_EXIT_BOOT_SERVICES, // Type
|
||
|
TPL_CALLBACK, // NotifyTpl
|
||
|
UnregisterAtExitBootServices, // NotifyFunction
|
||
|
StatusCodeRouter, // NotifyContext
|
||
|
&ExitBootEvent // Event
|
||
|
);
|
||
|
if (EFI_ERROR (Status)) {
|
||
|
//
|
||
|
// We have to unregister the callback right now, and fail the function.
|
||
|
//
|
||
|
DEBUG ((
|
||
|
DEBUG_ERROR,
|
||
|
"%a:%a: failed to create ExitBootServices() event: "
|
||
|
"%r\n",
|
||
|
gEfiCallerBaseName,
|
||
|
__func__,
|
||
|
Status
|
||
|
));
|
||
|
StatusCodeRouter->Unregister (HandleStatusCode);
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
return EFI_SUCCESS;
|
||
|
}
|