VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/FirmwareNew/OvmfPkg/Library/PlatformBmPrintScLib/StatusCodeHandler.c

Last change on this file was 103938, checked in by vboxsync, 13 months ago

Devices/EFI/Firmware: Don't print the loading/starting messages to the screen and serial ports

  • Property svn:eol-style set to native
File size: 10.2 KB
Line 
1/** @file
2 Register a status code handler for printing the Boot Manager's LoadImage()
3 and StartImage() preparations, and return codes, to the UEFI console.
4
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
7 starting).
8
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.
15
16 Copyright (C) 2019, Red Hat, Inc.
17
18 SPDX-License-Identifier: BSD-2-Clause-Patent
19**/
20
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>
31
32#include <Protocol/ReportStatusCodeHandler.h>
33
34#include <Guid/GlobalVariable.h>
35#include <Guid/StatusCodeDataTypeId.h>
36
37#include <Pi/PiStatusCode.h>
38
39//
40// Convenience variables for the status codes that are relevant for LoadImage()
41// and StartImage() preparations and return codes.
42//
43STATIC EFI_STATUS_CODE_VALUE mLoadPrep;
44STATIC EFI_STATUS_CODE_VALUE mLoadFail;
45STATIC EFI_STATUS_CODE_VALUE mStartPrep;
46STATIC EFI_STATUS_CODE_VALUE mStartFail;
47
48/**
49 Handle status codes reported through ReportStatusCodeLib /
50 EFI_STATUS_CODE_PROTOCOL.ReportStatusCode(). Format matching status codes to
51 the system console.
52
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.
56
57 The parameter list of this function precisely matches that of
58 EFI_STATUS_CODE_PROTOCOL.ReportStatusCode().
59
60 The return status of this function is ignored by the caller, but the function
61 still returns sensible codes:
62
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.
66
67 @retval EFI_INVALID_PARAMETER Unknown or malformed contents have been
68 detected in Data.
69
70 @retval EFI_INCOMPATIBLE_VERSION Unexpected UEFI variable behavior has been
71 encountered.
72
73 @return Error codes propagated from underlying
74 services.
75**/
76STATIC
77EFI_STATUS
78EFIAPI
79HandleStatusCode (
80 IN EFI_STATUS_CODE_TYPE CodeType,
81 IN EFI_STATUS_CODE_VALUE Value,
82 IN UINT32 Instance,
83 IN EFI_GUID *CallerId,
84 IN EFI_STATUS_CODE_DATA *Data
85 )
86{
87 UINTN VariableSize;
88 UINT16 BootCurrent;
89 EFI_STATUS Status;
90 CHAR16 BootOptionName[ARRAY_SIZE (L"Boot####")];
91 EFI_BOOT_MANAGER_LOAD_OPTION BmBootOption;
92 BOOLEAN DevPathStringIsDynamic;
93 CHAR16 *DevPathString;
94
95 //
96 // Ignore all status codes that are irrelevant for LoadImage() and
97 // StartImage() preparations and return codes.
98 //
99 if ((Value != mLoadPrep) && (Value != mLoadFail) &&
100 (Value != mStartPrep) && (Value != mStartFail))
101 {
102 return EFI_SUCCESS;
103 }
104
105 //
106 // Ignore status codes that are not reported by the same containing module.
107 //
108 if (!CompareGuid (CallerId, &gEfiCallerIdGuid)) {
109 return EFI_SUCCESS;
110 }
111
112 //
113 // Sanity-check Data in case of failure reports.
114 //
115 if (((Value == mLoadFail) || (Value == mStartFail)) &&
116 ((Data == NULL) ||
117 (Data->HeaderSize != sizeof *Data) ||
118 (Data->Size != sizeof (EFI_RETURN_STATUS_EXTENDED_DATA) - sizeof *Data) ||
119 !CompareGuid (&Data->Type, &gEfiStatusCodeSpecificDataGuid)))
120 {
121 DEBUG ((
122 DEBUG_ERROR,
123 "%a:%a: malformed Data\n",
124 gEfiCallerBaseName,
125 __func__
126 ));
127 return EFI_INVALID_PARAMETER;
128 }
129
130 //
131 // Get the number of the Boot#### option that the status code applies to.
132 //
133 VariableSize = sizeof BootCurrent;
134 Status = gRT->GetVariable (
135 EFI_BOOT_CURRENT_VARIABLE_NAME,
136 &gEfiGlobalVariableGuid,
137 NULL /* Attributes */,
138 &VariableSize,
139 &BootCurrent
140 );
141 if (EFI_ERROR (Status)) {
142 DEBUG ((
143 DEBUG_ERROR,
144 "%a:%a: failed to get %g:\"%s\": %r\n",
145 gEfiCallerBaseName,
146 __func__,
147 &gEfiGlobalVariableGuid,
148 EFI_BOOT_CURRENT_VARIABLE_NAME,
149 Status
150 ));
151 return Status;
152 }
153
154 if (VariableSize != sizeof BootCurrent) {
155 DEBUG ((
156 DEBUG_ERROR,
157 "%a:%a: got %Lu bytes for %g:\"%s\", expected %Lu\n",
158 gEfiCallerBaseName,
159 __func__,
160 (UINT64)VariableSize,
161 &gEfiGlobalVariableGuid,
162 EFI_BOOT_CURRENT_VARIABLE_NAME,
163 (UINT64)sizeof BootCurrent
164 ));
165 return EFI_INCOMPATIBLE_VERSION;
166 }
167
168 //
169 // Get the Boot#### option that the status code applies to.
170 //
171 UnicodeSPrint (
172 BootOptionName,
173 sizeof BootOptionName,
174 L"Boot%04x",
175 BootCurrent
176 );
177 Status = EfiBootManagerVariableToLoadOption (BootOptionName, &BmBootOption);
178 if (EFI_ERROR (Status)) {
179 DEBUG ((
180 DEBUG_ERROR,
181 "%a:%a: EfiBootManagerVariableToLoadOption(\"%s\"): %r\n",
182 gEfiCallerBaseName,
183 __func__,
184 BootOptionName,
185 Status
186 ));
187 return Status;
188 }
189
190 //
191 // Format the device path.
192 //
193 DevPathStringIsDynamic = TRUE;
194 DevPathString = ConvertDevicePathToText (
195 BmBootOption.FilePath,
196 FALSE, // DisplayOnly
197 FALSE // AllowShortcuts
198 );
199 if (DevPathString == NULL) {
200 DevPathStringIsDynamic = FALSE;
201 DevPathString = L"<out of memory while formatting device path>";
202 }
203
204 //
205 // Print the message to the console.
206 //
207 if ((Value == mLoadPrep) || (Value == mStartPrep)) {
208#ifndef VBOX /* This gets written everywhere even on the serial console, so get rid of it. */
209 Print (
210 L"%a: %a %s \"%s\" from %s\n",
211 gEfiCallerBaseName,
212 Value == mLoadPrep ? "loading" : "starting",
213 BootOptionName,
214 BmBootOption.Description,
215 DevPathString
216 );
217#endif
218 } else {
219 Print (
220 L"%a: failed to %a %s \"%s\" from %s: %r\n",
221 gEfiCallerBaseName,
222 Value == mLoadFail ? "load" : "start",
223 BootOptionName,
224 BmBootOption.Description,
225 DevPathString,
226 ((EFI_RETURN_STATUS_EXTENDED_DATA *)Data)->ReturnStatus
227 );
228 }
229
230 //
231 // Done.
232 //
233 if (DevPathStringIsDynamic) {
234 FreePool (DevPathString);
235 }
236
237 EfiBootManagerFreeLoadOption (&BmBootOption);
238 return EFI_SUCCESS;
239}
240
241/**
242 Unregister HandleStatusCode() at ExitBootServices().
243
244 (See EFI_RSC_HANDLER_PROTOCOL in Volume 3 of the Platform Init spec.)
245
246 @param[in] Event Event whose notification function is being invoked.
247
248 @param[in] Context Pointer to EFI_RSC_HANDLER_PROTOCOL, originally looked up
249 when HandleStatusCode() was registered.
250**/
251STATIC
252VOID
253EFIAPI
254UnregisterAtExitBootServices (
255 IN EFI_EVENT Event,
256 IN VOID *Context
257 )
258{
259 EFI_RSC_HANDLER_PROTOCOL *StatusCodeRouter;
260
261 StatusCodeRouter = Context;
262 StatusCodeRouter->Unregister (HandleStatusCode);
263}
264
265/**
266 Register a status code handler for printing the Boot Manager's LoadImage()
267 and StartImage() preparations, and return codes, to the UEFI console.
268
269 @retval EFI_SUCCESS The status code handler has been successfully
270 registered.
271
272 @return Error codes propagated from boot services and from
273 EFI_RSC_HANDLER_PROTOCOL.
274**/
275EFI_STATUS
276EFIAPI
277PlatformBmPrintScRegisterHandler (
278 VOID
279 )
280{
281 EFI_STATUS Status;
282 EFI_RSC_HANDLER_PROTOCOL *StatusCodeRouter;
283 EFI_EVENT ExitBootEvent;
284
285 Status = gBS->LocateProtocol (
286 &gEfiRscHandlerProtocolGuid,
287 NULL /* Registration */,
288 (VOID **)&StatusCodeRouter
289 );
290 ASSERT_EFI_ERROR (Status);
291 if (EFI_ERROR (Status)) {
292 return Status;
293 }
294
295 //
296 // Set the EFI_STATUS_CODE_VALUE convenience variables.
297 //
298 mLoadPrep = PcdGet32 (PcdProgressCodeOsLoaderLoad);
299 mLoadFail = (EFI_SOFTWARE_DXE_BS_DRIVER |
300 EFI_SW_DXE_BS_EC_BOOT_OPTION_LOAD_ERROR);
301 mStartPrep = PcdGet32 (PcdProgressCodeOsLoaderStart);
302 mStartFail = (EFI_SOFTWARE_DXE_BS_DRIVER |
303 EFI_SW_DXE_BS_EC_BOOT_OPTION_FAILED);
304
305 //
306 // Register the handler callback.
307 //
308 Status = StatusCodeRouter->Register (HandleStatusCode, TPL_CALLBACK);
309 if (EFI_ERROR (Status)) {
310 DEBUG ((
311 DEBUG_ERROR,
312 "%a:%a: failed to register status code handler: %r\n",
313 gEfiCallerBaseName,
314 __func__,
315 Status
316 ));
317 return Status;
318 }
319
320 //
321 // Status code reporting and routing/handling extend into OS runtime. Since
322 // we don't want our handler to survive the BDS phase, we have to unregister
323 // the callback at ExitBootServices(). (See EFI_RSC_HANDLER_PROTOCOL in
324 // Volume 3 of the Platform Init spec.)
325 //
326 Status = gBS->CreateEvent (
327 EVT_SIGNAL_EXIT_BOOT_SERVICES, // Type
328 TPL_CALLBACK, // NotifyTpl
329 UnregisterAtExitBootServices, // NotifyFunction
330 StatusCodeRouter, // NotifyContext
331 &ExitBootEvent // Event
332 );
333 if (EFI_ERROR (Status)) {
334 //
335 // We have to unregister the callback right now, and fail the function.
336 //
337 DEBUG ((
338 DEBUG_ERROR,
339 "%a:%a: failed to create ExitBootServices() event: "
340 "%r\n",
341 gEfiCallerBaseName,
342 __func__,
343 Status
344 ));
345 StatusCodeRouter->Unregister (HandleStatusCode);
346 return Status;
347 }
348
349 return EFI_SUCCESS;
350}
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette