Home | History | Annotate | Download | only in FaultTolerantWritePei
      1 /** @file
      2   This driver installs gEdkiiFaultTolerantWriteGuid PPI to inform
      3   the check for FTW last write data has been done.
      4 
      5 Copyright (c) 2013, 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 #include <PiPei.h>
     17 
     18 #include <Guid/SystemNvDataGuid.h>
     19 #include <Guid/FaultTolerantWrite.h>
     20 #include <Library/PeiServicesLib.h>
     21 #include <Library/PcdLib.h>
     22 #include <Library/DebugLib.h>
     23 #include <Library/BaseMemoryLib.h>
     24 #include <Library/HobLib.h>
     25 
     26 EFI_PEI_PPI_DESCRIPTOR     mPpiListVariable = {
     27   (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
     28   &gEdkiiFaultTolerantWriteGuid,
     29   NULL
     30 };
     31 
     32 /**
     33   Get the last Write Header pointer.
     34   The last write header is the header whose 'complete' state hasn't been set.
     35   After all, this header may be a EMPTY header entry for next Allocate.
     36 
     37 
     38   @param FtwWorkSpaceHeader Pointer of the working block header
     39   @param FtwWorkSpaceSize   Size of the work space
     40   @param FtwWriteHeader     Pointer to retrieve the last write header
     41 
     42   @retval  EFI_SUCCESS      Get the last write record successfully
     43   @retval  EFI_ABORTED      The FTW work space is damaged
     44 
     45 **/
     46 EFI_STATUS
     47 FtwGetLastWriteHeader (
     48   IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER  *FtwWorkSpaceHeader,
     49   IN UINTN                                    FtwWorkSpaceSize,
     50   OUT EFI_FAULT_TOLERANT_WRITE_HEADER         **FtwWriteHeader
     51   )
     52 {
     53   UINTN                           Offset;
     54   EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader;
     55 
     56   *FtwWriteHeader = NULL;
     57   FtwHeader       = (EFI_FAULT_TOLERANT_WRITE_HEADER *) (FtwWorkSpaceHeader + 1);
     58   Offset          = sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER);
     59 
     60   while (FtwHeader->Complete == FTW_VALID_STATE) {
     61     Offset += FTW_WRITE_TOTAL_SIZE (FtwHeader->NumberOfWrites, FtwHeader->PrivateDataSize);
     62     //
     63     // If Offset exceed the FTW work space boudary, return error.
     64     //
     65     if (Offset >= FtwWorkSpaceSize) {
     66       *FtwWriteHeader = FtwHeader;
     67       return EFI_ABORTED;
     68     }
     69 
     70     FtwHeader = (EFI_FAULT_TOLERANT_WRITE_HEADER *) ((UINT8 *) FtwWorkSpaceHeader + Offset);
     71   }
     72   //
     73   // Last write header is found
     74   //
     75   *FtwWriteHeader = FtwHeader;
     76 
     77   return EFI_SUCCESS;
     78 }
     79 
     80 /**
     81   Get the last Write Record pointer. The last write Record is the Record
     82   whose DestinationCompleted state hasn't been set. After all, this Record
     83   may be a EMPTY record entry for next write.
     84 
     85 
     86   @param FtwWriteHeader  Pointer to the write record header
     87   @param FtwWriteRecord  Pointer to retrieve the last write record
     88 
     89   @retval EFI_SUCCESS        Get the last write record successfully
     90   @retval EFI_ABORTED        The FTW work space is damaged
     91 
     92 **/
     93 EFI_STATUS
     94 FtwGetLastWriteRecord (
     95   IN EFI_FAULT_TOLERANT_WRITE_HEADER          *FtwWriteHeader,
     96   OUT EFI_FAULT_TOLERANT_WRITE_RECORD         **FtwWriteRecord
     97   )
     98 {
     99   UINTN                           Index;
    100   EFI_FAULT_TOLERANT_WRITE_RECORD *FtwRecord;
    101 
    102   *FtwWriteRecord = NULL;
    103   FtwRecord       = (EFI_FAULT_TOLERANT_WRITE_RECORD *) (FtwWriteHeader + 1);
    104 
    105   //
    106   // Try to find the last write record "that has not completed"
    107   //
    108   for (Index = 0; Index < FtwWriteHeader->NumberOfWrites; Index += 1) {
    109     if (FtwRecord->DestinationComplete != FTW_VALID_STATE) {
    110       //
    111       // The last write record is found
    112       //
    113       *FtwWriteRecord = FtwRecord;
    114       return EFI_SUCCESS;
    115     }
    116 
    117     FtwRecord++;
    118 
    119     if (FtwWriteHeader->PrivateDataSize != 0) {
    120       FtwRecord = (EFI_FAULT_TOLERANT_WRITE_RECORD *) ((UINTN) FtwRecord + (UINTN) FtwWriteHeader->PrivateDataSize);
    121     }
    122   }
    123   //
    124   //  if Index == NumberOfWrites, then
    125   //  the last record has been written successfully,
    126   //  but the Header->Complete Flag has not been set.
    127   //  also return the last record.
    128   //
    129   if (Index == FtwWriteHeader->NumberOfWrites) {
    130     *FtwWriteRecord = (EFI_FAULT_TOLERANT_WRITE_RECORD *) ((UINTN) FtwRecord - FTW_RECORD_SIZE (FtwWriteHeader->PrivateDataSize));
    131     return EFI_SUCCESS;
    132   }
    133 
    134   return EFI_ABORTED;
    135 }
    136 
    137 /**
    138   Check to see if it is a valid work space.
    139 
    140 
    141   @param WorkingHeader   Pointer of working block header
    142   @param WorkingLength   Working block length
    143 
    144   @retval TRUE          The work space is valid.
    145   @retval FALSE         The work space is invalid.
    146 
    147 **/
    148 BOOLEAN
    149 IsValidWorkSpace (
    150   IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER    *WorkingHeader,
    151   IN UINTN                                      WorkingLength
    152   )
    153 {
    154   UINT8 Data;
    155 
    156   if (WorkingHeader == NULL) {
    157     return FALSE;
    158   }
    159 
    160   if ((WorkingHeader->WorkingBlockValid != FTW_VALID_STATE) || (WorkingHeader->WorkingBlockInvalid == FTW_VALID_STATE)) {
    161     DEBUG ((EFI_D_ERROR, "FtwPei: Work block header valid bit check error\n"));
    162     return FALSE;
    163   }
    164 
    165   if (WorkingHeader->WriteQueueSize != (WorkingLength - sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER))) {
    166     DEBUG ((EFI_D_ERROR, "FtwPei: Work block header WriteQueueSize check error\n"));
    167     return FALSE;
    168   }
    169 
    170   //
    171   // Check signature with gEdkiiWorkingBlockSignatureGuid
    172   //
    173   if (!CompareGuid (&gEdkiiWorkingBlockSignatureGuid, &WorkingHeader->Signature)) {
    174     DEBUG ((EFI_D_ERROR, "FtwPei: Work block header signature check error, it should be gEdkiiWorkingBlockSignatureGuid\n"));
    175     //
    176     // To be compatible with old signature gEfiSystemNvDataFvGuid.
    177     //
    178     if (!CompareGuid (&gEfiSystemNvDataFvGuid, &WorkingHeader->Signature)) {
    179       return FALSE;
    180     } else {
    181       Data = *(UINT8 *) (WorkingHeader + 1);
    182       if (Data != 0xff) {
    183         DEBUG ((EFI_D_ERROR, "FtwPei: Old format FTW structure can't be handled\n"));
    184         ASSERT (FALSE);
    185         return FALSE;
    186       }
    187     }
    188   }
    189 
    190   return TRUE;
    191 
    192 }
    193 
    194 /**
    195   Main entry for Fault Tolerant Write PEIM.
    196 
    197   @param[in]  FileHandle              Handle of the file being invoked.
    198   @param[in]  PeiServices             Pointer to PEI Services table.
    199 
    200   @retval EFI_SUCCESS  If the interface could be successfully installed
    201   @retval Others       Returned from PeiServicesInstallPpi()
    202 
    203 **/
    204 EFI_STATUS
    205 EFIAPI
    206 PeimFaultTolerantWriteInitialize (
    207   IN       EFI_PEI_FILE_HANDLE  FileHandle,
    208   IN CONST EFI_PEI_SERVICES     **PeiServices
    209   )
    210 {
    211   EFI_STATUS                                Status;
    212   EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER   *FtwWorkingBlockHeader;
    213   EFI_FAULT_TOLERANT_WRITE_HEADER           *FtwLastWriteHeader;
    214   EFI_FAULT_TOLERANT_WRITE_RECORD           *FtwLastWriteRecord;
    215   EFI_PHYSICAL_ADDRESS                      WorkSpaceAddress;
    216   UINTN                                     WorkSpaceLength;
    217   EFI_PHYSICAL_ADDRESS                      SpareAreaAddress;
    218   UINTN                                     SpareAreaLength;
    219   EFI_PHYSICAL_ADDRESS                      WorkSpaceInSpareArea;
    220   FAULT_TOLERANT_WRITE_LAST_WRITE_DATA      FtwLastWrite;
    221 
    222   FtwWorkingBlockHeader = NULL;
    223   FtwLastWriteHeader = NULL;
    224   FtwLastWriteRecord = NULL;
    225 
    226   WorkSpaceAddress = (EFI_PHYSICAL_ADDRESS) PcdGet64 (PcdFlashNvStorageFtwWorkingBase64);
    227   if (WorkSpaceAddress == 0) {
    228     WorkSpaceAddress = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageFtwWorkingBase);
    229   }
    230   WorkSpaceLength = (UINTN) PcdGet32 (PcdFlashNvStorageFtwWorkingSize);
    231 
    232   SpareAreaAddress = (EFI_PHYSICAL_ADDRESS) PcdGet64 (PcdFlashNvStorageFtwSpareBase64);
    233   if (SpareAreaAddress == 0) {
    234     SpareAreaAddress = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageFtwSpareBase);
    235   }
    236   SpareAreaLength = (UINTN) PcdGet32 (PcdFlashNvStorageFtwSpareSize);
    237 
    238   //
    239   // The address of FTW working base and spare base must not be 0.
    240   //
    241   ASSERT ((WorkSpaceAddress != 0) && (SpareAreaAddress != 0));
    242 
    243   FtwWorkingBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) (UINTN) WorkSpaceAddress;
    244   if (IsValidWorkSpace (FtwWorkingBlockHeader, WorkSpaceLength)) {
    245     Status = FtwGetLastWriteHeader (
    246                FtwWorkingBlockHeader,
    247                WorkSpaceLength,
    248                &FtwLastWriteHeader
    249                );
    250     if (!EFI_ERROR (Status)) {
    251       Status = FtwGetLastWriteRecord (
    252                  FtwLastWriteHeader,
    253                  &FtwLastWriteRecord
    254                  );
    255     }
    256 
    257     if (!EFI_ERROR (Status)) {
    258       ASSERT (FtwLastWriteRecord != NULL);
    259       if ((FtwLastWriteRecord->SpareComplete == FTW_VALID_STATE) && (FtwLastWriteRecord->DestinationComplete != FTW_VALID_STATE)) {
    260         //
    261         // If FTW last write was still in progress with SpareComplete set and DestinationComplete not set.
    262         // It means the target buffer has been backed up in spare block, then target block has been erased,
    263         // but the target buffer has not been writen in target block from spare block, we need to build
    264         // FAULT_TOLERANT_WRITE_LAST_WRITE_DATA GUID hob to hold the FTW last write data.
    265         //
    266         FtwLastWrite.TargetAddress = (EFI_PHYSICAL_ADDRESS) (UINTN) ((INT64) SpareAreaAddress + FtwLastWriteRecord->RelativeOffset);
    267         FtwLastWrite.SpareAddress = SpareAreaAddress;
    268         FtwLastWrite.Length = SpareAreaLength;
    269         DEBUG ((
    270           EFI_D_INFO,
    271           "FtwPei last write data: TargetAddress - 0x%x SpareAddress - 0x%x Length - 0x%x\n",
    272           (UINTN) FtwLastWrite.TargetAddress,
    273           (UINTN) FtwLastWrite.SpareAddress,
    274           (UINTN) FtwLastWrite.Length));
    275         BuildGuidDataHob (&gEdkiiFaultTolerantWriteGuid, (VOID *) &FtwLastWrite, sizeof (FAULT_TOLERANT_WRITE_LAST_WRITE_DATA));
    276       }
    277     }
    278   } else {
    279     FtwWorkingBlockHeader = NULL;
    280     //
    281     // If the working block workspace is not valid, try to find workspace in the spare block.
    282     //
    283     WorkSpaceInSpareArea = SpareAreaAddress + SpareAreaLength - WorkSpaceLength;
    284     while (WorkSpaceInSpareArea >= SpareAreaAddress) {
    285       if (CompareGuid (&gEdkiiWorkingBlockSignatureGuid, (EFI_GUID *) (UINTN) WorkSpaceInSpareArea)) {
    286         //
    287         // Found the workspace.
    288         //
    289         DEBUG ((EFI_D_INFO, "FtwPei: workspace in spare block is at 0x%x.\n", (UINTN) WorkSpaceInSpareArea));
    290         FtwWorkingBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) (UINTN) WorkSpaceInSpareArea;
    291         break;
    292       }
    293       WorkSpaceInSpareArea = WorkSpaceInSpareArea - sizeof (EFI_GUID);
    294     }
    295     if ((FtwWorkingBlockHeader != NULL) && IsValidWorkSpace (FtwWorkingBlockHeader, WorkSpaceLength)) {
    296       //
    297       // It was workspace self reclaim, build FAULT_TOLERANT_WRITE_LAST_WRITE_DATA GUID hob for it.
    298       //
    299       FtwLastWrite.TargetAddress = WorkSpaceAddress - (WorkSpaceInSpareArea - SpareAreaAddress);
    300       FtwLastWrite.SpareAddress = SpareAreaAddress;
    301       FtwLastWrite.Length = SpareAreaLength;
    302       DEBUG ((
    303         EFI_D_INFO,
    304         "FtwPei last write data: TargetAddress - 0x%x SpareAddress - 0x%x Length - 0x%x\n",
    305         (UINTN) FtwLastWrite.TargetAddress,
    306         (UINTN) FtwLastWrite.SpareAddress,
    307         (UINTN) FtwLastWrite.Length));
    308       BuildGuidDataHob (&gEdkiiFaultTolerantWriteGuid, (VOID *) &FtwLastWrite, sizeof (FAULT_TOLERANT_WRITE_LAST_WRITE_DATA));
    309     } else {
    310       //
    311       // Both are invalid.
    312       //
    313       DEBUG ((EFI_D_ERROR, "FtwPei: Both working and spare block are invalid.\n"));
    314     }
    315   }
    316 
    317   //
    318   // Install gEdkiiFaultTolerantWriteGuid PPI to inform the check for FTW last write data has been done.
    319   //
    320   return PeiServicesInstallPpi (&mPpiListVariable);
    321 }
    322