diff --git a/Makefile b/Makefile index de11060..3f25a14 100644 --- a/Makefile +++ b/Makefile @@ -50,6 +50,7 @@ OBJS += builtin-run.o OBJS += builtin-setup.o OBJS += builtin-stop.o OBJS += builtin-version.o +OBJS += devices.o OBJS += disk/core.o OBJS += framebuffer.o OBJS += guest_compat.o diff --git a/devices.c b/devices.c new file mode 100644 index 0000000..9f1941d --- /dev/null +++ b/devices.c @@ -0,0 +1,86 @@ +#include "kvm/devices.h" +#include "kvm/kvm.h" + +#include +#include + +struct device_bus { + struct rb_root root; + int dev_num; +}; + +static struct device_bus device_trees[DEVICE_BUS_MAX] = { + [0 ... (DEVICE_BUS_MAX - 1)] = { RB_ROOT, 0 }, +}; + +int device__register(struct device_header *dev) +{ + struct device_bus *bus; + struct rb_node **node, *parent = NULL; + + if (dev->bus_type >= DEVICE_BUS_MAX) { + pr_warning("Ignoring device registration on unknown bus %d\n", + dev->bus_type); + return -EINVAL; + } + + bus = &device_trees[dev->bus_type]; + dev->dev_num = bus->dev_num++; + + node = &bus->root.rb_node; + while (*node) { + int num = rb_entry(*node, struct device_header, node)->dev_num; + int result = dev->dev_num - num; + + if (result < 0) + node = &((*node)->rb_left); + else if (result > 0) + node = &((*node)->rb_right); + else + return -EEXIST; + } + + rb_link_node(&dev->node, parent, node); + rb_insert_color(&dev->node, &bus->root); + return 0; +} + +struct device_header *device__find_dev(enum device_bus_type bus_type, u8 dev_num) +{ + struct rb_node *node; + + if (bus_type >= DEVICE_BUS_MAX) + return ERR_PTR(-EINVAL); + + node = device_trees[bus_type].root.rb_node; + while (node) { + struct device_header *dev = rb_entry(node, struct device_header, + node); + if (dev_num < dev->dev_num) { + node = node->rb_left; + } else if (dev_num > dev->dev_num) { + node = node->rb_right; + } else { + return dev; + } + } + + return NULL; +} + +struct device_header *device__first_dev(enum device_bus_type bus_type) +{ + struct rb_node *node; + + if (bus_type >= DEVICE_BUS_MAX) + return NULL; + + node = rb_first(&device_trees[bus_type].root); + return node ? rb_entry(node, struct device_header, node) : NULL; +} + +struct device_header *device__next_dev(struct device_header *dev) +{ + struct rb_node *node = rb_next(&dev->node); + return node ? rb_entry(node, struct device_header, node) : NULL; +} diff --git a/hw/pci-shmem.c b/hw/pci-shmem.c index 4161335..00e5d93 100644 --- a/hw/pci-shmem.c +++ b/hw/pci-shmem.c @@ -1,3 +1,4 @@ +#include "kvm/devices.h" #include "kvm/pci-shmem.h" #include "kvm/virtio-pci-dev.h" #include "kvm/irq.h" @@ -30,6 +31,11 @@ static struct pci_device_header pci_shmem_pci_device = { .msix.pba_offset = cpu_to_le32(0x1001), /* Use BAR 1 */ }; +static struct device_header pci_shmem_device = { + .bus_type = DEVICE_BUS_PCI, + .data = &pci_shmem_pci_device, +}; + /* registers for the Inter-VM shared memory device */ enum ivshmem_registers { INTRMASK = 0, @@ -346,7 +352,7 @@ int shmem_parser(const struct option *opt, const char *arg, int unset) int pci_shmem__init(struct kvm *kvm) { - u8 dev, line, pin; + u8 line, pin; char *mem; int r; @@ -354,7 +360,7 @@ int pci_shmem__init(struct kvm *kvm) return 0; /* Register good old INTx */ - r = irq__register_device(PCI_DEVICE_ID_PCI_SHMEM, &dev, &pin, &line); + r = irq__register_device(PCI_DEVICE_ID_PCI_SHMEM, &pin, &line); if (r < 0) return r; @@ -384,7 +390,7 @@ int pci_shmem__init(struct kvm *kvm) pci_shmem_pci_device.bar[2] = cpu_to_le32(shmem_region->phys_addr | PCI_BASE_ADDRESS_SPACE_MEMORY); pci_shmem_pci_device.bar_size[2] = shmem_region->size; - pci__register(&pci_shmem_pci_device, dev); + device__register(&pci_shmem_device); /* Open shared memory and plug it into the guest */ mem = setup_shmem(shmem_region->handle, shmem_region->size, diff --git a/hw/vesa.c b/hw/vesa.c index a211491..33a675f 100644 --- a/hw/vesa.c +++ b/hw/vesa.c @@ -1,5 +1,6 @@ #include "kvm/vesa.h" +#include "kvm/devices.h" #include "kvm/virtio-pci-dev.h" #include "kvm/framebuffer.h" #include "kvm/kvm-cpu.h" @@ -44,19 +45,24 @@ static struct pci_device_header vesa_pci_device = { .bar_size[1] = VESA_MEM_SIZE, }; +static struct device_header vesa_device = { + .bus_type = DEVICE_BUS_PCI, + .data = &vesa_pci_device, +}; + static struct framebuffer vesafb; struct framebuffer *vesa__init(struct kvm *kvm) { u16 vesa_base_addr; - u8 dev, line, pin; + u8 line, pin; char *mem; int r; if (!kvm->cfg.vnc && !kvm->cfg.sdl) return NULL; - r = irq__register_device(PCI_DEVICE_ID_VESA, &dev, &pin, &line); + r = irq__register_device(PCI_DEVICE_ID_VESA, &pin, &line); if (r < 0) return ERR_PTR(r); @@ -68,7 +74,7 @@ struct framebuffer *vesa__init(struct kvm *kvm) vesa_pci_device.irq_line = line; vesa_base_addr = (u16)r; vesa_pci_device.bar[0] = cpu_to_le32(vesa_base_addr | PCI_BASE_ADDRESS_SPACE_IO); - pci__register(&vesa_pci_device, dev); + device__register(&vesa_device); mem = mmap(NULL, VESA_MEM_SIZE, PROT_RW, MAP_ANON_NORESERVE, -1, 0); if (mem == MAP_FAILED) diff --git a/include/kvm/devices.h b/include/kvm/devices.h new file mode 100644 index 0000000..c5de3de --- /dev/null +++ b/include/kvm/devices.h @@ -0,0 +1,27 @@ +#ifndef KVM__DEVICES_H +#define KVM__DEVICES_H + +#include +#include + +enum device_bus_type { + DEVICE_BUS_PCI, + DEVICE_BUS_MMIO, + DEVICE_BUS_MAX, +}; + +struct device_header { + enum device_bus_type bus_type; + void *data; + int dev_num; + struct rb_node node; +}; + +int device__register(struct device_header *dev); +struct device_header *device__find_dev(enum device_bus_type bus_type, + u8 dev_num); + +struct device_header *device__first_dev(enum device_bus_type bus_type); +struct device_header *device__next_dev(struct device_header *dev); + +#endif /* KVM__DEVICES_H */ diff --git a/include/kvm/irq.h b/include/kvm/irq.h index 43fecaf..5c1274b 100644 --- a/include/kvm/irq.h +++ b/include/kvm/irq.h @@ -22,7 +22,7 @@ struct pci_dev { struct list_head lines; }; -int irq__register_device(u32 dev, u8 *num, u8 *pin, u8 *line); +int irq__register_device(u32 dev, u8 *pin, u8 *line); struct rb_node *irq__get_pci_tree(void); diff --git a/include/kvm/pci.h b/include/kvm/pci.h index 26639b5..3da3811 100644 --- a/include/kvm/pci.h +++ b/include/kvm/pci.h @@ -9,7 +9,6 @@ #include "kvm/kvm.h" #include "kvm/msi.h" -#define PCI_MAX_DEVICES 256 /* * PCI Configuration Mechanism #1 I/O ports. See Section 3.7.4.1. * ("Configuration Mechanism #1") of the PCI Local Bus Specification 2.1 for @@ -86,7 +85,6 @@ struct pci_device_header { int pci__init(struct kvm *kvm); int pci__exit(struct kvm *kvm); -int pci__register(struct pci_device_header *dev, u8 dev_num); struct pci_device_header *pci__find_dev(u8 dev_num); u32 pci_get_io_space_block(u32 size); void pci__config_wr(struct kvm *kvm, union pci_config_address addr, void *data, int size); diff --git a/include/kvm/virtio-mmio.h b/include/kvm/virtio-mmio.h index e0ede3c..983c8fc 100644 --- a/include/kvm/virtio-mmio.h +++ b/include/kvm/virtio-mmio.h @@ -47,6 +47,7 @@ struct virtio_mmio { struct kvm *kvm; u8 irq; struct virtio_mmio_hdr hdr; + struct device_header dev_hdr; struct virtio_mmio_ioevent_param ioeventfds[VIRTIO_MMIO_MAX_VQ]; }; diff --git a/include/kvm/virtio-pci.h b/include/kvm/virtio-pci.h index 44130e0..6d9a558 100644 --- a/include/kvm/virtio-pci.h +++ b/include/kvm/virtio-pci.h @@ -1,6 +1,7 @@ #ifndef KVM__VIRTIO_PCI_H #define KVM__VIRTIO_PCI_H +#include "kvm/devices.h" #include "kvm/pci.h" #include @@ -19,6 +20,7 @@ struct virtio_pci_ioevent_param { struct virtio_pci { struct pci_device_header pci_hdr; + struct device_header dev_hdr; void *dev; u16 base_addr; diff --git a/pci.c b/pci.c index c77d3cc..8d3732d 100644 --- a/pci.c +++ b/pci.c @@ -1,3 +1,4 @@ +#include "kvm/devices.h" #include "kvm/pci.h" #include "kvm/ioport.h" #include "kvm/util.h" @@ -8,8 +9,6 @@ #define PCI_BAR_OFFSET(b) (offsetof(struct pci_device_header, bar[b])) -static struct pci_device_header *pci_devices[PCI_MAX_DEVICES]; - static union pci_config_address pci_config_address; /* This is within our PCI gap - in an unused area. @@ -63,20 +62,13 @@ static struct ioport_operations pci_config_address_ops = { static bool pci_device_exists(u8 bus_number, u8 device_number, u8 function_number) { - struct pci_device_header *dev; - if (pci_config_address.bus_number != bus_number) return false; if (pci_config_address.function_number != function_number) return false; - if (device_number >= PCI_MAX_DEVICES) - return false; - - dev = pci_devices[device_number]; - - return dev != NULL; + return !IS_ERR_OR_NULL(device__find_dev(DEVICE_BUS_PCI, device_number)); } static bool pci_config_data_out(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size) @@ -121,12 +113,13 @@ void pci__config_wr(struct kvm *kvm, union pci_config_address addr, void *data, offset = addr.w & 0xff; if (offset < sizeof(struct pci_device_header)) { - void *p = pci_devices[dev_num]; + void *p = device__find_dev(DEVICE_BUS_PCI, dev_num)->data; + struct pci_device_header *hdr = p; u8 bar = (offset - PCI_BAR_OFFSET(0)) / (sizeof(u32)); u32 sz = PCI_IO_SIZE; - if (bar < 6 && pci_devices[dev_num]->bar_size[bar]) - sz = pci_devices[dev_num]->bar_size[bar]; + if (bar < 6 && hdr->bar_size[bar]) + sz = hdr->bar_size[bar]; /* * If the kernel masks the BAR it would expect to find the @@ -158,7 +151,7 @@ void pci__config_rd(struct kvm *kvm, union pci_config_address addr, void *data, offset = addr.w & 0xff; if (offset < sizeof(struct pci_device_header)) { - void *p = pci_devices[dev_num]; + void *p = device__find_dev(DEVICE_BUS_PCI, dev_num)->data; memcpy(data, p + offset, size); } else { @@ -169,22 +162,14 @@ void pci__config_rd(struct kvm *kvm, union pci_config_address addr, void *data, } } -int pci__register(struct pci_device_header *dev, u8 dev_num) -{ - if (dev_num >= PCI_MAX_DEVICES) - return -ENOSPC; - - pci_devices[dev_num] = dev; - - return 0; -} - struct pci_device_header *pci__find_dev(u8 dev_num) { - if (dev_num >= PCI_MAX_DEVICES) - return ERR_PTR(-EOVERFLOW); + struct device_header *hdr = device__find_dev(DEVICE_BUS_PCI, dev_num); - return pci_devices[dev_num]; + if (IS_ERR_OR_NULL(hdr)) + return NULL; + + return hdr->data; } int pci__init(struct kvm *kvm) diff --git a/powerpc/irq.c b/powerpc/irq.c index e89fa3b..ae9da50 100644 --- a/powerpc/irq.c +++ b/powerpc/irq.c @@ -8,6 +8,7 @@ * by the Free Software Foundation. */ +#include "kvm/devices.h" #include "kvm/irq.h" #include "kvm/kvm.h" #include "kvm/util.h" @@ -31,15 +32,8 @@ * generic & cope with multiple PPC platform types. */ -static int pci_devs = 0; - -int irq__register_device(u32 dev, u8 *num, u8 *pin, u8 *line) +int irq__register_device(u32 dev, u8 *pin, u8 *line) { - if (pci_devs >= PCI_MAX_DEVICES) - die("Hit PCI device limit!\n"); - - *num = pci_devs++; - *pin = 1; /* * Have I said how nasty I find this? Line should be dontcare... PHB diff --git a/powerpc/spapr_pci.c b/powerpc/spapr_pci.c index 9f0ce7a..5f4016c 100644 --- a/powerpc/spapr_pci.c +++ b/powerpc/spapr_pci.c @@ -302,7 +302,7 @@ int spapr_populate_pci_devices(struct kvm *kvm, /* Populate PCI devices and allocate IRQs */ devices = 0; - for (devid = 0; devid < PCI_MAX_DEVICES; devid++) { + for (devid = 0; devid < KVM_MAX_DEVICES; devid++) { uint32_t *irqmap = interrupt_map[devices]; struct pci_device_header *hdr = pci__find_dev(devid); diff --git a/virtio/mmio.c b/virtio/mmio.c index fe42e95..f94c54f 100644 --- a/virtio/mmio.c +++ b/virtio/mmio.c @@ -1,3 +1,4 @@ +#include "kvm/devices.h" #include "kvm/virtio-mmio.h" #include "kvm/ioeventfd.h" #include "kvm/ioport.h" @@ -218,7 +219,7 @@ int virtio_mmio_init(struct kvm *kvm, void *dev, struct virtio_device *vdev, int device_id, int subsys_id, int class) { struct virtio_mmio *vmmio = vdev->virtio; - u8 device, pin, line; + u8 pin, line; vmmio->addr = virtio_mmio_get_io_space_block(VIRTIO_MMIO_IO_SIZE); vmmio->kvm = kvm; @@ -235,9 +236,15 @@ int virtio_mmio_init(struct kvm *kvm, void *dev, struct virtio_device *vdev, .queue_num_max = 256, }; - if (irq__register_device(subsys_id, &device, &pin, &line) < 0) + if (irq__register_device(subsys_id, &pin, &line) < 0) return -1; vmmio->irq = line; + vmmio->dev_hdr = (struct device_header) { + .bus_type = DEVICE_BUS_MMIO, + .data = vmmio, + }; + + device__register(&vmmio->dev_hdr); /* * Instantiate guest virtio-mmio devices using kernel command line diff --git a/virtio/pci.c b/virtio/pci.c index adc8efc..3d3e599 100644 --- a/virtio/pci.c +++ b/virtio/pci.c @@ -306,7 +306,7 @@ int virtio_pci__init(struct kvm *kvm, void *dev, struct virtio_device *vdev, int device_id, int subsys_id, int class) { struct virtio_pci *vpci = vdev->virtio; - u8 pin, line, ndev; + u8 pin, line; int r; vpci->dev = dev; @@ -343,6 +343,11 @@ int virtio_pci__init(struct kvm *kvm, void *dev, struct virtio_device *vdev, .bar_size[3] = PCI_IO_SIZE, }; + vpci->dev_hdr = (struct device_header) { + .bus_type = DEVICE_BUS_PCI, + .data = &vpci->pci_hdr, + }; + vpci->pci_hdr.msix.cap = PCI_CAP_ID_MSIX; vpci->pci_hdr.msix.next = 0; /* @@ -366,7 +371,7 @@ int virtio_pci__init(struct kvm *kvm, void *dev, struct virtio_device *vdev, vpci->pci_hdr.msix.pba_offset = cpu_to_le32(1 | PCI_IO_SIZE); /* Use BAR 3 */ vpci->config_vector = 0; - r = irq__register_device(subsys_id, &ndev, &pin, &line); + r = irq__register_device(subsys_id, &pin, &line); if (r < 0) goto free_mmio; @@ -375,7 +380,7 @@ int virtio_pci__init(struct kvm *kvm, void *dev, struct virtio_device *vdev, vpci->pci_hdr.irq_pin = pin; vpci->pci_hdr.irq_line = line; - r = pci__register(&vpci->pci_hdr, ndev); + r = device__register(&vpci->dev_hdr); if (r < 0) goto free_ioport; diff --git a/x86/irq.c b/x86/irq.c index 8dc90bf..7447c6b 100644 --- a/x86/irq.c +++ b/x86/irq.c @@ -17,7 +17,6 @@ #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 */ @@ -86,7 +85,7 @@ static int insert(struct rb_root *root, struct pci_dev *data) return 0; } -int irq__register_device(u32 dev, u8 *num, u8 *pin, u8 *line) +int irq__register_device(u32 dev, u8 *pin, u8 *line) { struct pci_dev *node; int r; @@ -128,7 +127,6 @@ int irq__register_device(u32 dev, u8 *num, u8 *pin, u8 *line) new->line = next_line++; *line = new->line; *pin = node->pin; - *num = next_dev++; list_add(&new->node, &node->lines);