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