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