xref: /kvmtool/x86/irq.c (revision 1de749578efa0b26d92c3feee089c3b89cfe5961)
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 
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 
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
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 
1395f0a22b7SSasha Levin void irq__init(struct kvm *kvm)
1405f0a22b7SSasha Levin {
1415f0a22b7SSasha Levin 	int i, r;
1425f0a22b7SSasha Levin 
1435f0a22b7SSasha Levin 	irq_routing = malloc(sizeof(struct kvm_irq_routing) +
1445f0a22b7SSasha Levin 			IRQ_MAX_GSI * sizeof(struct kvm_irq_routing_entry));
1455f0a22b7SSasha Levin 	if (irq_routing == NULL)
1465f0a22b7SSasha Levin 		die("Failed allocating space for GSI table");
1475f0a22b7SSasha Levin 
1485f0a22b7SSasha Levin 	/* Hook first 8 GSIs to master IRQCHIP */
1495f0a22b7SSasha Levin 	for (i = 0; i < 8; i++)
1502eb7ffa0SSasha Levin 		if (i != 2)
1515f0a22b7SSasha Levin 			irq__add_routing(i, KVM_IRQ_ROUTING_IRQCHIP, IRQCHIP_MASTER, i);
1525f0a22b7SSasha Levin 
1535f0a22b7SSasha Levin 	/* Hook next 8 GSIs to slave IRQCHIP */
1545f0a22b7SSasha Levin 	for (i = 8; i < 16; i++)
1552eb7ffa0SSasha Levin 		irq__add_routing(i, KVM_IRQ_ROUTING_IRQCHIP, IRQCHIP_SLAVE, i - 8);
1565f0a22b7SSasha Levin 
1575f0a22b7SSasha Levin 	/* Last but not least, IOAPIC */
1585f0a22b7SSasha Levin 	for (i = 0; i < 24; i++) {
1595f0a22b7SSasha Levin 		if (i == 0)
1605f0a22b7SSasha Levin 			irq__add_routing(i, KVM_IRQ_ROUTING_IRQCHIP, IRQCHIP_IOAPIC, 2);
1612eb7ffa0SSasha Levin 		else if (i != 2)
1625f0a22b7SSasha Levin 			irq__add_routing(i, KVM_IRQ_ROUTING_IRQCHIP, IRQCHIP_IOAPIC, i);
1635f0a22b7SSasha Levin 	}
1645f0a22b7SSasha Levin 
1655f0a22b7SSasha Levin 	r = ioctl(kvm->vm_fd, KVM_SET_GSI_ROUTING, irq_routing);
1665f0a22b7SSasha Levin 	if (r)
1675f0a22b7SSasha Levin 		die("Failed setting GSI routes");
1685f0a22b7SSasha Levin }
1695f0a22b7SSasha Levin 
170*1de74957SSasha Levin int irq__add_msix_route(struct kvm *kvm, struct msi_msg *msg)
1715f0a22b7SSasha Levin {
1725f0a22b7SSasha Levin 	int r;
1735f0a22b7SSasha Levin 
1745f0a22b7SSasha Levin 	irq_routing->entries[irq_routing->nr++] =
1755f0a22b7SSasha Levin 		(struct kvm_irq_routing_entry) {
1765f0a22b7SSasha Levin 			.gsi = gsi,
1775f0a22b7SSasha Levin 			.type = KVM_IRQ_ROUTING_MSI,
178*1de74957SSasha Levin 			.u.msi.address_hi = msg->address_hi,
179*1de74957SSasha Levin 			.u.msi.address_lo = msg->address_lo,
180*1de74957SSasha Levin 			.u.msi.data = msg->data,
1815f0a22b7SSasha Levin 		};
1825f0a22b7SSasha Levin 
1835f0a22b7SSasha Levin 	r = ioctl(kvm->vm_fd, KVM_SET_GSI_ROUTING, irq_routing);
1845f0a22b7SSasha Levin 	if (r)
1855f0a22b7SSasha Levin 		return r;
1865f0a22b7SSasha Levin 
1875f0a22b7SSasha Levin 	return gsi++;
1885f0a22b7SSasha Levin }
1895f0a22b7SSasha Levin 
190c383fe9cSSasha Levin struct rb_node *irq__get_pci_tree(void)
191c383fe9cSSasha Levin {
192c383fe9cSSasha Levin 	return rb_first(&pci_tree);
193c383fe9cSSasha Levin }
194