diff --git a/xen/arch/riscv/domain.c b/xen/arch/riscv/domain.c index 14cc85093b..694a03ab74 100644 --- a/xen/arch/riscv/domain.c +++ b/xen/arch/riscv/domain.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -168,6 +169,68 @@ void arch_vcpu_destroy(struct vcpu *v) vfree((void *)&v->arch.cpu_info[1] - STACK_SIZE); } +int vcpu_set_interrupt(struct vcpu *v, unsigned int irq) +{ + bool kick_vcpu; + + /* We only allow VS-mode software, timer, and external interrupts */ + if ( irq != IRQ_VS_SOFT && + irq != IRQ_VS_TIMER && + irq != IRQ_VS_EXT ) + return -EINVAL; + + kick_vcpu = !test_and_set_bit(irq, v->arch.irqs_pending); + + /* + * The counterpart of this barrier is the one encoded implicitly in xchg() + * which is used in consumer part (vcpu_flush_interrupts()). + */ + smp_wmb(); + + kick_vcpu |= !test_and_set_bit(irq, v->arch.irqs_pending_mask); + + if ( kick_vcpu ) + vcpu_kick(v); + + return 0; +} + +int vcpu_unset_interrupt(struct vcpu *v, unsigned int irq) +{ + /* We only allow VS-mode software, timer, external interrupts */ + if ( irq != IRQ_VS_SOFT && + irq != IRQ_VS_TIMER && + irq != IRQ_VS_EXT ) + return -EINVAL; + + clear_bit(irq, v->arch.irqs_pending); + /* + * The counterpart of this barrier is the one encoded implicitly in xchg() + * which is used in consumer part (vcpu_flush_interrupts()). + */ + smp_wmb(); + set_bit(irq, v->arch.irqs_pending_mask); + + return 0; +} + +void vcpu_sync_interrupts(struct vcpu *curr) +{ + unsigned long hvip = csr_read(CSR_HVIP); + + ASSERT(curr == current); + + /* Sync-up HVIP.VSSIP bit changes done by Guest */ + if ( ((curr->arch.hvip ^ hvip) & BIT(IRQ_VS_SOFT, UL)) && + !test_and_set_bit(IRQ_VS_SOFT, &curr->arch.irqs_pending_mask) ) + { + if ( hvip & BIT(IRQ_VS_SOFT, UL) ) + set_bit(IRQ_VS_SOFT, &curr->arch.irqs_pending); + else + clear_bit(IRQ_VS_SOFT, &curr->arch.irqs_pending); + } +} + static void __init __maybe_unused build_assertions(void) { /* diff --git a/xen/arch/riscv/include/asm/domain.h b/xen/arch/riscv/include/asm/domain.h index 17be792afe..1ecfe18c85 100644 --- a/xen/arch/riscv/include/asm/domain.h +++ b/xen/arch/riscv/include/asm/domain.h @@ -54,8 +54,25 @@ struct arch_vcpu { register_t hideleg; register_t henvcfg; register_t hstateen0; + register_t hvip; register_t vsatp; + + /* + * VCPU interrupts + * + * We have a lockless approach for tracking pending VCPU interrupts + * implemented using atomic bitops. The irqs_pending bitmap represent + * pending interrupts whereas irqs_pending_mask represent bits changed + * in irqs_pending. Our approach is modeled around multiple producer + * and single consumer problem where the consumer is the VCPU itself. + * + * DECLARE_BITMAP() is needed here to support 64 vCPU local interrupts + * on RV32 host. + */ +#define RISCV_VCPU_NR_IRQS MAX(BITS_PER_LONG, 64) + DECLARE_BITMAP(irqs_pending, RISCV_VCPU_NR_IRQS); + DECLARE_BITMAP(irqs_pending_mask, RISCV_VCPU_NR_IRQS); }; struct paging_domain { @@ -94,6 +111,11 @@ static inline void update_guest_memory_policy(struct vcpu *v, static inline void arch_vcpu_block(struct vcpu *v) {} +int vcpu_set_interrupt(struct vcpu *v, unsigned int irq); +int vcpu_unset_interrupt(struct vcpu *v, unsigned int irq); + +void vcpu_sync_interrupts(struct vcpu *curr); + #endif /* ASM__RISCV__DOMAIN_H */ /* diff --git a/xen/arch/riscv/traps.c b/xen/arch/riscv/traps.c index 9fca941526..551f886e3a 100644 --- a/xen/arch/riscv/traps.c +++ b/xen/arch/riscv/traps.c @@ -171,6 +171,10 @@ static void do_unexpected_trap(const struct cpu_user_regs *regs) static void check_for_pcpu_work(void) { + struct vcpu *curr = current; + + vcpu_sync_interrupts(curr); + p2m_handle_vmenter(); }