In the STM32 RCC, there is a block of 5 "enable" registers, each of which has 32 bits; each bit determines the level of one of the 5 * 32 = 160 enable_irq output lines. The code calculates the irq to be worked on using irq_offset = ((addr - STM32_RCC_AHB1_ENR) / 4) * 32;
This assumes that the registers are all consecutive; however, there is a gap between the AHB1/2/3 registers and the APB1/2 registers, so for the APB1/2 registers we calculate a number that is 32 too high and can index off the end of the enable_irq[] array. The handling of the reset registers has an identical bug. Adjust the calculation of irq_offset to cope with the gap, and fix the case labels so accesses to the gap fall into the default LOG_UNIMP rather than being treated as if they were an actual register. Coverity CID: 1663683, 1663686 Cc: [email protected] Signed-off-by: Peter Maydell <[email protected]> --- hw/misc/stm32_rcc.c | 31 +++++++++++++++++++++++++++---- include/hw/misc/stm32_rcc.h | 6 +++++- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/hw/misc/stm32_rcc.c b/hw/misc/stm32_rcc.c index 74ea29b156..5cfb39e560 100644 --- a/hw/misc/stm32_rcc.c +++ b/hw/misc/stm32_rcc.c @@ -53,6 +53,27 @@ static uint64_t stm32_rcc_read(void *opaque, hwaddr addr, unsigned int size) return value; } +static int reg_offset_to_irq_offset(hwaddr addr) +{ + /* + * The reset and enable registers aren't all consecutive. In getting the + * irq index from the register offset, we need to account for the gap + * between the AHB regs and the APB regs. + */ + switch (addr) { + case STM32_RCC_AHB1_RSTR ... STM32_RCC_AHB3_RSTR: + return ((addr - STM32_RCC_AHB1_RSTR) / 4) * 32; + case STM32_RCC_APB1_RSTR ... STM32_RCC_APB2_RSTR: + return ((addr - STM32_RCC_APB1_RSTR) / 4) * 32 + STM32_RCC_N_AHB_IRQS; + case STM32_RCC_AHB1_ENR ... STM32_RCC_AHB3_ENR: + return ((addr - STM32_RCC_AHB1_ENR) / 4) * 32; + case STM32_RCC_APB1_ENR ... STM32_RCC_APB2_ENR: + return ((addr - STM32_RCC_APB1_ENR) / 4) * 32 + STM32_RCC_N_AHB_IRQS; + default: + g_assert_not_reached(); + } +} + static void stm32_rcc_write(void *opaque, hwaddr addr, uint64_t val64, unsigned int size) { @@ -69,11 +90,12 @@ static void stm32_rcc_write(void *opaque, hwaddr addr, } switch (addr) { - case STM32_RCC_AHB1_RSTR...STM32_RCC_APB2_RSTR: + case STM32_RCC_AHB1_RSTR ... STM32_RCC_AHB3_RSTR: + case STM32_RCC_APB1_RSTR ... STM32_RCC_APB2_RSTR: prev_value = s->regs[addr / 4]; s->regs[addr / 4] = value; - irq_offset = ((addr - STM32_RCC_AHB1_RSTR) / 4) * 32; + irq_offset = reg_offset_to_irq_offset(addr); for (int i = 0; i < 32; i++) { new_value = extract32(value, i, 1); if (extract32(prev_value, i, 1) && !new_value) { @@ -82,11 +104,12 @@ static void stm32_rcc_write(void *opaque, hwaddr addr, } } return; - case STM32_RCC_AHB1_ENR...STM32_RCC_APB2_ENR: + case STM32_RCC_AHB1_ENR ... STM32_RCC_AHB3_ENR: + case STM32_RCC_APB1_ENR ... STM32_RCC_APB2_ENR: prev_value = s->regs[addr / 4]; s->regs[addr / 4] = value; - irq_offset = ((addr - STM32_RCC_AHB1_ENR) / 4) * 32; + irq_offset = reg_offset_to_irq_offset(addr); for (int i = 0; i < 32; i++) { new_value = extract32(value, i, 1); if (!extract32(prev_value, i, 1) && new_value) { diff --git a/include/hw/misc/stm32_rcc.h b/include/hw/misc/stm32_rcc.h index 4dccacc2db..a94781bf34 100644 --- a/include/hw/misc/stm32_rcc.h +++ b/include/hw/misc/stm32_rcc.h @@ -65,7 +65,11 @@ #define STM32_RCC_NREGS ((STM32_RCC_DCKCFGR2 >> 2) + 1) #define STM32_RCC_PERIPHERAL_SIZE 0x400 -#define STM32_RCC_NIRQS (32 * 5) /* 32 bits per reg, 5 en/rst regs */ + +/* 32 bits per reg, 3 AHB regs and 2 APB regs */ +#define STM32_RCC_N_AHB_IRQS (32 * 3) +#define STM32_RCC_N_APB_IRQS (32 * 2) +#define STM32_RCC_NIRQS (STM32_RCC_N_AHB_IRQS + STM32_RCC_N_APB_IRQS) #define STM32_RCC_GPIO_IRQ_OFFSET 0 -- 2.43.0
