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
 -----BEGIN PGP SIGNATURE-----
 
 iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAmWfypMZHHBldGVyLm1h
 eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3sleD/4tQOMteba5BNMDns6R96E4
 kj5q0Iy9XyzQ486Q4cIZXI5N3BddCp2ks8WeS2W3w4IT/lms0U6UwXV4E98I4I/b
 KSfOoUd/cp8IvdvzfpWbmQcPMoauHZdCUN33pYYXOjfi1RkpzgNU5Qgh09Nl/xYU
 V3oaEvWhLtepT/fwJLYxoqVHDaEmyW+6zriF0+eGjZvkhgPyhllla9eti7AyHTfH
 T3A4Fyx/wudRE3NP6xsLfxldriJTxQeba+TqLSh3IXn/PMtK13/ARsY/hl72Q4ML
 Fgad8Zho4eXbuOQ9oiqb7gp4K3IKd9/8FbCzECoIAq7AnLAD4KwpLQR8GULRvYW3
 0eQq2txTXQWNcmWpIyDRRME+qeNVwWSk+QJDs5WuhVqlVQ4hpqtgFf1EX+7ORdS1
 WG0fb8etvr8oCSkzCmP/o6xYGJ0EyTVMU5DmWviy3bxMrUMcmobjvCQr/n2gC713
 1NDmEaYPbl+pX8EMu8byst7/No2PXRgIO0UVVb4KZybfhNy+BBs+LiMVlSRS5YH4
 8NWtoYZlG9RcPnY+8Xrxz9VTi2cNAAcdbf5uK3snJxkFV2SmV3oBoMxWen3mee0f
 2PNVEbt9zvPV8hViBVLsqRhVXd9wMq6motIRlkKge1u1TvwIxO21ibykI3tvYOGv
 BffIjhUdnYtX90JAtXtFDw==
 =yQwf
 -----END PGP SIGNATURE-----

Merge tag 'pull-target-arm-20240111' of https://git.linaro.org/people/pmaydell/qemu-arm into staging

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

# -----BEGIN PGP SIGNATURE-----
#
# iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAmWfypMZHHBldGVyLm1h
# eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3sleD/4tQOMteba5BNMDns6R96E4
# kj5q0Iy9XyzQ486Q4cIZXI5N3BddCp2ks8WeS2W3w4IT/lms0U6UwXV4E98I4I/b
# KSfOoUd/cp8IvdvzfpWbmQcPMoauHZdCUN33pYYXOjfi1RkpzgNU5Qgh09Nl/xYU
# V3oaEvWhLtepT/fwJLYxoqVHDaEmyW+6zriF0+eGjZvkhgPyhllla9eti7AyHTfH
# T3A4Fyx/wudRE3NP6xsLfxldriJTxQeba+TqLSh3IXn/PMtK13/ARsY/hl72Q4ML
# Fgad8Zho4eXbuOQ9oiqb7gp4K3IKd9/8FbCzECoIAq7AnLAD4KwpLQR8GULRvYW3
# 0eQq2txTXQWNcmWpIyDRRME+qeNVwWSk+QJDs5WuhVqlVQ4hpqtgFf1EX+7ORdS1
# WG0fb8etvr8oCSkzCmP/o6xYGJ0EyTVMU5DmWviy3bxMrUMcmobjvCQr/n2gC713
# 1NDmEaYPbl+pX8EMu8byst7/No2PXRgIO0UVVb4KZybfhNy+BBs+LiMVlSRS5YH4
# 8NWtoYZlG9RcPnY+8Xrxz9VTi2cNAAcdbf5uK3snJxkFV2SmV3oBoMxWen3mee0f
# 2PNVEbt9zvPV8hViBVLsqRhVXd9wMq6motIRlkKge1u1TvwIxO21ibykI3tvYOGv
# BffIjhUdnYtX90JAtXtFDw==
# =yQwf
# -----END PGP SIGNATURE-----
# gpg: Signature made Thu 11 Jan 2024 11:01:39 GMT
# gpg:                using RSA key E1A5C593CD419DE28E8315CF3C2525ED14360CDE
# gpg:                issuer "peter.maydell@linaro.org"
# gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" [ultimate]
# gpg:                 aka "Peter Maydell <pmaydell@gmail.com>" [ultimate]
# gpg:                 aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" [ultimate]
# gpg:                 aka "Peter Maydell <peter@archaic.org.uk>" [ultimate]
# Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83  15CF 3C25 25ED 1436 0CDE

* tag 'pull-target-arm-20240111' of https://git.linaro.org/people/pmaydell/qemu-arm: (41 commits)
  target/arm: Add FEAT_NV2 to max, neoverse-n2, neoverse-v1 CPUs
  target/arm: Enhance CPU_LOG_INT to show SPSR on AArch64 exception-entry
  target/arm: Report HCR_EL2.{NV,NV1,NV2} in cpu dumps
  hw/intc/arm_gicv3_cpuif: Mark up VNCR offsets for GIC CPU registers
  target/arm: Mark up VNCR offsets (offsets >= 0x200, except GIC)
  target/arm: Mark up VNCR offsets (offsets 0x168..0x1f8)
  target/arm: Mark up VNCR offsets (offsets 0x100..0x160)
  target/arm: Mark up VNCR offsets (offsets 0x0..0xff)
  target/arm: Report VNCR_EL2 based faults correctly
  target/arm: Implement FEAT_NV2 redirection of sysregs to RAM
  target/arm: Handle FEAT_NV2 redirection of SPSR_EL2, ELR_EL2, ESR_EL2, FAR_EL2
  target/arm: Handle FEAT_NV2 changes to when SPSR_EL1.M reports EL2
  target/arm: Implement VNCR_EL2 register
  target/arm: Handle HCR_EL2 accesses for FEAT_NV2 bits
  target/arm: Add FEAT_NV to max, neoverse-n2, neoverse-v1 CPUs
  target/arm: Handle FEAT_NV page table attribute changes
  target/arm: Treat LDTR* and STTR* as LDR/STR when NV, NV1 is 1, 1
  target/arm: Don't honour PSTATE.PAN when HCR_EL2.{NV, NV1} == {1, 1}
  target/arm: Always use arm_pan_enabled() when checking if PAN is enabled
  target/arm: Trap registers when HCR_EL2.{NV, NV1} == {1, 1}
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2024-01-11 11:05:44 +00:00
commit f614acb745
39 changed files with 1203 additions and 80 deletions

View File

@ -1122,6 +1122,21 @@ L: qemu-arm@nongnu.org
S: Maintained S: Maintained
F: hw/arm/olimex-stm32-h405.c 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
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 SmartFusion2
M: Subbaraya Sundeep <sundeep.lkml@gmail.com> M: Subbaraya Sundeep <sundeep.lkml@gmail.com>
M: Peter Maydell <peter.maydell@linaro.org> M: Peter Maydell <peter.maydell@linaro.org>

View File

@ -19,6 +19,7 @@ CONFIG_ARM_VIRT=y
# CONFIG_NSERIES=n # CONFIG_NSERIES=n
# CONFIG_STELLARIS=n # CONFIG_STELLARIS=n
# CONFIG_STM32VLDISCOVERY=n # CONFIG_STM32VLDISCOVERY=n
# CONFIG_B_L475E_IOT01A=n
# CONFIG_REALVIEW=n # CONFIG_REALVIEW=n
# CONFIG_VERSATILE=n # CONFIG_VERSATILE=n
# CONFIG_VEXPRESS=n # CONFIG_VEXPRESS=n

View File

@ -0,0 +1,46 @@
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

View File

@ -63,6 +63,8 @@ the following architecture extensions:
- FEAT_MTE (Memory Tagging Extension) - FEAT_MTE (Memory Tagging Extension)
- FEAT_MTE2 (Memory Tagging Extension) - FEAT_MTE2 (Memory Tagging Extension)
- FEAT_MTE3 (MTE Asymmetric Fault Handling) - FEAT_MTE3 (MTE Asymmetric Fault Handling)
- FEAT_NV (Nested Virtualization)
- FEAT_NV2 (Enhanced nested virtualization support)
- FEAT_PACIMP (Pointer authentication - IMPLEMENTATION DEFINED algorithm) - FEAT_PACIMP (Pointer authentication - IMPLEMENTATION DEFINED algorithm)
- FEAT_PACQARMA3 (Pointer authentication - QARMA3 algorithm) - FEAT_PACQARMA3 (Pointer authentication - QARMA3 algorithm)
- FEAT_PACQARMA5 (Pointer authentication - QARMA5 algorithm) - FEAT_PACQARMA5 (Pointer authentication - QARMA5 algorithm)

View File

@ -16,11 +16,13 @@ based on this chip :
- ``netduino2`` Netduino 2 board with STM32F205RFT6 microcontroller - ``netduino2`` Netduino 2 board with STM32F205RFT6 microcontroller
The STM32F4 series is based on ARM Cortex-M4F core. This series is pin-to-pin The STM32F4 series is based on ARM Cortex-M4F core, as well as the STM32L4
compatible with STM32F2 series. The following machines are based on this chip : 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 - ``netduinoplus2`` Netduino Plus 2 board with STM32F405RGT6 microcontroller
- ``olimex-stm32-h405`` Olimex STM32 H405 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. There are many other STM32 series that are currently not supported by QEMU.

View File

@ -84,6 +84,7 @@ undocumented; you can get a complete list by running
arm/vexpress arm/vexpress
arm/aspeed arm/aspeed
arm/bananapi_m2u.rst arm/bananapi_m2u.rst
arm/b-l475e-iot01a.rst
arm/sabrelite arm/sabrelite
arm/digic arm/digic
arm/cubieboard arm/cubieboard

View File

@ -449,6 +449,17 @@ config STM32F405_SOC
select STM32F4XX_SYSCFG select STM32F4XX_SYSCFG
select STM32F4XX_EXTI select STM32F4XX_EXTI
config B_L475E_IOT01A
bool
default y
depends on TCG && ARM
select STM32L4X5_SOC
config STM32L4X5_SOC
bool
select ARM_V7M
select OR_IRQ
config XLNX_ZYNQMP_ARM config XLNX_ZYNQMP_ARM
bool bool
default y if PIXMAN default y if PIXMAN
@ -537,6 +548,7 @@ config FSL_IMX6
select IMX_I2C select IMX_I2C
select IMX_USBPHY select IMX_USBPHY
select WDT_IMX2 select WDT_IMX2
select PL310 # cache controller
select SDHCI select SDHCI
config ASPEED_SOC config ASPEED_SOC

View File

@ -256,6 +256,8 @@ static void armv7m_instance_init(Object *obj)
object_initialize_child(obj, "nvic", &s->nvic, TYPE_NVIC); object_initialize_child(obj, "nvic", &s->nvic, TYPE_NVIC);
object_property_add_alias(obj, "num-irq", object_property_add_alias(obj, "num-irq",
OBJECT(&s->nvic), "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], object_initialize_child(obj, "systick-reg-ns", &s->systick[M_REG_NS],
TYPE_SYSTICK); TYPE_SYSTICK);

72
hw/arm/b-l475e-iot01a.c Normal file
View File

@ -0,0 +1,72 @@
/*
* 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)

View File

@ -154,6 +154,9 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp)
qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_FIQ)); 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)) { if (!sysbus_realize(SYS_BUS_DEVICE(&s->ccm), errp)) {
return; return;
} }

View File

@ -42,6 +42,8 @@ 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_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_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_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_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_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')) arm_ss.add(when: 'CONFIG_FSL_IMX25', if_true: files('fsl-imx25.c', 'imx25_pdk.c'))

View File

@ -60,6 +60,7 @@ static void emcraft_sf2_s2s010_init(MachineState *machine)
memory_region_add_subregion(sysmem, DDR_BASE_ADDRESS, ddr); memory_region_add_subregion(sysmem, DDR_BASE_ADDRESS, ddr);
dev = qdev_new(TYPE_MSF2_SOC); 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, "part-name", "M2S010");
qdev_prop_set_string(dev, "cpu-type", mc->default_cpu_type); qdev_prop_set_string(dev, "cpu-type", mc->default_cpu_type);

View File

@ -44,6 +44,7 @@ static void netduino2_init(MachineState *machine)
clock_set_hz(sysclk, SYSCLK_FRQ); clock_set_hz(sysclk, SYSCLK_FRQ);
dev = qdev_new(TYPE_STM32F205_SOC); dev = qdev_new(TYPE_STM32F205_SOC);
object_property_add_child(OBJECT(machine), "soc", OBJECT(dev));
qdev_connect_clock_in(dev, "sysclk", sysclk); qdev_connect_clock_in(dev, "sysclk", sysclk);
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);

View File

@ -44,6 +44,7 @@ static void netduinoplus2_init(MachineState *machine)
clock_set_hz(sysclk, SYSCLK_FRQ); clock_set_hz(sysclk, SYSCLK_FRQ);
dev = qdev_new(TYPE_STM32F405_SOC); dev = qdev_new(TYPE_STM32F405_SOC);
object_property_add_child(OBJECT(machine), "soc", OBJECT(dev));
qdev_connect_clock_in(dev, "sysclk", sysclk); qdev_connect_clock_in(dev, "sysclk", sysclk);
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);

View File

@ -47,6 +47,7 @@ static void olimex_stm32_h405_init(MachineState *machine)
clock_set_hz(sysclk, SYSCLK_FRQ); clock_set_hz(sysclk, SYSCLK_FRQ);
dev = qdev_new(TYPE_STM32F405_SOC); dev = qdev_new(TYPE_STM32F405_SOC);
object_property_add_child(OBJECT(machine), "soc", OBJECT(dev));
qdev_connect_clock_in(dev, "sysclk", sysclk); qdev_connect_clock_in(dev, "sysclk", sysclk);
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);

View File

@ -47,6 +47,7 @@
#define BP_GAMEPAD 0x04 #define BP_GAMEPAD 0x04
#define NUM_IRQ_LINES 64 #define NUM_IRQ_LINES 64
#define NUM_PRIO_BITS 3
typedef const struct { typedef const struct {
const char *name; const char *name;
@ -1067,6 +1068,7 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board)
nvic = qdev_new(TYPE_ARMV7M); nvic = qdev_new(TYPE_ARMV7M);
qdev_prop_set_uint32(nvic, "num-irq", NUM_IRQ_LINES); 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_string(nvic, "cpu-type", ms->cpu_type);
qdev_prop_set_bit(nvic, "enable-bitband", true); qdev_prop_set_bit(nvic, "enable-bitband", true);
qdev_connect_clock_in(nvic, "cpuclk", qdev_connect_clock_in(nvic, "cpuclk",

View File

@ -115,6 +115,7 @@ static void stm32f100_soc_realize(DeviceState *dev_soc, Error **errp)
/* Init ARMv7m */ /* Init ARMv7m */
armv7m = DEVICE(&s->armv7m); armv7m = DEVICE(&s->armv7m);
qdev_prop_set_uint32(armv7m, "num-irq", 61); 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_string(armv7m, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m3"));
qdev_prop_set_bit(armv7m, "enable-bitband", true); qdev_prop_set_bit(armv7m, "enable-bitband", true);
qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk); qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk);

View File

@ -127,6 +127,7 @@ static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp)
armv7m = DEVICE(&s->armv7m); armv7m = DEVICE(&s->armv7m);
qdev_prop_set_uint32(armv7m, "num-irq", 96); 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_string(armv7m, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m3"));
qdev_prop_set_bit(armv7m, "enable-bitband", true); qdev_prop_set_bit(armv7m, "enable-bitband", true);
qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk); qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk);

View File

@ -149,6 +149,7 @@ static void stm32f405_soc_realize(DeviceState *dev_soc, Error **errp)
armv7m = DEVICE(&s->armv7m); armv7m = DEVICE(&s->armv7m);
qdev_prop_set_uint32(armv7m, "num-irq", 96); 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_string(armv7m, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m4"));
qdev_prop_set_bit(armv7m, "enable-bitband", true); qdev_prop_set_bit(armv7m, "enable-bitband", true);
qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk); qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk);

266
hw/arm/stm32l4x5_soc.c Normal file
View File

@ -0,0 +1,266 @@
/*
* 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_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);
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)

View File

@ -47,6 +47,7 @@ static void stm32vldiscovery_init(MachineState *machine)
clock_set_hz(sysclk, SYSCLK_FRQ); clock_set_hz(sysclk, SYSCLK_FRQ);
dev = qdev_new(TYPE_STM32F100_SOC); dev = qdev_new(TYPE_STM32F100_SOC);
object_property_add_child(OBJECT(machine), "soc", OBJECT(dev));
qdev_connect_clock_in(dev, "sysclk", sysclk); qdev_connect_clock_in(dev, "sysclk", sysclk);
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);

View File

@ -1434,16 +1434,25 @@ static void icv_eoir_write(CPUARMState *env, const ARMCPRegInfo *ri,
idx = icv_find_active(cs, irq); idx = icv_find_active(cs, irq);
if (idx < 0) { 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 { } else {
uint64_t lr = cs->ich_lr_el2[idx]; uint64_t lr = cs->ich_lr_el2[idx];
int thisgrp = (lr & ICH_LR_EL2_GROUP) ? GICV3_G1NS : GICV3_G0; int thisgrp = (lr & ICH_LR_EL2_GROUP) ? GICV3_G1NS : GICV3_G0;
int lr_gprio = ich_lr_prio(lr) & icv_gprio_mask(cs, grp); int lr_gprio = ich_lr_prio(lr) & icv_gprio_mask(cs, grp);
if (thisgrp == grp && lr_gprio == dropprio) { if (thisgrp == grp && lr_gprio == dropprio) {
if (!icv_eoi_split(env, cs)) { if (!icv_eoi_split(env, cs) || irq >= GICV3_LPI_INTID_START) {
/* Priority drop and deactivate not split: deactivate irq now */ /*
* 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); icv_deactivate_irq(cs, idx);
} }
} }
@ -2675,6 +2684,7 @@ static const ARMCPRegInfo gicv3_cpuif_hcr_reginfo[] = {
{ .name = "ICH_AP0R0_EL2", .state = ARM_CP_STATE_BOTH, { .name = "ICH_AP0R0_EL2", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 4, .crn = 12, .crm = 8, .opc2 = 0, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 8, .opc2 = 0,
.type = ARM_CP_IO | ARM_CP_NO_RAW, .type = ARM_CP_IO | ARM_CP_NO_RAW,
.nv2_redirect_offset = 0x480,
.access = PL2_RW, .access = PL2_RW,
.readfn = ich_ap_read, .readfn = ich_ap_read,
.writefn = ich_ap_write, .writefn = ich_ap_write,
@ -2682,6 +2692,7 @@ static const ARMCPRegInfo gicv3_cpuif_hcr_reginfo[] = {
{ .name = "ICH_AP1R0_EL2", .state = ARM_CP_STATE_BOTH, { .name = "ICH_AP1R0_EL2", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 4, .crn = 12, .crm = 9, .opc2 = 0, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 9, .opc2 = 0,
.type = ARM_CP_IO | ARM_CP_NO_RAW, .type = ARM_CP_IO | ARM_CP_NO_RAW,
.nv2_redirect_offset = 0x4a0,
.access = PL2_RW, .access = PL2_RW,
.readfn = ich_ap_read, .readfn = ich_ap_read,
.writefn = ich_ap_write, .writefn = ich_ap_write,
@ -2689,6 +2700,7 @@ static const ARMCPRegInfo gicv3_cpuif_hcr_reginfo[] = {
{ .name = "ICH_HCR_EL2", .state = ARM_CP_STATE_BOTH, { .name = "ICH_HCR_EL2", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 4, .crn = 12, .crm = 11, .opc2 = 0, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 11, .opc2 = 0,
.type = ARM_CP_IO | ARM_CP_NO_RAW, .type = ARM_CP_IO | ARM_CP_NO_RAW,
.nv2_redirect_offset = 0x4c0,
.access = PL2_RW, .access = PL2_RW,
.readfn = ich_hcr_read, .readfn = ich_hcr_read,
.writefn = ich_hcr_write, .writefn = ich_hcr_write,
@ -2720,6 +2732,7 @@ static const ARMCPRegInfo gicv3_cpuif_hcr_reginfo[] = {
{ .name = "ICH_VMCR_EL2", .state = ARM_CP_STATE_BOTH, { .name = "ICH_VMCR_EL2", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 4, .crn = 12, .crm = 11, .opc2 = 7, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 11, .opc2 = 7,
.type = ARM_CP_IO | ARM_CP_NO_RAW, .type = ARM_CP_IO | ARM_CP_NO_RAW,
.nv2_redirect_offset = 0x4c8,
.access = PL2_RW, .access = PL2_RW,
.readfn = ich_vmcr_read, .readfn = ich_vmcr_read,
.writefn = ich_vmcr_write, .writefn = ich_vmcr_write,
@ -2730,6 +2743,7 @@ static const ARMCPRegInfo gicv3_cpuif_ich_apxr1_reginfo[] = {
{ .name = "ICH_AP0R1_EL2", .state = ARM_CP_STATE_BOTH, { .name = "ICH_AP0R1_EL2", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 4, .crn = 12, .crm = 8, .opc2 = 1, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 8, .opc2 = 1,
.type = ARM_CP_IO | ARM_CP_NO_RAW, .type = ARM_CP_IO | ARM_CP_NO_RAW,
.nv2_redirect_offset = 0x488,
.access = PL2_RW, .access = PL2_RW,
.readfn = ich_ap_read, .readfn = ich_ap_read,
.writefn = ich_ap_write, .writefn = ich_ap_write,
@ -2737,6 +2751,7 @@ static const ARMCPRegInfo gicv3_cpuif_ich_apxr1_reginfo[] = {
{ .name = "ICH_AP1R1_EL2", .state = ARM_CP_STATE_BOTH, { .name = "ICH_AP1R1_EL2", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 4, .crn = 12, .crm = 9, .opc2 = 1, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 9, .opc2 = 1,
.type = ARM_CP_IO | ARM_CP_NO_RAW, .type = ARM_CP_IO | ARM_CP_NO_RAW,
.nv2_redirect_offset = 0x4a8,
.access = PL2_RW, .access = PL2_RW,
.readfn = ich_ap_read, .readfn = ich_ap_read,
.writefn = ich_ap_write, .writefn = ich_ap_write,
@ -2747,6 +2762,7 @@ static const ARMCPRegInfo gicv3_cpuif_ich_apxr23_reginfo[] = {
{ .name = "ICH_AP0R2_EL2", .state = ARM_CP_STATE_BOTH, { .name = "ICH_AP0R2_EL2", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 4, .crn = 12, .crm = 8, .opc2 = 2, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 8, .opc2 = 2,
.type = ARM_CP_IO | ARM_CP_NO_RAW, .type = ARM_CP_IO | ARM_CP_NO_RAW,
.nv2_redirect_offset = 0x490,
.access = PL2_RW, .access = PL2_RW,
.readfn = ich_ap_read, .readfn = ich_ap_read,
.writefn = ich_ap_write, .writefn = ich_ap_write,
@ -2754,6 +2770,7 @@ static const ARMCPRegInfo gicv3_cpuif_ich_apxr23_reginfo[] = {
{ .name = "ICH_AP0R3_EL2", .state = ARM_CP_STATE_BOTH, { .name = "ICH_AP0R3_EL2", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 4, .crn = 12, .crm = 8, .opc2 = 3, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 8, .opc2 = 3,
.type = ARM_CP_IO | ARM_CP_NO_RAW, .type = ARM_CP_IO | ARM_CP_NO_RAW,
.nv2_redirect_offset = 0x498,
.access = PL2_RW, .access = PL2_RW,
.readfn = ich_ap_read, .readfn = ich_ap_read,
.writefn = ich_ap_write, .writefn = ich_ap_write,
@ -2761,6 +2778,7 @@ static const ARMCPRegInfo gicv3_cpuif_ich_apxr23_reginfo[] = {
{ .name = "ICH_AP1R2_EL2", .state = ARM_CP_STATE_BOTH, { .name = "ICH_AP1R2_EL2", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 4, .crn = 12, .crm = 9, .opc2 = 2, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 9, .opc2 = 2,
.type = ARM_CP_IO | ARM_CP_NO_RAW, .type = ARM_CP_IO | ARM_CP_NO_RAW,
.nv2_redirect_offset = 0x4b0,
.access = PL2_RW, .access = PL2_RW,
.readfn = ich_ap_read, .readfn = ich_ap_read,
.writefn = ich_ap_write, .writefn = ich_ap_write,
@ -2768,6 +2786,7 @@ static const ARMCPRegInfo gicv3_cpuif_ich_apxr23_reginfo[] = {
{ .name = "ICH_AP1R3_EL2", .state = ARM_CP_STATE_BOTH, { .name = "ICH_AP1R3_EL2", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 4, .crn = 12, .crm = 9, .opc2 = 3, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 9, .opc2 = 3,
.type = ARM_CP_IO | ARM_CP_NO_RAW, .type = ARM_CP_IO | ARM_CP_NO_RAW,
.nv2_redirect_offset = 0x4b8,
.access = PL2_RW, .access = PL2_RW,
.readfn = ich_ap_read, .readfn = ich_ap_read,
.writefn = ich_ap_write, .writefn = ich_ap_write,
@ -2889,6 +2908,7 @@ void gicv3_init_cpuif(GICv3State *s)
.opc0 = 3, .opc1 = 4, .crn = 12, .opc0 = 3, .opc1 = 4, .crn = 12,
.crm = 12 + (j >> 3), .opc2 = j & 7, .crm = 12 + (j >> 3), .opc2 = j & 7,
.type = ARM_CP_IO | ARM_CP_NO_RAW, .type = ARM_CP_IO | ARM_CP_NO_RAW,
.nv2_redirect_offset = 0x400 + 8 * j,
.access = PL2_RW, .access = PL2_RW,
.readfn = ich_lr_read, .readfn = ich_lr_read,
.writefn = ich_lr_write, .writefn = ich_lr_write,

View File

@ -2572,6 +2572,11 @@ static const VMStateDescription vmstate_nvic = {
static Property props_nvic[] = { static Property props_nvic[] = {
/* Number of external IRQ lines (so excluding the 16 internal exceptions) */ /* Number of external IRQ lines (so excluding the 16 internal exceptions) */
DEFINE_PROP_UINT32("num-irq", NVICState, num_irq, 64), 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() DEFINE_PROP_END_OF_LIST()
}; };
@ -2685,7 +2690,23 @@ static void armv7m_nvic_realize(DeviceState *dev, Error **errp)
/* include space for internal exception vectors */ /* include space for internal exception vectors */
s->num_irq += NVIC_FIRST_IRQ; 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 * This device provides a single memory region which covers the

View File

@ -43,6 +43,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(ARMv7MState, ARMV7M)
* a qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET). * a qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET).
* + Property "cpu-type": CPU type to instantiate * + Property "cpu-type": CPU type to instantiate
* + Property "num-irq": number of external IRQ lines * + 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 * + Property "memory": MemoryRegion defining the physical address space
* that CPU accesses see. (The NVIC, bitbanding and other CPU-internal * that CPU accesses see. (The NVIC, bitbanding and other CPU-internal
* devices will be automatically layered on top of this view.) * devices will be automatically layered on top of this view.)

View File

@ -0,0 +1,57 @@
/*
* 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

View File

@ -118,6 +118,11 @@ enum {
* ARM pseudocode function CheckSMEAccess(). * ARM pseudocode function CheckSMEAccess().
*/ */
ARM_CP_SME = 1 << 19, 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,
}; };
/* /*
@ -821,6 +826,11 @@ typedef void CPResetFn(CPUARMState *env, const ARMCPRegInfo *opaque);
#define CP_ANY 0xff #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 */ /* Definition of an ARM coprocessor register */
struct ARMCPRegInfo { struct ARMCPRegInfo {
/* Name of register (useful mainly for debugging, need not be unique) */ /* Name of register (useful mainly for debugging, need not be unique) */
@ -862,6 +872,13 @@ struct ARMCPRegInfo {
* value encodes both the trap register and bit within it. * value encodes both the trap register and bit within it.
*/ */
FGTBit fgt; 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 * 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 * this register was defined: can be used to hand data through to the
@ -937,7 +954,7 @@ struct ARMCPRegInfo {
CPResetFn *resetfn; CPResetFn *resetfn;
/* /*
* "Original" writefn and readfn. * "Original" readfn, writefn, accessfn.
* For ARMv8.1-VHE register aliases, we overwrite the read/write * For ARMv8.1-VHE register aliases, we overwrite the read/write
* accessor functions of various EL1/EL0 to perform the runtime * accessor functions of various EL1/EL0 to perform the runtime
* check for which sysreg should actually be modified, and then * check for which sysreg should actually be modified, and then
@ -948,6 +965,7 @@ struct ARMCPRegInfo {
*/ */
CPReadFn *orig_readfn; CPReadFn *orig_readfn;
CPWriteFn *orig_writefn; CPWriteFn *orig_writefn;
CPAccessFn *orig_accessfn;
}; };
/* /*
@ -1079,4 +1097,38 @@ void define_cortex_a72_a57_a53_cp_reginfo(ARMCPU *cpu);
CPAccessResult access_tvm_trvm(CPUARMState *, const ARMCPRegInfo *, bool); 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 */ #endif /* TARGET_ARM_CPREGS_H */

View File

@ -839,6 +839,16 @@ static inline bool isar_feature_aa64_e0pd(const ARMISARegisters *id)
return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, E0PD) != 0; 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_nv2(const ARMISARegisters *id)
{
return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, NV) >= 2;
}
static inline bool isar_feature_aa64_pmuv3p1(const ARMISARegisters *id) static inline bool isar_feature_aa64_pmuv3p1(const ARMISARegisters *id)
{ {
return FIELD_EX64(id->id_aa64dfr0, ID_AA64DFR0, PMUVER) >= 4 && return FIELD_EX64(id->id_aa64dfr0, ID_AA64DFR0, PMUVER) >= 4 &&

View File

@ -1059,6 +1059,7 @@ static void aarch64_cpu_dump_state(CPUState *cs, FILE *f, int flags)
uint32_t psr = pstate_read(env); uint32_t psr = pstate_read(env);
int i, j; int i, j;
int el = arm_current_el(env); int el = arm_current_el(env);
uint64_t hcr = arm_hcr_el2_eff(env);
const char *ns_status; const char *ns_status;
bool sve; bool sve;
@ -1096,6 +1097,10 @@ static void aarch64_cpu_dump_state(CPUState *cs, FILE *f, int flags)
if (cpu_isar_feature(aa64_bti, cpu)) { if (cpu_isar_feature(aa64_bti, cpu)) {
qemu_fprintf(f, " BTYPE=%d", (psr & PSTATE_BTYPE) >> 10); 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)) { if (!(flags & CPU_DUMP_FPU)) {
qemu_fprintf(f, "\n"); qemu_fprintf(f, "\n");
return; return;
@ -2238,9 +2243,6 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
/* FEAT_MPAM (Memory Partitioning and Monitoring Extension) */ /* FEAT_MPAM (Memory Partitioning and Monitoring Extension) */
cpu->isar.id_aa64pfr0 = cpu->isar.id_aa64pfr0 =
FIELD_DP64(cpu->isar.id_aa64pfr0, ID_AA64PFR0, MPAM, 0); 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);
} }
/* MPU can be configured out of a PMSA CPU either by setting has-mpu /* MPU can be configured out of a PMSA CPU either by setting has-mpu

View File

@ -120,12 +120,12 @@ enum {
#define TARGET_INSN_START_EXTRA_WORDS 2 #define TARGET_INSN_START_EXTRA_WORDS 2
/* The 2nd extra word holding syndrome info for data aborts does not use /* 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. * help the sleb128 encoder do a better job.
* When restoring the CPU state, we shift it back up. * When restoring the CPU state, we shift it back up.
*/ */
#define ARM_INSN_START_WORD2_MASK ((1 << 26) - 1) #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 /* We currently assume float and double are IEEE single and double
precision respectively. precision respectively.
@ -547,6 +547,9 @@ typedef struct CPUArchState {
uint64_t gpccr_el3; uint64_t gpccr_el3;
uint64_t gptbr_el3; uint64_t gptbr_el3;
uint64_t mfar_el3; uint64_t mfar_el3;
/* NV2 register */
uint64_t vncr_el2;
} cp15; } cp15;
struct { struct {
@ -3232,17 +3235,26 @@ FIELD(TBFLAG_A64, PSTATE_ZA, 23, 1)
FIELD(TBFLAG_A64, SVL, 24, 4) FIELD(TBFLAG_A64, SVL, 24, 4)
/* Indicates that SME Streaming mode is active, and SMCR_ELx.FA64 is not. */ /* Indicates that SME Streaming mode is active, and SMCR_ELx.FA64 is not. */
FIELD(TBFLAG_A64, SME_TRAP_NONSTREAMING, 28, 1) 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, NAA, 30, 1)
FIELD(TBFLAG_A64, ATA0, 31, 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)
/* 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. * 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) \ #define DP_TBFLAG_ANY(DST, WHICH, VAL) \
(DST.flags = FIELD_DP32(DST.flags, TBFLAG_ANY, WHICH, VAL)) (DST.flags = FIELD_DP32(DST.flags, TBFLAG_ANY, WHICH, VAL))
#define DP_TBFLAG_A64(DST, 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) \ #define DP_TBFLAG_A32(DST, WHICH, VAL) \
(DST.flags2 = FIELD_DP32(DST.flags2, TBFLAG_A32, WHICH, VAL)) (DST.flags2 = FIELD_DP32(DST.flags2, TBFLAG_A32, WHICH, VAL))
#define DP_TBFLAG_M32(DST, WHICH, VAL) \ #define DP_TBFLAG_M32(DST, WHICH, VAL) \
@ -3251,7 +3263,7 @@ FIELD(TBFLAG_A64, ATA0, 31, 1)
(DST.flags2 = FIELD_DP32(DST.flags2, TBFLAG_AM32, WHICH, VAL)) (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_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_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_M32(IN, WHICH) FIELD_EX32(IN.flags2, TBFLAG_M32, WHICH)
#define EX_TBFLAG_AM32(IN, WHICH) FIELD_EX32(IN.flags2, TBFLAG_AM32, WHICH) #define EX_TBFLAG_AM32(IN, WHICH) FIELD_EX32(IN.flags2, TBFLAG_AM32, WHICH)

View File

@ -844,6 +844,16 @@ static CPAccessResult access_tda(CPUARMState *env, const ARMCPRegInfo *ri,
return CP_ACCESS_OK; 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 * Check for traps to Debug Comms Channel registers. If FEAT_FGT
* is implemented then these are controlled by MDCR_EL2.TDCC for * is implemented then these are controlled by MDCR_EL2.TDCC for
@ -950,6 +960,7 @@ static const ARMCPRegInfo debug_cp_reginfo[] = {
.cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 2, .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 2,
.access = PL1_RW, .accessfn = access_tda, .access = PL1_RW, .accessfn = access_tda,
.fgt = FGT_MDSCR_EL1, .fgt = FGT_MDSCR_EL1,
.nv2_redirect_offset = 0x158,
.fieldoffset = offsetof(CPUARMState, cp15.mdscr_el1), .fieldoffset = offsetof(CPUARMState, cp15.mdscr_el1),
.resetvalue = 0 }, .resetvalue = 0 },
/* /*
@ -1062,7 +1073,7 @@ static const ARMCPRegInfo debug_aa32_el1_reginfo[] = {
*/ */
{ .name = "DBGVCR32_EL2", .state = ARM_CP_STATE_AA64, { .name = "DBGVCR32_EL2", .state = ARM_CP_STATE_AA64,
.opc0 = 2, .opc1 = 4, .crn = 0, .crm = 7, .opc2 = 0, .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 }, .type = ARM_CP_NOP | ARM_CP_EL3_NO_EL2_KEEP },
}; };

View File

@ -263,6 +263,18 @@ void init_cpreg_list(ARMCPU *cpu)
g_list_free(keys); g_list_free(keys);
} }
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;
}
}
/* /*
* Some registers are not accessible from AArch32 EL3 if SCR.NS == 0. * Some registers are not accessible from AArch32 EL3 if SCR.NS == 0.
*/ */
@ -635,6 +647,7 @@ static const ARMCPRegInfo cp_reginfo[] = {
.opc0 = 3, .opc1 = 0, .crn = 13, .crm = 0, .opc2 = 1, .opc0 = 3, .opc1 = 0, .crn = 13, .crm = 0, .opc2 = 1,
.access = PL1_RW, .accessfn = access_tvm_trvm, .access = PL1_RW, .accessfn = access_tvm_trvm,
.fgt = FGT_CONTEXTIDR_EL1, .fgt = FGT_CONTEXTIDR_EL1,
.nv2_redirect_offset = 0x108 | NV2_REDIR_NV1,
.secure = ARM_CP_SECSTATE_NS, .secure = ARM_CP_SECSTATE_NS,
.fieldoffset = offsetof(CPUARMState, cp15.contextidr_el[1]), .fieldoffset = offsetof(CPUARMState, cp15.contextidr_el[1]),
.resetvalue = 0, .writefn = contextidr_write, .raw_writefn = raw_write, }, .resetvalue = 0, .writefn = contextidr_write, .raw_writefn = raw_write, },
@ -871,6 +884,7 @@ static const ARMCPRegInfo v6_cp_reginfo[] = {
{ .name = "CPACR", .state = ARM_CP_STATE_BOTH, .opc0 = 3, { .name = "CPACR", .state = ARM_CP_STATE_BOTH, .opc0 = 3,
.crn = 1, .crm = 0, .opc1 = 0, .opc2 = 2, .accessfn = cpacr_access, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 2, .accessfn = cpacr_access,
.fgt = FGT_CPACR_EL1, .fgt = FGT_CPACR_EL1,
.nv2_redirect_offset = 0x100 | NV2_REDIR_NV1,
.access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.cpacr_el1), .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.cpacr_el1),
.resetfn = cpacr_reset, .writefn = cpacr_write, .readfn = cpacr_read }, .resetfn = cpacr_reset, .writefn = cpacr_write, .readfn = cpacr_read },
}; };
@ -2238,11 +2252,13 @@ static const ARMCPRegInfo v7_cp_reginfo[] = {
.opc0 = 3, .opc1 = 0, .crn = 5, .crm = 1, .opc2 = 0, .opc0 = 3, .opc1 = 0, .crn = 5, .crm = 1, .opc2 = 0,
.access = PL1_RW, .accessfn = access_tvm_trvm, .access = PL1_RW, .accessfn = access_tvm_trvm,
.fgt = FGT_AFSR0_EL1, .fgt = FGT_AFSR0_EL1,
.nv2_redirect_offset = 0x128 | NV2_REDIR_NV1,
.type = ARM_CP_CONST, .resetvalue = 0 }, .type = ARM_CP_CONST, .resetvalue = 0 },
{ .name = "AFSR1_EL1", .state = ARM_CP_STATE_BOTH, { .name = "AFSR1_EL1", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 0, .crn = 5, .crm = 1, .opc2 = 1, .opc0 = 3, .opc1 = 0, .crn = 5, .crm = 1, .opc2 = 1,
.access = PL1_RW, .accessfn = access_tvm_trvm, .access = PL1_RW, .accessfn = access_tvm_trvm,
.fgt = FGT_AFSR1_EL1, .fgt = FGT_AFSR1_EL1,
.nv2_redirect_offset = 0x130 | NV2_REDIR_NV1,
.type = ARM_CP_CONST, .resetvalue = 0 }, .type = ARM_CP_CONST, .resetvalue = 0 },
/* /*
* MAIR can just read-as-written because we don't implement caches * MAIR can just read-as-written because we don't implement caches
@ -2252,6 +2268,7 @@ static const ARMCPRegInfo v7_cp_reginfo[] = {
.opc0 = 3, .opc1 = 0, .crn = 10, .crm = 2, .opc2 = 0, .opc0 = 3, .opc1 = 0, .crn = 10, .crm = 2, .opc2 = 0,
.access = PL1_RW, .accessfn = access_tvm_trvm, .access = PL1_RW, .accessfn = access_tvm_trvm,
.fgt = FGT_MAIR_EL1, .fgt = FGT_MAIR_EL1,
.nv2_redirect_offset = 0x140 | NV2_REDIR_NV1,
.fieldoffset = offsetof(CPUARMState, cp15.mair_el[1]), .fieldoffset = offsetof(CPUARMState, cp15.mair_el[1]),
.resetvalue = 0 }, .resetvalue = 0 },
{ .name = "MAIR_EL3", .state = ARM_CP_STATE_AA64, { .name = "MAIR_EL3", .state = ARM_CP_STATE_AA64,
@ -3174,6 +3191,7 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = {
.opc0 = 3, .opc1 = 3, .crn = 14, .crm = 2, .opc2 = 1, .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 2, .opc2 = 1,
.type = ARM_CP_IO, .access = PL0_RW, .type = ARM_CP_IO, .access = PL0_RW,
.accessfn = gt_ptimer_access, .accessfn = gt_ptimer_access,
.nv2_redirect_offset = 0x180 | NV2_REDIR_NV1,
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].ctl), .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].ctl),
.resetvalue = 0, .resetvalue = 0,
.readfn = gt_phys_redir_ctl_read, .raw_readfn = raw_read, .readfn = gt_phys_redir_ctl_read, .raw_readfn = raw_read,
@ -3191,6 +3209,7 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = {
.opc0 = 3, .opc1 = 3, .crn = 14, .crm = 3, .opc2 = 1, .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 3, .opc2 = 1,
.type = ARM_CP_IO, .access = PL0_RW, .type = ARM_CP_IO, .access = PL0_RW,
.accessfn = gt_vtimer_access, .accessfn = gt_vtimer_access,
.nv2_redirect_offset = 0x170 | NV2_REDIR_NV1,
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].ctl), .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].ctl),
.resetvalue = 0, .resetvalue = 0,
.readfn = gt_virt_redir_ctl_read, .raw_readfn = raw_read, .readfn = gt_virt_redir_ctl_read, .raw_readfn = raw_read,
@ -3270,6 +3289,7 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = {
.opc0 = 3, .opc1 = 3, .crn = 14, .crm = 2, .opc2 = 2, .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 2, .opc2 = 2,
.access = PL0_RW, .access = PL0_RW,
.type = ARM_CP_IO, .type = ARM_CP_IO,
.nv2_redirect_offset = 0x178 | NV2_REDIR_NV1,
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].cval), .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].cval),
.resetvalue = 0, .accessfn = gt_ptimer_access, .resetvalue = 0, .accessfn = gt_ptimer_access,
.readfn = gt_phys_redir_cval_read, .raw_readfn = raw_read, .readfn = gt_phys_redir_cval_read, .raw_readfn = raw_read,
@ -3287,6 +3307,7 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = {
.opc0 = 3, .opc1 = 3, .crn = 14, .crm = 3, .opc2 = 2, .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 3, .opc2 = 2,
.access = PL0_RW, .access = PL0_RW,
.type = ARM_CP_IO, .type = ARM_CP_IO,
.nv2_redirect_offset = 0x168 | NV2_REDIR_NV1,
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].cval), .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].cval),
.resetvalue = 0, .accessfn = gt_vtimer_access, .resetvalue = 0, .accessfn = gt_vtimer_access,
.readfn = gt_virt_redir_cval_read, .raw_readfn = raw_read, .readfn = gt_virt_redir_cval_read, .raw_readfn = raw_read,
@ -3324,6 +3345,11 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = {
static CPAccessResult e2h_access(CPUARMState *env, const ARMCPRegInfo *ri, static CPAccessResult e2h_access(CPUARMState *env, const ARMCPRegInfo *ri,
bool isread) 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)) { if (!(arm_hcr_el2_eff(env) & HCR_E2H)) {
return CP_ACCESS_TRAP; return CP_ACCESS_TRAP;
} }
@ -3609,7 +3635,7 @@ static void ats_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
g_assert(ss != ARMSS_Secure); /* ARMv8.4-SecEL2 is 64-bit only */ g_assert(ss != ARMSS_Secure); /* ARMv8.4-SecEL2 is 64-bit only */
/* fall through */ /* fall through */
case 1: 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; mmu_idx = ARMMMUIdx_Stage1_E1_PAN;
} else { } else {
mmu_idx = ARMMMUIdx_Stage1_E1; mmu_idx = ARMMMUIdx_Stage1_E1;
@ -3703,6 +3729,15 @@ static CPAccessResult at_s1e2_access(CPUARMState *env, const ARMCPRegInfo *ri,
return at_e012_access(env, ri, isread); 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, static void ats_write64(CPUARMState *env, const ARMCPRegInfo *ri,
uint64_t value) uint64_t value)
{ {
@ -3716,7 +3751,7 @@ static void ats_write64(CPUARMState *env, const ARMCPRegInfo *ri,
case 0: case 0:
switch (ri->opc1) { switch (ri->opc1) {
case 0: /* AT S1E1R, AT S1E1W, AT S1E1RP, AT S1E1WP */ 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 ? mmu_idx = regime_e20 ?
ARMMMUIdx_E20_2_PAN : ARMMMUIdx_Stage1_E1_PAN; ARMMMUIdx_E20_2_PAN : ARMMMUIdx_Stage1_E1_PAN;
} else { } else {
@ -4252,6 +4287,7 @@ static const ARMCPRegInfo vmsa_pmsa_cp_reginfo[] = {
.opc0 = 3, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 0, .opc0 = 3, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 0,
.access = PL1_RW, .accessfn = access_tvm_trvm, .access = PL1_RW, .accessfn = access_tvm_trvm,
.fgt = FGT_FAR_EL1, .fgt = FGT_FAR_EL1,
.nv2_redirect_offset = 0x220 | NV2_REDIR_NV1,
.fieldoffset = offsetof(CPUARMState, cp15.far_el[1]), .fieldoffset = offsetof(CPUARMState, cp15.far_el[1]),
.resetvalue = 0, }, .resetvalue = 0, },
}; };
@ -4261,11 +4297,13 @@ static const ARMCPRegInfo vmsa_cp_reginfo[] = {
.opc0 = 3, .crn = 5, .crm = 2, .opc1 = 0, .opc2 = 0, .opc0 = 3, .crn = 5, .crm = 2, .opc1 = 0, .opc2 = 0,
.access = PL1_RW, .accessfn = access_tvm_trvm, .access = PL1_RW, .accessfn = access_tvm_trvm,
.fgt = FGT_ESR_EL1, .fgt = FGT_ESR_EL1,
.nv2_redirect_offset = 0x138 | NV2_REDIR_NV1,
.fieldoffset = offsetof(CPUARMState, cp15.esr_el[1]), .resetvalue = 0, }, .fieldoffset = offsetof(CPUARMState, cp15.esr_el[1]), .resetvalue = 0, },
{ .name = "TTBR0_EL1", .state = ARM_CP_STATE_BOTH, { .name = "TTBR0_EL1", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 0, .crn = 2, .crm = 0, .opc2 = 0, .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 0, .opc2 = 0,
.access = PL1_RW, .accessfn = access_tvm_trvm, .access = PL1_RW, .accessfn = access_tvm_trvm,
.fgt = FGT_TTBR0_EL1, .fgt = FGT_TTBR0_EL1,
.nv2_redirect_offset = 0x200 | NV2_REDIR_NV1,
.writefn = vmsa_ttbr_write, .resetvalue = 0, .raw_writefn = raw_write, .writefn = vmsa_ttbr_write, .resetvalue = 0, .raw_writefn = raw_write,
.bank_fieldoffsets = { offsetof(CPUARMState, cp15.ttbr0_s), .bank_fieldoffsets = { offsetof(CPUARMState, cp15.ttbr0_s),
offsetof(CPUARMState, cp15.ttbr0_ns) } }, offsetof(CPUARMState, cp15.ttbr0_ns) } },
@ -4273,6 +4311,7 @@ static const ARMCPRegInfo vmsa_cp_reginfo[] = {
.opc0 = 3, .opc1 = 0, .crn = 2, .crm = 0, .opc2 = 1, .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 0, .opc2 = 1,
.access = PL1_RW, .accessfn = access_tvm_trvm, .access = PL1_RW, .accessfn = access_tvm_trvm,
.fgt = FGT_TTBR1_EL1, .fgt = FGT_TTBR1_EL1,
.nv2_redirect_offset = 0x210 | NV2_REDIR_NV1,
.writefn = vmsa_ttbr_write, .resetvalue = 0, .raw_writefn = raw_write, .writefn = vmsa_ttbr_write, .resetvalue = 0, .raw_writefn = raw_write,
.bank_fieldoffsets = { offsetof(CPUARMState, cp15.ttbr1_s), .bank_fieldoffsets = { offsetof(CPUARMState, cp15.ttbr1_s),
offsetof(CPUARMState, cp15.ttbr1_ns) } }, offsetof(CPUARMState, cp15.ttbr1_ns) } },
@ -4280,6 +4319,7 @@ static const ARMCPRegInfo vmsa_cp_reginfo[] = {
.opc0 = 3, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 2, .opc0 = 3, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 2,
.access = PL1_RW, .accessfn = access_tvm_trvm, .access = PL1_RW, .accessfn = access_tvm_trvm,
.fgt = FGT_TCR_EL1, .fgt = FGT_TCR_EL1,
.nv2_redirect_offset = 0x120 | NV2_REDIR_NV1,
.writefn = vmsa_tcr_el12_write, .writefn = vmsa_tcr_el12_write,
.raw_writefn = raw_write, .raw_writefn = raw_write,
.resetvalue = 0, .resetvalue = 0,
@ -4519,6 +4559,7 @@ static const ARMCPRegInfo lpae_cp_reginfo[] = {
.opc0 = 3, .crn = 10, .crm = 3, .opc1 = 0, .opc2 = 0, .opc0 = 3, .crn = 10, .crm = 3, .opc1 = 0, .opc2 = 0,
.access = PL1_RW, .accessfn = access_tvm_trvm, .access = PL1_RW, .accessfn = access_tvm_trvm,
.fgt = FGT_AMAIR_EL1, .fgt = FGT_AMAIR_EL1,
.nv2_redirect_offset = 0x148 | NV2_REDIR_NV1,
.type = ARM_CP_CONST, .resetvalue = 0 }, .type = ARM_CP_CONST, .resetvalue = 0 },
/* AMAIR1 is mapped to AMAIR_EL1[63:32] */ /* AMAIR1 is mapped to AMAIR_EL1[63:32] */
{ .name = "AMAIR1", .cp = 15, .crn = 10, .crm = 3, .opc1 = 0, .opc2 = 1, { .name = "AMAIR1", .cp = 15, .crn = 10, .crm = 3, .opc1 = 0, .opc2 = 1,
@ -5341,6 +5382,19 @@ 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 #ifdef CONFIG_USER_ONLY
/* /*
* `IC IVAU` is handled to improve compatibility with JITs that dual-map their * `IC IVAU` is handled to improve compatibility with JITs that dual-map their
@ -5568,22 +5622,22 @@ static const ARMCPRegInfo v8_cp_reginfo[] = {
.opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 0, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 0,
.access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
.fgt = FGT_ATS1E1R, .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, { .name = "AT_S1E1W", .state = ARM_CP_STATE_AA64,
.opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 1, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 1,
.access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
.fgt = FGT_ATS1E1W, .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, { .name = "AT_S1E0R", .state = ARM_CP_STATE_AA64,
.opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 2, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 2,
.access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
.fgt = FGT_ATS1E0R, .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, { .name = "AT_S1E0W", .state = ARM_CP_STATE_AA64,
.opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 3, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 3,
.access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
.fgt = FGT_ATS1E0W, .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, { .name = "AT_S12E1R", .state = ARM_CP_STATE_AA64,
.opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 4, .opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 4,
.access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
@ -5689,12 +5743,14 @@ static const ARMCPRegInfo v8_cp_reginfo[] = {
{ .name = "ELR_EL1", .state = ARM_CP_STATE_AA64, { .name = "ELR_EL1", .state = ARM_CP_STATE_AA64,
.type = ARM_CP_ALIAS, .type = ARM_CP_ALIAS,
.opc0 = 3, .opc1 = 0, .crn = 4, .crm = 0, .opc2 = 1, .opc0 = 3, .opc1 = 0, .crn = 4, .crm = 0, .opc2 = 1,
.access = PL1_RW, .access = PL1_RW, .accessfn = access_nv1,
.nv2_redirect_offset = 0x230 | NV2_REDIR_NV1,
.fieldoffset = offsetof(CPUARMState, elr_el[1]) }, .fieldoffset = offsetof(CPUARMState, elr_el[1]) },
{ .name = "SPSR_EL1", .state = ARM_CP_STATE_AA64, { .name = "SPSR_EL1", .state = ARM_CP_STATE_AA64,
.type = ARM_CP_ALIAS, .type = ARM_CP_ALIAS,
.opc0 = 3, .opc1 = 0, .crn = 4, .crm = 0, .opc2 = 0, .opc0 = 3, .opc1 = 0, .crn = 4, .crm = 0, .opc2 = 0,
.access = PL1_RW, .access = PL1_RW, .accessfn = access_nv1,
.nv2_redirect_offset = 0x160 | NV2_REDIR_NV1,
.fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_SVC]) }, .fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_SVC]) },
/* /*
* We rely on the access checks not allowing the guest to write to the * We rely on the access checks not allowing the guest to write to the
@ -5708,6 +5764,7 @@ static const ARMCPRegInfo v8_cp_reginfo[] = {
.fieldoffset = offsetof(CPUARMState, sp_el[0]) }, .fieldoffset = offsetof(CPUARMState, sp_el[0]) },
{ .name = "SP_EL1", .state = ARM_CP_STATE_AA64, { .name = "SP_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 4, .crn = 4, .crm = 1, .opc2 = 0, .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, .access = PL2_RW, .type = ARM_CP_ALIAS | ARM_CP_EL3_NO_EL2_KEEP,
.fieldoffset = offsetof(CPUARMState, sp_el[1]) }, .fieldoffset = offsetof(CPUARMState, sp_el[1]) },
{ .name = "SPSel", .state = ARM_CP_STATE_AA64, { .name = "SPSel", .state = ARM_CP_STATE_AA64,
@ -5815,6 +5872,12 @@ static void do_hcr_write(CPUARMState *env, uint64_t value, uint64_t valid_mask)
if (cpu_isar_feature(aa64_rme, cpu)) { if (cpu_isar_feature(aa64_rme, cpu)) {
valid_mask |= HCR_GPF; valid_mask |= HCR_GPF;
} }
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)) { if (cpu_isar_feature(any_evt, cpu)) {
@ -5833,9 +5896,10 @@ static void do_hcr_write(CPUARMState *env, uint64_t value, uint64_t valid_mask)
* HCR_DC disables stage1 and enables stage2 translation * HCR_DC disables stage1 and enables stage2 translation
* HCR_DCT enables tagging on (disabled) stage1 translation * HCR_DCT enables tagging on (disabled) stage1 translation
* HCR_FWB changes the interpretation of stage2 descriptor bits * 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) & 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)); tlb_flush(CPU(cpu));
} }
env->cp15.hcr_el2 = value; env->cp15.hcr_el2 = value;
@ -6001,7 +6065,7 @@ static void hcrx_write(CPUARMState *env, const ARMCPRegInfo *ri,
static CPAccessResult access_hxen(CPUARMState *env, const ARMCPRegInfo *ri, static CPAccessResult access_hxen(CPUARMState *env, const ARMCPRegInfo *ri,
bool isread) bool isread)
{ {
if (arm_current_el(env) < 3 if (arm_current_el(env) == 2
&& arm_feature(env, ARM_FEATURE_EL3) && arm_feature(env, ARM_FEATURE_EL3)
&& !(env->cp15.scr_el3 & SCR_HXEN)) { && !(env->cp15.scr_el3 & SCR_HXEN)) {
return CP_ACCESS_TRAP_EL3; return CP_ACCESS_TRAP_EL3;
@ -6013,6 +6077,7 @@ static const ARMCPRegInfo hcrx_el2_reginfo = {
.name = "HCRX_EL2", .state = ARM_CP_STATE_AA64, .name = "HCRX_EL2", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 4, .crn = 1, .crm = 2, .opc2 = 2, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 2, .opc2 = 2,
.access = PL2_RW, .writefn = hcrx_write, .accessfn = access_hxen, .access = PL2_RW, .writefn = hcrx_write, .accessfn = access_hxen,
.nv2_redirect_offset = 0xa0,
.fieldoffset = offsetof(CPUARMState, cp15.hcrx_el2), .fieldoffset = offsetof(CPUARMState, cp15.hcrx_el2),
}; };
@ -6079,6 +6144,7 @@ static const ARMCPRegInfo el2_cp_reginfo[] = {
.type = ARM_CP_IO, .type = ARM_CP_IO,
.opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 0, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 0,
.access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.hcr_el2), .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.hcr_el2),
.nv2_redirect_offset = 0x78,
.writefn = hcr_write, .raw_writefn = raw_write }, .writefn = hcr_write, .raw_writefn = raw_write },
{ .name = "HCR", .state = ARM_CP_STATE_AA32, { .name = "HCR", .state = ARM_CP_STATE_AA32,
.type = ARM_CP_ALIAS | ARM_CP_IO, .type = ARM_CP_ALIAS | ARM_CP_IO,
@ -6089,14 +6155,16 @@ static const ARMCPRegInfo el2_cp_reginfo[] = {
.opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 7, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 7,
.access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
{ .name = "ELR_EL2", .state = ARM_CP_STATE_AA64, { .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, .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 0, .opc2 = 1,
.access = PL2_RW, .access = PL2_RW,
.fieldoffset = offsetof(CPUARMState, elr_el[2]) }, .fieldoffset = offsetof(CPUARMState, elr_el[2]) },
{ .name = "ESR_EL2", .state = ARM_CP_STATE_BOTH, { .name = "ESR_EL2", .state = ARM_CP_STATE_BOTH,
.type = ARM_CP_NV2_REDIRECT,
.opc0 = 3, .opc1 = 4, .crn = 5, .crm = 2, .opc2 = 0, .opc0 = 3, .opc1 = 4, .crn = 5, .crm = 2, .opc2 = 0,
.access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.esr_el[2]) }, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.esr_el[2]) },
{ .name = "FAR_EL2", .state = ARM_CP_STATE_BOTH, { .name = "FAR_EL2", .state = ARM_CP_STATE_BOTH,
.type = ARM_CP_NV2_REDIRECT,
.opc0 = 3, .opc1 = 4, .crn = 6, .crm = 0, .opc2 = 0, .opc0 = 3, .opc1 = 4, .crn = 6, .crm = 0, .opc2 = 0,
.access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.far_el[2]) }, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.far_el[2]) },
{ .name = "HIFAR", .state = ARM_CP_STATE_AA32, { .name = "HIFAR", .state = ARM_CP_STATE_AA32,
@ -6105,7 +6173,7 @@ static const ARMCPRegInfo el2_cp_reginfo[] = {
.access = PL2_RW, .access = PL2_RW,
.fieldoffset = offsetofhigh32(CPUARMState, cp15.far_el[2]) }, .fieldoffset = offsetofhigh32(CPUARMState, cp15.far_el[2]) },
{ .name = "SPSR_EL2", .state = ARM_CP_STATE_AA64, { .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, .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 0, .opc2 = 0,
.access = PL2_RW, .access = PL2_RW,
.fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_HYP]) }, .fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_HYP]) },
@ -6161,6 +6229,7 @@ static const ARMCPRegInfo el2_cp_reginfo[] = {
{ .name = "VTCR_EL2", .state = ARM_CP_STATE_AA64, { .name = "VTCR_EL2", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 4, .crn = 2, .crm = 1, .opc2 = 2, .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 1, .opc2 = 2,
.access = PL2_RW, .access = PL2_RW,
.nv2_redirect_offset = 0x40,
/* no .writefn needed as this can't cause an ASID change */ /* no .writefn needed as this can't cause an ASID change */
.fieldoffset = offsetof(CPUARMState, cp15.vtcr_el2) }, .fieldoffset = offsetof(CPUARMState, cp15.vtcr_el2) },
{ .name = "VTTBR", .state = ARM_CP_STATE_AA32, { .name = "VTTBR", .state = ARM_CP_STATE_AA32,
@ -6172,6 +6241,7 @@ static const ARMCPRegInfo el2_cp_reginfo[] = {
{ .name = "VTTBR_EL2", .state = ARM_CP_STATE_AA64, { .name = "VTTBR_EL2", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 4, .crn = 2, .crm = 1, .opc2 = 0, .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 1, .opc2 = 0,
.access = PL2_RW, .writefn = vttbr_write, .raw_writefn = raw_write, .access = PL2_RW, .writefn = vttbr_write, .raw_writefn = raw_write,
.nv2_redirect_offset = 0x20,
.fieldoffset = offsetof(CPUARMState, cp15.vttbr_el2) }, .fieldoffset = offsetof(CPUARMState, cp15.vttbr_el2) },
{ .name = "SCTLR_EL2", .state = ARM_CP_STATE_BOTH, { .name = "SCTLR_EL2", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 4, .crn = 1, .crm = 0, .opc2 = 0, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 0, .opc2 = 0,
@ -6180,6 +6250,7 @@ static const ARMCPRegInfo el2_cp_reginfo[] = {
{ .name = "TPIDR_EL2", .state = ARM_CP_STATE_BOTH, { .name = "TPIDR_EL2", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 4, .crn = 13, .crm = 0, .opc2 = 2, .opc0 = 3, .opc1 = 4, .crn = 13, .crm = 0, .opc2 = 2,
.access = PL2_RW, .resetvalue = 0, .access = PL2_RW, .resetvalue = 0,
.nv2_redirect_offset = 0x90,
.fieldoffset = offsetof(CPUARMState, cp15.tpidr_el[2]) }, .fieldoffset = offsetof(CPUARMState, cp15.tpidr_el[2]) },
{ .name = "TTBR0_EL2", .state = ARM_CP_STATE_AA64, { .name = "TTBR0_EL2", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 4, .crn = 2, .crm = 0, .opc2 = 0, .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 0, .opc2 = 0,
@ -6275,6 +6346,7 @@ static const ARMCPRegInfo el2_cp_reginfo[] = {
.opc0 = 3, .opc1 = 4, .crn = 14, .crm = 0, .opc2 = 3, .opc0 = 3, .opc1 = 4, .crn = 14, .crm = 0, .opc2 = 3,
.access = PL2_RW, .type = ARM_CP_IO, .resetvalue = 0, .access = PL2_RW, .type = ARM_CP_IO, .resetvalue = 0,
.writefn = gt_cntvoff_write, .writefn = gt_cntvoff_write,
.nv2_redirect_offset = 0x60,
.fieldoffset = offsetof(CPUARMState, cp15.cntvoff_el2) }, .fieldoffset = offsetof(CPUARMState, cp15.cntvoff_el2) },
{ .name = "CNTVOFF", .cp = 15, .opc1 = 4, .crm = 14, { .name = "CNTVOFF", .cp = 15, .opc1 = 4, .crm = 14,
.access = PL2_RW, .type = ARM_CP_64BIT | ARM_CP_ALIAS | ARM_CP_IO, .access = PL2_RW, .type = ARM_CP_64BIT | ARM_CP_ALIAS | ARM_CP_IO,
@ -6313,6 +6385,7 @@ static const ARMCPRegInfo el2_cp_reginfo[] = {
{ .name = "HSTR_EL2", .state = ARM_CP_STATE_BOTH, { .name = "HSTR_EL2", .state = ARM_CP_STATE_BOTH,
.cp = 15, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 3, .cp = 15, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 3,
.access = PL2_RW, .access = PL2_RW,
.nv2_redirect_offset = 0x80,
.fieldoffset = offsetof(CPUARMState, cp15.hstr_el2) }, .fieldoffset = offsetof(CPUARMState, cp15.hstr_el2) },
}; };
@ -6338,10 +6411,12 @@ static const ARMCPRegInfo el2_sec_cp_reginfo[] = {
{ .name = "VSTTBR_EL2", .state = ARM_CP_STATE_AA64, { .name = "VSTTBR_EL2", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 4, .crn = 2, .crm = 6, .opc2 = 0, .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 6, .opc2 = 0,
.access = PL2_RW, .accessfn = sel2_access, .access = PL2_RW, .accessfn = sel2_access,
.nv2_redirect_offset = 0x30,
.fieldoffset = offsetof(CPUARMState, cp15.vsttbr_el2) }, .fieldoffset = offsetof(CPUARMState, cp15.vsttbr_el2) },
{ .name = "VSTCR_EL2", .state = ARM_CP_STATE_AA64, { .name = "VSTCR_EL2", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 4, .crn = 2, .crm = 6, .opc2 = 2, .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 6, .opc2 = 2,
.access = PL2_RW, .accessfn = sel2_access, .access = PL2_RW, .accessfn = sel2_access,
.nv2_redirect_offset = 0x48,
.fieldoffset = offsetof(CPUARMState, cp15.vstcr_el2) }, .fieldoffset = offsetof(CPUARMState, cp15.vstcr_el2) },
}; };
@ -6509,6 +6584,42 @@ static void el2_e2h_write(CPUARMState *env, const ARMCPRegInfo *ri,
writefn(env, ri, value); 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 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;
}
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) static void define_arm_vh_e2h_redirects_aliases(ARMCPU *cpu)
{ {
struct E2HAlias { struct E2HAlias {
@ -6608,6 +6719,41 @@ static void define_arm_vh_e2h_redirects_aliases(ARMCPU *cpu)
new_reg->type |= ARM_CP_ALIAS; new_reg->type |= ARM_CP_ALIAS;
/* Remove PL1/PL0 access, leaving PL2/PL3 R/W in place. */ /* Remove PL1/PL0 access, leaving PL2/PL3 R/W in place. */
new_reg->access &= PL2_RW | PL3_RW; 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;
new_reg->orig_accessfn = src_reg->accessfn;
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;
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, ok = g_hash_table_insert(cpu->cp_regs,
(gpointer)(uintptr_t)a->new_key, new_reg); (gpointer)(uintptr_t)a->new_key, new_reg);
@ -6741,9 +6887,11 @@ static const ARMCPRegInfo minimal_ras_reginfo[] = {
.type = ARM_CP_CONST, .resetvalue = 0 }, .type = ARM_CP_CONST, .resetvalue = 0 },
{ .name = "VDISR_EL2", .state = ARM_CP_STATE_BOTH, { .name = "VDISR_EL2", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 4, .crn = 12, .crm = 1, .opc2 = 1, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 1, .opc2 = 1,
.nv2_redirect_offset = 0x500,
.access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.vdisr_el2) }, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.vdisr_el2) },
{ .name = "VSESR_EL2", .state = ARM_CP_STATE_BOTH, { .name = "VSESR_EL2", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 4, .crn = 5, .crm = 2, .opc2 = 3, .opc0 = 3, .opc1 = 4, .crn = 5, .crm = 2, .opc2 = 3,
.nv2_redirect_offset = 0x508,
.access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.vsesr_el2) }, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.vsesr_el2) },
}; };
@ -6915,6 +7063,7 @@ static void zcr_write(CPUARMState *env, const ARMCPRegInfo *ri,
static const ARMCPRegInfo zcr_reginfo[] = { static const ARMCPRegInfo zcr_reginfo[] = {
{ .name = "ZCR_EL1", .state = ARM_CP_STATE_AA64, { .name = "ZCR_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 1, .crm = 2, .opc2 = 0, .opc0 = 3, .opc1 = 0, .crn = 1, .crm = 2, .opc2 = 0,
.nv2_redirect_offset = 0x1e0 | NV2_REDIR_NV1,
.access = PL1_RW, .type = ARM_CP_SVE, .access = PL1_RW, .type = ARM_CP_SVE,
.fieldoffset = offsetof(CPUARMState, vfp.zcr_el[1]), .fieldoffset = offsetof(CPUARMState, vfp.zcr_el[1]),
.writefn = zcr_write, .raw_writefn = raw_write }, .writefn = zcr_write, .raw_writefn = raw_write },
@ -6951,10 +7100,21 @@ static CPAccessResult access_tpidr2(CPUARMState *env, const ARMCPRegInfo *ri,
return CP_ACCESS_OK; return CP_ACCESS_OK;
} }
static CPAccessResult access_esm(CPUARMState *env, const ARMCPRegInfo *ri, static CPAccessResult access_smprimap(CPUARMState *env, const ARMCPRegInfo *ri,
bool isread) 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 if (arm_current_el(env) < 3
&& arm_feature(env, ARM_FEATURE_EL3) && arm_feature(env, ARM_FEATURE_EL3)
&& !FIELD_EX64(env->cp15.cptr_el[3], CPTR_EL3, ESM)) { && !FIELD_EX64(env->cp15.cptr_el[3], CPTR_EL3, ESM)) {
@ -7045,6 +7205,7 @@ static const ARMCPRegInfo sme_reginfo[] = {
.writefn = svcr_write, .raw_writefn = raw_write }, .writefn = svcr_write, .raw_writefn = raw_write },
{ .name = "SMCR_EL1", .state = ARM_CP_STATE_AA64, { .name = "SMCR_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 1, .crm = 2, .opc2 = 6, .opc0 = 3, .opc1 = 0, .crn = 1, .crm = 2, .opc2 = 6,
.nv2_redirect_offset = 0x1f0 | NV2_REDIR_NV1,
.access = PL1_RW, .type = ARM_CP_SME, .access = PL1_RW, .type = ARM_CP_SME,
.fieldoffset = offsetof(CPUARMState, vfp.smcr_el[1]), .fieldoffset = offsetof(CPUARMState, vfp.smcr_el[1]),
.writefn = smcr_write, .raw_writefn = raw_write }, .writefn = smcr_write, .raw_writefn = raw_write },
@ -7073,12 +7234,13 @@ static const ARMCPRegInfo sme_reginfo[] = {
*/ */
{ .name = "SMPRI_EL1", .state = ARM_CP_STATE_AA64, { .name = "SMPRI_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 1, .crm = 2, .opc2 = 4, .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, .fgt = FGT_NSMPRI_EL1,
.type = ARM_CP_CONST, .resetvalue = 0 }, .type = ARM_CP_CONST, .resetvalue = 0 },
{ .name = "SMPRIMAP_EL2", .state = ARM_CP_STATE_AA64, { .name = "SMPRIMAP_EL2", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 4, .crn = 1, .crm = 2, .opc2 = 5, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 2, .opc2 = 5,
.access = PL2_RW, .accessfn = access_esm, .nv2_redirect_offset = 0x1f8,
.access = PL2_RW, .accessfn = access_smprimap,
.type = ARM_CP_CONST, .resetvalue = 0 }, .type = ARM_CP_CONST, .resetvalue = 0 },
}; };
@ -7728,7 +7890,46 @@ static CPAccessResult access_mte(CPUARMState *env, const ARMCPRegInfo *ri,
bool isread) bool isread)
{ {
int el = arm_current_el(env); 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_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)
{
/*
* TFSR_EL2: similar to generic access_mte(), but we need to
* account for FEAT_NV. At EL1 this must be a FEAT_NV access;
* 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 && (arm_hcr_el2_eff(env) & HCR_NV2)) {
return CP_ACCESS_OK;
}
if (el < 2 && arm_is_el2_enabled(env)) { if (el < 2 && arm_is_el2_enabled(env)) {
uint64_t hcr = arm_hcr_el2_eff(env); uint64_t hcr = arm_hcr_el2_eff(env);
if (!(hcr & HCR_ATA) && (!(hcr & HCR_E2H) || !(hcr & HCR_TGE))) { if (!(hcr & HCR_ATA) && (!(hcr & HCR_E2H) || !(hcr & HCR_TGE))) {
@ -7760,11 +7961,13 @@ static const ARMCPRegInfo mte_reginfo[] = {
.fieldoffset = offsetof(CPUARMState, cp15.tfsr_el[0]) }, .fieldoffset = offsetof(CPUARMState, cp15.tfsr_el[0]) },
{ .name = "TFSR_EL1", .state = ARM_CP_STATE_AA64, { .name = "TFSR_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 5, .crm = 6, .opc2 = 0, .opc0 = 3, .opc1 = 0, .crn = 5, .crm = 6, .opc2 = 0,
.access = PL1_RW, .accessfn = access_mte, .access = PL1_RW, .accessfn = access_tfsr_el1,
.nv2_redirect_offset = 0x190 | NV2_REDIR_NV1,
.fieldoffset = offsetof(CPUARMState, cp15.tfsr_el[1]) }, .fieldoffset = offsetof(CPUARMState, cp15.tfsr_el[1]) },
{ .name = "TFSR_EL2", .state = ARM_CP_STATE_AA64, { .name = "TFSR_EL2", .state = ARM_CP_STATE_AA64,
.type = ARM_CP_NV2_REDIRECT,
.opc0 = 3, .opc1 = 4, .crn = 5, .crm = 6, .opc2 = 0, .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]) }, .fieldoffset = offsetof(CPUARMState, cp15.tfsr_el[2]) },
{ .name = "TFSR_EL3", .state = ARM_CP_STATE_AA64, { .name = "TFSR_EL3", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 6, .crn = 5, .crm = 6, .opc2 = 0, .opc0 = 3, .opc1 = 6, .crn = 5, .crm = 6, .opc2 = 0,
@ -7912,6 +8115,18 @@ static CPAccessResult access_scxtnum(CPUARMState *env, const ARMCPRegInfo *ri,
return CP_ACCESS_OK; 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[] = { static const ARMCPRegInfo scxtnum_reginfo[] = {
{ .name = "SCXTNUM_EL0", .state = ARM_CP_STATE_AA64, { .name = "SCXTNUM_EL0", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 3, .crn = 13, .crm = 0, .opc2 = 7, .opc0 = 3, .opc1 = 3, .crn = 13, .crm = 0, .opc2 = 7,
@ -7920,8 +8135,9 @@ static const ARMCPRegInfo scxtnum_reginfo[] = {
.fieldoffset = offsetof(CPUARMState, scxtnum_el[0]) }, .fieldoffset = offsetof(CPUARMState, scxtnum_el[0]) },
{ .name = "SCXTNUM_EL1", .state = ARM_CP_STATE_AA64, { .name = "SCXTNUM_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 13, .crm = 0, .opc2 = 7, .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, .fgt = FGT_SCXTNUM_EL1,
.nv2_redirect_offset = 0x188 | NV2_REDIR_NV1,
.fieldoffset = offsetof(CPUARMState, scxtnum_el[1]) }, .fieldoffset = offsetof(CPUARMState, scxtnum_el[1]) },
{ .name = "SCXTNUM_EL2", .state = ARM_CP_STATE_AA64, { .name = "SCXTNUM_EL2", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 4, .crn = 13, .crm = 0, .opc2 = 7, .opc0 = 3, .opc1 = 4, .crn = 13, .crm = 0, .opc2 = 7,
@ -7946,25 +8162,53 @@ static CPAccessResult access_fgt(CPUARMState *env, const ARMCPRegInfo *ri,
static const ARMCPRegInfo fgt_reginfo[] = { static const ARMCPRegInfo fgt_reginfo[] = {
{ .name = "HFGRTR_EL2", .state = ARM_CP_STATE_AA64, { .name = "HFGRTR_EL2", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 4, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 4,
.nv2_redirect_offset = 0x1b8,
.access = PL2_RW, .accessfn = access_fgt, .access = PL2_RW, .accessfn = access_fgt,
.fieldoffset = offsetof(CPUARMState, cp15.fgt_read[FGTREG_HFGRTR]) }, .fieldoffset = offsetof(CPUARMState, cp15.fgt_read[FGTREG_HFGRTR]) },
{ .name = "HFGWTR_EL2", .state = ARM_CP_STATE_AA64, { .name = "HFGWTR_EL2", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 5, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 5,
.nv2_redirect_offset = 0x1c0,
.access = PL2_RW, .accessfn = access_fgt, .access = PL2_RW, .accessfn = access_fgt,
.fieldoffset = offsetof(CPUARMState, cp15.fgt_write[FGTREG_HFGWTR]) }, .fieldoffset = offsetof(CPUARMState, cp15.fgt_write[FGTREG_HFGWTR]) },
{ .name = "HDFGRTR_EL2", .state = ARM_CP_STATE_AA64, { .name = "HDFGRTR_EL2", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 4, .crn = 3, .crm = 1, .opc2 = 4, .opc0 = 3, .opc1 = 4, .crn = 3, .crm = 1, .opc2 = 4,
.nv2_redirect_offset = 0x1d0,
.access = PL2_RW, .accessfn = access_fgt, .access = PL2_RW, .accessfn = access_fgt,
.fieldoffset = offsetof(CPUARMState, cp15.fgt_read[FGTREG_HDFGRTR]) }, .fieldoffset = offsetof(CPUARMState, cp15.fgt_read[FGTREG_HDFGRTR]) },
{ .name = "HDFGWTR_EL2", .state = ARM_CP_STATE_AA64, { .name = "HDFGWTR_EL2", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 4, .crn = 3, .crm = 1, .opc2 = 5, .opc0 = 3, .opc1 = 4, .crn = 3, .crm = 1, .opc2 = 5,
.nv2_redirect_offset = 0x1d8,
.access = PL2_RW, .accessfn = access_fgt, .access = PL2_RW, .accessfn = access_fgt,
.fieldoffset = offsetof(CPUARMState, cp15.fgt_write[FGTREG_HDFGWTR]) }, .fieldoffset = offsetof(CPUARMState, cp15.fgt_write[FGTREG_HDFGWTR]) },
{ .name = "HFGITR_EL2", .state = ARM_CP_STATE_AA64, { .name = "HFGITR_EL2", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 6, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 6,
.nv2_redirect_offset = 0x1c8,
.access = PL2_RW, .accessfn = access_fgt, .access = PL2_RW, .accessfn = access_fgt,
.fieldoffset = offsetof(CPUARMState, cp15.fgt_exec[FGTREG_HFGITR]) }, .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,
.nv2_redirect_offset = 0xb0,
.fieldoffset = offsetof(CPUARMState, cp15.vncr_el2) },
};
#endif /* TARGET_AARCH64 */ #endif /* TARGET_AARCH64 */
static CPAccessResult access_predinv(CPUARMState *env, const ARMCPRegInfo *ri, static CPAccessResult access_predinv(CPUARMState *env, const ARMCPRegInfo *ri,
@ -8125,12 +8369,14 @@ static const ARMCPRegInfo vhe_reginfo[] = {
.opc0 = 3, .opc1 = 5, .crn = 14, .crm = 2, .opc2 = 1, .opc0 = 3, .opc1 = 5, .crn = 14, .crm = 2, .opc2 = 1,
.type = ARM_CP_IO | ARM_CP_ALIAS, .type = ARM_CP_IO | ARM_CP_ALIAS,
.access = PL2_RW, .accessfn = e2h_access, .access = PL2_RW, .accessfn = e2h_access,
.nv2_redirect_offset = 0x180 | NV2_REDIR_NO_NV1,
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].ctl), .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].ctl),
.writefn = gt_phys_ctl_write, .raw_writefn = raw_write }, .writefn = gt_phys_ctl_write, .raw_writefn = raw_write },
{ .name = "CNTV_CTL_EL02", .state = ARM_CP_STATE_AA64, { .name = "CNTV_CTL_EL02", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 5, .crn = 14, .crm = 3, .opc2 = 1, .opc0 = 3, .opc1 = 5, .crn = 14, .crm = 3, .opc2 = 1,
.type = ARM_CP_IO | ARM_CP_ALIAS, .type = ARM_CP_IO | ARM_CP_ALIAS,
.access = PL2_RW, .accessfn = e2h_access, .access = PL2_RW, .accessfn = e2h_access,
.nv2_redirect_offset = 0x170 | NV2_REDIR_NO_NV1,
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].ctl), .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].ctl),
.writefn = gt_virt_ctl_write, .raw_writefn = raw_write }, .writefn = gt_virt_ctl_write, .raw_writefn = raw_write },
{ .name = "CNTP_TVAL_EL02", .state = ARM_CP_STATE_AA64, { .name = "CNTP_TVAL_EL02", .state = ARM_CP_STATE_AA64,
@ -8147,11 +8393,13 @@ static const ARMCPRegInfo vhe_reginfo[] = {
.opc0 = 3, .opc1 = 5, .crn = 14, .crm = 2, .opc2 = 2, .opc0 = 3, .opc1 = 5, .crn = 14, .crm = 2, .opc2 = 2,
.type = ARM_CP_IO | ARM_CP_ALIAS, .type = ARM_CP_IO | ARM_CP_ALIAS,
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].cval), .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].cval),
.nv2_redirect_offset = 0x178 | NV2_REDIR_NO_NV1,
.access = PL2_RW, .accessfn = e2h_access, .access = PL2_RW, .accessfn = e2h_access,
.writefn = gt_phys_cval_write, .raw_writefn = raw_write }, .writefn = gt_phys_cval_write, .raw_writefn = raw_write },
{ .name = "CNTV_CVAL_EL02", .state = ARM_CP_STATE_AA64, { .name = "CNTV_CVAL_EL02", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 5, .crn = 14, .crm = 3, .opc2 = 2, .opc0 = 3, .opc1 = 5, .crn = 14, .crm = 3, .opc2 = 2,
.type = ARM_CP_IO | ARM_CP_ALIAS, .type = ARM_CP_IO | ARM_CP_ALIAS,
.nv2_redirect_offset = 0x168 | NV2_REDIR_NO_NV1,
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].cval), .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].cval),
.access = PL2_RW, .accessfn = e2h_access, .access = PL2_RW, .accessfn = e2h_access,
.writefn = gt_virt_cval_write, .raw_writefn = raw_write }, .writefn = gt_virt_cval_write, .raw_writefn = raw_write },
@ -8164,12 +8412,12 @@ static const ARMCPRegInfo ats1e1_reginfo[] = {
.opc0 = 1, .opc1 = 0, .crn = 7, .crm = 9, .opc2 = 0, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 9, .opc2 = 0,
.access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
.fgt = FGT_ATS1E1RP, .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, { .name = "AT_S1E1WP", .state = ARM_CP_STATE_AA64,
.opc0 = 1, .opc1 = 0, .crn = 7, .crm = 9, .opc2 = 1, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 9, .opc2 = 1,
.access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
.fgt = FGT_ATS1E1WP, .fgt = FGT_ATS1E1WP,
.accessfn = at_e012_access, .writefn = ats_write64 }, .accessfn = at_s1e01_access, .writefn = ats_write64 },
}; };
static const ARMCPRegInfo ats1cp_reginfo[] = { static const ARMCPRegInfo ats1cp_reginfo[] = {
@ -8793,6 +9041,7 @@ void register_cp_regs_for_features(ARMCPU *cpu)
.opc0 = 3, .opc1 = 4, .crn = 0, .crm = 0, .opc2 = 0, .opc0 = 3, .opc1 = 4, .crn = 0, .crm = 0, .opc2 = 0,
.access = PL2_RW, .resetvalue = cpu->midr, .access = PL2_RW, .resetvalue = cpu->midr,
.type = ARM_CP_EL3_NO_EL2_C_NZ, .type = ARM_CP_EL3_NO_EL2_C_NZ,
.nv2_redirect_offset = 0x88,
.fieldoffset = offsetof(CPUARMState, cp15.vpidr_el2) }, .fieldoffset = offsetof(CPUARMState, cp15.vpidr_el2) },
{ .name = "VMPIDR", .state = ARM_CP_STATE_AA32, { .name = "VMPIDR", .state = ARM_CP_STATE_AA32,
.cp = 15, .opc1 = 4, .crn = 0, .crm = 0, .opc2 = 5, .cp = 15, .opc1 = 4, .crn = 0, .crm = 0, .opc2 = 5,
@ -8804,6 +9053,7 @@ void register_cp_regs_for_features(ARMCPU *cpu)
.opc0 = 3, .opc1 = 4, .crn = 0, .crm = 0, .opc2 = 5, .opc0 = 3, .opc1 = 4, .crn = 0, .crm = 0, .opc2 = 5,
.access = PL2_RW, .resetvalue = vmpidr_def, .access = PL2_RW, .resetvalue = vmpidr_def,
.type = ARM_CP_EL3_NO_EL2_C_NZ, .type = ARM_CP_EL3_NO_EL2_C_NZ,
.nv2_redirect_offset = 0x50,
.fieldoffset = offsetof(CPUARMState, cp15.vmpidr_el2) }, .fieldoffset = offsetof(CPUARMState, cp15.vmpidr_el2) },
}; };
/* /*
@ -9233,6 +9483,7 @@ void register_cp_regs_for_features(ARMCPU *cpu)
{ .name = "ACTLR_EL1", .state = ARM_CP_STATE_BOTH, { .name = "ACTLR_EL1", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 1, .opc0 = 3, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 1,
.access = PL1_RW, .accessfn = access_tacr, .access = PL1_RW, .accessfn = access_tacr,
.nv2_redirect_offset = 0x118,
.type = ARM_CP_CONST, .resetvalue = cpu->reset_auxcr }, .type = ARM_CP_CONST, .resetvalue = cpu->reset_auxcr },
{ .name = "ACTLR_EL2", .state = ARM_CP_STATE_BOTH, { .name = "ACTLR_EL2", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 4, .crn = 1, .crm = 0, .opc2 = 1, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 0, .opc2 = 1,
@ -9302,7 +9553,9 @@ void register_cp_regs_for_features(ARMCPU *cpu)
{ .name = "VBAR", .state = ARM_CP_STATE_BOTH, { .name = "VBAR", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .crn = 12, .crm = 0, .opc1 = 0, .opc2 = 0, .opc0 = 3, .crn = 12, .crm = 0, .opc1 = 0, .opc2 = 0,
.access = PL1_RW, .writefn = vbar_write, .access = PL1_RW, .writefn = vbar_write,
.accessfn = access_nv1,
.fgt = FGT_VBAR_EL1, .fgt = FGT_VBAR_EL1,
.nv2_redirect_offset = 0x250 | NV2_REDIR_NV1,
.bank_fieldoffsets = { offsetof(CPUARMState, cp15.vbar_s), .bank_fieldoffsets = { offsetof(CPUARMState, cp15.vbar_s),
offsetof(CPUARMState, cp15.vbar_ns) }, offsetof(CPUARMState, cp15.vbar_ns) },
.resetvalue = 0 }, .resetvalue = 0 },
@ -9317,6 +9570,7 @@ void register_cp_regs_for_features(ARMCPU *cpu)
.opc0 = 3, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 0, .opc0 = 3, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 0,
.access = PL1_RW, .accessfn = access_tvm_trvm, .access = PL1_RW, .accessfn = access_tvm_trvm,
.fgt = FGT_SCTLR_EL1, .fgt = FGT_SCTLR_EL1,
.nv2_redirect_offset = 0x110 | NV2_REDIR_NV1,
.bank_fieldoffsets = { offsetof(CPUARMState, cp15.sctlr_s), .bank_fieldoffsets = { offsetof(CPUARMState, cp15.sctlr_s),
offsetof(CPUARMState, cp15.sctlr_ns) }, offsetof(CPUARMState, cp15.sctlr_ns) },
.writefn = sctlr_write, .resetvalue = cpu->reset_sctlr, .writefn = sctlr_write, .resetvalue = cpu->reset_sctlr,
@ -9447,6 +9701,10 @@ void register_cp_regs_for_features(ARMCPU *cpu)
define_arm_cp_regs(cpu, rme_mte_reginfo); define_arm_cp_regs(cpu, rme_mte_reginfo);
} }
} }
if (cpu_isar_feature(aa64_nv2, cpu)) {
define_arm_cp_regs(cpu, nv2_reginfo);
}
#endif #endif
if (cpu_isar_feature(any_predinv, cpu)) { if (cpu_isar_feature(any_predinv, cpu)) {
@ -11134,6 +11392,20 @@ static void arm_cpu_do_interrupt_aarch64(CPUState *cs)
old_mode = pstate_read(env); old_mode = pstate_read(env);
aarch64_save_sp(env, arm_current_el(env)); aarch64_save_sp(env, arm_current_el(env));
env->elr_el[new_el] = env->pc; env->elr_el[new_el] = env->pc;
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 { } else {
old_mode = cpsr_read_for_spsr_elx(env); old_mode = cpsr_read_for_spsr_elx(env);
env->elr_el[new_el] = env->regs[15]; env->elr_el[new_el] = env->regs[15];
@ -11144,6 +11416,7 @@ static void arm_cpu_do_interrupt_aarch64(CPUState *cs)
} }
env->banked_spsr[aarch64_banked_spsr_index(new_el)] = old_mode; 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", qemu_log_mask(CPU_LOG_INT, "...with ELR 0x%" PRIx64 "\n",
env->elr_el[new_el]); env->elr_el[new_el]);
@ -11987,15 +12260,6 @@ ARMMMUIdx arm_v7m_mmu_idx_for_secstate(CPUARMState *env, bool secstate)
} }
#endif #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 arm_mmu_idx_el(CPUARMState *env, int el)
{ {
ARMMMUIdx idx; ARMMMUIdx idx;

View File

@ -1581,6 +1581,12 @@ 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 * get_phys_addr_lpae: perform one stage of page table walk, LPAE format
* *
@ -1989,6 +1995,21 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw,
xn = extract64(attrs, 54, 1); xn = extract64(attrs, 54, 1);
pxn = extract64(attrs, 53, 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 * Note that we modified ptw->in_space earlier for NSTable, but
* result->f.attrs retains a copy of the original security space. * result->f.attrs retains a copy of the original security space.

View File

@ -86,6 +86,9 @@ typedef enum {
#define ARM_EL_IL (1 << ARM_EL_IL_SHIFT) #define ARM_EL_IL (1 << ARM_EL_IL_SHIFT)
#define ARM_EL_ISV (1 << ARM_EL_ISV_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) static inline uint32_t syn_get_ec(uint32_t syn)
{ {
return syn >> ARM_EL_EC_SHIFT; return syn >> ARM_EL_EC_SHIFT;
@ -256,13 +259,12 @@ static inline uint32_t syn_bxjtrap(int cv, int cond, int rm)
(cv << 24) | (cond << 20) | 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) 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) return (EC_GPC << ARM_EL_EC_SHIFT) | ARM_EL_IL | (s2ptw << 21)
| (ind << 20) | (gpcsc << 14) | (cm << 8) | (s1ptw << 7) | (ind << 20) | (gpcsc << 14) | (vncr << 13) | (cm << 8)
| (wnr << 6) | fsc; | (s1ptw << 7) | (wnr << 6) | fsc;
} }
static inline uint32_t syn_insn_abort(int same_el, int ea, int s1ptw, int fsc) static inline uint32_t syn_insn_abort(int same_el, int ea, int s1ptw, int fsc)
@ -295,6 +297,16 @@ static inline uint32_t syn_data_abort_with_iss(int same_el,
| (ea << 9) | (cm << 8) | (s1ptw << 7) | (wnr << 6) | fsc; | (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) 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) return (EC_SOFTWARESTEP << ARM_EL_EC_SHIFT) | (same_el << ARM_EL_EC_SHIFT)

View File

@ -1105,6 +1105,16 @@ void aarch64_max_tcg_initfn(Object *obj)
u = FIELD_DP32(u, CLIDR_EL1, LOUU, 0); u = FIELD_DP32(u, CLIDR_EL1, LOUU, 0);
cpu->clidr = u; 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 = cpu->isar.id_aa64isar0;
t = FIELD_DP64(t, ID_AA64ISAR0, AES, 2); /* FEAT_PMULL */ t = FIELD_DP64(t, ID_AA64ISAR0, AES, 2); /* FEAT_PMULL */
t = FIELD_DP64(t, ID_AA64ISAR0, SHA1, 1); /* FEAT_SHA1 */ t = FIELD_DP64(t, ID_AA64ISAR0, SHA1, 1); /* FEAT_SHA1 */
@ -1194,6 +1204,7 @@ void aarch64_max_tcg_initfn(Object *obj)
t = FIELD_DP64(t, ID_AA64MMFR2, UAO, 1); /* FEAT_UAO */ 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, IESB, 1); /* FEAT_IESB */
t = FIELD_DP64(t, ID_AA64MMFR2, VARANGE, 1); /* FEAT_LVA */ t = FIELD_DP64(t, ID_AA64MMFR2, VARANGE, 1); /* FEAT_LVA */
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, ST, 1); /* FEAT_TTST */
t = FIELD_DP64(t, ID_AA64MMFR2, AT, 1); /* FEAT_LSE2 */ t = FIELD_DP64(t, ID_AA64MMFR2, AT, 1); /* FEAT_LSE2 */
t = FIELD_DP64(t, ID_AA64MMFR2, IDS, 1); /* FEAT_IDST */ t = FIELD_DP64(t, ID_AA64MMFR2, IDS, 1); /* FEAT_IDST */

View File

@ -169,6 +169,7 @@ static CPUARMTBFlags rebuild_hflags_a64(CPUARMState *env, int el, int fp_el,
CPUARMTBFlags flags = {}; CPUARMTBFlags flags = {};
ARMMMUIdx stage1 = stage_1_mmu_idx(mmu_idx); ARMMMUIdx stage1 = stage_1_mmu_idx(mmu_idx);
uint64_t tcr = regime_tcr(env, mmu_idx); uint64_t tcr = regime_tcr(env, mmu_idx);
uint64_t hcr = arm_hcr_el2_eff(env);
uint64_t sctlr; uint64_t sctlr;
int tbii, tbid; int tbii, tbid;
@ -260,8 +261,10 @@ static CPUARMTBFlags rebuild_hflags_a64(CPUARMState *env, int el, int fp_el,
switch (mmu_idx) { switch (mmu_idx) {
case ARMMMUIdx_E10_1: case ARMMMUIdx_E10_1:
case ARMMMUIdx_E10_1_PAN: case ARMMMUIdx_E10_1_PAN:
/* TODO: ARMv8.3-NV */ /* FEAT_NV: NV,NV1 == 1,1 means we don't do UNPRIV accesses */
DP_TBFLAG_A64(flags, UNPRIV, 1); if ((hcr & (HCR_NV | HCR_NV1)) != (HCR_NV | HCR_NV1)) {
DP_TBFLAG_A64(flags, UNPRIV, 1);
}
break; break;
case ARMMMUIdx_E20_2: case ARMMMUIdx_E20_2:
case ARMMMUIdx_E20_2_PAN: case ARMMMUIdx_E20_2_PAN:
@ -285,13 +288,34 @@ static CPUARMTBFlags rebuild_hflags_a64(CPUARMState *env, int el, int fp_el,
if (arm_fgt_active(env, el)) { if (arm_fgt_active(env, el)) {
DP_TBFLAG_ANY(flags, FGT_ACTIVE, 1); DP_TBFLAG_ANY(flags, FGT_ACTIVE, 1);
if (FIELD_EX64(env->cp15.fgt_exec[FGTREG_HFGITR], HFGITR_EL2, ERET)) { 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)) { if (fgt_svc(env, el)) {
DP_TBFLAG_ANY(flags, FGT_SVC, 1); 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);
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 (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);
}
}
}
if (cpu_isar_feature(aa64_mte, env_archcpu(env))) { if (cpu_isar_feature(aa64_mte, env_archcpu(env))) {
/* /*
* Set MTE_ACTIVE if any access may be Checked, and leave clear * Set MTE_ACTIVE if any access may be Checked, and leave clear

View File

@ -985,7 +985,14 @@ void HELPER(pre_smc)(CPUARMState *env, uint32_t syndrome)
* *
* Conduit SMC, valid call Trap to EL2 PSCI Call * Conduit SMC, valid call Trap to EL2 PSCI Call
* Conduit SMC, inval call Trap to EL2 Undef insn * 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. /* On ARMv8 with EL3 AArch64, SMD applies to both S and NS state.
@ -999,9 +1006,12 @@ void HELPER(pre_smc)(CPUARMState *env, uint32_t syndrome)
: smd_flag && !secure; : smd_flag && !secure;
if (!arm_feature(env, ARM_FEATURE_EL3) && if (!arm_feature(env, ARM_FEATURE_EL3) &&
!(arm_hcr_el2_eff(env) & HCR_NV) &&
cpu->psci_conduit != QEMU_PSCI_CONDUIT_SMC) { 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 * firmware within QEMU, and we want an EL2 guest to be able
* to forbid its EL1 from making PSCI calls into QEMU's * to forbid its EL1 from making PSCI calls into QEMU's
* "firmware" via HCR.TSC, so for these purposes treat * "firmware" via HCR.TSC, so for these purposes treat

View File

@ -50,7 +50,15 @@ static inline uint32_t merge_syn_data_abort(uint32_t template_syn,
* ST64BV, or ST64BV0 insns report syndrome info even for stage-1 * ST64BV, or ST64BV0 insns report syndrome info even for stage-1
* faults and regardless of the target EL. * 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) { || fi->s1ptw || !fi->stage2) {
syn = syn_data_abort_no_iss(same_el, 0, syn = syn_data_abort_no_iss(same_el, 0,
fi->ea, 0, fi->s1ptw, is_write, fsc); fi->ea, 0, fi->s1ptw, is_write, fsc);
@ -169,6 +177,20 @@ void arm_deliver_fault(ARMCPU *cpu, vaddr addr,
int current_el = arm_current_el(env); int current_el = arm_current_el(env);
bool same_el; bool same_el;
uint32_t syn, exc, fsr, fsc; 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)) { if (report_as_gpc_exception(cpu, current_el, fi)) {
target_el = 3; target_el = 3;
@ -177,7 +199,8 @@ void arm_deliver_fault(ARMCPU *cpu, vaddr addr,
syn = syn_gpc(fi->stage2 && fi->type == ARMFault_GPCFOnWalk, syn = syn_gpc(fi->stage2 && fi->type == ARMFault_GPCFOnWalk,
access_type == MMU_INST_FETCH, 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); access_type == MMU_DATA_STORE, fsc);
env->cp15.mfar_el3 = fi->paddr; env->cp15.mfar_el3 = fi->paddr;

View File

@ -1606,7 +1606,7 @@ static bool trans_ERET(DisasContext *s, arg_ERET *a)
if (s->current_el == 0) { if (s->current_el == 0) {
return false; return false;
} }
if (s->fgt_eret) { if (s->trap_eret) {
gen_exception_insn_el(s, 0, EXCP_UDEF, syn_erettrap(0), 2); gen_exception_insn_el(s, 0, EXCP_UDEF, syn_erettrap(0), 2);
return true; return true;
} }
@ -1633,7 +1633,7 @@ static bool trans_ERETA(DisasContext *s, arg_reta *a)
return false; return false;
} }
/* The FGT trap takes precedence over an auth trap. */ /* 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); gen_exception_insn_el(s, 0, EXCP_UDEF, syn_erettrap(a->m ? 3 : 2), 2);
return true; return true;
} }
@ -2132,16 +2132,19 @@ static void handle_sys(DisasContext *s, bool isread,
crn, crm, op0, op1, op2); crn, crm, op0, op1, op2);
const ARMCPRegInfo *ri = get_arm_cp_reginfo(s->cp_regs, key); const ARMCPRegInfo *ri = get_arm_cp_reginfo(s->cp_regs, key);
bool need_exit_tb = false; bool need_exit_tb = false;
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_ptr tcg_ri = NULL;
TCGv_i64 tcg_rt; 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) { if (crn == 11 || crn == 15) {
/* /*
* Check for TIDCP trap, which must take precedence over * Check for TIDCP trap, which must take precedence over
* the UNDEF for "no such register" etc. * the UNDEF for "no such register" etc.
*/ */
syndrome = syn_aa64_sysregtrap(op0, op1, op2, crn, crm, rt, isread);
switch (s->current_el) { switch (s->current_el) {
case 0: case 0:
if (dc_isar_feature(aa64_tidcp1, s)) { if (dc_isar_feature(aa64_tidcp1, s)) {
@ -2165,17 +2168,65 @@ static void handle_sys(DisasContext *s, bool isread,
return; 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 */ /* Check access permissions */
if (!cp_access_ok(s->current_el, ri, isread)) { 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->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;
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
* 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)) { if (ri->accessfn || (ri->fgt && s->fgt_active)) {
/* Emit code to perform further access permissions checks at /* Emit code to perform further access permissions checks at
* runtime; this may result in an exception. * runtime; this may result in an exception.
*/ */
syndrome = syn_aa64_sysregtrap(op0, op1, op2, crn, crm, rt, isread);
gen_a64_update_pc(s, 0); gen_a64_update_pc(s, 0);
tcg_ri = tcg_temp_new_ptr(); tcg_ri = tcg_temp_new_ptr();
gen_helper_access_check_cp_reg(tcg_ri, tcg_env, gen_helper_access_check_cp_reg(tcg_ri, tcg_env,
@ -2190,6 +2241,78 @@ static void handle_sys(DisasContext *s, bool isread,
gen_a64_update_pc(s, 0); gen_a64_update_pc(s, 0);
} }
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;
}
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));
}
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);
uint32_t syn;
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);
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 {
tcg_gen_qemu_st_i64(tcg_rt, ptr, memidx, mop);
}
return;
}
/* Handle special cases first */ /* Handle special cases first */
switch (ri->type & ARM_CP_SPECIAL_MASK) { switch (ri->type & ARM_CP_SPECIAL_MASK) {
case 0: case 0:
@ -2205,12 +2328,17 @@ static void handle_sys(DisasContext *s, bool isread,
} }
return; return;
case ARM_CP_CURRENTEL: 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. * 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_rt = cpu_reg(s, rt);
tcg_gen_movi_i64(tcg_rt, s->current_el << 2); tcg_gen_movi_i64(tcg_rt, el << 2);
return; return;
}
case ARM_CP_DC_ZVA: case ARM_CP_DC_ZVA:
/* Writes clear the aligned block of memory which rt points into. */ /* Writes clear the aligned block of memory which rt points into. */
if (s->mte_active[0]) { if (s->mte_active[0]) {
@ -2268,13 +2396,6 @@ static void handle_sys(DisasContext *s, bool isread,
default: default:
g_assert_not_reached(); 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) { if (ri->type & ARM_CP_IO) {
/* I/O operations must end the TB here (whether read or write) */ /* I/O operations must end the TB here (whether read or write) */
@ -13980,7 +14101,7 @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase,
dc->pstate_il = EX_TBFLAG_ANY(tb_flags, PSTATE__IL); dc->pstate_il = EX_TBFLAG_ANY(tb_flags, PSTATE__IL);
dc->fgt_active = EX_TBFLAG_ANY(tb_flags, FGT_ACTIVE); dc->fgt_active = EX_TBFLAG_ANY(tb_flags, FGT_ACTIVE);
dc->fgt_svc = EX_TBFLAG_ANY(tb_flags, FGT_SVC); 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->sve_excp_el = EX_TBFLAG_A64(tb_flags, SVEEXC_EL);
dc->sme_excp_el = EX_TBFLAG_A64(tb_flags, SMEEXC_EL); dc->sme_excp_el = EX_TBFLAG_A64(tb_flags, SMEEXC_EL);
dc->vl = (EX_TBFLAG_A64(tb_flags, VL) + 1) * 16; dc->vl = (EX_TBFLAG_A64(tb_flags, VL) + 1) * 16;
@ -13997,6 +14118,11 @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase,
dc->pstate_za = EX_TBFLAG_A64(tb_flags, PSTATE_ZA); dc->pstate_za = EX_TBFLAG_A64(tb_flags, PSTATE_ZA);
dc->sme_trap_nonstreaming = EX_TBFLAG_A64(tb_flags, SME_TRAP_NONSTREAMING); dc->sme_trap_nonstreaming = EX_TBFLAG_A64(tb_flags, SME_TRAP_NONSTREAMING);
dc->naa = EX_TBFLAG_A64(tb_flags, NAA); 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->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_len = 0;
dc->vec_stride = 0; dc->vec_stride = 0;
dc->cp_regs = arm_cpu->cp_regs; dc->cp_regs = arm_cpu->cp_regs;

View File

@ -138,12 +138,22 @@ typedef struct DisasContext {
bool mve_no_pred; bool mve_no_pred;
/* True if fine-grained traps are active */ /* True if fine-grained traps are active */
bool fgt_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 */ /* True if fine-grained trap on SVC is enabled */
bool fgt_svc; 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 */ /* True if FEAT_LSE2 SCTLR_ELx.nAA is set */
bool naa; 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;
/* 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, a copy of PSTATE.BTYPE, which will be 0 without v8.5-BTI.
* < 0, set by the current instruction. * < 0, set by the current instruction.
@ -159,6 +169,8 @@ typedef struct DisasContext {
int c15_cpar; int c15_cpar;
/* TCG op of the current insn_start. */ /* TCG op of the current insn_start. */
TCGOp *insn_start; TCGOp *insn_start;
/* Offset from VNCR_EL2 when FEAT_NV2 redirects this reg to memory */
uint32_t nv2_redirect_offset;
} DisasContext; } DisasContext;
typedef struct DisasCompare { typedef struct DisasCompare {