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
);
67 // Last write header is found
69 *FtwWriteHeader
= FtwHeader
;
75 Get the last Write Record pointer. The last write Record is the Record
76 whose DestinationCompleted state hasn't been set. After all, this Record
77 may be a EMPTY record entry for next write.
80 @param FtwWriteHeader Pointer to the write record header
81 @param FtwWriteRecord Pointer to retrieve the last write record
83 @retval EFI_SUCCESS Get the last write record successfully
84 @retval EFI_ABORTED The FTW work space is damaged
88 FtwGetLastWriteRecord (
89 IN EFI_FAULT_TOLERANT_WRITE_HEADER
*FtwWriteHeader
,
90 OUT EFI_FAULT_TOLERANT_WRITE_RECORD
**FtwWriteRecord
94 EFI_FAULT_TOLERANT_WRITE_RECORD
*FtwRecord
;
96 *FtwWriteRecord
= NULL
;
97 FtwRecord
= (EFI_FAULT_TOLERANT_WRITE_RECORD
*) (FtwWriteHeader
+ 1);
100 // Try to find the last write record "that has not completed"
102 for (Index
= 0; Index
< FtwWriteHeader
->NumberOfWrites
; Index
+= 1) {
103 if (FtwRecord
->DestinationComplete
!= FTW_VALID_STATE
) {
105 // The last write record is found
107 *FtwWriteRecord
= FtwRecord
;
113 if (FtwWriteHeader
->PrivateDataSize
!= 0) {
114 FtwRecord
= (EFI_FAULT_TOLERANT_WRITE_RECORD
*) ((UINTN
) FtwRecord
+ (UINTN
) FtwWriteHeader
->PrivateDataSize
);
118 // if Index == NumberOfWrites, then
119 // the last record has been written successfully,
120 // but the Header->Complete Flag has not been set.
121 // also return the last record.
123 if (Index
== FtwWriteHeader
->NumberOfWrites
) {
124 *FtwWriteRecord
= (EFI_FAULT_TOLERANT_WRITE_RECORD
*) ((UINTN
) FtwRecord
- FTW_RECORD_SIZE (FtwWriteHeader
->PrivateDataSize
));
132 Check to see if it is a valid work space.
135 @param WorkingHeader Pointer of working block header
136 @param WorkingLength Working block length
138 @retval TRUE The work space is valid.
139 @retval FALSE The work space is invalid.
144 IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
*WorkingHeader
,
145 IN UINTN WorkingLength
150 if (WorkingHeader
== NULL
) {
154 if ((WorkingHeader
->WorkingBlockValid
!= FTW_VALID_STATE
) || (WorkingHeader
->WorkingBlockInvalid
== FTW_VALID_STATE
)) {
155 DEBUG ((EFI_D_ERROR
, "FtwPei: Work block header valid bit check error\n"));
159 if (WorkingHeader
->WriteQueueSize
!= (WorkingLength
- sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
))) {
160 DEBUG ((EFI_D_ERROR
, "FtwPei: Work block header WriteQueueSize check error\n"));
165 // Check signature with gEdkiiWorkingBlockSignatureGuid
167 if (!CompareGuid (&gEdkiiWorkingBlockSignatureGuid
, &WorkingHeader
->Signature
)) {
168 DEBUG ((EFI_D_ERROR
, "FtwPei: Work block header signature check error, it should be gEdkiiWorkingBlockSignatureGuid\n"));
170 // To be compatible with old signature gEfiSystemNvDataFvGuid.
172 if (!CompareGuid (&gEfiSystemNvDataFvGuid
, &WorkingHeader
->Signature
)) {
175 Data
= *(UINT8
*) (WorkingHeader
+ 1);
177 DEBUG ((EFI_D_ERROR
, "FtwPei: Old format FTW structure can't be handled\n"));
189 Main entry for Fault Tolerant Write PEIM.
191 @param[in] FileHandle Handle of the file being invoked.
192 @param[in] PeiServices Pointer to PEI Services table.
194 @retval EFI_SUCCESS If the interface could be successfully installed
195 @retval Others Returned from PeiServicesInstallPpi()
200 PeimFaultTolerantWriteInitialize (
201 IN EFI_PEI_FILE_HANDLE FileHandle
,
202 IN CONST EFI_PEI_SERVICES
**PeiServices
206 EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
*FtwWorkingBlockHeader
;
207 EFI_FAULT_TOLERANT_WRITE_HEADER
*FtwLastWriteHeader
;
208 EFI_FAULT_TOLERANT_WRITE_RECORD
*FtwLastWriteRecord
;
209 EFI_PHYSICAL_ADDRESS WorkSpaceAddress
;
210 UINTN WorkSpaceLength
;
211 EFI_PHYSICAL_ADDRESS SpareAreaAddress
;
212 UINTN SpareAreaLength
;
213 EFI_PHYSICAL_ADDRESS WorkSpaceInSpareArea
;
214 FAULT_TOLERANT_WRITE_LAST_WRITE_DATA FtwLastWrite
;
216 FtwWorkingBlockHeader
= NULL
;
217 FtwLastWriteHeader
= NULL
;
218 FtwLastWriteRecord
= NULL
;
220 WorkSpaceAddress
= (EFI_PHYSICAL_ADDRESS
) PcdGet64 (PcdFlashNvStorageFtwWorkingBase64
);
221 if (WorkSpaceAddress
== 0) {
222 WorkSpaceAddress
= (EFI_PHYSICAL_ADDRESS
) PcdGet32 (PcdFlashNvStorageFtwWorkingBase
);
224 WorkSpaceLength
= (UINTN
) PcdGet32 (PcdFlashNvStorageFtwWorkingSize
);
226 SpareAreaAddress
= (EFI_PHYSICAL_ADDRESS
) PcdGet64 (PcdFlashNvStorageFtwSpareBase64
);
227 if (SpareAreaAddress
== 0) {
228 SpareAreaAddress
= (EFI_PHYSICAL_ADDRESS
) PcdGet32 (PcdFlashNvStorageFtwSpareBase
);
230 SpareAreaLength
= (UINTN
) PcdGet32 (PcdFlashNvStorageFtwSpareSize
);
233 // The address of FTW working base and spare base must not be 0.
235 ASSERT ((WorkSpaceAddress
!= 0) && (SpareAreaAddress
!= 0));
237 FtwWorkingBlockHeader
= (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
*) (UINTN
) WorkSpaceAddress
;
238 if (IsValidWorkSpace (FtwWorkingBlockHeader
, WorkSpaceLength
)) {
239 Status
= FtwGetLastWriteHeader (
240 FtwWorkingBlockHeader
,
244 if (!EFI_ERROR (Status
)) {
245 Status
= FtwGetLastWriteRecord (
251 if (!EFI_ERROR (Status
)) {
252 ASSERT (FtwLastWriteRecord
!= NULL
);
253 if ((FtwLastWriteRecord
->SpareComplete
== FTW_VALID_STATE
) && (FtwLastWriteRecord
->DestinationComplete
!= FTW_VALID_STATE
)) {
255 // If FTW last write was still in progress with SpareComplete set and DestinationComplete not set.
256 // It means the target buffer has been backed up in spare block, then target block has been erased,
257 // but the target buffer has not been writen in target block from spare block, we need to build
258 // FAULT_TOLERANT_WRITE_LAST_WRITE_DATA GUID hob to hold the FTW last write data.
260 FtwLastWrite
.TargetAddress
= (EFI_PHYSICAL_ADDRESS
) (UINTN
) ((INT64
) SpareAreaAddress
+ FtwLastWriteRecord
->RelativeOffset
);
261 FtwLastWrite
.SpareAddress
= SpareAreaAddress
;
262 FtwLastWrite
.Length
= SpareAreaLength
;
265 "FtwPei last write data: TargetAddress - 0x%x SpareAddress - 0x%x Length - 0x%x\n",
266 (UINTN
) FtwLastWrite
.TargetAddress
,
267 (UINTN
) FtwLastWrite
.SpareAddress
,
268 (UINTN
) FtwLastWrite
.Length
));
269 BuildGuidDataHob (&gEdkiiFaultTolerantWriteGuid
, (VOID
*) &FtwLastWrite
, sizeof (FAULT_TOLERANT_WRITE_LAST_WRITE_DATA
));
273 FtwWorkingBlockHeader
= NULL
;
275 // If the working block workspace is not valid, try to find workspace in the spare block.
277 WorkSpaceInSpareArea
= SpareAreaAddress
+ SpareAreaLength
- WorkSpaceLength
;
278 while (WorkSpaceInSpareArea
>= SpareAreaAddress
) {
279 if (CompareGuid (&gEdkiiWorkingBlockSignatureGuid
, (EFI_GUID
*) (UINTN
) WorkSpaceInSpareArea
)) {
281 // Found the workspace.
283 DEBUG ((EFI_D_INFO
, "FtwPei: workspace in spare block is at 0x%x.\n", (UINTN
) WorkSpaceInSpareArea
));
284 FtwWorkingBlockHeader
= (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
*) (UINTN
) WorkSpaceInSpareArea
;
287 WorkSpaceInSpareArea
= WorkSpaceInSpareArea
- sizeof (EFI_GUID
);
289 if ((FtwWorkingBlockHeader
!= NULL
) && IsValidWorkSpace (FtwWorkingBlockHeader
, WorkSpaceLength
)) {
291 // It was workspace self reclaim, build FAULT_TOLERANT_WRITE_LAST_WRITE_DATA GUID hob for it.
293 FtwLastWrite
.TargetAddress
= WorkSpaceAddress
- (WorkSpaceInSpareArea
- SpareAreaAddress
);
294 FtwLastWrite
.SpareAddress
= SpareAreaAddress
;
295 FtwLastWrite
.Length
= SpareAreaLength
;
298 "FtwPei last write data: TargetAddress - 0x%x SpareAddress - 0x%x Length - 0x%x\n",
299 (UINTN
) FtwLastWrite
.TargetAddress
,
300 (UINTN
) FtwLastWrite
.SpareAddress
,
301 (UINTN
) FtwLastWrite
.Length
));
302 BuildGuidDataHob (&gEdkiiFaultTolerantWriteGuid
, (VOID
*) &FtwLastWrite
, sizeof (FAULT_TOLERANT_WRITE_LAST_WRITE_DATA
));
307 DEBUG ((EFI_D_ERROR
, "FtwPei: Both working and spare block are invalid.\n"));
312 // Install gEdkiiFaultTolerantWriteGuid PPI to inform the check for FTW last write data has been done.
314 return PeiServicesInstallPpi (&mPpiListVariable
);