我的前一篇文章《Android 启动加载器分析 —— ABL(1)》中分析了当代高通平台的 ABL 的整体启动流程,但并未对如何启动至 fastboot 模式和 Linux 内核进行详细的解释。本文将对 fastboot 模式的代码进行分析。
启动至 fastboot 模式的条件
在 ABL 中,当启动镜像验证失败、BootLinux (&Info)
函数启动失败或者接收到启动至 fastboot 的命令(比如使用 adb 重启至 bootloader、在启动时按下了相应的组合按键)时,以下代码会被执行来初始化并执行 fastboot 模式:
1 | fastboot: |
初始化 fastboot 模式
初始化并执行 fastboot 模式的代码是 FastbootInitialize ()
这个函数,它被定义在 QcomModulePkg/Library/FastbootLib/FastbootMain.c
中。它首先调用 FastbootUsbDeviceStart ()
启动 USB 设备,这样就可以接收计算机传来的 fastboot 命令,然后调用 DisplayFastbootMenu ()
显示 fastboot 的菜单。
然后进入一个接收 USB 的死循环处理 USB 事件,直到 fastboot 停止。之后则关闭 fastboot 模式、停止监听按键并停止 USB 设备。
1 | /* Close the fastboot app and stop USB device */ |
返回后就会退出 fastboot 这个 app,再次启动设备。
启动 USB 设备
启动之前需要配置 USB 控制器,fastboot 首先使用 InitUsbControllerGuid
这个 GUID、通过在全局的 EFI_BOOT_SERVICES *gBS
实例来初始化一个 USB 控制器,代码如下:
1 | Status = gBS->CreateEventEx (EVT_NOTIFY_SIGNAL, TPL_CALLBACK, DummyNotify, |
然后通过 UsbDeviceProtolGuid
寻找 fastboot 要使用的协议,存入 Fbd
的 UsbDeviceProtocol
字段中:
1 | Status = gBS->LocateProtocol (&UsbDeviceProtolGuid, NULL, |
这个字段是一个 EFI_USB_DEVICE_PROTOCOL *UsbDeviceProtocol
的指针,其定义在 QcomModulePkg/Include/Protocol/EFIUsbDevice.h
中。
在这之后,fastboot 的命令和变量会被初始化,为接收到的命令准备相应的回调函数。
此时 USB 设备还未被完全注册,因此接下来需要注册设备并启动 USB 设备,包括获取 USB 可用的最大速度、USB 设备规范(包括 vendor ID 和 device ID 等)和设备描述符等。其实现在 QcomModulePkg/Library/FastbootLib/UsbDescriptor.c
中。
注意这时有 SS DevDescriptors/Descriptors
和 DevDescriptors/Descriptors
两种,分别是 Super Speed USB(3.X)和 High Speed USB(2.0)两套描述符。各种 USB 相关的描述符都定义在 MdePkg/Include/IndustryStandard/Usb.h
中,这里最重要的 USB_DEVICE_DESCRIPTOR
定义如下:
1 | /// |
配置好这些 descriptor 之后,调用前面创建的 USB protocol 来启动 USB 设备:
1 | /* Start the usb device */ |
最后,为发送和接收 USB transfer 数据创建缓冲。
注册 fastboot 命令
在启动 USB 设备之前,fastboot 内可用的命令和相关的变量由 EFI_STATUS FastbootCmdsInit (VOID)
来注册,这个函数在 QcomModulePkg/Library/FastbootLib/FastbootCmds.c
中。这个函数为 fastboot 相关命令创建缓冲区和多线程环境来调用回调,然后调用 FastbootCommandSetup
来创建可用的命令与变量:
1 | /* By Default enable list is empty */ |
这些基础的命令可以用来解锁、设置 A/B 分区的激活状态和刷写分区等。
1 | /* |
这两条命令是用来控制启动镜像区的刷写。
1 | /* |
这里注册的 fastboot boot <image>
命令可以启动自定义镜像。
1 | {"oem enable-charger-screen", CmdOemEnableChargerScreen}, |
以上是 OEM 有关的设置和信息。
1 | {"continue", CmdContinue}, |
这是重启和启动相关的命令。
1 | {"getvar:", CmdGetVar}, |
最后的 getvar
命令可以获取设备在 fastboot 模式中相关的变量,而变量的发布则使用 FastbootPublishVar (key, value)
。
在 fastboot 模式中的事件循环
一般情况下,在 fastboot 中会有三个主要的事件循环:
- 接收按键事件更新 fastboot 菜单(在
VOID DisplayFastbootMenu (VOID)
中绘制并创建,在QcomModulePkg/Library/BootLib/FastbootMenu.c
中定义 ); - 通过 USB 接收计算机的 fastboot 发来的命令(在注册 fastboot 时创建);
- 主循环调用
HandleUsbEvents ()
监听 USB 设备的通知,包括设备连接等事件。
结论
本文分析总结了 ABL 在 fastboot 模式下的代码与流程。