summaryrefslogtreecommitdiffstats
path: root/drivers/iommu/iommufd/hwpt_noiommu.c
blob: a80209a7429a31f6392615908e29814d0de05ae3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES
 */
#include <linux/generic_pt/iommu.h>
#include <linux/iommu.h>
#include "../iommu-pages.h"
#include "iommufd_private.h"

static const struct iommu_domain_ops noiommu_amdv1_ops;

struct noiommu_domain {
	union {
		struct iommu_domain domain;
		struct pt_iommu_amdv1 amdv1;
	};
	spinlock_t lock;
};
PT_IOMMU_CHECK_DOMAIN(struct noiommu_domain, amdv1.iommu, domain);

static void noiommu_change_top(struct pt_iommu *iommu_table,
			       phys_addr_t top_paddr, unsigned int top_level)
{
}

static spinlock_t *noiommu_get_top_lock(struct pt_iommu *iommupt)
{
	struct noiommu_domain *domain =
		container_of(iommupt, struct noiommu_domain, amdv1.iommu);

	return &domain->lock;
}

static const struct pt_iommu_driver_ops noiommu_driver_ops = {
	.get_top_lock = noiommu_get_top_lock,
	.change_top = noiommu_change_top,
};

static struct iommu_domain *
noiommu_alloc_paging_flags(struct device *dev, u32 flags,
			   const struct iommu_user_data *user_data)
{
	struct pt_iommu_amdv1_cfg cfg = {};
	struct noiommu_domain *dom;
	int rc;

	if (flags || user_data)
		return ERR_PTR(-EOPNOTSUPP);

	cfg.common.hw_max_vasz_lg2 = 64;
	cfg.common.hw_max_oasz_lg2 = 52;
	cfg.starting_level = 2;
	cfg.common.features =
		(BIT(PT_FEAT_DYNAMIC_TOP) | BIT(PT_FEAT_AMDV1_ENCRYPT_TABLES) |
		 BIT(PT_FEAT_AMDV1_FORCE_COHERENCE));

	dom = kzalloc_obj(*dom);
	if (!dom)
		return ERR_PTR(-ENOMEM);

	spin_lock_init(&dom->lock);
	dom->amdv1.iommu.nid = NUMA_NO_NODE;
	dom->amdv1.iommu.driver_ops = &noiommu_driver_ops;
	dom->domain.ops = &noiommu_amdv1_ops;

	/* Use SW-only page table which is based on AMDV1 */
	rc = pt_iommu_amdv1_init(&dom->amdv1, &cfg, GFP_KERNEL);
	if (rc) {
		kfree(dom);
		return ERR_PTR(rc);
	}

	return &dom->domain;
}

static void noiommu_domain_free(struct iommu_domain *iommu_domain)
{
	struct noiommu_domain *domain =
		container_of(iommu_domain, struct noiommu_domain, domain);

	pt_iommu_deinit(&domain->amdv1.iommu);
	kfree(domain);
}

static void noiommu_iotlb_sync(struct iommu_domain *domain,
			       struct iommu_iotlb_gather *gather)
{
	iommu_put_pages_list(&gather->freelist);
}

/*
 * Domain ops for iommufd no-IOMMU mode. Uses AMDV1 format as a
 * SW-only IOPT because it has the best multi-page size options
 * of all the formats. IOVAs serve only for IOVA-to-PA lookups,
 * not for hardware DMA translation.
 */
static const struct iommu_domain_ops noiommu_amdv1_ops = {
	IOMMU_PT_DOMAIN_OPS(amdv1),
	.iotlb_sync = noiommu_iotlb_sync,
	.free = noiommu_domain_free,
};

const struct iommu_ops iommufd_noiommu_ops = {
	.domain_alloc_paging_flags = noiommu_alloc_paging_flags,
};