diff options
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/VarCheckLib/VarCheckLib.c')
-rw-r--r-- | src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/VarCheckLib/VarCheckLib.c | 671 |
1 files changed, 671 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/VarCheckLib/VarCheckLib.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/VarCheckLib/VarCheckLib.c new file mode 100644 index 00000000..568f26fe --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/VarCheckLib/VarCheckLib.c @@ -0,0 +1,671 @@ +/** @file + Implementation functions and structures for var check services. + +Copyright (c) 2015 - 2021, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Library/VarCheckLib.h> +#include <Library/BaseLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/DebugLib.h> +#include <Library/MemoryAllocationLib.h> + +#include <Guid/GlobalVariable.h> +#include <Guid/HardwareErrorVariable.h> + +BOOLEAN mVarCheckLibEndOfDxe = FALSE; + +#define VAR_CHECK_TABLE_SIZE 0x8 + +UINTN mVarCheckLibEndOfDxeCallbackCount = 0; +UINTN mVarCheckLibEndOfDxeCallbackMaxCount = 0; +VAR_CHECK_END_OF_DXE_CALLBACK *mVarCheckLibEndOfDxeCallback = NULL; + +UINTN mVarCheckLibAddressPointerCount = 0; +UINTN mVarCheckLibAddressPointerMaxCount = 0; +VOID ***mVarCheckLibAddressPointer = NULL; + +UINTN mNumberOfVarCheckHandler = 0; +UINTN mMaxNumberOfVarCheckHandler = 0; +VAR_CHECK_SET_VARIABLE_CHECK_HANDLER *mVarCheckHandlerTable = NULL; + +typedef struct { + EFI_GUID Guid; + VAR_CHECK_VARIABLE_PROPERTY VariableProperty; + //CHAR16 *Name; +} VAR_CHECK_VARIABLE_ENTRY; + +UINTN mNumberOfVarCheckVariable = 0; +UINTN mMaxNumberOfVarCheckVariable = 0; +VARIABLE_ENTRY_PROPERTY **mVarCheckVariableTable = NULL; + +// +// Handle variables with wildcard name specially. +// +VARIABLE_ENTRY_PROPERTY mVarCheckVariableWithWildcardName[] = { + { + &gEfiGlobalVariableGuid, + L"Boot####", + { + 0 + }, + }, + { + &gEfiGlobalVariableGuid, + L"Driver####", + { + 0 + }, + }, + { + &gEfiGlobalVariableGuid, + L"SysPrep####", + { + 0 + }, + }, + { + &gEfiGlobalVariableGuid, + L"Key####", + { + 0 + }, + }, + { + &gEfiGlobalVariableGuid, + L"PlatformRecovery####", + { + 0 + }, + }, + { + &gEfiHardwareErrorVariableGuid, + L"HwErrRec####", + { + 0 + }, + }, +}; + +/** + Check if a Unicode character is an upper case hexadecimal character. + + This function checks if a Unicode character is an upper case + hexadecimal character. The valid upper case hexadecimal character is + L'0' to L'9', or L'A' to L'F'. + + + @param[in] Char The character to check against. + + @retval TRUE If the Char is an upper case hexadecmial character. + @retval FALSE If the Char is not an upper case hexadecmial character. + +**/ +BOOLEAN +EFIAPI +VarCheckInternalIsHexaDecimalDigitCharacter ( + IN CHAR16 Char + ) +{ + return (BOOLEAN) ((Char >= L'0' && Char <= L'9') || (Char >= L'A' && Char <= L'F')); +} + +/** + Variable property get with wildcard name. + + @param[in] VariableName Pointer to variable name. + @param[in] VendorGuid Pointer to variable vendor GUID. + @param[in] WildcardMatch Try wildcard match or not. + + @return Pointer to variable property. + +**/ +VAR_CHECK_VARIABLE_PROPERTY * +VariablePropertyGetWithWildcardName ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN BOOLEAN WildcardMatch + ) +{ + UINTN Index; + UINTN NameLength; + + NameLength = StrLen (VariableName) - 4; + for (Index = 0; Index < sizeof (mVarCheckVariableWithWildcardName)/sizeof (mVarCheckVariableWithWildcardName[0]); Index++) { + if (CompareGuid (mVarCheckVariableWithWildcardName[Index].Guid, VendorGuid)){ + if (WildcardMatch) { + if ((StrLen (VariableName) == StrLen (mVarCheckVariableWithWildcardName[Index].Name)) && + (StrnCmp (VariableName, mVarCheckVariableWithWildcardName[Index].Name, NameLength) == 0) && + VarCheckInternalIsHexaDecimalDigitCharacter (VariableName[NameLength]) && + VarCheckInternalIsHexaDecimalDigitCharacter (VariableName[NameLength + 1]) && + VarCheckInternalIsHexaDecimalDigitCharacter (VariableName[NameLength + 2]) && + VarCheckInternalIsHexaDecimalDigitCharacter (VariableName[NameLength + 3])) { + return &mVarCheckVariableWithWildcardName[Index].VariableProperty; + } + } + if (StrCmp (mVarCheckVariableWithWildcardName[Index].Name, VariableName) == 0) { + return &mVarCheckVariableWithWildcardName[Index].VariableProperty; + } + } + } + + return NULL; +} + +/** + Variable property get function. + + @param[in] Name Pointer to the variable name. + @param[in] Guid Pointer to the vendor GUID. + @param[in] WildcardMatch Try wildcard match or not. + + @return Pointer to the property of variable specified by the Name and Guid. + +**/ +VAR_CHECK_VARIABLE_PROPERTY * +VariablePropertyGetFunction ( + IN CHAR16 *Name, + IN EFI_GUID *Guid, + IN BOOLEAN WildcardMatch + ) +{ + UINTN Index; + VAR_CHECK_VARIABLE_ENTRY *Entry; + CHAR16 *VariableName; + + for (Index = 0; Index < mNumberOfVarCheckVariable; Index++) { + Entry = (VAR_CHECK_VARIABLE_ENTRY *) mVarCheckVariableTable[Index]; + VariableName = (CHAR16 *) ((UINTN) Entry + sizeof (*Entry)); + if (CompareGuid (&Entry->Guid, Guid) && (StrCmp (VariableName, Name) == 0)) { + return &Entry->VariableProperty; + } + } + + return VariablePropertyGetWithWildcardName (Name, Guid, WildcardMatch); +} + +/** + Var check add table entry. + + @param[in, out] Table Pointer to table buffer. + @param[in, out] MaxNumber Pointer to maximum number of entry in the table. + @param[in, out] CurrentNumber Pointer to current number of entry in the table. + @param[in] Entry Entry will be added to the table. + + @retval EFI_SUCCESS Reallocate memory successfully. + @retval EFI_OUT_OF_RESOURCES No enough memory to allocate. + +**/ +EFI_STATUS +VarCheckAddTableEntry ( + IN OUT UINTN **Table, + IN OUT UINTN *MaxNumber, + IN OUT UINTN *CurrentNumber, + IN UINTN Entry + ) +{ + UINTN *TempTable; + + // + // Check whether the table is enough to store new entry. + // + if (*CurrentNumber == *MaxNumber) { + // + // Reallocate memory for the table. + // + TempTable = ReallocateRuntimePool ( + *MaxNumber * sizeof (UINTN), + (*MaxNumber + VAR_CHECK_TABLE_SIZE) * sizeof (UINTN), + *Table + ); + + // + // No enough resource to allocate. + // + if (TempTable == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + *Table = TempTable; + // + // Increase max number. + // + *MaxNumber += VAR_CHECK_TABLE_SIZE; + } + + // + // Add entry to the table. + // + (*Table)[*CurrentNumber] = Entry; + (*CurrentNumber)++; + + return EFI_SUCCESS; +} + +/** + Register END_OF_DXE callback. + The callback will be invoked by VarCheckLibInitializeAtEndOfDxe(). + + @param[in] Callback END_OF_DXE callback. + + @retval EFI_SUCCESS The callback was registered successfully. + @retval EFI_INVALID_PARAMETER Callback is NULL. + @retval EFI_ACCESS_DENIED EFI_END_OF_DXE_EVENT_GROUP_GUID or EFI_EVENT_GROUP_READY_TO_BOOT has + already been signaled. + @retval EFI_OUT_OF_RESOURCES There is not enough resource for the callback register request. + +**/ +EFI_STATUS +EFIAPI +VarCheckLibRegisterEndOfDxeCallback ( + IN VAR_CHECK_END_OF_DXE_CALLBACK Callback + ) +{ + EFI_STATUS Status; + + if (Callback == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (mVarCheckLibEndOfDxe) { + return EFI_ACCESS_DENIED; + } + + Status = VarCheckAddTableEntry ( + (UINTN **) &mVarCheckLibEndOfDxeCallback, + &mVarCheckLibEndOfDxeCallbackMaxCount, + &mVarCheckLibEndOfDxeCallbackCount, + (UINTN) Callback + ); + + DEBUG ((EFI_D_INFO, "VarCheckLibRegisterEndOfDxeCallback - 0x%x %r\n", Callback, Status)); + + return Status; +} + +/** + Var check initialize at END_OF_DXE. + + This function needs to be called at END_OF_DXE. + Address pointers may be returned, + and caller needs to ConvertPointer() for the pointers. + + @param[in, out] AddressPointerCount Output pointer to address pointer count. + + @return Address pointer buffer, NULL if input AddressPointerCount is NULL. + +**/ +VOID *** +EFIAPI +VarCheckLibInitializeAtEndOfDxe ( + IN OUT UINTN *AddressPointerCount OPTIONAL + ) +{ + VOID *TempTable; + UINTN TotalCount; + UINTN Index; + + for (Index = 0; Index < mVarCheckLibEndOfDxeCallbackCount; Index++) { + // + // Invoke the callback registered by VarCheckLibRegisterEndOfDxeCallback(). + // + mVarCheckLibEndOfDxeCallback[Index] (); + } + if (mVarCheckLibEndOfDxeCallback != NULL) { + // + // Free the callback buffer. + // + mVarCheckLibEndOfDxeCallbackCount = 0; + mVarCheckLibEndOfDxeCallbackMaxCount = 0; + FreePool ((VOID *) mVarCheckLibEndOfDxeCallback); + mVarCheckLibEndOfDxeCallback = NULL; + } + + mVarCheckLibEndOfDxe = TRUE; + + if (AddressPointerCount == NULL) { + if (mVarCheckLibAddressPointer != NULL) { + // + // Free the address pointer buffer. + // + mVarCheckLibAddressPointerCount = 0; + mVarCheckLibAddressPointerMaxCount = 0; + FreePool ((VOID *) mVarCheckLibAddressPointer); + mVarCheckLibAddressPointer = NULL; + } + return NULL; + } + + // + // Get the total count needed. + // Also cover VarCheckHandler and the entries, and VarCheckVariable and the entries. + // + TotalCount = mVarCheckLibAddressPointerCount + (mNumberOfVarCheckHandler + 1) + (mNumberOfVarCheckVariable + 1); + TempTable = ReallocateRuntimePool ( + mVarCheckLibAddressPointerMaxCount * sizeof (VOID **), + TotalCount * sizeof (VOID **), + (VOID *) mVarCheckLibAddressPointer + ); + + if (TempTable != NULL) { + mVarCheckLibAddressPointer = (VOID ***) TempTable; + + // + // Cover VarCheckHandler and the entries. + // + mVarCheckLibAddressPointer[mVarCheckLibAddressPointerCount++] = (VOID **) &mVarCheckHandlerTable; + for (Index = 0; Index < mNumberOfVarCheckHandler; Index++) { + mVarCheckLibAddressPointer[mVarCheckLibAddressPointerCount++] = (VOID **) &mVarCheckHandlerTable[Index]; + } + + // + // Cover VarCheckVariable and the entries. + // + mVarCheckLibAddressPointer[mVarCheckLibAddressPointerCount++] = (VOID **) &mVarCheckVariableTable; + for (Index = 0; Index < mNumberOfVarCheckVariable; Index++) { + mVarCheckLibAddressPointer[mVarCheckLibAddressPointerCount++] = (VOID **) &mVarCheckVariableTable[Index]; + } + + ASSERT (mVarCheckLibAddressPointerCount == TotalCount); + mVarCheckLibAddressPointerMaxCount = mVarCheckLibAddressPointerCount; + } + + *AddressPointerCount = mVarCheckLibAddressPointerCount; + return mVarCheckLibAddressPointer; +} + +/** + Register address pointer. + The AddressPointer may be returned by VarCheckLibInitializeAtEndOfDxe(). + + @param[in] AddressPointer Address pointer. + + @retval EFI_SUCCESS The address pointer was registered successfully. + @retval EFI_INVALID_PARAMETER AddressPointer is NULL. + @retval EFI_ACCESS_DENIED EFI_END_OF_DXE_EVENT_GROUP_GUID or EFI_EVENT_GROUP_READY_TO_BOOT has + already been signaled. + @retval EFI_OUT_OF_RESOURCES There is not enough resource for the address pointer register request. + +**/ +EFI_STATUS +EFIAPI +VarCheckLibRegisterAddressPointer ( + IN VOID **AddressPointer + ) +{ + EFI_STATUS Status; + + if (AddressPointer == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (mVarCheckLibEndOfDxe) { + return EFI_ACCESS_DENIED; + } + + Status = VarCheckAddTableEntry( + (UINTN **) &mVarCheckLibAddressPointer, + &mVarCheckLibAddressPointerMaxCount, + &mVarCheckLibAddressPointerCount, + (UINTN) AddressPointer + ); + + DEBUG ((EFI_D_INFO, "VarCheckLibRegisterAddressPointer - 0x%x %r\n", AddressPointer, Status)); + + return Status; +} + +/** + Register SetVariable check handler. + + @param[in] Handler Pointer to check handler. + + @retval EFI_SUCCESS The SetVariable check handler was registered successfully. + @retval EFI_INVALID_PARAMETER Handler is NULL. + @retval EFI_ACCESS_DENIED EFI_END_OF_DXE_EVENT_GROUP_GUID or EFI_EVENT_GROUP_READY_TO_BOOT has + already been signaled. + @retval EFI_OUT_OF_RESOURCES There is not enough resource for the SetVariable check handler register request. + @retval EFI_UNSUPPORTED This interface is not implemented. + For example, it is unsupported in VarCheck protocol if both VarCheck and SmmVarCheck protocols are present. + +**/ +EFI_STATUS +EFIAPI +VarCheckLibRegisterSetVariableCheckHandler ( + IN VAR_CHECK_SET_VARIABLE_CHECK_HANDLER Handler + ) +{ + EFI_STATUS Status; + + if (Handler == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (mVarCheckLibEndOfDxe) { + return EFI_ACCESS_DENIED; + } + + Status = VarCheckAddTableEntry( + (UINTN **) &mVarCheckHandlerTable, + &mMaxNumberOfVarCheckHandler, + &mNumberOfVarCheckHandler, + (UINTN) Handler + ); + + DEBUG ((EFI_D_INFO, "VarCheckLibRegisterSetVariableCheckHandler - 0x%x %r\n", Handler, Status)); + + return Status; +} + +/** + Variable property set. + + @param[in] Name Pointer to the variable name. + @param[in] Guid Pointer to the vendor GUID. + @param[in] VariableProperty Pointer to the input variable property. + + @retval EFI_SUCCESS The property of variable specified by the Name and Guid was set successfully. + @retval EFI_INVALID_PARAMETER Name, Guid or VariableProperty is NULL, or Name is an empty string, + or the fields of VariableProperty are not valid. + @retval EFI_ACCESS_DENIED EFI_END_OF_DXE_EVENT_GROUP_GUID or EFI_EVENT_GROUP_READY_TO_BOOT has + already been signaled. + @retval EFI_OUT_OF_RESOURCES There is not enough resource for the variable property set request. + +**/ +EFI_STATUS +EFIAPI +VarCheckLibVariablePropertySet ( + IN CHAR16 *Name, + IN EFI_GUID *Guid, + IN VAR_CHECK_VARIABLE_PROPERTY *VariableProperty + ) +{ + EFI_STATUS Status; + VAR_CHECK_VARIABLE_ENTRY *Entry; + CHAR16 *VariableName; + VAR_CHECK_VARIABLE_PROPERTY *Property; + + if (Name == NULL || Name[0] == 0 || Guid == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (VariableProperty == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (VariableProperty->Revision != VAR_CHECK_VARIABLE_PROPERTY_REVISION) { + return EFI_INVALID_PARAMETER; + } + + if (mVarCheckLibEndOfDxe) { + return EFI_ACCESS_DENIED; + } + + Status = EFI_SUCCESS; + + // + // Get the pointer of property data for set. + // + Property = VariablePropertyGetFunction (Name, Guid, FALSE); + if (Property != NULL) { + CopyMem (Property, VariableProperty, sizeof (*VariableProperty)); + } else { + Entry = AllocateRuntimeZeroPool (sizeof (*Entry) + StrSize (Name)); + if (Entry == NULL) { + return EFI_OUT_OF_RESOURCES; + } + VariableName = (CHAR16 *) ((UINTN) Entry + sizeof (*Entry)); + StrCpyS (VariableName, StrSize (Name)/sizeof (CHAR16), Name); + CopyGuid (&Entry->Guid, Guid); + CopyMem (&Entry->VariableProperty, VariableProperty, sizeof (*VariableProperty)); + + Status = VarCheckAddTableEntry( + (UINTN **) &mVarCheckVariableTable, + &mMaxNumberOfVarCheckVariable, + &mNumberOfVarCheckVariable, + (UINTN) Entry + ); + + if (EFI_ERROR (Status)) { + FreePool (Entry); + } + } + + return Status; +} + +/** + Variable property get. + + @param[in] Name Pointer to the variable name. + @param[in] Guid Pointer to the vendor GUID. + @param[out] VariableProperty Pointer to the output variable property. + + @retval EFI_SUCCESS The property of variable specified by the Name and Guid was got successfully. + @retval EFI_INVALID_PARAMETER Name, Guid or VariableProperty is NULL, or Name is an empty string. + @retval EFI_NOT_FOUND The property of variable specified by the Name and Guid was not found. + +**/ +EFI_STATUS +EFIAPI +VarCheckLibVariablePropertyGet ( + IN CHAR16 *Name, + IN EFI_GUID *Guid, + OUT VAR_CHECK_VARIABLE_PROPERTY *VariableProperty + ) +{ + VAR_CHECK_VARIABLE_PROPERTY *Property; + + if (Name == NULL || Name[0] == 0 || Guid == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (VariableProperty == NULL) { + return EFI_INVALID_PARAMETER; + } + + Property = VariablePropertyGetFunction (Name, Guid, TRUE); + // + // Also check the property revision before using the property data. + // There is no property set to this variable(wildcard name) + // if the revision is not VAR_CHECK_VARIABLE_PROPERTY_REVISION. + // + if ((Property != NULL) && (Property->Revision == VAR_CHECK_VARIABLE_PROPERTY_REVISION)) { + CopyMem (VariableProperty, Property, sizeof (*VariableProperty)); + return EFI_SUCCESS; + } + + return EFI_NOT_FOUND; +} + +/** + SetVariable check. + + @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. + @param[in] RequestSource Request source. + + @retval EFI_SUCCESS The SetVariable check result was success. + @retval EFI_INVALID_PARAMETER An invalid combination of attribute bits, name, GUID, + DataSize and Data value was supplied. + @retval EFI_WRITE_PROTECTED The variable in question is read-only. + @retval Others The other return status from check handler. + +**/ +EFI_STATUS +EFIAPI +VarCheckLibSetVariableCheck ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN UINT32 Attributes, + IN UINTN DataSize, + IN VOID *Data, + IN VAR_CHECK_REQUEST_SOURCE RequestSource + ) +{ + EFI_STATUS Status; + UINTN Index; + VAR_CHECK_VARIABLE_PROPERTY *Property; + + if (!mVarCheckLibEndOfDxe) { + // + // Only do check after End Of Dxe. + // + return EFI_SUCCESS; + } + + Property = VariablePropertyGetFunction (VariableName, VendorGuid, TRUE); + // + // Also check the property revision before using the property data. + // There is no property set to this variable(wildcard name) + // if the revision is not VAR_CHECK_VARIABLE_PROPERTY_REVISION. + // + if ((Property != NULL) && (Property->Revision == VAR_CHECK_VARIABLE_PROPERTY_REVISION)) { + if ((RequestSource != VarCheckFromTrusted) && ((Property->Property & VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY) != 0)) { + DEBUG ((EFI_D_INFO, "Variable Check ReadOnly variable fail %r - %g:%s\n", EFI_WRITE_PROTECTED, VendorGuid, VariableName)); + return EFI_WRITE_PROTECTED; + } + if (!((((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0) && (DataSize == 0)) || (Attributes == 0))) { + // + // Not to delete variable. + // + if ((Property->Attributes != 0) && ((Attributes & (~EFI_VARIABLE_APPEND_WRITE)) != Property->Attributes)) { + DEBUG ((EFI_D_INFO, "Variable Check Attributes(0x%08x to 0x%08x) fail %r - %g:%s\n", Property->Attributes, Attributes, EFI_INVALID_PARAMETER, VendorGuid, VariableName)); + return EFI_INVALID_PARAMETER; + } + if (DataSize != 0) { + if ((DataSize < Property->MinSize) || (DataSize > Property->MaxSize)) { + DEBUG ((EFI_D_INFO, "Variable Check DataSize fail(0x%x not in 0x%x - 0x%x) %r - %g:%s\n", DataSize, Property->MinSize, Property->MaxSize, EFI_INVALID_PARAMETER, VendorGuid, VariableName)); + return EFI_INVALID_PARAMETER; + } + } + } + } + + for (Index = 0; Index < mNumberOfVarCheckHandler; Index++) { + Status = mVarCheckHandlerTable[Index] ( + VariableName, + VendorGuid, + Attributes, + DataSize, + Data + ); + if (Status == EFI_WRITE_PROTECTED && RequestSource == VarCheckFromTrusted) { + // + // If RequestSource is trusted, then allow variable to be set even if it + // is write protected. + // + continue; + } + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_INFO, "Variable Check handler fail %r - %g:%s\n", Status, VendorGuid, VariableName)); + return Status; + } + } + return EFI_SUCCESS; +} |