最近在調試UEFI中 option rom 的支持,板卡使用的是南京神易的網絡隔離卡,在他們的板卡上適配的。在這個過程中,收貨還是挺多的,個人感覺咋遇到bug的時候,首先要做的是冷靜仔細的分析,定位問題的方向很重要,只有找對了方向才能,後面做的纔是有用功,否則做再多的驗證也只是無用功,也只能當做排除發法的選項。下面就將以問題的方式來講解oprom驅動的執行原理和過程。話不多少,直接上乾貨。
PCI掃描過程中是如何確定PCI設備是支持option rom的?
在pci掃描設備的過程中,在函數PciSearchDevice中會調用函數GetOpRomInfo這個函數,PCI的掃描函數這裏就不做過多的介紹了。
函數調用關係如下:
PciBusDriverBindingStart->PciEnumerator->PciRootBridgeEnumerator->PciScanBus
->PciSearchDevice->GetOpRomInfo
下面詳細的介紹一下函數GetOpRomInfo,代碼如下:
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 = (~AllOnes) + 1;
return EFI_SUCCESS;
}
首先看一下函數入參,PciIoDevice是一個PCI_IO_DEVICE 類型的指針,PCI_IO_DEVICE結構定義如下:
struct _PCI_IO_DEVICE {
UINT32 Signature;
EFI_HANDLE Handle;
EFI_PCI_IO_PROTOCOL PciIo;
LIST_ENTRY Link;
EFI_BUS_SPECIFIC_DRIVER_OVERRIDE_PROTOCOL PciDriverOverride;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *PciRootBridgeIo;
EFI_LOAD_FILE2_PROTOCOL LoadFile2;
//
// PCI configuration space header type
//
PCI_TYPE00 Pci;
//
// Bus number, Device number, Function number
//
UINT8 BusNumber;
UINT8 DeviceNumber;
UINT8 FunctionNumber;
//
// BAR for this PCI Device
//
PCI_BAR PciBar[PCI_MAX_BAR];
//
// The bridge device this pci device is subject to
//
PCI_IO_DEVICE *Parent;
//
// A linked list for children Pci Device if it is bridge device
//
LIST_ENTRY ChildList;
//
// TRUE if the PCI bus driver creates the handle for this PCI device
//
BOOLEAN Registered;
//
// TRUE if the PCI bus driver successfully allocates the resource required by
// this PCI device
//
BOOLEAN Allocated;
//
// The attribute this PCI device currently set
//
UINT64 Attributes;
//
// The attributes this PCI device actually supports
//
UINT64 Supports;
//
// The resource decode the bridge supports
//
UINT32 Decodes;
//
// TRUE if the ROM image is from the PCI Option ROM BAR
//
BOOLEAN EmbeddedRom;
//
// The OptionRom Size
//
UINT64 RomSize;
//
// TRUE if all OpROM (in device or in platform specific position) have been processed
//
BOOLEAN AllOpRomProcessed;
//
// TRUE if there is any EFI driver in the OptionRom
//
BOOLEAN BusOverride;
//
// A list tracking reserved resource on a bridge device
//
LIST_ENTRY ReservedResourceList;
//
// A list tracking image handle of platform specific overriding driver
//
LIST_ENTRY OptionRomDriverList;
EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *ResourcePaddingDescriptors;
EFI_HPC_PADDING_ATTRIBUTES PaddingAttributes;
//
// Bus number ranges for a PCI Root Bridge device
//
EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *BusNumberRanges;
BOOLEAN IsPciExp;
//
// For SR-IOV
//
UINT8 PciExpressCapabilityOffset;
UINT32 AriCapabilityOffset;
UINT32 SrIovCapabilityOffset;
UINT32 MrIovCapabilityOffset;
PCI_BAR VfPciBar[PCI_MAX_BAR];
UINT32 SystemPageSize;
UINT16 InitialVFs;
UINT16 ReservedBusNum;
//
// Per PCI to PCI Bridge spec, I/O window is 4K aligned,
// but some chipsets support non-standard I/O window alignments less than 4K.
// This field is used to support this case.
//
UINT16 BridgeIoAlignment;
};
這個結構包含了一個PCI 設備的全部信息,其中和option rom相關的有
Decodes:表示橋片是否支持解碼操作
EmbeddedRom:這個設備是否有內嵌的rom
RomSize:內嵌的rom的大小
AllOpRomProcessed:rom中的代碼是否處理過(這裏就是指load 和start)
BusOverride:是否是在pcibusoverde過程去執行
OptionRomDriverList:這個設備下面的optionrom驅動的鏈表,有一個驅動就會添加到鏈表中.
在GetOpRomInfo中這裏只是探測了這個設備是否有Rom,如果有讀取RomSize的大小到PciIoDevice中。這時探測到了這個pci設備包含的rom大小。
然後在函數PciHostBridgeResourceAllocator總調用ProcessOptionRom對optionrom進行處理,函數調用關係如下:
PciBusDriverBindingStar->PciEnumerator->PciHostBridgeResourceAllocator
->ProcessOptionRom
下面詳細的介紹一下ProcessOptionRom,其代碼如下:
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的代碼實現:
EFI_STATUS
LoadOpRomImage (
IN PCI_IO_DEVICE *PciDevice,
IN UINT64 RomBase
)
{
UINT8 RomBarIndex;
UINT8 Indicator;
UINT16 OffsetPcir = 0;
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;
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;
}
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;
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;
}
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->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,
PciDevice->PciIo.RomImage,
PciDevice->PciIo.RomSize
);
//
// Free allocated memory
//
FreePool (RomHeader);
FreePool (RomPcir);
return RetStatus;
}
根據以上代碼可知,首先讀取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;
)根據裏面的內容判斷是否是一個有效的Option Rom 驅動.讀取數據之後,相應的校驗都正確之後,會重PCI_DATA_STRUCTURE結構中計算出RomImageSize的大小,這裏需要注意RomImageSize的大小是ImageLength*512,這裏是uefi spec規定的.同時RomImageSize的大小應該和InitializationSize(下面結構體的第二個成員)大小一致,否則認爲是無效的option rom.這些實在後面的LocalLoadFile2函數中體現的.
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;
}
根據上面的代碼可知,此時將這個設備的信息Seg號,Bus號, Dev號,Func號,ImageHandle,RomImage(load到內存之後的地址),RomSize就是rom的大小都保存在了全局變量mRomImageTable內,這是一個全局的指針,沒添加一個變量指針指針位置向後移動.這樣在掃描過程中將所有的支持oprom 的pci設備都保存在了mRomImageTable內了.
Option Rom 驅動後面是如何被處理成的 ?
在函數PciBusDriverBindingStart中,當pci設備都枚舉之後,再此調用這個函數的時候,就會調用函數PciEnumeratorLight
在這個函數內去調用函數ProcessOptionRomLight,其代碼如下:
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);這個函數的代碼如下:
BOOLEAN
PciRomGetImageMapping (
IN PCI_IO_DEVICE *PciIoDevice
)
{
EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *PciRootBridgeIo;
UINTN Index;
PciRootBridgeIo = PciIoDevice->PciRootBridgeIo;
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 ) {
if (mRomImageTable[Index].ImageHandle != NULL) {
AddDriver (PciIoDevice, mRomImageTable[Index].ImageHandle, NULL);
}
PciIoDevice->PciIo.RomImage = mRomImageTable[Index].RomImage;
PciIoDevice->PciIo.RomSize = mRomImageTable[Index].RomSize;
return TRUE;
}
}
return FALSE;
}
根據上面的代碼可知,如果傳進來的這個設備的信息和之前枚舉設備時添加到mRomImageTable中的設備信息匹配上之後,就會調用AddDriver,用這設備的ImageHandle,和PciIoDevice信息創建一個驅動,並添加到pci設備的OptionRomDriverList中,AddDriver
的代碼如下:
EFI_STATUS
AddDriver (
IN PCI_IO_DEVICE *PciIoDevice,
IN EFI_HANDLE DriverImageHandle,
IN EFI_DEVICE_PATH_PROTOCOL *DriverImagePath
)
{
PCI_DRIVER_OVERRIDE_LIST *Node;
//
// Caller should pass in either Image Handle or Image Path, but not both.
//
ASSERT ((DriverImageHandle == NULL) || (DriverImagePath == NULL));
Node = AllocateZeroPool (sizeof (PCI_DRIVER_OVERRIDE_LIST));
if (Node == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Node->Signature = DRIVER_OVERRIDE_SIGNATURE;
Node->DriverImageHandle = DriverImageHandle;
Node->DriverImagePath = DuplicateDevicePath (DriverImagePath);
InsertTailList (&PciIoDevice->OptionRomDriverList, &Node->Link);
PciIoDevice->BusOverride = TRUE;
return EFI_SUCCESS;
}
AddDriver的主要作用就是創建這個設備驅動的devicepatch路徑,並將驅動,這個驅動的節點添加到OptionRomDriverList鏈表中.這裏驅動節點的類型是PCI_DRIVER_OVERRIDE_LIST,並更新PciIoDevice->BusOverride = TRUE;後面會使用這個標誌.
這樣就將一個option rom驅動添加到了驅動列表列面.
Option Rom 驅動是如何被load 以及執行的?
在函數PciBusDriverBindingStart後面會調用函數StartPciDevices,在這個函數內會調用函數StartPciDevicesOnBridge,然後再調用RegisterPciDevice函數,整個函數的調用關係如下:
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;
UINT8 Indicator;
Indicator = 0;
RomHeader = RomImage;
if (RomHeader == NULL) {
return FALSE;
}
do {
if (RomHeader->Signature != PCI_EXPANSION_ROM_HEADER_SIGNATURE) {
RomHeader = (PCI_EXPANSION_ROM_HEADER *) ((UINT8 *) RomHeader + 512);
continue;
}
//
// The PCI Data Structure must be DWORD aligned.
//
if (RomHeader->PcirOffset == 0 ||
(RomHeader->PcirOffset & 3) != 0 ||
(UINT8 *) RomHeader + RomHeader->PcirOffset + sizeof (PCI_DATA_STRUCTURE) > (UINT8 *) RomImage + RomSize) {
break;
}
RomPcir = (PCI_DATA_STRUCTURE *) ((UINT8 *) RomHeader + RomHeader->PcirOffset);
if (RomPcir->Signature != PCI_DATA_STRUCTURE_SIGNATURE) {
break;
}
if (RomPcir->CodeType == PCI_CODE_TYPE_EFI_IMAGE) {
return TRUE;
}
Indicator = RomPcir->Indicator;
RomHeader = (PCI_EXPANSION_ROM_HEADER *) ((UINT8 *) RomHeader + RomPcir->ImageLength * 512);
} while (((UINT8 *) RomHeader < (UINT8 *) RomImage + RomSize) && ((Indicator & 0x80) == 0x00));
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函數就是下面的代碼:
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的代碼如下:
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;
UINT32 InitializationSize;
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);
ASSERT (Pcir->Signature == PCI_DATA_STRUCTURE_SIGNATURE);
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 = Pcir->ImageLength * 512;
InitializationSize = (UINT32) EfiRomHeader->InitializationSize * 512;
if (InitializationSize > ImageSize || EfiRomHeader->EfiImageHeaderOffset >= InitializationSize) {
return EFI_NOT_FOUND;
}
ImageBuffer = (UINT8 *) EfiRomHeader + EfiRomHeader->EfiImageHeaderOffset;
ImageLength = InitializationSize - 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失敗,也就會導致執行失敗.我們平臺一開始不支持也就是這個原因.這個函數要求的條件沒有滿足,所以導致laod
失敗.
下面還是回到函數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;
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;
Indicator = 0;
//
// Get the Address of the Option Rom image
//
RomBar = PciDevice->PciIo.RomImage;
RomBarOffset = (UINT8 *) RomBar;
RetStatus = EFI_NOT_FOUND;
if (RomBar == NULL) {
return RetStatus;
}
ASSERT (((EFI_PCI_EXPANSION_ROM_HEADER *) RomBarOffset)->Signature == PCI_EXPANSION_ROM_HEADER_SIGNATURE);
do {
EfiRomHeader = (EFI_PCI_EXPANSION_ROM_HEADER *) RomBarOffset;
if (EfiRomHeader->Signature != PCI_EXPANSION_ROM_HEADER_SIGNATURE) {
RomBarOffset += 512;
continue;
}
Pcir = (PCI_DATA_STRUCTURE *) (RomBarOffset + EfiRomHeader->PcirOffset);
ASSERT (Pcir->Signature == PCI_DATA_STRUCTURE_SIGNATURE);
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 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
);
if (EFI_ERROR (Status)) {
//
// Record the Option ROM Image device path when LoadImage fails.
// PciOverride.GetDriver() will try to look for the Image Handle using the device path later.
//
AddDriver (PciDevice, NULL, PciOptionRomImageDevicePath);
} else {
Status = gBS->StartImage (ImageHandle, NULL, NULL);
if (!EFI_ERROR (Status)) {
//
// Record the Option ROM Image Handle
//
AddDriver (PciDevice, ImageHandle, NULL);
PciRomAddImageMapping (
ImageHandle,
PciDevice->PciRootBridgeIo->SegmentNumber,
PciDevice->BusNumber,
PciDevice->DeviceNumber,
PciDevice->FunctionNumber,
PciDevice->PciIo.RomImage,
PciDevice->PciIo.RomSize
);
RetStatus = EFI_SUCCESS;
}
}
FreePool (PciOptionRomImageDevicePath);
NextImage:
RomBarOffset += ImageSize;
} while (((Indicator & 0x80) == 0x00) && (((UINTN) RomBarOffset - (UINTN) RomBar) < PciDevice->RomSize));
return RetStatus;
}
由上面的代碼可知,在load成功之後就會去執行start.注意這裏面load函數內部就會走上面註冊的函數.執行完start函數,也就是運行了驅動的entrypoint之後,會執行AddDriver.
在後面Dxe結尾的時候,在函數connectcontroller函數中,會調用GetDriver函數,GetDriver函數代碼如下:
EFI_STATUS
EFIAPI
GetDriver (
IN EFI_BUS_SPECIFIC_DRIVER_OVERRIDE_PROTOCOL *This,
IN OUT EFI_HANDLE *DriverImageHandle
)
{
PCI_IO_DEVICE *PciIoDevice;
LIST_ENTRY *Link;
PCI_DRIVER_OVERRIDE_LIST *Override;
BOOLEAN ReturnNext;
Override = NULL;
PciIoDevice = PCI_IO_DEVICE_FROM_PCI_DRIVER_OVERRIDE_THIS (This);
ReturnNext = (BOOLEAN) (*DriverImageHandle == NULL);
for ( Link = GetFirstNode (&PciIoDevice->OptionRomDriverList)
; !IsNull (&PciIoDevice->OptionRomDriverList, Link)
; Link = GetNextNode (&PciIoDevice->OptionRomDriverList, Link)
) {
Override = DRIVER_OVERRIDE_FROM_LINK (Link);
if (ReturnNext) {
if (Override->DriverImageHandle == NULL) {
Override->DriverImageHandle = LocateImageHandle (Override->DriverImagePath);
}
if (Override->DriverImageHandle == NULL) {
//
// The Option ROM identified by Override->DriverImagePath is not loaded.
//
continue;
} else {
*DriverImageHandle = Override->DriverImageHandle;
return EFI_SUCCESS;
}
}
if (*DriverImageHandle == Override->DriverImageHandle) {
ReturnNext = TRUE;
}
}
ASSERT (IsNull (&PciIoDevice->OptionRomDriverList, Link));
//
// ReturnNext indicates a handle match happens.
// If all nodes are checked without handle match happening,
// the DriverImageHandle should be a invalid handle.
//
if (ReturnNext) {
return EFI_NOT_FOUND;
} else {
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函數.
代碼邏輯如下:
//
// 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);