1/** @file
2
3   Internal functions to operate Working Block Space.
4
5Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR>
6This program and the accompanying materials
7are licensed and made available under the terms and conditions of the BSD License
8which accompanies this distribution.  The full text of the license may be found at
9http://opensource.org/licenses/bsd-license.php
10
11THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13
14**/
15
16
17#include "FaultTolerantWrite.h"
18
19EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER mWorkingBlockHeader = {ZERO_GUID, 0, 0, 0, 0, {0, 0, 0}, 0};
20
21/**
22  Initialize a local work space header.
23
24  Since Signature and WriteQueueSize have been known, Crc can be calculated out,
25  then the work space header will be fixed.
26**/
27VOID
28InitializeLocalWorkSpaceHeader (
29  VOID
30  )
31{
32  EFI_STATUS                              Status;
33
34  //
35  // Check signature with gEdkiiWorkingBlockSignatureGuid.
36  //
37  if (CompareGuid (&gEdkiiWorkingBlockSignatureGuid, &mWorkingBlockHeader.Signature)) {
38    //
39    // The local work space header has been initialized.
40    //
41    return;
42  }
43
44  SetMem (
45    &mWorkingBlockHeader,
46    sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),
47    FTW_ERASED_BYTE
48    );
49
50  //
51  // Here using gEdkiiWorkingBlockSignatureGuid as the signature.
52  //
53  CopyMem (
54    &mWorkingBlockHeader.Signature,
55    &gEdkiiWorkingBlockSignatureGuid,
56    sizeof (EFI_GUID)
57    );
58  mWorkingBlockHeader.WriteQueueSize = (UINT64) (PcdGet32 (PcdFlashNvStorageFtwWorkingSize) - sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER));
59
60  //
61  // Crc is calculated with all the fields except Crc and STATE, so leave them as FTW_ERASED_BYTE.
62  //
63
64  //
65  // Calculate the Crc of woking block header
66  //
67  Status = gBS->CalculateCrc32 (
68                  &mWorkingBlockHeader,
69                  sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),
70                  &mWorkingBlockHeader.Crc
71                  );
72  ASSERT_EFI_ERROR (Status);
73
74  mWorkingBlockHeader.WorkingBlockValid    = FTW_VALID_STATE;
75  mWorkingBlockHeader.WorkingBlockInvalid  = FTW_INVALID_STATE;
76}
77
78/**
79  Check to see if it is a valid work space.
80
81
82  @param WorkingHeader   Pointer of working block header
83
84  @retval TRUE          The work space is valid.
85  @retval FALSE         The work space is invalid.
86
87**/
88BOOLEAN
89IsValidWorkSpace (
90  IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingHeader
91  )
92{
93  if (WorkingHeader == NULL) {
94    return FALSE;
95  }
96
97  if (CompareMem (WorkingHeader, &mWorkingBlockHeader, sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER)) == 0) {
98    return TRUE;
99  }
100
101  DEBUG ((EFI_D_INFO, "Ftw: Work block header check mismatch\n"));
102  return FALSE;
103}
104
105/**
106  Initialize a work space when there is no work space.
107
108  @param WorkingHeader   Pointer of working block header
109
110  @retval  EFI_SUCCESS    The function completed successfully
111  @retval  EFI_ABORTED    The function could not complete successfully.
112
113**/
114EFI_STATUS
115InitWorkSpaceHeader (
116  IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingHeader
117  )
118{
119  if (WorkingHeader == NULL) {
120    return EFI_INVALID_PARAMETER;
121  }
122
123  CopyMem (WorkingHeader, &mWorkingBlockHeader, sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER));
124
125  return EFI_SUCCESS;
126}
127
128/**
129  Read work space data from work block or spare block.
130
131  @param FvBlock        FVB Protocol interface to access the block.
132  @param BlockSize      The size of the block.
133  @param Lba            Lba of the block.
134  @param Offset         The offset within the block.
135  @param Length         The number of bytes to read from the block.
136  @param Buffer         The data is read.
137
138  @retval EFI_SUCCESS   The function completed successfully.
139  @retval EFI_ABORTED   The function could not complete successfully.
140
141**/
142EFI_STATUS
143ReadWorkSpaceData (
144  IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,
145  IN UINTN                              BlockSize,
146  IN EFI_LBA                            Lba,
147  IN UINTN                              Offset,
148  IN UINTN                              Length,
149  OUT UINT8                             *Buffer
150  )
151{
152  EFI_STATUS            Status;
153  UINT8                 *Ptr;
154  UINTN                 MyLength;
155
156  //
157  // Calculate the real Offset and Lba to write.
158  //
159  while (Offset >= BlockSize) {
160    Offset -= BlockSize;
161    Lba++;
162  }
163
164  Ptr = Buffer;
165  while (Length > 0) {
166    if ((Offset + Length) > BlockSize) {
167      MyLength = BlockSize - Offset;
168    } else {
169      MyLength = Length;
170    }
171
172    Status = FvBlock->Read (
173                        FvBlock,
174                        Lba,
175                        Offset,
176                        &MyLength,
177                        Ptr
178                        );
179    if (EFI_ERROR (Status)) {
180      return EFI_ABORTED;
181    }
182    Offset = 0;
183    Length -= MyLength;
184    Ptr += MyLength;
185    Lba++;
186  }
187
188  return EFI_SUCCESS;
189}
190
191/**
192  Write work space data to work block.
193
194  @param FvBlock        FVB Protocol interface to access the block.
195  @param BlockSize      The size of the block.
196  @param Lba            Lba of the block.
197  @param Offset         The offset within the block to place the data.
198  @param Length         The number of bytes to write to the block.
199  @param Buffer         The data to write.
200
201  @retval EFI_SUCCESS   The function completed successfully.
202  @retval EFI_ABORTED   The function could not complete successfully.
203
204**/
205EFI_STATUS
206WriteWorkSpaceData (
207  IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock,
208  IN UINTN                              BlockSize,
209  IN EFI_LBA                            Lba,
210  IN UINTN                              Offset,
211  IN UINTN                              Length,
212  IN UINT8                              *Buffer
213  )
214{
215  EFI_STATUS            Status;
216  UINT8                 *Ptr;
217  UINTN                 MyLength;
218
219  //
220  // Calculate the real Offset and Lba to write.
221  //
222  while (Offset >= BlockSize) {
223    Offset -= BlockSize;
224    Lba++;
225  }
226
227  Ptr = Buffer;
228  while (Length > 0) {
229    if ((Offset + Length) > BlockSize) {
230      MyLength = BlockSize - Offset;
231    } else {
232      MyLength = Length;
233    }
234
235    Status = FvBlock->Write (
236                        FvBlock,
237                        Lba,
238                        Offset,
239                        &MyLength,
240                        Ptr
241                        );
242    if (EFI_ERROR (Status)) {
243      return EFI_ABORTED;
244    }
245    Offset = 0;
246    Length -= MyLength;
247    Ptr += MyLength;
248    Lba++;
249  }
250  return EFI_SUCCESS;
251}
252
253/**
254  Read from working block to refresh the work space in memory.
255
256  @param FtwDevice   Point to private data of FTW driver
257
258  @retval  EFI_SUCCESS    The function completed successfully
259  @retval  EFI_ABORTED    The function could not complete successfully.
260
261**/
262EFI_STATUS
263WorkSpaceRefresh (
264  IN EFI_FTW_DEVICE  *FtwDevice
265  )
266{
267  EFI_STATUS                      Status;
268  UINTN                           RemainingSpaceSize;
269
270  //
271  // Initialize WorkSpace as FTW_ERASED_BYTE
272  //
273  SetMem (
274    FtwDevice->FtwWorkSpace,
275    FtwDevice->FtwWorkSpaceSize,
276    FTW_ERASED_BYTE
277    );
278
279  //
280  // Read from working block
281  //
282  Status = ReadWorkSpaceData (
283             FtwDevice->FtwFvBlock,
284             FtwDevice->WorkBlockSize,
285             FtwDevice->FtwWorkSpaceLba,
286             FtwDevice->FtwWorkSpaceBase,
287             FtwDevice->FtwWorkSpaceSize,
288             FtwDevice->FtwWorkSpace
289             );
290  if (EFI_ERROR (Status)) {
291    return EFI_ABORTED;
292  }
293  //
294  // Refresh the FtwLastWriteHeader
295  //
296  Status = FtwGetLastWriteHeader (
297            FtwDevice->FtwWorkSpaceHeader,
298            FtwDevice->FtwWorkSpaceSize,
299            &FtwDevice->FtwLastWriteHeader
300            );
301  RemainingSpaceSize = FtwDevice->FtwWorkSpaceSize - ((UINTN) FtwDevice->FtwLastWriteHeader - (UINTN) FtwDevice->FtwWorkSpace);
302  DEBUG ((EFI_D_INFO, "Ftw: Remaining work space size - %x\n", RemainingSpaceSize));
303  //
304  // If FtwGetLastWriteHeader() returns error, or the remaining space size is even not enough to contain
305  // one EFI_FAULT_TOLERANT_WRITE_HEADER + one EFI_FAULT_TOLERANT_WRITE_RECORD(It will cause that the header
306  // pointed by FtwDevice->FtwLastWriteHeader or record pointed by FtwDevice->FtwLastWriteRecord may contain invalid data),
307  // it needs to reclaim work space.
308  //
309  if (EFI_ERROR (Status) || RemainingSpaceSize < sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER) + sizeof (EFI_FAULT_TOLERANT_WRITE_RECORD)) {
310    //
311    // reclaim work space in working block.
312    //
313    Status = FtwReclaimWorkSpace (FtwDevice, TRUE);
314    if (EFI_ERROR (Status)) {
315      DEBUG ((EFI_D_ERROR, "Ftw: Reclaim workspace - %r\n", Status));
316      return EFI_ABORTED;
317    }
318    //
319    // Read from working block again
320    //
321    Status = ReadWorkSpaceData (
322               FtwDevice->FtwFvBlock,
323               FtwDevice->WorkBlockSize,
324               FtwDevice->FtwWorkSpaceLba,
325               FtwDevice->FtwWorkSpaceBase,
326               FtwDevice->FtwWorkSpaceSize,
327               FtwDevice->FtwWorkSpace
328               );
329    if (EFI_ERROR (Status)) {
330      return EFI_ABORTED;
331    }
332
333    Status = FtwGetLastWriteHeader (
334              FtwDevice->FtwWorkSpaceHeader,
335              FtwDevice->FtwWorkSpaceSize,
336              &FtwDevice->FtwLastWriteHeader
337              );
338    if (EFI_ERROR (Status)) {
339      return EFI_ABORTED;
340    }
341  }
342  //
343  // Refresh the FtwLastWriteRecord
344  //
345  Status = FtwGetLastWriteRecord (
346            FtwDevice->FtwLastWriteHeader,
347            &FtwDevice->FtwLastWriteRecord
348            );
349  if (EFI_ERROR (Status)) {
350    return EFI_ABORTED;
351  }
352
353  return EFI_SUCCESS;
354}
355
356/**
357  Reclaim the work space on the working block.
358
359  @param FtwDevice       Point to private data of FTW driver
360  @param PreserveRecord  Whether to preserve the working record is needed
361
362  @retval EFI_SUCCESS            The function completed successfully
363  @retval EFI_OUT_OF_RESOURCES   Allocate memory error
364  @retval EFI_ABORTED            The function could not complete successfully
365
366**/
367EFI_STATUS
368FtwReclaimWorkSpace (
369  IN EFI_FTW_DEVICE  *FtwDevice,
370  IN BOOLEAN         PreserveRecord
371  )
372{
373  EFI_STATUS                              Status;
374  UINTN                                   Length;
375  EFI_FAULT_TOLERANT_WRITE_HEADER         *Header;
376  UINT8                                   *TempBuffer;
377  UINTN                                   TempBufferSize;
378  UINTN                                   SpareBufferSize;
379  UINT8                                   *SpareBuffer;
380  EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingBlockHeader;
381  UINTN                                   Index;
382  UINT8                                   *Ptr;
383  EFI_LBA                                 WorkSpaceLbaOffset;
384
385  DEBUG ((EFI_D_INFO, "Ftw: start to reclaim work space\n"));
386
387  WorkSpaceLbaOffset = FtwDevice->FtwWorkSpaceLba - FtwDevice->FtwWorkBlockLba;
388
389  //
390  // Read all original data from working block to a memory buffer
391  //
392  TempBufferSize = FtwDevice->NumberOfWorkBlock * FtwDevice->WorkBlockSize;
393  TempBuffer     = AllocateZeroPool (TempBufferSize);
394  if (TempBuffer == NULL) {
395    return EFI_OUT_OF_RESOURCES;
396  }
397
398  Ptr = TempBuffer;
399  for (Index = 0; Index < FtwDevice->NumberOfWorkBlock; Index += 1) {
400    Length = FtwDevice->WorkBlockSize;
401    Status = FtwDevice->FtwFvBlock->Read (
402                                          FtwDevice->FtwFvBlock,
403                                          FtwDevice->FtwWorkBlockLba + Index,
404                                          0,
405                                          &Length,
406                                          Ptr
407                                          );
408    if (EFI_ERROR (Status)) {
409      FreePool (TempBuffer);
410      return EFI_ABORTED;
411    }
412
413    Ptr += Length;
414  }
415  //
416  // Clean up the workspace, remove all the completed records.
417  //
418  Ptr = TempBuffer +
419        (UINTN) WorkSpaceLbaOffset * FtwDevice->WorkBlockSize +
420        FtwDevice->FtwWorkSpaceBase;
421
422  //
423  // Clear the content of buffer that will save the new work space data
424  //
425  SetMem (Ptr, FtwDevice->FtwWorkSpaceSize, FTW_ERASED_BYTE);
426
427  //
428  // Copy EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER to buffer
429  //
430  CopyMem (
431    Ptr,
432    FtwDevice->FtwWorkSpaceHeader,
433    sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER)
434    );
435  if (PreserveRecord) {
436    //
437    // Get the last record following the header,
438    //
439    Status = FtwGetLastWriteHeader (
440               FtwDevice->FtwWorkSpaceHeader,
441               FtwDevice->FtwWorkSpaceSize,
442               &FtwDevice->FtwLastWriteHeader
443               );
444    Header = FtwDevice->FtwLastWriteHeader;
445    if (!EFI_ERROR (Status) && (Header != NULL) && (Header->Complete != FTW_VALID_STATE) && (Header->HeaderAllocated == FTW_VALID_STATE)) {
446      CopyMem (
447        Ptr + sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),
448        FtwDevice->FtwLastWriteHeader,
449        FTW_WRITE_TOTAL_SIZE (Header->NumberOfWrites, Header->PrivateDataSize)
450        );
451    }
452  }
453
454  CopyMem (
455    FtwDevice->FtwWorkSpace,
456    Ptr,
457    FtwDevice->FtwWorkSpaceSize
458    );
459
460  FtwGetLastWriteHeader (
461    FtwDevice->FtwWorkSpaceHeader,
462    FtwDevice->FtwWorkSpaceSize,
463    &FtwDevice->FtwLastWriteHeader
464    );
465
466  FtwGetLastWriteRecord (
467    FtwDevice->FtwLastWriteHeader,
468    &FtwDevice->FtwLastWriteRecord
469    );
470
471  //
472  // Set the WorkingBlockValid and WorkingBlockInvalid as INVALID
473  //
474  WorkingBlockHeader                      = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) (TempBuffer +
475                                            (UINTN) WorkSpaceLbaOffset * FtwDevice->WorkBlockSize +
476                                            FtwDevice->FtwWorkSpaceBase);
477  WorkingBlockHeader->WorkingBlockValid   = FTW_INVALID_STATE;
478  WorkingBlockHeader->WorkingBlockInvalid = FTW_INVALID_STATE;
479
480  //
481  // Try to keep the content of spare block
482  // Save spare block into a spare backup memory buffer (Sparebuffer)
483  //
484  SpareBufferSize = FtwDevice->SpareAreaLength;
485  SpareBuffer     = AllocatePool (SpareBufferSize);
486  if (SpareBuffer == NULL) {
487    FreePool (TempBuffer);
488    return EFI_OUT_OF_RESOURCES;
489  }
490
491  Ptr = SpareBuffer;
492  for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
493    Length = FtwDevice->SpareBlockSize;
494    Status = FtwDevice->FtwBackupFvb->Read (
495                                        FtwDevice->FtwBackupFvb,
496                                        FtwDevice->FtwSpareLba + Index,
497                                        0,
498                                        &Length,
499                                        Ptr
500                                        );
501    if (EFI_ERROR (Status)) {
502      FreePool (TempBuffer);
503      FreePool (SpareBuffer);
504      return EFI_ABORTED;
505    }
506
507    Ptr += Length;
508  }
509  //
510  // Write the memory buffer to spare block
511  //
512  Status  = FtwEraseSpareBlock (FtwDevice);
513  if (EFI_ERROR (Status)) {
514    FreePool (TempBuffer);
515    FreePool (SpareBuffer);
516    return EFI_ABORTED;
517  }
518  Ptr     = TempBuffer;
519  for (Index = 0; TempBufferSize > 0; Index += 1) {
520    if (TempBufferSize > FtwDevice->SpareBlockSize) {
521      Length = FtwDevice->SpareBlockSize;
522    } else {
523      Length = TempBufferSize;
524    }
525    Status = FtwDevice->FtwBackupFvb->Write (
526                                            FtwDevice->FtwBackupFvb,
527                                            FtwDevice->FtwSpareLba + Index,
528                                            0,
529                                            &Length,
530                                            Ptr
531                                            );
532    if (EFI_ERROR (Status)) {
533      FreePool (TempBuffer);
534      FreePool (SpareBuffer);
535      return EFI_ABORTED;
536    }
537
538    Ptr += Length;
539    TempBufferSize -= Length;
540  }
541  //
542  // Free TempBuffer
543  //
544  FreePool (TempBuffer);
545
546  //
547  // Set the WorkingBlockValid in spare block
548  //
549  Status = FtwUpdateFvState (
550            FtwDevice->FtwBackupFvb,
551            FtwDevice->SpareBlockSize,
552            FtwDevice->FtwSpareLba + FtwDevice->FtwWorkSpaceLbaInSpare,
553            FtwDevice->FtwWorkSpaceBaseInSpare + sizeof (EFI_GUID) + sizeof (UINT32),
554            WORKING_BLOCK_VALID
555            );
556  if (EFI_ERROR (Status)) {
557    FreePool (SpareBuffer);
558    return EFI_ABORTED;
559  }
560  //
561  // Before erase the working block, set WorkingBlockInvalid in working block.
562  //
563  // Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER,
564  //                          WorkingBlockInvalid);
565  //
566  Status = FtwUpdateFvState (
567            FtwDevice->FtwFvBlock,
568            FtwDevice->WorkBlockSize,
569            FtwDevice->FtwWorkSpaceLba,
570            FtwDevice->FtwWorkSpaceBase + sizeof (EFI_GUID) + sizeof (UINT32),
571            WORKING_BLOCK_INVALID
572            );
573  if (EFI_ERROR (Status)) {
574    FreePool (SpareBuffer);
575    return EFI_ABORTED;
576  }
577
578  FtwDevice->FtwWorkSpaceHeader->WorkingBlockInvalid = FTW_VALID_STATE;
579
580  //
581  // Write the spare block to working block
582  //
583  Status = FlushSpareBlockToWorkingBlock (FtwDevice);
584  if (EFI_ERROR (Status)) {
585    FreePool (SpareBuffer);
586    return Status;
587  }
588  //
589  // Restore spare backup buffer into spare block , if no failure happened during FtwWrite.
590  //
591  Status  = FtwEraseSpareBlock (FtwDevice);
592  if (EFI_ERROR (Status)) {
593    FreePool (SpareBuffer);
594    return EFI_ABORTED;
595  }
596  Ptr     = SpareBuffer;
597  for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) {
598    Length = FtwDevice->SpareBlockSize;
599    Status = FtwDevice->FtwBackupFvb->Write (
600                                        FtwDevice->FtwBackupFvb,
601                                        FtwDevice->FtwSpareLba + Index,
602                                        0,
603                                        &Length,
604                                        Ptr
605                                        );
606    if (EFI_ERROR (Status)) {
607      FreePool (SpareBuffer);
608      return EFI_ABORTED;
609    }
610
611    Ptr += Length;
612  }
613
614  FreePool (SpareBuffer);
615
616  DEBUG ((EFI_D_INFO, "Ftw: reclaim work space successfully\n"));
617
618  return EFI_SUCCESS;
619}
620