[SeaBIOS] [RfC PATCH 1/2] pci: move to two-pass pci initialization

Isaku Yamahata yamahata at valinux.co.jp
Wed May 25 06:33:53 CEST 2011


The patch doesn't check any overflows. And it assumes that
BAR assignment is always possible. However it isn't true.
Some people complained about it before, so I added overflow check.

Some devices have very huge BAR like 1G, 2G...
For example, qemu ivshmem or device-assignment of home-made device.
Those huge BAR would be unallocated with warning.
(Usually such huge BAR is 64bit, and we don't allocate 64bit BAR
to 4G+ area at the moment.)

Other possibility is that pci bus can have many child buses and
many devices.
Some people has expressed that they want several hundreds pci slots.
In this case, assign BAR as much as possible, and leave rest BARs
unallocated with warning.

The patch is RfC, so you simply haven't address those yet, though.

thanks,

On Wed, May 25, 2011 at 11:34:06AM +0900, Isaku Yamahata wrote:
> Basically it looks good. Great work. I've wanted to see this kind
> of patch. Some comments below.
> 
> On Tue, May 24, 2011 at 11:05:32AM +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.
> > 
> > TODO: actually use the calculated stuff.
> > 
> > Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>
> > ---
> >  src/pciinit.c |  352 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
> >  1 files changed, 350 insertions(+), 2 deletions(-)
> > 
> > diff --git a/src/pciinit.c b/src/pciinit.c
> > index 97df126..537eed0 100644
> > --- a/src/pciinit.c
> > +++ b/src/pciinit.c
> > @@ -17,12 +17,77 @@
> >  #define PCI_ROM_SLOT 6
> >  #define PCI_NUM_REGIONS 7
> >  
> > -static void pci_bios_init_device_in_bus(int bus);
> > +#define PCI_IO_INDEX_SHIFT 2
> > +#define PCI_MEM_INDEX_SHIFT 12
> > +
> > +#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 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[32];
> 
> Statically allocated? Should we be go for three pass by splitting
> the first pass into two?
> - 1.A pass: assign bus number and count the total number of pci buses.
> allocate busses by malloc_tmp() which will be freed later.
> - 1.B pass: calculate region size
> 
> 
> > +
> > +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 = 0;
> > +
> > +    while (size > (1 << index)) {
> > +        index++;
> > +    }
> > +    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
> > @@ -411,17 +476,296 @@ static const struct pci_device_id pci_mem_addr_tbl[] = {
> >      PCI_DEVICE_END,
> >  };
> >  
> > +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, u16 bdf)
> > +{
> > +    u16 class;
> > +    int i;
> > +
> > +    class = pci_config_readw(bdf, PCI_CLASS_DEVICE);
> > +    if (class == PCI_CLASS_BRIDGE_PCI) {
> > +        u8 secbus = pci_config_readb(bdf, PCI_SECONDARY_BUS);
> > +        if (secbus >= ARRAY_SIZE(busses)) {
> > +            dprintf(1, "PCI: busses array too small, skipping bus %d\n", secbus);
> > +            return;
> > +        }
> > +        struct pci_bus *s = busses + secbus;
> > +        pci_bios_check_device_in_bus(secbus);
> > +        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",
> > +                secbus, 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);
> > +
> > +        if (!(val & PCI_BASE_ADDRESS_SPACE_IO) &&
> > +            (val & PCI_BASE_ADDRESS_MEM_TYPE_MASK) == PCI_BASE_ADDRESS_MEM_TYPE_64) {
> > +            i++;
> > +        }
> > +    }
> > +}
> > +
> > +static void pci_bios_map_device(struct pci_bus *bus, u16 bdf)
> > +{
> > +    u16 class;
> > +    int i;
> > +
> > +    class = pci_config_readw(bdf, PCI_CLASS_DEVICE);
> > +    if (class == PCI_CLASS_BRIDGE_PCI) {
> > +        u8 secbus = pci_config_readb(bdf, PCI_SECONDARY_BUS);
> > +        if (secbus >= ARRAY_SIZE(busses)) {
> > +            return;
> > +        }
> > +        struct pci_bus *s = busses + secbus;
> > +        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", secbus);
> > +        pci_bios_init_bus_bases(s);
> > +        /* TODO: commit assignments */
> > +        pci_bios_map_device_in_bus(secbus);
> > +        return;
> > +    }
> > +
> > +    for (i = 0; i < PCI_NUM_REGIONS; i++) {
> > +        u32 val, size, addr;
> > +        pci_bios_bus_get_bar(bus, bdf, i, &val, &size);
> > +        if (val == 0) {
> > +            continue;
> > +        }
> > +
> > +        addr = pci_bios_bus_get_addr(bus, val, size);
> > +        dprintf(1, "  bar %d, addr %x, size %x [%s]\n",
> > +                i, addr, size,
> > +                val & PCI_BASE_ADDRESS_SPACE_IO ? "io" : "mem");
> > +        /* TODO: commit assignments */
> > +
> > +        if (!(val & PCI_BASE_ADDRESS_SPACE_IO) &&
> > +            (val & PCI_BASE_ADDRESS_MEM_TYPE_MASK) == PCI_BASE_ADDRESS_MEM_TYPE_64) {
> > +            i++;
> > +        }
> > +    }
> > +}
> > +
> > +static void pci_bios_check_device_in_bus(int bus)
> > +{
> > +    int bdf, max;
> > +
> > +    dprintf(1, "PCI: check devices bus %d\n", bus);
> > +    foreachpci_in_bus(bdf, max, bus) {
> > +        pci_bios_check_device(&busses[bus], bdf);
> > +    }
> > +}
> > +
> > +static void pci_bios_map_device_in_bus(int bus)
> > +{
> > +    int bdf, max;
> > +
> > +    foreachpci_in_bus(bdf, max, bus) {
> > +        dprintf(1, "PCI: map device bus %d, bfd 0x%x\n", bus, bdf);
> > +        pci_bios_map_device(&busses[bus], bdf);
> > +    }
> > +}
> > +
> > +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;
> > +    }
> > +}
> > +
> > +static void pci_bios_init_root_regions(void)
> > +{
> > +    struct pci_bus *bus = &busses[0];
> > +    u32 reserved = 0xffffffff - 0xfec00000 + 1;
> > +
> > +    /* calculate memory windows */
> > +    if (bus->mem_sum) {
> > +        u32 window = bus->mem_max;
> > +        while (bus->mem_sum + reserved > window) {
> > +            window += bus->mem_max;
> > +        }
> > +        bus->mem_base = 0xffffffff - window + 1;
> > +        reserved = 0xffffffff - bus->mem_base + 1;
> > +    }
> > +
> > +    if (bus->prefmem_sum) {
> > +        u32 window = bus->prefmem_max;
> > +        while (bus->prefmem_sum + reserved > window) {
> > +            window += bus->prefmem_max;
> > +        }
> > +        bus->prefmem_base = 0xffffffff - window + 1;
> > +        reserved = 0xffffffff - bus->prefmem_base + 1;
> > +    }
> > +
> > +    bus->io_base = 0xc000;
> > +
> > +    /* simple sanity check */
> > +    /* TODO: check e820 table */
> > +    if (bus->mem_base < RamSize) {
> > +        dprintf(1, "PCI: out of space for memory bars\n");
> > +        /* Hmm, what to do now? */
> > +    }
> > +
> > +    dprintf(1, "PCI: init bases bus 0 (primary)\n");
> > +    pci_bios_init_bus_bases(bus);
> > +}
> > +
> >  void
> >  pci_setup(void)
> >  {
> > +    int bdf, max;
> > +
> >      if (CONFIG_COREBOOT)
> >          // Already done by coreboot.
> >          return;
> >  
> >      dprintf(3, "pci setup\n");
> >  
> > +    dprintf(1, "=== PCI bus & bridge init ===\n");
> >      pci_bios_init_bus();
> >  
> > +    dprintf(1, "=== PCI new allocation pass #1 ===\n");
> > +    pci_bios_check_device_in_bus(0 /* host bus */);
> > +
> >      pci_region_init(&pci_bios_io_region, 0xc000, 64 * 1024 - 1);
> >      struct pci_mem_addr addr = {
> >          .pci_bios_mem_region = &pci_bios_mem_region,
> > @@ -429,7 +773,11 @@ pci_setup(void)
> >      };
> >      pci_find_init_device(pci_mem_addr_tbl, &addr);
> >  
> > -    int bdf, max;
> > +    dprintf(1, "=== PCI new allocation pass #2 ===\n");
> > +    pci_bios_init_root_regions();
> > +    pci_bios_map_device_in_bus(0 /* host bus */);
> > +
> > +    dprintf(1, "=== PCI old allocation pass ===\n");
> >      foreachpci(bdf, max) {
> >          pci_init_device(pci_isa_bridge_tbl, bdf, NULL);
> >      }
> > -- 
> > 1.7.1
> > 
> > 
> > _______________________________________________
> > SeaBIOS mailing list
> > SeaBIOS at seabios.org
> > http://www.seabios.org/mailman/listinfo/seabios
> > 
> 
> -- 
> yamahata
> 
> _______________________________________________
> SeaBIOS mailing list
> SeaBIOS at seabios.org
> http://www.seabios.org/mailman/listinfo/seabios
> 

-- 
yamahata



More information about the SeaBIOS mailing list