Add DXE, SMM, and STANDALONE MM implementations of the MemoryProtectionHobLib.
Signed-off-by: Taylor Beebe <t...@taylorbeebe.com> Cc: Jian J Wang <jian.j.w...@intel.com> Cc: Liming Gao <gaolim...@byosoft.com.cn> Cc: Dandan Bi <dandan...@intel.com> --- MdeModulePkg/Library/MemoryProtectionHobLib/DxeMemoryProtectionHobLib.c | 182 ++++++++++++++++++++ MdeModulePkg/Library/MemoryProtectionHobLib/MmCommonMemoryProtectionHobLib.c | 139 +++++++++++++++ MdeModulePkg/Library/MemoryProtectionHobLib/SmmMemoryProtectionHobLib.c | 37 ++++ MdeModulePkg/Library/MemoryProtectionHobLib/StandaloneMmMemoryProtectionHobLib.c | 37 ++++ MdeModulePkg/Library/MemoryProtectionHobLib/DxeMemoryProtectionHobLib.inf | 34 ++++ MdeModulePkg/Library/MemoryProtectionHobLib/SmmMemoryProtectionHobLib.inf | 35 ++++ MdeModulePkg/Library/MemoryProtectionHobLib/StandaloneMmMemoryProtectionHobLib.inf | 36 ++++ MdeModulePkg/MdeModulePkg.dsc | 3 + 8 files changed, 503 insertions(+) diff --git a/MdeModulePkg/Library/MemoryProtectionHobLib/DxeMemoryProtectionHobLib.c b/MdeModulePkg/Library/MemoryProtectionHobLib/DxeMemoryProtectionHobLib.c new file mode 100644 index 000000000000..fa6137f90eba --- /dev/null +++ b/MdeModulePkg/Library/MemoryProtectionHobLib/DxeMemoryProtectionHobLib.c @@ -0,0 +1,182 @@ +/** @file +Library fills out gDxeMps global + +Copyright (c) Microsoft Corporation. +SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include <Uefi.h> +#include <Pi/PiMultiPhase.h> +#include <Uefi/UefiMultiPhase.h> + +#include <Library/DxeMemoryProtectionHobLib.h> +#include <Library/DebugLib.h> +#include <Library/HobLib.h> +#include <Library/BaseMemoryLib.h> + +DXE_MEMORY_PROTECTION_SETTINGS gDxeMps; + +/** + Gets the input EFI_MEMORY_TYPE from the input DXE_HEAP_GUARD_MEMORY_TYPES bitfield + + @param[in] MemoryType Memory type to check. + @param[in] HeapGuardMemoryType DXE_HEAP_GUARD_MEMORY_TYPES bitfield + + @return TRUE The given EFI_MEMORY_TYPE is TRUE in the given DXE_HEAP_GUARD_MEMORY_TYPES + @return FALSE The given EFI_MEMORY_TYPE is FALSE in the given DXE_HEAP_GUARD_MEMORY_TYPES +**/ +BOOLEAN +EFIAPI +GetDxeMemoryTypeSettingFromBitfield ( + IN EFI_MEMORY_TYPE MemoryType, + IN DXE_HEAP_GUARD_MEMORY_TYPES HeapGuardMemoryType + ) +{ + switch (MemoryType) { + case EfiReservedMemoryType: + return HeapGuardMemoryType.Fields.EfiReservedMemoryType; + case EfiLoaderCode: + return HeapGuardMemoryType.Fields.EfiLoaderCode; + case EfiLoaderData: + return HeapGuardMemoryType.Fields.EfiLoaderData; + case EfiBootServicesCode: + return HeapGuardMemoryType.Fields.EfiBootServicesCode; + case EfiBootServicesData: + return HeapGuardMemoryType.Fields.EfiBootServicesData; + case EfiRuntimeServicesCode: + return HeapGuardMemoryType.Fields.EfiRuntimeServicesCode; + case EfiRuntimeServicesData: + return HeapGuardMemoryType.Fields.EfiRuntimeServicesData; + case EfiConventionalMemory: + return HeapGuardMemoryType.Fields.EfiConventionalMemory; + case EfiUnusableMemory: + return HeapGuardMemoryType.Fields.EfiUnusableMemory; + case EfiACPIReclaimMemory: + return HeapGuardMemoryType.Fields.EfiACPIReclaimMemory; + case EfiACPIMemoryNVS: + return HeapGuardMemoryType.Fields.EfiACPIMemoryNVS; + case EfiMemoryMappedIO: + return HeapGuardMemoryType.Fields.EfiMemoryMappedIO; + case EfiMemoryMappedIOPortSpace: + return HeapGuardMemoryType.Fields.EfiMemoryMappedIOPortSpace; + case EfiPalCode: + return HeapGuardMemoryType.Fields.EfiPalCode; + case EfiPersistentMemory: + return HeapGuardMemoryType.Fields.EfiPersistentMemory; + default: + return FALSE; + } +} + +/** + This function checks the memory protection settings and provides warnings of conflicts and/or + potentially unforseen consequences from the settings. This logic will only ever turn off + protections to create consistency, never turn others on. +**/ +VOID +DxeMemoryProtectionSettingsConsistencyCheck ( + VOID + ) +{ + if ((gDxeMps.HeapGuardPolicy.Fields.PoolGuardEnabled || gDxeMps.HeapGuardPolicy.Fields.PageGuardEnabled) && + gDxeMps.HeapGuardPolicy.Fields.FreedMemoryGuardEnabled) + { + DEBUG (( + DEBUG_WARN, + "%a: - HeapGuardPolicy.FreedMemoryGuardEnabled and " + "UEFI HeapGuardPolicy.PoolGuardEnabled/HeapGuardPolicy.PageGuardEnabled " + "cannot be active at the same time. Setting all three to ZERO in " + "the memory protection settings global.\n", + __func__ + )); + ASSERT ( + !(gDxeMps.HeapGuardPolicy.Fields.FreedMemoryGuardEnabled && + (gDxeMps.HeapGuardPolicy.Fields.PoolGuardEnabled || gDxeMps.HeapGuardPolicy.Fields.PageGuardEnabled)) + ); + gDxeMps.HeapGuardPolicy.Fields.PoolGuardEnabled = 0; + gDxeMps.HeapGuardPolicy.Fields.PageGuardEnabled = 0; + gDxeMps.HeapGuardPolicy.Fields.FreedMemoryGuardEnabled = 0; + } + + if (gDxeMps.HeapGuardPoolType.Data && + (!(gDxeMps.HeapGuardPolicy.Fields.PoolGuardEnabled))) + { + DEBUG (( + DEBUG_WARN, + "%a: - Heap Guard Pool protections are active, " + "but neither HeapGuardPolicy.PoolGuardEnabled nor " + "HeapGuardPolicy.PoolGuardEnabled are active.\n", + __func__ + )); + } + + if (gDxeMps.HeapGuardPageType.Data && + (!(gDxeMps.HeapGuardPolicy.Fields.PageGuardEnabled))) + { + DEBUG (( + DEBUG_WARN, + "%a: - Heap Guard Page protections are active, " + "but neither HeapGuardPolicy.PageGuardEnabled nor " + "HeapGuardPolicy.PageGuardEnabled are active.\n", + __func__ + )); + } + + if (gDxeMps.NxProtectionPolicy.Fields.EfiBootServicesData != gDxeMps.NxProtectionPolicy.Fields.EfiConventionalMemory) { + DEBUG (( + DEBUG_WARN, + "%a: - NxProtectionPolicy.EfiBootServicesData " + "and NxProtectionPolicy.EfiConventionalMemory must have the same value. " + "Setting both to ZERO in the memory protection settings global.\n", + __func__ + )); + ASSERT ( + gDxeMps.NxProtectionPolicy.Fields.EfiBootServicesData == + gDxeMps.NxProtectionPolicy.Fields.EfiConventionalMemory + ); + gDxeMps.NxProtectionPolicy.Fields.EfiBootServicesData = 0; + gDxeMps.NxProtectionPolicy.Fields.EfiConventionalMemory = 0; + } +} + +/** + Populates gDxeMps global with the data present in the HOB. If the HOB entry does not exist, + this constructor will zero the memory protection settings. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The constructor always returns EFI_SUCCESS. +**/ +EFI_STATUS +EFIAPI +DxeMemoryProtectionHobLibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + VOID *Ptr; + + Ptr = GetFirstGuidHob (&gDxeMemoryProtectionSettingsGuid); + + // + // Cache the Memory Protection Settings HOB entry + // + if (Ptr != NULL) { + if (*((UINT8 *)GET_GUID_HOB_DATA (Ptr)) != (UINT8)DXE_MEMORY_PROTECTION_SETTINGS_CURRENT_VERSION) { + DEBUG (( + DEBUG_ERROR, + "%a: - Version number of the Memory Protection Settings HOB is invalid!\n", + __func__ + )); + ASSERT (*((UINT8 *)GET_GUID_HOB_DATA (Ptr)) == (UINT8)DXE_MEMORY_PROTECTION_SETTINGS_CURRENT_VERSION); + ZeroMem (&gDxeMps, sizeof (gDxeMps)); + return EFI_SUCCESS; + } + + CopyMem (&gDxeMps, GET_GUID_HOB_DATA (Ptr), sizeof (DXE_MEMORY_PROTECTION_SETTINGS)); + DxeMemoryProtectionSettingsConsistencyCheck (); + } + + return EFI_SUCCESS; +} diff --git a/MdeModulePkg/Library/MemoryProtectionHobLib/MmCommonMemoryProtectionHobLib.c b/MdeModulePkg/Library/MemoryProtectionHobLib/MmCommonMemoryProtectionHobLib.c new file mode 100644 index 000000000000..c546a943a515 --- /dev/null +++ b/MdeModulePkg/Library/MemoryProtectionHobLib/MmCommonMemoryProtectionHobLib.c @@ -0,0 +1,139 @@ +/** @file +Library fills out gMmMps global + +Copyright (c) Microsoft Corporation. +SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include <Uefi.h> +#include <Pi/PiMultiPhase.h> +#include <Uefi/UefiMultiPhase.h> + +#include <Library/MmMemoryProtectionHobLib.h> +#include <Library/DebugLib.h> +#include <Library/HobLib.h> +#include <Library/BaseMemoryLib.h> + +MM_MEMORY_PROTECTION_SETTINGS gMmMps; + +/** + Gets the input EFI_MEMORY_TYPE from the input MM_HEAP_GUARD_MEMORY_TYPES bitfield + + @param[in] MemoryType Memory type to check. + @param[in] HeapGuardMemoryType MM_HEAP_GUARD_MEMORY_TYPES bitfield + + @return TRUE The given EFI_MEMORY_TYPE is TRUE in the given MM_HEAP_GUARD_MEMORY_TYPES + @return FALSE The given EFI_MEMORY_TYPE is FALSE in the given MM_HEAP_GUARD_MEMORY_TYPES +**/ +BOOLEAN +EFIAPI +GetMmMemoryTypeSettingFromBitfield ( + IN EFI_MEMORY_TYPE MemoryType, + IN MM_HEAP_GUARD_MEMORY_TYPES HeapGuardMemoryType + ) +{ + switch (MemoryType) { + case EfiReservedMemoryType: + return HeapGuardMemoryType.Fields.EfiReservedMemoryType; + case EfiLoaderCode: + return HeapGuardMemoryType.Fields.EfiLoaderCode; + case EfiLoaderData: + return HeapGuardMemoryType.Fields.EfiLoaderData; + case EfiBootServicesCode: + return HeapGuardMemoryType.Fields.EfiBootServicesCode; + case EfiBootServicesData: + return HeapGuardMemoryType.Fields.EfiBootServicesData; + case EfiRuntimeServicesCode: + return HeapGuardMemoryType.Fields.EfiRuntimeServicesCode; + case EfiRuntimeServicesData: + return HeapGuardMemoryType.Fields.EfiRuntimeServicesData; + case EfiConventionalMemory: + return HeapGuardMemoryType.Fields.EfiConventionalMemory; + case EfiUnusableMemory: + return HeapGuardMemoryType.Fields.EfiUnusableMemory; + case EfiACPIReclaimMemory: + return HeapGuardMemoryType.Fields.EfiACPIReclaimMemory; + case EfiACPIMemoryNVS: + return HeapGuardMemoryType.Fields.EfiACPIMemoryNVS; + case EfiMemoryMappedIO: + return HeapGuardMemoryType.Fields.EfiMemoryMappedIO; + case EfiMemoryMappedIOPortSpace: + return HeapGuardMemoryType.Fields.EfiMemoryMappedIOPortSpace; + case EfiPalCode: + return HeapGuardMemoryType.Fields.EfiPalCode; + case EfiPersistentMemory: + return HeapGuardMemoryType.Fields.EfiPersistentMemory; + default: + return FALSE; + } +} + +/** + This function checks the memory protection settings and provides warnings of conflicts and/or + potentially unforseen consequences from the settings. This logic will only ever turn off + protections to create consistency, never turn others on. +**/ +VOID +MmMemoryProtectionSettingsConsistencyCheck ( + VOID + ) +{ + if (gMmMps.HeapGuardPoolType.Data && + (!(gMmMps.HeapGuardPolicy.Fields.PoolGuardEnabled))) + { + DEBUG (( + DEBUG_WARN, + "%a: - Bits set in gMmMps.HeapGuardPoolType, but gMmMps.HeapGuardPolicy.Fields.PoolGuardEnabled is inactive. " + "No pool guards will be set.\n", + __func__ + )); + } + + if (gMmMps.HeapGuardPageType.Data && + (!(gMmMps.HeapGuardPolicy.Fields.PageGuardEnabled))) + { + DEBUG (( + DEBUG_WARN, + "%a: - Bits are set in gMmMps.HeapGuardPageType, but gMmMps.HeapGuardPolicy.Fields.PageGuardEnabled is inactive. " + "No page guards will be set.\n", + __func__ + )); + } +} + +/** + Abstraction layer for library constructor of Standalone MM and SMM instances. + + @retval EFI_SUCCESS The constructor always returns EFI_SUCCESS. +**/ +EFI_STATUS +EFIAPI +MmMemoryProtectionHobLibConstructorCommon ( + VOID + ) +{ + VOID *Ptr; + + Ptr = GetFirstGuidHob (&gMmMemoryProtectionSettingsGuid); + + // + // Cache the Memory Protection Settings HOB entry + // + if (Ptr != NULL) { + if (*((UINT8 *)GET_GUID_HOB_DATA (Ptr)) != (UINT8)MM_MEMORY_PROTECTION_SETTINGS_CURRENT_VERSION) { + DEBUG (( + DEBUG_ERROR, + "%a: - Version number of the Memory Protection Settings HOB is invalid!\n", + __func__ + )); + ASSERT (*((UINT8 *)GET_GUID_HOB_DATA (Ptr)) == (UINT8)MM_MEMORY_PROTECTION_SETTINGS_CURRENT_VERSION); + ZeroMem (&gMmMps, sizeof (gMmMps)); + return EFI_SUCCESS; + } + + CopyMem (&gMmMps, GET_GUID_HOB_DATA (Ptr), sizeof (MM_MEMORY_PROTECTION_SETTINGS)); + MmMemoryProtectionSettingsConsistencyCheck (); + } + + return EFI_SUCCESS; +} diff --git a/MdeModulePkg/Library/MemoryProtectionHobLib/SmmMemoryProtectionHobLib.c b/MdeModulePkg/Library/MemoryProtectionHobLib/SmmMemoryProtectionHobLib.c new file mode 100644 index 000000000000..fffc90a7215c --- /dev/null +++ b/MdeModulePkg/Library/MemoryProtectionHobLib/SmmMemoryProtectionHobLib.c @@ -0,0 +1,37 @@ +/** @file +Library fills out gMmMps global + +Copyright (c) Microsoft Corporation. +SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include <PiSmm.h> + +/** + Abstraction layer for library constructor of Standalone MM and SMM instances. + + @retval EFI_SUCCESS The constructor always returns EFI_SUCCESS. +**/ +EFI_STATUS +EFIAPI +MmMemoryProtectionHobLibConstructorCommon ( + VOID + ); + +/** + Library constructor of SMM instance. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The constructor always returns EFI_SUCCESS. +**/ +EFI_STATUS +EFIAPI +SmmMemoryProtectionHobLibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + return MmMemoryProtectionHobLibConstructorCommon (); +} diff --git a/MdeModulePkg/Library/MemoryProtectionHobLib/StandaloneMmMemoryProtectionHobLib.c b/MdeModulePkg/Library/MemoryProtectionHobLib/StandaloneMmMemoryProtectionHobLib.c new file mode 100644 index 000000000000..3fd8b9f2593d --- /dev/null +++ b/MdeModulePkg/Library/MemoryProtectionHobLib/StandaloneMmMemoryProtectionHobLib.c @@ -0,0 +1,37 @@ +/** @file +Library fills out gMmMps global + +Copyright (c) Microsoft Corporation. +SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include <PiMm.h> + +/** + Abstraction layer for library constructor of Standalone MM and SMM instances. + + @retval EFI_SUCCESS The constructor always returns EFI_SUCCESS. +**/ +EFI_STATUS +EFIAPI +MmMemoryProtectionHobLibConstructorCommon ( + VOID + ); + +/** + Library constructor of Standalone MM instance. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI MM System Table. + + @retval EFI_SUCCESS The constructor always returns EFI_SUCCESS. +**/ +EFI_STATUS +EFIAPI +StandaloneMmMemoryProtectionHobLibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_MM_SYSTEM_TABLE *SystemTable + ) +{ + return MmMemoryProtectionHobLibConstructorCommon (); +} diff --git a/MdeModulePkg/Library/MemoryProtectionHobLib/DxeMemoryProtectionHobLib.inf b/MdeModulePkg/Library/MemoryProtectionHobLib/DxeMemoryProtectionHobLib.inf new file mode 100644 index 000000000000..57ca55446b81 --- /dev/null +++ b/MdeModulePkg/Library/MemoryProtectionHobLib/DxeMemoryProtectionHobLib.inf @@ -0,0 +1,34 @@ +## @file +# DXE library instance to support platform-specific global controls for all memory protections. +# +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = DxeMemoryProtectionHobLib + FILE_GUID = f497f7de-b9ab-4b9f-807e-89778922542d + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + LIBRARY_CLASS = DxeMemoryProtectionHobLib|DXE_DRIVER DXE_CORE UEFI_APPLICATION UEFI_DRIVER + CONSTRUCTOR = DxeMemoryProtectionHobLibConstructor + +# +# VALID_ARCHITECTURES = IA32 X64 AARCH64 +# + +[Sources] + DxeMemoryProtectionHobLib.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + HobLib + DebugLib + BaseMemoryLib + +[Guids] + gDxeMemoryProtectionSettingsGuid diff --git a/MdeModulePkg/Library/MemoryProtectionHobLib/SmmMemoryProtectionHobLib.inf b/MdeModulePkg/Library/MemoryProtectionHobLib/SmmMemoryProtectionHobLib.inf new file mode 100644 index 000000000000..4651158bd405 --- /dev/null +++ b/MdeModulePkg/Library/MemoryProtectionHobLib/SmmMemoryProtectionHobLib.inf @@ -0,0 +1,35 @@ +## @file +# SMM library instance to support platform-specific global controls for all memory protections. +# +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = SmmMemoryProtectionHobLib + FILE_GUID = dc9666f4-917f-400d-8026-2b3beeeff195 + MODULE_TYPE = DXE_SMM_DRIVER + VERSION_STRING = 1.0 + LIBRARY_CLASS = MmMemoryProtectionHobLib|SMM_CORE DXE_SMM_DRIVER + CONSTRUCTOR = SmmMemoryProtectionHobLibConstructor + +# +# VALID_ARCHITECTURES = IA32 X64 AARCH64 +# + +[Sources] + MmCommonMemoryProtectionHobLib.c + SmmMemoryProtectionHobLib.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + HobLib + DebugLib + BaseMemoryLib + +[Guids] + gMmMemoryProtectionSettingsGuid diff --git a/MdeModulePkg/Library/MemoryProtectionHobLib/StandaloneMmMemoryProtectionHobLib.inf b/MdeModulePkg/Library/MemoryProtectionHobLib/StandaloneMmMemoryProtectionHobLib.inf new file mode 100644 index 000000000000..3cadb5ec6e9a --- /dev/null +++ b/MdeModulePkg/Library/MemoryProtectionHobLib/StandaloneMmMemoryProtectionHobLib.inf @@ -0,0 +1,36 @@ +## @file +# SMM library instance to support platform-specific global controls for all memory protections. +# +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = StandaloneMmMemoryProtectionHobLib + FILE_GUID = C0A0D9C4-A249-483A-86EA-D73146D397B3 + MODULE_TYPE = MM_CORE_STANDALONE + PI_SPECIFICATION_VERSION = 0x00010032 + VERSION_STRING = 1.0 + LIBRARY_CLASS = MmMemoryProtectionHobLib|MM_CORE_STANDALONE MM_STANDALONE + CONSTRUCTOR = StandaloneMmMemoryProtectionHobLibConstructor + +# +# VALID_ARCHITECTURES = IA32 X64 AARCH64 +# + +[Sources] + MmCommonMemoryProtectionHobLib.c + StandaloneMmMemoryProtectionHobLib.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + HobLib + DebugLib + BaseMemoryLib + +[Guids] + gMmMemoryProtectionSettingsGuid diff --git a/MdeModulePkg/MdeModulePkg.dsc b/MdeModulePkg/MdeModulePkg.dsc index ab6848dc934b..bad4318771f9 100644 --- a/MdeModulePkg/MdeModulePkg.dsc +++ b/MdeModulePkg/MdeModulePkg.dsc @@ -237,6 +237,9 @@ [Components] MdeModulePkg/Library/DebugAgentLibNull/DebugAgentLibNull.inf MdeModulePkg/Library/DeviceManagerUiLib/DeviceManagerUiLib.inf MdeModulePkg/Library/LockBoxNullLib/LockBoxNullLib.inf + MdeModulePkg/Library/MemoryProtectionHobLib/DxeMemoryProtectionHobLib.inf + MdeModulePkg/Library/MemoryProtectionHobLib/SmmMemoryProtectionHobLib.inf + MdeModulePkg/Library/MemoryProtectionHobLib/StandaloneMmMemoryProtectionHobLib.inf MdeModulePkg/Library/MemoryProtectionHobLibNull/DxeMemoryProtectionHobLibNull.inf MdeModulePkg/Library/MemoryProtectionHobLibNull/MmMemoryProtectionHobLibNull.inf MdeModulePkg/Library/PciHostBridgeLibNull/PciHostBridgeLibNull.inf -- 2.36.1.windows.1 -=-=-=-=-=-=-=-=-=-=-=- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#105989): https://edk2.groups.io/g/devel/message/105989 Mute This Topic: https://groups.io/mt/99437081/21656 Group Owner: devel+ow...@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [arch...@mail-archive.com] -=-=-=-=-=-=-=-=-=-=-=-