From 557e63ac32b5638e2e9cf3a0421d6c0b1ffc820d Mon Sep 17 00:00:00 2001 From: Mike Beaton Date: Mon, 2 Sep 2024 21:50:51 +0100 Subject: [PATCH] OcMainLib: Add UEFI/Unload config option to unload existing firmware drivers --- Changelog.md | 1 + Docs/Configuration.tex | 34 ++ Docs/Sample.plist | 2 + Docs/SampleCustom.plist | 2 + .../Acidanthera/Library/OcConfigurationLib.h | 10 +- Include/Acidanthera/Library/OcMainLib.h | 22 ++ .../OcConfigurationLib/OcConfigurationLib.c | 6 + Library/OcMainLib/OcMainLib.inf | 2 + Library/OcMainLib/OpenCoreUefi.c | 2 + Library/OcMainLib/OpenCoreUefiUnloadDrivers.c | 352 ++++++++++++++++++ 10 files changed, 432 insertions(+), 1 deletion(-) create mode 100644 Library/OcMainLib/OpenCoreUefiUnloadDrivers.c diff --git a/Changelog.md b/Changelog.md index 80077cefd7a..d376f22d7ac 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,7 @@ OpenCore Changelog ================== #### v1.0.2 - Fixed error in macrecovery when running headless, thx @mkorje +- Added `UEFI` `Unload` option to unload existing firmware drivers #### v1.0.1 - Updated code and added progress bar to macrecovery, thx @soyeonswife63 diff --git a/Docs/Configuration.tex b/Docs/Configuration.tex index 5f4d0fb4ea1..d062c169da4 100755 --- a/Docs/Configuration.tex +++ b/Docs/Configuration.tex @@ -7560,6 +7560,40 @@ \subsection{Properties}\label{uefiprops} could be the second 256 MB corrupted by the Intel HD 3000 or an area with faulty RAM. Refer to the \hyperref[uefirsvdprops]{ReservedMemory Properties} section below for details. +\item + \texttt{Unload}\\ + \textbf{Type}: \texttt{plist\ array}\\ + \textbf{Failsafe}: Empty\\ + \textbf{Description}: Unload specified firmware drivers. + + To be filled with \texttt{plist\ string} entries containing + the names of firmware drivers to unload before loading the + \texttt{Drivers} section. + This setting is typically only required if a user-provided driver is a variant of + an existing system firmware driver, and if the new driver would detect itself + as partially loaded, or otherwise fail to operate correctly, if the old + driver is not unloaded first. + + \textbf{Warning}: Unloading system firmware drivers is usually not required + and not recommended. Poorly written drivers may crash when + unloaded, or cause subsequent crashes (e.g by allowing themselves to be + unloaded even though they have active dependencies). However standard UEFI + network stack drivers should unload cleanly. + + \emph{Note 1}: See \texttt{SysReport/Drivers/DriverImageNames.txt} for the + list of drivers which this option can attempt to unload. + The relevant name is the driver component name. Drivers are only listed if they + implement \texttt{DriverBindingProtocol} and \texttt{LoadedImageProtocol}, + and have an available component name. + + \emph{Note 2}: The NVRAM \texttt{Lang} and \texttt{PlatformLang} variables + are ignored when determining the driver component names recognised by this + option, and listed in the \texttt{SysReport} file. This is in order to make + unloading images stable across changes in these variables. + The UEFI Shell \texttt{dh} command takes account of these variables, + so in some circumstances may display different driver component names from + those listed for this option, unless these variables are cleared. + \end{enumerate} \subsection{APFS Properties}\label{uefiapfsprops} diff --git a/Docs/Sample.plist b/Docs/Sample.plist index 673180dc09b..7f2b343ddbc 100644 --- a/Docs/Sample.plist +++ b/Docs/Sample.plist @@ -2051,6 +2051,8 @@ RuntimeCode + Unload + diff --git a/Docs/SampleCustom.plist b/Docs/SampleCustom.plist index 8ea9dacf0c5..920aed8abf0 100644 --- a/Docs/SampleCustom.plist +++ b/Docs/SampleCustom.plist @@ -2419,6 +2419,8 @@ RuntimeCode + Unload + diff --git a/Include/Acidanthera/Library/OcConfigurationLib.h b/Include/Acidanthera/Library/OcConfigurationLib.h index 470f54e8ccb..d92e1f64939 100644 --- a/Include/Acidanthera/Library/OcConfigurationLib.h +++ b/Include/Acidanthera/Library/OcConfigurationLib.h @@ -606,6 +606,13 @@ OC_DECLARE (OC_PLATFORM_CONFIG) Uefi section **/ +/// +/// Array of driver names to unload. +/// +#define OC_UEFI_UNLOAD_ARRAY_FIELDS(_, __) \ + OC_ARRAY (OC_STRING, _, __) +OC_DECLARE (OC_UEFI_UNLOAD_ARRAY) + /// /// Drivers is an ordered array of drivers to load. /// @@ -782,7 +789,8 @@ OC_DECLARE (OC_UEFI_RSVD_ARRAY) _(OC_UEFI_OUTPUT , Output , , OC_CONSTR2 (OC_UEFI_OUTPUT, _, __) , OC_DESTR (OC_UEFI_OUTPUT)) \ _(OC_UEFI_PROTOCOL_OVERRIDES , ProtocolOverrides , , OC_CONSTR2 (OC_UEFI_PROTOCOL_OVERRIDES, _, __) , OC_DESTR (OC_UEFI_PROTOCOL_OVERRIDES)) \ _(OC_UEFI_QUIRKS , Quirks , , OC_CONSTR2 (OC_UEFI_QUIRKS, _, __) , OC_DESTR (OC_UEFI_QUIRKS)) \ - _(OC_UEFI_RSVD_ARRAY , ReservedMemory , , OC_CONSTR2 (OC_UEFI_RSVD_ARRAY, _, __) , OC_DESTR (OC_UEFI_RSVD_ARRAY)) + _(OC_UEFI_RSVD_ARRAY , ReservedMemory , , OC_CONSTR2 (OC_UEFI_RSVD_ARRAY, _, __) , OC_DESTR (OC_UEFI_RSVD_ARRAY)) \ + _(OC_UEFI_UNLOAD_ARRAY , Unload , , OC_CONSTR2 (OC_UEFI_UNLOAD_ARRAY, _, __) , OC_DESTR (OC_UEFI_UNLOAD_ARRAY)) OC_DECLARE (OC_UEFI_CONFIG) /** diff --git a/Include/Acidanthera/Library/OcMainLib.h b/Include/Acidanthera/Library/OcMainLib.h index e853ff20e08..7453016ca3c 100644 --- a/Include/Acidanthera/Library/OcMainLib.h +++ b/Include/Acidanthera/Library/OcMainLib.h @@ -428,4 +428,26 @@ OcPlatformIs64BitSupported ( IN UINT32 KernelVersion ); +/** + Unload loaded images by name. + + @param[in] Config OpenCore configuration. +**/ +VOID +OcUnloadDrivers ( + IN OC_GLOBAL_CONFIG *Config + ); + +/** + Dump loaded image driver info to the specified directory. + + @param[in] Root Directory to write CPU data. + + @retval EFI_SUCCESS on success. +**/ +EFI_STATUS +OcDriverInfoDump ( + IN EFI_FILE_PROTOCOL *Root + ); + #endif // OC_MAIN_LIB diff --git a/Library/OcConfigurationLib/OcConfigurationLib.c b/Library/OcConfigurationLib/OcConfigurationLib.c index 09749e8769a..108ca05a566 100644 --- a/Library/OcConfigurationLib/OcConfigurationLib.c +++ b/Library/OcConfigurationLib/OcConfigurationLib.c @@ -73,6 +73,7 @@ OC_STRUCTORS (OC_PLATFORM_NVRAM_CONFIG, ()) OC_STRUCTORS (OC_PLATFORM_SMBIOS_CONFIG, ()) OC_STRUCTORS (OC_PLATFORM_CONFIG, ()) +OC_ARRAY_STRUCTORS (OC_UEFI_UNLOAD_ARRAY) OC_STRUCTORS (OC_UEFI_DRIVER_ENTRY, ()) OC_ARRAY_STRUCTORS (OC_UEFI_DRIVER_ARRAY) OC_STRUCTORS (OC_UEFI_APFS, ()) @@ -852,6 +853,10 @@ STATIC OC_SCHEMA mUefiReservedMemorySchema = OC_SCHEMA_DICT (NULL, mUefiReservedMemoryEntrySchema); +STATIC +OC_SCHEMA + mMiscUnloadImagesSchema = OC_SCHEMA_STRING (NULL); + STATIC OC_SCHEMA mUefiConfigurationSchema[] = { @@ -865,6 +870,7 @@ OC_SCHEMA OC_SCHEMA_DICT ("ProtocolOverrides", mUefiProtocolOverridesSchema), OC_SCHEMA_DICT ("Quirks", mUefiQuirksSchema), OC_SCHEMA_ARRAY_IN ("ReservedMemory", OC_GLOBAL_CONFIG, Uefi.ReservedMemory, &mUefiReservedMemorySchema), + OC_SCHEMA_ARRAY_IN ("Unload", OC_GLOBAL_CONFIG, Uefi.Unload, &mMiscUnloadImagesSchema), }; // diff --git a/Library/OcMainLib/OcMainLib.inf b/Library/OcMainLib/OcMainLib.inf index 21109ab6430..b3f770b6483 100644 --- a/Library/OcMainLib/OcMainLib.inf +++ b/Library/OcMainLib/OcMainLib.inf @@ -31,6 +31,7 @@ OpenCoreUefi.c OpenCoreUefiAudio.c OpenCoreUefiInOut.c + OpenCoreUefiUnloadDrivers.c OpenCoreVault.c [Packages] @@ -47,6 +48,7 @@ gEfiAudioDecodeProtocolGuid ## SOMETIMES_CONSUMES gEfiDevicePathProtocolGuid ## CONSUMES gEfiDevicePathProtocolGuid ## CONSUMES + gEfiDriverBindingProtocolGuid ## CONSUMES gEfiSimplePointerProtocolGuid ## SOMETIMES_CONSUMES gEfiLoadedImageProtocolGuid ## CONSUMES gEfiSimpleFileSystemProtocolGuid ## CONSUMES diff --git a/Library/OcMainLib/OpenCoreUefi.c b/Library/OcMainLib/OpenCoreUefi.c index 35e26c73680..6ad1b6929cd 100644 --- a/Library/OcMainLib/OpenCoreUefi.c +++ b/Library/OcMainLib/OpenCoreUefi.c @@ -897,6 +897,8 @@ OcLoadUefiSupport ( EFI_EVENT Event; BOOLEAN AccelEnabled; + OcUnloadDrivers (Config); + OcReinstallProtocols (Config); OcImageLoaderInit (Config->Booter.Quirks.ProtectUefiServices, Config->Booter.Quirks.FixupAppleEfiImages); diff --git a/Library/OcMainLib/OpenCoreUefiUnloadDrivers.c b/Library/OcMainLib/OpenCoreUefiUnloadDrivers.c new file mode 100644 index 00000000000..85cc0861162 --- /dev/null +++ b/Library/OcMainLib/OpenCoreUefiUnloadDrivers.c @@ -0,0 +1,352 @@ +/** @file + Unload images by name. Includes image name code from ShellPkg UefiHandleParsingLib.c. + + Copyright (c) 2010 - 2017, Intel Corporation. All rights reserved.
+ (C) Copyright 2013-2015 Hewlett-Packard Development Company, L.P.
+ (C) Copyright 2015-2021 Hewlett Packard Enterprise Development LP
+ Copyright (c) 2024, Mike Beaton. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include + +typedef +VOID +(*PROCESS_IMAGE)( + IN VOID *Context, + IN CONST CHAR16 *Name, + IN EFI_HANDLE Handle + ); + +typedef struct { + CHAR16 *UnloadName; + BOOLEAN Unloaded; +} UNLOAD_INFO; + +typedef struct { + UINTN UnloadNameCount; + UNLOAD_INFO *UnloadInfo; +} UNLOAD_IMAGE_CONTEXT; + +typedef struct { + CHAR8 *FileBuffer; + UINTN FileBufferSize; +} DRIVER_REPORT_CONTEXT; + +/** + Get the best supported language for this driver. + + First base on the user input language to search, otherwise get the first language + from the supported language list. The caller needs to free the best language buffer. + + @param[in] SupportedLanguages The supported languages for this driver. + @param[in] InputLanguage The user input language. + @param[in] Iso639Language Whether get language for ISO639. + + @return The best supported language for this driver. +**/ +CHAR8 * +EFIAPI +GetBestLanguageForDriver ( + IN CONST CHAR8 *SupportedLanguages, + IN CONST CHAR8 *InputLanguage, + IN BOOLEAN Iso639Language + ) +{ + CHAR8 *BestLanguage; + + BestLanguage = GetBestLanguage ( + SupportedLanguages, + Iso639Language, + (InputLanguage != NULL) ? InputLanguage : "", + SupportedLanguages, + NULL + ); + + return BestLanguage; +} + +/** + Function to retrieve the driver name (if possible) from the ComponentName or + ComponentName2 protocol + + @param[in] TheHandle The driver handle to get the name of. + @param[in] Language The language to use. + + @retval NULL The name could not be found. + @return A pointer to the string name. Do not de-allocate the memory. +**/ +CONST CHAR16 * +EFIAPI +GetStringNameFromHandle ( + IN CONST EFI_HANDLE TheHandle, + IN CONST CHAR8 *Language + ) +{ + EFI_COMPONENT_NAME2_PROTOCOL *CompNameStruct; + EFI_STATUS Status; + CHAR16 *RetVal; + CHAR8 *BestLang; + + BestLang = NULL; + + Status = gBS->OpenProtocol ( + TheHandle, + &gEfiComponentName2ProtocolGuid, + (VOID **)&CompNameStruct, + gImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + BestLang = GetBestLanguageForDriver (CompNameStruct->SupportedLanguages, Language, FALSE); + Status = CompNameStruct->GetDriverName (CompNameStruct, BestLang, &RetVal); + if (BestLang != NULL) { + FreePool (BestLang); + BestLang = NULL; + } + + if (!EFI_ERROR (Status)) { + return (RetVal); + } + } + + Status = gBS->OpenProtocol ( + TheHandle, + &gEfiComponentNameProtocolGuid, + (VOID **)&CompNameStruct, + gImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + BestLang = GetBestLanguageForDriver (CompNameStruct->SupportedLanguages, Language, FALSE); + Status = CompNameStruct->GetDriverName (CompNameStruct, BestLang, &RetVal); + if (BestLang != NULL) { + FreePool (BestLang); + } + + if (!EFI_ERROR (Status)) { + return (RetVal); + } + } + + return (NULL); +} + +VOID +ReportImageName ( + IN VOID *Context, + IN CONST CHAR16 *Name, + IN EFI_HANDLE Handle + ) +{ + DRIVER_REPORT_CONTEXT *ReportContext; + + ReportContext = Context; + + OcAsciiPrintBuffer ( + &ReportContext->FileBuffer, + &ReportContext->FileBufferSize, + "%s\n", + Name + ); +} + +VOID +UnloadImageByName ( + IN VOID *Context, + IN CONST CHAR16 *Name, + IN EFI_HANDLE Handle + ) +{ + EFI_STATUS Status; + UINTN Index; + UNLOAD_IMAGE_CONTEXT *UnloadContext; + + UnloadContext = Context; + + for (Index = 0; Index < UnloadContext->UnloadNameCount; Index++) { + if (StrCmp (UnloadContext->UnloadInfo[Index].UnloadName, Name) == 0) { + Status = gBS->UnloadImage (Handle); + DEBUG (( + EFI_ERROR (Status) ? DEBUG_WARN : DEBUG_INFO, + "OC: UnloadImage %s - %r\n", + UnloadContext->UnloadInfo[Index].UnloadName, + Status + )); + UnloadContext->UnloadInfo[Index].Unloaded = TRUE; ///< Do not report as 'not found' even if we failed to unload it. + break; + } + } +} + +// +// Perform action for all driver binding protocols which are +// present on the same handle as loaded image protocol and +// where the handle has a usable name. +// +// Note: In terms of choosing a name to identify the driver, while the loaded image +// section name is shorter and corresponds better to the driver's file name, and the +// firmware volume GUID from the loaded image path identifies the driver more uniquely, +// these are both not always available for a loaded image (across various firmware), +// whereas the driver component name normally is. +// +VOID +ProcessAllDrivers ( + IN VOID *Context, + PROCESS_IMAGE ProcessImage + ) +{ + EFI_STATUS Status; + UINTN HandleCount; + EFI_HANDLE *HandleBuffer; + EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; + UINTN Index; + CONST CHAR16 *Name; + + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiDriverBindingProtocolGuid, + NULL, + &HandleCount, + &HandleBuffer + ); + if (EFI_ERROR (Status)) { + return; + } + + for (Index = 0; Index < HandleCount; Index++) { + Status = gBS->HandleProtocol ( + HandleBuffer[Index], + &gEfiLoadedImageProtocolGuid, + (VOID **)&LoadedImage + ); + if (!EFI_ERROR (Status)) { + Name = GetStringNameFromHandle (HandleBuffer[Index], NULL); ///< Do not free this one. + if (Name != NULL) { + ProcessImage (Context, Name, HandleBuffer[Index]); + } + } + } + + FreePool (HandleBuffer); + + return; +} + +VOID +OcUnloadDrivers ( + IN OC_GLOBAL_CONFIG *Config + ) +{ + UINTN Index; + UINTN Index2; + UINTN UnloadInfoSize; + UNLOAD_IMAGE_CONTEXT UnloadContext; + + if (Config->Uefi.Unload.Count == 0) { + return; + } + + if (!BaseOverflowMulUN ( + Config->Uefi.Unload.Count, + sizeof (*UnloadContext.UnloadInfo), + &UnloadInfoSize + )) + { + UnloadContext.UnloadInfo = AllocateZeroPool (UnloadInfoSize); + } else { + UnloadContext.UnloadInfo = NULL; + } + + if (UnloadContext.UnloadInfo == NULL) { + DEBUG ((DEBUG_ERROR, "OC: Failed to allocate unload names!\n")); + return; + } + + for (Index = 0; Index < Config->Uefi.Unload.Count; ++Index) { + UnloadContext.UnloadInfo[Index].UnloadName = AsciiStrCopyToUnicode ( + OC_BLOB_GET ( + Config->Uefi.Unload.Values[Index] + ), + 0 + ); + if (UnloadContext.UnloadInfo[Index].UnloadName == NULL) { + for (Index2 = 0; Index2 < Index; ++Index2) { + FreePool (UnloadContext.UnloadInfo[Index2].UnloadName); + } + + FreePool (UnloadContext.UnloadInfo); + DEBUG ((DEBUG_ERROR, "OC: Failed to allocate unload names!\n")); + return; + } + } + + UnloadContext.UnloadNameCount = Config->Uefi.Unload.Count; + + ProcessAllDrivers (&UnloadContext, UnloadImageByName); + + for (Index = 0; Index < Config->Uefi.Unload.Count; ++Index) { + if (!UnloadContext.UnloadInfo[Index].Unloaded) { + DEBUG ((DEBUG_INFO, "OC: Unload %s - %r\n", UnloadContext.UnloadInfo[Index].UnloadName, EFI_NOT_FOUND)); + } + + FreePool (UnloadContext.UnloadInfo[Index].UnloadName); + } + + FreePool (UnloadContext.UnloadInfo); + + return; +} + +EFI_STATUS +OcDriverInfoDump ( + IN EFI_FILE_PROTOCOL *Root + ) +{ + EFI_STATUS Status; + DRIVER_REPORT_CONTEXT Context; + CHAR16 TmpFileName[32]; + + ASSERT (Root != NULL); + + Context.FileBufferSize = SIZE_1KB; + Context.FileBuffer = AllocateZeroPool (Context.FileBufferSize); + if (Context.FileBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + ProcessAllDrivers (&Context, ReportImageName); + + // + // Save dumped driver info to file. + // + if (Context.FileBuffer != NULL) { + UnicodeSPrint (TmpFileName, sizeof (TmpFileName), L"DriverImageNames.txt"); + Status = OcSetFileData (Root, TmpFileName, Context.FileBuffer, (UINT32)AsciiStrLen (Context.FileBuffer)); + DEBUG ((DEBUG_INFO, "OC: Dumped driver info - %r\n", Status)); + + FreePool (Context.FileBuffer); + } + + return EFI_SUCCESS; +}