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