dxe driver 之间可以通过三种方式来沟通
第一种方式是用protocal
protocol 使用前需要先install,install protocol 主要有下面三个API
InstallProtocolInterface()
ReInstallProtocolInterface()
InstallMultipleProtocolInterfaces()
要使用protocol 需要用下面两个API
LocateProtocol()
OpenProtocol()
EFI_HANDLE mNewHandle = NULL;
EFI_SAMPLE_PROTOCOL mSampleProtocol = {
SampleProtocolApi
};
EFI_STATUS
EFIAPI
SampleProtocolApi(
VOID
)
{
return EFI_SUCCESS;
}
EFI_STATUS
EFIAPI
SampleDriverInitialize (
IN EFI_HANDLE
ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
Status = gBS->InstallMultipleProtocolInterfaces (
&mNewHandle,
&gEfiSampleProtocolGuid,
&mSampleProtocol,
NULL
);
ASSERT_EFI_ERROR (Status);
return EFI_SUCCESS;
}
一般我们会在inf文件中指定moudle的入口函数,例如 ENTRY_POINT = InstallAcpiS3Save
本例中的ENTRY_POINT就是SampleDriverInitialize
在这个SampleDriverInitialize函数中就会调用InstallMultipleProtocolInterfaces 来install protocal,每个
protocal 都有一个gEfiSampleProtocolGuid。
因此我们在使用这个protocal的时候就会根据gEfiSampleProtocolGuid 来找到mSampleProtocol,这样就可以用mSampleProtocol 中注册的函数或者变量
EFI_STATUS
SampleFunction (
VOID
)
{
EFI_STATUS
Status;
EFI_SAMPLE_PROTOCOL *SampleProtocol;
Status = gBS->LocateProtocol (
&gEfiSampleProtocolGuid,
NULL,
(VOID **) &SampleProtocol
);
if (EFI_ERROR (Status)) {
return Status;
}
Status = SampleProtocol->SampleProtocolApi();
return Status;
}
例如在SampleFunction 中首先让LocateProtocol通过gEfiSampleProtocolGuid 就找到SampleProtocol,也就是上面install的mSampleProtocol。这样就可以使用mSampleProtocol的SampleProtocolApi() 函数。
而InstallProtocolInterface 和 LocateProtocol 都是在mdemodulepkg/core/dxe/dxemian 中赋值的
EFI_BOOT_SERVICES mBootServices = {
{
(EFI_INSTALL_PROTOCOL_INTERFACE) CoreInstallProtocolInterface, // InstallProtocolInterface
(EFI_REINSTALL_PROTOCOL_INTERFACE) CoreReinstallProtocolInterface, // ReinstallProtocolInterface
}
我们先看看InstallProtocolInterface
EFI_STATUS
EFIAPI
CoreInstallProtocolInterface (
IN OUT EFI_HANDLE *UserHandle,
IN EFI_GUID *Protocol,
IN EFI_INTERFACE_TYPE InterfaceType,
IN VOID *Interface
)
{
return CoreInstallProtocolInterfaceNotify (
UserHandle,
Protocol,
InterfaceType,
Interface,
TRUE
);
#不做任何出来添加notify 为true后调用CoreInstallProtocolInterfaceNotify
}
在CoreInstallProtocolInterfaceNotify 中下面这段code是基本的检测。
if (UserHandle == NULL || Protocol == NULL) {
return EFI_INVALID_PARAMETER;
}
if (InterfaceType != EFI_NATIVE_INTERFACE) {
return EFI_INVALID_PARAMETER;
}
可以通过这行log将要install的protocal打印出来方便debug
DEBUG((DEBUG_INFO, "InstallProtocolInterface: %g %p\n", Protocol, Interface));
if (*UserHandle != NULL) {
Status = CoreHandleProtocol (*UserHandle, Protocol, (VOID **)&ExistingInterface);
if (!EFI_ERROR (Status)) {
return EFI_INVALID_PARAMETER;
}
}
如果UserHandle 不是null,就要检查这个protocal是否已经注册过,也就说protocal不能重复注册,也不会覆盖注册
调用CoreFindProtocolEntry 来找PROTOCOL_ENTRY。其核心代码如下:
for (Link = mProtocolDatabase.ForwardLink;
Link != &mProtocolDatabase;
Link = Link->ForwardLink) {
Item = CR(Link, PROTOCOL_ENTRY, AllEntries, PROTOCOL_ENTRY_SIGNATURE);
if (CompareGuid (&Item->ProtocolID, Protocol)) {
//
// This is the protocol entry
//
ProtEntry = Item;
break;
}
}
主要是通过比较guid是否相等,由于我们是新注册的因此肯定找不到,因此就新建一个PROTOCOL_ENTRY,然后将其插入到mProtocolDatabase
if ((ProtEntry == NULL) && Create) {
ProtEntry = AllocatePool (sizeof(PROTOCOL_ENTRY));
if (ProtEntry != NULL) {
//
// Initialize new protocol entry structure
//
ProtEntry->Signature = PROTOCOL_ENTRY_SIGNATURE;
CopyGuid ((VOID *)&ProtEntry->ProtocolID, Protocol);
InitializeListHead (&ProtEntry->Protocols);
InitializeListHead (&ProtEntry->Notify);
//
// Add it to protocol database
//
InsertTailList (&mProtocolDatabase, &ProtEntry->AllEntries);
}
}
继续回到CoreInstallProtocolInterfaceNotify 中
找到ProtEntry后,就要申请一个 PROTOCOL_INTERFACE *Prot;
Prot = AllocateZeroPool (sizeof(PROTOCOL_INTERFACE));
if (Prot == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto Done;
}
最终要的就是Prot 对象
如果UserHandle 里面的内容是零的话,也就是UserHandle 没有指向任何handle
Handle = (IHANDLE *)*UserHandle;
if (Handle == NULL) {
Handle = AllocateZeroPool (sizeof(IHANDLE));
if (Handle == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto Done;
}
//
// Initialize new handler structure
//
Handle->Signature = EFI_HANDLE_SIGNATURE;
InitializeListHead (&Handle->Protocols);
//
// Initialize the Key to show that the handle has been created/modified
//
gHandleDatabaseKey++;
Handle->Key = gHandleDatabaseKey;
//
// Add this handle to the list global list of all handles
// in the system
//
InsertTailList (&gHandleList, &Handle->AllHandles);
}
因此这里申请IHANDLE 对象,初始化之后就将其插入到gHandleList 中,也就是说uefi中所有的handle都是在gHandleList 这个list中,这是一个全局变量.
Prot->Signature = PROTOCOL_INTERFACE_SIGNATURE;
Prot->Handle = Handle;
Prot->Protocol = ProtEntry;
Prot->Interface = Interface;
//
// Initalize OpenProtocol Data base
//
InitializeListHead (&Prot->OpenList);
Prot->OpenListCount = 0;
//
// Add this protocol interface to the head of the supported
// protocol list for this handle
//
InsertHeadList (&Handle->Protocols, &Prot->Link);
//
// Add this protocol interface to the tail of the
// protocol entry
//
InsertTailList (&ProtEntry->Protocols, &Prot->ByProtocol);
这段code就是将ProtEntry 插入到Prot 中,由于ProtEntry 已经在mProtocolDatabase中了,因此根据mProtocolDatabase 就可以找到Prot。
再来看看CoreLocateProtocol 是怎么根据guid找到protocal的
CoreLocateProtocol (
IN EFI_GUID *Protocol,
IN VOID *Registration OPTIONAL,
OUT VOID **Interface
)
{
EFI_STATUS Status;
LOCATE_POSITION Position;
PROTOCOL_NOTIFY *ProtNotify;
IHANDLE *Handle;
if (Interface == NULL) {
return EFI_INVALID_PARAMETER;
}
if (Protocol == NULL) {
return EFI_NOT_FOUND;
}
*Interface = NULL;
Status = EFI_SUCCESS;
//
// Set initial position
//
Position.Protocol = Protocol;
Position.SearchKey = Registration;
Position.Position = &gHandleList;
//
// Lock the protocol database
//
Status = CoreAcquireLockOrFail (&gProtocolDatabaseLock);
if (EFI_ERROR (Status)) {
return EFI_NOT_FOUND;
}
mEfiLocateHandleRequest += 1;
if (Registration == NULL) {
//
// Look up the protocol entry and set the head pointer
//
Position.ProtEntry = CoreFindProtocolEntry (Protocol, FALSE);
if (Position.ProtEntry == NULL) {
Status = EFI_NOT_FOUND;
goto Done;
}
Position.Position = &Position.ProtEntry->Protocols;
Handle = CoreGetNextLocateByProtocol (&Position, Interface);
} else {
Handle = CoreGetNextLocateByRegisterNotify (&Position, Interface);
}
if (Handle == NULL) {
Status = EFI_NOT_FOUND;
} else if (Registration != NULL) {
//
// If this is a search by register notify and a handle was
// returned, update the register notification position
//
ProtNotify = Registration;
ProtNotify->Position = ProtNotify->Position->ForwardLink;
}
Done:
CoreReleaseProtocolLock ();
return Status;
}
在本例中Registration 为null,因此在CoreLocateProtocol 中是走if的case
首先通过CoreFindProtocolEntry找到ProtEntry,然后根据ProtEntry 找到interface和handle,最后将interface返回,基本上是InstallProtocolInterface的反过程.
第一种方式是用protocal
protocol 使用前需要先install,install protocol 主要有下面三个API
InstallProtocolInterface()
ReInstallProtocolInterface()
InstallMultipleProtocolInterfaces()
要使用protocol 需要用下面两个API
LocateProtocol()
OpenProtocol()
EFI_HANDLE mNewHandle = NULL;
EFI_SAMPLE_PROTOCOL mSampleProtocol = {
SampleProtocolApi
};
EFI_STATUS
EFIAPI
SampleProtocolApi(
VOID
)
{
return EFI_SUCCESS;
}
EFI_STATUS
EFIAPI
SampleDriverInitialize (
IN EFI_HANDLE
ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
Status = gBS->InstallMultipleProtocolInterfaces (
&mNewHandle,
&gEfiSampleProtocolGuid,
&mSampleProtocol,
NULL
);
ASSERT_EFI_ERROR (Status);
return EFI_SUCCESS;
}
一般我们会在inf文件中指定moudle的入口函数,例如 ENTRY_POINT = InstallAcpiS3Save
本例中的ENTRY_POINT就是SampleDriverInitialize
在这个SampleDriverInitialize函数中就会调用InstallMultipleProtocolInterfaces 来install protocal,每个
protocal 都有一个gEfiSampleProtocolGuid。
因此我们在使用这个protocal的时候就会根据gEfiSampleProtocolGuid 来找到mSampleProtocol,这样就可以用mSampleProtocol 中注册的函数或者变量
EFI_STATUS
SampleFunction (
VOID
)
{
EFI_STATUS
Status;
EFI_SAMPLE_PROTOCOL *SampleProtocol;
Status = gBS->LocateProtocol (
&gEfiSampleProtocolGuid,
NULL,
(VOID **) &SampleProtocol
);
if (EFI_ERROR (Status)) {
return Status;
}
Status = SampleProtocol->SampleProtocolApi();
return Status;
}
例如在SampleFunction 中首先让LocateProtocol通过gEfiSampleProtocolGuid 就找到SampleProtocol,也就是上面install的mSampleProtocol。这样就可以使用mSampleProtocol的SampleProtocolApi() 函数。
而InstallProtocolInterface 和 LocateProtocol 都是在mdemodulepkg/core/dxe/dxemian 中赋值的
EFI_BOOT_SERVICES mBootServices = {
{
(EFI_INSTALL_PROTOCOL_INTERFACE) CoreInstallProtocolInterface, // InstallProtocolInterface
(EFI_REINSTALL_PROTOCOL_INTERFACE) CoreReinstallProtocolInterface, // ReinstallProtocolInterface
}
我们先看看InstallProtocolInterface
EFI_STATUS
EFIAPI
CoreInstallProtocolInterface (
IN OUT EFI_HANDLE *UserHandle,
IN EFI_GUID *Protocol,
IN EFI_INTERFACE_TYPE InterfaceType,
IN VOID *Interface
)
{
return CoreInstallProtocolInterfaceNotify (
UserHandle,
Protocol,
InterfaceType,
Interface,
TRUE
);
#不做任何出来添加notify 为true后调用CoreInstallProtocolInterfaceNotify
}
在CoreInstallProtocolInterfaceNotify 中下面这段code是基本的检测。
if (UserHandle == NULL || Protocol == NULL) {
return EFI_INVALID_PARAMETER;
}
if (InterfaceType != EFI_NATIVE_INTERFACE) {
return EFI_INVALID_PARAMETER;
}
可以通过这行log将要install的protocal打印出来方便debug
DEBUG((DEBUG_INFO, "InstallProtocolInterface: %g %p\n", Protocol, Interface));
if (*UserHandle != NULL) {
Status = CoreHandleProtocol (*UserHandle, Protocol, (VOID **)&ExistingInterface);
if (!EFI_ERROR (Status)) {
return EFI_INVALID_PARAMETER;
}
}
如果UserHandle 不是null,就要检查这个protocal是否已经注册过,也就说protocal不能重复注册,也不会覆盖注册
调用CoreFindProtocolEntry 来找PROTOCOL_ENTRY。其核心代码如下:
for (Link = mProtocolDatabase.ForwardLink;
Link != &mProtocolDatabase;
Link = Link->ForwardLink) {
Item = CR(Link, PROTOCOL_ENTRY, AllEntries, PROTOCOL_ENTRY_SIGNATURE);
if (CompareGuid (&Item->ProtocolID, Protocol)) {
//
// This is the protocol entry
//
ProtEntry = Item;
break;
}
}
主要是通过比较guid是否相等,由于我们是新注册的因此肯定找不到,因此就新建一个PROTOCOL_ENTRY,然后将其插入到mProtocolDatabase
if ((ProtEntry == NULL) && Create) {
ProtEntry = AllocatePool (sizeof(PROTOCOL_ENTRY));
if (ProtEntry != NULL) {
//
// Initialize new protocol entry structure
//
ProtEntry->Signature = PROTOCOL_ENTRY_SIGNATURE;
CopyGuid ((VOID *)&ProtEntry->ProtocolID, Protocol);
InitializeListHead (&ProtEntry->Protocols);
InitializeListHead (&ProtEntry->Notify);
//
// Add it to protocol database
//
InsertTailList (&mProtocolDatabase, &ProtEntry->AllEntries);
}
}
继续回到CoreInstallProtocolInterfaceNotify 中
找到ProtEntry后,就要申请一个 PROTOCOL_INTERFACE *Prot;
Prot = AllocateZeroPool (sizeof(PROTOCOL_INTERFACE));
if (Prot == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto Done;
}
最终要的就是Prot 对象
如果UserHandle 里面的内容是零的话,也就是UserHandle 没有指向任何handle
Handle = (IHANDLE *)*UserHandle;
if (Handle == NULL) {
Handle = AllocateZeroPool (sizeof(IHANDLE));
if (Handle == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto Done;
}
//
// Initialize new handler structure
//
Handle->Signature = EFI_HANDLE_SIGNATURE;
InitializeListHead (&Handle->Protocols);
//
// Initialize the Key to show that the handle has been created/modified
//
gHandleDatabaseKey++;
Handle->Key = gHandleDatabaseKey;
//
// Add this handle to the list global list of all handles
// in the system
//
InsertTailList (&gHandleList, &Handle->AllHandles);
}
因此这里申请IHANDLE 对象,初始化之后就将其插入到gHandleList 中,也就是说uefi中所有的handle都是在gHandleList 这个list中,这是一个全局变量.
Prot->Signature = PROTOCOL_INTERFACE_SIGNATURE;
Prot->Handle = Handle;
Prot->Protocol = ProtEntry;
Prot->Interface = Interface;
//
// Initalize OpenProtocol Data base
//
InitializeListHead (&Prot->OpenList);
Prot->OpenListCount = 0;
//
// Add this protocol interface to the head of the supported
// protocol list for this handle
//
InsertHeadList (&Handle->Protocols, &Prot->Link);
//
// Add this protocol interface to the tail of the
// protocol entry
//
InsertTailList (&ProtEntry->Protocols, &Prot->ByProtocol);
这段code就是将ProtEntry 插入到Prot 中,由于ProtEntry 已经在mProtocolDatabase中了,因此根据mProtocolDatabase 就可以找到Prot。
再来看看CoreLocateProtocol 是怎么根据guid找到protocal的
CoreLocateProtocol (
IN EFI_GUID *Protocol,
IN VOID *Registration OPTIONAL,
OUT VOID **Interface
)
{
EFI_STATUS Status;
LOCATE_POSITION Position;
PROTOCOL_NOTIFY *ProtNotify;
IHANDLE *Handle;
if (Interface == NULL) {
return EFI_INVALID_PARAMETER;
}
if (Protocol == NULL) {
return EFI_NOT_FOUND;
}
*Interface = NULL;
Status = EFI_SUCCESS;
//
// Set initial position
//
Position.Protocol = Protocol;
Position.SearchKey = Registration;
Position.Position = &gHandleList;
//
// Lock the protocol database
//
Status = CoreAcquireLockOrFail (&gProtocolDatabaseLock);
if (EFI_ERROR (Status)) {
return EFI_NOT_FOUND;
}
mEfiLocateHandleRequest += 1;
if (Registration == NULL) {
//
// Look up the protocol entry and set the head pointer
//
Position.ProtEntry = CoreFindProtocolEntry (Protocol, FALSE);
if (Position.ProtEntry == NULL) {
Status = EFI_NOT_FOUND;
goto Done;
}
Position.Position = &Position.ProtEntry->Protocols;
Handle = CoreGetNextLocateByProtocol (&Position, Interface);
} else {
Handle = CoreGetNextLocateByRegisterNotify (&Position, Interface);
}
if (Handle == NULL) {
Status = EFI_NOT_FOUND;
} else if (Registration != NULL) {
//
// If this is a search by register notify and a handle was
// returned, update the register notification position
//
ProtNotify = Registration;
ProtNotify->Position = ProtNotify->Position->ForwardLink;
}
Done:
CoreReleaseProtocolLock ();
return Status;
}
在本例中Registration 为null,因此在CoreLocateProtocol 中是走if的case
首先通过CoreFindProtocolEntry找到ProtEntry,然后根据ProtEntry 找到interface和handle,最后将interface返回,基本上是InstallProtocolInterface的反过程.