:p
atchew
Login
Two small bugfixes, plus most of RTH's refactoring of cpregs handling. -- PMM The following changes since commit 1fba9dc71a170b3a05b9d3272dd8ecfe7f26e215: Merge tag 'pull-request-2022-05-04' of https://gitlab.com/thuth/qemu into staging (2022-05-04 08:07:02 -0700) are available in the Git repository at: https://git.linaro.org/people/pmaydell/qemu-arm.git tags/pull-target-arm-20220505 for you to fetch changes up to 99a50d1a67c602126fc2b3a4812d3000eba9bf34: target/arm: read access to performance counters from EL0 (2022-05-05 09:36:22 +0100) ---------------------------------------------------------------- target-arm queue: * Enable read access to performance counters from EL0 * Enable SCTLR_EL1.BT0 for aarch64-linux-user * Refactoring of cpreg handling ---------------------------------------------------------------- Alex Zuepke (1): target/arm: read access to performance counters from EL0 Richard Henderson (22): target/arm: Enable SCTLR_EL1.BT0 for aarch64-linux-user target/arm: Split out cpregs.h target/arm: Reorg CPAccessResult and access_check_cp_reg target/arm: Replace sentinels with ARRAY_SIZE in cpregs.h target/arm: Make some more cpreg data static const target/arm: Reorg ARMCPRegInfo type field bits target/arm: Avoid bare abort() or assert(0) target/arm: Change cpreg access permissions to enum target/arm: Name CPState type target/arm: Name CPSecureState type target/arm: Drop always-true test in define_arm_vh_e2h_redirects_aliases target/arm: Store cpregs key in the hash table directly target/arm: Merge allocation of the cpreg and its name target/arm: Hoist computation of key in add_cpreg_to_hashtable target/arm: Consolidate cpreg updates in add_cpreg_to_hashtable target/arm: Use bool for is64 and ns in add_cpreg_to_hashtable target/arm: Hoist isbanked computation in add_cpreg_to_hashtable target/arm: Perform override check early in add_cpreg_to_hashtable target/arm: Reformat comments in add_cpreg_to_hashtable target/arm: Remove HOST_BIG_ENDIAN ifdef in add_cpreg_to_hashtable target/arm: Add isar predicates for FEAT_Debugv8p2 target/arm: Add isar_feature_{aa64,any}_ras target/arm/cpregs.h | 453 ++++++++++++++++++++++++++++++++++++++ target/arm/cpu.h | 393 +++------------------------------ hw/arm/pxa2xx.c | 2 +- hw/arm/pxa2xx_pic.c | 2 +- hw/intc/arm_gicv3_cpuif.c | 6 +- hw/intc/arm_gicv3_kvm.c | 3 +- target/arm/cpu.c | 25 +-- target/arm/cpu64.c | 2 +- target/arm/cpu_tcg.c | 5 +- target/arm/gdbstub.c | 5 +- target/arm/helper.c | 358 +++++++++++++----------------- target/arm/hvf/hvf.c | 2 +- target/arm/kvm-stub.c | 4 +- target/arm/kvm.c | 4 +- target/arm/machine.c | 4 +- target/arm/op_helper.c | 57 ++--- target/arm/translate-a64.c | 14 +- target/arm/translate-neon.c | 2 +- target/arm/translate.c | 13 +- tests/tcg/aarch64/bti-3.c | 42 ++++ tests/tcg/aarch64/Makefile.target | 6 +- 21 files changed, 738 insertions(+), 664 deletions(-) create mode 100644 target/arm/cpregs.h create mode 100644 tests/tcg/aarch64/bti-3.c
From: Richard Henderson <richard.henderson@linaro.org> This controls whether the PACI{A,B}SP instructions trap with BTYPE=3 (indirect branch from register other than x16/x17). The linux kernel sets this in bti_enable(). Resolves: https://gitlab.com/qemu-project/qemu/-/issues/998 Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20220427042312.294300-1-richard.henderson@linaro.org [PMM: remove stray change to makefile comment] Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/cpu.c | 2 ++ tests/tcg/aarch64/bti-3.c | 42 +++++++++++++++++++++++++++++++ tests/tcg/aarch64/Makefile.target | 6 ++--- 3 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 tests/tcg/aarch64/bti-3.c diff --git a/target/arm/cpu.c b/target/arm/cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -XXX,XX +XXX,XX @@ static void arm_cpu_reset(DeviceState *dev) /* Enable all PAC keys. */ env->cp15.sctlr_el[1] |= (SCTLR_EnIA | SCTLR_EnIB | SCTLR_EnDA | SCTLR_EnDB); + /* Trap on btype=3 for PACIxSP. */ + env->cp15.sctlr_el[1] |= SCTLR_BT0; /* and to the FP/Neon instructions */ env->cp15.cpacr_el1 = deposit64(env->cp15.cpacr_el1, 20, 2, 3); /* and to the SVE instructions */ diff --git a/tests/tcg/aarch64/bti-3.c b/tests/tcg/aarch64/bti-3.c new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/tests/tcg/aarch64/bti-3.c @@ -XXX,XX +XXX,XX @@ +/* + * BTI vs PACIASP + */ + +#include "bti-crt.inc.c" + +static void skip2_sigill(int sig, siginfo_t *info, ucontext_t *uc) +{ + uc->uc_mcontext.pc += 8; + uc->uc_mcontext.pstate = 1; +} + +#define BTYPE_1() \ + asm("mov %0,#1; adr x16, 1f; br x16; 1: hint #25; mov %0,#0" \ + : "=r"(skipped) : : "x16", "x30") + +#define BTYPE_2() \ + asm("mov %0,#1; adr x16, 1f; blr x16; 1: hint #25; mov %0,#0" \ + : "=r"(skipped) : : "x16", "x30") + +#define BTYPE_3() \ + asm("mov %0,#1; adr x15, 1f; br x15; 1: hint #25; mov %0,#0" \ + : "=r"(skipped) : : "x15", "x30") + +#define TEST(WHICH, EXPECT) \ + do { WHICH(); fail += skipped ^ EXPECT; } while (0) + +int main() +{ + int fail = 0; + int skipped; + + /* Signal-like with SA_SIGINFO. */ + signal_info(SIGILL, skip2_sigill); + + /* With SCTLR_EL1.BT0 set, PACIASP is not compatible with type=3. */ + TEST(BTYPE_1, 0); + TEST(BTYPE_2, 0); + TEST(BTYPE_3, 1); + + return fail; +} diff --git a/tests/tcg/aarch64/Makefile.target b/tests/tcg/aarch64/Makefile.target index XXXXXXX..XXXXXXX 100644 --- a/tests/tcg/aarch64/Makefile.target +++ b/tests/tcg/aarch64/Makefile.target @@ -XXX,XX +XXX,XX @@ endif # BTI Tests # bti-1 tests the elf notes, so we require special compiler support. ifneq ($(CROSS_CC_HAS_ARMV8_BTI),) -AARCH64_TESTS += bti-1 -bti-1: CFLAGS += -mbranch-protection=standard -bti-1: LDFLAGS += -nostdlib +AARCH64_TESTS += bti-1 bti-3 +bti-1 bti-3: CFLAGS += -mbranch-protection=standard +bti-1 bti-3: LDFLAGS += -nostdlib endif # bti-2 tests PROT_BTI, so no special compiler support required. AARCH64_TESTS += bti-2 -- 2.25.1
From: Richard Henderson <richard.henderson@linaro.org> Move ARMCPRegInfo and all related declarations to a new internal header, out of the public cpu.h. Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20220501055028.646596-2-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/cpregs.h | 413 +++++++++++++++++++++++++++++++++++++ target/arm/cpu.h | 368 --------------------------------- hw/arm/pxa2xx.c | 1 + hw/arm/pxa2xx_pic.c | 1 + hw/intc/arm_gicv3_cpuif.c | 1 + hw/intc/arm_gicv3_kvm.c | 2 + target/arm/cpu.c | 1 + target/arm/cpu64.c | 1 + target/arm/cpu_tcg.c | 1 + target/arm/gdbstub.c | 3 +- target/arm/helper.c | 1 + target/arm/op_helper.c | 1 + target/arm/translate-a64.c | 4 +- target/arm/translate.c | 3 +- 14 files changed, 427 insertions(+), 374 deletions(-) create mode 100644 target/arm/cpregs.h diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/target/arm/cpregs.h @@ -XXX,XX +XXX,XX @@ +/* + * QEMU ARM CP Register access and descriptions + * + * Copyright (c) 2022 Linaro Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see + * <http://www.gnu.org/licenses/gpl-2.0.html> + */ + +#ifndef TARGET_ARM_CPREGS_H +#define TARGET_ARM_CPREGS_H + +/* + * ARMCPRegInfo type field bits. If the SPECIAL bit is set this is a + * special-behaviour cp reg and bits [11..8] indicate what behaviour + * it has. Otherwise it is a simple cp reg, where CONST indicates that + * TCG can assume the value to be constant (ie load at translate time) + * and 64BIT indicates a 64 bit wide coprocessor register. SUPPRESS_TB_END + * indicates that the TB should not be ended after a write to this register + * (the default is that the TB ends after cp writes). OVERRIDE permits + * a register definition to override a previous definition for the + * same (cp, is64, crn, crm, opc1, opc2) tuple: either the new or the + * old must have the OVERRIDE bit set. + * ALIAS indicates that this register is an alias view of some underlying + * state which is also visible via another register, and that the other + * register is handling migration and reset; registers marked ALIAS will not be + * migrated but may have their state set by syncing of register state from KVM. + * NO_RAW indicates that this register has no underlying state and does not + * support raw access for state saving/loading; it will not be used for either + * migration or KVM state synchronization. (Typically this is for "registers" + * which are actually used as instructions for cache maintenance and so on.) + * IO indicates that this register does I/O and therefore its accesses + * need to be marked with gen_io_start() and also end the TB. In particular, + * registers which implement clocks or timers require this. + * RAISES_EXC is for when the read or write hook might raise an exception; + * the generated code will synchronize the CPU state before calling the hook + * so that it is safe for the hook to call raise_exception(). + * NEWEL is for writes to registers that might change the exception + * level - typically on older ARM chips. For those cases we need to + * re-read the new el when recomputing the translation flags. + */ +#define ARM_CP_SPECIAL 0x0001 +#define ARM_CP_CONST 0x0002 +#define ARM_CP_64BIT 0x0004 +#define ARM_CP_SUPPRESS_TB_END 0x0008 +#define ARM_CP_OVERRIDE 0x0010 +#define ARM_CP_ALIAS 0x0020 +#define ARM_CP_IO 0x0040 +#define ARM_CP_NO_RAW 0x0080 +#define ARM_CP_NOP (ARM_CP_SPECIAL | 0x0100) +#define ARM_CP_WFI (ARM_CP_SPECIAL | 0x0200) +#define ARM_CP_NZCV (ARM_CP_SPECIAL | 0x0300) +#define ARM_CP_CURRENTEL (ARM_CP_SPECIAL | 0x0400) +#define ARM_CP_DC_ZVA (ARM_CP_SPECIAL | 0x0500) +#define ARM_CP_DC_GVA (ARM_CP_SPECIAL | 0x0600) +#define ARM_CP_DC_GZVA (ARM_CP_SPECIAL | 0x0700) +#define ARM_LAST_SPECIAL ARM_CP_DC_GZVA +#define ARM_CP_FPU 0x1000 +#define ARM_CP_SVE 0x2000 +#define ARM_CP_NO_GDB 0x4000 +#define ARM_CP_RAISES_EXC 0x8000 +#define ARM_CP_NEWEL 0x10000 +/* Used only as a terminator for ARMCPRegInfo lists */ +#define ARM_CP_SENTINEL 0xfffff +/* Mask of only the flag bits in a type field */ +#define ARM_CP_FLAG_MASK 0x1f0ff + +/* + * Valid values for ARMCPRegInfo state field, indicating which of + * the AArch32 and AArch64 execution states this register is visible in. + * If the reginfo doesn't explicitly specify then it is AArch32 only. + * If the reginfo is declared to be visible in both states then a second + * reginfo is synthesised for the AArch32 view of the AArch64 register, + * such that the AArch32 view is the lower 32 bits of the AArch64 one. + * Note that we rely on the values of these enums as we iterate through + * the various states in some places. + */ +enum { + ARM_CP_STATE_AA32 = 0, + ARM_CP_STATE_AA64 = 1, + ARM_CP_STATE_BOTH = 2, +}; + +/* + * ARM CP register secure state flags. These flags identify security state + * attributes for a given CP register entry. + * The existence of both or neither secure and non-secure flags indicates that + * the register has both a secure and non-secure hash entry. A single one of + * these flags causes the register to only be hashed for the specified + * security state. + * Although definitions may have any combination of the S/NS bits, each + * registered entry will only have one to identify whether the entry is secure + * or non-secure. + */ +enum { + ARM_CP_SECSTATE_S = (1 << 0), /* bit[0]: Secure state register */ + ARM_CP_SECSTATE_NS = (1 << 1), /* bit[1]: Non-secure state register */ +}; + +/* + * Return true if cptype is a valid type field. This is used to try to + * catch errors where the sentinel has been accidentally left off the end + * of a list of registers. + */ +static inline bool cptype_valid(int cptype) +{ + return ((cptype & ~ARM_CP_FLAG_MASK) == 0) + || ((cptype & ARM_CP_SPECIAL) && + ((cptype & ~ARM_CP_FLAG_MASK) <= ARM_LAST_SPECIAL)); +} + +/* + * Access rights: + * We define bits for Read and Write access for what rev C of the v7-AR ARM ARM + * defines as PL0 (user), PL1 (fiq/irq/svc/abt/und/sys, ie privileged), and + * PL2 (hyp). The other level which has Read and Write bits is Secure PL1 + * (ie any of the privileged modes in Secure state, or Monitor mode). + * If a register is accessible in one privilege level it's always accessible + * in higher privilege levels too. Since "Secure PL1" also follows this rule + * (ie anything visible in PL2 is visible in S-PL1, some things are only + * visible in S-PL1) but "Secure PL1" is a bit of a mouthful, we bend the + * terminology a little and call this PL3. + * In AArch64 things are somewhat simpler as the PLx bits line up exactly + * with the ELx exception levels. + * + * If access permissions for a register are more complex than can be + * described with these bits, then use a laxer set of restrictions, and + * do the more restrictive/complex check inside a helper function. + */ +#define PL3_R 0x80 +#define PL3_W 0x40 +#define PL2_R (0x20 | PL3_R) +#define PL2_W (0x10 | PL3_W) +#define PL1_R (0x08 | PL2_R) +#define PL1_W (0x04 | PL2_W) +#define PL0_R (0x02 | PL1_R) +#define PL0_W (0x01 | PL1_W) + +/* + * For user-mode some registers are accessible to EL0 via a kernel + * trap-and-emulate ABI. In this case we define the read permissions + * as actually being PL0_R. However some bits of any given register + * may still be masked. + */ +#ifdef CONFIG_USER_ONLY +#define PL0U_R PL0_R +#else +#define PL0U_R PL1_R +#endif + +#define PL3_RW (PL3_R | PL3_W) +#define PL2_RW (PL2_R | PL2_W) +#define PL1_RW (PL1_R | PL1_W) +#define PL0_RW (PL0_R | PL0_W) + +typedef enum CPAccessResult { + /* Access is permitted */ + CP_ACCESS_OK = 0, + /* + * Access fails due to a configurable trap or enable which would + * result in a categorized exception syndrome giving information about + * the failing instruction (ie syndrome category 0x3, 0x4, 0x5, 0x6, + * 0xc or 0x18). The exception is taken to the usual target EL (EL1 or + * PL1 if in EL0, otherwise to the current EL). + */ + CP_ACCESS_TRAP = 1, + /* + * Access fails and results in an exception syndrome 0x0 ("uncategorized"). + * Note that this is not a catch-all case -- the set of cases which may + * result in this failure is specifically defined by the architecture. + */ + CP_ACCESS_TRAP_UNCATEGORIZED = 2, + /* As CP_ACCESS_TRAP, but for traps directly to EL2 or EL3 */ + CP_ACCESS_TRAP_EL2 = 3, + CP_ACCESS_TRAP_EL3 = 4, + /* As CP_ACCESS_UNCATEGORIZED, but for traps directly to EL2 or EL3 */ + CP_ACCESS_TRAP_UNCATEGORIZED_EL2 = 5, + CP_ACCESS_TRAP_UNCATEGORIZED_EL3 = 6, +} CPAccessResult; + +typedef struct ARMCPRegInfo ARMCPRegInfo; + +/* + * Access functions for coprocessor registers. These cannot fail and + * may not raise exceptions. + */ +typedef uint64_t CPReadFn(CPUARMState *env, const ARMCPRegInfo *opaque); +typedef void CPWriteFn(CPUARMState *env, const ARMCPRegInfo *opaque, + uint64_t value); +/* Access permission check functions for coprocessor registers. */ +typedef CPAccessResult CPAccessFn(CPUARMState *env, + const ARMCPRegInfo *opaque, + bool isread); +/* Hook function for register reset */ +typedef void CPResetFn(CPUARMState *env, const ARMCPRegInfo *opaque); + +#define CP_ANY 0xff + +/* Definition of an ARM coprocessor register */ +struct ARMCPRegInfo { + /* Name of register (useful mainly for debugging, need not be unique) */ + const char *name; + /* + * Location of register: coprocessor number and (crn,crm,opc1,opc2) + * tuple. Any of crm, opc1 and opc2 may be CP_ANY to indicate a + * 'wildcard' field -- any value of that field in the MRC/MCR insn + * will be decoded to this register. The register read and write + * callbacks will be passed an ARMCPRegInfo with the crn/crm/opc1/opc2 + * used by the program, so it is possible to register a wildcard and + * then behave differently on read/write if necessary. + * For 64 bit registers, only crm and opc1 are relevant; crn and opc2 + * must both be zero. + * For AArch64-visible registers, opc0 is also used. + * Since there are no "coprocessors" in AArch64, cp is purely used as a + * way to distinguish (for KVM's benefit) guest-visible system registers + * from demuxed ones provided to preserve the "no side effects on + * KVM register read/write from QEMU" semantics. cp==0x13 is guest + * visible (to match KVM's encoding); cp==0 will be converted to + * cp==0x13 when the ARMCPRegInfo is registered, for convenience. + */ + uint8_t cp; + uint8_t crn; + uint8_t crm; + uint8_t opc0; + uint8_t opc1; + uint8_t opc2; + /* Execution state in which this register is visible: ARM_CP_STATE_* */ + int state; + /* Register type: ARM_CP_* bits/values */ + int type; + /* Access rights: PL*_[RW] */ + int access; + /* Security state: ARM_CP_SECSTATE_* bits/values */ + int secure; + /* + * 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 + * register read/write functions, since they are passed the ARMCPRegInfo*. + */ + void *opaque; + /* + * Value of this register, if it is ARM_CP_CONST. Otherwise, if + * fieldoffset is non-zero, the reset value of the register. + */ + uint64_t resetvalue; + /* + * Offset of the field in CPUARMState for this register. + * This is not needed if either: + * 1. type is ARM_CP_CONST or one of the ARM_CP_SPECIALs + * 2. both readfn and writefn are specified + */ + ptrdiff_t fieldoffset; /* offsetof(CPUARMState, field) */ + + /* + * Offsets of the secure and non-secure fields in CPUARMState for the + * register if it is banked. These fields are only used during the static + * registration of a register. During hashing the bank associated + * with a given security state is copied to fieldoffset which is used from + * there on out. + * + * It is expected that register definitions use either fieldoffset or + * bank_fieldoffsets in the definition but not both. It is also expected + * that both bank offsets are set when defining a banked register. This + * use indicates that a register is banked. + */ + ptrdiff_t bank_fieldoffsets[2]; + + /* + * Function for making any access checks for this register in addition to + * those specified by the 'access' permissions bits. If NULL, no extra + * checks required. The access check is performed at runtime, not at + * translate time. + */ + CPAccessFn *accessfn; + /* + * Function for handling reads of this register. If NULL, then reads + * will be done by loading from the offset into CPUARMState specified + * by fieldoffset. + */ + CPReadFn *readfn; + /* + * Function for handling writes of this register. If NULL, then writes + * will be done by writing to the offset into CPUARMState specified + * by fieldoffset. + */ + CPWriteFn *writefn; + /* + * Function for doing a "raw" read; used when we need to copy + * coprocessor state to the kernel for KVM or out for + * migration. This only needs to be provided if there is also a + * readfn and it has side effects (for instance clear-on-read bits). + */ + CPReadFn *raw_readfn; + /* + * Function for doing a "raw" write; used when we need to copy KVM + * kernel coprocessor state into userspace, or for inbound + * migration. This only needs to be provided if there is also a + * writefn and it masks out "unwritable" bits or has write-one-to-clear + * or similar behaviour. + */ + CPWriteFn *raw_writefn; + /* + * Function for resetting the register. If NULL, then reset will be done + * by writing resetvalue to the field specified in fieldoffset. If + * fieldoffset is 0 then no reset will be done. + */ + CPResetFn *resetfn; + + /* + * "Original" writefn and readfn. + * 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 + * forwards the operation. Before overwriting the accessors, + * the original function is copied here, so that accesses that + * really do go to the EL1/EL0 version proceed normally. + * (The corresponding EL2 register is linked via opaque.) + */ + CPReadFn *orig_readfn; + CPWriteFn *orig_writefn; +}; + +/* + * Macros which are lvalues for the field in CPUARMState for the + * ARMCPRegInfo *ri. + */ +#define CPREG_FIELD32(env, ri) \ + (*(uint32_t *)((char *)(env) + (ri)->fieldoffset)) +#define CPREG_FIELD64(env, ri) \ + (*(uint64_t *)((char *)(env) + (ri)->fieldoffset)) + +#define REGINFO_SENTINEL { .type = ARM_CP_SENTINEL } + +void define_arm_cp_regs_with_opaque(ARMCPU *cpu, + const ARMCPRegInfo *regs, void *opaque); +void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu, + const ARMCPRegInfo *regs, void *opaque); +static inline void define_arm_cp_regs(ARMCPU *cpu, const ARMCPRegInfo *regs) +{ + define_arm_cp_regs_with_opaque(cpu, regs, 0); +} +static inline void define_one_arm_cp_reg(ARMCPU *cpu, const ARMCPRegInfo *regs) +{ + define_one_arm_cp_reg_with_opaque(cpu, regs, 0); +} +const ARMCPRegInfo *get_arm_cp_reginfo(GHashTable *cpregs, uint32_t encoded_cp); + +/* + * Definition of an ARM co-processor register as viewed from + * userspace. This is used for presenting sanitised versions of + * registers to userspace when emulating the Linux AArch64 CPU + * ID/feature ABI (advertised as HWCAP_CPUID). + */ +typedef struct ARMCPRegUserSpaceInfo { + /* Name of register */ + const char *name; + + /* Is the name actually a glob pattern */ + bool is_glob; + + /* Only some bits are exported to user space */ + uint64_t exported_bits; + + /* Fixed bits are applied after the mask */ + uint64_t fixed_bits; +} ARMCPRegUserSpaceInfo; + +#define REGUSERINFO_SENTINEL { .name = NULL } + +void modify_arm_cp_regs(ARMCPRegInfo *regs, const ARMCPRegUserSpaceInfo *mods); + +/* CPWriteFn that can be used to implement writes-ignored behaviour */ +void arm_cp_write_ignore(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value); +/* CPReadFn that can be used for read-as-zero behaviour */ +uint64_t arm_cp_read_zero(CPUARMState *env, const ARMCPRegInfo *ri); + +/* + * CPResetFn that does nothing, for use if no reset is required even + * if fieldoffset is non zero. + */ +void arm_cp_reset_ignore(CPUARMState *env, const ARMCPRegInfo *opaque); + +/* + * Return true if this reginfo struct's field in the cpu state struct + * is 64 bits wide. + */ +static inline bool cpreg_field_is_64bit(const ARMCPRegInfo *ri) +{ + return (ri->state == ARM_CP_STATE_AA64) || (ri->type & ARM_CP_64BIT); +} + +static inline bool cp_access_ok(int current_el, + const ARMCPRegInfo *ri, int isread) +{ + return (ri->access >> ((current_el * 2) + isread)) & 1; +} + +/* Raw read of a coprocessor register (as needed for migration, etc) */ +uint64_t read_raw_cp_reg(CPUARMState *env, const ARMCPRegInfo *ri); + +#endif /* TARGET_ARM_CPREGS_H */ diff --git a/target/arm/cpu.h b/target/arm/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -XXX,XX +XXX,XX @@ static inline uint64_t cpreg_to_kvm_id(uint32_t cpregid) return kvmid; } -/* ARMCPRegInfo type field bits. If the SPECIAL bit is set this is a - * special-behaviour cp reg and bits [11..8] indicate what behaviour - * it has. Otherwise it is a simple cp reg, where CONST indicates that - * TCG can assume the value to be constant (ie load at translate time) - * and 64BIT indicates a 64 bit wide coprocessor register. SUPPRESS_TB_END - * indicates that the TB should not be ended after a write to this register - * (the default is that the TB ends after cp writes). OVERRIDE permits - * a register definition to override a previous definition for the - * same (cp, is64, crn, crm, opc1, opc2) tuple: either the new or the - * old must have the OVERRIDE bit set. - * ALIAS indicates that this register is an alias view of some underlying - * state which is also visible via another register, and that the other - * register is handling migration and reset; registers marked ALIAS will not be - * migrated but may have their state set by syncing of register state from KVM. - * NO_RAW indicates that this register has no underlying state and does not - * support raw access for state saving/loading; it will not be used for either - * migration or KVM state synchronization. (Typically this is for "registers" - * which are actually used as instructions for cache maintenance and so on.) - * IO indicates that this register does I/O and therefore its accesses - * need to be marked with gen_io_start() and also end the TB. In particular, - * registers which implement clocks or timers require this. - * RAISES_EXC is for when the read or write hook might raise an exception; - * the generated code will synchronize the CPU state before calling the hook - * so that it is safe for the hook to call raise_exception(). - * NEWEL is for writes to registers that might change the exception - * level - typically on older ARM chips. For those cases we need to - * re-read the new el when recomputing the translation flags. - */ -#define ARM_CP_SPECIAL 0x0001 -#define ARM_CP_CONST 0x0002 -#define ARM_CP_64BIT 0x0004 -#define ARM_CP_SUPPRESS_TB_END 0x0008 -#define ARM_CP_OVERRIDE 0x0010 -#define ARM_CP_ALIAS 0x0020 -#define ARM_CP_IO 0x0040 -#define ARM_CP_NO_RAW 0x0080 -#define ARM_CP_NOP (ARM_CP_SPECIAL | 0x0100) -#define ARM_CP_WFI (ARM_CP_SPECIAL | 0x0200) -#define ARM_CP_NZCV (ARM_CP_SPECIAL | 0x0300) -#define ARM_CP_CURRENTEL (ARM_CP_SPECIAL | 0x0400) -#define ARM_CP_DC_ZVA (ARM_CP_SPECIAL | 0x0500) -#define ARM_CP_DC_GVA (ARM_CP_SPECIAL | 0x0600) -#define ARM_CP_DC_GZVA (ARM_CP_SPECIAL | 0x0700) -#define ARM_LAST_SPECIAL ARM_CP_DC_GZVA -#define ARM_CP_FPU 0x1000 -#define ARM_CP_SVE 0x2000 -#define ARM_CP_NO_GDB 0x4000 -#define ARM_CP_RAISES_EXC 0x8000 -#define ARM_CP_NEWEL 0x10000 -/* Used only as a terminator for ARMCPRegInfo lists */ -#define ARM_CP_SENTINEL 0xfffff -/* Mask of only the flag bits in a type field */ -#define ARM_CP_FLAG_MASK 0x1f0ff - -/* Valid values for ARMCPRegInfo state field, indicating which of - * the AArch32 and AArch64 execution states this register is visible in. - * If the reginfo doesn't explicitly specify then it is AArch32 only. - * If the reginfo is declared to be visible in both states then a second - * reginfo is synthesised for the AArch32 view of the AArch64 register, - * such that the AArch32 view is the lower 32 bits of the AArch64 one. - * Note that we rely on the values of these enums as we iterate through - * the various states in some places. - */ -enum { - ARM_CP_STATE_AA32 = 0, - ARM_CP_STATE_AA64 = 1, - ARM_CP_STATE_BOTH = 2, -}; - -/* ARM CP register secure state flags. These flags identify security state - * attributes for a given CP register entry. - * The existence of both or neither secure and non-secure flags indicates that - * the register has both a secure and non-secure hash entry. A single one of - * these flags causes the register to only be hashed for the specified - * security state. - * Although definitions may have any combination of the S/NS bits, each - * registered entry will only have one to identify whether the entry is secure - * or non-secure. - */ -enum { - ARM_CP_SECSTATE_S = (1 << 0), /* bit[0]: Secure state register */ - ARM_CP_SECSTATE_NS = (1 << 1), /* bit[1]: Non-secure state register */ -}; - -/* Return true if cptype is a valid type field. This is used to try to - * catch errors where the sentinel has been accidentally left off the end - * of a list of registers. - */ -static inline bool cptype_valid(int cptype) -{ - return ((cptype & ~ARM_CP_FLAG_MASK) == 0) - || ((cptype & ARM_CP_SPECIAL) && - ((cptype & ~ARM_CP_FLAG_MASK) <= ARM_LAST_SPECIAL)); -} - -/* Access rights: - * We define bits for Read and Write access for what rev C of the v7-AR ARM ARM - * defines as PL0 (user), PL1 (fiq/irq/svc/abt/und/sys, ie privileged), and - * PL2 (hyp). The other level which has Read and Write bits is Secure PL1 - * (ie any of the privileged modes in Secure state, or Monitor mode). - * If a register is accessible in one privilege level it's always accessible - * in higher privilege levels too. Since "Secure PL1" also follows this rule - * (ie anything visible in PL2 is visible in S-PL1, some things are only - * visible in S-PL1) but "Secure PL1" is a bit of a mouthful, we bend the - * terminology a little and call this PL3. - * In AArch64 things are somewhat simpler as the PLx bits line up exactly - * with the ELx exception levels. - * - * If access permissions for a register are more complex than can be - * described with these bits, then use a laxer set of restrictions, and - * do the more restrictive/complex check inside a helper function. - */ -#define PL3_R 0x80 -#define PL3_W 0x40 -#define PL2_R (0x20 | PL3_R) -#define PL2_W (0x10 | PL3_W) -#define PL1_R (0x08 | PL2_R) -#define PL1_W (0x04 | PL2_W) -#define PL0_R (0x02 | PL1_R) -#define PL0_W (0x01 | PL1_W) - -/* - * For user-mode some registers are accessible to EL0 via a kernel - * trap-and-emulate ABI. In this case we define the read permissions - * as actually being PL0_R. However some bits of any given register - * may still be masked. - */ -#ifdef CONFIG_USER_ONLY -#define PL0U_R PL0_R -#else -#define PL0U_R PL1_R -#endif - -#define PL3_RW (PL3_R | PL3_W) -#define PL2_RW (PL2_R | PL2_W) -#define PL1_RW (PL1_R | PL1_W) -#define PL0_RW (PL0_R | PL0_W) - /* Return the highest implemented Exception Level */ static inline int arm_highest_el(CPUARMState *env) { @@ -XXX,XX +XXX,XX @@ static inline int arm_current_el(CPUARMState *env) } } -typedef struct ARMCPRegInfo ARMCPRegInfo; - -typedef enum CPAccessResult { - /* Access is permitted */ - CP_ACCESS_OK = 0, - /* Access fails due to a configurable trap or enable which would - * result in a categorized exception syndrome giving information about - * the failing instruction (ie syndrome category 0x3, 0x4, 0x5, 0x6, - * 0xc or 0x18). The exception is taken to the usual target EL (EL1 or - * PL1 if in EL0, otherwise to the current EL). - */ - CP_ACCESS_TRAP = 1, - /* Access fails and results in an exception syndrome 0x0 ("uncategorized"). - * Note that this is not a catch-all case -- the set of cases which may - * result in this failure is specifically defined by the architecture. - */ - CP_ACCESS_TRAP_UNCATEGORIZED = 2, - /* As CP_ACCESS_TRAP, but for traps directly to EL2 or EL3 */ - CP_ACCESS_TRAP_EL2 = 3, - CP_ACCESS_TRAP_EL3 = 4, - /* As CP_ACCESS_UNCATEGORIZED, but for traps directly to EL2 or EL3 */ - CP_ACCESS_TRAP_UNCATEGORIZED_EL2 = 5, - CP_ACCESS_TRAP_UNCATEGORIZED_EL3 = 6, -} CPAccessResult; - -/* Access functions for coprocessor registers. These cannot fail and - * may not raise exceptions. - */ -typedef uint64_t CPReadFn(CPUARMState *env, const ARMCPRegInfo *opaque); -typedef void CPWriteFn(CPUARMState *env, const ARMCPRegInfo *opaque, - uint64_t value); -/* Access permission check functions for coprocessor registers. */ -typedef CPAccessResult CPAccessFn(CPUARMState *env, - const ARMCPRegInfo *opaque, - bool isread); -/* Hook function for register reset */ -typedef void CPResetFn(CPUARMState *env, const ARMCPRegInfo *opaque); - -#define CP_ANY 0xff - -/* Definition of an ARM coprocessor register */ -struct ARMCPRegInfo { - /* Name of register (useful mainly for debugging, need not be unique) */ - const char *name; - /* Location of register: coprocessor number and (crn,crm,opc1,opc2) - * tuple. Any of crm, opc1 and opc2 may be CP_ANY to indicate a - * 'wildcard' field -- any value of that field in the MRC/MCR insn - * will be decoded to this register. The register read and write - * callbacks will be passed an ARMCPRegInfo with the crn/crm/opc1/opc2 - * used by the program, so it is possible to register a wildcard and - * then behave differently on read/write if necessary. - * For 64 bit registers, only crm and opc1 are relevant; crn and opc2 - * must both be zero. - * For AArch64-visible registers, opc0 is also used. - * Since there are no "coprocessors" in AArch64, cp is purely used as a - * way to distinguish (for KVM's benefit) guest-visible system registers - * from demuxed ones provided to preserve the "no side effects on - * KVM register read/write from QEMU" semantics. cp==0x13 is guest - * visible (to match KVM's encoding); cp==0 will be converted to - * cp==0x13 when the ARMCPRegInfo is registered, for convenience. - */ - uint8_t cp; - uint8_t crn; - uint8_t crm; - uint8_t opc0; - uint8_t opc1; - uint8_t opc2; - /* Execution state in which this register is visible: ARM_CP_STATE_* */ - int state; - /* Register type: ARM_CP_* bits/values */ - int type; - /* Access rights: PL*_[RW] */ - int access; - /* Security state: ARM_CP_SECSTATE_* bits/values */ - int secure; - /* 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 - * register read/write functions, since they are passed the ARMCPRegInfo*. - */ - void *opaque; - /* Value of this register, if it is ARM_CP_CONST. Otherwise, if - * fieldoffset is non-zero, the reset value of the register. - */ - uint64_t resetvalue; - /* Offset of the field in CPUARMState for this register. - * - * This is not needed if either: - * 1. type is ARM_CP_CONST or one of the ARM_CP_SPECIALs - * 2. both readfn and writefn are specified - */ - ptrdiff_t fieldoffset; /* offsetof(CPUARMState, field) */ - - /* Offsets of the secure and non-secure fields in CPUARMState for the - * register if it is banked. These fields are only used during the static - * registration of a register. During hashing the bank associated - * with a given security state is copied to fieldoffset which is used from - * there on out. - * - * It is expected that register definitions use either fieldoffset or - * bank_fieldoffsets in the definition but not both. It is also expected - * that both bank offsets are set when defining a banked register. This - * use indicates that a register is banked. - */ - ptrdiff_t bank_fieldoffsets[2]; - - /* Function for making any access checks for this register in addition to - * those specified by the 'access' permissions bits. If NULL, no extra - * checks required. The access check is performed at runtime, not at - * translate time. - */ - CPAccessFn *accessfn; - /* Function for handling reads of this register. If NULL, then reads - * will be done by loading from the offset into CPUARMState specified - * by fieldoffset. - */ - CPReadFn *readfn; - /* Function for handling writes of this register. If NULL, then writes - * will be done by writing to the offset into CPUARMState specified - * by fieldoffset. - */ - CPWriteFn *writefn; - /* Function for doing a "raw" read; used when we need to copy - * coprocessor state to the kernel for KVM or out for - * migration. This only needs to be provided if there is also a - * readfn and it has side effects (for instance clear-on-read bits). - */ - CPReadFn *raw_readfn; - /* Function for doing a "raw" write; used when we need to copy KVM - * kernel coprocessor state into userspace, or for inbound - * migration. This only needs to be provided if there is also a - * writefn and it masks out "unwritable" bits or has write-one-to-clear - * or similar behaviour. - */ - CPWriteFn *raw_writefn; - /* Function for resetting the register. If NULL, then reset will be done - * by writing resetvalue to the field specified in fieldoffset. If - * fieldoffset is 0 then no reset will be done. - */ - CPResetFn *resetfn; - - /* - * "Original" writefn and readfn. - * 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 - * forwards the operation. Before overwriting the accessors, - * the original function is copied here, so that accesses that - * really do go to the EL1/EL0 version proceed normally. - * (The corresponding EL2 register is linked via opaque.) - */ - CPReadFn *orig_readfn; - CPWriteFn *orig_writefn; -}; - -/* Macros which are lvalues for the field in CPUARMState for the - * ARMCPRegInfo *ri. - */ -#define CPREG_FIELD32(env, ri) \ - (*(uint32_t *)((char *)(env) + (ri)->fieldoffset)) -#define CPREG_FIELD64(env, ri) \ - (*(uint64_t *)((char *)(env) + (ri)->fieldoffset)) - -#define REGINFO_SENTINEL { .type = ARM_CP_SENTINEL } - -void define_arm_cp_regs_with_opaque(ARMCPU *cpu, - const ARMCPRegInfo *regs, void *opaque); -void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu, - const ARMCPRegInfo *regs, void *opaque); -static inline void define_arm_cp_regs(ARMCPU *cpu, const ARMCPRegInfo *regs) -{ - define_arm_cp_regs_with_opaque(cpu, regs, 0); -} -static inline void define_one_arm_cp_reg(ARMCPU *cpu, const ARMCPRegInfo *regs) -{ - define_one_arm_cp_reg_with_opaque(cpu, regs, 0); -} -const ARMCPRegInfo *get_arm_cp_reginfo(GHashTable *cpregs, uint32_t encoded_cp); - -/* - * Definition of an ARM co-processor register as viewed from - * userspace. This is used for presenting sanitised versions of - * registers to userspace when emulating the Linux AArch64 CPU - * ID/feature ABI (advertised as HWCAP_CPUID). - */ -typedef struct ARMCPRegUserSpaceInfo { - /* Name of register */ - const char *name; - - /* Is the name actually a glob pattern */ - bool is_glob; - - /* Only some bits are exported to user space */ - uint64_t exported_bits; - - /* Fixed bits are applied after the mask */ - uint64_t fixed_bits; -} ARMCPRegUserSpaceInfo; - -#define REGUSERINFO_SENTINEL { .name = NULL } - -void modify_arm_cp_regs(ARMCPRegInfo *regs, const ARMCPRegUserSpaceInfo *mods); - -/* CPWriteFn that can be used to implement writes-ignored behaviour */ -void arm_cp_write_ignore(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value); -/* CPReadFn that can be used for read-as-zero behaviour */ -uint64_t arm_cp_read_zero(CPUARMState *env, const ARMCPRegInfo *ri); - -/* CPResetFn that does nothing, for use if no reset is required even - * if fieldoffset is non zero. - */ -void arm_cp_reset_ignore(CPUARMState *env, const ARMCPRegInfo *opaque); - -/* Return true if this reginfo struct's field in the cpu state struct - * is 64 bits wide. - */ -static inline bool cpreg_field_is_64bit(const ARMCPRegInfo *ri) -{ - return (ri->state == ARM_CP_STATE_AA64) || (ri->type & ARM_CP_64BIT); -} - -static inline bool cp_access_ok(int current_el, - const ARMCPRegInfo *ri, int isread) -{ - return (ri->access >> ((current_el * 2) + isread)) & 1; -} - -/* Raw read of a coprocessor register (as needed for migration, etc) */ -uint64_t read_raw_cp_reg(CPUARMState *env, const ARMCPRegInfo *ri); - /** * write_list_to_cpustate * @cpu: ARMCPU diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/pxa2xx.c +++ b/hw/arm/pxa2xx.c @@ -XXX,XX +XXX,XX @@ #include "qemu/cutils.h" #include "qemu/log.h" #include "qom/object.h" +#include "target/arm/cpregs.h" static struct { hwaddr io_base; diff --git a/hw/arm/pxa2xx_pic.c b/hw/arm/pxa2xx_pic.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/pxa2xx_pic.c +++ b/hw/arm/pxa2xx_pic.c @@ -XXX,XX +XXX,XX @@ #include "hw/sysbus.h" #include "migration/vmstate.h" #include "qom/object.h" +#include "target/arm/cpregs.h" #define ICIP 0x00 /* Interrupt Controller IRQ Pending register */ #define ICMR 0x04 /* Interrupt Controller Mask register */ diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index XXXXXXX..XXXXXXX 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -XXX,XX +XXX,XX @@ #include "gicv3_internal.h" #include "hw/irq.h" #include "cpu.h" +#include "target/arm/cpregs.h" /* * Special case return value from hppvi_index(); must be larger than diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c index XXXXXXX..XXXXXXX 100644 --- a/hw/intc/arm_gicv3_kvm.c +++ b/hw/intc/arm_gicv3_kvm.c @@ -XXX,XX +XXX,XX @@ #include "vgic_common.h" #include "migration/blocker.h" #include "qom/object.h" +#include "target/arm/cpregs.h" + #ifdef DEBUG_GICV3_KVM #define DPRINTF(fmt, ...) \ diff --git a/target/arm/cpu.c b/target/arm/cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -XXX,XX +XXX,XX @@ #include "kvm_arm.h" #include "disas/capstone.h" #include "fpu/softfloat.h" +#include "cpregs.h" static void arm_cpu_set_pc(CPUState *cs, vaddr value) { diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -XXX,XX +XXX,XX @@ #include "hvf_arm.h" #include "qapi/visitor.h" #include "hw/qdev-properties.h" +#include "cpregs.h" #ifndef CONFIG_USER_ONLY diff --git a/target/arm/cpu_tcg.c b/target/arm/cpu_tcg.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu_tcg.c +++ b/target/arm/cpu_tcg.c @@ -XXX,XX +XXX,XX @@ #if !defined(CONFIG_USER_ONLY) #include "hw/boards.h" #endif +#include "cpregs.h" /* CPU models. These are not needed for the AArch64 linux-user build. */ #if !defined(CONFIG_USER_ONLY) || !defined(TARGET_AARCH64) diff --git a/target/arm/gdbstub.c b/target/arm/gdbstub.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/gdbstub.c +++ b/target/arm/gdbstub.c @@ -XXX,XX +XXX,XX @@ */ #include "qemu/osdep.h" #include "cpu.h" -#include "internals.h" #include "exec/gdbstub.h" +#include "internals.h" +#include "cpregs.h" typedef struct RegisterSysregXmlParam { CPUState *cs; diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ #include "exec/cpu_ldst.h" #include "semihosting/common-semi.h" #endif +#include "cpregs.h" #define ARM_CPU_FREQ 1000000000 /* FIXME: 1 GHz, should be configurable */ #define PMCR_NUM_COUNTERS 4 /* QEMU IMPDEF choice */ diff --git a/target/arm/op_helper.c b/target/arm/op_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/op_helper.c +++ b/target/arm/op_helper.c @@ -XXX,XX +XXX,XX @@ #include "internals.h" #include "exec/exec-all.h" #include "exec/cpu_ldst.h" +#include "cpregs.h" #define SIGNBIT (uint32_t)0x80000000 #define SIGNBIT64 ((uint64_t)1 << 63) diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -XXX,XX +XXX,XX @@ #include "translate.h" #include "internals.h" #include "qemu/host-utils.h" - #include "semihosting/semihost.h" #include "exec/gen-icount.h" - #include "exec/helper-proto.h" #include "exec/helper-gen.h" #include "exec/log.h" - +#include "cpregs.h" #include "translate-a64.h" #include "qemu/atomic128.h" diff --git a/target/arm/translate.c b/target/arm/translate.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -XXX,XX +XXX,XX @@ #include "qemu/bitops.h" #include "arm_ldst.h" #include "semihosting/semihost.h" - #include "exec/helper-proto.h" #include "exec/helper-gen.h" - #include "exec/log.h" +#include "cpregs.h" #define ENABLE_ARCH_4T arm_dc_feature(s, ARM_FEATURE_V4T) -- 2.25.1
From: Richard Henderson <richard.henderson@linaro.org> Rearrange the values of the enumerators of CPAccessResult so that we may directly extract the target el. For the two special cases in access_check_cp_reg, use CPAccessResult. Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20220501055028.646596-3-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/cpregs.h | 26 ++++++++++++-------- target/arm/op_helper.c | 56 +++++++++++++++++++++--------------------- 2 files changed, 44 insertions(+), 38 deletions(-) diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -XXX,XX +XXX,XX @@ static inline bool cptype_valid(int cptype) typedef enum CPAccessResult { /* Access is permitted */ CP_ACCESS_OK = 0, + + /* + * Combined with one of the following, the low 2 bits indicate the + * target exception level. If 0, the exception is taken to the usual + * target EL (EL1 or PL1 if in EL0, otherwise to the current EL). + */ + CP_ACCESS_EL_MASK = 3, + /* * Access fails due to a configurable trap or enable which would * result in a categorized exception syndrome giving information about * the failing instruction (ie syndrome category 0x3, 0x4, 0x5, 0x6, - * 0xc or 0x18). The exception is taken to the usual target EL (EL1 or - * PL1 if in EL0, otherwise to the current EL). + * 0xc or 0x18). */ - CP_ACCESS_TRAP = 1, + CP_ACCESS_TRAP = (1 << 2), + CP_ACCESS_TRAP_EL2 = CP_ACCESS_TRAP | 2, + CP_ACCESS_TRAP_EL3 = CP_ACCESS_TRAP | 3, + /* * Access fails and results in an exception syndrome 0x0 ("uncategorized"). * Note that this is not a catch-all case -- the set of cases which may * result in this failure is specifically defined by the architecture. */ - CP_ACCESS_TRAP_UNCATEGORIZED = 2, - /* As CP_ACCESS_TRAP, but for traps directly to EL2 or EL3 */ - CP_ACCESS_TRAP_EL2 = 3, - CP_ACCESS_TRAP_EL3 = 4, - /* As CP_ACCESS_UNCATEGORIZED, but for traps directly to EL2 or EL3 */ - CP_ACCESS_TRAP_UNCATEGORIZED_EL2 = 5, - CP_ACCESS_TRAP_UNCATEGORIZED_EL3 = 6, + CP_ACCESS_TRAP_UNCATEGORIZED = (2 << 2), + CP_ACCESS_TRAP_UNCATEGORIZED_EL2 = CP_ACCESS_TRAP_UNCATEGORIZED | 2, + CP_ACCESS_TRAP_UNCATEGORIZED_EL3 = CP_ACCESS_TRAP_UNCATEGORIZED | 3, } CPAccessResult; typedef struct ARMCPRegInfo ARMCPRegInfo; diff --git a/target/arm/op_helper.c b/target/arm/op_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/op_helper.c +++ b/target/arm/op_helper.c @@ -XXX,XX +XXX,XX @@ void HELPER(access_check_cp_reg)(CPUARMState *env, void *rip, uint32_t syndrome, uint32_t isread) { const ARMCPRegInfo *ri = rip; + CPAccessResult res = CP_ACCESS_OK; int target_el; if (arm_feature(env, ARM_FEATURE_XSCALE) && ri->cp < 14 && extract32(env->cp15.c15_cpar, ri->cp, 1) == 0) { - raise_exception(env, EXCP_UDEF, syndrome, exception_target_el(env)); + res = CP_ACCESS_TRAP; + goto fail; } /* @@ -XXX,XX +XXX,XX @@ void HELPER(access_check_cp_reg)(CPUARMState *env, void *rip, uint32_t syndrome, mask &= ~((1 << 4) | (1 << 14)); if (env->cp15.hstr_el2 & mask) { - target_el = 2; - goto exept; + res = CP_ACCESS_TRAP_EL2; + goto fail; } } - if (!ri->accessfn) { + if (ri->accessfn) { + res = ri->accessfn(env, ri, isread); + } + if (likely(res == CP_ACCESS_OK)) { return; } - switch (ri->accessfn(env, ri, isread)) { - case CP_ACCESS_OK: - return; + fail: + switch (res & ~CP_ACCESS_EL_MASK) { case CP_ACCESS_TRAP: - target_el = exception_target_el(env); - break; - case CP_ACCESS_TRAP_EL2: - /* Requesting a trap to EL2 when we're in EL3 is - * a bug in the access function. - */ - assert(arm_current_el(env) != 3); - target_el = 2; - break; - case CP_ACCESS_TRAP_EL3: - target_el = 3; break; case CP_ACCESS_TRAP_UNCATEGORIZED: - target_el = exception_target_el(env); - syndrome = syn_uncategorized(); - break; - case CP_ACCESS_TRAP_UNCATEGORIZED_EL2: - target_el = 2; - syndrome = syn_uncategorized(); - break; - case CP_ACCESS_TRAP_UNCATEGORIZED_EL3: - target_el = 3; syndrome = syn_uncategorized(); break; default: g_assert_not_reached(); } -exept: + target_el = res & CP_ACCESS_EL_MASK; + switch (target_el) { + case 0: + target_el = exception_target_el(env); + break; + case 2: + assert(arm_current_el(env) != 3); + assert(arm_is_el2_enabled(env)); + break; + case 3: + assert(arm_feature(env, ARM_FEATURE_EL3)); + break; + default: + /* No "direct" traps to EL1 */ + g_assert_not_reached(); + } + raise_exception(env, EXCP_UDEF, syndrome, target_el); } -- 2.25.1
From: Richard Henderson <richard.henderson@linaro.org> Remove a possible source of error by removing REGINFO_SENTINEL and using ARRAY_SIZE (convinently hidden inside a macro) to find the end of the set of regs being registered or modified. The space saved by not having the extra array element reduces the executable's .data.rel.ro section by about 9k. Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20220501055028.646596-4-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/cpregs.h | 53 +++++++++--------- hw/arm/pxa2xx.c | 1 - hw/arm/pxa2xx_pic.c | 1 - hw/intc/arm_gicv3_cpuif.c | 5 -- hw/intc/arm_gicv3_kvm.c | 1 - target/arm/cpu64.c | 1 - target/arm/cpu_tcg.c | 4 -- target/arm/helper.c | 111 ++++++++------------------------------ 8 files changed, 48 insertions(+), 129 deletions(-) diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -XXX,XX +XXX,XX @@ #define ARM_CP_NO_GDB 0x4000 #define ARM_CP_RAISES_EXC 0x8000 #define ARM_CP_NEWEL 0x10000 -/* Used only as a terminator for ARMCPRegInfo lists */ -#define ARM_CP_SENTINEL 0xfffff /* Mask of only the flag bits in a type field */ #define ARM_CP_FLAG_MASK 0x1f0ff @@ -XXX,XX +XXX,XX @@ enum { ARM_CP_SECSTATE_NS = (1 << 1), /* bit[1]: Non-secure state register */ }; -/* - * Return true if cptype is a valid type field. This is used to try to - * catch errors where the sentinel has been accidentally left off the end - * of a list of registers. - */ -static inline bool cptype_valid(int cptype) -{ - return ((cptype & ~ARM_CP_FLAG_MASK) == 0) - || ((cptype & ARM_CP_SPECIAL) && - ((cptype & ~ARM_CP_FLAG_MASK) <= ARM_LAST_SPECIAL)); -} - /* * Access rights: * We define bits for Read and Write access for what rev C of the v7-AR ARM ARM @@ -XXX,XX +XXX,XX @@ struct ARMCPRegInfo { #define CPREG_FIELD64(env, ri) \ (*(uint64_t *)((char *)(env) + (ri)->fieldoffset)) -#define REGINFO_SENTINEL { .type = ARM_CP_SENTINEL } +void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu, const ARMCPRegInfo *reg, + void *opaque); -void define_arm_cp_regs_with_opaque(ARMCPU *cpu, - const ARMCPRegInfo *regs, void *opaque); -void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu, - const ARMCPRegInfo *regs, void *opaque); -static inline void define_arm_cp_regs(ARMCPU *cpu, const ARMCPRegInfo *regs) -{ - define_arm_cp_regs_with_opaque(cpu, regs, 0); -} static inline void define_one_arm_cp_reg(ARMCPU *cpu, const ARMCPRegInfo *regs) { - define_one_arm_cp_reg_with_opaque(cpu, regs, 0); + define_one_arm_cp_reg_with_opaque(cpu, regs, NULL); } + +void define_arm_cp_regs_with_opaque_len(ARMCPU *cpu, const ARMCPRegInfo *regs, + void *opaque, size_t len); + +#define define_arm_cp_regs_with_opaque(CPU, REGS, OPAQUE) \ + do { \ + QEMU_BUILD_BUG_ON(ARRAY_SIZE(REGS) == 0); \ + define_arm_cp_regs_with_opaque_len(CPU, REGS, OPAQUE, \ + ARRAY_SIZE(REGS)); \ + } while (0) + +#define define_arm_cp_regs(CPU, REGS) \ + define_arm_cp_regs_with_opaque(CPU, REGS, NULL) + const ARMCPRegInfo *get_arm_cp_reginfo(GHashTable *cpregs, uint32_t encoded_cp); /* @@ -XXX,XX +XXX,XX @@ typedef struct ARMCPRegUserSpaceInfo { uint64_t fixed_bits; } ARMCPRegUserSpaceInfo; -#define REGUSERINFO_SENTINEL { .name = NULL } +void modify_arm_cp_regs_with_len(ARMCPRegInfo *regs, size_t regs_len, + const ARMCPRegUserSpaceInfo *mods, + size_t mods_len); -void modify_arm_cp_regs(ARMCPRegInfo *regs, const ARMCPRegUserSpaceInfo *mods); +#define modify_arm_cp_regs(REGS, MODS) \ + do { \ + QEMU_BUILD_BUG_ON(ARRAY_SIZE(REGS) == 0); \ + QEMU_BUILD_BUG_ON(ARRAY_SIZE(MODS) == 0); \ + modify_arm_cp_regs_with_len(REGS, ARRAY_SIZE(REGS), \ + MODS, ARRAY_SIZE(MODS)); \ + } while (0) /* CPWriteFn that can be used to implement writes-ignored behaviour */ void arm_cp_write_ignore(CPUARMState *env, const ARMCPRegInfo *ri, diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/pxa2xx.c +++ b/hw/arm/pxa2xx.c @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo pxa_cp_reginfo[] = { { .name = "PWRMODE", .cp = 14, .crn = 7, .crm = 0, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .type = ARM_CP_IO, .readfn = arm_cp_read_zero, .writefn = pxa2xx_pwrmode_write }, - REGINFO_SENTINEL }; static void pxa2xx_setup_cp14(PXA2xxState *s) diff --git a/hw/arm/pxa2xx_pic.c b/hw/arm/pxa2xx_pic.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/pxa2xx_pic.c +++ b/hw/arm/pxa2xx_pic.c @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo pxa_pic_cp_reginfo[] = { REGINFO_FOR_PIC_CP("ICLR2", 8), REGINFO_FOR_PIC_CP("ICFP2", 9), REGINFO_FOR_PIC_CP("ICPR2", 0xa), - REGINFO_SENTINEL }; static const MemoryRegionOps pxa2xx_pic_ops = { diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index XXXXXXX..XXXXXXX 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo gicv3_cpuif_reginfo[] = { .readfn = icc_igrpen1_el3_read, .writefn = icc_igrpen1_el3_write, }, - REGINFO_SENTINEL }; static uint64_t ich_ap_read(CPUARMState *env, const ARMCPRegInfo *ri) @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo gicv3_cpuif_hcr_reginfo[] = { .readfn = ich_vmcr_read, .writefn = ich_vmcr_write, }, - REGINFO_SENTINEL }; static const ARMCPRegInfo gicv3_cpuif_ich_apxr1_reginfo[] = { @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo gicv3_cpuif_ich_apxr1_reginfo[] = { .readfn = ich_ap_read, .writefn = ich_ap_write, }, - REGINFO_SENTINEL }; static const ARMCPRegInfo gicv3_cpuif_ich_apxr23_reginfo[] = { @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo gicv3_cpuif_ich_apxr23_reginfo[] = { .readfn = ich_ap_read, .writefn = ich_ap_write, }, - REGINFO_SENTINEL }; static void gicv3_cpuif_el_change_hook(ARMCPU *cpu, void *opaque) @@ -XXX,XX +XXX,XX @@ void gicv3_init_cpuif(GICv3State *s) .readfn = ich_lr_read, .writefn = ich_lr_write, }, - REGINFO_SENTINEL }; define_arm_cp_regs(cpu, lr_regset); } diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c index XXXXXXX..XXXXXXX 100644 --- a/hw/intc/arm_gicv3_kvm.c +++ b/hw/intc/arm_gicv3_kvm.c @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo gicv3_cpuif_reginfo[] = { */ .resetfn = arm_gicv3_icc_reset, }, - REGINFO_SENTINEL }; /** diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo cortex_a72_a57_a53_cp_reginfo[] = { { .name = "L2MERRSR", .cp = 15, .opc1 = 3, .crm = 15, .access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_64BIT, .resetvalue = 0 }, - REGINFO_SENTINEL }; static void aarch64_a57_initfn(Object *obj) diff --git a/target/arm/cpu_tcg.c b/target/arm/cpu_tcg.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu_tcg.c +++ b/target/arm/cpu_tcg.c @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo cortexa8_cp_reginfo[] = { .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "L2AUXCR", .cp = 15, .crn = 9, .crm = 0, .opc1 = 1, .opc2 = 2, .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, - REGINFO_SENTINEL }; static void cortex_a8_initfn(Object *obj) @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo cortexa9_cp_reginfo[] = { .access = PL1_RW, .resetvalue = 0, .type = ARM_CP_CONST }, { .name = "TLB_ATTR", .cp = 15, .crn = 15, .crm = 7, .opc1 = 5, .opc2 = 2, .access = PL1_RW, .resetvalue = 0, .type = ARM_CP_CONST }, - REGINFO_SENTINEL }; static void cortex_a9_initfn(Object *obj) @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo cortexa15_cp_reginfo[] = { #endif { .name = "L2ECTLR", .cp = 15, .crn = 9, .crm = 0, .opc1 = 1, .opc2 = 3, .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, - REGINFO_SENTINEL }; static void cortex_a7_initfn(Object *obj) @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo cortexr5_cp_reginfo[] = { .access = PL1_RW, .type = ARM_CP_CONST }, { .name = "DCACHE_INVAL", .cp = 15, .opc1 = 0, .crn = 15, .crm = 5, .opc2 = 0, .access = PL1_W, .type = ARM_CP_NOP }, - REGINFO_SENTINEL }; static void cortex_r5_initfn(Object *obj) diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo cp_reginfo[] = { .secure = ARM_CP_SECSTATE_S, .fieldoffset = offsetof(CPUARMState, cp15.contextidr_s), .resetvalue = 0, .writefn = contextidr_write, .raw_writefn = raw_write, }, - REGINFO_SENTINEL }; static const ARMCPRegInfo not_v8_cp_reginfo[] = { @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo not_v8_cp_reginfo[] = { { .name = "CACHEMAINT", .cp = 15, .crn = 7, .crm = CP_ANY, .opc1 = 0, .opc2 = CP_ANY, .access = PL1_W, .type = ARM_CP_NOP | ARM_CP_OVERRIDE }, - REGINFO_SENTINEL }; static const ARMCPRegInfo not_v6_cp_reginfo[] = { @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo not_v6_cp_reginfo[] = { */ { .name = "WFI_v5", .cp = 15, .crn = 7, .crm = 8, .opc1 = 0, .opc2 = 2, .access = PL1_W, .type = ARM_CP_WFI }, - REGINFO_SENTINEL }; static const ARMCPRegInfo not_v7_cp_reginfo[] = { @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo not_v7_cp_reginfo[] = { .opc1 = 0, .opc2 = 0, .access = PL1_RW, .type = ARM_CP_NOP }, { .name = "NMRR", .cp = 15, .crn = 10, .crm = 2, .opc1 = 0, .opc2 = 1, .access = PL1_RW, .type = ARM_CP_NOP }, - REGINFO_SENTINEL }; static void cpacr_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo v6_cp_reginfo[] = { .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 2, .accessfn = cpacr_access, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.cpacr_el1), .resetfn = cpacr_reset, .writefn = cpacr_write, .readfn = cpacr_read }, - REGINFO_SENTINEL }; typedef struct pm_event { @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo v7_cp_reginfo[] = { { .name = "TLBIMVAA", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 3, .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, .writefn = tlbimvaa_write }, - REGINFO_SENTINEL }; static const ARMCPRegInfo v7mp_cp_reginfo[] = { @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo v7mp_cp_reginfo[] = { { .name = "TLBIMVAAIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 3, .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, .writefn = tlbimvaa_is_write }, - REGINFO_SENTINEL }; static const ARMCPRegInfo pmovsset_cp_reginfo[] = { @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo pmovsset_cp_reginfo[] = { .fieldoffset = offsetof(CPUARMState, cp15.c9_pmovsr), .writefn = pmovsset_write, .raw_writefn = raw_write }, - REGINFO_SENTINEL }; static void teecr_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo t2ee_cp_reginfo[] = { { .name = "TEEHBR", .cp = 14, .crn = 1, .crm = 0, .opc1 = 6, .opc2 = 0, .access = PL0_RW, .fieldoffset = offsetof(CPUARMState, teehbr), .accessfn = teehbr_access, .resetvalue = 0 }, - REGINFO_SENTINEL }; static const ARMCPRegInfo v6k_cp_reginfo[] = { @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo v6k_cp_reginfo[] = { .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.tpidrprw_s), offsetoflow32(CPUARMState, cp15.tpidrprw_ns) }, .resetvalue = 0 }, - REGINFO_SENTINEL }; #ifndef CONFIG_USER_ONLY @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_SEC].cval), .writefn = gt_sec_cval_write, .raw_writefn = raw_write, }, - REGINFO_SENTINEL }; static CPAccessResult e2h_access(CPUARMState *env, const ARMCPRegInfo *ri, @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { .access = PL0_R, .type = ARM_CP_NO_RAW | ARM_CP_IO, .readfn = gt_virt_cnt_read, }, - REGINFO_SENTINEL }; #endif @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo vapa_cp_reginfo[] = { .access = PL1_W, .accessfn = ats_access, .writefn = ats_write, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC }, #endif - REGINFO_SENTINEL }; /* Return basic MPU access permission bits. */ @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo pmsav7_cp_reginfo[] = { .fieldoffset = offsetof(CPUARMState, pmsav7.rnr[M_REG_NS]), .writefn = pmsav7_rgnr_write, .resetfn = arm_cp_reset_ignore }, - REGINFO_SENTINEL }; static const ARMCPRegInfo pmsav5_cp_reginfo[] = { @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo pmsav5_cp_reginfo[] = { { .name = "946_PRBS7", .cp = 15, .crn = 6, .crm = 7, .opc1 = 0, .opc2 = CP_ANY, .access = PL1_RW, .resetvalue = 0, .fieldoffset = offsetof(CPUARMState, cp15.c6_region[7]) }, - REGINFO_SENTINEL }; static void vmsa_ttbcr_raw_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo vmsa_pmsa_cp_reginfo[] = { .access = PL1_RW, .accessfn = access_tvm_trvm, .fieldoffset = offsetof(CPUARMState, cp15.far_el[1]), .resetvalue = 0, }, - REGINFO_SENTINEL }; static const ARMCPRegInfo vmsa_cp_reginfo[] = { @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo vmsa_cp_reginfo[] = { /* No offsetoflow32 -- pass the entire TCR to writefn/raw_writefn. */ .bank_fieldoffsets = { offsetof(CPUARMState, cp15.tcr_el[3]), offsetof(CPUARMState, cp15.tcr_el[1])} }, - REGINFO_SENTINEL }; /* Note that unlike TTBCR, writing to TTBCR2 does not require flushing @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo omap_cp_reginfo[] = { { .name = "C9", .cp = 15, .crn = 9, .crm = CP_ANY, .opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_OVERRIDE, .resetvalue = 0 }, - REGINFO_SENTINEL }; static void xscale_cpar_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo xscale_cp_reginfo[] = { { .name = "XSCALE_UNLOCK_DCACHE", .cp = 15, .opc1 = 0, .crn = 9, .crm = 2, .opc2 = 1, .access = PL1_W, .type = ARM_CP_NOP }, - REGINFO_SENTINEL }; static const ARMCPRegInfo dummy_c15_cp_reginfo[] = { @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo dummy_c15_cp_reginfo[] = { .access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_NO_RAW | ARM_CP_OVERRIDE, .resetvalue = 0 }, - REGINFO_SENTINEL }; static const ARMCPRegInfo cache_dirty_status_cp_reginfo[] = { @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo cache_dirty_status_cp_reginfo[] = { { .name = "CDSR", .cp = 15, .crn = 7, .crm = 10, .opc1 = 0, .opc2 = 6, .access = PL1_R, .type = ARM_CP_CONST | ARM_CP_NO_RAW, .resetvalue = 0 }, - REGINFO_SENTINEL }; static const ARMCPRegInfo cache_block_ops_cp_reginfo[] = { @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo cache_block_ops_cp_reginfo[] = { .access = PL0_W, .type = ARM_CP_NOP|ARM_CP_64BIT }, { .name = "CIDCR", .cp = 15, .crm = 14, .opc1 = 0, .access = PL1_W, .type = ARM_CP_NOP|ARM_CP_64BIT }, - REGINFO_SENTINEL }; static const ARMCPRegInfo cache_test_clean_cp_reginfo[] = { @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo cache_test_clean_cp_reginfo[] = { { .name = "TCI_DCACHE", .cp = 15, .crn = 7, .crm = 14, .opc1 = 0, .opc2 = 3, .access = PL0_R, .type = ARM_CP_CONST | ARM_CP_NO_RAW, .resetvalue = (1 << 30) }, - REGINFO_SENTINEL }; static const ARMCPRegInfo strongarm_cp_reginfo[] = { @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo strongarm_cp_reginfo[] = { .crm = CP_ANY, .opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_RW, .resetvalue = 0, .type = ARM_CP_CONST | ARM_CP_OVERRIDE | ARM_CP_NO_RAW }, - REGINFO_SENTINEL }; static uint64_t midr_read(CPUARMState *env, const ARMCPRegInfo *ri) @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo lpae_cp_reginfo[] = { .bank_fieldoffsets = { offsetof(CPUARMState, cp15.ttbr1_s), offsetof(CPUARMState, cp15.ttbr1_ns) }, .writefn = vmsa_ttbr_write, }, - REGINFO_SENTINEL }; static uint64_t aa64_fpcr_read(CPUARMState *env, const ARMCPRegInfo *ri) @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo v8_cp_reginfo[] = { .access = PL1_RW, .accessfn = access_trap_aa32s_el1, .writefn = sdcr_write, .fieldoffset = offsetoflow32(CPUARMState, cp15.mdcr_el3) }, - REGINFO_SENTINEL }; /* Used to describe the behaviour of EL2 regs when EL2 does not exist. */ @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo el3_no_el2_cp_reginfo[] = { .type = ARM_CP_CONST, .cp = 15, .opc1 = 4, .crn = 6, .crm = 0, .opc2 = 2, .access = PL2_RW, .resetvalue = 0 }, - REGINFO_SENTINEL }; /* Ditto, but for registers which exist in ARMv8 but not v7 */ @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo el3_no_el2_v8_cp_reginfo[] = { .cp = 15, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 4, .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, - REGINFO_SENTINEL }; static void do_hcr_write(CPUARMState *env, uint64_t value, uint64_t valid_mask) @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo el2_cp_reginfo[] = { .cp = 15, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 3, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.hstr_el2) }, - REGINFO_SENTINEL }; static const ARMCPRegInfo el2_v8_cp_reginfo[] = { @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo el2_v8_cp_reginfo[] = { .access = PL2_RW, .fieldoffset = offsetofhigh32(CPUARMState, cp15.hcr_el2), .writefn = hcr_writehigh }, - REGINFO_SENTINEL }; static CPAccessResult sel2_access(CPUARMState *env, const ARMCPRegInfo *ri, @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo el2_sec_cp_reginfo[] = { .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 6, .opc2 = 2, .access = PL2_RW, .accessfn = sel2_access, .fieldoffset = offsetof(CPUARMState, cp15.vstcr_el2) }, - REGINFO_SENTINEL }; static CPAccessResult nsacr_access(CPUARMState *env, const ARMCPRegInfo *ri, @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo el3_cp_reginfo[] = { .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 7, .opc2 = 5, .access = PL3_W, .type = ARM_CP_NO_RAW, .writefn = tlbi_aa64_vae3_write }, - REGINFO_SENTINEL }; #ifndef CONFIG_USER_ONLY @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo debug_cp_reginfo[] = { .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 0, .access = PL1_RW, .accessfn = access_tda, .type = ARM_CP_NOP }, - REGINFO_SENTINEL }; static const ARMCPRegInfo debug_lpae_cp_reginfo[] = { @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo debug_lpae_cp_reginfo[] = { .access = PL0_R, .type = ARM_CP_CONST|ARM_CP_64BIT, .resetvalue = 0 }, { .name = "DBGDSAR", .cp = 14, .crm = 2, .opc1 = 0, .access = PL0_R, .type = ARM_CP_CONST|ARM_CP_64BIT, .resetvalue = 0 }, - REGINFO_SENTINEL }; /* Return the exception level to which exceptions should be taken @@ -XXX,XX +XXX,XX @@ static void define_debug_regs(ARMCPU *cpu) .fieldoffset = offsetof(CPUARMState, cp15.dbgbcr[i]), .writefn = dbgbcr_write, .raw_writefn = raw_write }, - REGINFO_SENTINEL }; define_arm_cp_regs(cpu, dbgregs); } @@ -XXX,XX +XXX,XX @@ static void define_debug_regs(ARMCPU *cpu) .fieldoffset = offsetof(CPUARMState, cp15.dbgwcr[i]), .writefn = dbgwcr_write, .raw_writefn = raw_write }, - REGINFO_SENTINEL }; define_arm_cp_regs(cpu, dbgregs); } @@ -XXX,XX +XXX,XX @@ static void define_pmu_regs(ARMCPU *cpu) .type = ARM_CP_IO, .readfn = pmevtyper_readfn, .writefn = pmevtyper_writefn, .raw_writefn = pmevtyper_rawwrite }, - REGINFO_SENTINEL }; define_arm_cp_regs(cpu, pmev_regs); g_free(pmevcntr_name); @@ -XXX,XX +XXX,XX @@ static void define_pmu_regs(ARMCPU *cpu) .cp = 15, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 5, .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, .resetvalue = extract64(cpu->pmceid1, 32, 32) }, - REGINFO_SENTINEL }; define_arm_cp_regs(cpu, v81_pmu_regs); } @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo lor_reginfo[] = { .opc0 = 3, .opc1 = 0, .crn = 10, .crm = 4, .opc2 = 7, .access = PL1_R, .accessfn = access_lor_ns, .type = ARM_CP_CONST, .resetvalue = 0 }, - REGINFO_SENTINEL }; #ifdef TARGET_AARCH64 @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo pauth_reginfo[] = { .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 1, .opc2 = 3, .access = PL1_RW, .accessfn = access_pauth, .fieldoffset = offsetof(CPUARMState, keys.apib.hi) }, - REGINFO_SENTINEL }; static const ARMCPRegInfo tlbirange_reginfo[] = { @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo tlbirange_reginfo[] = { .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 6, .opc2 = 5, .access = PL3_W, .type = ARM_CP_NO_RAW, .writefn = tlbi_aa64_rvae3_write }, - REGINFO_SENTINEL }; static const ARMCPRegInfo tlbios_reginfo[] = { @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo tlbios_reginfo[] = { .opc0 = 1, .opc1 = 6, .crn = 8, .crm = 1, .opc2 = 5, .access = PL3_W, .type = ARM_CP_NO_RAW, .writefn = tlbi_aa64_vae3is_write }, - REGINFO_SENTINEL }; static uint64_t rndr_readfn(CPUARMState *env, const ARMCPRegInfo *ri) @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo rndr_reginfo[] = { .type = ARM_CP_NO_RAW | ARM_CP_SUPPRESS_TB_END | ARM_CP_IO, .opc0 = 3, .opc1 = 3, .crn = 2, .crm = 4, .opc2 = 1, .access = PL0_R, .readfn = rndr_readfn }, - REGINFO_SENTINEL }; #ifndef CONFIG_USER_ONLY @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo dcpop_reg[] = { .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 12, .opc2 = 1, .access = PL0_W, .type = ARM_CP_NO_RAW | ARM_CP_SUPPRESS_TB_END, .accessfn = aa64_cacheop_poc_access, .writefn = dccvap_writefn }, - REGINFO_SENTINEL }; static const ARMCPRegInfo dcpodp_reg[] = { @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo dcpodp_reg[] = { .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 13, .opc2 = 1, .access = PL0_W, .type = ARM_CP_NO_RAW | ARM_CP_SUPPRESS_TB_END, .accessfn = aa64_cacheop_poc_access, .writefn = dccvap_writefn }, - REGINFO_SENTINEL }; #endif /*CONFIG_USER_ONLY*/ @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo mte_reginfo[] = { { .name = "DC_CIGDSW", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 14, .opc2 = 6, .type = ARM_CP_NOP, .access = PL1_W, .accessfn = access_tsw }, - REGINFO_SENTINEL }; static const ARMCPRegInfo mte_tco_ro_reginfo[] = { { .name = "TCO", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 4, .crm = 2, .opc2 = 7, .type = ARM_CP_CONST, .access = PL0_RW, }, - REGINFO_SENTINEL }; static const ARMCPRegInfo mte_el0_cacheop_reginfo[] = { @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo mte_el0_cacheop_reginfo[] = { .accessfn = aa64_zva_access, #endif }, - REGINFO_SENTINEL }; #endif @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo predinv_reginfo[] = { { .name = "CPPRCTX", .state = ARM_CP_STATE_AA32, .cp = 15, .opc1 = 0, .crn = 7, .crm = 3, .opc2 = 7, .type = ARM_CP_NOP, .access = PL0_W, .accessfn = access_predinv }, - REGINFO_SENTINEL }; static uint64_t ccsidr2_read(CPUARMState *env, const ARMCPRegInfo *ri) @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo ccsidr2_reginfo[] = { .access = PL1_R, .accessfn = access_aa64_tid2, .readfn = ccsidr2_read, .type = ARM_CP_NO_RAW }, - REGINFO_SENTINEL }; static CPAccessResult access_aa64_tid3(CPUARMState *env, const ARMCPRegInfo *ri, @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo jazelle_regs[] = { .cp = 14, .crn = 2, .crm = 0, .opc1 = 7, .opc2 = 0, .accessfn = access_joscr_jmcr, .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, - REGINFO_SENTINEL }; static const ARMCPRegInfo vhe_reginfo[] = { @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo vhe_reginfo[] = { .access = PL2_RW, .accessfn = e2h_access, .writefn = gt_virt_cval_write, .raw_writefn = raw_write }, #endif - REGINFO_SENTINEL }; #ifndef CONFIG_USER_ONLY @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo ats1e1_reginfo[] = { .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 9, .opc2 = 1, .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, .writefn = ats_write64 }, - REGINFO_SENTINEL }; static const ARMCPRegInfo ats1cp_reginfo[] = { @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo ats1cp_reginfo[] = { .cp = 15, .opc1 = 0, .crn = 7, .crm = 9, .opc2 = 1, .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, .writefn = ats_write }, - REGINFO_SENTINEL }; #endif @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo actlr2_hactlr2_reginfo[] = { .cp = 15, .opc1 = 4, .crn = 1, .crm = 0, .opc2 = 3, .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, - REGINFO_SENTINEL }; void register_cp_regs_for_features(ARMCPU *cpu) @@ -XXX,XX +XXX,XX @@ void register_cp_regs_for_features(ARMCPU *cpu) .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa32_tid3, .resetvalue = cpu->isar.id_isar6 }, - REGINFO_SENTINEL }; define_arm_cp_regs(cpu, v6_idregs); define_arm_cp_regs(cpu, v6_cp_reginfo); @@ -XXX,XX +XXX,XX @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 7, .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, .resetvalue = cpu->pmceid1 }, - REGINFO_SENTINEL }; #ifdef CONFIG_USER_ONLY ARMCPRegUserSpaceInfo v8_user_idregs[] = { @@ -XXX,XX +XXX,XX @@ void register_cp_regs_for_features(ARMCPU *cpu) .exported_bits = 0x000000f0ffffffff }, { .name = "ID_AA64ISAR*_EL1_RESERVED", .is_glob = true }, - REGUSERINFO_SENTINEL }; modify_arm_cp_regs(v8_idregs, v8_user_idregs); #endif @@ -XXX,XX +XXX,XX @@ void register_cp_regs_for_features(ARMCPU *cpu) .access = PL2_RW, .resetvalue = vmpidr_def, .fieldoffset = offsetof(CPUARMState, cp15.vmpidr_el2) }, - REGINFO_SENTINEL }; define_arm_cp_regs(cpu, vpidr_regs); define_arm_cp_regs(cpu, el2_cp_reginfo); @@ -XXX,XX +XXX,XX @@ void register_cp_regs_for_features(ARMCPU *cpu) .access = PL2_RW, .accessfn = access_el3_aa32ns, .type = ARM_CP_NO_RAW, .writefn = arm_cp_write_ignore, .readfn = mpidr_read }, - REGINFO_SENTINEL }; define_arm_cp_regs(cpu, vpidr_regs); define_arm_cp_regs(cpu, el3_no_el2_cp_reginfo); @@ -XXX,XX +XXX,XX @@ void register_cp_regs_for_features(ARMCPU *cpu) .raw_writefn = raw_write, .writefn = sctlr_write, .fieldoffset = offsetof(CPUARMState, cp15.sctlr_el[3]), .resetvalue = cpu->reset_sctlr }, - REGINFO_SENTINEL }; define_arm_cp_regs(cpu, el3_regs); @@ -XXX,XX +XXX,XX @@ void register_cp_regs_for_features(ARMCPU *cpu) { .name = "DUMMY", .cp = 15, .crn = 0, .crm = 7, .opc1 = 0, .opc2 = CP_ANY, .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 }, - REGINFO_SENTINEL }; ARMCPRegInfo id_v8_midr_cp_reginfo[] = { { .name = "MIDR_EL1", .state = ARM_CP_STATE_BOTH, @@ -XXX,XX +XXX,XX @@ void register_cp_regs_for_features(ARMCPU *cpu) .access = PL1_R, .accessfn = access_aa64_tid1, .type = ARM_CP_CONST, .resetvalue = cpu->revidr }, - REGINFO_SENTINEL }; ARMCPRegInfo id_cp_reginfo[] = { /* These are common to v8 and pre-v8 */ @@ -XXX,XX +XXX,XX @@ void register_cp_regs_for_features(ARMCPU *cpu) .access = PL1_R, .accessfn = access_aa32_tid1, .type = ARM_CP_CONST, .resetvalue = 0 }, - REGINFO_SENTINEL }; /* TLBTR is specific to VMSA */ ARMCPRegInfo id_tlbtr_reginfo = { @@ -XXX,XX +XXX,XX @@ void register_cp_regs_for_features(ARMCPU *cpu) { .name = "MIDR_EL1", .exported_bits = 0x00000000ffffffff }, { .name = "REVIDR_EL1" }, - REGUSERINFO_SENTINEL }; modify_arm_cp_regs(id_v8_midr_cp_reginfo, id_v8_user_midr_cp_reginfo); #endif if (arm_feature(env, ARM_FEATURE_OMAPCP) || arm_feature(env, ARM_FEATURE_STRONGARM)) { - ARMCPRegInfo *r; + size_t i; /* Register the blanket "writes ignored" value first to cover the * whole space. Then update the specific ID registers to allow write * access, so that they ignore writes rather than causing them to * UNDEF. */ define_one_arm_cp_reg(cpu, &crn0_wi_reginfo); - for (r = id_pre_v8_midr_cp_reginfo; - r->type != ARM_CP_SENTINEL; r++) { - r->access = PL1_RW; + for (i = 0; i < ARRAY_SIZE(id_pre_v8_midr_cp_reginfo); ++i) { + id_pre_v8_midr_cp_reginfo[i].access = PL1_RW; } - for (r = id_cp_reginfo; r->type != ARM_CP_SENTINEL; r++) { - r->access = PL1_RW; + for (i = 0; i < ARRAY_SIZE(id_cp_reginfo); ++i) { + id_cp_reginfo[i].access = PL1_RW; } id_mpuir_reginfo.access = PL1_RW; id_tlbtr_reginfo.access = PL1_RW; @@ -XXX,XX +XXX,XX @@ void register_cp_regs_for_features(ARMCPU *cpu) { .name = "MPIDR_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 5, .access = PL1_R, .readfn = mpidr_read, .type = ARM_CP_NO_RAW }, - REGINFO_SENTINEL }; #ifdef CONFIG_USER_ONLY ARMCPRegUserSpaceInfo mpidr_user_cp_reginfo[] = { { .name = "MPIDR_EL1", .fixed_bits = 0x0000000080000000 }, - REGUSERINFO_SENTINEL }; modify_arm_cp_regs(mpidr_cp_reginfo, mpidr_user_cp_reginfo); #endif @@ -XXX,XX +XXX,XX @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 6, .crn = 1, .crm = 0, .opc2 = 1, .access = PL3_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, - REGINFO_SENTINEL }; define_arm_cp_regs(cpu, auxcr_reginfo); if (cpu_isar_feature(aa32_ac2, cpu)) { @@ -XXX,XX +XXX,XX @@ void register_cp_regs_for_features(ARMCPU *cpu) .type = ARM_CP_CONST, .opc0 = 3, .opc1 = 1, .crn = 15, .crm = 3, .opc2 = 0, .access = PL1_R, .resetvalue = cpu->reset_cbar }, - REGINFO_SENTINEL }; /* We don't implement a r/w 64 bit CBAR currently */ assert(arm_feature(env, ARM_FEATURE_CBAR_RO)); @@ -XXX,XX +XXX,XX @@ void register_cp_regs_for_features(ARMCPU *cpu) .bank_fieldoffsets = { offsetof(CPUARMState, cp15.vbar_s), offsetof(CPUARMState, cp15.vbar_ns) }, .resetvalue = 0 }, - REGINFO_SENTINEL }; define_arm_cp_regs(cpu, vbar_cp_reginfo); } @@ -XXX,XX +XXX,XX @@ void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu, r->writefn); } } - /* Bad type field probably means missing sentinel at end of reg list */ - assert(cptype_valid(r->type)); + for (crm = crmmin; crm <= crmmax; crm++) { for (opc1 = opc1min; opc1 <= opc1max; opc1++) { for (opc2 = opc2min; opc2 <= opc2max; opc2++) { @@ -XXX,XX +XXX,XX @@ void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu, } } -void define_arm_cp_regs_with_opaque(ARMCPU *cpu, - const ARMCPRegInfo *regs, void *opaque) +/* Define a whole list of registers */ +void define_arm_cp_regs_with_opaque_len(ARMCPU *cpu, const ARMCPRegInfo *regs, + void *opaque, size_t len) { - /* Define a whole list of registers */ - const ARMCPRegInfo *r; - for (r = regs; r->type != ARM_CP_SENTINEL; r++) { - define_one_arm_cp_reg_with_opaque(cpu, r, opaque); + size_t i; + for (i = 0; i < len; ++i) { + define_one_arm_cp_reg_with_opaque(cpu, regs + i, opaque); } } @@ -XXX,XX +XXX,XX @@ void define_arm_cp_regs_with_opaque(ARMCPU *cpu, * user-space cannot alter any values and dynamic values pertaining to * execution state are hidden from user space view anyway. */ -void modify_arm_cp_regs(ARMCPRegInfo *regs, const ARMCPRegUserSpaceInfo *mods) +void modify_arm_cp_regs_with_len(ARMCPRegInfo *regs, size_t regs_len, + const ARMCPRegUserSpaceInfo *mods, + size_t mods_len) { - const ARMCPRegUserSpaceInfo *m; - ARMCPRegInfo *r; - - for (m = mods; m->name; m++) { + for (size_t mi = 0; mi < mods_len; ++mi) { + const ARMCPRegUserSpaceInfo *m = mods + mi; GPatternSpec *pat = NULL; + if (m->is_glob) { pat = g_pattern_spec_new(m->name); } - for (r = regs; r->type != ARM_CP_SENTINEL; r++) { + for (size_t ri = 0; ri < regs_len; ++ri) { + ARMCPRegInfo *r = regs + ri; + if (pat && g_pattern_match_string(pat, r->name)) { r->type = ARM_CP_CONST; r->access = PL0U_R; -- 2.25.1
From: Richard Henderson <richard.henderson@linaro.org> These particular data structures are not modified at runtime. Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20220501055028.646596-5-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/helper.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ void register_cp_regs_for_features(ARMCPU *cpu) .resetvalue = cpu->pmceid1 }, }; #ifdef CONFIG_USER_ONLY - ARMCPRegUserSpaceInfo v8_user_idregs[] = { + static const ARMCPRegUserSpaceInfo v8_user_idregs[] = { { .name = "ID_AA64PFR0_EL1", .exported_bits = 0x000f000f00ff0000, .fixed_bits = 0x0000000000000011 }, @@ -XXX,XX +XXX,XX @@ void register_cp_regs_for_features(ARMCPU *cpu) */ if (arm_feature(env, ARM_FEATURE_EL3)) { if (arm_feature(env, ARM_FEATURE_AARCH64)) { - ARMCPRegInfo nsacr = { + static const ARMCPRegInfo nsacr = { .name = "NSACR", .type = ARM_CP_CONST, .cp = 15, .opc1 = 0, .crn = 1, .crm = 1, .opc2 = 2, .access = PL1_RW, .accessfn = nsacr_access, @@ -XXX,XX +XXX,XX @@ void register_cp_regs_for_features(ARMCPU *cpu) }; define_one_arm_cp_reg(cpu, &nsacr); } else { - ARMCPRegInfo nsacr = { + static const ARMCPRegInfo nsacr = { .name = "NSACR", .cp = 15, .opc1 = 0, .crn = 1, .crm = 1, .opc2 = 2, .access = PL3_RW | PL1_R, @@ -XXX,XX +XXX,XX @@ void register_cp_regs_for_features(ARMCPU *cpu) } } else { if (arm_feature(env, ARM_FEATURE_V8)) { - ARMCPRegInfo nsacr = { + static const ARMCPRegInfo nsacr = { .name = "NSACR", .type = ARM_CP_CONST, .cp = 15, .opc1 = 0, .crn = 1, .crm = 1, .opc2 = 2, .access = PL1_R, @@ -XXX,XX +XXX,XX @@ void register_cp_regs_for_features(ARMCPU *cpu) .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->pmsav7_dregion << 8 }; - ARMCPRegInfo crn0_wi_reginfo = { + static const ARMCPRegInfo crn0_wi_reginfo = { .name = "CRN0_WI", .cp = 15, .crn = 0, .crm = CP_ANY, .opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_W, .type = ARM_CP_NOP | ARM_CP_OVERRIDE }; #ifdef CONFIG_USER_ONLY - ARMCPRegUserSpaceInfo id_v8_user_midr_cp_reginfo[] = { + static const ARMCPRegUserSpaceInfo id_v8_user_midr_cp_reginfo[] = { { .name = "MIDR_EL1", .exported_bits = 0x00000000ffffffff }, { .name = "REVIDR_EL1" }, @@ -XXX,XX +XXX,XX @@ void register_cp_regs_for_features(ARMCPU *cpu) .access = PL1_R, .readfn = mpidr_read, .type = ARM_CP_NO_RAW }, }; #ifdef CONFIG_USER_ONLY - ARMCPRegUserSpaceInfo mpidr_user_cp_reginfo[] = { + static const ARMCPRegUserSpaceInfo mpidr_user_cp_reginfo[] = { { .name = "MPIDR_EL1", .fixed_bits = 0x0000000080000000 }, }; @@ -XXX,XX +XXX,XX @@ void register_cp_regs_for_features(ARMCPU *cpu) } if (arm_feature(env, ARM_FEATURE_VBAR)) { - ARMCPRegInfo vbar_cp_reginfo[] = { + static const ARMCPRegInfo vbar_cp_reginfo[] = { { .name = "VBAR", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .crn = 12, .crm = 0, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .writefn = vbar_write, -- 2.25.1
From: Richard Henderson <richard.henderson@linaro.org> Instead of defining ARM_CP_FLAG_MASK to remove flags, define ARM_CP_SPECIAL_MASK to isolate special cases. Sort the specials to the low bits. Use an enum. Split the large comment block so as to document each value separately. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20220501055028.646596-6-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/cpregs.h | 130 +++++++++++++++++++++++-------------- target/arm/cpu.c | 4 +- target/arm/helper.c | 4 +- target/arm/translate-a64.c | 6 +- target/arm/translate.c | 6 +- 5 files changed, 92 insertions(+), 58 deletions(-) diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -XXX,XX +XXX,XX @@ #define TARGET_ARM_CPREGS_H /* - * ARMCPRegInfo type field bits. If the SPECIAL bit is set this is a - * special-behaviour cp reg and bits [11..8] indicate what behaviour - * it has. Otherwise it is a simple cp reg, where CONST indicates that - * TCG can assume the value to be constant (ie load at translate time) - * and 64BIT indicates a 64 bit wide coprocessor register. SUPPRESS_TB_END - * indicates that the TB should not be ended after a write to this register - * (the default is that the TB ends after cp writes). OVERRIDE permits - * a register definition to override a previous definition for the - * same (cp, is64, crn, crm, opc1, opc2) tuple: either the new or the - * old must have the OVERRIDE bit set. - * ALIAS indicates that this register is an alias view of some underlying - * state which is also visible via another register, and that the other - * register is handling migration and reset; registers marked ALIAS will not be - * migrated but may have their state set by syncing of register state from KVM. - * NO_RAW indicates that this register has no underlying state and does not - * support raw access for state saving/loading; it will not be used for either - * migration or KVM state synchronization. (Typically this is for "registers" - * which are actually used as instructions for cache maintenance and so on.) - * IO indicates that this register does I/O and therefore its accesses - * need to be marked with gen_io_start() and also end the TB. In particular, - * registers which implement clocks or timers require this. - * RAISES_EXC is for when the read or write hook might raise an exception; - * the generated code will synchronize the CPU state before calling the hook - * so that it is safe for the hook to call raise_exception(). - * NEWEL is for writes to registers that might change the exception - * level - typically on older ARM chips. For those cases we need to - * re-read the new el when recomputing the translation flags. + * ARMCPRegInfo type field bits: */ -#define ARM_CP_SPECIAL 0x0001 -#define ARM_CP_CONST 0x0002 -#define ARM_CP_64BIT 0x0004 -#define ARM_CP_SUPPRESS_TB_END 0x0008 -#define ARM_CP_OVERRIDE 0x0010 -#define ARM_CP_ALIAS 0x0020 -#define ARM_CP_IO 0x0040 -#define ARM_CP_NO_RAW 0x0080 -#define ARM_CP_NOP (ARM_CP_SPECIAL | 0x0100) -#define ARM_CP_WFI (ARM_CP_SPECIAL | 0x0200) -#define ARM_CP_NZCV (ARM_CP_SPECIAL | 0x0300) -#define ARM_CP_CURRENTEL (ARM_CP_SPECIAL | 0x0400) -#define ARM_CP_DC_ZVA (ARM_CP_SPECIAL | 0x0500) -#define ARM_CP_DC_GVA (ARM_CP_SPECIAL | 0x0600) -#define ARM_CP_DC_GZVA (ARM_CP_SPECIAL | 0x0700) -#define ARM_LAST_SPECIAL ARM_CP_DC_GZVA -#define ARM_CP_FPU 0x1000 -#define ARM_CP_SVE 0x2000 -#define ARM_CP_NO_GDB 0x4000 -#define ARM_CP_RAISES_EXC 0x8000 -#define ARM_CP_NEWEL 0x10000 -/* Mask of only the flag bits in a type field */ -#define ARM_CP_FLAG_MASK 0x1f0ff +enum { + /* + * Register must be handled specially during translation. + * The method is one of the values below: + */ + ARM_CP_SPECIAL_MASK = 0x000f, + /* Special: no change to PE state: writes ignored, reads ignored. */ + ARM_CP_NOP = 0x0001, + /* Special: sysreg is WFI, for v5 and v6. */ + ARM_CP_WFI = 0x0002, + /* Special: sysreg is NZCV. */ + ARM_CP_NZCV = 0x0003, + /* Special: sysreg is CURRENTEL. */ + ARM_CP_CURRENTEL = 0x0004, + /* Special: sysreg is DC ZVA or similar. */ + ARM_CP_DC_ZVA = 0x0005, + ARM_CP_DC_GVA = 0x0006, + ARM_CP_DC_GZVA = 0x0007, + + /* Flag: reads produce resetvalue; writes ignored. */ + ARM_CP_CONST = 1 << 4, + /* Flag: For ARM_CP_STATE_AA32, sysreg is 64-bit. */ + ARM_CP_64BIT = 1 << 5, + /* + * Flag: TB should not be ended after a write to this register + * (the default is that the TB ends after cp writes). + */ + ARM_CP_SUPPRESS_TB_END = 1 << 6, + /* + * Flag: Permit a register definition to override a previous definition + * for the same (cp, is64, crn, crm, opc1, opc2) tuple: either the new + * or the old must have the ARM_CP_OVERRIDE bit set. + */ + ARM_CP_OVERRIDE = 1 << 7, + /* + * Flag: Register is an alias view of some underlying state which is also + * visible via another register, and that the other register is handling + * migration and reset; registers marked ARM_CP_ALIAS will not be migrated + * but may have their state set by syncing of register state from KVM. + */ + ARM_CP_ALIAS = 1 << 8, + /* + * Flag: Register does I/O and therefore its accesses need to be marked + * with gen_io_start() and also end the TB. In particular, registers which + * implement clocks or timers require this. + */ + ARM_CP_IO = 1 << 9, + /* + * Flag: Register has no underlying state and does not support raw access + * for state saving/loading; it will not be used for either migration or + * KVM state synchronization. Typically this is for "registers" which are + * actually used as instructions for cache maintenance and so on. + */ + ARM_CP_NO_RAW = 1 << 10, + /* + * Flag: The read or write hook might raise an exception; the generated + * code will synchronize the CPU state before calling the hook so that it + * is safe for the hook to call raise_exception(). + */ + ARM_CP_RAISES_EXC = 1 << 11, + /* + * Flag: Writes to the sysreg might change the exception level - typically + * on older ARM chips. For those cases we need to re-read the new el when + * recomputing the translation flags. + */ + ARM_CP_NEWEL = 1 << 12, + /* + * Flag: Access check for this sysreg is identical to accessing FPU state + * from an instruction: use translation fp_access_check(). + */ + ARM_CP_FPU = 1 << 13, + /* + * Flag: Access check for this sysreg is identical to accessing SVE state + * from an instruction: use translation sve_access_check(). + */ + ARM_CP_SVE = 1 << 14, + /* Flag: Do not expose in gdb sysreg xml. */ + ARM_CP_NO_GDB = 1 << 15, +}; /* * Valid values for ARMCPRegInfo state field, indicating which of diff --git a/target/arm/cpu.c b/target/arm/cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -XXX,XX +XXX,XX @@ static void cp_reg_reset(gpointer key, gpointer value, gpointer opaque) ARMCPRegInfo *ri = value; ARMCPU *cpu = opaque; - if (ri->type & (ARM_CP_SPECIAL | ARM_CP_ALIAS)) { + if (ri->type & (ARM_CP_SPECIAL_MASK | ARM_CP_ALIAS)) { return; } @@ -XXX,XX +XXX,XX @@ static void cp_reg_check_reset(gpointer key, gpointer value, gpointer opaque) ARMCPU *cpu = opaque; uint64_t oldvalue, newvalue; - if (ri->type & (ARM_CP_SPECIAL | ARM_CP_ALIAS | ARM_CP_NO_RAW)) { + if (ri->type & (ARM_CP_SPECIAL_MASK | ARM_CP_ALIAS | ARM_CP_NO_RAW)) { return; } diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, * multiple times. Special registers (ie NOP/WFI) are * never migratable and not even raw-accessible. */ - if ((r->type & ARM_CP_SPECIAL)) { + if (r->type & ARM_CP_SPECIAL_MASK) { r2->type |= ARM_CP_NO_RAW; } if (((r->crm == CP_ANY) && crm != 0) || @@ -XXX,XX +XXX,XX @@ void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu, /* Check that the register definition has enough info to handle * reads and writes if they are permitted. */ - if (!(r->type & (ARM_CP_SPECIAL|ARM_CP_CONST))) { + if (!(r->type & (ARM_CP_SPECIAL_MASK | ARM_CP_CONST))) { if (r->access & PL3_R) { assert((r->fieldoffset || (r->bank_fieldoffsets[0] && r->bank_fieldoffsets[1])) || diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -XXX,XX +XXX,XX @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread, } /* Handle special cases first */ - switch (ri->type & ~(ARM_CP_FLAG_MASK & ~ARM_CP_SPECIAL)) { + switch (ri->type & ARM_CP_SPECIAL_MASK) { + case 0: + break; case ARM_CP_NOP: return; case ARM_CP_NZCV: @@ -XXX,XX +XXX,XX @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread, } return; default: - break; + g_assert_not_reached(); } if ((ri->type & ARM_CP_FPU) && !fp_access_check(s)) { return; diff --git a/target/arm/translate.c b/target/arm/translate.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -XXX,XX +XXX,XX @@ static void do_coproc_insn(DisasContext *s, int cpnum, int is64, } /* Handle special cases first */ - switch (ri->type & ~(ARM_CP_FLAG_MASK & ~ARM_CP_SPECIAL)) { + switch (ri->type & ARM_CP_SPECIAL_MASK) { + case 0: + break; case ARM_CP_NOP: return; case ARM_CP_WFI: @@ -XXX,XX +XXX,XX @@ static void do_coproc_insn(DisasContext *s, int cpnum, int is64, s->base.is_jmp = DISAS_WFI; return; default: - break; + g_assert_not_reached(); } if ((tb_cflags(s->base.tb) & CF_USE_ICOUNT) && (ri->type & ARM_CP_IO)) { -- 2.25.1
From: Richard Henderson <richard.henderson@linaro.org> Standardize on g_assert_not_reached() for "should not happen". Retain abort() when preceeded by fprintf or error_report. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20220501055028.646596-7-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/helper.c | 7 +++---- target/arm/hvf/hvf.c | 2 +- target/arm/kvm-stub.c | 4 ++-- target/arm/kvm.c | 4 ++-- target/arm/machine.c | 4 ++-- target/arm/translate-a64.c | 4 ++-- target/arm/translate-neon.c | 2 +- target/arm/translate.c | 4 ++-- 8 files changed, 15 insertions(+), 16 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu, break; default: /* broken reginfo with out-of-range opc1 */ - assert(false); - break; + g_assert_not_reached(); } /* assert our permissions are not too lax (stricter is fine) */ assert((r->access & ~mask) == 0); @@ -XXX,XX +XXX,XX @@ static bool get_phys_addr_v5(CPUARMState *env, uint32_t address, break; default: /* Never happens, but compiler isn't smart enough to tell. */ - abort(); + g_assert_not_reached(); } } *prot = ap_to_rw_prot(env, mmu_idx, ap, domain_prot); @@ -XXX,XX +XXX,XX @@ static bool get_phys_addr_v6(CPUARMState *env, uint32_t address, break; default: /* Never happens, but compiler isn't smart enough to tell. */ - abort(); + g_assert_not_reached(); } } if (domain_prot == 3) { diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -XXX,XX +XXX,XX @@ int hvf_vcpu_exec(CPUState *cpu) /* we got kicked, no exit to process */ return 0; default: - assert(0); + g_assert_not_reached(); } hvf_sync_vtimer(cpu); diff --git a/target/arm/kvm-stub.c b/target/arm/kvm-stub.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/kvm-stub.c +++ b/target/arm/kvm-stub.c @@ -XXX,XX +XXX,XX @@ bool write_kvmstate_to_list(ARMCPU *cpu) { - abort(); + g_assert_not_reached(); } bool write_list_to_kvmstate(ARMCPU *cpu, int level) { - abort(); + g_assert_not_reached(); } diff --git a/target/arm/kvm.c b/target/arm/kvm.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -XXX,XX +XXX,XX @@ bool write_kvmstate_to_list(ARMCPU *cpu) ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r); break; default: - abort(); + g_assert_not_reached(); } if (ret) { ok = false; @@ -XXX,XX +XXX,XX @@ bool write_list_to_kvmstate(ARMCPU *cpu, int level) r.addr = (uintptr_t)(cpu->cpreg_values + i); break; default: - abort(); + g_assert_not_reached(); } ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r); if (ret) { diff --git a/target/arm/machine.c b/target/arm/machine.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/machine.c +++ b/target/arm/machine.c @@ -XXX,XX +XXX,XX @@ static int cpu_pre_save(void *opaque) if (kvm_enabled()) { if (!write_kvmstate_to_list(cpu)) { /* This should never fail */ - abort(); + g_assert_not_reached(); } /* @@ -XXX,XX +XXX,XX @@ static int cpu_pre_save(void *opaque) } else { if (!write_cpustate_to_list(cpu, false)) { /* This should never fail. */ - abort(); + g_assert_not_reached(); } } diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -XXX,XX +XXX,XX @@ static void handle_fp_1src_half(DisasContext *s, int opcode, int rd, int rn) gen_helper_advsimd_rinth(tcg_res, tcg_op, fpst); break; default: - abort(); + g_assert_not_reached(); } write_fp_sreg(s, rd, tcg_res); @@ -XXX,XX +XXX,XX @@ static void handle_fp_fcvt(DisasContext *s, int opcode, break; } default: - abort(); + g_assert_not_reached(); } } diff --git a/target/arm/translate-neon.c b/target/arm/translate-neon.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/translate-neon.c +++ b/target/arm/translate-neon.c @@ -XXX,XX +XXX,XX @@ static bool trans_VLDST_single(DisasContext *s, arg_VLDST_single *a) } break; default: - abort(); + g_assert_not_reached(); } if ((vd + a->stride * (nregs - 1)) > 31) { /* diff --git a/target/arm/translate.c b/target/arm/translate.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -XXX,XX +XXX,XX @@ static void gen_srs(DisasContext *s, offset = 4; break; default: - abort(); + g_assert_not_reached(); } tcg_gen_addi_i32(addr, addr, offset); tmp = load_reg(s, 14); @@ -XXX,XX +XXX,XX @@ static void gen_srs(DisasContext *s, offset = 0; break; default: - abort(); + g_assert_not_reached(); } tcg_gen_addi_i32(addr, addr, offset); gen_helper_set_r13_banked(cpu_env, tcg_constant_i32(mode), addr); -- 2.25.1
From: Richard Henderson <richard.henderson@linaro.org> Create a typedef as well, and use it in ARMCPRegInfo. This won't be perfect for debugging, but it'll nicely display the most common cases. Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20220501055028.646596-8-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/cpregs.h | 44 +++++++++++++++++++++++--------------------- target/arm/helper.c | 2 +- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -XXX,XX +XXX,XX @@ enum { * described with these bits, then use a laxer set of restrictions, and * do the more restrictive/complex check inside a helper function. */ -#define PL3_R 0x80 -#define PL3_W 0x40 -#define PL2_R (0x20 | PL3_R) -#define PL2_W (0x10 | PL3_W) -#define PL1_R (0x08 | PL2_R) -#define PL1_W (0x04 | PL2_W) -#define PL0_R (0x02 | PL1_R) -#define PL0_W (0x01 | PL1_W) +typedef enum { + PL3_R = 0x80, + PL3_W = 0x40, + PL2_R = 0x20 | PL3_R, + PL2_W = 0x10 | PL3_W, + PL1_R = 0x08 | PL2_R, + PL1_W = 0x04 | PL2_W, + PL0_R = 0x02 | PL1_R, + PL0_W = 0x01 | PL1_W, -/* - * For user-mode some registers are accessible to EL0 via a kernel - * trap-and-emulate ABI. In this case we define the read permissions - * as actually being PL0_R. However some bits of any given register - * may still be masked. - */ + /* + * For user-mode some registers are accessible to EL0 via a kernel + * trap-and-emulate ABI. In this case we define the read permissions + * as actually being PL0_R. However some bits of any given register + * may still be masked. + */ #ifdef CONFIG_USER_ONLY -#define PL0U_R PL0_R + PL0U_R = PL0_R, #else -#define PL0U_R PL1_R + PL0U_R = PL1_R, #endif -#define PL3_RW (PL3_R | PL3_W) -#define PL2_RW (PL2_R | PL2_W) -#define PL1_RW (PL1_R | PL1_W) -#define PL0_RW (PL0_R | PL0_W) + PL3_RW = PL3_R | PL3_W, + PL2_RW = PL2_R | PL2_W, + PL1_RW = PL1_R | PL1_W, + PL0_RW = PL0_R | PL0_W, +} CPAccessRights; typedef enum CPAccessResult { /* Access is permitted */ @@ -XXX,XX +XXX,XX @@ struct ARMCPRegInfo { /* Register type: ARM_CP_* bits/values */ int type; /* Access rights: PL*_[RW] */ - int access; + CPAccessRights access; /* Security state: ARM_CP_SECSTATE_* bits/values */ int secure; /* diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu, * to encompass the generic architectural permission check. */ if (r->state != ARM_CP_STATE_AA32) { - int mask = 0; + CPAccessRights mask; switch (r->opc1) { case 0: /* min_EL EL1, but some accessible to EL0 via kernel ABI */ -- 2.25.1
From: Richard Henderson <richard.henderson@linaro.org> Give this enum a name and use in ARMCPRegInfo, add_cpreg_to_hashtable and define_one_arm_cp_reg_with_opaque. Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20220501055028.646596-9-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/cpregs.h | 6 +++--- target/arm/helper.c | 6 ++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -XXX,XX +XXX,XX @@ enum { * Note that we rely on the values of these enums as we iterate through * the various states in some places. */ -enum { +typedef enum { ARM_CP_STATE_AA32 = 0, ARM_CP_STATE_AA64 = 1, ARM_CP_STATE_BOTH = 2, -}; +} CPState; /* * ARM CP register secure state flags. These flags identify security state @@ -XXX,XX +XXX,XX @@ struct ARMCPRegInfo { uint8_t opc1; uint8_t opc2; /* Execution state in which this register is visible: ARM_CP_STATE_* */ - int state; + CPState state; /* Register type: ARM_CP_* bits/values */ int type; /* Access rights: PL*_[RW] */ diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) } static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, - void *opaque, int state, int secstate, + void *opaque, CPState state, int secstate, int crm, int opc1, int opc2, const char *name) { @@ -XXX,XX +XXX,XX @@ void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu, * bits; the ARM_CP_64BIT* flag applies only to the AArch32 view of * the register, if any. */ - int crm, opc1, opc2, state; + int crm, opc1, opc2; int crmmin = (r->crm == CP_ANY) ? 0 : r->crm; int crmmax = (r->crm == CP_ANY) ? 15 : r->crm; int opc1min = (r->opc1 == CP_ANY) ? 0 : r->opc1; int opc1max = (r->opc1 == CP_ANY) ? 7 : r->opc1; int opc2min = (r->opc2 == CP_ANY) ? 0 : r->opc2; int opc2max = (r->opc2 == CP_ANY) ? 7 : r->opc2; + CPState state; + /* 64 bit registers have only CRm and Opc1 fields */ assert(!((r->type & ARM_CP_64BIT) && (r->opc2 || r->crn))); /* op0 only exists in the AArch64 encodings */ -- 2.25.1
From: Richard Henderson <richard.henderson@linaro.org> Give this enum a name and use in ARMCPRegInfo and add_cpreg_to_hashtable. Add the enumerator ARM_CP_SECSTATE_BOTH to clarify how 0 is handled in define_one_arm_cp_reg_with_opaque. Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20220501055028.646596-10-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/cpregs.h | 7 ++++--- target/arm/helper.c | 7 +++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -XXX,XX +XXX,XX @@ typedef enum { * registered entry will only have one to identify whether the entry is secure * or non-secure. */ -enum { +typedef enum { + ARM_CP_SECSTATE_BOTH = 0, /* define one cpreg for each secstate */ ARM_CP_SECSTATE_S = (1 << 0), /* bit[0]: Secure state register */ ARM_CP_SECSTATE_NS = (1 << 1), /* bit[1]: Non-secure state register */ -}; +} CPSecureState; /* * Access rights: @@ -XXX,XX +XXX,XX @@ struct ARMCPRegInfo { /* Access rights: PL*_[RW] */ CPAccessRights access; /* Security state: ARM_CP_SECSTATE_* bits/values */ - int secure; + CPSecureState secure; /* * The opaque pointer passed to define_arm_cp_regs_with_opaque() when * this register was defined: can be used to hand data through to the diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) } static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, - void *opaque, CPState state, int secstate, + void *opaque, CPState state, + CPSecureState secstate, int crm, int opc1, int opc2, const char *name) { @@ -XXX,XX +XXX,XX @@ void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu, r->secure, crm, opc1, opc2, r->name); break; - default: + case ARM_CP_SECSTATE_BOTH: name = g_strdup_printf("%s_S", r->name); add_cpreg_to_hashtable(cpu, r, opaque, state, ARM_CP_SECSTATE_S, @@ -XXX,XX +XXX,XX @@ void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu, ARM_CP_SECSTATE_NS, crm, opc1, opc2, r->name); break; + default: + g_assert_not_reached(); } } else { /* AArch64 registers get mapped to non-secure instance -- 2.25.1
From: Richard Henderson <richard.henderson@linaro.org> The new_key field is always non-zero -- drop the if. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20220501055028.646596-11-richard.henderson@linaro.org [PMM: reinstated dropped PL3_RW mask] Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/helper.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ static void define_arm_vh_e2h_redirects_aliases(ARMCPU *cpu) for (i = 0; i < ARRAY_SIZE(aliases); i++) { const struct E2HAlias *a = &aliases[i]; - ARMCPRegInfo *src_reg, *dst_reg; + ARMCPRegInfo *src_reg, *dst_reg, *new_reg; + uint32_t *new_key; + bool ok; if (a->feature && !a->feature(&cpu->isar)) { continue; @@ -XXX,XX +XXX,XX @@ static void define_arm_vh_e2h_redirects_aliases(ARMCPU *cpu) g_assert(src_reg->opaque == NULL); /* Create alias before redirection so we dup the right data. */ - if (a->new_key) { - ARMCPRegInfo *new_reg = g_memdup(src_reg, sizeof(ARMCPRegInfo)); - uint32_t *new_key = g_memdup(&a->new_key, sizeof(uint32_t)); - bool ok; + new_reg = g_memdup(src_reg, sizeof(ARMCPRegInfo)); + new_key = g_memdup(&a->new_key, sizeof(uint32_t)); - new_reg->name = a->new_name; - new_reg->type |= ARM_CP_ALIAS; - /* Remove PL1/PL0 access, leaving PL2/PL3 R/W in place. */ - new_reg->access &= PL2_RW | PL3_RW; + new_reg->name = a->new_name; + new_reg->type |= ARM_CP_ALIAS; + /* Remove PL1/PL0 access, leaving PL2/PL3 R/W in place. */ + new_reg->access &= PL2_RW | PL3_RW; - ok = g_hash_table_insert(cpu->cp_regs, new_key, new_reg); - g_assert(ok); - } + ok = g_hash_table_insert(cpu->cp_regs, new_key, new_reg); + g_assert(ok); src_reg->opaque = dst_reg; src_reg->orig_readfn = src_reg->readfn ?: raw_read; -- 2.25.1
From: Richard Henderson <richard.henderson@linaro.org> Cast the uint32_t key into a gpointer directly, which allows us to avoid allocating storage for each key. Use g_hash_table_lookup when we already have a gpointer (e.g. for callbacks like count_cpreg), or when using get_arm_cp_reginfo would require casting away const. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20220501055028.646596-12-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/cpu.c | 4 ++-- target/arm/gdbstub.c | 2 +- target/arm/helper.c | 41 ++++++++++++++++++----------------------- 3 files changed, 21 insertions(+), 26 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -XXX,XX +XXX,XX @@ static void arm_cpu_initfn(Object *obj) ARMCPU *cpu = ARM_CPU(obj); cpu_set_cpustate_pointers(cpu); - cpu->cp_regs = g_hash_table_new_full(g_int_hash, g_int_equal, - g_free, cpreg_hashtable_data_destroy); + cpu->cp_regs = g_hash_table_new_full(g_direct_hash, g_direct_equal, + NULL, cpreg_hashtable_data_destroy); QLIST_INIT(&cpu->pre_el_change_hooks); QLIST_INIT(&cpu->el_change_hooks); diff --git a/target/arm/gdbstub.c b/target/arm/gdbstub.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/gdbstub.c +++ b/target/arm/gdbstub.c @@ -XXX,XX +XXX,XX @@ static void arm_gen_one_xml_sysreg_tag(GString *s, DynamicGDBXMLInfo *dyn_xml, static void arm_register_sysreg_for_xml(gpointer key, gpointer value, gpointer p) { - uint32_t ri_key = *(uint32_t *)key; + uint32_t ri_key = (uintptr_t)key; ARMCPRegInfo *ri = value; RegisterSysregXmlParam *param = (RegisterSysregXmlParam *)p; GString *s = param->s; diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ bool write_list_to_cpustate(ARMCPU *cpu) static void add_cpreg_to_list(gpointer key, gpointer opaque) { ARMCPU *cpu = opaque; - uint64_t regidx; - const ARMCPRegInfo *ri; - - regidx = *(uint32_t *)key; - ri = get_arm_cp_reginfo(cpu->cp_regs, regidx); + uint32_t regidx = (uintptr_t)key; + const ARMCPRegInfo *ri = get_arm_cp_reginfo(cpu->cp_regs, regidx); if (!(ri->type & (ARM_CP_NO_RAW|ARM_CP_ALIAS))) { cpu->cpreg_indexes[cpu->cpreg_array_len] = cpreg_to_kvm_id(regidx); @@ -XXX,XX +XXX,XX @@ static void add_cpreg_to_list(gpointer key, gpointer opaque) static void count_cpreg(gpointer key, gpointer opaque) { ARMCPU *cpu = opaque; - uint64_t regidx; const ARMCPRegInfo *ri; - regidx = *(uint32_t *)key; - ri = get_arm_cp_reginfo(cpu->cp_regs, regidx); + ri = g_hash_table_lookup(cpu->cp_regs, key); if (!(ri->type & (ARM_CP_NO_RAW|ARM_CP_ALIAS))) { cpu->cpreg_array_len++; @@ -XXX,XX +XXX,XX @@ static void count_cpreg(gpointer key, gpointer opaque) static gint cpreg_key_compare(gconstpointer a, gconstpointer b) { - uint64_t aidx = cpreg_to_kvm_id(*(uint32_t *)a); - uint64_t bidx = cpreg_to_kvm_id(*(uint32_t *)b); + uint64_t aidx = cpreg_to_kvm_id((uintptr_t)a); + uint64_t bidx = cpreg_to_kvm_id((uintptr_t)b); if (aidx > bidx) { return 1; @@ -XXX,XX +XXX,XX @@ static void define_arm_vh_e2h_redirects_aliases(ARMCPU *cpu) for (i = 0; i < ARRAY_SIZE(aliases); i++) { const struct E2HAlias *a = &aliases[i]; ARMCPRegInfo *src_reg, *dst_reg, *new_reg; - uint32_t *new_key; bool ok; if (a->feature && !a->feature(&cpu->isar)) { continue; } - src_reg = g_hash_table_lookup(cpu->cp_regs, &a->src_key); - dst_reg = g_hash_table_lookup(cpu->cp_regs, &a->dst_key); + src_reg = g_hash_table_lookup(cpu->cp_regs, + (gpointer)(uintptr_t)a->src_key); + dst_reg = g_hash_table_lookup(cpu->cp_regs, + (gpointer)(uintptr_t)a->dst_key); g_assert(src_reg != NULL); g_assert(dst_reg != NULL); @@ -XXX,XX +XXX,XX @@ static void define_arm_vh_e2h_redirects_aliases(ARMCPU *cpu) /* Create alias before redirection so we dup the right data. */ new_reg = g_memdup(src_reg, sizeof(ARMCPRegInfo)); - new_key = g_memdup(&a->new_key, sizeof(uint32_t)); new_reg->name = a->new_name; new_reg->type |= ARM_CP_ALIAS; /* Remove PL1/PL0 access, leaving PL2/PL3 R/W in place. */ new_reg->access &= PL2_RW | PL3_RW; - ok = g_hash_table_insert(cpu->cp_regs, new_key, new_reg); + ok = g_hash_table_insert(cpu->cp_regs, + (gpointer)(uintptr_t)a->new_key, new_reg); g_assert(ok); src_reg->opaque = dst_reg; @@ -XXX,XX +XXX,XX @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, /* Private utility function for define_one_arm_cp_reg_with_opaque(): * add a single reginfo struct to the hash table. */ - uint32_t *key = g_new(uint32_t, 1); + uint32_t key; ARMCPRegInfo *r2 = g_memdup(r, sizeof(ARMCPRegInfo)); int is64 = (r->type & ARM_CP_64BIT) ? 1 : 0; int ns = (secstate & ARM_CP_SECSTATE_NS) ? 1 : 0; @@ -XXX,XX +XXX,XX @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, if (r->cp == 0 || r->state == ARM_CP_STATE_BOTH) { r2->cp = CP_REG_ARM64_SYSREG_CP; } - *key = ENCODE_AA64_CP_REG(r2->cp, r2->crn, crm, - r2->opc0, opc1, opc2); + key = ENCODE_AA64_CP_REG(r2->cp, r2->crn, crm, + r2->opc0, opc1, opc2); } else { - *key = ENCODE_CP_REG(r2->cp, is64, ns, r2->crn, crm, opc1, opc2); + key = ENCODE_CP_REG(r2->cp, is64, ns, r2->crn, crm, opc1, opc2); } if (opaque) { r2->opaque = opaque; @@ -XXX,XX +XXX,XX @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, * requested. */ if (!(r->type & ARM_CP_OVERRIDE)) { - ARMCPRegInfo *oldreg; - oldreg = g_hash_table_lookup(cpu->cp_regs, key); + const ARMCPRegInfo *oldreg = get_arm_cp_reginfo(cpu->cp_regs, key); if (oldreg && !(oldreg->type & ARM_CP_OVERRIDE)) { fprintf(stderr, "Register redefined: cp=%d %d bit " "crn=%d crm=%d opc1=%d opc2=%d, " @@ -XXX,XX +XXX,XX @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, g_assert_not_reached(); } } - g_hash_table_insert(cpu->cp_regs, key, r2); + g_hash_table_insert(cpu->cp_regs, (gpointer)(uintptr_t)key, r2); } @@ -XXX,XX +XXX,XX @@ void modify_arm_cp_regs_with_len(ARMCPRegInfo *regs, size_t regs_len, const ARMCPRegInfo *get_arm_cp_reginfo(GHashTable *cpregs, uint32_t encoded_cp) { - return g_hash_table_lookup(cpregs, &encoded_cp); + return g_hash_table_lookup(cpregs, (gpointer)(uintptr_t)encoded_cp); } void arm_cp_write_ignore(CPUARMState *env, const ARMCPRegInfo *ri, -- 2.25.1
From: Richard Henderson <richard.henderson@linaro.org> Simplify freeing cp_regs hash table entries by using a single allocation for the entire value. This fixes a theoretical bug if we were to ever free the entire hash table, because we've been installing string literal constants into the cpreg structure in define_arm_vh_e2h_redirects_aliases. However, at present we only free entries created for AArch32 wildcard cpregs which get overwritten by more specific cpregs, so this bug is never exposed. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20220501055028.646596-13-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/cpu.c | 16 +--------------- target/arm/helper.c | 10 ++++++++-- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -XXX,XX +XXX,XX @@ uint64_t arm_cpu_mp_affinity(int idx, uint8_t clustersz) return (Aff1 << ARM_AFF1_SHIFT) | Aff0; } -static void cpreg_hashtable_data_destroy(gpointer data) -{ - /* - * Destroy function for cpu->cp_regs hashtable data entries. - * We must free the name string because it was g_strdup()ed in - * add_cpreg_to_hashtable(). It's OK to cast away the 'const' - * from r->name because we know we definitely allocated it. - */ - ARMCPRegInfo *r = data; - - g_free((void *)r->name); - g_free(r); -} - static void arm_cpu_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); cpu_set_cpustate_pointers(cpu); cpu->cp_regs = g_hash_table_new_full(g_direct_hash, g_direct_equal, - NULL, cpreg_hashtable_data_destroy); + NULL, g_free); QLIST_INIT(&cpu->pre_el_change_hooks); QLIST_INIT(&cpu->el_change_hooks); diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, * add a single reginfo struct to the hash table. */ uint32_t key; - ARMCPRegInfo *r2 = g_memdup(r, sizeof(ARMCPRegInfo)); + ARMCPRegInfo *r2; int is64 = (r->type & ARM_CP_64BIT) ? 1 : 0; int ns = (secstate & ARM_CP_SECSTATE_NS) ? 1 : 0; + size_t name_len; + + /* Combine cpreg and name into one allocation. */ + name_len = strlen(name) + 1; + r2 = g_malloc(sizeof(*r2) + name_len); + *r2 = *r; + r2->name = memcpy(r2 + 1, name, name_len); - r2->name = g_strdup(name); /* Reset the secure state to the specific incoming state. This is * necessary as the register may have been defined with both states. */ -- 2.25.1
From: Richard Henderson <richard.henderson@linaro.org> Move the computation of key to the top of the function. Hoist the resolution of cp as well, as an input to the computation of key. This will be required by a subsequent patch. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20220501055028.646596-14-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/helper.c | 49 +++++++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, ARMCPRegInfo *r2; int is64 = (r->type & ARM_CP_64BIT) ? 1 : 0; int ns = (secstate & ARM_CP_SECSTATE_NS) ? 1 : 0; + int cp = r->cp; size_t name_len; + switch (state) { + case ARM_CP_STATE_AA32: + /* We assume it is a cp15 register if the .cp field is left unset. */ + if (cp == 0 && r->state == ARM_CP_STATE_BOTH) { + cp = 15; + } + key = ENCODE_CP_REG(cp, is64, ns, r->crn, crm, opc1, opc2); + break; + case ARM_CP_STATE_AA64: + /* + * To allow abbreviation of ARMCPRegInfo definitions, we treat + * cp == 0 as equivalent to the value for "standard guest-visible + * sysreg". STATE_BOTH definitions are also always "standard sysreg" + * in their AArch64 view (the .cp value may be non-zero for the + * benefit of the AArch32 view). + */ + if (cp == 0 || r->state == ARM_CP_STATE_BOTH) { + cp = CP_REG_ARM64_SYSREG_CP; + } + key = ENCODE_AA64_CP_REG(cp, r->crn, crm, r->opc0, opc1, opc2); + break; + default: + g_assert_not_reached(); + } + /* Combine cpreg and name into one allocation. */ name_len = strlen(name) + 1; r2 = g_malloc(sizeof(*r2) + name_len); @@ -XXX,XX +XXX,XX @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, } if (r->state == ARM_CP_STATE_BOTH) { - /* We assume it is a cp15 register if the .cp field is left unset. - */ - if (r2->cp == 0) { - r2->cp = 15; - } - #if HOST_BIG_ENDIAN if (r2->fieldoffset) { r2->fieldoffset += sizeof(uint32_t); @@ -XXX,XX +XXX,XX @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, #endif } } - if (state == ARM_CP_STATE_AA64) { - /* To allow abbreviation of ARMCPRegInfo - * definitions, we treat cp == 0 as equivalent to - * the value for "standard guest-visible sysreg". - * STATE_BOTH definitions are also always "standard - * sysreg" in their AArch64 view (the .cp value may - * be non-zero for the benefit of the AArch32 view). - */ - if (r->cp == 0 || r->state == ARM_CP_STATE_BOTH) { - r2->cp = CP_REG_ARM64_SYSREG_CP; - } - key = ENCODE_AA64_CP_REG(r2->cp, r2->crn, crm, - r2->opc0, opc1, opc2); - } else { - key = ENCODE_CP_REG(r2->cp, is64, ns, r2->crn, crm, opc1, opc2); - } if (opaque) { r2->opaque = opaque; } @@ -XXX,XX +XXX,XX @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, /* Make sure reginfo passed to helpers for wildcarded regs * has the correct crm/opc1/opc2 for this reg, not CP_ANY: */ + r2->cp = cp; r2->crm = crm; r2->opc1 = opc1; r2->opc2 = opc2; -- 2.25.1
From: Richard Henderson <richard.henderson@linaro.org> Put most of the value writeback to the same place, and improve the comment that goes with them. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20220501055028.646596-15-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/helper.c | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, *r2 = *r; r2->name = memcpy(r2 + 1, name, name_len); - /* Reset the secure state to the specific incoming state. This is - * necessary as the register may have been defined with both states. + /* + * Update fields to match the instantiation, overwiting wildcards + * such as CP_ANY, ARM_CP_STATE_BOTH, or ARM_CP_SECSTATE_BOTH. */ + r2->cp = cp; + r2->crm = crm; + r2->opc1 = opc1; + r2->opc2 = opc2; + r2->state = state; r2->secure = secstate; + if (opaque) { + r2->opaque = opaque; + } if (r->bank_fieldoffsets[0] && r->bank_fieldoffsets[1]) { /* Register is banked (using both entries in array). @@ -XXX,XX +XXX,XX @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, #endif } } - if (opaque) { - r2->opaque = opaque; - } - /* reginfo passed to helpers is correct for the actual access, - * and is never ARM_CP_STATE_BOTH: - */ - r2->state = state; - /* Make sure reginfo passed to helpers for wildcarded regs - * has the correct crm/opc1/opc2 for this reg, not CP_ANY: - */ - r2->cp = cp; - r2->crm = crm; - r2->opc1 = opc1; - r2->opc2 = opc2; + /* By convention, for wildcarded registers only the first * entry is used for migration; the others are marked as * ALIAS so we don't try to transfer the register -- 2.25.1
From: Richard Henderson <richard.henderson@linaro.org> Bool is a more appropriate type for these variables. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20220501055028.646596-16-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/helper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, */ uint32_t key; ARMCPRegInfo *r2; - int is64 = (r->type & ARM_CP_64BIT) ? 1 : 0; - int ns = (secstate & ARM_CP_SECSTATE_NS) ? 1 : 0; + bool is64 = r->type & ARM_CP_64BIT; + bool ns = secstate & ARM_CP_SECSTATE_NS; int cp = r->cp; size_t name_len; -- 2.25.1
From: Richard Henderson <richard.henderson@linaro.org> Computing isbanked only once makes the code a bit easier to read. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20220501055028.646596-17-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/helper.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, bool is64 = r->type & ARM_CP_64BIT; bool ns = secstate & ARM_CP_SECSTATE_NS; int cp = r->cp; + bool isbanked; size_t name_len; switch (state) { @@ -XXX,XX +XXX,XX @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, r2->opaque = opaque; } - if (r->bank_fieldoffsets[0] && r->bank_fieldoffsets[1]) { + isbanked = r->bank_fieldoffsets[0] && r->bank_fieldoffsets[1]; + if (isbanked) { /* Register is banked (using both entries in array). * Overwriting fieldoffset as the array is only used to define * banked registers but later only fieldoffset is used. @@ -XXX,XX +XXX,XX @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, } if (state == ARM_CP_STATE_AA32) { - if (r->bank_fieldoffsets[0] && r->bank_fieldoffsets[1]) { + if (isbanked) { /* If the register is banked then we don't need to migrate or * reset the 32-bit instance in certain cases: * -- 2.25.1
From: Richard Henderson <richard.henderson@linaro.org> Perform the override check early, so that it is still done even when we decide to discard an unreachable cpreg. Use assert not printf+abort. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20220501055028.646596-18-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/helper.c | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, g_assert_not_reached(); } + /* Overriding of an existing definition must be explicitly requested. */ + if (!(r->type & ARM_CP_OVERRIDE)) { + const ARMCPRegInfo *oldreg = get_arm_cp_reginfo(cpu->cp_regs, key); + if (oldreg) { + assert(oldreg->type & ARM_CP_OVERRIDE); + } + } + /* Combine cpreg and name into one allocation. */ name_len = strlen(name) + 1; r2 = g_malloc(sizeof(*r2) + name_len); @@ -XXX,XX +XXX,XX @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, assert(!raw_accessors_invalid(r2)); } - /* Overriding of an existing definition must be explicitly - * requested. - */ - if (!(r->type & ARM_CP_OVERRIDE)) { - const ARMCPRegInfo *oldreg = get_arm_cp_reginfo(cpu->cp_regs, key); - if (oldreg && !(oldreg->type & ARM_CP_OVERRIDE)) { - fprintf(stderr, "Register redefined: cp=%d %d bit " - "crn=%d crm=%d opc1=%d opc2=%d, " - "was %s, now %s\n", r2->cp, 32 + 32 * is64, - r2->crn, r2->crm, r2->opc1, r2->opc2, - oldreg->name, r2->name); - g_assert_not_reached(); - } - } g_hash_table_insert(cpu->cp_regs, (gpointer)(uintptr_t)key, r2); } -- 2.25.1
From: Richard Henderson <richard.henderson@linaro.org> Put the block comments into the current coding style. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20220501055028.646596-19-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/helper.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) return cpu_list; } +/* + * Private utility function for define_one_arm_cp_reg_with_opaque(): + * add a single reginfo struct to the hash table. + */ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, void *opaque, CPState state, CPSecureState secstate, int crm, int opc1, int opc2, const char *name) { - /* Private utility function for define_one_arm_cp_reg_with_opaque(): - * add a single reginfo struct to the hash table. - */ uint32_t key; ARMCPRegInfo *r2; bool is64 = r->type & ARM_CP_64BIT; @@ -XXX,XX +XXX,XX @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, isbanked = r->bank_fieldoffsets[0] && r->bank_fieldoffsets[1]; if (isbanked) { - /* Register is banked (using both entries in array). + /* + * Register is banked (using both entries in array). * Overwriting fieldoffset as the array is only used to define * banked registers but later only fieldoffset is used. */ @@ -XXX,XX +XXX,XX @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, if (state == ARM_CP_STATE_AA32) { if (isbanked) { - /* If the register is banked then we don't need to migrate or + /* + * If the register is banked then we don't need to migrate or * reset the 32-bit instance in certain cases: * * 1) If the register has both 32-bit and 64-bit instances then we @@ -XXX,XX +XXX,XX @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, r2->type |= ARM_CP_ALIAS; } } else if ((secstate != r->secure) && !ns) { - /* The register is not banked so we only want to allow migration of - * the non-secure instance. + /* + * The register is not banked so we only want to allow migration + * of the non-secure instance. */ r2->type |= ARM_CP_ALIAS; } @@ -XXX,XX +XXX,XX @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, } } - /* By convention, for wildcarded registers only the first + /* + * By convention, for wildcarded registers only the first * entry is used for migration; the others are marked as * ALIAS so we don't try to transfer the register * multiple times. Special registers (ie NOP/WFI) are @@ -XXX,XX +XXX,XX @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, r2->type |= ARM_CP_ALIAS | ARM_CP_NO_GDB; } - /* Check that raw accesses are either forbidden or handled. Note that + /* + * Check that raw accesses are either forbidden or handled. Note that * we can't assert this earlier because the setup of fieldoffset for * banked registers has to be done first. */ -- 2.25.1
From: Richard Henderson <richard.henderson@linaro.org> Since e03b56863d2bc, our host endian indicator is unconditionally set, which means that we can use a normal C condition. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20220501055028.646596-20-richard.henderson@linaro.org [PMM: quote correct git hash in commit message] Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/helper.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, r2->type |= ARM_CP_ALIAS; } - if (r->state == ARM_CP_STATE_BOTH) { -#if HOST_BIG_ENDIAN - if (r2->fieldoffset) { - r2->fieldoffset += sizeof(uint32_t); - } -#endif + if (HOST_BIG_ENDIAN && + r->state == ARM_CP_STATE_BOTH && r2->fieldoffset) { + r2->fieldoffset += sizeof(uint32_t); } } -- 2.25.1
From: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20220501055028.646596-24-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/cpu.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -XXX,XX +XXX,XX @@ static inline bool isar_feature_aa32_ssbs(const ARMISARegisters *id) return FIELD_EX32(id->id_pfr2, ID_PFR2, SSBS) != 0; } +static inline bool isar_feature_aa32_debugv8p2(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_dfr0, ID_DFR0, COPDBG) >= 8; +} + /* * 64-bit feature tests via id registers. */ @@ -XXX,XX +XXX,XX @@ static inline bool isar_feature_aa64_ssbs(const ARMISARegisters *id) return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, SSBS) != 0; } +static inline bool isar_feature_aa64_debugv8p2(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64dfr0, ID_AA64DFR0, DEBUGVER) >= 8; +} + static inline bool isar_feature_aa64_sve2(const ARMISARegisters *id) { return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, SVEVER) != 0; @@ -XXX,XX +XXX,XX @@ static inline bool isar_feature_any_tts2uxn(const ARMISARegisters *id) return isar_feature_aa64_tts2uxn(id) || isar_feature_aa32_tts2uxn(id); } +static inline bool isar_feature_any_debugv8p2(const ARMISARegisters *id) +{ + return isar_feature_aa64_debugv8p2(id) || isar_feature_aa32_debugv8p2(id); +} + /* * Forward to the above feature tests given an ARMCPU pointer. */ -- 2.25.1
From: Richard Henderson <richard.henderson@linaro.org> Add the aa64 predicate for detecting RAS support from id registers. We already have the aa32 version from the M-profile work. Add the 'any' predicate for testing both aa64 and aa32. Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20220501055028.646596-34-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/cpu.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -XXX,XX +XXX,XX @@ static inline bool isar_feature_aa64_aa32_el1(const ARMISARegisters *id) return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, EL1) >= 2; } +static inline bool isar_feature_aa64_ras(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, RAS) != 0; +} + static inline bool isar_feature_aa64_sve(const ARMISARegisters *id) { return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, SVE) != 0; @@ -XXX,XX +XXX,XX @@ static inline bool isar_feature_any_debugv8p2(const ARMISARegisters *id) return isar_feature_aa64_debugv8p2(id) || isar_feature_aa32_debugv8p2(id); } +static inline bool isar_feature_any_ras(const ARMISARegisters *id) +{ + return isar_feature_aa64_ras(id) || isar_feature_aa32_ras(id); +} + /* * Forward to the above feature tests given an ARMCPU pointer. */ -- 2.25.1
From: Alex Zuepke <alex.zuepke@tum.de> The ARMv8 manual defines that PMUSERENR_EL0.ER enables read-access to both PMXEVCNTR_EL0 and PMEVCNTR<n>_EL0 registers, however, we only use it for PMXEVCNTR_EL0. Extend to PMEVCNTR<n>_EL0 as well. Signed-off-by: Alex Zuepke <alex.zuepke@tum.de> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20220428132717.84190-1-alex.zuepke@tum.de Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/helper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ static void define_pmu_regs(ARMCPU *cpu) .crm = 8 | (3 & (i >> 3)), .opc1 = 0, .opc2 = i & 7, .access = PL0_RW, .type = ARM_CP_IO | ARM_CP_ALIAS, .readfn = pmevcntr_readfn, .writefn = pmevcntr_writefn, - .accessfn = pmreg_access }, + .accessfn = pmreg_access_xevcntr }, { .name = pmevcntr_el0_name, .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 8 | (3 & (i >> 3)), - .opc2 = i & 7, .access = PL0_RW, .accessfn = pmreg_access, + .opc2 = i & 7, .access = PL0_RW, .accessfn = pmreg_access_xevcntr, .type = ARM_CP_IO, .readfn = pmevcntr_readfn, .writefn = pmevcntr_writefn, .raw_readfn = pmevcntr_rawread, -- 2.25.1
Hi; here's another arm pullreq; main parts here are the new i.MX 8MM EVK board, and a lot of refactoring. thanks -- PMM The following changes since commit aa15257174da180c6a8a9d58f87319cfe61c5520: Merge tag 'pbouvier/pr/plugins-20260424' of https://gitlab.com/p-b-o/qemu into staging (2026-04-25 10:22:04 -0400) are available in the Git repository at: https://gitlab.com/pm215/qemu.git tags/pull-target-arm-20260427 for you to fetch changes up to 4575da5ecb7a27544aa6da1f78e700e8100ceaea: target/arm: report register in WFIT syndromes (2026-04-27 11:46:34 +0100) ---------------------------------------------------------------- target-arm queue: docs/system: add FEAT_AA32 and FEAT_AA64 to emulation list hw/arm: Add the i.MX 8MM EVK(Evaluation Kit) board target/arm: Build M-profile helper code once only hw/arm: Remove hw_error() for the unimplemented CM_LMBUSCNT register hw: Move ARM_SYSCTL_GPIO definitions to arm sysctl specific header target/arm: Allow 'aarch64=off' to be set for TCG CPUs target/arm: Allow some sysregs to not have to be an exact match for migration hw/arm/raspi4b: NOP all DTB nodes when removing unimplemented devices hw/arm/fsl-imx6ul: Implement LCDIF display device target/arm: Refactor syndrome value code to use registerfields target/arm: Report the register in WFxT syndromes ---------------------------------------------------------------- Alex Bennée (24): docs/system: add FEAT_AA32 and FEAT_AA64 to emulation list target/arm: migrate basic syndrome helpers to registerfields target/arm: migrate system/cp trap syndromes to registerfields target/arm: migrate FP/SIMD trap syndromes to registerfields target/arm: migrate eret trap syndromes to registerfields target/arm: migrate SME trap syndromes to registerfields target/arm: migrate PAC trap syndromes to registerfields target/arm: migrate BTI trap syndromes to registerfields target/arm: migrate BXJ trap syndromes to registerfields target/arm: migrate Granule Protection traps to registerfields target/arm: migrate fault syndromes to registerfields target/arm: migrate debug syndromes to registerfields target/arm: migrate wfx syndromes to registerfields target/arm: migrate gcs syndromes to registerfields target/arm: migrate memory op syndromes to registerfields target/arm: migrate check_hcr_el2_trap to use syndrome helper target/arm: use syndrome helpers in arm_cpu_do_interrupt_aarch32_hyp target/arm: use syndrome helpers to set SAME_EL EC bit target/arm: make whpx use syndrome helpers for decode target/arm: make hvf use syndrome helpers for decode target/arm: use syndrome helpers in merge_syn_data_abort target/arm: use syndrome helpers to query VNCR bit target/arm: remove old syndrome defines target/arm: report register in WFIT syndromes Eric Auger (7): target/arm/cpu: Introduce the infrastructure for cpreg migration tolerances target/arm/machine: Handle ToleranceNotOnBothEnds migration tolerances target/arm/machine: Handle ToleranceOnlySrcTestValue migration tolerance target/arm/cpu64: Mitigate migration failures due to spurious TCR_EL1, PIRE0_EL1 and PIR_EL1 target/arm/cpu64: Define cpreg migration tolerance for KVM_REG_ARM_VENDOR_HYP_BMAP_2 target/arm/helper: Define cpreg migration tolerance for DGBDTR_EL0 Revert "target/arm: Reinstate bogus AArch32 DBGDTRTX register for migration compat" Gaurav Sharma (15): hw/arm: Add the i.MX 8MM EVK(Evaluation Kit) board hw/misc/imx8mp_analog: Add property to analog device hw/arm/fsl-imx8mm: Add Analog device IP to iMX8MM SOC hw/arm/fsl-imx8mm: Add Clock Control Module IP to iMX8MM hw/arm/fsl-imx8mm: Implemented support for SNVS hw/arm/fsl-imx8mm: Adding support for USDHC storage controllers hw/arm/fsl-imx8mm: Add PCIe support hw/arm/fsl-imx8mm: Add GPIO controllers hw/arm/fsl-imx8mm: Adding support for I2C emulation hw/arm/fsl-imx8mm: Adding support for SPI controller hw/arm/fsl-imx8mm: Adding support for Watchdog Timers hw/arm/fsl-imx8mm: Adding support for General Purpose Timers hw/arm/fsl-imx8mm: Adding support for ENET ethernet controller hw/arm/fsl-imx8mm: Adding support for USB controller hw/arm/fsl-imx8mm: Adding functional testing of iMX8MM emulation Osama Abdelkader (1): hw/arm/raspi4b: NOP all DTB nodes when removing unimplemented devices Peter Maydell (4): hw: Move ARM_SYSCTL_GPIO definitions to arm sysctl specific header target/arm: Clear AArch64 ID regs from ARMISARegisters if AArch64 disabled target/arm: Allow 'aarch64=off' to be set for TCG CPUs tests/functional/aarch64: Add basic test of TCG aarch64=off Philippe Mathieu-Daudé (9): target/arm: Ignore endianness when setting MTE tags target/arm: Explode MO_TExx -> MO_TE | MO_xx target/arm: Hoist MO_TE into mve_advance_vpt() target/arm: Hoist MO_TE into MVE DO_VSTR() macro target/arm: Introduce mo_endian() helper target/arm: Replace MO_TE -> mo_endian() for MVE helpers target/arm: Compile mve_helper.c once target/arm: Replace MO_TE -> mo_endian() for Cortex-M helpers target/arm: Compile m_helper.c once Thomas Huth (1): hw/arm: Remove hw_error() for the unimplemented CM_LMBUSCNT register Yucai Liu (2): hw/display: Add i.MX6UL LCDIF device model hw/arm/fsl-imx6ul: Wire in the LCDIF device model MAINTAINERS | 16 +- docs/system/arm/cpu-features.rst | 10 +- docs/system/arm/emulation.rst | 2 + docs/system/arm/{imx8mp-evk.rst => imx8m.rst} | 49 +- docs/system/target-arm.rst | 2 +- hw/arm/Kconfig | 25 + hw/arm/fsl-imx6ul.c | 12 +- hw/arm/fsl-imx8mm.c | 704 ++++++++++++++++++++++ hw/arm/imx8mm-evk.c | 130 ++++ hw/arm/integratorcp.c | 3 - hw/arm/meson.build | 2 + hw/arm/raspi4b.c | 10 +- hw/arm/realview.c | 2 +- hw/arm/vexpress.c | 2 +- hw/arm/virt.c | 1 - hw/display/Kconfig | 4 + hw/display/imx6ul_lcdif.c | 453 ++++++++++++++ hw/display/meson.build | 1 + hw/misc/arm_sysctl.c | 2 +- hw/misc/imx8mp_analog.c | 12 +- hw/timer/imx_gpt.c | 26 + hw/vmapple/vmapple.c | 1 - include/hw/arm/fsl-imx6ul.h | 4 +- include/hw/arm/fsl-imx8mm.h | 242 ++++++++ include/hw/arm/primecell.h | 12 - include/hw/display/imx6ul_lcdif.h | 37 ++ include/hw/misc/arm_sysctl.h | 16 + include/hw/misc/imx8mp_analog.h | 3 + include/hw/timer/imx_gpt.h | 2 + target/arm/cpu-features.h | 5 + target/arm/cpu.c | 153 ++++- target/arm/cpu.h | 4 +- target/arm/cpu64.c | 39 ++ target/arm/debug_helper.c | 29 - target/arm/helper.c | 27 +- target/arm/hvf/hvf.c | 14 +- target/arm/internals.h | 60 ++ target/arm/machine.c | 33 +- target/arm/syndrome.h | 595 ++++++++++++++---- target/arm/tcg/debug.c | 2 +- target/arm/tcg/helper-a64.c | 2 +- target/arm/tcg/helper-defs.h | 2 +- target/arm/tcg/m_helper.c | 8 +- target/arm/tcg/meson.build | 6 +- target/arm/tcg/mve_helper.c | 102 ++-- target/arm/tcg/op_helper.c | 7 +- target/arm/tcg/tlb_helper.c | 6 +- target/arm/tcg/translate-a64.c | 2 +- target/arm/tcg/vfp_helper.c | 5 +- target/arm/trace-events | 2 + target/arm/whpx/whpx-all.c | 13 +- tests/functional/aarch64/meson.build | 3 + tests/functional/aarch64/test_imx8mm_evk.py | 69 +++ tests/functional/aarch64/test_virt_aarch64_off.py | 37 ++ tests/qtest/arm-cpu-features.c | 8 +- 55 files changed, 2743 insertions(+), 275 deletions(-) rename docs/system/arm/{imx8mp-evk.rst => imx8m.rst} (58%) create mode 100644 hw/arm/fsl-imx8mm.c create mode 100644 hw/arm/imx8mm-evk.c create mode 100644 hw/display/imx6ul_lcdif.c create mode 100644 include/hw/arm/fsl-imx8mm.h delete mode 100644 include/hw/arm/primecell.h create mode 100644 include/hw/display/imx6ul_lcdif.h create mode 100644 include/hw/misc/arm_sysctl.h create mode 100755 tests/functional/aarch64/test_imx8mm_evk.py create mode 100755 tests/functional/aarch64/test_virt_aarch64_off.py
From: Alex Bennée <alex.bennee@linaro.org> This is just a documentation tweak as we already support both. FEAT_AA32 implies FEAT_AA32EL0. FEAT_AA64 implies FEAT_AA64EL[0123]. This is however useful if you are using emulation.rst as a source of truth of what QEMU emulates and when cross checking with Features.json from Arm. Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20260421093506.616307-1-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- docs/system/arm/emulation.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index XXXXXXX..XXXXXXX 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -XXX,XX +XXX,XX @@ QEMU's TCG emulation includes support for the Armv5, Armv6, Armv7, Armv8 and Armv9 versions of the A-profile architecture. It also has support for the following architecture extensions: +- FEAT_AA32 (PE Support for AArch32) - FEAT_AA32BF16 (AArch32 BFloat16 instructions) - FEAT_AA32EL0 (Support for AArch32 at EL0) - FEAT_AA32EL1 (Support for AArch32 at EL1) @@ -XXX,XX +XXX,XX @@ the following architecture extensions: - FEAT_AA32EL3 (Support for AArch32 at EL3) - FEAT_AA32HPD (AArch32 hierarchical permission disables) - FEAT_AA32I8MM (AArch32 Int8 matrix multiplication instructions) +- FEAT_AA64 (PE uses AArch64 after last reboot) - FEAT_AA64EL0 (Support for AArch64 at EL0) - FEAT_AA64EL1 (Support for AArch64 at EL1) - FEAT_AA64EL2 (Support for AArch64 at EL2) -- 2.43.0
From: Gaurav Sharma <gaurav.sharma_7@nxp.com> Implemented CPUs, RAM, UARTs and Interrupt Controller Other peripherals are represented as TYPE_UNIMPLEMENTED_DEVICE Complete memory map of the SoC is provided. Set default RAM size to 2GB and default CPU count to 4 to match the real i.MX8MM EVK hardware configuration. Documentation is shared with imx8mp-evk to avoid duplication. Signed-off-by: Gaurav Sharma <gaurav.sharma_7@nxp.com> [PMM: fixed over-long lines in doc] Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Bernhard Beschow <shentey@gmail.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- MAINTAINERS | 11 +- docs/system/arm/{imx8mp-evk.rst => imx8m.rst} | 49 ++- docs/system/target-arm.rst | 2 +- hw/arm/Kconfig | 12 + hw/arm/fsl-imx8mm.c | 377 ++++++++++++++++++ hw/arm/imx8mm-evk.c | 112 ++++++ hw/arm/meson.build | 2 + include/hw/arm/fsl-imx8mm.h | 158 ++++++++ 8 files changed, 709 insertions(+), 14 deletions(-) rename docs/system/arm/{imx8mp-evk.rst => imx8m.rst} (58%) create mode 100644 hw/arm/fsl-imx8mm.c create mode 100644 hw/arm/imx8mm-evk.c create mode 100644 include/hw/arm/fsl-imx8mm.h diff --git a/MAINTAINERS b/MAINTAINERS index XXXXXXX..XXXXXXX 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -XXX,XX +XXX,XX @@ F: hw/pci-host/designware.c F: include/hw/pci-host/designware.h F: docs/system/arm/mcimx7d-sabre.rst +MCIMX8MM-EVK / iMX8MM +M: Gaurav Sharma <gaurav.sharma_7@nxp.com> +L: qemu-arm@nongnu.org +S: Maintained +F: hw/arm/fsl-imx8mm.c +F: hw/arm/imx8mm-evk.c +F: include/hw/arm/fsl-imx8mm.h +F: docs/system/arm/imx8m.rst + MCIMX8MP-EVK / i.MX8MP M: Bernhard Beschow <shentey@gmail.com> L: qemu-arm@nongnu.org @@ -XXX,XX +XXX,XX @@ F: hw/rtc/rs5c372.c F: include/hw/arm/fsl-imx8mp.h F: include/hw/misc/imx8mp_*.h F: include/hw/pci-host/fsl_imx8m_phy.h -F: docs/system/arm/imx8mp-evk.rst +F: docs/system/arm/imx8m.rst F: tests/functional/aarch64/test_imx8mp_evk.py F: tests/qtest/rs5c372-test.c diff --git a/docs/system/arm/imx8mp-evk.rst b/docs/system/arm/imx8m.rst similarity index 58% rename from docs/system/arm/imx8mp-evk.rst rename to docs/system/arm/imx8m.rst index XXXXXXX..XXXXXXX 100644 --- a/docs/system/arm/imx8mp-evk.rst +++ b/docs/system/arm/imx8m.rst @@ -XXX,XX +XXX,XX @@ -NXP i.MX 8M Plus Evaluation Kit (``imx8mp-evk``) -================================================ +NXP i.MX 8M Plus and i.MX 8M Mini Evaluation Kits (``imx8mp-evk``, ``imx8mm-evk``) +================================================================================== -The ``imx8mp-evk`` machine models the i.MX 8M Plus Evaluation Kit, based on an -i.MX 8M Plus SoC. +The ``imx8mp-evk`` and ``imx8mm-evk`` machine models the i.MX 8M Plus +and i.MX 8M Mini Evaluation Kits, based on i.MX 8M Plus and i.MX8M +Mini SoCs. Supported devices ----------------- -The ``imx8mp-evk`` machine implements the following devices: +The ``imx8mp-evk`` and ``imx8mm-evk`` machines implement the +following devices: * Up to 4 Cortex-A53 cores * Generic Interrupt Controller (GICv3) @@ -XXX,XX +XXX,XX @@ The ``imx8mp-evk`` machine implements the following devices: Boot options ------------ -The ``imx8mp-evk`` machine can start a Linux kernel directly using the standard -``-kernel`` functionality. +The ``imx8mp-evk`` and ``imx8mm-evk`` machines can start a Linux +kernel directly using the standard ``-kernel`` functionality. Direct Linux Kernel Boot '''''''''''''''''''''''' @@ -XXX,XX +XXX,XX @@ is to generate an image with Buildroot. Version 2024.11.1 is tested at the time of writing and involves two steps. First run the following commands in the toplevel directory of the Buildroot source tree: +For i.MX 8M Plus EVK: + .. code-block:: bash $ make freescale_imx8mpevk_defconfig $ make +For i.MX 8M Mini EVK: + +.. code-block:: bash + + $ make freescale_imx8mmevk_defconfig + $ make + Once finished successfully there is an ``output/image`` subfolder. Navigate into it and resize the SD card image to a power of two: @@ -XXX,XX +XXX,XX @@ it and resize the SD card image to a power of two: Now that everything is prepared the machine can be started as follows: +For i.MX 8M Plus EVK: + .. code-block:: bash $ qemu-system-aarch64 -M imx8mp-evk \ @@ -XXX,XX +XXX,XX @@ Now that everything is prepared the machine can be started as follows: -append "root=/dev/mmcblk2p2" \ -drive file=sdcard.img,if=sd,bus=2,format=raw,id=mmcblk2 +For i.MX 8M Mini EVK: + +.. code-block:: bash + + $ qemu-system-aarch64 -M imx8mm-evk -smp 4 -m 2G \ + -display none -serial null -serial stdio \ + -kernel Image \ + -dtb imx8mm-evk.dtb \ + -append "root=/dev/mmcblk2p2" \ + -drive file=sdcard.img,if=sd,bus=2,format=raw,id=mmcblk2 KVM Acceleration ---------------- @@ -XXX,XX +XXX,XX @@ To enable hardware-assisted acceleration via KVM, append ``-accel kvm`` to the command line. While this speeds up performance significantly, be aware of the following limitations: -* The ``imx8mp-evk`` machine is not included under the "virtualization use case" - of :doc:`QEMU's security policy </system/security>`. This means that you - should not trust that it can contain malicious guests, whether it is run - using TCG or KVM. If you don't trust your guests and you're relying on QEMU to - be the security boundary, you want to choose another machine such as ``virt``. +* The ``imx8mp-evk`` and ``imx8mm-evk`` machines are not included + under the "virtualization use case" of :doc:`QEMU's security + policy </system/security>`. This means that you should not trust that + it can contain malicious guests, whether it is run using TCG or KVM. + If you don't trust your guests and you're relying on QEMU to be the + security boundary, you want to choose another machine such as + ``virt``. * Rather than Cortex-A53 CPUs, the same CPU type as the host's will be used. This is a limitation of KVM and may not work with guests with a tight dependency on Cortex-A53. diff --git a/docs/system/target-arm.rst b/docs/system/target-arm.rst index XXXXXXX..XXXXXXX 100644 --- a/docs/system/target-arm.rst +++ b/docs/system/target-arm.rst @@ -XXX,XX +XXX,XX @@ Board-specific documentation arm/imx25-pdk arm/mcimx6ul-evk arm/mcimx7d-sabre - arm/imx8mp-evk + arm/imx8m arm/orangepi arm/raspi arm/collie diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -XXX,XX +XXX,XX @@ config FSL_IMX8MP_EVK depends on TCG select FSL_IMX8MP +config FSL_IMX8MM + bool + select ARM_GIC + select IMX + +config FSL_IMX8MM_EVK + bool + default y + depends on AARCH64 + depends on TCG + select FSL_IMX8MM + config ARM_SMMUV3_ACCEL bool depends on ARM_SMMUV3 diff --git a/hw/arm/fsl-imx8mm.c b/hw/arm/fsl-imx8mm.c new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/hw/arm/fsl-imx8mm.c @@ -XXX,XX +XXX,XX @@ +/* + * i.MX 8MM SoC Implementation + * + * Based on hw/arm/fsl-imx6.c + * + * Copyright (c) 2025, NXP Semiconductors + * Author: Gaurav Sharma <gaurav.sharma_7@nxp.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * iMX8MM Reference Manual - https://www.nxp.com/products/i.MX8MMINI -> Documentation + */ + +#include "qemu/osdep.h" +#include "system/address-spaces.h" +#include "hw/arm/bsa.h" +#include "hw/arm/fsl-imx8mm.h" +#include "hw/misc/unimp.h" +#include "hw/core/boards.h" +#include "system/kvm.h" +#include "system/system.h" +#include "target/arm/cpu.h" +#include "target/arm/cpu-qom.h" +#include "target/arm/kvm_arm.h" +#include "qapi/error.h" +#include "qobject/qlist.h" + +static const struct { + hwaddr addr; + size_t size; + const char *name; +} fsl_imx8mm_memmap[] = { + [FSL_IMX8MM_RAM] = { FSL_IMX8MM_RAM_START, FSL_IMX8MM_RAM_SIZE_MAX, "ram" }, + [FSL_IMX8MM_DDR_PHY_BROADCAST] = { 0x3dc00000, 4 * MiB, "ddr_phy_broadcast" }, + [FSL_IMX8MM_DDR_PERF_MON] = { 0x3d800000, 4 * MiB, "ddr_perf_mon" }, + [FSL_IMX8MM_DDR_CTL] = { 0x3d400000, 4 * MiB, "ddr_ctl" }, + [FSL_IMX8MM_DDR_PHY] = { 0x3c000000, 16 * MiB, "ddr_phy" }, + [FSL_IMX8MM_GIC_DIST] = { 0x38800000, 512 * KiB, "gic_dist" }, + [FSL_IMX8MM_GIC_REDIST] = { 0x38880000, 512 * KiB, "gic_redist" }, + [FSL_IMX8MM_VPU] = { 0x38340000, 2 * MiB, "vpu" }, + [FSL_IMX8MM_VPU_BLK_CTRL] = { 0x38330000, 2 * MiB, "vpu_blk_ctrl" }, + [FSL_IMX8MM_VPU_G2_DECODER] = { 0x38310000, 1 * MiB, "vpu_g2_decoder" }, + [FSL_IMX8MM_VPU_G1_DECODER] = { 0x38300000, 1 * MiB, "vpu_g1_decoder" }, + [FSL_IMX8MM_USB2_OTG] = { 0x32e50200, 0x200, "usb2_otg" }, + [FSL_IMX8MM_USB2] = { 0x32e50000, 0x200, "usb2" }, + [FSL_IMX8MM_USB1_OTG] = { 0x32e40200, 0x200, "usb1_otg" }, + [FSL_IMX8MM_USB1] = { 0x32e40000, 0x200, "usb1" }, + [FSL_IMX8MM_GPU2D] = { 0x38000000, 64 * KiB, "gpu2d" }, + [FSL_IMX8MM_QSPI1_RX_BUFFER] = { 0x34000000, 32 * MiB, "qspi1_rx_buffer" }, + [FSL_IMX8MM_PCIE1] = { 0x33800000, 4 * MiB, "pcie1" }, + [FSL_IMX8MM_QSPI1_TX_BUFFER] = { 0x33008000, 32 * KiB, "qspi1_tx_buffer" }, + [FSL_IMX8MM_APBH_DMA] = { 0x33000000, 32 * KiB, "apbh_dma" }, + + /* AIPS-4 Begin */ + [FSL_IMX8MM_TZASC] = { 0x32f80000, 64 * KiB, "tzasc" }, + [FSL_IMX8MM_PCIE_PHY1] = { 0x32f00000, 64 * KiB, "pcie_phy1" }, + [FSL_IMX8MM_MEDIA_BLK_CTL] = { 0x32e28000, 256, "media_blk_ctl" }, + [FSL_IMX8MM_LCDIF] = { 0x32e00000, 64 * KiB, "lcdif" }, + [FSL_IMX8MM_MIPI_DSI] = { 0x32e10000, 64 * KiB, "mipi_dsi" }, + [FSL_IMX8MM_MIPI_CSI] = { 0x32e30000, 64 * KiB, "mipi_csi" }, + [FSL_IMX8MM_AIPS4_CONFIGURATION] = { 0x32df0000, 64 * KiB, "aips4_configuration" }, + /* AIPS-4 End */ + + [FSL_IMX8MM_INTERCONNECT] = { 0x32700000, 1 * MiB, "interconnect" }, + + /* AIPS-3 Begin */ + [FSL_IMX8MM_ENET1] = { 0x30be0000, 64 * KiB, "enet1" }, + [FSL_IMX8MM_SDMA1] = { 0x30bd0000, 64 * KiB, "sdma1" }, + [FSL_IMX8MM_QSPI] = { 0x30bb0000, 64 * KiB, "qspi" }, + [FSL_IMX8MM_USDHC3] = { 0x30b60000, 64 * KiB, "usdhc3" }, + [FSL_IMX8MM_USDHC2] = { 0x30b50000, 64 * KiB, "usdhc2" }, + [FSL_IMX8MM_USDHC1] = { 0x30b40000, 64 * KiB, "usdhc1" }, + [FSL_IMX8MM_SEMAPHORE_HS] = { 0x30ac0000, 64 * KiB, "semaphore_hs" }, + [FSL_IMX8MM_MU_B] = { 0x30ab0000, 64 * KiB, "mu_b" }, + [FSL_IMX8MM_MU_A] = { 0x30aa0000, 64 * KiB, "mu_a" }, + [FSL_IMX8MM_UART4] = { 0x30a60000, 64 * KiB, "uart4" }, + [FSL_IMX8MM_I2C4] = { 0x30a50000, 64 * KiB, "i2c4" }, + [FSL_IMX8MM_I2C3] = { 0x30a40000, 64 * KiB, "i2c3" }, + [FSL_IMX8MM_I2C2] = { 0x30a30000, 64 * KiB, "i2c2" }, + [FSL_IMX8MM_I2C1] = { 0x30a20000, 64 * KiB, "i2c1" }, + [FSL_IMX8MM_AIPS3_CONFIGURATION] = { 0x309f0000, 64 * KiB, "aips3_configuration" }, + [FSL_IMX8MM_CAAM] = { 0x30900000, 256 * KiB, "caam" }, + [FSL_IMX8MM_SPBA1] = { 0x308f0000, 64 * KiB, "spba1" }, + [FSL_IMX8MM_UART2] = { 0x30890000, 64 * KiB, "uart2" }, + [FSL_IMX8MM_UART3] = { 0x30880000, 64 * KiB, "uart3" }, + [FSL_IMX8MM_UART1] = { 0x30860000, 64 * KiB, "uart1" }, + [FSL_IMX8MM_ECSPI3] = { 0x30840000, 64 * KiB, "ecspi3" }, + [FSL_IMX8MM_ECSPI2] = { 0x30830000, 64 * KiB, "ecspi2" }, + [FSL_IMX8MM_ECSPI1] = { 0x30820000, 64 * KiB, "ecspi1" }, + /* AIPS-3 End */ + + /* AIPS-2 Begin */ + [FSL_IMX8MM_QOSC] = { 0x307f0000, 64 * KiB, "qosc" }, + [FSL_IMX8MM_PERFMON2] = { 0x307d0000, 64 * KiB, "perfmon2" }, + [FSL_IMX8MM_PERFMON1] = { 0x307c0000, 64 * KiB, "perfmon1" }, + [FSL_IMX8MM_GPT4] = { 0x30700000, 64 * KiB, "gpt4" }, + [FSL_IMX8MM_GPT5] = { 0x306f0000, 64 * KiB, "gpt5" }, + [FSL_IMX8MM_GPT6] = { 0x306e0000, 64 * KiB, "gpt6" }, + [FSL_IMX8MM_SYSCNT_CTRL] = { 0x306c0000, 64 * KiB, "syscnt_ctrl" }, + [FSL_IMX8MM_SYSCNT_CMP] = { 0x306b0000, 64 * KiB, "syscnt_cmp" }, + [FSL_IMX8MM_SYSCNT_RD] = { 0x306a0000, 64 * KiB, "syscnt_rd" }, + [FSL_IMX8MM_PWM4] = { 0x30690000, 64 * KiB, "pwm4" }, + [FSL_IMX8MM_PWM3] = { 0x30680000, 64 * KiB, "pwm3" }, + [FSL_IMX8MM_PWM2] = { 0x30670000, 64 * KiB, "pwm2" }, + [FSL_IMX8MM_PWM1] = { 0x30660000, 64 * KiB, "pwm1" }, + [FSL_IMX8MM_AIPS2_CONFIGURATION] = { 0x305f0000, 64 * KiB, "aips2_configuration" }, + /* AIPS-2 End */ + + /* AIPS-1 Begin */ + [FSL_IMX8MM_CSU] = { 0x303e0000, 64 * KiB, "csu" }, + [FSL_IMX8MM_RDC] = { 0x303d0000, 64 * KiB, "rdc" }, + [FSL_IMX8MM_SEMAPHORE2] = { 0x303c0000, 64 * KiB, "semaphore2" }, + [FSL_IMX8MM_SEMAPHORE1] = { 0x303b0000, 64 * KiB, "semaphore1" }, + [FSL_IMX8MM_GPC] = { 0x303a0000, 64 * KiB, "gpc" }, + [FSL_IMX8MM_SRC] = { 0x30390000, 64 * KiB, "src" }, + [FSL_IMX8MM_CCM] = { 0x30380000, 64 * KiB, "ccm" }, + [FSL_IMX8MM_SNVS_HP] = { 0x30370000, 64 * KiB, "snvs_hp" }, + [FSL_IMX8MM_ANA_PLL] = { 0x30360000, 64 * KiB, "ana_pll" }, + [FSL_IMX8MM_OCOTP_CTRL] = { 0x30350000, 64 * KiB, "ocotp_ctrl" }, + [FSL_IMX8MM_IOMUXC_GPR] = { 0x30340000, 64 * KiB, "iomuxc_gpr" }, + [FSL_IMX8MM_IOMUXC] = { 0x30330000, 64 * KiB, "iomuxc" }, + [FSL_IMX8MM_GPT3] = { 0x302f0000, 64 * KiB, "gpt3" }, + [FSL_IMX8MM_GPT2] = { 0x302e0000, 64 * KiB, "gpt2" }, + [FSL_IMX8MM_GPT1] = { 0x302d0000, 64 * KiB, "gpt1" }, + [FSL_IMX8MM_SDMA2] = { 0x302c0000, 64 * KiB, "sdma2" }, + [FSL_IMX8MM_SDMA3] = { 0x302b0000, 64 * KiB, "sdma3" }, + [FSL_IMX8MM_WDOG3] = { 0x302a0000, 64 * KiB, "wdog3" }, + [FSL_IMX8MM_WDOG2] = { 0x30290000, 64 * KiB, "wdog2" }, + [FSL_IMX8MM_WDOG1] = { 0x30280000, 64 * KiB, "wdog1" }, + [FSL_IMX8MM_ANA_OSC] = { 0x30270000, 64 * KiB, "ana_osc" }, + [FSL_IMX8MM_ANA_TSENSOR] = { 0x30260000, 64 * KiB, "ana_tsensor" }, + [FSL_IMX8MM_GPIO5] = { 0x30240000, 64 * KiB, "gpio5" }, + [FSL_IMX8MM_GPIO4] = { 0x30230000, 64 * KiB, "gpio4" }, + [FSL_IMX8MM_GPIO3] = { 0x30220000, 64 * KiB, "gpio3" }, + [FSL_IMX8MM_GPIO2] = { 0x30210000, 64 * KiB, "gpio2" }, + [FSL_IMX8MM_GPIO1] = { 0x30200000, 64 * KiB, "gpio1" }, + [FSL_IMX8MM_AIPS1_CONFIGURATION] = { 0x301f0000, 64 * KiB, "aips1_configuration" }, + [FSL_IMX8MM_SAI6] = { 0x30060000, 64 * KiB, "sai6" }, + [FSL_IMX8MM_SAI5] = { 0x30050000, 64 * KiB, "sai5" }, + [FSL_IMX8MM_SAI3] = { 0x30030000, 64 * KiB, "sai3" }, + [FSL_IMX8MM_SAI2] = { 0x30020000, 64 * KiB, "sai2" }, + [FSL_IMX8MM_SAI1] = { 0x30010000, 64 * KiB, "sai1" }, + + /* AIPS-1 End */ + + [FSL_IMX8MM_A53_DAP] = { 0x28000000, 16 * MiB, "a53_dap" }, + [FSL_IMX8MM_PCIE1_MEM] = { 0x18000000, 128 * MiB, "pcie1_mem" }, + [FSL_IMX8MM_QSPI_MEM] = { 0x08000000, 256 * MiB, "qspi_mem" }, + [FSL_IMX8MM_OCRAM] = { 0x00900000, 256 * KiB, "ocram" }, + [FSL_IMX8MM_TCM_DTCM] = { 0x00800000, 128 * KiB, "tcm_dtcm" }, + [FSL_IMX8MM_TCM_ITCM] = { 0x007e0000, 128 * KiB, "tcm_itcm" }, + [FSL_IMX8MM_OCRAM_S] = { 0x00180000, 32 * KiB, "ocram_s" }, + [FSL_IMX8MM_CAAM_MEM] = { 0x00100000, 32 * KiB, "caam_mem" }, + [FSL_IMX8MM_BOOT_ROM_PROTECTED] = { 0x0003f000, 4 * KiB, "boot_rom_protected" }, + [FSL_IMX8MM_BOOT_ROM] = { 0x00000000, 252 * KiB, "boot_rom" }, +}; + +static void fsl_imx8mm_init(Object *obj) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + FslImx8mmState *s = FSL_IMX8MM(obj); + const char *cpu_type = ms->cpu_type ?: ARM_CPU_TYPE_NAME("cortex-a53"); + int i; + + for (i = 0; i < MIN(ms->smp.cpus, FSL_IMX8MM_NUM_CPUS); i++) { + g_autofree char *name = g_strdup_printf("cpu%d", i); + object_initialize_child(obj, name, &s->cpu[i], cpu_type); + } + + object_initialize_child(obj, "gic", &s->gic, gicv3_class_name()); + + for (i = 0; i < FSL_IMX8MM_NUM_UARTS; i++) { + g_autofree char *name = g_strdup_printf("uart%d", i + 1); + object_initialize_child(obj, name, &s->uart[i], TYPE_IMX_SERIAL); + } + +} + +static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + FslImx8mmState *s = FSL_IMX8MM(dev); + DeviceState *gicdev = DEVICE(&s->gic); + int i; + + if (ms->smp.cpus > FSL_IMX8MM_NUM_CPUS) { + error_setg(errp, "%s: Only %d CPUs are supported (%d requested)", + TYPE_FSL_IMX8MM, FSL_IMX8MM_NUM_CPUS, ms->smp.cpus); + return; + } + + /* CPUs */ + for (i = 0; i < ms->smp.cpus; i++) { + /* On uniprocessor, the CBAR is set to 0 */ + if (ms->smp.cpus > 1 && + object_property_find(OBJECT(&s->cpu[i]), "reset-cbar")) { + object_property_set_int(OBJECT(&s->cpu[i]), "reset-cbar", + fsl_imx8mm_memmap[FSL_IMX8MM_GIC_DIST].addr, + &error_abort); + } + + /* + * CNTFID0 base frequency in Hz of system counter + */ + object_property_set_int(OBJECT(&s->cpu[i]), "cntfrq", 8000000, + &error_abort); + + if (object_property_find(OBJECT(&s->cpu[i]), "has_el2")) { + object_property_set_bool(OBJECT(&s->cpu[i]), "has_el2", + !kvm_enabled(), &error_abort); + } + + if (object_property_find(OBJECT(&s->cpu[i]), "has_el3")) { + object_property_set_bool(OBJECT(&s->cpu[i]), "has_el3", + !kvm_enabled(), &error_abort); + } + + if (i) { + /* + * Secondary CPUs start in powered-down state (and can be + * powered up via the SRC system reset controller) + */ + object_property_set_bool(OBJECT(&s->cpu[i]), "start-powered-off", + true, &error_abort); + } + + if (!qdev_realize(DEVICE(&s->cpu[i]), NULL, errp)) { + return; + } + } + + /* GIC */ + { + SysBusDevice *gicsbd = SYS_BUS_DEVICE(&s->gic); + QList *redist_region_count; + bool pmu = object_property_get_bool(OBJECT(first_cpu), "pmu", NULL); + + qdev_prop_set_uint32(gicdev, "num-cpu", ms->smp.cpus); + qdev_prop_set_uint32(gicdev, "num-irq", + FSL_IMX8MM_NUM_IRQS + GIC_INTERNAL); + redist_region_count = qlist_new(); + qlist_append_int(redist_region_count, ms->smp.cpus); + qdev_prop_set_array(gicdev, "redist-region-count", redist_region_count); + object_property_set_link(OBJECT(&s->gic), "sysmem", + OBJECT(get_system_memory()), &error_fatal); + if (!sysbus_realize(gicsbd, errp)) { + return; + } + sysbus_mmio_map(gicsbd, 0, fsl_imx8mm_memmap[FSL_IMX8MM_GIC_DIST].addr); + sysbus_mmio_map(gicsbd, 1, fsl_imx8mm_memmap[FSL_IMX8MM_GIC_REDIST].addr); + + /* + * Wire the outputs from each CPU's generic timer and the GICv3 + * maintenance interrupt signal to the appropriate GIC PPI inputs, and + * the GIC's IRQ/FIQ interrupt outputs to the CPU's inputs. + */ + for (i = 0; i < ms->smp.cpus; i++) { + DeviceState *cpudev = DEVICE(&s->cpu[i]); + int intidbase = FSL_IMX8MM_NUM_IRQS + i * GIC_INTERNAL; + qemu_irq irq; + + /* + * Mapping from the output timer irq lines from the CPU to the + * GIC PPI inputs. + */ + static const int timer_irqs[] = { + [GTIMER_PHYS] = ARCH_TIMER_NS_EL1_IRQ, + [GTIMER_VIRT] = ARCH_TIMER_VIRT_IRQ, + [GTIMER_HYP] = ARCH_TIMER_NS_EL2_IRQ, + [GTIMER_SEC] = ARCH_TIMER_S_EL1_IRQ, + }; + + for (int j = 0; j < ARRAY_SIZE(timer_irqs); j++) { + irq = qdev_get_gpio_in(gicdev, intidbase + timer_irqs[j]); + qdev_connect_gpio_out(cpudev, j, irq); + } + + irq = qdev_get_gpio_in(gicdev, intidbase + ARCH_GIC_MAINT_IRQ); + qdev_connect_gpio_out_named(cpudev, "gicv3-maintenance-interrupt", + 0, irq); + + irq = qdev_get_gpio_in(gicdev, intidbase + VIRTUAL_PMU_IRQ); + qdev_connect_gpio_out_named(cpudev, "pmu-interrupt", 0, irq); + + sysbus_connect_irq(gicsbd, i, + qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); + sysbus_connect_irq(gicsbd, i + ms->smp.cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_FIQ)); + sysbus_connect_irq(gicsbd, i + 2 * ms->smp.cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ)); + sysbus_connect_irq(gicsbd, i + 3 * ms->smp.cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ)); + + if (kvm_enabled()) { + if (pmu) { + assert(arm_feature(&s->cpu[i].env, ARM_FEATURE_PMU)); + if (kvm_irqchip_in_kernel()) { + kvm_arm_pmu_set_irq(&s->cpu[i], VIRTUAL_PMU_IRQ); + } + kvm_arm_pmu_init(&s->cpu[i]); + } + } + } + } + + /* UARTs */ + for (i = 0; i < FSL_IMX8MM_NUM_UARTS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } serial_table[FSL_IMX8MM_NUM_UARTS] = { + { fsl_imx8mm_memmap[FSL_IMX8MM_UART1].addr, FSL_IMX8MM_UART1_IRQ }, + { fsl_imx8mm_memmap[FSL_IMX8MM_UART2].addr, FSL_IMX8MM_UART2_IRQ }, + { fsl_imx8mm_memmap[FSL_IMX8MM_UART3].addr, FSL_IMX8MM_UART3_IRQ }, + { fsl_imx8mm_memmap[FSL_IMX8MM_UART4].addr, FSL_IMX8MM_UART4_IRQ }, + }; + + qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hd(i)); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->uart[i]), errp)) { + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart[i]), 0, serial_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0, + qdev_get_gpio_in(gicdev, serial_table[i].irq)); + } + + /* On-Chip RAM */ + if (!memory_region_init_ram(&s->ocram, OBJECT(dev), "imx8mm.ocram", + fsl_imx8mm_memmap[FSL_IMX8MM_OCRAM].size, + errp)) { + return; + } + memory_region_add_subregion(get_system_memory(), + fsl_imx8mm_memmap[FSL_IMX8MM_OCRAM].addr, + &s->ocram); + + /* Unimplemented devices */ + for (i = 0; i < ARRAY_SIZE(fsl_imx8mm_memmap); i++) { + switch (i) { + case FSL_IMX8MM_GIC_DIST: + case FSL_IMX8MM_GIC_REDIST: + case FSL_IMX8MM_RAM: + case FSL_IMX8MM_OCRAM: + case FSL_IMX8MM_UART1 ... FSL_IMX8MM_UART4: + /* device implemented and treated above */ + break; + + default: + create_unimplemented_device(fsl_imx8mm_memmap[i].name, + fsl_imx8mm_memmap[i].addr, + fsl_imx8mm_memmap[i].size); + break; + } + } +} + +static void fsl_imx8mm_class_init(ObjectClass *oc, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = fsl_imx8mm_realize; + + dc->desc = "i.MX 8MM SoC"; +} + +static const TypeInfo fsl_imx8mm_types[] = { + { + .name = TYPE_FSL_IMX8MM, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(FslImx8mmState), + .instance_init = fsl_imx8mm_init, + .class_init = fsl_imx8mm_class_init, + }, +}; + +DEFINE_TYPES(fsl_imx8mm_types) diff --git a/hw/arm/imx8mm-evk.c b/hw/arm/imx8mm-evk.c new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/hw/arm/imx8mm-evk.c @@ -XXX,XX +XXX,XX @@ +/* + * NXP i.MX 8MM Evaluation Kit System Emulation + * + * Copyright (c) 2025, NXP Semiconductors + * Author: Gaurav Sharma <gaurav.sharma_7@nxp.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "system/address-spaces.h" +#include "hw/arm/boot.h" +#include "hw/arm/fsl-imx8mm.h" +#include "hw/arm/machines-qom.h" +#include "hw/core/boards.h" +#include "hw/core/qdev-properties.h" +#include "system/kvm.h" +#include "system/qtest.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include <libfdt.h> + +static void imx8mm_evk_modify_dtb(const struct arm_boot_info *info, void *fdt) +{ + int i, offset; + + /* Temporarily disable following nodes until they are implemented */ + const char *nodes_to_remove[] = { + "nxp,imx8mm-fspi", + "fsl,imx8mm-mipi-csi", + "fsl,imx8mm-mipi-dsim" + }; + + for (i = 0; i < ARRAY_SIZE(nodes_to_remove); i++) { + const char *dev_str = nodes_to_remove[i]; + + offset = fdt_node_offset_by_compatible(fdt, -1, dev_str); + while (offset >= 0) { + fdt_nop_node(fdt, offset); + offset = fdt_node_offset_by_compatible(fdt, offset, dev_str); + } + } + + /* Remove cpu-idle-states property from CPU nodes */ + offset = fdt_node_offset_by_compatible(fdt, -1, "arm,cortex-a53"); + while (offset >= 0) { + fdt_nop_property(fdt, offset, "cpu-idle-states"); + offset = fdt_node_offset_by_compatible(fdt, offset, "arm,cortex-a53"); + } + + if (kvm_enabled()) { + /* Use system counter frequency from host CPU to fix time in guest */ + offset = fdt_node_offset_by_compatible(fdt, -1, "arm,armv8-timer"); + while (offset >= 0) { + fdt_nop_property(fdt, offset, "clock-frequency"); + offset = fdt_node_offset_by_compatible(fdt, offset, "arm,armv8-timer"); + } + } +} + +static void imx8mm_evk_init(MachineState *machine) +{ + static struct arm_boot_info boot_info; + FslImx8mmState *s; + + if (machine->ram_size > FSL_IMX8MM_RAM_SIZE_MAX) { + error_report("RAM size " RAM_ADDR_FMT " above max supported (%08" PRIx64 ")", + machine->ram_size, FSL_IMX8MM_RAM_SIZE_MAX); + exit(1); + } + + boot_info = (struct arm_boot_info) { + .loader_start = FSL_IMX8MM_RAM_START, + .board_id = -1, + .ram_size = machine->ram_size, + .psci_conduit = QEMU_PSCI_CONDUIT_SMC, + .modify_dtb = imx8mm_evk_modify_dtb, + }; + + s = FSL_IMX8MM(object_new_with_props(TYPE_FSL_IMX8MM, OBJECT(machine), + "soc", &error_fatal, NULL)); + sysbus_realize_and_unref(SYS_BUS_DEVICE(s), &error_fatal); + + memory_region_add_subregion(get_system_memory(), FSL_IMX8MM_RAM_START, + machine->ram); + + if (!qtest_enabled()) { + arm_load_kernel(&s->cpu[0], machine, &boot_info); + } +} + +static const char *imx8mm_evk_get_default_cpu_type(const MachineState *ms) +{ + if (kvm_enabled()) { + return ARM_CPU_TYPE_NAME("host"); + } + + return ARM_CPU_TYPE_NAME("cortex-a53"); +} + +static void imx8mm_evk_machine_init(MachineClass *mc) +{ + mc->desc = "NXP i.MX 8MM EVK Board"; + mc->init = imx8mm_evk_init; + mc->max_cpus = FSL_IMX8MM_NUM_CPUS; + mc->default_cpus = FSL_IMX8MM_NUM_CPUS; + mc->default_ram_size = 2 * GiB; + mc->default_ram_id = "imx8mm-evk.ram"; + mc->get_default_cpu_type = imx8mm_evk_get_default_cpu_type; +} + +DEFINE_MACHINE_AARCH64("imx8mm-evk", imx8mm_evk_machine_init) diff --git a/hw/arm/meson.build b/hw/arm/meson.build index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -XXX,XX +XXX,XX @@ arm_common_ss.add(when: 'CONFIG_ARMSSE', if_true: files('armsse.c')) arm_common_ss.add(when: 'CONFIG_FSL_IMX7', if_true: files('fsl-imx7.c', 'mcimx7d-sabre.c')) arm_common_ss.add(when: 'CONFIG_FSL_IMX8MP', if_true: files('fsl-imx8mp.c')) arm_common_ss.add(when: 'CONFIG_FSL_IMX8MP_EVK', if_true: files('imx8mp-evk.c')) +arm_common_ss.add(when: 'CONFIG_FSL_IMX8MM', if_true: files('fsl-imx8mm.c')) +arm_common_ss.add(when: 'CONFIG_FSL_IMX8MM_EVK', if_true: files('imx8mm-evk.c')) arm_common_ss.add(when: 'CONFIG_ARM_SMMUV3', if_true: files('smmuv3.c')) arm_common_ss.add(when: 'CONFIG_ARM_SMMUV3_ACCEL', if_true: files('smmuv3-accel.c')) stub_ss.add(files('smmuv3-accel-stubs.c')) diff --git a/include/hw/arm/fsl-imx8mm.h b/include/hw/arm/fsl-imx8mm.h new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/include/hw/arm/fsl-imx8mm.h @@ -XXX,XX +XXX,XX @@ +/* + * i.MX 8MM SoC Definitions + * + * Copyright (c) 2025, NXP Semiconductors + * Author: Gaurav Sharma <gaurav.sharma_7@nxp.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef FSL_IMX8MM_H +#define FSL_IMX8MM_H + +#include "cpu.h" +#include "hw/char/imx_serial.h" +#include "hw/intc/arm_gicv3_common.h" +#include "qom/object.h" +#include "qemu/units.h" + +#define TYPE_FSL_IMX8MM "fsl-imx8mm" +OBJECT_DECLARE_SIMPLE_TYPE(FslImx8mmState, FSL_IMX8MM) + +#define FSL_IMX8MM_RAM_START 0x40000000 +#define FSL_IMX8MM_RAM_SIZE_MAX (4 * GiB) + +enum FslImx8mmConfiguration { + FSL_IMX8MM_NUM_CPUS = 4, + FSL_IMX8MM_NUM_IRQS = 128, + FSL_IMX8MM_NUM_UARTS = 4, +}; + +struct FslImx8mmState { + SysBusDevice parent_obj; + + ARMCPU cpu[FSL_IMX8MM_NUM_CPUS]; + GICv3State gic; + IMXSerialState uart[FSL_IMX8MM_NUM_UARTS]; + MemoryRegion ocram; +}; + +enum FslImx8mmMemoryRegions { + FSL_IMX8MM_A53_DAP, + FSL_IMX8MM_AIPS1_CONFIGURATION, + FSL_IMX8MM_AIPS2_CONFIGURATION, + FSL_IMX8MM_AIPS3_CONFIGURATION, + FSL_IMX8MM_AIPS4_CONFIGURATION, + FSL_IMX8MM_ANA_OSC, + FSL_IMX8MM_ANA_PLL, + FSL_IMX8MM_ANA_TSENSOR, + FSL_IMX8MM_APBH_DMA, + FSL_IMX8MM_BOOT_ROM, + FSL_IMX8MM_BOOT_ROM_PROTECTED, + FSL_IMX8MM_CAAM, + FSL_IMX8MM_CAAM_MEM, + FSL_IMX8MM_CCM, + FSL_IMX8MM_CSU, + FSL_IMX8MM_DDR_CTL, + FSL_IMX8MM_DDR_PERF_MON, + FSL_IMX8MM_DDR_PHY, + FSL_IMX8MM_DDR_PHY_BROADCAST, + FSL_IMX8MM_ECSPI1, + FSL_IMX8MM_ECSPI2, + FSL_IMX8MM_ECSPI3, + FSL_IMX8MM_ENET1, + FSL_IMX8MM_GIC_DIST, + FSL_IMX8MM_GIC_REDIST, + FSL_IMX8MM_GPC, + FSL_IMX8MM_GPIO1, + FSL_IMX8MM_GPIO2, + FSL_IMX8MM_GPIO3, + FSL_IMX8MM_GPIO4, + FSL_IMX8MM_GPIO5, + FSL_IMX8MM_GPT1, + FSL_IMX8MM_GPT2, + FSL_IMX8MM_GPT3, + FSL_IMX8MM_GPT4, + FSL_IMX8MM_GPT5, + FSL_IMX8MM_GPT6, + FSL_IMX8MM_GPU2D, + FSL_IMX8MM_I2C1, + FSL_IMX8MM_I2C2, + FSL_IMX8MM_I2C3, + FSL_IMX8MM_I2C4, + FSL_IMX8MM_INTERCONNECT, + FSL_IMX8MM_IOMUXC, + FSL_IMX8MM_IOMUXC_GPR, + FSL_IMX8MM_MEDIA_BLK_CTL, + FSL_IMX8MM_LCDIF, + FSL_IMX8MM_MIPI_CSI, + FSL_IMX8MM_MIPI_DSI, + FSL_IMX8MM_MU_A, + FSL_IMX8MM_MU_B, + FSL_IMX8MM_OCOTP_CTRL, + FSL_IMX8MM_OCRAM, + FSL_IMX8MM_OCRAM_S, + FSL_IMX8MM_PCIE1, + FSL_IMX8MM_PCIE1_MEM, + FSL_IMX8MM_PCIE_PHY1, + FSL_IMX8MM_PERFMON1, + FSL_IMX8MM_PERFMON2, + FSL_IMX8MM_PWM1, + FSL_IMX8MM_PWM2, + FSL_IMX8MM_PWM3, + FSL_IMX8MM_PWM4, + FSL_IMX8MM_QOSC, + FSL_IMX8MM_QSPI, + FSL_IMX8MM_QSPI1_RX_BUFFER, + FSL_IMX8MM_QSPI1_TX_BUFFER, + FSL_IMX8MM_QSPI_MEM, + FSL_IMX8MM_RAM, + FSL_IMX8MM_RDC, + FSL_IMX8MM_SAI1, + FSL_IMX8MM_SAI2, + FSL_IMX8MM_SAI3, + FSL_IMX8MM_SAI5, + FSL_IMX8MM_SAI6, + FSL_IMX8MM_SDMA1, + FSL_IMX8MM_SDMA2, + FSL_IMX8MM_SDMA3, + FSL_IMX8MM_SEMAPHORE1, + FSL_IMX8MM_SEMAPHORE2, + FSL_IMX8MM_SEMAPHORE_HS, + FSL_IMX8MM_SNVS_HP, + FSL_IMX8MM_SPBA1, + FSL_IMX8MM_SRC, + FSL_IMX8MM_SYSCNT_CMP, + FSL_IMX8MM_SYSCNT_CTRL, + FSL_IMX8MM_SYSCNT_RD, + FSL_IMX8MM_TCM_DTCM, + FSL_IMX8MM_TCM_ITCM, + FSL_IMX8MM_TZASC, + FSL_IMX8MM_UART1, + FSL_IMX8MM_UART2, + FSL_IMX8MM_UART3, + FSL_IMX8MM_UART4, + FSL_IMX8MM_USB1, + FSL_IMX8MM_USB2, + FSL_IMX8MM_USB1_OTG, + FSL_IMX8MM_USB2_OTG, + FSL_IMX8MM_USDHC1, + FSL_IMX8MM_USDHC2, + FSL_IMX8MM_USDHC3, + FSL_IMX8MM_VPU, + FSL_IMX8MM_VPU_BLK_CTRL, + FSL_IMX8MM_VPU_G1_DECODER, + FSL_IMX8MM_VPU_G2_DECODER, + FSL_IMX8MM_WDOG1, + FSL_IMX8MM_WDOG2, + FSL_IMX8MM_WDOG3, +}; + +enum FslImx8mmIrqs { + FSL_IMX8MM_UART1_IRQ = 26, + FSL_IMX8MM_UART2_IRQ = 27, + FSL_IMX8MM_UART3_IRQ = 28, + FSL_IMX8MM_UART4_IRQ = 29, +}; + +#endif /* FSL_IMX8MM_H */ -- 2.43.0
From: Gaurav Sharma <gaurav.sharma_7@nxp.com> Add configurable properties for register reset values that differ between i.MX 8M variants (Plus, Mini, etc.). This allows the same device implementation to be shared across multiple SoCs. Properties added: - arm-pll-fdiv-ctl0-reset: ARM PLL divider control reset value Default value is set to match i.MX 8MP reset value (0x000FA031). This can be overridden in the variant like iMX8MM with its own reset value. Reviewed-by: Bernhard Beschow <shentey@gmail.com> Signed-off-by: Gaurav Sharma <gaurav.sharma_7@nxp.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/misc/imx8mp_analog.c | 12 +++++++++++- include/hw/misc/imx8mp_analog.h | 3 +++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/hw/misc/imx8mp_analog.c b/hw/misc/imx8mp_analog.c index XXXXXXX..XXXXXXX 100644 --- a/hw/misc/imx8mp_analog.c +++ b/hw/misc/imx8mp_analog.c @@ -XXX,XX +XXX,XX @@ #include "qemu/log.h" #include "hw/misc/imx8mp_analog.h" +#include "hw/core/qdev-properties.h" #include "migration/vmstate.h" #define ANALOG_PLL_LOCK BIT(31) @@ -XXX,XX +XXX,XX @@ static void imx8mp_analog_reset(DeviceState *dev) s->analog[ANALOG_VPU_PLL_LOCKD_CTRL] = 0x0010003f; s->analog[ANALOG_VPU_PLL_MNIT_CTRL] = 0x00280081; s->analog[ANALOG_ARM_PLL_GEN_CTRL] = 0x00000810; - s->analog[ANALOG_ARM_PLL_FDIV_CTL0] = 0x000fa031; + + /* Use property value instead of hardcoded */ + s->analog[ANALOG_ARM_PLL_FDIV_CTL0] = s->arm_pll_fdiv_ctl0_reset; + s->analog[ANALOG_ARM_PLL_LOCKD_CTRL] = 0x0010003f; s->analog[ANALOG_ARM_PLL_MNIT_CTRL] = 0x00280081; s->analog[ANALOG_SYS_PLL1_GEN_CTRL] = 0x0aaaa810; @@ -XXX,XX +XXX,XX @@ static const VMStateDescription imx8mp_analog_vmstate = { }, }; +static const Property imx8mp_analog_properties[] = { + DEFINE_PROP_UINT32("arm-pll-fdiv-ctl0-reset", IMX8MPAnalogState, + arm_pll_fdiv_ctl0_reset, 0x000fa031), /* imx8mp default */ +}; + static void imx8mp_analog_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); device_class_set_legacy_reset(dc, imx8mp_analog_reset); + device_class_set_props(dc, imx8mp_analog_properties); dc->vmsd = &imx8mp_analog_vmstate; dc->desc = "i.MX 8M Plus Analog Module"; } diff --git a/include/hw/misc/imx8mp_analog.h b/include/hw/misc/imx8mp_analog.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/misc/imx8mp_analog.h +++ b/include/hw/misc/imx8mp_analog.h @@ -XXX,XX +XXX,XX @@ struct IMX8MPAnalogState { } mmio; uint32_t analog[ANALOG_MAX]; + + /* Property for variant-specific reset values */ + uint32_t arm_pll_fdiv_ctl0_reset; }; #endif /* IMX8MP_ANALOG_H */ -- 2.43.0
From: Gaurav Sharma <gaurav.sharma_7@nxp.com> Add the Analog IP to i.MX8MM SoC. iMX8MM and i.MX8MP uses the same Analog IP so the analog ip source will be shared. The ARM PLL divider control register (arm-pll-fdiv-ctl0) has a different reset value on i.MX8MM (0x000fa030) compared to i.MX8MP (0x000fa031). So iMX8MM will be overriding this property with its own reset-value. Reviewed-by: Bernhard Beschow <shentey@gmail.com> Signed-off-by: Gaurav Sharma <gaurav.sharma_7@nxp.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/Kconfig | 1 + hw/arm/fsl-imx8mm.c | 12 ++++++++++++ include/hw/arm/fsl-imx8mm.h | 2 ++ 3 files changed, 15 insertions(+) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -XXX,XX +XXX,XX @@ config FSL_IMX8MP_EVK config FSL_IMX8MM bool select ARM_GIC + select FSL_IMX8MP_ANALOG select IMX config FSL_IMX8MM_EVK diff --git a/hw/arm/fsl-imx8mm.c b/hw/arm/fsl-imx8mm.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/fsl-imx8mm.c +++ b/hw/arm/fsl-imx8mm.c @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_init(Object *obj) object_initialize_child(obj, "gic", &s->gic, gicv3_class_name()); + object_initialize_child(obj, "analog", &s->analog, TYPE_IMX8MP_ANALOG); + for (i = 0; i < FSL_IMX8MM_NUM_UARTS; i++) { g_autofree char *name = g_strdup_printf("uart%d", i + 1); object_initialize_child(obj, name, &s->uart[i], TYPE_IMX_SERIAL); @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) } } + /* Analog */ + object_property_set_uint(OBJECT(&s->analog), "arm-pll-fdiv-ctl0-reset", + 0x000fa030, &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->analog), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->analog), 0, + fsl_imx8mm_memmap[FSL_IMX8MM_ANA_PLL].addr); + /* UARTs */ for (i = 0; i < FSL_IMX8MM_NUM_UARTS; i++) { static const struct { @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) /* Unimplemented devices */ for (i = 0; i < ARRAY_SIZE(fsl_imx8mm_memmap); i++) { switch (i) { + case FSL_IMX8MM_ANA_PLL: case FSL_IMX8MM_GIC_DIST: case FSL_IMX8MM_GIC_REDIST: case FSL_IMX8MM_RAM: diff --git a/include/hw/arm/fsl-imx8mm.h b/include/hw/arm/fsl-imx8mm.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/arm/fsl-imx8mm.h +++ b/include/hw/arm/fsl-imx8mm.h @@ -XXX,XX +XXX,XX @@ #include "cpu.h" #include "hw/char/imx_serial.h" #include "hw/intc/arm_gicv3_common.h" +#include "hw/misc/imx8mp_analog.h" #include "qom/object.h" #include "qemu/units.h" @@ -XXX,XX +XXX,XX @@ struct FslImx8mmState { ARMCPU cpu[FSL_IMX8MM_NUM_CPUS]; GICv3State gic; + IMX8MPAnalogState analog; IMXSerialState uart[FSL_IMX8MM_NUM_UARTS]; MemoryRegion ocram; }; -- 2.43.0
From: Gaurav Sharma <gaurav.sharma_7@nxp.com> Add the Clock Control Module (CCM) device to i.MX8MM SoC. The CCM implementation is shared with i.MX8MP as the register layout is identical between the two variants.Hence iMX8MM will be using the source of iMX8MP CCM. Reviewed-by: Bernhard Beschow <shentey@gmail.com> Signed-off-by: Gaurav Sharma <gaurav.sharma_7@nxp.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/Kconfig | 1 + hw/arm/fsl-imx8mm.c | 10 ++++++++++ include/hw/arm/fsl-imx8mm.h | 2 ++ 3 files changed, 13 insertions(+) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -XXX,XX +XXX,XX @@ config FSL_IMX8MM bool select ARM_GIC select FSL_IMX8MP_ANALOG + select FSL_IMX8MP_CCM select IMX config FSL_IMX8MM_EVK diff --git a/hw/arm/fsl-imx8mm.c b/hw/arm/fsl-imx8mm.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/fsl-imx8mm.c +++ b/hw/arm/fsl-imx8mm.c @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_init(Object *obj) object_initialize_child(obj, "gic", &s->gic, gicv3_class_name()); + object_initialize_child(obj, "ccm", &s->ccm, TYPE_IMX8MP_CCM); + object_initialize_child(obj, "analog", &s->analog, TYPE_IMX8MP_ANALOG); for (i = 0; i < FSL_IMX8MM_NUM_UARTS; i++) { @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) } } + /* CCM */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->ccm), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccm), 0, + fsl_imx8mm_memmap[FSL_IMX8MM_CCM].addr); + /* Analog */ object_property_set_uint(OBJECT(&s->analog), "arm-pll-fdiv-ctl0-reset", 0x000fa030, &error_abort); @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) for (i = 0; i < ARRAY_SIZE(fsl_imx8mm_memmap); i++) { switch (i) { case FSL_IMX8MM_ANA_PLL: + case FSL_IMX8MM_CCM: case FSL_IMX8MM_GIC_DIST: case FSL_IMX8MM_GIC_REDIST: case FSL_IMX8MM_RAM: diff --git a/include/hw/arm/fsl-imx8mm.h b/include/hw/arm/fsl-imx8mm.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/arm/fsl-imx8mm.h +++ b/include/hw/arm/fsl-imx8mm.h @@ -XXX,XX +XXX,XX @@ #include "hw/char/imx_serial.h" #include "hw/intc/arm_gicv3_common.h" #include "hw/misc/imx8mp_analog.h" +#include "hw/misc/imx8mp_ccm.h" #include "qom/object.h" #include "qemu/units.h" @@ -XXX,XX +XXX,XX @@ struct FslImx8mmState { ARMCPU cpu[FSL_IMX8MM_NUM_CPUS]; GICv3State gic; + IMX8MPCCMState ccm; IMX8MPAnalogState analog; IMXSerialState uart[FSL_IMX8MM_NUM_UARTS]; MemoryRegion ocram; -- 2.43.0
From: Gaurav Sharma <gaurav.sharma_7@nxp.com> SNVS contains an RTC which allows Linux to deal correctly with time Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Bernhard Beschow <shentey@gmail.com> Signed-off-by: Gaurav Sharma <gaurav.sharma_7@nxp.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/fsl-imx8mm.c | 10 ++++++++++ include/hw/arm/fsl-imx8mm.h | 2 ++ 2 files changed, 12 insertions(+) diff --git a/hw/arm/fsl-imx8mm.c b/hw/arm/fsl-imx8mm.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/fsl-imx8mm.c +++ b/hw/arm/fsl-imx8mm.c @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_init(Object *obj) object_initialize_child(obj, "analog", &s->analog, TYPE_IMX8MP_ANALOG); + object_initialize_child(obj, "snvs", &s->snvs, TYPE_IMX7_SNVS); + for (i = 0; i < FSL_IMX8MM_NUM_UARTS; i++) { g_autofree char *name = g_strdup_printf("uart%d", i + 1); object_initialize_child(obj, name, &s->uart[i], TYPE_IMX_SERIAL); @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) fsl_imx8mm_memmap[FSL_IMX8MM_OCRAM].addr, &s->ocram); + /* SNVS */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->snvs), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->snvs), 0, + fsl_imx8mm_memmap[FSL_IMX8MM_SNVS_HP].addr); + /* Unimplemented devices */ for (i = 0; i < ARRAY_SIZE(fsl_imx8mm_memmap); i++) { switch (i) { @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) case FSL_IMX8MM_GIC_REDIST: case FSL_IMX8MM_RAM: case FSL_IMX8MM_OCRAM: + case FSL_IMX8MM_SNVS_HP: case FSL_IMX8MM_UART1 ... FSL_IMX8MM_UART4: /* device implemented and treated above */ break; diff --git a/include/hw/arm/fsl-imx8mm.h b/include/hw/arm/fsl-imx8mm.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/arm/fsl-imx8mm.h +++ b/include/hw/arm/fsl-imx8mm.h @@ -XXX,XX +XXX,XX @@ #include "cpu.h" #include "hw/char/imx_serial.h" #include "hw/intc/arm_gicv3_common.h" +#include "hw/misc/imx7_snvs.h" #include "hw/misc/imx8mp_analog.h" #include "hw/misc/imx8mp_ccm.h" #include "qom/object.h" @@ -XXX,XX +XXX,XX @@ struct FslImx8mmState { GICv3State gic; IMX8MPCCMState ccm; IMX8MPAnalogState analog; + IMX7SNVSState snvs; IMXSerialState uart[FSL_IMX8MM_NUM_UARTS]; MemoryRegion ocram; }; -- 2.43.0
From: Gaurav Sharma <gaurav.sharma_7@nxp.com> It enables emulation of SD/MMC cards through a virtual SDHCI interface The emulated SDHCI controller allows guest OS to use emulated storage as a standard block device. This will allow running the images such as those generated by Buildroot. Reviewed-by: Philippe Mathieu-Daude <philmd@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Bernhard Beschow <shentey@gmail.com> Signed-off-by: Gaurav Sharma <gaurav.sharma_7@nxp.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/Kconfig | 1 + hw/arm/fsl-imx8mm.c | 25 +++++++++++++++++++++++++ hw/arm/imx8mm-evk.c | 17 +++++++++++++++++ include/hw/arm/fsl-imx8mm.h | 7 +++++++ 4 files changed, 50 insertions(+) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -XXX,XX +XXX,XX @@ config FSL_IMX8MM select FSL_IMX8MP_ANALOG select FSL_IMX8MP_CCM select IMX + select SDHCI config FSL_IMX8MM_EVK bool diff --git a/hw/arm/fsl-imx8mm.c b/hw/arm/fsl-imx8mm.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/fsl-imx8mm.c +++ b/hw/arm/fsl-imx8mm.c @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_init(Object *obj) object_initialize_child(obj, name, &s->uart[i], TYPE_IMX_SERIAL); } + for (i = 0; i < FSL_IMX8MM_NUM_USDHCS; i++) { + g_autofree char *name = g_strdup_printf("usdhc%d", i + 1); + object_initialize_child(obj, name, &s->usdhc[i], TYPE_IMX_USDHC); + } } static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) fsl_imx8mm_memmap[FSL_IMX8MM_OCRAM].addr, &s->ocram); + /* USDHCs */ + for (i = 0; i < FSL_IMX8MM_NUM_USDHCS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } usdhc_table[FSL_IMX8MM_NUM_USDHCS] = { + { fsl_imx8mm_memmap[FSL_IMX8MM_USDHC1].addr, FSL_IMX8MM_USDHC1_IRQ }, + { fsl_imx8mm_memmap[FSL_IMX8MM_USDHC2].addr, FSL_IMX8MM_USDHC2_IRQ }, + { fsl_imx8mm_memmap[FSL_IMX8MM_USDHC3].addr, FSL_IMX8MM_USDHC3_IRQ }, + }; + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->usdhc[i]), errp)) { + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->usdhc[i]), 0, usdhc_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->usdhc[i]), 0, + qdev_get_gpio_in(gicdev, usdhc_table[i].irq)); + } + /* SNVS */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->snvs), errp)) { return; @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) case FSL_IMX8MM_OCRAM: case FSL_IMX8MM_SNVS_HP: case FSL_IMX8MM_UART1 ... FSL_IMX8MM_UART4: + case FSL_IMX8MM_USDHC1 ... FSL_IMX8MM_USDHC3: /* device implemented and treated above */ break; diff --git a/hw/arm/imx8mm-evk.c b/hw/arm/imx8mm-evk.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/imx8mm-evk.c +++ b/hw/arm/imx8mm-evk.c @@ -XXX,XX +XXX,XX @@ static void imx8mm_evk_init(MachineState *machine) memory_region_add_subregion(get_system_memory(), FSL_IMX8MM_RAM_START, machine->ram); + for (int i = 0; i < FSL_IMX8MM_NUM_USDHCS; i++) { + BusState *bus; + DeviceState *carddev; + BlockBackend *blk; + DriveInfo *di = drive_get(IF_SD, i, 0); + + if (!di) { + continue; + } + + blk = blk_by_legacy_dinfo(di); + bus = qdev_get_child_bus(DEVICE(&s->usdhc[i]), "sd-bus"); + carddev = qdev_new(TYPE_SD_CARD); + qdev_prop_set_drive_err(carddev, "drive", blk, &error_fatal); + qdev_realize_and_unref(carddev, bus, &error_fatal); + } + if (!qtest_enabled()) { arm_load_kernel(&s->cpu[0], machine, &boot_info); } diff --git a/include/hw/arm/fsl-imx8mm.h b/include/hw/arm/fsl-imx8mm.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/arm/fsl-imx8mm.h +++ b/include/hw/arm/fsl-imx8mm.h @@ -XXX,XX +XXX,XX @@ #include "hw/misc/imx7_snvs.h" #include "hw/misc/imx8mp_analog.h" #include "hw/misc/imx8mp_ccm.h" +#include "hw/sd/sdhci.h" #include "qom/object.h" #include "qemu/units.h" @@ -XXX,XX +XXX,XX @@ enum FslImx8mmConfiguration { FSL_IMX8MM_NUM_CPUS = 4, FSL_IMX8MM_NUM_IRQS = 128, FSL_IMX8MM_NUM_UARTS = 4, + FSL_IMX8MM_NUM_USDHCS = 3, }; struct FslImx8mmState { @@ -XXX,XX +XXX,XX @@ struct FslImx8mmState { IMX7SNVSState snvs; IMXSerialState uart[FSL_IMX8MM_NUM_UARTS]; MemoryRegion ocram; + SDHCIState usdhc[FSL_IMX8MM_NUM_USDHCS]; }; enum FslImx8mmMemoryRegions { @@ -XXX,XX +XXX,XX @@ enum FslImx8mmMemoryRegions { }; enum FslImx8mmIrqs { + FSL_IMX8MM_USDHC1_IRQ = 22, + FSL_IMX8MM_USDHC2_IRQ = 23, + FSL_IMX8MM_USDHC3_IRQ = 24, + FSL_IMX8MM_UART1_IRQ = 26, FSL_IMX8MM_UART2_IRQ = 27, FSL_IMX8MM_UART3_IRQ = 28, -- 2.43.0
From: Gaurav Sharma <gaurav.sharma_7@nxp.com> This enables support for Designware PCI Express Controller emulation It provides a controlled environment to debug the linux pci subsystem Reviewed-by: Philippe Mathieu-Daude <philmd@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Bernhard Beschow <shentey@gmail.com> Signed-off-by: Gaurav Sharma <gaurav.sharma_7@nxp.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/Kconfig | 3 +++ hw/arm/fsl-imx8mm.c | 30 ++++++++++++++++++++++++++++++ include/hw/arm/fsl-imx8mm.h | 10 ++++++++++ 3 files changed, 43 insertions(+) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -XXX,XX +XXX,XX @@ config FSL_IMX8MP_EVK config FSL_IMX8MM bool + imply PCI_DEVICES select ARM_GIC select FSL_IMX8MP_ANALOG select FSL_IMX8MP_CCM select IMX select SDHCI + select PCI_EXPRESS_DESIGNWARE + select PCI_EXPRESS_FSL_IMX8M_PHY config FSL_IMX8MM_EVK bool diff --git a/hw/arm/fsl-imx8mm.c b/hw/arm/fsl-imx8mm.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/fsl-imx8mm.c +++ b/hw/arm/fsl-imx8mm.c @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_init(Object *obj) g_autofree char *name = g_strdup_printf("usdhc%d", i + 1); object_initialize_child(obj, name, &s->usdhc[i], TYPE_IMX_USDHC); } + + object_initialize_child(obj, "pcie", &s->pcie, TYPE_DESIGNWARE_PCIE_HOST); + object_initialize_child(obj, "pcie_phy", &s->pcie_phy, + TYPE_FSL_IMX8M_PCIE_PHY); } static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->snvs), 0, fsl_imx8mm_memmap[FSL_IMX8MM_SNVS_HP].addr); + /* PCIe */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->pcie), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->pcie), 0, + fsl_imx8mm_memmap[FSL_IMX8MM_PCIE1].addr); + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 0, + qdev_get_gpio_in(gicdev, FSL_IMX8MM_PCI_INTA_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 1, + qdev_get_gpio_in(gicdev, FSL_IMX8MM_PCI_INTB_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 2, + qdev_get_gpio_in(gicdev, FSL_IMX8MM_PCI_INTC_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 3, + qdev_get_gpio_in(gicdev, FSL_IMX8MM_PCI_INTD_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 4, + qdev_get_gpio_in(gicdev, FSL_IMX8MM_PCI_MSI_IRQ)); + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->pcie_phy), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->pcie_phy), 0, + fsl_imx8mm_memmap[FSL_IMX8MM_PCIE_PHY1].addr); + /* Unimplemented devices */ for (i = 0; i < ARRAY_SIZE(fsl_imx8mm_memmap); i++) { switch (i) { @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) case FSL_IMX8MM_CCM: case FSL_IMX8MM_GIC_DIST: case FSL_IMX8MM_GIC_REDIST: + case FSL_IMX8MM_PCIE1: + case FSL_IMX8MM_PCIE_PHY1: case FSL_IMX8MM_RAM: case FSL_IMX8MM_OCRAM: case FSL_IMX8MM_SNVS_HP: diff --git a/include/hw/arm/fsl-imx8mm.h b/include/hw/arm/fsl-imx8mm.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/arm/fsl-imx8mm.h +++ b/include/hw/arm/fsl-imx8mm.h @@ -XXX,XX +XXX,XX @@ #include "hw/misc/imx7_snvs.h" #include "hw/misc/imx8mp_analog.h" #include "hw/misc/imx8mp_ccm.h" +#include "hw/pci-host/designware.h" +#include "hw/pci-host/fsl_imx8m_phy.h" #include "hw/sd/sdhci.h" #include "qom/object.h" #include "qemu/units.h" @@ -XXX,XX +XXX,XX @@ struct FslImx8mmState { IMXSerialState uart[FSL_IMX8MM_NUM_UARTS]; MemoryRegion ocram; SDHCIState usdhc[FSL_IMX8MM_NUM_USDHCS]; + DesignwarePCIEHost pcie; + FslImx8mPciePhyState pcie_phy; }; enum FslImx8mmMemoryRegions { @@ -XXX,XX +XXX,XX @@ enum FslImx8mmIrqs { FSL_IMX8MM_UART2_IRQ = 27, FSL_IMX8MM_UART3_IRQ = 28, FSL_IMX8MM_UART4_IRQ = 29, + + FSL_IMX8MM_PCI_INTA_IRQ = 122, + FSL_IMX8MM_PCI_INTB_IRQ = 123, + FSL_IMX8MM_PCI_INTC_IRQ = 124, + FSL_IMX8MM_PCI_INTD_IRQ = 125, + FSL_IMX8MM_PCI_MSI_IRQ = 127, }; #endif /* FSL_IMX8MM_H */ -- 2.43.0
From: Gaurav Sharma <gaurav.sharma_7@nxp.com> Enabled GPIO controller emulation Also updated the GPIO IRQ lines of iMX8MM Reviewed-by: Philippe Mathieu-Daude <philmd@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Bernhard Beschow <shentey@gmail.com> Signed-off-by: Gaurav Sharma <gaurav.sharma_7@nxp.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/fsl-imx8mm.c | 54 +++++++++++++++++++++++++++++++++++++ include/hw/arm/fsl-imx8mm.h | 14 ++++++++++ 2 files changed, 68 insertions(+) diff --git a/hw/arm/fsl-imx8mm.c b/hw/arm/fsl-imx8mm.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/fsl-imx8mm.c +++ b/hw/arm/fsl-imx8mm.c @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_init(Object *obj) object_initialize_child(obj, name, &s->uart[i], TYPE_IMX_SERIAL); } + for (i = 0; i < FSL_IMX8MM_NUM_GPIOS; i++) { + g_autofree char *name = g_strdup_printf("gpio%d", i + 1); + object_initialize_child(obj, name, &s->gpio[i], TYPE_IMX_GPIO); + } + for (i = 0; i < FSL_IMX8MM_NUM_USDHCS; i++) { g_autofree char *name = g_strdup_printf("usdhc%d", i + 1); object_initialize_child(obj, name, &s->usdhc[i], TYPE_IMX_USDHC); @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) fsl_imx8mm_memmap[FSL_IMX8MM_OCRAM].addr, &s->ocram); + /* GPIOs */ + for (i = 0; i < FSL_IMX8MM_NUM_GPIOS; i++) { + static const struct { + hwaddr addr; + unsigned int irq_low; + unsigned int irq_high; + } gpio_table[FSL_IMX8MM_NUM_GPIOS] = { + { + fsl_imx8mm_memmap[FSL_IMX8MM_GPIO1].addr, + FSL_IMX8MM_GPIO1_LOW_IRQ, + FSL_IMX8MM_GPIO1_HIGH_IRQ + }, + { + fsl_imx8mm_memmap[FSL_IMX8MM_GPIO2].addr, + FSL_IMX8MM_GPIO2_LOW_IRQ, + FSL_IMX8MM_GPIO2_HIGH_IRQ + }, + { + fsl_imx8mm_memmap[FSL_IMX8MM_GPIO3].addr, + FSL_IMX8MM_GPIO3_LOW_IRQ, + FSL_IMX8MM_GPIO3_HIGH_IRQ + }, + { + fsl_imx8mm_memmap[FSL_IMX8MM_GPIO4].addr, + FSL_IMX8MM_GPIO4_LOW_IRQ, + FSL_IMX8MM_GPIO4_HIGH_IRQ + }, + { + fsl_imx8mm_memmap[FSL_IMX8MM_GPIO5].addr, + FSL_IMX8MM_GPIO5_LOW_IRQ, + FSL_IMX8MM_GPIO5_HIGH_IRQ + }, + }; + object_property_set_bool(OBJECT(&s->gpio[i]), "has-edge-sel", true, + &error_abort); + object_property_set_bool(OBJECT(&s->gpio[i]), "has-upper-pin-irq", + true, &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio[i]), errp)) { + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio[i]), 0, gpio_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 0, + qdev_get_gpio_in(gicdev, gpio_table[i].irq_low)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 1, + qdev_get_gpio_in(gicdev, gpio_table[i].irq_high)); + } + /* USDHCs */ for (i = 0; i < FSL_IMX8MM_NUM_USDHCS; i++) { static const struct { @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) case FSL_IMX8MM_CCM: case FSL_IMX8MM_GIC_DIST: case FSL_IMX8MM_GIC_REDIST: + case FSL_IMX8MM_GPIO1 ... FSL_IMX8MM_GPIO5: case FSL_IMX8MM_PCIE1: case FSL_IMX8MM_PCIE_PHY1: case FSL_IMX8MM_RAM: diff --git a/include/hw/arm/fsl-imx8mm.h b/include/hw/arm/fsl-imx8mm.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/arm/fsl-imx8mm.h +++ b/include/hw/arm/fsl-imx8mm.h @@ -XXX,XX +XXX,XX @@ #include "cpu.h" #include "hw/char/imx_serial.h" +#include "hw/gpio/imx_gpio.h" #include "hw/intc/arm_gicv3_common.h" #include "hw/misc/imx7_snvs.h" #include "hw/misc/imx8mp_analog.h" @@ -XXX,XX +XXX,XX @@ OBJECT_DECLARE_SIMPLE_TYPE(FslImx8mmState, FSL_IMX8MM) enum FslImx8mmConfiguration { FSL_IMX8MM_NUM_CPUS = 4, + FSL_IMX8MM_NUM_GPIOS = 5, FSL_IMX8MM_NUM_IRQS = 128, FSL_IMX8MM_NUM_UARTS = 4, FSL_IMX8MM_NUM_USDHCS = 3, @@ -XXX,XX +XXX,XX @@ struct FslImx8mmState { ARMCPU cpu[FSL_IMX8MM_NUM_CPUS]; GICv3State gic; + IMXGPIOState gpio[FSL_IMX8MM_NUM_GPIOS]; IMX8MPCCMState ccm; IMX8MPAnalogState analog; IMX7SNVSState snvs; @@ -XXX,XX +XXX,XX @@ enum FslImx8mmIrqs { FSL_IMX8MM_UART3_IRQ = 28, FSL_IMX8MM_UART4_IRQ = 29, + FSL_IMX8MM_GPIO1_LOW_IRQ = 64, + FSL_IMX8MM_GPIO1_HIGH_IRQ = 65, + FSL_IMX8MM_GPIO2_LOW_IRQ = 66, + FSL_IMX8MM_GPIO2_HIGH_IRQ = 67, + FSL_IMX8MM_GPIO3_LOW_IRQ = 68, + FSL_IMX8MM_GPIO3_HIGH_IRQ = 69, + FSL_IMX8MM_GPIO4_LOW_IRQ = 70, + FSL_IMX8MM_GPIO4_HIGH_IRQ = 71, + FSL_IMX8MM_GPIO5_LOW_IRQ = 72, + FSL_IMX8MM_GPIO5_HIGH_IRQ = 73, + FSL_IMX8MM_PCI_INTA_IRQ = 122, FSL_IMX8MM_PCI_INTB_IRQ = 123, FSL_IMX8MM_PCI_INTC_IRQ = 124, -- 2.43.0
From: Gaurav Sharma <gaurav.sharma_7@nxp.com> This can be used to test and debug I2C device drivers. Added I2C interrupts Reviewed-by: Philippe Mathieu-Daude <philmd@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Bernhard Beschow <shentey@gmail.com> Signed-off-by: Gaurav Sharma <gaurav.sharma_7@nxp.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/Kconfig | 2 ++ hw/arm/fsl-imx8mm.c | 27 +++++++++++++++++++++++++++ include/hw/arm/fsl-imx8mm.h | 8 ++++++++ 3 files changed, 37 insertions(+) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -XXX,XX +XXX,XX @@ config FSL_IMX8MP_EVK config FSL_IMX8MM bool imply PCI_DEVICES + imply I2C_DEVICES select ARM_GIC select FSL_IMX8MP_ANALOG select FSL_IMX8MP_CCM select IMX + select IMX_I2C select SDHCI select PCI_EXPRESS_DESIGNWARE select PCI_EXPRESS_FSL_IMX8M_PHY diff --git a/hw/arm/fsl-imx8mm.c b/hw/arm/fsl-imx8mm.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/fsl-imx8mm.c +++ b/hw/arm/fsl-imx8mm.c @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_init(Object *obj) object_initialize_child(obj, name, &s->uart[i], TYPE_IMX_SERIAL); } + for (i = 0; i < FSL_IMX8MM_NUM_I2CS; i++) { + g_autofree char *name = g_strdup_printf("i2c%d", i + 1); + object_initialize_child(obj, name, &s->i2c[i], TYPE_IMX_I2C); + } + for (i = 0; i < FSL_IMX8MM_NUM_GPIOS; i++) { g_autofree char *name = g_strdup_printf("gpio%d", i + 1); object_initialize_child(obj, name, &s->gpio[i], TYPE_IMX_GPIO); @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) fsl_imx8mm_memmap[FSL_IMX8MM_OCRAM].addr, &s->ocram); + /* I2Cs */ + for (i = 0; i < FSL_IMX8MM_NUM_I2CS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } i2c_table[FSL_IMX8MM_NUM_I2CS] = { + { fsl_imx8mm_memmap[FSL_IMX8MM_I2C1].addr, FSL_IMX8MM_I2C1_IRQ }, + { fsl_imx8mm_memmap[FSL_IMX8MM_I2C2].addr, FSL_IMX8MM_I2C2_IRQ }, + { fsl_imx8mm_memmap[FSL_IMX8MM_I2C3].addr, FSL_IMX8MM_I2C3_IRQ }, + { fsl_imx8mm_memmap[FSL_IMX8MM_I2C4].addr, FSL_IMX8MM_I2C4_IRQ }, + }; + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->i2c[i]), errp)) { + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c[i]), 0, i2c_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c[i]), 0, + qdev_get_gpio_in(gicdev, i2c_table[i].irq)); + } + /* GPIOs */ for (i = 0; i < FSL_IMX8MM_NUM_GPIOS; i++) { static const struct { @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) case FSL_IMX8MM_GIC_DIST: case FSL_IMX8MM_GIC_REDIST: case FSL_IMX8MM_GPIO1 ... FSL_IMX8MM_GPIO5: + case FSL_IMX8MM_I2C1 ... FSL_IMX8MM_I2C4: case FSL_IMX8MM_PCIE1: case FSL_IMX8MM_PCIE_PHY1: case FSL_IMX8MM_RAM: diff --git a/include/hw/arm/fsl-imx8mm.h b/include/hw/arm/fsl-imx8mm.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/arm/fsl-imx8mm.h +++ b/include/hw/arm/fsl-imx8mm.h @@ -XXX,XX +XXX,XX @@ #include "cpu.h" #include "hw/char/imx_serial.h" #include "hw/gpio/imx_gpio.h" +#include "hw/i2c/imx_i2c.h" #include "hw/intc/arm_gicv3_common.h" #include "hw/misc/imx7_snvs.h" #include "hw/misc/imx8mp_analog.h" @@ -XXX,XX +XXX,XX @@ OBJECT_DECLARE_SIMPLE_TYPE(FslImx8mmState, FSL_IMX8MM) enum FslImx8mmConfiguration { FSL_IMX8MM_NUM_CPUS = 4, FSL_IMX8MM_NUM_GPIOS = 5, + FSL_IMX8MM_NUM_I2CS = 4, FSL_IMX8MM_NUM_IRQS = 128, FSL_IMX8MM_NUM_UARTS = 4, FSL_IMX8MM_NUM_USDHCS = 3, @@ -XXX,XX +XXX,XX @@ struct FslImx8mmState { IMX8MPCCMState ccm; IMX8MPAnalogState analog; IMX7SNVSState snvs; + IMXI2CState i2c[FSL_IMX8MM_NUM_I2CS]; IMXSerialState uart[FSL_IMX8MM_NUM_UARTS]; MemoryRegion ocram; SDHCIState usdhc[FSL_IMX8MM_NUM_USDHCS]; @@ -XXX,XX +XXX,XX @@ enum FslImx8mmIrqs { FSL_IMX8MM_UART3_IRQ = 28, FSL_IMX8MM_UART4_IRQ = 29, + FSL_IMX8MM_I2C1_IRQ = 35, + FSL_IMX8MM_I2C2_IRQ = 36, + FSL_IMX8MM_I2C3_IRQ = 37, + FSL_IMX8MM_I2C4_IRQ = 38, + FSL_IMX8MM_GPIO1_LOW_IRQ = 64, FSL_IMX8MM_GPIO1_HIGH_IRQ = 65, FSL_IMX8MM_GPIO2_LOW_IRQ = 66, -- 2.43.0
From: Gaurav Sharma <gaurav.sharma_7@nxp.com> It enables emulation of ECSPI in iMX8MM Added SPI IRQ lines Reviewed-by: Philippe Mathieu-Daude <philmd@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Bernhard Beschow <shentey@gmail.com> Signed-off-by: Gaurav Sharma <gaurav.sharma_7@nxp.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/fsl-imx8mm.c | 26 ++++++++++++++++++++++++++ include/hw/arm/fsl-imx8mm.h | 7 +++++++ 2 files changed, 33 insertions(+) diff --git a/hw/arm/fsl-imx8mm.c b/hw/arm/fsl-imx8mm.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/fsl-imx8mm.c +++ b/hw/arm/fsl-imx8mm.c @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_init(Object *obj) object_initialize_child(obj, name, &s->usdhc[i], TYPE_IMX_USDHC); } + for (i = 0; i < FSL_IMX8MM_NUM_ECSPIS; i++) { + g_autofree char *name = g_strdup_printf("spi%d", i + 1); + object_initialize_child(obj, name, &s->spi[i], TYPE_IMX_SPI); + } + object_initialize_child(obj, "pcie", &s->pcie, TYPE_DESIGNWARE_PCIE_HOST); object_initialize_child(obj, "pcie_phy", &s->pcie_phy, TYPE_FSL_IMX8M_PCIE_PHY); @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in(gicdev, usdhc_table[i].irq)); } + /* ECSPIs */ + for (i = 0; i < FSL_IMX8MM_NUM_ECSPIS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } spi_table[FSL_IMX8MM_NUM_ECSPIS] = { + { fsl_imx8mm_memmap[FSL_IMX8MM_ECSPI1].addr, FSL_IMX8MM_ECSPI1_IRQ }, + { fsl_imx8mm_memmap[FSL_IMX8MM_ECSPI2].addr, FSL_IMX8MM_ECSPI2_IRQ }, + { fsl_imx8mm_memmap[FSL_IMX8MM_ECSPI3].addr, FSL_IMX8MM_ECSPI3_IRQ }, + }; + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->spi[i]), errp)) { + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 0, spi_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->spi[i]), 0, + qdev_get_gpio_in(gicdev, spi_table[i].irq)); + } + /* SNVS */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->snvs), errp)) { return; @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) case FSL_IMX8MM_GIC_DIST: case FSL_IMX8MM_GIC_REDIST: case FSL_IMX8MM_GPIO1 ... FSL_IMX8MM_GPIO5: + case FSL_IMX8MM_ECSPI1 ... FSL_IMX8MM_ECSPI3: case FSL_IMX8MM_I2C1 ... FSL_IMX8MM_I2C4: case FSL_IMX8MM_PCIE1: case FSL_IMX8MM_PCIE_PHY1: diff --git a/include/hw/arm/fsl-imx8mm.h b/include/hw/arm/fsl-imx8mm.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/arm/fsl-imx8mm.h +++ b/include/hw/arm/fsl-imx8mm.h @@ -XXX,XX +XXX,XX @@ #include "hw/pci-host/designware.h" #include "hw/pci-host/fsl_imx8m_phy.h" #include "hw/sd/sdhci.h" +#include "hw/ssi/imx_spi.h" #include "qom/object.h" #include "qemu/units.h" @@ -XXX,XX +XXX,XX @@ OBJECT_DECLARE_SIMPLE_TYPE(FslImx8mmState, FSL_IMX8MM) enum FslImx8mmConfiguration { FSL_IMX8MM_NUM_CPUS = 4, + FSL_IMX8MM_NUM_ECSPIS = 3, FSL_IMX8MM_NUM_GPIOS = 5, FSL_IMX8MM_NUM_I2CS = 4, FSL_IMX8MM_NUM_IRQS = 128, @@ -XXX,XX +XXX,XX @@ struct FslImx8mmState { IMX8MPCCMState ccm; IMX8MPAnalogState analog; IMX7SNVSState snvs; + IMXSPIState spi[FSL_IMX8MM_NUM_ECSPIS]; IMXI2CState i2c[FSL_IMX8MM_NUM_I2CS]; IMXSerialState uart[FSL_IMX8MM_NUM_UARTS]; MemoryRegion ocram; @@ -XXX,XX +XXX,XX @@ enum FslImx8mmIrqs { FSL_IMX8MM_UART3_IRQ = 28, FSL_IMX8MM_UART4_IRQ = 29, + FSL_IMX8MM_ECSPI1_IRQ = 31, + FSL_IMX8MM_ECSPI2_IRQ = 32, + FSL_IMX8MM_ECSPI3_IRQ = 33, + FSL_IMX8MM_I2C1_IRQ = 35, FSL_IMX8MM_I2C2_IRQ = 36, FSL_IMX8MM_I2C3_IRQ = 37, -- 2.43.0
From: Gaurav Sharma <gaurav.sharma_7@nxp.com> It enables emulation of WDT in iMX8MM Added WDT IRQ lines Reviewed-by: Philippe Mathieu-Daude <philmd@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Bernhard Beschow <shentey@gmail.com> Signed-off-by: Gaurav Sharma <gaurav.sharma_7@nxp.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/Kconfig | 1 + hw/arm/fsl-imx8mm.c | 28 ++++++++++++++++++++++++++++ include/hw/arm/fsl-imx8mm.h | 7 +++++++ 3 files changed, 36 insertions(+) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -XXX,XX +XXX,XX @@ config FSL_IMX8MM select SDHCI select PCI_EXPRESS_DESIGNWARE select PCI_EXPRESS_FSL_IMX8M_PHY + select WDT_IMX2 config FSL_IMX8MM_EVK bool diff --git a/hw/arm/fsl-imx8mm.c b/hw/arm/fsl-imx8mm.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/fsl-imx8mm.c +++ b/hw/arm/fsl-imx8mm.c @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_init(Object *obj) object_initialize_child(obj, name, &s->spi[i], TYPE_IMX_SPI); } + for (i = 0; i < FSL_IMX8MM_NUM_WDTS; i++) { + g_autofree char *name = g_strdup_printf("wdt%d", i); + object_initialize_child(obj, name, &s->wdt[i], TYPE_IMX2_WDT); + } + object_initialize_child(obj, "pcie", &s->pcie, TYPE_DESIGNWARE_PCIE_HOST); object_initialize_child(obj, "pcie_phy", &s->pcie_phy, TYPE_FSL_IMX8M_PCIE_PHY); @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->snvs), 0, fsl_imx8mm_memmap[FSL_IMX8MM_SNVS_HP].addr); + /* Watchdogs */ + for (i = 0; i < FSL_IMX8MM_NUM_WDTS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } wdog_table[FSL_IMX8MM_NUM_WDTS] = { + { fsl_imx8mm_memmap[FSL_IMX8MM_WDOG1].addr, FSL_IMX8MM_WDOG1_IRQ }, + { fsl_imx8mm_memmap[FSL_IMX8MM_WDOG2].addr, FSL_IMX8MM_WDOG2_IRQ }, + { fsl_imx8mm_memmap[FSL_IMX8MM_WDOG3].addr, FSL_IMX8MM_WDOG3_IRQ }, + }; + + object_property_set_bool(OBJECT(&s->wdt[i]), "pretimeout-support", + true, &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->wdt[i]), errp)) { + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt[i]), 0, wdog_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->wdt[i]), 0, + qdev_get_gpio_in(gicdev, wdog_table[i].irq)); + } + /* PCIe */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->pcie), errp)) { return; @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) case FSL_IMX8MM_SNVS_HP: case FSL_IMX8MM_UART1 ... FSL_IMX8MM_UART4: case FSL_IMX8MM_USDHC1 ... FSL_IMX8MM_USDHC3: + case FSL_IMX8MM_WDOG1 ... FSL_IMX8MM_WDOG3: /* device implemented and treated above */ break; diff --git a/include/hw/arm/fsl-imx8mm.h b/include/hw/arm/fsl-imx8mm.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/arm/fsl-imx8mm.h +++ b/include/hw/arm/fsl-imx8mm.h @@ -XXX,XX +XXX,XX @@ #include "hw/pci-host/fsl_imx8m_phy.h" #include "hw/sd/sdhci.h" #include "hw/ssi/imx_spi.h" +#include "hw/watchdog/wdt_imx2.h" #include "qom/object.h" #include "qemu/units.h" @@ -XXX,XX +XXX,XX @@ enum FslImx8mmConfiguration { FSL_IMX8MM_NUM_IRQS = 128, FSL_IMX8MM_NUM_UARTS = 4, FSL_IMX8MM_NUM_USDHCS = 3, + FSL_IMX8MM_NUM_WDTS = 3, }; struct FslImx8mmState { @@ -XXX,XX +XXX,XX @@ struct FslImx8mmState { IMXSerialState uart[FSL_IMX8MM_NUM_UARTS]; MemoryRegion ocram; SDHCIState usdhc[FSL_IMX8MM_NUM_USDHCS]; + IMX2WdtState wdt[FSL_IMX8MM_NUM_WDTS]; DesignwarePCIEHost pcie; FslImx8mPciePhyState pcie_phy; }; @@ -XXX,XX +XXX,XX @@ enum FslImx8mmIrqs { FSL_IMX8MM_GPIO5_LOW_IRQ = 72, FSL_IMX8MM_GPIO5_HIGH_IRQ = 73, + FSL_IMX8MM_WDOG1_IRQ = 78, + FSL_IMX8MM_WDOG2_IRQ = 79, + FSL_IMX8MM_WDOG3_IRQ = 10, + FSL_IMX8MM_PCI_INTA_IRQ = 122, FSL_IMX8MM_PCI_INTB_IRQ = 123, FSL_IMX8MM_PCI_INTC_IRQ = 124, -- 2.43.0
From: Gaurav Sharma <gaurav.sharma_7@nxp.com> It enables emulation of GPT in iMX8MM Added GPT IRQ lines Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Bernhard Beschow <shentey@gmail.com> Signed-off-by: Gaurav Sharma <gaurav.sharma_7@nxp.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/Kconfig | 1 + hw/arm/fsl-imx8mm.c | 54 +++++++++++++++++++++++++++++++++++++ hw/timer/imx_gpt.c | 26 ++++++++++++++++++ include/hw/arm/fsl-imx8mm.h | 11 ++++++++ include/hw/timer/imx_gpt.h | 2 ++ 5 files changed, 94 insertions(+) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -XXX,XX +XXX,XX @@ config FSL_IMX8MM select FSL_IMX8MP_CCM select IMX select IMX_I2C + select OR_IRQ select SDHCI select PCI_EXPRESS_DESIGNWARE select PCI_EXPRESS_FSL_IMX8M_PHY diff --git a/hw/arm/fsl-imx8mm.c b/hw/arm/fsl-imx8mm.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/fsl-imx8mm.c +++ b/hw/arm/fsl-imx8mm.c @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_init(Object *obj) object_initialize_child(obj, name, &s->uart[i], TYPE_IMX_SERIAL); } + for (i = 0; i < FSL_IMX8MM_NUM_GPTS; i++) { + g_autofree char *name = g_strdup_printf("gpt%d", i + 1); + object_initialize_child(obj, name, &s->gpt[i], TYPE_IMX8MM_GPT); + } + object_initialize_child(obj, "gpt5-gpt6-irq", &s->gpt5_gpt6_irq, + TYPE_OR_IRQ); + for (i = 0; i < FSL_IMX8MM_NUM_I2CS; i++) { g_autofree char *name = g_strdup_printf("i2c%d", i + 1); object_initialize_child(obj, name, &s->i2c[i], TYPE_IMX_I2C); @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) fsl_imx8mm_memmap[FSL_IMX8MM_OCRAM].addr, &s->ocram); + /* GPTs */ + object_property_set_int(OBJECT(&s->gpt5_gpt6_irq), "num-lines", 2, + &error_abort); + if (!qdev_realize(DEVICE(&s->gpt5_gpt6_irq), NULL, errp)) { + return; + } + + qdev_connect_gpio_out(DEVICE(&s->gpt5_gpt6_irq), 0, + qdev_get_gpio_in(gicdev, FSL_IMX8MM_GPT5_GPT6_IRQ)); + + for (i = 0; i < FSL_IMX8MM_NUM_GPTS; i++) { + hwaddr gpt_addrs[FSL_IMX8MM_NUM_GPTS] = { + fsl_imx8mm_memmap[FSL_IMX8MM_GPT1].addr, + fsl_imx8mm_memmap[FSL_IMX8MM_GPT2].addr, + fsl_imx8mm_memmap[FSL_IMX8MM_GPT3].addr, + fsl_imx8mm_memmap[FSL_IMX8MM_GPT4].addr, + fsl_imx8mm_memmap[FSL_IMX8MM_GPT5].addr, + fsl_imx8mm_memmap[FSL_IMX8MM_GPT6].addr, + }; + + s->gpt[i].ccm = IMX_CCM(&s->ccm); + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpt[i]), errp)) { + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpt[i]), 0, gpt_addrs[i]); + + if (i < FSL_IMX8MM_NUM_GPTS - 2) { + static const unsigned int gpt_irqs[FSL_IMX8MM_NUM_GPTS - 2] = { + FSL_IMX8MM_GPT1_IRQ, + FSL_IMX8MM_GPT2_IRQ, + FSL_IMX8MM_GPT3_IRQ, + FSL_IMX8MM_GPT4_IRQ, + }; + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpt[i]), 0, + qdev_get_gpio_in(gicdev, gpt_irqs[i])); + } else { + int irq = i - FSL_IMX8MM_NUM_GPTS + 2; + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpt[i]), 0, + qdev_get_gpio_in(DEVICE(&s->gpt5_gpt6_irq), irq)); + } + } + /* I2Cs */ for (i = 0; i < FSL_IMX8MM_NUM_I2CS; i++) { static const struct { @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) case FSL_IMX8MM_GIC_DIST: case FSL_IMX8MM_GIC_REDIST: case FSL_IMX8MM_GPIO1 ... FSL_IMX8MM_GPIO5: + case FSL_IMX8MM_GPT1 ... FSL_IMX8MM_GPT6: case FSL_IMX8MM_ECSPI1 ... FSL_IMX8MM_ECSPI3: case FSL_IMX8MM_I2C1 ... FSL_IMX8MM_I2C4: case FSL_IMX8MM_PCIE1: diff --git a/hw/timer/imx_gpt.c b/hw/timer/imx_gpt.c index XXXXXXX..XXXXXXX 100644 --- a/hw/timer/imx_gpt.c +++ b/hw/timer/imx_gpt.c @@ -XXX,XX +XXX,XX @@ * Originally written by Hans Jiang * Updated by Peter Chubb * Updated by Jean-Christophe Dubois <jcd@tribudubois.net> + * Updated by Gaurav Sharma <gaurav.sharma_7@nxp.com> * * This code is licensed under GPL version 2 or later. See * the COPYING file in the top-level directory. @@ -XXX,XX +XXX,XX @@ static const IMXClk imx8mp_gpt_clocks[] = { CLK_NONE, /* 111 not defined */ }; +static const IMXClk imx8mm_gpt_clocks[] = { + CLK_NONE, /* 000 No clock source */ + CLK_IPG, /* 001 ipg_clk, 532MHz */ + CLK_IPG_HIGH, /* 010 ipg_clk_highfreq */ + CLK_EXT, /* 011 External clock */ + CLK_32k, /* 100 ipg_clk_32k */ + CLK_HIGH, /* 101 ipg_clk_16M */ + CLK_NONE, /* 110 not defined */ + CLK_NONE, /* 111 not defined */ +}; + /* Must be called from within ptimer_transaction_begin/commit block */ static void imx_gpt_set_freq(IMXGPTState *s) { @@ -XXX,XX +XXX,XX @@ static void imx8mp_gpt_init(Object *obj) s->clocks = imx8mp_gpt_clocks; } +static void imx8mm_gpt_init(Object *obj) +{ + IMXGPTState *s = IMX_GPT(obj); + + s->clocks = imx8mm_gpt_clocks; +} + static const TypeInfo imx25_gpt_info = { .name = TYPE_IMX25_GPT, .parent = TYPE_SYS_BUS_DEVICE, @@ -XXX,XX +XXX,XX @@ static const TypeInfo imx8mp_gpt_info = { .instance_init = imx8mp_gpt_init, }; +static const TypeInfo imx8mm_gpt_info = { + .name = TYPE_IMX8MM_GPT, + .parent = TYPE_IMX25_GPT, + .instance_init = imx8mm_gpt_init, +}; + static void imx_gpt_register_types(void) { type_register_static(&imx25_gpt_info); @@ -XXX,XX +XXX,XX @@ static void imx_gpt_register_types(void) type_register_static(&imx6ul_gpt_info); type_register_static(&imx7_gpt_info); type_register_static(&imx8mp_gpt_info); + type_register_static(&imx8mm_gpt_info); } type_init(imx_gpt_register_types) diff --git a/include/hw/arm/fsl-imx8mm.h b/include/hw/arm/fsl-imx8mm.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/arm/fsl-imx8mm.h +++ b/include/hw/arm/fsl-imx8mm.h @@ -XXX,XX +XXX,XX @@ #include "hw/misc/imx7_snvs.h" #include "hw/misc/imx8mp_analog.h" #include "hw/misc/imx8mp_ccm.h" +#include "hw/or-irq.h" #include "hw/pci-host/designware.h" #include "hw/pci-host/fsl_imx8m_phy.h" #include "hw/sd/sdhci.h" #include "hw/ssi/imx_spi.h" +#include "hw/timer/imx_gpt.h" #include "hw/watchdog/wdt_imx2.h" #include "qom/object.h" #include "qemu/units.h" @@ -XXX,XX +XXX,XX @@ enum FslImx8mmConfiguration { FSL_IMX8MM_NUM_CPUS = 4, FSL_IMX8MM_NUM_ECSPIS = 3, FSL_IMX8MM_NUM_GPIOS = 5, + FSL_IMX8MM_NUM_GPTS = 6, FSL_IMX8MM_NUM_I2CS = 4, FSL_IMX8MM_NUM_IRQS = 128, FSL_IMX8MM_NUM_UARTS = 4, @@ -XXX,XX +XXX,XX @@ struct FslImx8mmState { ARMCPU cpu[FSL_IMX8MM_NUM_CPUS]; GICv3State gic; + IMXGPTState gpt[FSL_IMX8MM_NUM_GPTS]; IMXGPIOState gpio[FSL_IMX8MM_NUM_GPIOS]; IMX8MPCCMState ccm; IMX8MPAnalogState analog; @@ -XXX,XX +XXX,XX @@ struct FslImx8mmState { IMX2WdtState wdt[FSL_IMX8MM_NUM_WDTS]; DesignwarePCIEHost pcie; FslImx8mPciePhyState pcie_phy; + OrIRQState gpt5_gpt6_irq; }; enum FslImx8mmMemoryRegions { @@ -XXX,XX +XXX,XX @@ enum FslImx8mmIrqs { FSL_IMX8MM_I2C3_IRQ = 37, FSL_IMX8MM_I2C4_IRQ = 38, + FSL_IMX8MM_GPT1_IRQ = 55, + FSL_IMX8MM_GPT2_IRQ = 54, + FSL_IMX8MM_GPT3_IRQ = 53, + FSL_IMX8MM_GPT4_IRQ = 52, + FSL_IMX8MM_GPT5_GPT6_IRQ = 51, + FSL_IMX8MM_GPIO1_LOW_IRQ = 64, FSL_IMX8MM_GPIO1_HIGH_IRQ = 65, FSL_IMX8MM_GPIO2_LOW_IRQ = 66, diff --git a/include/hw/timer/imx_gpt.h b/include/hw/timer/imx_gpt.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/timer/imx_gpt.h +++ b/include/hw/timer/imx_gpt.h @@ -XXX,XX +XXX,XX @@ * Originally written by Hans Jiang * Updated by Peter Chubb * Updated by Jean-Christophe Dubois <jcd@tribudubois.net> + * Updated by Gaurav Sharma <gaurav.sharma_7@nxp.com> * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -XXX,XX +XXX,XX @@ #define TYPE_IMX6UL_GPT "imx6ul.gpt" #define TYPE_IMX7_GPT "imx7.gpt" #define TYPE_IMX8MP_GPT "imx8mp.gpt" +#define TYPE_IMX8MM_GPT "imx8mm.gpt" #define TYPE_IMX_GPT TYPE_IMX25_GPT -- 2.43.0
From: Gaurav Sharma <gaurav.sharma_7@nxp.com> It enables emulation of ENET ethernet controller in iMX8MM Enables testing and debugging of network dependent drivers Added ENET MAC IRQ lines Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Bernhard Beschow <shentey@gmail.com> Signed-off-by: Gaurav Sharma <gaurav.sharma_7@nxp.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/Kconfig | 1 + hw/arm/fsl-imx8mm.c | 24 ++++++++++++++++++++++++ hw/arm/imx8mm-evk.c | 1 + include/hw/arm/fsl-imx8mm.h | 10 +++++++++- 4 files changed, 35 insertions(+), 1 deletion(-) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -XXX,XX +XXX,XX @@ config FSL_IMX8MM select FSL_IMX8MP_ANALOG select FSL_IMX8MP_CCM select IMX + select IMX_FEC select IMX_I2C select OR_IRQ select SDHCI diff --git a/hw/arm/fsl-imx8mm.c b/hw/arm/fsl-imx8mm.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/fsl-imx8mm.c +++ b/hw/arm/fsl-imx8mm.c @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_init(Object *obj) object_initialize_child(obj, name, &s->wdt[i], TYPE_IMX2_WDT); } + object_initialize_child(obj, "eth0", &s->enet, TYPE_IMX_ENET); + object_initialize_child(obj, "pcie", &s->pcie, TYPE_DESIGNWARE_PCIE_HOST); object_initialize_child(obj, "pcie_phy", &s->pcie_phy, TYPE_FSL_IMX8M_PCIE_PHY); @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in(gicdev, spi_table[i].irq)); } + /* ENET1 */ + object_property_set_uint(OBJECT(&s->enet), "phy-num", s->phy_num, + &error_abort); + object_property_set_uint(OBJECT(&s->enet), "tx-ring-num", 3, &error_abort); + qemu_configure_nic_device(DEVICE(&s->enet), true, NULL); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->enet), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->enet), 0, + fsl_imx8mm_memmap[FSL_IMX8MM_ENET1].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->enet), 0, + qdev_get_gpio_in(gicdev, FSL_IMX8MM_ENET1_MAC_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->enet), 1, + qdev_get_gpio_in(gicdev, FSL_IMX6_ENET1_MAC_1588_IRQ)); + /* SNVS */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->snvs), errp)) { return; @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) case FSL_IMX8MM_GPIO1 ... FSL_IMX8MM_GPIO5: case FSL_IMX8MM_GPT1 ... FSL_IMX8MM_GPT6: case FSL_IMX8MM_ECSPI1 ... FSL_IMX8MM_ECSPI3: + case FSL_IMX8MM_ENET1: case FSL_IMX8MM_I2C1 ... FSL_IMX8MM_I2C4: case FSL_IMX8MM_PCIE1: case FSL_IMX8MM_PCIE_PHY1: @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) } } +static const Property fsl_imx8mm_properties[] = { + DEFINE_PROP_UINT32("fec1-phy-num", FslImx8mmState, phy_num, 0), + DEFINE_PROP_BOOL("fec1-phy-connected", FslImx8mmState, phy_connected, true), +}; + static void fsl_imx8mm_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); + device_class_set_props(dc, fsl_imx8mm_properties); dc->realize = fsl_imx8mm_realize; dc->desc = "i.MX 8MM SoC"; diff --git a/hw/arm/imx8mm-evk.c b/hw/arm/imx8mm-evk.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/imx8mm-evk.c +++ b/hw/arm/imx8mm-evk.c @@ -XXX,XX +XXX,XX @@ static void imx8mm_evk_init(MachineState *machine) s = FSL_IMX8MM(object_new_with_props(TYPE_FSL_IMX8MM, OBJECT(machine), "soc", &error_fatal, NULL)); + object_property_set_uint(OBJECT(s), "fec1-phy-num", 1, &error_fatal); sysbus_realize_and_unref(SYS_BUS_DEVICE(s), &error_fatal); memory_region_add_subregion(get_system_memory(), FSL_IMX8MM_RAM_START, diff --git a/include/hw/arm/fsl-imx8mm.h b/include/hw/arm/fsl-imx8mm.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/arm/fsl-imx8mm.h +++ b/include/hw/arm/fsl-imx8mm.h @@ -XXX,XX +XXX,XX @@ #include "hw/misc/imx7_snvs.h" #include "hw/misc/imx8mp_analog.h" #include "hw/misc/imx8mp_ccm.h" -#include "hw/or-irq.h" +#include "hw/net/imx_fec.h" +#include "hw/core/or-irq.h" #include "hw/pci-host/designware.h" #include "hw/pci-host/fsl_imx8m_phy.h" #include "hw/sd/sdhci.h" @@ -XXX,XX +XXX,XX @@ struct FslImx8mmState { IMXI2CState i2c[FSL_IMX8MM_NUM_I2CS]; IMXSerialState uart[FSL_IMX8MM_NUM_UARTS]; MemoryRegion ocram; + IMXFECState enet; SDHCIState usdhc[FSL_IMX8MM_NUM_USDHCS]; IMX2WdtState wdt[FSL_IMX8MM_NUM_WDTS]; DesignwarePCIEHost pcie; FslImx8mPciePhyState pcie_phy; OrIRQState gpt5_gpt6_irq; + + uint32_t phy_num; + bool phy_connected; }; enum FslImx8mmMemoryRegions { @@ -XXX,XX +XXX,XX @@ enum FslImx8mmIrqs { FSL_IMX8MM_WDOG2_IRQ = 79, FSL_IMX8MM_WDOG3_IRQ = 10, + FSL_IMX8MM_ENET1_MAC_IRQ = 118, + FSL_IMX6_ENET1_MAC_1588_IRQ = 121, + FSL_IMX8MM_PCI_INTA_IRQ = 122, FSL_IMX8MM_PCI_INTB_IRQ = 123, FSL_IMX8MM_PCI_INTC_IRQ = 124, -- 2.43.0
From: Gaurav Sharma <gaurav.sharma_7@nxp.com> It enables emulation of USB on iMX8MM Enables testing and debugging of USB drivers Reviewed-by: Philippe Mathieu-Daude <philmd@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Bernhard Beschow <shentey@gmail.com> Signed-off-by: Gaurav Sharma <gaurav.sharma_7@nxp.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/Kconfig | 1 + hw/arm/fsl-imx8mm.c | 27 +++++++++++++++++++++++++++ include/hw/arm/fsl-imx8mm.h | 6 ++++++ 3 files changed, 34 insertions(+) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -XXX,XX +XXX,XX @@ config FSL_IMX8MM select SDHCI select PCI_EXPRESS_DESIGNWARE select PCI_EXPRESS_FSL_IMX8M_PHY + select USB_DWC3 select WDT_IMX2 config FSL_IMX8MM_EVK diff --git a/hw/arm/fsl-imx8mm.c b/hw/arm/fsl-imx8mm.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/fsl-imx8mm.c +++ b/hw/arm/fsl-imx8mm.c @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_init(Object *obj) object_initialize_child(obj, name, &s->usdhc[i], TYPE_IMX_USDHC); } + for (i = 0; i < FSL_IMX8MM_NUM_USBS; i++) { + g_autofree char *name = g_strdup_printf("usb%d", i); + object_initialize_child(obj, name, &s->usb[i], TYPE_USB_DWC3); + } + for (i = 0; i < FSL_IMX8MM_NUM_ECSPIS; i++) { g_autofree char *name = g_strdup_printf("spi%d", i + 1); object_initialize_child(obj, name, &s->spi[i], TYPE_IMX_SPI); @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in(gicdev, usdhc_table[i].irq)); } + /* USBs */ + for (i = 0; i < FSL_IMX8MM_NUM_USBS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } usb_table[FSL_IMX8MM_NUM_USBS] = { + { fsl_imx8mm_memmap[FSL_IMX8MM_USB1].addr, FSL_IMX8MM_USB1_IRQ }, + { fsl_imx8mm_memmap[FSL_IMX8MM_USB2].addr, FSL_IMX8MM_USB2_IRQ }, + }; + + qdev_prop_set_uint32(DEVICE(&s->usb[i].sysbus_xhci), "p2", 1); + qdev_prop_set_uint32(DEVICE(&s->usb[i].sysbus_xhci), "p3", 1); + qdev_prop_set_uint32(DEVICE(&s->usb[i].sysbus_xhci), "slots", 2); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->usb[i]), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->usb[i]), 0, usb_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->usb[i].sysbus_xhci), 0, + qdev_get_gpio_in(gicdev, usb_table[i].irq)); + } + /* ECSPIs */ for (i = 0; i < FSL_IMX8MM_NUM_ECSPIS; i++) { static const struct { @@ -XXX,XX +XXX,XX @@ static void fsl_imx8mm_realize(DeviceState *dev, Error **errp) case FSL_IMX8MM_OCRAM: case FSL_IMX8MM_SNVS_HP: case FSL_IMX8MM_UART1 ... FSL_IMX8MM_UART4: + case FSL_IMX8MM_USB1 ... FSL_IMX8MM_USB2: case FSL_IMX8MM_USDHC1 ... FSL_IMX8MM_USDHC3: case FSL_IMX8MM_WDOG1 ... FSL_IMX8MM_WDOG3: /* device implemented and treated above */ diff --git a/include/hw/arm/fsl-imx8mm.h b/include/hw/arm/fsl-imx8mm.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/arm/fsl-imx8mm.h +++ b/include/hw/arm/fsl-imx8mm.h @@ -XXX,XX +XXX,XX @@ #include "hw/sd/sdhci.h" #include "hw/ssi/imx_spi.h" #include "hw/timer/imx_gpt.h" +#include "hw/usb/hcd-dwc3.h" #include "hw/watchdog/wdt_imx2.h" #include "qom/object.h" #include "qemu/units.h" @@ -XXX,XX +XXX,XX @@ enum FslImx8mmConfiguration { FSL_IMX8MM_NUM_I2CS = 4, FSL_IMX8MM_NUM_IRQS = 128, FSL_IMX8MM_NUM_UARTS = 4, + FSL_IMX8MM_NUM_USBS = 2, FSL_IMX8MM_NUM_USDHCS = 3, FSL_IMX8MM_NUM_WDTS = 3, }; @@ -XXX,XX +XXX,XX @@ struct FslImx8mmState { IMXFECState enet; SDHCIState usdhc[FSL_IMX8MM_NUM_USDHCS]; IMX2WdtState wdt[FSL_IMX8MM_NUM_WDTS]; + USBDWC3 usb[FSL_IMX8MM_NUM_USBS]; DesignwarePCIEHost pcie; FslImx8mPciePhyState pcie_phy; OrIRQState gpt5_gpt6_irq; @@ -XXX,XX +XXX,XX @@ enum FslImx8mmIrqs { FSL_IMX8MM_I2C3_IRQ = 37, FSL_IMX8MM_I2C4_IRQ = 38, + FSL_IMX8MM_USB1_IRQ = 40, + FSL_IMX8MM_USB2_IRQ = 41, + FSL_IMX8MM_GPT1_IRQ = 55, FSL_IMX8MM_GPT2_IRQ = 54, FSL_IMX8MM_GPT3_IRQ = 53, -- 2.43.0
From: Gaurav Sharma <gaurav.sharma_7@nxp.com> Added script that would validate the iMX8MM emulation by checking the linux console log. If it succeeds, it will return:- ok 1 test_imx8mm_evk.Imx8mmEvkMachine.test_aarch64_imx8mm_evk_usdhc Signed-off-by: Gaurav Sharma <gaurav.sharma_7@nxp.com> Reviewed-by: Thomas Huth <thuth@redhat.com> Reviewed-by: Bernhard Beschow <shentey@gmail.com> [PMM: Add missing blank lines that flake8 wants] Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- MAINTAINERS | 1 + tests/functional/aarch64/meson.build | 2 + tests/functional/aarch64/test_imx8mm_evk.py | 69 +++++++++++++++++++++ 3 files changed, 72 insertions(+) create mode 100755 tests/functional/aarch64/test_imx8mm_evk.py diff --git a/MAINTAINERS b/MAINTAINERS index XXXXXXX..XXXXXXX 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -XXX,XX +XXX,XX @@ F: hw/arm/fsl-imx8mm.c F: hw/arm/imx8mm-evk.c F: include/hw/arm/fsl-imx8mm.h F: docs/system/arm/imx8m.rst +F: tests/functional/aarch64/test_imx8mm_evk.py MCIMX8MP-EVK / i.MX8MP M: Bernhard Beschow <shentey@gmail.com> diff --git a/tests/functional/aarch64/meson.build b/tests/functional/aarch64/meson.build index XXXXXXX..XXXXXXX 100644 --- a/tests/functional/aarch64/meson.build +++ b/tests/functional/aarch64/meson.build @@ -XXX,XX +XXX,XX @@ test_aarch64_timeouts = { 'aspeed_ast2700a2' : 600, 'aspeed_ast2700fc' : 600, 'device_passthrough' : 720, + 'imx8mm_evk' : 240, 'imx8mp_evk' : 240, 'raspi4' : 480, 'reverse_debug' : 180, @@ -XXX,XX +XXX,XX @@ tests_aarch64_system_thorough = [ 'aspeed_ast2700fc', 'device_passthrough', 'hotplug_pci', + 'imx8mm_evk', 'imx8mp_evk', 'kvm', 'multiprocess', diff --git a/tests/functional/aarch64/test_imx8mm_evk.py b/tests/functional/aarch64/test_imx8mm_evk.py new file mode 100755 index XXXXXXX..XXXXXXX --- /dev/null +++ b/tests/functional/aarch64/test_imx8mm_evk.py @@ -XXX,XX +XXX,XX @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset + + +class Imx8mmEvkMachine(LinuxKernelTest): + + ASSET_IMAGE = Asset( + ('https://cloud.debian.org/images/cloud/bookworm/20231210-1590/' + 'debian-12-generic-arm64-20231210-1590.tar.xz'), + '7ebf1577b32d5af6204df74b54ca2e4675de9b5a9fa14f3ff70b88eeb7b3b359') + + KERNEL_OFFSET = 0x51000000 + KERNEL_SIZE = 32622528 + INITRD_OFFSET = 0x76000000 + INITRD_SIZE = 30987766 + DTB_OFFSET = 0x64DB5000 + DTB_SIZE = 36812 + + def extract(self, in_path, out_path, offset, size): + try: + with open(in_path, "rb") as source: + source.seek(offset) + data = source.read(size) + with open(out_path, "wb") as target: + target.write(data) + except (IOError, ValueError) as e: + self.log.error(f"Failed to extract {out_path}: {e}") + raise + + def setUp(self): + super().setUp() + + self.image_path = self.scratch_file("disk.raw") + self.kernel_path = self.scratch_file("linux") + self.initrd_path = self.scratch_file("initrd.zstd") + self.dtb_path = self.scratch_file("imx8mm-evk.dtb") + + self.archive_extract(self.ASSET_IMAGE) + self.extract(self.image_path, self.kernel_path, + self.KERNEL_OFFSET, self.KERNEL_SIZE) + self.extract(self.image_path, self.initrd_path, + self.INITRD_OFFSET, self.INITRD_SIZE) + self.extract(self.image_path, self.dtb_path, + self.DTB_OFFSET, self.DTB_SIZE) + + def test_aarch64_imx8mm_evk_usdhc(self): + self.require_accelerator("tcg") + self.set_machine('imx8mm-evk') + self.vm.set_console(console_index=1) + self.vm.add_args('-m', '2G', + '-smp', '4', + '-kernel', self.kernel_path, + '-initrd', self.initrd_path, + '-dtb', self.dtb_path, + '-append', 'root=/dev/mmcblk2p1', + '-drive', f'file={self.image_path},if=sd,bus=2,' + 'format=raw,id=mmcblk2,snapshot=on') + + self.vm.launch() + self.wait_for_console_pattern('Welcome to ') + + +if __name__ == '__main__': + LinuxKernelTest.main() -- 2.43.0
From: Philippe Mathieu-Daudé <philmd@linaro.org> As mentioned by Richard in [*]: We don't actually need any specific endianness here, because every byte has the same value. So we could simply drop MO_TE. That would produce a store in host-endianness, which will be fractionally more efficient on some hosts. [*] https://lore.kernel.org/qemu-devel/d64d3576-9188-4e74-afdc-3492c9feb8e0@linaro.org/ Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20260414005348.4767-2-philmd@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/helper-a64.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -XXX,XX +XXX,XX @@ static uint64_t set_step_tags(CPUARMState *env, uint64_t toaddr, * the page dirty and will use the fast path. */ uint64_t repldata = data * 0x0101010101010101ULL; - MemOpIdx oi16 = make_memop_idx(MO_TE | MO_128, memidx); + MemOpIdx oi16 = make_memop_idx(MO_128, memidx); cpu_st16_mmu(env, toaddr, int128_make128(repldata, repldata), oi16, ra); mte_mops_set_tags(env, toaddr, 16, *mtedesc); return 16; -- 2.43.0
From: Philippe Mathieu-Daudé <philmd@linaro.org> Extract the implicit MO_TE definition in order to replace it in the next commit. Mechanical change using: $ for n in UW UL UQ UO SW SL SQ; do \ sed -i -e "s/MO_TE$n/MO_TE | MO_$n/" \ $(git grep -l MO_TE$n target/arm); \ done Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20260414005348.4767-3-philmd@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/m_helper.c | 6 +-- target/arm/tcg/mve_helper.c | 79 ++++++++++++++++++++----------------- 2 files changed, 45 insertions(+), 40 deletions(-) diff --git a/target/arm/tcg/m_helper.c b/target/arm/tcg/m_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/m_helper.c +++ b/target/arm/tcg/m_helper.c @@ -XXX,XX +XXX,XX @@ void HELPER(v7m_blxns)(CPUARMState *env, uint32_t dest) /* Note that these stores can throw exceptions on MPU faults */ ARMMMUIdx mmu_idx = arm_mmu_idx(env); - MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, + MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, arm_to_core_mmu_idx(mmu_idx)); cpu_stl_mmu(env, sp, nextinst, oi, GETPC()); cpu_stl_mmu(env, sp + 4, saved_psr, oi, GETPC()); @@ -XXX,XX +XXX,XX @@ void HELPER(v7m_vlstm)(CPUARMState *env, uint32_t fptr) bool lspact = env->v7m.fpccr[s] & R_V7M_FPCCR_LSPACT_MASK; uintptr_t ra = GETPC(); ARMMMUIdx mmu_idx = arm_mmu_idx(env); - MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, + MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, arm_to_core_mmu_idx(mmu_idx)); assert(env->v7m.secure); @@ -XXX,XX +XXX,XX @@ void HELPER(v7m_vlldm)(CPUARMState *env, uint32_t fptr) ARMCPU *cpu = env_archcpu(env); uintptr_t ra = GETPC(); ARMMMUIdx mmu_idx = arm_mmu_idx(env); - MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, + MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, arm_to_core_mmu_idx(mmu_idx)); /* fptr is the value of Rn, the frame pointer we load the FP regs from */ diff --git a/target/arm/tcg/mve_helper.c b/target/arm/tcg/mve_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/mve_helper.c +++ b/target/arm/tcg/mve_helper.c @@ -XXX,XX +XXX,XX @@ static void mve_advance_vpt(CPUARMState *env) } DO_VLDR(vldrb, MO_UB, 1, uint8_t, ldb, 1, uint8_t) -DO_VLDR(vldrh, MO_TEUW, 2, uint16_t, ldw, 2, uint16_t) -DO_VLDR(vldrw, MO_TEUL, 4, uint32_t, ldl, 4, uint32_t) +DO_VLDR(vldrh, MO_TE | MO_UW, 2, uint16_t, ldw, 2, uint16_t) +DO_VLDR(vldrw, MO_TE | MO_UL, 4, uint32_t, ldl, 4, uint32_t) DO_VSTR(vstrb, MO_UB, 1, stb, 1, uint8_t) -DO_VSTR(vstrh, MO_TEUW, 2, stw, 2, uint16_t) -DO_VSTR(vstrw, MO_TEUL, 4, stl, 4, uint32_t) +DO_VSTR(vstrh, MO_TE | MO_UW, 2, stw, 2, uint16_t) +DO_VSTR(vstrw, MO_TE | MO_UL, 4, stl, 4, uint32_t) DO_VLDR(vldrb_sh, MO_SB, 1, int8_t, ldb, 2, int16_t) DO_VLDR(vldrb_sw, MO_SB, 1, int8_t, ldb, 4, int32_t) DO_VLDR(vldrb_uh, MO_UB, 1, uint8_t, ldb, 2, uint16_t) DO_VLDR(vldrb_uw, MO_UB, 1, uint8_t, ldb, 4, uint32_t) -DO_VLDR(vldrh_sw, MO_TESW, 2, int16_t, ldw, 4, int32_t) -DO_VLDR(vldrh_uw, MO_TEUW, 2, uint16_t, ldw, 4, uint32_t) +DO_VLDR(vldrh_sw, MO_TE | MO_SW, 2, int16_t, ldw, 4, int32_t) +DO_VLDR(vldrh_uw, MO_TE | MO_UW, 2, uint16_t, ldw, 4, uint32_t) DO_VSTR(vstrb_h, MO_UB, 1, stb, 2, int16_t) DO_VSTR(vstrb_w, MO_UB, 1, stb, 4, int32_t) -DO_VSTR(vstrh_w, MO_TEUW, 2, stw, 4, int32_t) +DO_VSTR(vstrh_w, MO_TE | MO_UW, 2, stw, 4, int32_t) #undef DO_VLDR #undef DO_VSTR @@ -XXX,XX +XXX,XX @@ DO_VSTR(vstrh_w, MO_TEUW, 2, stw, 4, int32_t) unsigned e; \ uint32_t addr; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mmu_idx); \ + MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ for (e = 0; e < 16 / 4; e++, mask >>= 4, eci_mask >>= 4) { \ if (!(eci_mask & 1)) { \ continue; \ @@ -XXX,XX +XXX,XX @@ DO_VSTR(vstrh_w, MO_TEUW, 2, stw, 4, int32_t) unsigned e; \ uint32_t addr; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mmu_idx); \ + MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ for (e = 0; e < 16 / 4; e++, mask >>= 4, eci_mask >>= 4) { \ if (!(eci_mask & 1)) { \ continue; \ @@ -XXX,XX +XXX,XX @@ DO_VSTR(vstrh_w, MO_TEUW, 2, stw, 4, int32_t) DO_VLDR_SG(vldrb_sg_sh, MO_SB, int8_t, ldb, 2, int16_t, uint16_t, ADDR_ADD, false) DO_VLDR_SG(vldrb_sg_sw, MO_SB, int8_t, ldb, 4, int32_t, uint32_t, ADDR_ADD, false) -DO_VLDR_SG(vldrh_sg_sw, MO_TESW, int16_t, ldw, 4, int32_t, uint32_t, ADDR_ADD, false) +DO_VLDR_SG(vldrh_sg_sw, MO_TE | MO_SW, int16_t, ldw, 4, + int32_t, uint32_t, ADDR_ADD, false) DO_VLDR_SG(vldrb_sg_ub, MO_UB, uint8_t, ldb, 1, uint8_t, uint8_t, ADDR_ADD, false) DO_VLDR_SG(vldrb_sg_uh, MO_UB, uint8_t, ldb, 2, uint16_t, uint16_t, ADDR_ADD, false) DO_VLDR_SG(vldrb_sg_uw, MO_UB, uint8_t, ldb, 4, uint32_t, uint32_t, ADDR_ADD, false) -DO_VLDR_SG(vldrh_sg_uh, MO_TEUW, uint16_t, ldw, 2, uint16_t, uint16_t, ADDR_ADD, false) -DO_VLDR_SG(vldrh_sg_uw, MO_TEUW, uint16_t, ldw, 4, uint32_t, uint32_t, ADDR_ADD, false) -DO_VLDR_SG(vldrw_sg_uw, MO_TEUL, uint32_t, ldl, 4, uint32_t, uint32_t, ADDR_ADD, false) +DO_VLDR_SG(vldrh_sg_uh, MO_TE | MO_UW, uint16_t, ldw, 2, + uint16_t, uint16_t, ADDR_ADD, false) +DO_VLDR_SG(vldrh_sg_uw, MO_TE | MO_UW, uint16_t, ldw, 4, + uint32_t, uint32_t, ADDR_ADD, false) +DO_VLDR_SG(vldrw_sg_uw, MO_TE | MO_UL, uint32_t, ldl, 4, + uint32_t, uint32_t, ADDR_ADD, false) DO_VLDR64_SG(vldrd_sg_ud, ADDR_ADD, false) -DO_VLDR_SG(vldrh_sg_os_sw, MO_TESW, int16_t, ldw, 4, +DO_VLDR_SG(vldrh_sg_os_sw, MO_TE | MO_SW, int16_t, ldw, 4, int32_t, uint32_t, ADDR_ADD_OSH, false) -DO_VLDR_SG(vldrh_sg_os_uh, MO_TEUW, uint16_t, ldw, 2, +DO_VLDR_SG(vldrh_sg_os_uh, MO_TE | MO_UW, uint16_t, ldw, 2, uint16_t, uint16_t, ADDR_ADD_OSH, false) -DO_VLDR_SG(vldrh_sg_os_uw, MO_TEUW, uint16_t, ldw, 4, +DO_VLDR_SG(vldrh_sg_os_uw, MO_TE | MO_UW, uint16_t, ldw, 4, uint32_t, uint32_t, ADDR_ADD_OSH, false) -DO_VLDR_SG(vldrw_sg_os_uw, MO_TEUL, uint32_t, ldl, 4, +DO_VLDR_SG(vldrw_sg_os_uw, MO_TE | MO_UL, uint32_t, ldl, 4, uint32_t, uint32_t, ADDR_ADD_OSW, false) DO_VLDR64_SG(vldrd_sg_os_ud, ADDR_ADD_OSD, false) DO_VSTR_SG(vstrb_sg_ub, MO_UB, stb, 1, uint8_t, ADDR_ADD, false) DO_VSTR_SG(vstrb_sg_uh, MO_UB, stb, 2, uint16_t, ADDR_ADD, false) DO_VSTR_SG(vstrb_sg_uw, MO_UB, stb, 4, uint32_t, ADDR_ADD, false) -DO_VSTR_SG(vstrh_sg_uh, MO_TEUW, stw, 2, uint16_t, ADDR_ADD, false) -DO_VSTR_SG(vstrh_sg_uw, MO_TEUW, stw, 4, uint32_t, ADDR_ADD, false) -DO_VSTR_SG(vstrw_sg_uw, MO_TEUL, stl, 4, uint32_t, ADDR_ADD, false) +DO_VSTR_SG(vstrh_sg_uh, MO_TE | MO_UW, stw, 2, uint16_t, ADDR_ADD, false) +DO_VSTR_SG(vstrh_sg_uw, MO_TE | MO_UW, stw, 4, uint32_t, ADDR_ADD, false) +DO_VSTR_SG(vstrw_sg_uw, MO_TE | MO_UL, stl, 4, uint32_t, ADDR_ADD, false) DO_VSTR64_SG(vstrd_sg_ud, ADDR_ADD, false) -DO_VSTR_SG(vstrh_sg_os_uh, MO_TEUW, stw, 2, uint16_t, ADDR_ADD_OSH, false) -DO_VSTR_SG(vstrh_sg_os_uw, MO_TEUW, stw, 4, uint32_t, ADDR_ADD_OSH, false) -DO_VSTR_SG(vstrw_sg_os_uw, MO_TEUL, stl, 4, uint32_t, ADDR_ADD_OSW, false) +DO_VSTR_SG(vstrh_sg_os_uh, MO_TE | MO_UW, stw, 2, uint16_t, ADDR_ADD_OSH, false) +DO_VSTR_SG(vstrh_sg_os_uw, MO_TE | MO_UW, stw, 4, uint32_t, ADDR_ADD_OSH, false) +DO_VSTR_SG(vstrw_sg_os_uw, MO_TE | MO_UL, stl, 4, uint32_t, ADDR_ADD_OSW, false) DO_VSTR64_SG(vstrd_sg_os_ud, ADDR_ADD_OSD, false) -DO_VLDR_SG(vldrw_sg_wb_uw, MO_TEUL, uint32_t, ldl, 4, uint32_t, uint32_t, ADDR_ADD, true) +DO_VLDR_SG(vldrw_sg_wb_uw, MO_TE | MO_UL, uint32_t, ldl, 4, + uint32_t, uint32_t, ADDR_ADD, true) DO_VLDR64_SG(vldrd_sg_wb_ud, ADDR_ADD, true) -DO_VSTR_SG(vstrw_sg_wb_uw, MO_TEUL, stl, 4, uint32_t, ADDR_ADD, true) +DO_VSTR_SG(vstrw_sg_wb_uw, MO_TE | MO_UL, stl, 4, uint32_t, ADDR_ADD, true) DO_VSTR64_SG(vstrd_sg_wb_ud, ADDR_ADD, true) /* @@ -XXX,XX +XXX,XX @@ DO_VSTR64_SG(vstrd_sg_wb_ud, ADDR_ADD, true) static const uint8_t off[4] = { O1, O2, O3, O4 }; \ uint32_t addr, data; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mmu_idx); \ + MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -XXX,XX +XXX,XX @@ DO_VSTR64_SG(vstrd_sg_wb_ud, ADDR_ADD, true) int y; /* y counts 0 2 0 2 */ \ uint16_t *qd; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mmu_idx); \ + MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ for (beat = 0, y = 0; beat < 4; beat++, mask >>= 4, y ^= 2) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -XXX,XX +XXX,XX @@ DO_VSTR64_SG(vstrd_sg_wb_ud, ADDR_ADD, true) uint32_t *qd; \ int y; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mmu_idx); \ + MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -XXX,XX +XXX,XX @@ DO_VLD4W(vld43w, 6, 7, 8, 9) uint32_t addr, data; \ uint8_t *qd; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mmu_idx); \ + MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -XXX,XX +XXX,XX @@ DO_VLD4W(vld43w, 6, 7, 8, 9) int e; \ uint16_t *qd; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mmu_idx); \ + MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -XXX,XX +XXX,XX @@ DO_VLD4W(vld43w, 6, 7, 8, 9) uint32_t addr, data; \ uint32_t *qd; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mmu_idx); \ + MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -XXX,XX +XXX,XX @@ DO_VLD2W(vld21w, 8, 12, 16, 20) static const uint8_t off[4] = { O1, O2, O3, O4 }; \ uint32_t addr, data; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mmu_idx); \ + MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -XXX,XX +XXX,XX @@ DO_VLD2W(vld21w, 8, 12, 16, 20) int y; /* y counts 0 2 0 2 */ \ uint16_t *qd; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mmu_idx); \ + MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ for (beat = 0, y = 0; beat < 4; beat++, mask >>= 4, y ^= 2) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -XXX,XX +XXX,XX @@ DO_VLD2W(vld21w, 8, 12, 16, 20) uint32_t *qd; \ int y; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mmu_idx); \ + MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -XXX,XX +XXX,XX @@ DO_VST4W(vst43w, 6, 7, 8, 9) uint32_t addr, data; \ uint8_t *qd; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mmu_idx); \ + MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -XXX,XX +XXX,XX @@ DO_VST4W(vst43w, 6, 7, 8, 9) int e; \ uint16_t *qd; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mmu_idx); \ + MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -XXX,XX +XXX,XX @@ DO_VST4W(vst43w, 6, 7, 8, 9) uint32_t addr, data; \ uint32_t *qd; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mmu_idx); \ + MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ -- 2.43.0
From: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20260414005348.4767-4-philmd@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/mve_helper.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/target/arm/tcg/mve_helper.c b/target/arm/tcg/mve_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/mve_helper.c +++ b/target/arm/tcg/mve_helper.c @@ -XXX,XX +XXX,XX @@ static void mve_advance_vpt(CPUARMState *env) uint16_t eci_mask = mve_eci_mask(env); \ unsigned b, e; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MFLAG | MO_ALIGN, mmu_idx); \ + MemOpIdx oi = make_memop_idx(MO_TE | MFLAG | MO_ALIGN, \ + mmu_idx); \ /* \ * R_SXTM allows the dest reg to become UNKNOWN for abandoned \ * beats so we don't care if we update part of the dest and \ @@ -XXX,XX +XXX,XX @@ static void mve_advance_vpt(CPUARMState *env) uint16_t mask = mve_element_mask(env); \ unsigned b, e; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MFLAG | MO_ALIGN, mmu_idx); \ + MemOpIdx oi = make_memop_idx(MO_TE | MFLAG | MO_ALIGN, \ + mmu_idx); \ for (b = 0, e = 0; b < 16; b += ESIZE, e++) { \ if (mask & (1 << b)) { \ cpu_##STTYPE##_mmu(env, addr, d[H##ESIZE(e)], oi, GETPC()); \ @@ -XXX,XX +XXX,XX @@ static void mve_advance_vpt(CPUARMState *env) } DO_VLDR(vldrb, MO_UB, 1, uint8_t, ldb, 1, uint8_t) -DO_VLDR(vldrh, MO_TE | MO_UW, 2, uint16_t, ldw, 2, uint16_t) -DO_VLDR(vldrw, MO_TE | MO_UL, 4, uint32_t, ldl, 4, uint32_t) +DO_VLDR(vldrh, MO_UW, 2, uint16_t, ldw, 2, uint16_t) +DO_VLDR(vldrw, MO_UL, 4, uint32_t, ldl, 4, uint32_t) DO_VSTR(vstrb, MO_UB, 1, stb, 1, uint8_t) -DO_VSTR(vstrh, MO_TE | MO_UW, 2, stw, 2, uint16_t) -DO_VSTR(vstrw, MO_TE | MO_UL, 4, stl, 4, uint32_t) +DO_VSTR(vstrh, MO_UW, 2, stw, 2, uint16_t) +DO_VSTR(vstrw, MO_UL, 4, stl, 4, uint32_t) DO_VLDR(vldrb_sh, MO_SB, 1, int8_t, ldb, 2, int16_t) DO_VLDR(vldrb_sw, MO_SB, 1, int8_t, ldb, 4, int32_t) DO_VLDR(vldrb_uh, MO_UB, 1, uint8_t, ldb, 2, uint16_t) DO_VLDR(vldrb_uw, MO_UB, 1, uint8_t, ldb, 4, uint32_t) -DO_VLDR(vldrh_sw, MO_TE | MO_SW, 2, int16_t, ldw, 4, int32_t) -DO_VLDR(vldrh_uw, MO_TE | MO_UW, 2, uint16_t, ldw, 4, uint32_t) +DO_VLDR(vldrh_sw, MO_SW, 2, int16_t, ldw, 4, int32_t) +DO_VLDR(vldrh_uw, MO_UW, 2, uint16_t, ldw, 4, uint32_t) DO_VSTR(vstrb_h, MO_UB, 1, stb, 2, int16_t) DO_VSTR(vstrb_w, MO_UB, 1, stb, 4, int32_t) -DO_VSTR(vstrh_w, MO_TE | MO_UW, 2, stw, 4, int32_t) +DO_VSTR(vstrh_w, MO_UW, 2, stw, 4, int32_t) #undef DO_VLDR #undef DO_VSTR -- 2.43.0
From: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20260414005348.4767-5-philmd@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/mve_helper.c | 43 +++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/target/arm/tcg/mve_helper.c b/target/arm/tcg/mve_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/mve_helper.c +++ b/target/arm/tcg/mve_helper.c @@ -XXX,XX +XXX,XX @@ DO_VSTR(vstrh_w, MO_UW, 2, stw, 4, int32_t) unsigned e; \ uint32_t addr; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MFLAG | MO_ALIGN, mmu_idx); \ + MemOpIdx oi = make_memop_idx(MO_TE | MFLAG | MO_ALIGN, \ + mmu_idx); \ for (e = 0; e < 16 / ESIZE; e++, mask >>= ESIZE, eci_mask >>= ESIZE) { \ if (!(eci_mask & 1)) { \ continue; \ @@ -XXX,XX +XXX,XX @@ DO_VSTR(vstrh_w, MO_UW, 2, stw, 4, int32_t) unsigned e; \ uint32_t addr; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MFLAG | MO_ALIGN, mmu_idx); \ + MemOpIdx oi = make_memop_idx(MO_TE | MFLAG | MO_ALIGN, \ + mmu_idx); \ for (e = 0; e < 16 / ESIZE; e++, mask >>= ESIZE, eci_mask >>= ESIZE) { \ if (!(eci_mask & 1)) { \ continue; \ @@ -XXX,XX +XXX,XX @@ DO_VSTR(vstrh_w, MO_UW, 2, stw, 4, int32_t) DO_VLDR_SG(vldrb_sg_sh, MO_SB, int8_t, ldb, 2, int16_t, uint16_t, ADDR_ADD, false) DO_VLDR_SG(vldrb_sg_sw, MO_SB, int8_t, ldb, 4, int32_t, uint32_t, ADDR_ADD, false) -DO_VLDR_SG(vldrh_sg_sw, MO_TE | MO_SW, int16_t, ldw, 4, - int32_t, uint32_t, ADDR_ADD, false) +DO_VLDR_SG(vldrh_sg_sw, MO_SW, int16_t, ldw, 4, int32_t, uint32_t, ADDR_ADD, false) DO_VLDR_SG(vldrb_sg_ub, MO_UB, uint8_t, ldb, 1, uint8_t, uint8_t, ADDR_ADD, false) DO_VLDR_SG(vldrb_sg_uh, MO_UB, uint8_t, ldb, 2, uint16_t, uint16_t, ADDR_ADD, false) DO_VLDR_SG(vldrb_sg_uw, MO_UB, uint8_t, ldb, 4, uint32_t, uint32_t, ADDR_ADD, false) -DO_VLDR_SG(vldrh_sg_uh, MO_TE | MO_UW, uint16_t, ldw, 2, - uint16_t, uint16_t, ADDR_ADD, false) -DO_VLDR_SG(vldrh_sg_uw, MO_TE | MO_UW, uint16_t, ldw, 4, - uint32_t, uint32_t, ADDR_ADD, false) -DO_VLDR_SG(vldrw_sg_uw, MO_TE | MO_UL, uint32_t, ldl, 4, - uint32_t, uint32_t, ADDR_ADD, false) +DO_VLDR_SG(vldrh_sg_uh, MO_UW, uint16_t, ldw, 2, uint16_t, uint16_t, ADDR_ADD, false) +DO_VLDR_SG(vldrh_sg_uw, MO_UW, uint16_t, ldw, 4, uint32_t, uint32_t, ADDR_ADD, false) +DO_VLDR_SG(vldrw_sg_uw, MO_UL, uint32_t, ldl, 4, uint32_t, uint32_t, ADDR_ADD, false) DO_VLDR64_SG(vldrd_sg_ud, ADDR_ADD, false) -DO_VLDR_SG(vldrh_sg_os_sw, MO_TE | MO_SW, int16_t, ldw, 4, +DO_VLDR_SG(vldrh_sg_os_sw, MO_SW, int16_t, ldw, 4, int32_t, uint32_t, ADDR_ADD_OSH, false) -DO_VLDR_SG(vldrh_sg_os_uh, MO_TE | MO_UW, uint16_t, ldw, 2, +DO_VLDR_SG(vldrh_sg_os_uh, MO_UW, uint16_t, ldw, 2, uint16_t, uint16_t, ADDR_ADD_OSH, false) -DO_VLDR_SG(vldrh_sg_os_uw, MO_TE | MO_UW, uint16_t, ldw, 4, +DO_VLDR_SG(vldrh_sg_os_uw, MO_UW, uint16_t, ldw, 4, uint32_t, uint32_t, ADDR_ADD_OSH, false) -DO_VLDR_SG(vldrw_sg_os_uw, MO_TE | MO_UL, uint32_t, ldl, 4, +DO_VLDR_SG(vldrw_sg_os_uw, MO_UL, uint32_t, ldl, 4, uint32_t, uint32_t, ADDR_ADD_OSW, false) DO_VLDR64_SG(vldrd_sg_os_ud, ADDR_ADD_OSD, false) DO_VSTR_SG(vstrb_sg_ub, MO_UB, stb, 1, uint8_t, ADDR_ADD, false) DO_VSTR_SG(vstrb_sg_uh, MO_UB, stb, 2, uint16_t, ADDR_ADD, false) DO_VSTR_SG(vstrb_sg_uw, MO_UB, stb, 4, uint32_t, ADDR_ADD, false) -DO_VSTR_SG(vstrh_sg_uh, MO_TE | MO_UW, stw, 2, uint16_t, ADDR_ADD, false) -DO_VSTR_SG(vstrh_sg_uw, MO_TE | MO_UW, stw, 4, uint32_t, ADDR_ADD, false) -DO_VSTR_SG(vstrw_sg_uw, MO_TE | MO_UL, stl, 4, uint32_t, ADDR_ADD, false) +DO_VSTR_SG(vstrh_sg_uh, MO_UW, stw, 2, uint16_t, ADDR_ADD, false) +DO_VSTR_SG(vstrh_sg_uw, MO_UW, stw, 4, uint32_t, ADDR_ADD, false) +DO_VSTR_SG(vstrw_sg_uw, MO_UL, stl, 4, uint32_t, ADDR_ADD, false) DO_VSTR64_SG(vstrd_sg_ud, ADDR_ADD, false) -DO_VSTR_SG(vstrh_sg_os_uh, MO_TE | MO_UW, stw, 2, uint16_t, ADDR_ADD_OSH, false) -DO_VSTR_SG(vstrh_sg_os_uw, MO_TE | MO_UW, stw, 4, uint32_t, ADDR_ADD_OSH, false) -DO_VSTR_SG(vstrw_sg_os_uw, MO_TE | MO_UL, stl, 4, uint32_t, ADDR_ADD_OSW, false) +DO_VSTR_SG(vstrh_sg_os_uh, MO_UW, stw, 2, uint16_t, ADDR_ADD_OSH, false) +DO_VSTR_SG(vstrh_sg_os_uw, MO_UW, stw, 4, uint32_t, ADDR_ADD_OSH, false) +DO_VSTR_SG(vstrw_sg_os_uw, MO_UL, stl, 4, uint32_t, ADDR_ADD_OSW, false) DO_VSTR64_SG(vstrd_sg_os_ud, ADDR_ADD_OSD, false) -DO_VLDR_SG(vldrw_sg_wb_uw, MO_TE | MO_UL, uint32_t, ldl, 4, - uint32_t, uint32_t, ADDR_ADD, true) +DO_VLDR_SG(vldrw_sg_wb_uw, MO_UL, uint32_t, ldl, 4, uint32_t, uint32_t, ADDR_ADD, true) DO_VLDR64_SG(vldrd_sg_wb_ud, ADDR_ADD, true) -DO_VSTR_SG(vstrw_sg_wb_uw, MO_TE | MO_UL, stl, 4, uint32_t, ADDR_ADD, true) +DO_VSTR_SG(vstrw_sg_wb_uw, MO_UL, stl, 4, uint32_t, ADDR_ADD, true) DO_VSTR64_SG(vstrd_sg_wb_ud, ADDR_ADD, true) /* -- 2.43.0
From: Philippe Mathieu-Daudé <philmd@linaro.org> Suggested-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20260414005348.4767-6-philmd@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/internals.h | 6 ++++++ target/arm/tcg/mve_helper.c | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/target/arm/internals.h b/target/arm/internals.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -XXX,XX +XXX,XX @@ #include "exec/hwaddr.h" #include "exec/vaddr.h" #include "exec/breakpoint.h" +#include "exec/memop.h" #include "accel/tcg/tb-cpu-state.h" #include "hw/core/registerfields.h" #include "tcg/tcg-gvec-desc.h" @@ -XXX,XX +XXX,XX @@ #define BANK_HYP 6 #define BANK_MON 7 +static inline MemOp mo_endian(CPUARMState *env) +{ + return EX_TBFLAG_ANY(env->hflags, BE_DATA) ? MO_BE : MO_LE; +} + static inline int arm_env_mmu_index(CPUARMState *env) { return EX_TBFLAG_ANY(env->hflags, MMUIDX); diff --git a/target/arm/tcg/mve_helper.c b/target/arm/tcg/mve_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/mve_helper.c +++ b/target/arm/tcg/mve_helper.c @@ -XXX,XX +XXX,XX @@ DO_VSTR(vstrh_w, MO_UW, 2, stw, 4, int32_t) unsigned e; \ uint32_t addr; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ + MemOpIdx oi = make_memop_idx(mo_endian(env) | MO_UL | MO_ALIGN, \ + mmu_idx); \ for (e = 0; e < 16 / 4; e++, mask >>= 4, eci_mask >>= 4) { \ if (!(eci_mask & 1)) { \ continue; \ -- 2.43.0
From: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20260414005348.4767-7-philmd@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/mve_helper.c | 47 +++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/target/arm/tcg/mve_helper.c b/target/arm/tcg/mve_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/mve_helper.c +++ b/target/arm/tcg/mve_helper.c @@ -XXX,XX +XXX,XX @@ static void mve_advance_vpt(CPUARMState *env) uint16_t eci_mask = mve_eci_mask(env); \ unsigned b, e; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TE | MFLAG | MO_ALIGN, \ + MemOpIdx oi = make_memop_idx(mo_endian(env) | MFLAG | MO_ALIGN, \ mmu_idx); \ /* \ * R_SXTM allows the dest reg to become UNKNOWN for abandoned \ @@ -XXX,XX +XXX,XX @@ static void mve_advance_vpt(CPUARMState *env) uint16_t mask = mve_element_mask(env); \ unsigned b, e; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TE | MFLAG | MO_ALIGN, \ + MemOpIdx oi = make_memop_idx(mo_endian(env) | MFLAG | MO_ALIGN, \ mmu_idx); \ for (b = 0, e = 0; b < 16; b += ESIZE, e++) { \ if (mask & (1 << b)) { \ @@ -XXX,XX +XXX,XX @@ DO_VSTR(vstrh_w, MO_UW, 2, stw, 4, int32_t) unsigned e; \ uint32_t addr; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TE | MFLAG | MO_ALIGN, \ + MemOpIdx oi = make_memop_idx(mo_endian(env) | MFLAG | MO_ALIGN, \ mmu_idx); \ for (e = 0; e < 16 / ESIZE; e++, mask >>= ESIZE, eci_mask >>= ESIZE) { \ if (!(eci_mask & 1)) { \ @@ -XXX,XX +XXX,XX @@ DO_VSTR(vstrh_w, MO_UW, 2, stw, 4, int32_t) unsigned e; \ uint32_t addr; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TE | MFLAG | MO_ALIGN, \ + MemOpIdx oi = make_memop_idx(mo_endian(env) | MFLAG | MO_ALIGN, \ mmu_idx); \ for (e = 0; e < 16 / ESIZE; e++, mask >>= ESIZE, eci_mask >>= ESIZE) { \ if (!(eci_mask & 1)) { \ @@ -XXX,XX +XXX,XX @@ DO_VSTR(vstrh_w, MO_UW, 2, stw, 4, int32_t) unsigned e; \ uint32_t addr; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ + MemOpIdx oi = make_memop_idx(mo_endian(env) | MO_UL | MO_ALIGN, \ + mmu_idx); \ for (e = 0; e < 16 / 4; e++, mask >>= 4, eci_mask >>= 4) { \ if (!(eci_mask & 1)) { \ continue; \ @@ -XXX,XX +XXX,XX @@ DO_VSTR64_SG(vstrd_sg_wb_ud, ADDR_ADD, true) static const uint8_t off[4] = { O1, O2, O3, O4 }; \ uint32_t addr, data; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ + MemOpIdx oi = make_memop_idx(mo_endian(env) | MO_UL | MO_ALIGN, \ + mmu_idx); \ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -XXX,XX +XXX,XX @@ DO_VSTR64_SG(vstrd_sg_wb_ud, ADDR_ADD, true) int y; /* y counts 0 2 0 2 */ \ uint16_t *qd; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ + MemOpIdx oi = make_memop_idx(mo_endian(env) | MO_UL | MO_ALIGN, \ + mmu_idx); \ for (beat = 0, y = 0; beat < 4; beat++, mask >>= 4, y ^= 2) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -XXX,XX +XXX,XX @@ DO_VSTR64_SG(vstrd_sg_wb_ud, ADDR_ADD, true) uint32_t *qd; \ int y; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ + MemOpIdx oi = make_memop_idx(mo_endian(env) | MO_UL | MO_ALIGN, \ + mmu_idx); \ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -XXX,XX +XXX,XX @@ DO_VLD4W(vld43w, 6, 7, 8, 9) uint32_t addr, data; \ uint8_t *qd; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ + MemOpIdx oi = make_memop_idx(mo_endian(env) | MO_UL | MO_ALIGN, \ + mmu_idx); \ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -XXX,XX +XXX,XX @@ DO_VLD4W(vld43w, 6, 7, 8, 9) int e; \ uint16_t *qd; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ + MemOpIdx oi = make_memop_idx(mo_endian(env) | MO_UL | MO_ALIGN, \ + mmu_idx); \ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -XXX,XX +XXX,XX @@ DO_VLD4W(vld43w, 6, 7, 8, 9) uint32_t addr, data; \ uint32_t *qd; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ + MemOpIdx oi = make_memop_idx(mo_endian(env) | MO_UL | MO_ALIGN, \ + mmu_idx); \ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -XXX,XX +XXX,XX @@ DO_VLD2W(vld21w, 8, 12, 16, 20) static const uint8_t off[4] = { O1, O2, O3, O4 }; \ uint32_t addr, data; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ + MemOpIdx oi = make_memop_idx(mo_endian(env) | MO_UL | MO_ALIGN, \ + mmu_idx); \ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -XXX,XX +XXX,XX @@ DO_VLD2W(vld21w, 8, 12, 16, 20) int y; /* y counts 0 2 0 2 */ \ uint16_t *qd; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ + MemOpIdx oi = make_memop_idx(mo_endian(env) | MO_UL | MO_ALIGN, \ + mmu_idx); \ for (beat = 0, y = 0; beat < 4; beat++, mask >>= 4, y ^= 2) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -XXX,XX +XXX,XX @@ DO_VLD2W(vld21w, 8, 12, 16, 20) uint32_t *qd; \ int y; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ + MemOpIdx oi = make_memop_idx(mo_endian(env) | MO_UL | MO_ALIGN, \ + mmu_idx); \ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -XXX,XX +XXX,XX @@ DO_VST4W(vst43w, 6, 7, 8, 9) uint32_t addr, data; \ uint8_t *qd; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ + MemOpIdx oi = make_memop_idx(mo_endian(env) | MO_UL | MO_ALIGN, \ + mmu_idx); \ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -XXX,XX +XXX,XX @@ DO_VST4W(vst43w, 6, 7, 8, 9) int e; \ uint16_t *qd; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ + MemOpIdx oi = make_memop_idx(mo_endian(env) | MO_UL | MO_ALIGN, \ + mmu_idx); \ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -XXX,XX +XXX,XX @@ DO_VST4W(vst43w, 6, 7, 8, 9) uint32_t addr, data; \ uint32_t *qd; \ int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ - MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, mmu_idx);\ + MemOpIdx oi = make_memop_idx(mo_endian(env) | MO_UL | MO_ALIGN, \ + mmu_idx); \ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ -- 2.43.0
From: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20260414005348.4767-8-philmd@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/meson.build | 3 ++- target/arm/tcg/mve_helper.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/target/arm/tcg/meson.build b/target/arm/tcg/meson.build index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/meson.build +++ b/target/arm/tcg/meson.build @@ -XXX,XX +XXX,XX @@ arm_ss.add(when: 'TARGET_AARCH64', if_false: files('stubs32.c')) arm_ss.add(files( 'cpu32.c', 'm_helper.c', - 'mve_helper.c', )) arm_ss.add(when: 'TARGET_AARCH64', if_true: files( @@ -XXX,XX +XXX,XX @@ arm_common_system_ss.add( 'debug.c', 'hflags.c', 'gengvec.c', + 'mve_helper.c', 'neon_helper.c', 'op_helper.c', 'psci.c', @@ -XXX,XX +XXX,XX @@ arm_user_ss.add( 'debug.c', 'gengvec.c', 'hflags.c', + 'mve_helper.c', 'neon_helper.c', 'op_helper.c', 'tlb_helper.c', diff --git a/target/arm/tcg/mve_helper.c b/target/arm/tcg/mve_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/mve_helper.c +++ b/target/arm/tcg/mve_helper.c @@ -XXX,XX +XXX,XX @@ #include "helper-mve.h" #include "internals.h" #include "vec_internal.h" -#include "accel/tcg/cpu-ldst.h" +#include "accel/tcg/cpu-ldst-common.h" #include "tcg/tcg.h" #include "fpu/softfloat.h" #include "crypto/clmul.h" -- 2.43.0
From: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20260414005348.4767-9-philmd@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/m_helper.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/arm/tcg/m_helper.c b/target/arm/tcg/m_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/m_helper.c +++ b/target/arm/tcg/m_helper.c @@ -XXX,XX +XXX,XX @@ void HELPER(v7m_blxns)(CPUARMState *env, uint32_t dest) /* Note that these stores can throw exceptions on MPU faults */ ARMMMUIdx mmu_idx = arm_mmu_idx(env); - MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, + MemOpIdx oi = make_memop_idx(mo_endian(env) | MO_UL | MO_ALIGN, arm_to_core_mmu_idx(mmu_idx)); cpu_stl_mmu(env, sp, nextinst, oi, GETPC()); cpu_stl_mmu(env, sp + 4, saved_psr, oi, GETPC()); @@ -XXX,XX +XXX,XX @@ void HELPER(v7m_vlstm)(CPUARMState *env, uint32_t fptr) bool lspact = env->v7m.fpccr[s] & R_V7M_FPCCR_LSPACT_MASK; uintptr_t ra = GETPC(); ARMMMUIdx mmu_idx = arm_mmu_idx(env); - MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, + MemOpIdx oi = make_memop_idx(mo_endian(env) | MO_UL | MO_ALIGN, arm_to_core_mmu_idx(mmu_idx)); assert(env->v7m.secure); @@ -XXX,XX +XXX,XX @@ void HELPER(v7m_vlldm)(CPUARMState *env, uint32_t fptr) ARMCPU *cpu = env_archcpu(env); uintptr_t ra = GETPC(); ARMMMUIdx mmu_idx = arm_mmu_idx(env); - MemOpIdx oi = make_memop_idx(MO_TE | MO_UL | MO_ALIGN, + MemOpIdx oi = make_memop_idx(mo_endian(env) | MO_UL | MO_ALIGN, arm_to_core_mmu_idx(mmu_idx)); /* fptr is the value of Rn, the frame pointer we load the FP regs from */ -- 2.43.0
From: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20260414005348.4767-10-philmd@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/m_helper.c | 2 +- target/arm/tcg/meson.build | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/target/arm/tcg/m_helper.c b/target/arm/tcg/m_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/m_helper.c +++ b/target/arm/tcg/m_helper.c @@ -XXX,XX +XXX,XX @@ #include "qemu/log.h" #include "exec/page-protection.h" #ifdef CONFIG_TCG -#include "accel/tcg/cpu-ldst.h" +#include "accel/tcg/cpu-ldst-common.h" #include "semihosting/common-semi.h" #endif #if !defined(CONFIG_USER_ONLY) diff --git a/target/arm/tcg/meson.build b/target/arm/tcg/meson.build index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/meson.build +++ b/target/arm/tcg/meson.build @@ -XXX,XX +XXX,XX @@ arm_ss.add(when: 'TARGET_AARCH64', if_false: files('stubs32.c')) arm_ss.add(files( 'cpu32.c', - 'm_helper.c', )) arm_ss.add(when: 'TARGET_AARCH64', if_true: files( @@ -XXX,XX +XXX,XX @@ arm_common_system_ss.add( 'debug.c', 'hflags.c', 'gengvec.c', + 'm_helper.c', 'mve_helper.c', 'neon_helper.c', 'op_helper.c', @@ -XXX,XX +XXX,XX @@ arm_user_ss.add( 'debug.c', 'gengvec.c', 'hflags.c', + 'm_helper.c', 'mve_helper.c', 'neon_helper.c', 'op_helper.c', -- 2.43.0
From: Thomas Huth <thuth@redhat.com> When writing to this register, QEMU currently aborts: $ echo "readl 0x10000018" | ./qemu-system-arm -audiodev none,id=snd0 \ -M integratorcp,accel=qtest,audiodev=snd0 -display none -qtest stdio [I 0.000000] OPENED [R +0.001907] readl 0x10000018 qemu: hardware error: integratorcm_read: CM_LMBUSCNT [...] Aborted (core dumped) This is bad, a guest should ideally never be able to kill QEMU like this. Now, according to the "Intergrator/CP User Guide" from: https://developer.arm.com/documentation/dui0159/b/porting-integrator-ap-and-im-pd1/registers "The Integrator/AP CM_LMBUSCNT has been removed." That means this register does not seem to be implemented on real CP boards at all, only for older AP boards. Thus it should be fine if we simply ignore this register in QEMU and handle it like all other unimplemented registers in the "default" handler of the case statement. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3407 Signed-off-by: Thomas Huth <thuth@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20260420064933.64765-1-thuth@redhat.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/integratorcp.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/hw/arm/integratorcp.c b/hw/arm/integratorcp.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/integratorcp.c +++ b/hw/arm/integratorcp.c @@ -XXX,XX +XXX,XX @@ static uint64_t integratorcm_read(void *opaque, hwaddr offset, } else { return s->cm_lock; } - case 6: /* CM_LMBUSCNT */ - /* ??? High frequency timer. */ - hw_error("integratorcm_read: CM_LMBUSCNT"); case 7: /* CM_AUXOSC */ return s->cm_auxosc; case 8: /* CM_SDRAM */ -- 2.43.0
include/hw/arm/primecell.h used to be more expansive, but now the only thing it defines is the ARM_SYSCTL_GPIO_* constants for the GPIO lines for the arm-sysctl system-control device used on the Realview, Versatile and Versatile Express boards. Replace it with a header file specific to that device. virt.c and vmapple.c included primecell.h despite not using the constants it defined; there we can simply drop the include entirely. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20260416172627.690396-1-peter.maydell@linaro.org --- MAINTAINERS | 2 +- hw/arm/realview.c | 2 +- hw/arm/vexpress.c | 2 +- hw/arm/virt.c | 1 - hw/misc/arm_sysctl.c | 2 +- hw/vmapple/vmapple.c | 1 - include/hw/arm/primecell.h | 12 ------------ include/hw/misc/arm_sysctl.h | 16 ++++++++++++++++ 8 files changed, 20 insertions(+), 18 deletions(-) delete mode 100644 include/hw/arm/primecell.h create mode 100644 include/hw/misc/arm_sysctl.h diff --git a/MAINTAINERS b/MAINTAINERS index XXXXXXX..XXXXXXX 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -XXX,XX +XXX,XX @@ F: hw/ssi/pl022.c F: include/hw/ssi/pl022.h F: hw/rtc/pl031.c F: include/hw/rtc/pl031.h -F: include/hw/arm/primecell.h F: hw/timer/cmsdk-apb-timer.c F: include/hw/timer/cmsdk-apb-timer.h F: tests/qtest/cmsdk-apb-timer-test.c @@ -XXX,XX +XXX,XX @@ F: hw/*/versatile* F: hw/i2c/arm_sbcon_i2c.c F: include/hw/i2c/arm_sbcon_i2c.h F: hw/misc/arm_sysctl.c +F: include/hw/misc/arm_sysctl.h F: docs/system/arm/versatile.rst Virt diff --git a/hw/arm/realview.c b/hw/arm/realview.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/realview.c +++ b/hw/arm/realview.c @@ -XXX,XX +XXX,XX @@ #include "target/arm/cpu.h" #include "hw/core/sysbus.h" #include "hw/arm/boot.h" -#include "hw/arm/primecell.h" #include "hw/arm/machines-qom.h" #include "hw/core/split-irq.h" +#include "hw/misc/arm_sysctl.h" #include "hw/net/lan9118.h" #include "hw/net/smc91c111.h" #include "hw/pci/pci.h" diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/vexpress.c +++ b/hw/arm/vexpress.c @@ -XXX,XX +XXX,XX @@ #include "qemu/datadir.h" #include "hw/core/sysbus.h" #include "hw/arm/boot.h" -#include "hw/arm/primecell.h" #include "hw/arm/machines-qom.h" +#include "hw/misc/arm_sysctl.h" #include "hw/net/lan9118.h" #include "hw/i2c/i2c.h" #include "net/net.h" diff --git a/hw/arm/virt.c b/hw/arm/virt.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -XXX,XX +XXX,XX @@ #include "monitor/qdev.h" #include "hw/core/sysbus.h" #include "hw/arm/boot.h" -#include "hw/arm/primecell.h" #include "hw/arm/virt.h" #include "hw/arm/machines-qom.h" #include "hw/block/flash.h" diff --git a/hw/misc/arm_sysctl.c b/hw/misc/arm_sysctl.c index XXXXXXX..XXXXXXX 100644 --- a/hw/misc/arm_sysctl.c +++ b/hw/misc/arm_sysctl.c @@ -XXX,XX +XXX,XX @@ #include "system/runstate.h" #include "qemu/bitops.h" #include "hw/core/sysbus.h" +#include "hw/misc/arm_sysctl.h" #include "migration/vmstate.h" -#include "hw/arm/primecell.h" #include "qemu/log.h" #include "qemu/module.h" #include "qom/object.h" diff --git a/hw/vmapple/vmapple.c b/hw/vmapple/vmapple.c index XXXXXXX..XXXXXXX 100644 --- a/hw/vmapple/vmapple.c +++ b/hw/vmapple/vmapple.c @@ -XXX,XX +XXX,XX @@ #include "hw/core/sysbus.h" #include "hw/usb/usb.h" #include "hw/arm/boot.h" -#include "hw/arm/primecell.h" #include "hw/char/pl011.h" #include "hw/intc/arm_gic.h" #include "hw/intc/arm_gicv3_common.h" diff --git a/include/hw/arm/primecell.h b/include/hw/arm/primecell.h deleted file mode 100644 index XXXXXXX..XXXXXXX --- a/include/hw/arm/primecell.h +++ /dev/null @@ -XXX,XX +XXX,XX @@ -#ifndef PRIMECELL_H -#define PRIMECELL_H - -/* Declarations for ARM PrimeCell based periperals. */ -/* Also includes some devices that are currently only used by the - ARM boards. */ - -/* arm_sysctl GPIO lines */ -#define ARM_SYSCTL_GPIO_MMC_WPROT 0 -#define ARM_SYSCTL_GPIO_MMC_CARDIN 1 - -#endif diff --git a/include/hw/misc/arm_sysctl.h b/include/hw/misc/arm_sysctl.h new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/include/hw/misc/arm_sysctl.h @@ -XXX,XX +XXX,XX @@ +/* + * Status and system control registers for ARM RealView/Versatile boards. + * + * Copyright (c) Linaro Ltd + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_MISC_ARM_SYSCTL_H +#define HW_MISC_ARM_SYSCTL_H + +/* arm_sysctl inbound GPIO lines */ +#define ARM_SYSCTL_GPIO_MMC_WPROT 0 +#define ARM_SYSCTL_GPIO_MMC_CARDIN 1 + +#endif -- 2.43.0
If we create a normally-AArch64 CPU and configure it with aarch64=off, this will by default leave all the AArch64 ID register values in its ARMISARegisters struct untouched. That in turn means that tests of cpu_isar_feature(aa64_something, cpu) will return true. Until now we have had a design policy that you shouldn't check an aa64_ feature unless you know that the CPU has AArch64; but this is quite fragile as it's easy to forget and only causes a problem in the corner case where AArch64 was turned off. In particular, when we extend the ability to disable AArch64 from only KVM to also TCG there are many more aa64 feature check points which we would otherwise have to audit for whether they needed to be guarded with a check on ARM_FEATURE_AARCH64. Instead, make the CPU realize function zero out all the 64-bit ID registers if a TCG CPU doesn't have AArch64; this will make aa64_ feature tests generally return false. We only do this for TCG because only TCG really needs it, and for KVM it might be confusing to have QEMU's idea of the ID registers be different from KVM's. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20260416165353.589569-2-peter.maydell@linaro.org --- target/arm/cpu.c | 35 +++++++++++++++++++++++++++++++++++ target/arm/cpu.h | 3 ++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -XXX,XX +XXX,XX @@ void arm_cpu_finalize_features(ARMCPU *cpu, Error **errp) } } +static void arm_clear_aarch64_idregs(ARMCPU *cpu) +{ + /* Zero out all the AArch64 ID registers in ARMISARegisters */ + SET_IDREG(&cpu->isar, ID_AA64ISAR0, 0); + SET_IDREG(&cpu->isar, ID_AA64ISAR1, 0); + SET_IDREG(&cpu->isar, ID_AA64ISAR2, 0); + SET_IDREG(&cpu->isar, ID_AA64PFR0, 0); + SET_IDREG(&cpu->isar, ID_AA64PFR1, 0); + SET_IDREG(&cpu->isar, ID_AA64PFR2, 0); + SET_IDREG(&cpu->isar, ID_AA64MMFR0, 0); + SET_IDREG(&cpu->isar, ID_AA64MMFR1, 0); + SET_IDREG(&cpu->isar, ID_AA64MMFR2, 0); + SET_IDREG(&cpu->isar, ID_AA64MMFR3, 0); + SET_IDREG(&cpu->isar, ID_AA64DFR0, 0); + SET_IDREG(&cpu->isar, ID_AA64DFR1, 0); + SET_IDREG(&cpu->isar, ID_AA64AFR0, 0); + SET_IDREG(&cpu->isar, ID_AA64AFR1, 0); + SET_IDREG(&cpu->isar, ID_AA64ZFR0, 0); + SET_IDREG(&cpu->isar, ID_AA64SMFR0, 0); +} + static void arm_cpu_realizefn(DeviceState *dev, Error **errp) { CPUState *cs = CPU(dev); @@ -XXX,XX +XXX,XX @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) } #endif + /* + * A TCG aarch64=off CPU has no AArch64 at all, so we clear out the + * ID registers to avoid cpu_isar_feature(aa64_something, cpu) tests + * incorrectly returning true. We don't do this for other accelerators + * (which in practice means "for KVM", since no others have AArch32 + * guest support) because from KVM's point of view the AArch64 ID + * registers still exist and must have their correct values. So we + * avoid clearing them out so that we don't have QEMU and KVM with + * different ideas of the ID registers. + */ + if (tcg_enabled() && !arm_feature(env, ARM_FEATURE_AARCH64)) { + arm_clear_aarch64_idregs(cpu); + } + #ifdef CONFIG_USER_ONLY /* * User mode relies on IC IVAU instructions to catch modification of diff --git a/target/arm/cpu.h b/target/arm/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -XXX,XX +XXX,XX @@ struct ArchCPU { * Note that if you add an ID register to the ARMISARegisters struct * you need to also update the 32-bit and 64-bit versions of the * kvm_arm_get_host_cpu_features() function to correctly populate the - * field by reading the value from the KVM vCPU. + * field by reading the value from the KVM vCPU. If it is an AArch64 + * ID register then you also must update arm_clear_aarch64_idregs(). */ struct ARMISARegisters { uint32_t mvfr0; -- 2.43.0
Allow the 'aarch64=off' property, which is currently KVM-only, to be set for TCG CPUs also. Note that we don't permit it on the qemu-aarch64 user-mode binary: this makes no sense as that executable can only handle AArch64 syscalls (and it would also assert at startup since it doesn't compile in the A32-specific GDB xml files like arm-neon.xml). Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Clément Chigot <chigot@adacore.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20260416165353.589569-3-peter.maydell@linaro.org --- docs/system/arm/cpu-features.rst | 10 +++++---- target/arm/cpu-features.h | 5 +++++ target/arm/cpu.c | 36 ++++++++++++++++++++++++++++---- tests/qtest/arm-cpu-features.c | 8 ++----- 4 files changed, 45 insertions(+), 14 deletions(-) diff --git a/docs/system/arm/cpu-features.rst b/docs/system/arm/cpu-features.rst index XXXXXXX..XXXXXXX 100644 --- a/docs/system/arm/cpu-features.rst +++ b/docs/system/arm/cpu-features.rst @@ -XXX,XX +XXX,XX @@ not implement ARMv8-A, will not have the ``aarch64`` CPU property. QEMU's support may be limited for some CPU features, only partially supporting the feature or only supporting the feature under certain configurations. For example, the ``aarch64`` CPU feature, which, when -disabled, enables the optional AArch32 CPU feature, is only supported -when using the KVM accelerator and when running on a host CPU type that -supports the feature. While ``aarch64`` currently only works with KVM, -it could work with TCG. CPU features that are specific to KVM are +disabled, enables the optional AArch32 CPU feature, can only be set to +``off`` on the TCG and KVM accelerators, and it cannot be set to +``off`` under KVM unless running on a host CPU type that supports +running guests in AArch32. + +CPU features that are inherently specific to KVM are prefixed with "kvm-" and are described in "KVM VCPU Features". CPU Feature Probing diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -XXX,XX +XXX,XX @@ static inline bool isar_feature_aa64_aa32_el2(const ARMISARegisters *id) return FIELD_EX64_IDREG(id, ID_AA64PFR0, EL2) >= 2; } +static inline bool isar_feature_aa64_aa32_el3(const ARMISARegisters *id) +{ + return FIELD_EX64_IDREG(id, ID_AA64PFR0, EL3) >= 2; +} + static inline bool isar_feature_aa64_ras(const ARMISARegisters *id) { return FIELD_EX64_IDREG(id, ID_AA64PFR0, RAS) != 0; diff --git a/target/arm/cpu.c b/target/arm/cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -XXX,XX +XXX,XX @@ static void aarch64_cpu_set_aarch64(Object *obj, bool value, Error **errp) * uniform execution state like do_interrupt. */ if (value == false) { - if (!kvm_enabled() || !kvm_arm_aarch32_supported()) { - error_setg(errp, "'aarch64' feature cannot be disabled " - "unless KVM is enabled and 32-bit EL1 " - "is supported"); + if (kvm_enabled()) { + if (!kvm_arm_aarch32_supported()) { + error_setg(errp, "'aarch64' feature cannot be disabled for KVM " + "because this host does not support 32-bit EL1"); + return; + } + } else if (tcg_enabled()) { +#ifdef CONFIG_USER_ONLY + error_setg(errp, "'aarch64' feature cannot be disabled for " + "usermode emulator qemu-aarch64; use qemu-arm instead"); + return; +#else + bool aa32_at_highest_el; + if (arm_feature(&cpu->env, ARM_FEATURE_EL3)) { + aa32_at_highest_el = cpu_isar_feature(aa64_aa32_el3, cpu); + } else if (arm_feature(&cpu->env, ARM_FEATURE_EL2)) { + aa32_at_highest_el = cpu_isar_feature(aa64_aa32_el2, cpu); + } else { + aa32_at_highest_el = cpu_isar_feature(aa64_aa32_el1, cpu); + } + + if (!aa32_at_highest_el) { + error_setg(errp, "'aarch64' feature cannot be disabled for " + "this TCG CPU because it does not support 32-bit " + "execution at its highest implemented exception " + "level"); + return; + } +#endif + } else { + error_setg(errp, "'aarch64' feature cannot be disabled for " + "this accelerator"); return; } unset_feature(&cpu->env, ARM_FEATURE_AARCH64); diff --git a/tests/qtest/arm-cpu-features.c b/tests/qtest/arm-cpu-features.c index XXXXXXX..XXXXXXX 100644 --- a/tests/qtest/arm-cpu-features.c +++ b/tests/qtest/arm-cpu-features.c @@ -XXX,XX +XXX,XX @@ static void test_query_cpu_model_expansion(const void *data) sve_tests_default(qts, "max"); pauth_tests_default(qts, "max"); - /* Test that features that depend on KVM generate errors without. */ - assert_error(qts, "max", - "'aarch64' feature cannot be disabled " - "unless KVM is enabled and 32-bit EL1 " - "is supported", - "{ 'aarch64': false }"); + /* TCG allows us to turn off AArch64 on the 'max' CPU type */ + assert_set_feature(qts, "max", "aarch64", false); } qtest_quit(qts); -- 2.43.0
Add a basic test of the TCG 'aarch64=off' functionality; this is the same as our existing arm/test_virt test, but it runs the AArch32 guest kernel on qemu-system-aarch64 with -cpu max,aarch64=off. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20260416165353.589569-4-peter.maydell@linaro.org --- tests/functional/aarch64/meson.build | 1 + .../aarch64/test_virt_aarch64_off.py | 37 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100755 tests/functional/aarch64/test_virt_aarch64_off.py diff --git a/tests/functional/aarch64/meson.build b/tests/functional/aarch64/meson.build index XXXXXXX..XXXXXXX 100644 --- a/tests/functional/aarch64/meson.build +++ b/tests/functional/aarch64/meson.build @@ -XXX,XX +XXX,XX @@ tests_aarch64_system_thorough = [ 'tcg_plugins', 'tuxrun', 'virt', + 'virt_aarch64_off', 'virt_gpu', 'virt_vbsa', 'xen', diff --git a/tests/functional/aarch64/test_virt_aarch64_off.py b/tests/functional/aarch64/test_virt_aarch64_off.py new file mode 100755 index XXXXXXX..XXXXXXX --- /dev/null +++ b/tests/functional/aarch64/test_virt_aarch64_off.py @@ -XXX,XX +XXX,XX @@ +#!/usr/bin/env python3 +# +# Functional test that boots an AArch32 Linux kernel and checks the console +# on a TCG aarch64 CPU with aarch64=off. This is the same image etc +# as we use in tests/functional/arm/test_virt.py. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset + +class ArmVirtMachine(LinuxKernelTest): + + ASSET_KERNEL = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/' + 'releases/29/Everything/armhfp/os/images/pxeboot/vmlinuz'), + '18dd5f1a9a28bd539f9d047f7c0677211bae528e8712b40ca5a229a4ad8e2591') + + def test_arm_virt(self): + self.set_machine('virt') + # KVM aarch64=off requires a host CPU that supports it, so + # restrict the test to TCG only + self.require_accelerator('tcg') + self.vm.add_args('-cpu', 'max,aarch64=off') + + kernel_path = self.ASSET_KERNEL.fetch() + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyAMA0') + self.vm.add_args('-kernel', kernel_path, + '-append', kernel_command_line) + self.vm.launch() + console_pattern = 'Kernel command line: %s' % kernel_command_line + self.wait_for_console_pattern(console_pattern) + +if __name__ == '__main__': + LinuxKernelTest.main() -- 2.43.0
From: Eric Auger <eric.auger@redhat.com> We introduce a datatype for a tolerance with respect to a given cpreg migration issue. The tolerance applies to a given cpreg kvm index, and can be of different types: a) mismatch in cpreg indexes - ToleranceNotOnBothEnds (cpreg index is allowed to be only present on one end) - ToleranceOnlySrcTestValue (cpreg index is allowed to be only present in source if its value @mask field matches @value) b) mismatch in cpreg values - ToleranceDiffInMask (value differences are allowed only within a mask) - ToleranceFieldLT (incoming field value must be less than a given value) - ToleranceFieldGT (incoming field value must be greater than a given value) A QLIST of such tolerances can be populated using a new helper: arm_register_cpreg_mig_tolerance() and arm_cpu_match_cpreg_mig_tolerance() allows to check whether a tolerance exists for a given kvm index and its criterion is matched. callers for those helpers will be introduced in subsequent patches. Only registration of migration tolerances related to cpreg index mismatch is currently allowed. Signed-off-by: Eric Auger <eric.auger@redhat.com> Message-id: 20260420140552.104369-2-eric.auger@redhat.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/cpu.c | 82 ++++++++++++++++++++++++++++++++++++++++++ target/arm/cpu.h | 1 + target/arm/internals.h | 54 ++++++++++++++++++++++++++++ 3 files changed, 137 insertions(+) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -XXX,XX +XXX,XX @@ void arm_register_el_change_hook(ARMCPU *cpu, ARMELChangeHookFn *hook, QLIST_INSERT_HEAD(&cpu->el_change_hooks, entry, node); } +static ARMCPRegMigTolerance *find_mig_tolerance(ARMCPU *cpu, uint64_t kvmidx) +{ + ARMCPRegMigTolerance *t; + QLIST_FOREACH(t, &cpu->cpreg_mig_tolerances, node) { + if (t->kvmidx == kvmidx) { + return t; + } + } + return NULL; +} + +void arm_register_cpreg_mig_tolerance(ARMCPU *cpu, uint64_t kvmidx, + uint64_t mask, uint64_t value, + ARMCPRegMigToleranceType type) +{ + ARMCPRegMigTolerance *entry; + + /* make sure the kvmidx has not tolerance already registered */ + assert(!find_mig_tolerance(cpu, kvmidx)); + + assert(type == ToleranceNotOnBothEnds || + type == ToleranceOnlySrcTestValue); + + entry = g_new0(ARMCPRegMigTolerance, 1); + + entry->kvmidx = kvmidx; + entry->mask = mask; + entry->value = value; + entry->type = type; + + QLIST_INSERT_HEAD(&cpu->cpreg_mig_tolerances, entry, node); +} + +bool arm_cpu_match_cpreg_mig_tolerance(ARMCPU *cpu, uint64_t kvmidx, + uint64_t vmstate_value, uint64_t local_value, + ARMCPRegMigToleranceType type) +{ + ARMCPRegMigTolerance *t = find_mig_tolerance(cpu, kvmidx); + uint64_t diff, diff_outside_mask, field; + + if (!t || t->type != type) { + return false; + } + + if (type == ToleranceNotOnBothEnds) { + return true; + } + + if (type == ToleranceOnlySrcTestValue && + ((vmstate_value & t->mask) == t->value)) { + return true; + } + + /* Need to check the mask */ + diff = vmstate_value ^ local_value; + diff_outside_mask = diff & ~t->mask; + + if (diff_outside_mask) { + /* there are differences outside of the mask */ + return false; + } + if (type == ToleranceDiffInMask) { + /* differences only in the field, tolerance matched */ + return true; + } + /* need to compare field value against authorized ones */ + field = vmstate_value & t->mask; + if (type == ToleranceFieldLT && (field < t->value)) { + return true; + } + if (type == ToleranceFieldGT && (field > t->value)) { + return true; + } + return false; +} + static void cp_reg_reset(gpointer key, gpointer value, gpointer opaque) { /* Reset a single ARMCPRegInfo register */ @@ -XXX,XX +XXX,XX @@ static void arm_cpu_initfn(Object *obj) QLIST_INIT(&cpu->pre_el_change_hooks); QLIST_INIT(&cpu->el_change_hooks); + QLIST_INIT(&cpu->cpreg_mig_tolerances); #ifdef CONFIG_USER_ONLY # ifdef TARGET_AARCH64 @@ -XXX,XX +XXX,XX @@ static void arm_cpu_finalizefn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); ARMELChangeHook *hook, *next; + ARMCPRegMigTolerance *t, *n; g_hash_table_destroy(cpu->cp_regs); @@ -XXX,XX +XXX,XX @@ static void arm_cpu_finalizefn(Object *obj) QLIST_REMOVE(hook, node); g_free(hook); } + QLIST_FOREACH_SAFE(t, &cpu->cpreg_mig_tolerances, node, n) { + QLIST_REMOVE(t, node); + g_free(t); + } #ifndef CONFIG_USER_ONLY if (cpu->pmu_timer) { timer_free(cpu->pmu_timer); diff --git a/target/arm/cpu.h b/target/arm/cpu.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -XXX,XX +XXX,XX @@ struct ArchCPU { QLIST_HEAD(, ARMELChangeHook) pre_el_change_hooks; QLIST_HEAD(, ARMELChangeHook) el_change_hooks; + QLIST_HEAD(, ARMCPRegMigTolerance) cpreg_mig_tolerances; int32_t node_id; /* NUMA node this CPU belongs to */ diff --git a/target/arm/internals.h b/target/arm/internals.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -XXX,XX +XXX,XX @@ int compare_u64(const void *a, const void *b); /* Used in FEAT_MEC to set the MECIDWidthm1 field in the MECIDR_EL2 register. */ #define MECID_WIDTH 16 +typedef enum { + ToleranceNotOnBothEnds, + ToleranceOnlySrcTestValue, + ToleranceDiffInMask, + ToleranceFieldLT, + ToleranceFieldGT, +} ARMCPRegMigToleranceType; + +typedef struct ARMCPRegMigTolerance { + uint64_t kvmidx; + uint64_t mask; + uint64_t value; + ARMCPRegMigToleranceType type; + QLIST_ENTRY(ARMCPRegMigTolerance) node; +} ARMCPRegMigTolerance; + +/** + * arm_register_cpreg_mig_tolerance: + * Register a migration tolerance wrt one given cpreg identified by its + * @kvmidx. Calling this function twice for the same @kvmidx is a + * programming error and will cause an assertion failure. + * + * @cpu: vcpu to apply the migration tolerance on + * @kvmidx: kvm index of the cpreg the tolerance applies to + * @mask: bitmask where a difference is tolerated + * (relevant with ToleranceDiffInMask) + * @value: value the bitmask field is compared with + * (relevant with ToleranceFieldLT and ToleranceFieldGT) + * @type: type of the migration tolerance: + * - ToleranceNotOnBothEnds (cpreg index is allowed to be only present + * on one end) + * - ToleranceOnlySrcTestValue (cpreg index is allowed to be only + * present in source if its value @mask field matches @value) + * - ToleranceDiffInMask (mismatch in cpreg values are only tolerated + * if differences are within @mask) + * - ToleranceFieldLT (mismatch in cpreg values are only tolerated + * if incoming @bitmask field value is less than @value) + * - ToleranceFieldGT (mismatch in cpreg values are only tolerated + * if incoming @bitmask field value is greater than @value) + */ +void arm_register_cpreg_mig_tolerance(ARMCPU *cpu, uint64_t kvmidx, + uint64_t mask, uint64_t value, + ARMCPRegMigToleranceType type); + +/** + * arm_cpu_match_cpreg_mig_tolerance: + * Check whether a tolerance of type @type exists for a given @kvmidx + * and the tolerance criterion is satisfied + */ +bool arm_cpu_match_cpreg_mig_tolerance(ARMCPU *cpu, uint64_t kvmidx, + uint64_t vmstate_value, uint64_t local_value, + ARMCPRegMigToleranceType type); + + #endif -- 2.43.0
From: Eric Auger <eric.auger@redhat.com> If there is a mismatch between the cpreg indexes found on both ends, check whether a tolerance was registered for the given kvmidx. If any, silence warning/errors. Signed-off-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260420140552.104369-3-eric.auger@redhat.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/machine.c | 21 +++++++++++++++------ target/arm/trace-events | 2 ++ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/target/arm/machine.c b/target/arm/machine.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/machine.c +++ b/target/arm/machine.c @@ -XXX,XX +XXX,XX @@ static void handle_cpreg_missing_in_incoming_stream(ARMCPU *cpu, uint64_t kvmidx { g_autofree gchar *name = print_register_name(kvmidx); + if (arm_cpu_match_cpreg_mig_tolerance(cpu, kvmidx, + 0, 0, ToleranceNotOnBothEnds)) { + trace_tolerate_cpreg_missing_in_incoming_stream(name); + return; + } warn_report("%s: %s " "expected by the destination but not in the incoming stream: " "skip it", __func__, name); } /* - * Handle the situation where @kvmidx is in the incoming stream - * but not on destination. This currently fails the migration but - * we plan to accomodate some exceptions, hence the boolean returned value. + * Handle the situation where @kvmidx is in the incoming + * stream but not on destination. This fails the migration if + * no cpreg mig tolerance is matched for this @kvmidx + * Return true if the migration should eventually fail */ static bool handle_cpreg_only_in_incoming_stream(ARMCPU *cpu, uint64_t kvmidx) { g_autofree gchar *name = print_register_name(kvmidx); - bool fail = true; + if (arm_cpu_match_cpreg_mig_tolerance(cpu, kvmidx, + 0, 0, ToleranceNotOnBothEnds)) { + trace_tolerate_cpreg_only_in_incoming_stream(name); + return false; + } error_report("%s: %s in the incoming stream but unknown on the " "destination: fail migration", __func__, name); - - return fail; + return true; } static int cpu_post_load(void *opaque, int version_id) diff --git a/target/arm/trace-events b/target/arm/trace-events index XXXXXXX..XXXXXXX 100644 --- a/target/arm/trace-events +++ b/target/arm/trace-events @@ -XXX,XX +XXX,XX @@ arm_psci_call(uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3, uint32_t cpuid # machine.c cpu_post_load(uint32_t cpreg_vmstate_array_len, uint32_t cpreg_array_len) "cpreg_vmstate_array_len=%d cpreg_array_len=%d" +tolerate_cpreg_missing_in_incoming_stream(char *name) "%s is missing in incoming stream but this is explicitly tolerated" +tolerate_cpreg_only_in_incoming_stream(char *name) "%s is in incoming stream but not on destination but this is explicitly tolerated" -- 2.43.0
From: Eric Auger <eric.auger@redhat.com> Pass the value of the incoming register to handle_cpreg_only_in_incoming_stream and check whether there is a matching ToleranceOnlySrcTestValue tolerance. Signed-off-by: Eric Auger <eric.auger@redhat.com> Message-id: 20260420140552.104369-4-eric.auger@redhat.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/machine.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/target/arm/machine.c b/target/arm/machine.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/machine.c +++ b/target/arm/machine.c @@ -XXX,XX +XXX,XX @@ static void handle_cpreg_missing_in_incoming_stream(ARMCPU *cpu, uint64_t kvmidx * no cpreg mig tolerance is matched for this @kvmidx * Return true if the migration should eventually fail */ -static bool handle_cpreg_only_in_incoming_stream(ARMCPU *cpu, uint64_t kvmidx) +static bool +handle_cpreg_only_in_incoming_stream(ARMCPU *cpu, uint64_t kvmidx, uint64_t value) { g_autofree gchar *name = print_register_name(kvmidx); if (arm_cpu_match_cpreg_mig_tolerance(cpu, kvmidx, - 0, 0, ToleranceNotOnBothEnds)) { + 0, 0, ToleranceNotOnBothEnds) || + arm_cpu_match_cpreg_mig_tolerance(cpu, kvmidx, + value, 0, ToleranceOnlySrcTestValue)) { trace_tolerate_cpreg_only_in_incoming_stream(name); return false; } @@ -XXX,XX +XXX,XX @@ static int cpu_post_load(void *opaque, int version_id) } if (cpu->cpreg_vmstate_indexes[v] < cpu->cpreg_indexes[i]) { fail = handle_cpreg_only_in_incoming_stream(cpu, - cpu->cpreg_vmstate_indexes[v++]); + cpu->cpreg_vmstate_indexes[v], + cpu->cpreg_vmstate_values[v]); + v++; continue; } /* matching register, copy the value over */ @@ -XXX,XX +XXX,XX @@ static int cpu_post_load(void *opaque, int version_id) */ for ( ; v < cpu->cpreg_vmstate_array_len; v++) { fail = handle_cpreg_only_in_incoming_stream(cpu, - cpu->cpreg_vmstate_indexes[v]); + cpu->cpreg_vmstate_indexes[v], + cpu->cpreg_vmstate_values[v]); } if (fail) { return -1; -- 2.43.0
From: Eric Auger <eric.auger@redhat.com> Before linux v6.13 those registers were erroneously unconditionally exposed and this was fixed by commits: - 0fcb4eea5345 ("KVM: arm64: Hide TCR2_EL1 from userspace when disabled for guests") - a68cddbe47ef ("KVM: arm64: Hide S1PIE registers from userspace when disabled for guests") in v6.13. This means if we migrate from an old kernel host to a >= 6.13 kernel host, migration currently fails. Declare cpreg migration tolerance for those registers. Signed-off-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Sebastian Ott <sebott@redhat.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260420140552.104369-5-eric.auger@redhat.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/cpu64.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -XXX,XX +XXX,XX @@ static void aarch64_a53_initfn(Object *obj) define_cortex_a72_a57_a53_cp_reginfo(cpu); } +#if defined(CONFIG_KVM) +static void kvm_arm_set_cpreg_mig_tolerances(ARMCPU *cpu) +{ + /* + * Registers that may be in the incoming stream and not exposed + * on the destination + */ + + /* + * TCR_EL1 was erroneously unconditionnally exposed before linux v6.13. + * See commit 0fcb4eea5345 ("KVM: arm64: Hide TCR2_EL1 from userspace + * when disabled for guests") + */ + arm_register_cpreg_mig_tolerance(cpu, ARM64_SYS_REG(3, 0, 2, 0, 3), + 0, 0, ToleranceNotOnBothEnds); + /* + * PIRE0_EL1 and PIR_EL1 were erroneously unconditionnally exposed + * before linux v6.13. See commit a68cddbe47ef ("KVM: arm64: Hide + * S1PIE registers from userspace when disabled for guests") + */ + arm_register_cpreg_mig_tolerance(cpu, ARM64_SYS_REG(3, 0, 10, 2, 2), + 0, 0, ToleranceNotOnBothEnds); + arm_register_cpreg_mig_tolerance(cpu, ARM64_SYS_REG(3, 0, 10, 2, 3), + 0, 0, ToleranceNotOnBothEnds); +} +#endif + static void aarch64_host_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); @@ -XXX,XX +XXX,XX @@ static void aarch64_host_initfn(Object *obj) #endif #if defined(CONFIG_KVM) + kvm_arm_set_cpreg_mig_tolerances(cpu); kvm_arm_set_cpu_features_from_host(cpu); aarch64_add_sve_properties(obj); #elif defined(CONFIG_HVF) -- 2.43.0
From: Eric Auger <eric.auger@redhat.com> KVM_REG_ARM_VENDOR_HYP_BMAP_2 pseudo FW register is exposed from v6.15 onwards. Backward migration from a >= v6.15 to an older kernel would fail without cpreg migration tolerance definition for this register. If the register is present on source but not on destination, its value must be checked to make sure it matches the reset value, ie. 0, meaning no service is exposed to the guest, hence the choice of a ToleranceOnlySrcTestValue migration tolerance. Signed-off-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260420140552.104369-6-eric.auger@redhat.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/cpu64.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -XXX,XX +XXX,XX @@ static void kvm_arm_set_cpreg_mig_tolerances(ARMCPU *cpu) 0, 0, ToleranceNotOnBothEnds); arm_register_cpreg_mig_tolerance(cpu, ARM64_SYS_REG(3, 0, 10, 2, 3), 0, 0, ToleranceNotOnBothEnds); + + /* + * KVM_REG_ARM_VENDOR_HYP_BMAP_2 pseudo FW register is exposed + * from v6.15 onwards. Backward migration from a >= v6.15 to an older + * kernel would fail without cpreg migration tolerance definition. + * If the register is present on source but not on destination, make + * sure it has its reset value, ie. 0, meaning no service is exposed + * to the guest. + */ + arm_register_cpreg_mig_tolerance(cpu, KVM_REG_ARM_FW_FEAT_BMAP_REG(3), + UINT64_MAX, 0, ToleranceOnlySrcTestValue); } #endif -- 2.43.0
From: Eric Auger <eric.auger@redhat.com> We want to remove AArch32 DBGDTRTX which was erroneously exposed. This was attempted by 655659a74a36b ("target/arm: Correct encoding of Debug Communications Channel registers") but it was discovered that the removal of this debug register broke forward migration on TCG. Now we have the cpreg migration tolerance infrastructure, we can declare one for the DBGDTRTX. This allow to revert the reinstate patch. Signed-off-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Sebastian Ott <sebott@redhat.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260420140552.104369-7-eric.auger@redhat.com [PMM: revised comment, included note about when we can drop the workaround] Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/helper.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ void register_cp_regs_for_features(ARMCPU *cpu) .fgt = FGT_CLIDR_EL1, .resetvalue = GET_IDREG(isar, CLIDR) }; + uint64_t dbgtr_el0_kvmidx = + cpreg_to_kvm_id(ENCODE_CP_REG(14, 0, 1, 0, 5, 3, 0)); + define_one_arm_cp_reg(cpu, &clidr); define_arm_cp_regs(cpu, v7_cp_reginfo); define_debug_regs(cpu); + /* + * We used to incorrectly expose a non-existent AArch32 "DBGDTRTX" + * register with this encoding. This has been fixed by commit + * 655659a74a36 ("target/arm: Correct encoding of Debug + * Communications Channel registers") by the introduction of correct + * separate cpreg definitions for AA64 and AA32 versions. However, + * the old cpreg definition couldn't be removed without breaking + * migration, so commit 4f2b82f604 reinstated the bogus encoding + * for migration data only. + * + * Now that we have migration tolerance infrastructure, we can use + * this to allow forward migration from the buggy QEMU versions, + * accepting and ignoring the bogus register if it is in the + * source data. QEMU 11.0 was the last version that sent the + * bogus encoding, so this workaround can be removed at the point + * where we no longer care about migration from that version + * (i.e. when we remove the "virt-11.0" machine type). + */ + arm_register_cpreg_mig_tolerance(cpu, dbgtr_el0_kvmidx, + 0, 0, ToleranceNotOnBothEnds); } else { define_arm_cp_regs(cpu, not_v7_cp_reginfo); } -- 2.43.0
From: Eric Auger <eric.auger@redhat.com> This reverts commit 4f2b82f60431 ("target/arm: Reinstate bogus AArch32 DBGDTRTX register for migration compat). We don't need that commit anymore as the AArch32 DBGDTRTX register is declared to be safe to ignore in the incoming migration stream. Signed-off-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Sebastian Ott <sebott@redhat.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260420140552.104369-8-eric.auger@redhat.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/debug_helper.c | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/target/arm/debug_helper.c b/target/arm/debug_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/debug_helper.c +++ b/target/arm/debug_helper.c @@ -XXX,XX +XXX,XX @@ static void dbgclaimclr_write(CPUARMState *env, const ARMCPRegInfo *ri, env->cp15.dbgclaim &= ~(value & 0xFF); } -static CPAccessResult access_bogus(CPUARMState *env, const ARMCPRegInfo *ri, - bool isread) -{ - /* Always UNDEF, as if this cpreg didn't exist */ - return CP_ACCESS_UNDEFINED; -} - static const ARMCPRegInfo debug_cp_reginfo[] = { /* * DBGDRAR, DBGDSAR: always RAZ since we don't implement memory mapped @@ -XXX,XX +XXX,XX @@ static const ARMCPRegInfo debug_cp_reginfo[] = { .opc0 = 2, .opc1 = 3, .crn = 0, .crm = 4, .opc2 = 0, .access = PL0_RW, .accessfn = access_tdcc, .type = ARM_CP_CONST, .resetvalue = 0 }, - /* - * This is not a real AArch32 register. We used to incorrectly expose - * this due to a QEMU bug; to avoid breaking migration compatibility we - * need to continue to provide it so that we don't fail the inbound - * migration when it tells us about a sysreg that we don't have. - * We set an always-fails .accessfn, which means that the guest doesn't - * actually see this register (it will always UNDEF, identically to if - * there were no cpreg definition for it other than that we won't print - * a LOG_UNIMP message about it), and we set the ARM_CP_NO_GDB flag so the - * gdbstub won't see it either. - * (We can't just set .access = 0, because add_cpreg_to_hashtable() - * helpfully ignores cpregs which aren't accessible to the highest - * implemented EL.) - * - * TODO: implement a system for being able to describe "this register - * can be ignored if it appears in the inbound stream"; then we can - * remove this temporary hack. - */ - { .name = "BOGUS_DBGDTR_EL0", .state = ARM_CP_STATE_AA32, - .cp = 14, .opc1 = 3, .crn = 0, .crm = 5, .opc2 = 0, - .access = PL0_RW, .accessfn = access_bogus, - .type = ARM_CP_CONST | ARM_CP_NO_GDB, .resetvalue = 0 }, /* * OSECCR_EL1 provides a mechanism for an operating system * to access the contents of EDECCR. EDECCR is not implemented though, -- 2.43.0
From: Osama Abdelkader <osama.abdelkader@gmail.com> fdt_node_offset_by_compatible(fdt, -1, compat) only finds the first match. If the blob has more than one node with the same compatible string, extra nodes will remain active. Remove all the matching nodes, using the same loop as imx8mp-evk.c does for this purpose. Signed-off-by: Osama Abdelkader <osama.abdelkader@gmail.com> Message-id: 20260420162114.308519-1-osama.abdelkader@gmail.com Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/raspi4b.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/hw/arm/raspi4b.c b/hw/arm/raspi4b.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/raspi4b.c +++ b/hw/arm/raspi4b.c @@ -XXX,XX +XXX,XX @@ static void raspi4_modify_dtb(const struct arm_boot_info *info, void *fdt) for (int i = 0; i < ARRAY_SIZE(nodes_to_remove); i++) { const char *dev_str = nodes_to_remove[i]; + int offset; - int offset = fdt_node_offset_by_compatible(fdt, -1, dev_str); - if (offset >= 0) { - if (!fdt_nop_node(fdt, offset)) { - warn_report("bcm2711 dtc: %s has been disabled!", dev_str); + offset = fdt_node_offset_by_compatible(fdt, -1, dev_str); + while (offset >= 0) { + if (fdt_nop_node(fdt, offset) == 0) { + warn_report("bcm2711 dtb: %s has been disabled!", dev_str); } + offset = fdt_node_offset_by_compatible(fdt, offset, dev_str); } } -- 2.43.0
From: Yucai Liu <1486344514@qq.com> Implement a basic i.MX6UL LCDIF controller model with MMIO registers, frame-done interrupt behavior, and framebuffer-backed display updates for RGB565 and XRGB8888 input formats. Place the LCDIF device under hw/display and build it via a dedicated CONFIG_IMX6UL_LCDIF symbol. Model register fields with registerfields.h helpers and provide migration support via vmstate. Signed-off-by: Yucai Liu <1486344514@qq.com> Message-id: 20260412110240.93116-2-yangyanglan718@gmail.com Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- MAINTAINERS | 2 + hw/display/Kconfig | 4 + hw/display/imx6ul_lcdif.c | 453 ++++++++++++++++++++++++++++++ hw/display/meson.build | 1 + include/hw/display/imx6ul_lcdif.h | 37 +++ 5 files changed, 497 insertions(+) create mode 100644 hw/display/imx6ul_lcdif.c create mode 100644 include/hw/display/imx6ul_lcdif.h diff --git a/MAINTAINERS b/MAINTAINERS index XXXXXXX..XXXXXXX 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -XXX,XX +XXX,XX @@ L: qemu-arm@nongnu.org S: Odd Fixes F: hw/arm/mcimx6ul-evk.c F: hw/arm/fsl-imx6ul.c +F: hw/display/imx6ul_lcdif.c F: hw/misc/imx6ul_ccm.c F: include/hw/arm/fsl-imx6ul.h +F: include/hw/display/imx6ul_lcdif.h F: include/hw/misc/imx6ul_ccm.h F: docs/system/arm/mcimx6ul-evk.rst diff --git a/hw/display/Kconfig b/hw/display/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/hw/display/Kconfig +++ b/hw/display/Kconfig @@ -XXX,XX +XXX,XX @@ config PL110 bool select FRAMEBUFFER +config IMX6UL_LCDIF + bool + select FRAMEBUFFER + config SII9022 bool depends on I2C diff --git a/hw/display/imx6ul_lcdif.c b/hw/display/imx6ul_lcdif.c new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/hw/display/imx6ul_lcdif.c @@ -XXX,XX +XXX,XX @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * i.MX6UL LCDIF controller + * + * Copyright (c) 2026 Yucai Liu <1486344514@qq.com> + */ + +#include "qemu/osdep.h" +#include "hw/display/imx6ul_lcdif.h" +#include "hw/core/irq.h" +#include "hw/core/registerfields.h" +#include "hw/display/framebuffer.h" +#include "migration/vmstate.h" +#include "system/address-spaces.h" +#include "qemu/module.h" +#include "qemu/units.h" +#include "ui/pixel_ops.h" + +#define LCDIF_MMIO_SIZE (16 * KiB) +#define LCDIF_RESET_CTRL1 0x000f0000 + +REG32(CTRL, 0x00) + FIELD(CTRL, RUN, 0, 1) + FIELD(CTRL, WORD_LENGTH, 8, 2) +REG32(CTRL1, 0x10) + FIELD(CTRL1, CUR_FRAME_DONE_IRQ, 9, 1) + FIELD(CTRL1, CUR_FRAME_DONE_IRQ_EN, 13, 1) + FIELD(CTRL1, BYTE_PACKING_FORMAT, 16, 4) +REG32(V4_TRANSFER_COUNT, 0x30) + FIELD(V4_TRANSFER_COUNT, H_COUNT, 0, 16) + FIELD(V4_TRANSFER_COUNT, V_COUNT, 16, 16) +REG32(V4_CUR_BUF, 0x40) +REG32(V4_NEXT_BUF, 0x50) +REG32(AS_NEXT_BUF, 0x230) + +#define REG_SET 0x4 +#define REG_CLR 0x8 +#define REG_TOG 0xc + +#define CTRL_WORD_LENGTH_16 0 +#define CTRL_WORD_LENGTH_24 3 + +#define FRAME_PERIOD_NS (16 * 1000 * 1000ULL) + +enum IMX6ULLCDIFReg { + IMX6UL_LCDIF_REG_CTRL = A_CTRL >> 4, + IMX6UL_LCDIF_REG_CTRL1 = A_CTRL1 >> 4, + IMX6UL_LCDIF_REG_V4_TRANSFER_COUNT = A_V4_TRANSFER_COUNT >> 4, + IMX6UL_LCDIF_REG_V4_CUR_BUF = A_V4_CUR_BUF >> 4, + IMX6UL_LCDIF_REG_V4_NEXT_BUF = A_V4_NEXT_BUF >> 4, + IMX6UL_LCDIF_REG_AS_NEXT_BUF = A_AS_NEXT_BUF >> 4, +}; + +static inline bool imx6ul_lcdif_reg_exists(hwaddr reg) +{ + return (reg >> 4) < IMX6UL_LCDIF_REGS_NUM; +} + +static inline bool imx6ul_lcdif_reg_has_setclr(hwaddr reg) +{ + switch (reg) { + case A_CTRL: + case A_CTRL1: + return true; + default: + return false; + } +} + +static inline bool imx6ul_lcdif_is_running(IMX6ULLCDIFState *s) +{ + uint32_t ctrl = s->regs[IMX6UL_LCDIF_REG_CTRL]; + + return FIELD_EX32(ctrl, CTRL, RUN); +} + +static inline bool imx6ul_lcdif_frame_done_pending(IMX6ULLCDIFState *s) +{ + uint32_t ctrl1 = s->regs[IMX6UL_LCDIF_REG_CTRL1]; + + return FIELD_EX32(ctrl1, CTRL1, CUR_FRAME_DONE_IRQ); +} + +static void imx6ul_lcdif_schedule_frame(IMX6ULLCDIFState *s) +{ + int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + + timer_mod(s->frame_timer, now + FRAME_PERIOD_NS); +} + +static void imx6ul_lcdif_maybe_schedule_frame(IMX6ULLCDIFState *s) +{ + if (imx6ul_lcdif_is_running(s) && !imx6ul_lcdif_frame_done_pending(s)) { + imx6ul_lcdif_schedule_frame(s); + } else { + timer_del(s->frame_timer); + } +} + +static void imx6ul_lcdif_update_irq(IMX6ULLCDIFState *s) +{ + uint32_t ctrl1 = s->regs[IMX6UL_LCDIF_REG_CTRL1]; + bool level = FIELD_EX32(ctrl1, CTRL1, CUR_FRAME_DONE_IRQ_EN) && + FIELD_EX32(ctrl1, CTRL1, CUR_FRAME_DONE_IRQ); + + qemu_set_irq(s->irq, level); +} + +static void imx6ul_lcdif_frame_done(IMX6ULLCDIFState *s) +{ + uint32_t ctrl1 = s->regs[IMX6UL_LCDIF_REG_CTRL1]; + + ctrl1 = FIELD_DP32(ctrl1, CTRL1, CUR_FRAME_DONE_IRQ, 1); + s->regs[IMX6UL_LCDIF_REG_CTRL1] = ctrl1; + imx6ul_lcdif_update_irq(s); +} + +static void imx6ul_lcdif_draw_line_rgb565(void *opaque, uint8_t *dst, + const uint8_t *src, int width, + int dststep) +{ + uint32_t *dst32 = (uint32_t *)dst; + int i; + + for (i = 0; i < width; i++) { + uint16_t pixel = lduw_le_p(src); + uint8_t r = ((pixel >> 11) & 0x1f) << 3; + uint8_t g = ((pixel >> 5) & 0x3f) << 2; + uint8_t b = (pixel & 0x1f) << 3; + + *dst32++ = rgb_to_pixel32(r, g, b); + src += 2; + } +} + +static void imx6ul_lcdif_draw_line_xrgb8888(void *opaque, uint8_t *dst, + const uint8_t *src, int width, + int dststep) +{ + uint32_t *dst32 = (uint32_t *)dst; + int i; + + for (i = 0; i < width; i++) { + uint32_t pixel = ldl_le_p(src); + uint8_t r = (pixel >> 16) & 0xff; + uint8_t g = (pixel >> 8) & 0xff; + uint8_t b = pixel & 0xff; + + *dst32++ = rgb_to_pixel32(r, g, b); + src += 4; + } +} + +static void imx6ul_lcdif_update_display(void *opaque) +{ + IMX6ULLCDIFState *s = opaque; + DisplaySurface *surface = qemu_console_surface(s->con); + uint32_t transfer_count = s->regs[IMX6UL_LCDIF_REG_V4_TRANSFER_COUNT]; + uint32_t width = FIELD_EX32(transfer_count, V4_TRANSFER_COUNT, H_COUNT); + uint32_t height = FIELD_EX32(transfer_count, V4_TRANSFER_COUNT, V_COUNT); + uint32_t ctrl = s->regs[IMX6UL_LCDIF_REG_CTRL]; + uint32_t frame_base = s->regs[IMX6UL_LCDIF_REG_V4_CUR_BUF]; + drawfn fn; + int first = 0; + int last = 0; + int src_width; + + if (!imx6ul_lcdif_is_running(s) || width == 0 || height == 0) { + return; + } + + switch (FIELD_EX32(ctrl, CTRL, WORD_LENGTH)) { + case CTRL_WORD_LENGTH_16: + s->src_bpp = 2; + fn = imx6ul_lcdif_draw_line_rgb565; + break; + case CTRL_WORD_LENGTH_24: + s->src_bpp = 4; + fn = imx6ul_lcdif_draw_line_xrgb8888; + break; + default: + return; + } + + if (surface_width(surface) != width || surface_height(surface) != height) { + qemu_console_resize(s->con, width, height); + surface = qemu_console_surface(s->con); + s->invalidate = true; + } + + src_width = width * s->src_bpp; + if (s->invalidate || s->fb_base != frame_base || + s->src_width != src_width || s->rows != height) { + framebuffer_update_memory_section(&s->fbsection, get_system_memory(), + frame_base, height, src_width); + s->fb_base = frame_base; + s->src_width = src_width; + s->rows = height; + } + + framebuffer_update_display(surface, &s->fbsection, width, height, + src_width, surface_stride(surface), 0, + s->invalidate, fn, s, &first, &last); + if (first >= 0) { + dpy_gfx_update(s->con, 0, first, width, last - first + 1); + } + + s->invalidate = false; +} + +static void imx6ul_lcdif_invalidate_display(void *opaque) +{ + IMX6ULLCDIFState *s = opaque; + + s->invalidate = true; +} + +static const GraphicHwOps imx6ul_lcdif_graphic_ops = { + .invalidate = imx6ul_lcdif_invalidate_display, + .gfx_update = imx6ul_lcdif_update_display, +}; + +static void imx6ul_lcdif_frame_timer_cb(void *opaque) +{ + IMX6ULLCDIFState *s = opaque; + + if (!imx6ul_lcdif_is_running(s) || imx6ul_lcdif_frame_done_pending(s)) { + return; + } + + imx6ul_lcdif_frame_done(s); +} + +static uint64_t imx6ul_lcdif_read(void *opaque, hwaddr offset, unsigned size) +{ + IMX6ULLCDIFState *s = opaque; + hwaddr reg = offset & ~0xf; + uint32_t idx; + + assert(size == 4); + assert(!(offset & 0x3)); + assert(offset < LCDIF_MMIO_SIZE); + + idx = reg >> 4; + if (idx >= ARRAY_SIZE(s->regs)) { + return 0; + } + + return s->regs[idx]; +} + +static void imx6ul_lcdif_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + IMX6ULLCDIFState *s = opaque; + hwaddr reg = offset & ~0xf; + uint32_t idx; + uint32_t oldv; + + assert(size == 4); + assert(!(offset & 0x3)); + assert(offset < LCDIF_MMIO_SIZE); + + if (!imx6ul_lcdif_reg_exists(reg)) { + return; + } + + idx = reg >> 4; + oldv = s->regs[idx]; + + switch (offset & 0xf) { + case 0: + s->regs[idx] = (uint32_t)value; + break; + case REG_SET: + if (!imx6ul_lcdif_reg_has_setclr(reg)) { + return; + } + s->regs[idx] = oldv | (uint32_t)value; + break; + case REG_CLR: + if (!imx6ul_lcdif_reg_has_setclr(reg)) { + return; + } + s->regs[idx] = oldv & ~(uint32_t)value; + break; + case REG_TOG: + if (!imx6ul_lcdif_reg_has_setclr(reg)) { + return; + } + s->regs[idx] = oldv ^ (uint32_t)value; + break; + default: + g_assert_not_reached(); + } + + switch (reg) { + case A_CTRL: + if (!FIELD_EX32(oldv, CTRL, RUN) && + FIELD_EX32(s->regs[idx], CTRL, RUN)) { + s->invalidate = true; + graphic_hw_invalidate(s->con); + imx6ul_lcdif_maybe_schedule_frame(s); + break; + } + if (FIELD_EX32(oldv, CTRL, RUN) && + !FIELD_EX32(s->regs[idx], CTRL, RUN)) { + timer_del(s->frame_timer); + } + break; + case A_CTRL1: + if (FIELD_EX32(oldv, CTRL1, CUR_FRAME_DONE_IRQ) && + !FIELD_EX32(s->regs[idx], CTRL1, CUR_FRAME_DONE_IRQ)) { + imx6ul_lcdif_maybe_schedule_frame(s); + } + break; + case A_V4_TRANSFER_COUNT: + s->invalidate = true; + graphic_hw_invalidate(s->con); + break; + case A_V4_CUR_BUF: + s->invalidate = true; + graphic_hw_invalidate(s->con); + break; + case A_V4_NEXT_BUF: + s->regs[IMX6UL_LCDIF_REG_V4_CUR_BUF] = s->regs[idx]; + imx6ul_lcdif_frame_done(s); + s->invalidate = true; + graphic_hw_invalidate(s->con); + imx6ul_lcdif_maybe_schedule_frame(s); + return; + case A_AS_NEXT_BUF: + imx6ul_lcdif_frame_done(s); + imx6ul_lcdif_maybe_schedule_frame(s); + return; + default: + break; + } + + imx6ul_lcdif_update_irq(s); +} + +static const MemoryRegionOps imx6ul_lcdif_ops = { + .read = imx6ul_lcdif_read, + .write = imx6ul_lcdif_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void imx6ul_lcdif_reset(DeviceState *dev) +{ + IMX6ULLCDIFState *s = IMX6UL_LCDIF(dev); + + memset(s->regs, 0, sizeof(s->regs)); + s->regs[IMX6UL_LCDIF_REG_CTRL1] = LCDIF_RESET_CTRL1; + s->fb_base = 0; + s->src_width = 0; + s->rows = 0; + s->src_bpp = 0; + s->invalidate = true; + timer_del(s->frame_timer); + imx6ul_lcdif_update_irq(s); +} + +static int imx6ul_lcdif_post_load(void *opaque, int version_id) +{ + IMX6ULLCDIFState *s = opaque; + + s->fb_base = 0; + s->src_width = 0; + s->rows = 0; + s->src_bpp = 0; + s->invalidate = true; + + imx6ul_lcdif_update_irq(s); + if (imx6ul_lcdif_is_running(s) && + !imx6ul_lcdif_frame_done_pending(s) && + !timer_pending(s->frame_timer)) { + imx6ul_lcdif_schedule_frame(s); + } + + return 0; +} + +static const VMStateDescription vmstate_imx6ul_lcdif = { + .name = TYPE_IMX6UL_LCDIF, + .version_id = 1, + .minimum_version_id = 1, + .post_load = imx6ul_lcdif_post_load, + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, IMX6ULLCDIFState, IMX6UL_LCDIF_REGS_NUM), + VMSTATE_TIMER_PTR(frame_timer, IMX6ULLCDIFState), + VMSTATE_END_OF_LIST() + }, +}; + +static void imx6ul_lcdif_realize(DeviceState *dev, Error **errp) +{ + IMX6ULLCDIFState *s = IMX6UL_LCDIF(dev); + + s->frame_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + imx6ul_lcdif_frame_timer_cb, s); + s->invalidate = true; + memory_region_init_io(&s->iomem, OBJECT(dev), &imx6ul_lcdif_ops, s, + TYPE_IMX6UL_LCDIF, LCDIF_MMIO_SIZE); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); + sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq); + s->con = graphic_console_init(dev, 0, &imx6ul_lcdif_graphic_ops, s); +} + +static void imx6ul_lcdif_unrealize(DeviceState *dev) +{ + IMX6ULLCDIFState *s = IMX6UL_LCDIF(dev); + + timer_del(s->frame_timer); + timer_free(s->frame_timer); + s->frame_timer = NULL; + + if (s->con) { + graphic_console_close(s->con); + s->con = NULL; + } +} + +static void imx6ul_lcdif_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = imx6ul_lcdif_realize; + dc->unrealize = imx6ul_lcdif_unrealize; + dc->vmsd = &vmstate_imx6ul_lcdif; + device_class_set_legacy_reset(dc, imx6ul_lcdif_reset); + dc->desc = "i.MX6UL LCDIF"; +} + +static const TypeInfo imx6ul_lcdif_info = { + .name = TYPE_IMX6UL_LCDIF, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IMX6ULLCDIFState), + .class_init = imx6ul_lcdif_class_init, +}; + +static void imx6ul_lcdif_register_types(void) +{ + type_register_static(&imx6ul_lcdif_info); +} + +type_init(imx6ul_lcdif_register_types) diff --git a/hw/display/meson.build b/hw/display/meson.build index XXXXXXX..XXXXXXX 100644 --- a/hw/display/meson.build +++ b/hw/display/meson.build @@ -XXX,XX +XXX,XX @@ system_ss.add(when: ['CONFIG_VGA_CIRRUS', 'CONFIG_VGA_ISA'], if_true: files('cir system_ss.add(when: 'CONFIG_G364FB', if_true: files('g364fb.c')) system_ss.add(when: 'CONFIG_JAZZ_LED', if_true: files('jazz_led.c')) system_ss.add(when: 'CONFIG_PL110', if_true: files('pl110.c')) +system_ss.add(when: 'CONFIG_IMX6UL_LCDIF', if_true: files('imx6ul_lcdif.c')) system_ss.add(when: 'CONFIG_SII9022', if_true: files('sii9022.c')) system_ss.add(when: 'CONFIG_SSD0303', if_true: files('ssd0303.c')) system_ss.add(when: 'CONFIG_SSD0323', if_true: files('ssd0323.c')) diff --git a/include/hw/display/imx6ul_lcdif.h b/include/hw/display/imx6ul_lcdif.h new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/include/hw/display/imx6ul_lcdif.h @@ -XXX,XX +XXX,XX @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * i.MX6UL LCDIF controller + * + * Copyright (c) 2026 Yucai Liu <1486344514@qq.com> + */ + +#ifndef IMX6UL_LCDIF_H +#define IMX6UL_LCDIF_H + +#include "hw/core/sysbus.h" +#include "qom/object.h" +#include "qemu/timer.h" +#include "ui/console.h" + +#define TYPE_IMX6UL_LCDIF "imx6ul-lcdif" +#define IMX6UL_LCDIF_REGS_NUM ((0x230 >> 4) + 1) +OBJECT_DECLARE_SIMPLE_TYPE(IMX6ULLCDIFState, IMX6UL_LCDIF) + +struct IMX6ULLCDIFState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + MemoryRegionSection fbsection; + qemu_irq irq; + QemuConsole *con; + QEMUTimer *frame_timer; + uint32_t fb_base; + uint32_t src_width; + uint32_t rows; + uint8_t src_bpp; + bool invalidate; + uint32_t regs[IMX6UL_LCDIF_REGS_NUM]; +}; + +#endif /* IMX6UL_LCDIF_H */ -- 2.43.0
From: Yucai Liu <1486344514@qq.com> Instantiate LCDIF as a child object of the i.MX6UL SoC in init and realize it in the SoC realize path before MMIO/IRQ hookup. Also make FSL_IMX6UL select CONFIG_IMX6UL_LCDIF and map the LCDIF region with a 16 KiB size to match the SoC memory map. Signed-off-by: Yucai Liu <1486344514@qq.com> Message-id: 20260412110240.93116-3-yangyanglan718@gmail.com Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/Kconfig | 1 + hw/arm/fsl-imx6ul.c | 12 ++++++++++-- include/hw/arm/fsl-imx6ul.h | 4 +++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -XXX,XX +XXX,XX @@ config FSL_IMX6UL imply I2C_DEVICES select A15MPCORE select IMX + select IMX6UL_LCDIF select IMX_FEC select IMX_I2C select IMX_USBPHY diff --git a/hw/arm/fsl-imx6ul.c b/hw/arm/fsl-imx6ul.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/fsl-imx6ul.c +++ b/hw/arm/fsl-imx6ul.c @@ -XXX,XX +XXX,XX @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/arm/fsl-imx6ul.h" +#include "hw/display/imx6ul_lcdif.h" #include "hw/misc/unimp.h" #include "hw/usb/imx-usb-phy.h" #include "hw/core/boards.h" @@ -XXX,XX +XXX,XX @@ static void fsl_imx6ul_init(Object *obj) object_initialize_child(obj, name, &s->usb[i], TYPE_CHIPIDEA); } + /* + * LCDIF + */ + object_initialize_child(obj, "lcdif", &s->lcdif, TYPE_IMX6UL_LCDIF); + /* * SDHCIs */ @@ -XXX,XX +XXX,XX @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) /* * LCD */ - create_unimplemented_device("lcdif", FSL_IMX6UL_LCDIF_ADDR, - FSL_IMX6UL_LCDIF_SIZE); + sysbus_realize(SYS_BUS_DEVICE(&s->lcdif), &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->lcdif), 0, FSL_IMX6UL_LCDIF_ADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->lcdif), 0, + qdev_get_gpio_in(gic, FSL_IMX6UL_LCDIF_IRQ)); /* * CSU diff --git a/include/hw/arm/fsl-imx6ul.h b/include/hw/arm/fsl-imx6ul.h index XXXXXXX..XXXXXXX 100644 --- a/include/hw/arm/fsl-imx6ul.h +++ b/include/hw/arm/fsl-imx6ul.h @@ -XXX,XX +XXX,XX @@ #include "hw/net/imx_fec.h" #include "hw/usb/chipidea.h" #include "hw/usb/imx-usb-phy.h" +#include "hw/display/imx6ul_lcdif.h" #include "system/memory.h" #include "target/arm/cpu.h" #include "qom/object.h" @@ -XXX,XX +XXX,XX @@ struct FslIMX6ULState { IMX2WdtState wdt[FSL_IMX6UL_NUM_WDTS]; IMXUSBPHYState usbphy[FSL_IMX6UL_NUM_USB_PHYS]; ChipideaState usb[FSL_IMX6UL_NUM_USBS]; + IMX6ULLCDIFState lcdif; MemoryRegion rom; MemoryRegion caam; MemoryRegion ocram; @@ -XXX,XX +XXX,XX @@ enum FslIMX6ULMemoryMap { FSL_IMX6UL_PXP_SIZE = (16 * KiB), FSL_IMX6UL_LCDIF_ADDR = 0x021C8000, - FSL_IMX6UL_LCDIF_SIZE = 0x100, + FSL_IMX6UL_LCDIF_SIZE = (16 * KiB), FSL_IMX6UL_CSI_ADDR = 0x021C4000, FSL_IMX6UL_CSI_SIZE = 0x100, -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> We have a registerfields interface which we can use for defining fields alongside helpers to access them. Define the basic syndrome layout and convert the helpers that take the imm16 data directly. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20260422125250.1303100-2-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/syndrome.h | 75 ++++++++++++++++++++++++++++++++----------- 1 file changed, 57 insertions(+), 18 deletions(-) diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -XXX,XX +XXX,XX @@ #ifndef TARGET_ARM_SYNDROME_H #define TARGET_ARM_SYNDROME_H -#include "qemu/bitops.h" +#include "hw/core/registerfields.h" /* Valid Syndrome Register EC field values */ enum arm_exception_class { @@ -XXX,XX +XXX,XX @@ enum arm_exception_class { EC_AA64_BKPT = 0x3c, }; +/* Generic syndrome encoding layout for HSR and lower 32 bits of ESR_EL2 */ +FIELD(SYNDROME, EC, 26, 6) +FIELD(SYNDROME, IL, 25, 1) +FIELD(SYNDROME, ISS, 0, 25) + typedef enum { SME_ET_AccessTrap, SME_ET_Streaming, @@ -XXX,XX +XXX,XX @@ typedef enum { static inline uint32_t syn_get_ec(uint32_t syn) { - return syn >> ARM_EL_EC_SHIFT; + return FIELD_EX32(syn, SYNDROME, EC); } static inline uint32_t syn_set_ec(uint32_t syn, uint32_t ec) { - return deposit32(syn, ARM_EL_EC_SHIFT, ARM_EL_EC_LENGTH, ec); + return FIELD_DP32(syn, SYNDROME, EC, ec); } /* @@ -XXX,XX +XXX,XX @@ static inline uint32_t syn_set_ec(uint32_t syn, uint32_t ec) */ static inline uint32_t syn_uncategorized(void) { - return (EC_UNCATEGORIZED << ARM_EL_EC_SHIFT) | ARM_EL_IL; + uint32_t res = syn_set_ec(0, EC_UNCATEGORIZED); + res = FIELD_DP32(res, SYNDROME, IL, 1); + return res; } +FIELD(ISS_IMM16, IMM16, 0, 16) + static inline uint32_t syn_aa64_svc(uint32_t imm16) { - return (EC_AA64_SVC << ARM_EL_EC_SHIFT) | ARM_EL_IL | (imm16 & 0xffff); + uint32_t res = syn_set_ec(0, EC_AA64_SVC); + res = FIELD_DP32(res, SYNDROME, IL, 1); + res = FIELD_DP32(res, ISS_IMM16, IMM16, imm16); + return res; } static inline uint32_t syn_aa64_hvc(uint32_t imm16) { - return (EC_AA64_HVC << ARM_EL_EC_SHIFT) | ARM_EL_IL | (imm16 & 0xffff); + uint32_t res = syn_set_ec(0, EC_AA64_HVC); + res = FIELD_DP32(res, SYNDROME, IL, 1); + res = FIELD_DP32(res, ISS_IMM16, IMM16, imm16); + return res; } static inline uint32_t syn_aa64_smc(uint32_t imm16) { - return (EC_AA64_SMC << ARM_EL_EC_SHIFT) | ARM_EL_IL | (imm16 & 0xffff); + uint32_t res = syn_set_ec(0, EC_AA64_SMC); + res = FIELD_DP32(res, SYNDROME, IL, 1); + res = FIELD_DP32(res, ISS_IMM16, IMM16, imm16); + return res; } static inline uint32_t syn_aa32_svc(uint32_t imm16, bool is_16bit) { - return (EC_AA32_SVC << ARM_EL_EC_SHIFT) | (imm16 & 0xffff) - | (is_16bit ? 0 : ARM_EL_IL); + uint32_t res = syn_set_ec(0, EC_AA32_SVC); + res = FIELD_DP32(res, SYNDROME, IL, is_16bit ? 0 : 1); + res = FIELD_DP32(res, ISS_IMM16, IMM16, imm16); + return res; } static inline uint32_t syn_aa32_hvc(uint32_t imm16) { - return (EC_AA32_HVC << ARM_EL_EC_SHIFT) | ARM_EL_IL | (imm16 & 0xffff); + uint32_t res = syn_set_ec(0, EC_AA32_HVC); + res = FIELD_DP32(res, SYNDROME, IL, 1); + res = FIELD_DP32(res, ISS_IMM16, IMM16, imm16); + return res; } static inline uint32_t syn_aa32_smc(void) { - return (EC_AA32_SMC << ARM_EL_EC_SHIFT) | ARM_EL_IL; + uint32_t res = syn_set_ec(0, EC_AA32_SMC); + res = FIELD_DP32(res, SYNDROME, IL, 1); + return res; } static inline uint32_t syn_aa64_bkpt(uint32_t imm16) { - return (EC_AA64_BKPT << ARM_EL_EC_SHIFT) | ARM_EL_IL | (imm16 & 0xffff); + uint32_t res = syn_set_ec(0, EC_AA64_BKPT); + res = FIELD_DP32(res, SYNDROME, IL, 1); + res = FIELD_DP32(res, ISS_IMM16, IMM16, imm16); + return res; } static inline uint32_t syn_aa32_bkpt(uint32_t imm16, bool is_16bit) { - return (EC_AA32_BKPT << ARM_EL_EC_SHIFT) | (imm16 & 0xffff) - | (is_16bit ? 0 : ARM_EL_IL); + uint32_t res = syn_set_ec(0, EC_AA32_BKPT); + res = FIELD_DP32(res, SYNDROME, IL, is_16bit ? 0 : 1); + res = FIELD_DP32(res, ISS_IMM16, IMM16, imm16); + return res; } static inline uint32_t syn_aa64_sysregtrap(int op0, int op1, int op2, @@ -XXX,XX +XXX,XX @@ static inline uint32_t syn_simd_access_trap(int cv, int cond, bool is_16bit) static inline uint32_t syn_sve_access_trap(void) { - return (EC_SVEACCESSTRAP << ARM_EL_EC_SHIFT) | ARM_EL_IL; + uint32_t res = syn_set_ec(0, EC_SVEACCESSTRAP); + res = FIELD_DP32(res, SYNDROME, IL, 1); + return res; } /* @@ -XXX,XX +XXX,XX @@ static inline uint32_t syn_wfx(int cv, int cond, int ti, bool is_16bit) static inline uint32_t syn_illegalstate(void) { - return (EC_ILLEGALSTATE << ARM_EL_EC_SHIFT) | ARM_EL_IL; + uint32_t res = syn_set_ec(0, EC_ILLEGALSTATE); + res = FIELD_DP32(res, SYNDROME, IL, 1); + return res; } static inline uint32_t syn_pcalignment(void) { - return (EC_PCALIGNMENT << ARM_EL_EC_SHIFT) | ARM_EL_IL; + uint32_t res = syn_set_ec(0, EC_PCALIGNMENT); + res = FIELD_DP32(res, SYNDROME, IL, 1); + return res; } static inline uint32_t syn_gcs_data_check(GCSInstructionType it, int rn) @@ -XXX,XX +XXX,XX @@ static inline uint32_t syn_gcs_gcsstr(int ra, int rn) static inline uint32_t syn_serror(uint32_t extra) { - return (EC_SERROR << ARM_EL_EC_SHIFT) | ARM_EL_IL | extra; + uint32_t res = syn_set_ec(0, EC_SERROR); + res = FIELD_DP32(res, SYNDROME, IL, 1); + res = FIELD_DP32(res, SYNDROME, ISS, extra); + return res; } static inline uint32_t syn_mop(bool is_set, bool is_setg, int options, -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> Migrate syn_aa64_sysregtrap and co-processor register trap syndromes to the registerfields API. The co-processor syndromes are split between single and duel register moves. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20260422125250.1303100-3-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/syndrome.h | 124 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 102 insertions(+), 22 deletions(-) diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -XXX,XX +XXX,XX @@ enum arm_exception_class { /* Generic syndrome encoding layout for HSR and lower 32 bits of ESR_EL2 */ FIELD(SYNDROME, EC, 26, 6) -FIELD(SYNDROME, IL, 25, 1) +FIELD(SYNDROME, IL, 25, 1) /* IL=1 for 32 bit instructions */ FIELD(SYNDROME, ISS, 0, 25) typedef enum { @@ -XXX,XX +XXX,XX @@ static inline uint32_t syn_aa64_smc(uint32_t imm16) static inline uint32_t syn_aa32_svc(uint32_t imm16, bool is_16bit) { uint32_t res = syn_set_ec(0, EC_AA32_SVC); - res = FIELD_DP32(res, SYNDROME, IL, is_16bit ? 0 : 1); + res = FIELD_DP32(res, SYNDROME, IL, !is_16bit); res = FIELD_DP32(res, ISS_IMM16, IMM16, imm16); return res; } @@ -XXX,XX +XXX,XX @@ static inline uint32_t syn_aa64_bkpt(uint32_t imm16) static inline uint32_t syn_aa32_bkpt(uint32_t imm16, bool is_16bit) { uint32_t res = syn_set_ec(0, EC_AA32_BKPT); - res = FIELD_DP32(res, SYNDROME, IL, is_16bit ? 0 : 1); + res = FIELD_DP32(res, SYNDROME, IL, !is_16bit); res = FIELD_DP32(res, ISS_IMM16, IMM16, imm16); return res; } +/* + * ISS encoding for an exception from MSR, MRS, or System instruction + * in AArch64 state. + */ +FIELD(SYSREG_ISS, ISREAD, 0, 1) /* Direction, 1 is read */ +FIELD(SYSREG_ISS, CRM, 1, 4) +FIELD(SYSREG_ISS, RT, 5, 5) +FIELD(SYSREG_ISS, CRN, 10, 4) +FIELD(SYSREG_ISS, OP1, 14, 3) +FIELD(SYSREG_ISS, OP2, 17, 3) +FIELD(SYSREG_ISS, OP0, 20, 2) + static inline uint32_t syn_aa64_sysregtrap(int op0, int op1, int op2, int crn, int crm, int rt, int isread) { - return (EC_SYSTEMREGISTERTRAP << ARM_EL_EC_SHIFT) | ARM_EL_IL - | (op0 << 20) | (op2 << 17) | (op1 << 14) | (crn << 10) | (rt << 5) - | (crm << 1) | isread; + uint32_t res = syn_set_ec(0, EC_SYSTEMREGISTERTRAP); + res = FIELD_DP32(res, SYNDROME, IL, 1); + + res = FIELD_DP32(res, SYSREG_ISS, OP0, op0); + res = FIELD_DP32(res, SYSREG_ISS, OP2, op2); + res = FIELD_DP32(res, SYSREG_ISS, OP1, op1); + res = FIELD_DP32(res, SYSREG_ISS, CRN, crn); + res = FIELD_DP32(res, SYSREG_ISS, RT, rt); + res = FIELD_DP32(res, SYSREG_ISS, CRM, crm); + res = FIELD_DP32(res, SYSREG_ISS, ISREAD, isread); + + return res; } +/* + * ISS encoding for an exception from an MCR or MRC access + * (move to/from co-processor) + */ +FIELD(COPROC_ISS, ISREAD, 0, 1) +FIELD(COPROC_ISS, CRM, 1, 4) +FIELD(COPROC_ISS, RT, 5, 5) +FIELD(COPROC_ISS, CRN, 10, 4) +FIELD(COPROC_ISS, OP1, 14, 3) +FIELD(COPROC_ISS, OP2, 17, 3) +FIELD(COPROC_ISS, COND, 20, 4) +FIELD(COPROC_ISS, CV, 24, 1) + static inline uint32_t syn_cp14_rt_trap(int cv, int cond, int opc1, int opc2, int crn, int crm, int rt, int isread, bool is_16bit) { - return (EC_CP14RTTRAP << ARM_EL_EC_SHIFT) - | (is_16bit ? 0 : ARM_EL_IL) - | (cv << 24) | (cond << 20) | (opc2 << 17) | (opc1 << 14) - | (crn << 10) | (rt << 5) | (crm << 1) | isread; + uint32_t res = syn_set_ec(0, EC_CP14RTTRAP); + res = FIELD_DP32(res, SYNDROME, IL, !is_16bit); + + res = FIELD_DP32(res, COPROC_ISS, CV, cv); + res = FIELD_DP32(res, COPROC_ISS, COND, cond); + res = FIELD_DP32(res, COPROC_ISS, OP2, opc2); + res = FIELD_DP32(res, COPROC_ISS, OP1, opc1); + res = FIELD_DP32(res, COPROC_ISS, CRN, crn); + res = FIELD_DP32(res, COPROC_ISS, RT, rt); + res = FIELD_DP32(res, COPROC_ISS, CRM, crm); + res = FIELD_DP32(res, COPROC_ISS, ISREAD, isread); + + return res; } static inline uint32_t syn_cp15_rt_trap(int cv, int cond, int opc1, int opc2, int crn, int crm, int rt, int isread, bool is_16bit) { - return (EC_CP15RTTRAP << ARM_EL_EC_SHIFT) - | (is_16bit ? 0 : ARM_EL_IL) - | (cv << 24) | (cond << 20) | (opc2 << 17) | (opc1 << 14) - | (crn << 10) | (rt << 5) | (crm << 1) | isread; + uint32_t res = syn_set_ec(0, EC_CP15RTTRAP); + res = FIELD_DP32(res, SYNDROME, IL, !is_16bit); + + res = FIELD_DP32(res, COPROC_ISS, CV, cv); + res = FIELD_DP32(res, COPROC_ISS, COND, cond); + res = FIELD_DP32(res, COPROC_ISS, OP2, opc2); + res = FIELD_DP32(res, COPROC_ISS, OP1, opc1); + res = FIELD_DP32(res, COPROC_ISS, CRN, crn); + res = FIELD_DP32(res, COPROC_ISS, RT, rt); + res = FIELD_DP32(res, COPROC_ISS, CRM, crm); + res = FIELD_DP32(res, COPROC_ISS, ISREAD, isread); + + return res; } +/* + * ISS encoding for an exception from an MCRR or MRRC access + * (move to/from co-processor with 2 regs) + */ +FIELD(COPROC_R2_ISS, ISREAD, 0, 1) +FIELD(COPROC_R2_ISS, CRM, 1, 4) +FIELD(COPROC_R2_ISS, RT, 5, 5) +FIELD(COPROC_R2_ISS, RT2, 10, 5) +FIELD(COPROC_R2_ISS, OP1, 16, 4) +FIELD(COPROC_R2_ISS, COND, 20, 4) +FIELD(COPROC_R2_ISS, CV, 24, 1) + static inline uint32_t syn_cp14_rrt_trap(int cv, int cond, int opc1, int crm, int rt, int rt2, int isread, bool is_16bit) { - return (EC_CP14RRTTRAP << ARM_EL_EC_SHIFT) - | (is_16bit ? 0 : ARM_EL_IL) - | (cv << 24) | (cond << 20) | (opc1 << 16) - | (rt2 << 10) | (rt << 5) | (crm << 1) | isread; + uint32_t res = syn_set_ec(0, EC_CP14RRTTRAP); + res = FIELD_DP32(res, SYNDROME, IL, !is_16bit); + + res = FIELD_DP32(res, COPROC_R2_ISS, CV, cv); + res = FIELD_DP32(res, COPROC_R2_ISS, COND, cond); + res = FIELD_DP32(res, COPROC_R2_ISS, OP1, opc1); + res = FIELD_DP32(res, COPROC_R2_ISS, RT2, rt2); + res = FIELD_DP32(res, COPROC_R2_ISS, RT, rt); + res = FIELD_DP32(res, COPROC_R2_ISS, CRM, crm); + res = FIELD_DP32(res, COPROC_R2_ISS, ISREAD, isread); + + return res; } static inline uint32_t syn_cp15_rrt_trap(int cv, int cond, int opc1, int crm, int rt, int rt2, int isread, bool is_16bit) { - return (EC_CP15RRTTRAP << ARM_EL_EC_SHIFT) - | (is_16bit ? 0 : ARM_EL_IL) - | (cv << 24) | (cond << 20) | (opc1 << 16) - | (rt2 << 10) | (rt << 5) | (crm << 1) | isread; + uint32_t res = syn_set_ec(0, EC_CP15RRTTRAP); + res = FIELD_DP32(res, SYNDROME, IL, !is_16bit); + + res = FIELD_DP32(res, COPROC_R2_ISS, CV, cv); + res = FIELD_DP32(res, COPROC_R2_ISS, COND, cond); + res = FIELD_DP32(res, COPROC_R2_ISS, OP1, opc1); + res = FIELD_DP32(res, COPROC_R2_ISS, RT2, rt2); + res = FIELD_DP32(res, COPROC_R2_ISS, RT, rt); + res = FIELD_DP32(res, COPROC_R2_ISS, CRM, crm); + res = FIELD_DP32(res, COPROC_R2_ISS, ISREAD, isread); + + return res; } static inline uint32_t syn_fp_access_trap(int cv, int cond, bool is_16bit, -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> The syn_simd_access trap was never used so remove it. We should only see the COPROC encoding on v7 architectures. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20260422125250.1303100-4-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/syndrome.h | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -XXX,XX +XXX,XX @@ static inline uint32_t syn_cp15_rrt_trap(int cv, int cond, int opc1, int crm, return res; } +/* + * ISS encoding for an exception from an access to a register of + * instruction resulting from the FPEN or TFP traps. + */ +FIELD(FP_ISS, COPROC, 0, 4) /* ARMv7 only */ +FIELD(FP_ISS, COND, 20, 4) +FIELD(FP_ISS, CV, 24, 1) + static inline uint32_t syn_fp_access_trap(int cv, int cond, bool is_16bit, int coproc) { /* AArch32 FP trap or any AArch64 FP/SIMD trap: TA == 0 */ - return (EC_ADVSIMDFPACCESSTRAP << ARM_EL_EC_SHIFT) - | (is_16bit ? 0 : ARM_EL_IL) - | (cv << 24) | (cond << 20) | coproc; -} + uint32_t res = syn_set_ec(0, EC_ADVSIMDFPACCESSTRAP); + res = FIELD_DP32(res, SYNDROME, IL, !is_16bit); -static inline uint32_t syn_simd_access_trap(int cv, int cond, bool is_16bit) -{ - /* AArch32 SIMD trap: TA == 1 coproc == 0 */ - return (EC_ADVSIMDFPACCESSTRAP << ARM_EL_EC_SHIFT) - | (is_16bit ? 0 : ARM_EL_IL) - | (cv << 24) | (cond << 20) | (1 << 5); + res = FIELD_DP32(res, FP_ISS, CV, cv); + res = FIELD_DP32(res, FP_ISS, COND, cond); + res = FIELD_DP32(res, FP_ISS, COPROC, coproc); + + return res; } static inline uint32_t syn_sve_access_trap(void) -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> For simplicity keep the OP as a two bit field rather than the two interlinked fields in the docs (ERET/ERETA). Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20260422125250.1303100-5-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/syndrome.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -XXX,XX +XXX,XX @@ static inline uint32_t syn_sve_access_trap(void) } /* + * ISS encoding for an exception from an ERET, ERETAA or ERETAB + * instructions. + * * eret_op is bits [1:0] of the ERET instruction, so: * 0 for ERET, 2 for ERETAA, 3 for ERETAB. */ +FIELD(ERET_ISS, OP, 0, 2) + static inline uint32_t syn_erettrap(int eret_op) { - return (EC_ERETTRAP << ARM_EL_EC_SHIFT) | ARM_EL_IL | eret_op; + uint32_t res = syn_set_ec(0, EC_ERETTRAP); + res = FIELD_DP32(res, SYNDROME, IL, 1); + res = FIELD_DP32(res, ERET_ISS, OP, eret_op); + + return res; } static inline uint32_t syn_smetrap(SMEExceptionType etype, bool is_16bit) -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20260422125250.1303100-6-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/syndrome.h | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -XXX,XX +XXX,XX @@ static inline uint32_t syn_erettrap(int eret_op) return res; } +/* + * ISS encoding for an exception due to SME functionality + */ +FIELD(SME_ISS, SMTC, 0, 2) + static inline uint32_t syn_smetrap(SMEExceptionType etype, bool is_16bit) { - return (EC_SMETRAP << ARM_EL_EC_SHIFT) - | (is_16bit ? 0 : ARM_EL_IL) | etype; + uint32_t res = syn_set_ec(0, EC_SMETRAP); + res = FIELD_DP32(res, SYNDROME, IL, !is_16bit); + res = FIELD_DP32(res, SME_ISS, SMTC, etype); + + return res; } static inline uint32_t syn_pacfail(bool data, int keynumber) -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> syn_pactrap is fairly simple as the ISS is all RES0. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20260422125250.1303100-7-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/syndrome.h | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -XXX,XX +XXX,XX @@ static inline uint32_t syn_smetrap(SMEExceptionType etype, bool is_16bit) return res; } +/* + * ISS encoding for a PAC Fail exceptions + */ +FIELD(PACFAIL_ISS, BnA, 0, 1) /* B key or A key */ +FIELD(PACFAIL_ISS, DnI, 1, 1) /* Data or Instruction */ + static inline uint32_t syn_pacfail(bool data, int keynumber) { - int error_code = (data << 1) | keynumber; - return (EC_PACFAIL << ARM_EL_EC_SHIFT) | ARM_EL_IL | error_code; + uint32_t res = syn_set_ec(0, EC_PACFAIL); + res = FIELD_DP32(res, SYNDROME, IL, 1); + + res = FIELD_DP32(res, PACFAIL_ISS, DnI, data); + res = FIELD_DP32(res, PACFAIL_ISS, BnA, keynumber); + + return res; } +/* + * ISS encoding for an exception from a trapped Pointer + * Authentication instruction is RES0 + */ static inline uint32_t syn_pactrap(void) { - return (EC_PACTRAP << ARM_EL_EC_SHIFT) | ARM_EL_IL; + uint32_t res = syn_set_ec(0, EC_PACTRAP); + res = FIELD_DP32(res, SYNDROME, IL, 1); + return res; } static inline uint32_t syn_btitrap(int btype) -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20260422125250.1303100-8-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/syndrome.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -XXX,XX +XXX,XX @@ static inline uint32_t syn_pactrap(void) return res; } +/* + * ISS encoding for an exception from a Branch Target Identification + * instruction. + */ +FIELD(BTI_ISS, BTYPE, 0, 2) + static inline uint32_t syn_btitrap(int btype) { - return (EC_BTITRAP << ARM_EL_EC_SHIFT) | ARM_EL_IL | btype; + uint32_t res = syn_set_ec(0, EC_BTITRAP); + res = FIELD_DP32(res, SYNDROME, IL, 1); + res = FIELD_DP32(res, BTI_ISS, BTYPE, btype); + return res; } static inline uint32_t syn_bxjtrap(int cv, int cond, int rm) -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> This is an Armv7 specific syndrome for chips with Jazelle functionality. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20260422125250.1303100-9-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/syndrome.h | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -XXX,XX +XXX,XX @@ static inline uint32_t syn_btitrap(int btype) return res; } +/* + * ISS encoding for trapped BXJ execution + * + * This is an Armv7 encoding. + */ +FIELD(BXJ_ISS, RM, 0, 4) +/* bits 4:19 are Reserved, UNK/SBZP */ +FIELD(BXJ_ISS, COND, 20, 4) +FIELD(BXJ_ISS, CV, 24, 1) + static inline uint32_t syn_bxjtrap(int cv, int cond, int rm) { - return (EC_BXJTRAP << ARM_EL_EC_SHIFT) | ARM_EL_IL | - (cv << 24) | (cond << 20) | rm; + uint32_t res = syn_set_ec(0, EC_BXJTRAP); + res = FIELD_DP32(res, SYNDROME, IL, 1); + + res = FIELD_DP32(res, BXJ_ISS, CV, cv); + res = FIELD_DP32(res, BXJ_ISS, COND, cond); + res = FIELD_DP32(res, BXJ_ISS, RM, rm); + + return res; } static inline uint32_t syn_gpc(int s2ptw, int ind, int gpcsc, int vncr, -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20260422125250.1303100-10-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/syndrome.h | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -XXX,XX +XXX,XX @@ static inline uint32_t syn_bxjtrap(int cv, int cond, int rm) return res; } +/* + * ISS encoding for a Granule Protection Check exception + * + * These are only reported to EL3 + */ +FIELD(GPC_ISS, xFSC, 0, 6) +FIELD(GPC_ISS, WnR, 6, 1) /* Write not Read */ +FIELD(GPC_ISS, S1PTW, 7, 1) +FIELD(GPC_ISS, CM, 8, 1) +FIELD(GPC_ISS, VNCR, 13, 1) +FIELD(GPC_ISS, GPCSC, 14, 6) +FIELD(GPC_ISS, InD, 20, 1) /* Instruction not Data access */ +FIELD(GPC_ISS, S2PTW, 21, 1) + static inline uint32_t syn_gpc(int s2ptw, int ind, int gpcsc, int vncr, int cm, int s1ptw, int wnr, int fsc) { - return (EC_GPC << ARM_EL_EC_SHIFT) | ARM_EL_IL | (s2ptw << 21) - | (ind << 20) | (gpcsc << 14) | (vncr << 13) | (cm << 8) - | (s1ptw << 7) | (wnr << 6) | fsc; + uint32_t res = syn_set_ec(0, EC_GPC); + res = FIELD_DP32(res, SYNDROME, IL, 1); + + res = FIELD_DP32(res, GPC_ISS, S2PTW, s2ptw); + res = FIELD_DP32(res, GPC_ISS, InD, ind); + res = FIELD_DP32(res, GPC_ISS, GPCSC, gpcsc); + res = FIELD_DP32(res, GPC_ISS, VNCR, vncr); + res = FIELD_DP32(res, GPC_ISS, CM, cm); + res = FIELD_DP32(res, GPC_ISS, S1PTW, s1ptw); + res = FIELD_DP32(res, GPC_ISS, WnR, wnr); + res = FIELD_DP32(res, GPC_ISS, xFSC, fsc); + + return res; } static inline uint32_t syn_insn_abort(int same_el, int ea, int s1ptw, int fsc) -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> Migrate syn_insn_abort and syn_data_abort_* to the registerfields API. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20260422125250.1303100-11-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/syndrome.h | 87 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 74 insertions(+), 13 deletions(-) diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -XXX,XX +XXX,XX @@ static inline uint32_t syn_gpc(int s2ptw, int ind, int gpcsc, int vncr, return res; } +/* + * ISS encoding for an exception from an Instruction Abort + * + * (aka instruction abort) + */ +FIELD(IABORT_ISS, IFSC, 0, 6) +FIELD(IABORT_ISS, S1PTW, 7, 1) +FIELD(IABORT_ISS, EA, 9, 1) +FIELD(IABORT_ISS, FnV, 10, 1) /* FAR not Valid */ +FIELD(IABORT_ISS, SET, 11, 2) +FIELD(IABORT_ISS, PFV, 14, 1) +FIELD(IABORT_ISS, TopLevel, 21, 1) /* FEAT_THE */ + static inline uint32_t syn_insn_abort(int same_el, int ea, int s1ptw, int fsc) { - return (EC_INSNABORT << ARM_EL_EC_SHIFT) | (same_el << ARM_EL_EC_SHIFT) - | ARM_EL_IL | (ea << 9) | (s1ptw << 7) | fsc; + uint32_t res = syn_set_ec(0, EC_INSNABORT + same_el); + res = FIELD_DP32(res, SYNDROME, IL, 1); + + res = FIELD_DP32(res, IABORT_ISS, EA, ea); + res = FIELD_DP32(res, IABORT_ISS, S1PTW, s1ptw); + res = FIELD_DP32(res, IABORT_ISS, IFSC, fsc); + + return res; } +/* + * ISS encoding for an exception from a Data Abort + */ +FIELD(DABORT_ISS, DFSC, 0, 6) +FIELD(DABORT_ISS, WNR, 6, 1) +FIELD(DABORT_ISS, S1PTW, 7, 1) +FIELD(DABORT_ISS, CM, 8, 1) +FIELD(DABORT_ISS, EA, 9, 1) +FIELD(DABORT_ISS, FnV, 10, 1) +FIELD(DABORT_ISS, LST, 11, 2) +FIELD(DABORT_ISS, VNCR, 13, 1) +FIELD(DABORT_ISS, AR, 14, 1) +FIELD(DABORT_ISS, SF, 15, 1) +FIELD(DABORT_ISS, SRT, 16, 5) +FIELD(DABORT_ISS, SSE, 21, 1) +FIELD(DABORT_ISS, SAS, 22, 2) +FIELD(DABORT_ISS, ISV, 24, 1) + static inline uint32_t syn_data_abort_no_iss(int same_el, int fnv, int ea, int cm, int s1ptw, int wnr, int fsc) { - return (EC_DATAABORT << ARM_EL_EC_SHIFT) | (same_el << ARM_EL_EC_SHIFT) - | ARM_EL_IL - | (fnv << 10) | (ea << 9) | (cm << 8) | (s1ptw << 7) - | (wnr << 6) | fsc; + uint32_t res = syn_set_ec(0, EC_DATAABORT + same_el); + res = FIELD_DP32(res, SYNDROME, IL, 1); + + res = FIELD_DP32(res, DABORT_ISS, FnV, fnv); + res = FIELD_DP32(res, DABORT_ISS, EA, ea); + res = FIELD_DP32(res, DABORT_ISS, CM, cm); + res = FIELD_DP32(res, DABORT_ISS, S1PTW, s1ptw); + res = FIELD_DP32(res, DABORT_ISS, WNR, wnr); + res = FIELD_DP32(res, DABORT_ISS, DFSC, fsc); + + return res; } static inline uint32_t syn_data_abort_with_iss(int same_el, @@ -XXX,XX +XXX,XX @@ static inline uint32_t syn_data_abort_with_iss(int same_el, int wnr, int fsc, bool is_16bit) { - return (EC_DATAABORT << ARM_EL_EC_SHIFT) | (same_el << ARM_EL_EC_SHIFT) - | (is_16bit ? 0 : ARM_EL_IL) - | ARM_EL_ISV | (sas << 22) | (sse << 21) | (srt << 16) - | (sf << 15) | (ar << 14) - | (ea << 9) | (cm << 8) | (s1ptw << 7) | (wnr << 6) | fsc; + uint32_t res = syn_set_ec(0, EC_DATAABORT + same_el); + res = FIELD_DP32(res, SYNDROME, IL, !is_16bit); + + res = FIELD_DP32(res, DABORT_ISS, ISV, 1); + res = FIELD_DP32(res, DABORT_ISS, SAS, sas); + res = FIELD_DP32(res, DABORT_ISS, SSE, sse); + res = FIELD_DP32(res, DABORT_ISS, SRT, srt); + res = FIELD_DP32(res, DABORT_ISS, SF, sf); + res = FIELD_DP32(res, DABORT_ISS, AR, ar); + res = FIELD_DP32(res, DABORT_ISS, EA, ea); + res = FIELD_DP32(res, DABORT_ISS, CM, cm); + res = FIELD_DP32(res, DABORT_ISS, S1PTW, s1ptw); + res = FIELD_DP32(res, DABORT_ISS, WNR, wnr); + res = FIELD_DP32(res, DABORT_ISS, DFSC, fsc); + + return res; } /* @@ -XXX,XX +XXX,XX @@ static inline uint32_t syn_data_abort_with_iss(int same_el, */ 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; + uint32_t res = syn_set_ec(0, EC_DATAABORT_SAME_EL); + res = FIELD_DP32(res, SYNDROME, IL, 1); + + res = FIELD_DP32(res, DABORT_ISS, VNCR, 1); + res = FIELD_DP32(res, DABORT_ISS, WNR, wnr); + res = FIELD_DP32(res, DABORT_ISS, DFSC, fsc); + + return res; } static inline uint32_t syn_swstep(int same_el, int isv, int ex) -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> Migrate syn_swstep, syn_watchpoint and syn_breakpoint to the registerfields API. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20260422125250.1303100-12-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/syndrome.h | 54 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 6 deletions(-) diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -XXX,XX +XXX,XX @@ static inline uint32_t syn_data_abort_vncr(int ea, int wnr, int fsc) return res; } +/* + * ISS encoding for an exception from a Software Step exception. + */ +FIELD(SOFTSTEP_ISS, IFSC, 0, 6) +FIELD(SOFTSTEP_ISS, EX, 6, 1) +FIELD(SOFTSTEP_ISS, ISV, 24, 1) + 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) - | ARM_EL_IL | (isv << 24) | (ex << 6) | 0x22; + uint32_t res = syn_set_ec(0, EC_SOFTWARESTEP + same_el); + res = FIELD_DP32(res, SYNDROME, IL, 1); + + res = FIELD_DP32(res, SOFTSTEP_ISS, ISV, isv); + res = FIELD_DP32(res, SOFTSTEP_ISS, EX, ex); + res = FIELD_DP32(res, SOFTSTEP_ISS, IFSC, 0x22); + + return res; } +/* + * ISS encoding for an exception from a Watchpoint exception + */ +FIELD(WATCHPOINT_ISS, DFSC, 0, 6) +FIELD(WATCHPOINT_ISS, WNR, 6, 1) +FIELD(WATCHPOINT_ISS, CM, 8, 1) +FIELD(WATCHPOINT_ISS, FnV, 10, 1) +FIELD(WATCHPOINT_ISS, VNCR, 13, 1) /* FEAT_NV2 */ +FIELD(WATCHPOINT_ISS, FnP, 15, 1) +FIELD(WATCHPOINT_ISS, WPF, 16, 1) +/* bellow mandatory from FEAT_Debugv8p9 */ +FIELD(WATCHPOINT_ISS, WPTV, 17, 1) /* FEAT_Debugv8p2 - WPT valid */ +FIELD(WATCHPOINT_ISS, WPT, 18, 6) /* FEAT_Debugv8p2 - missing WP number */ + static inline uint32_t syn_watchpoint(int same_el, int cm, int wnr) { - return (EC_WATCHPOINT << ARM_EL_EC_SHIFT) | (same_el << ARM_EL_EC_SHIFT) - | ARM_EL_IL | (cm << 8) | (wnr << 6) | 0x22; + uint32_t res = syn_set_ec(0, EC_WATCHPOINT + same_el); + res = FIELD_DP32(res, SYNDROME, IL, 1); + + res = FIELD_DP32(res, WATCHPOINT_ISS, CM, cm); + res = FIELD_DP32(res, WATCHPOINT_ISS, WNR, wnr); + res = FIELD_DP32(res, WATCHPOINT_ISS, DFSC, 0x22); + + return res; } +/* + * ISS encoding for an exception from a Breakpoint or a Vector Catch + * debug exception. + */ +FIELD(BREAKPOINT_ISS, IFSC, 0, 6) + static inline uint32_t syn_breakpoint(int same_el) { - return (EC_BREAKPOINT << ARM_EL_EC_SHIFT) | (same_el << ARM_EL_EC_SHIFT) - | ARM_EL_IL | 0x22; + uint32_t res = syn_set_ec(0, EC_BREAKPOINT + same_el); + res = FIELD_DP32(res, SYNDROME, IL, 1); + res = FIELD_DP32(res, BREAKPOINT_ISS, IFSC, 0x22); + + return res; } static inline uint32_t syn_wfx(int cv, int cond, int ti, bool is_16bit) -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> This will help later when we expand the fields we report. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20260422125250.1303100-13-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/syndrome.h | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -XXX,XX +XXX,XX @@ static inline uint32_t syn_breakpoint(int same_el) return res; } +/* + * ISS encoding for an exception from a WF* instruction + */ +FIELD(WFX_ISS, TI, 0, 2) +FIELD(WFX_ISS, RV, 2, 1) +FIELD(WFX_ISS, RN, 5, 5) +FIELD(WFX_ISS, COND, 20, 4) +FIELD(WFX_ISS, CV, 24, 1) + static inline uint32_t syn_wfx(int cv, int cond, int ti, bool is_16bit) { - return (EC_WFX_TRAP << ARM_EL_EC_SHIFT) | - (is_16bit ? 0 : (1 << ARM_EL_IL_SHIFT)) | - (cv << 24) | (cond << 20) | ti; + uint32_t res = syn_set_ec(0, EC_WFX_TRAP); + res = FIELD_DP32(res, SYNDROME, IL, !is_16bit); + + res = FIELD_DP32(res, WFX_ISS, CV, cv); + res = FIELD_DP32(res, WFX_ISS, COND, cond); + res = FIELD_DP32(res, WFX_ISS, TI, ti); + + return res; } static inline uint32_t syn_illegalstate(void) -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> Tweak arg names to make it clear raddr is the data address register number. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20260422125250.1303100-14-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/syndrome.h | 39 +++++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -XXX,XX +XXX,XX @@ static inline uint32_t syn_pcalignment(void) return res; } +/* + * ISS encoding for a GCS exception + * + * Field validity depends on EXTYPE + */ +FIELD(GCS_ISS, IT, 0, 5) +FIELD(GCS_ISS, RN, 5, 5) /* only for non EXLOCK exceptions */ +FIELD(GCS_ISS, RADDR, 10, 5) /* only for GCSSTR/GCSSTTR traps */ +FIELD(GCS_ISS, EXTYPE, 20, 4) + static inline uint32_t syn_gcs_data_check(GCSInstructionType it, int rn) { - return ((EC_GCS << ARM_EL_EC_SHIFT) | ARM_EL_IL | - (GCS_ET_DataCheck << 20) | (rn << 5) | it); + uint32_t res = syn_set_ec(0, EC_GCS); + res = FIELD_DP32(res, SYNDROME, IL, 1); + + res = FIELD_DP32(res, GCS_ISS, EXTYPE, GCS_ET_DataCheck); + res = FIELD_DP32(res, GCS_ISS, RN, rn); + res = FIELD_DP32(res, GCS_ISS, IT, it); + + return res; } static inline uint32_t syn_gcs_exlock(void) { - return (EC_GCS << ARM_EL_EC_SHIFT) | ARM_EL_IL | (GCS_ET_EXLOCK << 20); + uint32_t res = syn_set_ec(0, EC_GCS); + res = FIELD_DP32(res, SYNDROME, IL, 1); + + res = FIELD_DP32(res, GCS_ISS, EXTYPE, GCS_ET_EXLOCK); + + return res; } -static inline uint32_t syn_gcs_gcsstr(int ra, int rn) +static inline uint32_t syn_gcs_gcsstr(int raddr, int rn) { - return ((EC_GCS << ARM_EL_EC_SHIFT) | ARM_EL_IL | - (GCS_ET_GCSSTR_GCSSTTR << 20) | (ra << 10) | (rn << 5)); + uint32_t res = syn_set_ec(0, EC_GCS); + res = FIELD_DP32(res, SYNDROME, IL, 1); + + res = FIELD_DP32(res, GCS_ISS, EXTYPE, GCS_ET_GCSSTR_GCSSTTR); + res = FIELD_DP32(res, GCS_ISS, RADDR, raddr); + res = FIELD_DP32(res, GCS_ISS, RN, rn); + + return res; } static inline uint32_t syn_serror(uint32_t extra) -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20260422125250.1303100-15-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/syndrome.h | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -XXX,XX +XXX,XX @@ static inline uint32_t syn_serror(uint32_t extra) return res; } +/* + * ISS encoding for an exception from the Memory Copy and Memory Set + * instructions. + */ +FIELD(MOP_ISS, SIZEREG, 0, 5) +FIELD(MOP_ISS, SRCREG, 5, 5) +FIELD(MOP_ISS, DESTREG, 10, 5) +FIELD(MOP_ISS, FORMATOPT, 16, 2) +FIELD(MOP_ISS, OPT_A, 16, 1) +FIELD(MOP_ISS, WRONG_OPT, 17, 1) +FIELD(MOP_ISS, EPILOGUE, 18, 1) +FIELD(MOP_ISS, OPTIONS, 19, 4) +FIELD(MOP_ISS, IS_SETG, 23, 1) +FIELD(MOP_ISS, MEMINST, 24, 1) + static inline uint32_t syn_mop(bool is_set, bool is_setg, int options, bool epilogue, bool wrong_option, bool option_a, int destreg, int srcreg, int sizereg) { - return (EC_MOP << ARM_EL_EC_SHIFT) | ARM_EL_IL | - (is_set << 24) | (is_setg << 23) | (options << 19) | - (epilogue << 18) | (wrong_option << 17) | (option_a << 16) | - (destreg << 10) | (srcreg << 5) | sizereg; + uint32_t res = syn_set_ec(0, EC_MOP); + res = FIELD_DP32(res, SYNDROME, IL, 1); + + res = FIELD_DP32(res, MOP_ISS, MEMINST, is_set); + res = FIELD_DP32(res, MOP_ISS, IS_SETG, is_setg); + res = FIELD_DP32(res, MOP_ISS, OPTIONS, options); + res = FIELD_DP32(res, MOP_ISS, EPILOGUE, epilogue); + res = FIELD_DP32(res, MOP_ISS, WRONG_OPT, wrong_option); + res = FIELD_DP32(res, MOP_ISS, OPT_A, option_a); + res = FIELD_DP32(res, MOP_ISS, DESTREG, destreg); + res = FIELD_DP32(res, MOP_ISS, SRCREG, srcreg); + res = FIELD_DP32(res, MOP_ISS, SIZEREG, sizereg); + + return res; } -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> It shares the same COPROC_ISS encoding as the other CP traps although not all the fields are used. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20260422125250.1303100-16-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/syndrome.h | 16 ++++++++++++++++ target/arm/tcg/vfp_helper.c | 5 +---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -XXX,XX +XXX,XX @@ FIELD(COPROC_ISS, OP2, 17, 3) FIELD(COPROC_ISS, COND, 20, 4) FIELD(COPROC_ISS, CV, 24, 1) +static inline uint32_t syn_cp10_rt_trap(int cv, int cond, int opc1, + int crn, int rt, int isread) +{ + uint32_t res = syn_set_ec(0, EC_FPIDTRAP); + res = FIELD_DP32(res, SYNDROME, IL, 1); + + res = FIELD_DP32(res, COPROC_ISS, CV, cv); + res = FIELD_DP32(res, COPROC_ISS, COND, cond); + res = FIELD_DP32(res, COPROC_ISS, OP1, opc1); + res = FIELD_DP32(res, COPROC_ISS, CRN, crn); + res = FIELD_DP32(res, COPROC_ISS, RT, rt); + res = FIELD_DP32(res, COPROC_ISS, ISREAD, isread); + + return res; +} + static inline uint32_t syn_cp14_rt_trap(int cv, int cond, int opc1, int opc2, int crn, int crm, int rt, int isread, bool is_16bit) diff --git a/target/arm/tcg/vfp_helper.c b/target/arm/tcg/vfp_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/vfp_helper.c +++ b/target/arm/tcg/vfp_helper.c @@ -XXX,XX +XXX,XX @@ void HELPER(check_hcr_el2_trap)(CPUARMState *env, uint32_t rt, uint32_t reg) g_assert_not_reached(); } - syndrome = ((EC_FPIDTRAP << ARM_EL_EC_SHIFT) - | ARM_EL_IL - | (1 << 24) | (0xe << 20) | (7 << 14) - | (reg << 10) | (rt << 5) | 1); + syndrome = syn_cp10_rt_trap(1, 0xe, 7, reg, rt, 1); raise_exception(env, EXCP_HYP_TRAP, syndrome, 2); } -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> One more step towards dropping the old #defines. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20260422125250.1303100-17-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/helper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -XXX,XX +XXX,XX @@ static void arm_cpu_do_interrupt_aarch32_hyp(CPUState *cs) */ if (cs->exception_index == EXCP_PREFETCH_ABORT || (cs->exception_index == EXCP_DATA_ABORT && - !(env->exception.syndrome & ARM_EL_ISV)) || + !FIELD_EX32(env->exception.syndrome, SYNDROME, IL)) || syn_get_ec(env->exception.syndrome) == EC_UNCATEGORIZED) { - env->exception.syndrome &= ~ARM_EL_IL; + env->exception.syndrome = FIELD_DP32(env->exception.syndrome, SYNDROME, IL, 0); } } env->cp15.esr_el[2] = env->exception.syndrome; -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> This removes the last use of ARM_EL_EC_SHIFT. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20260422125250.1303100-18-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/syndrome.h | 1 - target/arm/tcg/debug.c | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -XXX,XX +XXX,XX @@ typedef enum { } GCSInstructionType; #define ARM_EL_EC_LENGTH 6 -#define ARM_EL_EC_SHIFT 26 #define ARM_EL_IL_SHIFT 25 #define ARM_EL_ISV_SHIFT 24 #define ARM_EL_IL (1 << ARM_EL_IL_SHIFT) diff --git a/target/arm/tcg/debug.c b/target/arm/tcg/debug.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/debug.c +++ b/target/arm/tcg/debug.c @@ -XXX,XX +XXX,XX @@ raise_exception_debug(CPUARMState *env, uint32_t excp, uint32_t syndrome) * Similarly for watchpoint and breakpoint matches. */ assert(debug_el >= cur_el); - syndrome |= (debug_el == cur_el) << ARM_EL_EC_SHIFT; + syndrome |= (debug_el == cur_el) << R_SYNDROME_EC_SHIFT; raise_exception(env, excp, syndrome, debug_el); } -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> Rather than open coding a bunch of shifts and masks we can use the syndrome definitions. While we are at it assert it really is a EC_DATAABORT. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20260422125250.1303100-19-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/whpx/whpx-all.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/target/arm/whpx/whpx-all.c b/target/arm/whpx/whpx-all.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/whpx/whpx-all.c +++ b/target/arm/whpx/whpx-all.c @@ -XXX,XX +XXX,XX @@ static int whpx_handle_mmio(CPUState *cpu, WHV_MEMORY_ACCESS_CONTEXT *ctx) { uint64_t syndrome = ctx->Syndrome; - bool isv = syndrome & ARM_EL_ISV; - bool iswrite = (syndrome >> 6) & 1; - bool sse = (syndrome >> 21) & 1; - uint32_t sas = (syndrome >> 22) & 3; + bool isv = FIELD_EX32(syndrome, DABORT_ISS, ISV); + bool iswrite = FIELD_EX32(syndrome, DABORT_ISS, WNR); + bool sse = FIELD_EX32(syndrome, DABORT_ISS, SSE); + uint32_t sas = FIELD_EX32(syndrome, DABORT_ISS, SAS); uint32_t len = 1 << sas; - uint32_t srt = (syndrome >> 16) & 0x1f; - uint32_t cm = (syndrome >> 8) & 0x1; + uint32_t srt = FIELD_EX32(syndrome, DABORT_ISS, SRT); + uint32_t cm = FIELD_EX32(syndrome, DABORT_ISS, CM); uint64_t val = 0; + assert(syn_get_ec(syndrome) == EC_DATAABORT); assert(!cm); assert(isv); -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> Rather than open coding a bunch of shifts and masks we can use the syndrome definitions. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-id: 20260422125250.1303100-20-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/hvf/hvf.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -XXX,XX +XXX,XX @@ static int hvf_handle_exception(CPUState *cpu, hv_vcpu_exit_exception_t *excp) break; } case EC_DATAABORT: { - bool isv = syndrome & ARM_EL_ISV; - bool iswrite = (syndrome >> 6) & 1; - bool s1ptw = (syndrome >> 7) & 1; - bool sse = (syndrome >> 21) & 1; - uint32_t sas = (syndrome >> 22) & 3; + bool isv = FIELD_EX32(syndrome, DABORT_ISS, ISV); + bool iswrite = FIELD_EX32(syndrome, DABORT_ISS, WNR); + bool s1ptw = FIELD_EX32(syndrome, DABORT_ISS, S1PTW); + bool sse = FIELD_EX32(syndrome, DABORT_ISS, SSE); + uint32_t sas = FIELD_EX32(syndrome, DABORT_ISS, SAS); uint32_t len = 1 << sas; - uint32_t srt = (syndrome >> 16) & 0x1f; - uint32_t cm = (syndrome >> 8) & 0x1; + uint32_t srt = FIELD_EX32(syndrome, DABORT_ISS, SRT); + uint32_t cm = FIELD_EX32(syndrome, DABORT_ISS, CM); uint64_t val = 0; uint64_t ipa = excp->physical_address; AddressSpace *as = cpu_get_address_space(cpu, ARMASIdx_NS); -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> One more step to removing the old defines. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20260422125250.1303100-21-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/tlb_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/tcg/tlb_helper.c b/target/arm/tcg/tlb_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/tlb_helper.c +++ b/target/arm/tcg/tlb_helper.c @@ -XXX,XX +XXX,XX @@ static inline uint64_t merge_syn_data_abort(uint32_t template_syn, */ assert(!fi->stage2); syn = syn_data_abort_vncr(fi->ea, is_write, fsc); - } else if (!(template_syn & ARM_EL_ISV) || target_el != 2 + } else if (!FIELD_EX32(template_syn, DABORT_ISS, 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); -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> These are only valid for data abort syndromes. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20260422125250.1303100-22-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/tcg/tlb_helper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/arm/tcg/tlb_helper.c b/target/arm/tcg/tlb_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/tlb_helper.c +++ b/target/arm/tcg/tlb_helper.c @@ -XXX,XX +XXX,XX @@ static inline uint64_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_VNCR) { + if (FIELD_EX32(template_syn, DABORT_ISS, 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 @@ -XXX,XX +XXX,XX @@ void arm_deliver_fault(ARMCPU *cpu, vaddr addr, * because we masked that out in disas_set_insn_syndrome()) */ bool is_vncr = (access_type != MMU_INST_FETCH) && - (env->exception.syndrome & ARM_EL_VNCR); + FIELD_EX32(env->exception.syndrome, DABORT_ISS, VNCR); if (is_vncr) { /* FEAT_NV2 faults on accesses via VNCR_EL2 go to EL2 */ -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> Now everything is defined with registerfields we can drop the old defines. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20260422125250.1303100-23-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/syndrome.h | 9 --------- 1 file changed, 9 deletions(-) diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -XXX,XX +XXX,XX @@ typedef enum { GCS_IT_GCSPOPX = 9, } GCSInstructionType; -#define ARM_EL_EC_LENGTH 6 -#define ARM_EL_IL_SHIFT 25 -#define ARM_EL_ISV_SHIFT 24 -#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 FIELD_EX32(syn, SYNDROME, EC); -- 2.43.0
From: Alex Bennée <alex.bennee@linaro.org> Pass the register number (rd) to the wfit helper and report it in the syndrome ISS. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-id: 20260422125250.1303100-24-alex.bennee@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target/arm/syndrome.h | 11 ++++++++++- target/arm/tcg/helper-defs.h | 2 +- target/arm/tcg/op_helper.c | 7 ++++--- target/arm/tcg/translate-a64.c | 2 +- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -XXX,XX +XXX,XX @@ FIELD(WFX_ISS, RN, 5, 5) FIELD(WFX_ISS, COND, 20, 4) FIELD(WFX_ISS, CV, 24, 1) -static inline uint32_t syn_wfx(int cv, int cond, int ti, bool is_16bit) +typedef enum { + WFI = 0b00, + WFE = 0b01, + WFIT = 0b10, + WFET = 0xb11 +} wfx_ti; + +static inline uint32_t syn_wfx(int cv, int cond, int rn, bool rv, wfx_ti ti, bool is_16bit) { uint32_t res = syn_set_ec(0, EC_WFX_TRAP); res = FIELD_DP32(res, SYNDROME, IL, !is_16bit); @@ -XXX,XX +XXX,XX @@ static inline uint32_t syn_wfx(int cv, int cond, int ti, bool is_16bit) res = FIELD_DP32(res, WFX_ISS, CV, cv); res = FIELD_DP32(res, WFX_ISS, COND, cond); res = FIELD_DP32(res, WFX_ISS, TI, ti); + res = FIELD_DP32(res, WFX_ISS, RN, rn); + res = FIELD_DP32(res, WFX_ISS, RV, rv); return res; } diff --git a/target/arm/tcg/helper-defs.h b/target/arm/tcg/helper-defs.h index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/helper-defs.h +++ b/target/arm/tcg/helper-defs.h @@ -XXX,XX +XXX,XX @@ DEF_HELPER_2(exception_pc_alignment, noreturn, env, vaddr) DEF_HELPER_1(setend, void, env) DEF_HELPER_2(wfi, void, env, i32) DEF_HELPER_1(wfe, void, env) -DEF_HELPER_2(wfit, void, env, i64) +DEF_HELPER_2(wfit, void, env, i32) DEF_HELPER_1(yield, void, env) DEF_HELPER_1(pre_hvc, void, env) DEF_HELPER_2(pre_smc, void, env, i32) diff --git a/target/arm/tcg/op_helper.c b/target/arm/tcg/op_helper.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/op_helper.c +++ b/target/arm/tcg/op_helper.c @@ -XXX,XX +XXX,XX @@ void HELPER(wfi)(CPUARMState *env, uint32_t insn_len) env->regs[15] -= insn_len; } - raise_exception(env, excp, syn_wfx(1, 0xe, 0, insn_len == 2), + raise_exception(env, excp, syn_wfx(1, 0xe, 0, false, WFI, insn_len == 2), target_el); } @@ -XXX,XX +XXX,XX @@ void HELPER(wfi)(CPUARMState *env, uint32_t insn_len) #endif } -void HELPER(wfit)(CPUARMState *env, uint64_t timeout) +void HELPER(wfit)(CPUARMState *env, uint32_t rd) { #ifdef CONFIG_USER_ONLY /* @@ -XXX,XX +XXX,XX @@ void HELPER(wfit)(CPUARMState *env, uint64_t timeout) int target_el = check_wfx_trap(env, false, &excp); /* The WFIT should time out when CNTVCT_EL0 >= the specified value. */ uint64_t cntval = gt_get_countervalue(env); + uint64_t timeout = env->xregs[rd]; /* * We want the value that we would get if we read CNTVCT_EL0 from * the current exception level, so the direct_access offset, not @@ -XXX,XX +XXX,XX @@ void HELPER(wfit)(CPUARMState *env, uint64_t timeout) if (target_el) { env->pc -= 4; - raise_exception(env, excp, syn_wfx(1, 0xe, 2, false), target_el); + raise_exception(env, excp, syn_wfx(1, 0xe, rd, true, WFIT, false), target_el); } if (uadd64_overflow(timeout, offset, &nexttick)) { diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index XXXXXXX..XXXXXXX 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -XXX,XX +XXX,XX @@ static bool trans_WFIT(DisasContext *s, arg_WFIT *a) } gen_a64_update_pc(s, 4); - gen_helper_wfit(tcg_env, cpu_reg(s, a->rd)); + gen_helper_wfit(tcg_env, tcg_constant_i32(a->rd)); /* Go back to the main loop to check for interrupts */ s->base.is_jmp = DISAS_EXIT; return true; -- 2.43.0