]>
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 | |
5 | Copyright (c) 2013, Intel Corporation. All rights reserved.<BR>\r | |
6 | This program and the accompanying materials \r | |
7 | are licensed and made available under the terms and conditions of the BSD License \r | |
8 | which accompanies this distribution. The full text of the license may be found at \r | |
9 | http://opensource.org/licenses/bsd-license.php \r | |
10 | \r | |
11 | THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, \r | |
12 | WITHOUT 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 | |
26 | EFI_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 | |
46 | EFI_STATUS\r | |
47 | FtwGetLastWriteHeader (\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 | |
93 | EFI_STATUS\r | |
94 | FtwGetLastWriteRecord (\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 | |
148 | BOOLEAN\r | |
149 | IsValidWorkSpace (\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 | |
204 | EFI_STATUS\r | |
205 | EFIAPI\r | |
206 | PeimFaultTolerantWriteInitialize (\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 |