Implement functions necessarry to have working external interrupts in hypervisor mode. The following changes are done: - Add a common function intc_handle_external_irq() to call APLIC specific function to handle an interrupt. - Update do_trap() function to handle IRQ_S_EXT case; add the check to catch case when cause of trap is an interrupt. - Add handle_interrrupt() member to intc_hw_operations structure. - Enable local interrupt delivery for IMSIC by implementation and calling of imsic_ids_local_delivery() in imsic_init(); additionally introduce helper imsic_csr_write() to update IMSIC_EITHRESHOLD and IMSIC_EITHRESHOLD. - Enable hypervisor external interrupts. - Implement aplic_handler_interrupt() and use it to init ->handle_interrupt member of intc_hw_operations for APLIC. - Add implementation of do_IRQ() to dispatch the interrupt.
The current patch is based on the code from [1]. [1] https://gitlab.com/xen-project/people/olkur/xen/-/commit/7390e2365828b83e27ead56b03114a56e3699dd5 Co-developed-by: Romain Caritey <romain.cari...@microchip.com> Signed-off-by: Oleksii Kurochko <oleksii.kuroc...@gmail.com> --- xen/arch/riscv/aplic.c | 19 +++++++++++++ xen/arch/riscv/imsic.c | 25 +++++++++++++++++ xen/arch/riscv/include/asm/imsic.h | 7 +++++ xen/arch/riscv/include/asm/intc.h | 5 ++++ xen/arch/riscv/include/asm/irq.h | 3 +++ xen/arch/riscv/intc.c | 7 +++++ xen/arch/riscv/irq.c | 43 ++++++++++++++++++++++++++++++ xen/arch/riscv/traps.c | 18 +++++++++++++ 8 files changed, 127 insertions(+) diff --git a/xen/arch/riscv/aplic.c b/xen/arch/riscv/aplic.c index 4b60cb9a77..38b57ed1ac 100644 --- a/xen/arch/riscv/aplic.c +++ b/xen/arch/riscv/aplic.c @@ -261,6 +261,21 @@ static void aplic_set_irq_affinity(struct irq_desc *desc, const cpumask_t *mask) spin_unlock(&aplic.lock); } +static void aplic_handle_interrupt(unsigned long cause, struct cpu_user_regs *regs) +{ + /* disable to avoid more external interrupts */ + csr_clear(CSR_SIE, 1UL << IRQ_S_EXT); + + /* clear the pending bit */ + csr_clear(CSR_SIP, 1UL << IRQ_S_EXT); + + /* dispatch the interrupt */ + do_IRQ(regs, csr_swap(CSR_STOPEI, 0) >> TOPI_IID_SHIFT); + + /* enable external interrupts */ + csr_set(CSR_SIE, 1UL << IRQ_S_EXT); +} + static hw_irq_controller aplic_host_irq_type = { .typename = "aplic", .startup = aplic_irq_startup, @@ -278,6 +293,7 @@ static const struct intc_hw_operations aplic_ops = { .host_irq_type = &aplic_host_irq_type, .set_irq_priority = aplic_set_irq_priority, .set_irq_type = aplic_set_irq_type, + .handle_interrupt = aplic_handle_interrupt, }; static int aplic_irq_xlate(const uint32_t *intspec, unsigned int intsize, @@ -318,6 +334,9 @@ static int __init aplic_preinit(struct dt_device_node *node, const void *dat) register_intc_ops(&aplic_ops); + /* Enable supervisor external interrupt */ + csr_set(CSR_SIE, 1UL << IRQ_S_EXT); + return 0; } diff --git a/xen/arch/riscv/imsic.c b/xen/arch/riscv/imsic.c index 8198d008ef..e00f2d69df 100644 --- a/xen/arch/riscv/imsic.c +++ b/xen/arch/riscv/imsic.c @@ -19,8 +19,19 @@ #include <asm/imsic.h> +#define IMSIC_DISABLE_EIDELIVERY 0 +#define IMSIC_ENABLE_EIDELIVERY 1 +#define IMSIC_DISABLE_EITHRESHOLD 1 +#define IMSIC_ENABLE_EITHRESHOLD 0 + static struct imsic_config imsic_cfg; +#define imsic_csr_write(c, v) \ +do { \ + csr_write(CSR_SISELECT, c); \ + csr_write(CSR_SIREG, v); \ +} while (0) + #define imsic_csr_set(c, v) \ do { \ csr_write(CSR_SISELECT, c); \ @@ -33,6 +44,20 @@ do { \ csr_clear(CSR_SIREG, v); \ } while (0) +void imsic_ids_local_delivery(bool enable) +{ + if ( enable ) + { + imsic_csr_write(IMSIC_EITHRESHOLD, IMSIC_ENABLE_EITHRESHOLD); + imsic_csr_write(IMSIC_EIDELIVERY, IMSIC_ENABLE_EIDELIVERY); + } + else + { + imsic_csr_write(IMSIC_EITHRESHOLD, IMSIC_DISABLE_EITHRESHOLD); + imsic_csr_write(IMSIC_EIDELIVERY, IMSIC_DISABLE_EIDELIVERY); + } +} + static void imsic_local_eix_update(unsigned long base_id, unsigned long num_id, bool pend, bool val) { diff --git a/xen/arch/riscv/include/asm/imsic.h b/xen/arch/riscv/include/asm/imsic.h index d2c0178529..b2c674f271 100644 --- a/xen/arch/riscv/include/asm/imsic.h +++ b/xen/arch/riscv/include/asm/imsic.h @@ -12,6 +12,7 @@ #define ASM__RISCV__IMSIC_H #include <xen/spinlock.h> +#include <xen/stdbool.h> #include <xen/types.h> #define IMSIC_MMIO_PAGE_SHIFT 12 @@ -20,6 +21,10 @@ #define IMSIC_MIN_ID 63 #define IMSIC_MAX_ID 2048 +#define IMSIC_EIDELIVERY 0x70 + +#define IMSIC_EITHRESHOLD 0x72 + #define IMSIC_EIP0 0x80 #define IMSIC_EIPx_BITS 32 @@ -78,4 +83,6 @@ const struct imsic_config *imsic_get_config(void); void imsic_irq_enable(unsigned int hwirq); void imsic_irq_disable(unsigned int hwirq); +void imsic_ids_local_delivery(bool enable); + #endif /* ASM__RISCV__IMSIC_H */ diff --git a/xen/arch/riscv/include/asm/intc.h b/xen/arch/riscv/include/asm/intc.h index db53caa07b..e4363af87d 100644 --- a/xen/arch/riscv/include/asm/intc.h +++ b/xen/arch/riscv/include/asm/intc.h @@ -34,6 +34,8 @@ struct intc_hw_operations { /* Set IRQ priority */ void (*set_irq_priority)(struct irq_desc *desc, unsigned int priority); + /* handle external interrupt */ + void (*handle_interrupt)(unsigned long cause, struct cpu_user_regs *regs); }; void intc_preinit(void); @@ -45,4 +47,7 @@ void register_intc_ops(const struct intc_hw_operations *ops); struct irq_desc; void intc_route_irq_to_xen(struct irq_desc *desc, unsigned int priority); +struct cpu_user_regs; +void intc_handle_external_irqs(unsigned long cause, struct cpu_user_regs *regs); + #endif /* ASM__RISCV__INTERRUPT_CONTOLLER_H */ diff --git a/xen/arch/riscv/include/asm/irq.h b/xen/arch/riscv/include/asm/irq.h index 163a478d78..9558d3fa61 100644 --- a/xen/arch/riscv/include/asm/irq.h +++ b/xen/arch/riscv/include/asm/irq.h @@ -51,6 +51,9 @@ int platform_get_irq(const struct dt_device_node *device, int index); void init_IRQ(void); +struct cpu_user_regs; +void do_IRQ(struct cpu_user_regs *regs, unsigned int irq); + #endif /* ASM__RISCV__IRQ_H */ /* diff --git a/xen/arch/riscv/intc.c b/xen/arch/riscv/intc.c index 8274897d8c..41a4310ead 100644 --- a/xen/arch/riscv/intc.c +++ b/xen/arch/riscv/intc.c @@ -51,6 +51,13 @@ static void intc_set_irq_priority(struct irq_desc *desc, unsigned int priority) intc_hw_ops->set_irq_priority(desc, priority); } +void intc_handle_external_irqs(unsigned long cause, struct cpu_user_regs *regs) +{ + ASSERT(intc_hw_ops && intc_hw_ops->handle_interrupt); + + intc_hw_ops->handle_interrupt(cause, regs); +} + void intc_route_irq_to_xen(struct irq_desc *desc, unsigned int priority) { ASSERT(test_bit(_IRQ_DISABLED, &desc->status)); diff --git a/xen/arch/riscv/irq.c b/xen/arch/riscv/irq.c index c332e000c4..3c0b95220a 100644 --- a/xen/arch/riscv/irq.c +++ b/xen/arch/riscv/irq.c @@ -11,6 +11,10 @@ #include <xen/errno.h> #include <xen/init.h> #include <xen/irq.h> +#include <xen/spinlock.h> + +#include <asm/hardirq.h> +#include <asm/intc.h> static irq_desc_t irq_desc[NR_IRQS]; @@ -83,3 +87,42 @@ void __init init_IRQ(void) if ( init_irq_data() < 0 ) panic("initialization of IRQ data failed\n"); } + +/* Dispatch an interrupt */ +void do_IRQ(struct cpu_user_regs *regs, unsigned int irq) +{ + struct irq_desc *desc = irq_to_desc(irq); + struct irqaction *action; + + irq_enter(); + + spin_lock(&desc->lock); + desc->handler->ack(desc); + + if ( test_bit(_IRQ_DISABLED, &desc->status) ) + goto out; + + set_bit(_IRQ_INPROGRESS, &desc->status); + + action = desc->action; + + spin_unlock_irq(&desc->lock); + +#ifndef CONFIG_IRQ_HAS_MULTIPLE_ACTION + action->handler(irq, action->dev_id); +#else + do { + action->handler(irq, action->dev_id); + action = action->next; + } while ( action ); +#endif /* CONFIG_IRQ_HAS_MULTIPLE_ACTION */ + + spin_lock_irq(&desc->lock); + + clear_bit(_IRQ_INPROGRESS, &desc->status); + +out: + desc->handler->end(desc); + spin_unlock(&desc->lock); + irq_exit(); +} diff --git a/xen/arch/riscv/traps.c b/xen/arch/riscv/traps.c index ea3638a54f..da5813e34a 100644 --- a/xen/arch/riscv/traps.c +++ b/xen/arch/riscv/traps.c @@ -11,6 +11,7 @@ #include <xen/nospec.h> #include <xen/sched.h> +#include <asm/intc.h> #include <asm/processor.h> #include <asm/riscv_encoding.h> #include <asm/traps.h> @@ -128,6 +129,23 @@ void do_trap(struct cpu_user_regs *cpu_regs) } fallthrough; default: + if ( cause & CAUSE_IRQ_FLAG ) + { + /* Handle interrupt */ + unsigned long icause = cause & ~CAUSE_IRQ_FLAG; + + switch ( icause ) + { + case IRQ_S_EXT: + intc_handle_external_irqs(cause, cpu_regs); + break; + default: + break; + } + + break; + } + do_unexpected_trap(cpu_regs); break; } -- 2.49.0