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 16*f6108d72SJean-Philippe Brucker struct msi_routing_ops irq__default_routing_ops; 17*f6108d72SJean-Philippe Brucker struct msi_routing_ops *msi_routing_ops = &irq__default_routing_ops; 18*f6108d72SJean-Philippe Brucker 198ccc8549SAndre Przywara struct kvm_irq_routing *irq_routing = NULL; 209dc5430cSWill Deacon 219dc5430cSWill Deacon int irq__alloc_line(void) 229dc5430cSWill Deacon { 239dc5430cSWill Deacon return next_line++; 249dc5430cSWill Deacon } 25cb87229bSMarc Zyngier 26cb87229bSMarc Zyngier int irq__get_nr_allocated_lines(void) 27cb87229bSMarc Zyngier { 28cb87229bSMarc Zyngier return next_line - KVM_IRQ_OFFSET; 29cb87229bSMarc Zyngier } 308ccc8549SAndre Przywara 318ccc8549SAndre Przywara int irq__allocate_routing_entry(void) 328ccc8549SAndre Przywara { 338ccc8549SAndre Przywara size_t table_size = sizeof(struct kvm_irq_routing); 348ccc8549SAndre Przywara size_t old_size = table_size; 358ccc8549SAndre Przywara int nr_entries = 0; 368ccc8549SAndre Przywara 378ccc8549SAndre Przywara if (irq_routing) 388ccc8549SAndre Przywara nr_entries = irq_routing->nr; 398ccc8549SAndre Przywara 408ccc8549SAndre Przywara if (nr_entries < allocated_gsis) 418ccc8549SAndre Przywara return 0; 428ccc8549SAndre Przywara 438ccc8549SAndre Przywara old_size += sizeof(struct kvm_irq_routing_entry) * allocated_gsis; 448ccc8549SAndre Przywara allocated_gsis = ALIGN(nr_entries + 1, 32); 458ccc8549SAndre Przywara table_size += sizeof(struct kvm_irq_routing_entry) * allocated_gsis; 468ccc8549SAndre Przywara irq_routing = realloc(irq_routing, table_size); 478ccc8549SAndre Przywara 488ccc8549SAndre Przywara if (irq_routing == NULL) 498ccc8549SAndre Przywara return -ENOMEM; 508ccc8549SAndre Przywara memset((void *)irq_routing + old_size, 0, table_size - old_size); 518ccc8549SAndre Przywara 528ccc8549SAndre Przywara irq_routing->nr = nr_entries; 538ccc8549SAndre Przywara irq_routing->flags = 0; 548ccc8549SAndre Przywara 558ccc8549SAndre Przywara return 0; 568ccc8549SAndre Przywara } 578ccc8549SAndre Przywara 588ccc8549SAndre Przywara static bool check_for_irq_routing(struct kvm *kvm) 598ccc8549SAndre Przywara { 608ccc8549SAndre Przywara static int has_irq_routing = 0; 618ccc8549SAndre Przywara 628ccc8549SAndre Przywara if (has_irq_routing == 0) { 638ccc8549SAndre Przywara if (kvm__supports_extension(kvm, KVM_CAP_IRQ_ROUTING)) 648ccc8549SAndre Przywara has_irq_routing = 1; 658ccc8549SAndre Przywara else 668ccc8549SAndre Przywara has_irq_routing = -1; 678ccc8549SAndre Przywara } 688ccc8549SAndre Przywara 698ccc8549SAndre Przywara return has_irq_routing > 0; 708ccc8549SAndre Przywara } 718ccc8549SAndre Przywara 72*f6108d72SJean-Philippe Brucker static int irq__update_msix_routes(struct kvm *kvm, 73*f6108d72SJean-Philippe Brucker struct kvm_irq_routing_entry *entry) 74*f6108d72SJean-Philippe Brucker { 75*f6108d72SJean-Philippe Brucker return ioctl(kvm->vm_fd, KVM_SET_GSI_ROUTING, irq_routing); 76*f6108d72SJean-Philippe Brucker } 77*f6108d72SJean-Philippe Brucker 78*f6108d72SJean-Philippe Brucker static bool irq__default_can_signal_msi(struct kvm *kvm) 79*f6108d72SJean-Philippe Brucker { 80*f6108d72SJean-Philippe Brucker return kvm__supports_extension(kvm, KVM_CAP_SIGNAL_MSI); 81*f6108d72SJean-Philippe Brucker } 82*f6108d72SJean-Philippe Brucker 83*f6108d72SJean-Philippe Brucker static int irq__default_signal_msi(struct kvm *kvm, struct kvm_msi *msi) 84*f6108d72SJean-Philippe Brucker { 85*f6108d72SJean-Philippe Brucker return ioctl(kvm->vm_fd, KVM_SIGNAL_MSI, msi); 86*f6108d72SJean-Philippe Brucker } 87*f6108d72SJean-Philippe Brucker 88*f6108d72SJean-Philippe Brucker struct msi_routing_ops irq__default_routing_ops = { 89*f6108d72SJean-Philippe Brucker .update_route = irq__update_msix_routes, 90*f6108d72SJean-Philippe Brucker .signal_msi = irq__default_signal_msi, 91*f6108d72SJean-Philippe Brucker .can_signal_msi = irq__default_can_signal_msi, 92*f6108d72SJean-Philippe Brucker }; 93*f6108d72SJean-Philippe Brucker 94*f6108d72SJean-Philippe Brucker bool irq__can_signal_msi(struct kvm *kvm) 95*f6108d72SJean-Philippe Brucker { 96*f6108d72SJean-Philippe Brucker return msi_routing_ops->can_signal_msi(kvm); 97*f6108d72SJean-Philippe Brucker } 98*f6108d72SJean-Philippe Brucker 99*f6108d72SJean-Philippe Brucker int irq__signal_msi(struct kvm *kvm, struct kvm_msi *msi) 100*f6108d72SJean-Philippe Brucker { 101*f6108d72SJean-Philippe Brucker return msi_routing_ops->signal_msi(kvm, msi); 102*f6108d72SJean-Philippe Brucker } 103*f6108d72SJean-Philippe Brucker 104f9ef46f2SAndre Przywara int irq__add_msix_route(struct kvm *kvm, struct msi_msg *msg, u32 device_id) 1058ccc8549SAndre Przywara { 1068ccc8549SAndre Przywara int r; 107*f6108d72SJean-Philippe Brucker struct kvm_irq_routing_entry *entry; 1088ccc8549SAndre Przywara 1098ccc8549SAndre Przywara if (!check_for_irq_routing(kvm)) 1108ccc8549SAndre Przywara return -ENXIO; 1118ccc8549SAndre Przywara 1128ccc8549SAndre Przywara r = irq__allocate_routing_entry(); 1138ccc8549SAndre Przywara if (r) 1148ccc8549SAndre Przywara return r; 1158ccc8549SAndre Przywara 116*f6108d72SJean-Philippe Brucker entry = &irq_routing->entries[irq_routing->nr]; 117*f6108d72SJean-Philippe Brucker *entry = (struct kvm_irq_routing_entry) { 1188ccc8549SAndre Przywara .gsi = next_gsi, 1198ccc8549SAndre Przywara .type = KVM_IRQ_ROUTING_MSI, 1208ccc8549SAndre Przywara .u.msi.address_hi = msg->address_hi, 1218ccc8549SAndre Przywara .u.msi.address_lo = msg->address_lo, 1228ccc8549SAndre Przywara .u.msi.data = msg->data, 1238ccc8549SAndre Przywara }; 1248ccc8549SAndre Przywara 125f9ef46f2SAndre Przywara if (kvm->msix_needs_devid) { 126*f6108d72SJean-Philippe Brucker entry->flags = KVM_MSI_VALID_DEVID; 127*f6108d72SJean-Philippe Brucker entry->u.msi.devid = device_id; 128f9ef46f2SAndre Przywara } 129*f6108d72SJean-Philippe Brucker 130f9ef46f2SAndre Przywara irq_routing->nr++; 131f9ef46f2SAndre Przywara 132*f6108d72SJean-Philippe Brucker r = msi_routing_ops->update_route(kvm, entry); 1338ccc8549SAndre Przywara if (r) 1348ccc8549SAndre Przywara return r; 1358ccc8549SAndre Przywara 1368ccc8549SAndre Przywara return next_gsi++; 1378ccc8549SAndre Przywara } 1388ccc8549SAndre Przywara 1396518065aSAndre Przywara static bool update_data(u32 *ptr, u32 newdata) 1406518065aSAndre Przywara { 1416518065aSAndre Przywara if (*ptr == newdata) 1426518065aSAndre Przywara return false; 1436518065aSAndre Przywara 1446518065aSAndre Przywara *ptr = newdata; 1456518065aSAndre Przywara return true; 1466518065aSAndre Przywara } 1476518065aSAndre Przywara 1486518065aSAndre Przywara void irq__update_msix_route(struct kvm *kvm, u32 gsi, struct msi_msg *msg) 1496518065aSAndre Przywara { 1506518065aSAndre Przywara struct kvm_irq_routing_msi *entry; 1516518065aSAndre Przywara unsigned int i; 1526518065aSAndre Przywara bool changed; 1536518065aSAndre Przywara 1546518065aSAndre Przywara for (i = 0; i < irq_routing->nr; i++) 1556518065aSAndre Przywara if (gsi == irq_routing->entries[i].gsi) 1566518065aSAndre Przywara break; 1576518065aSAndre Przywara if (i == irq_routing->nr) 1586518065aSAndre Przywara return; 1596518065aSAndre Przywara 1606518065aSAndre Przywara entry = &irq_routing->entries[i].u.msi; 1616518065aSAndre Przywara 1626518065aSAndre Przywara changed = update_data(&entry->address_hi, msg->address_hi); 1636518065aSAndre Przywara changed |= update_data(&entry->address_lo, msg->address_lo); 1646518065aSAndre Przywara changed |= update_data(&entry->data, msg->data); 1656518065aSAndre Przywara 1666518065aSAndre Przywara if (!changed) 1676518065aSAndre Przywara return; 1686518065aSAndre Przywara 169*f6108d72SJean-Philippe Brucker if (msi_routing_ops->update_route(kvm, &irq_routing->entries[i])) 1706518065aSAndre Przywara die_perror("KVM_SET_GSI_ROUTING"); 1716518065aSAndre Przywara } 1726518065aSAndre Przywara 1738ccc8549SAndre Przywara int __attribute__((weak)) irq__exit(struct kvm *kvm) 1748ccc8549SAndre Przywara { 1758ccc8549SAndre Przywara free(irq_routing); 1768ccc8549SAndre Przywara return 0; 1778ccc8549SAndre Przywara } 1788ccc8549SAndre Przywara dev_base_exit(irq__exit); 179