diff --git a/include/hw/intc/arm_gicv5_types.h b/include/hw/intc/arm_gicv5_types.h index 5966ebde05..eaed42f49f 100644 --- a/include/hw/intc/arm_gicv5_types.h +++ b/include/hw/intc/arm_gicv5_types.h @@ -12,6 +12,8 @@ #ifndef HW_INTC_ARM_GICv5_TYPES_H #define HW_INTC_ARM_GICv5_TYPES_H +#include "hw/core/registerfields.h" + /* * The GICv5 has four physical Interrupt Domains. This numbering must * match the encoding used in IRS_IDR0.INT_DOM. @@ -86,4 +88,23 @@ typedef enum GICv5TriggerMode { #define PRIO_IDLE 0xff +/* + * We keep track of candidate highest possible pending interrupts + * using this struct. + * + * Unlike GICv3, we don't need a separate NMI bool, because for GICv5 + * superpriority is signaled by @prio == 0. + * + * In this struct the intid includes the interrupt type in bits + * [31:29] (i.e. it is in the form defined by R_TJPHS). + */ +typedef struct GICv5PendingIrq { + uint32_t intid; + uint8_t prio; +} GICv5PendingIrq; + +/* Fields in a generic 32-bit INTID, per R_TJPHS */ +FIELD(INTID, ID, 0, 24) +FIELD(INTID, TYPE, 29, 3) + #endif diff --git a/meson.build b/meson.build index 5fbdc75a0f..2c95d6995a 100644 --- a/meson.build +++ b/meson.build @@ -3688,6 +3688,7 @@ if have_system or have_user 'hw/core', 'target/arm', 'target/arm/hvf', + 'target/arm/tcg', 'target/hppa', 'target/i386', 'target/i386/kvm', diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 76db5a9197..f27f2e6bcc 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -611,6 +611,9 @@ typedef struct CPUArchState { uint64_t ppi_enable[GICV5_NUM_PPIS / 64]; /* The PRIO regs have 1 byte per PPI, so 8 PPIs to a register */ uint64_t ppi_priority[GICV5_NUM_PPIS / 8]; + + /* Cached highest-priority pending PPI for each domain */ + GICv5PendingIrq ppi_hppi[NUM_GICV5_DOMAINS]; } gicv5_cpuif; struct { diff --git a/target/arm/tcg/gicv5-cpuif.c b/target/arm/tcg/gicv5-cpuif.c index 33e4762ef4..80bb1cd391 100644 --- a/target/arm/tcg/gicv5-cpuif.c +++ b/target/arm/tcg/gicv5-cpuif.c @@ -11,6 +11,7 @@ #include "internals.h" #include "cpregs.h" #include "hw/intc/arm_gicv5_stream.h" +#include "trace.h" FIELD(GIC_CDPRI, ID, 0, 24) FIELD(GIC_CDPRI, TYPE, 29, 3) @@ -105,6 +106,57 @@ static uint64_t gic_running_prio(CPUARMState *env, GICv5Domain domain) return hap < 32 ? hap : PRIO_IDLE; } +static void gic_recalc_ppi_hppi(CPUARMState *env) +{ + /* + * Recalculate the HPPI PPI: this is the best PPI which is + * enabled, pending and not active. + */ + for (int i = 0; i < ARRAY_SIZE(env->gicv5_cpuif.ppi_hppi); i++) { + env->gicv5_cpuif.ppi_hppi[i].intid = 0; + env->gicv5_cpuif.ppi_hppi[i].prio = PRIO_IDLE; + }; + + for (int i = 0; i < ARRAY_SIZE(env->gicv5_cpuif.ppi_active); i++) { + uint64_t en_pend_nact = env->gicv5_cpuif.ppi_enable[i] & + env->gicv5_cpuif.ppi_pend[i] & + ~env->gicv5_cpuif.ppi_active[i]; + + while (en_pend_nact) { + /* + * When EL3 is supported ICC_PPI_DOMAINR_EL3 tells us + * the domain of each PPI. While we only support EL1, the + * domain is always NS. + */ + GICv5Domain ppi_domain = GICV5_ID_NS; + uint8_t prio; + int ppi; + int bit = ctz64(en_pend_nact); + + en_pend_nact &= ~(1 << bit); + + ppi = i * 64 + bit; + prio = extract64(env->gicv5_cpuif.ppi_priority[ppi / 8], + (ppi & 7) * 8, 5); + + if (prio < env->gicv5_cpuif.ppi_hppi[ppi_domain].prio) { + uint32_t intid = 0; + + intid = FIELD_DP32(intid, INTID, ID, ppi); + intid = FIELD_DP32(intid, INTID, TYPE, GICV5_PPI); + env->gicv5_cpuif.ppi_hppi[ppi_domain].intid = intid; + env->gicv5_cpuif.ppi_hppi[ppi_domain].prio = prio; + } + } + } + + for (int i = 0; i < ARRAY_SIZE(env->gicv5_cpuif.ppi_hppi); i++) { + trace_gicv5_recalc_ppi_hppi(i, + env->gicv5_cpuif.ppi_hppi[i].intid, + env->gicv5_cpuif.ppi_hppi[i].prio); + } +} + static void gic_cddis_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { @@ -200,6 +252,7 @@ static void gic_ppi_cactive_write(CPUARMState *env, const ARMCPRegInfo *ri, { uint64_t old = raw_read(env, ri); raw_write(env, ri, old & ~value); + gic_recalc_ppi_hppi(env); } static void gic_ppi_sactive_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -207,6 +260,7 @@ static void gic_ppi_sactive_write(CPUARMState *env, const ARMCPRegInfo *ri, { uint64_t old = raw_read(env, ri); raw_write(env, ri, old | value); + gic_recalc_ppi_hppi(env); } static void gic_ppi_cpend_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -217,6 +271,7 @@ static void gic_ppi_cpend_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t hm = env->gicv5_cpuif.ppi_hm[ri->opc2 & 1]; value &= ~hm; raw_write(env, ri, old & ~value); + gic_recalc_ppi_hppi(env); } static void gic_ppi_spend_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -227,18 +282,21 @@ static void gic_ppi_spend_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t hm = env->gicv5_cpuif.ppi_hm[ri->opc2 & 1]; value &= ~hm; raw_write(env, ri, old | value); + gic_recalc_ppi_hppi(env); } static void gic_ppi_enable_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { raw_write(env, ri, value); + gic_recalc_ppi_hppi(env); } static void gic_ppi_priority_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { raw_write(env, ri, value); + gic_recalc_ppi_hppi(env); } /* diff --git a/target/arm/tcg/trace-events b/target/arm/tcg/trace-events new file mode 100644 index 0000000000..7dc5f781c5 --- /dev/null +++ b/target/arm/tcg/trace-events @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# See docs/devel/tracing.rst for syntax documentation. + +# gicv5-cpuif.c +gicv5_recalc_ppi_hppi(int domain, uint32_t id, uint8_t prio) "domain %d new PPI HPPI id 0x%x prio %u" diff --git a/target/arm/tcg/trace.h b/target/arm/tcg/trace.h new file mode 100644 index 0000000000..c6e89d018b --- /dev/null +++ b/target/arm/tcg/trace.h @@ -0,0 +1 @@ +#include "trace/trace-target_arm_tcg.h"