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