X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/b3b48f529fe8dad6c28bd25ec4a4a7ee7a0b0dcd..3408d5aee0c078074ba547ca5025d350cbaf64b0:/hw/intc/arm_gicv3_cpuif.c diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index f3845a6614..5cbafaf497 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -14,10 +14,19 @@ #include "qemu/osdep.h" #include "qemu/bitops.h" +#include "qemu/main-loop.h" #include "trace.h" #include "gicv3_internal.h" #include "cpu.h" +void gicv3_set_gicv3state(CPUState *cpu, GICv3CPUState *s) +{ + ARMCPU *arm_cpu = ARM_CPU(cpu); + CPUARMState *env = &arm_cpu->env; + + env->gicv3state = (void *)s; +}; + static GICv3CPUState *icc_cs_from_env(CPUARMState *env) { /* Given the CPU, find the right GICv3CPUState struct. @@ -207,18 +216,35 @@ static uint32_t icv_gprio_mask(GICv3CPUState *cs, int group) { /* Return a mask word which clears the subpriority bits from * a priority value for a virtual interrupt in the specified group. - * This depends on the VBPR value: + * This depends on the VBPR value. + * If using VBPR0 then: * a BPR of 0 means the group priority bits are [7:1]; * a BPR of 1 means they are [7:2], and so on down to * a BPR of 7 meaning no group priority bits at all. + * If using VBPR1 then: + * a BPR of 0 is impossible (the minimum value is 1) + * a BPR of 1 means the group priority bits are [7:1]; + * a BPR of 2 means they are [7:2], and so on down to + * a BPR of 7 meaning the group priority is [7]. + * * Which BPR to use depends on the group of the interrupt and * the current ICH_VMCR_EL2.VCBPR settings. + * + * This corresponds to the VGroupBits() pseudocode. */ + int bpr; + if (group == GICV3_G1NS && cs->ich_vmcr_el2 & ICH_VMCR_EL2_VCBPR) { group = GICV3_G0; } - return ~0U << (read_vbpr(cs, group) + 1); + bpr = read_vbpr(cs, group); + if (group == GICV3_G1NS) { + assert(bpr > 0); + bpr--; + } + + return ~0U << (bpr + 1); } static bool icv_hppi_can_preempt(GICv3CPUState *cs, uint64_t lr) @@ -352,6 +378,53 @@ static uint32_t maintenance_interrupt_state(GICv3CPUState *cs) static void gicv3_cpuif_virt_update(GICv3CPUState *cs) { + /* Tell the CPU about any pending virtual interrupts or + * maintenance interrupts, following a change to the state + * of the CPU interface relevant to virtual interrupts. + * + * CAUTION: this function will call qemu_set_irq() on the + * CPU maintenance IRQ line, which is typically wired up + * to the GIC as a per-CPU interrupt. This means that it + * will recursively call back into the GIC code via + * gicv3_redist_set_irq() and thus into the CPU interface code's + * gicv3_cpuif_update(). It is therefore important that this + * function is only called as the final action of a CPU interface + * register write implementation, after all the GIC state + * fields have been updated. gicv3_cpuif_update() also must + * not cause this function to be called, but that happens + * naturally as a result of there being no architectural + * linkage between the physical and virtual GIC logic. + */ + int idx; + int irqlevel = 0; + int fiqlevel = 0; + int maintlevel = 0; + + idx = hppvi_index(cs); + trace_gicv3_cpuif_virt_update(gicv3_redist_affid(cs), idx); + if (idx >= 0) { + uint64_t lr = cs->ich_lr_el2[idx]; + + if (icv_hppi_can_preempt(cs, lr)) { + /* Virtual interrupts are simple: G0 are always FIQ, and G1 IRQ */ + if (lr & ICH_LR_EL2_GROUP) { + irqlevel = 1; + } else { + fiqlevel = 1; + } + } + } + + if (cs->ich_hcr_el2 & ICH_HCR_EL2_EN) { + maintlevel = maintenance_interrupt_state(cs); + } + + trace_gicv3_cpuif_virt_set_irqs(gicv3_redist_affid(cs), fiqlevel, + irqlevel, maintlevel); + + qemu_set_irq(cs->parent_vfiq, fiqlevel); + qemu_set_irq(cs->parent_virq, irqlevel); + qemu_set_irq(cs->maintenance_irq, maintlevel); } static uint64_t icv_ap_read(CPUARMState *env, const ARMCPRegInfo *ri) @@ -618,20 +691,37 @@ static uint32_t icc_gprio_mask(GICv3CPUState *cs, int group) { /* Return a mask word which clears the subpriority bits from * a priority value for an interrupt in the specified group. - * This depends on the BPR value: + * This depends on the BPR value. For CBPR0 (S or NS): * a BPR of 0 means the group priority bits are [7:1]; * a BPR of 1 means they are [7:2], and so on down to * a BPR of 7 meaning no group priority bits at all. + * For CBPR1 NS: + * a BPR of 0 is impossible (the minimum value is 1) + * a BPR of 1 means the group priority bits are [7:1]; + * a BPR of 2 means they are [7:2], and so on down to + * a BPR of 7 meaning the group priority is [7]. + * * Which BPR to use depends on the group of the interrupt and * the current ICC_CTLR.CBPR settings. + * + * This corresponds to the GroupBits() pseudocode. */ + int bpr; + if ((group == GICV3_G1 && cs->icc_ctlr_el1[GICV3_S] & ICC_CTLR_EL1_CBPR) || (group == GICV3_G1NS && cs->icc_ctlr_el1[GICV3_NS] & ICC_CTLR_EL1_CBPR)) { group = GICV3_G0; } - return ~0U << ((cs->icc_bpr[group] & 7) + 1); + bpr = cs->icc_bpr[group] & 7; + + if (group == GICV3_G1NS) { + assert(bpr > 0); + bpr--; + } + + return ~0U << (bpr + 1); } static bool icc_no_enabled_hppi(GICv3CPUState *cs) @@ -686,6 +776,8 @@ void gicv3_cpuif_update(GICv3CPUState *cs) ARMCPU *cpu = ARM_CPU(cs->cpu); CPUARMState *env = &cpu->env; + g_assert(qemu_mutex_iothread_locked()); + trace_gicv3_cpuif_update(gicv3_redist_affid(cs), cs->hppi.irq, cs->hppi.grp, cs->hppi.prio); @@ -1330,6 +1422,7 @@ static void icc_bpr_write(CPUARMState *env, const ARMCPRegInfo *ri, { GICv3CPUState *cs = icc_cs_from_env(env); int grp = (ri->crm == 8) ? GICV3_G0 : GICV3_G1; + uint64_t minval; if (icv_access(env, grp == GICV3_G0 ? HCR_FMO : HCR_IMO)) { icv_bpr_write(env, ri, value); @@ -1357,6 +1450,11 @@ static void icc_bpr_write(CPUARMState *env, const ARMCPRegInfo *ri, return; } + minval = (grp == GICV3_G1NS) ? GIC_MIN_BPR_NS : GIC_MIN_BPR; + if (value < minval) { + value = minval; + } + cs->icc_bpr[grp] = value & 7; gicv3_cpuif_update(cs); } @@ -1786,9 +1884,17 @@ static CPAccessResult gicv3_irqfiq_access(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { CPAccessResult r = CP_ACCESS_OK; + GICv3CPUState *cs = icc_cs_from_env(env); + int el = arm_current_el(env); + + if ((cs->ich_hcr_el2 & ICH_HCR_EL2_TC) && + el == 1 && !arm_is_secure_below_el3(env)) { + /* Takes priority over a possible EL3 trap */ + return CP_ACCESS_TRAP_EL2; + } if ((env->cp15.scr_el3 & (SCR_FIQ | SCR_IRQ)) == (SCR_FIQ | SCR_IRQ)) { - switch (arm_current_el(env)) { + switch (el) { case 1: if (arm_is_secure_below_el3(env) || ((env->cp15.hcr_el2 & (HCR_IMO | HCR_FMO)) == 0)) { @@ -1814,13 +1920,47 @@ static CPAccessResult gicv3_irqfiq_access(CPUARMState *env, return r; } +static CPAccessResult gicv3_dir_access(CPUARMState *env, + const ARMCPRegInfo *ri, bool isread) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + + if ((cs->ich_hcr_el2 & ICH_HCR_EL2_TDIR) && + arm_current_el(env) == 1 && !arm_is_secure_below_el3(env)) { + /* Takes priority over a possible EL3 trap */ + return CP_ACCESS_TRAP_EL2; + } + + return gicv3_irqfiq_access(env, ri, isread); +} + +static CPAccessResult gicv3_sgi_access(CPUARMState *env, + const ARMCPRegInfo *ri, bool isread) +{ + if ((env->cp15.hcr_el2 & (HCR_IMO | HCR_FMO)) && + arm_current_el(env) == 1 && !arm_is_secure_below_el3(env)) { + /* Takes priority over a possible EL3 trap */ + return CP_ACCESS_TRAP_EL2; + } + + return gicv3_irqfiq_access(env, ri, isread); +} + static CPAccessResult gicv3_fiq_access(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { CPAccessResult r = CP_ACCESS_OK; + GICv3CPUState *cs = icc_cs_from_env(env); + int el = arm_current_el(env); + + if ((cs->ich_hcr_el2 & ICH_HCR_EL2_TALL0) && + el == 1 && !arm_is_secure_below_el3(env)) { + /* Takes priority over a possible EL3 trap */ + return CP_ACCESS_TRAP_EL2; + } if (env->cp15.scr_el3 & SCR_FIQ) { - switch (arm_current_el(env)) { + switch (el) { case 1: if (arm_is_secure_below_el3(env) || ((env->cp15.hcr_el2 & HCR_FMO) == 0)) { @@ -1850,9 +1990,17 @@ static CPAccessResult gicv3_irq_access(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { CPAccessResult r = CP_ACCESS_OK; + GICv3CPUState *cs = icc_cs_from_env(env); + int el = arm_current_el(env); + + if ((cs->ich_hcr_el2 & ICH_HCR_EL2_TALL1) && + el == 1 && !arm_is_secure_below_el3(env)) { + /* Takes priority over a possible EL3 trap */ + return CP_ACCESS_TRAP_EL2; + } if (env->cp15.scr_el3 & SCR_IRQ) { - switch (arm_current_el(env)) { + switch (el) { case 1: if (arm_is_secure_below_el3(env) || ((env->cp15.hcr_el2 & HCR_IMO) == 0)) { @@ -1891,11 +2039,7 @@ static void icc_reset(CPUARMState *env, const ARMCPRegInfo *ri) cs->icc_pmr_el1 = 0; cs->icc_bpr[GICV3_G0] = GIC_MIN_BPR; cs->icc_bpr[GICV3_G1] = GIC_MIN_BPR; - if (arm_feature(env, ARM_FEATURE_EL3)) { - cs->icc_bpr[GICV3_G1NS] = GIC_MIN_BPR_NS; - } else { - cs->icc_bpr[GICV3_G1NS] = GIC_MIN_BPR; - } + cs->icc_bpr[GICV3_G1NS] = GIC_MIN_BPR_NS; memset(cs->icc_apr, 0, sizeof(cs->icc_apr)); memset(cs->icc_igrpen, 0, sizeof(cs->icc_igrpen)); cs->icc_ctlr_el3 = ICC_CTLR_EL3_NDS | ICC_CTLR_EL3_A3V | @@ -1906,7 +2050,7 @@ static void icc_reset(CPUARMState *env, const ARMCPRegInfo *ri) cs->ich_hcr_el2 = 0; memset(cs->ich_lr_el2, 0, sizeof(cs->ich_lr_el2)); cs->ich_vmcr_el2 = ICH_VMCR_EL2_VFIQEN | - (icv_min_vbpr(cs) << ICH_VMCR_EL2_VBPR1_SHIFT) | + ((icv_min_vbpr(cs) + 1) << ICH_VMCR_EL2_VBPR1_SHIFT) | (icv_min_vbpr(cs) << ICH_VMCR_EL2_VBPR0_SHIFT); } @@ -2008,7 +2152,7 @@ static const ARMCPRegInfo gicv3_cpuif_reginfo[] = { { .name = "ICC_DIR_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 11, .opc2 = 1, .type = ARM_CP_IO | ARM_CP_NO_RAW, - .access = PL1_W, .accessfn = gicv3_irqfiq_access, + .access = PL1_W, .accessfn = gicv3_dir_access, .writefn = icc_dir_write, }, { .name = "ICC_RPR_EL1", .state = ARM_CP_STATE_BOTH, @@ -2020,37 +2164,37 @@ static const ARMCPRegInfo gicv3_cpuif_reginfo[] = { { .name = "ICC_SGI1R_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 11, .opc2 = 5, .type = ARM_CP_IO | ARM_CP_NO_RAW, - .access = PL1_W, .accessfn = gicv3_irqfiq_access, + .access = PL1_W, .accessfn = gicv3_sgi_access, .writefn = icc_sgi1r_write, }, { .name = "ICC_SGI1R", .cp = 15, .opc1 = 0, .crm = 12, .type = ARM_CP_64BIT | ARM_CP_IO | ARM_CP_NO_RAW, - .access = PL1_W, .accessfn = gicv3_irqfiq_access, + .access = PL1_W, .accessfn = gicv3_sgi_access, .writefn = icc_sgi1r_write, }, { .name = "ICC_ASGI1R_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 11, .opc2 = 6, .type = ARM_CP_IO | ARM_CP_NO_RAW, - .access = PL1_W, .accessfn = gicv3_irqfiq_access, + .access = PL1_W, .accessfn = gicv3_sgi_access, .writefn = icc_asgi1r_write, }, { .name = "ICC_ASGI1R", .cp = 15, .opc1 = 1, .crm = 12, .type = ARM_CP_64BIT | ARM_CP_IO | ARM_CP_NO_RAW, - .access = PL1_W, .accessfn = gicv3_irqfiq_access, + .access = PL1_W, .accessfn = gicv3_sgi_access, .writefn = icc_asgi1r_write, }, { .name = "ICC_SGI0R_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 11, .opc2 = 7, .type = ARM_CP_IO | ARM_CP_NO_RAW, - .access = PL1_W, .accessfn = gicv3_irqfiq_access, + .access = PL1_W, .accessfn = gicv3_sgi_access, .writefn = icc_sgi0r_write, }, { .name = "ICC_SGI0R", .cp = 15, .opc1 = 2, .crm = 12, .type = ARM_CP_64BIT | ARM_CP_IO | ARM_CP_NO_RAW, - .access = PL1_W, .accessfn = gicv3_irqfiq_access, + .access = PL1_W, .accessfn = gicv3_sgi_access, .writefn = icc_sgi0r_write, }, { .name = "ICC_IAR1_EL1", .state = ARM_CP_STATE_BOTH, @@ -2333,7 +2477,7 @@ static uint64_t ich_elrsr_read(CPUARMState *env, const ARMCPRegInfo *ri) uint64_t lr = cs->ich_lr_el2[i]; if ((lr & ICH_LR_EL2_STATE_MASK) == 0 && - ((lr & ICH_LR_EL2_HW) == 1 || (lr & ICH_LR_EL2_EOI) == 0)) { + ((lr & ICH_LR_EL2_HW) != 0 || (lr & ICH_LR_EL2_EOI) == 0)) { value |= (1 << i); } } @@ -2480,6 +2624,8 @@ void gicv3_init_cpuif(GICv3State *s) && cpu->gic_num_lrs) { int j; + cs->maintenance_irq = cpu->gicv3_maintenance_interrupt; + cs->num_list_regs = cpu->gic_num_lrs; cs->vpribits = cpu->gic_vpribits; cs->vprebits = cpu->gic_vprebits;