:p
atchew
Login
Mostly my FEAT_NV/NV2 stuff, but some other smaller series too. -- PMM The following changes since commit 9468484fe904ab4691de6d9c34616667f377ceac: Merge tag 'block-pull-request' of https://gitlab.com/stefanha/qemu into staging (2024-01-09 10:32:23 +0000) are available in the Git repository at: https://git.linaro.org/people/pmaydell/qemu-arm.git tags/pull-target-arm-20240111 for you to fetch changes up to e2862554c257e908a3833265e38365e794abd362: target/arm: Add FEAT_NV2 to max, neoverse-n2, neoverse-v1 CPUs (2024-01-09 14:44:45 +0000) ---------------------------------------------------------------- target-arm queue: * Emulate FEAT_NV, FEAT_NV2 * add cache controller for Freescale i.MX6 * Add minimal support for the B-L475E-IOT01A board * Allow SoC models to configure M-profile CPUs with correct number of NVIC priority bits * Add missing QOM parent for v7-M SoCs * Set CTR_EL0.{IDC,DIC} for the 'max' CPU * hw/intc/arm_gicv3_cpuif: handle LPIs in in the list registers ---------------------------------------------------------------- Inès Varhol (2): hw/arm: Add minimal support for the STM32L4x5 SoC hw/arm: Add minimal support for the B-L475E-IOT01A board Nikita Ostrenkov (1): hw/arm: add cache controller for Freescale i.MX6 Peter Maydell (34): target/arm: Set CTR_EL0.{IDC,DIC} for the 'max' CPU hw/intc/arm_gicv3_cpuif: handle LPIs in in the list registers target/arm: Handle HCR_EL2 accesses for bits introduced with FEAT_NV target/arm: Implement HCR_EL2.AT handling target/arm: Enable trapping of ERET for FEAT_NV target/arm: Always honour HCR_EL2.TSC when HCR_EL2.NV is set target/arm: Allow use of upper 32 bits of TBFLAG_A64 target/arm: Record correct opcode fields in cpreg for E2H aliases target/arm: *_EL12 registers should UNDEF when HCR_EL2.E2H is 0 target/arm: Make EL2 cpreg accessfns safe for FEAT_NV EL1 accesses target/arm: Move FPU/SVE/SME access checks up above ARM_CP_SPECIAL_MASK check target/arm: Trap sysreg accesses for FEAT_NV target/arm: Make NV reads of CurrentEL return EL2 target/arm: Set SPSR_EL1.M correctly when nested virt is enabled target/arm: Trap registers when HCR_EL2.{NV, NV1} == {1, 1} target/arm: Always use arm_pan_enabled() when checking if PAN is enabled target/arm: Don't honour PSTATE.PAN when HCR_EL2.{NV, NV1} == {1, 1} target/arm: Treat LDTR* and STTR* as LDR/STR when NV, NV1 is 1, 1 target/arm: Handle FEAT_NV page table attribute changes target/arm: Add FEAT_NV to max, neoverse-n2, neoverse-v1 CPUs target/arm: Handle HCR_EL2 accesses for FEAT_NV2 bits target/arm: Implement VNCR_EL2 register target/arm: Handle FEAT_NV2 changes to when SPSR_EL1.M reports EL2 target/arm: Handle FEAT_NV2 redirection of SPSR_EL2, ELR_EL2, ESR_EL2, FAR_EL2 target/arm: Implement FEAT_NV2 redirection of sysregs to RAM target/arm: Report VNCR_EL2 based faults correctly target/arm: Mark up VNCR offsets (offsets 0x0..0xff) target/arm: Mark up VNCR offsets (offsets 0x100..0x160) target/arm: Mark up VNCR offsets (offsets 0x168..0x1f8) target/arm: Mark up VNCR offsets (offsets >= 0x200, except GIC) hw/intc/arm_gicv3_cpuif: Mark up VNCR offsets for GIC CPU registers target/arm: Report HCR_EL2.{NV,NV1,NV2} in cpu dumps target/arm: Enhance CPU_LOG_INT to show SPSR on AArch64 exception-entry target/arm: Add FEAT_NV2 to max, neoverse-n2, neoverse-v1 CPUs Philippe Mathieu-Daudé (1): hw/arm: Add missing QOM parent for v7-M SoCs Samuel Tardieu (3): hw/intc/armv7m_nvic: add "num-prio-bits" property hw/arm/armv7m: alias the NVIC "num-prio-bits" property hw/arm/socs: configure priority bits for existing SOCs MAINTAINERS | 15 ++ docs/system/arm/b-l475e-iot01a.rst | 46 +++++ docs/system/arm/emulation.rst | 2 + docs/system/arm/stm32.rst | 6 +- docs/system/target-arm.rst | 1 + configs/devices/arm-softmmu/default.mak | 1 + include/hw/arm/armv7m.h | 1 + include/hw/arm/stm32l4x5_soc.h | 57 ++++++ target/arm/cpregs.h | 54 +++++- target/arm/cpu-features.h | 10 + target/arm/cpu.h | 24 ++- target/arm/syndrome.h | 20 +- target/arm/tcg/translate.h | 16 +- hw/arm/armv7m.c | 2 + hw/arm/b-l475e-iot01a.c | 72 +++++++ hw/arm/fsl-imx6.c | 3 + hw/arm/msf2-som.c | 1 + hw/arm/netduino2.c | 1 + hw/arm/netduinoplus2.c | 1 + hw/arm/olimex-stm32-h405.c | 1 + hw/arm/stellaris.c | 2 + hw/arm/stm32f100_soc.c | 1 + hw/arm/stm32f205_soc.c | 1 + hw/arm/stm32f405_soc.c | 1 + hw/arm/stm32l4x5_soc.c | 266 ++++++++++++++++++++++++++ hw/arm/stm32vldiscovery.c | 1 + hw/intc/arm_gicv3_cpuif.c | 28 ++- hw/intc/armv7m_nvic.c | 23 ++- target/arm/cpu.c | 8 +- target/arm/debug_helper.c | 13 +- target/arm/helper.c | 326 +++++++++++++++++++++++++++++--- target/arm/ptw.c | 21 ++ target/arm/tcg/cpu64.c | 11 ++ target/arm/tcg/hflags.c | 30 ++- target/arm/tcg/op_helper.c | 16 +- target/arm/tcg/tlb_helper.c | 27 ++- target/arm/tcg/translate-a64.c | 160 ++++++++++++++-- hw/arm/Kconfig | 12 ++ hw/arm/meson.build | 2 + 39 files changed, 1203 insertions(+), 80 deletions(-) create mode 100644 docs/system/arm/b-l475e-iot01a.rst create mode 100644 include/hw/arm/stm32l4x5_soc.h create mode 100644 hw/arm/b-l475e-iot01a.c create mode 100644 hw/arm/stm32l4x5_soc.c
From: Nikita Ostrenkov <n.ostrenkov@gmail.com> Signed-off-by: Nikita Ostrenkov <n.ostrenkov@gmail.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20231219105510.4907-1-n.ostrenkov@gmail.com [PMM: fixed stray whitespace] Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/fsl-imx6.c | 3 +++ hw/arm/Kconfig | 1 + 2 files changed, 4 insertions(+) diff --git a/hw/arm/fsl-imx6.c b/hw/arm/fsl-imx6.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/fsl-imx6.c +++ b/hw/arm/fsl-imx6.c @@ -XXX,XX +XXX,XX @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_FIQ)); } + /* L2 cache controller */ + sysbus_create_simple("l2x0", FSL_IMX6_PL310_ADDR, NULL); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->ccm), errp)) { return; } diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -XXX,XX +XXX,XX @@ config FSL_IMX6 select IMX_I2C select IMX_USBPHY select WDT_IMX2 + select PL310 # cache controller select SDHCI config ASPEED_SOC -- 2.34.1
From: Inès Varhol <ines.varhol@telecom-paris.fr> This patch adds a new STM32L4x5 SoC, it is necessary to add support for the B-L475E-IOT01A board. The implementation is derived from the STM32F405 SoC. The implementation contains no peripherals, only memory regions are implemented. Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Acked-by: Alistair Francis <alistair.francis@wdc.com> Signed-off-by: Arnaud Minier <arnaud.minier@telecom-paris.fr> Signed-off-by: Inès Varhol <ines.varhol@telecom-paris.fr> Message-id: 20240108135849.351719-2-ines.varhol@telecom-paris.fr Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- MAINTAINERS | 8 + include/hw/arm/stm32l4x5_soc.h | 57 +++++++ hw/arm/stm32l4x5_soc.c | 265 +++++++++++++++++++++++++++++++++ hw/arm/Kconfig | 5 + hw/arm/meson.build | 1 + 5 files changed, 336 insertions(+) create mode 100644 include/hw/arm/stm32l4x5_soc.h create mode 100644 hw/arm/stm32l4x5_soc.c diff --git a/MAINTAINERS b/MAINTAINERS index XXXXXXX..XXXXXXX 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -XXX,XX +XXX,XX @@ L: qemu-arm@nongnu.org S: Maintained F: hw/arm/olimex-stm32-h405.c +STM32L4x5 SoC Family +M: Arnaud Minier <arnaud.minier@telecom-paris.fr> +M: Inès Varhol <ines.varhol@telecom-paris.fr> +L: qemu-arm@nongnu.org +S: Maintained +F: hw/arm/stm32l4x5_soc.c +F: include/hw/arm/stm32l4x5_soc.h + SmartFusion2 M: Subbaraya Sundeep <sundeep.lkml@gmail.com> M: Peter Maydell <peter.maydell@linaro.org> diff --git a/include/hw/arm/stm32l4x5_soc.h b/include/hw/arm/stm32l4x5_soc.h new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/include/hw/arm/stm32l4x5_soc.h @@ -XXX,XX +XXX,XX @@ +/* + * STM32L4x5 SoC family + * + * Copyright (c) 2023 Arnaud Minier <arnaud.minier@telecom-paris.fr> + * Copyright (c) 2023 Inès Varhol <ines.varhol@telecom-paris.fr> + * + * 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 heavily inspired by the stm32f405_soc by Alistair Francis. + * Original code is licensed under the MIT License: + * + * Copyright (c) 2014 Alistair Francis <alistair@alistair23.me> + */ + +/* + * 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_ARM_STM32L4x5_SOC_H +#define HW_ARM_STM32L4x5_SOC_H + +#include "exec/memory.h" +#include "hw/arm/armv7m.h" +#include "qom/object.h" + +#define TYPE_STM32L4X5_SOC "stm32l4x5-soc" +#define TYPE_STM32L4X5XC_SOC "stm32l4x5xc-soc" +#define TYPE_STM32L4X5XE_SOC "stm32l4x5xe-soc" +#define TYPE_STM32L4X5XG_SOC "stm32l4x5xg-soc" +OBJECT_DECLARE_TYPE(Stm32l4x5SocState, Stm32l4x5SocClass, STM32L4X5_SOC) + +struct Stm32l4x5SocState { + SysBusDevice parent_obj; + + ARMv7MState armv7m; + + MemoryRegion sram1; + MemoryRegion sram2; + MemoryRegion flash; + MemoryRegion flash_alias; + + Clock *sysclk; + Clock *refclk; +}; + +struct Stm32l4x5SocClass { + SysBusDeviceClass parent_class; + + size_t flash_size; +}; + +#endif diff --git a/hw/arm/stm32l4x5_soc.c b/hw/arm/stm32l4x5_soc.c new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/hw/arm/stm32l4x5_soc.c @@ -XXX,XX +XXX,XX @@ +/* + * STM32L4x5 SoC family + * + * Copyright (c) 2023 Arnaud Minier <arnaud.minier@telecom-paris.fr> + * Copyright (c) 2023 Inès Varhol <ines.varhol@telecom-paris.fr> + * + * 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 heavily inspired by the stm32f405_soc by Alistair Francis. + * Original code is licensed under the MIT License: + * + * Copyright (c) 2014 Alistair Francis <alistair@alistair23.me> + */ + +/* + * 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/units.h" +#include "qapi/error.h" +#include "exec/address-spaces.h" +#include "sysemu/sysemu.h" +#include "hw/arm/stm32l4x5_soc.h" +#include "hw/qdev-clock.h" +#include "hw/misc/unimp.h" + +#define FLASH_BASE_ADDRESS 0x08000000 +#define SRAM1_BASE_ADDRESS 0x20000000 +#define SRAM1_SIZE (96 * KiB) +#define SRAM2_BASE_ADDRESS 0x10000000 +#define SRAM2_SIZE (32 * KiB) + +static void stm32l4x5_soc_initfn(Object *obj) +{ + Stm32l4x5SocState *s = STM32L4X5_SOC(obj); + + s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0); + s->refclk = qdev_init_clock_in(DEVICE(s), "refclk", NULL, NULL, 0); +} + +static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp) +{ + ERRP_GUARD(); + Stm32l4x5SocState *s = STM32L4X5_SOC(dev_soc); + const Stm32l4x5SocClass *sc = STM32L4X5_SOC_GET_CLASS(dev_soc); + MemoryRegion *system_memory = get_system_memory(); + DeviceState *armv7m; + + /* + * We use s->refclk internally and only define it with qdev_init_clock_in() + * so it is correctly parented and not leaked on an init/deinit; it is not + * intended as an externally exposed clock. + */ + if (clock_has_source(s->refclk)) { + error_setg(errp, "refclk clock must not be wired up by the board code"); + return; + } + + if (!clock_has_source(s->sysclk)) { + error_setg(errp, "sysclk clock must be wired up by the board code"); + return; + } + + /* + * TODO: ideally we should model the SoC RCC and its ability to + * change the sysclk frequency and define different sysclk sources. + */ + + /* The refclk always runs at frequency HCLK / 8 */ + clock_set_mul_div(s->refclk, 8, 1); + clock_set_source(s->refclk, s->sysclk); + + if (!memory_region_init_rom(&s->flash, OBJECT(dev_soc), "flash", + sc->flash_size, errp)) { + return; + } + memory_region_init_alias(&s->flash_alias, OBJECT(dev_soc), + "flash_boot_alias", &s->flash, 0, + sc->flash_size); + + memory_region_add_subregion(system_memory, FLASH_BASE_ADDRESS, &s->flash); + memory_region_add_subregion(system_memory, 0, &s->flash_alias); + + if (!memory_region_init_ram(&s->sram1, OBJECT(dev_soc), "SRAM1", SRAM1_SIZE, + errp)) { + return; + } + memory_region_add_subregion(system_memory, SRAM1_BASE_ADDRESS, &s->sram1); + + if (!memory_region_init_ram(&s->sram2, OBJECT(dev_soc), "SRAM2", SRAM2_SIZE, + errp)) { + return; + } + memory_region_add_subregion(system_memory, SRAM2_BASE_ADDRESS, &s->sram2); + + object_initialize_child(OBJECT(dev_soc), "armv7m", &s->armv7m, TYPE_ARMV7M); + armv7m = DEVICE(&s->armv7m); + qdev_prop_set_uint32(armv7m, "num-irq", 96); + qdev_prop_set_string(armv7m, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m4")); + qdev_prop_set_bit(armv7m, "enable-bitband", true); + qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk); + qdev_connect_clock_in(armv7m, "refclk", s->refclk); + object_property_set_link(OBJECT(&s->armv7m), "memory", + OBJECT(system_memory), &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->armv7m), errp)) { + return; + } + + /* APB1 BUS */ + create_unimplemented_device("TIM2", 0x40000000, 0x400); + create_unimplemented_device("TIM3", 0x40000400, 0x400); + create_unimplemented_device("TIM4", 0x40000800, 0x400); + create_unimplemented_device("TIM5", 0x40000C00, 0x400); + create_unimplemented_device("TIM6", 0x40001000, 0x400); + create_unimplemented_device("TIM7", 0x40001400, 0x400); + /* RESERVED: 0x40001800, 0x1000 */ + create_unimplemented_device("RTC", 0x40002800, 0x400); + create_unimplemented_device("WWDG", 0x40002C00, 0x400); + create_unimplemented_device("IWDG", 0x40003000, 0x400); + /* RESERVED: 0x40001800, 0x400 */ + create_unimplemented_device("SPI2", 0x40003800, 0x400); + create_unimplemented_device("SPI3", 0x40003C00, 0x400); + /* RESERVED: 0x40004000, 0x400 */ + create_unimplemented_device("USART2", 0x40004400, 0x400); + create_unimplemented_device("USART3", 0x40004800, 0x400); + create_unimplemented_device("UART4", 0x40004C00, 0x400); + create_unimplemented_device("UART5", 0x40005000, 0x400); + create_unimplemented_device("I2C1", 0x40005400, 0x400); + create_unimplemented_device("I2C2", 0x40005800, 0x400); + create_unimplemented_device("I2C3", 0x40005C00, 0x400); + /* RESERVED: 0x40006000, 0x400 */ + create_unimplemented_device("CAN1", 0x40006400, 0x400); + /* RESERVED: 0x40006800, 0x400 */ + create_unimplemented_device("PWR", 0x40007000, 0x400); + create_unimplemented_device("DAC1", 0x40007400, 0x400); + create_unimplemented_device("OPAMP", 0x40007800, 0x400); + create_unimplemented_device("LPTIM1", 0x40007C00, 0x400); + create_unimplemented_device("LPUART1", 0x40008000, 0x400); + /* RESERVED: 0x40008400, 0x400 */ + create_unimplemented_device("SWPMI1", 0x40008800, 0x400); + /* RESERVED: 0x40008C00, 0x800 */ + create_unimplemented_device("LPTIM2", 0x40009400, 0x400); + /* RESERVED: 0x40009800, 0x6800 */ + + /* APB2 BUS */ + 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 */ + create_unimplemented_device("SDMMC1", 0x40012800, 0x400); + create_unimplemented_device("TIM1", 0x40012C00, 0x400); + create_unimplemented_device("SPI1", 0x40013000, 0x400); + create_unimplemented_device("TIM8", 0x40013400, 0x400); + create_unimplemented_device("USART1", 0x40013800, 0x400); + /* RESERVED: 0x40013C00, 0x400 */ + create_unimplemented_device("TIM15", 0x40014000, 0x400); + create_unimplemented_device("TIM16", 0x40014400, 0x400); + create_unimplemented_device("TIM17", 0x40014800, 0x400); + /* RESERVED: 0x40014C00, 0x800 */ + create_unimplemented_device("SAI1", 0x40015400, 0x400); + create_unimplemented_device("SAI2", 0x40015800, 0x400); + /* RESERVED: 0x40015C00, 0x400 */ + create_unimplemented_device("DFSDM1", 0x40016000, 0x400); + /* RESERVED: 0x40016400, 0x9C00 */ + + /* AHB1 BUS */ + create_unimplemented_device("DMA1", 0x40020000, 0x400); + create_unimplemented_device("DMA2", 0x40020400, 0x400); + /* RESERVED: 0x40020800, 0x800 */ + create_unimplemented_device("RCC", 0x40021000, 0x400); + /* RESERVED: 0x40021400, 0xC00 */ + create_unimplemented_device("FLASH", 0x40022000, 0x400); + /* RESERVED: 0x40022400, 0xC00 */ + create_unimplemented_device("CRC", 0x40023000, 0x400); + /* RESERVED: 0x40023400, 0x400 */ + create_unimplemented_device("TSC", 0x40024000, 0x400); + + /* RESERVED: 0x40024400, 0x7FDBC00 */ + + /* AHB2 BUS */ + create_unimplemented_device("GPIOA", 0x48000000, 0x400); + create_unimplemented_device("GPIOB", 0x48000400, 0x400); + create_unimplemented_device("GPIOC", 0x48000800, 0x400); + create_unimplemented_device("GPIOD", 0x48000C00, 0x400); + create_unimplemented_device("GPIOE", 0x48001000, 0x400); + create_unimplemented_device("GPIOF", 0x48001400, 0x400); + create_unimplemented_device("GPIOG", 0x48001800, 0x400); + create_unimplemented_device("GPIOH", 0x48001C00, 0x400); + /* RESERVED: 0x48002000, 0x7FDBC00 */ + create_unimplemented_device("OTG_FS", 0x50000000, 0x40000); + create_unimplemented_device("ADC", 0x50040000, 0x400); + /* RESERVED: 0x50040400, 0x20400 */ + create_unimplemented_device("RNG", 0x50060800, 0x400); + + /* AHB3 BUS */ + create_unimplemented_device("FMC", 0xA0000000, 0x1000); + create_unimplemented_device("QUADSPI", 0xA0001000, 0x400); +} + +static void stm32l4x5_soc_class_init(ObjectClass *klass, void *data) +{ + + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = stm32l4x5_soc_realize; + /* Reason: Mapped at fixed location on the system bus */ + dc->user_creatable = false; + /* No vmstate or reset required: device has no internal state */ +} + +static void stm32l4x5xc_soc_class_init(ObjectClass *oc, void *data) +{ + Stm32l4x5SocClass *ssc = STM32L4X5_SOC_CLASS(oc); + + ssc->flash_size = 256 * KiB; +} + +static void stm32l4x5xe_soc_class_init(ObjectClass *oc, void *data) +{ + Stm32l4x5SocClass *ssc = STM32L4X5_SOC_CLASS(oc); + + ssc->flash_size = 512 * KiB; +} + +static void stm32l4x5xg_soc_class_init(ObjectClass *oc, void *data) +{ + Stm32l4x5SocClass *ssc = STM32L4X5_SOC_CLASS(oc); + + ssc->flash_size = 1 * MiB; +} + +static const TypeInfo stm32l4x5_soc_types[] = { + { + .name = TYPE_STM32L4X5XC_SOC, + .parent = TYPE_STM32L4X5_SOC, + .class_init = stm32l4x5xc_soc_class_init, + }, { + .name = TYPE_STM32L4X5XE_SOC, + .parent = TYPE_STM32L4X5_SOC, + .class_init = stm32l4x5xe_soc_class_init, + }, { + .name = TYPE_STM32L4X5XG_SOC, + .parent = TYPE_STM32L4X5_SOC, + .class_init = stm32l4x5xg_soc_class_init, + }, { + .name = TYPE_STM32L4X5_SOC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Stm32l4x5SocState), + .instance_init = stm32l4x5_soc_initfn, + .class_size = sizeof(Stm32l4x5SocClass), + .class_init = stm32l4x5_soc_class_init, + .abstract = true, + } +}; + +DEFINE_TYPES(stm32l4x5_soc_types) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -XXX,XX +XXX,XX @@ config STM32F405_SOC select STM32F4XX_SYSCFG select STM32F4XX_EXTI +config STM32L4X5_SOC + bool + select ARM_V7M + select OR_IRQ + config XLNX_ZYNQMP_ARM bool default y if PIXMAN diff --git a/hw/arm/meson.build b/hw/arm/meson.build index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -XXX,XX +XXX,XX @@ arm_ss.add(when: 'CONFIG_RASPI', if_true: files('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_STM32L4X5_SOC', if_true: files('stm32l4x5_soc.c')) arm_ss.add(when: 'CONFIG_XLNX_ZYNQMP_ARM', if_true: files('xlnx-zynqmp.c', 'xlnx-zcu102.c')) arm_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal.c', 'xlnx-versal-virt.c')) arm_ss.add(when: 'CONFIG_FSL_IMX25', if_true: files('fsl-imx25.c', 'imx25_pdk.c')) -- 2.34.1
From: Inès Varhol <ines.varhol@telecom-paris.fr> This commit adds a new B-L475E-IOT01A board using the STM32L475VG SoC as well as a dedicated documentation file. The implementation is derived from the Netduino Plus 2 machine. There are no peripherals implemented yet, only memory regions. Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Acked-by: Alistair Francis <alistair.francis@wdc.com> Signed-off-by: Arnaud Minier <arnaud.minier@telecom-paris.fr> Signed-off-by: Inès Varhol <ines.varhol@telecom-paris.fr> Message-id: 20240108135849.351719-3-ines.varhol@telecom-paris.fr Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- MAINTAINERS | 7 +++ docs/system/arm/b-l475e-iot01a.rst | 46 ++++++++++++++++ docs/system/arm/stm32.rst | 6 ++- docs/system/target-arm.rst | 1 + configs/devices/arm-softmmu/default.mak | 1 + hw/arm/b-l475e-iot01a.c | 72 +++++++++++++++++++++++++ hw/arm/Kconfig | 6 +++ hw/arm/meson.build | 1 + 8 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 docs/system/arm/b-l475e-iot01a.rst create mode 100644 hw/arm/b-l475e-iot01a.c diff --git a/MAINTAINERS b/MAINTAINERS index XXXXXXX..XXXXXXX 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -XXX,XX +XXX,XX @@ S: Maintained F: hw/arm/stm32l4x5_soc.c F: include/hw/arm/stm32l4x5_soc.h +B-L475E-IOT01A IoT Node +M: Arnaud Minier <arnaud.minier@telecom-paris.fr> +M: Inès Varhol <ines.varhol@telecom-paris.fr> +L: qemu-arm@nongnu.org +S: Maintained +F: hw/arm/b-l475e-iot01a.c + SmartFusion2 M: Subbaraya Sundeep <sundeep.lkml@gmail.com> M: Peter Maydell <peter.maydell@linaro.org> diff --git a/docs/system/arm/b-l475e-iot01a.rst b/docs/system/arm/b-l475e-iot01a.rst new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/docs/system/arm/b-l475e-iot01a.rst @@ -XXX,XX +XXX,XX @@ +B-L475E-IOT01A IoT Node (``b-l475e-iot01a``) +============================================ + +The B-L475E-IOT01A IoT Node uses the STM32L475VG SoC which is based on +ARM Cortex-M4F core. It is part of STMicroelectronics +:doc:`STM32 boards </system/arm/stm32>` and more specifically the STM32L4 +ultra-low power series. The STM32L4x5 chip runs at up to 80 MHz and +integrates 128 KiB of SRAM and up to 1MiB of Flash. The B-L475E-IOT01A board +namely features 64 Mibit QSPI Flash, BT, WiFi and RF connectivity, +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: + +- Cortex-M4F based STM32L4x5 SoC + +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) +- General-purpose I/Os (GPIO) +- Analog to Digital Converter (ADC) +- SPI controller +- Timer controller (TIMER) + +See the complete list of unimplemented peripheral devices +in the STM32L4x5 module : ``./hw/arm/stm32l4x5_soc.c`` + +Boot options +"""""""""""" + +The B-L475E-IOT01A machine can be started using the ``-kernel`` +option to load a firmware. Example: + +.. code-block:: bash + + $ qemu-system-arm -M b-l475e-iot01a -kernel firmware.bin + diff --git a/docs/system/arm/stm32.rst b/docs/system/arm/stm32.rst index XXXXXXX..XXXXXXX 100644 --- a/docs/system/arm/stm32.rst +++ b/docs/system/arm/stm32.rst @@ -XXX,XX +XXX,XX @@ 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 : +The STM32F4 series is based on ARM Cortex-M4F core, as well as the STM32L4 +ultra-low-power series. The STM32F4 series is pin-to-pin compatible with STM32F2 series. +The following machines are based on this ARM Cortex-M4F chip : - ``netduinoplus2`` Netduino Plus 2 board with STM32F405RGT6 microcontroller - ``olimex-stm32-h405`` Olimex STM32 H405 board with STM32F405RGT6 microcontroller +- ``b-l475e-iot01a`` :doc:`B-L475E-IOT01A IoT Node </system/arm/b-l475e-iot01a>` board with STM32L475VG microcontroller There are many other STM32 series that are currently not supported by QEMU. diff --git a/docs/system/target-arm.rst b/docs/system/target-arm.rst index XXXXXXX..XXXXXXX 100644 --- a/docs/system/target-arm.rst +++ b/docs/system/target-arm.rst @@ -XXX,XX +XXX,XX @@ undocumented; you can get a complete list by running arm/vexpress arm/aspeed arm/bananapi_m2u.rst + arm/b-l475e-iot01a.rst arm/sabrelite arm/digic arm/cubieboard diff --git a/configs/devices/arm-softmmu/default.mak b/configs/devices/arm-softmmu/default.mak index XXXXXXX..XXXXXXX 100644 --- a/configs/devices/arm-softmmu/default.mak +++ b/configs/devices/arm-softmmu/default.mak @@ -XXX,XX +XXX,XX @@ CONFIG_ARM_VIRT=y # CONFIG_NSERIES=n # CONFIG_STELLARIS=n # CONFIG_STM32VLDISCOVERY=n +# CONFIG_B_L475E_IOT01A=n # CONFIG_REALVIEW=n # CONFIG_VERSATILE=n # CONFIG_VEXPRESS=n diff --git a/hw/arm/b-l475e-iot01a.c b/hw/arm/b-l475e-iot01a.c new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/hw/arm/b-l475e-iot01a.c @@ -XXX,XX +XXX,XX @@ +/* + * B-L475E-IOT01A Discovery Kit machine + * (B-L475E-IOT01A IoT Node) + * + * Copyright (c) 2023 Arnaud Minier <arnaud.minier@telecom-paris.fr> + * Copyright (c) 2023 Inès Varhol <ines.varhol@telecom-paris.fr> + * + * 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 heavily inspired by the netduinoplus2 by Alistair Francis. + * Original code is licensed under the MIT License: + * + * Copyright (c) 2014 Alistair Francis <alistair@alistair23.me> + */ + +/* + * The reference used is the STMicroElectronics UM2153 User manual + * Discovery kit for IoT node, multi-channel communication with STM32L4. + * https://www.st.com/en/evaluation-tools/b-l475e-iot01a.html#documentation + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/boards.h" +#include "hw/qdev-properties.h" +#include "hw/qdev-clock.h" +#include "qemu/error-report.h" +#include "hw/arm/stm32l4x5_soc.h" +#include "hw/arm/boot.h" + +/* Main SYSCLK frequency in Hz (80MHz) */ +#define MAIN_SYSCLK_FREQ_HZ 80000000ULL + +static void b_l475e_iot01a_init(MachineState *machine) +{ + const Stm32l4x5SocClass *sc; + DeviceState *dev; + Clock *sysclk; + + /* This clock doesn't need migration because it is fixed-frequency */ + sysclk = clock_new(OBJECT(machine), "SYSCLK"); + clock_set_hz(sysclk, MAIN_SYSCLK_FREQ_HZ); + + dev = qdev_new(TYPE_STM32L4X5XG_SOC); + object_property_add_child(OBJECT(machine), "soc", OBJECT(dev)); + qdev_connect_clock_in(dev, "sysclk", sysclk); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + + sc = STM32L4X5_SOC_GET_CLASS(dev); + armv7m_load_kernel(ARM_CPU(first_cpu), + machine->kernel_filename, + 0, sc->flash_size); +} + +static void b_l475e_iot01a_machine_init(MachineClass *mc) +{ + static const char *machine_valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("cortex-m4"), + NULL + }; + mc->desc = "B-L475E-IOT01A Discovery Kit (Cortex-M4)"; + mc->init = b_l475e_iot01a_init; + mc->valid_cpu_types = machine_valid_cpu_types; + + /* SRAM pre-allocated as part of the SoC instantiation */ + mc->default_ram_size = 0; +} + +DEFINE_MACHINE("b-l475e-iot01a", b_l475e_iot01a_machine_init) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -XXX,XX +XXX,XX @@ config STM32F405_SOC select STM32F4XX_SYSCFG select STM32F4XX_EXTI +config B_L475E_IOT01A + bool + default y + depends on TCG && ARM + select STM32L4X5_SOC + config STM32L4X5_SOC bool select ARM_V7M diff --git a/hw/arm/meson.build b/hw/arm/meson.build index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -XXX,XX +XXX,XX @@ arm_ss.add(when: 'CONFIG_RASPI', if_true: files('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_B_L475E_IOT01A', if_true: files('b-l475e-iot01a.c')) arm_ss.add(when: 'CONFIG_STM32L4X5_SOC', if_true: files('stm32l4x5_soc.c')) arm_ss.add(when: 'CONFIG_XLNX_ZYNQMP_ARM', if_true: files('xlnx-zynqmp.c', 'xlnx-zcu102.c')) arm_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal.c', 'xlnx-versal-virt.c')) -- 2.34.1
From: Samuel Tardieu <sam@rfc1149.net> Cortex-M NVIC can have a different number of priority bits. Cortex-M0/M0+/M1 devices must use 2 or more bits, while devices based on ARMv7m and up must use 3 or more bits. This adds a "num-prio-bits" property which will get sensible default values if unset (2 or 8 depending on the device). Unless a SOC specifies the number of bits to use, the previous behavior is maintained for backward compatibility. Signed-off-by: Samuel Tardieu <sam@rfc1149.net> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20240106181503.1746200-2-sam@rfc1149.net Suggested-by: Anton Kochkov <anton.kochkov@proton.me> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1122 Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/intc/armv7m_nvic.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index XXXXXXX..XXXXXXX 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -XXX,XX +XXX,XX @@ static const VMStateDescription vmstate_nvic = { static Property props_nvic[] = { /* Number of external IRQ lines (so excluding the 16 internal exceptions) */ DEFINE_PROP_UINT32("num-irq", NVICState, num_irq, 64), + /* + * Number of the maximum priority bits that can be used. 0 means + * to use a reasonable default. + */ + DEFINE_PROP_UINT8("num-prio-bits", NVICState, num_prio_bits, 0), DEFINE_PROP_END_OF_LIST() }; @@ -XXX,XX +XXX,XX @@ static void armv7m_nvic_realize(DeviceState *dev, Error **errp) /* include space for internal exception vectors */ s->num_irq += NVIC_FIRST_IRQ; - s->num_prio_bits = arm_feature(&s->cpu->env, ARM_FEATURE_V7) ? 8 : 2; + if (s->num_prio_bits == 0) { + /* + * If left unspecified, use 2 bits by default on Cortex-M0/M0+/M1 + * and 8 bits otherwise. + */ + s->num_prio_bits = arm_feature(&s->cpu->env, ARM_FEATURE_V7) ? 8 : 2; + } else { + uint8_t min_prio_bits = + arm_feature(&s->cpu->env, ARM_FEATURE_V7) ? 3 : 2; + if (s->num_prio_bits < min_prio_bits || s->num_prio_bits > 8) { + error_setg(errp, + "num-prio-bits %d is outside " + "NVIC acceptable range [%d-8]", + s->num_prio_bits, min_prio_bits); + return; + } + } /* * This device provides a single memory region which covers the -- 2.34.1
From: Samuel Tardieu <sam@rfc1149.net> A SoC will not have a direct access to the NVIC embedded in its ARM core. By aliasing the "num-prio-bits" property similarly to what is done for the "num-irq" one, a SoC can easily configure it on its armv7m instance. Signed-off-by: Samuel Tardieu <sam@rfc1149.net> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20240106181503.1746200-3-sam@rfc1149.net Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- include/hw/arm/armv7m.h | 1 + hw/arm/armv7m.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/include/hw/arm/armv7m.h b/include/hw/arm/armv7m.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/arm/armv7m.h +++ b/include/hw/arm/armv7m.h @@ -XXX,XX +XXX,XX @@ OBJECT_DECLARE_SIMPLE_TYPE(ARMv7MState, ARMV7M) * a qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET). * + Property "cpu-type": CPU type to instantiate * + Property "num-irq": number of external IRQ lines + * + Property "num-prio-bits": number of priority bits in the NVIC * + Property "memory": MemoryRegion defining the physical address space * that CPU accesses see. (The NVIC, bitbanding and other CPU-internal * devices will be automatically layered on top of this view.) diff --git a/hw/arm/armv7m.c b/hw/arm/armv7m.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/armv7m.c +++ b/hw/arm/armv7m.c @@ -XXX,XX +XXX,XX @@ static void armv7m_instance_init(Object *obj) object_initialize_child(obj, "nvic", &s->nvic, TYPE_NVIC); object_property_add_alias(obj, "num-irq", OBJECT(&s->nvic), "num-irq"); + object_property_add_alias(obj, "num-prio-bits", + OBJECT(&s->nvic), "num-prio-bits"); object_initialize_child(obj, "systick-reg-ns", &s->systick[M_REG_NS], TYPE_SYSTICK); -- 2.34.1
From: Samuel Tardieu <sam@rfc1149.net> Update the number of priority bits for a number of existing SoCs according to their technical documentation: - STM32F100/F205/F405/L4x5: 4 bits - Stellaris (Sandstorm/Fury): 3 bits Signed-off-by: Samuel Tardieu <sam@rfc1149.net> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20240106181503.1746200-4-sam@rfc1149.net Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/stellaris.c | 2 ++ hw/arm/stm32f100_soc.c | 1 + hw/arm/stm32f205_soc.c | 1 + hw/arm/stm32f405_soc.c | 1 + hw/arm/stm32l4x5_soc.c | 1 + 5 files changed, 6 insertions(+) diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -XXX,XX +XXX,XX @@ #define BP_GAMEPAD 0x04 #define NUM_IRQ_LINES 64 +#define NUM_PRIO_BITS 3 typedef const struct { const char *name; @@ -XXX,XX +XXX,XX @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) nvic = qdev_new(TYPE_ARMV7M); qdev_prop_set_uint32(nvic, "num-irq", NUM_IRQ_LINES); + qdev_prop_set_uint8(nvic, "num-prio-bits", NUM_PRIO_BITS); qdev_prop_set_string(nvic, "cpu-type", ms->cpu_type); qdev_prop_set_bit(nvic, "enable-bitband", true); qdev_connect_clock_in(nvic, "cpuclk", diff --git a/hw/arm/stm32f100_soc.c b/hw/arm/stm32f100_soc.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/stm32f100_soc.c +++ b/hw/arm/stm32f100_soc.c @@ -XXX,XX +XXX,XX @@ static void stm32f100_soc_realize(DeviceState *dev_soc, Error **errp) /* Init ARMv7m */ armv7m = DEVICE(&s->armv7m); qdev_prop_set_uint32(armv7m, "num-irq", 61); + qdev_prop_set_uint8(armv7m, "num-prio-bits", 4); qdev_prop_set_string(armv7m, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m3")); qdev_prop_set_bit(armv7m, "enable-bitband", true); qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk); diff --git a/hw/arm/stm32f205_soc.c b/hw/arm/stm32f205_soc.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/stm32f205_soc.c +++ b/hw/arm/stm32f205_soc.c @@ -XXX,XX +XXX,XX @@ static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp) armv7m = DEVICE(&s->armv7m); qdev_prop_set_uint32(armv7m, "num-irq", 96); + qdev_prop_set_uint8(armv7m, "num-prio-bits", 4); qdev_prop_set_string(armv7m, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m3")); qdev_prop_set_bit(armv7m, "enable-bitband", true); qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk); diff --git a/hw/arm/stm32f405_soc.c b/hw/arm/stm32f405_soc.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/stm32f405_soc.c +++ b/hw/arm/stm32f405_soc.c @@ -XXX,XX +XXX,XX @@ static void stm32f405_soc_realize(DeviceState *dev_soc, Error **errp) armv7m = DEVICE(&s->armv7m); qdev_prop_set_uint32(armv7m, "num-irq", 96); + qdev_prop_set_uint8(armv7m, "num-prio-bits", 4); qdev_prop_set_string(armv7m, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m4")); qdev_prop_set_bit(armv7m, "enable-bitband", true); qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk); diff --git a/hw/arm/stm32l4x5_soc.c b/hw/arm/stm32l4x5_soc.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/stm32l4x5_soc.c +++ b/hw/arm/stm32l4x5_soc.c @@ -XXX,XX +XXX,XX @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp) object_initialize_child(OBJECT(dev_soc), "armv7m", &s->armv7m, TYPE_ARMV7M); armv7m = DEVICE(&s->armv7m); qdev_prop_set_uint32(armv7m, "num-irq", 96); + qdev_prop_set_uint32(armv7m, "num-prio-bits", 4); qdev_prop_set_string(armv7m, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m4")); qdev_prop_set_bit(armv7m, "enable-bitband", true); qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk); -- 2.34.1
From: Philippe Mathieu-Daudé <philmd@linaro.org> QDev objects created with qdev_new() need to manually add their parent relationship with object_property_add_child(). Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Message-id: 20240104141159.53883-1-philmd@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/msf2-som.c | 1 + hw/arm/netduino2.c | 1 + hw/arm/netduinoplus2.c | 1 + hw/arm/olimex-stm32-h405.c | 1 + hw/arm/stm32vldiscovery.c | 1 + 5 files changed, 5 insertions(+) diff --git a/hw/arm/msf2-som.c b/hw/arm/msf2-som.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/msf2-som.c +++ b/hw/arm/msf2-som.c @@ -XXX,XX +XXX,XX @@ static void emcraft_sf2_s2s010_init(MachineState *machine) memory_region_add_subregion(sysmem, DDR_BASE_ADDRESS, ddr); dev = qdev_new(TYPE_MSF2_SOC); + object_property_add_child(OBJECT(machine), "soc", OBJECT(dev)); qdev_prop_set_string(dev, "part-name", "M2S010"); qdev_prop_set_string(dev, "cpu-type", mc->default_cpu_type); diff --git a/hw/arm/netduino2.c b/hw/arm/netduino2.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/netduino2.c +++ b/hw/arm/netduino2.c @@ -XXX,XX +XXX,XX @@ static void netduino2_init(MachineState *machine) clock_set_hz(sysclk, SYSCLK_FRQ); dev = qdev_new(TYPE_STM32F205_SOC); + object_property_add_child(OBJECT(machine), "soc", OBJECT(dev)); qdev_connect_clock_in(dev, "sysclk", sysclk); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); diff --git a/hw/arm/netduinoplus2.c b/hw/arm/netduinoplus2.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/netduinoplus2.c +++ b/hw/arm/netduinoplus2.c @@ -XXX,XX +XXX,XX @@ static void netduinoplus2_init(MachineState *machine) clock_set_hz(sysclk, SYSCLK_FRQ); dev = qdev_new(TYPE_STM32F405_SOC); + object_property_add_child(OBJECT(machine), "soc", OBJECT(dev)); qdev_connect_clock_in(dev, "sysclk", sysclk); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); diff --git a/hw/arm/olimex-stm32-h405.c b/hw/arm/olimex-stm32-h405.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/olimex-stm32-h405.c +++ b/hw/arm/olimex-stm32-h405.c @@ -XXX,XX +XXX,XX @@ static void olimex_stm32_h405_init(MachineState *machine) clock_set_hz(sysclk, SYSCLK_FRQ); dev = qdev_new(TYPE_STM32F405_SOC); + object_property_add_child(OBJECT(machine), "soc", OBJECT(dev)); qdev_connect_clock_in(dev, "sysclk", sysclk); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); diff --git a/hw/arm/stm32vldiscovery.c b/hw/arm/stm32vldiscovery.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/stm32vldiscovery.c +++ b/hw/arm/stm32vldiscovery.c @@ -XXX,XX +XXX,XX @@ static void stm32vldiscovery_init(MachineState *machine) clock_set_hz(sysclk, SYSCLK_FRQ); dev = qdev_new(TYPE_STM32F100_SOC); + object_property_add_child(OBJECT(machine), "soc", OBJECT(dev)); qdev_connect_clock_in(dev, "sysclk", sysclk); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); -- 2.34.1
The CTR_EL0 register has some bits which allow the implementation to tell the guest that it does not need to do cache maintenance for data-to-instruction coherence and instruction-to-data coherence. QEMU doesn't emulate caches and so our cache maintenance insns are all NOPs. We already have some models of specific CPUs where we set these bits (e.g. the Neoverse V1), but the 'max' CPU still uses the settings it inherits from Cortex-A57. Set the bits for 'max' as well, so the guest doesn't need to do unnecessary work. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Miguel Luis <miguel.luis@oracle.com> --- target/arm/tcg/cpu64.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -XXX,XX +XXX,XX @@ void aarch64_max_tcg_initfn(Object *obj) u = FIELD_DP32(u, CLIDR_EL1, LOUU, 0); cpu->clidr = u; + /* + * Set CTR_EL0.DIC and IDC to tell the guest it doesnt' need to + * do any cache maintenance for data-to-instruction or + * instruction-to-guest coherence. (Our cache ops are nops.) + */ + t = cpu->ctr; + t = FIELD_DP64(t, CTR_EL0, IDC, 1); + t = FIELD_DP64(t, CTR_EL0, DIC, 1); + cpu->ctr = t; + t = cpu->isar.id_aa64isar0; t = FIELD_DP64(t, ID_AA64ISAR0, AES, 2); /* FEAT_PMULL */ t = FIELD_DP64(t, ID_AA64ISAR0, SHA1, 1); /* FEAT_SHA1 */ -- 2.34.1
The hypervisor can deliver (virtual) LPIs to a guest by setting up a list register to have an intid which is an LPI. The GIC has to treat these a little differently to standard interrupt IDs, because LPIs have no Active state, and so the guest will only EOI them, it will not also deactivate them. So icv_eoir_write() must do two things: * if the LPI ID is not in any list register, we drop the priority but do not increment the EOI count * if the LPI ID is in a list register, we immediately deactivate it, regardless of the split-drop-and-deactivate control This can be seen in the VirtualWriteEOIR0() and VirtualWriteEOIR1() pseudocode in the GICv3 architecture specification. Without this fix, potentially a hypervisor guest might stall because LPIs get stuck in a bogus Active+Pending state. Cc: qemu-stable@nongnu.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Miguel Luis <miguel.luis@oracle.com> --- hw/intc/arm_gicv3_cpuif.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index XXXXXXX..XXXXXXX 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -XXX,XX +XXX,XX @@ static void icv_eoir_write(CPUARMState *env, const ARMCPRegInfo *ri, idx = icv_find_active(cs, irq); if (idx < 0) { - /* No valid list register corresponding to EOI ID */ - icv_increment_eoicount(cs); + /* + * No valid list register corresponding to EOI ID; if this is a vLPI + * not in the list regs then do nothing; otherwise increment EOI count + */ + if (irq < GICV3_LPI_INTID_START) { + icv_increment_eoicount(cs); + } } else { uint64_t lr = cs->ich_lr_el2[idx]; int thisgrp = (lr & ICH_LR_EL2_GROUP) ? GICV3_G1NS : GICV3_G0; int lr_gprio = ich_lr_prio(lr) & icv_gprio_mask(cs, grp); if (thisgrp == grp && lr_gprio == dropprio) { - if (!icv_eoi_split(env, cs)) { - /* Priority drop and deactivate not split: deactivate irq now */ + if (!icv_eoi_split(env, cs) || irq >= GICV3_LPI_INTID_START) { + /* + * Priority drop and deactivate not split: deactivate irq now. + * LPIs always get their active state cleared immediately + * because no separate deactivate is expected. + */ icv_deactivate_irq(cs, idx); } } -- 2.34.1
FEAT_NV defines three new bits in HCR_EL2: NV, NV1 and AT. When the feature is enabled, allow these bits to be written, and flush the TLBs for the bits which affect page table interpretation. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Miguel Luis <miguel.luis@oracle.com> --- target/arm/cpu-features.h | 5 +++++ target/arm/helper.c | 6 +++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -XXX,XX +XXX,XX @@ static inline bool isar_feature_aa64_e0pd(const ARMISARegisters *id) return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, E0PD) != 0; } +static inline bool isar_feature_aa64_nv(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, NV) != 0; +} + static inline bool isar_feature_aa64_pmuv3p1(const ARMISARegisters *id) { return FIELD_EX64(id->id_aa64dfr0, ID_AA64DFR0, PMUVER) >= 4 && diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ static void do_hcr_write(CPUARMState *env, uint64_t value, uint64_t valid_mask) if (cpu_isar_feature(aa64_rme, cpu)) { valid_mask |= HCR_GPF; } + if (cpu_isar_feature(aa64_nv, cpu)) { + valid_mask |= HCR_NV | HCR_NV1 | HCR_AT; + } } if (cpu_isar_feature(any_evt, cpu)) { @@ -XXX,XX +XXX,XX @@ static void do_hcr_write(CPUARMState *env, uint64_t value, uint64_t valid_mask) * HCR_DC disables stage1 and enables stage2 translation * HCR_DCT enables tagging on (disabled) stage1 translation * HCR_FWB changes the interpretation of stage2 descriptor bits + * HCR_NV and HCR_NV1 affect interpretation of descriptor bits */ if ((env->cp15.hcr_el2 ^ value) & - (HCR_VM | HCR_PTW | HCR_DC | HCR_DCT | HCR_FWB)) { + (HCR_VM | HCR_PTW | HCR_DC | HCR_DCT | HCR_FWB | HCR_NV | HCR_NV1)) { tlb_flush(CPU(cpu)); } env->cp15.hcr_el2 = value; -- 2.34.1
The FEAT_NV HCR_EL2.AT bit enables trapping of some address translation instructions from EL1 to EL2. Implement this behaviour. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Miguel Luis <miguel.luis@oracle.com> --- target/arm/helper.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ static CPAccessResult at_s1e2_access(CPUARMState *env, const ARMCPRegInfo *ri, return at_e012_access(env, ri, isread); } +static CPAccessResult at_s1e01_access(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + if (arm_current_el(env) == 1 && (arm_hcr_el2_eff(env) & HCR_AT)) { + return CP_ACCESS_TRAP_EL2; + } + return at_e012_access(env, ri, isread); +} + static void ats_write64(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo v8_cp_reginfo[] = { .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 0, .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, .fgt = FGT_ATS1E1R, - .accessfn = at_e012_access, .writefn = ats_write64 }, + .accessfn = at_s1e01_access, .writefn = ats_write64 }, { .name = "AT_S1E1W", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 1, .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, .fgt = FGT_ATS1E1W, - .accessfn = at_e012_access, .writefn = ats_write64 }, + .accessfn = at_s1e01_access, .writefn = ats_write64 }, { .name = "AT_S1E0R", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 2, .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, .fgt = FGT_ATS1E0R, - .accessfn = at_e012_access, .writefn = ats_write64 }, + .accessfn = at_s1e01_access, .writefn = ats_write64 }, { .name = "AT_S1E0W", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 3, .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, .fgt = FGT_ATS1E0W, - .accessfn = at_e012_access, .writefn = ats_write64 }, + .accessfn = at_s1e01_access, .writefn = ats_write64 }, { .name = "AT_S12E1R", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 4, .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo ats1e1_reginfo[] = { .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 9, .opc2 = 0, .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, .fgt = FGT_ATS1E1RP, - .accessfn = at_e012_access, .writefn = ats_write64 }, + .accessfn = at_s1e01_access, .writefn = ats_write64 }, { .name = "AT_S1E1WP", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 9, .opc2 = 1, .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, .fgt = FGT_ATS1E1WP, - .accessfn = at_e012_access, .writefn = ats_write64 }, + .accessfn = at_s1e01_access, .writefn = ats_write64 }, }; static const ARMCPRegInfo ats1cp_reginfo[] = { -- 2.34.1
When FEAT_NV is turned on via the HCR_EL2.NV bit, ERET instructions are trapped, with the same syndrome information as for the existing FEAT_FGT fine-grained trap (in the pseudocode this is handled in AArch64.CheckForEretTrap()). Rename the DisasContext and tbflag bits to reflect that they are no longer exclusively for FGT traps, and set the tbflag bit when FEAT_NV is enabled as well as when the FGT is enabled. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Miguel Luis <miguel.luis@oracle.com> --- target/arm/cpu.h | 2 +- target/arm/tcg/translate.h | 4 ++-- target/arm/tcg/hflags.c | 11 ++++++++++- target/arm/tcg/translate-a64.c | 6 +++--- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -XXX,XX +XXX,XX @@ FIELD(TBFLAG_A64, PSTATE_ZA, 23, 1) FIELD(TBFLAG_A64, SVL, 24, 4) /* Indicates that SME Streaming mode is active, and SMCR_ELx.FA64 is not. */ FIELD(TBFLAG_A64, SME_TRAP_NONSTREAMING, 28, 1) -FIELD(TBFLAG_A64, FGT_ERET, 29, 1) +FIELD(TBFLAG_A64, TRAP_ERET, 29, 1) FIELD(TBFLAG_A64, NAA, 30, 1) FIELD(TBFLAG_A64, ATA0, 31, 1) diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -XXX,XX +XXX,XX @@ typedef struct DisasContext { bool mve_no_pred; /* True if fine-grained traps are active */ bool fgt_active; - /* True if fine-grained trap on ERET is enabled */ - bool fgt_eret; /* True if fine-grained trap on SVC is enabled */ bool fgt_svc; + /* True if a trap on ERET is enabled (FGT or NV) */ + bool trap_eret; /* True if FEAT_LSE2 SCTLR_ELx.nAA is set */ bool naa; /* diff --git a/target/arm/tcg/hflags.c b/target/arm/tcg/hflags.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/hflags.c +++ b/target/arm/tcg/hflags.c @@ -XXX,XX +XXX,XX @@ static CPUARMTBFlags rebuild_hflags_a64(CPUARMState *env, int el, int fp_el, CPUARMTBFlags flags = {}; ARMMMUIdx stage1 = stage_1_mmu_idx(mmu_idx); uint64_t tcr = regime_tcr(env, mmu_idx); + uint64_t hcr = arm_hcr_el2_eff(env); uint64_t sctlr; int tbii, tbid; @@ -XXX,XX +XXX,XX @@ static CPUARMTBFlags rebuild_hflags_a64(CPUARMState *env, int el, int fp_el, if (arm_fgt_active(env, el)) { DP_TBFLAG_ANY(flags, FGT_ACTIVE, 1); if (FIELD_EX64(env->cp15.fgt_exec[FGTREG_HFGITR], HFGITR_EL2, ERET)) { - DP_TBFLAG_A64(flags, FGT_ERET, 1); + DP_TBFLAG_A64(flags, TRAP_ERET, 1); } if (fgt_svc(env, el)) { DP_TBFLAG_ANY(flags, FGT_SVC, 1); } } + /* + * ERET can also be trapped for FEAT_NV. arm_hcr_el2_eff() takes care + * of "is EL2 enabled" and the NV bit can only be set if FEAT_NV is present. + */ + if (el == 1 && (hcr & HCR_NV)) { + DP_TBFLAG_A64(flags, TRAP_ERET, 1); + } + if (cpu_isar_feature(aa64_mte, env_archcpu(env))) { /* * Set MTE_ACTIVE if any access may be Checked, and leave clear diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -XXX,XX +XXX,XX @@ static bool trans_ERET(DisasContext *s, arg_ERET *a) if (s->current_el == 0) { return false; } - if (s->fgt_eret) { + if (s->trap_eret) { gen_exception_insn_el(s, 0, EXCP_UDEF, syn_erettrap(0), 2); return true; } @@ -XXX,XX +XXX,XX @@ static bool trans_ERETA(DisasContext *s, arg_reta *a) return false; } /* The FGT trap takes precedence over an auth trap. */ - if (s->fgt_eret) { + if (s->trap_eret) { gen_exception_insn_el(s, 0, EXCP_UDEF, syn_erettrap(a->m ? 3 : 2), 2); return true; } @@ -XXX,XX +XXX,XX @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase, dc->pstate_il = EX_TBFLAG_ANY(tb_flags, PSTATE__IL); dc->fgt_active = EX_TBFLAG_ANY(tb_flags, FGT_ACTIVE); dc->fgt_svc = EX_TBFLAG_ANY(tb_flags, FGT_SVC); - dc->fgt_eret = EX_TBFLAG_A64(tb_flags, FGT_ERET); + dc->trap_eret = EX_TBFLAG_A64(tb_flags, TRAP_ERET); dc->sve_excp_el = EX_TBFLAG_A64(tb_flags, SVEEXC_EL); dc->sme_excp_el = EX_TBFLAG_A64(tb_flags, SMEEXC_EL); dc->vl = (EX_TBFLAG_A64(tb_flags, VL) + 1) * 16; -- 2.34.1
The HCR_EL2.TSC trap for trapping EL1 execution of SMC instructions has a behaviour change for FEAT_NV when EL3 is not implemented: * in older architecture versions TSC was required to have no effect (i.e. the SMC insn UNDEFs) * with FEAT_NV, when HCR_EL2.NV == 1 the trap must apply (i.e. SMC traps to EL2, as it already does in all cases when EL3 is implemented) * in newer architecture versions, the behaviour either without FEAT_NV or with FEAT_NV and HCR_EL2.NV == 0 is relaxed to an IMPDEF choice between UNDEF and trap-to-EL2 (i.e. it is permitted to always honour HCR_EL2.TSC) for AArch64 only Add the condition to honour the trap bit when HCR_EL2.NV == 1. We leave the HCR_EL2.NV == 0 case with the existing (UNDEF) behaviour, as our IMPDEF choice (both because it avoids a behaviour change for older CPU models and because we'd have to distinguish AArch32 from AArch64 if we opted to trap to EL2). Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Miguel Luis <miguel.luis@oracle.com> --- target/arm/tcg/op_helper.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/target/arm/tcg/op_helper.c b/target/arm/tcg/op_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/op_helper.c +++ b/target/arm/tcg/op_helper.c @@ -XXX,XX +XXX,XX @@ void HELPER(pre_smc)(CPUARMState *env, uint32_t syndrome) * * Conduit SMC, valid call Trap to EL2 PSCI Call * Conduit SMC, inval call Trap to EL2 Undef insn - * Conduit not SMC Undef insn Undef insn + * Conduit not SMC Undef or trap[1] Undef insn + * + * [1] In this case: + * - if HCR_EL2.NV == 1 we must trap to EL2 + * - if HCR_EL2.NV == 0 then newer architecture revisions permit + * AArch64 (but not AArch32) to trap to EL2 as an IMPDEF choice + * - otherwise we must UNDEF + * We take the IMPDEF choice to always UNDEF if HCR_EL2.NV == 0. */ /* On ARMv8 with EL3 AArch64, SMD applies to both S and NS state. @@ -XXX,XX +XXX,XX @@ void HELPER(pre_smc)(CPUARMState *env, uint32_t syndrome) : smd_flag && !secure; if (!arm_feature(env, ARM_FEATURE_EL3) && + !(arm_hcr_el2_eff(env) & HCR_NV) && cpu->psci_conduit != QEMU_PSCI_CONDUIT_SMC) { - /* If we have no EL3 then SMC always UNDEFs and can't be - * trapped to EL2. PSCI-via-SMC is a sort of ersatz EL3 + /* + * If we have no EL3 then traditionally SMC always UNDEFs and can't be + * trapped to EL2. For nested virtualization, SMC can be trapped to + * the outer hypervisor. PSCI-via-SMC is a sort of ersatz EL3 * firmware within QEMU, and we want an EL2 guest to be able * to forbid its EL1 from making PSCI calls into QEMU's * "firmware" via HCR.TSC, so for these purposes treat -- 2.34.1
The TBFLAG_A64 TB flag bits go in flags2, which for AArch64 guests we know is 64 bits. However at the moment we use FIELD_EX32() and FIELD_DP32() to read and write these bits, which only works for bits 0 to 31. Since we're about to add a flag that uses bit 32, switch to FIELD_EX64() and FIELD_DP64() so that this will work. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Miguel Luis <miguel.luis@oracle.com> --- target/arm/cpu.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -XXX,XX +XXX,XX @@ FIELD(TBFLAG_A64, NAA, 30, 1) FIELD(TBFLAG_A64, ATA0, 31, 1) /* - * Helpers for using the above. + * Helpers for using the above. Note that only the A64 accessors use + * FIELD_DP64() and FIELD_EX64(), because in the other cases the flags + * word either is or might be 32 bits only. */ #define DP_TBFLAG_ANY(DST, WHICH, VAL) \ (DST.flags = FIELD_DP32(DST.flags, TBFLAG_ANY, WHICH, VAL)) #define DP_TBFLAG_A64(DST, WHICH, VAL) \ - (DST.flags2 = FIELD_DP32(DST.flags2, TBFLAG_A64, WHICH, VAL)) + (DST.flags2 = FIELD_DP64(DST.flags2, TBFLAG_A64, WHICH, VAL)) #define DP_TBFLAG_A32(DST, WHICH, VAL) \ (DST.flags2 = FIELD_DP32(DST.flags2, TBFLAG_A32, WHICH, VAL)) #define DP_TBFLAG_M32(DST, WHICH, VAL) \ @@ -XXX,XX +XXX,XX @@ FIELD(TBFLAG_A64, ATA0, 31, 1) (DST.flags2 = FIELD_DP32(DST.flags2, TBFLAG_AM32, WHICH, VAL)) #define EX_TBFLAG_ANY(IN, WHICH) FIELD_EX32(IN.flags, TBFLAG_ANY, WHICH) -#define EX_TBFLAG_A64(IN, WHICH) FIELD_EX32(IN.flags2, TBFLAG_A64, WHICH) +#define EX_TBFLAG_A64(IN, WHICH) FIELD_EX64(IN.flags2, TBFLAG_A64, WHICH) #define EX_TBFLAG_A32(IN, WHICH) FIELD_EX32(IN.flags2, TBFLAG_A32, WHICH) #define EX_TBFLAG_M32(IN, WHICH) FIELD_EX32(IN.flags2, TBFLAG_M32, WHICH) #define EX_TBFLAG_AM32(IN, WHICH) FIELD_EX32(IN.flags2, TBFLAG_AM32, WHICH) -- 2.34.1
For FEAT_VHE, we define a set of register aliases, so that for instance: * the SCTLR_EL1 either accesses the real SCTLR_EL1, or (if E2H is 1) SCTLR_EL2 * a new SCTLR_EL12 register accesses SCTLR_EL1 if E2H is 1 However when we create the 'new_reg' cpreg struct for the SCTLR_EL12 register, we duplicate the information in the SCTLR_EL1 cpreg, which means the opcode fields are those of SCTLR_EL1, not SCTLR_EL12. This is a problem for code which looks at the cpreg opcode fields to determine behaviour (e.g. in access_check_cp_reg()). In practice the current checks we do there don't intersect with the *_EL12 registers, but for FEAT_NV this will become a problem. Write the correct values from the encoding into the new_reg struct. This restores the invariant that the cpreg that you get back from the hashtable has opcode fields that match the key you used to retrieve it. When we call the readfn or writefn for the target register, we pass it the cpreg struct for that target register, not the one for the alias, in case the readfn/writefn want to look at the opcode fields to determine behaviour. This means we need to interpose custom read/writefns for the e12 aliases. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Miguel Luis <miguel.luis@oracle.com> --- target/arm/helper.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ static void el2_e2h_write(CPUARMState *env, const ARMCPRegInfo *ri, writefn(env, ri, value); } +static uint64_t el2_e2h_e12_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + /* Pass the EL1 register accessor its ri, not the EL12 alias ri */ + return ri->orig_readfn(env, ri->opaque); +} + +static void el2_e2h_e12_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* Pass the EL1 register accessor its ri, not the EL12 alias ri */ + return ri->orig_writefn(env, ri->opaque, value); +} + static void define_arm_vh_e2h_redirects_aliases(ARMCPU *cpu) { struct E2HAlias { @@ -XXX,XX +XXX,XX @@ static void define_arm_vh_e2h_redirects_aliases(ARMCPU *cpu) new_reg->type |= ARM_CP_ALIAS; /* Remove PL1/PL0 access, leaving PL2/PL3 R/W in place. */ new_reg->access &= PL2_RW | PL3_RW; + /* The new_reg op fields are as per new_key, not the target reg */ + new_reg->crn = (a->new_key & CP_REG_ARM64_SYSREG_CRN_MASK) + >> CP_REG_ARM64_SYSREG_CRN_SHIFT; + new_reg->crm = (a->new_key & CP_REG_ARM64_SYSREG_CRM_MASK) + >> CP_REG_ARM64_SYSREG_CRM_SHIFT; + new_reg->opc0 = (a->new_key & CP_REG_ARM64_SYSREG_OP0_MASK) + >> CP_REG_ARM64_SYSREG_OP0_SHIFT; + new_reg->opc1 = (a->new_key & CP_REG_ARM64_SYSREG_OP1_MASK) + >> CP_REG_ARM64_SYSREG_OP1_SHIFT; + new_reg->opc2 = (a->new_key & CP_REG_ARM64_SYSREG_OP2_MASK) + >> CP_REG_ARM64_SYSREG_OP2_SHIFT; + new_reg->opaque = src_reg; + new_reg->orig_readfn = src_reg->readfn ?: raw_read; + new_reg->orig_writefn = src_reg->writefn ?: raw_write; + if (!new_reg->raw_readfn) { + new_reg->raw_readfn = raw_read; + } + if (!new_reg->raw_writefn) { + new_reg->raw_writefn = raw_write; + } + new_reg->readfn = el2_e2h_e12_read; + new_reg->writefn = el2_e2h_e12_write; ok = g_hash_table_insert(cpu->cp_regs, (gpointer)(uintptr_t)a->new_key, new_reg); -- 2.34.1
The alias registers like SCTLR_EL12 only exist when HCR_EL2.E2H is 1; they should UNDEF otherwise. We weren't implementing this. Add an intercept of the accessfn for these aliases, and implement the UNDEF check. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Miguel Luis <miguel.luis@oracle.com> --- target/arm/cpregs.h | 3 ++- target/arm/helper.c | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -XXX,XX +XXX,XX @@ struct ARMCPRegInfo { CPResetFn *resetfn; /* - * "Original" writefn and readfn. + * "Original" readfn, writefn, accessfn. * For ARMv8.1-VHE register aliases, we overwrite the read/write * accessor functions of various EL1/EL0 to perform the runtime * check for which sysreg should actually be modified, and then @@ -XXX,XX +XXX,XX @@ struct ARMCPRegInfo { */ CPReadFn *orig_readfn; CPWriteFn *orig_writefn; + CPAccessFn *orig_accessfn; }; /* diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ static void el2_e2h_e12_write(CPUARMState *env, const ARMCPRegInfo *ri, return ri->orig_writefn(env, ri->opaque, value); } +static CPAccessResult el2_e2h_e12_access(CPUARMState *env, + const ARMCPRegInfo *ri, + bool isread) +{ + /* FOO_EL12 aliases only exist when E2H is 1; otherwise they UNDEF */ + if (!(arm_hcr_el2_eff(env) & HCR_E2H)) { + return CP_ACCESS_TRAP_UNCATEGORIZED; + } + if (ri->orig_accessfn) { + return ri->orig_accessfn(env, ri->opaque, isread); + } + return CP_ACCESS_OK; +} + static void define_arm_vh_e2h_redirects_aliases(ARMCPU *cpu) { struct E2HAlias { @@ -XXX,XX +XXX,XX @@ static void define_arm_vh_e2h_redirects_aliases(ARMCPU *cpu) new_reg->opaque = src_reg; new_reg->orig_readfn = src_reg->readfn ?: raw_read; new_reg->orig_writefn = src_reg->writefn ?: raw_write; + new_reg->orig_accessfn = src_reg->accessfn; if (!new_reg->raw_readfn) { new_reg->raw_readfn = raw_read; } @@ -XXX,XX +XXX,XX @@ static void define_arm_vh_e2h_redirects_aliases(ARMCPU *cpu) } new_reg->readfn = el2_e2h_e12_read; new_reg->writefn = el2_e2h_e12_write; + new_reg->accessfn = el2_e2h_e12_access; ok = g_hash_table_insert(cpu->cp_regs, (gpointer)(uintptr_t)a->new_key, new_reg); -- 2.34.1
FEAT_NV and FEAT_NV2 will allow EL1 to attempt to access cpregs that only exist at EL2. This means we're going to want to run their accessfns when the CPU is at EL1. In almost all cases, the behaviour we want is "the accessfn returns OK if at EL1". Mostly the accessfn already does the right thing; in a few cases we need to explicitly check that the EL is not 1 before applying various trap controls, or split out an accessfn used both for an _EL1 and an _EL2 register into two so we can handle the FEAT_NV case correctly for the _EL2 register. There are two registers where we want the accessfn to trap for a FEAT_NV EL1 access: VSTTBR_EL2 and VSTCR_EL2 should UNDEF an access from NonSecure EL1, not trap to EL2 under FEAT_NV. The way we have written sel2_access() already results in this behaviour. We can identify the registers we care about here because they all have opc1 == 4 or 5. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Miguel Luis <miguel.luis@oracle.com> --- target/arm/debug_helper.c | 12 +++++++- target/arm/helper.c | 65 ++++++++++++++++++++++++++++++++++----- 2 files changed, 69 insertions(+), 8 deletions(-) diff --git a/target/arm/debug_helper.c b/target/arm/debug_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/debug_helper.c +++ b/target/arm/debug_helper.c @@ -XXX,XX +XXX,XX @@ static CPAccessResult access_tda(CPUARMState *env, const ARMCPRegInfo *ri, return CP_ACCESS_OK; } +static CPAccessResult access_dbgvcr32(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + /* MCDR_EL3.TDMA doesn't apply for FEAT_NV traps */ + if (arm_current_el(env) == 2 && (env->cp15.mdcr_el3 & MDCR_TDA)) { + return CP_ACCESS_TRAP_EL3; + } + return CP_ACCESS_OK; +} + /* * Check for traps to Debug Comms Channel registers. If FEAT_FGT * is implemented then these are controlled by MDCR_EL2.TDCC for @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo debug_aa32_el1_reginfo[] = { */ { .name = "DBGVCR32_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 2, .opc1 = 4, .crn = 0, .crm = 7, .opc2 = 0, - .access = PL2_RW, .accessfn = access_tda, + .access = PL2_RW, .accessfn = access_dbgvcr32, .type = ARM_CP_NOP | ARM_CP_EL3_NO_EL2_KEEP }, }; diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { static CPAccessResult e2h_access(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { + if (arm_current_el(env) == 1) { + /* This must be a FEAT_NV access */ + /* TODO: FEAT_ECV will need to check CNTHCTL_EL2 here */ + return CP_ACCESS_OK; + } if (!(arm_hcr_el2_eff(env) & HCR_E2H)) { return CP_ACCESS_TRAP; } @@ -XXX,XX +XXX,XX @@ static void hcrx_write(CPUARMState *env, const ARMCPRegInfo *ri, static CPAccessResult access_hxen(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { - if (arm_current_el(env) < 3 + if (arm_current_el(env) == 2 && arm_feature(env, ARM_FEATURE_EL3) && !(env->cp15.scr_el3 & SCR_HXEN)) { return CP_ACCESS_TRAP_EL3; @@ -XXX,XX +XXX,XX @@ static CPAccessResult el2_e2h_e12_access(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { + if (arm_current_el(env) == 1) { + /* + * This must be a FEAT_NV access (will either trap or redirect + * to memory). None of the registers with _EL12 aliases want to + * apply their trap controls for this kind of access, so don't + * call the orig_accessfn or do the "UNDEF when E2H is 0" check. + */ + return CP_ACCESS_OK; + } /* FOO_EL12 aliases only exist when E2H is 1; otherwise they UNDEF */ if (!(arm_hcr_el2_eff(env) & HCR_E2H)) { return CP_ACCESS_TRAP_UNCATEGORIZED; @@ -XXX,XX +XXX,XX @@ static CPAccessResult access_tpidr2(CPUARMState *env, const ARMCPRegInfo *ri, return CP_ACCESS_OK; } -static CPAccessResult access_esm(CPUARMState *env, const ARMCPRegInfo *ri, - bool isread) +static CPAccessResult access_smprimap(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + /* If EL1 this is a FEAT_NV access and CPTR_EL3.ESM doesn't apply */ + if (arm_current_el(env) == 2 + && arm_feature(env, ARM_FEATURE_EL3) + && !FIELD_EX64(env->cp15.cptr_el[3], CPTR_EL3, ESM)) { + return CP_ACCESS_TRAP_EL3; + } + return CP_ACCESS_OK; +} + +static CPAccessResult access_smpri(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) { - /* TODO: FEAT_FGT for SMPRI_EL1 but not SMPRIMAP_EL2 */ if (arm_current_el(env) < 3 && arm_feature(env, ARM_FEATURE_EL3) && !FIELD_EX64(env->cp15.cptr_el[3], CPTR_EL3, ESM)) { @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo sme_reginfo[] = { */ { .name = "SMPRI_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 1, .crm = 2, .opc2 = 4, - .access = PL1_RW, .accessfn = access_esm, + .access = PL1_RW, .accessfn = access_smpri, .fgt = FGT_NSMPRI_EL1, .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "SMPRIMAP_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 2, .opc2 = 5, - .access = PL2_RW, .accessfn = access_esm, + .access = PL2_RW, .accessfn = access_smprimap, .type = ARM_CP_CONST, .resetvalue = 0 }, }; @@ -XXX,XX +XXX,XX @@ static CPAccessResult access_mte(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { int el = arm_current_el(env); + if (el < 2 && arm_is_el2_enabled(env)) { + uint64_t hcr = arm_hcr_el2_eff(env); + if (!(hcr & HCR_ATA) && (!(hcr & HCR_E2H) || !(hcr & HCR_TGE))) { + return CP_ACCESS_TRAP_EL2; + } + } + if (el < 3 && + arm_feature(env, ARM_FEATURE_EL3) && + !(env->cp15.scr_el3 & SCR_ATA)) { + return CP_ACCESS_TRAP_EL3; + } + return CP_ACCESS_OK; +} +static CPAccessResult access_tfsr_el2(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + /* + * TFSR_EL2: similar to generic access_mte(), but we need to + * account for FEAT_NV. At EL1 this must be a FEAT_NV access; + * we will trap to EL2 and the HCR/SCR traps do not apply. + */ + int el = arm_current_el(env); + + if (el == 1) { + return CP_ACCESS_OK; + } if (el < 2 && arm_is_el2_enabled(env)) { uint64_t hcr = arm_hcr_el2_eff(env); if (!(hcr & HCR_ATA) && (!(hcr & HCR_E2H) || !(hcr & HCR_TGE))) { @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo mte_reginfo[] = { .fieldoffset = offsetof(CPUARMState, cp15.tfsr_el[1]) }, { .name = "TFSR_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 5, .crm = 6, .opc2 = 0, - .access = PL2_RW, .accessfn = access_mte, + .access = PL2_RW, .accessfn = access_tfsr_el2, .fieldoffset = offsetof(CPUARMState, cp15.tfsr_el[2]) }, { .name = "TFSR_EL3", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 6, .crn = 5, .crm = 6, .opc2 = 0, -- 2.34.1
In handle_sys() we don't do the check for whether the register is marked as needing an FPU/SVE/SME access check until after we've handled the special cases covered by ARM_CP_SPECIAL_MASK. This is conceptually the wrong way around, because if for example we happen to implement an FPU-access-checked register as ARM_CP_NOP, we should do the access check first. Move the access checks up so they are with all the other access checks, not sandwiched between the special-case read/write handling and the normal-case read/write handling. This doesn't change behaviour at the moment, because we happen not to define any cpregs with both ARM_CPU_{FPU,SVE,SME} and one of the cases dealt with by ARM_CP_SPECIAL_MASK. Moving this code also means we have the correct place to put the FEAT_NV/FEAT_NV2 access handling, which should come after the access checks and before we try to do any read/write action. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Miguel Luis <miguel.luis@oracle.com> --- target/arm/tcg/translate-a64.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -XXX,XX +XXX,XX @@ static void handle_sys(DisasContext *s, bool isread, gen_a64_update_pc(s, 0); } + if ((ri->type & ARM_CP_FPU) && !fp_access_check_only(s)) { + return; + } else if ((ri->type & ARM_CP_SVE) && !sve_access_check(s)) { + return; + } else if ((ri->type & ARM_CP_SME) && !sme_access_check(s)) { + return; + } + /* Handle special cases first */ switch (ri->type & ARM_CP_SPECIAL_MASK) { case 0: @@ -XXX,XX +XXX,XX @@ static void handle_sys(DisasContext *s, bool isread, default: g_assert_not_reached(); } - if ((ri->type & ARM_CP_FPU) && !fp_access_check_only(s)) { - return; - } else if ((ri->type & ARM_CP_SVE) && !sve_access_check(s)) { - return; - } else if ((ri->type & ARM_CP_SME) && !sme_access_check(s)) { - return; - } if (ri->type & ARM_CP_IO) { /* I/O operations must end the TB here (whether read or write) */ -- 2.34.1
For FEAT_NV, accesses to system registers and instructions from EL1 which would normally UNDEF there but which work in EL2 need to instead be trapped to EL2. Detect this both for "we know this will UNDEF at translate time" and "we found this UNDEFs at runtime", and make the affected registers trap to EL2 instead. The Arm ARM defines the set of registers that should trap in terms of their names; for our implementation this would be both awkward and inefficent as a test, so we instead trap based on the opc1 field of the sysreg. The regularity of the architectural choice of encodings for sysregs means that in practice this captures exactly the correct set of registers. Regardless of how we try to define the registers this trapping applies to, there's going to be a certain possibility of breakage if new architectural features introduce new registers that don't follow the current rules (FEAT_MEC is one example already visible in the released sysreg XML, though not yet in the Arm ARM). This approach seems to me to be straightforward and likely to require a minimum of manual overrides. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Miguel Luis <miguel.luis@oracle.com> --- target/arm/cpregs.h | 34 +++++++++++++++++++++++ target/arm/cpu.h | 1 + target/arm/tcg/translate.h | 2 ++ target/arm/tcg/hflags.c | 1 + target/arm/tcg/translate-a64.c | 49 +++++++++++++++++++++++++++------- 5 files changed, 77 insertions(+), 10 deletions(-) diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -XXX,XX +XXX,XX @@ void define_cortex_a72_a57_a53_cp_reginfo(ARMCPU *cpu); CPAccessResult access_tvm_trvm(CPUARMState *, const ARMCPRegInfo *, bool); +/** + * arm_cpreg_trap_in_nv: Return true if cpreg traps in nested virtualization + * + * Return true if this cpreg is one which should be trapped to EL2 if + * it is executed at EL1 when nested virtualization is enabled via HCR_EL2.NV. + */ +static inline bool arm_cpreg_traps_in_nv(const ARMCPRegInfo *ri) +{ + /* + * The Arm ARM defines the registers to be trapped in terms of + * their names (I_TZTZL). However the underlying principle is "if + * it would UNDEF at EL1 but work at EL2 then it should trap", and + * the way the encoding of sysregs and system instructions is done + * means that the right set of registers is exactly those where + * the opc1 field is 4 or 5. (You can see this also in the assert + * we do that the opc1 field and the permissions mask line up in + * define_one_arm_cp_reg_with_opaque().) + * Checking the opc1 field is easier for us and avoids the problem + * that we do not consistently use the right architectural names + * for all sysregs, since we treat the name field as largely for debug. + * + * However we do this check, it is going to be at least potentially + * fragile to future new sysregs, but this seems the least likely + * to break. + * + * In particular, note that the released sysreg XML defines that + * the FEAT_MEC sysregs and instructions do not follow this FEAT_NV + * trapping rule, so we will need to add an ARM_CP_* flag to indicate + * "register does not trap on NV" to handle those if/when we implement + * FEAT_MEC. + */ + return ri->opc1 == 4 || ri->opc1 == 5; +} + #endif /* TARGET_ARM_CPREGS_H */ diff --git a/target/arm/cpu.h b/target/arm/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -XXX,XX +XXX,XX @@ FIELD(TBFLAG_A64, SME_TRAP_NONSTREAMING, 28, 1) FIELD(TBFLAG_A64, TRAP_ERET, 29, 1) FIELD(TBFLAG_A64, NAA, 30, 1) FIELD(TBFLAG_A64, ATA0, 31, 1) +FIELD(TBFLAG_A64, NV, 32, 1) /* * Helpers for using the above. Note that only the A64 accessors use diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -XXX,XX +XXX,XX @@ typedef struct DisasContext { bool trap_eret; /* True if FEAT_LSE2 SCTLR_ELx.nAA is set */ bool naa; + /* True if FEAT_NV HCR_EL2.NV is enabled */ + bool nv; /* * >= 0, a copy of PSTATE.BTYPE, which will be 0 without v8.5-BTI. * < 0, set by the current instruction. diff --git a/target/arm/tcg/hflags.c b/target/arm/tcg/hflags.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/hflags.c +++ b/target/arm/tcg/hflags.c @@ -XXX,XX +XXX,XX @@ static CPUARMTBFlags rebuild_hflags_a64(CPUARMState *env, int el, int fp_el, */ if (el == 1 && (hcr & HCR_NV)) { DP_TBFLAG_A64(flags, TRAP_ERET, 1); + DP_TBFLAG_A64(flags, NV, 1); } if (cpu_isar_feature(aa64_mte, env_archcpu(env))) { diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -XXX,XX +XXX,XX @@ static void handle_sys(DisasContext *s, bool isread, crn, crm, op0, op1, op2); const ARMCPRegInfo *ri = get_arm_cp_reginfo(s->cp_regs, key); bool need_exit_tb = false; + bool nv_trap_to_el2 = false; + bool skip_fp_access_checks = false; TCGv_ptr tcg_ri = NULL; TCGv_i64 tcg_rt; - uint32_t syndrome; + uint32_t syndrome = syn_aa64_sysregtrap(op0, op1, op2, crn, crm, rt, isread); if (crn == 11 || crn == 15) { /* * Check for TIDCP trap, which must take precedence over * the UNDEF for "no such register" etc. */ - syndrome = syn_aa64_sysregtrap(op0, op1, op2, crn, crm, rt, isread); switch (s->current_el) { case 0: if (dc_isar_feature(aa64_tidcp1, s)) { @@ -XXX,XX +XXX,XX @@ static void handle_sys(DisasContext *s, bool isread, /* Check access permissions */ if (!cp_access_ok(s->current_el, ri, isread)) { - gen_sysreg_undef(s, isread, op0, op1, op2, crn, crm, rt); - return; + /* + * FEAT_NV/NV2 handling does not do the usual FP access checks + * for registers only accessible at EL2 (though it *does* do them + * for registers accessible at EL1). + */ + skip_fp_access_checks = true; + if (s->nv && arm_cpreg_traps_in_nv(ri)) { + /* + * This register / instruction exists and is an EL2 register, so + * we must trap to EL2 if accessed in nested virtualization EL1 + * instead of UNDEFing. We'll do that after the usual access checks. + * (This makes a difference only for a couple of registers like + * VSTTBR_EL2 where the "UNDEF if NonSecure" should take priority + * over the trap-to-EL2. Most trapped-by-FEAT_NV registers have + * an accessfn which does nothing when called from EL1, because + * the trap-to-EL3 controls which would apply to that register + * at EL2 don't take priority over the FEAT_NV trap-to-EL2.) + */ + nv_trap_to_el2 = true; + } else { + gen_sysreg_undef(s, isread, op0, op1, op2, crn, crm, rt); + return; + } } if (ri->accessfn || (ri->fgt && s->fgt_active)) { /* Emit code to perform further access permissions checks at * runtime; this may result in an exception. */ - syndrome = syn_aa64_sysregtrap(op0, op1, op2, crn, crm, rt, isread); gen_a64_update_pc(s, 0); tcg_ri = tcg_temp_new_ptr(); gen_helper_access_check_cp_reg(tcg_ri, tcg_env, @@ -XXX,XX +XXX,XX @@ static void handle_sys(DisasContext *s, bool isread, gen_a64_update_pc(s, 0); } - if ((ri->type & ARM_CP_FPU) && !fp_access_check_only(s)) { - return; - } else if ((ri->type & ARM_CP_SVE) && !sve_access_check(s)) { - return; - } else if ((ri->type & ARM_CP_SME) && !sme_access_check(s)) { + if (!skip_fp_access_checks) { + if ((ri->type & ARM_CP_FPU) && !fp_access_check_only(s)) { + return; + } else if ((ri->type & ARM_CP_SVE) && !sve_access_check(s)) { + return; + } else if ((ri->type & ARM_CP_SME) && !sme_access_check(s)) { + return; + } + } + + if (nv_trap_to_el2) { + gen_exception_insn_el(s, 0, EXCP_UDEF, syndrome, 2); return; } @@ -XXX,XX +XXX,XX @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase, dc->pstate_za = EX_TBFLAG_A64(tb_flags, PSTATE_ZA); dc->sme_trap_nonstreaming = EX_TBFLAG_A64(tb_flags, SME_TRAP_NONSTREAMING); dc->naa = EX_TBFLAG_A64(tb_flags, NAA); + dc->nv = EX_TBFLAG_A64(tb_flags, NV); dc->vec_len = 0; dc->vec_stride = 0; dc->cp_regs = arm_cpu->cp_regs; -- 2.34.1
FEAT_NV requires that when HCR_EL2.NV is set reads of the CurrentEL register from EL1 always report EL2 rather than the real EL. Implement this. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Miguel Luis <miguel.luis@oracle.com> --- target/arm/tcg/translate-a64.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -XXX,XX +XXX,XX @@ static void handle_sys(DisasContext *s, bool isread, } return; case ARM_CP_CURRENTEL: - /* Reads as current EL value from pstate, which is + { + /* + * Reads as current EL value from pstate, which is * guaranteed to be constant by the tb flags. + * For nested virt we should report EL2. */ + int el = s->nv ? 2 : s->current_el; tcg_rt = cpu_reg(s, rt); - tcg_gen_movi_i64(tcg_rt, s->current_el << 2); + tcg_gen_movi_i64(tcg_rt, el << 2); return; + } case ARM_CP_DC_ZVA: /* Writes clear the aligned block of memory which rt points into. */ if (s->mte_active[0]) { -- 2.34.1
FEAT_NV requires that when HCR_EL2.{NV,NV1} == {1,0} and an exception is taken from EL1 to EL1 then the reported EL in SPSR_EL1.M should be EL2, not EL1. Implement this behaviour. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Miguel Luis <miguel.luis@oracle.com> --- target/arm/helper.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ static void arm_cpu_do_interrupt_aarch64(CPUState *cs) old_mode = pstate_read(env); aarch64_save_sp(env, arm_current_el(env)); env->elr_el[new_el] = env->pc; + + if (cur_el == 1 && new_el == 1 && + ((arm_hcr_el2_eff(env) & (HCR_NV | HCR_NV1)) == HCR_NV)) { + /* I_ZJRNN: report EL2 in the SPSR by setting M[3:2] to 0b10 */ + old_mode = deposit32(old_mode, 2, 2, 2); + } } else { old_mode = cpsr_read_for_spsr_elx(env); env->elr_el[new_el] = env->regs[15]; -- 2.34.1
When HCR_EL2.{NV,NV1} is {1,1} we must trap five extra registers to EL2: VBAR_EL1, ELR_EL1, SPSR_EL1, SCXTNUM_EL1 and TFSR_EL1. Implement these traps. This trap does not apply when FEAT_NV2 is implemented and enabled; include the check that HCR_EL2.NV2 is 0 here, to save us having to come back and add it later. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Miguel Luis <miguel.luis@oracle.com> --- target/arm/helper.c | 45 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ static void mdcr_el2_write(CPUARMState *env, const ARMCPRegInfo *ri, } } +static CPAccessResult access_nv1(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + if (arm_current_el(env) == 1) { + uint64_t hcr_nv = arm_hcr_el2_eff(env) & (HCR_NV | HCR_NV1 | HCR_NV2); + + if (hcr_nv == (HCR_NV | HCR_NV1)) { + return CP_ACCESS_TRAP_EL2; + } + } + return CP_ACCESS_OK; +} + #ifdef CONFIG_USER_ONLY /* * `IC IVAU` is handled to improve compatibility with JITs that dual-map their @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo v8_cp_reginfo[] = { { .name = "ELR_EL1", .state = ARM_CP_STATE_AA64, .type = ARM_CP_ALIAS, .opc0 = 3, .opc1 = 0, .crn = 4, .crm = 0, .opc2 = 1, - .access = PL1_RW, + .access = PL1_RW, .accessfn = access_nv1, .fieldoffset = offsetof(CPUARMState, elr_el[1]) }, { .name = "SPSR_EL1", .state = ARM_CP_STATE_AA64, .type = ARM_CP_ALIAS, .opc0 = 3, .opc1 = 0, .crn = 4, .crm = 0, .opc2 = 0, - .access = PL1_RW, + .access = PL1_RW, .accessfn = access_nv1, .fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_SVC]) }, /* * We rely on the access checks not allowing the guest to write to the @@ -XXX,XX +XXX,XX @@ static CPAccessResult access_mte(CPUARMState *env, const ARMCPRegInfo *ri, return CP_ACCESS_OK; } +static CPAccessResult access_tfsr_el1(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + CPAccessResult nv1 = access_nv1(env, ri, isread); + + if (nv1 != CP_ACCESS_OK) { + return nv1; + } + return access_mte(env, ri, isread); +} + static CPAccessResult access_tfsr_el2(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo mte_reginfo[] = { .fieldoffset = offsetof(CPUARMState, cp15.tfsr_el[0]) }, { .name = "TFSR_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 5, .crm = 6, .opc2 = 0, - .access = PL1_RW, .accessfn = access_mte, + .access = PL1_RW, .accessfn = access_tfsr_el1, .fieldoffset = offsetof(CPUARMState, cp15.tfsr_el[1]) }, { .name = "TFSR_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 5, .crm = 6, .opc2 = 0, @@ -XXX,XX +XXX,XX @@ static CPAccessResult access_scxtnum(CPUARMState *env, const ARMCPRegInfo *ri, return CP_ACCESS_OK; } +static CPAccessResult access_scxtnum_el1(CPUARMState *env, + const ARMCPRegInfo *ri, + bool isread) +{ + CPAccessResult nv1 = access_nv1(env, ri, isread); + + if (nv1 != CP_ACCESS_OK) { + return nv1; + } + return access_scxtnum(env, ri, isread); +} + static const ARMCPRegInfo scxtnum_reginfo[] = { { .name = "SCXTNUM_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 13, .crm = 0, .opc2 = 7, @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo scxtnum_reginfo[] = { .fieldoffset = offsetof(CPUARMState, scxtnum_el[0]) }, { .name = "SCXTNUM_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 13, .crm = 0, .opc2 = 7, - .access = PL1_RW, .accessfn = access_scxtnum, + .access = PL1_RW, .accessfn = access_scxtnum_el1, .fgt = FGT_SCXTNUM_EL1, .fieldoffset = offsetof(CPUARMState, scxtnum_el[1]) }, { .name = "SCXTNUM_EL2", .state = ARM_CP_STATE_AA64, @@ -XXX,XX +XXX,XX @@ void register_cp_regs_for_features(ARMCPU *cpu) { .name = "VBAR", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .crn = 12, .crm = 0, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .writefn = vbar_write, + .accessfn = access_nv1, .fgt = FGT_VBAR_EL1, .bank_fieldoffsets = { offsetof(CPUARMState, cp15.vbar_s), offsetof(CPUARMState, cp15.vbar_ns) }, -- 2.34.1
Currently the code in target/arm/helper.c mostly checks the PAN bits in env->pstate or env->uncached_cpsr directly when it wants to know if PAN is enabled, because in most callsites we know whether we are in AArch64 or AArch32. We do have an arm_pan_enabled() function, but we only use it in a few places where the code might run in either an AArch32 or AArch64 context. For FEAT_NV, when HCR_EL2.{NV,NV1} is {1,1} PAN is always disabled even when the PSTATE.PAN bit is set, the "is PAN enabled" test becomes more complicated. Make all places that check for PAN use arm_pan_enabled(), so we have a place to put the FEAT_NV test. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Miguel Luis <miguel.luis@oracle.com> --- target/arm/helper.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ void init_cpreg_list(ARMCPU *cpu) g_list_free(keys); } +static bool arm_pan_enabled(CPUARMState *env) +{ + if (is_a64(env)) { + return env->pstate & PSTATE_PAN; + } else { + return env->uncached_cpsr & CPSR_PAN; + } +} + /* * Some registers are not accessible from AArch32 EL3 if SCR.NS == 0. */ @@ -XXX,XX +XXX,XX @@ static void ats_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) g_assert(ss != ARMSS_Secure); /* ARMv8.4-SecEL2 is 64-bit only */ /* fall through */ case 1: - if (ri->crm == 9 && (env->uncached_cpsr & CPSR_PAN)) { + if (ri->crm == 9 && arm_pan_enabled(env)) { mmu_idx = ARMMMUIdx_Stage1_E1_PAN; } else { mmu_idx = ARMMMUIdx_Stage1_E1; @@ -XXX,XX +XXX,XX @@ static void ats_write64(CPUARMState *env, const ARMCPRegInfo *ri, case 0: switch (ri->opc1) { case 0: /* AT S1E1R, AT S1E1W, AT S1E1RP, AT S1E1WP */ - if (ri->crm == 9 && (env->pstate & PSTATE_PAN)) { + if (ri->crm == 9 && arm_pan_enabled(env)) { mmu_idx = regime_e20 ? ARMMMUIdx_E20_2_PAN : ARMMMUIdx_Stage1_E1_PAN; } else { @@ -XXX,XX +XXX,XX @@ ARMMMUIdx arm_v7m_mmu_idx_for_secstate(CPUARMState *env, bool secstate) } #endif -static bool arm_pan_enabled(CPUARMState *env) -{ - if (is_a64(env)) { - return env->pstate & PSTATE_PAN; - } else { - return env->uncached_cpsr & CPSR_PAN; - } -} - ARMMMUIdx arm_mmu_idx_el(CPUARMState *env, int el) { ARMMMUIdx idx; -- 2.34.1
For FEAT_NV, when HCR_EL2.{NV,NV1} is {1,1} PAN is always disabled even when the PSTATE.PAN bit is set. Implement this by having arm_pan_enabled() return false in this situation. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Miguel Luis <miguel.luis@oracle.com> --- target/arm/helper.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ void init_cpreg_list(ARMCPU *cpu) static bool arm_pan_enabled(CPUARMState *env) { if (is_a64(env)) { + if ((arm_hcr_el2_eff(env) & (HCR_NV | HCR_NV1)) == (HCR_NV | HCR_NV1)) { + return false; + } return env->pstate & PSTATE_PAN; } else { return env->uncached_cpsr & CPSR_PAN; -- 2.34.1
FEAT_NV requires (per I_JKLJK) that when HCR_EL2.{NV,NV1} is {1,1} the unprivileged-access instructions LDTR, STTR etc behave as normal loads and stores. Implement the check that handles this. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Miguel Luis <miguel.luis@oracle.com> --- target/arm/tcg/hflags.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/target/arm/tcg/hflags.c b/target/arm/tcg/hflags.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/hflags.c +++ b/target/arm/tcg/hflags.c @@ -XXX,XX +XXX,XX @@ static CPUARMTBFlags rebuild_hflags_a64(CPUARMState *env, int el, int fp_el, switch (mmu_idx) { case ARMMMUIdx_E10_1: case ARMMMUIdx_E10_1_PAN: - /* TODO: ARMv8.3-NV */ - DP_TBFLAG_A64(flags, UNPRIV, 1); + /* FEAT_NV: NV,NV1 == 1,1 means we don't do UNPRIV accesses */ + if ((hcr & (HCR_NV | HCR_NV1)) != (HCR_NV | HCR_NV1)) { + DP_TBFLAG_A64(flags, UNPRIV, 1); + } break; case ARMMMUIdx_E20_2: case ARMMMUIdx_E20_2_PAN: -- 2.34.1
FEAT_NV requires that when HCR_EL2.{NV,NV1} == {1,1} the handling of some of the page table attribute bits changes for the EL1&0 translation regime: * for block and page descriptors: - bit [54] holds PXN, not UXN - bit [53] is RES0, and the effective value of UXN is 0 - bit [6], AP[1], is treated as 0 * for table descriptors, when hierarchical permissions are enabled: - bit [60] holds PXNTable, not UXNTable - bit [59] is RES0 - bit [61], APTable[0] is treated as 0 Implement these changes to the page table attribute handling. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Miguel Luis <miguel.luis@oracle.com> --- target/arm/ptw.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/target/arm/ptw.c b/target/arm/ptw.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -XXX,XX +XXX,XX @@ static bool lpae_block_desc_valid(ARMCPU *cpu, bool ds, } } +static bool nv_nv1_enabled(CPUARMState *env, S1Translate *ptw) +{ + uint64_t hcr = arm_hcr_el2_eff_secstate(env, ptw->in_space); + return (hcr & (HCR_NV | HCR_NV1)) == (HCR_NV | HCR_NV1); +} + /** * get_phys_addr_lpae: perform one stage of page table walk, LPAE format * @@ -XXX,XX +XXX,XX @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, xn = extract64(attrs, 54, 1); pxn = extract64(attrs, 53, 1); + if (el == 1 && nv_nv1_enabled(env, ptw)) { + /* + * With FEAT_NV, when HCR_EL2.{NV,NV1} == {1,1}, the block/page + * descriptor bit 54 holds PXN, 53 is RES0, and the effective value + * of UXN is 0. Similarly for bits 59 and 60 in table descriptors + * (which we have already folded into bits 53 and 54 of attrs). + * AP[1] (descriptor bit 6, our ap bit 0) is treated as 0. + * Similarly, APTable[0] from the table descriptor is treated as 0; + * we already folded this into AP[1] and squashing that to 0 does + * the right thing. + */ + pxn = xn; + xn = 0; + ap &= ~1; + } /* * Note that we modified ptw->in_space earlier for NSTable, but * result->f.attrs retains a copy of the original security space. -- 2.34.1
Enable FEAT_NV on the 'max' CPU, and stop filtering it out for the Neoverse N2 and Neoverse V1 CPUs. We continue to downgrade FEAT_NV2 support to FEAT_NV for the latter two CPU types. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Miguel Luis <miguel.luis@oracle.com> --- docs/system/arm/emulation.rst | 1 + target/arm/cpu.c | 8 +++++--- target/arm/tcg/cpu64.c | 1 + 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index XXXXXXX..XXXXXXX 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -XXX,XX +XXX,XX @@ the following architecture extensions: - FEAT_MTE (Memory Tagging Extension) - FEAT_MTE2 (Memory Tagging Extension) - FEAT_MTE3 (MTE Asymmetric Fault Handling) +- FEAT_NV (Nested Virtualization) - FEAT_PACIMP (Pointer authentication - IMPLEMENTATION DEFINED algorithm) - FEAT_PACQARMA3 (Pointer authentication - QARMA3 algorithm) - FEAT_PACQARMA5 (Pointer authentication - QARMA5 algorithm) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -XXX,XX +XXX,XX @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) /* FEAT_MPAM (Memory Partitioning and Monitoring Extension) */ cpu->isar.id_aa64pfr0 = FIELD_DP64(cpu->isar.id_aa64pfr0, ID_AA64PFR0, MPAM, 0); - /* FEAT_NV (Nested Virtualization) */ - cpu->isar.id_aa64mmfr2 = - FIELD_DP64(cpu->isar.id_aa64mmfr2, ID_AA64MMFR2, NV, 0); + /* FEAT_NV2 (Enhanced Nested Virtualization support) */ + if (FIELD_EX64(cpu->isar.id_aa64mmfr2, ID_AA64MMFR2, NV) > 1) { + cpu->isar.id_aa64mmfr2 = + FIELD_DP64(cpu->isar.id_aa64mmfr2, ID_AA64MMFR2, NV, 1); + } } /* MPU can be configured out of a PMSA CPU either by setting has-mpu diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -XXX,XX +XXX,XX @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64MMFR2, UAO, 1); /* FEAT_UAO */ t = FIELD_DP64(t, ID_AA64MMFR2, IESB, 1); /* FEAT_IESB */ t = FIELD_DP64(t, ID_AA64MMFR2, VARANGE, 1); /* FEAT_LVA */ + t = FIELD_DP64(t, ID_AA64MMFR2, NV, 1); /* FEAT_NV */ t = FIELD_DP64(t, ID_AA64MMFR2, ST, 1); /* FEAT_TTST */ t = FIELD_DP64(t, ID_AA64MMFR2, AT, 1); /* FEAT_LSE2 */ t = FIELD_DP64(t, ID_AA64MMFR2, IDS, 1); /* FEAT_IDST */ -- 2.34.1
FEAT_NV2 defines another new bit in HCR_EL2: NV2. When the feature is enabled, allow this bit to be written in HCR_EL2. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Miguel Luis <miguel.luis@oracle.com> --- target/arm/cpu-features.h | 5 +++++ target/arm/helper.c | 3 +++ 2 files changed, 8 insertions(+) diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -XXX,XX +XXX,XX @@ static inline bool isar_feature_aa64_nv(const ARMISARegisters *id) return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, NV) != 0; } +static inline bool isar_feature_aa64_nv2(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, NV) >= 2; +} + static inline bool isar_feature_aa64_pmuv3p1(const ARMISARegisters *id) { return FIELD_EX64(id->id_aa64dfr0, ID_AA64DFR0, PMUVER) >= 4 && diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ static void do_hcr_write(CPUARMState *env, uint64_t value, uint64_t valid_mask) if (cpu_isar_feature(aa64_nv, cpu)) { valid_mask |= HCR_NV | HCR_NV1 | HCR_AT; } + if (cpu_isar_feature(aa64_nv2, cpu)) { + valid_mask |= HCR_NV2; + } } if (cpu_isar_feature(any_evt, cpu)) { -- 2.34.1
For FEAT_NV2, a new system register VNCR_EL2 holds the base address of the memory which nested-guest system register accesses are redirected to. Implement this register. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Miguel Luis <miguel.luis@oracle.com> --- target/arm/cpu.h | 3 +++ target/arm/helper.c | 26 ++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -XXX,XX +XXX,XX @@ typedef struct CPUArchState { uint64_t gpccr_el3; uint64_t gptbr_el3; uint64_t mfar_el3; + + /* NV2 register */ + uint64_t vncr_el2; } cp15; struct { diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo fgt_reginfo[] = { .access = PL2_RW, .accessfn = access_fgt, .fieldoffset = offsetof(CPUARMState, cp15.fgt_exec[FGTREG_HFGITR]) }, }; + +static void vncr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* + * Clear the RES0 bottom 12 bits; this means at runtime we can guarantee + * that VNCR_EL2 + offset is 64-bit aligned. We don't need to do anything + * about the RESS bits at the top -- we choose the "generate an EL2 + * translation abort on use" CONSTRAINED UNPREDICTABLE option (i.e. let + * the ptw.c code detect the resulting invalid address). + */ + env->cp15.vncr_el2 = value & ~0xfffULL; +} + +static const ARMCPRegInfo nv2_reginfo[] = { + { .name = "VNCR_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 2, .opc2 = 0, + .access = PL2_RW, + .writefn = vncr_write, + .fieldoffset = offsetof(CPUARMState, cp15.vncr_el2) }, +}; + #endif /* TARGET_AARCH64 */ static CPAccessResult access_predinv(CPUARMState *env, const ARMCPRegInfo *ri, @@ -XXX,XX +XXX,XX @@ void register_cp_regs_for_features(ARMCPU *cpu) define_arm_cp_regs(cpu, rme_mte_reginfo); } } + + if (cpu_isar_feature(aa64_nv2, cpu)) { + define_arm_cp_regs(cpu, nv2_reginfo); + } #endif if (cpu_isar_feature(any_predinv, cpu)) { -- 2.34.1
With FEAT_NV2, the condition for when SPSR_EL1.M should report that an exception was taken from EL2 changes. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Miguel Luis <miguel.luis@oracle.com> --- target/arm/helper.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ static void arm_cpu_do_interrupt_aarch64(CPUState *cs) aarch64_save_sp(env, arm_current_el(env)); env->elr_el[new_el] = env->pc; - if (cur_el == 1 && new_el == 1 && - ((arm_hcr_el2_eff(env) & (HCR_NV | HCR_NV1)) == HCR_NV)) { - /* I_ZJRNN: report EL2 in the SPSR by setting M[3:2] to 0b10 */ - old_mode = deposit32(old_mode, 2, 2, 2); + if (cur_el == 1 && new_el == 1) { + uint64_t hcr = arm_hcr_el2_eff(env); + if ((hcr & (HCR_NV | HCR_NV1 | HCR_NV2)) == HCR_NV || + (hcr & (HCR_NV | HCR_NV2)) == (HCR_NV | HCR_NV2)) { + /* + * FEAT_NV, FEAT_NV2 may need to report EL2 in the SPSR + * by setting M[3:2] to 0b10. + * If NV2 is disabled, change SPSR when NV,NV1 == 1,0 (I_ZJRNN) + * If NV2 is enabled, change SPSR when NV is 1 (I_DBTLM) + */ + old_mode = deposit32(old_mode, 2, 2, 2); + } } } else { old_mode = cpsr_read_for_spsr_elx(env); -- 2.34.1
Under FEAT_NV2, when HCR_EL2.{NV,NV2} == 0b11 at EL1, accesses to the registers SPSR_EL2, ELR_EL2, ESR_EL2, FAR_EL2 and TFSR_EL2 (which would UNDEF without FEAT_NV or FEAT_NV2) should instead access the equivalent EL1 registers SPSR_EL1, ELR_EL1, ESR_EL1, FAR_EL1 and TFSR_EL1. Because there are only five registers involved and the encoding for the EL1 register is identical to that of the EL2 register except that opc1 is 0, we handle this by finding the EL1 register in the hash table and using it instead. Note that traps that apply to direct accesses to the EL1 register, such as active fine-grained traps or other trap bits, do not trigger when it is accessed via the EL2 encoding in this way. However, some traps that are defined by the EL2 register may apply. We therefore call the EL2 register's accessfn first. The only one of the five which has such traps is TFSR_EL2: make sure its accessfn correctly handles both FEAT_NV (where we trap to EL2 without checking ATA bits) and FEAT_NV2 (where we check ATA bits and then redirect to TFSR_EL1). (We don't need the NV1 tbflag bit until the next patch, but we introduce it here to avoid putting the NV, NV1, NV2 bits in an odd order.) Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Miguel Luis <miguel.luis@oracle.com> --- target/arm/cpregs.h | 5 +++++ target/arm/cpu.h | 2 ++ target/arm/tcg/translate.h | 4 ++++ target/arm/helper.c | 13 +++++++++---- target/arm/tcg/hflags.c | 6 ++++++ target/arm/tcg/translate-a64.c | 33 ++++++++++++++++++++++++++++++++- 6 files changed, 58 insertions(+), 5 deletions(-) diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -XXX,XX +XXX,XX @@ enum { * ARM pseudocode function CheckSMEAccess(). */ ARM_CP_SME = 1 << 19, + /* + * Flag: one of the four EL2 registers which redirect to the + * equivalent EL1 register when FEAT_NV2 is enabled. + */ + ARM_CP_NV2_REDIRECT = 1 << 20, }; /* diff --git a/target/arm/cpu.h b/target/arm/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -XXX,XX +XXX,XX @@ FIELD(TBFLAG_A64, TRAP_ERET, 29, 1) FIELD(TBFLAG_A64, NAA, 30, 1) FIELD(TBFLAG_A64, ATA0, 31, 1) FIELD(TBFLAG_A64, NV, 32, 1) +FIELD(TBFLAG_A64, NV1, 33, 1) +FIELD(TBFLAG_A64, NV2, 34, 1) /* * Helpers for using the above. Note that only the A64 accessors use diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -XXX,XX +XXX,XX @@ typedef struct DisasContext { bool naa; /* True if FEAT_NV HCR_EL2.NV is enabled */ bool nv; + /* True if NV enabled and HCR_EL2.NV1 is set */ + bool nv1; + /* True if NV enabled and HCR_EL2.NV2 is set */ + bool nv2; /* * >= 0, a copy of PSTATE.BTYPE, which will be 0 without v8.5-BTI. * < 0, set by the current instruction. diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo el2_cp_reginfo[] = { .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 7, .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "ELR_EL2", .state = ARM_CP_STATE_AA64, - .type = ARM_CP_ALIAS, + .type = ARM_CP_ALIAS | ARM_CP_NV2_REDIRECT, .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 0, .opc2 = 1, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, elr_el[2]) }, { .name = "ESR_EL2", .state = ARM_CP_STATE_BOTH, + .type = ARM_CP_NV2_REDIRECT, .opc0 = 3, .opc1 = 4, .crn = 5, .crm = 2, .opc2 = 0, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.esr_el[2]) }, { .name = "FAR_EL2", .state = ARM_CP_STATE_BOTH, + .type = ARM_CP_NV2_REDIRECT, .opc0 = 3, .opc1 = 4, .crn = 6, .crm = 0, .opc2 = 0, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.far_el[2]) }, { .name = "HIFAR", .state = ARM_CP_STATE_AA32, @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo el2_cp_reginfo[] = { .access = PL2_RW, .fieldoffset = offsetofhigh32(CPUARMState, cp15.far_el[2]) }, { .name = "SPSR_EL2", .state = ARM_CP_STATE_AA64, - .type = ARM_CP_ALIAS, + .type = ARM_CP_ALIAS | ARM_CP_NV2_REDIRECT, .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 0, .opc2 = 0, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_HYP]) }, @@ -XXX,XX +XXX,XX @@ static CPAccessResult access_tfsr_el2(CPUARMState *env, const ARMCPRegInfo *ri, /* * TFSR_EL2: similar to generic access_mte(), but we need to * account for FEAT_NV. At EL1 this must be a FEAT_NV access; - * we will trap to EL2 and the HCR/SCR traps do not apply. + * if NV2 is enabled then we will redirect this to TFSR_EL1 + * after doing the HCR and SCR ATA traps; otherwise this will + * be a trap to EL2 and the HCR/SCR traps do not apply. */ int el = arm_current_el(env); - if (el == 1) { + if (el == 1 && (arm_hcr_el2_eff(env) & HCR_NV2)) { return CP_ACCESS_OK; } if (el < 2 && arm_is_el2_enabled(env)) { @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo mte_reginfo[] = { .access = PL1_RW, .accessfn = access_tfsr_el1, .fieldoffset = offsetof(CPUARMState, cp15.tfsr_el[1]) }, { .name = "TFSR_EL2", .state = ARM_CP_STATE_AA64, + .type = ARM_CP_NV2_REDIRECT, .opc0 = 3, .opc1 = 4, .crn = 5, .crm = 6, .opc2 = 0, .access = PL2_RW, .accessfn = access_tfsr_el2, .fieldoffset = offsetof(CPUARMState, cp15.tfsr_el[2]) }, diff --git a/target/arm/tcg/hflags.c b/target/arm/tcg/hflags.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/hflags.c +++ b/target/arm/tcg/hflags.c @@ -XXX,XX +XXX,XX @@ static CPUARMTBFlags rebuild_hflags_a64(CPUARMState *env, int el, int fp_el, if (el == 1 && (hcr & HCR_NV)) { DP_TBFLAG_A64(flags, TRAP_ERET, 1); DP_TBFLAG_A64(flags, NV, 1); + if (hcr & HCR_NV1) { + DP_TBFLAG_A64(flags, NV1, 1); + } + if (hcr & HCR_NV2) { + DP_TBFLAG_A64(flags, NV2, 1); + } } if (cpu_isar_feature(aa64_mte, env_archcpu(env))) { diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -XXX,XX +XXX,XX @@ static void handle_sys(DisasContext *s, bool isread, const ARMCPRegInfo *ri = get_arm_cp_reginfo(s->cp_regs, key); bool need_exit_tb = false; bool nv_trap_to_el2 = false; + bool nv_redirect_reg = false; bool skip_fp_access_checks = false; TCGv_ptr tcg_ri = NULL; TCGv_i64 tcg_rt; @@ -XXX,XX +XXX,XX @@ static void handle_sys(DisasContext *s, bool isread, * for registers accessible at EL1). */ skip_fp_access_checks = true; - if (s->nv && arm_cpreg_traps_in_nv(ri)) { + if (s->nv2 && (ri->type & ARM_CP_NV2_REDIRECT)) { + /* + * This is one of the few EL2 registers which should redirect + * to the equivalent EL1 register. We do that after running + * the EL2 register's accessfn. + */ + nv_redirect_reg = true; + } else if (s->nv && arm_cpreg_traps_in_nv(ri)) { /* * This register / instruction exists and is an EL2 register, so * we must trap to EL2 if accessed in nested virtualization EL1 @@ -XXX,XX +XXX,XX @@ static void handle_sys(DisasContext *s, bool isread, return; } + if (nv_redirect_reg) { + /* + * FEAT_NV2 redirection of an EL2 register to an EL1 register. + * Conveniently in all cases the encoding of the EL1 register is + * identical to the EL2 register except that opc1 is 0. + * Get the reginfo for the EL1 register to use for the actual access. + * We don't use the EL1 register's access function, and + * fine-grained-traps on EL1 also do not apply here. + */ + key = ENCODE_AA64_CP_REG(CP_REG_ARM64_SYSREG_CP, + crn, crm, op0, 0, op2); + ri = get_arm_cp_reginfo(s->cp_regs, key); + assert(ri); + assert(cp_access_ok(s->current_el, ri, isread)); + /* + * We might not have done an update_pc earlier, so check we don't + * need it. We could support this in future if necessary. + */ + assert(!(ri->type & ARM_CP_RAISES_EXC)); + } + /* Handle special cases first */ switch (ri->type & ARM_CP_SPECIAL_MASK) { case 0: @@ -XXX,XX +XXX,XX @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase, dc->sme_trap_nonstreaming = EX_TBFLAG_A64(tb_flags, SME_TRAP_NONSTREAMING); dc->naa = EX_TBFLAG_A64(tb_flags, NAA); dc->nv = EX_TBFLAG_A64(tb_flags, NV); + dc->nv1 = EX_TBFLAG_A64(tb_flags, NV1); + dc->nv2 = EX_TBFLAG_A64(tb_flags, NV2); dc->vec_len = 0; dc->vec_stride = 0; dc->cp_regs = arm_cpu->cp_regs; -- 2.34.1
FEAT_NV2 requires that when HCR_EL2.{NV,NV2} == 0b11 then accesses by EL1 to certain system registers are redirected to RAM. The full list of affected registers is in the table in rule R_CSRPQ in the Arm ARM. The registers may be normally accessible at EL1 (like ACTLR_EL1), or normally UNDEF at EL1 (like HCR_EL2). Some registers redirect to RAM only when HCR_EL2.NV1 is 0, and some only when HCR_EL2.NV1 is 1; others trap in both cases. Add the infrastructure for identifying which registers should be redirected and turning them into memory accesses. This code does not set the correct syndrome or arrange for the exception to be taken to the correct target EL if the access via VNCR_EL2 faults; we will do that in the next commit. Subsequent commits will mark up the relevant regdefs to set their nv2_redirect_offset, and if relevant one of the two flags which indicates that the redirect happens only for a particular value of HCR_EL2.NV1. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Tested-by: Miguel Luis <miguel.luis@oracle.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> --- target/arm/cpregs.h | 12 ++++++++ target/arm/cpu.h | 4 +++ target/arm/tcg/translate.h | 6 ++++ target/arm/tcg/hflags.c | 6 ++++ target/arm/tcg/translate-a64.c | 56 ++++++++++++++++++++++++++++++++++ 5 files changed, 84 insertions(+) diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -XXX,XX +XXX,XX @@ typedef void CPResetFn(CPUARMState *env, const ARMCPRegInfo *opaque); #define CP_ANY 0xff +/* Flags in the high bits of nv2_redirect_offset */ +#define NV2_REDIR_NV1 0x4000 /* Only redirect when HCR_EL2.NV1 == 1 */ +#define NV2_REDIR_NO_NV1 0x8000 /* Only redirect when HCR_EL2.NV1 == 0 */ +#define NV2_REDIR_FLAG_MASK 0xc000 + /* Definition of an ARM coprocessor register */ struct ARMCPRegInfo { /* Name of register (useful mainly for debugging, need not be unique) */ @@ -XXX,XX +XXX,XX @@ struct ARMCPRegInfo { * value encodes both the trap register and bit within it. */ FGTBit fgt; + + /* + * Offset from VNCR_EL2 when FEAT_NV2 redirects access to memory; + * may include an NV2_REDIR_* flag. + */ + uint32_t nv2_redirect_offset; + /* * The opaque pointer passed to define_arm_cp_regs_with_opaque() when * this register was defined: can be used to hand data through to the diff --git a/target/arm/cpu.h b/target/arm/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -XXX,XX +XXX,XX @@ FIELD(TBFLAG_A64, ATA0, 31, 1) FIELD(TBFLAG_A64, NV, 32, 1) FIELD(TBFLAG_A64, NV1, 33, 1) FIELD(TBFLAG_A64, NV2, 34, 1) +/* Set if FEAT_NV2 RAM accesses use the EL2&0 translation regime */ +FIELD(TBFLAG_A64, NV2_MEM_E20, 35, 1) +/* Set if FEAT_NV2 RAM accesses are big-endian */ +FIELD(TBFLAG_A64, NV2_MEM_BE, 36, 1) /* * Helpers for using the above. Note that only the A64 accessors use diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -XXX,XX +XXX,XX @@ typedef struct DisasContext { bool nv1; /* True if NV enabled and HCR_EL2.NV2 is set */ bool nv2; + /* True if NV2 enabled and NV2 RAM accesses use EL2&0 translation regime */ + bool nv2_mem_e20; + /* True if NV2 enabled and NV2 RAM accesses are big-endian */ + bool nv2_mem_be; /* * >= 0, a copy of PSTATE.BTYPE, which will be 0 without v8.5-BTI. * < 0, set by the current instruction. @@ -XXX,XX +XXX,XX @@ typedef struct DisasContext { int c15_cpar; /* TCG op of the current insn_start. */ TCGOp *insn_start; + /* Offset from VNCR_EL2 when FEAT_NV2 redirects this reg to memory */ + uint32_t nv2_redirect_offset; } DisasContext; typedef struct DisasCompare { diff --git a/target/arm/tcg/hflags.c b/target/arm/tcg/hflags.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/hflags.c +++ b/target/arm/tcg/hflags.c @@ -XXX,XX +XXX,XX @@ static CPUARMTBFlags rebuild_hflags_a64(CPUARMState *env, int el, int fp_el, } if (hcr & HCR_NV2) { DP_TBFLAG_A64(flags, NV2, 1); + if (hcr & HCR_E2H) { + DP_TBFLAG_A64(flags, NV2_MEM_E20, 1); + } + if (env->cp15.sctlr_el[2] & SCTLR_EE) { + DP_TBFLAG_A64(flags, NV2_MEM_BE, 1); + } } } diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -XXX,XX +XXX,XX @@ static void handle_sys(DisasContext *s, bool isread, bool nv_trap_to_el2 = false; bool nv_redirect_reg = false; bool skip_fp_access_checks = false; + bool nv2_mem_redirect = false; TCGv_ptr tcg_ri = NULL; TCGv_i64 tcg_rt; uint32_t syndrome = syn_aa64_sysregtrap(op0, op1, op2, crn, crm, rt, isread); @@ -XXX,XX +XXX,XX @@ static void handle_sys(DisasContext *s, bool isread, return; } + if (s->nv2 && ri->nv2_redirect_offset) { + /* + * Some registers always redirect to memory; some only do so if + * HCR_EL2.NV1 is 0, and some only if NV1 is 1 (these come in + * pairs which share an offset; see the table in R_CSRPQ). + */ + if (ri->nv2_redirect_offset & NV2_REDIR_NV1) { + nv2_mem_redirect = s->nv1; + } else if (ri->nv2_redirect_offset & NV2_REDIR_NO_NV1) { + nv2_mem_redirect = !s->nv1; + } else { + nv2_mem_redirect = true; + } + } + /* Check access permissions */ if (!cp_access_ok(s->current_el, ri, isread)) { /* @@ -XXX,XX +XXX,XX @@ static void handle_sys(DisasContext *s, bool isread, * the EL2 register's accessfn. */ nv_redirect_reg = true; + assert(!nv2_mem_redirect); + } else if (nv2_mem_redirect) { + /* + * NV2 redirect-to-memory takes precedence over trap to EL2 or + * UNDEF to EL1. + */ } else if (s->nv && arm_cpreg_traps_in_nv(ri)) { /* * This register / instruction exists and is an EL2 register, so @@ -XXX,XX +XXX,XX @@ static void handle_sys(DisasContext *s, bool isread, assert(!(ri->type & ARM_CP_RAISES_EXC)); } + if (nv2_mem_redirect) { + /* + * This system register is being redirected into an EL2 memory access. + * This means it is not an IO operation, doesn't change hflags, + * and need not end the TB, because it has no side effects. + * + * The access is 64-bit single copy atomic, guaranteed aligned because + * of the definition of VCNR_EL2. Its endianness depends on + * SCTLR_EL2.EE, not on the data endianness of EL1. + * It is done under either the EL2 translation regime or the EL2&0 + * translation regime, depending on HCR_EL2.E2H. It behaves as if + * PSTATE.PAN is 0. + */ + TCGv_i64 ptr = tcg_temp_new_i64(); + MemOp mop = MO_64 | MO_ALIGN | MO_ATOM_IFALIGN; + ARMMMUIdx armmemidx = s->nv2_mem_e20 ? ARMMMUIdx_E20_2 : ARMMMUIdx_E2; + int memidx = arm_to_core_mmu_idx(armmemidx); + + mop |= (s->nv2_mem_be ? MO_BE : MO_LE); + + tcg_gen_ld_i64(ptr, tcg_env, offsetof(CPUARMState, cp15.vncr_el2)); + tcg_gen_addi_i64(ptr, ptr, + (ri->nv2_redirect_offset & ~NV2_REDIR_FLAG_MASK)); + tcg_rt = cpu_reg(s, rt); + if (isread) { + tcg_gen_qemu_ld_i64(tcg_rt, ptr, memidx, mop); + } else { + tcg_gen_qemu_st_i64(tcg_rt, ptr, memidx, mop); + } + return; + } + /* Handle special cases first */ switch (ri->type & ARM_CP_SPECIAL_MASK) { case 0: @@ -XXX,XX +XXX,XX @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase, dc->nv = EX_TBFLAG_A64(tb_flags, NV); dc->nv1 = EX_TBFLAG_A64(tb_flags, NV1); dc->nv2 = EX_TBFLAG_A64(tb_flags, NV2); + dc->nv2_mem_e20 = EX_TBFLAG_A64(tb_flags, NV2_MEM_E20); + dc->nv2_mem_be = EX_TBFLAG_A64(tb_flags, NV2_MEM_BE); dc->vec_len = 0; dc->vec_stride = 0; dc->cp_regs = arm_cpu->cp_regs; -- 2.34.1
If FEAT_NV2 redirects a system register access to a memory offset from VNCR_EL2, that access might fault. In this case we need to report the correct syndrome information: * Data Abort, from same-EL * no ISS information * the VNCR bit (bit 13) is set and the exception must be taken to EL2. Save an appropriate syndrome template when generating code; we can then use that to: * select the right target EL * reconstitute a correct final syndrome for the data abort * report the right syndrome if we take a FEAT_RME granule protection fault on the VNCR-based write Note that because VNCR is bit 13, we must start keeping bit 13 in template syndromes, by adjusting ARM_INSN_START_WORD2_SHIFT. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Miguel Luis <miguel.luis@oracle.com> --- target/arm/cpu.h | 4 ++-- target/arm/syndrome.h | 20 ++++++++++++++++---- target/arm/tcg/tlb_helper.c | 27 +++++++++++++++++++++++++-- target/arm/tcg/translate-a64.c | 4 ++++ 4 files changed, 47 insertions(+), 8 deletions(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -XXX,XX +XXX,XX @@ enum { #define TARGET_INSN_START_EXTRA_WORDS 2 /* The 2nd extra word holding syndrome info for data aborts does not use - * the upper 6 bits nor the lower 14 bits. We mask and shift it down to + * the upper 6 bits nor the lower 13 bits. We mask and shift it down to * help the sleb128 encoder do a better job. * When restoring the CPU state, we shift it back up. */ #define ARM_INSN_START_WORD2_MASK ((1 << 26) - 1) -#define ARM_INSN_START_WORD2_SHIFT 14 +#define ARM_INSN_START_WORD2_SHIFT 13 /* We currently assume float and double are IEEE single and double precision respectively. diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -XXX,XX +XXX,XX @@ typedef enum { #define ARM_EL_IL (1 << ARM_EL_IL_SHIFT) #define ARM_EL_ISV (1 << ARM_EL_ISV_SHIFT) +/* In the Data Abort syndrome */ +#define ARM_EL_VNCR (1 << 13) + static inline uint32_t syn_get_ec(uint32_t syn) { return syn >> ARM_EL_EC_SHIFT; @@ -XXX,XX +XXX,XX @@ static inline uint32_t syn_bxjtrap(int cv, int cond, int rm) (cv << 24) | (cond << 20) | rm; } -static inline uint32_t syn_gpc(int s2ptw, int ind, int gpcsc, +static inline uint32_t syn_gpc(int s2ptw, int ind, int gpcsc, int vncr, int cm, int s1ptw, int wnr, int fsc) { - /* TODO: FEAT_NV2 adds VNCR */ return (EC_GPC << ARM_EL_EC_SHIFT) | ARM_EL_IL | (s2ptw << 21) - | (ind << 20) | (gpcsc << 14) | (cm << 8) | (s1ptw << 7) - | (wnr << 6) | fsc; + | (ind << 20) | (gpcsc << 14) | (vncr << 13) | (cm << 8) + | (s1ptw << 7) | (wnr << 6) | fsc; } static inline uint32_t syn_insn_abort(int same_el, int ea, int s1ptw, int fsc) @@ -XXX,XX +XXX,XX @@ static inline uint32_t syn_data_abort_with_iss(int same_el, | (ea << 9) | (cm << 8) | (s1ptw << 7) | (wnr << 6) | fsc; } +/* + * Faults due to FEAT_NV2 VNCR_EL2-based accesses report as same-EL + * Data Aborts with the VNCR bit set. + */ +static inline uint32_t syn_data_abort_vncr(int ea, int wnr, int fsc) +{ + return (EC_DATAABORT << ARM_EL_EC_SHIFT) | (1 << ARM_EL_EC_SHIFT) + | ARM_EL_IL | ARM_EL_VNCR | (wnr << 6) | fsc; +} + static inline uint32_t syn_swstep(int same_el, int isv, int ex) { return (EC_SOFTWARESTEP << ARM_EL_EC_SHIFT) | (same_el << ARM_EL_EC_SHIFT) diff --git a/target/arm/tcg/tlb_helper.c b/target/arm/tcg/tlb_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/tlb_helper.c +++ b/target/arm/tcg/tlb_helper.c @@ -XXX,XX +XXX,XX @@ static inline uint32_t merge_syn_data_abort(uint32_t template_syn, * ST64BV, or ST64BV0 insns report syndrome info even for stage-1 * faults and regardless of the target EL. */ - if (!(template_syn & ARM_EL_ISV) || target_el != 2 + if (template_syn & ARM_EL_VNCR) { + /* + * FEAT_NV2 faults on accesses via VNCR_EL2 are a special case: + * they are always reported as "same EL", even though we are going + * from EL1 to EL2. + */ + assert(!fi->stage2); + syn = syn_data_abort_vncr(fi->ea, is_write, fsc); + } else if (!(template_syn & ARM_EL_ISV) || target_el != 2 || fi->s1ptw || !fi->stage2) { syn = syn_data_abort_no_iss(same_el, 0, fi->ea, 0, fi->s1ptw, is_write, fsc); @@ -XXX,XX +XXX,XX @@ void arm_deliver_fault(ARMCPU *cpu, vaddr addr, int current_el = arm_current_el(env); bool same_el; uint32_t syn, exc, fsr, fsc; + /* + * We know this must be a data or insn abort, and that + * env->exception.syndrome contains the template syndrome set + * up at translate time. So we can check only the VNCR bit + * (and indeed syndrome does not have the EC field in it, + * because we masked that out in disas_set_insn_syndrome()) + */ + bool is_vncr = (mmu_idx != MMU_INST_FETCH) && + (env->exception.syndrome & ARM_EL_VNCR); + + if (is_vncr) { + /* FEAT_NV2 faults on accesses via VNCR_EL2 go to EL2 */ + target_el = 2; + } if (report_as_gpc_exception(cpu, current_el, fi)) { target_el = 3; @@ -XXX,XX +XXX,XX @@ void arm_deliver_fault(ARMCPU *cpu, vaddr addr, syn = syn_gpc(fi->stage2 && fi->type == ARMFault_GPCFOnWalk, access_type == MMU_INST_FETCH, - encode_gpcsc(fi), 0, fi->s1ptw, + encode_gpcsc(fi), is_vncr, + 0, fi->s1ptw, access_type == MMU_DATA_STORE, fsc); env->cp15.mfar_el3 = fi->paddr; diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -XXX,XX +XXX,XX @@ static void handle_sys(DisasContext *s, bool isread, MemOp mop = MO_64 | MO_ALIGN | MO_ATOM_IFALIGN; ARMMMUIdx armmemidx = s->nv2_mem_e20 ? ARMMMUIdx_E20_2 : ARMMMUIdx_E2; int memidx = arm_to_core_mmu_idx(armmemidx); + uint32_t syn; mop |= (s->nv2_mem_be ? MO_BE : MO_LE); @@ -XXX,XX +XXX,XX @@ static void handle_sys(DisasContext *s, bool isread, tcg_gen_addi_i64(ptr, ptr, (ri->nv2_redirect_offset & ~NV2_REDIR_FLAG_MASK)); tcg_rt = cpu_reg(s, rt); + + syn = syn_data_abort_vncr(0, !isread, 0); + disas_set_insn_syndrome(s, syn); if (isread) { tcg_gen_qemu_ld_i64(tcg_rt, ptr, memidx, mop); } else { -- 2.34.1
Mark up the cpreginfo structs to indicate offsets for system registers from VNCR_EL2, as defined in table D8-66 in rule R_CSRPQ in the Arm ARM. This commit covers offsets below 0x100; all of these registers are redirected to memory regardless of the value of HCR_EL2.NV1. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Miguel Luis <miguel.luis@oracle.com> --- target/arm/helper.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo hcrx_el2_reginfo = { .name = "HCRX_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 2, .opc2 = 2, .access = PL2_RW, .writefn = hcrx_write, .accessfn = access_hxen, + .nv2_redirect_offset = 0xa0, .fieldoffset = offsetof(CPUARMState, cp15.hcrx_el2), }; @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo el2_cp_reginfo[] = { .type = ARM_CP_IO, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 0, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.hcr_el2), + .nv2_redirect_offset = 0x78, .writefn = hcr_write, .raw_writefn = raw_write }, { .name = "HCR", .state = ARM_CP_STATE_AA32, .type = ARM_CP_ALIAS | ARM_CP_IO, @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo el2_cp_reginfo[] = { { .name = "VTCR_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 1, .opc2 = 2, .access = PL2_RW, + .nv2_redirect_offset = 0x40, /* no .writefn needed as this can't cause an ASID change */ .fieldoffset = offsetof(CPUARMState, cp15.vtcr_el2) }, { .name = "VTTBR", .state = ARM_CP_STATE_AA32, @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo el2_cp_reginfo[] = { { .name = "VTTBR_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 1, .opc2 = 0, .access = PL2_RW, .writefn = vttbr_write, .raw_writefn = raw_write, + .nv2_redirect_offset = 0x20, .fieldoffset = offsetof(CPUARMState, cp15.vttbr_el2) }, { .name = "SCTLR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 0, .opc2 = 0, @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo el2_cp_reginfo[] = { { .name = "TPIDR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 13, .crm = 0, .opc2 = 2, .access = PL2_RW, .resetvalue = 0, + .nv2_redirect_offset = 0x90, .fieldoffset = offsetof(CPUARMState, cp15.tpidr_el[2]) }, { .name = "TTBR0_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 0, .opc2 = 0, @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo el2_cp_reginfo[] = { .opc0 = 3, .opc1 = 4, .crn = 14, .crm = 0, .opc2 = 3, .access = PL2_RW, .type = ARM_CP_IO, .resetvalue = 0, .writefn = gt_cntvoff_write, + .nv2_redirect_offset = 0x60, .fieldoffset = offsetof(CPUARMState, cp15.cntvoff_el2) }, { .name = "CNTVOFF", .cp = 15, .opc1 = 4, .crm = 14, .access = PL2_RW, .type = ARM_CP_64BIT | ARM_CP_ALIAS | ARM_CP_IO, @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo el2_cp_reginfo[] = { { .name = "HSTR_EL2", .state = ARM_CP_STATE_BOTH, .cp = 15, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 3, .access = PL2_RW, + .nv2_redirect_offset = 0x80, .fieldoffset = offsetof(CPUARMState, cp15.hstr_el2) }, }; @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo el2_sec_cp_reginfo[] = { { .name = "VSTTBR_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 6, .opc2 = 0, .access = PL2_RW, .accessfn = sel2_access, + .nv2_redirect_offset = 0x30, .fieldoffset = offsetof(CPUARMState, cp15.vsttbr_el2) }, { .name = "VSTCR_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 6, .opc2 = 2, .access = PL2_RW, .accessfn = sel2_access, + .nv2_redirect_offset = 0x48, .fieldoffset = offsetof(CPUARMState, cp15.vstcr_el2) }, }; @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo nv2_reginfo[] = { .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 2, .opc2 = 0, .access = PL2_RW, .writefn = vncr_write, + .nv2_redirect_offset = 0xb0, .fieldoffset = offsetof(CPUARMState, cp15.vncr_el2) }, }; @@ -XXX,XX +XXX,XX @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 4, .crn = 0, .crm = 0, .opc2 = 0, .access = PL2_RW, .resetvalue = cpu->midr, .type = ARM_CP_EL3_NO_EL2_C_NZ, + .nv2_redirect_offset = 0x88, .fieldoffset = offsetof(CPUARMState, cp15.vpidr_el2) }, { .name = "VMPIDR", .state = ARM_CP_STATE_AA32, .cp = 15, .opc1 = 4, .crn = 0, .crm = 0, .opc2 = 5, @@ -XXX,XX +XXX,XX @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 4, .crn = 0, .crm = 0, .opc2 = 5, .access = PL2_RW, .resetvalue = vmpidr_def, .type = ARM_CP_EL3_NO_EL2_C_NZ, + .nv2_redirect_offset = 0x50, .fieldoffset = offsetof(CPUARMState, cp15.vmpidr_el2) }, }; /* -- 2.34.1
Mark up the cpreginfo structs to indicate offsets for system registers from VNCR_EL2, as defined in table D8-66 in rule R_CSRPQ in the Arm ARM. This commit covers offsets 0x100 to 0x160. Many (but not all) of the registers in this range have _EL12 aliases, and the slot in memory is shared between the _EL12 version of the register and the _EL1 version. Where we programmatically generate the regdef for the _EL12 register, arrange that its nv2_redirect_offset is set up correctly to do this. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Miguel Luis <miguel.luis@oracle.com> --- target/arm/debug_helper.c | 1 + target/arm/helper.c | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/target/arm/debug_helper.c b/target/arm/debug_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/debug_helper.c +++ b/target/arm/debug_helper.c @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo debug_cp_reginfo[] = { .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 2, .access = PL1_RW, .accessfn = access_tda, .fgt = FGT_MDSCR_EL1, + .nv2_redirect_offset = 0x158, .fieldoffset = offsetof(CPUARMState, cp15.mdscr_el1), .resetvalue = 0 }, /* diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo cp_reginfo[] = { .opc0 = 3, .opc1 = 0, .crn = 13, .crm = 0, .opc2 = 1, .access = PL1_RW, .accessfn = access_tvm_trvm, .fgt = FGT_CONTEXTIDR_EL1, + .nv2_redirect_offset = 0x108 | NV2_REDIR_NV1, .secure = ARM_CP_SECSTATE_NS, .fieldoffset = offsetof(CPUARMState, cp15.contextidr_el[1]), .resetvalue = 0, .writefn = contextidr_write, .raw_writefn = raw_write, }, @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo v6_cp_reginfo[] = { { .name = "CPACR", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 2, .accessfn = cpacr_access, .fgt = FGT_CPACR_EL1, + .nv2_redirect_offset = 0x100 | NV2_REDIR_NV1, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.cpacr_el1), .resetfn = cpacr_reset, .writefn = cpacr_write, .readfn = cpacr_read }, }; @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .opc0 = 3, .opc1 = 0, .crn = 5, .crm = 1, .opc2 = 0, .access = PL1_RW, .accessfn = access_tvm_trvm, .fgt = FGT_AFSR0_EL1, + .nv2_redirect_offset = 0x128 | NV2_REDIR_NV1, .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "AFSR1_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 5, .crm = 1, .opc2 = 1, .access = PL1_RW, .accessfn = access_tvm_trvm, .fgt = FGT_AFSR1_EL1, + .nv2_redirect_offset = 0x130 | NV2_REDIR_NV1, .type = ARM_CP_CONST, .resetvalue = 0 }, /* * MAIR can just read-as-written because we don't implement caches @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .opc0 = 3, .opc1 = 0, .crn = 10, .crm = 2, .opc2 = 0, .access = PL1_RW, .accessfn = access_tvm_trvm, .fgt = FGT_MAIR_EL1, + .nv2_redirect_offset = 0x140 | NV2_REDIR_NV1, .fieldoffset = offsetof(CPUARMState, cp15.mair_el[1]), .resetvalue = 0 }, { .name = "MAIR_EL3", .state = ARM_CP_STATE_AA64, @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo vmsa_cp_reginfo[] = { .opc0 = 3, .crn = 5, .crm = 2, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .accessfn = access_tvm_trvm, .fgt = FGT_ESR_EL1, + .nv2_redirect_offset = 0x138 | NV2_REDIR_NV1, .fieldoffset = offsetof(CPUARMState, cp15.esr_el[1]), .resetvalue = 0, }, { .name = "TTBR0_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 0, .opc2 = 0, @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo vmsa_cp_reginfo[] = { .opc0 = 3, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 2, .access = PL1_RW, .accessfn = access_tvm_trvm, .fgt = FGT_TCR_EL1, + .nv2_redirect_offset = 0x120 | NV2_REDIR_NV1, .writefn = vmsa_tcr_el12_write, .raw_writefn = raw_write, .resetvalue = 0, @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo lpae_cp_reginfo[] = { .opc0 = 3, .crn = 10, .crm = 3, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .accessfn = access_tvm_trvm, .fgt = FGT_AMAIR_EL1, + .nv2_redirect_offset = 0x148 | NV2_REDIR_NV1, .type = ARM_CP_CONST, .resetvalue = 0 }, /* AMAIR1 is mapped to AMAIR_EL1[63:32] */ { .name = "AMAIR1", .cp = 15, .crn = 10, .crm = 3, .opc1 = 0, .opc2 = 1, @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo v8_cp_reginfo[] = { .type = ARM_CP_ALIAS, .opc0 = 3, .opc1 = 0, .crn = 4, .crm = 0, .opc2 = 0, .access = PL1_RW, .accessfn = access_nv1, + .nv2_redirect_offset = 0x160 | NV2_REDIR_NV1, .fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_SVC]) }, /* * We rely on the access checks not allowing the guest to write to the @@ -XXX,XX +XXX,XX @@ static void define_arm_vh_e2h_redirects_aliases(ARMCPU *cpu) new_reg->writefn = el2_e2h_e12_write; new_reg->accessfn = el2_e2h_e12_access; + /* + * If the _EL1 register is redirected to memory by FEAT_NV2, + * then it shares the offset with the _EL12 register, + * and which one is redirected depends on HCR_EL2.NV1. + */ + if (new_reg->nv2_redirect_offset) { + assert(new_reg->nv2_redirect_offset & NV2_REDIR_NV1); + new_reg->nv2_redirect_offset &= ~NV2_REDIR_NV1; + new_reg->nv2_redirect_offset |= NV2_REDIR_NO_NV1; + } + ok = g_hash_table_insert(cpu->cp_regs, (gpointer)(uintptr_t)a->new_key, new_reg); g_assert(ok); @@ -XXX,XX +XXX,XX @@ void register_cp_regs_for_features(ARMCPU *cpu) { .name = "ACTLR_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 1, .access = PL1_RW, .accessfn = access_tacr, + .nv2_redirect_offset = 0x118, .type = ARM_CP_CONST, .resetvalue = cpu->reset_auxcr }, { .name = "ACTLR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 0, .opc2 = 1, @@ -XXX,XX +XXX,XX @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 0, .access = PL1_RW, .accessfn = access_tvm_trvm, .fgt = FGT_SCTLR_EL1, + .nv2_redirect_offset = 0x110 | NV2_REDIR_NV1, .bank_fieldoffsets = { offsetof(CPUARMState, cp15.sctlr_s), offsetof(CPUARMState, cp15.sctlr_ns) }, .writefn = sctlr_write, .resetvalue = cpu->reset_sctlr, -- 2.34.1
Mark up the cpreginfo structs to indicate offsets for system registers from VNCR_EL2, as defined in table D8-66 in rule R_CSRPQ in the Arm ARM. This commit covers offsets 0x168 to 0x1f8. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Miguel Luis <miguel.luis@oracle.com> --- target/arm/helper.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 2, .opc2 = 1, .type = ARM_CP_IO, .access = PL0_RW, .accessfn = gt_ptimer_access, + .nv2_redirect_offset = 0x180 | NV2_REDIR_NV1, .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].ctl), .resetvalue = 0, .readfn = gt_phys_redir_ctl_read, .raw_readfn = raw_read, @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 3, .opc2 = 1, .type = ARM_CP_IO, .access = PL0_RW, .accessfn = gt_vtimer_access, + .nv2_redirect_offset = 0x170 | NV2_REDIR_NV1, .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].ctl), .resetvalue = 0, .readfn = gt_virt_redir_ctl_read, .raw_readfn = raw_read, @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 2, .opc2 = 2, .access = PL0_RW, .type = ARM_CP_IO, + .nv2_redirect_offset = 0x178 | NV2_REDIR_NV1, .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].cval), .resetvalue = 0, .accessfn = gt_ptimer_access, .readfn = gt_phys_redir_cval_read, .raw_readfn = raw_read, @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 3, .opc2 = 2, .access = PL0_RW, .type = ARM_CP_IO, + .nv2_redirect_offset = 0x168 | NV2_REDIR_NV1, .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].cval), .resetvalue = 0, .accessfn = gt_vtimer_access, .readfn = gt_virt_redir_cval_read, .raw_readfn = raw_read, @@ -XXX,XX +XXX,XX @@ static void zcr_write(CPUARMState *env, const ARMCPRegInfo *ri, static const ARMCPRegInfo zcr_reginfo[] = { { .name = "ZCR_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 1, .crm = 2, .opc2 = 0, + .nv2_redirect_offset = 0x1e0 | NV2_REDIR_NV1, .access = PL1_RW, .type = ARM_CP_SVE, .fieldoffset = offsetof(CPUARMState, vfp.zcr_el[1]), .writefn = zcr_write, .raw_writefn = raw_write }, @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo sme_reginfo[] = { .writefn = svcr_write, .raw_writefn = raw_write }, { .name = "SMCR_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 1, .crm = 2, .opc2 = 6, + .nv2_redirect_offset = 0x1f0 | NV2_REDIR_NV1, .access = PL1_RW, .type = ARM_CP_SME, .fieldoffset = offsetof(CPUARMState, vfp.smcr_el[1]), .writefn = smcr_write, .raw_writefn = raw_write }, @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo sme_reginfo[] = { .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "SMPRIMAP_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 2, .opc2 = 5, + .nv2_redirect_offset = 0x1f8, .access = PL2_RW, .accessfn = access_smprimap, .type = ARM_CP_CONST, .resetvalue = 0 }, }; @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo mte_reginfo[] = { { .name = "TFSR_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 5, .crm = 6, .opc2 = 0, .access = PL1_RW, .accessfn = access_tfsr_el1, + .nv2_redirect_offset = 0x190 | NV2_REDIR_NV1, .fieldoffset = offsetof(CPUARMState, cp15.tfsr_el[1]) }, { .name = "TFSR_EL2", .state = ARM_CP_STATE_AA64, .type = ARM_CP_NV2_REDIRECT, @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo scxtnum_reginfo[] = { .opc0 = 3, .opc1 = 0, .crn = 13, .crm = 0, .opc2 = 7, .access = PL1_RW, .accessfn = access_scxtnum_el1, .fgt = FGT_SCXTNUM_EL1, + .nv2_redirect_offset = 0x188 | NV2_REDIR_NV1, .fieldoffset = offsetof(CPUARMState, scxtnum_el[1]) }, { .name = "SCXTNUM_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 13, .crm = 0, .opc2 = 7, @@ -XXX,XX +XXX,XX @@ static CPAccessResult access_fgt(CPUARMState *env, const ARMCPRegInfo *ri, static const ARMCPRegInfo fgt_reginfo[] = { { .name = "HFGRTR_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 4, + .nv2_redirect_offset = 0x1b8, .access = PL2_RW, .accessfn = access_fgt, .fieldoffset = offsetof(CPUARMState, cp15.fgt_read[FGTREG_HFGRTR]) }, { .name = "HFGWTR_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 5, + .nv2_redirect_offset = 0x1c0, .access = PL2_RW, .accessfn = access_fgt, .fieldoffset = offsetof(CPUARMState, cp15.fgt_write[FGTREG_HFGWTR]) }, { .name = "HDFGRTR_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 3, .crm = 1, .opc2 = 4, + .nv2_redirect_offset = 0x1d0, .access = PL2_RW, .accessfn = access_fgt, .fieldoffset = offsetof(CPUARMState, cp15.fgt_read[FGTREG_HDFGRTR]) }, { .name = "HDFGWTR_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 3, .crm = 1, .opc2 = 5, + .nv2_redirect_offset = 0x1d8, .access = PL2_RW, .accessfn = access_fgt, .fieldoffset = offsetof(CPUARMState, cp15.fgt_write[FGTREG_HDFGWTR]) }, { .name = "HFGITR_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 6, + .nv2_redirect_offset = 0x1c8, .access = PL2_RW, .accessfn = access_fgt, .fieldoffset = offsetof(CPUARMState, cp15.fgt_exec[FGTREG_HFGITR]) }, }; @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo vhe_reginfo[] = { .opc0 = 3, .opc1 = 5, .crn = 14, .crm = 2, .opc2 = 1, .type = ARM_CP_IO | ARM_CP_ALIAS, .access = PL2_RW, .accessfn = e2h_access, + .nv2_redirect_offset = 0x180 | NV2_REDIR_NO_NV1, .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].ctl), .writefn = gt_phys_ctl_write, .raw_writefn = raw_write }, { .name = "CNTV_CTL_EL02", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 5, .crn = 14, .crm = 3, .opc2 = 1, .type = ARM_CP_IO | ARM_CP_ALIAS, .access = PL2_RW, .accessfn = e2h_access, + .nv2_redirect_offset = 0x170 | NV2_REDIR_NO_NV1, .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].ctl), .writefn = gt_virt_ctl_write, .raw_writefn = raw_write }, { .name = "CNTP_TVAL_EL02", .state = ARM_CP_STATE_AA64, @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo vhe_reginfo[] = { .opc0 = 3, .opc1 = 5, .crn = 14, .crm = 2, .opc2 = 2, .type = ARM_CP_IO | ARM_CP_ALIAS, .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].cval), + .nv2_redirect_offset = 0x178 | NV2_REDIR_NO_NV1, .access = PL2_RW, .accessfn = e2h_access, .writefn = gt_phys_cval_write, .raw_writefn = raw_write }, { .name = "CNTV_CVAL_EL02", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 5, .crn = 14, .crm = 3, .opc2 = 2, .type = ARM_CP_IO | ARM_CP_ALIAS, + .nv2_redirect_offset = 0x168 | NV2_REDIR_NO_NV1, .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].cval), .access = PL2_RW, .accessfn = e2h_access, .writefn = gt_virt_cval_write, .raw_writefn = raw_write }, -- 2.34.1
Mark up the cpreginfo structs to indicate offsets for system registers from VNCR_EL2, as defined in table D8-66 in rule R_CSRPQ in the Arm ARM. This covers all the remaining offsets at 0x200 and above, except for the GIC ICH_* registers. (Note that because we don't implement FEAT_SPE, FEAT_TRF, FEAT_MPAM, FEAT_BRBE or FEAT_AMUv1p1 we don't implement any of the registers that use offsets at 0x800 and above.) Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Miguel Luis <miguel.luis@oracle.com> --- target/arm/helper.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo vmsa_pmsa_cp_reginfo[] = { .opc0 = 3, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .accessfn = access_tvm_trvm, .fgt = FGT_FAR_EL1, + .nv2_redirect_offset = 0x220 | NV2_REDIR_NV1, .fieldoffset = offsetof(CPUARMState, cp15.far_el[1]), .resetvalue = 0, }, }; @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo vmsa_cp_reginfo[] = { .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 0, .opc2 = 0, .access = PL1_RW, .accessfn = access_tvm_trvm, .fgt = FGT_TTBR0_EL1, + .nv2_redirect_offset = 0x200 | NV2_REDIR_NV1, .writefn = vmsa_ttbr_write, .resetvalue = 0, .raw_writefn = raw_write, .bank_fieldoffsets = { offsetof(CPUARMState, cp15.ttbr0_s), offsetof(CPUARMState, cp15.ttbr0_ns) } }, @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo vmsa_cp_reginfo[] = { .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 0, .opc2 = 1, .access = PL1_RW, .accessfn = access_tvm_trvm, .fgt = FGT_TTBR1_EL1, + .nv2_redirect_offset = 0x210 | NV2_REDIR_NV1, .writefn = vmsa_ttbr_write, .resetvalue = 0, .raw_writefn = raw_write, .bank_fieldoffsets = { offsetof(CPUARMState, cp15.ttbr1_s), offsetof(CPUARMState, cp15.ttbr1_ns) } }, @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo v8_cp_reginfo[] = { .type = ARM_CP_ALIAS, .opc0 = 3, .opc1 = 0, .crn = 4, .crm = 0, .opc2 = 1, .access = PL1_RW, .accessfn = access_nv1, + .nv2_redirect_offset = 0x230 | NV2_REDIR_NV1, .fieldoffset = offsetof(CPUARMState, elr_el[1]) }, { .name = "SPSR_EL1", .state = ARM_CP_STATE_AA64, .type = ARM_CP_ALIAS, @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo v8_cp_reginfo[] = { .fieldoffset = offsetof(CPUARMState, sp_el[0]) }, { .name = "SP_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 1, .opc2 = 0, + .nv2_redirect_offset = 0x240, .access = PL2_RW, .type = ARM_CP_ALIAS | ARM_CP_EL3_NO_EL2_KEEP, .fieldoffset = offsetof(CPUARMState, sp_el[1]) }, { .name = "SPSel", .state = ARM_CP_STATE_AA64, @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo minimal_ras_reginfo[] = { .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "VDISR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 1, .opc2 = 1, + .nv2_redirect_offset = 0x500, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.vdisr_el2) }, { .name = "VSESR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 5, .crm = 2, .opc2 = 3, + .nv2_redirect_offset = 0x508, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.vsesr_el2) }, }; @@ -XXX,XX +XXX,XX @@ void register_cp_regs_for_features(ARMCPU *cpu) .access = PL1_RW, .writefn = vbar_write, .accessfn = access_nv1, .fgt = FGT_VBAR_EL1, + .nv2_redirect_offset = 0x250 | NV2_REDIR_NV1, .bank_fieldoffsets = { offsetof(CPUARMState, cp15.vbar_s), offsetof(CPUARMState, cp15.vbar_ns) }, .resetvalue = 0 }, -- 2.34.1
Mark up the cpreginfo structs for the GIC CPU registers to indicate the offsets from VNCR_EL2, as defined in table D8-66 in rule R_CSRPQ in the Arm ARM. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Miguel Luis <miguel.luis@oracle.com> --- hw/intc/arm_gicv3_cpuif.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index XXXXXXX..XXXXXXX 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo gicv3_cpuif_hcr_reginfo[] = { { .name = "ICH_AP0R0_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 8, .opc2 = 0, .type = ARM_CP_IO | ARM_CP_NO_RAW, + .nv2_redirect_offset = 0x480, .access = PL2_RW, .readfn = ich_ap_read, .writefn = ich_ap_write, @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo gicv3_cpuif_hcr_reginfo[] = { { .name = "ICH_AP1R0_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 9, .opc2 = 0, .type = ARM_CP_IO | ARM_CP_NO_RAW, + .nv2_redirect_offset = 0x4a0, .access = PL2_RW, .readfn = ich_ap_read, .writefn = ich_ap_write, @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo gicv3_cpuif_hcr_reginfo[] = { { .name = "ICH_HCR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 11, .opc2 = 0, .type = ARM_CP_IO | ARM_CP_NO_RAW, + .nv2_redirect_offset = 0x4c0, .access = PL2_RW, .readfn = ich_hcr_read, .writefn = ich_hcr_write, @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo gicv3_cpuif_hcr_reginfo[] = { { .name = "ICH_VMCR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 11, .opc2 = 7, .type = ARM_CP_IO | ARM_CP_NO_RAW, + .nv2_redirect_offset = 0x4c8, .access = PL2_RW, .readfn = ich_vmcr_read, .writefn = ich_vmcr_write, @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo gicv3_cpuif_ich_apxr1_reginfo[] = { { .name = "ICH_AP0R1_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 8, .opc2 = 1, .type = ARM_CP_IO | ARM_CP_NO_RAW, + .nv2_redirect_offset = 0x488, .access = PL2_RW, .readfn = ich_ap_read, .writefn = ich_ap_write, @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo gicv3_cpuif_ich_apxr1_reginfo[] = { { .name = "ICH_AP1R1_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 9, .opc2 = 1, .type = ARM_CP_IO | ARM_CP_NO_RAW, + .nv2_redirect_offset = 0x4a8, .access = PL2_RW, .readfn = ich_ap_read, .writefn = ich_ap_write, @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo gicv3_cpuif_ich_apxr23_reginfo[] = { { .name = "ICH_AP0R2_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 8, .opc2 = 2, .type = ARM_CP_IO | ARM_CP_NO_RAW, + .nv2_redirect_offset = 0x490, .access = PL2_RW, .readfn = ich_ap_read, .writefn = ich_ap_write, @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo gicv3_cpuif_ich_apxr23_reginfo[] = { { .name = "ICH_AP0R3_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 8, .opc2 = 3, .type = ARM_CP_IO | ARM_CP_NO_RAW, + .nv2_redirect_offset = 0x498, .access = PL2_RW, .readfn = ich_ap_read, .writefn = ich_ap_write, @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo gicv3_cpuif_ich_apxr23_reginfo[] = { { .name = "ICH_AP1R2_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 9, .opc2 = 2, .type = ARM_CP_IO | ARM_CP_NO_RAW, + .nv2_redirect_offset = 0x4b0, .access = PL2_RW, .readfn = ich_ap_read, .writefn = ich_ap_write, @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo gicv3_cpuif_ich_apxr23_reginfo[] = { { .name = "ICH_AP1R3_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 9, .opc2 = 3, .type = ARM_CP_IO | ARM_CP_NO_RAW, + .nv2_redirect_offset = 0x4b8, .access = PL2_RW, .readfn = ich_ap_read, .writefn = ich_ap_write, @@ -XXX,XX +XXX,XX @@ void gicv3_init_cpuif(GICv3State *s) .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 12 + (j >> 3), .opc2 = j & 7, .type = ARM_CP_IO | ARM_CP_NO_RAW, + .nv2_redirect_offset = 0x400 + 8 * j, .access = PL2_RW, .readfn = ich_lr_read, .writefn = ich_lr_write, -- 2.34.1
When interpreting CPU dumps where FEAT_NV and FEAT_NV2 are in use, it's helpful to include the values of HCR_EL2.{NV,NV1,NV2} in the CPU dump format, as a way of distinguishing when we are in EL1 as part of executing guest-EL2 and when we are just in normal EL1. Add the bits to the end of the log line that shows PSTATE and similar information: PSTATE=000003c9 ---- EL2h BTYPE=0 NV NV2 Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Miguel Luis <miguel.luis@oracle.com> --- target/arm/cpu.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -XXX,XX +XXX,XX @@ static void aarch64_cpu_dump_state(CPUState *cs, FILE *f, int flags) uint32_t psr = pstate_read(env); int i, j; int el = arm_current_el(env); + uint64_t hcr = arm_hcr_el2_eff(env); const char *ns_status; bool sve; @@ -XXX,XX +XXX,XX @@ static void aarch64_cpu_dump_state(CPUState *cs, FILE *f, int flags) if (cpu_isar_feature(aa64_bti, cpu)) { qemu_fprintf(f, " BTYPE=%d", (psr & PSTATE_BTYPE) >> 10); } + qemu_fprintf(f, "%s%s%s", + (hcr & HCR_NV) ? " NV" : "", + (hcr & HCR_NV1) ? " NV1" : "", + (hcr & HCR_NV2) ? " NV2" : ""); if (!(flags & CPU_DUMP_FPU)) { qemu_fprintf(f, "\n"); return; -- 2.34.1
We already print various lines of information when we take an exception, including the ELR and (if relevant) the FAR. Now that FEAT_NV means that we might report something other than the old PSTATE to the guest as the SPSR, it's worth logging this as well. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Miguel Luis <miguel.luis@oracle.com> --- target/arm/helper.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ static void arm_cpu_do_interrupt_aarch64(CPUState *cs) } env->banked_spsr[aarch64_banked_spsr_index(new_el)] = old_mode; + qemu_log_mask(CPU_LOG_INT, "...with SPSR 0x%x\n", old_mode); qemu_log_mask(CPU_LOG_INT, "...with ELR 0x%" PRIx64 "\n", env->elr_el[new_el]); -- 2.34.1
Enable FEAT_NV2 on the 'max' CPU, and stop filtering it out for the Neoverse N2 and Neoverse V1 CPUs. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Miguel Luis <miguel.luis@oracle.com> --- docs/system/arm/emulation.rst | 1 + target/arm/cpu.c | 5 ----- target/arm/tcg/cpu64.c | 2 +- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index XXXXXXX..XXXXXXX 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -XXX,XX +XXX,XX @@ the following architecture extensions: - FEAT_MTE2 (Memory Tagging Extension) - FEAT_MTE3 (MTE Asymmetric Fault Handling) - FEAT_NV (Nested Virtualization) +- FEAT_NV2 (Enhanced nested virtualization support) - FEAT_PACIMP (Pointer authentication - IMPLEMENTATION DEFINED algorithm) - FEAT_PACQARMA3 (Pointer authentication - QARMA3 algorithm) - FEAT_PACQARMA5 (Pointer authentication - QARMA5 algorithm) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -XXX,XX +XXX,XX @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) /* FEAT_MPAM (Memory Partitioning and Monitoring Extension) */ cpu->isar.id_aa64pfr0 = FIELD_DP64(cpu->isar.id_aa64pfr0, ID_AA64PFR0, MPAM, 0); - /* FEAT_NV2 (Enhanced Nested Virtualization support) */ - if (FIELD_EX64(cpu->isar.id_aa64mmfr2, ID_AA64MMFR2, NV) > 1) { - cpu->isar.id_aa64mmfr2 = - FIELD_DP64(cpu->isar.id_aa64mmfr2, ID_AA64MMFR2, NV, 1); - } } /* MPU can be configured out of a PMSA CPU either by setting has-mpu diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -XXX,XX +XXX,XX @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64MMFR2, UAO, 1); /* FEAT_UAO */ t = FIELD_DP64(t, ID_AA64MMFR2, IESB, 1); /* FEAT_IESB */ t = FIELD_DP64(t, ID_AA64MMFR2, VARANGE, 1); /* FEAT_LVA */ - t = FIELD_DP64(t, ID_AA64MMFR2, NV, 1); /* FEAT_NV */ + t = FIELD_DP64(t, ID_AA64MMFR2, NV, 2); /* FEAT_NV2 */ t = FIELD_DP64(t, ID_AA64MMFR2, ST, 1); /* FEAT_TTST */ t = FIELD_DP64(t, ID_AA64MMFR2, AT, 1); /* FEAT_LSE2 */ t = FIELD_DP64(t, ID_AA64MMFR2, IDS, 1); /* FEAT_IDST */ -- 2.34.1
v3->v4: Windows headers define an INT type which clashed with an enum value name in arm_gicv3_its.c... The following changes since commit eae587e8e3694b1aceab23239493fb4c7e1a80f5: Merge remote-tracking branch 'remotes/armbru/tags/pull-qapi-2021-09-13' into staging (2021-09-13 11:00:30 +0100) are available in the Git repository at: https://git.linaro.org/people/pmaydell/qemu-arm.git tags/pull-target-arm-20210913-3 for you to fetch changes up to 28e987a7e7edaa3ca7feeac65edca26145df8814: hw/arm/mps2.c: Mark internal-only I2C buses as 'full' (2021-09-13 21:01:08 +0100) ---------------------------------------------------------------- target-arm queue: * mark MPS2/MPS3 board-internal i2c buses as 'full' so that command line user-created devices are not plugged into them * Take an exception if PSTATE.IL is set * Support an emulated ITS in the virt board * Add support for kudo-bmc board * Probe for KVM_CAP_ARM_VM_IPA_SIZE when creating scratch VM * cadence_uart: Fix clock handling issues that prevented u-boot from running ---------------------------------------------------------------- Bin Meng (6): hw/misc: zynq_slcr: Correctly compute output clocks in the reset exit phase hw/char: cadence_uart: Disable transmit when input clock is disabled hw/char: cadence_uart: Move clock/reset check to uart_can_receive() hw/char: cadence_uart: Convert to memop_with_attrs() ops hw/char: cadence_uart: Ignore access when unclocked or in reset for uart_{read, write}() hw/char: cadence_uart: Log a guest error when device is unclocked or in reset Chris Rauer (1): hw/arm: Add support for kudo-bmc board. Marc Zyngier (1): hw/arm/virt: KVM: Probe for KVM_CAP_ARM_VM_IPA_SIZE when creating scratch VM Peter Maydell (5): target/arm: Take an exception if PSTATE.IL is set qdev: Support marking individual buses as 'full' hw/arm/mps2-tz.c: Add extra data parameter to MakeDevFn hw/arm/mps2-tz.c: Mark internal-only I2C buses as 'full' hw/arm/mps2.c: Mark internal-only I2C buses as 'full' Richard Henderson (1): target/arm: Merge disas_a64_insn into aarch64_tr_translate_insn Shashi Mallela (9): hw/intc: GICv3 ITS initial framework hw/intc: GICv3 ITS register definitions added hw/intc: GICv3 ITS command queue framework hw/intc: GICv3 ITS Command processing hw/intc: GICv3 ITS Feature enablement hw/intc: GICv3 redistributor ITS processing tests/data/acpi/virt: Add IORT files for ITS hw/arm/virt: add ITS support in virt GIC tests/data/acpi/virt: Update IORT files for ITS docs/system/arm/nuvoton.rst | 1 + hw/intc/gicv3_internal.h | 188 ++++- include/hw/arm/virt.h | 2 + include/hw/intc/arm_gicv3_common.h | 13 + include/hw/intc/arm_gicv3_its_common.h | 32 +- include/hw/qdev-core.h | 24 + target/arm/cpu.h | 1 + target/arm/kvm_arm.h | 4 +- target/arm/syndrome.h | 5 + target/arm/translate.h | 2 + hw/arm/mps2-tz.c | 92 ++- hw/arm/mps2.c | 12 +- hw/arm/npcm7xx_boards.c | 34 + hw/arm/virt.c | 29 +- hw/char/cadence_uart.c | 61 +- hw/intc/arm_gicv3.c | 14 + hw/intc/arm_gicv3_common.c | 13 + hw/intc/arm_gicv3_cpuif.c | 7 +- hw/intc/arm_gicv3_dist.c | 5 +- hw/intc/arm_gicv3_its.c | 1322 ++++++++++++++++++++++++++++++++ hw/intc/arm_gicv3_its_common.c | 7 +- hw/intc/arm_gicv3_its_kvm.c | 2 +- hw/intc/arm_gicv3_redist.c | 153 +++- hw/misc/zynq_slcr.c | 31 +- softmmu/qdev-monitor.c | 7 +- target/arm/helper-a64.c | 1 + target/arm/helper.c | 8 + target/arm/kvm.c | 7 +- target/arm/translate-a64.c | 255 +++--- target/arm/translate.c | 21 + hw/intc/meson.build | 1 + tests/data/acpi/virt/IORT | Bin 0 -> 124 bytes tests/data/acpi/virt/IORT.memhp | Bin 0 -> 124 bytes tests/data/acpi/virt/IORT.numamem | Bin 0 -> 124 bytes tests/data/acpi/virt/IORT.pxb | Bin 0 -> 124 bytes 35 files changed, 2144 insertions(+), 210 deletions(-) create mode 100644 hw/intc/arm_gicv3_its.c create mode 100644 tests/data/acpi/virt/IORT create mode 100644 tests/data/acpi/virt/IORT.memhp create mode 100644 tests/data/acpi/virt/IORT.numamem create mode 100644 tests/data/acpi/virt/IORT.pxb