通常我们所说的CPU如高通平台MSM8998、苹果A12, 华为海思平台(麒麟980、990)等,这些我们虽然叫CPU,但并不是只有一个CPU,实际上是一个芯片组,在芯片组内部有很多CPU 协同工作的。不同处理器的子系统有:
图1 X12芯片组
图2 其他芯片组
APSS是我们常说的主CPU处理器,比如 ARM Cortex-A7,主频可达1.28GHz,它的启动地址是 0x00100000 (1 M)。
RPM主要负责电源管理相关的事务,比如ARM Cortex-M3,主频可达300MHz,它的启动地址是0x002000000 (2 M)。
主要负责通迅相关业务的处理,比如打电话、发短信、数据业务等 。如Modem Qualcomm Hexagon DSP6,主频可达1GHz,启动地址可自定义配置。
主要负责wifi,bt 等无线相关的事宜。如ARM9, 启动地址为 0x0 或者 0xFFFF0000 ,或自定义配置。
主要负责低功耗相关的处理,最初是audio 处理算法等,后面慢慢地新增了很多功能。如LPASS Hexagon, 启动地址可自定义配置。
在高通平台中,有很多镜像,其启动顺序,保存位置,及各个的功能均是不一样的。
APPS PBL是芯片厂商固化的应用程序,高通芯片出厂自带刷入的,用户或者开发者无法操件它。当芯片复位的时候,芯片会执行内部ROM固化的PBL,PBL执行的目的就是加载SBL并运行。这段程序,我们无法看到其内部代码。
主要作用是:
(1)启动设备和接口检测,支持紧急模式下载;
上电时自启动,通过查询对应的BootOption GPIO状态 或者fuse,知道当前硬件的启动方式(比如,是通过 USB 来启动,还是通过Flash eMMC/NAND来启动)。USB 启动的话,如果拿电脑举例 ,就是我们的 USB 启动盘。在高通中,当检测到要从USB 启动,或者当前Flash eMMC/NAND中是没有镜像时,当前高通板子就会修改USB 口,枚举出 9008 口用于镜像下载。
(2) 加载和验证TCM(Tightly-Coupled Memory)和OCIMEM(On-Chip Internal Memory)的SBL可执行片段。
从Flash eMMC/NAND中加载并校验 SBL1 ELF镜像,通过ELF 头信息知道应该如何将 SBL 镜像拆分加载到不同的内存区域(主要是 L2 TCM 和 RPM Code RAM 两块内存区域)。加载完SBL镜像后,就把 PC 指针交给 SBL,SBL会输出PBL相关的调用关系和对应函数的执行时间,如下图可以查看当前PBL执行时间约为116.718ms:
XBL是扩展引导加载程序(加载会被细分为SBL1、SBL2、SBL3等),受PBL引导加载至内存中,主要工作是对硬件环境初始化,并从flash中加载RPM、QSEE等镜像至内存中并执行其初始化。在该阶段会进行项目、板级、器件等区分,并将区分的信息通过数据结构传递至ABL。
XBL阶段的初始化入口为sbl1_main_ctl,函数的调用位于汇编文件中(文件为:sdx12\boot_images\core\boot\secboot3\hw\mdm\sbl1\sbl1.S),函数调用如下所示:
sbl1_main_ctl实现位于sdx12\boot_images\core\boot\secboot3\hw\mdm\sbl1\sbl1_mc.c,其代码执行流程如下所示:
首先配置MMU的寄存器信息,并读取PBL部分的相关启动信息在后续加载完log机制后进行打印输出。之后对当前阶段的内存进行初始化,读取平台信息,对DAL进行初始化,对硬件初始化(配置pmic设备、温度传感器等;),最后调用SBL阶段总的入口处理函数boot_config_process_bl(),函数通过循环调用boot_config_process_entry来跳转sbl1_config_table数组中的有效成员(加载rpm、app等镜像)。sbl1_config_table中包含了SBL->DEVCFG\APDP\QSEE\RPM\EFS1\EFS2\EFS3\APPSBL\ACDB\MBA\Q6 Modem\AVS.MDSP\APPS等配置,部分内容如下所示:
上图中需要关注的字段主要有load(是否要加载该镜像)、exec(镜像是否要立即执行)、jump(是否执行跳转程序跳转到对应image的入口执行程序)、exec_func(镜像的入口函数)、jump_func(镜像的调整函数)、pre_procs(镜像的pre执行函数)、post_procs(镜像的post执行函数)、boot_load_cancel(取消加载)、target_img_str(打印字符串,在执行相关镜像操作时会打印打印当前在加载哪个镜像)。
在boot_config_process_entry函数中通过判断sbl1_config_table数组各成员的load参数是否为TRUE来打印“Image Load, Start”。
对于boot_config_process_bl()函数的执行逻辑如下图所示:
运行在L2 TCM 和 RPM Code RAM 两块内存区域,和内存子系统初始化相关的模块均由Cortex-A7 来处理。
主要作用:
(1) 初始化memory子系统(总线,DDR,时钟,CDT);
PBL中没有DDR是可以运行的,但SBL中一定要对DDR 做初始化,因为后续所有的镜像load都是load到 DDR中)。一般来说,几乎所有的DDR 相关问题的修改都是在SBL1 中做的,除了DDR的频率的调整是在RPM 中做。
(2) 加载和认证TrustZone 、RPM、MBA、APPSBL、modem镜像;
(3) 内存dump,保存看门狗调试信息;
(4) 开机温升检测,PMIC驱动加载和初始化;
(5) DDR配置等。
运行在 eMMC/NAND LPDDR2 中,和安全相关的模块是由Cortex-A7 来处理,主要功能就是初始化TrustZone bsp 模块,初始化可信任环境。
(1)初始化secure run-time 相关的执行环境;
(2)配置 xPU;
(3)读取fuse 信息;
(4)对各种子系统的 image 镜像做鉴权。
运行在 RPM Code RAM中,由Cortex-M3 来处理电源管理相关模块。RPM的代码实现,主要是Power、休眠唤醒和时钟相关的管理,比如当系统申请休眠后,会投票给 RPM,由RPM 来检查是否是所有系统都申请休眠了,最终由 RPM 来对系统进行监管,此时系统所有的核都进入休眠了。还有RPM 可以设置 DDR 的频率(最高频率,定频等),还包括配置各路 Power LDO 的电压值等。RPM_FW 是由 QTEE 来启动执行的,RPM 启动起来后,我们的UART log 就可以正常打开调试了
运行在 eMMC/NAND LPDDR2 中,注意此处LK是运行32位的,而此时CPU是64位指令集模式,也就是在64位指令集下运行32位指令。此处也就是我们后面要说的fastboot,lk ,UEFI 相关的实现部分。主要功能是加载和验证Kernel 镜像(boot.img),随后将 PC 指针给到 Kernel 当中。PBL 和 SBL 是只运行在 AArch32 模式下的。
Modem Hexagon 运行在 Modem ROM and Hexagon TCM (data and stack)中。在这么多子系统中,只有Modem 是有独立 PBL(Primary Boot Loader)的。
其主要的功能如下:
(1)在Modem ROM中建立初始化Heaxagon TCM内存
(2)从LPDDR2中加载MBA 到Heaxagon TCM中;
(3) MBA 是Modem 的加密校验模块,负责校验后面的Modem模块是否安全
Modem Hexagon 运行在 MSS Hexagon TCM中。
其主要功能如下:
(1)校验后面的Modem模块是否安全
(2)配置Modem 的secure 环境
内核启动流程如下所示:
启动入口位于“sdx12\apps_proc\kernel\msm-5.4\arch\arm64\kernel\head.S”文件中,最终调用start_kernel()函数执行kernel的加载,函数位于“kernel\msm-5.4\init\main.c”文件中,函数逻辑如下所示:
内核启动的流程如下图所示: