| #include "kvm/irq.h" |
| #include "kvm/kvm.h" |
| #include "kvm/util.h" |
| |
| #include <linux/types.h> |
| #include <linux/rbtree.h> |
| #include <linux/list.h> |
| #include <linux/kvm.h> |
| #include <sys/ioctl.h> |
| |
| #include <stddef.h> |
| #include <stdlib.h> |
| |
| #define IRQ_MAX_GSI 64 |
| #define IRQCHIP_MASTER 0 |
| #define IRQCHIP_SLAVE 1 |
| #define IRQCHIP_IOAPIC 2 |
| |
| static u8 next_line = 5; |
| static u8 next_dev = 1; |
| static struct rb_root pci_tree = RB_ROOT; |
| |
| /* First 24 GSIs are routed between IRQCHIPs and IOAPICs */ |
| static u32 gsi = 24; |
| |
| struct kvm_irq_routing *irq_routing; |
| |
| static int irq__add_routing(u32 gsi, u32 type, u32 irqchip, u32 pin) |
| { |
| if (gsi >= IRQ_MAX_GSI) |
| return -ENOSPC; |
| |
| irq_routing->entries[irq_routing->nr++] = |
| (struct kvm_irq_routing_entry) { |
| .gsi = gsi, |
| .type = type, |
| .u.irqchip.irqchip = irqchip, |
| .u.irqchip.pin = pin, |
| }; |
| |
| return 0; |
| } |
| |
| static struct pci_dev *search(struct rb_root *root, u32 id) |
| { |
| struct rb_node *node = root->rb_node; |
| |
| while (node) { |
| struct pci_dev *data = rb_entry(node, struct pci_dev, node); |
| int result; |
| |
| result = id - data->id; |
| |
| if (result < 0) |
| node = node->rb_left; |
| else if (result > 0) |
| node = node->rb_right; |
| else |
| return data; |
| } |
| return NULL; |
| } |
| |
| static int insert(struct rb_root *root, struct pci_dev *data) |
| { |
| struct rb_node **new = &(root->rb_node), *parent = NULL; |
| |
| /* Figure out where to put new node */ |
| while (*new) { |
| struct pci_dev *this = container_of(*new, struct pci_dev, node); |
| int result = data->id - this->id; |
| |
| parent = *new; |
| if (result < 0) |
| new = &((*new)->rb_left); |
| else if (result > 0) |
| new = &((*new)->rb_right); |
| else |
| return -EEXIST; |
| } |
| |
| /* Add new node and rebalance tree. */ |
| rb_link_node(&data->node, parent, new); |
| rb_insert_color(&data->node, root); |
| |
| return 0; |
| } |
| |
| int irq__register_device(u32 dev, u8 *num, u8 *pin, u8 *line) |
| { |
| struct pci_dev *node; |
| int r; |
| |
| node = search(&pci_tree, dev); |
| |
| if (!node) { |
| /* We haven't found a node - First device of it's kind */ |
| node = malloc(sizeof(*node)); |
| if (node == NULL) |
| return -ENOMEM; |
| |
| *node = (struct pci_dev) { |
| .id = dev, |
| /* |
| * PCI supports only INTA#,B#,C#,D# per device. |
| * A#,B#,C#,D# are allowed for multifunctional |
| * devices so stick with A# for our single |
| * function devices. |
| */ |
| .pin = 1, |
| }; |
| |
| INIT_LIST_HEAD(&node->lines); |
| |
| r = insert(&pci_tree, node); |
| if (r) { |
| free(node); |
| return r; |
| } |
| } |
| |
| if (node) { |
| /* This device already has a pin assigned, give out a new line and device id */ |
| struct irq_line *new = malloc(sizeof(*new)); |
| if (new == NULL) |
| return -ENOMEM; |
| |
| new->line = next_line++; |
| *line = new->line; |
| *pin = node->pin; |
| *num = next_dev++; |
| |
| list_add(&new->node, &node->lines); |
| |
| return 0; |
| } |
| |
| return -EFAULT; |
| } |
| |
| int irq__init(struct kvm *kvm) |
| { |
| int i, r; |
| |
| irq_routing = calloc(sizeof(struct kvm_irq_routing) + |
| IRQ_MAX_GSI * sizeof(struct kvm_irq_routing_entry), 1); |
| if (irq_routing == NULL) |
| return -ENOMEM; |
| |
| /* Hook first 8 GSIs to master IRQCHIP */ |
| for (i = 0; i < 8; i++) |
| if (i != 2) |
| irq__add_routing(i, KVM_IRQ_ROUTING_IRQCHIP, IRQCHIP_MASTER, i); |
| |
| /* Hook next 8 GSIs to slave IRQCHIP */ |
| for (i = 8; i < 16; i++) |
| irq__add_routing(i, KVM_IRQ_ROUTING_IRQCHIP, IRQCHIP_SLAVE, i - 8); |
| |
| /* Last but not least, IOAPIC */ |
| for (i = 0; i < 24; i++) { |
| if (i == 0) |
| irq__add_routing(i, KVM_IRQ_ROUTING_IRQCHIP, IRQCHIP_IOAPIC, 2); |
| else if (i != 2) |
| irq__add_routing(i, KVM_IRQ_ROUTING_IRQCHIP, IRQCHIP_IOAPIC, i); |
| } |
| |
| r = ioctl(kvm->vm_fd, KVM_SET_GSI_ROUTING, irq_routing); |
| if (r) { |
| free(irq_routing); |
| return errno; |
| } |
| |
| return 0; |
| } |
| |
| int irq__exit(struct kvm *kvm) |
| { |
| struct rb_node *ent; |
| |
| free(irq_routing); |
| |
| while ((ent = rb_first(&pci_tree))) { |
| struct pci_dev *dev; |
| struct irq_line *line; |
| |
| dev = rb_entry(ent, struct pci_dev, node); |
| while (!list_empty(&dev->lines)) { |
| line = list_first_entry(&dev->lines, struct irq_line, node); |
| list_del(&line->node); |
| free(line); |
| } |
| rb_erase(&dev->node, &pci_tree); |
| free(dev); |
| } |
| |
| return 0; |
| } |
| |
| int irq__add_msix_route(struct kvm *kvm, struct msi_msg *msg) |
| { |
| int r; |
| |
| irq_routing->entries[irq_routing->nr++] = |
| (struct kvm_irq_routing_entry) { |
| .gsi = gsi, |
| .type = KVM_IRQ_ROUTING_MSI, |
| .u.msi.address_hi = msg->address_hi, |
| .u.msi.address_lo = msg->address_lo, |
| .u.msi.data = msg->data, |
| }; |
| |
| r = ioctl(kvm->vm_fd, KVM_SET_GSI_ROUTING, irq_routing); |
| if (r) |
| return r; |
| |
| return gsi++; |
| } |
| |
| struct rb_node *irq__get_pci_tree(void) |
| { |
| return rb_first(&pci_tree); |
| } |