[SeaBIOS] [PATCH 5/5] pci: add prefmem64 region type.

Gerd Hoffmann kraxel at redhat.com
Thu Feb 16 18:21:36 CET 2012


This patch adds a prefmem64 region type.  64bit prefmem pci bars are
assigned to that region if the device is either on the root bus or if
they are behind a 64bit capable bridge and all other prefmem bars behind
that bridge are 64bit capable too.

The patch also 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).  It also
avoids assigning ressources for both prefmem and prefmem64 ;)

With this patch applied seabios is able to map 64bit bars above 4G.

TODO: detect 64bit capable bridges.

Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>
---
 src/acpi-dsdt.dsl |    7 +++
 src/config.h      |    2 +
 src/pciinit.c     |  107 +++++++++++++++++++++++++++++++++++++++-------------
 3 files changed, 89 insertions(+), 27 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/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 a98f6e3..927e59a 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,6 +44,7 @@ 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;
 };
 
@@ -65,13 +68,17 @@ 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)
+    if (!bar->isprefetch)
+        return PCI_REGION_TYPE_MEM;
+    if (!bar->is64)
         return PCI_REGION_TYPE_PREFMEM;
-    return PCI_REGION_TYPE_MEM;
+    if (bus->bus_dev != NULL && !bus->use_prefmem64)
+        return PCI_REGION_TYPE_PREFMEM;
+    return PCI_REGION_TYPE_PREFMEM64;
 }
 
 static u32 pci_bar(struct pci_device *pci, int region_num)
@@ -410,11 +417,16 @@ static void pci_bios_check_devices(struct pci_bus *busses)
             continue;
         bus = &busses[pci->secondary_bus];
         bus->bus_dev = pci;
+        /*
+         * TODO: figure whenever the bridge can handle 64bit prefmem
+         * and set bus->use_prefmem64 if so.
+         */
     }
 
     // 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 +435,10 @@ 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) {
+                bus->use_prefmem64 = 0;
+            }
+
             if (pci->bars[i].is64)
                 i++;
         }
@@ -437,7 +453,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);
 
@@ -457,28 +473,38 @@ 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;
             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 %llx, mem %llx, prefmem %llx\n",
+        dprintf(1, "PCI: secondary bus %d sizes: 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;
@@ -490,6 +516,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;
 }
 
@@ -504,6 +536,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);
         base = bus->r[type].base;
@@ -512,7 +546,7 @@ static void pci_bios_init_bus_bases(struct pci_bus *bus)
             if (!bus->r[type].count[i])
                 continue;
             newbase = base + size * bus->r[type].count[i];
-            dprintf(1, "    size %8llx: %d bar(s), %8llx -> %8llx\n",
+            dprintf(1, "    size %llx: %d bar(s), %llx -> %llx\n",
                     size, bus->r[type].count[i], base, newbase - 1);
             bus->r[type].bases[i] = base;
             base = newbase;
@@ -520,9 +554,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];
@@ -550,30 +585,51 @@ 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 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;
+        }
         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.
@@ -588,7 +644,7 @@ 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);
+            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]);
@@ -617,9 +673,6 @@ pci_setup(void)
 
     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;
@@ -637,7 +690,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");
     }
 
-- 
1.7.1




More information about the SeaBIOS mailing list