Skip to content

Commit 7543ee6

Browse files
yiliu1765joergroedel
authored andcommitted
iommu/vt-d: Add pasid replace helpers
pasid replacement allows converting a present pasid entry to be FS, SS, PT or nested, hence add helpers for such operations. Suggested-by: Lu Baolu <baolu.lu@linux.intel.com> Reviewed-by: Lu Baolu <baolu.lu@linux.intel.com> Reviewed-by: Kevin Tian <kevin.tian@intel.com> Signed-off-by: Yi Liu <yi.l.liu@intel.com> Link: https://lore.kernel.org/r/20241107122234.7424-5-yi.l.liu@intel.com Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com> Signed-off-by: Joerg Roedel <jroedel@suse.de>
1 parent 2cb5ff6 commit 7543ee6

File tree

2 files changed

+205
-0
lines changed

2 files changed

+205
-0
lines changed

drivers/iommu/intel/pasid.c

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,50 @@ int intel_pasid_setup_first_level(struct intel_iommu *iommu,
389389
return 0;
390390
}
391391

392+
int intel_pasid_replace_first_level(struct intel_iommu *iommu,
393+
struct device *dev, pgd_t *pgd,
394+
u32 pasid, u16 did, u16 old_did,
395+
int flags)
396+
{
397+
struct pasid_entry *pte, new_pte;
398+
399+
if (!ecap_flts(iommu->ecap)) {
400+
pr_err("No first level translation support on %s\n",
401+
iommu->name);
402+
return -EINVAL;
403+
}
404+
405+
if ((flags & PASID_FLAG_FL5LP) && !cap_fl5lp_support(iommu->cap)) {
406+
pr_err("No 5-level paging support for first-level on %s\n",
407+
iommu->name);
408+
return -EINVAL;
409+
}
410+
411+
pasid_pte_config_first_level(iommu, &new_pte, pgd, did, flags);
412+
413+
spin_lock(&iommu->lock);
414+
pte = intel_pasid_get_entry(dev, pasid);
415+
if (!pte) {
416+
spin_unlock(&iommu->lock);
417+
return -ENODEV;
418+
}
419+
420+
if (!pasid_pte_is_present(pte)) {
421+
spin_unlock(&iommu->lock);
422+
return -EINVAL;
423+
}
424+
425+
WARN_ON(old_did != pasid_get_domain_id(pte));
426+
427+
*pte = new_pte;
428+
spin_unlock(&iommu->lock);
429+
430+
intel_pasid_flush_present(iommu, dev, pasid, old_did, pte);
431+
intel_iommu_drain_pasid_prq(dev, pasid);
432+
433+
return 0;
434+
}
435+
392436
/*
393437
* Set up the scalable mode pasid entry for second only translation type.
394438
*/
@@ -456,6 +500,57 @@ int intel_pasid_setup_second_level(struct intel_iommu *iommu,
456500
return 0;
457501
}
458502

503+
int intel_pasid_replace_second_level(struct intel_iommu *iommu,
504+
struct dmar_domain *domain,
505+
struct device *dev, u16 old_did,
506+
u32 pasid)
507+
{
508+
struct pasid_entry *pte, new_pte;
509+
struct dma_pte *pgd;
510+
u64 pgd_val;
511+
u16 did;
512+
513+
/*
514+
* If hardware advertises no support for second level
515+
* translation, return directly.
516+
*/
517+
if (!ecap_slts(iommu->ecap)) {
518+
pr_err("No second level translation support on %s\n",
519+
iommu->name);
520+
return -EINVAL;
521+
}
522+
523+
pgd = domain->pgd;
524+
pgd_val = virt_to_phys(pgd);
525+
did = domain_id_iommu(domain, iommu);
526+
527+
pasid_pte_config_second_level(iommu, &new_pte, pgd_val,
528+
domain->agaw, did,
529+
domain->dirty_tracking);
530+
531+
spin_lock(&iommu->lock);
532+
pte = intel_pasid_get_entry(dev, pasid);
533+
if (!pte) {
534+
spin_unlock(&iommu->lock);
535+
return -ENODEV;
536+
}
537+
538+
if (!pasid_pte_is_present(pte)) {
539+
spin_unlock(&iommu->lock);
540+
return -EINVAL;
541+
}
542+
543+
WARN_ON(old_did != pasid_get_domain_id(pte));
544+
545+
*pte = new_pte;
546+
spin_unlock(&iommu->lock);
547+
548+
intel_pasid_flush_present(iommu, dev, pasid, old_did, pte);
549+
intel_iommu_drain_pasid_prq(dev, pasid);
550+
551+
return 0;
552+
}
553+
459554
/*
460555
* Set up dirty tracking on a second only or nested translation type.
461556
*/
@@ -568,6 +663,38 @@ int intel_pasid_setup_pass_through(struct intel_iommu *iommu,
568663
return 0;
569664
}
570665

666+
int intel_pasid_replace_pass_through(struct intel_iommu *iommu,
667+
struct device *dev, u16 old_did,
668+
u32 pasid)
669+
{
670+
struct pasid_entry *pte, new_pte;
671+
u16 did = FLPT_DEFAULT_DID;
672+
673+
pasid_pte_config_pass_through(iommu, &new_pte, did);
674+
675+
spin_lock(&iommu->lock);
676+
pte = intel_pasid_get_entry(dev, pasid);
677+
if (!pte) {
678+
spin_unlock(&iommu->lock);
679+
return -ENODEV;
680+
}
681+
682+
if (!pasid_pte_is_present(pte)) {
683+
spin_unlock(&iommu->lock);
684+
return -EINVAL;
685+
}
686+
687+
WARN_ON(old_did != pasid_get_domain_id(pte));
688+
689+
*pte = new_pte;
690+
spin_unlock(&iommu->lock);
691+
692+
intel_pasid_flush_present(iommu, dev, pasid, old_did, pte);
693+
intel_iommu_drain_pasid_prq(dev, pasid);
694+
695+
return 0;
696+
}
697+
571698
/*
572699
* Set the page snoop control for a pasid entry which has been set up.
573700
*/
@@ -698,6 +825,69 @@ int intel_pasid_setup_nested(struct intel_iommu *iommu, struct device *dev,
698825
return 0;
699826
}
700827

828+
int intel_pasid_replace_nested(struct intel_iommu *iommu,
829+
struct device *dev, u32 pasid,
830+
u16 old_did, struct dmar_domain *domain)
831+
{
832+
struct iommu_hwpt_vtd_s1 *s1_cfg = &domain->s1_cfg;
833+
struct dmar_domain *s2_domain = domain->s2_domain;
834+
u16 did = domain_id_iommu(domain, iommu);
835+
struct pasid_entry *pte, new_pte;
836+
837+
/* Address width should match the address width supported by hardware */
838+
switch (s1_cfg->addr_width) {
839+
case ADDR_WIDTH_4LEVEL:
840+
break;
841+
case ADDR_WIDTH_5LEVEL:
842+
if (!cap_fl5lp_support(iommu->cap)) {
843+
dev_err_ratelimited(dev,
844+
"5-level paging not supported\n");
845+
return -EINVAL;
846+
}
847+
break;
848+
default:
849+
dev_err_ratelimited(dev, "Invalid stage-1 address width %d\n",
850+
s1_cfg->addr_width);
851+
return -EINVAL;
852+
}
853+
854+
if ((s1_cfg->flags & IOMMU_VTD_S1_SRE) && !ecap_srs(iommu->ecap)) {
855+
pr_err_ratelimited("No supervisor request support on %s\n",
856+
iommu->name);
857+
return -EINVAL;
858+
}
859+
860+
if ((s1_cfg->flags & IOMMU_VTD_S1_EAFE) && !ecap_eafs(iommu->ecap)) {
861+
pr_err_ratelimited("No extended access flag support on %s\n",
862+
iommu->name);
863+
return -EINVAL;
864+
}
865+
866+
pasid_pte_config_nestd(iommu, &new_pte, s1_cfg, s2_domain, did);
867+
868+
spin_lock(&iommu->lock);
869+
pte = intel_pasid_get_entry(dev, pasid);
870+
if (!pte) {
871+
spin_unlock(&iommu->lock);
872+
return -ENODEV;
873+
}
874+
875+
if (!pasid_pte_is_present(pte)) {
876+
spin_unlock(&iommu->lock);
877+
return -EINVAL;
878+
}
879+
880+
WARN_ON(old_did != pasid_get_domain_id(pte));
881+
882+
*pte = new_pte;
883+
spin_unlock(&iommu->lock);
884+
885+
intel_pasid_flush_present(iommu, dev, pasid, old_did, pte);
886+
intel_iommu_drain_pasid_prq(dev, pasid);
887+
888+
return 0;
889+
}
890+
701891
/*
702892
* Interfaces to setup or teardown a pasid table to the scalable-mode
703893
* context table entry:

drivers/iommu/intel/pasid.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,21 @@ int intel_pasid_setup_pass_through(struct intel_iommu *iommu,
303303
struct device *dev, u32 pasid);
304304
int intel_pasid_setup_nested(struct intel_iommu *iommu, struct device *dev,
305305
u32 pasid, struct dmar_domain *domain);
306+
int intel_pasid_replace_first_level(struct intel_iommu *iommu,
307+
struct device *dev, pgd_t *pgd,
308+
u32 pasid, u16 did, u16 old_did,
309+
int flags);
310+
int intel_pasid_replace_second_level(struct intel_iommu *iommu,
311+
struct dmar_domain *domain,
312+
struct device *dev, u16 old_did,
313+
u32 pasid);
314+
int intel_pasid_replace_pass_through(struct intel_iommu *iommu,
315+
struct device *dev, u16 old_did,
316+
u32 pasid);
317+
int intel_pasid_replace_nested(struct intel_iommu *iommu,
318+
struct device *dev, u32 pasid,
319+
u16 old_did, struct dmar_domain *domain);
320+
306321
void intel_pasid_tear_down_entry(struct intel_iommu *iommu,
307322
struct device *dev, u32 pasid,
308323
bool fault_ignore);

0 commit comments

Comments
 (0)