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:
commit
f614acb745
15
MAINTAINERS
15
MAINTAINERS
@ -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>
|
||||
|
@ -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
|
||||
|
46
docs/system/arm/b-l475e-iot01a.rst
Normal file
46
docs/system/arm/b-l475e-iot01a.rst
Normal 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
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
72
hw/arm/b-l475e-iot01a.c
Normal 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)
|
@ -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;
|
||||
}
|
||||
|
@ -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'))
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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",
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
266
hw/arm/stm32l4x5_soc.c
Normal 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)
|
@ -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);
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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.)
|
||||
|
57
include/hw/arm/stm32l4x5_soc.h
Normal file
57
include/hw/arm/stm32l4x5_soc.h
Normal 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
|
@ -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 */
|
||||
|
@ -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 &&
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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 },
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user