Commit 103f4e7c authored by Suravee Suthikulpanit's avatar Suravee Suthikulpanit Committed by Joerg Roedel
Browse files

iommu/amd: Add support for nested domain attach/detach



Introduce set_dte_nested() to program guest translation settings in
the host DTE when attaches the nested domain to a device.

Reviewed-by: default avatarNicolin Chen <nicolinc@nvidia.com>
Signed-off-by: default avatarSuravee Suthikulpanit <suravee.suthikulpanit@amd.com>
Signed-off-by: default avatarJoerg Roedel <joerg.roedel@amd.com>
parent 93eee2a4
Loading
Loading
Loading
Loading
+73 −0
Original line number Diff line number Diff line
@@ -183,6 +183,78 @@ amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags,
	return ERR_PTR(ret);
}

static void set_dte_nested(struct amd_iommu *iommu, struct iommu_domain *dom,
			   struct iommu_dev_data *dev_data, struct dev_table_entry *new)
{
	struct protection_domain *parent;
	struct nested_domain *ndom = to_ndomain(dom);
	struct iommu_hwpt_amd_guest *gdte = &ndom->gdte;
	struct pt_iommu_amdv1_hw_info pt_info;

	/*
	 * The nest parent domain is attached during the call to the
	 * struct iommu_ops.viommu_init(), which will be stored as part
	 * of the struct amd_iommu_viommu.parent.
	 */
	if (WARN_ON(!ndom->viommu || !ndom->viommu->parent))
		return;

	parent = ndom->viommu->parent;
	amd_iommu_make_clear_dte(dev_data, new);

	/* Retrieve the current pagetable info via the IOMMU PT API. */
	pt_iommu_amdv1_hw_info(&parent->amdv1, &pt_info);

	/*
	 * Use domain ID from nested domain to program DTE.
	 * See amd_iommu_alloc_domain_nested().
	 */
	amd_iommu_set_dte_v1(dev_data, parent, ndom->gdom_info->hdom_id,
			     &pt_info, new);

	/* GV is required for nested page table */
	new->data[0] |= DTE_FLAG_GV;

	/* Guest PPR */
	new->data[0] |= gdte->dte[0] & DTE_FLAG_PPR;

	/* Guest translation stuff */
	new->data[0] |= gdte->dte[0] & (DTE_GLX | DTE_FLAG_GIOV);

	/* GCR3 table */
	new->data[0] |= gdte->dte[0] & DTE_GCR3_14_12;
	new->data[1] |= gdte->dte[1] & (DTE_GCR3_30_15 | DTE_GCR3_51_31);

	/* Guest paging mode */
	new->data[2] |= gdte->dte[2] & DTE_GPT_LEVEL_MASK;
}

static int nested_attach_device(struct iommu_domain *dom, struct device *dev,
				struct iommu_domain *old)
{
	struct dev_table_entry new = {0};
	struct iommu_dev_data *dev_data = dev_iommu_priv_get(dev);
	struct amd_iommu *iommu = get_amd_iommu_from_dev_data(dev_data);
	int ret = 0;

	/*
	 * Needs to make sure PASID is not enabled
	 * for this attach path.
	 */
	if (WARN_ON(dev_data->pasid_enabled))
		return -EINVAL;

	mutex_lock(&dev_data->mutex);

	set_dte_nested(iommu, dom, dev_data, &new);

	amd_iommu_update_dte(iommu, dev_data, &new);

	mutex_unlock(&dev_data->mutex);

	return ret;
}

static void nested_domain_free(struct iommu_domain *dom)
{
	struct guest_domain_mapping_info *curr;
@@ -217,5 +289,6 @@ static void nested_domain_free(struct iommu_domain *dom)
}

static const struct iommu_domain_ops nested_domain_ops = {
	.attach_dev = nested_attach_device,
	.free = nested_domain_free,
};