From 92bf8ee6203aa4bfec7aa7bc0c1ea50caf073855 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 16 Nov 2018 21:53:21 -0500 Subject: [PATCH] target/arm: Correctly implement handling of HCR_EL2.{VI, VF} In commit 8a0fc3a29fc2315325400 we tried to implement HCR_EL2.{VI,VF}, but we got it wrong and had to revert it. In that commit we implemented them as simply tracking whether there is a pending virtual IRQ or virtual FIQ. This is not correct -- these bits cause a software-generated VIRQ/VFIQ, which is distinct from whether there is a hardware-generated VIRQ/VFIQ caused by the external interrupt controller. So we need to track separately the HCR_EL2 bit state and the external virq/vfiq line state, and OR the two together to get the actual pending VIRQ/VFIQ state. Fixes: 8a0fc3a29fc2315325400c738f807d0d4ae0ab7f Backports commit 89430fc6f80a5aef1d4cbd6fc26b40c30793786c from qemu --- qemu/aarch64.h | 2 ++ qemu/aarch64eb.h | 2 ++ qemu/arm.h | 2 ++ qemu/armeb.h | 2 ++ qemu/header_gen.py | 2 ++ qemu/m68k.h | 2 ++ qemu/mips.h | 2 ++ qemu/mips64.h | 2 ++ qemu/mips64el.h | 2 ++ qemu/mipsel.h | 2 ++ qemu/powerpc.h | 2 ++ qemu/sparc.h | 2 ++ qemu/sparc64.h | 2 ++ qemu/target/arm/cpu.c | 42 +++++++++++++++++++++++++++++++++++++ qemu/target/arm/helper.c | 22 ++++++++++++++++--- qemu/target/arm/internals.h | 18 ++++++++++++++++ qemu/x86_64.h | 2 ++ 17 files changed, 107 insertions(+), 3 deletions(-) diff --git a/qemu/aarch64.h b/qemu/aarch64.h index 303cc6eb..bb413ca4 100644 --- a/qemu/aarch64.h +++ b/qemu/aarch64.h @@ -151,6 +151,8 @@ #define arm_cpu_register_gdb_regs_for_features arm_cpu_register_gdb_regs_for_features_aarch64 #define arm_cpu_register_types arm_cpu_register_types_aarch64 #define arm_cpu_set_pc arm_cpu_set_pc_aarch64 +#define arm_cpu_update_virq arm_cpu_update_virq_aarch64 +#define arm_cpu_update_vfiq arm_cpu_update_vfiq_aarch64 #define arm_cpus arm_cpus_aarch64 #define arm_current_el arm_current_el_aarch64 #define arm_dc_feature arm_dc_feature_aarch64 diff --git a/qemu/aarch64eb.h b/qemu/aarch64eb.h index 4f33a2e5..304bdff6 100644 --- a/qemu/aarch64eb.h +++ b/qemu/aarch64eb.h @@ -151,6 +151,8 @@ #define arm_cpu_register_gdb_regs_for_features arm_cpu_register_gdb_regs_for_features_aarch64eb #define arm_cpu_register_types arm_cpu_register_types_aarch64eb #define arm_cpu_set_pc arm_cpu_set_pc_aarch64eb +#define arm_cpu_update_virq arm_cpu_update_virq_aarch64eb +#define arm_cpu_update_vfiq arm_cpu_update_vfiq_aarch64eb #define arm_cpus arm_cpus_aarch64eb #define arm_current_el arm_current_el_aarch64eb #define arm_dc_feature arm_dc_feature_aarch64eb diff --git a/qemu/arm.h b/qemu/arm.h index e57a3cdb..64c494e3 100644 --- a/qemu/arm.h +++ b/qemu/arm.h @@ -151,6 +151,8 @@ #define arm_cpu_register_gdb_regs_for_features arm_cpu_register_gdb_regs_for_features_arm #define arm_cpu_register_types arm_cpu_register_types_arm #define arm_cpu_set_pc arm_cpu_set_pc_arm +#define arm_cpu_update_virq arm_cpu_update_virq_arm +#define arm_cpu_update_vfiq arm_cpu_update_vfiq_arm #define arm_cpus arm_cpus_arm #define arm_current_el arm_current_el_arm #define arm_dc_feature arm_dc_feature_arm diff --git a/qemu/armeb.h b/qemu/armeb.h index 44a45b87..435734ff 100644 --- a/qemu/armeb.h +++ b/qemu/armeb.h @@ -151,6 +151,8 @@ #define arm_cpu_register_gdb_regs_for_features arm_cpu_register_gdb_regs_for_features_armeb #define arm_cpu_register_types arm_cpu_register_types_armeb #define arm_cpu_set_pc arm_cpu_set_pc_armeb +#define arm_cpu_update_virq arm_cpu_update_virq_armeb +#define arm_cpu_update_vfiq arm_cpu_update_vfiq_armeb #define arm_cpus arm_cpus_armeb #define arm_current_el arm_current_el_armeb #define arm_dc_feature arm_dc_feature_armeb diff --git a/qemu/header_gen.py b/qemu/header_gen.py index 31e66ae2..b06231c7 100644 --- a/qemu/header_gen.py +++ b/qemu/header_gen.py @@ -157,6 +157,8 @@ symbols = ( 'arm_cpu_register_gdb_regs_for_features', 'arm_cpu_register_types', 'arm_cpu_set_pc', + 'arm_cpu_update_virq', + 'arm_cpu_update_vfiq', 'arm_cpus', 'arm_current_el', 'arm_dc_feature', diff --git a/qemu/m68k.h b/qemu/m68k.h index c6115bff..45e4b357 100644 --- a/qemu/m68k.h +++ b/qemu/m68k.h @@ -151,6 +151,8 @@ #define arm_cpu_register_gdb_regs_for_features arm_cpu_register_gdb_regs_for_features_m68k #define arm_cpu_register_types arm_cpu_register_types_m68k #define arm_cpu_set_pc arm_cpu_set_pc_m68k +#define arm_cpu_update_virq arm_cpu_update_virq_m68k +#define arm_cpu_update_vfiq arm_cpu_update_vfiq_m68k #define arm_cpus arm_cpus_m68k #define arm_current_el arm_current_el_m68k #define arm_dc_feature arm_dc_feature_m68k diff --git a/qemu/mips.h b/qemu/mips.h index a685f27b..fe25dec9 100644 --- a/qemu/mips.h +++ b/qemu/mips.h @@ -151,6 +151,8 @@ #define arm_cpu_register_gdb_regs_for_features arm_cpu_register_gdb_regs_for_features_mips #define arm_cpu_register_types arm_cpu_register_types_mips #define arm_cpu_set_pc arm_cpu_set_pc_mips +#define arm_cpu_update_virq arm_cpu_update_virq_mips +#define arm_cpu_update_vfiq arm_cpu_update_vfiq_mips #define arm_cpus arm_cpus_mips #define arm_current_el arm_current_el_mips #define arm_dc_feature arm_dc_feature_mips diff --git a/qemu/mips64.h b/qemu/mips64.h index ea8e85bf..4d432e50 100644 --- a/qemu/mips64.h +++ b/qemu/mips64.h @@ -151,6 +151,8 @@ #define arm_cpu_register_gdb_regs_for_features arm_cpu_register_gdb_regs_for_features_mips64 #define arm_cpu_register_types arm_cpu_register_types_mips64 #define arm_cpu_set_pc arm_cpu_set_pc_mips64 +#define arm_cpu_update_virq arm_cpu_update_virq_mips64 +#define arm_cpu_update_vfiq arm_cpu_update_vfiq_mips64 #define arm_cpus arm_cpus_mips64 #define arm_current_el arm_current_el_mips64 #define arm_dc_feature arm_dc_feature_mips64 diff --git a/qemu/mips64el.h b/qemu/mips64el.h index 7bff5341..05475211 100644 --- a/qemu/mips64el.h +++ b/qemu/mips64el.h @@ -151,6 +151,8 @@ #define arm_cpu_register_gdb_regs_for_features arm_cpu_register_gdb_regs_for_features_mips64el #define arm_cpu_register_types arm_cpu_register_types_mips64el #define arm_cpu_set_pc arm_cpu_set_pc_mips64el +#define arm_cpu_update_virq arm_cpu_update_virq_mips64el +#define arm_cpu_update_vfiq arm_cpu_update_vfiq_mips64el #define arm_cpus arm_cpus_mips64el #define arm_current_el arm_current_el_mips64el #define arm_dc_feature arm_dc_feature_mips64el diff --git a/qemu/mipsel.h b/qemu/mipsel.h index a1061380..73e83d10 100644 --- a/qemu/mipsel.h +++ b/qemu/mipsel.h @@ -151,6 +151,8 @@ #define arm_cpu_register_gdb_regs_for_features arm_cpu_register_gdb_regs_for_features_mipsel #define arm_cpu_register_types arm_cpu_register_types_mipsel #define arm_cpu_set_pc arm_cpu_set_pc_mipsel +#define arm_cpu_update_virq arm_cpu_update_virq_mipsel +#define arm_cpu_update_vfiq arm_cpu_update_vfiq_mipsel #define arm_cpus arm_cpus_mipsel #define arm_current_el arm_current_el_mipsel #define arm_dc_feature arm_dc_feature_mipsel diff --git a/qemu/powerpc.h b/qemu/powerpc.h index 6c3d391a..213be711 100644 --- a/qemu/powerpc.h +++ b/qemu/powerpc.h @@ -151,6 +151,8 @@ #define arm_cpu_register_gdb_regs_for_features arm_cpu_register_gdb_regs_for_features_powerpc #define arm_cpu_register_types arm_cpu_register_types_powerpc #define arm_cpu_set_pc arm_cpu_set_pc_powerpc +#define arm_cpu_update_virq arm_cpu_update_virq_powerpc +#define arm_cpu_update_vfiq arm_cpu_update_vfiq_powerpc #define arm_cpus arm_cpus_powerpc #define arm_current_el arm_current_el_powerpc #define arm_dc_feature arm_dc_feature_powerpc diff --git a/qemu/sparc.h b/qemu/sparc.h index fb1ff336..ebd88ed1 100644 --- a/qemu/sparc.h +++ b/qemu/sparc.h @@ -151,6 +151,8 @@ #define arm_cpu_register_gdb_regs_for_features arm_cpu_register_gdb_regs_for_features_sparc #define arm_cpu_register_types arm_cpu_register_types_sparc #define arm_cpu_set_pc arm_cpu_set_pc_sparc +#define arm_cpu_update_virq arm_cpu_update_virq_sparc +#define arm_cpu_update_vfiq arm_cpu_update_vfiq_sparc #define arm_cpus arm_cpus_sparc #define arm_current_el arm_current_el_sparc #define arm_dc_feature arm_dc_feature_sparc diff --git a/qemu/sparc64.h b/qemu/sparc64.h index 8d61dfdf..a38017c9 100644 --- a/qemu/sparc64.h +++ b/qemu/sparc64.h @@ -151,6 +151,8 @@ #define arm_cpu_register_gdb_regs_for_features arm_cpu_register_gdb_regs_for_features_sparc64 #define arm_cpu_register_types arm_cpu_register_types_sparc64 #define arm_cpu_set_pc arm_cpu_set_pc_sparc64 +#define arm_cpu_update_virq arm_cpu_update_virq_sparc64 +#define arm_cpu_update_vfiq arm_cpu_update_vfiq_sparc64 #define arm_cpus arm_cpus_sparc64 #define arm_current_el arm_current_el_sparc64 #define arm_dc_feature arm_dc_feature_sparc64 diff --git a/qemu/target/arm/cpu.c b/qemu/target/arm/cpu.c index 0e3453a6..a851e932 100644 --- a/qemu/target/arm/cpu.c +++ b/qemu/target/arm/cpu.c @@ -428,6 +428,48 @@ static bool arm_v7m_cpu_exec_interrupt(CPUState *cs, int interrupt_request) } #endif +void arm_cpu_update_virq(ARMCPU *cpu) +{ + /* + * Update the interrupt level for VIRQ, which is the logical OR of + * the HCR_EL2.VI bit and the input line level from the GIC. + */ + CPUARMState *env = &cpu->env; + CPUState *cs = CPU(cpu); + + bool new_state = (env->cp15.hcr_el2 & HCR_VI) || + (env->irq_line_state & CPU_INTERRUPT_VIRQ); + + if (new_state != ((cs->interrupt_request & CPU_INTERRUPT_VIRQ) != 0)) { + if (new_state) { + cpu_interrupt(cs, CPU_INTERRUPT_VIRQ); + } else { + cpu_reset_interrupt(cs, CPU_INTERRUPT_VIRQ); + } + } +} + +void arm_cpu_update_vfiq(ARMCPU *cpu) +{ + /* + * Update the interrupt level for VFIQ, which is the logical OR of + * the HCR_EL2.VF bit and the input line level from the GIC. + */ + CPUARMState *env = &cpu->env; + CPUState *cs = CPU(cpu); + + bool new_state = (env->cp15.hcr_el2 & HCR_VF) || + (env->irq_line_state & CPU_INTERRUPT_VFIQ); + + if (new_state != ((cs->interrupt_request & CPU_INTERRUPT_VFIQ) != 0)) { + if (new_state) { + cpu_interrupt(cs, CPU_INTERRUPT_VFIQ); + } else { + cpu_reset_interrupt(cs, CPU_INTERRUPT_VFIQ); + } + } +} + static QEMU_UNUSED_FUNC bool arm_cpu_is_big_endian(CPUState *cs) { ARMCPU *cpu = ARM_CPU(NULL, cs); diff --git a/qemu/target/arm/helper.c b/qemu/target/arm/helper.c index f79f8d44..2f8b4c6b 100644 --- a/qemu/target/arm/helper.c +++ b/qemu/target/arm/helper.c @@ -3512,6 +3512,22 @@ static void hcr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) tlb_flush(CPU(cpu)); } env->cp15.hcr_el2 = value; + + /* + * Updates to VI and VF require us to update the status of + * virtual interrupts, which are the logical OR of these bits + * and the state of the input lines from the GIC. (This requires + * that we have the iothread lock, which is done by marking the + * reginfo structs as ARM_CP_IO.) + * Note that if a write to HCR pends a VIRQ or VFIQ it is never + * possible for it to be taken immediately, because VIRQ and + * VFIQ are masked unless running at EL0 or EL1, and HCR + * can only be written at EL2. + */ + // Unicorn: Commented out + //g_assert(qemu_mutex_iothread_locked()); + arm_cpu_update_virq(cpu); + arm_cpu_update_vfiq(cpu); } static void hcr_writehigh(CPUARMState *env, const ARMCPRegInfo *ri, @@ -3532,10 +3548,10 @@ static void hcr_writelow(CPUARMState *env, const ARMCPRegInfo *ri, static const ARMCPRegInfo el2_cp_reginfo[] = { { "HCR_EL2", 0,1,1, 3,4,0, ARM_CP_STATE_AA64, - 0, PL2_RW, 0, NULL, 0, offsetof(CPUARMState, cp15.hcr_el2), {0, 0}, + ARM_CP_IO, PL2_RW, 0, NULL, 0, offsetof(CPUARMState, cp15.hcr_el2), {0, 0}, NULL, NULL, hcr_write }, { "HCR", 15,1,1, 0,4,0, ARM_CP_STATE_AA32, - ARM_CP_ALIAS, PL2_RW, 0, NULL, 0, offsetof(CPUARMState, cp15.hcr_el2), + ARM_CP_ALIAS | ARM_CP_IO, PL2_RW, 0, NULL, 0, offsetof(CPUARMState, cp15.hcr_el2), {0, 0}, NULL, NULL, hcr_writelow }, { "ELR_EL2", 0,4,0, 3,4,1, ARM_CP_STATE_AA64, ARM_CP_ALIAS, PL2_RW, 0, NULL, 0, offsetof(CPUARMState, elr_el[2]) }, @@ -3700,7 +3716,7 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { static const ARMCPRegInfo el2_v8_cp_reginfo[] = { { "HCR2", 15,1,1, 0,4,4, ARM_CP_STATE_AA32, - ARM_CP_ALIAS, PL2_RW, 0, NULL, 0, offsetofhigh32(CPUARMState, cp15.hcr_el2), + ARM_CP_ALIAS | ARM_CP_IO, PL2_RW, 0, NULL, 0, offsetofhigh32(CPUARMState, cp15.hcr_el2), {0, 0}, NULL, NULL, hcr_writehigh }, REGINFO_SENTINEL }; diff --git a/qemu/target/arm/internals.h b/qemu/target/arm/internals.h index 1f1110fa..fc8900d8 100644 --- a/qemu/target/arm/internals.h +++ b/qemu/target/arm/internals.h @@ -873,4 +873,22 @@ static inline const char *aarch32_mode_name(uint32_t psr) return cpu_mode_names[psr & 0xf]; } +/** + * arm_cpu_update_virq: Update CPU_INTERRUPT_VIRQ bit in cs->interrupt_request + * + * Update the CPU_INTERRUPT_VIRQ bit in cs->interrupt_request, following + * a change to either the input VIRQ line from the GIC or the HCR_EL2.VI bit. + * Must be called with the iothread lock held. + */ +void arm_cpu_update_virq(ARMCPU *cpu); + +/** + * arm_cpu_update_vfiq: Update CPU_INTERRUPT_VFIQ bit in cs->interrupt_request + * + * Update the CPU_INTERRUPT_VFIQ bit in cs->interrupt_request, following + * a change to either the input VFIQ line from the GIC or the HCR_EL2.VF bit. + * Must be called with the iothread lock held. + */ +void arm_cpu_update_vfiq(ARMCPU *cpu); + #endif diff --git a/qemu/x86_64.h b/qemu/x86_64.h index 4204cb15..6a46085e 100644 --- a/qemu/x86_64.h +++ b/qemu/x86_64.h @@ -151,6 +151,8 @@ #define arm_cpu_register_gdb_regs_for_features arm_cpu_register_gdb_regs_for_features_x86_64 #define arm_cpu_register_types arm_cpu_register_types_x86_64 #define arm_cpu_set_pc arm_cpu_set_pc_x86_64 +#define arm_cpu_update_virq arm_cpu_update_virq_x86_64 +#define arm_cpu_update_vfiq arm_cpu_update_vfiq_x86_64 #define arm_cpus arm_cpus_x86_64 #define arm_current_el arm_current_el_x86_64 #define arm_dc_feature arm_dc_feature_x86_64