From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mx0b-002e3701.pphosted.com (mx0b-002e3701.pphosted.com [148.163.143.35]) by mx.groups.io with SMTP id smtpd.web10.5182.1635383273999635130 for ; Wed, 27 Oct 2021 18:08:02 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@hpe.com header.s=pps0720 header.b=kyiRJmrH; spf=temperror, err=temporary DNS error (domain: hpe.com, ip: 148.163.143.35, mailfrom: prvs=09357d8486=abner.chang@hpe.com) Received: from pps.filterd (m0134423.ppops.net [127.0.0.1]) by mx0b-002e3701.pphosted.com (8.16.1.2/8.16.1.2) with SMTP id 19RJq9Yr013458; Thu, 28 Oct 2021 01:07:31 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=hpe.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding; s=pps0720; bh=sh9Fvm5CbVmBOGN42azG2EY/n5mOvYF12oe5EIqKIWk=; b=kyiRJmrHYnnwlGhljlkWcFJuMhPNPv1frvMR5kYyov0Lal+27pNEtf0UCLtampm8rzhX cWqvHJGF3gU+kSSamj520mwAYafyB73z77HVSSU249Vizb+cU6xgl0JjyVy/XtMxy/SL XA8Po5mOo4j1Y+euXCZIOmUaIY7BrMLfht2+fBUpGa1au5mdksqav9EvvIKCUddtZJxC CSWtFVrfxs1x2HLjSvt8G9MJfeW00HxOzNYVfwDCXCCclAYIcvUJdN5h9SkEqke7E9RN zt7fRj5hwcjLrHQ41BVhzafh9oDVK8S35R6R3ymh2KF4qgon7g2OrMEcxqbaObJKU5Q/ VQ== Received: from g2t2353.austin.hpe.com (g2t2353.austin.hpe.com [15.233.44.26]) by mx0b-002e3701.pphosted.com with ESMTP id 3bxvwvjb6x-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Thu, 28 Oct 2021 01:07:30 +0000 Received: from g2t2360.austin.hpecorp.net (g2t2360.austin.hpecorp.net [16.196.225.135]) by g2t2353.austin.hpe.com (Postfix) with ESMTP id D3B6E90; Thu, 28 Oct 2021 01:07:29 +0000 (UTC) Received: from SAC2XFT1JT.asiapacific.hpqcorp.net (unknown [10.43.62.133]) by g2t2360.austin.hpecorp.net (Postfix) with ESMTP id EEBDE36; Thu, 28 Oct 2021 01:07:28 +0000 (UTC) From: "Abner Chang" To: devel@edk2.groups.io Cc: nickle.wang@hpe.com, Liming Gao Subject: [PATCH RESEND 3/5] edk2-staging/RedfishPkg: Implementation of EDKII_REDFISH_PLATFORM_CONFIG_PROTOCOL Date: Thu, 28 Oct 2021 09:07:21 +0800 Message-Id: <20211028010723.37316-4-abner.chang@hpe.com> X-Mailer: git-send-email 2.21.0.windows.1 In-Reply-To: <20211028010723.37316-1-abner.chang@hpe.com> References: <20211028010723.37316-1-abner.chang@hpe.com> MIME-Version: 1.0 X-Proofpoint-GUID: m5QYnUfTuCpo07S28pv-1ekJeMJjaha8 X-Proofpoint-ORIG-GUID: m5QYnUfTuCpo07S28pv-1ekJeMJjaha8 X-HPE-SCL: -1 X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.182.1,Aquarius:18.0.790,Hydra:6.0.425,FMLib:17.0.607.475 definitions=2021-10-27_07,2021-10-26_01,2020-04-07_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 malwarescore=0 lowpriorityscore=0 clxscore=1015 mlxlogscore=999 suspectscore=0 bulkscore=0 adultscore=0 mlxscore=0 priorityscore=1501 phishscore=0 spamscore=0 impostorscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-2110150000 definitions=main-2110280005 Content-Transfer-Encoding: 8bit This is the Implementation of EDKII_REDFISH_PLATFORM_CONFIG_PROTOCOL, which is the EDKII Redfish Platform Config driver instance that accesses EDK2 HII configuration format and storage. Signed-off-by: Nickle Wang Cc: Abner Chang Cc: Liming Gao --- .../RedfishPlatformConfigDxe.inf | 53 + .../RedfishPlatformConfigDxe.h | 64 + .../RedfishPlatformConfigImpl.h | 274 ++++ .../RedfishPlatformConfigDxe.c | 1304 +++++++++++++++++ .../RedfishPlatformConfigImpl.c | 1240 ++++++++++++++++ 5 files changed, 2935 insertions(+) create mode 100644 RedfishPkg/RedfishPlatformConfigDxe/RedfishPlatformConfigDxe.inf create mode 100644 RedfishPkg/RedfishPlatformConfigDxe/RedfishPlatformConfigDxe.h create mode 100644 RedfishPkg/RedfishPlatformConfigDxe/RedfishPlatformConfigImpl.h create mode 100644 RedfishPkg/RedfishPlatformConfigDxe/RedfishPlatformConfigDxe.c create mode 100644 RedfishPkg/RedfishPlatformConfigDxe/RedfishPlatformConfigImpl.c diff --git a/RedfishPkg/RedfishPlatformConfigDxe/RedfishPlatformConfigDxe.inf b/RedfishPkg/RedfishPlatformConfigDxe/RedfishPlatformConfigDxe.inf new file mode 100644 index 00000000000..16739bef7a3 --- /dev/null +++ b/RedfishPkg/RedfishPlatformConfigDxe/RedfishPlatformConfigDxe.inf @@ -0,0 +1,53 @@ +## @file +# Implementation of EDKII_REDFISH_PLATFORM_CONFIG_PROTOCOL interfaces. +# +# (C) Copyright 2021 Hewlett Packard Enterprise Development LP
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = RedfishPlatformConfigDxe + FILE_GUID = BEAEFFE1-0633-41B5-913C-9389339C2927 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = RedfishPlatformConfigDxeEntryPoint + UNLOAD_IMAGE = RedfishPlatformConfigDxeUnload + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + RedfishPkg/RedfishPkg.dec + +[Sources] + RedfishPlatformConfigDxe.h + RedfishPlatformConfigDxe.c + RedfishPlatformConfigImpl.h + RedfishPlatformConfigImpl.c + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + DevicePathLib + HiiLib + HiiUtilityLib + MemoryAllocationLib + PrintLib + UefiLib + UefiBootServicesTableLib + UefiRuntimeServicesTableLib + UefiDriverEntryPoint + +[Protocols] + gEdkIIRedfishPlatformConfigProtocolGuid ## PRODUCED + gEfiHiiDatabaseProtocolGuid ## CONSUMED + gEfiHiiStringProtocolGuid ## CONSUMED + gEfiRegularExpressionProtocolGuid ## CONSUMED + +[Guids] + gEfiRegexSyntaxTypePerlGuid ## CONSUMED + +[Depex] + TRUE \ No newline at end of file diff --git a/RedfishPkg/RedfishPlatformConfigDxe/RedfishPlatformConfigDxe.h b/RedfishPkg/RedfishPlatformConfigDxe/RedfishPlatformConfigDxe.h new file mode 100644 index 00000000000..99a613d229c --- /dev/null +++ b/RedfishPkg/RedfishPlatformConfigDxe/RedfishPlatformConfigDxe.h @@ -0,0 +1,64 @@ +/** @file + This file defines the EDKII Redfish Platform Config Protocol interface. + + (C) Copyright 2021 Hewlett Packard Enterprise Development LP
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef EDKII_REDFISH_PLATFORM_CONFIG_DXE_H_ +#define EDKII_REDFISH_PLATFORM_CONFIG_DXE_H_ + +#include + +// +// Libraries +// +#include +#include +#include +#include +#include +#include +#include +#include + +// +// Produced Protocols +// +#include +#include +#include +#include + +// +// Definition of EDKII_REDFISH_PLATFORM_CONFIG_NOTIFY. +// +typedef struct { + EFI_EVENT ProtocolEvent; // Protocol notification event. + VOID *Registration; // Protocol notification registration. +} REDFISH_PLATFORM_CONFIG_NOTIFY; + +// +// Definition of REDFISH_PLATFORM_CONFIG_PRIVATE. +// +typedef struct { + EFI_HANDLE ImageHandle; // Driver image handle. + EDKII_REDFISH_PLATFORM_CONFIG_PROTOCOL Protocol; + REDFISH_PLATFORM_CONFIG_NOTIFY HiiDbNotify; + EFI_HII_DATABASE_PROTOCOL *HiiDatabase; // The HII database protocol. + REDFISH_PLATFORM_CONFIG_NOTIFY HiiStringNotify; + EFI_HII_STRING_PROTOCOL *HiiString; // HII String Protocol. + REDFISH_PLATFORM_CONFIG_NOTIFY RegexNotify; + EFI_REGULAR_EXPRESSION_PROTOCOL *RegularExpressionProtocol; // Regular Expression Protocol. + EFI_HANDLE NotifyHandle; // The notify handle. + LIST_ENTRY FormsetList; // The list to keep cached HII formset. + LIST_ENTRY PendingList; // The list to keep updated HII handle. +} REDFISH_PLATFORM_CONFIG_PRIVATE; + +#define REDFISH_PLATFORM_CONFIG_PRIVATE_FROM_THIS(a) BASE_CR (a, REDFISH_PLATFORM_CONFIG_PRIVATE, Protocol) +#define REGULAR_EXPRESSION_INCLUDE_ALL L".*" +#define CONFIGURE_LANGUAGE_PREFIX "x-uefi-redfish-" + +#endif diff --git a/RedfishPkg/RedfishPlatformConfigDxe/RedfishPlatformConfigImpl.h b/RedfishPkg/RedfishPlatformConfigDxe/RedfishPlatformConfigImpl.h new file mode 100644 index 00000000000..e0ba0fb2d3d --- /dev/null +++ b/RedfishPkg/RedfishPlatformConfigDxe/RedfishPlatformConfigImpl.h @@ -0,0 +1,274 @@ +/** @file + This file defines the EDKII Redfish Platform Config Protocol interface. + + (C) Copyright 2021 Hewlett Packard Enterprise Development LP
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef EDKII_REDFISH_PLATFORM_CONFIG_IMPL_H_ +#define EDKII_REDFISH_PLATFORM_CONFIG_IMPL_H_ + +#include + +// +// Libraries +// +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define REDFISH_PLATFORM_CONFIG_DELETE_EXPIRED_FORMSET 0x00 +#define IS_EMPTY_STRING(a) (a == NULL || a[0] == L'\0') +#define ENGLISH_LANGUAGE_CODE "en-US" +#define X_UEFI_SCHEMA_PREFIX "x-uefi-redfish-" + +// +// Definition of REDFISH_PLATFORM_CONFIG_PRIVATE. +// +typedef struct { + LIST_ENTRY Link; + EFI_HII_HANDLE HiiHandle; + BOOLEAN IsDeleted; +} REDFISH_PLATFORM_CONFIG_PENDING_LIST; + +#define REDFISH_PLATFORM_CONFIG_PENDING_LIST_FROM_LINK(a) BASE_CR (a, REDFISH_PLATFORM_CONFIG_PENDING_LIST, Link) + +typedef struct { + UINTN Count; // Number of schema in list + CHAR8 **SchemaList; // Schema list +} REDFISH_PLATFORM_CONFIG_SCHEMA; + +// +// Definition of REDFISH_PLATFORM_CONFIG_FORM_SET_PRIVATE +// +typedef struct { + LIST_ENTRY Link; + HII_FORMSET *HiiFormSet; // Pointer to HII formset data. + EFI_GUID Guid; // Formset GUID. + EFI_HII_HANDLE HiiHandle; // Hii Handle of this formset. + LIST_ENTRY HiiFormList; // Form list that keep form data under this formset. + CHAR16 *DevicePathStr; // Device path of this formset. + REDFISH_PLATFORM_CONFIG_SCHEMA SupportedSchema; // Schema that is supported in this formset. +} REDFISH_PLATFORM_CONFIG_FORM_SET_PRIVATE; + +#define REDFISH_PLATFORM_CONFIG_FORMSET_FROM_LINK(a) BASE_CR (a, REDFISH_PLATFORM_CONFIG_FORM_SET_PRIVATE, Link) + +// +// Definition of REDFISH_PLATFORM_CONFIG_FORM_PRIVATE +// +typedef struct { + LIST_ENTRY Link; + UINT16 Id; // Form ID. + EFI_STRING_ID Title; // String token of form title. + REDFISH_PLATFORM_CONFIG_FORM_SET_PRIVATE *ParentFormset; + HII_FORM *HiiForm; // Pointer to HII form data. + LIST_ENTRY StatementList; // Statement list that keep statement under this form. +} REDFISH_PLATFORM_CONFIG_FORM_PRIVATE; + +#define REDFISH_PLATFORM_CONFIG_FORM_FROM_LINK(a) BASE_CR (a, REDFISH_PLATFORM_CONFIG_FORM_PRIVATE, Link) + +// +// Definition of REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE +// +typedef struct { + LIST_ENTRY Link; + REDFISH_PLATFORM_CONFIG_FORM_PRIVATE *ParentForm; + HII_STATEMENT *HiiStatement; // Pointer to HII statement data. + EFI_QUESTION_ID QuestionId; // Question ID of this statement. + EFI_STRING_ID Description; // String token of this question. + EFI_STRING DesStringCache; // The string cache for search function. +} REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE; + +#define REDFISH_PLATFORM_CONFIG_STATEMENT_FROM_LINK(a) BASE_CR (a, REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE, Link) + +// +// Definition of REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE_REF +// +typedef struct { + LIST_ENTRY Link; + REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE *Statement; +} REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE_REF; + +#define REDFISH_PLATFORM_CONFIG_STATEMENT_REF_FROM_LINK(a) BASE_CR (a, REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE_REF, Link) + +// +// Definition of REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE_LIST +// +typedef struct { + LIST_ENTRY StatementList; // List of REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE_REF + UINTN Count; +} REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE_LIST; + +/** + Release formset list and all the forms that belong to this formset. + + @param[in] FormsetList Pointer to formst list that needs to be + released. + + @retval EFI_STATUS + +**/ +EFI_STATUS +ReleaseFormsetList ( + IN LIST_ENTRY *FormsetList + ); + +/** + Release formset list and all the forms that belong to this formset. + + @param[in] FormsetList Pointer to formst list that needs to be + released. + + @retval EFI_STATUS + +**/ +EFI_STATUS +LoadFormsetList ( + IN EFI_HII_HANDLE *HiiHandle, + OUT LIST_ENTRY *FormsetList + ); + +/** + When HII database is updated. Keep updated HII handle into pending list so + we can process them later. + + @param[in] HiiHandle HII handle instance. + @param[in] PendingList Pending list to keep HII handle which is recently updated. + + @retval EFI_SUCCESS HII handle is saved in pending list. + @retval EFI_INVALID_PARAMETER HiiHnalde is NULL or PendingList is NULL. + @retval EFI_OUT_OF_RESOURCES System is out of memory. + +**/ +EFI_STATUS +NotifyFormsetUpdate ( + IN EFI_HII_HANDLE *HiiHandle, + IN LIST_ENTRY *PendingList + ); + +/** + When HII database is updated and form-set is deleted. Keep deleted HII handle into pending list so + we can process them later. + + @param[in] HiiHandle HII handle instance. + @param[in] PendingList Pending list to keep HII handle which is recently updated. + + @retval EFI_SUCCESS HII handle is saved in pending list. + @retval EFI_INVALID_PARAMETER HiiHnalde is NULL or PendingList is NULL. + @retval EFI_OUT_OF_RESOURCES System is out of memory. + +**/ +EFI_STATUS +NotifyFormsetDeleted ( + IN EFI_HII_HANDLE *HiiHandle, + IN LIST_ENTRY *PendingList + ); + +/** + Get statement private instance by the given configure language. + + @param[in] FormsetList Form-set list to search. + @param[in] Schema Schema to be matched. + @param[in] ConfigureLang Configure language. + + @retval REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE * Pointer to statement private instance. + +**/ +REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE * +GetStatementPrivateByConfigureLang ( + IN LIST_ENTRY *FormsetList, + IN CHAR8 *Schema, + IN EFI_STRING ConfigureLang + ); + +/** + Search and find statement private instance by given regular expression patthern + which describes the Configure Language. + + @param[in] RegularExpressionProtocol Regular express protocol. + @param[in] FormsetList Form-set list to search. + @param[in] Schema Schema to be matched. + @param[in] Pattern Regular expression pattern. + @param[out] StatementList Statement list that match above pattern. + + @retval EFI_SUCCESS Statement list is returned. + @retval EFI_INVALID_PARAMETER Input parameter is NULL. + @retval EFI_NOT_READY Regular express protocol is NULL. + @retval EFI_NOT_FOUND No statement is found. + @retval EFI_OUT_OF_RESOURCES System is out of memory. + +**/ +EFI_STATUS +GetStatementPrivateByConfigureLangRegex ( + IN EFI_REGULAR_EXPRESSION_PROTOCOL *RegularExpressionProtocol, + IN LIST_ENTRY *FormsetList, + IN CHAR8 *Schema, + IN EFI_STRING Pattern, + OUT REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE_LIST *StatementList + ); + +/** + There are HII database update and we need to process them accordingly so that we + won't use stale data. This function will parse updated HII handle again in order + to get updated data-set. + + @param[in] FormsetList List to keep HII form-set. + @param[in] PendingList List to keep HII handle that is updated. + + @retval EFI_SUCCESS HII handle is saved in pending list. + @retval EFI_INVALID_PARAMETER FormsetList is NULL or PendingList is NULL. + +**/ +EFI_STATUS +ProcessPendingList ( + IN LIST_ENTRY *FormsetList, + IN LIST_ENTRY *PendingList + ); + +/** + Retrieves a string from a string package in a English language. The + returned string is allocated using AllocatePool(). The caller is responsible + for freeing the allocated buffer using FreePool(). + + If HiiHandle is NULL, then ASSERT(). + If StringId is 0, then ASSET. + + @param[in] HiiStringProtocol EFI_HII_STRING_PROTOCOL instance. + @param[in] HiiHandle A handle that was previously registered in the HII Database. + @param[in] StringId The identifier of the string to retrieved from the string + package associated with HiiHandle. + + @retval NULL The string specified by StringId is not present in the string package. + @retval Other The string was returned. + +**/ +EFI_STRING +HiiGetRedfishString ( + IN EFI_HII_HANDLE HiiHandle, + IN CHAR8 *Language, + IN EFI_STRING_ID StringId + ); + +/** + Release all resource in statement list. + + @param[in] StatementList Statement list to be released. + + @retval EFI_SUCCESS All resource are released. + @retval EFI_INVALID_PARAMETER StatementList is NULL. + +**/ +EFI_STATUS +ReleaseStatementList ( + IN REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE_LIST *StatementList + ); + +#endif diff --git a/RedfishPkg/RedfishPlatformConfigDxe/RedfishPlatformConfigDxe.c b/RedfishPkg/RedfishPlatformConfigDxe/RedfishPlatformConfigDxe.c new file mode 100644 index 00000000000..67818cccd24 --- /dev/null +++ b/RedfishPkg/RedfishPlatformConfigDxe/RedfishPlatformConfigDxe.c @@ -0,0 +1,1304 @@ +/** @file + + The implementation of EDKII Redfidh Platform Config Protocol. + + (C) Copyright 2021 Hewlett Packard Enterprise Development LP
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "RedfishPlatformConfigDxe.h" +#include "RedfishPlatformConfigImpl.h" + +REDFISH_PLATFORM_CONFIG_PRIVATE *mRedfishPlatformConfigPrivate = NULL; + +/** + Compare two value in HII statement format. + + @param[in] Value1 Firt value to compare. + @param[in] Value2 Second value to be compared. + + @retval UINTN 0 is retuned when two values are equal. + 1 is returned when first value is greater than second value. + -1 is returned when second value is greater than first value. + +**/ +UINTN +CompareHiiStatementValue ( + IN HII_STATEMENT_VALUE *Value1, + IN HII_STATEMENT_VALUE *Value2 + ) +{ + INTN Result; + UINT64 Data1; + UINT64 Data2; + + if (Value1 == NULL || Value2 == NULL) { + return 0xFF; + } + + switch (Value1->Type) { + case EFI_IFR_TYPE_NUM_SIZE_8: + Data1 = Value1->Value.u8; + break; + case EFI_IFR_TYPE_NUM_SIZE_16: + Data1 = Value1->Value.u16; + break; + case EFI_IFR_TYPE_NUM_SIZE_32: + Data1 = Value1->Value.u32; + break; + case EFI_IFR_TYPE_NUM_SIZE_64: + Data1 = Value1->Value.u64; + break; + case EFI_IFR_TYPE_BOOLEAN: + Data1 = (Value1->Value.b ? 1 : 0); + break; + default: + return 0xFF; + } + + switch (Value2->Type) { + case EFI_IFR_TYPE_NUM_SIZE_8: + Data2 = Value2->Value.u8; + break; + case EFI_IFR_TYPE_NUM_SIZE_16: + Data2 = Value2->Value.u16; + break; + case EFI_IFR_TYPE_NUM_SIZE_32: + Data2 = Value2->Value.u32; + break; + case EFI_IFR_TYPE_NUM_SIZE_64: + Data2 = Value2->Value.u64; + break; + case EFI_IFR_TYPE_BOOLEAN: + Data2 = (Value2->Value.b ? 1 : 0); + break; + default: + return 0xFF; + } + + Result = (Data1 == Data2 ? 0 : (Data1 > Data2 ? 1 : -1)); + + return Result; +} + +/** + Convert HII value to the string in HII one-of opcode. + + @param[in] Statement Statement private instance + + @retval EFI_STRING_ID The string ID in HII database. + 0 is returned when something goes wrong. + +**/ +EFI_STRING_ID +HiiValueToOneOfOptionStringId ( + IN REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE *Statement + ) +{ + LIST_ENTRY *Link; + HII_QUESTION_OPTION *Option; + + if (Statement->HiiStatement->Operand != EFI_IFR_ONE_OF_OP) { + return 0; + } + + if (IsListEmpty (&Statement->HiiStatement->OptionListHead)) { + return 0; + } + + Link = GetFirstNode (&Statement->HiiStatement->OptionListHead); + while (!IsNull (&Statement->HiiStatement->OptionListHead, Link)) { + Option = HII_QUESTION_OPTION_FROM_LINK (Link); + + if (CompareHiiStatementValue (&Statement->HiiStatement->Value, &Option->Value) == 0) { + return Option->Text; + } + + Link = GetNextNode (&Statement->HiiStatement->OptionListHead, Link); + } + + return 0; +} + +/** + Convert HII string to the value in HII one-of opcode. + + @param[in] Statement Statement private instance + @param[in] Schema Schema string + @param[in] HiiString Input string + @param[out] Value Value returned + + @retval EFI_SUCCESS HII value is returned successfully. + @retval Others Errors occur + +**/ +EFI_STATUS +HiiStringToOneOfOptionValue ( + IN REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE *Statement, + IN CHAR8 *Schema, + IN EFI_STRING HiiString, + OUT HII_STATEMENT_VALUE *Value + ) +{ + LIST_ENTRY *Link; + HII_QUESTION_OPTION *Option; + EFI_STRING TmpString; + BOOLEAN Found; + + if (Statement == NULL || IS_EMPTY_STRING (HiiString) || Value == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (Statement->HiiStatement->Operand != EFI_IFR_ONE_OF_OP) { + return EFI_UNSUPPORTED; + } + + if (IsListEmpty (&Statement->HiiStatement->OptionListHead)) { + return EFI_NOT_FOUND; + } + + Found = FALSE; + Link = GetFirstNode (&Statement->HiiStatement->OptionListHead); + while (!IsNull (&Statement->HiiStatement->OptionListHead, Link)) { + Option = HII_QUESTION_OPTION_FROM_LINK (Link); + + TmpString = HiiGetRedfishString (Statement->ParentForm->ParentFormset->HiiHandle, Schema, Option->Text); + if (TmpString != NULL) { + if (StrCmp (TmpString, HiiString) == 0) { + CopyMem (Value, &Option->Value, sizeof (HII_STATEMENT_VALUE)); + Found = TRUE; + } + FreePool (TmpString); + } + + if (Found) { + return EFI_SUCCESS; + } + + Link = GetNextNode (&Statement->HiiStatement->OptionListHead, Link); + } + + return EFI_NOT_FOUND; +} + +/** + Convert HII value to numeric value in Redfish format. + + @param[in] Value Value to be converted. + @param[out] RedfishValue Value in Redfish format. + + @retval EFI_SUCCESS Redfish value is returned successfully. + @retval Others Errors occur + +**/ +EFI_STATUS +HiiValueToRedfishNumeric ( + IN HII_STATEMENT_VALUE *Value, + OUT EDKII_REDFISH_VALUE *RedfishValue + ) +{ + if (Value == NULL || RedfishValue == NULL) { + return EFI_INVALID_PARAMETER; + } + + switch (Value->Type) { + case EFI_IFR_TYPE_NUM_SIZE_8: + RedfishValue->Type = REDFISH_VALUE_TYPE_INTEGER; + RedfishValue->Value.Integer = (INT64)Value->Value.u8; + break; + case EFI_IFR_TYPE_NUM_SIZE_16: + RedfishValue->Type = REDFISH_VALUE_TYPE_INTEGER; + RedfishValue->Value.Integer = (INT64)Value->Value.u16; + break; + case EFI_IFR_TYPE_NUM_SIZE_32: + RedfishValue->Type = REDFISH_VALUE_TYPE_INTEGER; + RedfishValue->Value.Integer = (INT64)Value->Value.u32; + break; + case EFI_IFR_TYPE_NUM_SIZE_64: + RedfishValue->Type = REDFISH_VALUE_TYPE_INTEGER; + RedfishValue->Value.Integer = (INT64)Value->Value.u64; + break; + case EFI_IFR_TYPE_BOOLEAN: + RedfishValue->Type = REDFISH_VALUE_TYPE_BOOLEAN; + RedfishValue->Value.Boolean = Value->Value.b; + break; + default: + RedfishValue->Type = REDFISH_VALUE_TYPE_UNKNOWN; + break; + } + + return EFI_SUCCESS; +} + +/** + Convert numeric value in Redfish format to HII value. + + @param[in] RedfishValue Value in Redfish format to be converted. + @param[out] Value HII value returned. + + @retval EFI_SUCCESS HII value is returned successfully. + @retval Others Errors occur + +**/ +EFI_STATUS +RedfishNumericToHiiValue ( + IN EDKII_REDFISH_VALUE *RedfishValue, + OUT HII_STATEMENT_VALUE *Value + ) +{ + if (Value == NULL || RedfishValue == NULL) { + return EFI_INVALID_PARAMETER; + } + + switch (RedfishValue->Type) { + case REDFISH_VALUE_TYPE_INTEGER: + Value->Type = EFI_IFR_TYPE_NUM_SIZE_64; + Value->Value.u64 = (UINT64)RedfishValue->Value.Integer; + break; + case REDFISH_VALUE_TYPE_BOOLEAN: + Value->Type = EFI_IFR_TYPE_BOOLEAN; + Value->Value.b = RedfishValue->Value.Boolean; + break; + default: + Value->Type = EFI_IFR_TYPE_UNDEFINED; + break; + } + + return EFI_SUCCESS; +} + +/** + Return the full Redfish schema string from the given Schema and Version. + + Returned schema string is: Schema + '.' + Version + + @param[in] Schema Schema string + @param[in] Version Schema version string + + @retval CHAR8 * Schema string. NULL when errors occur. + +**/ +CHAR8 * +GetFullSchemaString ( + IN CHAR8 *Schema, + IN CHAR8 *Version + ) +{ + UINTN Size; + CHAR8 *FullName; + + if (IS_EMPTY_STRING (Schema) || IS_EMPTY_STRING (Version)) { + return NULL; + } + + Size = AsciiStrSize(CONFIGURE_LANGUAGE_PREFIX) + AsciiStrSize (Schema) + AsciiStrSize (Version); + + FullName = AllocatePool (Size); + if (FullName == NULL) { + DEBUG ((DEBUG_ERROR, "%a, out-of-resource\n", __FUNCTION__)); + return NULL; + } + + AsciiSPrint (FullName, Size, "%a%a.%a", CONFIGURE_LANGUAGE_PREFIX, Schema, Version); + + return FullName; +} + +/** + Common implementation to get statement private instance. + + @param[in] RedfishPlatformConfigPrivate Private instance. + @param[in] Schema Redfish schema string. + @param[in] ConfigureLang Configure language that refers to this statement. + @param[out] Statement Statement instance + + @retval EFI_SUCCESS HII value is returned successfully. + @retval Others Errors occur + +**/ +EFI_STATUS +RedfishPlatformConfigGetStatementCommon ( + IN REDFISH_PLATFORM_CONFIG_PRIVATE *RedfishPlatformConfigPrivate, + IN CHAR8 *Schema, + IN EFI_STRING ConfigureLang, + OUT REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE **Statement + ) +{ + EFI_STATUS Status; + REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE *TargetStatement; + + if (RedfishPlatformConfigPrivate == NULL || IS_EMPTY_STRING (Schema) || IS_EMPTY_STRING (ConfigureLang) || Statement == NULL) { + return EFI_INVALID_PARAMETER; + } + + *Statement = NULL; + + Status = ProcessPendingList (&RedfishPlatformConfigPrivate->FormsetList, &RedfishPlatformConfigPrivate->PendingList); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a, ProcessPendingList failure: %r\n", __FUNCTION__, Status)); + return Status; + } + + TargetStatement = GetStatementPrivateByConfigureLang (&RedfishPlatformConfigPrivate->FormsetList, Schema, ConfigureLang); + if (TargetStatement == NULL) { + DEBUG ((DEBUG_ERROR, "%a, No match HII statement is found by the given %s in schema %a\n", __FUNCTION__, ConfigureLang, Schema)); + return EFI_NOT_FOUND; + } + + // + // Find current HII question value. + // + Status = GetQuestionValue ( + TargetStatement->ParentForm->ParentFormset->HiiFormSet, + TargetStatement->ParentForm->HiiForm, + TargetStatement->HiiStatement, + GetSetValueWithHiiDriver + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a, failed to get question current value: %r\n", __FUNCTION__, Status)); + return Status; + } + + + if (TargetStatement->HiiStatement->Value.Type == EFI_IFR_TYPE_UNDEFINED) { + return EFI_DEVICE_ERROR; + } + + // + // Return Value. + // + *Statement = TargetStatement; + + return EFI_SUCCESS; +} + +/** + Get Redfish value with the given Schema and Configure Language. + + @param[in] This Pointer to EDKII_REDFISH_PLATFORM_CONFIG_PROTOCOL instance. + @param[in] Schema The Redfish schema to query. + @param[in] Version The Redfish version to query. + @param[in] ConfigureLang The target value which match this configure Language. + @param[out] Value The returned value. + + @retval EFI_SUCCESS Value is returned successfully. + @retval Others Some error happened. + +**/ +EFI_STATUS +EFIAPI +RedfishPlatformConfigProtocolGetValue ( + IN EDKII_REDFISH_PLATFORM_CONFIG_PROTOCOL *This, + IN CHAR8 *Schema, + IN CHAR8 *Version, + IN EFI_STRING ConfigureLang, + OUT EDKII_REDFISH_VALUE *Value + ) +{ + EFI_STATUS Status; + REDFISH_PLATFORM_CONFIG_PRIVATE *RedfishPlatformConfigPrivate; + REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE *TargetStatement; + EFI_STRING_ID StringId; + CHAR8 *FullSchema; + EFI_STRING HiiString; + UINTN Size; + + if (This == NULL || IS_EMPTY_STRING (Schema) || IS_EMPTY_STRING (Version) || IS_EMPTY_STRING (ConfigureLang) || Value == NULL) { + return EFI_INVALID_PARAMETER; + } + + RedfishPlatformConfigPrivate = REDFISH_PLATFORM_CONFIG_PRIVATE_FROM_THIS (This); + Value->Type = REDFISH_VALUE_TYPE_UNKNOWN; + FullSchema = NULL; + HiiString = NULL; + + FullSchema = GetFullSchemaString (Schema, Version); + if (FullSchema == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = RedfishPlatformConfigGetStatementCommon (RedfishPlatformConfigPrivate, FullSchema, ConfigureLang, &TargetStatement); + if (EFI_ERROR (Status)) { + goto RELEASE_RESOURCE; + } + + switch (TargetStatement->HiiStatement->Operand) { + case EFI_IFR_ONE_OF_OP: + StringId = HiiValueToOneOfOptionStringId (TargetStatement); + if (StringId == 0) { + ASSERT (FALSE); + Status = EFI_DEVICE_ERROR; + goto RELEASE_RESOURCE; + } + + HiiString = HiiGetRedfishString (TargetStatement->ParentForm->ParentFormset->HiiHandle, FullSchema, StringId); + if (HiiString == NULL) { + DEBUG ((DEBUG_ERROR, "%a, Can not find string ID: 0x%x with %a\n", __FUNCTION__, StringId, FullSchema)); + Status = EFI_NOT_FOUND; + goto RELEASE_RESOURCE; + } + + Size = StrLen (HiiString) + 1; + Value->Value.Buffer = AllocatePool (Size); + if (Value->Value.Buffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto RELEASE_RESOURCE; + } + + UnicodeStrToAsciiStrS (HiiString, Value->Value.Buffer, Size); + Value->Type = REDFISH_VALUE_TYPE_STRING; + + break; + case EFI_IFR_STRING_OP: + if (TargetStatement->HiiStatement->Value.Type != EFI_IFR_TYPE_STRING) { + ASSERT (FALSE); + Status = EFI_DEVICE_ERROR; + goto RELEASE_RESOURCE; + } + + Value->Type = REDFISH_VALUE_TYPE_STRING; + Value->Value.Buffer = AllocateCopyPool (StrSize ((CHAR16 *)TargetStatement->HiiStatement->Value.Buffer), TargetStatement->HiiStatement->Value.Buffer); + if (Value->Value.Buffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto RELEASE_RESOURCE; + } + break; + case EFI_IFR_CHECKBOX_OP: + case EFI_IFR_NUMERIC_OP: + Status = HiiValueToRedfishNumeric (&TargetStatement->HiiStatement->Value, Value); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a, failed to convert HII value to Redfish value: %r\n", __FUNCTION__, Status)); + goto RELEASE_RESOURCE; + } + break; + default: + DEBUG ((DEBUG_ERROR, "%a, catch unsupported type: 0x%x! Please contact with author if we need to support this type.\n", __FUNCTION__, TargetStatement->HiiStatement->Operand)); + ASSERT (FALSE); + Status = EFI_UNSUPPORTED; + goto RELEASE_RESOURCE; + } + +RELEASE_RESOURCE: + + if (FullSchema != NULL) { + FreePool (FullSchema); + } + + if (HiiString != NULL) { + FreePool (HiiString); + } + + return Status; +} + +/** + Function to save question value into HII database. + + @param[in] HiiFormset HII form-set instance + @param[in] HiiForm HII form instance + @param[in] HiiStatement HII statement that keeps new value. + @param[in] Value New value to applyu. + + @retval EFI_SUCCESS HII value is returned successfully. + @retval Others Errors occur + +**/ +EFI_STATUS +RedfishPlatformConfigSaveQuestionValue ( + IN HII_FORMSET *HiiFormset, + IN HII_FORM *HiiForm, + IN HII_STATEMENT *HiiStatement, + IN HII_STATEMENT_VALUE *Value + ) +{ + EFI_STATUS Status; + + if (HiiFormset == NULL || HiiForm == NULL || HiiStatement == NULL || Value == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = SetQuestionValue ( + HiiFormset, + HiiForm, + HiiStatement, + Value + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a, failed to set question value: %r\n", __FUNCTION__, Status)); + return Status; + } + + Status = SubmitForm (HiiFormset, HiiForm); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a, failed to submit form: %r\n", __FUNCTION__, Status)); + return Status; + } + + return EFI_SUCCESS; +} + +/** + Common implementation to set statement private instance. + + @param[in] RedfishPlatformConfigPrivate Private instance. + @param[in] Schema Redfish schema string. + @param[in] ConfigureLang Configure language that refers to this statement. + @param[in] Statement Statement instance + + @retval EFI_SUCCESS HII value is returned successfully. + @retval Others Errors occur + +**/ +EFI_STATUS +RedfishPlatformConfigSetStatementCommon ( + IN REDFISH_PLATFORM_CONFIG_PRIVATE *RedfishPlatformConfigPrivate, + IN CHAR8 *Schema, + IN EFI_STRING ConfigureLang, + IN HII_STATEMENT_VALUE *StatementValue + ) +{ + EFI_STATUS Status; + REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE *TargetStatement; + EFI_STRING TempBuffer; + + if (RedfishPlatformConfigPrivate == NULL || IS_EMPTY_STRING (Schema) || IS_EMPTY_STRING (ConfigureLang) || StatementValue == NULL) { + return EFI_INVALID_PARAMETER; + } + + TempBuffer = NULL; + + Status = ProcessPendingList (&RedfishPlatformConfigPrivate->FormsetList, &RedfishPlatformConfigPrivate->PendingList); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a, ProcessPendingList failure: %r\n", __FUNCTION__, Status)); + return Status; + } + + TargetStatement = GetStatementPrivateByConfigureLang (&RedfishPlatformConfigPrivate->FormsetList, Schema, ConfigureLang); + if (TargetStatement == NULL) { + DEBUG ((DEBUG_ERROR, "%a, No match HII statement is found by the given %s in schema %a\n", __FUNCTION__, ConfigureLang, Schema)); + return EFI_NOT_FOUND; + } + + if (StatementValue->Type != TargetStatement->HiiStatement->Value.Type) { + // + // We treat one-of type as string in Redfish. But one-of statement is not + // in string format from HII point of view. Do a patch here. + // + if (TargetStatement->HiiStatement->Operand == EFI_IFR_ONE_OF_OP && StatementValue->Type == EFI_IFR_TYPE_STRING) { + TempBuffer = AllocatePool (StatementValue->BufferLen * sizeof (CHAR16)); + if (TempBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + AsciiStrToUnicodeStrS (StatementValue->Buffer, TempBuffer, StatementValue->BufferLen); + FreePool (StatementValue->Buffer); + StatementValue->Buffer = NULL; + StatementValue->BufferLen = 0; + + Status = HiiStringToOneOfOptionValue (TargetStatement, Schema, TempBuffer, StatementValue); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a, failed to find option value by the given %s\n", __FUNCTION__, TempBuffer)); + FreePool (TempBuffer); + return EFI_NOT_FOUND; + } + + FreePool (TempBuffer); + } else if (TargetStatement->HiiStatement->Operand == EFI_IFR_NUMERIC_OP && StatementValue->Type == EFI_IFR_TYPE_NUM_SIZE_64) { + // + // Redfish only has numeric value type and it does not care about the value size. + // Do a patch here so we have proper value size applied. + // + StatementValue->Type = TargetStatement->HiiStatement->Value.Type; + } else { + DEBUG ((DEBUG_ERROR, "%a, catch value type mismatch! input type: 0x%x but target value type: 0x%x\n", __FUNCTION__, StatementValue->Type, TargetStatement->HiiStatement->Value.Type)); + ASSERT (FALSE); + } + } + + Status = RedfishPlatformConfigSaveQuestionValue ( + TargetStatement->ParentForm->ParentFormset->HiiFormSet, + TargetStatement->ParentForm->HiiForm, + TargetStatement->HiiStatement, + StatementValue + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a, failed to save question value: %r\n", __FUNCTION__, Status)); + return Status; + } + + return EFI_SUCCESS; +} + +/** + Set Redfish value with the given Schema and Configure Language. + + @param[in] This Pointer to EDKII_REDFISH_PLATFORM_CONFIG_PROTOCOL instance. + @param[in] Schema The Redfish schema to query. + @param[in] Version The Redfish version to query. + @param[in] ConfigureLang The target value which match this configure Language. + @param[in] Value The value to set. + + @retval EFI_SUCCESS Value is returned successfully. + @retval Others Some error happened. + +**/ +EFI_STATUS +EFIAPI +RedfishPlatformConfigProtocolSetValue ( + IN EDKII_REDFISH_PLATFORM_CONFIG_PROTOCOL *This, + IN CHAR8 *Schema, + IN CHAR8 *Version, + IN EFI_STRING ConfigureLang, + IN EDKII_REDFISH_VALUE Value + ) +{ + EFI_STATUS Status; + REDFISH_PLATFORM_CONFIG_PRIVATE *RedfishPlatformConfigPrivate; + CHAR8 *FullSchema; + HII_STATEMENT_VALUE NewValue; + + if (This == NULL || IS_EMPTY_STRING (Schema) || IS_EMPTY_STRING (Version) || IS_EMPTY_STRING (ConfigureLang)) { + return EFI_INVALID_PARAMETER; + } + + if (Value.Type == REDFISH_VALUE_TYPE_UNKNOWN || Value.Type >= REDFISH_VALUE_TYPE_MAX) { + return EFI_INVALID_PARAMETER; + } + + RedfishPlatformConfigPrivate = REDFISH_PLATFORM_CONFIG_PRIVATE_FROM_THIS (This); + FullSchema = NULL; + + FullSchema = GetFullSchemaString (Schema, Version); + if (FullSchema == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + ZeroMem (&NewValue, sizeof (HII_STATEMENT_VALUE)); + + switch (Value.Type) { + case REDFISH_VALUE_TYPE_INTEGER: + case REDFISH_VALUE_TYPE_BOOLEAN: + Status = RedfishNumericToHiiValue (&Value, &NewValue); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a, failed to convert Redfish value to Hii value: %r\n", __FUNCTION__, Status)); + goto RELEASE_RESOURCE; + } + break; + case REDFISH_VALUE_TYPE_STRING: + NewValue.Type = EFI_IFR_TYPE_STRING; + NewValue.BufferLen = (UINT16)AsciiStrSize (Value.Value.Buffer); + NewValue.Buffer = AllocateCopyPool (NewValue.BufferLen, Value.Value.Buffer); + if (NewValue.Buffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto RELEASE_RESOURCE; + } + break; + default: + ASSERT (FALSE); + break; + } + + Status = RedfishPlatformConfigSetStatementCommon (RedfishPlatformConfigPrivate, FullSchema, ConfigureLang, &NewValue); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a, failed to set value to statement: %r\n", __FUNCTION__, Status)); + } + +RELEASE_RESOURCE: + + if (FullSchema != NULL) { + FreePool (FullSchema); + } + + return Status; +} + +/** + Get the list of Configure Language from platform configuration by the given Schema and Pattern. + + @param[in] This Pointer to EDKII_REDFISH_PLATFORM_CONFIG_PROTOCOL instance. + @param[in] Schema The Redfish schema to query. + @param[in] Version The Redfish version to query. + @param[in] Pattern The target Configure Language pattern. + @param[out] ConfigureLangList The list of Configure Language. + @param[out] Count The number of Configure Language in ConfigureLangList. + + @retval EFI_SUCCESS ConfigureLangList is returned successfully. + @retval Others Some error happened. + +**/ +EFI_STATUS +EFIAPI +RedfishPlatformConfigProtocolGetConfigureLang ( + IN EDKII_REDFISH_PLATFORM_CONFIG_PROTOCOL *This, + IN CHAR8 *Schema, + IN CHAR8 *Version, + IN EFI_STRING Pattern, + OUT EFI_STRING **ConfigureLangList, + OUT UINTN *Count + ) +{ + REDFISH_PLATFORM_CONFIG_PRIVATE *RedfishPlatformConfigPrivate; + EFI_STATUS Status; + REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE_LIST StatementList; + REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE_REF *StatementRef; + LIST_ENTRY *NextLink; + EFI_STRING TmpString; + EFI_STRING *TmpConfigureLangList; + UINTN Index; + CHAR8 *FullSchema; + + if (This == NULL || IS_EMPTY_STRING (Schema) || IS_EMPTY_STRING (Version) || Count == NULL || ConfigureLangList == NULL || IS_EMPTY_STRING (Pattern)) { + return EFI_INVALID_PARAMETER; + } + + *Count = 0; + *ConfigureLangList = NULL; + FullSchema = NULL; + RedfishPlatformConfigPrivate = REDFISH_PLATFORM_CONFIG_PRIVATE_FROM_THIS (This); + + Status = ProcessPendingList (&RedfishPlatformConfigPrivate->FormsetList, &RedfishPlatformConfigPrivate->PendingList); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a, ProcessPendingList failure: %r\n", __FUNCTION__, Status)); + return Status; + } + + FullSchema = GetFullSchemaString (Schema, Version); + if (FullSchema == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = GetStatementPrivateByConfigureLangRegex ( + RedfishPlatformConfigPrivate->RegularExpressionProtocol, + &RedfishPlatformConfigPrivate->FormsetList, + FullSchema, + Pattern, + &StatementList + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a, GetStatementPrivateByConfigureLangRegex failure: %r\n", __FUNCTION__, Status)); + goto RELEASE_RESOURCE; + } + + if (!IsListEmpty (&StatementList.StatementList)) { + + TmpConfigureLangList = AllocateZeroPool (sizeof (CHAR16 *) * StatementList.Count); + if (TmpConfigureLangList == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto RELEASE_RESOURCE; + } + + Index = 0; + NextLink = GetFirstNode (&StatementList.StatementList); + while (!IsNull (&StatementList.StatementList, NextLink)) { + StatementRef = REDFISH_PLATFORM_CONFIG_STATEMENT_REF_FROM_LINK (NextLink); + NextLink = GetNextNode (&StatementList.StatementList, NextLink); + + ASSERT (StatementRef->Statement->Description != 0); + if (StatementRef->Statement->Description != 0) { + TmpString = HiiGetRedfishString (StatementRef->Statement->ParentForm->ParentFormset->HiiHandle, FullSchema, StatementRef->Statement->Description); + ASSERT (TmpString != NULL); + if (TmpString != NULL) { + TmpConfigureLangList[Index] = AllocateCopyPool (StrSize (TmpString), TmpString); + ASSERT (TmpConfigureLangList[Index] != NULL); + FreePool (TmpString); + ++Index; + } + } + } + } + + *Count = StatementList.Count; + *ConfigureLangList = TmpConfigureLangList; + +RELEASE_RESOURCE: + + if (FullSchema != NULL) { + FreePool (FullSchema); + } + + ReleaseStatementList (&StatementList); + + return Status; +} + + +/** + Get the list of supported Redfish schema from paltform configuration on give HII handle. + + @param[in] This Pointer to EDKII_REDFISH_PLATFORM_CONFIG_PROTOCOL instance. + @param[in] HiiHandle The target handle to search. If handle is NULL, + this function return all schema from HII database. + @param[out] SupportedSchema The supported schema list which is separated by ';'. + The SupportedSchema is allocated by the callee. It's caller's + responsibility to free this buffer using FreePool(). + + @retval EFI_SUCCESS Schema is returned successfully. + @retval Others Some error happened. + +**/ +EFI_STATUS +EFIAPI +RedfishPlatformConfigProtocolGetSupportedSchema ( + IN EDKII_REDFISH_PLATFORM_CONFIG_PROTOCOL *This, + IN EFI_HII_HANDLE HiiHandle, OPTIONAL + OUT CHAR8 **SupportedSchema + ) +{ + REDFISH_PLATFORM_CONFIG_PRIVATE *RedfishPlatformConfigPrivate; + EFI_STATUS Status; + LIST_ENTRY *HiiFormsetLink; + LIST_ENTRY *HiiFormsetNextLink; + REDFISH_PLATFORM_CONFIG_FORM_SET_PRIVATE *HiiFormsetPrivate; + UINTN Index; + UINTN StringSize; + CHAR8 *StringBuffer; + UINTN StringIndex; + + if (This == NULL || SupportedSchema == NULL) { + return EFI_INVALID_PARAMETER; + } + + *SupportedSchema = NULL; + + RedfishPlatformConfigPrivate = REDFISH_PLATFORM_CONFIG_PRIVATE_FROM_THIS (This); + + Status = ProcessPendingList (&RedfishPlatformConfigPrivate->FormsetList, &RedfishPlatformConfigPrivate->PendingList); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a, ProcessPendingList failure: %r\n", __FUNCTION__, Status)); + return Status; + } + + if (IsListEmpty (&RedfishPlatformConfigPrivate->FormsetList)) { + return EFI_NOT_FOUND; + } + + // + // Calculate for string buffer size. + // + StringSize = 0; + HiiFormsetLink = GetFirstNode (&RedfishPlatformConfigPrivate->FormsetList); + while (!IsNull (&RedfishPlatformConfigPrivate->FormsetList, HiiFormsetLink)) { + HiiFormsetNextLink = GetNextNode (&RedfishPlatformConfigPrivate->FormsetList, HiiFormsetLink); + HiiFormsetPrivate = REDFISH_PLATFORM_CONFIG_FORMSET_FROM_LINK (HiiFormsetLink); + + if (HiiHandle != NULL && HiiHandle != HiiFormsetPrivate->HiiHandle) { + HiiFormsetLink = HiiFormsetNextLink; + continue; + } + + if (HiiFormsetPrivate->SupportedSchema.Count > 0) { + for (Index = 0; Index < HiiFormsetPrivate->SupportedSchema.Count; Index++) { + StringSize += AsciiStrSize (HiiFormsetPrivate->SupportedSchema.SchemaList[Index]); + } + } + + HiiFormsetLink = HiiFormsetNextLink; + } + + if (StringSize == 0) { + return EFI_NOT_FOUND; + } + + StringBuffer = AllocatePool (StringSize); + if (StringBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + StringIndex = 0; + HiiFormsetLink = GetFirstNode (&RedfishPlatformConfigPrivate->FormsetList); + while (!IsNull (&RedfishPlatformConfigPrivate->FormsetList, HiiFormsetLink)) { + HiiFormsetNextLink = GetNextNode (&RedfishPlatformConfigPrivate->FormsetList, HiiFormsetLink); + HiiFormsetPrivate = REDFISH_PLATFORM_CONFIG_FORMSET_FROM_LINK (HiiFormsetLink); + + if (HiiHandle != NULL && HiiHandle != HiiFormsetPrivate->HiiHandle) { + HiiFormsetLink = HiiFormsetNextLink; + continue; + } + + if (HiiFormsetPrivate->SupportedSchema.Count > 0) { + for (Index = 0; Index < HiiFormsetPrivate->SupportedSchema.Count; Index++) { + AsciiStrCpyS (&StringBuffer[StringIndex], (StringSize - StringIndex), HiiFormsetPrivate->SupportedSchema.SchemaList[Index]); + StringIndex += AsciiStrLen (HiiFormsetPrivate->SupportedSchema.SchemaList[Index]); + StringBuffer[StringIndex] = ';'; + ++StringIndex; + } + } + + HiiFormsetLink = HiiFormsetNextLink; + } + + StringBuffer[--StringIndex] = '\0'; + + *SupportedSchema = StringBuffer; + + return EFI_SUCCESS; +} + +/** + Functions which are registered to receive notification of + database events have this prototype. The actual event is encoded + in NotifyType. The following table describes how PackageType, + PackageGuid, Handle, and Package are used for each of the + notification types. + + @param[in] PackageType Package type of the notification. + @param[in] PackageGuid If PackageType is + EFI_HII_PACKAGE_TYPE_GUID, then this is + the pointer to the GUID from the Guid + field of EFI_HII_PACKAGE_GUID_HEADER. + Otherwise, it must be NULL. + @param[in] Package Points to the package referred to by the + notification Handle The handle of the package + list which contains the specified package. + @param[in] Handle The HII handle. + @param[in] NotifyType The type of change concerning the + database. See + EFI_HII_DATABASE_NOTIFY_TYPE. + +**/ +EFI_STATUS +EFIAPI +RedfishPlatformConfigFormUpdateNotify ( + IN UINT8 PackageType, + IN CONST EFI_GUID *PackageGuid, + IN CONST EFI_HII_PACKAGE_HEADER *Package, + IN EFI_HII_HANDLE Handle, + IN EFI_HII_DATABASE_NOTIFY_TYPE NotifyType + ) +{ + EFI_STATUS Status; + + if (NotifyType == EFI_HII_DATABASE_NOTIFY_NEW_PACK || NotifyType == EFI_HII_DATABASE_NOTIFY_ADD_PACK) { + // + // HII formset on this handle is updated by driver during run-time. The formset needs to be reloaded. + // + Status = NotifyFormsetUpdate (Handle, &mRedfishPlatformConfigPrivate->PendingList); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a, failed to notify updated formset of HII handle: 0x%x\n", __FUNCTION__, Handle)); + return Status; + } + } else if (NotifyType == EFI_HII_DATABASE_NOTIFY_REMOVE_PACK) { + // + // HII resource is removed. The formset is no longer exist. + // + Status = NotifyFormsetDeleted (Handle, &mRedfishPlatformConfigPrivate->PendingList); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a, failed to notify deleted formset of HII handle: 0x%x\n", __FUNCTION__, Handle)); + return Status; + } + } + + return EFI_SUCCESS; +} + +/** + This is a EFI_HII_STRING_PROTOCOL notification event handler. + + Install HII package notification. + + @param[in] Event Event whose notification function is being invoked. + @param[in] Context Pointer to the notification function's context. + +**/ +VOID +EFIAPI +HiiStringProtocolInstalled ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + + // + // Locate HII database protocol. + // + Status = gBS->LocateProtocol ( + &gEfiHiiStringProtocolGuid, + NULL, + (VOID **)&mRedfishPlatformConfigPrivate->HiiString + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a, locate EFI_HII_STRING_PROTOCOL failure: %r\n", __FUNCTION__, Status)); + return; + } + + gBS->CloseEvent (Event); + mRedfishPlatformConfigPrivate->HiiStringNotify.ProtocolEvent = NULL; +} + +/** + This is a EFI_HII_DATABASE_PROTOCOL notification event handler. + + Install HII package notification. + + @param[in] Event Event whose notification function is being invoked. + @param[in] Context Pointer to the notification function's context. + +**/ +VOID +EFIAPI +HiiDatabaseProtocolInstalled ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + + // + // Locate HII database protocol. + // + Status = gBS->LocateProtocol ( + &gEfiHiiDatabaseProtocolGuid, + NULL, + (VOID **)&mRedfishPlatformConfigPrivate->HiiDatabase + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a, locate EFI_HII_DATABASE_PROTOCOL failure: %r\n", __FUNCTION__, Status)); + return; + } + + // + // Register package notification when new form package is installed. + // + Status = mRedfishPlatformConfigPrivate->HiiDatabase->RegisterPackageNotify ( + mRedfishPlatformConfigPrivate->HiiDatabase, + EFI_HII_PACKAGE_FORMS, + NULL, + RedfishPlatformConfigFormUpdateNotify, + EFI_HII_DATABASE_NOTIFY_NEW_PACK, + &mRedfishPlatformConfigPrivate->NotifyHandle + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a, RegisterPackageNotify for EFI_HII_DATABASE_NOTIFY_NEW_PACK failure: %r\n", __FUNCTION__, Status)); + } + + // + // Register package notification when new form package is updated. + // + Status = mRedfishPlatformConfigPrivate->HiiDatabase->RegisterPackageNotify ( + mRedfishPlatformConfigPrivate->HiiDatabase, + EFI_HII_PACKAGE_FORMS, + NULL, + RedfishPlatformConfigFormUpdateNotify, + EFI_HII_DATABASE_NOTIFY_ADD_PACK, + &mRedfishPlatformConfigPrivate->NotifyHandle + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a, RegisterPackageNotify for EFI_HII_DATABASE_NOTIFY_NEW_PACK failure: %r\n", __FUNCTION__, Status)); + } + +#if REDFISH_PLATFORM_CONFIG_DELETE_EXPIRED_FORMSET + // + // Register package notification when new form package is removed. + // + Status = mRedfishPlatformConfigPrivate->HiiDatabase->RegisterPackageNotify ( + mRedfishPlatformConfigPrivate->HiiDatabase, + EFI_HII_PACKAGE_FORMS, + NULL, + RedfishPlatformConfigFormUpdateNotify, + EFI_HII_DATABASE_NOTIFY_REMOVE_PACK, + &mRedfishPlatformConfigPrivate->NotifyHandle + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a, RegisterPackageNotify for EFI_HII_DATABASE_NOTIFY_NEW_PACK failure: %r\n", __FUNCTION__, Status)); + } +#endif + + gBS->CloseEvent (Event); + mRedfishPlatformConfigPrivate->HiiDbNotify.ProtocolEvent = NULL; + +} + +/** + This is a EFI_REGULAR_EXPRESSION_PROTOCOL notification event handler. + + @param[in] Event Event whose notification function is being invoked. + @param[in] Context Pointer to the notification function's context. + +**/ +VOID +EFIAPI +RegexProtocolInstalled ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + + // + // Locate regular expression protocol. + // + Status = gBS->LocateProtocol ( + &gEfiRegularExpressionProtocolGuid, + NULL, + (VOID **)&mRedfishPlatformConfigPrivate->RegularExpressionProtocol + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a, locate EFI_REGULAR_EXPRESSION_PROTOCOL failure: %r\n", __FUNCTION__, Status)); + return; + } + + gBS->CloseEvent (Event); + mRedfishPlatformConfigPrivate->RegexNotify.ProtocolEvent = NULL; + +} + +/** + 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 +RedfishPlatformConfigDxeUnload ( + IN EFI_HANDLE ImageHandle + ) +{ + EFI_STATUS Status; + + if (mRedfishPlatformConfigPrivate != NULL) { + Status = gBS->UninstallProtocolInterface ( + mRedfishPlatformConfigPrivate->ImageHandle, + &gEdkIIRedfishPlatformConfigProtocolGuid, + (VOID*)&mRedfishPlatformConfigPrivate->Protocol + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a, can not uninstall gEdkIIRedfishPlatformConfigProtocolGuid: %r\n", __FUNCTION__, Status)); + ASSERT (FALSE); + } + + // + // Close events + // + if (mRedfishPlatformConfigPrivate->HiiDbNotify.ProtocolEvent != NULL) { + gBS->CloseEvent (mRedfishPlatformConfigPrivate->HiiDbNotify.ProtocolEvent); + } + if (mRedfishPlatformConfigPrivate->HiiStringNotify.ProtocolEvent != NULL) { + gBS->CloseEvent (mRedfishPlatformConfigPrivate->HiiStringNotify.ProtocolEvent); + } + if (mRedfishPlatformConfigPrivate->RegexNotify.ProtocolEvent != NULL) { + gBS->CloseEvent (mRedfishPlatformConfigPrivate->RegexNotify.ProtocolEvent); + } + + // + // Unregister package notification. + // + if (mRedfishPlatformConfigPrivate->NotifyHandle != NULL) { + mRedfishPlatformConfigPrivate->HiiDatabase->UnregisterPackageNotify ( + mRedfishPlatformConfigPrivate->HiiDatabase, + mRedfishPlatformConfigPrivate->NotifyHandle + ); + } + + ReleaseFormsetList (&mRedfishPlatformConfigPrivate->FormsetList); + FreePool (mRedfishPlatformConfigPrivate); + mRedfishPlatformConfigPrivate = NULL; + } + + return EFI_SUCCESS; +} + + +/** + 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. + + @param ImageHandle The firmware allocated handle for the UEFI image. + @param SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The operation completed successfully. + @retval Others An unexpected error occurred. +**/ +EFI_STATUS +EFIAPI +RedfishPlatformConfigDxeEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + mRedfishPlatformConfigPrivate = (REDFISH_PLATFORM_CONFIG_PRIVATE *)AllocateZeroPool (sizeof (REDFISH_PLATFORM_CONFIG_PRIVATE)); + if (mRedfishPlatformConfigPrivate == NULL) { + DEBUG ((DEBUG_ERROR, "%a, can not allocate pool for REDFISH_PLATFORM_CONFIG_PRIVATE\n", __FUNCTION__)); + ASSERT (FALSE); + return EFI_OUT_OF_RESOURCES; + } + + // + // Protocol initialization + // + mRedfishPlatformConfigPrivate->ImageHandle = ImageHandle; + mRedfishPlatformConfigPrivate->Protocol.GetValue = RedfishPlatformConfigProtocolGetValue; + mRedfishPlatformConfigPrivate->Protocol.SetValue = RedfishPlatformConfigProtocolSetValue; + mRedfishPlatformConfigPrivate->Protocol.GetConfigureLang = RedfishPlatformConfigProtocolGetConfigureLang; + mRedfishPlatformConfigPrivate->Protocol.GetSupportedSchema = RedfishPlatformConfigProtocolGetSupportedSchema; + + InitializeListHead (&mRedfishPlatformConfigPrivate->FormsetList); + InitializeListHead (&mRedfishPlatformConfigPrivate->PendingList); + + Status = gBS->InstallProtocolInterface ( + &ImageHandle, + &gEdkIIRedfishPlatformConfigProtocolGuid, + EFI_NATIVE_INTERFACE, + (VOID*)&mRedfishPlatformConfigPrivate->Protocol + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a, can not install gEdkIIRedfishPlatformConfigProtocolGuid: %r\n", __FUNCTION__, Status)); + ASSERT (FALSE); + } + + // + // Install protocol notification if HII database protocol is installed. + // + mRedfishPlatformConfigPrivate->HiiDbNotify.ProtocolEvent = EfiCreateProtocolNotifyEvent ( + &gEfiHiiDatabaseProtocolGuid, + TPL_CALLBACK, + HiiDatabaseProtocolInstalled, + NULL, + &mRedfishPlatformConfigPrivate->HiiDbNotify.Registration + ); + if (mRedfishPlatformConfigPrivate->HiiDbNotify.ProtocolEvent == NULL) { + DEBUG ((DEBUG_ERROR, "%a, failed to create protocol notification for gEfiHiiDatabaseProtocolGuid\n", __FUNCTION__)); + ASSERT (FALSE); + } + + // + // Install protocol notification if HII string protocol is installed. + // + mRedfishPlatformConfigPrivate->HiiStringNotify.ProtocolEvent = EfiCreateProtocolNotifyEvent ( + &gEfiHiiStringProtocolGuid, + TPL_CALLBACK, + HiiStringProtocolInstalled, + NULL, + &mRedfishPlatformConfigPrivate->HiiStringNotify.Registration + ); + if (mRedfishPlatformConfigPrivate->HiiStringNotify.ProtocolEvent == NULL) { + DEBUG ((DEBUG_ERROR, "%a, failed to create protocol notification for gEfiHiiStringProtocolGuid\n", __FUNCTION__)); + ASSERT (FALSE); + } + + // + // Install protocol notification if regular expression protocol is installed. + // + mRedfishPlatformConfigPrivate->RegexNotify.ProtocolEvent = EfiCreateProtocolNotifyEvent ( + &gEfiRegularExpressionProtocolGuid, + TPL_CALLBACK, + RegexProtocolInstalled, + NULL, + &mRedfishPlatformConfigPrivate->RegexNotify.Registration + ); + if (mRedfishPlatformConfigPrivate->RegexNotify.ProtocolEvent == NULL) { + DEBUG ((DEBUG_ERROR, "%a, failed to create protocol notification for gEfiRegularExpressionProtocolGuid\n", __FUNCTION__)); + ASSERT (FALSE); + } + + return EFI_SUCCESS; +} diff --git a/RedfishPkg/RedfishPlatformConfigDxe/RedfishPlatformConfigImpl.c b/RedfishPkg/RedfishPlatformConfigDxe/RedfishPlatformConfigImpl.c new file mode 100644 index 00000000000..d9eab6c883e --- /dev/null +++ b/RedfishPkg/RedfishPlatformConfigDxe/RedfishPlatformConfigImpl.c @@ -0,0 +1,1240 @@ +/** @file + + The implementation of EDKII Redfidh Platform Config Protocol. + + (C) Copyright 2021 Hewlett Packard Enterprise Development LP
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ +#include "RedfishPlatformConfigDxe.h" +#include "RedfishPlatformConfigImpl.h" + +extern REDFISH_PLATFORM_CONFIG_PRIVATE *mRedfishPlatformConfigPrivate; + +/** + Debug dump HII string + + @param[in] HiiHandle HII handle instance + @param[in] StringId HII string to dump + + @retval EFI_SUCCESS Dump HII string successfully + @retval Others Errors occur + +**/ +EFI_STATUS +DumpHiiString ( + IN EFI_HII_HANDLE HiiHandle, + IN EFI_STRING_ID StringId + ) +{ + EFI_STRING String; + + if (HiiHandle == NULL || StringId == 0) { + DEBUG ((DEBUG_INFO, "???")); + return EFI_INVALID_PARAMETER; + } + + String = HiiGetString (HiiHandle, StringId, NULL); + if (String == NULL) { + return EFI_NOT_FOUND; + } + + DEBUG ((DEBUG_INFO, "%s", String)); + FreePool (String); + + return EFI_SUCCESS; +} + +/** + Debug dump HII form-set data + + @param[in] FormsetPrivate HII form-set private instance. + + @retval EFI_SUCCESS Dump form-set successfully + @retval Others Errors occur + +**/ +EFI_STATUS +DumpFormset ( + IN REDFISH_PLATFORM_CONFIG_FORM_SET_PRIVATE *FormsetPrivate + ) +{ + LIST_ENTRY *HiiFormLink; + LIST_ENTRY *HiiNextFormLink; + REDFISH_PLATFORM_CONFIG_FORM_PRIVATE *HiiFormPrivate; + LIST_ENTRY *HiiStatementLink; + LIST_ENTRY *HiiNextStatementLink; + REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE *HiiStatementPrivate; + UINTN Index; + + if (FormsetPrivate == NULL) { + return EFI_INVALID_PARAMETER; + } + + Index = 0; + HiiFormLink = GetFirstNode (&FormsetPrivate->HiiFormList); + while (!IsNull (&FormsetPrivate->HiiFormList, HiiFormLink)) { + HiiFormPrivate = REDFISH_PLATFORM_CONFIG_FORM_FROM_LINK (HiiFormLink); + HiiNextFormLink = GetNextNode (&FormsetPrivate->HiiFormList, HiiFormLink); + + DEBUG ((DEBUG_INFO, " [%d] form: %d title: ", ++Index, HiiFormPrivate->Id)); + DumpHiiString (FormsetPrivate->HiiHandle, HiiFormPrivate->Title); + DEBUG ((DEBUG_INFO, "\n")); + + HiiStatementLink = GetFirstNode (&HiiFormPrivate->StatementList); + while (!IsNull (&HiiFormPrivate->StatementList, HiiStatementLink)) { + HiiStatementPrivate = REDFISH_PLATFORM_CONFIG_STATEMENT_FROM_LINK (HiiStatementLink); + HiiNextStatementLink = GetNextNode (&HiiFormPrivate->StatementList, HiiStatementLink); + + DEBUG ((DEBUG_INFO, " QID: 0x%x Prompt: ", HiiStatementPrivate->QuestionId)); + DumpHiiString (FormsetPrivate->HiiHandle, HiiStatementPrivate->Description); + DEBUG ((DEBUG_INFO, "\n")); + + HiiStatementLink = HiiNextStatementLink; + } + + HiiFormLink = HiiNextFormLink; + } + + return EFI_SUCCESS; +} + +/** + Debug dump HII form-set list + + @param[in] FormsetList Form-set list instance + + @retval EFI_SUCCESS Dump list successfully + @retval Others Errors occur + +**/ +EFI_STATUS +DumpFormsetList ( + IN LIST_ENTRY *FormsetList + ) +{ + LIST_ENTRY *HiiFormsetLink; + LIST_ENTRY *HiiFormsetNextLink; + REDFISH_PLATFORM_CONFIG_FORM_SET_PRIVATE *HiiFormsetPrivate; + UINTN Index; + + if (FormsetList == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (IsListEmpty (FormsetList)) { + DEBUG ((DEBUG_INFO, "%a, Empty formset list\n", __FUNCTION__)); + return EFI_SUCCESS; + } + + Index = 0; + HiiFormsetLink = GetFirstNode (FormsetList); + while (!IsNull (FormsetList, HiiFormsetLink)) { + HiiFormsetNextLink = GetNextNode (FormsetList, HiiFormsetLink); + HiiFormsetPrivate = REDFISH_PLATFORM_CONFIG_FORMSET_FROM_LINK (HiiFormsetLink); + + DEBUG ((DEBUG_INFO, "[%d] HII Handle: 0x%x formset: %g at %s\n", ++Index, HiiFormsetPrivate->HiiHandle, &HiiFormsetPrivate->Guid, HiiFormsetPrivate->DevicePathStr)); + DumpFormset (HiiFormsetPrivate); + + HiiFormsetLink = HiiFormsetNextLink; + } + + return EFI_SUCCESS; +} + +/** + Retrieves a string from a string package in a English language. The + returned string is allocated using AllocatePool(). The caller is responsible + for freeing the allocated buffer using FreePool(). + + If HiiHandle is NULL, then ASSERT(). + If StringId is 0, then ASSET. + + @param[in] HiiStringProtocol EFI_HII_STRING_PROTOCOL instance. + @param[in] HiiHandle A handle that was previously registered in the HII Database. + @param[in] StringId The identifier of the string to retrieved from the string + package associated with HiiHandle. + + @retval NULL The string specified by StringId is not present in the string package. + @retval Other The string was returned. + +**/ +EFI_STRING +HiiGetRedfishString ( + IN EFI_HII_HANDLE HiiHandle, + IN CHAR8 *Language, + IN EFI_STRING_ID StringId + ) +{ + EFI_STATUS Status; + UINTN StringSize; + CHAR16 TempString; + EFI_STRING String; + + if (mRedfishPlatformConfigPrivate->HiiString == NULL || HiiHandle == NULL || StringId == 0 || IS_EMPTY_STRING (Language)) { + ASSERT (FALSE); + return NULL; + } + + // + // Retrieve the size of the string in the string package for the BestLanguage + // + StringSize = 0; + Status = mRedfishPlatformConfigPrivate->HiiString->GetString ( + mRedfishPlatformConfigPrivate->HiiString, + Language, + HiiHandle, + StringId, + &TempString, + &StringSize, + NULL + ); + // + // If GetString() returns EFI_SUCCESS for a zero size, + // then there are no supported languages registered for HiiHandle. If GetString() + // returns an error other than EFI_BUFFER_TOO_SMALL, then HiiHandle is not present + // in the HII Database + // + if (Status != EFI_BUFFER_TOO_SMALL) { + return NULL; + } + + // + // Allocate a buffer for the return string + // + String = AllocateZeroPool (StringSize); + if (String == NULL) { + return NULL; + } + + // + // Retrieve the string from the string package + // + Status = mRedfishPlatformConfigPrivate->HiiString->GetString ( + mRedfishPlatformConfigPrivate->HiiString, + Language, + HiiHandle, + StringId, + String, + &StringSize, + NULL + ); + if (EFI_ERROR (Status)) { + // + // Free the buffer and return NULL if the supported languages can not be retrieved. + // + FreePool (String); + String = NULL; + } + + // + // Return the Null-terminated Unicode string + // + return String; +} + +/** + Get string from HII database in English language. + + @param[in] HiiHandle A handle that was previously registered in the HII Database. + @param[in] StringId The identifier of the string to retrieved from the string + package associated with HiiHandle. + + @retval NULL The string specified by StringId is not present in the string package. + @retval Other The string was returned. + +**/ +EFI_STRING +HiiGetEnglishString ( + IN EFI_HII_HANDLE HiiHandle, + IN EFI_STRING_ID StringId + ) +{ + return HiiGetRedfishString (HiiHandle, ENGLISH_LANGUAGE_CODE, StringId); +} + +/** + Check and see if this is supported schema or not. + + @param[in] SupportedSchema The list of supported schema. + @param[in] Schema Schema string to be checked. + + @retval BOOLEAN TRUE if this is supported schema. FALSE otherwise. + +**/ +BOOLEAN +CheckSupportedSchema ( + IN REDFISH_PLATFORM_CONFIG_SCHEMA *SupportedSchema, + IN CHAR8 *Schema + ) +{ + UINTN Index; + + if (SupportedSchema == NULL || IS_EMPTY_STRING (Schema)) { + return FALSE; + } + + if (SupportedSchema->Count == 0) { + return FALSE; + } + + for (Index = 0; Index < SupportedSchema->Count; Index++) { + if (AsciiStrCmp (SupportedSchema->SchemaList[Index], Schema) == 0) { + return TRUE; + } + } + + return FALSE; +} + +/** + Get the list of supported schema from the given HII handle. + + @param[in] HiiHandle HII handle instance. + @param[out] SupportedSchema Supported schema on this HII handle. + + @retval EFI_SUCCESS Schema list is returned. + @retval EFI_INVALID_PARAMETER HiiHandle is NULL or SupportedSchema is NULL. + @retval EFI_NOT_FOUND No supported schema found. + @retval EFI_OUT_OF_RESOURCES System is out of memory. + +**/ +EFI_STATUS +GetSupportedSchema ( + IN EFI_HII_HANDLE HiiHandle, + OUT REDFISH_PLATFORM_CONFIG_SCHEMA *SupportedSchema + ) +{ + CHAR8 *SupportedLanguages; + UINTN Index; + UINTN LangIndex; + UINTN Count; + UINTN StrSize; + UINTN ListIndex; + + if (HiiHandle == NULL || SupportedSchema == NULL) { + return EFI_INVALID_PARAMETER; + } + + SupportedSchema->Count = 0; + + SupportedLanguages = HiiGetSupportedLanguages (HiiHandle); + if (SupportedLanguages == NULL) { + return EFI_NOT_FOUND; + } + + Index = 0; + LangIndex = 0; + Count = 0; + while (TRUE) { + if (SupportedLanguages[Index] == ';' || SupportedLanguages[Index] == '\0') { + if (AsciiStrnCmp (&SupportedLanguages[LangIndex], X_UEFI_SCHEMA_PREFIX, AsciiStrLen (X_UEFI_SCHEMA_PREFIX)) == 0) { + ++Count; + } + LangIndex = Index + 1; + } + + if (SupportedLanguages[Index] == '\0') { + break; + } + + ++Index; + } + + if (Count == 0) { + return EFI_NOT_FOUND; + } + + SupportedSchema->Count = Count; + SupportedSchema->SchemaList = AllocatePool (sizeof (CHAR8 *) * Count); + if (SupportedSchema->SchemaList == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Index = 0; + LangIndex = 0; + ListIndex = 0; + while (TRUE) { + + if (SupportedLanguages[Index] == ';' || SupportedLanguages[Index] == '\0') { + if (AsciiStrnCmp (&SupportedLanguages[LangIndex], X_UEFI_SCHEMA_PREFIX, AsciiStrLen (X_UEFI_SCHEMA_PREFIX)) == 0) { + StrSize = Index - LangIndex; + SupportedSchema->SchemaList[ListIndex] = AllocateCopyPool ((StrSize + 1), &SupportedLanguages[LangIndex]); + SupportedSchema->SchemaList[ListIndex][StrSize] = '\0'; + ++ListIndex; + } + + LangIndex = Index + 1; + } + + if (SupportedLanguages[Index] == '\0') { + break; + } + + ++Index; + } + + return EFI_SUCCESS; +} + +/** + Search and find statement private instance by given regular expression patthern + which describes the Configure Language. + + @param[in] RegularExpressionProtocol Regular express protocol. + @param[in] FormsetList Form-set list to search. + @param[in] Schema Schema to be matched. + @param[in] Pattern Regular expression pattern. + @param[out] StatementList Statement list that match above pattern. + + @retval EFI_SUCCESS Statement list is returned. + @retval EFI_INVALID_PARAMETER Input parameter is NULL. + @retval EFI_NOT_READY Regular express protocol is NULL. + @retval EFI_NOT_FOUND No statement is found. + @retval EFI_OUT_OF_RESOURCES System is out of memory. + +**/ +EFI_STATUS +GetStatementPrivateByConfigureLangRegex ( + IN EFI_REGULAR_EXPRESSION_PROTOCOL *RegularExpressionProtocol, + IN LIST_ENTRY *FormsetList, + IN CHAR8 *Schema, + IN EFI_STRING Pattern, + OUT REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE_LIST *StatementList + ) +{ + LIST_ENTRY *HiiFormsetLink; + LIST_ENTRY *HiiFormsetNextLink; + REDFISH_PLATFORM_CONFIG_FORM_SET_PRIVATE *HiiFormsetPrivate; + LIST_ENTRY *HiiFormLink; + LIST_ENTRY *HiiNextFormLink; + REDFISH_PLATFORM_CONFIG_FORM_PRIVATE *HiiFormPrivate; + LIST_ENTRY *HiiStatementLink; + LIST_ENTRY *HiiNextStatementLink; + REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE *HiiStatementPrivate; + EFI_STRING TmpString; + UINTN CaptureCount; + BOOLEAN IsMatch; + EFI_STATUS Status; + REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE_REF *StatementRef; + + if (FormsetList == NULL || IS_EMPTY_STRING (Schema) || IS_EMPTY_STRING (Pattern) || StatementList == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (RegularExpressionProtocol == NULL) { + return EFI_NOT_READY; + } + + StatementList->Count = 0; + InitializeListHead (&StatementList->StatementList); + + if (IsListEmpty (FormsetList)) { + return EFI_NOT_FOUND; + } + + HiiFormsetLink = GetFirstNode (FormsetList); + while (!IsNull (FormsetList, HiiFormsetLink)) { + HiiFormsetNextLink = GetNextNode (FormsetList, HiiFormsetLink); + HiiFormsetPrivate = REDFISH_PLATFORM_CONFIG_FORMSET_FROM_LINK (HiiFormsetLink); + + // + // Performance check. + // If there is no desired Redfish schema found, skip this formset. + // + if (!CheckSupportedSchema (&HiiFormsetPrivate->SupportedSchema, Schema)) { + HiiFormsetLink = HiiFormsetNextLink; + continue; + } + + HiiFormLink = GetFirstNode (&HiiFormsetPrivate->HiiFormList); + while (!IsNull (&HiiFormsetPrivate->HiiFormList, HiiFormLink)) { + HiiNextFormLink = GetNextNode (&HiiFormsetPrivate->HiiFormList, HiiFormLink); + HiiFormPrivate = REDFISH_PLATFORM_CONFIG_FORM_FROM_LINK (HiiFormLink); + + HiiStatementLink =GetFirstNode (&HiiFormPrivate->StatementList); + while (!IsNull (&HiiFormPrivate->StatementList, HiiStatementLink)) { + HiiNextStatementLink = GetNextNode (&HiiFormPrivate->StatementList, HiiStatementLink); + HiiStatementPrivate = REDFISH_PLATFORM_CONFIG_STATEMENT_FROM_LINK (HiiStatementLink); + + if (HiiStatementPrivate->Description != 0) { + TmpString = HiiGetRedfishString (HiiFormsetPrivate->HiiHandle, Schema, HiiStatementPrivate->Description); + if (TmpString != NULL) { + Status = RegularExpressionProtocol->MatchString ( + RegularExpressionProtocol, + TmpString, + Pattern, + &gEfiRegexSyntaxTypePerlGuid, + &IsMatch, + NULL, + &CaptureCount + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a, MatchString \"%s\" failed: %r\n", __FUNCTION__, Pattern, Status)); + ASSERT (FALSE); + return Status; + } + + // + // Found + // + if (IsMatch) { + StatementRef = AllocateZeroPool (sizeof (REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE_REF)); + if (StatementRef == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + StatementRef->Statement = HiiStatementPrivate; + InsertTailList (&StatementList->StatementList, &StatementRef->Link); + ++StatementList->Count; + } + + FreePool (TmpString); + } + } + + HiiStatementLink = HiiNextStatementLink; + } + + HiiFormLink = HiiNextFormLink; + } + + HiiFormsetLink = HiiFormsetNextLink; + } + + return EFI_SUCCESS; +} + +/** + Get statement private instance by the given configure language. + + @param[in] FormsetList Form-set list to search. + @param[in] Schema Schema to be matched. + @param[in] ConfigureLang Configure language. + + @retval REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE * Pointer to statement private instance. + +**/ +REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE * +GetStatementPrivateByConfigureLang ( + IN LIST_ENTRY *FormsetList, + IN CHAR8 *Schema, + IN EFI_STRING ConfigureLang + ) +{ + LIST_ENTRY *HiiFormsetLink; + LIST_ENTRY *HiiFormsetNextLink; + REDFISH_PLATFORM_CONFIG_FORM_SET_PRIVATE *HiiFormsetPrivate; + LIST_ENTRY *HiiFormLink; + LIST_ENTRY *HiiNextFormLink; + REDFISH_PLATFORM_CONFIG_FORM_PRIVATE *HiiFormPrivate; + LIST_ENTRY *HiiStatementLink; + LIST_ENTRY *HiiNextStatementLink; + REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE *HiiStatementPrivate; + EFI_STRING TmpString; + + if (FormsetList == NULL || IS_EMPTY_STRING (Schema) || IS_EMPTY_STRING (ConfigureLang)) { + return NULL; + } + + if (IsListEmpty (FormsetList)) { + return NULL; + } + + HiiFormsetLink = GetFirstNode (FormsetList); + while (!IsNull (FormsetList, HiiFormsetLink)) { + HiiFormsetNextLink = GetNextNode (FormsetList, HiiFormsetLink); + HiiFormsetPrivate = REDFISH_PLATFORM_CONFIG_FORMSET_FROM_LINK (HiiFormsetLink); + + // + // Performance check. + // If there is no desired Redfish schema found, skip this formset. + // + if (!CheckSupportedSchema (&HiiFormsetPrivate->SupportedSchema, Schema)) { + HiiFormsetLink = HiiFormsetNextLink; + continue; + } + + HiiFormLink = GetFirstNode (&HiiFormsetPrivate->HiiFormList); + while (!IsNull (&HiiFormsetPrivate->HiiFormList, HiiFormLink)) { + HiiNextFormLink = GetNextNode (&HiiFormsetPrivate->HiiFormList, HiiFormLink); + HiiFormPrivate = REDFISH_PLATFORM_CONFIG_FORM_FROM_LINK (HiiFormLink); + + HiiStatementLink =GetFirstNode (&HiiFormPrivate->StatementList); + while (!IsNull (&HiiFormPrivate->StatementList, HiiStatementLink)) { + HiiNextStatementLink = GetNextNode (&HiiFormPrivate->StatementList, HiiStatementLink); + HiiStatementPrivate = REDFISH_PLATFORM_CONFIG_STATEMENT_FROM_LINK (HiiStatementLink); + + DEBUG_CODE ( + STATIC UINTN Index = 0; + DEBUG ((DEBUG_INFO, "%a, [%d] search %s in QID: 0x%x form: 0x%x formset: %g\n", __FUNCTION__, ++Index, ConfigureLang, HiiStatementPrivate->QuestionId, HiiFormPrivate->Id, &HiiFormsetPrivate->Guid)); + ); + + if (HiiStatementPrivate->Description != 0) { + TmpString = HiiGetRedfishString (HiiFormsetPrivate->HiiHandle, Schema, HiiStatementPrivate->Description); + if (TmpString != NULL) { + if (StrCmp (TmpString, ConfigureLang) == 0) { + FreePool (TmpString); + return HiiStatementPrivate; + } + + FreePool (TmpString); + } + } + + HiiStatementLink = HiiNextStatementLink; + } + + HiiFormLink = HiiNextFormLink; + } + + HiiFormsetLink = HiiFormsetNextLink; + } + + return NULL; +} + +/** + Get form-set private instance by the given HII handle. + + @param[in] HiiHandle HII handle instance. + @param[in] FormsetList Form-set list to search. + + @retval REDFISH_PLATFORM_CONFIG_FORM_SET_PRIVATE * Pointer to form-set private instance. + +**/ +REDFISH_PLATFORM_CONFIG_FORM_SET_PRIVATE * +GetFormsetPrivateByHiiHandle ( + IN EFI_HII_HANDLE HiiHandle, + IN LIST_ENTRY *FormsetList + ) +{ + LIST_ENTRY *HiiFormsetLink; + LIST_ENTRY *HiiFormsetNextLink; + REDFISH_PLATFORM_CONFIG_FORM_SET_PRIVATE *HiiFormsetPrivate; + + if (HiiHandle == NULL || FormsetList == NULL) { + return NULL; + } + + if (IsListEmpty (FormsetList)) { + return NULL; + } + + HiiFormsetLink = GetFirstNode (FormsetList); + while (!IsNull (FormsetList, HiiFormsetLink)) { + HiiFormsetNextLink = GetNextNode (FormsetList, HiiFormsetLink); + HiiFormsetPrivate = REDFISH_PLATFORM_CONFIG_FORMSET_FROM_LINK (HiiFormsetLink); + + if (HiiFormsetPrivate->HiiHandle == HiiHandle) { + return HiiFormsetPrivate; + } + + HiiFormsetLink = HiiFormsetNextLink; + } + + return NULL; +} + +/** + Release formset and all the forms and statements that belong to this formset. + + @param[in] FormsetPrivate Pointer to HP_HII_FORM_SET_PRIVATE + + @retval EFI_STATUS + +**/ +EFI_STATUS +ReleaseFormset ( + IN REDFISH_PLATFORM_CONFIG_FORM_SET_PRIVATE *FormsetPrivate + ) +{ + LIST_ENTRY *HiiFormLink; + LIST_ENTRY *HiiNextFormLink; + REDFISH_PLATFORM_CONFIG_FORM_PRIVATE *HiiFormPrivate; + LIST_ENTRY *HiiStatementLink; + LIST_ENTRY *HiiNextStatementLink; + REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE *HiiStatementPrivate; + UINTN Index; + + if (FormsetPrivate == NULL) { + return EFI_INVALID_PARAMETER; + } + + HiiFormLink = GetFirstNode (&FormsetPrivate->HiiFormList); + while (!IsNull (&FormsetPrivate->HiiFormList, HiiFormLink)) { + HiiFormPrivate = REDFISH_PLATFORM_CONFIG_FORM_FROM_LINK (HiiFormLink); + HiiNextFormLink = GetNextNode (&FormsetPrivate->HiiFormList, HiiFormLink); + + HiiStatementLink = GetFirstNode (&HiiFormPrivate->StatementList); + while (!IsNull (&HiiFormPrivate->StatementList, HiiStatementLink)) { + HiiStatementPrivate = REDFISH_PLATFORM_CONFIG_STATEMENT_FROM_LINK (HiiStatementLink); + HiiNextStatementLink = GetNextNode (&HiiFormPrivate->StatementList, HiiStatementLink); + + // + // HiiStatementPrivate->HiiStatement will be released in DestroyFormSet(). + // + + if (HiiStatementPrivate->DesStringCache != NULL) { + FreePool (HiiStatementPrivate->DesStringCache); + HiiStatementPrivate->DesStringCache = NULL; + } + + RemoveEntryList (&HiiStatementPrivate->Link); + FreePool (HiiStatementPrivate); + HiiStatementLink = HiiNextStatementLink; + } + + // + // HiiStatementPrivate->HiiForm will be released in DestroyFormSet(). + // + + RemoveEntryList (&HiiFormPrivate->Link); + FreePool (HiiFormPrivate); + HiiFormLink = HiiNextFormLink; + } + + if (FormsetPrivate->HiiFormSet != NULL) { + DestroyFormSet (FormsetPrivate->HiiFormSet); + FormsetPrivate->HiiFormSet = NULL; + } + + FreePool (FormsetPrivate->DevicePathStr); + + // + // Release schema list + // + if (FormsetPrivate->SupportedSchema.SchemaList != NULL) { + for (Index = 0; Index < FormsetPrivate->SupportedSchema.Count; Index++) { + FreePool (FormsetPrivate->SupportedSchema.SchemaList[Index]); + } + + FreePool (FormsetPrivate->SupportedSchema.SchemaList); + FormsetPrivate->SupportedSchema.SchemaList = NULL; + FormsetPrivate->SupportedSchema.Count = 0; + } + + return EFI_SUCCESS; +} + +/** + Create new form-set instance. + + @retval REDFISH_PLATFORM_CONFIG_FORM_SET_PRIVATE * Pointer to newly created form-set private instance. + +**/ +REDFISH_PLATFORM_CONFIG_FORM_SET_PRIVATE * +NewFormsetPrivate ( + VOID + ) +{ + REDFISH_PLATFORM_CONFIG_FORM_SET_PRIVATE *NewFormsetPrivate; + + NewFormsetPrivate = AllocateZeroPool (sizeof (REDFISH_PLATFORM_CONFIG_FORM_SET_PRIVATE)); + if (NewFormsetPrivate == NULL) { + return NULL; + } + + // + // Initial newly created formset private data. + // + InitializeListHead (&NewFormsetPrivate->HiiFormList); + + return NewFormsetPrivate; +} + +/** + Load the HII formset from the given HII handle. + + @param[in] HiiHandle Target HII handle to load. + @param[out] FormsetPrivate The formset private data. + + @retval EFI_STATUS + +**/ +EFI_STATUS +LoadFormset ( + IN EFI_HII_HANDLE HiiHandle, + OUT REDFISH_PLATFORM_CONFIG_FORM_SET_PRIVATE *FormsetPrivate + ) +{ + EFI_STATUS Status; + HII_FORMSET *HiiFormSet; + HII_FORM *HiiForm; + LIST_ENTRY *HiiFormLink; + REDFISH_PLATFORM_CONFIG_FORM_PRIVATE *HiiFormPrivate; + HII_STATEMENT *HiiStatement; + LIST_ENTRY *HiiStatementLink; + REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE *HiiStatementPrivate; + EFI_GUID ZeroGuid; + + if (HiiHandle == NULL || FormsetPrivate == NULL) { + return EFI_INVALID_PARAMETER; + } + + + HiiFormSet = AllocateZeroPool (sizeof (HII_FORMSET)); + if (HiiFormSet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Find HII formset by the given HII handle. + // + ZeroMem (&ZeroGuid, sizeof (ZeroGuid)); + Status = CreateFormSetFromHiiHandle (HiiHandle, &ZeroGuid, HiiFormSet); + if (EFI_ERROR (Status) || IsListEmpty (&HiiFormSet->FormListHead)) { + Status = EFI_NOT_FOUND; + goto ErrorExit; + } + + // + // Initialize formset + // + InitializeFormSet (HiiFormSet); + + // + // Initialize formset private data. + // + FormsetPrivate->HiiFormSet = HiiFormSet; + FormsetPrivate->HiiHandle = HiiHandle; + CopyGuid (&FormsetPrivate->Guid, &HiiFormSet->Guid); + FormsetPrivate->DevicePathStr = ConvertDevicePathToText (HiiFormSet->DevicePath, FALSE, FALSE); + Status = GetSupportedSchema (FormsetPrivate->HiiHandle, &FormsetPrivate->SupportedSchema); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_WARN, "%a, No schema from HII handle: 0x%x found: %r\n", __FUNCTION__, FormsetPrivate->HiiHandle, Status)); + } + + HiiFormLink = GetFirstNode (&HiiFormSet->FormListHead); + while (!IsNull (&HiiFormSet->FormListHead, HiiFormLink)) { + HiiForm = HII_FORM_FROM_LINK (HiiFormLink); + + HiiFormPrivate = AllocateZeroPool (sizeof (REDFISH_PLATFORM_CONFIG_FORM_PRIVATE)); + if (HiiFormPrivate == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ErrorExit; + } + + // + // Initialize form private data. + // + HiiFormPrivate->HiiForm = HiiForm; + HiiFormPrivate->Id = HiiForm->FormId; + HiiFormPrivate->Title = HiiForm->FormTitle; + HiiFormPrivate->ParentFormset = FormsetPrivate; + InitializeListHead (&HiiFormPrivate->StatementList); + + HiiStatementLink = GetFirstNode (&HiiForm->StatementListHead); + while (!IsNull (&HiiForm->StatementListHead, HiiStatementLink)) { + HiiStatement = HII_STATEMENT_FROM_LINK (HiiStatementLink); + + HiiStatementPrivate = AllocateZeroPool (sizeof (REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE)); + if (HiiStatementPrivate == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ErrorExit; + } + // + // Initialize statement private data. + // + HiiStatementPrivate->HiiStatement = HiiStatement; + HiiStatementPrivate->QuestionId = HiiStatement->QuestionId; + HiiStatementPrivate->Description = HiiStatement->Prompt; + HiiStatementPrivate->ParentForm = HiiFormPrivate; + + // + // Attach to statement list. + // + InsertTailList (&HiiFormPrivate->StatementList, &HiiStatementPrivate->Link); + HiiStatementLink = GetNextNode (&HiiForm->StatementListHead, HiiStatementLink); + } + // + // Attach to form list. + // + InsertTailList (&FormsetPrivate->HiiFormList, &HiiFormPrivate->Link); + HiiFormLink = GetNextNode (&HiiFormSet->FormListHead, HiiFormLink); + } + + return EFI_SUCCESS; + +ErrorExit: + + // + // Release HiiFormSet if HiiFormSet is not linked to FormsetPrivate yet. + // + if (HiiFormSet != NULL && FormsetPrivate->HiiFormSet != HiiFormSet) { + DestroyFormSet (HiiFormSet); + } + + // + // Release resource when error happens. + // + ReleaseFormset (FormsetPrivate); + + return Status; +} + +/** + Release formset list and all the forms that belong to this formset. + + @param[in] FormsetList Pointer to formst list that needs to be + released. + + @retval EFI_STATUS + +**/ +EFI_STATUS +LoadFormsetList ( + IN EFI_HII_HANDLE *HiiHandle, + OUT LIST_ENTRY *FormsetList + ) +{ + EFI_STATUS Status; + REDFISH_PLATFORM_CONFIG_FORM_SET_PRIVATE *FormsetPrivate; + + if (HiiHandle == NULL || FormsetList == NULL) { + return EFI_INVALID_PARAMETER; + } + + FormsetPrivate = GetFormsetPrivateByHiiHandle (HiiHandle, FormsetList); + if (FormsetPrivate != NULL) { + return EFI_ALREADY_STARTED; + } + + FormsetPrivate = NewFormsetPrivate (); + if (FormsetPrivate == NULL) { + DEBUG ((DEBUG_ERROR, "%a, out of resource\n", __FUNCTION__)); + return EFI_OUT_OF_RESOURCES; + } + + // + // Load formset on the given HII handle. + // + Status = LoadFormset (HiiHandle, FormsetPrivate); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a, failed to load formset: %r\n", __FUNCTION__, Status)); + FreePool (FormsetPrivate); + return Status; + } + + // + // Attach to cache list. + // + InsertTailList (FormsetList, &FormsetPrivate->Link); + + DEBUG_CODE ( + DumpFormsetList (FormsetList); + ); + + return EFI_SUCCESS; +} + +/** + Release formset list and all the forms that belong to this formset. + + @param[in] FormsetList Pointer to formst list that needs to be + released. + + @retval EFI_STATUS + +**/ +EFI_STATUS +ReleaseFormsetList ( + IN LIST_ENTRY *FormsetList + ) +{ + LIST_ENTRY *HiiFormsetLink; + LIST_ENTRY *HiiFormsetNextLink; + REDFISH_PLATFORM_CONFIG_FORM_SET_PRIVATE *HiiFormsetPrivate; + + if (FormsetList == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (IsListEmpty (FormsetList)) { + return EFI_SUCCESS; + } + + HiiFormsetLink = GetFirstNode (FormsetList); + while (!IsNull (FormsetList, HiiFormsetLink)) { + HiiFormsetNextLink = GetNextNode (FormsetList, HiiFormsetLink); + HiiFormsetPrivate = REDFISH_PLATFORM_CONFIG_FORMSET_FROM_LINK (HiiFormsetLink); + + // + // Detach from list. + // + RemoveEntryList (&HiiFormsetPrivate->Link); + ReleaseFormset (HiiFormsetPrivate); + FreePool (HiiFormsetPrivate); + HiiFormsetLink = HiiFormsetNextLink; + } + + return EFI_SUCCESS; +} + +/** + Get all pending list. + + @param[in] HiiHandle HII handle instance. + @param[in] PendingList Pending list to keep pending data. + + @retval REDFISH_PLATFORM_CONFIG_PENDING_LIST * Pointer to pending list data. + +**/ +REDFISH_PLATFORM_CONFIG_PENDING_LIST * +GetPendingList ( + IN EFI_HII_HANDLE *HiiHandle, + IN LIST_ENTRY *PendingList + ) +{ + LIST_ENTRY *PendingListLink; + REDFISH_PLATFORM_CONFIG_PENDING_LIST *Target; + + if (HiiHandle == NULL || PendingList == NULL) { + return NULL; + } + + if (IsListEmpty (PendingList)) { + return NULL; + } + + PendingListLink = GetFirstNode (PendingList); + while (!IsNull (PendingList, PendingListLink)) { + Target = REDFISH_PLATFORM_CONFIG_PENDING_LIST_FROM_LINK (PendingListLink); + + if (Target->HiiHandle == HiiHandle) { + return Target; + } + + PendingListLink = GetNextNode (PendingList, PendingListLink); + } + + return NULL; +} + +/** + When HII database is updated. Keep updated HII handle into pending list so + we can process them later. + + @param[in] HiiHandle HII handle instance. + @param[in] PendingList Pending list to keep HII handle which is recently updated. + + @retval EFI_SUCCESS HII handle is saved in pending list. + @retval EFI_INVALID_PARAMETER HiiHnalde is NULL or PendingList is NULL. + @retval EFI_OUT_OF_RESOURCES System is out of memory. + +**/ +EFI_STATUS +NotifyFormsetUpdate ( + IN EFI_HII_HANDLE *HiiHandle, + IN LIST_ENTRY *PendingList + ) +{ + REDFISH_PLATFORM_CONFIG_PENDING_LIST *TargetPendingList; + + if (HiiHandle == NULL || PendingList == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Check and see if this HII handle is processed already. + // + TargetPendingList = GetPendingList (HiiHandle, PendingList); + if (TargetPendingList != NULL) { + TargetPendingList->IsDeleted = FALSE; + DEBUG_CODE ( + DEBUG ((DEBUG_INFO, "%a, HII handle: 0x%x is updated\n", __FUNCTION__, HiiHandle)); + ); + return EFI_SUCCESS; + } + + TargetPendingList= AllocateZeroPool (sizeof (REDFISH_PLATFORM_CONFIG_PENDING_LIST)); + if (TargetPendingList == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + TargetPendingList->HiiHandle = HiiHandle; + TargetPendingList->IsDeleted = FALSE; + + InsertTailList (PendingList, &TargetPendingList->Link); + + DEBUG_CODE ( + DEBUG ((DEBUG_INFO, "%a, HII handle: 0x%x is created\n", __FUNCTION__, HiiHandle)); + ); + + return EFI_SUCCESS; +} + +/** + When HII database is updated and form-set is deleted. Keep deleted HII handle into pending list so + we can process them later. + + @param[in] HiiHandle HII handle instance. + @param[in] PendingList Pending list to keep HII handle which is recently updated. + + @retval EFI_SUCCESS HII handle is saved in pending list. + @retval EFI_INVALID_PARAMETER HiiHnalde is NULL or PendingList is NULL. + @retval EFI_OUT_OF_RESOURCES System is out of memory. + +**/ +EFI_STATUS +NotifyFormsetDeleted ( + IN EFI_HII_HANDLE *HiiHandle, + IN LIST_ENTRY *PendingList + ) +{ + REDFISH_PLATFORM_CONFIG_PENDING_LIST *TargetPendingList; + + if (HiiHandle == NULL || PendingList == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Check and see if this HII handle is processed already. + // + TargetPendingList = GetPendingList (HiiHandle, PendingList); + if (TargetPendingList != NULL) { + TargetPendingList->IsDeleted = TRUE; + DEBUG_CODE ( + DEBUG ((DEBUG_INFO, "%a, HII handle: 0x%x is updated and deleted\n", __FUNCTION__, HiiHandle)); + ); + return EFI_SUCCESS; + } + + TargetPendingList= AllocateZeroPool (sizeof (REDFISH_PLATFORM_CONFIG_PENDING_LIST)); + if (TargetPendingList == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + TargetPendingList->HiiHandle = HiiHandle; + TargetPendingList->IsDeleted = TRUE; + + InsertTailList (PendingList, &TargetPendingList->Link); + + DEBUG_CODE ( + DEBUG ((DEBUG_INFO, "%a, HII handle: 0x%x is deleted\n", __FUNCTION__, HiiHandle)); + ); + + return EFI_SUCCESS; +} + +/** + There are HII database update and we need to process them accordingly so that we + won't use stale data. This function will parse updated HII handle again in order + to get updated data-set. + + @param[in] FormsetList List to keep HII form-set. + @param[in] PendingList List to keep HII handle that is updated. + + @retval EFI_SUCCESS HII handle is saved in pending list. + @retval EFI_INVALID_PARAMETER FormsetList is NULL or PendingList is NULL. + +**/ +EFI_STATUS +ProcessPendingList ( + IN LIST_ENTRY *FormsetList, + IN LIST_ENTRY *PendingList + ) +{ + LIST_ENTRY *PendingListLink; + LIST_ENTRY *PendingListNextLink; + REDFISH_PLATFORM_CONFIG_PENDING_LIST *Target; + REDFISH_PLATFORM_CONFIG_FORM_SET_PRIVATE *FormsetPrivate; + EFI_STATUS Status; + + + if (FormsetList == NULL || PendingList == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (IsListEmpty (PendingList)) { + return EFI_SUCCESS; + } + + PendingListLink = GetFirstNode (PendingList); + while (!IsNull (PendingList, PendingListLink)) { + PendingListNextLink = GetNextNode (PendingList, PendingListLink); + Target = REDFISH_PLATFORM_CONFIG_PENDING_LIST_FROM_LINK (PendingListLink); + + if (Target->IsDeleted) { + // + // The HII resource on this HII handle is removed. Release the formset. + // + FormsetPrivate = GetFormsetPrivateByHiiHandle (Target->HiiHandle, FormsetList); + if (FormsetPrivate != NULL) { + DEBUG ((DEBUG_INFO, "%a, formset: %g is removed because driver release HII resource it already\n", __FUNCTION__, FormsetPrivate->Guid)); + RemoveEntryList (&FormsetPrivate->Link); + ReleaseFormset (FormsetPrivate); + FreePool (FormsetPrivate); + } else { + DEBUG ((DEBUG_WARN, "%a, formset on HII handle 0x%x was removed already\n", __FUNCTION__, Target->HiiHandle)); + } + } else { + // + // The HII resource on this HII handle is updated/removed. + // + FormsetPrivate = GetFormsetPrivateByHiiHandle (Target->HiiHandle, FormsetList); + if (FormsetPrivate != NULL) { + // + // HII formset already exist, release it and query again. + // + DEBUG ((DEBUG_INFO, "%a, formset: %g is updated. Release current formset\n", __FUNCTION__, &FormsetPrivate->Guid)); + RemoveEntryList (&FormsetPrivate->Link); + ReleaseFormset (FormsetPrivate); + FreePool (FormsetPrivate); + } + + Status = LoadFormsetList (Target->HiiHandle, FormsetList); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a, load formset from HII handle: 0x%x failed: %r\n", __FUNCTION__, Target->HiiHandle, Status)); + } + } + + // + // Detach it from list first. + // + RemoveEntryList (&Target->Link); + FreePool (Target); + + PendingListLink = PendingListNextLink; + } + + return EFI_SUCCESS; +} + +/** + Release all resource in statement list. + + @param[in] StatementList Statement list to be released. + + @retval EFI_SUCCESS All resource are released. + @retval EFI_INVALID_PARAMETER StatementList is NULL. + +**/ +EFI_STATUS +ReleaseStatementList ( + IN REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE_LIST *StatementList + ) +{ + REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE_REF *StatementRef; + LIST_ENTRY *NextLink; + + if (StatementList == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (IsListEmpty (&StatementList->StatementList)) { + return EFI_SUCCESS; + } + + NextLink = GetFirstNode (&StatementList->StatementList); + while (!IsNull (&StatementList->StatementList, NextLink)) { + StatementRef = REDFISH_PLATFORM_CONFIG_STATEMENT_REF_FROM_LINK (NextLink); + NextLink = GetNextNode (&StatementList->StatementList, NextLink); + + RemoveEntryList (&StatementRef->Link); + FreePool (StatementRef); + } + + return EFI_SUCCESS; +} -- 2.21.0.windows.1