这个驱动是和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的精髓就是将函数以这种回调函数的方式来实现。这种思想我们可以借鉴,自己写代码的时候可以采用这种思想,只是在注册对应的借口函数的时候,在哪里保存这个需要自己去实现。