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