qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH 15/17] vfio/pci: Move AMD device specific reset to q


From: Alex Williamson
Subject: [Qemu-devel] [PATCH 15/17] vfio/pci: Move AMD device specific reset to quirks
Date: Wed, 09 Sep 2015 12:31:06 -0600
User-agent: StGit/0.17.1-dirty

This is just another quirk, for reset rather than affecting memory
regions.  Move it to our new quirks file.

Signed-off-by: Alex Williamson <address@hidden>
---
 hw/vfio/pci-quirks.c |  168 ++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/vfio/pci.c        |  158 -----------------------------------------------
 hw/vfio/pci.h        |    1 
 trace-events         |    7 ++
 4 files changed, 177 insertions(+), 157 deletions(-)

diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c
index c70c004..03dbcf9 100644
--- a/hw/vfio/pci-quirks.c
+++ b/hw/vfio/pci-quirks.c
@@ -1046,3 +1046,171 @@ void vfio_bar_quirk_free(VFIOPCIDevice *vdev, int nr)
         g_free(quirk);
     }
 }
+
+/*
+ * Reset quirks
+ */
+
+/*
+ * AMD Radeon PCI config reset, based on Linux:
+ *   drivers/gpu/drm/radeon/ci_smc.c:ci_is_smc_running()
+ *   drivers/gpu/drm/radeon/radeon_device.c:radeon_pci_config_reset
+ *   drivers/gpu/drm/radeon/ci_smc.c:ci_reset_smc()
+ *   drivers/gpu/drm/radeon/ci_smc.c:ci_stop_smc_clock()
+ * IDs: include/drm/drm_pciids.h
+ * Registers: http://cgit.freedesktop.org/~agd5f/linux/commit/?id=4e2aa447f6f0
+ *
+ * Bonaire and Hawaii GPUs do not respond to a bus reset.  This is a bug in the
+ * hardware that should be fixed on future ASICs.  The symptom of this is that
+ * once the accerlated driver loads, Windows guests will bsod on subsequent
+ * attmpts to load the driver, such as after VM reset or shutdown/restart.  To
+ * work around this, we do an AMD specific PCI config reset, followed by an SMC
+ * reset.  The PCI config reset only works if SMC firmware is running, so we
+ * have a dependency on the state of the device as to whether this reset will
+ * be effective.  There are still cases where we won't be able to kick the
+ * device into working, but this greatly improves the usability overall.  The
+ * config reset magic is relatively common on AMD GPUs, but the setup and SMC
+ * poking is largely ASIC specific.
+ */
+static bool vfio_radeon_smc_is_running(VFIOPCIDevice *vdev)
+{
+    uint32_t clk, pc_c;
+
+    /*
+     * Registers 200h and 204h are index and data registers for acessing
+     * indirect configuration registers within the device.
+     */
+    vfio_region_write(&vdev->bars[5].region, 0x200, 0x80000004, 4);
+    clk = vfio_region_read(&vdev->bars[5].region, 0x204, 4);
+    vfio_region_write(&vdev->bars[5].region, 0x200, 0x80000370, 4);
+    pc_c = vfio_region_read(&vdev->bars[5].region, 0x204, 4);
+
+    return (!(clk & 1) && (0x20100 <= pc_c));
+}
+
+/*
+ * The scope of a config reset is controlled by a mode bit in the misc register
+ * and a fuse, exposed as a bit in another register.  The fuse is the default
+ * (0 = GFX, 1 = whole GPU), the misc bit is a toggle, with the forumula
+ * scope = !(misc ^ fuse), where the resulting scope is defined the same as
+ * the fuse.  A truth table therefore tells us that if misc == fuse, we need
+ * to flip the value of the bit in the misc register.
+ */
+static void vfio_radeon_set_gfx_only_reset(VFIOPCIDevice *vdev)
+{
+    uint32_t misc, fuse;
+    bool a, b;
+
+    vfio_region_write(&vdev->bars[5].region, 0x200, 0xc00c0000, 4);
+    fuse = vfio_region_read(&vdev->bars[5].region, 0x204, 4);
+    b = fuse & 64;
+
+    vfio_region_write(&vdev->bars[5].region, 0x200, 0xc0000010, 4);
+    misc = vfio_region_read(&vdev->bars[5].region, 0x204, 4);
+    a = misc & 2;
+
+    if (a == b) {
+        vfio_region_write(&vdev->bars[5].region, 0x204, misc ^ 2, 4);
+        vfio_region_read(&vdev->bars[5].region, 0x204, 4); /* flush */
+    }
+}
+
+static int vfio_radeon_reset(VFIOPCIDevice *vdev)
+{
+    PCIDevice *pdev = &vdev->pdev;
+    int i, ret = 0;
+    uint32_t data;
+
+    /* Defer to a kernel implemented reset */
+    if (vdev->vbasedev.reset_works) {
+        trace_vfio_quirk_ati_bonaire_reset_skipped(vdev->vbasedev.name);
+        return -ENODEV;
+    }
+
+    /* Enable only memory BAR access */
+    vfio_pci_write_config(pdev, PCI_COMMAND, PCI_COMMAND_MEMORY, 2);
+
+    /* Reset only works if SMC firmware is loaded and running */
+    if (!vfio_radeon_smc_is_running(vdev)) {
+        ret = -EINVAL;
+        trace_vfio_quirk_ati_bonaire_reset_no_smc(vdev->vbasedev.name);
+        goto out;
+    }
+
+    /* Make sure only the GFX function is reset */
+    vfio_radeon_set_gfx_only_reset(vdev);
+
+    /* AMD PCI config reset */
+    vfio_pci_write_config(pdev, 0x7c, 0x39d5e86b, 4);
+    usleep(100);
+
+    /* Read back the memory size to make sure we're out of reset */
+    for (i = 0; i < 100000; i++) {
+        if (vfio_region_read(&vdev->bars[5].region, 0x5428, 4) != 0xffffffff) {
+            goto reset_smc;
+        }
+        usleep(1);
+    }
+
+    trace_vfio_quirk_ati_bonaire_reset_timeout(vdev->vbasedev.name);
+
+reset_smc:
+    /* Reset SMC */
+    vfio_region_write(&vdev->bars[5].region, 0x200, 0x80000000, 4);
+    data = vfio_region_read(&vdev->bars[5].region, 0x204, 4);
+    data |= 1;
+    vfio_region_write(&vdev->bars[5].region, 0x204, data, 4);
+
+    /* Disable SMC clock */
+    vfio_region_write(&vdev->bars[5].region, 0x200, 0x80000004, 4);
+    data = vfio_region_read(&vdev->bars[5].region, 0x204, 4);
+    data |= 1;
+    vfio_region_write(&vdev->bars[5].region, 0x204, data, 4);
+
+    trace_vfio_quirk_ati_bonaire_reset_done(vdev->vbasedev.name);
+
+out:
+    /* Restore PCI command register */
+    vfio_pci_write_config(pdev, PCI_COMMAND, 0, 2);
+
+    return ret;
+}
+
+void vfio_setup_resetfn_quirk(VFIOPCIDevice *vdev)
+{
+    PCIDevice *pdev = &vdev->pdev;
+    uint16_t vendor, device;
+
+    vendor = pci_get_word(pdev->config + PCI_VENDOR_ID);
+    device = pci_get_word(pdev->config + PCI_DEVICE_ID);
+
+    switch (vendor) {
+    case 0x1002:
+        switch (device) {
+        /* Bonaire */
+        case 0x6649: /* Bonaire [FirePro W5100] */
+        case 0x6650:
+        case 0x6651:
+        case 0x6658: /* Bonaire XTX [Radeon R7 260X] */
+        case 0x665c: /* Bonaire XT [Radeon HD 7790/8770 / R9 260 OEM] */
+        case 0x665d: /* Bonaire [Radeon R7 200 Series] */
+        /* Hawaii */
+        case 0x67A0: /* Hawaii XT GL [FirePro W9100] */
+        case 0x67A1: /* Hawaii PRO GL [FirePro W8100] */
+        case 0x67A2:
+        case 0x67A8:
+        case 0x67A9:
+        case 0x67AA:
+        case 0x67B0: /* Hawaii XT [Radeon R9 290X] */
+        case 0x67B1: /* Hawaii PRO [Radeon R9 290] */
+        case 0x67B8:
+        case 0x67B9:
+        case 0x67BA:
+        case 0x67BE:
+            vdev->resetfn = vfio_radeon_reset;
+            trace_vfio_quirk_ati_bonaire_reset(vdev->vbasedev.name);
+            break;
+        }
+        break;
+    }
+}
diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
index 692231b..fc50c86 100644
--- a/hw/vfio/pci.c
+++ b/hw/vfio/pci.c
@@ -2316,162 +2316,6 @@ static void vfio_unregister_req_notifier(VFIOPCIDevice 
*vdev)
     vdev->req_enabled = false;
 }
 
-/*
- * AMD Radeon PCI config reset, based on Linux:
- *   drivers/gpu/drm/radeon/ci_smc.c:ci_is_smc_running()
- *   drivers/gpu/drm/radeon/radeon_device.c:radeon_pci_config_reset
- *   drivers/gpu/drm/radeon/ci_smc.c:ci_reset_smc()
- *   drivers/gpu/drm/radeon/ci_smc.c:ci_stop_smc_clock()
- * IDs: include/drm/drm_pciids.h
- * Registers: http://cgit.freedesktop.org/~agd5f/linux/commit/?id=4e2aa447f6f0
- *
- * Bonaire and Hawaii GPUs do not respond to a bus reset.  This is a bug in the
- * hardware that should be fixed on future ASICs.  The symptom of this is that
- * once the accerlated driver loads, Windows guests will bsod on subsequent
- * attmpts to load the driver, such as after VM reset or shutdown/restart.  To
- * work around this, we do an AMD specific PCI config reset, followed by an SMC
- * reset.  The PCI config reset only works if SMC firmware is running, so we
- * have a dependency on the state of the device as to whether this reset will
- * be effective.  There are still cases where we won't be able to kick the
- * device into working, but this greatly improves the usability overall.  The
- * config reset magic is relatively common on AMD GPUs, but the setup and SMC
- * poking is largely ASIC specific.
- */
-static bool vfio_radeon_smc_is_running(VFIOPCIDevice *vdev)
-{
-    uint32_t clk, pc_c;
-
-    /*
-     * Registers 200h and 204h are index and data registers for acessing
-     * indirect configuration registers within the device.
-     */
-    vfio_region_write(&vdev->bars[5].region, 0x200, 0x80000004, 4);
-    clk = vfio_region_read(&vdev->bars[5].region, 0x204, 4);
-    vfio_region_write(&vdev->bars[5].region, 0x200, 0x80000370, 4);
-    pc_c = vfio_region_read(&vdev->bars[5].region, 0x204, 4);
-
-    return (!(clk & 1) && (0x20100 <= pc_c));
-}
-
-/*
- * The scope of a config reset is controlled by a mode bit in the misc register
- * and a fuse, exposed as a bit in another register.  The fuse is the default
- * (0 = GFX, 1 = whole GPU), the misc bit is a toggle, with the forumula
- * scope = !(misc ^ fuse), where the resulting scope is defined the same as
- * the fuse.  A truth table therefore tells us that if misc == fuse, we need
- * to flip the value of the bit in the misc register.
- */
-static void vfio_radeon_set_gfx_only_reset(VFIOPCIDevice *vdev)
-{
-    uint32_t misc, fuse;
-    bool a, b;
-
-    vfio_region_write(&vdev->bars[5].region, 0x200, 0xc00c0000, 4);
-    fuse = vfio_region_read(&vdev->bars[5].region, 0x204, 4);
-    b = fuse & 64;
-
-    vfio_region_write(&vdev->bars[5].region, 0x200, 0xc0000010, 4);
-    misc = vfio_region_read(&vdev->bars[5].region, 0x204, 4);
-    a = misc & 2;
-
-    if (a == b) {
-        vfio_region_write(&vdev->bars[5].region, 0x204, misc ^ 2, 4);
-        vfio_region_read(&vdev->bars[5].region, 0x204, 4); /* flush */
-    }
-}
-
-static int vfio_radeon_reset(VFIOPCIDevice *vdev)
-{
-    PCIDevice *pdev = &vdev->pdev;
-    int i, ret = 0;
-    uint32_t data;
-
-    /* Defer to a kernel implemented reset */
-    if (vdev->vbasedev.reset_works) {
-        return -ENODEV;
-    }
-
-    /* Enable only memory BAR access */
-    vfio_pci_write_config(pdev, PCI_COMMAND, PCI_COMMAND_MEMORY, 2);
-
-    /* Reset only works if SMC firmware is loaded and running */
-    if (!vfio_radeon_smc_is_running(vdev)) {
-        ret = -EINVAL;
-        goto out;
-    }
-
-    /* Make sure only the GFX function is reset */
-    vfio_radeon_set_gfx_only_reset(vdev);
-
-    /* AMD PCI config reset */
-    vfio_pci_write_config(pdev, 0x7c, 0x39d5e86b, 4);
-    usleep(100);
-
-    /* Read back the memory size to make sure we're out of reset */
-    for (i = 0; i < 100000; i++) {
-        if (vfio_region_read(&vdev->bars[5].region, 0x5428, 4) != 0xffffffff) {
-            break;
-        }
-        usleep(1);
-    }
-
-    /* Reset SMC */
-    vfio_region_write(&vdev->bars[5].region, 0x200, 0x80000000, 4);
-    data = vfio_region_read(&vdev->bars[5].region, 0x204, 4);
-    data |= 1;
-    vfio_region_write(&vdev->bars[5].region, 0x204, data, 4);
-
-    /* Disable SMC clock */
-    vfio_region_write(&vdev->bars[5].region, 0x200, 0x80000004, 4);
-    data = vfio_region_read(&vdev->bars[5].region, 0x204, 4);
-    data |= 1;
-    vfio_region_write(&vdev->bars[5].region, 0x204, data, 4);
-
-out:
-    /* Restore PCI command register */
-    vfio_pci_write_config(pdev, PCI_COMMAND, 0, 2);
-
-    return ret;
-}
-
-static void vfio_setup_resetfn(VFIOPCIDevice *vdev)
-{
-    PCIDevice *pdev = &vdev->pdev;
-    uint16_t vendor, device;
-
-    vendor = pci_get_word(pdev->config + PCI_VENDOR_ID);
-    device = pci_get_word(pdev->config + PCI_DEVICE_ID);
-
-    switch (vendor) {
-    case 0x1002:
-        switch (device) {
-        /* Bonaire */
-        case 0x6649: /* Bonaire [FirePro W5100] */
-        case 0x6650:
-        case 0x6651:
-        case 0x6658: /* Bonaire XTX [Radeon R7 260X] */
-        case 0x665c: /* Bonaire XT [Radeon HD 7790/8770 / R9 260 OEM] */
-        case 0x665d: /* Bonaire [Radeon R7 200 Series] */
-        /* Hawaii */
-        case 0x67A0: /* Hawaii XT GL [FirePro W9100] */
-        case 0x67A1: /* Hawaii PRO GL [FirePro W8100] */
-        case 0x67A2:
-        case 0x67A8:
-        case 0x67A9:
-        case 0x67AA:
-        case 0x67B0: /* Hawaii XT [Radeon R9 290X] */
-        case 0x67B1: /* Hawaii PRO [Radeon R9 290] */
-        case 0x67B8:
-        case 0x67B9:
-        case 0x67BA:
-        case 0x67BE:
-            vdev->resetfn = vfio_radeon_reset;
-            break;
-        }
-        break;
-    }
-}
-
 static int vfio_initfn(PCIDevice *pdev)
 {
     VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pdev);
@@ -2620,7 +2464,7 @@ static int vfio_initfn(PCIDevice *pdev)
 
     vfio_register_err_notifier(vdev);
     vfio_register_req_notifier(vdev);
-    vfio_setup_resetfn(vdev);
+    vfio_setup_resetfn_quirk(vdev);
 
     return 0;
 
diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h
index ddaf3e9..e695952 100644
--- a/hw/vfio/pci.h
+++ b/hw/vfio/pci.h
@@ -148,5 +148,6 @@ void vfio_vga_quirk_free(VFIOPCIDevice *vdev);
 void vfio_bar_quirk_setup(VFIOPCIDevice *vdev, int nr);
 void vfio_bar_quirk_teardown(VFIOPCIDevice *vdev, int nr);
 void vfio_bar_quirk_free(VFIOPCIDevice *vdev, int nr);
+void vfio_setup_resetfn_quirk(VFIOPCIDevice *vdev);
 
 #endif /* HW_VFIO_VFIO_PCI_H */
diff --git a/trace-events b/trace-events
index bc5dc9c..9b630c1 100644
--- a/trace-events
+++ b/trace-events
@@ -1572,6 +1572,13 @@ vfio_quirk_rtl8168_msix_write(const char *name, uint16_t 
offset, uint64_t val) "
 vfio_quirk_rtl8168_msix_read(const char *name, uint16_t offset, uint64_t val) 
"%s MSI-X table read[0x%x]: 0x%"PRIx64
 vfio_quirk_rtl8168_probe(const char *name) "%s"
 
+vfio_quirk_ati_bonaire_reset_skipped(const char *name) "%s"
+vfio_quirk_ati_bonaire_reset_no_smc(const char *name) "%s"
+vfio_quirk_ati_bonaire_reset_timeout(const char *name) "%s"
+vfio_quirk_ati_bonaire_reset_done(const char *name) "%s"
+vfio_quirk_ati_bonaire_reset(const char *name) "%s"
+
+
 # hw/vfio/vfio-common.c
 vfio_region_write(const char *name, int index, uint64_t addr, uint64_t data, 
unsigned size) " (%s:region%d+0x%"PRIx64", 0x%"PRIx64 ", %d)"
 vfio_region_read(char *name, int index, uint64_t addr, unsigned size, uint64_t 
data) " (%s:region%d+0x%"PRIx64", %d) = 0x%"PRIx64




reply via email to

[Prev in Thread] Current Thread [Next in Thread]