alder_lake_bios/Insyde/InsydeSetupPkg/Drivers/H2OSetupChangeDxe/H2OSetupChange.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 ;
}