Home | History | Annotate | Download | only in UefiBootManagerLib
      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 *
     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
    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
    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
    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
    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
    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
    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