From af1a266670d040d2f4083ff309d732d648afba2a Mon Sep 17 00:00:00 2001 From: Angelos Mouzakitis Date: Tue, 10 Oct 2023 14:33:42 +0000 Subject: Add submodule dependency files Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec --- .../Library/UefiBootManagerLib/BmDriverHealth.c | 585 +++++++++++++++++++++ 1 file changed, 585 insertions(+) create mode 100644 roms/edk2/MdeModulePkg/Library/UefiBootManagerLib/BmDriverHealth.c (limited to 'roms/edk2/MdeModulePkg/Library/UefiBootManagerLib/BmDriverHealth.c') diff --git a/roms/edk2/MdeModulePkg/Library/UefiBootManagerLib/BmDriverHealth.c b/roms/edk2/MdeModulePkg/Library/UefiBootManagerLib/BmDriverHealth.c new file mode 100644 index 000000000..52e35e451 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Library/UefiBootManagerLib/BmDriverHealth.c @@ -0,0 +1,585 @@ +/** @file + Library functions which relates with driver health. + +Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.
+(C) Copyright 2015 Hewlett-Packard Development Company, L.P.
+(C) Copyright 2016 Hewlett Packard Enterprise Development LP
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "InternalBm.h" + +GLOBAL_REMOVE_IF_UNREFERENCED + CHAR16 *mBmHealthStatusText[] = { + L"Healthy", + L"Repair Required", + L"Configuration Required", + L"Failed", + L"Reconnect Required", + L"Reboot Required" + }; + +/** + Return the controller name. + + @param DriverHealthHandle The handle on which the Driver Health protocol instance is retrieved. + @param ControllerHandle The handle of a controller that the driver specified by DriverBindingHandle is managing. + This handle specifies the controller whose name is to be returned. + @param ChildHandle The handle of the child controller to retrieve the name of. This is an + optional parameter that may be NULL. It will be NULL for device drivers. + It will also be NULL for bus drivers that attempt to retrieve the name + of the bus controller. It will not be NULL for a bus driver that attempts + to retrieve the name of a child controller. + + @return A pointer to the Unicode string to return. This Unicode string is the name of the controller + specified by ControllerHandle and ChildHandle. +**/ +CHAR16 * +BmGetControllerName ( + IN EFI_HANDLE DriverHealthHandle, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle + ) +{ + EFI_STATUS Status; + CHAR16 *ControllerName; + CHAR8 *LanguageVariable; + CHAR8 *BestLanguage; + BOOLEAN Iso639Language; + EFI_COMPONENT_NAME_PROTOCOL *ComponentName; + + ControllerName = NULL; + + // + // Locate Component Name (2) protocol on the driver binging handle. + // + Iso639Language = FALSE; + Status = gBS->HandleProtocol ( + DriverHealthHandle, + &gEfiComponentName2ProtocolGuid, + (VOID **) &ComponentName + ); + if (EFI_ERROR (Status)) { + Status = gBS->HandleProtocol ( + DriverHealthHandle, + &gEfiComponentNameProtocolGuid, + (VOID **) &ComponentName + ); + if (!EFI_ERROR (Status)) { + Iso639Language = TRUE; + } + } + + if (!EFI_ERROR (Status)) { + GetEfiGlobalVariable2 (Iso639Language ? L"Lang" : L"PlatformLang", (VOID**)&LanguageVariable, NULL); + BestLanguage = GetBestLanguage( + ComponentName->SupportedLanguages, + Iso639Language, + (LanguageVariable != NULL) ? LanguageVariable : "", + Iso639Language ? "eng" : "en-US", + NULL + ); + if (LanguageVariable != NULL) { + FreePool (LanguageVariable); + } + + Status = ComponentName->GetControllerName ( + ComponentName, + ControllerHandle, + ChildHandle, + BestLanguage, + &ControllerName + ); + } + + if (!EFI_ERROR (Status)) { + return AllocateCopyPool (StrSize (ControllerName), ControllerName); + } else { + return ConvertDevicePathToText ( + DevicePathFromHandle (ChildHandle != NULL ? ChildHandle : ControllerHandle), + FALSE, + FALSE + ); + } +} + +/** + Display a set of messages returned by the GetHealthStatus () service of the EFI Driver Health Protocol + + @param DriverHealthInfo Pointer to the Driver Health information entry. +**/ +VOID +BmDisplayMessages ( + IN EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO *DriverHealthInfo + ) +{ + UINTN Index; + EFI_STRING String; + CHAR16 *ControllerName; + + if (DriverHealthInfo->MessageList == NULL || + DriverHealthInfo->MessageList[0].HiiHandle == NULL) { + return; + } + + ControllerName = BmGetControllerName ( + DriverHealthInfo->DriverHealthHandle, + DriverHealthInfo->ControllerHandle, + DriverHealthInfo->ChildHandle + ); + + DEBUG ((EFI_D_INFO, "Controller: %s\n", ControllerName)); + Print (L"Controller: %s\n", ControllerName); + for (Index = 0; DriverHealthInfo->MessageList[Index].HiiHandle != NULL; Index++) { + String = HiiGetString ( + DriverHealthInfo->MessageList[Index].HiiHandle, + DriverHealthInfo->MessageList[Index].StringId, + NULL + ); + if (String != NULL) { + Print (L" %s\n", String); + DEBUG ((EFI_D_INFO, " %s\n", String)); + FreePool (String); + } + } + + if (ControllerName != NULL) { + FreePool (ControllerName); + } +} + +/** + The repair notify function. + @param Value A value between 0 and Limit that identifies the current progress + of the repair operation. + @param Limit The maximum value of Value for the current repair operation. + If Limit is 0, then the completion progress is indeterminate. + For example, a driver that wants to specify progress in percent + would use a Limit value of 100. + + @retval EFI_SUCCESS Successfully return from the notify function. +**/ +EFI_STATUS +EFIAPI +BmRepairNotify ( + IN UINTN Value, + IN UINTN Limit + ) +{ + DEBUG ((EFI_D_INFO, "[BDS]RepairNotify: %d/%d\n", Value, Limit)); + Print (L"[BDS]RepairNotify: %d/%d\n", Value, Limit); + + return EFI_SUCCESS; +} + +/** + Collect the Driver Health status of a single controller. + + @param DriverHealthInfo A pointer to the array containing all of the platform driver health information. + @param Count Return the updated array count. + @param DriverHealthHandle The handle on which the Driver Health protocol instance is retrieved. + @param ControllerHandle The handle of the controller.. + @param ChildHandle The handle of the child controller to retrieve the health + status on. This is an optional parameter that may be NULL. + + @retval Status The status returned from GetHealthStatus. + @retval EFI_ABORTED The health status is healthy so no further query is needed. + +**/ +EFI_STATUS +BmGetSingleControllerHealthStatus ( + IN OUT EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO **DriverHealthInfo, + IN OUT UINTN *Count, + IN EFI_HANDLE DriverHealthHandle, + IN EFI_HANDLE ControllerHandle, OPTIONAL + IN EFI_HANDLE ChildHandle OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_DRIVER_HEALTH_PROTOCOL *DriverHealth; + EFI_DRIVER_HEALTH_HII_MESSAGE *MessageList; + EFI_HII_HANDLE FormHiiHandle; + EFI_DRIVER_HEALTH_STATUS HealthStatus; + + ASSERT (DriverHealthHandle != NULL); + // + // Retrieve the Driver Health Protocol from DriverHandle + // + Status = gBS->HandleProtocol ( + DriverHealthHandle, + &gEfiDriverHealthProtocolGuid, + (VOID **) &DriverHealth + ); + ASSERT_EFI_ERROR (Status); + + + if (ControllerHandle == NULL) { + // + // If ControllerHandle is NULL, the return the cumulative health status of the driver + // + Status = DriverHealth->GetHealthStatus (DriverHealth, NULL, NULL, &HealthStatus, NULL, NULL); + if (!EFI_ERROR (Status) && HealthStatus == EfiDriverHealthStatusHealthy) { + *DriverHealthInfo = ReallocatePool ( + (*Count) * sizeof (EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO), + (*Count + 1) * sizeof (EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO), + *DriverHealthInfo + ); + ASSERT (*DriverHealthInfo != NULL); + + (*DriverHealthInfo)[*Count].DriverHealthHandle = DriverHealthHandle; + (*DriverHealthInfo)[*Count].DriverHealth = DriverHealth; + (*DriverHealthInfo)[*Count].HealthStatus = HealthStatus; + + *Count = *Count + 1; + + Status = EFI_ABORTED; + } + return Status; + } + + MessageList = NULL; + FormHiiHandle = NULL; + + // + // Collect the health status with the optional HII message list + // + Status = DriverHealth->GetHealthStatus (DriverHealth, ControllerHandle, ChildHandle, &HealthStatus, &MessageList, &FormHiiHandle); + if (!EFI_ERROR (Status)) { + *DriverHealthInfo = ReallocatePool ( + (*Count) * sizeof (EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO), + (*Count + 1) * sizeof (EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO), + *DriverHealthInfo + ); + ASSERT (*DriverHealthInfo != NULL); + (*DriverHealthInfo)[*Count].DriverHealth = DriverHealth; + (*DriverHealthInfo)[*Count].DriverHealthHandle = DriverHealthHandle; + (*DriverHealthInfo)[*Count].ControllerHandle = ControllerHandle; + (*DriverHealthInfo)[*Count].ChildHandle = ChildHandle; + (*DriverHealthInfo)[*Count].HiiHandle = FormHiiHandle; + (*DriverHealthInfo)[*Count].MessageList = MessageList; + (*DriverHealthInfo)[*Count].HealthStatus = HealthStatus; + + *Count = *Count + 1; + } + + return Status; +} + +/** + Return all the Driver Health information. + + When the cumulative health status of all the controllers managed by the + driver who produces the EFI_DRIVER_HEALTH_PROTOCOL is healthy, only one + EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO entry is created for such + EFI_DRIVER_HEALTH_PROTOCOL instance. + Otherwise, every controller creates one EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO + entry. Additionally every child controller creates one + EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO entry if the driver is a bus driver. + + @param Count Return the count of the Driver Health information. + + @retval NULL No Driver Health information is returned. + @retval !NULL Pointer to the Driver Health information array. +**/ +EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO * +EFIAPI +EfiBootManagerGetDriverHealthInfo ( + UINTN *Count + ) +{ + EFI_STATUS Status; + UINTN NumHandles; + EFI_HANDLE *DriverHealthHandles; + UINTN DriverHealthIndex; + EFI_HANDLE *Handles; + UINTN HandleCount; + UINTN ControllerIndex; + UINTN ChildIndex; + EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO *DriverHealthInfo; + + // + // Initialize local variables + // + *Count = 0; + DriverHealthInfo = NULL; + Handles = NULL; + DriverHealthHandles = NULL; + NumHandles = 0; + HandleCount = 0; + + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiDriverHealthProtocolGuid, + NULL, + &NumHandles, + &DriverHealthHandles + ); + + if (Status == EFI_NOT_FOUND || NumHandles == 0) { + // + // If there are no Driver Health Protocols handles, then return EFI_NOT_FOUND + // + return NULL; + } + + ASSERT_EFI_ERROR (Status); + ASSERT (DriverHealthHandles != NULL); + + // + // Check the health status of all controllers in the platform + // Start by looping through all the Driver Health Protocol handles in the handle database + // + for (DriverHealthIndex = 0; DriverHealthIndex < NumHandles; DriverHealthIndex++) { + // + // Get the cumulative health status of the driver + // + Status = BmGetSingleControllerHealthStatus (&DriverHealthInfo, Count, DriverHealthHandles[DriverHealthIndex], NULL, NULL); + if (EFI_ERROR (Status)) { + continue; + } + + // + // See if the list of all handles in the handle database has been retrieved + // + // + if (Handles == NULL) { + // + // Retrieve the list of all handles from the handle database + // + Status = gBS->LocateHandleBuffer ( + AllHandles, + NULL, + NULL, + &HandleCount, + &Handles + ); + ASSERT_EFI_ERROR (Status); + } + // + // Loop through all the controller handles in the handle database + // + for (ControllerIndex = 0; ControllerIndex < HandleCount; ControllerIndex++) { + Status = BmGetSingleControllerHealthStatus (&DriverHealthInfo, Count, DriverHealthHandles[DriverHealthIndex], Handles[ControllerIndex], NULL); + if (EFI_ERROR (Status)) { + continue; + } + + // + // Loop through all the child handles in the handle database + // + for (ChildIndex = 0; ChildIndex < HandleCount; ChildIndex++) { + Status = BmGetSingleControllerHealthStatus (&DriverHealthInfo, Count, DriverHealthHandles[DriverHealthIndex], Handles[ControllerIndex], Handles[ChildIndex]); + if (EFI_ERROR (Status)) { + continue; + } + } + } + } + + Status = EFI_SUCCESS; + + if (Handles != NULL) { + FreePool (Handles); + } + if (DriverHealthHandles != NULL) { + FreePool (DriverHealthHandles); + } + + return DriverHealthInfo; +} + +/** + Free the Driver Health information array. + + @param DriverHealthInfo Pointer to array of the Driver Health information. + @param Count Count of the array. + + @retval EFI_SUCCESS The array is freed. + @retval EFI_INVALID_PARAMETER The array is NULL. +**/ +EFI_STATUS +EFIAPI +EfiBootManagerFreeDriverHealthInfo ( + EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO *DriverHealthInfo, + UINTN Count + ) +{ + UINTN Index; + + for (Index = 0; Index < Count; Index++) { + if (DriverHealthInfo[Index].MessageList != NULL) { + FreePool (DriverHealthInfo[Index].MessageList); + } + } + return gBS->FreePool (DriverHealthInfo); +} + +/** + Repair all the controllers according to the Driver Health status queried. + + @param ReconnectRepairCount To record the number of recursive call of + this function itself. +**/ +VOID +BmRepairAllControllers ( + UINTN ReconnectRepairCount + ) +{ + EFI_STATUS Status; + EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO *DriverHealthInfo; + EFI_DRIVER_HEALTH_STATUS HealthStatus; + UINTN Count; + UINTN Index; + BOOLEAN RepairRequired; + BOOLEAN ConfigurationRequired; + BOOLEAN ReconnectRequired; + BOOLEAN RebootRequired; + EFI_HII_HANDLE *HiiHandles; + EFI_FORM_BROWSER2_PROTOCOL *FormBrowser2; + UINT32 MaxRepairCount; + UINT32 RepairCount; + + // + // Configure PcdDriverHealthConfigureForm to ZeroGuid to disable driver health check. + // + if (IsZeroGuid (PcdGetPtr (PcdDriverHealthConfigureForm))) { + return; + } + + Status = gBS->LocateProtocol (&gEfiFormBrowser2ProtocolGuid, NULL, (VOID **) &FormBrowser2); + ASSERT_EFI_ERROR (Status); + + MaxRepairCount = PcdGet32 (PcdMaxRepairCount); + RepairCount = 0; + + do { + RepairRequired = FALSE; + ConfigurationRequired = FALSE; + + // + // Deal with Repair Required + // + DriverHealthInfo = EfiBootManagerGetDriverHealthInfo (&Count); + for (Index = 0; Index < Count; Index++) { + if (DriverHealthInfo[Index].HealthStatus == EfiDriverHealthStatusConfigurationRequired) { + ConfigurationRequired = TRUE; + } + + if (DriverHealthInfo[Index].HealthStatus == EfiDriverHealthStatusRepairRequired) { + RepairRequired = TRUE; + + BmDisplayMessages (&DriverHealthInfo[Index]); + + Status = DriverHealthInfo[Index].DriverHealth->Repair ( + DriverHealthInfo[Index].DriverHealth, + DriverHealthInfo[Index].ControllerHandle, + DriverHealthInfo[Index].ChildHandle, + BmRepairNotify + ); + if (!EFI_ERROR (Status) && !ConfigurationRequired) { + Status = DriverHealthInfo[Index].DriverHealth->GetHealthStatus ( + DriverHealthInfo[Index].DriverHealth, + DriverHealthInfo[Index].ControllerHandle, + DriverHealthInfo[Index].ChildHandle, + &HealthStatus, + NULL, + NULL + ); + if (!EFI_ERROR (Status) && (HealthStatus == EfiDriverHealthStatusConfigurationRequired)) { + ConfigurationRequired = TRUE; + } + } + } + } + + if (ConfigurationRequired) { + HiiHandles = HiiGetHiiHandles (NULL); + if (HiiHandles != NULL) { + for (Index = 0; HiiHandles[Index] != NULL; Index++) { + Status = FormBrowser2->SendForm ( + FormBrowser2, + &HiiHandles[Index], + 1, + PcdGetPtr (PcdDriverHealthConfigureForm), + 0, + NULL, + NULL + ); + if (!EFI_ERROR (Status)) { + break; + } + } + FreePool (HiiHandles); + } + } + + EfiBootManagerFreeDriverHealthInfo (DriverHealthInfo, Count); + RepairCount++; + } while ((RepairRequired || ConfigurationRequired) && ((MaxRepairCount == 0) || (RepairCount < MaxRepairCount))); + + RebootRequired = FALSE; + ReconnectRequired = FALSE; + DriverHealthInfo = EfiBootManagerGetDriverHealthInfo (&Count); + for (Index = 0; Index < Count; Index++) { + + BmDisplayMessages (&DriverHealthInfo[Index]); + + if (DriverHealthInfo[Index].HealthStatus == EfiDriverHealthStatusReconnectRequired) { + Status = gBS->DisconnectController (DriverHealthInfo[Index].ControllerHandle, NULL, NULL); + if (EFI_ERROR (Status)) { + // + // Disconnect failed. Need to promote reconnect to a reboot. + // + RebootRequired = TRUE; + } else { + gBS->ConnectController (DriverHealthInfo[Index].ControllerHandle, NULL, NULL, TRUE); + ReconnectRequired = TRUE; + } + } + + if (DriverHealthInfo[Index].HealthStatus == EfiDriverHealthStatusRebootRequired) { + RebootRequired = TRUE; + } + } + EfiBootManagerFreeDriverHealthInfo (DriverHealthInfo, Count); + + + DEBUG_CODE ( + CHAR16 *ControllerName; + + DriverHealthInfo = EfiBootManagerGetDriverHealthInfo (&Count); + for (Index = 0; Index < Count; Index++) { + ControllerName = BmGetControllerName ( + DriverHealthInfo[Index].DriverHealthHandle, + DriverHealthInfo[Index].ControllerHandle, + DriverHealthInfo[Index].ChildHandle + ); + DEBUG (( + EFI_D_INFO, + "%02d: %s - %s\n", + Index, + ControllerName, + mBmHealthStatusText[DriverHealthInfo[Index].HealthStatus] + )); + if (ControllerName != NULL) { + FreePool (ControllerName); + } + } + EfiBootManagerFreeDriverHealthInfo (DriverHealthInfo, Count); + ); + + if (ReconnectRequired) { + if (ReconnectRepairCount < MAX_RECONNECT_REPAIR) { + BmRepairAllControllers (ReconnectRepairCount + 1); + } else { + DEBUG ((DEBUG_ERROR, "[%a:%d] Repair failed after %d retries.\n", + __FUNCTION__, __LINE__, ReconnectRepairCount)); + } + } + + if (RebootRequired) { + DEBUG ((EFI_D_INFO, "[BDS] One of the Driver Health instances requires rebooting.\n")); + gRT->ResetSystem (EfiResetWarm, EFI_SUCCESS, 0, NULL); + } +} -- cgit