xref: /kvmtool/x86/irq.c (revision 5f0a22b7f8012b2b51b0aea2874d94f6f8355f8a)
1c383fe9cSSasha Levin #include "kvm/irq.h"
2*5f0a22b7SSasha Levin #include "kvm/kvm.h"
3*5f0a22b7SSasha 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>
8*5f0a22b7SSasha Levin #include <linux/kvm.h>
9*5f0a22b7SSasha Levin #include <sys/ioctl.h>
10c383fe9cSSasha Levin 
11c383fe9cSSasha Levin #include <stddef.h>
12c383fe9cSSasha Levin #include <stdlib.h>
13c383fe9cSSasha Levin 
14*5f0a22b7SSasha Levin #define IRQ_MAX_GSI			64
15*5f0a22b7SSasha Levin #define IRQCHIP_MASTER			0
16*5f0a22b7SSasha Levin #define IRQCHIP_SLAVE			1
17*5f0a22b7SSasha Levin #define IRQCHIP_IOAPIC			2
18*5f0a22b7SSasha Levin 
19c383fe9cSSasha Levin static u8		next_line	= 3;
20c383fe9cSSasha Levin static u8		next_dev	= 1;
21c383fe9cSSasha Levin static struct rb_root	pci_tree	= RB_ROOT;
22c383fe9cSSasha Levin 
23*5f0a22b7SSasha Levin /* First 24 GSIs are routed between IRQCHIPs and IOAPICs */
24*5f0a22b7SSasha Levin static u32 gsi = 24;
25*5f0a22b7SSasha Levin 
26*5f0a22b7SSasha Levin struct kvm_irq_routing *irq_routing;
27*5f0a22b7SSasha Levin 
28*5f0a22b7SSasha Levin static int irq__add_routing(u32 gsi, u32 type, u32 irqchip, u32 pin)
29*5f0a22b7SSasha Levin {
30*5f0a22b7SSasha Levin 	if (gsi >= IRQ_MAX_GSI)
31*5f0a22b7SSasha Levin 		return -ENOSPC;
32*5f0a22b7SSasha Levin 
33*5f0a22b7SSasha Levin 	irq_routing->entries[irq_routing->nr++] =
34*5f0a22b7SSasha Levin 		(struct kvm_irq_routing_entry) {
35*5f0a22b7SSasha Levin 			.gsi = gsi,
36*5f0a22b7SSasha Levin 			.type = type,
37*5f0a22b7SSasha Levin 			.u.irqchip.irqchip = irqchip,
38*5f0a22b7SSasha Levin 			.u.irqchip.pin = pin,
39*5f0a22b7SSasha Levin 		};
40*5f0a22b7SSasha Levin 
41*5f0a22b7SSasha Levin 	return 0;
42*5f0a22b7SSasha Levin }
43*5f0a22b7SSasha 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
79c383fe9cSSasha Levin 			return 0;
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 
86c383fe9cSSasha Levin 	return 1;
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;
92c383fe9cSSasha Levin 
93c383fe9cSSasha Levin 	node = search(&pci_tree, dev);
94c383fe9cSSasha Levin 
95c383fe9cSSasha Levin 	if (!node) {
96c383fe9cSSasha Levin 		/* We haven't found a node - First device of it's kind */
97c383fe9cSSasha Levin 		node = malloc(sizeof(*node));
98c383fe9cSSasha Levin 		if (node == NULL)
99c383fe9cSSasha Levin 			return -1;
100c383fe9cSSasha Levin 
101c383fe9cSSasha Levin 		*node = (struct pci_dev) {
102c383fe9cSSasha Levin 			.id	= dev,
10333348d03SCyrill Gorcunov 			/*
10433348d03SCyrill Gorcunov 			 * PCI supports only INTA#,B#,C#,D# per device.
10533348d03SCyrill Gorcunov 			 * A#,B#,C#,D# are allowed for multifunctional
10633348d03SCyrill Gorcunov 			 * devices so stick with A# for our single
10733348d03SCyrill Gorcunov 			 * function devices.
10833348d03SCyrill Gorcunov 			 */
10933348d03SCyrill Gorcunov 			.pin	= 1,
110c383fe9cSSasha Levin 		};
111c383fe9cSSasha Levin 
112c383fe9cSSasha Levin 		INIT_LIST_HEAD(&node->lines);
113c383fe9cSSasha Levin 
114c383fe9cSSasha Levin 		if (insert(&pci_tree, node) != 1) {
115c383fe9cSSasha Levin 			free(node);
116c383fe9cSSasha Levin 			return -1;
117c383fe9cSSasha Levin 		}
118c383fe9cSSasha Levin 	}
119c383fe9cSSasha Levin 
120c383fe9cSSasha Levin 	if (node) {
121c383fe9cSSasha Levin 		/* This device already has a pin assigned, give out a new line and device id */
122c383fe9cSSasha Levin 		struct irq_line *new = malloc(sizeof(*new));
123c383fe9cSSasha Levin 		if (new == NULL)
124c383fe9cSSasha Levin 			return -1;
125c383fe9cSSasha Levin 
126c383fe9cSSasha Levin 		new->line	= next_line++;
127c383fe9cSSasha Levin 		*line		= new->line;
128c383fe9cSSasha Levin 		*pin		= node->pin;
129c383fe9cSSasha Levin 		*num		= next_dev++;
130c383fe9cSSasha Levin 
131c383fe9cSSasha Levin 		list_add(&new->node, &node->lines);
132c383fe9cSSasha Levin 
133c383fe9cSSasha Levin 		return 0;
134c383fe9cSSasha Levin 	}
135c383fe9cSSasha Levin 
136c383fe9cSSasha Levin 	return -1;
137c383fe9cSSasha Levin }
138c383fe9cSSasha Levin 
139*5f0a22b7SSasha Levin void irq__init(struct kvm *kvm)
140*5f0a22b7SSasha Levin {
141*5f0a22b7SSasha Levin 	int i, r;
142*5f0a22b7SSasha Levin 
143*5f0a22b7SSasha Levin 	irq_routing = malloc(sizeof(struct kvm_irq_routing) +
144*5f0a22b7SSasha Levin 			IRQ_MAX_GSI * sizeof(struct kvm_irq_routing_entry));
145*5f0a22b7SSasha Levin 	if (irq_routing == NULL)
146*5f0a22b7SSasha Levin 		die("Failed allocating space for GSI table");
147*5f0a22b7SSasha Levin 
148*5f0a22b7SSasha Levin 	/* Hook first 8 GSIs to master IRQCHIP */
149*5f0a22b7SSasha Levin 	for (i = 0; i < 8; i++)
150*5f0a22b7SSasha Levin 		irq__add_routing(i, KVM_IRQ_ROUTING_IRQCHIP, IRQCHIP_MASTER, i);
151*5f0a22b7SSasha Levin 
152*5f0a22b7SSasha Levin 	/* Hook next 8 GSIs to slave IRQCHIP */
153*5f0a22b7SSasha Levin 	for (i = 8; i < 16; i++)
154*5f0a22b7SSasha Levin 		irq__add_routing(i, KVM_IRQ_ROUTING_IRQCHIP, IRQCHIP_SLAVE, 8-i);
155*5f0a22b7SSasha Levin 
156*5f0a22b7SSasha Levin 	/* Last but not least, IOAPIC */
157*5f0a22b7SSasha Levin 	for (i = 0; i < 24; i++) {
158*5f0a22b7SSasha Levin 		if (i == 0)
159*5f0a22b7SSasha Levin 			irq__add_routing(i, KVM_IRQ_ROUTING_IRQCHIP, IRQCHIP_IOAPIC, 2);
160*5f0a22b7SSasha Levin 		else
161*5f0a22b7SSasha Levin 			irq__add_routing(i, KVM_IRQ_ROUTING_IRQCHIP, IRQCHIP_IOAPIC, i);
162*5f0a22b7SSasha Levin 	}
163*5f0a22b7SSasha Levin 
164*5f0a22b7SSasha Levin 	r = ioctl(kvm->vm_fd, KVM_SET_GSI_ROUTING, irq_routing);
165*5f0a22b7SSasha Levin 	if (r)
166*5f0a22b7SSasha Levin 		die("Failed setting GSI routes");
167*5f0a22b7SSasha Levin }
168*5f0a22b7SSasha Levin 
169*5f0a22b7SSasha Levin int irq__add_msix_route(struct kvm *kvm, u32 low, u32 high, u32 data)
170*5f0a22b7SSasha Levin {
171*5f0a22b7SSasha Levin 	int r;
172*5f0a22b7SSasha Levin 
173*5f0a22b7SSasha Levin 	irq_routing->entries[irq_routing->nr++] =
174*5f0a22b7SSasha Levin 		(struct kvm_irq_routing_entry) {
175*5f0a22b7SSasha Levin 			.gsi = gsi,
176*5f0a22b7SSasha Levin 			.type = KVM_IRQ_ROUTING_MSI,
177*5f0a22b7SSasha Levin 			.u.msi.address_lo = low,
178*5f0a22b7SSasha Levin 			.u.msi.address_hi = high,
179*5f0a22b7SSasha Levin 			.u.msi.data = data,
180*5f0a22b7SSasha Levin 		};
181*5f0a22b7SSasha Levin 
182*5f0a22b7SSasha Levin 	r = ioctl(kvm->vm_fd, KVM_SET_GSI_ROUTING, irq_routing);
183*5f0a22b7SSasha Levin 	if (r)
184*5f0a22b7SSasha Levin 		return r;
185*5f0a22b7SSasha Levin 
186*5f0a22b7SSasha Levin 	return gsi++;
187*5f0a22b7SSasha Levin }
188*5f0a22b7SSasha Levin 
189c383fe9cSSasha Levin struct rb_node *irq__get_pci_tree(void)
190c383fe9cSSasha Levin {
191c383fe9cSSasha Levin 	return rb_first(&pci_tree);
192c383fe9cSSasha Levin }
193