[edk2-devel] [edk2-platforms][PATCH V3 15/16] Platform/Loongson: Add QemuFlashFvbServicesRuntimeDxe driver.

xianglai lixianglai at loongson.cn
Mon Oct 17 12:56:48 UTC 2022


Hi Ard Biesheuvel :

In the function FvbInitialize, It validate the firmware volume 
header.The firmware volume header address is PcdOvmfFdBaseAddress,

in Ovmf PcdOvmfFdBaseAddress is equal to 
PcdOvmfFlashNvStorageVariableBase, so the checksum can pass.
When loongson is implemented, PcdOvmfFdBaseAddress and 
PcdOvmfFlashNvStorageVariableBase are not equal,

the firmware checksum will fail and enter the exception branch.
In the exception branch, due to the existence of reserved space in the 
layout of loongson's NORflash,

the exception branch will also fail to pass, which will eventually lead 
to function call failure.


++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

EFI_STATUS
EFIAPI
FvbInitialize (
   IN EFI_HANDLE        ImageHandle,
   IN EFI_SYSTEM_TABLE  *SystemTable
   )

{

....

BaseAddress = (UINTN)PcdGet32 (PcdOvmfFdBaseAddress);

....

FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)BaseAddress;

   Status      = ValidateFvHeader (FwVolHeader);
   if (EFI_ERROR (Status)) {
     //
     // Get FvbInfo
     //
     Status = GetFvbInfo (Length, &FwVolHeader);
     if (EFI_ERROR (Status)) {
       DEBUG ((DEBUG_INFO, "EFI_ERROR (GetFvbInfo (Length, 
&FwVolHeader))\n"));
       return EFI_WRITE_PROTECTED;
     }
   }

....

}

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


But I think the reserved space in NORflash is unnecessary,

maybe I can remove the reserved space in Norflash and go through the 
exception branch to pass the firmware volume  header checksum,

then I can just  Using the flash library implementation in Ovmf.

I can try it like this.


Thanks

xianglai



On 2022/10/17 下午7:00, Ard Biesheuvel wrote:
> On Fri, 14 Oct 2022 at 06:01, xianglai li <lixianglai at loongson.cn> wrote:
>> This library provides flash read and write functionality
>> and supports writing variables to flash.
>>
>> REF: https://bugzilla.tianocore.org/show_bug.cgi?id=4054
>>
>> Signed-off-by: xianglai li <lixianglai at loongson.cn>
> Why do you need a new driver for this? We already have drivers to
> support QEMU's NOR flash emulation, and the NorFlashDxe driver is
> being migrated to OvmfPkg/ in a series that is on the list now for
> RISC-V
>
>> ---
>>   .../QemuFlashFvbServicesRuntimeDxe/FvbInfo.c  |  115 ++
>>   .../FvbServicesRuntimeDxe.inf                 |   73 ++
>>   .../FwBlockService.c                          | 1158 +++++++++++++++++
>>   .../FwBlockService.h                          |  178 +++
>>   .../FwBlockServiceDxe.c                       |  152 +++
>>   .../QemuFlash.c                               |  251 ++++
>>   .../QemuFlash.h                               |   86 ++
>>   .../QemuFlashDxe.c                            |   21 +
>>   .../Loongson/LoongArchQemuPkg/Loongson.dsc    |    4 +-
>>   9 files changed, 2036 insertions(+), 2 deletions(-)
>>   create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbInfo.c
>>   create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
>>   create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
>>   create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.h
>>   create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockServiceDxe.c
>>   create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c
>>   create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.h
>>   create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c
>>
>> diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbInfo.c b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbInfo.c
>> new file mode 100644
>> index 0000000000..df772f72be
>> --- /dev/null
>> +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbInfo.c
>> @@ -0,0 +1,115 @@
>> +/** @file
>> +  Defines data structure that is the volume header found.These data is intent
>> +  to decouple FVB driver with FV header.
>> +
>> +  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
>> +
>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>> +
>> +**/
>> +
>> +//
>> +// The package level header files this module uses
>> +//
>> +#include <Pi/PiFirmwareVolume.h>
>> +
>> +//
>> +// The protocols, PPI and GUID definitions for this module
>> +//
>> +#include <Guid/SystemNvDataGuid.h>
>> +//
>> +// The Library classes this module consumes
>> +//
>> +#include <Library/BaseLib.h>
>> +#include <Library/PcdLib.h>
>> +
>> +typedef struct {
>> +  UINT64                        FvLength;
>> +  EFI_FIRMWARE_VOLUME_HEADER    FvbInfo;
>> +  //
>> +  // EFI_FV_BLOCK_MAP_ENTRY    ExtraBlockMap[n];//n=0
>> +  //
>> +  EFI_FV_BLOCK_MAP_ENTRY        End[1];
>> +} EFI_FVB_MEDIA_INFO;
>> +
>> +EFI_FVB_MEDIA_INFO  mPlatformFvbMediaInfo[] = {
>> +  //
>> +  // System NvStorage FVB
>> +  //
>> +  {
>> +    FixedPcdGet32 (PcdAllVarSize),
>> +    {
>> +      {
>> +        0,
>> +      },  // ZeroVector[16]
>> +      EFI_SYSTEM_NV_DATA_FV_GUID,
>> +      FixedPcdGet32 (PcdAllVarSize),
>> +      EFI_FVH_SIGNATURE,
>> +      EFI_FVB2_MEMORY_MAPPED |
>> +      EFI_FVB2_READ_ENABLED_CAP |
>> +      EFI_FVB2_READ_STATUS |
>> +      EFI_FVB2_WRITE_ENABLED_CAP |
>> +      EFI_FVB2_WRITE_STATUS |
>> +      EFI_FVB2_ERASE_POLARITY |
>> +      EFI_FVB2_ALIGNMENT_16,
>> +      sizeof (EFI_FIRMWARE_VOLUME_HEADER) + sizeof (EFI_FV_BLOCK_MAP_ENTRY),
>> +      0,  // CheckSum
>> +      0,  // ExtHeaderOffset
>> +      {
>> +        0,
>> +      },  // Reserved[1]
>> +      2,  // Revision
>> +      {
>> +        {
>> +          (FixedPcdGet32 (PcdAllVarSize))/
>> +          FixedPcdGet32 (PcdFlashBlockSize),
>> +          FixedPcdGet32 (PcdFlashBlockSize),
>> +        }
>> +      } // BlockMap[1]
>> +    },
>> +    {
>> +      {
>> +        0,
>> +        0
>> +      }
>> +    }  // End[1]
>> +  }
>> +};
>> +
>> +EFI_STATUS
>> +GetFvbInfo (
>> +  IN  UINT64                      FvLength,
>> +  OUT EFI_FIRMWARE_VOLUME_HEADER  **FvbInfo
>> +  )
>> +{
>> +  STATIC BOOLEAN  Checksummed = FALSE;
>> +  UINTN           Index;
>> +
>> +  if (!Checksummed) {
>> +    for (Index = 0;
>> +         Index < sizeof (mPlatformFvbMediaInfo) / sizeof (EFI_FVB_MEDIA_INFO);
>> +         Index += 1)
>> +    {
>> +      UINT16  Checksum;
>> +      mPlatformFvbMediaInfo[Index].FvbInfo.Checksum = 0;
>> +      Checksum                                      = CalculateCheckSum16 (
>> +                                                        (UINT16 *)&mPlatformFvbMediaInfo[Index].FvbInfo,
>> +                                                        mPlatformFvbMediaInfo[Index].FvbInfo.HeaderLength
>> +                                                        );
>> +      mPlatformFvbMediaInfo[Index].FvbInfo.Checksum = Checksum;
>> +    }
>> +    Checksummed = TRUE;
>> +  }
>> +
>> +  for (Index = 0;
>> +       Index < sizeof (mPlatformFvbMediaInfo) / sizeof (EFI_FVB_MEDIA_INFO);
>> +       Index += 1)
>> +  {
>> +    if (mPlatformFvbMediaInfo[Index].FvLength == FvLength) {
>> +      *FvbInfo = &mPlatformFvbMediaInfo[Index].FvbInfo;
>> +      return EFI_SUCCESS;
>> +    }
>> +  }
>> +
>> +  return EFI_NOT_FOUND;
>> +}
>> diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
>> new file mode 100644
>> index 0000000000..4b0c42b075
>> --- /dev/null
>> +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
>> @@ -0,0 +1,73 @@
>> +## @file
>> +# Component description file for Emu Fimware Volume Block DXE driver module.
>> +#
>> +#  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
>> +#
>> +#  SPDX-License-Identifier: BSD-2-Clause-Patent
>> +#
>> +##
>> +
>> +
>> +[Defines]
>> +  INF_VERSION                    = 0x00010005
>> +  BASE_NAME                      = FvbServicesRuntimeDxe
>> +  FILE_GUID                      = 733cbac2-b23f-4b92-bc8e-fb01ce5907b7
>> +  MODULE_TYPE                    = DXE_RUNTIME_DRIVER
>> +  VERSION_STRING                 = 1.0
>> +  ENTRY_POINT                    = FvbInitialize
>> +
>> +#
>> +# The following information is for reference only and not required by the build
>> +# tools.
>> +#
>> +#  VALID_ARCHITECTURES           = IA32 X64
>> +#
>> +
>> +[Sources]
>> +  FvbInfo.c
>> +  FwBlockService.c
>> +  FwBlockServiceDxe.c
>> +  QemuFlash.c
>> +  QemuFlashDxe.c
>> +
>> +[Packages]
>> +  MdePkg/MdePkg.dec
>> +  MdeModulePkg/MdeModulePkg.dec
>> +  OvmfPkg/OvmfPkg.dec
>> +  Platform/Loongson/LoongArchQemuPkg/Loongson.dec
>> +
>> +[LibraryClasses]
>> +  BaseLib
>> +  BaseMemoryLib
>> +  DebugLib
>> +  DevicePathLib
>> +  DxeServicesTableLib
>> +  MemoryAllocationLib
>> +  PcdLib
>> +  UefiBootServicesTableLib
>> +  UefiDriverEntryPoint
>> +  UefiRuntimeLib
>> +
>> +[Guids]
>> +  gEfiEventVirtualAddressChangeGuid   # ALWAYS_CONSUMED
>> +  # gEfiEventVirtualAddressChangeGuid # Create Event: EVENT_GROUP_GUID
>> +
>> +[Protocols]
>> +  gEfiFirmwareVolumeBlockProtocolGuid           # PROTOCOL SOMETIMES_PRODUCED
>> +  gEfiDevicePathProtocolGuid                    # PROTOCOL SOMETIMES_PRODUCED
>> +
>> +[FixedPcd]
>> +  gLoongArchQemuPkgTokenSpaceGuid.PcdOvmfFlashNvStorageVariableBase
>> +  gLoongArchQemuPkgTokenSpaceGuid.PcdOvmfFlashNvStorageFtwWorkingBase
>> +  gLoongArchQemuPkgTokenSpaceGuid.PcdOvmfFlashNvStorageFtwSpareBase
>> +  gLoongArchQemuPkgTokenSpaceGuid.PcdFlashFdBase
>> +  gLoongArchQemuPkgTokenSpaceGuid.PcdAllVarSize
>> +  gLoongArchQemuPkgTokenSpaceGuid.PcdFlashBlockSize
>> +[Pcd]
>> +  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase64
>> +  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashVariablesEnable
>> +  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase64
>> +  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase64
>> +
>> +[Depex]
>> +  TRUE
>> diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
>> new file mode 100644
>> index 0000000000..d44f2b460c
>> --- /dev/null
>> +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.c
>> @@ -0,0 +1,1158 @@
>> +/** @file
>> +  Implementations for Firmware Volume Block protocol.
>> +
>> +  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
>> +
>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>> +
>> +**/
>> +
>> +//
>> +// The protocols, PPI and GUID definitions for this module
>> +//
>> +#include <Protocol/DevicePath.h>
>> +#include <Protocol/FirmwareVolumeBlock.h>
>> +
>> +//
>> +// The Library classes this module consumes
>> +//
>> +#include <Library/BaseLib.h>
>> +#include <Library/BaseMemoryLib.h>
>> +#include <Library/DebugLib.h>
>> +#include <Library/DevicePathLib.h>
>> +#include <Library/DxeServicesTableLib.h>
>> +#include <Library/MemoryAllocationLib.h>
>> +#include <Library/UefiBootServicesTableLib.h>
>> +
>> +#include "FwBlockService.h"
>> +#include "QemuFlash.h"
>> +
>> +#define EFI_FVB2_STATUS \
>> +          (EFI_FVB2_READ_STATUS | EFI_FVB2_WRITE_STATUS | EFI_FVB2_LOCK_STATUS)
>> +
>> +ESAL_FWB_GLOBAL  *mFvbModuleGlobal;
>> +
>> +FV_MEMMAP_DEVICE_PATH  mFvMemmapDevicePathTemplate = {
>> +  {
>> +    {
>> +      HARDWARE_DEVICE_PATH,
>> +      HW_MEMMAP_DP,
>> +      {
>> +        (UINT8)(sizeof (MEMMAP_DEVICE_PATH)),
>> +        (UINT8)(sizeof (MEMMAP_DEVICE_PATH) >> 8)
>> +      }
>> +    },
>> +    EfiMemoryMappedIO,
>> +    (EFI_PHYSICAL_ADDRESS)0,
>> +    (EFI_PHYSICAL_ADDRESS)0,
>> +  },
>> +  {
>> +    END_DEVICE_PATH_TYPE,
>> +    END_ENTIRE_DEVICE_PATH_SUBTYPE,
>> +    {
>> +      END_DEVICE_PATH_LENGTH,
>> +      0
>> +    }
>> +  }
>> +};
>> +
>> +FV_PIWG_DEVICE_PATH  mFvPIWGDevicePathTemplate = {
>> +  {
>> +    {
>> +      MEDIA_DEVICE_PATH,
>> +      MEDIA_PIWG_FW_VOL_DP,
>> +      {
>> +        (UINT8)(sizeof (MEDIA_FW_VOL_DEVICE_PATH)),
>> +        (UINT8)(sizeof (MEDIA_FW_VOL_DEVICE_PATH) >> 8)
>> +      }
>> +    },
>> +    { 0 }
>> +  },
>> +  {
>> +    END_DEVICE_PATH_TYPE,
>> +    END_ENTIRE_DEVICE_PATH_SUBTYPE,
>> +    {
>> +      END_DEVICE_PATH_LENGTH,
>> +      0
>> +    }
>> +  }
>> +};
>> +
>> +EFI_FW_VOL_BLOCK_DEVICE  mFvbDeviceTemplate = {
>> +  FVB_DEVICE_SIGNATURE,
>> +  NULL,
>> +  0,
>> +  {
>> +    FvbProtocolGetAttributes,
>> +    FvbProtocolSetAttributes,
>> +    FvbProtocolGetPhysicalAddress,
>> +    FvbProtocolGetBlockSize,
>> +    FvbProtocolRead,
>> +    FvbProtocolWrite,
>> +    FvbProtocolEraseBlocks,
>> +    NULL
>> +  }
>> +};
>> +
>> +
>> +EFI_STATUS
>> +GetFvbInstance (
>> +  IN  UINTN                Instance,
>> +  IN  ESAL_FWB_GLOBAL      *Global,
>> +  OUT EFI_FW_VOL_INSTANCE  **FwhInstance
>> +  )
>> +/*++
>> +
>> +  Routine Description:
>> +    Retrieves the physical address of a memory mapped FV
>> +
>> +  Arguments:
>> +    Instance              - The FV instance whose base address is going to be
>> +                            returned
>> +    Global                - Pointer to ESAL_FWB_GLOBAL that contains all
>> +                            instance data
>> +    FwhInstance           - The EFI_FW_VOL_INSTANCE firmware instance structure
>> +
>> +  Returns:
>> +    EFI_SUCCESS           - Successfully returns
>> +    EFI_INVALID_PARAMETER - Instance not found
>> +
>> +--*/
>> +{
>> +  EFI_FW_VOL_INSTANCE  *FwhRecord;
>> +
>> +  *FwhInstance = NULL;
>> +  if (Instance >= Global->NumFv) {
>> +    return EFI_INVALID_PARAMETER;
>> +  }
>> +  //
>> +  // Find the right instance of the FVB private data
>> +  //
>> +  FwhRecord = Global->FvInstance;
>> +  while (Instance > 0) {
>> +    FwhRecord = (EFI_FW_VOL_INSTANCE *)
>> +                (
>> +                 (UINTN)((UINT8 *)FwhRecord) + FwhRecord->VolumeHeader.HeaderLength +
>> +                 (sizeof (EFI_FW_VOL_INSTANCE) - sizeof (EFI_FIRMWARE_VOLUME_HEADER))
>> +                );
>> +    Instance--;
>> +  }
>> +
>> +  *FwhInstance = FwhRecord;
>> +
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +EFI_STATUS
>> +FvbGetPhysicalAddress (
>> +  IN UINTN                  Instance,
>> +  OUT EFI_PHYSICAL_ADDRESS  *Address,
>> +  IN ESAL_FWB_GLOBAL        *Global
>> +  )
>> +/*++
>> +
>> +  Routine Description:
>> +    Retrieves the physical address of a memory mapped FV
>> +
>> +  Arguments:
>> +    Instance              - The FV instance whose base address is going to be
>> +                            returned
>> +    Address               - Pointer to a caller allocated EFI_PHYSICAL_ADDRESS
>> +                            that on successful return, contains the base
>> +                            address of the firmware volume.
>> +    Global                - Pointer to ESAL_FWB_GLOBAL that contains all
>> +                            instance data
>> +
>> +  Returns:
>> +    EFI_SUCCESS           - Successfully returns
>> +    EFI_INVALID_PARAMETER - Instance not found
>> +
>> +--*/
>> +{
>> +  EFI_FW_VOL_INSTANCE  *FwhInstance;
>> +  EFI_STATUS           Status;
>> +
>> +  //
>> +  // Find the right instance of the FVB private data
>> +  //
>> +  Status = GetFvbInstance (Instance, Global, &FwhInstance);
>> +  ASSERT_EFI_ERROR (Status);
>> +  *Address = FwhInstance->FvBase;
>> +
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +EFI_STATUS
>> +FvbGetVolumeAttributes (
>> +  IN UINTN                  Instance,
>> +  OUT EFI_FVB_ATTRIBUTES_2  *Attributes,
>> +  IN ESAL_FWB_GLOBAL        *Global
>> +  )
>> +/*++
>> +
>> +  Routine Description:
>> +    Retrieves attributes, insures positive polarity of attribute bits, returns
>> +    resulting attributes in output parameter
>> +
>> +  Arguments:
>> +    Instance              - The FV instance whose attributes is going to be
>> +                            returned
>> +    Attributes            - Output buffer which contains attributes
>> +    Global                - Pointer to ESAL_FWB_GLOBAL that contains all
>> +                            instance data
>> +
>> +  Returns:
>> +    EFI_SUCCESS           - Successfully returns
>> +    EFI_INVALID_PARAMETER - Instance not found
>> +
>> +--*/
>> +{
>> +  EFI_FW_VOL_INSTANCE  *FwhInstance;
>> +  EFI_STATUS           Status;
>> +
>> +  //
>> +  // Find the right instance of the FVB private data
>> +  //
>> +  Status = GetFvbInstance (Instance, Global, &FwhInstance);
>> +  ASSERT_EFI_ERROR (Status);
>> +  *Attributes = FwhInstance->VolumeHeader.Attributes;
>> +
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +EFI_STATUS
>> +FvbGetLbaAddress (
>> +  IN  UINTN            Instance,
>> +  IN  EFI_LBA          Lba,
>> +  OUT UINTN            *LbaAddress,
>> +  OUT UINTN            *LbaLength,
>> +  OUT UINTN            *NumOfBlocks,
>> +  IN  ESAL_FWB_GLOBAL  *Global
>> +  )
>> +/*++
>> +
>> +  Routine Description:
>> +    Retrieves the starting address of an LBA in an FV
>> +
>> +  Arguments:
>> +    Instance              - The FV instance which the Lba belongs to
>> +    Lba                   - The logical block address
>> +    LbaAddress            - On output, contains the physical starting address
>> +                            of the Lba
>> +    LbaLength             - On output, contains the length of the block
>> +    NumOfBlocks           - A pointer to a caller allocated UINTN in which the
>> +                            number of consecutive blocks starting with Lba is
>> +                            returned. All blocks in this range have a size of
>> +                            BlockSize
>> +    Global                - Pointer to ESAL_FWB_GLOBAL that contains all
>> +                            instance data
>> +
>> +  Returns:
>> +    EFI_SUCCESS           - Successfully returns
>> +    EFI_INVALID_PARAMETER - Instance not found
>> +
>> +--*/
>> +{
>> +  UINT32                  NumBlocks;
>> +  UINT32                  BlockLength;
>> +  UINTN                   Offset;
>> +  EFI_LBA                 StartLba;
>> +  EFI_LBA                 NextLba;
>> +  EFI_FW_VOL_INSTANCE     *FwhInstance;
>> +  EFI_FV_BLOCK_MAP_ENTRY  *BlockMap;
>> +  EFI_STATUS              Status;
>> +
>> +  //
>> +  // Find the right instance of the FVB private data
>> +  //
>> +  Status = GetFvbInstance (Instance, Global, &FwhInstance);
>> +  ASSERT_EFI_ERROR (Status);
>> +
>> +  StartLba = 0;
>> +  Offset   = 0;
>> +  BlockMap = &(FwhInstance->VolumeHeader.BlockMap[0]);
>> +
>> +  //
>> +  // Parse the blockmap of the FV to find which map entry the Lba belongs to
>> +  //
>> +  while (TRUE) {
>> +    NumBlocks   = BlockMap->NumBlocks;
>> +    BlockLength = BlockMap->Length;
>> +
>> +    if ((NumBlocks == 0) || (BlockLength == 0)) {
>> +      return EFI_INVALID_PARAMETER;
>> +    }
>> +
>> +    NextLba = StartLba + NumBlocks;
>> +
>> +    //
>> +    // The map entry found
>> +    //
>> +    if ((Lba >= StartLba) && (Lba < NextLba)) {
>> +      Offset = Offset + (UINTN)MultU64x32 ((Lba - StartLba), BlockLength);
>> +      if (LbaAddress != NULL) {
>> +        *LbaAddress = FwhInstance->FvBase + Offset;
>> +      }
>> +
>> +      if (LbaLength != NULL) {
>> +        *LbaLength = BlockLength;
>> +      }
>> +
>> +      if (NumOfBlocks != NULL) {
>> +        *NumOfBlocks = (UINTN)(NextLba - Lba);
>> +      }
>> +
>> +      return EFI_SUCCESS;
>> +    }
>> +
>> +    StartLba = NextLba;
>> +    Offset   = Offset + NumBlocks * BlockLength;
>> +    BlockMap++;
>> +  }
>> +}
>> +
>> +EFI_STATUS
>> +FvbSetVolumeAttributes (
>> +  IN UINTN                     Instance,
>> +  IN OUT EFI_FVB_ATTRIBUTES_2  *Attributes,
>> +  IN ESAL_FWB_GLOBAL           *Global
>> +  )
>> +/*++
>> +
>> +  Routine Description:
>> +    Modifies the current settings of the firmware volume according to the
>> +    input parameter, and returns the new setting of the volume
>> +
>> +  Arguments:
>> +    Instance              - The FV instance whose attributes is going to be
>> +                            modified
>> +    Attributes            - On input, it is a pointer to EFI_FVB_ATTRIBUTES_2
>> +                            containing the desired firmware volume settings.
>> +                            On successful return, it contains the new settings
>> +                            of the firmware volume
>> +    Global                - Pointer to ESAL_FWB_GLOBAL that contains all
>> +                            instance data
>> +
>> +  Returns:
>> +    EFI_SUCCESS           - Successfully returns
>> +    EFI_ACCESS_DENIED     - The volume setting is locked and cannot be modified
>> +    EFI_INVALID_PARAMETER - Instance not found, or The attributes requested are
>> +                            in conflict with the capabilities as declared in
>> +                            the firmware volume header
>> +
>> +--*/
>> +{
>> +  EFI_FW_VOL_INSTANCE   *FwhInstance;
>> +  EFI_FVB_ATTRIBUTES_2  OldAttributes;
>> +  EFI_FVB_ATTRIBUTES_2  *AttribPtr;
>> +  UINT32                Capabilities;
>> +  UINT32                OldStatus;
>> +  UINT32                NewStatus;
>> +  EFI_STATUS            Status;
>> +  EFI_FVB_ATTRIBUTES_2  UnchangedAttributes;
>> +
>> +  //
>> +  // Find the right instance of the FVB private data
>> +  //
>> +  Status = GetFvbInstance (Instance, Global, &FwhInstance);
>> +  ASSERT_EFI_ERROR (Status);
>> +
>> +  AttribPtr =
>> +    (EFI_FVB_ATTRIBUTES_2 *)&(FwhInstance->VolumeHeader.Attributes);
>> +  OldAttributes = *AttribPtr;
>> +  Capabilities  = OldAttributes & (EFI_FVB2_READ_DISABLED_CAP | \
>> +                                   EFI_FVB2_READ_ENABLED_CAP | \
>> +                                   EFI_FVB2_WRITE_DISABLED_CAP | \
>> +                                   EFI_FVB2_WRITE_ENABLED_CAP | \
>> +                                   EFI_FVB2_LOCK_CAP \
>> +                                   );
>> +  OldStatus = OldAttributes & EFI_FVB2_STATUS;
>> +  NewStatus = *Attributes & EFI_FVB2_STATUS;
>> +
>> +  UnchangedAttributes = EFI_FVB2_READ_DISABLED_CAP  | \
>> +                        EFI_FVB2_READ_ENABLED_CAP   | \
>> +                        EFI_FVB2_WRITE_DISABLED_CAP | \
>> +                        EFI_FVB2_WRITE_ENABLED_CAP  | \
>> +                        EFI_FVB2_LOCK_CAP           | \
>> +                        EFI_FVB2_STICKY_WRITE       | \
>> +                        EFI_FVB2_MEMORY_MAPPED      | \
>> +                        EFI_FVB2_ERASE_POLARITY     | \
>> +                        EFI_FVB2_READ_LOCK_CAP      | \
>> +                        EFI_FVB2_WRITE_LOCK_CAP     | \
>> +                        EFI_FVB2_ALIGNMENT;
>> +
>> +  //
>> +  // Some attributes of FV is read only can *not* be set
>> +  //
>> +  if ((OldAttributes & UnchangedAttributes) ^
>> +      (*Attributes & UnchangedAttributes))
>> +  {
>> +    return EFI_INVALID_PARAMETER;
>> +  }
>> +  //
>> +  // If firmware volume is locked, no status bit can be updated
>> +  //
>> +  if (OldAttributes & EFI_FVB2_LOCK_STATUS) {
>> +    if (OldStatus ^ NewStatus) {
>> +      return EFI_ACCESS_DENIED;
>> +    }
>> +  }
>> +  //
>> +  // Test read disable
>> +  //
>> +  if ((Capabilities & EFI_FVB2_READ_DISABLED_CAP) == 0) {
>> +    if ((NewStatus & EFI_FVB2_READ_STATUS) == 0) {
>> +      return EFI_INVALID_PARAMETER;
>> +    }
>> +  }
>> +  //
>> +  // Test read enable
>> +  //
>> +  if ((Capabilities & EFI_FVB2_READ_ENABLED_CAP) == 0) {
>> +    if (NewStatus & EFI_FVB2_READ_STATUS) {
>> +      return EFI_INVALID_PARAMETER;
>> +    }
>> +  }
>> +  //
>> +  // Test write disable
>> +  //
>> +  if ((Capabilities & EFI_FVB2_WRITE_DISABLED_CAP) == 0) {
>> +    if ((NewStatus & EFI_FVB2_WRITE_STATUS) == 0) {
>> +      return EFI_INVALID_PARAMETER;
>> +    }
>> +  }
>> +  //
>> +  // Test write enable
>> +  //
>> +  if ((Capabilities & EFI_FVB2_WRITE_ENABLED_CAP) == 0) {
>> +    if (NewStatus & EFI_FVB2_WRITE_STATUS) {
>> +      return EFI_INVALID_PARAMETER;
>> +    }
>> +  }
>> +  //
>> +  // Test lock
>> +  //
>> +  if ((Capabilities & EFI_FVB2_LOCK_CAP) == 0) {
>> +    if (NewStatus & EFI_FVB2_LOCK_STATUS) {
>> +      return EFI_INVALID_PARAMETER;
>> +    }
>> +  }
>> +
>> +  *AttribPtr  = (*AttribPtr) & (0xFFFFFFFF & (~EFI_FVB2_STATUS));
>> +  *AttribPtr  = (*AttribPtr) | NewStatus;
>> +  *Attributes = *AttribPtr;
>> +
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +//
>> +// FVB protocol APIs
>> +//
>> +EFI_STATUS
>> +EFIAPI
>> +FvbProtocolGetPhysicalAddress (
>> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
>> +  OUT EFI_PHYSICAL_ADDRESS                     *Address
>> +  )
>> +/*++
>> +
>> +  Routine Description:
>> +
>> +    Retrieves the physical address of the device.
>> +
>> +  Arguments:
>> +
>> +    This                  - Calling context
>> +    Address               - Output buffer containing the address.
>> +
>> +  Returns:
>> +    EFI_SUCCESS           - Successfully returns
>> +
>> +--*/
>> +{
>> +  EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice;
>> +
>> +  FvbDevice = FVB_DEVICE_FROM_THIS (This);
>> +
>> +  return FvbGetPhysicalAddress (
>> +           FvbDevice->Instance,
>> +           Address,
>> +           mFvbModuleGlobal
>> +           );
>> +}
>> +
>> +EFI_STATUS
>> +EFIAPI
>> +FvbProtocolGetBlockSize (
>> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
>> +  IN CONST EFI_LBA                             Lba,
>> +  OUT UINTN                                    *BlockSize,
>> +  OUT UINTN                                    *NumOfBlocks
>> +  )
>> +/*++
>> +
>> +  Routine Description:
>> +    Retrieve the size of a logical block
>> +
>> +  Arguments:
>> +    This                  - Calling context
>> +    Lba                   - Indicates which block to return the size for.
>> +    BlockSize             - A pointer to a caller allocated UINTN in which
>> +                            the size of the block is returned
>> +    NumOfBlocks           - a pointer to a caller allocated UINTN in which the
>> +                            number of consecutive blocks starting with Lba is
>> +                            returned. All blocks in this range have a size of
>> +                            BlockSize
>> +
>> +  Returns:
>> +    EFI_SUCCESS           - The firmware volume was read successfully and
>> +                            contents are in Buffer
>> +
>> +--*/
>> +{
>> +  EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice;
>> +
>> +  FvbDevice = FVB_DEVICE_FROM_THIS (This);
>> +
>> +  return FvbGetLbaAddress (
>> +           FvbDevice->Instance,
>> +           Lba,
>> +           NULL,
>> +           BlockSize,
>> +           NumOfBlocks,
>> +           mFvbModuleGlobal
>> +           );
>> +}
>> +
>> +EFI_STATUS
>> +EFIAPI
>> +FvbProtocolGetAttributes (
>> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
>> +  OUT EFI_FVB_ATTRIBUTES_2                     *Attributes
>> +  )
>> +/*++
>> +
>> +  Routine Description:
>> +      Retrieves Volume attributes.  No polarity translations are done.
>> +
>> +  Arguments:
>> +      This                - Calling context
>> +      Attributes          - output buffer which contains attributes
>> +
>> +  Returns:
>> +    EFI_SUCCESS           - Successfully returns
>> +
>> +--*/
>> +{
>> +  EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice;
>> +
>> +  FvbDevice = FVB_DEVICE_FROM_THIS (This);
>> +
>> +  return FvbGetVolumeAttributes (
>> +           FvbDevice->Instance,
>> +           Attributes,
>> +           mFvbModuleGlobal
>> +           );
>> +}
>> +
>> +EFI_STATUS
>> +EFIAPI
>> +FvbProtocolSetAttributes (
>> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
>> +  IN OUT EFI_FVB_ATTRIBUTES_2                  *Attributes
>> +  )
>> +/*++
>> +
>> +  Routine Description:
>> +    Sets Volume attributes. No polarity translations are done.
>> +
>> +  Arguments:
>> +    This                  - Calling context
>> +    Attributes            - output buffer which contains attributes
>> +
>> +  Returns:
>> +    EFI_SUCCESS           - Successfully returns
>> +
>> +--*/
>> +{
>> +  EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice;
>> +
>> +  FvbDevice = FVB_DEVICE_FROM_THIS (This);
>> +
>> +  return FvbSetVolumeAttributes (
>> +           FvbDevice->Instance,
>> +           Attributes,
>> +           mFvbModuleGlobal
>> +           );
>> +}
>> +
>> +EFI_STATUS
>> +EFIAPI
>> +FvbProtocolEraseBlocks (
>> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
>> +  ...
>> +  )
>> +/*++
>> +
>> +  Routine Description:
>> +
>> +    The EraseBlock() function erases one or more blocks as denoted by the
>> +    variable argument list. The entire parameter list of blocks must be
>> +    verified prior to erasing any blocks.  If a block is requested that does
>> +    not exist within the associated firmware volume (it has a larger index than
>> +    the last block of the firmware volume), the EraseBlock() function must
>> +    return EFI_INVALID_PARAMETER without modifying the contents of the firmware
>> +    volume.
>> +
>> +  Arguments:
>> +    This                  - Calling context
>> +    ...                   - Starting LBA followed by Number of Lba to erase.
>> +                            a -1 to terminate the list.
>> +
>> +  Returns:
>> +    EFI_SUCCESS           - The erase request was successfully completed
>> +    EFI_ACCESS_DENIED     - The firmware volume is in the WriteDisabled state
>> +    EFI_DEVICE_ERROR      - The block device is not functioning correctly and
>> +                            could not be written. Firmware device may have been
>> +                            partially erased
>> +
>> +--*/
>> +{
>> +  EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice;
>> +  EFI_FW_VOL_INSTANCE      *FwhInstance;
>> +  UINTN                    NumOfBlocks;
>> +  VA_LIST                  args;
>> +  EFI_LBA                  StartingLba;
>> +  UINTN                    NumOfLba;
>> +  EFI_STATUS               Status;
>> +
>> +  FvbDevice = FVB_DEVICE_FROM_THIS (This);
>> +
>> +  Status = GetFvbInstance (
>> +             FvbDevice->Instance,
>> +             mFvbModuleGlobal,
>> +             &FwhInstance
>> +             );
>> +  ASSERT_EFI_ERROR (Status);
>> +
>> +  NumOfBlocks = FwhInstance->NumOfBlocks;
>> +
>> +  VA_START (args, This);
>> +
>> +  do {
>> +    StartingLba = VA_ARG (args, EFI_LBA);
>> +    if (StartingLba == EFI_LBA_LIST_TERMINATOR) {
>> +      break;
>> +    }
>> +
>> +    NumOfLba = VA_ARG (args, UINTN);
>> +
>> +    //
>> +    // Check input parameters
>> +    //
>> +    if ((NumOfLba == 0) || ((StartingLba + NumOfLba) > NumOfBlocks)) {
>> +      VA_END (args);
>> +      return EFI_INVALID_PARAMETER;
>> +    }
>> +  } while (1);
>> +
>> +  VA_END (args);
>> +
>> +  VA_START (args, This);
>> +  do {
>> +    StartingLba = VA_ARG (args, EFI_LBA);
>> +    if (StartingLba == EFI_LBA_LIST_TERMINATOR) {
>> +      break;
>> +    }
>> +
>> +    NumOfLba = VA_ARG (args, UINTN);
>> +
>> +    while (NumOfLba > 0) {
>> +      Status = QemuFlashEraseBlock (StartingLba);
>> +      if (EFI_ERROR (Status)) {
>> +        VA_END (args);
>> +        return Status;
>> +      }
>> +
>> +      StartingLba++;
>> +      NumOfLba--;
>> +    }
>> +
>> +  } while (1);
>> +
>> +  VA_END (args);
>> +
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +EFI_STATUS
>> +EFIAPI
>> +FvbProtocolWrite (
>> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
>> +  IN       EFI_LBA                             Lba,
>> +  IN       UINTN                               Offset,
>> +  IN OUT   UINTN                               *NumBytes,
>> +  IN       UINT8                               *Buffer
>> +  )
>> +/*++
>> +
>> +  Routine Description:
>> +
>> +    Writes data beginning at Lba:Offset from FV. The write terminates either
>> +    when *NumBytes of data have been written, or when a block boundary is
>> +    reached.  *NumBytes is updated to reflect the actual number of bytes
>> +    written. The write operation does not include erase. This routine will
>> +    attempt to write only the specified bytes. If the writes do not stick,
>> +    it will return an error.
>> +
>> +  Arguments:
>> +    This                  - Calling context
>> +    Lba                   - Block in which to begin write
>> +    Offset                - Offset in the block at which to begin write
>> +    NumBytes              - On input, indicates the requested write size. On
>> +                            output, indicates the actual number of bytes
>> +                            written
>> +    Buffer                - Buffer containing source data for the write.
>> +
>> +  Returns:
>> +    EFI_SUCCESS           - The firmware volume was written successfully
>> +    EFI_BAD_BUFFER_SIZE   - Write attempted across a LBA boundary. On output,
>> +                            NumBytes contains the total number of bytes
>> +                            actually written
>> +    EFI_ACCESS_DENIED     - The firmware volume is in the WriteDisabled state
>> +    EFI_DEVICE_ERROR      - The block device is not functioning correctly and
>> +                            could not be written
>> +    EFI_INVALID_PARAMETER - NumBytes or Buffer are NULL
>> +
>> +--*/
>> +{
>> +  return QemuFlashWrite (
>> +           (EFI_LBA)Lba,
>> +           (UINTN)Offset,
>> +           NumBytes,
>> +           (UINT8 *)Buffer
>> +           );
>> +}
>> +
>> +EFI_STATUS
>> +EFIAPI
>> +FvbProtocolRead (
>> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
>> +  IN CONST EFI_LBA                             Lba,
>> +  IN CONST UINTN                               Offset,
>> +  IN OUT UINTN                                 *NumBytes,
>> +  IN UINT8                                     *Buffer
>> +  )
>> +/*++
>> +
>> +  Routine Description:
>> +
>> +    Reads data beginning at Lba:Offset from FV. The Read terminates either
>> +    when *NumBytes of data have been read, or when a block boundary is
>> +    reached.  *NumBytes is updated to reflect the actual number of bytes
>> +    written. The write operation does not include erase. This routine will
>> +    attempt to write only the specified bytes. If the writes do not stick,
>> +    it will return an error.
>> +
>> +  Arguments:
>> +    This                  - Calling context
>> +    Lba                   - Block in which to begin Read
>> +    Offset                - Offset in the block at which to begin Read
>> +    NumBytes              - On input, indicates the requested write size. On
>> +                            output, indicates the actual number of bytes Read
>> +    Buffer                - Buffer containing source data for the Read.
>> +
>> +  Returns:
>> +    EFI_SUCCESS           - The firmware volume was read successfully and
>> +                            contents are in Buffer
>> +    EFI_BAD_BUFFER_SIZE   - Read attempted across a LBA boundary. On output,
>> +                            NumBytes contains the total number of bytes
>> +                            returned in Buffer
>> +    EFI_ACCESS_DENIED     - The firmware volume is in the ReadDisabled state
>> +    EFI_DEVICE_ERROR      - The block device is not functioning correctly and
>> +                            could not be read
>> +    EFI_INVALID_PARAMETER - NumBytes or Buffer are NULL
>> +
>> +--*/
>> +{
>> +  return QemuFlashRead (
>> +           (EFI_LBA)Lba,
>> +           (UINTN)Offset,
>> +           NumBytes,
>> +           (UINT8 *)Buffer
>> +           );
>> +}
>> +
>> +EFI_STATUS
>> +ValidateFvHeader (
>> +  EFI_FIRMWARE_VOLUME_HEADER  *FwVolHeader
>> +  )
>> +/*++
>> +
>> +  Routine Description:
>> +    Check the integrity of firmware volume header
>> +
>> +  Arguments:
>> +    FwVolHeader           - A pointer to a firmware volume header
>> +
>> +  Returns:
>> +    EFI_SUCCESS           - The firmware volume is consistent
>> +    EFI_NOT_FOUND         - The firmware volume has corrupted. So it is not an
>> +                            FV
>> +
>> +--*/
>> +{
>> +  UINT16  Checksum;
>> +
>> +  //
>> +  // Verify the header revision, header signature, length
>> +  // Length of FvBlock cannot be 2**64-1
>> +  // HeaderLength cannot be an odd number
>> +  //
>> +  if ((FwVolHeader->Revision != EFI_FVH_REVISION) ||
>> +      (FwVolHeader->Signature != EFI_FVH_SIGNATURE) ||
>> +      (FwVolHeader->FvLength == ((UINTN)-1)) ||
>> +      ((FwVolHeader->HeaderLength & 0x01) != 0)
>> +      )
>> +  {
>> +    return EFI_NOT_FOUND;
>> +  }
>> +
>> +  //
>> +  // Verify the header checksum
>> +  //
>> +
>> +  Checksum = CalculateSum16 (
>> +               (UINT16 *)FwVolHeader,
>> +               FwVolHeader->HeaderLength
>> +               );
>> +  if (Checksum != 0) {
>> +    UINT16  Expected;
>> +
>> +    Expected =
>> +      (UINT16)(((UINTN)FwVolHeader->Checksum + 0x10000 - Checksum) & 0xffff);
>> +
>> +    DEBUG ((
>> +      DEBUG_INFO,
>> +      "FV@%p Checksum is 0x%x, expected 0x%x\n",
>> +      FwVolHeader,
>> +      FwVolHeader->Checksum,
>> +      Expected
>> +      ));
>> +    return EFI_NOT_FOUND;
>> +  }
>> +
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +STATIC
>> +EFI_STATUS
>> +MarkMemoryRangeForRuntimeAccess (
>> +  EFI_PHYSICAL_ADDRESS                BaseAddress,
>> +  UINTN                               Length
>> +  )
>> +{
>> +  EFI_STATUS                          Status;
>> +
>> +  //
>> +  // Mark flash region as runtime memory
>> +  //
>> +  Status = gDS->RemoveMemorySpace (
>> +                  BaseAddress,
>> +                  Length
>> +                  );
>> +
>> +  Status = gDS->AddMemorySpace (
>> +                  EfiGcdMemoryTypeSystemMemory,
>> +                  BaseAddress,
>> +                  Length,
>> +                  EFI_MEMORY_UC | EFI_MEMORY_RUNTIME
>> +                  );
>> +  ASSERT_EFI_ERROR (Status);
>> +
>> +  Status = gBS->AllocatePages (
>> +                  AllocateAddress,
>> +                  EfiRuntimeServicesData,
>> +                  EFI_SIZE_TO_PAGES (Length),
>> +                  &BaseAddress
>> +                  );
>> +  ASSERT_EFI_ERROR (Status);
>> +
>> +  return Status;
>> +}
>> +
>> +STATIC
>> +EFI_STATUS
>> +InitializeVariableFvHeader (
>> +  VOID
>> +  )
>> +{
>> +  EFI_STATUS                  Status;
>> +  EFI_FIRMWARE_VOLUME_HEADER  *GoodFwVolHeader;
>> +  EFI_FIRMWARE_VOLUME_HEADER  *FwVolHeader;
>> +  UINTN                       Length;
>> +  UINTN                       WriteLength;
>> +  UINTN                       BlockSize;
>> +
>> +  FwVolHeader =
>> +    (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)
>> +    PcdGet64 (PcdOvmfFlashNvStorageVariableBase);
>> +
>> +  Length =  FixedPcdGet32 (PcdAllVarSize);
>> +  BlockSize = PcdGet32 (PcdFlashBlockSize);
>> +
>> +  Status = ValidateFvHeader (FwVolHeader);
>> +  if (!EFI_ERROR (Status)) {
>> +    if ((FwVolHeader->FvLength != Length) ||
>> +        (FwVolHeader->BlockMap[0].Length != BlockSize))
>> +    {
>> +      Status = EFI_VOLUME_CORRUPTED;
>> +    }
>> +  }
>> +  if (EFI_ERROR (Status)) {
>> +    UINTN  Offset;
>> +    UINTN  Start;
>> +
>> +    DEBUG ((
>> +      DEBUG_INFO,
>> +      "Variable FV header is not valid. It will be reinitialized.\n"
>> +      ));
>> +
>> +    //
>> +    // Get FvbInfo to provide in FwhInstance.
>> +    //
>> +    Status = GetFvbInfo (Length, &GoodFwVolHeader);
>> +    ASSERT (!EFI_ERROR (Status));
>> +
>> +    Start = (UINTN)(UINT8*) FwVolHeader - PcdGet64 (PcdFlashFdBase);
>> +    ASSERT (Start % BlockSize == 0 && Length % BlockSize == 0);
>> +    ASSERT (GoodFwVolHeader->HeaderLength <= BlockSize);
>> +
>> +    //
>> +    // Erase all the blocks
>> +    //
>> +    for (Offset = Start; Offset < Start + Length; Offset += BlockSize) {
>> +      Status = QemuFlashEraseBlock (Offset / BlockSize);
>> +      ASSERT_EFI_ERROR (Status);
>> +    }
>> +
>> +    //
>> +    // Write good FV header
>> +    //
>> +    WriteLength = GoodFwVolHeader->HeaderLength;
>> +    Status      = QemuFlashWrite (
>> +                    Start / BlockSize,
>> +                    0,
>> +                    &WriteLength,
>> +                    (UINT8 *)GoodFwVolHeader
>> +                    );
>> +    ASSERT_EFI_ERROR (Status);
>> +    ASSERT (WriteLength == GoodFwVolHeader->HeaderLength);
>> +  }
>> +
>> +  return Status;
>> +}
>> +
>> +EFI_STATUS
>> +EFIAPI
>> +FvbInitialize (
>> +  IN EFI_HANDLE        ImageHandle,
>> +  IN EFI_SYSTEM_TABLE  *SystemTable
>> +  )
>> +/*++
>> +
>> +  Routine Description:
>> +    This function does common initialization for FVB services
>> +
>> +  Arguments:
>> +
>> +  Returns:
>> +
>> +--*/
>> +{
>> +  EFI_STATUS                  Status;
>> +  EFI_FW_VOL_INSTANCE         *FwhInstance;
>> +  EFI_FIRMWARE_VOLUME_HEADER  *FwVolHeader;
>> +  UINT32                      BufferSize;
>> +  EFI_FV_BLOCK_MAP_ENTRY      *PtrBlockMapEntry;
>> +  EFI_FW_VOL_BLOCK_DEVICE     *FvbDevice;
>> +  UINT32                      MaxLbaSize;
>> +  EFI_PHYSICAL_ADDRESS        BaseAddress;
>> +  UINTN                       Length;
>> +  UINTN                       NumOfBlocks;
>> +  RETURN_STATUS               PcdStatus;
>> +
>> +  if (EFI_ERROR (QemuFlashInitialize ())) {
>> +    //
>> +    // Return an error so image will be unloaded
>> +    //
>> +    DEBUG ((
>> +      DEBUG_INFO,
>> +      "QEMU flash was not detected. Writable FVB is not being installed.\n"
>> +      ));
>> +    return EFI_WRITE_PROTECTED;
>> +  }
>> +
>> +  //
>> +  // Allocate runtime services data for global variable, which contains
>> +  // the private data of all firmware volume block instances
>> +  //
>> +  mFvbModuleGlobal = AllocateRuntimePool (sizeof (ESAL_FWB_GLOBAL));
>> +  ASSERT (mFvbModuleGlobal != NULL);
>> +
>> +  BaseAddress = (UINTN) PcdGet64 (PcdOvmfFlashNvStorageVariableBase);
>> +  Length = PcdGet32 (PcdAllVarSize);
>> +
>> +  Status = InitializeVariableFvHeader ();
>> +  if (EFI_ERROR (Status)) {
>> +    DEBUG ((
>> +      DEBUG_INFO,
>> +      "QEMU Flash: Unable to initialize variable FV header\n"
>> +      ));
>> +    return EFI_WRITE_PROTECTED;
>> +  }
>> +
>> +  FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)BaseAddress;
>> +  Status      = ValidateFvHeader (FwVolHeader);
>> +  if (EFI_ERROR (Status)) {
>> +    //
>> +    // Get FvbInfo
>> +    //
>> +    Status = GetFvbInfo (Length, &FwVolHeader);
>> +    if (EFI_ERROR (Status)) {
>> +      DEBUG ((DEBUG_INFO, "EFI_ERROR (GetFvbInfo (Length, &FwVolHeader))\n"));
>> +      return EFI_WRITE_PROTECTED;
>> +    }
>> +  }
>> +
>> +  BufferSize = (sizeof (EFI_FW_VOL_INSTANCE) +
>> +                FwVolHeader->HeaderLength -
>> +                sizeof (EFI_FIRMWARE_VOLUME_HEADER)
>> +                );
>> +  mFvbModuleGlobal->FvInstance = AllocateRuntimePool (BufferSize);
>> +  ASSERT (mFvbModuleGlobal->FvInstance != NULL);
>> +
>> +  FwhInstance = mFvbModuleGlobal->FvInstance;
>> +
>> +  mFvbModuleGlobal->NumFv = 0;
>> +  MaxLbaSize              = 0;
>> +
>> +  FwVolHeader =
>> +    (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)
>> +    PcdGet64 (PcdOvmfFlashNvStorageVariableBase);
>> +
>> +  FwhInstance->FvBase = (UINTN)BaseAddress;
>> +
>> +  CopyMem (
>> +    (UINTN *)&(FwhInstance->VolumeHeader),
>> +    (UINTN *)FwVolHeader,
>> +    FwVolHeader->HeaderLength
>> +    );
>> +  FwVolHeader = &(FwhInstance->VolumeHeader);
>> +
>> +  NumOfBlocks = 0;
>> +
>> +  for (PtrBlockMapEntry = FwVolHeader->BlockMap;
>> +       PtrBlockMapEntry->NumBlocks != 0;
>> +       PtrBlockMapEntry++)
>> +  {
>> +    //
>> +    // Get the maximum size of a block.
>> +    //
>> +    if (MaxLbaSize < PtrBlockMapEntry->Length) {
>> +      MaxLbaSize = PtrBlockMapEntry->Length;
>> +    }
>> +
>> +    NumOfBlocks = NumOfBlocks + PtrBlockMapEntry->NumBlocks;
>> +  }
>> +
>> +  //
>> +  // The total number of blocks in the FV.
>> +  //
>> +  FwhInstance->NumOfBlocks = NumOfBlocks;
>> +
>> +  //
>> +  // Add a FVB Protocol Instance
>> +  //
>> +  FvbDevice = AllocateRuntimePool (sizeof (EFI_FW_VOL_BLOCK_DEVICE));
>> +  ASSERT (FvbDevice != NULL);
>> +
>> +  CopyMem (FvbDevice, &mFvbDeviceTemplate, sizeof (EFI_FW_VOL_BLOCK_DEVICE));
>> +
>> +  FvbDevice->Instance = mFvbModuleGlobal->NumFv;
>> +  mFvbModuleGlobal->NumFv++;
>> +
>> +  //
>> +  // Set up the devicepath
>> +  //
>> +  if (FwVolHeader->ExtHeaderOffset == 0) {
>> +    FV_MEMMAP_DEVICE_PATH  *FvMemmapDevicePath;
>> +
>> +    //
>> +    // FV does not contains extension header, then produce MEMMAP_DEVICE_PATH
>> +    //
>> +    FvMemmapDevicePath = AllocateCopyPool (
>> +                           sizeof (FV_MEMMAP_DEVICE_PATH),
>> +                           &mFvMemmapDevicePathTemplate
>> +                           );
>> +    FvMemmapDevicePath->MemMapDevPath.StartingAddress = BaseAddress;
>> +    FvMemmapDevicePath->MemMapDevPath.EndingAddress   =
>> +      BaseAddress + FwVolHeader->FvLength - 1;
>> +    FvbDevice->DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)FvMemmapDevicePath;
>> +  } else {
>> +    FV_PIWG_DEVICE_PATH  *FvPiwgDevicePath;
>> +
>> +    FvPiwgDevicePath = AllocateCopyPool (
>> +                         sizeof (FV_PIWG_DEVICE_PATH),
>> +                         &mFvPIWGDevicePathTemplate
>> +                         );
>> +    CopyGuid (
>> +      &FvPiwgDevicePath->FvDevPath.FvName,
>> +      (GUID *)(UINTN)(BaseAddress + FwVolHeader->ExtHeaderOffset)
>> +      );
>> +    FvbDevice->DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)FvPiwgDevicePath;
>> +  }
>> +
>> +  //
>> +  // Module type specific hook.
>> +  //
>> +  InstallProtocolInterfaces (FvbDevice);
>> +
>> +  MarkMemoryRangeForRuntimeAccess (BaseAddress, Length);
>> +
>> +  //
>> +  // Set several PCD values to point to flash
>> +  //
>> +
>> +
>> +  PcdStatus = PcdSet64S (
>> +    PcdFlashNvStorageVariableBase64,
>> +    (UINTN) PcdGet64 (PcdOvmfFlashNvStorageVariableBase)
>> +    );
>> +  ASSERT_RETURN_ERROR (PcdStatus);
>> +  PcdStatus = PcdSet64S (
>> +    PcdFlashNvStorageFtwWorkingBase64,
>> +    PcdGet64 (PcdOvmfFlashNvStorageFtwWorkingBase)
>> +    );
>> +  ASSERT_RETURN_ERROR (PcdStatus);
>> +  PcdStatus = PcdSet64S (
>> +    PcdFlashNvStorageFtwSpareBase64,
>> +    PcdGet64 (PcdOvmfFlashNvStorageFtwSpareBase)
>> +    );
>> +  ASSERT_RETURN_ERROR (PcdStatus);
>> +
>> +  FwhInstance = (EFI_FW_VOL_INSTANCE *)
>> +                (
>> +                 (UINTN)((UINT8 *)FwhInstance) + FwVolHeader->HeaderLength +
>> +                 (sizeof (EFI_FW_VOL_INSTANCE) - sizeof (EFI_FIRMWARE_VOLUME_HEADER))
>> +                );
>> +
>> +  //
>> +  // Module type specific hook.
>> +  //
>> +  InstallVirtualAddressChangeHandler ();
>> +
>> +  PcdStatus = PcdSetBoolS (PcdOvmfFlashVariablesEnable, TRUE);
>> +  ASSERT_RETURN_ERROR (PcdStatus);
>> +  return EFI_SUCCESS;
>> +}
>> diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.h b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.h
>> new file mode 100644
>> index 0000000000..e7234a0c5e
>> --- /dev/null
>> +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockService.h
>> @@ -0,0 +1,178 @@
>> +/** @file
>> +  Firmware volume block driver for Intel Firmware Hub (FWH) device
>> +
>> +  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
>> +
>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>> +
>> +**/
>> +#ifndef _FW_BLOCK_SERVICE_H
>> +#define _FW_BLOCK_SERVICE_H
>> +
>> +typedef struct {
>> +  UINTN                         FvBase;
>> +  UINTN                         NumOfBlocks;
>> +  EFI_FIRMWARE_VOLUME_HEADER    VolumeHeader;
>> +} EFI_FW_VOL_INSTANCE;
>> +
>> +typedef struct {
>> +  UINT32                 NumFv;
>> +  EFI_FW_VOL_INSTANCE    *FvInstance;
>> +} ESAL_FWB_GLOBAL;
>> +
>> +extern ESAL_FWB_GLOBAL  *mFvbModuleGlobal;
>> +
>> +//
>> +// Fvb Protocol instance data
>> +//
>> +#define FVB_DEVICE_FROM_THIS(a)  CR (a, EFI_FW_VOL_BLOCK_DEVICE,\
>> +                                  FwVolBlockInstance, FVB_DEVICE_SIGNATURE)
>> +
>> +#define FVB_EXTEND_DEVICE_FROM_THIS(a)  CR (a, EFI_FW_VOL_BLOCK_DEVICE,\
>> +                                         FvbExtension, FVB_DEVICE_SIGNATURE)
>> +
>> +#define FVB_DEVICE_SIGNATURE  SIGNATURE_32 ('F', 'V', 'B', 'N')
>> +
>> +typedef struct {
>> +  MEDIA_FW_VOL_DEVICE_PATH    FvDevPath;
>> +  EFI_DEVICE_PATH_PROTOCOL    EndDevPath;
>> +} FV_PIWG_DEVICE_PATH;
>> +
>> +typedef struct {
>> +  MEMMAP_DEVICE_PATH          MemMapDevPath;
>> +  EFI_DEVICE_PATH_PROTOCOL    EndDevPath;
>> +} FV_MEMMAP_DEVICE_PATH;
>> +
>> +typedef struct {
>> +  UINTN                                 Signature;
>> +  EFI_DEVICE_PATH_PROTOCOL              *DevicePath;
>> +  UINTN                                 Instance;
>> +  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL    FwVolBlockInstance;
>> +} EFI_FW_VOL_BLOCK_DEVICE;
>> +
>> +EFI_STATUS
>> +GetFvbInfo (
>> +  IN  UINT64                      FvLength,
>> +  OUT EFI_FIRMWARE_VOLUME_HEADER  **FvbInfo
>> +  );
>> +
>> +EFI_STATUS
>> +FvbSetVolumeAttributes (
>> +  IN UINTN                     Instance,
>> +  IN OUT EFI_FVB_ATTRIBUTES_2  *Attributes,
>> +  IN ESAL_FWB_GLOBAL           *Global
>> +  );
>> +
>> +EFI_STATUS
>> +FvbGetVolumeAttributes (
>> +  IN UINTN                  Instance,
>> +  OUT EFI_FVB_ATTRIBUTES_2  *Attributes,
>> +  IN ESAL_FWB_GLOBAL        *Global
>> +  );
>> +
>> +EFI_STATUS
>> +FvbGetPhysicalAddress (
>> +  IN UINTN                  Instance,
>> +  OUT EFI_PHYSICAL_ADDRESS  *Address,
>> +  IN ESAL_FWB_GLOBAL        *Global
>> +  );
>> +
>> +EFI_STATUS
>> +EFIAPI
>> +FvbInitialize (
>> +  IN EFI_HANDLE        ImageHandle,
>> +  IN EFI_SYSTEM_TABLE  *SystemTable
>> +  );
>> +
>> +
>> +VOID
>> +EFIAPI
>> +FvbClassAddressChangeEvent (
>> +  IN EFI_EVENT  Event,
>> +  IN VOID       *Context
>> +  );
>> +
>> +EFI_STATUS
>> +FvbGetLbaAddress (
>> +  IN  UINTN            Instance,
>> +  IN  EFI_LBA          Lba,
>> +  OUT UINTN            *LbaAddress,
>> +  OUT UINTN            *LbaLength,
>> +  OUT UINTN            *NumOfBlocks,
>> +  IN  ESAL_FWB_GLOBAL  *Global
>> +  );
>> +
>> +//
>> +// Protocol APIs
>> +//
>> +EFI_STATUS
>> +EFIAPI
>> +FvbProtocolGetAttributes (
>> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
>> +  OUT EFI_FVB_ATTRIBUTES_2                     *Attributes
>> +  );
>> +
>> +EFI_STATUS
>> +EFIAPI
>> +FvbProtocolSetAttributes (
>> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
>> +  IN OUT EFI_FVB_ATTRIBUTES_2                  *Attributes
>> +  );
>> +
>> +EFI_STATUS
>> +EFIAPI
>> +FvbProtocolGetPhysicalAddress (
>> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
>> +  OUT EFI_PHYSICAL_ADDRESS                     *Address
>> +  );
>> +
>> +EFI_STATUS
>> +EFIAPI
>> +FvbProtocolGetBlockSize (
>> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
>> +  IN CONST EFI_LBA                             Lba,
>> +  OUT UINTN                                    *BlockSize,
>> +  OUT UINTN                                    *NumOfBlocks
>> +  );
>> +
>> +EFI_STATUS
>> +EFIAPI
>> +FvbProtocolRead (
>> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
>> +  IN CONST EFI_LBA                             Lba,
>> +  IN CONST UINTN                               Offset,
>> +  IN OUT UINTN                                 *NumBytes,
>> +  IN UINT8                                     *Buffer
>> +  );
>> +
>> +EFI_STATUS
>> +EFIAPI
>> +FvbProtocolWrite (
>> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
>> +  IN       EFI_LBA                             Lba,
>> +  IN       UINTN                               Offset,
>> +  IN OUT   UINTN                               *NumBytes,
>> +  IN       UINT8                               *Buffer
>> +  );
>> +
>> +EFI_STATUS
>> +EFIAPI
>> +FvbProtocolEraseBlocks (
>> +  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
>> +  ...
>> +  );
>> +
>> +//
>> +// The following functions have different implementations dependent on the
>> +// module type chosen for building this driver.
>> +//
>> +VOID
>> +InstallProtocolInterfaces (
>> +  IN EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice
>> +  );
>> +
>> +VOID
>> +InstallVirtualAddressChangeHandler (
>> +  VOID
>> +  );
>> +#endif
>> diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockServiceDxe.c b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockServiceDxe.c
>> new file mode 100644
>> index 0000000000..cb85499539
>> --- /dev/null
>> +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/FwBlockServiceDxe.c
>> @@ -0,0 +1,152 @@
>> +/** @file
>> +  Functions related to the Firmware Volume Block service whose
>> +  implementation is specific to the runtime DXE driver build.
>> +
>> +  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
>> +
>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>> +
>> +**/
>> +
>> +#include <Guid/EventGroup.h>
>> +#include <Library/DebugLib.h>
>> +#include <Library/DevicePathLib.h>
>> +#include <Library/PcdLib.h>
>> +#include <Library/UefiBootServicesTableLib.h>
>> +#include <Library/UefiRuntimeLib.h>
>> +#include <Protocol/DevicePath.h>
>> +#include <Protocol/FirmwareVolumeBlock.h>
>> +
>> +#include "FwBlockService.h"
>> +#include "QemuFlash.h"
>> +
>> +VOID
>> +InstallProtocolInterfaces (
>> +  IN EFI_FW_VOL_BLOCK_DEVICE  *FvbDevice
>> +  )
>> +{
>> +  EFI_STATUS                          Status;
>> +  EFI_HANDLE                          FwbHandle;
>> +  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *OldFwbInterface;
>> +
>> +  //
>> +  // Find a handle with a matching device path that has supports FW Block
>> +  // protocol
>> +  //
>> +  Status = gBS->LocateDevicePath (
>> +                  &gEfiFirmwareVolumeBlockProtocolGuid,
>> +                  &FvbDevice->DevicePath,
>> +                  &FwbHandle
>> +                  );
>> +  if (EFI_ERROR (Status)) {
>> +    //
>> +    // LocateDevicePath fails so install a new interface and device path
>> +    //
>> +    FwbHandle = NULL;
>> +    DEBUG ((DEBUG_INFO, "Installing QEMU flash FVB\n"));
>> +    Status = gBS->InstallMultipleProtocolInterfaces (
>> +                    &FwbHandle,
>> +                    &gEfiFirmwareVolumeBlockProtocolGuid,
>> +                    &FvbDevice->FwVolBlockInstance,
>> +                    &gEfiDevicePathProtocolGuid,
>> +                    FvbDevice->DevicePath,
>> +                    NULL
>> +                    );
>> +    ASSERT_EFI_ERROR (Status);
>> +  } else if (IsDevicePathEnd (FvbDevice->DevicePath)) {
>> +    //
>> +    // Device already exists, so reinstall the FVB protocol
>> +    //
>> +    Status = gBS->HandleProtocol (
>> +                    FwbHandle,
>> +                    &gEfiFirmwareVolumeBlockProtocolGuid,
>> +                    (VOID **)&OldFwbInterface
>> +                    );
>> +    ASSERT_EFI_ERROR (Status);
>> +
>> +    DEBUG ((DEBUG_INFO, "Reinstalling FVB for QEMU flash region\n"));
>> +    Status = gBS->ReinstallProtocolInterface (
>> +                    FwbHandle,
>> +                    &gEfiFirmwareVolumeBlockProtocolGuid,
>> +                    OldFwbInterface,
>> +                    &FvbDevice->FwVolBlockInstance
>> +                    );
>> +    ASSERT_EFI_ERROR (Status);
>> +  } else {
>> +    //
>> +    // There was a FVB protocol on an End Device Path node
>> +    //
>> +    ASSERT (FALSE);
>> +  }
>> +}
>> +
>> +
>> +STATIC
>> +VOID
>> +EFIAPI
>> +FvbVirtualAddressChangeEvent (
>> +  IN EFI_EVENT  Event,
>> +  IN VOID       *Context
>> +  )
>> +/*++
>> +
>> +  Routine Description:
>> +
>> +    Fixup internal data so that EFI and SAL can be call in virtual mode.
>> +    Call the passed in Child Notify event and convert the mFvbModuleGlobal
>> +    date items to there virtual address.
>> +
>> +  Arguments:
>> +
>> +    (Standard EFI notify event - EFI_EVENT_NOTIFY)
>> +
>> +  Returns:
>> +
>> +    None
>> +
>> +--*/
>> +{
>> +  EFI_FW_VOL_INSTANCE  *FwhInstance;
>> +  UINTN                Index;
>> +
>> +  FwhInstance = mFvbModuleGlobal->FvInstance;
>> +  EfiConvertPointer (0x0, (VOID **)&mFvbModuleGlobal->FvInstance);
>> +
>> +  //
>> +  // Convert the base address of all the instances
>> +  //
>> +  Index = 0;
>> +  while (Index < mFvbModuleGlobal->NumFv) {
>> +    EfiConvertPointer (0x0, (VOID **)&FwhInstance->FvBase);
>> +    FwhInstance = (EFI_FW_VOL_INSTANCE *)
>> +                  (
>> +                   (UINTN)((UINT8 *)FwhInstance) +
>> +                   FwhInstance->VolumeHeader.HeaderLength +
>> +                   (sizeof (EFI_FW_VOL_INSTANCE) - sizeof (EFI_FIRMWARE_VOLUME_HEADER))
>> +                  );
>> +    Index++;
>> +  }
>> +
>> +  EfiConvertPointer (0x0, (VOID **)&mFvbModuleGlobal);
>> +  QemuFlashConvertPointers ();
>> +}
>> +
>> +
>> +VOID
>> +InstallVirtualAddressChangeHandler (
>> +  VOID
>> +  )
>> +{
>> +  EFI_STATUS  Status;
>> +  EFI_EVENT   VirtualAddressChangeEvent;
>> +
>> +  Status = gBS->CreateEventEx (
>> +                  EVT_NOTIFY_SIGNAL,
>> +                  TPL_NOTIFY,
>> +                  FvbVirtualAddressChangeEvent,
>> +                  NULL,
>> +                  &gEfiEventVirtualAddressChangeGuid,
>> +                  &VirtualAddressChangeEvent
>> +                  );
>> +  ASSERT_EFI_ERROR (Status);
>> +}
>> diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c
>> new file mode 100644
>> index 0000000000..a85d736060
>> --- /dev/null
>> +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c
>> @@ -0,0 +1,251 @@
>> +/** @file
>> +  LoongArch support for QEMU system firmware flash device
>> +
>> +  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
>> +
>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>> +
>> +**/
>> +
>> +#include <Library/BaseMemoryLib.h>
>> +#include <Library/DebugLib.h>
>> +#include <Library/PcdLib.h>
>> +
>> +#include "QemuFlash.h"
>> +
>> +#define WRITE_BYTE_CMD           0x10
>> +#define BLOCK_ERASE_CMD          0x20
>> +#define CLEAR_STATUS_CMD         0x50
>> +#define READ_STATUS_CMD          0x70
>> +#define READ_DEVID_CMD           0x90
>> +#define BLOCK_ERASE_CONFIRM_CMD  0xd0
>> +#define READ_ARRAY_CMD           0xff
>> +
>> +#define CLEARED_ARRAY_STATUS  0x00
>> +
>> +UINT8  *mFlashBase;
>> +
>> +STATIC UINTN  mFdBlockSize  = 0;
>> +STATIC UINTN  mFdBlockCount = 0;
>> +
>> +STATIC
>> +volatile UINT8 *
>> +QemuFlashPtr (
>> +  IN        EFI_LBA  Lba,
>> +  IN        UINTN    Offset
>> +  )
>> +{
>> +  return mFlashBase + ((UINTN)Lba * mFdBlockSize) + Offset;
>> +}
>> +
>> +
>> +/**
>> +  Determines if the QEMU flash memory device is present.
>> +
>> +  @retval FALSE   The QEMU flash device is not present.
>> +  @retval TRUE    The QEMU flash device is present.
>> +
>> +**/
>> +STATIC
>> +BOOLEAN
>> +QemuFlashDetected (
>> +  VOID
>> +  )
>> +{
>> +  BOOLEAN         FlashDetected;
>> +  volatile UINT8  *Ptr;
>> +
>> +  UINTN  Offset;
>> +  UINT8  OriginalUint8;
>> +  UINT8  ProbeUint8;
>> +
>> +  FlashDetected = FALSE;
>> +  Ptr           = QemuFlashPtr (0, 0);
>> +
>> +  for (Offset = 0; Offset < mFdBlockSize; Offset++) {
>> +    Ptr        = QemuFlashPtr (0, Offset);
>> +    ProbeUint8 = *Ptr;
>> +    if ((ProbeUint8 != CLEAR_STATUS_CMD) &&
>> +        (ProbeUint8 != READ_STATUS_CMD) &&
>> +        (ProbeUint8 != CLEARED_ARRAY_STATUS))
>> +    {
>> +      break;
>> +    }
>> +  }
>> +
>> +  if (Offset >= mFdBlockSize) {
>> +    DEBUG ((DEBUG_INFO, "QEMU Flash: Failed to find probe location\n"));
>> +    return FALSE;
>> +  }
>> +
>> +  DEBUG ((DEBUG_INFO, "QEMU Flash: Attempting flash detection at %p\n", Ptr));
>> +
>> +  OriginalUint8 = *Ptr;
>> +  *Ptr          = CLEAR_STATUS_CMD;
>> +  ProbeUint8    = *Ptr;
>> +  if ((OriginalUint8 != CLEAR_STATUS_CMD) &&
>> +      (ProbeUint8 == CLEAR_STATUS_CMD))
>> +  {
>> +    DEBUG ((DEBUG_INFO, "QemuFlashDetected => FD behaves as RAM\n"));
>> +    *Ptr = OriginalUint8;
>> +  } else {
>> +    *Ptr       = READ_STATUS_CMD;
>> +    ProbeUint8 = *Ptr;
>> +    if (ProbeUint8 == OriginalUint8) {
>> +      DEBUG ((DEBUG_INFO, "QemuFlashDetected => FD behaves as ROM\n"));
>> +    } else if (ProbeUint8 == READ_STATUS_CMD) {
>> +      DEBUG ((DEBUG_INFO, "QemuFlashDetected => FD behaves as RAM\n"));
>> +      *Ptr = OriginalUint8;
>> +    } else if (ProbeUint8 == CLEARED_ARRAY_STATUS) {
>> +      DEBUG ((DEBUG_INFO, "QemuFlashDetected => FD behaves as FLASH\n"));
>> +      FlashDetected = TRUE;
>> +      *Ptr          = READ_ARRAY_CMD;
>> +    }
>> +  }
>> +
>> +  DEBUG ((
>> +    DEBUG_INFO,
>> +    "QemuFlashDetected => %a\n",
>> +    FlashDetected ? "Yes" : "No"
>> +    ));
>> +  return FlashDetected;
>> +}
>> +
>> +
>> +/**
>> +  Read from QEMU Flash
>> +
>> +  @param[in] Lba      The starting logical block index to read from.
>> +  @param[in] Offset   Offset into the block at which to begin reading.
>> +  @param[in] NumBytes On input, indicates the requested read size. On
>> +                      output, indicates the actual number of bytes read
>> +  @param[in] Buffer   Pointer to the buffer to read into.
>> +
>> +**/
>> +EFI_STATUS
>> +QemuFlashRead (
>> +  IN        EFI_LBA  Lba,
>> +  IN        UINTN    Offset,
>> +  IN        UINTN    *NumBytes,
>> +  IN        UINT8    *Buffer
>> +  )
>> +{
>> +  UINT8  *Ptr;
>> +
>> +  //
>> +  // Only write to the first 64k. We don't bother saving the FTW Spare
>> +  // block into the flash memory.
>> +  //
>> +  if (Lba >= mFdBlockCount) {
>> +    return EFI_INVALID_PARAMETER;
>> +  }
>> +
>> +  //
>> +  // Get flash address
>> +  //
>> +  Ptr = (UINT8 *)QemuFlashPtr (Lba, Offset);
>> +
>> +  CopyMem (Buffer, Ptr, *NumBytes);
>> +
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +
>> +/**
>> +  Write to QEMU Flash
>> +
>> +  @param[in] Lba      The starting logical block index to write to.
>> +  @param[in] Offset   Offset into the block at which to begin writing.
>> +  @param[in] NumBytes On input, indicates the requested write size. On
>> +                      output, indicates the actual number of bytes written
>> +  @param[in] Buffer   Pointer to the data to write.
>> +
>> +**/
>> +EFI_STATUS
>> +QemuFlashWrite (
>> +  IN        EFI_LBA  Lba,
>> +  IN        UINTN    Offset,
>> +  IN        UINTN    *NumBytes,
>> +  IN        UINT8    *Buffer
>> +  )
>> +{
>> +  volatile UINT8  *Ptr;
>> +  UINTN           Loop;
>> +
>> +  //
>> +  // Only write to the first 64k. We don't bother saving the FTW Spare
>> +  // block into the flash memory.
>> +  //
>> +  if (Lba >= mFdBlockCount) {
>> +    return EFI_INVALID_PARAMETER;
>> +  }
>> +
>> +  //
>> +  // Program flash
>> +  //
>> +  Ptr = QemuFlashPtr (Lba, Offset);
>> +  for (Loop = 0; Loop < *NumBytes; Loop++) {
>> +    *Ptr = WRITE_BYTE_CMD;
>> +    *Ptr = Buffer[Loop];
>> +    Ptr++;
>> +  }
>> +
>> +  //
>> +  // Restore flash to read mode
>> +  //
>> +  if (*NumBytes > 0) {
>> +    *(Ptr - 1) = READ_ARRAY_CMD;
>> +  }
>> +
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +
>> +/**
>> +  Erase a QEMU Flash block
>> +
>> +  @param Lba    The logical block index to erase.
>> +
>> +**/
>> +EFI_STATUS
>> +QemuFlashEraseBlock (
>> +  IN   EFI_LBA  Lba
>> +  )
>> +{
>> +  volatile UINT8  *Ptr;
>> +
>> +  if (Lba >= mFdBlockCount) {
>> +    return EFI_INVALID_PARAMETER;
>> +  }
>> +
>> +  Ptr = QemuFlashPtr (Lba, 0);
>> +  *Ptr = BLOCK_ERASE_CMD;
>> +  *Ptr = BLOCK_ERASE_CONFIRM_CMD;
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +
>> +/**
>> +  Initializes QEMU flash memory support
>> +
>> +  @retval EFI_WRITE_PROTECTED   The QEMU flash device is not present.
>> +  @retval EFI_SUCCESS           The QEMU flash device is supported.
>> +
>> +**/
>> +EFI_STATUS
>> +QemuFlashInitialize (
>> +  VOID
>> +  )
>> +{
>> +  mFlashBase   = (UINT8 *)(UINTN)PcdGet64 (PcdOvmfFlashNvStorageVariableBase);
>> +  mFdBlockSize = PcdGet32 (PcdFlashBlockSize);
>> +  ASSERT(PcdGet32 (PcdAllVarSize) % mFdBlockSize == 0);
>> +  mFdBlockCount = PcdGet32 (PcdAllVarSize) / mFdBlockSize;
>> +
>> +  if (!QemuFlashDetected ()) {
>> +    return EFI_WRITE_PROTECTED;
>> +  }
>> +
>> +  return EFI_SUCCESS;
>> +}
>> +
>> diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.h b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.h
>> new file mode 100644
>> index 0000000000..27caabc5f0
>> --- /dev/null
>> +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlash.h
>> @@ -0,0 +1,86 @@
>> +/** @file
>> +  LoongArch support for QEMU system firmware flash device
>> +
>> +  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
>> +
>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>> +
>> +**/
>> +
>> +#ifndef __QEMU_FLASH_H__
>> +#define __QEMU_FLASH_H__
>> +
>> +#include <Protocol/FirmwareVolumeBlock.h>
>> +
>> +extern UINT8  *mFlashBase;
>> +
>> +/**
>> +  Read from QEMU Flash
>> +
>> +  @param[in] Lba      The starting logical block index to read from.
>> +  @param[in] Offset   Offset into the block at which to begin reading.
>> +  @param[in] NumBytes On input, indicates the requested read size. On
>> +                      output, indicates the actual number of bytes read
>> +  @param[in] Buffer   Pointer to the buffer to read into.
>> +
>> +**/
>> +EFI_STATUS
>> +QemuFlashRead (
>> +  IN        EFI_LBA  Lba,
>> +  IN        UINTN    Offset,
>> +  IN        UINTN    *NumBytes,
>> +  IN        UINT8    *Buffer
>> +  );
>> +
>> +
>> +/**
>> +  Write to QEMU Flash
>> +
>> +  @param[in] Lba      The starting logical block index to write to.
>> +  @param[in] Offset   Offset into the block at which to begin writing.
>> +  @param[in] NumBytes On input, indicates the requested write size. On
>> +                      output, indicates the actual number of bytes written
>> +  @param[in] Buffer   Pointer to the data to write.
>> +
>> +**/
>> +EFI_STATUS
>> +QemuFlashWrite (
>> +  IN        EFI_LBA  Lba,
>> +  IN        UINTN    Offset,
>> +  IN        UINTN    *NumBytes,
>> +  IN        UINT8    *Buffer
>> +  );
>> +
>> +
>> +/**
>> +  Erase a QEMU Flash block
>> +
>> +  @param Lba    The logical block index to erase.
>> +
>> +**/
>> +EFI_STATUS
>> +QemuFlashEraseBlock (
>> +  IN   EFI_LBA  Lba
>> +  );
>> +
>> +
>> +/**
>> +  Initializes QEMU flash memory support
>> +
>> +  @retval EFI_WRITE_PROTECTED   The QEMU flash device is not present.
>> +  @retval EFI_SUCCESS           The QEMU flash device is supported.
>> +
>> +**/
>> +EFI_STATUS
>> +QemuFlashInitialize (
>> +  VOID
>> +  );
>> +
>> +
>> +VOID
>> +QemuFlashConvertPointers (
>> +  VOID
>> +  );
>> +
>> +#endif
>> +
>> diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c
>> new file mode 100644
>> index 0000000000..b63314aac2
>> --- /dev/null
>> +++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/QemuFlashFvbServicesRuntimeDxe/QemuFlashDxe.c
>> @@ -0,0 +1,21 @@
>> +/** @file
>> +  LoongArch support for QEMU system firmware flash device: functions specific to the
>> +  runtime DXE driver build.
>> +
>> +  Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
>> +
>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>> +
>> +**/
>> +
>> +#include <Library/UefiRuntimeLib.h>
>> +
>> +#include "QemuFlash.h"
>> +
>> +VOID
>> +QemuFlashConvertPointers (
>> +  VOID
>> +  )
>> +{
>> +  EfiConvertPointer (0x0, (VOID **)&mFlashBase);
>> +}
>> diff --git a/Platform/Loongson/LoongArchQemuPkg/Loongson.dsc b/Platform/Loongson/LoongArchQemuPkg/Loongson.dsc
>> index 74c83720b7..59beafb34f 100644
>> --- a/Platform/Loongson/LoongArchQemuPkg/Loongson.dsc
>> +++ b/Platform/Loongson/LoongArchQemuPkg/Loongson.dsc
>> @@ -171,10 +171,10 @@
>>     VirtioLib                        | OvmfPkg/Library/VirtioLib/VirtioLib.inf
>>     FrameBufferBltLib                | MdeModulePkg/Library/FrameBufferBltLib/FrameBufferBltLib.inf
>>     QemuFwCfgLib                     | OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibMmio.inf
>> -
>>     DebugLib                         | MdePkg/Library/BaseDebugLibSerialPort/BaseDebugLibSerialPort.inf
>> -
>>     PeiServicesLib                   | MdePkg/Library/PeiServicesLib/PeiServicesLib.inf
>> +  VariableFlashInfoLib             | MdeModulePkg/Library/BaseVariableFlashInfoLib/BaseVariableFlashInfoLib.inf
>> +
>>   [LibraryClasses.common.SEC]
>>     ReportStatusCodeLib              | MdeModulePkg/Library/PeiReportStatusCodeLib/PeiReportStatusCodeLib.inf
>>     HobLib                           | MdePkg/Library/PeiHobLib/PeiHobLib.inf
>> --
>> 2.31.1
>>



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#95293): https://edk2.groups.io/g/devel/message/95293
Mute This Topic: https://groups.io/mt/94320543/1813853
Group Owner: devel+owner at edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [edk2-devel-archive at redhat.com]
-=-=-=-=-=-=-=-=-=-=-=-




More information about the edk2-devel-archive mailing list