hw/intc/arm_gicv3: Implement NMI interrupt priority

If GICD_CTLR_DS bit is zero and the NMI is non-secure, the NMI priority is
higher than 0x80, otherwise it is higher than 0x0. And save the interrupt
non-maskable property in hppi.nmi to deliver NMI exception. Since both GICR
and GICD can deliver NMI, it is both necessary to check whether the pending
irq is NMI in gicv3_redist_update_noirqset and gicv3_update_noirqset.

Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Message-id: 20240407081733.3231820-21-ruanjinjie@huawei.com
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
master
Jinjie Ruan 2024-04-19 14:33:05 +01:00 committed by Peter Maydell
parent d2c0c6aab6
commit d89daa893f
3 changed files with 64 additions and 9 deletions

View File

@ -21,7 +21,7 @@
#include "hw/intc/arm_gicv3.h"
#include "gicv3_internal.h"
static bool irqbetter(GICv3CPUState *cs, int irq, uint8_t prio)
static bool irqbetter(GICv3CPUState *cs, int irq, uint8_t prio, bool nmi)
{
/* Return true if this IRQ at this priority should take
* precedence over the current recorded highest priority
@ -30,14 +30,23 @@ static bool irqbetter(GICv3CPUState *cs, int irq, uint8_t prio)
* is the same as this one (a property which the calling code
* relies on).
*/
if (prio < cs->hppi.prio) {
return true;
if (prio != cs->hppi.prio) {
return prio < cs->hppi.prio;
}
/*
* The same priority IRQ with non-maskable property should signal to
* the CPU as it have the priority higher than the labelled 0x80 or 0x00.
*/
if (nmi != cs->hppi.nmi) {
return nmi;
}
/* If multiple pending interrupts have the same priority then it is an
* IMPDEF choice which of them to signal to the CPU. We choose to
* signal the one with the lowest interrupt number.
*/
if (prio == cs->hppi.prio && irq <= cs->hppi.irq) {
if (irq <= cs->hppi.irq) {
return true;
}
return false;
@ -129,6 +138,40 @@ static uint32_t gicr_int_pending(GICv3CPUState *cs)
return pend;
}
static bool gicv3_get_priority(GICv3CPUState *cs, bool is_redist, int irq,
uint8_t *prio)
{
uint32_t nmi = 0x0;
if (is_redist) {
nmi = extract32(cs->gicr_inmir0, irq, 1);
} else {
nmi = *gic_bmp_ptr32(cs->gic->nmi, irq);
nmi = nmi & (1 << (irq & 0x1f));
}
if (nmi) {
/* DS = 0 & Non-secure NMI */
if (!(cs->gic->gicd_ctlr & GICD_CTLR_DS) &&
((is_redist && extract32(cs->gicr_igroupr0, irq, 1)) ||
(!is_redist && gicv3_gicd_group_test(cs->gic, irq)))) {
*prio = 0x80;
} else {
*prio = 0x0;
}
return true;
}
if (is_redist) {
*prio = cs->gicr_ipriorityr[irq];
} else {
*prio = cs->gic->gicd_ipriority[irq];
}
return false;
}
/* Update the interrupt status after state in a redistributor
* or CPU interface has changed, but don't tell the CPU i/f.
*/
@ -141,6 +184,7 @@ static void gicv3_redist_update_noirqset(GICv3CPUState *cs)
uint8_t prio;
int i;
uint32_t pend;
bool nmi = false;
/* Find out which redistributor interrupts are eligible to be
* signaled to the CPU interface.
@ -152,10 +196,11 @@ static void gicv3_redist_update_noirqset(GICv3CPUState *cs)
if (!(pend & (1 << i))) {
continue;
}
prio = cs->gicr_ipriorityr[i];
if (irqbetter(cs, i, prio)) {
nmi = gicv3_get_priority(cs, true, i, &prio);
if (irqbetter(cs, i, prio, nmi)) {
cs->hppi.irq = i;
cs->hppi.prio = prio;
cs->hppi.nmi = nmi;
seenbetter = true;
}
}
@ -168,9 +213,10 @@ static void gicv3_redist_update_noirqset(GICv3CPUState *cs)
if ((cs->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) && cs->gic->lpi_enable &&
(cs->gic->gicd_ctlr & GICD_CTLR_EN_GRP1NS) &&
(cs->hpplpi.prio != 0xff)) {
if (irqbetter(cs, cs->hpplpi.irq, cs->hpplpi.prio)) {
if (irqbetter(cs, cs->hpplpi.irq, cs->hpplpi.prio, cs->hpplpi.nmi)) {
cs->hppi.irq = cs->hpplpi.irq;
cs->hppi.prio = cs->hpplpi.prio;
cs->hppi.nmi = cs->hpplpi.nmi;
cs->hppi.grp = cs->hpplpi.grp;
seenbetter = true;
}
@ -213,6 +259,7 @@ static void gicv3_update_noirqset(GICv3State *s, int start, int len)
int i;
uint8_t prio;
uint32_t pend = 0;
bool nmi = false;
assert(start >= GIC_INTERNAL);
assert(len > 0);
@ -240,10 +287,11 @@ static void gicv3_update_noirqset(GICv3State *s, int start, int len)
*/
continue;
}
prio = s->gicd_ipriority[i];
if (irqbetter(cs, i, prio)) {
nmi = gicv3_get_priority(cs, false, i, &prio);
if (irqbetter(cs, i, prio, nmi)) {
cs->hppi.irq = i;
cs->hppi.prio = prio;
cs->hppi.nmi = nmi;
cs->seenbetter = true;
}
}
@ -293,6 +341,7 @@ void gicv3_full_update_noirqset(GICv3State *s)
for (i = 0; i < s->num_cpu; i++) {
s->cpu[i].hppi.prio = 0xff;
s->cpu[i].hppi.nmi = false;
}
/* Note that we can guarantee that these functions will not

View File

@ -536,8 +536,11 @@ static void arm_gicv3_common_reset_hold(Object *obj)
memset(cs->gicr_ipriorityr, 0, sizeof(cs->gicr_ipriorityr));
cs->hppi.prio = 0xff;
cs->hppi.nmi = false;
cs->hpplpi.prio = 0xff;
cs->hpplpi.nmi = false;
cs->hppvlpi.prio = 0xff;
cs->hppvlpi.nmi = false;
/* 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.

View File

@ -120,6 +120,7 @@ static void update_for_one_lpi(GICv3CPUState *cs, int irq,
((prio == hpp->prio) && (irq <= hpp->irq))) {
hpp->irq = irq;
hpp->prio = prio;
hpp->nmi = false;
/* LPIs and vLPIs are always non-secure Grp1 interrupts */
hpp->grp = GICV3_G1NS;
}
@ -156,6 +157,7 @@ static void update_for_all_lpis(GICv3CPUState *cs, uint64_t ptbase,
int i, bit;
hpp->prio = 0xff;
hpp->nmi = false;
for (i = GICV3_LPI_INTID_START / 8; i < pendt_size / 8; i++) {
address_space_read(as, ptbase + i, MEMTXATTRS_UNSPECIFIED, &pend, 1);
@ -241,6 +243,7 @@ static void gicv3_redist_update_vlpi_only(GICv3CPUState *cs)
if (!FIELD_EX64(cs->gicr_vpendbaser, GICR_VPENDBASER, VALID)) {
cs->hppvlpi.prio = 0xff;
cs->hppvlpi.nmi = false;
return;
}