Hi,
New revision of my 64bit pci patches. This is what I'm using to play with 64bit PCI bars (ivshmem + qxl devices) and bridge support (patches from mst).
Changes in v2: * tried to reduce the stack footprint of 64bit hex number printing. * changed patch splitting to make them (hopefully) more readable. * full support for bridges with 64bit memory windows, even nested.
Also availabe from: git://git.kraxel.org/seabios pci64
enjoy, Gerd
Gerd Hoffmann (6): output: add 64bit hex print support pci: split device discovery into multiple steps pci: 64bit support. pci: bridges can have two regions too pci: fix bridge ressource allocation. pci: add prefmem64
src/acpi-dsdt.dsl | 7 ++ src/acpi-dsdt.hex | 72 ++++++++++++--- src/config.h | 2 + src/output.c | 23 ++++- src/pci.h | 14 ++- src/pciinit.c | 272 ++++++++++++++++++++++++++++++++++++++--------------- 6 files changed, 294 insertions(+), 96 deletions(-)
--- src/output.c | 23 +++++++++++++++++++++-- 1 files changed, 21 insertions(+), 2 deletions(-)
diff --git a/src/output.c b/src/output.c index bdde7cc..32cbcaa 100644 --- a/src/output.c +++ b/src/output.c @@ -249,6 +249,7 @@ bvprintf(struct putcinfo *action, const char *fmt, va_list args) const char *n = s+1; int field_width = 0; int spacepad = 1; + int is64 = 0; for (;;) { c = GET_GLOBAL(*(u8*)n); if (!isdigit(c)) @@ -264,6 +265,11 @@ bvprintf(struct putcinfo *action, const char *fmt, va_list args) n++; c = GET_GLOBAL(*(u8*)n); } + if (c == 'l') { + is64 = 1; + n++; + c = GET_GLOBAL(*(u8*)n); + } s32 val; const char *sarg; switch (c) { @@ -289,8 +295,21 @@ bvprintf(struct putcinfo *action, const char *fmt, va_list args) field_width = 8; spacepad = 0; case 'x': - val = va_arg(args, s32); - puthex(action, val, field_width, spacepad); + if (is64) { + val = va_arg(args, s32); + u32 upper = va_arg(args, s32); + if (upper) { + if (field_width < 8) + field_width = 8; + puthex(action, upper, field_width - 8, spacepad); + puthex(action, val, 8, 0); + } else { + puthex(action, val, field_width, spacepad); + } + } else { + val = va_arg(args, s32); + puthex(action, val, field_width, spacepad); + } break; case 'c': val = va_arg(args, int);
On Wed, Feb 29, 2012 at 12:45:05PM +0100, Gerd Hoffmann wrote:
src/output.c | 23 +++++++++++++++++++++-- 1 files changed, 21 insertions(+), 2 deletions(-)
Thanks Gerd.
I think your patch missed a couple of corner cases. I played with it a bit and came up with the below.
-Kevin
From ab16772e7f51e5d9c8e0c44695a76320439d2c78 Mon Sep 17 00:00:00 2001
From: Kevin O'Connor kevin@koconnor.net Date: Mon, 5 Mar 2012 10:14:07 -0500 Subject: [PATCH] output: Add 64bit hex print support. To: seabios@seabios.org
Based on patch by Gerd Hoffmann kraxel@redhat.com. Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/memmap.c | 6 +--- src/output.c | 68 +++++++++++++++++++++++++++++++++++----------------------- src/post.c | 3 +- 3 files changed, 44 insertions(+), 33 deletions(-)
diff --git a/src/memmap.c b/src/memmap.c index 20ccae0..bcec34e 100644 --- a/src/memmap.c +++ b/src/memmap.c @@ -63,10 +63,8 @@ dump_map(void) for (i=0; i<e820_count; i++) { struct e820entry *e = &e820_list[i]; u64 e_end = e->start + e->size; - dprintf(1, " %d: %08x%08x - %08x%08x = %d %s\n", i - , (u32)(e->start >> 32), (u32)e->start - , (u32)(e_end >> 32), (u32)e_end - , e->type, e820_type_name(e->type)); + dprintf(1, " %d: %016llx - %016llx = %d %s\n", i + , e->start, e_end, e->type, e820_type_name(e->type)); } }
diff --git a/src/output.c b/src/output.c index bdde7cc..73d80e9 100644 --- a/src/output.c +++ b/src/output.c @@ -194,28 +194,10 @@ putsinglehex(struct putcinfo *action, u32 val) putc(action, val); }
-// Output an integer in hexadecimal. +// Output an integer in hexadecimal with a specified width. static void -puthex(struct putcinfo *action, u32 val, int width, int spacepad) -{ - if (!width) { - u32 tmp = val; - width = 1; - while (tmp >>= 4) - width++; - } else if (spacepad) { - u32 tmp = val; - u32 count = 1; - while (tmp >>= 4) - count++; - if (width > count) { - count = width - count; - width -= count; - while (count--) - putc(action, ' '); - } - } - +puthex(struct putcinfo *action, u32 val, int width) +{ switch (width) { default: putsinglehex(action, (val >> 28) & 0xf); case 7: putsinglehex(action, (val >> 24) & 0xf); @@ -228,6 +210,20 @@ puthex(struct putcinfo *action, u32 val, int width, int spacepad) } }
+// Output an integer in hexadecimal with a minimum width. +static void +putprettyhex(struct putcinfo *action, u32 val, int width, int spacepad) +{ + u32 tmp = val; + int count = 1; + while (tmp >>= 4) + count++; + width -= count; + while (width-- > 0) + putc(action, spacepad ? ' ' : '0'); + puthex(action, val, count); +} + static inline int isdigit(u8 c) { @@ -249,6 +245,7 @@ bvprintf(struct putcinfo *action, const char *fmt, va_list args) const char *n = s+1; int field_width = 0; int spacepad = 1; + int is64 = 0; for (;;) { c = GET_GLOBAL(*(u8*)n); if (!isdigit(c)) @@ -264,6 +261,11 @@ bvprintf(struct putcinfo *action, const char *fmt, va_list args) n++; c = GET_GLOBAL(*(u8*)n); } + if (c == 'l') { + is64 = 1; + n++; + c = GET_GLOBAL(*(u8*)n); + } s32 val; const char *sarg; switch (c) { @@ -272,6 +274,8 @@ bvprintf(struct putcinfo *action, const char *fmt, va_list args) break; case 'd': val = va_arg(args, s32); + if (is64) + va_arg(args, s32); if (val < 0) { putc(action, '-'); val = -val; @@ -280,17 +284,27 @@ bvprintf(struct putcinfo *action, const char *fmt, va_list args) break; case 'u': val = va_arg(args, s32); + if (is64) + va_arg(args, s32); putuint(action, val); break; case 'p': - /* %p always has 0x prepended */ + val = va_arg(args, s32); putc(action, '0'); putc(action, 'x'); - field_width = 8; - spacepad = 0; + puthex(action, val, 8); + break; case 'x': val = va_arg(args, s32); - puthex(action, val, field_width, spacepad); + if (is64) { + u32 upper = va_arg(args, s32); + if (upper) { + putprettyhex(action, upper, field_width - 8, spacepad); + puthex(action, val, 8); + break; + } + } + putprettyhex(action, val, field_width, spacepad); break; case 'c': val = va_arg(args, int); @@ -342,7 +356,7 @@ __dprintf(const char *fmt, ...) if (cur != &MainThread) { // Show "thread id" for this debug message. putc_debug(&debuginfo, '|'); - puthex(&debuginfo, (u32)cur, 8, 0); + puthex(&debuginfo, (u32)cur, 8); putc_debug(&debuginfo, '|'); putc_debug(&debuginfo, ' '); } @@ -444,12 +458,12 @@ hexdump(const void *d, int len) while (len > 0) { if (count % 8 == 0) { putc(&debuginfo, '\n'); - puthex(&debuginfo, count*4, 8, 0); + puthex(&debuginfo, count*4, 8); putc(&debuginfo, ':'); } else { putc(&debuginfo, ' '); } - puthex(&debuginfo, *(u32*)d, 8, 0); + puthex(&debuginfo, *(u32*)d, 8); count++; len-=4; d+=4; diff --git a/src/post.c b/src/post.c index b4ad1fa..bd0b530 100644 --- a/src/post.c +++ b/src/post.c @@ -150,8 +150,7 @@ ram_probe(void) add_e820(0xfffbc000, 4*4096, E820_RESERVED); }
- dprintf(1, "Ram Size=0x%08x (0x%08x%08x high)\n" - , RamSize, (u32)(RamSizeOver4G >> 32), (u32)RamSizeOver4G); + dprintf(1, "Ram Size=0x%08x (0x%016llx high)\n", RamSize, RamSizeOver4G); }
static void
On 03/05/12 16:23, Kevin O'Connor wrote:
On Wed, Feb 29, 2012 at 12:45:05PM +0100, Gerd Hoffmann wrote:
src/output.c | 23 +++++++++++++++++++++-- 1 files changed, 21 insertions(+), 2 deletions(-)
Thanks Gerd.
I think your patch missed a couple of corner cases. I played with it a bit and came up with the below.
Looks fine.
cheers, Gerd
First bridge init, next pci bar discovery, finally pci bar ressource allocation. Needed because we need to figure whenever we can map 64bit bars above 4G before doing ressource allocation.
Signed-off-by: Gerd Hoffmann kraxel@redhat.com --- src/pciinit.c | 39 ++++++++++++++++++++++++++++++++------- 1 files changed, 32 insertions(+), 7 deletions(-)
diff --git a/src/pciinit.c b/src/pciinit.c index 9f3fdd4..652564c 100644 --- a/src/pciinit.c +++ b/src/pciinit.c @@ -368,22 +368,28 @@ static void 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; + struct pci_bus *bus; + int i; + + // init pci bridges foreachpci(pci) { - if (pci->class == PCI_CLASS_BRIDGE_PCI) { - busses[pci->secondary_bus].bus_dev = pci; + if (pci->class != PCI_CLASS_BRIDGE_PCI) + continue; + bus = &busses[pci->secondary_bus]; + bus->bus_dev = pci; + } + + // discover pci bars + foreachpci(pci) { + if (pci->class == PCI_CLASS_BRIDGE_PCI) continue; - } - struct pci_bus *bus = &busses[pci_bdf_to_bus(pci->bdf)]; - int i; for (i = 0; i < PCI_NUM_REGIONS; i++) { u32 val, size; pci_bios_get_bar(pci, i, &val, &size); if (val == 0) continue;
- pci_bios_bus_reserve(bus, pci_addr_to_type(val), size); pci->bars[i].addr = val; pci->bars[i].size = size; pci->bars[i].is64 = (!(val & PCI_BASE_ADDRESS_SPACE_IO) && @@ -395,6 +401,25 @@ static void pci_bios_check_devices(struct pci_bus *busses) } }
+ // alloc ressources for pci bars + foreachpci(pci) { + if (pci->class == PCI_CLASS_BRIDGE_PCI) + continue; + bus = &busses[pci_bdf_to_bus(pci->bdf)]; + for (i = 0; i < PCI_NUM_REGIONS; i++) { + enum pci_region_type type; + if (pci->bars[i].addr == 0) + continue; + + type = pci_addr_to_type(pci->bars[i].addr); + pci_bios_bus_reserve(bus, type, + pci->bars[i].size); + + if (pci->bars[i].is64) + i++; + } + } + // Propagate required bus resources to parent busses. int secondary_bus; for (secondary_bus=MaxPCIBus; secondary_bus>0; secondary_bus--) {
Makes pciinit.c 64bit aware. Use 64bit everywhere. Support discovery and configuration of 64bit bars, with non-zero upper32 bits. While being at it introduce a struct pci_bar which can be passed easily.
Signed-off-by: Gerd Hoffmann kraxel@redhat.com --- src/pci.h | 14 ++++-- src/pciinit.c | 126 +++++++++++++++++++++++++++++++++++---------------------- 2 files changed, 87 insertions(+), 53 deletions(-)
diff --git a/src/pci.h b/src/pci.h index a2a5a4c..46f162e 100644 --- a/src/pci.h +++ b/src/pci.h @@ -39,6 +39,14 @@ void pci_config_maskw(u16 bdf, u32 addr, u16 off, u16 on); struct pci_device *pci_find_device(u16 vendid, u16 devid); struct pci_device *pci_find_class(u16 classid);
+struct pci_bar { + u64 addr; + u64 size; + int is64:1, + ismem:1, + isprefetch:1; +}; + struct pci_device { u16 bdf; u8 rootbus; @@ -51,11 +59,7 @@ struct pci_device { u8 prog_if, revision; u8 header_type; u8 secondary_bus; - struct { - u32 addr; - u32 size; - int is64; - } bars[PCI_NUM_REGIONS]; + struct pci_bar bars[PCI_NUM_REGIONS];
// Local information on device. int have_driver; diff --git a/src/pciinit.c b/src/pciinit.c index 652564c..aa391a0 100644 --- a/src/pciinit.c +++ b/src/pciinit.c @@ -35,12 +35,12 @@ struct pci_bus { struct { /* pci region stats */ u32 count[32 - PCI_MEM_INDEX_SHIFT]; - u32 sum, max; + u64 sum, max; /* seconday bus region sizes */ - u32 size; + u64 size; /* pci region assignments */ - u32 bases[32 - PCI_MEM_INDEX_SHIFT]; - u32 base; + u64 bases[32 - PCI_MEM_INDEX_SHIFT]; + u64 base; } r[PCI_REGION_TYPE_COUNT]; struct pci_device *bus_dev; }; @@ -65,13 +65,13 @@ static u32 pci_index_to_size(int index, enum pci_region_type type) return 0x1 << (index + shift); }
-static enum pci_region_type pci_addr_to_type(u32 addr) +static enum pci_region_type pci_addr_to_type(struct pci_bus *bus, struct pci_bar *bar) { - if (addr & PCI_BASE_ADDRESS_SPACE_IO) + if (!bar->ismem) return PCI_REGION_TYPE_IO; - if (addr & PCI_BASE_ADDRESS_MEM_PREFETCH) - return PCI_REGION_TYPE_PREFMEM; - return PCI_REGION_TYPE_MEM; + if (!bar->isprefetch) + return PCI_REGION_TYPE_MEM; + return PCI_REGION_TYPE_PREFMEM; }
static u32 pci_bar(struct pci_device *pci, int region_num) @@ -86,9 +86,15 @@ static u32 pci_bar(struct pci_device *pci, int region_num) }
static void -pci_set_io_region_addr(struct pci_device *pci, int region_num, u32 addr) +pci_set_io_region_addr(struct pci_device *pci, int region_num, + u64 addr, int is64) { - pci_config_writel(pci->bdf, pci_bar(pci, region_num), addr); + u32 ofs = pci_bar(pci, region_num); + + pci_config_writel(pci->bdf, ofs, addr & 0xffffffff); + if (is64) { + pci_config_writel(pci->bdf, ofs + 4, addr >> 32); + } }
@@ -141,10 +147,10 @@ static const struct pci_device_id pci_isa_bridge_tbl[] = { static void storage_ide_init(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); - pci_set_io_region_addr(pci, 1, PORT_ATA1_CTRL_BASE); - pci_set_io_region_addr(pci, 2, PORT_ATA2_CMD_BASE); - pci_set_io_region_addr(pci, 3, PORT_ATA2_CTRL_BASE); + 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 */ @@ -158,13 +164,13 @@ static void piix_ide_init(struct pci_device *pci, void *arg) static void pic_ibm_init(struct pci_device *pci, void *arg) { /* PIC, IBM, MPIC & MPIC2 */ - pci_set_io_region_addr(pci, 0, 0x80800000 + 0x00040000); + pci_set_io_region_addr(pci, 0, 0x80800000 + 0x00040000, 0); }
static void apple_macio_init(struct pci_device *pci, void *arg) { /* macio bridge */ - pci_set_io_region_addr(pci, 0, 0x80800000); + pci_set_io_region_addr(pci, 0, 0x80800000, 0); }
static const struct pci_device_id pci_class_tbl[] = { @@ -330,14 +336,14 @@ static u32 pci_size_roundup(u32 size) }
static void -pci_bios_get_bar(struct pci_device *pci, int bar, u32 *val, u32 *size) +pci_bios_get_bar(struct pci_device *pci, int nr, struct pci_bar *bar) { - u32 ofs = pci_bar(pci, bar); + u32 ofs = pci_bar(pci, nr); u16 bdf = pci->bdf; u32 old = pci_config_readl(bdf, ofs); - u32 mask; + u64 mask;
- if (bar == PCI_ROM_SLOT) { + if (nr == PCI_ROM_SLOT) { mask = PCI_ROM_ADDRESS_MASK; pci_config_writel(bdf, ofs, mask); } else { @@ -347,12 +353,38 @@ pci_bios_get_bar(struct pci_device *pci, int bar, u32 *val, u32 *size) mask = PCI_BASE_ADDRESS_MEM_MASK; pci_config_writel(bdf, ofs, ~0); } - *val = pci_config_readl(bdf, ofs); + bar->addr = pci_config_readl(bdf, ofs); pci_config_writel(bdf, ofs, old); - *size = (~(*val & mask)) + 1; + if ((bar->addr & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY) { + bar->ismem = 1; + if ((bar->addr & PCI_BASE_ADDRESS_MEM_TYPE_MASK) == + PCI_BASE_ADDRESS_MEM_TYPE_64) + bar->is64 = 1; + if (bar->addr & PCI_BASE_ADDRESS_MEM_PREFETCH) + bar->isprefetch = 1; + } + if (bar->is64) { + u32 hold, high; + hold = pci_config_readl(bdf, ofs + 4); + pci_config_writel(bdf, ofs + 4, ~0); + high = pci_config_readl(bdf, ofs + 4); + pci_config_writel(bdf, ofs + 4, hold); + bar->addr |= ((u64)high << 32); + mask |= ((u64)0xffffffff << 32); + bar->size = (~(bar->addr & mask)) + 1; + } else if (bar->addr != 0) { + bar->size = (~(bar->addr & mask) & 0xffffffff) + 1; + } + if (bar->addr != 0) { + dprintf(1, " %d: addr %llx size %llx %s %s%s\n", nr, + bar->addr, bar->size, + bar->ismem ? "mem" : "io", + bar->is64 ? "64bit" : "32bit", + bar->isprefetch ? " prefetchable" : ""); + } }
-static void pci_bios_bus_reserve(struct pci_bus *bus, int type, u32 size) +static void pci_bios_bus_reserve(struct pci_bus *bus, int type, u64 size) { u32 index;
@@ -384,18 +416,14 @@ static void pci_bios_check_devices(struct pci_bus *busses) foreachpci(pci) { if (pci->class == PCI_CLASS_BRIDGE_PCI) continue; + dprintf(1, "PCI: check device bdf=%02x:%02x.%x\n", + pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf), + pci_bdf_to_fn(pci->bdf)); for (i = 0; i < PCI_NUM_REGIONS; i++) { - u32 val, size; - pci_bios_get_bar(pci, i, &val, &size); - if (val == 0) + pci_bios_get_bar(pci, i, &pci->bars[i]); + if (pci->bars[i].addr == 0) continue;
- pci->bars[i].addr = val; - pci->bars[i].size = size; - pci->bars[i].is64 = (!(val & PCI_BASE_ADDRESS_SPACE_IO) && - (val & PCI_BASE_ADDRESS_MEM_TYPE_MASK) - == PCI_BASE_ADDRESS_MEM_TYPE_64); - if (pci->bars[i].is64) i++; } @@ -411,7 +439,7 @@ static void pci_bios_check_devices(struct pci_bus *busses) if (pci->bars[i].addr == 0) continue;
- type = pci_addr_to_type(pci->bars[i].addr); + type = pci_addr_to_type(bus, &pci->bars[i]); pci_bios_bus_reserve(bus, type, pci->bars[i].size);
@@ -429,7 +457,7 @@ static void pci_bios_check_devices(struct pci_bus *busses) struct pci_bus *parent = &busses[pci_bdf_to_bus(s->bus_dev->bdf)]; int type; for (type = 0; type < PCI_REGION_TYPE_COUNT; type++) { - u32 limit = (type == PCI_REGION_TYPE_IO) ? + u64 limit = (type == PCI_REGION_TYPE_IO) ? PCI_BRIDGE_IO_MIN : PCI_BRIDGE_MEM_MIN; s->r[type].size = s->r[type].sum; if (s->r[type].size < limit) @@ -437,7 +465,8 @@ static void pci_bios_check_devices(struct pci_bus *busses) s->r[type].size = pci_size_roundup(s->r[type].size); pci_bios_bus_reserve(parent, type, s->r[type].size); } - dprintf(1, "PCI: secondary bus %d sizes: io %x, mem %x, prefmem %x\n", + dprintf(1, "PCI: secondary bus %d sizes: " + "io %llx, mem %llx, prefmem %llx\n", secondary_bus, s->r[PCI_REGION_TYPE_IO].size, s->r[PCI_REGION_TYPE_MEM].size, @@ -474,19 +503,20 @@ static int pci_bios_init_root_regions(struct pci_bus *bus, u32 start, u32 end)
static void pci_bios_init_bus_bases(struct pci_bus *bus) { - u32 base, newbase, size; + u64 base, newbase, size; int type, i;
for (type = 0; type < PCI_REGION_TYPE_COUNT; type++) { - dprintf(1, " type %s max %x sum %x base %x\n", region_type_name[type], - bus->r[type].max, bus->r[type].sum, bus->r[type].base); + dprintf(1, " type %s max %llx sum %llx base %llx\n", + region_type_name[type], bus->r[type].max, + bus->r[type].sum, bus->r[type].base); base = bus->r[type].base; for (i = ARRAY_SIZE(bus->r[type].count)-1; i >= 0; i--) { size = pci_index_to_size(i, type); if (!bus->r[type].count[i]) continue; newbase = base + size * bus->r[type].count[i]; - dprintf(1, " size %8x: %d bar(s), %8x -> %8x\n", + dprintf(1, " size %8llx: %d bar(s), %8llx -> %8llx\n", size, bus->r[type].count[i], base, newbase - 1); bus->r[type].bases[i] = base; base = newbase; @@ -530,8 +560,8 @@ static void pci_bios_map_devices(struct pci_bus *busses) dprintf(1, "PCI: init bases bus %d (secondary)\n", secondary_bus); pci_bios_init_bus_bases(s);
- u32 base = s->r[PCI_REGION_TYPE_IO].base; - u32 limit = base + s->r[PCI_REGION_TYPE_IO].size - 1; + u64 base = s->r[PCI_REGION_TYPE_IO].base; + u64 limit = base + s->r[PCI_REGION_TYPE_IO].size - 1; pci_config_writeb(bdf, PCI_IO_BASE, base >> PCI_IO_SHIFT); pci_config_writew(bdf, PCI_IO_BASE_UPPER16, 0); pci_config_writeb(bdf, PCI_IO_LIMIT, limit >> PCI_IO_SHIFT); @@ -564,15 +594,15 @@ static void pci_bios_map_devices(struct pci_bus *busses) if (pci->bars[i].addr == 0) continue;
- int type = pci_addr_to_type(pci->bars[i].addr); - u32 addr = pci_bios_bus_get_addr(bus, type, pci->bars[i].size); - dprintf(1, " bar %d, addr %x, size %x [%s]\n", - i, addr, pci->bars[i].size, region_type_name[type]); - pci_set_io_region_addr(pci, i, addr); + int type = pci_addr_to_type(bus, &pci->bars[i]); + u64 addr = pci_bios_bus_get_addr(bus, type, pci->bars[i].size); + dprintf(1, " bar %d, addr %llx, size %llx [%s]\n", + i, addr, pci->bars[i].size, + region_type_name[type]); + pci_set_io_region_addr(pci, i, addr, pci->bars[i].is64);
if (pci->bars[i].is64) { i++; - pci_set_io_region_addr(pci, i, 0); } } }
Signed-off-by: Gerd Hoffmann kraxel@redhat.com --- src/pciinit.c | 20 ++++++++------------ 1 files changed, 8 insertions(+), 12 deletions(-)
diff --git a/src/pciinit.c b/src/pciinit.c index aa391a0..33b9bf7 100644 --- a/src/pciinit.c +++ b/src/pciinit.c @@ -402,7 +402,7 @@ static void pci_bios_check_devices(struct pci_bus *busses)
struct pci_device *pci; struct pci_bus *bus; - int i; + int i, num_regions;
// init pci bridges foreachpci(pci) { @@ -414,12 +414,11 @@ static void pci_bios_check_devices(struct pci_bus *busses)
// discover pci bars foreachpci(pci) { - if (pci->class == PCI_CLASS_BRIDGE_PCI) - continue; + num_regions = (pci->class == PCI_CLASS_BRIDGE_PCI) ? 2 : PCI_NUM_REGIONS; dprintf(1, "PCI: check device bdf=%02x:%02x.%x\n", pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf), pci_bdf_to_fn(pci->bdf)); - for (i = 0; i < PCI_NUM_REGIONS; i++) { + for (i = 0; i < num_regions; i++) { pci_bios_get_bar(pci, i, &pci->bars[i]); if (pci->bars[i].addr == 0) continue; @@ -431,10 +430,9 @@ static void pci_bios_check_devices(struct pci_bus *busses)
// alloc ressources for pci bars foreachpci(pci) { - if (pci->class == PCI_CLASS_BRIDGE_PCI) - continue; + num_regions = (pci->class == PCI_CLASS_BRIDGE_PCI) ? 2 : PCI_NUM_REGIONS; bus = &busses[pci_bdf_to_bus(pci->bdf)]; - for (i = 0; i < PCI_NUM_REGIONS; i++) { + for (i = 0; i < num_regions; i++) { enum pci_region_type type; if (pci->bars[i].addr == 0) continue; @@ -545,7 +543,7 @@ static void pci_bios_map_devices(struct pci_bus *busses) pci_bios_init_bus_bases(&busses[0]);
// Map regions on each secondary bus. - int secondary_bus; + int secondary_bus, num_regions, i; for (secondary_bus=1; secondary_bus<=MaxPCIBus; secondary_bus++) { struct pci_bus *s = &busses[secondary_bus]; if (!s->bus_dev) @@ -583,14 +581,12 @@ static void pci_bios_map_devices(struct pci_bus *busses) // Map regions on each device. struct pci_device *pci; foreachpci(pci) { - if (pci->class == PCI_CLASS_BRIDGE_PCI) - continue; + num_regions = (pci->class == PCI_CLASS_BRIDGE_PCI) ? 2 : PCI_NUM_REGIONS; u16 bdf = pci->bdf; dprintf(1, "PCI: map device bdf=%02x:%02x.%x\n" , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), pci_bdf_to_fn(bdf)); struct pci_bus *bus = &busses[pci_bdf_to_bus(bdf)]; - int i; - for (i = 0; i < PCI_NUM_REGIONS; i++) { + for (i = 0; i < num_regions; i++) { if (pci->bars[i].addr == 0) continue;
The patch changes the bridge ressource allocation: Unused memory windows are disabled: If none of the devices connected to the bridge has -- say -- I/O bars, then the I/O memory window is turned off. This implies that bridges without devices don't get any ressources assigned. The pci bridge spec wants us behave that way (figured while looking at drivers/pci/setup-bus.c in the linux kernel source tree).
Signed-off-by: Gerd Hoffmann kraxel@redhat.com --- src/pciinit.c | 43 +++++++++++++++++++++++++++++++++---------- 1 files changed, 33 insertions(+), 10 deletions(-)
diff --git a/src/pciinit.c b/src/pciinit.c index 33b9bf7..a03e17c 100644 --- a/src/pciinit.c +++ b/src/pciinit.c @@ -457,6 +457,8 @@ static void pci_bios_check_devices(struct pci_bus *busses) for (type = 0; type < PCI_REGION_TYPE_COUNT; type++) { u64 limit = (type == PCI_REGION_TYPE_IO) ? PCI_BRIDGE_IO_MIN : PCI_BRIDGE_MEM_MIN; + if (s->r[type].sum == 0) + continue; s->r[type].size = s->r[type].sum; if (s->r[type].size < limit) s->r[type].size = limit; @@ -505,6 +507,8 @@ static void pci_bios_init_bus_bases(struct pci_bus *bus) int type, i;
for (type = 0; type < PCI_REGION_TYPE_COUNT; type++) { + if (bus->r[type].sum == 0) + continue; dprintf(1, " type %s max %llx sum %llx base %llx\n", region_type_name[type], bus->r[type].max, bus->r[type].sum, bus->r[type].base); @@ -522,9 +526,10 @@ static void pci_bios_init_bus_bases(struct pci_bus *bus) } }
-static u32 pci_bios_bus_get_addr(struct pci_bus *bus, int type, u32 size) +static u64 pci_bios_bus_get_addr(struct pci_bus *bus, int type, u64 size) { - u32 index, addr; + u32 index; + u64 addr;
index = pci_size_to_index(size, type); addr = bus->r[type].bases[index]; @@ -552,30 +557,48 @@ static void pci_bios_map_devices(struct pci_bus *busses) struct pci_bus *parent = &busses[pci_bdf_to_bus(bdf)]; int type; for (type = 0; type < PCI_REGION_TYPE_COUNT; type++) { + if (s->r[type].sum == 0) + continue; s->r[type].base = pci_bios_bus_get_addr( parent, type, s->r[type].size); } dprintf(1, "PCI: init bases bus %d (secondary)\n", secondary_bus); pci_bios_init_bus_bases(s);
- u64 base = s->r[PCI_REGION_TYPE_IO].base; - u64 limit = base + s->r[PCI_REGION_TYPE_IO].size - 1; + u64 base, limit; + if (s->r[PCI_REGION_TYPE_IO].sum) { + base = s->r[PCI_REGION_TYPE_IO].base; + limit = base + s->r[PCI_REGION_TYPE_IO].size - 1; + } else { + base = PCI_BRIDGE_IO_MIN; + limit = 0; + } pci_config_writeb(bdf, PCI_IO_BASE, base >> 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);
- base = s->r[PCI_REGION_TYPE_MEM].base; - limit = base + s->r[PCI_REGION_TYPE_MEM].size - 1; + if (s->r[PCI_REGION_TYPE_MEM].sum) { + base = s->r[PCI_REGION_TYPE_MEM].base; + limit = base + s->r[PCI_REGION_TYPE_MEM].size - 1; + } else { + base = PCI_BRIDGE_MEM_MIN; + limit = 0; + } pci_config_writew(bdf, PCI_MEMORY_BASE, base >> PCI_MEMORY_SHIFT); pci_config_writew(bdf, PCI_MEMORY_LIMIT, limit >> PCI_MEMORY_SHIFT);
- base = s->r[PCI_REGION_TYPE_PREFMEM].base; - limit = base + s->r[PCI_REGION_TYPE_PREFMEM].size - 1; + if (s->r[PCI_REGION_TYPE_PREFMEM].sum) { + base = s->r[PCI_REGION_TYPE_PREFMEM].base; + limit = base + s->r[PCI_REGION_TYPE_PREFMEM].size - 1; + } else { + base = PCI_BRIDGE_MEM_MIN; + limit = 0; + } pci_config_writew(bdf, PCI_PREF_MEMORY_BASE, base >> 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, 0); - pci_config_writel(bdf, PCI_PREF_LIMIT_UPPER32, 0); + pci_config_writel(bdf, PCI_PREF_BASE_UPPER32, base >> 32); + pci_config_writel(bdf, PCI_PREF_LIMIT_UPPER32, base >> 32); }
// Map regions on each device.
This patch adds a prefmem64 region for 64bit pci prefmem bars which we'll go map above 4G. This will happen when either the device is on the root bus or it is behind a bridge supporting 64bit memory windows and all prefmem bars of all devices hooked up using that bridge are 64bit capable. --- src/acpi-dsdt.dsl | 7 +++++ src/acpi-dsdt.hex | 72 +++++++++++++++++++++++++++++++++++++++++++--------- src/config.h | 2 + src/pciinit.c | 72 +++++++++++++++++++++++++++++++++++++++++++--------- 4 files changed, 127 insertions(+), 26 deletions(-)
diff --git a/src/acpi-dsdt.dsl b/src/acpi-dsdt.dsl index 7082b65..c17e947 100644 --- a/src/acpi-dsdt.dsl +++ b/src/acpi-dsdt.dsl @@ -175,6 +175,13 @@ DefinitionBlock ( 0x00000000, // Address Translation Offset 0x1EC00000, // Address Length ,, , AddressRangeMemory, TypeStatic) + QWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, Cacheable, ReadWrite, + 0x00000000, // Address Space Granularity + 0x8000000000, // Address Range Minimum + 0xFFFFFFFFFF, // Address Range Maximum + 0x00000000, // Address Translation Offset + 0x8000000000, // Address Length + ,, , AddressRangeMemory, TypeStatic) }) } } diff --git a/src/acpi-dsdt.hex b/src/acpi-dsdt.hex index 5dc7bb4..2393827 100644 --- a/src/acpi-dsdt.hex +++ b/src/acpi-dsdt.hex @@ -3,12 +3,12 @@ static unsigned char AmlCode[] = { 0x53, 0x44, 0x54, -0xd3, -0x10, +0x1, +0x11, 0x0, 0x0, 0x1, -0x2d, +0x1e, 0x42, 0x58, 0x50, @@ -31,9 +31,9 @@ static unsigned char AmlCode[] = { 0x4e, 0x54, 0x4c, -0x28, -0x5, -0x10, +0x23, +0x1, +0x9, 0x20, 0x10, 0x49, @@ -110,16 +110,16 @@ static unsigned char AmlCode[] = { 0x47, 0x42, 0x10, -0x44, -0x81, +0x42, +0x84, 0x5f, 0x53, 0x42, 0x5f, 0x5b, 0x82, -0x4c, -0x80, +0x4a, +0x83, 0x50, 0x43, 0x49, @@ -2064,10 +2064,10 @@ static unsigned char AmlCode[] = { 0x52, 0x53, 0x11, -0x42, -0x7, +0x40, +0xa, 0xa, -0x6e, +0x9c, 0x88, 0xd, 0x0, @@ -2176,6 +2176,52 @@ static unsigned char AmlCode[] = { 0x0, 0xc0, 0x1e, +0x8a, +0x2b, +0x0, +0x0, +0xc, +0x3, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x80, +0x0, +0x0, +0x0, +0xff, +0xff, +0xff, +0xff, +0xff, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x80, +0x0, +0x0, +0x0, 0x79, 0x0, 0x10, diff --git a/src/config.h b/src/config.h index b0187a4..5850476 100644 --- a/src/config.h +++ b/src/config.h @@ -47,6 +47,8 @@
#define BUILD_PCIMEM_START 0xe0000000 #define BUILD_PCIMEM_END 0xfec00000 /* IOAPIC is mapped at */ +#define BUILD_PCIMEM64_START 0x8000000000ULL +#define BUILD_PCIMEM64_END 0xFFFFFFFFFFULL
#define BUILD_IOAPIC_ADDR 0xfec00000 #define BUILD_HPET_ADDRESS 0xfed00000 diff --git a/src/pciinit.c b/src/pciinit.c index a03e17c..b3eaf86 100644 --- a/src/pciinit.c +++ b/src/pciinit.c @@ -22,13 +22,15 @@ enum pci_region_type { PCI_REGION_TYPE_IO, PCI_REGION_TYPE_MEM, PCI_REGION_TYPE_PREFMEM, + PCI_REGION_TYPE_PREFMEM64, 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", + [ PCI_REGION_TYPE_IO ] = "io", + [ PCI_REGION_TYPE_MEM ] = "mem", + [ PCI_REGION_TYPE_PREFMEM ] = "prefmem", + [ PCI_REGION_TYPE_PREFMEM64 ] = "prefmem64", };
struct pci_bus { @@ -42,7 +44,9 @@ struct pci_bus { u64 bases[32 - PCI_MEM_INDEX_SHIFT]; u64 base; } r[PCI_REGION_TYPE_COUNT]; + int use_prefmem64; struct pci_device *bus_dev; + struct pci_bus *parent; };
static int pci_size_to_index(u32 size, enum pci_region_type type) @@ -67,11 +71,19 @@ static u32 pci_index_to_size(int index, enum pci_region_type type)
static enum pci_region_type pci_addr_to_type(struct pci_bus *bus, struct pci_bar *bar) { + struct pci_bus *b; + if (!bar->ismem) return PCI_REGION_TYPE_IO; if (!bar->isprefetch) return PCI_REGION_TYPE_MEM; - return PCI_REGION_TYPE_PREFMEM; + if (!bar->is64) + return PCI_REGION_TYPE_PREFMEM; + for (b = bus; b != NULL && b->bus_dev != NULL; b = b->parent) { + if (!bus->use_prefmem64) + return PCI_REGION_TYPE_PREFMEM; + } + return PCI_REGION_TYPE_PREFMEM64; }
static u32 pci_bar(struct pci_device *pci, int region_num) @@ -409,12 +421,26 @@ static void pci_bios_check_devices(struct pci_bus *busses) if (pci->class != PCI_CLASS_BRIDGE_PCI) continue; bus = &busses[pci->secondary_bus]; + bus->parent = &busses[pci_bdf_to_bus(pci->bdf)]; bus->bus_dev = pci; + 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) { + dprintf(1, "PCI: bridge bdf=%02x:%02x.%x supports prefmem64\n", + pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf), + pci_bdf_to_fn(pci->bdf)); + bus->use_prefmem64 = 1; + } }
// discover pci bars foreachpci(pci) { num_regions = (pci->class == PCI_CLASS_BRIDGE_PCI) ? 2 : PCI_NUM_REGIONS; + bus = &busses[pci_bdf_to_bus(pci->bdf)]; dprintf(1, "PCI: check device bdf=%02x:%02x.%x\n", pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf), pci_bdf_to_fn(pci->bdf)); @@ -423,6 +449,15 @@ static void pci_bios_check_devices(struct pci_bus *busses) if (pci->bars[i].addr == 0) continue;
+ if (pci->bars[i].ismem && pci->bars[i].isprefetch && !pci->bars[i].is64) { + /* 32bit prefmem bar found -- disable 64bit prefmem + * on all bridges all the way up to the root bus */ + struct pci_bus *b; + for (b = bus; b != NULL; b = b->parent) { + b->use_prefmem64 = 0; + } + } + if (pci->bars[i].is64) i++; } @@ -466,22 +501,29 @@ static void pci_bios_check_devices(struct pci_bus *busses) pci_bios_bus_reserve(parent, type, s->r[type].size); } dprintf(1, "PCI: secondary bus %d sizes: " - "io %llx, mem %llx, prefmem %llx\n", + "io %llx, mem %llx, prefmem %llx, prefmem64 %llx\n", secondary_bus, s->r[PCI_REGION_TYPE_IO].size, s->r[PCI_REGION_TYPE_MEM].size, - s->r[PCI_REGION_TYPE_PREFMEM].size); + s->r[PCI_REGION_TYPE_PREFMEM].size, + s->r[PCI_REGION_TYPE_PREFMEM64].size); } }
#define ROOT_BASE(top, sum, max) ALIGN_DOWN((top)-(sum),(max) ?: 1)
// Setup region bases (given the regions' size and alignment) -static int pci_bios_init_root_regions(struct pci_bus *bus, u32 start, u32 end) +static int pci_bios_init_root_regions(struct pci_bus *bus) { + u32 start = BUILD_PCIMEM_START; + u32 end = BUILD_PCIMEM_END; + u64 start64 = BUILD_PCIMEM64_START; + u64 end64 = BUILD_PCIMEM64_END; + bus->r[PCI_REGION_TYPE_IO].base = 0xc000;
int reg1 = PCI_REGION_TYPE_PREFMEM, reg2 = PCI_REGION_TYPE_MEM; + int reg3 = PCI_REGION_TYPE_PREFMEM64; if (bus->r[reg1].sum < bus->r[reg2].sum) { // Swap regions so larger area is more likely to align well. reg1 = PCI_REGION_TYPE_MEM; @@ -493,6 +535,12 @@ static int pci_bios_init_root_regions(struct pci_bus *bus, u32 start, u32 end) if (bus->r[reg1].base < start) // Memory range requested is larger than available. return -1; + + bus->r[reg3].base = ROOT_BASE(end64, bus->r[reg3].sum, bus->r[reg3].max); + if (bus->r[reg3].base < start64) + // Memory range requested is larger than available. + return -1; + return 0; }
@@ -591,6 +639,9 @@ static void pci_bios_map_devices(struct pci_bus *busses) if (s->r[PCI_REGION_TYPE_PREFMEM].sum) { base = s->r[PCI_REGION_TYPE_PREFMEM].base; limit = base + s->r[PCI_REGION_TYPE_PREFMEM].size - 1; + } else if (s->r[PCI_REGION_TYPE_PREFMEM64].sum) { + base = s->r[PCI_REGION_TYPE_PREFMEM64].base; + limit = base + s->r[PCI_REGION_TYPE_PREFMEM64].size - 1; } else { base = PCI_BRIDGE_MEM_MIN; limit = 0; @@ -641,11 +692,6 @@ pci_setup(void) return; }
- dprintf(3, "pci setup\n"); - - u32 start = BUILD_PCIMEM_START; - u32 end = BUILD_PCIMEM_END; - dprintf(1, "=== PCI bus & bridge init ===\n"); if (pci_probe_host() != 0) { return; @@ -663,7 +709,7 @@ pci_setup(void) } memset(busses, 0, sizeof(*busses) * (MaxPCIBus + 1)); pci_bios_check_devices(busses); - if (pci_bios_init_root_regions(&busses[0], start, end) != 0) { + if (pci_bios_init_root_regions(&busses[0]) != 0) { panic("PCI: out of address space\n"); }