如果用C语言(伪)代码描述整个BIOS 的执行流程,我想应该是这样的:
main()
{
SecStartup ( SizeOfRam, TempRamBase, *BootFirmwareVolume)
{
// Update the base address and length of Pei temporary memory
PeiCore (&SecCoreData, mPeiSecPlatformInformationPpi);
}
PeiCore()
{
PeiDispatcher (SecCoreData, &PrivateData);
}
DxeMain(VOID *HobStart)
{
CoreDispatcher ();
~~~~~~~~~~~~~~~~~~
}
gBds->Entry (gBds);
}
大家都知道,bios 主要由各个驱动构成(网卡驱动,显卡驱动,IO, Watchdog定时器 etc) , DXE Dispatcher 的职责就是从bios 芯片里面找到它们,并按正确的顺序去执行它们。
当dxe dispatcher 发现一个新的firmware volume 的时候,首先做的是找a priori 文件,a priori 文件里面放的驱动应该最先执行,一个firmware volume 里面最多只能有一个a priori 文件(当然,也可以没有a apriori 文件)。当a priori 文件里面提到的驱动都执行完成之后,剩下的驱动,就需要通过一些逻辑计算来最终确定它的运行顺序,这里所说的逻辑计算主要是去其依赖表达式的计算。
当a priori 文件里面所有的驱动以及所有依赖表达式都为真的驱动执行完之后,控制权就会交给BDS. BDS 的职责就是去建立console device 以及尝试去
引导系统,在BDS 做这些事的过程中,很可能又会发现一些firmware volume, 它就把DXE Dispatcher 叫过来将FV中的驱动找出来并执行完。
如果以流程图来表示,那是这样的:
相关数据结构:
这里值得一说的就是第二个参数和第三个参数, Link 是指已经发现了的,ScheduledLink 正如成员名一样,是马上要投入运行的。
442-447: 注释写得很清楚,如果dispatcher已经运行了,就不用再一次运行了。
接下来,就进入了整个函数的高潮部分。
468: 循环一直进行,直到mScheduledQueue 为空。
469:从link 里面找到DriverEntry. 这里的宏CR有必要拿出来讨论一下。
它的定义是这样的:
#define CR(Record, TYPE, Field, TestSignature) ((TYPE *) ((CHAR8 *) (Record) - (CHAR8 *) &(((TYPE *) 0)->Field)))
下面是它的官方解释:
这里解释得很清楚,分三步:
1. 拿到成员的地址
2.减去这个成员相对于它所在的结构体的偏移量.
3.将最终结果转换为它所在的结构体的类型。
简单来说:就是已经某个结构体成员的地址,如何去计算它所在结构体的位置。
有了前面这些先行知识,我们这一次将相关变量代入这个宏:
DriverEntry = CR (
mScheduledQueue.ForwardLink,
EFI_CORE_DRIVER_ENTRY,
ScheduledLink,
EFI_CORE_DRIVER_ENTRY_SIGNATURE
);
//
// Queue of drivers that are ready to dispatch. This queue is a subset of the
// mDiscoveredList.list of EFI_CORE_DRIVER_ENTRY.
//
LIST_ENTRY mScheduledQueue = INITIALIZE_LIST_HEAD_VARIABLE (mScheduledQueue);
#define INITIALIZE_LIST_HEAD_VARIABLE(ListHead) {&(ListHead), &(ListHead)}
它的作用是初始化双向链表的头节点,即mScheduledQueue的ForwardLink BackLink 都是 mScheduledQueue的地址,然后参考前面
EFI_CORE_DRIVER_ENTRY
的定义,减去相应偏移量,即得到一个DriverEntry的起始地址。
491-523: 对driver 的安全性作检查
如果您认为本教程质量不错,读后觉得收获很大,预期工资涨幅能超过30%,不妨小额赞助我一下,让我有动力继续写出高质量的教程。