Home | History | Annotate | Download | only in FaultTolerantWriteDxe
      1 /** @file
      2 
      3    Internal functions to operate Working Block Space.
      4 
      5 Copyright (c) 2006 - 2015, 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 EFI_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 **/
     27 VOID
     28 InitializeLocalWorkSpaceHeader (
     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 **/
     88 BOOLEAN
     89 IsValidWorkSpace (
     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 **/
    114 EFI_STATUS
    115 InitWorkSpaceHeader (
    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 **/
    142 EFI_STATUS
    143 ReadWorkSpaceData (
    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 **/
    205 EFI_STATUS
    206 WriteWorkSpaceData (
    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 **/
    262 EFI_STATUS
    263 WorkSpaceRefresh (
    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 **/
    367 EFI_STATUS
    368 FtwReclaimWorkSpace (
    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