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