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