pasid.c 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (C) 2024 Advanced Micro Devices, Inc.
  4. */
  5. #define pr_fmt(fmt) "AMD-Vi: " fmt
  6. #define dev_fmt(fmt) pr_fmt(fmt)
  7. #include <linux/iommu.h>
  8. #include <linux/mm_types.h>
  9. #include "amd_iommu.h"
  10. static inline bool is_pasid_enabled(struct iommu_dev_data *dev_data)
  11. {
  12. if (dev_data->pasid_enabled && dev_data->max_pasids &&
  13. dev_data->gcr3_info.gcr3_tbl != NULL)
  14. return true;
  15. return false;
  16. }
  17. static inline bool is_pasid_valid(struct iommu_dev_data *dev_data,
  18. ioasid_t pasid)
  19. {
  20. if (pasid > 0 && pasid < dev_data->max_pasids)
  21. return true;
  22. return false;
  23. }
  24. static void remove_dev_pasid(struct pdom_dev_data *pdom_dev_data)
  25. {
  26. /* Update GCR3 table and flush IOTLB */
  27. amd_iommu_clear_gcr3(pdom_dev_data->dev_data, pdom_dev_data->pasid);
  28. list_del(&pdom_dev_data->list);
  29. kfree(pdom_dev_data);
  30. }
  31. /* Clear PASID from device GCR3 table and remove pdom_dev_data from list */
  32. static void remove_pdom_dev_pasid(struct protection_domain *pdom,
  33. struct device *dev, ioasid_t pasid)
  34. {
  35. struct pdom_dev_data *pdom_dev_data;
  36. struct iommu_dev_data *dev_data = dev_iommu_priv_get(dev);
  37. lockdep_assert_held(&pdom->lock);
  38. for_each_pdom_dev_data(pdom_dev_data, pdom) {
  39. if (pdom_dev_data->dev_data == dev_data &&
  40. pdom_dev_data->pasid == pasid) {
  41. remove_dev_pasid(pdom_dev_data);
  42. break;
  43. }
  44. }
  45. }
  46. static void sva_arch_invalidate_secondary_tlbs(struct mmu_notifier *mn,
  47. struct mm_struct *mm,
  48. unsigned long start, unsigned long end)
  49. {
  50. struct pdom_dev_data *pdom_dev_data;
  51. struct protection_domain *sva_pdom;
  52. unsigned long flags;
  53. sva_pdom = container_of(mn, struct protection_domain, mn);
  54. spin_lock_irqsave(&sva_pdom->lock, flags);
  55. for_each_pdom_dev_data(pdom_dev_data, sva_pdom) {
  56. amd_iommu_dev_flush_pasid_pages(pdom_dev_data->dev_data,
  57. pdom_dev_data->pasid,
  58. start, end - start);
  59. }
  60. spin_unlock_irqrestore(&sva_pdom->lock, flags);
  61. }
  62. static void sva_mn_release(struct mmu_notifier *mn, struct mm_struct *mm)
  63. {
  64. struct pdom_dev_data *pdom_dev_data, *next;
  65. struct protection_domain *sva_pdom;
  66. unsigned long flags;
  67. sva_pdom = container_of(mn, struct protection_domain, mn);
  68. spin_lock_irqsave(&sva_pdom->lock, flags);
  69. /* Assume dev_data_list contains same PASID with different devices */
  70. for_each_pdom_dev_data_safe(pdom_dev_data, next, sva_pdom)
  71. remove_dev_pasid(pdom_dev_data);
  72. spin_unlock_irqrestore(&sva_pdom->lock, flags);
  73. }
  74. static const struct mmu_notifier_ops sva_mn = {
  75. .arch_invalidate_secondary_tlbs = sva_arch_invalidate_secondary_tlbs,
  76. .release = sva_mn_release,
  77. };
  78. int iommu_sva_set_dev_pasid(struct iommu_domain *domain,
  79. struct device *dev, ioasid_t pasid)
  80. {
  81. struct pdom_dev_data *pdom_dev_data;
  82. struct protection_domain *sva_pdom = to_pdomain(domain);
  83. struct iommu_dev_data *dev_data = dev_iommu_priv_get(dev);
  84. unsigned long flags;
  85. int ret = -EINVAL;
  86. /* PASID zero is used for requests from the I/O device without PASID */
  87. if (!is_pasid_valid(dev_data, pasid))
  88. return ret;
  89. /* Make sure PASID is enabled */
  90. if (!is_pasid_enabled(dev_data))
  91. return ret;
  92. /* Add PASID to protection domain pasid list */
  93. pdom_dev_data = kzalloc(sizeof(*pdom_dev_data), GFP_KERNEL);
  94. if (pdom_dev_data == NULL)
  95. return ret;
  96. pdom_dev_data->pasid = pasid;
  97. pdom_dev_data->dev_data = dev_data;
  98. spin_lock_irqsave(&sva_pdom->lock, flags);
  99. /* Setup GCR3 table */
  100. ret = amd_iommu_set_gcr3(dev_data, pasid,
  101. iommu_virt_to_phys(domain->mm->pgd));
  102. if (ret) {
  103. kfree(pdom_dev_data);
  104. goto out_unlock;
  105. }
  106. list_add(&pdom_dev_data->list, &sva_pdom->dev_data_list);
  107. out_unlock:
  108. spin_unlock_irqrestore(&sva_pdom->lock, flags);
  109. return ret;
  110. }
  111. void amd_iommu_remove_dev_pasid(struct device *dev, ioasid_t pasid,
  112. struct iommu_domain *domain)
  113. {
  114. struct protection_domain *sva_pdom;
  115. unsigned long flags;
  116. if (!is_pasid_valid(dev_iommu_priv_get(dev), pasid))
  117. return;
  118. sva_pdom = to_pdomain(domain);
  119. spin_lock_irqsave(&sva_pdom->lock, flags);
  120. /* Remove PASID from dev_data_list */
  121. remove_pdom_dev_pasid(sva_pdom, dev, pasid);
  122. spin_unlock_irqrestore(&sva_pdom->lock, flags);
  123. }
  124. static void iommu_sva_domain_free(struct iommu_domain *domain)
  125. {
  126. struct protection_domain *sva_pdom = to_pdomain(domain);
  127. if (sva_pdom->mn.ops)
  128. mmu_notifier_unregister(&sva_pdom->mn, domain->mm);
  129. amd_iommu_domain_free(domain);
  130. }
  131. static const struct iommu_domain_ops amd_sva_domain_ops = {
  132. .set_dev_pasid = iommu_sva_set_dev_pasid,
  133. .free = iommu_sva_domain_free
  134. };
  135. struct iommu_domain *amd_iommu_domain_alloc_sva(struct device *dev,
  136. struct mm_struct *mm)
  137. {
  138. struct protection_domain *pdom;
  139. int ret;
  140. pdom = protection_domain_alloc(IOMMU_DOMAIN_SVA, dev_to_node(dev));
  141. if (!pdom)
  142. return ERR_PTR(-ENOMEM);
  143. pdom->domain.ops = &amd_sva_domain_ops;
  144. pdom->mn.ops = &sva_mn;
  145. ret = mmu_notifier_register(&pdom->mn, mm);
  146. if (ret) {
  147. protection_domain_free(pdom);
  148. return ERR_PTR(ret);
  149. }
  150. return &pdom->domain;
  151. }