xref: /kvmtool/irq.c (revision f6108d72e977cce00e7bc824acd1d73da8cc9729)
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