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