/root/src/xen/xen/drivers/passthrough/vtd/quirks.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2010, Intel Corporation. |
3 | | * |
4 | | * This program is free software; you can redistribute it and/or modify it |
5 | | * under the terms and conditions of the GNU General Public License, |
6 | | * version 2, as published by the Free Software Foundation. |
7 | | * |
8 | | * This program is distributed in the hope it will be useful, but WITHOUT |
9 | | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
10 | | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
11 | | * more details. |
12 | | * |
13 | | * You should have received a copy of the GNU General Public License along with |
14 | | * this program; If not, see <http://www.gnu.org/licenses/>. |
15 | | * |
16 | | * Author: Allen Kay <allen.m.kay@intel.com> |
17 | | */ |
18 | | |
19 | | #include <xen/irq.h> |
20 | | #include <xen/sched.h> |
21 | | #include <xen/xmalloc.h> |
22 | | #include <xen/domain_page.h> |
23 | | #include <xen/iommu.h> |
24 | | #include <xen/numa.h> |
25 | | #include <xen/softirq.h> |
26 | | #include <xen/time.h> |
27 | | #include <xen/pci.h> |
28 | | #include <xen/pci_ids.h> |
29 | | #include <xen/pci_regs.h> |
30 | | #include <xen/keyhandler.h> |
31 | | #include <asm/msi.h> |
32 | | #include <asm/irq.h> |
33 | | #include <asm/pci.h> |
34 | | #include <mach_apic.h> |
35 | | #include "iommu.h" |
36 | | #include "dmar.h" |
37 | | #include "extern.h" |
38 | | #include "vtd.h" |
39 | | |
40 | 1 | #define IOH_DEV 0 |
41 | 4 | #define IGD_DEV 2 |
42 | | |
43 | 0 | #define IGD_BAR_MASK 0xFFFFFFFFFFFF0000 |
44 | 0 | #define GGC 0x52 |
45 | 0 | #define GGC_MEMORY_VT_ENABLED (0x8 << 8) |
46 | | |
47 | 57 | #define IS_CTG(id) (id == 0x2a408086) |
48 | 57 | #define IS_ILK(id) (id == 0x00408086 || id == 0x00448086 || id== 0x00628086 || id == 0x006A8086) |
49 | 57 | #define IS_CPT(id) (id == 0x01008086 || id == 0x01048086) |
50 | | |
51 | | /* SandyBridge IGD timeouts in milliseconds */ |
52 | 0 | #define SNB_IGD_TIMEOUT_LEGACY 1000 |
53 | 0 | #define SNB_IGD_TIMEOUT 670 |
54 | | static unsigned int snb_igd_timeout; |
55 | | |
56 | | static u32 __read_mostly ioh_id; |
57 | | static u32 __initdata igd_id; |
58 | | bool_t __read_mostly rwbf_quirk; |
59 | | static bool_t __read_mostly is_cantiga_b3; |
60 | | static bool_t __read_mostly is_snb_gfx; |
61 | | static u8 *__read_mostly igd_reg_va; |
62 | | static spinlock_t igd_lock; |
63 | | |
64 | | /* |
65 | | * QUIRK to workaround Xen boot issue on Calpella/Ironlake OEM BIOS |
66 | | * not enabling VT-d properly in IGD. The workaround is to not enabling |
67 | | * IGD VT-d translation if VT is not enabled in IGD. |
68 | | */ |
69 | | int is_igd_vt_enabled_quirk(void) |
70 | 0 | { |
71 | 0 | u16 ggc; |
72 | 0 |
|
73 | 0 | if ( !IS_ILK(ioh_id) ) |
74 | 0 | return 1; |
75 | 0 |
|
76 | 0 | /* integrated graphics on Intel platforms is located at 0:2.0 */ |
77 | 0 | ggc = pci_conf_read16(0, 0, IGD_DEV, 0, GGC); |
78 | 0 | return ( ggc & GGC_MEMORY_VT_ENABLED ? 1 : 0 ); |
79 | 0 | } |
80 | | |
81 | | /* |
82 | | * QUIRK to workaround cantiga VT-d buffer flush issue. |
83 | | * The workaround is to force write buffer flush even if |
84 | | * VT-d capability indicates it is not required. |
85 | | */ |
86 | | static void __init cantiga_b3_errata_init(void) |
87 | 1 | { |
88 | 1 | u16 vid; |
89 | 1 | u8 did_hi, rid; |
90 | 1 | |
91 | 1 | vid = pci_conf_read16(0, 0, IGD_DEV, 0, 0); |
92 | 1 | if ( vid != 0x8086 ) |
93 | 0 | return; |
94 | 1 | |
95 | 1 | did_hi = pci_conf_read8(0, 0, IGD_DEV, 0, 3); |
96 | 1 | rid = pci_conf_read8(0, 0, IGD_DEV, 0, 8); |
97 | 1 | |
98 | 1 | if ( (did_hi == 0x2A) && (rid == 0x7) ) |
99 | 0 | is_cantiga_b3 = 1; |
100 | 1 | } |
101 | | |
102 | | /* check for Sandybridge IGD device ID's */ |
103 | | static void __init snb_errata_init(void) |
104 | 1 | { |
105 | 1 | is_snb_gfx = IS_SNB_GFX(igd_id); |
106 | 1 | spin_lock_init(&igd_lock); |
107 | 1 | } |
108 | | |
109 | | /* |
110 | | * QUIRK to workaround Cantiga IGD VT-d low power errata. |
111 | | * This errata impacts IGD assignment on Cantiga systems |
112 | | * and can potentially cause VT-d operations to hang. |
113 | | * The workaround is to access an IGD PCI config register |
114 | | * to get IGD out of low power state before VT-d translation |
115 | | * enable/disable and IOTLB flushes. |
116 | | */ |
117 | | |
118 | | /* |
119 | | * map IGD MMIO+0x2000 page to allow Xen access to IGD 3D register. |
120 | | */ |
121 | | static void __init map_igd_reg(void) |
122 | 1 | { |
123 | 1 | u64 igd_mmio; |
124 | 1 | |
125 | 1 | if ( !is_cantiga_b3 && !is_snb_gfx ) |
126 | 1 | return; |
127 | 1 | |
128 | 0 | if ( igd_reg_va ) |
129 | 0 | return; |
130 | 0 |
|
131 | 0 | igd_mmio = pci_conf_read32(0, 0, IGD_DEV, 0, PCI_BASE_ADDRESS_1); |
132 | 0 | igd_mmio <<= 32; |
133 | 0 | igd_mmio += pci_conf_read32(0, 0, IGD_DEV, 0, PCI_BASE_ADDRESS_0); |
134 | 0 | igd_reg_va = ioremap(igd_mmio & IGD_BAR_MASK, 0x3000); |
135 | 0 | } |
136 | | |
137 | | /* |
138 | | * force IGD to exit low power mode by accessing a IGD 3D regsiter. |
139 | | */ |
140 | | static int cantiga_vtd_ops_preamble(struct iommu* iommu) |
141 | 4.56M | { |
142 | 4.56M | struct intel_iommu *intel = iommu->intel; |
143 | 4.56M | struct acpi_drhd_unit *drhd = intel ? intel->drhd : NULL; |
144 | 4.56M | |
145 | 4.56M | if ( !is_igd_drhd(drhd) || !is_cantiga_b3 ) |
146 | 4.56M | return 0; |
147 | 4.56M | |
148 | 0 | if ( !igd_reg_va ) |
149 | 0 | return 0; |
150 | 0 |
|
151 | 0 | /* |
152 | 0 | * Read IGD register at IGD MMIO + 0x20A4 to force IGD |
153 | 0 | * to exit low power state. |
154 | 0 | */ |
155 | 0 | return *(volatile int *)(igd_reg_va + 0x20A4); |
156 | 0 | } |
157 | | |
158 | | /* |
159 | | * Sandybridge RC6 power management inhibit state erratum. |
160 | | * This can cause power high power consumption. |
161 | | * Workaround is to prevent graphics get into RC6 |
162 | | * state when doing VT-d IOTLB operations, do the VT-d |
163 | | * IOTLB operation, and then re-enable RC6 state. |
164 | | * |
165 | | * This quirk is enabled with the snb_igd_quirk command |
166 | | * line parameter. Specifying snb_igd_quirk with no value |
167 | | * (or any of the standard boolean values) enables this |
168 | | * quirk and sets the timeout to the legacy timeout of |
169 | | * 1000 msec. Setting this parameter to the string |
170 | | * "cap" enables this quirk and sets the timeout to |
171 | | * the theoretical maximum of 670 msec. Setting this |
172 | | * parameter to a numerical value enables the quirk and |
173 | | * sets the timeout to that numerical number of msecs. |
174 | | */ |
175 | | static void snb_vtd_ops_preamble(struct iommu* iommu) |
176 | 0 | { |
177 | 0 | struct intel_iommu *intel = iommu->intel; |
178 | 0 | struct acpi_drhd_unit *drhd = intel ? intel->drhd : NULL; |
179 | 0 | s_time_t start_time; |
180 | 0 |
|
181 | 0 | if ( !is_igd_drhd(drhd) || !is_snb_gfx ) |
182 | 0 | return; |
183 | 0 |
|
184 | 0 | if ( !igd_reg_va ) |
185 | 0 | return; |
186 | 0 |
|
187 | 0 | *(volatile u32 *)(igd_reg_va + 0x2054) = 0x000FFFFF; |
188 | 0 | *(volatile u32 *)(igd_reg_va + 0x2700) = 0; |
189 | 0 |
|
190 | 0 | start_time = NOW(); |
191 | 0 | while ( (*(volatile u32 *)(igd_reg_va + 0x22AC) & 0xF) != 0 ) |
192 | 0 | { |
193 | 0 | if ( NOW() > start_time + snb_igd_timeout ) |
194 | 0 | { |
195 | 0 | dprintk(XENLOG_INFO VTDPREFIX, |
196 | 0 | "snb_vtd_ops_preamble: failed to disable idle handshake\n"); |
197 | 0 | break; |
198 | 0 | } |
199 | 0 | cpu_relax(); |
200 | 0 | } |
201 | 0 |
|
202 | 0 | *(volatile u32 *)(igd_reg_va + 0x2050) = 0x10001; |
203 | 0 | } |
204 | | |
205 | | static void snb_vtd_ops_postamble(struct iommu* iommu) |
206 | 0 | { |
207 | 0 | struct intel_iommu *intel = iommu->intel; |
208 | 0 | struct acpi_drhd_unit *drhd = intel ? intel->drhd : NULL; |
209 | 0 |
|
210 | 0 | if ( !is_igd_drhd(drhd) || !is_snb_gfx ) |
211 | 0 | return; |
212 | 0 |
|
213 | 0 | if ( !igd_reg_va ) |
214 | 0 | return; |
215 | 0 |
|
216 | 0 | *(volatile u32 *)(igd_reg_va + 0x2054) = 0xA; |
217 | 0 | *(volatile u32 *)(igd_reg_va + 0x2050) = 0x10000; |
218 | 0 | } |
219 | | |
220 | | /* |
221 | | * call before VT-d translation enable and IOTLB flush operations. |
222 | | */ |
223 | | |
224 | | void vtd_ops_preamble_quirk(struct iommu* iommu) |
225 | 4.56M | { |
226 | 4.56M | cantiga_vtd_ops_preamble(iommu); |
227 | 4.56M | if ( snb_igd_timeout != 0 ) |
228 | 0 | { |
229 | 0 | spin_lock(&igd_lock); |
230 | 0 |
|
231 | 0 | /* match unlock in postamble */ |
232 | 0 | snb_vtd_ops_preamble(iommu); |
233 | 0 | } |
234 | 4.56M | } |
235 | | |
236 | | /* |
237 | | * call after VT-d translation enable and IOTLB flush operations. |
238 | | */ |
239 | | void vtd_ops_postamble_quirk(struct iommu* iommu) |
240 | 4.56M | { |
241 | 4.56M | if ( snb_igd_timeout != 0 ) |
242 | 0 | { |
243 | 0 | snb_vtd_ops_postamble(iommu); |
244 | 0 |
|
245 | 0 | /* match the lock in preamble */ |
246 | 0 | spin_unlock(&igd_lock); |
247 | 0 | } |
248 | 4.56M | } |
249 | | |
250 | | static int __init parse_snb_timeout(const char *s) |
251 | 0 | { |
252 | 0 | int t; |
253 | 0 | const char *q = NULL; |
254 | 0 |
|
255 | 0 | t = parse_bool(s, NULL); |
256 | 0 | if ( t < 0 ) |
257 | 0 | { |
258 | 0 | if ( *s == '\0' ) |
259 | 0 | t = SNB_IGD_TIMEOUT_LEGACY; |
260 | 0 | else if ( strcmp(s, "cap") == 0 ) |
261 | 0 | t = SNB_IGD_TIMEOUT; |
262 | 0 | else |
263 | 0 | t = strtoul(s, &q, 0); |
264 | 0 | } |
265 | 0 | else |
266 | 0 | t = t ? SNB_IGD_TIMEOUT_LEGACY : 0; |
267 | 0 | snb_igd_timeout = MILLISECS(t); |
268 | 0 |
|
269 | 0 | return (q && *q) ? -EINVAL : 0; |
270 | 0 | } |
271 | | custom_param("snb_igd_quirk", parse_snb_timeout); |
272 | | |
273 | | /* 5500/5520/X58 Chipset Interrupt remapping errata, for stepping B-3. |
274 | | * Fixed in stepping C-2. */ |
275 | | static void __init tylersburg_intremap_quirk(void) |
276 | 1 | { |
277 | 1 | uint32_t bus, device; |
278 | 1 | uint8_t rev; |
279 | 1 | |
280 | 257 | for ( bus = 0; bus < 0x100; bus++ ) |
281 | 256 | { |
282 | 256 | /* Match on System Management Registers on Device 20 Function 0 */ |
283 | 256 | device = pci_conf_read32(0, bus, 20, 0, PCI_VENDOR_ID); |
284 | 256 | rev = pci_conf_read8(0, bus, 20, 0, PCI_REVISION_ID); |
285 | 256 | |
286 | 256 | if ( rev == 0x13 && device == 0x342e8086 ) |
287 | 0 | { |
288 | 0 | printk(XENLOG_WARNING VTDPREFIX |
289 | 0 | "Disabling IOMMU due to Intel 5500/5520/X58 Chipset errata #47, #53\n"); |
290 | 0 | iommu_enable = 0; |
291 | 0 | break; |
292 | 0 | } |
293 | 256 | } |
294 | 1 | } |
295 | | |
296 | | /* initialize platform identification flags */ |
297 | | void __init platform_quirks_init(void) |
298 | 1 | { |
299 | 1 | ioh_id = pci_conf_read32(0, 0, IOH_DEV, 0, 0); |
300 | 1 | igd_id = pci_conf_read32(0, 0, IGD_DEV, 0, 0); |
301 | 1 | |
302 | 1 | /* Mobile 4 Series Chipset neglects to set RWBF capability. */ |
303 | 1 | if ( ioh_id == 0x2a408086 ) |
304 | 0 | { |
305 | 0 | dprintk(XENLOG_INFO VTDPREFIX, "DMAR: Forcing write-buffer flush\n"); |
306 | 0 | rwbf_quirk = 1; |
307 | 0 | } |
308 | 1 | |
309 | 1 | /* initialize cantiga B3 identification */ |
310 | 1 | cantiga_b3_errata_init(); |
311 | 1 | |
312 | 1 | snb_errata_init(); |
313 | 1 | |
314 | 1 | /* ioremap IGD MMIO+0x2000 page */ |
315 | 1 | map_igd_reg(); |
316 | 1 | |
317 | 1 | /* Tylersburg interrupt remap quirk */ |
318 | 1 | if ( iommu_intremap ) |
319 | 1 | tylersburg_intremap_quirk(); |
320 | 1 | } |
321 | | |
322 | | /* |
323 | | * QUIRK to workaround wifi direct assignment issue. This issue |
324 | | * impacts only cases where Intel integrated wifi device is directly |
325 | | * is directly assigned to a guest. |
326 | | * |
327 | | * The workaround is to map ME phantom device 0:3.7 or 0:22.7 |
328 | | * to the ME vt-d engine if detect the user is trying to directly |
329 | | * assigning Intel integrated wifi device to a guest. |
330 | | */ |
331 | | |
332 | | static int __must_check map_me_phantom_function(struct domain *domain, |
333 | | u32 dev, int map) |
334 | 0 | { |
335 | 0 | struct acpi_drhd_unit *drhd; |
336 | 0 | struct pci_dev *pdev; |
337 | 0 | int rc; |
338 | 0 |
|
339 | 0 | /* find ME VT-d engine base on a real ME device */ |
340 | 0 | pdev = pci_get_pdev(0, 0, PCI_DEVFN(dev, 0)); |
341 | 0 | drhd = acpi_find_matched_drhd_unit(pdev); |
342 | 0 |
|
343 | 0 | /* map or unmap ME phantom function */ |
344 | 0 | if ( map ) |
345 | 0 | rc = domain_context_mapping_one(domain, drhd->iommu, 0, |
346 | 0 | PCI_DEVFN(dev, 7), NULL); |
347 | 0 | else |
348 | 0 | rc = domain_context_unmap_one(domain, drhd->iommu, 0, |
349 | 0 | PCI_DEVFN(dev, 7)); |
350 | 0 |
|
351 | 0 | return rc; |
352 | 0 | } |
353 | | |
354 | | int me_wifi_quirk(struct domain *domain, u8 bus, u8 devfn, int map) |
355 | 57 | { |
356 | 57 | u32 id; |
357 | 57 | int rc = 0; |
358 | 57 | |
359 | 57 | id = pci_conf_read32(0, 0, 0, 0, 0); |
360 | 57 | if ( IS_CTG(id) ) |
361 | 0 | { |
362 | 0 | /* quit if ME does not exist */ |
363 | 0 | if ( pci_conf_read32(0, 0, 3, 0, 0) == 0xffffffff ) |
364 | 0 | return 0; |
365 | 0 |
|
366 | 0 | /* if device is WLAN device, map ME phantom device 0:3.7 */ |
367 | 0 | id = pci_conf_read32(0, bus, PCI_SLOT(devfn), PCI_FUNC(devfn), 0); |
368 | 0 | switch (id) |
369 | 0 | { |
370 | 0 | case 0x42328086: |
371 | 0 | case 0x42358086: |
372 | 0 | case 0x42368086: |
373 | 0 | case 0x42378086: |
374 | 0 | case 0x423a8086: |
375 | 0 | case 0x423b8086: |
376 | 0 | case 0x423c8086: |
377 | 0 | case 0x423d8086: |
378 | 0 | rc = map_me_phantom_function(domain, 3, map); |
379 | 0 | break; |
380 | 0 | default: |
381 | 0 | break; |
382 | 0 | } |
383 | 0 | } |
384 | 57 | else if ( IS_ILK(id) || IS_CPT(id) ) |
385 | 0 | { |
386 | 0 | /* quit if ME does not exist */ |
387 | 0 | if ( pci_conf_read32(0, 0, 22, 0, 0) == 0xffffffff ) |
388 | 0 | return 0; |
389 | 0 |
|
390 | 0 | /* if device is WLAN device, map ME phantom device 0:22.7 */ |
391 | 0 | id = pci_conf_read32(0, bus, PCI_SLOT(devfn), PCI_FUNC(devfn), 0); |
392 | 0 | switch (id) |
393 | 0 | { |
394 | 0 | case 0x00878086: /* Kilmer Peak */ |
395 | 0 | case 0x00898086: |
396 | 0 | case 0x00828086: /* Taylor Peak */ |
397 | 0 | case 0x00858086: |
398 | 0 | case 0x008F8086: /* Rainbow Peak */ |
399 | 0 | case 0x00908086: |
400 | 0 | case 0x00918086: |
401 | 0 | case 0x42388086: /* Puma Peak */ |
402 | 0 | case 0x422b8086: |
403 | 0 | case 0x422c8086: |
404 | 0 | rc = map_me_phantom_function(domain, 22, map); |
405 | 0 | break; |
406 | 0 | default: |
407 | 0 | break; |
408 | 0 | } |
409 | 0 | } |
410 | 57 | |
411 | 57 | return rc; |
412 | 57 | } |
413 | | |
414 | | void pci_vtd_quirk(const struct pci_dev *pdev) |
415 | 68 | { |
416 | 68 | int seg = pdev->seg; |
417 | 68 | int bus = pdev->bus; |
418 | 68 | int dev = PCI_SLOT(pdev->devfn); |
419 | 68 | int func = PCI_FUNC(pdev->devfn); |
420 | 68 | int pos; |
421 | 68 | bool_t ff; |
422 | 68 | u32 val, val2; |
423 | 68 | u64 bar; |
424 | 68 | paddr_t pa; |
425 | 68 | const char *action; |
426 | 68 | |
427 | 68 | if ( pci_conf_read16(seg, bus, dev, func, PCI_VENDOR_ID) != |
428 | 68 | PCI_VENDOR_ID_INTEL ) |
429 | 6 | return; |
430 | 68 | |
431 | 62 | switch ( pci_conf_read16(seg, bus, dev, func, PCI_DEVICE_ID) ) |
432 | 62 | { |
433 | 62 | /* |
434 | 62 | * Mask reporting Intel VT-d faults to IOH core logic: |
435 | 62 | * - Some platform escalates VT-d faults to platform errors. |
436 | 62 | * - This can cause system failure upon non-fatal VT-d faults. |
437 | 62 | * - Potential security issue if malicious guest trigger VT-d faults. |
438 | 62 | */ |
439 | 0 | case 0x342e: /* Tylersburg chipset (Nehalem / Westmere systems) */ |
440 | 0 | case 0x3728: /* Xeon C5500/C3500 (JasperForest) */ |
441 | 0 | case 0x3c28: /* Sandybridge */ |
442 | 0 | val = pci_conf_read32(seg, bus, dev, func, 0x1AC); |
443 | 0 | pci_conf_write32(seg, bus, dev, func, 0x1AC, val | (1 << 31)); |
444 | 0 | printk(XENLOG_INFO "Masked VT-d error signaling on %04x:%02x:%02x.%u\n", |
445 | 0 | seg, bus, dev, func); |
446 | 0 | break; |
447 | 0 |
|
448 | 0 | /* Tylersburg (EP)/Boxboro (MP) chipsets (NHM-EP/EX, WSM-EP/EX) */ |
449 | 0 | case 0x3400 ... 0x3407: /* host bridges */ |
450 | 0 | case 0x3408 ... 0x3411: case 0x3420 ... 0x3421: /* root ports */ |
451 | 0 | /* JasperForest (Intel Xeon Processor C5500/C3500 */ |
452 | 0 | case 0x3700 ... 0x370f: /* host bridges */ |
453 | 0 | case 0x3720 ... 0x3724: /* root ports */ |
454 | 0 | /* Sandybridge-EP (Romley) */ |
455 | 0 | case 0x3c00: /* host bridge */ |
456 | 0 | case 0x3c01 ... 0x3c0b: /* root ports */ |
457 | 0 | pos = pci_find_ext_capability(seg, bus, pdev->devfn, |
458 | 0 | PCI_EXT_CAP_ID_ERR); |
459 | 0 | if ( !pos ) |
460 | 0 | { |
461 | 0 | pos = pci_find_ext_capability(seg, bus, pdev->devfn, |
462 | 0 | PCI_EXT_CAP_ID_VNDR); |
463 | 0 | while ( pos ) |
464 | 0 | { |
465 | 0 | val = pci_conf_read32(seg, bus, dev, func, pos + PCI_VNDR_HEADER); |
466 | 0 | if ( PCI_VNDR_HEADER_ID(val) == 4 && PCI_VNDR_HEADER_REV(val) == 1 ) |
467 | 0 | { |
468 | 0 | pos += PCI_VNDR_HEADER; |
469 | 0 | break; |
470 | 0 | } |
471 | 0 | pos = pci_find_next_ext_capability(seg, bus, pdev->devfn, pos, |
472 | 0 | PCI_EXT_CAP_ID_VNDR); |
473 | 0 | } |
474 | 0 | ff = 0; |
475 | 0 | } |
476 | 0 | else |
477 | 0 | ff = pcie_aer_get_firmware_first(pdev); |
478 | 0 | if ( !pos ) |
479 | 0 | { |
480 | 0 | printk(XENLOG_WARNING "%04x:%02x:%02x.%u without AER capability?\n", |
481 | 0 | seg, bus, dev, func); |
482 | 0 | break; |
483 | 0 | } |
484 | 0 |
|
485 | 0 | val = pci_conf_read32(seg, bus, dev, func, pos + PCI_ERR_UNCOR_MASK); |
486 | 0 | val2 = pci_conf_read32(seg, bus, dev, func, pos + PCI_ERR_COR_MASK); |
487 | 0 | if ( (val & PCI_ERR_UNC_UNSUP) && (val2 & PCI_ERR_COR_ADV_NFAT) ) |
488 | 0 | action = "Found masked"; |
489 | 0 | else if ( !ff ) |
490 | 0 | { |
491 | 0 | pci_conf_write32(seg, bus, dev, func, pos + PCI_ERR_UNCOR_MASK, |
492 | 0 | val | PCI_ERR_UNC_UNSUP); |
493 | 0 | pci_conf_write32(seg, bus, dev, func, pos + PCI_ERR_COR_MASK, |
494 | 0 | val2 | PCI_ERR_COR_ADV_NFAT); |
495 | 0 | action = "Masked"; |
496 | 0 | } |
497 | 0 | else |
498 | 0 | action = "Must not mask"; |
499 | 0 |
|
500 | 0 | /* XPUNCERRMSK Send Completion with Unsupported Request */ |
501 | 0 | val = pci_conf_read32(seg, bus, dev, func, 0x20c); |
502 | 0 | pci_conf_write32(seg, bus, dev, func, 0x20c, val | (1 << 4)); |
503 | 0 |
|
504 | 0 | printk(XENLOG_INFO "%s UR signaling on %04x:%02x:%02x.%u\n", |
505 | 0 | action, seg, bus, dev, func); |
506 | 0 | break; |
507 | 0 |
|
508 | 0 | case 0x0040: case 0x0044: case 0x0048: /* Nehalem/Westmere */ |
509 | 0 | case 0x0100: case 0x0104: case 0x0108: /* Sandybridge */ |
510 | 0 | case 0x0150: case 0x0154: case 0x0158: /* Ivybridge */ |
511 | 0 | case 0x0a00: case 0x0a04: case 0x0a08: case 0x0a0f: /* Haswell ULT */ |
512 | 0 | case 0x0c00: case 0x0c04: case 0x0c08: case 0x0c0f: /* Haswell */ |
513 | 0 | case 0x0d00: case 0x0d04: case 0x0d08: case 0x0d0f: /* Haswell */ |
514 | 0 | case 0x1600: case 0x1604: case 0x1608: case 0x160f: /* Broadwell */ |
515 | 0 | case 0x1610: case 0x1614: case 0x1618: /* Broadwell */ |
516 | 0 | case 0x1900: case 0x1904: case 0x1908: case 0x190c: case 0x190f: /* Skylake */ |
517 | 0 | case 0x1910: case 0x1918: case 0x191f: /* Skylake */ |
518 | 0 | bar = pci_conf_read32(seg, bus, dev, func, 0x6c); |
519 | 0 | bar = (bar << 32) | pci_conf_read32(seg, bus, dev, func, 0x68); |
520 | 0 | pa = bar & 0x7ffffff000UL; /* bits 12...38 */ |
521 | 0 | if ( (bar & 1) && pa && |
522 | 0 | page_is_ram_type(paddr_to_pfn(pa), RAM_TYPE_RESERVED) ) |
523 | 0 | { |
524 | 0 | u32 __iomem *va = ioremap(pa, PAGE_SIZE); |
525 | 0 |
|
526 | 0 | if ( va ) |
527 | 0 | { |
528 | 0 | __set_bit(0x1c8 * 8 + 20, va); |
529 | 0 | iounmap(va); |
530 | 0 | printk(XENLOG_INFO "Masked UR signaling on %04x:%02x:%02x.%u\n", |
531 | 0 | seg, bus, dev, func); |
532 | 0 | } |
533 | 0 | else |
534 | 0 | printk(XENLOG_ERR "Could not map %"PRIpaddr" for %04x:%02x:%02x.%u\n", |
535 | 0 | pa, seg, bus, dev, func); |
536 | 0 | } |
537 | 0 | else |
538 | 0 | printk(XENLOG_WARNING "Bogus DMIBAR %#"PRIx64" on %04x:%02x:%02x.%u\n", |
539 | 0 | bar, seg, bus, dev, func); |
540 | 0 | break; |
541 | 62 | } |
542 | 62 | } |