一 : 标准Application的加载过程

二 : HANDLE 维护的Protocol链表

在加载Application的时候 有重新安装了Protocol:EFI_LOADED_IMAGE_PROTOCOL
且这个Protocol很强大:
Can be used on any image handle to obtain information about the loaded image.

好,下面看一下 打开一个 PROTOCOL的通路是什么样子的,能否有更好的实现方式
下面画了一个开始-结果的图

/*
在Handle持有的PROTOCOL的链表中根据 GUID 来找到该PROTOCOL
GUID唯一的标识,这样的策略随处可见了 如:
optee:
OPTEE中使用UUID来标识一个TA,把UUID通过RPC机制传递到REE侧,根据它来选择要加载的TA
linux kernel:
Linux Kernel中通过PID来标识一个Task(暂不考虑其他), 使用pid来访问Task : find_task_by_pid_ns()
唯一太重要了,不仅仅在software中, 身份证号是唯一的 它重要吧,
但是我越来越认为 身份证号码的设计:暴露地域和生日 是不合理的 哈哈 扯远了😄
param:
@UserHandle
HANDLE是Protocol的提供者, 它的原型是 IHANDLE,表示某一个对象 如一个Image 或Controller等
属于它自己的Protocol都挂接在它维护的链表上(参见上图), 当需要的时候,就到该链表上找吧
这也是Linux Kernel 思想的体现
@Protocol
刚说过 弱水三千 只取你这一瓢
*/
PROTOCOL_INTERFACE *CoreGetProtocolInterface (
IN EFI_HANDLE UserHandle,
IN EFI_GUID *Protocol
)
{
/*
好,开始遍历吧
*/
for (Link = Handle->Protocols.ForwardLink; Link != &Handle->Protocols; Link = Link->ForwardLink) {
Prot = CR(Link, PROTOCOL_INTERFACE, Link, PROTOCOL_INTERFACE_SIGNATURE);
/*
拿到 Protocol 实例
*/
ProtEntry = Prot->Protocol;
/*
开始比较GUID,然后返回,
*/
if (CompareGuid (&ProtEntry->ProtocolID, Protocol)) {
return Prot;
}
}
}

EFI_STATUS
EFIAPI
CoreOpenProtocol (...)
{
/*
Linux Kernel 的思想又再一次迸发出强大的虹吸效应 !
看下面
1 : 分配内存空间
2 : 赋值初始化
3 : 添加到链表上
这种思想是每个操作系统必备的基础设施 !
无论是在 伟大的 Linux Kernel 中 还是 非常优秀的 Security OS : OPTEE
其身影无处不在 !
*/
/*
分配内存空间吧 (小块的内存空间)
*/
OpenData = AllocatePool (sizeof(OPEN_PROTOCOL_DATA));
if (OpenData == NULL) {
Status = EFI_OUT_OF_RESOURCES;
} else {
/*
各种属性填充上去
*/
OpenData->Signature = OPEN_PROTOCOL_DATA_SIGNATURE;
OpenData->AgentHandle = ImageHandle;
OpenData->ControllerHandle = ControllerHandle;
OpenData->Attributes = Attributes;
OpenData->OpenCount = 1;
/*
添加到 OpenList链表中
Protocol被成功打开一次 OpenList被更新一次.
*/
InsertTailList (&Prot->OpenList, &OpenData->Link);
/*
引用计数 +1
当然, 在 CoreCloseProtocol()中会将该值 -1
*/
Prot->OpenListCount++;
Status = EFI_SUCCESS;
}
}
/*
ProtocolsPerHandle 用于获得指定设备所支持的所有 Protocols
下面是将这些protocol的GUID放入到buffer中返回
*/
EFI_STATUS EFIAPI CoreProtocolsPerHandle (...)
{
EFI_GUID **Buffer;
....
/*
分配内存空间
调用者别忘了释放该内存
*/
Buffer = AllocatePool (sizeof (EFI_GUID *) * ProtocolCount);
....
*ProtocolBuffer = Buffer;
/*
遍历 HANDLE 持有的PROTOCOL链表
*/
for ( Link = Handle->Protocols.ForwardLink, ProtocolCount = 0;
Link != &Handle->Protocols;
Link = Link->ForwardLink, ProtocolCount++) {
/*
获取PROTOCOL
*/
Prot = CR(Link, PROTOCOL_INTERFACE, Link, PROTOCOL_INTERFACE_SIGNATURE);
/*
将该PROTOCOL的 GUID放入到 ProtocolBuffer 返回
*/
Buffer[ProtocolCount] = &(Prot->Protocol->ProtocolID);
}
}
/*
调用者赋值释放存放GUID的内存空间
*/
Status = gBS->ProtocolsPerHandle(TheHandle, &ProtocolBuffer, &ProtocolCount);
if (EFI_ERROR (Status))
return 0;
FreePool (ProtocolBuffer);

457





