[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH 2/4] hvmloader: Add support for PCI to PCI bridge



Most of this code is ported from SeaBIOS.

This allows many more PCI devices to be added.

It can also allow Windows to find it boot disks.

Signed-off-by: Don Slutz <dslutz@xxxxxxxxxxx>
CC: Don Slutz <dslutz@xxxxxxxxxxx>
---
 tools/firmware/hvmloader/pci.c      | 805 +++++++++++++++++++++++++++---------
 tools/firmware/hvmloader/pci_regs.h |  39 ++
 2 files changed, 647 insertions(+), 197 deletions(-)

diff --git a/tools/firmware/hvmloader/pci.c b/tools/firmware/hvmloader/pci.c
index 5ff87a7..189ce5f 100644
--- a/tools/firmware/hvmloader/pci.c
+++ b/tools/firmware/hvmloader/pci.c
@@ -38,28 +38,572 @@ uint64_t pci_hi_mem_start = 0, pci_hi_mem_end = 0;
 enum virtual_vga virtual_vga = VGA_none;
 unsigned long igd_opregion_pgbase = 0;
 
+#define ALIGN(x,a)              __ALIGN_MASK(x,(typeof(x))(a)-1)
+#define __ALIGN_MASK(x,mask)    (((x)+(mask))&~(mask))
+#define ALIGN_DOWN(x,a)         ((x) & ~((typeof(x))(a)-1))
+
+static inline uint8_t devfn_to_bus(uint32_t devfn) {
+    return devfn >> 8;
+}
+static inline uint8_t devfn_to_dev(uint32_t devfn) {
+    return (devfn >> 3) & 0x1f;
+}
+static inline uint8_t devfn_to_fn(uint32_t devfn) {
+    return devfn & 0x07;
+}
+
+struct pci_device {
+    uint32_t devfn;
+
+    /* Configuration space device information */
+    uint16_t class, vendor, device;
+    uint8_t header_type;
+    uint8_t secondary_bus;
+};
+
+#define PCI_CLASS_BRIDGE_PCI 0x0604
+#define PCI_HEADER_TYPE_MULTI_FUNCTION 0x80
+
+#define PCI_ROM_SLOT 6
+#define PCI_NUM_REGIONS 7
+#define PCI_BRIDGE_NUM_REGIONS 2
+
+#define PCI_DEVICE_MEM_MIN     0x1000
+#define PCI_BRIDGE_IO_MIN      0x1000
+#define PCI_BRIDGE_MEM_MIN   0x100000
+
+enum pci_region_type {
+    PCI_REGION_TYPE_IO,
+    PCI_REGION_TYPE_MEM,
+    PCI_REGION_TYPE_PREFMEM,
+    PCI_REGION_TYPE_COUNT,
+};
+
+static const char *region_type_name[] = {
+    [ PCI_REGION_TYPE_IO ]      = "io",
+    [ PCI_REGION_TYPE_MEM ]     = "mem",
+    [ PCI_REGION_TYPE_PREFMEM ] = "prefmem",
+};
+
+struct pci_region_entry {
+    struct pci_device *dev;
+    int bar;
+    uint64_t size;
+    uint64_t align;
+    int is64;
+    enum pci_region_type type;
+    struct pci_region_entry *next;
+};
+
+struct pci_region {
+    /* pci region assignments */
+    uint64_t base;
+    struct pci_region_entry *list;
+};
+
+struct pci_bus {
+    struct pci_region r[PCI_REGION_TYPE_COUNT];
+    struct pci_device *bus_dev;
+};
+
+/* Resources assignable to PCI devices via BARs. */
+struct resource {
+    uint64_t base, max;
+};
+
+static uint32_t pci_bar(struct pci_device *pci, int region_num)
+{
+    uint8_t type = pci->header_type & ~PCI_HEADER_TYPE_MULTI_FUNCTION;
+
+    if (region_num != PCI_ROM_SLOT) {
+        return PCI_BASE_ADDRESS_0 + region_num * 4;
+    }
+
+    return type == PCI_HEADER_TYPE_BRIDGE ? PCI_ROM_ADDRESS1 : PCI_ROM_ADDRESS;
+}
+
+static void
+pci_set_io_region_addr(struct pci_device *pci, int bar, uint64_t addr, int 
is64)
+{
+    uint32_t ofs = pci_bar(pci, bar);
+    uint32_t addr32 = addr >> 32;
+
+    pci_writel(pci->devfn, ofs, addr);
+    if (is64)
+        pci_writel(pci->devfn, ofs + 4, addr32);
+}
+
+/****************************************************************
+ * Bus initialization
+ ****************************************************************/
+
+static void
+pci_bios_init_bus_rec(int bus, uint8_t *pci_bus)
+{
+    int devfn;
+    uint16_t class;
+
+    /* prevent accidental access to unintended devices */
+    for ( devfn = bus * 256; devfn < (bus + 1) * 256; devfn++ ) {
+        class = pci_readw(devfn, PCI_CLASS_DEVICE);
+        if (class == PCI_CLASS_BRIDGE_PCI) {
+            pci_writeb(devfn, PCI_SECONDARY_BUS, 255);
+            pci_writeb(devfn, PCI_SUBORDINATE_BUS, 0);
+        }
+    }
+
+    for ( devfn = bus * 256; devfn < (bus + 1) * 256; devfn++ ) {
+        uint8_t pribus, secbus, subbus;
+
+        class = pci_readw(devfn, PCI_CLASS_DEVICE);
+        if (class != PCI_CLASS_BRIDGE_PCI) {
+            continue;
+        }
+
+        pribus = pci_readb(devfn, PCI_PRIMARY_BUS);
+        if (pribus != bus) {
+            pci_writeb(devfn, PCI_PRIMARY_BUS, bus);
+        }
+
+        secbus = pci_readb(devfn, PCI_SECONDARY_BUS);
+        (*pci_bus)++;
+        if (*pci_bus != secbus) {
+            secbus = *pci_bus;
+            pci_writeb(devfn, PCI_SECONDARY_BUS, secbus);
+        }
+
+        /* set to max for access to all subordinate buses.
+           later set it to accurate value */
+        subbus = pci_readb(devfn, PCI_SUBORDINATE_BUS);
+        pci_writeb(devfn, PCI_SUBORDINATE_BUS, 255);
+
+        pci_bios_init_bus_rec(secbus, pci_bus);
+
+        if (subbus != *pci_bus) {
+            subbus = *pci_bus;
+        }
+        pci_writeb(devfn, PCI_SUBORDINATE_BUS, subbus);
+    }
+}
+
+static uint8_t
+pci_bios_init_bus(void)
+{
+    uint8_t pci_bus = 0;
+
+    pci_bios_init_bus_rec(0 /* host bus */, &pci_bus);
+    return pci_bus;
+}
+
+
+/****************************************************************
+ * Bus sizing
+ ****************************************************************/
+
+static void
+pci_bios_get_bar(struct pci_device *pci, int bar,
+                 int *ptype, uint64_t *psize, int *pis64)
+{
+    uint32_t ofs = pci_bar(pci, bar);
+    uint16_t devfn = pci->devfn;
+    uint32_t old = pci_readl(devfn, ofs);
+    int is64 = 0, type = PCI_REGION_TYPE_MEM;
+    uint64_t mask;
+    uint64_t val;
+
+    if (bar == PCI_ROM_SLOT) {
+        mask = PCI_ROM_ADDRESS_MASK;
+        pci_writel(devfn, ofs, mask);
+    } else {
+        if (old & PCI_BASE_ADDRESS_SPACE_IO) {
+            mask = PCI_BASE_ADDRESS_IO_MASK;
+            type = PCI_REGION_TYPE_IO;
+        } else {
+            mask = PCI_BASE_ADDRESS_MEM_MASK;
+            if (old & PCI_BASE_ADDRESS_MEM_PREFETCH)
+                type = PCI_REGION_TYPE_PREFMEM;
+            is64 = ((old & PCI_BASE_ADDRESS_MEM_TYPE_MASK)
+                    == PCI_BASE_ADDRESS_MEM_TYPE_64);
+        }
+        pci_writel(devfn, ofs, ~0);
+    }
+    val = pci_readl(devfn, ofs);
+    pci_writel(devfn, ofs, old);
+    if (is64) {
+        uint32_t hold = pci_readl(devfn, ofs + 4);
+        uint32_t high;
+
+        pci_writel(devfn, ofs + 4, ~0);
+        high = pci_readl(devfn, ofs + 4);
+        pci_writel(devfn, ofs + 4, hold);
+        val |= ((uint64_t)high << 32);
+        mask |= ((uint64_t)0xffffffff << 32);
+        *psize = (~(val & mask)) + 1;
+    } else {
+        *psize = ((~(val & mask)) + 1) & 0xffffffff;
+    }
+    *ptype = type;
+    *pis64 = is64;
+}
+
+static int pci_bios_bridge_region_is64(struct pci_region *r,
+                                       struct pci_device *pci, int type)
+{
+    uint32_t pmem;
+    struct pci_region_entry *entry;
+
+    if (type != PCI_REGION_TYPE_PREFMEM)
+        return 0;
+    pmem = pci_readl(pci->devfn, PCI_PREF_MEMORY_BASE);
+    if (!pmem) {
+        pci_writel(pci->devfn, PCI_PREF_MEMORY_BASE, 0xfff0fff0);
+        pmem = pci_readl(pci->devfn, PCI_PREF_MEMORY_BASE);
+        pci_writel(pci->devfn, PCI_PREF_MEMORY_BASE, 0x0);
+    }
+    if ((pmem & PCI_PREF_RANGE_TYPE_MASK) != PCI_PREF_RANGE_TYPE_64)
+        return 0;
+    entry = r->list;
+    while (entry) {
+        if (!entry->is64)
+            return 0;
+        entry = entry->next;
+    }
+    return 1;
+}
+
+static uint64_t pci_region_align(struct pci_region *r)
+{
+    if (!r->list)
+        return 1;
+    /* The first entry in the sorted list has the largest alignment */
+    return r->list->align;
+}
+
+static uint64_t pci_region_sum(struct pci_region *r)
+{
+    struct pci_region_entry *entry = r->list;
+    uint64_t sum = 0;
+    while (entry) {
+        sum += entry->size;
+        entry = entry->next;
+    }
+    return sum;
+}
+
+static void pci_region_migrate_64bit_entries(struct pci_region *from,
+                                             struct pci_region *to)
+{
+    struct pci_region_entry **pprev = &from->list, **last = &to->list;
+    while (*pprev) {
+        struct pci_region_entry *entry = *pprev;
+        if (!entry->is64) {
+            pprev = &entry->next;
+            continue;
+        }
+        /* Move from source list to destination list. */
+        *pprev = entry->next;
+        entry->next = NULL;
+        *last = entry;
+        last = &entry->next;
+    }
+}
+
+static void
+pci_region_create_entry(struct pci_region_entry * entry,
+                        struct pci_bus *bus, struct pci_device *dev,
+                        int bar, uint64_t size, uint64_t align, int type, int 
is64)
+{
+    struct pci_region_entry **pprev;
+
+    memset(entry, 0, sizeof(*entry));
+    entry->dev = dev;
+    entry->bar = bar;
+    entry->size = size;
+    entry->align = align;
+    entry->is64 = is64;
+    entry->type = type;
+
+    /* Insert into list in sorted order. */
+    for (pprev = &bus->r[type].list; *pprev; pprev = &(*pprev)->next) {
+        struct pci_region_entry *pos = *pprev;
+        if (pos->align < align || (pos->align == align && pos->size < size))
+            break;
+    }
+    entry->next = *pprev;
+    *pprev = entry;
+}
+
+static int pci_bios_check_devices(struct pci_device *pci_base, unsigned int 
nr_pci_devs,
+                                  struct pci_bus *busses, int MaxPCIBus)
+{
+    int pci_idx;
+    int secondary_bus;
+    struct pci_region_entry *entry = (struct pci_region_entry 
*)&busses[MaxPCIBus + 1];
+
+    /* Calculate resources needed for regular (non-bus) devices. */
+    for ( pci_idx = 0; pci_idx < nr_pci_devs; pci_idx++ ) {
+        struct pci_device *pci = &pci_base[pci_idx];
+        struct pci_bus *bus;
+        int i;
+
+        if (pci->class == PCI_CLASS_BRIDGE_PCI)
+            busses[pci->secondary_bus].bus_dev = pci;
+
+        bus = &busses[devfn_to_bus(pci->devfn)];
+        for (i = 0; i < PCI_NUM_REGIONS; i++) {
+            int type, is64;
+            uint64_t size;
+
+            if ((pci->class == PCI_CLASS_BRIDGE_PCI) &&
+                (i >= PCI_BRIDGE_NUM_REGIONS && i < PCI_ROM_SLOT))
+                continue;
+
+            pci_bios_get_bar(pci, i, &type, &size, &is64);
+            if (size == 0)
+                continue;
+
+            if (type != PCI_REGION_TYPE_IO && size < PCI_DEVICE_MEM_MIN)
+                size = PCI_DEVICE_MEM_MIN;
+            pci_region_create_entry(entry, bus, pci, i,
+                                    size, size, type, is64);
+            entry++;
+
+            if (is64)
+                i++;
+        }
+    }
+
+    /* Propagate required bus resources to parent busses. */
+    for (secondary_bus=MaxPCIBus; secondary_bus>0; secondary_bus--) {
+        struct pci_bus *s = &busses[secondary_bus];
+        struct pci_bus *parent;
+        int type;
+
+        if (!s->bus_dev)
+            continue;
+        parent = &busses[devfn_to_bus(s->bus_dev->devfn)];
+        for (type = 0; type < PCI_REGION_TYPE_COUNT; type++) {
+            uint64_t align = (type == PCI_REGION_TYPE_IO) ?
+                PCI_BRIDGE_IO_MIN : PCI_BRIDGE_MEM_MIN;
+            uint64_t sum;
+            uint64_t size;
+            int is64;                    
+
+            if (pci_region_align(&s->r[type]) > align)
+                align = pci_region_align(&s->r[type]);
+            sum = pci_region_sum(&s->r[type]);
+            size = ALIGN(sum, align);
+            is64 = pci_bios_bridge_region_is64(&s->r[type],
+                                               s->bus_dev, type);
+            /* entry->bar is -1 if the entry represents a bridge region */
+            pci_region_create_entry(entry, parent, s->bus_dev, -1,
+                                    size, align, type, is64);
+            entry++;
+        }
+    }
+    return 0;
+}
+
+
+/****************************************************************
+ * BAR assignment
+ ****************************************************************/
+
+/* Setup region bases (given the regions' size and alignment) */
+static int pci_bios_init_root_regions(struct pci_bus *bus,
+                                      struct resource *mem_resource,
+                                      struct resource *high_mem_resource,
+                                      struct resource *io_resource)
+{
+    uint64_t sum, align;
+    struct pci_region *r_end = &bus->r[PCI_REGION_TYPE_PREFMEM];
+    struct pci_region *r_start = &bus->r[PCI_REGION_TYPE_MEM];
+
+    bus->r[PCI_REGION_TYPE_IO].base = io_resource->base;
+
+    if (pci_region_align(r_start) >= pci_region_align(r_end)) {
+        /* Swap regions to improve alignment. */
+        r_end = r_start;
+        r_start = &bus->r[PCI_REGION_TYPE_PREFMEM];
+    }
+    align = pci_region_align(r_end);
+    r_end->base = ALIGN(mem_resource->base, align);
+    sum = pci_region_sum(r_end);
+    align = pci_region_align(r_start);
+    r_start->base = ALIGN((r_end->base + sum), align);
+    sum = pci_region_sum(r_start);
+
+    if ((r_start->base < mem_resource->base) ||
+        ((r_start->base + sum) > mem_resource->max))
+        /* Memory range requested is larger than available. */
+        return -1;
+    return 0;
+}
+
+static uint32_t pci_bios_size_root_regions(struct pci_bus *bus)
+{
+    uint64_t sum, align;
+    struct pci_region *r_end = &bus->r[PCI_REGION_TYPE_PREFMEM];
+    struct pci_region *r_start = &bus->r[PCI_REGION_TYPE_MEM];
+
+    if (pci_region_align(r_start) < pci_region_align(r_end)) {
+        /* Swap regions to improve alignment. */
+        r_end = r_start;
+        r_start = &bus->r[PCI_REGION_TYPE_PREFMEM];
+    }
+    sum = pci_region_sum(r_end);
+    align = pci_region_align(r_end);
+    r_end->base = ALIGN_DOWN((pci_mem_end - sum), align);
+    sum = pci_region_sum(r_start);
+    align = pci_region_align(r_start);
+    r_start->base = ALIGN_DOWN((r_end->base - sum), align);
+
+    return pci_mem_end - r_start->base;
+}
+
+#define PCI_IO_SHIFT            8
+#define PCI_MEMORY_SHIFT        16
+#define PCI_PREF_MEMORY_SHIFT   16
+
+static void
+pci_region_map_one_entry(struct pci_region_entry *entry, uint64_t addr)
+{
+    uint16_t devfn = entry->dev->devfn;
+    uint64_t limit = addr + entry->size - 1;
+    uint32_t cmd;
+
+    if (entry->size) {
+        /* Now enable the memory or I/O mapping. */
+        cmd = pci_readb(devfn, PCI_COMMAND);
+        if ( entry->type == PCI_REGION_TYPE_IO )
+            cmd |= PCI_COMMAND_IO;
+        else
+            cmd |= PCI_COMMAND_MEMORY;
+        pci_writeb(devfn, PCI_COMMAND, cmd);
+    }
+
+    if (entry->bar >= 0) {
+        if (entry->size)
+            printf("pci dev %02x:%02x.%x"
+                   " bar %d size "PRIllx": "PRIllx" [%s]\n",
+                   devfn_to_bus(devfn), devfn_to_dev(devfn),
+                   devfn_to_fn(devfn), entry->bar, PRIllx_arg(entry->size),
+                   PRIllx_arg(addr), region_type_name[entry->type]);
+
+        pci_set_io_region_addr(entry->dev, entry->bar, addr, entry->is64);
+        return;
+    }
+
+    if (entry->size)
+        printf("pci bri %02x:%02x.%x"
+               "       size "PRIllx": "PRIllx" [%s]\n",
+               devfn_to_bus(devfn), devfn_to_dev(devfn), devfn_to_fn(devfn),
+               PRIllx_arg(entry->size), PRIllx_arg(addr),
+               region_type_name[entry->type]);
+
+    if (entry->type == PCI_REGION_TYPE_IO) {
+        pci_writeb(devfn, PCI_IO_BASE, addr >> PCI_IO_SHIFT);
+        pci_writew(devfn, PCI_IO_BASE_UPPER16, 0);
+        pci_writeb(devfn, PCI_IO_LIMIT, limit >> PCI_IO_SHIFT);
+        pci_writew(devfn, PCI_IO_LIMIT_UPPER16, 0);
+    }
+    if (entry->type == PCI_REGION_TYPE_MEM) {
+        pci_writew(devfn, PCI_MEMORY_BASE, addr >> PCI_MEMORY_SHIFT);
+        pci_writew(devfn, PCI_MEMORY_LIMIT, limit >> PCI_MEMORY_SHIFT);
+    }
+    if (entry->type == PCI_REGION_TYPE_PREFMEM) {
+        ASSERT(PCI_MEMORY_SHIFT == PCI_PREF_MEMORY_SHIFT);
+        pci_writew(devfn, PCI_PREF_MEMORY_BASE, addr >> PCI_MEMORY_SHIFT);
+        pci_writew(devfn, PCI_PREF_MEMORY_LIMIT, limit >> PCI_MEMORY_SHIFT);
+        pci_writel(devfn, PCI_PREF_BASE_UPPER32, addr >> 32);
+        pci_writel(devfn, PCI_PREF_LIMIT_UPPER32, limit >> 32);
+    }
+}
+
+static void pci_region_map_entries(struct pci_bus *busses, struct pci_region 
*r)
+{
+    struct pci_region_entry *entry = r->list;
+    while (entry) {
+        uint64_t addr = r->base;
+        struct pci_region_entry *next = entry->next;
+
+        r->base += entry->size;
+        if (entry->bar == -1)
+            /* Update bus base address if entry is a bridge region */
+            busses[entry->dev->secondary_bus].r[entry->type].base = addr;
+        pci_region_map_one_entry(entry, addr);
+        entry = next;
+    }
+}
+
+static void pci_bios_map_devices(struct pci_bus *busses, int MaxPCIBus,
+                                 struct resource *mem_resource,
+                                 struct resource *high_mem_resource,
+                                 struct resource *io_resource)
+{
+    int bus;
+
+    if (pci_bios_init_root_regions(busses, mem_resource, high_mem_resource,
+                                   io_resource)) {
+        struct pci_region r64_mem, r64_pref;
+        uint64_t sum_mem;
+        uint64_t sum_pref;
+        uint64_t align_mem;
+        uint64_t align_pref;
+
+        r64_mem.list = NULL;
+        r64_pref.list = NULL;
+        pci_region_migrate_64bit_entries(&busses[0].r[PCI_REGION_TYPE_MEM],
+                                         &r64_mem);
+        pci_region_migrate_64bit_entries(&busses[0].r[PCI_REGION_TYPE_PREFMEM],
+                                         &r64_pref);
+        if (pci_bios_init_root_regions(busses, mem_resource, high_mem_resource,
+                                       io_resource)) {
+            printf("PCI: out of 32bit address space\n");
+            ASSERT(0);
+        }
+
+        sum_mem = pci_region_sum(&r64_mem);
+        sum_pref = pci_region_sum(&r64_pref);
+        align_mem = pci_region_align(&r64_mem);
+        align_pref = pci_region_align(&r64_pref);
+
+        ASSERT(high_mem_resource->base);
+        r64_mem.base = ALIGN(high_mem_resource->base, align_mem);
+        r64_pref.base = ALIGN(r64_mem.base + sum_mem, align_pref);
+        if ((r64_mem.base < high_mem_resource->base) ||
+            ((r64_pref.base + sum_pref) > high_mem_resource->max)) {
+            printf("PCI: out of 64bit address space\n");
+            ASSERT(0);
+        }
+        pci_region_map_entries(busses, &r64_mem);
+        pci_region_map_entries(busses, &r64_pref);
+    }
+    /* Map regions on each device. */
+    for ( bus = 0; bus <= MaxPCIBus; bus++ ) {
+        int type;
+        for ( type = 0; type < PCI_REGION_TYPE_COUNT; type++ )
+            pci_region_map_entries(busses, &busses[bus].r[type]);
+    }
+}
+
+/****************************************************************
+ * Main setup code
+ ****************************************************************/
+
 void pci_setup(void)
 {
-    uint8_t is_64bar, using_64bar, bar64_relocate = 0;
-    uint32_t devfn, bar_reg, cmd, bar_data, bar_data_upper;
-    uint64_t base, bar_sz, bar_sz_upper, mmio_total = 0;
+    uint32_t devfn, cmd, mmio_total = 0;
     uint32_t vga_devfn = 256;
     uint16_t class, vendor_id, device_id;
-    unsigned int bar, pin, link, isa_irq;
-
+    unsigned int pin, link, isa_irq;
     /* Resources assignable to PCI devices via BARs. */
-    struct resource {
-        uint64_t base, max;
-    } *resource, mem_resource, high_mem_resource, io_resource;
-
-    /* Create a list of device BARs in descending order of size. */
-    struct bars {
-        uint32_t is_64bar;
-        uint32_t devfn;
-        uint32_t bar_reg;
-        uint64_t bar_sz;
-    } *bars = (struct bars *)scratch_start;
-    unsigned int i, nr_bars = 0;
+    struct resource mem_resource, high_mem_resource, io_resource;
+    uint32_t maxBus = 1;
+
+    struct pci_device *pci_devs = (struct pci_device *)scratch_start;
+    unsigned int nr_pci_devs = 0, pci_idx;
+    struct pci_bus *busses;
     uint64_t mmio_hole_size = 0;
 
     const char *s;
@@ -88,6 +632,38 @@ void pci_setup(void)
     printf("Relocating guest memory for lowmem MMIO space %s\n",
            allow_memory_relocate?"enabled":"disabled");
 
+    /* Calculate the max PCI bus */
+    maxBus = pci_bios_init_bus();
+
+    /* Scan the PCI busses */
+    for ( devfn = 0; devfn < (maxBus + 1) * 256; devfn++ )
+    {
+        class     = pci_readw(devfn, PCI_CLASS_DEVICE);
+        vendor_id = pci_readw(devfn, PCI_VENDOR_ID);
+        device_id = pci_readw(devfn, PCI_DEVICE_ID);
+        if ( (vendor_id == 0xffff) && (device_id == 0xffff) )
+            continue;
+        if ( (vendor_id == 0x8086) && 
+             ((device_id == 0x7000) || (device_id == 0x7110)) ) {
+            ASSERT(PCI_ISA_DEVFN == devfn);
+        }
+        pci_devs[nr_pci_devs].devfn = devfn;
+        pci_devs[nr_pci_devs].class = class;
+        pci_devs[nr_pci_devs].vendor = vendor_id;
+        pci_devs[nr_pci_devs].device = device_id;
+        pci_devs[nr_pci_devs].header_type = pci_readb(devfn, PCI_HEADER_TYPE);
+        pci_devs[nr_pci_devs].secondary_bus = 0;
+        nr_pci_devs++;
+
+        if (class != PCI_CLASS_BRIDGE_PCI) {
+            continue;
+        }
+        pci_devs[nr_pci_devs - 1].secondary_bus = pci_readb(devfn, 
PCI_SECONDARY_BUS);
+    }
+    busses = (struct pci_bus *)&pci_devs[nr_pci_devs];
+    memset(busses, 0, sizeof(*busses) * (maxBus + 1));
+    pci_bios_check_devices(pci_devs, nr_pci_devs, busses, maxBus);
+
     s = xenstore_read("platform/mmio_hole_size", NULL);
     if ( s )
         mmio_hole_size = strtoll(s, NULL, 0);
@@ -106,17 +682,14 @@ void pci_setup(void)
     outb(0x4d0, (uint8_t)(PCI_ISA_IRQ_MASK >> 0));
     outb(0x4d1, (uint8_t)(PCI_ISA_IRQ_MASK >> 8));
 
-    /* Scan the PCI bus and map resources. */
-    for ( devfn = 0; devfn < 256; devfn++ )
-    {
-        class     = pci_readw(devfn, PCI_CLASS_DEVICE);
-        vendor_id = pci_readw(devfn, PCI_VENDOR_ID);
-        device_id = pci_readw(devfn, PCI_DEVICE_ID);
-        if ( (vendor_id == 0xffff) && (device_id == 0xffff) )
-            continue;
+    /* Scan the PCI bus and adjust resources. */
+    for ( pci_idx = 0; pci_idx < nr_pci_devs; pci_idx++ ) {
+        struct pci_device *pci = &pci_devs[pci_idx];
 
-        ASSERT((devfn != PCI_ISA_DEVFN) ||
-               ((vendor_id == 0x8086) && (device_id == 0x7000)));
+        devfn = pci->devfn;
+        class = pci->class;
+        vendor_id = pci->vendor;
+        device_id = pci->device;
 
         switch ( class )
         {
@@ -170,75 +743,6 @@ void pci_setup(void)
             break;
         }
 
-        /* Map the I/O memory and port resources. */
-        for ( bar = 0; bar < 7; bar++ )
-        {
-            bar_sz_upper = 0;
-            bar_reg = PCI_BASE_ADDRESS_0 + 4*bar;
-            if ( bar == 6 )
-                bar_reg = PCI_ROM_ADDRESS;
-
-            bar_data = pci_readl(devfn, bar_reg);
-            if ( bar_reg != PCI_ROM_ADDRESS )
-            {
-                is_64bar = !!((bar_data & (PCI_BASE_ADDRESS_SPACE |
-                             PCI_BASE_ADDRESS_MEM_TYPE_MASK)) ==
-                             (PCI_BASE_ADDRESS_SPACE_MEMORY |
-                             PCI_BASE_ADDRESS_MEM_TYPE_64));
-                pci_writel(devfn, bar_reg, ~0);
-            }
-            else
-            {
-                is_64bar = 0;
-                pci_writel(devfn, bar_reg,
-                           (bar_data | PCI_ROM_ADDRESS_MASK) &
-                           ~PCI_ROM_ADDRESS_ENABLE);
-            }
-            bar_sz = pci_readl(devfn, bar_reg);
-            pci_writel(devfn, bar_reg, bar_data);
-
-            if ( bar_reg != PCI_ROM_ADDRESS )
-                bar_sz &= (((bar_data & PCI_BASE_ADDRESS_SPACE) ==
-                            PCI_BASE_ADDRESS_SPACE_MEMORY) ?
-                           PCI_BASE_ADDRESS_MEM_MASK :
-                           (PCI_BASE_ADDRESS_IO_MASK & 0xffff));
-            else
-                bar_sz &= PCI_ROM_ADDRESS_MASK;
-            if (is_64bar) {
-                bar_data_upper = pci_readl(devfn, bar_reg + 4);
-                pci_writel(devfn, bar_reg + 4, ~0);
-                bar_sz_upper = pci_readl(devfn, bar_reg + 4);
-                pci_writel(devfn, bar_reg + 4, bar_data_upper);
-                bar_sz = (bar_sz_upper << 32) | bar_sz;
-            }
-            bar_sz &= ~(bar_sz - 1);
-            if ( bar_sz == 0 )
-                continue;
-
-            for ( i = 0; i < nr_bars; i++ )
-                if ( bars[i].bar_sz < bar_sz )
-                    break;
-
-            if ( i != nr_bars )
-                memmove(&bars[i+1], &bars[i], (nr_bars-i) * sizeof(*bars));
-
-            bars[i].is_64bar = is_64bar;
-            bars[i].devfn   = devfn;
-            bars[i].bar_reg = bar_reg;
-            bars[i].bar_sz  = bar_sz;
-
-            if ( ((bar_data & PCI_BASE_ADDRESS_SPACE) ==
-                  PCI_BASE_ADDRESS_SPACE_MEMORY) ||
-                 (bar_reg == PCI_ROM_ADDRESS) )
-                mmio_total += bar_sz;
-
-            nr_bars++;
-
-            /*The upper half is already calculated, skip it! */
-            if (is_64bar)
-                bar++;
-        }
-
         /* Map the interrupt. */
         pin = pci_readb(devfn, PCI_INTERRUPT_PIN);
         if ( pin != 0 )
@@ -247,8 +751,9 @@ void pci_setup(void)
             link = ((pin - 1) + (devfn >> 3)) & 3;
             isa_irq = pci_readb(PCI_ISA_DEVFN, 0x60 + link);
             pci_writeb(devfn, PCI_INTERRUPT_LINE, isa_irq);
-            printf("pci dev %02x:%x INT%c->IRQ%u\n",
-                   devfn>>3, devfn&7, 'A'+pin-1, isa_irq);
+            printf("pci dev %02x:%02x.%x INT%c->IRQ%u\n",
+                   devfn_to_bus(devfn), devfn_to_dev(devfn),
+                   devfn_to_fn(devfn), 'A'+pin-1, isa_irq);
         }
 
         /* Enable bus mastering. */
@@ -276,9 +781,12 @@ void pci_setup(void)
                    pci_mem_start, HVM_BELOW_4G_MMIO_START,
                    (long)mmio_hole_size);
         }
+        mmio_total = pci_bios_size_root_regions(busses);
     }
     else
     {
+        mmio_total = pci_bios_size_root_regions(busses);
+
         /*
          * At the moment qemu-xen can't deal with relocated memory regions.
          * It's too close to the release to make a proper fix; for now,
@@ -305,7 +813,6 @@ void pci_setup(void)
     {
         printf("Low MMIO hole not large enough for all devices,"
                " relocating some BARs to 64-bit\n");
-        bar64_relocate = 1;
     }
 
     /* Relocate RAM that overlaps PCI space (in 64k-page chunks). */
@@ -352,104 +859,8 @@ void pci_setup(void)
     io_resource.base = 0xc000;
     io_resource.max = 0x10000;
 
-    /* Assign iomem and ioport resources in descending order of size. */
-    for ( i = 0; i < nr_bars; i++ )
-    {
-        devfn   = bars[i].devfn;
-        bar_reg = bars[i].bar_reg;
-        bar_sz  = bars[i].bar_sz;
-
-        /*
-         * Relocate to high memory if the total amount of MMIO needed
-         * is more than the low MMIO available.  Because devices are
-         * processed in order of bar_sz, this will preferentially
-         * relocate larger devices to high memory first.
-         *
-         * NB: The code here is rather fragile, as the check here to see
-         * whether bar_sz will fit in the low MMIO region doesn't match the
-         * real check made below, which involves aligning the base offset of 
the
-         * bar with the size of the bar itself.  As it happens, this will 
always
-         * be satisfied because:
-         * - The first one will succeed because the MMIO hole can only start at
-         *   0x{f,e,c,8}00000000.  If it fits, it will be aligned properly.
-         * - All subsequent ones will be aligned because the list is ordered
-         *   large to small, and bar_sz is always a power of 2. (At least
-         *   the code here assumes it to be.)
-         * Should either of those two conditions change, this code will break.
-         */
-        using_64bar = bars[i].is_64bar && bar64_relocate
-            && (mmio_total > (mem_resource.max - mem_resource.base));
-        bar_data = pci_readl(devfn, bar_reg);
-
-        if ( (bar_data & PCI_BASE_ADDRESS_SPACE) ==
-             PCI_BASE_ADDRESS_SPACE_MEMORY )
-        {
-            /* Mapping high memory if PCI device is 64 bits bar */
-            if ( using_64bar ) {
-                if ( high_mem_resource.base & (bar_sz - 1) )
-                    high_mem_resource.base = high_mem_resource.base - 
-                        (high_mem_resource.base & (bar_sz - 1)) + bar_sz;
-                if ( !pci_hi_mem_start )
-                    pci_hi_mem_start = high_mem_resource.base;
-                resource = &high_mem_resource;
-                bar_data &= ~PCI_BASE_ADDRESS_MEM_MASK;
-            } 
-            else {
-                resource = &mem_resource;
-                bar_data &= ~PCI_BASE_ADDRESS_MEM_MASK;
-            }
-            mmio_total -= bar_sz;
-        }
-        else
-        {
-            resource = &io_resource;
-            bar_data &= ~PCI_BASE_ADDRESS_IO_MASK;
-        }
-
-        base = (resource->base  + bar_sz - 1) & ~(uint64_t)(bar_sz - 1);
-        bar_data |= (uint32_t)base;
-        bar_data_upper = (uint32_t)(base >> 32);
-        base += bar_sz;
-
-        if ( (base < resource->base) || (base > resource->max) )
-        {
-            printf("pci dev %02x:%x bar %02x size "PRIllx": no space for "
-                   "resource!\n", devfn>>3, devfn&7, bar_reg,
-                   PRIllx_arg(bar_sz));
-            continue;
-        }
-
-        resource->base = base;
-
-        pci_writel(devfn, bar_reg, bar_data);
-        if (using_64bar)
-            pci_writel(devfn, bar_reg + 4, bar_data_upper);
-        printf("pci dev %02x:%x bar %02x size "PRIllx": %x%08x\n",
-               devfn>>3, devfn&7, bar_reg,
-               PRIllx_arg(bar_sz),
-               bar_data_upper, bar_data);
-                       
-
-        /* Now enable the memory or I/O mapping. */
-        cmd = pci_readw(devfn, PCI_COMMAND);
-        if ( (bar_reg == PCI_ROM_ADDRESS) ||
-             ((bar_data & PCI_BASE_ADDRESS_SPACE) ==
-              PCI_BASE_ADDRESS_SPACE_MEMORY) )
-            cmd |= PCI_COMMAND_MEMORY;
-        else
-            cmd |= PCI_COMMAND_IO;
-        pci_writew(devfn, PCI_COMMAND, cmd);
-    }
-
-    if ( pci_hi_mem_start )
-    {
-        /*
-         * Make end address alignment match the start address one's so that
-         * fewer variable range MTRRs are needed to cover the range.
-         */
-        pci_hi_mem_end = ((high_mem_resource.base - 1) |
-                          ((pci_hi_mem_start & -pci_hi_mem_start) - 1)) + 1;
-    }
+    pci_bios_map_devices(busses, maxBus, &mem_resource, &high_mem_resource,
+                         &io_resource);
 
     if ( vga_devfn != 256 )
     {
diff --git a/tools/firmware/hvmloader/pci_regs.h 
b/tools/firmware/hvmloader/pci_regs.h
index 7bf2d87..66cf6de 100644
--- a/tools/firmware/hvmloader/pci_regs.h
+++ b/tools/firmware/hvmloader/pci_regs.h
@@ -105,6 +105,45 @@
 #define PCI_MIN_GNT  0x3e /* 8 bits */
 #define PCI_MAX_LAT  0x3f /* 8 bits */
 
+/* Header type 1 (PCI-to-PCI bridges) */
+#define PCI_PRIMARY_BUS         0x18    /* Primary bus number */
+#define PCI_SECONDARY_BUS       0x19    /* Secondary bus number */
+#define PCI_SUBORDINATE_BUS     0x1a    /* Highest bus number behind the 
bridge */
+#define PCI_SEC_LATENCY_TIMER   0x1b    /* Latency timer for secondary 
interface */
+#define PCI_IO_BASE             0x1c    /* I/O range behind the bridge */
+#define PCI_IO_LIMIT            0x1d
+#define  PCI_IO_RANGE_TYPE_MASK 0x0fUL  /* I/O bridging type */
+#define  PCI_IO_RANGE_TYPE_16   0x00
+#define  PCI_IO_RANGE_TYPE_32   0x01
+#define  PCI_IO_RANGE_MASK      (~0x0fUL)
+#define PCI_SEC_STATUS          0x1e    /* Secondary status register, only bit 
14 used */
+#define PCI_MEMORY_BASE         0x20    /* Memory range behind */
+#define PCI_MEMORY_LIMIT        0x22
+#define  PCI_MEMORY_RANGE_TYPE_MASK 0x0fUL
+#define  PCI_MEMORY_RANGE_MASK  (~0x0fUL)
+#define PCI_PREF_MEMORY_BASE    0x24    /* Prefetchable memory range behind */
+#define PCI_PREF_MEMORY_LIMIT   0x26
+#define  PCI_PREF_RANGE_TYPE_MASK 0x0fUL
+#define  PCI_PREF_RANGE_TYPE_32 0x00
+#define  PCI_PREF_RANGE_TYPE_64 0x01
+#define  PCI_PREF_RANGE_MASK    (~0x0fUL)
+#define PCI_PREF_BASE_UPPER32   0x28    /* Upper half of prefetchable memory 
range */
+#define PCI_PREF_LIMIT_UPPER32  0x2c
+#define PCI_IO_BASE_UPPER16     0x30    /* Upper half of I/O addresses */
+#define PCI_IO_LIMIT_UPPER16    0x32
+/* 0x34 same as for htype 0 */
+/* 0x35-0x3b is reserved */
+#define PCI_ROM_ADDRESS1        0x38    /* Same as PCI_ROM_ADDRESS, but for 
htype 1 */
+/* 0x3c-0x3d are same as for htype 0 */
+#define PCI_BRIDGE_CONTROL      0x3e
+#define  PCI_BRIDGE_CTL_PARITY  0x01    /* Enable parity detection on 
secondary interface */
+#define  PCI_BRIDGE_CTL_SERR    0x02    /* The same for SERR forwarding */
+#define  PCI_BRIDGE_CTL_ISA     0x04    /* Enable ISA mode */
+#define  PCI_BRIDGE_CTL_VGA     0x08    /* Forward VGA addresses */
+#define  PCI_BRIDGE_CTL_MASTER_ABORT    0x20  /* Report master aborts */
+#define  PCI_BRIDGE_CTL_BUS_RESET       0x40    /* Secondary bus reset */
+#define  PCI_BRIDGE_CTL_FAST_BACK       0x80    /* Fast Back2Back enabled on 
secondary interface */
+
 #define PCI_INTEL_OPREGION 0xfc /* 4 bits */
 
 #endif /* __HVMLOADER_PCI_REGS_H__ */
-- 
1.8.4


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.