0a122b
From e1fb6e4160e9d9eb1c7b01ea4b5b3a94c953c379 Mon Sep 17 00:00:00 2001
0a122b
From: Laszlo Ersek <lersek@redhat.com>
0a122b
Date: Sat, 11 Jan 2014 18:00:03 +0100
0a122b
Subject: [PATCH 13/22] hw/i386/pc_sysfw: support two flash drives
0a122b
0a122b
RH-Author: Laszlo Ersek <lersek@redhat.com>
0a122b
Message-id: <1389463208-6278-14-git-send-email-lersek@redhat.com>
0a122b
Patchwork-id: 56626
0a122b
O-Subject: [RHEL-7.0 qemu-kvm PATCH 13/18] hw/i386/pc_sysfw: support two flash drives
0a122b
Bugzilla: 1032346
0a122b
RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
0a122b
RH-Acked-by: Amos Kong <akong@redhat.com>
0a122b
RH-Acked-by: Andrew Jones <drjones@redhat.com>
0a122b
0a122b
This patch allows the user to usefully specify
0a122b
0a122b
  -drive file=img_1,if=pflash,format=raw,readonly \
0a122b
  -drive file=img_2,if=pflash,format=raw
0a122b
0a122b
on the command line. The flash images will be mapped under 4G in their
0a122b
reverse unit order -- that is, with their base addresses progressing
0a122b
downwards, in increasing unit order.
0a122b
0a122b
(The unit number increases with command line order if not explicitly
0a122b
specified.)
0a122b
0a122b
This accommodates the following use case: suppose that OVMF is split in
0a122b
two parts, a writeable host file for non-volatile variable storage, and a
0a122b
read-only part for bootstrap and decompressible executable code.
0a122b
0a122b
The binary code part would be read-only, centrally managed on the host
0a122b
system, and passed in as unit 0. The variable store would be writeable,
0a122b
VM-specific, and passed in as unit 1.
0a122b
0a122b
  00000000ffe00000-00000000ffe1ffff (prio 0, R-): system.flash1
0a122b
  00000000ffe20000-00000000ffffffff (prio 0, R-): system.flash0
0a122b
0a122b
(If the guest tries to write to the flash range that is backed by the
0a122b
read-only drive, pflash_update() is never called; various flash
0a122b
programming/erase errors are returned to the guest instead. See the
0a122b
callers of pflash_update(), and the initialization of "pfl->ro", in
0a122b
"hw/block/pflash_cfi01.c".)
0a122b
0a122b
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
0a122b
Reviewed-by: Markus Armbruster <armbru@redhat.com>
0a122b
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
0a122b
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
0a122b
(cherry picked from commit 637a5acb46b36a25b506ba6545e9a53350585b03)
0a122b
---
0a122b
 hw/i386/pc_sysfw.c | 105 +++++++++++++++++++++++++++++++++++++++++++----------
0a122b
 1 file changed, 86 insertions(+), 19 deletions(-)
0a122b
0a122b
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
0a122b
---
0a122b
 hw/i386/pc_sysfw.c |  105 ++++++++++++++++++++++++++++++++++++++++++---------
0a122b
 1 files changed, 86 insertions(+), 19 deletions(-)
0a122b
0a122b
diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c
0a122b
index eb56dee..6f5ecde 100644
0a122b
--- a/hw/i386/pc_sysfw.c
0a122b
+++ b/hw/i386/pc_sysfw.c
0a122b
@@ -72,35 +72,102 @@ static void pc_isa_bios_init(MemoryRegion *rom_memory,
0a122b
     memory_region_set_readonly(isa_bios, true);
0a122b
 }
0a122b
 
0a122b
-static void pc_system_flash_init(MemoryRegion *rom_memory,
0a122b
-                                 DriveInfo *pflash_drv)
0a122b
+#define FLASH_MAP_UNIT_MAX 2
0a122b
+
0a122b
+/* We don't have a theoretically justifiable exact lower bound on the base
0a122b
+ * address of any flash mapping. In practice, the IO-APIC MMIO range is
0a122b
+ * [0xFEE00000..0xFEE01000[ -- see IO_APIC_DEFAULT_ADDRESS --, leaving free
0a122b
+ * only 18MB-4KB below 4G. For now, restrict the cumulative mapping to 8MB in
0a122b
+ * size.
0a122b
+ */
0a122b
+#define FLASH_MAP_BASE_MIN ((hwaddr)(0x100000000ULL - 8*1024*1024))
0a122b
+
0a122b
+/* This function maps flash drives from 4G downward, in order of their unit
0a122b
+ * numbers. The mapping starts at unit#0, with unit number increments of 1, and
0a122b
+ * stops before the first missing flash drive, or before
0a122b
+ * unit#FLASH_MAP_UNIT_MAX, whichever is reached first.
0a122b
+ *
0a122b
+ * Addressing within one flash drive is of course not reversed.
0a122b
+ *
0a122b
+ * An error message is printed and the process exits if:
0a122b
+ * - the size of the backing file for a flash drive is non-positive, or not a
0a122b
+ *   multiple of the required sector size, or
0a122b
+ * - the current mapping's base address would fall below FLASH_MAP_BASE_MIN.
0a122b
+ *
0a122b
+ * The drive with unit#0 (if available) is mapped at the highest address, and
0a122b
+ * it is passed to pc_isa_bios_init(). Merging several drives for isa-bios is
0a122b
+ * not supported.
0a122b
+ */
0a122b
+static void pc_system_flash_init(MemoryRegion *rom_memory)
0a122b
 {
0a122b
+    int unit;
0a122b
+    DriveInfo *pflash_drv;
0a122b
     BlockDriverState *bdrv;
0a122b
     int64_t size;
0a122b
-    hwaddr phys_addr;
0a122b
+    char *fatal_errmsg = NULL;
0a122b
+    hwaddr phys_addr = 0x100000000ULL;
0a122b
     int sector_bits, sector_size;
0a122b
     pflash_t *system_flash;
0a122b
     MemoryRegion *flash_mem;
0a122b
+    char name[64];
0a122b
 
0a122b
-    bdrv = pflash_drv->bdrv;
0a122b
-    size = bdrv_getlength(pflash_drv->bdrv);
0a122b
     sector_bits = 12;
0a122b
     sector_size = 1 << sector_bits;
0a122b
 
0a122b
-    if ((size % sector_size) != 0) {
0a122b
-        fprintf(stderr,
0a122b
-                "qemu: PC system firmware (pflash) must be a multiple of 0x%x\n",
0a122b
-                sector_size);
0a122b
-        exit(1);
0a122b
+    for (unit = 0;
0a122b
+         (unit < FLASH_MAP_UNIT_MAX &&
0a122b
+          (pflash_drv = drive_get(IF_PFLASH, 0, unit)) != NULL);
0a122b
+         ++unit) {
0a122b
+        bdrv = pflash_drv->bdrv;
0a122b
+        size = bdrv_getlength(bdrv);
0a122b
+        if (size < 0) {
0a122b
+            fatal_errmsg = g_strdup_printf("failed to get backing file size");
0a122b
+        } else if (size == 0) {
0a122b
+            fatal_errmsg = g_strdup_printf("PC system firmware (pflash) "
0a122b
+                               "cannot have zero size");
0a122b
+        } else if ((size % sector_size) != 0) {
0a122b
+            fatal_errmsg = g_strdup_printf("PC system firmware (pflash) "
0a122b
+                               "must be a multiple of 0x%x", sector_size);
0a122b
+        } else if (phys_addr < size || phys_addr - size < FLASH_MAP_BASE_MIN) {
0a122b
+            fatal_errmsg = g_strdup_printf("oversized backing file, pflash "
0a122b
+                               "segments cannot be mapped under "
0a122b
+                               TARGET_FMT_plx, FLASH_MAP_BASE_MIN);
0a122b
+        }
0a122b
+        if (fatal_errmsg != NULL) {
0a122b
+            Location loc;
0a122b
+
0a122b
+            /* push a new, "none" location on the location stack; overwrite its
0a122b
+             * contents with the location saved in the option; print the error
0a122b
+             * (includes location); pop the top
0a122b
+             */
0a122b
+            loc_push_none(&loc;;
0a122b
+            if (pflash_drv->opts != NULL) {
0a122b
+                qemu_opts_loc_restore(pflash_drv->opts);
0a122b
+            }
0a122b
+            error_report("%s", fatal_errmsg);
0a122b
+            loc_pop(&loc;;
0a122b
+            g_free(fatal_errmsg);
0a122b
+            exit(1);
0a122b
+        }
0a122b
+
0a122b
+        phys_addr -= size;
0a122b
+
0a122b
+        /* pflash_cfi01_register() creates a deep copy of the name */
0a122b
+        snprintf(name, sizeof name, "system.flash%d", unit);
0a122b
+        system_flash = pflash_cfi01_register(phys_addr, NULL /* qdev */, name,
0a122b
+                                             size, bdrv, sector_size,
0a122b
+                                             size >> sector_bits,
0a122b
+                                             1      /* width */,
0a122b
+                                             0x0000 /* id0 */,
0a122b
+                                             0x0000 /* id1 */,
0a122b
+                                             0x0000 /* id2 */,
0a122b
+                                             0x0000 /* id3 */,
0a122b
+                                             0      /* be */);
0a122b
+        if (unit == 0) {
0a122b
+            flash_mem = pflash_cfi01_get_memory(system_flash);
0a122b
+            pc_isa_bios_init(rom_memory, flash_mem, size);
0a122b
+        }
0a122b
     }
0a122b
-
0a122b
-    phys_addr = 0x100000000ULL - size;
0a122b
-    system_flash = pflash_cfi01_register(phys_addr, NULL, "system.flash", size,
0a122b
-                                         bdrv, sector_size, size >> sector_bits,
0a122b
-                                         1, 0x0000, 0x0000, 0x0000, 0x0000, 0);
0a122b
-    flash_mem = pflash_cfi01_get_memory(system_flash);
0a122b
-
0a122b
-    pc_isa_bios_init(rom_memory, flash_mem, size);
0a122b
 }
0a122b
 
0a122b
 static void old_pc_system_rom_init(MemoryRegion *rom_memory, bool isapc_ram_fw)
0a122b
@@ -181,5 +248,5 @@ void pc_system_firmware_init(MemoryRegion *rom_memory, bool isapc_ram_fw)
0a122b
         exit(1);
0a122b
     }
0a122b
 
0a122b
-    pc_system_flash_init(rom_memory, pflash_drv);
0a122b
+    pc_system_flash_init(rom_memory);
0a122b
 }
0a122b
-- 
0a122b
1.7.1
0a122b