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
16f6108d72SJean-Philippe Brucker struct msi_routing_ops irq__default_routing_ops;
17f6108d72SJean-Philippe Brucker struct msi_routing_ops *msi_routing_ops = &irq__default_routing_ops;
18f6108d72SJean-Philippe Brucker
198ccc8549SAndre Przywara struct kvm_irq_routing *irq_routing = NULL;
209dc5430cSWill Deacon
irq__alloc_line(void)219dc5430cSWill Deacon int irq__alloc_line(void)
229dc5430cSWill Deacon {
239dc5430cSWill Deacon return next_line++;
249dc5430cSWill Deacon }
25cb87229bSMarc Zyngier
irq__get_nr_allocated_lines(void)26cb87229bSMarc Zyngier int irq__get_nr_allocated_lines(void)
27cb87229bSMarc Zyngier {
28cb87229bSMarc Zyngier return next_line - KVM_IRQ_OFFSET;
29cb87229bSMarc Zyngier }
308ccc8549SAndre Przywara
irq__allocate_routing_entry(void)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
check_for_irq_routing(struct kvm * kvm)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
irq__update_msix_routes(struct kvm * kvm,struct kvm_irq_routing_entry * entry)72f6108d72SJean-Philippe Brucker static int irq__update_msix_routes(struct kvm *kvm,
73f6108d72SJean-Philippe Brucker struct kvm_irq_routing_entry *entry)
74f6108d72SJean-Philippe Brucker {
75f6108d72SJean-Philippe Brucker return ioctl(kvm->vm_fd, KVM_SET_GSI_ROUTING, irq_routing);
76f6108d72SJean-Philippe Brucker }
77f6108d72SJean-Philippe Brucker
irq__default_can_signal_msi(struct kvm * kvm)78f6108d72SJean-Philippe Brucker static bool irq__default_can_signal_msi(struct kvm *kvm)
79f6108d72SJean-Philippe Brucker {
80f6108d72SJean-Philippe Brucker return kvm__supports_extension(kvm, KVM_CAP_SIGNAL_MSI);
81f6108d72SJean-Philippe Brucker }
82f6108d72SJean-Philippe Brucker
irq__default_signal_msi(struct kvm * kvm,struct kvm_msi * msi)83f6108d72SJean-Philippe Brucker static int irq__default_signal_msi(struct kvm *kvm, struct kvm_msi *msi)
84f6108d72SJean-Philippe Brucker {
85f6108d72SJean-Philippe Brucker return ioctl(kvm->vm_fd, KVM_SIGNAL_MSI, msi);
86f6108d72SJean-Philippe Brucker }
87f6108d72SJean-Philippe Brucker
88f6108d72SJean-Philippe Brucker struct msi_routing_ops irq__default_routing_ops = {
89f6108d72SJean-Philippe Brucker .update_route = irq__update_msix_routes,
90f6108d72SJean-Philippe Brucker .signal_msi = irq__default_signal_msi,
91f6108d72SJean-Philippe Brucker .can_signal_msi = irq__default_can_signal_msi,
92f6108d72SJean-Philippe Brucker };
93f6108d72SJean-Philippe Brucker
irq__can_signal_msi(struct kvm * kvm)94f6108d72SJean-Philippe Brucker bool irq__can_signal_msi(struct kvm *kvm)
95f6108d72SJean-Philippe Brucker {
96f6108d72SJean-Philippe Brucker return msi_routing_ops->can_signal_msi(kvm);
97f6108d72SJean-Philippe Brucker }
98f6108d72SJean-Philippe Brucker
irq__signal_msi(struct kvm * kvm,struct kvm_msi * msi)99f6108d72SJean-Philippe Brucker int irq__signal_msi(struct kvm *kvm, struct kvm_msi *msi)
100f6108d72SJean-Philippe Brucker {
101f6108d72SJean-Philippe Brucker return msi_routing_ops->signal_msi(kvm, msi);
102f6108d72SJean-Philippe Brucker }
103f6108d72SJean-Philippe Brucker
irq__add_msix_route(struct kvm * kvm,struct msi_msg * msg,u32 device_id)104f9ef46f2SAndre Przywara int irq__add_msix_route(struct kvm *kvm, struct msi_msg *msg, u32 device_id)
1058ccc8549SAndre Przywara {
1068ccc8549SAndre Przywara int r;
107f6108d72SJean-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
116f6108d72SJean-Philippe Brucker entry = &irq_routing->entries[irq_routing->nr];
117f6108d72SJean-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) {
126f6108d72SJean-Philippe Brucker entry->flags = KVM_MSI_VALID_DEVID;
127f6108d72SJean-Philippe Brucker entry->u.msi.devid = device_id;
128f9ef46f2SAndre Przywara }
129f6108d72SJean-Philippe Brucker
130f9ef46f2SAndre Przywara irq_routing->nr++;
131f9ef46f2SAndre Przywara
132f6108d72SJean-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
update_data(u32 * ptr,u32 newdata)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
irq__update_msix_route(struct kvm * kvm,u32 gsi,struct msi_msg * msg)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
169f6108d72SJean-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
irq__common_add_irqfd(struct kvm * kvm,unsigned int gsi,int trigger_fd,int resample_fd)173*e59679d2SJean-Philippe Brucker int irq__common_add_irqfd(struct kvm *kvm, unsigned int gsi, int trigger_fd,
174*e59679d2SJean-Philippe Brucker int resample_fd)
175*e59679d2SJean-Philippe Brucker {
176*e59679d2SJean-Philippe Brucker struct kvm_irqfd irqfd = {
177*e59679d2SJean-Philippe Brucker .fd = trigger_fd,
178*e59679d2SJean-Philippe Brucker .gsi = gsi,
179*e59679d2SJean-Philippe Brucker .flags = resample_fd > 0 ? KVM_IRQFD_FLAG_RESAMPLE : 0,
180*e59679d2SJean-Philippe Brucker .resamplefd = resample_fd,
181*e59679d2SJean-Philippe Brucker };
182*e59679d2SJean-Philippe Brucker
183*e59679d2SJean-Philippe Brucker /* If we emulate MSI routing, translate the MSI to the corresponding IRQ */
184*e59679d2SJean-Philippe Brucker if (msi_routing_ops->translate_gsi)
185*e59679d2SJean-Philippe Brucker irqfd.gsi = msi_routing_ops->translate_gsi(kvm, gsi);
186*e59679d2SJean-Philippe Brucker
187*e59679d2SJean-Philippe Brucker return ioctl(kvm->vm_fd, KVM_IRQFD, &irqfd);
188*e59679d2SJean-Philippe Brucker }
189*e59679d2SJean-Philippe Brucker
irq__common_del_irqfd(struct kvm * kvm,unsigned int gsi,int trigger_fd)190*e59679d2SJean-Philippe Brucker void irq__common_del_irqfd(struct kvm *kvm, unsigned int gsi, int trigger_fd)
191*e59679d2SJean-Philippe Brucker {
192*e59679d2SJean-Philippe Brucker struct kvm_irqfd irqfd = {
193*e59679d2SJean-Philippe Brucker .fd = trigger_fd,
194*e59679d2SJean-Philippe Brucker .gsi = gsi,
195*e59679d2SJean-Philippe Brucker .flags = KVM_IRQFD_FLAG_DEASSIGN,
196*e59679d2SJean-Philippe Brucker };
197*e59679d2SJean-Philippe Brucker
198*e59679d2SJean-Philippe Brucker if (msi_routing_ops->translate_gsi)
199*e59679d2SJean-Philippe Brucker irqfd.gsi = msi_routing_ops->translate_gsi(kvm, gsi);
200*e59679d2SJean-Philippe Brucker
201*e59679d2SJean-Philippe Brucker ioctl(kvm->vm_fd, KVM_IRQFD, &irqfd);
202*e59679d2SJean-Philippe Brucker }
203*e59679d2SJean-Philippe Brucker
irq__exit(struct kvm * kvm)2048ccc8549SAndre Przywara int __attribute__((weak)) irq__exit(struct kvm *kvm)
2058ccc8549SAndre Przywara {
2068ccc8549SAndre Przywara free(irq_routing);
2078ccc8549SAndre Przywara return 0;
2088ccc8549SAndre Przywara }
2098ccc8549SAndre Przywara dev_base_exit(irq__exit);
210