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