[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
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |