Loading hw/vfio/pci-quirks.c +168 −0 Original line number Diff line number Diff line Loading @@ -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 accessing * 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; } } hw/vfio/pci.c +1 −157 Original line number Diff line number Diff line Loading @@ -2315,162 +2315,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 accessing * 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); Loading Loading @@ -2619,7 +2463,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; Loading hw/vfio/pci.h +1 −0 Original line number Diff line number Diff line Loading @@ -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 */ trace-events +7 −0 Original line number Diff line number Diff line Loading @@ -1585,6 +1585,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 Loading Loading
hw/vfio/pci-quirks.c +168 −0 Original line number Diff line number Diff line Loading @@ -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 accessing * 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; } }
hw/vfio/pci.c +1 −157 Original line number Diff line number Diff line Loading @@ -2315,162 +2315,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 accessing * 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); Loading Loading @@ -2619,7 +2463,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; Loading
hw/vfio/pci.h +1 −0 Original line number Diff line number Diff line Loading @@ -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 */
trace-events +7 −0 Original line number Diff line number Diff line Loading @@ -1585,6 +1585,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 Loading