// Initialize PCI devices (on emulators) // // Copyright (C) 2008 Kevin O'Connor // Copyright (C) 2006 Fabrice Bellard // // This file may be distributed under the terms of the GNU LGPLv3 license. #include "util.h" // dprintf #include "pci.h" // pci_config_readl #include "pci_ids.h" // PCI_VENDOR_ID_INTEL #include "pci_regs.h" // PCI_COMMAND #include "ioport.h" // PORT_ATA1_CMD_BASE #include "config.h" // CONFIG_* #include "memmap.h" // add_e820 #include "dev-q35.h" /* PM Timer ticks per second (HZ) */ #define PM_TIMER_FREQUENCY 3579545 #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", }; u64 pcimem_start = BUILD_PCIMEM_START; u64 pcimem_end = BUILD_PCIMEM_END; u64 pcimem64_start = BUILD_PCIMEM64_START; u64 pcimem64_end = BUILD_PCIMEM64_END; struct pci_region_entry { struct pci_device *dev; int bar; u64 size; u64 align; int is64; enum pci_region_type type; struct pci_region_entry *next; }; struct pci_region { /* pci region assignments */ u64 base; struct pci_region_entry *list; }; struct pci_bus { struct pci_region r[PCI_REGION_TYPE_COUNT]; struct pci_device *bus_dev; }; static u32 pci_bar(struct pci_device *pci, int region_num) { if (region_num != PCI_ROM_SLOT) { return PCI_BASE_ADDRESS_0 + region_num * 4; } #define PCI_HEADER_TYPE_MULTI_FUNCTION 0x80 u8 type = pci->header_type & ~PCI_HEADER_TYPE_MULTI_FUNCTION; 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, u64 addr, int is64) { u32 ofs = pci_bar(pci, bar); pci_config_writel(pci->bdf, ofs, addr); if (is64) pci_config_writel(pci->bdf, ofs + 4, addr >> 32); } /**************************************************************** * Misc. device init ****************************************************************/ /* host irqs corresponding to PCI irqs A-D */ const u8 pci_irqs[4] = { 10, 10, 11, 11 }; // Return the global irq number corresponding to a host bus device irq pin. static int pci_slot_get_irq(struct pci_device *pci, int pin) { int slot_addend = 0; while (pci->parent != NULL) { slot_addend += pci_bdf_to_dev(pci->bdf); pci = pci->parent; } slot_addend += pci_bdf_to_dev(pci->bdf) - 1; return pci_irqs[(pin - 1 + slot_addend) & 3]; } /* PIIX3/PIIX4 PCI to ISA bridge */ static void piix_isa_bridge_setup(struct pci_device *pci, void *arg) { int i, irq; u8 elcr[2]; elcr[0] = 0x00; elcr[1] = 0x00; for (i = 0; i < 4; i++) { irq = pci_irqs[i]; /* set to trigger level */ elcr[irq >> 3] |= (1 << (irq & 7)); /* activate irq remapping in PIIX */ pci_config_writeb(pci->bdf, 0x60 + i, irq); } outb(elcr[0], 0x4d0); outb(elcr[1], 0x4d1); dprintf(1, "PIIX3/PIIX4 init: elcr=%02x %02x\n", elcr[0], elcr[1]); } /* ICH9 LPC PCI to ISA bridge */ /* PCI_VENDOR_ID_INTEL && PCI_DEVICE_ID_INTEL_ICH9_LPC */ void mch_isa_bridge_setup(struct pci_device *dev, void *arg) { u16 bdf = dev->bdf; int i, irq; u8 elcr[2]; elcr[0] = 0x00; elcr[1] = 0x00; for (i = 0; i < 4; i++) { irq = pci_irqs[i]; /* set to trigger level */ elcr[irq >> 3] |= (1 << (irq & 7)); /* activate irq remapping in LPC */ /* PIRQ[A-D] routing */ pci_config_writeb(bdf, ICH9_LPC_PIRQA_ROUT + i, irq | ICH9_LPC_PIRQ_ROUT_IRQEN); /* PIRQ[E-H] routing */ pci_config_writeb(bdf, ICH9_LPC_PIRQE_ROUT + i, irq | ICH9_LPC_PIRQ_ROUT_IRQEN); } outb(elcr[0], ICH9_LPC_PORT_ELCR1); outb(elcr[1], ICH9_LPC_PORT_ELCR2); dprintf(1, "Q35 LPC init: elcr=%02x %02x\n", elcr[0], elcr[1]); /* pm io base */ pci_config_writel(bdf, ICH9_LPC_PMBASE, PORT_ACPI_PM_BASE | ICH9_LPC_PMBASE_RTE); /* acpi enable, SCI: IRQ9 000b = irq9*/ pci_config_writeb(bdf, ICH9_LPC_ACPI_CTRL, ICH9_LPC_ACPI_CTRL_ACPI_EN); pmtimer_setup(PORT_ACPI_PM_BASE + 0x08, PM_TIMER_FREQUENCY / 1000); } static void storage_ide_setup(struct pci_device *pci, void *arg) { /* IDE: we map it as in ISA mode */ pci_set_io_region_addr(pci, 0, PORT_ATA1_CMD_BASE, 0); pci_set_io_region_addr(pci, 1, PORT_ATA1_CTRL_BASE, 0); pci_set_io_region_addr(pci, 2, PORT_ATA2_CMD_BASE, 0); pci_set_io_region_addr(pci, 3, PORT_ATA2_CTRL_BASE, 0); } /* PIIX3/PIIX4 IDE */ static void piix_ide_setup(struct pci_device *pci, void *arg) { u16 bdf = pci->bdf; pci_config_writew(bdf, 0x40, 0x8000); // enable IDE0 pci_config_writew(bdf, 0x42, 0x8000); // enable IDE1 } static void pic_ibm_setup(struct pci_device *pci, void *arg) { /* PIC, IBM, MPIC & MPIC2 */ pci_set_io_region_addr(pci, 0, 0x80800000 + 0x00040000, 0); } static void apple_macio_setup(struct pci_device *pci, void *arg) { /* macio bridge */ pci_set_io_region_addr(pci, 0, 0x80800000, 0); } /* PIIX4 Power Management device (for ACPI) */ static void piix4_pm_setup(struct pci_device *pci, void *arg) { u16 bdf = pci->bdf; // acpi sci is hardwired to 9 pci_config_writeb(bdf, PCI_INTERRUPT_LINE, 9); pci_config_writel(bdf, 0x40, PORT_ACPI_PM_BASE | 1); pci_config_writeb(bdf, 0x80, 0x01); /* enable PM io space */ pci_config_writel(bdf, 0x90, PORT_SMB_BASE | 1); pci_config_writeb(bdf, 0xd2, 0x09); /* enable SMBus io space */ pmtimer_setup(PORT_ACPI_PM_BASE + 0x08, PM_TIMER_FREQUENCY / 1000); } /* ICH9 SMBUS */ /* PCI_VENDOR_ID_INTEL && PCI_DEVICE_ID_INTEL_ICH9_SMBUS */ void ich9_smbus_setup(struct pci_device *dev, void *arg) { u16 bdf = dev->bdf; /* map smbus into io space */ pci_config_writel(bdf, ICH9_SMB_SMB_BASE, PORT_SMB_BASE | PCI_BASE_ADDRESS_SPACE_IO); /* enable SMBus */ pci_config_writeb(bdf, ICH9_SMB_HOSTC, ICH9_SMB_HOSTC_HST_EN); } static const struct pci_device_id pci_device_tbl[] = { /* PIIX3/PIIX4 PCI to ISA bridge */ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_0, piix_isa_bridge_setup), PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_0, piix_isa_bridge_setup), PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_LPC, mch_isa_bridge_setup), /* STORAGE IDE */ PCI_DEVICE_CLASS(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_1, PCI_CLASS_STORAGE_IDE, piix_ide_setup), PCI_DEVICE_CLASS(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB, PCI_CLASS_STORAGE_IDE, piix_ide_setup), PCI_DEVICE_CLASS(PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_IDE, storage_ide_setup), /* PIC, IBM, MIPC & MPIC2 */ PCI_DEVICE_CLASS(PCI_VENDOR_ID_IBM, 0x0046, PCI_CLASS_SYSTEM_PIC, pic_ibm_setup), PCI_DEVICE_CLASS(PCI_VENDOR_ID_IBM, 0xFFFF, PCI_CLASS_SYSTEM_PIC, pic_ibm_setup), /* PIIX4 Power Management device (for ACPI) */ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, piix4_pm_setup), PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_SMBUS, ich9_smbus_setup), /* 0xff00 */ PCI_DEVICE_CLASS(PCI_VENDOR_ID_APPLE, 0x0017, 0xff00, apple_macio_setup), PCI_DEVICE_CLASS(PCI_VENDOR_ID_APPLE, 0x0022, 0xff00, apple_macio_setup), PCI_DEVICE_END, }; static void pci_bios_init_device(struct pci_device *pci) { u16 bdf = pci->bdf; dprintf(1, "PCI: init bdf=%02x:%02x.%x id=%04x:%04x\n" , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), pci_bdf_to_fn(bdf) , pci->vendor, pci->device); /* map the interrupt */ int pin = pci_config_readb(bdf, PCI_INTERRUPT_PIN); if (pin != 0) pci_config_writeb(bdf, PCI_INTERRUPT_LINE, pci_slot_get_irq(pci, pin)); pci_init_device(pci_device_tbl, pci, NULL); /* enable memory mappings */ pci_config_maskw(bdf, PCI_COMMAND, 0, PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_SERR); } static void pci_bios_init_devices(void) { struct pci_device *pci; foreachpci(pci) { pci_bios_init_device(pci); } } /**************************************************************** * Platform device initialization ****************************************************************/ void i440fx_mem_addr_setup(struct pci_device *dev, void *arg) { if (RamSize <= 0x80000000) pcimem_start = 0x80000000; else if (RamSize <= 0xc0000000) pcimem_start = 0xc0000000; } void mch_mem_addr_setup(struct pci_device *dev, void *arg) { u64 addr = Q35_HOST_BRIDGE_PCIEXBAR_ADDR; u32 size = Q35_HOST_BRIDGE_PCIEXBAR_SIZE; /* setup mmconfig */ u16 bdf = dev->bdf; u32 upper = addr >> 32; u32 lower = (addr & 0xffffffff) | Q35_HOST_BRIDGE_PCIEXBAREN; pci_config_writel(bdf, Q35_HOST_BRIDGE_PCIEXBAR, 0); pci_config_writel(bdf, Q35_HOST_BRIDGE_PCIEXBAR + 4, upper); pci_config_writel(bdf, Q35_HOST_BRIDGE_PCIEXBAR, lower); add_e820(addr, size, E820_RESERVED); /* setup pci i/o window (above mmconfig) */ pcimem_start = addr + size; } static const struct pci_device_id pci_platform_tbl[] = { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82441, i440fx_mem_addr_setup), PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_Q35_MCH, mch_mem_addr_setup), PCI_DEVICE_END }; static void pci_bios_init_platform(void) { struct pci_device *pci; foreachpci(pci) { pci_init_device(pci_platform_tbl, pci, NULL); } } /**************************************************************** * Bus initialization ****************************************************************/ static void pci_bios_init_bus_rec(int bus, u8 *pci_bus) { int bdf; u16 class; dprintf(1, "PCI: %s bus = 0x%x\n", __func__, bus); /* prevent accidental access to unintended devices */ foreachbdf(bdf, bus) { class = pci_config_readw(bdf, PCI_CLASS_DEVICE); if (class == PCI_CLASS_BRIDGE_PCI) { pci_config_writeb(bdf, PCI_SECONDARY_BUS, 255); pci_config_writeb(bdf, PCI_SUBORDINATE_BUS, 0); } } foreachbdf(bdf, bus) { class = pci_config_readw(bdf, PCI_CLASS_DEVICE); if (class != PCI_CLASS_BRIDGE_PCI) { continue; } dprintf(1, "PCI: %s bdf = 0x%x\n", __func__, bdf); u8 pribus = pci_config_readb(bdf, PCI_PRIMARY_BUS); if (pribus != bus) { dprintf(1, "PCI: primary bus = 0x%x -> 0x%x\n", pribus, bus); pci_config_writeb(bdf, PCI_PRIMARY_BUS, bus); } else { dprintf(1, "PCI: primary bus = 0x%x\n", pribus); } u8 secbus = pci_config_readb(bdf, PCI_SECONDARY_BUS); (*pci_bus)++; if (*pci_bus != secbus) { dprintf(1, "PCI: secondary bus = 0x%x -> 0x%x\n", secbus, *pci_bus); secbus = *pci_bus; pci_config_writeb(bdf, PCI_SECONDARY_BUS, secbus); } else { dprintf(1, "PCI: secondary bus = 0x%x\n", secbus); } /* set to max for access to all subordinate buses. later set it to accurate value */ u8 subbus = pci_config_readb(bdf, PCI_SUBORDINATE_BUS); pci_config_writeb(bdf, PCI_SUBORDINATE_BUS, 255); pci_bios_init_bus_rec(secbus, pci_bus); if (subbus != *pci_bus) { dprintf(1, "PCI: subordinate bus = 0x%x -> 0x%x\n", subbus, *pci_bus); subbus = *pci_bus; } else { dprintf(1, "PCI: subordinate bus = 0x%x\n", subbus); } pci_config_writeb(bdf, PCI_SUBORDINATE_BUS, subbus); } } static void pci_bios_init_bus(void) { u8 pci_bus = 0; pci_bios_init_bus_rec(0 /* host bus */, &pci_bus); } /**************************************************************** * Bus sizing ****************************************************************/ static void pci_bios_get_bar(struct pci_device *pci, int bar, int *ptype, u64 *psize, int *pis64) { u32 ofs = pci_bar(pci, bar); u16 bdf = pci->bdf; u32 old = pci_config_readl(bdf, ofs); int is64 = 0, type = PCI_REGION_TYPE_MEM; u64 mask; if (bar == PCI_ROM_SLOT) { mask = PCI_ROM_ADDRESS_MASK; pci_config_writel(bdf, 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_config_writel(bdf, ofs, ~0); } u64 val = pci_config_readl(bdf, ofs); pci_config_writel(bdf, ofs, old); if (is64) { u32 hold = pci_config_readl(bdf, ofs + 4); pci_config_writel(bdf, ofs + 4, ~0); u32 high = pci_config_readl(bdf, ofs + 4); pci_config_writel(bdf, ofs + 4, hold); val |= ((u64)high << 32); mask |= ((u64)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) { if (type != PCI_REGION_TYPE_PREFMEM) return 0; u32 pmem = pci_config_readl(pci->bdf, PCI_PREF_MEMORY_BASE); if (!pmem) { pci_config_writel(pci->bdf, PCI_PREF_MEMORY_BASE, 0xfff0fff0); pmem = pci_config_readl(pci->bdf, PCI_PREF_MEMORY_BASE); pci_config_writel(pci->bdf, PCI_PREF_MEMORY_BASE, 0x0); } if ((pmem & PCI_PREF_RANGE_TYPE_MASK) != PCI_PREF_RANGE_TYPE_64) return 0; struct pci_region_entry *entry = r->list; while (entry) { if (!entry->is64) return 0; entry = entry->next; } return 1; } static u64 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 u64 pci_region_sum(struct pci_region *r) { struct pci_region_entry *entry = r->list; u64 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 struct pci_region_entry * pci_region_create_entry(struct pci_bus *bus, struct pci_device *dev, int bar, u64 size, u64 align, int type, int is64) { struct pci_region_entry *entry = malloc_tmp(sizeof(*entry)); if (!entry) { warn_noalloc(); return NULL; } 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. struct pci_region_entry **pprev; 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; return entry; } static int pci_bios_check_devices(struct pci_bus *busses) { dprintf(1, "PCI: check devices\n"); // Calculate resources needed for regular (non-bus) devices. struct pci_device *pci; foreachpci(pci) { if (pci->class == PCI_CLASS_BRIDGE_PCI) busses[pci->secondary_bus].bus_dev = pci; struct pci_bus *bus = &busses[pci_bdf_to_bus(pci->bdf)]; int i; for (i = 0; i < PCI_NUM_REGIONS; i++) { if ((pci->class == PCI_CLASS_BRIDGE_PCI) && (i >= PCI_BRIDGE_NUM_REGIONS && i < PCI_ROM_SLOT)) continue; int type, is64; u64 size; 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; struct pci_region_entry *entry = pci_region_create_entry( bus, pci, i, size, size, type, is64); if (!entry) return -1; if (is64) i++; } } // Propagate required bus resources to parent busses. int secondary_bus; for (secondary_bus=MaxPCIBus; secondary_bus>0; secondary_bus--) { struct pci_bus *s = &busses[secondary_bus]; if (!s->bus_dev) continue; struct pci_bus *parent = &busses[pci_bdf_to_bus(s->bus_dev->bdf)]; int type; for (type = 0; type < PCI_REGION_TYPE_COUNT; type++) { u64 align = (type == PCI_REGION_TYPE_IO) ? PCI_BRIDGE_IO_MIN : PCI_BRIDGE_MEM_MIN; if (pci_region_align(&s->r[type]) > align) align = pci_region_align(&s->r[type]); u64 sum = pci_region_sum(&s->r[type]); u64 size = ALIGN(sum, align); int is64 = pci_bios_bridge_region_is64(&s->r[type], s->bus_dev, type); // entry->bar is -1 if the entry represents a bridge region struct pci_region_entry *entry = pci_region_create_entry( parent, s->bus_dev, -1, size, align, type, is64); if (!entry) return -1; dprintf(1, "PCI: secondary bus %d size %08llx type %s\n", entry->dev->secondary_bus, size, region_type_name[entry->type]); } } return 0; } /**************************************************************** * BAR assignment ****************************************************************/ // Setup region bases (given the regions' size and alignment) static int pci_bios_init_root_regions(struct pci_bus *bus) { bus->r[PCI_REGION_TYPE_IO].base = 0xc000; 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]; } u64 sum = pci_region_sum(r_end); u64 align = pci_region_align(r_end); r_end->base = ALIGN_DOWN((pcimem_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); if ((r_start->base < pcimem_start) || (r_start->base > pcimem_end)) // Memory range requested is larger than available. return -1; return 0; } #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, u64 addr) { u16 bdf = entry->dev->bdf; if (entry->bar >= 0) { dprintf(1, "PCI: map device bdf=%02x:%02x.%x" " bar %d, addr %08llx, size %08llx [%s]\n", pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), pci_bdf_to_fn(bdf), entry->bar, addr, entry->size, region_type_name[entry->type]); pci_set_io_region_addr(entry->dev, entry->bar, addr, entry->is64); return; } u64 limit = addr + entry->size - 1; if (entry->type == PCI_REGION_TYPE_IO) { pci_config_writeb(bdf, PCI_IO_BASE, addr >> PCI_IO_SHIFT); pci_config_writew(bdf, PCI_IO_BASE_UPPER16, 0); pci_config_writeb(bdf, PCI_IO_LIMIT, limit >> PCI_IO_SHIFT); pci_config_writew(bdf, PCI_IO_LIMIT_UPPER16, 0); } if (entry->type == PCI_REGION_TYPE_MEM) { pci_config_writew(bdf, PCI_MEMORY_BASE, addr >> PCI_MEMORY_SHIFT); pci_config_writew(bdf, PCI_MEMORY_LIMIT, limit >> PCI_MEMORY_SHIFT); } if (entry->type == PCI_REGION_TYPE_PREFMEM) { pci_config_writew(bdf, PCI_PREF_MEMORY_BASE, addr >> PCI_PREF_MEMORY_SHIFT); pci_config_writew(bdf, PCI_PREF_MEMORY_LIMIT, limit >> PCI_PREF_MEMORY_SHIFT); pci_config_writel(bdf, PCI_PREF_BASE_UPPER32, addr >> 32); pci_config_writel(bdf, 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) { u64 addr = r->base; 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); struct pci_region_entry *next = entry->next; free(entry); entry = next; } } static void pci_bios_map_devices(struct pci_bus *busses) { if (pci_bios_init_root_regions(busses)) { struct pci_region r64_mem, r64_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)) panic("PCI: out of 32bit address space\n"); u64 sum_mem = pci_region_sum(&r64_mem); u64 sum_pref = pci_region_sum(&r64_pref); u64 align_mem = pci_region_align(&r64_mem); u64 align_pref = pci_region_align(&r64_pref); r64_mem.base = ALIGN(0x100000000LL + RamSizeOver4G, align_mem); r64_pref.base = ALIGN(r64_mem.base + sum_mem, align_pref); pcimem64_start = r64_mem.base; pcimem64_end = r64_pref.base + sum_pref; pci_region_map_entries(busses, &r64_mem); pci_region_map_entries(busses, &r64_pref); } else { // no bars mapped high -> drop 64bit window (see dsdt) pcimem64_start = 0; } // Map regions on each device. int bus; 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) { if (!CONFIG_QEMU) return; dprintf(3, "pci setup\n"); dprintf(1, "=== PCI bus & bridge init ===\n"); if (pci_probe_host() != 0) { return; } pci_bios_init_bus(); dprintf(1, "=== PCI device probing ===\n"); pci_probe_devices(); pcimem_start = RamSize; pci_bios_init_platform(); dprintf(1, "=== PCI new allocation pass #1 ===\n"); struct pci_bus *busses = malloc_tmp(sizeof(*busses) * (MaxPCIBus + 1)); if (!busses) { warn_noalloc(); return; } memset(busses, 0, sizeof(*busses) * (MaxPCIBus + 1)); if (pci_bios_check_devices(busses)) return; dprintf(1, "=== PCI new allocation pass #2 ===\n"); pci_bios_map_devices(busses); pci_bios_init_devices(); free(busses); }