|
10 | 10 | #define pr_fmt(fmt) "DMAR: " fmt
|
11 | 11 |
|
12 | 12 | #include <linux/bitops.h>
|
| 13 | +#include <linux/cpufeature.h> |
13 | 14 | #include <linux/dmar.h>
|
14 | 15 | #include <linux/intel-iommu.h>
|
15 | 16 | #include <linux/iommu.h>
|
@@ -389,6 +390,26 @@ static inline void pasid_set_page_snoop(struct pasid_entry *pe, bool value)
|
389 | 390 | pasid_set_bits(&pe->val[1], 1 << 23, value);
|
390 | 391 | }
|
391 | 392 |
|
| 393 | +/* |
| 394 | + * Setup the First Level Page table Pointer field (Bit 140~191) |
| 395 | + * of a scalable mode PASID entry. |
| 396 | + */ |
| 397 | +static inline void |
| 398 | +pasid_set_flptr(struct pasid_entry *pe, u64 value) |
| 399 | +{ |
| 400 | + pasid_set_bits(&pe->val[2], VTD_PAGE_MASK, value); |
| 401 | +} |
| 402 | + |
| 403 | +/* |
| 404 | + * Setup the First Level Paging Mode field (Bit 130~131) of a |
| 405 | + * scalable mode PASID entry. |
| 406 | + */ |
| 407 | +static inline void |
| 408 | +pasid_set_flpm(struct pasid_entry *pe, u64 value) |
| 409 | +{ |
| 410 | + pasid_set_bits(&pe->val[2], GENMASK_ULL(3, 2), value << 2); |
| 411 | +} |
| 412 | + |
392 | 413 | static void
|
393 | 414 | pasid_cache_invalidation_with_pasid(struct intel_iommu *iommu,
|
394 | 415 | u16 did, int pasid)
|
@@ -459,6 +480,65 @@ void intel_pasid_tear_down_entry(struct intel_iommu *iommu,
|
459 | 480 | devtlb_invalidation_with_pasid(iommu, dev, pasid);
|
460 | 481 | }
|
461 | 482 |
|
| 483 | +/* |
| 484 | + * Set up the scalable mode pasid table entry for first only |
| 485 | + * translation type. |
| 486 | + */ |
| 487 | +int intel_pasid_setup_first_level(struct intel_iommu *iommu, |
| 488 | + struct device *dev, pgd_t *pgd, |
| 489 | + int pasid, u16 did, int flags) |
| 490 | +{ |
| 491 | + struct pasid_entry *pte; |
| 492 | + |
| 493 | + if (!ecap_flts(iommu->ecap)) { |
| 494 | + pr_err("No first level translation support on %s\n", |
| 495 | + iommu->name); |
| 496 | + return -EINVAL; |
| 497 | + } |
| 498 | + |
| 499 | + pte = intel_pasid_get_entry(dev, pasid); |
| 500 | + if (WARN_ON(!pte)) |
| 501 | + return -EINVAL; |
| 502 | + |
| 503 | + pasid_clear_entry(pte); |
| 504 | + |
| 505 | + /* Setup the first level page table pointer: */ |
| 506 | + pasid_set_flptr(pte, (u64)__pa(pgd)); |
| 507 | + if (flags & PASID_FLAG_SUPERVISOR_MODE) { |
| 508 | + if (!ecap_srs(iommu->ecap)) { |
| 509 | + pr_err("No supervisor request support on %s\n", |
| 510 | + iommu->name); |
| 511 | + return -EINVAL; |
| 512 | + } |
| 513 | + pasid_set_sre(pte); |
| 514 | + } |
| 515 | + |
| 516 | +#ifdef CONFIG_X86 |
| 517 | + if (cpu_feature_enabled(X86_FEATURE_LA57)) |
| 518 | + pasid_set_flpm(pte, 1); |
| 519 | +#endif /* CONFIG_X86 */ |
| 520 | + |
| 521 | + pasid_set_domain_id(pte, did); |
| 522 | + pasid_set_address_width(pte, iommu->agaw); |
| 523 | + pasid_set_page_snoop(pte, !!ecap_smpwc(iommu->ecap)); |
| 524 | + |
| 525 | + /* Setup Present and PASID Granular Transfer Type: */ |
| 526 | + pasid_set_translation_type(pte, 1); |
| 527 | + pasid_set_present(pte); |
| 528 | + |
| 529 | + if (!ecap_coherent(iommu->ecap)) |
| 530 | + clflush_cache_range(pte, sizeof(*pte)); |
| 531 | + |
| 532 | + if (cap_caching_mode(iommu->cap)) { |
| 533 | + pasid_cache_invalidation_with_pasid(iommu, did, pasid); |
| 534 | + iotlb_invalidation_with_pasid(iommu, did, pasid); |
| 535 | + } else { |
| 536 | + iommu_flush_write_buffer(iommu); |
| 537 | + } |
| 538 | + |
| 539 | + return 0; |
| 540 | +} |
| 541 | + |
462 | 542 | /*
|
463 | 543 | * Set up the scalable mode pasid entry for second only translation type.
|
464 | 544 | */
|
|
0 commit comments