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