Initial import.
[mirror_edk2.git] / EdkModulePkg / Universal / FirmwareVolume / FaultTolerantWriteLite / Dxe / FtwWorkSpace.c
1 /*++
2
3 Copyright (c) 2006, Intel Corporation
4 All rights reserved. This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
8
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
11
12 Module Name:
13
14 FtwWorkSpace.c
15
16 Abstract:
17
18 Revision History
19
20 --*/
21
22
23 #include <FtwLite.h>
24
25 BOOLEAN
26 IsValidWorkSpace (
27 IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingHeader
28 )
29 /*++
30
31 Routine Description:
32 Check to see if it is a valid work space.
33
34 Arguments:
35 WorkingHeader - Pointer of working block header
36
37 Returns:
38 EFI_SUCCESS - The function completed successfully
39 EFI_ABORTED - The function could not complete successfully.
40
41 --*/
42 {
43 EFI_STATUS Status;
44 EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER WorkingBlockHeader;
45
46 ASSERT (WorkingHeader != NULL);
47 if (WorkingHeader->WorkingBlockValid != FTW_VALID_STATE) {
48 return FALSE;
49 }
50 //
51 // Check signature with gEfiSystemNvDataFvGuid
52 //
53 if (!CompareGuid (&gEfiSystemNvDataFvGuid, &WorkingHeader->Signature)) {
54 return FALSE;
55 }
56 //
57 // Check the CRC of header
58 //
59 CopyMem (
60 &WorkingBlockHeader,
61 WorkingHeader,
62 sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER)
63 );
64
65 //
66 // Filter out the Crc and State fields
67 //
68 SetMem (
69 &WorkingBlockHeader.Crc,
70 sizeof (UINT32),
71 FTW_ERASED_BYTE
72 );
73 WorkingBlockHeader.WorkingBlockValid = FTW_ERASE_POLARITY;
74 WorkingBlockHeader.WorkingBlockInvalid = FTW_ERASE_POLARITY;
75
76 //
77 // Calculate the Crc of woking block header
78 //
79 Status = gBS->CalculateCrc32 (
80 (UINT8 *) &WorkingBlockHeader,
81 sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),
82 &WorkingBlockHeader.Crc
83 );
84 ASSERT_EFI_ERROR (Status);
85
86 if (WorkingBlockHeader.Crc != WorkingHeader->Crc) {
87 DEBUG ((EFI_D_FTW_LITE, "FtwLite: Work block header CRC check error\n"));
88 return FALSE;
89 }
90
91 return TRUE;
92 }
93
94 EFI_STATUS
95 InitWorkSpaceHeader (
96 IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingHeader
97 )
98 /*++
99
100 Routine Description:
101 Initialize a work space when there is no work space.
102
103 Arguments:
104 WorkingHeader - Pointer of working block header
105
106 Returns:
107 EFI_SUCCESS - The function completed successfully
108 EFI_ABORTED - The function could not complete successfully.
109
110 --*/
111 {
112 EFI_STATUS Status;
113
114 ASSERT (WorkingHeader != NULL);
115
116 //
117 // Here using gEfiSystemNvDataFvGuid as the signature.
118 //
119 CopyMem (
120 &WorkingHeader->Signature,
121 &gEfiSystemNvDataFvGuid,
122 sizeof (EFI_GUID)
123 );
124 WorkingHeader->WriteQueueSize = FTW_WORKING_QUEUE_SIZE;
125
126 //
127 // Crc is calculated with all the fields except Crc and STATE
128 //
129 WorkingHeader->WorkingBlockValid = FTW_ERASE_POLARITY;
130 WorkingHeader->WorkingBlockInvalid = FTW_ERASE_POLARITY;
131 SetMem (&WorkingHeader->Crc, sizeof (UINT32), FTW_ERASED_BYTE);
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 ASSERT_EFI_ERROR (Status);
142
143 //
144 // Restore the WorkingBlockValid flag to VALID state
145 //
146 WorkingHeader->WorkingBlockValid = FTW_VALID_STATE;
147 WorkingHeader->WorkingBlockInvalid = FTW_INVALID_STATE;
148
149 return EFI_SUCCESS;
150 }
151
152 EFI_STATUS
153 FtwUpdateFvState (
154 IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,
155 IN EFI_LBA Lba,
156 IN UINTN Offset,
157 IN UINT8 NewBit
158 )
159 /*++
160
161 Routine Description:
162 Update a bit of state on a block device. The location of the bit is
163 calculated by the (Lba, Offset, bit). Here bit is determined by the
164 the name of a certain bit.
165
166 Arguments:
167 FvBlock - FVB Protocol interface to access SrcBlock and DestBlock
168 Lba - Lba of a block
169 Offset - Offset on the Lba
170 NewBit - New value that will override the old value if it can be change
171
172 Returns:
173 EFI_SUCCESS - A state bit has been updated successfully
174 Others - Access block device error.
175
176 Notes:
177 Assume all bits of State are inside the same BYTE.
178
179 EFI_ABORTED - Read block fail
180 --*/
181 {
182 EFI_STATUS Status;
183 UINT8 State;
184 UINTN Length;
185
186 //
187 // Read state from device, assume State is only one byte.
188 //
189 Length = sizeof (UINT8);
190 Status = FvBlock->Read (FvBlock, Lba, Offset, &Length, &State);
191 if (EFI_ERROR (Status)) {
192 return EFI_ABORTED;
193 }
194
195 State ^= FTW_POLARITY_REVERT;
196 State |= NewBit;
197 State ^= FTW_POLARITY_REVERT;
198
199 //
200 // Write state back to device
201 //
202 Length = sizeof (UINT8);
203 Status = FvBlock->Write (FvBlock, Lba, Offset, &Length, &State);
204
205 return Status;
206 }
207
208 EFI_STATUS
209 FtwGetLastRecord (
210 IN EFI_FTW_LITE_DEVICE *FtwLiteDevice,
211 OUT EFI_FTW_LITE_RECORD **FtwLastRecord
212 )
213 /*++
214
215 Routine Description:
216 Get the last Write record pointer.
217 The last record is the record whose 'complete' state hasn't been set.
218 After all, this header may be a EMPTY header entry for next Allocate.
219
220 Arguments:
221 FtwLiteDevice - Private data of this driver
222 FtwLastRecord - Pointer to retrieve the last write record
223
224 Returns:
225 EFI_SUCCESS - Get the last write record successfully
226 EFI_ABORTED - The FTW work space is damaged
227
228 --*/
229 {
230 EFI_FTW_LITE_RECORD *Record;
231
232 Record = (EFI_FTW_LITE_RECORD *) (FtwLiteDevice->FtwWorkSpaceHeader + 1);
233 while (Record->WriteCompleted == FTW_VALID_STATE) {
234 //
235 // If Offset exceed the FTW work space boudary, return error.
236 //
237 if ((UINTN) ((UINT8 *) Record - FtwLiteDevice->FtwWorkSpace) > FtwLiteDevice->FtwWorkSpaceSize) {
238 return EFI_ABORTED;
239 }
240
241 Record++;
242 }
243 //
244 // Last write record is found
245 //
246 *FtwLastRecord = Record;
247 return EFI_SUCCESS;
248 }
249
250 EFI_STATUS
251 WorkSpaceRefresh (
252 IN EFI_FTW_LITE_DEVICE *FtwLiteDevice
253 )
254 /*++
255
256 Routine Description:
257 Read from working block to refresh the work space in memory.
258
259 Arguments:
260 FtwLiteDevice - Point to private data of FTW driver
261
262 Returns:
263 EFI_SUCCESS - The function completed successfully
264 EFI_ABORTED - The function could not complete successfully.
265
266 --*/
267 {
268 EFI_STATUS Status;
269 UINTN Length;
270 UINTN Offset;
271 EFI_FTW_LITE_RECORD *Record;
272
273 //
274 // Initialize WorkSpace as FTW_ERASED_BYTE
275 //
276 SetMem (
277 FtwLiteDevice->FtwWorkSpace,
278 FtwLiteDevice->FtwWorkSpaceSize,
279 FTW_ERASED_BYTE
280 );
281
282 //
283 // Read from working block
284 //
285 Length = FtwLiteDevice->FtwWorkSpaceSize;
286 Status = FtwLiteDevice->FtwFvBlock->Read (
287 FtwLiteDevice->FtwFvBlock,
288 FtwLiteDevice->FtwWorkSpaceLba,
289 FtwLiteDevice->FtwWorkSpaceBase,
290 &Length,
291 FtwLiteDevice->FtwWorkSpace
292 );
293 if (EFI_ERROR (Status)) {
294 return EFI_ABORTED;
295 }
296 //
297 // Refresh the FtwLastRecord
298 //
299 Status = FtwGetLastRecord (FtwLiteDevice, &FtwLiteDevice->FtwLastRecord);
300
301 Record = FtwLiteDevice->FtwLastRecord;
302 Offset = (UINTN) (UINT8 *) Record - (UINTN) FtwLiteDevice->FtwWorkSpace;
303
304 //
305 // IF work space has error or Record is out of the workspace limit, THEN
306 // call reclaim.
307 //
308 if (EFI_ERROR (Status) || (Offset + WRITE_TOTAL_SIZE >= FtwLiteDevice->FtwWorkSpaceSize)) {
309 //
310 // reclaim work space in working block.
311 //
312 Status = FtwReclaimWorkSpace (FtwLiteDevice);
313 if (EFI_ERROR (Status)) {
314 DEBUG ((EFI_D_FTW_LITE, "FtwLite: Reclaim workspace - %r\n", Status));
315 return EFI_ABORTED;
316 }
317 }
318
319 return EFI_SUCCESS;
320 }
321
322 EFI_STATUS
323 CleanupWorkSpace (
324 IN EFI_FTW_LITE_DEVICE *FtwLiteDevice,
325 IN OUT UINT8 *FtwSpaceBuffer,
326 IN UINTN BufferSize
327 )
328 /*++
329
330 Routine Description:
331 Reclaim the work space. Get rid of all the completed write records
332 and write records in the Fault Tolerant work space.
333
334 Arguments:
335 FtwLiteDevice - Point to private data of FTW driver
336 FtwSpaceBuffer - Buffer to contain the reclaimed clean data
337 BufferSize - Size of the FtwSpaceBuffer
338
339 Returns:
340 EFI_SUCCESS - The function completed successfully
341 EFI_BUFFER_TOO_SMALL - The FtwSpaceBuffer is too small
342 EFI_ABORTED - The function could not complete successfully.
343
344 --*/
345 {
346 UINTN Length;
347 EFI_FTW_LITE_RECORD *Record;
348
349 //
350 // To check if the buffer is large enough
351 //
352 Length = FtwLiteDevice->FtwWorkSpaceSize;
353 if (BufferSize < Length) {
354 return EFI_BUFFER_TOO_SMALL;
355 }
356 //
357 // Clear the content of buffer that will save the new work space data
358 //
359 SetMem (FtwSpaceBuffer, Length, FTW_ERASED_BYTE);
360
361 //
362 // Copy EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER to buffer
363 //
364 CopyMem (
365 FtwSpaceBuffer,
366 FtwLiteDevice->FtwWorkSpaceHeader,
367 sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER)
368 );
369
370 //
371 // Get the last record
372 //
373 Record = FtwLiteDevice->FtwLastRecord;
374 if ((Record != NULL) && (Record->WriteAllocated == FTW_VALID_STATE) && (Record->WriteCompleted != FTW_VALID_STATE)) {
375 CopyMem (
376 (UINT8 *) FtwSpaceBuffer + sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),
377 Record,
378 WRITE_TOTAL_SIZE
379 );
380 }
381
382 return EFI_SUCCESS;
383 }
384
385 EFI_STATUS
386 FtwReclaimWorkSpace (
387 IN EFI_FTW_LITE_DEVICE *FtwLiteDevice
388 )
389 /*++
390
391 Routine Description:
392 Reclaim the work space on the working block.
393
394 Arguments:
395 FtwLiteDevice - Point to private data of FTW driver
396
397 Returns:
398 EFI_SUCCESS - The function completed successfully
399 EFI_OUT_OF_RESOURCES - Allocate memory error
400 EFI_ABORTED - The function could not complete successfully
401
402 --*/
403 {
404 EFI_STATUS Status;
405 UINT8 *TempBuffer;
406 UINTN TempBufferSize;
407 UINT8 *Ptr;
408 UINTN Length;
409 UINTN Index;
410 UINTN SpareBufferSize;
411 UINT8 *SpareBuffer;
412 EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingBlockHeader;
413
414 DEBUG ((EFI_D_FTW_LITE, "FtwLite: start to reclaim work space\n"));
415
416 //
417 // Read all original data from working block to a memory buffer
418 //
419 TempBufferSize = FtwLiteDevice->SpareAreaLength;
420 Status = gBS->AllocatePool (
421 EfiBootServicesData,
422 TempBufferSize,
423 (VOID **) &TempBuffer
424 );
425 if (EFI_ERROR (Status)) {
426 return EFI_OUT_OF_RESOURCES;
427 }
428
429 ZeroMem (TempBuffer, TempBufferSize);
430
431 Ptr = TempBuffer;
432 for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {
433 Length = FtwLiteDevice->SizeOfSpareBlock;
434 Status = FtwLiteDevice->FtwFvBlock->Read (
435 FtwLiteDevice->FtwFvBlock,
436 FtwLiteDevice->FtwWorkBlockLba + Index,
437 0,
438 &Length,
439 Ptr
440 );
441 if (EFI_ERROR (Status)) {
442 gBS->FreePool (TempBuffer);
443 return EFI_ABORTED;
444 }
445
446 Ptr += Length;
447 }
448 //
449 // Clean up the workspace, remove all the completed records.
450 //
451 Ptr = TempBuffer +
452 ((UINTN) (FtwLiteDevice->FtwWorkSpaceLba - FtwLiteDevice->FtwWorkBlockLba)) *
453 FtwLiteDevice->SizeOfSpareBlock +
454 FtwLiteDevice->FtwWorkSpaceBase;
455 Status = CleanupWorkSpace (
456 FtwLiteDevice,
457 Ptr,
458 FtwLiteDevice->FtwWorkSpaceSize
459 );
460
461 CopyMem (
462 FtwLiteDevice->FtwWorkSpace,
463 Ptr,
464 FtwLiteDevice->FtwWorkSpaceSize
465 );
466
467 Status = FtwGetLastRecord (FtwLiteDevice, &FtwLiteDevice->FtwLastRecord);
468
469 //
470 // Set the WorkingBlockValid and WorkingBlockInvalid as INVALID
471 //
472 WorkingBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) Ptr;
473 WorkingBlockHeader->WorkingBlockValid = FTW_INVALID_STATE;
474 WorkingBlockHeader->WorkingBlockInvalid = FTW_INVALID_STATE;
475
476 //
477 // Try to keep the content of spare block
478 // Save spare block into a spare backup memory buffer (Sparebuffer)
479 //
480 SpareBufferSize = FtwLiteDevice->SpareAreaLength;
481 SpareBuffer = AllocatePool (SpareBufferSize);
482 if (SpareBuffer == NULL) {
483 gBS->FreePool (TempBuffer);
484 return EFI_OUT_OF_RESOURCES;
485 }
486
487 Ptr = SpareBuffer;
488 for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {
489 Length = FtwLiteDevice->SizeOfSpareBlock;
490 Status = FtwLiteDevice->FtwBackupFvb->Read (
491 FtwLiteDevice->FtwBackupFvb,
492 FtwLiteDevice->FtwSpareLba + Index,
493 0,
494 &Length,
495 Ptr
496 );
497 if (EFI_ERROR (Status)) {
498 gBS->FreePool (TempBuffer);
499 gBS->FreePool (SpareBuffer);
500 return EFI_ABORTED;
501 }
502
503 Ptr += Length;
504 }
505 //
506 // Write the memory buffer to spare block
507 //
508 Status = FtwEraseSpareBlock (FtwLiteDevice);
509 Ptr = TempBuffer;
510 for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {
511 Length = FtwLiteDevice->SizeOfSpareBlock;
512 Status = FtwLiteDevice->FtwBackupFvb->Write (
513 FtwLiteDevice->FtwBackupFvb,
514 FtwLiteDevice->FtwSpareLba + Index,
515 0,
516 &Length,
517 Ptr
518 );
519 if (EFI_ERROR (Status)) {
520 gBS->FreePool (TempBuffer);
521 gBS->FreePool (SpareBuffer);
522 return EFI_ABORTED;
523 }
524
525 Ptr += Length;
526 }
527 //
528 // Free TempBuffer
529 //
530 gBS->FreePool (TempBuffer);
531
532 //
533 // Write the spare block to working block
534 //
535 Status = FlushSpareBlockToWorkingBlock (FtwLiteDevice);
536 if (EFI_ERROR (Status)) {
537 gBS->FreePool (SpareBuffer);
538 return Status;
539 }
540 //
541 // Restore spare backup buffer into spare block , if no failure happened during FtwWrite.
542 //
543 Status = FtwEraseSpareBlock (FtwLiteDevice);
544 Ptr = SpareBuffer;
545 for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {
546 Length = FtwLiteDevice->SizeOfSpareBlock;
547 Status = FtwLiteDevice->FtwBackupFvb->Write (
548 FtwLiteDevice->FtwBackupFvb,
549 FtwLiteDevice->FtwSpareLba + Index,
550 0,
551 &Length,
552 Ptr
553 );
554 if (EFI_ERROR (Status)) {
555 gBS->FreePool (SpareBuffer);
556 return EFI_ABORTED;
557 }
558
559 Ptr += Length;
560 }
561
562 gBS->FreePool (SpareBuffer);
563
564 DEBUG ((EFI_D_FTW_LITE, "FtwLite: reclaim work space success\n"));
565
566 return EFI_SUCCESS;
567 }