/** @file Var Check PCD handler. Copyright (c) 2015, 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 #include #include #include #include #include #include "VarCheckPcdStructure.h" //#define DUMP_VAR_CHECK_PCD GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 mVarCheckPcdHex[] = {'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 VarCheckPcdInternalDumpHex ( 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] = mVarCheckPcdHex[TempByte >> 4]; Val[Index * 3 + 1] = mVarCheckPcdHex[TempByte & 0xF]; Val[Index * 3 + 2] = (CHAR8) ((Index == 7) ? '-' : ' '); Str[Index] = (CHAR8) ((TempByte < ' ' || TempByte > 'z') ? '.' : TempByte); } Val[Index * 3] = 0; Str[Index] = 0; DEBUG ((EFI_D_INFO, "%*a%08X: %-48a *%a*\r\n", Indent, "", Offset, Val, Str)); Data += Size; Offset += Size; DataSize -= Size; } } /** Var Check Pcd ValidData. @param[in] PcdValidData Pointer to Pcd ValidData @param[in] Data Data pointer. @param[in] DataSize Size of Data to set. @retval TRUE Check pass @retval FALSE Check fail. **/ BOOLEAN VarCheckPcdValidData ( IN VAR_CHECK_PCD_VALID_DATA_HEADER *PcdValidData, IN VOID *Data, IN UINTN DataSize ) { UINT64 OneData; UINT64 Minimum; UINT64 Maximum; UINT64 OneValue; UINT8 *Ptr; OneData = 0; CopyMem (&OneData, (UINT8 *) Data + PcdValidData->VarOffset, PcdValidData->StorageWidth); switch (PcdValidData->Type) { case VarCheckPcdValidList: Ptr = (UINT8 *) ((VAR_CHECK_PCD_VALID_LIST *) PcdValidData + 1); while ((UINTN) Ptr < (UINTN) PcdValidData + PcdValidData->Length) { OneValue = 0; CopyMem (&OneValue, Ptr, PcdValidData->StorageWidth); if (OneData == OneValue) { // // Match // break; } Ptr += PcdValidData->StorageWidth; } if ((UINTN) Ptr >= ((UINTN) PcdValidData + PcdValidData->Length)) { // // No match // DEBUG ((EFI_D_INFO, "VarCheckPcdValidData fail: ValidList mismatch (0x%lx)\n", OneData)); DEBUG_CODE (VarCheckPcdInternalDumpHex (2, 0, PcdValidData->Length, (UINT8 *) PcdValidData);); return FALSE; } break; case VarCheckPcdValidRange: Minimum = 0; Maximum = 0; Ptr = (UINT8 *) ((VAR_CHECK_PCD_VALID_RANGE *) PcdValidData + 1); while ((UINTN) Ptr < (UINTN) PcdValidData + PcdValidData->Length) { CopyMem (&Minimum, Ptr, PcdValidData->StorageWidth); Ptr += PcdValidData->StorageWidth; CopyMem (&Maximum, Ptr, PcdValidData->StorageWidth); Ptr += PcdValidData->StorageWidth; if ((OneData >= Minimum) && (OneData <= Maximum)) { return TRUE; } } DEBUG ((EFI_D_INFO, "VarCheckPcdValidData fail: ValidRange mismatch (0x%lx)\n", OneData)); DEBUG_CODE (VarCheckPcdInternalDumpHex (2, 0, PcdValidData->Length, (UINT8 *) PcdValidData);); return FALSE; break; default: ASSERT (FALSE); break; } return TRUE; } VAR_CHECK_PCD_VARIABLE_HEADER *mVarCheckPcdBin = NULL; UINTN mVarCheckPcdBinSize = 0; /** SetVariable check handler PCD. @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 SetVariableCheckHandlerPcd ( IN CHAR16 *VariableName, IN EFI_GUID *VendorGuid, IN UINT32 Attributes, IN UINTN DataSize, IN VOID *Data ) { VAR_CHECK_PCD_VARIABLE_HEADER *PcdVariable; VAR_CHECK_PCD_VALID_DATA_HEADER *PcdValidData; if (mVarCheckPcdBin == NULL) { return EFI_SUCCESS; } if ((((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0) && (DataSize == 0)) || (Attributes == 0)) { // // Do not check delete variable. // return EFI_SUCCESS; } // // For Pcd Variable header align. // PcdVariable = (VAR_CHECK_PCD_VARIABLE_HEADER *) HEADER_ALIGN (mVarCheckPcdBin); while ((UINTN) PcdVariable < ((UINTN) mVarCheckPcdBin + mVarCheckPcdBinSize)) { if ((StrCmp ((CHAR16 *) (PcdVariable + 1), VariableName) == 0) && (CompareGuid (&PcdVariable->Guid, VendorGuid))) { // // Found the Pcd Variable that could be used to do check. // DEBUG ((EFI_D_INFO, "VarCheckPcdVariable - %s:%g with Attributes = 0x%08x Size = 0x%x\n", VariableName, VendorGuid, Attributes, DataSize)); if ((PcdVariable->Attributes != 0) && PcdVariable->Attributes != Attributes) { DEBUG ((EFI_D_INFO, "VarCheckPcdVariable fail for Attributes - 0x%08x\n", PcdVariable->Attributes)); return EFI_SECURITY_VIOLATION; } if (DataSize == 0) { DEBUG ((EFI_D_INFO, "VarCheckPcdVariable - CHECK PASS with DataSize == 0 !\n")); return EFI_SUCCESS; } // // Do the check. // For Pcd ValidData header align. // PcdValidData = (VAR_CHECK_PCD_VALID_DATA_HEADER *) HEADER_ALIGN (((UINTN) PcdVariable + PcdVariable->HeaderLength)); while ((UINTN) PcdValidData < ((UINTN) PcdVariable + PcdVariable->Length)) { if (((UINTN) PcdValidData->VarOffset + PcdValidData->StorageWidth) <= DataSize) { if (!VarCheckPcdValidData (PcdValidData, Data, DataSize)) { return EFI_SECURITY_VIOLATION; } } // // For Pcd ValidData header align. // PcdValidData = (VAR_CHECK_PCD_VALID_DATA_HEADER *) HEADER_ALIGN (((UINTN) PcdValidData + PcdValidData->Length)); } DEBUG ((EFI_D_INFO, "VarCheckPcdVariable - ALL CHECK PASS!\n")); return EFI_SUCCESS; } // // For Pcd Variable header align. // PcdVariable = (VAR_CHECK_PCD_VARIABLE_HEADER *) HEADER_ALIGN (((UINTN) PcdVariable + PcdVariable->Length)); } // Not found, so pass. return EFI_SUCCESS; } #ifdef DUMP_VAR_CHECK_PCD /** Dump Pcd ValidData. @param[in] PcdValidData Pointer to Pcd ValidData. **/ VOID DumpPcdValidData ( IN VAR_CHECK_PCD_VALID_DATA_HEADER *PcdValidData ) { UINT64 Minimum; UINT64 Maximum; UINT64 OneValue; UINT8 *Ptr; DEBUG ((EFI_D_INFO, " VAR_CHECK_PCD_VALID_DATA_HEADER\n")); DEBUG ((EFI_D_INFO, " Type - 0x%02x\n", PcdValidData->Type)); DEBUG ((EFI_D_INFO, " Length - 0x%02x\n", PcdValidData->Length)); DEBUG ((EFI_D_INFO, " VarOffset - 0x%04x\n", PcdValidData->VarOffset)); DEBUG ((EFI_D_INFO, " StorageWidth - 0x%02x\n", PcdValidData->StorageWidth)); switch (PcdValidData->Type) { case VarCheckPcdValidList: Ptr = (UINT8 *) ((VAR_CHECK_PCD_VALID_LIST *) PcdValidData + 1); while ((UINTN) Ptr < ((UINTN) PcdValidData + PcdValidData->Length)) { OneValue = 0; CopyMem (&OneValue, Ptr, PcdValidData->StorageWidth); switch (PcdValidData->StorageWidth) { case sizeof (UINT8): DEBUG ((EFI_D_INFO, " ValidList - 0x%02x\n", OneValue)); break; case sizeof (UINT16): DEBUG ((EFI_D_INFO, " ValidList - 0x%04x\n", OneValue)); break; case sizeof (UINT32): DEBUG ((EFI_D_INFO, " ValidList - 0x%08x\n", OneValue)); break; case sizeof (UINT64): DEBUG ((EFI_D_INFO, " ValidList - 0x%016lx\n", OneValue)); break; default: ASSERT (FALSE); break; } Ptr += PcdValidData->StorageWidth; } break; case VarCheckPcdValidRange: Minimum = 0; Maximum = 0; Ptr = (UINT8 *) ((VAR_CHECK_PCD_VALID_RANGE *) PcdValidData + 1); while ((UINTN) Ptr < (UINTN) PcdValidData + PcdValidData->Length) { CopyMem (&Minimum, Ptr, PcdValidData->StorageWidth); Ptr += PcdValidData->StorageWidth; CopyMem (&Maximum, Ptr, PcdValidData->StorageWidth); Ptr += PcdValidData->StorageWidth; switch (PcdValidData->StorageWidth) { case sizeof (UINT8): DEBUG ((EFI_D_INFO, " Minimum - 0x%02x\n", Minimum)); DEBUG ((EFI_D_INFO, " Maximum - 0x%02x\n", Maximum)); break; case sizeof (UINT16): DEBUG ((EFI_D_INFO, " Minimum - 0x%04x\n", Minimum)); DEBUG ((EFI_D_INFO, " Maximum - 0x%04x\n", Maximum)); break; case sizeof (UINT32): DEBUG ((EFI_D_INFO, " Minimum - 0x%08x\n", Minimum)); DEBUG ((EFI_D_INFO, " Maximum - 0x%08x\n", Maximum)); break; case sizeof (UINT64): DEBUG ((EFI_D_INFO, " Minimum - 0x%016lx\n", Minimum)); DEBUG ((EFI_D_INFO, " Maximum - 0x%016lx\n", Maximum)); break; default: ASSERT (FALSE); break; } } break; default: ASSERT (FALSE); break; } } /** Dump Pcd Variable. @param[in] PcdVariable Pointer to Pcd Variable. **/ VOID DumpPcdVariable ( IN VAR_CHECK_PCD_VARIABLE_HEADER *PcdVariable ) { VAR_CHECK_PCD_VALID_DATA_HEADER *PcdValidData; DEBUG ((EFI_D_INFO, "VAR_CHECK_PCD_VARIABLE_HEADER\n")); DEBUG ((EFI_D_INFO, " Revision - 0x%04x\n", PcdVariable->Revision)); DEBUG ((EFI_D_INFO, " HeaderLength - 0x%04x\n", PcdVariable->HeaderLength)); DEBUG ((EFI_D_INFO, " Length - 0x%08x\n", PcdVariable->Length)); DEBUG ((EFI_D_INFO, " Type - 0x%02x\n", PcdVariable->Type)); DEBUG ((EFI_D_INFO, " Attributes - 0x%08x\n", PcdVariable->Attributes)); DEBUG ((EFI_D_INFO, " Guid - %g\n", &PcdVariable->Guid)); DEBUG ((EFI_D_INFO, " Name - %s\n", PcdVariable + 1)); // // For Pcd ValidData header align. // PcdValidData = (VAR_CHECK_PCD_VALID_DATA_HEADER *) HEADER_ALIGN (((UINTN) PcdVariable + PcdVariable->HeaderLength)); while ((UINTN) PcdValidData < ((UINTN) PcdVariable + PcdVariable->Length)) { // // Dump Pcd ValidData related to the Pcd Variable. // DumpPcdValidData (PcdValidData); // // For Pcd ValidData header align. // PcdValidData = (VAR_CHECK_PCD_VALID_DATA_HEADER *) HEADER_ALIGN (((UINTN) PcdValidData + PcdValidData->Length)); } } /** Dump Var Check PCD. @param[in] VarCheckPcdBin Pointer to VarCheckPcdBin. @param[in] VarCheckPcdBinSize VarCheckPcdBin size. **/ VOID DumpVarCheckPcd ( IN VOID *VarCheckPcdBin, IN UINTN VarCheckPcdBinSize ) { VAR_CHECK_PCD_VARIABLE_HEADER *PcdVariable; DEBUG ((EFI_D_INFO, "DumpVarCheckPcd\n")); // // For Pcd Variable header align. // PcdVariable = (VAR_CHECK_PCD_VARIABLE_HEADER *) HEADER_ALIGN (VarCheckPcdBin); while ((UINTN) PcdVariable < ((UINTN) VarCheckPcdBin + VarCheckPcdBinSize)) { DumpPcdVariable (PcdVariable); // // For Pcd Variable header align. // PcdVariable = (VAR_CHECK_PCD_VARIABLE_HEADER *) HEADER_ALIGN (((UINTN) PcdVariable + PcdVariable->Length)); } } #endif /** Locate VarCheckPcdBin. **/ VOID EFIAPI LocateVarCheckPcdBin ( VOID ) { EFI_STATUS Status; VAR_CHECK_PCD_VARIABLE_HEADER *VarCheckPcdBin; UINTN VarCheckPcdBinSize; // // Search the VarCheckPcdBin from the first RAW section of current FFS. // Status = GetSectionFromFfs ( EFI_SECTION_RAW, 0, (VOID **) &VarCheckPcdBin, &VarCheckPcdBinSize ); if (!EFI_ERROR (Status)) { // // AllocateRuntimeZeroPool () from MemoryAllocateLib is used for runtime access // in SetVariable check handler. // mVarCheckPcdBin = AllocateRuntimeCopyPool (VarCheckPcdBinSize, VarCheckPcdBin); ASSERT (mVarCheckPcdBin != NULL); mVarCheckPcdBinSize = VarCheckPcdBinSize; FreePool (VarCheckPcdBin); DEBUG ((EFI_D_INFO, "VarCheckPcdBin - at 0x%x size = 0x%x\n", mVarCheckPcdBin, mVarCheckPcdBinSize)); #ifdef DUMP_VAR_CHECK_PCD DEBUG_CODE ( DumpVarCheckPcd (mVarCheckPcdBin, mVarCheckPcdBinSize); ); #endif } else { DEBUG ((EFI_D_INFO, "[VarCheckPcd] No VarCheckPcdBin found at the first RAW section\n")); } } /** Constructor function of VarCheckPcdLib to register var check PCD 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 VarCheckPcdLibNullClassConstructor ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { LocateVarCheckPcdBin (); VarCheckLibRegisterAddressPointer ((VOID **) &mVarCheckPcdBin); VarCheckLibRegisterSetVariableCheckHandler (SetVariableCheckHandlerPcd); return EFI_SUCCESS; }