/** @file
The implementation of HII IFR parser.
Copyright (c) 2019, Intel Corporation. All rights reserved.
(C) Copyright 2021 Hewlett Packard Enterprise Development LP
Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "HiiInternal.h"
/**
Initialize Statement header members.
@param[in] OpCodeData Pointer of the raw OpCode data.
@param[in,out] FormSet Pointer of the current FormSet.
@param[in,out] Form Pointer of the current Form.
@return The Statement.
**/
HII_STATEMENT *
CreateStatement (
IN UINT8 *OpCodeData,
IN OUT HII_FORMSET *FormSet,
IN OUT HII_FORM *Form
)
{
HII_STATEMENT *Statement;
EFI_IFR_STATEMENT_HEADER *StatementHdr;
INTN ConditionalExprCount;
if (Form == NULL) {
//
// Only guid op may out side the form level.
//
if (((EFI_IFR_OP_HEADER *)OpCodeData)->OpCode != EFI_IFR_GUID_OP) {
return NULL;
}
}
Statement = (HII_STATEMENT *)AllocateZeroPool (sizeof (HII_STATEMENT));
if (Statement == NULL) {
return NULL;
}
InitializeListHead (&Statement->DefaultListHead);
InitializeListHead (&Statement->OptionListHead);
InitializeListHead (&Statement->InconsistentListHead);
InitializeListHead (&Statement->NoSubmitListHead);
InitializeListHead (&Statement->WarningListHead);
Statement->Signature = HII_STATEMENT_SIGNATURE;
Statement->Operand = ((EFI_IFR_OP_HEADER *)OpCodeData)->OpCode;
Statement->OpCode = (EFI_IFR_OP_HEADER *)OpCodeData;
Statement->QuestionReferToBitField = FALSE;
StatementHdr = (EFI_IFR_STATEMENT_HEADER *)(OpCodeData + sizeof (EFI_IFR_OP_HEADER));
CopyMem (&Statement->Prompt, &StatementHdr->Prompt, sizeof (EFI_STRING_ID));
CopyMem (&Statement->Help, &StatementHdr->Help, sizeof (EFI_STRING_ID));
ConditionalExprCount = GetConditionalExpressionCount (ExpressStatement);
if (ConditionalExprCount > 0) {
//
// Form is inside of suppressif
//
Statement->ExpressionList = (HII_EXPRESSION_LIST *)AllocatePool (
(UINTN)(sizeof (HII_EXPRESSION_LIST) + ((ConditionalExprCount - 1) * sizeof (HII_EXPRESSION *)))
);
if (Statement->ExpressionList == NULL) {
return NULL;
}
Statement->ExpressionList->Count = (UINTN)ConditionalExprCount;
Statement->ExpressionList->Signature = HII_EXPRESSION_LIST_SIGNATURE;
CopyMem (
Statement->ExpressionList->Expression,
GetConditionalExpressionList (ExpressStatement),
(UINTN)(sizeof (HII_EXPRESSION *) * ConditionalExprCount)
);
}
//
// Insert this Statement into current Form
//
if (Form == NULL) {
InsertTailList (&FormSet->StatementListOSF, &Statement->Link);
} else {
InsertTailList (&Form->StatementListHead, &Statement->Link);
}
return Statement;
}
/**
Initialize Question's members.
@param[in] OpCodeData Pointer of the raw OpCode data.
@param[in,out] FormSet Pointer of the current FormSet.
@param[in,out] Form Pointer of the current Form.
@return The Question.
**/
HII_STATEMENT *
CreateQuestion (
IN UINT8 *OpCodeData,
IN OUT HII_FORMSET *FormSet,
IN OUT HII_FORM *Form
)
{
HII_STATEMENT *Statement;
EFI_IFR_QUESTION_HEADER *QuestionHdr;
LIST_ENTRY *Link;
HII_FORMSET_STORAGE *Storage;
HII_NAME_VALUE_NODE *NameValueNode;
BOOLEAN Find;
Statement = CreateStatement (OpCodeData, FormSet, Form);
if (Statement == NULL) {
return NULL;
}
QuestionHdr = (EFI_IFR_QUESTION_HEADER *)(OpCodeData + sizeof (EFI_IFR_OP_HEADER));
CopyMem (&Statement->QuestionId, &QuestionHdr->QuestionId, sizeof (EFI_QUESTION_ID));
CopyMem (&Statement->VarStoreId, &QuestionHdr->VarStoreId, sizeof (EFI_VARSTORE_ID));
CopyMem (&Statement->VarStoreInfo.VarOffset, &QuestionHdr->VarStoreInfo.VarOffset, sizeof (UINT16));
Statement->QuestionFlags = QuestionHdr->Flags;
if (Statement->VarStoreId == 0) {
//
// VarStoreId of zero indicates no variable storage
//
return Statement;
}
//
// Find Storage for this Question
//
Link = GetFirstNode (&FormSet->StorageListHead);
while (!IsNull (&FormSet->StorageListHead, Link)) {
Storage = HII_STORAGE_FROM_LINK (Link);
if (Statement->VarStoreId == Storage->VarStoreId) {
Statement->Storage = Storage;
break;
}
Link = GetNextNode (&FormSet->StorageListHead, Link);
}
if (Statement->Storage == NULL) {
return NULL;
}
//
// Initialize varname for Name/Value or EFI Variable
//
if ((Statement->Storage->Type == EFI_HII_VARSTORE_NAME_VALUE) ||
(Statement->Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE))
{
Statement->VariableName = GetTokenString (Statement->VarStoreInfo.VarName, FormSet->HiiHandle);
if (Statement->VariableName == NULL) {
return NULL;
}
if (Statement->Storage->Type == EFI_HII_VARSTORE_NAME_VALUE) {
//
// Check whether old string node already exist.
//
Find = FALSE;
if (!IsListEmpty (&Statement->Storage->NameValueList)) {
Link = GetFirstNode (&Statement->Storage->NameValueList);
while (!IsNull (&Statement->Storage->NameValueList, Link)) {
NameValueNode = HII_NAME_VALUE_NODE_FROM_LINK (Link);
if (StrCmp (Statement->VariableName, NameValueNode->Name) == 0) {
Find = TRUE;
break;
}
Link = GetNextNode (&Statement->Storage->NameValueList, Link);
}
}
if (!Find) {
//
// Insert to Name/Value varstore list
//
NameValueNode = AllocateZeroPool (sizeof (HII_NAME_VALUE_NODE));
if (NameValueNode == NULL) {
return NULL;
}
NameValueNode->Signature = HII_NAME_VALUE_NODE_SIGNATURE;
NameValueNode->Name = AllocateCopyPool (StrSize (Statement->VariableName), Statement->VariableName);
if (NameValueNode->Name == NULL) {
FreePool (NameValueNode);
return NULL;
}
NameValueNode->Value = AllocateZeroPool (0x10);
if (NameValueNode->Value == NULL) {
FreePool (NameValueNode->Name);
FreePool (NameValueNode);
return NULL;
}
InsertTailList (&Statement->Storage->NameValueList, &NameValueNode->Link);
}
}
}
return Statement;
}
/**
Allocate a HII_EXPRESSION node.
@param[in,out] Form The Form associated with this Expression
@param[in] OpCode The binary opcode data.
@return Pointer to a HII_EXPRESSION data structure.
**/
HII_EXPRESSION *
CreateExpression (
IN OUT HII_FORM *Form,
IN UINT8 *OpCode
)
{
HII_EXPRESSION *Expression;
Expression = AllocateZeroPool (sizeof (HII_EXPRESSION));
if (Expression == NULL) {
return NULL;
}
Expression->Signature = HII_EXPRESSION_SIGNATURE;
InitializeListHead (&Expression->OpCodeListHead);
Expression->OpCode = (EFI_IFR_OP_HEADER *)OpCode;
return Expression;
}
/**
Create ConfigHdr string for a storage.
@param[in] FormSet Pointer of the current FormSet
@param[in,out] Storage Pointer of the storage
@retval EFI_SUCCESS Initialize ConfigHdr success
**/
EFI_STATUS
InitializeConfigHdr (
IN HII_FORMSET *FormSet,
IN OUT HII_FORMSET_STORAGE *Storage
)
{
CHAR16 *Name;
if ((Storage->Type == EFI_HII_VARSTORE_BUFFER) ||
(Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE_BUFFER))
{
Name = Storage->Name;
} else {
Name = NULL;
}
Storage->ConfigHdr = HiiConstructConfigHdr (
&Storage->Guid,
Name,
FormSet->DriverHandle
);
if (Storage->ConfigHdr == NULL) {
return EFI_NOT_FOUND;
}
return EFI_SUCCESS;
}
/**
Convert Ascii string to Unicode.
This is an internal function.
@param[in] AsciiString The Ascii string to be converted.
@param[out] UnicodeString The Unicode string retrieved.
**/
VOID
AsciiToUnicode (
IN CHAR8 *AsciiString,
OUT CHAR16 *UnicodeString
)
{
UINT8 Index;
Index = 0;
while (AsciiString[Index] != 0) {
UnicodeString[Index] = (CHAR16)AsciiString[Index];
Index++;
}
UnicodeString[Index] = L'\0';
}
/**
Allocate a HII_FORMSET_STORAGE data structure and insert to FormSet Storage List.
@param[in] FormSet Pointer of the current FormSet
@param[in] StorageType Storage type.
@param[in] OpCodeData Binary data for this opcode.
@return Pointer to a HII_FORMSET_STORAGE data structure.
**/
HII_FORMSET_STORAGE *
CreateStorage (
IN HII_FORMSET *FormSet,
IN UINT8 StorageType,
IN UINT8 *OpCodeData
)
{
HII_FORMSET_STORAGE *Storage;
CHAR8 *AsciiStorageName;
AsciiStorageName = NULL;
Storage = AllocateZeroPool (sizeof (HII_FORMSET_STORAGE));
if (Storage == NULL) {
return NULL;
}
Storage->Signature = HII_STORAGE_SIGNATURE;
Storage->Type = StorageType;
switch (StorageType) {
case EFI_HII_VARSTORE_BUFFER:
CopyMem (&Storage->Guid, &((EFI_IFR_VARSTORE *)OpCodeData)->Guid, sizeof (EFI_GUID));
CopyMem (&Storage->Size, &((EFI_IFR_VARSTORE *)OpCodeData)->Size, sizeof (UINT16));
Storage->Buffer = AllocateZeroPool (Storage->Size);
if (Storage->Buffer == NULL) {
FreePool (Storage);
return NULL;
}
AsciiStorageName = (CHAR8 *)((EFI_IFR_VARSTORE *)OpCodeData)->Name;
Storage->Name = AllocatePool (sizeof (CHAR16) * (AsciiStrLen (AsciiStorageName) + 1));
if (Storage->Name == NULL) {
FreePool (Storage);
return NULL;
}
AsciiToUnicode (AsciiStorageName, Storage->Name);
break;
case EFI_HII_VARSTORE_EFI_VARIABLE:
case EFI_HII_VARSTORE_EFI_VARIABLE_BUFFER:
CopyMem (&Storage->Guid, &((EFI_IFR_VARSTORE_EFI *)OpCodeData)->Guid, sizeof (EFI_GUID));
CopyMem (&Storage->Attributes, &((EFI_IFR_VARSTORE_EFI *)OpCodeData)->Attributes, sizeof (UINT32));
CopyMem (&Storage->Size, &((EFI_IFR_VARSTORE_EFI *)OpCodeData)->Size, sizeof (UINT16));
if (StorageType == EFI_HII_VARSTORE_EFI_VARIABLE_BUFFER) {
Storage->Buffer = AllocateZeroPool (Storage->Size);
if (Storage->Buffer == NULL) {
FreePool (Storage);
return NULL;
}
}
AsciiStorageName = (CHAR8 *)((EFI_IFR_VARSTORE_EFI *)OpCodeData)->Name;
Storage->Name = AllocatePool (sizeof (CHAR16) * (AsciiStrLen (AsciiStorageName) + 1));
if (Storage->Name == NULL) {
FreePool (Storage);
return NULL;
}
AsciiToUnicode (AsciiStorageName, Storage->Name);
break;
case EFI_HII_VARSTORE_NAME_VALUE:
CopyMem (&Storage->Guid, &((EFI_IFR_VARSTORE_NAME_VALUE *)OpCodeData)->Guid, sizeof (EFI_GUID));
InitializeListHead (&Storage->NameValueList);
break;
default:
break;
}
InitializeConfigHdr (FormSet, Storage);
InsertTailList (&FormSet->StorageListHead, &Storage->Link);
return Storage;
}
/**
Get formset storage based on the input varstoreid info.
@param[in] FormSet Pointer of the current FormSet.
@param[in] VarStoreId Varstore ID info.
@return Pointer to a HII_FORMSET_STORAGE data structure.
**/
HII_FORMSET_STORAGE *
GetFstStgFromVarId (
IN HII_FORMSET *FormSet,
IN EFI_VARSTORE_ID VarStoreId
)
{
HII_FORMSET_STORAGE *Storage;
LIST_ENTRY *Link;
BOOLEAN Found;
Found = FALSE;
Storage = NULL;
//
// Find Formset Storage for this Question
//
Link = GetFirstNode (&FormSet->StorageListHead);
while (!IsNull (&FormSet->StorageListHead, Link)) {
Storage = HII_STORAGE_FROM_LINK (Link);
if (Storage->VarStoreId == VarStoreId) {
Found = TRUE;
break;
}
Link = GetNextNode (&FormSet->StorageListHead, Link);
}
return Found ? Storage : NULL;
}
/**
Initialize Request Element of a Question. ::= '&' | '&'