xref: /qemu/hw/vfio/pci-quirks.c (revision 0e54f24a5b4bb756715928058b60a7d5f70ccd7f)
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 /*
66*0e54f24aSAlex Williamson  * Device specific region quirks (mostly backdoors to PCI config space)
67c00d61d8SAlex Williamson  */
68c00d61d8SAlex Williamson 
69*0e54f24aSAlex Williamson /*
70*0e54f24aSAlex Williamson  * The generic window quirks operate on an address and data register,
71*0e54f24aSAlex Williamson  * vfio_generic_window_address_quirk handles the address register and
72*0e54f24aSAlex Williamson  * vfio_generic_window_data_quirk handles the data register.  These ops
73*0e54f24aSAlex Williamson  * pass reads and writes through to hardware until a value matching the
74*0e54f24aSAlex Williamson  * stored address match/mask is written.  When this occurs, the data
75*0e54f24aSAlex Williamson  * register access emulated PCI config space for the device rather than
76*0e54f24aSAlex Williamson  * passing through accesses.  This enables devices where PCI config space
77*0e54f24aSAlex Williamson  * is accessible behind a window register to maintain the virtualization
78*0e54f24aSAlex Williamson  * provided through vfio.
79*0e54f24aSAlex Williamson  */
80*0e54f24aSAlex Williamson typedef struct VFIOConfigWindowMatch {
81*0e54f24aSAlex Williamson     uint32_t match;
82*0e54f24aSAlex Williamson     uint32_t mask;
83*0e54f24aSAlex Williamson } VFIOConfigWindowMatch;
84*0e54f24aSAlex Williamson 
85*0e54f24aSAlex Williamson typedef struct VFIOConfigWindowQuirk {
86*0e54f24aSAlex Williamson     struct VFIOPCIDevice *vdev;
87*0e54f24aSAlex Williamson 
88*0e54f24aSAlex Williamson     uint32_t address_val;
89*0e54f24aSAlex Williamson 
90*0e54f24aSAlex Williamson     uint32_t address_offset;
91*0e54f24aSAlex Williamson     uint32_t data_offset;
92*0e54f24aSAlex Williamson 
93*0e54f24aSAlex Williamson     bool window_enabled;
94*0e54f24aSAlex Williamson     uint8_t bar;
95*0e54f24aSAlex Williamson 
96*0e54f24aSAlex Williamson     MemoryRegion *addr_mem;
97*0e54f24aSAlex Williamson     MemoryRegion *data_mem;
98*0e54f24aSAlex Williamson 
99*0e54f24aSAlex Williamson     uint32_t nr_matches;
100*0e54f24aSAlex Williamson     VFIOConfigWindowMatch matches[];
101*0e54f24aSAlex Williamson } VFIOConfigWindowQuirk;
102*0e54f24aSAlex Williamson 
103*0e54f24aSAlex Williamson static uint64_t vfio_generic_window_quirk_address_read(void *opaque,
104*0e54f24aSAlex Williamson                                                        hwaddr addr,
105*0e54f24aSAlex Williamson                                                        unsigned size)
106*0e54f24aSAlex Williamson {
107*0e54f24aSAlex Williamson     VFIOConfigWindowQuirk *window = opaque;
108*0e54f24aSAlex Williamson     VFIOPCIDevice *vdev = window->vdev;
109*0e54f24aSAlex Williamson 
110*0e54f24aSAlex Williamson     return vfio_region_read(&vdev->bars[window->bar].region,
111*0e54f24aSAlex Williamson                             addr + window->address_offset, size);
112*0e54f24aSAlex Williamson }
113*0e54f24aSAlex Williamson 
114*0e54f24aSAlex Williamson static void vfio_generic_window_quirk_address_write(void *opaque, hwaddr addr,
115*0e54f24aSAlex Williamson                                                     uint64_t data,
116*0e54f24aSAlex Williamson                                                     unsigned size)
117*0e54f24aSAlex Williamson {
118*0e54f24aSAlex Williamson     VFIOConfigWindowQuirk *window = opaque;
119*0e54f24aSAlex Williamson     VFIOPCIDevice *vdev = window->vdev;
120*0e54f24aSAlex Williamson     int i;
121*0e54f24aSAlex Williamson 
122*0e54f24aSAlex Williamson     window->window_enabled = false;
123*0e54f24aSAlex Williamson 
124*0e54f24aSAlex Williamson     vfio_region_write(&vdev->bars[window->bar].region,
125*0e54f24aSAlex Williamson                       addr + window->address_offset, data, size);
126*0e54f24aSAlex Williamson 
127*0e54f24aSAlex Williamson     for (i = 0; i < window->nr_matches; i++) {
128*0e54f24aSAlex Williamson         if ((data & ~window->matches[i].mask) == window->matches[i].match) {
129*0e54f24aSAlex Williamson             window->window_enabled = true;
130*0e54f24aSAlex Williamson             window->address_val = data & window->matches[i].mask;
131*0e54f24aSAlex Williamson             trace_vfio_quirk_generic_window_address_write(vdev->vbasedev.name,
132*0e54f24aSAlex Williamson                                     memory_region_name(window->addr_mem), data);
133*0e54f24aSAlex Williamson             break;
134*0e54f24aSAlex Williamson         }
135*0e54f24aSAlex Williamson     }
136*0e54f24aSAlex Williamson }
137*0e54f24aSAlex Williamson 
138*0e54f24aSAlex Williamson static const MemoryRegionOps vfio_generic_window_address_quirk = {
139*0e54f24aSAlex Williamson     .read = vfio_generic_window_quirk_address_read,
140*0e54f24aSAlex Williamson     .write = vfio_generic_window_quirk_address_write,
141*0e54f24aSAlex Williamson     .endianness = DEVICE_LITTLE_ENDIAN,
142*0e54f24aSAlex Williamson };
143*0e54f24aSAlex Williamson 
144*0e54f24aSAlex Williamson static uint64_t vfio_generic_window_quirk_data_read(void *opaque,
145*0e54f24aSAlex Williamson                                                     hwaddr addr, unsigned size)
146*0e54f24aSAlex Williamson {
147*0e54f24aSAlex Williamson     VFIOConfigWindowQuirk *window = opaque;
148*0e54f24aSAlex Williamson     VFIOPCIDevice *vdev = window->vdev;
149*0e54f24aSAlex Williamson     uint64_t data;
150*0e54f24aSAlex Williamson 
151*0e54f24aSAlex Williamson     /* Always read data reg, discard if window enabled */
152*0e54f24aSAlex Williamson     data = vfio_region_read(&vdev->bars[window->bar].region,
153*0e54f24aSAlex Williamson                             addr + window->data_offset, size);
154*0e54f24aSAlex Williamson 
155*0e54f24aSAlex Williamson     if (window->window_enabled) {
156*0e54f24aSAlex Williamson         data = vfio_pci_read_config(&vdev->pdev, window->address_val, size);
157*0e54f24aSAlex Williamson         trace_vfio_quirk_generic_window_data_read(vdev->vbasedev.name,
158*0e54f24aSAlex Williamson                                     memory_region_name(window->data_mem), data);
159*0e54f24aSAlex Williamson     }
160*0e54f24aSAlex Williamson 
161*0e54f24aSAlex Williamson     return data;
162*0e54f24aSAlex Williamson }
163*0e54f24aSAlex Williamson 
164*0e54f24aSAlex Williamson static void vfio_generic_window_quirk_data_write(void *opaque, hwaddr addr,
165*0e54f24aSAlex Williamson                                                  uint64_t data, unsigned size)
166*0e54f24aSAlex Williamson {
167*0e54f24aSAlex Williamson     VFIOConfigWindowQuirk *window = opaque;
168*0e54f24aSAlex Williamson     VFIOPCIDevice *vdev = window->vdev;
169*0e54f24aSAlex Williamson 
170*0e54f24aSAlex Williamson     if (window->window_enabled) {
171*0e54f24aSAlex Williamson         vfio_pci_write_config(&vdev->pdev, window->address_val, data, size);
172*0e54f24aSAlex Williamson         trace_vfio_quirk_generic_window_data_write(vdev->vbasedev.name,
173*0e54f24aSAlex Williamson                                     memory_region_name(window->data_mem), data);
174*0e54f24aSAlex Williamson         return;
175*0e54f24aSAlex Williamson     }
176*0e54f24aSAlex Williamson 
177*0e54f24aSAlex Williamson     vfio_region_write(&vdev->bars[window->bar].region,
178*0e54f24aSAlex Williamson                       addr + window->data_offset, data, size);
179*0e54f24aSAlex Williamson }
180*0e54f24aSAlex Williamson 
181*0e54f24aSAlex Williamson static const MemoryRegionOps vfio_generic_window_data_quirk = {
182*0e54f24aSAlex Williamson     .read = vfio_generic_window_quirk_data_read,
183*0e54f24aSAlex Williamson     .write = vfio_generic_window_quirk_data_write,
184*0e54f24aSAlex Williamson     .endianness = DEVICE_LITTLE_ENDIAN,
185*0e54f24aSAlex Williamson };
186*0e54f24aSAlex Williamson 
187c00d61d8SAlex Williamson /* Is range1 fully contained within range2?  */
188c00d61d8SAlex Williamson static bool vfio_range_contained(uint64_t first1, uint64_t len1,
189c00d61d8SAlex Williamson                                  uint64_t first2, uint64_t len2) {
190c00d61d8SAlex Williamson     return (first1 >= first2 && first1 + len1 <= first2 + len2);
191c00d61d8SAlex Williamson }
192c00d61d8SAlex Williamson 
193c00d61d8SAlex Williamson static bool vfio_flags_enabled(uint8_t flags, uint8_t mask)
194c00d61d8SAlex Williamson {
195c00d61d8SAlex Williamson     return (mask && (flags & mask) == mask);
196c00d61d8SAlex Williamson }
197c00d61d8SAlex Williamson 
198c00d61d8SAlex Williamson static uint64_t vfio_generic_window_quirk_read(void *opaque,
199c00d61d8SAlex Williamson                                                hwaddr addr, unsigned size)
200c00d61d8SAlex Williamson {
2018c4f2348SAlex Williamson     VFIOLegacyQuirk *quirk = opaque;
202c00d61d8SAlex Williamson     VFIOPCIDevice *vdev = quirk->vdev;
203c00d61d8SAlex Williamson     uint64_t data;
204c00d61d8SAlex Williamson 
205c00d61d8SAlex Williamson     if (vfio_flags_enabled(quirk->data.flags, quirk->data.read_flags) &&
206c00d61d8SAlex Williamson         ranges_overlap(addr, size,
207c00d61d8SAlex Williamson                        quirk->data.data_offset, quirk->data.data_size)) {
208c00d61d8SAlex Williamson         hwaddr offset = addr - quirk->data.data_offset;
209c00d61d8SAlex Williamson 
210c00d61d8SAlex Williamson         if (!vfio_range_contained(addr, size, quirk->data.data_offset,
211c00d61d8SAlex Williamson                                   quirk->data.data_size)) {
212c00d61d8SAlex Williamson             hw_error("%s: window data read not fully contained: %s",
2138c4f2348SAlex Williamson                      __func__, memory_region_name(quirk->mem));
214c00d61d8SAlex Williamson         }
215c00d61d8SAlex Williamson 
216c00d61d8SAlex Williamson         data = vfio_pci_read_config(&vdev->pdev,
217c00d61d8SAlex Williamson                                     quirk->data.address_val + offset, size);
218c00d61d8SAlex Williamson 
2198c4f2348SAlex Williamson         trace_vfio_generic_window_quirk_read(memory_region_name(quirk->mem),
220c00d61d8SAlex Williamson                                              vdev->vbasedev.name,
221c00d61d8SAlex Williamson                                              quirk->data.bar,
222c00d61d8SAlex Williamson                                              addr, size, data);
223c00d61d8SAlex Williamson     } else {
224c00d61d8SAlex Williamson         data = vfio_region_read(&vdev->bars[quirk->data.bar].region,
225c00d61d8SAlex Williamson                                 addr + quirk->data.base_offset, size);
226c00d61d8SAlex Williamson     }
227c00d61d8SAlex Williamson 
228c00d61d8SAlex Williamson     return data;
229c00d61d8SAlex Williamson }
230c00d61d8SAlex Williamson 
231c00d61d8SAlex Williamson static void vfio_generic_window_quirk_write(void *opaque, hwaddr addr,
232c00d61d8SAlex Williamson                                             uint64_t data, unsigned size)
233c00d61d8SAlex Williamson {
2348c4f2348SAlex Williamson     VFIOLegacyQuirk *quirk = opaque;
235c00d61d8SAlex Williamson     VFIOPCIDevice *vdev = quirk->vdev;
236c00d61d8SAlex Williamson 
237c00d61d8SAlex Williamson     if (ranges_overlap(addr, size,
238c00d61d8SAlex Williamson                        quirk->data.address_offset, quirk->data.address_size)) {
239c00d61d8SAlex Williamson 
240c00d61d8SAlex Williamson         if (addr != quirk->data.address_offset) {
241c00d61d8SAlex Williamson             hw_error("%s: offset write into address window: %s",
2428c4f2348SAlex Williamson                      __func__, memory_region_name(quirk->mem));
243c00d61d8SAlex Williamson         }
244c00d61d8SAlex Williamson 
245c00d61d8SAlex Williamson         if ((data & ~quirk->data.address_mask) == quirk->data.address_match) {
246c00d61d8SAlex Williamson             quirk->data.flags |= quirk->data.write_flags |
247c00d61d8SAlex Williamson                                  quirk->data.read_flags;
248c00d61d8SAlex Williamson             quirk->data.address_val = data & quirk->data.address_mask;
249c00d61d8SAlex Williamson         } else {
250c00d61d8SAlex Williamson             quirk->data.flags &= ~(quirk->data.write_flags |
251c00d61d8SAlex Williamson                                    quirk->data.read_flags);
252c00d61d8SAlex Williamson         }
253c00d61d8SAlex Williamson     }
254c00d61d8SAlex Williamson 
255c00d61d8SAlex Williamson     if (vfio_flags_enabled(quirk->data.flags, quirk->data.write_flags) &&
256c00d61d8SAlex Williamson         ranges_overlap(addr, size,
257c00d61d8SAlex Williamson                        quirk->data.data_offset, quirk->data.data_size)) {
258c00d61d8SAlex Williamson         hwaddr offset = addr - quirk->data.data_offset;
259c00d61d8SAlex Williamson 
260c00d61d8SAlex Williamson         if (!vfio_range_contained(addr, size, quirk->data.data_offset,
261c00d61d8SAlex Williamson                                   quirk->data.data_size)) {
262c00d61d8SAlex Williamson             hw_error("%s: window data write not fully contained: %s",
2638c4f2348SAlex Williamson                      __func__, memory_region_name(quirk->mem));
264c00d61d8SAlex Williamson         }
265c00d61d8SAlex Williamson 
266c00d61d8SAlex Williamson         vfio_pci_write_config(&vdev->pdev,
267c00d61d8SAlex Williamson                               quirk->data.address_val + offset, data, size);
2688c4f2348SAlex Williamson         trace_vfio_generic_window_quirk_write(memory_region_name(quirk->mem),
269c00d61d8SAlex Williamson                                               vdev->vbasedev.name,
270c00d61d8SAlex Williamson                                               quirk->data.bar,
271c00d61d8SAlex Williamson                                               addr, data, size);
272c00d61d8SAlex Williamson         return;
273c00d61d8SAlex Williamson     }
274c00d61d8SAlex Williamson 
275c00d61d8SAlex Williamson     vfio_region_write(&vdev->bars[quirk->data.bar].region,
276c00d61d8SAlex Williamson                    addr + quirk->data.base_offset, data, size);
277c00d61d8SAlex Williamson }
278c00d61d8SAlex Williamson 
279c00d61d8SAlex Williamson static const MemoryRegionOps vfio_generic_window_quirk = {
280c00d61d8SAlex Williamson     .read = vfio_generic_window_quirk_read,
281c00d61d8SAlex Williamson     .write = vfio_generic_window_quirk_write,
282c00d61d8SAlex Williamson     .endianness = DEVICE_LITTLE_ENDIAN,
283c00d61d8SAlex Williamson };
284c00d61d8SAlex Williamson 
285c00d61d8SAlex Williamson static uint64_t vfio_generic_quirk_read(void *opaque,
286c00d61d8SAlex Williamson                                         hwaddr addr, unsigned size)
287c00d61d8SAlex Williamson {
2888c4f2348SAlex Williamson     VFIOLegacyQuirk *quirk = opaque;
289c00d61d8SAlex Williamson     VFIOPCIDevice *vdev = quirk->vdev;
290c00d61d8SAlex Williamson     hwaddr base = quirk->data.address_match & TARGET_PAGE_MASK;
291c00d61d8SAlex Williamson     hwaddr offset = quirk->data.address_match & ~TARGET_PAGE_MASK;
292c00d61d8SAlex Williamson     uint64_t data;
293c00d61d8SAlex Williamson 
294c00d61d8SAlex Williamson     if (vfio_flags_enabled(quirk->data.flags, quirk->data.read_flags) &&
295c00d61d8SAlex Williamson         ranges_overlap(addr, size, offset, quirk->data.address_mask + 1)) {
296c00d61d8SAlex Williamson         if (!vfio_range_contained(addr, size, offset,
297c00d61d8SAlex Williamson                                   quirk->data.address_mask + 1)) {
298c00d61d8SAlex Williamson             hw_error("%s: read not fully contained: %s",
2998c4f2348SAlex Williamson                      __func__, memory_region_name(quirk->mem));
300c00d61d8SAlex Williamson         }
301c00d61d8SAlex Williamson 
302c00d61d8SAlex Williamson         data = vfio_pci_read_config(&vdev->pdev, addr - offset, size);
303c00d61d8SAlex Williamson 
3048c4f2348SAlex Williamson         trace_vfio_generic_quirk_read(memory_region_name(quirk->mem),
305c00d61d8SAlex Williamson                                       vdev->vbasedev.name, quirk->data.bar,
306c00d61d8SAlex Williamson                                       addr + base, size, data);
307c00d61d8SAlex Williamson     } else {
308c00d61d8SAlex Williamson         data = vfio_region_read(&vdev->bars[quirk->data.bar].region,
309c00d61d8SAlex Williamson                                 addr + base, size);
310c00d61d8SAlex Williamson     }
311c00d61d8SAlex Williamson 
312c00d61d8SAlex Williamson     return data;
313c00d61d8SAlex Williamson }
314c00d61d8SAlex Williamson 
315c00d61d8SAlex Williamson static void vfio_generic_quirk_write(void *opaque, hwaddr addr,
316c00d61d8SAlex Williamson                                      uint64_t data, unsigned size)
317c00d61d8SAlex Williamson {
3188c4f2348SAlex Williamson     VFIOLegacyQuirk *quirk = opaque;
319c00d61d8SAlex Williamson     VFIOPCIDevice *vdev = quirk->vdev;
320c00d61d8SAlex Williamson     hwaddr base = quirk->data.address_match & TARGET_PAGE_MASK;
321c00d61d8SAlex Williamson     hwaddr offset = quirk->data.address_match & ~TARGET_PAGE_MASK;
322c00d61d8SAlex Williamson 
323c00d61d8SAlex Williamson     if (vfio_flags_enabled(quirk->data.flags, quirk->data.write_flags) &&
324c00d61d8SAlex Williamson         ranges_overlap(addr, size, offset, quirk->data.address_mask + 1)) {
325c00d61d8SAlex Williamson         if (!vfio_range_contained(addr, size, offset,
326c00d61d8SAlex Williamson                                   quirk->data.address_mask + 1)) {
327c00d61d8SAlex Williamson             hw_error("%s: write not fully contained: %s",
3288c4f2348SAlex Williamson                      __func__, memory_region_name(quirk->mem));
329c00d61d8SAlex Williamson         }
330c00d61d8SAlex Williamson 
331c00d61d8SAlex Williamson         vfio_pci_write_config(&vdev->pdev, addr - offset, data, size);
332c00d61d8SAlex Williamson 
3338c4f2348SAlex Williamson         trace_vfio_generic_quirk_write(memory_region_name(quirk->mem),
334c00d61d8SAlex Williamson                                        vdev->vbasedev.name, quirk->data.bar,
335c00d61d8SAlex Williamson                                        addr + base, data, size);
336c00d61d8SAlex Williamson     } else {
337c00d61d8SAlex Williamson         vfio_region_write(&vdev->bars[quirk->data.bar].region,
338c00d61d8SAlex Williamson                           addr + base, data, size);
339c00d61d8SAlex Williamson     }
340c00d61d8SAlex Williamson }
341c00d61d8SAlex Williamson 
342c00d61d8SAlex Williamson static const MemoryRegionOps vfio_generic_quirk = {
343c00d61d8SAlex Williamson     .read = vfio_generic_quirk_read,
344c00d61d8SAlex Williamson     .write = vfio_generic_quirk_write,
345c00d61d8SAlex Williamson     .endianness = DEVICE_LITTLE_ENDIAN,
346c00d61d8SAlex Williamson };
347c00d61d8SAlex Williamson 
348c00d61d8SAlex Williamson #define PCI_VENDOR_ID_ATI               0x1002
349c00d61d8SAlex Williamson 
350c00d61d8SAlex Williamson /*
351c00d61d8SAlex Williamson  * Radeon HD cards (HD5450 & HD7850) report the upper byte of the I/O port BAR
352c00d61d8SAlex Williamson  * through VGA register 0x3c3.  On newer cards, the I/O port BAR is always
353c00d61d8SAlex Williamson  * BAR4 (older cards like the X550 used BAR1, but we don't care to support
354c00d61d8SAlex Williamson  * those).  Note that on bare metal, a read of 0x3c3 doesn't always return the
355c00d61d8SAlex Williamson  * I/O port BAR address.  Originally this was coded to return the virtual BAR
356c00d61d8SAlex Williamson  * address only if the physical register read returns the actual BAR address,
357c00d61d8SAlex Williamson  * but users have reported greater success if we return the virtual address
358c00d61d8SAlex Williamson  * unconditionally.
359c00d61d8SAlex Williamson  */
360c00d61d8SAlex Williamson static uint64_t vfio_ati_3c3_quirk_read(void *opaque,
361c00d61d8SAlex Williamson                                         hwaddr addr, unsigned size)
362c00d61d8SAlex Williamson {
363b946d286SAlex Williamson     VFIOPCIDevice *vdev = opaque;
364c00d61d8SAlex Williamson     uint64_t data = vfio_pci_read_config(&vdev->pdev,
365b946d286SAlex Williamson                                          PCI_BASE_ADDRESS_4 + 1, size);
366b946d286SAlex Williamson 
367b946d286SAlex Williamson     trace_vfio_quirk_ati_3c3_read(vdev->vbasedev.name, data);
368c00d61d8SAlex Williamson 
369c00d61d8SAlex Williamson     return data;
370c00d61d8SAlex Williamson }
371c00d61d8SAlex Williamson 
372c00d61d8SAlex Williamson static const MemoryRegionOps vfio_ati_3c3_quirk = {
373c00d61d8SAlex Williamson     .read = vfio_ati_3c3_quirk_read,
374c00d61d8SAlex Williamson     .endianness = DEVICE_LITTLE_ENDIAN,
375c00d61d8SAlex Williamson };
376c00d61d8SAlex Williamson 
377c00d61d8SAlex Williamson static void vfio_vga_probe_ati_3c3_quirk(VFIOPCIDevice *vdev)
378c00d61d8SAlex Williamson {
379c00d61d8SAlex Williamson     VFIOQuirk *quirk;
380c00d61d8SAlex Williamson 
381c00d61d8SAlex Williamson     /*
382c00d61d8SAlex Williamson      * As long as the BAR is >= 256 bytes it will be aligned such that the
383c00d61d8SAlex Williamson      * lower byte is always zero.  Filter out anything else, if it exists.
384c00d61d8SAlex Williamson      */
385b946d286SAlex Williamson     if (!vfio_pci_is(vdev, PCI_VENDOR_ID_ATI, PCI_ANY_ID) ||
386b946d286SAlex Williamson         !vdev->bars[4].ioport || vdev->bars[4].region.size < 256) {
387c00d61d8SAlex Williamson         return;
388c00d61d8SAlex Williamson     }
389c00d61d8SAlex Williamson 
390c00d61d8SAlex Williamson     quirk = g_malloc0(sizeof(*quirk));
391b946d286SAlex Williamson     quirk->mem = g_malloc0_n(sizeof(MemoryRegion), 1);
3928c4f2348SAlex Williamson     quirk->nr_mem = 1;
393c00d61d8SAlex Williamson 
394b946d286SAlex Williamson     memory_region_init_io(quirk->mem, OBJECT(vdev), &vfio_ati_3c3_quirk, vdev,
395c00d61d8SAlex Williamson                           "vfio-ati-3c3-quirk", 1);
396c00d61d8SAlex Williamson     memory_region_add_subregion(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].mem,
3978c4f2348SAlex Williamson                                 3 /* offset 3 bytes from 0x3c0 */, quirk->mem);
398c00d61d8SAlex Williamson 
399c00d61d8SAlex Williamson     QLIST_INSERT_HEAD(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].quirks,
400c00d61d8SAlex Williamson                       quirk, next);
401c00d61d8SAlex Williamson 
402b946d286SAlex Williamson     trace_vfio_quirk_ati_3c3_probe(vdev->vbasedev.name);
403c00d61d8SAlex Williamson }
404c00d61d8SAlex Williamson 
405c00d61d8SAlex Williamson /*
406*0e54f24aSAlex Williamson  * Newer ATI/AMD devices, including HD5450 and HD7850, have a mirror to PCI
407c00d61d8SAlex Williamson  * config space through MMIO BAR2 at offset 0x4000.  Nothing seems to access
408c00d61d8SAlex Williamson  * the MMIO space directly, but a window to this space is provided through
409c00d61d8SAlex Williamson  * I/O port BAR4.  Offset 0x0 is the address register and offset 0x4 is the
410c00d61d8SAlex Williamson  * data register.  When the address is programmed to a range of 0x4000-0x4fff
411c00d61d8SAlex Williamson  * PCI configuration space is available.  Experimentation seems to indicate
412*0e54f24aSAlex Williamson  * that read-only may be provided by hardware.
413c00d61d8SAlex Williamson  */
414*0e54f24aSAlex Williamson static void vfio_probe_ati_bar4_quirk(VFIOPCIDevice *vdev, int nr)
415c00d61d8SAlex Williamson {
416c00d61d8SAlex Williamson     VFIOQuirk *quirk;
417*0e54f24aSAlex Williamson     VFIOConfigWindowQuirk *window;
418c00d61d8SAlex Williamson 
419*0e54f24aSAlex Williamson     /* This windows doesn't seem to be used except by legacy VGA code */
420*0e54f24aSAlex Williamson     if (!vfio_pci_is(vdev, PCI_VENDOR_ID_ATI, PCI_ANY_ID) ||
421*0e54f24aSAlex Williamson         !vdev->has_vga || nr != 4) {
422c00d61d8SAlex Williamson         return;
423c00d61d8SAlex Williamson     }
424c00d61d8SAlex Williamson 
425c00d61d8SAlex Williamson     quirk = g_malloc0(sizeof(*quirk));
426*0e54f24aSAlex Williamson     quirk->mem = g_malloc0_n(sizeof(MemoryRegion), 2);
427*0e54f24aSAlex Williamson     quirk->nr_mem = 2;
428*0e54f24aSAlex Williamson     window = quirk->data = g_malloc0(sizeof(*window) +
429*0e54f24aSAlex Williamson                                      sizeof(VFIOConfigWindowMatch));
430*0e54f24aSAlex Williamson     window->vdev = vdev;
431*0e54f24aSAlex Williamson     window->address_offset = 0;
432*0e54f24aSAlex Williamson     window->data_offset = 4;
433*0e54f24aSAlex Williamson     window->nr_matches = 1;
434*0e54f24aSAlex Williamson     window->matches[0].match = 0x4000;
435*0e54f24aSAlex Williamson     window->matches[0].mask = PCIE_CONFIG_SPACE_SIZE - 1;
436*0e54f24aSAlex Williamson     window->bar = nr;
437*0e54f24aSAlex Williamson     window->addr_mem = &quirk->mem[0];
438*0e54f24aSAlex Williamson     window->data_mem = &quirk->mem[1];
439c00d61d8SAlex Williamson 
440*0e54f24aSAlex Williamson     memory_region_init_io(window->addr_mem, OBJECT(vdev),
441*0e54f24aSAlex Williamson                           &vfio_generic_window_address_quirk, window,
442*0e54f24aSAlex Williamson                           "vfio-ati-bar4-window-address-quirk", 4);
443c00d61d8SAlex Williamson     memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
444*0e54f24aSAlex Williamson                                         window->address_offset,
445*0e54f24aSAlex Williamson                                         window->addr_mem, 1);
446*0e54f24aSAlex Williamson 
447*0e54f24aSAlex Williamson     memory_region_init_io(window->data_mem, OBJECT(vdev),
448*0e54f24aSAlex Williamson                           &vfio_generic_window_data_quirk, window,
449*0e54f24aSAlex Williamson                           "vfio-ati-bar4-window-data-quirk", 4);
450*0e54f24aSAlex Williamson     memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
451*0e54f24aSAlex Williamson                                         window->data_offset,
452*0e54f24aSAlex Williamson                                         window->data_mem, 1);
453c00d61d8SAlex Williamson 
454c00d61d8SAlex Williamson     QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
455c00d61d8SAlex Williamson 
456*0e54f24aSAlex Williamson     trace_vfio_quirk_ati_bar4_probe(vdev->vbasedev.name);
457c00d61d8SAlex Williamson }
458c00d61d8SAlex Williamson 
459c00d61d8SAlex Williamson /*
460c00d61d8SAlex Williamson  * Trap the BAR2 MMIO window to config space as well.
461c00d61d8SAlex Williamson  */
462c00d61d8SAlex Williamson static void vfio_probe_ati_bar2_4000_quirk(VFIOPCIDevice *vdev, int nr)
463c00d61d8SAlex Williamson {
464c00d61d8SAlex Williamson     PCIDevice *pdev = &vdev->pdev;
465c00d61d8SAlex Williamson     VFIOQuirk *quirk;
4668c4f2348SAlex Williamson     VFIOLegacyQuirk *legacy;
467c00d61d8SAlex Williamson 
468c00d61d8SAlex Williamson     /* Only enable on newer devices where BAR2 is 64bit */
469c00d61d8SAlex Williamson     if (!vdev->has_vga || nr != 2 || !vdev->bars[2].mem64 ||
470c00d61d8SAlex Williamson         pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_ATI) {
471c00d61d8SAlex Williamson         return;
472c00d61d8SAlex Williamson     }
473c00d61d8SAlex Williamson 
474c00d61d8SAlex Williamson     quirk = g_malloc0(sizeof(*quirk));
4758c4f2348SAlex Williamson     quirk->data = legacy = g_malloc0(sizeof(*legacy));
4768c4f2348SAlex Williamson     quirk->mem = legacy->mem = g_malloc0_n(sizeof(MemoryRegion), 1);
4778c4f2348SAlex Williamson     quirk->nr_mem = 1;
4788c4f2348SAlex Williamson     legacy->vdev = vdev;
4798c4f2348SAlex Williamson     legacy->data.flags = legacy->data.read_flags = legacy->data.write_flags = 1;
4808c4f2348SAlex Williamson     legacy->data.address_match = 0x4000;
4818c4f2348SAlex Williamson     legacy->data.address_mask = PCIE_CONFIG_SPACE_SIZE - 1;
4828c4f2348SAlex Williamson     legacy->data.bar = nr;
483c00d61d8SAlex Williamson 
4848c4f2348SAlex Williamson     memory_region_init_io(quirk->mem, OBJECT(vdev), &vfio_generic_quirk, legacy,
485c00d61d8SAlex Williamson                           "vfio-ati-bar2-4000-quirk",
4868c4f2348SAlex Williamson                           TARGET_PAGE_ALIGN(legacy->data.address_mask + 1));
487c00d61d8SAlex Williamson     memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
4888c4f2348SAlex Williamson                           legacy->data.address_match & TARGET_PAGE_MASK,
4898c4f2348SAlex Williamson                           quirk->mem, 1);
490c00d61d8SAlex Williamson 
491c00d61d8SAlex Williamson     QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
492c00d61d8SAlex Williamson 
493c00d61d8SAlex Williamson     trace_vfio_probe_ati_bar2_4000_quirk(vdev->vbasedev.name);
494c00d61d8SAlex Williamson }
495c00d61d8SAlex Williamson 
496c00d61d8SAlex Williamson /*
497c00d61d8SAlex Williamson  * Older ATI/AMD cards like the X550 have a similar window to that above.
498c00d61d8SAlex Williamson  * I/O port BAR1 provides a window to a mirror of PCI config space located
499c00d61d8SAlex Williamson  * in BAR2 at offset 0xf00.  We don't care to support such older cards, but
500c00d61d8SAlex Williamson  * note it for future reference.
501c00d61d8SAlex Williamson  */
502c00d61d8SAlex Williamson 
503c00d61d8SAlex Williamson #define PCI_VENDOR_ID_NVIDIA                    0x10de
504c00d61d8SAlex Williamson 
505c00d61d8SAlex Williamson /*
506c00d61d8SAlex Williamson  * Nvidia has several different methods to get to config space, the
507c00d61d8SAlex Williamson  * nouveu project has several of these documented here:
508c00d61d8SAlex Williamson  * https://github.com/pathscale/envytools/tree/master/hwdocs
509c00d61d8SAlex Williamson  *
510c00d61d8SAlex Williamson  * The first quirk is actually not documented in envytools and is found
511c00d61d8SAlex Williamson  * on 10de:01d1 (NVIDIA Corporation G72 [GeForce 7300 LE]).  This is an
512c00d61d8SAlex Williamson  * NV46 chipset.  The backdoor uses the legacy VGA I/O ports to access
513c00d61d8SAlex Williamson  * the mirror of PCI config space found at BAR0 offset 0x1800.  The access
514c00d61d8SAlex Williamson  * sequence first writes 0x338 to I/O port 0x3d4.  The target offset is
515c00d61d8SAlex Williamson  * then written to 0x3d0.  Finally 0x538 is written for a read and 0x738
516c00d61d8SAlex Williamson  * is written for a write to 0x3d4.  The BAR0 offset is then accessible
517c00d61d8SAlex Williamson  * through 0x3d0.  This quirk doesn't seem to be necessary on newer cards
518c00d61d8SAlex Williamson  * that use the I/O port BAR5 window but it doesn't hurt to leave it.
519c00d61d8SAlex Williamson  */
5206029a424SAlex Williamson typedef enum {NONE = 0, SELECT, WINDOW, READ, WRITE} VFIONvidia3d0State;
5216029a424SAlex Williamson static const char *nv3d0_states[] = { "NONE", "SELECT",
5226029a424SAlex Williamson                                       "WINDOW", "READ", "WRITE" };
5236029a424SAlex Williamson 
5246029a424SAlex Williamson typedef struct VFIONvidia3d0Quirk {
5256029a424SAlex Williamson     VFIOPCIDevice *vdev;
5266029a424SAlex Williamson     VFIONvidia3d0State state;
5276029a424SAlex Williamson     uint32_t offset;
5286029a424SAlex Williamson } VFIONvidia3d0Quirk;
5296029a424SAlex Williamson 
5306029a424SAlex Williamson static uint64_t vfio_nvidia_3d4_quirk_read(void *opaque,
5316029a424SAlex Williamson                                            hwaddr addr, unsigned size)
5326029a424SAlex Williamson {
5336029a424SAlex Williamson     VFIONvidia3d0Quirk *quirk = opaque;
5346029a424SAlex Williamson     VFIOPCIDevice *vdev = quirk->vdev;
5356029a424SAlex Williamson 
5366029a424SAlex Williamson     quirk->state = NONE;
5376029a424SAlex Williamson 
5386029a424SAlex Williamson     return vfio_vga_read(&vdev->vga.region[QEMU_PCI_VGA_IO_HI],
5396029a424SAlex Williamson                          addr + 0x14, size);
5406029a424SAlex Williamson }
5416029a424SAlex Williamson 
5426029a424SAlex Williamson static void vfio_nvidia_3d4_quirk_write(void *opaque, hwaddr addr,
5436029a424SAlex Williamson                                         uint64_t data, unsigned size)
5446029a424SAlex Williamson {
5456029a424SAlex Williamson     VFIONvidia3d0Quirk *quirk = opaque;
5466029a424SAlex Williamson     VFIOPCIDevice *vdev = quirk->vdev;
5476029a424SAlex Williamson     VFIONvidia3d0State old_state = quirk->state;
5486029a424SAlex Williamson 
5496029a424SAlex Williamson     quirk->state = NONE;
5506029a424SAlex Williamson 
5516029a424SAlex Williamson     switch (data) {
5526029a424SAlex Williamson     case 0x338:
5536029a424SAlex Williamson         if (old_state == NONE) {
5546029a424SAlex Williamson             quirk->state = SELECT;
5556029a424SAlex Williamson             trace_vfio_quirk_nvidia_3d0_state(vdev->vbasedev.name,
5566029a424SAlex Williamson                                               nv3d0_states[quirk->state]);
5576029a424SAlex Williamson         }
5586029a424SAlex Williamson         break;
5596029a424SAlex Williamson     case 0x538:
5606029a424SAlex Williamson         if (old_state == WINDOW) {
5616029a424SAlex Williamson             quirk->state = READ;
5626029a424SAlex Williamson             trace_vfio_quirk_nvidia_3d0_state(vdev->vbasedev.name,
5636029a424SAlex Williamson                                               nv3d0_states[quirk->state]);
5646029a424SAlex Williamson         }
5656029a424SAlex Williamson         break;
5666029a424SAlex Williamson     case 0x738:
5676029a424SAlex Williamson         if (old_state == WINDOW) {
5686029a424SAlex Williamson             quirk->state = WRITE;
5696029a424SAlex Williamson             trace_vfio_quirk_nvidia_3d0_state(vdev->vbasedev.name,
5706029a424SAlex Williamson                                               nv3d0_states[quirk->state]);
5716029a424SAlex Williamson         }
5726029a424SAlex Williamson         break;
5736029a424SAlex Williamson     }
5746029a424SAlex Williamson 
5756029a424SAlex Williamson     vfio_vga_write(&vdev->vga.region[QEMU_PCI_VGA_IO_HI],
5766029a424SAlex Williamson                    addr + 0x14, data, size);
5776029a424SAlex Williamson }
5786029a424SAlex Williamson 
5796029a424SAlex Williamson static const MemoryRegionOps vfio_nvidia_3d4_quirk = {
5806029a424SAlex Williamson     .read = vfio_nvidia_3d4_quirk_read,
5816029a424SAlex Williamson     .write = vfio_nvidia_3d4_quirk_write,
5826029a424SAlex Williamson     .endianness = DEVICE_LITTLE_ENDIAN,
583c00d61d8SAlex Williamson };
584c00d61d8SAlex Williamson 
585c00d61d8SAlex Williamson static uint64_t vfio_nvidia_3d0_quirk_read(void *opaque,
586c00d61d8SAlex Williamson                                            hwaddr addr, unsigned size)
587c00d61d8SAlex Williamson {
5886029a424SAlex Williamson     VFIONvidia3d0Quirk *quirk = opaque;
589c00d61d8SAlex Williamson     VFIOPCIDevice *vdev = quirk->vdev;
5906029a424SAlex Williamson     VFIONvidia3d0State old_state = quirk->state;
591c00d61d8SAlex Williamson     uint64_t data = vfio_vga_read(&vdev->vga.region[QEMU_PCI_VGA_IO_HI],
5926029a424SAlex Williamson                                   addr + 0x10, size);
593c00d61d8SAlex Williamson 
5946029a424SAlex Williamson     quirk->state = NONE;
5956029a424SAlex Williamson 
5966029a424SAlex Williamson     if (old_state == READ &&
5976029a424SAlex Williamson         (quirk->offset & ~(PCI_CONFIG_SPACE_SIZE - 1)) == 0x1800) {
5986029a424SAlex Williamson         uint8_t offset = quirk->offset & (PCI_CONFIG_SPACE_SIZE - 1);
5996029a424SAlex Williamson 
6006029a424SAlex Williamson         data = vfio_pci_read_config(&vdev->pdev, offset, size);
6016029a424SAlex Williamson         trace_vfio_quirk_nvidia_3d0_read(vdev->vbasedev.name,
6026029a424SAlex Williamson                                          offset, size, data);
603c00d61d8SAlex Williamson     }
604c00d61d8SAlex Williamson 
605c00d61d8SAlex Williamson     return data;
606c00d61d8SAlex Williamson }
607c00d61d8SAlex Williamson 
608c00d61d8SAlex Williamson static void vfio_nvidia_3d0_quirk_write(void *opaque, hwaddr addr,
609c00d61d8SAlex Williamson                                         uint64_t data, unsigned size)
610c00d61d8SAlex Williamson {
6116029a424SAlex Williamson     VFIONvidia3d0Quirk *quirk = opaque;
612c00d61d8SAlex Williamson     VFIOPCIDevice *vdev = quirk->vdev;
6136029a424SAlex Williamson     VFIONvidia3d0State old_state = quirk->state;
614c00d61d8SAlex Williamson 
6156029a424SAlex Williamson     quirk->state = NONE;
6166029a424SAlex Williamson 
6176029a424SAlex Williamson     if (old_state == SELECT) {
6186029a424SAlex Williamson         quirk->offset = (uint32_t)data;
6196029a424SAlex Williamson         quirk->state = WINDOW;
6206029a424SAlex Williamson         trace_vfio_quirk_nvidia_3d0_state(vdev->vbasedev.name,
6216029a424SAlex Williamson                                           nv3d0_states[quirk->state]);
6226029a424SAlex Williamson     } else if (old_state == WRITE) {
6236029a424SAlex Williamson         if ((quirk->offset & ~(PCI_CONFIG_SPACE_SIZE - 1)) == 0x1800) {
6246029a424SAlex Williamson             uint8_t offset = quirk->offset & (PCI_CONFIG_SPACE_SIZE - 1);
6256029a424SAlex Williamson 
6266029a424SAlex Williamson             vfio_pci_write_config(&vdev->pdev, offset, data, size);
6276029a424SAlex Williamson             trace_vfio_quirk_nvidia_3d0_write(vdev->vbasedev.name,
6286029a424SAlex Williamson                                               offset, data, size);
629c00d61d8SAlex Williamson             return;
630c00d61d8SAlex Williamson         }
631c00d61d8SAlex Williamson     }
632c00d61d8SAlex Williamson 
633c00d61d8SAlex Williamson     vfio_vga_write(&vdev->vga.region[QEMU_PCI_VGA_IO_HI],
6346029a424SAlex Williamson                    addr + 0x10, data, size);
635c00d61d8SAlex Williamson }
636c00d61d8SAlex Williamson 
637c00d61d8SAlex Williamson static const MemoryRegionOps vfio_nvidia_3d0_quirk = {
638c00d61d8SAlex Williamson     .read = vfio_nvidia_3d0_quirk_read,
639c00d61d8SAlex Williamson     .write = vfio_nvidia_3d0_quirk_write,
640c00d61d8SAlex Williamson     .endianness = DEVICE_LITTLE_ENDIAN,
641c00d61d8SAlex Williamson };
642c00d61d8SAlex Williamson 
643c00d61d8SAlex Williamson static void vfio_vga_probe_nvidia_3d0_quirk(VFIOPCIDevice *vdev)
644c00d61d8SAlex Williamson {
645c00d61d8SAlex Williamson     VFIOQuirk *quirk;
6466029a424SAlex Williamson     VFIONvidia3d0Quirk *data;
647c00d61d8SAlex Williamson 
6486029a424SAlex Williamson     if (!vfio_pci_is(vdev, PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID) ||
649c00d61d8SAlex Williamson         !vdev->bars[1].region.size) {
650c00d61d8SAlex Williamson         return;
651c00d61d8SAlex Williamson     }
652c00d61d8SAlex Williamson 
653c00d61d8SAlex Williamson     quirk = g_malloc0(sizeof(*quirk));
6546029a424SAlex Williamson     quirk->data = data = g_malloc0(sizeof(*data));
6556029a424SAlex Williamson     quirk->mem = g_malloc0_n(sizeof(MemoryRegion), 2);
6566029a424SAlex Williamson     quirk->nr_mem = 2;
6576029a424SAlex Williamson     data->vdev = vdev;
658c00d61d8SAlex Williamson 
6596029a424SAlex Williamson     memory_region_init_io(&quirk->mem[0], OBJECT(vdev), &vfio_nvidia_3d4_quirk,
6606029a424SAlex Williamson                           data, "vfio-nvidia-3d4-quirk", 2);
661c00d61d8SAlex Williamson     memory_region_add_subregion(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].mem,
6626029a424SAlex Williamson                                 0x14 /* 0x3c0 + 0x14 */, &quirk->mem[0]);
6636029a424SAlex Williamson 
6646029a424SAlex Williamson     memory_region_init_io(&quirk->mem[1], OBJECT(vdev), &vfio_nvidia_3d0_quirk,
6656029a424SAlex Williamson                           data, "vfio-nvidia-3d0-quirk", 2);
6666029a424SAlex Williamson     memory_region_add_subregion(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].mem,
6676029a424SAlex Williamson                                 0x10 /* 0x3c0 + 0x10 */, &quirk->mem[1]);
668c00d61d8SAlex Williamson 
669c00d61d8SAlex Williamson     QLIST_INSERT_HEAD(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].quirks,
670c00d61d8SAlex Williamson                       quirk, next);
671c00d61d8SAlex Williamson 
6726029a424SAlex Williamson     trace_vfio_quirk_nvidia_3d0_probe(vdev->vbasedev.name);
673c00d61d8SAlex Williamson }
674c00d61d8SAlex Williamson 
675c00d61d8SAlex Williamson /*
676c00d61d8SAlex Williamson  * The second quirk is documented in envytools.  The I/O port BAR5 is just
677c00d61d8SAlex Williamson  * a set of address/data ports to the MMIO BARs.  The BAR we care about is
678c00d61d8SAlex Williamson  * again BAR0.  This backdoor is apparently a bit newer than the one above
679c00d61d8SAlex Williamson  * so we need to not only trap 256 bytes @0x1800, but all of PCI config
680c00d61d8SAlex Williamson  * space, including extended space is available at the 4k @0x88000.
681c00d61d8SAlex Williamson  */
682*0e54f24aSAlex Williamson typedef struct VFIONvidiaBAR5Quirk {
683*0e54f24aSAlex Williamson     uint32_t master;
684*0e54f24aSAlex Williamson     uint32_t enable;
685*0e54f24aSAlex Williamson     MemoryRegion *addr_mem;
686*0e54f24aSAlex Williamson     MemoryRegion *data_mem;
687*0e54f24aSAlex Williamson     bool enabled;
688*0e54f24aSAlex Williamson     VFIOConfigWindowQuirk window; /* last for match data */
689*0e54f24aSAlex Williamson } VFIONvidiaBAR5Quirk;
690c00d61d8SAlex Williamson 
691*0e54f24aSAlex Williamson static void vfio_nvidia_bar5_enable(VFIONvidiaBAR5Quirk *bar5)
692*0e54f24aSAlex Williamson {
693*0e54f24aSAlex Williamson     VFIOPCIDevice *vdev = bar5->window.vdev;
694*0e54f24aSAlex Williamson 
695*0e54f24aSAlex Williamson     if (((bar5->master & bar5->enable) & 0x1) == bar5->enabled) {
696*0e54f24aSAlex Williamson         return;
697*0e54f24aSAlex Williamson     }
698*0e54f24aSAlex Williamson 
699*0e54f24aSAlex Williamson     bar5->enabled = !bar5->enabled;
700*0e54f24aSAlex Williamson     trace_vfio_quirk_nvidia_bar5_state(vdev->vbasedev.name,
701*0e54f24aSAlex Williamson                                        bar5->enabled ?  "Enable" : "Disable");
702*0e54f24aSAlex Williamson     memory_region_set_enabled(bar5->addr_mem, bar5->enabled);
703*0e54f24aSAlex Williamson     memory_region_set_enabled(bar5->data_mem, bar5->enabled);
704*0e54f24aSAlex Williamson }
705*0e54f24aSAlex Williamson 
706*0e54f24aSAlex Williamson static uint64_t vfio_nvidia_bar5_quirk_master_read(void *opaque,
707*0e54f24aSAlex Williamson                                                    hwaddr addr, unsigned size)
708*0e54f24aSAlex Williamson {
709*0e54f24aSAlex Williamson     VFIONvidiaBAR5Quirk *bar5 = opaque;
710*0e54f24aSAlex Williamson     VFIOPCIDevice *vdev = bar5->window.vdev;
711*0e54f24aSAlex Williamson 
712*0e54f24aSAlex Williamson     return vfio_region_read(&vdev->bars[5].region, addr, size);
713*0e54f24aSAlex Williamson }
714*0e54f24aSAlex Williamson 
715*0e54f24aSAlex Williamson static void vfio_nvidia_bar5_quirk_master_write(void *opaque, hwaddr addr,
716c00d61d8SAlex Williamson                                                 uint64_t data, unsigned size)
717c00d61d8SAlex Williamson {
718*0e54f24aSAlex Williamson     VFIONvidiaBAR5Quirk *bar5 = opaque;
719*0e54f24aSAlex Williamson     VFIOPCIDevice *vdev = bar5->window.vdev;
720c00d61d8SAlex Williamson 
721*0e54f24aSAlex Williamson     vfio_region_write(&vdev->bars[5].region, addr, data, size);
722*0e54f24aSAlex Williamson 
723*0e54f24aSAlex Williamson     bar5->master = data;
724*0e54f24aSAlex Williamson     vfio_nvidia_bar5_enable(bar5);
725c00d61d8SAlex Williamson }
726c00d61d8SAlex Williamson 
727*0e54f24aSAlex Williamson static const MemoryRegionOps vfio_nvidia_bar5_quirk_master = {
728*0e54f24aSAlex Williamson     .read = vfio_nvidia_bar5_quirk_master_read,
729*0e54f24aSAlex Williamson     .write = vfio_nvidia_bar5_quirk_master_write,
730c00d61d8SAlex Williamson     .endianness = DEVICE_LITTLE_ENDIAN,
731c00d61d8SAlex Williamson };
732c00d61d8SAlex Williamson 
733*0e54f24aSAlex Williamson static uint64_t vfio_nvidia_bar5_quirk_enable_read(void *opaque,
734*0e54f24aSAlex Williamson                                                    hwaddr addr, unsigned size)
735c00d61d8SAlex Williamson {
736*0e54f24aSAlex Williamson     VFIONvidiaBAR5Quirk *bar5 = opaque;
737*0e54f24aSAlex Williamson     VFIOPCIDevice *vdev = bar5->window.vdev;
738c00d61d8SAlex Williamson 
739*0e54f24aSAlex Williamson     return vfio_region_read(&vdev->bars[5].region, addr + 4, size);
740*0e54f24aSAlex Williamson }
741*0e54f24aSAlex Williamson 
742*0e54f24aSAlex Williamson static void vfio_nvidia_bar5_quirk_enable_write(void *opaque, hwaddr addr,
743*0e54f24aSAlex Williamson                                                 uint64_t data, unsigned size)
744*0e54f24aSAlex Williamson {
745*0e54f24aSAlex Williamson     VFIONvidiaBAR5Quirk *bar5 = opaque;
746*0e54f24aSAlex Williamson     VFIOPCIDevice *vdev = bar5->window.vdev;
747*0e54f24aSAlex Williamson 
748*0e54f24aSAlex Williamson     vfio_region_write(&vdev->bars[5].region, addr + 4, data, size);
749*0e54f24aSAlex Williamson 
750*0e54f24aSAlex Williamson     bar5->enable = data;
751*0e54f24aSAlex Williamson     vfio_nvidia_bar5_enable(bar5);
752*0e54f24aSAlex Williamson }
753*0e54f24aSAlex Williamson 
754*0e54f24aSAlex Williamson static const MemoryRegionOps vfio_nvidia_bar5_quirk_enable = {
755*0e54f24aSAlex Williamson     .read = vfio_nvidia_bar5_quirk_enable_read,
756*0e54f24aSAlex Williamson     .write = vfio_nvidia_bar5_quirk_enable_write,
757*0e54f24aSAlex Williamson     .endianness = DEVICE_LITTLE_ENDIAN,
758*0e54f24aSAlex Williamson };
759*0e54f24aSAlex Williamson 
760*0e54f24aSAlex Williamson static void vfio_probe_nvidia_bar5_quirk(VFIOPCIDevice *vdev, int nr)
761*0e54f24aSAlex Williamson {
762*0e54f24aSAlex Williamson     VFIOQuirk *quirk;
763*0e54f24aSAlex Williamson     VFIONvidiaBAR5Quirk *bar5;
764*0e54f24aSAlex Williamson     VFIOConfigWindowQuirk *window;
765*0e54f24aSAlex Williamson 
766*0e54f24aSAlex Williamson     if (!vfio_pci_is(vdev, PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID) ||
767*0e54f24aSAlex Williamson         !vdev->has_vga || nr != 5) {
768c00d61d8SAlex Williamson         return;
769c00d61d8SAlex Williamson     }
770c00d61d8SAlex Williamson 
771c00d61d8SAlex Williamson     quirk = g_malloc0(sizeof(*quirk));
772*0e54f24aSAlex Williamson     quirk->mem = g_malloc0_n(sizeof(MemoryRegion), 4);
773*0e54f24aSAlex Williamson     quirk->nr_mem = 4;
774*0e54f24aSAlex Williamson     bar5 = quirk->data = g_malloc0(sizeof(*bar5) +
775*0e54f24aSAlex Williamson                                    (sizeof(VFIOConfigWindowMatch) * 2));
776*0e54f24aSAlex Williamson     window = &bar5->window;
777c00d61d8SAlex Williamson 
778*0e54f24aSAlex Williamson     window->vdev = vdev;
779*0e54f24aSAlex Williamson     window->address_offset = 0x8;
780*0e54f24aSAlex Williamson     window->data_offset = 0xc;
781*0e54f24aSAlex Williamson     window->nr_matches = 2;
782*0e54f24aSAlex Williamson     window->matches[0].match = 0x1800;
783*0e54f24aSAlex Williamson     window->matches[0].mask = PCI_CONFIG_SPACE_SIZE - 1;
784*0e54f24aSAlex Williamson     window->matches[1].match = 0x88000;
785*0e54f24aSAlex Williamson     window->matches[1].mask = PCIE_CONFIG_SPACE_SIZE - 1;
786*0e54f24aSAlex Williamson     window->bar = nr;
787*0e54f24aSAlex Williamson     window->addr_mem = bar5->addr_mem = &quirk->mem[0];
788*0e54f24aSAlex Williamson     window->data_mem = bar5->data_mem = &quirk->mem[1];
789*0e54f24aSAlex Williamson 
790*0e54f24aSAlex Williamson     memory_region_init_io(window->addr_mem, OBJECT(vdev),
791*0e54f24aSAlex Williamson                           &vfio_generic_window_address_quirk, window,
792*0e54f24aSAlex Williamson                           "vfio-nvidia-bar5-window-address-quirk", 4);
793c00d61d8SAlex Williamson     memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
794*0e54f24aSAlex Williamson                                         window->address_offset,
795*0e54f24aSAlex Williamson                                         window->addr_mem, 1);
796*0e54f24aSAlex Williamson     memory_region_set_enabled(window->addr_mem, false);
797*0e54f24aSAlex Williamson 
798*0e54f24aSAlex Williamson     memory_region_init_io(window->data_mem, OBJECT(vdev),
799*0e54f24aSAlex Williamson                           &vfio_generic_window_data_quirk, window,
800*0e54f24aSAlex Williamson                           "vfio-nvidia-bar5-window-data-quirk", 4);
801*0e54f24aSAlex Williamson     memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
802*0e54f24aSAlex Williamson                                         window->data_offset,
803*0e54f24aSAlex Williamson                                         window->data_mem, 1);
804*0e54f24aSAlex Williamson     memory_region_set_enabled(window->data_mem, false);
805*0e54f24aSAlex Williamson 
806*0e54f24aSAlex Williamson     memory_region_init_io(&quirk->mem[2], OBJECT(vdev),
807*0e54f24aSAlex Williamson                           &vfio_nvidia_bar5_quirk_master, bar5,
808*0e54f24aSAlex Williamson                           "vfio-nvidia-bar5-master-quirk", 4);
809*0e54f24aSAlex Williamson     memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
810*0e54f24aSAlex Williamson                                         0, &quirk->mem[2], 1);
811*0e54f24aSAlex Williamson 
812*0e54f24aSAlex Williamson     memory_region_init_io(&quirk->mem[3], OBJECT(vdev),
813*0e54f24aSAlex Williamson                           &vfio_nvidia_bar5_quirk_enable, bar5,
814*0e54f24aSAlex Williamson                           "vfio-nvidia-bar5-enable-quirk", 4);
815*0e54f24aSAlex Williamson     memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
816*0e54f24aSAlex Williamson                                         4, &quirk->mem[3], 1);
817c00d61d8SAlex Williamson 
818c00d61d8SAlex Williamson     QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
819c00d61d8SAlex Williamson 
820*0e54f24aSAlex Williamson     trace_vfio_quirk_nvidia_bar5_probe(vdev->vbasedev.name);
821c00d61d8SAlex Williamson }
822c00d61d8SAlex Williamson 
823c00d61d8SAlex Williamson static void vfio_nvidia_88000_quirk_write(void *opaque, hwaddr addr,
824c00d61d8SAlex Williamson                                           uint64_t data, unsigned size)
825c00d61d8SAlex Williamson {
8268c4f2348SAlex Williamson     VFIOLegacyQuirk *quirk = opaque;
827c00d61d8SAlex Williamson     VFIOPCIDevice *vdev = quirk->vdev;
828c00d61d8SAlex Williamson     PCIDevice *pdev = &vdev->pdev;
829c00d61d8SAlex Williamson     hwaddr base = quirk->data.address_match & TARGET_PAGE_MASK;
830c00d61d8SAlex Williamson 
831c00d61d8SAlex Williamson     vfio_generic_quirk_write(opaque, addr, data, size);
832c00d61d8SAlex Williamson 
833c00d61d8SAlex Williamson     /*
834c00d61d8SAlex Williamson      * Nvidia seems to acknowledge MSI interrupts by writing 0xff to the
835c00d61d8SAlex Williamson      * MSI capability ID register.  Both the ID and next register are
836c00d61d8SAlex Williamson      * read-only, so we allow writes covering either of those to real hw.
837c00d61d8SAlex Williamson      * NB - only fixed for the 0x88000 MMIO window.
838c00d61d8SAlex Williamson      */
839c00d61d8SAlex Williamson     if ((pdev->cap_present & QEMU_PCI_CAP_MSI) &&
840c00d61d8SAlex Williamson         vfio_range_contained(addr, size, pdev->msi_cap, PCI_MSI_FLAGS)) {
841c00d61d8SAlex Williamson         vfio_region_write(&vdev->bars[quirk->data.bar].region,
842c00d61d8SAlex Williamson                           addr + base, data, size);
843c00d61d8SAlex Williamson     }
844c00d61d8SAlex Williamson }
845c00d61d8SAlex Williamson 
846c00d61d8SAlex Williamson static const MemoryRegionOps vfio_nvidia_88000_quirk = {
847c00d61d8SAlex Williamson     .read = vfio_generic_quirk_read,
848c00d61d8SAlex Williamson     .write = vfio_nvidia_88000_quirk_write,
849c00d61d8SAlex Williamson     .endianness = DEVICE_LITTLE_ENDIAN,
850c00d61d8SAlex Williamson };
851c00d61d8SAlex Williamson 
852c00d61d8SAlex Williamson /*
853c00d61d8SAlex Williamson  * Finally, BAR0 itself.  We want to redirect any accesses to either
854c00d61d8SAlex Williamson  * 0x1800 or 0x88000 through the PCI config space access functions.
855c00d61d8SAlex Williamson  *
856c00d61d8SAlex Williamson  * NB - quirk at a page granularity or else they don't seem to work when
857c00d61d8SAlex Williamson  *      BARs are mmap'd
858c00d61d8SAlex Williamson  *
859c00d61d8SAlex Williamson  * Here's offset 0x88000...
860c00d61d8SAlex Williamson  */
861c00d61d8SAlex Williamson static void vfio_probe_nvidia_bar0_88000_quirk(VFIOPCIDevice *vdev, int nr)
862c00d61d8SAlex Williamson {
863c00d61d8SAlex Williamson     PCIDevice *pdev = &vdev->pdev;
864c00d61d8SAlex Williamson     VFIOQuirk *quirk;
8658c4f2348SAlex Williamson     VFIOLegacyQuirk *legacy;
866c00d61d8SAlex Williamson     uint16_t vendor, class;
867c00d61d8SAlex Williamson 
868c00d61d8SAlex Williamson     vendor = pci_get_word(pdev->config + PCI_VENDOR_ID);
869c00d61d8SAlex Williamson     class = pci_get_word(pdev->config + PCI_CLASS_DEVICE);
870c00d61d8SAlex Williamson 
871c00d61d8SAlex Williamson     if (nr != 0 || vendor != PCI_VENDOR_ID_NVIDIA ||
872c00d61d8SAlex Williamson         class != PCI_CLASS_DISPLAY_VGA) {
873c00d61d8SAlex Williamson         return;
874c00d61d8SAlex Williamson     }
875c00d61d8SAlex Williamson 
876c00d61d8SAlex Williamson     quirk = g_malloc0(sizeof(*quirk));
8778c4f2348SAlex Williamson     quirk->data = legacy = g_malloc0(sizeof(*legacy));
8788c4f2348SAlex Williamson     quirk->mem = legacy->mem = g_malloc0_n(sizeof(MemoryRegion), 1);
8798c4f2348SAlex Williamson     quirk->nr_mem = 1;
8808c4f2348SAlex Williamson     legacy->vdev = vdev;
8818c4f2348SAlex Williamson     legacy->data.flags = legacy->data.read_flags = legacy->data.write_flags = 1;
8828c4f2348SAlex Williamson     legacy->data.address_match = 0x88000;
8838c4f2348SAlex Williamson     legacy->data.address_mask = PCIE_CONFIG_SPACE_SIZE - 1;
8848c4f2348SAlex Williamson     legacy->data.bar = nr;
885c00d61d8SAlex Williamson 
8868c4f2348SAlex Williamson     memory_region_init_io(quirk->mem, OBJECT(vdev), &vfio_nvidia_88000_quirk,
8878c4f2348SAlex Williamson                           legacy, "vfio-nvidia-bar0-88000-quirk",
8888c4f2348SAlex Williamson                           TARGET_PAGE_ALIGN(legacy->data.address_mask + 1));
889c00d61d8SAlex Williamson     memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
8908c4f2348SAlex Williamson                           legacy->data.address_match & TARGET_PAGE_MASK,
8918c4f2348SAlex Williamson                           quirk->mem, 1);
892c00d61d8SAlex Williamson 
893c00d61d8SAlex Williamson     QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
894c00d61d8SAlex Williamson 
895c00d61d8SAlex Williamson     trace_vfio_probe_nvidia_bar0_88000_quirk(vdev->vbasedev.name);
896c00d61d8SAlex Williamson }
897c00d61d8SAlex Williamson 
898c00d61d8SAlex Williamson /*
899c00d61d8SAlex Williamson  * And here's the same for BAR0 offset 0x1800...
900c00d61d8SAlex Williamson  */
901c00d61d8SAlex Williamson static void vfio_probe_nvidia_bar0_1800_quirk(VFIOPCIDevice *vdev, int nr)
902c00d61d8SAlex Williamson {
903c00d61d8SAlex Williamson     PCIDevice *pdev = &vdev->pdev;
904c00d61d8SAlex Williamson     VFIOQuirk *quirk;
9058c4f2348SAlex Williamson     VFIOLegacyQuirk *legacy;
906c00d61d8SAlex Williamson 
907c00d61d8SAlex Williamson     if (!vdev->has_vga || nr != 0 ||
908c00d61d8SAlex Williamson         pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_NVIDIA) {
909c00d61d8SAlex Williamson         return;
910c00d61d8SAlex Williamson     }
911c00d61d8SAlex Williamson 
912c00d61d8SAlex Williamson     /* Log the chipset ID */
913c00d61d8SAlex Williamson     trace_vfio_probe_nvidia_bar0_1800_quirk_id(
914c00d61d8SAlex Williamson             (unsigned int)(vfio_region_read(&vdev->bars[0].region, 0, 4) >> 20)
915c00d61d8SAlex Williamson             & 0xff);
916c00d61d8SAlex Williamson 
917c00d61d8SAlex Williamson     quirk = g_malloc0(sizeof(*quirk));
9188c4f2348SAlex Williamson     quirk->data = legacy = g_malloc0(sizeof(*legacy));
9198c4f2348SAlex Williamson     quirk->mem = legacy->mem = g_malloc0_n(sizeof(MemoryRegion), 1);
9208c4f2348SAlex Williamson     quirk->nr_mem = 1;
9218c4f2348SAlex Williamson     legacy->vdev = vdev;
9228c4f2348SAlex Williamson     legacy->data.flags = legacy->data.read_flags = legacy->data.write_flags = 1;
9238c4f2348SAlex Williamson     legacy->data.address_match = 0x1800;
9248c4f2348SAlex Williamson     legacy->data.address_mask = PCI_CONFIG_SPACE_SIZE - 1;
9258c4f2348SAlex Williamson     legacy->data.bar = nr;
926c00d61d8SAlex Williamson 
9278c4f2348SAlex Williamson     memory_region_init_io(quirk->mem, OBJECT(vdev), &vfio_generic_quirk, legacy,
928c00d61d8SAlex Williamson                           "vfio-nvidia-bar0-1800-quirk",
9298c4f2348SAlex Williamson                           TARGET_PAGE_ALIGN(legacy->data.address_mask + 1));
930c00d61d8SAlex Williamson     memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
9318c4f2348SAlex Williamson                           legacy->data.address_match & TARGET_PAGE_MASK,
9328c4f2348SAlex Williamson                           quirk->mem, 1);
933c00d61d8SAlex Williamson 
934c00d61d8SAlex Williamson     QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
935c00d61d8SAlex Williamson 
936c00d61d8SAlex Williamson     trace_vfio_probe_nvidia_bar0_1800_quirk(vdev->vbasedev.name);
937c00d61d8SAlex Williamson }
938c00d61d8SAlex Williamson 
939c00d61d8SAlex Williamson /*
940c00d61d8SAlex Williamson  * TODO - Some Nvidia devices provide config access to their companion HDA
941c00d61d8SAlex Williamson  * device and even to their parent bridge via these config space mirrors.
942c00d61d8SAlex Williamson  * Add quirks for those regions.
943c00d61d8SAlex Williamson  */
944c00d61d8SAlex Williamson 
945c00d61d8SAlex Williamson #define PCI_VENDOR_ID_REALTEK 0x10ec
946c00d61d8SAlex Williamson 
947c00d61d8SAlex Williamson /*
948c00d61d8SAlex Williamson  * RTL8168 devices have a backdoor that can access the MSI-X table.  At BAR2
949c00d61d8SAlex Williamson  * offset 0x70 there is a dword data register, offset 0x74 is a dword address
950c00d61d8SAlex Williamson  * register.  According to the Linux r8169 driver, the MSI-X table is addressed
951c00d61d8SAlex Williamson  * when the "type" portion of the address register is set to 0x1.  This appears
952c00d61d8SAlex Williamson  * to be bits 16:30.  Bit 31 is both a write indicator and some sort of
953c00d61d8SAlex Williamson  * "address latched" indicator.  Bits 12:15 are a mask field, which we can
954c00d61d8SAlex Williamson  * ignore because the MSI-X table should always be accessed as a dword (full
955c00d61d8SAlex Williamson  * mask).  Bits 0:11 is offset within the type.
956c00d61d8SAlex Williamson  *
957c00d61d8SAlex Williamson  * Example trace:
958c00d61d8SAlex Williamson  *
959c00d61d8SAlex Williamson  * Read from MSI-X table offset 0
960c00d61d8SAlex Williamson  * vfio: vfio_bar_write(0000:05:00.0:BAR2+0x74, 0x1f000, 4) // store read addr
961c00d61d8SAlex Williamson  * vfio: vfio_bar_read(0000:05:00.0:BAR2+0x74, 4) = 0x8001f000 // latch
962c00d61d8SAlex Williamson  * vfio: vfio_bar_read(0000:05:00.0:BAR2+0x70, 4) = 0xfee00398 // read data
963c00d61d8SAlex Williamson  *
964c00d61d8SAlex Williamson  * Write 0xfee00000 to MSI-X table offset 0
965c00d61d8SAlex Williamson  * vfio: vfio_bar_write(0000:05:00.0:BAR2+0x70, 0xfee00000, 4) // write data
966c00d61d8SAlex Williamson  * vfio: vfio_bar_write(0000:05:00.0:BAR2+0x74, 0x8001f000, 4) // do write
967c00d61d8SAlex Williamson  * vfio: vfio_bar_read(0000:05:00.0:BAR2+0x74, 4) = 0x1f000 // complete
968c00d61d8SAlex Williamson  */
969954258a5SAlex Williamson typedef struct VFIOrtl8168Quirk {
970954258a5SAlex Williamson     VFIOPCIDevice *vdev;
971954258a5SAlex Williamson     uint32_t addr;
972954258a5SAlex Williamson     uint32_t data;
973954258a5SAlex Williamson     bool enabled;
974954258a5SAlex Williamson } VFIOrtl8168Quirk;
975954258a5SAlex Williamson 
976954258a5SAlex Williamson static uint64_t vfio_rtl8168_quirk_address_read(void *opaque,
977c00d61d8SAlex Williamson                                                 hwaddr addr, unsigned size)
978c00d61d8SAlex Williamson {
979954258a5SAlex Williamson     VFIOrtl8168Quirk *rtl = opaque;
980954258a5SAlex Williamson     VFIOPCIDevice *vdev = rtl->vdev;
981954258a5SAlex Williamson     uint64_t data = vfio_region_read(&vdev->bars[2].region, addr + 0x74, size);
982c00d61d8SAlex Williamson 
983954258a5SAlex Williamson     if (rtl->enabled) {
984954258a5SAlex Williamson         data = rtl->addr ^ 0x80000000U; /* latch/complete */
985954258a5SAlex Williamson         trace_vfio_quirk_rtl8168_fake_latch(vdev->vbasedev.name, data);
986c00d61d8SAlex Williamson     }
987c00d61d8SAlex Williamson 
988954258a5SAlex Williamson     return data;
989c00d61d8SAlex Williamson }
990c00d61d8SAlex Williamson 
991954258a5SAlex Williamson static void vfio_rtl8168_quirk_address_write(void *opaque, hwaddr addr,
992c00d61d8SAlex Williamson                                              uint64_t data, unsigned size)
993c00d61d8SAlex Williamson {
994954258a5SAlex Williamson     VFIOrtl8168Quirk *rtl = opaque;
995954258a5SAlex Williamson     VFIOPCIDevice *vdev = rtl->vdev;
996c00d61d8SAlex Williamson 
997954258a5SAlex Williamson     rtl->enabled = false;
998954258a5SAlex Williamson 
999c00d61d8SAlex Williamson     if ((data & 0x7fff0000) == 0x10000) { /* MSI-X table */
1000954258a5SAlex Williamson         rtl->enabled = true;
1001954258a5SAlex Williamson         rtl->addr = (uint32_t)data;
1002c00d61d8SAlex Williamson 
1003c00d61d8SAlex Williamson         if (data & 0x80000000U) { /* Do write */
1004c00d61d8SAlex Williamson             if (vdev->pdev.cap_present & QEMU_PCI_CAP_MSIX) {
1005c00d61d8SAlex Williamson                 hwaddr offset = data & 0xfff;
1006954258a5SAlex Williamson                 uint64_t val = rtl->data;
1007c00d61d8SAlex Williamson 
1008954258a5SAlex Williamson                 trace_vfio_quirk_rtl8168_msix_write(vdev->vbasedev.name,
1009c00d61d8SAlex Williamson                                                     (uint16_t)offset, val);
1010c00d61d8SAlex Williamson 
1011c00d61d8SAlex Williamson                 /* Write to the proper guest MSI-X table instead */
1012c00d61d8SAlex Williamson                 memory_region_dispatch_write(&vdev->pdev.msix_table_mmio,
1013c00d61d8SAlex Williamson                                              offset, val, size,
1014c00d61d8SAlex Williamson                                              MEMTXATTRS_UNSPECIFIED);
1015c00d61d8SAlex Williamson             }
1016c00d61d8SAlex Williamson             return; /* Do not write guest MSI-X data to hardware */
1017c00d61d8SAlex Williamson         }
1018c00d61d8SAlex Williamson     }
1019c00d61d8SAlex Williamson 
1020954258a5SAlex Williamson     vfio_region_write(&vdev->bars[2].region, addr + 0x74, data, size);
1021c00d61d8SAlex Williamson }
1022c00d61d8SAlex Williamson 
1023954258a5SAlex Williamson static const MemoryRegionOps vfio_rtl_address_quirk = {
1024954258a5SAlex Williamson     .read = vfio_rtl8168_quirk_address_read,
1025954258a5SAlex Williamson     .write = vfio_rtl8168_quirk_address_write,
1026c00d61d8SAlex Williamson     .valid = {
1027c00d61d8SAlex Williamson         .min_access_size = 4,
1028c00d61d8SAlex Williamson         .max_access_size = 4,
1029c00d61d8SAlex Williamson         .unaligned = false,
1030c00d61d8SAlex Williamson     },
1031c00d61d8SAlex Williamson     .endianness = DEVICE_LITTLE_ENDIAN,
1032c00d61d8SAlex Williamson };
1033c00d61d8SAlex Williamson 
1034954258a5SAlex Williamson static uint64_t vfio_rtl8168_quirk_data_read(void *opaque,
1035954258a5SAlex Williamson                                              hwaddr addr, unsigned size)
1036c00d61d8SAlex Williamson {
1037954258a5SAlex Williamson     VFIOrtl8168Quirk *rtl = opaque;
1038954258a5SAlex Williamson     VFIOPCIDevice *vdev = rtl->vdev;
1039954258a5SAlex Williamson     uint64_t data = vfio_region_read(&vdev->bars[2].region, addr + 0x74, size);
1040c00d61d8SAlex Williamson 
1041954258a5SAlex Williamson     if (rtl->enabled && (vdev->pdev.cap_present & QEMU_PCI_CAP_MSIX)) {
1042954258a5SAlex Williamson         hwaddr offset = rtl->addr & 0xfff;
1043954258a5SAlex Williamson         memory_region_dispatch_read(&vdev->pdev.msix_table_mmio, offset,
1044954258a5SAlex Williamson                                     &data, size, MEMTXATTRS_UNSPECIFIED);
1045954258a5SAlex Williamson         trace_vfio_quirk_rtl8168_msix_read(vdev->vbasedev.name, offset, data);
1046954258a5SAlex Williamson     }
1047954258a5SAlex Williamson 
1048954258a5SAlex Williamson     return data;
1049954258a5SAlex Williamson }
1050954258a5SAlex Williamson 
1051954258a5SAlex Williamson static void vfio_rtl8168_quirk_data_write(void *opaque, hwaddr addr,
1052954258a5SAlex Williamson                                           uint64_t data, unsigned size)
1053954258a5SAlex Williamson {
1054954258a5SAlex Williamson     VFIOrtl8168Quirk *rtl = opaque;
1055954258a5SAlex Williamson     VFIOPCIDevice *vdev = rtl->vdev;
1056954258a5SAlex Williamson 
1057954258a5SAlex Williamson     rtl->data = (uint32_t)data;
1058954258a5SAlex Williamson 
1059954258a5SAlex Williamson     vfio_region_write(&vdev->bars[2].region, addr + 0x70, data, size);
1060954258a5SAlex Williamson }
1061954258a5SAlex Williamson 
1062954258a5SAlex Williamson static const MemoryRegionOps vfio_rtl_data_quirk = {
1063954258a5SAlex Williamson     .read = vfio_rtl8168_quirk_data_read,
1064954258a5SAlex Williamson     .write = vfio_rtl8168_quirk_data_write,
1065954258a5SAlex Williamson     .valid = {
1066954258a5SAlex Williamson         .min_access_size = 4,
1067954258a5SAlex Williamson         .max_access_size = 4,
1068954258a5SAlex Williamson         .unaligned = false,
1069954258a5SAlex Williamson     },
1070954258a5SAlex Williamson     .endianness = DEVICE_LITTLE_ENDIAN,
1071954258a5SAlex Williamson };
1072954258a5SAlex Williamson 
1073954258a5SAlex Williamson static void vfio_probe_rtl8168_bar2_quirk(VFIOPCIDevice *vdev, int nr)
1074954258a5SAlex Williamson {
1075954258a5SAlex Williamson     VFIOQuirk *quirk;
1076954258a5SAlex Williamson     VFIOrtl8168Quirk *rtl;
1077954258a5SAlex Williamson 
1078954258a5SAlex Williamson     if (!vfio_pci_is(vdev, PCI_VENDOR_ID_REALTEK, 0x8168) || nr != 2) {
1079c00d61d8SAlex Williamson         return;
1080c00d61d8SAlex Williamson     }
1081c00d61d8SAlex Williamson 
1082c00d61d8SAlex Williamson     quirk = g_malloc0(sizeof(*quirk));
1083954258a5SAlex Williamson     quirk->mem = g_malloc0_n(sizeof(MemoryRegion), 2);
1084954258a5SAlex Williamson     quirk->nr_mem = 2;
1085954258a5SAlex Williamson     quirk->data = rtl = g_malloc0(sizeof(*rtl));
1086954258a5SAlex Williamson     rtl->vdev = vdev;
1087c00d61d8SAlex Williamson 
1088954258a5SAlex Williamson     memory_region_init_io(&quirk->mem[0], OBJECT(vdev),
1089954258a5SAlex Williamson                           &vfio_rtl_address_quirk, rtl,
1090954258a5SAlex Williamson                           "vfio-rtl8168-window-address-quirk", 4);
1091c00d61d8SAlex Williamson     memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
1092954258a5SAlex Williamson                                         0x74, &quirk->mem[0], 1);
1093954258a5SAlex Williamson 
1094954258a5SAlex Williamson     memory_region_init_io(&quirk->mem[1], OBJECT(vdev),
1095954258a5SAlex Williamson                           &vfio_rtl_data_quirk, rtl,
1096954258a5SAlex Williamson                           "vfio-rtl8168-window-data-quirk", 4);
1097954258a5SAlex Williamson     memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
1098954258a5SAlex Williamson                                         0x70, &quirk->mem[1], 1);
1099c00d61d8SAlex Williamson 
1100c00d61d8SAlex Williamson     QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
1101c00d61d8SAlex Williamson 
1102954258a5SAlex Williamson     trace_vfio_quirk_rtl8168_probe(vdev->vbasedev.name);
1103c00d61d8SAlex Williamson }
1104c00d61d8SAlex Williamson 
1105c00d61d8SAlex Williamson /*
1106c00d61d8SAlex Williamson  * Common quirk probe entry points.
1107c00d61d8SAlex Williamson  */
1108c00d61d8SAlex Williamson void vfio_vga_quirk_setup(VFIOPCIDevice *vdev)
1109c00d61d8SAlex Williamson {
1110c00d61d8SAlex Williamson     vfio_vga_probe_ati_3c3_quirk(vdev);
1111c00d61d8SAlex Williamson     vfio_vga_probe_nvidia_3d0_quirk(vdev);
1112c00d61d8SAlex Williamson }
1113c00d61d8SAlex Williamson 
1114c00d61d8SAlex Williamson void vfio_vga_quirk_teardown(VFIOPCIDevice *vdev)
1115c00d61d8SAlex Williamson {
1116c00d61d8SAlex Williamson     VFIOQuirk *quirk;
11178c4f2348SAlex Williamson     int i, j;
1118c00d61d8SAlex Williamson 
1119c00d61d8SAlex Williamson     for (i = 0; i < ARRAY_SIZE(vdev->vga.region); i++) {
1120c00d61d8SAlex Williamson         QLIST_FOREACH(quirk, &vdev->vga.region[i].quirks, next) {
11218c4f2348SAlex Williamson             for (j = 0; j < quirk->nr_mem; j++) {
11228c4f2348SAlex Williamson                 memory_region_del_subregion(&vdev->vga.region[i].mem,
11238c4f2348SAlex Williamson                                             &quirk->mem[j]);
11248c4f2348SAlex Williamson             }
1125c00d61d8SAlex Williamson         }
1126c00d61d8SAlex Williamson     }
1127c00d61d8SAlex Williamson }
1128c00d61d8SAlex Williamson 
1129c00d61d8SAlex Williamson void vfio_vga_quirk_free(VFIOPCIDevice *vdev)
1130c00d61d8SAlex Williamson {
11318c4f2348SAlex Williamson     int i, j;
1132c00d61d8SAlex Williamson 
1133c00d61d8SAlex Williamson     for (i = 0; i < ARRAY_SIZE(vdev->vga.region); i++) {
1134c00d61d8SAlex Williamson         while (!QLIST_EMPTY(&vdev->vga.region[i].quirks)) {
1135c00d61d8SAlex Williamson             VFIOQuirk *quirk = QLIST_FIRST(&vdev->vga.region[i].quirks);
1136c00d61d8SAlex Williamson             QLIST_REMOVE(quirk, next);
11378c4f2348SAlex Williamson             for (j = 0; j < quirk->nr_mem; j++) {
11388c4f2348SAlex Williamson                 object_unparent(OBJECT(&quirk->mem[j]));
11398c4f2348SAlex Williamson             }
11408c4f2348SAlex Williamson             g_free(quirk->mem);
11418c4f2348SAlex Williamson             g_free(quirk->data);
1142c00d61d8SAlex Williamson             g_free(quirk);
1143c00d61d8SAlex Williamson         }
1144c00d61d8SAlex Williamson     }
1145c00d61d8SAlex Williamson }
1146c00d61d8SAlex Williamson 
1147c00d61d8SAlex Williamson void vfio_bar_quirk_setup(VFIOPCIDevice *vdev, int nr)
1148c00d61d8SAlex Williamson {
1149*0e54f24aSAlex Williamson     vfio_probe_ati_bar4_quirk(vdev, nr);
1150c00d61d8SAlex Williamson     vfio_probe_ati_bar2_4000_quirk(vdev, nr);
1151*0e54f24aSAlex Williamson     vfio_probe_nvidia_bar5_quirk(vdev, nr);
1152c00d61d8SAlex Williamson     vfio_probe_nvidia_bar0_88000_quirk(vdev, nr);
1153c00d61d8SAlex Williamson     vfio_probe_nvidia_bar0_1800_quirk(vdev, nr);
1154954258a5SAlex Williamson     vfio_probe_rtl8168_bar2_quirk(vdev, nr);
1155c00d61d8SAlex Williamson }
1156c00d61d8SAlex Williamson 
1157c00d61d8SAlex Williamson void vfio_bar_quirk_teardown(VFIOPCIDevice *vdev, int nr)
1158c00d61d8SAlex Williamson {
1159c00d61d8SAlex Williamson     VFIOBAR *bar = &vdev->bars[nr];
1160c00d61d8SAlex Williamson     VFIOQuirk *quirk;
11618c4f2348SAlex Williamson     int i;
1162c00d61d8SAlex Williamson 
1163c00d61d8SAlex Williamson     QLIST_FOREACH(quirk, &bar->quirks, next) {
11648c4f2348SAlex Williamson         for (i = 0; i < quirk->nr_mem; i++) {
11658c4f2348SAlex Williamson             memory_region_del_subregion(&bar->region.mem, &quirk->mem[i]);
11668c4f2348SAlex Williamson         }
1167c00d61d8SAlex Williamson     }
1168c00d61d8SAlex Williamson }
1169c00d61d8SAlex Williamson 
1170c00d61d8SAlex Williamson void vfio_bar_quirk_free(VFIOPCIDevice *vdev, int nr)
1171c00d61d8SAlex Williamson {
1172c00d61d8SAlex Williamson     VFIOBAR *bar = &vdev->bars[nr];
11738c4f2348SAlex Williamson     int i;
1174c00d61d8SAlex Williamson 
1175c00d61d8SAlex Williamson     while (!QLIST_EMPTY(&bar->quirks)) {
1176c00d61d8SAlex Williamson         VFIOQuirk *quirk = QLIST_FIRST(&bar->quirks);
1177c00d61d8SAlex Williamson         QLIST_REMOVE(quirk, next);
11788c4f2348SAlex Williamson         for (i = 0; i < quirk->nr_mem; i++) {
11798c4f2348SAlex Williamson             object_unparent(OBJECT(&quirk->mem[i]));
11808c4f2348SAlex Williamson         }
11818c4f2348SAlex Williamson         g_free(quirk->mem);
11828c4f2348SAlex Williamson         g_free(quirk->data);
1183c00d61d8SAlex Williamson         g_free(quirk);
1184c00d61d8SAlex Williamson     }
1185c00d61d8SAlex Williamson }
1186