| #include "kvm/kvm.h" |
| #include "kvm/kvm-cpu.h" |
| #include "kvm/irq.h" |
| #include "kvm/fdt.h" |
| #include "kvm/virtio.h" |
| |
| enum irqchip_type riscv_irqchip = IRQCHIP_UNKNOWN; |
| bool riscv_irqchip_inkernel; |
| void (*riscv_irqchip_trigger)(struct kvm *kvm, int irq, int level, bool edge) |
| = NULL; |
| void (*riscv_irqchip_generate_fdt_node)(void *fdt, struct kvm *kvm) = NULL; |
| u32 riscv_irqchip_phandle = PHANDLE_RESERVED; |
| u32 riscv_irqchip_msi_phandle = PHANDLE_RESERVED; |
| bool riscv_irqchip_line_sensing; |
| bool riscv_irqchip_irqfd_ready; |
| |
| void kvm__irq_line(struct kvm *kvm, int irq, int level) |
| { |
| struct kvm_irq_level irq_level; |
| |
| if (riscv_irqchip_inkernel) { |
| irq_level.irq = irq; |
| irq_level.level = !!level; |
| if (ioctl(kvm->vm_fd, KVM_IRQ_LINE, &irq_level) < 0) |
| pr_warning("%s: Could not KVM_IRQ_LINE for irq %d\n", |
| __func__, irq); |
| } else { |
| if (riscv_irqchip_trigger) |
| riscv_irqchip_trigger(kvm, irq, level, false); |
| else |
| pr_warning("%s: Can't change level for irq %d\n", |
| __func__, irq); |
| } |
| } |
| |
| void kvm__irq_trigger(struct kvm *kvm, int irq) |
| { |
| if (riscv_irqchip_inkernel) { |
| kvm__irq_line(kvm, irq, VIRTIO_IRQ_HIGH); |
| kvm__irq_line(kvm, irq, VIRTIO_IRQ_LOW); |
| } else { |
| if (riscv_irqchip_trigger) |
| riscv_irqchip_trigger(kvm, irq, 1, true); |
| else |
| pr_warning("%s: Can't trigger irq %d\n", |
| __func__, irq); |
| } |
| } |
| |
| struct riscv_irqfd_line { |
| unsigned int gsi; |
| int trigger_fd; |
| int resample_fd; |
| struct list_head list; |
| }; |
| |
| static LIST_HEAD(irqfd_lines); |
| |
| int riscv__add_irqfd(struct kvm *kvm, unsigned int gsi, int trigger_fd, |
| int resample_fd) |
| { |
| struct riscv_irqfd_line *line; |
| |
| if (riscv_irqchip_irqfd_ready) |
| return irq__common_add_irqfd(kvm, gsi, trigger_fd, |
| resample_fd); |
| |
| /* Postpone the routing setup until irqchip is initialized */ |
| line = malloc(sizeof(*line)); |
| if (!line) |
| return -ENOMEM; |
| |
| *line = (struct riscv_irqfd_line) { |
| .gsi = gsi, |
| .trigger_fd = trigger_fd, |
| .resample_fd = resample_fd, |
| }; |
| list_add(&line->list, &irqfd_lines); |
| |
| return 0; |
| } |
| |
| void riscv__del_irqfd(struct kvm *kvm, unsigned int gsi, int trigger_fd) |
| { |
| struct riscv_irqfd_line *line; |
| |
| if (riscv_irqchip_irqfd_ready) { |
| 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; |
| } |
| } |
| |
| int riscv__setup_irqfd_lines(struct kvm *kvm) |
| { |
| int ret; |
| struct riscv_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; |
| } |
| |
| void riscv__generate_irq_prop(void *fdt, u8 irq, enum irq_type irq_type) |
| { |
| u32 prop[2], size; |
| |
| prop[0] = cpu_to_fdt32(irq); |
| size = sizeof(u32); |
| if (riscv_irqchip_line_sensing) { |
| prop[1] = cpu_to_fdt32(irq_type); |
| size += sizeof(u32); |
| } |
| |
| _FDT(fdt_property(fdt, "interrupts", prop, size)); |
| } |
| |
| static void (*riscv__irqchip_create_funcs[])(struct kvm *kvm) = { |
| aia__create, |
| plic__create, |
| }; |
| |
| void riscv__irqchip_create(struct kvm *kvm) |
| { |
| unsigned int i; |
| |
| /* Try irqchip.create function one after another */ |
| for (i = 0; i < ARRAY_SIZE(riscv__irqchip_create_funcs); i++) { |
| riscv__irqchip_create_funcs[i](kvm); |
| if (riscv_irqchip != IRQCHIP_UNKNOWN) |
| return; |
| } |
| |
| /* Fail since irqchip is unknown */ |
| die("No IRQCHIP found\n"); |
| } |