/root/src/xen/xen/drivers/passthrough/x86/ats.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * This program is free software; you can redistribute it and/or modify it |
3 | | * under the terms and conditions of the GNU General Public License, |
4 | | * version 2, as published by the Free Software Foundation. |
5 | | * |
6 | | * This program is distributed in the hope it will be useful, but WITHOUT |
7 | | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
8 | | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
9 | | * more details. |
10 | | * |
11 | | * You should have received a copy of the GNU General Public License along with |
12 | | * this program; If not, see <http://www.gnu.org/licenses/>. |
13 | | */ |
14 | | |
15 | | #include <xen/sched.h> |
16 | | #include <xen/pci.h> |
17 | | #include <xen/pci_regs.h> |
18 | | #include "../ats.h" |
19 | | |
20 | | bool_t __read_mostly ats_enabled = 0; |
21 | | boolean_param("ats", ats_enabled); |
22 | | |
23 | | int enable_ats_device(struct pci_dev *pdev, struct list_head *ats_list) |
24 | 0 | { |
25 | 0 | u32 value; |
26 | 0 | u16 seg = pdev->seg; |
27 | 0 | u8 bus = pdev->bus, devfn = pdev->devfn; |
28 | 0 | int pos; |
29 | 0 |
|
30 | 0 | pos = pci_find_ext_capability(seg, bus, devfn, PCI_EXT_CAP_ID_ATS); |
31 | 0 | BUG_ON(!pos); |
32 | 0 |
|
33 | 0 | if ( iommu_verbose ) |
34 | 0 | dprintk(XENLOG_INFO, "%04x:%02x:%02x.%u: ATS capability found\n", |
35 | 0 | seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); |
36 | 0 |
|
37 | 0 | value = pci_conf_read16(seg, bus, PCI_SLOT(devfn), |
38 | 0 | PCI_FUNC(devfn), pos + ATS_REG_CTL); |
39 | 0 | if ( value & ATS_ENABLE ) |
40 | 0 | { |
41 | 0 | struct pci_dev *other; |
42 | 0 |
|
43 | 0 | list_for_each_entry ( other, ats_list, ats.list ) |
44 | 0 | if ( other == pdev ) |
45 | 0 | { |
46 | 0 | pos = 0; |
47 | 0 | break; |
48 | 0 | } |
49 | 0 | } |
50 | 0 |
|
51 | 0 | if ( !(value & ATS_ENABLE) ) |
52 | 0 | { |
53 | 0 | value |= ATS_ENABLE; |
54 | 0 | pci_conf_write16(seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn), |
55 | 0 | pos + ATS_REG_CTL, value); |
56 | 0 | } |
57 | 0 |
|
58 | 0 | if ( pos ) |
59 | 0 | { |
60 | 0 | pdev->ats.cap_pos = pos; |
61 | 0 | value = pci_conf_read16(seg, bus, PCI_SLOT(devfn), |
62 | 0 | PCI_FUNC(devfn), pos + ATS_REG_CAP); |
63 | 0 | pdev->ats.queue_depth = value & ATS_QUEUE_DEPTH_MASK ?: |
64 | 0 | ATS_QUEUE_DEPTH_MASK + 1; |
65 | 0 | list_add(&pdev->ats.list, ats_list); |
66 | 0 | } |
67 | 0 |
|
68 | 0 | if ( iommu_verbose ) |
69 | 0 | dprintk(XENLOG_INFO, "%04x:%02x:%02x.%u: ATS %s enabled\n", |
70 | 0 | seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn), |
71 | 0 | pos ? "is" : "was"); |
72 | 0 |
|
73 | 0 | return pos; |
74 | 0 | } |
75 | | |
76 | | void disable_ats_device(struct pci_dev *pdev) |
77 | 0 | { |
78 | 0 | u32 value; |
79 | 0 | u16 seg = pdev->seg; |
80 | 0 | u8 bus = pdev->bus, devfn = pdev->devfn; |
81 | 0 |
|
82 | 0 | BUG_ON(!pdev->ats.cap_pos); |
83 | 0 |
|
84 | 0 | value = pci_conf_read16(seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn), |
85 | 0 | pdev->ats.cap_pos + ATS_REG_CTL); |
86 | 0 | value &= ~ATS_ENABLE; |
87 | 0 | pci_conf_write16(seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn), |
88 | 0 | pdev->ats.cap_pos + ATS_REG_CTL, value); |
89 | 0 |
|
90 | 0 | list_del(&pdev->ats.list); |
91 | 0 |
|
92 | 0 | if ( iommu_verbose ) |
93 | 0 | dprintk(XENLOG_INFO, "%04x:%02x:%02x.%u: ATS is disabled\n", |
94 | 0 | seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); |
95 | 0 | } |