diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2021-07-11 13:11:32 +0100 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2021-07-11 13:11:32 +0100 |
commit | 3cfcc329afd99138e654b65f6f49156fca2e8cdd (patch) | |
tree | 8d5a6393fb486352ad967fbe47f2e4dcb42927f7 | |
parent | 42e1d798a6a01817bdcf722ac27eea01531e21cd (diff) | |
parent | 05449abb1d4c5f0c69ceb3d8d03cbc75de39b646 (diff) | |
download | qemu-3cfcc329afd99138e654b65f6f49156fca2e8cdd.zip qemu-3cfcc329afd99138e654b65f6f49156fca2e8cdd.tar.gz qemu-3cfcc329afd99138e654b65f6f49156fca2e8cdd.tar.bz2 |
Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20210709' into staging
target-arm queue:
* New machine type: stm32vldiscovery
* hw/intc/arm_gicv3_cpuif: Fix virtual irq number check in icv_[dir|eoir]_write
* hw/gpio/pl061: Honour Luminary PL061 PUR and PDR registers
* virt: Fix implementation of GPIO-based powerdown/shutdown mechanism
* Correct the encoding of MDCCSR_EL0 and DBGDSCRint
* hw/intc: Improve formatting of MEMTX_ERROR guest error message
# gpg: Signature made Fri 09 Jul 2021 17:09:10 BST
# gpg: using RSA key E1A5C593CD419DE28E8315CF3C2525ED14360CDE
# gpg: issuer "peter.maydell@linaro.org"
# gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" [ultimate]
# gpg: aka "Peter Maydell <pmaydell@gmail.com>" [ultimate]
# gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" [ultimate]
# Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE
* remotes/pmaydell/tags/pull-target-arm-20210709:
hw/intc: Improve formatting of MEMTX_ERROR guest error message
target/arm: Correct the encoding of MDCCSR_EL0 and DBGDSCRint
hw/arm/stellaris: Expand comment about handling of OLED chipselect
hw/gpio/pl061: Document a shortcoming in our implementation
hw/gpio/pl061: Convert to 3-phase reset and assert GPIO lines correctly on reset
hw/arm/virt: Make PL061 GPIO lines pulled low, not high
hw/gpio/pl061: Make pullup/pulldown of outputs configurable
hw/gpio/pl061: Honour Luminary PL061 PUR and PDR registers
hw/gpio/pl061: Document the interface of this device
hw/gpio/pl061: Add tracepoints for register read and write
hw/gpio/pl061: Clean up read/write offset handling logic
hw/gpio/pl061: Convert DPRINTF to tracepoints
hw/intc/arm_gicv3_cpuif: Fix virtual irq number check in icv_[dir|eoir]_write
tests/boot-serial-test: Add STM32VLDISCOVERY board testcase
docs/system: arm: Add stm32 boards description
stm32vldiscovery: Add the STM32VLDISCOVERY Machine
stm32f100: Add the stm32f100 SoC
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r-- | MAINTAINERS | 13 | ||||
-rw-r--r-- | default-configs/devices/arm-softmmu.mak | 1 | ||||
-rw-r--r-- | docs/system/arm/stm32.rst | 66 | ||||
-rw-r--r-- | docs/system/target-arm.rst | 1 | ||||
-rw-r--r-- | hw/arm/Kconfig | 10 | ||||
-rw-r--r-- | hw/arm/meson.build | 2 | ||||
-rw-r--r-- | hw/arm/stellaris.c | 56 | ||||
-rw-r--r-- | hw/arm/stm32f100_soc.c | 182 | ||||
-rw-r--r-- | hw/arm/stm32vldiscovery.c | 66 | ||||
-rw-r--r-- | hw/arm/virt.c | 3 | ||||
-rw-r--r-- | hw/gpio/pl061.c | 345 | ||||
-rw-r--r-- | hw/gpio/trace-events | 9 | ||||
-rw-r--r-- | hw/intc/arm_gicv3_cpuif.c | 4 | ||||
-rw-r--r-- | hw/intc/arm_gicv3_redist.c | 4 | ||||
-rw-r--r-- | include/hw/arm/stm32f100_soc.h | 57 | ||||
-rw-r--r-- | target/arm/helper.c | 16 | ||||
-rw-r--r-- | tests/qtest/boot-serial-test.c | 37 |
17 files changed, 792 insertions, 80 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 6c27914..0c288d2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -893,6 +893,13 @@ F: hw/*/stellaris* F: include/hw/input/gamepad.h F: docs/system/arm/stellaris.rst +STM32VLDISCOVERY +M: Alexandre Iooss <erdnaxe@crans.org> +L: qemu-arm@nongnu.org +S: Maintained +F: hw/arm/stm32vldiscovery.c +F: docs/system/arm/stm32.rst + Versatile Express M: Peter Maydell <peter.maydell@linaro.org> L: qemu-arm@nongnu.org @@ -948,6 +955,12 @@ L: qemu-arm@nongnu.org S: Maintained F: hw/arm/virt-acpi-build.c +STM32F100 +M: Alexandre Iooss <erdnaxe@crans.org> +L: qemu-arm@nongnu.org +S: Maintained +F: hw/arm/stm32f100_soc.c + STM32F205 M: Alistair Francis <alistair@alistair23.me> M: Peter Maydell <peter.maydell@linaro.org> diff --git a/default-configs/devices/arm-softmmu.mak b/default-configs/devices/arm-softmmu.mak index 0500156..cdc0e97 100644 --- a/default-configs/devices/arm-softmmu.mak +++ b/default-configs/devices/arm-softmmu.mak @@ -18,6 +18,7 @@ CONFIG_CHEETAH=y CONFIG_SX1=y CONFIG_NSERIES=y CONFIG_STELLARIS=y +CONFIG_STM32VLDISCOVERY=y CONFIG_REALVIEW=y CONFIG_VERSATILE=y CONFIG_VEXPRESS=y diff --git a/docs/system/arm/stm32.rst b/docs/system/arm/stm32.rst new file mode 100644 index 0000000..508b92c --- /dev/null +++ b/docs/system/arm/stm32.rst @@ -0,0 +1,66 @@ +STMicroelectronics STM32 boards (``netduino2``, ``netduinoplus2``, ``stm32vldiscovery``) +======================================================================================== + +The `STM32`_ chips are a family of 32-bit ARM-based microcontroller by +STMicroelectronics. + +.. _STM32: https://www.st.com/en/microcontrollers-microprocessors/stm32-32-bit-arm-cortex-mcus.html + +The STM32F1 series is based on ARM Cortex-M3 core. The following machines are +based on this chip : + +- ``stm32vldiscovery`` STM32VLDISCOVERY board with STM32F100RBT6 microcontroller + +The STM32F2 series is based on ARM Cortex-M3 core. The following machines are +based on this chip : + +- ``netduino2`` Netduino 2 board with STM32F205RFT6 microcontroller + +The STM32F4 series is based on ARM Cortex-M4F core. This series is pin-to-pin +compatible with STM32F2 series. The following machines are based on this chip : + +- ``netduinoplus2`` Netduino Plus 2 board with STM32F405RGT6 microcontroller + +There are many other STM32 series that are currently not supported by QEMU. + +Supported devices +----------------- + + * ARM Cortex-M3, Cortex M4F + * Analog to Digital Converter (ADC) + * EXTI interrupt + * Serial ports (USART) + * SPI controller + * System configuration (SYSCFG) + * Timer controller (TIMER) + +Missing devices +--------------- + + * Camera interface (DCMI) + * Controller Area Network (CAN) + * Cycle Redundancy Check (CRC) calculation unit + * Digital to Analog Converter (DAC) + * DMA controller + * Ethernet controller + * Flash Interface Unit + * GPIO controller + * I2C controller + * Inter-Integrated Sound (I2S) controller + * Power supply configuration (PWR) + * Random Number Generator (RNG) + * Real-Time Clock (RTC) controller + * Reset and Clock Controller (RCC) + * Secure Digital Input/Output (SDIO) interface + * USB OTG + * Watchdog controller (IWDG, WWDG) + +Boot options +------------ + +The STM32 machines can be started using the ``-kernel`` option to load a +firmware. Example: + +.. code-block:: bash + + $ qemu-system-arm -M stm32vldiscovery -kernel firmware.bin diff --git a/docs/system/target-arm.rst b/docs/system/target-arm.rst index 13b3eea..705b883 100644 --- a/docs/system/target-arm.rst +++ b/docs/system/target-arm.rst @@ -97,6 +97,7 @@ undocumented; you can get a complete list by running arm/collie arm/sx1 arm/stellaris + arm/stm32 arm/virt arm/xlnx-versal-virt diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 647b5c8..c521189 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -239,6 +239,10 @@ config STELLARIS select STELLARIS_ENET # ethernet select UNIMP +config STM32VLDISCOVERY + bool + select STM32F100_SOC + config STRONGARM bool select PXA2XX @@ -326,6 +330,12 @@ config RASPI select SDHCI select USB_DWC2 +config STM32F100_SOC + bool + select ARM_V7M + select STM32F2XX_USART + select STM32F2XX_SPI + config STM32F205_SOC bool select ARM_V7M diff --git a/hw/arm/meson.build b/hw/arm/meson.build index be39117..721a8eb 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -24,6 +24,7 @@ arm_ss.add(when: 'CONFIG_Z2', if_true: files('z2.c')) arm_ss.add(when: 'CONFIG_REALVIEW', if_true: files('realview.c')) arm_ss.add(when: 'CONFIG_SBSA_REF', if_true: files('sbsa-ref.c')) arm_ss.add(when: 'CONFIG_STELLARIS', if_true: files('stellaris.c')) +arm_ss.add(when: 'CONFIG_STM32VLDISCOVERY', if_true: files('stm32vldiscovery.c')) arm_ss.add(when: 'CONFIG_COLLIE', if_true: files('collie.c')) arm_ss.add(when: 'CONFIG_VERSATILE', if_true: files('versatilepb.c')) arm_ss.add(when: 'CONFIG_VEXPRESS', if_true: files('vexpress.c')) @@ -39,6 +40,7 @@ arm_ss.add(when: 'CONFIG_STRONGARM', if_true: files('strongarm.c')) arm_ss.add(when: 'CONFIG_ALLWINNER_A10', if_true: files('allwinner-a10.c', 'cubieboard.c')) arm_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3.c', 'orangepi.c')) arm_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_peripherals.c', 'bcm2836.c', 'raspi.c')) +arm_ss.add(when: 'CONFIG_STM32F100_SOC', if_true: files('stm32f100_soc.c')) arm_ss.add(when: 'CONFIG_STM32F205_SOC', if_true: files('stm32f205_soc.c')) arm_ss.add(when: 'CONFIG_STM32F405_SOC', if_true: files('stm32f405_soc.c')) arm_ss.add(when: 'CONFIG_XLNX_ZYNQMP_ARM', if_true: files('xlnx-zynqmp.c', 'xlnx-zcu102.c')) diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index 8b4dab9..ad48cf2 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -1453,13 +1453,67 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) DeviceState *sddev; DeviceState *ssddev; - /* Some boards have both an OLED controller and SD card connected to + /* + * Some boards have both an OLED controller and SD card connected to * the same SSI port, with the SD card chip select connected to a * GPIO pin. Technically the OLED chip select is connected to the * SSI Fss pin. We do not bother emulating that as both devices * should never be selected simultaneously, and our OLED controller * ignores stray 0xff commands that occur when deselecting the SD * card. + * + * The h/w wiring is: + * - GPIO pin D0 is wired to the active-low SD card chip select + * - GPIO pin A3 is wired to the active-low OLED chip select + * - The SoC wiring of the PL061 "auxiliary function" for A3 is + * SSI0Fss ("frame signal"), which is an output from the SoC's + * SSI controller. The SSI controller takes SSI0Fss low when it + * transmits a frame, so it can work as a chip-select signal. + * - GPIO A4 is aux-function SSI0Rx, and wired to the SD card Tx + * (the OLED never sends data to the CPU, so no wiring needed) + * - GPIO A5 is aux-function SSI0Tx, and wired to the SD card Rx + * and the OLED display-data-in + * - GPIO A2 is aux-function SSI0Clk, wired to SD card and OLED + * serial-clock input + * So a guest that wants to use the OLED can configure the PL061 + * to make pins A2, A3, A5 aux-function, so they are connected + * directly to the SSI controller. When the SSI controller sends + * data it asserts SSI0Fss which selects the OLED. + * A guest that wants to use the SD card configures A2, A4 and A5 + * as aux-function, but leaves A3 as a software-controlled GPIO + * line. It asserts the SD card chip-select by using the PL061 + * to control pin D0, and lets the SSI controller handle Clk, Tx + * and Rx. (The SSI controller asserts Fss during tx cycles as + * usual, but because A3 is not set to aux-function this is not + * forwarded to the OLED, and so the OLED stays unselected.) + * + * The QEMU implementation instead is: + * - GPIO pin D0 is wired to the active-low SD card chip select, + * and also to the OLED chip-select which is implemented + * as *active-high* + * - SSI controller signals go to the devices regardless of + * whether the guest programs A2, A4, A5 as aux-function or not + * + * The problem with this implementation is if the guest doesn't + * care about the SD card and only uses the OLED. In that case it + * may choose never to do anything with D0 (leaving it in its + * default floating state, which reliably leaves the card disabled + * because an SD card has a pullup on CS within the card itself), + * and only set up A2, A3, A5. This for us would mean the OLED + * never gets the chip-select assert it needs. We work around + * this with a manual raise of D0 here (despite board creation + * code being the wrong place to raise IRQ lines) to put the OLED + * into an initially selected state. + * + * In theory the right way to model this would be: + * - Implement aux-function support in the PL061, with an + * extra set of AFIN and AFOUT GPIO lines (set up so that + * if a GPIO line is in auxfn mode the main GPIO in and out + * track the AFIN and AFOUT lines) + * - Wire the AFOUT for D0 up to either a line from the + * SSI controller that's pulled low around every transmit, + * or at least to an always-0 line here on the board + * - Make the ssd0323 OLED controller chipselect active-low */ bus = qdev_get_child_bus(dev, "ssi"); diff --git a/hw/arm/stm32f100_soc.c b/hw/arm/stm32f100_soc.c new file mode 100644 index 0000000..0c4a5c6 --- /dev/null +++ b/hw/arm/stm32f100_soc.c @@ -0,0 +1,182 @@ +/* + * STM32F100 SoC + * + * Copyright (c) 2021 Alexandre Iooss <erdnaxe@crans.org> + * Copyright (c) 2014 Alistair Francis <alistair@alistair23.me> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/module.h" +#include "hw/arm/boot.h" +#include "exec/address-spaces.h" +#include "hw/arm/stm32f100_soc.h" +#include "hw/qdev-properties.h" +#include "hw/misc/unimp.h" +#include "sysemu/sysemu.h" + +/* stm32f100_soc implementation is derived from stm32f205_soc */ + +static const uint32_t usart_addr[STM_NUM_USARTS] = { 0x40013800, 0x40004400, + 0x40004800 }; +static const uint32_t spi_addr[STM_NUM_SPIS] = { 0x40013000, 0x40003800 }; + +static const int usart_irq[STM_NUM_USARTS] = {37, 38, 39}; +static const int spi_irq[STM_NUM_SPIS] = {35, 36}; + +static void stm32f100_soc_initfn(Object *obj) +{ + STM32F100State *s = STM32F100_SOC(obj); + int i; + + object_initialize_child(obj, "armv7m", &s->armv7m, TYPE_ARMV7M); + + for (i = 0; i < STM_NUM_USARTS; i++) { + object_initialize_child(obj, "usart[*]", &s->usart[i], + TYPE_STM32F2XX_USART); + } + + for (i = 0; i < STM_NUM_SPIS; i++) { + object_initialize_child(obj, "spi[*]", &s->spi[i], TYPE_STM32F2XX_SPI); + } +} + +static void stm32f100_soc_realize(DeviceState *dev_soc, Error **errp) +{ + STM32F100State *s = STM32F100_SOC(dev_soc); + DeviceState *dev, *armv7m; + SysBusDevice *busdev; + int i; + + MemoryRegion *system_memory = get_system_memory(); + MemoryRegion *sram = g_new(MemoryRegion, 1); + MemoryRegion *flash = g_new(MemoryRegion, 1); + MemoryRegion *flash_alias = g_new(MemoryRegion, 1); + + /* + * Init flash region + * Flash starts at 0x08000000 and then is aliased to boot memory at 0x0 + */ + memory_region_init_rom(flash, OBJECT(dev_soc), "STM32F100.flash", + FLASH_SIZE, &error_fatal); + memory_region_init_alias(flash_alias, OBJECT(dev_soc), + "STM32F100.flash.alias", flash, 0, FLASH_SIZE); + memory_region_add_subregion(system_memory, FLASH_BASE_ADDRESS, flash); + memory_region_add_subregion(system_memory, 0, flash_alias); + + /* Init SRAM region */ + memory_region_init_ram(sram, NULL, "STM32F100.sram", SRAM_SIZE, + &error_fatal); + memory_region_add_subregion(system_memory, SRAM_BASE_ADDRESS, sram); + + /* Init ARMv7m */ + armv7m = DEVICE(&s->armv7m); + qdev_prop_set_uint32(armv7m, "num-irq", 61); + qdev_prop_set_string(armv7m, "cpu-type", s->cpu_type); + qdev_prop_set_bit(armv7m, "enable-bitband", true); + object_property_set_link(OBJECT(&s->armv7m), "memory", + OBJECT(get_system_memory()), &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->armv7m), errp)) { + return; + } + + /* Attach UART (uses USART registers) and USART controllers */ + for (i = 0; i < STM_NUM_USARTS; i++) { + dev = DEVICE(&(s->usart[i])); + qdev_prop_set_chr(dev, "chardev", serial_hd(i)); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->usart[i]), errp)) { + return; + } + busdev = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(busdev, 0, usart_addr[i]); + sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m, usart_irq[i])); + } + + /* SPI 1 and 2 */ + for (i = 0; i < STM_NUM_SPIS; i++) { + dev = DEVICE(&(s->spi[i])); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->spi[i]), errp)) { + return; + } + busdev = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(busdev, 0, spi_addr[i]); + sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m, spi_irq[i])); + } + + create_unimplemented_device("timer[2]", 0x40000000, 0x400); + create_unimplemented_device("timer[3]", 0x40000400, 0x400); + create_unimplemented_device("timer[4]", 0x40000800, 0x400); + create_unimplemented_device("timer[6]", 0x40001000, 0x400); + create_unimplemented_device("timer[7]", 0x40001400, 0x400); + create_unimplemented_device("RTC", 0x40002800, 0x400); + create_unimplemented_device("WWDG", 0x40002C00, 0x400); + create_unimplemented_device("IWDG", 0x40003000, 0x400); + create_unimplemented_device("I2C1", 0x40005400, 0x400); + create_unimplemented_device("I2C2", 0x40005800, 0x400); + create_unimplemented_device("BKP", 0x40006C00, 0x400); + create_unimplemented_device("PWR", 0x40007000, 0x400); + create_unimplemented_device("DAC", 0x40007400, 0x400); + create_unimplemented_device("CEC", 0x40007800, 0x400); + 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("ADC1", 0x40012400, 0x400); + create_unimplemented_device("timer[1]", 0x40012C00, 0x400); + create_unimplemented_device("timer[15]", 0x40014000, 0x400); + create_unimplemented_device("timer[16]", 0x40014400, 0x400); + create_unimplemented_device("timer[17]", 0x40014800, 0x400); + create_unimplemented_device("DMA", 0x40020000, 0x400); + create_unimplemented_device("RCC", 0x40021000, 0x400); + create_unimplemented_device("Flash Int", 0x40022000, 0x400); + create_unimplemented_device("CRC", 0x40023000, 0x400); +} + +static Property stm32f100_soc_properties[] = { + DEFINE_PROP_STRING("cpu-type", STM32F100State, cpu_type), + DEFINE_PROP_END_OF_LIST(), +}; + +static void stm32f100_soc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = stm32f100_soc_realize; + device_class_set_props(dc, stm32f100_soc_properties); +} + +static const TypeInfo stm32f100_soc_info = { + .name = TYPE_STM32F100_SOC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(STM32F100State), + .instance_init = stm32f100_soc_initfn, + .class_init = stm32f100_soc_class_init, +}; + +static void stm32f100_soc_types(void) +{ + type_register_static(&stm32f100_soc_info); +} + +type_init(stm32f100_soc_types) diff --git a/hw/arm/stm32vldiscovery.c b/hw/arm/stm32vldiscovery.c new file mode 100644 index 0000000..7e8191e --- /dev/null +++ b/hw/arm/stm32vldiscovery.c @@ -0,0 +1,66 @@ +/* + * ST STM32VLDISCOVERY machine + * + * Copyright (c) 2021 Alexandre Iooss <erdnaxe@crans.org> + * Copyright (c) 2014 Alistair Francis <alistair@alistair23.me> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/boards.h" +#include "hw/qdev-properties.h" +#include "qemu/error-report.h" +#include "hw/arm/stm32f100_soc.h" +#include "hw/arm/boot.h" + +/* stm32vldiscovery implementation is derived from netduinoplus2 */ + +/* Main SYSCLK frequency in Hz (24MHz) */ +#define SYSCLK_FRQ 24000000ULL + +static void stm32vldiscovery_init(MachineState *machine) +{ + DeviceState *dev; + + /* + * TODO: ideally we would model the SoC RCC and let it handle + * system_clock_scale, including its ability to define different + * possible SYSCLK sources. + */ + system_clock_scale = NANOSECONDS_PER_SECOND / SYSCLK_FRQ; + + dev = qdev_new(TYPE_STM32F100_SOC); + qdev_prop_set_string(dev, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m3")); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + + armv7m_load_kernel(ARM_CPU(first_cpu), + machine->kernel_filename, + FLASH_SIZE); +} + +static void stm32vldiscovery_machine_init(MachineClass *mc) +{ + mc->desc = "ST STM32VLDISCOVERY (Cortex-M3)"; + mc->init = stm32vldiscovery_init; +} + +DEFINE_MACHINE("stm32vldiscovery", stm32vldiscovery_machine_init) + diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 4b96f06..93ab9d2 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -895,6 +895,9 @@ static void create_gpio_devices(const VirtMachineState *vms, int gpio, MachineState *ms = MACHINE(vms); pl061_dev = qdev_new("pl061"); + /* Pull lines down to 0 if not driven by the PL061 */ + qdev_prop_set_uint32(pl061_dev, "pullups", 0); + qdev_prop_set_uint32(pl061_dev, "pulldowns", 0xff); s = SYS_BUS_DEVICE(pl061_dev); sysbus_realize_and_unref(s, &error_fatal); memory_region_add_subregion(mem, base, sysbus_mmio_get_region(s, 0)); diff --git a/hw/gpio/pl061.c b/hw/gpio/pl061.c index e72e775..899be86 100644 --- a/hw/gpio/pl061.c +++ b/hw/gpio/pl061.c @@ -6,28 +6,39 @@ * Written by Paul Brook * * This code is licensed under the GPL. + * + * QEMU interface: + * + sysbus MMIO region 0: the device registers + * + sysbus IRQ: the GPIOINTR interrupt line + * + unnamed GPIO inputs 0..7: inputs to connect to the emulated GPIO lines + * + unnamed GPIO outputs 0..7: the emulated GPIO lines, considered as + * outputs + * + QOM property "pullups": an integer defining whether non-floating lines + * configured as inputs should be pulled up to logical 1 (ie whether in + * real hardware they have a pullup resistor on the line out of the PL061). + * This should be an 8-bit value, where bit 0 is 1 if GPIO line 0 should + * be pulled high, bit 1 configures line 1, and so on. The default is 0xff, + * indicating that all GPIO lines are pulled up to logical 1. + * + QOM property "pulldowns": an integer defining whether non-floating lines + * configured as inputs should be pulled down to logical 0 (ie whether in + * real hardware they have a pulldown resistor on the line out of the PL061). + * This should be an 8-bit value, where bit 0 is 1 if GPIO line 0 should + * be pulled low, bit 1 configures line 1, and so on. The default is 0x0. + * It is an error to set a bit in both "pullups" and "pulldowns". If a bit + * is 0 in both, then the line is considered to be floating, and it will + * not have qemu_set_irq() called on it when it is configured as an input. */ #include "qemu/osdep.h" #include "hw/irq.h" #include "hw/sysbus.h" +#include "hw/qdev-properties.h" #include "migration/vmstate.h" +#include "qapi/error.h" #include "qemu/log.h" #include "qemu/module.h" #include "qom/object.h" - -//#define DEBUG_PL061 1 - -#ifdef DEBUG_PL061 -#define DPRINTF(fmt, ...) \ -do { printf("pl061: " fmt , ## __VA_ARGS__); } while (0) -#define BADF(fmt, ...) \ -do { fprintf(stderr, "pl061: error: " fmt , ## __VA_ARGS__); exit(1);} while (0) -#else -#define DPRINTF(fmt, ...) do {} while(0) -#define BADF(fmt, ...) \ -do { fprintf(stderr, "pl061: error: " fmt , ## __VA_ARGS__);} while (0) -#endif +#include "trace.h" static const uint8_t pl061_id[12] = { 0x00, 0x00, 0x00, 0x00, 0x61, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; @@ -67,7 +78,9 @@ struct PL061State { qemu_irq irq; qemu_irq out[N_GPIOS]; const unsigned char *id; - uint32_t rsvd_start; /* reserved area: [rsvd_start, 0xfcc] */ + /* Properties, for non-Luminary PL061 */ + uint32_t pullups; + uint32_t pulldowns; }; static const VMStateDescription vmstate_pl061 = { @@ -100,26 +113,75 @@ static const VMStateDescription vmstate_pl061 = { } }; +static uint8_t pl061_floating(PL061State *s) +{ + /* + * Return mask of bits which correspond to pins configured as inputs + * and which are floating (neither pulled up to 1 nor down to 0). + */ + uint8_t floating; + + if (s->id == pl061_id_luminary) { + /* + * If both PUR and PDR bits are clear, there is neither a pullup + * nor a pulldown in place, and the output truly floats. + */ + floating = ~(s->pur | s->pdr); + } else { + floating = ~(s->pullups | s->pulldowns); + } + return floating & ~s->dir; +} + +static uint8_t pl061_pullups(PL061State *s) +{ + /* + * Return mask of bits which correspond to pins configured as inputs + * and which are pulled up to 1. + */ + uint8_t pullups; + + if (s->id == pl061_id_luminary) { + /* + * The Luminary variant of the PL061 has an extra registers which + * the guest can use to configure whether lines should be pullup + * or pulldown. + */ + pullups = s->pur; + } else { + pullups = s->pullups; + } + return pullups & ~s->dir; +} + static void pl061_update(PL061State *s) { uint8_t changed; uint8_t mask; uint8_t out; int i; - - DPRINTF("dir = %d, data = %d\n", s->dir, s->data); - - /* Outputs float high. */ - /* FIXME: This is board dependent. */ - out = (s->data & s->dir) | ~s->dir; + uint8_t pullups = pl061_pullups(s); + uint8_t floating = pl061_floating(s); + + trace_pl061_update(DEVICE(s)->canonical_path, s->dir, s->data, + pullups, floating); + + /* + * Pins configured as output are driven from the data register; + * otherwise if they're pulled up they're 1, and if they're floating + * then we give them the same value they had previously, so we don't + * report any change to the other end. + */ + out = (s->data & s->dir) | pullups | (s->old_out_data & floating); changed = s->old_out_data ^ out; if (changed) { s->old_out_data = out; for (i = 0; i < N_GPIOS; i++) { mask = 1 << i; if (changed & mask) { - DPRINTF("Set output %d = %d\n", i, (out & mask) != 0); - qemu_set_irq(s->out[i], (out & mask) != 0); + int level = (out & mask) != 0; + trace_pl061_set_output(DEVICE(s)->canonical_path, i, level); + qemu_set_irq(s->out[i], level); } } } @@ -131,7 +193,8 @@ static void pl061_update(PL061State *s) for (i = 0; i < N_GPIOS; i++) { mask = 1 << i; if (changed & mask) { - DPRINTF("Changed input %d = %d\n", i, (s->data & mask) != 0); + trace_pl061_input_change(DEVICE(s)->canonical_path, i, + (s->data & mask) != 0); if (!(s->isense & mask)) { /* Edge interrupt */ @@ -150,7 +213,8 @@ static void pl061_update(PL061State *s) /* Level interrupt */ s->istate |= ~(s->data ^ s->iev) & s->isense; - DPRINTF("istate = %02X\n", s->istate); + trace_pl061_update_istate(DEVICE(s)->canonical_path, + s->istate, s->im, (s->istate & s->im) != 0); qemu_set_irq(s->irq, (s->istate & s->im) != 0); } @@ -159,62 +223,114 @@ static uint64_t pl061_read(void *opaque, hwaddr offset, unsigned size) { PL061State *s = (PL061State *)opaque; + uint64_t r = 0; - if (offset < 0x400) { - return s->data & (offset >> 2); - } - if (offset >= s->rsvd_start && offset <= 0xfcc) { - goto err_out; - } - if (offset >= 0xfd0 && offset < 0x1000) { - return s->id[(offset - 0xfd0) >> 2]; - } switch (offset) { + case 0x0 ... 0x3ff: /* Data */ + r = s->data & (offset >> 2); + break; case 0x400: /* Direction */ - return s->dir; + r = s->dir; + break; case 0x404: /* Interrupt sense */ - return s->isense; + r = s->isense; + break; case 0x408: /* Interrupt both edges */ - return s->ibe; + r = s->ibe; + break; case 0x40c: /* Interrupt event */ - return s->iev; + r = s->iev; + break; case 0x410: /* Interrupt mask */ - return s->im; + r = s->im; + break; case 0x414: /* Raw interrupt status */ - return s->istate; + r = s->istate; + break; case 0x418: /* Masked interrupt status */ - return s->istate & s->im; + r = s->istate & s->im; + break; case 0x420: /* Alternate function select */ - return s->afsel; + r = s->afsel; + break; case 0x500: /* 2mA drive */ - return s->dr2r; + if (s->id != pl061_id_luminary) { + goto bad_offset; + } + r = s->dr2r; + break; case 0x504: /* 4mA drive */ - return s->dr4r; + if (s->id != pl061_id_luminary) { + goto bad_offset; + } + r = s->dr4r; + break; case 0x508: /* 8mA drive */ - return s->dr8r; + if (s->id != pl061_id_luminary) { + goto bad_offset; + } + r = s->dr8r; + break; case 0x50c: /* Open drain */ - return s->odr; + if (s->id != pl061_id_luminary) { + goto bad_offset; + } + r = s->odr; + break; case 0x510: /* Pull-up */ - return s->pur; + if (s->id != pl061_id_luminary) { + goto bad_offset; + } + r = s->pur; + break; case 0x514: /* Pull-down */ - return s->pdr; + if (s->id != pl061_id_luminary) { + goto bad_offset; + } + r = s->pdr; + break; case 0x518: /* Slew rate control */ - return s->slr; + if (s->id != pl061_id_luminary) { + goto bad_offset; + } + r = s->slr; + break; case 0x51c: /* Digital enable */ - return s->den; + if (s->id != pl061_id_luminary) { + goto bad_offset; + } + r = s->den; + break; case 0x520: /* Lock */ - return s->locked; + if (s->id != pl061_id_luminary) { + goto bad_offset; + } + r = s->locked; + break; case 0x524: /* Commit */ - return s->cr; + if (s->id != pl061_id_luminary) { + goto bad_offset; + } + r = s->cr; + break; case 0x528: /* Analog mode select */ - return s->amsel; + if (s->id != pl061_id_luminary) { + goto bad_offset; + } + r = s->amsel; + break; + case 0xfd0 ... 0xfff: /* ID registers */ + r = s->id[(offset - 0xfd0) >> 2]; + break; default: + bad_offset: + qemu_log_mask(LOG_GUEST_ERROR, + "pl061_read: Bad offset %x\n", (int)offset); break; } -err_out: - qemu_log_mask(LOG_GUEST_ERROR, - "pl061_read: Bad offset %x\n", (int)offset); - return 0; + + trace_pl061_read(DEVICE(s)->canonical_path, offset, r); + return r; } static void pl061_write(void *opaque, hwaddr offset, @@ -223,16 +339,14 @@ static void pl061_write(void *opaque, hwaddr offset, PL061State *s = (PL061State *)opaque; uint8_t mask; - if (offset < 0x400) { + trace_pl061_write(DEVICE(s)->canonical_path, offset, value); + + switch (offset) { + case 0 ... 0x3ff: mask = (offset >> 2) & s->dir; s->data = (s->data & ~mask) | (value & mask); pl061_update(s); return; - } - if (offset >= s->rsvd_start) { - goto err_out; - } - switch (offset) { case 0x400: /* Direction */ s->dir = value & 0xff; break; @@ -256,56 +370,99 @@ static void pl061_write(void *opaque, hwaddr offset, s->afsel = (s->afsel & ~mask) | (value & mask); break; case 0x500: /* 2mA drive */ + if (s->id != pl061_id_luminary) { + goto bad_offset; + } s->dr2r = value & 0xff; break; case 0x504: /* 4mA drive */ + if (s->id != pl061_id_luminary) { + goto bad_offset; + } s->dr4r = value & 0xff; break; case 0x508: /* 8mA drive */ + if (s->id != pl061_id_luminary) { + goto bad_offset; + } s->dr8r = value & 0xff; break; case 0x50c: /* Open drain */ + if (s->id != pl061_id_luminary) { + goto bad_offset; + } s->odr = value & 0xff; break; case 0x510: /* Pull-up */ + if (s->id != pl061_id_luminary) { + goto bad_offset; + } s->pur = value & 0xff; break; case 0x514: /* Pull-down */ + if (s->id != pl061_id_luminary) { + goto bad_offset; + } s->pdr = value & 0xff; break; case 0x518: /* Slew rate control */ + if (s->id != pl061_id_luminary) { + goto bad_offset; + } s->slr = value & 0xff; break; case 0x51c: /* Digital enable */ + if (s->id != pl061_id_luminary) { + goto bad_offset; + } s->den = value & 0xff; break; case 0x520: /* Lock */ + if (s->id != pl061_id_luminary) { + goto bad_offset; + } s->locked = (value != 0xacce551); break; case 0x524: /* Commit */ + if (s->id != pl061_id_luminary) { + goto bad_offset; + } if (!s->locked) s->cr = value & 0xff; break; case 0x528: + if (s->id != pl061_id_luminary) { + goto bad_offset; + } s->amsel = value & 0xff; break; default: - goto err_out; + bad_offset: + qemu_log_mask(LOG_GUEST_ERROR, + "pl061_write: Bad offset %x\n", (int)offset); + return; } pl061_update(s); return; -err_out: - qemu_log_mask(LOG_GUEST_ERROR, - "pl061_write: Bad offset %x\n", (int)offset); } -static void pl061_reset(DeviceState *dev) +static void pl061_enter_reset(Object *obj, ResetType type) { - PL061State *s = PL061(dev); + PL061State *s = PL061(obj); + + trace_pl061_reset(DEVICE(s)->canonical_path); /* reset values from PL061 TRM, Stellaris LM3S5P31 & LM3S8962 Data Sheet */ + + /* + * FIXME: For the LM3S6965, not all of the PL061 instances have the + * same reset values for GPIOPUR, GPIOAFSEL and GPIODEN, so in theory + * we should allow the board to configure these via properties. + * In practice, we don't wire anything up to the affected GPIO lines + * (PB7, PC0, PC1, PC2, PC3 -- they're used for JTAG), so we can + * get away with this inaccuracy. + */ s->data = 0; - s->old_out_data = 0; s->old_in_data = 0; s->dir = 0; s->isense = 0; @@ -327,6 +484,24 @@ static void pl061_reset(DeviceState *dev) s->amsel = 0; } +static void pl061_hold_reset(Object *obj) +{ + PL061State *s = PL061(obj); + int i, level; + uint8_t floating = pl061_floating(s); + uint8_t pullups = pl061_pullups(s); + + for (i = 0; i < N_GPIOS; i++) { + if (extract32(floating, i, 1)) { + continue; + } + level = extract32(pullups, i, 1); + trace_pl061_set_output(DEVICE(s)->canonical_path, i, level); + qemu_set_irq(s->out[i], level); + } + s->old_out_data = pullups; +} + static void pl061_set_irq(void * opaque, int irq, int level) { PL061State *s = (PL061State *)opaque; @@ -352,7 +527,6 @@ static void pl061_luminary_init(Object *obj) PL061State *s = PL061(obj); s->id = pl061_id_luminary; - s->rsvd_start = 0x52c; } static void pl061_init(Object *obj) @@ -362,7 +536,6 @@ static void pl061_init(Object *obj) SysBusDevice *sbd = SYS_BUS_DEVICE(obj); s->id = pl061_id; - s->rsvd_start = 0x424; memory_region_init_io(&s->iomem, obj, &pl061_ops, s, "pl061", 0x1000); sysbus_init_mmio(sbd, &s->iomem); @@ -371,12 +544,40 @@ static void pl061_init(Object *obj) qdev_init_gpio_out(dev, s->out, N_GPIOS); } +static void pl061_realize(DeviceState *dev, Error **errp) +{ + PL061State *s = PL061(dev); + + if (s->pullups > 0xff) { + error_setg(errp, "pullups property must be between 0 and 0xff"); + return; + } + if (s->pulldowns > 0xff) { + error_setg(errp, "pulldowns property must be between 0 and 0xff"); + return; + } + if (s->pullups & s->pulldowns) { + error_setg(errp, "no bit may be set both in pullups and pulldowns"); + return; + } +} + +static Property pl061_props[] = { + DEFINE_PROP_UINT32("pullups", PL061State, pullups, 0xff), + DEFINE_PROP_UINT32("pulldowns", PL061State, pulldowns, 0x0), + DEFINE_PROP_END_OF_LIST() +}; + static void pl061_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); dc->vmsd = &vmstate_pl061; - dc->reset = &pl061_reset; + dc->realize = pl061_realize; + device_class_set_props(dc, pl061_props); + rc->phases.enter = pl061_enter_reset; + rc->phases.hold = pl061_hold_reset; } static const TypeInfo pl061_info = { diff --git a/hw/gpio/trace-events b/hw/gpio/trace-events index f0b6641..1dab99c 100644 --- a/hw/gpio/trace-events +++ b/hw/gpio/trace-events @@ -13,6 +13,15 @@ nrf51_gpio_write(uint64_t offset, uint64_t value) "offset 0x%" PRIx64 " value 0x nrf51_gpio_set(int64_t line, int64_t value) "line %" PRIi64 " value %" PRIi64 nrf51_gpio_update_output_irq(int64_t line, int64_t value) "line %" PRIi64 " value %" PRIi64 +# pl061.c +pl061_update(const char *id, uint32_t dir, uint32_t data, uint32_t pullups, uint32_t floating) "%s GPIODIR 0x%x GPIODATA 0x%x pullups 0x%x floating 0x%x" +pl061_set_output(const char *id, int gpio, int level) "%s setting output %d to %d" +pl061_input_change(const char *id, int gpio, int level) "%s input %d changed to %d" +pl061_update_istate(const char *id, uint32_t istate, uint32_t im, int level) "%s GPIORIS 0x%x GPIOIE 0x%x interrupt level %d" +pl061_read(const char *id, uint64_t offset, uint64_t r) "%s offset 0x%" PRIx64 " value 0x%" PRIx64 +pl061_write(const char *id, uint64_t offset, uint64_t value) "%s offset 0x%" PRIx64 " value 0x%" PRIx64 +pl061_reset(const char *id) "%s reset" + # sifive_gpio.c sifive_gpio_read(uint64_t offset, uint64_t r) "offset 0x%" PRIx64 " value 0x%" PRIx64 sifive_gpio_write(uint64_t offset, uint64_t value) "offset 0x%" PRIx64 " value 0x%" PRIx64 diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index 3e0641a..a032d50 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -1227,7 +1227,7 @@ static void icv_dir_write(CPUARMState *env, const ARMCPRegInfo *ri, trace_gicv3_icv_dir_write(gicv3_redist_affid(cs), value); - if (irq >= cs->gic->num_irq) { + if (irq >= GICV3_MAXIRQ) { /* Also catches special interrupt numbers and LPIs */ return; } @@ -1262,7 +1262,7 @@ static void icv_eoir_write(CPUARMState *env, const ARMCPRegInfo *ri, trace_gicv3_icv_eoir_write(ri->crm == 8 ? 0 : 1, gicv3_redist_affid(cs), value); - if (irq >= cs->gic->num_irq) { + if (irq >= GICV3_MAXIRQ) { /* Also catches special interrupt numbers and LPIs */ return; } diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c index 8645220..53da703 100644 --- a/hw/intc/arm_gicv3_redist.c +++ b/hw/intc/arm_gicv3_redist.c @@ -453,7 +453,7 @@ MemTxResult gicv3_redist_read(void *opaque, hwaddr offset, uint64_t *data, if (r == MEMTX_ERROR) { qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid guest read at offset " TARGET_FMT_plx - "size %u\n", __func__, offset, size); + " size %u\n", __func__, offset, size); trace_gicv3_redist_badread(gicv3_redist_affid(cs), offset, size, attrs.secure); /* The spec requires that reserved registers are RAZ/WI; @@ -510,7 +510,7 @@ MemTxResult gicv3_redist_write(void *opaque, hwaddr offset, uint64_t data, if (r == MEMTX_ERROR) { qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid guest write at offset " TARGET_FMT_plx - "size %u\n", __func__, offset, size); + " size %u\n", __func__, offset, size); trace_gicv3_redist_badwrite(gicv3_redist_affid(cs), offset, data, size, attrs.secure); /* The spec requires that reserved registers are RAZ/WI; diff --git a/include/hw/arm/stm32f100_soc.h b/include/hw/arm/stm32f100_soc.h new file mode 100644 index 0000000..71bffcf --- /dev/null +++ b/include/hw/arm/stm32f100_soc.h @@ -0,0 +1,57 @@ +/* + * STM32F100 SoC + * + * Copyright (c) 2021 Alexandre Iooss <erdnaxe@crans.org> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef HW_ARM_STM32F100_SOC_H +#define HW_ARM_STM32F100_SOC_H + +#include "hw/char/stm32f2xx_usart.h" +#include "hw/ssi/stm32f2xx_spi.h" +#include "hw/arm/armv7m.h" +#include "qom/object.h" + +#define TYPE_STM32F100_SOC "stm32f100-soc" +OBJECT_DECLARE_SIMPLE_TYPE(STM32F100State, STM32F100_SOC) + +#define STM_NUM_USARTS 3 +#define STM_NUM_SPIS 2 + +#define FLASH_BASE_ADDRESS 0x08000000 +#define FLASH_SIZE (128 * 1024) +#define SRAM_BASE_ADDRESS 0x20000000 +#define SRAM_SIZE (8 * 1024) + +struct STM32F100State { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + char *cpu_type; + + ARMv7MState armv7m; + + STM32F2XXUsartState usart[STM_NUM_USARTS]; + STM32F2XXSPIState spi[STM_NUM_SPIS]; +}; + +#endif diff --git a/target/arm/helper.c b/target/arm/helper.c index a66c1f0..910ace4 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -6326,11 +6326,21 @@ static const ARMCPRegInfo debug_cp_reginfo[] = { .access = PL1_RW, .accessfn = access_tda, .fieldoffset = offsetof(CPUARMState, cp15.mdscr_el1), .resetvalue = 0 }, - /* MDCCSR_EL0, aka DBGDSCRint. This is a read-only mirror of MDSCR_EL1. + /* + * MDCCSR_EL0[30:29] map to EDSCR[30:29]. Simply RAZ as the external + * Debug Communication Channel is not implemented. + */ + { .name = "MDCCSR_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 2, .opc1 = 3, .crn = 0, .crm = 1, .opc2 = 0, + .access = PL0_R, .accessfn = access_tda, + .type = ARM_CP_CONST, .resetvalue = 0 }, + /* + * DBGDSCRint[15,12,5:2] map to MDSCR_EL1[15,12,5:2]. Map all bits as + * it is unlikely a guest will care. * We don't implement the configurable EL0 access. */ - { .name = "MDCCSR_EL0", .state = ARM_CP_STATE_BOTH, - .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 0, + { .name = "DBGDSCRint", .state = ARM_CP_STATE_AA32, + .cp = 14, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 0, .type = ARM_CP_ALIAS, .access = PL1_R, .accessfn = access_tda, .fieldoffset = offsetof(CPUARMState, cp15.mdscr_el1), }, diff --git a/tests/qtest/boot-serial-test.c b/tests/qtest/boot-serial-test.c index d40addd..96849ce 100644 --- a/tests/qtest/boot-serial-test.c +++ b/tests/qtest/boot-serial-test.c @@ -94,6 +94,41 @@ static const uint8_t kernel_nrf51[] = { 0x1c, 0x25, 0x00, 0x40 /* 0x4000251c = UART TXD */ }; +static const uint8_t kernel_stm32vldiscovery[] = { + 0x00, 0x00, 0x00, 0x00, /* Stack top address */ + 0x1d, 0x00, 0x00, 0x00, /* Reset handler address */ + 0x00, 0x00, 0x00, 0x00, /* NMI */ + 0x00, 0x00, 0x00, 0x00, /* Hard fault */ + 0x00, 0x00, 0x00, 0x00, /* Memory management fault */ + 0x00, 0x00, 0x00, 0x00, /* Bus fault */ + 0x00, 0x00, 0x00, 0x00, /* Usage fault */ + 0x0b, 0x4b, /* ldr r3, [pc, #44] Get RCC */ + 0x44, 0xf2, 0x04, 0x02, /* movw r2, #16388 */ + 0x1a, 0x60, /* str r2, [r3] */ + 0x0a, 0x4b, /* ldr r3, [pc, #40] Get GPIOA */ + 0x1a, 0x68, /* ldr r2, [r3] */ + 0x22, 0xf0, 0xf0, 0x02, /* bic r2, r2, #240 */ + 0x1a, 0x60, /* str r2, [r3] */ + 0x1a, 0x68, /* ldr r2, [r3] */ + 0x42, 0xf0, 0xb0, 0x02, /* orr r2, r2, #176 */ + 0x1a, 0x60, /* str r2, [r3] */ + 0x07, 0x4b, /* ldr r3, [pc, #26] Get BAUD */ + 0x45, 0x22, /* movs r2, #69 */ + 0x1a, 0x60, /* str r2, [r3] */ + 0x06, 0x4b, /* ldr r3, [pc, #24] Get ENABLE */ + 0x42, 0xf2, 0x08, 0x02, /* movw r2, #8200 */ + 0x1a, 0x60, /* str r2, [r3] */ + 0x05, 0x4b, /* ldr r3, [pc, #20] Get TXD */ + 0x54, 0x22, /* movs r2, 'T' */ + 0x1a, 0x60, /* str r2, [r3] */ + 0xfe, 0xe7, /* b . */ + 0x18, 0x10, 0x02, 0x40, /* 0x40021018 = RCC */ + 0x04, 0x08, 0x01, 0x40, /* 0x40010804 = GPIOA */ + 0x08, 0x38, 0x01, 0x40, /* 0x40013808 = USART1 BAUD */ + 0x0c, 0x38, 0x01, 0x40, /* 0x4001380c = USART1 ENABLE */ + 0x04, 0x38, 0x01, 0x40 /* 0x40013804 = USART1 TXD */ +}; + typedef struct testdef { const char *arch; /* Target architecture */ const char *machine; /* Name of the machine */ @@ -144,6 +179,8 @@ static testdef_t tests[] = { { "aarch64", "virt", "-cpu max", "TT", sizeof(kernel_aarch64), kernel_aarch64 }, { "arm", "microbit", "", "T", sizeof(kernel_nrf51), kernel_nrf51 }, + { "arm", "stm32vldiscovery", "", "T", + sizeof(kernel_stm32vldiscovery), kernel_stm32vldiscovery }, { NULL } }; |