aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVladimir Chtchetkine <vchtchetkine@google.com>2012-01-12 13:37:40 -0800
committerVladimir Chtchetkine <vchtchetkine@google.com>2012-01-13 08:16:05 -0800
commitdb450d73092add519efddcd6d55c7a0e7541ec70 (patch)
treea4de5f7d4c9d1fb35a376dcd198da8c06c846b4f
parentaa1af37d8b3c3a21eb4cac4a225225425b50d08c (diff)
downloadexternal_qemu-db450d73092add519efddcd6d55c7a0e7541ec70.zip
external_qemu-db450d73092add519efddcd6d55c7a0e7541ec70.tar.gz
external_qemu-db450d73092add519efddcd6d55c7a0e7541ec70.tar.bz2
Respect HW configs when loading VM from snapshots.
Changing HW configuration properties may cause emulator / guest system crash on condition that VM has been loaded from a snapshot. This CL addresses this issue in the following way: 1. Each time a snapshot is saved, a backup copy of HW config is saved with it. 2. Each time a snapshot is loaded, emulator finds an appropriate HW config backup, and compares current HW config with the one that was saved in the backup, and if configs are different, emulator exits with an appropriate error. Change-Id: I730bec0afbe166e88189fdcc4804b76e109e4422
-rw-r--r--Makefile.common3
-rw-r--r--android/main.c6
-rw-r--r--android/snaphost-android.c180
-rw-r--r--android/snaphost-android.h32
-rw-r--r--android/utils/ini.c53
-rw-r--r--android/utils/ini.h17
-rw-r--r--vl-android.c15
7 files changed, 294 insertions, 12 deletions
diff --git a/Makefile.common b/Makefile.common
index 59c18ea..4a711c6 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -429,7 +429,8 @@ CORE_MISC_SOURCES = \
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 @@ int main(int argc, char **argv)
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 @@ EXIT:
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 @@ iniFile_saveToFile( IniFile* f, const char* filepath )
}
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 @@ iniFile_saveToFile( IniFile* f, const char* filepath )
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 @@ IniFile* iniFile_newFromFile( const char* filePath);
*/
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 @@ int iniFile_getPairCount( IniFile* f );
*/
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 @@ int qemu_main(int argc, char **argv, char **envp);
#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 @@ extern void android_emulator_set_base_port(int port);
#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 @@ static void main_loop(void)
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 @@ int main(int argc, char **argv, char **envp)
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 @@ int main(int argc, char **argv, char **envp)
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)) {