1. Uefi2.1 feature - Add Hardware Error Record Persistence Support
[mirror_edk2.git] / MdeModulePkg / Universal / Variable / EmuRuntimeDxe / EmuVariable.c
index f56cf04215501fed3401edf07bca331a49798ec6..2c5dd365db20961cc89cab300f0d38eb4388e660 100644 (file)
@@ -60,7 +60,6 @@ ReleaseLockOnlyAtBootTime (
 \r
 STATIC\r
 UINT8 *\r
-EFIAPI\r
 GetVariableDataPtr (\r
   IN  VARIABLE_HEADER   *Variable\r
   )\r
@@ -91,7 +90,6 @@ Returns:
 \r
 STATIC\r
 VARIABLE_HEADER *\r
-EFIAPI\r
 GetNextVariablePtr (\r
   IN  VARIABLE_HEADER   *Variable\r
   )\r
@@ -132,7 +130,6 @@ Returns:
 \r
 STATIC\r
 VARIABLE_HEADER *\r
-EFIAPI\r
 GetEndPointer (\r
   IN VARIABLE_STORE_HEADER       *VolHeader\r
   )\r
@@ -160,7 +157,6 @@ Returns:
 \r
 STATIC\r
 EFI_STATUS\r
-EFIAPI\r
 FindVariable (\r
   IN  CHAR16                  *VariableName,\r
   IN  EFI_GUID                *VendorGuid,\r
@@ -278,7 +274,11 @@ Arguments:
 \r
 Returns:\r
 \r
-  EFI STATUS\r
+  EFI_INVALID_PARAMETER       - Invalid parameter\r
+  EFI_SUCCESS                 - Find the specified variable\r
+  EFI_NOT_FOUND               - Not found\r
+  EFI_BUFFER_TO_SMALL         - DataSize is too small for the result\r
+\r
 \r
 --*/\r
 {\r
@@ -463,7 +463,12 @@ Arguments:
 \r
 Returns:\r
 \r
-  EFI STATUS\r
+  EFI_INVALID_PARAMETER           - Invalid parameter\r
+  EFI_SUCCESS                     - Set successfully\r
+  EFI_OUT_OF_RESOURCES            - Resource not enough to set variable\r
+  EFI_NOT_FOUND                   - Not found\r
+  EFI_DEVICE_ERROR                - Variable can not be saved due to hardware failure\r
+  EFI_WRITE_PROTECTED             - Variable is read-only\r
 \r
 --*/\r
 {\r
@@ -475,147 +480,188 @@ Returns:
   UINTN                   VarDataOffset;\r
   UINTN                   VarSize;\r
 \r
+  //\r
+  // Check input parameters\r
+  //\r
   if (VariableName == NULL || VariableName[0] == 0 || VendorGuid == NULL) {\r
     return EFI_INVALID_PARAMETER;\r
+  }  \r
+  //\r
+  //  Make sure if runtime bit is set, boot service bit is set also\r
+  //\r
+  if ((Attributes & (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) == EFI_VARIABLE_RUNTIME_ACCESS) {\r
+    return EFI_INVALID_PARAMETER;\r
   }\r
-\r
+  //\r
+  //  The size of the VariableName, including the Unicode Null in bytes plus\r
+  //  the DataSize is limited to maximum size of MAX_HARDWARE_ERROR_VARIABLE_SIZE (32K)\r
+  //  bytes for HwErrRec, and MAX_VARIABLE_SIZE (1024) bytes for the others.\r
+  //\r
+  if ((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD) {\r
+    if ((DataSize > MAX_HARDWARE_ERROR_VARIABLE_SIZE) ||                                                       \r
+        (sizeof (VARIABLE_HEADER) + StrSize (VariableName) + DataSize > MAX_HARDWARE_ERROR_VARIABLE_SIZE)) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }    \r
+  } else {\r
+  //\r
+  //  The size of the VariableName, including the Unicode Null in bytes plus\r
+  //  the DataSize is limited to maximum size of MAX_VARIABLE_SIZE (1024) bytes.\r
+  //\r
+    if ((DataSize > MAX_VARIABLE_SIZE) ||\r
+        (sizeof (VARIABLE_HEADER) + StrSize (VariableName) + DataSize > MAX_VARIABLE_SIZE)) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }  \r
+  }  \r
+  //\r
+  // Check whether the input variable is already existed\r
+  //\r
+  \r
   Status = FindVariable (VariableName, VendorGuid, &Variable, Global);\r
 \r
-  if (Status == EFI_INVALID_PARAMETER) {\r
-    goto Done;\r
-  } else if (!EFI_ERROR (Status) && Variable.Volatile && EfiAtRuntime()) {\r
+  if (Status == EFI_SUCCESS && Variable.CurrPtr != NULL) {\r
     //\r
-    // If EfiAtRuntime 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
-    Status = EFI_WRITE_PROTECTED;\r
-    goto Done;\r
-  } else if (sizeof (VARIABLE_HEADER) + (StrSize (VariableName) + DataSize) > MAX_VARIABLE_SIZE) {\r
-    //\r
-    //  The size of the VariableName, including the Unicode Null in bytes plus\r
-    //  the DataSize is limited to maximum size of MAX_VARIABLE_SIZE (1024) bytes.\r
-    //\r
-    Status = EFI_INVALID_PARAMETER;\r
-    goto Done;\r
-  } else if ((Attributes & (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) == EFI_VARIABLE_RUNTIME_ACCESS\r
-          ) {\r
+    // Update/Delete existing variable\r
     //\r
-    //  Make sure if runtime bit is set, boot service bit is set also\r
-    //\r
-    Status = EFI_INVALID_PARAMETER;\r
-    goto Done;\r
-  } else if (EfiAtRuntime () && Attributes && !(Attributes & EFI_VARIABLE_RUNTIME_ACCESS)) {\r
-    //\r
-    // Runtime but Attribute is not Runtime\r
-    //\r
-    Status = EFI_INVALID_PARAMETER;\r
-    goto Done;\r
-  } else if (EfiAtRuntime () && Attributes && !(Attributes & EFI_VARIABLE_NON_VOLATILE)) {\r
-    //\r
-    // Cannot set volatile variable in Runtime\r
-    //\r
-    Status = EFI_INVALID_PARAMETER;\r
-    goto Done;\r
-  } else if (DataSize == 0 || (Attributes & (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) == 0) {\r
+\r
+    if (EfiAtRuntime ()) {        \r
+      //\r
+      // If EfiAtRuntime 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 have NV attribute can be updated/deleted in Runtime\r
+      //\r
+      if (!(Variable.CurrPtr->Attributes & EFI_VARIABLE_NON_VOLATILE)) {\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
     // specified causes it to be deleted.\r
     //\r
-    if (!EFI_ERROR (Status)) {\r
+    if (DataSize == 0 || (Attributes & (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) == 0) {\r
       Variable.CurrPtr->State &= VAR_DELETED;\r
       Status = EFI_SUCCESS;\r
       goto Done;\r
     }\r
 \r
-    Status = EFI_NOT_FOUND;\r
-    goto Done;\r
-  } else {\r
-    if (!EFI_ERROR (Status)) {\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 (Variable.CurrPtr->DataSize == DataSize &&\r
+        !CompareMem (Data, GetVariableDataPtr (Variable.CurrPtr), DataSize)\r
+          ) {\r
+      Status = EFI_SUCCESS;\r
+      goto Done;\r
+    } else if (Variable.CurrPtr->State == VAR_ADDED) {\r
       //\r
-      // If the variable is marked valid and the same data has been passed in\r
-      // then return to the caller immediately.\r
+      // Mark the old variable as in delete transition\r
       //\r
-      if (Variable.CurrPtr->DataSize == DataSize &&\r
-          !CompareMem (Data, GetVariableDataPtr (Variable.CurrPtr), DataSize)\r
-            ) {\r
-        Status = EFI_SUCCESS;\r
-        goto Done;\r
-      } else if (Variable.CurrPtr->State == VAR_ADDED) {\r
-        //\r
-        // Mark the old variable as in delete transition\r
-        //\r
-        Variable.CurrPtr->State &= VAR_IN_DELETED_TRANSITION;\r
-      }\r
+      Variable.CurrPtr->State &= VAR_IN_DELETED_TRANSITION;\r
     }\r
+    \r
+  } else if (Status == EFI_NOT_FOUND) {\r
     //\r
-    // Create a new variable and copy the data.\r
+    // Create a new variable\r
+    //  \r
+    \r
     //\r
-    VarNameOffset = sizeof (VARIABLE_HEADER);\r
-    VarNameSize   = StrSize (VariableName);\r
-    VarDataOffset = VarNameOffset + VarNameSize + GET_PAD_SIZE (VarNameSize);\r
-    VarSize       = VarDataOffset + DataSize + GET_PAD_SIZE (DataSize);\r
-\r
-    if (Attributes & EFI_VARIABLE_NON_VOLATILE) {\r
-      if ((UINT32) (VarSize +*NonVolatileOffset) >\r
-            ((VARIABLE_STORE_HEADER *) ((UINTN) (Global->NonVolatileVariableBase)))->Size\r
-            ) {\r
-        Status = EFI_OUT_OF_RESOURCES;\r
-        goto Done;\r
-      }\r
-\r
-      NextVariable        = (VARIABLE_HEADER *) (UINT8 *) (*NonVolatileOffset + (UINTN) Global->NonVolatileVariableBase);\r
-      *NonVolatileOffset  = *NonVolatileOffset + VarSize;\r
-    } else {\r
-      if (EfiAtRuntime ()) {\r
-        Status = EFI_INVALID_PARAMETER;\r
-        goto Done;\r
-      }\r
-\r
-      if ((UINT32) (VarSize +*VolatileOffset) >\r
-            ((VARIABLE_STORE_HEADER *) ((UINTN) (Global->VolatileVariableBase)))->Size\r
-            ) {\r
-        Status = EFI_OUT_OF_RESOURCES;\r
-        goto Done;\r
-      }\r
-\r
-      NextVariable    = (VARIABLE_HEADER *) (UINT8 *) (*VolatileOffset + (UINTN) Global->VolatileVariableBase);\r
-      *VolatileOffset = *VolatileOffset + VarSize;\r
+    // Make sure we are trying to create a new variable.\r
+    // Setting a data variable with no access, or zero DataSize 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
-    NextVariable->StartId     = VARIABLE_DATA;\r
-    NextVariable->Attributes  = Attributes;\r
-    NextVariable->State       = VAR_ADDED;\r
-    NextVariable->Reserved    = 0;\r
-\r
+        \r
     //\r
-    // There will be pad bytes after Data, the NextVariable->NameSize and\r
-    // NextVariable->NameSize should not include pad size so that variable\r
-    // service can get actual size in GetVariable\r
+    // Only variable have NV|RT attribute can be created in Runtime\r
     //\r
-    NextVariable->NameSize  = (UINT32)VarNameSize;\r
-    NextVariable->DataSize  = (UINT32)DataSize;\r
-\r
-    CopyMem (&NextVariable->VendorGuid, VendorGuid, sizeof (EFI_GUID));\r
-    CopyMem (\r
-      (UINT8 *) ((UINTN) NextVariable + VarNameOffset),\r
-      VariableName,\r
-      VarNameSize\r
-      );\r
-    CopyMem (\r
-      (UINT8 *) ((UINTN) NextVariable + VarDataOffset),\r
-      Data,\r
-      DataSize\r
-      );\r
-\r
+    if (EfiAtRuntime () &&\r
+        (!(Attributes & EFI_VARIABLE_RUNTIME_ACCESS) || !(Attributes & EFI_VARIABLE_NON_VOLATILE))) {\r
+      Status = EFI_INVALID_PARAMETER;\r
+      goto Done;\r
+    }         \r
+  } else {\r
     //\r
-    // Mark the old variable as deleted\r
+    // Status should be EFI_INVALID_PARAMETER here according to return status of FindVariable().\r
     //\r
-    if (!EFI_ERROR (Status)) {\r
-      Variable.CurrPtr->State &= VAR_DELETED;\r
+    ASSERT (Status == EFI_INVALID_PARAMETER);\r
+    goto Done;\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
+  VarNameOffset = sizeof (VARIABLE_HEADER);\r
+  VarNameSize   = StrSize (VariableName);\r
+  VarDataOffset = VarNameOffset + VarNameSize + GET_PAD_SIZE (VarNameSize);\r
+  VarSize       = VarDataOffset + DataSize + GET_PAD_SIZE (DataSize);\r
+\r
+  if (Attributes & EFI_VARIABLE_NON_VOLATILE) {\r
+    if ((UINT32) (VarSize +*NonVolatileOffset) >\r
+          ((VARIABLE_STORE_HEADER *) ((UINTN) (Global->NonVolatileVariableBase)))->Size\r
+          ) {\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+      goto Done;\r
     }\r
+\r
+    NextVariable        = (VARIABLE_HEADER *) (UINT8 *) (*NonVolatileOffset + (UINTN) Global->NonVolatileVariableBase);\r
+    *NonVolatileOffset  = *NonVolatileOffset + VarSize;\r
+  } else {\r
+    if ((UINT32) (VarSize +*VolatileOffset) >\r
+          ((VARIABLE_STORE_HEADER *) ((UINTN) (Global->VolatileVariableBase)))->Size\r
+          ) {\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+      goto Done;\r
+    }\r
+\r
+    NextVariable    = (VARIABLE_HEADER *) (UINT8 *) (*VolatileOffset + (UINTN) Global->VolatileVariableBase);\r
+    *VolatileOffset = *VolatileOffset + VarSize;\r
   }\r
 \r
+  NextVariable->StartId     = VARIABLE_DATA;\r
+  NextVariable->Attributes  = Attributes;\r
+  NextVariable->State       = VAR_ADDED;\r
+  NextVariable->Reserved    = 0;\r
+\r
+  //\r
+  // There will be pad bytes after Data, the NextVariable->NameSize and\r
+  // NextVariable->NameSize 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
+  CopyMem (&NextVariable->VendorGuid, VendorGuid, sizeof (EFI_GUID));\r
+  CopyMem (\r
+    (UINT8 *) ((UINTN) NextVariable + VarNameOffset),\r
+    VariableName,\r
+    VarNameSize\r
+    );\r
+  CopyMem (\r
+    (UINT8 *) ((UINTN) NextVariable + VarDataOffset),\r
+    Data,\r
+    DataSize\r
+    );\r
+\r
+  //\r
+  // Mark the old variable as deleted\r
+  //\r
+  if (!EFI_ERROR (Status)) {\r
+    Variable.CurrPtr->State &= VAR_DELETED;\r
+  }\r
+  \r
   Status = EFI_SUCCESS;\r
 Done:\r
   ReleaseLockOnlyAtBootTime (&Global->VariableServicesLock);\r
@@ -645,8 +691,8 @@ Arguments:
   MaximumVariableStorageSize      Pointer to the maximum size of the storage space available\r
                                   for the EFI variables associated with the attributes specified.\r
   RemainingVariableStorageSize    Pointer to the remaining size of the storage space available\r
-                                  for the EFI variables associated with the attributes specified.\r
-  MaximumVariableSize             Pointer to the maximum size of the individual EFI variables\r
+                                  for EFI variables associated with the attributes specified.\r
+  MaximumVariableSize             Pointer to the maximum size of an individual EFI variables\r
                                   associated with the attributes specified.\r
   Global                          Pointer to VARIABLE_GLOBAL structure.\r
   Instance                        Instance of the Firmware Volume.\r
@@ -665,15 +711,15 @@ Returns:
   UINT64                 VariableSize;\r
   VARIABLE_STORE_HEADER  *VariableStoreHeader;\r
 \r
-  if(MaximumVariableStorageSize == NULL || RemainingVariableStorageSize == NULL || MaximumVariableSize == NULL) {\r
+  if(MaximumVariableStorageSize == NULL || RemainingVariableStorageSize == NULL || MaximumVariableSize == NULL || Attributes == 0) {\r
     return EFI_INVALID_PARAMETER;\r
   }\r
-\r
-  if((Attributes & (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS)) == 0) {\r
+  \r
+  if((Attributes & (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_HARDWARE_ERROR_RECORD)) == 0) {\r
     //\r
     // Make sure the Attributes combination is supported by the platform.\r
     //\r
-    return EFI_UNSUPPORTED;\r
+    return EFI_UNSUPPORTED;  \r
   } else if ((Attributes & (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) == EFI_VARIABLE_RUNTIME_ACCESS) {\r
     //\r
     // Make sure if runtime bit is set, boot service bit is set also.\r
@@ -713,9 +759,16 @@ Returns:
   *RemainingVariableStorageSize = VariableStoreHeader->Size - sizeof (VARIABLE_STORE_HEADER);\r
 \r
   //\r
-  // Let *MaximumVariableSize be MAX_VARIABLE_SIZE\r
+  // Let *MaximumVariableSize be MAX_VARIABLE_SIZE with the exception of the variable header size.\r
   //\r
-  *MaximumVariableSize = MAX_VARIABLE_SIZE;\r
+  *MaximumVariableSize = MAX_VARIABLE_SIZE - sizeof (VARIABLE_HEADER);\r
+\r
+  //\r
+  // Harware error record variable needs larger size.\r
+  //\r
+  if ((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD) {\r
+    *MaximumVariableSize = MAX_HARDWARE_ERROR_VARIABLE_SIZE - sizeof (VARIABLE_HEADER);\r
+  }\r
 \r
   //\r
   // Point to the starting address of the variables.\r
@@ -743,13 +796,18 @@ Returns:
     Variable = NextVariable;\r
   }\r
 \r
+  if (*RemainingVariableStorageSize < sizeof (VARIABLE_HEADER)) {\r
+    *MaximumVariableSize = 0;\r
+  } else if ((*RemainingVariableStorageSize - sizeof (VARIABLE_HEADER)) < *MaximumVariableSize) {\r
+    *MaximumVariableSize = *RemainingVariableStorageSize - sizeof (VARIABLE_HEADER);\r
+  }\r
+  \r
   ReleaseLockOnlyAtBootTime (&Global->VariableServicesLock);\r
   return EFI_SUCCESS;\r
 }\r
 \r
 STATIC\r
 EFI_STATUS\r
-EFIAPI\r
 InitializeVariableStore (\r
   OUT EFI_PHYSICAL_ADDRESS  *VariableBase,\r
   OUT UINTN                 *LastVariableOffset\r