Get system state configuration from QEMU and patch DSDT with it.

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>
diff --git a/src/acpi-dsdt.dsl b/src/acpi-dsdt.dsl
index 15299ee..2060686 100644
--- a/src/acpi-dsdt.dsl
+++ b/src/acpi-dsdt.dsl
@@ -664,38 +664,6 @@
         }
     }
 
-
-/****************************************************************
- * Suspend
- ****************************************************************/
-
-    /*
-     * S3 (suspend-to-ram), S4 (suspend-to-disk) and S5 (power-off) type codes:
-     * must match piix4 emulation.
-     */
-    Name (\_S3, Package (0x04)
-    {
-        0x01,  /* PM1a_CNT.SLP_TYP */
-        0x01,  /* PM1b_CNT.SLP_TYP */
-        Zero,  /* reserved */
-        Zero   /* reserved */
-    })
-    Name (\_S4, Package (0x04)
-    {
-        Zero,  /* PM1a_CNT.SLP_TYP */
-        Zero,  /* PM1b_CNT.SLP_TYP */
-        Zero,  /* reserved */
-        Zero   /* reserved */
-    })
-    Name (\_S5, Package (0x04)
-    {
-        Zero,  /* PM1a_CNT.SLP_TYP */
-        Zero,  /* PM1b_CNT.SLP_TYP */
-        Zero,  /* reserved */
-        Zero   /* reserved */
-    })
-
-
 /****************************************************************
  * CPU hotplug
  ****************************************************************/
diff --git a/src/acpi.c b/src/acpi.c
index 365267b..55e4607 100644
--- a/src/acpi.c
+++ b/src/acpi.c
@@ -518,6 +518,8 @@
 
 static void* build_pcihp(void)
 {
+    char *sys_states;
+    int sys_state_size;
     u32 rmvc_pcrm;
     int i;
 
@@ -549,6 +551,19 @@
         }
     }
 
+    sys_states = romfile_loadfile("etc/system-states", &sys_state_size);
+    if (!sys_states || sys_state_size != 6)
+        sys_states = (char[]){128, 0, 0, 129, 128, 128};
+
+    if (!(sys_states[3] & 128))
+        ssdt[acpi_s3_name[0]] = 'X';
+    if (!(sys_states[4] & 128))
+        ssdt[acpi_s4_name[0]] = 'X';
+    else
+        ssdt[acpi_s4_pkg[0] + 1] = ssdt[acpi_s4_pkg[0] + 3] = sys_states[4] & 127;
+    ((struct acpi_table_header*)ssdt)->checksum = 0;
+    ((struct acpi_table_header*)ssdt)->checksum -= checksum(ssdt, sizeof(ssdp_pcihp_aml));
+
     return ssdt;
 }
 
diff --git a/src/ssdt-pcihp.dsl b/src/ssdt-pcihp.dsl
index 4b435b8..12555e2 100644
--- a/src/ssdt-pcihp.dsl
+++ b/src/ssdt-pcihp.dsl
@@ -95,4 +95,40 @@
             gen_pci_hotplug(1f)
         }
     }
+
+    Scope(\) {
+/****************************************************************
+ * Suspend
+ ****************************************************************/
+
+    /*
+     * 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)
+        {
+            One,  /* PM1a_CNT.SLP_TYP */
+            One,  /* PM1b_CNT.SLP_TYP */
+            Zero,  /* reserved */
+            Zero   /* reserved */
+        })
+        ACPI_EXTRACT_NAME_STRING acpi_s4_name
+        ACPI_EXTRACT_PKG_START acpi_s4_pkg
+        Name (_S4, Package (0x04)
+        {
+            0x2,  /* PM1a_CNT.SLP_TYP */
+            0x2,  /* PM1b_CNT.SLP_TYP */
+            Zero,  /* reserved */
+            Zero   /* reserved */
+        })
+        Name (_S5, Package (0x04)
+        {
+            Zero,  /* PM1a_CNT.SLP_TYP */
+            Zero,  /* PM1b_CNT.SLP_TYP */
+            Zero,  /* reserved */
+            Zero   /* reserved */
+        })
+    }
 }