]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/FirmwareVolume/FaultTolerantWriteDxe/FtwWorkSpace.c
Enhance FtwReclaimWorkSpace() so that it can be called when the working space header...
[mirror_edk2.git] / MdeModulePkg / Universal / FirmwareVolume / FaultTolerantWriteDxe / FtwWorkSpace.c
1 /** @file
2
3 Internal functions to operate Working Block Space.
4
5 Copyright (c) 2006 - 2008, Intel Corporation
6 All rights reserved. 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 <FtwLite.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 ASSERT (WorkingHeader != NULL);
38 if (WorkingHeader->WorkingBlockValid != FTW_VALID_STATE) {
39 return FALSE;
40 }
41 //
42 // Check signature with gEfiSystemNvDataFvGuid
43 //
44 if (!CompareGuid (&gEfiSystemNvDataFvGuid, &WorkingHeader->Signature)) {
45 return FALSE;
46 }
47 //
48 // Check the CRC of header
49 //
50 CopyMem (
51 &WorkingBlockHeader,
52 WorkingHeader,
53 sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER)
54 );
55
56 //
57 // Filter out the Crc and State fields
58 //
59 SetMem (
60 &WorkingBlockHeader.Crc,
61 sizeof (UINT32),
62 FTW_ERASED_BYTE
63 );
64 WorkingBlockHeader.WorkingBlockValid = FTW_ERASE_POLARITY;
65 WorkingBlockHeader.WorkingBlockInvalid = FTW_ERASE_POLARITY;
66
67 //
68 // Calculate the Crc of woking block header
69 //
70 Status = gBS->CalculateCrc32 (
71 (UINT8 *) &WorkingBlockHeader,
72 sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),
73 &WorkingBlockHeader.Crc
74 );
75 ASSERT_EFI_ERROR (Status);
76
77 if (WorkingBlockHeader.Crc != WorkingHeader->Crc) {
78 DEBUG ((EFI_D_FTW_LITE, "FtwLite: Work block header CRC check error\n"));
79 return FALSE;
80 }
81
82 return TRUE;
83 }
84
85 /**
86 Initialize a work space when there is no work space.
87
88
89 @param WorkingHeader Pointer of working block header
90
91 @retval EFI_SUCCESS The function completed successfully
92 @retval EFI_ABORTED The function could not complete successfully.
93
94 **/
95 EFI_STATUS
96 InitWorkSpaceHeader (
97 IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingHeader
98 )
99 {
100 EFI_STATUS Status;
101
102 ASSERT (WorkingHeader != NULL);
103
104 //
105 // Here using gEfiSystemNvDataFvGuid as the signature.
106 //
107 CopyMem (
108 &WorkingHeader->Signature,
109 &gEfiSystemNvDataFvGuid,
110 sizeof (EFI_GUID)
111 );
112 WorkingHeader->WriteQueueSize = FTW_WORKING_QUEUE_SIZE;
113
114 //
115 // Crc is calculated with all the fields except Crc and STATE
116 //
117 WorkingHeader->WorkingBlockValid = FTW_ERASE_POLARITY;
118 WorkingHeader->WorkingBlockInvalid = FTW_ERASE_POLARITY;
119 SetMem (&WorkingHeader->Crc, sizeof (UINT32), FTW_ERASED_BYTE);
120
121 //
122 // Calculate the CRC value
123 //
124 Status = gBS->CalculateCrc32 (
125 (UINT8 *) WorkingHeader,
126 sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),
127 &WorkingHeader->Crc
128 );
129 ASSERT_EFI_ERROR (Status);
130
131 //
132 // Restore the WorkingBlockValid flag to VALID state
133 //
134 WorkingHeader->WorkingBlockValid = FTW_VALID_STATE;
135 WorkingHeader->WorkingBlockInvalid = FTW_INVALID_STATE;
136
137 return EFI_SUCCESS;
138 }
139
140 /**
141 Update a bit of state on a block device. The location of the bit is
142 calculated by the (Lba, Offset, bit). Here bit is determined by the
143 the name of a certain bit.
144
145
146 @param FvBlock FVB Protocol interface to access SrcBlock and DestBlock
147 @param Lba Lba of a block
148 @param Offset Offset on the Lba
149 @param NewBit New value that will override the old value if it can be change
150
151 @retval EFI_SUCCESS A state bit has been updated successfully
152 @retval Others Access block device error.
153 Notes:
154 Assume all bits of State are inside the same BYTE.
155 @retval EFI_ABORTED Read block fail
156
157 **/
158 EFI_STATUS
159 FtwUpdateFvState (
160 IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,
161 IN EFI_LBA Lba,
162 IN UINTN Offset,
163 IN UINT8 NewBit
164 )
165 {
166 EFI_STATUS Status;
167 UINT8 State;
168 UINTN Length;
169
170 //
171 // Read state from device, assume State is only one byte.
172 //
173 Length = sizeof (UINT8);
174 Status = FvBlock->Read (FvBlock, Lba, Offset, &Length, &State);
175 if (EFI_ERROR (Status)) {
176 return EFI_ABORTED;
177 }
178
179 State ^= FTW_POLARITY_REVERT;
180 State = (UINT8) (State | NewBit);
181 State ^= FTW_POLARITY_REVERT;
182
183 //
184 // Write state back to device
185 //
186 Length = sizeof (UINT8);
187 Status = FvBlock->Write (FvBlock, Lba, Offset, &Length, &State);
188
189 return Status;
190 }
191
192 /**
193 Get the last Write record pointer.
194 The last record is the record whose 'complete' state hasn't been set.
195 After all, this header may be a EMPTY header entry for next Allocate.
196
197
198 @param FtwLiteDevice Private data of this driver
199 @param FtwLastRecord Pointer to retrieve the last write record
200
201 @retval EFI_SUCCESS Get the last write record successfully
202 @retval EFI_ABORTED The FTW work space is damaged
203
204 **/
205 EFI_STATUS
206 FtwGetLastRecord (
207 IN EFI_FTW_LITE_DEVICE *FtwLiteDevice,
208 OUT EFI_FTW_LITE_RECORD **FtwLastRecord
209 )
210 {
211 EFI_FTW_LITE_RECORD *Record;
212
213 *FtwLastRecord = NULL;
214 Record = (EFI_FTW_LITE_RECORD *) (FtwLiteDevice->FtwWorkSpaceHeader + 1);
215 while (Record->WriteCompleted == FTW_VALID_STATE) {
216 //
217 // If Offset exceed the FTW work space boudary, return error.
218 //
219 if ((UINTN) ((UINT8 *) Record - FtwLiteDevice->FtwWorkSpace) > FtwLiteDevice->FtwWorkSpaceSize) {
220 return EFI_ABORTED;
221 }
222
223 Record++;
224 }
225 //
226 // Last write record is found
227 //
228 *FtwLastRecord = Record;
229 return EFI_SUCCESS;
230 }
231
232 /**
233 Read from working block to refresh the work space in memory.
234
235
236 @param FtwLiteDevice Point to private data of FTW driver
237
238 @retval EFI_SUCCESS The function completed successfully
239 @retval EFI_ABORTED The function could not complete successfully.
240
241 **/
242 EFI_STATUS
243 WorkSpaceRefresh (
244 IN EFI_FTW_LITE_DEVICE *FtwLiteDevice
245 )
246 {
247 EFI_STATUS Status;
248 UINTN Length;
249 UINTN Offset;
250 EFI_FTW_LITE_RECORD *Record;
251
252 //
253 // Initialize WorkSpace as FTW_ERASED_BYTE
254 //
255 SetMem (
256 FtwLiteDevice->FtwWorkSpace,
257 FtwLiteDevice->FtwWorkSpaceSize,
258 FTW_ERASED_BYTE
259 );
260
261 //
262 // Read from working block
263 //
264 Length = FtwLiteDevice->FtwWorkSpaceSize;
265 Status = FtwLiteDevice->FtwFvBlock->Read (
266 FtwLiteDevice->FtwFvBlock,
267 FtwLiteDevice->FtwWorkSpaceLba,
268 FtwLiteDevice->FtwWorkSpaceBase,
269 &Length,
270 FtwLiteDevice->FtwWorkSpace
271 );
272 if (EFI_ERROR (Status)) {
273 return EFI_ABORTED;
274 }
275 //
276 // Refresh the FtwLastRecord
277 //
278 Status = FtwGetLastRecord (FtwLiteDevice, &FtwLiteDevice->FtwLastRecord);
279
280 Record = FtwLiteDevice->FtwLastRecord;
281 Offset = (UINTN) (UINT8 *) Record - (UINTN) FtwLiteDevice->FtwWorkSpace;
282
283 //
284 // IF work space has error or Record is out of the workspace limit, THEN
285 // call reclaim.
286 //
287 if (EFI_ERROR (Status) || (Offset + WRITE_TOTAL_SIZE >= FtwLiteDevice->FtwWorkSpaceSize)) {
288 //
289 // reclaim work space in working block.
290 //
291 Status = FtwReclaimWorkSpace (FtwLiteDevice, TRUE);
292 if (EFI_ERROR (Status)) {
293 DEBUG ((EFI_D_FTW_LITE, "FtwLite: Reclaim workspace - %r\n", Status));
294 return EFI_ABORTED;
295 }
296 }
297
298 return EFI_SUCCESS;
299 }
300
301 /**
302 Reclaim the work space on the working block.
303
304
305 @param FtwLiteDevice Point to private data of FTW driver
306
307 @retval EFI_SUCCESS The function completed successfully
308 @retval EFI_OUT_OF_RESOURCES Allocate memory error
309 @retval EFI_ABORTED The function could not complete successfully
310
311 **/
312 EFI_STATUS
313 FtwReclaimWorkSpace (
314 IN EFI_FTW_LITE_DEVICE *FtwLiteDevice,
315 IN BOOLEAN PreserveRecord
316 )
317 {
318 EFI_STATUS Status;
319 UINT8 *TempBuffer;
320 UINTN TempBufferSize;
321 UINT8 *Ptr;
322 UINTN Length;
323 UINTN Index;
324 UINTN SpareBufferSize;
325 UINT8 *SpareBuffer;
326 EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingBlockHeader;
327 EFI_FTW_LITE_RECORD *Record;
328
329 DEBUG ((EFI_D_FTW_LITE, "FtwLite: start to reclaim work space\n"));
330
331 //
332 // Read all original data from working block to a memory buffer
333 //
334 TempBufferSize = FtwLiteDevice->SpareAreaLength;
335 TempBuffer = AllocateZeroPool (TempBufferSize);
336 if (TempBuffer == NULL) {
337 return EFI_OUT_OF_RESOURCES;
338 }
339
340 Ptr = TempBuffer;
341 for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {
342 Length = FtwLiteDevice->SizeOfSpareBlock;
343 Status = FtwLiteDevice->FtwFvBlock->Read (
344 FtwLiteDevice->FtwFvBlock,
345 FtwLiteDevice->FtwWorkBlockLba + Index,
346 0,
347 &Length,
348 Ptr
349 );
350 if (EFI_ERROR (Status)) {
351 FreePool (TempBuffer);
352 return EFI_ABORTED;
353 }
354
355 Ptr += Length;
356 }
357 //
358 // Clean up the workspace, remove all the completed records.
359 //
360 Ptr = TempBuffer +
361 ((UINTN) (FtwLiteDevice->FtwWorkSpaceLba - FtwLiteDevice->FtwWorkBlockLba)) *
362 FtwLiteDevice->SizeOfSpareBlock + FtwLiteDevice->FtwWorkSpaceBase;
363
364 //
365 // Clear the content of buffer that will save the new work space data
366 //
367 SetMem (Ptr, FtwLiteDevice->FtwWorkSpaceSize, FTW_ERASED_BYTE);
368
369 //
370 // Copy EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER to buffer
371 //
372 CopyMem (
373 Ptr,
374 FtwLiteDevice->FtwWorkSpaceHeader,
375 sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER)
376 );
377 if (PreserveRecord) {
378 //
379 // Get the last record
380 //
381 Status = FtwGetLastRecord (FtwLiteDevice, &FtwLiteDevice->FtwLastRecord);
382 Record = FtwLiteDevice->FtwLastRecord;
383 if (!EFI_ERROR (Status) &&
384 Record != NULL &&
385 Record->WriteAllocated == FTW_VALID_STATE &&
386 Record->WriteCompleted != FTW_VALID_STATE) {
387 CopyMem (
388 (UINT8 *) Ptr + sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),
389 Record,
390 WRITE_TOTAL_SIZE
391 );
392 }
393 }
394
395 CopyMem (
396 FtwLiteDevice->FtwWorkSpace,
397 Ptr,
398 FtwLiteDevice->FtwWorkSpaceSize
399 );
400
401 Status = FtwGetLastRecord (FtwLiteDevice, &FtwLiteDevice->FtwLastRecord);
402
403 //
404 // Set the WorkingBlockValid and WorkingBlockInvalid as INVALID
405 //
406 WorkingBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) Ptr;
407 WorkingBlockHeader->WorkingBlockValid = FTW_INVALID_STATE;
408 WorkingBlockHeader->WorkingBlockInvalid = FTW_INVALID_STATE;
409
410 //
411 // Try to keep the content of spare block
412 // Save spare block into a spare backup memory buffer (Sparebuffer)
413 //
414 SpareBufferSize = FtwLiteDevice->SpareAreaLength;
415 SpareBuffer = AllocatePool (SpareBufferSize);
416 if (SpareBuffer == NULL) {
417 FreePool (TempBuffer);
418 return EFI_OUT_OF_RESOURCES;
419 }
420
421 Ptr = SpareBuffer;
422 for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {
423 Length = FtwLiteDevice->SizeOfSpareBlock;
424 Status = FtwLiteDevice->FtwBackupFvb->Read (
425 FtwLiteDevice->FtwBackupFvb,
426 FtwLiteDevice->FtwSpareLba + Index,
427 0,
428 &Length,
429 Ptr
430 );
431 if (EFI_ERROR (Status)) {
432 FreePool (TempBuffer);
433 FreePool (SpareBuffer);
434 return EFI_ABORTED;
435 }
436
437 Ptr += Length;
438 }
439 //
440 // Write the memory buffer to spare block
441 //
442 Status = FtwEraseSpareBlock (FtwLiteDevice);
443 Ptr = TempBuffer;
444 for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {
445 Length = FtwLiteDevice->SizeOfSpareBlock;
446 Status = FtwLiteDevice->FtwBackupFvb->Write (
447 FtwLiteDevice->FtwBackupFvb,
448 FtwLiteDevice->FtwSpareLba + Index,
449 0,
450 &Length,
451 Ptr
452 );
453 if (EFI_ERROR (Status)) {
454 FreePool (TempBuffer);
455 FreePool (SpareBuffer);
456 return EFI_ABORTED;
457 }
458
459 Ptr += Length;
460 }
461 //
462 // Free TempBuffer
463 //
464 FreePool (TempBuffer);
465
466 //
467 // Write the spare block to working block
468 //
469 Status = FlushSpareBlockToWorkingBlock (FtwLiteDevice);
470 if (EFI_ERROR (Status)) {
471 FreePool (SpareBuffer);
472 return Status;
473 }
474 //
475 // Restore spare backup buffer into spare block , if no failure happened during FtwWrite.
476 //
477 Status = FtwEraseSpareBlock (FtwLiteDevice);
478 Ptr = SpareBuffer;
479 for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {
480 Length = FtwLiteDevice->SizeOfSpareBlock;
481 Status = FtwLiteDevice->FtwBackupFvb->Write (
482 FtwLiteDevice->FtwBackupFvb,
483 FtwLiteDevice->FtwSpareLba + Index,
484 0,
485 &Length,
486 Ptr
487 );
488 if (EFI_ERROR (Status)) {
489 FreePool (SpareBuffer);
490 return EFI_ABORTED;
491 }
492
493 Ptr += Length;
494 }
495
496 FreePool (SpareBuffer);
497
498 DEBUG ((EFI_D_FTW_LITE, "FtwLite: reclaim work space success\n"));
499
500 return EFI_SUCCESS;
501 }