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

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