Load memory window setup for pci from host. This makes it possible for host to make sure setup matches hardware exactly: especially important for when ACPI tables are loaded from host. This will also make it easier to add more chipsets down the road.
Signed-off-by: Michael S. Tsirkin mst@redhat.com ---
Changes from v1: - fix bug in 64 bit range check - address Kevin's comments: move file load into pciinit.c dont reorder initialization sizeof style fix
src/pciinit.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 75 insertions(+), 16 deletions(-)
diff --git a/src/pciinit.c b/src/pciinit.c index bb9355f..a4a5bf5 100644 --- a/src/pciinit.c +++ b/src/pciinit.c @@ -13,6 +13,7 @@ #include "config.h" // CONFIG_* #include "memmap.h" // add_e820 #include "paravirt.h" // RamSize +#include "byteorder.h" // le64_to_cpu #include "dev-q35.h"
/* PM Timer ticks per second (HZ) */ @@ -61,6 +62,13 @@ struct pci_bus { struct pci_device *bus_dev; };
+struct pci_mem { + u64 start32; + u64 end32; + u64 start64; + u64 end64; +}; + static u32 pci_bar(struct pci_device *pci, int region_num) { if (region_num != PCI_ROM_SLOT) { @@ -361,6 +369,8 @@ static void pci_enable_default_vga(void)
void i440fx_mem_addr_setup(struct pci_device *dev, void *arg) { + if (arg) + /* use supplied memory */; if (RamSize <= 0x80000000) pcimem_start = 0x80000000; else if (RamSize <= 0xc0000000) @@ -383,8 +393,9 @@ void mch_mem_addr_setup(struct pci_device *dev, void *arg) pci_config_writel(bdf, Q35_HOST_BRIDGE_PCIEXBAR, lower); add_e820(addr, size, E820_RESERVED);
- /* setup pci i/o window (above mmconfig) */ - pcimem_start = addr + size; + /* unless done already, setup pci i/o window (above mmconfig) */ + if (!arg) + pcimem_start = addr + size;
pci_slot_get_irq = mch_pci_slot_get_irq; } @@ -397,11 +408,11 @@ static const struct pci_device_id pci_platform_tbl[] = { PCI_DEVICE_END };
-static void pci_bios_init_platform(void) +static void pci_bios_init_platform(struct pci_mem *mem) { struct pci_device *pci; foreachpci(pci) { - pci_init_device(pci_platform_tbl, pci, NULL); + pci_init_device(pci_platform_tbl, pci, mem); } }
@@ -762,10 +773,14 @@ static void pci_region_map_entries(struct pci_bus *busses, struct pci_region *r) } }
-static void pci_bios_map_devices(struct pci_bus *busses) +static void pci_bios_map_devices(struct pci_bus *busses, struct pci_mem *mem) { if (pci_bios_init_root_regions(busses)) { struct pci_region r64_mem, r64_pref; + + if (mem && mem->start64 >= mem->end64) + panic("PCI: out of 32bit address space\n"); + r64_mem.list = NULL; r64_pref.list = NULL; pci_region_migrate_64bit_entries(&busses[0].r[PCI_REGION_TYPE_MEM], @@ -781,14 +796,27 @@ static void pci_bios_map_devices(struct pci_bus *busses) u64 align_mem = pci_region_align(&r64_mem); u64 align_pref = pci_region_align(&r64_pref);
- r64_mem.base = ALIGN(0x100000000LL + RamSizeOver4G, align_mem); - r64_pref.base = ALIGN(r64_mem.base + sum_mem, align_pref); - pcimem64_start = r64_mem.base; - pcimem64_end = r64_pref.base + sum_pref; + if (mem) { + /* + * Non prefetcheable memory at start of the window, + * prefetcheable memory at the end. + * This way OS has the maximum flexibility for + * allocating the rest of the memory. + */ + r64_mem.base = ALIGN(mem->start64, align_mem); + r64_pref.base = ALIGN_DOWN(mem->end64 - sum_pref + 1, align_pref); + if (sum_pref && r64_pref.base < r64_mem.base + sum_mem) + panic("PCI: out of 64bit address space\n"); + } else { + r64_mem.base = ALIGN(0x100000000LL + RamSizeOver4G, align_mem); + r64_pref.base = ALIGN(r64_mem.base + sum_mem, align_pref); + pcimem64_start = r64_mem.base; + pcimem64_end = r64_pref.base + sum_pref; + }
pci_region_map_entries(busses, &r64_mem); pci_region_map_entries(busses, &r64_pref); - } else { + } else if (!mem) { // no bars mapped high -> drop 64bit window (see dsdt) pcimem64_start = 0; } @@ -801,11 +829,28 @@ static void pci_bios_map_devices(struct pci_bus *busses) } }
+static +struct pci_mem *pci_mem_get(void) +{ + int psize; + struct pci_mem *mem = romfile_loadfile("etc/pci-info", &psize); + if (!mem) + return NULL; + if (psize < sizeof(*mem)) { + free(mem); + return NULL; + } + mem->start32 = le64_to_cpu(mem->start32); + mem->end32 = le64_to_cpu(mem->end32); + mem->start64 = le64_to_cpu(mem->start64); + mem->end64 = le64_to_cpu(mem->end64); + return mem; +} +
/**************************************************************** * Main setup code ****************************************************************/ - void pci_setup(void) { @@ -823,25 +868,39 @@ pci_setup(void) dprintf(1, "=== PCI device probing ===\n"); pci_probe_devices();
- pcimem_start = RamSize; - pci_bios_init_platform(); + struct pci_mem *mem = pci_mem_get(); + + if (mem) { + pcimem_start = mem->start32; + pcimem_end = mem->end32; + pcimem64_start = mem->start64; + pcimem64_end = mem->end64; + } else { + pcimem_start = RamSize; + } + + pci_bios_init_platform(mem);
dprintf(1, "=== PCI new allocation pass #1 ===\n"); struct pci_bus *busses = malloc_tmp(sizeof(*busses) * (MaxPCIBus + 1)); if (!busses) { warn_noalloc(); - return; + goto done; } memset(busses, 0, sizeof(*busses) * (MaxPCIBus + 1)); if (pci_bios_check_devices(busses)) - return; + goto done;
dprintf(1, "=== PCI new allocation pass #2 ===\n"); - pci_bios_map_devices(busses); + pci_bios_map_devices(busses, mem);
pci_bios_init_devices();
free(busses);
pci_enable_default_vga(); + +done: + if (mem) + free(mem); }
On Tue, Apr 30, 2013 at 09:34:55AM +0300, Michael S. Tsirkin wrote:
Load memory window setup for pci from host. This makes it possible for host to make sure setup matches hardware exactly: especially important for when ACPI tables are loaded from host. This will also make it easier to add more chipsets down the road.
Signed-off-by: Michael S. Tsirkin mst@redhat.com
Changes from v1:
- fix bug in 64 bit range check
- address Kevin's comments: move file load into pciinit.c dont reorder initialization sizeof style fix
On a related note: end is currently one after last byte of the region. Would it be cleaner to rework code to have the address of the last byte there, as the name implies, and change the interface to qemu appropriately?
src/pciinit.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 75 insertions(+), 16 deletions(-)
diff --git a/src/pciinit.c b/src/pciinit.c index bb9355f..a4a5bf5 100644 --- a/src/pciinit.c +++ b/src/pciinit.c @@ -13,6 +13,7 @@ #include "config.h" // CONFIG_* #include "memmap.h" // add_e820 #include "paravirt.h" // RamSize +#include "byteorder.h" // le64_to_cpu #include "dev-q35.h"
/* PM Timer ticks per second (HZ) */ @@ -61,6 +62,13 @@ struct pci_bus { struct pci_device *bus_dev; };
+struct pci_mem {
- u64 start32;
- u64 end32;
- u64 start64;
- u64 end64;
+};
static u32 pci_bar(struct pci_device *pci, int region_num) { if (region_num != PCI_ROM_SLOT) { @@ -361,6 +369,8 @@ static void pci_enable_default_vga(void)
void i440fx_mem_addr_setup(struct pci_device *dev, void *arg) {
- if (arg)
- /* use supplied memory */; if (RamSize <= 0x80000000) pcimem_start = 0x80000000; else if (RamSize <= 0xc0000000)
@@ -383,8 +393,9 @@ void mch_mem_addr_setup(struct pci_device *dev, void *arg) pci_config_writel(bdf, Q35_HOST_BRIDGE_PCIEXBAR, lower); add_e820(addr, size, E820_RESERVED);
- /* setup pci i/o window (above mmconfig) */
- pcimem_start = addr + size;
/* unless done already, setup pci i/o window (above mmconfig) */
if (!arg)
pcimem_start = addr + size;
pci_slot_get_irq = mch_pci_slot_get_irq;
} @@ -397,11 +408,11 @@ static const struct pci_device_id pci_platform_tbl[] = { PCI_DEVICE_END };
-static void pci_bios_init_platform(void) +static void pci_bios_init_platform(struct pci_mem *mem) { struct pci_device *pci; foreachpci(pci) {
pci_init_device(pci_platform_tbl, pci, NULL);
}pci_init_device(pci_platform_tbl, pci, mem);
}
@@ -762,10 +773,14 @@ static void pci_region_map_entries(struct pci_bus *busses, struct pci_region *r) } }
-static void pci_bios_map_devices(struct pci_bus *busses) +static void pci_bios_map_devices(struct pci_bus *busses, struct pci_mem *mem) { if (pci_bios_init_root_regions(busses)) { struct pci_region r64_mem, r64_pref;
if (mem && mem->start64 >= mem->end64)
panic("PCI: out of 32bit address space\n");
r64_mem.list = NULL; r64_pref.list = NULL; pci_region_migrate_64bit_entries(&busses[0].r[PCI_REGION_TYPE_MEM],
@@ -781,14 +796,27 @@ static void pci_bios_map_devices(struct pci_bus *busses) u64 align_mem = pci_region_align(&r64_mem); u64 align_pref = pci_region_align(&r64_pref);
r64_mem.base = ALIGN(0x100000000LL + RamSizeOver4G, align_mem);
r64_pref.base = ALIGN(r64_mem.base + sum_mem, align_pref);
pcimem64_start = r64_mem.base;
pcimem64_end = r64_pref.base + sum_pref;
if (mem) {
/*
* Non prefetcheable memory at start of the window,
* prefetcheable memory at the end.
* This way OS has the maximum flexibility for
* allocating the rest of the memory.
*/
r64_mem.base = ALIGN(mem->start64, align_mem);
r64_pref.base = ALIGN_DOWN(mem->end64 - sum_pref + 1, align_pref);
if (sum_pref && r64_pref.base < r64_mem.base + sum_mem)
panic("PCI: out of 64bit address space\n");
} else {
r64_mem.base = ALIGN(0x100000000LL + RamSizeOver4G, align_mem);
r64_pref.base = ALIGN(r64_mem.base + sum_mem, align_pref);
pcimem64_start = r64_mem.base;
pcimem64_end = r64_pref.base + sum_pref;
} pci_region_map_entries(busses, &r64_mem); pci_region_map_entries(busses, &r64_pref);
- } else {
- } else if (!mem) { // no bars mapped high -> drop 64bit window (see dsdt) pcimem64_start = 0; }
@@ -801,11 +829,28 @@ static void pci_bios_map_devices(struct pci_bus *busses) } }
+static +struct pci_mem *pci_mem_get(void) +{
- int psize;
- struct pci_mem *mem = romfile_loadfile("etc/pci-info", &psize);
- if (!mem)
return NULL;
- if (psize < sizeof(*mem)) {
free(mem);
return NULL;
- }
- mem->start32 = le64_to_cpu(mem->start32);
- mem->end32 = le64_to_cpu(mem->end32);
- mem->start64 = le64_to_cpu(mem->start64);
- mem->end64 = le64_to_cpu(mem->end64);
- return mem;
+}
/****************************************************************
- Main setup code
****************************************************************/
void pci_setup(void) { @@ -823,25 +868,39 @@ pci_setup(void) dprintf(1, "=== PCI device probing ===\n"); pci_probe_devices();
- pcimem_start = RamSize;
- pci_bios_init_platform();
struct pci_mem *mem = pci_mem_get();
if (mem) {
pcimem_start = mem->start32;
pcimem_end = mem->end32;
pcimem64_start = mem->start64;
pcimem64_end = mem->end64;
} else {
pcimem_start = RamSize;
}
pci_bios_init_platform(mem);
dprintf(1, "=== PCI new allocation pass #1 ===\n"); struct pci_bus *busses = malloc_tmp(sizeof(*busses) * (MaxPCIBus + 1)); if (!busses) { warn_noalloc();
return;
} memset(busses, 0, sizeof(*busses) * (MaxPCIBus + 1)); if (pci_bios_check_devices(busses))goto done;
return;
goto done;
dprintf(1, "=== PCI new allocation pass #2 ===\n");
- pci_bios_map_devices(busses);
pci_bios_map_devices(busses, mem);
pci_bios_init_devices();
free(busses);
pci_enable_default_vga();
+done:
- if (mem)
free(mem);
}
MST
On Tue, Apr 30, 2013 at 09:34:55AM +0300, Michael S. Tsirkin wrote:
Load memory window setup for pci from host. This makes it possible for host to make sure setup matches hardware exactly: especially important for when ACPI tables are loaded from host. This will also make it easier to add more chipsets down the road.
Signed-off-by: Michael S. Tsirkin mst@redhat.com
Changes from v1:
- fix bug in 64 bit range check
- address Kevin's comments: move file load into pciinit.c dont reorder initialization sizeof style fix
src/pciinit.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 75 insertions(+), 16 deletions(-)
QEMU now includes these interfaces - could the bios side be merged please?
diff --git a/src/pciinit.c b/src/pciinit.c index bb9355f..a4a5bf5 100644 --- a/src/pciinit.c +++ b/src/pciinit.c @@ -13,6 +13,7 @@ #include "config.h" // CONFIG_* #include "memmap.h" // add_e820 #include "paravirt.h" // RamSize +#include "byteorder.h" // le64_to_cpu #include "dev-q35.h"
/* PM Timer ticks per second (HZ) */ @@ -61,6 +62,13 @@ struct pci_bus { struct pci_device *bus_dev; };
+struct pci_mem {
- u64 start32;
- u64 end32;
- u64 start64;
- u64 end64;
+};
static u32 pci_bar(struct pci_device *pci, int region_num) { if (region_num != PCI_ROM_SLOT) { @@ -361,6 +369,8 @@ static void pci_enable_default_vga(void)
void i440fx_mem_addr_setup(struct pci_device *dev, void *arg) {
- if (arg)
- /* use supplied memory */; if (RamSize <= 0x80000000) pcimem_start = 0x80000000; else if (RamSize <= 0xc0000000)
@@ -383,8 +393,9 @@ void mch_mem_addr_setup(struct pci_device *dev, void *arg) pci_config_writel(bdf, Q35_HOST_BRIDGE_PCIEXBAR, lower); add_e820(addr, size, E820_RESERVED);
- /* setup pci i/o window (above mmconfig) */
- pcimem_start = addr + size;
/* unless done already, setup pci i/o window (above mmconfig) */
if (!arg)
pcimem_start = addr + size;
pci_slot_get_irq = mch_pci_slot_get_irq;
} @@ -397,11 +408,11 @@ static const struct pci_device_id pci_platform_tbl[] = { PCI_DEVICE_END };
-static void pci_bios_init_platform(void) +static void pci_bios_init_platform(struct pci_mem *mem) { struct pci_device *pci; foreachpci(pci) {
pci_init_device(pci_platform_tbl, pci, NULL);
}pci_init_device(pci_platform_tbl, pci, mem);
}
@@ -762,10 +773,14 @@ static void pci_region_map_entries(struct pci_bus *busses, struct pci_region *r) } }
-static void pci_bios_map_devices(struct pci_bus *busses) +static void pci_bios_map_devices(struct pci_bus *busses, struct pci_mem *mem) { if (pci_bios_init_root_regions(busses)) { struct pci_region r64_mem, r64_pref;
if (mem && mem->start64 >= mem->end64)
panic("PCI: out of 32bit address space\n");
r64_mem.list = NULL; r64_pref.list = NULL; pci_region_migrate_64bit_entries(&busses[0].r[PCI_REGION_TYPE_MEM],
@@ -781,14 +796,27 @@ static void pci_bios_map_devices(struct pci_bus *busses) u64 align_mem = pci_region_align(&r64_mem); u64 align_pref = pci_region_align(&r64_pref);
r64_mem.base = ALIGN(0x100000000LL + RamSizeOver4G, align_mem);
r64_pref.base = ALIGN(r64_mem.base + sum_mem, align_pref);
pcimem64_start = r64_mem.base;
pcimem64_end = r64_pref.base + sum_pref;
if (mem) {
/*
* Non prefetcheable memory at start of the window,
* prefetcheable memory at the end.
* This way OS has the maximum flexibility for
* allocating the rest of the memory.
*/
r64_mem.base = ALIGN(mem->start64, align_mem);
r64_pref.base = ALIGN_DOWN(mem->end64 - sum_pref + 1, align_pref);
if (sum_pref && r64_pref.base < r64_mem.base + sum_mem)
panic("PCI: out of 64bit address space\n");
} else {
r64_mem.base = ALIGN(0x100000000LL + RamSizeOver4G, align_mem);
r64_pref.base = ALIGN(r64_mem.base + sum_mem, align_pref);
pcimem64_start = r64_mem.base;
pcimem64_end = r64_pref.base + sum_pref;
} pci_region_map_entries(busses, &r64_mem); pci_region_map_entries(busses, &r64_pref);
- } else {
- } else if (!mem) { // no bars mapped high -> drop 64bit window (see dsdt) pcimem64_start = 0; }
@@ -801,11 +829,28 @@ static void pci_bios_map_devices(struct pci_bus *busses) } }
+static +struct pci_mem *pci_mem_get(void) +{
- int psize;
- struct pci_mem *mem = romfile_loadfile("etc/pci-info", &psize);
- if (!mem)
return NULL;
- if (psize < sizeof(*mem)) {
free(mem);
return NULL;
- }
- mem->start32 = le64_to_cpu(mem->start32);
- mem->end32 = le64_to_cpu(mem->end32);
- mem->start64 = le64_to_cpu(mem->start64);
- mem->end64 = le64_to_cpu(mem->end64);
- return mem;
+}
/****************************************************************
- Main setup code
****************************************************************/
void pci_setup(void) { @@ -823,25 +868,39 @@ pci_setup(void) dprintf(1, "=== PCI device probing ===\n"); pci_probe_devices();
- pcimem_start = RamSize;
- pci_bios_init_platform();
struct pci_mem *mem = pci_mem_get();
if (mem) {
pcimem_start = mem->start32;
pcimem_end = mem->end32;
pcimem64_start = mem->start64;
pcimem64_end = mem->end64;
} else {
pcimem_start = RamSize;
}
pci_bios_init_platform(mem);
dprintf(1, "=== PCI new allocation pass #1 ===\n"); struct pci_bus *busses = malloc_tmp(sizeof(*busses) * (MaxPCIBus + 1)); if (!busses) { warn_noalloc();
return;
} memset(busses, 0, sizeof(*busses) * (MaxPCIBus + 1)); if (pci_bios_check_devices(busses))goto done;
return;
goto done;
dprintf(1, "=== PCI new allocation pass #2 ===\n");
- pci_bios_map_devices(busses);
pci_bios_map_devices(busses, mem);
pci_bios_init_devices();
free(busses);
pci_enable_default_vga();
+done:
- if (mem)
free(mem);
}
MST
On Tue, Apr 30, 2013 at 09:34:55AM +0300, Michael S. Tsirkin wrote:
Load memory window setup for pci from host. This makes it possible for host to make sure setup matches hardware exactly: especially important for when ACPI tables are loaded from host. This will also make it easier to add more chipsets down the road.
I'm struggling to understand this patch. I think the code may need some refactoring.
[...]
--- a/src/pciinit.c +++ b/src/pciinit.c @@ -13,6 +13,7 @@ #include "config.h" // CONFIG_* #include "memmap.h" // add_e820 #include "paravirt.h" // RamSize +#include "byteorder.h" // le64_to_cpu #include "dev-q35.h"
/* PM Timer ticks per second (HZ) */ @@ -61,6 +62,13 @@ struct pci_bus { struct pci_device *bus_dev; };
+struct pci_mem {
- u64 start32;
SeaBIOS uses spaces; not tabs.
[...]
void i440fx_mem_addr_setup(struct pci_device *dev, void *arg) {
- if (arg)
- /* use supplied memory */;
Huh?
[...]
/****************************************************************
- Main setup code
****************************************************************/
void pci_setup(void) { @@ -823,25 +868,39 @@ pci_setup(void) dprintf(1, "=== PCI device probing ===\n"); pci_probe_devices();
- pcimem_start = RamSize;
- pci_bios_init_platform();
struct pci_mem *mem = pci_mem_get();
if (mem) {
pcimem_start = mem->start32;
pcimem_end = mem->end32;
pcimem64_start = mem->start64;
pcimem64_end = mem->end64;
} else {
pcimem_start = RamSize;
}
pci_bios_init_platform(mem);
dprintf(1, "=== PCI new allocation pass #1 ===\n"); struct pci_bus *busses = malloc_tmp(sizeof(*busses) * (MaxPCIBus + 1)); if (!busses) { warn_noalloc();
return;
} memset(busses, 0, sizeof(*busses) * (MaxPCIBus + 1)); if (pci_bios_check_devices(busses))goto done;
return;
goto done;
dprintf(1, "=== PCI new allocation pass #2 ===\n");
- pci_bios_map_devices(busses);
pci_bios_map_devices(busses, mem);
pci_bios_init_devices();
free(busses);
pci_enable_default_vga();
+done:
- if (mem)
free(mem);
I don't understand why dynamic memory is used, and I don't understand why "mem" is passed around and inspected in so many places.
-Kevin
On Sun, Jul 14, 2013 at 06:51:11PM -0400, Kevin O'Connor wrote:
On Tue, Apr 30, 2013 at 09:34:55AM +0300, Michael S. Tsirkin wrote:
Load memory window setup for pci from host. This makes it possible for host to make sure setup matches hardware exactly: especially important for when ACPI tables are loaded from host. This will also make it easier to add more chipsets down the road.
I'm struggling to understand this patch. I think the code may need some refactoring.
[...]
--- a/src/pciinit.c +++ b/src/pciinit.c @@ -13,6 +13,7 @@ #include "config.h" // CONFIG_* #include "memmap.h" // add_e820 #include "paravirt.h" // RamSize +#include "byteorder.h" // le64_to_cpu #include "dev-q35.h"
/* PM Timer ticks per second (HZ) */ @@ -61,6 +62,13 @@ struct pci_bus { struct pci_device *bus_dev; };
+struct pci_mem {
- u64 start32;
SeaBIOS uses spaces; not tabs.
[...]
void i440fx_mem_addr_setup(struct pci_device *dev, void *arg) {
- if (arg)
- /* use supplied memory */;
Huh?
[...]
/****************************************************************
- Main setup code
****************************************************************/
void pci_setup(void) { @@ -823,25 +868,39 @@ pci_setup(void) dprintf(1, "=== PCI device probing ===\n"); pci_probe_devices();
- pcimem_start = RamSize;
- pci_bios_init_platform();
struct pci_mem *mem = pci_mem_get();
if (mem) {
pcimem_start = mem->start32;
pcimem_end = mem->end32;
pcimem64_start = mem->start64;
pcimem64_end = mem->end64;
} else {
pcimem_start = RamSize;
}
pci_bios_init_platform(mem);
dprintf(1, "=== PCI new allocation pass #1 ===\n"); struct pci_bus *busses = malloc_tmp(sizeof(*busses) * (MaxPCIBus + 1)); if (!busses) { warn_noalloc();
return;
} memset(busses, 0, sizeof(*busses) * (MaxPCIBus + 1)); if (pci_bios_check_devices(busses))goto done;
return;
goto done;
dprintf(1, "=== PCI new allocation pass #2 ===\n");
- pci_bios_map_devices(busses);
pci_bios_map_devices(busses, mem);
pci_bios_init_devices();
free(busses);
pci_enable_default_vga();
+done:
- if (mem)
free(mem);
I don't understand why dynamic memory is used, and I don't understand why "mem" is passed around and inspected in so many places.
-Kevin
This was an attempt to keep the patch minimal. If we don't want to test the flag in many places, code will have to be refactored.