blob: 21d9704145d00c43bc8b4d6e3a3697a915c7d147 [file] [log] [blame]
#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 \
(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);
_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);
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 - 1);
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_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;
}