]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - MdeModulePkg/Universal/FaultTolerantWriteDxe/UpdateWorkingBlock.c
MdeModulePkg DxeCore: Add FVH signature check before VerifyFvHeaderChecksum in FwVol.
[mirror_edk2.git] / MdeModulePkg / Universal / FaultTolerantWriteDxe / UpdateWorkingBlock.c
... / ...
CommitLineData
1/** @file\r
2\r
3 Internal functions to operate Working Block Space.\r
4\r
5Copyright (c) 2006 - 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\r
17#include "FaultTolerantWrite.h"\r
18\r
19EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER mWorkingBlockHeader = {ZERO_GUID, 0, 0, 0, 0, {0, 0, 0}, 0};\r
20\r
21/**\r
22 Initialize a local work space header.\r
23\r
24 Since Signature and WriteQueueSize have been known, Crc can be calculated out,\r
25 then the work space header will be fixed.\r
26**/\r
27VOID\r
28InitializeLocalWorkSpaceHeader (\r
29 VOID\r
30 )\r
31{\r
32 EFI_STATUS Status;\r
33\r
34 //\r
35 // Check signature with gEdkiiWorkingBlockSignatureGuid.\r
36 //\r
37 if (CompareGuid (&gEdkiiWorkingBlockSignatureGuid, &mWorkingBlockHeader.Signature)) {\r
38 //\r
39 // The local work space header has been initialized.\r
40 //\r
41 return;\r
42 }\r
43\r
44 SetMem (\r
45 &mWorkingBlockHeader,\r
46 sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),\r
47 FTW_ERASED_BYTE\r
48 );\r
49\r
50 //\r
51 // Here using gEdkiiWorkingBlockSignatureGuid as the signature.\r
52 //\r
53 CopyMem (\r
54 &mWorkingBlockHeader.Signature,\r
55 &gEdkiiWorkingBlockSignatureGuid,\r
56 sizeof (EFI_GUID)\r
57 );\r
58 mWorkingBlockHeader.WriteQueueSize = (UINT64) (PcdGet32 (PcdFlashNvStorageFtwWorkingSize) - sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER));\r
59\r
60 //\r
61 // Crc is calculated with all the fields except Crc and STATE, so leave them as FTW_ERASED_BYTE.\r
62 //\r
63\r
64 //\r
65 // Calculate the Crc of woking block header\r
66 //\r
67 Status = gBS->CalculateCrc32 (\r
68 &mWorkingBlockHeader,\r
69 sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),\r
70 &mWorkingBlockHeader.Crc\r
71 );\r
72 ASSERT_EFI_ERROR (Status);\r
73\r
74 mWorkingBlockHeader.WorkingBlockValid = FTW_VALID_STATE;\r
75 mWorkingBlockHeader.WorkingBlockInvalid = FTW_INVALID_STATE;\r
76}\r
77\r
78/**\r
79 Check to see if it is a valid work space.\r
80\r
81\r
82 @param WorkingHeader Pointer of working block header\r
83\r
84 @retval TRUE The work space is valid.\r
85 @retval FALSE The work space is invalid.\r
86\r
87**/\r
88BOOLEAN\r
89IsValidWorkSpace (\r
90 IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingHeader\r
91 )\r
92{\r
93 if (WorkingHeader == NULL) {\r
94 return FALSE;\r
95 }\r
96\r
97 if (CompareMem (WorkingHeader, &mWorkingBlockHeader, sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER)) == 0) {\r
98 return TRUE;\r
99 }\r
100\r
101 DEBUG ((EFI_D_ERROR, "Ftw: Work block header check error\n"));\r
102 return FALSE;\r
103}\r
104\r
105/**\r
106 Initialize a work space when there is no work space.\r
107\r
108 @param WorkingHeader Pointer of working block header\r
109\r
110 @retval EFI_SUCCESS The function completed successfully\r
111 @retval EFI_ABORTED The function could not complete successfully.\r
112\r
113**/\r
114EFI_STATUS\r
115InitWorkSpaceHeader (\r
116 IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingHeader\r
117 )\r
118{\r
119 if (WorkingHeader == NULL) {\r
120 return EFI_INVALID_PARAMETER;\r
121 }\r
122\r
123 CopyMem (WorkingHeader, &mWorkingBlockHeader, sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER));\r
124\r
125 return EFI_SUCCESS;\r
126}\r
127\r
128/**\r
129 Read from working block to refresh the work space in memory.\r
130\r
131 @param FtwDevice Point to private data of FTW driver\r
132\r
133 @retval EFI_SUCCESS The function completed successfully\r
134 @retval EFI_ABORTED The function could not complete successfully.\r
135\r
136**/\r
137EFI_STATUS\r
138WorkSpaceRefresh (\r
139 IN EFI_FTW_DEVICE *FtwDevice\r
140 )\r
141{\r
142 EFI_STATUS Status;\r
143 UINTN Length;\r
144 UINTN RemainingSpaceSize;\r
145\r
146 //\r
147 // Initialize WorkSpace as FTW_ERASED_BYTE\r
148 //\r
149 SetMem (\r
150 FtwDevice->FtwWorkSpace,\r
151 FtwDevice->FtwWorkSpaceSize,\r
152 FTW_ERASED_BYTE\r
153 );\r
154\r
155 //\r
156 // Read from working block\r
157 //\r
158 Length = FtwDevice->FtwWorkSpaceSize;\r
159 Status = FtwDevice->FtwFvBlock->Read (\r
160 FtwDevice->FtwFvBlock,\r
161 FtwDevice->FtwWorkSpaceLba,\r
162 FtwDevice->FtwWorkSpaceBase,\r
163 &Length,\r
164 FtwDevice->FtwWorkSpace\r
165 );\r
166 if (EFI_ERROR (Status)) {\r
167 return EFI_ABORTED;\r
168 }\r
169 //\r
170 // Refresh the FtwLastWriteHeader\r
171 //\r
172 Status = FtwGetLastWriteHeader (\r
173 FtwDevice->FtwWorkSpaceHeader,\r
174 FtwDevice->FtwWorkSpaceSize,\r
175 &FtwDevice->FtwLastWriteHeader\r
176 );\r
177 RemainingSpaceSize = FtwDevice->FtwWorkSpaceSize - ((UINTN) FtwDevice->FtwLastWriteHeader - (UINTN) FtwDevice->FtwWorkSpace);\r
178 DEBUG ((EFI_D_INFO, "Ftw: Remaining work space size - %x\n", RemainingSpaceSize));\r
179 //\r
180 // If FtwGetLastWriteHeader() returns error, or the remaining space size is even not enough to contain\r
181 // one EFI_FAULT_TOLERANT_WRITE_HEADER + one EFI_FAULT_TOLERANT_WRITE_RECORD(It will cause that the header\r
182 // pointed by FtwDevice->FtwLastWriteHeader or record pointed by FtwDevice->FtwLastWriteRecord may contain invalid data),\r
183 // it needs to reclaim work space.\r
184 //\r
185 if (EFI_ERROR (Status) || RemainingSpaceSize < sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER) + sizeof (EFI_FAULT_TOLERANT_WRITE_RECORD)) {\r
186 //\r
187 // reclaim work space in working block.\r
188 //\r
189 Status = FtwReclaimWorkSpace (FtwDevice, TRUE);\r
190 if (EFI_ERROR (Status)) {\r
191 DEBUG ((EFI_D_ERROR, "Ftw: Reclaim workspace - %r\n", Status));\r
192 return EFI_ABORTED;\r
193 }\r
194 //\r
195 // Read from working block again\r
196 //\r
197 Length = FtwDevice->FtwWorkSpaceSize;\r
198 Status = FtwDevice->FtwFvBlock->Read (\r
199 FtwDevice->FtwFvBlock,\r
200 FtwDevice->FtwWorkSpaceLba,\r
201 FtwDevice->FtwWorkSpaceBase,\r
202 &Length,\r
203 FtwDevice->FtwWorkSpace\r
204 );\r
205 if (EFI_ERROR (Status)) {\r
206 return EFI_ABORTED;\r
207 }\r
208\r
209 Status = FtwGetLastWriteHeader (\r
210 FtwDevice->FtwWorkSpaceHeader,\r
211 FtwDevice->FtwWorkSpaceSize,\r
212 &FtwDevice->FtwLastWriteHeader\r
213 );\r
214 if (EFI_ERROR (Status)) {\r
215 return EFI_ABORTED;\r
216 }\r
217 }\r
218 //\r
219 // Refresh the FtwLastWriteRecord\r
220 //\r
221 Status = FtwGetLastWriteRecord (\r
222 FtwDevice->FtwLastWriteHeader,\r
223 &FtwDevice->FtwLastWriteRecord\r
224 );\r
225 if (EFI_ERROR (Status)) {\r
226 return EFI_ABORTED;\r
227 }\r
228\r
229 return EFI_SUCCESS;\r
230}\r
231\r
232/**\r
233 Reclaim the work space on the working block.\r
234\r
235 @param FtwDevice Point to private data of FTW driver\r
236 @param PreserveRecord Whether to preserve the working record is needed\r
237\r
238 @retval EFI_SUCCESS The function completed successfully\r
239 @retval EFI_OUT_OF_RESOURCES Allocate memory error\r
240 @retval EFI_ABORTED The function could not complete successfully\r
241\r
242**/\r
243EFI_STATUS\r
244FtwReclaimWorkSpace (\r
245 IN EFI_FTW_DEVICE *FtwDevice,\r
246 IN BOOLEAN PreserveRecord\r
247 )\r
248{\r
249 EFI_STATUS Status;\r
250 UINTN Length;\r
251 EFI_FAULT_TOLERANT_WRITE_HEADER *Header;\r
252 UINT8 *TempBuffer;\r
253 UINTN TempBufferSize;\r
254 UINTN SpareBufferSize;\r
255 UINT8 *SpareBuffer;\r
256 EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingBlockHeader;\r
257 UINTN Index;\r
258 UINT8 *Ptr;\r
259 EFI_LBA WorkSpaceLbaOffset;\r
260\r
261 DEBUG ((EFI_D_INFO, "Ftw: start to reclaim work space\n"));\r
262\r
263 WorkSpaceLbaOffset = FtwDevice->FtwWorkSpaceLba - FtwDevice->FtwWorkBlockLba;\r
264\r
265 //\r
266 // Read all original data from working block to a memory buffer\r
267 //\r
268 TempBufferSize = FtwDevice->SpareAreaLength;\r
269 TempBuffer = AllocateZeroPool (TempBufferSize);\r
270 if (TempBuffer == NULL) {\r
271 return EFI_OUT_OF_RESOURCES;\r
272 }\r
273\r
274 Ptr = TempBuffer;\r
275 for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {\r
276 Length = FtwDevice->BlockSize;\r
277 Status = FtwDevice->FtwFvBlock->Read (\r
278 FtwDevice->FtwFvBlock,\r
279 FtwDevice->FtwWorkBlockLba + Index,\r
280 0,\r
281 &Length,\r
282 Ptr\r
283 );\r
284 if (EFI_ERROR (Status)) {\r
285 FreePool (TempBuffer);\r
286 return EFI_ABORTED;\r
287 }\r
288\r
289 Ptr += Length;\r
290 }\r
291 //\r
292 // Clean up the workspace, remove all the completed records.\r
293 //\r
294 Ptr = TempBuffer +\r
295 (UINTN) WorkSpaceLbaOffset * FtwDevice->BlockSize +\r
296 FtwDevice->FtwWorkSpaceBase;\r
297\r
298 //\r
299 // Clear the content of buffer that will save the new work space data\r
300 //\r
301 SetMem (Ptr, FtwDevice->FtwWorkSpaceSize, FTW_ERASED_BYTE);\r
302\r
303 //\r
304 // Copy EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER to buffer\r
305 //\r
306 CopyMem (\r
307 Ptr,\r
308 FtwDevice->FtwWorkSpaceHeader,\r
309 sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER)\r
310 );\r
311 if (PreserveRecord) {\r
312 //\r
313 // Get the last record following the header,\r
314 //\r
315 Status = FtwGetLastWriteHeader (\r
316 FtwDevice->FtwWorkSpaceHeader,\r
317 FtwDevice->FtwWorkSpaceSize,\r
318 &FtwDevice->FtwLastWriteHeader\r
319 );\r
320 Header = FtwDevice->FtwLastWriteHeader;\r
321 if (!EFI_ERROR (Status) && (Header != NULL) && (Header->Complete != FTW_VALID_STATE) && (Header->HeaderAllocated == FTW_VALID_STATE)) {\r
322 CopyMem (\r
323 Ptr + sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),\r
324 FtwDevice->FtwLastWriteHeader,\r
325 FTW_WRITE_TOTAL_SIZE (Header->NumberOfWrites, Header->PrivateDataSize)\r
326 );\r
327 }\r
328 }\r
329\r
330 CopyMem (\r
331 FtwDevice->FtwWorkSpace,\r
332 Ptr,\r
333 FtwDevice->FtwWorkSpaceSize\r
334 );\r
335\r
336 FtwGetLastWriteHeader (\r
337 FtwDevice->FtwWorkSpaceHeader,\r
338 FtwDevice->FtwWorkSpaceSize,\r
339 &FtwDevice->FtwLastWriteHeader\r
340 );\r
341\r
342 FtwGetLastWriteRecord (\r
343 FtwDevice->FtwLastWriteHeader,\r
344 &FtwDevice->FtwLastWriteRecord\r
345 );\r
346\r
347 //\r
348 // Set the WorkingBlockValid and WorkingBlockInvalid as INVALID\r
349 //\r
350 WorkingBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) (TempBuffer +\r
351 (UINTN) WorkSpaceLbaOffset * FtwDevice->BlockSize +\r
352 FtwDevice->FtwWorkSpaceBase);\r
353 WorkingBlockHeader->WorkingBlockValid = FTW_INVALID_STATE;\r
354 WorkingBlockHeader->WorkingBlockInvalid = FTW_INVALID_STATE;\r
355\r
356 //\r
357 // Try to keep the content of spare block\r
358 // Save spare block into a spare backup memory buffer (Sparebuffer)\r
359 //\r
360 SpareBufferSize = FtwDevice->SpareAreaLength;\r
361 SpareBuffer = AllocatePool (SpareBufferSize);\r
362 if (SpareBuffer == NULL) {\r
363 FreePool (TempBuffer);\r
364 return EFI_OUT_OF_RESOURCES;\r
365 }\r
366\r
367 Ptr = SpareBuffer;\r
368 for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {\r
369 Length = FtwDevice->BlockSize;\r
370 Status = FtwDevice->FtwBackupFvb->Read (\r
371 FtwDevice->FtwBackupFvb,\r
372 FtwDevice->FtwSpareLba + Index,\r
373 0,\r
374 &Length,\r
375 Ptr\r
376 );\r
377 if (EFI_ERROR (Status)) {\r
378 FreePool (TempBuffer);\r
379 FreePool (SpareBuffer);\r
380 return EFI_ABORTED;\r
381 }\r
382\r
383 Ptr += Length;\r
384 }\r
385 //\r
386 // Write the memory buffer to spare block\r
387 //\r
388 Status = FtwEraseSpareBlock (FtwDevice);\r
389 Ptr = TempBuffer;\r
390 for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {\r
391 Length = FtwDevice->BlockSize;\r
392 Status = FtwDevice->FtwBackupFvb->Write (\r
393 FtwDevice->FtwBackupFvb,\r
394 FtwDevice->FtwSpareLba + Index,\r
395 0,\r
396 &Length,\r
397 Ptr\r
398 );\r
399 if (EFI_ERROR (Status)) {\r
400 FreePool (TempBuffer);\r
401 FreePool (SpareBuffer);\r
402 return EFI_ABORTED;\r
403 }\r
404\r
405 Ptr += Length;\r
406 }\r
407 //\r
408 // Free TempBuffer\r
409 //\r
410 FreePool (TempBuffer);\r
411\r
412 //\r
413 // Set the WorkingBlockValid in spare block\r
414 //\r
415 Status = FtwUpdateFvState (\r
416 FtwDevice->FtwBackupFvb,\r
417 FtwDevice->FtwSpareLba + WorkSpaceLbaOffset,\r
418 FtwDevice->FtwWorkSpaceBase + sizeof (EFI_GUID) + sizeof (UINT32),\r
419 WORKING_BLOCK_VALID\r
420 );\r
421 if (EFI_ERROR (Status)) {\r
422 FreePool (SpareBuffer);\r
423 return EFI_ABORTED;\r
424 }\r
425 //\r
426 // Before erase the working block, set WorkingBlockInvalid in working block.\r
427 //\r
428 // Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER,\r
429 // WorkingBlockInvalid);\r
430 //\r
431 Status = FtwUpdateFvState (\r
432 FtwDevice->FtwFvBlock,\r
433 FtwDevice->FtwWorkSpaceLba,\r
434 FtwDevice->FtwWorkSpaceBase + sizeof (EFI_GUID) + sizeof (UINT32),\r
435 WORKING_BLOCK_INVALID\r
436 );\r
437 if (EFI_ERROR (Status)) {\r
438 FreePool (SpareBuffer);\r
439 return EFI_ABORTED;\r
440 }\r
441\r
442 FtwDevice->FtwWorkSpaceHeader->WorkingBlockInvalid = FTW_VALID_STATE;\r
443\r
444 //\r
445 // Write the spare block to working block\r
446 //\r
447 Status = FlushSpareBlockToWorkingBlock (FtwDevice);\r
448 if (EFI_ERROR (Status)) {\r
449 FreePool (SpareBuffer);\r
450 return Status;\r
451 }\r
452 //\r
453 // Restore spare backup buffer into spare block , if no failure happened during FtwWrite.\r
454 //\r
455 Status = FtwEraseSpareBlock (FtwDevice);\r
456 Ptr = SpareBuffer;\r
457 for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {\r
458 Length = FtwDevice->BlockSize;\r
459 Status = FtwDevice->FtwBackupFvb->Write (\r
460 FtwDevice->FtwBackupFvb,\r
461 FtwDevice->FtwSpareLba + Index,\r
462 0,\r
463 &Length,\r
464 Ptr\r
465 );\r
466 if (EFI_ERROR (Status)) {\r
467 FreePool (SpareBuffer);\r
468 return EFI_ABORTED;\r
469 }\r
470\r
471 Ptr += Length;\r
472 }\r
473\r
474 FreePool (SpareBuffer);\r
475\r
476 DEBUG ((EFI_D_INFO, "Ftw: reclaim work space successfully\n"));\r
477\r
478 return EFI_SUCCESS;\r
479}\r