2 Register a status code handler for printing the Boot Manager's LoadImage()
3 and StartImage() preparations, and return codes, to the UEFI console.
5 This feature enables users that are not accustomed to analyzing the firmware
6 log to glean some information about UEFI boot option processing (loading and
9 This library instance filters out (ignores) status codes that are not
10 reported by the containing firmware module. The intent is to link this
11 library instance into BdsDxe via PlatformBootManagerLib (which BdsDxe depends
12 upon), then catch only those status codes that BdsDxe reports (which happens
13 via UefiBootManagerLib). Status codes reported by other modules (such as
14 UiApp), via UefiBootManagerLib or otherwise, are meant to be ignored.
16 Copyright (C) 2019, Red Hat, Inc.
18 SPDX-License-Identifier: BSD-2-Clause-Patent
21 #include <Library/BaseMemoryLib.h>
22 #include <Library/DebugLib.h>
23 #include <Library/DevicePathLib.h>
24 #include <Library/MemoryAllocationLib.h>
25 #include <Library/PcdLib.h>
26 #include <Library/PrintLib.h>
27 #include <Library/UefiBootManagerLib.h>
28 #include <Library/UefiBootServicesTableLib.h>
29 #include <Library/UefiLib.h>
30 #include <Library/UefiRuntimeServicesTableLib.h>
32 #include <Protocol/ReportStatusCodeHandler.h>
34 #include <Guid/GlobalVariable.h>
35 #include <Guid/StatusCodeDataTypeId.h>
37 #include <Pi/PiStatusCode.h>
40 // Convenience variables for the status codes that are relevant for LoadImage()
41 // and StartImage() preparations and return codes.
43 STATIC EFI_STATUS_CODE_VALUE mLoadPrep
;
44 STATIC EFI_STATUS_CODE_VALUE mLoadFail
;
45 STATIC EFI_STATUS_CODE_VALUE mStartPrep
;
46 STATIC EFI_STATUS_CODE_VALUE mStartFail
;
49 Handle status codes reported through ReportStatusCodeLib /
50 EFI_STATUS_CODE_PROTOCOL.ReportStatusCode(). Format matching status codes to
53 The highest TPL at which this handler can be registered with
54 EFI_RSC_HANDLER_PROTOCOL.Register() is TPL_CALLBACK. That's because
55 HandleStatusCode() uses the UEFI variable services.
57 The parameter list of this function precisely matches that of
58 EFI_STATUS_CODE_PROTOCOL.ReportStatusCode().
60 The return status of this function is ignored by the caller, but the function
61 still returns sensible codes:
63 @retval EFI_SUCCESS The status code has been processed; either
64 as a no-op, due to filtering, or by
65 formatting it to the system console.
67 @retval EFI_INVALID_PARAMETER Unknown or malformed contents have been
70 @retval EFI_INCOMPATIBLE_VERSION Unexpected UEFI variable behavior has been
73 @return Error codes propagated from underlying
80 IN EFI_STATUS_CODE_TYPE CodeType
,
81 IN EFI_STATUS_CODE_VALUE Value
,
83 IN EFI_GUID
*CallerId
,
84 IN EFI_STATUS_CODE_DATA
*Data
90 CHAR16 BootOptionName
[ARRAY_SIZE (L
"Boot####")];
91 EFI_BOOT_MANAGER_LOAD_OPTION BmBootOption
;
92 BOOLEAN DevPathStringIsDynamic
;
93 CHAR16
*DevPathString
;
96 // Ignore all status codes that are irrelevant for LoadImage() and
97 // StartImage() preparations and return codes.
99 if ((Value
!= mLoadPrep
) && (Value
!= mLoadFail
) &&
100 (Value
!= mStartPrep
) && (Value
!= mStartFail
))
106 // Ignore status codes that are not reported by the same containing module.
108 if (!CompareGuid (CallerId
, &gEfiCallerIdGuid
)) {
113 // Sanity-check Data in case of failure reports.
115 if (((Value
== mLoadFail
) || (Value
== mStartFail
)) &&
117 (Data
->HeaderSize
!= sizeof *Data
) ||
118 (Data
->Size
!= sizeof (EFI_RETURN_STATUS_EXTENDED_DATA
) - sizeof *Data
) ||
119 !CompareGuid (&Data
->Type
, &gEfiStatusCodeSpecificDataGuid
)))
123 "%a:%a: malformed Data\n",
127 return EFI_INVALID_PARAMETER
;
131 // Get the number of the Boot#### option that the status code applies to.
133 VariableSize
= sizeof BootCurrent
;
134 Status
= gRT
->GetVariable (
135 EFI_BOOT_CURRENT_VARIABLE_NAME
,
136 &gEfiGlobalVariableGuid
,
137 NULL
/* Attributes */,
141 if (EFI_ERROR (Status
)) {
144 "%a:%a: failed to get %g:\"%s\": %r\n",
147 &gEfiGlobalVariableGuid
,
148 EFI_BOOT_CURRENT_VARIABLE_NAME
,
154 if (VariableSize
!= sizeof BootCurrent
) {
157 "%a:%a: got %Lu bytes for %g:\"%s\", expected %Lu\n",
160 (UINT64
)VariableSize
,
161 &gEfiGlobalVariableGuid
,
162 EFI_BOOT_CURRENT_VARIABLE_NAME
,
163 (UINT64
)sizeof BootCurrent
165 return EFI_INCOMPATIBLE_VERSION
;
169 // Get the Boot#### option that the status code applies to.
173 sizeof BootOptionName
,
177 Status
= EfiBootManagerVariableToLoadOption (BootOptionName
, &BmBootOption
);
178 if (EFI_ERROR (Status
)) {
181 "%a:%a: EfiBootManagerVariableToLoadOption(\"%s\"): %r\n",
191 // Format the device path.
193 DevPathStringIsDynamic
= TRUE
;
194 DevPathString
= ConvertDevicePathToText (
195 BmBootOption
.FilePath
,
196 FALSE
, // DisplayOnly
197 FALSE
// AllowShortcuts
199 if (DevPathString
== NULL
) {
200 DevPathStringIsDynamic
= FALSE
;
201 DevPathString
= L
"<out of memory while formatting device path>";
205 // Print the message to the console.
207 if ((Value
== mLoadPrep
) || (Value
== mStartPrep
)) {
209 L
"%a: %a %s \"%s\" from %s\n",
211 Value
== mLoadPrep
? "loading" : "starting",
213 BmBootOption
.Description
,
218 L
"%a: failed to %a %s \"%s\" from %s: %r\n",
220 Value
== mLoadFail
? "load" : "start",
222 BmBootOption
.Description
,
224 ((EFI_RETURN_STATUS_EXTENDED_DATA
*)Data
)->ReturnStatus
231 if (DevPathStringIsDynamic
) {
232 FreePool (DevPathString
);
235 EfiBootManagerFreeLoadOption (&BmBootOption
);
240 Unregister HandleStatusCode() at ExitBootServices().
242 (See EFI_RSC_HANDLER_PROTOCOL in Volume 3 of the Platform Init spec.)
244 @param[in] Event Event whose notification function is being invoked.
246 @param[in] Context Pointer to EFI_RSC_HANDLER_PROTOCOL, originally looked up
247 when HandleStatusCode() was registered.
252 UnregisterAtExitBootServices (
257 EFI_RSC_HANDLER_PROTOCOL
*StatusCodeRouter
;
259 StatusCodeRouter
= Context
;
260 StatusCodeRouter
->Unregister (HandleStatusCode
);
264 Register a status code handler for printing the Boot Manager's LoadImage()
265 and StartImage() preparations, and return codes, to the UEFI console.
267 @retval EFI_SUCCESS The status code handler has been successfully
270 @return Error codes propagated from boot services and from
271 EFI_RSC_HANDLER_PROTOCOL.
275 PlatformBmPrintScRegisterHandler (
280 EFI_RSC_HANDLER_PROTOCOL
*StatusCodeRouter
;
281 EFI_EVENT ExitBootEvent
;
283 Status
= gBS
->LocateProtocol (
284 &gEfiRscHandlerProtocolGuid
,
285 NULL
/* Registration */,
286 (VOID
**)&StatusCodeRouter
288 ASSERT_EFI_ERROR (Status
);
289 if (EFI_ERROR (Status
)) {
294 // Set the EFI_STATUS_CODE_VALUE convenience variables.
296 mLoadPrep
= PcdGet32 (PcdProgressCodeOsLoaderLoad
);
297 mLoadFail
= (EFI_SOFTWARE_DXE_BS_DRIVER
|
298 EFI_SW_DXE_BS_EC_BOOT_OPTION_LOAD_ERROR
);
299 mStartPrep
= PcdGet32 (PcdProgressCodeOsLoaderStart
);
300 mStartFail
= (EFI_SOFTWARE_DXE_BS_DRIVER
|
301 EFI_SW_DXE_BS_EC_BOOT_OPTION_FAILED
);
304 // Register the handler callback.
306 Status
= StatusCodeRouter
->Register (HandleStatusCode
, TPL_CALLBACK
);
307 if (EFI_ERROR (Status
)) {
310 "%a:%a: failed to register status code handler: %r\n",
319 // Status code reporting and routing/handling extend into OS runtime. Since
320 // we don't want our handler to survive the BDS phase, we have to unregister
321 // the callback at ExitBootServices(). (See EFI_RSC_HANDLER_PROTOCOL in
322 // Volume 3 of the Platform Init spec.)
324 Status
= gBS
->CreateEvent (
325 EVT_SIGNAL_EXIT_BOOT_SERVICES
, // Type
326 TPL_CALLBACK
, // NotifyTpl
327 UnregisterAtExitBootServices
, // NotifyFunction
328 StatusCodeRouter
, // NotifyContext
329 &ExitBootEvent
// Event
331 if (EFI_ERROR (Status
)) {
333 // We have to unregister the callback right now, and fail the function.
337 "%a:%a: failed to create ExitBootServices() event: "
343 StatusCodeRouter
->Unregister (HandleStatusCode
);