无标题
BCM8915x BootLoader
面向第一次接触本仓库的开发者。目标:用最短路径搞懂「这颗芯片是怎么从上电跑到应用固件的」,以及代码是怎么组织的。
0. 先建立大局观
BCM8915x 是一颗多核 SoC:
| 核 | 角色 |
|---|---|
| Cortex-M7 | 引导核。本仓库的代码就跑在它上面(BootLoader) |
| Cortex-A55 ×N | 应用核(跑大系统 / Linux 等) |
| MCU0 / MCU1 | 其它 M 系列协处理核 |
BootLoader 的使命:上电后把自己跑起来 → 初始化 DDR → 依次从 flash 加载各个核的镜像并放它们出复位(release from reset)→ 最后加载 M7 主应用固件并把控制权交出去(handover),自己退场。
一句话记住整条链(细节见第 2 节):
上电 → 复位向量 → EarlyInit → 启动 RTOS → BOOTSRV_TaskFunc |
1. 目录地图
只列学习阶段最该关注的。其余(crypto/nvm/oneui/tools)用到再看。
| 目录 | 是什么 | 优先级 |
|---|---|---|
init/bcm8915x/chip/common/ |
启动汇编 + 芯片早期初始化 + SVC 分发表。一切从这里开始 | ⭐⭐⭐ |
init/bootsrv/ |
Boot Service:加载镜像、拉起各核的主逻辑 | ⭐⭐⭐ |
init/loader/ |
Loader 服务:真正从 flash 分区/文件系统读镜像、校验 | ⭐⭐ |
base/ |
OS 抽象层、RTOS 启动(bcm_os.c)、ARMv7-M 架构相关(lib/arch/armv7m/) |
⭐⭐ |
cpu/arm/ |
CPU 抽象:cache、MPU、中断、SVC 请求封装 | ⭐⭐ |
system/drivers/ |
外设驱动(uart / gpio / dma / ddr / pcie / mcu …) | ⭐ 用到再看 |
architecture/ |
通用基础设施:消息队列(msgq)、轻量队列(lwq)、srvrt 运行时、ptu 分区表工具 | ⭐ |
build/ |
Makefile 片段、链接脚本、编译器配置 | 编译报错时看 |
2. 推荐阅读顺序(跟着启动流程走)
按下面顺序读文件,就是跟着芯片真实的执行流走一遍。不要一上来啃驱动。
第 1 站:复位与启动汇编
init/bcm8915x/chip/common/base_startup.S
看 BCM_OS_RESET_HANDLER(复位入口)做了什么:
- 设置向量表偏移寄存器 VTOR (
0xE000ED08) - 涂抹(paint)栈和 BSS ——
0xA5A5A5A5是栈用量检测的水位标记 MSR MSP设主栈 →BCM8915X_EarlyInit(芯片底层初始化:MPU、内存)- 使能中断、加 MPU 栈保护
CORTEX_MX_SetThreadMode切到非特权线程模式 + PSP(记住这一步,第 4 站会用到)BCM_StartOS把控制权交给 RTOS
第 2 站:RTOS 启动
base/lib/bcm_os.c 里的 BCM_StartOS
大致看 RTOS 怎么建任务、起调度器。不用深挖,知道它最终会调度运行 Boot Service 的任务函数即可。
第 3 站:核心 —— Boot Service 主逻辑 ⭐
init/bootsrv/os/common/bootsrv.c 的 BOOTSRV_TaskFunc()
这是整个仓库最该精读的一个函数。它顺序执行:
PTU_GetAuxImgInfoById(...SYSC...)读系统配置(各核镜像在哪个 LUN、起始块、各种开关)BOOTSRV_InitDDR()初始化 DRAM 控制器BOOTSRV_BootA55()→ 加载 A55 镜像 →DDRDRV_A55Init设 reset vector 并放 A55 出复位MCU_InitProcessor(...MCU1...)启动 MCU1BOOTSRV_PcieInit()PCIe / tandem boot- 加载 M7 主固件 →
SRVRT_Teardown()拆运行时 →BCM_ExecHandover(entry)跳转交权,不返回
例外:
waitInBl为真(调试/烧录场景)时停在 bootloader,不跳转。
第 4 站:搞懂 SVC 系统调用机制 ⭐
这是本代码库最重要的一个「机关」,不懂它读驱动会处处卡壳。
- 触发端:
cpu/arm/lib/cpu_cortex.c的CPU_SvcRequest()— 一行内联汇编SVC #0 - 处理端:
base/lib/arch/armv7m/armv7m.c的BASE_ARMv7mSVCHandler() - 分发表:
init/bcm8915x/chip/common/svc_handlers.c的CPU_SvcHandlerFuncTbl[]
为什么需要它:第 1 站结尾把系统降到了非特权线程模式。非特权代码不能直接碰特权资源(cache 维护、MPU、跳转交权等)。于是驱动/服务想做特权操作时,填一个请求结构体,用 SVC #0 陷入特权 Handler 模式,由分发表按 ID 调对应的特权服务函数,做完再返回非特权态。
链路:
非特权代码 |
例:
BCM_ExecHandover(跳转到新固件)内部就是走这条 SVC 路径(BCM_OS_CMD_EXEC_HANDOVER),因为「替换掉自己」是特权操作。
第 5 站:镜像从哪来
init/loader/lib/loader_srv.c 的 LOADER_SrvLoadFsImgReq / LOADER_SrvLoadPartitionReq
看镜像是如何从 flash 分区 / 文件系统按 UUID 读出、校验、放到内存的。搭配 architecture/ptu/(分区表工具)一起看。
3. 读这类嵌入式代码的几个技巧
- 命名前缀即模块:
BOOTSRV_= boot service,LOADER_= loader,CPU_= cpu 抽象,DDRDRV_= DDR 驱动,CORTEX_MX_= Cortex-M 架构,BCM_= OS/公共。看前缀就知道去哪个目录找。 - **
os/basevsos/none**:很多驱动有两套实现。base= 有 RTOS 的版本(走 SVC / 消息队列),none= 裸机版本。当前 bootloader 用哪套看编译配置。 @trace #BRCM_SW...注释:需求/设计追溯标签(安全认证用),阅读时可忽略,别被它们淹没。COMP_ASM等COMP_宏:编译器抽象层,定义在architecture/abstract/inc/compiler.h。COMP_ASM=__asm volatile。- **
.venv、obj/、out/、.cache/**:生成物 / 环境目录,搜索时跳过。
4. 动手:先让它编起来
(详见根目录 README.md,这里给最短路径)
uv venv && source .venv/bin/activate |
编出来后:
- 用
compile_commands.json配合 clangd,让编辑器能跳转定义(读代码神器)。 - 从
BOOTSRV_TaskFunc开始,用「跳转到定义」一层层往下点,比通读快得多。
5. 关键点
- 上电后第一条执行的代码在哪个文件、哪个符号?
- 为什么启动最后要切到「非特权线程模式」?带来了什么约束?
- 一个非特权驱动想做 cache flush(特权操作),要走什么机制?涉及哪 3 个文件?
BOOTSRV_TaskFunc一共拉起了哪几个核?各自的镜像从哪读、怎么放出复位?- BootLoader 跳到 M7 主固件前做了哪两步收尾?为什么跳转要走 SVC?
参考
init/bcm8915x/chip/common/base_startup.S的BCM_OS_RESET_HANDLER。它是复位向量指向的符号,位于.reset_func段,芯片上电取复位向量后跳到这里。为了权限隔离 / 故障域收窄:应用与服务代码跑在非特权态,碰不到特权资源就无法误改关键硬件,配合 MPU 还能拦栈溢出。约束是——任何特权操作(cache 维护、MPU 配置、跳转交权、访问受限寄存器)都不能直接做,必须通过
SVC #0陷入特权 Handler 模式代为执行。走 SVC 系统调用机制。三个文件:
cpu/arm/lib/cpu_cortex.c—CPU_SvcRequest()触发SVC #0base/lib/arch/armv7m/armv7m.c—BASE_ARMv7mSVCHandler()接住并分发init/bcm8915x/chip/common/svc_handlers.c—CPU_SvcHandlerFuncTbl[]按 ID 查表调具体特权服务
(cache 具体命令封装在cpu/arm/os/base/cache_osil.c,填sysReqID=CPU_SVC_DCA_ID后走上面这条链)
三类核:
- A55 应用核:
BOOTSRV_BootA55→LOADER_SrvLoadFsImgReq按 UUID 从 flash 文件系统加载 →DDRDRV_A55Init把入口写进 A55 reset vector 并放出复位。 - MCU1:加载
MCU1镜像 →MCU_InitProcessor(MCU_PROCESSOR_ID_MCU1, ..., entryPt, opt)设入口并释放。 - M7 主固件(最后一步,见第 5 题):加载
MCU0/FW 分区镜像,最终跳转交给它。
镜像位置都来自SYSC系统配置里的LUN+ 起始块。
- A55 应用核:
两步收尾:
SRVRT_Teardown()(拆掉 bootloader 的服务运行时)→BCM_ExecHandover(entryPt)(跳转,不返回)。跳转走 SVC 是因为「替换掉当前正在运行的自己」(改栈、改 VTOR、丢弃上下文)是特权操作,而BOOTSRV_TaskFunc跑在非特权线程态,只能通过BCM_OS_CMD_EXEC_HANDOVER这个 SVC 命令陷入特权态完成。
5.5 启动时序图(上电 → 交权)
标注了每一步对应的 文件 : 符号,读代码时对照着走。
┌──────────────┐ |
★ 记住第 [3] 步:正是这一步的非特权降级,导致后面 (c)/(d)/(f) 里所有特权动作都必须绕道 SVC。
6. 下一步深入方向(按兴趣挑)
- 加载与安全:
init/loader/+crypto/(镜像签名校验) - A55 拉起细节:
system/drivers/ddrdrv/的DDRDRV_A55Init(reset vector 寄存器操作) - 交权细节:
svc_handlers.c里BCM_OS_CMD_EXEC_HANDOVER对应的 handler,看 M7 如何自我替换跳转 - RTOS 内核:
base/+architecture/msgq、architecture/srvrt

