+/**\r
+ Update the variable region with Variable information. These are the same \r
+ arguments as the EFI Variable services.\r
+\r
+ @param[in] VariableName Name of variable.\r
+ @param[in] VendorGuid Guid of variable.\r
+ @param[in] Data Variable data.\r
+ @param[in] DataSize Size of data. 0 means delete.\r
+ @param[in] Attributes Attribues of the variable.\r
+ @param[in] CacheVariable The variable information which is used to keep track of variable usage.\r
+ \r
+ @retval EFI_SUCCESS The update operation is success.\r
+ @retval EFI_OUT_OF_RESOURCES Variable region is full, can not write other data into this region.\r
+\r
+**/\r
+EFI_STATUS\r
+UpdateVariable (\r
+ IN CHAR16 *VariableName,\r
+ IN EFI_GUID *VendorGuid,\r
+ IN VOID *Data,\r
+ IN UINTN DataSize,\r
+ IN UINT32 Attributes OPTIONAL,\r
+ IN VARIABLE_POINTER_TRACK *CacheVariable\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ VARIABLE_HEADER *NextVariable;\r
+ UINTN ScratchSize;\r
+ UINTN NonVolatileVarableStoreSize;\r
+ UINTN VarNameOffset;\r
+ UINTN VarDataOffset;\r
+ UINTN VarNameSize;\r
+ UINTN VarSize;\r
+ BOOLEAN Volatile;\r
+ EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;\r
+ UINT8 State;\r
+ BOOLEAN Reclaimed;\r
+ VARIABLE_POINTER_TRACK *Variable;\r
+ VARIABLE_POINTER_TRACK NvVariable;\r
+ VARIABLE_STORE_HEADER *VariableStoreHeader;\r
+ UINTN CacheOffset;\r
+\r
+ if ((mVariableModuleGlobal->FvbInstance == NULL) && ((Attributes & EFI_VARIABLE_NON_VOLATILE) != 0)) {\r
+ //\r
+ // The FVB protocol is not ready. Trying to update NV variable prior to the installation\r
+ // of EFI_VARIABLE_WRITE_ARCH_PROTOCOL.\r
+ //\r
+ return EFI_NOT_AVAILABLE_YET; \r
+ }\r
+\r
+ if ((CacheVariable->CurrPtr == NULL) || CacheVariable->Volatile) {\r
+ Variable = CacheVariable;\r
+ } else {\r
+ //\r
+ // Update/Delete existing NV variable.\r
+ // CacheVariable points to the variable in the memory copy of Flash area\r
+ // Now let Variable points to the same variable in Flash area.\r
+ //\r
+ VariableStoreHeader = (VARIABLE_STORE_HEADER *) ((UINTN) mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase);\r
+ Variable = &NvVariable; \r
+ Variable->StartPtr = GetStartPointer (VariableStoreHeader);\r
+ Variable->EndPtr = GetEndPointer (VariableStoreHeader);\r
+ Variable->CurrPtr = (VARIABLE_HEADER *)((UINTN)Variable->StartPtr + ((UINTN)CacheVariable->CurrPtr - (UINTN)CacheVariable->StartPtr));\r
+ Variable->Volatile = FALSE;\r
+ } \r
+\r
+ Fvb = mVariableModuleGlobal->FvbInstance;\r
+ Reclaimed = FALSE;\r
+\r
+ if (Variable->CurrPtr != NULL) {\r
+ //\r
+ // Update/Delete existing variable.\r
+ //\r
+ if (AtRuntime ()) { \r
+ //\r
+ // If AtRuntime and the variable is Volatile and Runtime Access, \r
+ // the volatile is ReadOnly, and SetVariable should be aborted and \r
+ // return EFI_WRITE_PROTECTED.\r
+ //\r
+ if (Variable->Volatile) {\r
+ Status = EFI_WRITE_PROTECTED;\r
+ goto Done;\r
+ }\r
+ //\r
+ // Only variable that have NV attributes can be updated/deleted in Runtime.\r
+ //\r
+ if ((Variable->CurrPtr->Attributes & EFI_VARIABLE_NON_VOLATILE) == 0) {\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto Done; \r
+ }\r
+ }\r
+\r
+ //\r
+ // Setting a data variable with no access, or zero DataSize attributes\r
+ // causes it to be deleted.\r
+ //\r
+ if (DataSize == 0 || (Attributes & (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) == 0) { \r
+ State = Variable->CurrPtr->State;\r
+ State &= VAR_DELETED;\r
+\r
+ Status = UpdateVariableStore (\r
+ &mVariableModuleGlobal->VariableGlobal,\r
+ Variable->Volatile,\r
+ FALSE,\r
+ Fvb,\r
+ (UINTN) &Variable->CurrPtr->State,\r
+ sizeof (UINT8),\r
+ &State\r
+ ); \r
+ if (!EFI_ERROR (Status)) {\r
+ UpdateVariableInfo (VariableName, VendorGuid, Variable->Volatile, FALSE, FALSE, TRUE, FALSE);\r
+ if (!Variable->Volatile) {\r
+ CacheVariable->CurrPtr->State = State;\r
+ }\r
+ }\r
+ goto Done; \r
+ }\r
+ //\r
+ // If the variable is marked valid, and the same data has been passed in,\r
+ // then return to the caller immediately.\r
+ //\r
+ if (DataSizeOfVariable (Variable->CurrPtr) == DataSize &&\r
+ (CompareMem (Data, GetVariableDataPtr (Variable->CurrPtr), DataSize) == 0)) {\r
+ \r
+ UpdateVariableInfo (VariableName, VendorGuid, Variable->Volatile, FALSE, TRUE, FALSE, FALSE);\r
+ Status = EFI_SUCCESS;\r
+ goto Done;\r
+ } else if ((Variable->CurrPtr->State == VAR_ADDED) ||\r
+ (Variable->CurrPtr->State == (VAR_ADDED & VAR_IN_DELETED_TRANSITION))) {\r
+\r
+ //\r
+ // Mark the old variable as in delete transition.\r
+ //\r
+ State = Variable->CurrPtr->State;\r
+ State &= VAR_IN_DELETED_TRANSITION;\r
+\r
+ Status = UpdateVariableStore (\r
+ &mVariableModuleGlobal->VariableGlobal,\r
+ Variable->Volatile,\r
+ FALSE,\r
+ Fvb,\r
+ (UINTN) &Variable->CurrPtr->State,\r
+ sizeof (UINT8),\r
+ &State\r
+ ); \r
+ if (EFI_ERROR (Status)) {\r
+ goto Done; \r
+ } \r
+ if (!Variable->Volatile) {\r
+ CacheVariable->CurrPtr->State = State;\r
+ }\r
+ } \r
+ } else {\r
+ //\r
+ // Not found existing variable. Create a new variable.\r
+ // \r
+ \r
+ //\r
+ // Make sure we are trying to create a new variable.\r
+ // Setting a data variable with zero DataSize or no access attributes means to delete it. \r
+ //\r
+ if (DataSize == 0 || (Attributes & (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) == 0) {\r
+ Status = EFI_NOT_FOUND;\r
+ goto Done;\r
+ }\r
+ \r
+ //\r
+ // Only variable have NV|RT attribute can be created in Runtime.\r
+ //\r
+ if (AtRuntime () &&\r
+ (((Attributes & EFI_VARIABLE_RUNTIME_ACCESS) == 0) || ((Attributes & EFI_VARIABLE_NON_VOLATILE) == 0))) {\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto Done;\r
+ } \r
+ }\r
+\r
+ //\r
+ // Function part - create a new variable and copy the data.\r
+ // Both update a variable and create a variable will come here.\r
+\r
+ //\r
+ // Tricky part: Use scratch data area at the end of volatile variable store\r
+ // as a temporary storage.\r
+ //\r
+ NextVariable = GetEndPointer ((VARIABLE_STORE_HEADER *) ((UINTN) mVariableModuleGlobal->VariableGlobal.VolatileVariableBase));\r
+ ScratchSize = MAX (PcdGet32 (PcdMaxVariableSize), PcdGet32 (PcdMaxHardwareErrorVariableSize));\r
+\r
+ SetMem (NextVariable, ScratchSize, 0xff);\r
+\r
+ NextVariable->StartId = VARIABLE_DATA;\r
+ NextVariable->Attributes = Attributes;\r
+ //\r
+ // NextVariable->State = VAR_ADDED;\r
+ //\r
+ NextVariable->Reserved = 0;\r
+ VarNameOffset = sizeof (VARIABLE_HEADER);\r
+ VarNameSize = StrSize (VariableName);\r
+ CopyMem (\r
+ (UINT8 *) ((UINTN) NextVariable + VarNameOffset),\r
+ VariableName,\r
+ VarNameSize\r
+ );\r
+ VarDataOffset = VarNameOffset + VarNameSize + GET_PAD_SIZE (VarNameSize);\r
+ CopyMem (\r
+ (UINT8 *) ((UINTN) NextVariable + VarDataOffset),\r
+ Data,\r
+ DataSize\r
+ );\r
+ CopyMem (&NextVariable->VendorGuid, VendorGuid, sizeof (EFI_GUID));\r
+ //\r
+ // There will be pad bytes after Data, the NextVariable->NameSize and\r
+ // NextVariable->DataSize should not include pad size so that variable\r
+ // service can get actual size in GetVariable.\r
+ //\r
+ NextVariable->NameSize = (UINT32)VarNameSize;\r
+ NextVariable->DataSize = (UINT32)DataSize;\r
+\r
+ //\r
+ // The actual size of the variable that stores in storage should\r
+ // include pad size.\r
+ //\r
+ VarSize = VarDataOffset + DataSize + GET_PAD_SIZE (DataSize);\r
+ if ((Attributes & EFI_VARIABLE_NON_VOLATILE) != 0) {\r
+ //\r
+ // Create a nonvolatile variable.\r
+ //\r
+ Volatile = FALSE;\r
+ NonVolatileVarableStoreSize = ((VARIABLE_STORE_HEADER *)(UINTN)(mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase))->Size;\r
+ if ((((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != 0) \r
+ && ((VarSize + mVariableModuleGlobal->HwErrVariableTotalSize) > PcdGet32 (PcdHwErrStorageSize)))\r
+ || (((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == 0) \r
+ && ((VarSize + mVariableModuleGlobal->CommonVariableTotalSize) > NonVolatileVarableStoreSize - sizeof (VARIABLE_STORE_HEADER) - PcdGet32 (PcdHwErrStorageSize)))) {\r
+ if (AtRuntime ()) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Done;\r
+ }\r
+ //\r
+ // Perform garbage collection & reclaim operation.\r
+ //\r
+ Status = Reclaim (mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase, \r
+ &mVariableModuleGlobal->NonVolatileLastVariableOffset, FALSE, Variable->CurrPtr);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Done;\r
+ }\r
+ //\r
+ // If still no enough space, return out of resources.\r
+ //\r
+ if ((((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != 0) \r
+ && ((VarSize + mVariableModuleGlobal->HwErrVariableTotalSize) > PcdGet32 (PcdHwErrStorageSize)))\r
+ || (((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == 0) \r
+ && ((VarSize + mVariableModuleGlobal->CommonVariableTotalSize) > NonVolatileVarableStoreSize - sizeof (VARIABLE_STORE_HEADER) - PcdGet32 (PcdHwErrStorageSize)))) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Done;\r
+ }\r
+ Reclaimed = TRUE;\r
+ }\r
+ //\r
+ // Four steps\r
+ // 1. Write variable header\r
+ // 2. Set variable state to header valid \r
+ // 3. Write variable data\r
+ // 4. Set variable state to valid\r
+ //\r
+ //\r
+ // Step 1:\r
+ //\r
+ CacheOffset = mVariableModuleGlobal->NonVolatileLastVariableOffset;\r
+ Status = UpdateVariableStore (\r
+ &mVariableModuleGlobal->VariableGlobal,\r
+ FALSE,\r
+ TRUE,\r
+ Fvb,\r
+ mVariableModuleGlobal->NonVolatileLastVariableOffset,\r
+ sizeof (VARIABLE_HEADER),\r
+ (UINT8 *) NextVariable\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto Done;\r
+ }\r
+\r
+ //\r
+ // Step 2:\r
+ //\r
+ NextVariable->State = VAR_HEADER_VALID_ONLY;\r
+ Status = UpdateVariableStore (\r
+ &mVariableModuleGlobal->VariableGlobal,\r
+ FALSE,\r
+ TRUE,\r
+ Fvb,\r
+ mVariableModuleGlobal->NonVolatileLastVariableOffset + OFFSET_OF (VARIABLE_HEADER, State),\r
+ sizeof (UINT8),\r
+ &NextVariable->State\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto Done;\r
+ }\r
+ //\r
+ // Step 3:\r
+ //\r
+ Status = UpdateVariableStore (\r
+ &mVariableModuleGlobal->VariableGlobal,\r
+ FALSE,\r
+ TRUE,\r
+ Fvb,\r
+ mVariableModuleGlobal->NonVolatileLastVariableOffset + sizeof (VARIABLE_HEADER),\r
+ (UINT32) VarSize - sizeof (VARIABLE_HEADER),\r
+ (UINT8 *) NextVariable + sizeof (VARIABLE_HEADER)\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto Done;\r
+ }\r
+ //\r
+ // Step 4:\r
+ //\r
+ NextVariable->State = VAR_ADDED;\r
+ Status = UpdateVariableStore (\r
+ &mVariableModuleGlobal->VariableGlobal,\r
+ FALSE,\r
+ TRUE,\r
+ Fvb,\r
+ mVariableModuleGlobal->NonVolatileLastVariableOffset + OFFSET_OF (VARIABLE_HEADER, State),\r
+ sizeof (UINT8),\r
+ &NextVariable->State\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto Done;\r
+ }\r
+\r
+ mVariableModuleGlobal->NonVolatileLastVariableOffset += HEADER_ALIGN (VarSize);\r
+\r
+ if ((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != 0) {\r
+ mVariableModuleGlobal->HwErrVariableTotalSize += HEADER_ALIGN (VarSize);\r
+ } else {\r
+ mVariableModuleGlobal->CommonVariableTotalSize += HEADER_ALIGN (VarSize);\r
+ }\r
+ //\r
+ // update the memory copy of Flash region.\r
+ //\r
+ CopyMem ((UINT8 *)mNvVariableCache + CacheOffset, (UINT8 *)NextVariable, VarSize);\r
+ } else {\r
+ //\r
+ // Create a volatile variable.\r
+ // \r
+ Volatile = TRUE;\r
+\r
+ if ((UINT32) (VarSize + mVariableModuleGlobal->VolatileLastVariableOffset) >\r
+ ((VARIABLE_STORE_HEADER *) ((UINTN) (mVariableModuleGlobal->VariableGlobal.VolatileVariableBase)))->Size) {\r
+ //\r
+ // Perform garbage collection & reclaim operation.\r
+ //\r
+ Status = Reclaim (mVariableModuleGlobal->VariableGlobal.VolatileVariableBase, \r
+ &mVariableModuleGlobal->VolatileLastVariableOffset, TRUE, Variable->CurrPtr);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Done;\r
+ }\r
+ //\r
+ // If still no enough space, return out of resources.\r
+ //\r
+ if ((UINT32) (VarSize + mVariableModuleGlobal->VolatileLastVariableOffset) >\r
+ ((VARIABLE_STORE_HEADER *) ((UINTN) (mVariableModuleGlobal->VariableGlobal.VolatileVariableBase)))->Size\r
+ ) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Done;\r
+ }\r
+ Reclaimed = TRUE;\r
+ }\r
+\r
+ NextVariable->State = VAR_ADDED;\r
+ Status = UpdateVariableStore (\r
+ &mVariableModuleGlobal->VariableGlobal,\r
+ TRUE,\r
+ TRUE,\r
+ Fvb,\r
+ mVariableModuleGlobal->VolatileLastVariableOffset,\r
+ (UINT32) VarSize,\r
+ (UINT8 *) NextVariable\r
+ );\r