Hi Mike, > For the case when PropertyChanged left FALSE, allocated values AsciiStringValue and IntegerValue are leaked. Thanks for catching this memory issue. > MatchPropertyWithJsonContext() seems to be a heavy call. > The only useful thing this loop does in that case is printing missed property if enabled. Yes, it makes sense to break loop when we have EFI_NOT_FOUND case. I guess I don’t do this is just because I like to know how many properties are missing on BMC as you suggested. I addressed above comments in version2 path set here: https://edk2.groups.io/g/devel/message/117292 And we also need same changes for other feature drivers. I will send out another patch for them later. Regards, Nickle From: Mike Maslenkin Sent: Wednesday, March 27, 2024 6:57 AM To: Nickle Wang Cc: devel@edk2.groups.io; Abner Chang ; Igor Kulchytskyy ; Nick Ramirez Subject: Re: [edk2-devel] [edk2-redfish-client][PATCH 1/2] RedfishClientPkg/Features: support Redfish Secure Boot External email: Use caution opening links or attachments Hi Nickle, On Tue, Mar 26, 2024 at 4:57 PM Nickle Wang > wrote: Hi Mike, Thanks for your review. I addressed your review comments in pull request: https://github.com/tianocore/edk2-redfish-client/pull/83 I am waiting for Abner’s patch (PR#84) because I plan to incorporate Abner’s changes into this driver. I will send version 2 patch series here for review later. Regards, Nickle I see my comments were addressed, and I agree with that. But I think I found another minor leaks in a common pattern: + if (PropertyChecker (SecureBootCs->SecureBootMode, ProvisionMode)) { + Status = GetSetupMode (&SetupMode); + if (!EFI_ERROR (Status)) { + AsciiStringValue = AllocateZeroPool (SECURE_BOOT_MODE_STR_LEN *sizeof (CHAR8)); + if (AsciiStringValue != NULL) { + AsciiSPrint (AsciiStringValue, SECURE_BOOT_MODE_STR_LEN *sizeof (CHAR8), "%a", (SetupMode == USER_MODE ? SECURE_BOOT_USER_MODE : SECURE_BOOT_SETUP_MODE)); + if (ProvisionMode || (AsciiStrCmp (SecureBootCs->SecureBootMode, AsciiStringValue) != 0)) { + SecureBootCs->SecureBootMode = AsciiStringValue; + PropertyChanged = TRUE; + } + } + } else { + DEBUG ((DEBUG_ERROR, "%a: cannot read setup mode: %r\n", __func__, Status)); + } + } For the case when PropertyChanged left FALSE, allocated values AsciiStringValue and IntegerValue are leaked. Also there is a one new question below: From: Mike Maslenkin > Sent: Friday, March 15, 2024 3:38 AM To: devel@edk2.groups.io; Nickle Wang > Cc: Abner Chang >; Igor Kulchytskyy >; Nick Ramirez > Subject: Re: [edk2-devel] [edk2-redfish-client][PATCH 1/2] RedfishClientPkg/Features: support Redfish Secure Boot External email: Use caution opening links or attachments Hi Nickle, please find my comments below On 14. 3. 2024., at 17:53, Nickle Wang via groups.io > wrote: Introduce SecureBoot driver to support /redfish/v1/Systems/SYS/SecureBoot resource. Signed-off-by: Nickle Wang > Cc: Abner Chang > Cc: Igor Kulchytskyy > Cc: Nick Ramirez > --- .../RedfishClientComponents.dsc.inc | 2 + RedfishClientPkg/RedfishClientLibs.dsc.inc | 4 + .../SecureBoot/v1_1_0/Dxe/SecureBootDxe.inf | 60 ++ .../v1_1_0/Common/SecureBootCommon.h | 40 + .../v1_1_0/Common/SecureBootCommon.c | 756 ++++++++++++++++ .../SecureBoot/v1_1_0/Dxe/SecureBootDxe.c | 808 ++++++++++++++++++ RedfishClientPkg/RedfishClient.fdf.inc | 1 + 7 files changed, 1671 insertions(+) create mode 100644 RedfishClientPkg/Features/SecureBoot/v1_1_0/Dxe/SecureBootDxe.inf create mode 100644 RedfishClientPkg/Features/SecureBoot/v1_1_0/Common/SecureBootCommon.h create mode 100644 RedfishClientPkg/Features/SecureBoot/v1_1_0/Common/SecureBootCommon.c create mode 100644 RedfishClientPkg/Features/SecureBoot/v1_1_0/Dxe/SecureBootDxe.c diff --git a/RedfishClientPkg/RedfishClientComponents.dsc.inc b/RedfishClientPkg/RedfishClientComponents.dsc.inc index ae2a4b025..42fc0c299 100644 --- a/RedfishClientPkg/RedfishClientComponents.dsc.inc +++ b/RedfishClientPkg/RedfishClientComponents.dsc.inc @@ -34,6 +34,7 @@ RedfishClientPkg/Features/Bios/v1_0_9/Dxe/BiosDxe.inf RedfishClientPkg/Features/BootOptionCollection/BootOptionCollectionDxe.inf RedfishClientPkg/Features/BootOption/v1_0_4/Dxe/BootOptionDxe.inf + RedfishClientPkg/Features/SecureBoot/v1_1_0/Dxe/SecureBootDxe.inf !include RedfishClientPkg/RedfishJsonStructureDxe.dsc.inc @@ -47,3 +48,4 @@ RedfishClientPkg/Converter/Bios/v1_0_9/RedfishBios_V1_0_9_Dxe.inf RedfishClientPkg/Converter/BootOptionCollection/RedfishBootOptionCollection_Dxe.inf RedfishClientPkg/Converter/BootOption/v1_0_4/RedfishBootOption_V1_0_4_Dxe.inf + RedfishClientPkg/Converter/SecureBoot/v1_1_0/RedfishSecureBoot_V1_1_0_Dxe.inf diff --git a/RedfishClientPkg/RedfishClientLibs.dsc.inc b/RedfishClientPkg/RedfishClientLibs.dsc.inc index 6599926ab..9126465df 100644 --- a/RedfishClientPkg/RedfishClientLibs.dsc.inc +++ b/RedfishClientPkg/RedfishClientLibs.dsc.inc @@ -25,6 +25,8 @@ BiosV1_0_9Lib|RedfishClientPkg/ConverterLib/edk2library/Bios/v1_0_9/Lib.inf BootOptionCollectionLib|RedfishClientPkg/ConverterLib/edk2library/BootOptionCollection/Lib.inf BootOptionV1_0_4Lib|RedfishClientPkg/ConverterLib/edk2library/BootOption/v1_0_4/Lib.inf + SecureBootV1_1_0Lib|RedfishClientPkg/ConverterLib/edk2library/SecureBoot/v1_1_0/Lib.inf + # # Above modules should be pulled in by build tool. # @@ -42,3 +44,5 @@ RedfishAddendumLib|RedfishClientPkg/Library/RedfishAddendumLib/RedfishAddendumLib.inf RedfishDebugLib|RedfishPkg/Library/RedfishDebugLib/RedfishDebugLib.inf RedfishHttpLib|RedfishPkg/Library/RedfishHttpLib/RedfishHttpLib.inf + SecureBootVariableLib|SecurityPkg/Library/SecureBootVariableLib/SecureBootVariableLib.inf + PlatformPKProtectionLib|SecurityPkg/Library/PlatformPKProtectionLibVarPolicy/PlatformPKProtectionLibVarPolicy.inf diff --git a/RedfishClientPkg/Features/SecureBoot/v1_1_0/Dxe/SecureBootDxe.inf b/RedfishClientPkg/Features/SecureBoot/v1_1_0/Dxe/SecureBootDxe.inf new file mode 100644 index 000000000..1ad8c623f --- /dev/null +++ b/RedfishClientPkg/Features/SecureBoot/v1_1_0/Dxe/SecureBootDxe.inf @@ -0,0 +1,60 @@ +## @file +# +# (C) Copyright 2020-2022 Hewlett Packard Enterprise Development LP
+# Copyright (c) 2022-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = SecureBootDxe + FILE_GUID = 5E4025F8-DA42-468A-853E-6A1091D35052 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = RedfishResourceEntryPoint + UNLOAD_IMAGE = RedfishResourceUnload + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + SecurityPkg/SecurityPkg.dec + RedfishPkg/RedfishPkg.dec + RedfishClientPkg/RedfishClientPkg.dec + +[Sources] + ../Common/SecureBootCommon.h + ../Common/SecureBootCommon.c + SecureBootDxe.c + +[LibraryClasses] + BaseMemoryLib + DebugLib + EdkIIRedfishResourceConfigLib + RedfishFeatureUtilityLib + RedfishVersionLib + RedfishResourceIdentifyLib + SecureBootVariableLib + UefiLib + UefiDriverEntryPoint + RedfishAddendumLib + UefiRuntimeServicesTableLib + +[Protocols] + gEdkIIRedfishConfigHandlerProtocolGuid ## PRODUCED + gEfiRestJsonStructureProtocolGuid ## CONSUMED + gEdkIIRedfishResourceConfigProtocolGuid ## PRODUCED + gEdkIIRedfishFeatureProtocolGuid ## CONSUMED + +[Guids] + gEfiSecureBootEnableDisableGuid ## CONSUMED + +[Pcd] + gEfiRedfishClientPkgTokenSpaceGuid.PcdMaxRedfishSchemaStringSize + gEfiRedfishClientPkgTokenSpaceGuid.PcdMaxRedfishSchemaVersionSize + gEfiRedfishClientPkgTokenSpaceGuid.PcdRedfishSystemRebootRequired + +[Depex] + TRUE diff --git a/RedfishClientPkg/Features/SecureBoot/v1_1_0/Common/SecureBootCommon.h b/RedfishClientPkg/Features/SecureBoot/v1_1_0/Common/SecureBootCommon.h new file mode 100644 index 000000000..0d1824160 --- /dev/null +++ b/RedfishClientPkg/Features/SecureBoot/v1_1_0/Common/SecureBootCommon.h @@ -0,0 +1,40 @@ +/** @file + + Redfish feature driver implementation - internal header file + (C) Copyright 2020-2022 Hewlett Packard Enterprise Development LP
+ Copyright (c) 2022-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef EFI_REDFISH_SECUREBOOT_COMMON_H_ +#define EFI_REDFISH_SECUREBOOT_COMMON_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +// +// Schema information. +// +#define REDFISH_MANAGED_URI L"Systems/{}/SecureBoot" +#define REDFISH_DUMMY_CONFIG_LANG L"Systems/{1}/SecureBoot" +#define MAX_URI_LENGTH 256 +#define RESOURCE_SCHEMA "SecureBoot" +#define RESOURCE_SCHEMA_MAJOR "1" +#define RESOURCE_SCHEMA_MINOR "1" +#define RESOURCE_SCHEMA_ERRATA "0" +#define RESOURCE_SCHEMA_VERSION "v1_1_0" +#define SECURE_BOOT_SETUP_MODE "SetupMode" +#define SECURE_BOOT_USER_MODE "UserMode" +#define SECURE_BOOT_ENABLED "Enabled" +#define SECURE_BOOT_DISABLED "Disabled" +#define SECURE_BOOT_MODE_STR_LEN 16 + +#endif diff --git a/RedfishClientPkg/Features/SecureBoot/v1_1_0/Common/SecureBootCommon.c b/RedfishClientPkg/Features/SecureBoot/v1_1_0/Common/SecureBootCommon.c new file mode 100644 index 000000000..56a45ee72 --- /dev/null +++ b/RedfishClientPkg/Features/SecureBoot/v1_1_0/Common/SecureBootCommon.c @@ -0,0 +1,756 @@ +/** @file + Redfish feature driver implementation - common functions + + (C) Copyright 2020-2022 Hewlett Packard Enterprise Development LP
+ Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "SecureBootCommon.h" + +CHAR8 SecureBootEmptyJson[] = "{\"@odata.id\": \"\", \"@odata.type\": \"#SecureBoot.v1_1_0.SecureBoot\", \"Id\": \"\", \"Name\": \"\", \"Attributes\":{}}"; + +REDFISH_RESOURCE_COMMON_PRIVATE *mRedfishResourcePrivate = NULL; +EFI_HANDLE mRedfishResourceConfigProtocolHandle = NULL; +CHAR16 *mSecureBootSupportedAttributes[SECURE_BOOT_MODE_STR_LEN] = { + L"SecureBootCurrentBoot", + L"SecureBootEnable", + L"SecureBootMode" +}; + +/** + Read EFI_SECURE_BOOT_ENABLE_NAME variable and return its value to caller. + + @retval BOOLEAN TRUE when EFI_SECURE_BOOT_ENABLE_NAME value is SECURE_BOOT_ENABLE + FALSE when EFI_SECURE_BOOT_ENABLE_NAME value is SECURE_BOOT_DISABLE +**/ +BOOLEAN +RedfishReadSecureBootEnable ( + VOID + ) +{ + UINT8 *Buffer; + BOOLEAN SecureBootEnableValue; + + Buffer = NULL; + SecureBootEnableValue = FALSE; + + GetVariable2 ( + EFI_SECURE_BOOT_ENABLE_NAME, + &gEfiSecureBootEnableDisableGuid, + (VOID **)&Buffer, + NULL + ); + + if (Buffer != NULL) { + if (*Buffer == SECURE_BOOT_ENABLE) { + SecureBootEnableValue = TRUE; + } + + FreePool (Buffer); + } + + return SecureBootEnableValue; +} + +/** + Write EFI_SECURE_BOOT_ENABLE_NAME variable with given value. + + @param[in] SecureBootEnableValue Value to write. TRUE is SECURE_BOOT_ENABLE. + FALSE is SECURE_BOOT_DISABLE. + + @retval EFI_SUCCESS Write value successfully. + @retval Others Some error happened. +**/ +EFI_STATUS +RedfishWriteSecureBootEnable ( + BOOLEAN SecureBootEnableValue + ) +{ + EFI_STATUS Status; + UINT8 VarValue; + + VarValue = (SecureBootEnableValue ? SECURE_BOOT_ENABLE : SECURE_BOOT_DISABLE); + Status = gRT->SetVariable ( + EFI_SECURE_BOOT_ENABLE_NAME, + &gEfiSecureBootEnableDisableGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + sizeof (VarValue), + &VarValue + ); + + return Status; +} + +/** + Consume Redfish resource in given Json data. + + @param[in] This Pointer to REDFISH_RESOURCE_COMMON_PRIVATE instance. + @param[in] Json The JSON to consume. + @param[in] HeaderEtag The Etag string returned in HTTP header. + + @retval EFI_SUCCESS Consume Redfish attribute successfully. + @retval Others Some error happened. + +**/ +EFI_STATUS +RedfishConsumeResourceCommon ( + IN REDFISH_RESOURCE_COMMON_PRIVATE *Private, + IN CHAR8 *Json, + IN CHAR8 *HeaderEtag OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_REDFISH_SECUREBOOT_V1_1_0 *SecureBoot; + EFI_REDFISH_SECUREBOOT_V1_1_0_CS *SecureBootCs; + BOOLEAN SecureBootEnableDisable; + + if ((Private == NULL) || IS_EMPTY_STRING (Json)) { + return EFI_INVALID_PARAMETER; + } + + SecureBoot = NULL; + SecureBootCs = NULL; + SecureBootEnableDisable = RedfishReadSecureBootEnable (); + + Status = Private->JsonStructProtocol->ToStructure ( + Private->JsonStructProtocol, + NULL, + Json, + (EFI_REST_JSON_STRUCTURE_HEADER **)&SecureBoot + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: ToStructure() failed: %r\n", __func__, Status)); + return Status; + } + + SecureBootCs = SecureBoot->SecureBoot; + + // + // Check ETAG to see if we need to consume it + // + if (CheckEtag (Private->Uri, HeaderEtag, SecureBootCs->odata_etag)) { + // + // No change + // + DEBUG ((REDFISH_DEBUG_TRACE, "%a: ETAG: %s has no change, ignore consume action\n", __func__, Private->Uri)); + Status = EFI_ALREADY_STARTED; + goto ON_RELEASE; + } + + // + // Secure boot enable + // + if (SecureBootCs->SecureBootEnable != NULL) { + if (SecureBootEnableDisable != *SecureBootCs->SecureBootEnable) { + // + // Write value to "SecureBootEnable" variable. AuthVariableLib will enable or disable secure boot + // based on "SecureBootEnable" value. + // + Status = RedfishWriteSecureBootEnable (*SecureBootCs->SecureBootEnable); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: write secure boot enable disable failed: %r\n", __func__, Status)); + } else { + REDFISH_ENABLE_SYSTEM_REBOOT (); + } + } else { + DEBUG ((REDFISH_DEBUG_TRACE, "%a: secure boot mode is not changed\n", __func__)); + } + } + +ON_RELEASE: + + // + // Release resource. + // + Private->JsonStructProtocol->DestoryStructure ( + Private->JsonStructProtocol, + (EFI_REST_JSON_STRUCTURE_HEADER *)SecureBoot + ); + + return EFI_SUCCESS; +} + +/** + Provision Redfish resource. This function reads secure boot variable and convert it + to Redfish attribute. + + @param[in] JsonStructProtocol Pointer to Json structure protocol. + @param[in] InputJson Jason data on input. + @param[in] ResourceId Resource ID. This is optional. + @param[in] ConfigureLang Configure language for this Redfish resource. + @param[in] ProvisionMode TRUE when this is to provision Redfish attribute to + Redfish service. FALSE is to update Redfish attribute + to Redfish service. + @param[out] ResultJson Json data on output. + + @retval EFI_SUCCESS Provision Redfish attribute successfully. + @retval Others Some error happened. + +**/ +EFI_STATUS +ProvisioningSecureBootProperties ( + IN EFI_REST_JSON_STRUCTURE_PROTOCOL *JsonStructProtocol, + IN CHAR8 *InputJson, + IN CHAR8 *ResourceId OPTIONAL, + IN EFI_STRING ConfigureLang, + IN BOOLEAN ProvisionMode, + OUT CHAR8 **ResultJson + ) +{ + EFI_REDFISH_SECUREBOOT_V1_1_0 *SecureBoot; + EFI_REDFISH_SECUREBOOT_V1_1_0_CS *SecureBootCs; + EFI_STATUS Status; + BOOLEAN PropertyChanged; + CHAR8 *AsciiStringValue; + INT32 *IntegerValue; + UINT8 SetupMode; + BOOLEAN SecureBootEnabled; + BOOLEAN SecureBootEnableDisable; + + if ((JsonStructProtocol == NULL) || (ResultJson == NULL) || IS_EMPTY_STRING (InputJson) || IS_EMPTY_STRING (ConfigureLang)) { + return EFI_INVALID_PARAMETER; + } + + DEBUG ((REDFISH_DEBUG_TRACE, "%a provision for %s with: %s\n", __func__, ConfigureLang, (ProvisionMode ? L"Provision resource" : L"Update resource"))); + + *ResultJson = NULL; + PropertyChanged = FALSE; + AsciiStringValue = NULL; + SecureBootEnableDisable = RedfishReadSecureBootEnable (); + SecureBootEnabled = IsSecureBootEnabled (); + + SecureBoot = NULL; + Status = JsonStructProtocol->ToStructure ( + JsonStructProtocol, + NULL, + InputJson, + (EFI_REST_JSON_STRUCTURE_HEADER **)&SecureBoot + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: ToStructure failure: %r\n", __func__, Status)); + return Status; + } + + SecureBootCs = SecureBoot->SecureBoot; + + // + // ID + // + if (SecureBootCs->Id != NULL) { + SecureBootCs->Id = NULL; + } + + // + // Name + // + if (SecureBootCs->Name != NULL) { + SecureBootCs->Name = NULL; + } + + // + // Secure boot variables that we will handle here + // + // EFI_SETUP_MODE_NAME (gEfiGlobalVariableGuid) + // EFI_SECURE_BOOT_MODE_NAME (gEfiGlobalVariableGuid) + // EFI_SECURE_BOOT_ENABLE_NAME (gEfiSecureBootEnableDisableGuid) + // + + // + // Current Boot + // + if (PropertyChecker (SecureBootCs->SecureBootCurrentBoot, ProvisionMode)) { + AsciiStringValue = AllocateZeroPool (SECURE_BOOT_MODE_STR_LEN * sizeof (CHAR8)); + if (AsciiStringValue != NULL) { + AsciiSPrint (AsciiStringValue, SECURE_BOOT_MODE_STR_LEN, "%a", (SecureBootEnabled ? SECURE_BOOT_ENABLED : SECURE_BOOT_DISABLED)); + if (ProvisionMode || (AsciiStrCmp (SecureBootCs->SecureBootCurrentBoot, AsciiStringValue) != 0)) { + SecureBootCs->SecureBootCurrentBoot = AsciiStringValue; + PropertyChanged = TRUE; + } + } else { + DEBUG ((DEBUG_ERROR, "%a: out of resource\n", __func__)); + } + } + + // + // Secure boot enable + // + if (PropertyChecker (SecureBootCs->SecureBootEnable, ProvisionMode)) { + if (ProvisionMode || (*SecureBootCs->SecureBootEnable != SecureBootEnableDisable)) { + IntegerValue = AllocatePool (sizeof (*IntegerValue)); + if (IntegerValue != NULL) { + *IntegerValue = (SecureBootEnableDisable ? 0x01 : 0x00); + SecureBootCs->SecureBootEnable = IntegerValue; + PropertyChanged = TRUE; + } else { + DEBUG ((DEBUG_ERROR, "%a: out of resource\n", __func__)); + } + } + } + + // + // Secure boot mode + // + if (PropertyChecker (SecureBootCs->SecureBootMode, ProvisionMode)) { + Status = GetSetupMode (&SetupMode); + if (!EFI_ERROR (Status)) { + AsciiStringValue = AllocateZeroPool (SECURE_BOOT_MODE_STR_LEN *sizeof (CHAR8)); + if (AsciiStringValue != NULL) { + AsciiSPrint (AsciiStringValue, SECURE_BOOT_MODE_STR_LEN *sizeof (CHAR8), "%a", (SetupMode == USER_MODE ? SECURE_BOOT_USER_MODE : SECURE_BOOT_SETUP_MODE)); + if (ProvisionMode || (AsciiStrCmp (SecureBootCs->SecureBootMode, AsciiStringValue) != 0)) { + SecureBootCs->SecureBootMode = AsciiStringValue; + PropertyChanged = TRUE; + } + } + } else { + DEBUG ((DEBUG_ERROR, "%a: cannot read setup mode: %r\n", __func__, Status)); + } + } + + // + // Convert C structure back to JSON text. + // + Status = JsonStructProtocol->ToJson ( + JsonStructProtocol, + (EFI_REST_JSON_STRUCTURE_HEADER *)SecureBoot, + ResultJson + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: ToJson() failed: %r\n", __func__, Status)); + return Status; SecureBoot structure leak. It is released below. + } + + // + // Release resource. + // + JsonStructProtocol->DestoryStructure ( + JsonStructProtocol, + (EFI_REST_JSON_STRUCTURE_HEADER *)SecureBoot + ); + + return (PropertyChanged ? EFI_SUCCESS : EFI_NOT_FOUND); +} + +/** + Provision Redfish resource and upload data to Redfish service. This function + checks OEM data and platform addendum data before sending data to Redfish service. + + @param[in] Private Pointer to private data. + + @retval EFI_SUCCESS Provision Redfish resource successfully. + @retval Others Some error happened. + +**/ +EFI_STATUS +ProvisioningSecureBootResource ( + IN REDFISH_RESOURCE_COMMON_PRIVATE *Private + ) +{ + EFI_STATUS Status; + CHAR8 *Json; + CHAR8 *JsonWithAddendum; + REDFISH_RESPONSE Response; + + if (Private == NULL) { + return EFI_INVALID_PARAMETER; + } + + ZeroMem (&Response, sizeof (REDFISH_RESPONSE)); + Json = NULL; + + Status = ProvisioningSecureBootProperties ( + Private->JsonStructProtocol, + SecureBootEmptyJson, + NULL, + REDFISH_DUMMY_CONFIG_LANG, + TRUE, + &Json + ); + if (EFI_ERROR (Status)) { + if (Status == EFI_NOT_FOUND) { + DEBUG ((REDFISH_DEBUG_TRACE, "%a: provisioning existing resource for %s ignored. Nothing changed\n", __func__, REDFISH_DUMMY_CONFIG_LANG)); + Status = EFI_SUCCESS; + } else { + DEBUG ((DEBUG_ERROR, "%a: provisioning existing resource for %s failed: %r\n", __func__, REDFISH_DUMMY_CONFIG_LANG, Status)); + } + + goto ON_RELEASE; + } + + // + // Check and see if platform has OEM data or not + // + Status = RedfishGetOemData ( + Private->Uri, + RESOURCE_SCHEMA, + RESOURCE_SCHEMA_VERSION, + Json, + &JsonWithAddendum + ); + if (!EFI_ERROR (Status) && (JsonWithAddendum != NULL)) { + FreePool (Json); + Json = JsonWithAddendum; + JsonWithAddendum = NULL; + } + + // + // Check and see if platform has addendum data or not + // + Status = RedfishGetAddendumData ( + Private->Uri, + RESOURCE_SCHEMA, + RESOURCE_SCHEMA_VERSION, + Json, + &JsonWithAddendum + ); + if (!EFI_ERROR (Status) && (JsonWithAddendum != NULL)) { + FreePool (Json); + Json = JsonWithAddendum; + JsonWithAddendum = NULL; + } + + DEBUG ((REDFISH_DEBUG_TRACE, "%a: provisioning existing resource for %s\n", __func__, REDFISH_DUMMY_CONFIG_LANG)); + + // + // PATCH back to instance + // + Status = RedfishHttpPatchResource (Private->RedfishService, Private->Uri, Json, &Response); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: patch resource for %s failed: %r\n", __func__, REDFISH_DUMMY_CONFIG_LANG, Status)); + } + +ON_RELEASE: + + if (Json != NULL) { + FreePool (Json); + } + + RedfishHttpFreeResponse (&Response); + + return Status; +} + +/** + Provisioning redfish resource to Redfish service. + + @param[in] Private Pointer to private data. + @param[in] ResourceExist TRUE if resource exists, PUT method will be used. + FALSE if resource does not exist POST method is used. Actually ProvisioningSecureBootResource uses PATCH only. + + @retval EFI_SUCCESS Provision resource successfully. + @retval Others Some error happened. + +**/ +EFI_STATUS +RedfishProvisioningResourceCommon ( + IN REDFISH_RESOURCE_COMMON_PRIVATE *Private, + IN BOOLEAN ResourceExist + ) +{ + if (Private == NULL) { + return EFI_INVALID_PARAMETER; + } + + return ProvisioningSecureBootResource (Private); +} + +/** + Check resource from given Json data. + + @param[in] This Pointer to REDFISH_RESOURCE_COMMON_PRIVATE instance. + @param[in] Json The JSON data to check. + @param[in] HeaderEtag The Etag string returned in HTTP header. + + @retval EFI_SUCCESS Check resource successfully. + @retval Others Some error happened. + +**/ +EFI_STATUS +RedfishCheckResourceCommon ( + IN REDFISH_RESOURCE_COMMON_PRIVATE *Private, + IN CHAR8 *Json, + IN CHAR8 *HeaderEtag OPTIONAL + ) +{ + UINTN Index; + EFI_STATUS Status; + UINTN Count; + EFI_STRING Property; + + if ((Private == NULL) || IS_EMPTY_STRING (Json)) { + return EFI_INVALID_PARAMETER; + } + + // + // Check ETAG to see if we need to check it + // + if (CheckEtag (Private->Uri, HeaderEtag, NULL)) { + // + // No change + // + DEBUG ((REDFISH_DEBUG_TRACE, "%a: ETAG: %s has no change, ignore check action\n", __func__, Private->Uri)); + return EFI_SUCCESS; + } + + Count = sizeof (mSecureBootSupportedAttributes) / sizeof (mSecureBootSupportedAttributes[0]); + if (Count == 0) { + return EFI_UNSUPPORTED; + } + + Status = EFI_SUCCESS; + for (Index = 0; Index < Count; Index++) { + Property = mSecureBootSupportedAttributes[Index]; + if (Property == NULL) { + continue; + } + + DEBUG ((REDFISH_DEBUG_TRACE, "%a: [%d] check attribute for: %s\n", __func__, Index, Property)); + if (!MatchPropertyWithJsonContext (Property, Json)) { + DEBUG ((REDFISH_DEBUG_TRACE, "%a: property is missing: %s\n", __func__, Property)); + Status = EFI_NOT_FOUND; Oh! I have a question about this condition. Can we just break the loop here? I'm asking mostly in context of Bios feature driver where the same loop exists. While in this driver Count not greater than 3, but for Bios driver there could be a hundreds of iterations and AFAIR during provisioning we have EFI_NOT_FOUND right after the first iteration. MatchPropertyWithJsonContext() seems to be a heavy call. The only useful thing this loop does in that case is printing missed property if enabled. + } + } + + return Status; +} + +/** + Update resource to Redfish service. + + @param[in] Private Pointer to REDFISH_RESOURCE_COMMON_PRIVATE instance. + @param[in] Json The JSON data to be updated. + + @retval EFI_SUCCESS Update resource successfully. + @retval Others Some error happened. + +**/ +EFI_STATUS +RedfishUpdateResourceCommon ( + IN REDFISH_RESOURCE_COMMON_PRIVATE *Private, + IN CHAR8 *InputJson + ) +{ + EFI_STATUS Status; + CHAR8 *Json; + CHAR8 *JsonWithAddendum; + REDFISH_RESPONSE Response; + + if ((Private == NULL) || IS_EMPTY_STRING (InputJson)) { + return EFI_INVALID_PARAMETER; + } + + ZeroMem (&Response, sizeof (REDFISH_RESPONSE)); + Json = NULL; + + Status = ProvisioningSecureBootProperties ( + Private->JsonStructProtocol, + SecureBootEmptyJson, + NULL, + REDFISH_DUMMY_CONFIG_LANG, + TRUE, + &Json + ); + if (EFI_ERROR (Status)) { + if (Status == EFI_NOT_FOUND) { + DEBUG ((REDFISH_DEBUG_TRACE, "%a: update resource for %s ignored. Nothing changed\n", __func__, REDFISH_DUMMY_CONFIG_LANG)); + Status = EFI_SUCCESS; + } else { + DEBUG ((DEBUG_ERROR, "%a: update resource for %s failed: %r\n", __func__, REDFISH_DUMMY_CONFIG_LANG, Status)); + } + + goto ON_RELEASE; + } + + // + // Check and see if platform has OEM data or not + // + Status = RedfishGetOemData ( + Private->Uri, + RESOURCE_SCHEMA, + RESOURCE_SCHEMA_VERSION, + Json, + &JsonWithAddendum + ); + if (!EFI_ERROR (Status) && (JsonWithAddendum != NULL)) { + FreePool (Json); + Json = JsonWithAddendum; + JsonWithAddendum = NULL; + } + + // + // Check and see if platform has addendum data or not + // + Status = RedfishGetAddendumData ( + Private->Uri, + RESOURCE_SCHEMA, + RESOURCE_SCHEMA_VERSION, + Json, + &JsonWithAddendum + ); + if (!EFI_ERROR (Status) && (JsonWithAddendum != NULL)) { + FreePool (Json); + Json = JsonWithAddendum; + JsonWithAddendum = NULL; + } + + DEBUG ((REDFISH_DEBUG_TRACE, "%a: update resource for %s\n", __func__, REDFISH_DUMMY_CONFIG_LANG)); + + // + // PATCH back to instance + // + Status = RedfishHttpPatchResource (Private->RedfishService, Private->Uri, Json, &Response); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: patch resource for %s failed: %r\n", __func__, REDFISH_DUMMY_CONFIG_LANG, Status)); + } + +ON_RELEASE: + + if (Json != NULL) { + FreePool (Json); + } + + RedfishHttpFreeResponse (&Response); + + return Status; +} + +/** + Identify resource in given Json data. + + @param[in] Private Pointer to REDFISH_RESOURCE_COMMON_PRIVATE instance. + @param[in] Json The JSON to be identified. + + @retval EFI_SUCCESS Identify resource successfully. + @retval Others Some error happened. + +**/ +EFI_STATUS +RedfishIdentifyResourceCommon ( + IN REDFISH_RESOURCE_COMMON_PRIVATE *Private, + IN CHAR8 *Json + ) +{ + BOOLEAN Supported; + + Supported = RedfishIdentifyResource (Private->Uri, Private->Json); + if (Supported) { + // + // Keep URI and ConfigLang mapping + // + RedfishSetRedfishUri (REDFISH_DUMMY_CONFIG_LANG, Private->Uri); + } + + return (Supported ? EFI_SUCCESS : EFI_UNSUPPORTED); +} + +/** + Handle Redfish resource in Uri. + + @param[in] Private Pointer to REDFISH_RESOURCE_COMMON_PRIVATE instance. + @param[in] Uri URI to Redfish resource that we like to process. + + @retval EFI_SUCCESS Handle resource successfully. + @retval Others Some error happened. + +**/ +EFI_STATUS +HandleResource ( + IN REDFISH_RESOURCE_COMMON_PRIVATE *Private, + IN EFI_STRING Uri + ) +{ + EFI_STATUS Status; + REDFISH_SCHEMA_INFO SchemaInfo; + EFI_STRING ConfigLang; + + if ((Private == NULL) || IS_EMPTY_STRING (Uri)) { + return EFI_INVALID_PARAMETER; + } + + // + // Resource match + // + + DEBUG ((REDFISH_DEBUG_TRACE, "%a: process resource for: %s\n", __func__, Uri)); + + Status = GetRedfishSchemaInfo (Private->RedfishService, Private->JsonStructProtocol, Uri, NULL, &SchemaInfo); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: failed to get schema information from: %s %r\n", __func__, Uri, Status)); + return Status; + } + + // + // Check and see if this is target resource that we want to handle. + // Some resource is handled by other provider so we have to make sure this first. + // + DEBUG ((REDFISH_DEBUG_TRACE, "%a: Identify for %s\n", __func__, Uri)); + ConfigLang = RedfishGetConfigLanguage (Uri); + if (ConfigLang == NULL) { + Status = EdkIIRedfishResourceConfigIdentify (&SchemaInfo, Uri, NULL, Private->InformationExchange); + if (EFI_ERROR (Status)) { + if (Status == EFI_UNSUPPORTED) { + DEBUG ((REDFISH_DEBUG_TRACE, "%a: \"%s\" is not handled by us\n", __func__, Uri)); + return EFI_SUCCESS; + } else if (Status == EFI_NOT_FOUND) { + DEBUG ((REDFISH_DEBUG_TRACE, "%a: \"%s\" has nothing to handle\n", __func__, Uri)); + return EFI_SUCCESS; + } + + DEBUG ((DEBUG_ERROR, "%a: fail to identify resource: \"%s\": %r\n", __func__, Uri, Status)); + return Status; + } + } else { + DEBUG ((REDFISH_DEBUG_TRACE, "%a: history record found: %s\n", __func__, ConfigLang)); + FreePool (ConfigLang); + } + + // + // Check and see if target property exist or not even when collection member exists. + // If not, we sill do provision. %s/sill/still + // + DEBUG ((REDFISH_DEBUG_TRACE, "%a Check for %s\n", __func__, Uri)); + Status = EdkIIRedfishResourceConfigCheck (&SchemaInfo, Uri, NULL); + if (EFI_ERROR (Status)) { + if (Status == EFI_UNSUPPORTED) { + DEBUG ((REDFISH_DEBUG_TRACE, "%a: \"%s\" is not handled by us\n", __func__, Uri)); + return EFI_SUCCESS; + } + + // + // The target property does not exist, do the provision to create property. + // + DEBUG ((REDFISH_DEBUG_TRACE, "%a provision for %s\n", __func__, Uri)); + Status = EdkIIRedfishResourceConfigProvisioning (&SchemaInfo, Uri, NULL, Private->InformationExchange, FALSE); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: failed to provision with GET mode: %r\n", __func__, Status)); + } + + DEBUG ((REDFISH_DEBUG_TRACE, "%a: process resource for: %s finished\n", __func__, Uri)); + + return Status; + } + + // + // Consume first. + // + DEBUG ((REDFISH_DEBUG_TRACE, "%a consume for %s\n", __func__, Uri)); + Status = EdkIIRedfishResourceConfigConsume (&SchemaInfo, Uri, NULL); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: failed to consume resource for: %s: %r\n", __func__, Uri, Status)); + } + + // + // Patch. + // + DEBUG ((REDFISH_DEBUG_TRACE, "%a update for %s\n", __func__, Uri)); + Status = EdkIIRedfishResourceConfigUpdate (&SchemaInfo, Uri, NULL); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: failed to update resource for: %s: %r\n", __func__, Uri, Status)); + } + + DEBUG ((REDFISH_DEBUG_TRACE, "%a: process resource for: %s finished\n", __func__, Uri)); + + return Status; +} diff --git a/RedfishClientPkg/Features/SecureBoot/v1_1_0/Dxe/SecureBootDxe.c b/RedfishClientPkg/Features/SecureBoot/v1_1_0/Dxe/SecureBootDxe.c new file mode 100644 index 000000000..a0f4f3d14 --- /dev/null +++ b/RedfishClientPkg/Features/SecureBoot/v1_1_0/Dxe/SecureBootDxe.c @@ -0,0 +1,808 @@ +/** @file + Redfish feature driver implementation - SecureBoot + + (C) Copyright 2020-2022 Hewlett Packard Enterprise Development LP
+ Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "../Common/SecureBootCommon.h" + +extern REDFISH_RESOURCE_COMMON_PRIVATE *mRedfishResourcePrivate; +extern EFI_HANDLE mRedfishResourceConfigProtocolHandle; + +EFI_STATUS +HandleResource ( + IN REDFISH_RESOURCE_COMMON_PRIVATE *Private, + IN EFI_STRING Uri + ); + +/** + Provisioning redfish resource by given URI. + + @param[in] This Pointer to EFI_HP_REDFISH_HII_PROTOCOL instance. + @param[in] Uri Target URI to create resource. + @param[in] PostMode TRUE if the resource does not exist, post method is used. + FALSE if the resource exist but property is missing, put method is used. + + @retval EFI_SUCCESS Value is returned successfully. + @retval Others Some error happened. + +**/ +EFI_STATUS +EFIAPI +RedfishResourceProvisioningResource ( + IN EDKII_REDFISH_RESOURCE_CONFIG_PROTOCOL *This, + IN EFI_STRING Uri, + IN BOOLEAN PostMode + ) +{ + REDFISH_RESOURCE_COMMON_PRIVATE *Private; + EFI_STATUS Status; + REDFISH_RESPONSE Response; + + if ((This == NULL) || IS_EMPTY_STRING (Uri)) { + return EFI_INVALID_PARAMETER; + } + + DEBUG ((REDFISH_DEBUG_TRACE, "%a: provisioning in %s mode\n", __func__, (PostMode ? L"POST" : L"PATCH"))); This message conflicts with PostMode parameter description. + + ZeroMem (&Response, sizeof (REDFISH_RESPONSE)); + Private = REDFISH_RESOURCE_COMMON_PRIVATE_DATA_FROM_RESOURCE_PROTOCOL (This); + + if (Private->RedfishService == NULL) { + return EFI_NOT_READY; + } + + Status = RedfishHttpGetResource (Private->RedfishService, Uri, NULL, &Response, TRUE); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: get resource from: %s failed\n", __func__, Uri)); + return Status; + } + + Private->Uri = Uri; + Private->Payload = Response.Payload; + ASSERT (Private->Payload != NULL); + + Status = RedfishProvisioningResourceCommon (Private, !PostMode); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: failed to provision resource to: %s: %r\n", __func__, Uri, Status)); + } else { + // + // Get latest ETag on URI and keep it in variable. + // + SetEtagFromUri (Private->RedfishService, Private->Uri, TRUE); + } + + // + // Release resource + // + RedfishHttpFreeResponse (&Response); + Private->Payload = NULL; + + return Status; +} + +/** + Consume resource from given URI. + + @param[in] This Pointer to EFI_HP_REDFISH_HII_PROTOCOL instance. + @param[in] Uri The target URI to consume. + + @retval EFI_SUCCESS Value is returned successfully. + @retval Others Some error happened. + +**/ +EFI_STATUS +EFIAPI +RedfishResourceConsumeResource ( + IN EDKII_REDFISH_RESOURCE_CONFIG_PROTOCOL *This, + IN EFI_STRING Uri + ) +{ + REDFISH_RESOURCE_COMMON_PRIVATE *Private; + EFI_STATUS Status; + REDFISH_RESPONSE Response; + EFI_STRING PendingSettingUri; + REDFISH_RESPONSE PendingSettingResponse; + REDFISH_RESPONSE *ExpectedResponse; + CHAR8 *Etag; + + if ((This == NULL) || IS_EMPTY_STRING (Uri)) { + return EFI_INVALID_PARAMETER; + } + + ZeroMem (&Response, sizeof (REDFISH_RESPONSE)); + ZeroMem (&PendingSettingResponse, sizeof (REDFISH_RESPONSE)); + Private = REDFISH_RESOURCE_COMMON_PRIVATE_DATA_FROM_RESOURCE_PROTOCOL (This); + + if (Private->RedfishService == NULL) { + return EFI_NOT_READY; + } + + Status = RedfishHttpGetResource (Private->RedfishService, Uri, NULL, &Response, TRUE); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: get resource from: %s failed\n", __func__, Uri)); + return Status; + } + + // + // Check and see if "@Redfish.Settings" exist or not. + // + PendingSettingUri = NULL; + Status = GetPendingSettings ( + Private->RedfishService, + Response.Payload, + &PendingSettingResponse, + &PendingSettingUri + ); + if (!EFI_ERROR (Status)) { + DEBUG ((REDFISH_DEBUG_TRACE, "%a: @Redfish.Settings found: %s\n", __func__, PendingSettingUri)); + Private->Uri = PendingSettingUri; + ExpectedResponse = &PendingSettingResponse; + } else { + Private->Uri = Uri; + ExpectedResponse = &Response; + } + + Private->Payload = ExpectedResponse->Payload; + ASSERT (Private->Payload != NULL); + + Private->Json = JsonDumpString (RedfishJsonInPayload (Private->Payload), EDKII_JSON_COMPACT); + ASSERT (Private->Json != NULL); + + // + // Searching for etag in HTTP response header + // + Etag = NULL; + Status = GetHttpResponseEtag (ExpectedResponse, &Etag); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: failed to get ETag from HTTP header\n", __func__)); + } + + Status = RedfishConsumeResourceCommon (Private, Private->Json, Etag); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: failed to consume resource from: %s: %r\n", __func__, Private->Uri, Status)); + } + + // + // Release resource + // + RedfishHttpFreeResponse (&Response); + RedfishHttpFreeResponse (&PendingSettingResponse); + Private->Payload = NULL; + + if (Private->Json != NULL) { + FreePool (Private->Json); + Private->Json = NULL; + } + + if (Etag != NULL) { + FreePool (Etag); + } + + if (PendingSettingUri != NULL) { + FreePool (PendingSettingUri); + } + + return Status; +} + +/** + Get information about this protocol. + + @param[in] This Pointer to EFI_HP_REDFISH_HII_PROTOCOL instance. + @param[out] Schema Supported schema. + @param[out] Major Supported major number. + @param[out] Minor Supported minor number. + @param[out] Errata Supported errata number. + + @retval EFI_SUCCESS Value is returned successfully. + @retval Others Some error happened. + +**/ +EFI_STATUS +EFIAPI +RedfishResourceGetInfo ( + IN EDKII_REDFISH_RESOURCE_CONFIG_PROTOCOL *This, + OUT REDFISH_SCHEMA_INFO *Info + ) +{ + if ((This == NULL) || (Info == NULL)) { + return EFI_INVALID_PARAMETER; + } + + AsciiStrCpyS (Info->Schema, REDFISH_SCHEMA_STRING_SIZE, RESOURCE_SCHEMA); + AsciiStrCpyS (Info->Major, REDFISH_SCHEMA_VERSION_SIZE, RESOURCE_SCHEMA_MAJOR); + AsciiStrCpyS (Info->Minor, REDFISH_SCHEMA_VERSION_SIZE, RESOURCE_SCHEMA_MINOR); + AsciiStrCpyS (Info->Errata, REDFISH_SCHEMA_VERSION_SIZE, RESOURCE_SCHEMA_ERRATA); + + return EFI_SUCCESS; +} + +/** + Update resource to given URI. + + @param[in] This Pointer to EFI_HP_REDFISH_HII_PROTOCOL instance. + @param[in] Uri The target URI to consume. + + @retval EFI_SUCCESS Value is returned successfully. + @retval Others Some error happened. + +**/ +EFI_STATUS +EFIAPI +RedfishResourceUpdate ( + IN EDKII_REDFISH_RESOURCE_CONFIG_PROTOCOL *This, + IN EFI_STRING Uri + ) +{ + REDFISH_RESOURCE_COMMON_PRIVATE *Private; + EFI_STATUS Status; + REDFISH_RESPONSE Response; + + if ((This == NULL) || IS_EMPTY_STRING (Uri)) { + return EFI_INVALID_PARAMETER; + } + + ZeroMem (&Response, sizeof (REDFISH_RESPONSE)); + Private = REDFISH_RESOURCE_COMMON_PRIVATE_DATA_FROM_RESOURCE_PROTOCOL (This); + + if (Private->RedfishService == NULL) { + return EFI_NOT_READY; + } + + Status = RedfishHttpGetResource (Private->RedfishService, Uri, NULL, &Response, TRUE); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: get resource from: %s failed\n", __func__, Uri)); + return Status; + } + + Private->Uri = Uri; + Private->Payload = Response.Payload; + ASSERT (Private->Payload != NULL); + + Private->Json = JsonDumpString (RedfishJsonInPayload (Private->Payload), EDKII_JSON_COMPACT); + ASSERT (Private->Json != NULL); + + Status = RedfishUpdateResourceCommon (Private, Private->Json); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: failed to update resource to: %s: %r\n", __func__, Uri, Status)); + } else { + // + // Get latest ETag on URI and keep it in variable. + // + SetEtagFromUri (Private->RedfishService, Private->Uri, TRUE); + } + + // + // Release resource + // + RedfishHttpFreeResponse (&Response); + Private->Payload = NULL; + + if (Private->Json != NULL) { + FreePool (Private->Json); + Private->Json = NULL; + } + + return Status; +} + +/** + Check resource on given URI. + + @param[in] This Pointer to EFI_HP_REDFISH_HII_PROTOCOL instance. + @param[in] Uri The target URI to consume. + + @retval EFI_SUCCESS Value is returned successfully. + @retval Others Some error happened. + +**/ +EFI_STATUS +EFIAPI +RedfishResourceCheck ( + IN EDKII_REDFISH_RESOURCE_CONFIG_PROTOCOL *This, + IN EFI_STRING Uri + ) +{ + REDFISH_RESOURCE_COMMON_PRIVATE *Private; + EFI_STATUS Status; + REDFISH_RESPONSE Response; + CHAR8 *Etag; + + if ((This == NULL) || IS_EMPTY_STRING (Uri)) { + return EFI_INVALID_PARAMETER; + } + + ZeroMem (&Response, sizeof (REDFISH_RESPONSE)); + Private = REDFISH_RESOURCE_COMMON_PRIVATE_DATA_FROM_RESOURCE_PROTOCOL (This); + + if (Private->RedfishService == NULL) { + return EFI_NOT_READY; + } + + Status = RedfishHttpGetResource (Private->RedfishService, Uri, NULL, &Response, TRUE); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: get resource from: %s failed\n", __func__, Uri)); + return Status; + } + + Private->Uri = Uri; + Private->Payload = Response.Payload; + ASSERT (Private->Payload != NULL); + + Private->Json = JsonDumpString (RedfishJsonInPayload (Private->Payload), EDKII_JSON_COMPACT); + ASSERT (Private->Json != NULL); + + // + // Find etag in HTTP response header + // + Etag = NULL; + Status = GetHttpResponseEtag (&Response, &Etag); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: failed to get ETag from HTTP header\n", __func__)); + } + + Status = RedfishCheckResourceCommon (Private, Private->Json, Etag); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: failed to check resource from: %s: %r\n", __func__, Uri, Status)); + } + + // + // Release resource + // + if (Etag != NULL) { + FreePool (Etag); + } + + RedfishHttpFreeResponse (&Response); + Private->Payload = NULL; + + if (Private->Json != NULL) { + FreePool (Private->Json); + Private->Json = NULL; + } + + return Status; +} + +/** + Identify resource on given URI. + + @param[in] This Pointer to EDKII_REDFISH_RESOURCE_CONFIG_PROTOCOL instance. + @param[in] Uri The target URI to consume. + + @retval EFI_SUCCESS This is target resource which we want to handle. + @retval EFI_UNSUPPORTED This is not the target resource. + @retval Others Some error happened. + +**/ +EFI_STATUS +EFIAPI +RedfishResourceIdentify ( + IN EDKII_REDFISH_RESOURCE_CONFIG_PROTOCOL *This, + IN EFI_STRING Uri + ) +{ + REDFISH_RESOURCE_COMMON_PRIVATE *Private; + EFI_STATUS Status; + REDFISH_RESPONSE Response; + + if ((This == NULL) || IS_EMPTY_STRING (Uri)) { + return EFI_INVALID_PARAMETER; + } + + ZeroMem (&Response, sizeof (REDFISH_RESPONSE)); + Private = REDFISH_RESOURCE_COMMON_PRIVATE_DATA_FROM_RESOURCE_PROTOCOL (This); + + if (Private->RedfishService == NULL) { + return EFI_NOT_READY; + } + + Status = RedfishHttpGetResource (Private->RedfishService, Uri, NULL, &Response, TRUE); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: get resource from: %s failed\n", __func__, Uri)); + return Status; + } + + Private->Uri = Uri; + Private->Payload = Response.Payload; + ASSERT (Private->Payload != NULL); + + Private->Json = JsonDumpString (RedfishJsonInPayload (Private->Payload), EDKII_JSON_COMPACT); + ASSERT (Private->Json != NULL); + + Status = RedfishIdentifyResourceCommon (Private, Private->Json); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: identify %s failed: %r\n", __func__, Uri, Status)); + } + + // + // Release resource + // + RedfishHttpFreeResponse (&Response); + Private->Payload = NULL; + + if (Private->Json != NULL) { + FreePool (Private->Json); + Private->Json = NULL; + } + + return Status; +} + +EDKII_REDFISH_RESOURCE_CONFIG_PROTOCOL mRedfishResourceConfig = { + RedfishResourceProvisioningResource, + RedfishResourceConsumeResource, + RedfishResourceUpdate, + RedfishResourceCheck, + RedfishResourceIdentify, + RedfishResourceGetInfo +}; + +/** + Initialize a Redfish configure handler. + + This function will be called by the Redfish config driver to initialize each Redfish configure + handler. + + @param[in] This Pointer to EDKII_REDFISH_CONFIG_HANDLER_PROTOCOL instance. + @param[in] RedfishConfigServiceInfo Redfish service information. + + @retval EFI_SUCCESS The handler has been initialized successfully. + @retval EFI_DEVICE_ERROR Failed to create or configure the REST EX protocol instance. + @retval EFI_ALREADY_STARTED This handler has already been initialized. + @retval Other Error happens during the initialization. + +**/ +EFI_STATUS +EFIAPI +RedfishResourceInit ( + IN EDKII_REDFISH_CONFIG_HANDLER_PROTOCOL *This, + IN REDFISH_CONFIG_SERVICE_INFORMATION *RedfishConfigServiceInfo + ) +{ + REDFISH_RESOURCE_COMMON_PRIVATE *Private; + + Private = REDFISH_RESOURCE_COMMON_PRIVATE_DATA_FROM_CONFIG_PROTOCOL (This); + + Private->RedfishService = RedfishCreateService (RedfishConfigServiceInfo); + if (Private->RedfishService == NULL) { + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +/** + Stop a Redfish configure handler. + + @param[in] This Pointer to EDKII_REDFISH_CONFIG_HANDLER_PROTOCOL instance. + + @retval EFI_SUCCESS This handler has been stoped successfully. + @retval Others Some error happened. + +**/ +EFI_STATUS +EFIAPI +RedfishResourceStop ( + IN EDKII_REDFISH_CONFIG_HANDLER_PROTOCOL *This + ) +{ + REDFISH_RESOURCE_COMMON_PRIVATE *Private; + + Private = REDFISH_RESOURCE_COMMON_PRIVATE_DATA_FROM_CONFIG_PROTOCOL (This); + + if (Private->Event != NULL) { + gBS->CloseEvent (Private->Event); + Private->Event = NULL; + } + + if (Private->RedfishService != NULL) { + RedfishCleanupService (Private->RedfishService); + Private->RedfishService = NULL; + } + + return EFI_SUCCESS; +} + +EDKII_REDFISH_CONFIG_HANDLER_PROTOCOL mRedfishConfigHandler = { + RedfishResourceInit, + RedfishResourceStop +}; + +/** + Callback function when gEfiRestJsonStructureProtocolGuid is installed. + + @param[in] Event Event whose notification function is being invoked. + @param[in] Context Pointer to the notification function's context. +**/ +VOID +EFIAPI +EfiRestJsonStructureProtocolIsReady ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + + if (mRedfishResourcePrivate == NULL) { + return; + } + + if (mRedfishResourcePrivate->JsonStructProtocol != NULL) { + return; + } + + Status = gBS->LocateProtocol ( + &gEfiRestJsonStructureProtocolGuid, + NULL, + (VOID **)&mRedfishResourcePrivate->JsonStructProtocol + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: failed to locate gEfiRestJsonStructureProtocolGuid: %r\n", __func__, Status)); + } + + gBS->CloseEvent (Event); +} + +/** + Unloads an image. + + @param ImageHandle Handle that identifies the image to be unloaded. + + @retval EFI_SUCCESS The image has been unloaded. + @retval EFI_INVALID_PARAMETER ImageHandle is not a valid image handle. + +**/ +EFI_STATUS +EFIAPI +RedfishResourceUnload ( + IN EFI_HANDLE ImageHandle + ) +{ + EFI_STATUS Status; + EDKII_REDFISH_CONFIG_HANDLER_PROTOCOL *ConfigHandler; + + if (mRedfishResourcePrivate == NULL) { + return EFI_NOT_READY; + } + + ConfigHandler = NULL; + + // + // Firstly, find ConfigHandler Protocol interface in this ImageHandle. + // + Status = gBS->OpenProtocol ( + ImageHandle, + &gEdkIIRedfishConfigHandlerProtocolGuid, + (VOID **)&ConfigHandler, + NULL, + NULL, + EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL + ); + if (EFI_ERROR (Status) || (ConfigHandler == NULL)) { + return Status; + } + + ConfigHandler->Stop (ConfigHandler); + + // + // Last, uninstall ConfigHandler Protocol and resource protocol. + // + Status = gBS->UninstallMultipleProtocolInterfaces ( + ImageHandle, + &gEdkIIRedfishConfigHandlerProtocolGuid, + ConfigHandler, + &gEdkIIRedfishResourceConfigProtocolGuid, + &mRedfishResourcePrivate->RedfishResourceConfig, + NULL + ); + + FreePool (mRedfishResourcePrivate); + mRedfishResourcePrivate = NULL; + + return Status; +} + +/** + The callback function provided by Redfish Feature driver. + + @param[in] This Pointer to EDKII_REDFISH_FEATURE_PROTOCOL instance. + @param[in] FeatureAction The action Redfish feature driver should take. + @param[in] Uri The collection URI. + @param[in] Context The context of Redfish feature driver. + @param[in,out] InformationExchange The pointer to RESOURCE_INFORMATION_EXCHANGE + + @retval EFI_SUCCESS Redfish feature driver callback is executed successfully. + @retval Others Some errors happened. + + @retval EFI_SUCCESS Redfish feature driver callback is executed successfully. + @retval Others Some errors happened. + +**/ +EFI_STATUS +EFIAPI +RedfishExternalResourceResourceFeatureCallback ( + IN EDKII_REDFISH_FEATURE_PROTOCOL *This, + IN FEATURE_CALLBACK_ACTION FeatureAction, + IN VOID *Context, + IN OUT RESOURCE_INFORMATION_EXCHANGE *InformationExchange + ) +{ + EFI_STATUS Status; + REDFISH_SERVICE RedfishService; + REDFISH_RESOURCE_COMMON_PRIVATE *Private; + EFI_STRING ResourceUri; + EFI_STRING SecureBootUri; + + if (FeatureAction != CallbackActionStartOperation) { + return EFI_UNSUPPORTED; + } + + Private = (REDFISH_RESOURCE_COMMON_PRIVATE *)Context; + + RedfishService = Private->RedfishService; + if (RedfishService == NULL) { + DEBUG ((DEBUG_ERROR, "%a: no Redfish service configured\n", __func__)); + return EFI_NOT_READY; + } + + // + // Save in private structure. + // + Private->InformationExchange = InformationExchange; + + // + // Find Redfish version on Redfish ser + // + Private->RedfishVersion = RedfishGetVersion (RedfishService); + + // + // Create the full URI from Redfish service root. + // + ResourceUri = (EFI_STRING)AllocateZeroPool (MAX_URI_LENGTH * sizeof (CHAR16)); + if (ResourceUri == NULL) { + DEBUG ((DEBUG_ERROR, "%a: Fail to allocate memory for full URI.\n", __func__)); + return EFI_OUT_OF_RESOURCES; + } + + StrCatS (ResourceUri, MAX_URI_LENGTH, Private->RedfishVersion); + StrCatS (ResourceUri, MAX_URI_LENGTH, InformationExchange->SendInformation.FullUri); + + // + // Initialize collection path + // + SecureBootUri = RedfishGetUri (ResourceUri); + if (SecureBootUri == NULL) { + ASSERT (FALSE); + FreePool (ResourceUri); + return EFI_OUT_OF_RESOURCES; + } + + Status = HandleResource (Private, SecureBootUri); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: process external resource: %s failed: %r\n", __func__, SecureBootUri, Status)); + } + + FreePool (SecureBootUri); + FreePool (ResourceUri); + return Status; +} + +/** + Callback function when gEdkIIRedfishFeatureProtocolGuid is installed. + + @param[in] Event Event whose notification function is being invoked. + @param[in] Context Pointer to the notification function's context. +**/ +VOID +EFIAPI +EdkIIRedfishFeatureProtocolIsReady ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + EDKII_REDFISH_FEATURE_PROTOCOL *FeatureProtocol; + + if (mRedfishResourcePrivate == NULL) { + return; + } + + if (mRedfishResourcePrivate->FeatureProtocol != NULL) { + return; + } + + Status = gBS->LocateProtocol ( + &gEdkIIRedfishFeatureProtocolGuid, + NULL, + (VOID **)&FeatureProtocol + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: failed to locate gEdkIIRedfishFeatureProtocolGuid: %r\n", __func__, Status)); + gBS->CloseEvent (Event); + return; + } + + Status = FeatureProtocol->Register ( + FeatureProtocol, + REDFISH_MANAGED_URI, + RedfishExternalResourceResourceFeatureCallback, + (VOID *)mRedfishResourcePrivate + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: failed to register %s: %r\n", __func__, REDFISH_MANAGED_URI, Status)); + } + + mRedfishResourcePrivate->FeatureProtocol = FeatureProtocol; + + gBS->CloseEvent (Event); +} + +/** + This is the declaration of an EFI image entry point. This entry point is + the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including + both device drivers and bus drivers. It initialize the global variables and + publish the driver binding protocol. + + @param[in] ImageHandle The firmware allocated handle for the UEFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_ACCESS_DENIED EFI_ISCSI_INITIATOR_NAME_PROTOCOL was installed unexpectedly. + @retval Others Other errors as indicated. +**/ +EFI_STATUS +EFIAPI +RedfishResourceEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + VOID *Registration; + + if (mRedfishResourcePrivate != NULL) { + return EFI_ALREADY_STARTED; + } + + mRedfishResourceConfigProtocolHandle = ImageHandle; + + mRedfishResourcePrivate = AllocateZeroPool (sizeof (REDFISH_RESOURCE_COMMON_PRIVATE)); + CopyMem (&mRedfishResourcePrivate->ConfigHandler, &mRedfishConfigHandler, sizeof (EDKII_REDFISH_CONFIG_HANDLER_PROTOCOL)); + CopyMem (&mRedfishResourcePrivate->RedfishResourceConfig, &mRedfishResourceConfig, sizeof (EDKII_REDFISH_RESOURCE_CONFIG_PROTOCOL)); + + // + // Publish config handler protocol and resource protocol. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &ImageHandle, + &gEdkIIRedfishConfigHandlerProtocolGuid, + &mRedfishResourcePrivate->ConfigHandler, + &gEdkIIRedfishResourceConfigProtocolGuid, + &mRedfishResourcePrivate->RedfishResourceConfig, + NULL + ); + + EfiCreateProtocolNotifyEvent ( + &gEfiRestJsonStructureProtocolGuid, + TPL_CALLBACK, + EfiRestJsonStructureProtocolIsReady, + NULL, + &Registration + ); + + EfiCreateProtocolNotifyEvent ( + &gEdkIIRedfishFeatureProtocolGuid, + TPL_CALLBACK, + EdkIIRedfishFeatureProtocolIsReady, + (VOID *)mRedfishResourcePrivate, + &Registration + ); + + return Status; +} diff --git a/RedfishClientPkg/RedfishClient.fdf.inc b/RedfishClientPkg/RedfishClient.fdf.inc index 59b8acba1..154f641b2 100644 --- a/RedfishClientPkg/RedfishClient.fdf.inc +++ b/RedfishClientPkg/RedfishClient.fdf.inc @@ -25,6 +25,7 @@ INF RedfishClientPkg/HiiToRedfishBiosDxe/HiiToRedfishBiosDxe.inf INF RedfishClientPkg/Features/BootOptionCollection/BootOptionCollectionDxe.inf INF RedfishClientPkg/Features/BootOption/v1_0_4/Dxe/BootOptionDxe.inf + INF RedfishClientPkg/Features/SecureBoot/v1_1_0/Dxe/SecureBootDxe.inf !include RedfishClientPkg/RedfishJsonStructureDxe.fdf.inc # -- 2.34.1 Regards, Mike Regards, Mike. -=-=-=-=-=-=-=-=-=-=-=- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#117294): https://edk2.groups.io/g/devel/message/117294 Mute This Topic: https://groups.io/mt/104927848/7686176 Group Owner: devel+owner@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io] -=-=-=-=-=-=-=-=-=-=-=-