Blame SOURCES/kvm-pc-Support-firmware-configuration-with-blockdev.patch

7711c0
From 056788975c5ebe651d8016ec714d6120da4bedc7 Mon Sep 17 00:00:00 2001
7711c0
From: Markus Armbruster <armbru@redhat.com>
7711c0
Date: Fri, 17 May 2019 06:51:17 +0200
7711c0
Subject: [PATCH 50/53] pc: Support firmware configuration with -blockdev
7711c0
MIME-Version: 1.0
7711c0
Content-Type: text/plain; charset=UTF-8
7711c0
Content-Transfer-Encoding: 8bit
7711c0
7711c0
RH-Author: Markus Armbruster <armbru@redhat.com>
7711c0
Message-id: <20190517065120.12028-29-armbru@redhat.com>
7711c0
Patchwork-id: 87999
7711c0
O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH v3 28/31] pc: Support firmware configuration with -blockdev
7711c0
Bugzilla: 1624009
7711c0
RH-Acked-by: Philippe Mathieu-Daudé <philmd@redhat.com>
7711c0
RH-Acked-by: Thomas Huth <thuth@redhat.com>
7711c0
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
7711c0
7711c0
The PC machines put firmware in ROM by default.  To get it put into
7711c0
flash memory (required by OVMF), you have to use -drive
7711c0
if=pflash,unit=0,... and optionally -drive if=pflash,unit=1,...
7711c0
7711c0
Why two -drive?  This permits setting up one part of the flash memory
7711c0
read-only, and the other part read/write.  It also makes upgrading
7711c0
firmware on the host easier.  Below the hood, it creates two separate
7711c0
flash devices, because we were too lazy to improve our flash device
7711c0
models to support sector protection.
7711c0
7711c0
The problem at hand is to do the same with -blockdev somehow, as one
7711c0
more step towards deprecating -drive.
7711c0
7711c0
Mapping -drive if=none,... to -blockdev is a solved problem.  With
7711c0
if=T other than if=none, -drive additionally configures a block device
7711c0
frontend.  For non-onboard devices, that part maps to -device.  Also a
7711c0
solved problem.  For onboard devices such as PC flash memory, we have
7711c0
an unsolved problem.
7711c0
7711c0
This is actually an instance of a wider problem: our general device
7711c0
configuration interface doesn't cover onboard devices.  Instead, we have
7711c0
a zoo of ad hoc interfaces that are much more limited.  One of them is
7711c0
-drive, which we'd rather deprecate, but can't until we have suitable
7711c0
replacements for all its uses.
7711c0
7711c0
Sadly, I can't attack the wider problem today.  So back to the narrow
7711c0
problem.
7711c0
7711c0
My first idea was to reduce it to its solved buddy by using pluggable
7711c0
instead of onboard devices for the flash memory.  Workable, but it
7711c0
requires some extra smarts in firmware descriptors and libvirt.  Paolo
7711c0
had an idea that is simpler for libvirt: keep the devices onboard, and
7711c0
add machine properties for their block backends.
7711c0
7711c0
The implementation is less than straightforward, I'm afraid.
7711c0
7711c0
First, block backend properties are *qdev* properties.  Machines can't
7711c0
have those, as they're not devices.  I could duplicate these qdev
7711c0
properties as QOM properties, but I hate that.
7711c0
7711c0
More seriously, the properties do not belong to the machine, they
7711c0
belong to the onboard flash devices.  Adding them to the machine would
7711c0
then require bad magic to somehow transfer them to the flash devices.
7711c0
Fortunately, QOM provides the means to handle exactly this case: add
7711c0
alias properties to the machine that forward to the onboard devices'
7711c0
properties.
7711c0
7711c0
Properties need to be created in .instance_init() methods.  For PC
7711c0
machines, that's pc_machine_initfn().  To make alias properties work,
7711c0
we need to create the onboard flash devices there, too.  Requires
7711c0
several bug fixes, in the previous commits.  We also have to realize
7711c0
the devices.  More on that below.
7711c0
7711c0
If the user sets pflash0, firmware resides in flash memory.
7711c0
pc_system_firmware_init() maps and realizes the flash devices.
7711c0
7711c0
Else, firmware resides in ROM.  The onboard flash devices aren't used
7711c0
then.  pc_system_firmware_init() destroys them unrealized, along with
7711c0
the alias properties.
7711c0
7711c0
The existing code to pick up drives defined with -drive if=pflash is
7711c0
replaced by code to desugar into the machine properties.
7711c0
7711c0
Signed-off-by: Markus Armbruster <armbru@redhat.com>
7711c0
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
7711c0
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
7711c0
Message-Id: <87ftrtux81.fsf@dusky.pond.sub.org>
7711c0
(cherry picked from commit ebc29e1beab02646702c8cb9a1d29b68f72ad503)
7711c0
[Conflict in hw/i386/pc.c due to lack of commit f5878b03811, and in
7711c0
hw/i386/pc_sysfw.c due to lack of commit d471bf3ebba and due to the
7711c0
part of downstream commit 84733200df4 that came from c3f813d2f53]
7711c0
7711c0
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
7711c0
---
7711c0
 hw/i386/pc.c         |   2 +
7711c0
 hw/i386/pc_sysfw.c   | 248 +++++++++++++++++++++++++++++++++------------------
7711c0
 include/hw/i386/pc.h |   3 +
7711c0
 3 files changed, 164 insertions(+), 89 deletions(-)
7711c0
7711c0
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
7711c0
index 86e9d43..1f55a28 100644
7711c0
--- a/hw/i386/pc.c
7711c0
+++ b/hw/i386/pc.c
7711c0
@@ -2249,6 +2249,8 @@ static void pc_machine_initfn(Object *obj)
7711c0
     pcms->smbus = true;
7711c0
     pcms->sata = true;
7711c0
     pcms->pit = true;
7711c0
+
7711c0
+    pc_system_flash_create(pcms);
7711c0
 }
7711c0
 
7711c0
 static void pc_machine_reset(void)
7711c0
diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c
7711c0
index 2a0e18c..a120342 100644
7711c0
--- a/hw/i386/pc_sysfw.c
7711c0
+++ b/hw/i386/pc_sysfw.c
7711c0
@@ -28,6 +28,7 @@
7711c0
 #include "sysemu/block-backend.h"
7711c0
 #include "qemu/error-report.h"
7711c0
 #include "qemu/option.h"
7711c0
+#include "qemu/units.h"
7711c0
 #include "hw/sysbus.h"
7711c0
 #include "hw/hw.h"
7711c0
 #include "hw/i386/pc.h"
7711c0
@@ -39,6 +40,17 @@
7711c0
 
7711c0
 #define BIOS_FILENAME "bios.bin"
7711c0
 
7711c0
+/*
7711c0
+ * We don't have a theoretically justifiable exact lower bound on the base
7711c0
+ * address of any flash mapping. In practice, the IO-APIC MMIO range is
7711c0
+ * [0xFEE00000..0xFEE01000] -- see IO_APIC_DEFAULT_ADDRESS --, leaving free
7711c0
+ * only 18MB-4KB below 4G. For now, restrict the cumulative mapping to 8MB in
7711c0
+ * size.
7711c0
+ */
7711c0
+#define FLASH_SIZE_LIMIT (8 * MiB)
7711c0
+
7711c0
+#define FLASH_SECTOR_SIZE 4096
7711c0
+
7711c0
 static void pc_isa_bios_init(MemoryRegion *rom_memory,
7711c0
                              MemoryRegion *flash_mem,
7711c0
                              int ram_size)
7711c0
@@ -70,96 +82,118 @@ static void pc_isa_bios_init(MemoryRegion *rom_memory,
7711c0
     memory_region_set_readonly(isa_bios, true);
7711c0
 }
7711c0
 
7711c0
-#define FLASH_MAP_UNIT_MAX 2
7711c0
+static PFlashCFI01 *pc_pflash_create(PCMachineState *pcms,
7711c0
+                                     const char *name,
7711c0
+                                     const char *alias_prop_name)
7711c0
+{
7711c0
+    DeviceState *dev = qdev_create(NULL, TYPE_PFLASH_CFI01);
7711c0
 
7711c0
-/* We don't have a theoretically justifiable exact lower bound on the base
7711c0
- * address of any flash mapping. In practice, the IO-APIC MMIO range is
7711c0
- * [0xFEE00000..0xFEE01000[ -- see IO_APIC_DEFAULT_ADDRESS --, leaving free
7711c0
- * only 18MB-4KB below 4G. For now, restrict the cumulative mapping to 8MB in
7711c0
- * size.
7711c0
- */
7711c0
-#define FLASH_MAP_BASE_MIN ((hwaddr)(0x100000000ULL - 8*1024*1024))
7711c0
+    qdev_prop_set_uint64(dev, "sector-length", FLASH_SECTOR_SIZE);
7711c0
+    qdev_prop_set_uint8(dev, "width", 1);
7711c0
+    qdev_prop_set_string(dev, "name", name);
7711c0
+    object_property_add_child(OBJECT(pcms), name, OBJECT(dev),
7711c0
+                              &error_abort);
7711c0
+    object_property_add_alias(OBJECT(pcms), alias_prop_name,
7711c0
+                              OBJECT(dev), "drive", &error_abort);
7711c0
+    return PFLASH_CFI01(dev);
7711c0
+}
7711c0
 
7711c0
-/* This function maps flash drives from 4G downward, in order of their unit
7711c0
- * numbers. The mapping starts at unit#0, with unit number increments of 1, and
7711c0
- * stops before the first missing flash drive, or before
7711c0
- * unit#FLASH_MAP_UNIT_MAX, whichever is reached first.
7711c0
- *
7711c0
- * Addressing within one flash drive is of course not reversed.
7711c0
- *
7711c0
- * An error message is printed and the process exits if:
7711c0
- * - the size of the backing file for a flash drive is non-positive, or not a
7711c0
- *   multiple of the required sector size, or
7711c0
- * - the current mapping's base address would fall below FLASH_MAP_BASE_MIN.
7711c0
+void pc_system_flash_create(PCMachineState *pcms)
7711c0
+{
7711c0
+    PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
7711c0
+
7711c0
+    if (pcmc->pci_enabled) {
7711c0
+        pcms->flash[0] = pc_pflash_create(pcms, "system.flash0",
7711c0
+                                          "pflash0");
7711c0
+        pcms->flash[1] = pc_pflash_create(pcms, "system.flash1",
7711c0
+                                          "pflash1");
7711c0
+    }
7711c0
+}
7711c0
+
7711c0
+static void pc_system_flash_cleanup_unused(PCMachineState *pcms)
7711c0
+{
7711c0
+    char *prop_name;
7711c0
+    int i;
7711c0
+    Object *dev_obj;
7711c0
+
7711c0
+    assert(PC_MACHINE_GET_CLASS(pcms)->pci_enabled);
7711c0
+
7711c0
+    for (i = 0; i < ARRAY_SIZE(pcms->flash); i++) {
7711c0
+        dev_obj = OBJECT(pcms->flash[i]);
7711c0
+        if (!object_property_get_bool(dev_obj, "realized", &error_abort)) {
7711c0
+            prop_name = g_strdup_printf("pflash%d", i);
7711c0
+            object_property_del(OBJECT(pcms), prop_name, &error_abort);
7711c0
+            g_free(prop_name);
7711c0
+            object_unparent(dev_obj);
7711c0
+            pcms->flash[i] = NULL;
7711c0
+        }
7711c0
+    }
7711c0
+}
7711c0
+
7711c0
+/*
7711c0
+ * Map the pcms->flash[] from 4GiB downward, and realize.
7711c0
+ * Map them in descending order, i.e. pcms->flash[0] at the top,
7711c0
+ * without gaps.
7711c0
+ * Stop at the first pcms->flash[0] lacking a block backend.
7711c0
+ * Set each flash's size from its block backend.  Fatal error if the
7711c0
+ * size isn't a non-zero multiple of 4KiB, or the total size exceeds
7711c0
+ * FLASH_SIZE_LIMIT.
7711c0
  *
7711c0
- * The drive with unit#0 (if available) is mapped at the highest address, and
7711c0
- * it is passed to pc_isa_bios_init(). Merging several drives for isa-bios is
7711c0
+ * If pcms->flash[0] has a block backend, its memory is passed to
7711c0
+ * pc_isa_bios_init().  Merging several flash devices for isa-bios is
7711c0
  * not supported.
7711c0
  */
7711c0
-static void pc_system_flash_init(MemoryRegion *rom_memory)
7711c0
+static void pc_system_flash_map(PCMachineState *pcms,
7711c0
+                                MemoryRegion *rom_memory)
7711c0
 {
7711c0
-    int unit;
7711c0
-    DriveInfo *pflash_drv;
7711c0
+    hwaddr total_size = 0;
7711c0
+    int i;
7711c0
     BlockBackend *blk;
7711c0
     int64_t size;
7711c0
-    char *fatal_errmsg = NULL;
7711c0
-    hwaddr phys_addr = 0x100000000ULL;
7711c0
-    uint32_t sector_size = 4096;
7711c0
     PFlashCFI01 *system_flash;
7711c0
     MemoryRegion *flash_mem;
7711c0
-    char name[64];
7711c0
     void *flash_ptr;
7711c0
     int ret, flash_size;
7711c0
 
7711c0
-    for (unit = 0;
7711c0
-         (unit < FLASH_MAP_UNIT_MAX &&
7711c0
-          (pflash_drv = drive_get(IF_PFLASH, 0, unit)) != NULL);
7711c0
-         ++unit) {
7711c0
-        blk = blk_by_legacy_dinfo(pflash_drv);
7711c0
+    assert(PC_MACHINE_GET_CLASS(pcms)->pci_enabled);
7711c0
+
7711c0
+    for (i = 0; i < ARRAY_SIZE(pcms->flash); i++) {
7711c0
+        system_flash = pcms->flash[i];
7711c0
+        blk = pflash_cfi01_get_blk(system_flash);
7711c0
+        if (!blk) {
7711c0
+            break;
7711c0
+        }
7711c0
         size = blk_getlength(blk);
7711c0
         if (size < 0) {
7711c0
-            fatal_errmsg = g_strdup_printf("failed to get backing file size");
7711c0
-        } else if (size == 0) {
7711c0
-            fatal_errmsg = g_strdup_printf("PC system firmware (pflash) "
7711c0
-                               "cannot have zero size");
7711c0
-        } else if ((size % sector_size) != 0) {
7711c0
-            fatal_errmsg = g_strdup_printf("PC system firmware (pflash) "
7711c0
-                               "must be a multiple of 0x%x", sector_size);
7711c0
-        } else if (phys_addr < size || phys_addr - size < FLASH_MAP_BASE_MIN) {
7711c0
-            fatal_errmsg = g_strdup_printf("oversized backing file, pflash "
7711c0
-                               "segments cannot be mapped under "
7711c0
-                               TARGET_FMT_plx, FLASH_MAP_BASE_MIN);
7711c0
+            error_report("can't get size of block device %s: %s",
7711c0
+                         blk_name(blk), strerror(-size));
7711c0
+            exit(1);
7711c0
         }
7711c0
-        if (fatal_errmsg != NULL) {
7711c0
-            Location loc;
7711c0
-
7711c0
-            /* push a new, "none" location on the location stack; overwrite its
7711c0
-             * contents with the location saved in the option; print the error
7711c0
-             * (includes location); pop the top
7711c0
-             */
7711c0
-            loc_push_none(&loc;;
7711c0
-            if (pflash_drv->opts != NULL) {
7711c0
-                qemu_opts_loc_restore(pflash_drv->opts);
7711c0
-            }
7711c0
-            error_report("%s", fatal_errmsg);
7711c0
-            loc_pop(&loc;;
7711c0
-            g_free(fatal_errmsg);
7711c0
+        if (size == 0 || size % FLASH_SECTOR_SIZE != 0) {
7711c0
+            error_report("system firmware block device %s has invalid size "
7711c0
+                         "%" PRId64,
7711c0
+                         blk_name(blk), size);
7711c0
+            info_report("its size must be a non-zero multiple of 0x%x",
7711c0
+                        FLASH_SECTOR_SIZE);
7711c0
+            exit(1);
7711c0
+        }
7711c0
+        if ((hwaddr)size != size
7711c0
+            || total_size > HWADDR_MAX - size
7711c0
+            || total_size + size > FLASH_SIZE_LIMIT) {
7711c0
+            error_report("combined size of system firmware exceeds "
7711c0
+                         "%" PRIu64 " bytes",
7711c0
+                         FLASH_SIZE_LIMIT);
7711c0
             exit(1);
7711c0
         }
7711c0
 
7711c0
-        phys_addr -= size;
7711c0
-
7711c0
-        /* pflash_cfi01_register() creates a deep copy of the name */
7711c0
-        snprintf(name, sizeof name, "system.flash%d", unit);
7711c0
-        system_flash = pflash_cfi01_register(phys_addr, name,
7711c0
-                                             size, blk, sector_size,
7711c0
-                                             1      /* width */,
7711c0
-                                             0x0000 /* id0 */,
7711c0
-                                             0x0000 /* id1 */,
7711c0
-                                             0x0000 /* id2 */,
7711c0
-                                             0x0000 /* id3 */,
7711c0
-                                             0      /* be */);
7711c0
-        if (unit == 0) {
7711c0
+        total_size += size;
7711c0
+        qdev_prop_set_uint32(DEVICE(system_flash), "num-blocks",
7711c0
+                             size / FLASH_SECTOR_SIZE);
7711c0
+        qdev_init_nofail(DEVICE(system_flash));
7711c0
+        sysbus_mmio_map(SYS_BUS_DEVICE(system_flash), 0,
7711c0
+                        0x100000000ULL - total_size);
7711c0
+
7711c0
+        if (i == 0) {
7711c0
             flash_mem = pflash_cfi01_get_memory(system_flash);
7711c0
             pc_isa_bios_init(rom_memory, flash_mem, size);
7711c0
 
7711c0
@@ -244,32 +278,68 @@ void pc_system_firmware_init(PCMachineState *pcms,
7711c0
                              MemoryRegion *rom_memory)
7711c0
 {
7711c0
     PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
7711c0
-    bool isapc_ram_fw = !pcmc->pci_enabled;
7711c0
+    int i;
7711c0
     DriveInfo *pflash_drv;
7711c0
+    BlockBackend *pflash_blk[ARRAY_SIZE(pcms->flash)];
7711c0
+    Location loc;
7711c0
 
7711c0
-    pflash_drv = drive_get(IF_PFLASH, 0, 0);
7711c0
-
7711c0
-    if (isapc_ram_fw || pflash_drv == NULL) {
7711c0
-        /* When a pflash drive is not found, use rom-mode */
7711c0
-        old_pc_system_rom_init(rom_memory, isapc_ram_fw);
7711c0
+    if (!pcmc->pci_enabled) {
7711c0
+        old_pc_system_rom_init(rom_memory, true);
7711c0
         return;
7711c0
     }
7711c0
 
7711c0
-    if (shadow_bios_after_incoming) {
7711c0
-        MachineClass *mc;
7711c0
+    /* Map legacy -drive if=pflash to machine properties */
7711c0
+    for (i = 0; i < ARRAY_SIZE(pcms->flash); i++) {
7711c0
+        pflash_blk[i] = pflash_cfi01_get_blk(pcms->flash[i]);
7711c0
+        pflash_drv = drive_get(IF_PFLASH, 0, i);
7711c0
+        if (!pflash_drv) {
7711c0
+            continue;
7711c0
+        }
7711c0
+        loc_push_none(&loc;;
7711c0
+        qemu_opts_loc_restore(pflash_drv->opts);
7711c0
+        if (pflash_blk[i]) {
7711c0
+            error_report("clashes with -machine");
7711c0
+            exit(1);
7711c0
+        }
7711c0
+        pflash_blk[i] = blk_by_legacy_dinfo(pflash_drv);
7711c0
+        qdev_prop_set_drive(DEVICE(pcms->flash[i]),
7711c0
+                            "drive", pflash_blk[i], &error_fatal);
7711c0
+        loc_pop(&loc;;
7711c0
+    }
7711c0
 
7711c0
-        mc = MACHINE_GET_CLASS(current_machine);
7711c0
-        error_report("flash-based firmware is not supported by machine %s",
7711c0
-                     mc->name);
7711c0
-        exit(1);
7711c0
+    /* Reject gaps */
7711c0
+    for (i = 1; i < ARRAY_SIZE(pcms->flash); i++) {
7711c0
+        if (pflash_blk[i] && !pflash_blk[i - 1]) {
7711c0
+            error_report("pflash%d requires pflash%d", i, i - 1);
7711c0
+            exit(1);
7711c0
+        }
7711c0
     }
7711c0
 
7711c0
-    if (kvm_enabled() && !kvm_readonly_mem_enabled()) {
7711c0
-        /* Older KVM cannot execute from device memory. So, flash memory
7711c0
-         * cannot be used unless the readonly memory kvm capability is present. */
7711c0
-        fprintf(stderr, "qemu: pflash with kvm requires KVM readonly memory support\n");
7711c0
-        exit(1);
7711c0
+    if (!pflash_blk[0]) {
7711c0
+        /* Machine property pflash0 not set, use ROM mode */
7711c0
+        old_pc_system_rom_init(rom_memory, false);
7711c0
+    } else {
7711c0
+        if (shadow_bios_after_incoming) {
7711c0
+            MachineClass *mc;
7711c0
+
7711c0
+            mc = MACHINE_GET_CLASS(current_machine);
7711c0
+            error_report("flash-based firmware is not supported by machine %s",
7711c0
+                         mc->name);
7711c0
+            exit(1);
7711c0
+        }
7711c0
+
7711c0
+        if (kvm_enabled() && !kvm_readonly_mem_enabled()) {
7711c0
+            /*
7711c0
+             * Older KVM cannot execute from device memory. So, flash
7711c0
+             * memory cannot be used unless the readonly memory kvm
7711c0
+             * capability is present.
7711c0
+             */
7711c0
+            error_report("pflash with kvm requires KVM readonly memory support");
7711c0
+            exit(1);
7711c0
+        }
7711c0
+
7711c0
+        pc_system_flash_map(pcms, rom_memory);
7711c0
     }
7711c0
 
7711c0
-    pc_system_flash_init(rom_memory);
7711c0
+    pc_system_flash_cleanup_unused(pcms);
7711c0
 }
7711c0
diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
7711c0
index 611e111..b91b95d 100644
7711c0
--- a/include/hw/i386/pc.h
7711c0
+++ b/include/hw/i386/pc.h
7711c0
@@ -6,6 +6,7 @@
7711c0
 #include "hw/boards.h"
7711c0
 #include "hw/isa/isa.h"
7711c0
 #include "hw/block/fdc.h"
7711c0
+#include "hw/block/flash.h"
7711c0
 #include "net/net.h"
7711c0
 #include "hw/i386/ioapic.h"
7711c0
 
7711c0
@@ -41,6 +42,7 @@ struct PCMachineState {
7711c0
     PCIBus *bus;
7711c0
     FWCfgState *fw_cfg;
7711c0
     qemu_irq *gsi;
7711c0
+    PFlashCFI01 *flash[2];
7711c0
 
7711c0
     /* Configuration options: */
7711c0
     uint64_t max_ram_below_4g;
7711c0
@@ -290,6 +292,7 @@ extern PCIDevice *piix4_dev;
7711c0
 int piix4_init(PCIBus *bus, ISABus **isa_bus, int devfn);
7711c0
 
7711c0
 /* pc_sysfw.c */
7711c0
+void pc_system_flash_create(PCMachineState *pcms);
7711c0
 void pc_system_firmware_init(PCMachineState *pcms, MemoryRegion *rom_memory);
7711c0
 
7711c0
 /* acpi-build.c */
7711c0
-- 
7711c0
1.8.3.1
7711c0