]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/FaultTolerantWriteDxe/UpdateWorkingBlock.c
MdeModulePkg: Variable drivers robustly handle crashes during Reclaim().
[mirror_edk2.git] / MdeModulePkg / Universal / FaultTolerantWriteDxe / UpdateWorkingBlock.c
1 /** @file
2
3 Internal functions to operate Working Block Space.
4
5 Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.<BR>
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
10
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13
14 **/
15
16
17 #include "FaultTolerantWrite.h"
18
19 EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER mWorkingBlockHeader = {ZERO_GUID, 0, 0, 0, 0, {0, 0, 0}, 0};
20
21 /**
22 Initialize a local work space header.
23
24 Since Signature and WriteQueueSize have been known, Crc can be calculated out,
25 then the work space header will be fixed.
26 **/
27 VOID
28 InitializeLocalWorkSpaceHeader (
29 VOID
30 )
31 {
32 EFI_STATUS Status;
33
34 //
35 // Check signature with gEdkiiWorkingBlockSignatureGuid.
36 //
37 if (CompareGuid (&gEdkiiWorkingBlockSignatureGuid, &mWorkingBlockHeader.Signature)) {
38 //
39 // The local work space header has been initialized.
40 //
41 return;
42 }
43
44 SetMem (
45 &mWorkingBlockHeader,
46 sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),
47 FTW_ERASED_BYTE
48 );
49
50 //
51 // Here using gEdkiiWorkingBlockSignatureGuid as the signature.
52 //
53 CopyMem (
54 &mWorkingBlockHeader.Signature,
55 &gEdkiiWorkingBlockSignatureGuid,
56 sizeof (EFI_GUID)
57 );
58 mWorkingBlockHeader.WriteQueueSize = (UINT64) (PcdGet32 (PcdFlashNvStorageFtwWorkingSize) - sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER));
59
60 //
61 // Crc is calculated with all the fields except Crc and STATE, so leave them as FTW_ERASED_BYTE.
62 //
63
64 //
65 // Calculate the Crc of woking block header
66 //
67 Status = gBS->CalculateCrc32 (
68 &mWorkingBlockHeader,
69 sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),
70 &mWorkingBlockHeader.Crc
71 );
72 ASSERT_EFI_ERROR (Status);
73
74 mWorkingBlockHeader.WorkingBlockValid = FTW_VALID_STATE;
75 mWorkingBlockHeader.WorkingBlockInvalid = FTW_INVALID_STATE;
76 }
77
78 /**
79 Check to see if it is a valid work space.
80
81
82 @param WorkingHeader Pointer of working block header
83
84 @retval TRUE The work space is valid.
85 @retval FALSE The work space is invalid.
86
87 **/
88 BOOLEAN
89 IsValidWorkSpace (
90 IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingHeader
91 )
92 {
93 if (WorkingHeader == NULL) {
94 return FALSE;
95 }
96
97 if (CompareMem (WorkingHeader, &mWorkingBlockHeader, sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER)) == 0) {
98 return TRUE;
99 }
100
101 DEBUG ((EFI_D_ERROR, "Ftw: Work block header check error\n"));
102 return FALSE;
103 }
104
105 /**
106 Initialize a work space when there is no work space.
107
108 @param WorkingHeader Pointer of working block header
109
110 @retval EFI_SUCCESS The function completed successfully
111 @retval EFI_ABORTED The function could not complete successfully.
112
113 **/
114 EFI_STATUS
115 InitWorkSpaceHeader (
116 IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingHeader
117 )
118 {
119 if (WorkingHeader == NULL) {
120 return EFI_INVALID_PARAMETER;
121 }
122
123 CopyMem (WorkingHeader, &mWorkingBlockHeader, sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER));
124
125 return EFI_SUCCESS;
126 }
127
128 /**
129 Read from working block to refresh the work space in memory.
130
131 @param FtwDevice Point to private data of FTW driver
132
133 @retval EFI_SUCCESS The function completed successfully
134 @retval EFI_ABORTED The function could not complete successfully.
135
136 **/
137 EFI_STATUS
138 WorkSpaceRefresh (
139 IN EFI_FTW_DEVICE *FtwDevice
140 )
141 {
142 EFI_STATUS Status;
143 UINTN Length;
144 UINTN RemainingSpaceSize;
145
146 //
147 // Initialize WorkSpace as FTW_ERASED_BYTE
148 //
149 SetMem (
150 FtwDevice->FtwWorkSpace,
151 FtwDevice->FtwWorkSpaceSize,
152 FTW_ERASED_BYTE
153 );
154
155 //
156 // Read from working block
157 //
158 Length = FtwDevice->FtwWorkSpaceSize;
159 Status = FtwDevice->FtwFvBlock->Read (
160 FtwDevice->FtwFvBlock,
161 FtwDevice->FtwWorkSpaceLba,
162 FtwDevice->FtwWorkSpaceBase,
163 &Length,
164 FtwDevice->FtwWorkSpace
165 );
166 if (EFI_ERROR (Status)) {
167 return EFI_ABORTED;
168 }
169 //
170 // Refresh the FtwLastWriteHeader
171 //
172 Status = FtwGetLastWriteHeader (
173 FtwDevice->FtwWorkSpaceHeader,
174 FtwDevice->FtwWorkSpaceSize,
175 &FtwDevice->FtwLastWriteHeader
176 );
177 RemainingSpaceSize = FtwDevice->FtwWorkSpaceSize - ((UINTN) FtwDevice->FtwLastWriteHeader - (UINTN) FtwDevice->FtwWorkSpace);
178 DEBUG ((EFI_D_INFO, "Ftw: Remaining work space size - %x\n", RemainingSpaceSize));
179 //
180 // If FtwGetLastWriteHeader() returns error, or the remaining space size is even not enough to contain
181 // one EFI_FAULT_TOLERANT_WRITE_HEADER + one EFI_FAULT_TOLERANT_WRITE_RECORD(It will cause that the header
182 // pointed by FtwDevice->FtwLastWriteHeader or record pointed by FtwDevice->FtwLastWriteRecord may contain invalid data),
183 // it needs to reclaim work space.
184 //
185 if (EFI_ERROR (Status) || RemainingSpaceSize < sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER) + sizeof (EFI_FAULT_TOLERANT_WRITE_RECORD)) {
186 //
187 // reclaim work space in working block.
188 //
189 Status = FtwReclaimWorkSpace (FtwDevice, TRUE);
190 if (EFI_ERROR (Status)) {
191 DEBUG ((EFI_D_ERROR, "Ftw: Reclaim workspace - %r\n", Status));
192 return EFI_ABORTED;
193 }
194 //
195 // Read from working block again
196 //
197 Length = FtwDevice->FtwWorkSpaceSize;
198 Status = FtwDevice->FtwFvBlock->Read (
199 FtwDevice->FtwFvBlock,
200 FtwDevice->FtwWorkSpaceLba,
201 FtwDevice->FtwWorkSpaceBase,
202 &Length,
203 FtwDevice->FtwWorkSpace
204 );
205 if (EFI_ERROR (Status)) {
206 return EFI_ABORTED;
207 }
208
209 Status = FtwGetLastWriteHeader (
210 FtwDevice->FtwWorkSpaceHeader,
211 FtwDevice->FtwWorkSpaceSize,
212 &FtwDevice->FtwLastWriteHeader
213 );
214 if (EFI_ERROR (Status)) {
215 return EFI_ABORTED;
216 }
217 }
218 //
219 // Refresh the FtwLastWriteRecord
220 //
221 Status = FtwGetLastWriteRecord (
222 FtwDevice->FtwLastWriteHeader,
223 &FtwDevice->FtwLastWriteRecord
224 );
225 if (EFI_ERROR (Status)) {
226 return EFI_ABORTED;
227 }
228
229 return EFI_SUCCESS;
230 }
231
232 /**
233 Reclaim the work space on the working block.
234
235 @param FtwDevice Point to private data of FTW driver
236 @param PreserveRecord Whether to preserve the working record is needed
237
238 @retval EFI_SUCCESS The function completed successfully
239 @retval EFI_OUT_OF_RESOURCES Allocate memory error
240 @retval EFI_ABORTED The function could not complete successfully
241
242 **/
243 EFI_STATUS
244 FtwReclaimWorkSpace (
245 IN EFI_FTW_DEVICE *FtwDevice,
246 IN BOOLEAN PreserveRecord
247 )
248 {
249 EFI_STATUS Status;
250 UINTN Length;
251 EFI_FAULT_TOLERANT_WRITE_HEADER *Header;
252 UINT8 *TempBuffer;
253 UINTN TempBufferSize;
254 UINTN SpareBufferSize;
255 UINT8 *SpareBuffer;
256 EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingBlockHeader;
257 UINTN Index;
258 UINT8 *Ptr;
259 EFI_LBA WorkSpaceLbaOffset;
260
261 DEBUG ((EFI_D_ERROR, "Ftw: start to reclaim work space\n"));
262
263 WorkSpaceLbaOffset = FtwDevice->FtwWorkSpaceLba - FtwDevice->FtwWorkBlockLba;
264
265 //
266 // Read all original data from working block to a memory buffer
267 //
268 TempBufferSize = FtwDevice->SpareAreaLength;
269 TempBuffer = AllocateZeroPool (TempBufferSize);
270 if (TempBuffer == NULL) {
271 return EFI_OUT_OF_RESOURCES;
272 }
273
274 Ptr = TempBuffer;
275 for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
276 Length = FtwDevice->BlockSize;
277 Status = FtwDevice->FtwFvBlock->Read (
278 FtwDevice->FtwFvBlock,
279 FtwDevice->FtwWorkBlockLba + Index,
280 0,
281 &Length,
282 Ptr
283 );
284 if (EFI_ERROR (Status)) {
285 FreePool (TempBuffer);
286 return EFI_ABORTED;
287 }
288
289 Ptr += Length;
290 }
291 //
292 // Clean up the workspace, remove all the completed records.
293 //
294 Ptr = TempBuffer +
295 (UINTN) WorkSpaceLbaOffset * FtwDevice->BlockSize +
296 FtwDevice->FtwWorkSpaceBase;
297
298 //
299 // Clear the content of buffer that will save the new work space data
300 //
301 SetMem (Ptr, FtwDevice->FtwWorkSpaceSize, FTW_ERASED_BYTE);
302
303 //
304 // Copy EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER to buffer
305 //
306 CopyMem (
307 Ptr,
308 FtwDevice->FtwWorkSpaceHeader,
309 sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER)
310 );
311 if (PreserveRecord) {
312 //
313 // Get the last record following the header,
314 //
315 Status = FtwGetLastWriteHeader (
316 FtwDevice->FtwWorkSpaceHeader,
317 FtwDevice->FtwWorkSpaceSize,
318 &FtwDevice->FtwLastWriteHeader
319 );
320 Header = FtwDevice->FtwLastWriteHeader;
321 if (!EFI_ERROR (Status) && (Header != NULL) && (Header->Complete != FTW_VALID_STATE) && (Header->HeaderAllocated == FTW_VALID_STATE)) {
322 CopyMem (
323 Ptr + sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),
324 FtwDevice->FtwLastWriteHeader,
325 FTW_WRITE_TOTAL_SIZE (Header->NumberOfWrites, Header->PrivateDataSize)
326 );
327 }
328 }
329
330 CopyMem (
331 FtwDevice->FtwWorkSpace,
332 Ptr,
333 FtwDevice->FtwWorkSpaceSize
334 );
335
336 FtwGetLastWriteHeader (
337 FtwDevice->FtwWorkSpaceHeader,
338 FtwDevice->FtwWorkSpaceSize,
339 &FtwDevice->FtwLastWriteHeader
340 );
341
342 FtwGetLastWriteRecord (
343 FtwDevice->FtwLastWriteHeader,
344 &FtwDevice->FtwLastWriteRecord
345 );
346
347 //
348 // Set the WorkingBlockValid and WorkingBlockInvalid as INVALID
349 //
350 WorkingBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) (TempBuffer +
351 (UINTN) WorkSpaceLbaOffset * FtwDevice->BlockSize +
352 FtwDevice->FtwWorkSpaceBase);
353 WorkingBlockHeader->WorkingBlockValid = FTW_INVALID_STATE;
354 WorkingBlockHeader->WorkingBlockInvalid = FTW_INVALID_STATE;
355
356 //
357 // Try to keep the content of spare block
358 // Save spare block into a spare backup memory buffer (Sparebuffer)
359 //
360 SpareBufferSize = FtwDevice->SpareAreaLength;
361 SpareBuffer = AllocatePool (SpareBufferSize);
362 if (SpareBuffer == NULL) {
363 FreePool (TempBuffer);
364 return EFI_OUT_OF_RESOURCES;
365 }
366
367 Ptr = SpareBuffer;
368 for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
369 Length = FtwDevice->BlockSize;
370 Status = FtwDevice->FtwBackupFvb->Read (
371 FtwDevice->FtwBackupFvb,
372 FtwDevice->FtwSpareLba + Index,
373 0,
374 &Length,
375 Ptr
376 );
377 if (EFI_ERROR (Status)) {
378 FreePool (TempBuffer);
379 FreePool (SpareBuffer);
380 return EFI_ABORTED;
381 }
382
383 Ptr += Length;
384 }
385 //
386 // Write the memory buffer to spare block
387 //
388 Status = FtwEraseSpareBlock (FtwDevice);
389 Ptr = TempBuffer;
390 for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
391 Length = FtwDevice->BlockSize;
392 Status = FtwDevice->FtwBackupFvb->Write (
393 FtwDevice->FtwBackupFvb,
394 FtwDevice->FtwSpareLba + Index,
395 0,
396 &Length,
397 Ptr
398 );
399 if (EFI_ERROR (Status)) {
400 FreePool (TempBuffer);
401 FreePool (SpareBuffer);
402 return EFI_ABORTED;
403 }
404
405 Ptr += Length;
406 }
407 //
408 // Free TempBuffer
409 //
410 FreePool (TempBuffer);
411
412 //
413 // Set the WorkingBlockValid in spare block
414 //
415 Status = FtwUpdateFvState (
416 FtwDevice->FtwBackupFvb,
417 FtwDevice->FtwSpareLba + WorkSpaceLbaOffset,
418 FtwDevice->FtwWorkSpaceBase + sizeof (EFI_GUID) + sizeof (UINT32),
419 WORKING_BLOCK_VALID
420 );
421 if (EFI_ERROR (Status)) {
422 FreePool (SpareBuffer);
423 return EFI_ABORTED;
424 }
425 //
426 // Before erase the working block, set WorkingBlockInvalid in working block.
427 //
428 // Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER,
429 // WorkingBlockInvalid);
430 //
431 Status = FtwUpdateFvState (
432 FtwDevice->FtwFvBlock,
433 FtwDevice->FtwWorkSpaceLba,
434 FtwDevice->FtwWorkSpaceBase + sizeof (EFI_GUID) + sizeof (UINT32),
435 WORKING_BLOCK_INVALID
436 );
437 if (EFI_ERROR (Status)) {
438 FreePool (SpareBuffer);
439 return EFI_ABORTED;
440 }
441
442 FtwDevice->FtwWorkSpaceHeader->WorkingBlockInvalid = FTW_VALID_STATE;
443
444 //
445 // Write the spare block to working block
446 //
447 Status = FlushSpareBlockToWorkingBlock (FtwDevice);
448 if (EFI_ERROR (Status)) {
449 FreePool (SpareBuffer);
450 return Status;
451 }
452 //
453 // Restore spare backup buffer into spare block , if no failure happened during FtwWrite.
454 //
455 Status = FtwEraseSpareBlock (FtwDevice);
456 Ptr = SpareBuffer;
457 for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
458 Length = FtwDevice->BlockSize;
459 Status = FtwDevice->FtwBackupFvb->Write (
460 FtwDevice->FtwBackupFvb,
461 FtwDevice->FtwSpareLba + Index,
462 0,
463 &Length,
464 Ptr
465 );
466 if (EFI_ERROR (Status)) {
467 FreePool (SpareBuffer);
468 return EFI_ABORTED;
469 }
470
471 Ptr += Length;
472 }
473
474 FreePool (SpareBuffer);
475
476 DEBUG ((EFI_D_ERROR, "Ftw: reclaim work space successfully\n"));
477
478 return EFI_SUCCESS;
479 }