2 Library functions which relates with driver health.
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
11 #include "InternalBm.h"
13 GLOBAL_REMOVE_IF_UNREFERENCED
14 CHAR16
*mBmHealthStatusText
[] = {
17 L
"Configuration Required",
19 L
"Reconnect Required",
24 Return the controller name.
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.
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.
40 IN EFI_HANDLE DriverHealthHandle
,
41 IN EFI_HANDLE ControllerHandle
,
42 IN EFI_HANDLE ChildHandle
46 CHAR16
*ControllerName
;
47 CHAR8
*LanguageVariable
;
49 BOOLEAN Iso639Language
;
50 EFI_COMPONENT_NAME_PROTOCOL
*ComponentName
;
52 ControllerName
= NULL
;
55 // Locate Component Name (2) protocol on the driver binging handle.
57 Iso639Language
= FALSE
;
58 Status
= gBS
->HandleProtocol (
60 &gEfiComponentName2ProtocolGuid
,
61 (VOID
**) &ComponentName
63 if (EFI_ERROR (Status
)) {
64 Status
= gBS
->HandleProtocol (
66 &gEfiComponentNameProtocolGuid
,
67 (VOID
**) &ComponentName
69 if (!EFI_ERROR (Status
)) {
70 Iso639Language
= TRUE
;
74 if (!EFI_ERROR (Status
)) {
75 GetEfiGlobalVariable2 (Iso639Language
? L
"Lang" : L
"PlatformLang", (VOID
**)&LanguageVariable
, NULL
);
76 BestLanguage
= GetBestLanguage(
77 ComponentName
->SupportedLanguages
,
79 (LanguageVariable
!= NULL
) ? LanguageVariable
: "",
80 Iso639Language
? "eng" : "en-US",
83 if (LanguageVariable
!= NULL
) {
84 FreePool (LanguageVariable
);
87 Status
= ComponentName
->GetControllerName (
96 if (!EFI_ERROR (Status
)) {
97 return AllocateCopyPool (StrSize (ControllerName
), ControllerName
);
99 return ConvertDevicePathToText (
100 DevicePathFromHandle (ChildHandle
!= NULL
? ChildHandle
: ControllerHandle
),
108 Display a set of messages returned by the GetHealthStatus () service of the EFI Driver Health Protocol
110 @param DriverHealthInfo Pointer to the Driver Health information entry.
114 IN EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO
*DriverHealthInfo
119 CHAR16
*ControllerName
;
121 if (DriverHealthInfo
->MessageList
== NULL
||
122 DriverHealthInfo
->MessageList
[0].HiiHandle
== NULL
) {
126 ControllerName
= BmGetControllerName (
127 DriverHealthInfo
->DriverHealthHandle
,
128 DriverHealthInfo
->ControllerHandle
,
129 DriverHealthInfo
->ChildHandle
132 DEBUG ((DEBUG_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
,
140 if (String
!= NULL
) {
141 Print (L
" %s\n", String
);
142 DEBUG ((DEBUG_INFO
, " %s\n", String
));
147 if (ControllerName
!= NULL
) {
148 FreePool (ControllerName
);
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.
161 @retval EFI_SUCCESS Successfully return from the notify function.
170 DEBUG ((DEBUG_INFO
, "[BDS]RepairNotify: %d/%d\n", Value
, Limit
));
171 Print (L
"[BDS]RepairNotify: %d/%d\n", Value
, Limit
);
177 Collect the Driver Health status of a single controller.
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.
186 @retval Status The status returned from GetHealthStatus.
187 @retval EFI_ABORTED The health status is healthy so no further query is needed.
191 BmGetSingleControllerHealthStatus (
192 IN OUT EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO
**DriverHealthInfo
,
194 IN EFI_HANDLE DriverHealthHandle
,
195 IN EFI_HANDLE ControllerHandle OPTIONAL
,
196 IN EFI_HANDLE ChildHandle OPTIONAL
200 EFI_DRIVER_HEALTH_PROTOCOL
*DriverHealth
;
201 EFI_DRIVER_HEALTH_HII_MESSAGE
*MessageList
;
202 EFI_HII_HANDLE FormHiiHandle
;
203 EFI_DRIVER_HEALTH_STATUS HealthStatus
;
205 ASSERT (DriverHealthHandle
!= NULL
);
207 // Retrieve the Driver Health Protocol from DriverHandle
209 Status
= gBS
->HandleProtocol (
211 &gEfiDriverHealthProtocolGuid
,
212 (VOID
**) &DriverHealth
214 ASSERT_EFI_ERROR (Status
);
217 if (ControllerHandle
== NULL
) {
219 // If ControllerHandle is NULL, the return the cumulative health status of the driver
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
),
228 ASSERT (*DriverHealthInfo
!= NULL
);
230 (*DriverHealthInfo
)[*Count
].DriverHealthHandle
= DriverHealthHandle
;
231 (*DriverHealthInfo
)[*Count
].DriverHealth
= DriverHealth
;
232 (*DriverHealthInfo
)[*Count
].HealthStatus
= HealthStatus
;
236 Status
= EFI_ABORTED
;
242 FormHiiHandle
= NULL
;
245 // Collect the health status with the optional HII message list
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
),
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
;
270 Return all the Driver Health information.
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.
280 @param Count Return the count of the Driver Health information.
282 @retval NULL No Driver Health information is returned.
283 @retval !NULL Pointer to the Driver Health information array.
285 EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO
*
287 EfiBootManagerGetDriverHealthInfo (
293 EFI_HANDLE
*DriverHealthHandles
;
294 UINTN DriverHealthIndex
;
297 UINTN ControllerIndex
;
299 EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO
*DriverHealthInfo
;
302 // Initialize local variables
305 DriverHealthInfo
= NULL
;
307 DriverHealthHandles
= NULL
;
311 Status
= gBS
->LocateHandleBuffer (
313 &gEfiDriverHealthProtocolGuid
,
319 if (Status
== EFI_NOT_FOUND
|| NumHandles
== 0) {
321 // If there are no Driver Health Protocols handles, then return EFI_NOT_FOUND
326 ASSERT_EFI_ERROR (Status
);
327 ASSERT (DriverHealthHandles
!= NULL
);
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
333 for (DriverHealthIndex
= 0; DriverHealthIndex
< NumHandles
; DriverHealthIndex
++) {
335 // Get the cumulative health status of the driver
337 Status
= BmGetSingleControllerHealthStatus (&DriverHealthInfo
, Count
, DriverHealthHandles
[DriverHealthIndex
], NULL
, NULL
);
338 if (EFI_ERROR (Status
)) {
343 // See if the list of all handles in the handle database has been retrieved
346 if (Handles
== NULL
) {
348 // Retrieve the list of all handles from the handle database
350 Status
= gBS
->LocateHandleBuffer (
357 ASSERT_EFI_ERROR (Status
);
360 // Loop through all the controller handles in the handle database
362 for (ControllerIndex
= 0; ControllerIndex
< HandleCount
; ControllerIndex
++) {
363 Status
= BmGetSingleControllerHealthStatus (&DriverHealthInfo
, Count
, DriverHealthHandles
[DriverHealthIndex
], Handles
[ControllerIndex
], NULL
);
364 if (EFI_ERROR (Status
)) {
369 // Loop through all the child handles in the handle database
371 for (ChildIndex
= 0; ChildIndex
< HandleCount
; ChildIndex
++) {
372 Status
= BmGetSingleControllerHealthStatus (&DriverHealthInfo
, Count
, DriverHealthHandles
[DriverHealthIndex
], Handles
[ControllerIndex
], Handles
[ChildIndex
]);
373 if (EFI_ERROR (Status
)) {
380 Status
= EFI_SUCCESS
;
382 if (Handles
!= NULL
) {
385 if (DriverHealthHandles
!= NULL
) {
386 FreePool (DriverHealthHandles
);
389 return DriverHealthInfo
;
393 Free the Driver Health information array.
395 @param DriverHealthInfo Pointer to array of the Driver Health information.
396 @param Count Count of the array.
398 @retval EFI_SUCCESS The array is freed.
399 @retval EFI_INVALID_PARAMETER The array is NULL.
403 EfiBootManagerFreeDriverHealthInfo (
404 EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO
*DriverHealthInfo
,
410 for (Index
= 0; Index
< Count
; Index
++) {
411 if (DriverHealthInfo
[Index
].MessageList
!= NULL
) {
412 FreePool (DriverHealthInfo
[Index
].MessageList
);
415 return gBS
->FreePool (DriverHealthInfo
);
419 Repair all the controllers according to the Driver Health status queried.
421 @param ReconnectRepairCount To record the number of recursive call of
422 this function itself.
425 BmRepairAllControllers (
426 UINTN ReconnectRepairCount
430 EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO
*DriverHealthInfo
;
431 EFI_DRIVER_HEALTH_STATUS HealthStatus
;
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
;
444 // Configure PcdDriverHealthConfigureForm to ZeroGuid to disable driver health check.
446 if (IsZeroGuid (PcdGetPtr (PcdDriverHealthConfigureForm
))) {
450 Status
= gBS
->LocateProtocol (&gEfiFormBrowser2ProtocolGuid
, NULL
, (VOID
**) &FormBrowser2
);
451 ASSERT_EFI_ERROR (Status
);
453 MaxRepairCount
= PcdGet32 (PcdMaxRepairCount
);
457 RepairRequired
= FALSE
;
458 ConfigurationRequired
= FALSE
;
461 // Deal with Repair Required
463 DriverHealthInfo
= EfiBootManagerGetDriverHealthInfo (&Count
);
464 for (Index
= 0; Index
< Count
; Index
++) {
465 if (DriverHealthInfo
[Index
].HealthStatus
== EfiDriverHealthStatusConfigurationRequired
) {
466 ConfigurationRequired
= TRUE
;
469 if (DriverHealthInfo
[Index
].HealthStatus
== EfiDriverHealthStatusRepairRequired
) {
470 RepairRequired
= TRUE
;
472 BmDisplayMessages (&DriverHealthInfo
[Index
]);
474 Status
= DriverHealthInfo
[Index
].DriverHealth
->Repair (
475 DriverHealthInfo
[Index
].DriverHealth
,
476 DriverHealthInfo
[Index
].ControllerHandle
,
477 DriverHealthInfo
[Index
].ChildHandle
,
480 if (!EFI_ERROR (Status
) && !ConfigurationRequired
) {
481 Status
= DriverHealthInfo
[Index
].DriverHealth
->GetHealthStatus (
482 DriverHealthInfo
[Index
].DriverHealth
,
483 DriverHealthInfo
[Index
].ControllerHandle
,
484 DriverHealthInfo
[Index
].ChildHandle
,
489 if (!EFI_ERROR (Status
) && (HealthStatus
== EfiDriverHealthStatusConfigurationRequired
)) {
490 ConfigurationRequired
= TRUE
;
496 if (ConfigurationRequired
) {
497 HiiHandles
= HiiGetHiiHandles (NULL
);
498 if (HiiHandles
!= NULL
) {
499 for (Index
= 0; HiiHandles
[Index
] != NULL
; Index
++) {
500 Status
= FormBrowser2
->SendForm (
504 PcdGetPtr (PcdDriverHealthConfigureForm
),
509 if (!EFI_ERROR (Status
)) {
513 FreePool (HiiHandles
);
517 EfiBootManagerFreeDriverHealthInfo (DriverHealthInfo
, Count
);
519 } while ((RepairRequired
|| ConfigurationRequired
) && ((MaxRepairCount
== 0) || (RepairCount
< MaxRepairCount
)));
521 RebootRequired
= FALSE
;
522 ReconnectRequired
= FALSE
;
523 DriverHealthInfo
= EfiBootManagerGetDriverHealthInfo (&Count
);
524 for (Index
= 0; Index
< Count
; Index
++) {
526 BmDisplayMessages (&DriverHealthInfo
[Index
]);
528 if (DriverHealthInfo
[Index
].HealthStatus
== EfiDriverHealthStatusReconnectRequired
) {
529 Status
= gBS
->DisconnectController (DriverHealthInfo
[Index
].ControllerHandle
, NULL
, NULL
);
530 if (EFI_ERROR (Status
)) {
532 // Disconnect failed. Need to promote reconnect to a reboot.
534 RebootRequired
= TRUE
;
536 gBS
->ConnectController (DriverHealthInfo
[Index
].ControllerHandle
, NULL
, NULL
, TRUE
);
537 ReconnectRequired
= TRUE
;
541 if (DriverHealthInfo
[Index
].HealthStatus
== EfiDriverHealthStatusRebootRequired
) {
542 RebootRequired
= TRUE
;
545 EfiBootManagerFreeDriverHealthInfo (DriverHealthInfo
, Count
);
549 CHAR16
*ControllerName
;
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
563 mBmHealthStatusText
[DriverHealthInfo
[Index
].HealthStatus
]
565 if (ControllerName
!= NULL
) {
566 FreePool (ControllerName
);
569 EfiBootManagerFreeDriverHealthInfo (DriverHealthInfo
, Count
);
572 if (ReconnectRequired
) {
573 if (ReconnectRepairCount
< MAX_RECONNECT_REPAIR
) {
574 BmRepairAllControllers (ReconnectRepairCount
+ 1);
576 DEBUG ((DEBUG_ERROR
, "[%a:%d] Repair failed after %d retries.\n",
577 __FUNCTION__
, DEBUG_LINE_NUMBER
, ReconnectRepairCount
));
581 if (RebootRequired
) {
582 DEBUG ((DEBUG_INFO
, "[BDS] One of the Driver Health instances requires rebooting.\n"));
583 gRT
->ResetSystem (EfiResetWarm
, EFI_SUCCESS
, 0, NULL
);