QEMU may want to disable guest's S3/S4 support and it wants to distinguish between regular powerdown and S4 powerdown. To support that new fw_cfg option was added that passes supported system states and what value should guest use to enter each state. States are passed in 6 byte array. Each byte represents one system state. If byte at offset X has its MSB set it means that system state X is supported and to enter it guest should use the value from lowest 7 bits. Patch also detects old QEMU and uses values that work in backwards compatible way there.
Signed-off-by: Gleb Natapov gleb@redhat.com --- src/acpi-dsdt.dsl | 7 +++++-- src/acpi-dsdt.hex | 21 ++++++++++++++++----- src/acpi.c | 12 +++++++++++- src/paravirt.c | 16 ++++++++++++++++ src/paravirt.h | 2 ++ 5 files changed, 50 insertions(+), 8 deletions(-)
diff --git a/src/acpi-dsdt.dsl b/src/acpi-dsdt.dsl index 4bdc268..cd4ce52 100644 --- a/src/acpi-dsdt.dsl +++ b/src/acpi-dsdt.dsl @@ -613,6 +613,7 @@ DefinitionBlock ( * S3 (suspend-to-ram), S4 (suspend-to-disk) and S5 (power-off) type codes: * must match piix4 emulation. */ + ACPI_EXTRACT_NAME_STRING acpi_s3_name Name (_S3, Package (0x04) { 0x01, /* PM1a_CNT.SLP_TYP */ @@ -620,10 +621,12 @@ DefinitionBlock ( Zero, /* reserved */ Zero /* reserved */ }) + ACPI_EXTRACT_NAME_STRING acpi_s4_name + ACPI_EXTRACT_PKG_START acpi_s4_pkg Name (_S4, Package (0x04) { - Zero, /* PM1a_CNT.SLP_TYP */ - Zero, /* PM1b_CNT.SLP_TYP */ + 0x2, /* PM1a_CNT.SLP_TYP */ + 0x2, /* PM1b_CNT.SLP_TYP */ Zero, /* reserved */ Zero /* reserved */ }) diff --git a/src/acpi-dsdt.hex b/src/acpi-dsdt.hex index a4af597..8a50f55 100644 --- a/src/acpi-dsdt.hex +++ b/src/acpi-dsdt.hex @@ -1,14 +1,20 @@ +static unsigned short acpi_s3_name[] = { +0xf57 +}; +static unsigned short acpi_s4_name[] = { +0xf63 +}; static unsigned char AmlCode[] = { 0x44, 0x53, 0x44, 0x54, -0x21, +0x23, 0x11, 0x0, 0x0, 0x1, -0xe8, +0xcc, 0x42, 0x58, 0x50, @@ -3943,10 +3949,12 @@ static unsigned char AmlCode[] = { 0x34, 0x5f, 0x12, -0x6, +0x8, 0x4, -0x0, -0x0, +0xa, +0x2, +0xa, +0x2, 0x0, 0x0, 0x8, @@ -4385,3 +4393,6 @@ static unsigned char AmlCode[] = { 0xa4, 0x1 }; +static unsigned short acpi_s4_pkg[] = { +0xf6a +}; diff --git a/src/acpi.c b/src/acpi.c index 30888b9..1e7d466 100644 --- a/src/acpi.c +++ b/src/acpi.c @@ -734,13 +734,23 @@ acpi_bios_init(void) } if (fadt && !fadt->dsdt) { /* default DSDT */ - void *dsdt = malloc_high(sizeof(AmlCode)); + char *dsdt = malloc_high(sizeof(AmlCode)); + char sys_states[6]; if (!dsdt) { warn_noalloc(); return; } memcpy(dsdt, AmlCode, sizeof(AmlCode)); fill_dsdt(fadt, dsdt); + qemu_cfg_system_states(sys_states); + if (!(sys_states[3] & 128)) + dsdt[acpi_s3_name[0]] = 'X'; + if (!(sys_states[4] & 128)) + dsdt[acpi_s4_name[0]] = 'X'; + else + dsdt[acpi_s4_pkg[0] + 1] = dsdt[acpi_s4_pkg[0] + 3] = sys_states[4] & 127; + ((struct acpi_table_header*)dsdt)->checksum = 0; + ((struct acpi_table_header*)dsdt)->checksum -= checksum(dsdt, sizeof(AmlCode)); }
// Build final rsdt table diff --git a/src/paravirt.c b/src/paravirt.c index 9cf77de..201bbc3 100644 --- a/src/paravirt.c +++ b/src/paravirt.c @@ -92,6 +92,22 @@ int qemu_cfg_irq0_override(void) return v; }
+int qemu_cfg_system_states(char *states) +{ + char s[6] = {128, 0, 0, 129, 128, 128}; + + if (qemu_cfg_present) { + qemu_cfg_read_entry(states, QEMU_CFG_SYSTEM_STATES, 6); + if (states[0]) + return 1; + } + + /* use defaults matching old QEMU */ + memcpy(states, s, sizeof(s)); + + return 0; +} + u16 qemu_cfg_acpi_additional_tables(void) { u16 cnt; diff --git a/src/paravirt.h b/src/paravirt.h index f39e226..b69646c 100644 --- a/src/paravirt.h +++ b/src/paravirt.h @@ -40,6 +40,7 @@ static inline int kvm_para_available(void) #define QEMU_CFG_SMBIOS_ENTRIES (QEMU_CFG_ARCH_LOCAL + 1) #define QEMU_CFG_IRQ0_OVERRIDE (QEMU_CFG_ARCH_LOCAL + 2) #define QEMU_CFG_E820_TABLE (QEMU_CFG_ARCH_LOCAL + 3) +#define QEMU_CFG_SYSTEM_STATES (QEMU_CFG_ARCH_LOCAL + 5)
extern int qemu_cfg_present;
@@ -109,4 +110,5 @@ u64 romfile_loadint(const char *name, u64 defval); u32 qemu_cfg_e820_entries(void); void* qemu_cfg_e820_load_next(void *addr);
+int qemu_cfg_system_states(char *states); #endif