From d95f260aee3ea1b12779cdede7456480ea9ea3d1 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 12 Dec 2023 16:23:13 +0000 Subject: [PATCH 01/12] docs/devel/docs: Document .hx file syntax We don't currently document the syntax of .hx files anywhere except in a few comments at the top of individual .hx files. We don't even have somewhere in the developer docs where we could do this. Add a new files docs/devel/docs.rst which can be a place to document how our docs build process works. For the moment, put in only a brief introductory paragraph and the documentation of the .hx files. We could later add to this file by for example describing how the QAPI-schema-to-docs process works, or anything else that developers might need to know about how to add documentation. Make the .hx files refer to this doc file, and clean up their header comments to be more accurate for the usage in each file and less cut-n-pasted. Signed-off-by: Peter Maydell Reviewed-by: Luc Michel Reviewed-by: David Woodhouse Message-id: 20231212162313.1742462-1-peter.maydell@linaro.org --- MAINTAINERS | 1 + docs/devel/docs.rst | 60 ++++++++++++++++++++++++++++++++++++++ docs/devel/index-build.rst | 1 + hmp-commands-info.hx | 10 +++---- hmp-commands.hx | 10 +++---- qemu-img-cmds.hx | 2 ++ qemu-options.hx | 2 ++ 7 files changed, 76 insertions(+), 10 deletions(-) create mode 100644 docs/devel/docs.rst diff --git a/MAINTAINERS b/MAINTAINERS index b406fb20c0..8e8ca270c4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4175,6 +4175,7 @@ F: docs/conf.py F: docs/*/conf.py F: docs/sphinx/ F: docs/_templates/ +F: docs/devel/docs.rst Miscellaneous ------------- diff --git a/docs/devel/docs.rst b/docs/devel/docs.rst new file mode 100644 index 0000000000..7da067905b --- /dev/null +++ b/docs/devel/docs.rst @@ -0,0 +1,60 @@ + +================== +QEMU Documentation +================== + +QEMU's documentation is written in reStructuredText format and +built using the Sphinx documentation generator. We generate both +the HTML manual and the manpages from the some documentation sources. + +hxtool and .hx files +-------------------- + +The documentation for QEMU command line options and Human Monitor Protocol +(HMP) commands is written in files with the ``.hx`` suffix. These +are processed in two ways: + + * ``scripts/hxtool`` creates C header files from them, which are included + in QEMU to do things like handle the ``--help`` option output + * a Sphinx extension in ``docs/sphinx/hxtool.py`` generates rST output + to be included in the HTML or manpage documentation + +The syntax of these ``.hx`` files is simple. It is broadly an +alternation of C code put into the C output and rST format text +put into the documention. A few special directives are recognised; +these are all-caps and must be at the beginning of the line. + +``HXCOMM`` is the comment marker. The line, including any arbitrary +text after the marker, is discarded and appears neither in the C output +nor the documentation output. + +``SRST`` starts a reStructuredText section. Following lines +are put into the documentation verbatim, and discarded from the C output. + +``ERST`` ends the documentation section started with ``SRST``, +and switches back to a C code section. + +``DEFHEADING()`` defines a heading that should appear in both the +``--help`` output and in the documentation. This directive should +be in the C code block. If there is a string inside the brackets, +this is the heading to use. If this string is empty, it produces +a blank line in the ``--help`` output and is ignored for the rST +output. + +``ARCHHEADING()`` is a variant of ``DEFHEADING()`` which produces +the heading only if the specified guest architecture was compiled +into QEMU. This should be avoided in new documentation. + +Within C code sections, you should check the comments at the top +of the file to see what the expected usage is, because this +varies between files. For instance in ``qemu-options.hx`` we use +the ``DEF()`` macro to define each option and specify its ``--help`` +text, but in ``hmp-commands.hx`` the C code sections are elements +of an array of structs of type ``HMPCommand`` which define the +name, behaviour and help text for each monitor command. + +In the file ``qemu-options.hx``, do not try to define a +reStructuredText label within a documentation section. This file +is included into two separate Sphinx documents, and some +versions of Sphinx will complain about the duplicate label +that results. diff --git a/docs/devel/index-build.rst b/docs/devel/index-build.rst index 57e8d39d98..90b406ca0e 100644 --- a/docs/devel/index-build.rst +++ b/docs/devel/index-build.rst @@ -10,6 +10,7 @@ the basics if you are adding new files and targets to the build. build-system kconfig + docs testing acpi-bits qtest diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index f5b37eb74a..da120f82a3 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -1,8 +1,8 @@ -HXCOMM Use DEFHEADING() to define headings in both help text and rST. -HXCOMM Text between SRST and ERST is copied to the rST version and -HXCOMM discarded from C version. -HXCOMM DEF(command, args, callback, arg_string, help) is used to construct -HXCOMM monitor info commands +HXCOMM See docs/devel/docs.rst for the format of this file. +HXCOMM +HXCOMM This file defines the contents of an array of HMPCommand structs +HXCOMM which specify the name, behaviour and help text for HMP commands. +HXCOMM Text between SRST and ERST is rST format documentation. HXCOMM HXCOMM can be used for comments, discarded from both rST and C. HXCOMM HXCOMM In this file, generally SRST fragments should have two extra diff --git a/hmp-commands.hx b/hmp-commands.hx index 765349ed14..2db5701d49 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1,8 +1,8 @@ -HXCOMM Use DEFHEADING() to define headings in both help text and rST. -HXCOMM Text between SRST and ERST is copied to the rST version and -HXCOMM discarded from C version. -HXCOMM DEF(command, args, callback, arg_string, help) is used to construct -HXCOMM monitor commands +HXCOMM See docs/devel/docs.rst for the format of this file. +HXCOMM +HXCOMM This file defines the contents of an array of HMPCommand structs +HXCOMM which specify the name, behaviour and help text for HMP commands. +HXCOMM Text between SRST and ERST is rST format documentation. HXCOMM HXCOMM can be used for comments, discarded from both rST and C. diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx index 068692d13e..c9dd70a892 100644 --- a/qemu-img-cmds.hx +++ b/qemu-img-cmds.hx @@ -1,3 +1,5 @@ +HXCOMM See docs/devel/docs.rst for the format of this file. +HXCOMM HXCOMM Keep the list of subcommands sorted by name. HXCOMM Use DEFHEADING() to define headings in both help text and rST HXCOMM Text between SRST and ERST are copied to rST version and diff --git a/qemu-options.hx b/qemu-options.hx index b66570ae00..1912b19cb8 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1,3 +1,5 @@ +HXCOMM See docs/devel/docs.rst for the format of this file. +HXCOMM HXCOMM Use DEFHEADING() to define headings in both help text and rST. HXCOMM Text between SRST and ERST is copied to the rST version and HXCOMM discarded from C version. From 71e269fb7bd14b5e4b055b6978a4ab193e11ee8e Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 9 Jan 2024 14:38:04 +0000 Subject: [PATCH 02/12] target/arm: arm_pamax() no longer needs to do feature propagation In arm_pamax(), we need to cope with the virt board calling this function on a CPU object which has been inited but not realize. We used to do propagation of feature-flag implications (such as "V7VE implies LPAE") at realize, so we have some code in arm_pamax() which manually checks for both V7VE and LPAE feature flags. In commit b8f7959f28c4f36 we moved the feature propagation for almost all features from realize to post-init. That means that now when the virt board calls arm_pamax(), the feature propagation has been done. So we can drop the manual propagation handling and check only for the feature we actually care about, which is ARM_FEATURE_LPAE. Retain the comment that the virt board is calling this function with a not completely realized CPU object, because that is a potential beartrap for later changes which is worth calling out. (Note that b8f7959f28c4f36 actually fixed a bug in the arm_pamax() handling: arm_pamax() was missing a check for ARM_FEATURE_V8, so it incorrectly thought that the qemu-system-arm 'max' CPU did not have LPAE and turned off 'highmem' support in the virt board. Following b8f7959f28c4f36 qemu-system-arm 'max' is treated the same as 'cortex-a15' and other v7 LPAE CPUs, because the generic feature propagation code does correctly propagate V8 -> V7VE -> LPAE.) Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20240109143804.1118307-1-peter.maydell@linaro.org --- target/arm/ptw.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/target/arm/ptw.c b/target/arm/ptw.c index 2d4fa8dbca..5eb3577bcd 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -95,7 +95,10 @@ static const uint8_t pamax_map[] = { [6] = 52, }; -/* The cpu-specific constant value of PAMax; also used by hw/arm/virt. */ +/* + * The cpu-specific constant value of PAMax; also used by hw/arm/virt. + * Note that machvirt_init calls this on a CPU that is inited but not realized! + */ unsigned int arm_pamax(ARMCPU *cpu) { if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { @@ -110,13 +113,8 @@ unsigned int arm_pamax(ARMCPU *cpu) return pamax_map[parange]; } - /* - * In machvirt_init, we call arm_pamax on a cpu that is not fully - * initialized, so we can't rely on the propagation done in realize. - */ - if (arm_feature(&cpu->env, ARM_FEATURE_LPAE) || - arm_feature(&cpu->env, ARM_FEATURE_V7VE)) { - /* v7 with LPAE */ + if (arm_feature(&cpu->env, ARM_FEATURE_LPAE)) { + /* v7 or v8 with LPAE */ return 40; } /* Anything else */ From 8cf1da729170b7cc47ebaca46912a4f37675dc95 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 9 Jan 2024 17:08:34 +0000 Subject: [PATCH 03/12] docs/system/arm/virt.rst: Improve 'highmem' option docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Improve the 'highmem' option docs to note that by default we assume that a 32-bit kernel on an LPAE-capable CPU has LPAE enabled, and what the consequences are. Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Reviewed-by: Eric Auger Message-id: 20240109170834.1387457-1-peter.maydell@linaro.org --- docs/system/arm/virt.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/system/arm/virt.rst b/docs/system/arm/virt.rst index 7c4c80180c..c245c52b7a 100644 --- a/docs/system/arm/virt.rst +++ b/docs/system/arm/virt.rst @@ -96,7 +96,13 @@ mte highmem Set ``on``/``off`` to enable/disable placing devices and RAM in physical address space above 32 bits. The default is ``on`` for machine types - later than ``virt-2.12``. + later than ``virt-2.12`` when the CPU supports an address space + bigger than 32 bits (i.e. 64-bit CPUs, and 32-bit CPUs with the + Large Physical Address Extension (LPAE) feature). If you want to + boot a 32-bit kernel which does not have ``CONFIG_LPAE`` enabled on + a CPU type which implements LPAE, you will need to manually set + this to ``off``; otherwise some devices, such as the PCI controller, + will not be accessible. compact-highmem Set ``on``/``off`` to enable/disable the compact layout for high memory regions. From c9948fdd023dd2031d868c8e26f387e6705c918c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?In=C3=A8s=20Varhol?= Date: Tue, 9 Jan 2024 17:06:02 +0100 Subject: [PATCH 04/12] hw/misc: Implement STM32L4x5 EXTI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Although very similar to the STM32F4xx EXTI, STM32L4x5 EXTI generates more than 32 event/interrupt requests and thus uses more registers than STM32F4xx EXTI which generates 23 event/interrupt requests. Acked-by: Alistair Francis Signed-off-by: Arnaud Minier Signed-off-by: Inès Varhol Reviewed-by: Philippe Mathieu-Daudé Message-id: 20240109160658.311932-2-ines.varhol@telecom-paris.fr Signed-off-by: Peter Maydell --- docs/system/arm/b-l475e-iot01a.rst | 5 +- hw/misc/Kconfig | 3 + hw/misc/meson.build | 1 + hw/misc/stm32l4x5_exti.c | 290 +++++++++++++++++++++++++++++ hw/misc/trace-events | 5 + include/hw/misc/stm32l4x5_exti.h | 51 +++++ 6 files changed, 352 insertions(+), 3 deletions(-) create mode 100644 hw/misc/stm32l4x5_exti.c create mode 100644 include/hw/misc/stm32l4x5_exti.h diff --git a/docs/system/arm/b-l475e-iot01a.rst b/docs/system/arm/b-l475e-iot01a.rst index 2b128e6b84..72f256ace7 100644 --- a/docs/system/arm/b-l475e-iot01a.rst +++ b/docs/system/arm/b-l475e-iot01a.rst @@ -12,17 +12,16 @@ USART, I2C, SPI, CAN and USB OTG, as well as a variety of sensors. Supported devices """"""""""""""""" -Currently, B-L475E-IOT01A machine's implementation is minimal, -it only supports the following device: +Currently B-L475E-IOT01A machine's only supports the following devices: - Cortex-M4F based STM32L4x5 SoC +- STM32L4x5 EXTI (Extended interrupts and events controller) Missing devices """"""""""""""" The B-L475E-IOT01A does *not* support the following devices: -- Extended interrupts and events controller (EXTI) - Reset and clock control (RCC) - Serial ports (UART) - System configuration controller (SYSCFG) diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig index cc8a8c1418..3efe3dc2cc 100644 --- a/hw/misc/Kconfig +++ b/hw/misc/Kconfig @@ -87,6 +87,9 @@ config STM32F4XX_SYSCFG config STM32F4XX_EXTI bool +config STM32L4X5_EXTI + bool + config MIPS_ITU bool diff --git a/hw/misc/meson.build b/hw/misc/meson.build index 36c20d5637..16db6e228d 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -110,6 +110,7 @@ system_ss.add(when: 'CONFIG_XLNX_VERSAL_TRNG', if_true: files( system_ss.add(when: 'CONFIG_STM32F2XX_SYSCFG', if_true: files('stm32f2xx_syscfg.c')) system_ss.add(when: 'CONFIG_STM32F4XX_SYSCFG', if_true: files('stm32f4xx_syscfg.c')) system_ss.add(when: 'CONFIG_STM32F4XX_EXTI', if_true: files('stm32f4xx_exti.c')) +system_ss.add(when: 'CONFIG_STM32L4X5_EXTI', if_true: files('stm32l4x5_exti.c')) system_ss.add(when: 'CONFIG_MPS2_FPGAIO', if_true: files('mps2-fpgaio.c')) system_ss.add(when: 'CONFIG_MPS2_SCC', if_true: files('mps2-scc.c')) diff --git a/hw/misc/stm32l4x5_exti.c b/hw/misc/stm32l4x5_exti.c new file mode 100644 index 0000000000..9fd859160d --- /dev/null +++ b/hw/misc/stm32l4x5_exti.c @@ -0,0 +1,290 @@ +/* + * STM32L4x5 EXTI (Extended interrupts and events controller) + * + * Copyright (c) 2023 Arnaud Minier + * Copyright (c) 2023 Samuel Tardieu + * Copyright (c) 2023 Inès Varhol + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * This work is based on the stm32f4xx_exti by Alistair Francis. + * Original code is licensed under the MIT License: + * + * Copyright (c) 2014 Alistair Francis + */ + +/* + * The reference used is the STMicroElectronics RM0351 Reference manual + * for STM32L4x5 and STM32L4x6 advanced Arm ® -based 32-bit MCUs. + * https://www.st.com/en/microcontrollers-microprocessors/stm32l4x5/documentation.html + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "trace.h" +#include "hw/irq.h" +#include "migration/vmstate.h" +#include "hw/misc/stm32l4x5_exti.h" + +#define EXTI_IMR1 0x00 +#define EXTI_EMR1 0x04 +#define EXTI_RTSR1 0x08 +#define EXTI_FTSR1 0x0C +#define EXTI_SWIER1 0x10 +#define EXTI_PR1 0x14 +#define EXTI_IMR2 0x20 +#define EXTI_EMR2 0x24 +#define EXTI_RTSR2 0x28 +#define EXTI_FTSR2 0x2C +#define EXTI_SWIER2 0x30 +#define EXTI_PR2 0x34 + +#define EXTI_NUM_GPIO_EVENT_IN_LINES 16 +#define EXTI_MAX_IRQ_PER_BANK 32 +#define EXTI_IRQS_BANK0 32 +#define EXTI_IRQS_BANK1 8 + +static const unsigned irqs_per_bank[EXTI_NUM_REGISTER] = { + EXTI_IRQS_BANK0, + EXTI_IRQS_BANK1, +}; + +static const uint32_t exti_romask[EXTI_NUM_REGISTER] = { + 0xff820000, /* 0b11111111_10000010_00000000_00000000 */ + 0x00000087, /* 0b00000000_00000000_00000000_10000111 */ +}; + +static unsigned regbank_index_by_irq(unsigned irq) +{ + return irq >= EXTI_MAX_IRQ_PER_BANK ? 1 : 0; +} + +static unsigned regbank_index_by_addr(hwaddr addr) +{ + return addr >= EXTI_IMR2 ? 1 : 0; +} + +static unsigned valid_mask(unsigned bank) +{ + return MAKE_64BIT_MASK(0, irqs_per_bank[bank]); +} + +static unsigned configurable_mask(unsigned bank) +{ + return valid_mask(bank) & ~exti_romask[bank]; +} + +static void stm32l4x5_exti_reset_hold(Object *obj) +{ + Stm32l4x5ExtiState *s = STM32L4X5_EXTI(obj); + + for (unsigned bank = 0; bank < EXTI_NUM_REGISTER; bank++) { + s->imr[bank] = exti_romask[bank]; + s->emr[bank] = 0x00000000; + s->rtsr[bank] = 0x00000000; + s->ftsr[bank] = 0x00000000; + s->swier[bank] = 0x00000000; + s->pr[bank] = 0x00000000; + } +} + +static void stm32l4x5_exti_set_irq(void *opaque, int irq, int level) +{ + Stm32l4x5ExtiState *s = opaque; + const unsigned bank = regbank_index_by_irq(irq); + const int oirq = irq; + + trace_stm32l4x5_exti_set_irq(irq, level); + + /* Shift the value to enable access in x2 registers. */ + irq %= EXTI_MAX_IRQ_PER_BANK; + + /* If the interrupt is masked, pr won't be raised */ + if (!extract32(s->imr[bank], irq, 1)) { + return; + } + + if (((1 << irq) & s->rtsr[bank]) && level) { + /* Rising Edge */ + s->pr[bank] |= 1 << irq; + qemu_irq_pulse(s->irq[oirq]); + } else if (((1 << irq) & s->ftsr[bank]) && !level) { + /* Falling Edge */ + s->pr[bank] |= 1 << irq; + qemu_irq_pulse(s->irq[oirq]); + } + /* + * In the following situations : + * - falling edge but rising trigger selected + * - rising edge but falling trigger selected + * - no trigger selected + * No action is required + */ +} + +static uint64_t stm32l4x5_exti_read(void *opaque, hwaddr addr, + unsigned int size) +{ + Stm32l4x5ExtiState *s = opaque; + uint32_t r = 0; + const unsigned bank = regbank_index_by_addr(addr); + + switch (addr) { + case EXTI_IMR1: + case EXTI_IMR2: + r = s->imr[bank]; + break; + case EXTI_EMR1: + case EXTI_EMR2: + r = s->emr[bank]; + break; + case EXTI_RTSR1: + case EXTI_RTSR2: + r = s->rtsr[bank]; + break; + case EXTI_FTSR1: + case EXTI_FTSR2: + r = s->ftsr[bank]; + break; + case EXTI_SWIER1: + case EXTI_SWIER2: + r = s->swier[bank]; + break; + case EXTI_PR1: + case EXTI_PR2: + r = s->pr[bank]; + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "STM32L4X5_exti_read: Bad offset 0x%" HWADDR_PRIx "\n", + addr); + break; + } + + trace_stm32l4x5_exti_read(addr, r); + + return r; +} + +static void stm32l4x5_exti_write(void *opaque, hwaddr addr, + uint64_t val64, unsigned int size) +{ + Stm32l4x5ExtiState *s = opaque; + const unsigned bank = regbank_index_by_addr(addr); + + trace_stm32l4x5_exti_write(addr, val64); + + switch (addr) { + case EXTI_IMR1: + case EXTI_IMR2: + s->imr[bank] = val64 & valid_mask(bank); + return; + case EXTI_EMR1: + case EXTI_EMR2: + s->emr[bank] = val64 & valid_mask(bank); + return; + case EXTI_RTSR1: + case EXTI_RTSR2: + s->rtsr[bank] = val64 & configurable_mask(bank); + return; + case EXTI_FTSR1: + case EXTI_FTSR2: + s->ftsr[bank] = val64 & configurable_mask(bank); + return; + case EXTI_SWIER1: + case EXTI_SWIER2: { + const uint32_t set = val64 & configurable_mask(bank); + const uint32_t pend = set & ~s->swier[bank] & s->imr[bank] & + ~s->pr[bank]; + s->swier[bank] = set; + s->pr[bank] |= pend; + for (unsigned i = 0; i < irqs_per_bank[bank]; i++) { + if (extract32(pend, i, 1)) { + qemu_irq_pulse(s->irq[i + 32 * bank]); + } + } + return; + } + case EXTI_PR1: + case EXTI_PR2: { + const uint32_t cleared = s->pr[bank] & val64 & configurable_mask(bank); + /* This bit is cleared by writing a 1 to it */ + s->pr[bank] &= ~cleared; + /* Software triggered interrupts are cleared as well */ + s->swier[bank] &= ~cleared; + return; + } + default: + qemu_log_mask(LOG_GUEST_ERROR, + "STM32L4X5_exti_write: Bad offset 0x%" HWADDR_PRIx "\n", + addr); + } +} + +static const MemoryRegionOps stm32l4x5_exti_ops = { + .read = stm32l4x5_exti_read, + .write = stm32l4x5_exti_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl.min_access_size = 4, + .impl.max_access_size = 4, + .impl.unaligned = false, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .valid.unaligned = false, +}; + +static void stm32l4x5_exti_init(Object *obj) +{ + Stm32l4x5ExtiState *s = STM32L4X5_EXTI(obj); + + for (size_t i = 0; i < EXTI_NUM_INTERRUPT_OUT_LINES; i++) { + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq[i]); + } + + memory_region_init_io(&s->mmio, obj, &stm32l4x5_exti_ops, s, + TYPE_STM32L4X5_EXTI, 0x400); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); + + qdev_init_gpio_in(DEVICE(obj), stm32l4x5_exti_set_irq, + EXTI_NUM_GPIO_EVENT_IN_LINES); +} + +static const VMStateDescription vmstate_stm32l4x5_exti = { + .name = TYPE_STM32L4X5_EXTI, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(imr, Stm32l4x5ExtiState, EXTI_NUM_REGISTER), + VMSTATE_UINT32_ARRAY(emr, Stm32l4x5ExtiState, EXTI_NUM_REGISTER), + VMSTATE_UINT32_ARRAY(rtsr, Stm32l4x5ExtiState, EXTI_NUM_REGISTER), + VMSTATE_UINT32_ARRAY(ftsr, Stm32l4x5ExtiState, EXTI_NUM_REGISTER), + VMSTATE_UINT32_ARRAY(swier, Stm32l4x5ExtiState, EXTI_NUM_REGISTER), + VMSTATE_UINT32_ARRAY(pr, Stm32l4x5ExtiState, EXTI_NUM_REGISTER), + VMSTATE_END_OF_LIST() + } +}; + +static void stm32l4x5_exti_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + dc->vmsd = &vmstate_stm32l4x5_exti; + rc->phases.hold = stm32l4x5_exti_reset_hold; +} + +static const TypeInfo stm32l4x5_exti_types[] = { + { + .name = TYPE_STM32L4X5_EXTI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Stm32l4x5ExtiState), + .instance_init = stm32l4x5_exti_init, + .class_init = stm32l4x5_exti_class_init, + } +}; + +DEFINE_TYPES(stm32l4x5_exti_types) diff --git a/hw/misc/trace-events b/hw/misc/trace-events index 85725506bf..fccd3204bf 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -163,6 +163,11 @@ stm32f4xx_exti_set_irq(int irq, int level) "Set EXTI: %d to %d" stm32f4xx_exti_read(uint64_t addr) "reg read: addr: 0x%" PRIx64 " " stm32f4xx_exti_write(uint64_t addr, uint64_t data) "reg write: addr: 0x%" PRIx64 " val: 0x%" PRIx64 "" +# stm32l4x5_exti.c +stm32l4x5_exti_set_irq(int irq, int level) "Set EXTI: %d to %d" +stm32l4x5_exti_read(uint64_t addr, uint64_t data) "reg read: addr: 0x%" PRIx64 " val: 0x%" PRIx64 "" +stm32l4x5_exti_write(uint64_t addr, uint64_t data) "reg write: addr: 0x%" PRIx64 " val: 0x%" PRIx64 "" + # tz-mpc.c tz_mpc_reg_read(uint32_t offset, uint64_t data, unsigned size) "TZ MPC regs read: offset 0x%x data 0x%" PRIx64 " size %u" tz_mpc_reg_write(uint32_t offset, uint64_t data, unsigned size) "TZ MPC regs write: offset 0x%x data 0x%" PRIx64 " size %u" diff --git a/include/hw/misc/stm32l4x5_exti.h b/include/hw/misc/stm32l4x5_exti.h new file mode 100644 index 0000000000..be961d2f01 --- /dev/null +++ b/include/hw/misc/stm32l4x5_exti.h @@ -0,0 +1,51 @@ +/* + * STM32L4x5 EXTI (Extended interrupts and events controller) + * + * Copyright (c) 2023 Arnaud Minier + * Copyright (c) 2023 Inès Varhol + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * This work is based on the stm32f4xx_exti by Alistair Francis. + * Original code is licensed under the MIT License: + * + * Copyright (c) 2014 Alistair Francis + */ + +/* + * The reference used is the STMicroElectronics RM0351 Reference manual + * for STM32L4x5 and STM32L4x6 advanced Arm ® -based 32-bit MCUs. + * https://www.st.com/en/microcontrollers-microprocessors/stm32l4x5/documentation.html + */ + +#ifndef HW_STM32L4X5_EXTI_H +#define HW_STM32L4X5_EXTI_H + +#include "hw/sysbus.h" +#include "qom/object.h" + +#define TYPE_STM32L4X5_EXTI "stm32l4x5-exti" +OBJECT_DECLARE_SIMPLE_TYPE(Stm32l4x5ExtiState, STM32L4X5_EXTI) + +#define EXTI_NUM_INTERRUPT_OUT_LINES 40 +#define EXTI_NUM_REGISTER 2 + +struct Stm32l4x5ExtiState { + SysBusDevice parent_obj; + + MemoryRegion mmio; + + uint32_t imr[EXTI_NUM_REGISTER]; + uint32_t emr[EXTI_NUM_REGISTER]; + uint32_t rtsr[EXTI_NUM_REGISTER]; + uint32_t ftsr[EXTI_NUM_REGISTER]; + uint32_t swier[EXTI_NUM_REGISTER]; + uint32_t pr[EXTI_NUM_REGISTER]; + + qemu_irq irq[EXTI_NUM_INTERRUPT_OUT_LINES]; +}; + +#endif From 52671f69f7a4cdc235903fd9005ac4b2e4770593 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?In=C3=A8s=20Varhol?= Date: Tue, 9 Jan 2024 17:06:03 +0100 Subject: [PATCH 05/12] hw/arm: Connect STM32L4x5 EXTI to STM32L4x5 SoC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tested-by: Philippe Mathieu-Daudé Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Signed-off-by: Arnaud Minier Signed-off-by: Inès Varhol Message-id: 20240109160658.311932-3-ines.varhol@telecom-paris.fr Signed-off-by: Peter Maydell --- hw/arm/Kconfig | 1 + hw/arm/stm32l4x5_soc.c | 52 +++++++++++++++++++++++++++++++++- include/hw/arm/stm32l4x5_soc.h | 3 ++ 3 files changed, 55 insertions(+), 1 deletion(-) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 39d255425b..da9c6e5064 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -459,6 +459,7 @@ config STM32L4X5_SOC bool select ARM_V7M select OR_IRQ + select STM32L4X5_EXTI config XLNX_ZYNQMP_ARM bool diff --git a/hw/arm/stm32l4x5_soc.c b/hw/arm/stm32l4x5_soc.c index 159d5315c9..756eb69488 100644 --- a/hw/arm/stm32l4x5_soc.c +++ b/hw/arm/stm32l4x5_soc.c @@ -36,10 +36,51 @@ #define SRAM2_BASE_ADDRESS 0x10000000 #define SRAM2_SIZE (32 * KiB) +#define EXTI_ADDR 0x40010400 + +#define NUM_EXTI_IRQ 40 +/* Match exti line connections with their CPU IRQ number */ +/* See Vector Table (Reference Manual p.396) */ +static const int exti_irq[NUM_EXTI_IRQ] = { + 6, /* GPIO[0] */ + 7, /* GPIO[1] */ + 8, /* GPIO[2] */ + 9, /* GPIO[3] */ + 10, /* GPIO[4] */ + 23, 23, 23, 23, 23, /* GPIO[5..9] */ + 40, 40, 40, 40, 40, 40, /* GPIO[10..15] */ + 1, /* PVD */ + 67, /* OTG_FS_WKUP, Direct */ + 41, /* RTC_ALARM */ + 2, /* RTC_TAMP_STAMP2/CSS_LSE */ + 3, /* RTC wakeup timer */ + 63, /* COMP1 */ + 63, /* COMP2 */ + 31, /* I2C1 wakeup, Direct */ + 33, /* I2C2 wakeup, Direct */ + 72, /* I2C3 wakeup, Direct */ + 37, /* USART1 wakeup, Direct */ + 38, /* USART2 wakeup, Direct */ + 39, /* USART3 wakeup, Direct */ + 52, /* UART4 wakeup, Direct */ + 53, /* UART4 wakeup, Direct */ + 70, /* LPUART1 wakeup, Direct */ + 65, /* LPTIM1, Direct */ + 66, /* LPTIM2, Direct */ + 76, /* SWPMI1 wakeup, Direct */ + 1, /* PVM1 wakeup */ + 1, /* PVM2 wakeup */ + 1, /* PVM3 wakeup */ + 1, /* PVM4 wakeup */ + 78 /* LCD wakeup, Direct */ +}; + static void stm32l4x5_soc_initfn(Object *obj) { Stm32l4x5SocState *s = STM32L4X5_SOC(obj); + object_initialize_child(obj, "exti", &s->exti, TYPE_STM32L4X5_EXTI); + s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0); s->refclk = qdev_init_clock_in(DEVICE(s), "refclk", NULL, NULL, 0); } @@ -51,6 +92,7 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp) const Stm32l4x5SocClass *sc = STM32L4X5_SOC_GET_CLASS(dev_soc); MemoryRegion *system_memory = get_system_memory(); DeviceState *armv7m; + SysBusDevice *busdev; /* * We use s->refclk internally and only define it with qdev_init_clock_in() @@ -113,6 +155,15 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp) return; } + busdev = SYS_BUS_DEVICE(&s->exti); + if (!sysbus_realize(busdev, errp)) { + return; + } + sysbus_mmio_map(busdev, 0, EXTI_ADDR); + for (unsigned i = 0; i < NUM_EXTI_IRQ; i++) { + sysbus_connect_irq(busdev, i, qdev_get_gpio_in(armv7m, exti_irq[i])); + } + /* APB1 BUS */ create_unimplemented_device("TIM2", 0x40000000, 0x400); create_unimplemented_device("TIM3", 0x40000400, 0x400); @@ -153,7 +204,6 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp) create_unimplemented_device("SYSCFG", 0x40010000, 0x30); create_unimplemented_device("VREFBUF", 0x40010030, 0x1D0); create_unimplemented_device("COMP", 0x40010200, 0x200); - create_unimplemented_device("EXTI", 0x40010400, 0x400); /* RESERVED: 0x40010800, 0x1400 */ create_unimplemented_device("FIREWALL", 0x40011C00, 0x400); /* RESERVED: 0x40012000, 0x800 */ diff --git a/include/hw/arm/stm32l4x5_soc.h b/include/hw/arm/stm32l4x5_soc.h index 2fd44a36a9..f7305568dc 100644 --- a/include/hw/arm/stm32l4x5_soc.h +++ b/include/hw/arm/stm32l4x5_soc.h @@ -26,6 +26,7 @@ #include "exec/memory.h" #include "hw/arm/armv7m.h" +#include "hw/misc/stm32l4x5_exti.h" #include "qom/object.h" #define TYPE_STM32L4X5_SOC "stm32l4x5-soc" @@ -39,6 +40,8 @@ struct Stm32l4x5SocState { ARMv7MState armv7m; + Stm32l4x5ExtiState exti; + MemoryRegion sram1; MemoryRegion sram2; MemoryRegion flash; From 4cb445717eed79b3b1db0bfe5c4ccf838f895751 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?In=C3=A8s=20Varhol?= Date: Tue, 9 Jan 2024 17:06:04 +0100 Subject: [PATCH 06/12] tests/qtest: Add STM32L4x5 EXTI QTest testcase MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Acked-by: Alistair Francis Signed-off-by: Arnaud Minier Signed-off-by: Inès Varhol Message-id: 20240109160658.311932-4-ines.varhol@telecom-paris.fr Signed-off-by: Peter Maydell --- tests/qtest/meson.build | 5 + tests/qtest/stm32l4x5_exti-test.c | 524 ++++++++++++++++++++++++++++++ 2 files changed, 529 insertions(+) create mode 100644 tests/qtest/stm32l4x5_exti-test.c diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index fd40136fa9..07fdd2dc1a 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -195,6 +195,10 @@ qtests_aspeed = \ ['aspeed_hace-test', 'aspeed_smc-test', 'aspeed_gpio-test'] + +qtests_stm32l4x5 = \ + ['stm32l4x5_exti-test'] + qtests_arm = \ (config_all_devices.has_key('CONFIG_MPS2') ? ['sse-timer-test'] : []) + \ (config_all_devices.has_key('CONFIG_CMSDK_APB_DUALTIMER') ? ['cmsdk-apb-dualtimer-test'] : []) + \ @@ -208,6 +212,7 @@ qtests_arm = \ (config_all_devices.has_key('CONFIG_TPM_TIS_I2C') ? ['tpm-tis-i2c-test'] : []) + \ (config_all_devices.has_key('CONFIG_VEXPRESS') ? ['test-arm-mptimer'] : []) + \ (config_all_devices.has_key('CONFIG_MICROBIT') ? ['microbit-test'] : []) + \ + (config_all_devices.has_key('CONFIG_STM32L4X5_SOC') ? qtests_stm32l4x5 : []) + \ ['arm-cpu-features', 'boot-serial-test'] diff --git a/tests/qtest/stm32l4x5_exti-test.c b/tests/qtest/stm32l4x5_exti-test.c new file mode 100644 index 0000000000..c390077713 --- /dev/null +++ b/tests/qtest/stm32l4x5_exti-test.c @@ -0,0 +1,524 @@ +/* + * QTest testcase for STM32L4x5_EXTI + * + * Copyright (c) 2023 Arnaud Minier + * Copyright (c) 2023 Inès Varhol + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest-single.h" + +#define EXTI_BASE_ADDR 0x40010400 +#define EXTI_IMR1 0x00 +#define EXTI_EMR1 0x04 +#define EXTI_RTSR1 0x08 +#define EXTI_FTSR1 0x0C +#define EXTI_SWIER1 0x10 +#define EXTI_PR1 0x14 +#define EXTI_IMR2 0x20 +#define EXTI_EMR2 0x24 +#define EXTI_RTSR2 0x28 +#define EXTI_FTSR2 0x2C +#define EXTI_SWIER2 0x30 +#define EXTI_PR2 0x34 + +#define NVIC_ISER 0xE000E100 +#define NVIC_ISPR 0xE000E200 +#define NVIC_ICPR 0xE000E280 + +#define EXTI0_IRQ 6 +#define EXTI1_IRQ 7 +#define EXTI35_IRQ 1 + +static void enable_nvic_irq(unsigned int n) +{ + writel(NVIC_ISER, 1 << n); +} + +static void unpend_nvic_irq(unsigned int n) +{ + writel(NVIC_ICPR, 1 << n); +} + +static bool check_nvic_pending(unsigned int n) +{ + return readl(NVIC_ISPR) & (1 << n); +} + +static void exti_writel(unsigned int offset, uint32_t value) +{ + writel(EXTI_BASE_ADDR + offset, value); +} + +static uint32_t exti_readl(unsigned int offset) +{ + return readl(EXTI_BASE_ADDR + offset); +} + +static void exti_set_irq(int num, int level) +{ + qtest_set_irq_in(global_qtest, "/machine/soc/exti", NULL, + num, level); +} + +static void test_reg_write_read(void) +{ + /* Test that non-reserved bits in xMR and xTSR can be set and cleared */ + + exti_writel(EXTI_IMR1, 0xFFFFFFFF); + g_assert_cmpuint(exti_readl(EXTI_IMR1), ==, 0xFFFFFFFF); + exti_writel(EXTI_IMR1, 0x00000000); + g_assert_cmpuint(exti_readl(EXTI_IMR1), ==, 0x00000000); + + exti_writel(EXTI_EMR1, 0xFFFFFFFF); + g_assert_cmpuint(exti_readl(EXTI_EMR1), ==, 0xFFFFFFFF); + exti_writel(EXTI_EMR1, 0x00000000); + g_assert_cmpuint(exti_readl(EXTI_EMR1), ==, 0x00000000); + + exti_writel(EXTI_RTSR1, 0xFFFFFFFF); + g_assert_cmpuint(exti_readl(EXTI_RTSR1), ==, 0x007DFFFF); + exti_writel(EXTI_RTSR1, 0x00000000); + g_assert_cmpuint(exti_readl(EXTI_RTSR1), ==, 0x00000000); + + exti_writel(EXTI_FTSR1, 0xFFFFFFFF); + g_assert_cmpuint(exti_readl(EXTI_FTSR1), ==, 0x007DFFFF); + exti_writel(EXTI_FTSR1, 0x00000000); + g_assert_cmpuint(exti_readl(EXTI_FTSR1), ==, 0x00000000); + + exti_writel(EXTI_IMR2, 0xFFFFFFFF); + g_assert_cmpuint(exti_readl(EXTI_IMR2), ==, 0x000000FF); + exti_writel(EXTI_IMR2, 0x00000000); + g_assert_cmpuint(exti_readl(EXTI_IMR2), ==, 0x00000000); + + exti_writel(EXTI_EMR2, 0xFFFFFFFF); + g_assert_cmpuint(exti_readl(EXTI_EMR2), ==, 0x000000FF); + exti_writel(EXTI_EMR2, 0x00000000); + g_assert_cmpuint(exti_readl(EXTI_EMR2), ==, 0x00000000); + + exti_writel(EXTI_RTSR2, 0xFFFFFFFF); + g_assert_cmpuint(exti_readl(EXTI_RTSR2), ==, 0x00000078); + exti_writel(EXTI_RTSR2, 0x00000000); + g_assert_cmpuint(exti_readl(EXTI_RTSR2), ==, 0x00000000); + + exti_writel(EXTI_FTSR2, 0xFFFFFFFF); + g_assert_cmpuint(exti_readl(EXTI_FTSR2), ==, 0x00000078); + exti_writel(EXTI_FTSR2, 0x00000000); + g_assert_cmpuint(exti_readl(EXTI_FTSR2), ==, 0x00000000); +} + +static void test_direct_lines_write(void) +{ + /* Test that direct lines reserved bits are not written to */ + + exti_writel(EXTI_RTSR1, 0xFF820000); + g_assert_cmpuint(exti_readl(EXTI_RTSR1), ==, 0x00000000); + + exti_writel(EXTI_FTSR1, 0xFF820000); + g_assert_cmpuint(exti_readl(EXTI_FTSR1), ==, 0x00000000); + + exti_writel(EXTI_SWIER1, 0xFF820000); + g_assert_cmpuint(exti_readl(EXTI_SWIER1), ==, 0x00000000); + + exti_writel(EXTI_PR1, 0xFF820000); + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + + exti_writel(EXTI_RTSR2, 0x00000087); + g_assert_cmpuint(exti_readl(EXTI_RTSR2), ==, 0x00000000); + + exti_writel(EXTI_FTSR2, 0x00000087); + g_assert_cmpuint(exti_readl(EXTI_FTSR2), ==, 0x00000000); + + exti_writel(EXTI_SWIER2, 0x00000087); + g_assert_cmpuint(exti_readl(EXTI_SWIER2), ==, 0x00000000); + + exti_writel(EXTI_PR2, 0x00000087); + g_assert_cmpuint(exti_readl(EXTI_PR2), ==, 0x00000000); +} + +static void test_reserved_bits_write(void) +{ + /* Test that reserved bits stay are not written to */ + + exti_writel(EXTI_IMR2, 0xFFFFFF00); + g_assert_cmpuint(exti_readl(EXTI_IMR2), ==, 0x00000000); + + exti_writel(EXTI_EMR2, 0xFFFFFF00); + g_assert_cmpuint(exti_readl(EXTI_EMR2), ==, 0x00000000); + + exti_writel(EXTI_RTSR2, 0xFFFFFF00); + g_assert_cmpuint(exti_readl(EXTI_RTSR2), ==, 0x00000000); + + exti_writel(EXTI_FTSR2, 0xFFFFFF00); + g_assert_cmpuint(exti_readl(EXTI_FTSR2), ==, 0x00000000); + + exti_writel(EXTI_SWIER2, 0xFFFFFF00); + g_assert_cmpuint(exti_readl(EXTI_SWIER2), ==, 0x00000000); + + exti_writel(EXTI_PR2, 0xFFFFFF00); + g_assert_cmpuint(exti_readl(EXTI_PR2), ==, 0x00000000); +} + +static void test_software_interrupt(void) +{ + /* + * Test that we can launch a software irq by : + * - enabling its line in IMR + * - and then setting a bit from '0' to '1' in SWIER + * + * And that the interruption stays pending in NVIC + * even after clearing the pending bit in PR. + */ + + /* + * Testing interrupt line EXTI0 + * Bit 0 in EXTI_*1 registers (EXTI0) corresponds to GPIO Px_0 + */ + + enable_nvic_irq(EXTI0_IRQ); + /* Check that there are no interrupts already pending in PR */ + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + /* Check that this specific interrupt isn't pending in NVIC */ + g_assert_false(check_nvic_pending(EXTI0_IRQ)); + + /* Enable interrupt line EXTI0 */ + exti_writel(EXTI_IMR1, 0x00000001); + /* Set the right SWIER bit from '0' to '1' */ + exti_writel(EXTI_SWIER1, 0x00000000); + exti_writel(EXTI_SWIER1, 0x00000001); + + /* Check that the write in SWIER was effective */ + g_assert_cmpuint(exti_readl(EXTI_SWIER1), ==, 0x00000001); + /* Check that the corresponding pending bit in PR is set */ + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000001); + /* Check that the corresponding interrupt is pending in the NVIC */ + g_assert_true(check_nvic_pending(EXTI0_IRQ)); + + /* Clear the pending bit in PR */ + exti_writel(EXTI_PR1, 0x00000001); + + /* Check that the write in PR was effective */ + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + /* Check that the corresponding bit in SWIER was cleared */ + g_assert_cmpuint(exti_readl(EXTI_SWIER1), ==, 0x00000000); + /* Check that the interrupt is still pending in the NVIC */ + g_assert_true(check_nvic_pending(EXTI0_IRQ)); + + /* + * Testing interrupt line EXTI35 + * Bit 3 in EXTI_*2 registers (EXTI35) corresponds to PVM 1 Wakeup + */ + + enable_nvic_irq(EXTI35_IRQ); + /* Check that there are no interrupts already pending */ + g_assert_cmpuint(exti_readl(EXTI_PR2), ==, 0x00000000); + g_assert_false(check_nvic_pending(EXTI35_IRQ)); + + /* Enable interrupt line EXTI0 */ + exti_writel(EXTI_IMR2, 0x00000008); + /* Set the right SWIER bit from '0' to '1' */ + exti_writel(EXTI_SWIER2, 0x00000000); + exti_writel(EXTI_SWIER2, 0x00000008); + + /* Check that the write in SWIER was effective */ + g_assert_cmpuint(exti_readl(EXTI_SWIER2), ==, 0x00000008); + /* Check that the corresponding pending bit in PR is set */ + g_assert_cmpuint(exti_readl(EXTI_PR2), ==, 0x00000008); + /* Check that the corresponding interrupt is pending in the NVIC */ + g_assert_true(check_nvic_pending(EXTI35_IRQ)); + + /* Clear the pending bit in PR */ + exti_writel(EXTI_PR2, 0x00000008); + + /* Check that the write in PR was effective */ + g_assert_cmpuint(exti_readl(EXTI_PR2), ==, 0x00000000); + /* Check that the corresponding bit in SWIER was cleared */ + g_assert_cmpuint(exti_readl(EXTI_SWIER2), ==, 0x00000000); + /* Check that the interrupt is still pending in the NVIC */ + g_assert_true(check_nvic_pending(EXTI35_IRQ)); + + /* Clean NVIC */ + unpend_nvic_irq(EXTI0_IRQ); + g_assert_false(check_nvic_pending(EXTI0_IRQ)); + unpend_nvic_irq(EXTI35_IRQ); + g_assert_false(check_nvic_pending(EXTI35_IRQ)); +} + +static void test_edge_selector(void) +{ + enable_nvic_irq(EXTI0_IRQ); + + /* Configure EXTI line 0 irq on rising edge */ + exti_set_irq(0, 1); + exti_writel(EXTI_IMR1, 0x00000001); + exti_writel(EXTI_RTSR1, 0x00000001); + exti_writel(EXTI_FTSR1, 0x00000000); + + /* Test that an irq is raised on rising edge only */ + exti_set_irq(0, 0); + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + g_assert_false(check_nvic_pending(EXTI0_IRQ)); + + exti_set_irq(0, 1); + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000001); + g_assert_true(check_nvic_pending(EXTI0_IRQ)); + + /* Clean the test */ + exti_writel(EXTI_PR1, 0x00000001); + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + unpend_nvic_irq(EXTI0_IRQ); + g_assert_false(check_nvic_pending(EXTI0_IRQ)); + + /* Configure EXTI line 0 irq on falling edge */ + exti_set_irq(0, 0); + exti_writel(EXTI_IMR1, 0x00000001); + exti_writel(EXTI_RTSR1, 0x00000000); + exti_writel(EXTI_FTSR1, 0x00000001); + + /* Test that an irq is raised on falling edge only */ + exti_set_irq(0, 1); + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + g_assert_false(check_nvic_pending(EXTI0_IRQ)); + + exti_set_irq(0, 0); + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000001); + g_assert_true(check_nvic_pending(EXTI0_IRQ)); + + /* Clean the test */ + exti_writel(EXTI_PR1, 0x00000001); + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + unpend_nvic_irq(EXTI0_IRQ); + g_assert_false(check_nvic_pending(EXTI0_IRQ)); + + /* Configure EXTI line 0 irq on falling and rising edge */ + exti_writel(EXTI_IMR1, 0x00000001); + exti_writel(EXTI_RTSR1, 0x00000001); + exti_writel(EXTI_FTSR1, 0x00000001); + + /* Test that an irq is raised on rising edge */ + exti_set_irq(0, 1); + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000001); + g_assert_true(check_nvic_pending(EXTI0_IRQ)); + + /* Clean the test */ + exti_writel(EXTI_PR1, 0x00000001); + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + unpend_nvic_irq(EXTI0_IRQ); + g_assert_false(check_nvic_pending(EXTI0_IRQ)); + + /* Test that an irq is raised on falling edge */ + exti_set_irq(0, 0); + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000001); + g_assert_true(check_nvic_pending(EXTI0_IRQ)); + + /* Clean the test */ + exti_writel(EXTI_PR1, 0x00000001); + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + unpend_nvic_irq(EXTI0_IRQ); + g_assert_false(check_nvic_pending(EXTI0_IRQ)); + + /* Configure EXTI line 0 irq without selecting an edge trigger */ + exti_writel(EXTI_IMR1, 0x00000001); + exti_writel(EXTI_RTSR1, 0x00000000); + exti_writel(EXTI_FTSR1, 0x00000000); + + /* Test that no irq is raised */ + exti_set_irq(0, 1); + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + g_assert_false(check_nvic_pending(EXTI0_IRQ)); + + exti_set_irq(0, 0); + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + g_assert_false(check_nvic_pending(EXTI0_IRQ)); +} + +static void test_no_software_interrupt(void) +{ + /* + * Test that software irq doesn't happen when : + * - corresponding bit in IMR isn't set + * - SWIER is set to 1 before IMR is set to 1 + */ + + /* + * Testing interrupt line EXTI0 + * Bit 0 in EXTI_*1 registers (EXTI0) corresponds to GPIO Px_0 + */ + + enable_nvic_irq(EXTI0_IRQ); + /* Check that there are no interrupts already pending in PR */ + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + /* Check that this specific interrupt isn't pending in NVIC */ + g_assert_false(check_nvic_pending(EXTI0_IRQ)); + + /* Mask interrupt line EXTI0 */ + exti_writel(EXTI_IMR1, 0x00000000); + /* Set the corresponding SWIER bit from '0' to '1' */ + exti_writel(EXTI_SWIER1, 0x00000000); + exti_writel(EXTI_SWIER1, 0x00000001); + + /* Check that the write in SWIER was effective */ + g_assert_cmpuint(exti_readl(EXTI_SWIER1), ==, 0x00000001); + /* Check that the pending bit in PR wasn't set */ + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + /* Check that the interrupt isn't pending in NVIC */ + g_assert_false(check_nvic_pending(EXTI0_IRQ)); + + /* Enable interrupt line EXTI0 */ + exti_writel(EXTI_IMR1, 0x00000001); + + /* Check that the pending bit in PR wasn't set */ + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + /* Check that the interrupt isn't pending in NVIC */ + g_assert_false(check_nvic_pending(EXTI0_IRQ)); + + /* + * Testing interrupt line EXTI35 + * Bit 3 in EXTI_*2 registers (EXTI35) corresponds to PVM 1 Wakeup + */ + + enable_nvic_irq(EXTI35_IRQ); + /* Check that there are no interrupts already pending in PR */ + g_assert_cmpuint(exti_readl(EXTI_PR2), ==, 0x00000000); + /* Check that this specific interrupt isn't pending in NVIC */ + g_assert_false(check_nvic_pending(EXTI35_IRQ)); + + /* Mask interrupt line EXTI35 */ + exti_writel(EXTI_IMR2, 0x00000000); + /* Set the corresponding SWIER bit from '0' to '1' */ + exti_writel(EXTI_SWIER2, 0x00000000); + exti_writel(EXTI_SWIER2, 0x00000008); + + /* Check that the write in SWIER was effective */ + g_assert_cmpuint(exti_readl(EXTI_SWIER2), ==, 0x00000008); + /* Check that the pending bit in PR wasn't set */ + g_assert_cmpuint(exti_readl(EXTI_PR2), ==, 0x00000000); + /* Check that the interrupt isn't pending in NVIC */ + g_assert_false(check_nvic_pending(EXTI35_IRQ)); + + /* Enable interrupt line EXTI35 */ + exti_writel(EXTI_IMR2, 0x00000008); + + /* Check that the pending bit in PR wasn't set */ + g_assert_cmpuint(exti_readl(EXTI_PR2), ==, 0x00000000); + /* Check that the interrupt isn't pending in NVIC */ + g_assert_false(check_nvic_pending(EXTI35_IRQ)); +} + +static void test_masked_interrupt(void) +{ + /* + * Test that irq doesn't happen when : + * - corresponding bit in IMR isn't set + * - SWIER is set to 1 before IMR is set to 1 + */ + + /* + * Testing interrupt line EXTI1 + * with rising edge from GPIOx pin 1 + */ + + enable_nvic_irq(EXTI1_IRQ); + /* Check that there are no interrupts already pending in PR */ + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + /* Check that this specific interrupt isn't pending in NVIC */ + g_assert_false(check_nvic_pending(EXTI1_IRQ)); + + /* Mask interrupt line EXTI1 */ + exti_writel(EXTI_IMR1, 0x00000000); + + /* Configure interrupt on rising edge */ + exti_writel(EXTI_RTSR1, 0x00000002); + + /* Simulate rising edge from GPIO line 1 */ + exti_set_irq(1, 1); + + /* Check that the pending bit in PR wasn't set */ + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + /* Check that the interrupt isn't pending in NVIC */ + g_assert_false(check_nvic_pending(EXTI1_IRQ)); + + /* Enable interrupt line EXTI1 */ + exti_writel(EXTI_IMR1, 0x00000002); + + /* Check that the pending bit in PR wasn't set */ + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + /* Check that the interrupt isn't pending in NVIC */ + g_assert_false(check_nvic_pending(EXTI1_IRQ)); +} + +static void test_interrupt(void) +{ + /* + * Test that we can launch an irq by : + * - enabling its line in IMR + * - configuring interrupt on rising edge + * - and then setting the input line from '0' to '1' + * + * And that the interruption stays pending in NVIC + * even after clearing the pending bit in PR. + */ + + /* + * Testing interrupt line EXTI1 + * with rising edge from GPIOx pin 1 + */ + + enable_nvic_irq(EXTI1_IRQ); + /* Check that there are no interrupts already pending in PR */ + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + /* Check that this specific interrupt isn't pending in NVIC */ + g_assert_false(check_nvic_pending(EXTI1_IRQ)); + + /* Enable interrupt line EXTI1 */ + exti_writel(EXTI_IMR1, 0x00000002); + + /* Configure interrupt on rising edge */ + exti_writel(EXTI_RTSR1, 0x00000002); + + /* Simulate rising edge from GPIO line 1 */ + exti_set_irq(1, 1); + + /* Check that the pending bit in PR was set */ + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000002); + /* Check that the interrupt is pending in NVIC */ + g_assert_true(check_nvic_pending(EXTI1_IRQ)); + + /* Clear the pending bit in PR */ + exti_writel(EXTI_PR1, 0x00000002); + + /* Check that the write in PR was effective */ + g_assert_cmpuint(exti_readl(EXTI_PR1), ==, 0x00000000); + /* Check that the interrupt is still pending in the NVIC */ + g_assert_true(check_nvic_pending(EXTI1_IRQ)); + + /* Clean NVIC */ + unpend_nvic_irq(EXTI1_IRQ); + g_assert_false(check_nvic_pending(EXTI1_IRQ)); +} + +int main(int argc, char **argv) +{ + int ret; + + g_test_init(&argc, &argv, NULL); + g_test_set_nonfatal_assertions(); + qtest_add_func("stm32l4x5/exti/direct_lines", test_direct_lines_write); + qtest_add_func("stm32l4x5/exti/reserved_bits", test_reserved_bits_write); + qtest_add_func("stm32l4x5/exti/reg_write_read", test_reg_write_read); + qtest_add_func("stm32l4x5/exti/no_software_interrupt", + test_no_software_interrupt); + qtest_add_func("stm32l4x5/exti/software_interrupt", + test_software_interrupt); + qtest_add_func("stm32l4x5/exti/masked_interrupt", test_masked_interrupt); + qtest_add_func("stm32l4x5/exti/interrupt", test_interrupt); + qtest_add_func("stm32l4x5/exti/test_edge_selector", test_edge_selector); + + qtest_start("-machine b-l475e-iot01a"); + ret = g_test_run(); + qtest_end(); + + return ret; +} From 20936684b6dd02eec35591661553a57f3515cf5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?In=C3=A8s=20Varhol?= Date: Tue, 9 Jan 2024 20:41:57 +0100 Subject: [PATCH 07/12] hw/misc: Implement STM32L4x5 SYSCFG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Acked-by: Alistair Francis Signed-off-by: Arnaud Minier Signed-off-by: Inès Varhol Message-id: 20240109194438.70934-2-ines.varhol@telecom-paris.fr Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- docs/system/arm/b-l475e-iot01a.rst | 2 +- hw/misc/Kconfig | 3 + hw/misc/meson.build | 1 + hw/misc/stm32l4x5_syscfg.c | 266 +++++++++++++++++++++++++++++ hw/misc/trace-events | 6 + include/hw/misc/stm32l4x5_syscfg.h | 54 ++++++ 6 files changed, 331 insertions(+), 1 deletion(-) create mode 100644 hw/misc/stm32l4x5_syscfg.c create mode 100644 include/hw/misc/stm32l4x5_syscfg.h diff --git a/docs/system/arm/b-l475e-iot01a.rst b/docs/system/arm/b-l475e-iot01a.rst index 72f256ace7..1a021b306a 100644 --- a/docs/system/arm/b-l475e-iot01a.rst +++ b/docs/system/arm/b-l475e-iot01a.rst @@ -16,6 +16,7 @@ Currently B-L475E-IOT01A machine's only supports the following devices: - Cortex-M4F based STM32L4x5 SoC - STM32L4x5 EXTI (Extended interrupts and events controller) +- STM32L4x5 SYSCFG (System configuration controller) Missing devices """"""""""""""" @@ -24,7 +25,6 @@ The B-L475E-IOT01A does *not* support the following devices: - Reset and clock control (RCC) - Serial ports (UART) -- System configuration controller (SYSCFG) - General-purpose I/Os (GPIO) - Analog to Digital Converter (ADC) - SPI controller diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig index 3efe3dc2cc..4fc6b29b43 100644 --- a/hw/misc/Kconfig +++ b/hw/misc/Kconfig @@ -90,6 +90,9 @@ config STM32F4XX_EXTI config STM32L4X5_EXTI bool +config STM32L4X5_SYSCFG + bool + config MIPS_ITU bool diff --git a/hw/misc/meson.build b/hw/misc/meson.build index 16db6e228d..2ca2ce4b62 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -111,6 +111,7 @@ system_ss.add(when: 'CONFIG_STM32F2XX_SYSCFG', if_true: files('stm32f2xx_syscfg. system_ss.add(when: 'CONFIG_STM32F4XX_SYSCFG', if_true: files('stm32f4xx_syscfg.c')) system_ss.add(when: 'CONFIG_STM32F4XX_EXTI', if_true: files('stm32f4xx_exti.c')) system_ss.add(when: 'CONFIG_STM32L4X5_EXTI', if_true: files('stm32l4x5_exti.c')) +system_ss.add(when: 'CONFIG_STM32L4X5_SYSCFG', if_true: files('stm32l4x5_syscfg.c')) system_ss.add(when: 'CONFIG_MPS2_FPGAIO', if_true: files('mps2-fpgaio.c')) system_ss.add(when: 'CONFIG_MPS2_SCC', if_true: files('mps2-scc.c')) diff --git a/hw/misc/stm32l4x5_syscfg.c b/hw/misc/stm32l4x5_syscfg.c new file mode 100644 index 0000000000..fd68cb800b --- /dev/null +++ b/hw/misc/stm32l4x5_syscfg.c @@ -0,0 +1,266 @@ +/* + * STM32L4x5 SYSCFG (System Configuration Controller) + * + * Copyright (c) 2023 Arnaud Minier + * Copyright (c) 2023 Inès Varhol + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * This work is based on the stm32f4xx_syscfg by Alistair Francis. + * Original code is licensed under the MIT License: + * + * Copyright (c) 2014 Alistair Francis + */ + +/* + * The reference used is the STMicroElectronics RM0351 Reference manual + * for STM32L4x5 and STM32L4x6 advanced Arm ® -based 32-bit MCUs. + * https://www.st.com/en/microcontrollers-microprocessors/stm32l4x5/documentation.html + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "trace.h" +#include "hw/irq.h" +#include "migration/vmstate.h" +#include "hw/misc/stm32l4x5_syscfg.h" + +#define SYSCFG_MEMRMP 0x00 +#define SYSCFG_CFGR1 0x04 +#define SYSCFG_EXTICR1 0x08 +#define SYSCFG_EXTICR2 0x0C +#define SYSCFG_EXTICR3 0x10 +#define SYSCFG_EXTICR4 0x14 +#define SYSCFG_SCSR 0x18 +#define SYSCFG_CFGR2 0x1C +#define SYSCFG_SWPR 0x20 +#define SYSCFG_SKR 0x24 +#define SYSCFG_SWPR2 0x28 + +/* 00000000_00000000_00000001_00000111 */ +#define ACTIVABLE_BITS_MEMRP 0x00000107 + +/* 11111100_11111111_00000001_00000000 */ +#define ACTIVABLE_BITS_CFGR1 0xFCFF0100 +/* 00000000_00000000_00000000_00000001 */ +#define FIREWALL_DISABLE_CFGR1 0x00000001 + +/* 00000000_00000000_11111111_11111111 */ +#define ACTIVABLE_BITS_EXTICR 0x0000FFFF + +/* 00000000_00000000_00000000_00000011 */ +/* #define ACTIVABLE_BITS_SCSR 0x00000003 */ + +/* 00000000_00000000_00000000_00001111 */ +#define ECC_LOCK_CFGR2 0x0000000F +/* 00000000_00000000_00000001_00000000 */ +#define SRAM2_PARITY_ERROR_FLAG_CFGR2 0x00000100 + +/* 00000000_00000000_00000000_11111111 */ +#define ACTIVABLE_BITS_SKR 0x000000FF + +#define NUM_LINES_PER_EXTICR_REG 4 + +static void stm32l4x5_syscfg_hold_reset(Object *obj) +{ + Stm32l4x5SyscfgState *s = STM32L4X5_SYSCFG(obj); + + s->memrmp = 0x00000000; + s->cfgr1 = 0x7C000001; + s->exticr[0] = 0x00000000; + s->exticr[1] = 0x00000000; + s->exticr[2] = 0x00000000; + s->exticr[3] = 0x00000000; + s->scsr = 0x00000000; + s->cfgr2 = 0x00000000; + s->swpr = 0x00000000; + s->skr = 0x00000000; + s->swpr2 = 0x00000000; +} + +static void stm32l4x5_syscfg_set_irq(void *opaque, int irq, int level) +{ + Stm32l4x5SyscfgState *s = opaque; + const uint8_t gpio = irq / GPIO_NUM_PINS; + const int line = irq % GPIO_NUM_PINS; + + const int exticr_reg = line / NUM_LINES_PER_EXTICR_REG; + const int startbit = (line % NUM_LINES_PER_EXTICR_REG) * 4; + + g_assert(gpio < NUM_GPIOS); + trace_stm32l4x5_syscfg_set_irq(gpio, line, level); + + if (extract32(s->exticr[exticr_reg], startbit, 4) == gpio) { + trace_stm32l4x5_syscfg_forward_exti(line); + qemu_set_irq(s->gpio_out[line], level); + } +} + +static uint64_t stm32l4x5_syscfg_read(void *opaque, hwaddr addr, + unsigned int size) +{ + Stm32l4x5SyscfgState *s = opaque; + + trace_stm32l4x5_syscfg_read(addr); + + switch (addr) { + case SYSCFG_MEMRMP: + return s->memrmp; + case SYSCFG_CFGR1: + return s->cfgr1; + case SYSCFG_EXTICR1...SYSCFG_EXTICR4: + return s->exticr[(addr - SYSCFG_EXTICR1) / 4]; + case SYSCFG_SCSR: + return s->scsr; + case SYSCFG_CFGR2: + return s->cfgr2; + case SYSCFG_SWPR: + return s->swpr; + case SYSCFG_SKR: + return s->skr; + case SYSCFG_SWPR2: + return s->swpr2; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, addr); + return 0; + } +} +static void stm32l4x5_syscfg_write(void *opaque, hwaddr addr, + uint64_t value, unsigned int size) +{ + Stm32l4x5SyscfgState *s = opaque; + + trace_stm32l4x5_syscfg_write(addr, value); + + switch (addr) { + case SYSCFG_MEMRMP: + qemu_log_mask(LOG_UNIMP, + "%s: Changing the memory mapping isn't supported\n", + __func__); + s->memrmp = value & ACTIVABLE_BITS_MEMRP; + return; + case SYSCFG_CFGR1: + qemu_log_mask(LOG_UNIMP, + "%s: Functions in CFGRx aren't supported\n", + __func__); + /* bit 0 (firewall dis.) is cleared by software, set only by reset. */ + s->cfgr1 = (s->cfgr1 & value & FIREWALL_DISABLE_CFGR1) | + (value & ACTIVABLE_BITS_CFGR1); + return; + case SYSCFG_EXTICR1...SYSCFG_EXTICR4: + s->exticr[(addr - SYSCFG_EXTICR1) / 4] = + (value & ACTIVABLE_BITS_EXTICR); + return; + case SYSCFG_SCSR: + qemu_log_mask(LOG_UNIMP, + "%s: Erasing SRAM2 isn't supported\n", + __func__); + /* + * only non reserved bits are : + * bit 0 (write-protected by a passkey), bit 1 (meant to be read) + * so it serves no purpose yet to add : + * s->scsr = value & 0x3; + */ + return; + case SYSCFG_CFGR2: + qemu_log_mask(LOG_UNIMP, + "%s: Functions in CFGRx aren't supported\n", + __func__); + /* bit 8 (SRAM2 PEF) is cleared by software by writing a '1'.*/ + /* bits[3:0] (ECC Lock) are set by software, cleared only by reset.*/ + s->cfgr2 = (s->cfgr2 | (value & ECC_LOCK_CFGR2)) & + ~(value & SRAM2_PARITY_ERROR_FLAG_CFGR2); + return; + case SYSCFG_SWPR: + qemu_log_mask(LOG_UNIMP, + "%s: Write protecting SRAM2 isn't supported\n", + __func__); + /* These bits are set by software and cleared only by reset.*/ + s->swpr |= value; + return; + case SYSCFG_SKR: + qemu_log_mask(LOG_UNIMP, + "%s: Erasing SRAM2 isn't supported\n", + __func__); + s->skr = value & ACTIVABLE_BITS_SKR; + return; + case SYSCFG_SWPR2: + qemu_log_mask(LOG_UNIMP, + "%s: Write protecting SRAM2 isn't supported\n", + __func__); + /* These bits are set by software and cleared only by reset.*/ + s->swpr2 |= value; + return; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, addr); + } +} + +static const MemoryRegionOps stm32l4x5_syscfg_ops = { + .read = stm32l4x5_syscfg_read, + .write = stm32l4x5_syscfg_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl.min_access_size = 4, + .impl.max_access_size = 4, + .impl.unaligned = false, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .valid.unaligned = false, +}; + +static void stm32l4x5_syscfg_init(Object *obj) +{ + Stm32l4x5SyscfgState *s = STM32L4X5_SYSCFG(obj); + + memory_region_init_io(&s->mmio, obj, &stm32l4x5_syscfg_ops, s, + TYPE_STM32L4X5_SYSCFG, 0x400); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); + + qdev_init_gpio_in(DEVICE(obj), stm32l4x5_syscfg_set_irq, + GPIO_NUM_PINS * NUM_GPIOS); + qdev_init_gpio_out(DEVICE(obj), s->gpio_out, GPIO_NUM_PINS); +} + +static const VMStateDescription vmstate_stm32l4x5_syscfg = { + .name = TYPE_STM32L4X5_SYSCFG, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(memrmp, Stm32l4x5SyscfgState), + VMSTATE_UINT32(cfgr1, Stm32l4x5SyscfgState), + VMSTATE_UINT32_ARRAY(exticr, Stm32l4x5SyscfgState, + SYSCFG_NUM_EXTICR), + VMSTATE_UINT32(scsr, Stm32l4x5SyscfgState), + VMSTATE_UINT32(cfgr2, Stm32l4x5SyscfgState), + VMSTATE_UINT32(swpr, Stm32l4x5SyscfgState), + VMSTATE_UINT32(skr, Stm32l4x5SyscfgState), + VMSTATE_UINT32(swpr2, Stm32l4x5SyscfgState), + VMSTATE_END_OF_LIST() + } +}; + +static void stm32l4x5_syscfg_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + dc->vmsd = &vmstate_stm32l4x5_syscfg; + rc->phases.hold = stm32l4x5_syscfg_hold_reset; +} + +static const TypeInfo stm32l4x5_syscfg_info[] = { + { + .name = TYPE_STM32L4X5_SYSCFG, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Stm32l4x5SyscfgState), + .instance_init = stm32l4x5_syscfg_init, + .class_init = stm32l4x5_syscfg_class_init, + } +}; + +DEFINE_TYPES(stm32l4x5_syscfg_info) diff --git a/hw/misc/trace-events b/hw/misc/trace-events index fccd3204bf..5f5bc92222 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -163,6 +163,12 @@ stm32f4xx_exti_set_irq(int irq, int level) "Set EXTI: %d to %d" stm32f4xx_exti_read(uint64_t addr) "reg read: addr: 0x%" PRIx64 " " stm32f4xx_exti_write(uint64_t addr, uint64_t data) "reg write: addr: 0x%" PRIx64 " val: 0x%" PRIx64 "" +# stm32l4x5_syscfg.c +stm32l4x5_syscfg_set_irq(int gpio, int line, int level) "irq from GPIO: %d, line: %d, level: %d" +stm32l4x5_syscfg_forward_exti(int irq) "irq %d forwarded to EXTI" +stm32l4x5_syscfg_read(uint64_t addr) "reg read: addr: 0x%" PRIx64 " " +stm32l4x5_syscfg_write(uint64_t addr, uint64_t data) "reg write: addr: 0x%" PRIx64 " val: 0x%" PRIx64 "" + # stm32l4x5_exti.c stm32l4x5_exti_set_irq(int irq, int level) "Set EXTI: %d to %d" stm32l4x5_exti_read(uint64_t addr, uint64_t data) "reg read: addr: 0x%" PRIx64 " val: 0x%" PRIx64 "" diff --git a/include/hw/misc/stm32l4x5_syscfg.h b/include/hw/misc/stm32l4x5_syscfg.h new file mode 100644 index 0000000000..29c3522f9d --- /dev/null +++ b/include/hw/misc/stm32l4x5_syscfg.h @@ -0,0 +1,54 @@ +/* + * STM32L4x5 SYSCFG (System Configuration Controller) + * + * Copyright (c) 2023 Arnaud Minier + * Copyright (c) 2023 Inès Varhol + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * This work is based on the stm32f4xx_syscfg by Alistair Francis. + * Original code is licensed under the MIT License: + * + * Copyright (c) 2014 Alistair Francis + */ + +/* + * The reference used is the STMicroElectronics RM0351 Reference manual + * for STM32L4x5 and STM32L4x6 advanced Arm ® -based 32-bit MCUs. + * https://www.st.com/en/microcontrollers-microprocessors/stm32l4x5/documentation.html + */ + +#ifndef HW_STM32L4X5_SYSCFG_H +#define HW_STM32L4X5_SYSCFG_H + +#include "hw/sysbus.h" +#include "qom/object.h" + +#define TYPE_STM32L4X5_SYSCFG "stm32l4x5-syscfg" +OBJECT_DECLARE_SIMPLE_TYPE(Stm32l4x5SyscfgState, STM32L4X5_SYSCFG) + +#define NUM_GPIOS 8 +#define GPIO_NUM_PINS 16 +#define SYSCFG_NUM_EXTICR 4 + +struct Stm32l4x5SyscfgState { + SysBusDevice parent_obj; + + MemoryRegion mmio; + + uint32_t memrmp; + uint32_t cfgr1; + uint32_t exticr[SYSCFG_NUM_EXTICR]; + uint32_t scsr; + uint32_t cfgr2; + uint32_t swpr; + uint32_t skr; + uint32_t swpr2; + + qemu_irq gpio_out[GPIO_NUM_PINS]; +}; + +#endif From 7dfe2312e4653dd796dc2731668ad65237563e39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?In=C3=A8s=20Varhol?= Date: Tue, 9 Jan 2024 20:41:58 +0100 Subject: [PATCH 08/12] hw/arm: Connect STM32L4x5 SYSCFG to STM32L4x5 SoC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SYSCFG input GPIOs aren't connected yet. When the STM32L4x5 GPIO device will be implemented, its output GPIOs will be connected to the SYSCFG input GPIOs. Tested-by: Philippe Mathieu-Daudé Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Arnaud Minier Signed-off-by: Inès Varhol Reviewed-by: Alistair Francis Message-id: 20240109194438.70934-3-ines.varhol@telecom-paris.fr Signed-off-by: Peter Maydell --- hw/arm/Kconfig | 1 + hw/arm/stm32l4x5_soc.c | 21 ++++++++++++++++++++- include/hw/arm/stm32l4x5_soc.h | 2 ++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index da9c6e5064..218b454e97 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -459,6 +459,7 @@ config STM32L4X5_SOC bool select ARM_V7M select OR_IRQ + select STM32L4X5_SYSCFG select STM32L4X5_EXTI config XLNX_ZYNQMP_ARM diff --git a/hw/arm/stm32l4x5_soc.c b/hw/arm/stm32l4x5_soc.c index 756eb69488..f470ff74ec 100644 --- a/hw/arm/stm32l4x5_soc.c +++ b/hw/arm/stm32l4x5_soc.c @@ -37,6 +37,7 @@ #define SRAM2_SIZE (32 * KiB) #define EXTI_ADDR 0x40010400 +#define SYSCFG_ADDR 0x40010000 #define NUM_EXTI_IRQ 40 /* Match exti line connections with their CPU IRQ number */ @@ -80,6 +81,7 @@ static void stm32l4x5_soc_initfn(Object *obj) Stm32l4x5SocState *s = STM32L4X5_SOC(obj); object_initialize_child(obj, "exti", &s->exti, TYPE_STM32L4X5_EXTI); + object_initialize_child(obj, "syscfg", &s->syscfg, TYPE_STM32L4X5_SYSCFG); s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0); s->refclk = qdev_init_clock_in(DEVICE(s), "refclk", NULL, NULL, 0); @@ -155,6 +157,19 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp) return; } + /* System configuration controller */ + busdev = SYS_BUS_DEVICE(&s->syscfg); + if (!sysbus_realize(busdev, errp)) { + return; + } + sysbus_mmio_map(busdev, 0, SYSCFG_ADDR); + /* + * TODO: when the GPIO device is implemented, connect it + * to SYCFG using `qdev_connect_gpio_out`, NUM_GPIOS and + * GPIO_NUM_PINS. + */ + + /* EXTI device */ busdev = SYS_BUS_DEVICE(&s->exti); if (!sysbus_realize(busdev, errp)) { return; @@ -164,6 +179,11 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp) sysbus_connect_irq(busdev, i, qdev_get_gpio_in(armv7m, exti_irq[i])); } + for (unsigned i = 0; i < 16; i++) { + qdev_connect_gpio_out(DEVICE(&s->syscfg), i, + qdev_get_gpio_in(DEVICE(&s->exti), i)); + } + /* APB1 BUS */ create_unimplemented_device("TIM2", 0x40000000, 0x400); create_unimplemented_device("TIM3", 0x40000400, 0x400); @@ -201,7 +221,6 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp) /* RESERVED: 0x40009800, 0x6800 */ /* APB2 BUS */ - create_unimplemented_device("SYSCFG", 0x40010000, 0x30); create_unimplemented_device("VREFBUF", 0x40010030, 0x1D0); create_unimplemented_device("COMP", 0x40010200, 0x200); /* RESERVED: 0x40010800, 0x1400 */ diff --git a/include/hw/arm/stm32l4x5_soc.h b/include/hw/arm/stm32l4x5_soc.h index f7305568dc..baf70410b5 100644 --- a/include/hw/arm/stm32l4x5_soc.h +++ b/include/hw/arm/stm32l4x5_soc.h @@ -26,6 +26,7 @@ #include "exec/memory.h" #include "hw/arm/armv7m.h" +#include "hw/misc/stm32l4x5_syscfg.h" #include "hw/misc/stm32l4x5_exti.h" #include "qom/object.h" @@ -41,6 +42,7 @@ struct Stm32l4x5SocState { ARMv7MState armv7m; Stm32l4x5ExtiState exti; + Stm32l4x5SyscfgState syscfg; MemoryRegion sram1; MemoryRegion sram2; From a96bff6157ca89bbf7fc7aabd670fa4072c922cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?In=C3=A8s=20Varhol?= Date: Tue, 9 Jan 2024 20:41:59 +0100 Subject: [PATCH 09/12] tests/qtest: Add STM32L4x5 SYSCFG QTest testcase MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tested-by: Philippe Mathieu-Daudé Reviewed-by: Philippe Mathieu-Daudé Acked-by: Alistair Francis Signed-off-by: Arnaud Minier Signed-off-by: Inès Varhol Message-id: 20240109194438.70934-4-ines.varhol@telecom-paris.fr Signed-off-by: Peter Maydell --- tests/qtest/meson.build | 3 +- tests/qtest/stm32l4x5_syscfg-test.c | 331 ++++++++++++++++++++++++++++ 2 files changed, 333 insertions(+), 1 deletion(-) create mode 100644 tests/qtest/stm32l4x5_syscfg-test.c diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 07fdd2dc1a..5e4bc3da85 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -197,7 +197,8 @@ qtests_aspeed = \ 'aspeed_gpio-test'] qtests_stm32l4x5 = \ - ['stm32l4x5_exti-test'] + ['stm32l4x5_exti-test', + 'stm32l4x5_syscfg-test'] qtests_arm = \ (config_all_devices.has_key('CONFIG_MPS2') ? ['sse-timer-test'] : []) + \ diff --git a/tests/qtest/stm32l4x5_syscfg-test.c b/tests/qtest/stm32l4x5_syscfg-test.c new file mode 100644 index 0000000000..ed4801798d --- /dev/null +++ b/tests/qtest/stm32l4x5_syscfg-test.c @@ -0,0 +1,331 @@ +/* + * QTest testcase for STM32L4x5_SYSCFG + * + * Copyright (c) 2023 Arnaud Minier + * Copyright (c) 2023 Inès Varhol + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest-single.h" + +#define SYSCFG_BASE_ADDR 0x40010000 +#define SYSCFG_MEMRMP 0x00 +#define SYSCFG_CFGR1 0x04 +#define SYSCFG_EXTICR1 0x08 +#define SYSCFG_EXTICR2 0x0C +#define SYSCFG_EXTICR3 0x10 +#define SYSCFG_EXTICR4 0x14 +#define SYSCFG_SCSR 0x18 +#define SYSCFG_CFGR2 0x1C +#define SYSCFG_SWPR 0x20 +#define SYSCFG_SKR 0x24 +#define SYSCFG_SWPR2 0x28 +#define INVALID_ADDR 0x2C + +static void syscfg_writel(unsigned int offset, uint32_t value) +{ + writel(SYSCFG_BASE_ADDR + offset, value); +} + +static uint32_t syscfg_readl(unsigned int offset) +{ + return readl(SYSCFG_BASE_ADDR + offset); +} + +static void syscfg_set_irq(int num, int level) +{ + qtest_set_irq_in(global_qtest, "/machine/soc/syscfg", + NULL, num, level); +} + +static void system_reset(void) +{ + QDict *response; + response = qtest_qmp(global_qtest, "{'execute': 'system_reset'}"); + g_assert(qdict_haskey(response, "return")); + qobject_unref(response); +} + +static void test_reset(void) +{ + /* + * Test that registers are initialized at the correct values + */ + g_assert_cmpuint(syscfg_readl(SYSCFG_MEMRMP), ==, 0x00000000); + + g_assert_cmpuint(syscfg_readl(SYSCFG_CFGR1), ==, 0x7C000001); + + g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR1), ==, 0x00000000); + + g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR2), ==, 0x00000000); + + g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR3), ==, 0x00000000); + + g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR4), ==, 0x00000000); + + g_assert_cmpuint(syscfg_readl(SYSCFG_SCSR), ==, 0x00000000); + + g_assert_cmpuint(syscfg_readl(SYSCFG_CFGR2), ==, 0x00000000); + + g_assert_cmpuint(syscfg_readl(SYSCFG_SWPR), ==, 0x00000000); + + g_assert_cmpuint(syscfg_readl(SYSCFG_SKR), ==, 0x00000000); + + g_assert_cmpuint(syscfg_readl(SYSCFG_SWPR2), ==, 0x00000000); +} + +static void test_reserved_bits(void) +{ + /* + * Test that reserved bits stay at reset value + * (which is 0 for all of them) by writing '1' + * in all reserved bits (keeping reset value for + * other bits) and checking that the + * register is still at reset value + */ + syscfg_writel(SYSCFG_MEMRMP, 0xFFFFFEF8); + g_assert_cmpuint(syscfg_readl(SYSCFG_MEMRMP), ==, 0x00000000); + + syscfg_writel(SYSCFG_CFGR1, 0x7F00FEFF); + g_assert_cmpuint(syscfg_readl(SYSCFG_CFGR1), ==, 0x7C000001); + + syscfg_writel(SYSCFG_EXTICR1, 0xFFFF0000); + g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR1), ==, 0x00000000); + + syscfg_writel(SYSCFG_EXTICR2, 0xFFFF0000); + g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR2), ==, 0x00000000); + + syscfg_writel(SYSCFG_EXTICR3, 0xFFFF0000); + g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR3), ==, 0x00000000); + + syscfg_writel(SYSCFG_EXTICR4, 0xFFFF0000); + g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR4), ==, 0x00000000); + + syscfg_writel(SYSCFG_SKR, 0xFFFFFF00); + g_assert_cmpuint(syscfg_readl(SYSCFG_SKR), ==, 0x00000000); +} + +static void test_set_and_clear(void) +{ + /* + * Test that regular bits can be set and cleared + */ + syscfg_writel(SYSCFG_MEMRMP, 0x00000107); + g_assert_cmpuint(syscfg_readl(SYSCFG_MEMRMP), ==, 0x00000107); + syscfg_writel(SYSCFG_MEMRMP, 0x00000000); + g_assert_cmpuint(syscfg_readl(SYSCFG_MEMRMP), ==, 0x00000000); + + /* cfgr1 bit 0 is clear only so we keep it set */ + syscfg_writel(SYSCFG_CFGR1, 0xFCFF0101); + g_assert_cmpuint(syscfg_readl(SYSCFG_CFGR1), ==, 0xFCFF0101); + syscfg_writel(SYSCFG_CFGR1, 0x00000001); + g_assert_cmpuint(syscfg_readl(SYSCFG_CFGR1), ==, 0x00000001); + + syscfg_writel(SYSCFG_EXTICR1, 0x0000FFFF); + g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR1), ==, 0x0000FFFF); + syscfg_writel(SYSCFG_EXTICR1, 0x00000000); + g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR1), ==, 0x00000000); + + syscfg_writel(SYSCFG_EXTICR2, 0x0000FFFF); + g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR2), ==, 0x0000FFFF); + syscfg_writel(SYSCFG_EXTICR2, 0x00000000); + g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR2), ==, 0x00000000); + + syscfg_writel(SYSCFG_EXTICR3, 0x0000FFFF); + g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR3), ==, 0x0000FFFF); + syscfg_writel(SYSCFG_EXTICR3, 0x00000000); + g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR3), ==, 0x00000000); + + syscfg_writel(SYSCFG_EXTICR4, 0x0000FFFF); + g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR4), ==, 0x0000FFFF); + syscfg_writel(SYSCFG_EXTICR4, 0x00000000); + g_assert_cmpuint(syscfg_readl(SYSCFG_EXTICR4), ==, 0x00000000); + + syscfg_writel(SYSCFG_SKR, 0x000000FF); + g_assert_cmpuint(syscfg_readl(SYSCFG_SKR), ==, 0x000000FF); + syscfg_writel(SYSCFG_SKR, 0x00000000); + g_assert_cmpuint(syscfg_readl(SYSCFG_SKR), ==, 0x00000000); +} + +static void test_clear_by_writing_1(void) +{ + /* + * Test that writing '1' doesn't set the bit + */ + syscfg_writel(SYSCFG_CFGR2, 0x00000100); + g_assert_cmpuint(syscfg_readl(SYSCFG_CFGR2), ==, 0x00000000); +} + +static void test_set_only_bits(void) +{ + /* + * Test that set only bits stay can't be cleared + */ + syscfg_writel(SYSCFG_CFGR2, 0x0000000F); + syscfg_writel(SYSCFG_CFGR2, 0x00000000); + g_assert_cmpuint(syscfg_readl(SYSCFG_CFGR2), ==, 0x0000000F); + + syscfg_writel(SYSCFG_SWPR, 0xFFFFFFFF); + syscfg_writel(SYSCFG_SWPR, 0x00000000); + g_assert_cmpuint(syscfg_readl(SYSCFG_SWPR), ==, 0xFFFFFFFF); + + syscfg_writel(SYSCFG_SWPR2, 0xFFFFFFFF); + syscfg_writel(SYSCFG_SWPR2, 0x00000000); + g_assert_cmpuint(syscfg_readl(SYSCFG_SWPR2), ==, 0xFFFFFFFF); + + system_reset(); +} + +static void test_clear_only_bits(void) +{ + /* + * Test that clear only bits stay can't be set + */ + syscfg_writel(SYSCFG_CFGR1, 0x00000000); + syscfg_writel(SYSCFG_CFGR1, 0x00000001); + g_assert_cmpuint(syscfg_readl(SYSCFG_CFGR1), ==, 0x00000000); + + system_reset(); +} + +static void test_interrupt(void) +{ + /* + * Test that GPIO rising lines result in an irq + * with the right configuration + */ + qtest_irq_intercept_in(global_qtest, "/machine/soc/exti"); + + /* GPIOA is the default source for EXTI lines 0 to 15 */ + + syscfg_set_irq(0, 1); + + g_assert_true(get_irq(0)); + + + syscfg_set_irq(15, 1); + + g_assert_true(get_irq(15)); + + /* Configure GPIOB[1] as the source input for EXTI1 */ + syscfg_writel(SYSCFG_EXTICR1, 0x00000010); + + syscfg_set_irq(17, 1); + + g_assert_true(get_irq(1)); + + /* Clean the test */ + syscfg_writel(SYSCFG_EXTICR1, 0x00000000); + syscfg_set_irq(0, 0); + syscfg_set_irq(15, 0); + syscfg_set_irq(17, 0); +} + +static void test_irq_pin_multiplexer(void) +{ + /* + * Test that syscfg irq sets the right exti irq + */ + + qtest_irq_intercept_in(global_qtest, "/machine/soc/exti"); + + syscfg_set_irq(0, 1); + + /* Check that irq 0 was set and irq 15 wasn't */ + g_assert_true(get_irq(0)); + g_assert_false(get_irq(15)); + + /* Clean the test */ + syscfg_set_irq(0, 0); + + syscfg_set_irq(15, 1); + + /* Check that irq 15 was set and irq 0 wasn't */ + g_assert_true(get_irq(15)); + g_assert_false(get_irq(0)); + + /* Clean the test */ + syscfg_set_irq(15, 0); +} + +static void test_irq_gpio_multiplexer(void) +{ + /* + * Test that an irq is generated only by the right GPIO + */ + + qtest_irq_intercept_in(global_qtest, "/machine/soc/exti"); + + /* GPIOA is the default source for EXTI lines 0 to 15 */ + + /* Check that setting rising pin GPIOA[0] generates an irq */ + syscfg_set_irq(0, 1); + + g_assert_true(get_irq(0)); + + /* Clean the test */ + syscfg_set_irq(0, 0); + + /* Check that setting rising pin GPIOB[0] doesn't generate an irq */ + syscfg_set_irq(16, 1); + + g_assert_false(get_irq(0)); + + /* Clean the test */ + syscfg_set_irq(16, 0); + + /* Configure GPIOB[0] as the source input for EXTI0 */ + syscfg_writel(SYSCFG_EXTICR1, 0x00000001); + + /* Check that setting rising pin GPIOA[0] doesn't generate an irq */ + syscfg_set_irq(0, 1); + + g_assert_false(get_irq(0)); + + /* Clean the test */ + syscfg_set_irq(0, 0); + + /* Check that setting rising pin GPIOB[0] generates an irq */ + syscfg_set_irq(16, 1); + + g_assert_true(get_irq(0)); + + /* Clean the test */ + syscfg_set_irq(16, 0); + syscfg_writel(SYSCFG_EXTICR1, 0x00000000); +} + +int main(int argc, char **argv) +{ + int ret; + + g_test_init(&argc, &argv, NULL); + g_test_set_nonfatal_assertions(); + + qtest_add_func("stm32l4x5/syscfg/test_reset", test_reset); + qtest_add_func("stm32l4x5/syscfg/test_reserved_bits", + test_reserved_bits); + qtest_add_func("stm32l4x5/syscfg/test_set_and_clear", + test_set_and_clear); + qtest_add_func("stm32l4x5/syscfg/test_clear_by_writing_1", + test_clear_by_writing_1); + qtest_add_func("stm32l4x5/syscfg/test_set_only_bits", + test_set_only_bits); + qtest_add_func("stm32l4x5/syscfg/test_clear_only_bits", + test_clear_only_bits); + qtest_add_func("stm32l4x5/syscfg/test_interrupt", + test_interrupt); + qtest_add_func("stm32l4x5/syscfg/test_irq_pin_multiplexer", + test_irq_pin_multiplexer); + qtest_add_func("stm32l4x5/syscfg/test_irq_gpio_multiplexer", + test_irq_gpio_multiplexer); + + qtest_start("-machine b-l475e-iot01a"); + ret = g_test_run(); + qtest_end(); + + return ret; +} From 7c8faaf2a4fa8ad2d291662ac7e7eeea1fe8d47c Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Tue, 9 Jan 2024 19:45:08 +0100 Subject: [PATCH 10/12] hw/timer: fix systick trace message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Samuel Tardieu Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Laurent Vivier Message-id: 20240109184508.3189599-1-sam@rfc1149.net Fixes: ff68dacbc786 ("armv7m: Split systick out from NVIC") Signed-off-by: Peter Maydell --- hw/timer/trace-events | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/timer/trace-events b/hw/timer/trace-events index 3eccef8385..8145e18e3d 100644 --- a/hw/timer/trace-events +++ b/hw/timer/trace-events @@ -35,7 +35,7 @@ aspeed_timer_read(uint64_t offset, unsigned size, uint64_t value) "From 0x%" PRI # armv7m_systick.c systick_reload(void) "systick reload" -systick_timer_tick(void) "systick reload" +systick_timer_tick(void) "systick tick" systick_read(uint64_t addr, uint32_t value, unsigned size) "systick read addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u" systick_write(uint64_t addr, uint32_t value, unsigned size) "systick write addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u" From c2d7faf713e04fc6b2d5b33d77cab0672c5bb30e Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Thu, 11 Jan 2024 15:10:54 +1000 Subject: [PATCH 11/12] hw/arm/virt: Consolidate valid CPU types It's found that some of the CPU type names in the array of valid CPU types are invalid because their corresponding classes aren't registered, as reported by Peter Maydell. [gshan@gshan build]$ ./qemu-system-arm -machine virt -cpu cortex-a9 qemu-system-arm: Invalid CPU model: cortex-a9 The valid models are: cortex-a7, cortex-a15, (null), (null), (null), (null), (null), (null), (null), (null), (null), (null), (null), max Fix it by consolidating the array of valid CPU types. After it's applied, we have the following output when TCG is enabled. [gshan@gshan build]$ ./qemu-system-arm -machine virt -cpu cortex-a9 qemu-system-arm: Invalid CPU model: cortex-a9 The valid models are: cortex-a7, cortex-a15, max [gshan@gshan build]$ ./qemu-system-aarch64 -machine virt -cpu cortex-a9 qemu-system-aarch64: Invalid CPU model: cortex-a9 The valid models are: cortex-a7, cortex-a15, cortex-a35, cortex-a55, cortex-a72, cortex-a76, cortex-a710, a64fx, neoverse-n1, neoverse-v1, neoverse-n2, cortex-a53, cortex-a57, max Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2084 Reported-by: Peter Maydell Signed-off-by: Gavin Shan Reviewed-by: Cornelia Huck Message-id: 20240111051054.83304-1-gshan@redhat.com Fixes: fa8c617791 ("hw/arm/virt: Check CPU type in machine_run_board_init()") Signed-off-by: Gavin Shan Signed-off-by: Peter Maydell --- hw/arm/virt.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 2793121cb4..5cbc69dff8 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -2905,6 +2905,7 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) #ifdef CONFIG_TCG ARM_CPU_TYPE_NAME("cortex-a7"), ARM_CPU_TYPE_NAME("cortex-a15"), +#ifdef TARGET_AARCH64 ARM_CPU_TYPE_NAME("cortex-a35"), ARM_CPU_TYPE_NAME("cortex-a55"), ARM_CPU_TYPE_NAME("cortex-a72"), @@ -2914,12 +2915,15 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) ARM_CPU_TYPE_NAME("neoverse-n1"), ARM_CPU_TYPE_NAME("neoverse-v1"), ARM_CPU_TYPE_NAME("neoverse-n2"), -#endif +#endif /* TARGET_AARCH64 */ +#endif /* CONFIG_TCG */ +#ifdef TARGET_AARCH64 ARM_CPU_TYPE_NAME("cortex-a53"), ARM_CPU_TYPE_NAME("cortex-a57"), #if defined(CONFIG_KVM) || defined(CONFIG_HVF) ARM_CPU_TYPE_NAME("host"), -#endif +#endif /* CONFIG_KVM || CONFIG_HVF */ +#endif /* TARGET_AARCH64 */ ARM_CPU_TYPE_NAME("max"), NULL }; From 410c2a4d75f52f6a2fe978eda5a9b6f854afe5ea Mon Sep 17 00:00:00 2001 From: Anastasia Belova Date: Mon, 15 Jan 2024 12:22:16 +0300 Subject: [PATCH 12/12] load_elf: fix iterator's type for elf file processing j is used while loading an ELF file to byteswap segments' data. If data is larger than 2GB an overflow may happen. So j should be elf_word. This commit fixes a minor bug: it's unlikely anybody is trying to load ELF files with 2GB+ segments for wrong-endianness targets, but if they did, it wouldn't work correctly. Found by Linux Verification Center (linuxtesting.org) with SVACE. Cc: qemu-stable@nongnu.org Fixes: 7ef295ea5b ("loader: Add data swap option to load-elf") Signed-off-by: Anastasia Belova Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- include/hw/elf_ops.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/hw/elf_ops.h b/include/hw/elf_ops.h index 0a5c258fe6..9c35d1b9da 100644 --- a/include/hw/elf_ops.h +++ b/include/hw/elf_ops.h @@ -500,7 +500,7 @@ static ssize_t glue(load_elf, SZ)(const char *name, int fd, } if (data_swab) { - int j; + elf_word j; for (j = 0; j < file_size; j += (1 << data_swab)) { uint8_t *dp = data + j; switch (data_swab) {