]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWrite.c
MdeModulePkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / MdeModulePkg / Universal / FaultTolerantWriteDxe / FaultTolerantWrite.c
CommitLineData
85e923a5
LG
1/** @file\r
2\r
d1102dba 3 These are the common Fault Tolerant Write (FTW) functions that are shared\r
8a2d4996 4 by DXE FTW driver and SMM FTW driver.\r
85e923a5 5\r
d1102dba 6Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>\r
9d510e61 7SPDX-License-Identifier: BSD-2-Clause-Patent\r
85e923a5
LG
8\r
9**/\r
10\r
11#include "FaultTolerantWrite.h"\r
12\r
85e923a5
LG
13//\r
14// Fault Tolerant Write Protocol API\r
15//\r
16/**\r
17 Query the largest block that may be updated in a fault tolerant manner.\r
18\r
19\r
d1102dba 20 @param This The pointer to this protocol instance.\r
85e923a5
LG
21 @param BlockSize A pointer to a caller allocated UINTN that is updated to\r
22 indicate the size of the largest block that can be updated.\r
23\r
24 @return EFI_SUCCESS The function completed successfully\r
25\r
26**/\r
27EFI_STATUS\r
28EFIAPI\r
29FtwGetMaxBlockSize (\r
30 IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This,\r
31 OUT UINTN *BlockSize\r
32 )\r
33{\r
34 EFI_FTW_DEVICE *FtwDevice;\r
35\r
36 if (!FeaturePcdGet(PcdFullFtwServiceEnable)) {\r
37 return EFI_UNSUPPORTED;\r
38 }\r
39\r
40 FtwDevice = FTW_CONTEXT_FROM_THIS (This);\r
41\r
42 *BlockSize = FtwDevice->SpareAreaLength;\r
43\r
44 return EFI_SUCCESS;\r
45}\r
46\r
47/**\r
48 Allocates space for the protocol to maintain information about writes.\r
49 Since writes must be completed in a fault tolerant manner and multiple\r
50 updates will require more resources to be successful, this function\r
51 enables the protocol to ensure that enough space exists to track\r
52 information about the upcoming writes.\r
53\r
54 All writes must be completed or aborted before another fault tolerant write can occur.\r
55\r
d1102dba 56 @param This The pointer to this protocol instance.\r
85e923a5
LG
57 @param CallerId The GUID identifying the write.\r
58 @param PrivateDataSize The size of the caller's private data\r
59 that must be recorded for each write.\r
60 @param NumberOfWrites The number of fault tolerant block writes\r
61 that will need to occur.\r
62\r
63 @return EFI_SUCCESS The function completed successfully\r
64 @retval EFI_ABORTED The function could not complete successfully.\r
65 @retval EFI_ACCESS_DENIED All allocated writes have not been completed.\r
66\r
67**/\r
68EFI_STATUS\r
69EFIAPI\r
70FtwAllocate (\r
71 IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This,\r
72 IN EFI_GUID *CallerId,\r
73 IN UINTN PrivateDataSize,\r
74 IN UINTN NumberOfWrites\r
75 )\r
76{\r
77 EFI_STATUS Status;\r
85e923a5
LG
78 UINTN Offset;\r
79 EFI_FTW_DEVICE *FtwDevice;\r
80 EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader;\r
81\r
82 FtwDevice = FTW_CONTEXT_FROM_THIS (This);\r
83\r
84 Status = WorkSpaceRefresh (FtwDevice);\r
85 if (EFI_ERROR (Status)) {\r
86 return EFI_ABORTED;\r
87 }\r
88 //\r
89 // Check if there is enough space for the coming allocation\r
90 //\r
3e02ebb2 91 if (FTW_WRITE_TOTAL_SIZE (NumberOfWrites, PrivateDataSize) > FtwDevice->FtwWorkSpaceHeader->WriteQueueSize) {\r
85e923a5
LG
92 DEBUG ((EFI_D_ERROR, "Ftw: Allocate() request exceed Workspace, Caller: %g\n", CallerId));\r
93 return EFI_BUFFER_TOO_SMALL;\r
94 }\r
95 //\r
96 // Find the last write header and record.\r
97 // If the FtwHeader is complete, skip the completed last write header/records\r
98 //\r
99 FtwHeader = FtwDevice->FtwLastWriteHeader;\r
100\r
101 //\r
102 // Previous write has not completed, access denied.\r
103 //\r
104 if ((FtwHeader->HeaderAllocated == FTW_VALID_STATE) || (FtwHeader->WritesAllocated == FTW_VALID_STATE)) {\r
105 return EFI_ACCESS_DENIED;\r
106 }\r
107 //\r
108 // If workspace is not enough, then reclaim workspace\r
109 //\r
110 Offset = (UINT8 *) FtwHeader - (UINT8 *) FtwDevice->FtwWorkSpace;\r
3e02ebb2 111 if (Offset + FTW_WRITE_TOTAL_SIZE (NumberOfWrites, PrivateDataSize) > FtwDevice->FtwWorkSpaceSize) {\r
85e923a5
LG
112 Status = FtwReclaimWorkSpace (FtwDevice, TRUE);\r
113 if (EFI_ERROR (Status)) {\r
114 return EFI_ABORTED;\r
115 }\r
116\r
117 FtwHeader = FtwDevice->FtwLastWriteHeader;\r
118 }\r
119 //\r
120 // Prepare FTW write header,\r
121 // overwrite the buffer and write to workspace.\r
122 //\r
123 FtwHeader->WritesAllocated = FTW_INVALID_STATE;\r
124 FtwHeader->Complete = FTW_INVALID_STATE;\r
125 CopyMem (&FtwHeader->CallerId, CallerId, sizeof (EFI_GUID));\r
126 FtwHeader->NumberOfWrites = NumberOfWrites;\r
127 FtwHeader->PrivateDataSize = PrivateDataSize;\r
128 FtwHeader->HeaderAllocated = FTW_VALID_STATE;\r
129\r
0d3edd9d
SZ
130 Status = WriteWorkSpaceData (\r
131 FtwDevice->FtwFvBlock,\r
132 FtwDevice->WorkBlockSize,\r
133 FtwDevice->FtwWorkSpaceLba,\r
134 FtwDevice->FtwWorkSpaceBase + Offset,\r
135 sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER),\r
136 (UINT8 *) FtwHeader\r
137 );\r
85e923a5
LG
138 if (EFI_ERROR (Status)) {\r
139 return EFI_ABORTED;\r
140 }\r
141 //\r
142 // Update Header->WriteAllocated as VALID\r
143 //\r
144 Status = FtwUpdateFvState (\r
145 FtwDevice->FtwFvBlock,\r
0d3edd9d 146 FtwDevice->WorkBlockSize,\r
85e923a5
LG
147 FtwDevice->FtwWorkSpaceLba,\r
148 FtwDevice->FtwWorkSpaceBase + Offset,\r
149 WRITES_ALLOCATED\r
150 );\r
151 if (EFI_ERROR (Status)) {\r
152 return EFI_ABORTED;\r
153 }\r
154\r
155 DEBUG (\r
9a95972e 156 (EFI_D_INFO,\r
85e923a5
LG
157 "Ftw: Allocate() success, Caller:%g, # %d\n",\r
158 CallerId,\r
159 NumberOfWrites)\r
160 );\r
161\r
162 return EFI_SUCCESS;\r
163}\r
164\r
165\r
166/**\r
0d3edd9d 167 Write a record with fault tolerant manner.\r
85e923a5
LG
168 Since the content has already backuped in spare block, the write is\r
169 guaranteed to be completed with fault tolerant manner.\r
170\r
d1102dba 171 @param This The pointer to this protocol instance.\r
85e923a5
LG
172 @param Fvb The FVB protocol that provides services for\r
173 reading, writing, and erasing the target block.\r
0d3edd9d 174 @param BlockSize The size of the block.\r
85e923a5
LG
175\r
176 @retval EFI_SUCCESS The function completed successfully\r
177 @retval EFI_ABORTED The function could not complete successfully\r
178\r
179**/\r
180EFI_STATUS\r
181FtwWriteRecord (\r
182 IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This,\r
0d3edd9d
SZ
183 IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb,\r
184 IN UINTN BlockSize\r
85e923a5
LG
185 )\r
186{\r
187 EFI_STATUS Status;\r
188 EFI_FTW_DEVICE *FtwDevice;\r
189 EFI_FAULT_TOLERANT_WRITE_HEADER *Header;\r
190 EFI_FAULT_TOLERANT_WRITE_RECORD *Record;\r
191 UINTN Offset;\r
0d3edd9d 192 UINTN NumberOfWriteBlocks;\r
85e923a5
LG
193\r
194 FtwDevice = FTW_CONTEXT_FROM_THIS (This);\r
195\r
196 //\r
197 // Spare Complete but Destination not complete,\r
8a2d4996 198 // Recover the target block with the spare block.\r
85e923a5
LG
199 //\r
200 Header = FtwDevice->FtwLastWriteHeader;\r
201 Record = FtwDevice->FtwLastWriteRecord;\r
202\r
203 //\r
204 // IF target block is working block, THEN Flush Spare Block To Working Block;\r
205 // ELSE flush spare block to target block, which may be boot block after all.\r
206 //\r
207 if (IsWorkingBlock (FtwDevice, Fvb, Record->Lba)) {\r
208 //\r
209 // If target block is working block,\r
210 // it also need to set SPARE_COMPLETED to spare block.\r
211 //\r
212 Offset = (UINT8 *) Record - FtwDevice->FtwWorkSpace;\r
213 Status = FtwUpdateFvState (\r
214 FtwDevice->FtwBackupFvb,\r
0d3edd9d
SZ
215 FtwDevice->SpareBlockSize,\r
216 FtwDevice->FtwSpareLba + FtwDevice->FtwWorkSpaceLbaInSpare,\r
217 FtwDevice->FtwWorkSpaceBaseInSpare + Offset,\r
85e923a5
LG
218 SPARE_COMPLETED\r
219 );\r
220 if (EFI_ERROR (Status)) {\r
221 return EFI_ABORTED;\r
222 }\r
223\r
224 Status = FlushSpareBlockToWorkingBlock (FtwDevice);\r
0d3edd9d 225 } else if (IsBootBlock (FtwDevice, Fvb)) {\r
85e923a5
LG
226 //\r
227 // Update boot block\r
228 //\r
229 Status = FlushSpareBlockToBootBlock (FtwDevice);\r
230 } else {\r
231 //\r
232 // Update blocks other than working block or boot block\r
233 //\r
0d3edd9d
SZ
234 NumberOfWriteBlocks = FTW_BLOCKS ((UINTN) (Record->Offset + Record->Length), BlockSize);\r
235 Status = FlushSpareBlockToTargetBlock (FtwDevice, Fvb, Record->Lba, BlockSize, NumberOfWriteBlocks);\r
85e923a5
LG
236 }\r
237\r
238 if (EFI_ERROR (Status)) {\r
239 return EFI_ABORTED;\r
240 }\r
241 //\r
242 // Record the DestionationComplete in record\r
243 //\r
244 Offset = (UINT8 *) Record - FtwDevice->FtwWorkSpace;\r
245 Status = FtwUpdateFvState (\r
246 FtwDevice->FtwFvBlock,\r
0d3edd9d 247 FtwDevice->WorkBlockSize,\r
85e923a5
LG
248 FtwDevice->FtwWorkSpaceLba,\r
249 FtwDevice->FtwWorkSpaceBase + Offset,\r
250 DEST_COMPLETED\r
251 );\r
252 if (EFI_ERROR (Status)) {\r
253 return EFI_ABORTED;\r
254 }\r
255\r
256 Record->DestinationComplete = FTW_VALID_STATE;\r
257\r
258 //\r
259 // If this is the last Write in these write sequence,\r
260 // set the complete flag of write header.\r
261 //\r
262 if (IsLastRecordOfWrites (Header, Record)) {\r
263 Offset = (UINT8 *) Header - FtwDevice->FtwWorkSpace;\r
264 Status = FtwUpdateFvState (\r
265 FtwDevice->FtwFvBlock,\r
0d3edd9d 266 FtwDevice->WorkBlockSize,\r
85e923a5
LG
267 FtwDevice->FtwWorkSpaceLba,\r
268 FtwDevice->FtwWorkSpaceBase + Offset,\r
269 WRITES_COMPLETED\r
270 );\r
271 Header->Complete = FTW_VALID_STATE;\r
272 if (EFI_ERROR (Status)) {\r
273 return EFI_ABORTED;\r
274 }\r
275 }\r
276\r
277 return EFI_SUCCESS;\r
278}\r
279\r
280/**\r
281 Starts a target block update. This function will record data about write\r
282 in fault tolerant storage and will complete the write in a recoverable\r
283 manner, ensuring at all times that either the original contents or\r
284 the modified contents are available.\r
285\r
d1102dba 286 @param This The pointer to this protocol instance.\r
85e923a5
LG
287 @param Lba The logical block address of the target block.\r
288 @param Offset The offset within the target block to place the data.\r
289 @param Length The number of bytes to write to the target block.\r
290 @param PrivateData A pointer to private data that the caller requires to\r
291 complete any pending writes in the event of a fault.\r
292 @param FvBlockHandle The handle of FVB protocol that provides services for\r
293 reading, writing, and erasing the target block.\r
294 @param Buffer The data to write.\r
295\r
d1102dba
LG
296 @retval EFI_SUCCESS The function completed successfully\r
297 @retval EFI_ABORTED The function could not complete successfully.\r
298 @retval EFI_BAD_BUFFER_SIZE The input data can't fit within the spare block.\r
85e923a5 299 Offset + *NumBytes > SpareAreaLength.\r
d1102dba 300 @retval EFI_ACCESS_DENIED No writes have been allocated.\r
85e923a5
LG
301 @retval EFI_OUT_OF_RESOURCES Cannot allocate enough memory resource.\r
302 @retval EFI_NOT_FOUND Cannot find FVB protocol by handle.\r
303\r
304**/\r
305EFI_STATUS\r
306EFIAPI\r
307FtwWrite (\r
308 IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This,\r
309 IN EFI_LBA Lba,\r
310 IN UINTN Offset,\r
311 IN UINTN Length,\r
312 IN VOID *PrivateData,\r
313 IN EFI_HANDLE FvBlockHandle,\r
314 IN VOID *Buffer\r
315 )\r
316{\r
317 EFI_STATUS Status;\r
318 EFI_FTW_DEVICE *FtwDevice;\r
319 EFI_FAULT_TOLERANT_WRITE_HEADER *Header;\r
320 EFI_FAULT_TOLERANT_WRITE_RECORD *Record;\r
321 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;\r
322 UINTN MyLength;\r
323 UINTN MyOffset;\r
324 UINTN MyBufferSize;\r
325 UINT8 *MyBuffer;\r
326 UINTN SpareBufferSize;\r
327 UINT8 *SpareBuffer;\r
328 UINTN Index;\r
329 UINT8 *Ptr;\r
330 EFI_PHYSICAL_ADDRESS FvbPhysicalAddress;\r
0d3edd9d
SZ
331 UINTN BlockSize;\r
332 UINTN NumberOfBlocks;\r
333 UINTN NumberOfWriteBlocks;\r
334 UINTN WriteLength;\r
85e923a5
LG
335\r
336 FtwDevice = FTW_CONTEXT_FROM_THIS (This);\r
337\r
338 Status = WorkSpaceRefresh (FtwDevice);\r
339 if (EFI_ERROR (Status)) {\r
340 return EFI_ABORTED;\r
341 }\r
342\r
343 Header = FtwDevice->FtwLastWriteHeader;\r
344 Record = FtwDevice->FtwLastWriteRecord;\r
d1102dba 345\r
85e923a5
LG
346 if (IsErasedFlashBuffer ((UINT8 *) Header, sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER))) {\r
347 if (PrivateData == NULL) {\r
348 //\r
349 // Ftw Write Header is not allocated.\r
350 // No additional private data, the private data size is zero. Number of record can be set to 1.\r
351 //\r
352 Status = FtwAllocate (This, &gEfiCallerIdGuid, 0, 1);\r
353 if (EFI_ERROR (Status)) {\r
354 return Status;\r
355 }\r
356 } else {\r
357 //\r
358 // Ftw Write Header is not allocated\r
359 // Additional private data is not NULL, the private data size can't be determined.\r
360 //\r
361 DEBUG ((EFI_D_ERROR, "Ftw: no allocates space for write record!\n"));\r
362 DEBUG ((EFI_D_ERROR, "Ftw: Allocate service should be called before Write service!\n"));\r
363 return EFI_NOT_READY;\r
364 }\r
365 }\r
366\r
367 //\r
368 // If Record is out of the range of Header, return access denied.\r
369 //\r
809e2bbf 370 if (((UINTN) Record - (UINTN) Header) > FTW_WRITE_TOTAL_SIZE (Header->NumberOfWrites - 1, Header->PrivateDataSize)) {\r
85e923a5
LG
371 return EFI_ACCESS_DENIED;\r
372 }\r
373\r
374 //\r
375 // Check the COMPLETE flag of last write header\r
376 //\r
377 if (Header->Complete == FTW_VALID_STATE) {\r
378 return EFI_ACCESS_DENIED;\r
379 }\r
380\r
381 if (Record->DestinationComplete == FTW_VALID_STATE) {\r
382 return EFI_ACCESS_DENIED;\r
383 }\r
384\r
385 if ((Record->SpareComplete == FTW_VALID_STATE) && (Record->DestinationComplete != FTW_VALID_STATE)) {\r
386 return EFI_NOT_READY;\r
387 }\r
0d3edd9d 388\r
85e923a5
LG
389 //\r
390 // Get the FVB protocol by handle\r
391 //\r
392 Status = FtwGetFvbByHandle (FvBlockHandle, &Fvb);\r
393 if (EFI_ERROR (Status)) {\r
394 return EFI_NOT_FOUND;\r
395 }\r
396\r
397 Status = Fvb->GetPhysicalAddress (Fvb, &FvbPhysicalAddress);\r
398 if (EFI_ERROR (Status)) {\r
0d3edd9d
SZ
399 DEBUG ((EFI_D_ERROR, "Ftw: Write(), Get FVB physical address - %r\n", Status));\r
400 return EFI_ABORTED;\r
401 }\r
402\r
403 //\r
404 // Now, one FVB has one type of BlockSize.\r
405 //\r
406 Status = Fvb->GetBlockSize (Fvb, 0, &BlockSize, &NumberOfBlocks);\r
407 if (EFI_ERROR (Status)) {\r
408 DEBUG ((EFI_D_ERROR, "Ftw: Write(), Get block size - %r\n", Status));\r
85e923a5
LG
409 return EFI_ABORTED;\r
410 }\r
411\r
0d3edd9d
SZ
412 NumberOfWriteBlocks = FTW_BLOCKS (Offset + Length, BlockSize);\r
413 DEBUG ((EFI_D_INFO, "Ftw: Write(), BlockSize - 0x%x, NumberOfWriteBlock - 0x%x\n", BlockSize, NumberOfWriteBlocks));\r
414 WriteLength = NumberOfWriteBlocks * BlockSize;\r
415\r
416 //\r
417 // Check if the input data can fit within the spare block.\r
418 //\r
419 if (WriteLength > FtwDevice->SpareAreaLength) {\r
420 return EFI_BAD_BUFFER_SIZE;\r
421 }\r
422\r
85e923a5
LG
423 //\r
424 // Set BootBlockUpdate FLAG if it's updating boot block.\r
425 //\r
0d3edd9d 426 if (IsBootBlock (FtwDevice, Fvb)) {\r
85e923a5 427 Record->BootBlockUpdate = FTW_VALID_STATE;\r
0d3edd9d
SZ
428 //\r
429 // Boot Block and Spare Block should have same block size and block numbers.\r
430 //\r
431 ASSERT ((BlockSize == FtwDevice->SpareBlockSize) && (NumberOfWriteBlocks == FtwDevice->NumberOfSpareBlock));\r
85e923a5
LG
432 }\r
433 //\r
434 // Write the record to the work space.\r
435 //\r
436 Record->Lba = Lba;\r
437 Record->Offset = Offset;\r
438 Record->Length = Length;\r
0d3edd9d 439 Record->RelativeOffset = (INT64) (FvbPhysicalAddress + (UINTN) Lba * BlockSize) - (INT64) FtwDevice->SpareAreaAddress;\r
f0480ecf 440 if (PrivateData != NULL) {\r
3e02ebb2 441 CopyMem ((Record + 1), PrivateData, (UINTN) Header->PrivateDataSize);\r
f0480ecf 442 }\r
85e923a5
LG
443\r
444 MyOffset = (UINT8 *) Record - FtwDevice->FtwWorkSpace;\r
3e02ebb2 445 MyLength = FTW_RECORD_SIZE (Header->PrivateDataSize);\r
85e923a5 446\r
0d3edd9d
SZ
447 Status = WriteWorkSpaceData (\r
448 FtwDevice->FtwFvBlock,\r
449 FtwDevice->WorkBlockSize,\r
450 FtwDevice->FtwWorkSpaceLba,\r
451 FtwDevice->FtwWorkSpaceBase + MyOffset,\r
452 MyLength,\r
453 (UINT8 *) Record\r
454 );\r
85e923a5
LG
455 if (EFI_ERROR (Status)) {\r
456 return EFI_ABORTED;\r
457 }\r
458 //\r
459 // Record has written to working block, then do the data.\r
460 //\r
461 //\r
462 // Allocate a memory buffer\r
463 //\r
0d3edd9d 464 MyBufferSize = WriteLength;\r
85e923a5
LG
465 MyBuffer = AllocatePool (MyBufferSize);\r
466 if (MyBuffer == NULL) {\r
467 return EFI_OUT_OF_RESOURCES;\r
468 }\r
469 //\r
470 // Read all original data from target block to memory buffer\r
471 //\r
472 Ptr = MyBuffer;\r
0d3edd9d
SZ
473 for (Index = 0; Index < NumberOfWriteBlocks; Index += 1) {\r
474 MyLength = BlockSize;\r
85e923a5
LG
475 Status = Fvb->Read (Fvb, Lba + Index, 0, &MyLength, Ptr);\r
476 if (EFI_ERROR (Status)) {\r
477 FreePool (MyBuffer);\r
478 return EFI_ABORTED;\r
479 }\r
480\r
481 Ptr += MyLength;\r
482 }\r
483 //\r
484 // Overwrite the updating range data with\r
485 // the input buffer content\r
486 //\r
487 CopyMem (MyBuffer + Offset, Buffer, Length);\r
488\r
489 //\r
490 // Try to keep the content of spare block\r
491 // Save spare block into a spare backup memory buffer (Sparebuffer)\r
492 //\r
493 SpareBufferSize = FtwDevice->SpareAreaLength;\r
494 SpareBuffer = AllocatePool (SpareBufferSize);\r
495 if (SpareBuffer == NULL) {\r
496 FreePool (MyBuffer);\r
497 return EFI_OUT_OF_RESOURCES;\r
498 }\r
499\r
500 Ptr = SpareBuffer;\r
501 for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {\r
0d3edd9d 502 MyLength = FtwDevice->SpareBlockSize;\r
85e923a5
LG
503 Status = FtwDevice->FtwBackupFvb->Read (\r
504 FtwDevice->FtwBackupFvb,\r
505 FtwDevice->FtwSpareLba + Index,\r
506 0,\r
507 &MyLength,\r
508 Ptr\r
509 );\r
510 if (EFI_ERROR (Status)) {\r
511 FreePool (MyBuffer);\r
512 FreePool (SpareBuffer);\r
513 return EFI_ABORTED;\r
514 }\r
515\r
516 Ptr += MyLength;\r
517 }\r
518 //\r
519 // Write the memory buffer to spare block\r
0d3edd9d 520 // Do not assume Spare Block and Target Block have same block size\r
85e923a5
LG
521 //\r
522 Status = FtwEraseSpareBlock (FtwDevice);\r
de2d7497
SZ
523 if (EFI_ERROR (Status)) {\r
524 FreePool (MyBuffer);\r
525 FreePool (SpareBuffer);\r
526 return EFI_ABORTED;\r
527 }\r
85e923a5 528 Ptr = MyBuffer;\r
0d3edd9d
SZ
529 for (Index = 0; MyBufferSize > 0; Index += 1) {\r
530 if (MyBufferSize > FtwDevice->SpareBlockSize) {\r
531 MyLength = FtwDevice->SpareBlockSize;\r
532 } else {\r
533 MyLength = MyBufferSize;\r
534 }\r
85e923a5
LG
535 Status = FtwDevice->FtwBackupFvb->Write (\r
536 FtwDevice->FtwBackupFvb,\r
537 FtwDevice->FtwSpareLba + Index,\r
538 0,\r
539 &MyLength,\r
540 Ptr\r
541 );\r
542 if (EFI_ERROR (Status)) {\r
543 FreePool (MyBuffer);\r
544 FreePool (SpareBuffer);\r
545 return EFI_ABORTED;\r
546 }\r
547\r
548 Ptr += MyLength;\r
0d3edd9d 549 MyBufferSize -= MyLength;\r
85e923a5
LG
550 }\r
551 //\r
552 // Free MyBuffer\r
553 //\r
554 FreePool (MyBuffer);\r
555\r
556 //\r
557 // Set the SpareComplete in the FTW record,\r
558 //\r
559 MyOffset = (UINT8 *) Record - FtwDevice->FtwWorkSpace;\r
560 Status = FtwUpdateFvState (\r
561 FtwDevice->FtwFvBlock,\r
0d3edd9d 562 FtwDevice->WorkBlockSize,\r
85e923a5
LG
563 FtwDevice->FtwWorkSpaceLba,\r
564 FtwDevice->FtwWorkSpaceBase + MyOffset,\r
565 SPARE_COMPLETED\r
566 );\r
567 if (EFI_ERROR (Status)) {\r
568 FreePool (SpareBuffer);\r
569 return EFI_ABORTED;\r
570 }\r
571\r
572 Record->SpareComplete = FTW_VALID_STATE;\r
573\r
574 //\r
575 // Since the content has already backuped in spare block, the write is\r
576 // guaranteed to be completed with fault tolerant manner.\r
577 //\r
0d3edd9d 578 Status = FtwWriteRecord (This, Fvb, BlockSize);\r
85e923a5
LG
579 if (EFI_ERROR (Status)) {\r
580 FreePool (SpareBuffer);\r
581 return EFI_ABORTED;\r
582 }\r
583 //\r
584 // Restore spare backup buffer into spare block , if no failure happened during FtwWrite.\r
585 //\r
586 Status = FtwEraseSpareBlock (FtwDevice);\r
de2d7497
SZ
587 if (EFI_ERROR (Status)) {\r
588 FreePool (SpareBuffer);\r
589 return EFI_ABORTED;\r
590 }\r
85e923a5
LG
591 Ptr = SpareBuffer;\r
592 for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {\r
0d3edd9d 593 MyLength = FtwDevice->SpareBlockSize;\r
85e923a5
LG
594 Status = FtwDevice->FtwBackupFvb->Write (\r
595 FtwDevice->FtwBackupFvb,\r
596 FtwDevice->FtwSpareLba + Index,\r
597 0,\r
598 &MyLength,\r
599 Ptr\r
600 );\r
601 if (EFI_ERROR (Status)) {\r
602 FreePool (SpareBuffer);\r
603 return EFI_ABORTED;\r
604 }\r
605\r
606 Ptr += MyLength;\r
607 }\r
608 //\r
609 // All success.\r
610 //\r
611 FreePool (SpareBuffer);\r
612\r
613 DEBUG (\r
9a95972e 614 (EFI_D_INFO,\r
85e923a5
LG
615 "Ftw: Write() success, (Lba:Offset)=(%lx:0x%x), Length: 0x%x\n",\r
616 Lba,\r
617 Offset,\r
618 Length)\r
619 );\r
620\r
621 return EFI_SUCCESS;\r
622}\r
623\r
624/**\r
625 Restarts a previously interrupted write. The caller must provide the\r
626 block protocol needed to complete the interrupted write.\r
627\r
d1102dba 628 @param This The pointer to this protocol instance.\r
85e923a5
LG
629 @param FvBlockHandle The handle of FVB protocol that provides services for\r
630 reading, writing, and erasing the target block.\r
631\r
632 @retval EFI_SUCCESS The function completed successfully\r
633 @retval EFI_ACCESS_DENIED No pending writes exist\r
634 @retval EFI_NOT_FOUND FVB protocol not found by the handle\r
635 @retval EFI_ABORTED The function could not complete successfully\r
636\r
637**/\r
638EFI_STATUS\r
639EFIAPI\r
640FtwRestart (\r
641 IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This,\r
642 IN EFI_HANDLE FvBlockHandle\r
643 )\r
644{\r
645 EFI_STATUS Status;\r
646 EFI_FTW_DEVICE *FtwDevice;\r
647 EFI_FAULT_TOLERANT_WRITE_HEADER *Header;\r
648 EFI_FAULT_TOLERANT_WRITE_RECORD *Record;\r
649 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;\r
0d3edd9d
SZ
650 UINTN BlockSize;\r
651 UINTN NumberOfBlocks;\r
85e923a5
LG
652\r
653 FtwDevice = FTW_CONTEXT_FROM_THIS (This);\r
654\r
655 Status = WorkSpaceRefresh (FtwDevice);\r
656 if (EFI_ERROR (Status)) {\r
657 return EFI_ABORTED;\r
658 }\r
659\r
660 Header = FtwDevice->FtwLastWriteHeader;\r
661 Record = FtwDevice->FtwLastWriteRecord;\r
662\r
663 //\r
664 // Spare Complete but Destination not complete,\r
665 // Recover the targt block with the spare block.\r
666 //\r
667 Status = FtwGetFvbByHandle (FvBlockHandle, &Fvb);\r
668 if (EFI_ERROR (Status)) {\r
669 return EFI_NOT_FOUND;\r
670 }\r
671\r
0d3edd9d
SZ
672 //\r
673 // Now, one FVB has one type of BlockSize\r
674 //\r
675 Status = Fvb->GetBlockSize (Fvb, 0, &BlockSize, &NumberOfBlocks);\r
676 if (EFI_ERROR (Status)) {\r
677 DEBUG ((EFI_D_ERROR, "Ftw: Restart(), Get block size - %r\n", Status));\r
678 return EFI_ABORTED;\r
679 }\r
680\r
85e923a5
LG
681 //\r
682 // Check the COMPLETE flag of last write header\r
683 //\r
684 if (Header->Complete == FTW_VALID_STATE) {\r
685 return EFI_ACCESS_DENIED;\r
686 }\r
687\r
688 //\r
689 // Check the flags of last write record\r
690 //\r
691 if (Record->DestinationComplete == FTW_VALID_STATE) {\r
692 return EFI_ACCESS_DENIED;\r
693 }\r
694\r
695 if ((Record->SpareComplete != FTW_VALID_STATE)) {\r
696 return EFI_ABORTED;\r
697 }\r
698\r
699 //\r
700 // Since the content has already backuped in spare block, the write is\r
701 // guaranteed to be completed with fault tolerant manner.\r
702 //\r
0d3edd9d 703 Status = FtwWriteRecord (This, Fvb, BlockSize);\r
85e923a5
LG
704 if (EFI_ERROR (Status)) {\r
705 return EFI_ABORTED;\r
706 }\r
707\r
708 //\r
709 // Erase Spare block\r
710 // This is restart, no need to keep spareblock content.\r
711 //\r
de2d7497
SZ
712 Status = FtwEraseSpareBlock (FtwDevice);\r
713 if (EFI_ERROR (Status)) {\r
714 return EFI_ABORTED;\r
715 }\r
85e923a5 716\r
fcdfd249 717 DEBUG ((EFI_D_INFO, "%a(): success\n", __FUNCTION__));\r
85e923a5
LG
718 return EFI_SUCCESS;\r
719}\r
720\r
721/**\r
722 Aborts all previous allocated writes.\r
723\r
d1102dba 724 @param This The pointer to this protocol instance.\r
85e923a5
LG
725\r
726 @retval EFI_SUCCESS The function completed successfully\r
727 @retval EFI_ABORTED The function could not complete successfully.\r
728 @retval EFI_NOT_FOUND No allocated writes exist.\r
729\r
730**/\r
731EFI_STATUS\r
732EFIAPI\r
733FtwAbort (\r
734 IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This\r
735 )\r
736{\r
737 EFI_STATUS Status;\r
738 UINTN Offset;\r
739 EFI_FTW_DEVICE *FtwDevice;\r
740\r
741 FtwDevice = FTW_CONTEXT_FROM_THIS (This);\r
742\r
743 Status = WorkSpaceRefresh (FtwDevice);\r
744 if (EFI_ERROR (Status)) {\r
745 return EFI_ABORTED;\r
746 }\r
747\r
3e02ebb2
SZ
748 if (FtwDevice->FtwLastWriteHeader->HeaderAllocated != FTW_VALID_STATE) {\r
749 return EFI_NOT_FOUND;\r
750 }\r
751\r
85e923a5
LG
752 if (FtwDevice->FtwLastWriteHeader->Complete == FTW_VALID_STATE) {\r
753 return EFI_NOT_FOUND;\r
754 }\r
755 //\r
756 // Update the complete state of the header as VALID and abort.\r
757 //\r
758 Offset = (UINT8 *) FtwDevice->FtwLastWriteHeader - FtwDevice->FtwWorkSpace;\r
759 Status = FtwUpdateFvState (\r
760 FtwDevice->FtwFvBlock,\r
0d3edd9d 761 FtwDevice->WorkBlockSize,\r
85e923a5
LG
762 FtwDevice->FtwWorkSpaceLba,\r
763 FtwDevice->FtwWorkSpaceBase + Offset,\r
764 WRITES_COMPLETED\r
765 );\r
766 if (EFI_ERROR (Status)) {\r
767 return EFI_ABORTED;\r
768 }\r
769\r
770 FtwDevice->FtwLastWriteHeader->Complete = FTW_VALID_STATE;\r
771\r
fcdfd249 772 DEBUG ((EFI_D_INFO, "%a(): success\n", __FUNCTION__));\r
85e923a5
LG
773 return EFI_SUCCESS;\r
774}\r
775\r
776/**\r
777 Starts a target block update. This records information about the write\r
778 in fault tolerant storage and will complete the write in a recoverable\r
779 manner, ensuring at all times that either the original contents or\r
780 the modified contents are available.\r
781\r
d1102dba 782 @param This The pointer to this protocol instance.\r
85e923a5
LG
783 @param CallerId The GUID identifying the last write.\r
784 @param Lba The logical block address of the last write.\r
785 @param Offset The offset within the block of the last write.\r
786 @param Length The length of the last write.\r
787 @param PrivateDataSize bytes from the private data\r
788 stored for this write.\r
789 @param PrivateData A pointer to a buffer. The function will copy\r
790 @param Complete A Boolean value with TRUE indicating\r
791 that the write was completed.\r
792\r
793 @retval EFI_SUCCESS The function completed successfully\r
794 @retval EFI_ABORTED The function could not complete successfully\r
795 @retval EFI_NOT_FOUND No allocated writes exist\r
796 @retval EFI_BUFFER_TOO_SMALL Input buffer is not larget enough\r
797\r
798**/\r
799EFI_STATUS\r
800EFIAPI\r
801FtwGetLastWrite (\r
802 IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This,\r
803 OUT EFI_GUID *CallerId,\r
804 OUT EFI_LBA *Lba,\r
805 OUT UINTN *Offset,\r
806 OUT UINTN *Length,\r
807 IN OUT UINTN *PrivateDataSize,\r
808 OUT VOID *PrivateData,\r
809 OUT BOOLEAN *Complete\r
810 )\r
811{\r
812 EFI_STATUS Status;\r
813 EFI_FTW_DEVICE *FtwDevice;\r
814 EFI_FAULT_TOLERANT_WRITE_HEADER *Header;\r
815 EFI_FAULT_TOLERANT_WRITE_RECORD *Record;\r
816\r
817 if (!FeaturePcdGet(PcdFullFtwServiceEnable)) {\r
818 return EFI_UNSUPPORTED;\r
819 }\r
820\r
821 FtwDevice = FTW_CONTEXT_FROM_THIS (This);\r
822\r
823 Status = WorkSpaceRefresh (FtwDevice);\r
824 if (EFI_ERROR (Status)) {\r
825 return EFI_ABORTED;\r
826 }\r
827\r
828 Header = FtwDevice->FtwLastWriteHeader;\r
829 Record = FtwDevice->FtwLastWriteRecord;\r
830\r
831 //\r
832 // If Header is incompleted and the last record has completed, then\r
833 // call Abort() to set the Header->Complete FLAG.\r
834 //\r
835 if ((Header->Complete != FTW_VALID_STATE) &&\r
836 (Record->DestinationComplete == FTW_VALID_STATE) &&\r
837 IsLastRecordOfWrites (Header, Record)\r
838 ) {\r
839\r
840 Status = FtwAbort (This);\r
841 *Complete = TRUE;\r
842 return EFI_NOT_FOUND;\r
843 }\r
844 //\r
845 // If there is no write header/record, return not found.\r
846 //\r
847 if (Header->HeaderAllocated != FTW_VALID_STATE) {\r
848 *Complete = TRUE;\r
849 return EFI_NOT_FOUND;\r
850 }\r
851 //\r
852 // If this record SpareComplete has not set, then it can not restart.\r
853 //\r
854 if (Record->SpareComplete != FTW_VALID_STATE) {\r
f0480ecf
LG
855 Status = GetPreviousRecordOfWrites (Header, &Record);\r
856 if (EFI_ERROR (Status)) {\r
85e923a5 857 FtwAbort (This);\r
85e923a5
LG
858 *Complete = TRUE;\r
859 return EFI_NOT_FOUND;\r
85e923a5 860 }\r
d2fbaaab 861 ASSERT (Record != NULL);\r
85e923a5 862 }\r
f0480ecf 863\r
85e923a5
LG
864 //\r
865 // Fill all the requested values\r
866 //\r
867 CopyMem (CallerId, &Header->CallerId, sizeof (EFI_GUID));\r
868 *Lba = Record->Lba;\r
3e02ebb2
SZ
869 *Offset = (UINTN) Record->Offset;\r
870 *Length = (UINTN) Record->Length;\r
85e923a5
LG
871 *Complete = (BOOLEAN) (Record->DestinationComplete == FTW_VALID_STATE);\r
872\r
873 if (*PrivateDataSize < Header->PrivateDataSize) {\r
3e02ebb2 874 *PrivateDataSize = (UINTN) Header->PrivateDataSize;\r
85e923a5
LG
875 PrivateData = NULL;\r
876 Status = EFI_BUFFER_TOO_SMALL;\r
877 } else {\r
3e02ebb2 878 *PrivateDataSize = (UINTN) Header->PrivateDataSize;\r
85e923a5
LG
879 CopyMem (PrivateData, Record + 1, *PrivateDataSize);\r
880 Status = EFI_SUCCESS;\r
881 }\r
882\r
fcdfd249 883 DEBUG ((EFI_D_INFO, "%a(): success\n", __FUNCTION__));\r
85e923a5
LG
884\r
885 return Status;\r
886}\r
887\r