diff --git a/OvmfPkg/Include/Library/PlatformBmPrintScLib.h b/OvmfPkg/Include/Library/PlatformBmPrintScLib.h new file mode 100644 index 0000000000..1777f9d7c9 --- /dev/null +++ b/OvmfPkg/Include/Library/PlatformBmPrintScLib.h @@ -0,0 +1,41 @@ +/** @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). + + Copyright (C) 2019, Red Hat, Inc. + + 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. +**/ + +#ifndef __PLATFORM_BM_PRINT_SC_LIB__ +#define __PLATFORM_BM_PRINT_SC_LIB__ + +#include + +/** + 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 + ); + +#endif // __PLATFORM_BM_PRINT_SC_LIB__ diff --git a/OvmfPkg/Library/PlatformBmPrintScLib/PlatformBmPrintScLib.inf b/OvmfPkg/Library/PlatformBmPrintScLib/PlatformBmPrintScLib.inf new file mode 100644 index 0000000000..8f02e0b482 --- /dev/null +++ b/OvmfPkg/Library/PlatformBmPrintScLib/PlatformBmPrintScLib.inf @@ -0,0 +1,66 @@ +## @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. +# +# 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. +## + +[Defines] + INF_VERSION = 1.27 + BASE_NAME = PlatformBmPrintScLib + FILE_GUID = 3417c705-903e-41a7-9485-3fafebf60917 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + LIBRARY_CLASS = PlatformBmPrintScLib|DXE_DRIVER + +[Sources] + StatusCodeHandler.c + +[Packages] + MdeModulePkg/MdeModulePkg.dec + MdePkg/MdePkg.dec + +[LibraryClasses] + BaseMemoryLib + DebugLib + DevicePathLib + MemoryAllocationLib + PcdLib + PrintLib + UefiBootManagerLib + UefiBootServicesTableLib + UefiLib + UefiRuntimeServicesTableLib + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdProgressCodeOsLoaderLoad ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdProgressCodeOsLoaderStart ## CONSUMES + +[Protocols] + gEfiRscHandlerProtocolGuid ## CONSUMES + +[Guids] + gEfiGlobalVariableGuid ## CONSUMES + gEfiStatusCodeSpecificDataGuid ## CONSUMES + +[Depex.common.DXE_DRIVER] + gEfiRscHandlerProtocolGuid AND gEfiVariableArchProtocolGuid diff --git a/OvmfPkg/Library/PlatformBmPrintScLib/StatusCodeHandler.c b/OvmfPkg/Library/PlatformBmPrintScLib/StatusCodeHandler.c new file mode 100644 index 0000000000..3afe9d9690 --- /dev/null +++ b/OvmfPkg/Library/PlatformBmPrintScLib/StatusCodeHandler.c @@ -0,0 +1,310 @@ +/** @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. + + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + + +// +// 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, + __FUNCTION__)); + 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, __FUNCTION__, &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, __FUNCTION__, (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, __FUNCTION__, 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""; + } + + // + // 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, __FUNCTION__, 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, __FUNCTION__, Status)); + StatusCodeRouter->Unregister (HandleStatusCode); + return Status; + } + + return EFI_SUCCESS; +} diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec index 7666297cf8..e50c6179a2 100644 --- a/OvmfPkg/OvmfPkg.dec +++ b/OvmfPkg/OvmfPkg.dec @@ -45,6 +45,11 @@ # access. PciCapPciSegmentLib|Include/Library/PciCapPciSegmentLib.h + ## @libraryclass Register a status code handler for printing the Boot + # Manager's LoadImage() and StartImage() preparations, and + # return codes, to the UEFI console. + PlatformBmPrintScLib|Include/Library/PlatformBmPrintScLib.h + ## @libraryclass Access QEMU's firmware configuration interface # QemuFwCfgLib|Include/Library/QemuFwCfgLib.h diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc index f9216af479..5b885590b2 100644 --- a/OvmfPkg/OvmfPkgIa32.dsc +++ b/OvmfPkg/OvmfPkgIa32.dsc @@ -348,6 +348,7 @@ UdpIoLib|MdeModulePkg/Library/DxeUdpIoLib/DxeUdpIoLib.inf DpcLib|MdeModulePkg/Library/DxeDpcLib/DxeDpcLib.inf PlatformBootManagerLib|OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf + PlatformBmPrintScLib|OvmfPkg/Library/PlatformBmPrintScLib/PlatformBmPrintScLib.inf QemuBootOrderLib|OvmfPkg/Library/QemuBootOrderLib/QemuBootOrderLib.inf CpuExceptionHandlerLib|UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf !if $(SMM_REQUIRE) == TRUE diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc index 1e470de744..bbf0853ee6 100644 --- a/OvmfPkg/OvmfPkgIa32X64.dsc +++ b/OvmfPkg/OvmfPkgIa32X64.dsc @@ -353,6 +353,7 @@ UdpIoLib|MdeModulePkg/Library/DxeUdpIoLib/DxeUdpIoLib.inf DpcLib|MdeModulePkg/Library/DxeDpcLib/DxeDpcLib.inf PlatformBootManagerLib|OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf + PlatformBmPrintScLib|OvmfPkg/Library/PlatformBmPrintScLib/PlatformBmPrintScLib.inf QemuBootOrderLib|OvmfPkg/Library/QemuBootOrderLib/QemuBootOrderLib.inf CpuExceptionHandlerLib|UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf !if $(SMM_REQUIRE) == TRUE diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc index e4929d8cf4..d81460f520 100644 --- a/OvmfPkg/OvmfPkgX64.dsc +++ b/OvmfPkg/OvmfPkgX64.dsc @@ -353,6 +353,7 @@ UdpIoLib|MdeModulePkg/Library/DxeUdpIoLib/DxeUdpIoLib.inf DpcLib|MdeModulePkg/Library/DxeDpcLib/DxeDpcLib.inf PlatformBootManagerLib|OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf + PlatformBmPrintScLib|OvmfPkg/Library/PlatformBmPrintScLib/PlatformBmPrintScLib.inf QemuBootOrderLib|OvmfPkg/Library/QemuBootOrderLib/QemuBootOrderLib.inf CpuExceptionHandlerLib|UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf !if $(SMM_REQUIRE) == TRUE