mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
xen/riscv: add external interrupt handling for hypervisor mode
Implement functions necessary 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@microchip.com>
Signed-off-by: Oleksii Kurochko <oleksii.kurochko@gmail.com>
Acked-by: Jan Beulich <jbeulich@suse.com>
This commit is contained in:
1 parent
d4676a1398
commit
a8b85fabf6
8 files changed
+163
-1
No files matched your search
@@ -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>
|
||||
@@ -227,6 +228,73 @@ static void cf_check aplic_set_irq_affinity(struct irq_desc *desc, const cpumask
|
||||
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,
|
||||
@@ -240,6 +308,8 @@ static const struct intc_hw_operations 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,
|
||||
@@ -279,6 +349,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,13 @@
|
||||
#define APLIC_DOMAINCFG_IE BIT(8, U)
|
||||
#define APLIC_DOMAINCFG_DM BIT(2, U)
|
||||
|
||||
#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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -14,6 +14,7 @@ enum intc_version {
|
||||
INTC_APLIC,
|
||||
};
|
||||
|
||||
struct cpu_user_regs;
|
||||
struct irq_desc;
|
||||
|
||||
struct intc_info {
|
||||
@@ -37,6 +38,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);
|
||||
@@ -47,4 +51,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 */
|
||||
@@ -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 */
|
||||
|
||||
/*
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||