]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/FaultTolerantWritePei/FaultTolerantWritePei.c
MdeModulePkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / MdeModulePkg / Universal / FaultTolerantWritePei / FaultTolerantWritePei.c
1 /** @file
2 This driver installs gEdkiiFaultTolerantWriteGuid PPI to inform
3 the check for FTW last write data has been done.
4
5 Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.<BR>
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7
8 **/
9
10 #include <PiPei.h>
11
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>
19
20 EFI_PEI_PPI_DESCRIPTOR mPpiListVariable = {
21 (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
22 &gEdkiiFaultTolerantWriteGuid,
23 NULL
24 };
25
26 /**
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.
30
31
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
35
36 @retval EFI_SUCCESS Get the last write record successfully
37 @retval EFI_ABORTED The FTW work space is damaged
38
39 **/
40 EFI_STATUS
41 FtwGetLastWriteHeader (
42 IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *FtwWorkSpaceHeader,
43 IN UINTN FtwWorkSpaceSize,
44 OUT EFI_FAULT_TOLERANT_WRITE_HEADER **FtwWriteHeader
45 )
46 {
47 UINTN Offset;
48 EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader;
49
50 *FtwWriteHeader = NULL;
51 FtwHeader = (EFI_FAULT_TOLERANT_WRITE_HEADER *) (FtwWorkSpaceHeader + 1);
52 Offset = sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER);
53
54 while (FtwHeader->Complete == FTW_VALID_STATE) {
55 Offset += FTW_WRITE_TOTAL_SIZE (FtwHeader->NumberOfWrites, FtwHeader->PrivateDataSize);
56 //
57 // If Offset exceed the FTW work space boudary, return error.
58 //
59 if (Offset >= FtwWorkSpaceSize) {
60 *FtwWriteHeader = FtwHeader;
61 return EFI_ABORTED;
62 }
63
64 FtwHeader = (EFI_FAULT_TOLERANT_WRITE_HEADER *) ((UINT8 *) FtwWorkSpaceHeader + Offset);
65 }
66 //
67 // Last write header is found
68 //
69 *FtwWriteHeader = FtwHeader;
70
71 return EFI_SUCCESS;
72 }
73
74 /**
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.
78
79
80 @param FtwWriteHeader Pointer to the write record header
81 @param FtwWriteRecord Pointer to retrieve the last write record
82
83 @retval EFI_SUCCESS Get the last write record successfully
84 @retval EFI_ABORTED The FTW work space is damaged
85
86 **/
87 EFI_STATUS
88 FtwGetLastWriteRecord (
89 IN EFI_FAULT_TOLERANT_WRITE_HEADER *FtwWriteHeader,
90 OUT EFI_FAULT_TOLERANT_WRITE_RECORD **FtwWriteRecord
91 )
92 {
93 UINTN Index;
94 EFI_FAULT_TOLERANT_WRITE_RECORD *FtwRecord;
95
96 *FtwWriteRecord = NULL;
97 FtwRecord = (EFI_FAULT_TOLERANT_WRITE_RECORD *) (FtwWriteHeader + 1);
98
99 //
100 // Try to find the last write record "that has not completed"
101 //
102 for (Index = 0; Index < FtwWriteHeader->NumberOfWrites; Index += 1) {
103 if (FtwRecord->DestinationComplete != FTW_VALID_STATE) {
104 //
105 // The last write record is found
106 //
107 *FtwWriteRecord = FtwRecord;
108 return EFI_SUCCESS;
109 }
110
111 FtwRecord++;
112
113 if (FtwWriteHeader->PrivateDataSize != 0) {
114 FtwRecord = (EFI_FAULT_TOLERANT_WRITE_RECORD *) ((UINTN) FtwRecord + (UINTN) FtwWriteHeader->PrivateDataSize);
115 }
116 }
117 //
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.
122 //
123 if (Index == FtwWriteHeader->NumberOfWrites) {
124 *FtwWriteRecord = (EFI_FAULT_TOLERANT_WRITE_RECORD *) ((UINTN) FtwRecord - FTW_RECORD_SIZE (FtwWriteHeader->PrivateDataSize));
125 return EFI_SUCCESS;
126 }
127
128 return EFI_ABORTED;
129 }
130
131 /**
132 Check to see if it is a valid work space.
133
134
135 @param WorkingHeader Pointer of working block header
136 @param WorkingLength Working block length
137
138 @retval TRUE The work space is valid.
139 @retval FALSE The work space is invalid.
140
141 **/
142 BOOLEAN
143 IsValidWorkSpace (
144 IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingHeader,
145 IN UINTN WorkingLength
146 )
147 {
148 UINT8 Data;
149
150 if (WorkingHeader == NULL) {
151 return FALSE;
152 }
153
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"));
156 return FALSE;
157 }
158
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"));
161 return FALSE;
162 }
163
164 //
165 // Check signature with gEdkiiWorkingBlockSignatureGuid
166 //
167 if (!CompareGuid (&gEdkiiWorkingBlockSignatureGuid, &WorkingHeader->Signature)) {
168 DEBUG ((EFI_D_ERROR, "FtwPei: Work block header signature check error, it should be gEdkiiWorkingBlockSignatureGuid\n"));
169 //
170 // To be compatible with old signature gEfiSystemNvDataFvGuid.
171 //
172 if (!CompareGuid (&gEfiSystemNvDataFvGuid, &WorkingHeader->Signature)) {
173 return FALSE;
174 } else {
175 Data = *(UINT8 *) (WorkingHeader + 1);
176 if (Data != 0xff) {
177 DEBUG ((EFI_D_ERROR, "FtwPei: Old format FTW structure can't be handled\n"));
178 ASSERT (FALSE);
179 return FALSE;
180 }
181 }
182 }
183
184 return TRUE;
185
186 }
187
188 /**
189 Main entry for Fault Tolerant Write PEIM.
190
191 @param[in] FileHandle Handle of the file being invoked.
192 @param[in] PeiServices Pointer to PEI Services table.
193
194 @retval EFI_SUCCESS If the interface could be successfully installed
195 @retval Others Returned from PeiServicesInstallPpi()
196
197 **/
198 EFI_STATUS
199 EFIAPI
200 PeimFaultTolerantWriteInitialize (
201 IN EFI_PEI_FILE_HANDLE FileHandle,
202 IN CONST EFI_PEI_SERVICES **PeiServices
203 )
204 {
205 EFI_STATUS Status;
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;
215
216 FtwWorkingBlockHeader = NULL;
217 FtwLastWriteHeader = NULL;
218 FtwLastWriteRecord = NULL;
219
220 WorkSpaceAddress = (EFI_PHYSICAL_ADDRESS) PcdGet64 (PcdFlashNvStorageFtwWorkingBase64);
221 if (WorkSpaceAddress == 0) {
222 WorkSpaceAddress = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageFtwWorkingBase);
223 }
224 WorkSpaceLength = (UINTN) PcdGet32 (PcdFlashNvStorageFtwWorkingSize);
225
226 SpareAreaAddress = (EFI_PHYSICAL_ADDRESS) PcdGet64 (PcdFlashNvStorageFtwSpareBase64);
227 if (SpareAreaAddress == 0) {
228 SpareAreaAddress = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageFtwSpareBase);
229 }
230 SpareAreaLength = (UINTN) PcdGet32 (PcdFlashNvStorageFtwSpareSize);
231
232 //
233 // The address of FTW working base and spare base must not be 0.
234 //
235 ASSERT ((WorkSpaceAddress != 0) && (SpareAreaAddress != 0));
236
237 FtwWorkingBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) (UINTN) WorkSpaceAddress;
238 if (IsValidWorkSpace (FtwWorkingBlockHeader, WorkSpaceLength)) {
239 Status = FtwGetLastWriteHeader (
240 FtwWorkingBlockHeader,
241 WorkSpaceLength,
242 &FtwLastWriteHeader
243 );
244 if (!EFI_ERROR (Status)) {
245 Status = FtwGetLastWriteRecord (
246 FtwLastWriteHeader,
247 &FtwLastWriteRecord
248 );
249 }
250
251 if (!EFI_ERROR (Status)) {
252 ASSERT (FtwLastWriteRecord != NULL);
253 if ((FtwLastWriteRecord->SpareComplete == FTW_VALID_STATE) && (FtwLastWriteRecord->DestinationComplete != FTW_VALID_STATE)) {
254 //
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.
259 //
260 FtwLastWrite.TargetAddress = (EFI_PHYSICAL_ADDRESS) (UINTN) ((INT64) SpareAreaAddress + FtwLastWriteRecord->RelativeOffset);
261 FtwLastWrite.SpareAddress = SpareAreaAddress;
262 FtwLastWrite.Length = SpareAreaLength;
263 DEBUG ((
264 EFI_D_INFO,
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));
270 }
271 }
272 } else {
273 FtwWorkingBlockHeader = NULL;
274 //
275 // If the working block workspace is not valid, try to find workspace in the spare block.
276 //
277 WorkSpaceInSpareArea = SpareAreaAddress + SpareAreaLength - WorkSpaceLength;
278 while (WorkSpaceInSpareArea >= SpareAreaAddress) {
279 if (CompareGuid (&gEdkiiWorkingBlockSignatureGuid, (EFI_GUID *) (UINTN) WorkSpaceInSpareArea)) {
280 //
281 // Found the workspace.
282 //
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;
285 break;
286 }
287 WorkSpaceInSpareArea = WorkSpaceInSpareArea - sizeof (EFI_GUID);
288 }
289 if ((FtwWorkingBlockHeader != NULL) && IsValidWorkSpace (FtwWorkingBlockHeader, WorkSpaceLength)) {
290 //
291 // It was workspace self reclaim, build FAULT_TOLERANT_WRITE_LAST_WRITE_DATA GUID hob for it.
292 //
293 FtwLastWrite.TargetAddress = WorkSpaceAddress - (WorkSpaceInSpareArea - SpareAreaAddress);
294 FtwLastWrite.SpareAddress = SpareAreaAddress;
295 FtwLastWrite.Length = SpareAreaLength;
296 DEBUG ((
297 EFI_D_INFO,
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));
303 } else {
304 //
305 // Both are invalid.
306 //
307 DEBUG ((EFI_D_ERROR, "FtwPei: Both working and spare block are invalid.\n"));
308 }
309 }
310
311 //
312 // Install gEdkiiFaultTolerantWriteGuid PPI to inform the check for FTW last write data has been done.
313 //
314 return PeiServicesInstallPpi (&mPpiListVariable);
315 }