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