1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (C) 2024 Advanced Micro Devices, Inc.
6 #define pr_fmt(fmt) "AMD-Vi: " fmt
7 #define dev_fmt(fmt) pr_fmt(fmt)
9 #include <linux/iommu.h>
10 #include <linux/mm_types.h>
12 #include "amd_iommu.h"
14 static inline bool is_pasid_enabled(struct iommu_dev_data
*dev_data
)
16 if (dev_data
->pasid_enabled
&& dev_data
->max_pasids
&&
17 dev_data
->gcr3_info
.gcr3_tbl
!= NULL
)
23 static inline bool is_pasid_valid(struct iommu_dev_data
*dev_data
,
26 if (pasid
> 0 && pasid
< dev_data
->max_pasids
)
32 static void remove_dev_pasid(struct pdom_dev_data
*pdom_dev_data
)
34 /* Update GCR3 table and flush IOTLB */
35 amd_iommu_clear_gcr3(pdom_dev_data
->dev_data
, pdom_dev_data
->pasid
);
37 list_del(&pdom_dev_data
->list
);
41 /* Clear PASID from device GCR3 table and remove pdom_dev_data from list */
42 static void remove_pdom_dev_pasid(struct protection_domain
*pdom
,
43 struct device
*dev
, ioasid_t pasid
)
45 struct pdom_dev_data
*pdom_dev_data
;
46 struct iommu_dev_data
*dev_data
= dev_iommu_priv_get(dev
);
48 lockdep_assert_held(&pdom
->lock
);
50 for_each_pdom_dev_data(pdom_dev_data
, pdom
) {
51 if (pdom_dev_data
->dev_data
== dev_data
&&
52 pdom_dev_data
->pasid
== pasid
) {
53 remove_dev_pasid(pdom_dev_data
);
59 static void sva_arch_invalidate_secondary_tlbs(struct mmu_notifier
*mn
,
61 unsigned long start
, unsigned long end
)
63 struct pdom_dev_data
*pdom_dev_data
;
64 struct protection_domain
*sva_pdom
;
67 sva_pdom
= container_of(mn
, struct protection_domain
, mn
);
69 spin_lock_irqsave(&sva_pdom
->lock
, flags
);
71 for_each_pdom_dev_data(pdom_dev_data
, sva_pdom
) {
72 amd_iommu_dev_flush_pasid_pages(pdom_dev_data
->dev_data
,
77 spin_unlock_irqrestore(&sva_pdom
->lock
, flags
);
80 static void sva_mn_release(struct mmu_notifier
*mn
, struct mm_struct
*mm
)
82 struct pdom_dev_data
*pdom_dev_data
, *next
;
83 struct protection_domain
*sva_pdom
;
86 sva_pdom
= container_of(mn
, struct protection_domain
, mn
);
88 spin_lock_irqsave(&sva_pdom
->lock
, flags
);
90 /* Assume dev_data_list contains same PASID with different devices */
91 for_each_pdom_dev_data_safe(pdom_dev_data
, next
, sva_pdom
)
92 remove_dev_pasid(pdom_dev_data
);
94 spin_unlock_irqrestore(&sva_pdom
->lock
, flags
);
97 static const struct mmu_notifier_ops sva_mn
= {
98 .arch_invalidate_secondary_tlbs
= sva_arch_invalidate_secondary_tlbs
,
99 .release
= sva_mn_release
,
102 int iommu_sva_set_dev_pasid(struct iommu_domain
*domain
,
103 struct device
*dev
, ioasid_t pasid
,
104 struct iommu_domain
*old
)
106 struct pdom_dev_data
*pdom_dev_data
;
107 struct protection_domain
*sva_pdom
= to_pdomain(domain
);
108 struct iommu_dev_data
*dev_data
= dev_iommu_priv_get(dev
);
115 /* PASID zero is used for requests from the I/O device without PASID */
116 if (!is_pasid_valid(dev_data
, pasid
))
119 /* Make sure PASID is enabled */
120 if (!is_pasid_enabled(dev_data
))
123 /* Add PASID to protection domain pasid list */
124 pdom_dev_data
= kzalloc(sizeof(*pdom_dev_data
), GFP_KERNEL
);
125 if (pdom_dev_data
== NULL
)
128 pdom_dev_data
->pasid
= pasid
;
129 pdom_dev_data
->dev_data
= dev_data
;
131 spin_lock_irqsave(&sva_pdom
->lock
, flags
);
133 /* Setup GCR3 table */
134 ret
= amd_iommu_set_gcr3(dev_data
, pasid
,
135 iommu_virt_to_phys(domain
->mm
->pgd
));
137 kfree(pdom_dev_data
);
141 list_add(&pdom_dev_data
->list
, &sva_pdom
->dev_data_list
);
144 spin_unlock_irqrestore(&sva_pdom
->lock
, flags
);
148 void amd_iommu_remove_dev_pasid(struct device
*dev
, ioasid_t pasid
,
149 struct iommu_domain
*domain
)
151 struct protection_domain
*sva_pdom
;
154 if (!is_pasid_valid(dev_iommu_priv_get(dev
), pasid
))
157 sva_pdom
= to_pdomain(domain
);
159 spin_lock_irqsave(&sva_pdom
->lock
, flags
);
161 /* Remove PASID from dev_data_list */
162 remove_pdom_dev_pasid(sva_pdom
, dev
, pasid
);
164 spin_unlock_irqrestore(&sva_pdom
->lock
, flags
);
167 static void iommu_sva_domain_free(struct iommu_domain
*domain
)
169 struct protection_domain
*sva_pdom
= to_pdomain(domain
);
171 if (sva_pdom
->mn
.ops
)
172 mmu_notifier_unregister(&sva_pdom
->mn
, domain
->mm
);
174 amd_iommu_domain_free(domain
);
177 static const struct iommu_domain_ops amd_sva_domain_ops
= {
178 .set_dev_pasid
= iommu_sva_set_dev_pasid
,
179 .free
= iommu_sva_domain_free
182 struct iommu_domain
*amd_iommu_domain_alloc_sva(struct device
*dev
,
183 struct mm_struct
*mm
)
185 struct protection_domain
*pdom
;
188 pdom
= protection_domain_alloc(IOMMU_DOMAIN_SVA
, dev_to_node(dev
));
190 return ERR_PTR(-ENOMEM
);
192 pdom
->domain
.ops
= &amd_sva_domain_ops
;
193 pdom
->mn
.ops
= &sva_mn
;
195 ret
= mmu_notifier_register(&pdom
->mn
, mm
);
197 protection_domain_free(pdom
);
201 return &pdom
->domain
;