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