xref: /kvmtool/x86/irq.c (revision e3c4f8aa776211ff22c6f6af2aabb92c52b6725e)
1c383fe9cSSasha Levin #include "kvm/irq.h"
25f0a22b7SSasha Levin #include "kvm/kvm.h"
35f0a22b7SSasha Levin #include "kvm/util.h"
4c383fe9cSSasha Levin 
5c383fe9cSSasha Levin #include <linux/types.h>
6c383fe9cSSasha Levin #include <linux/rbtree.h>
7c383fe9cSSasha Levin #include <linux/list.h>
85f0a22b7SSasha Levin #include <linux/kvm.h>
95f0a22b7SSasha Levin #include <sys/ioctl.h>
10c383fe9cSSasha Levin 
11c383fe9cSSasha Levin #include <stddef.h>
12c383fe9cSSasha Levin #include <stdlib.h>
13c383fe9cSSasha Levin 
145f0a22b7SSasha Levin #define IRQ_MAX_GSI			64
155f0a22b7SSasha Levin #define IRQCHIP_MASTER			0
165f0a22b7SSasha Levin #define IRQCHIP_SLAVE			1
175f0a22b7SSasha Levin #define IRQCHIP_IOAPIC			2
185f0a22b7SSasha Levin 
1999de8863SSasha Levin static u8		next_line	= 5;
20c383fe9cSSasha Levin static u8		next_dev	= 1;
21c383fe9cSSasha Levin static struct rb_root	pci_tree	= RB_ROOT;
22c383fe9cSSasha Levin 
235f0a22b7SSasha Levin /* First 24 GSIs are routed between IRQCHIPs and IOAPICs */
245f0a22b7SSasha Levin static u32 gsi = 24;
255f0a22b7SSasha Levin 
265f0a22b7SSasha Levin struct kvm_irq_routing *irq_routing;
275f0a22b7SSasha Levin 
285f0a22b7SSasha Levin static int irq__add_routing(u32 gsi, u32 type, u32 irqchip, u32 pin)
295f0a22b7SSasha Levin {
305f0a22b7SSasha Levin 	if (gsi >= IRQ_MAX_GSI)
315f0a22b7SSasha Levin 		return -ENOSPC;
325f0a22b7SSasha Levin 
335f0a22b7SSasha Levin 	irq_routing->entries[irq_routing->nr++] =
345f0a22b7SSasha Levin 		(struct kvm_irq_routing_entry) {
355f0a22b7SSasha Levin 			.gsi = gsi,
365f0a22b7SSasha Levin 			.type = type,
375f0a22b7SSasha Levin 			.u.irqchip.irqchip = irqchip,
385f0a22b7SSasha Levin 			.u.irqchip.pin = pin,
395f0a22b7SSasha Levin 		};
405f0a22b7SSasha Levin 
415f0a22b7SSasha Levin 	return 0;
425f0a22b7SSasha Levin }
435f0a22b7SSasha Levin 
44c383fe9cSSasha Levin static struct pci_dev *search(struct rb_root *root, u32 id)
45c383fe9cSSasha Levin {
46c383fe9cSSasha Levin 	struct rb_node *node = root->rb_node;
47c383fe9cSSasha Levin 
48c383fe9cSSasha Levin 	while (node) {
49c383fe9cSSasha Levin 		struct pci_dev *data = container_of(node, struct pci_dev, node);
50c383fe9cSSasha Levin 		int result;
51c383fe9cSSasha Levin 
52c383fe9cSSasha Levin 		result = id - data->id;
53c383fe9cSSasha Levin 
54c383fe9cSSasha Levin 		if (result < 0)
55c383fe9cSSasha Levin 			node = node->rb_left;
56c383fe9cSSasha Levin 		else if (result > 0)
57c383fe9cSSasha Levin 			node = node->rb_right;
58c383fe9cSSasha Levin 		else
59c383fe9cSSasha Levin 			return data;
60c383fe9cSSasha Levin 	}
61c383fe9cSSasha Levin 	return NULL;
62c383fe9cSSasha Levin }
63c383fe9cSSasha Levin 
64c383fe9cSSasha Levin static int insert(struct rb_root *root, struct pci_dev *data)
65c383fe9cSSasha Levin {
66c383fe9cSSasha Levin 	struct rb_node **new = &(root->rb_node), *parent = NULL;
67c383fe9cSSasha Levin 
68c383fe9cSSasha Levin 	/* Figure out where to put new node */
69c383fe9cSSasha Levin 	while (*new) {
70c383fe9cSSasha Levin 		struct pci_dev *this	= container_of(*new, struct pci_dev, node);
71c383fe9cSSasha Levin 		int result		= data->id - this->id;
72c383fe9cSSasha Levin 
73c383fe9cSSasha Levin 		parent = *new;
74c383fe9cSSasha Levin 		if (result < 0)
75c383fe9cSSasha Levin 			new = &((*new)->rb_left);
76c383fe9cSSasha Levin 		else if (result > 0)
77c383fe9cSSasha Levin 			new = &((*new)->rb_right);
78c383fe9cSSasha Levin 		else
79*e3c4f8aaSSasha Levin 			return -EEXIST;
80c383fe9cSSasha Levin 	}
81c383fe9cSSasha Levin 
82c383fe9cSSasha Levin 	/* Add new node and rebalance tree. */
83c383fe9cSSasha Levin 	rb_link_node(&data->node, parent, new);
84c383fe9cSSasha Levin 	rb_insert_color(&data->node, root);
85c383fe9cSSasha Levin 
86*e3c4f8aaSSasha Levin 	return 0;
87c383fe9cSSasha Levin }
88c383fe9cSSasha Levin 
89c383fe9cSSasha Levin int irq__register_device(u32 dev, u8 *num, u8 *pin, u8 *line)
90c383fe9cSSasha Levin {
91c383fe9cSSasha Levin 	struct pci_dev *node;
92*e3c4f8aaSSasha Levin 	int r;
93c383fe9cSSasha Levin 
94c383fe9cSSasha Levin 	node = search(&pci_tree, dev);
95c383fe9cSSasha Levin 
96c383fe9cSSasha Levin 	if (!node) {
97c383fe9cSSasha Levin 		/* We haven't found a node - First device of it's kind */
98c383fe9cSSasha Levin 		node = malloc(sizeof(*node));
99c383fe9cSSasha Levin 		if (node == NULL)
100*e3c4f8aaSSasha Levin 			return -ENOMEM;
101c383fe9cSSasha Levin 
102c383fe9cSSasha Levin 		*node = (struct pci_dev) {
103c383fe9cSSasha Levin 			.id	= dev,
10433348d03SCyrill Gorcunov 			/*
10533348d03SCyrill Gorcunov 			 * PCI supports only INTA#,B#,C#,D# per device.
10633348d03SCyrill Gorcunov 			 * A#,B#,C#,D# are allowed for multifunctional
10733348d03SCyrill Gorcunov 			 * devices so stick with A# for our single
10833348d03SCyrill Gorcunov 			 * function devices.
10933348d03SCyrill Gorcunov 			 */
11033348d03SCyrill Gorcunov 			.pin	= 1,
111c383fe9cSSasha Levin 		};
112c383fe9cSSasha Levin 
113c383fe9cSSasha Levin 		INIT_LIST_HEAD(&node->lines);
114c383fe9cSSasha Levin 
115*e3c4f8aaSSasha Levin 		r = insert(&pci_tree, node);
116*e3c4f8aaSSasha Levin 		if (r) {
117c383fe9cSSasha Levin 			free(node);
118*e3c4f8aaSSasha Levin 			return r;
119c383fe9cSSasha Levin 		}
120c383fe9cSSasha Levin 	}
121c383fe9cSSasha Levin 
122c383fe9cSSasha Levin 	if (node) {
123c383fe9cSSasha Levin 		/* This device already has a pin assigned, give out a new line and device id */
124c383fe9cSSasha Levin 		struct irq_line *new = malloc(sizeof(*new));
125c383fe9cSSasha Levin 		if (new == NULL)
126*e3c4f8aaSSasha Levin 			return -ENOMEM;
127c383fe9cSSasha Levin 
128c383fe9cSSasha Levin 		new->line	= next_line++;
129c383fe9cSSasha Levin 		*line		= new->line;
130c383fe9cSSasha Levin 		*pin		= node->pin;
131c383fe9cSSasha Levin 		*num		= next_dev++;
132c383fe9cSSasha Levin 
133c383fe9cSSasha Levin 		list_add(&new->node, &node->lines);
134c383fe9cSSasha Levin 
135c383fe9cSSasha Levin 		return 0;
136c383fe9cSSasha Levin 	}
137c383fe9cSSasha Levin 
138*e3c4f8aaSSasha Levin 	return -EFAULT;
139c383fe9cSSasha Levin }
140c383fe9cSSasha Levin 
141*e3c4f8aaSSasha Levin int irq__init(struct kvm *kvm)
1425f0a22b7SSasha Levin {
1435f0a22b7SSasha Levin 	int i, r;
1445f0a22b7SSasha Levin 
145beb8f24bSSasha Levin 	irq_routing = calloc(sizeof(struct kvm_irq_routing) +
146beb8f24bSSasha Levin 			IRQ_MAX_GSI * sizeof(struct kvm_irq_routing_entry), 1);
1475f0a22b7SSasha Levin 	if (irq_routing == NULL)
148*e3c4f8aaSSasha Levin 		return -ENOMEM;
1495f0a22b7SSasha Levin 
1505f0a22b7SSasha Levin 	/* Hook first 8 GSIs to master IRQCHIP */
1515f0a22b7SSasha Levin 	for (i = 0; i < 8; i++)
1522eb7ffa0SSasha Levin 		if (i != 2)
1535f0a22b7SSasha Levin 			irq__add_routing(i, KVM_IRQ_ROUTING_IRQCHIP, IRQCHIP_MASTER, i);
1545f0a22b7SSasha Levin 
1555f0a22b7SSasha Levin 	/* Hook next 8 GSIs to slave IRQCHIP */
1565f0a22b7SSasha Levin 	for (i = 8; i < 16; i++)
1572eb7ffa0SSasha Levin 		irq__add_routing(i, KVM_IRQ_ROUTING_IRQCHIP, IRQCHIP_SLAVE, i - 8);
1585f0a22b7SSasha Levin 
1595f0a22b7SSasha Levin 	/* Last but not least, IOAPIC */
1605f0a22b7SSasha Levin 	for (i = 0; i < 24; i++) {
1615f0a22b7SSasha Levin 		if (i == 0)
1625f0a22b7SSasha Levin 			irq__add_routing(i, KVM_IRQ_ROUTING_IRQCHIP, IRQCHIP_IOAPIC, 2);
1632eb7ffa0SSasha Levin 		else if (i != 2)
1645f0a22b7SSasha Levin 			irq__add_routing(i, KVM_IRQ_ROUTING_IRQCHIP, IRQCHIP_IOAPIC, i);
1655f0a22b7SSasha Levin 	}
1665f0a22b7SSasha Levin 
1675f0a22b7SSasha Levin 	r = ioctl(kvm->vm_fd, KVM_SET_GSI_ROUTING, irq_routing);
168*e3c4f8aaSSasha Levin 	if (r) {
169*e3c4f8aaSSasha Levin 		free(irq_routing);
170*e3c4f8aaSSasha Levin 		return errno;
171*e3c4f8aaSSasha Levin 	}
172*e3c4f8aaSSasha Levin 
173*e3c4f8aaSSasha Levin 	return 0;
174*e3c4f8aaSSasha Levin }
175*e3c4f8aaSSasha Levin 
176*e3c4f8aaSSasha Levin int irq__exit(struct kvm *kvm)
177*e3c4f8aaSSasha Levin {
178*e3c4f8aaSSasha Levin 	free(irq_routing);
179*e3c4f8aaSSasha Levin 
180*e3c4f8aaSSasha Levin 	return 0;
1815f0a22b7SSasha Levin }
1825f0a22b7SSasha Levin 
1831de74957SSasha Levin int irq__add_msix_route(struct kvm *kvm, struct msi_msg *msg)
1845f0a22b7SSasha Levin {
1855f0a22b7SSasha Levin 	int r;
1865f0a22b7SSasha Levin 
1875f0a22b7SSasha Levin 	irq_routing->entries[irq_routing->nr++] =
1885f0a22b7SSasha Levin 		(struct kvm_irq_routing_entry) {
1895f0a22b7SSasha Levin 			.gsi = gsi,
1905f0a22b7SSasha Levin 			.type = KVM_IRQ_ROUTING_MSI,
1911de74957SSasha Levin 			.u.msi.address_hi = msg->address_hi,
1921de74957SSasha Levin 			.u.msi.address_lo = msg->address_lo,
1931de74957SSasha Levin 			.u.msi.data = msg->data,
1945f0a22b7SSasha Levin 		};
1955f0a22b7SSasha Levin 
1965f0a22b7SSasha Levin 	r = ioctl(kvm->vm_fd, KVM_SET_GSI_ROUTING, irq_routing);
1975f0a22b7SSasha Levin 	if (r)
1985f0a22b7SSasha Levin 		return r;
1995f0a22b7SSasha Levin 
2005f0a22b7SSasha Levin 	return gsi++;
2015f0a22b7SSasha Levin }
2025f0a22b7SSasha Levin 
203c383fe9cSSasha Levin struct rb_node *irq__get_pci_tree(void)
204c383fe9cSSasha Levin {
205c383fe9cSSasha Levin 	return rb_first(&pci_tree);
206c383fe9cSSasha Levin }
207