dmaengine: ste_dma40: Fix memcpy channel parsing

d40_of_probe() validates the memcpy-channels property against
D40_MEMCPY_MAX_CHANS, but reads the property directly into a smaller global
array. A long property can therefore overwrite adjacent data. The mutable
global also lets a later DMA40 instance replace the memcpy channel mapping
used for future allocations on an earlier instance.

Store the mapping in the per-device platform data, validate the property
against that storage, and check the property read result. The property is
required and d40_probe() always populates the platform data, so remove the
obsolete global mapping and fallback.

Fixes: a7dacb68b3 ("dmaengine: ste_dma40: Allow memcpy channels to be configured from DT")
Reported-by: sashiko-bot@kernel.org
Closes: https://lore.kernel.org/dmaengine/20260819225215.5EA2F1F000E9@smtp.kernel.org/
Assisted-by: LLM
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Linus Walleij <linusw@kernel.org>
Link: https://patch.msgid.link/20260927-dma40-fixes-v7-15-89f595e8851d@kernel.org
Signed-off-by: Vinod Koul <vkoul@kernel.org>
This commit is contained in:
Linus Walleij 2026-09-27 11:03:28 +02:00 committed by Vinod Koul
parent 9a3762b470
commit f2baf04a69

View File

@ -28,6 +28,8 @@
#include "ste_dma40.h"
#include "ste_dma40_ll.h"
#define D40_MEMCPY_MAX_CHANS 8
/**
* struct stedma40_platform_data - Configuration struct for the dma device.
*
@ -41,6 +43,7 @@
* to use SoftLLI.
* @use_esram_lcla: flag for mapping the lcla into esram region
* @num_of_memcpy_chans: The number of channels reserved for memcpy.
* @memcpy_channels: The event lines used for memcpy.
* @num_of_phy_chans: The number of physical channels implemented in HW.
* 0 means reading the number of channels from DMA HW but this is only valid
* for 'multiple of 4' channels, like 8.
@ -51,6 +54,7 @@ struct stedma40_platform_data {
int num_of_soft_lli_chans;
bool use_esram_lcla;
int num_of_memcpy_chans;
u32 memcpy_channels[D40_MEMCPY_MAX_CHANS];
int num_of_phy_chans;
};
@ -89,25 +93,6 @@ struct stedma40_platform_data {
#define D40_ALLOC_PHY BIT(30)
#define D40_ALLOC_LOG_FREE 0
#define D40_MEMCPY_MAX_CHANS 8
/* Reserved event lines for memcpy only. */
#define DB8500_DMA_MEMCPY_EV_0 51
#define DB8500_DMA_MEMCPY_EV_1 56
#define DB8500_DMA_MEMCPY_EV_2 57
#define DB8500_DMA_MEMCPY_EV_3 58
#define DB8500_DMA_MEMCPY_EV_4 59
#define DB8500_DMA_MEMCPY_EV_5 60
static int dma40_memcpy_channels[] = {
DB8500_DMA_MEMCPY_EV_0,
DB8500_DMA_MEMCPY_EV_1,
DB8500_DMA_MEMCPY_EV_2,
DB8500_DMA_MEMCPY_EV_3,
DB8500_DMA_MEMCPY_EV_4,
DB8500_DMA_MEMCPY_EV_5,
};
/* Default configuration for physical memcpy */
static const struct stedma40_chan_cfg dma40_memcpy_conf_phy = {
.mode = STEDMA40_MODE_PHYSICAL,
@ -2149,7 +2134,8 @@ static int d40_config_memcpy(struct d40_chan *d40c)
if (dma_has_cap(DMA_MEMCPY, cap) && !dma_has_cap(DMA_SLAVE, cap)) {
d40c->dma_cfg = dma40_memcpy_conf_log;
d40c->dma_cfg.dev_type = dma40_memcpy_channels[d40c->chan.chan_id];
d40c->dma_cfg.dev_type =
d40c->base->plat_data->memcpy_channels[d40c->chan.chan_id];
d40_log_cfg(&d40c->dma_cfg,
&d40c->log_def.lcsp1, &d40c->log_def.lcsp3);
@ -3429,12 +3415,7 @@ static int __init d40_hw_detect_init(struct platform_device *pdev,
num_phy_chans = min(num_phy_chans, STEDMA40_MAX_PHYS);
/* The number of channels used for memcpy */
if (plat_data->num_of_memcpy_chans)
num_memcpy_chans = plat_data->num_of_memcpy_chans;
else
num_memcpy_chans = ARRAY_SIZE(dma40_memcpy_channels);
num_memcpy_chans = min(num_memcpy_chans, D40_MEMCPY_MAX_CHANS);
num_memcpy_chans = plat_data->num_of_memcpy_chans;
num_log_chans = num_phy_chans * D40_MAX_LOG_CHAN_PER_PHY;
dev_info(dev,
@ -3683,6 +3664,7 @@ static int __init d40_of_probe(struct device *dev,
struct stedma40_platform_data *pdata;
int num_phy = 0, num_memcpy = 0, num_disabled = 0;
const __be32 *list;
int ret;
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
@ -3696,18 +3678,19 @@ static int __init d40_of_probe(struct device *dev,
list = of_get_property(np, "memcpy-channels", &num_memcpy);
num_memcpy /= sizeof(*list);
if (num_memcpy > D40_MEMCPY_MAX_CHANS || num_memcpy <= 0) {
if (num_memcpy > ARRAY_SIZE(pdata->memcpy_channels) ||
num_memcpy <= 0) {
d40_err(dev,
"Invalid number of memcpy channels specified (%d)\n",
num_memcpy);
return -EINVAL;
}
ret = of_property_read_u32_array(np, "memcpy-channels",
pdata->memcpy_channels, num_memcpy);
if (ret)
return ret;
pdata->num_of_memcpy_chans = num_memcpy;
of_property_read_u32_array(np, "memcpy-channels",
dma40_memcpy_channels,
num_memcpy);
list = of_get_property(np, "disabled-channels", &num_disabled);
num_disabled /= sizeof(*list);