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