diff options
Diffstat (limited to 'qemu/roms/seabios/src/fw/pciinit.c')
-rw-r--r-- | qemu/roms/seabios/src/fw/pciinit.c | 964 |
1 files changed, 0 insertions, 964 deletions
diff --git a/qemu/roms/seabios/src/fw/pciinit.c b/qemu/roms/seabios/src/fw/pciinit.c deleted file mode 100644 index c31c2fa0c..000000000 --- a/qemu/roms/seabios/src/fw/pciinit.c +++ /dev/null @@ -1,964 +0,0 @@ -// Initialize PCI devices (on emulators) -// -// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> -// Copyright (C) 2006 Fabrice Bellard -// -// This file may be distributed under the terms of the GNU LGPLv3 license. - -#include "byteorder.h" // le64_to_cpu -#include "config.h" // CONFIG_* -#include "dev-q35.h" // Q35_HOST_BRIDGE_PCIEXBAR_ADDR -#include "dev-piix.h" // PIIX_* -#include "e820map.h" // e820_add -#include "hw/ata.h" // PORT_ATA1_CMD_BASE -#include "hw/pci.h" // pci_config_readl -#include "hw/pci_ids.h" // PCI_VENDOR_ID_INTEL -#include "hw/pci_regs.h" // PCI_COMMAND -#include "list.h" // struct hlist_node -#include "malloc.h" // free -#include "output.h" // dprintf -#include "paravirt.h" // RamSize -#include "romfile.h" // romfile_loadint -#include "string.h" // memset -#include "util.h" // pci_setup -#include "x86.h" // outb - -#define PCI_DEVICE_MEM_MIN (1<<12) // 4k == page size -#define PCI_BRIDGE_MEM_MIN (1<<21) // 2M == hugepage size -#define PCI_BRIDGE_IO_MIN 0x1000 // mandated by pci bridge spec - -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; -u64 pci_io_low_end = 0xa000; - -struct pci_region_entry { - struct pci_device *dev; - int bar; - u64 size; - u64 align; - int is64; - enum pci_region_type type; - struct hlist_node node; -}; - -struct pci_region { - /* pci region assignments */ - u64 base; - struct hlist_head 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 -}; - -static int dummy_pci_slot_get_irq(struct pci_device *pci, int pin) -{ - dprintf(1, "pci_slot_get_irq called with unknown routing\n"); - - return 0xff; /* PCI defined "unknown" or "no connection" for x86 */ -} - -static int (*pci_slot_get_irq)(struct pci_device *pci, int pin) = - dummy_pci_slot_get_irq; - -// Return the global irq number corresponding to a host bus device irq pin. -static int piix_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]; -} - -static int mch_pci_slot_get_irq(struct pci_device *pci, int pin) -{ - int pin_addend = 0; - while (pci->parent != NULL) { - pin_addend += pci_bdf_to_dev(pci->bdf); - pci = pci->parent; - } - u8 slot = pci_bdf_to_dev(pci->bdf); - if (slot <= 24) - /* Slots 0-24 rotate slot:pin mapping similar to piix above, but - with a different starting index - see q35-acpi-dsdt.dsl */ - return pci_irqs[(pin - 1 + pin_addend + slot) & 3]; - /* Slots 25-31 all use LNKA mapping (or LNKE, but A:D = E:H) */ - return pci_irqs[(pin - 1 + pin_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], PIIX_PORT_ELCR1); - outb(elcr[1], PIIX_PORT_ELCR2); - 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 */ -static 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); - /* PIRQ[E-H] routing */ - pci_config_writeb(bdf, ICH9_LPC_PIRQE_ROUT + i, irq); - } - 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, - 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); - - /* set root complex register block BAR */ - pci_config_writel(bdf, ICH9_LPC_RCBA, - ICH9_LPC_RCBA_ADDR | ICH9_LPC_RCBA_EN); - e820_add(ICH9_LPC_RCBA_ADDR, 16*1024, E820_RESERVED); - - acpi_pm1a_cnt = acpi_pm_base + 0x04; - pmtimer_setup(acpi_pm_base + 0x08); -} - -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); -} - -static void piix4_pm_config_setup(u16 bdf) -{ - // acpi sci is hardwired to 9 - pci_config_writeb(bdf, PCI_INTERRUPT_LINE, 9); - - pci_config_writel(bdf, PIIX_PMBASE, acpi_pm_base | 1); - pci_config_writeb(bdf, PIIX_PMREGMISC, 0x01); /* enable PM io space */ - pci_config_writel(bdf, PIIX_SMBHSTBASE, (acpi_pm_base + 0x100) | 1); - pci_config_writeb(bdf, PIIX_SMBHSTCFG, 0x09); /* enable SMBus io space */ -} - -static int PiixPmBDF = -1; - -/* PIIX4 Power Management device (for ACPI) */ -static void piix4_pm_setup(struct pci_device *pci, void *arg) -{ - PiixPmBDF = pci->bdf; - piix4_pm_config_setup(pci->bdf); - - acpi_pm1a_cnt = acpi_pm_base + 0x04; - pmtimer_setup(acpi_pm_base + 0x08); -} - -/* ICH9 SMBUS */ -/* PCI_VENDOR_ID_INTEL && PCI_DEVICE_ID_INTEL_ICH9_SMBUS */ -static 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, - (acpi_pm_base + 0x100) | 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, -}; - -void pci_resume(void) -{ - if (!CONFIG_QEMU) { - return; - } - - if (PiixPmBDF >= 0) { - piix4_pm_config_setup(PiixPmBDF); - } -} - -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); - /* enable SERR# for forwarding */ - if (pci->header_type & PCI_HEADER_TYPE_BRIDGE) - pci_config_maskw(bdf, PCI_BRIDGE_CONTROL, 0, - PCI_BRIDGE_CTL_SERR); -} - -static void pci_bios_init_devices(void) -{ - struct pci_device *pci; - foreachpci(pci) { - pci_bios_init_device(pci); - } -} - -static void pci_enable_default_vga(void) -{ - struct pci_device *pci; - - foreachpci(pci) { - if (is_pci_vga(pci)) { - dprintf(1, "PCI: Using %02x:%02x.%x for primary VGA\n", - pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf), - pci_bdf_to_fn(pci->bdf)); - return; - } - } - - pci = pci_find_class(PCI_CLASS_DISPLAY_VGA); - if (!pci) { - dprintf(1, "PCI: No VGA devices found\n"); - return; - } - - dprintf(1, "PCI: Enabling %02x:%02x.%x for primary VGA\n", - pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf), - pci_bdf_to_fn(pci->bdf)); - - pci_config_maskw(pci->bdf, PCI_COMMAND, 0, - PCI_COMMAND_IO | PCI_COMMAND_MEMORY); - - while (pci->parent) { - pci = pci->parent; - - dprintf(1, "PCI: Setting VGA enable on bridge %02x:%02x.%x\n", - pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf), - pci_bdf_to_fn(pci->bdf)); - - pci_config_maskw(pci->bdf, PCI_BRIDGE_CONTROL, 0, PCI_BRIDGE_CTL_VGA); - pci_config_maskw(pci->bdf, PCI_COMMAND, 0, - PCI_COMMAND_IO | PCI_COMMAND_MEMORY); - } -} - -/**************************************************************** - * Platform device initialization - ****************************************************************/ - -static void i440fx_mem_addr_setup(struct pci_device *dev, void *arg) -{ - if (RamSize <= 0x80000000) - pcimem_start = 0x80000000; - else if (RamSize <= 0xc0000000) - pcimem_start = 0xc0000000; - - pci_slot_get_irq = piix_pci_slot_get_irq; -} - -static 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); - e820_add(addr, size, E820_RESERVED); - - /* setup pci i/o window (above mmconfig) */ - pcimem_start = addr + size; - - pci_slot_get_irq = mch_pci_slot_get_irq; - - /* setup io address space */ - if (acpi_pm_base < 0x1000) - pci_io_low_end = 0x10000; - else - pci_io_low_end = acpi_pm_base; -} - -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 extraroots = romfile_loadint("etc/extra-pci-roots", 0); - u8 pci_bus = 0; - - pci_bios_init_bus_rec(0 /* host bus */, &pci_bus); - - if (extraroots) { - while (pci_bus < 0xff) { - pci_bus++; - pci_bios_init_bus_rec(pci_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; - hlist_for_each_entry(entry, &r->list, node) { - if (!entry->is64) - return 0; - } - return 1; -} - -static u64 pci_region_align(struct pci_region *r) -{ - struct pci_region_entry *entry; - hlist_for_each_entry(entry, &r->list, node) { - // The first entry in the sorted list has the largest alignment - return entry->align; - } - return 1; -} - -static u64 pci_region_sum(struct pci_region *r) -{ - u64 sum = 0; - struct pci_region_entry *entry; - hlist_for_each_entry(entry, &r->list, node) { - sum += entry->size; - } - return sum; -} - -static void pci_region_migrate_64bit_entries(struct pci_region *from, - struct pci_region *to) -{ - struct hlist_node *n, **last = &to->list.first; - struct pci_region_entry *entry; - hlist_for_each_entry_safe(entry, n, &from->list, node) { - if (!entry->is64) - continue; - if (entry->dev->class == PCI_CLASS_SERIAL_USB) - continue; - // Move from source list to destination list. - hlist_del(&entry->node); - hlist_add(&entry->node, last); - last = &entry->node.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 hlist_node **pprev; - struct pci_region_entry *pos; - hlist_for_each_entry_pprev(pos, pprev, &bus->r[type].list, node) { - if (pos->align < align || (pos->align == align && pos->size < size)) - break; - } - hlist_add(&entry->node, pprev); - return entry; -} - -static int pci_bus_hotplug_support(struct pci_bus *bus, u8 pcie_cap) -{ - u8 shpc_cap; - - if (pcie_cap) { - u16 pcie_flags = pci_config_readw(bus->bus_dev->bdf, - pcie_cap + PCI_EXP_FLAGS); - u8 port_type = ((pcie_flags & PCI_EXP_FLAGS_TYPE) >> - (__builtin_ffs(PCI_EXP_FLAGS_TYPE) - 1)); - u8 downstream_port = (port_type == PCI_EXP_TYPE_DOWNSTREAM) || - (port_type == PCI_EXP_TYPE_ROOT_PORT); - /* - * PCI Express SPEC, 7.8.2: - * Slot Implemented – When Set, this bit indicates that the Link - * HwInit associated with this Port is connected to a slot (as - * compared to being connected to a system-integrated device or - * being disabled). - * This bit is valid for Downstream Ports. This bit is undefined - * for Upstream Ports. - */ - u16 slot_implemented = pcie_flags & PCI_EXP_FLAGS_SLOT; - - return downstream_port && slot_implemented; - } - - shpc_cap = pci_find_capability(bus->bus_dev, PCI_CAP_ID_SHPC, 0); - return !!shpc_cap; -} - -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)]; - if (!bus->bus_dev) - /* - * Resources for all root busses go in busses[0] - */ - bus = &busses[0]; - 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)]; - if (!parent->bus_dev) - /* - * Resources for all root busses go in busses[0] - */ - parent = &busses[0]; - int type; - u8 pcie_cap = pci_find_capability(s->bus_dev, PCI_CAP_ID_EXP, 0); - int hotplug_support = pci_bus_hotplug_support(s, pcie_cap); - 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_bridge_has_region(s->bus_dev, type)) - continue; - if (pci_region_align(&s->r[type]) > align) - align = pci_region_align(&s->r[type]); - u64 sum = pci_region_sum(&s->r[type]); - int resource_optional = pcie_cap && (type == PCI_REGION_TYPE_IO); - if (!sum && hotplug_support && !resource_optional) - sum = align; /* reserve min size for hot-plug */ - 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_io(struct pci_bus *bus) -{ - /* - * QEMU I/O address space usage: - * 0000 - 0fff legacy isa, pci config, pci root bus, ... - * 1000 - 9fff free - * a000 - afff hotplug (cpu, pci via acpi, i440fx/piix only) - * b000 - bfff power management (PORT_ACPI_PM_BASE) - * [ qemu 1.4+ implements pci config registers - * properly so guests can place the registers - * where they want, on older versions its fixed ] - * c000 - ffff free, traditionally used for pci io - */ - struct pci_region *r_io = &bus->r[PCI_REGION_TYPE_IO]; - u64 sum = pci_region_sum(r_io); - if (sum < 0x4000) { - /* traditional region is big enougth, use it */ - r_io->base = 0xc000; - } else if (sum < pci_io_low_end - 0x1000) { - /* use the larger region at 0x1000 */ - r_io->base = 0x1000; - } else { - /* not enouth io address space -> error out */ - return -1; - } - dprintf(1, "PCI: IO: %4llx - %4llx\n", r_io->base, r_io->base + sum - 1); - return 0; -} - -static int pci_bios_init_root_regions_mem(struct pci_bus *bus) -{ - 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 hlist_node *n; - struct pci_region_entry *entry; - hlist_for_each_entry_safe(entry, n, &r->list, node) { - 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); - hlist_del(&entry->node); - free(entry); - } -} - -static void pci_bios_map_devices(struct pci_bus *busses) -{ - if (pci_bios_init_root_regions_io(busses)) - panic("PCI: out of I/O address space\n"); - - dprintf(1, "PCI: 32: %016llx - %016llx\n", pcimem_start, pcimem_end); - if (pci_bios_init_root_regions_mem(busses)) { - struct pci_region r64_mem, r64_pref; - r64_mem.list.first = NULL; - r64_pref.list.first = 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_mem(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 = le64_to_cpu(romfile_loadint("etc/reserved-memory-end", 0)); - if (r64_mem.base < 0x100000000LL + RamSizeOver4G) - r64_mem.base = 0x100000000LL + RamSizeOver4G; - r64_mem.base = ALIGN(r64_mem.base, align_mem); - r64_mem.base = ALIGN(r64_mem.base, (1LL<<30)); // 1G hugepage - r64_pref.base = r64_mem.base + sum_mem; - r64_pref.base = ALIGN(r64_pref.base, align_pref); - r64_pref.base = ALIGN(r64_pref.base, (1LL<<30)); // 1G hugepage - pcimem64_start = r64_mem.base; - pcimem64_end = r64_pref.base + sum_pref; - pcimem64_end = ALIGN(pcimem64_end, (1LL<<30)); // 1G hugepage - dprintf(1, "PCI: 64: %016llx - %016llx\n", pcimem64_start, pcimem64_end); - - 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); - - pci_enable_default_vga(); -} |