UEFI下FirmwareVolumeBlockService驱动的实现

这个驱动是和variable的驱动一起使用的,是variable驱动的下一层,variable是要使用这个驱动中的read和write函数的,然后这个驱动去调用FlashOperationProtocolsDxe中的read和write函数。

下面看一下这个驱动的具体实现:下面是驱动入口的函数

EFI_STATUS
EFIAPI
FvbInitialize (
  IN EFI_HANDLE         ImageHandle,
  IN EFI_SYSTEM_TABLE   *SystemTable
  )
{
  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *OldFwbInterface;
  EFI_FIRMWARE_VOLUME_HEADER          *FwVolHeader;
  EFI_FW_VOL_INSTANCE                 *FwhInstance;
  EFI_FV_BLOCK_MAP_ENTRY              *PtrBlockMapEntry;
  EFI_FW_VOL_BLOCK_DEVICE             *FvbDevice;
  EFI_PHYSICAL_ADDRESS                BaseAddress;
  EFI_DXE_SERVICES                    *DxeServices;
  EFI_HANDLE                          FwbHandle;
  EFI_STATUS                          Status;
  UINT32                              BufferSize;
  UINT32                              MaxLbaSize;
  UINT64                              Length;
  UINTN                               NumOfBlocks;

  //
  // Get the DXE services table
  //
  DbgPrint(DEBUG_INFO,"Enter FvbInitialize(). \n");
  DxeServices = gDS;
  Status = gBS->LocateProtocol (
                  &gFlashDeviceOperationProtocolGuid,
                  NULL,
                  (VOID **) &mFlashDevOpProtocol
           );
  if(EFI_ERROR(Status)){
    DEBUG((EFI_D_ERROR, "Flash Operation Protocol Not Installed.\n"));
    DbgPrint(DEBUG_INFO,"Return FvbInitialize() (%r)\n",Status);
    return Status;
  }

  BaseAddress = PcdGet64(PcdFlashNvStorageVariableBase64);
  Length      = FV_LENGTH;

  DbgPrint(DEBUG_INFO,"GetFvbBaseAddress:0x%llx \n",BaseAddress);
  FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *) (UINTN) BaseAddress;
  Status = ValidateFvHeader (FwVolHeader);
  ASSERT_EFI_ERROR(Status);

  //
  // 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);
  //
  // Calculate the total size for all firmware volume block instances
  //
  BufferSize = (sizeof (EFI_FW_VOL_INSTANCE) + FwVolHeader->HeaderLength - sizeof (EFI_FIRMWARE_VOLUME_HEADER));

  mFvbModuleGlobal->FvInstance[FVB_PHYSICAL] = AllocateRuntimePool (BufferSize);
  ASSERT (mFvbModuleGlobal->FvInstance[FVB_PHYSICAL] != NULL);

  //
  // Make a virtual copy of the FvInstance pointer.
  //
  FwhInstance = mFvbModuleGlobal->FvInstance[FVB_PHYSICAL];
  mFvbModuleGlobal->FvInstance[FVB_VIRTUAL] = FwhInstance;
  mFvbModuleGlobal->NumFv                   = 0;
  MaxLbaSize = 0;

  FwhInstance->FvBase[FVB_PHYSICAL] = (UINTN) BaseAddress;
  FwhInstance->FvBase[FVB_VIRTUAL]  = (UINTN) BaseAddress;
  CopyMem ((UINTN *) &(FwhInstance->VolumeHeader), (UINTN *) FwVolHeader, FwVolHeader->HeaderLength);
  FwVolHeader = &(FwhInstance->VolumeHeader);
  EfiInitializeLock (&(FwhInstance->FvbDevLock), TPL_HIGH_LEVEL);
  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) {
    FvbDevice->DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) AllocateCopyPool (sizeof (FV_MEMMAP_DEVICE_PATH), &mFvMemmapDevicePathTemplate);
    ((FV_MEMMAP_DEVICE_PATH *) FvbDevice->DevicePath)->MemMapDevPath.StartingAddress = BaseAddress ;
    ((FV_MEMMAP_DEVICE_PATH *) FvbDevice->DevicePath)->MemMapDevPath.EndingAddress   = BaseAddress + FwVolHeader->FvLength - 1;
  } else {
    FvbDevice->DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) AllocateCopyPool (sizeof (FV_PIWG_DEVICE_PATH), &mFvPIWGDevicePathTemplate);
    CopyGuid (
      &((FV_PIWG_DEVICE_PATH *)FvbDevice->DevicePath)->FvDevPath.FvName, 
      (GUID *)(UINTN)(BaseAddress + FwVolHeader->ExtHeaderOffset)
      );
  }


   Status = gBS->LocateDevicePath (&gEfiFirmwareVolumeBlockProtocolGuid, &FvbDevice->DevicePath, &FwbHandle);
  DbgPrint(DEBUG_INFO,"Locate Protocol:0x%g (%r)\n",&gEfiFirmwareVolumeBlockProtocolGuid,Status);
    if (EFI_ERROR (Status)) {
      FwbHandle = NULL;
      Status = gBS->InstallMultipleProtocolInterfaces (
                      &FwbHandle,
                      &gEfiFirmwareVolumeBlockProtocolGuid,
                      &FvbDevice->FwVolBlockInstance,
                      &gEfiDevicePathProtocolGuid,
                      FvbDevice->DevicePath,
                      NULL
                   );
      ASSERT_EFI_ERROR (Status);
    } else if (IsDevicePathEnd (FvbDevice->DevicePath)) {

      Status = gBS->HandleProtocol (
                      FwbHandle,
                      &gEfiFirmwareVolumeBlockProtocolGuid,
                      (VOID**)&OldFwbInterface
                      );
      ASSERT_EFI_ERROR (Status);
    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);
    }

    FwhInstance = (EFI_FW_VOL_INSTANCE *)(
        (UINTN) ((UINT8 *) FwhInstance) + FwVolHeader->HeaderLength +
          (sizeof (EFI_FW_VOL_INSTANCE) - sizeof (EFI_FIRMWARE_VOLUME_HEADER))
      );

  Status = EFI_SUCCESS;
  DbgPrint(DEBUG_INFO,"Return FvbInitialize() (%r)\n",Status);
  return Status;
}

上面的代码就是这个驱动的入口函数,根据代码可知,首先要locate到所需要的gFlashDeviceOperationProtocol,这个protocol就是这个驱动所依赖的,如果这个驱动没有安装,就直接推出了。我们的代码在运行过程中,gFlashDeviceOperationProtocol要比这个驱动先执行,因此等这个函数执行的时候,是可以locate到的。然后获取到Firmware的地址,这个地址就是我们上电后执行的地址,然后检查这个地址存放的内容是不是在编译阶段设置好的数据,这哥工作是在函数ValidateFvHeader()中做的,看一下详细的代码就会清楚了,下面是函数ValidateFvHeader的代码

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

--*/
{
  UINTN          Checksum;
  UINT16          HeaderLength;
  UINT16          *Ptr;

  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
  //
  HeaderLength = (UINT16)((FwVolHeader->HeaderLength)/2);
  Ptr = (UINT16*) FwVolHeader;
  Checksum = 0;
  while ( HeaderLength > 0) {
    Checksum = Checksum + (*Ptr);
    HeaderLength--;
    Ptr++;
  }
  if (Checksum != GetFvbHeaderCheckSum(&mLoongsonPlatformFvbInfo.FvbInfo)) {
    return EFI_NOT_FOUND;
  }

  return EFI_SUCCESS;
}

首先检查头中的一些属性,如果属性都不对那说明肯定不是有效的,然后检查校验和数据也就是函数GetFvbHeaderCheckSum的工作,这个函数的入参是全局变量mLoongsonPlatformFvbInfo.FvbInfo的信息,这个信息和编译的时候设置的数据是一致的,否则就不能保证我们使用的Fv是有效的。只要这个Fv是有效的才会继续向下执行代码。下面开始申请内存,来存放全局变量mFvbModuleGlobal中的数据,他的类型如下:

typedef struct {

  UINT32              NumFv;

  EFI_FW_VOL_INSTANCE *FvInstance[2];

  UINT8               *FvbScratchSpace[2];

} ESAL_FWB_GLOBAL;

申请了这样类型的结构的全局变量之后,就为这个变量中的内容开始赋值,其中的FvInstance就是后面要使用的FV。然后将这个protocol安装上,为后面variable使用,其实整个uefi的variable都是使用了这个protocol的。这是中间一层,最后一层就是flashoperationprotocol。这里面注册的protocol的函数,其中的读写函数都是通过下面这个全局变量来初始化的,将这个变量的拷贝到对应的FvProtocol实体的设备节点上。

EFI_FW_VOL_BLOCK_DEVICE mFvbDeviceTemplate = {

  FVB_DEVICE_SIGNATURE,

  NULL,

  0,

  { 

    FvbProtocolGetAttributes,

    FvbProtocolSetAttributes,

    FvbProtocolGetPhysicalAddress,

    FvbProtocolGetBlockSize,

    FvbProtocolRead,

    FvbProtocolWrite,

    FvbProtocolEraseBlocks, 

    NULL 

  } 

};

EFI_FW_VOL_BLOCK_DEVICE它的结构如下:

typedef struct {

  UINTN                               Signature;

  EFI_DEVICE_PATH_PROTOCOL            *DevicePath;

  UINTN                               Instance;

  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  FwVolBlockInstance;

} EFI_FW_VOL_BLOCK_DEVICE;

这个结构还是很重要的,里面包含了整个Fv结构需要的全部信息,然后这样这个Fv所提供的结构函数都注册到了对应的protocol中,供后面的代码使用。现在想象uefi的精髓就是将函数以这种回调函数的方式来实现。这种思想我们可以借鉴,自己写代码的时候可以采用这种思想,只是在注册对应的借口函数的时候,在哪里保存这个需要自己去实现。


版权声明:本文为Lq19880521原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。