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
))
127 ControllerName
= BmGetControllerName (
128 DriverHealthInfo
->DriverHealthHandle
,
129 DriverHealthInfo
->ControllerHandle
,
130 DriverHealthInfo
->ChildHandle
133 DEBUG ((DEBUG_INFO
, "Controller: %s\n", ControllerName
));
134 Print (L
"Controller: %s\n", ControllerName
);
135 for (Index
= 0; DriverHealthInfo
->MessageList
[Index
].HiiHandle
!= NULL
; Index
++) {
136 String
= HiiGetString (
137 DriverHealthInfo
->MessageList
[Index
].HiiHandle
,
138 DriverHealthInfo
->MessageList
[Index
].StringId
,
141 if (String
!= NULL
) {
142 Print (L
" %s\n", String
);
143 DEBUG ((DEBUG_INFO
, " %s\n", String
));
148 if (ControllerName
!= NULL
) {
149 FreePool (ControllerName
);
154 The repair notify function.
155 @param Value A value between 0 and Limit that identifies the current progress
156 of the repair operation.
157 @param Limit The maximum value of Value for the current repair operation.
158 If Limit is 0, then the completion progress is indeterminate.
159 For example, a driver that wants to specify progress in percent
160 would use a Limit value of 100.
162 @retval EFI_SUCCESS Successfully return from the notify function.
171 DEBUG ((DEBUG_INFO
, "[BDS]RepairNotify: %d/%d\n", Value
, Limit
));
172 Print (L
"[BDS]RepairNotify: %d/%d\n", Value
, Limit
);
178 Collect the Driver Health status of a single controller.
180 @param DriverHealthInfo A pointer to the array containing all of the platform driver health information.
181 @param Count Return the updated array count.
182 @param DriverHealthHandle The handle on which the Driver Health protocol instance is retrieved.
183 @param ControllerHandle The handle of the controller..
184 @param ChildHandle The handle of the child controller to retrieve the health
185 status on. This is an optional parameter that may be NULL.
187 @retval Status The status returned from GetHealthStatus.
188 @retval EFI_ABORTED The health status is healthy so no further query is needed.
192 BmGetSingleControllerHealthStatus (
193 IN OUT EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO
**DriverHealthInfo
,
195 IN EFI_HANDLE DriverHealthHandle
,
196 IN EFI_HANDLE ControllerHandle OPTIONAL
,
197 IN EFI_HANDLE ChildHandle OPTIONAL
201 EFI_DRIVER_HEALTH_PROTOCOL
*DriverHealth
;
202 EFI_DRIVER_HEALTH_HII_MESSAGE
*MessageList
;
203 EFI_HII_HANDLE FormHiiHandle
;
204 EFI_DRIVER_HEALTH_STATUS HealthStatus
;
206 ASSERT (DriverHealthHandle
!= NULL
);
208 // Retrieve the Driver Health Protocol from DriverHandle
210 Status
= gBS
->HandleProtocol (
212 &gEfiDriverHealthProtocolGuid
,
213 (VOID
**)&DriverHealth
215 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
;
243 FormHiiHandle
= NULL
;
246 // Collect the health status with the optional HII message list
248 Status
= DriverHealth
->GetHealthStatus (DriverHealth
, ControllerHandle
, ChildHandle
, &HealthStatus
, &MessageList
, &FormHiiHandle
);
249 if (!EFI_ERROR (Status
)) {
250 *DriverHealthInfo
= ReallocatePool (
251 (*Count
) * sizeof (EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO
),
252 (*Count
+ 1) * sizeof (EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO
),
255 ASSERT (*DriverHealthInfo
!= NULL
);
256 (*DriverHealthInfo
)[*Count
].DriverHealth
= DriverHealth
;
257 (*DriverHealthInfo
)[*Count
].DriverHealthHandle
= DriverHealthHandle
;
258 (*DriverHealthInfo
)[*Count
].ControllerHandle
= ControllerHandle
;
259 (*DriverHealthInfo
)[*Count
].ChildHandle
= ChildHandle
;
260 (*DriverHealthInfo
)[*Count
].HiiHandle
= FormHiiHandle
;
261 (*DriverHealthInfo
)[*Count
].MessageList
= MessageList
;
262 (*DriverHealthInfo
)[*Count
].HealthStatus
= HealthStatus
;
271 Return all the Driver Health information.
273 When the cumulative health status of all the controllers managed by the
274 driver who produces the EFI_DRIVER_HEALTH_PROTOCOL is healthy, only one
275 EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO entry is created for such
276 EFI_DRIVER_HEALTH_PROTOCOL instance.
277 Otherwise, every controller creates one EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO
278 entry. Additionally every child controller creates one
279 EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO entry if the driver is a bus driver.
281 @param Count Return the count of the Driver Health information.
283 @retval NULL No Driver Health information is returned.
284 @retval !NULL Pointer to the Driver Health information array.
286 EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO
*
288 EfiBootManagerGetDriverHealthInfo (
294 EFI_HANDLE
*DriverHealthHandles
;
295 UINTN DriverHealthIndex
;
298 UINTN ControllerIndex
;
300 EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO
*DriverHealthInfo
;
303 // Initialize local variables
306 DriverHealthInfo
= NULL
;
308 DriverHealthHandles
= NULL
;
312 Status
= gBS
->LocateHandleBuffer (
314 &gEfiDriverHealthProtocolGuid
,
320 if ((Status
== EFI_NOT_FOUND
) || (NumHandles
== 0)) {
322 // If there are no Driver Health Protocols handles, then return EFI_NOT_FOUND
327 ASSERT_EFI_ERROR (Status
);
328 ASSERT (DriverHealthHandles
!= NULL
);
331 // Check the health status of all controllers in the platform
332 // Start by looping through all the Driver Health Protocol handles in the handle database
334 for (DriverHealthIndex
= 0; DriverHealthIndex
< NumHandles
; DriverHealthIndex
++) {
336 // Get the cumulative health status of the driver
338 Status
= BmGetSingleControllerHealthStatus (&DriverHealthInfo
, Count
, DriverHealthHandles
[DriverHealthIndex
], NULL
, NULL
);
339 if (EFI_ERROR (Status
)) {
344 // See if the list of all handles in the handle database has been retrieved
347 if (Handles
== NULL
) {
349 // Retrieve the list of all handles from the handle database
351 Status
= gBS
->LocateHandleBuffer (
358 ASSERT_EFI_ERROR (Status
);
362 // Loop through all the controller handles in the handle database
364 for (ControllerIndex
= 0; ControllerIndex
< HandleCount
; ControllerIndex
++) {
365 Status
= BmGetSingleControllerHealthStatus (&DriverHealthInfo
, Count
, DriverHealthHandles
[DriverHealthIndex
], Handles
[ControllerIndex
], NULL
);
366 if (EFI_ERROR (Status
)) {
371 // Loop through all the child handles in the handle database
373 for (ChildIndex
= 0; ChildIndex
< HandleCount
; ChildIndex
++) {
374 Status
= BmGetSingleControllerHealthStatus (&DriverHealthInfo
, Count
, DriverHealthHandles
[DriverHealthIndex
], Handles
[ControllerIndex
], Handles
[ChildIndex
]);
375 if (EFI_ERROR (Status
)) {
382 Status
= EFI_SUCCESS
;
384 if (Handles
!= NULL
) {
388 if (DriverHealthHandles
!= NULL
) {
389 FreePool (DriverHealthHandles
);
392 return DriverHealthInfo
;
396 Free the Driver Health information array.
398 @param DriverHealthInfo Pointer to array of the Driver Health information.
399 @param Count Count of the array.
401 @retval EFI_SUCCESS The array is freed.
402 @retval EFI_INVALID_PARAMETER The array is NULL.
406 EfiBootManagerFreeDriverHealthInfo (
407 EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO
*DriverHealthInfo
,
413 for (Index
= 0; Index
< Count
; Index
++) {
414 if (DriverHealthInfo
[Index
].MessageList
!= NULL
) {
415 FreePool (DriverHealthInfo
[Index
].MessageList
);
419 return gBS
->FreePool (DriverHealthInfo
);
423 Repair all the controllers according to the Driver Health status queried.
425 @param ReconnectRepairCount To record the number of recursive call of
426 this function itself.
429 BmRepairAllControllers (
430 UINTN ReconnectRepairCount
434 EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO
*DriverHealthInfo
;
435 EFI_DRIVER_HEALTH_STATUS HealthStatus
;
438 BOOLEAN RepairRequired
;
439 BOOLEAN ConfigurationRequired
;
440 BOOLEAN ReconnectRequired
;
441 BOOLEAN RebootRequired
;
442 EFI_HII_HANDLE
*HiiHandles
;
443 EFI_FORM_BROWSER2_PROTOCOL
*FormBrowser2
;
444 UINT32 MaxRepairCount
;
448 // Configure PcdDriverHealthConfigureForm to ZeroGuid to disable driver health check.
450 if (IsZeroGuid (PcdGetPtr (PcdDriverHealthConfigureForm
))) {
454 Status
= gBS
->LocateProtocol (&gEfiFormBrowser2ProtocolGuid
, NULL
, (VOID
**)&FormBrowser2
);
455 ASSERT_EFI_ERROR (Status
);
457 MaxRepairCount
= PcdGet32 (PcdMaxRepairCount
);
461 RepairRequired
= FALSE
;
462 ConfigurationRequired
= FALSE
;
465 // Deal with Repair Required
467 DriverHealthInfo
= EfiBootManagerGetDriverHealthInfo (&Count
);
468 for (Index
= 0; Index
< Count
; Index
++) {
469 if (DriverHealthInfo
[Index
].HealthStatus
== EfiDriverHealthStatusConfigurationRequired
) {
470 ConfigurationRequired
= TRUE
;
473 if (DriverHealthInfo
[Index
].HealthStatus
== EfiDriverHealthStatusRepairRequired
) {
474 RepairRequired
= TRUE
;
476 BmDisplayMessages (&DriverHealthInfo
[Index
]);
478 Status
= DriverHealthInfo
[Index
].DriverHealth
->Repair (
479 DriverHealthInfo
[Index
].DriverHealth
,
480 DriverHealthInfo
[Index
].ControllerHandle
,
481 DriverHealthInfo
[Index
].ChildHandle
,
484 if (!EFI_ERROR (Status
) && !ConfigurationRequired
) {
485 Status
= DriverHealthInfo
[Index
].DriverHealth
->GetHealthStatus (
486 DriverHealthInfo
[Index
].DriverHealth
,
487 DriverHealthInfo
[Index
].ControllerHandle
,
488 DriverHealthInfo
[Index
].ChildHandle
,
493 if (!EFI_ERROR (Status
) && (HealthStatus
== EfiDriverHealthStatusConfigurationRequired
)) {
494 ConfigurationRequired
= TRUE
;
500 if (ConfigurationRequired
) {
501 HiiHandles
= HiiGetHiiHandles (NULL
);
502 if (HiiHandles
!= NULL
) {
503 for (Index
= 0; HiiHandles
[Index
] != NULL
; Index
++) {
504 Status
= FormBrowser2
->SendForm (
508 PcdGetPtr (PcdDriverHealthConfigureForm
),
513 if (!EFI_ERROR (Status
)) {
518 FreePool (HiiHandles
);
522 EfiBootManagerFreeDriverHealthInfo (DriverHealthInfo
, Count
);
524 } while ((RepairRequired
|| ConfigurationRequired
) && ((MaxRepairCount
== 0) || (RepairCount
< MaxRepairCount
)));
526 RebootRequired
= FALSE
;
527 ReconnectRequired
= FALSE
;
528 DriverHealthInfo
= EfiBootManagerGetDriverHealthInfo (&Count
);
529 for (Index
= 0; Index
< Count
; Index
++) {
530 BmDisplayMessages (&DriverHealthInfo
[Index
]);
532 if (DriverHealthInfo
[Index
].HealthStatus
== EfiDriverHealthStatusReconnectRequired
) {
533 Status
= gBS
->DisconnectController (DriverHealthInfo
[Index
].ControllerHandle
, NULL
, NULL
);
534 if (EFI_ERROR (Status
)) {
536 // Disconnect failed. Need to promote reconnect to a reboot.
538 RebootRequired
= TRUE
;
540 gBS
->ConnectController (DriverHealthInfo
[Index
].ControllerHandle
, NULL
, NULL
, TRUE
);
541 ReconnectRequired
= TRUE
;
545 if (DriverHealthInfo
[Index
].HealthStatus
== EfiDriverHealthStatusRebootRequired
) {
546 RebootRequired
= TRUE
;
550 EfiBootManagerFreeDriverHealthInfo (DriverHealthInfo
, Count
);
553 CHAR16
*ControllerName
;
555 DriverHealthInfo
= EfiBootManagerGetDriverHealthInfo (&Count
);
556 for (Index
= 0; Index
< Count
; Index
++) {
557 ControllerName
= BmGetControllerName (
558 DriverHealthInfo
[Index
].DriverHealthHandle
,
559 DriverHealthInfo
[Index
].ControllerHandle
,
560 DriverHealthInfo
[Index
].ChildHandle
567 mBmHealthStatusText
[DriverHealthInfo
[Index
].HealthStatus
]
569 if (ControllerName
!= NULL
) {
570 FreePool (ControllerName
);
574 EfiBootManagerFreeDriverHealthInfo (DriverHealthInfo
, Count
);
577 if (ReconnectRequired
) {
578 if (ReconnectRepairCount
< MAX_RECONNECT_REPAIR
) {
579 BmRepairAllControllers (ReconnectRepairCount
+ 1);
583 "[%a:%d] Repair failed after %d retries.\n",
591 if (RebootRequired
) {
592 DEBUG ((DEBUG_INFO
, "[BDS] One of the Driver Health instances requires rebooting.\n"));
593 gRT
->ResetSystem (EfiResetWarm
, EFI_SUCCESS
, 0, NULL
);