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