summaryrefslogtreecommitdiff
path: root/include/linux/iommufd.h
blob: 6e7efe83bc5d83e8d4f5cede9a680860bc5fed9b (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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (C) 2021 Intel Corporation
 * Copyright (c) 2021-2022, NVIDIA CORPORATION & AFFILIATES
 */
#ifndef __LINUX_IOMMUFD_H
#define __LINUX_IOMMUFD_H

#include <linux/err.h>
#include <linux/errno.h>
#include <linux/iommu.h>
#include <linux/refcount.h>
#include <linux/types.h>
#include <linux/xarray.h>
#include <uapi/linux/iommufd.h>

struct device;
struct file;
struct iommu_group;
struct iommu_user_data;
struct iommu_user_data_array;
struct iommufd_access;
struct iommufd_ctx;
struct iommufd_device;
struct iommufd_viommu_ops;
struct page;

enum iommufd_object_type {
	IOMMUFD_OBJ_NONE,
	IOMMUFD_OBJ_ANY = IOMMUFD_OBJ_NONE,
	IOMMUFD_OBJ_DEVICE,
	IOMMUFD_OBJ_HWPT_PAGING,
	IOMMUFD_OBJ_HWPT_NESTED,
	IOMMUFD_OBJ_IOAS,
	IOMMUFD_OBJ_ACCESS,
	IOMMUFD_OBJ_FAULT,
	IOMMUFD_OBJ_VIOMMU,
	IOMMUFD_OBJ_VDEVICE,
	IOMMUFD_OBJ_VEVENTQ,
	IOMMUFD_OBJ_HW_QUEUE,
#ifdef CONFIG_IOMMUFD_TEST
	IOMMUFD_OBJ_SELFTEST,
#endif
	IOMMUFD_OBJ_MAX,
};

/* Base struct for all objects with a userspace ID handle. */
struct iommufd_object {
	/*
	 * Destroy will sleep and wait for wait_cnt to go to zero. This allows
	 * concurrent users of the ID to reliably avoid causing a spurious
	 * destroy failure. Incrementing this count should either be short
	 * lived or be revoked and blocked during pre_destroy().
	 */
	refcount_t wait_cnt;
	refcount_t users;
	enum iommufd_object_type type;
	unsigned int id;
};

struct iommufd_device *iommufd_device_bind(struct iommufd_ctx *ictx,
					   struct device *dev, u32 *id);
void iommufd_device_unbind(struct iommufd_device *idev);

int iommufd_device_attach(struct iommufd_device *idev, ioasid_t pasid,
			  u32 *pt_id);
int iommufd_device_replace(struct iommufd_device *idev, ioasid_t pasid,
			   u32 *pt_id);
void iommufd_device_detach(struct iommufd_device *idev, ioasid_t pasid);

struct iommufd_ctx *iommufd_device_to_ictx(struct iommufd_device *idev);
u32 iommufd_device_to_id(struct iommufd_device *idev);

struct iommufd_access_ops {
	u8 needs_pin_pages : 1;
	void (*unmap)(void *data, unsigned long iova, unsigned long length);
};

enum {
	IOMMUFD_ACCESS_RW_READ = 0,
	IOMMUFD_ACCESS_RW_WRITE = 1 << 0,
	/* Set if the caller is in a kthread then rw will use kthread_use_mm() */
	IOMMUFD_ACCESS_RW_KTHREAD = 1 << 1,

	/* Only for use by selftest */
	__IOMMUFD_ACCESS_RW_SLOW_PATH = 1 << 2,
};

struct iommufd_access *
iommufd_access_create(struct iommufd_ctx *ictx,
		      const struct iommufd_access_ops *ops, void *data, u32 *id);
void iommufd_access_destroy(struct iommufd_access *access);
int iommufd_access_attach(struct iommufd_access *access, u32 ioas_id);
int iommufd_access_replace(struct iommufd_access *access, u32 ioas_id);
void iommufd_access_detach(struct iommufd_access *access);

void iommufd_ctx_get(struct iommufd_ctx *ictx);

struct iommufd_viommu {
	struct iommufd_object obj;
	struct iommufd_ctx *ictx;
	struct iommu_device *iommu_dev;
	struct iommufd_hwpt_paging *hwpt;

	const struct iommufd_viommu_ops *ops;

	struct xarray vdevs;
	struct list_head veventqs;
	struct rw_semaphore veventqs_rwsem;

	enum iommu_viommu_type type;
};

struct iommufd_vdevice {
	struct iommufd_object obj;
	struct iommufd_viommu *viommu;
	struct iommufd_device *idev;

	/*
	 * Virtual device ID per vIOMMU, e.g. vSID of ARM SMMUv3, vDeviceID of
	 * AMD IOMMU, and vRID of Intel VT-d
	 */
	u64 virt_id;

	/* Clean up all driver-specific parts of an iommufd_vdevice */
	void (*destroy)(struct iommufd_vdevice *vdev);
};

struct iommufd_hw_queue {
	struct iommufd_object obj;
	struct iommufd_viommu *viommu;
	struct iommufd_access *access;

	u64 base_addr; /* in guest physical address space */
	size_t length;

	enum iommu_hw_queue_type type;

	/* Clean up all driver-specific parts of an iommufd_hw_queue */
	void (*destroy)(struct iommufd_hw_queue *hw_queue);
};

/**
 * struct iommufd_viommu_ops - vIOMMU specific operations
 * @destroy: Clean up all driver-specific parts of an iommufd_viommu. The memory
 *           of the vIOMMU will be free-ed by iommufd core after calling this op
 * @alloc_domain_nested: Allocate a IOMMU_DOMAIN_NESTED on a vIOMMU that holds a
 *                       nesting parent domain (IOMMU_DOMAIN_PAGING). @user_data
 *                       must be defined in include/uapi/linux/iommufd.h.
 *                       It must fully initialize the new iommu_domain before
 *                       returning. Upon failure, ERR_PTR must be returned.
 * @cache_invalidate: Flush hardware cache used by a vIOMMU. It can be used for
 *                    any IOMMU hardware specific cache: TLB and device cache.
 *                    The @array passes in the cache invalidation requests, in
 *                    form of a driver data structure. A driver must update the
 *                    array->entry_num to report the number of handled requests.
 *                    The data structure of the array entry must be defined in
 *                    include/uapi/linux/iommufd.h
 * @vdevice_size: Size of the driver-defined vDEVICE structure per this vIOMMU
 * @vdevice_init: Initialize the driver-level structure of a vDEVICE object, or
 *                related HW procedure. @vdev is already initialized by iommufd
 *                core: vdev->dev and vdev->viommu pointers; vdev->id carries a
 *                per-vIOMMU virtual ID (refer to struct iommu_vdevice_alloc in
 *                include/uapi/linux/iommufd.h)
 *                If driver has a deinit function to revert what vdevice_init op
 *                does, it should set it to the @vdev->destroy function pointer
 * @get_hw_queue_size: Get the size of a driver-defined HW queue structure for a
 *                     given @viommu corresponding to @queue_type. Driver should
 *                     return 0 if HW queue aren't supported accordingly. It is
 *                     required for driver to use the HW_QUEUE_STRUCT_SIZE macro
 *                     to sanitize the driver-level HW queue structure related
 *                     to the core one
 * @hw_queue_init_phys: Initialize the driver-level structure of a HW queue that
 *                      is initialized with its core-level structure that holds
 *                      all the info about a guest queue memory.
 *                      Driver providing this op indicates that HW accesses the
 *                      guest queue memory via physical addresses.
 *                      @index carries the logical HW QUEUE ID per vIOMMU in a
 *                      guest VM, for a multi-queue model. @base_addr_pa carries
 *                      the physical location of the guest queue
 *                      If driver has a deinit function to revert what this op
 *                      does, it should set it to the @hw_queue->destroy pointer
 */
struct iommufd_viommu_ops {
	void (*destroy)(struct iommufd_viommu *viommu);
	struct iommu_domain *(*alloc_domain_nested)(
		struct iommufd_viommu *viommu, u32 flags,
		const struct iommu_user_data *user_data);
	int (*cache_invalidate)(struct iommufd_viommu *viommu,
				struct iommu_user_data_array *array);
	const size_t vdevice_size;
	int (*vdevice_init)(struct iommufd_vdevice *vdev);
	size_t (*get_hw_queue_size)(struct iommufd_viommu *viommu,
				    enum iommu_hw_queue_type queue_type);
	/* AMD's HW will add hw_queue_init simply using @hw_queue->base_addr */
	int (*hw_queue_init_phys)(struct iommufd_hw_queue *hw_queue, u32 index,
				  phys_addr_t base_addr_pa);
};

#if IS_ENABLED(CONFIG_IOMMUFD)
struct iommufd_ctx *iommufd_ctx_from_file(struct file *file);
struct iommufd_ctx *iommufd_ctx_from_fd(int fd);
void iommufd_ctx_put(struct iommufd_ctx *ictx);
bool iommufd_ctx_has_group(struct iommufd_ctx *ictx, struct iommu_group *group);

int iommufd_access_pin_pages(struct iommufd_access *access, unsigned long iova,
			     unsigned long length, struct page **out_pages,
			     unsigned int flags);
void iommufd_access_unpin_pages(struct iommufd_access *access,
				unsigned long iova, unsigned long length);
int iommufd_access_rw(struct iommufd_access *access, unsigned long iova,
		      void *data, size_t len, unsigned int flags);
int iommufd_vfio_compat_ioas_get_id(struct iommufd_ctx *ictx, u32 *out_ioas_id);
int iommufd_vfio_compat_ioas_create(struct iommufd_ctx *ictx);
int iommufd_vfio_compat_set_no_iommu(struct iommufd_ctx *ictx);
#else /* !CONFIG_IOMMUFD */
static inline struct iommufd_ctx *iommufd_ctx_from_file(struct file *file)
{
	return ERR_PTR(-EOPNOTSUPP);
}

static inline void iommufd_ctx_put(struct iommufd_ctx *ictx)
{
}

static inline int iommufd_access_pin_pages(struct iommufd_access *access,
					   unsigned long iova,
					   unsigned long length,
					   struct page **out_pages,
					   unsigned int flags)
{
	return -EOPNOTSUPP;
}

static inline void iommufd_access_unpin_pages(struct iommufd_access *access,
					      unsigned long iova,
					      unsigned long length)
{
}

static inline int iommufd_access_rw(struct iommufd_access *access,
				    unsigned long iova, void *data, size_t len,
				    unsigned int flags)
{
	return -EOPNOTSUPP;
}

static inline int iommufd_vfio_compat_ioas_create(struct iommufd_ctx *ictx)
{
	return -EOPNOTSUPP;
}

static inline int iommufd_vfio_compat_set_no_iommu(struct iommufd_ctx *ictx)
{
	return -EOPNOTSUPP;
}
#endif /* CONFIG_IOMMUFD */

#if IS_ENABLED(CONFIG_IOMMUFD_DRIVER_CORE)
int _iommufd_object_depend(struct iommufd_object *obj_dependent,
			   struct iommufd_object *obj_depended);
void _iommufd_object_undepend(struct iommufd_object *obj_dependent,
			      struct iommufd_object *obj_depended);
int _iommufd_alloc_mmap(struct iommufd_ctx *ictx, struct iommufd_object *owner,
			phys_addr_t mmio_addr, size_t length,
			unsigned long *offset);
void _iommufd_destroy_mmap(struct iommufd_ctx *ictx,
			   struct iommufd_object *owner, unsigned long offset);
struct device *iommufd_vdevice_to_device(struct iommufd_vdevice *vdev);
struct device *iommufd_viommu_find_dev(struct iommufd_viommu *viommu,
				       unsigned long vdev_id);
int iommufd_viommu_get_vdev_id(struct iommufd_viommu *viommu,
			       struct device *dev, unsigned long *vdev_id);
int iommufd_viommu_report_event(struct iommufd_viommu *viommu,
				enum iommu_veventq_type type, void *event_data,
				size_t data_len);
#else /* !CONFIG_IOMMUFD_DRIVER_CORE */
static inline int _iommufd_object_depend(struct iommufd_object *obj_dependent,
					 struct iommufd_object *obj_depended)
{
	return -EOPNOTSUPP;
}

static inline void
_iommufd_object_undepend(struct iommufd_object *obj_dependent,
			 struct iommufd_object *obj_depended)
{
}

static inline int _iommufd_alloc_mmap(struct iommufd_ctx *ictx,
				      struct iommufd_object *owner,
				      phys_addr_t mmio_addr, size_t length,
				      unsigned long *offset)
{
	return -EOPNOTSUPP;
}

static inline void _iommufd_destroy_mmap(struct iommufd_ctx *ictx,
					 struct iommufd_object *owner,
					 unsigned long offset)
{
}

static inline struct device *
iommufd_vdevice_to_device(struct iommufd_vdevice *vdev)
{
	return NULL;
}

static inline struct device *
iommufd_viommu_find_dev(struct iommufd_viommu *viommu, unsigned long vdev_id)
{
	return NULL;
}

static inline int iommufd_viommu_get_vdev_id(struct iommufd_viommu *viommu,
					     struct device *dev,
					     unsigned long *vdev_id)
{
	return -ENOENT;
}

static inline int iommufd_viommu_report_event(struct iommufd_viommu *viommu,
					      enum iommu_veventq_type type,
					      void *event_data, size_t data_len)
{
	return -EOPNOTSUPP;
}
#endif /* CONFIG_IOMMUFD_DRIVER_CORE */

#define VIOMMU_STRUCT_SIZE(drv_struct, member)                                 \
	(sizeof(drv_struct) +                                                  \
	 BUILD_BUG_ON_ZERO(offsetof(drv_struct, member)) +                     \
	 BUILD_BUG_ON_ZERO(!__same_type(struct iommufd_viommu,                 \
					((drv_struct *)NULL)->member)))

#define VDEVICE_STRUCT_SIZE(drv_struct, member)                                \
	(sizeof(drv_struct) +                                                  \
	 BUILD_BUG_ON_ZERO(offsetof(drv_struct, member)) +                     \
	 BUILD_BUG_ON_ZERO(!__same_type(struct iommufd_vdevice,                \
					((drv_struct *)NULL)->member)))

#define HW_QUEUE_STRUCT_SIZE(drv_struct, member)                               \
	(sizeof(drv_struct) +                                                  \
	 BUILD_BUG_ON_ZERO(offsetof(drv_struct, member)) +                     \
	 BUILD_BUG_ON_ZERO(!__same_type(struct iommufd_hw_queue,               \
					((drv_struct *)NULL)->member)))

/*
 * Helpers for IOMMU driver to build/destroy a dependency between two sibling
 * structures created by one of the allocators above
 */
#define iommufd_hw_queue_depend(dependent, depended, member)                   \
	({                                                                     \
		int ret = -EINVAL;                                             \
									       \
		static_assert(__same_type(struct iommufd_hw_queue,             \
					  dependent->member));                 \
		static_assert(__same_type(typeof(*dependent), *depended));     \
		if (!WARN_ON_ONCE(dependent->member.viommu !=                  \
				  depended->member.viommu))                    \
			ret = _iommufd_object_depend(&dependent->member.obj,   \
						     &depended->member.obj);   \
		ret;                                                           \
	})

#define iommufd_hw_queue_undepend(dependent, depended, member)                 \
	({                                                                     \
		static_assert(__same_type(struct iommufd_hw_queue,             \
					  dependent->member));                 \
		static_assert(__same_type(typeof(*dependent), *depended));     \
		WARN_ON_ONCE(dependent->member.viommu !=                       \
			     depended->member.viommu);                         \
		_iommufd_object_undepend(&dependent->member.obj,               \
					 &depended->member.obj);               \
	})

/*
 * Helpers for IOMMU driver to alloc/destroy an mmapable area for a structure.
 *
 * To support an mmappable MMIO region, kernel driver must first register it to
 * iommufd core to allocate an @offset, during a driver-structure initialization
 * (e.g. viommu_init op). Then, it should report to user space this @offset and
 * the @length of the MMIO region for mmap syscall.
 */
static inline int iommufd_viommu_alloc_mmap(struct iommufd_viommu *viommu,
					    phys_addr_t mmio_addr,
					    size_t length,
					    unsigned long *offset)
{
	return _iommufd_alloc_mmap(viommu->ictx, &viommu->obj, mmio_addr,
				   length, offset);
}

static inline void iommufd_viommu_destroy_mmap(struct iommufd_viommu *viommu,
					       unsigned long offset)
{
	_iommufd_destroy_mmap(viommu->ictx, &viommu->obj, offset);
}
#endif