]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/FaultTolerantWriteDxe/UpdateWorkingBlock.c
MdeModulePkg: Apply uncrustify changes
[mirror_edk2.git] / MdeModulePkg / Universal / FaultTolerantWriteDxe / UpdateWorkingBlock.c
1 /** @file
2
3 Internal functions to operate Working Block Space.
4
5 Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7
8 **/
9
10 #include "FaultTolerantWrite.h"
11
12 EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER mWorkingBlockHeader = { ZERO_GUID, 0, 0, 0, 0, { 0, 0, 0 }, 0 };
13
14 /**
15 Initialize a local work space header.
16
17 Since Signature and WriteQueueSize have been known, Crc can be calculated out,
18 then the work space header will be fixed.
19 **/
20 VOID
21 InitializeLocalWorkSpaceHeader (
22 VOID
23 )
24 {
25 //
26 // Check signature with gEdkiiWorkingBlockSignatureGuid.
27 //
28 if (CompareGuid (&gEdkiiWorkingBlockSignatureGuid, &mWorkingBlockHeader.Signature)) {
29 //
30 // The local work space header has been initialized.
31 //
32 return;
33 }
34
35 SetMem (
36 &mWorkingBlockHeader,
37 sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),
38 FTW_ERASED_BYTE
39 );
40
41 //
42 // Here using gEdkiiWorkingBlockSignatureGuid as the signature.
43 //
44 CopyMem (
45 &mWorkingBlockHeader.Signature,
46 &gEdkiiWorkingBlockSignatureGuid,
47 sizeof (EFI_GUID)
48 );
49 mWorkingBlockHeader.WriteQueueSize = PcdGet32 (PcdFlashNvStorageFtwWorkingSize) - sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER);
50
51 //
52 // Crc is calculated with all the fields except Crc and STATE, so leave them as FTW_ERASED_BYTE.
53 //
54
55 //
56 // Calculate the Crc of woking block header
57 //
58 mWorkingBlockHeader.Crc = FtwCalculateCrc32 (
59 &mWorkingBlockHeader,
60 sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER)
61 );
62
63 mWorkingBlockHeader.WorkingBlockValid = FTW_VALID_STATE;
64 mWorkingBlockHeader.WorkingBlockInvalid = FTW_INVALID_STATE;
65 }
66
67 /**
68 Check to see if it is a valid work space.
69
70
71 @param WorkingHeader Pointer of working block header
72
73 @retval TRUE The work space is valid.
74 @retval FALSE The work space is invalid.
75
76 **/
77 BOOLEAN
78 IsValidWorkSpace (
79 IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingHeader
80 )
81 {
82 if (WorkingHeader == NULL) {
83 return FALSE;
84 }
85
86 if (CompareMem (WorkingHeader, &mWorkingBlockHeader, sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER)) == 0) {
87 return TRUE;
88 }
89
90 DEBUG ((DEBUG_INFO, "Ftw: Work block header check mismatch\n"));
91 return FALSE;
92 }
93
94 /**
95 Initialize a work space when there is no work space.
96
97 @param WorkingHeader Pointer of working block header
98
99 @retval EFI_SUCCESS The function completed successfully
100 @retval EFI_ABORTED The function could not complete successfully.
101
102 **/
103 EFI_STATUS
104 InitWorkSpaceHeader (
105 IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingHeader
106 )
107 {
108 if (WorkingHeader == NULL) {
109 return EFI_INVALID_PARAMETER;
110 }
111
112 CopyMem (WorkingHeader, &mWorkingBlockHeader, sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER));
113
114 return EFI_SUCCESS;
115 }
116
117 /**
118 Read work space data from work block or spare block.
119
120 @param FvBlock FVB Protocol interface to access the block.
121 @param BlockSize The size of the block.
122 @param Lba Lba of the block.
123 @param Offset The offset within the block.
124 @param Length The number of bytes to read from the block.
125 @param Buffer The data is read.
126
127 @retval EFI_SUCCESS The function completed successfully.
128 @retval EFI_ABORTED The function could not complete successfully.
129
130 **/
131 EFI_STATUS
132 ReadWorkSpaceData (
133 IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,
134 IN UINTN BlockSize,
135 IN EFI_LBA Lba,
136 IN UINTN Offset,
137 IN UINTN Length,
138 OUT UINT8 *Buffer
139 )
140 {
141 EFI_STATUS Status;
142 UINT8 *Ptr;
143 UINTN MyLength;
144
145 //
146 // Calculate the real Offset and Lba to write.
147 //
148 while (Offset >= BlockSize) {
149 Offset -= BlockSize;
150 Lba++;
151 }
152
153 Ptr = Buffer;
154 while (Length > 0) {
155 if ((Offset + Length) > BlockSize) {
156 MyLength = BlockSize - Offset;
157 } else {
158 MyLength = Length;
159 }
160
161 Status = FvBlock->Read (
162 FvBlock,
163 Lba,
164 Offset,
165 &MyLength,
166 Ptr
167 );
168 if (EFI_ERROR (Status)) {
169 return EFI_ABORTED;
170 }
171
172 Offset = 0;
173 Length -= MyLength;
174 Ptr += MyLength;
175 Lba++;
176 }
177
178 return EFI_SUCCESS;
179 }
180
181 /**
182 Write work space data to work block.
183
184 @param FvBlock FVB Protocol interface to access the block.
185 @param BlockSize The size of the block.
186 @param Lba Lba of the block.
187 @param Offset The offset within the block to place the data.
188 @param Length The number of bytes to write to the block.
189 @param Buffer The data to write.
190
191 @retval EFI_SUCCESS The function completed successfully.
192 @retval EFI_ABORTED The function could not complete successfully.
193
194 **/
195 EFI_STATUS
196 WriteWorkSpaceData (
197 IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,
198 IN UINTN BlockSize,
199 IN EFI_LBA Lba,
200 IN UINTN Offset,
201 IN UINTN Length,
202 IN UINT8 *Buffer
203 )
204 {
205 EFI_STATUS Status;
206 UINT8 *Ptr;
207 UINTN MyLength;
208
209 //
210 // Calculate the real Offset and Lba to write.
211 //
212 while (Offset >= BlockSize) {
213 Offset -= BlockSize;
214 Lba++;
215 }
216
217 Ptr = Buffer;
218 while (Length > 0) {
219 if ((Offset + Length) > BlockSize) {
220 MyLength = BlockSize - Offset;
221 } else {
222 MyLength = Length;
223 }
224
225 Status = FvBlock->Write (
226 FvBlock,
227 Lba,
228 Offset,
229 &MyLength,
230 Ptr
231 );
232 if (EFI_ERROR (Status)) {
233 return EFI_ABORTED;
234 }
235
236 Offset = 0;
237 Length -= MyLength;
238 Ptr += MyLength;
239 Lba++;
240 }
241
242 return EFI_SUCCESS;
243 }
244
245 /**
246 Read from working block to refresh the work space in memory.
247
248 @param FtwDevice Point to private data of FTW driver
249
250 @retval EFI_SUCCESS The function completed successfully
251 @retval EFI_ABORTED The function could not complete successfully.
252
253 **/
254 EFI_STATUS
255 WorkSpaceRefresh (
256 IN EFI_FTW_DEVICE *FtwDevice
257 )
258 {
259 EFI_STATUS Status;
260 UINTN RemainingSpaceSize;
261
262 //
263 // Initialize WorkSpace as FTW_ERASED_BYTE
264 //
265 SetMem (
266 FtwDevice->FtwWorkSpace,
267 FtwDevice->FtwWorkSpaceSize,
268 FTW_ERASED_BYTE
269 );
270
271 //
272 // Read from working block
273 //
274 Status = ReadWorkSpaceData (
275 FtwDevice->FtwFvBlock,
276 FtwDevice->WorkBlockSize,
277 FtwDevice->FtwWorkSpaceLba,
278 FtwDevice->FtwWorkSpaceBase,
279 FtwDevice->FtwWorkSpaceSize,
280 FtwDevice->FtwWorkSpace
281 );
282 if (EFI_ERROR (Status)) {
283 return EFI_ABORTED;
284 }
285
286 //
287 // Refresh the FtwLastWriteHeader
288 //
289 Status = FtwGetLastWriteHeader (
290 FtwDevice->FtwWorkSpaceHeader,
291 FtwDevice->FtwWorkSpaceSize,
292 &FtwDevice->FtwLastWriteHeader
293 );
294 RemainingSpaceSize = FtwDevice->FtwWorkSpaceSize - ((UINTN)FtwDevice->FtwLastWriteHeader - (UINTN)FtwDevice->FtwWorkSpace);
295 DEBUG ((DEBUG_INFO, "Ftw: Remaining work space size - %x\n", RemainingSpaceSize));
296 //
297 // If FtwGetLastWriteHeader() returns error, or the remaining space size is even not enough to contain
298 // one EFI_FAULT_TOLERANT_WRITE_HEADER + one EFI_FAULT_TOLERANT_WRITE_RECORD(It will cause that the header
299 // pointed by FtwDevice->FtwLastWriteHeader or record pointed by FtwDevice->FtwLastWriteRecord may contain invalid data),
300 // it needs to reclaim work space.
301 //
302 if (EFI_ERROR (Status) || (RemainingSpaceSize < sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER) + sizeof (EFI_FAULT_TOLERANT_WRITE_RECORD))) {
303 //
304 // reclaim work space in working block.
305 //
306 Status = FtwReclaimWorkSpace (FtwDevice, TRUE);
307 if (EFI_ERROR (Status)) {
308 DEBUG ((DEBUG_ERROR, "Ftw: Reclaim workspace - %r\n", Status));
309 return EFI_ABORTED;
310 }
311
312 //
313 // Read from working block again
314 //
315 Status = ReadWorkSpaceData (
316 FtwDevice->FtwFvBlock,
317 FtwDevice->WorkBlockSize,
318 FtwDevice->FtwWorkSpaceLba,
319 FtwDevice->FtwWorkSpaceBase,
320 FtwDevice->FtwWorkSpaceSize,
321 FtwDevice->FtwWorkSpace
322 );
323 if (EFI_ERROR (Status)) {
324 return EFI_ABORTED;
325 }
326
327 Status = FtwGetLastWriteHeader (
328 FtwDevice->FtwWorkSpaceHeader,
329 FtwDevice->FtwWorkSpaceSize,
330 &FtwDevice->FtwLastWriteHeader
331 );
332 if (EFI_ERROR (Status)) {
333 return EFI_ABORTED;
334 }
335 }
336
337 //
338 // Refresh the FtwLastWriteRecord
339 //
340 Status = FtwGetLastWriteRecord (
341 FtwDevice->FtwLastWriteHeader,
342 &FtwDevice->FtwLastWriteRecord
343 );
344 if (EFI_ERROR (Status)) {
345 return EFI_ABORTED;
346 }
347
348 return EFI_SUCCESS;
349 }
350
351 /**
352 Reclaim the work space on the working block.
353
354 @param FtwDevice Point to private data of FTW driver
355 @param PreserveRecord Whether to preserve the working record is needed
356
357 @retval EFI_SUCCESS The function completed successfully
358 @retval EFI_OUT_OF_RESOURCES Allocate memory error
359 @retval EFI_ABORTED The function could not complete successfully
360
361 **/
362 EFI_STATUS
363 FtwReclaimWorkSpace (
364 IN EFI_FTW_DEVICE *FtwDevice,
365 IN BOOLEAN PreserveRecord
366 )
367 {
368 EFI_STATUS Status;
369 UINTN Length;
370 EFI_FAULT_TOLERANT_WRITE_HEADER *Header;
371 UINT8 *TempBuffer;
372 UINTN TempBufferSize;
373 UINTN SpareBufferSize;
374 UINT8 *SpareBuffer;
375 EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingBlockHeader;
376 UINTN Index;
377 UINT8 *Ptr;
378 EFI_LBA WorkSpaceLbaOffset;
379
380 DEBUG ((DEBUG_INFO, "Ftw: start to reclaim work space\n"));
381
382 WorkSpaceLbaOffset = FtwDevice->FtwWorkSpaceLba - FtwDevice->FtwWorkBlockLba;
383
384 //
385 // Read all original data from working block to a memory buffer
386 //
387 TempBufferSize = FtwDevice->NumberOfWorkBlock * FtwDevice->WorkBlockSize;
388 TempBuffer = AllocateZeroPool (TempBufferSize);
389 if (TempBuffer == NULL) {
390 return EFI_OUT_OF_RESOURCES;
391 }
392
393 Ptr = TempBuffer;
394 for (Index = 0; Index < FtwDevice->NumberOfWorkBlock; Index += 1) {
395 Length = FtwDevice->WorkBlockSize;
396 Status = FtwDevice->FtwFvBlock->Read (
397 FtwDevice->FtwFvBlock,
398 FtwDevice->FtwWorkBlockLba + Index,
399 0,
400 &Length,
401 Ptr
402 );
403 if (EFI_ERROR (Status)) {
404 FreePool (TempBuffer);
405 return EFI_ABORTED;
406 }
407
408 Ptr += Length;
409 }
410
411 //
412 // Clean up the workspace, remove all the completed records.
413 //
414 Ptr = TempBuffer +
415 (UINTN)WorkSpaceLbaOffset * FtwDevice->WorkBlockSize +
416 FtwDevice->FtwWorkSpaceBase;
417
418 //
419 // Clear the content of buffer that will save the new work space data
420 //
421 SetMem (Ptr, FtwDevice->FtwWorkSpaceSize, FTW_ERASED_BYTE);
422
423 //
424 // Copy EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER to buffer
425 //
426 CopyMem (
427 Ptr,
428 FtwDevice->FtwWorkSpaceHeader,
429 sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER)
430 );
431 if (PreserveRecord) {
432 //
433 // Get the last record following the header,
434 //
435 Status = FtwGetLastWriteHeader (
436 FtwDevice->FtwWorkSpaceHeader,
437 FtwDevice->FtwWorkSpaceSize,
438 &FtwDevice->FtwLastWriteHeader
439 );
440 Header = FtwDevice->FtwLastWriteHeader;
441 if (!EFI_ERROR (Status) && (Header != NULL) && (Header->Complete != FTW_VALID_STATE) && (Header->HeaderAllocated == FTW_VALID_STATE)) {
442 CopyMem (
443 Ptr + sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),
444 FtwDevice->FtwLastWriteHeader,
445 FTW_WRITE_TOTAL_SIZE (Header->NumberOfWrites, Header->PrivateDataSize)
446 );
447 }
448 }
449
450 CopyMem (
451 FtwDevice->FtwWorkSpace,
452 Ptr,
453 FtwDevice->FtwWorkSpaceSize
454 );
455
456 FtwGetLastWriteHeader (
457 FtwDevice->FtwWorkSpaceHeader,
458 FtwDevice->FtwWorkSpaceSize,
459 &FtwDevice->FtwLastWriteHeader
460 );
461
462 FtwGetLastWriteRecord (
463 FtwDevice->FtwLastWriteHeader,
464 &FtwDevice->FtwLastWriteRecord
465 );
466
467 //
468 // Set the WorkingBlockValid and WorkingBlockInvalid as INVALID
469 //
470 WorkingBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *)(TempBuffer +
471 (UINTN)WorkSpaceLbaOffset * FtwDevice->WorkBlockSize +
472 FtwDevice->FtwWorkSpaceBase);
473 WorkingBlockHeader->WorkingBlockValid = FTW_INVALID_STATE;
474 WorkingBlockHeader->WorkingBlockInvalid = FTW_INVALID_STATE;
475
476 //
477 // Try to keep the content of spare block
478 // Save spare block into a spare backup memory buffer (Sparebuffer)
479 //
480 SpareBufferSize = FtwDevice->SpareAreaLength;
481 SpareBuffer = AllocatePool (SpareBufferSize);
482 if (SpareBuffer == NULL) {
483 FreePool (TempBuffer);
484 return EFI_OUT_OF_RESOURCES;
485 }
486
487 Ptr = SpareBuffer;
488 for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
489 Length = FtwDevice->SpareBlockSize;
490 Status = FtwDevice->FtwBackupFvb->Read (
491 FtwDevice->FtwBackupFvb,
492 FtwDevice->FtwSpareLba + Index,
493 0,
494 &Length,
495 Ptr
496 );
497 if (EFI_ERROR (Status)) {
498 FreePool (TempBuffer);
499 FreePool (SpareBuffer);
500 return EFI_ABORTED;
501 }
502
503 Ptr += Length;
504 }
505
506 //
507 // Write the memory buffer to spare block
508 //
509 Status = FtwEraseSpareBlock (FtwDevice);
510 if (EFI_ERROR (Status)) {
511 FreePool (TempBuffer);
512 FreePool (SpareBuffer);
513 return EFI_ABORTED;
514 }
515
516 Ptr = TempBuffer;
517 for (Index = 0; TempBufferSize > 0; Index += 1) {
518 if (TempBufferSize > FtwDevice->SpareBlockSize) {
519 Length = FtwDevice->SpareBlockSize;
520 } else {
521 Length = TempBufferSize;
522 }
523
524 Status = FtwDevice->FtwBackupFvb->Write (
525 FtwDevice->FtwBackupFvb,
526 FtwDevice->FtwSpareLba + Index,
527 0,
528 &Length,
529 Ptr
530 );
531 if (EFI_ERROR (Status)) {
532 FreePool (TempBuffer);
533 FreePool (SpareBuffer);
534 return EFI_ABORTED;
535 }
536
537 Ptr += Length;
538 TempBufferSize -= Length;
539 }
540
541 //
542 // Free TempBuffer
543 //
544 FreePool (TempBuffer);
545
546 //
547 // Set the WorkingBlockValid in spare block
548 //
549 Status = FtwUpdateFvState (
550 FtwDevice->FtwBackupFvb,
551 FtwDevice->SpareBlockSize,
552 FtwDevice->FtwSpareLba + FtwDevice->FtwWorkSpaceLbaInSpare,
553 FtwDevice->FtwWorkSpaceBaseInSpare + sizeof (EFI_GUID) + sizeof (UINT32),
554 WORKING_BLOCK_VALID
555 );
556 if (EFI_ERROR (Status)) {
557 FreePool (SpareBuffer);
558 return EFI_ABORTED;
559 }
560
561 //
562 // Before erase the working block, set WorkingBlockInvalid in working block.
563 //
564 // Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER,
565 // WorkingBlockInvalid);
566 //
567 Status = FtwUpdateFvState (
568 FtwDevice->FtwFvBlock,
569 FtwDevice->WorkBlockSize,
570 FtwDevice->FtwWorkSpaceLba,
571 FtwDevice->FtwWorkSpaceBase + sizeof (EFI_GUID) + sizeof (UINT32),
572 WORKING_BLOCK_INVALID
573 );
574 if (EFI_ERROR (Status)) {
575 FreePool (SpareBuffer);
576 return EFI_ABORTED;
577 }
578
579 FtwDevice->FtwWorkSpaceHeader->WorkingBlockInvalid = FTW_VALID_STATE;
580
581 //
582 // Write the spare block to working block
583 //
584 Status = FlushSpareBlockToWorkingBlock (FtwDevice);
585 if (EFI_ERROR (Status)) {
586 FreePool (SpareBuffer);
587 return Status;
588 }
589
590 //
591 // Restore spare backup buffer into spare block , if no failure happened during FtwWrite.
592 //
593 Status = FtwEraseSpareBlock (FtwDevice);
594 if (EFI_ERROR (Status)) {
595 FreePool (SpareBuffer);
596 return EFI_ABORTED;
597 }
598
599 Ptr = SpareBuffer;
600 for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
601 Length = FtwDevice->SpareBlockSize;
602 Status = FtwDevice->FtwBackupFvb->Write (
603 FtwDevice->FtwBackupFvb,
604 FtwDevice->FtwSpareLba + Index,
605 0,
606 &Length,
607 Ptr
608 );
609 if (EFI_ERROR (Status)) {
610 FreePool (SpareBuffer);
611 return EFI_ABORTED;
612 }
613
614 Ptr += Length;
615 }
616
617 FreePool (SpareBuffer);
618
619 DEBUG ((DEBUG_INFO, "Ftw: reclaim work space successfully\n"));
620
621 return EFI_SUCCESS;
622 }