Merge "Respect HW configs when loading VM from snapshots."
diff --git a/Makefile.common b/Makefile.common
index 59c18ea..4a711c6 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -429,7 +429,8 @@
android/camera/camera-format-converters.c \
android/camera/camera-service.c \
android/adb-server.c \
- android/adb-qemud.c
+ android/adb-qemud.c \
+ android/snaphost-android.c
$(call gen-hw-config-defs)
diff --git a/android/main.c b/android/main.c
index 1adb6a1..c56bb5c 100644
--- a/android/main.c
+++ b/android/main.c
@@ -1308,7 +1308,11 @@
coreHwIniPath = tempfile_path(tempIni);
}
- if (iniFile_saveToFile(hwIni, coreHwIniPath) < 0) {
+ /* While saving HW config, ignore valueless entries. This will not break
+ * anything, but will significantly simplify comparing the current HW
+ * config with the one that has been associated with a snapshot (in case
+ * VM starts from a snapshot for this instance of emulator). */
+ if (iniFile_saveToFileClean(hwIni, coreHwIniPath) < 0) {
derror("Could not write hardware.ini to %s: %s", coreHwIniPath, strerror(errno));
exit(2);
}
diff --git a/android/snaphost-android.c b/android/snaphost-android.c
new file mode 100644
index 0000000..205531c
--- /dev/null
+++ b/android/snaphost-android.c
@@ -0,0 +1,180 @@
+/* Copyright (C) 2012 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+*/
+
+#include "qemu-common.h"
+#include "android/globals.h"
+#include "android/snaphost-android.h"
+#include "android/utils/debug.h"
+
+#define E(...) derror(__VA_ARGS__)
+#define W(...) dwarning(__VA_ARGS__)
+#define D(...) VERBOSE_PRINT(init,__VA_ARGS__)
+
+/* Compares two instance of an ini file.
+ * This routine compares all entries (key,value pairs) found in one ini file
+ * against entries in another file. The files are considered to be equal if:
+ * 1. Number of entries in each file is equal.
+ * 2. Each entry in one file has a corresponded entry in another file, and their
+ * values are equal.
+ * Param:
+ * current - Ini file containing the current configuration.
+ * saved - Ini file containing a previously saved configuration.
+ * Return:
+ * 0 if files are equal, or 1 if they are not equal, or -1 if an error has
+ * occurred.
+ */
+static int
+_cmp_hw_config(IniFile* current, IniFile* saved)
+{
+ int n, ret = 0;
+ const int num_pairs = iniFile_getPairCount(current);
+
+ /* Check 1: must contain same number of entries. */
+ if (num_pairs != iniFile_getPairCount(saved)) {
+ D("Different numbers of entries in the HW config files. Current contans %d, while saved contains %d entries.",
+ num_pairs, iniFile_getPairCount(saved));
+ return -1;
+ }
+
+ /* Iterate through the entries in the current file, comparing them to entries
+ * in the saved file. */
+ for (n = 0; n < num_pairs && ret == 0; n++) {
+ char* key, *value1, *value2;
+
+ if (iniFile_getEntry(current, n, &key, &value1)) {
+ D("Unable to obtain entry %d from the current HW config file", n);
+ return -1;
+ }
+
+ value2 = iniFile_getString(saved, key, "");
+ if (value2 == NULL) {
+ D("Saved HW config file is missing entry ('%s', '%s') found in the current HW config.",
+ key, value1);
+ free(key);
+ free(value1);
+ return 1;
+ }
+
+ ret = strcmp(value1, value2);
+ if (ret) {
+ D("HW config value mismatch for a key '%s': current is '%s' while saved was '%s'",
+ key, value1, value2);
+ }
+
+ free(value2);
+ free(value1);
+ free(key);
+ }
+
+ return ret ? 1 : 0;
+}
+
+/* Builds path to the HW config backup file that is used to store HW config
+ * settings used for that snapshot. The backup path is a concatenation of the
+ * snapshot storage file path, snapshot name, and an 'ini' extension. This
+ * way we can track HW configuration for different snapshot names store in
+ * different storage files.
+ * Param:
+ * name - Name of the snapshot inside the snapshot storage file.
+ * Return:
+ * Path to the HW config backup file on success, or NULL on an error.
+ */
+static char*
+_build_hwcfg_path(const char* name)
+{
+ const int path_len = strlen(android_hw->disk_snapStorage_path) +
+ strlen(name) + 6;
+ char* bkp_path = malloc(path_len);
+ if (bkp_path == NULL) {
+ E("Unable to allocate %d bytes for HW config path!", path_len);
+ return NULL;
+ }
+
+ snprintf(bkp_path, path_len, "%s.%s.ini",
+ android_hw->disk_snapStorage_path, name);
+
+ return bkp_path;
+}
+
+int
+snaphost_match_configs(IniFile* hw_ini, const char* name)
+{
+ /* Make sure that snapshot storage path is valid. */
+ if (android_hw->disk_snapStorage_path == NULL ||
+ *android_hw->disk_snapStorage_path == '\0') {
+ return 1;
+ }
+
+ /* Build path to the HW config for the loading VM. */
+ char* bkp_path = _build_hwcfg_path(name);
+ if (bkp_path == NULL) {
+ return 0;
+ }
+
+ /* Load HW config from the previous emulator launch. */
+ IniFile* hwcfg_bkp = iniFile_newFromFile(bkp_path);
+
+ if (hwcfg_bkp != NULL) {
+ if (_cmp_hw_config(hw_ini, hwcfg_bkp)) {
+ E("Unable to load VM from snapshot. The snapshot has been saved for a different hardware configuration.");
+ free(bkp_path);
+ return 0;
+ }
+ iniFile_free(hwcfg_bkp);
+ } else {
+ /* It could be that a snapshot file has been copied from another
+ * location without copying the backup file, or snapshot file has not
+ * been created yet. In either case we can't do much checking here,
+ * so, lets be hopefull that user knows what (s)he is doing. */
+ D("Missing HW config backup file '%s'", bkp_path);
+ }
+
+ free(bkp_path);
+
+ return 1;
+}
+
+void
+snaphost_save_config(const char* name)
+{
+ /* Make sure that snapshot storage path is valid. */
+ if (android_hw->disk_snapStorage_path == NULL ||
+ *android_hw->disk_snapStorage_path == '\0') {
+ return;
+ }
+
+ /* Build path to the HW config for the saving VM. */
+ char* bkp_path = _build_hwcfg_path(name);
+ if (bkp_path == NULL) {
+ return;
+ }
+
+ /* Create HW config backup file from the current HW config settings. */
+ IniFile* hwcfg_bkp = iniFile_newFromMemory("", bkp_path);
+ if (hwcfg_bkp == NULL) {
+ W("Unable to create backup HW config file '%s'. Error: %s",
+ bkp_path, strerror(errno));
+ return;
+ }
+ androidHwConfig_write(android_hw, hwcfg_bkp);
+
+ /* Save backup file. */
+ if (!iniFile_saveToFileClean(hwcfg_bkp, bkp_path)) {
+ D("HW config has been backed up to '%s'", bkp_path);
+ } else {
+ /* Treat this as a "soft" error. Yes, we couldn't save the backup, but
+ * this should not cancel snapshot saving. */
+ W("Unable to save HW config file '%s'. Error: %s", bkp_path, strerror(errno));
+ }
+ iniFile_free(hwcfg_bkp);
+ free(bkp_path);
+}
diff --git a/android/snaphost-android.h b/android/snaphost-android.h
new file mode 100644
index 0000000..ae558ba
--- /dev/null
+++ b/android/snaphost-android.h
@@ -0,0 +1,32 @@
+/* Copyright (C) 2012 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+*/
+
+#ifndef _ANDROID_SNAPHOST_ANDROID_H_
+#define _ANDROID_SNAPHOST_ANDROID_H_
+
+/* Matches HW config saved for a VM snapshot against the current HW config.
+ * Param:
+ * hw_ini - IniFile instance containing the current HW config settings.
+ * name - Name of the snapshot for which the VM is loading.
+ * Return:
+ * Boolean: 1 if HW configurations match, or 0 if they don't match.
+ */
+extern int snaphost_match_configs(IniFile* hw_ini, const char* name);
+
+/* Saves HW config settings for the current VM.
+ * Param:
+ * name - Name of the snapshot for the current VM.
+ */
+extern void snaphost_save_config(const char* name);
+
+#endif /* _ANDROID_SNAPHOST_ANDROID_H_ */
+
diff --git a/android/utils/ini.c b/android/utils/ini.c
index ff4a8af..43f1321 100644
--- a/android/utils/ini.c
+++ b/android/utils/ini.c
@@ -298,8 +298,18 @@
return ini;
}
-int
-iniFile_saveToFile( IniFile* f, const char* filepath )
+/* Common routine for saving IniFile instance to the given file.
+ * Param:
+ * f - IniFile instance to save.
+ * filepath - Path to a file where to save the instance.
+ * strip - If 1, ignore (don't save) pairs with empty values. If 0, save all
+ * pairs found in the IniFile instance, including the ones that contain
+ * empty values.
+ * Returns:
+ * 0 on success, -1 on error (see errno for error code)
+ */
+static int
+iniFile_saveToFileCommon( IniFile* f, const char* filepath, int strip )
{
FILE* fp = fopen(filepath, "wt");
IniPair* pair = f->pairs;
@@ -313,11 +323,13 @@
}
for ( ; pair < pairEnd; pair++ ) {
- char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
- p = bufprint(temp, end, "%s = %s\n", pair->key, pair->value);
- if (fwrite(temp, p - temp, 1, fp) != 1) {
- result = -1;
- break;
+ if ((pair->value && *pair->value) || !strip) {
+ char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
+ p = bufprint(temp, end, "%s = %s\n", pair->key, pair->value);
+ if (fwrite(temp, p - temp, 1, fp) != 1) {
+ result = -1;
+ break;
+ }
}
}
@@ -325,6 +337,33 @@
return result;
}
+int
+iniFile_saveToFile( IniFile* f, const char* filepath )
+{
+ return iniFile_saveToFileCommon(f, filepath, 0);
+}
+
+int
+iniFile_saveToFileClean( IniFile* f, const char* filepath )
+{
+ return iniFile_saveToFileCommon(f, filepath, 1);
+}
+
+int
+iniFile_getEntry(IniFile* f, int index, char** key, char** value)
+{
+ if (index >= f->numPairs) {
+ D("Index %d exceeds the number of ini file entries %d",
+ index, f->numPairs);
+ return -1;
+ }
+
+ *key = ASTRDUP(f->pairs[index].key);
+ *value = ASTRDUP(f->pairs[index].value);
+
+ return 0;
+}
+
char*
iniFile_getString( IniFile* f, const char* key, const char* defaultValue )
{
diff --git a/android/utils/ini.h b/android/utils/ini.h
index 5730ee0..fd56575 100644
--- a/android/utils/ini.h
+++ b/android/utils/ini.h
@@ -37,6 +37,11 @@
*/
int iniFile_saveToFile( IniFile* f, const char* filePath );
+/* try to write an IniFile into a given file, ignorig pairs with empty values.
+ * returns 0 on success, -1 on error (see errno for error code)
+ */
+int iniFile_saveToFileClean( IniFile* f, const char* filepath );
+
/* free an IniFile object */
void iniFile_free( IniFile* f );
@@ -48,6 +53,18 @@
*/
const char* iniFile_getValue( IniFile* f, const char* key );
+/* Copies a 'key, value' pair for an entry in the file.
+ * Param:
+ * f - Initialized IniFile instance.
+ * index - Index of the entry to copy. Must be less than value returned from the
+ * iniFile_getPairCount routine.
+ * key, value - Receives key, and value strings for the entry. If this routine
+ * succeeds, the caller must free the buffers allocated for the strings.
+ * Return:
+ * 0 on success, -1 if the index exceeds the capacity of the file
+ */
+int iniFile_getEntry(IniFile* f, int index, char** key, char** value);
+
/* returns a copy of the value of a given key, or NULL if defaultValue is NULL.
* caller must free() it.
*/
diff --git a/vl-android.c b/vl-android.c
index 322ddca..8f439ac 100644
--- a/vl-android.c
+++ b/vl-android.c
@@ -208,6 +208,8 @@
#include "android/core-init-utils.h"
#include "android/audio-test.h"
+#include "android/snaphost-android.h"
+
#ifdef CONFIG_STANDALONE_CORE
/* Verbose value used by the standalone emulator core (without UI) */
unsigned long android_verbose;
@@ -235,8 +237,6 @@
#include "libslirp.h"
#endif
-
-
#define DEFAULT_RAM_SIZE 128
/* Max number of USB devices that can be specified on the commandline. */
@@ -2025,6 +2025,10 @@
no_shutdown = 0;
} else {
if (savevm_on_exit != NULL) {
+ /* Prior to saving VM to the snapshot file, save HW config
+ * settings for that VM, so we can match them when VM gets
+ * loaded from the snapshot. */
+ snaphost_save_config(savevm_on_exit);
do_savevm(cur_mon, savevm_on_exit);
}
break;
@@ -3511,6 +3515,12 @@
androidHwConfig_init(android_hw, 0);
androidHwConfig_read(android_hw, hw_ini);
+ /* If we're loading VM from a snapshot, make sure that the current HW config
+ * matches the one with which the VM has been saved. */
+ if (loadvm && *loadvm && !snaphost_match_configs(hw_ini, loadvm)) {
+ exit(0);
+ }
+
iniFile_free(hw_ini);
{
@@ -4229,7 +4239,6 @@
stralloc_reset(kernel_config);
}
-
for (env = first_cpu; env != NULL; env = env->next_cpu) {
for (i = 0; i < nb_numa_nodes; i++) {
if (node_cpumask[i] & (1 << env->cpu_index)) {