2 This driver installs gEdkiiFaultTolerantWriteGuid PPI to inform
3 the check for FTW last write data has been done.
5 Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.<BR>
6 SPDX-License-Identifier: BSD-2-Clause-Patent
12 #include <Guid/SystemNvDataGuid.h>
13 #include <Guid/FaultTolerantWrite.h>
14 #include <Library/PeiServicesLib.h>
15 #include <Library/PcdLib.h>
16 #include <Library/DebugLib.h>
17 #include <Library/BaseMemoryLib.h>
18 #include <Library/HobLib.h>
20 EFI_PEI_PPI_DESCRIPTOR mPpiListVariable
= {
21 (EFI_PEI_PPI_DESCRIPTOR_PPI
| EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST
),
22 &gEdkiiFaultTolerantWriteGuid
,
27 Get the last Write Header pointer.
28 The last write header is the header whose 'complete' state hasn't been set.
29 After all, this header may be a EMPTY header entry for next Allocate.
32 @param FtwWorkSpaceHeader Pointer of the working block header
33 @param FtwWorkSpaceSize Size of the work space
34 @param FtwWriteHeader Pointer to retrieve the last write header
36 @retval EFI_SUCCESS Get the last write record successfully
37 @retval EFI_ABORTED The FTW work space is damaged
41 FtwGetLastWriteHeader (
42 IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
*FtwWorkSpaceHeader
,
43 IN UINTN FtwWorkSpaceSize
,
44 OUT EFI_FAULT_TOLERANT_WRITE_HEADER
**FtwWriteHeader
48 EFI_FAULT_TOLERANT_WRITE_HEADER
*FtwHeader
;
50 *FtwWriteHeader
= NULL
;
51 FtwHeader
= (EFI_FAULT_TOLERANT_WRITE_HEADER
*)(FtwWorkSpaceHeader
+ 1);
52 Offset
= sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
);
54 while (FtwHeader
->Complete
== FTW_VALID_STATE
) {
55 Offset
+= FTW_WRITE_TOTAL_SIZE (FtwHeader
->NumberOfWrites
, FtwHeader
->PrivateDataSize
);
57 // If Offset exceed the FTW work space boudary, return error.
59 if (Offset
>= FtwWorkSpaceSize
) {
60 *FtwWriteHeader
= FtwHeader
;
64 FtwHeader
= (EFI_FAULT_TOLERANT_WRITE_HEADER
*)((UINT8
*)FtwWorkSpaceHeader
+ Offset
);
68 // Last write header is found
70 *FtwWriteHeader
= FtwHeader
;
76 Get the last Write Record pointer. The last write Record is the Record
77 whose DestinationCompleted state hasn't been set. After all, this Record
78 may be a EMPTY record entry for next write.
81 @param FtwWriteHeader Pointer to the write record header
82 @param FtwWriteRecord Pointer to retrieve the last write record
84 @retval EFI_SUCCESS Get the last write record successfully
85 @retval EFI_ABORTED The FTW work space is damaged
89 FtwGetLastWriteRecord (
90 IN EFI_FAULT_TOLERANT_WRITE_HEADER
*FtwWriteHeader
,
91 OUT EFI_FAULT_TOLERANT_WRITE_RECORD
**FtwWriteRecord
95 EFI_FAULT_TOLERANT_WRITE_RECORD
*FtwRecord
;
97 *FtwWriteRecord
= NULL
;
98 FtwRecord
= (EFI_FAULT_TOLERANT_WRITE_RECORD
*)(FtwWriteHeader
+ 1);
101 // Try to find the last write record "that has not completed"
103 for (Index
= 0; Index
< FtwWriteHeader
->NumberOfWrites
; Index
+= 1) {
104 if (FtwRecord
->DestinationComplete
!= FTW_VALID_STATE
) {
106 // The last write record is found
108 *FtwWriteRecord
= FtwRecord
;
114 if (FtwWriteHeader
->PrivateDataSize
!= 0) {
115 FtwRecord
= (EFI_FAULT_TOLERANT_WRITE_RECORD
*)((UINTN
)FtwRecord
+ (UINTN
)FtwWriteHeader
->PrivateDataSize
);
120 // if Index == NumberOfWrites, then
121 // the last record has been written successfully,
122 // but the Header->Complete Flag has not been set.
123 // also return the last record.
125 if (Index
== FtwWriteHeader
->NumberOfWrites
) {
126 *FtwWriteRecord
= (EFI_FAULT_TOLERANT_WRITE_RECORD
*)((UINTN
)FtwRecord
- FTW_RECORD_SIZE (FtwWriteHeader
->PrivateDataSize
));
134 Check to see if it is a valid work space.
137 @param WorkingHeader Pointer of working block header
138 @param WorkingLength Working block length
140 @retval TRUE The work space is valid.
141 @retval FALSE The work space is invalid.
146 IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
*WorkingHeader
,
147 IN UINTN WorkingLength
152 if (WorkingHeader
== NULL
) {
156 if ((WorkingHeader
->WorkingBlockValid
!= FTW_VALID_STATE
) || (WorkingHeader
->WorkingBlockInvalid
== FTW_VALID_STATE
)) {
157 DEBUG ((DEBUG_ERROR
, "FtwPei: Work block header valid bit check error\n"));
161 if (WorkingHeader
->WriteQueueSize
!= (WorkingLength
- sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
))) {
162 DEBUG ((DEBUG_ERROR
, "FtwPei: Work block header WriteQueueSize check error\n"));
167 // Check signature with gEdkiiWorkingBlockSignatureGuid
169 if (!CompareGuid (&gEdkiiWorkingBlockSignatureGuid
, &WorkingHeader
->Signature
)) {
170 DEBUG ((DEBUG_ERROR
, "FtwPei: Work block header signature check error, it should be gEdkiiWorkingBlockSignatureGuid\n"));
172 // To be compatible with old signature gEfiSystemNvDataFvGuid.
174 if (!CompareGuid (&gEfiSystemNvDataFvGuid
, &WorkingHeader
->Signature
)) {
177 Data
= *(UINT8
*)(WorkingHeader
+ 1);
179 DEBUG ((DEBUG_ERROR
, "FtwPei: Old format FTW structure can't be handled\n"));
190 Main entry for Fault Tolerant Write PEIM.
192 @param[in] FileHandle Handle of the file being invoked.
193 @param[in] PeiServices Pointer to PEI Services table.
195 @retval EFI_SUCCESS If the interface could be successfully installed
196 @retval Others Returned from PeiServicesInstallPpi()
201 PeimFaultTolerantWriteInitialize (
202 IN EFI_PEI_FILE_HANDLE FileHandle
,
203 IN CONST EFI_PEI_SERVICES
**PeiServices
207 EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
*FtwWorkingBlockHeader
;
208 EFI_FAULT_TOLERANT_WRITE_HEADER
*FtwLastWriteHeader
;
209 EFI_FAULT_TOLERANT_WRITE_RECORD
*FtwLastWriteRecord
;
210 EFI_PHYSICAL_ADDRESS WorkSpaceAddress
;
211 UINTN WorkSpaceLength
;
212 EFI_PHYSICAL_ADDRESS SpareAreaAddress
;
213 UINTN SpareAreaLength
;
214 EFI_PHYSICAL_ADDRESS WorkSpaceInSpareArea
;
215 FAULT_TOLERANT_WRITE_LAST_WRITE_DATA FtwLastWrite
;
217 FtwWorkingBlockHeader
= NULL
;
218 FtwLastWriteHeader
= NULL
;
219 FtwLastWriteRecord
= NULL
;
221 WorkSpaceAddress
= (EFI_PHYSICAL_ADDRESS
)PcdGet64 (PcdFlashNvStorageFtwWorkingBase64
);
222 if (WorkSpaceAddress
== 0) {
223 WorkSpaceAddress
= (EFI_PHYSICAL_ADDRESS
)PcdGet32 (PcdFlashNvStorageFtwWorkingBase
);
226 WorkSpaceLength
= (UINTN
)PcdGet32 (PcdFlashNvStorageFtwWorkingSize
);
228 SpareAreaAddress
= (EFI_PHYSICAL_ADDRESS
)PcdGet64 (PcdFlashNvStorageFtwSpareBase64
);
229 if (SpareAreaAddress
== 0) {
230 SpareAreaAddress
= (EFI_PHYSICAL_ADDRESS
)PcdGet32 (PcdFlashNvStorageFtwSpareBase
);
233 SpareAreaLength
= (UINTN
)PcdGet32 (PcdFlashNvStorageFtwSpareSize
);
236 // The address of FTW working base and spare base must not be 0.
238 ASSERT ((WorkSpaceAddress
!= 0) && (SpareAreaAddress
!= 0));
240 FtwWorkingBlockHeader
= (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
*)(UINTN
)WorkSpaceAddress
;
241 if (IsValidWorkSpace (FtwWorkingBlockHeader
, WorkSpaceLength
)) {
242 Status
= FtwGetLastWriteHeader (
243 FtwWorkingBlockHeader
,
247 if (!EFI_ERROR (Status
)) {
248 Status
= FtwGetLastWriteRecord (
254 if (!EFI_ERROR (Status
)) {
255 ASSERT (FtwLastWriteRecord
!= NULL
);
256 if ((FtwLastWriteRecord
->SpareComplete
== FTW_VALID_STATE
) && (FtwLastWriteRecord
->DestinationComplete
!= FTW_VALID_STATE
)) {
258 // If FTW last write was still in progress with SpareComplete set and DestinationComplete not set.
259 // It means the target buffer has been backed up in spare block, then target block has been erased,
260 // but the target buffer has not been writen in target block from spare block, we need to build
261 // FAULT_TOLERANT_WRITE_LAST_WRITE_DATA GUID hob to hold the FTW last write data.
263 FtwLastWrite
.TargetAddress
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)((INT64
)SpareAreaAddress
+ FtwLastWriteRecord
->RelativeOffset
);
264 FtwLastWrite
.SpareAddress
= SpareAreaAddress
;
265 FtwLastWrite
.Length
= SpareAreaLength
;
268 "FtwPei last write data: TargetAddress - 0x%x SpareAddress - 0x%x Length - 0x%x\n",
269 (UINTN
)FtwLastWrite
.TargetAddress
,
270 (UINTN
)FtwLastWrite
.SpareAddress
,
271 (UINTN
)FtwLastWrite
.Length
273 BuildGuidDataHob (&gEdkiiFaultTolerantWriteGuid
, (VOID
*)&FtwLastWrite
, sizeof (FAULT_TOLERANT_WRITE_LAST_WRITE_DATA
));
277 FtwWorkingBlockHeader
= NULL
;
279 // If the working block workspace is not valid, try to find workspace in the spare block.
281 WorkSpaceInSpareArea
= SpareAreaAddress
+ SpareAreaLength
- WorkSpaceLength
;
282 while (WorkSpaceInSpareArea
>= SpareAreaAddress
) {
283 if (CompareGuid (&gEdkiiWorkingBlockSignatureGuid
, (EFI_GUID
*)(UINTN
)WorkSpaceInSpareArea
)) {
285 // Found the workspace.
287 DEBUG ((DEBUG_INFO
, "FtwPei: workspace in spare block is at 0x%x.\n", (UINTN
)WorkSpaceInSpareArea
));
288 FtwWorkingBlockHeader
= (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
*)(UINTN
)WorkSpaceInSpareArea
;
292 WorkSpaceInSpareArea
= WorkSpaceInSpareArea
- sizeof (EFI_GUID
);
295 if ((FtwWorkingBlockHeader
!= NULL
) && IsValidWorkSpace (FtwWorkingBlockHeader
, WorkSpaceLength
)) {
297 // It was workspace self reclaim, build FAULT_TOLERANT_WRITE_LAST_WRITE_DATA GUID hob for it.
299 FtwLastWrite
.TargetAddress
= WorkSpaceAddress
- (WorkSpaceInSpareArea
- SpareAreaAddress
);
300 FtwLastWrite
.SpareAddress
= SpareAreaAddress
;
301 FtwLastWrite
.Length
= SpareAreaLength
;
304 "FtwPei last write data: TargetAddress - 0x%x SpareAddress - 0x%x Length - 0x%x\n",
305 (UINTN
)FtwLastWrite
.TargetAddress
,
306 (UINTN
)FtwLastWrite
.SpareAddress
,
307 (UINTN
)FtwLastWrite
.Length
309 BuildGuidDataHob (&gEdkiiFaultTolerantWriteGuid
, (VOID
*)&FtwLastWrite
, sizeof (FAULT_TOLERANT_WRITE_LAST_WRITE_DATA
));
314 DEBUG ((DEBUG_ERROR
, "FtwPei: Both working and spare block are invalid.\n"));
319 // Install gEdkiiFaultTolerantWriteGuid PPI to inform the check for FTW last write data has been done.
321 return PeiServicesInstallPpi (&mPpiListVariable
);