]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Library/UefiBootManagerLib/BmDriverHealth.c
MdeModulePkg: Change complex DEBUG_CODE() to DEBUG_CODE_BEGIN/END()
[mirror_edk2.git] / MdeModulePkg / Library / UefiBootManagerLib / BmDriverHealth.c
1 /** @file
2 Library functions which relates with driver health.
3
4 Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>
5 (C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR>
6 (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
7 SPDX-License-Identifier: BSD-2-Clause-Patent
8
9 **/
10
11 #include "InternalBm.h"
12
13 GLOBAL_REMOVE_IF_UNREFERENCED
14 CHAR16 *mBmHealthStatusText[] = {
15 L"Healthy",
16 L"Repair Required",
17 L"Configuration Required",
18 L"Failed",
19 L"Reconnect Required",
20 L"Reboot Required"
21 };
22
23 /**
24 Return the controller name.
25
26 @param DriverHealthHandle The handle on which the Driver Health protocol instance is retrieved.
27 @param ControllerHandle The handle of a controller that the driver specified by DriverBindingHandle is managing.
28 This handle specifies the controller whose name is to be returned.
29 @param ChildHandle The handle of the child controller to retrieve the name of. This is an
30 optional parameter that may be NULL. It will be NULL for device drivers.
31 It will also be NULL for bus drivers that attempt to retrieve the name
32 of the bus controller. It will not be NULL for a bus driver that attempts
33 to retrieve the name of a child controller.
34
35 @return A pointer to the Unicode string to return. This Unicode string is the name of the controller
36 specified by ControllerHandle and ChildHandle.
37 **/
38 CHAR16 *
39 BmGetControllerName (
40 IN EFI_HANDLE DriverHealthHandle,
41 IN EFI_HANDLE ControllerHandle,
42 IN EFI_HANDLE ChildHandle
43 )
44 {
45 EFI_STATUS Status;
46 CHAR16 *ControllerName;
47 CHAR8 *LanguageVariable;
48 CHAR8 *BestLanguage;
49 BOOLEAN Iso639Language;
50 EFI_COMPONENT_NAME_PROTOCOL *ComponentName;
51
52 ControllerName = NULL;
53
54 //
55 // Locate Component Name (2) protocol on the driver binging handle.
56 //
57 Iso639Language = FALSE;
58 Status = gBS->HandleProtocol (
59 DriverHealthHandle,
60 &gEfiComponentName2ProtocolGuid,
61 (VOID **) &ComponentName
62 );
63 if (EFI_ERROR (Status)) {
64 Status = gBS->HandleProtocol (
65 DriverHealthHandle,
66 &gEfiComponentNameProtocolGuid,
67 (VOID **) &ComponentName
68 );
69 if (!EFI_ERROR (Status)) {
70 Iso639Language = TRUE;
71 }
72 }
73
74 if (!EFI_ERROR (Status)) {
75 GetEfiGlobalVariable2 (Iso639Language ? L"Lang" : L"PlatformLang", (VOID**)&LanguageVariable, NULL);
76 BestLanguage = GetBestLanguage(
77 ComponentName->SupportedLanguages,
78 Iso639Language,
79 (LanguageVariable != NULL) ? LanguageVariable : "",
80 Iso639Language ? "eng" : "en-US",
81 NULL
82 );
83 if (LanguageVariable != NULL) {
84 FreePool (LanguageVariable);
85 }
86
87 Status = ComponentName->GetControllerName (
88 ComponentName,
89 ControllerHandle,
90 ChildHandle,
91 BestLanguage,
92 &ControllerName
93 );
94 }
95
96 if (!EFI_ERROR (Status)) {
97 return AllocateCopyPool (StrSize (ControllerName), ControllerName);
98 } else {
99 return ConvertDevicePathToText (
100 DevicePathFromHandle (ChildHandle != NULL ? ChildHandle : ControllerHandle),
101 FALSE,
102 FALSE
103 );
104 }
105 }
106
107 /**
108 Display a set of messages returned by the GetHealthStatus () service of the EFI Driver Health Protocol
109
110 @param DriverHealthInfo Pointer to the Driver Health information entry.
111 **/
112 VOID
113 BmDisplayMessages (
114 IN EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO *DriverHealthInfo
115 )
116 {
117 UINTN Index;
118 EFI_STRING String;
119 CHAR16 *ControllerName;
120
121 if (DriverHealthInfo->MessageList == NULL ||
122 DriverHealthInfo->MessageList[0].HiiHandle == NULL) {
123 return;
124 }
125
126 ControllerName = BmGetControllerName (
127 DriverHealthInfo->DriverHealthHandle,
128 DriverHealthInfo->ControllerHandle,
129 DriverHealthInfo->ChildHandle
130 );
131
132 DEBUG ((DEBUG_INFO, "Controller: %s\n", ControllerName));
133 Print (L"Controller: %s\n", ControllerName);
134 for (Index = 0; DriverHealthInfo->MessageList[Index].HiiHandle != NULL; Index++) {
135 String = HiiGetString (
136 DriverHealthInfo->MessageList[Index].HiiHandle,
137 DriverHealthInfo->MessageList[Index].StringId,
138 NULL
139 );
140 if (String != NULL) {
141 Print (L" %s\n", String);
142 DEBUG ((DEBUG_INFO, " %s\n", String));
143 FreePool (String);
144 }
145 }
146
147 if (ControllerName != NULL) {
148 FreePool (ControllerName);
149 }
150 }
151
152 /**
153 The repair notify function.
154 @param Value A value between 0 and Limit that identifies the current progress
155 of the repair operation.
156 @param Limit The maximum value of Value for the current repair operation.
157 If Limit is 0, then the completion progress is indeterminate.
158 For example, a driver that wants to specify progress in percent
159 would use a Limit value of 100.
160
161 @retval EFI_SUCCESS Successfully return from the notify function.
162 **/
163 EFI_STATUS
164 EFIAPI
165 BmRepairNotify (
166 IN UINTN Value,
167 IN UINTN Limit
168 )
169 {
170 DEBUG ((DEBUG_INFO, "[BDS]RepairNotify: %d/%d\n", Value, Limit));
171 Print (L"[BDS]RepairNotify: %d/%d\n", Value, Limit);
172
173 return EFI_SUCCESS;
174 }
175
176 /**
177 Collect the Driver Health status of a single controller.
178
179 @param DriverHealthInfo A pointer to the array containing all of the platform driver health information.
180 @param Count Return the updated array count.
181 @param DriverHealthHandle The handle on which the Driver Health protocol instance is retrieved.
182 @param ControllerHandle The handle of the controller..
183 @param ChildHandle The handle of the child controller to retrieve the health
184 status on. This is an optional parameter that may be NULL.
185
186 @retval Status The status returned from GetHealthStatus.
187 @retval EFI_ABORTED The health status is healthy so no further query is needed.
188
189 **/
190 EFI_STATUS
191 BmGetSingleControllerHealthStatus (
192 IN OUT EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO **DriverHealthInfo,
193 IN OUT UINTN *Count,
194 IN EFI_HANDLE DriverHealthHandle,
195 IN EFI_HANDLE ControllerHandle OPTIONAL,
196 IN EFI_HANDLE ChildHandle OPTIONAL
197 )
198 {
199 EFI_STATUS Status;
200 EFI_DRIVER_HEALTH_PROTOCOL *DriverHealth;
201 EFI_DRIVER_HEALTH_HII_MESSAGE *MessageList;
202 EFI_HII_HANDLE FormHiiHandle;
203 EFI_DRIVER_HEALTH_STATUS HealthStatus;
204
205 ASSERT (DriverHealthHandle != NULL);
206 //
207 // Retrieve the Driver Health Protocol from DriverHandle
208 //
209 Status = gBS->HandleProtocol (
210 DriverHealthHandle,
211 &gEfiDriverHealthProtocolGuid,
212 (VOID **) &DriverHealth
213 );
214 ASSERT_EFI_ERROR (Status);
215
216
217 if (ControllerHandle == NULL) {
218 //
219 // If ControllerHandle is NULL, the return the cumulative health status of the driver
220 //
221 Status = DriverHealth->GetHealthStatus (DriverHealth, NULL, NULL, &HealthStatus, NULL, NULL);
222 if (!EFI_ERROR (Status) && HealthStatus == EfiDriverHealthStatusHealthy) {
223 *DriverHealthInfo = ReallocatePool (
224 (*Count) * sizeof (EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO),
225 (*Count + 1) * sizeof (EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO),
226 *DriverHealthInfo
227 );
228 ASSERT (*DriverHealthInfo != NULL);
229
230 (*DriverHealthInfo)[*Count].DriverHealthHandle = DriverHealthHandle;
231 (*DriverHealthInfo)[*Count].DriverHealth = DriverHealth;
232 (*DriverHealthInfo)[*Count].HealthStatus = HealthStatus;
233
234 *Count = *Count + 1;
235
236 Status = EFI_ABORTED;
237 }
238 return Status;
239 }
240
241 MessageList = NULL;
242 FormHiiHandle = NULL;
243
244 //
245 // Collect the health status with the optional HII message list
246 //
247 Status = DriverHealth->GetHealthStatus (DriverHealth, ControllerHandle, ChildHandle, &HealthStatus, &MessageList, &FormHiiHandle);
248 if (!EFI_ERROR (Status)) {
249 *DriverHealthInfo = ReallocatePool (
250 (*Count) * sizeof (EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO),
251 (*Count + 1) * sizeof (EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO),
252 *DriverHealthInfo
253 );
254 ASSERT (*DriverHealthInfo != NULL);
255 (*DriverHealthInfo)[*Count].DriverHealth = DriverHealth;
256 (*DriverHealthInfo)[*Count].DriverHealthHandle = DriverHealthHandle;
257 (*DriverHealthInfo)[*Count].ControllerHandle = ControllerHandle;
258 (*DriverHealthInfo)[*Count].ChildHandle = ChildHandle;
259 (*DriverHealthInfo)[*Count].HiiHandle = FormHiiHandle;
260 (*DriverHealthInfo)[*Count].MessageList = MessageList;
261 (*DriverHealthInfo)[*Count].HealthStatus = HealthStatus;
262
263 *Count = *Count + 1;
264 }
265
266 return Status;
267 }
268
269 /**
270 Return all the Driver Health information.
271
272 When the cumulative health status of all the controllers managed by the
273 driver who produces the EFI_DRIVER_HEALTH_PROTOCOL is healthy, only one
274 EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO entry is created for such
275 EFI_DRIVER_HEALTH_PROTOCOL instance.
276 Otherwise, every controller creates one EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO
277 entry. Additionally every child controller creates one
278 EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO entry if the driver is a bus driver.
279
280 @param Count Return the count of the Driver Health information.
281
282 @retval NULL No Driver Health information is returned.
283 @retval !NULL Pointer to the Driver Health information array.
284 **/
285 EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO *
286 EFIAPI
287 EfiBootManagerGetDriverHealthInfo (
288 UINTN *Count
289 )
290 {
291 EFI_STATUS Status;
292 UINTN NumHandles;
293 EFI_HANDLE *DriverHealthHandles;
294 UINTN DriverHealthIndex;
295 EFI_HANDLE *Handles;
296 UINTN HandleCount;
297 UINTN ControllerIndex;
298 UINTN ChildIndex;
299 EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO *DriverHealthInfo;
300
301 //
302 // Initialize local variables
303 //
304 *Count = 0;
305 DriverHealthInfo = NULL;
306 Handles = NULL;
307 DriverHealthHandles = NULL;
308 NumHandles = 0;
309 HandleCount = 0;
310
311 Status = gBS->LocateHandleBuffer (
312 ByProtocol,
313 &gEfiDriverHealthProtocolGuid,
314 NULL,
315 &NumHandles,
316 &DriverHealthHandles
317 );
318
319 if (Status == EFI_NOT_FOUND || NumHandles == 0) {
320 //
321 // If there are no Driver Health Protocols handles, then return EFI_NOT_FOUND
322 //
323 return NULL;
324 }
325
326 ASSERT_EFI_ERROR (Status);
327 ASSERT (DriverHealthHandles != NULL);
328
329 //
330 // Check the health status of all controllers in the platform
331 // Start by looping through all the Driver Health Protocol handles in the handle database
332 //
333 for (DriverHealthIndex = 0; DriverHealthIndex < NumHandles; DriverHealthIndex++) {
334 //
335 // Get the cumulative health status of the driver
336 //
337 Status = BmGetSingleControllerHealthStatus (&DriverHealthInfo, Count, DriverHealthHandles[DriverHealthIndex], NULL, NULL);
338 if (EFI_ERROR (Status)) {
339 continue;
340 }
341
342 //
343 // See if the list of all handles in the handle database has been retrieved
344 //
345 //
346 if (Handles == NULL) {
347 //
348 // Retrieve the list of all handles from the handle database
349 //
350 Status = gBS->LocateHandleBuffer (
351 AllHandles,
352 NULL,
353 NULL,
354 &HandleCount,
355 &Handles
356 );
357 ASSERT_EFI_ERROR (Status);
358 }
359 //
360 // Loop through all the controller handles in the handle database
361 //
362 for (ControllerIndex = 0; ControllerIndex < HandleCount; ControllerIndex++) {
363 Status = BmGetSingleControllerHealthStatus (&DriverHealthInfo, Count, DriverHealthHandles[DriverHealthIndex], Handles[ControllerIndex], NULL);
364 if (EFI_ERROR (Status)) {
365 continue;
366 }
367
368 //
369 // Loop through all the child handles in the handle database
370 //
371 for (ChildIndex = 0; ChildIndex < HandleCount; ChildIndex++) {
372 Status = BmGetSingleControllerHealthStatus (&DriverHealthInfo, Count, DriverHealthHandles[DriverHealthIndex], Handles[ControllerIndex], Handles[ChildIndex]);
373 if (EFI_ERROR (Status)) {
374 continue;
375 }
376 }
377 }
378 }
379
380 Status = EFI_SUCCESS;
381
382 if (Handles != NULL) {
383 FreePool (Handles);
384 }
385 if (DriverHealthHandles != NULL) {
386 FreePool (DriverHealthHandles);
387 }
388
389 return DriverHealthInfo;
390 }
391
392 /**
393 Free the Driver Health information array.
394
395 @param DriverHealthInfo Pointer to array of the Driver Health information.
396 @param Count Count of the array.
397
398 @retval EFI_SUCCESS The array is freed.
399 @retval EFI_INVALID_PARAMETER The array is NULL.
400 **/
401 EFI_STATUS
402 EFIAPI
403 EfiBootManagerFreeDriverHealthInfo (
404 EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO *DriverHealthInfo,
405 UINTN Count
406 )
407 {
408 UINTN Index;
409
410 for (Index = 0; Index < Count; Index++) {
411 if (DriverHealthInfo[Index].MessageList != NULL) {
412 FreePool (DriverHealthInfo[Index].MessageList);
413 }
414 }
415 return gBS->FreePool (DriverHealthInfo);
416 }
417
418 /**
419 Repair all the controllers according to the Driver Health status queried.
420
421 @param ReconnectRepairCount To record the number of recursive call of
422 this function itself.
423 **/
424 VOID
425 BmRepairAllControllers (
426 UINTN ReconnectRepairCount
427 )
428 {
429 EFI_STATUS Status;
430 EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO *DriverHealthInfo;
431 EFI_DRIVER_HEALTH_STATUS HealthStatus;
432 UINTN Count;
433 UINTN Index;
434 BOOLEAN RepairRequired;
435 BOOLEAN ConfigurationRequired;
436 BOOLEAN ReconnectRequired;
437 BOOLEAN RebootRequired;
438 EFI_HII_HANDLE *HiiHandles;
439 EFI_FORM_BROWSER2_PROTOCOL *FormBrowser2;
440 UINT32 MaxRepairCount;
441 UINT32 RepairCount;
442
443 //
444 // Configure PcdDriverHealthConfigureForm to ZeroGuid to disable driver health check.
445 //
446 if (IsZeroGuid (PcdGetPtr (PcdDriverHealthConfigureForm))) {
447 return;
448 }
449
450 Status = gBS->LocateProtocol (&gEfiFormBrowser2ProtocolGuid, NULL, (VOID **) &FormBrowser2);
451 ASSERT_EFI_ERROR (Status);
452
453 MaxRepairCount = PcdGet32 (PcdMaxRepairCount);
454 RepairCount = 0;
455
456 do {
457 RepairRequired = FALSE;
458 ConfigurationRequired = FALSE;
459
460 //
461 // Deal with Repair Required
462 //
463 DriverHealthInfo = EfiBootManagerGetDriverHealthInfo (&Count);
464 for (Index = 0; Index < Count; Index++) {
465 if (DriverHealthInfo[Index].HealthStatus == EfiDriverHealthStatusConfigurationRequired) {
466 ConfigurationRequired = TRUE;
467 }
468
469 if (DriverHealthInfo[Index].HealthStatus == EfiDriverHealthStatusRepairRequired) {
470 RepairRequired = TRUE;
471
472 BmDisplayMessages (&DriverHealthInfo[Index]);
473
474 Status = DriverHealthInfo[Index].DriverHealth->Repair (
475 DriverHealthInfo[Index].DriverHealth,
476 DriverHealthInfo[Index].ControllerHandle,
477 DriverHealthInfo[Index].ChildHandle,
478 BmRepairNotify
479 );
480 if (!EFI_ERROR (Status) && !ConfigurationRequired) {
481 Status = DriverHealthInfo[Index].DriverHealth->GetHealthStatus (
482 DriverHealthInfo[Index].DriverHealth,
483 DriverHealthInfo[Index].ControllerHandle,
484 DriverHealthInfo[Index].ChildHandle,
485 &HealthStatus,
486 NULL,
487 NULL
488 );
489 if (!EFI_ERROR (Status) && (HealthStatus == EfiDriverHealthStatusConfigurationRequired)) {
490 ConfigurationRequired = TRUE;
491 }
492 }
493 }
494 }
495
496 if (ConfigurationRequired) {
497 HiiHandles = HiiGetHiiHandles (NULL);
498 if (HiiHandles != NULL) {
499 for (Index = 0; HiiHandles[Index] != NULL; Index++) {
500 Status = FormBrowser2->SendForm (
501 FormBrowser2,
502 &HiiHandles[Index],
503 1,
504 PcdGetPtr (PcdDriverHealthConfigureForm),
505 0,
506 NULL,
507 NULL
508 );
509 if (!EFI_ERROR (Status)) {
510 break;
511 }
512 }
513 FreePool (HiiHandles);
514 }
515 }
516
517 EfiBootManagerFreeDriverHealthInfo (DriverHealthInfo, Count);
518 RepairCount++;
519 } while ((RepairRequired || ConfigurationRequired) && ((MaxRepairCount == 0) || (RepairCount < MaxRepairCount)));
520
521 RebootRequired = FALSE;
522 ReconnectRequired = FALSE;
523 DriverHealthInfo = EfiBootManagerGetDriverHealthInfo (&Count);
524 for (Index = 0; Index < Count; Index++) {
525
526 BmDisplayMessages (&DriverHealthInfo[Index]);
527
528 if (DriverHealthInfo[Index].HealthStatus == EfiDriverHealthStatusReconnectRequired) {
529 Status = gBS->DisconnectController (DriverHealthInfo[Index].ControllerHandle, NULL, NULL);
530 if (EFI_ERROR (Status)) {
531 //
532 // Disconnect failed. Need to promote reconnect to a reboot.
533 //
534 RebootRequired = TRUE;
535 } else {
536 gBS->ConnectController (DriverHealthInfo[Index].ControllerHandle, NULL, NULL, TRUE);
537 ReconnectRequired = TRUE;
538 }
539 }
540
541 if (DriverHealthInfo[Index].HealthStatus == EfiDriverHealthStatusRebootRequired) {
542 RebootRequired = TRUE;
543 }
544 }
545 EfiBootManagerFreeDriverHealthInfo (DriverHealthInfo, Count);
546
547
548 DEBUG_CODE_BEGIN ();
549 CHAR16 *ControllerName;
550
551 DriverHealthInfo = EfiBootManagerGetDriverHealthInfo (&Count);
552 for (Index = 0; Index < Count; Index++) {
553 ControllerName = BmGetControllerName (
554 DriverHealthInfo[Index].DriverHealthHandle,
555 DriverHealthInfo[Index].ControllerHandle,
556 DriverHealthInfo[Index].ChildHandle
557 );
558 DEBUG ((
559 DEBUG_INFO,
560 "%02d: %s - %s\n",
561 Index,
562 ControllerName,
563 mBmHealthStatusText[DriverHealthInfo[Index].HealthStatus]
564 ));
565 if (ControllerName != NULL) {
566 FreePool (ControllerName);
567 }
568 }
569 EfiBootManagerFreeDriverHealthInfo (DriverHealthInfo, Count);
570 DEBUG_CODE_END ();
571
572 if (ReconnectRequired) {
573 if (ReconnectRepairCount < MAX_RECONNECT_REPAIR) {
574 BmRepairAllControllers (ReconnectRepairCount + 1);
575 } else {
576 DEBUG ((DEBUG_ERROR, "[%a:%d] Repair failed after %d retries.\n",
577 __FUNCTION__, DEBUG_LINE_NUMBER, ReconnectRepairCount));
578 }
579 }
580
581 if (RebootRequired) {
582 DEBUG ((DEBUG_INFO, "[BDS] One of the Driver Health instances requires rebooting.\n"));
583 gRT->ResetSystem (EfiResetWarm, EFI_SUCCESS, 0, NULL);
584 }
585 }