]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/FirmwareVolume/FaultTolerantWriteDxe/FtwWorkSpace.c
3da4d42cd338061d519bd33b5318b56a9f258a04
[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 Record = (EFI_FTW_LITE_RECORD *) (FtwLiteDevice->FtwWorkSpaceHeader + 1);
214 while (Record->WriteCompleted == FTW_VALID_STATE) {
215 //
216 // If Offset exceed the FTW work space boudary, return error.
217 //
218 if ((UINTN) ((UINT8 *) Record - FtwLiteDevice->FtwWorkSpace) > FtwLiteDevice->FtwWorkSpaceSize) {
219 return EFI_ABORTED;
220 }
221
222 Record++;
223 }
224 //
225 // Last write record is found
226 //
227 *FtwLastRecord = Record;
228 return EFI_SUCCESS;
229 }
230
231 /**
232 Read from working block to refresh the work space in memory.
233
234
235 @param FtwLiteDevice Point to private data of FTW driver
236
237 @retval EFI_SUCCESS The function completed successfully
238 @retval EFI_ABORTED The function could not complete successfully.
239
240 **/
241 EFI_STATUS
242 WorkSpaceRefresh (
243 IN EFI_FTW_LITE_DEVICE *FtwLiteDevice
244 )
245 {
246 EFI_STATUS Status;
247 UINTN Length;
248 UINTN Offset;
249 EFI_FTW_LITE_RECORD *Record;
250
251 //
252 // Initialize WorkSpace as FTW_ERASED_BYTE
253 //
254 SetMem (
255 FtwLiteDevice->FtwWorkSpace,
256 FtwLiteDevice->FtwWorkSpaceSize,
257 FTW_ERASED_BYTE
258 );
259
260 //
261 // Read from working block
262 //
263 Length = FtwLiteDevice->FtwWorkSpaceSize;
264 Status = FtwLiteDevice->FtwFvBlock->Read (
265 FtwLiteDevice->FtwFvBlock,
266 FtwLiteDevice->FtwWorkSpaceLba,
267 FtwLiteDevice->FtwWorkSpaceBase,
268 &Length,
269 FtwLiteDevice->FtwWorkSpace
270 );
271 if (EFI_ERROR (Status)) {
272 return EFI_ABORTED;
273 }
274 //
275 // Refresh the FtwLastRecord
276 //
277 Status = FtwGetLastRecord (FtwLiteDevice, &FtwLiteDevice->FtwLastRecord);
278
279 Record = FtwLiteDevice->FtwLastRecord;
280 Offset = (UINTN) (UINT8 *) Record - (UINTN) FtwLiteDevice->FtwWorkSpace;
281
282 //
283 // IF work space has error or Record is out of the workspace limit, THEN
284 // call reclaim.
285 //
286 if (EFI_ERROR (Status) || (Offset + WRITE_TOTAL_SIZE >= FtwLiteDevice->FtwWorkSpaceSize)) {
287 //
288 // reclaim work space in working block.
289 //
290 Status = FtwReclaimWorkSpace (FtwLiteDevice);
291 if (EFI_ERROR (Status)) {
292 DEBUG ((EFI_D_FTW_LITE, "FtwLite: Reclaim workspace - %r\n", Status));
293 return EFI_ABORTED;
294 }
295 }
296
297 return EFI_SUCCESS;
298 }
299
300 /**
301 Reclaim the work space. Get rid of all the completed write records
302 and write records in the Fault Tolerant work space.
303
304
305 @param FtwLiteDevice Point to private data of FTW driver
306 @param FtwSpaceBuffer Buffer to contain the reclaimed clean data
307 @param BufferSize Size of the FtwSpaceBuffer
308
309 @retval EFI_SUCCESS The function completed successfully
310 @retval EFI_BUFFER_TOO_SMALL The FtwSpaceBuffer is too small
311 @retval EFI_ABORTED The function could not complete successfully.
312
313 **/
314 EFI_STATUS
315 CleanupWorkSpace (
316 IN EFI_FTW_LITE_DEVICE *FtwLiteDevice,
317 IN OUT UINT8 *FtwSpaceBuffer,
318 IN UINTN BufferSize
319 )
320 {
321 UINTN Length;
322 EFI_FTW_LITE_RECORD *Record;
323
324 //
325 // To check if the buffer is large enough
326 //
327 Length = FtwLiteDevice->FtwWorkSpaceSize;
328 if (BufferSize < Length) {
329 return EFI_BUFFER_TOO_SMALL;
330 }
331 //
332 // Clear the content of buffer that will save the new work space data
333 //
334 SetMem (FtwSpaceBuffer, Length, FTW_ERASED_BYTE);
335
336 //
337 // Copy EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER to buffer
338 //
339 CopyMem (
340 FtwSpaceBuffer,
341 FtwLiteDevice->FtwWorkSpaceHeader,
342 sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER)
343 );
344
345 //
346 // Get the last record
347 //
348 Record = FtwLiteDevice->FtwLastRecord;
349 if ((Record != NULL) && (Record->WriteAllocated == FTW_VALID_STATE) && (Record->WriteCompleted != FTW_VALID_STATE)) {
350 CopyMem (
351 (UINT8 *) FtwSpaceBuffer + sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),
352 Record,
353 WRITE_TOTAL_SIZE
354 );
355 }
356
357 return EFI_SUCCESS;
358 }
359
360 /**
361 Reclaim the work space on the working block.
362
363
364 @param FtwLiteDevice Point to private data of FTW driver
365
366 @retval EFI_SUCCESS The function completed successfully
367 @retval EFI_OUT_OF_RESOURCES Allocate memory error
368 @retval EFI_ABORTED The function could not complete successfully
369
370 **/
371 EFI_STATUS
372 FtwReclaimWorkSpace (
373 IN EFI_FTW_LITE_DEVICE *FtwLiteDevice
374 )
375 {
376 EFI_STATUS Status;
377 UINT8 *TempBuffer;
378 UINTN TempBufferSize;
379 UINT8 *Ptr;
380 UINTN Length;
381 UINTN Index;
382 UINTN SpareBufferSize;
383 UINT8 *SpareBuffer;
384 EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingBlockHeader;
385
386 DEBUG ((EFI_D_FTW_LITE, "FtwLite: start to reclaim work space\n"));
387
388 //
389 // Read all original data from working block to a memory buffer
390 //
391 TempBufferSize = FtwLiteDevice->SpareAreaLength;
392 TempBuffer = AllocateZeroPool (TempBufferSize);
393 if (TempBuffer != NULL) {
394 return EFI_OUT_OF_RESOURCES;
395 }
396
397 Ptr = TempBuffer;
398 for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {
399 Length = FtwLiteDevice->SizeOfSpareBlock;
400 Status = FtwLiteDevice->FtwFvBlock->Read (
401 FtwLiteDevice->FtwFvBlock,
402 FtwLiteDevice->FtwWorkBlockLba + Index,
403 0,
404 &Length,
405 Ptr
406 );
407 if (EFI_ERROR (Status)) {
408 FreePool (TempBuffer);
409 return EFI_ABORTED;
410 }
411
412 Ptr += Length;
413 }
414 //
415 // Clean up the workspace, remove all the completed records.
416 //
417 Ptr = TempBuffer +
418 ((UINTN) (FtwLiteDevice->FtwWorkSpaceLba - FtwLiteDevice->FtwWorkBlockLba)) *
419 FtwLiteDevice->SizeOfSpareBlock + FtwLiteDevice->FtwWorkSpaceBase;
420
421 Status = CleanupWorkSpace (
422 FtwLiteDevice,
423 Ptr,
424 FtwLiteDevice->FtwWorkSpaceSize
425 );
426
427 CopyMem (
428 FtwLiteDevice->FtwWorkSpace,
429 Ptr,
430 FtwLiteDevice->FtwWorkSpaceSize
431 );
432
433 Status = FtwGetLastRecord (FtwLiteDevice, &FtwLiteDevice->FtwLastRecord);
434
435 //
436 // Set the WorkingBlockValid and WorkingBlockInvalid as INVALID
437 //
438 WorkingBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) Ptr;
439 WorkingBlockHeader->WorkingBlockValid = FTW_INVALID_STATE;
440 WorkingBlockHeader->WorkingBlockInvalid = FTW_INVALID_STATE;
441
442 //
443 // Try to keep the content of spare block
444 // Save spare block into a spare backup memory buffer (Sparebuffer)
445 //
446 SpareBufferSize = FtwLiteDevice->SpareAreaLength;
447 SpareBuffer = AllocatePool (SpareBufferSize);
448 if (SpareBuffer == NULL) {
449 FreePool (TempBuffer);
450 return EFI_OUT_OF_RESOURCES;
451 }
452
453 Ptr = SpareBuffer;
454 for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {
455 Length = FtwLiteDevice->SizeOfSpareBlock;
456 Status = FtwLiteDevice->FtwBackupFvb->Read (
457 FtwLiteDevice->FtwBackupFvb,
458 FtwLiteDevice->FtwSpareLba + Index,
459 0,
460 &Length,
461 Ptr
462 );
463 if (EFI_ERROR (Status)) {
464 FreePool (TempBuffer);
465 FreePool (SpareBuffer);
466 return EFI_ABORTED;
467 }
468
469 Ptr += Length;
470 }
471 //
472 // Write the memory buffer to spare block
473 //
474 Status = FtwEraseSpareBlock (FtwLiteDevice);
475 Ptr = TempBuffer;
476 for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {
477 Length = FtwLiteDevice->SizeOfSpareBlock;
478 Status = FtwLiteDevice->FtwBackupFvb->Write (
479 FtwLiteDevice->FtwBackupFvb,
480 FtwLiteDevice->FtwSpareLba + Index,
481 0,
482 &Length,
483 Ptr
484 );
485 if (EFI_ERROR (Status)) {
486 FreePool (TempBuffer);
487 FreePool (SpareBuffer);
488 return EFI_ABORTED;
489 }
490
491 Ptr += Length;
492 }
493 //
494 // Free TempBuffer
495 //
496 FreePool (TempBuffer);
497
498 //
499 // Write the spare block to working block
500 //
501 Status = FlushSpareBlockToWorkingBlock (FtwLiteDevice);
502 if (EFI_ERROR (Status)) {
503 FreePool (SpareBuffer);
504 return Status;
505 }
506 //
507 // Restore spare backup buffer into spare block , if no failure happened during FtwWrite.
508 //
509 Status = FtwEraseSpareBlock (FtwLiteDevice);
510 Ptr = SpareBuffer;
511 for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {
512 Length = FtwLiteDevice->SizeOfSpareBlock;
513 Status = FtwLiteDevice->FtwBackupFvb->Write (
514 FtwLiteDevice->FtwBackupFvb,
515 FtwLiteDevice->FtwSpareLba + Index,
516 0,
517 &Length,
518 Ptr
519 );
520 if (EFI_ERROR (Status)) {
521 FreePool (SpareBuffer);
522 return EFI_ABORTED;
523 }
524
525 Ptr += Length;
526 }
527
528 FreePool (SpareBuffer);
529
530 DEBUG ((EFI_D_FTW_LITE, "FtwLite: reclaim work space success\n"));
531
532 return EFI_SUCCESS;
533 }