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