References: bsc#907514 bsc#910258 bsc#918984 bsc#923967

# Commit a88b72fddd046a0978242411276861039ec99ad0
# Date 2015-07-23 10:13:12 +0200
# Author Jan Beulich <jbeulich@suse.com>
# Committer Jan Beulich <jbeulich@suse.com>
x86/PCI: add config space abstract write intercept logic

This is to be used by MSI code, and later to also be hooked up to
MMCFG accesses by Dom0.

Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>

--- sle11sp3.orig/xen/arch/x86/msi.c	2015-09-28 10:31:15.000000000 +0200
+++ sle11sp3/xen/arch/x86/msi.c	2015-06-22 09:06:30.000000000 +0200
@@ -1063,6 +1063,12 @@ void pci_cleanup_msi(struct pci_dev *pde
     msi_free_irqs(pdev);
 }
 
+int pci_msi_conf_write_intercept(struct pci_dev *pdev, unsigned int reg,
+                                 unsigned int size, uint32_t *data)
+{
+    return 0;
+}
+
 int pci_restore_msi_state(struct pci_dev *pdev)
 {
     unsigned long flags;
--- sle11sp3.orig/xen/arch/x86/pci.c	2010-01-13 18:58:11.000000000 +0100
+++ sle11sp3/xen/arch/x86/pci.c	2015-07-21 10:40:34.000000000 +0200
@@ -67,3 +67,28 @@ void pci_conf_write(uint32_t cf8, uint8_
 
     spin_unlock_irqrestore(&pci_config_lock, flags);
 }
+
+int pci_conf_write_intercept(unsigned int seg, unsigned int bdf,
+                             unsigned int reg, unsigned int size,
+                             uint32_t *data)
+{
+    struct pci_dev *pdev;
+    int rc = 0;
+
+    /*
+     * Avoid expensive operations when no hook is going to do anything
+     * for the access anyway.
+     */
+    if ( reg < 64 || reg >= 256 )
+        return 0;
+
+    spin_lock(&pcidevs_lock);
+
+    pdev = pci_get_pdev(seg, PCI_BUS(bdf), PCI_DEVFN2(bdf));
+    if ( pdev )
+        rc = pci_msi_conf_write_intercept(pdev, reg, size, data);
+
+    spin_unlock(&pcidevs_lock);
+
+    return rc;
+}
--- sle11sp3.orig/xen/arch/x86/traps.c	2015-07-08 11:43:22.000000000 +0200
+++ sle11sp3/xen/arch/x86/traps.c	2015-07-21 10:40:37.000000000 +0200
@@ -1729,8 +1729,8 @@ static int admin_io_okay(
     return ioports_access_permitted(v->domain, port, port + bytes - 1);
 }
 
-static bool_t pci_cfg_ok(struct domain *currd, bool_t write,
-                         unsigned int start, unsigned int size)
+static bool_t pci_cfg_ok(struct domain *currd, unsigned int start,
+                         unsigned int size, uint32_t *write)
 {
     uint32_t machine_bdf;
 
@@ -1762,8 +1762,12 @@ static bool_t pci_cfg_ok(struct domain *
             start |= CF8_ADDR_HI(currd->arch.pci_cf8);
     }
 
-    return !xsm_pci_config_permission(currd, machine_bdf, start,
-                                      start + size - 1, write);
+    if ( xsm_pci_config_permission(currd, machine_bdf, start,
+                                   start + size - 1, !!write) != 0 )
+         return 0;
+
+    return !write ||
+           pci_conf_write_intercept(0, machine_bdf, start, size, write) >= 0;
 }
 
 static uint32_t guest_io_read(
@@ -1817,7 +1821,7 @@ static uint32_t guest_io_read(
             size = min(bytes, 4 - (port & 3));
             if ( size == 3 )
                 size = 2;
-            if ( pci_cfg_ok(v->domain, 0, port & 3, size) )
+            if ( pci_cfg_ok(v->domain, port & 3, size, NULL) )
                 sub_data = pci_conf_read(v->domain->arch.pci_cf8, port & 3, size);
         }
 
@@ -1890,7 +1894,7 @@ static void guest_io_write(
             size = min(bytes, 4 - (port & 3));
             if ( size == 3 )
                 size = 2;
-            if ( pci_cfg_ok(v->domain, 1, port & 3, size) )
+            if ( pci_cfg_ok(v->domain, port & 3, size, &data) )
                 pci_conf_write(v->domain->arch.pci_cf8, port & 3, size, data);
         }
 
--- sle11sp3.orig/xen/include/asm-x86/pci.h	2015-07-08 11:43:22.000000000 +0200
+++ sle11sp3/xen/include/asm-x86/pci.h	2015-07-21 10:39:13.000000000 +0200
@@ -15,4 +15,11 @@ struct arch_pci_dev {
     vmask_t used_vectors;
 };
 
+struct pci_dev;
+int pci_conf_write_intercept(unsigned int seg, unsigned int bdf,
+                             unsigned int reg, unsigned int size,
+                             uint32_t *data);
+int pci_msi_conf_write_intercept(struct pci_dev *, unsigned int reg,
+                                 unsigned int size, uint32_t *data);
+
 #endif /* __X86_PCI_H__ */
