Hi,
Time to repost, aiming for merge. The patches survived my local testing and win2k8 testing by Alexey. Patch #4 got some finishing touches: Tries two 64bit I/O window sizes only, also added a comment explaining why it is done. Rebased to latest master.
cheers, Gerd
Gerd Hoffmann (4): update bios date pciinit: make pci ressources configurable update dsdt ressources at runtime pci: runtime i/o window sizing
src/acpi-dsdt.dsl | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++-- src/acpi.c | 28 ++++++++++++++++++++++- src/acpi.h | 9 +++++++ src/pci.h | 2 + src/pciinit.c | 46 +++++++++++++++++++++++++++++++------ src/smbios.c | 2 +- 6 files changed, 139 insertions(+), 13 deletions(-)
Linux ignores some information from acpi in case the bios is old as acpi support used to be buggy in the early days. This affects ressources for example (unless forced with pci=use_crs). So lets go for something more recent. With this patch applied the ressources for \SB.PCI0 will show up in /proc/{iomem,ioports}, tagged as "PCI Bus 0000:00".
Signed-off-by: Gerd Hoffmann kraxel@redhat.com --- src/smbios.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/src/smbios.c b/src/smbios.c index 20d2d47..fc84aad 100644 --- a/src/smbios.c +++ b/src/smbios.c @@ -93,7 +93,7 @@ smbios_entry_point_init(u16 max_structure_size, } while (0)
/* Type 0 -- BIOS Information */ -#define RELEASE_DATE_STR "01/01/2007" +#define RELEASE_DATE_STR "01/01/2011" static void * smbios_init_type_0(void *start) {
But its 2012 not 2011 now...
On Thu, Jun 7, 2012 at 10:34 AM, Gerd Hoffmann kraxel@redhat.com wrote:
Linux ignores some information from acpi in case the bios is old as acpi support used to be buggy in the early days. This affects ressources for example (unless forced with pci=use_crs). So lets go for something more recent. With this patch applied the ressources for \SB.PCI0 will show up in /proc/{iomem,ioports}, tagged as "PCI Bus 0000:00".
Signed-off-by: Gerd Hoffmann kraxel@redhat.com
src/smbios.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/src/smbios.c b/src/smbios.c index 20d2d47..fc84aad 100644 --- a/src/smbios.c +++ b/src/smbios.c @@ -93,7 +93,7 @@ smbios_entry_point_init(u16 max_structure_size, } while (0)
/* Type 0 -- BIOS Information */ -#define RELEASE_DATE_STR "01/01/2007" +#define RELEASE_DATE_STR "01/01/2011" static void * smbios_init_type_0(void *start) { -- 1.7.1
SeaBIOS mailing list SeaBIOS@seabios.org http://www.seabios.org/mailman/listinfo/seabios
This patch adds variables for the pci io window, so they are runtime-configurable. They are initialized with the values from config.h
Signed-off-by: Gerd Hoffmann kraxel@redhat.com --- src/pci.h | 2 ++ src/pciinit.c | 15 ++++++++++----- 2 files changed, 12 insertions(+), 5 deletions(-)
diff --git a/src/pci.h b/src/pci.h index 6be838c..ebf934c 100644 --- a/src/pci.h +++ b/src/pci.h @@ -56,6 +56,8 @@ struct pci_device { // Local information on device. int have_driver; }; +extern u64 pcimem_start, pcimem_end; +extern u64 pcimem64_start, pcimem64_end; extern struct pci_device *PCIDevices; extern int MaxPCIBus; int pci_probe_host(void); diff --git a/src/pciinit.c b/src/pciinit.c index 8452572..dc33f83 100644 --- a/src/pciinit.c +++ b/src/pciinit.c @@ -30,6 +30,11 @@ static const char *region_type_name[] = { [ PCI_REGION_TYPE_PREFMEM ] = "prefmem", };
+u64 pcimem_start = BUILD_PCIMEM_START; +u64 pcimem_end = BUILD_PCIMEM_END; +u64 pcimem64_start = BUILD_PCIMEM64_START; +u64 pcimem64_end = BUILD_PCIMEM64_END; + struct pci_region_entry { struct pci_device *dev; int bar; @@ -520,13 +525,13 @@ static int pci_bios_init_root_regions(struct pci_bus *bus) } u64 sum = pci_region_sum(r_end); u64 align = pci_region_align(r_end); - r_end->base = ALIGN_DOWN((BUILD_PCIMEM_END - sum), align); + r_end->base = ALIGN_DOWN((pcimem_end - sum), align); sum = pci_region_sum(r_start); align = pci_region_align(r_start); r_start->base = ALIGN_DOWN((r_end->base - sum), align);
- if ((r_start->base < BUILD_PCIMEM_START) || - (r_start->base > BUILD_PCIMEM_END)) + if ((r_start->base < pcimem_start) || + (r_start->base > pcimem_end)) // Memory range requested is larger than available. return -1; return 0; @@ -599,11 +604,11 @@ static void pci_bios_map_devices(struct pci_bus *busses) if (pci_bios_init_root_regions(busses)) panic("PCI: out of 32bit address space\n");
- r64_mem.base = BUILD_PCIMEM64_START; + r64_mem.base = pcimem64_start; u64 sum = pci_region_sum(&r64_mem); u64 align = pci_region_align(&r64_pref); r64_pref.base = ALIGN(r64_mem.base + sum, align); - if (r64_pref.base + pci_region_sum(&r64_pref) > BUILD_PCIMEM64_END) + if (r64_pref.base + pci_region_sum(&r64_pref) > pcimem64_end) panic("PCI: out of 64bit address space\n"); pci_region_map_entries(busses, &r64_mem); pci_region_map_entries(busses, &r64_pref);
Write the pci window location to memory and add a pointer to the SSDT (BDAT region). Turn \SB.PCI0._CRS into a method which looks up the information there and updates the ressources accordingly.
Signed-off-by: Gerd Hoffmann kraxel@redhat.com --- src/acpi-dsdt.dsl | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++-- src/acpi.c | 28 ++++++++++++++++++++++- src/acpi.h | 9 +++++++ 3 files changed, 98 insertions(+), 4 deletions(-)
diff --git a/src/acpi-dsdt.dsl b/src/acpi-dsdt.dsl index 4bdc268..5305ff9 100644 --- a/src/acpi-dsdt.dsl +++ b/src/acpi-dsdt.dsl @@ -59,7 +59,6 @@ DefinitionBlock ( } }
- /**************************************************************** * PCI Bus definition ****************************************************************/ @@ -132,7 +131,7 @@ DefinitionBlock ( B0EJ, 32, }
- Name (_CRS, ResourceTemplate () + Name (CRES, ResourceTemplate () { WordBusNumber (ResourceProducer, MinFixed, MaxFixed, PosDecode, 0x0000, // Address Space Granularity @@ -174,8 +173,68 @@ DefinitionBlock ( 0xFEBFFFFF, // Address Range Maximum 0x00000000, // Address Translation Offset 0x1EC00000, // Address Length - ,, , AddressRangeMemory, TypeStatic) + ,, PW32, AddressRangeMemory, TypeStatic) }) + Name (CR64, ResourceTemplate () + { + 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 + ,, PW64, AddressRangeMemory, TypeStatic) + }) + Method (_CRS, 0) + { + /* see see acpi.h, struct bfld */ + External (BDAT, OpRegionObj) + Field(BDAT, QWordAcc, NoLock, Preserve) { + P0S, 64, + P0E, 64, + P0L, 64, + P1S, 64, + P1E, 64, + P1L, 64, + } + Field(BDAT, DWordAcc, NoLock, Preserve) { + P0SL, 32, + P0SH, 32, + P0EL, 32, + P0EH, 32, + P0LL, 32, + P0LH, 32, + P1SL, 32, + P1SH, 32, + P1EL, 32, + P1EH, 32, + P1LL, 32, + P1LH, 32, + } + + /* fixup 32bit pci io window */ + CreateDWordField (CRES,_SB.PCI0.PW32._MIN, PS32) + CreateDWordField (CRES,_SB.PCI0.PW32._MAX, PE32) + CreateDWordField (CRES,_SB.PCI0.PW32._LEN, PL32) + Store (P0SL, PS32) + Store (P0EL, PE32) + Store (P0LL, PL32) + + If (LAnd(LEqual(P1SL, 0x00), LEqual(P1SH, 0x00))) { + Return (CRES) + } Else { + /* fixup 64bit pci io window */ + CreateQWordField (CR64,_SB.PCI0.PW64._MIN, PS64) + CreateQWordField (CR64,_SB.PCI0.PW64._MAX, PE64) + CreateQWordField (CR64,_SB.PCI0.PW64._LEN, PL64) + Store (P1S, PS64) + Store (P1E, PE64) + Store (P1L, PL64) + /* add window and return result */ + ConcatenateResTemplate (CRES, CR64, Local0) + Return (Local0) + } + } } }
diff --git a/src/acpi.c b/src/acpi.c index 5387183..365267b 100644 --- a/src/acpi.c +++ b/src/acpi.c @@ -415,7 +415,8 @@ build_ssdt(void) int length = ((1+3+4) + (acpi_cpus * SD_SIZEOF) + (1+2+5+(12*acpi_cpus)) - + (6+2+1+(1*acpi_cpus))); + + (6+2+1+(1*acpi_cpus)) + + 17); u8 *ssdt = malloc_high(sizeof(struct acpi_table_header) + length); if (! ssdt) { warn_noalloc(); @@ -477,6 +478,31 @@ build_ssdt(void) for (i=0; i<acpi_cpus; i++) *(ssdt_ptr++) = (i < CountCPUs) ? 0x01 : 0x00;
+ // store pci io windows: start, end, length + // this way we don't have to do the math in the dsdt + struct bfld *bfld = malloc_high(sizeof(struct bfld)); + bfld->p0s = pcimem_start; + bfld->p0e = pcimem_end - 1; + bfld->p0l = pcimem_end - pcimem_start; + bfld->p1s = pcimem64_start; + bfld->p1e = pcimem64_end - 1; + bfld->p1l = pcimem64_end - pcimem64_start; + + // build "OperationRegion(BDAT, SystemMemory, 0x12345678, 0x87654321)" + *(ssdt_ptr++) = 0x5B; // ExtOpPrefix + *(ssdt_ptr++) = 0x80; // OpRegionOp + *(ssdt_ptr++) = 'B'; + *(ssdt_ptr++) = 'D'; + *(ssdt_ptr++) = 'A'; + *(ssdt_ptr++) = 'T'; + *(ssdt_ptr++) = 0x00; // SystemMemory + *(ssdt_ptr++) = 0x0C; // DWordPrefix + *(u32*)ssdt_ptr = (u32)bfld; + ssdt_ptr += 4; + *(ssdt_ptr++) = 0x0C; // DWordPrefix + *(u32*)ssdt_ptr = sizeof(struct bfld); + ssdt_ptr += 4; + build_header((void*)ssdt, SSDT_SIGNATURE, ssdt_ptr - ssdt, 1);
//hexdump(ssdt, ssdt_ptr - ssdt); diff --git a/src/acpi.h b/src/acpi.h index e01315a..cb21561 100644 --- a/src/acpi.h +++ b/src/acpi.h @@ -98,4 +98,13 @@ struct fadt_descriptor_rev1 #endif } PACKED;
+struct bfld { + u64 p0s; /* pci window 0 (below 4g) - start */ + u64 p0e; /* pci window 0 (below 4g) - end */ + u64 p0l; /* pci window 0 (below 4g) - length */ + u64 p1s; /* pci window 1 (above 4g) - start */ + u64 p1e; /* pci window 1 (above 4g) - end */ + u64 p1l; /* pci window 1 (above 4g) - length */ +} PACKED; + #endif // acpi.h
Update the pci i/o windows at runtime, depending on the amount memory the machine has. The 32bit window starts above low memory and ends at the ioapic map address, the 64bit window is placed above high memory.
Signed-off-by: Gerd Hoffmann kraxel@redhat.com --- src/pciinit.c | 33 +++++++++++++++++++++++++++++---- 1 files changed, 29 insertions(+), 4 deletions(-)
diff --git a/src/pciinit.c b/src/pciinit.c index dc33f83..dd74963 100644 --- a/src/pciinit.c +++ b/src/pciinit.c @@ -592,6 +592,20 @@ static void pci_region_map_entries(struct pci_bus *busses, struct pci_region *r)
static void pci_bios_map_devices(struct pci_bus *busses) { + /* + * Try smaller 64bit windows first, sized to be half of the + * maximum physical address space of common cpus. + * Your cpu: try 'grep "address sizes" /proc/cpuinfo' + */ + static const u64 sizes[] = { + (1LL << 35), // 36 bits physical (64 GB, every PAE-capable cpu can do this) + (1LL << 39), // 40 bits physical (1 TB) + }; + u64 pcimem64_size; + int i; + + pcimem_start = RamSize; + if (pci_bios_init_root_regions(busses)) { struct pci_region r64_mem, r64_pref; r64_mem.list = NULL; @@ -604,14 +618,25 @@ static void pci_bios_map_devices(struct pci_bus *busses) if (pci_bios_init_root_regions(busses)) panic("PCI: out of 32bit address space\n");
- r64_mem.base = pcimem64_start; - u64 sum = pci_region_sum(&r64_mem); - u64 align = pci_region_align(&r64_pref); - r64_pref.base = ALIGN(r64_mem.base + sum, align); + for (i = 0; i < ARRAY_SIZE(sizes); i++) { + pcimem64_size = sizes[i]; + pcimem64_start = ALIGN(0x100000000LL + RamSizeOver4G, pcimem64_size); + pcimem64_end = pcimem64_start + pcimem64_size; + + r64_mem.base = pcimem64_start; + u64 sum = pci_region_sum(&r64_mem); + u64 align = pci_region_align(&r64_pref); + r64_pref.base = ALIGN(r64_mem.base + sum, align); + if (r64_pref.base + pci_region_sum(&r64_pref) <= pcimem64_end) + break; + } if (r64_pref.base + pci_region_sum(&r64_pref) > pcimem64_end) panic("PCI: out of 64bit address space\n"); pci_region_map_entries(busses, &r64_mem); pci_region_map_entries(busses, &r64_pref); + } else { + // no bars mapped high -> drop 64bit window (see dsdt) + pcimem64_start = 0; } // Map regions on each device. int bus;
On Thu, Jun 07, 2012 at 10:34:34AM +0200, Gerd Hoffmann wrote:
Update the pci i/o windows at runtime, depending on the amount memory the machine has. The 32bit window starts above low memory and ends at the ioapic map address, the 64bit window is placed above high memory.
[...]
r64_mem.base = pcimem64_start;
u64 sum = pci_region_sum(&r64_mem);
u64 align = pci_region_align(&r64_pref);
r64_pref.base = ALIGN(r64_mem.base + sum, align);
for (i = 0; i < ARRAY_SIZE(sizes); i++) {
pcimem64_size = sizes[i];
pcimem64_start = ALIGN(0x100000000LL + RamSizeOver4G, pcimem64_size);
Why align to 64GB (or 1TB)? How about just starting at 4GB past ram aligned to 4GB.
pcimem64_end = pcimem64_start + pcimem64_size;
r64_mem.base = pcimem64_start;
u64 sum = pci_region_sum(&r64_mem);
u64 align = pci_region_align(&r64_pref);
r64_pref.base = ALIGN(r64_mem.base + sum, align);
if (r64_pref.base + pci_region_sum(&r64_pref) <= pcimem64_end)
break;
Why not just assign "pcimem64_end = r64_pref.base + pci_region_sum(&r64_pref)" instead? That is, instead of looping to try certain sizes, why not just set the range according to how much space is found to be needed?
-Kevin
On Thu, Jun 07, 2012 at 10:34:30AM +0200, Gerd Hoffmann wrote:
Hi,
Time to repost, aiming for merge. The patches survived my local testing and win2k8 testing by Alexey. Patch #4 got some finishing touches: Tries two 64bit I/O window sizes only, also added a comment explaining why it is done. Rebased to latest master.
Thanks. I pushed patches 1-3. I'm still not understanding what patch 4 does. I'll try to take a closer look this weekend.
-Kevin