From: Michael Clark <m...@sifive.com> We can't allow the supervisor to control SEIP as this would allow the supervisor to clear a pending external interrupt which will result in lost a interrupt in the case a PLIC is attached. The SEIP bit must be hardware controlled when a PLIC is attached.
This logic was previously hard-coded so SEIP was always masked even if no PLIC was attached. This patch adds riscv_cpu_claim_interrupts so that the PLIC can register control of SEIP. In the case of models without a PLIC (spike), the SEIP bit remains software controlled. This interface allows for hardware control of supervisor timer and software interrupts by other interrupt controller models. Cc: Palmer Dabbelt <pal...@sifive.com> Cc: Sagar Karandikar <sag...@eecs.berkeley.edu> Cc: Bastian Koppelmann <kbast...@mail.uni-paderborn.de> Cc: Alistair Francis <alistair.fran...@wdc.com> Signed-off-by: Michael Clark <m...@sifive.com> Signed-off-by: Alistair Francis <alistair.fran...@wdc.com> Signed-off-by: Palmer Dabbelt <pal...@sifive.com> --- hw/riscv/sifive_plic.c | 15 +++++++++++++++ target/riscv/cpu.h | 2 ++ target/riscv/cpu_helper.c | 11 +++++++++++ target/riscv/csr.c | 10 ++-------- 4 files changed, 30 insertions(+), 8 deletions(-) diff --git a/hw/riscv/sifive_plic.c b/hw/riscv/sifive_plic.c index b859f919a71d..1c703e1a376a 100644 --- a/hw/riscv/sifive_plic.c +++ b/hw/riscv/sifive_plic.c @@ -23,6 +23,7 @@ #include "qemu/error-report.h" #include "hw/sysbus.h" #include "target/riscv/cpu.h" +#include "sysemu/sysemu.h" #include "hw/riscv/sifive_plic.h" #define RISCV_DEBUG_PLIC 0 @@ -431,6 +432,7 @@ static void sifive_plic_irq_request(void *opaque, int irq, int level) static void sifive_plic_realize(DeviceState *dev, Error **errp) { SiFivePLICState *plic = SIFIVE_PLIC(dev); + int i; memory_region_init_io(&plic->mmio, OBJECT(dev), &sifive_plic_ops, plic, TYPE_SIFIVE_PLIC, plic->aperture_size); @@ -443,6 +445,19 @@ static void sifive_plic_realize(DeviceState *dev, Error **errp) plic->enable = g_new0(uint32_t, plic->bitfield_words * plic->num_addrs); sysbus_init_mmio(SYS_BUS_DEVICE(dev), &plic->mmio); qdev_init_gpio_in(dev, sifive_plic_irq_request, plic->num_sources); + + /* We can't allow the supervisor to control SEIP as this would allow the + * supervisor to clear a pending external interrupt which will result in + * lost a interrupt in the case a PLIC is attached. The SEIP bit must be + * hardware controlled when a PLIC is attached. + */ + for (i = 0; i < smp_cpus; i++) { + RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(i)); + if (riscv_cpu_claim_interrupts(cpu, MIP_SEIP) < 0) { + error_report("SEIP already claimed"); + exit(1); + } + } } static void sifive_plic_class_init(ObjectClass *klass, void *data) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 9b673de23e90..df43a0898d65 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -140,6 +140,7 @@ struct CPURISCVState { * mip is 32-bits to allow atomic_read on 32-bit hosts. */ uint32_t mip; + uint32_t miclaim; target_ulong mie; target_ulong mideleg; @@ -266,6 +267,7 @@ void riscv_cpu_list(FILE *f, fprintf_function cpu_fprintf); #define cpu_mmu_index riscv_cpu_mmu_index #ifndef CONFIG_USER_ONLY +int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint32_t interrupts); uint32_t riscv_cpu_update_mip(RISCVCPU *cpu, uint32_t mask, uint32_t value); #define BOOL_TO_MASK(x) (-!!(x)) /* helper for riscv_cpu_update_mip value */ #endif diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index f49e98ed594b..555756d40cfb 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -72,6 +72,17 @@ bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request) #if !defined(CONFIG_USER_ONLY) +int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint32_t interrupts) +{ + CPURISCVState *env = &cpu->env; + if (env->miclaim & interrupts) { + return -1; + } else { + env->miclaim |= interrupts; + return 0; + } +} + /* iothread_mutex must be held */ uint32_t riscv_cpu_update_mip(RISCVCPU *cpu, uint32_t mask, uint32_t value) { diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 9a40b4c7baed..385bb38768d0 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -550,16 +550,10 @@ static int rmw_mip(CPURISCVState *env, int csrno, target_ulong *ret_value, target_ulong new_value, target_ulong write_mask) { RISCVCPU *cpu = riscv_env_get_cpu(env); - target_ulong mask = write_mask & delegable_ints; + /* Allow software control of delegable interrupts not claimed by hardware */ + target_ulong mask = write_mask & delegable_ints & ~env->miclaim; uint32_t old_mip; - /* We can't allow the supervisor to control SEIP as this would allow the - * supervisor to clear a pending external interrupt which will result in - * lost a interrupt in the case a PLIC is attached. The SEIP bit must be - * hardware controlled when a PLIC is attached. This should be an option - * for CPUs with software-delegated Supervisor External Interrupts. */ - mask &= ~MIP_SEIP; - if (mask) { qemu_mutex_lock_iothread(); old_mip = riscv_cpu_update_mip(cpu, mask, (new_value & mask)); -- 2.19.2