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