mirror of https://github.com/xqemu/xqemu.git
RISC-V: Allow setting and clearing multiple irqs
Change the API of riscv_set_local_interrupt to take a write mask and value to allow setting and clearing of multiple local interrupts atomically in a single call. Rename the new function to riscv_cpu_update_mip. Signed-off-by: Michael Clark <mjc@sifive.com> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Palmer Dabbelt <palmer@sifive.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com> Signed-off-by: Palmer Dabbelt <palmer@sifive.com>
This commit is contained in:
parent
09558375a6
commit
85ba724fd6
|
@ -47,12 +47,12 @@ static void sifive_clint_write_timecmp(RISCVCPU *cpu, uint64_t value)
|
||||||
if (cpu->env.timecmp <= rtc_r) {
|
if (cpu->env.timecmp <= rtc_r) {
|
||||||
/* if we're setting an MTIMECMP value in the "past",
|
/* if we're setting an MTIMECMP value in the "past",
|
||||||
immediately raise the timer interrupt */
|
immediately raise the timer interrupt */
|
||||||
riscv_set_local_interrupt(cpu, MIP_MTIP, 1);
|
riscv_cpu_update_mip(cpu, MIP_MTIP, BOOL_TO_MASK(1));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* otherwise, set up the future timer interrupt */
|
/* otherwise, set up the future timer interrupt */
|
||||||
riscv_set_local_interrupt(cpu, MIP_MTIP, 0);
|
riscv_cpu_update_mip(cpu, MIP_MTIP, BOOL_TO_MASK(0));
|
||||||
diff = cpu->env.timecmp - rtc_r;
|
diff = cpu->env.timecmp - rtc_r;
|
||||||
/* back to ns (note args switched in muldiv64) */
|
/* back to ns (note args switched in muldiv64) */
|
||||||
next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
|
next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
|
||||||
|
@ -67,7 +67,7 @@ static void sifive_clint_write_timecmp(RISCVCPU *cpu, uint64_t value)
|
||||||
static void sifive_clint_timer_cb(void *opaque)
|
static void sifive_clint_timer_cb(void *opaque)
|
||||||
{
|
{
|
||||||
RISCVCPU *cpu = opaque;
|
RISCVCPU *cpu = opaque;
|
||||||
riscv_set_local_interrupt(cpu, MIP_MTIP, 1);
|
riscv_cpu_update_mip(cpu, MIP_MTIP, BOOL_TO_MASK(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* CPU wants to read rtc or timecmp register */
|
/* CPU wants to read rtc or timecmp register */
|
||||||
|
@ -132,7 +132,7 @@ static void sifive_clint_write(void *opaque, hwaddr addr, uint64_t value,
|
||||||
if (!env) {
|
if (!env) {
|
||||||
error_report("clint: invalid timecmp hartid: %zu", hartid);
|
error_report("clint: invalid timecmp hartid: %zu", hartid);
|
||||||
} else if ((addr & 0x3) == 0) {
|
} else if ((addr & 0x3) == 0) {
|
||||||
riscv_set_local_interrupt(RISCV_CPU(cpu), MIP_MSIP, value != 0);
|
riscv_cpu_update_mip(RISCV_CPU(cpu), MIP_MSIP, BOOL_TO_MASK(value));
|
||||||
} else {
|
} else {
|
||||||
error_report("clint: invalid sip write: %08x", (uint32_t)addr);
|
error_report("clint: invalid sip write: %08x", (uint32_t)addr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -142,10 +142,10 @@ static void sifive_plic_update(SiFivePLICState *plic)
|
||||||
int level = sifive_plic_irqs_pending(plic, addrid);
|
int level = sifive_plic_irqs_pending(plic, addrid);
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case PLICMode_M:
|
case PLICMode_M:
|
||||||
riscv_set_local_interrupt(RISCV_CPU(cpu), MIP_MEIP, level);
|
riscv_cpu_update_mip(RISCV_CPU(cpu), MIP_MEIP, BOOL_TO_MASK(level));
|
||||||
break;
|
break;
|
||||||
case PLICMode_S:
|
case PLICMode_S:
|
||||||
riscv_set_local_interrupt(RISCV_CPU(cpu), MIP_SEIP, level);
|
riscv_cpu_update_mip(RISCV_CPU(cpu), MIP_SEIP, BOOL_TO_MASK(level));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -126,13 +126,18 @@ struct CPURISCVState {
|
||||||
|
|
||||||
target_ulong mhartid;
|
target_ulong mhartid;
|
||||||
target_ulong mstatus;
|
target_ulong mstatus;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* CAUTION! Unlike the rest of this struct, mip is accessed asynchonously
|
* CAUTION! Unlike the rest of this struct, mip is accessed asynchonously
|
||||||
* by I/O threads and other vCPUs, so hold the iothread mutex before
|
* by I/O threads. It should be read with atomic_read. It should be updated
|
||||||
* operating on it. CPU_INTERRUPT_HARD should be in effect iff this is
|
* using riscv_cpu_update_mip with the iothread mutex held. The iothread
|
||||||
* non-zero. Use riscv_cpu_set_local_interrupt.
|
* mutex must be held because mip must be consistent with the CPU inturrept
|
||||||
|
* state. riscv_cpu_update_mip calls cpu_interrupt or cpu_reset_interrupt
|
||||||
|
* wuth the invariant that CPU_INTERRUPT_HARD is set iff mip is non-zero.
|
||||||
|
* mip is 32-bits to allow atomic_read on 32-bit hosts.
|
||||||
*/
|
*/
|
||||||
uint32_t mip; /* allow atomic_read for >= 32-bit hosts */
|
uint32_t mip;
|
||||||
|
|
||||||
target_ulong mie;
|
target_ulong mie;
|
||||||
target_ulong mideleg;
|
target_ulong mideleg;
|
||||||
|
|
||||||
|
@ -247,7 +252,6 @@ void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
|
||||||
uintptr_t retaddr);
|
uintptr_t retaddr);
|
||||||
int riscv_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int size,
|
int riscv_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int size,
|
||||||
int rw, int mmu_idx);
|
int rw, int mmu_idx);
|
||||||
|
|
||||||
char *riscv_isa_string(RISCVCPU *cpu);
|
char *riscv_isa_string(RISCVCPU *cpu);
|
||||||
void riscv_cpu_list(FILE *f, fprintf_function cpu_fprintf);
|
void riscv_cpu_list(FILE *f, fprintf_function cpu_fprintf);
|
||||||
|
|
||||||
|
@ -255,6 +259,10 @@ void riscv_cpu_list(FILE *f, fprintf_function cpu_fprintf);
|
||||||
#define cpu_list riscv_cpu_list
|
#define cpu_list riscv_cpu_list
|
||||||
#define cpu_mmu_index riscv_cpu_mmu_index
|
#define cpu_mmu_index riscv_cpu_mmu_index
|
||||||
|
|
||||||
|
#ifndef CONFIG_USER_ONLY
|
||||||
|
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
|
||||||
void riscv_set_mode(CPURISCVState *env, target_ulong newpriv);
|
void riscv_set_mode(CPURISCVState *env, target_ulong newpriv);
|
||||||
|
|
||||||
void riscv_translate_init(void);
|
void riscv_translate_init(void);
|
||||||
|
@ -285,10 +293,6 @@ void csr_write_helper(CPURISCVState *env, target_ulong val_to_write,
|
||||||
target_ulong csrno);
|
target_ulong csrno);
|
||||||
target_ulong csr_read_helper(CPURISCVState *env, target_ulong csrno);
|
target_ulong csr_read_helper(CPURISCVState *env, target_ulong csrno);
|
||||||
|
|
||||||
#ifndef CONFIG_USER_ONLY
|
|
||||||
void riscv_set_local_interrupt(RISCVCPU *cpu, target_ulong mask, int value);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "exec/cpu-all.h"
|
#include "exec/cpu-all.h"
|
||||||
|
|
||||||
#endif /* RISCV_CPU_H */
|
#endif /* RISCV_CPU_H */
|
||||||
|
|
|
@ -171,10 +171,8 @@ void csr_write_helper(CPURISCVState *env, target_ulong val_to_write,
|
||||||
*/
|
*/
|
||||||
qemu_mutex_lock_iothread();
|
qemu_mutex_lock_iothread();
|
||||||
RISCVCPU *cpu = riscv_env_get_cpu(env);
|
RISCVCPU *cpu = riscv_env_get_cpu(env);
|
||||||
riscv_set_local_interrupt(cpu, MIP_SSIP,
|
riscv_cpu_update_mip(cpu, MIP_SSIP | MIP_STIP,
|
||||||
(val_to_write & MIP_SSIP) != 0);
|
(val_to_write & (MIP_SSIP | MIP_STIP)));
|
||||||
riscv_set_local_interrupt(cpu, MIP_STIP,
|
|
||||||
(val_to_write & MIP_STIP) != 0);
|
|
||||||
/*
|
/*
|
||||||
* csrs, csrc on mip.SEIP is not decomposable into separate read and
|
* csrs, csrc on mip.SEIP is not decomposable into separate read and
|
||||||
* write steps, so a different implementation is needed
|
* write steps, so a different implementation is needed
|
||||||
|
@ -657,16 +655,24 @@ target_ulong helper_csrrc(CPURISCVState *env, target_ulong src,
|
||||||
#ifndef CONFIG_USER_ONLY
|
#ifndef CONFIG_USER_ONLY
|
||||||
|
|
||||||
/* iothread_mutex must be held */
|
/* iothread_mutex must be held */
|
||||||
void riscv_set_local_interrupt(RISCVCPU *cpu, target_ulong mask, int value)
|
uint32_t riscv_cpu_update_mip(RISCVCPU *cpu, uint32_t mask, uint32_t value)
|
||||||
{
|
{
|
||||||
target_ulong old_mip = cpu->env.mip;
|
CPURISCVState *env = &cpu->env;
|
||||||
cpu->env.mip = (old_mip & ~mask) | (value ? mask : 0);
|
uint32_t old, new, cmp = atomic_read(&env->mip);
|
||||||
|
|
||||||
if (cpu->env.mip && !old_mip) {
|
do {
|
||||||
|
old = cmp;
|
||||||
|
new = (old & ~mask) | (value & mask);
|
||||||
|
cmp = atomic_cmpxchg(&env->mip, old, new);
|
||||||
|
} while (old != cmp);
|
||||||
|
|
||||||
|
if (new && !old) {
|
||||||
cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
|
cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
|
||||||
} else if (!cpu->env.mip && old_mip) {
|
} else if (!new && old) {
|
||||||
cpu_reset_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
|
cpu_reset_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return old;
|
||||||
}
|
}
|
||||||
|
|
||||||
void riscv_set_mode(CPURISCVState *env, target_ulong newpriv)
|
void riscv_set_mode(CPURISCVState *env, target_ulong newpriv)
|
||||||
|
|
Loading…
Reference in New Issue