Hi,
Removed the dust from the two-pass pci initialization code and posting the bits for review to move forward. Rebased from q35 tree to latest upstream master. Uses the new pci_device struct now.
cheers, Gerd
Gerd Hoffmann (4): pci: add two-pass pci initialization code pci: activate two-pass pci initialization code pci: remove old pci initilaization code pci: init boot devices only on address space shortage
Makefile | 2 +- src/pci.c | 3 + src/pci.h | 8 + src/pci_region.c | 77 ------- src/pciinit.c | 642 +++++++++++++++++++++++++++++++++++++++--------------- src/util.h | 31 +--- 6 files changed, 481 insertions(+), 282 deletions(-) delete mode 100644 src/pci_region.c
This patch adds a second device scan to the pci initialization, which counts the memory bars of the various sizes and types. Then it calculates the sizes and the packing of the prefetchable and non-prefetchable pci memory windows and prints the results.
The patch doesn't actually map the devices to make debugging easier.
Signed-off-by: Gerd Hoffmann kraxel@redhat.com --- src/pci.h | 8 + src/pciinit.c | 416 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 421 insertions(+), 3 deletions(-)
diff --git a/src/pci.h b/src/pci.h index a21a1fd..868752d 100644 --- a/src/pci.h +++ b/src/pci.h @@ -3,6 +3,9 @@
#include "types.h" // u32
+#define PCI_ROM_SLOT 6 +#define PCI_NUM_REGIONS 7 + static inline u8 pci_bdf_to_bus(u16 bdf) { return bdf >> 8; } @@ -48,6 +51,11 @@ struct pci_device { u8 prog_if, revision; u8 header_type; u8 secondary_bus; + struct { + u32 addr; + u32 size; + int is64; + } r[PCI_NUM_REGIONS];
// Local information on device. int have_driver; diff --git a/src/pciinit.c b/src/pciinit.c index bfff3db..80bc7ad 100644 --- a/src/pciinit.c +++ b/src/pciinit.c @@ -6,21 +6,88 @@ // This file may be distributed under the terms of the GNU LGPLv3 license.
#include "util.h" // dprintf +#include "memmap.h" // e820 #include "pci.h" // pci_config_readl #include "biosvar.h" // GET_EBDA #include "pci_ids.h" // PCI_VENDOR_ID_INTEL #include "pci_regs.h" // PCI_COMMAND #include "xen.h" // usingXen
-#define PCI_ROM_SLOT 6 -#define PCI_NUM_REGIONS 7 +#define PCI_IO_INDEX_SHIFT 2 +#define PCI_MEM_INDEX_SHIFT 12
-static void pci_bios_init_device_in_bus(int bus); +#define PCI_BRIDGE_IO_MIN 0x100 +#define PCI_BRIDGE_MEM_MIN 0x100000
static struct pci_region pci_bios_io_region; static struct pci_region pci_bios_mem_region; static struct pci_region pci_bios_prefmem_region;
+static struct { + u32 start; + u32 end; +} mem_holes[2]; +static int mem_holes_count; + +static struct pci_bus { + /* pci region stats */ + u32 io_count[16 - PCI_IO_INDEX_SHIFT]; + u32 mem_count[32 - PCI_MEM_INDEX_SHIFT]; + u32 prefmem_count[32 - PCI_MEM_INDEX_SHIFT]; + u32 io_sum, io_max; + u32 mem_sum, mem_max; + u32 prefmem_sum, prefmem_max; + /* seconday bus region sizes */ + u32 io_size, mem_size, prefmem_size; + /* pci region assignments */ + u32 io_bases[16 - PCI_IO_INDEX_SHIFT]; + u32 mem_bases[32 - PCI_MEM_INDEX_SHIFT]; + u32 prefmem_bases[32 - PCI_MEM_INDEX_SHIFT]; + u32 io_base, mem_base, prefmem_base; +} *busses; +static int busses_count; + +static void pci_bios_init_device_in_bus(int bus); +static void pci_bios_check_device_in_bus(int bus); +static void pci_bios_init_bus_bases(struct pci_bus *bus); +static void pci_bios_map_device_in_bus(int bus); + +static int pci_size_to_index(u32 size, int shift) +{ + int index = __fls(size); + + if (index < shift) + index = shift; + index -= shift; + return index; +} + +static u32 pci_size_roundup(u32 size) +{ + int index = pci_size_to_index(size, 0); + return 1 << index; +} + +static int pci_io_size_to_index(u32 size) +{ + return pci_size_to_index(size, PCI_IO_INDEX_SHIFT); +} + +static u32 pci_io_index_to_size(int index) +{ + return 1 << (index + PCI_IO_INDEX_SHIFT); +} + +static int pci_mem_size_to_index(u32 size) +{ + return pci_size_to_index(size, PCI_MEM_INDEX_SHIFT); +} + +static u32 pci_mem_index_to_size(int index) +{ + return 1 << (index + PCI_MEM_INDEX_SHIFT); +} + /* host irqs corresponding to PCI irqs A-D */ const u8 pci_irqs[4] = { 10, 10, 11, 11 @@ -440,6 +507,328 @@ pci_bios_init_bus(void) { u8 pci_bus = 0; pci_bios_init_bus_rec(0 /* host bus */, &pci_bus); + busses_count = pci_bus + 1; + busses = malloc_tmphigh(busses_count * sizeof(struct pci_bus)); +} + +static void pci_bios_bus_get_bar(struct pci_bus *bus, int bdf, int bar, + u32 *val, u32 *size) +{ + u32 ofs = pci_bar(bdf, bar); + u32 old = pci_config_readl(bdf, ofs); + u32 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; + else + mask = PCI_BASE_ADDRESS_MEM_MASK; + pci_config_writel(bdf, ofs, ~0); + } + *val = pci_config_readl(bdf, ofs); + pci_config_writel(bdf, ofs, old); + *size = (~(*val & mask)) + 1; +} + +static void pci_bios_bus_reserve(struct pci_bus *bus, u32 val, u32 size) +{ + u32 index; + + if (val & PCI_BASE_ADDRESS_SPACE_IO) { + index = pci_io_size_to_index(size); + size = pci_io_index_to_size(index); + bus->io_count[index]++; + bus->io_sum += size; + if (bus->io_max < size) + bus->io_max = size; + } else { + index = pci_mem_size_to_index(size); + size = pci_mem_index_to_size(index); + if (val & PCI_BASE_ADDRESS_MEM_PREFETCH) { + bus->prefmem_count[index]++; + bus->prefmem_sum += size; + if (bus->prefmem_max < size) + bus->prefmem_max = size; + } else { + bus->mem_count[index]++; + bus->mem_sum += size; + if (bus->mem_max < size) + bus->mem_max = size; + } + } +} + +static u32 pci_bios_bus_get_addr(struct pci_bus *bus, u32 val, u32 size) +{ + u32 index, addr; + + if (val & PCI_BASE_ADDRESS_SPACE_IO) { + index = pci_io_size_to_index(size); + addr = bus->io_bases[index]; + bus->io_bases[index] += pci_io_index_to_size(index); + } else { + index = pci_mem_size_to_index(size); + if (val & PCI_BASE_ADDRESS_MEM_PREFETCH) { + addr = bus->prefmem_bases[index]; + bus->prefmem_bases[index] += pci_mem_index_to_size(index); + } else { + addr = bus->mem_bases[index]; + bus->mem_bases[index] += pci_mem_index_to_size(index); + } + } + return addr; +} + +static void pci_bios_check_device(struct pci_bus *bus, struct pci_device *dev) +{ + u16 bdf = dev->bdf; + int i; + + if (dev->class == PCI_CLASS_BRIDGE_PCI) { + if (dev->secondary_bus >= busses_count) { + /* should never trigger */ + dprintf(1, "PCI: bus count too small (%d), skipping bus #%d\n", + busses_count, dev->secondary_bus); + return; + } + struct pci_bus *s = busses + dev->secondary_bus; + pci_bios_check_device_in_bus(dev->secondary_bus); + s->io_size = pci_size_roundup(s->io_sum); + s->mem_size = pci_size_roundup(s->mem_sum); + s->prefmem_size = pci_size_roundup(s->prefmem_sum); + if (s->io_size < PCI_BRIDGE_IO_MIN) { + s->io_size = PCI_BRIDGE_IO_MIN; + } + if (s->mem_size < PCI_BRIDGE_MEM_MIN) { + s->mem_size = PCI_BRIDGE_MEM_MIN; + } + if (s->prefmem_size < PCI_BRIDGE_MEM_MIN) { + s->prefmem_size = PCI_BRIDGE_MEM_MIN; + } + dprintf(1, "PCI: secondary bus %d sizes: io %x, mem %x, prefmem %x\n", + dev->secondary_bus, s->io_size, s->mem_size, s->prefmem_size); + pci_bios_bus_reserve(bus, PCI_BASE_ADDRESS_SPACE_IO, s->io_size); + pci_bios_bus_reserve(bus, 0, s->mem_size); + pci_bios_bus_reserve(bus, PCI_BASE_ADDRESS_MEM_PREFETCH, s->prefmem_size); + return; + } + + for (i = 0; i < PCI_NUM_REGIONS; i++) { + u32 val, size; + pci_bios_bus_get_bar(bus, bdf, i, &val, &size); + if (val == 0) { + continue; + } + pci_bios_bus_reserve(bus, val, size); + dev->r[i].addr = val; + dev->r[i].size = size; + dev->r[i].is64 = (!(val & PCI_BASE_ADDRESS_SPACE_IO) && + (val & PCI_BASE_ADDRESS_MEM_TYPE_MASK) == PCI_BASE_ADDRESS_MEM_TYPE_64); + + if (dev->r[i].is64) { + i++; + } + } +} + +static void pci_bios_map_device(struct pci_bus *bus, struct pci_device *dev) +{ + int i; + + if (dev->class == PCI_CLASS_BRIDGE_PCI) { + if (dev->secondary_bus >= busses_count) { + return; + } + struct pci_bus *s = busses + dev->secondary_bus; + s->io_base = pci_bios_bus_get_addr + (bus, PCI_BASE_ADDRESS_SPACE_IO, s->io_size); + s->mem_base = pci_bios_bus_get_addr + (bus, 0, s->mem_size); + s->prefmem_base = pci_bios_bus_get_addr + (bus, PCI_BASE_ADDRESS_MEM_PREFETCH, s->prefmem_size); + dprintf(1, "PCI: init bases bus %d (secondary)\n", dev->secondary_bus); + pci_bios_init_bus_bases(s); + /* TODO: commit assignments */ + pci_bios_map_device_in_bus(dev->secondary_bus); + return; + } + + for (i = 0; i < PCI_NUM_REGIONS; i++) { + u32 addr; + if (dev->r[i].addr == 0) { + continue; + } + + addr = pci_bios_bus_get_addr(bus, dev->r[i].addr, dev->r[i].size); + dprintf(1, " bar %d, addr %x, size %x [%s]\n", + i, addr, dev->r[i].size, + dev->r[i].addr & PCI_BASE_ADDRESS_SPACE_IO ? "io" : "mem"); + /* TODO: commit assignments */ + + if (dev->r[i].is64) { + i++; + } + } +} + +static void pci_bios_check_device_in_bus(int bus) +{ + struct pci_device *pci; + + dprintf(1, "PCI: check devices bus %d\n", bus); + foreachpci(pci) { + if (pci->rootbus != bus) + continue; + pci_bios_check_device(&busses[bus], pci); + } +} + +static void pci_bios_map_device_in_bus(int bus) +{ + struct pci_device *pci; + + foreachpci(pci) { + if (pci->rootbus != bus) + continue; + dprintf(1, "PCI: map device bus %d, bfd 0x%x\n", bus, pci->bdf); + pci_bios_map_device(&busses[bus], pci); + } +} + +static void pci_bios_init_bus_bases(struct pci_bus *bus) +{ + u32 base, newbase, size; + int i; + + /* assign prefetchable memory regions */ + dprintf(1, " prefmem max %x sum %x base %x\n", + bus->prefmem_max, bus->prefmem_sum, bus->prefmem_base); + base = bus->prefmem_base; + for (i = ARRAY_SIZE(bus->prefmem_count)-1; i >= 0; i--) { + size = pci_mem_index_to_size(i); + if (!bus->prefmem_count[i]) + continue; + newbase = base + size * bus->prefmem_count[i]; + dprintf(1, " size %8x: %d bar(s), %8x -> %8x\n", + size, bus->prefmem_count[i], base, newbase - 1); + bus->prefmem_bases[i] = base; + base = newbase; + } + + /* assign memory regions */ + dprintf(1, " mem max %x sum %x base %x\n", + bus->mem_max, bus->mem_sum, bus->mem_base); + base = bus->mem_base; + for (i = ARRAY_SIZE(bus->mem_count)-1; i >= 0; i--) { + size = pci_mem_index_to_size(i); + if (!bus->mem_count[i]) + continue; + newbase = base + size * bus->mem_count[i]; + dprintf(1, " mem size %8x: %d bar(s), %8x -> %8x\n", + size, bus->mem_count[i], base, newbase - 1); + bus->mem_bases[i] = base; + base = newbase; + } + + /* assign io regions */ + dprintf(1, " io max %x sum %x base %x\n", + bus->io_max, bus->io_sum, bus->io_base); + base = bus->io_base; + for (i = ARRAY_SIZE(bus->io_count)-1; i >= 0; i--) { + size = pci_io_index_to_size(i); + if (!bus->io_count[i]) + continue; + newbase = base + size * bus->io_count[i]; + dprintf(1, " io size %4x: %d bar(s), %4x -> %4x\n", + size, bus->io_count[i], base, newbase - 1); + bus->io_bases[i] = base; + base = newbase; + } +} + +#define ROOT_BASE(top, sum, align) ALIGN_DOWN((top)-(sum),(align)) + +static int pci_bios_init_root_regions(void) +{ + struct pci_bus *bus = &busses[0]; + int h1, h2; + + /* io ports */ + bus->io_base = 0xc000; + + /* try to fit all into one memory hole */ + for (h1 = mem_holes_count-1; h1 >= 0; h1--) { + if (bus->mem_sum < bus->prefmem_sum) { + bus->mem_base = ROOT_BASE(mem_holes[h1].end, + bus->mem_sum, bus->mem_max); + bus->prefmem_base = ROOT_BASE(bus->mem_base, + bus->prefmem_sum, bus->prefmem_max); + if (bus->prefmem_base >= mem_holes[h1].start) { + return 0; + } + } else { + bus->prefmem_base = ROOT_BASE(mem_holes[h1].end, + bus->prefmem_sum, bus->prefmem_max); + bus->mem_base = ROOT_BASE(bus->prefmem_base, + bus->mem_sum, bus->mem_max); + if (bus->mem_base >= mem_holes[h1].start) { + return 0; + } + } + } + + if (mem_holes_count <= 1) { + /* can't try to split -> OOM */ + return -1; + } + + /* try to fit the larger one first */ + for (h1 = mem_holes_count-1; h1 >= 0; h1--) { + if (bus->mem_sum < bus->prefmem_sum) { + bus->prefmem_base = ROOT_BASE(mem_holes[h1].end, + bus->prefmem_sum, bus->prefmem_max); + if (bus->prefmem_base >= mem_holes[h1].start) { + break; + } + } else { + bus->mem_base = ROOT_BASE(mem_holes[h1].end, + bus->mem_sum, bus->mem_max); + if (bus->mem_base >= mem_holes[h1].start) { + break; + } + } + } + + if (h1 == -1) { + /* all holes too small -> OOM */ + return -1; + } + + /* try to fit the smaller one now */ + for (h2 = mem_holes_count-1; h2 >= 0; h2--) { + if (h1 == h2) { + continue; /* hole already taken */ + } + if (bus->mem_sum < bus->prefmem_sum) { + bus->mem_base = ROOT_BASE(mem_holes[h2].end, + bus->mem_sum, bus->mem_max); + if (bus->mem_base >= mem_holes[h2].start) { + return 0; + } + } else { + bus->prefmem_base = ROOT_BASE(mem_holes[h2].end, + bus->prefmem_sum, bus->prefmem_max); + if (bus->prefmem_base >= mem_holes[h2].start) { + return 0; + } + } + } + + /* didn't work out ... */ + return -1; }
void @@ -453,19 +842,40 @@ pci_setup(void)
dprintf(3, "pci setup\n");
+ /* matches acpi-dsdt.dsl */ + mem_holes[0].start = 0xe0000000; + mem_holes[0].end = BUILD_IOAPIC_ADDR; + mem_holes_count = 1; + pci_region_init(&pci_bios_io_region, 0xc000, 64 * 1024 - 1); pci_region_init(&pci_bios_mem_region, BUILD_PCIMEM_START, BUILD_PCIMEM_END - 1); pci_region_init(&pci_bios_prefmem_region, BUILD_PCIPREFMEM_START, BUILD_PCIPREFMEM_END - 1);
+ dprintf(1, "=== PCI bus & bridge init ===\n"); pci_bios_init_bus();
+ dprintf(1, "=== PCI device probing ===\n"); pci_probe();
+ dprintf(1, "=== PCI new allocation pass #1 ===\n"); + pci_bios_check_device_in_bus(0 /* host bus */); + if (pci_bios_init_root_regions() != 0) { + dprintf(1, "PCI: out of address space\n"); + /* Hmm, what do now? */ + } + + dprintf(1, "=== PCI new allocation pass #2 ===\n"); + pci_bios_map_device_in_bus(0 /* host bus */); + + dprintf(1, "=== PCI old allocation pass ===\n"); struct pci_device *pci; foreachpci(pci) { pci_init_device(pci_isa_bridge_tbl, pci, NULL); } pci_bios_init_device_in_bus(0 /* host bus */); + + free(busses); + busses_count = 0; }
On Fri, Jun 24, 2011 at 05:24:35PM +0200, Gerd Hoffmann wrote:
This patch adds a second device scan to the pci initialization, which counts the memory bars of the various sizes and types. Then it calculates the sizes and the packing of the prefetchable and non-prefetchable pci memory windows and prints the results.
The patch doesn't actually map the devices to make debugging easier.
Thanks.
I must admit, I'm having a hard time "wrapping my head around" this new allocator.
Also, see my comments below.
[...]
+static struct pci_bus {
- /* pci region stats */
- u32 io_count[16 - PCI_IO_INDEX_SHIFT];
- u32 mem_count[32 - PCI_MEM_INDEX_SHIFT];
- u32 prefmem_count[32 - PCI_MEM_INDEX_SHIFT];
- u32 io_sum, io_max;
- u32 mem_sum, mem_max;
- u32 prefmem_sum, prefmem_max;
- /* seconday bus region sizes */
- u32 io_size, mem_size, prefmem_size;
- /* pci region assignments */
- u32 io_bases[16 - PCI_IO_INDEX_SHIFT];
- u32 mem_bases[32 - PCI_MEM_INDEX_SHIFT];
- u32 prefmem_bases[32 - PCI_MEM_INDEX_SHIFT];
- u32 io_base, mem_base, prefmem_base;
+} *busses;
Hrmm - is there an opportunity to define an array of three "region types" (io, mem, prefmem) instead? The coreboot PCI assignment code does this.
[...]
+static void pci_bios_check_device_in_bus(int bus) +{
- struct pci_device *pci;
- dprintf(1, "PCI: check devices bus %d\n", bus);
- foreachpci(pci) {
if (pci->rootbus != bus)
rootbus doesn't look correct here - it's an identifier for independent root PCI buses - pci_bdf_to_bus(pci->bdf) is likely what you want.
[...]
+static int pci_bios_init_root_regions(void) +{
- struct pci_bus *bus = &busses[0];
- int h1, h2;
- /* io ports */
- bus->io_base = 0xc000;
- /* try to fit all into one memory hole */
- for (h1 = mem_holes_count-1; h1 >= 0; h1--) {
if (bus->mem_sum < bus->prefmem_sum) {
I'm a bit confused by what this code does. However, tracking a list of memory regions and filling allocations in those regions is what the pmm code does. Can an IO zone be created and addSpace()/pmm_malloc() be used?
-Kevin
Hi,
+static struct pci_bus {
- /* pci region stats */
- u32 io_count[16 - PCI_IO_INDEX_SHIFT];
- u32 mem_count[32 - PCI_MEM_INDEX_SHIFT];
- u32 prefmem_count[32 - PCI_MEM_INDEX_SHIFT];
- u32 io_sum, io_max;
- u32 mem_sum, mem_max;
- u32 prefmem_sum, prefmem_max;
- /* seconday bus region sizes */
- u32 io_size, mem_size, prefmem_size;
- /* pci region assignments */
- u32 io_bases[16 - PCI_IO_INDEX_SHIFT];
- u32 mem_bases[32 - PCI_MEM_INDEX_SHIFT];
- u32 prefmem_bases[32 - PCI_MEM_INDEX_SHIFT];
- u32 io_base, mem_base, prefmem_base;
+} *busses;
Hrmm - is there an opportunity to define an array of three "region types" (io, mem, prefmem) instead? The coreboot PCI assignment code does this.
I'll try and see how the code will look like. Was thinking about that too.
[...]
+static void pci_bios_check_device_in_bus(int bus) +{
- struct pci_device *pci;
- dprintf(1, "PCI: check devices bus %d\n", bus);
- foreachpci(pci) {
if (pci->rootbus != bus)
rootbus doesn't look correct here - it's an identifier for independent root PCI buses - pci_bdf_to_bus(pci->bdf) is likely what you want.
Ok.
+static int pci_bios_init_root_regions(void) +{
- struct pci_bus *bus =&busses[0];
- int h1, h2;
- /* io ports */
- bus->io_base = 0xc000;
- /* try to fit all into one memory hole */
- for (h1 = mem_holes_count-1; h1>= 0; h1--) {
if (bus->mem_sum< bus->prefmem_sum) {
I'm a bit confused by what this code does.
A little more background:
I had (and still have in another branch) code which creates the mem_holes array from the e820 table to figure where seabios can allocate address space for the PCI memory regions. With the piix emulation this gave us one memory hold, from RamSize to BUILD_IOAPIC_ADDR. With the q35 emulation this gave us two memory holes, one from RamSize to 0xe0000000 and one from 0xf0000000 to BUILD_IOAPIC_ADDR due to mmconfig @ 0xe0000000 -> 0xefffffff.
The pci_bios_init_root_regions() function can place one pci memory region in one memory hole and the other one in the second in case they are to big to fit into one hole.
So the question is whenever we'll wanna go down the route to have dynamic i/o regions, with the dsdt changes discussed here recently. Or whenever we'll just stick with a fixed region and just make it larger for the q35 emulation. Say allow guest ram up to 0xc0000000, then have the mmconfig region @ 0xc0000000 -> 0xcfffffff, and have the single hole 0xd0000000 -> BUILD_IOAPIC_ADDR available for pci memory bar allocations. If we plan for a single memory hole the function can be simplified alot, basically everything below "if (mem_holes_count <= 1) { return -1 }" can be zapped then.
cheers, Gerd
This patch actually enables the two-pass pci initialization and deactivates the old pci initialization bits.
Signed-off-by: Gerd Hoffmann kraxel@redhat.com --- src/pciinit.c | 36 ++++++++++++++++++++++++++++++++++-- 1 files changed, 34 insertions(+), 2 deletions(-)
diff --git a/src/pciinit.c b/src/pciinit.c index 80bc7ad..5b16bef 100644 --- a/src/pciinit.c +++ b/src/pciinit.c @@ -122,6 +122,8 @@ static void pci_set_io_region_addr(u16 bdf, int region_num, u32 addr) */ static int pci_bios_allocate_region(u16 bdf, int region_num) { + return 0; + struct pci_region *r; u32 ofs = pci_bar(bdf, region_num);
@@ -183,6 +185,8 @@ static int pci_bios_allocate_region(u16 bdf, int region_num)
static void pci_bios_allocate_regions(struct pci_device *pci, void *arg) { + return; + int i; for (i = 0; i < PCI_NUM_REGIONS; i++) { int is_64bit = pci_bios_allocate_region(pci->bdf, i); @@ -238,6 +242,7 @@ static const struct pci_device_id pci_isa_bridge_tbl[] = { #define PCI_PREF_MEMORY_ALIGN (1UL << 20) #define PCI_PREF_MEMORY_SHIFT 16
+#if 0 static void pci_bios_init_device_bridge(struct pci_device *pci, void *arg) { u16 bdf = pci->bdf; @@ -322,6 +327,7 @@ static void pci_bios_init_device_bridge(struct pci_device *pci, void *arg)
pci_config_maskw(bdf, PCI_BRIDGE_CONTROL, 0, PCI_BRIDGE_CTL_SERR); } +#endif
static void storage_ide_init(struct pci_device *pci, void *arg) { @@ -373,9 +379,11 @@ static const struct pci_device_id pci_class_tbl[] = { PCI_DEVICE_CLASS(PCI_VENDOR_ID_APPLE, 0x0017, 0xff00, apple_macio_init), PCI_DEVICE_CLASS(PCI_VENDOR_ID_APPLE, 0x0022, 0xff00, apple_macio_init),
+#if 0 /* PCI bridge */ PCI_DEVICE_CLASS(PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_BRIDGE_PCI, pci_bios_init_device_bridge), +#endif
/* default */ PCI_DEVICE(PCI_ANY_ID, PCI_ANY_ID, pci_bios_allocate_regions), @@ -636,6 +644,7 @@ static void pci_bios_check_device(struct pci_bus *bus, struct pci_device *dev)
static void pci_bios_map_device(struct pci_bus *bus, struct pci_device *dev) { + u16 bdf = dev->bdf; int i;
if (dev->class == PCI_CLASS_BRIDGE_PCI) { @@ -643,6 +652,8 @@ static void pci_bios_map_device(struct pci_bus *bus, struct pci_device *dev) return; } struct pci_bus *s = busses + dev->secondary_bus; + u32 limit; + s->io_base = pci_bios_bus_get_addr (bus, PCI_BASE_ADDRESS_SPACE_IO, s->io_size); s->mem_base = pci_bios_bus_get_addr @@ -651,7 +662,25 @@ static void pci_bios_map_device(struct pci_bus *bus, struct pci_device *dev) (bus, PCI_BASE_ADDRESS_MEM_PREFETCH, s->prefmem_size); dprintf(1, "PCI: init bases bus %d (secondary)\n", dev->secondary_bus); pci_bios_init_bus_bases(s); - /* TODO: commit assignments */ + + limit = s->io_base + s->io_size - 1; + pci_config_writeb(bdf, PCI_IO_BASE, s->io_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); + + limit = s->mem_base + s->mem_size - 1; + pci_config_writew(bdf, PCI_MEMORY_BASE, s->mem_base >> PCI_MEMORY_SHIFT); + pci_config_writew(bdf, PCI_MEMORY_LIMIT, limit >> PCI_MEMORY_SHIFT); + + limit = s->prefmem_base + s->prefmem_size - 1; + pci_config_writew(bdf, PCI_PREF_MEMORY_BASE, + s->prefmem_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_bios_map_device_in_bus(dev->secondary_bus); return; } @@ -666,7 +695,7 @@ static void pci_bios_map_device(struct pci_bus *bus, struct pci_device *dev) dprintf(1, " bar %d, addr %x, size %x [%s]\n", i, addr, dev->r[i].size, dev->r[i].addr & PCI_BASE_ADDRESS_SPACE_IO ? "io" : "mem"); - /* TODO: commit assignments */ + pci_set_io_region_addr(bdf, i, addr);
if (dev->r[i].is64) { i++; @@ -869,11 +898,14 @@ pci_setup(void) dprintf(1, "=== PCI new allocation pass #2 ===\n"); pci_bios_map_device_in_bus(0 /* host bus */);
+#if 0 dprintf(1, "=== PCI old allocation pass ===\n"); struct pci_device *pci; foreachpci(pci) { pci_init_device(pci_isa_bridge_tbl, pci, NULL); } +#endif + pci_bios_init_device_in_bus(0 /* host bus */);
free(busses);
Signed-off-by: Gerd Hoffmann kraxel@redhat.com --- Makefile | 2 +- src/pci_region.c | 77 --------------------- src/pciinit.c | 197 ------------------------------------------------------ src/util.h | 29 -------- 4 files changed, 1 insertions(+), 304 deletions(-) delete mode 100644 src/pci_region.c
diff --git a/Makefile b/Makefile index a96c89d..7234b26 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,7 @@ SRC16=$(SRCBOTH) system.c disk.c font.c SRC32FLAT=$(SRCBOTH) post.c shadow.c memmap.c coreboot.c boot.c \ acpi.c smm.c mptable.c smbios.c pciinit.c optionroms.c mtrr.c \ lzmadecode.c bootsplash.c jpeg.c usb-hub.c paravirt.c \ - pci_region.c biostables.c xen.c + biostables.c xen.c SRC32SEG=util.c output.c pci.c pcibios.c apm.c stacks.c
cc-option = $(shell if test -z "`$(1) $(2) -S -o /dev/null -xc \ diff --git a/src/pci_region.c b/src/pci_region.c deleted file mode 100644 index 1d9de71..0000000 --- a/src/pci_region.c +++ /dev/null @@ -1,77 +0,0 @@ -// helper functions to manage pci io/memory/prefetch memory region -// -// Copyright (C) 2009 Isaku Yamahata <yamahata at valinux co jp> -// -// This file may be distributed under the terms of the GNU LGPLv3 license. -// -// - -#include "util.h" - -#define PCI_REGION_DISABLED (-1) - -void pci_region_init(struct pci_region *r, u32 first, u32 last) -{ - r->first = first; - r->last = last; - - r->cur_first = r->first; -} - -// PCI_REGION_DISABLED represents that the region is in special state. -// its value is chosen such that cur_first can't be PCI_REGION_DISABLED -// normally. -// NOTE: the area right below 4G is used for LAPIC, so such area can't -// be used for PCI memory. -u32 pci_region_disable(struct pci_region *r) -{ - return r->cur_first = PCI_REGION_DISABLED; -} - -static int pci_region_disabled(const struct pci_region *r) -{ - return r->cur_first == PCI_REGION_DISABLED; -} - -static u32 pci_region_alloc_align(struct pci_region *r, u32 size, u32 align) -{ - if (pci_region_disabled(r)) { - return 0; - } - - u32 s = ALIGN(r->cur_first, align); - if (s > r->last || s < r->cur_first) { - return 0; - } - u32 e = s + size; - if (e < s || e - 1 > r->last) { - return 0; - } - r->cur_first = e; - return s; -} - -u32 pci_region_alloc(struct pci_region *r, u32 size) -{ - return pci_region_alloc_align(r, size, size); -} - -u32 pci_region_align(struct pci_region *r, u32 align) -{ - return pci_region_alloc_align(r, 0, align); -} - -void pci_region_revert(struct pci_region *r, u32 addr) -{ - r->cur_first = addr; -} - -u32 pci_region_addr(const struct pci_region *r) -{ - return r->cur_first; -} - -u32 pci_region_size(const struct pci_region *r) -{ - return r->last - r->first + 1; -} diff --git a/src/pciinit.c b/src/pciinit.c index 5b16bef..5791ebe 100644 --- a/src/pciinit.c +++ b/src/pciinit.c @@ -19,10 +19,6 @@ #define PCI_BRIDGE_IO_MIN 0x100 #define PCI_BRIDGE_MEM_MIN 0x100000
-static struct pci_region pci_bios_io_region; -static struct pci_region pci_bios_mem_region; -static struct pci_region pci_bios_prefmem_region; - static struct { u32 start; u32 end; @@ -112,88 +108,6 @@ static void pci_set_io_region_addr(u16 bdf, int region_num, u32 addr) ofs = pci_bar(bdf, region_num);
pci_config_writel(bdf, ofs, addr); - dprintf(1, "region %d: 0x%08x\n", region_num, addr); -} - -/* - * return value - * 0: 32bit BAR - * non 0: 64bit BAR - */ -static int pci_bios_allocate_region(u16 bdf, int region_num) -{ - return 0; - - struct pci_region *r; - u32 ofs = pci_bar(bdf, region_num); - - u32 old = pci_config_readl(bdf, ofs); - u32 mask; - if (region_num == 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; - else - mask = PCI_BASE_ADDRESS_MEM_MASK; - pci_config_writel(bdf, ofs, ~0); - } - u32 val = pci_config_readl(bdf, ofs); - pci_config_writel(bdf, ofs, old); - - u32 size = (~(val & mask)) + 1; - if (val != 0) { - const char *type; - const char *msg; - if (val & PCI_BASE_ADDRESS_SPACE_IO) { - r = &pci_bios_io_region; - type = "io"; - msg = ""; - } else if ((val & PCI_BASE_ADDRESS_MEM_PREFETCH) && - /* keep behaviour on bus = 0 */ - pci_bdf_to_bus(bdf) != 0 && - /* If pci_bios_prefmem_addr == 0, keep old behaviour */ - pci_region_addr(&pci_bios_prefmem_region) != 0) { - r = &pci_bios_prefmem_region; - type = "prefmem"; - msg = "decrease BUILD_PCIMEM_SIZE and recompile. size %x"; - } else { - r = &pci_bios_mem_region; - type = "mem"; - msg = "increase BUILD_PCIMEM_SIZE and recompile."; - } - u32 addr = pci_region_alloc(r, size); - if (addr > 0) { - pci_set_io_region_addr(bdf, region_num, addr); - } else { - size = 0; - dprintf(1, - "%s region of (bdf 0x%x bar %d) can't be mapped. " - "%s size %x\n", - type, bdf, region_num, msg, pci_region_size(r)); - } - } - - int is_64bit = !(val & PCI_BASE_ADDRESS_SPACE_IO) && - (val & PCI_BASE_ADDRESS_MEM_TYPE_MASK) == PCI_BASE_ADDRESS_MEM_TYPE_64; - if (is_64bit && size > 0) { - pci_config_writel(bdf, ofs + 4, 0); - } - return is_64bit; -} - -static void pci_bios_allocate_regions(struct pci_device *pci, void *arg) -{ - return; - - int i; - for (i = 0; i < PCI_NUM_REGIONS; i++) { - int is_64bit = pci_bios_allocate_region(pci->bdf, i); - if (is_64bit){ - i++; - } - } }
/* return the global irq number corresponding to a given device irq @@ -242,93 +156,6 @@ static const struct pci_device_id pci_isa_bridge_tbl[] = { #define PCI_PREF_MEMORY_ALIGN (1UL << 20) #define PCI_PREF_MEMORY_SHIFT 16
-#if 0 -static void pci_bios_init_device_bridge(struct pci_device *pci, void *arg) -{ - u16 bdf = pci->bdf; - pci_bios_allocate_region(bdf, 0); - pci_bios_allocate_region(bdf, 1); - pci_bios_allocate_region(bdf, PCI_ROM_SLOT); - - u32 io_old = pci_region_addr(&pci_bios_io_region); - u32 mem_old = pci_region_addr(&pci_bios_mem_region); - u32 prefmem_old = pci_region_addr(&pci_bios_prefmem_region); - - /* IO BASE is assumed to be 16 bit */ - if (pci_region_align(&pci_bios_io_region, PCI_IO_ALIGN) == 0) { - pci_region_disable(&pci_bios_io_region); - } - if (pci_region_align(&pci_bios_mem_region, PCI_MEMORY_ALIGN) == 0) { - pci_region_disable(&pci_bios_mem_region); - } - if (pci_region_align(&pci_bios_prefmem_region, - PCI_PREF_MEMORY_ALIGN) == 0) { - pci_region_disable(&pci_bios_prefmem_region); - } - - u32 io_base = pci_region_addr(&pci_bios_io_region); - u32 mem_base = pci_region_addr(&pci_bios_mem_region); - u32 prefmem_base = pci_region_addr(&pci_bios_prefmem_region); - - u8 secbus = pci_config_readb(bdf, PCI_SECONDARY_BUS); - if (secbus > 0) { - pci_bios_init_device_in_bus(secbus); - } - - u32 io_end = pci_region_align(&pci_bios_io_region, PCI_IO_ALIGN); - if (io_end == 0) { - pci_region_revert(&pci_bios_io_region, io_old); - io_base = 0xffff; - io_end = 1; - } - pci_config_writeb(bdf, PCI_IO_BASE, io_base >> PCI_IO_SHIFT); - pci_config_writew(bdf, PCI_IO_BASE_UPPER16, 0); - pci_config_writeb(bdf, PCI_IO_LIMIT, (io_end - 1) >> PCI_IO_SHIFT); - pci_config_writew(bdf, PCI_IO_LIMIT_UPPER16, 0); - - u32 mem_end = pci_region_align(&pci_bios_mem_region, PCI_MEMORY_ALIGN); - if (mem_end == 0) { - pci_region_revert(&pci_bios_mem_region, mem_old); - mem_base = 0xffffffff; - mem_end = 1; - } - pci_config_writew(bdf, PCI_MEMORY_BASE, mem_base >> PCI_MEMORY_SHIFT); - pci_config_writew(bdf, PCI_MEMORY_LIMIT, (mem_end -1) >> PCI_MEMORY_SHIFT); - - u32 prefmem_end = pci_region_align(&pci_bios_prefmem_region, - PCI_PREF_MEMORY_ALIGN); - if (prefmem_end == 0) { - pci_region_revert(&pci_bios_prefmem_region, prefmem_old); - prefmem_base = 0xffffffff; - prefmem_end = 1; - } - pci_config_writew(bdf, PCI_PREF_MEMORY_BASE, - prefmem_base >> PCI_PREF_MEMORY_SHIFT); - pci_config_writew(bdf, PCI_PREF_MEMORY_LIMIT, - (prefmem_end - 1) >> PCI_PREF_MEMORY_SHIFT); - pci_config_writel(bdf, PCI_PREF_BASE_UPPER32, 0); - pci_config_writel(bdf, PCI_PREF_LIMIT_UPPER32, 0); - - dprintf(1, "PCI: br io = [0x%x, 0x%x)\n", io_base, io_end); - dprintf(1, "PCI: br mem = [0x%x, 0x%x)\n", mem_base, mem_end); - dprintf(1, "PCI: br pref = [0x%x, 0x%x)\n", prefmem_base, prefmem_end); - - u16 cmd = pci_config_readw(bdf, PCI_COMMAND); - cmd &= ~PCI_COMMAND_IO; - if (io_end > io_base) { - cmd |= PCI_COMMAND_IO; - } - cmd &= ~PCI_COMMAND_MEMORY; - if (mem_end > mem_base || prefmem_end > prefmem_base) { - cmd |= PCI_COMMAND_MEMORY; - } - cmd |= PCI_COMMAND_MASTER; - pci_config_writew(bdf, PCI_COMMAND, cmd); - - pci_config_maskw(bdf, PCI_BRIDGE_CONTROL, 0, PCI_BRIDGE_CTL_SERR); -} -#endif - static void storage_ide_init(struct pci_device *pci, void *arg) { u16 bdf = pci->bdf; @@ -345,7 +172,6 @@ static void piix_ide_init(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 - pci_bios_allocate_regions(pci, NULL); }
static void pic_ibm_init(struct pci_device *pci, void *arg) @@ -379,15 +205,6 @@ static const struct pci_device_id pci_class_tbl[] = { PCI_DEVICE_CLASS(PCI_VENDOR_ID_APPLE, 0x0017, 0xff00, apple_macio_init), PCI_DEVICE_CLASS(PCI_VENDOR_ID_APPLE, 0x0022, 0xff00, apple_macio_init),
-#if 0 - /* PCI bridge */ - PCI_DEVICE_CLASS(PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_BRIDGE_PCI, - pci_bios_init_device_bridge), -#endif - - /* default */ - PCI_DEVICE(PCI_ANY_ID, PCI_ANY_ID, pci_bios_allocate_regions), - PCI_DEVICE_END, };
@@ -876,12 +693,6 @@ pci_setup(void) mem_holes[0].end = BUILD_IOAPIC_ADDR; mem_holes_count = 1;
- pci_region_init(&pci_bios_io_region, 0xc000, 64 * 1024 - 1); - pci_region_init(&pci_bios_mem_region, - BUILD_PCIMEM_START, BUILD_PCIMEM_END - 1); - pci_region_init(&pci_bios_prefmem_region, - BUILD_PCIPREFMEM_START, BUILD_PCIPREFMEM_END - 1); - dprintf(1, "=== PCI bus & bridge init ===\n"); pci_bios_init_bus();
@@ -898,14 +709,6 @@ pci_setup(void) dprintf(1, "=== PCI new allocation pass #2 ===\n"); pci_bios_map_device_in_bus(0 /* host bus */);
-#if 0 - dprintf(1, "=== PCI old allocation pass ===\n"); - struct pci_device *pci; - foreachpci(pci) { - pci_init_device(pci_isa_bridge_tbl, pci, NULL); - } -#endif - pci_bios_init_device_in_bus(0 /* host bus */);
free(busses); diff --git a/src/util.h b/src/util.h index 7034d43..d856d50 100644 --- a/src/util.h +++ b/src/util.h @@ -344,35 +344,6 @@ void make_bios_writable(void); void make_bios_readonly(void); void qemu_prep_reset(void);
-// pci_region.c -// region allocator. pci region allocates the requested region -// sequentially with overflow check. -struct pci_region { - // The region is [first, last]. - u32 first; - u32 last; - - // The next allocation starts from here. - // i.e. [start, cur_first) is allocated. - // Right after initialization cur_first == first. - u32 cur_first; -}; -// initialize the pci_region of [first, last] -// last must not be 0xffffffff -void pci_region_init(struct pci_region *r, u32 first, u32 last); -// allocate the region of size -u32 pci_region_alloc(struct pci_region *r, u32 size); -// make the next allocation aligned to align -u32 pci_region_align(struct pci_region *r, u32 align); -// revert the allocation to addr. -void pci_region_revert(struct pci_region *r, u32 addr); -// make the allocation fail. -u32 pci_region_disable(struct pci_region *r); -// returns the current allocation point. -u32 pci_region_addr(const struct pci_region *r); -// returns the region size. -u32 pci_region_size(const struct pci_region *r); - // pciinit.c extern const u8 pci_irqs[4]; void pci_setup(void);
Try to handle address space shortage by skipping any device which isn't essential for boot.
Signed-off-by: Gerd Hoffmann kraxel@redhat.com --- src/pci.c | 3 +++ src/pciinit.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++-- src/util.h | 2 ++ 3 files changed, 54 insertions(+), 2 deletions(-)
diff --git a/src/pci.c b/src/pci.c index 78bbac2..af735b2 100644 --- a/src/pci.c +++ b/src/pci.c @@ -193,6 +193,9 @@ pci_find_class(u16 classid) int pci_init_device(const struct pci_device_id *ids , struct pci_device *pci, void *arg) { + if (!pci_enable_device(pci)) + return -1; + while (ids->vendid || ids->class_mask) { if ((ids->vendid == PCI_ANY_ID || ids->vendid == pci->vendor) && (ids->devid == PCI_ANY_ID || ids->devid == pci->device) && diff --git a/src/pciinit.c b/src/pciinit.c index 5791ebe..bfbea29 100644 --- a/src/pciinit.c +++ b/src/pciinit.c @@ -42,6 +42,7 @@ static struct pci_bus { u32 io_base, mem_base, prefmem_base; } *busses; static int busses_count; +static int boot_devices_only = 0;
static void pci_bios_init_device_in_bus(int bus); static void pci_bios_check_device_in_bus(int bus); @@ -156,6 +157,40 @@ static const struct pci_device_id pci_isa_bridge_tbl[] = { #define PCI_PREF_MEMORY_ALIGN (1UL << 20) #define PCI_PREF_MEMORY_SHIFT 16
+static int is_boot_device(struct pci_device *pci) +{ + /* check base class */ + switch (pci->class >> 8) { + case PCI_BASE_CLASS_STORAGE: + case PCI_BASE_CLASS_NETWORK: + case PCI_BASE_CLASS_BRIDGE: + case PCI_BASE_CLASS_SYSTEM: + return 1; + } + + /* check class */ + switch (pci->class) { + case PCI_CLASS_DISPLAY_VGA: + case PCI_CLASS_SERIAL_USB: + return 1; + } + + /* check if ROM present */ + if (pci->r[PCI_ROM_SLOT].addr != 0) { + return 1; + } + + return 0; +} + +int pci_enable_device(struct pci_device *pci) +{ + if (boot_devices_only) { + return is_boot_device(pci); + } + return 1; +} + static void storage_ide_init(struct pci_device *pci, void *arg) { u16 bdf = pci->bdf; @@ -412,6 +447,9 @@ static void pci_bios_check_device(struct pci_bus *bus, struct pci_device *dev) u16 bdf = dev->bdf; int i;
+ if (!pci_enable_device(dev)) + return; + if (dev->class == PCI_CLASS_BRIDGE_PCI) { if (dev->secondary_bus >= busses_count) { /* should never trigger */ @@ -464,6 +502,9 @@ static void pci_bios_map_device(struct pci_bus *bus, struct pci_device *dev) u16 bdf = dev->bdf; int i;
+ if (!pci_enable_device(dev)) + return; + if (dev->class == PCI_CLASS_BRIDGE_PCI) { if (dev->secondary_bus >= busses_count) { return; @@ -702,8 +743,14 @@ pci_setup(void) dprintf(1, "=== PCI new allocation pass #1 ===\n"); pci_bios_check_device_in_bus(0 /* host bus */); if (pci_bios_init_root_regions() != 0) { - dprintf(1, "PCI: out of address space\n"); - /* Hmm, what do now? */ + dprintf(1, "=== PCI new allocation pass #1a (boot only) ===\n"); + boot_devices_only = 1; + memset(busses, 0, busses_count * sizeof(struct pci_bus)); + pci_bios_check_device_in_bus(0 /* host bus */); + if (pci_bios_init_root_regions() != 0) { + dprintf(1, "PCI: out of address space\n"); + /* Hmm, what do now? */ + } }
dprintf(1, "=== PCI new allocation pass #2 ===\n"); diff --git a/src/util.h b/src/util.h index d856d50..8020181 100644 --- a/src/util.h +++ b/src/util.h @@ -347,6 +347,8 @@ void qemu_prep_reset(void); // pciinit.c extern const u8 pci_irqs[4]; void pci_setup(void); +struct pci_device; +int pci_enable_device(struct pci_device *pci);
// smm.c void smm_init(void);
On 06/24/11 17:24, Gerd Hoffmann wrote:
Hi,
Removed the dust from the two-pass pci initialization code and posting the bits for review to move forward. Rebased from q35 tree to latest upstream master. Uses the new pci_device struct now.
Oops, one pretty essential call somehow got lost while rebasing the bits, incremental fix attached ...
cheers, Gerd