]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWrite.c
Add SMRAM range check to fault tolerant write SMM SMI handler.
[mirror_edk2.git] / MdeModulePkg / Universal / FaultTolerantWriteDxe / FaultTolerantWrite.c
CommitLineData
85e923a5
LG
1/** @file\r
2\r
8a2d4996 3 These are the common Fault Tolerant Write (FTW) functions that are shared \r
4 by DXE FTW driver and SMM FTW driver.\r
85e923a5 5\r
e5eed7d3
HT
6Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>\r
7This program and the accompanying materials \r
85e923a5
LG
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
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
26 @param This The pointer to this protocol instance. \r
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
62 @param This The pointer to this protocol instance. \r
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
84 UINTN Length;\r
85 UINTN Offset;\r
86 EFI_FTW_DEVICE *FtwDevice;\r
87 EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader;\r
88\r
89 FtwDevice = FTW_CONTEXT_FROM_THIS (This);\r
90\r
91 Status = WorkSpaceRefresh (FtwDevice);\r
92 if (EFI_ERROR (Status)) {\r
93 return EFI_ABORTED;\r
94 }\r
95 //\r
96 // Check if there is enough space for the coming allocation\r
97 //\r
98 if (WRITE_TOTAL_SIZE (NumberOfWrites, PrivateDataSize) > FtwDevice->FtwWorkSpaceHeader->WriteQueueSize) {\r
99 DEBUG ((EFI_D_ERROR, "Ftw: Allocate() request exceed Workspace, Caller: %g\n", CallerId));\r
100 return EFI_BUFFER_TOO_SMALL;\r
101 }\r
102 //\r
103 // Find the last write header and record.\r
104 // If the FtwHeader is complete, skip the completed last write header/records\r
105 //\r
106 FtwHeader = FtwDevice->FtwLastWriteHeader;\r
107\r
108 //\r
109 // Previous write has not completed, access denied.\r
110 //\r
111 if ((FtwHeader->HeaderAllocated == FTW_VALID_STATE) || (FtwHeader->WritesAllocated == FTW_VALID_STATE)) {\r
112 return EFI_ACCESS_DENIED;\r
113 }\r
114 //\r
115 // If workspace is not enough, then reclaim workspace\r
116 //\r
117 Offset = (UINT8 *) FtwHeader - (UINT8 *) FtwDevice->FtwWorkSpace;\r
118 if (Offset + WRITE_TOTAL_SIZE (NumberOfWrites, PrivateDataSize) > FtwDevice->FtwWorkSpaceSize) {\r
119 Status = FtwReclaimWorkSpace (FtwDevice, TRUE);\r
120 if (EFI_ERROR (Status)) {\r
121 return EFI_ABORTED;\r
122 }\r
123\r
124 FtwHeader = FtwDevice->FtwLastWriteHeader;\r
125 }\r
126 //\r
127 // Prepare FTW write header,\r
128 // overwrite the buffer and write to workspace.\r
129 //\r
130 FtwHeader->WritesAllocated = FTW_INVALID_STATE;\r
131 FtwHeader->Complete = FTW_INVALID_STATE;\r
132 CopyMem (&FtwHeader->CallerId, CallerId, sizeof (EFI_GUID));\r
133 FtwHeader->NumberOfWrites = NumberOfWrites;\r
134 FtwHeader->PrivateDataSize = PrivateDataSize;\r
135 FtwHeader->HeaderAllocated = FTW_VALID_STATE;\r
136\r
137 Length = sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER);\r
138 Status = FtwDevice->FtwFvBlock->Write (\r
139 FtwDevice->FtwFvBlock,\r
140 FtwDevice->FtwWorkSpaceLba,\r
141 FtwDevice->FtwWorkSpaceBase + Offset,\r
142 &Length,\r
143 (UINT8 *) FtwHeader\r
144 );\r
145 if (EFI_ERROR (Status)) {\r
146 return EFI_ABORTED;\r
147 }\r
148 //\r
149 // Update Header->WriteAllocated as VALID\r
150 //\r
151 Status = FtwUpdateFvState (\r
152 FtwDevice->FtwFvBlock,\r
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
162 (EFI_D_ERROR,\r
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
173 Write a record with fault tolerant mannaer.\r
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
177 @param This The pointer to this protocol instance. \r
178 @param Fvb The FVB protocol that provides services for\r
179 reading, writing, and erasing the target block.\r
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
187 IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This,\r
188 IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb\r
189 )\r
190{\r
191 EFI_STATUS Status;\r
192 EFI_FTW_DEVICE *FtwDevice;\r
193 EFI_FAULT_TOLERANT_WRITE_HEADER *Header;\r
194 EFI_FAULT_TOLERANT_WRITE_RECORD *Record;\r
195 UINTN Offset;\r
196\r
197 FtwDevice = FTW_CONTEXT_FROM_THIS (This);\r
198\r
199 //\r
200 // Spare Complete but Destination not complete,\r
8a2d4996 201 // Recover the target block with the spare block.\r
85e923a5
LG
202 //\r
203 Header = FtwDevice->FtwLastWriteHeader;\r
204 Record = FtwDevice->FtwLastWriteRecord;\r
205\r
206 //\r
207 // IF target block is working block, THEN Flush Spare Block To Working Block;\r
208 // ELSE flush spare block to target block, which may be boot block after all.\r
209 //\r
210 if (IsWorkingBlock (FtwDevice, Fvb, Record->Lba)) {\r
211 //\r
212 // If target block is working block,\r
213 // it also need to set SPARE_COMPLETED to spare block.\r
214 //\r
215 Offset = (UINT8 *) Record - FtwDevice->FtwWorkSpace;\r
216 Status = FtwUpdateFvState (\r
217 FtwDevice->FtwBackupFvb,\r
218 FtwDevice->FtwWorkSpaceLba,\r
219 FtwDevice->FtwWorkSpaceBase + Offset,\r
220 SPARE_COMPLETED\r
221 );\r
222 if (EFI_ERROR (Status)) {\r
223 return EFI_ABORTED;\r
224 }\r
225\r
226 Status = FlushSpareBlockToWorkingBlock (FtwDevice);\r
227 } else if (IsBootBlock (FtwDevice, Fvb, Record->Lba)) {\r
228 //\r
229 // Update boot block\r
230 //\r
231 Status = FlushSpareBlockToBootBlock (FtwDevice);\r
232 } else {\r
233 //\r
234 // Update blocks other than working block or boot block\r
235 //\r
236 Status = FlushSpareBlockToTargetBlock (FtwDevice, Fvb, Record->Lba);\r
237 }\r
238\r
239 if (EFI_ERROR (Status)) {\r
240 return EFI_ABORTED;\r
241 }\r
242 //\r
243 // Record the DestionationComplete in record\r
244 //\r
245 Offset = (UINT8 *) Record - FtwDevice->FtwWorkSpace;\r
246 Status = FtwUpdateFvState (\r
247 FtwDevice->FtwFvBlock,\r
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
266 FtwDevice->FtwWorkSpaceLba,\r
267 FtwDevice->FtwWorkSpaceBase + Offset,\r
268 WRITES_COMPLETED\r
269 );\r
270 Header->Complete = FTW_VALID_STATE;\r
271 if (EFI_ERROR (Status)) {\r
272 return EFI_ABORTED;\r
273 }\r
274 }\r
275\r
276 return EFI_SUCCESS;\r
277}\r
278\r
279/**\r
280 Starts a target block update. This function will record data about write\r
281 in fault tolerant storage and will complete the write in a recoverable\r
282 manner, ensuring at all times that either the original contents or\r
283 the modified contents are available.\r
284\r
285 @param This The pointer to this protocol instance. \r
286 @param Lba The logical block address of the target block.\r
287 @param Offset The offset within the target block to place the data.\r
288 @param Length The number of bytes to write to the target block.\r
289 @param PrivateData A pointer to private data that the caller requires to\r
290 complete any pending writes in the event of a fault.\r
291 @param FvBlockHandle The handle of FVB protocol that provides services for\r
292 reading, writing, and erasing the target block.\r
293 @param Buffer The data to write.\r
294\r
295 @retval EFI_SUCCESS The function completed successfully \r
296 @retval EFI_ABORTED The function could not complete successfully. \r
297 @retval EFI_BAD_BUFFER_SIZE The input data can't fit within the spare block. \r
298 Offset + *NumBytes > SpareAreaLength.\r
299 @retval EFI_ACCESS_DENIED No writes have been allocated. \r
300 @retval EFI_OUT_OF_RESOURCES Cannot allocate enough memory resource.\r
301 @retval EFI_NOT_FOUND Cannot find FVB protocol by handle.\r
302\r
303**/\r
304EFI_STATUS\r
305EFIAPI\r
306FtwWrite (\r
307 IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This,\r
308 IN EFI_LBA Lba,\r
309 IN UINTN Offset,\r
310 IN UINTN Length,\r
311 IN VOID *PrivateData,\r
312 IN EFI_HANDLE FvBlockHandle,\r
313 IN VOID *Buffer\r
314 )\r
315{\r
316 EFI_STATUS Status;\r
317 EFI_FTW_DEVICE *FtwDevice;\r
318 EFI_FAULT_TOLERANT_WRITE_HEADER *Header;\r
319 EFI_FAULT_TOLERANT_WRITE_RECORD *Record;\r
320 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;\r
321 UINTN MyLength;\r
322 UINTN MyOffset;\r
323 UINTN MyBufferSize;\r
324 UINT8 *MyBuffer;\r
325 UINTN SpareBufferSize;\r
326 UINT8 *SpareBuffer;\r
327 UINTN Index;\r
328 UINT8 *Ptr;\r
329 EFI_PHYSICAL_ADDRESS FvbPhysicalAddress;\r
330\r
331 FtwDevice = FTW_CONTEXT_FROM_THIS (This);\r
332\r
333 Status = WorkSpaceRefresh (FtwDevice);\r
334 if (EFI_ERROR (Status)) {\r
335 return EFI_ABORTED;\r
336 }\r
337\r
338 Header = FtwDevice->FtwLastWriteHeader;\r
339 Record = FtwDevice->FtwLastWriteRecord;\r
340 \r
341 if (IsErasedFlashBuffer ((UINT8 *) Header, sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER))) {\r
342 if (PrivateData == NULL) {\r
343 //\r
344 // Ftw Write Header is not allocated.\r
345 // No additional private data, the private data size is zero. Number of record can be set to 1.\r
346 //\r
347 Status = FtwAllocate (This, &gEfiCallerIdGuid, 0, 1);\r
348 if (EFI_ERROR (Status)) {\r
349 return Status;\r
350 }\r
351 } else {\r
352 //\r
353 // Ftw Write Header is not allocated\r
354 // Additional private data is not NULL, the private data size can't be determined.\r
355 //\r
356 DEBUG ((EFI_D_ERROR, "Ftw: no allocates space for write record!\n"));\r
357 DEBUG ((EFI_D_ERROR, "Ftw: Allocate service should be called before Write service!\n"));\r
358 return EFI_NOT_READY;\r
359 }\r
360 }\r
361\r
362 //\r
363 // If Record is out of the range of Header, return access denied.\r
364 //\r
365 if (((UINTN)((UINT8 *) Record - (UINT8 *) Header)) > WRITE_TOTAL_SIZE (Header->NumberOfWrites - 1, Header->PrivateDataSize)) {\r
366 return EFI_ACCESS_DENIED;\r
367 }\r
368\r
369 //\r
370 // Check the COMPLETE flag of last write header\r
371 //\r
372 if (Header->Complete == FTW_VALID_STATE) {\r
373 return EFI_ACCESS_DENIED;\r
374 }\r
375\r
376 if (Record->DestinationComplete == FTW_VALID_STATE) {\r
377 return EFI_ACCESS_DENIED;\r
378 }\r
379\r
380 if ((Record->SpareComplete == FTW_VALID_STATE) && (Record->DestinationComplete != FTW_VALID_STATE)) {\r
381 return EFI_NOT_READY;\r
382 }\r
383 //\r
384 // Check if the input data can fit within the target block\r
385 //\r
386 if ((Offset + Length) > FtwDevice->SpareAreaLength) {\r
387 return EFI_BAD_BUFFER_SIZE;\r
388 }\r
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
399 DEBUG ((EFI_D_ERROR, "FtwLite: Get FVB physical address - %r\n", Status));\r
400 return EFI_ABORTED;\r
401 }\r
402\r
403 //\r
404 // Set BootBlockUpdate FLAG if it's updating boot block.\r
405 //\r
406 if (IsBootBlock (FtwDevice, Fvb, Lba)) {\r
407 Record->BootBlockUpdate = FTW_VALID_STATE;\r
408 }\r
409 //\r
410 // Write the record to the work space.\r
411 //\r
412 Record->Lba = Lba;\r
413 Record->Offset = Offset;\r
414 Record->Length = Length;\r
415 Record->FvBaseAddress = FvbPhysicalAddress;\r
f0480ecf
LG
416 if (PrivateData != NULL) {\r
417 CopyMem ((Record + 1), PrivateData, Header->PrivateDataSize);\r
418 }\r
85e923a5
LG
419\r
420 MyOffset = (UINT8 *) Record - FtwDevice->FtwWorkSpace;\r
421 MyLength = RECORD_SIZE (Header->PrivateDataSize);\r
422\r
423 Status = FtwDevice->FtwFvBlock->Write (\r
424 FtwDevice->FtwFvBlock,\r
425 FtwDevice->FtwWorkSpaceLba,\r
426 FtwDevice->FtwWorkSpaceBase + MyOffset,\r
427 &MyLength,\r
428 (UINT8 *) Record\r
429 );\r
430 if (EFI_ERROR (Status)) {\r
431 return EFI_ABORTED;\r
432 }\r
433 //\r
434 // Record has written to working block, then do the data.\r
435 //\r
436 //\r
437 // Allocate a memory buffer\r
438 //\r
439 MyBufferSize = FtwDevice->SpareAreaLength;\r
440 MyBuffer = AllocatePool (MyBufferSize);\r
441 if (MyBuffer == NULL) {\r
442 return EFI_OUT_OF_RESOURCES;\r
443 }\r
444 //\r
445 // Read all original data from target block to memory buffer\r
446 //\r
447 Ptr = MyBuffer;\r
448 for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {\r
449 MyLength = FtwDevice->BlockSize;\r
450 Status = Fvb->Read (Fvb, Lba + Index, 0, &MyLength, Ptr);\r
451 if (EFI_ERROR (Status)) {\r
452 FreePool (MyBuffer);\r
453 return EFI_ABORTED;\r
454 }\r
455\r
456 Ptr += MyLength;\r
457 }\r
458 //\r
459 // Overwrite the updating range data with\r
460 // the input buffer content\r
461 //\r
462 CopyMem (MyBuffer + Offset, Buffer, Length);\r
463\r
464 //\r
465 // Try to keep the content of spare block\r
466 // Save spare block into a spare backup memory buffer (Sparebuffer)\r
467 //\r
468 SpareBufferSize = FtwDevice->SpareAreaLength;\r
469 SpareBuffer = AllocatePool (SpareBufferSize);\r
470 if (SpareBuffer == NULL) {\r
471 FreePool (MyBuffer);\r
472 return EFI_OUT_OF_RESOURCES;\r
473 }\r
474\r
475 Ptr = SpareBuffer;\r
476 for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {\r
477 MyLength = FtwDevice->BlockSize;\r
478 Status = FtwDevice->FtwBackupFvb->Read (\r
479 FtwDevice->FtwBackupFvb,\r
480 FtwDevice->FtwSpareLba + Index,\r
481 0,\r
482 &MyLength,\r
483 Ptr\r
484 );\r
485 if (EFI_ERROR (Status)) {\r
486 FreePool (MyBuffer);\r
487 FreePool (SpareBuffer);\r
488 return EFI_ABORTED;\r
489 }\r
490\r
491 Ptr += MyLength;\r
492 }\r
493 //\r
494 // Write the memory buffer to spare block\r
495 //\r
496 Status = FtwEraseSpareBlock (FtwDevice);\r
497 Ptr = MyBuffer;\r
498 for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {\r
499 MyLength = FtwDevice->BlockSize;\r
500 Status = FtwDevice->FtwBackupFvb->Write (\r
501 FtwDevice->FtwBackupFvb,\r
502 FtwDevice->FtwSpareLba + Index,\r
503 0,\r
504 &MyLength,\r
505 Ptr\r
506 );\r
507 if (EFI_ERROR (Status)) {\r
508 FreePool (MyBuffer);\r
509 FreePool (SpareBuffer);\r
510 return EFI_ABORTED;\r
511 }\r
512\r
513 Ptr += MyLength;\r
514 }\r
515 //\r
516 // Free MyBuffer\r
517 //\r
518 FreePool (MyBuffer);\r
519\r
520 //\r
521 // Set the SpareComplete in the FTW record,\r
522 //\r
523 MyOffset = (UINT8 *) Record - FtwDevice->FtwWorkSpace;\r
524 Status = FtwUpdateFvState (\r
525 FtwDevice->FtwFvBlock,\r
526 FtwDevice->FtwWorkSpaceLba,\r
527 FtwDevice->FtwWorkSpaceBase + MyOffset,\r
528 SPARE_COMPLETED\r
529 );\r
530 if (EFI_ERROR (Status)) {\r
531 FreePool (SpareBuffer);\r
532 return EFI_ABORTED;\r
533 }\r
534\r
535 Record->SpareComplete = FTW_VALID_STATE;\r
536\r
537 //\r
538 // Since the content has already backuped in spare block, the write is\r
539 // guaranteed to be completed with fault tolerant manner.\r
540 //\r
541 Status = FtwWriteRecord (This, Fvb);\r
542 if (EFI_ERROR (Status)) {\r
543 FreePool (SpareBuffer);\r
544 return EFI_ABORTED;\r
545 }\r
546 //\r
547 // Restore spare backup buffer into spare block , if no failure happened during FtwWrite.\r
548 //\r
549 Status = FtwEraseSpareBlock (FtwDevice);\r
550 Ptr = SpareBuffer;\r
551 for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {\r
552 MyLength = FtwDevice->BlockSize;\r
553 Status = FtwDevice->FtwBackupFvb->Write (\r
554 FtwDevice->FtwBackupFvb,\r
555 FtwDevice->FtwSpareLba + Index,\r
556 0,\r
557 &MyLength,\r
558 Ptr\r
559 );\r
560 if (EFI_ERROR (Status)) {\r
561 FreePool (SpareBuffer);\r
562 return EFI_ABORTED;\r
563 }\r
564\r
565 Ptr += MyLength;\r
566 }\r
567 //\r
568 // All success.\r
569 //\r
570 FreePool (SpareBuffer);\r
571\r
572 DEBUG (\r
573 (EFI_D_ERROR,\r
574 "Ftw: Write() success, (Lba:Offset)=(%lx:0x%x), Length: 0x%x\n",\r
575 Lba,\r
576 Offset,\r
577 Length)\r
578 );\r
579\r
580 return EFI_SUCCESS;\r
581}\r
582\r
583/**\r
584 Restarts a previously interrupted write. The caller must provide the\r
585 block protocol needed to complete the interrupted write.\r
586\r
587 @param This The pointer to this protocol instance. \r
588 @param FvBlockHandle The handle of FVB protocol that provides services for\r
589 reading, writing, and erasing the target block.\r
590\r
591 @retval EFI_SUCCESS The function completed successfully\r
592 @retval EFI_ACCESS_DENIED No pending writes exist\r
593 @retval EFI_NOT_FOUND FVB protocol not found by the handle\r
594 @retval EFI_ABORTED The function could not complete successfully\r
595\r
596**/\r
597EFI_STATUS\r
598EFIAPI\r
599FtwRestart (\r
600 IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This,\r
601 IN EFI_HANDLE FvBlockHandle\r
602 )\r
603{\r
604 EFI_STATUS Status;\r
605 EFI_FTW_DEVICE *FtwDevice;\r
606 EFI_FAULT_TOLERANT_WRITE_HEADER *Header;\r
607 EFI_FAULT_TOLERANT_WRITE_RECORD *Record;\r
608 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;\r
609\r
610 FtwDevice = FTW_CONTEXT_FROM_THIS (This);\r
611\r
612 Status = WorkSpaceRefresh (FtwDevice);\r
613 if (EFI_ERROR (Status)) {\r
614 return EFI_ABORTED;\r
615 }\r
616\r
617 Header = FtwDevice->FtwLastWriteHeader;\r
618 Record = FtwDevice->FtwLastWriteRecord;\r
619\r
620 //\r
621 // Spare Complete but Destination not complete,\r
622 // Recover the targt block with the spare block.\r
623 //\r
624 Status = FtwGetFvbByHandle (FvBlockHandle, &Fvb);\r
625 if (EFI_ERROR (Status)) {\r
626 return EFI_NOT_FOUND;\r
627 }\r
628\r
629 //\r
630 // Check the COMPLETE flag of last write header\r
631 //\r
632 if (Header->Complete == FTW_VALID_STATE) {\r
633 return EFI_ACCESS_DENIED;\r
634 }\r
635\r
636 //\r
637 // Check the flags of last write record\r
638 //\r
639 if (Record->DestinationComplete == FTW_VALID_STATE) {\r
640 return EFI_ACCESS_DENIED;\r
641 }\r
642\r
643 if ((Record->SpareComplete != FTW_VALID_STATE)) {\r
644 return EFI_ABORTED;\r
645 }\r
646\r
647 //\r
648 // Since the content has already backuped in spare block, the write is\r
649 // guaranteed to be completed with fault tolerant manner.\r
650 //\r
651 Status = FtwWriteRecord (This, Fvb);\r
652 if (EFI_ERROR (Status)) {\r
653 return EFI_ABORTED;\r
654 }\r
655\r
656 //\r
657 // Erase Spare block\r
658 // This is restart, no need to keep spareblock content.\r
659 //\r
660 FtwEraseSpareBlock (FtwDevice);\r
661\r
662 DEBUG ((EFI_D_ERROR, "Ftw: Restart() success \n"));\r
663 return EFI_SUCCESS;\r
664}\r
665\r
666/**\r
667 Aborts all previous allocated writes.\r
668\r
669 @param This The pointer to this protocol instance. \r
670\r
671 @retval EFI_SUCCESS The function completed successfully\r
672 @retval EFI_ABORTED The function could not complete successfully.\r
673 @retval EFI_NOT_FOUND No allocated writes exist.\r
674\r
675**/\r
676EFI_STATUS\r
677EFIAPI\r
678FtwAbort (\r
679 IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This\r
680 )\r
681{\r
682 EFI_STATUS Status;\r
683 UINTN Offset;\r
684 EFI_FTW_DEVICE *FtwDevice;\r
685\r
686 FtwDevice = FTW_CONTEXT_FROM_THIS (This);\r
687\r
688 Status = WorkSpaceRefresh (FtwDevice);\r
689 if (EFI_ERROR (Status)) {\r
690 return EFI_ABORTED;\r
691 }\r
692\r
693 if (FtwDevice->FtwLastWriteHeader->Complete == FTW_VALID_STATE) {\r
694 return EFI_NOT_FOUND;\r
695 }\r
696 //\r
697 // Update the complete state of the header as VALID and abort.\r
698 //\r
699 Offset = (UINT8 *) FtwDevice->FtwLastWriteHeader - FtwDevice->FtwWorkSpace;\r
700 Status = FtwUpdateFvState (\r
701 FtwDevice->FtwFvBlock,\r
702 FtwDevice->FtwWorkSpaceLba,\r
703 FtwDevice->FtwWorkSpaceBase + Offset,\r
704 WRITES_COMPLETED\r
705 );\r
706 if (EFI_ERROR (Status)) {\r
707 return EFI_ABORTED;\r
708 }\r
709\r
710 FtwDevice->FtwLastWriteHeader->Complete = FTW_VALID_STATE;\r
711\r
712 DEBUG ((EFI_D_ERROR, "Ftw: Abort() success \n"));\r
713 return EFI_SUCCESS;\r
714}\r
715\r
716/**\r
717 Starts a target block update. This records information about the write\r
718 in fault tolerant storage and will complete the write in a recoverable\r
719 manner, ensuring at all times that either the original contents or\r
720 the modified contents are available.\r
721\r
722 @param This The pointer to this protocol instance. \r
723 @param CallerId The GUID identifying the last write.\r
724 @param Lba The logical block address of the last write.\r
725 @param Offset The offset within the block of the last write.\r
726 @param Length The length of the last write.\r
727 @param PrivateDataSize bytes from the private data\r
728 stored for this write.\r
729 @param PrivateData A pointer to a buffer. The function will copy\r
730 @param Complete A Boolean value with TRUE indicating\r
731 that the write was completed.\r
732\r
733 @retval EFI_SUCCESS The function completed successfully\r
734 @retval EFI_ABORTED The function could not complete successfully\r
735 @retval EFI_NOT_FOUND No allocated writes exist\r
736 @retval EFI_BUFFER_TOO_SMALL Input buffer is not larget enough\r
737\r
738**/\r
739EFI_STATUS\r
740EFIAPI\r
741FtwGetLastWrite (\r
742 IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL *This,\r
743 OUT EFI_GUID *CallerId,\r
744 OUT EFI_LBA *Lba,\r
745 OUT UINTN *Offset,\r
746 OUT UINTN *Length,\r
747 IN OUT UINTN *PrivateDataSize,\r
748 OUT VOID *PrivateData,\r
749 OUT BOOLEAN *Complete\r
750 )\r
751{\r
752 EFI_STATUS Status;\r
753 EFI_FTW_DEVICE *FtwDevice;\r
754 EFI_FAULT_TOLERANT_WRITE_HEADER *Header;\r
755 EFI_FAULT_TOLERANT_WRITE_RECORD *Record;\r
756\r
757 if (!FeaturePcdGet(PcdFullFtwServiceEnable)) {\r
758 return EFI_UNSUPPORTED;\r
759 }\r
760\r
761 FtwDevice = FTW_CONTEXT_FROM_THIS (This);\r
762\r
763 Status = WorkSpaceRefresh (FtwDevice);\r
764 if (EFI_ERROR (Status)) {\r
765 return EFI_ABORTED;\r
766 }\r
767\r
768 Header = FtwDevice->FtwLastWriteHeader;\r
769 Record = FtwDevice->FtwLastWriteRecord;\r
770\r
771 //\r
772 // If Header is incompleted and the last record has completed, then\r
773 // call Abort() to set the Header->Complete FLAG.\r
774 //\r
775 if ((Header->Complete != FTW_VALID_STATE) &&\r
776 (Record->DestinationComplete == FTW_VALID_STATE) &&\r
777 IsLastRecordOfWrites (Header, Record)\r
778 ) {\r
779\r
780 Status = FtwAbort (This);\r
781 *Complete = TRUE;\r
782 return EFI_NOT_FOUND;\r
783 }\r
784 //\r
785 // If there is no write header/record, return not found.\r
786 //\r
787 if (Header->HeaderAllocated != FTW_VALID_STATE) {\r
788 *Complete = TRUE;\r
789 return EFI_NOT_FOUND;\r
790 }\r
791 //\r
792 // If this record SpareComplete has not set, then it can not restart.\r
793 //\r
794 if (Record->SpareComplete != FTW_VALID_STATE) {\r
f0480ecf
LG
795 Status = GetPreviousRecordOfWrites (Header, &Record);\r
796 if (EFI_ERROR (Status)) {\r
85e923a5 797 FtwAbort (This);\r
85e923a5
LG
798 *Complete = TRUE;\r
799 return EFI_NOT_FOUND;\r
85e923a5 800 }\r
d2fbaaab 801 ASSERT (Record != NULL);\r
85e923a5 802 }\r
f0480ecf 803\r
85e923a5
LG
804 //\r
805 // Fill all the requested values\r
806 //\r
807 CopyMem (CallerId, &Header->CallerId, sizeof (EFI_GUID));\r
808 *Lba = Record->Lba;\r
809 *Offset = Record->Offset;\r
810 *Length = Record->Length;\r
811 *Complete = (BOOLEAN) (Record->DestinationComplete == FTW_VALID_STATE);\r
812\r
813 if (*PrivateDataSize < Header->PrivateDataSize) {\r
814 *PrivateDataSize = Header->PrivateDataSize;\r
815 PrivateData = NULL;\r
816 Status = EFI_BUFFER_TOO_SMALL;\r
817 } else {\r
818 *PrivateDataSize = Header->PrivateDataSize;\r
819 CopyMem (PrivateData, Record + 1, *PrivateDataSize);\r
820 Status = EFI_SUCCESS;\r
821 }\r
822\r
823 DEBUG ((EFI_D_ERROR, "Ftw: GetLasetWrite() success\n"));\r
824\r
825 return Status;\r
826}\r
827\r