2 This driver installs gEdkiiFaultTolerantWriteGuid PPI to inform
3 the check for FTW last write data has been done.
5 Copyright (c) 2013, Intel Corporation. All rights reserved.<BR>
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
18 #include <Guid/SystemNvDataGuid.h>
19 #include <Guid/FaultTolerantWrite.h>
20 #include <Library/PeiServicesLib.h>
21 #include <Library/PcdLib.h>
22 #include <Library/DebugLib.h>
23 #include <Library/BaseMemoryLib.h>
24 #include <Library/HobLib.h>
26 EFI_PEI_PPI_DESCRIPTOR mPpiListVariable
= {
27 (EFI_PEI_PPI_DESCRIPTOR_PPI
| EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST
),
28 &gEdkiiFaultTolerantWriteGuid
,
33 Get the last Write Header pointer.
34 The last write header is the header whose 'complete' state hasn't been set.
35 After all, this header may be a EMPTY header entry for next Allocate.
38 @param FtwWorkSpaceHeader Pointer of the working block header
39 @param FtwWorkSpaceSize Size of the work space
40 @param FtwWriteHeader Pointer to retrieve the last write header
42 @retval EFI_SUCCESS Get the last write record successfully
43 @retval EFI_ABORTED The FTW work space is damaged
47 FtwGetLastWriteHeader (
48 IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
*FtwWorkSpaceHeader
,
49 IN UINTN FtwWorkSpaceSize
,
50 OUT EFI_FAULT_TOLERANT_WRITE_HEADER
**FtwWriteHeader
54 EFI_FAULT_TOLERANT_WRITE_HEADER
*FtwHeader
;
56 *FtwWriteHeader
= NULL
;
57 FtwHeader
= (EFI_FAULT_TOLERANT_WRITE_HEADER
*) (FtwWorkSpaceHeader
+ 1);
58 Offset
= sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
);
60 while (FtwHeader
->Complete
== FTW_VALID_STATE
) {
61 Offset
+= FTW_WRITE_TOTAL_SIZE (FtwHeader
->NumberOfWrites
, FtwHeader
->PrivateDataSize
);
63 // If Offset exceed the FTW work space boudary, return error.
65 if (Offset
>= FtwWorkSpaceSize
) {
66 *FtwWriteHeader
= FtwHeader
;
70 FtwHeader
= (EFI_FAULT_TOLERANT_WRITE_HEADER
*) ((UINT8
*) FtwWorkSpaceHeader
+ Offset
);
73 // Last write header is found
75 *FtwWriteHeader
= FtwHeader
;
81 Get the last Write Record pointer. The last write Record is the Record
82 whose DestinationCompleted state hasn't been set. After all, this Record
83 may be a EMPTY record entry for next write.
86 @param FtwWriteHeader Pointer to the write record header
87 @param FtwWriteRecord Pointer to retrieve the last write record
89 @retval EFI_SUCCESS Get the last write record successfully
90 @retval EFI_ABORTED The FTW work space is damaged
94 FtwGetLastWriteRecord (
95 IN EFI_FAULT_TOLERANT_WRITE_HEADER
*FtwWriteHeader
,
96 OUT EFI_FAULT_TOLERANT_WRITE_RECORD
**FtwWriteRecord
100 EFI_FAULT_TOLERANT_WRITE_RECORD
*FtwRecord
;
102 *FtwWriteRecord
= NULL
;
103 FtwRecord
= (EFI_FAULT_TOLERANT_WRITE_RECORD
*) (FtwWriteHeader
+ 1);
106 // Try to find the last write record "that has not completed"
108 for (Index
= 0; Index
< FtwWriteHeader
->NumberOfWrites
; Index
+= 1) {
109 if (FtwRecord
->DestinationComplete
!= FTW_VALID_STATE
) {
111 // The last write record is found
113 *FtwWriteRecord
= FtwRecord
;
119 if (FtwWriteHeader
->PrivateDataSize
!= 0) {
120 FtwRecord
= (EFI_FAULT_TOLERANT_WRITE_RECORD
*) ((UINTN
) FtwRecord
+ (UINTN
) FtwWriteHeader
->PrivateDataSize
);
124 // if Index == NumberOfWrites, then
125 // the last record has been written successfully,
126 // but the Header->Complete Flag has not been set.
127 // also return the last record.
129 if (Index
== FtwWriteHeader
->NumberOfWrites
) {
130 *FtwWriteRecord
= (EFI_FAULT_TOLERANT_WRITE_RECORD
*) ((UINTN
) FtwRecord
- FTW_RECORD_SIZE (FtwWriteHeader
->PrivateDataSize
));
138 Check to see if it is a valid work space.
141 @param WorkingHeader Pointer of working block header
142 @param WorkingLength Working block length
144 @retval TRUE The work space is valid.
145 @retval FALSE The work space is invalid.
150 IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
*WorkingHeader
,
151 IN UINTN WorkingLength
156 if (WorkingHeader
== NULL
) {
160 if ((WorkingHeader
->WorkingBlockValid
!= FTW_VALID_STATE
) || (WorkingHeader
->WorkingBlockInvalid
== FTW_VALID_STATE
)) {
161 DEBUG ((EFI_D_ERROR
, "FtwPei: Work block header valid bit check error\n"));
165 if (WorkingHeader
->WriteQueueSize
!= (WorkingLength
- sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
))) {
166 DEBUG ((EFI_D_ERROR
, "FtwPei: Work block header WriteQueueSize check error\n"));
171 // Check signature with gEdkiiWorkingBlockSignatureGuid
173 if (!CompareGuid (&gEdkiiWorkingBlockSignatureGuid
, &WorkingHeader
->Signature
)) {
174 DEBUG ((EFI_D_ERROR
, "FtwPei: Work block header signature check error, it should be gEdkiiWorkingBlockSignatureGuid\n"));
176 // To be compatible with old signature gEfiSystemNvDataFvGuid.
178 if (!CompareGuid (&gEfiSystemNvDataFvGuid
, &WorkingHeader
->Signature
)) {
181 Data
= *(UINT8
*) (WorkingHeader
+ 1);
183 DEBUG ((EFI_D_ERROR
, "FtwPei: Old format FTW structure can't be handled\n"));
195 Main entry for Fault Tolerant Write PEIM.
197 @param[in] FileHandle Handle of the file being invoked.
198 @param[in] PeiServices Pointer to PEI Services table.
200 @retval EFI_SUCCESS If the interface could be successfully installed
201 @retval Others Returned from PeiServicesInstallPpi()
206 PeimFaultTolerantWriteInitialize (
207 IN EFI_PEI_FILE_HANDLE FileHandle
,
208 IN CONST EFI_PEI_SERVICES
**PeiServices
212 EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
*FtwWorkingBlockHeader
;
213 EFI_FAULT_TOLERANT_WRITE_HEADER
*FtwLastWriteHeader
;
214 EFI_FAULT_TOLERANT_WRITE_RECORD
*FtwLastWriteRecord
;
215 EFI_PHYSICAL_ADDRESS WorkSpaceAddress
;
216 UINTN WorkSpaceLength
;
217 EFI_PHYSICAL_ADDRESS SpareAreaAddress
;
218 UINTN SpareAreaLength
;
219 EFI_PHYSICAL_ADDRESS WorkSpaceInSpareArea
;
220 FAULT_TOLERANT_WRITE_LAST_WRITE_DATA FtwLastWrite
;
222 FtwWorkingBlockHeader
= NULL
;
223 FtwLastWriteHeader
= NULL
;
224 FtwLastWriteRecord
= NULL
;
226 WorkSpaceAddress
= (EFI_PHYSICAL_ADDRESS
) PcdGet64 (PcdFlashNvStorageFtwWorkingBase64
);
227 if (WorkSpaceAddress
== 0) {
228 WorkSpaceAddress
= (EFI_PHYSICAL_ADDRESS
) PcdGet32 (PcdFlashNvStorageFtwWorkingBase
);
230 WorkSpaceLength
= (UINTN
) PcdGet32 (PcdFlashNvStorageFtwWorkingSize
);
232 SpareAreaAddress
= (EFI_PHYSICAL_ADDRESS
) PcdGet64 (PcdFlashNvStorageFtwSpareBase64
);
233 if (SpareAreaAddress
== 0) {
234 SpareAreaAddress
= (EFI_PHYSICAL_ADDRESS
) PcdGet32 (PcdFlashNvStorageFtwSpareBase
);
236 SpareAreaLength
= (UINTN
) PcdGet32 (PcdFlashNvStorageFtwSpareSize
);
239 // The address of FTW working base and spare base must not be 0.
241 ASSERT ((WorkSpaceAddress
!= 0) && (SpareAreaAddress
!= 0));
243 FtwWorkingBlockHeader
= (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
*) (UINTN
) WorkSpaceAddress
;
244 if (IsValidWorkSpace (FtwWorkingBlockHeader
, WorkSpaceLength
)) {
245 Status
= FtwGetLastWriteHeader (
246 FtwWorkingBlockHeader
,
250 if (!EFI_ERROR (Status
)) {
251 Status
= FtwGetLastWriteRecord (
257 if (!EFI_ERROR (Status
)) {
258 ASSERT (FtwLastWriteRecord
!= NULL
);
259 if ((FtwLastWriteRecord
->SpareComplete
== FTW_VALID_STATE
) && (FtwLastWriteRecord
->DestinationComplete
!= FTW_VALID_STATE
)) {
261 // If FTW last write was still in progress with SpareComplete set and DestinationComplete not set.
262 // It means the target buffer has been backed up in spare block, then target block has been erased,
263 // but the target buffer has not been writen in target block from spare block, we need to build
264 // FAULT_TOLERANT_WRITE_LAST_WRITE_DATA GUID hob to hold the FTW last write data.
266 FtwLastWrite
.TargetAddress
= (EFI_PHYSICAL_ADDRESS
) (UINTN
) ((INT64
) SpareAreaAddress
+ FtwLastWriteRecord
->RelativeOffset
);
267 FtwLastWrite
.SpareAddress
= SpareAreaAddress
;
268 FtwLastWrite
.Length
= SpareAreaLength
;
271 "FtwPei last write data: TargetAddress - 0x%x SpareAddress - 0x%x Length - 0x%x\n",
272 (UINTN
) FtwLastWrite
.TargetAddress
,
273 (UINTN
) FtwLastWrite
.SpareAddress
,
274 (UINTN
) FtwLastWrite
.Length
));
275 BuildGuidDataHob (&gEdkiiFaultTolerantWriteGuid
, (VOID
*) &FtwLastWrite
, sizeof (FAULT_TOLERANT_WRITE_LAST_WRITE_DATA
));
279 FtwWorkingBlockHeader
= NULL
;
281 // If the working block workspace is not valid, try to find workspace in the spare block.
283 WorkSpaceInSpareArea
= SpareAreaAddress
+ SpareAreaLength
- WorkSpaceLength
;
284 while (WorkSpaceInSpareArea
>= SpareAreaAddress
) {
285 if (CompareGuid (&gEdkiiWorkingBlockSignatureGuid
, (EFI_GUID
*) (UINTN
) WorkSpaceInSpareArea
)) {
287 // Found the workspace.
289 DEBUG ((EFI_D_INFO
, "FtwPei: workspace in spare block is at 0x%x.\n", (UINTN
) WorkSpaceInSpareArea
));
290 FtwWorkingBlockHeader
= (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER
*) (UINTN
) WorkSpaceInSpareArea
;
293 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
));
308 BuildGuidDataHob (&gEdkiiFaultTolerantWriteGuid
, (VOID
*) &FtwLastWrite
, sizeof (FAULT_TOLERANT_WRITE_LAST_WRITE_DATA
));
313 DEBUG ((EFI_D_ERROR
, "FtwPei: Both working and spare block are invalid.\n"));
318 // Install gEdkiiFaultTolerantWriteGuid PPI to inform the check for FTW last write data has been done.
320 return PeiServicesInstallPpi (&mPpiListVariable
);