]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/FaultTolerantWriteDxe/UpdateWorkingBlock.c
MdeModulePkg: Variable drivers robustly handle crashes during Reclaim().
[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
3e02ebb2 5Copyright (c) 2006 - 2013, 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
05cfd5f2 19EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER mWorkingBlockHeader = {ZERO_GUID, 0, 0, 0, 0, {0, 0, 0}, 0};\r
85e923a5 20\r
05cfd5f2
SZ
21/**\r
22 Initialize a local work space header.\r
85e923a5 23\r
05cfd5f2
SZ
24 Since Signature and WriteQueueSize have been known, Crc can be calculated out,\r
25 then the work space header will be fixed.\r
85e923a5 26**/\r
05cfd5f2
SZ
27VOID\r
28InitializeLocalWorkSpaceHeader (\r
29 VOID\r
85e923a5
LG
30 )\r
31{\r
32 EFI_STATUS Status;\r
85e923a5 33\r
85e923a5 34 //\r
3e02ebb2 35 // Check signature with gEdkiiWorkingBlockSignatureGuid.\r
85e923a5 36 //\r
3e02ebb2 37 if (CompareGuid (&gEdkiiWorkingBlockSignatureGuid, &mWorkingBlockHeader.Signature)) {\r
05cfd5f2
SZ
38 //\r
39 // The local work space header has been initialized.\r
40 //\r
41 return;\r
85e923a5 42 }\r
05cfd5f2
SZ
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
85e923a5 50 //\r
3e02ebb2 51 // Here using gEdkiiWorkingBlockSignatureGuid as the signature.\r
85e923a5
LG
52 //\r
53 CopyMem (\r
05cfd5f2 54 &mWorkingBlockHeader.Signature,\r
3e02ebb2 55 &gEdkiiWorkingBlockSignatureGuid,\r
05cfd5f2 56 sizeof (EFI_GUID)\r
85e923a5 57 );\r
05cfd5f2 58 mWorkingBlockHeader.WriteQueueSize = (UINT64) (PcdGet32 (PcdFlashNvStorageFtwWorkingSize) - sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER));\r
85e923a5
LG
59\r
60 //\r
05cfd5f2 61 // Crc is calculated with all the fields except Crc and STATE, so leave them as FTW_ERASED_BYTE.\r
85e923a5 62 //\r
85e923a5
LG
63\r
64 //\r
65 // Calculate the Crc of woking block header\r
66 //\r
67 Status = gBS->CalculateCrc32 (\r
05cfd5f2 68 &mWorkingBlockHeader,\r
85e923a5 69 sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),\r
05cfd5f2 70 &mWorkingBlockHeader.Crc\r
85e923a5 71 );\r
05cfd5f2
SZ
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
85e923a5
LG
94 return FALSE;\r
95 }\r
96\r
05cfd5f2
SZ
97 if (CompareMem (WorkingHeader, &mWorkingBlockHeader, sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER)) == 0) {\r
98 return TRUE;\r
85e923a5
LG
99 }\r
100\r
05cfd5f2
SZ
101 DEBUG ((EFI_D_ERROR, "Ftw: Work block header check error\n"));\r
102 return FALSE;\r
85e923a5
LG
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
85e923a5
LG
119 if (WorkingHeader == NULL) {\r
120 return EFI_INVALID_PARAMETER;\r
121 }\r
85e923a5 122\r
05cfd5f2 123 CopyMem (WorkingHeader, &mWorkingBlockHeader, sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER));\r
85e923a5
LG
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
d379cbc7 144 UINTN RemainingSpaceSize;\r
85e923a5
LG
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
d379cbc7
SZ
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
85e923a5
LG
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
d26c7e82
SZ
214 if (EFI_ERROR (Status)) {\r
215 return EFI_ABORTED;\r
216 }\r
85e923a5
LG
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
d26c7e82 259 EFI_LBA WorkSpaceLbaOffset;\r
85e923a5
LG
260\r
261 DEBUG ((EFI_D_ERROR, "Ftw: start to reclaim work space\n"));\r
262\r
d26c7e82
SZ
263 WorkSpaceLbaOffset = FtwDevice->FtwWorkSpaceLba - FtwDevice->FtwWorkBlockLba;\r
264\r
85e923a5
LG
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
d26c7e82 295 (UINTN) WorkSpaceLbaOffset * FtwDevice->BlockSize +\r
85e923a5
LG
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
8dc8879a 321 if (!EFI_ERROR (Status) && (Header != NULL) && (Header->Complete != FTW_VALID_STATE) && (Header->HeaderAllocated == FTW_VALID_STATE)) {\r
85e923a5
LG
322 CopyMem (\r
323 Ptr + sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),\r
324 FtwDevice->FtwLastWriteHeader,\r
3e02ebb2 325 FTW_WRITE_TOTAL_SIZE (Header->NumberOfWrites, Header->PrivateDataSize)\r
85e923a5
LG
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
d26c7e82
SZ
342 FtwGetLastWriteRecord (\r
343 FtwDevice->FtwLastWriteHeader,\r
344 &FtwDevice->FtwLastWriteRecord\r
345 );\r
346\r
85e923a5
LG
347 //\r
348 // Set the WorkingBlockValid and WorkingBlockInvalid as INVALID\r
349 //\r
d26c7e82
SZ
350 WorkingBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) (TempBuffer +\r
351 (UINTN) WorkSpaceLbaOffset * FtwDevice->BlockSize +\r
352 FtwDevice->FtwWorkSpaceBase);\r
85e923a5
LG
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
d26c7e82 417 FtwDevice->FtwSpareLba + WorkSpaceLbaOffset,\r
85e923a5
LG
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_ERROR, "Ftw: reclaim work space successfully\n"));\r
477\r
478 return EFI_SUCCESS;\r
479}\r