* [PATCH v3 1/2] remoteproc: elf_loader: Create new callback for copying segments to remote cpu
2026-11-05 11:59 [PATCH v3 0/2] remoteproc: elf_loader: Make rproc_elf_load_segments() more generic Uwe Kleine-König
@ 2026-11-05 11:59 ` Uwe Kleine-König
[not found] ` <sashiko-outbox-198999@kernel.org>
2026-11-05 11:59 ` [PATCH v3 2/2] remoteproc: imx_dsp_rproc: Implement .memcpy_tova() Uwe Kleine-König
1 sibling, 1 reply; 4+ messages in thread
From: Uwe Kleine-König @ 2026-11-05 11:59 UTC (permalink / raw)
To: Bjorn Andersson, Mathieu Poirier, Wolfram Sang, Frank Li, Sascha Hauer
Cc: Pengutronix Kernel Team, Fabio Estevam, linux-remoteproc, imx,
linux-arm-kernel, linux-kernel, Peng Fan
Some devices have special needs about how an Elf segment is copied to
the remote cpu's memory space where the default implementation using
da_to_va() + memcpy_toio/memcpy and memset_io/memset isn't suitable. An
affected mainline driver is imx-dsp-rproc which includes a near-copy of
rproc_elf_load_segments() just to handle the IRAM only allowing 32 bit
accesses. On (not-yet-mainline) ADI SC846 the respective memory isn't
directly accessible at all and DMA must be used to copy the data. To
cover that usecase don't keep rproc_da_to_va() in
rproc_elf_load_segments() and let the new callback operate on the device
address.
The new callback can be used to implement just the driver specific
memory mapping + memcpy() + memset() to increase code reuse.
The patch only appears big because of indentation changes.
Reviewed-by: Peng Fan <peng.fan@nxp.com>
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
---
drivers/remoteproc/remoteproc_elf_loader.c | 64 +++++++++++++---------
include/linux/remoteproc.h | 4 ++
2 files changed, 41 insertions(+), 27 deletions(-)
diff --git a/drivers/remoteproc/remoteproc_elf_loader.c b/drivers/remoteproc/remoteproc_elf_loader.c
index da3cddbe7d4c..3c749a39e5da 100644
--- a/drivers/remoteproc/remoteproc_elf_loader.c
+++ b/drivers/remoteproc/remoteproc_elf_loader.c
@@ -227,35 +227,45 @@ int rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw)
break;
}
- /* grab the kernel address for this device address */
- ptr = rproc_da_to_va(rproc, da, memsz, &is_iomem);
- if (!ptr) {
- dev_err(dev, "bad phdr da 0x%llx mem 0x%llx\n", da,
- memsz);
- ret = -EINVAL;
- break;
- }
+ if (rproc->ops->memcpy_tova) {
+ ret = rproc->ops->memcpy_tova(rproc, da, elf_data + offset, filesz, memsz);
+ if (ret) {
+ dev_err(dev, "Failed to copy segment %d to device address 0x%llx: %pe\n",
+ i, da, ERR_PTR(ret));
+ break;
+ }
+ } else {
+ /* grab the kernel address for this device address */
+ ptr = rproc_da_to_va(rproc, da, memsz, &is_iomem);
+ if (!ptr) {
+ dev_err(dev, "bad phdr da 0x%llx mem 0x%llx\n", da,
+ memsz);
+ ret = -EINVAL;
+ break;
+ }
- /* put the segment where the remote processor expects it */
- if (filesz) {
- if (is_iomem)
- memcpy_toio((void __iomem *)ptr, elf_data + offset, filesz);
- else
- memcpy(ptr, elf_data + offset, filesz);
- }
+ /* put the segment where the remote processor expects it */
+ if (filesz) {
+ if (is_iomem)
+ memcpy_toio((void __iomem *)ptr, elf_data + offset, filesz);
+ else
+ memcpy(ptr, elf_data + offset, filesz);
+ }
- /*
- * Zero out remaining memory for this segment.
- *
- * This isn't strictly required since dma_alloc_coherent already
- * did this for us. albeit harmless, we may consider removing
- * this.
- */
- if (memsz > filesz) {
- if (is_iomem)
- memset_io((void __iomem *)(ptr + filesz), 0, memsz - filesz);
- else
- memset(ptr + filesz, 0, memsz - filesz);
+ /*
+ * Zero out remaining memory for this segment.
+ *
+ * This isn't strictly required since dma_alloc_coherent already
+ * did this for us. albeit harmless, we may consider removing
+ * this.
+ */
+ if (memsz > filesz) {
+ if (is_iomem)
+ memset_io((void __iomem *)(ptr + filesz),
+ 0, memsz - filesz);
+ else
+ memset(ptr + filesz, 0, memsz - filesz);
+ }
}
}
diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
index 00019b1895e6..e750a127cbc4 100644
--- a/include/linux/remoteproc.h
+++ b/include/linux/remoteproc.h
@@ -75,6 +75,7 @@ enum rsc_handling_status {
* @detach: detach from a device, leaving it powered up
* @kick: kick a virtqueue (virtqueue id given as a parameter)
* @da_to_va: optional platform hook to perform address translations
+ * @memcpy_tova:optional platform hook to copy data into remote memory range
* @parse_fw: parse firmware to extract information (e.g. resource table)
* @handle_rsc: optional platform hook to handle vendor resources. Should return
* RSC_HANDLED if resource was handled, RSC_IGNORED if not handled
@@ -99,6 +100,9 @@ struct rproc_ops {
int (*detach)(struct rproc *rproc);
void (*kick)(struct rproc *rproc, int vqid);
void * (*da_to_va)(struct rproc *rproc, u64 da, size_t len, bool *is_iomem);
+ int (*memcpy_tova)(struct rproc *rproc, u64 da, const void *src,
+ size_t filesz, size_t memsz);
+
int (*parse_fw)(struct rproc *rproc, const struct firmware *fw);
int (*handle_rsc)(struct rproc *rproc, u32 rsc_type, void *rsc,
int offset, int avail);
--
2.47.3
^ permalink raw reply [flat|nested] 4+ messages in thread* [PATCH v3 2/2] remoteproc: imx_dsp_rproc: Implement .memcpy_tova()
2026-11-05 11:59 [PATCH v3 0/2] remoteproc: elf_loader: Make rproc_elf_load_segments() more generic Uwe Kleine-König
2026-11-05 11:59 ` [PATCH v3 1/2] remoteproc: elf_loader: Create new callback for copying segments to remote cpu Uwe Kleine-König
@ 2026-11-05 11:59 ` Uwe Kleine-König
1 sibling, 0 replies; 4+ messages in thread
From: Uwe Kleine-König @ 2026-11-05 11:59 UTC (permalink / raw)
To: Bjorn Andersson, Mathieu Poirier, Wolfram Sang, Frank Li, Sascha Hauer
Cc: Pengutronix Kernel Team, Fabio Estevam, linux-remoteproc, imx,
linux-arm-kernel, linux-kernel, Peng Fan
This callback allows to use the core's rproc_elf_load_segments()
function instead of reimplementing it for very minor changes.
Reviewed-by: Peng Fan <peng.fan@nxp.com>
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
---
drivers/remoteproc/imx_dsp_rproc.c | 120 ++++++++---------------------
1 file changed, 30 insertions(+), 90 deletions(-)
diff --git a/drivers/remoteproc/imx_dsp_rproc.c b/drivers/remoteproc/imx_dsp_rproc.c
index 96d619daf3f5..324c10f18507 100644
--- a/drivers/remoteproc/imx_dsp_rproc.c
+++ b/drivers/remoteproc/imx_dsp_rproc.c
@@ -864,97 +864,41 @@ static int imx_dsp_rproc_memset(void *addr, u8 value, size_t size)
return 0;
}
-/*
- * imx_dsp_rproc_elf_load_segments() - load firmware segments to memory
- * @rproc: remote processor which will be booted using these fw segments
- * @fw: the ELF firmware image
- *
- * This function loads the firmware segments to memory, where the remote
- * processor expects them.
- *
- * Return: 0 on success and an appropriate error code otherwise
- */
-static int imx_dsp_rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw)
+static int imx_dsp_rproc_memcpy_tova(struct rproc *rproc, u64 da, const void *src,
+ size_t filesz, size_t memsz)
{
struct device *dev = &rproc->dev;
- const void *ehdr, *phdr;
- int i, ret = 0;
- u16 phnum;
- const u8 *elf_data = fw->data;
- u8 class = fw_elf_get_class(fw);
- u32 elf_phdr_get_size = elf_size_of_phdr(class);
+ void *ptr;
+ int ret;
- ehdr = elf_data;
- phnum = elf_hdr_get_e_phnum(class, ehdr);
- phdr = elf_data + elf_hdr_get_e_phoff(class, ehdr);
+ /* grab the kernel address for this device address */
+ ptr = rproc_da_to_va(rproc, da, memsz, NULL);
+ if (!ptr) {
+ dev_err(dev, "bad phdr da 0x%llx mem 0x%zx\n", da, memsz);
+ return -EINVAL;
+ }
- /* go through the available ELF segments */
- for (i = 0; i < phnum; i++, phdr += elf_phdr_get_size) {
- u64 da = elf_phdr_get_p_paddr(class, phdr);
- u64 memsz = elf_phdr_get_p_memsz(class, phdr);
- u64 filesz = elf_phdr_get_p_filesz(class, phdr);
- u64 offset = elf_phdr_get_p_offset(class, phdr);
- u32 type = elf_phdr_get_p_type(class, phdr);
- void *ptr;
-
- if (type != PT_LOAD || !memsz)
- continue;
-
- dev_dbg(dev, "phdr: type %d da 0x%llx memsz 0x%llx filesz 0x%llx\n",
- type, da, memsz, filesz);
-
- if (filesz > memsz) {
- dev_err(dev, "bad phdr filesz 0x%llx memsz 0x%llx\n",
- filesz, memsz);
- ret = -EINVAL;
- break;
- }
-
- if (offset + filesz > fw->size) {
- dev_err(dev, "truncated fw: need 0x%llx avail 0x%zx\n",
- offset + filesz, fw->size);
- ret = -EINVAL;
- break;
- }
-
- if (!rproc_u64_fit_in_size_t(memsz)) {
- dev_err(dev, "size (%llx) does not fit in size_t type\n",
- memsz);
- ret = -EOVERFLOW;
- break;
- }
-
- /* grab the kernel address for this device address */
- ptr = rproc_da_to_va(rproc, da, memsz, NULL);
- if (!ptr) {
- dev_err(dev, "bad phdr da 0x%llx mem 0x%llx\n", da,
- memsz);
- ret = -EINVAL;
- break;
- }
-
- /* put the segment where the remote processor expects it */
- if (filesz) {
- ret = imx_dsp_rproc_memcpy(ptr, elf_data + offset, filesz);
- if (ret) {
- dev_err(dev, "memory copy failed for da 0x%llx memsz 0x%llx\n",
- da, memsz);
- break;
- }
- }
-
- /* zero out remaining memory for this segment */
- if (memsz > filesz) {
- ret = imx_dsp_rproc_memset(ptr + filesz, 0, memsz - filesz);
- if (ret) {
- dev_err(dev, "memset failed for da 0x%llx memsz 0x%llx\n",
- da, memsz);
- break;
- }
+ /* put the segment where the remote processor expects it */
+ if (filesz) {
+ ret = imx_dsp_rproc_memcpy(ptr, src, filesz);
+ if (ret) {
+ dev_err(dev, "memory copy failed for da 0x%llx memsz 0x%zx\n",
+ da, memsz);
+ return ret;
}
}
- return ret;
+ /* zero out remaining memory for this segment */
+ if (memsz > filesz) {
+ ret = imx_dsp_rproc_memset(ptr + filesz, 0, memsz - filesz);
+ if (ret) {
+ dev_err(dev, "memset failed for da 0x%llx memsz 0x%zx\n",
+ da, memsz);
+ return ret;
+ }
+ }
+
+ return 0;
}
static int imx_dsp_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw)
@@ -969,7 +913,6 @@ static int imx_dsp_rproc_load(struct rproc *rproc, const struct firmware *fw)
struct imx_dsp_rproc *priv = rproc->priv;
const struct imx_dsp_rproc_dcfg *dsp_dcfg = priv->dsp_dcfg;
struct rproc_mem_entry *carveout;
- int ret;
/* Reset DSP if needed */
if (dsp_dcfg->reset)
@@ -985,11 +928,7 @@ static int imx_dsp_rproc_load(struct rproc *rproc, const struct firmware *fw)
}
}
- ret = imx_dsp_rproc_elf_load_segments(rproc, fw);
- if (ret)
- return ret;
-
- return 0;
+ return rproc_elf_load_segments(rproc, fw);
}
static const struct rproc_ops imx_dsp_rproc_ops = {
@@ -998,6 +937,7 @@ static const struct rproc_ops imx_dsp_rproc_ops = {
.start = imx_dsp_rproc_start,
.stop = imx_dsp_rproc_stop,
.kick = imx_dsp_rproc_kick,
+ .memcpy_tova = imx_dsp_rproc_memcpy_tova,
.load = imx_dsp_rproc_load,
.parse_fw = imx_dsp_rproc_parse_fw,
.handle_rsc = imx_dsp_rproc_handle_rsc,
--
2.47.3
^ permalink raw reply [flat|nested] 4+ messages in thread