mirror of
https://github.com/torvalds/linux.git
synced 2026-05-29 01:23:56 +02:00
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>
This commit is contained in:
parent
2cb5ff623d
commit
7543ee63e8
|
|
@ -389,6 +389,50 @@ int intel_pasid_setup_first_level(struct intel_iommu *iommu,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int intel_pasid_replace_first_level(struct intel_iommu *iommu,
|
||||||
|
struct device *dev, pgd_t *pgd,
|
||||||
|
u32 pasid, u16 did, u16 old_did,
|
||||||
|
int flags)
|
||||||
|
{
|
||||||
|
struct pasid_entry *pte, new_pte;
|
||||||
|
|
||||||
|
if (!ecap_flts(iommu->ecap)) {
|
||||||
|
pr_err("No first level translation support on %s\n",
|
||||||
|
iommu->name);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((flags & PASID_FLAG_FL5LP) && !cap_fl5lp_support(iommu->cap)) {
|
||||||
|
pr_err("No 5-level paging support for first-level on %s\n",
|
||||||
|
iommu->name);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pasid_pte_config_first_level(iommu, &new_pte, pgd, did, flags);
|
||||||
|
|
||||||
|
spin_lock(&iommu->lock);
|
||||||
|
pte = intel_pasid_get_entry(dev, pasid);
|
||||||
|
if (!pte) {
|
||||||
|
spin_unlock(&iommu->lock);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pasid_pte_is_present(pte)) {
|
||||||
|
spin_unlock(&iommu->lock);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
WARN_ON(old_did != pasid_get_domain_id(pte));
|
||||||
|
|
||||||
|
*pte = new_pte;
|
||||||
|
spin_unlock(&iommu->lock);
|
||||||
|
|
||||||
|
intel_pasid_flush_present(iommu, dev, pasid, old_did, pte);
|
||||||
|
intel_iommu_drain_pasid_prq(dev, pasid);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set up the scalable mode pasid entry for second only translation type.
|
* Set up the scalable mode pasid entry for second only translation type.
|
||||||
*/
|
*/
|
||||||
|
|
@ -456,6 +500,57 @@ int intel_pasid_setup_second_level(struct intel_iommu *iommu,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int intel_pasid_replace_second_level(struct intel_iommu *iommu,
|
||||||
|
struct dmar_domain *domain,
|
||||||
|
struct device *dev, u16 old_did,
|
||||||
|
u32 pasid)
|
||||||
|
{
|
||||||
|
struct pasid_entry *pte, new_pte;
|
||||||
|
struct dma_pte *pgd;
|
||||||
|
u64 pgd_val;
|
||||||
|
u16 did;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If hardware advertises no support for second level
|
||||||
|
* translation, return directly.
|
||||||
|
*/
|
||||||
|
if (!ecap_slts(iommu->ecap)) {
|
||||||
|
pr_err("No second level translation support on %s\n",
|
||||||
|
iommu->name);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pgd = domain->pgd;
|
||||||
|
pgd_val = virt_to_phys(pgd);
|
||||||
|
did = domain_id_iommu(domain, iommu);
|
||||||
|
|
||||||
|
pasid_pte_config_second_level(iommu, &new_pte, pgd_val,
|
||||||
|
domain->agaw, did,
|
||||||
|
domain->dirty_tracking);
|
||||||
|
|
||||||
|
spin_lock(&iommu->lock);
|
||||||
|
pte = intel_pasid_get_entry(dev, pasid);
|
||||||
|
if (!pte) {
|
||||||
|
spin_unlock(&iommu->lock);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pasid_pte_is_present(pte)) {
|
||||||
|
spin_unlock(&iommu->lock);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
WARN_ON(old_did != pasid_get_domain_id(pte));
|
||||||
|
|
||||||
|
*pte = new_pte;
|
||||||
|
spin_unlock(&iommu->lock);
|
||||||
|
|
||||||
|
intel_pasid_flush_present(iommu, dev, pasid, old_did, pte);
|
||||||
|
intel_iommu_drain_pasid_prq(dev, pasid);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set up dirty tracking on a second only or nested translation type.
|
* Set up dirty tracking on a second only or nested translation type.
|
||||||
*/
|
*/
|
||||||
|
|
@ -568,6 +663,38 @@ int intel_pasid_setup_pass_through(struct intel_iommu *iommu,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int intel_pasid_replace_pass_through(struct intel_iommu *iommu,
|
||||||
|
struct device *dev, u16 old_did,
|
||||||
|
u32 pasid)
|
||||||
|
{
|
||||||
|
struct pasid_entry *pte, new_pte;
|
||||||
|
u16 did = FLPT_DEFAULT_DID;
|
||||||
|
|
||||||
|
pasid_pte_config_pass_through(iommu, &new_pte, did);
|
||||||
|
|
||||||
|
spin_lock(&iommu->lock);
|
||||||
|
pte = intel_pasid_get_entry(dev, pasid);
|
||||||
|
if (!pte) {
|
||||||
|
spin_unlock(&iommu->lock);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pasid_pte_is_present(pte)) {
|
||||||
|
spin_unlock(&iommu->lock);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
WARN_ON(old_did != pasid_get_domain_id(pte));
|
||||||
|
|
||||||
|
*pte = new_pte;
|
||||||
|
spin_unlock(&iommu->lock);
|
||||||
|
|
||||||
|
intel_pasid_flush_present(iommu, dev, pasid, old_did, pte);
|
||||||
|
intel_iommu_drain_pasid_prq(dev, pasid);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set the page snoop control for a pasid entry which has been set up.
|
* Set the page snoop control for a pasid entry which has been set up.
|
||||||
*/
|
*/
|
||||||
|
|
@ -698,6 +825,69 @@ int intel_pasid_setup_nested(struct intel_iommu *iommu, struct device *dev,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int intel_pasid_replace_nested(struct intel_iommu *iommu,
|
||||||
|
struct device *dev, u32 pasid,
|
||||||
|
u16 old_did, struct dmar_domain *domain)
|
||||||
|
{
|
||||||
|
struct iommu_hwpt_vtd_s1 *s1_cfg = &domain->s1_cfg;
|
||||||
|
struct dmar_domain *s2_domain = domain->s2_domain;
|
||||||
|
u16 did = domain_id_iommu(domain, iommu);
|
||||||
|
struct pasid_entry *pte, new_pte;
|
||||||
|
|
||||||
|
/* Address width should match the address width supported by hardware */
|
||||||
|
switch (s1_cfg->addr_width) {
|
||||||
|
case ADDR_WIDTH_4LEVEL:
|
||||||
|
break;
|
||||||
|
case ADDR_WIDTH_5LEVEL:
|
||||||
|
if (!cap_fl5lp_support(iommu->cap)) {
|
||||||
|
dev_err_ratelimited(dev,
|
||||||
|
"5-level paging not supported\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
dev_err_ratelimited(dev, "Invalid stage-1 address width %d\n",
|
||||||
|
s1_cfg->addr_width);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((s1_cfg->flags & IOMMU_VTD_S1_SRE) && !ecap_srs(iommu->ecap)) {
|
||||||
|
pr_err_ratelimited("No supervisor request support on %s\n",
|
||||||
|
iommu->name);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((s1_cfg->flags & IOMMU_VTD_S1_EAFE) && !ecap_eafs(iommu->ecap)) {
|
||||||
|
pr_err_ratelimited("No extended access flag support on %s\n",
|
||||||
|
iommu->name);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pasid_pte_config_nestd(iommu, &new_pte, s1_cfg, s2_domain, did);
|
||||||
|
|
||||||
|
spin_lock(&iommu->lock);
|
||||||
|
pte = intel_pasid_get_entry(dev, pasid);
|
||||||
|
if (!pte) {
|
||||||
|
spin_unlock(&iommu->lock);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pasid_pte_is_present(pte)) {
|
||||||
|
spin_unlock(&iommu->lock);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
WARN_ON(old_did != pasid_get_domain_id(pte));
|
||||||
|
|
||||||
|
*pte = new_pte;
|
||||||
|
spin_unlock(&iommu->lock);
|
||||||
|
|
||||||
|
intel_pasid_flush_present(iommu, dev, pasid, old_did, pte);
|
||||||
|
intel_iommu_drain_pasid_prq(dev, pasid);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Interfaces to setup or teardown a pasid table to the scalable-mode
|
* Interfaces to setup or teardown a pasid table to the scalable-mode
|
||||||
* context table entry:
|
* context table entry:
|
||||||
|
|
|
||||||
|
|
@ -303,6 +303,21 @@ int intel_pasid_setup_pass_through(struct intel_iommu *iommu,
|
||||||
struct device *dev, u32 pasid);
|
struct device *dev, u32 pasid);
|
||||||
int intel_pasid_setup_nested(struct intel_iommu *iommu, struct device *dev,
|
int intel_pasid_setup_nested(struct intel_iommu *iommu, struct device *dev,
|
||||||
u32 pasid, struct dmar_domain *domain);
|
u32 pasid, struct dmar_domain *domain);
|
||||||
|
int intel_pasid_replace_first_level(struct intel_iommu *iommu,
|
||||||
|
struct device *dev, pgd_t *pgd,
|
||||||
|
u32 pasid, u16 did, u16 old_did,
|
||||||
|
int flags);
|
||||||
|
int intel_pasid_replace_second_level(struct intel_iommu *iommu,
|
||||||
|
struct dmar_domain *domain,
|
||||||
|
struct device *dev, u16 old_did,
|
||||||
|
u32 pasid);
|
||||||
|
int intel_pasid_replace_pass_through(struct intel_iommu *iommu,
|
||||||
|
struct device *dev, u16 old_did,
|
||||||
|
u32 pasid);
|
||||||
|
int intel_pasid_replace_nested(struct intel_iommu *iommu,
|
||||||
|
struct device *dev, u32 pasid,
|
||||||
|
u16 old_did, struct dmar_domain *domain);
|
||||||
|
|
||||||
void intel_pasid_tear_down_entry(struct intel_iommu *iommu,
|
void intel_pasid_tear_down_entry(struct intel_iommu *iommu,
|
||||||
struct device *dev, u32 pasid,
|
struct device *dev, u32 pasid,
|
||||||
bool fault_ignore);
|
bool fault_ignore);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user