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 *
BmGetControllerName(IN EFI_HANDLE DriverHealthHandle,IN EFI_HANDLE ControllerHandle,IN EFI_HANDLE ChildHandle)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
BmDisplayMessages(IN EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO * DriverHealthInfo)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 ((EFI_D_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 ((EFI_D_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
BmRepairNotify(IN UINTN Value,IN UINTN Limit)165 BmRepairNotify (
166   IN UINTN        Value,
167   IN UINTN        Limit
168   )
169 {
170   DEBUG ((EFI_D_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
BmGetSingleControllerHealthStatus(IN OUT EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO ** DriverHealthInfo,IN OUT UINTN * Count,IN EFI_HANDLE DriverHealthHandle,IN EFI_HANDLE ControllerHandle,OPTIONAL IN EFI_HANDLE ChildHandle OPTIONAL)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
EfiBootManagerGetDriverHealthInfo(UINTN * Count)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
EfiBootManagerFreeDriverHealthInfo(EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO * DriverHealthInfo,UINTN Count)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
BmRepairAllControllers(UINTN ReconnectRepairCount)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 (
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         EFI_D_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     );
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__, __LINE__, ReconnectRepairCount));
578     }
579   }
580 
581   if (RebootRequired) {
582     DEBUG ((EFI_D_INFO, "[BDS] One of the Driver Health instances requires rebooting.\n"));
583     gRT->ResetSystem (EfiResetWarm, EFI_SUCCESS, 0, NULL);
584   }
585 }
586