diff mbox series

[kvmtool,v2,5/6] riscv: Use AIA in-kernel irqchip whenever KVM RISC-V supports

Message ID 20230918125730.1371985-6-apatel@ventanamicro.com (mailing list archive)
State New, archived
Headers show
Series RISC-V AIA irqchip and Svnapot support | expand

Commit Message

Anup Patel Sept. 18, 2023, 12:57 p.m. UTC
The KVM RISC-V kernel module supports AIA in-kernel irqchip when
underlying host has AIA support. We detect and use AIA in-kernel
irqchip whenever possible otherwise we fallback to PLIC emulated
in user-space.

Signed-off-by: Anup Patel <apatel@ventanamicro.com>
---
 Makefile                     |   1 +
 riscv/aia.c                  | 227 +++++++++++++++++++++++++++++++++++
 riscv/include/kvm/fdt-arch.h |   8 +-
 riscv/include/kvm/kvm-arch.h |   2 +
 riscv/irq.c                  |   3 +
 5 files changed, 240 insertions(+), 1 deletion(-)
 create mode 100644 riscv/aia.c

Comments

Andrew Jones Oct. 25, 2023, 1:39 p.m. UTC | #1
On Mon, Sep 18, 2023 at 06:27:29PM +0530, Anup Patel wrote:
> The KVM RISC-V kernel module supports AIA in-kernel irqchip when
> underlying host has AIA support. We detect and use AIA in-kernel
> irqchip whenever possible otherwise we fallback to PLIC emulated
> in user-space.
> 
> Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> ---
>  Makefile                     |   1 +
>  riscv/aia.c                  | 227 +++++++++++++++++++++++++++++++++++
>  riscv/include/kvm/fdt-arch.h |   8 +-
>  riscv/include/kvm/kvm-arch.h |   2 +
>  riscv/irq.c                  |   3 +
>  5 files changed, 240 insertions(+), 1 deletion(-)
>  create mode 100644 riscv/aia.c
> 
> diff --git a/Makefile b/Makefile
> index e711670..acd5ffd 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -220,6 +220,7 @@ ifeq ($(ARCH),riscv)
>  	OBJS		+= riscv/kvm-cpu.o
>  	OBJS		+= riscv/pci.o
>  	OBJS		+= riscv/plic.o
> +	OBJS		+= riscv/aia.o
>  	ifeq ($(RISCV_XLEN),32)
>  		CFLAGS	+= -mabi=ilp32d -march=rv32gc
>  	endif
> diff --git a/riscv/aia.c b/riscv/aia.c
> new file mode 100644
> index 0000000..8c85b3f
> --- /dev/null
> +++ b/riscv/aia.c
> @@ -0,0 +1,227 @@
> +#include "kvm/devices.h"
> +#include "kvm/fdt.h"
> +#include "kvm/ioeventfd.h"
> +#include "kvm/ioport.h"
> +#include "kvm/kvm.h"
> +#include "kvm/kvm-cpu.h"
> +#include "kvm/irq.h"
> +#include "kvm/util.h"
> +
> +static int aia_fd = -1;
> +
> +static u32 aia_mode = KVM_DEV_RISCV_AIA_MODE_EMUL;
> +static struct kvm_device_attr aia_mode_attr = {
> +	.group	= KVM_DEV_RISCV_AIA_GRP_CONFIG,
> +	.attr	= KVM_DEV_RISCV_AIA_CONFIG_MODE,
> +};
> +
> +static u32 aia_nr_ids = 0;
> +static struct kvm_device_attr aia_nr_ids_attr = {
> +	.group	= KVM_DEV_RISCV_AIA_GRP_CONFIG,
> +	.attr	= KVM_DEV_RISCV_AIA_CONFIG_IDS,
> +};
> +
> +static u32 aia_nr_sources = 0;
> +static struct kvm_device_attr aia_nr_sources_attr = {
> +	.group	= KVM_DEV_RISCV_AIA_GRP_CONFIG,
> +	.attr	= KVM_DEV_RISCV_AIA_CONFIG_SRCS,
> +};
> +
> +static u32 aia_hart_bits = 0;
> +static struct kvm_device_attr aia_hart_bits_attr = {
> +	.group	= KVM_DEV_RISCV_AIA_GRP_CONFIG,
> +	.attr	= KVM_DEV_RISCV_AIA_CONFIG_HART_BITS,
> +};
> +
> +static u32 aia_nr_harts = 0;
> +
> +#define IRQCHIP_AIA_NR			0
> +
> +#define AIA_IMSIC_BASE			RISCV_IRQCHIP
> +#define AIA_IMSIC_ADDR(__hart)		\
> +	(AIA_IMSIC_BASE + (__hart) * KVM_DEV_RISCV_IMSIC_SIZE)
> +#define AIA_IMSIC_SIZE			\
> +	(aia_nr_harts * KVM_DEV_RISCV_IMSIC_SIZE)
> +#define AIA_APLIC_ADDR(__nr_harts)	\
> +	(AIA_IMSIC_BASE + (__nr_harts) * KVM_DEV_RISCV_IMSIC_SIZE)

AIA_APLIC_ADDR() probably doesn't need to take nr_harts since it's
always called with aia_nr_harts. So it could just be defined as

 #define AIA_APLIC_ADDR (AIA_IMSIC_BASE + AIA_IMSIC_SIZE)

> +
> +static void aia__generate_fdt_node(void *fdt, struct kvm *kvm)
> +{
> +	u32 i;
> +	char name[64];
> +	u32 reg_cells[4], *irq_cells;
> +
> +	irq_cells = calloc(aia_nr_harts * 2, sizeof(u32));
> +	if (!irq_cells)
> +		die("Failed to alloc irq_cells");
> +
> +	sprintf(name, "imsics@%08x", (u32)AIA_IMSIC_BASE);
> +	_FDT(fdt_begin_node(fdt, name));
> +	_FDT(fdt_property_string(fdt, "compatible", "riscv,imsics"));
> +	reg_cells[0] = 0;
> +	reg_cells[1] = cpu_to_fdt32(AIA_IMSIC_BASE);
> +	reg_cells[2] = 0;
> +	reg_cells[3] = cpu_to_fdt32(AIA_IMSIC_SIZE);
> +	_FDT(fdt_property(fdt, "reg", reg_cells, sizeof(reg_cells)));
> +	_FDT(fdt_property_cell(fdt, "#interrupt-cells", 0));
> +	_FDT(fdt_property(fdt, "interrupt-controller", NULL, 0));
> +	_FDT(fdt_property(fdt, "msi-controller", NULL, 0));
> +	_FDT(fdt_property_cell(fdt, "riscv,num-ids", aia_nr_ids));
> +	_FDT(fdt_property_cell(fdt, "phandle", PHANDLE_AIA_IMSIC));
> +	for (i = 0; i < aia_nr_harts; i++) {
> +		irq_cells[2*i + 0] = cpu_to_fdt32(PHANDLE_CPU_INTC_BASE + i);
> +		irq_cells[2*i + 1] = cpu_to_fdt32(9);
> +	}
> +	_FDT(fdt_property(fdt, "interrupts-extended", irq_cells,
> +			  sizeof(u32) * aia_nr_harts * 2));
> +	_FDT(fdt_end_node(fdt));
> +
> +	free(irq_cells);
> +
> +	/* Skip APLIC node if we have no interrupt sources */
> +	if (!aia_nr_sources)
> +		return;
> +
> +	sprintf(name, "aplic@%08x", (u32)AIA_APLIC_ADDR(aia_nr_harts));
> +	_FDT(fdt_begin_node(fdt, name));
> +	_FDT(fdt_property_string(fdt, "compatible", "riscv,aplic"));
> +	reg_cells[0] = 0;
> +	reg_cells[1] = cpu_to_fdt32(AIA_APLIC_ADDR(aia_nr_harts));
> +	reg_cells[2] = 0;
> +	reg_cells[3] = cpu_to_fdt32(KVM_DEV_RISCV_APLIC_SIZE);
> +	_FDT(fdt_property(fdt, "reg", reg_cells, sizeof(reg_cells)));
> +	_FDT(fdt_property_cell(fdt, "#interrupt-cells", 2));
> +	_FDT(fdt_property(fdt, "interrupt-controller", NULL, 0));
> +	_FDT(fdt_property_cell(fdt, "riscv,num-sources", aia_nr_sources));
> +	_FDT(fdt_property_cell(fdt, "phandle", PHANDLE_AIA_APLIC));
> +	_FDT(fdt_property_cell(fdt, "msi-parent", PHANDLE_AIA_IMSIC));
> +	_FDT(fdt_end_node(fdt));
> +}
> +
> +static int aia__irq_routing_init(struct kvm *kvm)
> +{
> +	int r;
> +	int irqlines = aia_nr_sources + 1;
> +
> +	/* Skip this if we have no interrupt sources */
> +	if (!aia_nr_sources)
> +		return 0;
> +
> +	/*
> +	 * This describes the default routing that the kernel uses without
> +	 * any routing explicitly set up via KVM_SET_GSI_ROUTING. So we
> +	 * don't need to commit these setting right now. The first actual
> +	 * user (MSI routing) will engage these mappings then.
> +	 */
> +	for (next_gsi = 0; next_gsi < irqlines; next_gsi++) {
> +		r = irq__allocate_routing_entry();
> +		if (r)
> +			return r;
> +
> +		irq_routing->entries[irq_routing->nr++] =
> +			(struct kvm_irq_routing_entry) {
> +				.gsi = next_gsi,
> +				.type = KVM_IRQ_ROUTING_IRQCHIP,
> +				.u.irqchip.irqchip = IRQCHIP_AIA_NR,
> +				.u.irqchip.pin = next_gsi,
> +		};
> +	}
> +
> +	return 0;
> +}
> +
> +static int aia__init(struct kvm *kvm)
> +{
> +	int i, ret;
> +	u64 aia_addr = 0;
> +	struct kvm_device_attr aia_addr_attr = {
> +		.group	= KVM_DEV_RISCV_AIA_GRP_ADDR,
> +		.addr	= (u64)(unsigned long)&aia_addr,
> +	};
> +	struct kvm_device_attr aia_init_attr = {
> +		.group	= KVM_DEV_RISCV_AIA_GRP_CTRL,
> +		.attr	= KVM_DEV_RISCV_AIA_CTRL_INIT,
> +	};
> +
> +	/* Setup global device attribute variables */
> +	aia_mode_attr.addr = (u64)(unsigned long)&aia_mode;
> +	aia_nr_ids_attr.addr = (u64)(unsigned long)&aia_nr_ids;
> +	aia_nr_sources_attr.addr = (u64)(unsigned long)&aia_nr_sources;
> +	aia_hart_bits_attr.addr = (u64)(unsigned long)&aia_hart_bits;
> +
> +	/* Do nothing if AIA device not created */
> +	if (aia_fd < 0)
> +		return 0;
> +
> +	/* Set/Get AIA device config parameters */
> +	ret = ioctl(aia_fd, KVM_GET_DEVICE_ATTR, &aia_mode_attr);
> +	if (ret)
> +		return ret;
> +	ret = ioctl(aia_fd, KVM_GET_DEVICE_ATTR, &aia_nr_ids_attr);
> +	if (ret)
> +		return ret;
> +	aia_nr_sources = irq__get_nr_allocated_lines();
> +	ret = ioctl(aia_fd, KVM_SET_DEVICE_ATTR, &aia_nr_sources_attr);
> +	if (ret)
> +		return ret;
> +	aia_hart_bits = fls_long(kvm->nrcpus);
> +	ret = ioctl(aia_fd, KVM_SET_DEVICE_ATTR, &aia_hart_bits_attr);
> +	if (ret)
> +		return ret;
> +
> +	/* Save number of HARTs for FDT generation */
> +	aia_nr_harts = kvm->nrcpus;
> +
> +	/* Set AIA device addresses */
> +	aia_addr = AIA_APLIC_ADDR(aia_nr_harts);
> +	aia_addr_attr.attr = KVM_DEV_RISCV_AIA_ADDR_APLIC;
> +	ret = ioctl(aia_fd, KVM_SET_DEVICE_ATTR, &aia_addr_attr);
> +	if (ret)
> +		return ret;
> +	for (i = 0; i < kvm->nrcpus; i++) {
> +		aia_addr = AIA_IMSIC_ADDR(i);
> +		aia_addr_attr.attr = KVM_DEV_RISCV_AIA_ADDR_IMSIC(i);
> +		ret = ioctl(aia_fd, KVM_SET_DEVICE_ATTR, &aia_addr_attr);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	/* Setup default IRQ routing */
> +	aia__irq_routing_init(kvm);
> +
> +	/* Initialize the AIA device */
> +	ret = ioctl(aia_fd, KVM_SET_DEVICE_ATTR, &aia_init_attr);
> +	if (ret)
> +		return ret;
> +
> +	/* Mark IRQFD as ready */
> +	riscv_irqchip_irqfd_ready = true;
> +
> +	return 0;
> +}
> +late_init(aia__init);
> +
> +void aia__create(struct kvm *kvm)
> +{
> +	int err;
> +	struct kvm_create_device aia_device = {
> +		.type = KVM_DEV_TYPE_RISCV_AIA,
> +		.flags = 0,
> +	};
> +
> +	if (kvm->cfg.arch.ext_disabled[KVM_RISCV_ISA_EXT_SSAIA])
> +		return;
> +
> +	err = ioctl(kvm->vm_fd, KVM_CREATE_DEVICE, &aia_device);
> +	if (err)
> +		return;
> +	aia_fd = aia_device.fd;
> +
> +	riscv_irqchip = IRQCHIP_AIA;
> +	riscv_irqchip_inkernel = true;
> +	riscv_irqchip_trigger = NULL;
> +	riscv_irqchip_generate_fdt_node = aia__generate_fdt_node;
> +	riscv_irqchip_phandle = PHANDLE_AIA_APLIC;
> +	riscv_irqchip_msi_phandle = PHANDLE_AIA_IMSIC;
> +	riscv_irqchip_line_sensing = true;
> +}
> diff --git a/riscv/include/kvm/fdt-arch.h b/riscv/include/kvm/fdt-arch.h
> index f7548e8..d88b832 100644
> --- a/riscv/include/kvm/fdt-arch.h
> +++ b/riscv/include/kvm/fdt-arch.h
> @@ -1,7 +1,13 @@
>  #ifndef KVM__KVM_FDT_H
>  #define KVM__KVM_FDT_H
>  
> -enum phandles {PHANDLE_RESERVED = 0, PHANDLE_PLIC, PHANDLES_MAX};
> +enum phandles {
> +	PHANDLE_RESERVED = 0,
> +	PHANDLE_PLIC,
> +	PHANDLE_AIA_APLIC,
> +	PHANDLE_AIA_IMSIC,
> +	PHANDLES_MAX
> +};
>  
>  #define PHANDLE_CPU_INTC_BASE	PHANDLES_MAX
>  
> diff --git a/riscv/include/kvm/kvm-arch.h b/riscv/include/kvm/kvm-arch.h
> index 1a8af6a..9f2159f 100644
> --- a/riscv/include/kvm/kvm-arch.h
> +++ b/riscv/include/kvm/kvm-arch.h
> @@ -100,6 +100,8 @@ extern u32 riscv_irqchip_msi_phandle;
>  extern bool riscv_irqchip_line_sensing;
>  extern bool riscv_irqchip_irqfd_ready;
>  
> +void aia__create(struct kvm *kvm);
> +

nit: I'd remove the blank line between *__create functions.

>  void plic__create(struct kvm *kvm);
>  
>  void pci__generate_fdt_nodes(void *fdt);
> diff --git a/riscv/irq.c b/riscv/irq.c
> index e6c0939..be3e7ac 100644
> --- a/riscv/irq.c
> +++ b/riscv/irq.c
> @@ -135,6 +135,9 @@ void riscv__generate_irq_prop(void *fdt, u8 irq, enum irq_type irq_type)
>  
>  void riscv__irqchip_create(struct kvm *kvm)
>  {
> +	/* Try AIA in-kernel irqchip. */
> +	aia__create(kvm);
> +
>  	/* Try PLIC irqchip */
>  	plic__create(kvm);

I realize plic__create() just returns if aia__create() successful set up
the irqchip, but structuring this like

 ret = aia__create(kvm);
 if (!ret)
    ret = plic__create(kvm);
 if (!ret)
    die(...);

Might be a little easier to for future readers of the code to grok on
first read.


Either way,

Reviewed-by: Andrew Jones <ajones@ventanamicro.com>

Thanks,
drew
Anup Patel Nov. 18, 2023, 1:01 p.m. UTC | #2
On Wed, Oct 25, 2023 at 7:09 PM Andrew Jones <ajones@ventanamicro.com> wrote:
>
> On Mon, Sep 18, 2023 at 06:27:29PM +0530, Anup Patel wrote:
> > The KVM RISC-V kernel module supports AIA in-kernel irqchip when
> > underlying host has AIA support. We detect and use AIA in-kernel
> > irqchip whenever possible otherwise we fallback to PLIC emulated
> > in user-space.
> >
> > Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> > ---
> >  Makefile                     |   1 +
> >  riscv/aia.c                  | 227 +++++++++++++++++++++++++++++++++++
> >  riscv/include/kvm/fdt-arch.h |   8 +-
> >  riscv/include/kvm/kvm-arch.h |   2 +
> >  riscv/irq.c                  |   3 +
> >  5 files changed, 240 insertions(+), 1 deletion(-)
> >  create mode 100644 riscv/aia.c
> >
> > diff --git a/Makefile b/Makefile
> > index e711670..acd5ffd 100644
> > --- a/Makefile
> > +++ b/Makefile
> > @@ -220,6 +220,7 @@ ifeq ($(ARCH),riscv)
> >       OBJS            += riscv/kvm-cpu.o
> >       OBJS            += riscv/pci.o
> >       OBJS            += riscv/plic.o
> > +     OBJS            += riscv/aia.o
> >       ifeq ($(RISCV_XLEN),32)
> >               CFLAGS  += -mabi=ilp32d -march=rv32gc
> >       endif
> > diff --git a/riscv/aia.c b/riscv/aia.c
> > new file mode 100644
> > index 0000000..8c85b3f
> > --- /dev/null
> > +++ b/riscv/aia.c
> > @@ -0,0 +1,227 @@
> > +#include "kvm/devices.h"
> > +#include "kvm/fdt.h"
> > +#include "kvm/ioeventfd.h"
> > +#include "kvm/ioport.h"
> > +#include "kvm/kvm.h"
> > +#include "kvm/kvm-cpu.h"
> > +#include "kvm/irq.h"
> > +#include "kvm/util.h"
> > +
> > +static int aia_fd = -1;
> > +
> > +static u32 aia_mode = KVM_DEV_RISCV_AIA_MODE_EMUL;
> > +static struct kvm_device_attr aia_mode_attr = {
> > +     .group  = KVM_DEV_RISCV_AIA_GRP_CONFIG,
> > +     .attr   = KVM_DEV_RISCV_AIA_CONFIG_MODE,
> > +};
> > +
> > +static u32 aia_nr_ids = 0;
> > +static struct kvm_device_attr aia_nr_ids_attr = {
> > +     .group  = KVM_DEV_RISCV_AIA_GRP_CONFIG,
> > +     .attr   = KVM_DEV_RISCV_AIA_CONFIG_IDS,
> > +};
> > +
> > +static u32 aia_nr_sources = 0;
> > +static struct kvm_device_attr aia_nr_sources_attr = {
> > +     .group  = KVM_DEV_RISCV_AIA_GRP_CONFIG,
> > +     .attr   = KVM_DEV_RISCV_AIA_CONFIG_SRCS,
> > +};
> > +
> > +static u32 aia_hart_bits = 0;
> > +static struct kvm_device_attr aia_hart_bits_attr = {
> > +     .group  = KVM_DEV_RISCV_AIA_GRP_CONFIG,
> > +     .attr   = KVM_DEV_RISCV_AIA_CONFIG_HART_BITS,
> > +};
> > +
> > +static u32 aia_nr_harts = 0;
> > +
> > +#define IRQCHIP_AIA_NR                       0
> > +
> > +#define AIA_IMSIC_BASE                       RISCV_IRQCHIP
> > +#define AIA_IMSIC_ADDR(__hart)               \
> > +     (AIA_IMSIC_BASE + (__hart) * KVM_DEV_RISCV_IMSIC_SIZE)
> > +#define AIA_IMSIC_SIZE                       \
> > +     (aia_nr_harts * KVM_DEV_RISCV_IMSIC_SIZE)
> > +#define AIA_APLIC_ADDR(__nr_harts)   \
> > +     (AIA_IMSIC_BASE + (__nr_harts) * KVM_DEV_RISCV_IMSIC_SIZE)
>
> AIA_APLIC_ADDR() probably doesn't need to take nr_harts since it's
> always called with aia_nr_harts. So it could just be defined as
>
>  #define AIA_APLIC_ADDR (AIA_IMSIC_BASE + AIA_IMSIC_SIZE)

Okay, I will update.

>
> > +
> > +static void aia__generate_fdt_node(void *fdt, struct kvm *kvm)
> > +{
> > +     u32 i;
> > +     char name[64];
> > +     u32 reg_cells[4], *irq_cells;
> > +
> > +     irq_cells = calloc(aia_nr_harts * 2, sizeof(u32));
> > +     if (!irq_cells)
> > +             die("Failed to alloc irq_cells");
> > +
> > +     sprintf(name, "imsics@%08x", (u32)AIA_IMSIC_BASE);
> > +     _FDT(fdt_begin_node(fdt, name));
> > +     _FDT(fdt_property_string(fdt, "compatible", "riscv,imsics"));
> > +     reg_cells[0] = 0;
> > +     reg_cells[1] = cpu_to_fdt32(AIA_IMSIC_BASE);
> > +     reg_cells[2] = 0;
> > +     reg_cells[3] = cpu_to_fdt32(AIA_IMSIC_SIZE);
> > +     _FDT(fdt_property(fdt, "reg", reg_cells, sizeof(reg_cells)));
> > +     _FDT(fdt_property_cell(fdt, "#interrupt-cells", 0));
> > +     _FDT(fdt_property(fdt, "interrupt-controller", NULL, 0));
> > +     _FDT(fdt_property(fdt, "msi-controller", NULL, 0));
> > +     _FDT(fdt_property_cell(fdt, "riscv,num-ids", aia_nr_ids));
> > +     _FDT(fdt_property_cell(fdt, "phandle", PHANDLE_AIA_IMSIC));
> > +     for (i = 0; i < aia_nr_harts; i++) {
> > +             irq_cells[2*i + 0] = cpu_to_fdt32(PHANDLE_CPU_INTC_BASE + i);
> > +             irq_cells[2*i + 1] = cpu_to_fdt32(9);
> > +     }
> > +     _FDT(fdt_property(fdt, "interrupts-extended", irq_cells,
> > +                       sizeof(u32) * aia_nr_harts * 2));
> > +     _FDT(fdt_end_node(fdt));
> > +
> > +     free(irq_cells);
> > +
> > +     /* Skip APLIC node if we have no interrupt sources */
> > +     if (!aia_nr_sources)
> > +             return;
> > +
> > +     sprintf(name, "aplic@%08x", (u32)AIA_APLIC_ADDR(aia_nr_harts));
> > +     _FDT(fdt_begin_node(fdt, name));
> > +     _FDT(fdt_property_string(fdt, "compatible", "riscv,aplic"));
> > +     reg_cells[0] = 0;
> > +     reg_cells[1] = cpu_to_fdt32(AIA_APLIC_ADDR(aia_nr_harts));
> > +     reg_cells[2] = 0;
> > +     reg_cells[3] = cpu_to_fdt32(KVM_DEV_RISCV_APLIC_SIZE);
> > +     _FDT(fdt_property(fdt, "reg", reg_cells, sizeof(reg_cells)));
> > +     _FDT(fdt_property_cell(fdt, "#interrupt-cells", 2));
> > +     _FDT(fdt_property(fdt, "interrupt-controller", NULL, 0));
> > +     _FDT(fdt_property_cell(fdt, "riscv,num-sources", aia_nr_sources));
> > +     _FDT(fdt_property_cell(fdt, "phandle", PHANDLE_AIA_APLIC));
> > +     _FDT(fdt_property_cell(fdt, "msi-parent", PHANDLE_AIA_IMSIC));
> > +     _FDT(fdt_end_node(fdt));
> > +}
> > +
> > +static int aia__irq_routing_init(struct kvm *kvm)
> > +{
> > +     int r;
> > +     int irqlines = aia_nr_sources + 1;
> > +
> > +     /* Skip this if we have no interrupt sources */
> > +     if (!aia_nr_sources)
> > +             return 0;
> > +
> > +     /*
> > +      * This describes the default routing that the kernel uses without
> > +      * any routing explicitly set up via KVM_SET_GSI_ROUTING. So we
> > +      * don't need to commit these setting right now. The first actual
> > +      * user (MSI routing) will engage these mappings then.
> > +      */
> > +     for (next_gsi = 0; next_gsi < irqlines; next_gsi++) {
> > +             r = irq__allocate_routing_entry();
> > +             if (r)
> > +                     return r;
> > +
> > +             irq_routing->entries[irq_routing->nr++] =
> > +                     (struct kvm_irq_routing_entry) {
> > +                             .gsi = next_gsi,
> > +                             .type = KVM_IRQ_ROUTING_IRQCHIP,
> > +                             .u.irqchip.irqchip = IRQCHIP_AIA_NR,
> > +                             .u.irqchip.pin = next_gsi,
> > +             };
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int aia__init(struct kvm *kvm)
> > +{
> > +     int i, ret;
> > +     u64 aia_addr = 0;
> > +     struct kvm_device_attr aia_addr_attr = {
> > +             .group  = KVM_DEV_RISCV_AIA_GRP_ADDR,
> > +             .addr   = (u64)(unsigned long)&aia_addr,
> > +     };
> > +     struct kvm_device_attr aia_init_attr = {
> > +             .group  = KVM_DEV_RISCV_AIA_GRP_CTRL,
> > +             .attr   = KVM_DEV_RISCV_AIA_CTRL_INIT,
> > +     };
> > +
> > +     /* Setup global device attribute variables */
> > +     aia_mode_attr.addr = (u64)(unsigned long)&aia_mode;
> > +     aia_nr_ids_attr.addr = (u64)(unsigned long)&aia_nr_ids;
> > +     aia_nr_sources_attr.addr = (u64)(unsigned long)&aia_nr_sources;
> > +     aia_hart_bits_attr.addr = (u64)(unsigned long)&aia_hart_bits;
> > +
> > +     /* Do nothing if AIA device not created */
> > +     if (aia_fd < 0)
> > +             return 0;
> > +
> > +     /* Set/Get AIA device config parameters */
> > +     ret = ioctl(aia_fd, KVM_GET_DEVICE_ATTR, &aia_mode_attr);
> > +     if (ret)
> > +             return ret;
> > +     ret = ioctl(aia_fd, KVM_GET_DEVICE_ATTR, &aia_nr_ids_attr);
> > +     if (ret)
> > +             return ret;
> > +     aia_nr_sources = irq__get_nr_allocated_lines();
> > +     ret = ioctl(aia_fd, KVM_SET_DEVICE_ATTR, &aia_nr_sources_attr);
> > +     if (ret)
> > +             return ret;
> > +     aia_hart_bits = fls_long(kvm->nrcpus);
> > +     ret = ioctl(aia_fd, KVM_SET_DEVICE_ATTR, &aia_hart_bits_attr);
> > +     if (ret)
> > +             return ret;
> > +
> > +     /* Save number of HARTs for FDT generation */
> > +     aia_nr_harts = kvm->nrcpus;
> > +
> > +     /* Set AIA device addresses */
> > +     aia_addr = AIA_APLIC_ADDR(aia_nr_harts);
> > +     aia_addr_attr.attr = KVM_DEV_RISCV_AIA_ADDR_APLIC;
> > +     ret = ioctl(aia_fd, KVM_SET_DEVICE_ATTR, &aia_addr_attr);
> > +     if (ret)
> > +             return ret;
> > +     for (i = 0; i < kvm->nrcpus; i++) {
> > +             aia_addr = AIA_IMSIC_ADDR(i);
> > +             aia_addr_attr.attr = KVM_DEV_RISCV_AIA_ADDR_IMSIC(i);
> > +             ret = ioctl(aia_fd, KVM_SET_DEVICE_ATTR, &aia_addr_attr);
> > +             if (ret)
> > +                     return ret;
> > +     }
> > +
> > +     /* Setup default IRQ routing */
> > +     aia__irq_routing_init(kvm);
> > +
> > +     /* Initialize the AIA device */
> > +     ret = ioctl(aia_fd, KVM_SET_DEVICE_ATTR, &aia_init_attr);
> > +     if (ret)
> > +             return ret;
> > +
> > +     /* Mark IRQFD as ready */
> > +     riscv_irqchip_irqfd_ready = true;
> > +
> > +     return 0;
> > +}
> > +late_init(aia__init);
> > +
> > +void aia__create(struct kvm *kvm)
> > +{
> > +     int err;
> > +     struct kvm_create_device aia_device = {
> > +             .type = KVM_DEV_TYPE_RISCV_AIA,
> > +             .flags = 0,
> > +     };
> > +
> > +     if (kvm->cfg.arch.ext_disabled[KVM_RISCV_ISA_EXT_SSAIA])
> > +             return;
> > +
> > +     err = ioctl(kvm->vm_fd, KVM_CREATE_DEVICE, &aia_device);
> > +     if (err)
> > +             return;
> > +     aia_fd = aia_device.fd;
> > +
> > +     riscv_irqchip = IRQCHIP_AIA;
> > +     riscv_irqchip_inkernel = true;
> > +     riscv_irqchip_trigger = NULL;
> > +     riscv_irqchip_generate_fdt_node = aia__generate_fdt_node;
> > +     riscv_irqchip_phandle = PHANDLE_AIA_APLIC;
> > +     riscv_irqchip_msi_phandle = PHANDLE_AIA_IMSIC;
> > +     riscv_irqchip_line_sensing = true;
> > +}
> > diff --git a/riscv/include/kvm/fdt-arch.h b/riscv/include/kvm/fdt-arch.h
> > index f7548e8..d88b832 100644
> > --- a/riscv/include/kvm/fdt-arch.h
> > +++ b/riscv/include/kvm/fdt-arch.h
> > @@ -1,7 +1,13 @@
> >  #ifndef KVM__KVM_FDT_H
> >  #define KVM__KVM_FDT_H
> >
> > -enum phandles {PHANDLE_RESERVED = 0, PHANDLE_PLIC, PHANDLES_MAX};
> > +enum phandles {
> > +     PHANDLE_RESERVED = 0,
> > +     PHANDLE_PLIC,
> > +     PHANDLE_AIA_APLIC,
> > +     PHANDLE_AIA_IMSIC,
> > +     PHANDLES_MAX
> > +};
> >
> >  #define PHANDLE_CPU_INTC_BASE        PHANDLES_MAX
> >
> > diff --git a/riscv/include/kvm/kvm-arch.h b/riscv/include/kvm/kvm-arch.h
> > index 1a8af6a..9f2159f 100644
> > --- a/riscv/include/kvm/kvm-arch.h
> > +++ b/riscv/include/kvm/kvm-arch.h
> > @@ -100,6 +100,8 @@ extern u32 riscv_irqchip_msi_phandle;
> >  extern bool riscv_irqchip_line_sensing;
> >  extern bool riscv_irqchip_irqfd_ready;
> >
> > +void aia__create(struct kvm *kvm);
> > +
>
> nit: I'd remove the blank line between *__create functions.

Okay, I will update.

>
> >  void plic__create(struct kvm *kvm);
> >
> >  void pci__generate_fdt_nodes(void *fdt);
> > diff --git a/riscv/irq.c b/riscv/irq.c
> > index e6c0939..be3e7ac 100644
> > --- a/riscv/irq.c
> > +++ b/riscv/irq.c
> > @@ -135,6 +135,9 @@ void riscv__generate_irq_prop(void *fdt, u8 irq, enum irq_type irq_type)
> >
> >  void riscv__irqchip_create(struct kvm *kvm)
> >  {
> > +     /* Try AIA in-kernel irqchip. */
> > +     aia__create(kvm);
> > +
> >       /* Try PLIC irqchip */
> >       plic__create(kvm);
>
> I realize plic__create() just returns if aia__create() successful set up
> the irqchip, but structuring this like
>
>  ret = aia__create(kvm);
>  if (!ret)
>     ret = plic__create(kvm);
>  if (!ret)
>     die(...);
>
> Might be a little easier to for future readers of the code to grok on
> first read.

Okay, I will update it so that it is obvious to readers that __create()
functions are tried one after another until one suceeds.

>
>
> Either way,
>
> Reviewed-by: Andrew Jones <ajones@ventanamicro.com>
>
> Thanks,
> drew

Regards,
Anup
diff mbox series

Patch

diff --git a/Makefile b/Makefile
index e711670..acd5ffd 100644
--- a/Makefile
+++ b/Makefile
@@ -220,6 +220,7 @@  ifeq ($(ARCH),riscv)
 	OBJS		+= riscv/kvm-cpu.o
 	OBJS		+= riscv/pci.o
 	OBJS		+= riscv/plic.o
+	OBJS		+= riscv/aia.o
 	ifeq ($(RISCV_XLEN),32)
 		CFLAGS	+= -mabi=ilp32d -march=rv32gc
 	endif
diff --git a/riscv/aia.c b/riscv/aia.c
new file mode 100644
index 0000000..8c85b3f
--- /dev/null
+++ b/riscv/aia.c
@@ -0,0 +1,227 @@ 
+#include "kvm/devices.h"
+#include "kvm/fdt.h"
+#include "kvm/ioeventfd.h"
+#include "kvm/ioport.h"
+#include "kvm/kvm.h"
+#include "kvm/kvm-cpu.h"
+#include "kvm/irq.h"
+#include "kvm/util.h"
+
+static int aia_fd = -1;
+
+static u32 aia_mode = KVM_DEV_RISCV_AIA_MODE_EMUL;
+static struct kvm_device_attr aia_mode_attr = {
+	.group	= KVM_DEV_RISCV_AIA_GRP_CONFIG,
+	.attr	= KVM_DEV_RISCV_AIA_CONFIG_MODE,
+};
+
+static u32 aia_nr_ids = 0;
+static struct kvm_device_attr aia_nr_ids_attr = {
+	.group	= KVM_DEV_RISCV_AIA_GRP_CONFIG,
+	.attr	= KVM_DEV_RISCV_AIA_CONFIG_IDS,
+};
+
+static u32 aia_nr_sources = 0;
+static struct kvm_device_attr aia_nr_sources_attr = {
+	.group	= KVM_DEV_RISCV_AIA_GRP_CONFIG,
+	.attr	= KVM_DEV_RISCV_AIA_CONFIG_SRCS,
+};
+
+static u32 aia_hart_bits = 0;
+static struct kvm_device_attr aia_hart_bits_attr = {
+	.group	= KVM_DEV_RISCV_AIA_GRP_CONFIG,
+	.attr	= KVM_DEV_RISCV_AIA_CONFIG_HART_BITS,
+};
+
+static u32 aia_nr_harts = 0;
+
+#define IRQCHIP_AIA_NR			0
+
+#define AIA_IMSIC_BASE			RISCV_IRQCHIP
+#define AIA_IMSIC_ADDR(__hart)		\
+	(AIA_IMSIC_BASE + (__hart) * KVM_DEV_RISCV_IMSIC_SIZE)
+#define AIA_IMSIC_SIZE			\
+	(aia_nr_harts * KVM_DEV_RISCV_IMSIC_SIZE)
+#define AIA_APLIC_ADDR(__nr_harts)	\
+	(AIA_IMSIC_BASE + (__nr_harts) * KVM_DEV_RISCV_IMSIC_SIZE)
+
+static void aia__generate_fdt_node(void *fdt, struct kvm *kvm)
+{
+	u32 i;
+	char name[64];
+	u32 reg_cells[4], *irq_cells;
+
+	irq_cells = calloc(aia_nr_harts * 2, sizeof(u32));
+	if (!irq_cells)
+		die("Failed to alloc irq_cells");
+
+	sprintf(name, "imsics@%08x", (u32)AIA_IMSIC_BASE);
+	_FDT(fdt_begin_node(fdt, name));
+	_FDT(fdt_property_string(fdt, "compatible", "riscv,imsics"));
+	reg_cells[0] = 0;
+	reg_cells[1] = cpu_to_fdt32(AIA_IMSIC_BASE);
+	reg_cells[2] = 0;
+	reg_cells[3] = cpu_to_fdt32(AIA_IMSIC_SIZE);
+	_FDT(fdt_property(fdt, "reg", reg_cells, sizeof(reg_cells)));
+	_FDT(fdt_property_cell(fdt, "#interrupt-cells", 0));
+	_FDT(fdt_property(fdt, "interrupt-controller", NULL, 0));
+	_FDT(fdt_property(fdt, "msi-controller", NULL, 0));
+	_FDT(fdt_property_cell(fdt, "riscv,num-ids", aia_nr_ids));
+	_FDT(fdt_property_cell(fdt, "phandle", PHANDLE_AIA_IMSIC));
+	for (i = 0; i < aia_nr_harts; i++) {
+		irq_cells[2*i + 0] = cpu_to_fdt32(PHANDLE_CPU_INTC_BASE + i);
+		irq_cells[2*i + 1] = cpu_to_fdt32(9);
+	}
+	_FDT(fdt_property(fdt, "interrupts-extended", irq_cells,
+			  sizeof(u32) * aia_nr_harts * 2));
+	_FDT(fdt_end_node(fdt));
+
+	free(irq_cells);
+
+	/* Skip APLIC node if we have no interrupt sources */
+	if (!aia_nr_sources)
+		return;
+
+	sprintf(name, "aplic@%08x", (u32)AIA_APLIC_ADDR(aia_nr_harts));
+	_FDT(fdt_begin_node(fdt, name));
+	_FDT(fdt_property_string(fdt, "compatible", "riscv,aplic"));
+	reg_cells[0] = 0;
+	reg_cells[1] = cpu_to_fdt32(AIA_APLIC_ADDR(aia_nr_harts));
+	reg_cells[2] = 0;
+	reg_cells[3] = cpu_to_fdt32(KVM_DEV_RISCV_APLIC_SIZE);
+	_FDT(fdt_property(fdt, "reg", reg_cells, sizeof(reg_cells)));
+	_FDT(fdt_property_cell(fdt, "#interrupt-cells", 2));
+	_FDT(fdt_property(fdt, "interrupt-controller", NULL, 0));
+	_FDT(fdt_property_cell(fdt, "riscv,num-sources", aia_nr_sources));
+	_FDT(fdt_property_cell(fdt, "phandle", PHANDLE_AIA_APLIC));
+	_FDT(fdt_property_cell(fdt, "msi-parent", PHANDLE_AIA_IMSIC));
+	_FDT(fdt_end_node(fdt));
+}
+
+static int aia__irq_routing_init(struct kvm *kvm)
+{
+	int r;
+	int irqlines = aia_nr_sources + 1;
+
+	/* Skip this if we have no interrupt sources */
+	if (!aia_nr_sources)
+		return 0;
+
+	/*
+	 * This describes the default routing that the kernel uses without
+	 * any routing explicitly set up via KVM_SET_GSI_ROUTING. So we
+	 * don't need to commit these setting right now. The first actual
+	 * user (MSI routing) will engage these mappings then.
+	 */
+	for (next_gsi = 0; next_gsi < irqlines; next_gsi++) {
+		r = irq__allocate_routing_entry();
+		if (r)
+			return r;
+
+		irq_routing->entries[irq_routing->nr++] =
+			(struct kvm_irq_routing_entry) {
+				.gsi = next_gsi,
+				.type = KVM_IRQ_ROUTING_IRQCHIP,
+				.u.irqchip.irqchip = IRQCHIP_AIA_NR,
+				.u.irqchip.pin = next_gsi,
+		};
+	}
+
+	return 0;
+}
+
+static int aia__init(struct kvm *kvm)
+{
+	int i, ret;
+	u64 aia_addr = 0;
+	struct kvm_device_attr aia_addr_attr = {
+		.group	= KVM_DEV_RISCV_AIA_GRP_ADDR,
+		.addr	= (u64)(unsigned long)&aia_addr,
+	};
+	struct kvm_device_attr aia_init_attr = {
+		.group	= KVM_DEV_RISCV_AIA_GRP_CTRL,
+		.attr	= KVM_DEV_RISCV_AIA_CTRL_INIT,
+	};
+
+	/* Setup global device attribute variables */
+	aia_mode_attr.addr = (u64)(unsigned long)&aia_mode;
+	aia_nr_ids_attr.addr = (u64)(unsigned long)&aia_nr_ids;
+	aia_nr_sources_attr.addr = (u64)(unsigned long)&aia_nr_sources;
+	aia_hart_bits_attr.addr = (u64)(unsigned long)&aia_hart_bits;
+
+	/* Do nothing if AIA device not created */
+	if (aia_fd < 0)
+		return 0;
+
+	/* Set/Get AIA device config parameters */
+	ret = ioctl(aia_fd, KVM_GET_DEVICE_ATTR, &aia_mode_attr);
+	if (ret)
+		return ret;
+	ret = ioctl(aia_fd, KVM_GET_DEVICE_ATTR, &aia_nr_ids_attr);
+	if (ret)
+		return ret;
+	aia_nr_sources = irq__get_nr_allocated_lines();
+	ret = ioctl(aia_fd, KVM_SET_DEVICE_ATTR, &aia_nr_sources_attr);
+	if (ret)
+		return ret;
+	aia_hart_bits = fls_long(kvm->nrcpus);
+	ret = ioctl(aia_fd, KVM_SET_DEVICE_ATTR, &aia_hart_bits_attr);
+	if (ret)
+		return ret;
+
+	/* Save number of HARTs for FDT generation */
+	aia_nr_harts = kvm->nrcpus;
+
+	/* Set AIA device addresses */
+	aia_addr = AIA_APLIC_ADDR(aia_nr_harts);
+	aia_addr_attr.attr = KVM_DEV_RISCV_AIA_ADDR_APLIC;
+	ret = ioctl(aia_fd, KVM_SET_DEVICE_ATTR, &aia_addr_attr);
+	if (ret)
+		return ret;
+	for (i = 0; i < kvm->nrcpus; i++) {
+		aia_addr = AIA_IMSIC_ADDR(i);
+		aia_addr_attr.attr = KVM_DEV_RISCV_AIA_ADDR_IMSIC(i);
+		ret = ioctl(aia_fd, KVM_SET_DEVICE_ATTR, &aia_addr_attr);
+		if (ret)
+			return ret;
+	}
+
+	/* Setup default IRQ routing */
+	aia__irq_routing_init(kvm);
+
+	/* Initialize the AIA device */
+	ret = ioctl(aia_fd, KVM_SET_DEVICE_ATTR, &aia_init_attr);
+	if (ret)
+		return ret;
+
+	/* Mark IRQFD as ready */
+	riscv_irqchip_irqfd_ready = true;
+
+	return 0;
+}
+late_init(aia__init);
+
+void aia__create(struct kvm *kvm)
+{
+	int err;
+	struct kvm_create_device aia_device = {
+		.type = KVM_DEV_TYPE_RISCV_AIA,
+		.flags = 0,
+	};
+
+	if (kvm->cfg.arch.ext_disabled[KVM_RISCV_ISA_EXT_SSAIA])
+		return;
+
+	err = ioctl(kvm->vm_fd, KVM_CREATE_DEVICE, &aia_device);
+	if (err)
+		return;
+	aia_fd = aia_device.fd;
+
+	riscv_irqchip = IRQCHIP_AIA;
+	riscv_irqchip_inkernel = true;
+	riscv_irqchip_trigger = NULL;
+	riscv_irqchip_generate_fdt_node = aia__generate_fdt_node;
+	riscv_irqchip_phandle = PHANDLE_AIA_APLIC;
+	riscv_irqchip_msi_phandle = PHANDLE_AIA_IMSIC;
+	riscv_irqchip_line_sensing = true;
+}
diff --git a/riscv/include/kvm/fdt-arch.h b/riscv/include/kvm/fdt-arch.h
index f7548e8..d88b832 100644
--- a/riscv/include/kvm/fdt-arch.h
+++ b/riscv/include/kvm/fdt-arch.h
@@ -1,7 +1,13 @@ 
 #ifndef KVM__KVM_FDT_H
 #define KVM__KVM_FDT_H
 
-enum phandles {PHANDLE_RESERVED = 0, PHANDLE_PLIC, PHANDLES_MAX};
+enum phandles {
+	PHANDLE_RESERVED = 0,
+	PHANDLE_PLIC,
+	PHANDLE_AIA_APLIC,
+	PHANDLE_AIA_IMSIC,
+	PHANDLES_MAX
+};
 
 #define PHANDLE_CPU_INTC_BASE	PHANDLES_MAX
 
diff --git a/riscv/include/kvm/kvm-arch.h b/riscv/include/kvm/kvm-arch.h
index 1a8af6a..9f2159f 100644
--- a/riscv/include/kvm/kvm-arch.h
+++ b/riscv/include/kvm/kvm-arch.h
@@ -100,6 +100,8 @@  extern u32 riscv_irqchip_msi_phandle;
 extern bool riscv_irqchip_line_sensing;
 extern bool riscv_irqchip_irqfd_ready;
 
+void aia__create(struct kvm *kvm);
+
 void plic__create(struct kvm *kvm);
 
 void pci__generate_fdt_nodes(void *fdt);
diff --git a/riscv/irq.c b/riscv/irq.c
index e6c0939..be3e7ac 100644
--- a/riscv/irq.c
+++ b/riscv/irq.c
@@ -135,6 +135,9 @@  void riscv__generate_irq_prop(void *fdt, u8 irq, enum irq_type irq_type)
 
 void riscv__irqchip_create(struct kvm *kvm)
 {
+	/* Try AIA in-kernel irqchip. */
+	aia__create(kvm);
+
 	/* Try PLIC irqchip */
 	plic__create(kvm);