Blame SOURCES/kvm-hw-pci-host-Fix-x86-Host-Bridges-64bit-PCI-hole.patch

4a2fec
From 888e98c6d61b16f59867b91e41af9c878f4d1193 Mon Sep 17 00:00:00 2001
4a2fec
From: Marcel Apfelbaum <marcel@redhat.com>
4a2fec
Date: Fri, 17 Nov 2017 16:26:38 +0100
4a2fec
Subject: [PATCH 29/30] hw/pci-host: Fix x86 Host Bridges 64bit PCI hole
4a2fec
4a2fec
RH-Author: Marcel Apfelbaum <marcel@redhat.com>
4a2fec
Message-id: <20171117162638.34466-1-marcel@redhat.com>
4a2fec
Patchwork-id: 77746
4a2fec
O-Subject: [RHEL-7.5 qemu-kvm-rhev PATCH] hw/pci-host: Fix x86 Host Bridges 64bit PCI hole
4a2fec
Bugzilla: 1390346
4a2fec
RH-Acked-by: Michael S. Tsirkin <mst@redhat.com>
4a2fec
RH-Acked-by: Eduardo Habkost <ehabkost@redhat.com>
4a2fec
RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
4a2fec
4a2fec
Tests: Hotplug an ivshmem device with 2G on Q35 and I440fx machines
4a2fec
       (passes only if this patch is applied)
4a2fec
4a2fec
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
4a2fec
4a2fec
Conflicts:
4a2fec
    - include/hw/i386/pc.h
4a2fec
    Moved x-pci-hole64-fix compat property from PC_COMPAT_2_10
4a2fec
    to PC_RHEL7_4_COMPAT
4a2fec
4a2fec
Currently there is no MMIO range over 4G
4a2fec
reserved for PCI hotplug. Since the 32bit PCI hole
4a2fec
depends on the number of cold-plugged PCI devices
4a2fec
and other factors, it is very possible is too small
4a2fec
to hotplug PCI devices with large BARs.
4a2fec
4a2fec
Fix it by reserving 2G for I4400FX chipset
4a2fec
in order to comply with older Win32 Guest OSes
4a2fec
and 32G for Q35 chipset.
4a2fec
4a2fec
Even if the new defaults of pci-hole64-size will appear in
4a2fec
"info qtree" also for older machines, the property was
4a2fec
not implemented so no changes will be visible to guests.
4a2fec
4a2fec
Note this is a regression since prev QEMU versions had
4a2fec
some range reserved for 64bit PCI hotplug.
4a2fec
4a2fec
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
4a2fec
Reviewed-by: Gerd Hoffmann <kraxel@redhat.com>
4a2fec
Signed-off-by: Marcel Apfelbaum <marcel@redhat.com>
4a2fec
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
4a2fec
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
4a2fec
(cherry picked from commit 9fa99d2519cbf71f871e46871df12cb446dc1c3e)
4a2fec
Signed-off-by: Marcel Apfelbaum <marcel@redhat.com>
4a2fec
---
4a2fec
 hw/i386/pc.c              | 22 ++++++++++++++++++++++
4a2fec
 hw/pci-host/piix.c        | 32 ++++++++++++++++++++++++++++++--
4a2fec
 hw/pci-host/q35.c         | 42 +++++++++++++++++++++++++++++++++++++++---
4a2fec
 include/hw/i386/pc.h      | 12 +++++++++++-
4a2fec
 include/hw/pci-host/q35.h |  1 +
4a2fec
 5 files changed, 103 insertions(+), 6 deletions(-)
4a2fec
4a2fec
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
4a2fec
index 83df57f..f37d60a 100644
4a2fec
--- a/hw/i386/pc.c
4a2fec
+++ b/hw/i386/pc.c
4a2fec
@@ -1482,6 +1482,28 @@ void pc_memory_init(PCMachineState *pcms,
4a2fec
     pcms->ioapic_as = &address_space_memory;
4a2fec
 }
4a2fec
 
4a2fec
+/*
4a2fec
+ * The 64bit pci hole starts after "above 4G RAM" and
4a2fec
+ * potentially the space reserved for memory hotplug.
4a2fec
+ */
4a2fec
+uint64_t pc_pci_hole64_start(void)
4a2fec
+{
4a2fec
+    PCMachineState *pcms = PC_MACHINE(qdev_get_machine());
4a2fec
+    PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
4a2fec
+    uint64_t hole64_start = 0;
4a2fec
+
4a2fec
+    if (pcmc->has_reserved_memory && pcms->hotplug_memory.base) {
4a2fec
+        hole64_start = pcms->hotplug_memory.base;
4a2fec
+        if (!pcmc->broken_reserved_end) {
4a2fec
+            hole64_start += memory_region_size(&pcms->hotplug_memory.mr);
4a2fec
+        }
4a2fec
+    } else {
4a2fec
+        hole64_start = 0x100000000ULL + pcms->above_4g_mem_size;
4a2fec
+    }
4a2fec
+
4a2fec
+    return ROUND_UP(hole64_start, 1ULL << 30);
4a2fec
+}
4a2fec
+
4a2fec
 qemu_irq pc_allocate_cpu_irq(void)
4a2fec
 {
4a2fec
     return qemu_allocate_irq(pic_irq_request, NULL, 0);
4a2fec
diff --git a/hw/pci-host/piix.c b/hw/pci-host/piix.c
4a2fec
index 68c3922..dc37bdf 100644
4a2fec
--- a/hw/pci-host/piix.c
4a2fec
+++ b/hw/pci-host/piix.c
4a2fec
@@ -50,6 +50,7 @@ typedef struct I440FXState {
4a2fec
     PCIHostState parent_obj;
4a2fec
     Range pci_hole;
4a2fec
     uint64_t pci_hole64_size;
4a2fec
+    bool pci_hole64_fix;
4a2fec
     uint32_t short_root_bus;
4a2fec
 } I440FXState;
4a2fec
 
4a2fec
@@ -112,6 +113,9 @@ struct PCII440FXState {
4a2fec
 #define I440FX_PAM_SIZE 7
4a2fec
 #define I440FX_SMRAM    0x72
4a2fec
 
4a2fec
+/* Keep it 2G to comply with older win32 guests */
4a2fec
+#define I440FX_PCI_HOST_HOLE64_SIZE_DEFAULT (1ULL << 31)
4a2fec
+
4a2fec
 /* Older coreboot versions (4.0 and older) read a config register that doesn't
4a2fec
  * exist in real hardware, to get the RAM size from QEMU.
4a2fec
  */
4a2fec
@@ -238,29 +242,52 @@ static void i440fx_pcihost_get_pci_hole_end(Object *obj, Visitor *v,
4a2fec
     visit_type_uint32(v, name, &value, errp);
4a2fec
 }
4a2fec
 
4a2fec
+/*
4a2fec
+ * The 64bit PCI hole start is set by the Guest firmware
4a2fec
+ * as the address of the first 64bit PCI MEM resource.
4a2fec
+ * If no PCI device has resources on the 64bit area,
4a2fec
+ * the 64bit PCI hole will start after "over 4G RAM" and the
4a2fec
+ * reserved space for memory hotplug if any.
4a2fec
+ */
4a2fec
 static void i440fx_pcihost_get_pci_hole64_start(Object *obj, Visitor *v,
4a2fec
                                                 const char *name,
4a2fec
                                                 void *opaque, Error **errp)
4a2fec
 {
4a2fec
     PCIHostState *h = PCI_HOST_BRIDGE(obj);
4a2fec
+    I440FXState *s = I440FX_PCI_HOST_BRIDGE(obj);
4a2fec
     Range w64;
4a2fec
     uint64_t value;
4a2fec
 
4a2fec
     pci_bus_get_w64_range(h->bus, &w64);
4a2fec
     value = range_is_empty(&w64) ? 0 : range_lob(&w64);
4a2fec
+    if (!value && s->pci_hole64_fix) {
4a2fec
+        value = pc_pci_hole64_start();
4a2fec
+    }
4a2fec
     visit_type_uint64(v, name, &value, errp);
4a2fec
 }
4a2fec
 
4a2fec
+/*
4a2fec
+ * The 64bit PCI hole end is set by the Guest firmware
4a2fec
+ * as the address of the last 64bit PCI MEM resource.
4a2fec
+ * Then it is expanded to the PCI_HOST_PROP_PCI_HOLE64_SIZE
4a2fec
+ * that can be configured by the user.
4a2fec
+ */
4a2fec
 static void i440fx_pcihost_get_pci_hole64_end(Object *obj, Visitor *v,
4a2fec
                                               const char *name, void *opaque,
4a2fec
                                               Error **errp)
4a2fec
 {
4a2fec
     PCIHostState *h = PCI_HOST_BRIDGE(obj);
4a2fec
+    I440FXState *s = I440FX_PCI_HOST_BRIDGE(obj);
4a2fec
+    uint64_t hole64_start = pc_pci_hole64_start();
4a2fec
     Range w64;
4a2fec
-    uint64_t value;
4a2fec
+    uint64_t value, hole64_end;
4a2fec
 
4a2fec
     pci_bus_get_w64_range(h->bus, &w64);
4a2fec
     value = range_is_empty(&w64) ? 0 : range_upb(&w64) + 1;
4a2fec
+    hole64_end = ROUND_UP(hole64_start + s->pci_hole64_size, 1ULL << 30);
4a2fec
+    if (s->pci_hole64_fix && value < hole64_end) {
4a2fec
+        value = hole64_end;
4a2fec
+    }
4a2fec
     visit_type_uint64(v, name, &value, errp);
4a2fec
 }
4a2fec
 
4a2fec
@@ -863,8 +890,9 @@ static const char *i440fx_pcihost_root_bus_path(PCIHostState *host_bridge,
4a2fec
 
4a2fec
 static Property i440fx_props[] = {
4a2fec
     DEFINE_PROP_SIZE(PCI_HOST_PROP_PCI_HOLE64_SIZE, I440FXState,
4a2fec
-                     pci_hole64_size, DEFAULT_PCI_HOLE64_SIZE),
4a2fec
+                     pci_hole64_size, I440FX_PCI_HOST_HOLE64_SIZE_DEFAULT),
4a2fec
     DEFINE_PROP_UINT32("short_root_bus", I440FXState, short_root_bus, 0),
4a2fec
+    DEFINE_PROP_BOOL("x-pci-hole64-fix", I440FXState, pci_hole64_fix, true),
4a2fec
     DEFINE_PROP_END_OF_LIST(),
4a2fec
 };
4a2fec
 
4a2fec
diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c
4a2fec
index 9cd07ce..d7cc898 100644
4a2fec
--- a/hw/pci-host/q35.c
4a2fec
+++ b/hw/pci-host/q35.c
4a2fec
@@ -37,6 +37,8 @@
4a2fec
  * Q35 host
4a2fec
  */
4a2fec
 
4a2fec
+#define Q35_PCI_HOST_HOLE64_SIZE_DEFAULT (1ULL << 35)
4a2fec
+
4a2fec
 static void q35_host_realize(DeviceState *dev, Error **errp)
4a2fec
 {
4a2fec
     PCIHostState *pci = PCI_HOST_BRIDGE(dev);
4a2fec
@@ -99,29 +101,52 @@ static void q35_host_get_pci_hole_end(Object *obj, Visitor *v,
4a2fec
     visit_type_uint32(v, name, &value, errp);
4a2fec
 }
4a2fec
 
4a2fec
+/*
4a2fec
+ * The 64bit PCI hole start is set by the Guest firmware
4a2fec
+ * as the address of the first 64bit PCI MEM resource.
4a2fec
+ * If no PCI device has resources on the 64bit area,
4a2fec
+ * the 64bit PCI hole will start after "over 4G RAM" and the
4a2fec
+ * reserved space for memory hotplug if any.
4a2fec
+ */
4a2fec
 static void q35_host_get_pci_hole64_start(Object *obj, Visitor *v,
4a2fec
                                           const char *name, void *opaque,
4a2fec
                                           Error **errp)
4a2fec
 {
4a2fec
     PCIHostState *h = PCI_HOST_BRIDGE(obj);
4a2fec
+    Q35PCIHost *s = Q35_HOST_DEVICE(obj);
4a2fec
     Range w64;
4a2fec
     uint64_t value;
4a2fec
 
4a2fec
     pci_bus_get_w64_range(h->bus, &w64);
4a2fec
     value = range_is_empty(&w64) ? 0 : range_lob(&w64);
4a2fec
+    if (!value && s->pci_hole64_fix) {
4a2fec
+        value = pc_pci_hole64_start();
4a2fec
+    }
4a2fec
     visit_type_uint64(v, name, &value, errp);
4a2fec
 }
4a2fec
 
4a2fec
+/*
4a2fec
+ * The 64bit PCI hole end is set by the Guest firmware
4a2fec
+ * as the address of the last 64bit PCI MEM resource.
4a2fec
+ * Then it is expanded to the PCI_HOST_PROP_PCI_HOLE64_SIZE
4a2fec
+ * that can be configured by the user.
4a2fec
+ */
4a2fec
 static void q35_host_get_pci_hole64_end(Object *obj, Visitor *v,
4a2fec
                                         const char *name, void *opaque,
4a2fec
                                         Error **errp)
4a2fec
 {
4a2fec
     PCIHostState *h = PCI_HOST_BRIDGE(obj);
4a2fec
+    Q35PCIHost *s = Q35_HOST_DEVICE(obj);
4a2fec
+    uint64_t hole64_start = pc_pci_hole64_start();
4a2fec
     Range w64;
4a2fec
-    uint64_t value;
4a2fec
+    uint64_t value, hole64_end;
4a2fec
 
4a2fec
     pci_bus_get_w64_range(h->bus, &w64);
4a2fec
     value = range_is_empty(&w64) ? 0 : range_upb(&w64) + 1;
4a2fec
+    hole64_end = ROUND_UP(hole64_start + s->mch.pci_hole64_size, 1ULL << 30);
4a2fec
+    if (s->pci_hole64_fix && value < hole64_end) {
4a2fec
+        value = hole64_end;
4a2fec
+    }
4a2fec
     visit_type_uint64(v, name, &value, errp);
4a2fec
 }
4a2fec
 
4a2fec
@@ -133,16 +158,25 @@ static void q35_host_get_mmcfg_size(Object *obj, Visitor *v, const char *name,
4a2fec
     visit_type_uint64(v, name, &e->size, errp);
4a2fec
 }
4a2fec
 
4a2fec
+/*
4a2fec
+ * NOTE: setting defaults for the mch.* fields in this table
4a2fec
+ * doesn't work, because mch is a separate QOM object that is
4a2fec
+ * zeroed by the object_initialize(&s->mch, ...) call inside
4a2fec
+ * q35_host_initfn().  The default values for those
4a2fec
+ * properties need to be initialized manually by
4a2fec
+ * q35_host_initfn() after the object_initialize() call.
4a2fec
+ */
4a2fec
 static Property q35_host_props[] = {
4a2fec
     DEFINE_PROP_UINT64(PCIE_HOST_MCFG_BASE, Q35PCIHost, parent_obj.base_addr,
4a2fec
                         MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT),
4a2fec
     DEFINE_PROP_SIZE(PCI_HOST_PROP_PCI_HOLE64_SIZE, Q35PCIHost,
4a2fec
-                     mch.pci_hole64_size, DEFAULT_PCI_HOLE64_SIZE),
4a2fec
+                     mch.pci_hole64_size, Q35_PCI_HOST_HOLE64_SIZE_DEFAULT),
4a2fec
     DEFINE_PROP_UINT32("short_root_bus", Q35PCIHost, mch.short_root_bus, 0),
4a2fec
     DEFINE_PROP_SIZE(PCI_HOST_BELOW_4G_MEM_SIZE, Q35PCIHost,
4a2fec
                      mch.below_4g_mem_size, 0),
4a2fec
     DEFINE_PROP_SIZE(PCI_HOST_ABOVE_4G_MEM_SIZE, Q35PCIHost,
4a2fec
                      mch.above_4g_mem_size, 0),
4a2fec
+    DEFINE_PROP_BOOL("x-pci-hole64-fix", Q35PCIHost, pci_hole64_fix, true),
4a2fec
     DEFINE_PROP_END_OF_LIST(),
4a2fec
 };
4a2fec
 
4a2fec
@@ -174,7 +208,9 @@ static void q35_host_initfn(Object *obj)
4a2fec
     object_property_add_child(OBJECT(s), "mch", OBJECT(&s->mch), NULL);
4a2fec
     qdev_prop_set_int32(DEVICE(&s->mch), "addr", PCI_DEVFN(0, 0));
4a2fec
     qdev_prop_set_bit(DEVICE(&s->mch), "multifunction", false);
4a2fec
-
4a2fec
+    /* mch's object_initialize resets the default value, set it again */
4a2fec
+    qdev_prop_set_uint64(DEVICE(s), PCI_HOST_PROP_PCI_HOLE64_SIZE,
4a2fec
+                         Q35_PCI_HOST_HOLE64_SIZE_DEFAULT);
4a2fec
     object_property_add(obj, PCI_HOST_PROP_PCI_HOLE_START, "uint32",
4a2fec
                         q35_host_get_pci_hole_start,
4a2fec
                         NULL, NULL, NULL, NULL);
4a2fec
diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
4a2fec
index 6f65d79..7b46121 100644
4a2fec
--- a/include/hw/i386/pc.h
4a2fec
+++ b/include/hw/i386/pc.h
4a2fec
@@ -241,7 +241,6 @@ void pc_guest_info_init(PCMachineState *pcms);
4a2fec
 #define PCI_HOST_PROP_PCI_HOLE64_SIZE  "pci-hole64-size"
4a2fec
 #define PCI_HOST_BELOW_4G_MEM_SIZE     "below-4g-mem-size"
4a2fec
 #define PCI_HOST_ABOVE_4G_MEM_SIZE     "above-4g-mem-size"
4a2fec
-#define DEFAULT_PCI_HOLE64_SIZE (~0x0ULL)
4a2fec
 
4a2fec
 
4a2fec
 void pc_pci_as_mapping_init(Object *owner, MemoryRegion *system_memory,
4a2fec
@@ -252,6 +251,7 @@ void pc_memory_init(PCMachineState *pcms,
4a2fec
                     MemoryRegion *system_memory,
4a2fec
                     MemoryRegion *rom_memory,
4a2fec
                     MemoryRegion **ram_memory);
4a2fec
+uint64_t pc_pci_hole64_start(void);
4a2fec
 qemu_irq pc_allocate_cpu_irq(void);
4a2fec
 DeviceState *pc_vga_init(ISABus *isa_bus, PCIBus *pci_bus);
4a2fec
 void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
4a2fec
@@ -1015,6 +1015,16 @@ extern void igd_passthrough_isa_bridge_create(PCIBus *bus, uint16_t gpu_dev_id);
4a2fec
             .driver   = "ICH9-LPC",\
4a2fec
             .property = "__com.redhat_force-rev1-fadt",\
4a2fec
             .value    = "on",\
4a2fec
+        },\
4a2fec
+        { /* PC_RHEL7_4_COMPAT from PC_COMPAT_2_10 */ \
4a2fec
+            .driver   = "i440FX-pcihost",\
4a2fec
+            .property = "x-pci-hole64-fix",\
4a2fec
+            .value    = "off",\
4a2fec
+        },\
4a2fec
+        { /* PC_RHEL7_4_COMPAT from PC_COMPAT_2_10 */ \
4a2fec
+            .driver   = "q35-pcihost",\
4a2fec
+            .property = "x-pci-hole64-fix",\
4a2fec
+            .value    = "off",\
4a2fec
         },
4a2fec
 
4a2fec
 
4a2fec
diff --git a/include/hw/pci-host/q35.h b/include/hw/pci-host/q35.h
4a2fec
index 58983c0..8f4ddde 100644
4a2fec
--- a/include/hw/pci-host/q35.h
4a2fec
+++ b/include/hw/pci-host/q35.h
4a2fec
@@ -68,6 +68,7 @@ typedef struct Q35PCIHost {
4a2fec
     PCIExpressHost parent_obj;
4a2fec
     /*< public >*/
4a2fec
 
4a2fec
+    bool pci_hole64_fix;
4a2fec
     MCHPCIState mch;
4a2fec
 } Q35PCIHost;
4a2fec
 
4a2fec
-- 
4a2fec
1.8.3.1
4a2fec