From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=209.132.183.28; helo=mx1.redhat.com; envelope-from=lersek@redhat.com; receiver=edk2-devel@lists.01.org Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id A8E2F2034C5F5 for ; Wed, 22 Nov 2017 15:54:45 -0800 (PST) Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 2EA685AFED; Wed, 22 Nov 2017 23:59:02 +0000 (UTC) Received: from lacos-laptop-7.usersys.redhat.com (ovpn-120-45.rdu2.redhat.com [10.10.120.45]) by smtp.corp.redhat.com (Postfix) with ESMTP id AA0D36178B; Wed, 22 Nov 2017 23:59:00 +0000 (UTC) From: Laszlo Ersek To: edk2-devel-01 Cc: Ard Biesheuvel , Jordan Justen , Ruiyu Ni , Anthony Perard , Julien Grall Date: Thu, 23 Nov 2017 00:58:48 +0100 Message-Id: <20171122235849.4177-5-lersek@redhat.com> In-Reply-To: <20171122235849.4177-1-lersek@redhat.com> References: <20171122235849.4177-1-lersek@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.39]); Wed, 22 Nov 2017 23:59:02 +0000 (UTC) Subject: [PATCH 4/5] OvmfPkg/PlatformBootManagerLib: print EDKII_OS_LOADER_DETAIL to ConOut X-BeenThere: edk2-devel@lists.01.org X-Mailman-Version: 2.1.22 Precedence: list List-Id: EDK II Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 22 Nov 2017 23:54:45 -0000 Parse and print the EDKII_OS_LOADER_DETAIL debug codes from UefiBootManagerLib (when it acts as part of BdsDxe -- not as part of UiApp, for example). In effect this displays LoadImage() and StartImage() attempts and failures on the splash screen, visibly to end-users. While at it, print two other (earlier) console messages about boot option generation and boot option filtering / reordering. Cc: Ard Biesheuvel Cc: Jordan Justen Cc: Ruiyu Ni Cc: Anthony Perard Cc: Julien Grall Ref: https://bugzilla.redhat.com/show_bug.cgi?id=1515418 Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Laszlo Ersek --- Notes: We can port this later to ArmVirtPkg. For that, first we'll have to replace commit 59541d41633c ("ArmVirtPkg: remove status code support", 2017-07-05) with a port of commit a6d594c5fabd ("OvmfPkg: use StatusCode Router and Handler from MdeModulePkg", 2016-08-03). OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf | 4 + OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.h | 15 + OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.c | 8 + OvmfPkg/Library/PlatformBootManagerLib/StatusCodeHandler.c | 298 ++++++++++++++++++++ 4 files changed, 325 insertions(+) diff --git a/OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf b/OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf index 27789b7377bc..36901fc39d95 100644 --- a/OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf +++ b/OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf @@ -21,24 +21,25 @@ [Defines] LIBRARY_CLASS = PlatformBootManagerLib|DXE_DRIVER # # The following information is for reference only and not required by the build tools. # # VALID_ARCHITECTURES = IA32 X64 IPF EBC # [Sources] BdsPlatform.c PlatformData.c QemuKernel.c + StatusCodeHandler.c BdsPlatform.h [Packages] MdePkg/MdePkg.dec MdeModulePkg/MdeModulePkg.dec IntelFrameworkModulePkg/IntelFrameworkModulePkg.dec SourceLevelDebugPkg/SourceLevelDebugPkg.dec OvmfPkg/OvmfPkg.dec [LibraryClasses] BaseLib MemoryAllocationLib @@ -54,28 +55,31 @@ [LibraryClasses] QemuFwCfgLib QemuFwCfgS3Lib LoadLinuxLib QemuBootOrderLib UefiLib [Pcd] gUefiOvmfPkgTokenSpaceGuid.PcdEmuVariableEvent gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashVariablesEnable gUefiOvmfPkgTokenSpaceGuid.PcdOvmfHostBridgePciDevId gEfiMdePkgTokenSpaceGuid.PcdPlatformBootTimeOut gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdShellFile + gEfiMdeModulePkgTokenSpaceGuid.PcdDebugCodeOsLoaderDetail [Pcd.IA32, Pcd.X64] gEfiMdePkgTokenSpaceGuid.PcdFSBClock [Protocols] gEfiDecompressProtocolGuid gEfiPciRootBridgeIoProtocolGuid gEfiS3SaveStateProtocolGuid # PROTOCOL SOMETIMES_CONSUMED gEfiDxeSmmReadyToLockProtocolGuid # PROTOCOL SOMETIMES_PRODUCED gEfiLoadedImageProtocolGuid # PROTOCOL SOMETIMES_PRODUCED gEfiFirmwareVolume2ProtocolGuid # PROTOCOL SOMETIMES_CONSUMED + gEfiRscHandlerProtocolGuid # PROTOCOL SOMETIMES_CONSUMED [Guids] gEfiXenInfoGuid gEfiEndOfDxeEventGroupGuid gRootBridgesConnectedEventGroupGuid + gEdkiiStatusCodeDataTypeOsLoaderDetailGuid diff --git a/OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.h b/OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.h index 97ffbb514825..493cfee85f54 100644 --- a/OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.h +++ b/OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.h @@ -183,13 +183,28 @@ PlatformInitializeConsole ( /** Loads and boots UEFI Linux via the FwCfg interface. @retval EFI_NOT_FOUND - The Linux kernel was not found **/ EFI_STATUS TryRunningQemuKernel ( VOID ); +/** + Register a status code handler for printing EDKII_OS_LOADER_DETAIL reports to + the 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 +RegisterStatusCodeHandler ( + VOID + ); + #endif // _PLATFORM_SPECIFIC_BDS_PLATFORM_H_ diff --git a/OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.c b/OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.c index 025252e72b39..429f2926d2af 100644 --- a/OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.c +++ b/OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.c @@ -1454,35 +1454,43 @@ Routine Description: BootLogoEnableLogo (); // // Perform some platform specific connect sequence // PlatformBdsConnectSequence (); // // Process QEMU's -kernel command line option // TryRunningQemuKernel (); + AsciiPrint ( + "%a: auto-generating trailing boot options for bootable devices\n", + gEfiCallerBaseName + ); EfiBootManagerRefreshAllBootOption (); // // Register UEFI Shell // PlatformRegisterFvBootOption ( PcdGetPtr (PcdShellFile), L"EFI Internal Shell", LOAD_OPTION_ACTIVE ); + AsciiPrint ("%a: filtering and reordering boot options\n", + gEfiCallerBaseName); RemoveStaleFvFileOptions (); SetBootOrderFromQemu (); + + RegisterStatusCodeHandler (); } /** This notification function is invoked when an instance of the EFI_DEVICE_PATH_PROTOCOL is produced. @param Event The event that occurred @param Context For EFI compatibility. Not used. **/ VOID EFIAPI diff --git a/OvmfPkg/Library/PlatformBootManagerLib/StatusCodeHandler.c b/OvmfPkg/Library/PlatformBootManagerLib/StatusCodeHandler.c new file mode 100644 index 000000000000..cec4fb6ed6ce --- /dev/null +++ b/OvmfPkg/Library/PlatformBootManagerLib/StatusCodeHandler.c @@ -0,0 +1,298 @@ +/** @file + Register a status code handler for printing EDKII_OS_LOADER_DETAIL reports to + the console. + + This feature enables users that are not accustomed to analyzing the OVMF + debug log to glean some information about UEFI boot option processing + (loading and starting). + + Copyright (C) 2017, 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 "BdsPlatform.h" + + +/** + 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_NOTIFY. That's because + AsciiPrint() uses EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL internally. + + 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 EFI_STATUS_CODE_DATA, or in the + EDKII_OS_LOADER_DETAIL payload within + EFI_STATUS_CODE_DATA. +**/ +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 + ) +{ + EDKII_OS_LOADER_DETAIL *OsLoaderDetail; + UINT8 *VariableSizeData; + CHAR16 *Description; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + BOOLEAN DevPathStringIsDynamic; + CHAR16 *DevPathString; + EFI_STATUS Status; + + // + // Ignore all status codes other than OsLoaderDetail. + // + if (Value != PcdGet32 (PcdDebugCodeOsLoaderDetail)) { + return EFI_SUCCESS; + } + + // + // The status codes we are interested in are emitted by UefiBootManagerLib. + // UefiBootManagerLib is built into several drivers and applications, e.g. + // BdsDxe and UiApp. Process (i.e., print to the console) only those status + // codes that come from BdsDxe; that is, from the driver module that this + // PlatformBootManagerLib instance is also built into. + // + if (!CompareGuid (CallerId, &gEfiCallerIdGuid)) { + return EFI_SUCCESS; + } + + // + // Sanity checks -- now that Value has been validated, we have expectations + // to enforce. + // + if ((Data == NULL) || + (Data->HeaderSize < sizeof *Data) || + (Data->Size < sizeof *OsLoaderDetail) || + (!CompareGuid (&Data->Type, + &gEdkiiStatusCodeDataTypeOsLoaderDetailGuid))) { + DEBUG (( + DEBUG_ERROR, + "%a: unknown or malformed data for status code 0x%x\n", + __FUNCTION__, + PcdGet32 (PcdDebugCodeOsLoaderDetail) + )); + return EFI_INVALID_PARAMETER; + } + + OsLoaderDetail = (EDKII_OS_LOADER_DETAIL *)( + (UINT8 *)Data + Data->HeaderSize + ); + + // + // More sanity checks. The additions on the RHS are carried out in UINTN and + // cannot overflow. + // + if (Data->Size < (sizeof *OsLoaderDetail + + OsLoaderDetail->DescriptionSize + + OsLoaderDetail->DevicePathSize)) { + DEBUG ((DEBUG_ERROR, "%a: malformed EDKII_OS_LOADER_DETAIL\n", + __FUNCTION__)); + return EFI_INVALID_PARAMETER; + } + + // + // Extract the known variable size fields from the payload. + // + VariableSizeData = (UINT8 *)(OsLoaderDetail + 1); + + Description = (CHAR16 *)VariableSizeData; + VariableSizeData += OsLoaderDetail->DescriptionSize; + + DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)VariableSizeData; + VariableSizeData += OsLoaderDetail->DevicePathSize; + + ASSERT (VariableSizeData - (UINT8 *)OsLoaderDetail <= Data->Size); + + // + // Prepare the extracted variable size fields for printing. + // + if (OsLoaderDetail->DescriptionSize == 0) { + Description = L""; + } + + DevPathStringIsDynamic = FALSE; + if (OsLoaderDetail->DevicePathSize == 0) { + DevPathString = L""; + } else { + DevPathString = ConvertDevicePathToText ( + DevicePath, + FALSE, // DisplayOnly + FALSE // AllowShortcuts + ); + if (DevPathString == NULL) { + DevPathString = L""; + } else { + DevPathStringIsDynamic = TRUE; + } + } + + // + // Print the message to the console. + // + switch (OsLoaderDetail->Type) { + case EDKII_OS_LOADER_DETAIL_TYPE_LOAD: + case EDKII_OS_LOADER_DETAIL_TYPE_START: + AsciiPrint ( + "%a: %a Boot%04x \"%s\" from %s\n", + gEfiCallerBaseName, + (OsLoaderDetail->Type == EDKII_OS_LOADER_DETAIL_TYPE_LOAD ? + "loading" : + "starting"), + OsLoaderDetail->BootOptionNumber, + Description, + DevPathString + ); + break; + + case EDKII_OS_LOADER_DETAIL_TYPE_LOAD_ERROR: + case EDKII_OS_LOADER_DETAIL_TYPE_START_ERROR: + AsciiPrint ( + "%a: failed to %a Boot%04x \"%s\" from %s: %r\n", + gEfiCallerBaseName, + (OsLoaderDetail->Type == EDKII_OS_LOADER_DETAIL_TYPE_LOAD_ERROR ? + "load" : + "start"), + OsLoaderDetail->BootOptionNumber, + Description, + DevPathString, + OsLoaderDetail->Status + ); + break; + + default: + DEBUG ((DEBUG_ERROR, "%a: unknown EDKII_OS_LOADER_DETAIL.Type 0x%x\n", + __FUNCTION__, OsLoaderDetail->Type)); + Status = EFI_INVALID_PARAMETER; + goto ReleaseDevPathString; + } + + Status = EFI_SUCCESS; + +ReleaseDevPathString: + if (DevPathStringIsDynamic) { + FreePool (DevPathString); + } + return Status; +} + + +/** + 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 EDKII_OS_LOADER_DETAIL reports to + the 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 +RegisterStatusCodeHandler ( + VOID + ) +{ + EFI_STATUS Status; + EFI_RSC_HANDLER_PROTOCOL *StatusCodeRouter; + EFI_EVENT ExitBootEvent; + + Status = gBS->LocateProtocol (&gEfiRscHandlerProtocolGuid, + NULL /* Registration */, (VOID **)&StatusCodeRouter); + // + // This protocol is provided by the ReportStatusCodeRouterRuntimeDxe driver + // that we build into the firmware image. Given that PlatformBootManagerLib + // is used as part of BdsDxe, and BDS Entry occurs after all DXE drivers have + // been dispatched, the EFI_RSC_HANDLER_PROTOCOL is available at this point. + // + ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Register the handler callback. + // + Status = StatusCodeRouter->Register (HandleStatusCode, TPL_CALLBACK); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: failed to register status code handler: %r\n", + __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: failed to create ExitBootServices() event: %r\n", + __FUNCTION__, Status)); + StatusCodeRouter->Unregister (HandleStatusCode); + return Status; + } + + return EFI_SUCCESS; +} -- 2.14.1.3.gb7cf6e02401b