]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Library/UefiBootManagerLib/BmDriverHealth.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / MdeModulePkg / Library / UefiBootManagerLib / BmDriverHealth.c
1 /** @file
2 Library functions which relates with driver health.
3
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
8
9 **/
10
11 #include "InternalBm.h"
12
13 GLOBAL_REMOVE_IF_UNREFERENCED
14 CHAR16 *mBmHealthStatusText[] = {
15 L"Healthy",
16 L"Repair Required",
17 L"Configuration Required",
18 L"Failed",
19 L"Reconnect Required",
20 L"Reboot Required"
21 };
22
23 /**
24 Return the controller name.
25
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.
34
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.
37 **/
38 CHAR16 *
39 BmGetControllerName (
40 IN EFI_HANDLE DriverHealthHandle,
41 IN EFI_HANDLE ControllerHandle,
42 IN EFI_HANDLE ChildHandle
43 )
44 {
45 EFI_STATUS Status;
46 CHAR16 *ControllerName;
47 CHAR8 *LanguageVariable;
48 CHAR8 *BestLanguage;
49 BOOLEAN Iso639Language;
50 EFI_COMPONENT_NAME_PROTOCOL *ComponentName;
51
52 ControllerName = NULL;
53
54 //
55 // Locate Component Name (2) protocol on the driver binging handle.
56 //
57 Iso639Language = FALSE;
58 Status = gBS->HandleProtocol (
59 DriverHealthHandle,
60 &gEfiComponentName2ProtocolGuid,
61 (VOID **)&ComponentName
62 );
63 if (EFI_ERROR (Status)) {
64 Status = gBS->HandleProtocol (
65 DriverHealthHandle,
66 &gEfiComponentNameProtocolGuid,
67 (VOID **)&ComponentName
68 );
69 if (!EFI_ERROR (Status)) {
70 Iso639Language = TRUE;
71 }
72 }
73
74 if (!EFI_ERROR (Status)) {
75 GetEfiGlobalVariable2 (Iso639Language ? L"Lang" : L"PlatformLang", (VOID **)&LanguageVariable, NULL);
76 BestLanguage = GetBestLanguage (
77 ComponentName->SupportedLanguages,
78 Iso639Language,
79 (LanguageVariable != NULL) ? LanguageVariable : "",
80 Iso639Language ? "eng" : "en-US",
81 NULL
82 );
83 if (LanguageVariable != NULL) {
84 FreePool (LanguageVariable);
85 }
86
87 Status = ComponentName->GetControllerName (
88 ComponentName,
89 ControllerHandle,
90 ChildHandle,
91 BestLanguage,
92 &ControllerName
93 );
94 }
95
96 if (!EFI_ERROR (Status)) {
97 return AllocateCopyPool (StrSize (ControllerName), ControllerName);
98 } else {
99 return ConvertDevicePathToText (
100 DevicePathFromHandle (ChildHandle != NULL ? ChildHandle : ControllerHandle),
101 FALSE,
102 FALSE
103 );
104 }
105 }
106
107 /**
108 Display a set of messages returned by the GetHealthStatus () service of the EFI Driver Health Protocol
109
110 @param DriverHealthInfo Pointer to the Driver Health information entry.
111 **/
112 VOID
113 BmDisplayMessages (
114 IN EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO *DriverHealthInfo
115 )
116 {
117 UINTN Index;
118 EFI_STRING String;
119 CHAR16 *ControllerName;
120
121 if ((DriverHealthInfo->MessageList == NULL) ||
122 (DriverHealthInfo->MessageList[0].HiiHandle == NULL))
123 {
124 return;
125 }
126
127 ControllerName = BmGetControllerName (
128 DriverHealthInfo->DriverHealthHandle,
129 DriverHealthInfo->ControllerHandle,
130 DriverHealthInfo->ChildHandle
131 );
132
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,
139 NULL
140 );
141 if (String != NULL) {
142 Print (L" %s\n", String);
143 DEBUG ((DEBUG_INFO, " %s\n", String));
144 FreePool (String);
145 }
146 }
147
148 if (ControllerName != NULL) {
149 FreePool (ControllerName);
150 }
151 }
152
153 /**
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.
161
162 @retval EFI_SUCCESS Successfully return from the notify function.
163 **/
164 EFI_STATUS
165 EFIAPI
166 BmRepairNotify (
167 IN UINTN Value,
168 IN UINTN Limit
169 )
170 {
171 DEBUG ((DEBUG_INFO, "[BDS]RepairNotify: %d/%d\n", Value, Limit));
172 Print (L"[BDS]RepairNotify: %d/%d\n", Value, Limit);
173
174 return EFI_SUCCESS;
175 }
176
177 /**
178 Collect the Driver Health status of a single controller.
179
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.
186
187 @retval Status The status returned from GetHealthStatus.
188 @retval EFI_ABORTED The health status is healthy so no further query is needed.
189
190 **/
191 EFI_STATUS
192 BmGetSingleControllerHealthStatus (
193 IN OUT EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO **DriverHealthInfo,
194 IN OUT UINTN *Count,
195 IN EFI_HANDLE DriverHealthHandle,
196 IN EFI_HANDLE ControllerHandle OPTIONAL,
197 IN EFI_HANDLE ChildHandle OPTIONAL
198 )
199 {
200 EFI_STATUS Status;
201 EFI_DRIVER_HEALTH_PROTOCOL *DriverHealth;
202 EFI_DRIVER_HEALTH_HII_MESSAGE *MessageList;
203 EFI_HII_HANDLE FormHiiHandle;
204 EFI_DRIVER_HEALTH_STATUS HealthStatus;
205
206 ASSERT (DriverHealthHandle != NULL);
207 //
208 // Retrieve the Driver Health Protocol from DriverHandle
209 //
210 Status = gBS->HandleProtocol (
211 DriverHealthHandle,
212 &gEfiDriverHealthProtocolGuid,
213 (VOID **)&DriverHealth
214 );
215 ASSERT_EFI_ERROR (Status);
216
217 if (ControllerHandle == NULL) {
218 //
219 // If ControllerHandle is NULL, the return the cumulative health status of the driver
220 //
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),
226 *DriverHealthInfo
227 );
228 ASSERT (*DriverHealthInfo != NULL);
229
230 (*DriverHealthInfo)[*Count].DriverHealthHandle = DriverHealthHandle;
231 (*DriverHealthInfo)[*Count].DriverHealth = DriverHealth;
232 (*DriverHealthInfo)[*Count].HealthStatus = HealthStatus;
233
234 *Count = *Count + 1;
235
236 Status = EFI_ABORTED;
237 }
238
239 return Status;
240 }
241
242 MessageList = NULL;
243 FormHiiHandle = NULL;
244
245 //
246 // Collect the health status with the optional HII message list
247 //
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),
253 *DriverHealthInfo
254 );
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;
263
264 *Count = *Count + 1;
265 }
266
267 return Status;
268 }
269
270 /**
271 Return all the Driver Health information.
272
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.
280
281 @param Count Return the count of the Driver Health information.
282
283 @retval NULL No Driver Health information is returned.
284 @retval !NULL Pointer to the Driver Health information array.
285 **/
286 EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO *
287 EFIAPI
288 EfiBootManagerGetDriverHealthInfo (
289 UINTN *Count
290 )
291 {
292 EFI_STATUS Status;
293 UINTN NumHandles;
294 EFI_HANDLE *DriverHealthHandles;
295 UINTN DriverHealthIndex;
296 EFI_HANDLE *Handles;
297 UINTN HandleCount;
298 UINTN ControllerIndex;
299 UINTN ChildIndex;
300 EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO *DriverHealthInfo;
301
302 //
303 // Initialize local variables
304 //
305 *Count = 0;
306 DriverHealthInfo = NULL;
307 Handles = NULL;
308 DriverHealthHandles = NULL;
309 NumHandles = 0;
310 HandleCount = 0;
311
312 Status = gBS->LocateHandleBuffer (
313 ByProtocol,
314 &gEfiDriverHealthProtocolGuid,
315 NULL,
316 &NumHandles,
317 &DriverHealthHandles
318 );
319
320 if ((Status == EFI_NOT_FOUND) || (NumHandles == 0)) {
321 //
322 // If there are no Driver Health Protocols handles, then return EFI_NOT_FOUND
323 //
324 return NULL;
325 }
326
327 ASSERT_EFI_ERROR (Status);
328 ASSERT (DriverHealthHandles != NULL);
329
330 //
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
333 //
334 for (DriverHealthIndex = 0; DriverHealthIndex < NumHandles; DriverHealthIndex++) {
335 //
336 // Get the cumulative health status of the driver
337 //
338 Status = BmGetSingleControllerHealthStatus (&DriverHealthInfo, Count, DriverHealthHandles[DriverHealthIndex], NULL, NULL);
339 if (EFI_ERROR (Status)) {
340 continue;
341 }
342
343 //
344 // See if the list of all handles in the handle database has been retrieved
345 //
346 //
347 if (Handles == NULL) {
348 //
349 // Retrieve the list of all handles from the handle database
350 //
351 Status = gBS->LocateHandleBuffer (
352 AllHandles,
353 NULL,
354 NULL,
355 &HandleCount,
356 &Handles
357 );
358 ASSERT_EFI_ERROR (Status);
359 }
360
361 //
362 // Loop through all the controller handles in the handle database
363 //
364 for (ControllerIndex = 0; ControllerIndex < HandleCount; ControllerIndex++) {
365 Status = BmGetSingleControllerHealthStatus (&DriverHealthInfo, Count, DriverHealthHandles[DriverHealthIndex], Handles[ControllerIndex], NULL);
366 if (EFI_ERROR (Status)) {
367 continue;
368 }
369
370 //
371 // Loop through all the child handles in the handle database
372 //
373 for (ChildIndex = 0; ChildIndex < HandleCount; ChildIndex++) {
374 Status = BmGetSingleControllerHealthStatus (&DriverHealthInfo, Count, DriverHealthHandles[DriverHealthIndex], Handles[ControllerIndex], Handles[ChildIndex]);
375 if (EFI_ERROR (Status)) {
376 continue;
377 }
378 }
379 }
380 }
381
382 Status = EFI_SUCCESS;
383
384 if (Handles != NULL) {
385 FreePool (Handles);
386 }
387
388 if (DriverHealthHandles != NULL) {
389 FreePool (DriverHealthHandles);
390 }
391
392 return DriverHealthInfo;
393 }
394
395 /**
396 Free the Driver Health information array.
397
398 @param DriverHealthInfo Pointer to array of the Driver Health information.
399 @param Count Count of the array.
400
401 @retval EFI_SUCCESS The array is freed.
402 @retval EFI_INVALID_PARAMETER The array is NULL.
403 **/
404 EFI_STATUS
405 EFIAPI
406 EfiBootManagerFreeDriverHealthInfo (
407 EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO *DriverHealthInfo,
408 UINTN Count
409 )
410 {
411 UINTN Index;
412
413 for (Index = 0; Index < Count; Index++) {
414 if (DriverHealthInfo[Index].MessageList != NULL) {
415 FreePool (DriverHealthInfo[Index].MessageList);
416 }
417 }
418
419 return gBS->FreePool (DriverHealthInfo);
420 }
421
422 /**
423 Repair all the controllers according to the Driver Health status queried.
424
425 @param ReconnectRepairCount To record the number of recursive call of
426 this function itself.
427 **/
428 VOID
429 BmRepairAllControllers (
430 UINTN ReconnectRepairCount
431 )
432 {
433 EFI_STATUS Status;
434 EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO *DriverHealthInfo;
435 EFI_DRIVER_HEALTH_STATUS HealthStatus;
436 UINTN Count;
437 UINTN Index;
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;
445 UINT32 RepairCount;
446
447 //
448 // Configure PcdDriverHealthConfigureForm to ZeroGuid to disable driver health check.
449 //
450 if (IsZeroGuid (PcdGetPtr (PcdDriverHealthConfigureForm))) {
451 return;
452 }
453
454 Status = gBS->LocateProtocol (&gEfiFormBrowser2ProtocolGuid, NULL, (VOID **)&FormBrowser2);
455 ASSERT_EFI_ERROR (Status);
456
457 MaxRepairCount = PcdGet32 (PcdMaxRepairCount);
458 RepairCount = 0;
459
460 do {
461 RepairRequired = FALSE;
462 ConfigurationRequired = FALSE;
463
464 //
465 // Deal with Repair Required
466 //
467 DriverHealthInfo = EfiBootManagerGetDriverHealthInfo (&Count);
468 for (Index = 0; Index < Count; Index++) {
469 if (DriverHealthInfo[Index].HealthStatus == EfiDriverHealthStatusConfigurationRequired) {
470 ConfigurationRequired = TRUE;
471 }
472
473 if (DriverHealthInfo[Index].HealthStatus == EfiDriverHealthStatusRepairRequired) {
474 RepairRequired = TRUE;
475
476 BmDisplayMessages (&DriverHealthInfo[Index]);
477
478 Status = DriverHealthInfo[Index].DriverHealth->Repair (
479 DriverHealthInfo[Index].DriverHealth,
480 DriverHealthInfo[Index].ControllerHandle,
481 DriverHealthInfo[Index].ChildHandle,
482 BmRepairNotify
483 );
484 if (!EFI_ERROR (Status) && !ConfigurationRequired) {
485 Status = DriverHealthInfo[Index].DriverHealth->GetHealthStatus (
486 DriverHealthInfo[Index].DriverHealth,
487 DriverHealthInfo[Index].ControllerHandle,
488 DriverHealthInfo[Index].ChildHandle,
489 &HealthStatus,
490 NULL,
491 NULL
492 );
493 if (!EFI_ERROR (Status) && (HealthStatus == EfiDriverHealthStatusConfigurationRequired)) {
494 ConfigurationRequired = TRUE;
495 }
496 }
497 }
498 }
499
500 if (ConfigurationRequired) {
501 HiiHandles = HiiGetHiiHandles (NULL);
502 if (HiiHandles != NULL) {
503 for (Index = 0; HiiHandles[Index] != NULL; Index++) {
504 Status = FormBrowser2->SendForm (
505 FormBrowser2,
506 &HiiHandles[Index],
507 1,
508 PcdGetPtr (PcdDriverHealthConfigureForm),
509 0,
510 NULL,
511 NULL
512 );
513 if (!EFI_ERROR (Status)) {
514 break;
515 }
516 }
517
518 FreePool (HiiHandles);
519 }
520 }
521
522 EfiBootManagerFreeDriverHealthInfo (DriverHealthInfo, Count);
523 RepairCount++;
524 } while ((RepairRequired || ConfigurationRequired) && ((MaxRepairCount == 0) || (RepairCount < MaxRepairCount)));
525
526 RebootRequired = FALSE;
527 ReconnectRequired = FALSE;
528 DriverHealthInfo = EfiBootManagerGetDriverHealthInfo (&Count);
529 for (Index = 0; Index < Count; Index++) {
530 BmDisplayMessages (&DriverHealthInfo[Index]);
531
532 if (DriverHealthInfo[Index].HealthStatus == EfiDriverHealthStatusReconnectRequired) {
533 Status = gBS->DisconnectController (DriverHealthInfo[Index].ControllerHandle, NULL, NULL);
534 if (EFI_ERROR (Status)) {
535 //
536 // Disconnect failed. Need to promote reconnect to a reboot.
537 //
538 RebootRequired = TRUE;
539 } else {
540 gBS->ConnectController (DriverHealthInfo[Index].ControllerHandle, NULL, NULL, TRUE);
541 ReconnectRequired = TRUE;
542 }
543 }
544
545 if (DriverHealthInfo[Index].HealthStatus == EfiDriverHealthStatusRebootRequired) {
546 RebootRequired = TRUE;
547 }
548 }
549
550 EfiBootManagerFreeDriverHealthInfo (DriverHealthInfo, Count);
551
552 DEBUG_CODE_BEGIN ();
553 CHAR16 *ControllerName;
554
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
561 );
562 DEBUG ((
563 DEBUG_INFO,
564 "%02d: %s - %s\n",
565 Index,
566 ControllerName,
567 mBmHealthStatusText[DriverHealthInfo[Index].HealthStatus]
568 ));
569 if (ControllerName != NULL) {
570 FreePool (ControllerName);
571 }
572 }
573
574 EfiBootManagerFreeDriverHealthInfo (DriverHealthInfo, Count);
575 DEBUG_CODE_END ();
576
577 if (ReconnectRequired) {
578 if (ReconnectRepairCount < MAX_RECONNECT_REPAIR) {
579 BmRepairAllControllers (ReconnectRepairCount + 1);
580 } else {
581 DEBUG ((
582 DEBUG_ERROR,
583 "[%a:%d] Repair failed after %d retries.\n",
584 __FUNCTION__,
585 DEBUG_LINE_NUMBER,
586 ReconnectRepairCount
587 ));
588 }
589 }
590
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);
594 }
595 }