1 /** @file
2
3 Internal functions to operate Working Block Space.
4
5 Copyright (c) 2006 - 2015, 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
InitializeLocalWorkSpaceHeader(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
IsValidWorkSpace(IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER * WorkingHeader)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_INFO, "Ftw: Work block header check mismatch\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
InitWorkSpaceHeader(IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER * WorkingHeader)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 work space data from work block or spare block.
130
131 @param FvBlock FVB Protocol interface to access the block.
132 @param BlockSize The size of the block.
133 @param Lba Lba of the block.
134 @param Offset The offset within the block.
135 @param Length The number of bytes to read from the block.
136 @param Buffer The data is read.
137
138 @retval EFI_SUCCESS The function completed successfully.
139 @retval EFI_ABORTED The function could not complete successfully.
140
141 **/
142 EFI_STATUS
ReadWorkSpaceData(IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL * FvBlock,IN UINTN BlockSize,IN EFI_LBA Lba,IN UINTN Offset,IN UINTN Length,OUT UINT8 * Buffer)143 ReadWorkSpaceData (
144 IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,
145 IN UINTN BlockSize,
146 IN EFI_LBA Lba,
147 IN UINTN Offset,
148 IN UINTN Length,
149 OUT UINT8 *Buffer
150 )
151 {
152 EFI_STATUS Status;
153 UINT8 *Ptr;
154 UINTN MyLength;
155
156 //
157 // Calculate the real Offset and Lba to write.
158 //
159 while (Offset >= BlockSize) {
160 Offset -= BlockSize;
161 Lba++;
162 }
163
164 Ptr = Buffer;
165 while (Length > 0) {
166 if ((Offset + Length) > BlockSize) {
167 MyLength = BlockSize - Offset;
168 } else {
169 MyLength = Length;
170 }
171
172 Status = FvBlock->Read (
173 FvBlock,
174 Lba,
175 Offset,
176 &MyLength,
177 Ptr
178 );
179 if (EFI_ERROR (Status)) {
180 return EFI_ABORTED;
181 }
182 Offset = 0;
183 Length -= MyLength;
184 Ptr += MyLength;
185 Lba++;
186 }
187
188 return EFI_SUCCESS;
189 }
190
191 /**
192 Write work space data to work block.
193
194 @param FvBlock FVB Protocol interface to access the block.
195 @param BlockSize The size of the block.
196 @param Lba Lba of the block.
197 @param Offset The offset within the block to place the data.
198 @param Length The number of bytes to write to the block.
199 @param Buffer The data to write.
200
201 @retval EFI_SUCCESS The function completed successfully.
202 @retval EFI_ABORTED The function could not complete successfully.
203
204 **/
205 EFI_STATUS
WriteWorkSpaceData(IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL * FvBlock,IN UINTN BlockSize,IN EFI_LBA Lba,IN UINTN Offset,IN UINTN Length,IN UINT8 * Buffer)206 WriteWorkSpaceData (
207 IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,
208 IN UINTN BlockSize,
209 IN EFI_LBA Lba,
210 IN UINTN Offset,
211 IN UINTN Length,
212 IN UINT8 *Buffer
213 )
214 {
215 EFI_STATUS Status;
216 UINT8 *Ptr;
217 UINTN MyLength;
218
219 //
220 // Calculate the real Offset and Lba to write.
221 //
222 while (Offset >= BlockSize) {
223 Offset -= BlockSize;
224 Lba++;
225 }
226
227 Ptr = Buffer;
228 while (Length > 0) {
229 if ((Offset + Length) > BlockSize) {
230 MyLength = BlockSize - Offset;
231 } else {
232 MyLength = Length;
233 }
234
235 Status = FvBlock->Write (
236 FvBlock,
237 Lba,
238 Offset,
239 &MyLength,
240 Ptr
241 );
242 if (EFI_ERROR (Status)) {
243 return EFI_ABORTED;
244 }
245 Offset = 0;
246 Length -= MyLength;
247 Ptr += MyLength;
248 Lba++;
249 }
250 return EFI_SUCCESS;
251 }
252
253 /**
254 Read from working block to refresh the work space in memory.
255
256 @param FtwDevice Point to private data of FTW driver
257
258 @retval EFI_SUCCESS The function completed successfully
259 @retval EFI_ABORTED The function could not complete successfully.
260
261 **/
262 EFI_STATUS
WorkSpaceRefresh(IN EFI_FTW_DEVICE * FtwDevice)263 WorkSpaceRefresh (
264 IN EFI_FTW_DEVICE *FtwDevice
265 )
266 {
267 EFI_STATUS Status;
268 UINTN RemainingSpaceSize;
269
270 //
271 // Initialize WorkSpace as FTW_ERASED_BYTE
272 //
273 SetMem (
274 FtwDevice->FtwWorkSpace,
275 FtwDevice->FtwWorkSpaceSize,
276 FTW_ERASED_BYTE
277 );
278
279 //
280 // Read from working block
281 //
282 Status = ReadWorkSpaceData (
283 FtwDevice->FtwFvBlock,
284 FtwDevice->WorkBlockSize,
285 FtwDevice->FtwWorkSpaceLba,
286 FtwDevice->FtwWorkSpaceBase,
287 FtwDevice->FtwWorkSpaceSize,
288 FtwDevice->FtwWorkSpace
289 );
290 if (EFI_ERROR (Status)) {
291 return EFI_ABORTED;
292 }
293 //
294 // Refresh the FtwLastWriteHeader
295 //
296 Status = FtwGetLastWriteHeader (
297 FtwDevice->FtwWorkSpaceHeader,
298 FtwDevice->FtwWorkSpaceSize,
299 &FtwDevice->FtwLastWriteHeader
300 );
301 RemainingSpaceSize = FtwDevice->FtwWorkSpaceSize - ((UINTN) FtwDevice->FtwLastWriteHeader - (UINTN) FtwDevice->FtwWorkSpace);
302 DEBUG ((EFI_D_INFO, "Ftw: Remaining work space size - %x\n", RemainingSpaceSize));
303 //
304 // If FtwGetLastWriteHeader() returns error, or the remaining space size is even not enough to contain
305 // one EFI_FAULT_TOLERANT_WRITE_HEADER + one EFI_FAULT_TOLERANT_WRITE_RECORD(It will cause that the header
306 // pointed by FtwDevice->FtwLastWriteHeader or record pointed by FtwDevice->FtwLastWriteRecord may contain invalid data),
307 // it needs to reclaim work space.
308 //
309 if (EFI_ERROR (Status) || RemainingSpaceSize < sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER) + sizeof (EFI_FAULT_TOLERANT_WRITE_RECORD)) {
310 //
311 // reclaim work space in working block.
312 //
313 Status = FtwReclaimWorkSpace (FtwDevice, TRUE);
314 if (EFI_ERROR (Status)) {
315 DEBUG ((EFI_D_ERROR, "Ftw: Reclaim workspace - %r\n", Status));
316 return EFI_ABORTED;
317 }
318 //
319 // Read from working block again
320 //
321 Status = ReadWorkSpaceData (
322 FtwDevice->FtwFvBlock,
323 FtwDevice->WorkBlockSize,
324 FtwDevice->FtwWorkSpaceLba,
325 FtwDevice->FtwWorkSpaceBase,
326 FtwDevice->FtwWorkSpaceSize,
327 FtwDevice->FtwWorkSpace
328 );
329 if (EFI_ERROR (Status)) {
330 return EFI_ABORTED;
331 }
332
333 Status = FtwGetLastWriteHeader (
334 FtwDevice->FtwWorkSpaceHeader,
335 FtwDevice->FtwWorkSpaceSize,
336 &FtwDevice->FtwLastWriteHeader
337 );
338 if (EFI_ERROR (Status)) {
339 return EFI_ABORTED;
340 }
341 }
342 //
343 // Refresh the FtwLastWriteRecord
344 //
345 Status = FtwGetLastWriteRecord (
346 FtwDevice->FtwLastWriteHeader,
347 &FtwDevice->FtwLastWriteRecord
348 );
349 if (EFI_ERROR (Status)) {
350 return EFI_ABORTED;
351 }
352
353 return EFI_SUCCESS;
354 }
355
356 /**
357 Reclaim the work space on the working block.
358
359 @param FtwDevice Point to private data of FTW driver
360 @param PreserveRecord Whether to preserve the working record is needed
361
362 @retval EFI_SUCCESS The function completed successfully
363 @retval EFI_OUT_OF_RESOURCES Allocate memory error
364 @retval EFI_ABORTED The function could not complete successfully
365
366 **/
367 EFI_STATUS
FtwReclaimWorkSpace(IN EFI_FTW_DEVICE * FtwDevice,IN BOOLEAN PreserveRecord)368 FtwReclaimWorkSpace (
369 IN EFI_FTW_DEVICE *FtwDevice,
370 IN BOOLEAN PreserveRecord
371 )
372 {
373 EFI_STATUS Status;
374 UINTN Length;
375 EFI_FAULT_TOLERANT_WRITE_HEADER *Header;
376 UINT8 *TempBuffer;
377 UINTN TempBufferSize;
378 UINTN SpareBufferSize;
379 UINT8 *SpareBuffer;
380 EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingBlockHeader;
381 UINTN Index;
382 UINT8 *Ptr;
383 EFI_LBA WorkSpaceLbaOffset;
384
385 DEBUG ((EFI_D_INFO, "Ftw: start to reclaim work space\n"));
386
387 WorkSpaceLbaOffset = FtwDevice->FtwWorkSpaceLba - FtwDevice->FtwWorkBlockLba;
388
389 //
390 // Read all original data from working block to a memory buffer
391 //
392 TempBufferSize = FtwDevice->NumberOfWorkBlock * FtwDevice->WorkBlockSize;
393 TempBuffer = AllocateZeroPool (TempBufferSize);
394 if (TempBuffer == NULL) {
395 return EFI_OUT_OF_RESOURCES;
396 }
397
398 Ptr = TempBuffer;
399 for (Index = 0; Index < FtwDevice->NumberOfWorkBlock; Index += 1) {
400 Length = FtwDevice->WorkBlockSize;
401 Status = FtwDevice->FtwFvBlock->Read (
402 FtwDevice->FtwFvBlock,
403 FtwDevice->FtwWorkBlockLba + Index,
404 0,
405 &Length,
406 Ptr
407 );
408 if (EFI_ERROR (Status)) {
409 FreePool (TempBuffer);
410 return EFI_ABORTED;
411 }
412
413 Ptr += Length;
414 }
415 //
416 // Clean up the workspace, remove all the completed records.
417 //
418 Ptr = TempBuffer +
419 (UINTN) WorkSpaceLbaOffset * FtwDevice->WorkBlockSize +
420 FtwDevice->FtwWorkSpaceBase;
421
422 //
423 // Clear the content of buffer that will save the new work space data
424 //
425 SetMem (Ptr, FtwDevice->FtwWorkSpaceSize, FTW_ERASED_BYTE);
426
427 //
428 // Copy EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER to buffer
429 //
430 CopyMem (
431 Ptr,
432 FtwDevice->FtwWorkSpaceHeader,
433 sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER)
434 );
435 if (PreserveRecord) {
436 //
437 // Get the last record following the header,
438 //
439 Status = FtwGetLastWriteHeader (
440 FtwDevice->FtwWorkSpaceHeader,
441 FtwDevice->FtwWorkSpaceSize,
442 &FtwDevice->FtwLastWriteHeader
443 );
444 Header = FtwDevice->FtwLastWriteHeader;
445 if (!EFI_ERROR (Status) && (Header != NULL) && (Header->Complete != FTW_VALID_STATE) && (Header->HeaderAllocated == FTW_VALID_STATE)) {
446 CopyMem (
447 Ptr + sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),
448 FtwDevice->FtwLastWriteHeader,
449 FTW_WRITE_TOTAL_SIZE (Header->NumberOfWrites, Header->PrivateDataSize)
450 );
451 }
452 }
453
454 CopyMem (
455 FtwDevice->FtwWorkSpace,
456 Ptr,
457 FtwDevice->FtwWorkSpaceSize
458 );
459
460 FtwGetLastWriteHeader (
461 FtwDevice->FtwWorkSpaceHeader,
462 FtwDevice->FtwWorkSpaceSize,
463 &FtwDevice->FtwLastWriteHeader
464 );
465
466 FtwGetLastWriteRecord (
467 FtwDevice->FtwLastWriteHeader,
468 &FtwDevice->FtwLastWriteRecord
469 );
470
471 //
472 // Set the WorkingBlockValid and WorkingBlockInvalid as INVALID
473 //
474 WorkingBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) (TempBuffer +
475 (UINTN) WorkSpaceLbaOffset * FtwDevice->WorkBlockSize +
476 FtwDevice->FtwWorkSpaceBase);
477 WorkingBlockHeader->WorkingBlockValid = FTW_INVALID_STATE;
478 WorkingBlockHeader->WorkingBlockInvalid = FTW_INVALID_STATE;
479
480 //
481 // Try to keep the content of spare block
482 // Save spare block into a spare backup memory buffer (Sparebuffer)
483 //
484 SpareBufferSize = FtwDevice->SpareAreaLength;
485 SpareBuffer = AllocatePool (SpareBufferSize);
486 if (SpareBuffer == NULL) {
487 FreePool (TempBuffer);
488 return EFI_OUT_OF_RESOURCES;
489 }
490
491 Ptr = SpareBuffer;
492 for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
493 Length = FtwDevice->SpareBlockSize;
494 Status = FtwDevice->FtwBackupFvb->Read (
495 FtwDevice->FtwBackupFvb,
496 FtwDevice->FtwSpareLba + Index,
497 0,
498 &Length,
499 Ptr
500 );
501 if (EFI_ERROR (Status)) {
502 FreePool (TempBuffer);
503 FreePool (SpareBuffer);
504 return EFI_ABORTED;
505 }
506
507 Ptr += Length;
508 }
509 //
510 // Write the memory buffer to spare block
511 //
512 Status = FtwEraseSpareBlock (FtwDevice);
513 if (EFI_ERROR (Status)) {
514 FreePool (TempBuffer);
515 FreePool (SpareBuffer);
516 return EFI_ABORTED;
517 }
518 Ptr = TempBuffer;
519 for (Index = 0; TempBufferSize > 0; Index += 1) {
520 if (TempBufferSize > FtwDevice->SpareBlockSize) {
521 Length = FtwDevice->SpareBlockSize;
522 } else {
523 Length = TempBufferSize;
524 }
525 Status = FtwDevice->FtwBackupFvb->Write (
526 FtwDevice->FtwBackupFvb,
527 FtwDevice->FtwSpareLba + Index,
528 0,
529 &Length,
530 Ptr
531 );
532 if (EFI_ERROR (Status)) {
533 FreePool (TempBuffer);
534 FreePool (SpareBuffer);
535 return EFI_ABORTED;
536 }
537
538 Ptr += Length;
539 TempBufferSize -= Length;
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 // Before erase the working block, set WorkingBlockInvalid in working block.
562 //
563 // Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER,
564 // WorkingBlockInvalid);
565 //
566 Status = FtwUpdateFvState (
567 FtwDevice->FtwFvBlock,
568 FtwDevice->WorkBlockSize,
569 FtwDevice->FtwWorkSpaceLba,
570 FtwDevice->FtwWorkSpaceBase + sizeof (EFI_GUID) + sizeof (UINT32),
571 WORKING_BLOCK_INVALID
572 );
573 if (EFI_ERROR (Status)) {
574 FreePool (SpareBuffer);
575 return EFI_ABORTED;
576 }
577
578 FtwDevice->FtwWorkSpaceHeader->WorkingBlockInvalid = FTW_VALID_STATE;
579
580 //
581 // Write the spare block to working block
582 //
583 Status = FlushSpareBlockToWorkingBlock (FtwDevice);
584 if (EFI_ERROR (Status)) {
585 FreePool (SpareBuffer);
586 return Status;
587 }
588 //
589 // Restore spare backup buffer into spare block , if no failure happened during FtwWrite.
590 //
591 Status = FtwEraseSpareBlock (FtwDevice);
592 if (EFI_ERROR (Status)) {
593 FreePool (SpareBuffer);
594 return EFI_ABORTED;
595 }
596 Ptr = SpareBuffer;
597 for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
598 Length = FtwDevice->SpareBlockSize;
599 Status = FtwDevice->FtwBackupFvb->Write (
600 FtwDevice->FtwBackupFvb,
601 FtwDevice->FtwSpareLba + Index,
602 0,
603 &Length,
604 Ptr
605 );
606 if (EFI_ERROR (Status)) {
607 FreePool (SpareBuffer);
608 return EFI_ABORTED;
609 }
610
611 Ptr += Length;
612 }
613
614 FreePool (SpareBuffer);
615
616 DEBUG ((EFI_D_INFO, "Ftw: reclaim work space successfully\n"));
617
618 return EFI_SUCCESS;
619 }
620