aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLu Baolu <baolu.lu@linux.intel.com>2018-07-14 03:46:59 -0400
committerJoerg Roedel <jroedel@suse.de>2018-07-20 08:44:24 -0400
commitcc580e41260dbf1a46269235f1f2b572137d9d03 (patch)
tree059b05cf142c60158f0e70a78d59eed717d13561
parent85319dcc8955f8f31828dc8bafff29f6aa011d93 (diff)
iommu/vt-d: Per PCI device pasid table interfaces
This patch adds the interfaces for per PCI device pasid table management. Currently we allocate one pasid table for all PCI devices under the scope of an IOMMU. It's insecure in some cases where multiple devices under one single IOMMU unit support PASID features. With per PCI device pasid table, we can achieve finer protection and isolation granularity. Cc: Ashok Raj <ashok.raj@intel.com> Cc: Jacob Pan <jacob.jun.pan@linux.intel.com> Cc: Kevin Tian <kevin.tian@intel.com> Cc: Liu Yi L <yi.l.liu@intel.com> Suggested-by: Ashok Raj <ashok.raj@intel.com> Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com> Reviewed-by: Liu Yi L <yi.l.liu@intel.com> Signed-off-by: Joerg Roedel <jroedel@suse.de>
-rw-r--r--drivers/iommu/intel-iommu.c1
-rw-r--r--drivers/iommu/intel-pasid.c179
-rw-r--r--drivers/iommu/intel-pasid.h18
-rw-r--r--drivers/iommu/intel-svm.c4
-rw-r--r--include/linux/intel-iommu.h2
5 files changed, 200 insertions, 4 deletions
diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index f020da439ace..211925a75fb4 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -2451,6 +2451,7 @@ static struct dmar_domain *dmar_insert_one_dev_info(struct intel_iommu *iommu,
2451 info->dev = dev; 2451 info->dev = dev;
2452 info->domain = domain; 2452 info->domain = domain;
2453 info->iommu = iommu; 2453 info->iommu = iommu;
2454 info->pasid_table = NULL;
2454 2455
2455 if (dev && dev_is_pci(dev)) { 2456 if (dev && dev_is_pci(dev)) {
2456 struct pci_dev *pdev = to_pci_dev(info->dev); 2457 struct pci_dev *pdev = to_pci_dev(info->dev);
diff --git a/drivers/iommu/intel-pasid.c b/drivers/iommu/intel-pasid.c
index e918fe01ce7f..fe95c9bd4d33 100644
--- a/drivers/iommu/intel-pasid.c
+++ b/drivers/iommu/intel-pasid.c
@@ -13,6 +13,8 @@
13#include <linux/intel-iommu.h> 13#include <linux/intel-iommu.h>
14#include <linux/iommu.h> 14#include <linux/iommu.h>
15#include <linux/memory.h> 15#include <linux/memory.h>
16#include <linux/pci.h>
17#include <linux/pci-ats.h>
16#include <linux/spinlock.h> 18#include <linux/spinlock.h>
17 19
18#include "intel-pasid.h" 20#include "intel-pasid.h"
@@ -58,3 +60,180 @@ void *intel_pasid_lookup_id(int pasid)
58 60
59 return p; 61 return p;
60} 62}
63
64/*
65 * Per device pasid table management:
66 */
67static inline void
68device_attach_pasid_table(struct device_domain_info *info,
69 struct pasid_table *pasid_table)
70{
71 info->pasid_table = pasid_table;
72 list_add(&info->table, &pasid_table->dev);
73}
74
75static inline void
76device_detach_pasid_table(struct device_domain_info *info,
77 struct pasid_table *pasid_table)
78{
79 info->pasid_table = NULL;
80 list_del(&info->table);
81}
82
83struct pasid_table_opaque {
84 struct pasid_table **pasid_table;
85 int segment;
86 int bus;
87 int devfn;
88};
89
90static int search_pasid_table(struct device_domain_info *info, void *opaque)
91{
92 struct pasid_table_opaque *data = opaque;
93
94 if (info->iommu->segment == data->segment &&
95 info->bus == data->bus &&
96 info->devfn == data->devfn &&
97 info->pasid_table) {
98 *data->pasid_table = info->pasid_table;
99 return 1;
100 }
101
102 return 0;
103}
104
105static int get_alias_pasid_table(struct pci_dev *pdev, u16 alias, void *opaque)
106{
107 struct pasid_table_opaque *data = opaque;
108
109 data->segment = pci_domain_nr(pdev->bus);
110 data->bus = PCI_BUS_NUM(alias);
111 data->devfn = alias & 0xff;
112
113 return for_each_device_domain(&search_pasid_table, data);
114}
115
116/*
117 * Allocate a pasid table for @dev. It should be called in a
118 * single-thread context.
119 */
120int intel_pasid_alloc_table(struct device *dev)
121{
122 struct device_domain_info *info;
123 struct pasid_table *pasid_table;
124 struct pasid_table_opaque data;
125 struct page *pages;
126 size_t size, count;
127 int ret, order;
128
129 info = dev->archdata.iommu;
130 if (WARN_ON(!info || !dev_is_pci(dev) ||
131 !info->pasid_supported || info->pasid_table))
132 return -EINVAL;
133
134 /* DMA alias device already has a pasid table, use it: */
135 data.pasid_table = &pasid_table;
136 ret = pci_for_each_dma_alias(to_pci_dev(dev),
137 &get_alias_pasid_table, &data);
138 if (ret)
139 goto attach_out;
140
141 pasid_table = kzalloc(sizeof(*pasid_table), GFP_ATOMIC);
142 if (!pasid_table)
143 return -ENOMEM;
144 INIT_LIST_HEAD(&pasid_table->dev);
145
146 size = sizeof(struct pasid_entry);
147 count = min_t(int, pci_max_pasids(to_pci_dev(dev)), intel_pasid_max_id);
148 order = get_order(size * count);
149 pages = alloc_pages_node(info->iommu->node,
150 GFP_ATOMIC | __GFP_ZERO,
151 order);
152 if (!pages)
153 return -ENOMEM;
154
155 pasid_table->table = page_address(pages);
156 pasid_table->order = order;
157 pasid_table->max_pasid = count;
158
159attach_out:
160 device_attach_pasid_table(info, pasid_table);
161
162 return 0;
163}
164
165void intel_pasid_free_table(struct device *dev)
166{
167 struct device_domain_info *info;
168 struct pasid_table *pasid_table;
169
170 info = dev->archdata.iommu;
171 if (!info || !dev_is_pci(dev) ||
172 !info->pasid_supported || !info->pasid_table)
173 return;
174
175 pasid_table = info->pasid_table;
176 device_detach_pasid_table(info, pasid_table);
177
178 if (!list_empty(&pasid_table->dev))
179 return;
180
181 free_pages((unsigned long)pasid_table->table, pasid_table->order);
182 kfree(pasid_table);
183}
184
185struct pasid_table *intel_pasid_get_table(struct device *dev)
186{
187 struct device_domain_info *info;
188
189 info = dev->archdata.iommu;
190 if (!info)
191 return NULL;
192
193 return info->pasid_table;
194}
195
196int intel_pasid_get_dev_max_id(struct device *dev)
197{
198 struct device_domain_info *info;
199
200 info = dev->archdata.iommu;
201 if (!info || !info->pasid_table)
202 return 0;
203
204 return info->pasid_table->max_pasid;
205}
206
207struct pasid_entry *intel_pasid_get_entry(struct device *dev, int pasid)
208{
209 struct pasid_table *pasid_table;
210 struct pasid_entry *entries;
211
212 pasid_table = intel_pasid_get_table(dev);
213 if (WARN_ON(!pasid_table || pasid < 0 ||
214 pasid >= intel_pasid_get_dev_max_id(dev)))
215 return NULL;
216
217 entries = pasid_table->table;
218
219 return &entries[pasid];
220}
221
222/*
223 * Interfaces for PASID table entry manipulation:
224 */
225static inline void pasid_clear_entry(struct pasid_entry *pe)
226{
227 WRITE_ONCE(pe->val, 0);
228}
229
230void intel_pasid_clear_entry(struct device *dev, int pasid)
231{
232 struct pasid_entry *pe;
233
234 pe = intel_pasid_get_entry(dev, pasid);
235 if (WARN_ON(!pe))
236 return;
237
238 pasid_clear_entry(pe);
239}
diff --git a/drivers/iommu/intel-pasid.h b/drivers/iommu/intel-pasid.h
index b1c5296290e5..1c05ed6fc5a5 100644
--- a/drivers/iommu/intel-pasid.h
+++ b/drivers/iommu/intel-pasid.h
@@ -13,9 +13,27 @@
13#define PASID_MIN 0x1 13#define PASID_MIN 0x1
14#define PASID_MAX 0x100000 14#define PASID_MAX 0x100000
15 15
16struct pasid_entry {
17 u64 val;
18};
19
20/* The representative of a PASID table */
21struct pasid_table {
22 void *table; /* pasid table pointer */
23 int order; /* page order of pasid table */
24 int max_pasid; /* max pasid */
25 struct list_head dev; /* device list */
26};
27
16extern u32 intel_pasid_max_id; 28extern u32 intel_pasid_max_id;
17int intel_pasid_alloc_id(void *ptr, int start, int end, gfp_t gfp); 29int intel_pasid_alloc_id(void *ptr, int start, int end, gfp_t gfp);
18void intel_pasid_free_id(int pasid); 30void intel_pasid_free_id(int pasid);
19void *intel_pasid_lookup_id(int pasid); 31void *intel_pasid_lookup_id(int pasid);
32int intel_pasid_alloc_table(struct device *dev);
33void intel_pasid_free_table(struct device *dev);
34struct pasid_table *intel_pasid_get_table(struct device *dev);
35int intel_pasid_get_dev_max_id(struct device *dev);
36struct pasid_entry *intel_pasid_get_entry(struct device *dev, int pasid);
37void intel_pasid_clear_entry(struct device *dev, int pasid);
20 38
21#endif /* __INTEL_PASID_H */ 39#endif /* __INTEL_PASID_H */
diff --git a/drivers/iommu/intel-svm.c b/drivers/iommu/intel-svm.c
index 56e65d0b1871..9b5dc7262677 100644
--- a/drivers/iommu/intel-svm.c
+++ b/drivers/iommu/intel-svm.c
@@ -34,10 +34,6 @@
34 34
35static irqreturn_t prq_event_thread(int irq, void *d); 35static irqreturn_t prq_event_thread(int irq, void *d);
36 36
37struct pasid_entry {
38 u64 val;
39};
40
41struct pasid_state_entry { 37struct pasid_state_entry {
42 u64 val; 38 u64 val;
43}; 39};
diff --git a/include/linux/intel-iommu.h b/include/linux/intel-iommu.h
index 4fd4c6fee93e..e7901d402337 100644
--- a/include/linux/intel-iommu.h
+++ b/include/linux/intel-iommu.h
@@ -476,6 +476,7 @@ struct intel_iommu {
476struct device_domain_info { 476struct device_domain_info {
477 struct list_head link; /* link to domain siblings */ 477 struct list_head link; /* link to domain siblings */
478 struct list_head global; /* link to global list */ 478 struct list_head global; /* link to global list */
479 struct list_head table; /* link to pasid table */
479 u8 bus; /* PCI bus number */ 480 u8 bus; /* PCI bus number */
480 u8 devfn; /* PCI devfn number */ 481 u8 devfn; /* PCI devfn number */
481 u16 pfsid; /* SRIOV physical function source ID */ 482 u16 pfsid; /* SRIOV physical function source ID */
@@ -489,6 +490,7 @@ struct device_domain_info {
489 struct device *dev; /* it's NULL for PCIe-to-PCI bridge */ 490 struct device *dev; /* it's NULL for PCIe-to-PCI bridge */
490 struct intel_iommu *iommu; /* IOMMU used by this device */ 491 struct intel_iommu *iommu; /* IOMMU used by this device */
491 struct dmar_domain *domain; /* pointer to domain */ 492 struct dmar_domain *domain; /* pointer to domain */
493 struct pasid_table *pasid_table; /* pasid table */
492}; 494};
493 495
494static inline void __iommu_flush_cache( 496static inline void __iommu_flush_cache(