|
|
|
@ -44,6 +44,21 @@ static inline int icv_min_vbpr(GICv3CPUState *cs) |
|
|
|
} |
|
|
|
|
|
|
|
/* Simple accessor functions for LR fields */ |
|
|
|
static uint32_t ich_lr_vintid(uint64_t lr) |
|
|
|
{ |
|
|
|
return extract64(lr, ICH_LR_EL2_VINTID_SHIFT, ICH_LR_EL2_VINTID_LENGTH); |
|
|
|
} |
|
|
|
|
|
|
|
static uint32_t ich_lr_pintid(uint64_t lr) |
|
|
|
{ |
|
|
|
return extract64(lr, ICH_LR_EL2_PINTID_SHIFT, ICH_LR_EL2_PINTID_LENGTH); |
|
|
|
} |
|
|
|
|
|
|
|
static uint32_t ich_lr_prio(uint64_t lr) |
|
|
|
{ |
|
|
|
return extract64(lr, ICH_LR_EL2_PRIORITY_SHIFT, ICH_LR_EL2_PRIORITY_LENGTH); |
|
|
|
} |
|
|
|
|
|
|
|
static int ich_lr_state(uint64_t lr) |
|
|
|
{ |
|
|
|
return extract64(lr, ICH_LR_EL2_STATE_SHIFT, ICH_LR_EL2_STATE_LENGTH); |
|
|
|
@ -115,6 +130,79 @@ static uint32_t icv_fullprio_mask(GICv3CPUState *cs) |
|
|
|
return ~0U << (8 - cs->vpribits); |
|
|
|
} |
|
|
|
|
|
|
|
static int ich_highest_active_virt_prio(GICv3CPUState *cs) |
|
|
|
{ |
|
|
|
/* Calculate the current running priority based on the set bits
|
|
|
|
* in the ICH Active Priority Registers. |
|
|
|
*/ |
|
|
|
int i; |
|
|
|
int aprmax = 1 << (cs->vprebits - 5); |
|
|
|
|
|
|
|
assert(aprmax <= ARRAY_SIZE(cs->ich_apr[0])); |
|
|
|
|
|
|
|
for (i = 0; i < aprmax; i++) { |
|
|
|
uint32_t apr = cs->ich_apr[GICV3_G0][i] | |
|
|
|
cs->ich_apr[GICV3_G1NS][i]; |
|
|
|
|
|
|
|
if (!apr) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
return (i * 32 + ctz32(apr)) << (icv_min_vbpr(cs) + 1); |
|
|
|
} |
|
|
|
/* No current active interrupts: return idle priority */ |
|
|
|
return 0xff; |
|
|
|
} |
|
|
|
|
|
|
|
static int hppvi_index(GICv3CPUState *cs) |
|
|
|
{ |
|
|
|
/* Return the list register index of the highest priority pending
|
|
|
|
* virtual interrupt, as per the HighestPriorityVirtualInterrupt |
|
|
|
* pseudocode. If no pending virtual interrupts, return -1. |
|
|
|
*/ |
|
|
|
int idx = -1; |
|
|
|
int i; |
|
|
|
/* Note that a list register entry with a priority of 0xff will
|
|
|
|
* never be reported by this function; this is the architecturally |
|
|
|
* correct behaviour. |
|
|
|
*/ |
|
|
|
int prio = 0xff; |
|
|
|
|
|
|
|
if (!(cs->ich_vmcr_el2 & (ICH_VMCR_EL2_VENG0 | ICH_VMCR_EL2_VENG1))) { |
|
|
|
/* Both groups disabled, definitely nothing to do */ |
|
|
|
return idx; |
|
|
|
} |
|
|
|
|
|
|
|
for (i = 0; i < cs->num_list_regs; i++) { |
|
|
|
uint64_t lr = cs->ich_lr_el2[i]; |
|
|
|
int thisprio; |
|
|
|
|
|
|
|
if (ich_lr_state(lr) != ICH_LR_EL2_STATE_PENDING) { |
|
|
|
/* Not Pending */ |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
/* Ignore interrupts if relevant group enable not set */ |
|
|
|
if (lr & ICH_LR_EL2_GROUP) { |
|
|
|
if (!(cs->ich_vmcr_el2 & ICH_VMCR_EL2_VENG1)) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
} else { |
|
|
|
if (!(cs->ich_vmcr_el2 & ICH_VMCR_EL2_VENG0)) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
thisprio = ich_lr_prio(lr); |
|
|
|
|
|
|
|
if (thisprio < prio) { |
|
|
|
prio = thisprio; |
|
|
|
idx = i; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return idx; |
|
|
|
} |
|
|
|
|
|
|
|
static uint32_t eoi_maintenance_interrupt_state(GICv3CPUState *cs, |
|
|
|
uint32_t *misr) |
|
|
|
{ |
|
|
|
@ -363,6 +451,35 @@ static void icv_ctlr_write(CPUARMState *env, const ARMCPRegInfo *ri, |
|
|
|
gicv3_cpuif_virt_update(cs); |
|
|
|
} |
|
|
|
|
|
|
|
static uint64_t icv_rpr_read(CPUARMState *env, const ARMCPRegInfo *ri) |
|
|
|
{ |
|
|
|
GICv3CPUState *cs = icc_cs_from_env(env); |
|
|
|
int prio = ich_highest_active_virt_prio(cs); |
|
|
|
|
|
|
|
trace_gicv3_icv_rpr_read(gicv3_redist_affid(cs), prio); |
|
|
|
return prio; |
|
|
|
} |
|
|
|
|
|
|
|
static uint64_t icv_hppir_read(CPUARMState *env, const ARMCPRegInfo *ri) |
|
|
|
{ |
|
|
|
GICv3CPUState *cs = icc_cs_from_env(env); |
|
|
|
int grp = ri->crm == 8 ? GICV3_G0 : GICV3_G1NS; |
|
|
|
int idx = hppvi_index(cs); |
|
|
|
uint64_t value = INTID_SPURIOUS; |
|
|
|
|
|
|
|
if (idx >= 0) { |
|
|
|
uint64_t lr = cs->ich_lr_el2[idx]; |
|
|
|
int thisgrp = (lr & ICH_LR_EL2_GROUP) ? GICV3_G1NS : GICV3_G0; |
|
|
|
|
|
|
|
if (grp == thisgrp) { |
|
|
|
value = ich_lr_vintid(lr); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
trace_gicv3_icv_hppir_read(grp, gicv3_redist_affid(cs), value); |
|
|
|
return value; |
|
|
|
} |
|
|
|
|
|
|
|
static int icc_highest_active_prio(GICv3CPUState *cs) |
|
|
|
{ |
|
|
|
/* Calculate the current running priority based on the set bits
|
|
|
|
@ -781,6 +898,97 @@ static void icc_deactivate_irq(GICv3CPUState *cs, int irq) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static bool icv_eoi_split(CPUARMState *env, GICv3CPUState *cs) |
|
|
|
{ |
|
|
|
/* Return true if we should split priority drop and interrupt
|
|
|
|
* deactivation, ie whether the virtual EOIMode bit is set. |
|
|
|
*/ |
|
|
|
return cs->ich_vmcr_el2 & ICH_VMCR_EL2_VEOIM; |
|
|
|
} |
|
|
|
|
|
|
|
static int icv_find_active(GICv3CPUState *cs, int irq) |
|
|
|
{ |
|
|
|
/* Given an interrupt number for an active interrupt, return the index
|
|
|
|
* of the corresponding list register, or -1 if there is no match. |
|
|
|
* Corresponds to FindActiveVirtualInterrupt pseudocode. |
|
|
|
*/ |
|
|
|
int i; |
|
|
|
|
|
|
|
for (i = 0; i < cs->num_list_regs; i++) { |
|
|
|
uint64_t lr = cs->ich_lr_el2[i]; |
|
|
|
|
|
|
|
if ((lr & ICH_LR_EL2_STATE_ACTIVE_BIT) && ich_lr_vintid(lr) == irq) { |
|
|
|
return i; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
static void icv_deactivate_irq(GICv3CPUState *cs, int idx) |
|
|
|
{ |
|
|
|
/* Deactivate the interrupt in the specified list register index */ |
|
|
|
uint64_t lr = cs->ich_lr_el2[idx]; |
|
|
|
|
|
|
|
if (lr & ICH_LR_EL2_HW) { |
|
|
|
/* Deactivate the associated physical interrupt */ |
|
|
|
int pirq = ich_lr_pintid(lr); |
|
|
|
|
|
|
|
if (pirq < INTID_SECURE) { |
|
|
|
icc_deactivate_irq(cs, pirq); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/* Clear the 'active' part of the state, so ActivePending->Pending
|
|
|
|
* and Active->Invalid. |
|
|
|
*/ |
|
|
|
lr &= ~ICH_LR_EL2_STATE_ACTIVE_BIT; |
|
|
|
cs->ich_lr_el2[idx] = lr; |
|
|
|
} |
|
|
|
|
|
|
|
static void icv_increment_eoicount(GICv3CPUState *cs) |
|
|
|
{ |
|
|
|
/* Increment the EOICOUNT field in ICH_HCR_EL2 */ |
|
|
|
int eoicount = extract64(cs->ich_hcr_el2, ICH_HCR_EL2_EOICOUNT_SHIFT, |
|
|
|
ICH_HCR_EL2_EOICOUNT_LENGTH); |
|
|
|
|
|
|
|
cs->ich_hcr_el2 = deposit64(cs->ich_hcr_el2, ICH_HCR_EL2_EOICOUNT_SHIFT, |
|
|
|
ICH_HCR_EL2_EOICOUNT_LENGTH, eoicount + 1); |
|
|
|
} |
|
|
|
|
|
|
|
static void icv_dir_write(CPUARMState *env, const ARMCPRegInfo *ri, |
|
|
|
uint64_t value) |
|
|
|
{ |
|
|
|
/* Deactivate interrupt */ |
|
|
|
GICv3CPUState *cs = icc_cs_from_env(env); |
|
|
|
int idx; |
|
|
|
int irq = value & 0xffffff; |
|
|
|
|
|
|
|
trace_gicv3_icv_dir_write(gicv3_redist_affid(cs), value); |
|
|
|
|
|
|
|
if (irq >= cs->gic->num_irq) { |
|
|
|
/* Also catches special interrupt numbers and LPIs */ |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (!icv_eoi_split(env, cs)) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
idx = icv_find_active(cs, irq); |
|
|
|
|
|
|
|
if (idx < 0) { |
|
|
|
/* No list register matching this, so increment the EOI count
|
|
|
|
* (might trigger a maintenance interrupt) |
|
|
|
*/ |
|
|
|
icv_increment_eoicount(cs); |
|
|
|
} else { |
|
|
|
icv_deactivate_irq(cs, idx); |
|
|
|
} |
|
|
|
|
|
|
|
gicv3_cpuif_virt_update(cs); |
|
|
|
} |
|
|
|
|
|
|
|
static void icc_eoir_write(CPUARMState *env, const ARMCPRegInfo *ri, |
|
|
|
uint64_t value) |
|
|
|
{ |
|
|
|
@ -831,8 +1039,13 @@ static void icc_eoir_write(CPUARMState *env, const ARMCPRegInfo *ri, |
|
|
|
static uint64_t icc_hppir0_read(CPUARMState *env, const ARMCPRegInfo *ri) |
|
|
|
{ |
|
|
|
GICv3CPUState *cs = icc_cs_from_env(env); |
|
|
|
uint64_t value = icc_hppir0_value(cs, env); |
|
|
|
uint64_t value; |
|
|
|
|
|
|
|
if (icv_access(env, HCR_FMO)) { |
|
|
|
return icv_hppir_read(env, ri); |
|
|
|
} |
|
|
|
|
|
|
|
value = icc_hppir0_value(cs, env); |
|
|
|
trace_gicv3_icc_hppir0_read(gicv3_redist_affid(cs), value); |
|
|
|
return value; |
|
|
|
} |
|
|
|
@ -840,8 +1053,13 @@ static uint64_t icc_hppir0_read(CPUARMState *env, const ARMCPRegInfo *ri) |
|
|
|
static uint64_t icc_hppir1_read(CPUARMState *env, const ARMCPRegInfo *ri) |
|
|
|
{ |
|
|
|
GICv3CPUState *cs = icc_cs_from_env(env); |
|
|
|
uint64_t value = icc_hppir1_value(cs, env); |
|
|
|
uint64_t value; |
|
|
|
|
|
|
|
if (icv_access(env, HCR_IMO)) { |
|
|
|
return icv_hppir_read(env, ri); |
|
|
|
} |
|
|
|
|
|
|
|
value = icc_hppir1_value(cs, env); |
|
|
|
trace_gicv3_icc_hppir1_read(gicv3_redist_affid(cs), value); |
|
|
|
return value; |
|
|
|
} |
|
|
|
@ -986,6 +1204,11 @@ static void icc_dir_write(CPUARMState *env, const ARMCPRegInfo *ri, |
|
|
|
bool irq_is_secure, single_sec_state, irq_is_grp0; |
|
|
|
bool route_fiq_to_el3, route_irq_to_el3, route_fiq_to_el2, route_irq_to_el2; |
|
|
|
|
|
|
|
if (icv_access(env, HCR_FMO | HCR_IMO)) { |
|
|
|
icv_dir_write(env, ri, value); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
trace_gicv3_icc_dir_write(gicv3_redist_affid(cs), value); |
|
|
|
|
|
|
|
if (irq >= cs->gic->num_irq) { |
|
|
|
@ -1057,7 +1280,13 @@ static void icc_dir_write(CPUARMState *env, const ARMCPRegInfo *ri, |
|
|
|
static uint64_t icc_rpr_read(CPUARMState *env, const ARMCPRegInfo *ri) |
|
|
|
{ |
|
|
|
GICv3CPUState *cs = icc_cs_from_env(env); |
|
|
|
int prio = icc_highest_active_prio(cs); |
|
|
|
int prio; |
|
|
|
|
|
|
|
if (icv_access(env, HCR_FMO | HCR_IMO)) { |
|
|
|
return icv_rpr_read(env, ri); |
|
|
|
} |
|
|
|
|
|
|
|
prio = icc_highest_active_prio(cs); |
|
|
|
|
|
|
|
if (arm_feature(env, ARM_FEATURE_EL3) && |
|
|
|
!arm_is_secure(env) && (env->cp15.scr_el3 & SCR_FIQ)) { |
|
|
|
|