dmaengine: ste_dma40: Fix event group bounds

The dev_type validation can allow values whose derived event group has no
matching physical channel pair. d40_allocate_channel() then indexes
phy_res with j + event_group * 2, and __d40_set_prio_rt() uses the same
group to select priority and realtime registers.

The physical-mode validation also compares dev_type with the number of
physical channels. However, dev_type identifies an event line: DB8500 has
eight physical channels but 64 source and 64 destination event lines. The
event group determines which physical channel pair can serve an event, so
the physical channel count is not a valid dev_type limit.

Reject dev_type values outside the hardware event-group range, remove the
incorrect physical channel count limit, and stop the physical-channel
search before a partial final channel group can index past phy_res.

Fixes: 26955c07dc ("dmaengine: ste_dma40: Amalgamate DMA source and destination channel numbers")
Reported-by: sashiko-bot@kernel.org
Closes: https://lore.kernel.org/dmaengine/20260819225008.5F9651F000E9@smtp.kernel.org/
Assisted-by: LLM
Link: https://lore.kernel.org/dmaengine/20260819225008.5F9651F000E9@smtp.kernel.org/
Signed-off-by: Linus Walleij <linusw@kernel.org>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Link: https://patch.msgid.link/20260927-dma40-fixes-v7-20-89f595e8851d@kernel.org
Signed-off-by: Vinod Koul <vkoul@kernel.org>
This commit is contained in:
Linus Walleij 2026-09-27 11:03:33 +02:00 committed by Vinod Koul
parent 57938e0c2a
commit e66a9dbe04

View File

@ -82,6 +82,10 @@ struct stedma40_platform_data {
#define D40_LCLA_LINK_PER_EVENT_GRP 128
#define D40_LCLA_END D40_LCLA_LINK_PER_EVENT_GRP
/* Number of event groups per hardware register layout */
#define D40_EVENT_GROUPS_V4A 4
#define D40_EVENT_GROUPS_V4B 5
/* Max number of logical channels per physical channel */
#define D40_MAX_LOG_CHAN_PER_PHY 32
@ -522,6 +526,7 @@ struct d40_chan {
* @high_prio_clear: the high priority clear register
* @interrupt_en: the interrupt enable register
* @interrupt_clear: the interrupt clear register
* @num_event_groups: number of supported event groups
* @il: the pointer to struct d40_interrupt_lookup
* @il_size: the size of d40_interrupt_lookup array
* @init_reg: the pointer to the struct d40_reg_val
@ -536,6 +541,7 @@ struct d40_gen_dmac {
u32 high_prio_clear;
u32 interrupt_en;
u32 interrupt_clear;
u32 num_event_groups;
struct d40_interrupt_lookup *il;
u32 il_size;
struct d40_reg_val *init_reg;
@ -1878,6 +1884,11 @@ static int d40_validate_conf(struct d40_chan *d40c,
bool is_log = conf->mode == STEDMA40_MODE_LOGICAL;
bool invalid_dev_type = conf->dev_type < 0;
if (!invalid_dev_type &&
D40_TYPE_TO_GROUP(conf->dev_type) >=
d40c->base->gen_dmac.num_event_groups)
invalid_dev_type = true;
if (!conf->dir) {
chan_err(d40c, "Invalid direction.\n");
res = -EINVAL;
@ -1894,8 +1905,7 @@ static int d40_validate_conf(struct d40_chan *d40c,
invalid_dev_type = conf->dev_type >= max_dev_type;
}
if (invalid_dev_type ||
(!is_log && conf->dev_type > d40c->base->num_phy_chans)) {
if (invalid_dev_type) {
chan_err(d40c, "Invalid device type (%d)\n", conf->dev_type);
res = -EINVAL;
}
@ -2060,8 +2070,12 @@ static int d40_allocate_channel(struct d40_chan *d40c, bool *first_phy_user)
}
}
} else
for (j = 0; j < d40c->base->num_phy_chans; j += 8) {
for (j = 0; j < d40c->base->num_phy_chans;
j += D40_GROUP_SIZE) {
int phy_num = j + event_group * 2;
if (phy_num + 1 >= num_phy_chans)
break;
for (i = phy_num; i < phy_num + 2; i++) {
if (d40_alloc_mask_set(&phys[i],
is_src,
@ -2081,8 +2095,10 @@ static int d40_allocate_channel(struct d40_chan *d40c, bool *first_phy_user)
return -EINVAL;
/* Find logical channel */
for (j = 0; j < d40c->base->num_phy_chans; j += 8) {
for (j = 0; j < d40c->base->num_phy_chans; j += D40_GROUP_SIZE) {
int phy_num = j + event_group * 2;
if (phy_num + 1 >= num_phy_chans)
break;
if (d40c->dma_cfg.use_fixed_channel) {
i = d40c->dma_cfg.phy_channel;
@ -3462,6 +3478,7 @@ static int __init d40_hw_detect_init(struct platform_device *pdev,
base->log_chans = &base->phy_chans[num_phy_chans];
if (base->plat_data->num_of_phy_chans == 14) {
base->gen_dmac.num_event_groups = D40_EVENT_GROUPS_V4B;
base->gen_dmac.backup = d40_backup_regs_v4b;
base->gen_dmac.backup_size = BACKUP_REGS_SZ_V4B;
base->gen_dmac.interrupt_en = D40_DREG_CPCMIS;
@ -3475,6 +3492,7 @@ static int __init d40_hw_detect_init(struct platform_device *pdev,
base->gen_dmac.init_reg = dma_init_reg_v4b;
base->gen_dmac.init_reg_size = ARRAY_SIZE(dma_init_reg_v4b);
} else {
base->gen_dmac.num_event_groups = D40_EVENT_GROUPS_V4A;
if (base->rev >= 3) {
base->gen_dmac.backup = d40_backup_regs_v4a;
base->gen_dmac.backup_size = BACKUP_REGS_SZ_V4A;