Cover image for Remoteproc resource table

Remoteproc resource table

Words 8.9k
Views
Visitors

Timeline

Timeline

2026-07-04

init

This article introduces the concept and implementation of the Remoteproc resource table. Remote processor firmware images typically contain a resource table, user applications, RTOS or bare-metal code, and the OpenAMP library. The resource table is used to describe the system resources required by the remote processor (such as contiguous physical memory, peripherals) and the Virtio device configurations it supports (such as vring addresses, sizes, etc.). The article analyzes in detail the struct resource in the Linux kernel_table structure and the resource entry header fw_rsc_hdr, and explains how the Remoteproc framework finds and registers Virtio devices through the resource table. Finally, taking STM32MP15C as an example, it introduces rsc_table's specific implementation, including the definition of the resource table, the initialization process, and its coordination with the reserved-memory node in the device tree.

Reference code:

STM32CubeMP1 MPU Firmware Package

  • STM32MP157C-EV1 RevC
  • STM32MP157C-DK2 RevC

Remote processor firmware image

A remote processor firmware image generally includes:

  • Resource table (resource_table)
  • User application
  • RTOS or bare metal (Bare Metal, abbreviated as BM) related code
  • The OpenAMP library.

After the main processor loads the remote processor’s firmware into the remote processor’s core, it decodes the firmware image to obtain the associated resources and reserves memory for the firmware code and data segments. After starting the remote processor, the main processor creates an RPMsg channel, which is used for remote communication.

remoteproc_image.drawio
remoteproc_image.drawio

The resources of the remote processor include:

  • System resources required by the remote processor before power-up, such as contiguous physical memory allocated to the remote processor, and peripherals allocated to the remote processor (these devices can be reserved or unused). These resources are configured instm32mp157-m4-srm.dtsithe resource manager of the device tree file (i.e.,m4_system_resourcesnode).
  • In addition to system resources, the remote processor will have a resource table (resource_table), the resource table may also contain resource entries that publish the functions supported by the remote processor or existing configurations, such as vring addresses, vring sizes, etc. in Virtio devices.

Only after all resource requirements are met (M-core configuration requirements, the resource table will require Linux to allocate corresponding resources, etc.) will Remoteproc start the device.

Resource Table

Under the kernel sourceinclude/linux/remoteproc.hfile, find the following structure resource_table, which is the firmware resource table header:

The struct resource_table defined by Linux

123456789101112131415161718192021222324252627282930313233
/** * 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 * * A resource table is essentially a list of system resources required * by the remote processor. It may also include configuration entries. * If needed, the remote processor firmware should contain this table * as a dedicated ".resource_table" ELF section. * * Some resources entries are mere announcements, where the host is informed * of specific remoteproc configuration. Other entries require the host to * do something (e.g. allocate a system resource). Sometimes a negotiation * is expected, where the firmware requests a resource, and once allocated, * the host should provide back its details (e.g. address of an allocated * memory region). * * 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. * * Immediately following this header are the resource entries themselves, * each of which begins with a resource entry header (as described below). */struct resource_table {	u32 ver;	u32 num;	u32 reserved[2];	u32 offset[];} __packed;

Following this firmware resource table header are the resource entries themselves. Each entry begins withstruct fw_rsc_hdra header. The content of the entry itself follows this header and is parsed according to the resource type. The following is the beginning of the resource entry header:

struct fw_rsc_hdr

12345678910111213
/** * 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[];} __packed;

where the resource type is defined as follows:

12345678910111213141516171819202122232425262728293031
/** * 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 of standard resources * @RSC_VENDOR_START:	start of the vendor specific resource types range * @RSC_VENDOR_END:	end of the vendor specific resource types range * * For more details regarding a specific resource type, please see its * dedicated structure below. * * 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,};

The above values are used asrproc_handle_rsc()function (inremoteproc_internal.hfile) lookup table index.

When registering a new remote processor, the Remoteproc framework will look up its resource table and register the Virtio devices it supports. The firmware should provide Remoteproc information about the Virtio devices it supports and their configuration,RSC_VDEVThe resource entry should specify the Virtio device ID (such asvirtio_ids.h), Virtio features, Virtio configuration space, vrings information, etc. We can see from the Linux file system/sys/kernel/debug/remoteproc/remoteproc0/resource_tablefile to get RSC_VDEV information. (That is, throughrproc_handle_rsc()function first looks up the relevant attributes, then goes toRSC_VDEVto find the specific configuration of that attribute.)

struct fw_rsc_carveout

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
/** * struct fw_rsc_carveout - physically contiguous memory request * @da: device address * @pa: physical address * @len: length (in bytes) * @flags: iommu protection flags * @reserved: reserved (must be zero) * @name: human-readable name of the requested memory region * * This resource entry requests the host to allocate a physically contiguous * memory region. * * These request entries should precede other firmware resource entries, * as other entries might request placing other data objects inside * these memory regions (e.g. data/code segments, trace resource entries, ...). * * Allocating memory this way helps utilizing the reserved physical memory * (e.g. CMA) more efficiently, and also minimizes the number of TLB entries * needed to map it (in case @rproc is using an IOMMU). Reducing the TLB * pressure is important; it may have a substantial impact on performance. * * If the firmware is compiled with static addresses, then @da should specify * the expected device address of this memory region. If @da is set to * FW_RSC_ADDR_ANY, then the host will dynamically allocate it, and then * overwrite @da with the dynamically allocated address. * * We will always use @da to negotiate the device addresses, even if it * isn't using an iommu. In that case, though, it will obviously contain * physical addresses. * * Some remote processors needs to know the allocated physical address * even if they do use an iommu. This is needed, e.g., if they control * hardware accelerators which access the physical memory directly (this * is the case with OMAP4 for instance). In that case, the host will * overwrite @pa with the dynamically allocated physical address. * Generally we don't want to expose physical addresses if we don't have to * (remote processors are generally _not_ trusted), so we might want to * change this to happen _only_ when explicitly required by the hardware. * * @flags is used to provide IOMMU protection flags, and @name should * (optionally) contain a human readable name of this carveout region * (mainly for debugging purposes). */struct fw_rsc_carveout {	u32 da;	u32 pa;	u32 len;	u32 flags;	u32 reserved;	u8 name[32];} __packed;

struct fw_rsc_devmem

12345678910111213141516171819202122232425262728293031323334353637
/** * struct fw_rsc_devmem - iommu mapping request * @da: device address * @pa: physical address * @len: length (in bytes) * @flags: iommu protection flags * @reserved: reserved (must be zero) * @name: human-readable name of the requested region to be mapped * * This resource entry requests the host to iommu map a physically contiguous * memory region. This is needed in case the remote processor requires * access to certain memory-based peripherals; _never_ use it to access * regular memory. * * This is obviously only needed if the remote processor is accessing memory * via an iommu. * * @da should specify the required device address, @pa should specify * the physical address we want to map, @len should specify the size of * the mapping and @flags is the IOMMU protection flags. As always, @name may * (optionally) contain a human readable name of this mapping (mainly for * debugging purposes). * * Note: at this point we just "trust" those devmem entries to contain valid * physical addresses, but this isn't safe and will be changed: eventually we * want remoteproc implementations to provide us ranges of physical addresses * the firmware is allowed to request, and not allow firmwares to request * access to physical addresses that are outside those ranges. */struct fw_rsc_devmem {	u32 da;	u32 pa;	u32 len;	u32 flags;	u32 reserved;	u8 name[32];} __packed;

struct fw_rsc_trace

12345678910111213141516171819202122
/** * struct fw_rsc_trace - trace buffer declaration * @da: device address * @len: length (in bytes) * @reserved: reserved (must be zero) * @name: human-readable name of the trace buffer * * This resource entry provides the host information about a trace buffer * into which the remote processor will write log messages. * * @da specifies the device address of the buffer, @len specifies * its size, and @name may contain a human readable name of the trace buffer. * * After booting the remote processor, the trace buffers are exposed to the * user via debugfs entries (called trace0, trace1, etc..). */struct fw_rsc_trace {	u32 da;	u32 len;	u32 reserved;	u8 name[32];} __packed;

struct fw_rsc_vdev_vring

123456789101112131415161718192021222324
/** * struct fw_rsc_vdev_vring - vring descriptor entry * @da: device address * @align: the alignment between the consumer and producer parts of the vring * @num: num of buffers supported by this vring (must be power of two) * @notifyid is a unique rproc-wide notify index for this vring. This notify * index is used when kicking a remote processor, to let it know that this * vring is triggered. * @pa: physical address * * This descriptor is not a resource entry by itself; it is part of the * vdev resource type (see below). * * Note that @da should either contain the device address where * the remote processor is expecting the vring, or indicate that * dynamically allocation of the vring's device address is supported. */struct fw_rsc_vdev_vring {	u32 da;	u32 align;	u32 num;	u32 notifyid;	u32 pa;} __packed;

struct fw_rsc_vdev

12345678910111213141516171819202122232425262728293031323334353637383940414243444546
/** * struct fw_rsc_vdev - virtio device header * @id: virtio device id (as in virtio_ids.h) * @notifyid is a unique rproc-wide notify index for this vdev. This notify * index is used when kicking a remote processor, to let it know that the * status/features of this vdev have changes. * @dfeatures specifies the virtio device features supported by the firmware * @gfeatures is a place holder used by the host to write back the * negotiated features that are supported by both sides. * @config_len is the size of the virtio config space of this vdev. The config * space lies in the resource table immediate after this vdev header. * @status is a place holder where the host will indicate its virtio progress. * @num_of_vrings indicates how many vrings are described in this vdev header * @reserved: reserved (must be zero) * @vring is an array of @num_of_vrings entries of 'struct fw_rsc_vdev_vring'. * * This resource is a virtio device header: it provides information about * the vdev, and is then used by the host and its peer remote processors * to negotiate and share certain virtio properties. * * By providing this resource entry, the firmware essentially asks remoteproc * to statically allocate a vdev upon registration of the rproc (dynamic vdev * allocation is not yet supported). * * Note: unlike virtualization systems, the term 'host' here means * the Linux side which is running remoteproc to control the remote * processors. We use the name 'gfeatures' to comply with virtio's terms, * though there isn't really any virtualized guest OS here: it's the host * which is responsible for negotiating the final features. * Yeah, it's a bit confusing. * * Note: immediately following this structure is the virtio config space for * this vdev (which is specific to the vdev; for more info, read the virtio * spec). the size of the config space is specified by @config_len. */struct fw_rsc_vdev {	u32 id;	u32 notifyid;	u32 dfeatures;	u32 gfeatures;	u32 config_len;	u8 status;	u8 num_of_vrings;	u8 reserved[2];	struct fw_rsc_vdev_vring vring[];} __packed;

struct rproc_mem_entry

1234567891011121314151617181920212223242526272829
/** * struct rproc_mem_entry - memory entry descriptor * @va:	virtual address * @dma: dma address * @len: length, in bytes * @da: device address * @release: release associated memory * @priv: associated data * @name: associated memory region name (optional) * @node: list node * @rsc_offset: offset in resource table * @flags: iommu protection flags * @of_resm_idx: reserved memory phandle index * @alloc: specific memory allocator function */struct rproc_mem_entry {	void *va;	dma_addr_t dma;	size_t len;	u32 da;	void *priv;	char name[32];	struct list_head node;	u32 rsc_offset;	u32 flags;	u32 of_resm_idx;	int (*alloc)(struct rproc *rproc, struct rproc_mem_entry *mem);	int (*release)(struct rproc *rproc, struct rproc_mem_entry *mem);};

STM32MP15C’s rsc_table implementation

WithSTMicroelectronics/STM32CubeMP1of the repositorySTM32MP157C-EV1as an example:

STM32CubeMP1/Projects/STM32MP157C-EV1/Applications/OpenAMP/OpenAMP_Dynamic_ResMgr/Src/rsc_table.c

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
/********************************************************************************  * @file    rsc_table.c  * @author  MCD Application Team  * @brief   Resource table  *  *   This file provides a default resource table requested by remote proc to  *  load the elf file. It also allows to add debug trace using a shared buffer.  *  ******************************************************************************  * @attention  *  * Copyright (c) 2021 STMicroelectronics.  * All rights reserved.  *  * This software is licensed under terms that can be found in the LICENSE file  * in the root directory of this software component.  * If no LICENSE file comes with this software, it is provided AS-IS.  *  ******************************************************************************  *//** @addtogroup RSC_TABLE  * @{  *//** @addtogroup resource_table  * @{  *//** @addtogroup resource_table_Private_Includes  * @{  */#if defined(__ICCARM__) || defined (__CC_ARM)#include <stddef.h> /* needed  for offsetof definition*/#endif#include "rsc_table.h"#include "openamp/open_amp.h"/**  * @}  *//** @addtogroup resource_table_Private_TypesDefinitions  * @{  *//**  * @}  *//** @addtogroup resource_table_Private_Defines  * @{  *//* Place resource table in special ELF section */#if defined(__GNUC__)#define __section_t(S)          __attribute__((__section__(#S)))#define __resource              __section_t(.resource_table)#endif#if defined (LINUX_RPROC_MASTER) #ifdef VIRTIO_MASTER_ONLY  #define CONST #else  #define CONST const #endif#else #define CONST#endif#define RPMSG_IPU_C0_FEATURES       1#define VRING_COUNT         		2/* VirtIO rpmsg device id */#define VIRTIO_ID_RPMSG_            7#if defined (__LOG_TRACE_IO_)extern char system_log_buf[];#endif#if defined(__GNUC__)#if !defined (__CC_ARM) && !defined (LINUX_RPROC_MASTER)/* Since GCC is not initializing the resource_table at startup, it is declared as volatile to avoid compiler optimization * for the CM4 (see resource_table_init() below) */volatile struct shared_resource_table __resource __attribute__((used))  resource_table;#elseCONST struct shared_resource_table __resource __attribute__((used)) resource_table = {#endif#elif defined(__ICCARM__)__root CONST struct shared_resource_table resource_table @ ".resource_table" = {#endif#if defined(__ICCARM__) || defined (__CC_ARM) || defined (LINUX_RPROC_MASTER)	.version = 1,#if defined (__LOG_TRACE_IO_)	.num = 2,#else	.num = 1,#endif	.reserved = {0, 0},	.offset = {		offsetof(struct shared_resource_table, vdev),		offsetof(struct shared_resource_table, cm_trace),	},	/* Virtio device entry */	.vdev= {		RSC_VDEV, VIRTIO_ID_RPMSG_, 0, RPMSG_IPU_C0_FEATURES, 0, 0, 0,		VRING_COUNT, {0, 0},	},	/* Vring rsc entry - part of vdev rsc entry */	.vring0 = {VRING_TX_ADDRESS, VRING_ALIGNMENT, VRING_NUM_BUFFS, VRING0_ID, 0},	.vring1 = {VRING_RX_ADDRESS, VRING_ALIGNMENT, VRING_NUM_BUFFS, VRING1_ID, 0},#if defined (__LOG_TRACE_IO_)	.cm_trace = {		RSC_TRACE,		(uint32_t)system_log_buf, SYSTEM_TRACE_BUF_SZ, 0, "cm4_log",	},#endif} ;#endifvoid resource_table_init(int RPMsgRole, void **table_ptr, int *length){#if !defined (LINUX_RPROC_MASTER)#if defined (__GNUC__) && ! defined (__CC_ARM)#ifdef VIRTIO_MASTER_ONLY    /*     * Currently the GCC linker doesn't initialize the resource_table global variable at startup     * it is done here by the master application.     */	memset(&resource_table, '\0', sizeof(struct shared_resource_table));	resource_table.num = 1;	resource_table.version = 1;	resource_table.offset[0] = offsetof(struct shared_resource_table, vdev);	resource_table.vring0.da = VRING_TX_ADDRESS;	resource_table.vring0.align = VRING_ALIGNMENT;	resource_table.vring0.num = VRING_NUM_BUFFS;	resource_table.vring0.notifyid = VRING0_ID;	resource_table.vring1.da = VRING_RX_ADDRESS;	resource_table.vring1.align = VRING_ALIGNMENT;	resource_table.vring1.num = VRING_NUM_BUFFS;	resource_table.vring1.notifyid = VRING1_ID;	resource_table.vdev.type = RSC_VDEV;	resource_table.vdev.id = VIRTIO_ID_RPMSG_;	resource_table.vdev.num_of_vrings=VRING_COUNT;	resource_table.vdev.dfeatures = RPMSG_IPU_C0_FEATURES;#else	/* For the slave application let's wait until the resource_table is correctly initialized */	while(resource_table.vring1.da != VRING_RX_ADDRESS)	{	}#endif#endif#endif  (void)RPMsgRole;  *length = sizeof(resource_table);  *table_ptr = (void *)&resource_table;}

AndSTM32CubeMP1/Projects/STM32MP157C-EV1/Applications/OpenAMP/OpenAMP_Dynamic_ResMgr/Inc/rsc_table.hAs follows:

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
/* * Copyright (c) 2021 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * *//* This file populates resource table for BM remote * for use by the Linux Master */#ifndef RSC_TABLE_H_#define RSC_TABLE_H_#include "openamp/open_amp.h"#include "openamp_conf.h"/* Private includes ----------------------------------------------------------*//* USER CODE BEGIN Includes *//* USER CODE END Includes *//* Exported types ------------------------------------------------------------*//* USER CODE BEGIN ET *//* Resource table for the given remote */struct shared_resource_table {	unsigned int version;	unsigned int num;	unsigned int reserved[2];	unsigned int offset[NUM_RESOURCE_ENTRIES];	/* text carveout entry */	/* rpmsg vdev entry */	struct fw_rsc_vdev vdev;	struct fw_rsc_vdev_vring vring0;	struct fw_rsc_vdev_vring vring1;	struct fw_rsc_trace cm_trace;};/* USER CODE END ET *//* Exported constants --------------------------------------------------------*//* USER CODE BEGIN EC *//* USER CODE END EC *//* Private defines -----------------------------------------------------------*//* USER CODE BEGIN Private defines *//* USER CODE END  Private defines *//* Exported macro ------------------------------------------------------------*//* USER CODE BEGIN EM *//* USER CODE END EM *//* Exported functions prototypes ---------------------------------------------*//* USER CODE BEGIN EFP *//* USER CODE END EFP */void resource_table_init(int RPMsgRole, void **table_ptr, int *length);#endif /* RSC_TABLE_H_ */

attribute marker

1
volatile struct shared_resource_table __resource __attribute__((used)) resource_table;
  • __resource = __attribute__((section(".resource_table")))

    • puts the entire resource_table variable into.resource_tablethe ELF section.
    • This is exactly the Linux siderproc_elf_load_rsc_table()/rproc_elf_find_loaded_rsc_table() section name it looks up — when A7 loads the firmware ELF, it locates the resource table by section name.
  • resource_table_init() Hand the address and length of this table to the OpenAMP middleware, which will be used by the M4’s virtio/rpmsg stack later.

struct shared_resource_table

STM32CubeMP1/Projects/STM32MP157C-EV1/Applications/OpenAMP/OpenAMP_Dynamic_ResMgr/Inc/rsc_table.h

12345678910
struct shared_resource_table {    unsigned int version;    unsigned int num;    unsigned int reserved[2];    unsigned int offset[NUM_RESOURCE_ENTRIES];   // NUM_RESOURCE_ENTRIES = 2    struct fw_rsc_vdev       vdev;    struct fw_rsc_vdev_vring vring0;    struct fw_rsc_vdev_vring vring1;    struct fw_rsc_trace      cm_trace;};

struct fw_rsc_vdev

STM32CubeMP1/Middlewares/Third_Party/OpenAMP/open-amp/lib/include/openamp/remoteproc.h

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748
/** * struct fw_rsc_vdev - virtio device header * @id: virtio device id (as in virtio_ids.h) * @notifyid is a unique rproc-wide notify index for this vdev. This notify * index is used when kicking a remote remoteproc, to let it know that the * status/features of this vdev have changes. * @dfeatures specifies the virtio device features supported by the firmware * @gfeatures is a place holder used by the host to write back the * negotiated features that are supported by both sides. * @config_len is the size of the virtio config space of this vdev. The config * space lies in the resource table immediate after this vdev header. * @status is a place holder where the host will indicate its virtio progress. * @num_of_vrings indicates how many vrings are described in this vdev header * @reserved: reserved (must be zero) * @vring is an array of @num_of_vrings entries of 'struct fw_rsc_vdev_vring'. * * This resource is a virtio device header: it provides information about * the vdev, and is then used by the host and its peer remote remoteprocs * to negotiate and share certain virtio properties. * * By providing this resource entry, the firmware essentially asks remoteproc * to statically allocate a vdev upon registration of the rproc (dynamic vdev * allocation is not yet supported). * * Note: unlike virtualization systems, the term 'host' here means * the Linux side which is running remoteproc to control the remote * remoteprocs. We use the name 'gfeatures' to comply with virtio's terms, * though there isn't really any virtualized guest OS here: it's the host * which is responsible for negotiating the final features. * Yeah, it's a bit confusing. * * Note: immediately following this structure is the virtio config space for * this vdev (which is specific to the vdev; for more info, read the virtio * spec). the size of the config space is specified by @config_len. */METAL_PACKED_BEGINstruct fw_rsc_vdev {	uint32_t type;         // ← ST added type at the very beginning!	uint32_t id;	uint32_t notifyid;	uint32_t dfeatures;	uint32_t gfeatures;	uint32_t config_len;	uint8_t status;	uint8_t num_of_vrings;	uint8_t reserved[2];	struct fw_rsc_vdev_vring vring[0];  // zero-length flexible array} METAL_PACKED_END;

ST’sfw_rsc_vdevfolds type into it

There are two points here:

  • STstruct fw_rsc_vdevadded type at the very front, while Linux’s struct fw_rsc_vdev has no type (type is in a separatestruct fw_rsc_hdrinside). But the two are exactly the same on the wire:
123
ST:    [vdev.type | vdev.id | ... | reserved]  ← type 在 vdev 内Linux: [hdr.type  | vdev.id | ... | reserved]  ← type 在 hdr 里,vdev 从 id 开始          byte0      byte4

ST does this so that a designated initializer can initialize the entire vdev entry (including type) at once.

  • C trick of zero-length vring[0] plus separate vring0/vring1 members

fw_rsc_The vring[0] at the end of vdev occupies 0 bytes, sostruct shared_resource_tablethe vring0 and vring1 members immediately following vdev in memory land exactly after vdev—that is, where Linux expects to find the vring array. This is equivalent to Linux’s vring[] flexible array, but ST uses separate named members for ease of initialization.

struct fw_rsc_vdev_vring

STM32CubeMP1/Middlewares/Third_Party/OpenAMP/open-amp/lib/include/openamp/remoteproc.h

12345678910111213141516171819202122232425
/** * struct fw_rsc_vdev_vring - vring descriptor entry * @da: device address * @align: the alignment between the consumer and producer parts of the vring * @num: num of buffers supported by this vring (must be power of two) * @notifyid is a unique rproc-wide notify index for this vring. This notify * index is used when kicking a remote remoteproc, to let it know that this * vring is triggered. * @reserved: reserved (must be zero) * * This descriptor is not a resource entry by itself; it is part of the * vdev resource type (see below). * * Note that @da should either contain the device address where * the remote remoteproc is expecting the vring, or indicate that * dynamically allocation of the vring's device address is supported. */METAL_PACKED_BEGINstruct fw_rsc_vdev_vring {	uint32_t da;	uint32_t align;	uint32_t num;	uint32_t notifyid;	uint32_t reserved;} METAL_PACKED_END;

struct fw_rsc_trace

STM32CubeMP1/Middlewares/Third_Party/OpenAMP/open-amp/lib/include/openamp/remoteproc.h

123456789101112131415161718192021222324
/** * struct fw_rsc_trace - trace buffer declaration * @da: device address * @len: length (in bytes) * @reserved: reserved (must be zero) * @name: human-readable name of the trace buffer * * This resource entry provides the host information about a trace buffer * into which the remote remoteproc will write log messages. * * @da specifies the device address of the buffer, @len specifies * its size, and @name may contain a human readable name of the trace buffer. * * After booting the remote remoteproc, the trace buffers are exposed to the * user via debugfs entries (called trace0, trace1, etc..). */METAL_PACKED_BEGINstruct fw_rsc_trace {	uint32_t type;	uint32_t da;	uint32_t len;	uint32_t reserved;	uint8_t name[RPROC_MAX_NAME_LEN];} METAL_PACKED_END;

rsc_table instance

STM32CubeMP1/Projects/STM32MP157C-EV1/Applications/OpenAMP/OpenAMP_Dynamic_ResMgr/Src/rsc_table.c

123456789101112131415161718192021222324252627282930313233343536373839404142434445
#if defined(__GNUC__)#if !defined (__CC_ARM) && !defined (LINUX_RPROC_MASTER)/* Since GCC is not initializing the resource_table at startup, it is declared as volatile to avoid compiler optimization * for the CM4 (see resource_table_init() below) */volatile struct shared_resource_table __resource __attribute__((used))  resource_table;#elseCONST struct shared_resource_table __resource __attribute__((used)) resource_table = {#endif#elif defined(__ICCARM__)__root CONST struct shared_resource_table resource_table @ ".resource_table" = {#endif#if defined(__ICCARM__) || defined (__CC_ARM) || defined (LINUX_RPROC_MASTER)	.version = 1,#if defined (__LOG_TRACE_IO_)	.num = 2,#else	.num = 1,#endif	.reserved = {0, 0},	.offset = {		offsetof(struct shared_resource_table, vdev),		offsetof(struct shared_resource_table, cm_trace),	},	/* Virtio device entry */	.vdev= {		RSC_VDEV, VIRTIO_ID_RPMSG_, 0, RPMSG_IPU_C0_FEATURES, 0, 0, 0,		VRING_COUNT, {0, 0},	},	/* Vring rsc entry - part of vdev rsc entry */	.vring0 = {VRING_TX_ADDRESS, VRING_ALIGNMENT, VRING_NUM_BUFFS, VRING0_ID, 0},	.vring1 = {VRING_RX_ADDRESS, VRING_ALIGNMENT, VRING_NUM_BUFFS, VRING1_ID, 0},#if defined (__LOG_TRACE_IO_)	.cm_trace = {		RSC_TRACE,		(uint32_t)system_log_buf, SYSTEM_TRACE_BUF_SZ, 0, "cm4_log",	},#endif} ;#endif

vdev

Because this project#define LINUX_RPROC_MASTERopenamp_conf.h), it takes the static initialization branch:

1234567891011
.vdev= {	RSC_VDEV,              // type        = 3       VIRTIO_ID_RPMSG_,      // id          = 7  (RPMsg device)       0,                     // notifyid    = 0  (backfilled by host)       RPMSG_IPU_C0_FEATURES, // dfeatures   = 1  (bit0 = VIRTIO_RPMSG_F_NS, name service)       0,                     // gfeatures   = 0  (host backfills the negotiated result)       0,                     // config_len  = 0  (RPMsg has no config space)       0,                     // status      = 0  (backfilled by host)	VRING_COUNT,           // num_of_vrings = 2       {0, 0},                // reserved[2]},

vring

12
.vring0 = {VRING_TX_ADDRESS, VRING_ALIGNMENT, VRING_NUM_BUFFS, VRING0_ID, 0},.vring1 = {VRING_RX_ADDRESS, VRING_ALIGNMENT, VRING_NUM_BUFFS, VRING1_ID, 0},
vring fieldsvring0 (TX)vring1 (RX)Description
da-1 (FW_RSC_ADDR_ANY)-1Device address. If it is -1, it indicates that dynamic allocation of the vring device address is supported.
align1616Alignment size. The number of alignment bytes between the vring Consumer and Producer parts.
num1616Number of buffers. The number of buffers supported by this vring (must be a power of 2).
notifyid0
(master→remote)
1
(remote→master)
Notification ID. A notification index unique within the entire remote processor (rproc) scope. When sending a signal (kick) to the remote, this ID lets it know which vring was triggered.
reserved00Reserved field. Must be 0.

Dynamic ResMgrThe core:da = -1

  • Static mode: hardcoded in firmwarevring.da = 0x10040000, Linux uses it directly.
  • Dynamic mode (STM32CubeMP1/Projects/STM32MP157C-EV1/Applications/OpenAMP/OpenAMP_Dynamic_ResMgr/): firmware writesda = -1, meaning “Linux allocates”, Linux from the DTvdev0vring0/1reservedcarveoutallocates vring memory from the pool, and backfills the real address tovring->da. The firmware declares requirements, host allocates and backfills — exactly what fw_rsc_vdev comment mentions “negotiation”.

openamp_conf.hThere are two branches:

1234567891011
#if defined LINUX_RPROC_MASTER          // ← this project takes this path    #define VRING_RX_ADDRESS  ((unsigned int)-1)   // FW_RSC_ADDR_ANY    #define VRING_TX_ADDRESS  ((unsigned int)-1)    #define VRING_ALIGNMENT   16    #define VRING_NUM_BUFFS   16#else                                   // Static allocation with M4 as master    #define VRING_RX_ADDRESS  SHM_START_ADDRESS      // Fixed address    #define VRING_TX_ADDRESS  (SHM_START_ADDRESS + 0x400)    #define VRING_ALIGNMENT   4    #define VRING_NUM_BUFFS   4#endif

cm_trace

1234567891011121314151617
#if defined (__LOG_TRACE_IO_)	.num = 2, // resource entry num#else	.num = 1,#endif...#if defined (__LOG_TRACE_IO_)	.cm_trace = {		RSC_TRACE,                // type = 2		(uint32_t)system_log_buf, // da = M4 log buffer address (note: real address, not -1)        SYSTEM_TRACE_BUF_SZ,      // len = 2048  (openamp_log.h)        0,                        // reserved        "cm4_log",                // name → host debugfs file name	},#endif

Note:

  • trace’s da is not-1
  • The trace buffer is allocated by M4 itself (system_log_buf is in M4 memory), host only reads and does not allocate. After host starts, it createsdebugfs/.../cm4_log, and what it reads is the log written by M4.

resource_table_init()

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647
void resource_table_init(int RPMsgRole, void **table_ptr, int *length){#if !defined (LINUX_RPROC_MASTER)#if defined (__GNUC__) && ! defined (__CC_ARM)#ifdef VIRTIO_MASTER_ONLY    /*     * Currently the GCC linker doesn't initialize the resource_table global variable at startup     * it is done here by the master application.     */	memset(&resource_table, '\0', sizeof(struct shared_resource_table));	resource_table.num = 1;	resource_table.version = 1;	resource_table.offset[0] = offsetof(struct shared_resource_table, vdev);	resource_table.vring0.da = VRING_TX_ADDRESS;	resource_table.vring0.align = VRING_ALIGNMENT;	resource_table.vring0.num = VRING_NUM_BUFFS;	resource_table.vring0.notifyid = VRING0_ID;	resource_table.vring1.da = VRING_RX_ADDRESS;	resource_table.vring1.align = VRING_ALIGNMENT;	resource_table.vring1.num = VRING_NUM_BUFFS;	resource_table.vring1.notifyid = VRING1_ID;	resource_table.vdev.type = RSC_VDEV;	resource_table.vdev.id = VIRTIO_ID_RPMSG_;	resource_table.vdev.num_of_vrings=VRING_COUNT;	resource_table.vdev.dfeatures = RPMSG_IPU_C0_FEATURES;#else	/* For the slave application let's wait until the resource_table is correctly initialized */	while(resource_table.vring1.da != VRING_RX_ADDRESS)	{	}#endif#endif#endif  (void)RPMsgRole;  *length = sizeof(resource_table);  *table_ptr = (void *)&resource_table;}

The preprocessor branches handle three scenarios:

ScenarioConditionbehavior
Linux as master
#ifdef LINUX_RPROC_MASTERSkip the entire#if !defined(LINUX_RPROC_MASTER)block; the table is initialized at compile time, and the function simply returns:
table_ptr = (void *)&resource_table
length = sizeof(resource_table)
GCC + M4 as masterGNUC&&!LINUX_RPROC_MASTER
&&VIRTIO_MASTER_ONLY
GCC does not initialize global variables with the section attribute at startup, somemsetAfter zeroing, fill field by field at runtime.
GCC + M4 as slaveGNUC&&!LINUX_RPROC_MASTERwhile(vring1.da != VRING_RX_ADDRESS){}Spin-wait for the master to fill in the table.

This#ifnesting is written by OpenAMP to be compatibleLinux-master/M4-master/M4-slavewith three topologies. For STM32MP157 (Linux master), the runtime part is not compiled; it actually only returns a pointer.*length = sizeof(resource_table);and*table_ptr = (void *)&resource_table;

Three types#ifThe branch indicates who fills the table for this M4 code in the three topologies.resource_table: Linux loads (static), M4 master fills at runtime, M4 slave waits to be filled.

Summary

resource_table
resource_table

Thisrsc_table.cis the “resource requirement list” submitted by the M4 firmware to Linux:

  • Declare avirtio-rpmsgdevice (id=7, 2 vrings, name-service feature)
  • a trace buffer, and set the vring address to -1 (FW_RSC_ADDR_ANY) for Linux to dynamically allocate — this is exactlyDynamic ResMgrthe meaning.

The entire table is placed in.resource_tableELF section, initialized at compile time,resource_table_init()just hands the pointer to the OpenAMP middleware.

Storage and System Resource Allocation

reserved-memory

Beforestm32mp157d-atk.dtsiUnder the device tree, you can see this code:

123456789101112131415161718192021222324252627282930313233343536373839404142
reserved-memory {	#address-cells = <1>;	#size-cells = <1>;	ranges;	mcuram2: mcuram2@10000000 {                compatible = "shared-dma-pool";                reg = <0x10000000 0x40000>;                no-map;        };         vdev0vring0: vdev0vring0@10040000 {                compatible = "shared-dma-pool";                reg = <0x10040000 0x1000>;                no-map;        };         vdev0vring1: vdev0vring1@10041000 {                compatible = "shared-dma-pool";                reg = <0x10041000 0x1000>;                no-map;        };         vdev0buffer: vdev0buffer@10042000 {                compatible = "shared-dma-pool";                reg = <0x10042000 0x4000>;                 no-map;        };         mcuram: mcuram@30000000 {                compatible = "shared-dma-pool";                reg = <0x30000000 0x40000>;                no-map;        };          retram: retram@38000000 {                compatible = "shared-dma-pool";                reg = <0x38000000 0x10000>;                no-map;        };};

reserved-memoryIndicates that the memory allocated under this node is all reserved memory,Reserved memory regions are generally used by specific drivers. They are different from the memory regions used by the Linux kernel. Generally, reserved memory functions are closely related to the Linux kernel’s DMA or CMA.

If a certain node’scompatibleattribute isshared-dma-pool, it means that the memory region of this node is used as a shared pool of DMA buffers for a group of devices. At this point, if you see in the node propertiesno-map, it means that this memory cannot be mapped by the Linux kernel as part of system memory and needs to be separated from system memory. If you see in the node propertiesreusableproperty, it means that this memory does not need to be separated from system memory. When a specific driver is not using this memory, the OS can use it.

Note that a node cannot have both the no-map and reusable properties at the same time, because they are logically contradictory.

We can see thatreserved-memoryeach child node under the node containsno-mapproperty, indicating that this memory cannot be used by the Linux kernel as system memory; it is actually reserved for the M4 system.

mcuram2

12345
mcuram2: mcuram2@10000000 {              compatible = "shared-dma-pool";              reg = <0x10000000 0x40000>;              no-map;      };

0x10000000 is the starting address of SRAM1, and the size 0x40000 is exactly 256KB. This region is the SRAM1+SRAM2 area, mainly used to store the code and data segments of the M4 firmware: SRAM1 (code) and SRAM2 (data). This can be seen from the linker script of the M4 project. However, you can also modify the linker script to redivide the address range, but note that the address range in the linker script must be consistent with the range configured in the device tree.

vdev0vring0vdev0vring1vdev0buffer

1234567891011121314151617
vdev0vring0: vdev0vring0@10040000 {        compatible = "shared-dma-pool";        reg = <0x10040000 0x1000>;        no-map;}; vdev0vring1: vdev0vring1@10041000 {        compatible = "shared-dma-pool";        reg = <0x10041000 0x1000>;        no-map;}; vdev0buffer: vdev0buffer@10042000 {        compatible = "shared-dma-pool";        reg = <0x10042000 0x4000>;         no-map;};

vdev0vring0vdev0vring1andvdev0bufferThe child nodes are exactly at SRAM3, i.e., the IPC buffer. The three nodes are allocated as follows:

  • vdev0vring0child node, 0x10040000 is the starting address of vring0, with an address length of 0x1000, i.e., 4KB.
  • vdev0vring1is the starting address of vring1, with an address length of 0x1000, i.e., 4KB. These two nodes are the vrings we mentioned earlier for sending and receiving messages.
  • vdev0bufferchild node, with a starting address of 0x10042000 and an address length of 0x4000, i.e., 16KB. This address falls within SRAM3, and this is the configured shared memory region.

vdev0vring0vdev0vring1andvdev0bufferOnly the first 24KB of SRAM3 is occupied. SRAM3 has 64KB and is not fully used, so if needed, you can also modify the device tree and linker script to use the unused addresses of SRAM3 for other functions.

mcuram

12345
mcuram: mcuram@30000000 {        compatible = "shared-dma-pool";        reg = <0x30000000 0x40000>;        no-map;}; 

mcuramchild node has a starting address of 0x30000000 and an address length of 0x40000, i.e., 256KB. This address is the SRAM1 and SRAM2 region in RAM aliases. Since the physical addresses of RAM aliases and SRAMs are the same, it is also necessary to configure the corresponding region “visible” to A7. This region corresponds to the M4 “visible”mcuram2region. Because mcuramandmcuram2have the same physical address, the functions of these two storage regions are the same. It can be said thatmcuramYesmcuram2is an alias storage region. This may be the origin of “aliases” in RAM aliases. Note that in the device tree,mcuramandmcuram2the memory segment definitions must be consistent.

retram

12345
retram: retram@38000000 {        compatible = "shared-dma-pool";        reg = <0x38000000 0x10000>;        no-map;};

retramchild node has a starting address of 0x38000000 and an address length of 0x10000, i.e., 64KB. It belongs to the RETRAM region in RAM aliases. This region corresponds to the RETRAM region in the BOOT storage area, and they share the same physical address. RETRAM is used to store the interrupt vector table of the M4 core (the interrupt vector table starts at 0x00000000). By default, the starting address of RETRAM is 0x38000000, and it is remapped to 0x00000000 to execute M4 code.

Summary

SRAM address allocation
SRAM address allocation

Child nodeAddressSizeRegion
mcuram20x10000000~0x10040000256KBSRAM1+SRAM2 visible to M4
vdev0vring00x10040000~0x100410004KBSRAM3
vdev0vring10x10041000~0x100420004KBSRAM3
vdev0buffer0x10042000~0x1004600016KBSRAM3
mcuram0x30000000~0x30040000256KBSRAM1+SRAM2 visible to A7
retram0x38000000~0x3801000064KBA7-visible RETRAM

For unified address management, A7 and M4 may have different address mappings for the physical address of the same real chip. For example, from A7’s perspective, it starts at 0x38000000, and this part is calledmcuram, while from M4’s perspective it starts at 0x10000000, and this part is calledmcuram2, but in fact these two addresses and the lengths they cover ultimately point to the same real memory chip.

m4_system_resources

stm32mp151.dtsiDevice tree file

123456789101112131415161718192021222324252627
mlahb {         compatible = "simple-bus";         #address-cells = <1>;         #size-cells = <1>;         dma-ranges = <0x00000000 0x38000000 0x10000>,                        <0x10000000 0x10000000 0x60000>,                        <0x30000000 0x30000000 0x60000>;          m4_rproc: m4@10000000 {                 compatible = "st,stm32mp1-m4";                 reg = <0x10000000 0x40000>,                        <0x30000000 0x40000>,                        <0x38000000 0x10000>;                 resets = <&scmi0_reset RST_SCMI0_MCU>;                 st,syscfg-holdboot = <&rcc 0x10C 0x1>;                 st,syscfg-tz = <&rcc 0x000 0x1>;                 st,syscfg-rsc-tbl = <&tamp 0x144 0xFFFFFFFF>;                 st,syscfg-copro-state = <&tamp 0x148 0xFFFFFFFF>;                 st,syscfg-pdds = <&pwr_mcu 0x0 0x1>;                 status = "disabled";                  m4_system_resources {                         compatible = "rproc-srm-core";                         status = "disabled";                 };         }; }; 

The above device tree node has am4_system_resourceschild node (that is, the M4 resource manager), which is used to configure the peripheral resources of the M4, in whichcompatiblethe one in the propertyrproc-srm-corewill match the kernel source’sdrivers/remoteproc/rproc_srm_core.cdriver file, the following isrproc_srm_core.cpart of the code of the file:

1234567891011121314151617
static const struct of_device_id rproc_srm_core_match[] = {         { .compatible = "rproc-srm-core", },         {}, };  MODULE_DEVICE_TABLE(of, rproc_srm_core_match);  static struct platform_driver rproc_srm_core_driver = {         .probe = rproc_srm_core_probe,         .remove = rproc_srm_core_remove,                .driver = {                 .name = "rproc-srm-core",                 .of_match_table = of_match_ptr(rproc_srm_core_match),         }, };  module_platform_driver(rproc_srm_core_driver);

After the device and driver are successfully matched, the function represented by the probe member variable of platform_driverrproc_srm_core_probe()is executed, and through this function, the rproc sub-device is registered (rproc represents a physical remote processor device, which can be said to be a peripheral).

of the kernel source codestm32mp157-m4-srm.dtsidevice tree file, as follows,&m4_rprocindicates that under the precedingm4_rprocnode, content is appended:

123456789101112131415161718192021222324252627282930313233343536373839
&m4_rproc {   m4_system_resources {     #address-cells = <1>;     #size-cells = <0>;      m4_timers2: timer@40000000 {             compatible = "rproc-srm-dev";             reg = <0x40000000 0x400>;             clocks = <&rcc TIM2_K>;             clock-names = "int";             status = "disabled";        };     /* some code omitted */      m4_adc: adc@48003000 {             compatible = "rproc-srm-dev";             reg = <0x48003000 0x400>;             clocks = <&rcc ADC12>, <&rcc ADC12_K>;             clock-names = "bus", "adc";             status = "disabled";       };      /* some code omitted */     m4_ethernet0: ethernet@5800a000 {             compatible = "rproc-srm-dev";             reg = <0x5800a000 0x2000>;             clock-names = "stmmaceth",                              "mac-clk-tx",                              "mac-clk-rx",                              "ethstp",                              "syscfg-clk";             clocks = <&rcc ETHMAC>,                     <&rcc ETHTX>,                      <&rcc ETHRX>,                      <&rcc ETHSTP>,                      <&rcc SYSCFG>;             status = "disabled";     };   }; };

The above codeconfigures the system resources of the M4, that is, which peripherals the M4 is configured with., howeverstatusthe properties are alldisabled, that is, although peripherals are configured, they are not enabled.

stm32mp157d-atk.dtsiThe file includesstm32mp157-m4-srm.dtsifiles

stm32mp157d-atk.dtsThe file also includesstm32mp157d-atk.dtsifiles

so you can directly instm32mp157d-atk.dtsiorstm32mp157d-atk.dtsIn the device tree file, select and enable a peripheral for the M4.

**M4 and A7 share some peripherals.**For example, GPIO is a shared resource. If this GPIO is not muxed for other functions and is simply used as a normal I/O, then both A7 and M4 can access these resources. For example,stm32mp157d-atk.dtsiunder it, there are a buzzer and two LED nodes configured, which use the GPIO function. These nodes are for A7, but M4 can also use them:

1234567891011121314151617181920212223242526
leds {    compatible = "gpio-leds";     led1 {        label = "sys-led";        gpios = <&gpioi 0 GPIO_ACTIVE_LOW>;        linux,default-trigger = "heartbeat";        default-state = "on";        status = "okay";    };     led2 {        label = "user-led";        gpios = <&gpiof 3 GPIO_ACTIVE_LOW>;        linux,default-trigger = "none";        default-state = "on";        status = "okay";    };     beep {        label = "beep";        gpios = <&gpioc 7 GPIO_ACTIVE_LOW>;        default-state = "off";    }; };

If a peripheral has exclusive functionality, that is, it can only be used by A7 alone or by M4 alone, then if both A7 and M4 use the peripheral together, there will be resource contention, and one side will malfunction (the main processor has a certain priority, and generally the coprocessor side malfunctions). For example, if A7 and M4 both occupy ADC1 to collect data, the data collected on the M4 side will be inaccurate, and it may fail to collect data and display 0.

For exclusive peripherals:

  • If A7 is to use this peripheral, the corresponding peripheral node for A7 must be configured in the device tree.
  • If this peripheral is to be used by M4, there is no need to configure the M4-related node in the device tree; you only need to configure the peripheral in the firmware (that is, configure it in the bare-metal program). After A7 loads and starts the firmware, M4 can use the peripheral.

If a peripheral node corresponding to A7 has already been configured in the device tree, and M4 wants to use that peripheral, it is best to comment out the related node occupied by A7 in the device tree. For example,stm32mp157d-atk.dtsiunder the device tree, there are the following nodes:

123456789101112131415161718192021222324
adc1_in6_pins_b: adc1-in6 {    pins {        pinmux = <STM32_PINMUX('A', 5, ANALOG)>;    }; };  &adc {   /* ADC1 & ADC2 common resources */   pinctrl-names = "default";   pinctrl-0 = <&adc1_in6_pins_b>;       vdd-supply = <&vdd>;       vdda-supply = <&vdd>;       vref-supply = <&vdd>;    status = "okay";    adc1: adc@0 {       /* private resources for ADC1 */       st,adc-channels = <19>;       st,min-sample-time-nsecs = <10000>;       status = "okay";   }; }; 

The above code snippet indicates that ADC1 is allocated to A7. If M4 wants to use it, this code does not need to be commented out. As long as A7 does not operate ADC1 after the Linux system is running, then after loading and running the M4 firmware (ADC1 has already been configured in the firmware), M4 can use ADC1 to collect data.

However, if A7 operates ADC1 at this time, the data collected by ADC1 on the M4 side will be inaccurate. Therefore, it is recommended to comment it out:

12345678910111213141516171819202122232425262728293031
/*     adc1_in6_pins_b: adc1-in6 {          pins {              pinmux = <STM32_PINMUX('A', 5, ANALOG)>;          };      }; */ /* &adc {   //  * ADC1 & ADC2 common resources *     pinctrl-names = "default";     pinctrl-0 = <&adc1_in6_pins_b>;         vdd-supply = <&vdd>;         vdda-supply = <&vdd>;         vref-supply = <&vdd>;      status = "okay";      adc1: adc@0 {     //    * private resources for ADC1 *         st,adc-channels = <19>;         st,min-sample-time-nsecs = <10000>;         status = "okay";     }; }; */ &m4_adc {         vref-supply = <&vrefbuf>;         status = "okay";            /* Enable M4's ADC */ };

That is, comment out the ADC1 part occupied by A7. Later,&m4_adcthe node part is manually added. This part can be added or not, but according to ST’s standard, it is best to add it. The device treestm32mp157c-dk2-m4-examples.dtsis a template file. When modifying the device tree, you can refer to the template file. The above modified&m4_adcnode is written by referring to this file.

After modifying the device tree, execute the following command to recompile the device tree:

1
make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabihf- dtbs

Then copy the compiledstm32mp157d-atk.dtbfile to the development board file system’s/bootdirectory, replacing the previous device tree binary file. Then execute the sync command to synchronize the cache, then restart the development board. After re-entering the Linux operating system, A7 can no longer operate ADC1. After loading and starting the M4 firmware, M4 can access ADC1 alone.

Linker Script

Linker script syntax

Entry address

12
/* Entry Point */ENTRY(Reset_Handler)

ENTRY(SYMBOL)Indicates that the value of the symbol SYMBOL is set to the entry address, that is, the address of the first instruction executed by the program.

Memory region definition

By default, the linker can allocate storage regions at arbitrary locations for sections. The MEMORY command can be used to describe which memory regions can be used by the linker and which should be avoided. A linker script may contain at most one MEMORY command.

123456
MEMORY {    /* Name, attributes, origin, length */     NAME [(ATTR)] : ORIGIN = ORIGIN, LENGTH = LEN} 
  • NAME is the name used to reference the memory region in the linker script; each memory region has a unique name.

  • ORIGIN is the start address.

  • LENGTH is the length of the region.

  • ATTROptional attribute string, used to restrict which input sections can be placed in this region. Supported characters include:

    • r: read-only section
    • w: read/write section
    • x: executable section
    • a: allocatable section
    • i or l: initialized section
    • !: invert attribute (indicates placing sections that do not satisfy any attribute after this character)

Usage: Once a memory region is defined, it can be used in section descriptions.>regionto instruct the linker to place a specific output section into that region.

1234567
SECTIONS{    .text :     {        *(.text)    } > mem}

The SECTIONS command is a very important command in the linker script. Its function is:to tell the linker how to map the sections of input files to the sections of the output file, and how to place output sections into the address space.

Like the MEMORY command, a linker script can contain only one SECTIONS command. If there is no SECTIONS command in the entire linker script, the linker combines all input sections with the same name into one output section, and the order of the input sections is the order in which they are discovered by the linker.

1234567
SECTIONS { 	.text : 	{ 		start.o (.text) 6 *(.text*) 	} >region }

Some common usages:

  • . = ALIGN(4): indicates 4-byte address alignment. That is, the start address of the section must be divisible by 4. Common examples are ALIGN(4) or ALIGN(8), i.e., 4-byte or 8-byte alignment.

  • PROVIDEandPROVIDE_HIDDENKeyword: indicates defining a symbol in the linker script file. This symbol is not defined by the object file, but is referenced by the object file.

  • KEEP()KEEP()The function is that when the linker’s–gc-sectionsgarbage collection option is enabled, this part cannot be collected. For example,KEEP(*(.text))means that not all.textSections are treated as garbage and collected.

  • /DISCARD/: is a special section name. If this section name is used as output, all matching sections will be discarded.

For more linker script syntax, refer to this site:

stm32mp15xx_m4.ld

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
/***********************************************************************************File        : LinkerScript.ld****Abstract    : Linker script for STM32MP1 series****Set heap size, stack size and stack location according**                to application requirements.****                Set memory bank area and size if external memory is used.****  Target      : STMicroelectronics STM32****  Distribution: The file is distributed “as is,” without any warranty**of any kind.********************************************************************************* @attention **<h2><center>&copy; Copyright (c) 2019 STMicroelectronics. ** All rights reserved.</center></h2>**** This software component is licensed by ST under BSD 3-Clause license,**the "License"; You may not use this file except in compliance with the ** License. You may obtain a copy of the License at:**opensource.org/licenses/BSD-3-Clause**********************************************************************************//* Entry Point */ENTRY(Reset_Handler)/* Highest address of the user mode stack */_estack = 0x10040000;    /* end of RAM */_Min_Heap_Size = 0x200;      /* required amount of heap  */_Min_Stack_Size = 0x400; /* required amount of stack *//* Memories definition */MEMORY{  m_interrupts (RX)  : ORIGIN = 0x00000000, LENGTH = 0x00000298  m_text       (RX)  : ORIGIN = 0x10000000, LENGTH = 0x00020000  m_data       (RW)  : ORIGIN = 0x10020000, LENGTH = 0x00020000  m_ipc_shm    (RW)  : ORIGIN = 0x10040000, LENGTH = 0x00008000} /* Symbols needed for OpenAMP to enable rpmsg */__OPENAMP_region_start__  = ORIGIN(m_ipc_shm);__OPENAMP_region_end__ = ORIGIN(m_ipc_shm)+LENGTH(m_ipc_shm);/* Sections */SECTIONS{  /* The startup code into ROM memory */   /* Interrupt vector table, placed in RETRAM */  .isr_vector :  {    . = ALIGN(4);    KEEP(*(.isr_vector)) /* Startup code */    . = ALIGN(4);  } > m_interrupts   /* The program code and other data into ROM memory */  .text :  {    . = ALIGN(4);    *(.text)           /* .text sections (code) */    *(.text*)          /* .text* sections (code) */    *(.glue_7)         /* glue arm to thumb code */    *(.glue_7t)        /* glue thumb to arm code */    *(.eh_frame)    KEEP (*(.init))    KEEP (*(.fini))    . = ALIGN(4);    _etext = .;        /* define a global symbols at end of code */  } > m_text  /* Constant data into ROM memory*/  .rodata :  {    . = ALIGN(4);    *(.rodata)         /* .rodata sections (constants, strings, etc.) */    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */    . = ALIGN(4);  } > m_text  .ARM.extab   : {   	. = ALIGN(4);  	*(.ARM.extab* .gnu.linkonce.armextab.*)  	. = ALIGN(4);  } > m_text    .ARM : {    . = ALIGN(4);    __exidx_start = .;    *(.ARM.exidx*)    __exidx_end = .;    . = ALIGN(4);  } > m_text  .preinit_array     :  {    . = ALIGN(4);    PROVIDE_HIDDEN (__preinit_array_start = .);    KEEP (*(.preinit_array*))    PROVIDE_HIDDEN (__preinit_array_end = .);    . = ALIGN(4);  } > m_text    .init_array :  {    . = ALIGN(4);    PROVIDE_HIDDEN (__init_array_start = .);    KEEP (*(SORT(.init_array.*)))    KEEP (*(.init_array*))    PROVIDE_HIDDEN (__init_array_end = .);    . = ALIGN(4);  } > m_text    .fini_array :  {    . = ALIGN(4);    PROVIDE_HIDDEN (__fini_array_start = .);    KEEP (*(SORT(.fini_array.*)))    KEEP (*(.fini_array*))    PROVIDE_HIDDEN (__fini_array_end = .);    . = ALIGN(4);  } > m_text  /* Used by the startup to initialize data */  __DATA_ROM = .;  _sidata = LOADADDR(.data);  /* Initialized data sections */  .data :  AT(__DATA_ROM)  {    . = ALIGN(4);    _sdata = .;        /* create a global symbol at data start */    *(.data)           /* .data sections */    *(.data*)          /* .data* sections */    . = ALIGN(4);    _edata = .;        /* define a global symbol at data end */  } > m_data  __DATA_END = __DATA_ROM + (_edata - _sdata);  text_end = ORIGIN(m_text) + LENGTH(m_text);  ASSERT(__DATA_END <= text_end, "region m_text overflowed with text and data")  .resource_table :  {    . = ALIGN(4);    KEEP (*(.resource_table*))    . = ALIGN(4);  } > m_data    /* Uninitialized data section into RAM memory */  . = ALIGN(4);  .bss :  {    /* This is used by the startup in order to initialize the .bss secion */    _sbss = .;         /* define a global symbol at bss start */    __bss_start__ = _sbss;    *(.bss)    *(.bss*)    *(COMMON)    . = ALIGN(4);    _ebss = .;         /* define a global symbol at bss end */    __bss_end__ = _ebss;  } > m_data  /* User_heap_stack section, used to check that there is enough RAM left */  ._user_heap_stack :  {    . = ALIGN(8);    PROVIDE ( end = . );    PROVIDE ( _end = . );    . = . + _Min_Heap_Size;    . = . + _Min_Stack_Size;    . = ALIGN(8);  } > m_data    /* Remove information from the compiler libraries */  /DISCARD/ :  {    libc.a ( * )    libm.a ( * )    libgcc.a ( * )  }  .ARM.attributes 0 : { *(.ARM.attributes) }}

Analysis:

123456789101112131415161718192021
/* Entry Point */ENTRY(Reset_Handler)/* Highest address of the user mode stack */_estack = 0x10040000;    /* end of RAM */_Min_Heap_Size = 0x200;      /* required amount of heap  */_Min_Stack_Size = 0x400; /* required amount of stack *//* Memories definition */MEMORY{  m_interrupts (RX)  : ORIGIN = 0x00000000, LENGTH = 0x00000298  m_text       (RX)  : ORIGIN = 0x10000000, LENGTH = 0x00020000  m_data       (RW)  : ORIGIN = 0x10020000, LENGTH = 0x00020000  m_ipc_shm    (RW)  : ORIGIN = 0x10040000, LENGTH = 0x00008000} /* Symbols needed for OpenAMP to enable rpmsg */__OPENAMP_region_start__  = ORIGIN(m_ipc_shm);__OPENAMP_region_end__ = ORIGIN(m_ipc_shm)+LENGTH(m_ipc_shm);
  1. Core configuration analysis

    • Program entry: specifyReset_Handleris the program entry, this function is in the startup filestartup_stm32mp15xx.sdefined in.

    • Stack top address: sets the stack’s highest address to0x10040000. It determines the initial position of the stack pointer (SP), corresponding to the mapping boundary of the internal SRAM.

    • Stack size: specifies the minimum space. The heap size is 512B, and the stack size is 1KB

  2. Memory region (MEMORY) definition

    • m_interrupts(0x00000000 ~ 0x00000298): mapped to the RETRAM region, used to store the M4 core’s interrupt vector table.

    • m_text(0x10000000 ~ 0x10020000): corresponds to SRAM1 (128KB), used to store the code section (Code).

    • m_data(0x10020000 ~ 0x10040000): corresponds to SRAM2 (128KB), used to store the data section (Data).

The linker script places the program execution address in SRAM:

  1. SRAM allocation logic under dual-core collaboration

The total SRAM space available to the M4 is SRAM1 ~ SRAM4 (384KB total), address range0x10000000 ~ 0x1005FFFF. When allocating, note the running mode:

  • Single-core mode (only M4 runs): SRAM1 ~ SRAM4 can be fully allocated to M4.
  • Dual-core mode (A7 + M4 running)
    • SRAM1 and SRAM2: Fully allocated to M4 (stores M4 code and data).
    • SRAM3: Shared by A7 and M4. Among them,0x10040000 ~ 0x10046000by default serves asthe memory exchange area for dual-core communication (IPC Shared Memory). The specific allocation ratio needs to refer to the Device Tree configuration under Linux.
1234567891011121314151617181920212223242526272829303132333435363738394041
reserved-memory {	#address-cells = <1>;	#size-cells = <1>;	ranges;	mcuram2: mcuram2@10000000 {                compatible = "shared-dma-pool";                reg = <0x10000000 0x40000>;                no-map;        };         vdev0vring0: vdev0vring0@10040000 {                compatible = "shared-dma-pool";                reg = <0x10040000 0x1000>;                no-map;        };         vdev0vring1: vdev0vring1@10041000 {                compatible = "shared-dma-pool";                reg = <0x10041000 0x1000>;                no-map;        };         vdev0buffer: vdev0buffer@10042000 {                compatible = "shared-dma-pool";                reg = <0x10042000 0x4000>;                 no-map;        };         mcuram: mcuram@30000000 {                compatible = "shared-dma-pool";                reg = <0x30000000 0x40000>;                no-map;        };          retram: retram@38000000 {                compatible = "shared-dma-pool";                reg = <0x38000000 0x10000>;                no-map;        };};

Andm_interruptsActually, it is in RETRAM. What is the remaining SRAM4 used for?

  • If Linux OS is not run, and only M4 bare-metal programs are run, the M4 core can fully use this area, as designated by the user.

  • If running the Linux OS, under the Linux device tree, SRAM4 is by default used as DMA for Linux functions. If you want to release this area, delete and release the corresponding node under the device tree (but it is not recommended, as A7 may become abnormal).

Several SRAM regions
Several SRAM regions

Based on the above analysis, the summary is as follows:

  • If A7 is not run, and only M4 is run (M4 can run bare-metal or RTOS): SRAM1~SRAM4 can be fully allocated to M4;
  • If A7 and M4 are run simultaneously (e.g., dual-core communication): SRAM1 and SRAM2 are dedicated to M4, some addresses of SRAM3 are used by M4 and A7 together, and SRAM4 is separately configured as DMA under Linux, i.e., occupied by A7. If you want to modify the address region range in MEMORY, be sure to modify it according to the address range of the memory map table.

References

Loading comments…