]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/FirmwareVolume/FaultTolerantWriteDxe/FtwWorkSpace.c
6474c2d0a5846309c80b125900aac369023a3f3b
[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 = (UINT64) (PcdGet32 (PcdFlashNvStorageFtwWorkingSize) - sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER));
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 + FTW_LITE_RECORD_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 @param PreserveRecord Whether get the last record or not
307
308 @retval EFI_SUCCESS The function completed successfully
309 @retval EFI_OUT_OF_RESOURCES Allocate memory error
310 @retval EFI_ABORTED The function could not complete successfully
311
312 **/
313 EFI_STATUS
314 FtwReclaimWorkSpace (
315 IN EFI_FTW_LITE_DEVICE *FtwLiteDevice,
316 IN BOOLEAN PreserveRecord
317 )
318 {
319 EFI_STATUS Status;
320 UINT8 *TempBuffer;
321 UINTN TempBufferSize;
322 UINT8 *Ptr;
323 UINTN Length;
324 UINTN Index;
325 UINTN SpareBufferSize;
326 UINT8 *SpareBuffer;
327 EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingBlockHeader;
328 EFI_FTW_LITE_RECORD *Record;
329
330 DEBUG ((EFI_D_FTW_LITE, "FtwLite: start to reclaim work space\n"));
331
332 //
333 // Read all original data from working block to a memory buffer
334 //
335 TempBufferSize = FtwLiteDevice->SpareAreaLength;
336 TempBuffer = AllocateZeroPool (TempBufferSize);
337 if (TempBuffer == NULL) {
338 return EFI_OUT_OF_RESOURCES;
339 }
340
341 Ptr = TempBuffer;
342 for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {
343 Length = FtwLiteDevice->SizeOfSpareBlock;
344 Status = FtwLiteDevice->FtwFvBlock->Read (
345 FtwLiteDevice->FtwFvBlock,
346 FtwLiteDevice->FtwWorkBlockLba + Index,
347 0,
348 &Length,
349 Ptr
350 );
351 if (EFI_ERROR (Status)) {
352 FreePool (TempBuffer);
353 return EFI_ABORTED;
354 }
355
356 Ptr += Length;
357 }
358 //
359 // Clean up the workspace, remove all the completed records.
360 //
361 Ptr = TempBuffer +
362 ((UINTN) (FtwLiteDevice->FtwWorkSpaceLba - FtwLiteDevice->FtwWorkBlockLba)) *
363 FtwLiteDevice->SizeOfSpareBlock + FtwLiteDevice->FtwWorkSpaceBase;
364
365 //
366 // Clear the content of buffer that will save the new work space data
367 //
368 SetMem (Ptr, FtwLiteDevice->FtwWorkSpaceSize, FTW_ERASED_BYTE);
369
370 //
371 // Copy EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER to buffer
372 //
373 CopyMem (
374 Ptr,
375 FtwLiteDevice->FtwWorkSpaceHeader,
376 sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER)
377 );
378 if (PreserveRecord) {
379 //
380 // Get the last record
381 //
382 Status = FtwGetLastRecord (FtwLiteDevice, &FtwLiteDevice->FtwLastRecord);
383 Record = FtwLiteDevice->FtwLastRecord;
384 if (!EFI_ERROR (Status) &&
385 Record != NULL &&
386 Record->WriteAllocated == FTW_VALID_STATE &&
387 Record->WriteCompleted != FTW_VALID_STATE) {
388 CopyMem (
389 (UINT8 *) Ptr + sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),
390 Record,
391 FTW_LITE_RECORD_SIZE
392 );
393 }
394 }
395
396 CopyMem (
397 FtwLiteDevice->FtwWorkSpace,
398 Ptr,
399 FtwLiteDevice->FtwWorkSpaceSize
400 );
401
402 Status = FtwGetLastRecord (FtwLiteDevice, &FtwLiteDevice->FtwLastRecord);
403
404 //
405 // Set the WorkingBlockValid and WorkingBlockInvalid as INVALID
406 //
407 WorkingBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) Ptr;
408 WorkingBlockHeader->WorkingBlockValid = FTW_INVALID_STATE;
409 WorkingBlockHeader->WorkingBlockInvalid = FTW_INVALID_STATE;
410
411 //
412 // Try to keep the content of spare block
413 // Save spare block into a spare backup memory buffer (Sparebuffer)
414 //
415 SpareBufferSize = FtwLiteDevice->SpareAreaLength;
416 SpareBuffer = AllocatePool (SpareBufferSize);
417 if (SpareBuffer == NULL) {
418 FreePool (TempBuffer);
419 return EFI_OUT_OF_RESOURCES;
420 }
421
422 Ptr = SpareBuffer;
423 for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {
424 Length = FtwLiteDevice->SizeOfSpareBlock;
425 Status = FtwLiteDevice->FtwBackupFvb->Read (
426 FtwLiteDevice->FtwBackupFvb,
427 FtwLiteDevice->FtwSpareLba + Index,
428 0,
429 &Length,
430 Ptr
431 );
432 if (EFI_ERROR (Status)) {
433 FreePool (TempBuffer);
434 FreePool (SpareBuffer);
435 return EFI_ABORTED;
436 }
437
438 Ptr += Length;
439 }
440 //
441 // Write the memory buffer to spare block
442 //
443 Status = FtwEraseSpareBlock (FtwLiteDevice);
444 Ptr = TempBuffer;
445 for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {
446 Length = FtwLiteDevice->SizeOfSpareBlock;
447 Status = FtwLiteDevice->FtwBackupFvb->Write (
448 FtwLiteDevice->FtwBackupFvb,
449 FtwLiteDevice->FtwSpareLba + Index,
450 0,
451 &Length,
452 Ptr
453 );
454 if (EFI_ERROR (Status)) {
455 FreePool (TempBuffer);
456 FreePool (SpareBuffer);
457 return EFI_ABORTED;
458 }
459
460 Ptr += Length;
461 }
462 //
463 // Free TempBuffer
464 //
465 FreePool (TempBuffer);
466
467 //
468 // Write the spare block to working block
469 //
470 Status = FlushSpareBlockToWorkingBlock (FtwLiteDevice);
471 if (EFI_ERROR (Status)) {
472 FreePool (SpareBuffer);
473 return Status;
474 }
475 //
476 // Restore spare backup buffer into spare block , if no failure happened during FtwWrite.
477 //
478 Status = FtwEraseSpareBlock (FtwLiteDevice);
479 Ptr = SpareBuffer;
480 for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {
481 Length = FtwLiteDevice->SizeOfSpareBlock;
482 Status = FtwLiteDevice->FtwBackupFvb->Write (
483 FtwLiteDevice->FtwBackupFvb,
484 FtwLiteDevice->FtwSpareLba + Index,
485 0,
486 &Length,
487 Ptr
488 );
489 if (EFI_ERROR (Status)) {
490 FreePool (SpareBuffer);
491 return EFI_ABORTED;
492 }
493
494 Ptr += Length;
495 }
496
497 FreePool (SpareBuffer);
498
499 DEBUG ((EFI_D_FTW_LITE, "FtwLite: reclaim work space success\n"));
500
501 return EFI_SUCCESS;
502 }