1 | /* SPDX-License-Identifier: GPL-2.0-only */ |
2 | /* Copyright (c) 2021-2022, NVIDIA CORPORATION & AFFILIATES */ |
3 | #ifndef __SELFTEST_IOMMUFD_UTILS |
4 | #define __SELFTEST_IOMMUFD_UTILS |
5 | |
6 | #include <unistd.h> |
7 | #include <stddef.h> |
8 | #include <sys/fcntl.h> |
9 | #include <sys/ioctl.h> |
10 | #include <stdint.h> |
11 | #include <assert.h> |
12 | |
13 | #include "../kselftest_harness.h" |
14 | #include "../../../../drivers/iommu/iommufd/iommufd_test.h" |
15 | |
16 | /* Hack to make assertions more readable */ |
17 | #define _IOMMU_TEST_CMD(x) IOMMU_TEST_CMD |
18 | |
19 | /* Imported from include/asm-generic/bitops/generic-non-atomic.h */ |
20 | #define BITS_PER_BYTE 8 |
21 | #define BITS_PER_LONG __BITS_PER_LONG |
22 | #define BIT_MASK(nr) (1UL << ((nr) % __BITS_PER_LONG)) |
23 | #define BIT_WORD(nr) ((nr) / __BITS_PER_LONG) |
24 | |
25 | static inline void set_bit(unsigned int nr, unsigned long *addr) |
26 | { |
27 | unsigned long mask = BIT_MASK(nr); |
28 | unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); |
29 | |
30 | *p |= mask; |
31 | } |
32 | |
33 | static inline bool test_bit(unsigned int nr, unsigned long *addr) |
34 | { |
35 | return 1UL & (addr[BIT_WORD(nr)] >> (nr & (BITS_PER_LONG - 1))); |
36 | } |
37 | |
38 | static void *buffer; |
39 | static unsigned long BUFFER_SIZE; |
40 | |
41 | static unsigned long PAGE_SIZE; |
42 | |
43 | #define sizeof_field(TYPE, MEMBER) sizeof((((TYPE *)0)->MEMBER)) |
44 | #define offsetofend(TYPE, MEMBER) \ |
45 | (offsetof(TYPE, MEMBER) + sizeof_field(TYPE, MEMBER)) |
46 | |
47 | /* |
48 | * Have the kernel check the refcount on pages. I don't know why a freshly |
49 | * mmap'd anon non-compound page starts out with a ref of 3 |
50 | */ |
51 | #define check_refs(_ptr, _length, _refs) \ |
52 | ({ \ |
53 | struct iommu_test_cmd test_cmd = { \ |
54 | .size = sizeof(test_cmd), \ |
55 | .op = IOMMU_TEST_OP_MD_CHECK_REFS, \ |
56 | .check_refs = { .length = _length, \ |
57 | .uptr = (uintptr_t)(_ptr), \ |
58 | .refs = _refs }, \ |
59 | }; \ |
60 | ASSERT_EQ(0, \ |
61 | ioctl(self->fd, \ |
62 | _IOMMU_TEST_CMD(IOMMU_TEST_OP_MD_CHECK_REFS), \ |
63 | &test_cmd)); \ |
64 | }) |
65 | |
66 | static int _test_cmd_mock_domain(int fd, unsigned int ioas_id, __u32 *stdev_id, |
67 | __u32 *hwpt_id, __u32 *idev_id) |
68 | { |
69 | struct iommu_test_cmd cmd = { |
70 | .size = sizeof(cmd), |
71 | .op = IOMMU_TEST_OP_MOCK_DOMAIN, |
72 | .id = ioas_id, |
73 | .mock_domain = {}, |
74 | }; |
75 | int ret; |
76 | |
77 | ret = ioctl(fd, IOMMU_TEST_CMD, &cmd); |
78 | if (ret) |
79 | return ret; |
80 | if (stdev_id) |
81 | *stdev_id = cmd.mock_domain.out_stdev_id; |
82 | assert(cmd.id != 0); |
83 | if (hwpt_id) |
84 | *hwpt_id = cmd.mock_domain.out_hwpt_id; |
85 | if (idev_id) |
86 | *idev_id = cmd.mock_domain.out_idev_id; |
87 | return 0; |
88 | } |
89 | #define test_cmd_mock_domain(ioas_id, stdev_id, hwpt_id, idev_id) \ |
90 | ASSERT_EQ(0, _test_cmd_mock_domain(self->fd, ioas_id, stdev_id, \ |
91 | hwpt_id, idev_id)) |
92 | #define test_err_mock_domain(_errno, ioas_id, stdev_id, hwpt_id) \ |
93 | EXPECT_ERRNO(_errno, _test_cmd_mock_domain(self->fd, ioas_id, \ |
94 | stdev_id, hwpt_id, NULL)) |
95 | |
96 | static int _test_cmd_mock_domain_flags(int fd, unsigned int ioas_id, |
97 | __u32 stdev_flags, __u32 *stdev_id, |
98 | __u32 *hwpt_id, __u32 *idev_id) |
99 | { |
100 | struct iommu_test_cmd cmd = { |
101 | .size = sizeof(cmd), |
102 | .op = IOMMU_TEST_OP_MOCK_DOMAIN_FLAGS, |
103 | .id = ioas_id, |
104 | .mock_domain_flags = { .dev_flags = stdev_flags }, |
105 | }; |
106 | int ret; |
107 | |
108 | ret = ioctl(fd, IOMMU_TEST_CMD, &cmd); |
109 | if (ret) |
110 | return ret; |
111 | if (stdev_id) |
112 | *stdev_id = cmd.mock_domain_flags.out_stdev_id; |
113 | assert(cmd.id != 0); |
114 | if (hwpt_id) |
115 | *hwpt_id = cmd.mock_domain_flags.out_hwpt_id; |
116 | if (idev_id) |
117 | *idev_id = cmd.mock_domain_flags.out_idev_id; |
118 | return 0; |
119 | } |
120 | #define test_cmd_mock_domain_flags(ioas_id, flags, stdev_id, hwpt_id, idev_id) \ |
121 | ASSERT_EQ(0, _test_cmd_mock_domain_flags(self->fd, ioas_id, flags, \ |
122 | stdev_id, hwpt_id, idev_id)) |
123 | #define test_err_mock_domain_flags(_errno, ioas_id, flags, stdev_id, hwpt_id) \ |
124 | EXPECT_ERRNO(_errno, \ |
125 | _test_cmd_mock_domain_flags(self->fd, ioas_id, flags, \ |
126 | stdev_id, hwpt_id, NULL)) |
127 | |
128 | static int _test_cmd_mock_domain_replace(int fd, __u32 stdev_id, __u32 pt_id, |
129 | __u32 *hwpt_id) |
130 | { |
131 | struct iommu_test_cmd cmd = { |
132 | .size = sizeof(cmd), |
133 | .op = IOMMU_TEST_OP_MOCK_DOMAIN_REPLACE, |
134 | .id = stdev_id, |
135 | .mock_domain_replace = { |
136 | .pt_id = pt_id, |
137 | }, |
138 | }; |
139 | int ret; |
140 | |
141 | ret = ioctl(fd, IOMMU_TEST_CMD, &cmd); |
142 | if (ret) |
143 | return ret; |
144 | if (hwpt_id) |
145 | *hwpt_id = cmd.mock_domain_replace.pt_id; |
146 | return 0; |
147 | } |
148 | |
149 | #define test_cmd_mock_domain_replace(stdev_id, pt_id) \ |
150 | ASSERT_EQ(0, _test_cmd_mock_domain_replace(self->fd, stdev_id, pt_id, \ |
151 | NULL)) |
152 | #define test_err_mock_domain_replace(_errno, stdev_id, pt_id) \ |
153 | EXPECT_ERRNO(_errno, _test_cmd_mock_domain_replace(self->fd, stdev_id, \ |
154 | pt_id, NULL)) |
155 | |
156 | static int _test_cmd_hwpt_alloc(int fd, __u32 device_id, __u32 pt_id, |
157 | __u32 flags, __u32 *hwpt_id, __u32 data_type, |
158 | void *data, size_t data_len) |
159 | { |
160 | struct iommu_hwpt_alloc cmd = { |
161 | .size = sizeof(cmd), |
162 | .flags = flags, |
163 | .dev_id = device_id, |
164 | .pt_id = pt_id, |
165 | .data_type = data_type, |
166 | .data_len = data_len, |
167 | .data_uptr = (uint64_t)data, |
168 | }; |
169 | int ret; |
170 | |
171 | ret = ioctl(fd, IOMMU_HWPT_ALLOC, &cmd); |
172 | if (ret) |
173 | return ret; |
174 | if (hwpt_id) |
175 | *hwpt_id = cmd.out_hwpt_id; |
176 | return 0; |
177 | } |
178 | |
179 | #define test_cmd_hwpt_alloc(device_id, pt_id, flags, hwpt_id) \ |
180 | ASSERT_EQ(0, _test_cmd_hwpt_alloc(self->fd, device_id, pt_id, flags, \ |
181 | hwpt_id, IOMMU_HWPT_DATA_NONE, NULL, \ |
182 | 0)) |
183 | #define test_err_hwpt_alloc(_errno, device_id, pt_id, flags, hwpt_id) \ |
184 | EXPECT_ERRNO(_errno, _test_cmd_hwpt_alloc( \ |
185 | self->fd, device_id, pt_id, flags, \ |
186 | hwpt_id, IOMMU_HWPT_DATA_NONE, NULL, 0)) |
187 | |
188 | #define test_cmd_hwpt_alloc_nested(device_id, pt_id, flags, hwpt_id, \ |
189 | data_type, data, data_len) \ |
190 | ASSERT_EQ(0, _test_cmd_hwpt_alloc(self->fd, device_id, pt_id, flags, \ |
191 | hwpt_id, data_type, data, data_len)) |
192 | #define test_err_hwpt_alloc_nested(_errno, device_id, pt_id, flags, hwpt_id, \ |
193 | data_type, data, data_len) \ |
194 | EXPECT_ERRNO(_errno, \ |
195 | _test_cmd_hwpt_alloc(self->fd, device_id, pt_id, flags, \ |
196 | hwpt_id, data_type, data, data_len)) |
197 | |
198 | #define test_cmd_hwpt_check_iotlb(hwpt_id, iotlb_id, expected) \ |
199 | ({ \ |
200 | struct iommu_test_cmd test_cmd = { \ |
201 | .size = sizeof(test_cmd), \ |
202 | .op = IOMMU_TEST_OP_MD_CHECK_IOTLB, \ |
203 | .id = hwpt_id, \ |
204 | .check_iotlb = { \ |
205 | .id = iotlb_id, \ |
206 | .iotlb = expected, \ |
207 | }, \ |
208 | }; \ |
209 | ASSERT_EQ(0, \ |
210 | ioctl(self->fd, \ |
211 | _IOMMU_TEST_CMD(IOMMU_TEST_OP_MD_CHECK_IOTLB), \ |
212 | &test_cmd)); \ |
213 | }) |
214 | |
215 | #define test_cmd_hwpt_check_iotlb_all(hwpt_id, expected) \ |
216 | ({ \ |
217 | int i; \ |
218 | for (i = 0; i < MOCK_NESTED_DOMAIN_IOTLB_NUM; i++) \ |
219 | test_cmd_hwpt_check_iotlb(hwpt_id, i, expected); \ |
220 | }) |
221 | |
222 | static int _test_cmd_hwpt_invalidate(int fd, __u32 hwpt_id, void *reqs, |
223 | uint32_t data_type, uint32_t lreq, |
224 | uint32_t *nreqs) |
225 | { |
226 | struct iommu_hwpt_invalidate cmd = { |
227 | .size = sizeof(cmd), |
228 | .hwpt_id = hwpt_id, |
229 | .data_type = data_type, |
230 | .data_uptr = (uint64_t)reqs, |
231 | .entry_len = lreq, |
232 | .entry_num = *nreqs, |
233 | }; |
234 | int rc = ioctl(fd, IOMMU_HWPT_INVALIDATE, &cmd); |
235 | *nreqs = cmd.entry_num; |
236 | return rc; |
237 | } |
238 | |
239 | #define test_cmd_hwpt_invalidate(hwpt_id, reqs, data_type, lreq, nreqs) \ |
240 | ({ \ |
241 | ASSERT_EQ(0, \ |
242 | _test_cmd_hwpt_invalidate(self->fd, hwpt_id, reqs, \ |
243 | data_type, lreq, nreqs)); \ |
244 | }) |
245 | #define test_err_hwpt_invalidate(_errno, hwpt_id, reqs, data_type, lreq, \ |
246 | nreqs) \ |
247 | ({ \ |
248 | EXPECT_ERRNO(_errno, _test_cmd_hwpt_invalidate( \ |
249 | self->fd, hwpt_id, reqs, \ |
250 | data_type, lreq, nreqs)); \ |
251 | }) |
252 | |
253 | static int _test_cmd_access_replace_ioas(int fd, __u32 access_id, |
254 | unsigned int ioas_id) |
255 | { |
256 | struct iommu_test_cmd cmd = { |
257 | .size = sizeof(cmd), |
258 | .op = IOMMU_TEST_OP_ACCESS_REPLACE_IOAS, |
259 | .id = access_id, |
260 | .access_replace_ioas = { .ioas_id = ioas_id }, |
261 | }; |
262 | int ret; |
263 | |
264 | ret = ioctl(fd, IOMMU_TEST_CMD, &cmd); |
265 | if (ret) |
266 | return ret; |
267 | return 0; |
268 | } |
269 | #define test_cmd_access_replace_ioas(access_id, ioas_id) \ |
270 | ASSERT_EQ(0, _test_cmd_access_replace_ioas(self->fd, access_id, ioas_id)) |
271 | |
272 | static int _test_cmd_set_dirty_tracking(int fd, __u32 hwpt_id, bool enabled) |
273 | { |
274 | struct iommu_hwpt_set_dirty_tracking cmd = { |
275 | .size = sizeof(cmd), |
276 | .flags = enabled ? IOMMU_HWPT_DIRTY_TRACKING_ENABLE : 0, |
277 | .hwpt_id = hwpt_id, |
278 | }; |
279 | int ret; |
280 | |
281 | ret = ioctl(fd, IOMMU_HWPT_SET_DIRTY_TRACKING, &cmd); |
282 | if (ret) |
283 | return -errno; |
284 | return 0; |
285 | } |
286 | #define test_cmd_set_dirty_tracking(hwpt_id, enabled) \ |
287 | ASSERT_EQ(0, _test_cmd_set_dirty_tracking(self->fd, hwpt_id, enabled)) |
288 | |
289 | static int _test_cmd_get_dirty_bitmap(int fd, __u32 hwpt_id, size_t length, |
290 | __u64 iova, size_t page_size, |
291 | __u64 *bitmap, __u32 flags) |
292 | { |
293 | struct iommu_hwpt_get_dirty_bitmap cmd = { |
294 | .size = sizeof(cmd), |
295 | .hwpt_id = hwpt_id, |
296 | .flags = flags, |
297 | .iova = iova, |
298 | .length = length, |
299 | .page_size = page_size, |
300 | .data = (uintptr_t)bitmap, |
301 | }; |
302 | int ret; |
303 | |
304 | ret = ioctl(fd, IOMMU_HWPT_GET_DIRTY_BITMAP, &cmd); |
305 | if (ret) |
306 | return ret; |
307 | return 0; |
308 | } |
309 | |
310 | #define test_cmd_get_dirty_bitmap(fd, hwpt_id, length, iova, page_size, \ |
311 | bitmap, flags) \ |
312 | ASSERT_EQ(0, _test_cmd_get_dirty_bitmap(fd, hwpt_id, length, iova, \ |
313 | page_size, bitmap, flags)) |
314 | |
315 | static int _test_cmd_mock_domain_set_dirty(int fd, __u32 hwpt_id, size_t length, |
316 | __u64 iova, size_t page_size, |
317 | __u64 *bitmap, __u64 *dirty) |
318 | { |
319 | struct iommu_test_cmd cmd = { |
320 | .size = sizeof(cmd), |
321 | .op = IOMMU_TEST_OP_DIRTY, |
322 | .id = hwpt_id, |
323 | .dirty = { |
324 | .iova = iova, |
325 | .length = length, |
326 | .page_size = page_size, |
327 | .uptr = (uintptr_t)bitmap, |
328 | } |
329 | }; |
330 | int ret; |
331 | |
332 | ret = ioctl(fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_DIRTY), &cmd); |
333 | if (ret) |
334 | return -ret; |
335 | if (dirty) |
336 | *dirty = cmd.dirty.out_nr_dirty; |
337 | return 0; |
338 | } |
339 | |
340 | #define test_cmd_mock_domain_set_dirty(fd, hwpt_id, length, iova, page_size, \ |
341 | bitmap, nr) \ |
342 | ASSERT_EQ(0, \ |
343 | _test_cmd_mock_domain_set_dirty(fd, hwpt_id, length, iova, \ |
344 | page_size, bitmap, nr)) |
345 | |
346 | static int _test_mock_dirty_bitmaps(int fd, __u32 hwpt_id, size_t length, |
347 | __u64 iova, size_t page_size, |
348 | size_t pte_page_size, __u64 *bitmap, |
349 | __u64 bitmap_size, __u32 flags, |
350 | struct __test_metadata *_metadata) |
351 | { |
352 | unsigned long npte = pte_page_size / page_size, pteset = 2 * npte; |
353 | unsigned long nbits = bitmap_size * BITS_PER_BYTE; |
354 | unsigned long j, i, nr = nbits / pteset ?: 1; |
355 | __u64 out_dirty = 0; |
356 | |
357 | /* Mark all even bits as dirty in the mock domain */ |
358 | memset(bitmap, 0, bitmap_size); |
359 | for (i = 0; i < nbits; i += pteset) |
360 | set_bit(nr: i, addr: (unsigned long *)bitmap); |
361 | |
362 | test_cmd_mock_domain_set_dirty(fd, hwpt_id, length, iova, page_size, |
363 | bitmap, &out_dirty); |
364 | ASSERT_EQ(nr, out_dirty); |
365 | |
366 | /* Expect all even bits as dirty in the user bitmap */ |
367 | memset(bitmap, 0, bitmap_size); |
368 | test_cmd_get_dirty_bitmap(fd, hwpt_id, length, iova, page_size, bitmap, |
369 | flags); |
370 | /* Beware ASSERT_EQ() is two statements -- braces are not redundant! */ |
371 | for (i = 0; i < nbits; i += pteset) { |
372 | for (j = 0; j < pteset; j++) { |
373 | ASSERT_EQ(j < npte, |
374 | test_bit(i + j, (unsigned long *)bitmap)); |
375 | } |
376 | ASSERT_EQ(!(i % pteset), test_bit(i, (unsigned long *)bitmap)); |
377 | } |
378 | |
379 | memset(bitmap, 0, bitmap_size); |
380 | test_cmd_get_dirty_bitmap(fd, hwpt_id, length, iova, page_size, bitmap, |
381 | flags); |
382 | |
383 | /* It as read already -- expect all zeroes */ |
384 | for (i = 0; i < nbits; i += pteset) { |
385 | for (j = 0; j < pteset; j++) { |
386 | ASSERT_EQ( |
387 | (j < npte) && |
388 | (flags & |
389 | IOMMU_HWPT_GET_DIRTY_BITMAP_NO_CLEAR), |
390 | test_bit(i + j, (unsigned long *)bitmap)); |
391 | } |
392 | } |
393 | |
394 | return 0; |
395 | } |
396 | #define test_mock_dirty_bitmaps(hwpt_id, length, iova, page_size, pte_size,\ |
397 | bitmap, bitmap_size, flags, _metadata) \ |
398 | ASSERT_EQ(0, _test_mock_dirty_bitmaps(self->fd, hwpt_id, length, iova, \ |
399 | page_size, pte_size, bitmap, \ |
400 | bitmap_size, flags, _metadata)) |
401 | |
402 | static int _test_cmd_create_access(int fd, unsigned int ioas_id, |
403 | __u32 *access_id, unsigned int flags) |
404 | { |
405 | struct iommu_test_cmd cmd = { |
406 | .size = sizeof(cmd), |
407 | .op = IOMMU_TEST_OP_CREATE_ACCESS, |
408 | .id = ioas_id, |
409 | .create_access = { .flags = flags }, |
410 | }; |
411 | int ret; |
412 | |
413 | ret = ioctl(fd, IOMMU_TEST_CMD, &cmd); |
414 | if (ret) |
415 | return ret; |
416 | *access_id = cmd.create_access.out_access_fd; |
417 | return 0; |
418 | } |
419 | #define test_cmd_create_access(ioas_id, access_id, flags) \ |
420 | ASSERT_EQ(0, _test_cmd_create_access(self->fd, ioas_id, access_id, \ |
421 | flags)) |
422 | |
423 | static int _test_cmd_destroy_access(unsigned int access_id) |
424 | { |
425 | return close(access_id); |
426 | } |
427 | #define test_cmd_destroy_access(access_id) \ |
428 | ASSERT_EQ(0, _test_cmd_destroy_access(access_id)) |
429 | |
430 | static int _test_cmd_destroy_access_pages(int fd, unsigned int access_id, |
431 | unsigned int access_pages_id) |
432 | { |
433 | struct iommu_test_cmd cmd = { |
434 | .size = sizeof(cmd), |
435 | .op = IOMMU_TEST_OP_DESTROY_ACCESS_PAGES, |
436 | .id = access_id, |
437 | .destroy_access_pages = { .access_pages_id = access_pages_id }, |
438 | }; |
439 | return ioctl(fd, IOMMU_TEST_CMD, &cmd); |
440 | } |
441 | #define test_cmd_destroy_access_pages(access_id, access_pages_id) \ |
442 | ASSERT_EQ(0, _test_cmd_destroy_access_pages(self->fd, access_id, \ |
443 | access_pages_id)) |
444 | #define test_err_destroy_access_pages(_errno, access_id, access_pages_id) \ |
445 | EXPECT_ERRNO(_errno, _test_cmd_destroy_access_pages( \ |
446 | self->fd, access_id, access_pages_id)) |
447 | |
448 | static int _test_ioctl_destroy(int fd, unsigned int id) |
449 | { |
450 | struct iommu_destroy cmd = { |
451 | .size = sizeof(cmd), |
452 | .id = id, |
453 | }; |
454 | return ioctl(fd, IOMMU_DESTROY, &cmd); |
455 | } |
456 | #define test_ioctl_destroy(id) ASSERT_EQ(0, _test_ioctl_destroy(self->fd, id)) |
457 | |
458 | static int _test_ioctl_ioas_alloc(int fd, __u32 *id) |
459 | { |
460 | struct iommu_ioas_alloc cmd = { |
461 | .size = sizeof(cmd), |
462 | }; |
463 | int ret; |
464 | |
465 | ret = ioctl(fd, IOMMU_IOAS_ALLOC, &cmd); |
466 | if (ret) |
467 | return ret; |
468 | *id = cmd.out_ioas_id; |
469 | return 0; |
470 | } |
471 | #define test_ioctl_ioas_alloc(id) \ |
472 | ({ \ |
473 | ASSERT_EQ(0, _test_ioctl_ioas_alloc(self->fd, id)); \ |
474 | ASSERT_NE(0, *(id)); \ |
475 | }) |
476 | |
477 | static int _test_ioctl_ioas_map(int fd, unsigned int ioas_id, void *buffer, |
478 | size_t length, __u64 *iova, unsigned int flags) |
479 | { |
480 | struct iommu_ioas_map cmd = { |
481 | .size = sizeof(cmd), |
482 | .flags = flags, |
483 | .ioas_id = ioas_id, |
484 | .user_va = (uintptr_t)buffer, |
485 | .length = length, |
486 | }; |
487 | int ret; |
488 | |
489 | if (flags & IOMMU_IOAS_MAP_FIXED_IOVA) |
490 | cmd.iova = *iova; |
491 | |
492 | ret = ioctl(fd, IOMMU_IOAS_MAP, &cmd); |
493 | *iova = cmd.iova; |
494 | return ret; |
495 | } |
496 | #define test_ioctl_ioas_map(buffer, length, iova_p) \ |
497 | ASSERT_EQ(0, _test_ioctl_ioas_map(self->fd, self->ioas_id, buffer, \ |
498 | length, iova_p, \ |
499 | IOMMU_IOAS_MAP_WRITEABLE | \ |
500 | IOMMU_IOAS_MAP_READABLE)) |
501 | |
502 | #define test_err_ioctl_ioas_map(_errno, buffer, length, iova_p) \ |
503 | EXPECT_ERRNO(_errno, \ |
504 | _test_ioctl_ioas_map(self->fd, self->ioas_id, buffer, \ |
505 | length, iova_p, \ |
506 | IOMMU_IOAS_MAP_WRITEABLE | \ |
507 | IOMMU_IOAS_MAP_READABLE)) |
508 | |
509 | #define test_ioctl_ioas_map_id(ioas_id, buffer, length, iova_p) \ |
510 | ASSERT_EQ(0, _test_ioctl_ioas_map(self->fd, ioas_id, buffer, length, \ |
511 | iova_p, \ |
512 | IOMMU_IOAS_MAP_WRITEABLE | \ |
513 | IOMMU_IOAS_MAP_READABLE)) |
514 | |
515 | #define test_ioctl_ioas_map_fixed(buffer, length, iova) \ |
516 | ({ \ |
517 | __u64 __iova = iova; \ |
518 | ASSERT_EQ(0, _test_ioctl_ioas_map( \ |
519 | self->fd, self->ioas_id, buffer, length, \ |
520 | &__iova, \ |
521 | IOMMU_IOAS_MAP_FIXED_IOVA | \ |
522 | IOMMU_IOAS_MAP_WRITEABLE | \ |
523 | IOMMU_IOAS_MAP_READABLE)); \ |
524 | }) |
525 | |
526 | #define test_ioctl_ioas_map_fixed_id(ioas_id, buffer, length, iova) \ |
527 | ({ \ |
528 | __u64 __iova = iova; \ |
529 | ASSERT_EQ(0, \ |
530 | _test_ioctl_ioas_map( \ |
531 | self->fd, ioas_id, buffer, length, &__iova, \ |
532 | IOMMU_IOAS_MAP_FIXED_IOVA | \ |
533 | IOMMU_IOAS_MAP_WRITEABLE | \ |
534 | IOMMU_IOAS_MAP_READABLE)); \ |
535 | }) |
536 | |
537 | #define test_err_ioctl_ioas_map_fixed(_errno, buffer, length, iova) \ |
538 | ({ \ |
539 | __u64 __iova = iova; \ |
540 | EXPECT_ERRNO(_errno, \ |
541 | _test_ioctl_ioas_map( \ |
542 | self->fd, self->ioas_id, buffer, length, \ |
543 | &__iova, \ |
544 | IOMMU_IOAS_MAP_FIXED_IOVA | \ |
545 | IOMMU_IOAS_MAP_WRITEABLE | \ |
546 | IOMMU_IOAS_MAP_READABLE)); \ |
547 | }) |
548 | |
549 | static int _test_ioctl_ioas_unmap(int fd, unsigned int ioas_id, uint64_t iova, |
550 | size_t length, uint64_t *out_len) |
551 | { |
552 | struct iommu_ioas_unmap cmd = { |
553 | .size = sizeof(cmd), |
554 | .ioas_id = ioas_id, |
555 | .iova = iova, |
556 | .length = length, |
557 | }; |
558 | int ret; |
559 | |
560 | ret = ioctl(fd, IOMMU_IOAS_UNMAP, &cmd); |
561 | if (out_len) |
562 | *out_len = cmd.length; |
563 | return ret; |
564 | } |
565 | #define test_ioctl_ioas_unmap(iova, length) \ |
566 | ASSERT_EQ(0, _test_ioctl_ioas_unmap(self->fd, self->ioas_id, iova, \ |
567 | length, NULL)) |
568 | |
569 | #define test_ioctl_ioas_unmap_id(ioas_id, iova, length) \ |
570 | ASSERT_EQ(0, _test_ioctl_ioas_unmap(self->fd, ioas_id, iova, length, \ |
571 | NULL)) |
572 | |
573 | #define test_err_ioctl_ioas_unmap(_errno, iova, length) \ |
574 | EXPECT_ERRNO(_errno, _test_ioctl_ioas_unmap(self->fd, self->ioas_id, \ |
575 | iova, length, NULL)) |
576 | |
577 | static int _test_ioctl_set_temp_memory_limit(int fd, unsigned int limit) |
578 | { |
579 | struct iommu_test_cmd memlimit_cmd = { |
580 | .size = sizeof(memlimit_cmd), |
581 | .op = IOMMU_TEST_OP_SET_TEMP_MEMORY_LIMIT, |
582 | .memory_limit = { .limit = limit }, |
583 | }; |
584 | |
585 | return ioctl(fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_SET_TEMP_MEMORY_LIMIT), |
586 | &memlimit_cmd); |
587 | } |
588 | |
589 | #define test_ioctl_set_temp_memory_limit(limit) \ |
590 | ASSERT_EQ(0, _test_ioctl_set_temp_memory_limit(self->fd, limit)) |
591 | |
592 | #define test_ioctl_set_default_memory_limit() \ |
593 | test_ioctl_set_temp_memory_limit(65536) |
594 | |
595 | static void teardown_iommufd(int fd, struct __test_metadata *_metadata) |
596 | { |
597 | struct iommu_test_cmd test_cmd = { |
598 | .size = sizeof(test_cmd), |
599 | .op = IOMMU_TEST_OP_MD_CHECK_REFS, |
600 | .check_refs = { .length = BUFFER_SIZE, |
601 | .uptr = (uintptr_t)buffer }, |
602 | }; |
603 | |
604 | if (fd == -1) |
605 | return; |
606 | |
607 | EXPECT_EQ(0, close(fd)); |
608 | |
609 | fd = open("/dev/iommu" , O_RDWR); |
610 | EXPECT_NE(-1, fd); |
611 | EXPECT_EQ(0, ioctl(fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_MD_CHECK_REFS), |
612 | &test_cmd)); |
613 | EXPECT_EQ(0, close(fd)); |
614 | } |
615 | |
616 | #define EXPECT_ERRNO(expected_errno, cmd) \ |
617 | ({ \ |
618 | ASSERT_EQ(-1, cmd); \ |
619 | EXPECT_EQ(expected_errno, errno); \ |
620 | }) |
621 | |
622 | #endif |
623 | |
624 | /* @data can be NULL */ |
625 | static int _test_cmd_get_hw_info(int fd, __u32 device_id, void *data, |
626 | size_t data_len, uint32_t *capabilities) |
627 | { |
628 | struct iommu_test_hw_info *info = (struct iommu_test_hw_info *)data; |
629 | struct iommu_hw_info cmd = { |
630 | .size = sizeof(cmd), |
631 | .dev_id = device_id, |
632 | .data_len = data_len, |
633 | .data_uptr = (uint64_t)data, |
634 | .out_capabilities = 0, |
635 | }; |
636 | int ret; |
637 | |
638 | ret = ioctl(fd, IOMMU_GET_HW_INFO, &cmd); |
639 | if (ret) |
640 | return ret; |
641 | |
642 | assert(cmd.out_data_type == IOMMU_HW_INFO_TYPE_SELFTEST); |
643 | |
644 | /* |
645 | * The struct iommu_test_hw_info should be the one defined |
646 | * by the current kernel. |
647 | */ |
648 | assert(cmd.data_len == sizeof(struct iommu_test_hw_info)); |
649 | |
650 | /* |
651 | * Trailing bytes should be 0 if user buffer is larger than |
652 | * the data that kernel reports. |
653 | */ |
654 | if (data_len > cmd.data_len) { |
655 | char *ptr = (char *)(data + cmd.data_len); |
656 | int idx = 0; |
657 | |
658 | while (idx < data_len - cmd.data_len) { |
659 | assert(!*(ptr + idx)); |
660 | idx++; |
661 | } |
662 | } |
663 | |
664 | if (info) { |
665 | if (data_len >= offsetofend(struct iommu_test_hw_info, test_reg)) |
666 | assert(info->test_reg == IOMMU_HW_INFO_SELFTEST_REGVAL); |
667 | if (data_len >= offsetofend(struct iommu_test_hw_info, flags)) |
668 | assert(!info->flags); |
669 | } |
670 | |
671 | if (capabilities) |
672 | *capabilities = cmd.out_capabilities; |
673 | |
674 | return 0; |
675 | } |
676 | |
677 | #define test_cmd_get_hw_info(device_id, data, data_len) \ |
678 | ASSERT_EQ(0, _test_cmd_get_hw_info(self->fd, device_id, data, \ |
679 | data_len, NULL)) |
680 | |
681 | #define test_err_get_hw_info(_errno, device_id, data, data_len) \ |
682 | EXPECT_ERRNO(_errno, _test_cmd_get_hw_info(self->fd, device_id, data, \ |
683 | data_len, NULL)) |
684 | |
685 | #define test_cmd_get_hw_capabilities(device_id, caps, mask) \ |
686 | ASSERT_EQ(0, _test_cmd_get_hw_info(self->fd, device_id, NULL, 0, &caps)) |
687 | |