xref: /qemu/hw/vfio/pci-quirks.c (revision b946d286114e09a81c303c7ec8ec3f7b33dff9e8)
1c00d61d8SAlex Williamson /*
2c00d61d8SAlex Williamson  * device quirks for PCI devices
3c00d61d8SAlex Williamson  *
4c00d61d8SAlex Williamson  * Copyright Red Hat, Inc. 2012-2015
5c00d61d8SAlex Williamson  *
6c00d61d8SAlex Williamson  * Authors:
7c00d61d8SAlex Williamson  *  Alex Williamson <alex.williamson@redhat.com>
8c00d61d8SAlex Williamson  *
9c00d61d8SAlex Williamson  * This work is licensed under the terms of the GNU GPL, version 2.  See
10c00d61d8SAlex Williamson  * the COPYING file in the top-level directory.
11c00d61d8SAlex Williamson  */
12c00d61d8SAlex Williamson 
13c00d61d8SAlex Williamson #include "pci.h"
14c00d61d8SAlex Williamson #include "trace.h"
15c00d61d8SAlex Williamson #include "qemu/range.h"
16c00d61d8SAlex Williamson 
17056dfcb6SAlex Williamson #define PCI_ANY_ID (~0)
18056dfcb6SAlex Williamson 
19056dfcb6SAlex Williamson /* Use uin32_t for vendor & device so PCI_ANY_ID expands and cannot match hw */
20056dfcb6SAlex Williamson static bool vfio_pci_is(VFIOPCIDevice *vdev, uint32_t vendor, uint32_t device)
21056dfcb6SAlex Williamson {
22056dfcb6SAlex Williamson     PCIDevice *pdev = &vdev->pdev;
23056dfcb6SAlex Williamson 
24056dfcb6SAlex Williamson     return (vendor == PCI_ANY_ID ||
25056dfcb6SAlex Williamson             vendor == pci_get_word(pdev->config + PCI_VENDOR_ID)) &&
26056dfcb6SAlex Williamson            (device == PCI_ANY_ID ||
27056dfcb6SAlex Williamson             device == pci_get_word(pdev->config + PCI_DEVICE_ID));
28056dfcb6SAlex Williamson }
29056dfcb6SAlex Williamson 
30c00d61d8SAlex Williamson /*
31c00d61d8SAlex Williamson  * List of device ids/vendor ids for which to disable
32c00d61d8SAlex Williamson  * option rom loading. This avoids the guest hangs during rom
33c00d61d8SAlex Williamson  * execution as noticed with the BCM 57810 card for lack of a
34c00d61d8SAlex Williamson  * more better way to handle such issues.
35c00d61d8SAlex Williamson  * The  user can still override by specifying a romfile or
36c00d61d8SAlex Williamson  * rombar=1.
37c00d61d8SAlex Williamson  * Please see https://bugs.launchpad.net/qemu/+bug/1284874
38c00d61d8SAlex Williamson  * for an analysis of the 57810 card hang. When adding
39c00d61d8SAlex Williamson  * a new vendor id/device id combination below, please also add
40c00d61d8SAlex Williamson  * your card/environment details and information that could
41c00d61d8SAlex Williamson  * help in debugging to the bug tracking this issue
42c00d61d8SAlex Williamson  */
43056dfcb6SAlex Williamson static const struct {
44056dfcb6SAlex Williamson     uint32_t vendor;
45056dfcb6SAlex Williamson     uint32_t device;
46056dfcb6SAlex Williamson } romblacklist[] = {
47056dfcb6SAlex Williamson     { 0x14e4, 0x168e }, /* Broadcom BCM 57810 */
48c00d61d8SAlex Williamson };
49c00d61d8SAlex Williamson 
50c00d61d8SAlex Williamson bool vfio_blacklist_opt_rom(VFIOPCIDevice *vdev)
51c00d61d8SAlex Williamson {
52056dfcb6SAlex Williamson     int i;
53c00d61d8SAlex Williamson 
54056dfcb6SAlex Williamson     for (i = 0 ; i < ARRAY_SIZE(romblacklist); i++) {
55056dfcb6SAlex Williamson         if (vfio_pci_is(vdev, romblacklist[i].vendor, romblacklist[i].device)) {
56056dfcb6SAlex Williamson             trace_vfio_quirk_rom_blacklisted(vdev->vbasedev.name,
57056dfcb6SAlex Williamson                                              romblacklist[i].vendor,
58056dfcb6SAlex Williamson                                              romblacklist[i].device);
59c00d61d8SAlex Williamson             return true;
60c00d61d8SAlex Williamson         }
61c00d61d8SAlex Williamson     }
62c00d61d8SAlex Williamson     return false;
63c00d61d8SAlex Williamson }
64c00d61d8SAlex Williamson 
65c00d61d8SAlex Williamson /*
66c00d61d8SAlex Williamson  * Device specific quirks
67c00d61d8SAlex Williamson  */
68c00d61d8SAlex Williamson 
69c00d61d8SAlex Williamson /* Is range1 fully contained within range2?  */
70c00d61d8SAlex Williamson static bool vfio_range_contained(uint64_t first1, uint64_t len1,
71c00d61d8SAlex Williamson                                  uint64_t first2, uint64_t len2) {
72c00d61d8SAlex Williamson     return (first1 >= first2 && first1 + len1 <= first2 + len2);
73c00d61d8SAlex Williamson }
74c00d61d8SAlex Williamson 
75c00d61d8SAlex Williamson static bool vfio_flags_enabled(uint8_t flags, uint8_t mask)
76c00d61d8SAlex Williamson {
77c00d61d8SAlex Williamson     return (mask && (flags & mask) == mask);
78c00d61d8SAlex Williamson }
79c00d61d8SAlex Williamson 
80c00d61d8SAlex Williamson static uint64_t vfio_generic_window_quirk_read(void *opaque,
81c00d61d8SAlex Williamson                                                hwaddr addr, unsigned size)
82c00d61d8SAlex Williamson {
838c4f2348SAlex Williamson     VFIOLegacyQuirk *quirk = opaque;
84c00d61d8SAlex Williamson     VFIOPCIDevice *vdev = quirk->vdev;
85c00d61d8SAlex Williamson     uint64_t data;
86c00d61d8SAlex Williamson 
87c00d61d8SAlex Williamson     if (vfio_flags_enabled(quirk->data.flags, quirk->data.read_flags) &&
88c00d61d8SAlex Williamson         ranges_overlap(addr, size,
89c00d61d8SAlex Williamson                        quirk->data.data_offset, quirk->data.data_size)) {
90c00d61d8SAlex Williamson         hwaddr offset = addr - quirk->data.data_offset;
91c00d61d8SAlex Williamson 
92c00d61d8SAlex Williamson         if (!vfio_range_contained(addr, size, quirk->data.data_offset,
93c00d61d8SAlex Williamson                                   quirk->data.data_size)) {
94c00d61d8SAlex Williamson             hw_error("%s: window data read not fully contained: %s",
958c4f2348SAlex Williamson                      __func__, memory_region_name(quirk->mem));
96c00d61d8SAlex Williamson         }
97c00d61d8SAlex Williamson 
98c00d61d8SAlex Williamson         data = vfio_pci_read_config(&vdev->pdev,
99c00d61d8SAlex Williamson                                     quirk->data.address_val + offset, size);
100c00d61d8SAlex Williamson 
1018c4f2348SAlex Williamson         trace_vfio_generic_window_quirk_read(memory_region_name(quirk->mem),
102c00d61d8SAlex Williamson                                              vdev->vbasedev.name,
103c00d61d8SAlex Williamson                                              quirk->data.bar,
104c00d61d8SAlex Williamson                                              addr, size, data);
105c00d61d8SAlex Williamson     } else {
106c00d61d8SAlex Williamson         data = vfio_region_read(&vdev->bars[quirk->data.bar].region,
107c00d61d8SAlex Williamson                                 addr + quirk->data.base_offset, size);
108c00d61d8SAlex Williamson     }
109c00d61d8SAlex Williamson 
110c00d61d8SAlex Williamson     return data;
111c00d61d8SAlex Williamson }
112c00d61d8SAlex Williamson 
113c00d61d8SAlex Williamson static void vfio_generic_window_quirk_write(void *opaque, hwaddr addr,
114c00d61d8SAlex Williamson                                             uint64_t data, unsigned size)
115c00d61d8SAlex Williamson {
1168c4f2348SAlex Williamson     VFIOLegacyQuirk *quirk = opaque;
117c00d61d8SAlex Williamson     VFIOPCIDevice *vdev = quirk->vdev;
118c00d61d8SAlex Williamson 
119c00d61d8SAlex Williamson     if (ranges_overlap(addr, size,
120c00d61d8SAlex Williamson                        quirk->data.address_offset, quirk->data.address_size)) {
121c00d61d8SAlex Williamson 
122c00d61d8SAlex Williamson         if (addr != quirk->data.address_offset) {
123c00d61d8SAlex Williamson             hw_error("%s: offset write into address window: %s",
1248c4f2348SAlex Williamson                      __func__, memory_region_name(quirk->mem));
125c00d61d8SAlex Williamson         }
126c00d61d8SAlex Williamson 
127c00d61d8SAlex Williamson         if ((data & ~quirk->data.address_mask) == quirk->data.address_match) {
128c00d61d8SAlex Williamson             quirk->data.flags |= quirk->data.write_flags |
129c00d61d8SAlex Williamson                                  quirk->data.read_flags;
130c00d61d8SAlex Williamson             quirk->data.address_val = data & quirk->data.address_mask;
131c00d61d8SAlex Williamson         } else {
132c00d61d8SAlex Williamson             quirk->data.flags &= ~(quirk->data.write_flags |
133c00d61d8SAlex Williamson                                    quirk->data.read_flags);
134c00d61d8SAlex Williamson         }
135c00d61d8SAlex Williamson     }
136c00d61d8SAlex Williamson 
137c00d61d8SAlex Williamson     if (vfio_flags_enabled(quirk->data.flags, quirk->data.write_flags) &&
138c00d61d8SAlex Williamson         ranges_overlap(addr, size,
139c00d61d8SAlex Williamson                        quirk->data.data_offset, quirk->data.data_size)) {
140c00d61d8SAlex Williamson         hwaddr offset = addr - quirk->data.data_offset;
141c00d61d8SAlex Williamson 
142c00d61d8SAlex Williamson         if (!vfio_range_contained(addr, size, quirk->data.data_offset,
143c00d61d8SAlex Williamson                                   quirk->data.data_size)) {
144c00d61d8SAlex Williamson             hw_error("%s: window data write not fully contained: %s",
1458c4f2348SAlex Williamson                      __func__, memory_region_name(quirk->mem));
146c00d61d8SAlex Williamson         }
147c00d61d8SAlex Williamson 
148c00d61d8SAlex Williamson         vfio_pci_write_config(&vdev->pdev,
149c00d61d8SAlex Williamson                               quirk->data.address_val + offset, data, size);
1508c4f2348SAlex Williamson         trace_vfio_generic_window_quirk_write(memory_region_name(quirk->mem),
151c00d61d8SAlex Williamson                                               vdev->vbasedev.name,
152c00d61d8SAlex Williamson                                               quirk->data.bar,
153c00d61d8SAlex Williamson                                               addr, data, size);
154c00d61d8SAlex Williamson         return;
155c00d61d8SAlex Williamson     }
156c00d61d8SAlex Williamson 
157c00d61d8SAlex Williamson     vfio_region_write(&vdev->bars[quirk->data.bar].region,
158c00d61d8SAlex Williamson                    addr + quirk->data.base_offset, data, size);
159c00d61d8SAlex Williamson }
160c00d61d8SAlex Williamson 
161c00d61d8SAlex Williamson static const MemoryRegionOps vfio_generic_window_quirk = {
162c00d61d8SAlex Williamson     .read = vfio_generic_window_quirk_read,
163c00d61d8SAlex Williamson     .write = vfio_generic_window_quirk_write,
164c00d61d8SAlex Williamson     .endianness = DEVICE_LITTLE_ENDIAN,
165c00d61d8SAlex Williamson };
166c00d61d8SAlex Williamson 
167c00d61d8SAlex Williamson static uint64_t vfio_generic_quirk_read(void *opaque,
168c00d61d8SAlex Williamson                                         hwaddr addr, unsigned size)
169c00d61d8SAlex Williamson {
1708c4f2348SAlex Williamson     VFIOLegacyQuirk *quirk = opaque;
171c00d61d8SAlex Williamson     VFIOPCIDevice *vdev = quirk->vdev;
172c00d61d8SAlex Williamson     hwaddr base = quirk->data.address_match & TARGET_PAGE_MASK;
173c00d61d8SAlex Williamson     hwaddr offset = quirk->data.address_match & ~TARGET_PAGE_MASK;
174c00d61d8SAlex Williamson     uint64_t data;
175c00d61d8SAlex Williamson 
176c00d61d8SAlex Williamson     if (vfio_flags_enabled(quirk->data.flags, quirk->data.read_flags) &&
177c00d61d8SAlex Williamson         ranges_overlap(addr, size, offset, quirk->data.address_mask + 1)) {
178c00d61d8SAlex Williamson         if (!vfio_range_contained(addr, size, offset,
179c00d61d8SAlex Williamson                                   quirk->data.address_mask + 1)) {
180c00d61d8SAlex Williamson             hw_error("%s: read not fully contained: %s",
1818c4f2348SAlex Williamson                      __func__, memory_region_name(quirk->mem));
182c00d61d8SAlex Williamson         }
183c00d61d8SAlex Williamson 
184c00d61d8SAlex Williamson         data = vfio_pci_read_config(&vdev->pdev, addr - offset, size);
185c00d61d8SAlex Williamson 
1868c4f2348SAlex Williamson         trace_vfio_generic_quirk_read(memory_region_name(quirk->mem),
187c00d61d8SAlex Williamson                                       vdev->vbasedev.name, quirk->data.bar,
188c00d61d8SAlex Williamson                                       addr + base, size, data);
189c00d61d8SAlex Williamson     } else {
190c00d61d8SAlex Williamson         data = vfio_region_read(&vdev->bars[quirk->data.bar].region,
191c00d61d8SAlex Williamson                                 addr + base, size);
192c00d61d8SAlex Williamson     }
193c00d61d8SAlex Williamson 
194c00d61d8SAlex Williamson     return data;
195c00d61d8SAlex Williamson }
196c00d61d8SAlex Williamson 
197c00d61d8SAlex Williamson static void vfio_generic_quirk_write(void *opaque, hwaddr addr,
198c00d61d8SAlex Williamson                                      uint64_t data, unsigned size)
199c00d61d8SAlex Williamson {
2008c4f2348SAlex Williamson     VFIOLegacyQuirk *quirk = opaque;
201c00d61d8SAlex Williamson     VFIOPCIDevice *vdev = quirk->vdev;
202c00d61d8SAlex Williamson     hwaddr base = quirk->data.address_match & TARGET_PAGE_MASK;
203c00d61d8SAlex Williamson     hwaddr offset = quirk->data.address_match & ~TARGET_PAGE_MASK;
204c00d61d8SAlex Williamson 
205c00d61d8SAlex Williamson     if (vfio_flags_enabled(quirk->data.flags, quirk->data.write_flags) &&
206c00d61d8SAlex Williamson         ranges_overlap(addr, size, offset, quirk->data.address_mask + 1)) {
207c00d61d8SAlex Williamson         if (!vfio_range_contained(addr, size, offset,
208c00d61d8SAlex Williamson                                   quirk->data.address_mask + 1)) {
209c00d61d8SAlex Williamson             hw_error("%s: write not fully contained: %s",
2108c4f2348SAlex Williamson                      __func__, memory_region_name(quirk->mem));
211c00d61d8SAlex Williamson         }
212c00d61d8SAlex Williamson 
213c00d61d8SAlex Williamson         vfio_pci_write_config(&vdev->pdev, addr - offset, data, size);
214c00d61d8SAlex Williamson 
2158c4f2348SAlex Williamson         trace_vfio_generic_quirk_write(memory_region_name(quirk->mem),
216c00d61d8SAlex Williamson                                        vdev->vbasedev.name, quirk->data.bar,
217c00d61d8SAlex Williamson                                        addr + base, data, size);
218c00d61d8SAlex Williamson     } else {
219c00d61d8SAlex Williamson         vfio_region_write(&vdev->bars[quirk->data.bar].region,
220c00d61d8SAlex Williamson                           addr + base, data, size);
221c00d61d8SAlex Williamson     }
222c00d61d8SAlex Williamson }
223c00d61d8SAlex Williamson 
224c00d61d8SAlex Williamson static const MemoryRegionOps vfio_generic_quirk = {
225c00d61d8SAlex Williamson     .read = vfio_generic_quirk_read,
226c00d61d8SAlex Williamson     .write = vfio_generic_quirk_write,
227c00d61d8SAlex Williamson     .endianness = DEVICE_LITTLE_ENDIAN,
228c00d61d8SAlex Williamson };
229c00d61d8SAlex Williamson 
230c00d61d8SAlex Williamson #define PCI_VENDOR_ID_ATI               0x1002
231c00d61d8SAlex Williamson 
232c00d61d8SAlex Williamson /*
233c00d61d8SAlex Williamson  * Radeon HD cards (HD5450 & HD7850) report the upper byte of the I/O port BAR
234c00d61d8SAlex Williamson  * through VGA register 0x3c3.  On newer cards, the I/O port BAR is always
235c00d61d8SAlex Williamson  * BAR4 (older cards like the X550 used BAR1, but we don't care to support
236c00d61d8SAlex Williamson  * those).  Note that on bare metal, a read of 0x3c3 doesn't always return the
237c00d61d8SAlex Williamson  * I/O port BAR address.  Originally this was coded to return the virtual BAR
238c00d61d8SAlex Williamson  * address only if the physical register read returns the actual BAR address,
239c00d61d8SAlex Williamson  * but users have reported greater success if we return the virtual address
240c00d61d8SAlex Williamson  * unconditionally.
241c00d61d8SAlex Williamson  */
242c00d61d8SAlex Williamson static uint64_t vfio_ati_3c3_quirk_read(void *opaque,
243c00d61d8SAlex Williamson                                         hwaddr addr, unsigned size)
244c00d61d8SAlex Williamson {
245*b946d286SAlex Williamson     VFIOPCIDevice *vdev = opaque;
246c00d61d8SAlex Williamson     uint64_t data = vfio_pci_read_config(&vdev->pdev,
247*b946d286SAlex Williamson                                          PCI_BASE_ADDRESS_4 + 1, size);
248*b946d286SAlex Williamson 
249*b946d286SAlex Williamson     trace_vfio_quirk_ati_3c3_read(vdev->vbasedev.name, data);
250c00d61d8SAlex Williamson 
251c00d61d8SAlex Williamson     return data;
252c00d61d8SAlex Williamson }
253c00d61d8SAlex Williamson 
254c00d61d8SAlex Williamson static const MemoryRegionOps vfio_ati_3c3_quirk = {
255c00d61d8SAlex Williamson     .read = vfio_ati_3c3_quirk_read,
256c00d61d8SAlex Williamson     .endianness = DEVICE_LITTLE_ENDIAN,
257c00d61d8SAlex Williamson };
258c00d61d8SAlex Williamson 
259c00d61d8SAlex Williamson static void vfio_vga_probe_ati_3c3_quirk(VFIOPCIDevice *vdev)
260c00d61d8SAlex Williamson {
261c00d61d8SAlex Williamson     VFIOQuirk *quirk;
262c00d61d8SAlex Williamson 
263c00d61d8SAlex Williamson     /*
264c00d61d8SAlex Williamson      * As long as the BAR is >= 256 bytes it will be aligned such that the
265c00d61d8SAlex Williamson      * lower byte is always zero.  Filter out anything else, if it exists.
266c00d61d8SAlex Williamson      */
267*b946d286SAlex Williamson     if (!vfio_pci_is(vdev, PCI_VENDOR_ID_ATI, PCI_ANY_ID) ||
268*b946d286SAlex Williamson         !vdev->bars[4].ioport || vdev->bars[4].region.size < 256) {
269c00d61d8SAlex Williamson         return;
270c00d61d8SAlex Williamson     }
271c00d61d8SAlex Williamson 
272c00d61d8SAlex Williamson     quirk = g_malloc0(sizeof(*quirk));
273*b946d286SAlex Williamson     quirk->mem = g_malloc0_n(sizeof(MemoryRegion), 1);
2748c4f2348SAlex Williamson     quirk->nr_mem = 1;
275c00d61d8SAlex Williamson 
276*b946d286SAlex Williamson     memory_region_init_io(quirk->mem, OBJECT(vdev), &vfio_ati_3c3_quirk, vdev,
277c00d61d8SAlex Williamson                           "vfio-ati-3c3-quirk", 1);
278c00d61d8SAlex Williamson     memory_region_add_subregion(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].mem,
2798c4f2348SAlex Williamson                                 3 /* offset 3 bytes from 0x3c0 */, quirk->mem);
280c00d61d8SAlex Williamson 
281c00d61d8SAlex Williamson     QLIST_INSERT_HEAD(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].quirks,
282c00d61d8SAlex Williamson                       quirk, next);
283c00d61d8SAlex Williamson 
284*b946d286SAlex Williamson     trace_vfio_quirk_ati_3c3_probe(vdev->vbasedev.name);
285c00d61d8SAlex Williamson }
286c00d61d8SAlex Williamson 
287c00d61d8SAlex Williamson /*
288c00d61d8SAlex Williamson  * Newer ATI/AMD devices, including HD5450 and HD7850, have a window to PCI
289c00d61d8SAlex Williamson  * config space through MMIO BAR2 at offset 0x4000.  Nothing seems to access
290c00d61d8SAlex Williamson  * the MMIO space directly, but a window to this space is provided through
291c00d61d8SAlex Williamson  * I/O port BAR4.  Offset 0x0 is the address register and offset 0x4 is the
292c00d61d8SAlex Williamson  * data register.  When the address is programmed to a range of 0x4000-0x4fff
293c00d61d8SAlex Williamson  * PCI configuration space is available.  Experimentation seems to indicate
294c00d61d8SAlex Williamson  * that only read-only access is provided, but we drop writes when the window
295c00d61d8SAlex Williamson  * is enabled to config space nonetheless.
296c00d61d8SAlex Williamson  */
297c00d61d8SAlex Williamson static void vfio_probe_ati_bar4_window_quirk(VFIOPCIDevice *vdev, int nr)
298c00d61d8SAlex Williamson {
299c00d61d8SAlex Williamson     PCIDevice *pdev = &vdev->pdev;
300c00d61d8SAlex Williamson     VFIOQuirk *quirk;
3018c4f2348SAlex Williamson     VFIOLegacyQuirk *legacy;
302c00d61d8SAlex Williamson 
303c00d61d8SAlex Williamson     if (!vdev->has_vga || nr != 4 ||
304c00d61d8SAlex Williamson         pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_ATI) {
305c00d61d8SAlex Williamson         return;
306c00d61d8SAlex Williamson     }
307c00d61d8SAlex Williamson 
308c00d61d8SAlex Williamson     quirk = g_malloc0(sizeof(*quirk));
3098c4f2348SAlex Williamson     quirk->data = legacy = g_malloc0(sizeof(*legacy));
3108c4f2348SAlex Williamson     quirk->mem = legacy->mem = g_malloc0_n(sizeof(MemoryRegion), 1);
3118c4f2348SAlex Williamson     quirk->nr_mem = 1;
3128c4f2348SAlex Williamson     legacy->vdev = vdev;
3138c4f2348SAlex Williamson     legacy->data.address_size = 4;
3148c4f2348SAlex Williamson     legacy->data.data_offset = 4;
3158c4f2348SAlex Williamson     legacy->data.data_size = 4;
3168c4f2348SAlex Williamson     legacy->data.address_match = 0x4000;
3178c4f2348SAlex Williamson     legacy->data.address_mask = PCIE_CONFIG_SPACE_SIZE - 1;
3188c4f2348SAlex Williamson     legacy->data.bar = nr;
3198c4f2348SAlex Williamson     legacy->data.read_flags = legacy->data.write_flags = 1;
320c00d61d8SAlex Williamson 
3218c4f2348SAlex Williamson     memory_region_init_io(quirk->mem, OBJECT(vdev),
3228c4f2348SAlex Williamson                           &vfio_generic_window_quirk, legacy,
323c00d61d8SAlex Williamson                           "vfio-ati-bar4-window-quirk", 8);
324c00d61d8SAlex Williamson     memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
3258c4f2348SAlex Williamson                           legacy->data.base_offset, quirk->mem, 1);
326c00d61d8SAlex Williamson 
327c00d61d8SAlex Williamson     QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
328c00d61d8SAlex Williamson 
329c00d61d8SAlex Williamson     trace_vfio_probe_ati_bar4_window_quirk(vdev->vbasedev.name);
330c00d61d8SAlex Williamson }
331c00d61d8SAlex Williamson 
332c00d61d8SAlex Williamson /*
333c00d61d8SAlex Williamson  * Trap the BAR2 MMIO window to config space as well.
334c00d61d8SAlex Williamson  */
335c00d61d8SAlex Williamson static void vfio_probe_ati_bar2_4000_quirk(VFIOPCIDevice *vdev, int nr)
336c00d61d8SAlex Williamson {
337c00d61d8SAlex Williamson     PCIDevice *pdev = &vdev->pdev;
338c00d61d8SAlex Williamson     VFIOQuirk *quirk;
3398c4f2348SAlex Williamson     VFIOLegacyQuirk *legacy;
340c00d61d8SAlex Williamson 
341c00d61d8SAlex Williamson     /* Only enable on newer devices where BAR2 is 64bit */
342c00d61d8SAlex Williamson     if (!vdev->has_vga || nr != 2 || !vdev->bars[2].mem64 ||
343c00d61d8SAlex Williamson         pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_ATI) {
344c00d61d8SAlex Williamson         return;
345c00d61d8SAlex Williamson     }
346c00d61d8SAlex Williamson 
347c00d61d8SAlex Williamson     quirk = g_malloc0(sizeof(*quirk));
3488c4f2348SAlex Williamson     quirk->data = legacy = g_malloc0(sizeof(*legacy));
3498c4f2348SAlex Williamson     quirk->mem = legacy->mem = g_malloc0_n(sizeof(MemoryRegion), 1);
3508c4f2348SAlex Williamson     quirk->nr_mem = 1;
3518c4f2348SAlex Williamson     legacy->vdev = vdev;
3528c4f2348SAlex Williamson     legacy->data.flags = legacy->data.read_flags = legacy->data.write_flags = 1;
3538c4f2348SAlex Williamson     legacy->data.address_match = 0x4000;
3548c4f2348SAlex Williamson     legacy->data.address_mask = PCIE_CONFIG_SPACE_SIZE - 1;
3558c4f2348SAlex Williamson     legacy->data.bar = nr;
356c00d61d8SAlex Williamson 
3578c4f2348SAlex Williamson     memory_region_init_io(quirk->mem, OBJECT(vdev), &vfio_generic_quirk, legacy,
358c00d61d8SAlex Williamson                           "vfio-ati-bar2-4000-quirk",
3598c4f2348SAlex Williamson                           TARGET_PAGE_ALIGN(legacy->data.address_mask + 1));
360c00d61d8SAlex Williamson     memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
3618c4f2348SAlex Williamson                           legacy->data.address_match & TARGET_PAGE_MASK,
3628c4f2348SAlex Williamson                           quirk->mem, 1);
363c00d61d8SAlex Williamson 
364c00d61d8SAlex Williamson     QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
365c00d61d8SAlex Williamson 
366c00d61d8SAlex Williamson     trace_vfio_probe_ati_bar2_4000_quirk(vdev->vbasedev.name);
367c00d61d8SAlex Williamson }
368c00d61d8SAlex Williamson 
369c00d61d8SAlex Williamson /*
370c00d61d8SAlex Williamson  * Older ATI/AMD cards like the X550 have a similar window to that above.
371c00d61d8SAlex Williamson  * I/O port BAR1 provides a window to a mirror of PCI config space located
372c00d61d8SAlex Williamson  * in BAR2 at offset 0xf00.  We don't care to support such older cards, but
373c00d61d8SAlex Williamson  * note it for future reference.
374c00d61d8SAlex Williamson  */
375c00d61d8SAlex Williamson 
376c00d61d8SAlex Williamson #define PCI_VENDOR_ID_NVIDIA                    0x10de
377c00d61d8SAlex Williamson 
378c00d61d8SAlex Williamson /*
379c00d61d8SAlex Williamson  * Nvidia has several different methods to get to config space, the
380c00d61d8SAlex Williamson  * nouveu project has several of these documented here:
381c00d61d8SAlex Williamson  * https://github.com/pathscale/envytools/tree/master/hwdocs
382c00d61d8SAlex Williamson  *
383c00d61d8SAlex Williamson  * The first quirk is actually not documented in envytools and is found
384c00d61d8SAlex Williamson  * on 10de:01d1 (NVIDIA Corporation G72 [GeForce 7300 LE]).  This is an
385c00d61d8SAlex Williamson  * NV46 chipset.  The backdoor uses the legacy VGA I/O ports to access
386c00d61d8SAlex Williamson  * the mirror of PCI config space found at BAR0 offset 0x1800.  The access
387c00d61d8SAlex Williamson  * sequence first writes 0x338 to I/O port 0x3d4.  The target offset is
388c00d61d8SAlex Williamson  * then written to 0x3d0.  Finally 0x538 is written for a read and 0x738
389c00d61d8SAlex Williamson  * is written for a write to 0x3d4.  The BAR0 offset is then accessible
390c00d61d8SAlex Williamson  * through 0x3d0.  This quirk doesn't seem to be necessary on newer cards
391c00d61d8SAlex Williamson  * that use the I/O port BAR5 window but it doesn't hurt to leave it.
392c00d61d8SAlex Williamson  */
393c00d61d8SAlex Williamson enum {
394c00d61d8SAlex Williamson     NV_3D0_NONE = 0,
395c00d61d8SAlex Williamson     NV_3D0_SELECT,
396c00d61d8SAlex Williamson     NV_3D0_WINDOW,
397c00d61d8SAlex Williamson     NV_3D0_READ,
398c00d61d8SAlex Williamson     NV_3D0_WRITE,
399c00d61d8SAlex Williamson };
400c00d61d8SAlex Williamson 
401c00d61d8SAlex Williamson static uint64_t vfio_nvidia_3d0_quirk_read(void *opaque,
402c00d61d8SAlex Williamson                                            hwaddr addr, unsigned size)
403c00d61d8SAlex Williamson {
4048c4f2348SAlex Williamson     VFIOLegacyQuirk *quirk = opaque;
405c00d61d8SAlex Williamson     VFIOPCIDevice *vdev = quirk->vdev;
406c00d61d8SAlex Williamson     PCIDevice *pdev = &vdev->pdev;
407c00d61d8SAlex Williamson     uint64_t data = vfio_vga_read(&vdev->vga.region[QEMU_PCI_VGA_IO_HI],
408c00d61d8SAlex Williamson                                   addr + quirk->data.base_offset, size);
409c00d61d8SAlex Williamson 
410c00d61d8SAlex Williamson     if (quirk->data.flags == NV_3D0_READ && addr == quirk->data.data_offset) {
411c00d61d8SAlex Williamson         data = vfio_pci_read_config(pdev, quirk->data.address_val, size);
412c00d61d8SAlex Williamson         trace_vfio_nvidia_3d0_quirk_read(size, data);
413c00d61d8SAlex Williamson     }
414c00d61d8SAlex Williamson 
415c00d61d8SAlex Williamson     quirk->data.flags = NV_3D0_NONE;
416c00d61d8SAlex Williamson 
417c00d61d8SAlex Williamson     return data;
418c00d61d8SAlex Williamson }
419c00d61d8SAlex Williamson 
420c00d61d8SAlex Williamson static void vfio_nvidia_3d0_quirk_write(void *opaque, hwaddr addr,
421c00d61d8SAlex Williamson                                         uint64_t data, unsigned size)
422c00d61d8SAlex Williamson {
4238c4f2348SAlex Williamson     VFIOLegacyQuirk *quirk = opaque;
424c00d61d8SAlex Williamson     VFIOPCIDevice *vdev = quirk->vdev;
425c00d61d8SAlex Williamson     PCIDevice *pdev = &vdev->pdev;
426c00d61d8SAlex Williamson 
427c00d61d8SAlex Williamson     switch (quirk->data.flags) {
428c00d61d8SAlex Williamson     case NV_3D0_NONE:
429c00d61d8SAlex Williamson         if (addr == quirk->data.address_offset && data == 0x338) {
430c00d61d8SAlex Williamson             quirk->data.flags = NV_3D0_SELECT;
431c00d61d8SAlex Williamson         }
432c00d61d8SAlex Williamson         break;
433c00d61d8SAlex Williamson     case NV_3D0_SELECT:
434c00d61d8SAlex Williamson         quirk->data.flags = NV_3D0_NONE;
435c00d61d8SAlex Williamson         if (addr == quirk->data.data_offset &&
436c00d61d8SAlex Williamson             (data & ~quirk->data.address_mask) == quirk->data.address_match) {
437c00d61d8SAlex Williamson             quirk->data.flags = NV_3D0_WINDOW;
438c00d61d8SAlex Williamson             quirk->data.address_val = data & quirk->data.address_mask;
439c00d61d8SAlex Williamson         }
440c00d61d8SAlex Williamson         break;
441c00d61d8SAlex Williamson     case NV_3D0_WINDOW:
442c00d61d8SAlex Williamson         quirk->data.flags = NV_3D0_NONE;
443c00d61d8SAlex Williamson         if (addr == quirk->data.address_offset) {
444c00d61d8SAlex Williamson             if (data == 0x538) {
445c00d61d8SAlex Williamson                 quirk->data.flags = NV_3D0_READ;
446c00d61d8SAlex Williamson             } else if (data == 0x738) {
447c00d61d8SAlex Williamson                 quirk->data.flags = NV_3D0_WRITE;
448c00d61d8SAlex Williamson             }
449c00d61d8SAlex Williamson         }
450c00d61d8SAlex Williamson         break;
451c00d61d8SAlex Williamson     case NV_3D0_WRITE:
452c00d61d8SAlex Williamson         quirk->data.flags = NV_3D0_NONE;
453c00d61d8SAlex Williamson         if (addr == quirk->data.data_offset) {
454c00d61d8SAlex Williamson             vfio_pci_write_config(pdev, quirk->data.address_val, data, size);
455c00d61d8SAlex Williamson             trace_vfio_nvidia_3d0_quirk_write(data, size);
456c00d61d8SAlex Williamson             return;
457c00d61d8SAlex Williamson         }
458c00d61d8SAlex Williamson         break;
459c00d61d8SAlex Williamson     }
460c00d61d8SAlex Williamson 
461c00d61d8SAlex Williamson     vfio_vga_write(&vdev->vga.region[QEMU_PCI_VGA_IO_HI],
462c00d61d8SAlex Williamson                    addr + quirk->data.base_offset, data, size);
463c00d61d8SAlex Williamson }
464c00d61d8SAlex Williamson 
465c00d61d8SAlex Williamson static const MemoryRegionOps vfio_nvidia_3d0_quirk = {
466c00d61d8SAlex Williamson     .read = vfio_nvidia_3d0_quirk_read,
467c00d61d8SAlex Williamson     .write = vfio_nvidia_3d0_quirk_write,
468c00d61d8SAlex Williamson     .endianness = DEVICE_LITTLE_ENDIAN,
469c00d61d8SAlex Williamson };
470c00d61d8SAlex Williamson 
471c00d61d8SAlex Williamson static void vfio_vga_probe_nvidia_3d0_quirk(VFIOPCIDevice *vdev)
472c00d61d8SAlex Williamson {
473c00d61d8SAlex Williamson     PCIDevice *pdev = &vdev->pdev;
474c00d61d8SAlex Williamson     VFIOQuirk *quirk;
4758c4f2348SAlex Williamson     VFIOLegacyQuirk *legacy;
476c00d61d8SAlex Williamson 
477c00d61d8SAlex Williamson     if (pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_NVIDIA ||
478c00d61d8SAlex Williamson         !vdev->bars[1].region.size) {
479c00d61d8SAlex Williamson         return;
480c00d61d8SAlex Williamson     }
481c00d61d8SAlex Williamson 
482c00d61d8SAlex Williamson     quirk = g_malloc0(sizeof(*quirk));
4838c4f2348SAlex Williamson     quirk->data = legacy = g_malloc0(sizeof(*legacy));
4848c4f2348SAlex Williamson     quirk->mem = legacy->mem = g_malloc0_n(sizeof(MemoryRegion), 1);
4858c4f2348SAlex Williamson     quirk->nr_mem = 1;
4868c4f2348SAlex Williamson     legacy->vdev = vdev;
4878c4f2348SAlex Williamson     legacy->data.base_offset = 0x10;
4888c4f2348SAlex Williamson     legacy->data.address_offset = 4;
4898c4f2348SAlex Williamson     legacy->data.address_size = 2;
4908c4f2348SAlex Williamson     legacy->data.address_match = 0x1800;
4918c4f2348SAlex Williamson     legacy->data.address_mask = PCI_CONFIG_SPACE_SIZE - 1;
4928c4f2348SAlex Williamson     legacy->data.data_offset = 0;
4938c4f2348SAlex Williamson     legacy->data.data_size = 4;
494c00d61d8SAlex Williamson 
4958c4f2348SAlex Williamson     memory_region_init_io(quirk->mem, OBJECT(vdev), &vfio_nvidia_3d0_quirk,
4968c4f2348SAlex Williamson                           legacy, "vfio-nvidia-3d0-quirk", 6);
497c00d61d8SAlex Williamson     memory_region_add_subregion(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].mem,
4988c4f2348SAlex Williamson                                 legacy->data.base_offset, quirk->mem);
499c00d61d8SAlex Williamson 
500c00d61d8SAlex Williamson     QLIST_INSERT_HEAD(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].quirks,
501c00d61d8SAlex Williamson                       quirk, next);
502c00d61d8SAlex Williamson 
503c00d61d8SAlex Williamson     trace_vfio_vga_probe_nvidia_3d0_quirk(vdev->vbasedev.name);
504c00d61d8SAlex Williamson }
505c00d61d8SAlex Williamson 
506c00d61d8SAlex Williamson /*
507c00d61d8SAlex Williamson  * The second quirk is documented in envytools.  The I/O port BAR5 is just
508c00d61d8SAlex Williamson  * a set of address/data ports to the MMIO BARs.  The BAR we care about is
509c00d61d8SAlex Williamson  * again BAR0.  This backdoor is apparently a bit newer than the one above
510c00d61d8SAlex Williamson  * so we need to not only trap 256 bytes @0x1800, but all of PCI config
511c00d61d8SAlex Williamson  * space, including extended space is available at the 4k @0x88000.
512c00d61d8SAlex Williamson  */
513c00d61d8SAlex Williamson enum {
514c00d61d8SAlex Williamson     NV_BAR5_ADDRESS = 0x1,
515c00d61d8SAlex Williamson     NV_BAR5_ENABLE = 0x2,
516c00d61d8SAlex Williamson     NV_BAR5_MASTER = 0x4,
517c00d61d8SAlex Williamson     NV_BAR5_VALID = 0x7,
518c00d61d8SAlex Williamson };
519c00d61d8SAlex Williamson 
520c00d61d8SAlex Williamson static void vfio_nvidia_bar5_window_quirk_write(void *opaque, hwaddr addr,
521c00d61d8SAlex Williamson                                                 uint64_t data, unsigned size)
522c00d61d8SAlex Williamson {
5238c4f2348SAlex Williamson     VFIOLegacyQuirk *quirk = opaque;
524c00d61d8SAlex Williamson 
525c00d61d8SAlex Williamson     switch (addr) {
526c00d61d8SAlex Williamson     case 0x0:
527c00d61d8SAlex Williamson         if (data & 0x1) {
528c00d61d8SAlex Williamson             quirk->data.flags |= NV_BAR5_MASTER;
529c00d61d8SAlex Williamson         } else {
530c00d61d8SAlex Williamson             quirk->data.flags &= ~NV_BAR5_MASTER;
531c00d61d8SAlex Williamson         }
532c00d61d8SAlex Williamson         break;
533c00d61d8SAlex Williamson     case 0x4:
534c00d61d8SAlex Williamson         if (data & 0x1) {
535c00d61d8SAlex Williamson             quirk->data.flags |= NV_BAR5_ENABLE;
536c00d61d8SAlex Williamson         } else {
537c00d61d8SAlex Williamson             quirk->data.flags &= ~NV_BAR5_ENABLE;
538c00d61d8SAlex Williamson         }
539c00d61d8SAlex Williamson         break;
540c00d61d8SAlex Williamson     case 0x8:
541c00d61d8SAlex Williamson         if (quirk->data.flags & NV_BAR5_MASTER) {
542c00d61d8SAlex Williamson             if ((data & ~0xfff) == 0x88000) {
543c00d61d8SAlex Williamson                 quirk->data.flags |= NV_BAR5_ADDRESS;
544c00d61d8SAlex Williamson                 quirk->data.address_val = data & 0xfff;
545c00d61d8SAlex Williamson             } else if ((data & ~0xff) == 0x1800) {
546c00d61d8SAlex Williamson                 quirk->data.flags |= NV_BAR5_ADDRESS;
547c00d61d8SAlex Williamson                 quirk->data.address_val = data & 0xff;
548c00d61d8SAlex Williamson             } else {
549c00d61d8SAlex Williamson                 quirk->data.flags &= ~NV_BAR5_ADDRESS;
550c00d61d8SAlex Williamson             }
551c00d61d8SAlex Williamson         }
552c00d61d8SAlex Williamson         break;
553c00d61d8SAlex Williamson     }
554c00d61d8SAlex Williamson 
555c00d61d8SAlex Williamson     vfio_generic_window_quirk_write(opaque, addr, data, size);
556c00d61d8SAlex Williamson }
557c00d61d8SAlex Williamson 
558c00d61d8SAlex Williamson static const MemoryRegionOps vfio_nvidia_bar5_window_quirk = {
559c00d61d8SAlex Williamson     .read = vfio_generic_window_quirk_read,
560c00d61d8SAlex Williamson     .write = vfio_nvidia_bar5_window_quirk_write,
561c00d61d8SAlex Williamson     .valid.min_access_size = 4,
562c00d61d8SAlex Williamson     .endianness = DEVICE_LITTLE_ENDIAN,
563c00d61d8SAlex Williamson };
564c00d61d8SAlex Williamson 
565c00d61d8SAlex Williamson static void vfio_probe_nvidia_bar5_window_quirk(VFIOPCIDevice *vdev, int nr)
566c00d61d8SAlex Williamson {
567c00d61d8SAlex Williamson     PCIDevice *pdev = &vdev->pdev;
568c00d61d8SAlex Williamson     VFIOQuirk *quirk;
5698c4f2348SAlex Williamson     VFIOLegacyQuirk *legacy;
570c00d61d8SAlex Williamson 
571c00d61d8SAlex Williamson     if (!vdev->has_vga || nr != 5 ||
572c00d61d8SAlex Williamson         pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_NVIDIA) {
573c00d61d8SAlex Williamson         return;
574c00d61d8SAlex Williamson     }
575c00d61d8SAlex Williamson 
576c00d61d8SAlex Williamson     quirk = g_malloc0(sizeof(*quirk));
5778c4f2348SAlex Williamson     quirk->data = legacy = g_malloc0(sizeof(*legacy));
5788c4f2348SAlex Williamson     quirk->mem = legacy->mem = g_malloc0_n(sizeof(MemoryRegion), 1);
5798c4f2348SAlex Williamson     quirk->nr_mem = 1;
5808c4f2348SAlex Williamson     legacy->vdev = vdev;
5818c4f2348SAlex Williamson     legacy->data.read_flags = legacy->data.write_flags = NV_BAR5_VALID;
5828c4f2348SAlex Williamson     legacy->data.address_offset = 0x8;
5838c4f2348SAlex Williamson     legacy->data.address_size = 0; /* actually 4, but avoids generic code */
5848c4f2348SAlex Williamson     legacy->data.data_offset = 0xc;
5858c4f2348SAlex Williamson     legacy->data.data_size = 4;
5868c4f2348SAlex Williamson     legacy->data.bar = nr;
587c00d61d8SAlex Williamson 
5888c4f2348SAlex Williamson     memory_region_init_io(quirk->mem, OBJECT(vdev),
5898c4f2348SAlex Williamson                           &vfio_nvidia_bar5_window_quirk, legacy,
590c00d61d8SAlex Williamson                           "vfio-nvidia-bar5-window-quirk", 16);
591c00d61d8SAlex Williamson     memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
5928c4f2348SAlex Williamson                                         0, quirk->mem, 1);
593c00d61d8SAlex Williamson 
594c00d61d8SAlex Williamson     QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
595c00d61d8SAlex Williamson 
596c00d61d8SAlex Williamson     trace_vfio_probe_nvidia_bar5_window_quirk(vdev->vbasedev.name);
597c00d61d8SAlex Williamson }
598c00d61d8SAlex Williamson 
599c00d61d8SAlex Williamson static void vfio_nvidia_88000_quirk_write(void *opaque, hwaddr addr,
600c00d61d8SAlex Williamson                                           uint64_t data, unsigned size)
601c00d61d8SAlex Williamson {
6028c4f2348SAlex Williamson     VFIOLegacyQuirk *quirk = opaque;
603c00d61d8SAlex Williamson     VFIOPCIDevice *vdev = quirk->vdev;
604c00d61d8SAlex Williamson     PCIDevice *pdev = &vdev->pdev;
605c00d61d8SAlex Williamson     hwaddr base = quirk->data.address_match & TARGET_PAGE_MASK;
606c00d61d8SAlex Williamson 
607c00d61d8SAlex Williamson     vfio_generic_quirk_write(opaque, addr, data, size);
608c00d61d8SAlex Williamson 
609c00d61d8SAlex Williamson     /*
610c00d61d8SAlex Williamson      * Nvidia seems to acknowledge MSI interrupts by writing 0xff to the
611c00d61d8SAlex Williamson      * MSI capability ID register.  Both the ID and next register are
612c00d61d8SAlex Williamson      * read-only, so we allow writes covering either of those to real hw.
613c00d61d8SAlex Williamson      * NB - only fixed for the 0x88000 MMIO window.
614c00d61d8SAlex Williamson      */
615c00d61d8SAlex Williamson     if ((pdev->cap_present & QEMU_PCI_CAP_MSI) &&
616c00d61d8SAlex Williamson         vfio_range_contained(addr, size, pdev->msi_cap, PCI_MSI_FLAGS)) {
617c00d61d8SAlex Williamson         vfio_region_write(&vdev->bars[quirk->data.bar].region,
618c00d61d8SAlex Williamson                           addr + base, data, size);
619c00d61d8SAlex Williamson     }
620c00d61d8SAlex Williamson }
621c00d61d8SAlex Williamson 
622c00d61d8SAlex Williamson static const MemoryRegionOps vfio_nvidia_88000_quirk = {
623c00d61d8SAlex Williamson     .read = vfio_generic_quirk_read,
624c00d61d8SAlex Williamson     .write = vfio_nvidia_88000_quirk_write,
625c00d61d8SAlex Williamson     .endianness = DEVICE_LITTLE_ENDIAN,
626c00d61d8SAlex Williamson };
627c00d61d8SAlex Williamson 
628c00d61d8SAlex Williamson /*
629c00d61d8SAlex Williamson  * Finally, BAR0 itself.  We want to redirect any accesses to either
630c00d61d8SAlex Williamson  * 0x1800 or 0x88000 through the PCI config space access functions.
631c00d61d8SAlex Williamson  *
632c00d61d8SAlex Williamson  * NB - quirk at a page granularity or else they don't seem to work when
633c00d61d8SAlex Williamson  *      BARs are mmap'd
634c00d61d8SAlex Williamson  *
635c00d61d8SAlex Williamson  * Here's offset 0x88000...
636c00d61d8SAlex Williamson  */
637c00d61d8SAlex Williamson static void vfio_probe_nvidia_bar0_88000_quirk(VFIOPCIDevice *vdev, int nr)
638c00d61d8SAlex Williamson {
639c00d61d8SAlex Williamson     PCIDevice *pdev = &vdev->pdev;
640c00d61d8SAlex Williamson     VFIOQuirk *quirk;
6418c4f2348SAlex Williamson     VFIOLegacyQuirk *legacy;
642c00d61d8SAlex Williamson     uint16_t vendor, class;
643c00d61d8SAlex Williamson 
644c00d61d8SAlex Williamson     vendor = pci_get_word(pdev->config + PCI_VENDOR_ID);
645c00d61d8SAlex Williamson     class = pci_get_word(pdev->config + PCI_CLASS_DEVICE);
646c00d61d8SAlex Williamson 
647c00d61d8SAlex Williamson     if (nr != 0 || vendor != PCI_VENDOR_ID_NVIDIA ||
648c00d61d8SAlex Williamson         class != PCI_CLASS_DISPLAY_VGA) {
649c00d61d8SAlex Williamson         return;
650c00d61d8SAlex Williamson     }
651c00d61d8SAlex Williamson 
652c00d61d8SAlex Williamson     quirk = g_malloc0(sizeof(*quirk));
6538c4f2348SAlex Williamson     quirk->data = legacy = g_malloc0(sizeof(*legacy));
6548c4f2348SAlex Williamson     quirk->mem = legacy->mem = g_malloc0_n(sizeof(MemoryRegion), 1);
6558c4f2348SAlex Williamson     quirk->nr_mem = 1;
6568c4f2348SAlex Williamson     legacy->vdev = vdev;
6578c4f2348SAlex Williamson     legacy->data.flags = legacy->data.read_flags = legacy->data.write_flags = 1;
6588c4f2348SAlex Williamson     legacy->data.address_match = 0x88000;
6598c4f2348SAlex Williamson     legacy->data.address_mask = PCIE_CONFIG_SPACE_SIZE - 1;
6608c4f2348SAlex Williamson     legacy->data.bar = nr;
661c00d61d8SAlex Williamson 
6628c4f2348SAlex Williamson     memory_region_init_io(quirk->mem, OBJECT(vdev), &vfio_nvidia_88000_quirk,
6638c4f2348SAlex Williamson                           legacy, "vfio-nvidia-bar0-88000-quirk",
6648c4f2348SAlex Williamson                           TARGET_PAGE_ALIGN(legacy->data.address_mask + 1));
665c00d61d8SAlex Williamson     memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
6668c4f2348SAlex Williamson                           legacy->data.address_match & TARGET_PAGE_MASK,
6678c4f2348SAlex Williamson                           quirk->mem, 1);
668c00d61d8SAlex Williamson 
669c00d61d8SAlex Williamson     QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
670c00d61d8SAlex Williamson 
671c00d61d8SAlex Williamson     trace_vfio_probe_nvidia_bar0_88000_quirk(vdev->vbasedev.name);
672c00d61d8SAlex Williamson }
673c00d61d8SAlex Williamson 
674c00d61d8SAlex Williamson /*
675c00d61d8SAlex Williamson  * And here's the same for BAR0 offset 0x1800...
676c00d61d8SAlex Williamson  */
677c00d61d8SAlex Williamson static void vfio_probe_nvidia_bar0_1800_quirk(VFIOPCIDevice *vdev, int nr)
678c00d61d8SAlex Williamson {
679c00d61d8SAlex Williamson     PCIDevice *pdev = &vdev->pdev;
680c00d61d8SAlex Williamson     VFIOQuirk *quirk;
6818c4f2348SAlex Williamson     VFIOLegacyQuirk *legacy;
682c00d61d8SAlex Williamson 
683c00d61d8SAlex Williamson     if (!vdev->has_vga || nr != 0 ||
684c00d61d8SAlex Williamson         pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_NVIDIA) {
685c00d61d8SAlex Williamson         return;
686c00d61d8SAlex Williamson     }
687c00d61d8SAlex Williamson 
688c00d61d8SAlex Williamson     /* Log the chipset ID */
689c00d61d8SAlex Williamson     trace_vfio_probe_nvidia_bar0_1800_quirk_id(
690c00d61d8SAlex Williamson             (unsigned int)(vfio_region_read(&vdev->bars[0].region, 0, 4) >> 20)
691c00d61d8SAlex Williamson             & 0xff);
692c00d61d8SAlex Williamson 
693c00d61d8SAlex Williamson     quirk = g_malloc0(sizeof(*quirk));
6948c4f2348SAlex Williamson     quirk->data = legacy = g_malloc0(sizeof(*legacy));
6958c4f2348SAlex Williamson     quirk->mem = legacy->mem = g_malloc0_n(sizeof(MemoryRegion), 1);
6968c4f2348SAlex Williamson     quirk->nr_mem = 1;
6978c4f2348SAlex Williamson     legacy->vdev = vdev;
6988c4f2348SAlex Williamson     legacy->data.flags = legacy->data.read_flags = legacy->data.write_flags = 1;
6998c4f2348SAlex Williamson     legacy->data.address_match = 0x1800;
7008c4f2348SAlex Williamson     legacy->data.address_mask = PCI_CONFIG_SPACE_SIZE - 1;
7018c4f2348SAlex Williamson     legacy->data.bar = nr;
702c00d61d8SAlex Williamson 
7038c4f2348SAlex Williamson     memory_region_init_io(quirk->mem, OBJECT(vdev), &vfio_generic_quirk, legacy,
704c00d61d8SAlex Williamson                           "vfio-nvidia-bar0-1800-quirk",
7058c4f2348SAlex Williamson                           TARGET_PAGE_ALIGN(legacy->data.address_mask + 1));
706c00d61d8SAlex Williamson     memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
7078c4f2348SAlex Williamson                           legacy->data.address_match & TARGET_PAGE_MASK,
7088c4f2348SAlex Williamson                           quirk->mem, 1);
709c00d61d8SAlex Williamson 
710c00d61d8SAlex Williamson     QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
711c00d61d8SAlex Williamson 
712c00d61d8SAlex Williamson     trace_vfio_probe_nvidia_bar0_1800_quirk(vdev->vbasedev.name);
713c00d61d8SAlex Williamson }
714c00d61d8SAlex Williamson 
715c00d61d8SAlex Williamson /*
716c00d61d8SAlex Williamson  * TODO - Some Nvidia devices provide config access to their companion HDA
717c00d61d8SAlex Williamson  * device and even to their parent bridge via these config space mirrors.
718c00d61d8SAlex Williamson  * Add quirks for those regions.
719c00d61d8SAlex Williamson  */
720c00d61d8SAlex Williamson 
721c00d61d8SAlex Williamson #define PCI_VENDOR_ID_REALTEK 0x10ec
722c00d61d8SAlex Williamson 
723c00d61d8SAlex Williamson /*
724c00d61d8SAlex Williamson  * RTL8168 devices have a backdoor that can access the MSI-X table.  At BAR2
725c00d61d8SAlex Williamson  * offset 0x70 there is a dword data register, offset 0x74 is a dword address
726c00d61d8SAlex Williamson  * register.  According to the Linux r8169 driver, the MSI-X table is addressed
727c00d61d8SAlex Williamson  * when the "type" portion of the address register is set to 0x1.  This appears
728c00d61d8SAlex Williamson  * to be bits 16:30.  Bit 31 is both a write indicator and some sort of
729c00d61d8SAlex Williamson  * "address latched" indicator.  Bits 12:15 are a mask field, which we can
730c00d61d8SAlex Williamson  * ignore because the MSI-X table should always be accessed as a dword (full
731c00d61d8SAlex Williamson  * mask).  Bits 0:11 is offset within the type.
732c00d61d8SAlex Williamson  *
733c00d61d8SAlex Williamson  * Example trace:
734c00d61d8SAlex Williamson  *
735c00d61d8SAlex Williamson  * Read from MSI-X table offset 0
736c00d61d8SAlex Williamson  * vfio: vfio_bar_write(0000:05:00.0:BAR2+0x74, 0x1f000, 4) // store read addr
737c00d61d8SAlex Williamson  * vfio: vfio_bar_read(0000:05:00.0:BAR2+0x74, 4) = 0x8001f000 // latch
738c00d61d8SAlex Williamson  * vfio: vfio_bar_read(0000:05:00.0:BAR2+0x70, 4) = 0xfee00398 // read data
739c00d61d8SAlex Williamson  *
740c00d61d8SAlex Williamson  * Write 0xfee00000 to MSI-X table offset 0
741c00d61d8SAlex Williamson  * vfio: vfio_bar_write(0000:05:00.0:BAR2+0x70, 0xfee00000, 4) // write data
742c00d61d8SAlex Williamson  * vfio: vfio_bar_write(0000:05:00.0:BAR2+0x74, 0x8001f000, 4) // do write
743c00d61d8SAlex Williamson  * vfio: vfio_bar_read(0000:05:00.0:BAR2+0x74, 4) = 0x1f000 // complete
744c00d61d8SAlex Williamson  */
745c00d61d8SAlex Williamson static uint64_t vfio_rtl8168_window_quirk_read(void *opaque,
746c00d61d8SAlex Williamson                                                hwaddr addr, unsigned size)
747c00d61d8SAlex Williamson {
7488c4f2348SAlex Williamson     VFIOLegacyQuirk *quirk = opaque;
749c00d61d8SAlex Williamson     VFIOPCIDevice *vdev = quirk->vdev;
750c00d61d8SAlex Williamson     uint64_t val = 0;
751c00d61d8SAlex Williamson 
752c00d61d8SAlex Williamson     if (!quirk->data.flags) { /* Non-MSI-X table access */
753c00d61d8SAlex Williamson         return vfio_region_read(&vdev->bars[quirk->data.bar].region,
754c00d61d8SAlex Williamson                                 addr + 0x70, size);
755c00d61d8SAlex Williamson     }
756c00d61d8SAlex Williamson 
757c00d61d8SAlex Williamson     switch (addr) {
758c00d61d8SAlex Williamson     case 4: /* address */
759c00d61d8SAlex Williamson         val = quirk->data.address_match ^ 0x80000000U; /* latch/complete */
760c00d61d8SAlex Williamson         break;
761c00d61d8SAlex Williamson     case 0: /* data */
762c00d61d8SAlex Williamson         if ((vdev->pdev.cap_present & QEMU_PCI_CAP_MSIX)) {
763c00d61d8SAlex Williamson             memory_region_dispatch_read(&vdev->pdev.msix_table_mmio,
764c00d61d8SAlex Williamson                                 (hwaddr)(quirk->data.address_match & 0xfff),
765c00d61d8SAlex Williamson                                 &val, size, MEMTXATTRS_UNSPECIFIED);
766c00d61d8SAlex Williamson         }
767c00d61d8SAlex Williamson         break;
768c00d61d8SAlex Williamson     }
769c00d61d8SAlex Williamson 
770c00d61d8SAlex Williamson     trace_vfio_rtl8168_quirk_read(vdev->vbasedev.name,
771c00d61d8SAlex Williamson                                   addr ? "address" : "data", val);
772c00d61d8SAlex Williamson     return val;
773c00d61d8SAlex Williamson }
774c00d61d8SAlex Williamson 
775c00d61d8SAlex Williamson static void vfio_rtl8168_window_quirk_write(void *opaque, hwaddr addr,
776c00d61d8SAlex Williamson                                             uint64_t data, unsigned size)
777c00d61d8SAlex Williamson {
7788c4f2348SAlex Williamson     VFIOLegacyQuirk *quirk = opaque;
779c00d61d8SAlex Williamson     VFIOPCIDevice *vdev = quirk->vdev;
780c00d61d8SAlex Williamson 
781c00d61d8SAlex Williamson     switch (addr) {
782c00d61d8SAlex Williamson     case 4: /* address */
783c00d61d8SAlex Williamson         if ((data & 0x7fff0000) == 0x10000) { /* MSI-X table */
784c00d61d8SAlex Williamson             quirk->data.flags = 1; /* Activate reads */
785c00d61d8SAlex Williamson             quirk->data.address_match = data;
786c00d61d8SAlex Williamson 
787c00d61d8SAlex Williamson             trace_vfio_rtl8168_quirk_write(vdev->vbasedev.name, data);
788c00d61d8SAlex Williamson 
789c00d61d8SAlex Williamson             if (data & 0x80000000U) { /* Do write */
790c00d61d8SAlex Williamson                 if (vdev->pdev.cap_present & QEMU_PCI_CAP_MSIX) {
791c00d61d8SAlex Williamson                     hwaddr offset = data & 0xfff;
792c00d61d8SAlex Williamson                     uint64_t val = quirk->data.address_mask;
793c00d61d8SAlex Williamson 
794c00d61d8SAlex Williamson                     trace_vfio_rtl8168_quirk_msix(vdev->vbasedev.name,
795c00d61d8SAlex Williamson                                                   (uint16_t)offset, val);
796c00d61d8SAlex Williamson 
797c00d61d8SAlex Williamson                     /* Write to the proper guest MSI-X table instead */
798c00d61d8SAlex Williamson                     memory_region_dispatch_write(&vdev->pdev.msix_table_mmio,
799c00d61d8SAlex Williamson                                                  offset, val, size,
800c00d61d8SAlex Williamson                                                  MEMTXATTRS_UNSPECIFIED);
801c00d61d8SAlex Williamson                 }
802c00d61d8SAlex Williamson                 return; /* Do not write guest MSI-X data to hardware */
803c00d61d8SAlex Williamson             }
804c00d61d8SAlex Williamson         } else {
805c00d61d8SAlex Williamson             quirk->data.flags = 0; /* De-activate reads, non-MSI-X */
806c00d61d8SAlex Williamson         }
807c00d61d8SAlex Williamson         break;
808c00d61d8SAlex Williamson     case 0: /* data */
809c00d61d8SAlex Williamson         quirk->data.address_mask = data;
810c00d61d8SAlex Williamson         break;
811c00d61d8SAlex Williamson     }
812c00d61d8SAlex Williamson 
813c00d61d8SAlex Williamson     vfio_region_write(&vdev->bars[quirk->data.bar].region,
814c00d61d8SAlex Williamson                       addr + 0x70, data, size);
815c00d61d8SAlex Williamson }
816c00d61d8SAlex Williamson 
817c00d61d8SAlex Williamson static const MemoryRegionOps vfio_rtl8168_window_quirk = {
818c00d61d8SAlex Williamson     .read = vfio_rtl8168_window_quirk_read,
819c00d61d8SAlex Williamson     .write = vfio_rtl8168_window_quirk_write,
820c00d61d8SAlex Williamson     .valid = {
821c00d61d8SAlex Williamson         .min_access_size = 4,
822c00d61d8SAlex Williamson         .max_access_size = 4,
823c00d61d8SAlex Williamson         .unaligned = false,
824c00d61d8SAlex Williamson     },
825c00d61d8SAlex Williamson     .endianness = DEVICE_LITTLE_ENDIAN,
826c00d61d8SAlex Williamson };
827c00d61d8SAlex Williamson 
828c00d61d8SAlex Williamson static void vfio_probe_rtl8168_bar2_window_quirk(VFIOPCIDevice *vdev, int nr)
829c00d61d8SAlex Williamson {
830c00d61d8SAlex Williamson     PCIDevice *pdev = &vdev->pdev;
831c00d61d8SAlex Williamson     VFIOQuirk *quirk;
8328c4f2348SAlex Williamson     VFIOLegacyQuirk *legacy;
833c00d61d8SAlex Williamson 
834c00d61d8SAlex Williamson     if (pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_REALTEK ||
835c00d61d8SAlex Williamson         pci_get_word(pdev->config + PCI_DEVICE_ID) != 0x8168 || nr != 2) {
836c00d61d8SAlex Williamson         return;
837c00d61d8SAlex Williamson     }
838c00d61d8SAlex Williamson 
839c00d61d8SAlex Williamson     quirk = g_malloc0(sizeof(*quirk));
8408c4f2348SAlex Williamson     quirk->data = legacy = g_malloc0(sizeof(*legacy));
8418c4f2348SAlex Williamson     quirk->mem = legacy->mem = g_malloc0_n(sizeof(MemoryRegion), 1);
8428c4f2348SAlex Williamson     quirk->nr_mem = 1;
8438c4f2348SAlex Williamson     legacy->vdev = vdev;
8448c4f2348SAlex Williamson     legacy->data.bar = nr;
845c00d61d8SAlex Williamson 
8468c4f2348SAlex Williamson     memory_region_init_io(quirk->mem, OBJECT(vdev), &vfio_rtl8168_window_quirk,
8478c4f2348SAlex Williamson                           legacy, "vfio-rtl8168-window-quirk", 8);
848c00d61d8SAlex Williamson     memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
8498c4f2348SAlex Williamson                                         0x70, quirk->mem, 1);
850c00d61d8SAlex Williamson 
851c00d61d8SAlex Williamson     QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
852c00d61d8SAlex Williamson 
853c00d61d8SAlex Williamson     trace_vfio_rtl8168_quirk_enable(vdev->vbasedev.name);
854c00d61d8SAlex Williamson }
855c00d61d8SAlex Williamson 
856c00d61d8SAlex Williamson /*
857c00d61d8SAlex Williamson  * Common quirk probe entry points.
858c00d61d8SAlex Williamson  */
859c00d61d8SAlex Williamson void vfio_vga_quirk_setup(VFIOPCIDevice *vdev)
860c00d61d8SAlex Williamson {
861c00d61d8SAlex Williamson     vfio_vga_probe_ati_3c3_quirk(vdev);
862c00d61d8SAlex Williamson     vfio_vga_probe_nvidia_3d0_quirk(vdev);
863c00d61d8SAlex Williamson }
864c00d61d8SAlex Williamson 
865c00d61d8SAlex Williamson void vfio_vga_quirk_teardown(VFIOPCIDevice *vdev)
866c00d61d8SAlex Williamson {
867c00d61d8SAlex Williamson     VFIOQuirk *quirk;
8688c4f2348SAlex Williamson     int i, j;
869c00d61d8SAlex Williamson 
870c00d61d8SAlex Williamson     for (i = 0; i < ARRAY_SIZE(vdev->vga.region); i++) {
871c00d61d8SAlex Williamson         QLIST_FOREACH(quirk, &vdev->vga.region[i].quirks, next) {
8728c4f2348SAlex Williamson             for (j = 0; j < quirk->nr_mem; j++) {
8738c4f2348SAlex Williamson                 memory_region_del_subregion(&vdev->vga.region[i].mem,
8748c4f2348SAlex Williamson                                             &quirk->mem[j]);
8758c4f2348SAlex Williamson             }
876c00d61d8SAlex Williamson         }
877c00d61d8SAlex Williamson     }
878c00d61d8SAlex Williamson }
879c00d61d8SAlex Williamson 
880c00d61d8SAlex Williamson void vfio_vga_quirk_free(VFIOPCIDevice *vdev)
881c00d61d8SAlex Williamson {
8828c4f2348SAlex Williamson     int i, j;
883c00d61d8SAlex Williamson 
884c00d61d8SAlex Williamson     for (i = 0; i < ARRAY_SIZE(vdev->vga.region); i++) {
885c00d61d8SAlex Williamson         while (!QLIST_EMPTY(&vdev->vga.region[i].quirks)) {
886c00d61d8SAlex Williamson             VFIOQuirk *quirk = QLIST_FIRST(&vdev->vga.region[i].quirks);
887c00d61d8SAlex Williamson             QLIST_REMOVE(quirk, next);
8888c4f2348SAlex Williamson             for (j = 0; j < quirk->nr_mem; j++) {
8898c4f2348SAlex Williamson                 object_unparent(OBJECT(&quirk->mem[j]));
8908c4f2348SAlex Williamson             }
8918c4f2348SAlex Williamson             g_free(quirk->mem);
8928c4f2348SAlex Williamson             g_free(quirk->data);
893c00d61d8SAlex Williamson             g_free(quirk);
894c00d61d8SAlex Williamson         }
895c00d61d8SAlex Williamson     }
896c00d61d8SAlex Williamson }
897c00d61d8SAlex Williamson 
898c00d61d8SAlex Williamson void vfio_bar_quirk_setup(VFIOPCIDevice *vdev, int nr)
899c00d61d8SAlex Williamson {
900c00d61d8SAlex Williamson     vfio_probe_ati_bar4_window_quirk(vdev, nr);
901c00d61d8SAlex Williamson     vfio_probe_ati_bar2_4000_quirk(vdev, nr);
902c00d61d8SAlex Williamson     vfio_probe_nvidia_bar5_window_quirk(vdev, nr);
903c00d61d8SAlex Williamson     vfio_probe_nvidia_bar0_88000_quirk(vdev, nr);
904c00d61d8SAlex Williamson     vfio_probe_nvidia_bar0_1800_quirk(vdev, nr);
905c00d61d8SAlex Williamson     vfio_probe_rtl8168_bar2_window_quirk(vdev, nr);
906c00d61d8SAlex Williamson }
907c00d61d8SAlex Williamson 
908c00d61d8SAlex Williamson void vfio_bar_quirk_teardown(VFIOPCIDevice *vdev, int nr)
909c00d61d8SAlex Williamson {
910c00d61d8SAlex Williamson     VFIOBAR *bar = &vdev->bars[nr];
911c00d61d8SAlex Williamson     VFIOQuirk *quirk;
9128c4f2348SAlex Williamson     int i;
913c00d61d8SAlex Williamson 
914c00d61d8SAlex Williamson     QLIST_FOREACH(quirk, &bar->quirks, next) {
9158c4f2348SAlex Williamson         for (i = 0; i < quirk->nr_mem; i++) {
9168c4f2348SAlex Williamson             memory_region_del_subregion(&bar->region.mem, &quirk->mem[i]);
9178c4f2348SAlex Williamson         }
918c00d61d8SAlex Williamson     }
919c00d61d8SAlex Williamson }
920c00d61d8SAlex Williamson 
921c00d61d8SAlex Williamson void vfio_bar_quirk_free(VFIOPCIDevice *vdev, int nr)
922c00d61d8SAlex Williamson {
923c00d61d8SAlex Williamson     VFIOBAR *bar = &vdev->bars[nr];
9248c4f2348SAlex Williamson     int i;
925c00d61d8SAlex Williamson 
926c00d61d8SAlex Williamson     while (!QLIST_EMPTY(&bar->quirks)) {
927c00d61d8SAlex Williamson         VFIOQuirk *quirk = QLIST_FIRST(&bar->quirks);
928c00d61d8SAlex Williamson         QLIST_REMOVE(quirk, next);
9298c4f2348SAlex Williamson         for (i = 0; i < quirk->nr_mem; i++) {
9308c4f2348SAlex Williamson             object_unparent(OBJECT(&quirk->mem[i]));
9318c4f2348SAlex Williamson         }
9328c4f2348SAlex Williamson         g_free(quirk->mem);
9338c4f2348SAlex Williamson         g_free(quirk->data);
934c00d61d8SAlex Williamson         g_free(quirk);
935c00d61d8SAlex Williamson     }
936c00d61d8SAlex Williamson }
937