目录
一、UEFI Driver 是什么?
二、UEFI Driver 加载流程
1.EFI_DRIVER_BINDING_PROTOCOL
2. PCI扫描过程中是如何确定PCI设备是支持option rom的?
3. Option Rom驱动后面是如何被处理的?
3. Option Rom驱动是如何被load以及执行的
总结
参考:BIOS知识枝桠——UEFI Driver_这里是为我所统率的战场-CSDN博客
UEFI中的Option ROM 驱动详解_太初有道-CSDN博客_optionrom
这个协议是由遵循UEFI驱动程序模型的每个驱动程序产生的,它是允许管理驱动程序和控制器的中心组件。它提供了一个服务来测试一个驱动程序是否支持一个特定的控制器,一个服务来启动管理一个控制器,一个服务来停止管理一个控制器。这些服务同样适用于总线控制器和设备控制器的驱动程序。(具体内容去UEFI Spec 查看)
GUID
#define EFI_DRIVER_BINDING_PROTOCOL_GUID \
{0x18A031AB,0xB443,0x4D1A,\
{0xA5,0xC0,0x0C,0x09,0x26,0x1E,0x9F,0x71}}
Protocol Interface Structure
typedef struct _EFI_DRIVER_BINDING_PROTOCOL {
EFI_DRIVER_BINDING_PROTOCOL_SUPPORTED Supported;
EFI_DRIVER_BINDING_PROTOCOL_START Start;
EFI_DRIVER_BINDING_PROTOCOL_STOP Stop;
UINT32 Version;
EFI_HANDLE ImageHandle;
EFI_HANDLE DriverBindingHandle;
} EFI_DRIVER_BINDING_PROTOCOL;
PciBusDriverBindingStart->PciEnumerator->PciHostBridgeEnumerator->PciRootBridgeEnumerator->PciScanBus->PciSearchDevice->GetOpRomInfo
/**
Get Pci device's oprom information.
@param PciIoDevice Input Pci device instance.
Output Pci device instance with updated OptionRom size.
@retval EFI_NOT_FOUND Pci device has not Option Rom.
@retval EFI_SUCCESS Pci device has Option Rom.
**/
EFI_STATUS
GetOpRomInfo (
IN OUT PCI_IO_DEVICE *PciIoDevice
)
{
UINT8 RomBarIndex;
UINT32 AllOnes;
UINT64 Address;
EFI_STATUS Status;
UINT8 Bus;
UINT8 Device;
UINT8 Function;
EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *PciRootBridgeIo;
Bus = PciIoDevice->BusNumber;
Device = PciIoDevice->DeviceNumber;
Function = PciIoDevice->FunctionNumber;
PciRootBridgeIo = PciIoDevice->PciRootBridgeIo;
//
// Offset is 0x30 if is not ppb
//
//
// 0x30
//
RomBarIndex = PCI_EXPANSION_ROM_BASE;
if (IS_PCI_BRIDGE (&PciIoDevice->Pci)) {
//
// If is ppb, 0x38
//
RomBarIndex = PCI_BRIDGE_ROMBAR;
}
//
// The bit0 is 0 to prevent the enabling of the Rom address decoder
//
AllOnes = 0xfffffffe;
Address = EFI_PCI_ADDRESS (Bus, Device, Function, RomBarIndex);
Status = PciRootBridgeIo->Pci.Write (
PciRootBridgeIo,
EfiPciWidthUint32,
Address,
1,
&AllOnes
);
if (EFI_ERROR (Status)) {
return EFI_NOT_FOUND;
}
//
// Read back
//
Status = PciRootBridgeIo->Pci.Read(
PciRootBridgeIo,
EfiPciWidthUint32,
Address,
1,
&AllOnes
);
if (EFI_ERROR (Status)) {
return EFI_NOT_FOUND;
}
//
// Bits [1, 10] are reserved
//
AllOnes &= 0xFFFFF800;
if ((AllOnes == 0) || (AllOnes == 0xFFFFF800)) {
return EFI_NOT_FOUND;
}
PciIoDevice->RomSize = (UINT64) ((~AllOnes) + 1);
return EFI_SUCCESS;
}
在GetOpRomInfo中这里只是探测了这个设备是否有Rom,如果有读取RomSize的大小到PciIoDevice中。这时探测到了这个pci设备包含的rom大小。
然后在函数PciHostBridgeResourceAllocator总调用ProcessOptionRom对optionrom进行处理,函数调用关系如下:
PciBusDriverBindingStar->PciEnumerator->PciHostBridgeResourceAllocator
->ProcessOptionRom
/**
This routine is used to process all PCI devices' Option Rom
on a certain root bridge.
@param Bridge Given parent's root bridge.
@param RomBase Base address of ROM driver loaded from.
@param MaxLength Maximum rom size.
**/
VOID
ProcessOptionRom (
IN PCI_IO_DEVICE *Bridge,
IN UINT64 RomBase,
IN UINT64 MaxLength
)
{
LIST_ENTRY *CurrentLink;
PCI_IO_DEVICE *Temp;
//
// Go through bridges to reach all devices
//
CurrentLink = Bridge->ChildList.ForwardLink;
while (CurrentLink != NULL && CurrentLink != &Bridge->ChildList) {
Temp = PCI_IO_DEVICE_FROM_LINK (CurrentLink);
if (!IsListEmpty (&Temp->ChildList)) {
//
// Go further to process the option rom under this bridge
//
ProcessOptionRom (Temp, RomBase, MaxLength);
}
if (Temp->RomSize != 0 && Temp->RomSize <= MaxLength) {
//
// Load and process the option rom
//
LoadOpRomImage (Temp, RomBase);
}
CurrentLink = CurrentLink->ForwardLink;
}
}
由上面的代码可知,入参传进来了这个pci设备的结构指针和RomBase,以及Rom的大小,这里注意ROMBase这里传进来的是PCI MEM空间的起始地址。是从上面分配空间过程中得到。根据代码的逻辑,如果这个设备是一个桥设备,那么继续递归其下游的子设备,然后在调用ProcessOptionRom处理桥下面设备的optionrom,如果不是一个桥那么就执行函数LoadOpRomImage,将rom中的数据读取到内存中。
下面是LoadOpRomImage的代码实现:
/**
Load Option Rom image for specified PCI device.
@param PciDevice Pci device instance.
@param RomBase Base address of Option Rom.
@retval EFI_OUT_OF_RESOURCES No enough memory to hold image.
@retval EFI_SUCESS Successfully loaded Option Rom.
**/
EFI_STATUS
LoadOpRomImage (
IN PCI_IO_DEVICE *PciDevice,
IN UINT64 RomBase
)
{
UINT8 RomBarIndex;
UINT8 Indicator;
UINT16 OffsetPcir;
UINT32 RomBarOffset;
UINT32 RomBar;
EFI_STATUS RetStatus;
BOOLEAN FirstCheck;
UINT8 *Image;
PCI_EXPANSION_ROM_HEADER *RomHeader;
PCI_DATA_STRUCTURE *RomPcir;
UINT64 RomSize;
UINT64 RomImageSize;
UINT32 LegacyImageLength;
UINT8 *RomInMemory;
UINT8 CodeType;
BOOLEAN HasEfiOpRom;
RomSize = PciDevice->RomSize;
Indicator = 0;
RomImageSize = 0;
RomInMemory = NULL;
CodeType = 0xFF;
//
// Get the RomBarIndex
//
//
// 0x30
//
RomBarIndex = PCI_EXPANSION_ROM_BASE;
if (IS_PCI_BRIDGE (&(PciDevice->Pci))) {
//
// if is ppb
//
//
// 0x38
//
RomBarIndex = PCI_BRIDGE_ROMBAR;
}
//
// Allocate memory for Rom header and PCIR
//
RomHeader = AllocatePool (sizeof (PCI_EXPANSION_ROM_HEADER));
if (RomHeader == NULL) {
return EFI_OUT_OF_RESOURCES;
}
RomPcir = AllocatePool (sizeof (PCI_DATA_STRUCTURE));
if (RomPcir == NULL) {
FreePool (RomHeader);
return EFI_OUT_OF_RESOURCES;
}
RomBar = (UINT32) RomBase;
//
// Enable RomBar
//
RomDecode (PciDevice, RomBarIndex, RomBar, TRUE);
RomBarOffset = RomBar;
RetStatus = EFI_NOT_FOUND;
FirstCheck = TRUE;
LegacyImageLength = 0;
HasEfiOpRom = FALSE;
do {
PciDevice->PciRootBridgeIo->Mem.Read (
PciDevice->PciRootBridgeIo,
EfiPciWidthUint8,
RomBarOffset,
sizeof (PCI_EXPANSION_ROM_HEADER),
(UINT8 *) RomHeader
);
if (RomHeader->Signature != PCI_EXPANSION_ROM_HEADER_SIGNATURE) {
RomBarOffset = RomBarOffset + 512;
if (FirstCheck) {
break;
} else {
RomImageSize = RomImageSize + 512;
continue;
}
}
FirstCheck = FALSE;
OffsetPcir = RomHeader->PcirOffset;
//
// If the pointer to the PCI Data Structure is invalid, no further images can be located.
// The PCI Data Structure must be DWORD aligned.
//
if (OffsetPcir == 0 ||
(OffsetPcir & 3) != 0 ||
RomImageSize + OffsetPcir + sizeof (PCI_DATA_STRUCTURE) > RomSize) {
break;
}
PciDevice->PciRootBridgeIo->Mem.Read (
PciDevice->PciRootBridgeIo,
EfiPciWidthUint8,
RomBarOffset + OffsetPcir,
sizeof (PCI_DATA_STRUCTURE),
(UINT8 *) RomPcir
);
//
// If a valid signature is not present in the PCI Data Structure, no further images can be located.
//
if (RomPcir->Signature != PCI_DATA_STRUCTURE_SIGNATURE) {
break;
}
if (RomImageSize + RomPcir->ImageLength * 512 > RomSize) {
break;
}
if (RomPcir->CodeType == PCI_CODE_TYPE_PCAT_IMAGE) {
CodeType = PCI_CODE_TYPE_PCAT_IMAGE;
LegacyImageLength = ((UINT32)((EFI_LEGACY_EXPANSION_ROM_HEADER *)RomHeader)->Size512) * 512;
} else if (RomPcir->CodeType == PCI_CODE_TYPE_EFI_IMAGE) {
HasEfiOpRom = TRUE;
}
Indicator = RomPcir->Indicator;
RomImageSize = RomImageSize + RomPcir->ImageLength * 512;
RomBarOffset = RomBarOffset + RomPcir->ImageLength * 512;
} while (((Indicator & 0x80) == 0x00) && ((RomBarOffset - RomBar) < RomSize));
//
// Some Legacy Cards do not report the correct ImageLength so used the maximum
// of the legacy length and the PCIR Image Length
//
if (CodeType == PCI_CODE_TYPE_PCAT_IMAGE) {
RomImageSize = MAX (RomImageSize, LegacyImageLength);
}
if (RomImageSize > 0) {
RetStatus = EFI_SUCCESS;
Image = AllocatePool ((UINT32) RomImageSize);
if (Image == NULL) {
RomDecode (PciDevice, RomBarIndex, RomBar, FALSE);
FreePool (RomHeader);
FreePool (RomPcir);
return EFI_OUT_OF_RESOURCES;
}
//
// Copy Rom image into memory
//
PciDevice->PciRootBridgeIo->Mem.Read (
PciDevice->PciRootBridgeIo,
EfiPciWidthUint8,
RomBar,
(UINT32) RomImageSize,
Image
);
RomInMemory = Image;
}
RomDecode (PciDevice, RomBarIndex, RomBar, FALSE);
PciDevice->HasEfiOpRom = HasEfiOpRom;
PciDevice->EmbeddedRom = TRUE;
PciDevice->PciIo.RomSize = RomImageSize;
PciDevice->PciIo.RomImage = RomInMemory;
//
// For OpROM read from PCI device:
// Add the Rom Image to internal database for later PCI light enumeration
//
PciRomAddImageMapping (
NULL,
PciDevice->PciRootBridgeIo->SegmentNumber,
PciDevice->BusNumber,
PciDevice->DeviceNumber,
PciDevice->FunctionNumber,
(UINT64) (UINTN) PciDevice->PciIo.RomImage,
PciDevice->PciIo.RomSize
);
//
// Free allocated memory
//
FreePool (RomHeader);
FreePool (RomPcir);
return RetStatus;
}
PciRomAddImageMapping :作用是将这个设备的信息Seg号,Bus号, Dev号,Fun号,ImageHandle,RomImage(load到内存之后的地址),RomSize就是rom的大小都保存在了全局变量mRomImageTable内,这是一个全局的指针,每添加一个变量指针指针位置向后移动.这样在扫描过程中将所有的支持oprom 的pci设备都保存在了mRomImageTable内了.
根据以上代码可知,首先读取PCI_EXPANSION_ROM_HEADER的数据,PCI_EXPANSION_ROM_HEADER结构如下:
typedef struct {
UINT16 Signature; // 0xaa55
UINT8 Reserved[0x16];
UINT16 PcirOffset;
} PCI_EXPANSION_ROM_HEADER;
读取这个结构数据主要是为了得到结构中的PcirOffset的值,通过这个值的偏移,可以得到PCI的数据PCI_DATA_STRUCTURE的内容,(结构定义如下:
typedef struct {
UINT32 Signature; // "PCIR"
UINT16 VendorId;
UINT16 DeviceId;
UINT16 Reserved0;
UINT16 Length;
UINT8 Revision;
UINT8 ClassCode[3];
UINT16 ImageLength;
UINT16 CodeRevision;
UINT8 CodeType;
UINT8 Indicator;
UINT16 Reserved1;
} PCI_DATA_STRUCTURE;
EFI_PCI_EXPANSION_ROM_HEADER
typedef struct {
UINT16 Signature; // 0xaa55
UINT16 InitializationSize;
UINT32 EfiSignature; // 0x0EF1
UINT16 EfiSubsystem;
UINT16 EfiMachineType;
UINT16 CompressionType;
UINT8 Reserved[8];
UINT16 EfiImageHeaderOffset;
UINT16 PcirOffset;
} EFI_PCI_EXPANSION_ROM_HEADER;
这里校验没有失败,那么就会调用函数PciRomAddImageMapping添加到数据库中.待后面处理.这里函数PciRomAddImageMapping代码如下:
VOID
PciRomAddImageMapping (
IN EFI_HANDLE ImageHandle,
IN UINTN Seg,
IN UINT8 Bus,
IN UINT8 Dev,
IN UINT8 Func,
IN VOID *RomImage,
IN UINT64 RomSize
)
{
UINTN Index;
PCI_ROM_IMAGE *NewTable;
for (Index = 0; Index < mNumberOfPciRomImages; Index++) {
if (mRomImageTable[Index].Seg == Seg &&
mRomImageTable[Index].Bus == Bus &&
mRomImageTable[Index].Dev == Dev &&
mRomImageTable[Index].Func == Func) {
//
// Expect once RomImage and RomSize are recorded, they will be passed in
// later when updating ImageHandle
//
ASSERT ((mRomImageTable[Index].RomImage == NULL) || (RomImage == mRomImageTable[Index].RomImage));
ASSERT ((mRomImageTable[Index].RomSize == 0 ) || (RomSize == mRomImageTable[Index].RomSize ));
break;
}
}
if (Index == mNumberOfPciRomImages) {
//
// Rom Image Table buffer needs to grow.
//
if (mNumberOfPciRomImages == mMaxNumberOfPciRomImages) {
NewTable = ReallocatePool (
mMaxNumberOfPciRomImages * sizeof (PCI_ROM_IMAGE),
(mMaxNumberOfPciRomImages + 0x20) * sizeof (PCI_ROM_IMAGE),
mRomImageTable
);
if (NewTable == NULL) {
return ;
}
mRomImageTable = NewTable;
mMaxNumberOfPciRomImages += 0x20;
}
//
// Record the new PCI device
//
mRomImageTable[Index].Seg = Seg;
mRomImageTable[Index].Bus = Bus;
mRomImageTable[Index].Dev = Dev;
mRomImageTable[Index].Func = Func;
mNumberOfPciRomImages++;
}
mRomImageTable[Index].ImageHandle = ImageHandle;
mRomImageTable[Index].RomImage = RomImage;
mRomImageTable[Index].RomSize = RomSize;
}
PciBusDriverBindingStart->PciEnumerator->PciEnumeratorLight
/**
Process the option ROM for all the children of the specified parent PCI device.
It can only be used after the first full Option ROM process.
@param PciIoDevice Pci device instance.
**/
VOID
ProcessOptionRomLight (
IN PCI_IO_DEVICE *PciIoDevice
)
{
PCI_IO_DEVICE *Temp;
LIST_ENTRY *CurrentLink;
//
// For RootBridge, PPB , P2C, go recursively to traverse all its children
//
CurrentLink = PciIoDevice->ChildList.ForwardLink;
while (CurrentLink != NULL && CurrentLink != &PciIoDevice->ChildList) {
Temp = PCI_IO_DEVICE_FROM_LINK (CurrentLink);
if (!IsListEmpty (&Temp->ChildList)) {
ProcessOptionRomLight (Temp);
}
PciRomGetImageMapping (Temp);
//
// The OpRom has already been processed in the first round
//
Temp->AllOpRomProcessed = TRUE;
CurrentLink = CurrentLink->ForwardLink;
}
}
根据上面的代码可知,枚举每一个pcie设备,如果是桥设备继续调用函数ProcessOptionRomLight自身,这里是递归函数.如果不是桥设备,那么就调用函数 PciRomGetImageMapping (Temp);这个函数的代码如下:
/**
Get Option rom driver's mapping for PCI device.
@param PciIoDevice Device instance.
@retval TRUE Found Image mapping.
@retval FALSE Cannot found image mapping.
**/
BOOLEAN
PciRomGetImageMapping (
IN PCI_IO_DEVICE *PciIoDevice
)
{
EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *PciRootBridgeIo;
UINTN Index;
BOOLEAN Found;
PciRootBridgeIo = PciIoDevice->PciRootBridgeIo;
Found = FALSE;
for (Index = 0; Index < mNumberOfPciRomImages; Index++) {
if (mRomImageTable[Index].Seg == PciRootBridgeIo->SegmentNumber &&
mRomImageTable[Index].Bus == PciIoDevice->BusNumber &&
mRomImageTable[Index].Dev == PciIoDevice->DeviceNumber &&
mRomImageTable[Index].Func == PciIoDevice->FunctionNumber ) {
Found = TRUE;
if (mRomImageTable[Index].ImageHandle != NULL) {
AddDriver (PciIoDevice, mRomImageTable[Index].ImageHandle);
} else {
PciIoDevice->PciIo.RomImage = (VOID *) (UINTN) mRomImageTable[Index].RomAddress;
PciIoDevice->PciIo.RomSize = (UINTN) mRomImageTable[Index].RomLength;
}
}
}
return Found;
}
根据上面的代码可知,如果传进来的这个设备的信息和之前枚举设备时添加到mRomImageTable中的设备信息匹配上之后,就会调用AddDriver,用这设备的ImageHandle,和PciIoDevice信息创建一个驱动,并添加到pci设备的OptionRomDriverList中,AddDriver
的代码如下:
/**
Add an overriding driver image.
@param PciIoDevice Instance of PciIo device.
@param DriverImageHandle new added driver image.
@retval EFI_SUCCESS Successfully added driver.
@retval EFI_OUT_OF_RESOURCES No memory resource for new driver instance.
@retval other Some error occurred when locating gEfiLoadedImageProtocolGuid.
**/
EFI_STATUS
AddDriver (
IN PCI_IO_DEVICE *PciIoDevice,
IN EFI_HANDLE DriverImageHandle
)
{
EFI_STATUS Status;
EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
PE_COFF_LOADER_IMAGE_CONTEXT ImageContext;
PCI_DRIVER_OVERRIDE_LIST *Node;
Status = gBS->HandleProtocol (DriverImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **) &LoadedImage);
if (EFI_ERROR (Status)) {
return Status;
}
Node = AllocatePool (sizeof (PCI_DRIVER_OVERRIDE_LIST));
if (Node == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Node->Signature = DRIVER_OVERRIDE_SIGNATURE;
Node->DriverImageHandle = DriverImageHandle;
InsertTailList (&PciIoDevice->OptionRomDriverList, &(Node->Link));
PciIoDevice->BusOverride = TRUE;
ImageContext.Handle = LoadedImage->ImageBase;
ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory;
//
// Get information about the image
//
PeCoffLoaderGetImageInfo (&ImageContext);
return EFI_SUCCESS;
}
AddDriver的主要作用就是创建这个设备驱动的devicepatch路径,并将驱动,这个驱动的节点添加到OptionRomDriverList链表中.这里驱动节点的类型是PCI_DRIVER_OVERRIDE_LIST,并更新PciIoDevice->BusOverride = TRUE;后面会使用这个标志.
这样就将一个option rom驱动添加到了驱动列表列面.
PciBusDriverBindingStart->StartPciDevices->StartPciDevicesOnBridge->RegisterPciDevice->ContainEfiImage
下面主要介绍一下函数ContainEfiImage,这个函数是判读这个option rom是否含有uefi image的函数,代码如下:
/**
Check if the RomImage contains EFI Images.
@param RomImage The ROM address of Image for check.
@param RomSize Size of ROM for check.
@retval TRUE ROM contain EFI Image.
@retval FALSE ROM not contain EFI Image.
**/
BOOLEAN
ContainEfiImage (
IN VOID *RomImage,
IN UINT64 RomSize
)
{
PCI_EXPANSION_ROM_HEADER *RomHeader;
PCI_DATA_STRUCTURE *RomPcir;
BOOLEAN FirstCheck;
FirstCheck = TRUE;
RomHeader = RomImage;
while ((UINT8 *) RomHeader < (UINT8 *) RomImage + RomSize) {
if (RomHeader->Signature != PCI_EXPANSION_ROM_HEADER_SIGNATURE) {
if (FirstCheck) {
return FALSE;
} else {
RomHeader = (PCI_EXPANSION_ROM_HEADER *) ((UINT8 *) RomHeader + 512);
continue;
}
}
FirstCheck = FALSE;
RomPcir = (PCI_DATA_STRUCTURE *) ((UINT8 *) RomHeader + RomHeader->PcirOffset);
if (RomPcir->CodeType == PCI_CODE_TYPE_EFI_IMAGE) {
return TRUE;
}
RomHeader = (PCI_EXPANSION_ROM_HEADER *) ((UINT8 *) RomHeader + RomPcir->Length * 512);
}
return FALSE;
}
由上面的代码可知,如果函数返回TRUE 就是表示这个OPtion Rom里面包含了一个正规的Uefi Image,是可以被load 和执行的.这个函数返回后,如果返回了TRUE 那么就会在函数RegisterPciDevice内安装&gEfiLoadFile2ProtocolGuid这个protocol,等待后面真正去load image的时候需要这里注册的接口&PciIoDevice->LoadFile2.代码如下:
if (HasEfiImage) {
Status = gBS->InstallMultipleProtocolInterfaces (
&PciIoDevice->Handle,
&gEfiLoadFile2ProtocolGuid,
&PciIoDevice->LoadFile2,
NULL
);
if (EFI_ERROR (Status)) {
gBS->UninstallMultipleProtocolInterfaces (
&PciIoDevice->Handle,
&gEfiDevicePathProtocolGuid,
PciIoDevice->DevicePath,
&gEfiPciIoProtocolGuid,
&PciIoDevice->PciIo,
NULL
);
return Status;
}
}
这里PciIoDevice->LoadFile2函数就是下面的代码:
/**
Causes the driver to load a specified file.
@param This Indicates a pointer to the calling context.
@param FilePath The device specific path of the file to load.
@param BootPolicy Should always be FALSE.
@param BufferSize On input the size of Buffer in bytes. On output with a return
code of EFI_SUCCESS, the amount of data transferred to Buffer.
On output with a return code of EFI_BUFFER_TOO_SMALL,
the size of Buffer required to retrieve the requested file.
@param Buffer The memory buffer to transfer the file to. If Buffer is NULL,
then no the size of the requested file is returned in BufferSize.
@retval EFI_SUCCESS The file was loaded.
@retval EFI_UNSUPPORTED BootPolicy is TRUE.
@retval EFI_INVALID_PARAMETER FilePath is not a valid device path, or
BufferSize is NULL.
@retval EFI_NOT_FOUND Not found PCI Option Rom on PCI device.
@retval EFI_DEVICE_ERROR Failed to decompress PCI Option Rom image.
@retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory entry.
BufferSize has been updated with the size needed to complete the request.
**/
EFI_STATUS
EFIAPI
LoadFile2 (
IN EFI_LOAD_FILE2_PROTOCOL *This,
IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
IN BOOLEAN BootPolicy,
IN OUT UINTN *BufferSize,
IN VOID *Buffer OPTIONAL
)
{
PCI_IO_DEVICE *PciIoDevice;
if (BootPolicy) {
return EFI_UNSUPPORTED;
}
PciIoDevice = PCI_IO_DEVICE_FROM_LOAD_FILE2_THIS (This);
return LocalLoadFile2 (
PciIoDevice,
FilePath,
BufferSize,
Buffer
);
}
LocalLoadFile2的代码如下:
/**
Load the EFI Image from Option ROM
@param PciIoDevice PCI IO device instance.
@param FilePath The file path of the EFI Image
@param BufferSize On input the size of Buffer in bytes. On output with a return
code of EFI_SUCCESS, the amount of data transferred to Buffer.
On output with a return code of EFI_BUFFER_TOO_SMALL,
the size of Buffer required to retrieve the requested file.
@param Buffer The memory buffer to transfer the file to. If Buffer is NULL,
then no the size of the requested file is returned in BufferSize.
@retval EFI_SUCCESS The file was loaded.
@retval EFI_INVALID_PARAMETER FilePath is not a valid device path, or
BufferSize is NULL.
@retval EFI_NOT_FOUND Not found PCI Option Rom on PCI device.
@retval EFI_DEVICE_ERROR Failed to decompress PCI Option Rom image.
@retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory entry.
BufferSize has been updated with the size needed to complete the request.
**/
EFI_STATUS
LocalLoadFile2 (
IN PCI_IO_DEVICE *PciIoDevice,
IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
IN OUT UINTN *BufferSize,
IN VOID *Buffer OPTIONAL
)
{
EFI_STATUS Status;
MEDIA_RELATIVE_OFFSET_RANGE_DEVICE_PATH *EfiOpRomImageNode;
EFI_PCI_EXPANSION_ROM_HEADER *EfiRomHeader;
PCI_DATA_STRUCTURE *Pcir;
UINT32 ImageSize;
UINT8 *ImageBuffer;
UINT32 ImageLength;
UINT32 DestinationSize;
UINT32 ScratchSize;
VOID *Scratch;
EFI_DECOMPRESS_PROTOCOL *Decompress;
EfiOpRomImageNode = (MEDIA_RELATIVE_OFFSET_RANGE_DEVICE_PATH *) FilePath;
if ((EfiOpRomImageNode == NULL) ||
(DevicePathType (FilePath) != MEDIA_DEVICE_PATH) ||
(DevicePathSubType (FilePath) != MEDIA_RELATIVE_OFFSET_RANGE_DP) ||
(DevicePathNodeLength (FilePath) != sizeof (MEDIA_RELATIVE_OFFSET_RANGE_DEVICE_PATH)) ||
(!IsDevicePathEnd (NextDevicePathNode (FilePath))) ||
(EfiOpRomImageNode->StartingOffset > EfiOpRomImageNode->EndingOffset) ||
(EfiOpRomImageNode->EndingOffset >= PciIoDevice->RomSize) ||
(BufferSize == NULL)
) {
return EFI_INVALID_PARAMETER;
}
EfiRomHeader = (EFI_PCI_EXPANSION_ROM_HEADER *) (
(UINT8 *) PciIoDevice->PciIo.RomImage + EfiOpRomImageNode->StartingOffset
);
if (EfiRomHeader->Signature != PCI_EXPANSION_ROM_HEADER_SIGNATURE) {
return EFI_NOT_FOUND;
}
Pcir = (PCI_DATA_STRUCTURE *) ((UINT8 *) EfiRomHeader + EfiRomHeader->PcirOffset);
if ((Pcir->CodeType == PCI_CODE_TYPE_EFI_IMAGE) &&
(EfiRomHeader->EfiSignature == EFI_PCI_EXPANSION_ROM_HEADER_EFISIGNATURE) &&
((EfiRomHeader->EfiSubsystem == EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER) ||
(EfiRomHeader->EfiSubsystem == EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER)) &&
(EfiRomHeader->CompressionType <= EFI_PCI_EXPANSION_ROM_HEADER_COMPRESSED)
) {
ImageSize = (UINT32) EfiRomHeader->InitializationSize * 512;
ImageBuffer = (UINT8 *) EfiRomHeader + EfiRomHeader->EfiImageHeaderOffset;
ImageLength = ImageSize - EfiRomHeader->EfiImageHeaderOffset;
if (EfiRomHeader->CompressionType != EFI_PCI_EXPANSION_ROM_HEADER_COMPRESSED) {
//
// Uncompressed: Copy the EFI Image directly to user's buffer
//
if (Buffer == NULL || *BufferSize < ImageLength) {
*BufferSize = ImageLength;
return EFI_BUFFER_TOO_SMALL;
}
*BufferSize = ImageLength;
CopyMem (Buffer, ImageBuffer, ImageLength);
return EFI_SUCCESS;
} else {
//
// Compressed: Uncompress before copying
//
Status = gBS->LocateProtocol (&gEfiDecompressProtocolGuid, NULL, (VOID **) &Decompress);
if (EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
}
Status = Decompress->GetInfo (
Decompress,
ImageBuffer,
ImageLength,
&DestinationSize,
&ScratchSize
);
if (EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
}
if (Buffer == NULL || *BufferSize < DestinationSize) {
*BufferSize = DestinationSize;
return EFI_BUFFER_TOO_SMALL;
}
*BufferSize = DestinationSize;
Scratch = AllocatePool (ScratchSize);
if (Scratch == NULL) {
return EFI_DEVICE_ERROR;
}
Status = Decompress->Decompress (
Decompress,
ImageBuffer,
ImageLength,
Buffer,
DestinationSize,
Scratch,
ScratchSize
);
FreePool (Scratch);
if (EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
}
return EFI_SUCCESS;
}
}
return EFI_NOT_FOUND;
}
LocalLoadFile2函数是load 的回调函数,代码细节这里不做详细的介绍了.很多Image 格式的要求都在代码里面有体现.有一点不满足那么就会load失败,也就会导致执行失败..
下面还是回到函数RegisterPciDevice,上面介绍万安装&gEfiLoadFile2ProtocolGuid之后,接下来就会调用函数ProcessOpRomImage,这个函数是真正执行load操作的,load成功之后就会去执行驱动的entrypoint函数.
下面是详细的代码实现:
/**
Load and start the Option Rom image.
@param PciDevice Pci device instance.
@retval EFI_SUCCESS Successfully loaded and started PCI Option Rom image.
@retval EFI_NOT_FOUND Failed to process PCI Option Rom image.
**/
EFI_STATUS
ProcessOpRomImage (
IN PCI_IO_DEVICE *PciDevice
)
{
UINT8 Indicator;
UINT32 ImageSize;
VOID *RomBar;
UINT8 *RomBarOffset;
EFI_HANDLE ImageHandle;
EFI_STATUS Status;
EFI_STATUS RetStatus;
BOOLEAN FirstCheck;
EFI_PCI_EXPANSION_ROM_HEADER *EfiRomHeader;
PCI_DATA_STRUCTURE *Pcir;
EFI_DEVICE_PATH_PROTOCOL *PciOptionRomImageDevicePath;
MEDIA_RELATIVE_OFFSET_RANGE_DEVICE_PATH EfiOpRomImageNode;
VOID *Buffer;
UINTN BufferSize;
EFI_LEGACY_BIOS_PROTOCOL *LegacyBios;
KERNEL_CONFIGURATION KernelConfig;
Status = GetKernelConfiguration (&KernelConfig);
if (Status == EFI_SUCCESS) {
//
// Legacy Boot mode, skip loading Native driver.
//
if (KernelConfig.BootType == LEGACY_BOOT_TYPE) {
return EFI_SUCCESS;
}
//
// In Dual boot mode, the Vga device will use the legacy ROM.
// skip the native GOP driver.
//
if (KernelConfig.BootType == DUAL_BOOT_TYPE) {
if (PciDevice->Pci.Hdr.ClassCode[2] == PCI_CLASS_DISPLAY) {
Status = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **)&LegacyBios);
if (!EFI_ERROR (Status)) {
Indicator = 0;
//
// Get the Address of the Rom image
//
RomBar = PciDevice->PciIo.RomImage;
RomBarOffset = (UINT8 *) RomBar;
RetStatus = EFI_NOT_FOUND;
FirstCheck = TRUE;
do {
EfiRomHeader = (EFI_PCI_EXPANSION_ROM_HEADER *) RomBarOffset;
if (EfiRomHeader->Signature != PCI_EXPANSION_ROM_HEADER_SIGNATURE) {
RomBarOffset = RomBarOffset + 512;
if (FirstCheck) {
break;
} else {
continue;
}
}
FirstCheck = FALSE;
Pcir = (PCI_DATA_STRUCTURE *) (RomBarOffset + EfiRomHeader->PcirOffset);
ImageSize = (UINT32) (Pcir->ImageLength * 512);
Indicator = Pcir->Indicator;
RomBarOffset = RomBarOffset + ImageSize;
if (Pcir->CodeType == PCI_CODE_TYPE_PCAT_IMAGE) {
//
// If BIOS support Legacy and the PCI device has Legacy oprom,then system will select legacy VGA rom to display.
//
return EFI_SUCCESS;
}
} while (((Indicator & 0x80) == 0x00) && ((UINTN) (RomBarOffset - (UINT8 *) RomBar) < PciDevice->RomSize));
}
}
}
}
Indicator = 0;
//
// Get the Address of the Option Rom image
//
RomBar = PciDevice->PciIo.RomImage;
RomBarOffset = (UINT8 *) RomBar;
RetStatus = EFI_NOT_FOUND;
FirstCheck = TRUE;
do {
EfiRomHeader = (EFI_PCI_EXPANSION_ROM_HEADER *) RomBarOffset;
if (EfiRomHeader->Signature != PCI_EXPANSION_ROM_HEADER_SIGNATURE) {
RomBarOffset += 512;
if (FirstCheck) {
break;
} else {
continue;
}
}
FirstCheck = FALSE;
Pcir = (PCI_DATA_STRUCTURE *) (RomBarOffset + EfiRomHeader->PcirOffset);
ImageSize = (UINT32) (Pcir->ImageLength * 512);
Indicator = Pcir->Indicator;
//
// Skip the image if it is not an EFI PCI Option ROM image
//
if (Pcir->CodeType != PCI_CODE_TYPE_EFI_IMAGE) {
goto NextImage;
}
//
// Skip the EFI PCI Option ROM image if its machine type is not supported
//
if (!EFI_IMAGE_MACHINE_TYPE_SUPPORTED (EfiRomHeader->EfiMachineType)) {
goto NextImage;
}
//
// Ignore the EFI PCI Option ROM image if it is an EFI application
//
if (EfiRomHeader->EfiSubsystem == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION) {
goto NextImage;
}
//
// Create Pci Option Rom Image device path header
//
EfiOpRomImageNode.Header.Type = MEDIA_DEVICE_PATH;
EfiOpRomImageNode.Header.SubType = MEDIA_RELATIVE_OFFSET_RANGE_DP;
SetDevicePathNodeLength (&EfiOpRomImageNode.Header, sizeof (EfiOpRomImageNode));
EfiOpRomImageNode.StartingOffset = (UINTN) RomBarOffset - (UINTN) RomBar;
EfiOpRomImageNode.EndingOffset = (UINTN) RomBarOffset + ImageSize - 1 - (UINTN) RomBar;
PciOptionRomImageDevicePath = AppendDevicePathNode (PciDevice->DevicePath, &EfiOpRomImageNode.Header);
ASSERT (PciOptionRomImageDevicePath != NULL);
//
// load image and start image
//
BufferSize = 0;
Buffer = NULL;
ImageHandle = NULL;
Status = gBS->LoadImage (
FALSE,
gPciBusDriverBinding.DriverBindingHandle,
PciOptionRomImageDevicePath,
Buffer,
BufferSize,
&ImageHandle
);
FreePool (PciOptionRomImageDevicePath);
if (!EFI_ERROR (Status)) {
Status = gBS->StartImage (ImageHandle, NULL, NULL);
if (!EFI_ERROR (Status)) {
AddDriver (PciDevice, ImageHandle);
PciRomAddImageMapping (
ImageHandle,
PciDevice->PciRootBridgeIo->SegmentNumber,
PciDevice->BusNumber,
PciDevice->DeviceNumber,
PciDevice->FunctionNumber,
(UINT64) (UINTN) PciDevice->PciIo.RomImage,
PciDevice->PciIo.RomSize
);
RetStatus = EFI_SUCCESS;
}
}
NextImage:
RomBarOffset += ImageSize;
} while (((Indicator & 0x80) == 0x00) && ((UINTN) (RomBarOffset - (UINT8 *) RomBar) < PciDevice->RomSize));
return RetStatus;
}
由上面的代码可知,在load成功之后就会去执行start.注意这里面load函数内部就会走上面注册的函数.执行完start函数,也就是运行了驱动的entrypoint之后,会执行AddDriver.
在后面Dxe结尾的时候,在函数connectcontroller函数中,会调用GetDriver函数,GetDriver函数代码如下:
/**
Uses a bus specific algorithm to retrieve a driver image handle for a controller.
@param This A pointer to the EFI_BUS_SPECIFIC_DRIVER_OVERRIDE_PROTOCOL instance.
@param DriverImageHandle On input, a pointer to the previous driver image handle returned
by GetDriver(). On output, a pointer to the next driver
image handle. Passing in a NULL, will return the first driver
image handle.
@retval EFI_SUCCESS A bus specific override driver is returned in DriverImageHandle.
@retval EFI_NOT_FOUND The end of the list of override drivers was reached.
A bus specific override driver is not returned in DriverImageHandle.
@retval EFI_INVALID_PARAMETER DriverImageHandle is not a handle that was returned on a
previous call to GetDriver().
**/
EFI_STATUS
EFIAPI
GetDriver (
IN EFI_BUS_SPECIFIC_DRIVER_OVERRIDE_PROTOCOL *This,
IN OUT EFI_HANDLE *DriverImageHandle
)
{
PCI_IO_DEVICE *PciIoDevice;
LIST_ENTRY *CurrentLink;
PCI_DRIVER_OVERRIDE_LIST *Node;
PciIoDevice = PCI_IO_DEVICE_FROM_PCI_DRIVER_OVERRIDE_THIS (This);
CurrentLink = PciIoDevice->OptionRomDriverList.ForwardLink;
while (CurrentLink != NULL && CurrentLink != &PciIoDevice->OptionRomDriverList) {
Node = DRIVER_OVERRIDE_FROM_LINK (CurrentLink);
if (*DriverImageHandle == NULL) {
*DriverImageHandle = Node->DriverImageHandle;
return EFI_SUCCESS;
}
if (*DriverImageHandle == Node->DriverImageHandle) {
if (CurrentLink->ForwardLink == &PciIoDevice->OptionRomDriverList ||
CurrentLink->ForwardLink == NULL) {
return EFI_NOT_FOUND;
}
//
// Get next node
//
Node = DRIVER_OVERRIDE_FROM_LINK (CurrentLink->ForwardLink);
*DriverImageHandle = Node->DriverImageHandle;
return EFI_SUCCESS;
}
CurrentLink = CurrentLink->ForwardLink;
}
return EFI_INVALID_PARAMETER;
}
在CoreconnectController中会调用函数CoreConnectSingleController,然后在这个函数内会调用GetDriver函数,代码如下:
//
// Get the Bus Specific Driver Override Protocol instance on the Controller Handle
//
Status = CoreHandleProtocol (
ControllerHandle,
&gEfiBusSpecificDriverOverrideProtocolGuid,
(VOID **) &BusSpecificDriverOverride
);
if (!EFI_ERROR (Status) && (BusSpecificDriverOverride != NULL)) {
DriverImageHandle = NULL;
do {
Status = BusSpecificDriverOverride->GetDriver (
BusSpecificDriverOverride,
&DriverImageHandle
);
if (!EFI_ERROR (Status)) {
AddSortedDriverBindingProtocol (
DriverImageHandle,
&NumberOfSortedDriverBindingProtocols,
SortedDriverBindingProtocols,
DriverBindingHandleCount,
DriverBindingHandleBuffer,
TRUE
);
}
} while (!EFI_ERROR (Status));
}
执行完之后,后面在执行驱动中的support函数的时候就会调用这个驱动的support函数,然后进而执行驱动的start函数.
代码逻辑如下:
// Then add all the remaining Driver Binding Protocols
//
SortIndex = NumberOfSortedDriverBindingProtocols;
for (Index = 0; Index < DriverBindingHandleCount; Index++) {
AddSortedDriverBindingProtocol (
DriverBindingHandleBuffer[Index],
&NumberOfSortedDriverBindingProtocols,
SortedDriverBindingProtocols,
DriverBindingHandleCount,
DriverBindingHandleBuffer,
FALSE
);
}
//
// Free the Driver Binding Handle Buffer
//
CoreFreePool (DriverBindingHandleBuffer);
//
// If the number of Driver Binding Protocols has increased since this function started, then return
// EFI_NOT_READY, so it will be restarted
//
Status = CoreLocateHandleBuffer (
ByProtocol,
&gEfiDriverBindingProtocolGuid,
NULL,
&NewDriverBindingHandleCount,
&NewDriverBindingHandleBuffer
);
CoreFreePool (NewDriverBindingHandleBuffer);
if (NewDriverBindingHandleCount > DriverBindingHandleCount) {
//
// Free any buffers that were allocated with AllocatePool()
//
CoreFreePool (SortedDriverBindingProtocols);
return EFI_NOT_READY;
}
//
// Sort the remaining DriverBinding Protocol based on their Version field from
// highest to lowest.
//
for ( ; SortIndex < NumberOfSortedDriverBindingProtocols; SortIndex++) {
HighestVersion = SortedDriverBindingProtocols[SortIndex]->Version;
HighestIndex = SortIndex;
for (Index = SortIndex + 1; Index < NumberOfSortedDriverBindingProtocols; Index++) {
if (SortedDriverBindingProtocols[Index]->Version > HighestVersion) {
HighestVersion = SortedDriverBindingProtocols[Index]->Version;
HighestIndex = Index;
}
}
if (SortIndex != HighestIndex) {
DriverBinding = SortedDriverBindingProtocols[SortIndex];
SortedDriverBindingProtocols[SortIndex] = SortedDriverBindingProtocols[HighestIndex];
SortedDriverBindingProtocols[HighestIndex] = DriverBinding;
}
}
//
// Loop until no more drivers can be started on ControllerHandle
//
OneStarted = FALSE;
do {
//
// Loop through the sorted Driver Binding Protocol Instances in order, and see if
// any of the Driver Binding Protocols support the controller specified by
// ControllerHandle.
//
DriverBinding = NULL;
DriverFound = FALSE;
for (Index = 0; (Index < NumberOfSortedDriverBindingProtocols) && !DriverFound; Index++) {
if (SortedDriverBindingProtocols[Index] != NULL) {
DriverBinding = SortedDriverBindingProtocols[Index];
PERF_START (DriverBinding->DriverBindingHandle, "DB:Support:", NULL, 0);
Status = DriverBinding->Supported(
DriverBinding,
ControllerHandle,
RemainingDevicePath
);
PERF_END (DriverBinding->DriverBindingHandle, "DB:Support:", NULL, 0);
if (!EFI_ERROR (Status)) {
SortedDriverBindingProtocols[Index] = NULL;
DriverFound = TRUE;
//
// A driver was found that supports ControllerHandle, so attempt to start the driver
// on ControllerHandle.
//
PERF_START (DriverBinding->DriverBindingHandle, "DB:Start:", NULL, 0);
Status = DriverBinding->Start (
DriverBinding,
ControllerHandle,
RemainingDevicePath
);
PERF_END (DriverBinding->DriverBindingHandle, "DB:Start:", NULL, 0);
if (!EFI_ERROR (Status)) {
//
// The driver was successfully started on ControllerHandle, so set a flag
//
OneStarted = TRUE;
}
}
}
}
} while (DriverFound);
提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。