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