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