A change in QEMU on how PCI bridges are setup revealed a bug in OpenBIOS PCI setup. On Sparc64, the BARs just happened to get somewhat correct values by accident before the commit but not after the change.
Avoid to set up BARs for host bridge. Fix bridge check, this lead to setting up 6 BARs instead of more correct 2. If a bridge doesn't have any devices behind it, disable it entirely. Fix Sparc64 PCI memory base.
Signed-off-by: Blue Swirl blauwirbel@gmail.com --- arch/sparc64/openbios.c | 2 +- drivers/pci.c | 67 ++++++++++++++++++++++++++++++++++------------ drivers/pci.h | 7 +++++ 3 files changed, 57 insertions(+), 19 deletions(-)
diff --git a/arch/sparc64/openbios.c b/arch/sparc64/openbios.c index ac709fe..a1544a8 100644 --- a/arch/sparc64/openbios.c +++ b/arch/sparc64/openbios.c @@ -64,7 +64,7 @@ static const struct hwdef hwdefs[] = { .cfg_base = APB_SPECIAL_BASE, .cfg_len = 0x2000000, .host_mem_base = APB_MEM_BASE, - .pci_mem_base = 0, + .pci_mem_base = 0x10000000, .mem_len = 0x10000000, .io_base = APB_SPECIAL_BASE + 0x2000000ULL, // PCI Bus I/O space .io_len = 0x10000, diff --git a/drivers/pci.c b/drivers/pci.c index f8c6414..6ed0c03 100644 --- a/drivers/pci.c +++ b/drivers/pci.c @@ -966,11 +966,18 @@ static void ob_pci_configure_bar(pci_addr addr, pci_config_t *config, size = min_align; reloc = (reloc + size -1) & ~(size - 1); if (*io_base == base) { + PCI_DPRINTF("changing io_base from 0x%lx to 0x%x\n", + *io_base, reloc + size); *io_base = reloc + size; reloc -= arch->io_base; } else { + PCI_DPRINTF("changing mem_base from 0x%lx to 0x%x\n", + *mem_base, reloc + size); *mem_base = reloc + size; } + PCI_DPRINTF("Configuring BARs for %s: reloc 0x%x omask 0x%x " + "io_base 0x%lx mem_base 0x%lx\n", + config->path, reloc, *p_omask, *io_base, *mem_base); pci_config_write32(addr, config_addr, reloc | *p_omask); config->assigned[reg] = reloc | *p_omask; } @@ -1021,26 +1028,30 @@ ob_pci_configure(pci_addr addr, pci_config_t *config, int num_regs, int rom_bar, pci_config_write16(addr, PCI_COMMAND, cmd); }
-static void ob_configure_pci_device(const char* parent_path, - int *bus_num, unsigned long *mem_base, unsigned long *io_base, - int bus, int devnum, int fn, int *p_is_multi); +static int ob_configure_pci_device(const char* parent_path, + int *bus_num, unsigned long *mem_base, + unsigned long *io_base, int bus, int devnum, + int fn, int *p_is_multi);
-static void ob_scan_pci_bus(int *bus_num, unsigned long *mem_base, - unsigned long *io_base, const char *path, - int bus) +static int ob_scan_pci_bus(int *bus_num, unsigned long *mem_base, + unsigned long *io_base, const char *path, + int bus) { - int devnum, fn, is_multi; + int devnum, fn, is_multi, ndevices = 0;
PCI_DPRINTF("\nScanning bus %d at %s...\n", bus, path);
for (devnum = 0; devnum < 32; devnum++) { is_multi = 0; for (fn = 0; fn==0 || (is_multi && fn<8); fn++) { - ob_configure_pci_device(path, bus_num, mem_base, io_base, - bus, devnum, fn, &is_multi); + ndevices += ob_configure_pci_device(path, bus_num, + mem_base, io_base, + bus, devnum, fn, + &is_multi);
} } + return ndevices; }
static void ob_configure_pci_bridge(pci_addr addr, @@ -1048,6 +1059,9 @@ static void ob_configure_pci_bridge(pci_addr addr, unsigned long *io_base, int primary_bus, pci_config_t *config) { + int ndevices; + uint8_t command; + config->primary_bus = primary_bus; pci_config_write8(addr, PCI_PRIMARY_BUS, config->primary_bus);
@@ -1062,16 +1076,30 @@ static void ob_configure_pci_bridge(pci_addr addr,
/* make pci bridge parent device, prepare for recursion */
- ob_scan_pci_bus(bus_num, mem_base, io_base, - config->path, config->secondary_bus); + ndevices = ob_scan_pci_bus(bus_num, mem_base, io_base, + config->path, config->secondary_bus); + if (!ndevices) { + /* no devices, disable bridging */ + PCI_DPRINTF("disabling bridge %s\n", config->path); + command = pci_config_read8(addr, PCI_COMMAND); + command &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | + PCI_COMMAND_VGA_PALETTE | PCI_COMMAND_MASTER); + pci_config_write8(addr, PCI_COMMAND, command); + pci_config_write8(addr, PCI_IO_BASE, 0); + pci_config_write8(addr, PCI_IO_LIMIT, 0); + pci_config_write8(addr, PCI_MEMORY_BASE, 0); + pci_config_write8(addr, PCI_MEMORY_LIMIT, 0); + return; + }
/* bus scan updates *bus_num to last revealed pci bus number */ config->subordinate_bus = *bus_num; pci_config_write8(addr, PCI_SUBORDINATE_BUS, config->subordinate_bus);
- PCI_DPRINTF("bridge %s PCI bus primary=%d secondary=%d subordinate=%d\n", - config->path, config->primary_bus, config->secondary_bus, - config->subordinate_bus); + PCI_DPRINTF("bridge %s PCI bus primary=%d secondary=%d subordinate=%d" + " ndev=%d\n", + config->path, config->primary_bus, config->secondary_bus, + config->subordinate_bus, ndevices);
pci_set_bus_range(config); } @@ -1117,7 +1145,7 @@ static int ob_pci_read_identification(int bus, int devnum, int fn, return 1; }
-static void ob_configure_pci_device(const char* parent_path, +static int ob_configure_pci_device(const char* parent_path, int *bus_num, unsigned long *mem_base, unsigned long *io_base, int bus, int devnum, int fn, int *p_is_multi) { @@ -1133,7 +1161,7 @@ static void ob_configure_pci_device(const char* parent_path, int is_host_bridge = 0;
if (!ob_pci_read_identification(bus, devnum, fn, &vid, &did, &class, &subclass)) { - return; + return 0; }
addr = PCI_ADDR(bus, devnum, fn); @@ -1195,16 +1223,18 @@ static void ob_configure_pci_device(const char* parent_path,
if (get_property(phandle, "vendor-id", NULL)) { PCI_DPRINTF("host bridge already configured\n"); - return; + return 0; } }
activate_dev(phandle);
- if (htype & PCI_HEADER_TYPE_BRIDGE) { + if (htype & PCI_HEADER_TYPE_BRIDGE || (class == PCI_BASE_CLASS_BRIDGE)) { + PCI_DPRINTF("Bridge 2 bars, htype %x\n", htype); num_bars = 2; rom_bar = PCI_ROM_ADDRESS1; } else { + PCI_DPRINTF("Device 6 bars, htype %x\n", htype); num_bars = 6; rom_bar = PCI_ROM_ADDRESS; } @@ -1240,6 +1270,7 @@ static void ob_configure_pci_device(const char* parent_path,
ob_configure_pci_bridge(addr, bus_num, mem_base, io_base, bus, &config); } + return 1; }
int ob_pci_init(void) diff --git a/drivers/pci.h b/drivers/pci.h index 0f6ae1f..4314507 100644 --- a/drivers/pci.h +++ b/drivers/pci.h @@ -7,6 +7,8 @@ #define PCI_COMMAND 0x04 #define PCI_COMMAND_IO 0x01 #define PCI_COMMAND_MEMORY 0x02 +#define PCI_COMMAND_MASTER 0x4 /* Enable bus mastering */ +#define PCI_COMMAND_VGA_PALETTE 0x20 /* Enable palette snooping */
#define PCI_STATUS 0x06 /* 16 bits */ #define PCI_STATUS_CAP_LIST 0x10 /* Support Capability List */ @@ -44,6 +46,11 @@ #define PCI_BASE_ADDR_4 0x20 #define PCI_BASE_ADDR_5 0x24
+#define PCI_IO_BASE 0x1c /* I/O range behind the bridge */ +#define PCI_IO_LIMIT 0x1d +#define PCI_MEMORY_BASE 0x20 /* Memory range behind */ +#define PCI_MEMORY_LIMIT 0x22 + #define PCI_SUBSYSTEM_VENDOR_ID 0x2c #define PCI_SUBSYSTEM_ID 0x2e