Unverified Commit 85ba724f authored by Michael Clark's avatar Michael Clark Committed by Palmer Dabbelt
Browse files

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: default avatarMichael Clark <mjc@sifive.com>
Reviewed-by: default avatarAlistair Francis <alistair.francis@wdc.com>
Reviewed-by: default avatarPalmer Dabbelt <palmer@sifive.com>
Signed-off-by: default avatarAlistair Francis <alistair.francis@wdc.com>
Signed-off-by: default avatarPalmer Dabbelt <palmer@sifive.com>
parent 09558375
Loading
Loading
Loading
Loading
+4 −4
Original line number Diff line number Diff line
@@ -47,12 +47,12 @@ static void sifive_clint_write_timecmp(RISCVCPU *cpu, uint64_t value)
    if (cpu->env.timecmp <= rtc_r) {
        /* if we're setting an MTIMECMP value in the "past",
           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;
    }

    /* 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;
    /* back to ns (note args switched in muldiv64) */
    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)
{
    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 */
@@ -132,7 +132,7 @@ static void sifive_clint_write(void *opaque, hwaddr addr, uint64_t value,
        if (!env) {
            error_report("clint: invalid timecmp hartid: %zu", hartid);
        } 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 {
            error_report("clint: invalid sip write: %08x", (uint32_t)addr);
        }
+2 −2
Original line number Diff line number Diff line
@@ -142,10 +142,10 @@ static void sifive_plic_update(SiFivePLICState *plic)
        int level = sifive_plic_irqs_pending(plic, addrid);
        switch (mode) {
        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;
        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;
        default:
            break;
+13 −9
Original line number Diff line number Diff line
@@ -126,13 +126,18 @@ struct CPURISCVState {

    target_ulong mhartid;
    target_ulong mstatus;

    /*
     * CAUTION! Unlike the rest of this struct, mip is accessed asynchonously
     * by I/O threads and other vCPUs, so hold the iothread mutex before
     * operating on it.  CPU_INTERRUPT_HARD should be in effect iff this is
     * non-zero.  Use riscv_cpu_set_local_interrupt.
     * by I/O threads. It should be read with atomic_read. It should be updated
     * using riscv_cpu_update_mip with the iothread mutex held. The iothread
     * 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 mideleg;

@@ -247,7 +252,6 @@ void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
                                    uintptr_t retaddr);
int riscv_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int size,
                              int rw, int mmu_idx);

char *riscv_isa_string(RISCVCPU *cpu);
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_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_translate_init(void);
@@ -285,10 +293,6 @@ void csr_write_helper(CPURISCVState *env, target_ulong val_to_write,
        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"

#endif /* RISCV_CPU_H */
+15 −9
Original line number Diff line number Diff line
@@ -171,10 +171,8 @@ void csr_write_helper(CPURISCVState *env, target_ulong val_to_write,
         */
        qemu_mutex_lock_iothread();
        RISCVCPU *cpu = riscv_env_get_cpu(env);
        riscv_set_local_interrupt(cpu, MIP_SSIP,
                                  (val_to_write & MIP_SSIP) != 0);
        riscv_set_local_interrupt(cpu, MIP_STIP,
                                  (val_to_write & MIP_STIP) != 0);
        riscv_cpu_update_mip(cpu, MIP_SSIP | MIP_STIP,
                                  (val_to_write & (MIP_SSIP | MIP_STIP)));
        /*
         * csrs, csrc on mip.SEIP is not decomposable into separate read and
         * 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

/* 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;
    cpu->env.mip = (old_mip & ~mask) | (value ? mask : 0);
    CPURISCVState *env = &cpu->env;
    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);
    } else if (!cpu->env.mip && old_mip) {
    } else if (!new && old) {
        cpu_reset_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
    }

    return old;
}

void riscv_set_mode(CPURISCVState *env, target_ulong newpriv)