1068 lines
34 KiB
C
1068 lines
34 KiB
C
/** @file
|
|
|
|
H2O Setup Change DXE implementation.
|
|
|
|
;******************************************************************************
|
|
;* Copyright (c) 2014 - 2020, Insyde Software Corporation. All Rights Reserved.
|
|
;*
|
|
;* You may not reproduce, distribute, publish, display, perform, modify, adapt,
|
|
;* transmit, broadcast, present, recite, release, license or otherwise exploit
|
|
;* any part of this publication in any form, by any means, without the prior
|
|
;* written permission of Insyde Software Corporation.
|
|
;*
|
|
;******************************************************************************
|
|
*/
|
|
|
|
#include <H2OSetupChange.h>
|
|
|
|
H2O_SUBMIT_SVC_PROTOCOL gH2OSubmitSvcProtocol = {
|
|
ExecuteSubmitSvc
|
|
};
|
|
|
|
CHAR16 *mFullDiagTitleString = L"History is full, please set action:";
|
|
CHAR16 *mFullDiagActionList[SETUP_CHANGE_MAX_OPTION] = {
|
|
{L"Stop the history"},
|
|
{L"Clear all history"},
|
|
{L"Overwrite older history"}
|
|
};
|
|
EFI_HII_DATE mOrgDate = {0};
|
|
EFI_HII_DATE mModDate = {0};
|
|
CHAR16 *mDatePromptStr = NULL;
|
|
|
|
/**
|
|
Add the change information of Date into mQuestionChangedList.
|
|
|
|
@param[in] *ChangedQuestionList Pointer to the changed questions.
|
|
|
|
@retval EFI_SUCCESS Add the change information successfully.
|
|
@retval EFI_OUT_OF_RESOURCES Add failure when allocating pool.
|
|
|
|
*/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
CheckDateChangeInfo (
|
|
IN LIST_ENTRY *ChangedQuestionList
|
|
)
|
|
{
|
|
FORM_BROWSER_STATEMENT_CHANGED *Question;
|
|
CHAR16 *TempSettingStr;
|
|
|
|
if (mOrgDate.Year == 0 || mOrgDate.Month == 0 || mOrgDate.Day== 0) {
|
|
// The Original date is not initialized.
|
|
DEBUG ((EFI_D_ERROR, "No Setup Date changed.\n"));
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
Question = AllocateZeroPool (sizeof (FORM_BROWSER_STATEMENT_CHANGED));
|
|
if (Question == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
Question->Signature = FORM_BROWSER_STATEMENT_CHANGED_SIGNATURE;
|
|
TempSettingStr = CatSPrint (
|
|
NULL,
|
|
L"%s: [%d/%d/%d] to [%d/%d/%d] \n",
|
|
mDatePromptStr,
|
|
mOrgDate.Year, mOrgDate.Month, mOrgDate.Day,
|
|
mModDate.Year, mModDate.Month, mModDate.Day
|
|
);
|
|
DEBUG ((EFI_D_ERROR, "Setup Date changed infor: %s\n", TempSettingStr));
|
|
|
|
Question->ChangedInfoStr = TempSettingStr;
|
|
InsertTailList (ChangedQuestionList, &Question->Link);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
This function verifies the leap year.
|
|
|
|
@param[in] Year Year in YYYY format.
|
|
|
|
@retval TRUE The year is a leap year.
|
|
@retval FALSE The year is not a leap year.
|
|
*/
|
|
BOOLEAN
|
|
IsLeapYear (
|
|
IN UINT16 Year
|
|
)
|
|
{
|
|
if (Year % 4 == 0) {
|
|
if (Year % 100 == 0) {
|
|
if (Year % 400 == 0) {
|
|
return TRUE;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
return TRUE;
|
|
}
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Converts EFI_TIME structure to a TimeStamp.
|
|
|
|
@param[in] Time EFI_TIME structure to be converted.
|
|
@param[out] TimeStamp TimeStamp converted from EFI_TIME structure.
|
|
*/
|
|
VOID
|
|
EfiTimeToTimeStamp (
|
|
IN EFI_TIME *Time,
|
|
OUT UINT32 *TimeStamp
|
|
)
|
|
{
|
|
UINT16 Year;
|
|
UINT16 AddedDays;
|
|
UINT8 Month;
|
|
UINT32 DaysOfMonth[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
|
|
|
|
//
|
|
// Find number of leap years
|
|
//
|
|
AddedDays = 0;
|
|
for (Year = BASE_YEAR; Year < Time->Year; ++Year) {
|
|
if (IsLeapYear (Year)) {
|
|
++AddedDays;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Number of days of complete years (include all leap years)
|
|
//
|
|
*TimeStamp = (Time->Year - BASE_YEAR) * DAYS_PER_YEAR;
|
|
*TimeStamp += AddedDays;
|
|
|
|
//
|
|
// Number of days from 1970/1/1 to now
|
|
//
|
|
for (Month = 0; Month < Time->Month - BASE_MONTH; ++Month) {
|
|
*TimeStamp += DaysOfMonth[Month];
|
|
}
|
|
*TimeStamp += Time->Day - BASE_DAY;
|
|
|
|
//
|
|
// Check this Feb. is 28 days or 29 days
|
|
//
|
|
if (IsLeapYear (Time->Year) && Time->Month > 2) {
|
|
*TimeStamp += 1;
|
|
}
|
|
|
|
//
|
|
// Convert days to seconds
|
|
//
|
|
*TimeStamp *= SECONDS_PER_DAY;
|
|
|
|
//
|
|
// Add rest seconds
|
|
//
|
|
*TimeStamp += (Time->Hour * SECONDS_PER_HOUR) +
|
|
(Time->Minute * SECONDS_PER_MINUTE) +
|
|
Time->Second;
|
|
|
|
}
|
|
|
|
/**
|
|
Store all of the showing string to specific storage or EFI Variable.
|
|
|
|
@param[out] This A pointer to the showing string.
|
|
|
|
@retval EFI_SUCCESS The showing string has been stored.
|
|
@retval EFI_Status Otherwise.
|
|
*/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
StoreInformation (
|
|
IN CHAR16 *StringBuf
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
CHAR16 *VariableName = H2O_SETUP_CHANGE_VARIABLE_NAME;
|
|
UINTN OrgVariableDataSize;
|
|
UINT8 *OrgVariableData = NULL;
|
|
VOID *SetupChangeVarPool = NULL;
|
|
H2O_SETUP_CHANGE_VARIABLE *SetupChangeVar;
|
|
H2O_SETUP_CHANGE_VARIABLE *OrgSetupChangeVar;
|
|
UINTN SetupChangeVarDataSize = 0;
|
|
UINT32 TimeStamp;
|
|
EFI_TIME Time;
|
|
CHAR16 *NewUnicodeStr;
|
|
UINTN NewUnicodeStrSize;
|
|
UINT8 AdjustPolicy;
|
|
H2O_DIALOG_PROTOCOL *H2ODialog;
|
|
UINT32 SelectedIndex;
|
|
EFI_INPUT_KEY Key;
|
|
UINTN NeedVariableSize;
|
|
UINTN CountVariableSize = 0;
|
|
UINT8 *CopyAddr;
|
|
|
|
//
|
|
// Calculate new data size and check if it is over the max Setup Change variable size.
|
|
//
|
|
NewUnicodeStrSize = StrLen (StringBuf) * 2;
|
|
NewUnicodeStr = StringBuf;
|
|
NeedVariableSize = sizeof(SetupChangeVar->TimeStamp) + sizeof(SetupChangeVar->Size) + NewUnicodeStrSize;
|
|
if (NeedVariableSize > PcdGet32(PcdMaxSetupChangeVariableSize)) {
|
|
DEBUG ((EFI_D_ERROR, "Needed Setup Change information size is over the max Setup Change Variable size.\n"));
|
|
DEBUG ((EFI_D_ERROR, "Needed Setup Change information size: 0x%x\n", NeedVariableSize));
|
|
DEBUG ((EFI_D_ERROR, "PcdMaxSetupChangeVariableSize: 0x%x\n", PcdGet32(PcdMaxSetupChangeVariableSize)));
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
OrgVariableDataSize = 0;
|
|
Status = CommonGetVariableDataAndSize (
|
|
VariableName,
|
|
&gH2OSetupChangeVariableGuid,
|
|
&OrgVariableDataSize,
|
|
&OrgVariableData
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
//
|
|
// Find old variable data, and update it with new data..
|
|
//
|
|
|
|
//
|
|
// Calculate the total size, and check if it exceeds the maximum size.
|
|
//
|
|
SetupChangeVarDataSize = NeedVariableSize + OrgVariableDataSize;
|
|
if (SetupChangeVarDataSize >= PcdGet32(PcdMaxSetupChangeVariableSize)) {
|
|
DEBUG ((EFI_D_INFO, "Total Setup Change information size is over the max Setup Change Variable size..\n"));
|
|
|
|
if (PcdGet8 (PcdSetupChangeFullLogAdjustPolicy) == SETUP_CHANGE_DISPLAY_USE_DIALOG) {
|
|
//
|
|
// Get option for the policy: Overwrite, Clear, Do nothing.
|
|
//
|
|
Status = gBS->LocateProtocol (&gH2ODialogProtocolGuid, NULL, (VOID **) &H2ODialog);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((EFI_D_INFO, "Locate H2ODialog Protocol: %r \n", Status));
|
|
FreePool (OrgVariableData);
|
|
return Status;
|
|
}
|
|
H2ODialog->OneOfOptionDialog (
|
|
(UINT32)SETUP_CHANGE_MAX_OPTION,
|
|
FALSE,
|
|
NULL,
|
|
&Key,
|
|
SETUP_CHANGE_MAX_OPTION_STRING_SIZE,
|
|
mFullDiagTitleString,
|
|
&SelectedIndex,
|
|
(CHAR16 **) (mFullDiagActionList),
|
|
0
|
|
);
|
|
if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) {
|
|
AdjustPolicy = (UINT8)SelectedIndex;
|
|
} else {
|
|
//
|
|
// User doesn't select any action for over size, don't do anything and returen.
|
|
//
|
|
DEBUG ((EFI_D_INFO, "Total size is over the max Setup Change variable size, and keep the old data content\n"));
|
|
FreePool (OrgVariableData);
|
|
return EFI_SUCCESS;
|
|
}
|
|
} else {
|
|
AdjustPolicy = (UINT8)PcdGet8 (PcdSetupChangeFullLogAdjustPolicy);
|
|
}
|
|
|
|
switch (AdjustPolicy) {
|
|
case SETUP_CHANGE_DISPLAY_DO_NOTHING:
|
|
FreePool (OrgVariableData);
|
|
return EFI_SUCCESS;
|
|
break;
|
|
|
|
case SETUP_CHANGE_DISPLAY_CLEAR:
|
|
//
|
|
// Clear all old data, and only will store the new data.
|
|
//
|
|
SetupChangeVarDataSize -= OrgVariableDataSize;
|
|
OrgVariableDataSize = 0;
|
|
break;
|
|
|
|
case SETUP_CHANGE_DISPLAY_OVERWRITE:
|
|
NeedVariableSize = sizeof(SetupChangeVar->TimeStamp) + sizeof(SetupChangeVar->Size) + NewUnicodeStrSize;
|
|
CountVariableSize = 0;
|
|
OrgSetupChangeVar = (H2O_SETUP_CHANGE_VARIABLE *) OrgVariableData;
|
|
while (CountVariableSize < OrgVariableDataSize) {
|
|
if (OrgSetupChangeVar->Size == 0) {
|
|
//
|
|
// Variable may be destroyed, skip all old data and break.
|
|
//
|
|
CountVariableSize = 0;
|
|
break;
|
|
}
|
|
CountVariableSize += OrgSetupChangeVar->Size;
|
|
//
|
|
// Count the suitable size to get enough space to store new data.
|
|
// The oldest data will be skipped.
|
|
//
|
|
if ((PcdGet32(PcdMaxSetupChangeVariableSize) - CountVariableSize) < NeedVariableSize) {
|
|
CountVariableSize -= OrgSetupChangeVar->Size;
|
|
break;
|
|
}
|
|
OrgSetupChangeVar = (H2O_SETUP_CHANGE_VARIABLE *) (OrgVariableData + CountVariableSize);
|
|
}
|
|
|
|
//
|
|
// Final data content: (Need data) + (part of old data base on OrgVariableDataSize)
|
|
//
|
|
SetupChangeVarDataSize = NeedVariableSize + CountVariableSize;
|
|
OrgVariableDataSize = CountVariableSize;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
//
|
|
// Can't find old data from Setup Change Variable, just create new one with new data.
|
|
//
|
|
SetupChangeVarDataSize = NeedVariableSize;
|
|
}
|
|
|
|
if (SetupChangeVarDataSize == 0) {
|
|
if (OrgVariableData != NULL) {
|
|
FreePool (OrgVariableData);
|
|
}
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
SetupChangeVarPool = AllocateZeroPool (SetupChangeVarDataSize);
|
|
if (SetupChangeVarPool == NULL) {
|
|
DEBUG ((EFI_D_INFO, "AllocateZeroPool(): %r \n", Status));
|
|
if (OrgVariableData != NULL) {
|
|
FreePool (OrgVariableData);
|
|
}
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
Status = gRT->GetTime (&Time, NULL);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((EFI_D_INFO, "EfiGetTime(): %r \n", Status));
|
|
if (OrgVariableData != NULL) {
|
|
FreePool (OrgVariableData);
|
|
}
|
|
FreePool (SetupChangeVarPool);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
} else {
|
|
EfiTimeToTimeStamp (&Time, &TimeStamp);
|
|
DEBUG ((EFI_D_INFO, "TimeStamp: %x \n", TimeStamp));
|
|
}
|
|
|
|
SetupChangeVar = (H2O_SETUP_CHANGE_VARIABLE *)SetupChangeVarPool;
|
|
SetupChangeVar->TimeStamp = TimeStamp;
|
|
//
|
|
// Size value will base on the newest data, because each copy of data will have its own size value.
|
|
//
|
|
SetupChangeVar->Size = (UINT16) (sizeof(SetupChangeVar->TimeStamp) +
|
|
sizeof(SetupChangeVar->Size) +
|
|
NewUnicodeStrSize
|
|
);
|
|
CopyMem (&SetupChangeVar->Data, NewUnicodeStr, NewUnicodeStrSize);
|
|
if (OrgVariableDataSize != 0) {
|
|
CopyAddr = (UINT8*) &(SetupChangeVar->Data);
|
|
CopyAddr += NewUnicodeStrSize;
|
|
CopyMem (CopyAddr, OrgVariableData, OrgVariableDataSize);
|
|
FreePool (OrgVariableData);
|
|
}
|
|
|
|
Status = CommonSetVariable (
|
|
VariableName,
|
|
&gH2OSetupChangeVariableGuid,
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
|
|
SetupChangeVarDataSize,
|
|
SetupChangeVar
|
|
);
|
|
DEBUG ((EFI_D_INFO, "CommonSetVariable() Setup Change Variable: %r \n", Status));
|
|
FreePool (SetupChangeVarPool);
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
/**
|
|
Contruct the showing string for the specified question data.
|
|
|
|
@param[in] This A pointer to the specified question data.
|
|
@param[out] This A pointer to the showing string.
|
|
|
|
@retval EFI_SUCCESS The showing string has been contructed.
|
|
@retval EFI_Status Otherwise.
|
|
*/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
ContructString (
|
|
IN H2O_FORM_BROWSER_Q *QuestionData,
|
|
OUT CHAR16 **StringBuf
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
H2O_FORM_BROWSER_PROTOCOL *FBProtocol;
|
|
UINT32 Index;
|
|
UINTN VarStoreCount;
|
|
EFI_VARSTORE_ID *VarStoreIdBuffer;
|
|
H2O_FORM_BROWSER_VS *VarbleStoreData;
|
|
UINT32 OptionIndex;
|
|
UINT64 OrgValue;
|
|
UINT64 ModValue;
|
|
CHAR16 *OrgStr;
|
|
CHAR16 *ModStr;
|
|
CHAR16 *OptionStr;
|
|
UINTN StringSize;
|
|
UINT8 Width;
|
|
EFI_HII_TIME *OrgTime;
|
|
EFI_HII_TIME *ModTime;
|
|
|
|
ASSERT (StringBuf != NULL);
|
|
if ((QuestionData->Prompt == NULL) || (StrLen(QuestionData->Prompt) == 0)) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
Status = gBS->LocateProtocol (&gH2OFormBrowserProtocolGuid, NULL, (VOID **) &FBProtocol);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = FBProtocol->GetVSAll (FBProtocol, QuestionData->PageId, &VarStoreCount, &VarStoreIdBuffer);
|
|
if (EFI_ERROR (Status)) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
VarbleStoreData = NULL;
|
|
for (Index = 0; Index < VarStoreCount; Index++) {
|
|
if (VarStoreIdBuffer[Index] == QuestionData->VarStoreId) {
|
|
Status = FBProtocol->GetVSInfo (
|
|
FBProtocol,
|
|
QuestionData->PageId,
|
|
QuestionData->VarStoreId,
|
|
&VarbleStoreData
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
if (Index == VarStoreCount || EFI_ERROR (Status)) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
*StringBuf = NULL;
|
|
|
|
switch (QuestionData->Operand) {
|
|
|
|
case EFI_IFR_ONE_OF_OP:
|
|
OrgStr = NULL;
|
|
ModStr = NULL;
|
|
OrgValue = VarbleStoreData->Buffer[QuestionData->VariableOffset];
|
|
ModValue = VarbleStoreData->EditBuffer[QuestionData->VariableOffset];
|
|
|
|
for (Index = 0; Index < QuestionData->NumberOfOptions; Index++) {
|
|
if (QuestionData->Options[Index].HiiValue.Value.u8 == OrgValue) {
|
|
OrgStr = QuestionData->Options[Index].Text;
|
|
}
|
|
|
|
if (QuestionData->Options[Index].HiiValue.Value.u8 == ModValue) {
|
|
ModStr = QuestionData->Options[Index].Text;
|
|
}
|
|
|
|
if (OrgStr != NULL && ModStr != NULL) {
|
|
*StringBuf = CatSPrint (NULL, L"%s: [%s] to [%s]\n", QuestionData->Prompt, OrgStr, ModStr);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EFI_IFR_NUMERIC_OP:
|
|
switch (QuestionData->Flags & EFI_IFR_NUMERIC_SIZE) {
|
|
|
|
case EFI_IFR_NUMERIC_SIZE_1:
|
|
OrgValue = (UINT64) (*((UINT8 *) (VarbleStoreData->Buffer + QuestionData->VariableOffset)));
|
|
ModValue = (UINT64) QuestionData->HiiValue.Value.u8;
|
|
break;
|
|
|
|
case EFI_IFR_NUMERIC_SIZE_2:
|
|
OrgValue = (UINT64) (*((UINT16 *) (VarbleStoreData->Buffer + QuestionData->VariableOffset)));
|
|
ModValue = (UINT64) QuestionData->HiiValue.Value.u16;
|
|
break;
|
|
|
|
case EFI_IFR_NUMERIC_SIZE_4:
|
|
OrgValue = (UINT64) (*((UINT32 *) (VarbleStoreData->Buffer + QuestionData->VariableOffset)));
|
|
ModValue = (UINT64) QuestionData->HiiValue.Value.u32;
|
|
break;
|
|
|
|
case EFI_IFR_NUMERIC_SIZE_8:
|
|
OrgValue = *((UINT64 *) (VarbleStoreData->Buffer + QuestionData->VariableOffset));
|
|
ModValue = QuestionData->HiiValue.Value.u64;
|
|
break;
|
|
|
|
default:
|
|
OrgValue = 0;
|
|
ModValue = 0;
|
|
break;
|
|
}
|
|
|
|
if (OrgValue != ModValue) {
|
|
*StringBuf = CatSPrint (
|
|
NULL,
|
|
(QuestionData->Flags & EFI_IFR_DISPLAY_UINT_HEX) ? L"%s: [0x%x] to [0x%x]\n" : L"%s: [%d] to [%d]\n",
|
|
QuestionData->Prompt,
|
|
OrgValue,
|
|
ModValue
|
|
);
|
|
}
|
|
break;
|
|
|
|
case EFI_IFR_ORDERED_LIST_OP:
|
|
if (QuestionData->NumberOfOptions < 2) {
|
|
//
|
|
// Not available number of options,
|
|
// At least need 2 options in ordered list.
|
|
//
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
// Use Option's value type to determine the width as the behavior of IfrParse.c.
|
|
Width = 1;
|
|
switch (QuestionData->Options[0].HiiValue.Type) {
|
|
|
|
case EFI_IFR_TYPE_NUM_SIZE_8:
|
|
Width = 1;
|
|
break;
|
|
|
|
case EFI_IFR_TYPE_NUM_SIZE_16:
|
|
Width = 2;
|
|
break;
|
|
|
|
case EFI_IFR_TYPE_NUM_SIZE_32:
|
|
Width = 4;
|
|
break;
|
|
|
|
case EFI_IFR_TYPE_NUM_SIZE_64:
|
|
Width = 8;
|
|
break;
|
|
|
|
default:
|
|
//
|
|
// Invalid type for Ordered List
|
|
//
|
|
break;
|
|
}
|
|
|
|
for (Index = 0, StringSize = 0; Index < QuestionData->NumberOfOptions; Index++) {
|
|
StringSize += (StrSize (QuestionData->Options[Index].Text) + sizeof (L"<> "));
|
|
}
|
|
OrgStr = AllocateZeroPool (StringSize);
|
|
ModStr = AllocateZeroPool (StringSize);
|
|
OptionStr = AllocateZeroPool (StringSize);
|
|
if (OrgStr == NULL || ModStr == NULL || OptionStr == NULL) {
|
|
break;
|
|
}
|
|
|
|
for (Index = 0, OrgValue = 0, ModValue = 0; Index < QuestionData->ContainerCount; Index++) {
|
|
CopyMem (&OrgValue, VarbleStoreData->Buffer + QuestionData->VariableOffset + Index * Width, Width);
|
|
CopyMem (&ModValue, VarbleStoreData->EditBuffer + QuestionData->VariableOffset + Index * Width, Width);
|
|
|
|
for (OptionIndex = 0; OptionIndex < QuestionData->NumberOfOptions; OptionIndex++) {
|
|
if (CompareMem (&QuestionData->Options[OptionIndex].HiiValue.Value.u64, &OrgValue, Width) == 0) {
|
|
UnicodeSPrint (OptionStr, StringSize, L"<%s> ", QuestionData->Options[OptionIndex].Text);
|
|
StrCatS (OrgStr, StringSize / sizeof(CHAR16), OptionStr);
|
|
}
|
|
if (CompareMem (&QuestionData->Options[OptionIndex].HiiValue.Value.u64, &ModValue, Width) == 0) {
|
|
UnicodeSPrint (OptionStr, StringSize, L"<%s> ", QuestionData->Options[OptionIndex].Text);
|
|
StrCatS (ModStr, StringSize / sizeof(CHAR16), OptionStr);
|
|
}
|
|
}
|
|
}
|
|
|
|
*StringBuf = CatSPrint (
|
|
NULL,
|
|
L"%s: [%s] to [%s]\n",
|
|
QuestionData->Prompt,
|
|
OrgStr,
|
|
ModStr
|
|
);
|
|
FreePool (OrgStr);
|
|
FreePool (ModStr);
|
|
FreePool (OptionStr);
|
|
break;
|
|
|
|
case EFI_IFR_CHECKBOX_OP:
|
|
*StringBuf = CatSPrint (
|
|
NULL,
|
|
L"%s: [%s] to [%s]\n",
|
|
QuestionData->Prompt,
|
|
(VarbleStoreData->Buffer[QuestionData->VariableOffset] == 0) ? L"Disabled" : L"Enabled",
|
|
(VarbleStoreData->EditBuffer[QuestionData->VariableOffset] == 0) ? L"Disabled" : L"Enabled"
|
|
);
|
|
break;
|
|
|
|
case EFI_IFR_STRING_OP:
|
|
*StringBuf = CatSPrint (
|
|
NULL,
|
|
L"%s: [%s] to [%s]\n",
|
|
QuestionData->Prompt,
|
|
(CHAR16 *) &VarbleStoreData->Buffer[QuestionData->VariableOffset],
|
|
QuestionData->HiiValue.Buffer
|
|
);
|
|
break;
|
|
|
|
case EFI_IFR_TIME_OP:
|
|
OrgTime = (EFI_HII_TIME *)&VarbleStoreData->Buffer[QuestionData->VariableOffset];
|
|
ModTime = (EFI_HII_TIME *)&QuestionData->HiiValue.Value.time;
|
|
*StringBuf = CatSPrint (
|
|
NULL,
|
|
L"%s: [%d:%d:%d] to [%d:%d:%d]\n",
|
|
QuestionData->Prompt,
|
|
OrgTime->Hour, OrgTime->Minute, OrgTime->Second,
|
|
ModTime->Hour, ModTime->Minute, ModTime->Second
|
|
);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (*StringBuf == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Remove the all question nodes from mQuestionChangedList.
|
|
|
|
@param[in] None.
|
|
|
|
@retval EFI_SUCCESS Remove the node successfully.
|
|
*/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
RemoveQuestionChangedList (
|
|
IN LIST_ENTRY *ChangedQuestionList
|
|
)
|
|
{
|
|
LIST_ENTRY *Link;
|
|
FORM_BROWSER_STATEMENT_CHANGED *QuestionNode;
|
|
|
|
Link = GetFirstNode (ChangedQuestionList);
|
|
while (!IsNull (ChangedQuestionList, Link)) {
|
|
QuestionNode = FORM_BROWSER_STATEMENT_CHANGED_FROM_LINK (Link);
|
|
Link = GetNextNode (ChangedQuestionList, Link);
|
|
|
|
RemoveEntryList (&QuestionNode->Link);
|
|
FreePool (QuestionNode->ChangedInfoStr);
|
|
FreePool (QuestionNode);
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
}
|
|
|
|
/**
|
|
Add the question node into mQuestionChangedList.
|
|
|
|
@param[in] QuestionData A pointer to the specified question.
|
|
|
|
@retval EFI_SUCCESS Add the question node successfully.
|
|
@retval EFI_OUT_OF_RESOURCES Add failure when allocating pool.
|
|
|
|
*/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
AddToChangedList (
|
|
IN LIST_ENTRY *ChangedQuestionList,
|
|
IN H2O_FORM_BROWSER_Q *QuestionData
|
|
)
|
|
{
|
|
FORM_BROWSER_STATEMENT_CHANGED *Question;
|
|
CHAR16 *TempStringPtr;
|
|
EFI_STATUS Status;
|
|
|
|
Status = ContructString (QuestionData, &TempStringPtr);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Question = AllocateZeroPool (sizeof (FORM_BROWSER_STATEMENT_CHANGED));
|
|
if (Question == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
Question->Signature = FORM_BROWSER_STATEMENT_CHANGED_SIGNATURE;
|
|
CopyMem (&(Question->Question), QuestionData, sizeof(H2O_FORM_BROWSER_Q));
|
|
|
|
Question->ChangedInfoStr = TempStringPtr;
|
|
InsertTailList (ChangedQuestionList, &Question->Link);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Execute the Submit service function.
|
|
|
|
@param[in] This A pointer to the H2O_SUBMIT_SVC_PROTOCOL instance.
|
|
@param[out] Request A pointer to the request from the Submit service functions.
|
|
Related definition can refer to "Browser actions" of FormBrowserEx.h.
|
|
@param[out] ShowSubmitDialog A pointer to the value if needing to show the original submit dialog.
|
|
|
|
@retval EFI_SUCCESS Execute the Submit service functions successfully.
|
|
@retval EFI_Status Otherwise.
|
|
*/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
ExecuteSubmitSvc (
|
|
IN H2O_SUBMIT_SVC_PROTOCOL *This,
|
|
OUT UINT32 *Request,
|
|
OUT BOOLEAN *ShowSubmitDialog
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
LIST_ENTRY *Link;
|
|
FORM_BROWSER_STATEMENT_CHANGED *QuestionNode;
|
|
CHAR16 *TitleString = L"BIOS Changed Setting:";
|
|
CHAR16 *ConfirmString = L"Save the changes?";
|
|
CHAR16 *ConfirmString2 = L"Exit the Setup?";
|
|
CHAR16 *NoChangeString = L"There is no changed item.";
|
|
CHAR16 *StringPtr;
|
|
H2O_DIALOG_PROTOCOL *H2ODialog;
|
|
H2O_FORM_BROWSER_PROTOCOL *FBProtocol;
|
|
UINT32 ChangedQuestionCount;
|
|
H2O_FORM_BROWSER_Q *ChangedQuestionBuffer;
|
|
UINT32 QuestionIndex;
|
|
UINTN StringLength;
|
|
EFI_INPUT_KEY Key;
|
|
LIST_ENTRY ChangedQuestionList;
|
|
|
|
Status = gBS->LocateProtocol (&gH2ODialogProtocolGuid, NULL, (VOID **) &H2ODialog);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = gBS->LocateProtocol (&gH2OFormBrowserProtocolGuid, NULL, (VOID **) &FBProtocol);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = FBProtocol->GetChangedQuestions (FBProtocol, &ChangedQuestionCount, &ChangedQuestionBuffer);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
InitializeListHead (&ChangedQuestionList);
|
|
|
|
for (QuestionIndex = 0; QuestionIndex < ChangedQuestionCount; QuestionIndex++) {
|
|
AddToChangedList (&ChangedQuestionList, &ChangedQuestionBuffer[QuestionIndex]);
|
|
}
|
|
|
|
CheckDateChangeInfo (&ChangedQuestionList);
|
|
|
|
if (IsListEmpty (&ChangedQuestionList)) {
|
|
Status = H2ODialog->ConfirmPageDialog (
|
|
DlgYesNo,
|
|
TitleString,
|
|
ConfirmString2,
|
|
NoChangeString,
|
|
&Key
|
|
);
|
|
if (EFI_ERROR (Status) || Key.UnicodeChar != CHAR_CARRIAGE_RETURN) {
|
|
*Request = BROWSER_ACTION_NONE;
|
|
} else {
|
|
*Request = BROWSER_ACTION_SUBMIT | BROWSER_ACTION_RESET;
|
|
}
|
|
} else {
|
|
StringLength = 0;
|
|
Link = GetFirstNode (&ChangedQuestionList);
|
|
while (!IsNull (&ChangedQuestionList, Link)) {
|
|
QuestionNode = FORM_BROWSER_STATEMENT_CHANGED_FROM_LINK (Link);
|
|
Link = GetNextNode (&ChangedQuestionList, Link);
|
|
StringLength += StrLen(QuestionNode->ChangedInfoStr);
|
|
}
|
|
|
|
StringPtr = AllocateZeroPool (sizeof (CHAR16) * (StringLength + 1));
|
|
if (StringPtr == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
Link = GetFirstNode (&ChangedQuestionList);
|
|
while (!IsNull (&ChangedQuestionList, Link)) {
|
|
QuestionNode = FORM_BROWSER_STATEMENT_CHANGED_FROM_LINK (Link);
|
|
Link = GetNextNode (&ChangedQuestionList, Link);
|
|
StrCatS (StringPtr, StringLength + 1, QuestionNode->ChangedInfoStr);
|
|
}
|
|
|
|
Status = H2ODialog->ConfirmPageDialog (
|
|
DlgYesNo,
|
|
TitleString,
|
|
ConfirmString,
|
|
StringPtr,
|
|
&Key
|
|
);
|
|
if (EFI_ERROR (Status) || Key.UnicodeChar != CHAR_CARRIAGE_RETURN) {
|
|
*Request = BROWSER_ACTION_NONE;
|
|
} else {
|
|
*Request = BROWSER_ACTION_SUBMIT | BROWSER_ACTION_RESET;
|
|
StoreInformation (StringPtr);
|
|
// Clear the variable because the user has saved the setup changes.
|
|
CommonSetVariable (
|
|
DATE_CHANGE_INFO_VAR_NAME,
|
|
&gH2OSetupChangeVariableGuid,
|
|
0,
|
|
0,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
FreePool (StringPtr);
|
|
RemoveQuestionChangedList (&ChangedQuestionList);
|
|
}
|
|
*ShowSubmitDialog = FALSE;
|
|
|
|
if (ChangedQuestionCount != 0) {
|
|
FreePool (ChangedQuestionBuffer);
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Log the change information for Date.
|
|
|
|
@param[in] Prompt A pointer to the prompt string of Date question.
|
|
@param[in] OrgDate A pointer to the original date.
|
|
@param[in] ModDate A pointer to the modified date.
|
|
|
|
@retval EFI_SUCCESS Execute the Log Date Change functions successfully.
|
|
@retval EFI_Status Otherwise.
|
|
*/
|
|
EFI_STATUS
|
|
LogDateChange (
|
|
IN CHAR16 *Prompt,
|
|
IN VOID *OrgDate,
|
|
IN VOID *ModDate
|
|
)
|
|
{
|
|
EFI_STATUS Status = EFI_UNSUPPORTED;
|
|
DATE_CHANGED_INFO *DateChangeInfo;
|
|
UINTN DateChangeInfoSize;
|
|
|
|
if (mOrgDate.Year == 0 || mOrgDate.Month == 0 || mOrgDate.Day== 0) {
|
|
// Initialize the Original date for the first time.
|
|
CopyMem (&mOrgDate, OrgDate, sizeof(EFI_HII_DATE));
|
|
}
|
|
|
|
CopyMem (&mModDate, ModDate, sizeof(EFI_HII_DATE));
|
|
|
|
if (mDatePromptStr != NULL) {
|
|
FreePool (mDatePromptStr);
|
|
mDatePromptStr = NULL;
|
|
}
|
|
|
|
//
|
|
// Check if the org date is the same as mod date.
|
|
//
|
|
if (CompareMem (&mOrgDate, &mModDate, sizeof(EFI_HII_DATE)) == 0) {
|
|
DEBUG ((EFI_D_ERROR, "No Setup Date changed.\n"));
|
|
ZeroMem (&mOrgDate, sizeof(EFI_HII_DATE));
|
|
// Delete the related variable.
|
|
Status = CommonSetVariable (
|
|
DATE_CHANGE_INFO_VAR_NAME,
|
|
&gH2OSetupChangeVariableGuid,
|
|
0,
|
|
0,
|
|
NULL
|
|
);
|
|
DEBUG ((EFI_D_ERROR, "Delete Date Change variable: %r \n", Status));
|
|
DEBUG ((EFI_D_ERROR, "LogDateChange End \n"));
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
mDatePromptStr = AllocateZeroPool (sizeof(CHAR16) * (StrLen(Prompt)+1));
|
|
if (mDatePromptStr != NULL) {
|
|
CopyMem (mDatePromptStr, Prompt, sizeof(CHAR16) * StrLen(Prompt));
|
|
}
|
|
|
|
DateChangeInfoSize = sizeof (EFI_HII_DATE) + sizeof(CHAR16)*(StrLen(Prompt)+1);
|
|
DateChangeInfo = AllocateZeroPool (DateChangeInfoSize);
|
|
if (DateChangeInfo != NULL) {
|
|
CopyMem (&(DateChangeInfo->OrgDate), &mOrgDate, sizeof(EFI_HII_DATE));
|
|
|
|
// Set a variable to store the information for next boot using.
|
|
Status = CommonSetVariable (
|
|
DATE_CHANGE_INFO_VAR_NAME,
|
|
&gH2OSetupChangeVariableGuid,
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
|
|
DateChangeInfoSize,
|
|
DateChangeInfo
|
|
);
|
|
DEBUG ((EFI_D_ERROR, "Set variable %s : %r\n", DATE_CHANGE_INFO_VAR_NAME, Status));
|
|
FreePool (DateChangeInfo);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Restore the change information for Date.
|
|
|
|
@param[in] VOID
|
|
|
|
@retval EFI_SUCCESS Execute the Restore Date Change functions successfully.
|
|
@retval EFI_Status Otherwise.
|
|
*/
|
|
EFI_STATUS
|
|
RestoreDateChange (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status = EFI_UNSUPPORTED;
|
|
EFI_TIME EfiTime;
|
|
|
|
if (mOrgDate.Year == 0 || mOrgDate.Month == 0 || mOrgDate.Day== 0) {
|
|
// No original Date information.
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Need set the Setup time because user may not accept the Setup time which is set by last boot.
|
|
//
|
|
Status = gRT->GetTime (&EfiTime, NULL);
|
|
if (!EFI_ERROR (Status)) {
|
|
EfiTime.Year = mOrgDate.Year;
|
|
EfiTime.Month= mOrgDate.Month;
|
|
EfiTime.Day = mOrgDate.Day;
|
|
Status = gRT->SetTime (&EfiTime);
|
|
if (!EFI_ERROR (Status)) {
|
|
DEBUG ((EFI_D_ERROR, "Set Original Setup time to %d/%d/%d : %r\n", EfiTime.Year, EfiTime.Month, EfiTime.Day, Status));
|
|
ZeroMem (&mOrgDate, sizeof(EFI_HII_DATE));
|
|
ZeroMem (&mModDate, sizeof(EFI_HII_DATE));
|
|
}
|
|
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
|
|
This function is a handler for BIOS to handle Setup Change.
|
|
|
|
(See Tiano Runtime Specification)
|
|
|
|
@retval EFI Status
|
|
*/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
SetupChangeStatusCode (
|
|
IN EFI_STATUS_CODE_TYPE CodeType,
|
|
IN EFI_STATUS_CODE_VALUE CodeValue,
|
|
IN UINT32 Instance OPTIONAL,
|
|
IN EFI_GUID * CallerId OPTIONAL,
|
|
IN EFI_STATUS_CODE_DATA * Data OPTIONAL
|
|
)
|
|
{
|
|
EFI_STATUS Status = EFI_UNSUPPORTED;
|
|
SETUP_DATE_CHANGE_STATUS_CODE_MESSAGE *StatusCodeData;
|
|
SETUP_DATE_CHANGE *SetupDateChange;
|
|
|
|
if (Data == NULL) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
StatusCodeData = (SETUP_DATE_CHANGE_STATUS_CODE_MESSAGE *) Data;
|
|
if (StatusCodeData->DataHeader.HeaderSize == 0) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
if ((CompareGuid (&(StatusCodeData->DataHeader.Type), &gH2OSetupChangeStatusCodeGuid) == TRUE) &&
|
|
(CodeType == EFI_PROGRESS_CODE) &&
|
|
(CodeValue == (EFI_SOFTWARE_DXE_RT_DRIVER | EFI_SW_RS_PC_SET_TIME))) {
|
|
SetupDateChange = (SETUP_DATE_CHANGE *) &(StatusCodeData->DateData);
|
|
|
|
if (SetupDateChange->SetTime == TRUE) {
|
|
//
|
|
// Log the time change.
|
|
//
|
|
Status = LogDateChange(SetupDateChange->Prompt, &(SetupDateChange->OrgDate), &(SetupDateChange->ModDate));
|
|
return Status;
|
|
} else {
|
|
//
|
|
// Restore the time change.
|
|
//
|
|
Status = RestoreDateChange ();
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Entry point of this driver. Install H2O Setup Change protocol into DXE.
|
|
|
|
@param[in] ImageHandle Image handle of this driver.
|
|
@param[in] SystemTable Global system service table.
|
|
|
|
@retval EFI Status
|
|
*/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
H2OSetupChangeDxeEntry (
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_RSC_HANDLER_PROTOCOL *RscHandlerProtocol = NULL;
|
|
UINTN DateChangeInfoDataSize;
|
|
DATE_CHANGED_INFO *DateChangeInfoData = NULL;
|
|
EFI_TIME EfiTime;
|
|
|
|
//
|
|
// Install H2OSetupChange protocol
|
|
//
|
|
Status = gBS->InstallMultipleProtocolInterfaces (
|
|
&ImageHandle,
|
|
&gH2OSubmitSvcProtocolGuid,
|
|
&gH2OSubmitSvcProtocol,
|
|
NULL
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = gBS->LocateProtocol (
|
|
&gEfiRscHandlerProtocolGuid,
|
|
NULL,
|
|
(VOID **) &RscHandlerProtocol
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
//
|
|
// Register the worker function to ReportStatusCodeRouter
|
|
//
|
|
Status = RscHandlerProtocol->Register (SetupChangeStatusCode, TPL_HIGH_LEVEL);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
Status = CommonGetVariableDataAndSize (
|
|
DATE_CHANGE_INFO_VAR_NAME,
|
|
&gH2OSetupChangeVariableGuid,
|
|
&DateChangeInfoDataSize,
|
|
&DateChangeInfoData
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
//
|
|
// Need set the Setup time because user may not accept the Setup time which is set by last boot.
|
|
//
|
|
Status = gRT->GetTime (&EfiTime, NULL);
|
|
if (!EFI_ERROR (Status)) {
|
|
EfiTime.Year = DateChangeInfoData->OrgDate.Year;
|
|
EfiTime.Month= DateChangeInfoData->OrgDate.Month;
|
|
EfiTime.Day = DateChangeInfoData->OrgDate.Day;
|
|
Status = gRT->SetTime (&EfiTime);
|
|
DEBUG ((EFI_D_ERROR, "Set Original Setup time to %d/%d/%d : %r\n", EfiTime.Year, EfiTime.Month, EfiTime.Day, Status));
|
|
}
|
|
}
|
|
|
|
return EFI_SUCCESS ;
|
|
}
|