/** @file Var Check Hii handler. Copyright (c) 2015 - 2017, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "VarCheckHii.h" GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 mVarCheckHiiHex[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; /** Dump some hexadecimal data. @param[in] Indent How many spaces to indent the output. @param[in] Offset The offset of the dump. @param[in] DataSize The size in bytes of UserData. @param[in] UserData The data to dump. **/ VOID VarCheckHiiInternalDumpHex ( IN UINTN Indent, IN UINTN Offset, IN UINTN DataSize, IN VOID *UserData ) { UINT8 *Data; CHAR8 Val[50]; CHAR8 Str[20]; UINT8 TempByte; UINTN Size; UINTN Index; Data = UserData; while (DataSize != 0) { Size = 16; if (Size > DataSize) { Size = DataSize; } for (Index = 0; Index < Size; Index += 1) { TempByte = Data[Index]; Val[Index * 3 + 0] = mVarCheckHiiHex[TempByte >> 4]; Val[Index * 3 + 1] = mVarCheckHiiHex[TempByte & 0xF]; Val[Index * 3 + 2] = (CHAR8)((Index == 7) ? '-' : ' '); Str[Index] = (CHAR8)((TempByte < ' ' || TempByte > 'z') ? '.' : TempByte); } Val[Index * 3] = 0; Str[Index] = 0; DEBUG ((DEBUG_INFO, "%*a%08X: %-48a *%a*\r\n", Indent, "", Offset, Val, Str)); Data += Size; Offset += Size; DataSize -= Size; } } /** Var Check Hii Question. @param[in] HiiQuestion Pointer to Hii Question @param[in] Data Data pointer. @param[in] DataSize Size of Data to set. @retval TRUE Check pass @retval FALSE Check fail. **/ BOOLEAN VarCheckHiiQuestion ( IN VAR_CHECK_HII_QUESTION_HEADER *HiiQuestion, IN VOID *Data, IN UINTN DataSize ) { UINT64 OneData; UINT64 Minimum; UINT64 Maximum; UINT64 OneValue; UINT8 *Ptr; UINT8 Index; UINT8 MaxContainers; UINT8 StartBit; UINT8 EndBit; UINT8 TotalBits; UINT16 VarOffsetByteLevel; UINT8 StorageWidthByteLevel; if (HiiQuestion->BitFieldStore) { VarOffsetByteLevel = HiiQuestion->VarOffset / 8; TotalBits = HiiQuestion->VarOffset % 8 + HiiQuestion->StorageWidth; StorageWidthByteLevel = (TotalBits % 8 == 0 ? TotalBits / 8 : TotalBits / 8 + 1); } else { VarOffsetByteLevel = HiiQuestion->VarOffset; StorageWidthByteLevel = HiiQuestion->StorageWidth; } if (((UINT32)VarOffsetByteLevel + StorageWidthByteLevel) > DataSize) { DEBUG ((DEBUG_INFO, "VarCheckHiiQuestion fail: (VarOffset(0x%04x) + StorageWidth(0x%02x)) > Size(0x%x)\n", VarOffsetByteLevel, StorageWidthByteLevel, DataSize)); return FALSE; } OneData = 0; CopyMem (&OneData, (UINT8 *)Data + VarOffsetByteLevel, StorageWidthByteLevel); if (HiiQuestion->BitFieldStore) { // // Get the value from the bit field. // StartBit = HiiQuestion->VarOffset % 8; EndBit = StartBit + HiiQuestion->StorageWidth - 1; OneData = BitFieldRead64 (OneData, StartBit, EndBit); } switch (HiiQuestion->OpCode) { case EFI_IFR_ONE_OF_OP: Ptr = (UINT8 *)((VAR_CHECK_HII_QUESTION_ONEOF *)HiiQuestion + 1); while ((UINTN)Ptr < (UINTN)HiiQuestion + HiiQuestion->Length) { OneValue = 0; if (HiiQuestion->BitFieldStore) { // // For OneOf stored in bit field, the value of options are saved as UINT32 type. // CopyMem (&OneValue, Ptr, sizeof (UINT32)); } else { CopyMem (&OneValue, Ptr, HiiQuestion->StorageWidth); } if (OneData == OneValue) { // // Match // break; } if (HiiQuestion->BitFieldStore) { Ptr += sizeof (UINT32); } else { Ptr += HiiQuestion->StorageWidth; } } if ((UINTN)Ptr >= ((UINTN)HiiQuestion + HiiQuestion->Length)) { // // No match // DEBUG ((DEBUG_INFO, "VarCheckHiiQuestion fail: OneOf mismatch (0x%lx)\n", OneData)); DEBUG_CODE ( VarCheckHiiInternalDumpHex (2, 0, HiiQuestion->Length, (UINT8 *)HiiQuestion); ); return FALSE; } break; case EFI_IFR_CHECKBOX_OP: if ((OneData != 0) && (OneData != 1)) { DEBUG ((DEBUG_INFO, "VarCheckHiiQuestion fail: CheckBox mismatch (0x%lx)\n", OneData)); DEBUG_CODE ( VarCheckHiiInternalDumpHex (2, 0, HiiQuestion->Length, (UINT8 *)HiiQuestion); ); return FALSE; } break; case EFI_IFR_NUMERIC_OP: Minimum = 0; Maximum = 0; Ptr = (UINT8 *)((VAR_CHECK_HII_QUESTION_NUMERIC *)HiiQuestion + 1); if (HiiQuestion->BitFieldStore) { // // For Numeric stored in bit field, the value of Maximum/Minimum are saved as UINT32 type. // CopyMem (&Minimum, Ptr, sizeof (UINT32)); Ptr += sizeof (UINT32); CopyMem (&Maximum, Ptr, sizeof (UINT32)); Ptr += sizeof (UINT32); } else { CopyMem (&Minimum, Ptr, HiiQuestion->StorageWidth); Ptr += HiiQuestion->StorageWidth; CopyMem (&Maximum, Ptr, HiiQuestion->StorageWidth); Ptr += HiiQuestion->StorageWidth; } // // No need to check Step, because it is ONLY for UI. // if ((OneData < Minimum) || (OneData > Maximum)) { DEBUG ((DEBUG_INFO, "VarCheckHiiQuestion fail: Numeric mismatch (0x%lx)\n", OneData)); DEBUG_CODE ( VarCheckHiiInternalDumpHex (2, 0, HiiQuestion->Length, (UINT8 *)HiiQuestion); ); return FALSE; } break; case EFI_IFR_ORDERED_LIST_OP: MaxContainers = ((VAR_CHECK_HII_QUESTION_ORDEREDLIST *)HiiQuestion)->MaxContainers; if (((UINT32)HiiQuestion->VarOffset + HiiQuestion->StorageWidth * MaxContainers) > DataSize) { DEBUG ((DEBUG_INFO, "VarCheckHiiQuestion fail: (VarOffset(0x%04x) + StorageWidth(0x%02x) * MaxContainers(0x%02x)) > Size(0x%x)\n", HiiQuestion->VarOffset, HiiQuestion->StorageWidth, MaxContainers, DataSize)); return FALSE; } for (Index = 0; Index < MaxContainers; Index++) { OneData = 0; CopyMem (&OneData, (UINT8 *)Data + HiiQuestion->VarOffset + HiiQuestion->StorageWidth * Index, HiiQuestion->StorageWidth); if (OneData == 0) { // // The value of 0 is used to determine if a particular "slot" in the array is empty. // continue; } Ptr = (UINT8 *)((VAR_CHECK_HII_QUESTION_ORDEREDLIST *)HiiQuestion + 1); while ((UINTN)Ptr < ((UINTN)HiiQuestion + HiiQuestion->Length)) { OneValue = 0; CopyMem (&OneValue, Ptr, HiiQuestion->StorageWidth); if (OneData == OneValue) { // // Match // break; } Ptr += HiiQuestion->StorageWidth; } if ((UINTN)Ptr >= ((UINTN)HiiQuestion + HiiQuestion->Length)) { // // No match // DEBUG ((DEBUG_INFO, "VarCheckHiiQuestion fail: OrderedList mismatch\n")); DEBUG_CODE ( VarCheckHiiInternalDumpHex (2, 0, HiiQuestion->StorageWidth * MaxContainers, (UINT8 *)Data + HiiQuestion->VarOffset); ); DEBUG_CODE ( VarCheckHiiInternalDumpHex (2, 0, HiiQuestion->Length, (UINT8 *)HiiQuestion); ); return FALSE; } } break; default: ASSERT (FALSE); break; } return TRUE; } VAR_CHECK_HII_VARIABLE_HEADER *mVarCheckHiiBin = NULL; UINTN mVarCheckHiiBinSize = 0; /** SetVariable check handler HII. @param[in] VariableName Name of Variable to set. @param[in] VendorGuid Variable vendor GUID. @param[in] Attributes Attribute value of the variable. @param[in] DataSize Size of Data to set. @param[in] Data Data pointer. @retval EFI_SUCCESS The SetVariable check result was success. @retval EFI_SECURITY_VIOLATION Check fail. **/ EFI_STATUS EFIAPI SetVariableCheckHandlerHii ( IN CHAR16 *VariableName, IN EFI_GUID *VendorGuid, IN UINT32 Attributes, IN UINTN DataSize, IN VOID *Data ) { VAR_CHECK_HII_VARIABLE_HEADER *HiiVariable; VAR_CHECK_HII_QUESTION_HEADER *HiiQuestion; if (mVarCheckHiiBin == NULL) { return EFI_SUCCESS; } if ((((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0) && (DataSize == 0)) || (Attributes == 0)) { // // Do not check delete variable. // return EFI_SUCCESS; } // // For Hii Variable header align. // HiiVariable = (VAR_CHECK_HII_VARIABLE_HEADER *)HEADER_ALIGN (mVarCheckHiiBin); while ((UINTN)HiiVariable < ((UINTN)mVarCheckHiiBin + mVarCheckHiiBinSize)) { if ((StrCmp ((CHAR16 *)(HiiVariable + 1), VariableName) == 0) && (CompareGuid (&HiiVariable->Guid, VendorGuid))) { // // Found the Hii Variable that could be used to do check. // DEBUG ((DEBUG_INFO, "VarCheckHiiVariable - %s:%g with Attributes = 0x%08x Size = 0x%x\n", VariableName, VendorGuid, Attributes, DataSize)); if (HiiVariable->Attributes != Attributes) { DEBUG ((DEBUG_INFO, "VarCheckHiiVariable fail for Attributes - 0x%08x\n", HiiVariable->Attributes)); return EFI_SECURITY_VIOLATION; } if (DataSize == 0) { DEBUG ((DEBUG_INFO, "VarCheckHiiVariable - CHECK PASS with DataSize == 0 !\n")); return EFI_SUCCESS; } if (HiiVariable->Size != DataSize) { DEBUG ((DEBUG_INFO, "VarCheckHiiVariable fail for Size - 0x%x\n", HiiVariable->Size)); return EFI_SECURITY_VIOLATION; } // // Do the check. // For Hii Question header align. // HiiQuestion = (VAR_CHECK_HII_QUESTION_HEADER *)HEADER_ALIGN (((UINTN)HiiVariable + HiiVariable->HeaderLength)); while ((UINTN)HiiQuestion < ((UINTN)HiiVariable + HiiVariable->Length)) { if (!VarCheckHiiQuestion (HiiQuestion, Data, DataSize)) { return EFI_SECURITY_VIOLATION; } // // For Hii Question header align. // HiiQuestion = (VAR_CHECK_HII_QUESTION_HEADER *)HEADER_ALIGN (((UINTN)HiiQuestion + HiiQuestion->Length)); } DEBUG ((DEBUG_INFO, "VarCheckHiiVariable - ALL CHECK PASS!\n")); return EFI_SUCCESS; } // // For Hii Variable header align. // HiiVariable = (VAR_CHECK_HII_VARIABLE_HEADER *)HEADER_ALIGN (((UINTN)HiiVariable + HiiVariable->Length)); } // Not found, so pass. return EFI_SUCCESS; } #ifdef DUMP_VAR_CHECK_HII GLOBAL_REMOVE_IF_UNREFERENCED VAR_CHECK_HII_OPCODE_STRING mHiiOpCodeStringTable[] = { { EFI_IFR_VARSTORE_EFI_OP, "EfiVarStore" }, { EFI_IFR_ONE_OF_OP, "OneOf" }, { EFI_IFR_CHECKBOX_OP, "CheckBox" }, { EFI_IFR_NUMERIC_OP, "Numeric" }, { EFI_IFR_ORDERED_LIST_OP, "OrderedList" }, }; /** HII opcode to string. @param[in] HiiOpCode Hii OpCode. @return Pointer to string. **/ CHAR8 * HiiOpCodeToStr ( IN UINT8 HiiOpCode ) { UINTN Index; for (Index = 0; Index < ARRAY_SIZE (mHiiOpCodeStringTable); Index++) { if (mHiiOpCodeStringTable[Index].HiiOpCode == HiiOpCode) { return mHiiOpCodeStringTable[Index].HiiOpCodeStr; } } return ""; } /** Dump Hii Question. @param[in] HiiQuestion Pointer to Hii Question. **/ VOID DumpHiiQuestion ( IN VAR_CHECK_HII_QUESTION_HEADER *HiiQuestion ) { UINT64 Minimum; UINT64 Maximum; UINT64 OneValue; UINT8 *Ptr; DEBUG ((DEBUG_INFO, " VAR_CHECK_HII_QUESTION_HEADER\n")); DEBUG ((DEBUG_INFO, " OpCode - 0x%02x (%a) (%a)\n", HiiQuestion->OpCode, HiiOpCodeToStr (HiiQuestion->OpCode), (HiiQuestion->BitFieldStore ? "bit level" : "byte level"))); DEBUG ((DEBUG_INFO, " Length - 0x%02x\n", HiiQuestion->Length)); DEBUG ((DEBUG_INFO, " VarOffset - 0x%04x (%a)\n", HiiQuestion->VarOffset, (HiiQuestion->BitFieldStore ? "bit level" : "byte level"))); DEBUG ((DEBUG_INFO, " StorageWidth - 0x%02x (%a)\n", HiiQuestion->StorageWidth, (HiiQuestion->BitFieldStore ? "bit level" : "byte level"))); switch (HiiQuestion->OpCode) { case EFI_IFR_ONE_OF_OP: Ptr = (UINT8 *)((VAR_CHECK_HII_QUESTION_ONEOF *)HiiQuestion + 1); while ((UINTN)Ptr < ((UINTN)HiiQuestion + HiiQuestion->Length)) { OneValue = 0; if (HiiQuestion->BitFieldStore) { // // For OneOf stored in bit field, the value of options are saved as UINT32 type. // CopyMem (&OneValue, Ptr, sizeof (UINT32)); DEBUG ((DEBUG_INFO, " OneOfOption - 0x%08x\n", OneValue)); } else { CopyMem (&OneValue, Ptr, HiiQuestion->StorageWidth); switch (HiiQuestion->StorageWidth) { case sizeof (UINT8): DEBUG ((DEBUG_INFO, " OneOfOption - 0x%02x\n", OneValue)); break; case sizeof (UINT16): DEBUG ((DEBUG_INFO, " OneOfOption - 0x%04x\n", OneValue)); break; case sizeof (UINT32): DEBUG ((DEBUG_INFO, " OneOfOption - 0x%08x\n", OneValue)); break; case sizeof (UINT64): DEBUG ((DEBUG_INFO, " OneOfOption - 0x%016lx\n", OneValue)); break; default: ASSERT (FALSE); break; } } if (HiiQuestion->BitFieldStore) { Ptr += sizeof (UINT32); } else { Ptr += HiiQuestion->StorageWidth; } } break; case EFI_IFR_CHECKBOX_OP: break; case EFI_IFR_NUMERIC_OP: Minimum = 0; Maximum = 0; Ptr = (UINT8 *)((VAR_CHECK_HII_QUESTION_NUMERIC *)HiiQuestion + 1); if (HiiQuestion->BitFieldStore) { // // For Numeric stored in bit field, the value of Maximum/Minimum are saved as UINT32 type. // CopyMem (&Minimum, Ptr, sizeof (UINT32)); Ptr += sizeof (UINT32); CopyMem (&Maximum, Ptr, sizeof (UINT32)); Ptr += sizeof (UINT32); DEBUG ((DEBUG_INFO, " Minimum - 0x%08x\n", Minimum)); DEBUG ((DEBUG_INFO, " Maximum - 0x%08x\n", Maximum)); } else { CopyMem (&Minimum, Ptr, HiiQuestion->StorageWidth); Ptr += HiiQuestion->StorageWidth; CopyMem (&Maximum, Ptr, HiiQuestion->StorageWidth); Ptr += HiiQuestion->StorageWidth; switch (HiiQuestion->StorageWidth) { case sizeof (UINT8): DEBUG ((DEBUG_INFO, " Minimum - 0x%02x\n", Minimum)); DEBUG ((DEBUG_INFO, " Maximum - 0x%02x\n", Maximum)); break; case sizeof (UINT16): DEBUG ((DEBUG_INFO, " Minimum - 0x%04x\n", Minimum)); DEBUG ((DEBUG_INFO, " Maximum - 0x%04x\n", Maximum)); break; case sizeof (UINT32): DEBUG ((DEBUG_INFO, " Minimum - 0x%08x\n", Minimum)); DEBUG ((DEBUG_INFO, " Maximum - 0x%08x\n", Maximum)); break; case sizeof (UINT64): DEBUG ((DEBUG_INFO, " Minimum - 0x%016lx\n", Minimum)); DEBUG ((DEBUG_INFO, " Maximum - 0x%016lx\n", Maximum)); break; default: ASSERT (FALSE); break; } } break; case EFI_IFR_ORDERED_LIST_OP: DEBUG ((DEBUG_INFO, " MaxContainers - 0x%02x\n", ((VAR_CHECK_HII_QUESTION_ORDEREDLIST *)HiiQuestion)->MaxContainers)); Ptr = (UINT8 *)((VAR_CHECK_HII_QUESTION_ORDEREDLIST *)HiiQuestion + 1); while ((UINTN)Ptr < ((UINTN)HiiQuestion + HiiQuestion->Length)) { OneValue = 0; CopyMem (&OneValue, Ptr, HiiQuestion->StorageWidth); switch (HiiQuestion->StorageWidth) { case sizeof (UINT8): DEBUG ((DEBUG_INFO, " OneOfOption - 0x%02x\n", OneValue)); break; case sizeof (UINT16): DEBUG ((DEBUG_INFO, " OneOfOption - 0x%04x\n", OneValue)); break; case sizeof (UINT32): DEBUG ((DEBUG_INFO, " OneOfOption - 0x%08x\n", OneValue)); break; case sizeof (UINT64): DEBUG ((DEBUG_INFO, " OneOfOption - 0x%016lx\n", OneValue)); break; default: ASSERT (FALSE); break; } Ptr += HiiQuestion->StorageWidth; } break; default: ASSERT (FALSE); break; } } /** Dump Hii Variable. @param[in] HiiVariable Pointer to Hii Variable. **/ VOID DumpHiiVariable ( IN VAR_CHECK_HII_VARIABLE_HEADER *HiiVariable ) { VAR_CHECK_HII_QUESTION_HEADER *HiiQuestion; DEBUG ((DEBUG_INFO, "VAR_CHECK_HII_VARIABLE_HEADER\n")); DEBUG ((DEBUG_INFO, " Revision - 0x%04x\n", HiiVariable->Revision)); DEBUG ((DEBUG_INFO, " HeaderLength - 0x%04x\n", HiiVariable->HeaderLength)); DEBUG ((DEBUG_INFO, " Length - 0x%08x\n", HiiVariable->Length)); DEBUG ((DEBUG_INFO, " OpCode - 0x%02x (%a)\n", HiiVariable->OpCode, HiiOpCodeToStr (HiiVariable->OpCode))); DEBUG ((DEBUG_INFO, " Size - 0x%04x\n", HiiVariable->Size)); DEBUG ((DEBUG_INFO, " Attributes - 0x%08x\n", HiiVariable->Attributes)); DEBUG ((DEBUG_INFO, " Guid - %g\n", &HiiVariable->Guid)); DEBUG ((DEBUG_INFO, " Name - %s\n", HiiVariable + 1)); // // For Hii Question header align. // HiiQuestion = (VAR_CHECK_HII_QUESTION_HEADER *)HEADER_ALIGN (((UINTN)HiiVariable + HiiVariable->HeaderLength)); while ((UINTN)HiiQuestion < ((UINTN)HiiVariable + HiiVariable->Length)) { // // Dump Hii Question related to the Hii Variable. // DumpHiiQuestion (HiiQuestion); // // For Hii Question header align. // HiiQuestion = (VAR_CHECK_HII_QUESTION_HEADER *)HEADER_ALIGN (((UINTN)HiiQuestion + HiiQuestion->Length)); } } /** Dump Var Check HII. @param[in] VarCheckHiiBin Pointer to VarCheckHiiBin. @param[in] VarCheckHiiBinSize VarCheckHiiBin size. **/ VOID DumpVarCheckHii ( IN VOID *VarCheckHiiBin, IN UINTN VarCheckHiiBinSize ) { VAR_CHECK_HII_VARIABLE_HEADER *HiiVariable; DEBUG ((DEBUG_INFO, "DumpVarCheckHii\n")); // // For Hii Variable header align. // HiiVariable = (VAR_CHECK_HII_VARIABLE_HEADER *)HEADER_ALIGN (VarCheckHiiBin); while ((UINTN)HiiVariable < ((UINTN)VarCheckHiiBin + VarCheckHiiBinSize)) { DumpHiiVariable (HiiVariable); // // For Hii Variable header align. // HiiVariable = (VAR_CHECK_HII_VARIABLE_HEADER *)HEADER_ALIGN (((UINTN)HiiVariable + HiiVariable->Length)); } } #endif /** Constructor function of VarCheckHiiLib to register var check HII handler. @param[in] ImageHandle The firmware allocated handle for the EFI image. @param[in] SystemTable A pointer to the EFI System Table. @retval EFI_SUCCESS The constructor executed correctly. **/ EFI_STATUS EFIAPI VarCheckHiiLibNullClassConstructor ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { VarCheckLibRegisterEndOfDxeCallback (VarCheckHiiGen); VarCheckLibRegisterAddressPointer ((VOID **)&mVarCheckHiiBin); VarCheckLibRegisterSetVariableCheckHandler (SetVariableCheckHandlerHii); return EFI_SUCCESS; }