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