2. OpenAMP框架核心组件解析

OpenAMP 并不是一个单一驱动,而是一套把“异构核之间如何发现、如何通知、如何搬数据”拆成若干可替换组件的软件框架。在 STM32MP 这类 Cortex-A + Cortex-M 的平台上,A 核通常运行 Linux,M 核运行裸机或 RTOS 固件,两侧通过共享内存交换数据、通过中断通知对方。要真正掌握 OpenAMP,关键不是记住五个名词,而是理解每个组件“管什么、不管什么”,以及它们之间的依赖顺序。

下面这张图给出五个核心组件在 STM32MP 上的职责与交互关系:remoteproc 负责生命周期与资源表,virtio 负责共享内存上的队列抽象,rpmsg 负责面向应用的消息通道,mailbox 负责跨核中断通知,共享内存则是所有数据的物理载体。

OpenAMP 五组件职责与交互(STM32MP 双核场景) Cortex-A7 / Linux 侧 remoteproc 驱动 加载固件 / 解析资源表 / start·stop virtio 设备(vdev) vring 队列 / 描述符 / used·avail 环 rpmsg 总线与通道 /dev/rpmsgX / 端点 / 名称服务 mailbox 控制器 IPCC 通道 / 触发跨核中断 共享内存(DDR 保留区) vring + rpmsg buffer 物理载体 Cortex-M4 / 固件侧 OpenAMP 库(libmetal) 初始化 / 资源表响应 / 端点注册 virtio 从端 共享 vring / 收发描述符 rpmsg 端点 通道回调 / 消息收发 API mailbox 客户端 IPCC 中断收发 / 通知对端 共享内存(同一物理区域) 两侧映射同一地址,非拷贝 IPCC 中断 同一物理内存 数据走共享内存,通知走 mailbox;remoteproc 决定“何时可用”,virtio/rpmsg 决定“怎么用”

remoteproc:生命周期与资源表的“总调度”

remoteproc 是 Linux 侧管理远端处理器(这里就是 M4)的框架。它做三件事:把固件(ELF 或 raw bin)搬到 M 核可执行的内存并启动它;解析固件里携带的资源表(resource table),从中获知 vring 的地址、大小、mailbox 通道号、共享内存区域等关键信息;在需要时停止 M 核并回收资源。资源表是 A 核与 M 核之间的“契约”,两侧必须对同一份结构体布局达成一致,否则 vring 地址对不上,通信根本无法建立。

在 STM32MP 上,典型做法是在设备树中声明一个保留内存区(reserved-memory)给 M 核固件和共享内存,remoteproc 驱动通过 st,stm32-rproc 之类的兼容属性绑定,加载 /lib/firmware/ 下的固件。启动后可在 /sys/class/remoteproc/remoteproc0/state 看到 running,用 echo stop > state 即可停止。这一步是后续所有通信的前提。

virtio:共享内存上的“队列抽象”

virtio 原本用于虚拟机,OpenAMP 借用它来定义双核间的数据传输队列。核心是 vring:由描述符表(descriptor table)、可用环(avail ring)和已用环(used ring)组成,放在共享内存中。发送方把缓冲区地址写入描述符,更新 avail 环,然后通过 mailbox 通知对方;接收方处理完后更新 used 环,再通知回去。这样双方不需要拷贝数据本身,只交换“描述符索引”,效率高且天然支持批量。

需要强调的是:virtio 只负责“队列怎么排、怎么通知”,不负责“消息发给哪个应用”。它提供的是字节流式的缓冲区传递能力,是 rpmsg 的底座。

rpmsg:面向应用的消息通道

rpmsg 在 virtio 之上建立“端点(endpoint)”和“通道(channel)”概念。每个端点有一个本地地址,通道用字符串名字标识(如 rpmsg-openamp-demo-channel)。A 核侧通过名称服务(name service)广播自己支持的通道,M 核侧匹配后建立连接。Linux 用户空间看到的是 /dev/rpmsg_ctrl0 和创建出来的 /dev/rpmsg0 字符设备,直接 read/write 即可收发消息;M 核侧则通过 rpmsg_send() 和端点回调处理。

rpmsg 还定义了消息头(源地址、目的地址、长度、标志),因此一条消息最大长度受 vring 缓冲区大小限制,通常单条不超过 512 字节,长数据需要应用层分片。

mailbox 与共享内存:通知与载体

mailbox 解决“对方怎么知道我写了数据”。在 STM32MP 上它对应 IPCC(Inter-Processor Communication Controller),每个通道可触发对侧中断。发送方更新 vring 后写 IPCC 寄存器,接收方中断服务程序读取并处理。共享内存则是所有 vring 和 rpmsg 缓冲区的物理存放地,两侧映射同一段 DDR 保留区,必须保证 cache 一致性——通常把该区域配置为 non-cacheable 或使用 dma_alloc_coherent 类接口,否则会出现“数据写了但对方读到旧值”的典型故障。

排查通信失败时,建议按依赖顺序检查:remoteproc 是否 running → 资源表中 vring 地址是否与设备树保留区一致 → mailbox 通道号两侧是否匹配 → 共享内存是否 non-cacheable。多数“收不到消息”都出在资源表或 cache 配置上。

把五个组件串起来看:remoteproc 建立运行环境并传递资源表,virtio 在共享内存上搭好队列,rpmsg 在队列上开出应用通道,mailbox 负责每次收发的跨核通知,共享内存承载全部数据。理解这条依赖链,后续调试和移植才有清晰的抓手。