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