[PATCH 00/14] Xvisor IMSIC driver and APLIC MSI-mode support

79 views
Skip to first unread message

Anup Patel

unread,
Apr 27, 2022, 11:51:51 PM4/27/22
to xvisor...@googlegroups.com, Anup Patel
This series completes the driver AIA driver support for Xvisor RISC-V. It
include the following:
1) Improved MSI framework for platform MSIs
2) Added IMSIC driver ported from Linux IMSIC driver
3) Added APIC MSI-mode in APLIC driver

These patches can also be found in riscv_imsic_v1 branch at:
https://github.com/avpatel/xvisor-next.git

Anup Patel (14):
DRIVERS: irqchip/riscv-aplic: Align priority and threshold with Linux
CORE: vmm_host_irqdomain: Add alloc() and free() domain operations
CORE: vmm_msi: Move compose_msi_msg() from MSI domain to irqchip
CORE: vmm_msi: Use desc->msg in vmm_msi_domain_alloc/free_irqs()
CORE: vmm_msi: Add vmm_msi_domain_write_msg() API
CORE: vmm_msi: Provide complete set of default ops
CORE: vmm_msi: Add common msi_index for both PCIe and Platform MSIs
CORE: vmm_host_irq: Allow irqchip drivers to mark chained interrupts
CORE: vmm_devres: Add custom action APIs
CORE: vmm_host_irqext: Fix extended IRQ allocations
LIBS: bitops: Improve get_count_order() implementation
DRIVERS: irqchip: Add RISC-V incoming MSI controller driver
DRIVERS: irqchip/riscv-aplic: Add support for MSI-mode
RISC-V: Enable RISC-V IMSIC in RV32 and RV64 defconfigs

arch/riscv/configs/generic-32b-defconfig | 1 +
arch/riscv/configs/generic-64b-defconfig | 1 +
commands/cmd_host.c | 4 +-
core/include/vmm_devres.h | 8 +
core/include/vmm_host_irq.h | 28 +
core/include/vmm_host_irqdomain.h | 8 +-
core/include/vmm_msi.h | 17 +-
core/vmm_devres.c | 54 +
core/vmm_host_irq.c | 54 +
core/vmm_host_irqdomain.c | 72 +-
core/vmm_host_irqext.c | 3 +-
core/vmm_msi.c | 123 ++-
core/vmm_platform_msi.c | 4 +-
drivers/include/drv/irqchip/riscv-imsic.h | 87 ++
drivers/irqchip/irq-riscv-aplic.c | 196 +++-
drivers/irqchip/irq-riscv-imsic.c | 1095 +++++++++++++++++++++
drivers/irqchip/objects.mk | 1 +
drivers/irqchip/openconf.cfg | 11 +
libs/include/libs/bitops.h | 31 +-
19 files changed, 1701 insertions(+), 97 deletions(-)
create mode 100644 drivers/include/drv/irqchip/riscv-imsic.h
create mode 100644 drivers/irqchip/irq-riscv-imsic.c

--
2.34.1

Anup Patel

unread,
Apr 27, 2022, 11:51:53 PM4/27/22
to xvisor...@googlegroups.com, Anup Patel
We should use default irq priority, enable threshold, and disable
threshold same as the Linux APLIC driver.

Signed-off-by: Anup Patel <apa...@ventanamicro.com>
---
drivers/irqchip/irq-riscv-aplic.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/irqchip/irq-riscv-aplic.c b/drivers/irqchip/irq-riscv-aplic.c
index 270f2d79..6374e8ea 100644
--- a/drivers/irqchip/irq-riscv-aplic.c
+++ b/drivers/irqchip/irq-riscv-aplic.c
@@ -48,11 +48,11 @@
#define MODULE_INIT aplic_init
#define MODULE_EXIT aplic_exit

-#define APLIC_DEFAULT_PRIORITY 0
+#define APLIC_DEFAULT_PRIORITY 1
#define APLIC_DISABLE_IDELIVERY 0
#define APLIC_ENABLE_IDELIVERY 1
-#define APLIC_DISABLE_ITHRESHOLD APLIC_DEFAULT_PRIORITY
-#define APLIC_ENABLE_ITHRESHOLD (APLIC_DEFAULT_PRIORITY + 1)
+#define APLIC_DISABLE_ITHRESHOLD 1
+#define APLIC_ENABLE_ITHRESHOLD 0

struct aplic_msi {
unsigned int hw_irq;
--
2.34.1

Anup Patel

unread,
Apr 27, 2022, 11:51:55 PM4/27/22
to xvisor...@googlegroups.com, Anup Patel
We add alloc() and free() domain operations to allow irqchip drivers
influence the allocation of hardware interrupt lines.

Signed-off-by: Anup Patel <apa...@ventanamicro.com>
---
core/include/vmm_host_irqdomain.h | 8 +++-
core/vmm_host_irqdomain.c | 72 ++++++++++++++++++++-----------
core/vmm_msi.c | 2 +-
3 files changed, 55 insertions(+), 27 deletions(-)

diff --git a/core/include/vmm_host_irqdomain.h b/core/include/vmm_host_irqdomain.h
index 04c38669..672eeb51 100644
--- a/core/include/vmm_host_irqdomain.h
+++ b/core/include/vmm_host_irqdomain.h
@@ -42,6 +42,8 @@ struct vmm_host_irqdomain;
* @unmap: Dispose of such a mapping
* @xlate: Given a device tree node and interrupt specifier, decode
* the hardware irq number and linux irq type value.
+ * @alloc: Allocate a specified number of hardware irqs
+ * @free: Free hardware irqs
*
* Functions below are provided by the driver and called whenever a new
* mapping is created or an old mapping is disposed. The driver can then
@@ -58,6 +60,10 @@ struct vmm_host_irqdomain_ops {
struct vmm_devtree_node *node,
const u32 *intspec, unsigned int intsize,
unsigned long *out_hwirq, unsigned int *out_type);
+ int (*alloc)(struct vmm_host_irqdomain *d,
+ unsigned int nr_irqs, void *arg);
+ void (*free)(struct vmm_host_irqdomain *d,
+ unsigned int hwirq, unsigned int nr_irqs);
};

/**
@@ -119,7 +125,7 @@ void vmm_host_irqdomain_dispose_mapping(unsigned int hirq);

/** Allocate and map host IRQs */
int vmm_host_irqdomain_alloc(struct vmm_host_irqdomain *domain,
- unsigned int irq_count);
+ unsigned int irq_count, void *arg);

/** Free and unmap host IRQs */
void vmm_host_irqdomain_free(struct vmm_host_irqdomain *domain,
diff --git a/core/vmm_host_irqdomain.c b/core/vmm_host_irqdomain.c
index c33b6b5f..9456d449 100644
--- a/core/vmm_host_irqdomain.c
+++ b/core/vmm_host_irqdomain.c
@@ -251,7 +251,8 @@ void vmm_host_irqdomain_dispose_mapping(unsigned int hirq)
}

int vmm_host_irqdomain_alloc(struct vmm_host_irqdomain *domain,
- unsigned int irq_count)
+ unsigned int irq_count,
+ void *arg)
{
int rc;
irq_flags_t flags;
@@ -262,25 +263,39 @@ int vmm_host_irqdomain_alloc(struct vmm_host_irqdomain *domain,
return VMM_EINVALID;
}

- vmm_spin_lock_irqsave_lite(&domain->bmap_lock, flags);
- count = 0;
- for (hwirq = 0; hwirq < domain->count; hwirq++) {
- if (bitmap_isset(domain->bmap, hwirq))
+ if (domain->ops->alloc) {
+ rc = domain->ops->alloc(domain, irq_count, arg);
+ if (rc < 0) {
+ return rc;
+ }
+ hwirq = rc;
+ vmm_spin_lock_irqsave_lite(&domain->bmap_lock, flags);
+ bitmap_set(domain->bmap, hwirq, irq_count);
+ vmm_spin_unlock_irqrestore_lite(&domain->bmap_lock, flags);
+ } else {
+ vmm_spin_lock_irqsave_lite(&domain->bmap_lock, flags);
+ if (!found) {
count = 0;
- else
- count++;
- if (count == irq_count) {
- found = true;
- hwirq = hwirq - (count - 1);
- break;
+ for (hwirq = 0; hwirq < domain->count; hwirq++) {
+ if (bitmap_isset(domain->bmap, hwirq))
+ count = 0;
+ else
+ count++;
+ if (count == irq_count) {
+ found = true;
+ hwirq = hwirq - (count - 1);
+ break;
+ }
+ }
}
- }
- if (!found) {
+ if (!found) {
+ vmm_spin_unlock_irqrestore_lite(&domain->bmap_lock,
+ flags);
+ return VMM_ENOENT;
+ }
+ bitmap_set(domain->bmap, hwirq, irq_count);
vmm_spin_unlock_irqrestore_lite(&domain->bmap_lock, flags);
- return VMM_ENOENT;
}
- bitmap_set(domain->bmap, hwirq, irq_count);
- vmm_spin_unlock_irqrestore_lite(&domain->bmap_lock, flags);

hirq = domain->base + hwirq;
for (i = 0; i < irq_count; i++) {
@@ -289,6 +304,14 @@ int vmm_host_irqdomain_alloc(struct vmm_host_irqdomain *domain,
for (j = 0; j < i; j++) {
__irqdomain_dispose_mapping(domain, hirq + j);
}
+ vmm_spin_lock_irqsave_lite(&domain->bmap_lock, flags);
+ bitmap_clear(domain->bmap, hwirq, irq_count);
+ vmm_spin_unlock_irqrestore_lite(&domain->bmap_lock,
+ flags);
+
+ if (domain->ops->free) {
+ domain->ops->free(domain, hwirq, irq_count);
+ }
return rc;
}
}
@@ -310,17 +333,16 @@ void vmm_host_irqdomain_free(struct vmm_host_irqdomain *domain,
return;

for (i = 0; i < irq_count; i++) {
- hwirq = hirq - domain->base + i;
+ __irqdomain_dispose_mapping(domain, hirq + i);
+ }

- vmm_spin_lock_irqsave_lite(&domain->bmap_lock, flags);
- if (!bitmap_isset(domain->bmap, hwirq)) {
- vmm_spin_unlock_irqrestore_lite(&domain->bmap_lock, flags);
- continue;
- }
- bitmap_clear(domain->bmap, hwirq, 1);
- vmm_spin_unlock_irqrestore_lite(&domain->bmap_lock, flags);
+ hwirq = hirq - domain->base;
+ vmm_spin_lock_irqsave_lite(&domain->bmap_lock, flags);
+ bitmap_clear(domain->bmap, hwirq, irq_count);
+ vmm_spin_unlock_irqrestore_lite(&domain->bmap_lock, flags);

- __irqdomain_dispose_mapping(domain, hirq);
+ if (domain->ops->free) {
+ domain->ops->free(domain, hwirq, irq_count);
}
}

diff --git a/core/vmm_msi.c b/core/vmm_msi.c
index 02e09b54..a6111373 100644
--- a/core/vmm_msi.c
+++ b/core/vmm_msi.c
@@ -232,7 +232,7 @@ int vmm_msi_domain_alloc_irqs(struct vmm_msi_domain *domain,
ops->set_desc(&arg, desc);

hirq = vmm_host_irqdomain_alloc(domain->parent,
- desc->nvec_used);
+ desc->nvec_used, &arg);
if (hirq < 0) {
ret = VMM_ENOSPC;
goto fail_handle_error;
--
2.34.1

Anup Patel

unread,
Apr 27, 2022, 11:51:57 PM4/27/22
to xvisor...@googlegroups.com, Anup Patel
The compose_msi_msg() is irqchip specific and not MSI domain
specific so we move this operation from MSI domain operations
to irqchip operations.

Signed-off-by: Anup Patel <apa...@ventanamicro.com>
---
core/include/vmm_host_irq.h | 7 +++++++
core/include/vmm_msi.h | 6 ------
core/vmm_host_irq.c | 15 +++++++++++++++
core/vmm_msi.c | 20 +++++++++-----------
4 files changed, 31 insertions(+), 17 deletions(-)

diff --git a/core/include/vmm_host_irq.h b/core/include/vmm_host_irq.h
index 2c679617..63cd3ac5 100644
--- a/core/include/vmm_host_irq.h
+++ b/core/include/vmm_host_irq.h
@@ -104,6 +104,7 @@ enum vmm_irq_return {
};

struct vmm_host_irq;
+struct vmm_msi_msg;

typedef enum vmm_irq_return vmm_irq_return_t;
typedef void (*vmm_host_irq_handler_t)(struct vmm_host_irq *, u32, void *);
@@ -125,6 +126,7 @@ struct vmm_host_irq_action {
* @irq_eoi: end of interrupt
* @irq_set_affinity: set the CPU affinity on SMP machines
* @irq_set_type: set the flow type (VMM_IRQ_TYPE_LEVEL/etc.) of an IRQ
+ * @irq_compose_msi_msg: compose MSI message for given interrupt source
* @irq_get_routed_state: get the routed state of an IRQ
* @irq_set_routed_state: set the routed state of an IRQ
*/
@@ -143,6 +145,8 @@ struct vmm_host_irq_chip {
int (*irq_set_type)(struct vmm_host_irq *irq, u32 flow_type);
void (*irq_raise)(struct vmm_host_irq *irq,
const struct vmm_cpumask *dest);
+ void (*irq_compose_msi_msg)(struct vmm_host_irq *irq,
+ struct vmm_msi_msg *msg);
u32 (*irq_get_routed_state)(struct vmm_host_irq *irq, u32 mask);
void (*irq_set_routed_state)(struct vmm_host_irq *irq,
u32 val, u32 mask);
@@ -404,6 +408,9 @@ static inline int vmm_host_irq_disable(u32 hirq)
int vmm_host_irq_raise(u32 hirq,
const struct vmm_cpumask *dest);

+/** Compose a MSI message for given host irq */
+int vmm_host_irq_compose_msi_msg(u32 hirq, struct vmm_msi_msg *msg);
+
/** Find a host irq with matching state mask */
int vmm_host_irq_find(u32 hirq_start, u32 state_mask, u32 *hirq);

diff --git a/core/include/vmm_msi.h b/core/include/vmm_msi.h
index 70b39038..b0d989aa 100644
--- a/core/include/vmm_msi.h
+++ b/core/include/vmm_msi.h
@@ -27,7 +27,6 @@
#include <vmm_types.h>
#include <vmm_devtree.h>
#include <vmm_devdrv.h>
-#include <vmm_host_irqdomain.h>
#include <libs/list.h>

struct vmm_msi_msg {
@@ -147,7 +146,6 @@ typedef void (*vmm_irq_write_msi_msg_t)(struct vmm_msi_desc *desc,
* @msi_finish: Optional callback to finalize the allocation
* @set_desc: Set the msi descriptor for an interrupt
* @handle_error: Optional error handler if the allocation fails
- * @msi_compose_msg Domain specific callback to compose MSI mesage
* @msi_write_msg Domain specific callback to write MSI message
*
* All of the above callbacks are used by vmm_msi_domain_alloc_irqs()
@@ -169,10 +167,6 @@ struct vmm_msi_domain_ops {
struct vmm_msi_desc *desc);
int (*handle_error)(struct vmm_msi_domain *domain,
struct vmm_msi_desc *desc, int error);
- int (*msi_compose_msg)(struct vmm_msi_domain *domain,
- struct vmm_msi_desc *desc,
- unsigned int hirq, unsigned int hwirq,
- struct vmm_msi_msg *msg);
void (*msi_write_msg)(struct vmm_msi_domain *domain,
struct vmm_msi_desc *desc,
unsigned int hirq, unsigned int hwirq,
diff --git a/core/vmm_host_irq.c b/core/vmm_host_irq.c
index a5c4adb6..418d6a31 100644
--- a/core/vmm_host_irq.c
+++ b/core/vmm_host_irq.c
@@ -529,6 +529,21 @@ int vmm_host_irq_raise(u32 hirq,
return VMM_OK;
}

+int vmm_host_irq_compose_msi_msg(u32 hirq, struct vmm_msi_msg *msg)
+{
+ struct vmm_host_irq *irq;
+
+ if (!msg)
+ return VMM_EINVALID;
+ if (NULL == (irq = vmm_host_irq_get(hirq)))
+ return VMM_ENOTAVAIL;
+ if (!irq->chip || !irq->chip->irq_compose_msi_msg)
+ return VMM_ENOSYS;
+
+ irq->chip->irq_compose_msi_msg(irq, msg);
+ return VMM_OK;
+}
+
int vmm_host_irq_find(u32 hirq_start, u32 state_mask, u32 *hirq)
{
u32 ite;
diff --git a/core/vmm_msi.c b/core/vmm_msi.c
index a6111373..eb9043f1 100644
--- a/core/vmm_msi.c
+++ b/core/vmm_msi.c
@@ -23,6 +23,8 @@

#include <vmm_error.h>
#include <vmm_heap.h>
+#include <vmm_host_irq.h>
+#include <vmm_host_irqdomain.h>
#include <vmm_spinlocks.h>
#include <vmm_msi.h>
#include <libs/stringlib.h>
@@ -261,17 +263,13 @@ int vmm_msi_domain_alloc_irqs(struct vmm_msi_domain *domain,

/* If everything went fine then we write MSI messages */
for_each_msi_entry(desc, dev) {
- if (ops->msi_compose_msg && ops->msi_write_msg) {
- hirq = desc->hirq;
- hwirq = vmm_host_irqdomain_to_hwirq(domain->parent,
- hirq);
- for (i = 0; i < desc->nvec_used; i++) {
- ret = ops->msi_compose_msg(domain, desc,
- hirq + i, hwirq + i, &msg);
- BUG_ON(ret < 0);
- ops->msi_write_msg(domain, desc,
- hirq + i, hwirq + i, &msg);
- }
+ hirq = desc->hirq;
+ hwirq = vmm_host_irqdomain_to_hwirq(domain->parent, hirq);
+ for (i = 0; i < desc->nvec_used; i++) {
+ ret = vmm_host_irq_compose_msi_msg(hirq + i, &msg);
+ BUG_ON(ret < 0);
+ ops->msi_write_msg(domain, desc,
+ hirq + i, hwirq + i, &msg);
}
}

--
2.34.1

Anup Patel

unread,
Apr 27, 2022, 11:51:59 PM4/27/22
to xvisor...@googlegroups.com, Anup Patel
The vmm_msi_domain_alloc/free_irqs() should use "msg" instance available
in "struct vmm_msi_desc" for preparing and writing MSI messages so that
we can track last written MSI message.

Signed-off-by: Anup Patel <apa...@ventanamicro.com>
---
core/vmm_msi.c | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)

diff --git a/core/vmm_msi.c b/core/vmm_msi.c
index eb9043f1..45dff281 100644
--- a/core/vmm_msi.c
+++ b/core/vmm_msi.c
@@ -218,7 +218,6 @@ int vmm_msi_domain_alloc_irqs(struct vmm_msi_domain *domain,
int nvec)
{
vmm_msi_alloc_info_t arg;
- struct vmm_msi_msg msg;
struct vmm_msi_desc *desc;
int i, ret = VMM_OK, hwirq, hirq = -1;
struct vmm_msi_domain_ops *ops = domain->ops;
@@ -266,10 +265,11 @@ int vmm_msi_domain_alloc_irqs(struct vmm_msi_domain *domain,
hirq = desc->hirq;
hwirq = vmm_host_irqdomain_to_hwirq(domain->parent, hirq);
for (i = 0; i < desc->nvec_used; i++) {
- ret = vmm_host_irq_compose_msi_msg(hirq + i, &msg);
+ memset(&desc->msg, 0, sizeof(desc->msg));
+ ret = vmm_host_irq_compose_msi_msg(hirq + i, &desc->msg);
BUG_ON(ret < 0);
ops->msi_write_msg(domain, desc,
- hirq + i, hwirq + i, &msg);
+ hirq + i, hwirq + i, &desc->msg);
}
}

@@ -287,15 +287,12 @@ void vmm_msi_domain_free_irqs(struct vmm_msi_domain *domain,
struct vmm_device *dev)
{
unsigned int i, hirq, hwirq;
- struct vmm_msi_msg msg;
struct vmm_msi_desc *desc;
struct vmm_msi_domain_ops *ops;

if (!domain || !dev)
return;
-
ops = domain->ops;
- memset(&msg, 0, sizeof(msg));

for_each_msi_entry(desc, dev) {
/*
@@ -310,10 +307,12 @@ void vmm_msi_domain_free_irqs(struct vmm_msi_domain *domain,
hirq = desc->hirq;
hwirq = vmm_host_irqdomain_to_hwirq(domain->parent, hirq);

+ memset(&desc->msg, 0, sizeof(desc->msg));
+
if (ops->msi_write_msg) {
for (i = 0; i < desc->nvec_used; i++) {
ops->msi_write_msg(domain, desc,
- hirq + i, hwirq + i, &msg);
+ hirq + i, hwirq + i, &desc->msg);
}
}

--
2.34.1

Anup Patel

unread,
Apr 27, 2022, 11:52:01 PM4/27/22
to xvisor...@googlegroups.com, Anup Patel, Anup Patel
We add vmm_msi_domain_write_msg() API which allows irqchip drivers
to explicitly update MSI message in the device when the affinity of
corresponding host IRQ changes.

Signed-off-by: Anup Patel <an...@brainfault.org>
---
core/include/vmm_host_irq.h | 7 +++++++
core/include/vmm_msi.h | 8 +++++++-
core/vmm_host_irq.c | 17 +++++++++++++++++
core/vmm_msi.c | 30 +++++++++++++++++++++++-------
4 files changed, 54 insertions(+), 8 deletions(-)

diff --git a/core/include/vmm_host_irq.h b/core/include/vmm_host_irq.h
index 63cd3ac5..8c65e18c 100644
--- a/core/include/vmm_host_irq.h
+++ b/core/include/vmm_host_irq.h
@@ -163,6 +163,7 @@ struct vmm_host_irq {
u32 count[CONFIG_CPU_COUNT];
void *chip_data;
struct vmm_host_irq_chip *chip;
+ void *msi_data;
vmm_host_irq_handler_t handler;
void *handler_data;
vmm_rwlock_t action_lock[CONFIG_CPU_COUNT];
@@ -224,6 +225,12 @@ int vmm_host_irq_set_chip_data(u32 hirq, void *chip_data);
/** Get host irq chip data from host irq instance */
void *vmm_host_irq_get_chip_data(struct vmm_host_irq *irq);

+/** Set host irq MSI data for given host irq number */
+int vmm_host_irq_set_msi_data(u32 hirq, void *msi_data);
+
+/** Get host irq MSI data from host irq instance */
+void *vmm_host_irq_get_msi_data(struct vmm_host_irq *irq);
+
/** Set host irq handler for given host irq number
* NOTE: For second argument, mention one of the
* vmm_handle_xxxxx functions from below
diff --git a/core/include/vmm_msi.h b/core/include/vmm_msi.h
index b0d989aa..264738a5 100644
--- a/core/include/vmm_msi.h
+++ b/core/include/vmm_msi.h
@@ -45,12 +45,16 @@ struct vmm_platform_msi_desc {
u16 msi_index;
};

+struct vmm_host_irq;
+struct vmm_msi_domain;
+
/**
* Descriptor structure for MSI based interrupts
* @list: List head for management
* @hirq: The base interrupt number
* @nvec_used: The number of vectors used
* @dev: Pointer to the device which uses this descriptor
+ * @domain: Pointer to the MSI domain which uses this descriptor
* @msg: The last set MSI message cached for reuse
*
* @masked: [PCI MSI/X] Mask bits
@@ -71,6 +75,7 @@ struct vmm_msi_desc {
unsigned int hirq;
unsigned int nvec_used;
struct vmm_device *dev;
+ struct vmm_msi_domain *domain;
struct vmm_msi_msg msg;

union {
@@ -133,7 +138,6 @@ typedef struct vmm_msi_alloc_info {
#define for_each_msi_entry(desc, dev) \
list_for_each_entry((desc), dev_to_msi_list((dev)), list)

-struct vmm_msi_domain;
typedef void (*vmm_irq_write_msi_msg_t)(struct vmm_msi_desc *desc,
struct vmm_msi_msg *msg);

@@ -236,6 +240,8 @@ static inline void *vmm_msi_domain_data(struct vmm_msi_domain *domain)
struct vmm_msi_domain *vmm_msi_find_domain(struct vmm_devtree_node *fwnode,
enum vmm_msi_domain_types type);

+void vmm_msi_domain_write_msg(struct vmm_host_irq *irq);
+
int vmm_msi_domain_alloc_irqs(struct vmm_msi_domain *domain,
struct vmm_device *dev,
int nvec);
diff --git a/core/vmm_host_irq.c b/core/vmm_host_irq.c
index 418d6a31..f0b1e788 100644
--- a/core/vmm_host_irq.c
+++ b/core/vmm_host_irq.c
@@ -248,6 +248,22 @@ void *vmm_host_irq_get_chip_data(struct vmm_host_irq *irq)
return (irq) ? irq->chip_data : NULL;
}

+int vmm_host_irq_set_msi_data(u32 hirq, void *msi_data)
+{
+ struct vmm_host_irq *irq = NULL;
+
+ if (NULL == (irq = vmm_host_irq_get(hirq)))
+ return VMM_EFAIL;
+
+ irq->msi_data = msi_data;
+ return VMM_OK;
+}
+
+void *vmm_host_irq_get_msi_data(struct vmm_host_irq *irq)
+{
+ return (irq) ? irq->msi_data : NULL;
+}
+
int vmm_host_irq_set_handler(u32 hirq, vmm_host_irq_handler_t handler)
{
struct vmm_host_irq *irq = NULL;
@@ -738,6 +754,7 @@ void __vmm_host_irq_init_desc(struct vmm_host_irq *irq,
}
irq->chip = NULL;
irq->chip_data = NULL;
+ irq->msi_data = NULL;
irq->handler = NULL;
irq->handler_data = NULL;
for (cpu = 0; cpu < CONFIG_CPU_COUNT; cpu++) {
diff --git a/core/vmm_msi.c b/core/vmm_msi.c
index 45dff281..d5430a8f 100644
--- a/core/vmm_msi.c
+++ b/core/vmm_msi.c
@@ -213,6 +213,25 @@ struct vmm_msi_domain *vmm_msi_find_domain(struct vmm_devtree_node *fwnode,
return domain;
}

+void vmm_msi_domain_write_msg(struct vmm_host_irq *irq)
+{
+ struct vmm_msi_desc *desc = vmm_host_irq_get_msi_data(irq);
+ struct vmm_msi_domain_ops *ops;
+ struct vmm_msi_domain *domain;
+ int ret;
+
+ if (!desc || !desc->domain)
+ return;
+ domain = desc->domain;
+ ops = domain->ops;
+
+ memset(&desc->msg, 0, sizeof(desc->msg));
+ ret = vmm_host_irq_compose_msi_msg(irq->num, &desc->msg);
+ BUG_ON(ret < 0);
+ ops->msi_write_msg(domain, desc,
+ irq->num, irq->hwirq, &desc->msg);
+}
+
int vmm_msi_domain_alloc_irqs(struct vmm_msi_domain *domain,
struct vmm_device *dev,
int nvec)
@@ -240,8 +259,10 @@ int vmm_msi_domain_alloc_irqs(struct vmm_msi_domain *domain,
}
hwirq = vmm_host_irqdomain_to_hwirq(domain->parent, hirq);
desc->hirq = hirq;
+ desc->domain = domain;

for (i = 0; i < desc->nvec_used; i++) {
+ vmm_host_irq_set_msi_data(hirq + i, desc);
ret = ops->msi_init(domain, hirq + i,
hwirq + i, &arg);
if (ret < 0) {
@@ -262,14 +283,9 @@ int vmm_msi_domain_alloc_irqs(struct vmm_msi_domain *domain,

/* If everything went fine then we write MSI messages */
for_each_msi_entry(desc, dev) {
- hirq = desc->hirq;
- hwirq = vmm_host_irqdomain_to_hwirq(domain->parent, hirq);
for (i = 0; i < desc->nvec_used; i++) {
- memset(&desc->msg, 0, sizeof(desc->msg));
- ret = vmm_host_irq_compose_msi_msg(hirq + i, &desc->msg);
- BUG_ON(ret < 0);
- ops->msi_write_msg(domain, desc,
- hirq + i, hwirq + i, &desc->msg);
+ vmm_msi_domain_write_msg(
+ vmm_host_irq_get(desc->hirq + i));
}
}

--
2.34.1

Anup Patel

unread,
Apr 27, 2022, 11:52:04 PM4/27/22
to xvisor...@googlegroups.com, Anup Patel
We provide default version of all MSI domain ops so that we don't
have to check before using these ops.

Signed-off-by: Anup Patel <apa...@ventanamicro.com>
---
core/vmm_msi.c | 78 +++++++++++++++++++++++++++++++++-----------------
1 file changed, 51 insertions(+), 27 deletions(-)

diff --git a/core/vmm_msi.c b/core/vmm_msi.c
index d5430a8f..d2604296 100644
--- a/core/vmm_msi.c
+++ b/core/vmm_msi.c
@@ -32,6 +32,24 @@
static DEFINE_SPINLOCK(msi_lock);
static LIST_HEAD(msi_domain_list);

+static int msi_domain_ops_init(struct vmm_msi_domain *domain,
+ unsigned int hirq, unsigned int hwirq,
+ vmm_msi_alloc_info_t *arg)
+{
+ return 0;
+}
+
+static void msi_domain_ops_free(struct vmm_msi_domain *domain,
+ unsigned int hirq)
+{
+}
+
+static int msi_domain_ops_check(struct vmm_msi_domain *domain,
+ struct vmm_device *dev)
+{
+ return 0;
+}
+
static int msi_domain_ops_prepare(struct vmm_msi_domain *domain,
struct vmm_device *dev,
int nvec, vmm_msi_alloc_info_t *arg)
@@ -40,30 +58,38 @@ static int msi_domain_ops_prepare(struct vmm_msi_domain *domain,
return 0;
}

+static void msi_domain_ops_finish(vmm_msi_alloc_info_t *arg, int retval)
+{
+}
+
static void msi_domain_ops_set_desc(vmm_msi_alloc_info_t *arg,
struct vmm_msi_desc *desc)
{
arg->desc = desc;
}

-static int msi_domain_ops_init(struct vmm_msi_domain *domain,
- unsigned int hirq, unsigned int hwirq,
- vmm_msi_alloc_info_t *arg)
+static int msi_domain_ops_handle_error(struct vmm_msi_domain *domain,
+ struct vmm_msi_desc *desc, int error)
{
- return 0;
+ return error;
}

-static int msi_domain_ops_check(struct vmm_msi_domain *domain,
- struct vmm_device *dev)
+static void msi_domain_ops_write_msg(struct vmm_msi_domain *domain,
+ struct vmm_msi_desc *desc,
+ unsigned int hirq, unsigned int hwirq,
+ struct vmm_msi_msg *msg)
{
- return 0;
}

static struct vmm_msi_domain_ops msi_domain_ops_default = {
.msi_init = msi_domain_ops_init,
+ .msi_free = msi_domain_ops_free,
.msi_check = msi_domain_ops_check,
.msi_prepare = msi_domain_ops_prepare,
+ .msi_finish = msi_domain_ops_finish,
.set_desc = msi_domain_ops_set_desc,
+ .handle_error = msi_domain_ops_handle_error,
+ .msi_write_msg = msi_domain_ops_write_msg,
};

static void vmm_msi_domain_update_dom_ops(struct vmm_msi_domain *domain)
@@ -77,12 +103,20 @@ static void vmm_msi_domain_update_dom_ops(struct vmm_msi_domain *domain)

if (ops->msi_init == NULL)
ops->msi_init = msi_domain_ops_default.msi_init;
+ if (ops->msi_free == NULL)
+ ops->msi_free = msi_domain_ops_default.msi_free;
if (ops->msi_check == NULL)
ops->msi_check = msi_domain_ops_default.msi_check;
if (ops->msi_prepare == NULL)
ops->msi_prepare = msi_domain_ops_default.msi_prepare;
+ if (ops->msi_finish == NULL)
+ ops->msi_finish = msi_domain_ops_default.msi_finish;
if (ops->set_desc == NULL)
ops->set_desc = msi_domain_ops_default.set_desc;
+ if (ops->handle_error == NULL)
+ ops->handle_error = msi_domain_ops_default.handle_error;
+ if (ops->msi_write_msg == NULL)
+ ops->msi_write_msg = msi_domain_ops_default.msi_write_msg;
}

struct vmm_msi_desc *vmm_alloc_msi_entry(struct vmm_device *dev)
@@ -266,11 +300,8 @@ int vmm_msi_domain_alloc_irqs(struct vmm_msi_domain *domain,
ret = ops->msi_init(domain, hirq + i,
hwirq + i, &arg);
if (ret < 0) {
- if (ops->msi_free) {
- for (i--; i > 0; i--)
- ops->msi_free(domain,
- hirq + i);
- }
+ for (i--; i > 0; i--)
+ ops->msi_free(domain, hirq + i);
vmm_host_irqdomain_free(domain->parent,
desc->hirq, desc->nvec_used);
goto fail_handle_error;
@@ -278,8 +309,7 @@ int vmm_msi_domain_alloc_irqs(struct vmm_msi_domain *domain,
}
}

- if (ops->msi_finish)
- ops->msi_finish(&arg, 0);
+ ops->msi_finish(&arg, 0);

/* If everything went fine then we write MSI messages */
for_each_msi_entry(desc, dev) {
@@ -292,10 +322,8 @@ int vmm_msi_domain_alloc_irqs(struct vmm_msi_domain *domain,
return VMM_OK;

fail_handle_error:
- if (ops->handle_error)
- ret = ops->handle_error(domain, desc, ret);
- if (ops->msi_finish)
- ops->msi_finish(&arg, ret);
+ ret = ops->handle_error(domain, desc, ret);
+ ops->msi_finish(&arg, ret);
return ret;
}

@@ -325,17 +353,13 @@ void vmm_msi_domain_free_irqs(struct vmm_msi_domain *domain,

memset(&desc->msg, 0, sizeof(desc->msg));

- if (ops->msi_write_msg) {
- for (i = 0; i < desc->nvec_used; i++) {
- ops->msi_write_msg(domain, desc,
- hirq + i, hwirq + i, &desc->msg);
- }
+ for (i = 0; i < desc->nvec_used; i++) {
+ ops->msi_write_msg(domain, desc,
+ hirq + i, hwirq + i, &desc->msg);
}

- if (ops->msi_free) {
- for (i = 0; i < desc->nvec_used; i++) {
- ops->msi_free(domain, hirq + i);
- }
+ for (i = 0; i < desc->nvec_used; i++) {
+ ops->msi_free(domain, hirq + i);
}

vmm_host_irqdomain_free(domain->parent,
--
2.34.1

Anup Patel

unread,
Apr 27, 2022, 11:52:06 PM4/27/22
to xvisor...@googlegroups.com, Anup Patel
We need msi_index for both PCIe and Platform MSIs so we move it
to "struct vmm_msi_desc" from "struct vmm_platform_msi_desc".

Signed-off-by: Anup Patel <apa...@ventanamicro.com>
---
core/include/vmm_msi.h | 3 ++-
core/vmm_platform_msi.c | 4 ++--
2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/core/include/vmm_msi.h b/core/include/vmm_msi.h
index 264738a5..c27e8bd8 100644
--- a/core/include/vmm_msi.h
+++ b/core/include/vmm_msi.h
@@ -38,7 +38,6 @@ struct vmm_msi_msg {
/**
* Platform device specific msi descriptor data
* @msi_priv_data: Pointer to platform private data
- * @msi_index: The index of the MSI descriptor for multi MSI
*/
struct vmm_platform_msi_desc {
struct vmm_platform_msi_priv_data *msi_priv_data;
@@ -53,6 +52,7 @@ struct vmm_msi_domain;
* @list: List head for management
* @hirq: The base interrupt number
* @nvec_used: The number of vectors used
+ * @msi_index: The index of the MSI descriptor for multi MSI
* @dev: Pointer to the device which uses this descriptor
* @domain: Pointer to the MSI domain which uses this descriptor
* @msg: The last set MSI message cached for reuse
@@ -74,6 +74,7 @@ struct vmm_msi_desc {
struct dlist list;
unsigned int hirq;
unsigned int nvec_used;
+ unsigned int msi_index;
struct vmm_device *dev;
struct vmm_msi_domain *domain;
struct vmm_msi_msg msg;
diff --git a/core/vmm_platform_msi.c b/core/vmm_platform_msi.c
index 0d3fadbf..3354b7a1 100644
--- a/core/vmm_platform_msi.c
+++ b/core/vmm_platform_msi.c
@@ -174,7 +174,7 @@ static int platform_msi_alloc_descs_with_irq(struct vmm_device *dev,
if (!list_empty(dev_to_msi_list(dev))) {
desc = list_last_entry(dev_to_msi_list(dev),
struct vmm_msi_desc, list);
- base = desc->platform.msi_index + 1;
+ base = desc->msi_index + 1;
}

for (i = 0; i < nvec; i++) {
@@ -183,7 +183,7 @@ static int platform_msi_alloc_descs_with_irq(struct vmm_device *dev,
break;

desc->platform.msi_priv_data = data;
- desc->platform.msi_index = base + i;
+ desc->msi_index = base + i;
desc->nvec_used = 1;
desc->hirq = hirq ? hirq + i : 0;

--
2.34.1

Anup Patel

unread,
Apr 27, 2022, 11:52:08 PM4/27/22
to xvisor...@googlegroups.com, Anup Patel
Currently, we have no way for irqchip drivers to mark chained interrupts
so such interrupts show-up in output of "host irq stats" command. This
patch adds minimal support to mark chained interrupts and also updates
"host irq stats" command to not print chanined interrupts.

Signed-off-by: Anup Patel <apa...@ventanamicro.com>
---
commands/cmd_host.c | 4 +++-
core/include/vmm_host_irq.h | 14 ++++++++++++++
core/vmm_host_irq.c | 22 ++++++++++++++++++++++
3 files changed, 39 insertions(+), 1 deletion(-)

diff --git a/commands/cmd_host.c b/commands/cmd_host.c
index a1ea7ff4..9de09ec5 100644
--- a/commands/cmd_host.c
+++ b/commands/cmd_host.c
@@ -218,7 +218,9 @@ static void irq_stats_print(struct vmm_chardev *cdev, u32 irqno)

irq = vmm_host_irq_get(irqno);
irq_name = vmm_host_irq_get_name(irq);
- if (vmm_host_irq_is_disabled(irq) || !irq_name) {
+ if (!irq || !irq_name ||
+ vmm_host_irq_is_disabled(irq) ||
+ vmm_host_irq_is_chained(irq)) {
return;
}
chip = vmm_host_irq_get_chip(irq);
diff --git a/core/include/vmm_host_irq.h b/core/include/vmm_host_irq.h
index 8c65e18c..fd9fb417 100644
--- a/core/include/vmm_host_irq.h
+++ b/core/include/vmm_host_irq.h
@@ -60,6 +60,7 @@ enum vmm_irq_trigger_types {
* @VMM_IRQ_STATE_ROUTED - Interrupt is routed to some guest
* @VMM_IRQ_STATE_IPI - Interrupt is an inter-processor interrupt
* @VMM_IRQ_STATE_EXTENDED - Interrupt is an extended interrupt
+ * @VMM_IRQ_STATE_CHAINED - Interrupt is a chained interrupt
*/
enum vmm_irq_states {
VMM_IRQ_STATE_TRIGGER_MASK = 0xf,
@@ -69,6 +70,7 @@ enum vmm_irq_states {
VMM_IRQ_STATE_ROUTED = (1 << 14),
VMM_IRQ_STATE_IPI = (1 << 15),
VMM_IRQ_STATE_EXTENDED = (1 << 16),
+ VMM_IRQ_STATE_CHAINED = (1 << 17),
};

/**
@@ -326,6 +328,12 @@ static inline bool vmm_host_irq_is_ipi(struct vmm_host_irq *irq)
return (irq->state & VMM_IRQ_STATE_IPI) ? TRUE : FALSE;
}

+/** Check if a host irq is a chained interrupt */
+static inline bool vmm_host_irq_is_chained(struct vmm_host_irq *irq)
+{
+ return (irq->state & VMM_IRQ_STATE_CHAINED) ? TRUE : FALSE;
+}
+
/** Check if a host irq is masked */
bool vmm_host_irq_is_masked(struct vmm_host_irq *irq);

@@ -393,6 +401,12 @@ int vmm_host_irq_mark_ipi(u32 hirq);
/** UnMark host irq as inter-processor interrupt */
int vmm_host_irq_unmark_ipi(u32 hirq);

+/** Mark host irq as chained interrupt */
+int vmm_host_irq_mark_chained(u32 hirq);
+
+/** UnMark host irq as chained interrupt */
+int vmm_host_irq_unmark_chained(u32 hirq);
+
/** Unmask a host irq (by default all irqs are masked) */
int vmm_host_irq_unmask(u32 hirq);

diff --git a/core/vmm_host_irq.c b/core/vmm_host_irq.c
index f0b1e788..9de7308c 100644
--- a/core/vmm_host_irq.c
+++ b/core/vmm_host_irq.c
@@ -465,6 +465,28 @@ int vmm_host_irq_unmark_ipi(u32 hirq)
return VMM_OK;
}

+int vmm_host_irq_mark_chained(u32 hirq)
+{
+ struct vmm_host_irq *irq;
+
+ if (NULL == (irq = vmm_host_irq_get(hirq)))
+ return VMM_ENOTAVAIL;
+
+ irq->state |= VMM_IRQ_STATE_CHAINED;
+ return VMM_OK;
+}
+
+int vmm_host_irq_unmark_chained(u32 hirq)
+{
+ struct vmm_host_irq *irq;
+
+ if (NULL == (irq = vmm_host_irq_get(hirq)))
+ return VMM_ENOTAVAIL;
+
+ irq->state &= ~VMM_IRQ_STATE_CHAINED;
+ return VMM_OK;
+}
+
bool vmm_host_irq_is_masked(struct vmm_host_irq *irq)
{
u32 percpu_state;
--
2.34.1

Anup Patel

unread,
Apr 27, 2022, 11:52:11 PM4/27/22
to xvisor...@googlegroups.com, Anup Patel
We add custion action APIs (similar to Linux) which allows device
drivers to register their own function in device resource managment.

Signed-off-by: Anup Patel <apa...@ventanamicro.com>
---
core/include/vmm_devres.h | 8 ++++++
core/vmm_devres.c | 54 +++++++++++++++++++++++++++++++++++++++
2 files changed, 62 insertions(+)

diff --git a/core/include/vmm_devres.h b/core/include/vmm_devres.h
index eb96795f..1276d72f 100644
--- a/core/include/vmm_devres.h
+++ b/core/include/vmm_devres.h
@@ -89,4 +89,12 @@ char *vmm_devm_strdup(struct vmm_device *dev, const char *s);
/** Resource-managed free */
void vmm_devm_free(struct vmm_device *dev, void *p);

+/** Add custom resource-managed action */
+int vmm_devm_add_action(struct vmm_device *dev,
+ void (*action)(void *), void *data);
+
+/** Remove custom resource-managed action */
+void vmm_devm_remove_action(struct vmm_device *dev,
+ void (*action)(void *), void *data);
+
#endif /* __VMM_DEVRES_H_ */
diff --git a/core/vmm_devres.c b/core/vmm_devres.c
index 0cc1f277..2e1e4ed9 100644
--- a/core/vmm_devres.c
+++ b/core/vmm_devres.c
@@ -342,3 +342,57 @@ void vmm_devm_free(struct vmm_device *dev, void *p)
WARN_ON(rc);
}

+/*
+ * Custom devres actions allow inserting a simple function call
+ * into the teardown sequence.
+ */
+
+struct action_devres {
+ void *data;
+ void (*action)(void *);
+};
+
+static int devm_action_match(struct vmm_device *dev, void *res, void *p)
+{
+ struct action_devres *devres = res;
+ struct action_devres *target = p;
+
+ return devres->action == target->action &&
+ devres->data == target->data;
+}
+
+static void devm_action_release(struct vmm_device *dev, void *res)
+{
+ struct action_devres *devres = res;
+
+ devres->action(devres->data);
+}
+
+int vmm_devm_add_action(struct vmm_device *dev,
+ void (*action)(void *), void *data)
+{
+ struct action_devres *devres;
+
+ devres = vmm_devres_alloc(devm_action_release,
+ sizeof(struct action_devres));
+ if (!devres)
+ return VMM_ENOMEM;
+
+ devres->data = data;
+ devres->action = action;
+
+ vmm_devres_add(dev, devres);
+ return 0;
+}
+
+void vmm_devm_remove_action(struct vmm_device *dev,
+ void (*action)(void *), void *data)
+{
+ struct action_devres devres = {
+ .data = data,
+ .action = action,
+ };
+
+ WARN_ON(vmm_devres_destroy(dev, devm_action_release,
+ devm_action_match, &devres));
+}
--
2.34.1

Anup Patel

unread,
Apr 27, 2022, 11:52:12 PM4/27/22
to xvisor...@googlegroups.com, Anup Patel
Currently, the vmm_host_irqext_alloc_region() tries expand the
extended IRQs only a fixed number of times. This fails if the
requested number of extended IRQs more than (number_of_tries
x HOST_IRQEXT_CHUNK).

To fix above described issues, we make number of tries to be
proportional to the number of extended IRQs requested.

Signed-off-by: Anup Patel <apa...@ventanamicro.com>
---
core/vmm_host_irqext.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/core/vmm_host_irqext.c b/core/vmm_host_irqext.c
index b90f6eeb..4e82fd15 100644
--- a/core/vmm_host_irqext.c
+++ b/core/vmm_host_irqext.c
@@ -148,7 +148,7 @@ static int _irqext_expand(void)
int vmm_host_irqext_alloc_region(u32 size)
{
irq_flags_t flags;
- int tries=4, size_log = 0, pos = -1;
+ int tries, size_log = 0, pos = -1;

while ((1 << size_log) < size) {
++size_log;
@@ -157,6 +157,7 @@ int vmm_host_irqext_alloc_region(u32 size)
if (!size_log || size_log > BITS_PER_LONG)
return VMM_ENOTAVAIL;

+ tries = ((1U << size_log) / HOST_IRQEXT_CHUNK) + 1;
vmm_write_lock_irqsave_lite(&iectrl.lock, flags);

try_again:
--
2.34.1

Anup Patel

unread,
Apr 27, 2022, 11:52:14 PM4/27/22
to xvisor...@googlegroups.com, Anup Patel
This patch improves get_count_order() implementation to match it
with Linux implmentation.

Signed-off-by: Anup Patel <apa...@ventanamicro.com>
---
libs/include/libs/bitops.h | 31 +++++++++++++++++++++----------
1 file changed, 21 insertions(+), 10 deletions(-)

diff --git a/libs/include/libs/bitops.h b/libs/include/libs/bitops.h
index 6b875a3d..44126606 100644
--- a/libs/include/libs/bitops.h
+++ b/libs/include/libs/bitops.h
@@ -267,6 +267,27 @@ static inline unsigned fls_long(unsigned long l)
return fls64(l);
}

+static inline int get_count_order(unsigned int count)
+{
+ if (count == 0)
+ return -1;
+
+ return fls(--count);
+}
+
+/**
+ * get_count_order_long - get order after rounding @l up to power of 2
+ * @l: parameter
+ *
+ * it is same as get_count_order() but with long type parameter
+ */
+static inline int get_count_order_long(unsigned long l)
+{
+ if (l == 0UL)
+ return -1;
+ return (int)fls_long(--l);
+}
+
/**
* __ffs64 - find first set bit in a 64 bit word
* @word: The 64 bit word
@@ -316,16 +337,6 @@ static __inline__ int get_bitmask_order(unsigned int count)
return order; /* We could be slightly more clever with -1 here... */
}

-static __inline__ int get_count_order(unsigned int count)
-{
- int order;
-
- order = fls(count) - 1;
- if (count & (count - 1))
- order++;
- return order;
-}
-
/**
* rol64 - rotate a 64-bit value left
* @word: value to rotate
--
2.34.1

Anup Patel

unread,
Apr 27, 2022, 11:52:17 PM4/27/22
to xvisor...@googlegroups.com, Anup Patel
The RISC-V advanced interrupt architecture (AIA) specification defines
a new MSI controller for managing MSIs on a RISC-V platform. This new
MSI controller is referred to as incoming message signaled interrupt
controller (IMSIC) which manages MSI on per-HART (or per-CPU) basis.
(For more details refer https://github.com/riscv/riscv-aia)

This patch adds an irqchip driver for RISC-V IMSIC found on RISC-V
platforms.

Signed-off-by: Anup Patel <apa...@ventanamicro.com>
---
drivers/include/drv/irqchip/riscv-imsic.h | 87 ++
drivers/irqchip/irq-riscv-imsic.c | 1095 +++++++++++++++++++++
drivers/irqchip/objects.mk | 1 +
drivers/irqchip/openconf.cfg | 11 +
4 files changed, 1194 insertions(+)
create mode 100644 drivers/include/drv/irqchip/riscv-imsic.h
create mode 100644 drivers/irqchip/irq-riscv-imsic.c

diff --git a/drivers/include/drv/irqchip/riscv-imsic.h b/drivers/include/drv/irqchip/riscv-imsic.h
new file mode 100644
index 00000000..a8988dfe
--- /dev/null
+++ b/drivers/include/drv/irqchip/riscv-imsic.h
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2022 Anup Patel.
+ */
+#ifndef __RISCV_IMSIC_H__
+#define __RISCV_IMSIC_H__
+
+#include <linux/types.h>
+
+#define IMSIC_MMIO_PAGE_SHIFT 12
+#define IMSIC_MMIO_PAGE_SZ (1UL << IMSIC_MMIO_PAGE_SHIFT)
+#define IMSIC_MMIO_PAGE_LE 0x00
+#define IMSIC_MMIO_PAGE_BE 0x04
+
+#define IMSIC_MIN_ID 63
+#define IMSIC_MAX_ID 2048
+
+#define IMSIC_EIDELIVERY 0x70
+
+#define IMSIC_EITHRESHOLD 0x72
+
+#define IMSIC_EIP0 0x80
+#define IMSIC_EIP63 0xbf
+#define IMSIC_EIPx_BITS 32
+
+#define IMSIC_EIE0 0xc0
+#define IMSIC_EIE63 0xff
+#define IMSIC_EIEx_BITS 32
+
+#define IMSIC_FIRST IMSIC_EIDELIVERY
+#define IMSIC_LAST IMSIC_EIE63
+
+#define IMSIC_MMIO_SETIPNUM_LE 0x00
+#define IMSIC_MMIO_SETIPNUM_BE 0x04
+
+struct imsic_global_config {
+ /*
+ * MSI Target Address Scheme
+ *
+ * XLEN-1 12 0
+ * | | |
+ * -------------------------------------------------------------
+ * |xxxxxx|Group Index|xxxxxxxxxxx|HART Index|Guest Index| 0 |
+ * -------------------------------------------------------------
+ */
+
+ /* Bits representing Guest index, HART index, and Group index */
+ u32 guest_index_bits;
+ u32 hart_index_bits;
+ u32 group_index_bits;
+ u32 group_index_shift;
+
+ /* Global base address matching all target MSI addresses */
+ physical_addr_t base_addr;
+
+ /* Number of interrupt identities */
+ u32 nr_ids;
+};
+
+struct imsic_local_config {
+ physical_addr_t msi_pa;
+ void *msi_va;
+};
+
+#ifdef CONFIG_RISCV_IMSIC
+
+extern const struct imsic_global_config *imsic_get_global_config(void);
+
+extern const struct imsic_local_config *imsic_get_local_config(
+ unsigned int cpu);
+
+#else
+
+static inline const struct imsic_global_config *imsic_get_global_config(void)
+{
+ return NULL;
+}
+
+static inline const struct imsic_local_config *imsic_get_local_config(
+ unsigned int cpu)
+{
+ return NULL;
+}
+
+#endif
+
+#endif
diff --git a/drivers/irqchip/irq-riscv-imsic.c b/drivers/irqchip/irq-riscv-imsic.c
new file mode 100644
index 00000000..e6d9a04b
--- /dev/null
+++ b/drivers/irqchip/irq-riscv-imsic.c
@@ -0,0 +1,1095 @@
+/**
+ * Copyright (c) 2022 Anup Patel.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * @file irq-riscv-imsic.c
+ * @author Anup Patel (an...@brainfault.org)
+ * @brief RISC-V Incoming Message Signaled Interrupt Controller (IMSIC) driver
+ */
+
+#include <vmm_error.h>
+#include <vmm_limits.h>
+#include <vmm_compiler.h>
+#include <vmm_stdio.h>
+#include <vmm_smp.h>
+#include <vmm_cpuhp.h>
+#include <vmm_heap.h>
+#include <vmm_percpu.h>
+#include <vmm_spinlocks.h>
+#include <vmm_resource.h>
+#include <vmm_modules.h>
+#include <vmm_msi.h>
+#include <vmm_devtree.h>
+#include <vmm_devdrv.h>
+#include <vmm_host_io.h>
+#include <vmm_host_aspace.h>
+#include <vmm_host_irq.h>
+#include <vmm_host_irqdomain.h>
+#include <drv/irqchip/riscv-imsic.h>
+#include <riscv_encoding.h>
+#include <cpu_hwcap.h>
+
+#define IMSIC_DISABLE_EIDELIVERY 0
+#define IMSIC_ENABLE_EIDELIVERY 1
+#define IMSIC_DISABLE_EITHRESHOLD 1
+#define IMSIC_ENABLE_EITHRESHOLD 0
+
+#define imsic_csr_write(__c, __v) \
+do { \
+ csr_write(CSR_SISELECT, __c); \
+ csr_write(CSR_SIREG, __v); \
+} while (0)
+
+#define imsic_csr_read(__c) \
+({ \
+ unsigned long __v; \
+ csr_write(CSR_SISELECT, __c); \
+ __v = csr_read(CSR_SIREG); \
+ __v; \
+})
+
+struct imsic_mmio {
+ physical_addr_t pa;
+ void *va;
+ physical_addr_t size;
+};
+
+struct imsic_priv {
+ /* Global configuration common for all HARTs */
+ struct imsic_global_config global;
+
+ /* MMIO regions */
+ u32 num_mmios;
+ struct imsic_mmio *mmios;
+
+ /* Global state of interrupt identities */
+ vmm_spinlock_t ids_lock;
+ unsigned long *ids_used_bimap;
+ unsigned long *ids_enabled_bimap;
+ unsigned int *ids_target_cpu;
+
+ /* Mask for connected CPUs */
+ struct vmm_cpumask lmask;
+
+ /* IPI domain */
+ u32 ipi_id;
+ u32 ipi_lsync_id;
+ struct vmm_host_irqdomain *ipi_domain;
+
+ /* IRQ domains */
+ struct vmm_host_irqdomain *base_domain;
+ struct vmm_msi_domain *plat_domain;
+};
+
+struct imsic_handler {
+ /* Local configuration for given HART */
+ struct imsic_local_config local;
+
+ /* Pointer to private context */
+ struct imsic_priv *priv;
+};
+
+static bool imsic_init_done;
+
+static int imsic_parent_irq;
+static DEFINE_PER_CPU(struct imsic_handler, imsic_handlers);
+
+const struct imsic_global_config *imsic_get_global_config(void)
+{
+ struct imsic_handler *handler = &this_cpu(imsic_handlers);
+
+ if (!handler || !handler->priv)
+ return NULL;
+
+ return &handler->priv->global;
+}
+VMM_EXPORT_SYMBOL_GPL(imsic_get_global_config);
+
+const struct imsic_local_config *imsic_get_local_config(unsigned int cpu)
+{
+ struct imsic_handler *handler = &per_cpu(imsic_handlers, cpu);
+
+ if (!handler || !handler->priv)
+ return NULL;
+
+ return &handler->local;
+}
+VMM_EXPORT_SYMBOL_GPL(imsic_get_local_config);
+
+static int imsic_cpu_page_phys(unsigned int cpu,
+ unsigned int guest_index,
+ physical_addr_t *out_msi_pa)
+{
+ struct imsic_handler *handler = &per_cpu(imsic_handlers, cpu);
+ struct imsic_global_config *global;
+ struct imsic_local_config *local;
+
+ if (!handler || !handler->priv)
+ return VMM_ENODEV;
+ local = &handler->local;
+ global = &handler->priv->global;
+
+ if (BIT(global->guest_index_bits) <= guest_index)
+ return VMM_EINVALID;
+
+ if (out_msi_pa)
+ *out_msi_pa = local->msi_pa +
+ (guest_index * IMSIC_MMIO_PAGE_SZ);
+
+ return 0;
+}
+
+static int imsic_get_cpu(struct imsic_priv *priv,
+ const struct vmm_cpumask *mask_val, bool force,
+ unsigned int *out_target_cpu)
+{
+ struct vmm_cpumask amask;
+ unsigned int cpu;
+
+ vmm_cpumask_and(&amask, &priv->lmask, mask_val);
+
+ if (force)
+ cpu = vmm_cpumask_first(&amask);
+ else
+ cpu = vmm_cpumask_any_and(&amask, cpu_online_mask);
+
+ if (cpu >= vmm_cpu_count)
+ return VMM_EINVALID;
+
+ if (out_target_cpu)
+ *out_target_cpu = cpu;
+
+ return 0;
+}
+
+static int imsic_get_cpu_msi_msg(unsigned int cpu, unsigned int id,
+ struct vmm_msi_msg *msg)
+{
+ physical_addr_t msi_addr;
+ int err;
+
+ err = imsic_cpu_page_phys(cpu, 0, &msi_addr);
+ if (err)
+ return err;
+
+ msg->address_hi = ((u64)msi_addr) >> 32;
+ msg->address_lo = ((u64)msi_addr) & 0xFFFFFFFF;
+ msg->data = id;
+
+ return err;
+}
+
+static void imsic_id_set_target(struct imsic_priv *priv,
+ unsigned int id, unsigned int target_cpu)
+{
+ irq_flags_t flags;
+
+ vmm_spin_lock_irqsave_lite(&priv->ids_lock, flags);
+ priv->ids_target_cpu[id] = target_cpu;
+ vmm_spin_unlock_irqrestore_lite(&priv->ids_lock, flags);
+}
+
+static unsigned int imsic_id_get_target(struct imsic_priv *priv,
+ unsigned int id)
+{
+ unsigned int ret;
+ irq_flags_t flags;
+
+ vmm_spin_lock_irqsave_lite(&priv->ids_lock, flags);
+ ret = priv->ids_target_cpu[id];
+ vmm_spin_unlock_irqrestore_lite(&priv->ids_lock, flags);
+
+ return ret;
+}
+
+static inline void __imsic_id_enable(unsigned int id)
+{
+ csr_write(CSR_SSETEIENUM, id);
+}
+
+static inline void __imsic_id_disable(unsigned int id)
+{
+ csr_write(CSR_SCLREIENUM, id);
+}
+
+#ifdef CONFIG_SMP
+static void __imsic_id_smp_sync(struct imsic_priv *priv)
+{
+ struct imsic_handler *handler;
+ struct vmm_cpumask amask;
+ int cpu;
+
+ vmm_cpumask_and(&amask, &priv->lmask, cpu_online_mask);
+ for_each_cpu(cpu, &amask) {
+ if (cpu == vmm_smp_processor_id())
+ continue;
+
+ handler = &per_cpu(imsic_handlers, cpu);
+ if (!handler || !handler->priv || !handler->local.msi_va) {
+ vmm_lwarning("imsic",
+ "CPU%d: handler not initialized\n", cpu);
+ continue;
+ }
+
+ vmm_writel(handler->priv->ipi_lsync_id, handler->local.msi_va);
+ }
+}
+#else
+#define __imsic_id_smp_sync(__priv)
+#endif
+
+static void imsic_id_enable(struct imsic_priv *priv, unsigned int id)
+{
+ irq_flags_t flags;
+
+ vmm_spin_lock_irqsave_lite(&priv->ids_lock, flags);
+ bitmap_set(priv->ids_enabled_bimap, id, 1);
+ __imsic_id_enable(id);
+ vmm_spin_unlock_irqrestore_lite(&priv->ids_lock, flags);
+
+ __imsic_id_smp_sync(priv);
+}
+
+static void imsic_id_disable(struct imsic_priv *priv, unsigned int id)
+{
+ irq_flags_t flags;
+
+ vmm_spin_lock_irqsave_lite(&priv->ids_lock, flags);
+ bitmap_set(priv->ids_enabled_bimap, id, 1);
+ __imsic_id_disable(id);
+ vmm_spin_unlock_irqrestore_lite(&priv->ids_lock, flags);
+
+ __imsic_id_smp_sync(priv);
+}
+
+static void imsic_ids_local_sync(struct imsic_priv *priv)
+{
+ int i;
+ irq_flags_t flags;
+
+ vmm_spin_lock_irqsave_lite(&priv->ids_lock, flags);
+ for (i = 1; i <= priv->global.nr_ids; i++) {
+ if (priv->ipi_id == i || priv->ipi_lsync_id == i)
+ continue;
+
+ if (test_bit(i, priv->ids_enabled_bimap))
+ __imsic_id_enable(i);
+ else
+ __imsic_id_disable(i);
+ }
+ vmm_spin_unlock_irqrestore_lite(&priv->ids_lock, flags);
+}
+
+static void imsic_ids_local_delivery(struct imsic_priv *priv, 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_EIDELIVERY, IMSIC_DISABLE_EIDELIVERY);
+ imsic_csr_write(IMSIC_EITHRESHOLD, IMSIC_DISABLE_EITHRESHOLD);
+ }
+}
+
+static int imsic_ids_alloc(struct imsic_priv *priv,
+ unsigned int max_id, unsigned int order)
+{
+ int ret;
+ irq_flags_t flags;
+
+ if ((priv->global.nr_ids < max_id) ||
+ (max_id < BIT(order)))
+ return VMM_EINVALID;
+
+ vmm_spin_lock_irqsave_lite(&priv->ids_lock, flags);
+ ret = bitmap_find_free_region(priv->ids_used_bimap,
+ max_id + 1, order);
+ vmm_spin_unlock_irqrestore_lite(&priv->ids_lock, flags);
+
+ return ret;
+}
+
+static void imsic_ids_free(struct imsic_priv *priv, unsigned int base_id,
+ unsigned int order)
+{
+ irq_flags_t flags;
+
+ vmm_spin_lock_irqsave_lite(&priv->ids_lock, flags);
+ bitmap_release_region(priv->ids_used_bimap, base_id, order);
+ vmm_spin_unlock_irqrestore_lite(&priv->ids_lock, flags);
+}
+
+static int __init imsic_ids_init(struct imsic_priv *priv)
+{
+ int i;
+ struct imsic_global_config *global = &priv->global;
+
+ INIT_SPIN_LOCK(&priv->ids_lock);
+
+ /* Allocate used bitmap */
+ priv->ids_used_bimap = vmm_calloc(BITS_TO_LONGS(global->nr_ids + 1),
+ sizeof(unsigned long));
+ if (!priv->ids_used_bimap) {
+ return VMM_ENOMEM;
+ }
+
+ /* Allocate enabled bitmap */
+ priv->ids_enabled_bimap = vmm_calloc(BITS_TO_LONGS(global->nr_ids + 1),
+ sizeof(unsigned long));
+ if (!priv->ids_enabled_bimap) {
+ vmm_free(priv->ids_used_bimap);
+ return VMM_ENOMEM;
+ }
+
+ /* Allocate target CPU array */
+ priv->ids_target_cpu = vmm_calloc(global->nr_ids + 1,
+ sizeof(unsigned int));
+ if (!priv->ids_target_cpu) {
+ vmm_free(priv->ids_enabled_bimap);
+ vmm_free(priv->ids_used_bimap);
+ return VMM_ENOMEM;
+ }
+ for (i = 0; i <= global->nr_ids; i++)
+ priv->ids_target_cpu[i] = UINT_MAX;
+
+ /* Reserve ID#0 because it is special and never implemented */
+ bitmap_set(priv->ids_used_bimap, 0, 1);
+
+ return 0;
+}
+
+static void __init imsic_ids_cleanup(struct imsic_priv *priv)
+{
+ vmm_free(priv->ids_target_cpu);
+ vmm_free(priv->ids_enabled_bimap);
+ vmm_free(priv->ids_used_bimap);
+}
+
+#ifdef CONFIG_SMP
+static void imsic_ipi_mask(struct vmm_host_irq *d)
+{
+ struct imsic_priv *priv = vmm_host_irq_get_chip_data(d);
+
+ __imsic_id_disable(priv->ipi_id);
+}
+
+static void imsic_ipi_unmask(struct vmm_host_irq *d)
+{
+ struct imsic_priv *priv = vmm_host_irq_get_chip_data(d);
+
+ __imsic_id_enable(priv->ipi_id);
+}
+
+static void imsic_ipi_send_mask(struct vmm_host_irq *d,
+ const struct vmm_cpumask *mask)
+{
+ int cpu;
+ struct imsic_handler *handler;
+
+ for_each_cpu(cpu, mask) {
+ handler = &per_cpu(imsic_handlers, cpu);
+ if (!handler || !handler->priv || !handler->local.msi_va) {
+ vmm_lwarning("imsic",
+ "CPU%d: handler not initialized\n", cpu);
+ continue;
+ }
+
+ vmm_writel(handler->priv->ipi_id, handler->local.msi_va);
+ }
+}
+
+static struct vmm_host_irq_chip imsic_ipi_chip = {
+ .name = "riscv-imsic-ipi",
+ .irq_mask = imsic_ipi_mask,
+ .irq_unmask = imsic_ipi_unmask,
+ .irq_raise = imsic_ipi_send_mask,
+};
+
+static int imsic_ipi_domain_map(struct vmm_host_irqdomain *dom,
+ unsigned int hirq, unsigned int hwirq)
+{
+ struct imsic_priv *priv = dom->host_data;
+
+ vmm_host_irq_mark_per_cpu(hirq);
+ vmm_host_irq_mark_ipi(hirq);
+ vmm_host_irq_set_chip(hirq, &imsic_ipi_chip);
+ vmm_host_irq_set_chip_data(hirq, priv);
+ vmm_host_irq_set_handler(hirq, vmm_handle_percpu_irq);
+
+ return VMM_OK;
+}
+
+static const struct vmm_host_irqdomain_ops imsic_ipi_domain_ops = {
+ .map = imsic_ipi_domain_map,
+};
+
+static void imsic_ipi_enable(struct imsic_priv *priv)
+{
+ __imsic_id_enable(priv->ipi_id);
+ __imsic_id_enable(priv->ipi_lsync_id);
+}
+
+static void imsic_ipi_disable(struct imsic_priv *priv)
+{
+ __imsic_id_disable(priv->ipi_lsync_id);
+ __imsic_id_disable(priv->ipi_id);
+}
+
+static int __init imsic_ipi_domain_init(struct imsic_priv *priv)
+{
+ int rc;
+
+ /* Do nothing if IPI interrupt identity not available */
+ if (!priv->ipi_id) {
+ goto skip_ipi;
+ }
+
+ /* Sanity check on IPI interrupt identity */
+ if (priv->global.nr_ids < priv->ipi_id) {
+ return VMM_EINVALID;
+ }
+
+ /* Reserve interrupt identity for IPI */
+ bitmap_set(priv->ids_used_bimap, priv->ipi_id, 1);
+
+ /* Create IMSIC IPI domain */
+ priv->ipi_domain = vmm_host_irqdomain_add(NULL, BITS_PER_LONG * 2,
+ 1, &imsic_ipi_domain_ops,
+ priv);
+ if (!priv->ipi_domain) {
+ bitmap_clear(priv->ids_used_bimap, priv->ipi_id, 1);
+ return VMM_ENOMEM;
+ }
+
+ /* Pre-create IPI mappings */
+ rc = vmm_host_irqdomain_create_mapping(priv->ipi_domain, 0);
+ if (rc < 0) {
+ vmm_lerror("imsic", "failed to create IPI mapping\n");
+ vmm_host_irqdomain_remove(priv->ipi_domain);
+ bitmap_clear(priv->ids_used_bimap, priv->ipi_id, 1);
+ return rc;
+ }
+
+skip_ipi:
+ /* Allocate interrupt identity for local enable/disable sync */
+ rc = imsic_ids_alloc(priv, priv->global.nr_ids, get_count_order(1));
+ if (rc < 0) {
+ vmm_host_irqdomain_remove(priv->ipi_domain);
+ bitmap_clear(priv->ids_used_bimap, priv->ipi_id, 1);
+ return rc;
+ }
+ priv->ipi_lsync_id = rc;
+
+ return VMM_OK;
+}
+
+static void __init imsic_ipi_domain_cleanup(struct imsic_priv *priv)
+{
+ imsic_ids_free(priv, priv->ipi_lsync_id, get_count_order(1));
+ vmm_host_irqdomain_remove(priv->ipi_domain);
+ bitmap_clear(priv->ids_used_bimap, priv->ipi_id, 1);
+}
+#else
+static void imsic_ipi_enable(struct imsic_priv *priv)
+{
+}
+
+static void imsic_ipi_disable(struct imsic_priv *priv)
+{
+}
+
+static int __init imsic_ipi_domain_init(struct imsic_priv *priv)
+{
+ /* Clear the IPI ids because we are not using IPIs */
+ priv->ipi_id = 0;
+ priv->ipi_lsync_id = 0;
+ return VMM_OK;
+}
+
+static void __init imsic_ipi_domain_cleanup(struct imsic_priv *priv)
+{
+}
+#endif
+
+static void imsic_irq_mask(struct vmm_host_irq *d)
+{
+ imsic_id_disable(vmm_host_irq_get_chip_data(d), d->hwirq);
+}
+
+static void imsic_irq_unmask(struct vmm_host_irq *d)
+{
+ imsic_id_enable(vmm_host_irq_get_chip_data(d), d->hwirq);
+}
+
+static void imsic_irq_compose_msi_msg(struct vmm_host_irq *d,
+ struct vmm_msi_msg *msg)
+{
+ struct imsic_priv *priv = vmm_host_irq_get_chip_data(d);
+ unsigned int cpu;
+ int err;
+
+ cpu = imsic_id_get_target(priv, d->hwirq);
+ WARN_ON(cpu == UINT_MAX);
+
+ err = imsic_get_cpu_msi_msg(cpu, d->hwirq, msg);
+ WARN_ON(err);
+}
+
+#ifdef CONFIG_SMP
+static int imsic_irq_set_affinity(struct vmm_host_irq *d,
+ const struct vmm_cpumask *mask_val,
+ bool force)
+{
+ struct imsic_priv *priv = vmm_host_irq_get_chip_data(d);
+ unsigned int target_cpu;
+ int rc;
+
+ rc = imsic_get_cpu(priv, mask_val, force, &target_cpu);
+ if (rc)
+ return rc;
+
+ imsic_id_set_target(priv, d->hwirq, target_cpu);
+
+ vmm_msi_domain_write_msg(d);
+
+ return VMM_OK;
+}
+#endif
+
+static struct vmm_host_irq_chip imsic_irq_base_chip = {
+ .name = "riscv-imsic",
+ .irq_mask = imsic_irq_mask,
+ .irq_unmask = imsic_irq_unmask,
+#ifdef CONFIG_SMP
+ .irq_set_affinity = imsic_irq_set_affinity,
+#endif
+ .irq_compose_msi_msg = imsic_irq_compose_msi_msg,
+};
+
+static int imsic_irq_domain_map(struct vmm_host_irqdomain *dom,
+ unsigned int hirq, unsigned int hwirq)
+{
+ struct imsic_priv *priv = dom->host_data;
+
+ vmm_host_irq_set_chip(hirq, &imsic_irq_base_chip);
+ vmm_host_irq_set_chip_data(hirq, priv);
+ vmm_host_irq_set_handler(hirq, vmm_handle_simple_irq);
+
+ return VMM_OK;
+}
+
+static int imsic_irq_domain_alloc(struct vmm_host_irqdomain *dom,
+ unsigned int nr_irqs, void *arg)
+{
+ struct imsic_priv *priv = dom->host_data;
+ physical_addr_t msi_addr;
+ int i, hwirq, err = 0;
+ unsigned int cpu;
+
+ err = imsic_get_cpu(priv, &priv->lmask, FALSE, &cpu);
+ if (err)
+ return err;
+
+ err = imsic_cpu_page_phys(cpu, 0, &msi_addr);
+ if (err)
+ return err;
+
+ hwirq = imsic_ids_alloc(priv, priv->global.nr_ids,
+ get_count_order(nr_irqs));
+ if (hwirq < 0)
+ return hwirq;
+
+ /* TODO: Notify IOMMU ?? */
+
+ for (i = 0; i < nr_irqs; i++) {
+ imsic_id_set_target(priv, hwirq + i, cpu);
+ }
+
+ return hwirq;
+}
+
+static void imsic_irq_domain_free(struct vmm_host_irqdomain *dom,
+ unsigned int hwirq, unsigned int nr_irqs)
+{
+ struct imsic_priv *priv = dom->host_data;
+
+ imsic_ids_free(priv, hwirq, get_count_order(nr_irqs));
+}
+
+static const struct vmm_host_irqdomain_ops imsic_base_domain_ops = {
+ .map = imsic_irq_domain_map,
+ .alloc = imsic_irq_domain_alloc,
+ .free = imsic_irq_domain_free,
+};
+
+static struct vmm_msi_domain_ops imsic_plat_domain_ops = {
+};
+
+static int __init imsic_irq_domains_init(struct imsic_priv *priv,
+ struct vmm_devtree_node *node)
+{
+ /* Create Base IRQ domain */
+ priv->base_domain = vmm_host_irqdomain_add(node, -1,
+ priv->global.nr_ids + 1,
+ &imsic_base_domain_ops,
+ priv);
+ if (!priv->base_domain) {
+ vmm_lerror("imsic", "Failed to create IMSIC base domain\n");
+ return VMM_ENOMEM;
+ }
+
+ priv->plat_domain = vmm_platform_msi_create_domain(node,
+ &imsic_plat_domain_ops,
+ priv->base_domain,
+ VMM_MSI_FLAG_USE_DEF_DOM_OPS,
+ priv);
+ if (!priv->plat_domain) {
+ vmm_lerror("imsic",
+ "Failed to create IMSIC platform MSI domain\n");
+ vmm_host_irqdomain_remove(priv->base_domain);
+ return VMM_ENOMEM;
+ }
+
+ /* TODO: Create PCI MSI domain */
+
+ return VMM_OK;
+}
+
+/*
+ * To handle an interrupt, we read the TOPEI CSR and write zero in one
+ * instruction. If TOPEI CSR is non-zero then we translate TOPEI.ID to
+ * Xvisor interrupt number and let Xvisor IRQ subsystem handle it.
+ */
+static vmm_irq_return_t imsic_handle_irq(int irq, void *dev)
+{
+ struct imsic_handler *handler = dev;
+ struct imsic_priv *priv = handler->priv;
+ struct vmm_host_irqdomain *domain;
+ u32 hwirq, base_hwirq, hirq;
+ bool have_irq = FALSE;
+
+ WARN_ON(!handler->priv);
+
+ while ((hwirq = csr_swap(CSR_STOPEI, 0))) {
+ hwirq = hwirq >> TOPEI_ID_SHIFT;
+ domain = priv->base_domain;
+ base_hwirq = 0;
+
+ if (hwirq == priv->ipi_id) {
+ domain = priv->ipi_domain;
+ base_hwirq = hwirq;
+ } else if (hwirq == priv->ipi_lsync_id) {
+ imsic_ids_local_sync(priv);
+ continue;
+ }
+
+ hirq = vmm_host_irqdomain_find_mapping(domain,
+ hwirq - base_hwirq);
+ vmm_host_generic_irq_exec(hirq);
+ have_irq = TRUE;
+ }
+
+ return (have_irq) ? VMM_IRQ_HANDLED : VMM_IRQ_NONE;
+}
+
+static int imsic_dying_cpu(struct vmm_cpuhp_notify *cpuhp, u32 cpu)
+{
+ struct imsic_handler *handler = &this_cpu(imsic_handlers);
+ struct imsic_priv *priv = handler->priv;
+
+ /* No need to disable per-CPU parent interrupt */
+
+ /* Locally disable interrupt delivery */
+ imsic_ids_local_delivery(priv, false);
+
+ /* Disable IPIs */
+ imsic_ipi_disable(priv);
+
+ return VMM_OK;
+}
+
+static int imsic_starting_cpu(struct vmm_cpuhp_notify *cpuhp, u32 cpu)
+{
+ struct imsic_handler *handler = &this_cpu(imsic_handlers);
+ struct imsic_priv *priv = handler->priv;
+
+ /* Enable per-CPU parent interrupt */
+ if (imsic_parent_irq)
+ vmm_host_irq_register(imsic_parent_irq, "riscv-imsic",
+ imsic_handle_irq, handler);
+ else {
+ vmm_lwarning("imsic",
+ "CPU%d: parent irq not available\n", cpu);
+ }
+
+ /* Enable IPIs */
+ imsic_ipi_enable(priv);
+
+ /*
+ * Interrupts identities might have been enabled/disabled while
+ * this CPU was not running so sync-up local enable/disable state.
+ */
+ imsic_ids_local_sync(priv);
+
+ /* Locally enable interrupt delivery */
+ imsic_ids_local_delivery(priv, true);
+
+ return VMM_OK;
+}
+
+static struct vmm_cpuhp_notify imsic_cpuhp = {
+ .name = "IMSIC",
+ .state = VMM_CPUHP_STATE_HOST_IRQ,
+ .startup = imsic_starting_cpu,
+ .teardown = imsic_dying_cpu,
+};
+
+static int __init imsic_init(struct vmm_devtree_node *node)
+{
+ int rc;
+ struct imsic_mmio *mmio;
+ struct imsic_priv *priv;
+ virtual_addr_t base_virt;
+ physical_addr_t base_addr;
+ struct imsic_handler *handler;
+ struct imsic_global_config *global;
+ u32 i, tmp, nr_parent_irqs, nr_handlers = 0;
+
+ if (imsic_init_done) {
+ vmm_lerror(node->name,
+ "already initialized hence ignoring\n");
+ return VMM_ENODEV;
+ }
+
+ if (!riscv_aia_available) {
+ vmm_lerror(node->name, "AIA support not available\n");
+ return VMM_ENODEV;
+ }
+
+ priv = vmm_zalloc(sizeof(*priv));
+ if (!priv) {
+ return VMM_ENOMEM;
+ }
+ global = &priv->global;
+
+ /* Find number of parent interrupts */
+ nr_parent_irqs = vmm_devtree_irq_count(node);
+ if (!nr_parent_irqs) {
+ vmm_lerror(node->name, "no parent irqs available\n");
+ return VMM_EINVALID;
+ }
+
+ /* Find number of guest index bits in MSI address */
+ rc = vmm_devtree_read_u32(node, "riscv,guest-index-bits",
+ &global->guest_index_bits);
+ if (rc)
+ global->guest_index_bits = 0;
+ tmp = BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT;
+ if (tmp < global->guest_index_bits) {
+ vmm_lerror(node->name, "guest index bits too big\n");
+ return VMM_EINVALID;
+ }
+
+ /* Find number of HART index bits */
+ rc = vmm_devtree_read_u32(node, "riscv,hart-index-bits",
+ &global->hart_index_bits);
+ if (rc) {
+ /* Assume default value */
+ global->hart_index_bits = __fls(nr_parent_irqs);
+ if (BIT(global->hart_index_bits) < nr_parent_irqs)
+ global->hart_index_bits++;
+ }
+ tmp = BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT -
+ global->guest_index_bits;
+ if (tmp < global->hart_index_bits) {
+ vmm_lerror(node->name, "HART index bits too big\n");
+ return VMM_EINVALID;
+ }
+
+ /* Find number of group index bits */
+ rc = vmm_devtree_read_u32(node, "riscv,group-index-bits",
+ &global->group_index_bits);
+ if (rc)
+ global->group_index_bits = 0;
+ tmp = BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT -
+ global->guest_index_bits - global->hart_index_bits;
+ if (tmp < global->group_index_bits) {
+ vmm_lerror(node->name, "group index bits too big\n");
+ return VMM_EINVALID;
+ }
+
+ /* Find first bit position of group index */
+ tmp = IMSIC_MMIO_PAGE_SHIFT * 2;
+ rc = vmm_devtree_read_u32(node, "riscv,group-index-shift",
+ &global->group_index_shift);
+ if (rc)
+ global->group_index_shift = tmp;
+ if (global->group_index_shift < tmp) {
+ vmm_lerror(node->name, "group index shift too small\n");
+ return VMM_EINVALID;
+ }
+ tmp = global->group_index_bits + global->group_index_shift - 1;
+ if (tmp >= BITS_PER_LONG) {
+ vmm_lerror(node->name, "group index shift too big\n");
+ return VMM_EINVALID;
+ }
+
+ /* Find number of interrupt identities */
+ rc = vmm_devtree_read_u32(node, "riscv,num-ids", &global->nr_ids);
+ if (rc) {
+ vmm_lerror(node->name,
+ "number of interrupt identities not found\n");
+ return rc;
+ }
+ if ((global->nr_ids < IMSIC_MIN_ID) ||
+ (global->nr_ids >= IMSIC_MAX_ID) ||
+ ((global->nr_ids & IMSIC_MIN_ID) != IMSIC_MIN_ID)) {
+ vmm_lerror(node->name,
+ "invalid number of interrupt identities\n");
+ return VMM_EINVALID;
+ }
+
+ /* Find interrupt indentity to be used for IPI */
+ if (vmm_devtree_read_u32(node, "riscv,ipi-id", &priv->ipi_id))
+ priv->ipi_id = 0;
+
+ /* Compute base address */
+ rc = vmm_devtree_regaddr(node, &global->base_addr, 0);
+ if (rc) {
+ vmm_lerror(node->name, "first MMIO resource not found\n");
+ return rc;
+ }
+ global->base_addr &= ~(BIT(global->guest_index_bits +
+ global->hart_index_bits +
+ IMSIC_MMIO_PAGE_SHIFT) - 1);
+ global->base_addr &= ~((BIT(global->group_index_bits) - 1) <<
+ global->group_index_shift);
+
+ /* Find number of MMIO register sets */
+ while (!vmm_devtree_regaddr(node, &base_addr, priv->num_mmios))
+ priv->num_mmios++;
+
+ /* Allocate MMIO register sets */
+ priv->mmios = vmm_calloc(priv->num_mmios, sizeof(*mmio));
+ if (!priv->mmios) {
+ rc = VMM_ENOMEM;
+ goto out_free_priv;
+ }
+
+ /* Parse and map MMIO register sets */
+ for (i = 0; i < priv->num_mmios; i++) {
+ mmio = &priv->mmios[i];
+
+ rc = vmm_devtree_regaddr(node, &mmio->pa, i);
+ if (rc) {
+ vmm_lerror(node->name,
+ "unable to parse MMIO addr of regset %d\n",
+ i);
+ goto out_iounmap;
+ }
+
+ rc = vmm_devtree_regsize(node, &mmio->size, i);
+ if (rc) {
+ vmm_lerror(node->name,
+ "unable to parse MMIO size of regset %d\n",
+ i);
+ goto out_iounmap;
+ }
+
+ base_addr = mmio->pa;
+ base_addr &= ~(BIT(global->guest_index_bits +
+ global->hart_index_bits +
+ IMSIC_MMIO_PAGE_SHIFT) - 1);
+ base_addr &= ~((BIT(global->group_index_bits) - 1) <<
+ global->group_index_shift);
+ if (base_addr != global->base_addr) {
+ rc = VMM_EINVALID;
+ vmm_lerror(node->name,
+ "address mismatch for regset %d\n",
+ i);
+ goto out_iounmap;
+ }
+
+ tmp = BIT(global->guest_index_bits) - 1;
+ if ((mmio->size / IMSIC_MMIO_PAGE_SZ) & tmp) {
+ rc = VMM_EINVALID;
+ vmm_lerror(node->name,
+ "size mismatch for regset %d\n",
+ i);
+ goto out_iounmap;
+ }
+
+ rc = vmm_devtree_request_regmap(node, &base_virt,
+ i, "RISC-V IMSIC");
+ if (rc) {
+ vmm_lerror(node->name,
+ "unable to map MMIO regset %d\n",
+ i);
+ goto out_iounmap;
+ }
+ mmio->va = (void *)base_virt;
+ }
+
+ /* Initialize interrupt identity management */
+ rc = imsic_ids_init(priv);
+ if (rc) {
+ vmm_lerror(node->name,
+ "failed to initialize interrupt management\n");
+ goto out_iounmap;
+ }
+
+ /* Configure handlers for target CPUs */
+ for (i = 0; i < nr_parent_irqs; i++) {
+ struct vmm_devtree_phandle_args parent;
+ unsigned long reloff;
+ u32 j, cpu, hartid;
+
+ if (vmm_devtree_irq_parse_one(node, i, &parent)) {
+ vmm_lwarning(node->name,
+ "failed to parse parent irq%d\n", i);
+ continue;
+ }
+
+ /*
+ * Skip interrupt pages other than external interrupts for
+ * out privilege level.
+ */
+ if (parent.args[0] != IRQ_S_EXT) {
+ vmm_lwarning(node->name,
+ "invalid hwirq for parent irq%d\n", i);
+ continue;
+ }
+
+ rc = riscv_node_to_hartid(parent.np->parent, &hartid);
+ if (rc) {
+ vmm_lwarning(node->name,
+ "hart ID for parent irq%d not found\n",
+ i);
+ continue;
+ }
+
+ rc = vmm_smp_map_cpuid(hartid, &cpu);
+ if (rc) {
+ vmm_lwarning(node->name,
+ "invalid cpuid for parent irq%d\n", i);
+ continue;
+ }
+
+ /* Find parent domain and map interrupt */
+ if (!imsic_parent_irq &&
+ vmm_devtree_irqdomain_find(parent.np)) {
+ imsic_parent_irq = vmm_devtree_irq_parse_map(node, i);
+ }
+
+ /* Find MMIO location of MSI page */
+ mmio = NULL;
+ reloff = i * BIT(global->guest_index_bits) *
+ IMSIC_MMIO_PAGE_SZ;
+ for (j = 0; priv->num_mmios; j++) {
+ if (reloff < priv->mmios[j].size) {
+ mmio = &priv->mmios[j];
+ break;
+ }
+
+ reloff -= priv->mmios[j].size;
+ }
+ if (!mmio) {
+ vmm_lwarning(node->name,
+ "MMIO not found for parent irq%d\n", i);
+ continue;
+ }
+
+ handler = &per_cpu(imsic_handlers, cpu);
+ if (handler->priv) {
+ vmm_lwarning(node->name,
+ "CPU%d handler already configured.\n",
+ cpu);
+ goto done;
+ }
+
+ vmm_cpumask_set_cpu(cpu, &priv->lmask);
+ handler->local.msi_pa = mmio->pa + reloff;
+ handler->local.msi_va = mmio->va + reloff;
+ handler->priv = priv;
+
+done:
+ nr_handlers++;
+ }
+
+ /* Initialize IPI domain */
+ rc = imsic_ipi_domain_init(priv);
+ if (rc) {
+ vmm_lerror(node->name, "Failed to initialize IPI domain\n");
+ goto out_ids_cleanup;
+ }
+
+ /* Initialize IRQ and MSI domains */
+ rc = imsic_irq_domains_init(priv, node);
+ if (rc) {
+ vmm_lerror(node->name,
+ "Failed to initialize IRQ and MSI domains\n");
+ goto out_ipi_domain_cleanup;
+ }
+
+ /* Setup cpuhp state */
+ vmm_cpuhp_register(&imsic_cpuhp, TRUE);
+
+ /*
+ * Only one IMSIC instance allowed in a platform for clean
+ * implementation of SMP IRQ affinity and per-CPU IPIs.
+ *
+ * This means on a multi-socket (or multi-die) platform we
+ * will have multiple MMIO regions for one IMSIC instance.
+ */
+ imsic_init_done = true;
+
+ vmm_init_printf("%s: hart-index-bits: %d, guest-index-bits: %d\n",
+ node->name, global->hart_index_bits,
+ global->guest_index_bits);
+ vmm_init_printf("%s: group-index-bits: %d, group-index-shift: %d\n",
+ node->name, global->group_index_bits,
+ global->group_index_shift);
+ vmm_init_printf("%s: mapped %d interrupts for %d CPUs at 0x%"PRIPADDR"\n",
+ node->name, global->nr_ids, nr_handlers,
+ global->base_addr);
+ if (priv->ipi_lsync_id)
+ vmm_init_printf("%s: enable/disable sync using interrupt %d\n",
+ node->name, priv->ipi_lsync_id);
+ if (priv->ipi_id)
+ vmm_init_printf("%s: providing IPIs using interrupt %d\n",
+ node->name, priv->ipi_id);
+
+ return VMM_OK;
+
+out_ipi_domain_cleanup:
+ imsic_ipi_domain_cleanup(priv);
+out_ids_cleanup:
+ imsic_ids_cleanup(priv);
+out_iounmap:
+ for (i = 0; i < priv->num_mmios; i++) {
+ if (priv->mmios[i].va) {
+ vmm_devtree_regunmap_release(node,
+ (virtual_addr_t)priv->mmios[i].va, i);
+ }
+ }
+ vmm_free(priv->mmios);
+out_free_priv:
+ vmm_free(priv);
+ return rc;
+}
+
+VMM_HOST_IRQ_INIT_DECLARE(riscvimsic, "riscv,imsics", imsic_init);
diff --git a/drivers/irqchip/objects.mk b/drivers/irqchip/objects.mk
index 4cd5d26c..577f0877 100644
--- a/drivers/irqchip/objects.mk
+++ b/drivers/irqchip/objects.mk
@@ -34,3 +34,4 @@ drivers-objs-$(CONFIG_RISCV_INTC)+= irqchip/irq-riscv-intc.o
drivers-objs-$(CONFIG_RISCV_ACLINT_SWI)+= irqchip/irq-riscv-aclint-swi.o
drivers-objs-$(CONFIG_SIFIVE_PLIC)+= irqchip/irq-sifive-plic.o
drivers-objs-$(CONFIG_RISCV_APLIC)+= irqchip/irq-riscv-aplic.o
+drivers-objs-$(CONFIG_RISCV_IMSIC)+= irqchip/irq-riscv-imsic.o
diff --git a/drivers/irqchip/openconf.cfg b/drivers/irqchip/openconf.cfg
index 0b8aaf8e..7a18a062 100644
--- a/drivers/irqchip/openconf.cfg
+++ b/drivers/irqchip/openconf.cfg
@@ -112,4 +112,15 @@ config CONFIG_RISCV_APLIC

If you don't know what to do here, say Y.

+config CONFIG_RISCV_IMSIC
+ bool "RISC-V Incoming MSI Controller"
+ default n
+ depends on CONFIG_RISCV
+ help
+ This enables support for the IMSIC chip found in RISC-V systems.
+ The IMSIC controls message signaled interrupts and forwards them
+ to each core as wired local interrupt.
+
+ If you don't know what to do here, say Y.
+
endmenu
--
2.34.1

Anup Patel

unread,
Apr 27, 2022, 11:52:19 PM4/27/22
to xvisor...@googlegroups.com, Anup Patel
When APLIC is in MSI-mode, it forwards wired interrupts as MSIs to
per-HART IMSIC interrupt files. This patch adds the missing MSI-mode
support in the APLIC driver which was intentionally left-out waiting
for the IMSIC driver.

Signed-off-by: Anup Patel <apa...@ventanamicro.com>
---
drivers/irqchip/irq-riscv-aplic.c | 190 +++++++++++++++++++++++++++++-
1 file changed, 187 insertions(+), 3 deletions(-)

diff --git a/drivers/irqchip/irq-riscv-aplic.c b/drivers/irqchip/irq-riscv-aplic.c
index 6374e8ea..b5bc4dab 100644
--- a/drivers/irqchip/irq-riscv-aplic.c
+++ b/drivers/irqchip/irq-riscv-aplic.c
@@ -32,13 +32,16 @@
#include <vmm_spinlocks.h>
#include <vmm_resource.h>
#include <vmm_modules.h>
+#include <vmm_msi.h>
#include <vmm_devtree.h>
#include <vmm_devdrv.h>
+#include <vmm_devres.h>
#include <vmm_host_io.h>
#include <vmm_host_aspace.h>
#include <vmm_host_irq.h>
#include <vmm_host_irqdomain.h>
#include <drv/irqchip/riscv-aplic.h>
+#include <drv/irqchip/riscv-imsic.h>
#include <cpu_hwcap.h>

#define MODULE_DESC "RISC-V APLIC Driver"
@@ -143,6 +146,7 @@ static int aplic_set_affinity(struct vmm_host_irq *d,
{
struct aplic_priv *priv = vmm_host_irq_get_chip_data(d);
struct aplic_idc *idc;
+ struct aplic_msi *msi;
unsigned int cpu, val;
struct vmm_cpumask amask;
void *target;
@@ -166,7 +170,9 @@ static int aplic_set_affinity(struct vmm_host_irq *d,
val |= APLIC_DEFAULT_PRIORITY;
vmm_writel(val, target);
} else {
- /* TODO: Set affinity of MSI parent irq */
+ msi = &priv->msis[d->hwirq];
+ return vmm_host_irq_set_affinity(msi->parent_irq,
+ vmm_cpumask_of(cpu), force);
}

return 0;
@@ -238,10 +244,188 @@ static void aplic_init_hw_global(struct aplic_priv *priv)
"unable to write 0x%x in domaincfg\n", val);
}

+/*
+ * To handle an APLIC MSI interrupts, we just find logical IRQ mapped to
+ * the corresponding HW IRQ line and let Linux IRQ subsystem handle the
+ * logical IRQ.
+ */
+static vmm_irq_return_t aplic_msi_handle_irq(int irq, void *dev)
+{
+ struct aplic_msi *msi = dev;
+ struct aplic_priv *priv = msi->priv;
+
+ irq = vmm_host_irqdomain_find_mapping(priv->irqdomain, msi->hw_irq);
+ if (unlikely(irq <= 0))
+ vmm_lwarning(priv->dev->name,
+ "can't find mapping for hwirq %u\n", msi->hw_irq);
+ else
+ vmm_host_generic_irq_exec(irq);
+
+ /*
+ * We don't need to explicitly clear APLIC IRQ pending bit
+ * because as-per RISC-V AIA specification the APLIC hardware
+ * state machine will auto-clear the IRQ pending bit after
+ * MSI write has been sent-out.
+ */
+
+ return VMM_IRQ_HANDLED;
+}
+
+static void aplic_msi_free(void *data)
+{
+ struct vmm_device *dev = data;
+
+ vmm_platform_msi_domain_free_irqs(dev);
+}
+
+static void aplic_msi_write_msg(struct vmm_msi_desc *desc,
+ struct vmm_msi_msg *msg)
+{
+ unsigned int group_index, hart_index, guest_index, val;
+ struct vmm_device *dev = msi_desc_to_dev(desc);
+ struct aplic_priv *priv = dev->priv;
+ struct aplic_msi *msi = &priv->msis[desc->msi_index + 1];
+ struct aplic_msicfg *mc = &priv->msicfg;
+ physical_addr_t tppn, tbppn;
+ void *target;
+
+ /* Save the MSI address and data */
+ msi->msg_addr = (((u64)msg->address_hi) << 32) | msg->address_lo;
+ msi->msg_data = msg->data;
+ WARN_ON(msi->msg_data > APLIC_TARGET_EIID_MASK);
+
+ /* Compute target HART PPN */
+ tppn = msi->msg_addr >> APLIC_xMSICFGADDR_PPN_SHIFT;
+
+ /* Compute target HART Base PPN */
+ tbppn = tppn;
+ tbppn &= ~APLIC_xMSICFGADDR_PPN_HART(mc->lhxs);
+ tbppn &= ~APLIC_xMSICFGADDR_PPN_LHX(mc->lhxw, mc->lhxs);
+ tbppn &= ~APLIC_xMSICFGADDR_PPN_HHX(mc->hhxw, mc->hhxs);
+ WARN_ON(tbppn != mc->base_ppn);
+
+ /* Compute target group and hart indexes */
+ group_index = (tppn >> APLIC_xMSICFGADDR_PPN_HHX_SHIFT(mc->hhxs)) &
+ APLIC_xMSICFGADDR_PPN_HHX_MASK(mc->hhxw);
+ hart_index = (tppn >> APLIC_xMSICFGADDR_PPN_LHX_SHIFT(mc->lhxs)) &
+ APLIC_xMSICFGADDR_PPN_LHX_MASK(mc->lhxw);
+ hart_index |= (group_index << mc->lhxw);
+ WARN_ON(hart_index > APLIC_TARGET_HART_IDX_MASK);
+
+ /* Compute target guest index */
+ guest_index = tppn & APLIC_xMSICFGADDR_PPN_HART(mc->lhxs);
+ WARN_ON(guest_index > APLIC_TARGET_GUEST_IDX_MASK);
+
+ /* Update IRQ TARGET register */
+ target = priv->regs + APLIC_TARGET_BASE;
+ target += (msi->hw_irq - 1) * sizeof(u32);
+ val = (hart_index & APLIC_TARGET_HART_IDX_MASK)
+ << APLIC_TARGET_HART_IDX_SHIFT;
+ val |= (guest_index & APLIC_TARGET_GUEST_IDX_MASK)
+ << APLIC_TARGET_GUEST_IDX_SHIFT;
+ val |= (msi->msg_data & APLIC_TARGET_EIID_MASK);
+ vmm_writel(val, target);
+}
+
static int aplic_setup_lmask_msis(struct aplic_priv *priv)
{
- /* TODO: */
- return VMM_ENODEV;
+ int i, rc;
+ struct aplic_msi *msi;
+ struct vmm_msi_desc *desc;
+ struct vmm_device *dev = priv->dev;
+ struct aplic_msicfg *mc = &priv->msicfg;
+ const struct imsic_global_config *imsic_global;
+
+ /*
+ * The APLIC outgoing MSI config registers assume target MSI
+ * controller to be RISC-V AIA IMSIC controller.
+ */
+ imsic_global = imsic_get_global_config();
+ if (!imsic_global) {
+ vmm_lerror(dev->name,
+ "IMSIC global config not found\n");
+ return VMM_ENODEV;
+ }
+
+ /* Find number of guest index bits (LHXS) */
+ mc->lhxs = imsic_global->guest_index_bits;
+ if (APLIC_xMSICFGADDRH_LHXS_MASK < mc->lhxs) {
+ vmm_lerror(dev->name,
+ "IMSIC guest index bits big for APLIC LHXS\n");
+ return VMM_EINVALID;
+ }
+
+ /* Find number of HART index bits (LHXW) */
+ mc->lhxw = imsic_global->hart_index_bits;
+ if (APLIC_xMSICFGADDRH_LHXW_MASK < mc->lhxw) {
+ vmm_lerror(dev->name,
+ "IMSIC hart index bits big for APLIC LHXW\n");
+ return VMM_EINVALID;
+ }
+
+ /* Find number of group index bits (HHXW) */
+ mc->hhxw = imsic_global->group_index_bits;
+ if (APLIC_xMSICFGADDRH_HHXW_MASK < mc->hhxw) {
+ vmm_lerror(dev->name,
+ "IMSIC group index bits big for APLIC HHXW\n");
+ return VMM_EINVALID;
+ }
+
+ /* Find first bit position of group index (HHXS) */
+ mc->hhxs = imsic_global->group_index_shift;
+ if (mc->hhxs < (2 * APLIC_xMSICFGADDR_PPN_SHIFT)) {
+ vmm_lerror(dev->name,
+ "IMSIC group index shift should be >= %d\n",
+ (2 * APLIC_xMSICFGADDR_PPN_SHIFT));
+ return VMM_EINVALID;
+ }
+ mc->hhxs -= (2 * APLIC_xMSICFGADDR_PPN_SHIFT);
+ if (APLIC_xMSICFGADDRH_HHXS_MASK < mc->hhxs) {
+ vmm_lerror(dev->name,
+ "IMSIC group index shift big for APLIC HHXS\n");
+ return VMM_EINVALID;
+ }
+
+ /* Compute PPN base */
+ mc->base_ppn = imsic_global->base_addr >> APLIC_xMSICFGADDR_PPN_SHIFT;
+ mc->base_ppn &= ~APLIC_xMSICFGADDR_PPN_HART(mc->lhxs);
+ mc->base_ppn &= ~APLIC_xMSICFGADDR_PPN_LHX(mc->lhxw, mc->lhxs);
+ mc->base_ppn &= ~APLIC_xMSICFGADDR_PPN_HHX(mc->hhxw, mc->hhxs);
+
+ /* Use all possible CPUs as lmask */
+ vmm_cpumask_copy(&priv->lmask, cpu_possible_mask);
+
+ /* Allocate one APLIC MSI for every IRQ line */
+ priv->msis = vmm_devm_calloc(dev, priv->nr_irqs + 1, sizeof(*msi));
+ if (!priv->msis)
+ return VMM_ENOMEM;
+ for (i = 0; i <= priv->nr_irqs; i++) {
+ priv->msis[i].hw_irq = i;
+ priv->msis[i].priv = priv;
+ }
+
+ /* Allocate platform MSIs from parent */
+ rc = vmm_platform_msi_domain_alloc_irqs(dev, priv->nr_irqs,
+ aplic_msi_write_msg);
+ if (rc) {
+ vmm_lerror(dev->name, "failed to allocate MSIs\n");
+ return rc;
+ }
+
+ /* Register callback to free-up MSIs */
+ vmm_devm_add_action(dev, aplic_msi_free, dev);
+
+ /* Configure chained handler for each APLIC MSI */
+ for_each_msi_entry(desc, dev) {
+ msi = &priv->msis[desc->msi_index + 1];
+ msi->parent_irq = desc->hirq;
+
+ vmm_host_irq_mark_chained(msi->parent_irq);
+ vmm_host_irq_register(msi->parent_irq, "riscv-aplic",
+ aplic_msi_handle_irq, msi);
+ }
+
+ return VMM_OK;
}

static vmm_irq_return_t aplic_idc_handle_irq(int irq, void *dev)
--
2.34.1

Anup Patel

unread,
Apr 27, 2022, 11:52:21 PM4/27/22
to xvisor...@googlegroups.com, Anup Patel
We enable RISC-V IMSIC in both RV32 and RV64 defconfigs because it
is available in QEMU virt machine.

Signed-off-by: Anup Patel <apa...@ventanamicro.com>
---
arch/riscv/configs/generic-32b-defconfig | 1 +
arch/riscv/configs/generic-64b-defconfig | 1 +
2 files changed, 2 insertions(+)

diff --git a/arch/riscv/configs/generic-32b-defconfig b/arch/riscv/configs/generic-32b-defconfig
index 8cc72ada..eed1d604 100644
--- a/arch/riscv/configs/generic-32b-defconfig
+++ b/arch/riscv/configs/generic-32b-defconfig
@@ -26,6 +26,7 @@ CONFIG_RISCV_INTC=y
CONFIG_RISCV_ACLINT_SWI=y
CONFIG_SIFIVE_PLIC=y
CONFIG_RISCV_APLIC=y
+CONFIG_RISCV_IMSIC=y
CONFIG_RISCV_TIMER=y
CONFIG_SERIAL=y
CONFIG_SERIAL_8250_UART=y
diff --git a/arch/riscv/configs/generic-64b-defconfig b/arch/riscv/configs/generic-64b-defconfig
index 6cab1434..59077fdd 100644
--- a/arch/riscv/configs/generic-64b-defconfig
+++ b/arch/riscv/configs/generic-64b-defconfig
@@ -26,6 +26,7 @@ CONFIG_RISCV_INTC=y
CONFIG_RISCV_ACLINT_SWI=y
CONFIG_SIFIVE_PLIC=y
CONFIG_RISCV_APLIC=y
+CONFIG_RISCV_IMSIC=y
CONFIG_RISCV_TIMER=y
CONFIG_SERIAL=y
CONFIG_SERIAL_8250_UART=y
--
2.34.1

Anup Patel

unread,
May 1, 2022, 7:14:14 AM5/1/22
to Xvisor Devel, Anup Patel
On Thu, Apr 28, 2022 at 9:21 AM Anup Patel <apa...@ventanamicro.com> wrote:
>
> This series completes the driver AIA driver support for Xvisor RISC-V. It
> include the following:
> 1) Improved MSI framework for platform MSIs
> 2) Added IMSIC driver ported from Linux IMSIC driver
> 3) Added APIC MSI-mode in APLIC driver
>
> These patches can also be found in riscv_imsic_v1 branch at:
> https://github.com/avpatel/xvisor-next.git
>
> Anup Patel (14):
> DRIVERS: irqchip/riscv-aplic: Align priority and threshold with Linux
> CORE: vmm_host_irqdomain: Add alloc() and free() domain operations
> CORE: vmm_msi: Move compose_msi_msg() from MSI domain to irqchip
> CORE: vmm_msi: Use desc->msg in vmm_msi_domain_alloc/free_irqs()
> CORE: vmm_msi: Add vmm_msi_domain_write_msg() API
> CORE: vmm_msi: Provide complete set of default ops
> CORE: vmm_msi: Add common msi_index for both PCIe and Platform MSIs
> CORE: vmm_host_irq: Allow irqchip drivers to mark chained interrupts
> CORE: vmm_devres: Add custom action APIs
> CORE: vmm_host_irqext: Fix extended IRQ allocations
> LIBS: bitops: Improve get_count_order() implementation
> DRIVERS: irqchip: Add RISC-V incoming MSI controller driver
> DRIVERS: irqchip/riscv-aplic: Add support for MSI-mode
> RISC-V: Enable RISC-V IMSIC in RV32 and RV64 defconfigs

Applied this patch to the xvisor-next repo.

Regards,
Anup

>
> arch/riscv/configs/generic-32b-defconfig | 1 +
> arch/riscv/configs/generic-64b-defconfig | 1 +
> commands/cmd_host.c | 4 +-
> core/include/vmm_devres.h | 8 +
> core/include/vmm_host_irq.h | 28 +
> core/include/vmm_host_irqdomain.h | 8 +-
> core/include/vmm_msi.h | 17 +-
> core/vmm_devres.c | 54 +
> core/vmm_host_irq.c | 54 +
> core/vmm_host_irqdomain.c | 72 +-
> core/vmm_host_irqext.c | 3 +-
> core/vmm_msi.c | 123 ++-
> core/vmm_platform_msi.c | 4 +-
> drivers/include/drv/irqchip/riscv-imsic.h | 87 ++
> drivers/irqchip/irq-riscv-aplic.c | 196 +++-
> drivers/irqchip/irq-riscv-imsic.c | 1095 +++++++++++++++++++++
> drivers/irqchip/objects.mk | 1 +
> drivers/irqchip/openconf.cfg | 11 +
> libs/include/libs/bitops.h | 31 +-
> 19 files changed, 1701 insertions(+), 97 deletions(-)
> create mode 100644 drivers/include/drv/irqchip/riscv-imsic.h
> create mode 100644 drivers/irqchip/irq-riscv-imsic.c
>
> --
> 2.34.1
>
> --
> You received this message because you are subscribed to the Google Groups "Xvisor Development" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to xvisor-devel...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/xvisor-devel/20220428035117.43453-1-apatel%40ventanamicro.com.
Reply all
Reply to author
Forward
0 new messages