xref: /kvmtool/irq.c (revision f9ef46f2f7816cc70cedf41a9c65c20e846886d8)
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 
69*f9ef46f2SAndre Przywara int irq__add_msix_route(struct kvm *kvm, struct msi_msg *msg, u32 device_id)
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 
80*f9ef46f2SAndre 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 
89*f9ef46f2SAndre Przywara 	if (kvm->msix_needs_devid) {
90*f9ef46f2SAndre Przywara 		irq_routing->entries[irq_routing->nr].flags = KVM_MSI_VALID_DEVID;
91*f9ef46f2SAndre Przywara 		irq_routing->entries[irq_routing->nr].u.msi.devid = device_id;
92*f9ef46f2SAndre Przywara 	}
93*f9ef46f2SAndre Przywara 	irq_routing->nr++;
94*f9ef46f2SAndre Przywara 
958ccc8549SAndre Przywara 	r = ioctl(kvm->vm_fd, KVM_SET_GSI_ROUTING, irq_routing);
968ccc8549SAndre Przywara 	if (r)
978ccc8549SAndre Przywara 		return r;
988ccc8549SAndre Przywara 
998ccc8549SAndre Przywara 	return next_gsi++;
1008ccc8549SAndre Przywara }
1018ccc8549SAndre Przywara 
1026518065aSAndre Przywara static bool update_data(u32 *ptr, u32 newdata)
1036518065aSAndre Przywara {
1046518065aSAndre Przywara 	if (*ptr == newdata)
1056518065aSAndre Przywara 		return false;
1066518065aSAndre Przywara 
1076518065aSAndre Przywara 	*ptr = newdata;
1086518065aSAndre Przywara 	return true;
1096518065aSAndre Przywara }
1106518065aSAndre Przywara 
1116518065aSAndre Przywara void irq__update_msix_route(struct kvm *kvm, u32 gsi, struct msi_msg *msg)
1126518065aSAndre Przywara {
1136518065aSAndre Przywara 	struct kvm_irq_routing_msi *entry;
1146518065aSAndre Przywara 	unsigned int i;
1156518065aSAndre Przywara 	bool changed;
1166518065aSAndre Przywara 
1176518065aSAndre Przywara 	for (i = 0; i < irq_routing->nr; i++)
1186518065aSAndre Przywara 		if (gsi == irq_routing->entries[i].gsi)
1196518065aSAndre Przywara 			break;
1206518065aSAndre Przywara 	if (i == irq_routing->nr)
1216518065aSAndre Przywara 		return;
1226518065aSAndre Przywara 
1236518065aSAndre Przywara 	entry = &irq_routing->entries[i].u.msi;
1246518065aSAndre Przywara 
1256518065aSAndre Przywara 	changed  = update_data(&entry->address_hi, msg->address_hi);
1266518065aSAndre Przywara 	changed |= update_data(&entry->address_lo, msg->address_lo);
1276518065aSAndre Przywara 	changed |= update_data(&entry->data, msg->data);
1286518065aSAndre Przywara 
1296518065aSAndre Przywara 	if (!changed)
1306518065aSAndre Przywara 		return;
1316518065aSAndre Przywara 
1326518065aSAndre Przywara 	if (ioctl(kvm->vm_fd, KVM_SET_GSI_ROUTING, irq_routing) == -1)
1336518065aSAndre Przywara 		die_perror("KVM_SET_GSI_ROUTING");
1346518065aSAndre Przywara }
1356518065aSAndre Przywara 
1368ccc8549SAndre Przywara int __attribute__((weak)) irq__exit(struct kvm *kvm)
1378ccc8549SAndre Przywara {
1388ccc8549SAndre Przywara 	free(irq_routing);
1398ccc8549SAndre Przywara 	return 0;
1408ccc8549SAndre Przywara }
1418ccc8549SAndre Przywara dev_base_exit(irq__exit);
142