远程处理器框架¶
简介¶
现代片上系统(SoC)通常在非对称多处理(AMP)配置中具有异构的远程处理器设备,这些设备可能运行着不同操作系统的实例,无论是 Linux 还是任何其他类型的实时操作系统。
例如,OMAP4 具有双核 Cortex-A9、双核 Cortex-M3 和一个 C64x+ DSP。在典型配置中,双核 Cortex-A9 以 SMP 配置运行 Linux,而其他三个内核(两个 M3 内核和一个 DSP)中的每个内核都以 AMP 配置运行其自己的 RTOS 实例。
remoteproc 框架允许不同的平台/架构控制(上电、加载固件、断电)这些远程处理器,同时抽象化硬件差异,因此整个驱动程序无需重复。此外,此框架还为支持此类通信的远程处理器添加了 rpmsg virtio 设备。这样,特定于平台的 remoteproc 驱动程序只需要提供一些底层处理程序,然后所有 rpmsg 驱动程序都可以正常工作(有关基于 virtio 的 rpmsg 总线及其驱动程序的更多信息,请阅读远程处理器消息(rpmsg)框架)。现在也可以注册其他类型的 virtio 设备。固件只需要发布它们支持哪种 virtio 设备,然后 remoteproc 将添加这些设备。这使得以最小的开发成本重用现有的 virtio 驱动程序与远程处理器后端成为可能。
用户 API¶
int rproc_boot(struct rproc *rproc)
启动远程处理器(即加载其固件、上电等)。
如果远程处理器已上电,则此函数会立即返回(成功)。
成功时返回 0,否则返回相应的错误值。注意:要使用此函数,您应该已经有一个有效的 rproc 句柄。有几种方法可以干净地实现这一点(devres、pdata、remoteproc_rpmsg.c 的方式,或者,如果这种情况变得普遍,我们也可能考虑为此使用 dev_archdata)。
int rproc_shutdown(struct rproc *rproc)
关闭远程处理器(先前使用 rproc_boot() 启动)。如果 @rproc 仍然被其他用户使用,则此函数只会减少电源引用计数并退出,而不会真正关闭设备电源。
成功时返回 0,否则返回相应的错误值。每次调用 rproc_boot() 都必须(最终)伴随着调用 rproc_shutdown()。冗余地调用 rproc_shutdown() 是一个错误。
注意
我们没有减少 rproc 的引用计数,只减少电源引用计数。这意味着即使在 rproc_shutdown() 返回后,@rproc 句柄仍然有效,如果需要,用户仍然可以将其与后续的 rproc_boot() 一起使用。
struct rproc *rproc_get_by_phandle(phandle phandle)
使用设备树 phandle 查找 rproc 句柄。成功时返回 rproc 句柄,失败时返回 NULL。此函数会增加远程处理器的引用计数,因此当不再需要 rproc 时,请始终使用 rproc_put() 将其减少回来。
典型用法¶
#include <linux/remoteproc.h>
/* in case we were given a valid 'rproc' handle */
int dummy_rproc_example(struct rproc *my_rproc)
{
int ret;
/* let's power on and boot our remote processor */
ret = rproc_boot(my_rproc);
if (ret) {
/*
* something went wrong. handle it and leave.
*/
}
/*
* our remote processor is now powered on... give it some work
*/
/* let's shut it down now */
rproc_shutdown(my_rproc);
}
实现者 API¶
struct rproc *rproc_alloc(struct device *dev, const char *name,
const struct rproc_ops *ops,
const char *firmware, int len)
分配一个新的远程处理器句柄,但尚未注册它。必需的参数是底层设备、此远程处理器的名称、特定于平台的 ops 处理程序、用于启动此 rproc 的固件名称以及分配 rproc 驱动程序所需的私有数据长度(以字节为单位)。
此函数应由 rproc 实现者在远程处理器初始化期间使用。
使用此函数创建 rproc 句柄后,当准备好时,实现应调用 rproc_add() 以完成远程处理器的注册。
成功时,返回新的 rproc,失败时返回 NULL。
注意
永远不要直接释放 @rproc,即使它尚未注册。相反,当您需要回滚 rproc_alloc() 时,请使用 rproc_free()。
void rproc_free(struct rproc *rproc)
释放由 rproc_alloc 分配的 rproc 句柄。
此函数本质上是通过减少 rproc 的引用计数来回滚 rproc_alloc()。它不会直接释放 rproc;只有当没有其他对 rproc 的引用并且其引用计数现在降至零时才会发生这种情况。
int rproc_add(struct rproc *rproc)
使用 rproc_alloc() 分配后,将 @rproc 注册到 remoteproc 框架。
每当探测到新的远程处理器设备时,都会由特定于平台的 rproc 实现调用此函数。
成功时返回 0,否则返回相应的错误代码。注意:此函数会启动一个异步固件加载上下文,该上下文将查找 rproc 固件支持的 virtio 设备。
如果找到,将创建并添加这些 virtio 设备,因此,作为注册此远程处理器的结果,可能会探测其他 virtio 驱动程序。
int rproc_del(struct rproc *rproc)
回滚 rproc_add()。
当特定于平台的 rproc 实现决定删除 rproc 设备时,应调用此函数。只有在先前成功完成 rproc_add() 的调用时,才应调用此函数。
在 rproc_del() 返回后,@rproc 仍然有效,其最后一个引用计数应通过调用 rproc_free() 来减少。
成功时返回 0,如果 @rproc 无效,则返回 -EINVAL。
void rproc_report_crash(struct rproc *rproc, enum rproc_crash_type type)
报告 remoteproc 中的崩溃
每次特定于平台的 rproc 实现检测到崩溃时,都必须调用此函数。不应从非 remoteproc 驱动程序调用此函数。可以从原子/中断上下文中调用此函数。
实现回调¶
这些回调应由特定于平台的 remoteproc 驱动程序提供
/**
* struct rproc_ops - platform-specific device handlers
* @start: power on the device and boot it
* @stop: power off the device
* @kick: kick a virtqueue (virtqueue id given as a parameter)
*/
struct rproc_ops {
int (*start)(struct rproc *rproc);
int (*stop)(struct rproc *rproc);
void (*kick)(struct rproc *rproc, int vqid);
};
每个 remoteproc 实现都应至少提供 ->start 和 ->stop 处理程序。如果也需要 rpmsg/virtio 功能,则还应提供 ->kick 处理程序。
->start()
处理程序接收一个 rproc 句柄,然后应上电并启动设备(使用 rproc->priv 访问特定于平台的私有数据)。如果需要,启动地址可以在 rproc->bootaddr 中找到(remoteproc 核心在此处放置 ELF 入口点)。成功时,应返回 0,失败时返回相应的错误代码。
->stop()
处理程序接收一个 rproc 句柄并关闭设备电源。成功时,返回 0,失败时返回相应的错误代码。
->kick() 处理程序接收一个 rproc 句柄,以及一个 virtqueue 的索引,其中放置了新消息。实现应中断远程处理器,并让其知道有挂起的消息。通知远程处理器要查找的 virtqueue 的确切索引是可选的:遍历现有的 virtqueue 并查找已用环中的新缓冲区很容易(且开销不大)。
二进制固件结构¶
目前,remoteproc 支持 ELF32 和 ELF64 固件二进制文件。但是,我们希望通过此框架支持的其他平台/设备很可能会基于不同的二进制格式。
当这些用例出现时,我们将不得不将二进制格式与框架核心分离,以便我们可以支持多种二进制格式,而无需复制公共代码。
解析固件时,其各个段会根据指定的设备地址加载到内存中(如果远程处理器直接访问内存,则可能是物理地址)。
除了标准的 ELF 段之外,大多数远程处理器还会包含一个特殊部分,我们称之为“资源表”。
资源表包含远程处理器在上电之前需要的系统资源,例如分配物理连续内存,或某些片上外围设备的 iommu 映射。只有在满足资源表的所有要求后,Remotecore 才会启动设备。
除了系统资源之外,资源表还可能包含资源条目,这些条目发布了远程处理器支持的功能或配置的存在,例如跟踪缓冲区和支持的 virtio 设备(及其配置)。
资源表以以下标头开头
/**
* struct resource_table - firmware resource table header
* @ver: version number
* @num: number of resource entries
* @reserved: reserved (must be zero)
* @offset: array of offsets pointing at the various resource entries
*
* The header of the resource table, as expressed by this structure,
* contains a version number (should we need to change this format in the
* future), the number of available resource entries, and their offsets
* in the table.
*/
struct resource_table {
u32 ver;
u32 num;
u32 reserved[2];
u32 offset[0];
} __packed;
紧随此标头之后的是资源条目本身,每个条目都以以下资源条目标头开头
/**
* struct fw_rsc_hdr - firmware resource entry header
* @type: resource type
* @data: resource data
*
* Every resource entry begins with a 'struct fw_rsc_hdr' header providing
* its @type. The content of the entry itself will immediately follow
* this header, and it should be parsed according to the resource type.
*/
struct fw_rsc_hdr {
u32 type;
u8 data[0];
} __packed;
某些资源条目只是公告,其中主机被告知特定的 remoteproc 配置。其他条目要求主机执行某些操作(例如,分配系统资源)。有时会进行协商,固件会请求资源,一旦分配,主机应提供其详细信息(例如,分配的内存区域的地址)。
以下是当前支持的各种资源类型
/**
* enum fw_resource_type - types of resource entries
*
* @RSC_CARVEOUT: request for allocation of a physically contiguous
* memory region.
* @RSC_DEVMEM: request to iommu_map a memory-based peripheral.
* @RSC_TRACE: announces the availability of a trace buffer into which
* the remote processor will be writing logs.
* @RSC_VDEV: declare support for a virtio device, and serve as its
* virtio header.
* @RSC_LAST: just keep this one at the end
* @RSC_VENDOR_START: start of the vendor specific resource types range
* @RSC_VENDOR_END: end of the vendor specific resource types range
*
* Please note that these values are used as indices to the rproc_handle_rsc
* lookup table, so please keep them sane. Moreover, @RSC_LAST is used to
* check the validity of an index before the lookup table is accessed, so
* please update it as needed.
*/
enum fw_resource_type {
RSC_CARVEOUT = 0,
RSC_DEVMEM = 1,
RSC_TRACE = 2,
RSC_VDEV = 3,
RSC_LAST = 4,
RSC_VENDOR_START = 128,
RSC_VENDOR_END = 512,
};
有关特定资源类型的更多详细信息,请参阅 include/linux/remoteproc.h 中其专用的结构。
我们还预计特定于平台的资源条目会在某个时候出现。当这种情况发生时,我们可以轻松地添加一个新的 RSC_PLATFORM 类型,并将这些资源传递给特定于平台的 rproc 驱动程序进行处理。
Virtio 和 remoteproc¶
固件应提供有关其支持的 virtio 设备及其配置的 remoteproc 信息:一个 RSC_VDEV 资源条目应指定 virtio 设备 ID(如 virtio_ids.h 中所示)、virtio 特性、virtio 配置空间、vring 信息等。
当注册一个新的远程处理器时,remoteproc 框架将查找其资源表,并注册其支持的 virtio 设备。一个固件可以支持任意数量和任意类型的 virtio 设备(如果需要,单个远程处理器也可以很容易地以这种方式支持多个 rpmsg virtio 设备)。
当然,RSC_VDEV 资源条目仅适用于 virtio 设备的静态分配。动态分配也将通过 rpmsg 总线实现(类似于我们已经如何动态分配 rpmsg 通道;请阅读远程处理器消息传递 (rpmsg) 框架 中有关此内容的更多信息)。