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