9ae3a8
From 8061ffe654905d1e184b82cb4b4d75647618a0c1 Mon Sep 17 00:00:00 2001
9ae3a8
From: Laszlo Ersek <lersek@redhat.com>
9ae3a8
Date: Thu, 24 Apr 2014 10:57:23 +0200
9ae3a8
Subject: [PATCH 11/12] uhci: UNfix irq routing for RHEL-6 machtypes (RHEL only)
9ae3a8
9ae3a8
RH-Author: Laszlo Ersek <lersek@redhat.com>
9ae3a8
Message-id: <1398337043-4967-1-git-send-email-lersek@redhat.com>
9ae3a8
Patchwork-id: 58540
9ae3a8
O-Subject: [RHEL-7.0 0day qemu-kvm PATCH] uhci: UNfix irq routing for RHEL-6 machtypes (RHEL only)
9ae3a8
Bugzilla: 1085701
9ae3a8
RH-Acked-by: Gerd Hoffmann <kraxel@redhat.com>
9ae3a8
RH-Acked-by: Michael S. Tsirkin <mst@redhat.com>
9ae3a8
RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
9ae3a8
9ae3a8
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1085701
9ae3a8
Brew:     https://brewweb.devel.redhat.com/taskinfo?taskID=7368968
9ae3a8
9ae3a8
Tested by myself and QE.
9ae3a8
9ae3a8
(Note that the bulk of the work related to this issue happened in bug
9ae3a8
1027565 comments 64 to 74, because QE was experiencing it with the other
9ae3a8
(original) issue reported in bug 1027565. Only later did I realize that
9ae3a8
bug 1085701 already existed for this specific problem.)
9ae3a8
9ae3a8
Refer to the following upstream commit, present in v1.2.0 and RHEL-7:
9ae3a8
9ae3a8
    commit 973002c11460efd3c17fe61a76711a103e30e1f9
9ae3a8
    Author: Gerd Hoffmann <kraxel@redhat.com>
9ae3a8
    Date:   Fri May 25 12:53:47 2012 +0200
9ae3a8
9ae3a8
        uhci: fix irq routing
9ae3a8
9ae3a8
        The multifunction ich9 ehci controller with uhci companions uses a
9ae3a8
        different interrupt pin for each function.  The three uhci devices
9ae3a8
        get pins A, B and C, whereas ehci uses pin D.  This way the guest
9ae3a8
        can assign different IRQ lines to each controller.
9ae3a8
9ae3a8
        Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
9ae3a8
9ae3a8
This is a performance optimization so that IRQ sharing can be avoided in
9ae3a8
the guest.
9ae3a8
9ae3a8
Contrarily, the RHEL-6 emulator exclusively assigns pin D to each of the
9ae3a8
three devices (see "hw/usb-uhci.c" there):
9ae3a8
- usb_uhci_ich9_1_initfn()
9ae3a8
- usb_uhci_ich9_2_initfn()
9ae3a8
- usb_uhci_ich9_3_initfn()
9ae3a8
9ae3a8
These init functions call usb_uhci_common_initfn(), which in turn assigns:
9ae3a8
9ae3a8
    pci_conf[0x3d] = 4; // interrupt pin 3
9ae3a8
9ae3a8
This mismatch breaks migration for rhel6.x.0 machine types from the
9ae3a8
RHEL-6.5 emulator to the RHEL-7.0 one.
9ae3a8
9ae3a8
For example, considering the uhci1 controller, the guest kernel, started
9ae3a8
on the RHEL-6.5 source host, will see pin D advertised, and (according to
9ae3a8
the PCI interrupt routing table provided by SeaBIOS) will route it to IRQ
9ae3a8
11:
9ae3a8
9ae3a8
  dmesg:
9ae3a8
9ae3a8
    uhci_hcd 0000:00:11.0: PCI INT D -> Link[LNKD] -> GSI 11 (level, high)
9ae3a8
    -> IRQ 11
9ae3a8
9ae3a8
  /proc/interrupts:
9ae3a8
9ae3a8
     10: ... IO-APIC-fasteoi   virtio0
9ae3a8
     11: ... IO-APIC-fasteoi   uhci_hcd:usb1
9ae3a8
9ae3a8
When the same guest kernel, using the same rhel6.5.0 machine type, is
9ae3a8
started fresh on the RHEL-7.0 target host, pin A is advertised instead
9ae3a8
(and routed to a different IRQ, based on the same table from SeaBIOS):
9ae3a8
9ae3a8
  dmesg:
9ae3a8
9ae3a8
    uhci_hcd 0000:00:11.0: PCI INT A -> Link[LNKA] -> GSI 10 (level, high)
9ae3a8
    -> IRQ 10
9ae3a8
9ae3a8
  /proc/interrupts:
9ae3a8
9ae3a8
     10: ... IO-APIC-fasteoi   uhci_hcd:usb1, virtio0
9ae3a8
9ae3a8
This is no problem as long as we don't migrate.
9ae3a8
9ae3a8
When we migrate the rhel6.x.0 machtype guest from the RHEL-6.5 host to the
9ae3a8
RHEL-7.0 host, the guest kernel (having booted on the source host) will
9ae3a8
expect the interrupts for the UHCI device on pin D / IRQ 11. However, the
9ae3a8
target host will inject the interrupts on pin A / IRQ 10. No handler in
9ae3a8
the guest kernel will claim such IRQ 10 instances (examples are: HDA or
9ae3a8
virtio-balloon), hence IRQ 10 will be disabled.
9ae3a8
9ae3a8
We can fix this in at least two ways:
9ae3a8
(1) Drop the persistent "UHCIState.irq_pin" field, and simply use the pin
9ae3a8
    identifier that is stored in (already migrated) PCI config space (at
9ae3a8
    offset 0x3d).
9ae3a8
(2) Introduce yet another RHEL-6 compatibility knob that selects pin D for
9ae3a8
    all three ICH9-UHCI controllers.
9ae3a8
9ae3a8
Since PCI config space could be write-accessible to the guest, plus it
9ae3a8
could originate from an untrusted migration source as well, and we'd use
9ae3a8
the setting as a subscript into the s->dev.irq[] array in
9ae3a8
uhci_update_irq(), it seems safer to go with (2).
9ae3a8
9ae3a8
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
9ae3a8
---
9ae3a8
 include/hw/usb.h  |  4 ++++
9ae3a8
 hw/i386/pc_piix.c |  1 +
9ae3a8
 hw/usb/hcd-uhci.c | 13 ++++++++++++-
9ae3a8
 3 files changed, 17 insertions(+), 1 deletion(-)
9ae3a8
9ae3a8
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
9ae3a8
---
9ae3a8
 hw/i386/pc_piix.c |    1 +
9ae3a8
 hw/usb/hcd-uhci.c |   13 ++++++++++++-
9ae3a8
 include/hw/usb.h  |    4 ++++
9ae3a8
 3 files changed, 17 insertions(+), 1 deletions(-)
9ae3a8
9ae3a8
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
9ae3a8
index fea98b6..67d51da 100644
9ae3a8
--- a/hw/i386/pc_piix.c
9ae3a8
+++ b/hw/i386/pc_piix.c
9ae3a8
@@ -955,6 +955,7 @@ static void pc_compat_rhel650(QEMUMachineInitArgs *args)
9ae3a8
     has_acpi_build = false;
9ae3a8
     gigabyte_align = false;
9ae3a8
     shadow_bios_after_incoming = true;
9ae3a8
+    ich9_uhci123_irqpin_override = true;
9ae3a8
 }
9ae3a8
 
9ae3a8
 static void pc_init_rhel650(QEMUMachineInitArgs *args)
9ae3a8
diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c
9ae3a8
index 4fc3410..3380107 100644
9ae3a8
--- a/hw/usb/hcd-uhci.c
9ae3a8
+++ b/hw/usb/hcd-uhci.c
9ae3a8
@@ -187,6 +187,8 @@ typedef struct UHCI_QH {
9ae3a8
     uint32_t el_link;
9ae3a8
 } UHCI_QH;
9ae3a8
 
9ae3a8
+bool ich9_uhci123_irqpin_override;
9ae3a8
+
9ae3a8
 static void uhci_async_cancel(UHCIAsync *async);
9ae3a8
 static void uhci_queue_fill(UHCIQueue *q, UHCI_TD *td);
9ae3a8
 
9ae3a8
@@ -1232,7 +1234,16 @@ static int usb_uhci_common_initfn(PCIDevice *dev)
9ae3a8
     /* TODO: reset value should be 0. */
9ae3a8
     pci_conf[USB_SBRN] = USB_RELEASE_1; // release number
9ae3a8
 
9ae3a8
-    s->irq_pin = u->info.irq_pin;
9ae3a8
+    if (ich9_uhci123_irqpin_override &&
9ae3a8
+        u->info.vendor_id == PCI_VENDOR_ID_INTEL &&
9ae3a8
+        (u->info.device_id == PCI_DEVICE_ID_INTEL_82801I_UHCI1 ||
9ae3a8
+         u->info.device_id == PCI_DEVICE_ID_INTEL_82801I_UHCI2 ||
9ae3a8
+         u->info.device_id == PCI_DEVICE_ID_INTEL_82801I_UHCI3)) {
9ae3a8
+        fprintf(stderr, "RHEL-6 compat: %s: irq_pin = 3\n", u->info.name);
9ae3a8
+        s->irq_pin = 3;
9ae3a8
+    } else {
9ae3a8
+        s->irq_pin = u->info.irq_pin;
9ae3a8
+    }
9ae3a8
     pci_config_set_interrupt_pin(pci_conf, s->irq_pin + 1);
9ae3a8
 
9ae3a8
     if (s->masterbus) {
9ae3a8
diff --git a/include/hw/usb.h b/include/hw/usb.h
9ae3a8
index b111be0..5668701 100644
9ae3a8
--- a/include/hw/usb.h
9ae3a8
+++ b/include/hw/usb.h
9ae3a8
@@ -578,4 +578,8 @@ int usb_get_quirks(uint16_t vendor_id, uint16_t product_id,
9ae3a8
                    uint8_t interface_class, uint8_t interface_subclass,
9ae3a8
                    uint8_t interface_protocol);
9ae3a8
 
9ae3a8
+
9ae3a8
+/* hcd-uhci.c -- RHEL-6 machine type compatibility */
9ae3a8
+extern bool ich9_uhci123_irqpin_override;
9ae3a8
+
9ae3a8
 #endif
9ae3a8
-- 
9ae3a8
1.7.1
9ae3a8