From nobody Tue Feb 10 08:01:14 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of groups.io designates 66.175.222.108 as permitted sender) client-ip=66.175.222.108; envelope-from=bounce+27952+90953+1787277+3901457@groups.io; helo=mail02.groups.io; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of groups.io designates 66.175.222.108 as permitted sender) smtp.mailfrom=bounce+27952+90953+1787277+3901457@groups.io; dmarc=fail(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1656633241; cv=none; d=zohomail.com; s=zohoarc; b=hzTdVQjKVxI/Z1ZLJqKpl7AZZ6mInEntvLAFX5Lq2nUYJrqKTApnVnY1jqFmryqbqj+G3g6GcJfVO9YGZz4kFQJNtHwgw3/RHRENU7eu0Ny0VX/hwHUYYXJlRuuO0koTC3/wv0yulCE0yPBkZ20Hl+TDy/VmNNsq0IuAuDF9vAE= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1656633241; h=Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:References:Sender:Subject:To; bh=tncexGBRLoQITMu1A2xIhQgls7wKwK6CuF2Yi1bJqTk=; b=AAk4UgLr8a7BvpBhuvKnrq6e4MEe79h5OoeD6bWMFJXmxCiGCXv26yy7TNTlBFKEGsB7PN7NKgKGKsYP6BU4KrH/aYJ4UIukrE78OWY8sDn+mDRsZOmWHQcvUAGqHc66v4T3lNBWq1zuLqpLF/9o6NDJ6Sm4imWXUrNfQEhgFus= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of groups.io designates 66.175.222.108 as permitted sender) smtp.mailfrom=bounce+27952+90953+1787277+3901457@groups.io; dmarc=fail header.from= (p=none dis=none) Received: from mail02.groups.io (mail02.groups.io [66.175.222.108]) by mx.zohomail.com with SMTPS id 1656633241862642.1748478923848; Thu, 30 Jun 2022 16:54:01 -0700 (PDT) Return-Path: X-Received: by 127.0.0.2 with SMTP id UZbNYY1788612x7H3VuIt5Ka; Thu, 30 Jun 2022 16:54:01 -0700 X-Received: from mail-pg1-f182.google.com (mail-pg1-f182.google.com [209.85.215.182]) by mx.groups.io with SMTP id smtpd.web11.32157.1656633240853965508 for ; Thu, 30 Jun 2022 16:54:00 -0700 X-Received: by mail-pg1-f182.google.com with SMTP id v126so813276pgv.11 for ; Thu, 30 Jun 2022 16:54:00 -0700 (PDT) X-Gm-Message-State: uZGzFmUBymZt9OcUMmMGt5vxx1787277AA= X-Google-Smtp-Source: AGRyM1tQ/Wha3N/GN7jTfpX7HWoZV5p5rYyxHWQfAa/lNkNlgXj9/U8P58zqNYawtJy+ufewnCauyw== X-Received: by 2002:aa7:8f01:0:b0:525:2428:1157 with SMTP id x1-20020aa78f01000000b0052524281157mr17454620pfr.41.1656633240147; Thu, 30 Jun 2022 16:54:00 -0700 (PDT) X-Received: from MININT-0U7P5GU.redmond.corp.microsoft.com ([2001:4898:80e8:7:19ac:d515:5a95:7969]) by smtp.gmail.com with ESMTPSA id x199-20020a627cd0000000b00525243d0dc6sm14679202pfc.15.2022.06.30.16.53.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 30 Jun 2022 16:53:59 -0700 (PDT) From: "Kun Qin" To: devel@edk2.groups.io Cc: Jiewen Yao , Jian J Wang , Min Xu , Jiewen Yao , Michael Kubacki Subject: [edk2-devel] [PATCH v3 05/11] SecurityPkg: SecureBootVariableLib: Added newly supported interfaces Date: Thu, 30 Jun 2022 16:53:35 -0700 Message-Id: <20220630235341.1746-6-kuqin12@gmail.com> In-Reply-To: <20220630235341.1746-1-kuqin12@gmail.com> References: <20220630235341.1746-1-kuqin12@gmail.com> MIME-Version: 1.0 Precedence: Bulk List-Unsubscribe: List-Subscribe: List-Help: Sender: devel@edk2.groups.io List-Id: Mailing-List: list devel@edk2.groups.io; contact devel+owner@edk2.groups.io Reply-To: devel@edk2.groups.io,kuqin12@gmail.com Content-Transfer-Encoding: quoted-printable DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=groups.io; q=dns/txt; s=20140610; t=1656633241; bh=Ek+igEjTb2ZwV0LnNi3F7QyMa/mrAPrAcYY7EP9yhZg=; h=Cc:Date:From:Reply-To:Subject:To; b=ZvsBryDdxpXPmEvDzkMeyQpIZ75AWK+BYsd0pYl1SunGUQ40XLl5VGu6rWje/e3gPmd 7rQsusYUoA/11ea7UeFpodeHdsi6oe1DXmsBloJcmg5ImanWLjIQLC6sTA08NGLmf/M5O jINTH16eM8Jl7w9+5rlwsNd9w9bdP4zsfKI= X-ZohoMail-DKIM: pass (identity @groups.io) X-ZM-MESSAGEID: 1656633243490100024 Content-Type: text/plain; charset="utf-8" From: kuqin REF: https://bugzilla.tianocore.org/show_bug.cgi?id=3D3911 This change updated the interfaces provided by SecureBootVariableLib. The new additions provided interfaces to enroll single authenticated variable from input, a helper function to query secure boot status, enroll all secure boot variables from UefiSecureBoot.h defined data structures, a as well as a routine that deletes all secure boot related variables. Cc: Jiewen Yao Cc: Jian J Wang Cc: Min Xu Signed-off-by: Kun Qin Reviewed-by: Jiewen Yao Acked-by: Michael Kubacki --- Notes: v3: - Updated default timestamp to epoch time [Jiewen] - Added reviewed-by tag [Jiewen] - Added acked-by tag [Michael Kubacki] SecurityPkg/Library/SecureBootVariableLib/SecureBootVariableLib.c | 365 = ++++++++++++++++++++ SecurityPkg/Include/Library/SecureBootVariableLib.h | 69 = ++++ SecurityPkg/Library/SecureBootVariableLib/SecureBootVariableLib.inf | 3 + 3 files changed, 437 insertions(+) diff --git a/SecurityPkg/Library/SecureBootVariableLib/SecureBootVariableLi= b.c b/SecurityPkg/Library/SecureBootVariableLib/SecureBootVariableLib.c index f56f0322e943..abca249c6504 100644 --- a/SecurityPkg/Library/SecureBootVariableLib/SecureBootVariableLib.c +++ b/SecurityPkg/Library/SecureBootVariableLib/SecureBootVariableLib.c @@ -21,6 +21,7 @@ #include #include #include +#include =20 // This time can be used when deleting variables, as it should be greater = than any variable time. EFI_TIME mMaxTimestamp =3D { @@ -37,6 +38,24 @@ EFI_TIME mMaxTimestamp =3D { 0x00 }; =20 +// +// This epoch time is the date that is used when creating SecureBoot defau= lt variables. +// NOTE: This is a placeholder date that doesn't correspond to anything el= se. +// +EFI_TIME mDefaultPayloadTimestamp =3D { + 1970, // Year (1970) + 1, // Month (Jan) + 1, // Day (1) + 0, // Hour + 0, // Minute + 0, // Second + 0, // Pad1 + 0, // Nanosecond + 0, // Timezone (Dummy value) + 0, // Daylight (Dummy value) + 0 // Pad2 +}; + /** Creates EFI Signature List structure. =20 @param[in] Data A pointer to signature data. @@ -413,6 +432,44 @@ GetSetupMode ( return EFI_SUCCESS; } =20 +/** + Helper function to quickly determine whether SecureBoot is enabled. + + @retval TRUE SecureBoot is verifiably enabled. + @retval FALSE SecureBoot is either disabled or an error prevented = checking. + +**/ +BOOLEAN +EFIAPI +IsSecureBootEnabled ( + VOID + ) +{ + EFI_STATUS Status; + UINT8 *SecureBoot; + + SecureBoot =3D NULL; + + Status =3D GetEfiGlobalVariable2 (EFI_SECURE_BOOT_MODE_NAME, (VOID **)&S= ecureBoot, NULL); + // + // Skip verification if SecureBoot variable doesn't exist. + // + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Cannot check SecureBoot variable %r \n ", Status= )); + return FALSE; + } + + // + // Skip verification if SecureBoot is disabled but not AuditMode + // + if (*SecureBoot =3D=3D SECURE_BOOT_MODE_DISABLE) { + FreePool (SecureBoot); + return FALSE; + } else { + return TRUE; + } +} + /** Clears the content of the 'db' variable. =20 @@ -531,3 +588,311 @@ DeletePlatformKey ( ); return Status; } + +/** + This function will delete the secure boot keys, thus + disabling secure boot. + + @return EFI_SUCCESS or underlying failure code. +**/ +EFI_STATUS +EFIAPI +DeleteSecureBootVariables ( + VOID + ) +{ + EFI_STATUS Status, TempStatus; + + DEBUG ((DEBUG_INFO, "%a - Attempting to delete the Secure Boot variables= .\n", __FUNCTION__)); + + // + // Step 1: Notify that a PK update is coming shortly... + Status =3D DisablePKProtection (); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a - Failed to signal PK update start! %r\n", __= FUNCTION__, Status)); + // Classify this as a PK deletion error. + Status =3D EFI_ABORTED; + } + + // + // Step 2: Attempt to delete the PK. + // Let's try to nuke the PK, why not... + if (!EFI_ERROR (Status)) { + Status =3D DeletePlatformKey (); + DEBUG ((DEBUG_INFO, "%a - PK Delete =3D %r\n", __FUNCTION__, Status)); + // If the PK is not found, then our work here is done. + if (Status =3D=3D EFI_NOT_FOUND) { + Status =3D EFI_SUCCESS; + } + // If any other error occurred, let's inform the caller that the PK de= lete in particular failed. + else if (EFI_ERROR (Status)) { + Status =3D EFI_ABORTED; + } + } + + // + // Step 3: Attempt to delete remaining keys/databases... + // Now that the PK is deleted (assuming Status =3D=3D EFI_SUCCESS) the s= ystem is in SETUP_MODE. + // Arguably we could leave these variables in place and let them be dele= ted by whoever wants to + // update all the SecureBoot variables. However, for cleanliness sake, l= et's try to + // get rid of them here. + if (!EFI_ERROR (Status)) { + // + // If any of THESE steps have an error, report the error but attempt t= o delete all keys. + // Using TempStatus will prevent an error from being trampled by an EF= I_SUCCESS. + // Overwrite Status ONLY if TempStatus is an error. + // + // If the error is EFI_NOT_FOUND, we can safely ignore it since we wer= e trying to delete + // the variables anyway. + // + TempStatus =3D DeleteKEK (); + DEBUG ((DEBUG_INFO, "%a - KEK Delete =3D %r\n", __FUNCTION__, TempStat= us)); + if (EFI_ERROR (TempStatus) && (TempStatus !=3D EFI_NOT_FOUND)) { + Status =3D EFI_ACCESS_DENIED; + } + + TempStatus =3D DeleteDb (); + DEBUG ((DEBUG_INFO, "%a - db Delete =3D %r\n", __FUNCTION__, TempStatu= s)); + if (EFI_ERROR (TempStatus) && (TempStatus !=3D EFI_NOT_FOUND)) { + Status =3D EFI_ACCESS_DENIED; + } + + TempStatus =3D DeleteDbx (); + DEBUG ((DEBUG_INFO, "%a - dbx Delete =3D %r\n", __FUNCTION__, TempStat= us)); + if (EFI_ERROR (TempStatus) && (TempStatus !=3D EFI_NOT_FOUND)) { + Status =3D EFI_ACCESS_DENIED; + } + + TempStatus =3D DeleteDbt (); + DEBUG ((DEBUG_INFO, "%a - dbt Delete =3D %r\n", __FUNCTION__, TempStat= us)); + if (EFI_ERROR (TempStatus) && (TempStatus !=3D EFI_NOT_FOUND)) { + Status =3D EFI_ACCESS_DENIED; + } + } + + return Status; +}// DeleteSecureBootVariables() + +/** + A helper function to take in a variable payload, wrap it in the + proper authenticated variable structure, and install it in the + EFI variable space. + + @param[in] VariableName The name of the key/database. + @param[in] VendorGuid The namespace (ie. vendor GUID) of the variable + @param[in] DataSize Size parameter for target secure boot variable. + @param[in] Data Pointer to signature list formatted secure boo= t variable content. + + @retval EFI_SUCCESS The enrollment for authenticated variab= le was successful. + @retval EFI_OUT_OF_RESOURCES There are not enough memory resources t= o create time based payload. + @retval EFI_INVALID_PARAMETER The parameter is invalid. + @retval Others Unexpected error happens. +**/ +EFI_STATUS +EFIAPI +EnrollFromInput ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN UINTN DataSize, + IN VOID *Data + ) +{ + VOID *Payload; + UINTN PayloadSize; + EFI_STATUS Status; + + Payload =3D NULL; + + if ((VariableName =3D=3D NULL) || (VendorGuid =3D=3D 0)) { + DEBUG ((DEBUG_ERROR, "Input vendor variable invalid: %p and %p\n", Var= iableName, VendorGuid)); + Status =3D EFI_INVALID_PARAMETER; + goto Exit; + } + + if ((Data =3D=3D NULL) || (DataSize =3D=3D 0)) { + // You might as well just use DeleteVariable... + DEBUG ((DEBUG_ERROR, "Input argument invalid: %p: %x\n", Data, DataSiz= e)); + Status =3D EFI_INVALID_PARAMETER; + goto Exit; + } + + // Bring in the noise... + PayloadSize =3D DataSize; + Payload =3D AllocateZeroPool (DataSize); + // Bring in the funk... + if (Payload =3D=3D NULL) { + return EFI_OUT_OF_RESOURCES; + } else { + CopyMem (Payload, Data, DataSize); + } + + Status =3D CreateTimeBasedPayload (&PayloadSize, (UINT8 **)&Payload, &mD= efaultPayloadTimestamp); + if (EFI_ERROR (Status) || (Payload =3D=3D NULL)) { + DEBUG ((DEBUG_ERROR, "Fail to create time-based data payload: %r\n", S= tatus)); + Payload =3D NULL; + Status =3D EFI_OUT_OF_RESOURCES; + goto Exit; + } + + // + // Allocate memory for auth variable + // + Status =3D gRT->SetVariable ( + VariableName, + VendorGuid, + (EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS | + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS), + PayloadSize, + Payload + ); + + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "error: %a (\"%s\", %g): %r\n", + __FUNCTION__, + VariableName, + VendorGuid, + Status + )); + } + +Exit: + // + // Always Put Away Your Toys + // Payload will be reassigned by CreateTimeBasedPayload()... + if (Payload !=3D NULL) { + FreePool (Payload); + Payload =3D NULL; + } + + return Status; +} + +/** + Similar to DeleteSecureBootVariables, this function is used to unilatera= lly + force the state of related SB variables (db, dbx, dbt, KEK, PK, etc.) to= be + the built-in, hardcoded default vars. + + @param[in] SecureBootPayload Payload information for secure boot relat= ed keys. + + @retval EFI_SUCCESS SecureBoot keys are now set to def= aults. + @retval EFI_ABORTED SecureBoot keys are not empty. Ple= ase delete keys first + or follow standard methods of alte= ring keys (ie. use the signing system). + @retval EFI_SECURITY_VIOLATION Failed to create the PK. + @retval Others Something failed in one of the sub= functions. + +**/ +EFI_STATUS +EFIAPI +SetSecureBootVariablesToDefault ( + IN CONST SECURE_BOOT_PAYLOAD_INFO *SecureBootPayload + ) +{ + EFI_STATUS Status; + UINT8 *Data; + UINTN DataSize; + + DEBUG ((DEBUG_INFO, "%a() Entry\n", __FUNCTION__)); + + if (SecureBootPayload =3D=3D NULL) { + DEBUG ((DEBUG_ERROR, "%a - Invalid SecureBoot payload is supplied!\n",= __FUNCTION__)); + return EFI_INVALID_PARAMETER; + } + + // + // Right off the bat, if SecureBoot is currently enabled, bail. + if (IsSecureBootEnabled ()) { + DEBUG ((DEBUG_ERROR, "%a - Cannot set default keys while SecureBoot is= enabled!\n", __FUNCTION__)); + return EFI_ABORTED; + } + + DEBUG ((DEBUG_INFO, "%a - Setting up key %s!\n", __FUNCTION__, SecureBoo= tPayload->SecureBootKeyName)); + + // + // Start running down the list, creating variables in our wake. + // dbx is a good place to start. + Data =3D (UINT8 *)SecureBootPayload->DbxPtr; + DataSize =3D SecureBootPayload->DbxSize; + Status =3D EnrollFromInput ( + EFI_IMAGE_SECURITY_DATABASE1, + &gEfiImageSecurityDatabaseGuid, + DataSize, + Data + ); + + // If that went well, try the db (make sure to pick the right one!). + if (!EFI_ERROR (Status)) { + Data =3D (UINT8 *)SecureBootPayload->DbPtr; + DataSize =3D SecureBootPayload->DbSize; + Status =3D EnrollFromInput ( + EFI_IMAGE_SECURITY_DATABASE, + &gEfiImageSecurityDatabaseGuid, + DataSize, + Data + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a - Failed to enroll DB %r!\n", __FUNCTION__,= Status)); + } + } else { + DEBUG ((DEBUG_ERROR, "%a - Failed to enroll DBX %r!\n", __FUNCTION__, = Status)); + } + + // Keep it going. Keep it going. dbt if supplied... + if (!EFI_ERROR (Status) && (SecureBootPayload->DbtPtr !=3D NULL)) { + Data =3D (UINT8 *)SecureBootPayload->DbtPtr; + DataSize =3D SecureBootPayload->DbtSize; + Status =3D EnrollFromInput ( + EFI_IMAGE_SECURITY_DATABASE2, + &gEfiImageSecurityDatabaseGuid, + DataSize, + Data + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a - Failed to enroll DBT %r!\n", __FUNCTION__= , Status)); + } + } + + // Keep it going. Keep it going. KEK... + if (!EFI_ERROR (Status)) { + Data =3D (UINT8 *)SecureBootPayload->KekPtr; + DataSize =3D SecureBootPayload->KekSize; + Status =3D EnrollFromInput ( + EFI_KEY_EXCHANGE_KEY_NAME, + &gEfiGlobalVariableGuid, + DataSize, + Data + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a - Failed to enroll KEK %r!\n", __FUNCTION__= , Status)); + } + } + + // + // Finally! The Big Daddy of them all. + // The PK! + // + if (!EFI_ERROR (Status)) { + // + // Finally, install the key. + Data =3D (UINT8 *)SecureBootPayload->PkPtr; + DataSize =3D SecureBootPayload->PkSize; + Status =3D EnrollFromInput ( + EFI_PLATFORM_KEY_NAME, + &gEfiGlobalVariableGuid, + DataSize, + Data + ); + + // + // Report PK creation errors. + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a - Failed to update the PK! - %r\n", __FUNCT= ION__, Status)); + Status =3D EFI_SECURITY_VIOLATION; + } + } + + return Status; +} diff --git a/SecurityPkg/Include/Library/SecureBootVariableLib.h b/Security= Pkg/Include/Library/SecureBootVariableLib.h index 24ff0df067fa..c486801c318b 100644 --- a/SecurityPkg/Include/Library/SecureBootVariableLib.h +++ b/SecurityPkg/Include/Library/SecureBootVariableLib.h @@ -43,6 +43,19 @@ GetSetupMode ( OUT UINT8 *SetupMode ); =20 +/** + Helper function to quickly determine whether SecureBoot is enabled. + + @retval TRUE SecureBoot is verifiably enabled. + @retval FALSE SecureBoot is either disabled or an error prevented = checking. + +**/ +BOOLEAN +EFIAPI +IsSecureBootEnabled ( + VOID + ); + /** Create a EFI Signature List with data supplied from input argument. The input certificates from KeyInfo parameter should be DER-encoded @@ -161,4 +174,60 @@ DeletePlatformKey ( VOID ); =20 +/** + This function will delete the secure boot keys, thus + disabling secure boot. + + @return EFI_SUCCESS or underlying failure code. +**/ +EFI_STATUS +EFIAPI +DeleteSecureBootVariables ( + VOID + ); + +/** + A helper function to take in a variable payload, wrap it in the + proper authenticated variable structure, and install it in the + EFI variable space. + + @param[in] VariableName The name of the key/database. + @param[in] VendorGuid The namespace (ie. vendor GUID) of the variable + @param[in] DataSize Size parameter for target secure boot variable. + @param[in] Data Pointer to signature list formatted secure boo= t variable content. + + @retval EFI_SUCCESS The enrollment for authenticated variab= le was successful. + @retval EFI_OUT_OF_RESOURCES There are not enough memory resources t= o create time based payload. + @retval EFI_INVALID_PARAMETER The parameter is invalid. + @retval Others Unexpected error happens. +**/ +EFI_STATUS +EFIAPI +EnrollFromInput ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN UINTN DataSize, + IN VOID *Data + ); + +/** + Similar to DeleteSecureBootVariables, this function is used to unilatera= lly + force the state of related SB variables (db, dbx, dbt, KEK, PK, etc.) to= be + the built-in, hardcoded default vars. + + @param[in] SecureBootPayload Payload information for secure boot relat= ed keys. + + @retval EFI_SUCCESS SecureBoot keys are now set to def= aults. + @retval EFI_ABORTED SecureBoot keys are not empty. Ple= ase delete keys first + or follow standard methods of alte= ring keys (ie. use the signing system). + @retval EFI_SECURITY_VIOLATION Failed to create the PK. + @retval Others Something failed in one of the sub= functions. + +**/ +EFI_STATUS +EFIAPI +SetSecureBootVariablesToDefault ( + IN CONST SECURE_BOOT_PAYLOAD_INFO *SecureBootPayload + ); + #endif diff --git a/SecurityPkg/Library/SecureBootVariableLib/SecureBootVariableLi= b.inf b/SecurityPkg/Library/SecureBootVariableLib/SecureBootVariableLib.inf index 3d4b77cfb073..eabe9db6c93f 100644 --- a/SecurityPkg/Library/SecureBootVariableLib/SecureBootVariableLib.inf +++ b/SecurityPkg/Library/SecureBootVariableLib/SecureBootVariableLib.inf @@ -38,6 +38,9 @@ [LibraryClasses] BaseMemoryLib DebugLib MemoryAllocationLib + PlatformPKProtectionLib + UefiLib + UefiRuntimeServicesTableLib =20 [Guids] ## CONSUMES ## Variable:L"SetupMode" --=20 2.36.0.windows.1 -=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#90953): https://edk2.groups.io/g/devel/message/90953 Mute This Topic: https://groups.io/mt/92098742/1787277 Group Owner: devel+owner@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [importer@patchew.org] -=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-