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