connnectController分析(暂未插图)
1. CoreConnectController函数
1.1. 函数介绍
实现BootServices 的connectController接口,该函数将尝试连接一个或多个驱动程序到一个设备。ControllerHandle 是设备句柄,DriverImageHandle 优先选择的驱动程序句柄。RemaingDevicePath将被传入驱动程序DriverBinding->Supported()和Drivebinding->Start()接口,当其为空时,驱动程序将创建所有的子设备,否则将只创建RemainingDevicePath指定的子设备。Recursive 为TRUE 时,该函数将尝试逐层递归连接,直至没有新的设备句柄产生,RemainingDevicePath 此时会被忽略。
在MdeModulePkg/Core/Dxe/hand这个文件夹中,存在对CoreConnectController的定义。
这个函数正式UEFI驱动为了上报系统盘使用的函数:
这里看一下这个上报盘的函数都做了哪些事:
我们在这个接口调用的时候,使用的参数是(channel->channelHandle, NULL, NULL, TRUE)。
整个bootservice的定义位置如下所示:
1.2. 主函数分析
第一个参数:
指向需要连接的控制器的handle
第二个参数:
指向一个支持driverbinding协议的链表
第三个参数:
由控制卡handle指定的一个子handle的devicepath信息
1.2.1. 参数有效性判断
函数执行的第一步是进行handle是否有效的判断:
判断的原则就是遍历所有的handle列表,判断判断当前的handle是否与给定的handle是一致的,若是一致的,则返回EFI_SUCCESS;否则返回EFI_INVALID_PARAMETER。
1.2.2. 赋值devicePath的备份
为了保证输入参数不被损坏,拷贝一个备份。
1.2.3. 将所有的drivers连接到控制卡handle
这里进行一个循环,只要连接的结果返回是EFI_NOT_READY,就表示当前在handle列表里面存在的driver handle协议是增加的了,连接操作需要重复执行一次。
1.2.4. 是否需要遍历子handle
只要改函数第4个传入的参数是TRUE,就会对该handle的所有子handle进行连接操作。
1.2.5. 返回
该函数在确认所有需要遍历的handle都被遍历了之后,便会返回ReturnStatus,主要涉及的返回值位置如下所示:
1.3. CoreConnectSingleController
该函数的作用主要是将控制器和驱动之间创建联系。
1.3.1. 第一步:初始化本地变量
1.3.2. 第二步:获取当前环境下的所有driverbinding协议
1.3.3. 第三步:为获取到的driverbinding协议进行排序
先申请到一个足够到的内存空间:
若传入的需要连接的device列表不是空的,那就将对应的协议放到这个排序的列表中:
1.3.4. 第四步:进行driver override 协议的绑定
首先找到当前环境下面的Platform Driver Overrid协议,这个协议的作用是将驱动连接到当前的控制器上。
1.3.5. 第五步:进行driver family override 协议的绑定
当该主函数传入的最后一个参数是TRUE,才会进行driver family override协议的绑定。否则不会执行:
1.3.6. 第六步:进行specific driver override协议的绑定
‘
1.3.7. 第七步:将剩余driver bingding协议加入到排序列表里
1.3.8. 第八步:判断当前的流程是否是否启动完成
若新查找的driverbinding协议增加了,说明流程处理是没有准备好的,需要重复处理该流程。
1.3.9. 第九步:进行剩余driverbinding协议的排序
将剩余的协议使用版本从高到底的顺序进行排序
1.3.10. 第十步:执行所有的driverbinding协议
第十步:返回值
在当前的控制器上有驱动执行成功;在没有驱动执行成功,但是遍历了devicepath节点,也会返回成功。
1.4. 递归连接所有控制卡handle的子选项
1.4.1. 使用的代码说明
在调用到这个函数的时候,会执行以下的代码:
屏蔽掉这行代码:
新存在的打印是:
可以确认在存在这行代码的时候,可以触发getNextTargetLun这个函数。
1.4.2. 基本数据结构
查看当前驱动的handle结构体:
按照UEFI handle的定义,每个handle都是在这样的一个列表里面。
每在一个handle上安装了一个协议,就会存在一个对应的PROTOCOL_INTERFACE结构,具体显示如下:
1.4.3. 获取子handle的数量
从指向的流程中可以知道对于每一个handle,都是在一个循环链表里面。这个链表里面极了。初步的理解为,以下的操作是:遍历所有的同类型handle链表,找到所有open属性是child的,将这些handle分别进行处理。
每在一个handle上绑定一个协议,就会存在一个PROTOCOL_INTERFACE结构的存在。这里遍历Handle列表,将该列表中的每一个绑定的协议遍历,观察是否存在打开属性是EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER的协议。存在的话将计数加一。
1.4.4. 获取所有的子handle的buffer
按照之前的流程,获取所有的子handle:
1.4.5. 对所有的子handle执行连接操作
1.5. 分析结论
这个函数的作用,实际上的作用就是在当前的控制器上连接一个或者多个的驱动。当后面的第二和第三个参数是空的时候,会对当前控制器上所有的驱动都连接一遍。连接一遍的意思就是,对该控制器上的所有驱动都执行一次driverbinding协议,从support函数入手,一直走完start函数。这也是为什么么会走到我们当前控制卡的support接口。该函数也会对SCSI bus的驱动进行再次的连接:
以至于我们的打印信息里面存在resetTargetLun的打印。
每次调用connectController函数,传入的imagehand是固定的,这就导致我们看到的driverBinding函数会被不断的调用,且存在不同的handle号了。
2. CoreDisconnectController函数
2.1. 函数介绍
将控制器与驱动之间的连接断开
2.2. 主函数分析
2.2.1. 参数分析
该函数传入三个参数,分别是控制卡handle、设备handle和子设备handle。这个函数的参数命名存在一定的误导性。这里的驱动模型与linux下面的一致,connect与disconnect的就是驱动和设备。这里也能明白一个道理,存在一个驱动,就会存在一个handle,存在一个设备也会存在一个handle。
根据开发的选择不同,一个驱动可以连接多个设备;一个设备也能连接多个驱动。关键看我们人为的怎么处理。
ps3rom的驱动要求与设备之间是1对1的关系。
2.2.2. 确认当前的handle是有效的
2.2.3. 重要结构体分析
这样的一个数据结构,里面记录了当前使用的控制卡handle和设备handle。以及需要使用的属性。
2.2.4. 判断是否指定了agentHandle
当没有指定agentHandle的时候,会遍历所有的ctrlHandle。找到所有agentHandle的OpenData节点,判断这个节点是否满足EFI_OPEN_PROTOCOL_BY_DRIVER。若是满足,会将计数不断加一。
2.2.5. 获取所有的agenthandle
收集当前的所有agentHandle
2.2.6. 获取当前控制器的driverbinding协议
根据driverImageHandle来获取当前绑定在设备上的driverdinding协议。具体调用的底层接口是:
2.2.7. 判断是否有子handle
2.2.8. 执行stop操作
执行stop函数。