diff options
author | Paolo Bonzini | 2014-07-28 17:34:15 +0200 |
---|---|---|
committer | Michael S. Tsirkin | 2014-07-28 23:02:39 +0200 |
commit | 07fb61760cdea7c3f1b9c897513986945bca8e89 (patch) | |
tree | e2ff26a669b9587e12bdd4aa86a2ecd244e6b057 /hw/i386/acpi-build.c | |
parent | acpi-dsdt: procedurally generate _PRT (diff) | |
download | qemu-07fb61760cdea7c3f1b9c897513986945bca8e89.tar.gz qemu-07fb61760cdea7c3f1b9c897513986945bca8e89.tar.xz qemu-07fb61760cdea7c3f1b9c897513986945bca8e89.zip |
pc: hack for migration compatibility from QEMU 2.0
Changing the ACPI table size causes migration to break, and the memory
hotplug work opened our eyes on how horribly we were breaking things in
2.0 already.
The ACPI table size is rounded to the next 4k, which one would think
gives some headroom. In practice this is not the case, because the user
can control the ACPI table size (each CPU adds 97 bytes to the SSDT and
8 to the MADT) and so some "-smp" values will break the 4k boundary and
fail to migrate. Similarly, PCI bridges add ~1870 bytes to the SSDT.
This patch concerns itself with fixing migration from QEMU 2.0. It
computes the payload size of QEMU 2.0 and always uses that one.
The previous patch shrunk the ACPI tables enough that the QEMU 2.0 size
should always be enough; non-AML tables can change depending on the
configuration (especially MADT, SRAT, HPET) but they remain the same
between QEMU 2.0 and 2.1, so we only compute our padding based on the
sizes of the SSDT and DSDT.
Migration from QEMU 1.7 should work for guests that have a number of CPUs
other than 12, 13, 14, 54, 55, 56, 97, 98, 139, 140. It was already
broken from QEMU 1.7 to QEMU 2.0 in the same way, though.
Even with this patch, QEMU 1.7 and 2.0 have two different ideas of
"-M pc-i440fx-2.0" when there are PCI bridges. Igor sent a patch to
adopt the QEMU 1.7 definition. I think distributions should apply
it if they move directly from QEMU 1.7 to 2.1+ without ever packaging
version 2.0.
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Tested-by: Igor Mammedov <imammedo@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Diffstat (limited to 'hw/i386/acpi-build.c')
-rw-r--r-- | hw/i386/acpi-build.c | 57 |
1 files changed, 53 insertions, 4 deletions
diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index ebc5f034e3..d90c471792 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -25,7 +25,9 @@ #include <glib.h> #include "qemu-common.h" #include "qemu/bitmap.h" +#include "qemu/osdep.h" #include "qemu/range.h" +#include "qemu/error-report.h" #include "hw/pci/pci.h" #include "qom/cpu.h" #include "hw/i386/pc.h" @@ -52,6 +54,14 @@ #include "qapi/qmp/qint.h" #include "qom/qom-qobject.h" +/* These are used to size the ACPI tables for -M pc-i440fx-1.7 and + * -M pc-i440fx-2.0. Even if the actual amount of AML generated grows + * a little bit, there should be plenty of free space since the DSDT + * shrunk by ~1.5k between QEMU 2.0 and QEMU 2.1. + */ +#define ACPI_BUILD_LEGACY_CPU_AML_SIZE 97 +#define ACPI_BUILD_ALIGN_SIZE 0x1000 + typedef struct AcpiCpuInfo { DECLARE_BITMAP(found_cpus, ACPI_CPU_HOTPLUG_ID_LIMIT); } AcpiCpuInfo; @@ -1440,13 +1450,14 @@ static void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables) { GArray *table_offsets; - unsigned facs, dsdt, rsdt; + unsigned facs, ssdt, dsdt, rsdt; AcpiCpuInfo cpu; AcpiPmInfo pm; AcpiMiscInfo misc; AcpiMcfgInfo mcfg; PcPciInfo pci; uint8_t *u; + size_t aml_len = 0; acpi_get_cpu_info(&cpu); acpi_get_pm_info(&pm); @@ -1474,13 +1485,20 @@ void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables) dsdt = tables->table_data->len; build_dsdt(tables->table_data, tables->linker, &misc); + /* Count the size of the DSDT and SSDT, we will need it for legacy + * sizing of ACPI tables. + */ + aml_len += tables->table_data->len - dsdt; + /* ACPI tables pointed to by RSDT */ acpi_add_table(table_offsets, tables->table_data); build_fadt(tables->table_data, tables->linker, &pm, facs, dsdt); + ssdt = tables->table_data->len; acpi_add_table(table_offsets, tables->table_data); build_ssdt(tables->table_data, tables->linker, &cpu, &pm, &misc, &pci, guest_info); + aml_len += tables->table_data->len - ssdt; acpi_add_table(table_offsets, tables->table_data); build_madt(tables->table_data, tables->linker, &cpu, guest_info); @@ -1513,14 +1531,45 @@ void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables) /* RSDP is in FSEG memory, so allocate it separately */ build_rsdp(tables->rsdp, tables->linker, rsdt); - /* We'll expose it all to Guest so align size to reduce + /* We'll expose it all to Guest so we want to reduce * chance of size changes. * RSDP is small so it's easy to keep it immutable, no need to * bother with alignment. + * + * We used to align the tables to 4k, but of course this would + * too simple to be enough. 4k turned out to be too small an + * alignment very soon, and in fact it is almost impossible to + * keep the table size stable for all (max_cpus, max_memory_slots) + * combinations. So the table size is always 64k for pc-i440fx-2.1 + * and we give an error if the table grows beyond that limit. + * + * We still have the problem of migrating from "-M pc-i440fx-2.0". For + * that, we exploit the fact that QEMU 2.1 generates _smaller_ tables + * than 2.0 and we can always pad the smaller tables with zeros. We can + * then use the exact size of the 2.0 tables. + * + * All this is for PIIX4, since QEMU 2.0 didn't support Q35 migration. */ - acpi_align_size(tables->table_data, 0x1000); + if (guest_info->legacy_acpi_table_size) { + /* Subtracting aml_len gives the size of fixed tables. Then add the + * size of the PIIX4 DSDT/SSDT in QEMU 2.0. + */ + int legacy_aml_len = + guest_info->legacy_acpi_table_size + + ACPI_BUILD_LEGACY_CPU_AML_SIZE * max_cpus; + int legacy_table_size = + ROUND_UP(tables->table_data->len - aml_len + legacy_aml_len, + ACPI_BUILD_ALIGN_SIZE); + if (tables->table_data->len > legacy_table_size) { + /* Should happen only with PCI bridges and -M pc-i440fx-2.0. */ + error_report("Warning: migration to QEMU 2.0 may not work."); + } + g_array_set_size(tables->table_data, legacy_table_size); + } else { + acpi_align_size(tables->table_data, ACPI_BUILD_ALIGN_SIZE); + } - acpi_align_size(tables->linker, 0x1000); + acpi_align_size(tables->linker, ACPI_BUILD_ALIGN_SIZE); /* Cleanup memory that's no longer used. */ g_array_free(table_offsets, true); |