diff mbox series

hw/misc/stm32l4x5_rcc: Add validation for MCOPRE and MCOSEL values

Message ID 20240618135550.3108739-1-zheyuma97@gmail.com (mailing list archive)
State New
Headers show
Series hw/misc/stm32l4x5_rcc: Add validation for MCOPRE and MCOSEL values | expand

Commit Message

Zheyu Ma June 18, 2024, 1:55 p.m. UTC
This commit adds validation checks for the MCOPRE and MCOSEL values in
the rcc_update_cfgr_register function. If the MCOPRE value exceeds
0b100 or the MCOSEL value exceeds 0b111, an error is logged and the
corresponding clock mux is disabled. This helps in identifying and
handling invalid configurations in the RCC registers.

Reproducer:
cat << EOF | qemu-system-aarch64 -display \
none -machine accel=qtest, -m 512M -machine b-l475e-iot01a -qtest \
stdio
writeq 0x40021008 0xffffffff
EOF

Signed-off-by: Zheyu Ma <zheyuma97@gmail.com>
---
 hw/misc/stm32l4x5_rcc.c | 28 ++++++++++++++++++++--------
 1 file changed, 20 insertions(+), 8 deletions(-)

Comments

Peter Maydell June 21, 2024, 12:59 p.m. UTC | #1
On Tue, 18 Jun 2024 at 14:56, Zheyu Ma <zheyuma97@gmail.com> wrote:
>
> This commit adds validation checks for the MCOPRE and MCOSEL values in
> the rcc_update_cfgr_register function. If the MCOPRE value exceeds
> 0b100 or the MCOSEL value exceeds 0b111, an error is logged and the
> corresponding clock mux is disabled. This helps in identifying and
> handling invalid configurations in the RCC registers.
>
> Reproducer:
> cat << EOF | qemu-system-aarch64 -display \
> none -machine accel=qtest, -m 512M -machine b-l475e-iot01a -qtest \
> stdio
> writeq 0x40021008 0xffffffff
> EOF
>
> Signed-off-by: Zheyu Ma <zheyuma97@gmail.com>
> ---
>  hw/misc/stm32l4x5_rcc.c | 28 ++++++++++++++++++++--------
>  1 file changed, 20 insertions(+), 8 deletions(-)


Applied to target-arm.next, thanks. (I added a line
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2356
to the commit message.)

-- PMM
Philippe Mathieu-Daudé June 21, 2024, 3:03 p.m. UTC | #2
On 18/6/24 15:55, Zheyu Ma wrote:
> This commit adds validation checks for the MCOPRE and MCOSEL values in
> the rcc_update_cfgr_register function. If the MCOPRE value exceeds
> 0b100 or the MCOSEL value exceeds 0b111, an error is logged and the
> corresponding clock mux is disabled. This helps in identifying and
> handling invalid configurations in the RCC registers.
> 
> Reproducer:
> cat << EOF | qemu-system-aarch64 -display \
> none -machine accel=qtest, -m 512M -machine b-l475e-iot01a -qtest \
> stdio
> writeq 0x40021008 0xffffffff
> EOF
> 
> Signed-off-by: Zheyu Ma <zheyuma97@gmail.com>
> ---
>   hw/misc/stm32l4x5_rcc.c | 28 ++++++++++++++++++++--------
>   1 file changed, 20 insertions(+), 8 deletions(-)
> 
> diff --git a/hw/misc/stm32l4x5_rcc.c b/hw/misc/stm32l4x5_rcc.c
> index 417bd5e85f..59d428fa66 100644
> --- a/hw/misc/stm32l4x5_rcc.c
> +++ b/hw/misc/stm32l4x5_rcc.c
> @@ -543,19 +543,31 @@ static void rcc_update_cfgr_register(Stm32l4x5RccState *s)
>       uint32_t val;
>       /* MCOPRE */
>       val = FIELD_EX32(s->cfgr, CFGR, MCOPRE);
> -    assert(val <= 0b100);
> -    clock_mux_set_factor(&s->clock_muxes[RCC_CLOCK_MUX_MCO],
> -                         1, 1 << val);
> +    if (val > 0b100) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "%s: Invalid MCOPRE value: 0x%"PRIx32"\n",
> +                      __func__, val);

How the hardware handles that? I suppose it just ignores the
value, so I'd simply:

           return;

instead of disabling the clock...

Inès, Arnaud, what do you think?

> +        clock_mux_set_enable(&s->clock_muxes[RCC_CLOCK_MUX_MCO], false);
> +    } else {
> +        clock_mux_set_factor(&s->clock_muxes[RCC_CLOCK_MUX_MCO],
> +                             1, 1 << val);
> +    }
>   
>       /* MCOSEL */
>       val = FIELD_EX32(s->cfgr, CFGR, MCOSEL);

MCOSEL is 3-bit wide, so ...

> -    assert(val <= 0b111);

... this condition is always true (and can be removed) ...

> -    if (val == 0) {
> +    if (val > 0b111) {

... and this path isn't reachable IIUC. So you added dead code.

> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "%s: Invalid MCOSEL value: 0x%"PRIx32"\n",
> +                      __func__, val);
>           clock_mux_set_enable(&s->clock_muxes[RCC_CLOCK_MUX_MCO], false);
>       } else {
> -        clock_mux_set_enable(&s->clock_muxes[RCC_CLOCK_MUX_MCO], true);
> -        clock_mux_set_source(&s->clock_muxes[RCC_CLOCK_MUX_MCO],
> -                             val - 1);
> +        if (val == 0) {
> +            clock_mux_set_enable(&s->clock_muxes[RCC_CLOCK_MUX_MCO], false);
> +        } else {
> +            clock_mux_set_enable(&s->clock_muxes[RCC_CLOCK_MUX_MCO], true);
> +            clock_mux_set_source(&s->clock_muxes[RCC_CLOCK_MUX_MCO],
> +                                 val - 1);
> +        }
>       }
>   
>       /* STOPWUCK */
Peter Maydell June 21, 2024, 3:20 p.m. UTC | #3
On Fri, 21 Jun 2024 at 16:03, Philippe Mathieu-Daudé <philmd@linaro.org> wrote:
>
> On 18/6/24 15:55, Zheyu Ma wrote:
> > This commit adds validation checks for the MCOPRE and MCOSEL values in
> > the rcc_update_cfgr_register function. If the MCOPRE value exceeds
> > 0b100 or the MCOSEL value exceeds 0b111, an error is logged and the
> > corresponding clock mux is disabled. This helps in identifying and
> > handling invalid configurations in the RCC registers.
> >
> > Reproducer:
> > cat << EOF | qemu-system-aarch64 -display \
> > none -machine accel=qtest, -m 512M -machine b-l475e-iot01a -qtest \
> > stdio
> > writeq 0x40021008 0xffffffff
> > EOF
> >
> > Signed-off-by: Zheyu Ma <zheyuma97@gmail.com>
> > ---
> >   hw/misc/stm32l4x5_rcc.c | 28 ++++++++++++++++++++--------
> >   1 file changed, 20 insertions(+), 8 deletions(-)
> >
> > diff --git a/hw/misc/stm32l4x5_rcc.c b/hw/misc/stm32l4x5_rcc.c
> > index 417bd5e85f..59d428fa66 100644
> > --- a/hw/misc/stm32l4x5_rcc.c
> > +++ b/hw/misc/stm32l4x5_rcc.c
> > @@ -543,19 +543,31 @@ static void rcc_update_cfgr_register(Stm32l4x5RccState *s)
> >       uint32_t val;
> >       /* MCOPRE */
> >       val = FIELD_EX32(s->cfgr, CFGR, MCOPRE);
> > -    assert(val <= 0b100);
> > -    clock_mux_set_factor(&s->clock_muxes[RCC_CLOCK_MUX_MCO],
> > -                         1, 1 << val);
> > +    if (val > 0b100) {
> > +        qemu_log_mask(LOG_GUEST_ERROR,
> > +                      "%s: Invalid MCOPRE value: 0x%"PRIx32"\n",
> > +                      __func__, val);
>
> How the hardware handles that? I suppose it just ignores the
> value, so I'd simply:
>
>            return;
>
> instead of disabling the clock...
>
> Inès, Arnaud, what do you think?

The datasheet just says the values are reserved. The hardware
might do anything, including behaving completely weirdly (e.g
trying to divide the clock by something more than 16 and running
into timing problems as a result). I suggested "disable" in review
on the first version of this patch (you'll definitely notice the
guest bug then ;-)).

> > +        clock_mux_set_enable(&s->clock_muxes[RCC_CLOCK_MUX_MCO], false);
> > +    } else {
> > +        clock_mux_set_factor(&s->clock_muxes[RCC_CLOCK_MUX_MCO],
> > +                             1, 1 << val);
> > +    }
> >
> >       /* MCOSEL */
> >       val = FIELD_EX32(s->cfgr, CFGR, MCOSEL);
>
> MCOSEL is 3-bit wide, so ...
>
> > -    assert(val <= 0b111);
>
> ... this condition is always true (and can be removed) ...

Yes, I noticed that. But if you look at the datasheet[*] the actual
field in the register is 4 bits wide, because on some l4x5 SoCs
bit 4 is used for something. So it seemed to me reasonable to have
the guard against this, because at some point somebody might update
the FIELD definition to match the datasheet field width.

[*] https://www.st.com/resource/en/reference_manual/rm0351-stm32l47xxx-stm32l48xxx-stm32l49xxx-and-stm32l4axxx-advanced-armbased-32bit-mcus-stmicroelectronics.pdf

thanks
-- PMM
diff mbox series

Patch

diff --git a/hw/misc/stm32l4x5_rcc.c b/hw/misc/stm32l4x5_rcc.c
index 417bd5e85f..59d428fa66 100644
--- a/hw/misc/stm32l4x5_rcc.c
+++ b/hw/misc/stm32l4x5_rcc.c
@@ -543,19 +543,31 @@  static void rcc_update_cfgr_register(Stm32l4x5RccState *s)
     uint32_t val;
     /* MCOPRE */
     val = FIELD_EX32(s->cfgr, CFGR, MCOPRE);
-    assert(val <= 0b100);
-    clock_mux_set_factor(&s->clock_muxes[RCC_CLOCK_MUX_MCO],
-                         1, 1 << val);
+    if (val > 0b100) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: Invalid MCOPRE value: 0x%"PRIx32"\n",
+                      __func__, val);
+        clock_mux_set_enable(&s->clock_muxes[RCC_CLOCK_MUX_MCO], false);
+    } else {
+        clock_mux_set_factor(&s->clock_muxes[RCC_CLOCK_MUX_MCO],
+                             1, 1 << val);
+    }
 
     /* MCOSEL */
     val = FIELD_EX32(s->cfgr, CFGR, MCOSEL);
-    assert(val <= 0b111);
-    if (val == 0) {
+    if (val > 0b111) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: Invalid MCOSEL value: 0x%"PRIx32"\n",
+                      __func__, val);
         clock_mux_set_enable(&s->clock_muxes[RCC_CLOCK_MUX_MCO], false);
     } else {
-        clock_mux_set_enable(&s->clock_muxes[RCC_CLOCK_MUX_MCO], true);
-        clock_mux_set_source(&s->clock_muxes[RCC_CLOCK_MUX_MCO],
-                             val - 1);
+        if (val == 0) {
+            clock_mux_set_enable(&s->clock_muxes[RCC_CLOCK_MUX_MCO], false);
+        } else {
+            clock_mux_set_enable(&s->clock_muxes[RCC_CLOCK_MUX_MCO], true);
+            clock_mux_set_source(&s->clock_muxes[RCC_CLOCK_MUX_MCO],
+                                 val - 1);
+        }
     }
 
     /* STOPWUCK */