Patchwork [for-1.4,2/2] pc_sysfw: fix migration for versions <= 1.3 via resizable ROM regions

login
register
mail settings
Submitter Michael Roth
Date Feb. 6, 2013, 12:48 a.m.
Message ID <1360111706-13138-3-git-send-email-mdroth@linux.vnet.ibm.com>
Download mbox | patch
Permalink /patch/218471/
State New
Headers show

Comments

Michael Roth - Feb. 6, 2013, 12:48 a.m.
SeaBIOS recently changed in tree from 128KB to 256KB. This causes
migration from versions prior to 1.4 to fail due to the corresponding
RAMBlock sizes not matching. Relaxing this check is insufficient since
it results in a partially overwritten BIOS region that leads to a KVM
exception on post-migration reboot.

Instead, use a VMStateRAMResizeHandler to re-initialize the RAMBlock and
relevant MemoryRegions in cases where the migrated ROM size is
different than the current one.

relevant 'info mtree' for 1.3:

  pci
  0000000000000000-7ffffffffffffffe (prio 0, RW): pci
    00000000000c0000-00000000000dffff (prio 1, RW): pc.rom
    00000000000e0000-00000000000fffff (prio 1, R-): alias isa-bios \
        @pc.bios 0000000000000000-000000000001ffff
    00000000fffe0000-00000000ffffffff (prio 0, R-): pc.bios

relevant 'info mtree' for 1.4 pre-migration:

  pci
  0000000000000000-7ffffffffffffffe (prio 0, RW): pci
    00000000000c0000-00000000000dffff (prio 1, RW): pc.rom
    00000000000e0000-00000000000fffff (prio 1, R-): alias isa-bios \
        @pc.bios 0000000000020000-000000000003ffff
    00000000fffc0000-00000000ffffffff (prio 0, R-): pc.bios

relevant 'info mtree' for 1.4 after the resize handler is called:

  pci
  0000000000000000-7ffffffffffffffe (prio 0, RW): pci
    00000000000c0000-00000000000dffff (prio 1, RW): pc.rom
    00000000000e0000-00000000000fffff (prio 1, R-): alias isa-bios \
        @pc.bios 0000000000000000-000000000001ffff
    00000000fffe0000-00000000ffffffff (prio 0, R-): pc.bios

Tested post-migration (from 1.3) functionality with qemu-jeos,
Ubuntu 12.10, and Windows 7.

Cc: qemu-stable@nongnu.org
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
---
 hw/pc_sysfw.c |  104 ++++++++++++++++++++++++++++++++++++++++++---------------
 1 file changed, 78 insertions(+), 26 deletions(-)

Patch

diff --git a/hw/pc_sysfw.c b/hw/pc_sysfw.c
index 7f6c12c..3053de4 100644
--- a/hw/pc_sysfw.c
+++ b/hw/pc_sysfw.c
@@ -138,12 +138,84 @@  static void pc_system_flash_init(MemoryRegion *rom_memory,
     pc_isa_bios_init(rom_memory, flash_mem, size);
 }
 
+typedef struct SystemRomResizeState {
+    MemoryRegion *rom_memory;
+    MemoryRegion *isa_bios;
+} SystemRomResizeState;
+
+static void system_rom_memory_init(MemoryRegion *rom_memory,
+                                   int bios_size, bool resizable);
+
+static int system_rom_memory_resize(MemoryRegion *bios, uint64_t size,
+                                    void *opaque)
+{
+    SystemRomResizeState *resize_state = opaque;
+
+    memory_region_del_subregion(resize_state->rom_memory, bios);
+    memory_region_del_subregion(resize_state->rom_memory,
+                                resize_state->isa_bios);
+    memory_region_destroy(resize_state->isa_bios);
+    memory_region_destroy(bios);
+    g_free(bios);
+    g_free(resize_state->isa_bios);
+
+    system_rom_memory_init(resize_state->rom_memory, size, false);
+
+    return 0;
+}
+
+static void system_rom_memory_resize_cleanup(void *opaque)
+{
+    SystemRomResizeState *resize_state = opaque;
+
+    g_free(resize_state);
+}
+
+static void system_rom_memory_init(MemoryRegion *rom_memory,
+                                   int bios_size, bool resizable)
+{
+    MemoryRegion *bios, *isa_bios;
+    int isa_bios_size;
+    SystemRomResizeState *resize_state;
+
+    bios = g_malloc(sizeof(*bios));
+    memory_region_init_ram(bios, "pc.bios", bios_size);
+    vmstate_register_ram_global(bios);
+    memory_region_set_readonly(bios, true);
+
+    /* map the last 128KB of the BIOS in ISA space */
+    isa_bios_size = bios_size;
+    if (isa_bios_size > (128 * 1024)) {
+        isa_bios_size = 128 * 1024;
+    }
+    isa_bios = g_malloc(sizeof(*isa_bios));
+    memory_region_init_alias(isa_bios, "isa-bios", bios,
+                             bios_size - isa_bios_size, isa_bios_size);
+    memory_region_add_subregion_overlap(rom_memory,
+                                        0x100000 - isa_bios_size,
+                                        isa_bios,
+                                        1);
+    memory_region_set_readonly(isa_bios, true);
+
+    /* map all the bios at the top of memory */
+    memory_region_add_subregion(rom_memory,
+                                (uint32_t)(-bios_size),
+                                bios);
+
+    if (resizable) {
+        resize_state = g_malloc0(sizeof(SystemRomResizeState));
+        resize_state->rom_memory = rom_memory;
+        resize_state->isa_bios = isa_bios;
+        vmstate_register_ram_resizable(bios, system_rom_memory_resize,
+                                       system_rom_memory_resize_cleanup,
+                                       resize_state);
+    }
+}
+
 static void old_pc_system_rom_init(MemoryRegion *rom_memory)
 {
     char *filename;
-    MemoryRegion *bios, *isa_bios;
-    int bios_size, isa_bios_size;
-    int ret;
+    int ret, bios_size;
 
     /* BIOS load */
     if (bios_name == NULL) {
@@ -159,10 +231,9 @@  static void old_pc_system_rom_init(MemoryRegion *rom_memory)
         (bios_size % 65536) != 0) {
         goto bios_error;
     }
-    bios = g_malloc(sizeof(*bios));
-    memory_region_init_ram(bios, "pc.bios", bios_size);
-    vmstate_register_ram_global(bios);
-    memory_region_set_readonly(bios, true);
+
+    system_rom_memory_init(rom_memory, bios_size, true);
+
     ret = rom_add_file_fixed(bios_name, (uint32_t)(-bios_size), -1);
     if (ret != 0) {
     bios_error:
@@ -172,25 +243,6 @@  static void old_pc_system_rom_init(MemoryRegion *rom_memory)
     if (filename) {
         g_free(filename);
     }
-
-    /* map the last 128KB of the BIOS in ISA space */
-    isa_bios_size = bios_size;
-    if (isa_bios_size > (128 * 1024)) {
-        isa_bios_size = 128 * 1024;
-    }
-    isa_bios = g_malloc(sizeof(*isa_bios));
-    memory_region_init_alias(isa_bios, "isa-bios", bios,
-                             bios_size - isa_bios_size, isa_bios_size);
-    memory_region_add_subregion_overlap(rom_memory,
-                                        0x100000 - isa_bios_size,
-                                        isa_bios,
-                                        1);
-    memory_region_set_readonly(isa_bios, true);
-
-    /* map all the bios at the top of memory */
-    memory_region_add_subregion(rom_memory,
-                                (uint32_t)(-bios_size),
-                                bios);
 }
 
 void pc_system_firmware_init(MemoryRegion *rom_memory)