4. STM32MP双核启动与固件加载机制

STM32MP1 系列内部集成 Cortex-A7 与 Cortex-M4 两个异构内核。上电后,Cortex-A7 侧由 BootROM 引导,经 TF-A、U-Boot 加载 Linux 内核;而 Cortex-M4 侧并不参与这条主启动链,它的固件由 Linux 运行起来之后,通过 remoteproc 框架按需加载。理解这一"先 A 后 M、由 A 控 M"的时序关系,是掌握 OpenAMP 通信能否建立的前提——只有 M4 固件被正确加载并启动,RPMsg 通道才会出现。

一、Cortex-M 固件的三种加载方式

在 STM32MP 平台上,M4 固件进入内存并运行主要有三种途径,适用场景各不相同。

1. remoteproc 动态加载(最常用)。把编译好的固件(通常是 elf 或 stm32 格式)放到 Linux 根文件系统的 /lib/firmware/ 目录下,由内核的 remoteproc 驱动读取、解析并写入 M4 可访问的 RAM 区域,然后释放复位、启动内核。这种方式灵活,可在运行时更换固件,是 OpenAMP 开发的标准做法。

2. U-Boot 阶段预加载。在 U-Boot 中通过 ext4load 或 fatload 把固件搬到指定 DDR 地址,并配置 rproc 相关环境变量,让内核启动时直接接管已就位的固件。适合固件较大、希望缩短 Linux 启动后等待时间的场景。

3. 固化到 Flash 并由 M4 自启动。部分配置下 M4 可在 A7 之前独立运行,但此时无法使用依赖 Linux 侧 vring 的 RPMsg,一般只用于纯裸机控制任务。

三种方式中,只有前两种能与 OpenAMP 协同工作,因为它们最终都由 Linux 的 remoteproc 框架统一管理。

二、remoteproc 控制接口

remoteproc 把每个远程处理器抽象成一个设备,用户空间通过 sysfs 与之交互。STM32MP1 上 M4 对应的节点通常是 /sys/class/remoteproc/remoteproc0/,关键文件有三个:

  • state:只读,反映当前状态,取值为 offline、running、suspended 等。
  • firmware:可写,指定要加载的固件文件名(相对 /lib/firmware/)。
  • name:只读,显示该 remoteproc 实例的名字,如 m4。

控制动作通过向 state 写入字符串完成:写 start 触发加载并启动,写 stop 停止并释放资源。下面是一段典型的操作序列:

# 查看当前状态
cat /sys/class/remoteproc/remoteproc0/state
# offline

# 指定固件
echo openamp_cm4.elf > /sys/class/remoteproc/remoteproc0/firmware

# 启动
echo start > /sys/class/remoteproc/remoteproc0/state
# 再次查看,应变为 running
cat /sys/class/remoteproc/remoteproc0/state
# running

# 停止
echo stop > /sys/class/remoteproc/remoteproc0/state

启动成功后,内核日志(dmesg)会出现类似 remoteproc remoteproc0: powering up m4 与 remoteproc remoteproc0: remote processor m4 is now up 的信息。若固件里包含 RPMsg 资源表,还会看到 virtio 设备注册的打印,随后 /dev/ 下出现 ttyRPMSG0 之类的字符设备节点——这正是 OpenAMP 通道就绪的标志。

三、启动、停止与复位流程

一次完整的启动并非简单"写 start",其内部经历若干阶段,可用下面的信号链来理解:

remoteproc 启动 / 停止 / 复位信号链 启动 写 start 解析固件 (elf/stm32) 写入 M4 RAM + 资源表 释放复位 M4 开始执行 virtio 注册 生成 rpmsg 节点 停止 写 stop 通知 M4 停止 (资源表回调) 撤销 vring 释放 rpmsg 拉低复位 M4 停机 state = offline 复位 stop → start 组合 重新加载固件并重启 vring / rpmsg 全部重建 旧通道失效,需重新打开

需要特别注意的是"复位":remoteproc 没有单独的 reset 文件,复位等价于先 stop 再 start。这一过程会销毁原有的 vring 与 RPMsg 端点,A7 侧此前打开的 /dev/ttyRPMSG0 文件描述符随之失效,必须重新 open 才能继续通信。很多"通信突然中断"的问题,根源就是 M4 侧异常复位后 A7 侧没有重新建立通道。

四、设备树配置要点

remoteproc 能否找到 M4、能否正确划分内存,取决于设备树。核心节点是 m4_rproc,关键属性包括:

  • compatible = "st,stm32mp1-m4":匹配 STM32MP1 的 M4 remoteproc 驱动。
  • memory-region:引用 reserved-memory 中为 M4 预留的区域,通常包含固件代码段、数据段和 vring 共享内存三部分。
  • resets 与 clocks:分别指向 M4 的复位控制器和时钟,驱动靠它们完成释放复位与使能时钟。
  • st,syscfg-*:用于配置 M4 的启动地址、向量表偏移等,必须与固件链接脚本一致。

一个简化的 reserved-memory 片段如下,注意 vring 区域必须与固件资源表中声明的地址严格对应,否则会出现"固件已 running 但 RPMsg 节点不出现"的现象:

reserved-memory {
    #address-cells = <1>;
    #size-cells = <1>;
    ranges;

    mcuram2: mcuram2@10000000 {
        reg = <0x10000000 0x40000>;
        no-map;
    };

    vdev0vring0: vdev0vring0@10040000 {
        reg = <0x10040000 0x1000>;
        no-map;
    };

    vdev0vring1: vdev0vring1@10041000 {
        reg = <0x10041000 0x1000>;
        no-map;
    };

    vdev0buffer: vdev0buffer@10042000 {
        reg = <0x10042000 0x4000>;
        no-map;
    };
};
调试建议:若 state 写入 start 后立即回到 offline,优先检查固件格式是否被驱动识别、memory-region 是否覆盖了固件实际加载地址;若 state 为 running 但无 rpmsg 节点,则重点核对 vring 地址与资源表是否一致。

五、判断加载是否成功的依据

综合来看,一次成功的 M4 固件加载应同时满足:state 显示 running;dmesg 中出现 "remote processor m4 is now up";/dev/ 下出现对应的 rpmsg 字符设备;A7 侧打开该设备后能收到 M4 发来的首帧数据。四者缺一,说明加载链在某个环节中断,可依据上面的信号链逐段排查。掌握这条从设备树到 rpmsg 节点的完整路径,后续 OpenAMP 通信的调试就有了明确的定位坐标。