Home | History | Annotate | Download | only in FvbRuntimeDxe
      1 /** @file
      2 
      3   Implement the Firmware Volume Block (FVB) services based on SMM FVB
      4   module and install FVB protocol.
      5 
      6 Copyright (c) 2010  - 2014, Intel Corporation. All rights reserved. <BR>
      7 
      8   This program and the accompanying materials are licensed and made available under
     10   the terms and conditions of the BSD License that accompanies this distribution.
     12   The full text of the license may be found at
     14   http://opensource.org/licenses/bsd-license.php.
     16 
     18   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     20   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     22 
     24 
     26 **/
     27 
     28 #include "FvbSmmDxe.h"
     29 
     30 EFI_HANDLE                       mHandle           = NULL;
     31 EFI_SMM_COMMUNICATION_PROTOCOL  *mSmmCommunication = NULL;
     32 
     33 //
     34 // Template structure used when installing FVB protocol.
     35 //
     36 EFI_FVB_DEVICE    mFvbDeviceTemplate = {
     37   FVB_DEVICE_SIGNATURE,
     38   NULL,
     39   {
     40     FvbGetAttributes,
     41     FvbSetAttributes,
     42     FvbGetPhysicalAddress,
     43     FvbGetBlockSize,
     44     FvbRead,
     45     FvbWrite,
     46     FvbEraseBlocks,
     47     NULL
     48   },
     49   NULL
     50 };
     51 
     52 FV_MEMMAP_DEVICE_PATH mFvMemmapDevicePathTemplate = {
     53   {
     54     {
     55       HARDWARE_DEVICE_PATH,
     56       HW_MEMMAP_DP,
     57       {
     58         (UINT8)(sizeof (MEMMAP_DEVICE_PATH)),
     59         (UINT8)(sizeof (MEMMAP_DEVICE_PATH) >> 8)
     60       }
     61     },
     62     EfiMemoryMappedIO,
     63     (EFI_PHYSICAL_ADDRESS) 0,
     64     (EFI_PHYSICAL_ADDRESS) 0,
     65   },
     66   {
     67     END_DEVICE_PATH_TYPE,
     68     END_ENTIRE_DEVICE_PATH_SUBTYPE,
     69     {
     70       END_DEVICE_PATH_LENGTH,
     71       0
     72     }
     73   }
     74 };
     75 
     76 FV_PIWG_DEVICE_PATH mFvPIWGDevicePathTemplate = {
     77   {
     78     {
     79       MEDIA_DEVICE_PATH,
     80       MEDIA_PIWG_FW_VOL_DP,
     81       {
     82         (UINT8)(sizeof (MEDIA_FW_VOL_DEVICE_PATH)),
     83         (UINT8)(sizeof (MEDIA_FW_VOL_DEVICE_PATH) >> 8)
     84       }
     85     },
     86     { 0 }
     87   },
     88   {
     89     END_DEVICE_PATH_TYPE,
     90     END_ENTIRE_DEVICE_PATH_SUBTYPE,
     91     {
     92       END_DEVICE_PATH_LENGTH,
     93       0
     94     }
     95   }
     96 };
     97 
     98 /**
     99   Initialize the communicate buffer using DataSize and Function.
    100 
    101   The communicate size is: SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE +
    102   DataSize.
    103 
    104   @param[out]      CommunicateBuffer The communicate buffer. Caller should free it after use.
    105   @param[out]      DataPtr           Points to the data in the communicate buffer. Caller should not free it.
    106   @param[in]       DataSize          The payload size.
    107   @param[in]       Function          The function number used to initialize the communicate header.
    108 
    109   @retval EFI_INVALID_PARAMETER      The data size is too big.
    110   @retval EFI_SUCCESS                Find the specified variable.
    111 
    112 **/
    113 EFI_STATUS
    114 InitCommunicateBuffer (
    115   OUT     VOID                              **CommunicateBuffer,
    116   OUT     VOID                              **DataPtr,
    117   IN      UINTN                             DataSize,
    118   IN      UINTN                             Function
    119   )
    120 {
    121   EFI_SMM_COMMUNICATE_HEADER                *SmmCommunicateHeader;
    122   SMM_FVB_COMMUNICATE_FUNCTION_HEADER       *SmmFvbFunctionHeader;
    123 
    124   //
    125   // The whole buffer size: SMM_COMMUNICATE_HEADER_SIZE + SMM_FVB_COMMUNICATE_HEADER_SIZE + DataSize.
    126   //
    127   SmmCommunicateHeader = AllocatePool (DataSize + SMM_COMMUNICATE_HEADER_SIZE + SMM_FVB_COMMUNICATE_HEADER_SIZE);
    128   ASSERT (SmmCommunicateHeader != NULL);
    129 
    130   //
    131   // Prepare data buffer.
    132   //
    133   CopyGuid (&SmmCommunicateHeader->HeaderGuid, &gEfiSmmFirmwareVolumeBlockProtocolGuid);
    134   SmmCommunicateHeader->MessageLength = DataSize + SMM_FVB_COMMUNICATE_HEADER_SIZE;
    135 
    136   SmmFvbFunctionHeader = (SMM_FVB_COMMUNICATE_FUNCTION_HEADER *) SmmCommunicateHeader->Data;
    137   SmmFvbFunctionHeader->Function = Function;
    138 
    139   *CommunicateBuffer = SmmCommunicateHeader;
    140   *DataPtr = SmmFvbFunctionHeader->Data;
    141 
    142   return EFI_SUCCESS;
    143 }
    144 
    145 
    146 /**
    147   Send the data in communicate buffer to SMM.
    148 
    149   @param[out]      SmmCommunicateHeader    The communicate buffer.
    150   @param[in]       DataSize                The payload size.
    151 
    152 **/
    153 EFI_STATUS
    154 SendCommunicateBuffer (
    155   IN      EFI_SMM_COMMUNICATE_HEADER        *SmmCommunicateHeader,
    156   IN      UINTN                             DataSize
    157   )
    158 {
    159   EFI_STATUS                                Status;
    160   UINTN                                     CommSize;
    161   SMM_FVB_COMMUNICATE_FUNCTION_HEADER       *SmmFvbFunctionHeader;
    162 
    163   CommSize = DataSize + SMM_COMMUNICATE_HEADER_SIZE + SMM_FVB_COMMUNICATE_HEADER_SIZE;
    164   Status = mSmmCommunication->Communicate (
    165                                 mSmmCommunication,
    166                                 SmmCommunicateHeader,
    167                                 &CommSize
    168                                 );
    169   ASSERT_EFI_ERROR (Status);
    170 
    171   SmmFvbFunctionHeader = (SMM_FVB_COMMUNICATE_FUNCTION_HEADER *) SmmCommunicateHeader->Data;
    172   return  SmmFvbFunctionHeader->ReturnStatus;
    173 }
    174 
    175 /**
    176   This function retrieves the attributes and current settings of the block.
    177 
    178   @param[in]  This       Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance.
    179 
    180   @param[out] Attributes Pointer to EFI_FVB_ATTRIBUTES_2 in which the attributes
    181                          and current settings are returned. Type EFI_FVB_ATTRIBUTES_2
    182                          is defined in EFI_FIRMWARE_VOLUME_HEADER.
    183 
    184   @retval EFI_SUCCESS              The firmware volume attributes were returned.
    185   @retval EFI_INVALID_PARAMETER    Attributes is NULL.
    186 **/
    187 EFI_STATUS
    188 EFIAPI
    189 FvbGetAttributes (
    190   IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL   *This,
    191      OUT   EFI_FVB_ATTRIBUTES_2                 *Attributes
    192   )
    193 {
    194   EFI_STATUS                                    Status;
    195   UINTN                                         PayloadSize;
    196   EFI_SMM_COMMUNICATE_HEADER                    *SmmCommunicateHeader;
    197   SMM_FVB_ATTRIBUTES_HEADER                     *SmmFvbAttributesHeader;
    198   EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL        *SmmFvb;
    199   EFI_FVB_DEVICE                                *FvbDevice;
    200 
    201   if (Attributes == NULL) {
    202     return EFI_INVALID_PARAMETER;
    203   }
    204 
    205   FvbDevice = FVB_DEVICE_FROM_THIS (This);
    206   SmmFvb    = FvbDevice->SmmFvbInstance;
    207 
    208   //
    209   // Initialize the communicate buffer.
    210   //
    211   PayloadSize  = sizeof (SMM_FVB_ATTRIBUTES_HEADER);
    212   Status = InitCommunicateBuffer (
    213              (VOID **)&SmmCommunicateHeader,
    214              (VOID **)&SmmFvbAttributesHeader,
    215              PayloadSize,
    216              EFI_FUNCTION_GET_ATTRIBUTES
    217              );
    218   if (EFI_ERROR (Status)) {
    219     return Status;
    220   }
    221 
    222   SmmFvbAttributesHeader->SmmFvb     = SmmFvb;
    223   SmmFvbAttributesHeader->Attributes = 0;
    224 
    225   //
    226   // Send data to SMM.
    227   //
    228   Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize);
    229 
    230   //
    231   // Get data from SMM.
    232   //
    233   *Attributes = SmmFvbAttributesHeader->Attributes;
    234   FreePool (SmmCommunicateHeader);
    235 
    236   return Status;
    237 }
    238 
    239 
    240 /**
    241   Sets Volume attributes. No polarity translations are done.
    242 
    243   @param[in]  This        Calling context.
    244   @param[out] Attributes  Output buffer which contains attributes.
    245 
    246   @retval     EFI_SUCCESS              Set the Attributes successfully.
    247   @retval     EFI_INVALID_PARAMETER    Attributes is NULL.
    248 
    249 **/
    250 EFI_STATUS
    251 EFIAPI
    252 FvbSetAttributes (
    253   IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL   *This,
    254   IN OUT   EFI_FVB_ATTRIBUTES_2                 *Attributes
    255   )
    256 {
    257   EFI_STATUS                                    Status;
    258   UINTN                                         PayloadSize;
    259   EFI_SMM_COMMUNICATE_HEADER                    *SmmCommunicateHeader;
    260   SMM_FVB_ATTRIBUTES_HEADER                     *SmmFvbAttributesHeader;
    261   EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL        *SmmFvb;
    262   EFI_FVB_DEVICE                                *FvbDevice;
    263 
    264   if (Attributes == NULL) {
    265     return EFI_INVALID_PARAMETER;
    266   }
    267 
    268   FvbDevice = FVB_DEVICE_FROM_THIS (This);
    269   SmmFvb    = FvbDevice->SmmFvbInstance;
    270 
    271   //
    272   // Initialize the communicate buffer.
    273   //
    274   PayloadSize  = sizeof (SMM_FVB_ATTRIBUTES_HEADER);
    275   Status = InitCommunicateBuffer (
    276              (VOID **)&SmmCommunicateHeader,
    277              (VOID **)&SmmFvbAttributesHeader,
    278              PayloadSize,
    279              EFI_FUNCTION_SET_ATTRIBUTES
    280              );
    281   if (EFI_ERROR (Status)) {
    282     return Status;
    283   }
    284 
    285   SmmFvbAttributesHeader->SmmFvb     = SmmFvb;
    286   SmmFvbAttributesHeader->Attributes = *Attributes;
    287 
    288   //
    289   // Send data to SMM.
    290   //
    291   Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize);
    292 
    293   //
    294   // Get data from SMM.
    295   //
    296   *Attributes = SmmFvbAttributesHeader->Attributes;
    297   FreePool (SmmCommunicateHeader);
    298 
    299   return Status;
    300 }
    301 
    302 
    303 /**
    304   Retrieves the physical address of the FVB instance.
    305 
    306   @param[in]  SmmFvb         A pointer to EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL.
    307   @param[out] Address        Output buffer containing the address.
    308 
    309   @retval     EFI_SUCCESS    Get the address successfully.
    310   @retval     Others         Failed to get address.
    311 
    312 **/
    313 EFI_STATUS
    314 GetPhysicalAddress (
    315   IN   EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *SmmFvb,
    316   OUT  EFI_PHYSICAL_ADDRESS                    *Address
    317   )
    318 {
    319   EFI_STATUS                                   Status;
    320   UINTN                                        PayloadSize;
    321   EFI_SMM_COMMUNICATE_HEADER                   *SmmCommunicateHeader;
    322   SMM_FVB_PHYSICAL_ADDRESS_HEADER              *SmmFvbPhysicalAddressHeader;
    323 
    324   //
    325   // Initialize the communicate buffer.
    326   //
    327   PayloadSize  = sizeof (SMM_FVB_PHYSICAL_ADDRESS_HEADER);
    328   Status = InitCommunicateBuffer (
    329              (VOID **)&SmmCommunicateHeader,
    330              (VOID **)&SmmFvbPhysicalAddressHeader,
    331              PayloadSize,
    332              EFI_FUNCTION_GET_PHYSICAL_ADDRESS
    333              );
    334   if (EFI_ERROR (Status)) {
    335     return Status;
    336   }
    337 
    338   SmmFvbPhysicalAddressHeader->SmmFvb  = SmmFvb;
    339   SmmFvbPhysicalAddressHeader->Address = 0;
    340 
    341   //
    342   // Send data to SMM.
    343   //
    344   Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize);
    345 
    346   //
    347   // Get data from SMM.
    348   //
    349   *Address = SmmFvbPhysicalAddressHeader->Address;
    350   FreePool (SmmCommunicateHeader);
    351 
    352   return Status;
    353 }
    354 
    355 
    356 /**
    357   Retrieves the physical address of the FVB instance.
    358 
    359   @param[in]  This                     A pointer to EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL.
    360   @param[out] Address                  Output buffer containing the address.
    361 
    362   @retval     EFI_SUCCESS              Get the address successfully.
    363   @retval     Others                   Failed to get the address.
    364 
    365 **/
    366 EFI_STATUS
    367 EFIAPI
    368 FvbGetPhysicalAddress (
    369   IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
    370      OUT   EFI_PHYSICAL_ADDRESS                *Address
    371   )
    372 {
    373   EFI_STATUS                                   Status;
    374   EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL       *SmmFvb;
    375   EFI_FVB_DEVICE                               *FvbDevice;
    376 
    377   if (Address == NULL) {
    378     return EFI_INVALID_PARAMETER;
    379   }
    380 
    381   FvbDevice = FVB_DEVICE_FROM_THIS (This);
    382   SmmFvb    = FvbDevice->SmmFvbInstance;
    383 
    384   Status = GetPhysicalAddress (SmmFvb, Address);
    385 
    386   return Status;
    387 }
    388 
    389 
    390 /**
    391   Retrieve the size of a logical block.
    392 
    393   @param[in]  This        Calling context.
    394   @param[in]  Lba         Indicates which block to return the size for.
    395   @param[out] BlockSize   A pointer to a caller allocated UINTN in which
    396                           the size of the block is returned.
    397   @param[out] NumOfBlocks A pointer to a caller allocated UINTN in which the
    398                           number of consecutive blocks starting with Lba is
    399                           returned. All blocks in this range have a size of
    400                           BlockSize.
    401 
    402   @retval     EFI_SUCCESS              Get BlockSize and NumOfBlocks successfully.
    403   @retval     EFI_INVALID_PARAMETER    BlockSize or NumOfBlocks are NULL.
    404 **/
    405 EFI_STATUS
    406 EFIAPI
    407 FvbGetBlockSize (
    408   IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
    409   IN       EFI_LBA                             Lba,
    410      OUT   UINTN                               *BlockSize,
    411      OUT   UINTN                               *NumOfBlocks
    412   )
    413 {
    414   EFI_STATUS                                   Status;
    415   UINTN                                        PayloadSize;
    416   EFI_SMM_COMMUNICATE_HEADER                   *SmmCommunicateHeader;
    417   SMM_FVB_BLOCK_SIZE_HEADER                    *SmmFvbBlockSizeHeader;
    418   EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL       *SmmFvb;
    419   EFI_FVB_DEVICE                               *FvbDevice;
    420 
    421   if ((BlockSize == NULL) || (NumOfBlocks == NULL)) {
    422     return EFI_INVALID_PARAMETER;
    423   }
    424 
    425   FvbDevice = FVB_DEVICE_FROM_THIS (This);
    426   SmmFvb    = FvbDevice->SmmFvbInstance;
    427 
    428   //
    429   // Initialize the communicate buffer.
    430   //
    431   PayloadSize  = sizeof (SMM_FVB_BLOCK_SIZE_HEADER);
    432   Status = InitCommunicateBuffer (
    433              (VOID **)&SmmCommunicateHeader,
    434              (VOID **)&SmmFvbBlockSizeHeader,
    435              PayloadSize,
    436              EFI_FUNCTION_GET_BLOCK_SIZE
    437              );
    438   if (EFI_ERROR (Status)) {
    439     return Status;
    440   }
    441 
    442   SmmFvbBlockSizeHeader->SmmFvb = SmmFvb;
    443   SmmFvbBlockSizeHeader->Lba    = Lba;
    444 
    445   //
    446   // Send data to SMM.
    447   //
    448   Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize);
    449 
    450   //
    451   // Get data from SMM.
    452   //
    453   *BlockSize   = SmmFvbBlockSizeHeader->BlockSize;
    454   *NumOfBlocks = SmmFvbBlockSizeHeader->NumOfBlocks;
    455   FreePool (SmmCommunicateHeader);
    456 
    457   return Status;
    458 }
    459 
    460 
    461 /**
    462   Reads data beginning at Lba:Offset from FV. The Read terminates either
    463   when *NumBytes of data have been read, or when a block boundary is
    464   reached.  *NumBytes is updated to reflect the actual number of bytes
    465   written. The write opertion does not include erase. This routine will
    466   attempt to write only the specified bytes. If the writes do not stick,
    467   it will return an error.
    468 
    469   @param[in]      This           Calling context
    470   @param[in]      Lba            Block in which to begin write
    471   @param[in]      Offset         Offset in the block at which to begin write
    472   @param[in,out]  NumBytes       On input, indicates the requested write size. On
    473                                  output, indicates the actual number of bytes written
    474   @param[in]      Buffer         Buffer containing source data for the write.
    475 
    476   @retval EFI_SUCCESS            The firmware volume was read successfully and
    477                                  contents are in Buffer.
    478   @retval EFI_BAD_BUFFER_SIZE    Read attempted across a LBA boundary. On output,
    479                                  NumBytes contains the total number of bytes returned
    480                                  in Buffer.
    481   @retval EFI_ACCESS_DENIED      The firmware volume is in the ReadDisabled state
    482   @retval EFI_DEVICE_ERROR       The block device is not functioning correctly and
    483                                  could not be read.
    484   @retval EFI_INVALID_PARAMETER  NumBytes or Buffer are NULL.
    485 
    486 **/
    487 EFI_STATUS
    488 EFIAPI
    489 FvbRead (
    490   IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL   *This,
    491   IN       EFI_LBA                              Lba,
    492   IN       UINTN                                Offset,
    493   IN OUT   UINTN                                *NumBytes,
    494      OUT   UINT8                                *Buffer
    495   )
    496 {
    497   EFI_STATUS                                    Status;
    498   UINTN                                         PayloadSize;
    499   EFI_SMM_COMMUNICATE_HEADER                    *SmmCommunicateHeader;
    500   SMM_FVB_READ_WRITE_HEADER                     *SmmFvbReadWriteHeader;
    501   EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL        *SmmFvb;
    502   EFI_FVB_DEVICE                                *FvbDevice;
    503 
    504   if ((NumBytes == NULL) || (Buffer == NULL)) {
    505     return EFI_INVALID_PARAMETER;
    506   }
    507 
    508   FvbDevice = FVB_DEVICE_FROM_THIS (This);
    509   SmmFvb    = FvbDevice->SmmFvbInstance;
    510 
    511   //
    512   // Initialize the communicate buffer.
    513   //
    514   PayloadSize  = sizeof (SMM_FVB_READ_WRITE_HEADER) + *NumBytes;
    515   Status = InitCommunicateBuffer (
    516              (VOID **)&SmmCommunicateHeader,
    517              (VOID **)&SmmFvbReadWriteHeader,
    518              PayloadSize, EFI_FUNCTION_READ
    519              );
    520   if (EFI_ERROR (Status)) {
    521     return Status;
    522   }
    523 
    524   SmmFvbReadWriteHeader->SmmFvb   = SmmFvb;
    525   SmmFvbReadWriteHeader->Lba      = Lba;
    526   SmmFvbReadWriteHeader->Offset   = Offset;
    527   SmmFvbReadWriteHeader->NumBytes = *NumBytes;
    528 
    529   //
    530   // Send data to SMM.
    531   //
    532   Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize);
    533 
    534   //
    535   // Get data from SMM.
    536   //
    537   *NumBytes = SmmFvbReadWriteHeader->NumBytes;
    538   if (!EFI_ERROR (Status)) {
    539     CopyMem (Buffer, (UINT8 *)(SmmFvbReadWriteHeader + 1), *NumBytes);
    540   }
    541   FreePool (SmmCommunicateHeader);
    542 
    543   return Status;
    544 }
    545 
    546 
    547 /**
    548   Writes data beginning at Lba:Offset from FV. The write terminates either
    549   when *NumBytes of data have been written, or when a block boundary is
    550   reached.  *NumBytes is updated to reflect the actual number of bytes
    551   written. The write opertion does not include erase. This routine will
    552   attempt to write only the specified bytes. If the writes do not stick,
    553   it will return an error.
    554 
    555   @param[in]      This           Calling context.
    556   @param[in]      Lba            Block in which to begin write.
    557   @param[in]      Offset         Offset in the block at which to begin write.
    558   @param[in,out]  NumBytes       On input, indicates the requested write size. On
    559                                  output, indicates the actual number of bytes written.
    560   @param[in]      Buffer         Buffer containing source data for the write.
    561 
    562   @retval EFI_SUCCESS            The firmware volume was written successfully.
    563   @retval EFI_BAD_BUFFER_SIZE    Write attempted across a LBA boundary. On output,
    564                                  NumBytes contains the total number of bytes
    565                                  actually written.
    566   @retval EFI_ACCESS_DENIED      The firmware volume is in the WriteDisabled state.
    567   @retval EFI_DEVICE_ERROR       The block device is not functioning correctly and
    568                                  could not be written.
    569   @retval EFI_INVALID_PARAMETER  NumBytes or Buffer are NULL.
    570 
    571 **/
    572 EFI_STATUS
    573 EFIAPI
    574 FvbWrite (
    575   IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL   *This,
    576   IN       EFI_LBA                              Lba,
    577   IN       UINTN                                Offset,
    578   IN OUT   UINTN                                *NumBytes,
    579   IN       UINT8                                *Buffer
    580   )
    581 {
    582   EFI_STATUS                                    Status;
    583   UINTN                                         PayloadSize;
    584   EFI_SMM_COMMUNICATE_HEADER                    *SmmCommunicateHeader;
    585   SMM_FVB_READ_WRITE_HEADER                     *SmmFvbReadWriteHeader;
    586   EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL        *SmmFvb;
    587   EFI_FVB_DEVICE                                *FvbDevice;
    588 
    589   if ((NumBytes == NULL) || (Buffer == NULL)) {
    590     return EFI_INVALID_PARAMETER;
    591   }
    592 
    593   FvbDevice = FVB_DEVICE_FROM_THIS (This);
    594   SmmFvb    = FvbDevice->SmmFvbInstance;
    595 
    596   //
    597   // Initialize the communicate buffer.
    598   //
    599   PayloadSize  = sizeof (SMM_FVB_READ_WRITE_HEADER) + *NumBytes;
    600   Status = InitCommunicateBuffer (
    601              (VOID **)&SmmCommunicateHeader,
    602              (VOID **)&SmmFvbReadWriteHeader,
    603              PayloadSize,
    604              EFI_FUNCTION_WRITE
    605              );
    606   if (EFI_ERROR (Status)) {
    607     return Status;
    608   }
    609 
    610   SmmFvbReadWriteHeader->SmmFvb   = SmmFvb;
    611   SmmFvbReadWriteHeader->Lba      = Lba;
    612   SmmFvbReadWriteHeader->Offset   = Offset;
    613   SmmFvbReadWriteHeader->NumBytes = *NumBytes;
    614   CopyMem ((UINT8 *)(SmmFvbReadWriteHeader + 1), Buffer, *NumBytes);
    615 
    616   //
    617   // Send data to SMM.
    618   //
    619   Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize);
    620 
    621   //
    622   // Get data from SMM.
    623   //
    624   *NumBytes = SmmFvbReadWriteHeader->NumBytes;
    625   FreePool (SmmCommunicateHeader);
    626 
    627   return Status;
    628 }
    629 
    630 
    631 /**
    632   The EraseBlock() function erases NumOfLba blocks started from StartingLba.
    633 
    634   @param[in] This            Calling context.
    635   @param[in] StartingLba     Starting LBA followed to erase.
    636   @param[in] NumOfLba        Number of block to erase.
    637 
    638   @retval EFI_SUCCESS        The erase request was successfully completed.
    639   @retval EFI_ACCESS_DENIED  The firmware volume is in the WriteDisabled state.
    640   @retval EFI_DEVICE_ERROR   The block device is not functioning correctly and
    641                              could not be written. Firmware device may have been
    642                              partially erased.
    643 
    644 **/
    645 EFI_STATUS
    646 EraseBlock (
    647   IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL    *This,
    648   IN       EFI_LBA                               StartingLba,
    649   IN       UINTN                                 NumOfLba
    650   )
    651 {
    652   EFI_STATUS                                     Status;
    653   UINTN                                          PayloadSize;
    654   EFI_SMM_COMMUNICATE_HEADER                    *SmmCommunicateHeader;
    655   SMM_FVB_BLOCKS_HEADER                         *SmmFvbBlocksHeader;
    656   EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL        *SmmFvb;
    657   EFI_FVB_DEVICE                                *FvbDevice;
    658 
    659   FvbDevice = FVB_DEVICE_FROM_THIS (This);
    660   SmmFvb    = FvbDevice->SmmFvbInstance;
    661 
    662   //
    663   // Initialize the communicate buffer.
    664   //
    665   PayloadSize  = sizeof (SMM_FVB_BLOCKS_HEADER);
    666   Status = InitCommunicateBuffer (
    667              (VOID **)&SmmCommunicateHeader,
    668              (VOID **)&SmmFvbBlocksHeader,
    669              PayloadSize,
    670              EFI_FUNCTION_ERASE_BLOCKS
    671              );
    672   if (EFI_ERROR (Status)) {
    673     return Status;
    674   }
    675 
    676   SmmFvbBlocksHeader->SmmFvb   = SmmFvb;
    677   SmmFvbBlocksHeader->StartLba = StartingLba;
    678   SmmFvbBlocksHeader->NumOfLba = NumOfLba;
    679 
    680   //
    681   // Send data to SMM.
    682   //
    683   Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize);
    684 
    685   //
    686   // Get data from SMM.
    687   //
    688   FreePool (SmmCommunicateHeader);
    689 
    690   return Status;
    691 }
    692 
    693 
    694 /**
    695   The EraseBlocks() function erases one or more blocks as denoted by the
    696   variable argument list. The entire parameter list of blocks must be verified
    697   prior to erasing any blocks.  If a block is requested that does not exist
    698   within the associated firmware volume (it has a larger index than the last
    699   block of the firmware volume), the EraseBlock() function must return
    700   EFI_INVALID_PARAMETER without modifying the contents of the firmware volume.
    701 
    702   @param[in] This           Calling context/
    703   @param[in] ...            Starting LBA followed by Number of Lba to erase.
    704                             a -1 to terminate the list.
    705 /
    706   @retval EFI_SUCCESS       The erase request was successfully completed
    707   @retval EFI_ACCESS_DENIED The firmware volume is in the WriteDisabled state/
    708   @retval EFI_DEVICE_ERROR  The block device is not functioning correctly and
    709                             could not be written. Firmware device may have been
    710                             partially erased/
    711 
    712 **/
    713 EFI_STATUS
    714 EFIAPI
    715 FvbEraseBlocks (
    716   IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL    *This,
    717   ...
    718   )
    719 {
    720   EFI_STATUS                                     Status;
    721   VA_LIST                                        Marker;
    722   EFI_LBA                                        StartingLba;
    723   UINTN                                          NumOfLba;
    724 
    725   Status = EFI_SUCCESS;
    726 
    727   //
    728   // Check the parameter.
    729   //
    730   VA_START (Marker, This);
    731   do {
    732     StartingLba = VA_ARG (Marker, EFI_LBA);
    733     if (StartingLba == EFI_LBA_LIST_TERMINATOR ) {
    734       break;
    735     }
    736 
    737     NumOfLba = VA_ARG (Marker, UINT32);
    738     if (NumOfLba == 0) {
    739       return EFI_INVALID_PARAMETER;
    740     }
    741 
    742   } while ( 1 );
    743   VA_END (Marker);
    744 
    745   //
    746   // Erase the blocks.
    747   //
    748   VA_START (Marker, This);
    749   do {
    750     StartingLba = VA_ARG (Marker, EFI_LBA);
    751     if (StartingLba == EFI_LBA_LIST_TERMINATOR ) {
    752       break;
    753     }
    754     NumOfLba = VA_ARG (Marker, UINT32);
    755     Status = EraseBlock (This, StartingLba, NumOfLba);
    756     if (EFI_ERROR (Status)) {
    757       break;
    758     }
    759   } while ( 1 );
    760   VA_END (Marker);
    761 
    762   return Status;
    763 }
    764 
    765 
    766 /**
    767   Install the FVB protocol which based on SMM FVB protocol.
    768 
    769   @param[in] SmmFvb        The SMM FVB protocol.
    770 
    771 **/
    772 VOID
    773 InstallFvb (
    774   IN    EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *SmmFvb
    775   )
    776 {
    777   EFI_STATUS                                    Status;
    778   EFI_HANDLE                                    FvbHandle;
    779   EFI_FVB_DEVICE                                *FvbDevice;
    780   EFI_FIRMWARE_VOLUME_HEADER                    *VolumeHeader;
    781   EFI_PHYSICAL_ADDRESS                          Address;
    782   EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL            *OldFvbInterface;
    783 
    784   FvbDevice = AllocateRuntimeCopyPool (sizeof (EFI_FVB_DEVICE), &mFvbDeviceTemplate);
    785   ASSERT (FvbDevice != NULL);
    786   FvbDevice->SmmFvbInstance = SmmFvb;
    787 
    788   Status = gBS->LocateProtocol (
    789                   &gEfiSmmCommunicationProtocolGuid,
    790                   NULL,
    791                   (VOID **) &mSmmCommunication
    792                   );
    793   ASSERT_EFI_ERROR (Status);
    794 
    795   Status = GetPhysicalAddress (SmmFvb, &Address);
    796   ASSERT_EFI_ERROR (Status);
    797 
    798   VolumeHeader = (EFI_FIRMWARE_VOLUME_HEADER *) (UINTN)Address;
    799 
    800   //
    801   // Set up the devicepath.
    802   //
    803   if (VolumeHeader->ExtHeaderOffset == 0) {
    804     //
    805     // FV does not contains extension header, then produce MEMMAP_DEVICE_PATH.
    806     //
    807     FvbDevice->DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) AllocateRuntimeCopyPool (sizeof (FV_MEMMAP_DEVICE_PATH), &mFvMemmapDevicePathTemplate);
    808     ((FV_MEMMAP_DEVICE_PATH *) FvbDevice->DevicePath)->MemMapDevPath.StartingAddress = (UINTN)Address;
    809     ((FV_MEMMAP_DEVICE_PATH *) FvbDevice->DevicePath)->MemMapDevPath.EndingAddress   = (UINTN)Address + VolumeHeader->FvLength - 1;
    810   } else {
    811     FvbDevice->DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) AllocateRuntimeCopyPool (sizeof (FV_PIWG_DEVICE_PATH), &mFvPIWGDevicePathTemplate);
    812     CopyGuid (
    813       &((FV_PIWG_DEVICE_PATH *)FvbDevice->DevicePath)->FvDevPath.FvName,
    814       (GUID *)(UINTN)((UINTN)Address + VolumeHeader->ExtHeaderOffset)
    815       );
    816   }
    817 
    818   //
    819   // Find a handle with a matching device path that has supports FW Block protocol.
    820   //
    821   Status = gBS->LocateDevicePath (
    822                   &gEfiFirmwareVolumeBlockProtocolGuid,
    823                   &FvbDevice->DevicePath,
    824                   &FvbHandle
    825                   );
    826   if (EFI_ERROR (Status) ) {
    827     //
    828     // LocateDevicePath fails so install a new interface and device path.
    829     //
    830     FvbHandle = NULL;
    831     Status =  gBS->InstallMultipleProtocolInterfaces (
    832                      &FvbHandle,
    833                      &gEfiFirmwareVolumeBlockProtocolGuid,
    834                      &FvbDevice->FvbInstance,
    835                      &gEfiDevicePathProtocolGuid,
    836                      FvbDevice->DevicePath,
    837                      NULL
    838                      );
    839     ASSERT_EFI_ERROR (Status);
    840   } else if (IsDevicePathEnd (FvbDevice->DevicePath)) {
    841     //
    842     // Device allready exists, so reinstall the FVB protocol.
    843     //
    844     Status = gBS->HandleProtocol (
    845                     FvbHandle,
    846                     &gEfiFirmwareVolumeBlockProtocolGuid,
    847                     (VOID **) &OldFvbInterface
    848                     );
    849     ASSERT_EFI_ERROR (Status);
    850 
    851     Status =  gBS->ReinstallProtocolInterface (
    852                      FvbHandle,
    853                      &gEfiFirmwareVolumeBlockProtocolGuid,
    854                      OldFvbInterface,
    855                      &FvbDevice->FvbInstance
    856                      );
    857     ASSERT_EFI_ERROR (Status);
    858   } else {
    859     //
    860     // There was a FVB protocol on an End Device Path node.
    861     //
    862     ASSERT (FALSE);
    863   }
    864 }
    865 
    866 
    867 /**
    868   SMM Firmware Volume Block Protocol notification event handler.
    869 
    870   Discover NV Variable Store and install Variable Write Arch Protocol.
    871 
    872   @param[in] Event    Event whose notification function is being invoked.
    873   @param[in] Context  Pointer to the notification function's context.
    874 **/
    875 VOID
    876 EFIAPI
    877 SmmFvbReady (
    878   IN  EFI_EVENT                                 Event,
    879   IN  VOID                                      *Context
    880   )
    881 {
    882   EFI_STATUS                                    Status;
    883   EFI_HANDLE                                    *HandleBuffer;
    884   UINTN                                         HandleCount;
    885   UINTN                                         Index;
    886   EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL        *SmmFvb;
    887 
    888   //
    889   // Locate all handles of Smm Fvb protocol.
    890   //
    891   Status = gBS->LocateHandleBuffer (
    892                   ByProtocol,
    893                   &gEfiSmmFirmwareVolumeBlockProtocolGuid,
    894                   NULL,
    895                   &HandleCount,
    896                   &HandleBuffer
    897                   );
    898   if (EFI_ERROR (Status)) {
    899     return ;
    900   }
    901 
    902   //
    903   // Install FVB protocol.
    904   //
    905   for (Index = 0; Index < HandleCount; Index++) {
    906     SmmFvb = NULL;
    907     Status = gBS->HandleProtocol (
    908                     HandleBuffer[Index],
    909                     &gEfiSmmFirmwareVolumeBlockProtocolGuid,
    910                     (VOID **) &SmmFvb
    911                     );
    912     if (EFI_ERROR (Status)) {
    913       break;
    914     }
    915 
    916     InstallFvb (SmmFvb);
    917   }
    918 
    919   FreePool (HandleBuffer);
    920 }
    921 
    922 
    923 /**
    924   The driver entry point for Firmware Volume Block Driver.
    925 
    926   The function does the necessary initialization work
    927   Firmware Volume Block Driver.
    928 
    929   @param[in]  ImageHandle       The firmware allocated handle for the UEFI image.
    930   @param[in]  SystemTable       A pointer to the EFI system table.
    931 
    932   @retval     EFI_SUCCESS       This funtion always return EFI_SUCCESS.
    933                                 It will ASSERT on errors.
    934 
    935 **/
    936 EFI_STATUS
    937 EFIAPI
    938 FvbSmmDxeInitialize (
    939   IN EFI_HANDLE                                 ImageHandle,
    940   IN EFI_SYSTEM_TABLE                           *SystemTable
    941   )
    942 {
    943   VOID                                          *SmmFvbRegistration;
    944 
    945   //
    946   // Smm FVB driver is ready.
    947   //
    948   EfiCreateProtocolNotifyEvent (
    949     &gEfiSmmFirmwareVolumeBlockProtocolGuid,
    950     TPL_CALLBACK,
    951     SmmFvbReady,
    952     NULL,
    953     &SmmFvbRegistration
    954     );
    955 
    956   return EFI_SUCCESS;
    957 }
    958 
    959