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