Skip to content

Commit cc580e4

Browse files
LuBaolujoergroedel
authored andcommitted
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 <[email protected]> Cc: Jacob Pan <[email protected]> Cc: Kevin Tian <[email protected]> Cc: Liu Yi L <[email protected]> Suggested-by: Ashok Raj <[email protected]> Signed-off-by: Lu Baolu <[email protected]> Reviewed-by: Liu Yi L <[email protected]> Signed-off-by: Joerg Roedel <[email protected]>
1 parent 85319dc commit cc580e4

File tree

5 files changed

+200
-4
lines changed

5 files changed

+200
-4
lines changed

drivers/iommu/intel-iommu.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2451,6 +2451,7 @@ static struct dmar_domain *dmar_insert_one_dev_info(struct intel_iommu *iommu,
24512451
info->dev = dev;
24522452
info->domain = domain;
24532453
info->iommu = iommu;
2454+
info->pasid_table = NULL;
24542455

24552456
if (dev && dev_is_pci(dev)) {
24562457
struct pci_dev *pdev = to_pci_dev(info->dev);

drivers/iommu/intel-pasid.c

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
#include <linux/intel-iommu.h>
1414
#include <linux/iommu.h>
1515
#include <linux/memory.h>
16+
#include <linux/pci.h>
17+
#include <linux/pci-ats.h>
1618
#include <linux/spinlock.h>
1719

1820
#include "intel-pasid.h"
@@ -58,3 +60,180 @@ void *intel_pasid_lookup_id(int pasid)
5860

5961
return p;
6062
}
63+
64+
/*
65+
* Per device pasid table management:
66+
*/
67+
static inline void
68+
device_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+
75+
static inline void
76+
device_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+
83+
struct pasid_table_opaque {
84+
struct pasid_table **pasid_table;
85+
int segment;
86+
int bus;
87+
int devfn;
88+
};
89+
90+
static 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+
105+
static 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+
*/
120+
int 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+
159+
attach_out:
160+
device_attach_pasid_table(info, pasid_table);
161+
162+
return 0;
163+
}
164+
165+
void 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+
185+
struct 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+
196+
int 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+
207+
struct 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+
*/
225+
static inline void pasid_clear_entry(struct pasid_entry *pe)
226+
{
227+
WRITE_ONCE(pe->val, 0);
228+
}
229+
230+
void 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+
}

drivers/iommu/intel-pasid.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,27 @@
1313
#define PASID_MIN 0x1
1414
#define PASID_MAX 0x100000
1515

16+
struct pasid_entry {
17+
u64 val;
18+
};
19+
20+
/* The representative of a PASID table */
21+
struct 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+
1628
extern u32 intel_pasid_max_id;
1729
int intel_pasid_alloc_id(void *ptr, int start, int end, gfp_t gfp);
1830
void intel_pasid_free_id(int pasid);
1931
void *intel_pasid_lookup_id(int pasid);
32+
int intel_pasid_alloc_table(struct device *dev);
33+
void intel_pasid_free_table(struct device *dev);
34+
struct pasid_table *intel_pasid_get_table(struct device *dev);
35+
int intel_pasid_get_dev_max_id(struct device *dev);
36+
struct pasid_entry *intel_pasid_get_entry(struct device *dev, int pasid);
37+
void intel_pasid_clear_entry(struct device *dev, int pasid);
2038

2139
#endif /* __INTEL_PASID_H */

drivers/iommu/intel-svm.c

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,6 @@
3434

3535
static irqreturn_t prq_event_thread(int irq, void *d);
3636

37-
struct pasid_entry {
38-
u64 val;
39-
};
40-
4137
struct pasid_state_entry {
4238
u64 val;
4339
};

include/linux/intel-iommu.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,7 @@ struct intel_iommu {
476476
struct device_domain_info {
477477
struct list_head link; /* link to domain siblings */
478478
struct list_head global; /* link to global list */
479+
struct list_head table; /* link to pasid table */
479480
u8 bus; /* PCI bus number */
480481
u8 devfn; /* PCI devfn number */
481482
u16 pfsid; /* SRIOV physical function source ID */
@@ -489,6 +490,7 @@ struct device_domain_info {
489490
struct device *dev; /* it's NULL for PCIe-to-PCI bridge */
490491
struct intel_iommu *iommu; /* IOMMU used by this device */
491492
struct dmar_domain *domain; /* pointer to domain */
493+
struct pasid_table *pasid_table; /* pasid table */
492494
};
493495

494496
static inline void __iommu_flush_cache(

0 commit comments

Comments
 (0)