| #include "kvm/fdt.h" |
| #include "kvm/irq.h" |
| #include "kvm/kvm.h" |
| #include "kvm/virtio.h" |
| |
| #include "arm-common/gic.h" |
| |
| #include <linux/byteorder.h> |
| #include <linux/kernel.h> |
| #include <linux/kvm.h> |
| #include <linux/sizes.h> |
| |
| #define IRQCHIP_GIC 0 |
| |
| static int gic_fd = -1; |
| static u64 gic_redists_base; |
| static u64 gic_redists_size; |
| static u64 gic_msi_base; |
| static u64 gic_msi_size = 0; |
| static bool vgic_is_init = false; |
| |
| struct kvm_irqfd_line { |
| unsigned int gsi; |
| int trigger_fd; |
| int resample_fd; |
| struct list_head list; |
| }; |
| |
| static LIST_HEAD(irqfd_lines); |
| |
| int irqchip_parser(const struct option *opt, const char *arg, int unset) |
| { |
| enum irqchip_type *type = opt->value; |
| |
| if (!strcmp(arg, "gicv2")) { |
| *type = IRQCHIP_GICV2; |
| } else if (!strcmp(arg, "gicv2m")) { |
| *type = IRQCHIP_GICV2M; |
| } else if (!strcmp(arg, "gicv3")) { |
| *type = IRQCHIP_GICV3; |
| } else if (!strcmp(arg, "gicv3-its")) { |
| *type = IRQCHIP_GICV3_ITS; |
| } else { |
| pr_err("irqchip: unknown type \"%s\"\n", arg); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| static int irq__setup_irqfd_lines(struct kvm *kvm) |
| { |
| int ret; |
| struct kvm_irqfd_line *line, *tmp; |
| |
| list_for_each_entry_safe(line, tmp, &irqfd_lines, list) { |
| ret = irq__common_add_irqfd(kvm, line->gsi, line->trigger_fd, |
| line->resample_fd); |
| if (ret < 0) { |
| pr_err("Failed to register IRQFD"); |
| return ret; |
| } |
| |
| list_del(&line->list); |
| free(line); |
| } |
| |
| return 0; |
| } |
| |
| static int irq__routing_init(struct kvm *kvm) |
| { |
| int r; |
| int irqlines = ALIGN(irq__get_nr_allocated_lines(), 32); |
| |
| /* |
| * 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_GIC, |
| .u.irqchip.pin = next_gsi, |
| }; |
| } |
| |
| return 0; |
| } |
| |
| static int gic__create_its_frame(struct kvm *kvm, u64 its_frame_addr) |
| { |
| struct kvm_create_device its_device = { |
| .type = KVM_DEV_TYPE_ARM_VGIC_ITS, |
| .flags = 0, |
| }; |
| struct kvm_device_attr its_attr = { |
| .group = KVM_DEV_ARM_VGIC_GRP_ADDR, |
| .attr = KVM_VGIC_ITS_ADDR_TYPE, |
| .addr = (u64)(unsigned long)&its_frame_addr, |
| }; |
| struct kvm_device_attr its_init_attr = { |
| .group = KVM_DEV_ARM_VGIC_GRP_CTRL, |
| .attr = KVM_DEV_ARM_VGIC_CTRL_INIT, |
| }; |
| int err; |
| |
| err = ioctl(kvm->vm_fd, KVM_CREATE_DEVICE, &its_device); |
| if (err) { |
| pr_err("GICv3 ITS requested, but kernel does not support it."); |
| pr_err("Try --irqchip=gicv3 instead"); |
| return err; |
| } |
| |
| err = ioctl(its_device.fd, KVM_HAS_DEVICE_ATTR, &its_attr); |
| if (err) { |
| close(its_device.fd); |
| its_device.fd = -1; |
| return err; |
| } |
| |
| err = ioctl(its_device.fd, KVM_SET_DEVICE_ATTR, &its_attr); |
| if (err) |
| return err; |
| |
| return ioctl(its_device.fd, KVM_SET_DEVICE_ATTR, &its_init_attr); |
| } |
| |
| static int gic__create_msi_frame(struct kvm *kvm, enum irqchip_type type, |
| u64 msi_frame_addr) |
| { |
| switch (type) { |
| case IRQCHIP_GICV2M: |
| return gic__create_gicv2m_frame(kvm, msi_frame_addr); |
| case IRQCHIP_GICV3_ITS: |
| return gic__create_its_frame(kvm, msi_frame_addr); |
| default: /* No MSI frame needed */ |
| return 0; |
| } |
| } |
| |
| static int gic__create_device(struct kvm *kvm, enum irqchip_type type) |
| { |
| int err; |
| u64 cpu_if_addr = ARM_GIC_CPUI_BASE; |
| u64 dist_addr = ARM_GIC_DIST_BASE; |
| struct kvm_create_device gic_device = { |
| .flags = 0, |
| }; |
| struct kvm_device_attr cpu_if_attr = { |
| .group = KVM_DEV_ARM_VGIC_GRP_ADDR, |
| .attr = KVM_VGIC_V2_ADDR_TYPE_CPU, |
| .addr = (u64)(unsigned long)&cpu_if_addr, |
| }; |
| struct kvm_device_attr dist_attr = { |
| .group = KVM_DEV_ARM_VGIC_GRP_ADDR, |
| .addr = (u64)(unsigned long)&dist_addr, |
| }; |
| struct kvm_device_attr redist_attr = { |
| .group = KVM_DEV_ARM_VGIC_GRP_ADDR, |
| .attr = KVM_VGIC_V3_ADDR_TYPE_REDIST, |
| .addr = (u64)(unsigned long)&gic_redists_base, |
| }; |
| |
| switch (type) { |
| case IRQCHIP_GICV2M: |
| case IRQCHIP_GICV2: |
| gic_device.type = KVM_DEV_TYPE_ARM_VGIC_V2; |
| dist_attr.attr = KVM_VGIC_V2_ADDR_TYPE_DIST; |
| break; |
| case IRQCHIP_GICV3: |
| case IRQCHIP_GICV3_ITS: |
| gic_device.type = KVM_DEV_TYPE_ARM_VGIC_V3; |
| dist_attr.attr = KVM_VGIC_V3_ADDR_TYPE_DIST; |
| break; |
| case IRQCHIP_AUTO: |
| return -ENODEV; |
| } |
| |
| err = ioctl(kvm->vm_fd, KVM_CREATE_DEVICE, &gic_device); |
| if (err) |
| return err; |
| |
| gic_fd = gic_device.fd; |
| |
| switch (type) { |
| case IRQCHIP_GICV2M: |
| case IRQCHIP_GICV2: |
| err = ioctl(gic_fd, KVM_SET_DEVICE_ATTR, &cpu_if_attr); |
| break; |
| case IRQCHIP_GICV3_ITS: |
| case IRQCHIP_GICV3: |
| err = ioctl(gic_fd, KVM_SET_DEVICE_ATTR, &redist_attr); |
| break; |
| case IRQCHIP_AUTO: |
| return -ENODEV; |
| } |
| if (err) |
| goto out_err; |
| |
| err = ioctl(gic_fd, KVM_SET_DEVICE_ATTR, &dist_attr); |
| if (err) |
| goto out_err; |
| |
| err = gic__create_msi_frame(kvm, type, gic_msi_base); |
| if (err) |
| goto out_err; |
| |
| return 0; |
| |
| out_err: |
| close(gic_fd); |
| gic_fd = -1; |
| return err; |
| } |
| |
| static int gic__create_irqchip(struct kvm *kvm) |
| { |
| int err; |
| struct kvm_arm_device_addr gic_addr[] = { |
| [0] = { |
| .id = KVM_VGIC_V2_ADDR_TYPE_DIST | |
| (KVM_ARM_DEVICE_VGIC_V2 << KVM_ARM_DEVICE_ID_SHIFT), |
| .addr = ARM_GIC_DIST_BASE, |
| }, |
| [1] = { |
| .id = KVM_VGIC_V2_ADDR_TYPE_CPU | |
| (KVM_ARM_DEVICE_VGIC_V2 << KVM_ARM_DEVICE_ID_SHIFT), |
| .addr = ARM_GIC_CPUI_BASE, |
| } |
| }; |
| |
| err = ioctl(kvm->vm_fd, KVM_CREATE_IRQCHIP); |
| if (err) |
| return err; |
| |
| err = ioctl(kvm->vm_fd, KVM_ARM_SET_DEVICE_ADDR, &gic_addr[0]); |
| if (err) |
| return err; |
| |
| err = ioctl(kvm->vm_fd, KVM_ARM_SET_DEVICE_ADDR, &gic_addr[1]); |
| return err; |
| } |
| |
| int gic__create(struct kvm *kvm, enum irqchip_type type) |
| { |
| enum irqchip_type try; |
| int err; |
| |
| switch (type) { |
| case IRQCHIP_AUTO: |
| for (try = IRQCHIP_GICV3_ITS; try >= IRQCHIP_GICV2; try--) { |
| err = gic__create(kvm, try); |
| if (!err) |
| break; |
| } |
| if (err) |
| return err; |
| |
| kvm->cfg.arch.irqchip = try; |
| return 0; |
| case IRQCHIP_GICV2M: |
| gic_msi_size = KVM_VGIC_V2M_SIZE; |
| gic_msi_base = ARM_GIC_CPUI_BASE - gic_msi_size; |
| break; |
| case IRQCHIP_GICV2: |
| break; |
| case IRQCHIP_GICV3_ITS: |
| /* The 64K page with the doorbell is included. */ |
| gic_msi_size = KVM_VGIC_V3_ITS_SIZE; |
| /* fall through */ |
| case IRQCHIP_GICV3: |
| gic_redists_size = kvm->cfg.nrcpus * ARM_GIC_REDIST_SIZE; |
| gic_redists_base = ARM_GIC_DIST_BASE - gic_redists_size; |
| gic_msi_base = gic_redists_base - gic_msi_size; |
| break; |
| default: |
| return -ENODEV; |
| } |
| |
| /* Try the new way first, and fallback on legacy method otherwise */ |
| err = gic__create_device(kvm, type); |
| if (err && type == IRQCHIP_GICV2) |
| err = gic__create_irqchip(kvm); |
| |
| return err; |
| } |
| |
| /* |
| * Sets the number of used interrupts and finalizes the GIC init explicitly. |
| */ |
| static int gic__init_gic(struct kvm *kvm) |
| { |
| int ret; |
| |
| int lines = irq__get_nr_allocated_lines(); |
| u32 nr_irqs = ALIGN(lines, 32) + GIC_SPI_IRQ_BASE; |
| struct kvm_device_attr nr_irqs_attr = { |
| .group = KVM_DEV_ARM_VGIC_GRP_NR_IRQS, |
| .addr = (u64)(unsigned long)&nr_irqs, |
| }; |
| struct kvm_device_attr vgic_init_attr = { |
| .group = KVM_DEV_ARM_VGIC_GRP_CTRL, |
| .attr = KVM_DEV_ARM_VGIC_CTRL_INIT, |
| }; |
| |
| /* |
| * If we didn't use the KVM_CREATE_DEVICE method, KVM will |
| * give us some default number of interrupts. The GIC initialization |
| * will be done automatically in this case. |
| */ |
| if (gic_fd < 0) |
| return 0; |
| |
| if (!ioctl(gic_fd, KVM_HAS_DEVICE_ATTR, &nr_irqs_attr)) { |
| ret = ioctl(gic_fd, KVM_SET_DEVICE_ATTR, &nr_irqs_attr); |
| if (ret) |
| return ret; |
| } |
| |
| irq__routing_init(kvm); |
| |
| if (!ioctl(gic_fd, KVM_HAS_DEVICE_ATTR, &vgic_init_attr)) { |
| ret = ioctl(gic_fd, KVM_SET_DEVICE_ATTR, &vgic_init_attr); |
| if (ret) |
| return ret; |
| } |
| |
| kvm->msix_needs_devid = kvm__supports_vm_extension(kvm, |
| KVM_CAP_MSI_DEVID); |
| |
| vgic_is_init = true; |
| |
| return irq__setup_irqfd_lines(kvm); |
| } |
| late_init(gic__init_gic) |
| |
| void gic__generate_fdt_nodes(void *fdt, enum irqchip_type type) |
| { |
| const char *compatible, *msi_compatible = NULL; |
| u64 msi_prop[2]; |
| u64 reg_prop[] = { |
| cpu_to_fdt64(ARM_GIC_DIST_BASE), cpu_to_fdt64(ARM_GIC_DIST_SIZE), |
| 0, 0, /* to be filled */ |
| }; |
| |
| switch (type) { |
| case IRQCHIP_GICV2M: |
| msi_compatible = "arm,gic-v2m-frame"; |
| /* fall-through */ |
| case IRQCHIP_GICV2: |
| compatible = "arm,cortex-a15-gic"; |
| reg_prop[2] = cpu_to_fdt64(ARM_GIC_CPUI_BASE); |
| reg_prop[3] = cpu_to_fdt64(ARM_GIC_CPUI_SIZE); |
| break; |
| case IRQCHIP_GICV3_ITS: |
| msi_compatible = "arm,gic-v3-its"; |
| /* fall-through */ |
| case IRQCHIP_GICV3: |
| compatible = "arm,gic-v3"; |
| reg_prop[2] = cpu_to_fdt64(gic_redists_base); |
| reg_prop[3] = cpu_to_fdt64(gic_redists_size); |
| break; |
| default: |
| return; |
| } |
| |
| _FDT(fdt_begin_node(fdt, "intc")); |
| _FDT(fdt_property_string(fdt, "compatible", compatible)); |
| _FDT(fdt_property_cell(fdt, "#interrupt-cells", GIC_FDT_IRQ_NUM_CELLS)); |
| _FDT(fdt_property(fdt, "interrupt-controller", NULL, 0)); |
| _FDT(fdt_property(fdt, "reg", reg_prop, sizeof(reg_prop))); |
| _FDT(fdt_property_cell(fdt, "phandle", PHANDLE_GIC)); |
| _FDT(fdt_property_cell(fdt, "#address-cells", 2)); |
| _FDT(fdt_property_cell(fdt, "#size-cells", 2)); |
| |
| if (msi_compatible) { |
| _FDT(fdt_property(fdt, "ranges", NULL, 0)); |
| |
| _FDT(fdt_begin_node(fdt, "msic")); |
| _FDT(fdt_property_string(fdt, "compatible", msi_compatible)); |
| _FDT(fdt_property(fdt, "msi-controller", NULL, 0)); |
| _FDT(fdt_property_cell(fdt, "phandle", PHANDLE_MSI)); |
| msi_prop[0] = cpu_to_fdt64(gic_msi_base); |
| msi_prop[1] = cpu_to_fdt64(gic_msi_size); |
| _FDT(fdt_property(fdt, "reg", msi_prop, sizeof(msi_prop))); |
| _FDT(fdt_end_node(fdt)); |
| } |
| |
| _FDT(fdt_end_node(fdt)); |
| } |
| |
| u32 gic__get_fdt_irq_cpumask(struct kvm *kvm) |
| { |
| /* Only for GICv2 */ |
| if (kvm->cfg.arch.irqchip == IRQCHIP_GICV3 || |
| kvm->cfg.arch.irqchip == IRQCHIP_GICV3_ITS) |
| return 0; |
| |
| if (kvm->nrcpus > 8) |
| return GIC_FDT_IRQ_PPI_CPU_MASK; |
| |
| return ((1U << kvm->nrcpus) - 1) << GIC_FDT_IRQ_PPI_CPU_SHIFT; |
| } |
| |
| #define KVM_IRQCHIP_IRQ(x) (KVM_ARM_IRQ_TYPE_SPI << KVM_ARM_IRQ_TYPE_SHIFT) |\ |
| ((x) & KVM_ARM_IRQ_NUM_MASK) |
| |
| void kvm__irq_line(struct kvm *kvm, int irq, int level) |
| { |
| struct kvm_irq_level irq_level = { |
| .irq = KVM_IRQCHIP_IRQ(irq), |
| .level = !!level, |
| }; |
| |
| if (irq < GIC_SPI_IRQ_BASE || irq > GIC_MAX_IRQ) |
| pr_warning("Ignoring invalid GIC IRQ %d", irq); |
| else if (ioctl(kvm->vm_fd, KVM_IRQ_LINE, &irq_level) < 0) |
| pr_warning("Could not KVM_IRQ_LINE for irq %d", irq); |
| } |
| |
| void kvm__irq_trigger(struct kvm *kvm, int irq) |
| { |
| kvm__irq_line(kvm, irq, VIRTIO_IRQ_HIGH); |
| kvm__irq_line(kvm, irq, VIRTIO_IRQ_LOW); |
| } |
| |
| int gic__add_irqfd(struct kvm *kvm, unsigned int gsi, int trigger_fd, |
| int resample_fd) |
| { |
| struct kvm_irqfd_line *line; |
| |
| if (vgic_is_init) |
| return irq__common_add_irqfd(kvm, gsi, trigger_fd, resample_fd); |
| |
| /* Postpone the routing setup until we have a distributor */ |
| line = malloc(sizeof(*line)); |
| if (!line) |
| return -ENOMEM; |
| |
| *line = (struct kvm_irqfd_line) { |
| .gsi = gsi, |
| .trigger_fd = trigger_fd, |
| .resample_fd = resample_fd, |
| }; |
| list_add(&line->list, &irqfd_lines); |
| |
| return 0; |
| } |
| |
| void gic__del_irqfd(struct kvm *kvm, unsigned int gsi, int trigger_fd) |
| { |
| struct kvm_irqfd_line *line; |
| |
| if (vgic_is_init) { |
| irq__common_del_irqfd(kvm, gsi, trigger_fd); |
| return; |
| } |
| |
| list_for_each_entry(line, &irqfd_lines, list) { |
| if (line->gsi != gsi) |
| continue; |
| |
| list_del(&line->list); |
| free(line); |
| break; |
| } |
| } |