]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/Variable/RuntimeDxe/VariableNonVolatile.c
MdeModulePkg/Variable: Attribute combination should return EFI_UNSUPPORTED
[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 UINT64 NvStorageSize64;
146 FAULT_TOLERANT_WRITE_LAST_WRITE_DATA *FtwLastWriteData;
147 UINT32 BackUpOffset;
148 UINT32 BackUpSize;
149 UINT32 HwErrStorageSize;
150 UINT32 MaxUserNvVariableSpaceSize;
151 UINT32 BoottimeReservedNvVariableSpaceSize;
152 EFI_STATUS Status;
153 VOID *FtwProtocol;
154
155 mVariableModuleGlobal->FvbInstance = NULL;
156
157 Status = GetVariableFlashNvStorageInfo (&NvStorageBase, &NvStorageSize64);
158 ASSERT_EFI_ERROR (Status);
159
160 Status = SafeUint64ToUint32 (NvStorageSize64, &NvStorageSize);
161 // This driver currently assumes the size will be UINT32 so assert the value is safe for now.
162 ASSERT_EFI_ERROR (Status);
163
164 ASSERT (NvStorageBase != 0);
165
166 //
167 // Allocate runtime memory used for a memory copy of the FLASH region.
168 // Keep the memory and the FLASH in sync as updates occur.
169 //
170 NvStorageData = AllocateRuntimeZeroPool (NvStorageSize);
171 if (NvStorageData == NULL) {
172 return EFI_OUT_OF_RESOURCES;
173 }
174
175 //
176 // Copy NV storage data to the memory buffer.
177 //
178 CopyMem (NvStorageData, (UINT8 *)(UINTN)NvStorageBase, NvStorageSize);
179
180 Status = GetFtwProtocol ((VOID **)&FtwProtocol);
181 //
182 // If FTW protocol has been installed, no need to check FTW last write data hob.
183 //
184 if (EFI_ERROR (Status)) {
185 //
186 // Check the FTW last write data hob.
187 //
188 GuidHob = GetFirstGuidHob (&gEdkiiFaultTolerantWriteGuid);
189 if (GuidHob != NULL) {
190 FtwLastWriteData = (FAULT_TOLERANT_WRITE_LAST_WRITE_DATA *)GET_GUID_HOB_DATA (GuidHob);
191 if (FtwLastWriteData->TargetAddress == NvStorageBase) {
192 DEBUG ((DEBUG_INFO, "Variable: NV storage is backed up in spare block: 0x%x\n", (UINTN)FtwLastWriteData->SpareAddress));
193 //
194 // Copy the backed up NV storage data to the memory buffer from spare block.
195 //
196 CopyMem (NvStorageData, (UINT8 *)(UINTN)(FtwLastWriteData->SpareAddress), NvStorageSize);
197 } else if ((FtwLastWriteData->TargetAddress > NvStorageBase) &&
198 (FtwLastWriteData->TargetAddress < (NvStorageBase + NvStorageSize)))
199 {
200 //
201 // Flash NV storage from the Offset is backed up in spare block.
202 //
203 BackUpOffset = (UINT32)(FtwLastWriteData->TargetAddress - NvStorageBase);
204 BackUpSize = NvStorageSize - BackUpOffset;
205 DEBUG ((DEBUG_INFO, "Variable: High partial NV storage from offset: %x is backed up in spare block: 0x%x\n", BackUpOffset, (UINTN)FtwLastWriteData->SpareAddress));
206 //
207 // Copy the partial backed up NV storage data to the memory buffer from spare block.
208 //
209 CopyMem (NvStorageData + BackUpOffset, (UINT8 *)(UINTN)FtwLastWriteData->SpareAddress, BackUpSize);
210 }
211 }
212 }
213
214 FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *)NvStorageData;
215
216 //
217 // Check if the Firmware Volume is not corrupted
218 //
219 if ((FvHeader->Signature != EFI_FVH_SIGNATURE) || (!CompareGuid (&gEfiSystemNvDataFvGuid, &FvHeader->FileSystemGuid))) {
220 FreePool (NvStorageData);
221 DEBUG ((DEBUG_ERROR, "Firmware Volume for Variable Store is corrupted\n"));
222 return EFI_VOLUME_CORRUPTED;
223 }
224
225 VariableStore = (VARIABLE_STORE_HEADER *)((UINTN)FvHeader + FvHeader->HeaderLength);
226 VariableStoreLength = NvStorageSize - FvHeader->HeaderLength;
227 ASSERT (sizeof (VARIABLE_STORE_HEADER) <= VariableStoreLength);
228 ASSERT (VariableStore->Size == VariableStoreLength);
229
230 //
231 // Check if the Variable Store header is not corrupted
232 //
233 if (GetVariableStoreStatus (VariableStore) != EfiValid) {
234 FreePool (NvStorageData);
235 DEBUG ((DEBUG_ERROR, "Variable Store header is corrupted\n"));
236 return EFI_VOLUME_CORRUPTED;
237 }
238
239 mNvFvHeaderCache = FvHeader;
240
241 *VariableStoreBase = (EFI_PHYSICAL_ADDRESS)(UINTN)VariableStore;
242
243 HwErrStorageSize = PcdGet32 (PcdHwErrStorageSize);
244 MaxUserNvVariableSpaceSize = PcdGet32 (PcdMaxUserNvVariableSpaceSize);
245 BoottimeReservedNvVariableSpaceSize = PcdGet32 (PcdBoottimeReservedNvVariableSpaceSize);
246
247 //
248 // Note that in EdkII variable driver implementation, Hardware Error Record type variable
249 // is stored with common variable in the same NV region. So the platform integrator should
250 // ensure that the value of PcdHwErrStorageSize is less than the value of
251 // (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER)).
252 //
253 ASSERT (HwErrStorageSize < (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER)));
254 //
255 // Ensure that the value of PcdMaxUserNvVariableSpaceSize is less than the value of
256 // (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER)) - PcdGet32 (PcdHwErrStorageSize).
257 //
258 ASSERT (MaxUserNvVariableSpaceSize < (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER) - HwErrStorageSize));
259 //
260 // Ensure that the value of PcdBoottimeReservedNvVariableSpaceSize is less than the value of
261 // (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER)) - PcdGet32 (PcdHwErrStorageSize).
262 //
263 ASSERT (BoottimeReservedNvVariableSpaceSize < (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER) - HwErrStorageSize));
264
265 mVariableModuleGlobal->CommonVariableSpace = ((UINTN)VariableStoreLength - sizeof (VARIABLE_STORE_HEADER) - HwErrStorageSize);
266 mVariableModuleGlobal->CommonMaxUserVariableSpace = ((MaxUserNvVariableSpaceSize != 0) ? MaxUserNvVariableSpaceSize : mVariableModuleGlobal->CommonVariableSpace);
267 mVariableModuleGlobal->CommonRuntimeVariableSpace = mVariableModuleGlobal->CommonVariableSpace - BoottimeReservedNvVariableSpaceSize;
268
269 DEBUG ((
270 DEBUG_INFO,
271 "Variable driver common space: 0x%x 0x%x 0x%x\n",
272 mVariableModuleGlobal->CommonVariableSpace,
273 mVariableModuleGlobal->CommonMaxUserVariableSpace,
274 mVariableModuleGlobal->CommonRuntimeVariableSpace
275 ));
276
277 //
278 // The max NV variable size should be < (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER)).
279 //
280 ASSERT (GetNonVolatileMaxVariableSize () < (VariableStoreLength - sizeof (VARIABLE_STORE_HEADER)));
281
282 return EFI_SUCCESS;
283 }
284
285 /**
286 Init non-volatile variable store.
287
288 @retval EFI_SUCCESS Function successfully executed.
289 @retval EFI_OUT_OF_RESOURCES Fail to allocate enough memory resource.
290 @retval EFI_VOLUME_CORRUPTED Variable Store or Firmware Volume for Variable Store is corrupted.
291
292 **/
293 EFI_STATUS
294 InitNonVolatileVariableStore (
295 VOID
296 )
297 {
298 VARIABLE_HEADER *Variable;
299 VARIABLE_HEADER *NextVariable;
300 EFI_PHYSICAL_ADDRESS VariableStoreBase;
301 UINTN VariableSize;
302 EFI_STATUS Status;
303
304 if (PcdGetBool (PcdEmuVariableNvModeEnable)) {
305 Status = InitEmuNonVolatileVariableStore (&VariableStoreBase);
306 if (EFI_ERROR (Status)) {
307 return Status;
308 }
309
310 mVariableModuleGlobal->VariableGlobal.EmuNvMode = TRUE;
311 DEBUG ((DEBUG_INFO, "Variable driver will work at emulated non-volatile variable mode!\n"));
312 } else {
313 Status = InitRealNonVolatileVariableStore (&VariableStoreBase);
314 if (EFI_ERROR (Status)) {
315 return Status;
316 }
317
318 mVariableModuleGlobal->VariableGlobal.EmuNvMode = FALSE;
319 }
320
321 mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase = VariableStoreBase;
322 mNvVariableCache = (VARIABLE_STORE_HEADER *)(UINTN)VariableStoreBase;
323 mVariableModuleGlobal->VariableGlobal.AuthFormat = (BOOLEAN)(CompareGuid (&mNvVariableCache->Signature, &gEfiAuthenticatedVariableGuid));
324
325 mVariableModuleGlobal->MaxVariableSize = PcdGet32 (PcdMaxVariableSize);
326 mVariableModuleGlobal->MaxAuthVariableSize = ((PcdGet32 (PcdMaxAuthVariableSize) != 0) ? PcdGet32 (PcdMaxAuthVariableSize) : mVariableModuleGlobal->MaxVariableSize);
327
328 //
329 // Parse non-volatile variable data and get last variable offset.
330 //
331 Variable = GetStartPointer (mNvVariableCache);
332 while (IsValidVariableHeader (Variable, GetEndPointer (mNvVariableCache))) {
333 NextVariable = GetNextVariablePtr (Variable, mVariableModuleGlobal->VariableGlobal.AuthFormat);
334 VariableSize = (UINTN)NextVariable - (UINTN)Variable;
335 if ((Variable->Attributes & (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_HARDWARE_ERROR_RECORD)) == (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_HARDWARE_ERROR_RECORD)) {
336 mVariableModuleGlobal->HwErrVariableTotalSize += VariableSize;
337 } else {
338 mVariableModuleGlobal->CommonVariableTotalSize += VariableSize;
339 }
340
341 Variable = NextVariable;
342 }
343
344 mVariableModuleGlobal->NonVolatileLastVariableOffset = (UINTN)Variable - (UINTN)mNvVariableCache;
345
346 return EFI_SUCCESS;
347 }