1、概述
BDS全称:Boot Dev Select(启动设备选择)
主要功能是加载并连接驱动程序,管理并启动引导项。在引导操作系统之前会初始化设备(USB键盘鼠标,VGA设备等),然后通过Variable功能来控制启动顺序,根据启动策略加载对应的引导项,启动操作系统或应用程序。
BDS主要功能:
• 加载设备驱动程序
• 初始化控制台设备
• 创建引导启动项
• 启动操作系统或应用程序
如果加载启动项失败,系统将重新执行DXE dispatcher 以加载更多的驱动,然后重新尝试加载启动项。
BDS策略通过全局NVRAM变量配置。这些变量可以通过运行时服务的GetVariable()读取,通过SetVariable() 设置。例如,变量BootOrder定义了启动顺序,变量Boot###定义了各个启动项。
用户选中某个启动项(或系统进入默认的启动项)后,OS Loader 启动,系统进入TSL阶段。
2、如何进入BDS阶段
此模块为BDS阶段- BdsEntry生成主入口。 当DxeCore分派此模块时,gEfiBdsArchProtocolGuid将被安装 ,包含BdsEntry接口。 DxeCore完成DXE阶段后,gEfiBdsArchProtocolGuid->BdsEntry将被调用。
通过方法BdsEntry();
- • 构造函数 安装 gEfiBdsArchProtocolGuid Protocol
- • DxeMain.c
- 文件最上面对gBS,gST,gRT全局变量进行初始化
- DxeMain()
//函数结尾调用
gBds->Entry (gBds);
- • BdsDxe.inf
MODULE_TYPE = DXE_DRIVER
ENTRY_POINT = BdsInitialize
3、BdsEntry()
3.1 代码流程
- 设置厂商及版本号
- 校验Variable,如果异常则进行删除
- 获取进度条时间
- 设置默认语言
- 前平台初始化
- 初始化热键服务
- 处理驱动程序
- 连接所有控制台设备
- 后平台初始化
- 获取启动菜单
- 判断是否进入Setup
- 等待按键按下,如果按键按下则,启动对应引导项
- 判断BootNext,如果不为空,则启动对应引导项
- 根据启动顺序依次尝试启动
要想理解BDS的启动流程,主要理解两部分 Connect Controller和Boot
Manger
3.2 Connect Controller
3.2.1 驱动初始化过程
- 硬件相关初始化(大部分在PEI阶段或DXE阶段进行)
- 前平台初始化中添加Driver####并更新DriverOrder
- 根据DriverOrder获取Driver####存储的DriverOption
- 根据DriverOption中的DevicePath进行Connect
- 加载DriverOption中DevicePath对应的Image,并运行该Image
- Disconnect Controller All
- 连接控制台
- Connect Controller All
- 连接控制台
- 后平台相关初始化,如串口等设备进行连接
•gBS->ConnectController()
• 排序
• 连接
将驱动安装到指定的设备控制器
• gBS->DisconnectController
将驱动从指定的设备控制器上卸载
.驱动初始化源码分析
|--> BdsEntry()
|--> PlatformBootManagerBeforeConsole() //前平台初始化
|--> LoadOptions = EfiBootManagerGetLoadOptions() //获取所有的DeviceOption
|--> ProcessLoadOptions(LoadOptions)
|--> for EfiBootManagerProcessLoadOption(LoadOptions)
|--> EfiBootManagerConnectDevicePath(LoadOption->FilePath) //Connect DevicePath
|--> while
|--> BmGetNextLoadOptionBuffer(LoadOption->FilePath) //Image
|--> gBS->LoadImage()
|--> gBS->StartImage()
|--> EfiBootManagerDisconnectAll()
|--> EfiBootManagerConnectAll()
|--> PlatformBootManagerAfterConsole()
3.2.2 优先级顺序
- Context Override
参数传入的Image Handle - Platform Driver Override
gEfiPlatformDriverOverrideProtocolGuid->GetDriver(ImageHandle) - Driver Family Override
支持gEfiDriverFamilyOverrideProtocolGuid - Bus Specific Driver Override
BusSpecificDriverOverride->GetDriver(ImageHandle) - Driver Binding
其他 - 根据版本号排序
优先级排序源码分析
|--> while
|--> if BusSpecificDriverOverride->GetDriver()
|--> AddSortedDriverBindingProtocol(TRUE) // 函数内部进行递归
|--> for
|--> CoreHandleProtocol(DriverBinding)
|--> if DriverBinding->ImageHandle == DriverBindingHandle
|--> AddSortedDriverBindingProtocol(FALSE)
|--> for AddSortedDriverBindingProtocol() // 5 添加剩下所有的
|--> for 根据Version对最后添加的进行排序
3.2.3 连接设备控制器
- 在Start或Stop函数中执行Controller相关的一些初始化
- 在ControllerHandle下去安装一些Protocol去实现或管理Controller某些特定功能或数据
- Start中通常不会执行硬件初始化的操作
- 硬件相关初始化动作,大部分都是在Pei Phase、Dxe 前期Non EFI Driver Model的Driver, 初始化Processor、Chipset或Platform,当然有些也通过Oprom 或file in an EFI System Partition来进行初始化。
连接设备控制器源码分析
|--> BdsEntry()
|--> ProcessLoadOptions()
|--> EfiBootManagerDisconnectAll()
|--> EfiBootManagerConnectAll()
|--> BmConnectAllDriversToAllControllers()
|--> gBS->LocateHandleBuffer()
|--> for gBS->ConnectController()
|--> CoreConnectController()
|--> CoreConnectSingleController()
|--> 排序
|--> for
|--> if DriverBinding->Supported() // 返回 Success
|--> DriverBinding->Start() // 一般用于安装 Protocol
|--> if //判断是否有子控制器
|--> CoreConnectController() //递归调用
3.3 Boot Manger
概要:
3.3.1 BootManager运行流程
- 获取BootNext Variable
- 初始化平台恢复引导项
- 添加UEFIShell等引导项
- 获取Setup引导选项
- 添加Hotkey并注册CallBack
- 初始化Hotkey服务
- 删除无效的引导项
- 如果默认进入Setup,则启动该引导项
- 等待按键按下
- 如果有按键按下则启动按键对应引导选项
- 如果BootNext引导项存在,则启动该引导项
- 根据BootOrder获取引导选项
- 按照BootOrder中的顺序依次尝试启动
- 如果平台恢复选项启动,最后尝试启动平台恢复对应的启动引导选项
3.3.2 启动项与Hotkey
BdsEntry()
- 获取引导项
- 添加Key####
- 获取所有的Key####
- 将Key####中内容加入mBmHotkeyList链表
- 为Key####创建Callback
- 等待按键按下
- 启动 mBmHotkeyBootOption
BmHotkeyCallback() - 获取当前按键对应引导项
- 引导项保存至 mBmHotkeyBootOption
|--> BdsEntry()
|--> PlatformBootManagerBeforeConsole() //前平台初始化
|--> PlatformRegisterOptionsAndKeys()
|--> EfiBootManagerGetBootManagerMenu() //获取Setup启动项
|--> EfiBootManagerAddKeyOptionVariable() //添加key#### Variable
|--> EfiBootManagerStartHotkeyService()
|--> BmGetKeyOptions()
|--> for BmProcessKeyOption()
|--> BmGenerateKeyShiftState()
|--> InsertTailList(mBmHotkeyList)
|--> BmRegisterHotkeyNotify()
|-->RegisterKeyNotify(BmHotkeyCallback) //注册callback
|--> BdsWait()
|--> while
|--> PlatformBootManagerWaitCallback() //显示
|--> BdsReadKeys()
|--> EfiBootManagerHotkeyBoot()
|--> EfiBootManagerBoot(&mBmHotkeyBootOption)
|--> BmHotkeyCallback()
|--> EfiBootManagerVariableToLoadOption(Hotkey->BootOption, &mBmHotkeyBootOption)
3.3.3 如何添加引导项
添加 UEFI Shell 启动选项
- 根据gUefiShellFileGuid获取设备节点信息
- 创建新DevicePath,指向该节点
- 添加至Boot#### Variable,需要DevicePath和描述符,返回对应的Number
- 根据Number设置 Key#### Variable
- 根据Key####中的Opation注册Callback函数
- Callback中根据Number获取的BootOption并记录在全局变量中
- 如果有按键按下则启动全局变量中的BootOption选项
添加 FileSystem 启动选项
- 获取所有的 BlockIo Protocol 对应的 Handle
- 依次获取 BlockIo Protocol
- 跳过逻辑分区
- 跳过 Fixed Block IO 和 可移动设备(USB,移动硬盘)
- 获取当前BlockHandle对应的设备路径类型
//ACPI boot type
#define BDS_EFI_ACPI_FLOPPY_BOOT 0x0201
//Message boot type
#define BDS_EFI_MESSAGE_ATAPI_BOOT 0x0301 // Type 03; Sub-Type 01
#define BDS_EFI_MESSAGE_SCSI_BOOT 0x0302 // Type 03; Sub-Type 02
#define BDS_EFI_MESSAGE_USB_DEVICE_BOOT 0x0305 // Type 03; Sub-Type 05
#define BDS_EFI_MESSAGE_SATA_BOOT 0x0312 // Type 03; Sub-Type 18
#define BDS_EFI_MESSAGE_MAC_BOOT 0x030b // Type 03; Sub-Type 11
#define BDS_EFI_MESSAGE_MISC_BOOT 0x03FF
//Media boot type
#define BDS_EFI_MEDIA_HD_BOOT 0x0401 // Type 04; Sub-Type 01
#define BDS_EFI_MEDIA_CDROM_BOOT 0x0402 // Type 04; Sub-Type 02
//BBS boot type
#define BDS_LEGACY_BBS_BOOT 0x0501 // Type 05; Sub-Type 01
#define BDS_EFI_UNSUPPORT 0xFFFF
- 初始化BootOption并加入BootOptions数组
- 获取所有的SimpleFileSystemProtocol对应的Handle
- 依次获取 SimpleFileSystemProtocol对应的IO设备
- 如果文件系统中可以搜索到.\EFI\BOOT\boot.efi 文件系统启动设备,加入BdsBootOptionList启动项
- 搜索包含PXE启动项的文件系统,加入到BdsBootOptionLis链表
- 核实是否有shell启动项,加入链表
添加 Network 启动选项
- 将连接地址全部转换为小写字母
- 判断是否是http或https的地址
- 初始化节点信息
- 创建device path, 指向该节点
- 根据链接地址更新节点信息
- 重新创建device path,指向该节点
- 根据device path等相关信息初始化 load option
- 将load option 加入 Boot#### Variable
3.3.4 如何启动引导项
引导流程
- 搜索该选项是否存在
- 设置BootCurrent
- gBS->LocateDevicePath(Handle)
- gBS->ConnectController(Handle)
- FileBuffer = BmGetNextLoadOptionBuffer(BootOption->FilePath) //获取引导文件
- gBS->LoadImage() //加载Image
- gBS->SetWatchdogTimer() //开启看门狗
- gBS->StartImage() //启动Image
- gBS->SetWatchdogTimer() //关闭看门狗
- gRT->SetVariable() //删除BootCurrent Variable