Commit 556af583 authored by Joerg Roedel's avatar Joerg Roedel
Browse files

Merge branch 'core' into amd/amd-vi

parents e3a682ea a33bf8d8
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -120,8 +120,8 @@ nvkm_device_tegra_probe_iommu(struct nvkm_device_tegra *tdev)
	mutex_init(&tdev->iommu.mutex);

	if (device_iommu_mapped(dev)) {
		tdev->iommu.domain = iommu_domain_alloc(&platform_bus_type);
		if (!tdev->iommu.domain)
		tdev->iommu.domain = iommu_paging_domain_alloc(dev);
		if (IS_ERR(tdev->iommu.domain))
			goto error;

		/*
+7 −1
Original line number Diff line number Diff line
@@ -46,6 +46,7 @@ extern int amd_iommu_gpt_level;
extern unsigned long amd_iommu_pgsize_bitmap;

/* Protection domain ops */
void amd_iommu_init_identity_domain(void);
struct protection_domain *protection_domain_alloc(unsigned int type, int nid);
void protection_domain_free(struct protection_domain *domain);
struct iommu_domain *amd_iommu_domain_alloc_sva(struct device *dev,
@@ -118,9 +119,14 @@ static inline bool check_feature2(u64 mask)
	return (amd_iommu_efr2 & mask);
}

static inline bool amd_iommu_v2_pgtbl_supported(void)
{
	return (check_feature(FEATURE_GIOSUP) && check_feature(FEATURE_GT));
}

static inline bool amd_iommu_gt_ppr_supported(void)
{
	return (check_feature(FEATURE_GT) &&
	return (amd_iommu_v2_pgtbl_supported() &&
		check_feature(FEATURE_PPR) &&
		check_feature(FEATURE_EPHSUP));
}
+10 −8
Original line number Diff line number Diff line
@@ -2070,14 +2070,6 @@ static int __init iommu_init_pci(struct amd_iommu *iommu)

	init_iommu_perf_ctr(iommu);

	if (amd_iommu_pgtable == AMD_IOMMU_V2) {
		if (!check_feature(FEATURE_GIOSUP) ||
		    !check_feature(FEATURE_GT)) {
			pr_warn("Cannot enable v2 page table for DMA-API. Fallback to v1.\n");
			amd_iommu_pgtable = AMD_IOMMU_V1;
		}
	}

	if (is_rd890_iommu(iommu->dev)) {
		int i, j;

@@ -2172,6 +2164,9 @@ static int __init amd_iommu_init_pci(void)
	struct amd_iommu_pci_seg *pci_seg;
	int ret;

	/* Init global identity domain before registering IOMMU */
	amd_iommu_init_identity_domain();

	for_each_iommu(iommu) {
		ret = iommu_init_pci(iommu);
		if (ret) {
@@ -3087,6 +3082,13 @@ static int __init early_amd_iommu_init(void)
	    FIELD_GET(FEATURE_GATS, amd_iommu_efr) == GUEST_PGTABLE_5_LEVEL)
		amd_iommu_gpt_level = PAGE_MODE_5_LEVEL;

	if (amd_iommu_pgtable == AMD_IOMMU_V2) {
		if (!amd_iommu_v2_pgtbl_supported()) {
			pr_warn("Cannot enable v2 page table for DMA-API. Fallback to v1.\n");
			amd_iommu_pgtable = AMD_IOMMU_V1;
		}
	}

	/* Disable any previously enabled IOMMUs */
	if (!is_kdump_kernel() || amd_iommu_disabled)
		disable_iommus();
+98 −43
Original line number Diff line number Diff line
@@ -74,6 +74,9 @@ struct kmem_cache *amd_iommu_irq_cache;

static void detach_device(struct device *dev);

static int amd_iommu_attach_device(struct iommu_domain *dom,
				   struct device *dev);

static void set_dte_entry(struct amd_iommu *iommu,
			  struct iommu_dev_data *dev_data);

@@ -2263,44 +2266,42 @@ void protection_domain_free(struct protection_domain *domain)
	kfree(domain);
}

static void protection_domain_init(struct protection_domain *domain, int nid)
{
	spin_lock_init(&domain->lock);
	INIT_LIST_HEAD(&domain->dev_list);
	INIT_LIST_HEAD(&domain->dev_data_list);
	domain->iop.pgtbl.cfg.amd.nid = nid;
}

struct protection_domain *protection_domain_alloc(unsigned int type, int nid)
{
	struct io_pgtable_ops *pgtbl_ops;
	struct protection_domain *domain;
	int pgtable;

	domain = kzalloc(sizeof(*domain), GFP_KERNEL);
	if (!domain)
		return NULL;

	domain->id = domain_id_alloc();
	if (!domain->id)
		goto err_free;
	if (!domain->id) {
		kfree(domain);
		return NULL;
	}

	spin_lock_init(&domain->lock);
	INIT_LIST_HEAD(&domain->dev_list);
	INIT_LIST_HEAD(&domain->dev_data_list);
	domain->iop.pgtbl.cfg.amd.nid = nid;
	protection_domain_init(domain, nid);

	switch (type) {
	/* No need to allocate io pgtable ops in passthrough mode */
	case IOMMU_DOMAIN_IDENTITY:
	case IOMMU_DOMAIN_SVA:
	return domain;
	case IOMMU_DOMAIN_DMA:
		pgtable = amd_iommu_pgtable;
		break;
	/*
	 * Force IOMMU v1 page table when allocating
	 * domain for pass-through devices.
	 */
	case IOMMU_DOMAIN_UNMANAGED:
		pgtable = AMD_IOMMU_V1;
		break;
	default:
		goto err_id;
}

static int pdom_setup_pgtable(struct protection_domain *domain,
			      unsigned int type, int pgtable)
{
	struct io_pgtable_ops *pgtbl_ops;

	/* No need to allocate io pgtable ops in passthrough mode */
	if (!(type & __IOMMU_DOMAIN_PAGING))
		return 0;

	switch (pgtable) {
	case AMD_IOMMU_V1:
		domain->pd_mode = PD_MODE_V1;
@@ -2309,25 +2310,20 @@ struct protection_domain *protection_domain_alloc(unsigned int type, int nid)
		domain->pd_mode = PD_MODE_V2;
		break;
	default:
		goto err_id;
		return -EINVAL;
	}

	pgtbl_ops =
		alloc_io_pgtable_ops(pgtable, &domain->iop.pgtbl.cfg, domain);
	if (!pgtbl_ops)
		goto err_id;
		return -ENOMEM;

	return domain;
err_id:
	domain_id_free(domain->id);
err_free:
	kfree(domain);
	return NULL;
	return 0;
}

static inline u64 dma_max_address(void)
static inline u64 dma_max_address(int pgtable)
{
	if (amd_iommu_pgtable == AMD_IOMMU_V1)
	if (pgtable == AMD_IOMMU_V1)
		return ~0ULL;

	/* V2 with 4/5 level page table */
@@ -2340,11 +2336,13 @@ static bool amd_iommu_hd_support(struct amd_iommu *iommu)
}

static struct iommu_domain *do_iommu_domain_alloc(unsigned int type,
						  struct device *dev, u32 flags)
						  struct device *dev,
						  u32 flags, int pgtable)
{
	bool dirty_tracking = flags & IOMMU_HWPT_ALLOC_DIRTY_TRACKING;
	struct protection_domain *domain;
	struct amd_iommu *iommu = NULL;
	int ret;

	if (dev)
		iommu = get_amd_iommu_from_dev(dev);
@@ -2356,16 +2354,20 @@ static struct iommu_domain *do_iommu_domain_alloc(unsigned int type,
	if (amd_iommu_snp_en && (type == IOMMU_DOMAIN_IDENTITY))
		return ERR_PTR(-EINVAL);

	if (dirty_tracking && !amd_iommu_hd_support(iommu))
		return ERR_PTR(-EOPNOTSUPP);

	domain = protection_domain_alloc(type,
					 dev ? dev_to_node(dev) : NUMA_NO_NODE);
	if (!domain)
		return ERR_PTR(-ENOMEM);

	ret = pdom_setup_pgtable(domain, type, pgtable);
	if (ret) {
		domain_id_free(domain->id);
		kfree(domain);
		return ERR_PTR(ret);
	}

	domain->domain.geometry.aperture_start = 0;
	domain->domain.geometry.aperture_end   = dma_max_address();
	domain->domain.geometry.aperture_end   = dma_max_address(pgtable);
	domain->domain.geometry.force_aperture = true;
	domain->domain.pgsize_bitmap = domain->iop.pgtbl.cfg.pgsize_bitmap;

@@ -2383,8 +2385,16 @@ static struct iommu_domain *do_iommu_domain_alloc(unsigned int type,
static struct iommu_domain *amd_iommu_domain_alloc(unsigned int type)
{
	struct iommu_domain *domain;
	int pgtable = amd_iommu_pgtable;

	domain = do_iommu_domain_alloc(type, NULL, 0);
	/*
	 * Force IOMMU v1 page table when allocating
	 * domain for pass-through devices.
	 */
	if (type == IOMMU_DOMAIN_UNMANAGED)
		pgtable = AMD_IOMMU_V1;

	domain = do_iommu_domain_alloc(type, NULL, 0, pgtable);
	if (IS_ERR(domain))
		return NULL;

@@ -2398,11 +2408,36 @@ amd_iommu_domain_alloc_user(struct device *dev, u32 flags,

{
	unsigned int type = IOMMU_DOMAIN_UNMANAGED;
	struct amd_iommu *iommu = NULL;
	const u32 supported_flags = IOMMU_HWPT_ALLOC_DIRTY_TRACKING |
						IOMMU_HWPT_ALLOC_PASID;

	if (dev)
		iommu = get_amd_iommu_from_dev(dev);

	if ((flags & ~IOMMU_HWPT_ALLOC_DIRTY_TRACKING) || parent || user_data)
	if ((flags & ~supported_flags) || parent || user_data)
		return ERR_PTR(-EOPNOTSUPP);

	return do_iommu_domain_alloc(type, dev, flags);
	/* Allocate domain with v2 page table if IOMMU supports PASID. */
	if (flags & IOMMU_HWPT_ALLOC_PASID) {
		if (!amd_iommu_pasid_supported())
			return ERR_PTR(-EOPNOTSUPP);

		return do_iommu_domain_alloc(type, dev, flags, AMD_IOMMU_V2);
	}

	/* Allocate domain with v1 page table for dirty tracking */
	if (flags & IOMMU_HWPT_ALLOC_DIRTY_TRACKING) {
		if (iommu && amd_iommu_hd_support(iommu)) {
			return do_iommu_domain_alloc(type, dev,
						     flags, AMD_IOMMU_V1);
		}

		return ERR_PTR(-EOPNOTSUPP);
	}

	/* If nothing specific is required use the kernel commandline default */
	return do_iommu_domain_alloc(type, dev, 0, amd_iommu_pgtable);
}

void amd_iommu_domain_free(struct iommu_domain *dom)
@@ -2444,6 +2479,25 @@ static struct iommu_domain blocked_domain = {
	}
};

static struct protection_domain identity_domain;

static const struct iommu_domain_ops identity_domain_ops = {
	.attach_dev = amd_iommu_attach_device,
};

void amd_iommu_init_identity_domain(void)
{
	struct iommu_domain *domain = &identity_domain.domain;

	domain->type = IOMMU_DOMAIN_IDENTITY;
	domain->ops = &identity_domain_ops;
	domain->owner = &amd_iommu_ops;

	identity_domain.id = domain_id_alloc();

	protection_domain_init(&identity_domain, NUMA_NO_NODE);
}

static int amd_iommu_attach_device(struct iommu_domain *dom,
				   struct device *dev)
{
@@ -2842,6 +2896,7 @@ static int amd_iommu_dev_disable_feature(struct device *dev,
const struct iommu_ops amd_iommu_ops = {
	.capable = amd_iommu_capable,
	.blocked_domain = &blocked_domain,
	.identity_domain = &identity_domain.domain,
	.domain_alloc = amd_iommu_domain_alloc,
	.domain_alloc_user = amd_iommu_domain_alloc_user,
	.domain_alloc_sva = amd_iommu_domain_alloc_sva,
+5 −1
Original line number Diff line number Diff line
@@ -3084,7 +3084,8 @@ arm_smmu_domain_alloc_user(struct device *dev, u32 flags,
			   const struct iommu_user_data *user_data)
{
	struct arm_smmu_master *master = dev_iommu_priv_get(dev);
	const u32 PAGING_FLAGS = IOMMU_HWPT_ALLOC_DIRTY_TRACKING;
	const u32 PAGING_FLAGS = IOMMU_HWPT_ALLOC_DIRTY_TRACKING |
					IOMMU_HWPT_ALLOC_PASID;
	struct arm_smmu_domain *smmu_domain;
	int ret;

@@ -3093,6 +3094,9 @@ arm_smmu_domain_alloc_user(struct device *dev, u32 flags,
	if (parent || user_data)
		return ERR_PTR(-EOPNOTSUPP);

	if (flags & IOMMU_HWPT_ALLOC_PASID)
		return arm_smmu_domain_alloc_paging(dev);

	smmu_domain = arm_smmu_domain_alloc();
	if (IS_ERR(smmu_domain))
		return ERR_CAST(smmu_domain);
Loading