@samitouri / QOSamiQemu / commits / 3c27494434

hw/misc/stm32_rcc: Correct offset-to-irq calculation

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: qemu-stable@nongnu.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-id: 20260709104832.1989240-1-peter.maydell@linaro.org

Peter Maydell committed Jul 13, 2026 at 12:34 UTC 3c2749443429a2a6b60bfa5c32964628631dfa23
2 files changed +32 -5
hw/misc/stm32_rcc.c
+27 -4
@@ -53,6 +53,27 @@ static uint64_t stm32_rcc_read(void *opaque, hwaddr addr, unsigned int size)
53 return value;
54 }
55
56 +static int reg_offset_to_irq_offset(hwaddr addr)
57 +{
58 + /*
59 + * The reset and enable registers aren't all consecutive. In getting the
60 + * irq index from the register offset, we need to account for the gap
61 + * between the AHB regs and the APB regs.
62 + */
63 + switch (addr) {
64 + case STM32_RCC_AHB1_RSTR ... STM32_RCC_AHB3_RSTR:
65 + return ((addr - STM32_RCC_AHB1_RSTR) / 4) * 32;
66 + case STM32_RCC_APB1_RSTR ... STM32_RCC_APB2_RSTR:
67 + return ((addr - STM32_RCC_APB1_RSTR) / 4) * 32 + STM32_RCC_N_AHB_IRQS;
68 + case STM32_RCC_AHB1_ENR ... STM32_RCC_AHB3_ENR:
69 + return ((addr - STM32_RCC_AHB1_ENR) / 4) * 32;
70 + case STM32_RCC_APB1_ENR ... STM32_RCC_APB2_ENR:
71 + return ((addr - STM32_RCC_APB1_ENR) / 4) * 32 + STM32_RCC_N_AHB_IRQS;
72 + default:
73 + g_assert_not_reached();
74 + }
75 +}
76 +
77 static void stm32_rcc_write(void *opaque, hwaddr addr,
78 uint64_t val64, unsigned int size)
79 {
@@ -69,11 +90,12 @@ static void stm32_rcc_write(void *opaque, hwaddr addr,
90 }
91
92 switch (addr) {
72 - case STM32_RCC_AHB1_RSTR...STM32_RCC_APB2_RSTR:
93 + case STM32_RCC_AHB1_RSTR ... STM32_RCC_AHB3_RSTR:
94 + case STM32_RCC_APB1_RSTR ... STM32_RCC_APB2_RSTR:
95 prev_value = s->regs[addr / 4];
96 s->regs[addr / 4] = value;
97
76 - irq_offset = ((addr - STM32_RCC_AHB1_RSTR) / 4) * 32;
98 + irq_offset = reg_offset_to_irq_offset(addr);
99 for (int i = 0; i < 32; i++) {
100 new_value = extract32(value, i, 1);
101 if (extract32(prev_value, i, 1) && !new_value) {
@@ -82,11 +104,12 @@ static void stm32_rcc_write(void *opaque, hwaddr addr,
104 }
105 }
106 return;
85 - case STM32_RCC_AHB1_ENR...STM32_RCC_APB2_ENR:
107 + case STM32_RCC_AHB1_ENR ... STM32_RCC_AHB3_ENR:
108 + case STM32_RCC_APB1_ENR ... STM32_RCC_APB2_ENR:
109 prev_value = s->regs[addr / 4];
110 s->regs[addr / 4] = value;
111
89 - irq_offset = ((addr - STM32_RCC_AHB1_ENR) / 4) * 32;
112 + irq_offset = reg_offset_to_irq_offset(addr);
113 for (int i = 0; i < 32; i++) {
114 new_value = extract32(value, i, 1);
115 if (!extract32(prev_value, i, 1) && new_value) {
include/hw/misc/stm32_rcc.h
+5 -1
@@ -65,7 +65,11 @@
65
66 #define STM32_RCC_NREGS ((STM32_RCC_DCKCFGR2 >> 2) + 1)
67 #define STM32_RCC_PERIPHERAL_SIZE 0x400
68 -#define STM32_RCC_NIRQS (32 * 5) /* 32 bits per reg, 5 en/rst regs */
68 +
69 +/* 32 bits per reg, 3 AHB regs and 2 APB regs */
70 +#define STM32_RCC_N_AHB_IRQS (32 * 3)
71 +#define STM32_RCC_N_APB_IRQS (32 * 2)
72 +#define STM32_RCC_NIRQS (STM32_RCC_N_AHB_IRQS + STM32_RCC_N_APB_IRQS)
73
74 #define STM32_RCC_GPIO_IRQ_OFFSET 0
75