|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [PATCH v3 12/14] xen/riscv: add external interrupt handling for hypervisor mode
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 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.Caritey@xxxxxxxxxxxxx>
Signed-off-by: Oleksii Kurochko <oleksii.kurochko@xxxxxxxxx>
Acked-by: Jan Beulich <jbeulich@xxxxxxxx>
---
Changes in V3:
- Add ASSERT(spin_is_locked(&desc->lock)) to aplic_set_irq_type().
- Fix code style for switch () in aplic_set_irq_type().
- Drop fallthrough between "case IRQ_TYPE_NONE: case IRQ_TYPE_INVALID:" as
there
is no other statements in "case IRQ_TYPE_NONE".
- Add Acked-by: Jan Beulich <jbeulich@xxxxxxxx>.
---
Changes in V2:
- use BIT() macros instead of 1UL << bit_num in aplic.c.
- Drop passing of a cause to aplic_handle_interrupt() function. And update
prototype of handle_interrupt() callback.
- Drop ASSERT() in intc_handle_external_irqs() as it is useless.
- Code style fixes.
- Drop cause argument for intc_handle_external_irqs().
- Update the commit message: drop words that imsic_ids_local_delivery() is
implemented in this patch, it is only called.
- Add cf_check for aplic_handle_interrupt(), aplic_set_irq_type().
- Move forward declarations in asm/intc.h up.
- Use plain C operator instead if {clear,set}_bit() for desc->status as it
is always used under spinlock().
- use writel() for writing to APLIC's sourcecfg in aplic_set_irq_type().
---
xen/arch/riscv/aplic.c | 71 ++++++++++++++++++++++++++++++
xen/arch/riscv/include/asm/aplic.h | 7 +++
xen/arch/riscv/include/asm/imsic.h | 1 +
xen/arch/riscv/include/asm/intc.h | 6 +++
xen/arch/riscv/include/asm/irq.h | 6 ++-
xen/arch/riscv/intc.c | 5 +++
xen/arch/riscv/irq.c | 47 ++++++++++++++++++++
xen/arch/riscv/traps.c | 19 ++++++++
8 files changed, 161 insertions(+), 1 deletion(-)
diff --git a/xen/arch/riscv/aplic.c b/xen/arch/riscv/aplic.c
index f48937ccc6..5415471680 100644
--- a/xen/arch/riscv/aplic.c
+++ b/xen/arch/riscv/aplic.c
@@ -9,6 +9,7 @@
* Copyright (c) 2024-2025 Vates
*/
+#include <xen/const.h>
#include <xen/device_tree.h>
#include <xen/errno.h>
#include <xen/init.h>
@@ -212,6 +213,71 @@ static void aplic_set_irq_affinity(struct irq_desc *desc,
const cpumask_t *mask)
spin_unlock(&aplic.lock);
}
+static void cf_check aplic_handle_interrupt(struct cpu_user_regs *regs)
+{
+ /* disable to avoid more external interrupts */
+ csr_clear(CSR_SIE, BIT(IRQ_S_EXT, UL));
+
+ /* clear the pending bit */
+ csr_clear(CSR_SIP, BIT(IRQ_S_EXT, UL));
+
+ /* dispatch the interrupt */
+ do_IRQ(regs, csr_swap(CSR_STOPEI, 0) >> TOPI_IID_SHIFT);
+
+ /* enable external interrupts */
+ csr_set(CSR_SIE, BIT(IRQ_S_EXT, UL));
+}
+
+static void cf_check aplic_set_irq_type(struct irq_desc *desc, unsigned int
type)
+{
+ /*
+ * Interrupt 0 isn't possible based on the spec:
+ * Each of an APLIC’s interrupt sources has a fixed unique identity
number in the range 1 to N,
+ * where N is the total number of sources at the APLIC. The number zero
is not a valid interrupt
+ * identity number at an APLIC. The maximum number of interrupt sources
an APLIC may support
+ * is 1023.
+ *
+ * Thereby interrupt 1 will correspond to bit 0 in sourcecfg[] register,
+ * interrupt 2 ->sourcecfg[1] and so on.
+ *
+ * And that is the reason why we need -1.
+ */
+ unsigned int irq_bit = desc->irq - 1;
+
+ ASSERT(spin_is_locked(&desc->lock));
+
+ spin_lock(&aplic.lock);
+
+ switch ( type )
+ {
+ case IRQ_TYPE_EDGE_RISING:
+ writel(APLIC_SOURCECFG_SM_EDGE_RISE, &aplic.regs->sourcecfg[irq_bit]);
+ break;
+
+ case IRQ_TYPE_EDGE_FALLING:
+ writel(APLIC_SOURCECFG_SM_EDGE_FALL, &aplic.regs->sourcecfg[irq_bit]);
+ break;
+
+ case IRQ_TYPE_LEVEL_HIGH:
+ writel(APLIC_SOURCECFG_SM_LEVEL_HIGH, &aplic.regs->sourcecfg[irq_bit]);
+ break;
+
+ case IRQ_TYPE_LEVEL_LOW:
+ writel(APLIC_SOURCECFG_SM_LEVEL_LOW, &aplic.regs->sourcecfg[irq_bit]);
+ break;
+
+ case IRQ_TYPE_NONE:
+ case IRQ_TYPE_INVALID:
+ writel(APLIC_SOURCECFG_SM_INACTIVE, &aplic.regs->sourcecfg[irq_bit]);
+ break;
+
+ default:
+ panic("%s: APLIC doesnt support IRQ type: 0x%x?\n", __func__, type);
+ }
+
+ spin_unlock(&aplic.lock);
+}
+
static const hw_irq_controller aplic_xen_irq_type = {
.typename = "aplic",
.startup = aplic_irq_startup,
@@ -225,6 +291,8 @@ static struct intc_hw_operations __ro_after_init aplic_ops
= {
.info = &aplic_info,
.init = aplic_init,
.host_irq_type = &aplic_xen_irq_type,
+ .handle_interrupt = aplic_handle_interrupt,
+ .set_irq_type = aplic_set_irq_type,
};
static int cf_check aplic_irq_xlate(const uint32_t *intspec,
@@ -264,6 +332,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, BIT(IRQ_S_EXT, UL));
+
return 0;
}
diff --git a/xen/arch/riscv/include/asm/aplic.h
b/xen/arch/riscv/include/asm/aplic.h
index a814a36a82..ef5b1d3e85 100644
--- a/xen/arch/riscv/include/asm/aplic.h
+++ b/xen/arch/riscv/include/asm/aplic.h
@@ -18,6 +18,13 @@
#define APLIC_DOMAINCFG_IE BIT(8, UL)
#define APLIC_DOMAINCFG_DM BIT(2, UL)
+#define APLIC_SOURCECFG_SM_INACTIVE 0x0
+#define APLIC_SOURCECFG_SM_DETACH 0x1
+#define APLIC_SOURCECFG_SM_EDGE_RISE 0x4
+#define APLIC_SOURCECFG_SM_EDGE_FALL 0x5
+#define APLIC_SOURCECFG_SM_LEVEL_HIGH 0x6
+#define APLIC_SOURCECFG_SM_LEVEL_LOW 0x7
+
#define APLIC_TARGET_HART_IDX_SHIFT 18
struct aplic_regs {
diff --git a/xen/arch/riscv/include/asm/imsic.h
b/xen/arch/riscv/include/asm/imsic.h
index a0eba55f99..4973016cd8 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
diff --git a/xen/arch/riscv/include/asm/intc.h
b/xen/arch/riscv/include/asm/intc.h
index 1a88505518..b11b26addd 100644
--- a/xen/arch/riscv/include/asm/intc.h
+++ b/xen/arch/riscv/include/asm/intc.h
@@ -12,6 +12,7 @@ enum intc_version {
INTC_APLIC,
};
+struct cpu_user_regs;
struct irq_desc;
struct intc_info {
@@ -35,6 +36,9 @@ struct intc_hw_operations {
void (*set_irq_type)(struct irq_desc *desc, unsigned int type);
/* Set IRQ priority */
void (*set_irq_priority)(struct irq_desc *desc, unsigned int priority);
+
+ /* handle external interrupt */
+ void (*handle_interrupt)(struct cpu_user_regs *regs);
};
void intc_preinit(void);
@@ -45,4 +49,6 @@ void intc_init(void);
void intc_route_irq_to_xen(struct irq_desc *desc, unsigned int priority);
+void intc_handle_external_irqs(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 84c3c2904d..94151eb083 100644
--- a/xen/arch/riscv/include/asm/irq.h
+++ b/xen/arch/riscv/include/asm/irq.h
@@ -33,16 +33,20 @@ struct arch_irq_desc {
unsigned int type;
};
+struct cpu_user_regs;
+struct dt_device_node;
+
static inline void arch_move_irqs(struct vcpu *v)
{
BUG_ON("unimplemented");
}
-struct dt_device_node;
int platform_get_irq(const struct dt_device_node *device, int index);
void init_IRQ(void);
+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 f2823267a9..ea317aea5a 100644
--- a/xen/arch/riscv/intc.c
+++ b/xen/arch/riscv/intc.c
@@ -50,6 +50,11 @@ 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(struct cpu_user_regs *regs)
+{
+ intc_hw_ops->handle_interrupt(regs);
+}
+
void intc_route_irq_to_xen(struct irq_desc *desc, unsigned int priority)
{
ASSERT(desc->status & IRQ_DISABLED);
diff --git a/xen/arch/riscv/irq.c b/xen/arch/riscv/irq.c
index 669ef3ae9e..466f1b4ba9 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];
@@ -90,3 +94,46 @@ 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);
+
+ if ( desc->handler->ack )
+ desc->handler->ack(desc);
+
+ if ( desc->status & IRQ_DISABLED )
+ goto out;
+
+ desc->status |= IRQ_INPROGRESS;
+
+ 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);
+
+ desc->status &= ~IRQ_INPROGRESS;
+
+ out:
+ if ( desc->handler->end )
+ 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..f061004d83 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,24 @@ 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(cpu_regs);
+ break;
+
+ default:
+ break;
+ }
+
+ break;
+ }
+
do_unexpected_trap(cpu_regs);
break;
}
--
2.49.0
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |