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
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
M: Subbaraya Sundeep <sundeep.lkml@gmail.com>
M: Peter Maydell <peter.maydell@linaro.org>

View file

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

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_MTE2 (Memory Tagging Extension)
- FEAT_MTE3 (MTE Asymmetric Fault Handling)
- FEAT_NV (Nested Virtualization)
- FEAT_NV2 (Enhanced nested virtualization support)
- FEAT_PACIMP (Pointer authentication - IMPLEMENTATION DEFINED algorithm)
- FEAT_PACQARMA3 (Pointer authentication - QARMA3 algorithm)
- FEAT_PACQARMA5 (Pointer authentication - QARMA5 algorithm)

View file

@ -16,11 +16,13 @@ based on this chip :
- ``netduino2`` Netduino 2 board with STM32F205RFT6 microcontroller
The STM32F4 series is based on ARM Cortex-M4F core. This series is pin-to-pin
compatible with STM32F2 series. The following machines are based on this chip :
The STM32F4 series is based on ARM Cortex-M4F core, as well as the STM32L4
ultra-low-power series. The STM32F4 series is pin-to-pin compatible with STM32F2 series.
The following machines are based on this ARM Cortex-M4F chip :
- ``netduinoplus2`` Netduino Plus 2 board with STM32F405RGT6 microcontroller
- ``olimex-stm32-h405`` Olimex STM32 H405 board with STM32F405RGT6 microcontroller
- ``b-l475e-iot01a`` :doc:`B-L475E-IOT01A IoT Node </system/arm/b-l475e-iot01a>` board with STM32L475VG microcontroller
There are many other STM32 series that are currently not supported by QEMU.

View file

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

View file

@ -449,6 +449,17 @@ config STM32F405_SOC
select STM32F4XX_SYSCFG
select STM32F4XX_EXTI
config B_L475E_IOT01A
bool
default y
depends on TCG && ARM
select STM32L4X5_SOC
config STM32L4X5_SOC
bool
select ARM_V7M
select OR_IRQ
config XLNX_ZYNQMP_ARM
bool
default y if PIXMAN
@ -537,6 +548,7 @@ config FSL_IMX6
select IMX_I2C
select IMX_USBPHY
select WDT_IMX2
select PL310 # cache controller
select SDHCI
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_property_add_alias(obj, "num-irq",
OBJECT(&s->nvic), "num-irq");
object_property_add_alias(obj, "num-prio-bits",
OBJECT(&s->nvic), "num-prio-bits");
object_initialize_child(obj, "systick-reg-ns", &s->systick[M_REG_NS],
TYPE_SYSTICK);

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));
}
/* L2 cache controller */
sysbus_create_simple("l2x0", FSL_IMX6_PL310_ADDR, NULL);
if (!sysbus_realize(SYS_BUS_DEVICE(&s->ccm), errp)) {
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_STM32F205_SOC', if_true: files('stm32f205_soc.c'))
arm_ss.add(when: 'CONFIG_STM32F405_SOC', if_true: files('stm32f405_soc.c'))
arm_ss.add(when: 'CONFIG_B_L475E_IOT01A', if_true: files('b-l475e-iot01a.c'))
arm_ss.add(when: 'CONFIG_STM32L4X5_SOC', if_true: files('stm32l4x5_soc.c'))
arm_ss.add(when: 'CONFIG_XLNX_ZYNQMP_ARM', if_true: files('xlnx-zynqmp.c', 'xlnx-zcu102.c'))
arm_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal.c', 'xlnx-versal-virt.c'))
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);
dev = qdev_new(TYPE_MSF2_SOC);
object_property_add_child(OBJECT(machine), "soc", OBJECT(dev));
qdev_prop_set_string(dev, "part-name", "M2S010");
qdev_prop_set_string(dev, "cpu-type", mc->default_cpu_type);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -127,6 +127,7 @@ static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp)
armv7m = DEVICE(&s->armv7m);
qdev_prop_set_uint32(armv7m, "num-irq", 96);
qdev_prop_set_uint8(armv7m, "num-prio-bits", 4);
qdev_prop_set_string(armv7m, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m3"));
qdev_prop_set_bit(armv7m, "enable-bitband", true);
qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk);

View file

@ -149,6 +149,7 @@ static void stm32f405_soc_realize(DeviceState *dev_soc, Error **errp)
armv7m = DEVICE(&s->armv7m);
qdev_prop_set_uint32(armv7m, "num-irq", 96);
qdev_prop_set_uint8(armv7m, "num-prio-bits", 4);
qdev_prop_set_string(armv7m, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m4"));
qdev_prop_set_bit(armv7m, "enable-bitband", true);
qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk);

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);
dev = qdev_new(TYPE_STM32F100_SOC);
object_property_add_child(OBJECT(machine), "soc", OBJECT(dev));
qdev_connect_clock_in(dev, "sysclk", sysclk);
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);

View file

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

View file

@ -2572,6 +2572,11 @@ static const VMStateDescription vmstate_nvic = {
static Property props_nvic[] = {
/* Number of external IRQ lines (so excluding the 16 internal exceptions) */
DEFINE_PROP_UINT32("num-irq", NVICState, num_irq, 64),
/*
* Number of the maximum priority bits that can be used. 0 means
* to use a reasonable default.
*/
DEFINE_PROP_UINT8("num-prio-bits", NVICState, num_prio_bits, 0),
DEFINE_PROP_END_OF_LIST()
};
@ -2685,7 +2690,23 @@ static void armv7m_nvic_realize(DeviceState *dev, Error **errp)
/* include space for internal exception vectors */
s->num_irq += NVIC_FIRST_IRQ;
s->num_prio_bits = arm_feature(&s->cpu->env, ARM_FEATURE_V7) ? 8 : 2;
if (s->num_prio_bits == 0) {
/*
* If left unspecified, use 2 bits by default on Cortex-M0/M0+/M1
* and 8 bits otherwise.
*/
s->num_prio_bits = arm_feature(&s->cpu->env, ARM_FEATURE_V7) ? 8 : 2;
} else {
uint8_t min_prio_bits =
arm_feature(&s->cpu->env, ARM_FEATURE_V7) ? 3 : 2;
if (s->num_prio_bits < min_prio_bits || s->num_prio_bits > 8) {
error_setg(errp,
"num-prio-bits %d is outside "
"NVIC acceptable range [%d-8]",
s->num_prio_bits, min_prio_bits);
return;
}
}
/*
* This device provides a single memory region which covers the

View file

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

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_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
/* Flags in the high bits of nv2_redirect_offset */
#define NV2_REDIR_NV1 0x4000 /* Only redirect when HCR_EL2.NV1 == 1 */
#define NV2_REDIR_NO_NV1 0x8000 /* Only redirect when HCR_EL2.NV1 == 0 */
#define NV2_REDIR_FLAG_MASK 0xc000
/* Definition of an ARM coprocessor register */
struct ARMCPRegInfo {
/* Name of register (useful mainly for debugging, need not be unique) */
@ -862,6 +872,13 @@ struct ARMCPRegInfo {
* value encodes both the trap register and bit within it.
*/
FGTBit fgt;
/*
* Offset from VNCR_EL2 when FEAT_NV2 redirects access to memory;
* may include an NV2_REDIR_* flag.
*/
uint32_t nv2_redirect_offset;
/*
* The opaque pointer passed to define_arm_cp_regs_with_opaque() when
* this register was defined: can be used to hand data through to the
@ -937,7 +954,7 @@ struct ARMCPRegInfo {
CPResetFn *resetfn;
/*
* "Original" writefn and readfn.
* "Original" readfn, writefn, accessfn.
* For ARMv8.1-VHE register aliases, we overwrite the read/write
* accessor functions of various EL1/EL0 to perform the runtime
* check for which sysreg should actually be modified, and then
@ -948,6 +965,7 @@ struct ARMCPRegInfo {
*/
CPReadFn *orig_readfn;
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);
/**
* 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 */

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;
}
static inline bool isar_feature_aa64_nv(const ARMISARegisters *id)
{
return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, NV) != 0;
}
static inline bool isar_feature_aa64_nv2(const ARMISARegisters *id)
{
return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, NV) >= 2;
}
static inline bool isar_feature_aa64_pmuv3p1(const ARMISARegisters *id)
{
return FIELD_EX64(id->id_aa64dfr0, ID_AA64DFR0, PMUVER) >= 4 &&

View file

@ -1059,6 +1059,7 @@ static void aarch64_cpu_dump_state(CPUState *cs, FILE *f, int flags)
uint32_t psr = pstate_read(env);
int i, j;
int el = arm_current_el(env);
uint64_t hcr = arm_hcr_el2_eff(env);
const char *ns_status;
bool sve;
@ -1096,6 +1097,10 @@ static void aarch64_cpu_dump_state(CPUState *cs, FILE *f, int flags)
if (cpu_isar_feature(aa64_bti, cpu)) {
qemu_fprintf(f, " BTYPE=%d", (psr & PSTATE_BTYPE) >> 10);
}
qemu_fprintf(f, "%s%s%s",
(hcr & HCR_NV) ? " NV" : "",
(hcr & HCR_NV1) ? " NV1" : "",
(hcr & HCR_NV2) ? " NV2" : "");
if (!(flags & CPU_DUMP_FPU)) {
qemu_fprintf(f, "\n");
return;
@ -2238,9 +2243,6 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
/* FEAT_MPAM (Memory Partitioning and Monitoring Extension) */
cpu->isar.id_aa64pfr0 =
FIELD_DP64(cpu->isar.id_aa64pfr0, ID_AA64PFR0, MPAM, 0);
/* FEAT_NV (Nested Virtualization) */
cpu->isar.id_aa64mmfr2 =
FIELD_DP64(cpu->isar.id_aa64mmfr2, ID_AA64MMFR2, NV, 0);
}
/* 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
/* The 2nd extra word holding syndrome info for data aborts does not use
* the upper 6 bits nor the lower 14 bits. We mask and shift it down to
* the upper 6 bits nor the lower 13 bits. We mask and shift it down to
* help the sleb128 encoder do a better job.
* When restoring the CPU state, we shift it back up.
*/
#define ARM_INSN_START_WORD2_MASK ((1 << 26) - 1)
#define ARM_INSN_START_WORD2_SHIFT 14
#define ARM_INSN_START_WORD2_SHIFT 13
/* We currently assume float and double are IEEE single and double
precision respectively.
@ -547,6 +547,9 @@ typedef struct CPUArchState {
uint64_t gpccr_el3;
uint64_t gptbr_el3;
uint64_t mfar_el3;
/* NV2 register */
uint64_t vncr_el2;
} cp15;
struct {
@ -3232,17 +3235,26 @@ FIELD(TBFLAG_A64, PSTATE_ZA, 23, 1)
FIELD(TBFLAG_A64, SVL, 24, 4)
/* Indicates that SME Streaming mode is active, and SMCR_ELx.FA64 is not. */
FIELD(TBFLAG_A64, SME_TRAP_NONSTREAMING, 28, 1)
FIELD(TBFLAG_A64, FGT_ERET, 29, 1)
FIELD(TBFLAG_A64, TRAP_ERET, 29, 1)
FIELD(TBFLAG_A64, NAA, 30, 1)
FIELD(TBFLAG_A64, ATA0, 31, 1)
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) \
(DST.flags = FIELD_DP32(DST.flags, TBFLAG_ANY, WHICH, VAL))
#define DP_TBFLAG_A64(DST, WHICH, VAL) \
(DST.flags2 = FIELD_DP32(DST.flags2, TBFLAG_A64, WHICH, VAL))
(DST.flags2 = FIELD_DP64(DST.flags2, TBFLAG_A64, WHICH, VAL))
#define DP_TBFLAG_A32(DST, WHICH, VAL) \
(DST.flags2 = FIELD_DP32(DST.flags2, TBFLAG_A32, WHICH, VAL))
#define DP_TBFLAG_M32(DST, WHICH, VAL) \
@ -3251,7 +3263,7 @@ FIELD(TBFLAG_A64, ATA0, 31, 1)
(DST.flags2 = FIELD_DP32(DST.flags2, TBFLAG_AM32, WHICH, VAL))
#define EX_TBFLAG_ANY(IN, WHICH) FIELD_EX32(IN.flags, TBFLAG_ANY, WHICH)
#define EX_TBFLAG_A64(IN, WHICH) FIELD_EX32(IN.flags2, TBFLAG_A64, WHICH)
#define EX_TBFLAG_A64(IN, WHICH) FIELD_EX64(IN.flags2, TBFLAG_A64, WHICH)
#define EX_TBFLAG_A32(IN, WHICH) FIELD_EX32(IN.flags2, TBFLAG_A32, WHICH)
#define EX_TBFLAG_M32(IN, WHICH) FIELD_EX32(IN.flags2, TBFLAG_M32, WHICH)
#define EX_TBFLAG_AM32(IN, WHICH) FIELD_EX32(IN.flags2, TBFLAG_AM32, WHICH)

View file

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

View file

@ -263,6 +263,18 @@ void init_cpreg_list(ARMCPU *cpu)
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.
*/
@ -635,6 +647,7 @@ static const ARMCPRegInfo cp_reginfo[] = {
.opc0 = 3, .opc1 = 0, .crn = 13, .crm = 0, .opc2 = 1,
.access = PL1_RW, .accessfn = access_tvm_trvm,
.fgt = FGT_CONTEXTIDR_EL1,
.nv2_redirect_offset = 0x108 | NV2_REDIR_NV1,
.secure = ARM_CP_SECSTATE_NS,
.fieldoffset = offsetof(CPUARMState, cp15.contextidr_el[1]),
.resetvalue = 0, .writefn = contextidr_write, .raw_writefn = raw_write, },
@ -871,6 +884,7 @@ static const ARMCPRegInfo v6_cp_reginfo[] = {
{ .name = "CPACR", .state = ARM_CP_STATE_BOTH, .opc0 = 3,
.crn = 1, .crm = 0, .opc1 = 0, .opc2 = 2, .accessfn = cpacr_access,
.fgt = FGT_CPACR_EL1,
.nv2_redirect_offset = 0x100 | NV2_REDIR_NV1,
.access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.cpacr_el1),
.resetfn = cpacr_reset, .writefn = cpacr_write, .readfn = cpacr_read },
};
@ -2238,11 +2252,13 @@ static const ARMCPRegInfo v7_cp_reginfo[] = {
.opc0 = 3, .opc1 = 0, .crn = 5, .crm = 1, .opc2 = 0,
.access = PL1_RW, .accessfn = access_tvm_trvm,
.fgt = FGT_AFSR0_EL1,
.nv2_redirect_offset = 0x128 | NV2_REDIR_NV1,
.type = ARM_CP_CONST, .resetvalue = 0 },
{ .name = "AFSR1_EL1", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 0, .crn = 5, .crm = 1, .opc2 = 1,
.access = PL1_RW, .accessfn = access_tvm_trvm,
.fgt = FGT_AFSR1_EL1,
.nv2_redirect_offset = 0x130 | NV2_REDIR_NV1,
.type = ARM_CP_CONST, .resetvalue = 0 },
/*
* MAIR can just read-as-written because we don't implement caches
@ -2252,6 +2268,7 @@ static const ARMCPRegInfo v7_cp_reginfo[] = {
.opc0 = 3, .opc1 = 0, .crn = 10, .crm = 2, .opc2 = 0,
.access = PL1_RW, .accessfn = access_tvm_trvm,
.fgt = FGT_MAIR_EL1,
.nv2_redirect_offset = 0x140 | NV2_REDIR_NV1,
.fieldoffset = offsetof(CPUARMState, cp15.mair_el[1]),
.resetvalue = 0 },
{ .name = "MAIR_EL3", .state = ARM_CP_STATE_AA64,
@ -3174,6 +3191,7 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = {
.opc0 = 3, .opc1 = 3, .crn = 14, .crm = 2, .opc2 = 1,
.type = ARM_CP_IO, .access = PL0_RW,
.accessfn = gt_ptimer_access,
.nv2_redirect_offset = 0x180 | NV2_REDIR_NV1,
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].ctl),
.resetvalue = 0,
.readfn = gt_phys_redir_ctl_read, .raw_readfn = raw_read,
@ -3191,6 +3209,7 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = {
.opc0 = 3, .opc1 = 3, .crn = 14, .crm = 3, .opc2 = 1,
.type = ARM_CP_IO, .access = PL0_RW,
.accessfn = gt_vtimer_access,
.nv2_redirect_offset = 0x170 | NV2_REDIR_NV1,
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].ctl),
.resetvalue = 0,
.readfn = gt_virt_redir_ctl_read, .raw_readfn = raw_read,
@ -3270,6 +3289,7 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = {
.opc0 = 3, .opc1 = 3, .crn = 14, .crm = 2, .opc2 = 2,
.access = PL0_RW,
.type = ARM_CP_IO,
.nv2_redirect_offset = 0x178 | NV2_REDIR_NV1,
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].cval),
.resetvalue = 0, .accessfn = gt_ptimer_access,
.readfn = gt_phys_redir_cval_read, .raw_readfn = raw_read,
@ -3287,6 +3307,7 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = {
.opc0 = 3, .opc1 = 3, .crn = 14, .crm = 3, .opc2 = 2,
.access = PL0_RW,
.type = ARM_CP_IO,
.nv2_redirect_offset = 0x168 | NV2_REDIR_NV1,
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].cval),
.resetvalue = 0, .accessfn = gt_vtimer_access,
.readfn = gt_virt_redir_cval_read, .raw_readfn = raw_read,
@ -3324,6 +3345,11 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = {
static CPAccessResult e2h_access(CPUARMState *env, const ARMCPRegInfo *ri,
bool isread)
{
if (arm_current_el(env) == 1) {
/* This must be a FEAT_NV access */
/* TODO: FEAT_ECV will need to check CNTHCTL_EL2 here */
return CP_ACCESS_OK;
}
if (!(arm_hcr_el2_eff(env) & HCR_E2H)) {
return CP_ACCESS_TRAP;
}
@ -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 */
/* fall through */
case 1:
if (ri->crm == 9 && (env->uncached_cpsr & CPSR_PAN)) {
if (ri->crm == 9 && arm_pan_enabled(env)) {
mmu_idx = ARMMMUIdx_Stage1_E1_PAN;
} else {
mmu_idx = ARMMMUIdx_Stage1_E1;
@ -3703,6 +3729,15 @@ static CPAccessResult at_s1e2_access(CPUARMState *env, const ARMCPRegInfo *ri,
return at_e012_access(env, ri, isread);
}
static CPAccessResult at_s1e01_access(CPUARMState *env, const ARMCPRegInfo *ri,
bool isread)
{
if (arm_current_el(env) == 1 && (arm_hcr_el2_eff(env) & HCR_AT)) {
return CP_ACCESS_TRAP_EL2;
}
return at_e012_access(env, ri, isread);
}
static void ats_write64(CPUARMState *env, const ARMCPRegInfo *ri,
uint64_t value)
{
@ -3716,7 +3751,7 @@ static void ats_write64(CPUARMState *env, const ARMCPRegInfo *ri,
case 0:
switch (ri->opc1) {
case 0: /* AT S1E1R, AT S1E1W, AT S1E1RP, AT S1E1WP */
if (ri->crm == 9 && (env->pstate & PSTATE_PAN)) {
if (ri->crm == 9 && arm_pan_enabled(env)) {
mmu_idx = regime_e20 ?
ARMMMUIdx_E20_2_PAN : ARMMMUIdx_Stage1_E1_PAN;
} else {
@ -4252,6 +4287,7 @@ static const ARMCPRegInfo vmsa_pmsa_cp_reginfo[] = {
.opc0 = 3, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 0,
.access = PL1_RW, .accessfn = access_tvm_trvm,
.fgt = FGT_FAR_EL1,
.nv2_redirect_offset = 0x220 | NV2_REDIR_NV1,
.fieldoffset = offsetof(CPUARMState, cp15.far_el[1]),
.resetvalue = 0, },
};
@ -4261,11 +4297,13 @@ static const ARMCPRegInfo vmsa_cp_reginfo[] = {
.opc0 = 3, .crn = 5, .crm = 2, .opc1 = 0, .opc2 = 0,
.access = PL1_RW, .accessfn = access_tvm_trvm,
.fgt = FGT_ESR_EL1,
.nv2_redirect_offset = 0x138 | NV2_REDIR_NV1,
.fieldoffset = offsetof(CPUARMState, cp15.esr_el[1]), .resetvalue = 0, },
{ .name = "TTBR0_EL1", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 0, .crn = 2, .crm = 0, .opc2 = 0,
.access = PL1_RW, .accessfn = access_tvm_trvm,
.fgt = FGT_TTBR0_EL1,
.nv2_redirect_offset = 0x200 | NV2_REDIR_NV1,
.writefn = vmsa_ttbr_write, .resetvalue = 0, .raw_writefn = raw_write,
.bank_fieldoffsets = { offsetof(CPUARMState, cp15.ttbr0_s),
offsetof(CPUARMState, cp15.ttbr0_ns) } },
@ -4273,6 +4311,7 @@ static const ARMCPRegInfo vmsa_cp_reginfo[] = {
.opc0 = 3, .opc1 = 0, .crn = 2, .crm = 0, .opc2 = 1,
.access = PL1_RW, .accessfn = access_tvm_trvm,
.fgt = FGT_TTBR1_EL1,
.nv2_redirect_offset = 0x210 | NV2_REDIR_NV1,
.writefn = vmsa_ttbr_write, .resetvalue = 0, .raw_writefn = raw_write,
.bank_fieldoffsets = { offsetof(CPUARMState, cp15.ttbr1_s),
offsetof(CPUARMState, cp15.ttbr1_ns) } },
@ -4280,6 +4319,7 @@ static const ARMCPRegInfo vmsa_cp_reginfo[] = {
.opc0 = 3, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 2,
.access = PL1_RW, .accessfn = access_tvm_trvm,
.fgt = FGT_TCR_EL1,
.nv2_redirect_offset = 0x120 | NV2_REDIR_NV1,
.writefn = vmsa_tcr_el12_write,
.raw_writefn = raw_write,
.resetvalue = 0,
@ -4519,6 +4559,7 @@ static const ARMCPRegInfo lpae_cp_reginfo[] = {
.opc0 = 3, .crn = 10, .crm = 3, .opc1 = 0, .opc2 = 0,
.access = PL1_RW, .accessfn = access_tvm_trvm,
.fgt = FGT_AMAIR_EL1,
.nv2_redirect_offset = 0x148 | NV2_REDIR_NV1,
.type = ARM_CP_CONST, .resetvalue = 0 },
/* AMAIR1 is mapped to AMAIR_EL1[63:32] */
{ .name = "AMAIR1", .cp = 15, .crn = 10, .crm = 3, .opc1 = 0, .opc2 = 1,
@ -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
/*
* `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,
.access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
.fgt = FGT_ATS1E1R,
.accessfn = at_e012_access, .writefn = ats_write64 },
.accessfn = at_s1e01_access, .writefn = ats_write64 },
{ .name = "AT_S1E1W", .state = ARM_CP_STATE_AA64,
.opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 1,
.access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
.fgt = FGT_ATS1E1W,
.accessfn = at_e012_access, .writefn = ats_write64 },
.accessfn = at_s1e01_access, .writefn = ats_write64 },
{ .name = "AT_S1E0R", .state = ARM_CP_STATE_AA64,
.opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 2,
.access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
.fgt = FGT_ATS1E0R,
.accessfn = at_e012_access, .writefn = ats_write64 },
.accessfn = at_s1e01_access, .writefn = ats_write64 },
{ .name = "AT_S1E0W", .state = ARM_CP_STATE_AA64,
.opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 3,
.access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
.fgt = FGT_ATS1E0W,
.accessfn = at_e012_access, .writefn = ats_write64 },
.accessfn = at_s1e01_access, .writefn = ats_write64 },
{ .name = "AT_S12E1R", .state = ARM_CP_STATE_AA64,
.opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 4,
.access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
@ -5689,12 +5743,14 @@ static const ARMCPRegInfo v8_cp_reginfo[] = {
{ .name = "ELR_EL1", .state = ARM_CP_STATE_AA64,
.type = ARM_CP_ALIAS,
.opc0 = 3, .opc1 = 0, .crn = 4, .crm = 0, .opc2 = 1,
.access = PL1_RW,
.access = PL1_RW, .accessfn = access_nv1,
.nv2_redirect_offset = 0x230 | NV2_REDIR_NV1,
.fieldoffset = offsetof(CPUARMState, elr_el[1]) },
{ .name = "SPSR_EL1", .state = ARM_CP_STATE_AA64,
.type = ARM_CP_ALIAS,
.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]) },
/*
* 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]) },
{ .name = "SP_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 4, .crn = 4, .crm = 1, .opc2 = 0,
.nv2_redirect_offset = 0x240,
.access = PL2_RW, .type = ARM_CP_ALIAS | ARM_CP_EL3_NO_EL2_KEEP,
.fieldoffset = offsetof(CPUARMState, sp_el[1]) },
{ .name = "SPSel", .state = ARM_CP_STATE_AA64,
@ -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)) {
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)) {
@ -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_DCT enables tagging on (disabled) stage1 translation
* HCR_FWB changes the interpretation of stage2 descriptor bits
* HCR_NV and HCR_NV1 affect interpretation of descriptor bits
*/
if ((env->cp15.hcr_el2 ^ value) &
(HCR_VM | HCR_PTW | HCR_DC | HCR_DCT | HCR_FWB)) {
(HCR_VM | HCR_PTW | HCR_DC | HCR_DCT | HCR_FWB | HCR_NV | HCR_NV1)) {
tlb_flush(CPU(cpu));
}
env->cp15.hcr_el2 = value;
@ -6001,7 +6065,7 @@ static void hcrx_write(CPUARMState *env, const ARMCPRegInfo *ri,
static CPAccessResult access_hxen(CPUARMState *env, const ARMCPRegInfo *ri,
bool isread)
{
if (arm_current_el(env) < 3
if (arm_current_el(env) == 2
&& arm_feature(env, ARM_FEATURE_EL3)
&& !(env->cp15.scr_el3 & SCR_HXEN)) {
return CP_ACCESS_TRAP_EL3;
@ -6013,6 +6077,7 @@ static const ARMCPRegInfo hcrx_el2_reginfo = {
.name = "HCRX_EL2", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 4, .crn = 1, .crm = 2, .opc2 = 2,
.access = PL2_RW, .writefn = hcrx_write, .accessfn = access_hxen,
.nv2_redirect_offset = 0xa0,
.fieldoffset = offsetof(CPUARMState, cp15.hcrx_el2),
};
@ -6079,6 +6144,7 @@ static const ARMCPRegInfo el2_cp_reginfo[] = {
.type = ARM_CP_IO,
.opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 0,
.access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.hcr_el2),
.nv2_redirect_offset = 0x78,
.writefn = hcr_write, .raw_writefn = raw_write },
{ .name = "HCR", .state = ARM_CP_STATE_AA32,
.type = ARM_CP_ALIAS | ARM_CP_IO,
@ -6089,14 +6155,16 @@ static const ARMCPRegInfo el2_cp_reginfo[] = {
.opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 7,
.access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
{ .name = "ELR_EL2", .state = ARM_CP_STATE_AA64,
.type = ARM_CP_ALIAS,
.type = ARM_CP_ALIAS | ARM_CP_NV2_REDIRECT,
.opc0 = 3, .opc1 = 4, .crn = 4, .crm = 0, .opc2 = 1,
.access = PL2_RW,
.fieldoffset = offsetof(CPUARMState, elr_el[2]) },
{ .name = "ESR_EL2", .state = ARM_CP_STATE_BOTH,
.type = ARM_CP_NV2_REDIRECT,
.opc0 = 3, .opc1 = 4, .crn = 5, .crm = 2, .opc2 = 0,
.access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.esr_el[2]) },
{ .name = "FAR_EL2", .state = ARM_CP_STATE_BOTH,
.type = ARM_CP_NV2_REDIRECT,
.opc0 = 3, .opc1 = 4, .crn = 6, .crm = 0, .opc2 = 0,
.access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.far_el[2]) },
{ .name = "HIFAR", .state = ARM_CP_STATE_AA32,
@ -6105,7 +6173,7 @@ static const ARMCPRegInfo el2_cp_reginfo[] = {
.access = PL2_RW,
.fieldoffset = offsetofhigh32(CPUARMState, cp15.far_el[2]) },
{ .name = "SPSR_EL2", .state = ARM_CP_STATE_AA64,
.type = ARM_CP_ALIAS,
.type = ARM_CP_ALIAS | ARM_CP_NV2_REDIRECT,
.opc0 = 3, .opc1 = 4, .crn = 4, .crm = 0, .opc2 = 0,
.access = PL2_RW,
.fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_HYP]) },
@ -6161,6 +6229,7 @@ static const ARMCPRegInfo el2_cp_reginfo[] = {
{ .name = "VTCR_EL2", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 4, .crn = 2, .crm = 1, .opc2 = 2,
.access = PL2_RW,
.nv2_redirect_offset = 0x40,
/* no .writefn needed as this can't cause an ASID change */
.fieldoffset = offsetof(CPUARMState, cp15.vtcr_el2) },
{ .name = "VTTBR", .state = ARM_CP_STATE_AA32,
@ -6172,6 +6241,7 @@ static const ARMCPRegInfo el2_cp_reginfo[] = {
{ .name = "VTTBR_EL2", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 4, .crn = 2, .crm = 1, .opc2 = 0,
.access = PL2_RW, .writefn = vttbr_write, .raw_writefn = raw_write,
.nv2_redirect_offset = 0x20,
.fieldoffset = offsetof(CPUARMState, cp15.vttbr_el2) },
{ .name = "SCTLR_EL2", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 4, .crn = 1, .crm = 0, .opc2 = 0,
@ -6180,6 +6250,7 @@ static const ARMCPRegInfo el2_cp_reginfo[] = {
{ .name = "TPIDR_EL2", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 4, .crn = 13, .crm = 0, .opc2 = 2,
.access = PL2_RW, .resetvalue = 0,
.nv2_redirect_offset = 0x90,
.fieldoffset = offsetof(CPUARMState, cp15.tpidr_el[2]) },
{ .name = "TTBR0_EL2", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 4, .crn = 2, .crm = 0, .opc2 = 0,
@ -6275,6 +6346,7 @@ static const ARMCPRegInfo el2_cp_reginfo[] = {
.opc0 = 3, .opc1 = 4, .crn = 14, .crm = 0, .opc2 = 3,
.access = PL2_RW, .type = ARM_CP_IO, .resetvalue = 0,
.writefn = gt_cntvoff_write,
.nv2_redirect_offset = 0x60,
.fieldoffset = offsetof(CPUARMState, cp15.cntvoff_el2) },
{ .name = "CNTVOFF", .cp = 15, .opc1 = 4, .crm = 14,
.access = PL2_RW, .type = ARM_CP_64BIT | ARM_CP_ALIAS | ARM_CP_IO,
@ -6313,6 +6385,7 @@ static const ARMCPRegInfo el2_cp_reginfo[] = {
{ .name = "HSTR_EL2", .state = ARM_CP_STATE_BOTH,
.cp = 15, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 3,
.access = PL2_RW,
.nv2_redirect_offset = 0x80,
.fieldoffset = offsetof(CPUARMState, cp15.hstr_el2) },
};
@ -6338,10 +6411,12 @@ static const ARMCPRegInfo el2_sec_cp_reginfo[] = {
{ .name = "VSTTBR_EL2", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 4, .crn = 2, .crm = 6, .opc2 = 0,
.access = PL2_RW, .accessfn = sel2_access,
.nv2_redirect_offset = 0x30,
.fieldoffset = offsetof(CPUARMState, cp15.vsttbr_el2) },
{ .name = "VSTCR_EL2", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 4, .crn = 2, .crm = 6, .opc2 = 2,
.access = PL2_RW, .accessfn = sel2_access,
.nv2_redirect_offset = 0x48,
.fieldoffset = offsetof(CPUARMState, cp15.vstcr_el2) },
};
@ -6509,6 +6584,42 @@ static void el2_e2h_write(CPUARMState *env, const ARMCPRegInfo *ri,
writefn(env, ri, value);
}
static uint64_t el2_e2h_e12_read(CPUARMState *env, const ARMCPRegInfo *ri)
{
/* Pass the EL1 register accessor its ri, not the EL12 alias ri */
return ri->orig_readfn(env, ri->opaque);
}
static void el2_e2h_e12_write(CPUARMState *env, const ARMCPRegInfo *ri,
uint64_t value)
{
/* Pass the EL1 register accessor its ri, not the EL12 alias ri */
return ri->orig_writefn(env, ri->opaque, value);
}
static 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)
{
struct E2HAlias {
@ -6608,6 +6719,41 @@ static void define_arm_vh_e2h_redirects_aliases(ARMCPU *cpu)
new_reg->type |= ARM_CP_ALIAS;
/* Remove PL1/PL0 access, leaving PL2/PL3 R/W in place. */
new_reg->access &= PL2_RW | PL3_RW;
/* The new_reg op fields are as per new_key, not the target reg */
new_reg->crn = (a->new_key & CP_REG_ARM64_SYSREG_CRN_MASK)
>> CP_REG_ARM64_SYSREG_CRN_SHIFT;
new_reg->crm = (a->new_key & CP_REG_ARM64_SYSREG_CRM_MASK)
>> CP_REG_ARM64_SYSREG_CRM_SHIFT;
new_reg->opc0 = (a->new_key & CP_REG_ARM64_SYSREG_OP0_MASK)
>> CP_REG_ARM64_SYSREG_OP0_SHIFT;
new_reg->opc1 = (a->new_key & CP_REG_ARM64_SYSREG_OP1_MASK)
>> CP_REG_ARM64_SYSREG_OP1_SHIFT;
new_reg->opc2 = (a->new_key & CP_REG_ARM64_SYSREG_OP2_MASK)
>> CP_REG_ARM64_SYSREG_OP2_SHIFT;
new_reg->opaque = src_reg;
new_reg->orig_readfn = src_reg->readfn ?: raw_read;
new_reg->orig_writefn = src_reg->writefn ?: raw_write;
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,
(gpointer)(uintptr_t)a->new_key, new_reg);
@ -6741,9 +6887,11 @@ static const ARMCPRegInfo minimal_ras_reginfo[] = {
.type = ARM_CP_CONST, .resetvalue = 0 },
{ .name = "VDISR_EL2", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 4, .crn = 12, .crm = 1, .opc2 = 1,
.nv2_redirect_offset = 0x500,
.access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.vdisr_el2) },
{ .name = "VSESR_EL2", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 4, .crn = 5, .crm = 2, .opc2 = 3,
.nv2_redirect_offset = 0x508,
.access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.vsesr_el2) },
};
@ -6915,6 +7063,7 @@ static void zcr_write(CPUARMState *env, const ARMCPRegInfo *ri,
static const ARMCPRegInfo zcr_reginfo[] = {
{ .name = "ZCR_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 1, .crm = 2, .opc2 = 0,
.nv2_redirect_offset = 0x1e0 | NV2_REDIR_NV1,
.access = PL1_RW, .type = ARM_CP_SVE,
.fieldoffset = offsetof(CPUARMState, vfp.zcr_el[1]),
.writefn = zcr_write, .raw_writefn = raw_write },
@ -6951,10 +7100,21 @@ static CPAccessResult access_tpidr2(CPUARMState *env, const ARMCPRegInfo *ri,
return CP_ACCESS_OK;
}
static CPAccessResult access_esm(CPUARMState *env, const ARMCPRegInfo *ri,
bool isread)
static CPAccessResult access_smprimap(CPUARMState *env, const ARMCPRegInfo *ri,
bool isread)
{
/* If EL1 this is a FEAT_NV access and CPTR_EL3.ESM doesn't apply */
if (arm_current_el(env) == 2
&& arm_feature(env, ARM_FEATURE_EL3)
&& !FIELD_EX64(env->cp15.cptr_el[3], CPTR_EL3, ESM)) {
return CP_ACCESS_TRAP_EL3;
}
return CP_ACCESS_OK;
}
static CPAccessResult access_smpri(CPUARMState *env, const ARMCPRegInfo *ri,
bool isread)
{
/* TODO: FEAT_FGT for SMPRI_EL1 but not SMPRIMAP_EL2 */
if (arm_current_el(env) < 3
&& arm_feature(env, ARM_FEATURE_EL3)
&& !FIELD_EX64(env->cp15.cptr_el[3], CPTR_EL3, ESM)) {
@ -7045,6 +7205,7 @@ static const ARMCPRegInfo sme_reginfo[] = {
.writefn = svcr_write, .raw_writefn = raw_write },
{ .name = "SMCR_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 1, .crm = 2, .opc2 = 6,
.nv2_redirect_offset = 0x1f0 | NV2_REDIR_NV1,
.access = PL1_RW, .type = ARM_CP_SME,
.fieldoffset = offsetof(CPUARMState, vfp.smcr_el[1]),
.writefn = smcr_write, .raw_writefn = raw_write },
@ -7073,12 +7234,13 @@ static const ARMCPRegInfo sme_reginfo[] = {
*/
{ .name = "SMPRI_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 1, .crm = 2, .opc2 = 4,
.access = PL1_RW, .accessfn = access_esm,
.access = PL1_RW, .accessfn = access_smpri,
.fgt = FGT_NSMPRI_EL1,
.type = ARM_CP_CONST, .resetvalue = 0 },
{ .name = "SMPRIMAP_EL2", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 4, .crn = 1, .crm = 2, .opc2 = 5,
.access = PL2_RW, .accessfn = access_esm,
.nv2_redirect_offset = 0x1f8,
.access = PL2_RW, .accessfn = access_smprimap,
.type = ARM_CP_CONST, .resetvalue = 0 },
};
@ -7728,7 +7890,46 @@ static CPAccessResult access_mte(CPUARMState *env, const ARMCPRegInfo *ri,
bool isread)
{
int el = arm_current_el(env);
if (el < 2 && arm_is_el2_enabled(env)) {
uint64_t hcr = arm_hcr_el2_eff(env);
if (!(hcr & HCR_ATA) && (!(hcr & HCR_E2H) || !(hcr & HCR_TGE))) {
return CP_ACCESS_TRAP_EL2;
}
}
if (el < 3 &&
arm_feature(env, ARM_FEATURE_EL3) &&
!(env->cp15.scr_el3 & SCR_ATA)) {
return CP_ACCESS_TRAP_EL3;
}
return CP_ACCESS_OK;
}
static CPAccessResult access_tfsr_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)) {
uint64_t hcr = arm_hcr_el2_eff(env);
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]) },
{ .name = "TFSR_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 5, .crm = 6, .opc2 = 0,
.access = PL1_RW, .accessfn = access_mte,
.access = PL1_RW, .accessfn = access_tfsr_el1,
.nv2_redirect_offset = 0x190 | NV2_REDIR_NV1,
.fieldoffset = offsetof(CPUARMState, cp15.tfsr_el[1]) },
{ .name = "TFSR_EL2", .state = ARM_CP_STATE_AA64,
.type = ARM_CP_NV2_REDIRECT,
.opc0 = 3, .opc1 = 4, .crn = 5, .crm = 6, .opc2 = 0,
.access = PL2_RW, .accessfn = access_mte,
.access = PL2_RW, .accessfn = access_tfsr_el2,
.fieldoffset = offsetof(CPUARMState, cp15.tfsr_el[2]) },
{ .name = "TFSR_EL3", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 6, .crn = 5, .crm = 6, .opc2 = 0,
@ -7912,6 +8115,18 @@ static CPAccessResult access_scxtnum(CPUARMState *env, const ARMCPRegInfo *ri,
return CP_ACCESS_OK;
}
static CPAccessResult access_scxtnum_el1(CPUARMState *env,
const ARMCPRegInfo *ri,
bool isread)
{
CPAccessResult nv1 = access_nv1(env, ri, isread);
if (nv1 != CP_ACCESS_OK) {
return nv1;
}
return access_scxtnum(env, ri, isread);
}
static const ARMCPRegInfo scxtnum_reginfo[] = {
{ .name = "SCXTNUM_EL0", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 3, .crn = 13, .crm = 0, .opc2 = 7,
@ -7920,8 +8135,9 @@ static const ARMCPRegInfo scxtnum_reginfo[] = {
.fieldoffset = offsetof(CPUARMState, scxtnum_el[0]) },
{ .name = "SCXTNUM_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 13, .crm = 0, .opc2 = 7,
.access = PL1_RW, .accessfn = access_scxtnum,
.access = PL1_RW, .accessfn = access_scxtnum_el1,
.fgt = FGT_SCXTNUM_EL1,
.nv2_redirect_offset = 0x188 | NV2_REDIR_NV1,
.fieldoffset = offsetof(CPUARMState, scxtnum_el[1]) },
{ .name = "SCXTNUM_EL2", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 4, .crn = 13, .crm = 0, .opc2 = 7,
@ -7946,25 +8162,53 @@ static CPAccessResult access_fgt(CPUARMState *env, const ARMCPRegInfo *ri,
static const ARMCPRegInfo fgt_reginfo[] = {
{ .name = "HFGRTR_EL2", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 4,
.nv2_redirect_offset = 0x1b8,
.access = PL2_RW, .accessfn = access_fgt,
.fieldoffset = offsetof(CPUARMState, cp15.fgt_read[FGTREG_HFGRTR]) },
{ .name = "HFGWTR_EL2", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 5,
.nv2_redirect_offset = 0x1c0,
.access = PL2_RW, .accessfn = access_fgt,
.fieldoffset = offsetof(CPUARMState, cp15.fgt_write[FGTREG_HFGWTR]) },
{ .name = "HDFGRTR_EL2", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 4, .crn = 3, .crm = 1, .opc2 = 4,
.nv2_redirect_offset = 0x1d0,
.access = PL2_RW, .accessfn = access_fgt,
.fieldoffset = offsetof(CPUARMState, cp15.fgt_read[FGTREG_HDFGRTR]) },
{ .name = "HDFGWTR_EL2", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 4, .crn = 3, .crm = 1, .opc2 = 5,
.nv2_redirect_offset = 0x1d8,
.access = PL2_RW, .accessfn = access_fgt,
.fieldoffset = offsetof(CPUARMState, cp15.fgt_write[FGTREG_HDFGWTR]) },
{ .name = "HFGITR_EL2", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 6,
.nv2_redirect_offset = 0x1c8,
.access = PL2_RW, .accessfn = access_fgt,
.fieldoffset = offsetof(CPUARMState, cp15.fgt_exec[FGTREG_HFGITR]) },
};
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 */
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,
.type = ARM_CP_IO | ARM_CP_ALIAS,
.access = PL2_RW, .accessfn = e2h_access,
.nv2_redirect_offset = 0x180 | NV2_REDIR_NO_NV1,
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].ctl),
.writefn = gt_phys_ctl_write, .raw_writefn = raw_write },
{ .name = "CNTV_CTL_EL02", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 5, .crn = 14, .crm = 3, .opc2 = 1,
.type = ARM_CP_IO | ARM_CP_ALIAS,
.access = PL2_RW, .accessfn = e2h_access,
.nv2_redirect_offset = 0x170 | NV2_REDIR_NO_NV1,
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].ctl),
.writefn = gt_virt_ctl_write, .raw_writefn = raw_write },
{ .name = "CNTP_TVAL_EL02", .state = ARM_CP_STATE_AA64,
@ -8147,11 +8393,13 @@ static const ARMCPRegInfo vhe_reginfo[] = {
.opc0 = 3, .opc1 = 5, .crn = 14, .crm = 2, .opc2 = 2,
.type = ARM_CP_IO | ARM_CP_ALIAS,
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].cval),
.nv2_redirect_offset = 0x178 | NV2_REDIR_NO_NV1,
.access = PL2_RW, .accessfn = e2h_access,
.writefn = gt_phys_cval_write, .raw_writefn = raw_write },
{ .name = "CNTV_CVAL_EL02", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 5, .crn = 14, .crm = 3, .opc2 = 2,
.type = ARM_CP_IO | ARM_CP_ALIAS,
.nv2_redirect_offset = 0x168 | NV2_REDIR_NO_NV1,
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].cval),
.access = PL2_RW, .accessfn = e2h_access,
.writefn = gt_virt_cval_write, .raw_writefn = raw_write },
@ -8164,12 +8412,12 @@ static const ARMCPRegInfo ats1e1_reginfo[] = {
.opc0 = 1, .opc1 = 0, .crn = 7, .crm = 9, .opc2 = 0,
.access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
.fgt = FGT_ATS1E1RP,
.accessfn = at_e012_access, .writefn = ats_write64 },
.accessfn = at_s1e01_access, .writefn = ats_write64 },
{ .name = "AT_S1E1WP", .state = ARM_CP_STATE_AA64,
.opc0 = 1, .opc1 = 0, .crn = 7, .crm = 9, .opc2 = 1,
.access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC,
.fgt = FGT_ATS1E1WP,
.accessfn = at_e012_access, .writefn = ats_write64 },
.accessfn = at_s1e01_access, .writefn = ats_write64 },
};
static const ARMCPRegInfo ats1cp_reginfo[] = {
@ -8793,6 +9041,7 @@ void register_cp_regs_for_features(ARMCPU *cpu)
.opc0 = 3, .opc1 = 4, .crn = 0, .crm = 0, .opc2 = 0,
.access = PL2_RW, .resetvalue = cpu->midr,
.type = ARM_CP_EL3_NO_EL2_C_NZ,
.nv2_redirect_offset = 0x88,
.fieldoffset = offsetof(CPUARMState, cp15.vpidr_el2) },
{ .name = "VMPIDR", .state = ARM_CP_STATE_AA32,
.cp = 15, .opc1 = 4, .crn = 0, .crm = 0, .opc2 = 5,
@ -8804,6 +9053,7 @@ void register_cp_regs_for_features(ARMCPU *cpu)
.opc0 = 3, .opc1 = 4, .crn = 0, .crm = 0, .opc2 = 5,
.access = PL2_RW, .resetvalue = vmpidr_def,
.type = ARM_CP_EL3_NO_EL2_C_NZ,
.nv2_redirect_offset = 0x50,
.fieldoffset = offsetof(CPUARMState, cp15.vmpidr_el2) },
};
/*
@ -9233,6 +9483,7 @@ void register_cp_regs_for_features(ARMCPU *cpu)
{ .name = "ACTLR_EL1", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 1,
.access = PL1_RW, .accessfn = access_tacr,
.nv2_redirect_offset = 0x118,
.type = ARM_CP_CONST, .resetvalue = cpu->reset_auxcr },
{ .name = "ACTLR_EL2", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 4, .crn = 1, .crm = 0, .opc2 = 1,
@ -9302,7 +9553,9 @@ void register_cp_regs_for_features(ARMCPU *cpu)
{ .name = "VBAR", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .crn = 12, .crm = 0, .opc1 = 0, .opc2 = 0,
.access = PL1_RW, .writefn = vbar_write,
.accessfn = access_nv1,
.fgt = FGT_VBAR_EL1,
.nv2_redirect_offset = 0x250 | NV2_REDIR_NV1,
.bank_fieldoffsets = { offsetof(CPUARMState, cp15.vbar_s),
offsetof(CPUARMState, cp15.vbar_ns) },
.resetvalue = 0 },
@ -9317,6 +9570,7 @@ void register_cp_regs_for_features(ARMCPU *cpu)
.opc0 = 3, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 0,
.access = PL1_RW, .accessfn = access_tvm_trvm,
.fgt = FGT_SCTLR_EL1,
.nv2_redirect_offset = 0x110 | NV2_REDIR_NV1,
.bank_fieldoffsets = { offsetof(CPUARMState, cp15.sctlr_s),
offsetof(CPUARMState, cp15.sctlr_ns) },
.writefn = sctlr_write, .resetvalue = cpu->reset_sctlr,
@ -9447,6 +9701,10 @@ void register_cp_regs_for_features(ARMCPU *cpu)
define_arm_cp_regs(cpu, rme_mte_reginfo);
}
}
if (cpu_isar_feature(aa64_nv2, cpu)) {
define_arm_cp_regs(cpu, nv2_reginfo);
}
#endif
if (cpu_isar_feature(any_predinv, cpu)) {
@ -11134,6 +11392,20 @@ static void arm_cpu_do_interrupt_aarch64(CPUState *cs)
old_mode = pstate_read(env);
aarch64_save_sp(env, arm_current_el(env));
env->elr_el[new_el] = env->pc;
if (cur_el == 1 && new_el == 1) {
uint64_t hcr = arm_hcr_el2_eff(env);
if ((hcr & (HCR_NV | HCR_NV1 | HCR_NV2)) == HCR_NV ||
(hcr & (HCR_NV | HCR_NV2)) == (HCR_NV | HCR_NV2)) {
/*
* FEAT_NV, FEAT_NV2 may need to report EL2 in the SPSR
* by setting M[3:2] to 0b10.
* If NV2 is disabled, change SPSR when NV,NV1 == 1,0 (I_ZJRNN)
* If NV2 is enabled, change SPSR when NV is 1 (I_DBTLM)
*/
old_mode = deposit32(old_mode, 2, 2, 2);
}
}
} else {
old_mode = cpsr_read_for_spsr_elx(env);
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;
qemu_log_mask(CPU_LOG_INT, "...with SPSR 0x%x\n", old_mode);
qemu_log_mask(CPU_LOG_INT, "...with ELR 0x%" PRIx64 "\n",
env->elr_el[new_el]);
@ -11987,15 +12260,6 @@ ARMMMUIdx arm_v7m_mmu_idx_for_secstate(CPUARMState *env, bool secstate)
}
#endif
static bool arm_pan_enabled(CPUARMState *env)
{
if (is_a64(env)) {
return env->pstate & PSTATE_PAN;
} else {
return env->uncached_cpsr & CPSR_PAN;
}
}
ARMMMUIdx arm_mmu_idx_el(CPUARMState *env, int el)
{
ARMMMUIdx idx;

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
*
@ -1989,6 +1995,21 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw,
xn = extract64(attrs, 54, 1);
pxn = extract64(attrs, 53, 1);
if (el == 1 && nv_nv1_enabled(env, ptw)) {
/*
* With FEAT_NV, when HCR_EL2.{NV,NV1} == {1,1}, the block/page
* descriptor bit 54 holds PXN, 53 is RES0, and the effective value
* of UXN is 0. Similarly for bits 59 and 60 in table descriptors
* (which we have already folded into bits 53 and 54 of attrs).
* AP[1] (descriptor bit 6, our ap bit 0) is treated as 0.
* Similarly, APTable[0] from the table descriptor is treated as 0;
* we already folded this into AP[1] and squashing that to 0 does
* the right thing.
*/
pxn = xn;
xn = 0;
ap &= ~1;
}
/*
* Note that we modified ptw->in_space earlier for NSTable, but
* result->f.attrs retains a copy of the original security space.

View file

@ -86,6 +86,9 @@ typedef enum {
#define ARM_EL_IL (1 << ARM_EL_IL_SHIFT)
#define ARM_EL_ISV (1 << ARM_EL_ISV_SHIFT)
/* In the Data Abort syndrome */
#define ARM_EL_VNCR (1 << 13)
static inline uint32_t syn_get_ec(uint32_t syn)
{
return syn >> ARM_EL_EC_SHIFT;
@ -256,13 +259,12 @@ static inline uint32_t syn_bxjtrap(int cv, int cond, int rm)
(cv << 24) | (cond << 20) | rm;
}
static inline uint32_t syn_gpc(int s2ptw, int ind, int gpcsc,
static inline uint32_t syn_gpc(int s2ptw, int ind, int gpcsc, int vncr,
int cm, int s1ptw, int wnr, int fsc)
{
/* TODO: FEAT_NV2 adds VNCR */
return (EC_GPC << ARM_EL_EC_SHIFT) | ARM_EL_IL | (s2ptw << 21)
| (ind << 20) | (gpcsc << 14) | (cm << 8) | (s1ptw << 7)
| (wnr << 6) | fsc;
| (ind << 20) | (gpcsc << 14) | (vncr << 13) | (cm << 8)
| (s1ptw << 7) | (wnr << 6) | fsc;
}
static inline uint32_t syn_insn_abort(int same_el, int ea, int s1ptw, int fsc)
@ -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;
}
/*
* Faults due to FEAT_NV2 VNCR_EL2-based accesses report as same-EL
* Data Aborts with the VNCR bit set.
*/
static inline uint32_t syn_data_abort_vncr(int ea, int wnr, int fsc)
{
return (EC_DATAABORT << ARM_EL_EC_SHIFT) | (1 << ARM_EL_EC_SHIFT)
| ARM_EL_IL | ARM_EL_VNCR | (wnr << 6) | fsc;
}
static inline uint32_t syn_swstep(int same_el, int isv, int ex)
{
return (EC_SOFTWARESTEP << ARM_EL_EC_SHIFT) | (same_el << ARM_EL_EC_SHIFT)

View file

@ -1105,6 +1105,16 @@ void aarch64_max_tcg_initfn(Object *obj)
u = FIELD_DP32(u, CLIDR_EL1, LOUU, 0);
cpu->clidr = u;
/*
* Set CTR_EL0.DIC and IDC to tell the guest it doesnt' need to
* do any cache maintenance for data-to-instruction or
* instruction-to-guest coherence. (Our cache ops are nops.)
*/
t = cpu->ctr;
t = FIELD_DP64(t, CTR_EL0, IDC, 1);
t = FIELD_DP64(t, CTR_EL0, DIC, 1);
cpu->ctr = t;
t = cpu->isar.id_aa64isar0;
t = FIELD_DP64(t, ID_AA64ISAR0, AES, 2); /* FEAT_PMULL */
t = FIELD_DP64(t, ID_AA64ISAR0, SHA1, 1); /* FEAT_SHA1 */
@ -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, IESB, 1); /* FEAT_IESB */
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, AT, 1); /* FEAT_LSE2 */
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 = {};
ARMMMUIdx stage1 = stage_1_mmu_idx(mmu_idx);
uint64_t tcr = regime_tcr(env, mmu_idx);
uint64_t hcr = arm_hcr_el2_eff(env);
uint64_t sctlr;
int tbii, tbid;
@ -260,8 +261,10 @@ static CPUARMTBFlags rebuild_hflags_a64(CPUARMState *env, int el, int fp_el,
switch (mmu_idx) {
case ARMMMUIdx_E10_1:
case ARMMMUIdx_E10_1_PAN:
/* TODO: ARMv8.3-NV */
DP_TBFLAG_A64(flags, UNPRIV, 1);
/* FEAT_NV: NV,NV1 == 1,1 means we don't do UNPRIV accesses */
if ((hcr & (HCR_NV | HCR_NV1)) != (HCR_NV | HCR_NV1)) {
DP_TBFLAG_A64(flags, UNPRIV, 1);
}
break;
case ARMMMUIdx_E20_2:
case ARMMMUIdx_E20_2_PAN:
@ -285,13 +288,34 @@ static CPUARMTBFlags rebuild_hflags_a64(CPUARMState *env, int el, int fp_el,
if (arm_fgt_active(env, el)) {
DP_TBFLAG_ANY(flags, FGT_ACTIVE, 1);
if (FIELD_EX64(env->cp15.fgt_exec[FGTREG_HFGITR], HFGITR_EL2, ERET)) {
DP_TBFLAG_A64(flags, FGT_ERET, 1);
DP_TBFLAG_A64(flags, TRAP_ERET, 1);
}
if (fgt_svc(env, el)) {
DP_TBFLAG_ANY(flags, FGT_SVC, 1);
}
}
/*
* ERET can also be trapped for FEAT_NV. arm_hcr_el2_eff() takes care
* of "is EL2 enabled" and the NV bit can only be set if FEAT_NV is present.
*/
if (el == 1 && (hcr & HCR_NV)) {
DP_TBFLAG_A64(flags, TRAP_ERET, 1);
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))) {
/*
* 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, 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.
@ -999,9 +1006,12 @@ void HELPER(pre_smc)(CPUARMState *env, uint32_t syndrome)
: smd_flag && !secure;
if (!arm_feature(env, ARM_FEATURE_EL3) &&
!(arm_hcr_el2_eff(env) & HCR_NV) &&
cpu->psci_conduit != QEMU_PSCI_CONDUIT_SMC) {
/* If we have no EL3 then SMC always UNDEFs and can't be
* trapped to EL2. PSCI-via-SMC is a sort of ersatz EL3
/*
* If we have no EL3 then traditionally SMC always UNDEFs and can't be
* trapped to EL2. For nested virtualization, SMC can be trapped to
* the outer hypervisor. PSCI-via-SMC is a sort of ersatz EL3
* firmware within QEMU, and we want an EL2 guest to be able
* to forbid its EL1 from making PSCI calls into QEMU's
* "firmware" via HCR.TSC, so for these purposes treat

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
* faults and regardless of the target EL.
*/
if (!(template_syn & ARM_EL_ISV) || target_el != 2
if (template_syn & ARM_EL_VNCR) {
/*
* FEAT_NV2 faults on accesses via VNCR_EL2 are a special case:
* they are always reported as "same EL", even though we are going
* from EL1 to EL2.
*/
assert(!fi->stage2);
syn = syn_data_abort_vncr(fi->ea, is_write, fsc);
} else if (!(template_syn & ARM_EL_ISV) || target_el != 2
|| fi->s1ptw || !fi->stage2) {
syn = syn_data_abort_no_iss(same_el, 0,
fi->ea, 0, fi->s1ptw, is_write, fsc);
@ -169,6 +177,20 @@ void arm_deliver_fault(ARMCPU *cpu, vaddr addr,
int current_el = arm_current_el(env);
bool same_el;
uint32_t syn, exc, fsr, fsc;
/*
* We know this must be a data or insn abort, and that
* env->exception.syndrome contains the template syndrome set
* up at translate time. So we can check only the VNCR bit
* (and indeed syndrome does not have the EC field in it,
* because we masked that out in disas_set_insn_syndrome())
*/
bool is_vncr = (mmu_idx != MMU_INST_FETCH) &&
(env->exception.syndrome & ARM_EL_VNCR);
if (is_vncr) {
/* FEAT_NV2 faults on accesses via VNCR_EL2 go to EL2 */
target_el = 2;
}
if (report_as_gpc_exception(cpu, current_el, fi)) {
target_el = 3;
@ -177,7 +199,8 @@ void arm_deliver_fault(ARMCPU *cpu, vaddr addr,
syn = syn_gpc(fi->stage2 && fi->type == ARMFault_GPCFOnWalk,
access_type == MMU_INST_FETCH,
encode_gpcsc(fi), 0, fi->s1ptw,
encode_gpcsc(fi), is_vncr,
0, fi->s1ptw,
access_type == MMU_DATA_STORE, fsc);
env->cp15.mfar_el3 = fi->paddr;

View file

@ -1606,7 +1606,7 @@ static bool trans_ERET(DisasContext *s, arg_ERET *a)
if (s->current_el == 0) {
return false;
}
if (s->fgt_eret) {
if (s->trap_eret) {
gen_exception_insn_el(s, 0, EXCP_UDEF, syn_erettrap(0), 2);
return true;
}
@ -1633,7 +1633,7 @@ static bool trans_ERETA(DisasContext *s, arg_reta *a)
return false;
}
/* The FGT trap takes precedence over an auth trap. */
if (s->fgt_eret) {
if (s->trap_eret) {
gen_exception_insn_el(s, 0, EXCP_UDEF, syn_erettrap(a->m ? 3 : 2), 2);
return true;
}
@ -2132,16 +2132,19 @@ static void handle_sys(DisasContext *s, bool isread,
crn, crm, op0, op1, op2);
const ARMCPRegInfo *ri = get_arm_cp_reginfo(s->cp_regs, key);
bool need_exit_tb = false;
bool nv_trap_to_el2 = false;
bool nv_redirect_reg = false;
bool skip_fp_access_checks = false;
bool nv2_mem_redirect = false;
TCGv_ptr tcg_ri = NULL;
TCGv_i64 tcg_rt;
uint32_t syndrome;
uint32_t syndrome = syn_aa64_sysregtrap(op0, op1, op2, crn, crm, rt, isread);
if (crn == 11 || crn == 15) {
/*
* Check for TIDCP trap, which must take precedence over
* the UNDEF for "no such register" etc.
*/
syndrome = syn_aa64_sysregtrap(op0, op1, op2, crn, crm, rt, isread);
switch (s->current_el) {
case 0:
if (dc_isar_feature(aa64_tidcp1, s)) {
@ -2165,17 +2168,65 @@ static void handle_sys(DisasContext *s, bool isread,
return;
}
if (s->nv2 && ri->nv2_redirect_offset) {
/*
* Some registers always redirect to memory; some only do so if
* HCR_EL2.NV1 is 0, and some only if NV1 is 1 (these come in
* pairs which share an offset; see the table in R_CSRPQ).
*/
if (ri->nv2_redirect_offset & NV2_REDIR_NV1) {
nv2_mem_redirect = s->nv1;
} else if (ri->nv2_redirect_offset & NV2_REDIR_NO_NV1) {
nv2_mem_redirect = !s->nv1;
} else {
nv2_mem_redirect = true;
}
}
/* Check access permissions */
if (!cp_access_ok(s->current_el, ri, isread)) {
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)) {
/* Emit code to perform further access permissions checks at
* runtime; this may result in an exception.
*/
syndrome = syn_aa64_sysregtrap(op0, op1, op2, crn, crm, rt, isread);
gen_a64_update_pc(s, 0);
tcg_ri = tcg_temp_new_ptr();
gen_helper_access_check_cp_reg(tcg_ri, tcg_env,
@ -2190,6 +2241,78 @@ static void handle_sys(DisasContext *s, bool isread,
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 */
switch (ri->type & ARM_CP_SPECIAL_MASK) {
case 0:
@ -2205,12 +2328,17 @@ static void handle_sys(DisasContext *s, bool isread,
}
return;
case ARM_CP_CURRENTEL:
/* Reads as current EL value from pstate, which is
{
/*
* Reads as current EL value from pstate, which is
* guaranteed to be constant by the tb flags.
* For nested virt we should report EL2.
*/
int el = s->nv ? 2 : s->current_el;
tcg_rt = cpu_reg(s, rt);
tcg_gen_movi_i64(tcg_rt, s->current_el << 2);
tcg_gen_movi_i64(tcg_rt, el << 2);
return;
}
case ARM_CP_DC_ZVA:
/* Writes clear the aligned block of memory which rt points into. */
if (s->mte_active[0]) {
@ -2268,13 +2396,6 @@ static void handle_sys(DisasContext *s, bool isread,
default:
g_assert_not_reached();
}
if ((ri->type & ARM_CP_FPU) && !fp_access_check_only(s)) {
return;
} else if ((ri->type & ARM_CP_SVE) && !sve_access_check(s)) {
return;
} else if ((ri->type & ARM_CP_SME) && !sme_access_check(s)) {
return;
}
if (ri->type & ARM_CP_IO) {
/* I/O operations must end the TB here (whether read or write) */
@ -13980,7 +14101,7 @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase,
dc->pstate_il = EX_TBFLAG_ANY(tb_flags, PSTATE__IL);
dc->fgt_active = EX_TBFLAG_ANY(tb_flags, FGT_ACTIVE);
dc->fgt_svc = EX_TBFLAG_ANY(tb_flags, FGT_SVC);
dc->fgt_eret = EX_TBFLAG_A64(tb_flags, FGT_ERET);
dc->trap_eret = EX_TBFLAG_A64(tb_flags, TRAP_ERET);
dc->sve_excp_el = EX_TBFLAG_A64(tb_flags, SVEEXC_EL);
dc->sme_excp_el = EX_TBFLAG_A64(tb_flags, SMEEXC_EL);
dc->vl = (EX_TBFLAG_A64(tb_flags, VL) + 1) * 16;
@ -13997,6 +14118,11 @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase,
dc->pstate_za = EX_TBFLAG_A64(tb_flags, PSTATE_ZA);
dc->sme_trap_nonstreaming = EX_TBFLAG_A64(tb_flags, SME_TRAP_NONSTREAMING);
dc->naa = EX_TBFLAG_A64(tb_flags, NAA);
dc->nv = EX_TBFLAG_A64(tb_flags, NV);
dc->nv1 = EX_TBFLAG_A64(tb_flags, NV1);
dc->nv2 = EX_TBFLAG_A64(tb_flags, NV2);
dc->nv2_mem_e20 = EX_TBFLAG_A64(tb_flags, NV2_MEM_E20);
dc->nv2_mem_be = EX_TBFLAG_A64(tb_flags, NV2_MEM_BE);
dc->vec_len = 0;
dc->vec_stride = 0;
dc->cp_regs = arm_cpu->cp_regs;

View file

@ -138,12 +138,22 @@ typedef struct DisasContext {
bool mve_no_pred;
/* True if fine-grained traps are active */
bool fgt_active;
/* True if fine-grained trap on ERET is enabled */
bool fgt_eret;
/* True if fine-grained trap on SVC is enabled */
bool fgt_svc;
/* True if a trap on ERET is enabled (FGT or NV) */
bool trap_eret;
/* True if FEAT_LSE2 SCTLR_ELx.nAA is set */
bool naa;
/* 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, set by the current instruction.
@ -159,6 +169,8 @@ typedef struct DisasContext {
int c15_cpar;
/* TCG op of the current insn_start. */
TCGOp *insn_start;
/* Offset from VNCR_EL2 when FEAT_NV2 redirects this reg to memory */
uint32_t nv2_redirect_offset;
} DisasContext;
typedef struct DisasCompare {