]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/Library/PlatformBmPrintScLib/StatusCodeHandler.c
OvmfPkg: Apply uncrustify changes
[mirror_edk2.git] / OvmfPkg / Library / PlatformBmPrintScLib / StatusCodeHandler.c
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 //
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;
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 **/
76 STATIC
77 EFI_STATUS
78 EFIAPI
79 HandleStatusCode (
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 __FUNCTION__
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 __FUNCTION__,
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 __FUNCTION__,
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 __FUNCTION__,
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 Print (
209 L"%a: %a %s \"%s\" from %s\n",
210 gEfiCallerBaseName,
211 Value == mLoadPrep ? "loading" : "starting",
212 BootOptionName,
213 BmBootOption.Description,
214 DevPathString
215 );
216 } else {
217 Print (
218 L"%a: failed to %a %s \"%s\" from %s: %r\n",
219 gEfiCallerBaseName,
220 Value == mLoadFail ? "load" : "start",
221 BootOptionName,
222 BmBootOption.Description,
223 DevPathString,
224 ((EFI_RETURN_STATUS_EXTENDED_DATA *)Data)->ReturnStatus
225 );
226 }
227
228 //
229 // Done.
230 //
231 if (DevPathStringIsDynamic) {
232 FreePool (DevPathString);
233 }
234
235 EfiBootManagerFreeLoadOption (&BmBootOption);
236 return EFI_SUCCESS;
237 }
238
239 /**
240 Unregister HandleStatusCode() at ExitBootServices().
241
242 (See EFI_RSC_HANDLER_PROTOCOL in Volume 3 of the Platform Init spec.)
243
244 @param[in] Event Event whose notification function is being invoked.
245
246 @param[in] Context Pointer to EFI_RSC_HANDLER_PROTOCOL, originally looked up
247 when HandleStatusCode() was registered.
248 **/
249 STATIC
250 VOID
251 EFIAPI
252 UnregisterAtExitBootServices (
253 IN EFI_EVENT Event,
254 IN VOID *Context
255 )
256 {
257 EFI_RSC_HANDLER_PROTOCOL *StatusCodeRouter;
258
259 StatusCodeRouter = Context;
260 StatusCodeRouter->Unregister (HandleStatusCode);
261 }
262
263 /**
264 Register a status code handler for printing the Boot Manager's LoadImage()
265 and StartImage() preparations, and return codes, to the UEFI console.
266
267 @retval EFI_SUCCESS The status code handler has been successfully
268 registered.
269
270 @return Error codes propagated from boot services and from
271 EFI_RSC_HANDLER_PROTOCOL.
272 **/
273 EFI_STATUS
274 EFIAPI
275 PlatformBmPrintScRegisterHandler (
276 VOID
277 )
278 {
279 EFI_STATUS Status;
280 EFI_RSC_HANDLER_PROTOCOL *StatusCodeRouter;
281 EFI_EVENT ExitBootEvent;
282
283 Status = gBS->LocateProtocol (
284 &gEfiRscHandlerProtocolGuid,
285 NULL /* Registration */,
286 (VOID **)&StatusCodeRouter
287 );
288 ASSERT_EFI_ERROR (Status);
289 if (EFI_ERROR (Status)) {
290 return Status;
291 }
292
293 //
294 // Set the EFI_STATUS_CODE_VALUE convenience variables.
295 //
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);
302
303 //
304 // Register the handler callback.
305 //
306 Status = StatusCodeRouter->Register (HandleStatusCode, TPL_CALLBACK);
307 if (EFI_ERROR (Status)) {
308 DEBUG ((
309 DEBUG_ERROR,
310 "%a:%a: failed to register status code handler: %r\n",
311 gEfiCallerBaseName,
312 __FUNCTION__,
313 Status
314 ));
315 return Status;
316 }
317
318 //
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.)
323 //
324 Status = gBS->CreateEvent (
325 EVT_SIGNAL_EXIT_BOOT_SERVICES, // Type
326 TPL_CALLBACK, // NotifyTpl
327 UnregisterAtExitBootServices, // NotifyFunction
328 StatusCodeRouter, // NotifyContext
329 &ExitBootEvent // Event
330 );
331 if (EFI_ERROR (Status)) {
332 //
333 // We have to unregister the callback right now, and fail the function.
334 //
335 DEBUG ((
336 DEBUG_ERROR,
337 "%a:%a: failed to create ExitBootServices() event: "
338 "%r\n",
339 gEfiCallerBaseName,
340 __FUNCTION__,
341 Status
342 ));
343 StatusCodeRouter->Unregister (HandleStatusCode);
344 return Status;
345 }
346
347 return EFI_SUCCESS;
348 }