]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/Variable/RuntimeDxe/VariableNonVolatile.c
MdeModulePkg/Variable: Add a file for NV variable functions
[mirror_edk2.git] / MdeModulePkg / Universal / Variable / RuntimeDxe / VariableNonVolatile.c
CommitLineData
6b0d7b01
MK
1/** @file\r
2 Common variable non-volatile store routines.\r
3\r
4Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>\r
5SPDX-License-Identifier: BSD-2-Clause-Patent\r
6\r
7**/\r
8\r
9#include "VariableNonVolatile.h"\r
10#include "VariableParsing.h"\r
11\r
12extern VARIABLE_MODULE_GLOBAL *mVariableModuleGlobal;\r
13\r
14/**\r
15 Get non-volatile maximum variable size.\r
16\r
17 @return Non-volatile maximum variable size.\r
18\r
19**/\r
20UINTN\r
21GetNonVolatileMaxVariableSize (\r
22 VOID\r
23 )\r
24{\r
25 if (PcdGet32 (PcdHwErrStorageSize) != 0) {\r
26 return MAX (MAX (PcdGet32 (PcdMaxVariableSize), PcdGet32 (PcdMaxAuthVariableSize)),\r
27 PcdGet32 (PcdMaxHardwareErrorVariableSize));\r
28 } else {\r
29 return MAX (PcdGet32 (PcdMaxVariableSize), PcdGet32 (PcdMaxAuthVariableSize));\r
30 }\r
31}\r
32\r
33/**\r
34 Init emulated non-volatile variable store.\r
35\r
36 @param[out] VariableStoreBase Output pointer to emulated non-volatile variable store base.\r
37\r
38 @retval EFI_SUCCESS Function successfully executed.\r
39 @retval EFI_OUT_OF_RESOURCES Fail to allocate enough memory resource.\r
40\r
41**/\r
42EFI_STATUS\r
43InitEmuNonVolatileVariableStore (\r
44 OUT EFI_PHYSICAL_ADDRESS *VariableStoreBase\r
45 )\r
46{\r
47 VARIABLE_STORE_HEADER *VariableStore;\r
48 UINT32 VariableStoreLength;\r
49 BOOLEAN FullyInitializeStore;\r
50 UINT32 HwErrStorageSize;\r
51\r
52 FullyInitializeStore = TRUE;\r
53\r
54 VariableStoreLength = PcdGet32 (PcdVariableStoreSize);\r
55 ASSERT (sizeof (VARIABLE_STORE_HEADER) <= VariableStoreLength);\r
56\r
57 //\r
58 // Allocate memory for variable store.\r
59 //\r
60 if (PcdGet64 (PcdEmuVariableNvStoreReserved) == 0) {\r
61 VariableStore = (VARIABLE_STORE_HEADER *) AllocateRuntimePool (VariableStoreLength);\r
62 if (VariableStore == NULL) {\r
63 return EFI_OUT_OF_RESOURCES;\r
64 }\r
65 } else {\r
66 //\r
67 // A memory location has been reserved for the NV variable store. Certain\r
68 // platforms may be able to preserve a memory range across system resets,\r
69 // thereby providing better NV variable emulation.\r
70 //\r
71 VariableStore =\r
72 (VARIABLE_STORE_HEADER *)(VOID*)(UINTN)\r
73 PcdGet64 (PcdEmuVariableNvStoreReserved);\r
74 if ((VariableStore->Size == VariableStoreLength) &&\r
75 (CompareGuid (&VariableStore->Signature, &gEfiAuthenticatedVariableGuid) ||\r
76 CompareGuid (&VariableStore->Signature, &gEfiVariableGuid)) &&\r
77 (VariableStore->Format == VARIABLE_STORE_FORMATTED) &&\r
78 (VariableStore->State == VARIABLE_STORE_HEALTHY)) {\r
79 DEBUG((\r
80 DEBUG_INFO,\r
81 "Variable Store reserved at %p appears to be valid\n",\r
82 VariableStore\r
83 ));\r
84 FullyInitializeStore = FALSE;\r
85 }\r
86 }\r
87\r
88 if (FullyInitializeStore) {\r
89 SetMem (VariableStore, VariableStoreLength, 0xff);\r
90 //\r
91 // Use gEfiAuthenticatedVariableGuid for potential auth variable support.\r
92 //\r
93 CopyGuid (&VariableStore->Signature, &gEfiAuthenticatedVariableGuid);\r
94 VariableStore->Size = VariableStoreLength;\r
95 VariableStore->Format = VARIABLE_STORE_FORMATTED;\r
96 VariableStore->State = VARIABLE_STORE_HEALTHY;\r
97 VariableStore->Reserved = 0;\r
98 VariableStore->Reserved1 = 0;\r
99 }\r
100\r
101 *VariableStoreBase = (EFI_PHYSICAL_ADDRESS) (UINTN) VariableStore;\r
102\r
103 HwErrStorageSize = PcdGet32 (PcdHwErrStorageSize);\r
104\r
105 //\r
106 // Note that in EdkII variable driver implementation, Hardware Error Record type variable\r
107 // is stored with common variable in the same NV region. So the platform integrator should\r
108 // ensure that the value of PcdHwErrStorageSize is less than the value of\r
109 // (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER)).\r
110 //\r
111 ASSERT (HwErrStorageSize < (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER)));\r
112\r
113 mVariableModuleGlobal->CommonVariableSpace = ((UINTN) VariableStoreLength - sizeof (VARIABLE_STORE_HEADER) - HwErrStorageSize);\r
114 mVariableModuleGlobal->CommonMaxUserVariableSpace = mVariableModuleGlobal->CommonVariableSpace;\r
115 mVariableModuleGlobal->CommonRuntimeVariableSpace = mVariableModuleGlobal->CommonVariableSpace;\r
116\r
117 return EFI_SUCCESS;\r
118}\r
119\r
120/**\r
121 Init real non-volatile variable store.\r
122\r
123 @param[out] VariableStoreBase Output pointer to real non-volatile variable store base.\r
124\r
125 @retval EFI_SUCCESS Function successfully executed.\r
126 @retval EFI_OUT_OF_RESOURCES Fail to allocate enough memory resource.\r
127 @retval EFI_VOLUME_CORRUPTED Variable Store or Firmware Volume for Variable Store is corrupted.\r
128\r
129**/\r
130EFI_STATUS\r
131InitRealNonVolatileVariableStore (\r
132 OUT EFI_PHYSICAL_ADDRESS *VariableStoreBase\r
133 )\r
134{\r
135 EFI_FIRMWARE_VOLUME_HEADER *FvHeader;\r
136 VARIABLE_STORE_HEADER *VariableStore;\r
137 UINT32 VariableStoreLength;\r
138 EFI_HOB_GUID_TYPE *GuidHob;\r
139 EFI_PHYSICAL_ADDRESS NvStorageBase;\r
140 UINT8 *NvStorageData;\r
141 UINT32 NvStorageSize;\r
142 FAULT_TOLERANT_WRITE_LAST_WRITE_DATA *FtwLastWriteData;\r
143 UINT32 BackUpOffset;\r
144 UINT32 BackUpSize;\r
145 UINT32 HwErrStorageSize;\r
146 UINT32 MaxUserNvVariableSpaceSize;\r
147 UINT32 BoottimeReservedNvVariableSpaceSize;\r
148 EFI_STATUS Status;\r
149 VOID *FtwProtocol;\r
150\r
151 mVariableModuleGlobal->FvbInstance = NULL;\r
152\r
153 //\r
154 // Allocate runtime memory used for a memory copy of the FLASH region.\r
155 // Keep the memory and the FLASH in sync as updates occur.\r
156 //\r
157 NvStorageSize = PcdGet32 (PcdFlashNvStorageVariableSize);\r
158 NvStorageData = AllocateRuntimeZeroPool (NvStorageSize);\r
159 if (NvStorageData == NULL) {\r
160 return EFI_OUT_OF_RESOURCES;\r
161 }\r
162\r
163 NvStorageBase = NV_STORAGE_VARIABLE_BASE;\r
164 ASSERT (NvStorageBase != 0);\r
165\r
166 //\r
167 // Copy NV storage data to the memory buffer.\r
168 //\r
169 CopyMem (NvStorageData, (UINT8 *) (UINTN) NvStorageBase, NvStorageSize);\r
170\r
171 Status = GetFtwProtocol ((VOID **)&FtwProtocol);\r
172 //\r
173 // If FTW protocol has been installed, no need to check FTW last write data hob.\r
174 //\r
175 if (EFI_ERROR (Status)) {\r
176 //\r
177 // Check the FTW last write data hob.\r
178 //\r
179 GuidHob = GetFirstGuidHob (&gEdkiiFaultTolerantWriteGuid);\r
180 if (GuidHob != NULL) {\r
181 FtwLastWriteData = (FAULT_TOLERANT_WRITE_LAST_WRITE_DATA *) GET_GUID_HOB_DATA (GuidHob);\r
182 if (FtwLastWriteData->TargetAddress == NvStorageBase) {\r
183 DEBUG ((DEBUG_INFO, "Variable: NV storage is backed up in spare block: 0x%x\n", (UINTN) FtwLastWriteData->SpareAddress));\r
184 //\r
185 // Copy the backed up NV storage data to the memory buffer from spare block.\r
186 //\r
187 CopyMem (NvStorageData, (UINT8 *) (UINTN) (FtwLastWriteData->SpareAddress), NvStorageSize);\r
188 } else if ((FtwLastWriteData->TargetAddress > NvStorageBase) &&\r
189 (FtwLastWriteData->TargetAddress < (NvStorageBase + NvStorageSize))) {\r
190 //\r
191 // Flash NV storage from the Offset is backed up in spare block.\r
192 //\r
193 BackUpOffset = (UINT32) (FtwLastWriteData->TargetAddress - NvStorageBase);\r
194 BackUpSize = NvStorageSize - BackUpOffset;\r
195 DEBUG ((DEBUG_INFO, "Variable: High partial NV storage from offset: %x is backed up in spare block: 0x%x\n", BackUpOffset, (UINTN) FtwLastWriteData->SpareAddress));\r
196 //\r
197 // Copy the partial backed up NV storage data to the memory buffer from spare block.\r
198 //\r
199 CopyMem (NvStorageData + BackUpOffset, (UINT8 *) (UINTN) FtwLastWriteData->SpareAddress, BackUpSize);\r
200 }\r
201 }\r
202 }\r
203\r
204 FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *) NvStorageData;\r
205\r
206 //\r
207 // Check if the Firmware Volume is not corrupted\r
208 //\r
209 if ((FvHeader->Signature != EFI_FVH_SIGNATURE) || (!CompareGuid (&gEfiSystemNvDataFvGuid, &FvHeader->FileSystemGuid))) {\r
210 FreePool (NvStorageData);\r
211 DEBUG ((DEBUG_ERROR, "Firmware Volume for Variable Store is corrupted\n"));\r
212 return EFI_VOLUME_CORRUPTED;\r
213 }\r
214\r
215 VariableStore = (VARIABLE_STORE_HEADER *) ((UINTN) FvHeader + FvHeader->HeaderLength);\r
216 VariableStoreLength = NvStorageSize - FvHeader->HeaderLength;\r
217 ASSERT (sizeof (VARIABLE_STORE_HEADER) <= VariableStoreLength);\r
218 ASSERT (VariableStore->Size == VariableStoreLength);\r
219\r
220 //\r
221 // Check if the Variable Store header is not corrupted\r
222 //\r
223 if (GetVariableStoreStatus (VariableStore) != EfiValid) {\r
224 FreePool (NvStorageData);\r
225 DEBUG((DEBUG_ERROR, "Variable Store header is corrupted\n"));\r
226 return EFI_VOLUME_CORRUPTED;\r
227 }\r
228\r
229 mNvFvHeaderCache = FvHeader;\r
230\r
231 *VariableStoreBase = (EFI_PHYSICAL_ADDRESS) (UINTN) VariableStore;\r
232\r
233 HwErrStorageSize = PcdGet32 (PcdHwErrStorageSize);\r
234 MaxUserNvVariableSpaceSize = PcdGet32 (PcdMaxUserNvVariableSpaceSize);\r
235 BoottimeReservedNvVariableSpaceSize = PcdGet32 (PcdBoottimeReservedNvVariableSpaceSize);\r
236\r
237 //\r
238 // Note that in EdkII variable driver implementation, Hardware Error Record type variable\r
239 // is stored with common variable in the same NV region. So the platform integrator should\r
240 // ensure that the value of PcdHwErrStorageSize is less than the value of\r
241 // (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER)).\r
242 //\r
243 ASSERT (HwErrStorageSize < (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER)));\r
244 //\r
245 // Ensure that the value of PcdMaxUserNvVariableSpaceSize is less than the value of\r
246 // (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER)) - PcdGet32 (PcdHwErrStorageSize).\r
247 //\r
248 ASSERT (MaxUserNvVariableSpaceSize < (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER) - HwErrStorageSize));\r
249 //\r
250 // Ensure that the value of PcdBoottimeReservedNvVariableSpaceSize is less than the value of\r
251 // (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER)) - PcdGet32 (PcdHwErrStorageSize).\r
252 //\r
253 ASSERT (BoottimeReservedNvVariableSpaceSize < (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER) - HwErrStorageSize));\r
254\r
255 mVariableModuleGlobal->CommonVariableSpace = ((UINTN) VariableStoreLength - sizeof (VARIABLE_STORE_HEADER) - HwErrStorageSize);\r
256 mVariableModuleGlobal->CommonMaxUserVariableSpace = ((MaxUserNvVariableSpaceSize != 0) ? MaxUserNvVariableSpaceSize : mVariableModuleGlobal->CommonVariableSpace);\r
257 mVariableModuleGlobal->CommonRuntimeVariableSpace = mVariableModuleGlobal->CommonVariableSpace - BoottimeReservedNvVariableSpaceSize;\r
258\r
259 DEBUG ((\r
260 DEBUG_INFO,\r
261 "Variable driver common space: 0x%x 0x%x 0x%x\n",\r
262 mVariableModuleGlobal->CommonVariableSpace,\r
263 mVariableModuleGlobal->CommonMaxUserVariableSpace,\r
264 mVariableModuleGlobal->CommonRuntimeVariableSpace\r
265 ));\r
266\r
267 //\r
268 // The max NV variable size should be < (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER)).\r
269 //\r
270 ASSERT (GetNonVolatileMaxVariableSize () < (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER)));\r
271\r
272 return EFI_SUCCESS;\r
273}\r
274\r
275/**\r
276 Init non-volatile variable store.\r
277\r
278 @retval EFI_SUCCESS Function successfully executed.\r
279 @retval EFI_OUT_OF_RESOURCES Fail to allocate enough memory resource.\r
280 @retval EFI_VOLUME_CORRUPTED Variable Store or Firmware Volume for Variable Store is corrupted.\r
281\r
282**/\r
283EFI_STATUS\r
284InitNonVolatileVariableStore (\r
285 VOID\r
286 )\r
287{\r
288 VARIABLE_HEADER *Variable;\r
289 VARIABLE_HEADER *NextVariable;\r
290 EFI_PHYSICAL_ADDRESS VariableStoreBase;\r
291 UINTN VariableSize;\r
292 EFI_STATUS Status;\r
293\r
294 if (PcdGetBool (PcdEmuVariableNvModeEnable)) {\r
295 Status = InitEmuNonVolatileVariableStore (&VariableStoreBase);\r
296 if (EFI_ERROR (Status)) {\r
297 return Status;\r
298 }\r
299 mVariableModuleGlobal->VariableGlobal.EmuNvMode = TRUE;\r
300 DEBUG ((DEBUG_INFO, "Variable driver will work at emulated non-volatile variable mode!\n"));\r
301 } else {\r
302 Status = InitRealNonVolatileVariableStore (&VariableStoreBase);\r
303 if (EFI_ERROR (Status)) {\r
304 return Status;\r
305 }\r
306 mVariableModuleGlobal->VariableGlobal.EmuNvMode = FALSE;\r
307 }\r
308\r
309 mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase = VariableStoreBase;\r
310 mNvVariableCache = (VARIABLE_STORE_HEADER *) (UINTN) VariableStoreBase;\r
311 mVariableModuleGlobal->VariableGlobal.AuthFormat = (BOOLEAN)(CompareGuid (&mNvVariableCache->Signature, &gEfiAuthenticatedVariableGuid));\r
312\r
313 mVariableModuleGlobal->MaxVariableSize = PcdGet32 (PcdMaxVariableSize);\r
314 mVariableModuleGlobal->MaxAuthVariableSize = ((PcdGet32 (PcdMaxAuthVariableSize) != 0) ? PcdGet32 (PcdMaxAuthVariableSize) : mVariableModuleGlobal->MaxVariableSize);\r
315\r
316 //\r
317 // Parse non-volatile variable data and get last variable offset.\r
318 //\r
319 Variable = GetStartPointer (mNvVariableCache);\r
320 while (IsValidVariableHeader (Variable, GetEndPointer (mNvVariableCache))) {\r
321 NextVariable = GetNextVariablePtr (Variable, mVariableModuleGlobal->VariableGlobal.AuthFormat);\r
322 VariableSize = (UINTN) NextVariable - (UINTN) Variable;\r
323 if ((Variable->Attributes & (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_HARDWARE_ERROR_RECORD)) == (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_HARDWARE_ERROR_RECORD)) {\r
324 mVariableModuleGlobal->HwErrVariableTotalSize += VariableSize;\r
325 } else {\r
326 mVariableModuleGlobal->CommonVariableTotalSize += VariableSize;\r
327 }\r
328\r
329 Variable = NextVariable;\r
330 }\r
331 mVariableModuleGlobal->NonVolatileLastVariableOffset = (UINTN) Variable - (UINTN) mNvVariableCache;\r
332\r
333 return EFI_SUCCESS;\r
334}\r