Re: [RFC PATCH v1 3/5] hw/arm/stm32f103: instantiate and map GPIO ports

Philippe Mathieu-Daudé <[email protected]> Mon, 27 Jul 2026 09:48:52 +0200
Newsgroups org.nongnu.qemu-rust,org.nongnu.qemu-arm,org.nongnu.qemu-devel
Message-ID <[email protected]>
On 26/7/26 16:51, Jack Wang wrote:
> Create the seven GPIO ports GPIOA..GPIOG by name via qdev_new() and map
> them at their APB2 base addresses, removing the corresponding
> unimplemented-device stubs so the real devices back that range.
> 
> The Rust GPIO type is only present in a Rust-enabled build, so the ports
> are created behind an object_class_by_name() check; with --disable-rust
> the type is absent and the range is simply left unbacked.
> 
> Signed-off-by: Jack Wang <[email protected]>
> ---
>   hw/arm/Kconfig                 |  1 +
>   hw/arm/stm32f103_board.c       |  2 +-
>   hw/arm/stm32f103_soc.c         | 54 +++++++++++++++++++++++++++++-----
>   include/hw/arm/stm32f103_soc.h |  9 ++++++
>   4 files changed, 58 insertions(+), 8 deletions(-)


> +/* GPIO addresses: GPIOA, GPIOB, GPIOC, GPIOD, GPIOE, GPIOF, GPIOG */
> +static const uint32_t gpio_addr[] = {

   static const uint32_t gpio_addr[STM32F103_NUM_GPIOS] = {

> +    0x40010800, 0x40010C00, 0x40011000, 0x40011400, 0x40011800, 0x40011C00,
> +    0x40012000
> +};
> +
>   static const int usart_irq[] = { 37, 38, 39, 52, 53 };
>   static const int spi_irq[]   = { 35, 36, 51 };
>   #define ADC_IRQ 18
> @@ -199,6 +209,37 @@ static void stm32f103_soc_realize(DeviceState *dev_soc, Error **errp)
>                              qdev_get_gpio_in(DEVICE(&s->adc_irqs), i));
>       }
>   
> +    /*
> +     * GPIO ports GPIOA..GPIOG.
> +     *
> +     * These are implemented by the Rust device "stm32f1xx-gpio-rust", created
> +     * by name with qdev_new() and mapped at its APB2 base address.  On STM32F1
> +     * a GPIO port has no dedicated NVIC line of its own: pin-change interrupts
> +     * are delivered through the EXTI controller (not yet modelled), so no
> +     * sysbus_connect_irq() to the NVIC is done here.
> +     *
> +     * The GPIO port is only provided by the Rust build; when QEMU is built
> +     * with --disable-rust the type is not registered, so skip creating the
> +     * ports and leave the address range unbacked.
> +     */
> +    if (object_class_by_name(TYPE_STM32F1XX_GPIO_RUST)) {
> +        for (i = 0; i < STM32F103_NUM_GPIOS; i++) {
> +            g_autofree char *name = g_strdup_printf("gpio%c", 'a' + i);
> +            s->gpio[i] = qdev_new(TYPE_STM32F1XX_GPIO_RUST);
> +            /*
> +             * Give each port a stable QOM name ("/machine/soc/gpioa"..) so
> +             * tests and the monitor can reach it and its gpio-out lines by
> +             * path.
> +             */
> +            object_property_add_child(OBJECT(s), name, OBJECT(s->gpio[i]));
> +            busdev = SYS_BUS_DEVICE(s->gpio[i]);
> +            if (!sysbus_realize_and_unref(busdev, errp)) {
> +                return;
> +            }
> +            sysbus_mmio_map(busdev, 0, gpio_addr[i]);
> +        }
> +    }

Please keep the UNIMP regions:

       else {
          create_unimplemented_device("GPIOA",       0x40010800, 0x400);
          ...
          create_unimplemented_device("GPIOG",       0x40012000, 0x400);
       }

> +
>       /*
>        * Unimplemented peripherals -- STM32F103 devices that do not yet
>        * have QEMU models.
> @@ -231,13 +272,12 @@ static void stm32f103_soc_realize(DeviceState *dev_soc, Error **errp)
>       /* APB2 peripherals */
>       create_unimplemented_device("AFIO",        0x40010000, 0x400);
>       create_unimplemented_device("EXTI",        0x40010400, 0x400);
> -    create_unimplemented_device("GPIOA",       0x40010800, 0x400);
> -    create_unimplemented_device("GPIOB",       0x40010C00, 0x400);
> -    create_unimplemented_device("GPIOC",       0x40011000, 0x400);
> -    create_unimplemented_device("GPIOD",       0x40011400, 0x400);
> -    create_unimplemented_device("GPIOE",       0x40011800, 0x400);
> -    create_unimplemented_device("GPIOF",       0x40011C00, 0x400);
> -    create_unimplemented_device("GPIOG",       0x40012000, 0x400);
> +    /*
> +     * GPIOA..GPIOG (0x40010800..0x40012000) are real devices mapped in the
> +     * GPIO loop above when the Rust build is used, so they must NOT be
> +     * registered as unimplemented devices here: overlapping MemoryRegions at
> +     * the same address abort at runtime.
> +     */
>       create_unimplemented_device("timer[1]",    0x40012C00, 0x400);
>       create_unimplemented_device("timer[8]",    0x40013400, 0x400);
>       create_unimplemented_device("timer[9]",    0x40014C00, 0x400);
> diff --git a/include/hw/arm/stm32f103_soc.h b/include/hw/arm/stm32f103_soc.h
> index 260f3695c0..8d78360ce0 100644
> --- a/include/hw/arm/stm32f103_soc.h
> +++ b/include/hw/arm/stm32f103_soc.h
> @@ -40,6 +40,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(STM32F103State, STM32F103_SOC)
>   #define STM32F103_NUM_USARTS 5
>   #define STM32F103_NUM_SPIS 3
>   #define STM32F103_NUM_ADCS 3
> +#define STM32F103_NUM_GPIOS 7