From 960075802f1c30e656c118916c7ea32de0fa8563 Mon Sep 17 00:00:00 2001
From: Laszlo Ersek <lersek@redhat.com>
Date: Thu, 17 Apr 2014 11:13:59 +0200
Subject: qemu_loadvm_state(): shadow SeaBIOS for VM incoming from RHEL-6 host
RH-Author: Laszlo Ersek <lersek@redhat.com>
Message-id: <1397733239-8835-1-git-send-email-lersek@redhat.com>
Patchwork-id: 58498
O-Subject: [RHEL-7.0 0day qemu-kvm PATCH v2] qemu_loadvm_state(): shadow SeaBIOS for VM incoming from RHEL-6 host
Bugzilla: 1103579
RH-Acked-by: Dr. David Alan Gilbert (git) <dgilbert@redhat.com>
RH-Acked-by: Marcel Apfelbaum <marcel.a@redhat.com>
RH-Acked-by: Eduardo Habkost <ehabkost@redhat.com>
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1027565
Brew: https://brewweb.devel.redhat.com/taskinfo?taskID=7352678
RHEL-only patch.
SeaBIOS's shadowing logic has no effect on "pc.ram" (only on "pc.bios" and
"pc.rom") when it runs on the RHEL-6 emulator. When such a guest is
migrated to the RHEL-7 emulator, where the PAM registers actually work,
these two UMBs under 1MB simply disappear from the guest's view, breaking
reboot and S3 resume.
Tested extensively by QE.
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
index c4e1af4..6f8420d 100644
--- a/hw/i386/pc_piix.c
+++ b/hw/i386/pc_piix.c
@@ -1155,6 +1155,7 @@ static void pc_compat_rhel650(MachineState *machine)
rom_file_has_mr = false;
has_acpi_build = false;
gigabyte_align = false;
+ shadow_bios_after_incoming = true;
}
static void pc_init_rhel650(MachineState *machine)
diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h
index 4faa8af..a369c3b 100644
--- a/include/sysemu/sysemu.h
+++ b/include/sysemu/sysemu.h
@@ -89,6 +89,7 @@ void qemu_savevm_state_complete(QEMUFile *f);
void qemu_savevm_state_cancel(void);
uint64_t qemu_savevm_state_pending(QEMUFile *f, uint64_t max_size);
int qemu_loadvm_state(QEMUFile *f);
+extern bool shadow_bios_after_incoming;
/* SLIRP */
void do_info_slirp(Monitor *mon);
diff --git a/savevm.c b/savevm.c
index e19ae0a..41495bc 100644
--- a/savevm.c
+++ b/savevm.c
@@ -51,6 +51,8 @@
#define ARP_PTYPE_IP 0x0800
#define ARP_OP_REQUEST_REV 0x3
+bool shadow_bios_after_incoming;
+
static int announce_self_create(uint8_t *buf,
uint8_t *mac_addr)
{
@@ -878,6 +880,63 @@ typedef struct LoadStateEntry {
int version_id;
} LoadStateEntry;
+static void shadow_bios(void)
+{
+ RAMBlock *block, *ram, *oprom, *bios;
+ size_t one_meg, oprom_size, bios_size;
+ uint8_t *cd_seg_host, *ef_seg_host;
+
+ ram = NULL;
+ oprom = NULL;
+ bios = NULL;
+ QTAILQ_FOREACH(block, &ram_list.blocks, next) {
+ if (strcmp("pc.ram", block->idstr) == 0) {
+ assert(ram == NULL);
+ ram = block;
+ } else if (strcmp("pc.rom", block->idstr) == 0) {
+ assert(oprom == NULL);
+ oprom = block;
+ } else if (strcmp("pc.bios", block->idstr) == 0) {
+ assert(bios == NULL);
+ bios = block;
+ }
+ }
+ assert(ram != NULL);
+ assert(oprom != NULL);
+ assert(bios != NULL);
+ assert(memory_region_is_ram(ram->mr));
+ assert(memory_region_is_ram(oprom->mr));
+ assert(memory_region_is_ram(bios->mr));
+ assert(int128_eq(ram->mr->size, int128_make64(ram->length)));
+ assert(int128_eq(oprom->mr->size, int128_make64(oprom->length)));
+ assert(int128_eq(bios->mr->size, int128_make64(bios->length)));
+
+ one_meg = 1024 * 1024;
+ oprom_size = 128 * 1024;
+ bios_size = 128 * 1024;
+ assert(ram->length >= one_meg);
+ assert(oprom->length == oprom_size);
+ assert(bios->length == bios_size);
+
+ ef_seg_host = memory_region_get_ram_ptr(ram->mr) + (one_meg - bios_size);
+ cd_seg_host = ef_seg_host - oprom_size;
+
+ /* This is a crude hack, but we must distinguish a rhel6.x.0 machtype guest
+ * coming in from a RHEL-6 emulator (where shadowing has had no effect on
+ * "pc.ram") from a similar guest coming in from a RHEL-7 emulator (where
+ * shadowing has worked). In the latter case we must not trample the live
+ * SeaBIOS variables in "pc.ram".
+ */
+ if (buffer_is_zero(ef_seg_host, bios_size)) {
+ fprintf(stderr, "copying E and F segments from pc.bios to pc.ram\n");
+ memcpy(ef_seg_host, memory_region_get_ram_ptr(bios->mr), bios_size);
+ }
+ if (buffer_is_zero(cd_seg_host, oprom_size)) {
+ fprintf(stderr, "copying C and D segments from pc.rom to pc.ram\n");
+ memcpy(cd_seg_host, memory_region_get_ram_ptr(oprom->mr), oprom_size);
+ }
+}
+
int qemu_loadvm_state(QEMUFile *f)
{
QLIST_HEAD(, LoadStateEntry) loadvm_handlers =
@@ -982,6 +1041,13 @@ int qemu_loadvm_state(QEMUFile *f)
}
}
+ /* Supplement SeaBIOS's shadowing now, because it was useless when the
+ * incoming VM started on the RHEL-6 emulator.
+ */
+ if (shadow_bios_after_incoming) {
+ shadow_bios();
+ }
+
cpu_synchronize_all_post_init();
ret = 0;