aboutsummaryrefslogtreecommitdiff
path: root/hw/intc
diff options
context:
space:
mode:
Diffstat (limited to 'hw/intc')
-rw-r--r--hw/intc/arm_gicv3_common.c1
-rw-r--r--hw/intc/arm_gicv3_cpuif.c119
-rw-r--r--hw/intc/arm_gicv3_redist.c8
-rw-r--r--hw/intc/gicv3_internal.h13
-rw-r--r--hw/intc/trace-events2
5 files changed, 137 insertions, 6 deletions
diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c
index 14d76d7484..3f47b3501f 100644
--- a/hw/intc/arm_gicv3_common.c
+++ b/hw/intc/arm_gicv3_common.c
@@ -487,6 +487,7 @@ static void arm_gicv3_common_reset(DeviceState *dev)
cs->hppi.prio = 0xff;
cs->hpplpi.prio = 0xff;
+ cs->hppvlpi.prio = 0xff;
/* State in the CPU interface must *not* be reset here, because it
* is part of the CPU's reset domain, not the GIC device's.
diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c
index 5fb64d4663..f11863ff61 100644
--- a/hw/intc/arm_gicv3_cpuif.c
+++ b/hw/intc/arm_gicv3_cpuif.c
@@ -21,6 +21,12 @@
#include "hw/irq.h"
#include "cpu.h"
+/*
+ * Special case return value from hppvi_index(); must be larger than
+ * the architecturally maximum possible list register index (which is 15)
+ */
+#define HPPVI_INDEX_VLPI 16
+
static GICv3CPUState *icc_cs_from_env(CPUARMState *env)
{
return env->gicv3state;
@@ -157,10 +163,18 @@ static int ich_highest_active_virt_prio(GICv3CPUState *cs)
static int hppvi_index(GICv3CPUState *cs)
{
- /* Return the list register index of the highest priority pending
+ /*
+ * Return the list register index of the highest priority pending
* virtual interrupt, as per the HighestPriorityVirtualInterrupt
* pseudocode. If no pending virtual interrupts, return -1.
+ * If the highest priority pending virtual interrupt is a vLPI,
+ * return HPPVI_INDEX_VLPI.
+ * (The pseudocode handles checking whether the vLPI is higher
+ * priority than the highest priority list register at every
+ * callsite of HighestPriorityVirtualInterrupt; we check it here.)
*/
+ ARMCPU *cpu = ARM_CPU(cs->cpu);
+ CPUARMState *env = &cpu->env;
int idx = -1;
int i;
/* Note that a list register entry with a priority of 0xff will
@@ -202,6 +216,23 @@ static int hppvi_index(GICv3CPUState *cs)
}
}
+ /*
+ * "no pending vLPI" is indicated with prio = 0xff, which always
+ * fails the priority check here. vLPIs are only considered
+ * when we are in Non-Secure state.
+ */
+ if (cs->hppvlpi.prio < prio && !arm_is_secure(env)) {
+ if (cs->hppvlpi.grp == GICV3_G0) {
+ if (cs->ich_vmcr_el2 & ICH_VMCR_EL2_VENG0) {
+ return HPPVI_INDEX_VLPI;
+ }
+ } else {
+ if (cs->ich_vmcr_el2 & ICH_VMCR_EL2_VENG1) {
+ return HPPVI_INDEX_VLPI;
+ }
+ }
+ }
+
return idx;
}
@@ -289,6 +320,47 @@ static bool icv_hppi_can_preempt(GICv3CPUState *cs, uint64_t lr)
return false;
}
+static bool icv_hppvlpi_can_preempt(GICv3CPUState *cs)
+{
+ /*
+ * Return true if we can signal the highest priority pending vLPI.
+ * We can assume we're Non-secure because hppvi_index() already
+ * tested for that.
+ */
+ uint32_t mask, rprio, vpmr;
+
+ if (!(cs->ich_hcr_el2 & ICH_HCR_EL2_EN)) {
+ /* Virtual interface disabled */
+ return false;
+ }
+
+ vpmr = extract64(cs->ich_vmcr_el2, ICH_VMCR_EL2_VPMR_SHIFT,
+ ICH_VMCR_EL2_VPMR_LENGTH);
+
+ if (cs->hppvlpi.prio >= vpmr) {
+ /* Priority mask masks this interrupt */
+ return false;
+ }
+
+ rprio = ich_highest_active_virt_prio(cs);
+ if (rprio == 0xff) {
+ /* No running interrupt so we can preempt */
+ return true;
+ }
+
+ mask = icv_gprio_mask(cs, cs->hppvlpi.grp);
+
+ /*
+ * We only preempt a running interrupt if the pending interrupt's
+ * group priority is sufficient (the subpriorities are not considered).
+ */
+ if ((cs->hppvlpi.prio & mask) < (rprio & mask)) {
+ return true;
+ }
+
+ return false;
+}
+
static uint32_t eoi_maintenance_interrupt_state(GICv3CPUState *cs,
uint32_t *misr)
{
@@ -386,8 +458,18 @@ void gicv3_cpuif_virt_irq_fiq_update(GICv3CPUState *cs)
int fiqlevel = 0;
idx = hppvi_index(cs);
- trace_gicv3_cpuif_virt_update(gicv3_redist_affid(cs), idx);
- if (idx >= 0) {
+ trace_gicv3_cpuif_virt_update(gicv3_redist_affid(cs), idx,
+ cs->hppvlpi.irq, cs->hppvlpi.grp,
+ cs->hppvlpi.prio);
+ if (idx == HPPVI_INDEX_VLPI) {
+ if (icv_hppvlpi_can_preempt(cs)) {
+ if (cs->hppvlpi.grp == GICV3_G0) {
+ fiqlevel = 1;
+ } else {
+ irqlevel = 1;
+ }
+ }
+ } else if (idx >= 0) {
uint64_t lr = cs->ich_lr_el2[idx];
if (icv_hppi_can_preempt(cs, lr)) {
@@ -619,7 +701,11 @@ static uint64_t icv_hppir_read(CPUARMState *env, const ARMCPRegInfo *ri)
int idx = hppvi_index(cs);
uint64_t value = INTID_SPURIOUS;
- if (idx >= 0) {
+ if (idx == HPPVI_INDEX_VLPI) {
+ if (cs->hppvlpi.grp == grp) {
+ value = cs->hppvlpi.irq;
+ }
+ } else if (idx >= 0) {
uint64_t lr = cs->ich_lr_el2[idx];
int thisgrp = (lr & ICH_LR_EL2_GROUP) ? GICV3_G1NS : GICV3_G0;
@@ -650,6 +736,18 @@ static void icv_activate_irq(GICv3CPUState *cs, int idx, int grp)
cs->ich_apr[grp][regno] |= (1 << regbit);
}
+static void icv_activate_vlpi(GICv3CPUState *cs)
+{
+ uint32_t mask = icv_gprio_mask(cs, cs->hppvlpi.grp);
+ int prio = cs->hppvlpi.prio & mask;
+ int aprbit = prio >> (8 - cs->vprebits);
+ int regno = aprbit / 32;
+ int regbit = aprbit % 32;
+
+ cs->ich_apr[cs->hppvlpi.grp][regno] |= (1 << regbit);
+ gicv3_redist_vlpi_pending(cs, cs->hppvlpi.irq, 0);
+}
+
static uint64_t icv_iar_read(CPUARMState *env, const ARMCPRegInfo *ri)
{
GICv3CPUState *cs = icc_cs_from_env(env);
@@ -657,7 +755,12 @@ static uint64_t icv_iar_read(CPUARMState *env, const ARMCPRegInfo *ri)
int idx = hppvi_index(cs);
uint64_t intid = INTID_SPURIOUS;
- if (idx >= 0) {
+ if (idx == HPPVI_INDEX_VLPI) {
+ if (cs->hppvlpi.grp == grp && icv_hppvlpi_can_preempt(cs)) {
+ intid = cs->hppvlpi.irq;
+ icv_activate_vlpi(cs);
+ }
+ } else if (idx >= 0) {
uint64_t lr = cs->ich_lr_el2[idx];
int thisgrp = (lr & ICH_LR_EL2_GROUP) ? GICV3_G1NS : GICV3_G0;
@@ -2632,6 +2735,12 @@ static void gicv3_cpuif_el_change_hook(ARMCPU *cpu, void *opaque)
GICv3CPUState *cs = opaque;
gicv3_cpuif_update(cs);
+ /*
+ * Because vLPIs are only pending in NonSecure state,
+ * an EL change can change the VIRQ/VFIQ status (but
+ * cannot affect the maintenance interrupt state)
+ */
+ gicv3_cpuif_virt_irq_fiq_update(cs);
}
void gicv3_init_cpuif(GICv3State *s)
diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c
index c310d7f8ff..3464972c13 100644
--- a/hw/intc/arm_gicv3_redist.c
+++ b/hw/intc/arm_gicv3_redist.c
@@ -855,6 +855,14 @@ void gicv3_redist_movall_lpis(GICv3CPUState *src, GICv3CPUState *dest)
gicv3_redist_update_lpi(dest);
}
+void gicv3_redist_vlpi_pending(GICv3CPUState *cs, int irq, int level)
+{
+ /*
+ * The redistributor handling for changing the pending state
+ * of a vLPI will be added in a subsequent commit.
+ */
+}
+
void gicv3_redist_process_vlpi(GICv3CPUState *cs, int irq, uint64_t vptaddr,
int doorbell, int level)
{
diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
index 052aa96c77..2a9d3cf974 100644
--- a/hw/intc/gicv3_internal.h
+++ b/hw/intc/gicv3_internal.h
@@ -612,6 +612,19 @@ void gicv3_redist_process_lpi(GICv3CPUState *cs, int irq, int level);
*/
void gicv3_redist_process_vlpi(GICv3CPUState *cs, int irq, uint64_t vptaddr,
int doorbell, int level);
+/**
+ * gicv3_redist_vlpi_pending:
+ * @cs: GICv3CPUState
+ * @irq: (virtual) interrupt number
+ * @level: level to set @irq to
+ *
+ * Set/clear the pending status of a virtual LPI in the vLPI table
+ * that this redistributor is currently using. (The difference between
+ * this and gicv3_redist_process_vlpi() is that this is called from
+ * the cpuif and does not need to do the not-running-on-this-vcpu checks.)
+ */
+void gicv3_redist_vlpi_pending(GICv3CPUState *cs, int irq, int level);
+
void gicv3_redist_lpi_pending(GICv3CPUState *cs, int irq, int level);
/**
* gicv3_redist_update_lpi:
diff --git a/hw/intc/trace-events b/hw/intc/trace-events
index 36c3fe4da0..5271590304 100644
--- a/hw/intc/trace-events
+++ b/hw/intc/trace-events
@@ -151,7 +151,7 @@ gicv3_icv_hppir_read(int grp, uint32_t cpu, uint64_t val) "GICv3 ICV_HPPIR%d rea
gicv3_icv_dir_write(uint32_t cpu, uint64_t val) "GICv3 ICV_DIR write cpu 0x%x value 0x%" PRIx64
gicv3_icv_iar_read(int grp, uint32_t cpu, uint64_t val) "GICv3 ICV_IAR%d read cpu 0x%x value 0x%" PRIx64
gicv3_icv_eoir_write(int grp, uint32_t cpu, uint64_t val) "GICv3 ICV_EOIR%d write cpu 0x%x value 0x%" PRIx64
-gicv3_cpuif_virt_update(uint32_t cpuid, int idx) "GICv3 CPU i/f 0x%x virt HPPI update LR index %d"
+gicv3_cpuif_virt_update(uint32_t cpuid, int idx, int hppvlpi, int grp, int prio) "GICv3 CPU i/f 0x%x virt HPPI update LR index %d HPPVLPI %d grp %d prio %d"
gicv3_cpuif_virt_set_irqs(uint32_t cpuid, int fiqlevel, int irqlevel) "GICv3 CPU i/f 0x%x virt HPPI update: setting FIQ %d IRQ %d"
gicv3_cpuif_virt_set_maint_irq(uint32_t cpuid, int maintlevel) "GICv3 CPU i/f 0x%x virt HPPI update: setting maintenance-irq %d"