遠端處理器框架¶
簡介¶
現代 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 處理程式。
The ->start() 處理程式接收 rproc 控制代碼,然後應開啟裝置電源並啟動它(使用 rproc->priv 訪問特定於平臺的私有資料)。啟動地址(如果需要)可以在 rproc->bootaddr 中找到(remoteproc 核心將 ELF 入口點放在那裡)。成功時,應返回 0,失敗時,應返回相應的錯誤程式碼。
The ->stop() 處理程式接收 rproc 控制代碼並關閉裝置電源。成功時,返回 0,失敗時,返回相應的錯誤程式碼。
The ->kick() 處理程式接收 rproc 控制代碼和 virtqueue 的索引,其中放置了新訊息。實現應該中斷遠端處理器,並告知它有待處理的訊息。通知遠端處理器要查詢的確切 virtqueue 索引是可選的:遍歷現有 virtqueue 並查詢已使用環中的新緩衝區很容易(且成本不高)。
二進位制韌體結構¶
目前,remoteproc 支援 ELF32 和 ELF64 韌體二進位制檔案。然而,非常有可能的是,我們希望使用此框架支援的其他平臺/裝置將基於不同的二進位制格式。
當這些用例出現時,我們將不得不將二進位制格式與框架核心分離,以便我們可以支援多種二進位制格式,而無需複製通用程式碼。
解析韌體時,會根據指定的裝置地址將各個段載入到記憶體中(如果遠端處理器直接訪問記憶體,則可能是物理地址)。
除了標準的 ELF 段之外,大多數遠端處理器還將包含一個特殊的 section,我們稱之為“資源表”。
資源表包含遠端處理器在啟動之前所需的系統資源,例如物理連續記憶體的分配,或某些片上外圍裝置的 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¶
韌體應向 remoteproc 提供有關其支援的 virtio 裝置及其配置的資訊:RSC_VDEV 資源條目應指定 virtio 裝置 id(如 virtio_ids.h 中所示)、virtio 特性、virtio 配置空間、vrings 資訊等。
註冊新的遠端處理器時,remoteproc 框架將查詢其資源表,並註冊其支援的 virtio 裝置。韌體可以支援任意數量的 virtio 裝置,以及任何型別(如果需要,單個遠端處理器也可以輕鬆地以這種方式支援多個 rpmsg virtio 裝置)。
當然,RSC_VDEV 資源條目僅適用於 virtio 裝置的靜態分配。還將可以使用 rpmsg 匯流排進行動態分配(類似於我們已經如何動態分配 rpmsg 通道;請在遠端處理器訊息傳遞 (rpmsg) 框架中閱讀更多相關資訊)。