|
37 | 37 | #include <asm/iommu.h> |
38 | 38 | #include <asm/gart.h> |
39 | 39 | #include <asm/dma.h> |
| 40 | +#include <uapi/linux/iommufd.h> |
40 | 41 |
|
41 | 42 | #include "amd_iommu.h" |
42 | 43 | #include "../dma-iommu.h" |
@@ -65,6 +66,7 @@ LIST_HEAD(hpet_map); |
65 | 66 | LIST_HEAD(acpihid_map); |
66 | 67 |
|
67 | 68 | const struct iommu_ops amd_iommu_ops; |
| 69 | +const struct iommu_dirty_ops amd_dirty_ops; |
68 | 70 |
|
69 | 71 | static ATOMIC_NOTIFIER_HEAD(ppr_notifier); |
70 | 72 | int amd_iommu_max_glx_val = -1; |
@@ -1610,6 +1612,9 @@ static void set_dte_entry(struct amd_iommu *iommu, u16 devid, |
1610 | 1612 | pte_root |= 1ULL << DEV_ENTRY_PPR; |
1611 | 1613 | } |
1612 | 1614 |
|
| 1615 | + if (domain->dirty_tracking) |
| 1616 | + pte_root |= DTE_FLAG_HAD; |
| 1617 | + |
1613 | 1618 | if (domain->flags & PD_IOMMUV2_MASK) { |
1614 | 1619 | u64 gcr3 = iommu_virt_to_phys(domain->gcr3_tbl); |
1615 | 1620 | u64 glx = domain->glx; |
@@ -2155,28 +2160,79 @@ static inline u64 dma_max_address(void) |
2155 | 2160 | return ((1ULL << PM_LEVEL_SHIFT(amd_iommu_gpt_level)) - 1); |
2156 | 2161 | } |
2157 | 2162 |
|
2158 | | -static struct iommu_domain *amd_iommu_domain_alloc(unsigned type) |
| 2163 | +static bool amd_iommu_hd_support(struct amd_iommu *iommu) |
2159 | 2164 | { |
| 2165 | + return iommu && (iommu->features & FEATURE_HDSUP); |
| 2166 | +} |
| 2167 | + |
| 2168 | +static struct iommu_domain *do_iommu_domain_alloc(unsigned int type, |
| 2169 | + struct device *dev, u32 flags) |
| 2170 | +{ |
| 2171 | + bool dirty_tracking = flags & IOMMU_HWPT_ALLOC_DIRTY_TRACKING; |
2160 | 2172 | struct protection_domain *domain; |
| 2173 | + struct amd_iommu *iommu = NULL; |
| 2174 | + |
| 2175 | + if (dev) { |
| 2176 | + iommu = rlookup_amd_iommu(dev); |
| 2177 | + if (!iommu) |
| 2178 | + return ERR_PTR(-ENODEV); |
| 2179 | + } |
2161 | 2180 |
|
2162 | 2181 | /* |
2163 | 2182 | * Since DTE[Mode]=0 is prohibited on SNP-enabled system, |
2164 | 2183 | * default to use IOMMU_DOMAIN_DMA[_FQ]. |
2165 | 2184 | */ |
2166 | 2185 | if (amd_iommu_snp_en && (type == IOMMU_DOMAIN_IDENTITY)) |
2167 | | - return NULL; |
| 2186 | + return ERR_PTR(-EINVAL); |
| 2187 | + |
| 2188 | + if (dirty_tracking && !amd_iommu_hd_support(iommu)) |
| 2189 | + return ERR_PTR(-EOPNOTSUPP); |
2168 | 2190 |
|
2169 | 2191 | domain = protection_domain_alloc(type); |
2170 | 2192 | if (!domain) |
2171 | | - return NULL; |
| 2193 | + return ERR_PTR(-ENOMEM); |
2172 | 2194 |
|
2173 | 2195 | domain->domain.geometry.aperture_start = 0; |
2174 | 2196 | domain->domain.geometry.aperture_end = dma_max_address(); |
2175 | 2197 | domain->domain.geometry.force_aperture = true; |
2176 | 2198 |
|
| 2199 | + if (iommu) { |
| 2200 | + domain->domain.type = type; |
| 2201 | + domain->domain.pgsize_bitmap = iommu->iommu.ops->pgsize_bitmap; |
| 2202 | + domain->domain.ops = iommu->iommu.ops->default_domain_ops; |
| 2203 | + |
| 2204 | + if (dirty_tracking) |
| 2205 | + domain->domain.dirty_ops = &amd_dirty_ops; |
| 2206 | + } |
| 2207 | + |
2177 | 2208 | return &domain->domain; |
2178 | 2209 | } |
2179 | 2210 |
|
| 2211 | +static struct iommu_domain *amd_iommu_domain_alloc(unsigned int type) |
| 2212 | +{ |
| 2213 | + struct iommu_domain *domain; |
| 2214 | + |
| 2215 | + domain = do_iommu_domain_alloc(type, NULL, 0); |
| 2216 | + if (IS_ERR(domain)) |
| 2217 | + return NULL; |
| 2218 | + |
| 2219 | + return domain; |
| 2220 | +} |
| 2221 | + |
| 2222 | +static struct iommu_domain * |
| 2223 | +amd_iommu_domain_alloc_user(struct device *dev, u32 flags, |
| 2224 | + struct iommu_domain *parent, |
| 2225 | + const struct iommu_user_data *user_data) |
| 2226 | + |
| 2227 | +{ |
| 2228 | + unsigned int type = IOMMU_DOMAIN_UNMANAGED; |
| 2229 | + |
| 2230 | + if ((flags & ~IOMMU_HWPT_ALLOC_DIRTY_TRACKING) || parent || user_data) |
| 2231 | + return ERR_PTR(-EOPNOTSUPP); |
| 2232 | + |
| 2233 | + return do_iommu_domain_alloc(type, dev, flags); |
| 2234 | +} |
| 2235 | + |
2180 | 2236 | static void amd_iommu_domain_free(struct iommu_domain *dom) |
2181 | 2237 | { |
2182 | 2238 | struct protection_domain *domain; |
@@ -2214,6 +2270,13 @@ static int amd_iommu_attach_device(struct iommu_domain *dom, |
2214 | 2270 |
|
2215 | 2271 | dev_data->defer_attach = false; |
2216 | 2272 |
|
| 2273 | + /* |
| 2274 | + * Restrict to devices with compatible IOMMU hardware support |
| 2275 | + * when enforcement of dirty tracking is enabled. |
| 2276 | + */ |
| 2277 | + if (dom->dirty_ops && !amd_iommu_hd_support(iommu)) |
| 2278 | + return -EINVAL; |
| 2279 | + |
2217 | 2280 | if (dev_data->domain) |
2218 | 2281 | detach_device(dev); |
2219 | 2282 |
|
@@ -2332,13 +2395,85 @@ static bool amd_iommu_capable(struct device *dev, enum iommu_cap cap) |
2332 | 2395 | return true; |
2333 | 2396 | case IOMMU_CAP_DEFERRED_FLUSH: |
2334 | 2397 | return true; |
| 2398 | + case IOMMU_CAP_DIRTY_TRACKING: { |
| 2399 | + struct amd_iommu *iommu = rlookup_amd_iommu(dev); |
| 2400 | + |
| 2401 | + return amd_iommu_hd_support(iommu); |
| 2402 | + } |
2335 | 2403 | default: |
2336 | 2404 | break; |
2337 | 2405 | } |
2338 | 2406 |
|
2339 | 2407 | return false; |
2340 | 2408 | } |
2341 | 2409 |
|
| 2410 | +static int amd_iommu_set_dirty_tracking(struct iommu_domain *domain, |
| 2411 | + bool enable) |
| 2412 | +{ |
| 2413 | + struct protection_domain *pdomain = to_pdomain(domain); |
| 2414 | + struct dev_table_entry *dev_table; |
| 2415 | + struct iommu_dev_data *dev_data; |
| 2416 | + bool domain_flush = false; |
| 2417 | + struct amd_iommu *iommu; |
| 2418 | + unsigned long flags; |
| 2419 | + u64 pte_root; |
| 2420 | + |
| 2421 | + spin_lock_irqsave(&pdomain->lock, flags); |
| 2422 | + if (!(pdomain->dirty_tracking ^ enable)) { |
| 2423 | + spin_unlock_irqrestore(&pdomain->lock, flags); |
| 2424 | + return 0; |
| 2425 | + } |
| 2426 | + |
| 2427 | + list_for_each_entry(dev_data, &pdomain->dev_list, list) { |
| 2428 | + iommu = rlookup_amd_iommu(dev_data->dev); |
| 2429 | + if (!iommu) |
| 2430 | + continue; |
| 2431 | + |
| 2432 | + dev_table = get_dev_table(iommu); |
| 2433 | + pte_root = dev_table[dev_data->devid].data[0]; |
| 2434 | + |
| 2435 | + pte_root = (enable ? pte_root | DTE_FLAG_HAD : |
| 2436 | + pte_root & ~DTE_FLAG_HAD); |
| 2437 | + |
| 2438 | + /* Flush device DTE */ |
| 2439 | + dev_table[dev_data->devid].data[0] = pte_root; |
| 2440 | + device_flush_dte(dev_data); |
| 2441 | + domain_flush = true; |
| 2442 | + } |
| 2443 | + |
| 2444 | + /* Flush IOTLB to mark IOPTE dirty on the next translation(s) */ |
| 2445 | + if (domain_flush) { |
| 2446 | + amd_iommu_domain_flush_tlb_pde(pdomain); |
| 2447 | + amd_iommu_domain_flush_complete(pdomain); |
| 2448 | + } |
| 2449 | + pdomain->dirty_tracking = enable; |
| 2450 | + spin_unlock_irqrestore(&pdomain->lock, flags); |
| 2451 | + |
| 2452 | + return 0; |
| 2453 | +} |
| 2454 | + |
| 2455 | +static int amd_iommu_read_and_clear_dirty(struct iommu_domain *domain, |
| 2456 | + unsigned long iova, size_t size, |
| 2457 | + unsigned long flags, |
| 2458 | + struct iommu_dirty_bitmap *dirty) |
| 2459 | +{ |
| 2460 | + struct protection_domain *pdomain = to_pdomain(domain); |
| 2461 | + struct io_pgtable_ops *ops = &pdomain->iop.iop.ops; |
| 2462 | + unsigned long lflags; |
| 2463 | + |
| 2464 | + if (!ops || !ops->read_and_clear_dirty) |
| 2465 | + return -EOPNOTSUPP; |
| 2466 | + |
| 2467 | + spin_lock_irqsave(&pdomain->lock, lflags); |
| 2468 | + if (!pdomain->dirty_tracking && dirty->bitmap) { |
| 2469 | + spin_unlock_irqrestore(&pdomain->lock, lflags); |
| 2470 | + return -EINVAL; |
| 2471 | + } |
| 2472 | + spin_unlock_irqrestore(&pdomain->lock, lflags); |
| 2473 | + |
| 2474 | + return ops->read_and_clear_dirty(ops, iova, size, flags, dirty); |
| 2475 | +} |
| 2476 | + |
2342 | 2477 | static void amd_iommu_get_resv_regions(struct device *dev, |
2343 | 2478 | struct list_head *head) |
2344 | 2479 | { |
@@ -2461,9 +2596,15 @@ static bool amd_iommu_enforce_cache_coherency(struct iommu_domain *domain) |
2461 | 2596 | return true; |
2462 | 2597 | } |
2463 | 2598 |
|
| 2599 | +const struct iommu_dirty_ops amd_dirty_ops = { |
| 2600 | + .set_dirty_tracking = amd_iommu_set_dirty_tracking, |
| 2601 | + .read_and_clear_dirty = amd_iommu_read_and_clear_dirty, |
| 2602 | +}; |
| 2603 | + |
2464 | 2604 | const struct iommu_ops amd_iommu_ops = { |
2465 | 2605 | .capable = amd_iommu_capable, |
2466 | 2606 | .domain_alloc = amd_iommu_domain_alloc, |
| 2607 | + .domain_alloc_user = amd_iommu_domain_alloc_user, |
2467 | 2608 | .probe_device = amd_iommu_probe_device, |
2468 | 2609 | .release_device = amd_iommu_release_device, |
2469 | 2610 | .probe_finalize = amd_iommu_probe_finalize, |
|
0 commit comments