mirror of https://github.com/acidanthera/audk.git
920 lines
28 KiB
C
920 lines
28 KiB
C
/** @file
|
|
Common Library Routines to assist in IFR creation on-the-fly
|
|
|
|
Copyright (c) 2006, Intel Corporation
|
|
All rights reserved. This program and the accompanying materials
|
|
are licensed and made available under the terms and conditions of the BSD License
|
|
which accompanies this distribution. The full text of the license may be found at
|
|
http://opensource.org/licenses/bsd-license.php
|
|
|
|
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
|
|
**/
|
|
|
|
#include "IfrSupportLibInternal.h"
|
|
|
|
/**
|
|
Determine what is the current language setting
|
|
The setting is stored in language variable in flash. This routine
|
|
will get setting by accesssing that variable. If failed to access
|
|
language variable, then use default setting that 'eng' as current
|
|
language setting.
|
|
|
|
@param Lang Pointer of system language
|
|
|
|
@return whether sucess to get setting from variable
|
|
**/
|
|
EFI_STATUS
|
|
GetCurrentLanguage (
|
|
OUT CHAR16 *Lang
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN Size;
|
|
UINTN Index;
|
|
CHAR8 Language[4];
|
|
|
|
//
|
|
// Getting the system language and placing it into our Global Data
|
|
//
|
|
Size = sizeof (Language);
|
|
|
|
Status = gRT->GetVariable (
|
|
(CHAR16 *) L"Lang",
|
|
&gEfiGlobalVariableGuid,
|
|
NULL,
|
|
&Size,
|
|
Language
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
AsciiStrCpy (Language, "eng");
|
|
}
|
|
|
|
for (Index = 0; Index < 3; Index++) {
|
|
//
|
|
// Bitwise AND ascii value with 0xDF yields an uppercase value.
|
|
// Sign extend into a unicode value
|
|
//
|
|
Lang[Index] = (CHAR16) (Language[Index] & 0xDF);
|
|
}
|
|
|
|
//
|
|
// Null-terminate the value
|
|
//
|
|
Lang[3] = (CHAR16) 0;
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Add a string to the incoming buffer and return the token and offset data
|
|
|
|
@param StringBuffer The incoming buffer
|
|
@param Language Currrent language
|
|
@param String The string to be added
|
|
@param StringToken The index where the string placed
|
|
|
|
@retval EFI_OUT_OF_RESOURCES No enough buffer to allocate
|
|
@retval EFI_SUCCESS String successfully added to the incoming buffer
|
|
**/
|
|
EFI_STATUS
|
|
AddString (
|
|
IN VOID *StringBuffer,
|
|
IN CHAR16 *Language,
|
|
IN CHAR16 *String,
|
|
IN OUT STRING_REF *StringToken
|
|
)
|
|
{
|
|
EFI_HII_STRING_PACK *StringPack;
|
|
EFI_HII_STRING_PACK *StringPackBuffer;
|
|
VOID *NewBuffer;
|
|
RELOFST *PackSource;
|
|
RELOFST *PackDestination;
|
|
UINT8 *Source;
|
|
UINT8 *Destination;
|
|
UINTN Index;
|
|
BOOLEAN Finished;
|
|
UINTN SizeofLanguage;
|
|
UINTN SizeofString;
|
|
|
|
StringPack = (EFI_HII_STRING_PACK *) StringBuffer;
|
|
Finished = FALSE;
|
|
|
|
//
|
|
// Pre-allocate a buffer sufficient for us to work on.
|
|
// We will use it as a destination scratch pad to build data on
|
|
// and when complete shift the data back to the original buffer
|
|
//
|
|
NewBuffer = AllocateZeroPool (DEFAULT_STRING_BUFFER_SIZE);
|
|
if (NewBuffer == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
StringPackBuffer = (EFI_HII_STRING_PACK *) NewBuffer;
|
|
|
|
//
|
|
// StringPack is terminated with a length 0 entry
|
|
//
|
|
for (; StringPack->Header.Length != 0;) {
|
|
//
|
|
// If this stringpack's language is same as CurrentLanguage, use it
|
|
//
|
|
if (CompareMem ((VOID *) ((CHAR8 *) (StringPack) + StringPack->LanguageNameString), Language, 3) == 0) {
|
|
//
|
|
// We have some data in this string pack, copy the string package up to the string data
|
|
//
|
|
CopyMem (&StringPackBuffer->Header, &StringPack->Header, sizeof (StringPack));
|
|
|
|
//
|
|
// These are references in the structure to tokens, need to increase them by the space occupied by an additional StringPointer
|
|
//
|
|
StringPackBuffer->LanguageNameString = (UINT16) (StringPackBuffer->LanguageNameString + (UINT16) sizeof (RELOFST));
|
|
StringPackBuffer->PrintableLanguageName = (UINT16) (StringPackBuffer->PrintableLanguageName + (UINT16) sizeof (RELOFST));
|
|
|
|
PackSource = (RELOFST *) (StringPack + 1);
|
|
PackDestination = (RELOFST *) (StringPackBuffer + 1);
|
|
for (Index = 0; PackSource[Index] != 0x0000; Index++) {
|
|
//
|
|
// Copy the stringpointers from old to new buffer
|
|
// remember that we are adding a string, so the string offsets will all go up by sizeof (RELOFST)
|
|
//
|
|
PackDestination[Index] = (UINT16) (PackDestination[Index] + sizeof (RELOFST));
|
|
}
|
|
|
|
//
|
|
// Add a new stringpointer in the new buffer since we are adding a string. Null terminate it
|
|
//
|
|
PackDestination[Index] = (UINT16)(PackDestination[Index-1] +
|
|
StrSize((CHAR16 *)((CHAR8 *)(StringPack) + PackSource[Index-1])));
|
|
PackDestination[Index + 1] = (UINT16) 0;
|
|
|
|
//
|
|
// Index is the token value for the new string
|
|
//
|
|
*StringToken = (UINT16) Index;
|
|
|
|
//
|
|
// Source now points to the beginning of the old buffer strings
|
|
// Destination now points to the beginning of the new buffer strings
|
|
//
|
|
Source = (UINT8 *) &PackSource[Index + 1];
|
|
Destination = (UINT8 *) &PackDestination[Index + 2];
|
|
|
|
//
|
|
// This should copy all the strings from the old buffer to the new buffer
|
|
//
|
|
for (; Index != 0; Index--) {
|
|
//
|
|
// Copy Source string to destination buffer
|
|
//
|
|
StrCpy ((CHAR16 *) Destination, (CHAR16 *) Source);
|
|
|
|
//
|
|
// Adjust the source/destination to the next string location
|
|
//
|
|
Destination = Destination + StrSize ((CHAR16 *) Source);
|
|
Source = Source + StrSize ((CHAR16 *) Source);
|
|
}
|
|
|
|
//
|
|
// This copies the new string to the destination buffer
|
|
//
|
|
StrCpy ((CHAR16 *) Destination, (CHAR16 *) String);
|
|
|
|
//
|
|
// Adjust the size of the changed string pack by adding the size of the new string
|
|
// along with the size of the additional offset entry for the new string
|
|
//
|
|
StringPackBuffer->Header.Length = (UINT32) ((UINTN) StringPackBuffer->Header.Length + StrSize (String) + sizeof (RELOFST));
|
|
|
|
//
|
|
// Advance the buffers to point to the next spots.
|
|
//
|
|
StringPackBuffer = (EFI_HII_STRING_PACK *) ((CHAR8 *) (StringPackBuffer) + StringPackBuffer->Header.Length);
|
|
StringPack = (EFI_HII_STRING_PACK *) ((CHAR8 *) (StringPack) + StringPack->Header.Length);
|
|
Finished = TRUE;
|
|
continue;
|
|
}
|
|
//
|
|
// This isn't the language of the stringpack we were asked to add a string to
|
|
// so we need to copy it to the new buffer.
|
|
//
|
|
CopyMem (&StringPackBuffer->Header, &StringPack->Header, StringPack->Header.Length);
|
|
|
|
//
|
|
// Advance the buffers to point to the next spots.
|
|
//
|
|
StringPackBuffer = (EFI_HII_STRING_PACK *) ((CHAR8 *) (StringPackBuffer) + StringPack->Header.Length);
|
|
StringPack = (EFI_HII_STRING_PACK *) ((CHAR8 *) (StringPack) + StringPack->Header.Length);
|
|
}
|
|
|
|
//
|
|
// If we didn't copy the new data to a stringpack yet
|
|
//
|
|
if (!Finished) {
|
|
PackDestination = (RELOFST *) (StringPackBuffer + 1);
|
|
//
|
|
// Pointing to a new string pack location
|
|
//
|
|
SizeofLanguage = StrSize (Language);
|
|
SizeofString = StrSize (String);
|
|
StringPackBuffer->Header.Length = (UINT32)
|
|
(
|
|
sizeof (EFI_HII_STRING_PACK) -
|
|
sizeof (EFI_STRING) +
|
|
sizeof (RELOFST) +
|
|
sizeof (RELOFST) +
|
|
SizeofLanguage +
|
|
SizeofString
|
|
);
|
|
StringPackBuffer->Header.Type = EFI_HII_STRING;
|
|
StringPackBuffer->LanguageNameString = (UINT16) ((UINTN) &PackDestination[3] - (UINTN) StringPackBuffer);
|
|
StringPackBuffer->PrintableLanguageName = (UINT16) ((UINTN) &PackDestination[3] - (UINTN) StringPackBuffer);
|
|
StringPackBuffer->Attributes = 0;
|
|
PackDestination[0] = (UINT16) ((UINTN) &PackDestination[3] - (UINTN) StringPackBuffer);
|
|
PackDestination[1] = (UINT16) (PackDestination[0] + StrSize (Language));
|
|
PackDestination[2] = (UINT16) 0;
|
|
|
|
//
|
|
// The first string location will be set to destination. The minimum number of strings
|
|
// associated with a stringpack will always be token 0 stored as the languagename (e.g. ENG, SPA, etc)
|
|
// and token 1 as the new string being added and and null entry for the stringpointers
|
|
//
|
|
Destination = (UINT8 *) &PackDestination[3];
|
|
|
|
//
|
|
// Copy the language name string to the new buffer
|
|
//
|
|
StrCpy ((CHAR16 *) Destination, Language);
|
|
|
|
//
|
|
// Advance the destination to the new empty spot
|
|
//
|
|
Destination = Destination + StrSize (Language);
|
|
|
|
//
|
|
// Copy the string to the new buffer
|
|
//
|
|
StrCpy ((CHAR16 *) Destination, String);
|
|
|
|
//
|
|
// Since we are starting with a new string pack - we know the new string is token 1
|
|
//
|
|
*StringToken = (UINT16) 1;
|
|
}
|
|
|
|
//
|
|
// Zero out the original buffer and copy the updated data in the new buffer to the old buffer
|
|
//
|
|
ZeroMem (StringBuffer, DEFAULT_STRING_BUFFER_SIZE);
|
|
CopyMem (StringBuffer, NewBuffer, DEFAULT_STRING_BUFFER_SIZE);
|
|
|
|
//
|
|
// Free the newly created buffer since we don't need it anymore
|
|
//
|
|
gBS->FreePool (NewBuffer);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Add op-code data to the FormBuffer
|
|
|
|
@param FormBuffer Form buffer to be inserted to
|
|
@param OpCodeData Op-code data to be inserted
|
|
|
|
@retval EFI_OUT_OF_RESOURCES No enough buffer to allocate
|
|
@retval EFI_SUCCESS Op-code data successfully inserted
|
|
**/
|
|
EFI_STATUS
|
|
AddOpCode (
|
|
IN VOID *FormBuffer,
|
|
IN OUT VOID *OpCodeData
|
|
)
|
|
{
|
|
EFI_HII_PACK_HEADER *NewBuffer;
|
|
UINT8 *Source;
|
|
UINT8 *Destination;
|
|
|
|
//
|
|
// Pre-allocate a buffer sufficient for us to work on.
|
|
// We will use it as a destination scratch pad to build data on
|
|
// and when complete shift the data back to the original buffer
|
|
//
|
|
NewBuffer = AllocateZeroPool (DEFAULT_FORM_BUFFER_SIZE);
|
|
if (NewBuffer == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
Source = (UINT8 *) FormBuffer;
|
|
Destination = (UINT8 *) NewBuffer;
|
|
|
|
//
|
|
// Copy the IFR Package header to the new buffer
|
|
//
|
|
CopyMem (Destination, Source, sizeof (EFI_HII_PACK_HEADER));
|
|
|
|
//
|
|
// Advance Source and Destination to next op-code
|
|
//
|
|
Source = Source + sizeof (EFI_HII_PACK_HEADER);
|
|
Destination = Destination + sizeof (EFI_HII_PACK_HEADER);
|
|
|
|
//
|
|
// Copy data to the new buffer until we run into the end_form
|
|
//
|
|
for (; ((FRAMEWORK_EFI_IFR_OP_HEADER *) Source)->OpCode != FRAMEWORK_EFI_IFR_END_FORM_OP;) {
|
|
//
|
|
// If the this opcode is an end_form_set we better be creating and endform
|
|
// Nonetheless, we will add data before the end_form_set. This also provides
|
|
// for interesting behavior in the code we will run, but has no bad side-effects
|
|
// since we will possibly do a 0 byte copy in this particular end-case.
|
|
//
|
|
if (((FRAMEWORK_EFI_IFR_OP_HEADER *) Source)->OpCode == FRAMEWORK_EFI_IFR_END_FORM_SET_OP) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Copy data to new buffer
|
|
//
|
|
CopyMem (Destination, Source, ((FRAMEWORK_EFI_IFR_OP_HEADER *) Source)->Length);
|
|
|
|
//
|
|
// Adjust Source/Destination to next op-code location
|
|
//
|
|
Destination = Destination + (UINTN) ((FRAMEWORK_EFI_IFR_OP_HEADER *) Source)->Length;
|
|
Source = Source + (UINTN) ((FRAMEWORK_EFI_IFR_OP_HEADER *) Source)->Length;
|
|
}
|
|
|
|
//
|
|
// Prior to the end_form is where we insert the new op-code data
|
|
//
|
|
CopyMem (Destination, OpCodeData, ((FRAMEWORK_EFI_IFR_OP_HEADER *) OpCodeData)->Length);
|
|
Destination = Destination + (UINTN) ((FRAMEWORK_EFI_IFR_OP_HEADER *) OpCodeData)->Length;
|
|
|
|
NewBuffer->Length = (UINT32) (NewBuffer->Length + (UINT32) (((FRAMEWORK_EFI_IFR_OP_HEADER *) OpCodeData)->Length));
|
|
|
|
//
|
|
// Copy end-form data to new buffer
|
|
//
|
|
CopyMem (Destination, Source, ((FRAMEWORK_EFI_IFR_OP_HEADER *) Source)->Length);
|
|
|
|
//
|
|
// Adjust Source/Destination to next op-code location
|
|
//
|
|
Destination = Destination + (UINTN) ((FRAMEWORK_EFI_IFR_OP_HEADER *) Source)->Length;
|
|
Source = Source + (UINTN) ((FRAMEWORK_EFI_IFR_OP_HEADER *) Source)->Length;
|
|
|
|
//
|
|
// Copy end-formset data to new buffer
|
|
//
|
|
CopyMem (Destination, Source, ((FRAMEWORK_EFI_IFR_OP_HEADER *) Source)->Length);
|
|
|
|
//
|
|
// Zero out the original buffer and copy the updated data in the new buffer to the old buffer
|
|
//
|
|
ZeroMem (FormBuffer, DEFAULT_FORM_BUFFER_SIZE);
|
|
CopyMem (FormBuffer, NewBuffer, DEFAULT_FORM_BUFFER_SIZE);
|
|
|
|
//
|
|
// Free the newly created buffer since we don't need it anymore
|
|
//
|
|
gBS->FreePool (NewBuffer);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Get the HII protocol interface
|
|
|
|
@param Hii HII protocol interface
|
|
|
|
@return the statue of locating HII protocol
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
GetHiiInterface (
|
|
OUT EFI_HII_PROTOCOL **Hii
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
//
|
|
// There should only be one HII protocol
|
|
//
|
|
Status = gBS->LocateProtocol (
|
|
&gEfiHiiProtocolGuid,
|
|
NULL,
|
|
(VOID **) Hii
|
|
);
|
|
|
|
return Status;;
|
|
}
|
|
|
|
/**
|
|
Extract information pertaining to the HiiHandle
|
|
|
|
@param HiiHandle Hii handle
|
|
@param ImageLength For input, length of DefaultImage;
|
|
For output, length of actually required
|
|
@param DefaultImage Image buffer prepared by caller
|
|
@param Guid Guid information about the form
|
|
|
|
@retval EFI_OUT_OF_RESOURCES No enough buffer to allocate
|
|
@retval EFI_BUFFER_TOO_SMALL DefualtImage has no enough ImageLength
|
|
@retval EFI_SUCCESS Successfully extract data from Hii database.
|
|
**/
|
|
EFI_STATUS
|
|
ExtractDataFromHiiHandle (
|
|
IN FRAMEWORK_EFI_HII_HANDLE HiiHandle,
|
|
IN OUT UINT16 *ImageLength,
|
|
OUT UINT8 *DefaultImage,
|
|
OUT EFI_GUID *Guid
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_HII_PROTOCOL *Hii;
|
|
UINTN DataLength;
|
|
UINT8 *RawData;
|
|
UINT8 *OldData;
|
|
UINTN Index;
|
|
UINTN Temp;
|
|
UINTN SizeOfNvStore;
|
|
UINTN CachedStart;
|
|
|
|
DataLength = DEFAULT_FORM_BUFFER_SIZE;
|
|
SizeOfNvStore = 0;
|
|
CachedStart = 0;
|
|
|
|
Status = GetHiiInterface (&Hii);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Allocate space for retrieval of IFR data
|
|
//
|
|
RawData = AllocateZeroPool (DataLength);
|
|
if (RawData == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Get all the forms associated with this HiiHandle
|
|
//
|
|
Status = Hii->GetForms (Hii, HiiHandle, 0, &DataLength, RawData);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
gBS->FreePool (RawData);
|
|
|
|
//
|
|
// Allocate space for retrieval of IFR data
|
|
//
|
|
RawData = AllocateZeroPool (DataLength);
|
|
if (RawData == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Get all the forms associated with this HiiHandle
|
|
//
|
|
Status = Hii->GetForms (Hii, HiiHandle, 0, &DataLength, RawData);
|
|
}
|
|
|
|
OldData = RawData;
|
|
|
|
//
|
|
// Point RawData to the beginning of the form data
|
|
//
|
|
RawData = (UINT8 *) ((UINTN) RawData + sizeof (EFI_HII_PACK_HEADER));
|
|
|
|
for (Index = 0; RawData[Index] != FRAMEWORK_EFI_IFR_END_FORM_SET_OP;) {
|
|
switch (RawData[Index]) {
|
|
case FRAMEWORK_EFI_IFR_FORM_SET_OP:
|
|
//
|
|
// Copy the GUID information from this handle
|
|
//
|
|
CopyMem (Guid, &((FRAMEWORK_EFI_IFR_FORM_SET *) &RawData[Index])->Guid, sizeof (EFI_GUID));
|
|
break;
|
|
|
|
case FRAMEWORK_EFI_IFR_ONE_OF_OP:
|
|
case FRAMEWORK_EFI_IFR_CHECKBOX_OP:
|
|
case FRAMEWORK_EFI_IFR_NUMERIC_OP:
|
|
case FRAMEWORK_EFI_IFR_DATE_OP:
|
|
case FRAMEWORK_EFI_IFR_TIME_OP:
|
|
case FRAMEWORK_EFI_IFR_PASSWORD_OP:
|
|
case FRAMEWORK_EFI_IFR_STRING_OP:
|
|
//
|
|
// Remember, multiple op-codes may reference the same item, so let's keep a running
|
|
// marker of what the highest QuestionId that wasn't zero length. This will accurately
|
|
// maintain the Size of the NvStore
|
|
//
|
|
if (((FRAMEWORK_EFI_IFR_ONE_OF *) &RawData[Index])->Width != 0) {
|
|
Temp = ((FRAMEWORK_EFI_IFR_ONE_OF *) &RawData[Index])->QuestionId + ((FRAMEWORK_EFI_IFR_ONE_OF *) &RawData[Index])->Width;
|
|
if (SizeOfNvStore < Temp) {
|
|
SizeOfNvStore = ((FRAMEWORK_EFI_IFR_ONE_OF *) &RawData[Index])->QuestionId + ((FRAMEWORK_EFI_IFR_ONE_OF *) &RawData[Index])->Width;
|
|
}
|
|
}
|
|
}
|
|
|
|
Index = RawData[Index + 1] + Index;
|
|
}
|
|
|
|
//
|
|
// Return an error if buffer is too small
|
|
//
|
|
if (SizeOfNvStore > *ImageLength) {
|
|
gBS->FreePool (OldData);
|
|
*ImageLength = (UINT16) SizeOfNvStore;
|
|
return EFI_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
if (DefaultImage != NULL) {
|
|
ZeroMem (DefaultImage, SizeOfNvStore);
|
|
}
|
|
|
|
//
|
|
// Copy the default image information to the user's buffer
|
|
//
|
|
for (Index = 0; RawData[Index] != FRAMEWORK_EFI_IFR_END_FORM_SET_OP;) {
|
|
switch (RawData[Index]) {
|
|
case FRAMEWORK_EFI_IFR_ONE_OF_OP:
|
|
CachedStart = ((FRAMEWORK_EFI_IFR_ONE_OF *) &RawData[Index])->QuestionId;
|
|
break;
|
|
|
|
case FRAMEWORK_EFI_IFR_ONE_OF_OPTION_OP:
|
|
if (((FRAMEWORK_EFI_IFR_ONE_OF_OPTION *) &RawData[Index])->Flags & FRAMEWORK_EFI_IFR_FLAG_DEFAULT) {
|
|
CopyMem (&DefaultImage[CachedStart], &((FRAMEWORK_EFI_IFR_ONE_OF_OPTION *) &RawData[Index])->Value, 2);
|
|
}
|
|
break;
|
|
|
|
case FRAMEWORK_EFI_IFR_CHECKBOX_OP:
|
|
DefaultImage[((FRAMEWORK_EFI_IFR_ONE_OF *) &RawData[Index])->QuestionId] = ((FRAMEWORK_EFI_IFR_CHECKBOX *) &RawData[Index])->Flags;
|
|
break;
|
|
|
|
case FRAMEWORK_EFI_IFR_NUMERIC_OP:
|
|
CopyMem (
|
|
&DefaultImage[((FRAMEWORK_EFI_IFR_ONE_OF *) &RawData[Index])->QuestionId],
|
|
&((FRAMEWORK_EFI_IFR_NUMERIC *) &RawData[Index])->Default,
|
|
2
|
|
);
|
|
break;
|
|
|
|
}
|
|
|
|
Index = RawData[Index + 1] + Index;
|
|
}
|
|
|
|
*ImageLength = (UINT16) SizeOfNvStore;
|
|
|
|
//
|
|
// Free our temporary repository of form data
|
|
//
|
|
gBS->FreePool (OldData);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Finds HII handle for given pack GUID previously registered with the HII.
|
|
|
|
@param HiiProtocol pointer to pointer to HII protocol interface.
|
|
If NULL, the interface will be found but not returned.
|
|
If it points to NULL, the interface will be found and
|
|
written back to the pointer that is pointed to.
|
|
@param Guid The GUID of the pack that registered with the HII.
|
|
|
|
@return Handle to the HII pack previously registered by the memory driver.
|
|
**/
|
|
FRAMEWORK_EFI_HII_HANDLE
|
|
FindHiiHandle (
|
|
IN OUT EFI_HII_PROTOCOL **HiiProtocol, OPTIONAL
|
|
IN EFI_GUID *Guid
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
FRAMEWORK_EFI_HII_HANDLE *HiiHandleBuffer;
|
|
FRAMEWORK_EFI_HII_HANDLE HiiHandle;
|
|
UINT16 HiiHandleBufferLength;
|
|
UINT32 NumberOfHiiHandles;
|
|
EFI_GUID HiiGuid;
|
|
EFI_HII_PROTOCOL *HiiProt;
|
|
UINT32 Index;
|
|
UINT16 Length;
|
|
|
|
HiiHandle = 0;
|
|
if ((HiiProtocol != NULL) && (*HiiProtocol != NULL)) {
|
|
//
|
|
// The protocol has been passed in
|
|
//
|
|
HiiProt = *HiiProtocol;
|
|
} else {
|
|
gBS->LocateProtocol (
|
|
&gEfiHiiProtocolGuid,
|
|
NULL,
|
|
(VOID **) &HiiProt
|
|
);
|
|
if (HiiProt == NULL) {
|
|
return HiiHandle;
|
|
}
|
|
|
|
if (HiiProtocol != NULL) {
|
|
//
|
|
// Return back the HII protocol for the caller as promissed
|
|
//
|
|
*HiiProtocol = HiiProt;
|
|
}
|
|
}
|
|
//
|
|
// Allocate buffer
|
|
//
|
|
HiiHandleBufferLength = 10;
|
|
HiiHandleBuffer = AllocatePool (HiiHandleBufferLength);
|
|
ASSERT (HiiHandleBuffer != NULL);
|
|
|
|
//
|
|
// Get the Handles of the packages that were registered with Hii
|
|
//
|
|
Status = HiiProt->FindHandles (
|
|
HiiProt,
|
|
&HiiHandleBufferLength,
|
|
HiiHandleBuffer
|
|
);
|
|
|
|
//
|
|
// Get a bigger bugffer if this one is to small, and try again
|
|
//
|
|
if (Status == EFI_BUFFER_TOO_SMALL) {
|
|
|
|
gBS->FreePool (HiiHandleBuffer);
|
|
|
|
HiiHandleBuffer = AllocatePool (HiiHandleBufferLength);
|
|
ASSERT (HiiHandleBuffer != NULL);
|
|
|
|
Status = HiiProt->FindHandles (
|
|
HiiProt,
|
|
&HiiHandleBufferLength,
|
|
HiiHandleBuffer
|
|
);
|
|
}
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
goto lbl_exit;
|
|
}
|
|
|
|
NumberOfHiiHandles = HiiHandleBufferLength / sizeof (FRAMEWORK_EFI_HII_HANDLE );
|
|
|
|
//
|
|
// Iterate Hii handles and look for the one that matches our Guid
|
|
//
|
|
for (Index = 0; Index < NumberOfHiiHandles; Index++) {
|
|
|
|
Length = 0;
|
|
ExtractDataFromHiiHandle (HiiHandleBuffer[Index], &Length, NULL, &HiiGuid);
|
|
|
|
if (CompareGuid (&HiiGuid, Guid)) {
|
|
|
|
HiiHandle = HiiHandleBuffer[Index];
|
|
break;
|
|
}
|
|
}
|
|
|
|
lbl_exit:
|
|
gBS->FreePool (HiiHandleBuffer);
|
|
return HiiHandle;
|
|
}
|
|
|
|
/**
|
|
Validate that the data associated with the HiiHandle in NVRAM is within
|
|
the reasonable parameters for that FormSet. Values for strings and passwords
|
|
are not verified due to their not having the equivalent of valid range settings.
|
|
|
|
@param HiiHandle Handle of the HII database entry to query
|
|
|
|
@param Results If return Status is EFI_SUCCESS, Results provides valid data
|
|
TRUE = NVRAM Data is within parameters
|
|
FALSE = NVRAM Data is NOT within parameters
|
|
@retval EFI_OUT_OF_RESOURCES No enough buffer to allocate
|
|
@retval EFI_SUCCESS Data successfully validated
|
|
**/
|
|
EFI_STATUS
|
|
ValidateDataFromHiiHandle (
|
|
IN FRAMEWORK_EFI_HII_HANDLE HiiHandle,
|
|
OUT BOOLEAN *Results
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_HII_PROTOCOL *Hii;
|
|
EFI_GUID Guid;
|
|
UINT8 *RawData;
|
|
UINT8 *OldData;
|
|
UINTN RawDataLength;
|
|
UINT8 *VariableData;
|
|
UINTN Index;
|
|
UINTN Temp;
|
|
UINTN SizeOfNvStore;
|
|
UINTN CachedStart;
|
|
BOOLEAN GotMatch;
|
|
|
|
RawDataLength = DEFAULT_FORM_BUFFER_SIZE;
|
|
SizeOfNvStore = 0;
|
|
CachedStart = 0;
|
|
GotMatch = FALSE;
|
|
*Results = TRUE;
|
|
|
|
Status = GetHiiInterface (&Hii);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Allocate space for retrieval of IFR data
|
|
//
|
|
RawData = AllocateZeroPool (RawDataLength);
|
|
if (RawData == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Get all the forms associated with this HiiHandle
|
|
//
|
|
Status = Hii->GetForms (Hii, HiiHandle, 0, &RawDataLength, RawData);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
gBS->FreePool (RawData);
|
|
|
|
//
|
|
// Allocate space for retrieval of IFR data
|
|
//
|
|
RawData = AllocateZeroPool (RawDataLength);
|
|
if (RawData == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Get all the forms associated with this HiiHandle
|
|
//
|
|
Status = Hii->GetForms (Hii, HiiHandle, 0, &RawDataLength, RawData);
|
|
}
|
|
|
|
OldData = RawData;
|
|
|
|
//
|
|
// Point RawData to the beginning of the form data
|
|
//
|
|
RawData = (UINT8 *) ((UINTN) RawData + sizeof (EFI_HII_PACK_HEADER));
|
|
|
|
for (Index = 0; RawData[Index] != FRAMEWORK_EFI_IFR_END_FORM_SET_OP;) {
|
|
if (RawData[Index] == FRAMEWORK_EFI_IFR_FORM_SET_OP) {
|
|
CopyMem (&Guid, &((FRAMEWORK_EFI_IFR_FORM_SET *) &RawData[Index])->Guid, sizeof (EFI_GUID));
|
|
break;
|
|
}
|
|
|
|
Index = RawData[Index + 1] + Index;
|
|
}
|
|
|
|
for (Index = 0; RawData[Index] != FRAMEWORK_EFI_IFR_END_FORM_SET_OP;) {
|
|
switch (RawData[Index]) {
|
|
case FRAMEWORK_EFI_IFR_FORM_SET_OP:
|
|
break;
|
|
|
|
case FRAMEWORK_EFI_IFR_ONE_OF_OP:
|
|
case FRAMEWORK_EFI_IFR_CHECKBOX_OP:
|
|
case FRAMEWORK_EFI_IFR_NUMERIC_OP:
|
|
case FRAMEWORK_EFI_IFR_DATE_OP:
|
|
case FRAMEWORK_EFI_IFR_TIME_OP:
|
|
case FRAMEWORK_EFI_IFR_PASSWORD_OP:
|
|
case FRAMEWORK_EFI_IFR_STRING_OP:
|
|
//
|
|
// Remember, multiple op-codes may reference the same item, so let's keep a running
|
|
// marker of what the highest QuestionId that wasn't zero length. This will accurately
|
|
// maintain the Size of the NvStore
|
|
//
|
|
if (((FRAMEWORK_EFI_IFR_ONE_OF *) &RawData[Index])->Width != 0) {
|
|
Temp = ((FRAMEWORK_EFI_IFR_ONE_OF *) &RawData[Index])->QuestionId + ((FRAMEWORK_EFI_IFR_ONE_OF *) &RawData[Index])->Width;
|
|
if (SizeOfNvStore < Temp) {
|
|
SizeOfNvStore = ((FRAMEWORK_EFI_IFR_ONE_OF *) &RawData[Index])->QuestionId + ((FRAMEWORK_EFI_IFR_ONE_OF *) &RawData[Index])->Width;
|
|
}
|
|
}
|
|
}
|
|
|
|
Index = RawData[Index + 1] + Index;
|
|
}
|
|
|
|
//
|
|
// Allocate memory for our File Form Tags
|
|
//
|
|
VariableData = AllocateZeroPool (SizeOfNvStore);
|
|
if (VariableData == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
Status = gRT->GetVariable (
|
|
(CHAR16 *) L"Setup",
|
|
&Guid,
|
|
NULL,
|
|
&SizeOfNvStore,
|
|
(VOID *) VariableData
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
|
|
//
|
|
// If there is a variable that exists already and it is larger than what we calculated the
|
|
// storage needs to be, we must assume the variable size from GetVariable is correct and not
|
|
// allow the truncation of the variable. It is very possible that the user who created the IFR
|
|
// we are cracking is not referring to a variable that was in a previous map, however we cannot
|
|
// allow it's truncation.
|
|
//
|
|
if (Status == EFI_BUFFER_TOO_SMALL) {
|
|
//
|
|
// Free the buffer that was allocated that was too small
|
|
//
|
|
gBS->FreePool (VariableData);
|
|
|
|
VariableData = AllocatePool (SizeOfNvStore);
|
|
if (VariableData == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
Status = gRT->GetVariable (
|
|
(CHAR16 *) L"Setup",
|
|
&Guid,
|
|
NULL,
|
|
&SizeOfNvStore,
|
|
(VOID *) VariableData
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Walk through the form and see that the variable data it refers to is ok.
|
|
// This allows for the possibility of stale (obsoleted) data in the variable
|
|
// can be overlooked without causing an error
|
|
//
|
|
for (Index = 0; RawData[Index] != FRAMEWORK_EFI_IFR_END_FORM_SET_OP;) {
|
|
switch (RawData[Index]) {
|
|
case FRAMEWORK_EFI_IFR_ONE_OF_OP:
|
|
//
|
|
// A one_of has no data, its the option that does - cache the storage Id
|
|
//
|
|
CachedStart = ((FRAMEWORK_EFI_IFR_ONE_OF *) &RawData[Index])->QuestionId;
|
|
break;
|
|
|
|
case FRAMEWORK_EFI_IFR_ONE_OF_OPTION_OP:
|
|
//
|
|
// A one_of_option can be any value
|
|
//
|
|
if (VariableData[CachedStart] == ((FRAMEWORK_EFI_IFR_ONE_OF_OPTION *) &RawData[Index])->Value) {
|
|
GotMatch = TRUE;
|
|
}
|
|
break;
|
|
|
|
case FRAMEWORK_EFI_IFR_END_ONE_OF_OP:
|
|
//
|
|
// At this point lets make sure that the data value in the NVRAM matches one of the options
|
|
//
|
|
if (!GotMatch) {
|
|
*Results = FALSE;
|
|
return EFI_SUCCESS;
|
|
}
|
|
break;
|
|
|
|
case FRAMEWORK_EFI_IFR_CHECKBOX_OP:
|
|
//
|
|
// A checkbox is a boolean, so 0 and 1 are valid
|
|
// Remember, QuestionId corresponds to the offset location of the data in the variable
|
|
//
|
|
if (VariableData[((FRAMEWORK_EFI_IFR_CHECKBOX *) &RawData[Index])->QuestionId] > 1) {
|
|
*Results = FALSE;
|
|
return EFI_SUCCESS;
|
|
}
|
|
break;
|
|
|
|
case FRAMEWORK_EFI_IFR_NUMERIC_OP:
|
|
if ((VariableData[((FRAMEWORK_EFI_IFR_NUMERIC *)&RawData[Index])->QuestionId] < ((FRAMEWORK_EFI_IFR_NUMERIC *)&RawData[Index])->Minimum) ||
|
|
(VariableData[((FRAMEWORK_EFI_IFR_NUMERIC *)&RawData[Index])->QuestionId] > ((FRAMEWORK_EFI_IFR_NUMERIC *)&RawData[Index])->Maximum)) {
|
|
*Results = FALSE;
|
|
return EFI_SUCCESS;
|
|
}
|
|
break;
|
|
|
|
}
|
|
|
|
Index = RawData[Index + 1] + Index;
|
|
}
|
|
|
|
//
|
|
// Free our temporary repository of form data
|
|
//
|
|
gBS->FreePool (OldData);
|
|
gBS->FreePool (VariableData);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|