Home | History | Annotate | Download | only in FaultTolerantWriteDxe
      1 /** @file
      2 
      3   Implement the Fault Tolerant Write (FTW) protocol based on SMM FTW
      4   module.
      5 
      6 Copyright (c) 2011 - 2013, Intel Corporation. All rights reserved. <BR>
      7 This program and the accompanying materials
      8 are licensed and made available under the terms and conditions of the BSD License
      9 which accompanies this distribution.  The full text of the license may be found at
     10 http://opensource.org/licenses/bsd-license.php
     11 
     12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     14 
     15 **/
     16 
     17 #include "FaultTolerantWriteSmmDxe.h"
     18 
     19 EFI_HANDLE                         mHandle                   = NULL;
     20 EFI_SMM_COMMUNICATION_PROTOCOL     *mSmmCommunication        = NULL;
     21 UINTN                              mPrivateDataSize          = 0;
     22 
     23 EFI_FAULT_TOLERANT_WRITE_PROTOCOL  mFaultTolerantWriteDriver = {
     24   FtwGetMaxBlockSize,
     25   FtwAllocate,
     26   FtwWrite,
     27   FtwRestart,
     28   FtwAbort,
     29   FtwGetLastWrite
     30 };
     31 
     32 /**
     33   Initialize the communicate buffer using DataSize and Function number.
     34 
     35   @param[out]      CommunicateBuffer The communicate buffer. Caller should free it after use.
     36   @param[out]      DataPtr           Points to the data in the communicate buffer. Caller should not free it.
     37   @param[in]       DataSize          The payload size.
     38   @param[in]       Function          The function number used to initialize the communicate header.
     39 
     40 **/
     41 VOID
     42 InitCommunicateBuffer (
     43   OUT     VOID                              **CommunicateBuffer,
     44   OUT     VOID                              **DataPtr,
     45   IN      UINTN                             DataSize,
     46   IN      UINTN                             Function
     47   )
     48 {
     49   EFI_SMM_COMMUNICATE_HEADER                *SmmCommunicateHeader;
     50   SMM_FTW_COMMUNICATE_FUNCTION_HEADER       *SmmFtwFunctionHeader;
     51 
     52   //
     53   // The whole buffer size: SMM_COMMUNICATE_HEADER_SIZE + SMM_FTW_COMMUNICATE_HEADER_SIZE + DataSize.
     54   //
     55   SmmCommunicateHeader = AllocateZeroPool (DataSize + SMM_COMMUNICATE_HEADER_SIZE + SMM_FTW_COMMUNICATE_HEADER_SIZE);
     56   ASSERT (SmmCommunicateHeader != NULL);
     57 
     58   //
     59   // Prepare data buffer.
     60   //
     61   CopyGuid (&SmmCommunicateHeader->HeaderGuid, &gEfiSmmFaultTolerantWriteProtocolGuid);
     62   SmmCommunicateHeader->MessageLength = DataSize + SMM_FTW_COMMUNICATE_HEADER_SIZE;
     63 
     64   SmmFtwFunctionHeader = (SMM_FTW_COMMUNICATE_FUNCTION_HEADER *) SmmCommunicateHeader->Data;
     65   SmmFtwFunctionHeader->Function = Function;
     66 
     67   *CommunicateBuffer = SmmCommunicateHeader;
     68   if (DataPtr != NULL) {
     69     *DataPtr = SmmFtwFunctionHeader->Data;
     70   }
     71 }
     72 
     73 
     74 /**
     75   Send the data in communicate buffer to SMI handler and get response.
     76 
     77   @param[in, out]  SmmCommunicateHeader    The communicate buffer.
     78   @param[in]       DataSize                The payload size.
     79 
     80 **/
     81 EFI_STATUS
     82 SendCommunicateBuffer (
     83   IN OUT  EFI_SMM_COMMUNICATE_HEADER        *SmmCommunicateHeader,
     84   IN      UINTN                             DataSize
     85   )
     86 {
     87   EFI_STATUS                                Status;
     88   UINTN                                     CommSize;
     89   SMM_FTW_COMMUNICATE_FUNCTION_HEADER       *SmmFtwFunctionHeader;
     90 
     91   CommSize = DataSize + SMM_COMMUNICATE_HEADER_SIZE + SMM_FTW_COMMUNICATE_HEADER_SIZE;
     92   Status = mSmmCommunication->Communicate (mSmmCommunication, SmmCommunicateHeader, &CommSize);
     93   ASSERT_EFI_ERROR (Status);
     94 
     95   SmmFtwFunctionHeader = (SMM_FTW_COMMUNICATE_FUNCTION_HEADER *) SmmCommunicateHeader->Data;
     96   return  SmmFtwFunctionHeader->ReturnStatus;
     97 }
     98 
     99 
    100 /**
    101   Get the FvbBaseAddress and FvbAttributes from the FVB handle FvbHandle.
    102 
    103   @param[in]   FvbHandle         The handle of FVB protocol that provides services.
    104   @param[out]  FvbBaseAddress    The base address of the FVB attached with FvbHandle.
    105   @param[out]  FvbAttributes     The attributes of the FVB attached with FvbHandle.
    106 
    107   @retval EFI_SUCCESS            The function completed successfully.
    108   @retval Others                 The function could not complete successfully.
    109 
    110 **/
    111 EFI_STATUS
    112 ConvertFvbHandle (
    113   IN  EFI_HANDLE                            FvbHandle,
    114   OUT EFI_PHYSICAL_ADDRESS                  *FvbBaseAddress,
    115   OUT EFI_FVB_ATTRIBUTES_2                  *FvbAttributes
    116   )
    117 {
    118   EFI_STATUS                                Status;
    119   EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL        *Fvb;
    120 
    121   Status = gBS->HandleProtocol (FvbHandle, &gEfiFirmwareVolumeBlockProtocolGuid, (VOID **) &Fvb);
    122   if (EFI_ERROR (Status)) {
    123     return Status;
    124   }
    125 
    126   Status = Fvb->GetPhysicalAddress (Fvb, FvbBaseAddress);
    127   if (EFI_ERROR (Status)) {
    128     return Status;
    129   }
    130 
    131   Status = Fvb->GetAttributes (Fvb, FvbAttributes);
    132   return Status;
    133 }
    134 
    135 
    136 /**
    137   Get the size of the largest block that can be updated in a fault-tolerant manner.
    138 
    139   @param[in]  This             Indicates a pointer to the calling context.
    140   @param[out] BlockSize        A pointer to a caller-allocated UINTN that is
    141                                updated to indicate the size of the largest block
    142                                that can be updated.
    143 
    144   @retval EFI_SUCCESS          The function completed successfully.
    145   @retval EFI_ABORTED          The function could not complete successfully.
    146 
    147 **/
    148 EFI_STATUS
    149 EFIAPI
    150 FtwGetMaxBlockSize (
    151   IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL      *This,
    152   OUT UINTN                                 *BlockSize
    153   )
    154 {
    155   EFI_STATUS                                Status;
    156   UINTN                                     PayloadSize;
    157   EFI_SMM_COMMUNICATE_HEADER                *SmmCommunicateHeader;
    158   SMM_FTW_GET_MAX_BLOCK_SIZE_HEADER         *SmmFtwBlockSizeHeader;
    159 
    160   //
    161   // Initialize the communicate buffer.
    162   //
    163   PayloadSize  = sizeof (SMM_FTW_GET_MAX_BLOCK_SIZE_HEADER);
    164   InitCommunicateBuffer ((VOID **)&SmmCommunicateHeader, (VOID **)&SmmFtwBlockSizeHeader, PayloadSize, FTW_FUNCTION_GET_MAX_BLOCK_SIZE);
    165 
    166   //
    167   // Send data to SMM.
    168   //
    169   Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize);
    170 
    171   //
    172   // Get data from SMM
    173   //
    174   *BlockSize = SmmFtwBlockSizeHeader->BlockSize;
    175   FreePool (SmmCommunicateHeader);
    176 
    177   return Status;
    178 }
    179 
    180 
    181 /**
    182   Allocates space for the protocol to maintain information about writes.
    183   Since writes must be completed in a fault-tolerant manner and multiple
    184   writes require more resources to be successful, this function
    185   enables the protocol to ensure that enough space exists to track
    186   information about upcoming writes.
    187 
    188   @param[in]  This             A pointer to the calling context.
    189   @param[in]  CallerId         The GUID identifying the write.
    190   @param[in]  PrivateDataSize  The size of the caller's private data  that must be
    191                                recorded for each write.
    192   @param[in]  NumberOfWrites   The number of fault tolerant block writes that will
    193                                need to occur.
    194 
    195   @retval EFI_SUCCESS          The function completed successfully
    196   @retval EFI_ABORTED          The function could not complete successfully.
    197   @retval EFI_ACCESS_DENIED    Not all allocated writes have been completed.  All
    198                                writes must be completed or aborted before another
    199                                fault tolerant write can occur.
    200 
    201 **/
    202 EFI_STATUS
    203 EFIAPI
    204 FtwAllocate (
    205   IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL      *This,
    206   IN EFI_GUID                               *CallerId,
    207   IN UINTN                                  PrivateDataSize,
    208   IN UINTN                                  NumberOfWrites
    209   )
    210 {
    211   EFI_STATUS                                Status;
    212   UINTN                                     PayloadSize;
    213   EFI_SMM_COMMUNICATE_HEADER                *SmmCommunicateHeader;
    214   SMM_FTW_ALLOCATE_HEADER                   *SmmFtwAllocateHeader;
    215 
    216   //
    217   // Initialize the communicate buffer.
    218   //
    219   PayloadSize  = sizeof (SMM_FTW_ALLOCATE_HEADER);
    220   InitCommunicateBuffer ((VOID **)&SmmCommunicateHeader, (VOID **)&SmmFtwAllocateHeader, PayloadSize, FTW_FUNCTION_ALLOCATE);
    221   CopyGuid (&SmmFtwAllocateHeader->CallerId, CallerId);
    222   SmmFtwAllocateHeader->PrivateDataSize = PrivateDataSize;
    223   SmmFtwAllocateHeader->NumberOfWrites  = NumberOfWrites;
    224 
    225   //
    226   // Send data to SMM.
    227   //
    228   Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize);
    229   if (!EFI_ERROR( Status)) {
    230     mPrivateDataSize = PrivateDataSize;
    231   }
    232 
    233   FreePool (SmmCommunicateHeader);
    234   return Status;
    235 }
    236 
    237 
    238 /**
    239   Starts a target block update. This records information about the write
    240   in fault tolerant storage, and will complete the write in a recoverable
    241   manner, ensuring at all times that either the original contents or
    242   the modified contents are available.
    243 
    244   @param[in]  This             The calling context.
    245   @param[in]  Lba              The logical block address of the target block.
    246   @param[in]  Offset           The offset within the target block to place the
    247                                data.
    248   @param[in]  Length           The number of bytes to write to the target block.
    249   @param[in]  PrivateData      A pointer to private data that the caller requires
    250                                to complete any pending writes in the event of a
    251                                fault.
    252   @param[in]  FvBlockHandle    The handle of FVB protocol that provides services
    253                                for reading, writing, and erasing the target block.
    254   @param[in]  Buffer           The data to write.
    255 
    256   @retval EFI_SUCCESS          The function completed successfully.
    257   @retval EFI_ABORTED          The function could not complete successfully.
    258   @retval EFI_BAD_BUFFER_SIZE  The write would span a block boundary, which is not
    259                                a valid action.
    260   @retval EFI_ACCESS_DENIED    No writes have been allocated.
    261   @retval EFI_NOT_READY        The last write has not been completed. Restart()
    262                                must be called to complete it.
    263 
    264 **/
    265 EFI_STATUS
    266 EFIAPI
    267 FtwWrite (
    268   IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL      *This,
    269   IN EFI_LBA                                Lba,
    270   IN UINTN                                  Offset,
    271   IN UINTN                                  Length,
    272   IN VOID                                   *PrivateData,
    273   IN EFI_HANDLE                             FvBlockHandle,
    274   IN VOID                                   *Buffer
    275   )
    276 {
    277   EFI_STATUS                                Status;
    278   UINTN                                     PayloadSize;
    279   EFI_SMM_COMMUNICATE_HEADER                *SmmCommunicateHeader;
    280   SMM_FTW_WRITE_HEADER                      *SmmFtwWriteHeader;
    281 
    282   //
    283   // Initialize the communicate buffer.
    284   //
    285   PayloadSize  = OFFSET_OF (SMM_FTW_WRITE_HEADER, Data) + Length;
    286   if (PrivateData != NULL) {
    287     //
    288     // The private data buffer size should be the same one in FtwAllocate API.
    289     //
    290     PayloadSize += mPrivateDataSize;
    291   }
    292   InitCommunicateBuffer ((VOID **)&SmmCommunicateHeader, (VOID **)&SmmFtwWriteHeader, PayloadSize, FTW_FUNCTION_WRITE);
    293 
    294   //
    295   // FvBlockHandle can not be used in SMM environment. Here we get the FVB protocol first, then get FVB base address
    296   // and its attribute. Send these information to SMM handler, the SMM handler will find the proper FVB to write data.
    297   //
    298   Status = ConvertFvbHandle (FvBlockHandle, &SmmFtwWriteHeader->FvbBaseAddress, &SmmFtwWriteHeader->FvbAttributes);
    299   if (EFI_ERROR (Status)) {
    300     FreePool (SmmCommunicateHeader);
    301     return EFI_ABORTED;
    302   }
    303 
    304   SmmFtwWriteHeader->Lba    = Lba;
    305   SmmFtwWriteHeader->Offset = Offset;
    306   SmmFtwWriteHeader->Length = Length;
    307   CopyMem (SmmFtwWriteHeader->Data, Buffer, Length);
    308   if (PrivateData == NULL) {
    309     SmmFtwWriteHeader->PrivateDataSize = 0;
    310   } else {
    311     SmmFtwWriteHeader->PrivateDataSize = mPrivateDataSize;
    312     CopyMem (&SmmFtwWriteHeader->Data[Length], PrivateData, mPrivateDataSize);
    313   }
    314 
    315   //
    316   // Send data to SMM.
    317   //
    318   Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize);
    319   FreePool (SmmCommunicateHeader);
    320   return Status;
    321 }
    322 
    323 
    324 /**
    325   Restarts a previously interrupted write. The caller must provide the
    326   block protocol needed to complete the interrupted write.
    327 
    328   @param[in]  This             The calling context.
    329   @param[in]  FvBlockHandle    The handle of FVB protocol that provides services.
    330 
    331   @retval EFI_SUCCESS          The function completed successfully.
    332   @retval EFI_ABORTED          The function could not complete successfully.
    333   @retval EFI_ACCESS_DENIED    No pending writes exist.
    334 
    335 **/
    336 EFI_STATUS
    337 EFIAPI
    338 FtwRestart (
    339   IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL      *This,
    340   IN EFI_HANDLE                             FvBlockHandle
    341   )
    342 {
    343   EFI_STATUS                                Status;
    344   UINTN                                     PayloadSize;
    345   EFI_SMM_COMMUNICATE_HEADER                *SmmCommunicateHeader;
    346   SMM_FTW_RESTART_HEADER                    *SmmFtwRestartHeader;
    347 
    348   //
    349   // Initialize the communicate buffer.
    350   //
    351   PayloadSize  = sizeof (SMM_FTW_RESTART_HEADER);
    352   InitCommunicateBuffer ((VOID **)&SmmCommunicateHeader, (VOID **)&SmmFtwRestartHeader, PayloadSize, FTW_FUNCTION_RESTART);
    353 
    354   //
    355   // FvBlockHandle can not be used in SMM environment. Here we get the FVB protocol first, then get FVB base address
    356   // and its attribute. Send these information to SMM handler, the SMM handler will find the proper FVB to write data.
    357   //
    358   Status = ConvertFvbHandle (FvBlockHandle, &SmmFtwRestartHeader->FvbBaseAddress, &SmmFtwRestartHeader->FvbAttributes);
    359   if (EFI_ERROR (Status)) {
    360     FreePool (SmmCommunicateHeader);
    361     return EFI_ABORTED;
    362   }
    363 
    364   //
    365   // Send data to SMM.
    366   //
    367   Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize);
    368   FreePool (SmmCommunicateHeader);
    369   return Status;
    370 }
    371 
    372 
    373 /**
    374   Aborts all previously allocated writes.
    375 
    376   @param[in]  This             The calling context.
    377 
    378   @retval EFI_SUCCESS          The function completed successfully.
    379   @retval EFI_ABORTED          The function could not complete successfully.
    380   @retval EFI_NOT_FOUND        No allocated writes exist.
    381 
    382 **/
    383 EFI_STATUS
    384 EFIAPI
    385 FtwAbort (
    386   IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL      *This
    387   )
    388 {
    389   EFI_STATUS                                Status;
    390   EFI_SMM_COMMUNICATE_HEADER                *SmmCommunicateHeader;
    391 
    392   //
    393   // Initialize the communicate buffer.
    394   //
    395   InitCommunicateBuffer ((VOID **)&SmmCommunicateHeader, NULL, 0, FTW_FUNCTION_ABORT);
    396 
    397   //
    398   // Send data to SMM.
    399   //
    400   Status = SendCommunicateBuffer (SmmCommunicateHeader, 0);
    401 
    402   FreePool (SmmCommunicateHeader);
    403   return Status;
    404 }
    405 
    406 
    407 /**
    408   Starts a target block update. This function records information about the write
    409   in fault-tolerant storage and completes the write in a recoverable
    410   manner, ensuring at all times that either the original contents or
    411   the modified contents are available.
    412 
    413   @param[in]      This            Indicates a pointer to the calling context.
    414   @param[out]     CallerId        The GUID identifying the last write.
    415   @param[out]     Lba             The logical block address of the last write.
    416   @param[out]     Offset          The offset within the block of the last write.
    417   @param[out]     Length          The length of the last write.
    418   @param[in, out] PrivateDataSize On input, the size of the PrivateData buffer. On
    419                                   output, the size of the private data stored for
    420                                   this write.
    421   @param[out]     PrivateData     A pointer to a buffer. The function will copy
    422                                   PrivateDataSize bytes from the private data stored
    423                                   for this write.
    424   @param[out]     Complete        A Boolean value with TRUE indicating that the write
    425                                   was completed.
    426 
    427   @retval EFI_SUCCESS             The function completed successfully.
    428   @retval EFI_ABORTED             The function could not complete successfully.
    429   @retval EFI_NOT_FOUND           No allocated writes exist.
    430 
    431 **/
    432 EFI_STATUS
    433 EFIAPI
    434 FtwGetLastWrite (
    435   IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL      *This,
    436   OUT EFI_GUID                              *CallerId,
    437   OUT EFI_LBA                               *Lba,
    438   OUT UINTN                                 *Offset,
    439   OUT UINTN                                 *Length,
    440   IN OUT UINTN                              *PrivateDataSize,
    441   OUT VOID                                  *PrivateData,
    442   OUT BOOLEAN                               *Complete
    443   )
    444 {
    445   EFI_STATUS                                Status;
    446   UINTN                                     PayloadSize;
    447   EFI_SMM_COMMUNICATE_HEADER                *SmmCommunicateHeader;
    448   SMM_FTW_GET_LAST_WRITE_HEADER             *SmmFtwGetLastWriteHeader;
    449 
    450   //
    451   // Initialize the communicate buffer.
    452   //
    453   PayloadSize  = OFFSET_OF (SMM_FTW_GET_LAST_WRITE_HEADER, Data) + *PrivateDataSize;
    454   InitCommunicateBuffer ((VOID **)&SmmCommunicateHeader, (VOID **)&SmmFtwGetLastWriteHeader, PayloadSize, FTW_FUNCTION_GET_LAST_WRITE);
    455   SmmFtwGetLastWriteHeader->PrivateDataSize = *PrivateDataSize;
    456 
    457   //
    458   // Send data to SMM.
    459   //
    460   Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize);
    461 
    462   //
    463   // Get data from SMM
    464   //
    465   *PrivateDataSize = SmmFtwGetLastWriteHeader->PrivateDataSize;
    466   if (Status == EFI_SUCCESS || Status == EFI_BUFFER_TOO_SMALL) {
    467     *Lba      = SmmFtwGetLastWriteHeader->Lba;
    468     *Offset   = SmmFtwGetLastWriteHeader->Offset;
    469     *Length   = SmmFtwGetLastWriteHeader->Length;
    470     *Complete = SmmFtwGetLastWriteHeader->Complete;
    471     CopyGuid (CallerId, &SmmFtwGetLastWriteHeader->CallerId);
    472     if (Status == EFI_SUCCESS) {
    473       CopyMem (PrivateData, SmmFtwGetLastWriteHeader->Data, *PrivateDataSize);
    474     }
    475   } else if (Status == EFI_NOT_FOUND) {
    476     *Complete = SmmFtwGetLastWriteHeader->Complete;
    477   }
    478 
    479   FreePool (SmmCommunicateHeader);
    480   return Status;
    481 }
    482 
    483 /**
    484   SMM Fault Tolerant Write Protocol notification event handler.
    485 
    486   Install Fault Tolerant Write Protocol.
    487 
    488   @param[in] Event    Event whose notification function is being invoked.
    489   @param[in] Context  Pointer to the notification function's context.
    490 **/
    491 VOID
    492 EFIAPI
    493 SmmFtwReady (
    494   IN  EFI_EVENT                             Event,
    495   IN  VOID                                  *Context
    496   )
    497 {
    498   EFI_STATUS                                Status;
    499   EFI_FAULT_TOLERANT_WRITE_PROTOCOL         *FtwProtocol;
    500 
    501   //
    502   // Just return to avoid install SMM FaultTolerantWriteProtocol again
    503   // if Fault Tolerant Write protocol had been installed.
    504   //
    505   Status = gBS->LocateProtocol (&gEfiFaultTolerantWriteProtocolGuid, NULL, (VOID **)&FtwProtocol);
    506   if (!EFI_ERROR (Status)) {
    507     return;
    508   }
    509 
    510   Status = gBS->LocateProtocol (&gEfiSmmCommunicationProtocolGuid, NULL, (VOID **) &mSmmCommunication);
    511   ASSERT_EFI_ERROR (Status);
    512 
    513   //
    514   // Install protocol interface
    515   //
    516   Status = gBS->InstallProtocolInterface (
    517                   &mHandle,
    518                   &gEfiFaultTolerantWriteProtocolGuid,
    519                   EFI_NATIVE_INTERFACE,
    520                   &mFaultTolerantWriteDriver
    521                   );
    522   ASSERT_EFI_ERROR (Status);
    523 
    524   Status = gBS->CloseEvent (Event);
    525   ASSERT_EFI_ERROR (Status);
    526 }
    527 
    528 
    529 /**
    530   The driver entry point for Fault Tolerant Write driver.
    531 
    532   The function does the necessary initialization work.
    533 
    534   @param[in]  ImageHandle       The firmware allocated handle for the UEFI image.
    535   @param[in]  SystemTable       A pointer to the EFI system table.
    536 
    537   @retval     EFI_SUCCESS       This funtion always return EFI_SUCCESS.
    538 
    539 **/
    540 EFI_STATUS
    541 EFIAPI
    542 FaultTolerantWriteSmmInitialize (
    543   IN EFI_HANDLE                             ImageHandle,
    544   IN EFI_SYSTEM_TABLE                       *SystemTable
    545   )
    546 {
    547   VOID                                      *SmmFtwRegistration;
    548 
    549   //
    550   // Smm FTW driver is ready
    551   //
    552   EfiCreateProtocolNotifyEvent (
    553     &gEfiSmmFaultTolerantWriteProtocolGuid,
    554     TPL_CALLBACK,
    555     SmmFtwReady,
    556     NULL,
    557     &SmmFtwRegistration
    558     );
    559 
    560   return EFI_SUCCESS;
    561 }
    562 
    563