18ccc8549SAndre Przywara #include <stdlib.h> 28ccc8549SAndre Przywara #include <sys/ioctl.h> 38ccc8549SAndre Przywara #include <linux/types.h> 48ccc8549SAndre Przywara #include <linux/kvm.h> 58ccc8549SAndre Przywara #include <errno.h> 68ccc8549SAndre Przywara 78ccc8549SAndre Przywara #include "kvm/kvm.h" 89dc5430cSWill Deacon #include "kvm/irq.h" 99dc5430cSWill Deacon #include "kvm/kvm-arch.h" 109dc5430cSWill Deacon 119dc5430cSWill Deacon static u8 next_line = KVM_IRQ_OFFSET; 128ccc8549SAndre Przywara static int allocated_gsis = 0; 138ccc8549SAndre Przywara 148ccc8549SAndre Przywara int next_gsi; 158ccc8549SAndre Przywara 168ccc8549SAndre Przywara struct kvm_irq_routing *irq_routing = NULL; 179dc5430cSWill Deacon 189dc5430cSWill Deacon int irq__alloc_line(void) 199dc5430cSWill Deacon { 209dc5430cSWill Deacon return next_line++; 219dc5430cSWill Deacon } 22cb87229bSMarc Zyngier 23cb87229bSMarc Zyngier int irq__get_nr_allocated_lines(void) 24cb87229bSMarc Zyngier { 25cb87229bSMarc Zyngier return next_line - KVM_IRQ_OFFSET; 26cb87229bSMarc Zyngier } 278ccc8549SAndre Przywara 288ccc8549SAndre Przywara int irq__allocate_routing_entry(void) 298ccc8549SAndre Przywara { 308ccc8549SAndre Przywara size_t table_size = sizeof(struct kvm_irq_routing); 318ccc8549SAndre Przywara size_t old_size = table_size; 328ccc8549SAndre Przywara int nr_entries = 0; 338ccc8549SAndre Przywara 348ccc8549SAndre Przywara if (irq_routing) 358ccc8549SAndre Przywara nr_entries = irq_routing->nr; 368ccc8549SAndre Przywara 378ccc8549SAndre Przywara if (nr_entries < allocated_gsis) 388ccc8549SAndre Przywara return 0; 398ccc8549SAndre Przywara 408ccc8549SAndre Przywara old_size += sizeof(struct kvm_irq_routing_entry) * allocated_gsis; 418ccc8549SAndre Przywara allocated_gsis = ALIGN(nr_entries + 1, 32); 428ccc8549SAndre Przywara table_size += sizeof(struct kvm_irq_routing_entry) * allocated_gsis; 438ccc8549SAndre Przywara irq_routing = realloc(irq_routing, table_size); 448ccc8549SAndre Przywara 458ccc8549SAndre Przywara if (irq_routing == NULL) 468ccc8549SAndre Przywara return -ENOMEM; 478ccc8549SAndre Przywara memset((void *)irq_routing + old_size, 0, table_size - old_size); 488ccc8549SAndre Przywara 498ccc8549SAndre Przywara irq_routing->nr = nr_entries; 508ccc8549SAndre Przywara irq_routing->flags = 0; 518ccc8549SAndre Przywara 528ccc8549SAndre Przywara return 0; 538ccc8549SAndre Przywara } 548ccc8549SAndre Przywara 558ccc8549SAndre Przywara static bool check_for_irq_routing(struct kvm *kvm) 568ccc8549SAndre Przywara { 578ccc8549SAndre Przywara static int has_irq_routing = 0; 588ccc8549SAndre Przywara 598ccc8549SAndre Przywara if (has_irq_routing == 0) { 608ccc8549SAndre Przywara if (kvm__supports_extension(kvm, KVM_CAP_IRQ_ROUTING)) 618ccc8549SAndre Przywara has_irq_routing = 1; 628ccc8549SAndre Przywara else 638ccc8549SAndre Przywara has_irq_routing = -1; 648ccc8549SAndre Przywara } 658ccc8549SAndre Przywara 668ccc8549SAndre Przywara return has_irq_routing > 0; 678ccc8549SAndre Przywara } 688ccc8549SAndre Przywara 698ccc8549SAndre Przywara int irq__add_msix_route(struct kvm *kvm, struct msi_msg *msg) 708ccc8549SAndre Przywara { 718ccc8549SAndre Przywara int r; 728ccc8549SAndre Przywara 738ccc8549SAndre Przywara if (!check_for_irq_routing(kvm)) 748ccc8549SAndre Przywara return -ENXIO; 758ccc8549SAndre Przywara 768ccc8549SAndre Przywara r = irq__allocate_routing_entry(); 778ccc8549SAndre Przywara if (r) 788ccc8549SAndre Przywara return r; 798ccc8549SAndre Przywara 808ccc8549SAndre Przywara irq_routing->entries[irq_routing->nr++] = 818ccc8549SAndre Przywara (struct kvm_irq_routing_entry) { 828ccc8549SAndre Przywara .gsi = next_gsi, 838ccc8549SAndre Przywara .type = KVM_IRQ_ROUTING_MSI, 848ccc8549SAndre Przywara .u.msi.address_hi = msg->address_hi, 858ccc8549SAndre Przywara .u.msi.address_lo = msg->address_lo, 868ccc8549SAndre Przywara .u.msi.data = msg->data, 878ccc8549SAndre Przywara }; 888ccc8549SAndre Przywara 898ccc8549SAndre Przywara r = ioctl(kvm->vm_fd, KVM_SET_GSI_ROUTING, irq_routing); 908ccc8549SAndre Przywara if (r) 918ccc8549SAndre Przywara return r; 928ccc8549SAndre Przywara 938ccc8549SAndre Przywara return next_gsi++; 948ccc8549SAndre Przywara } 958ccc8549SAndre Przywara 96*6518065aSAndre Przywara static bool update_data(u32 *ptr, u32 newdata) 97*6518065aSAndre Przywara { 98*6518065aSAndre Przywara if (*ptr == newdata) 99*6518065aSAndre Przywara return false; 100*6518065aSAndre Przywara 101*6518065aSAndre Przywara *ptr = newdata; 102*6518065aSAndre Przywara return true; 103*6518065aSAndre Przywara } 104*6518065aSAndre Przywara 105*6518065aSAndre Przywara void irq__update_msix_route(struct kvm *kvm, u32 gsi, struct msi_msg *msg) 106*6518065aSAndre Przywara { 107*6518065aSAndre Przywara struct kvm_irq_routing_msi *entry; 108*6518065aSAndre Przywara unsigned int i; 109*6518065aSAndre Przywara bool changed; 110*6518065aSAndre Przywara 111*6518065aSAndre Przywara for (i = 0; i < irq_routing->nr; i++) 112*6518065aSAndre Przywara if (gsi == irq_routing->entries[i].gsi) 113*6518065aSAndre Przywara break; 114*6518065aSAndre Przywara if (i == irq_routing->nr) 115*6518065aSAndre Przywara return; 116*6518065aSAndre Przywara 117*6518065aSAndre Przywara entry = &irq_routing->entries[i].u.msi; 118*6518065aSAndre Przywara 119*6518065aSAndre Przywara changed = update_data(&entry->address_hi, msg->address_hi); 120*6518065aSAndre Przywara changed |= update_data(&entry->address_lo, msg->address_lo); 121*6518065aSAndre Przywara changed |= update_data(&entry->data, msg->data); 122*6518065aSAndre Przywara 123*6518065aSAndre Przywara if (!changed) 124*6518065aSAndre Przywara return; 125*6518065aSAndre Przywara 126*6518065aSAndre Przywara if (ioctl(kvm->vm_fd, KVM_SET_GSI_ROUTING, irq_routing) == -1) 127*6518065aSAndre Przywara die_perror("KVM_SET_GSI_ROUTING"); 128*6518065aSAndre Przywara } 129*6518065aSAndre Przywara 1308ccc8549SAndre Przywara int __attribute__((weak)) irq__exit(struct kvm *kvm) 1318ccc8549SAndre Przywara { 1328ccc8549SAndre Przywara free(irq_routing); 1338ccc8549SAndre Przywara return 0; 1348ccc8549SAndre Przywara } 1358ccc8549SAndre Przywara dev_base_exit(irq__exit); 136