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 <dsl...@verizon.com> CC: Don Slutz <dsl...@verizon.com> --- 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@lists.xen.org http://lists.xen.org/xen-devel