1 /*
2  * Copyright © 2015 Intel Corporation
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21  * IN THE SOFTWARE.
22  *
23  * Authors:
24  *    Daniel Vetter <daniel.vetter@ffwll.ch>
25  */
26 
27 #include <fcntl.h>
28 #include <limits.h>
29 
30 #include "igt.h"
31 #include "i915/gem_vm.h"
32 
33 IGT_TEST_DESCRIPTION("Basic test for context set/get param input validation.");
34 
35 #define NEW_CTX	BIT(0)
36 #define USER BIT(1)
37 
set_priority(int i915)38 static void set_priority(int i915)
39 {
40 	static const int64_t test_values[] = {
41 		/* Test space too big, pick significant values */
42 		INT_MIN,
43 
44 		I915_CONTEXT_MIN_USER_PRIORITY - 1,
45 		I915_CONTEXT_MIN_USER_PRIORITY,
46 		I915_CONTEXT_MIN_USER_PRIORITY + 1,
47 
48 		I915_CONTEXT_DEFAULT_PRIORITY - 1,
49 		I915_CONTEXT_DEFAULT_PRIORITY,
50 		I915_CONTEXT_DEFAULT_PRIORITY + 1,
51 
52 		I915_CONTEXT_MAX_USER_PRIORITY - 1,
53 		I915_CONTEXT_MAX_USER_PRIORITY,
54 		I915_CONTEXT_MAX_USER_PRIORITY + 1,
55 
56 		INT_MAX
57 	};
58 	unsigned int size;
59 	int64_t *values;
60 
61 	igt_require(getuid() == 0);
62 
63 	size = ARRAY_SIZE(test_values);
64 	values = malloc(sizeof(test_values) * 8);
65 	igt_assert(values);
66 
67 	for (unsigned i = 0; i < size; i++) {
68 		values[i + 0*size] = test_values[i];
69 		values[i + 1*size] = test_values[i] | (uint64_t)1 << 32;
70 		values[i + 2*size] = test_values[i] | (uint64_t)rand() << 32;
71 		values[i + 3*size] = test_values[i] ^ rand();
72 		values[i + 4*size] = rand() % (I915_CONTEXT_MAX_USER_PRIORITY - I915_CONTEXT_MIN_USER_PRIORITY) + I915_CONTEXT_MIN_USER_PRIORITY;
73 		values[i + 5*size] = rand();
74 		values[i + 6*size] = rand() | (uint64_t)rand() << 32;
75 		values[i + 7*size] = (uint64_t)test_values[i] << 32;
76 	}
77 	size *= 8;
78 
79 	igt_permute_array(values, size, igt_exchange_int64);
80 
81 	igt_fork(flags, NEW_CTX | USER) {
82 		int fd = gem_reopen_driver(i915);
83 		struct drm_i915_gem_context_param arg = {
84 			.param = I915_CONTEXT_PARAM_PRIORITY,
85 			.ctx_id = flags & NEW_CTX ? gem_context_create(fd) : 0,
86 		};
87 		int64_t old_prio;
88 
89 		if (flags & USER) {
90 			igt_debug("Dropping root privilege\n");
91 			igt_drop_root();
92 		}
93 
94 		gem_context_get_param(fd, &arg);
95 		old_prio = arg.value;
96 
97 		for (unsigned i = 0; i < size; i++) {
98 			int64_t prio = values[i];
99 			int expected = 0;
100 			int err;
101 
102 			arg.value = prio;
103 
104 			if (flags & USER &&
105 			    prio > I915_CONTEXT_DEFAULT_PRIORITY)
106 				expected = -EPERM;
107 
108 			if (prio < I915_CONTEXT_MIN_USER_PRIORITY ||
109 			    prio > I915_CONTEXT_MAX_USER_PRIORITY)
110 				expected = -EINVAL;
111 
112 			err =__gem_context_set_param(fd, &arg);
113 			igt_assert_f(err == expected,
114 				     "Priority requested %" PRId64 " with flags %x, expected result %d, returned %d\n",
115 				     prio, flags, expected, err);
116 
117 			gem_context_get_param(fd, &arg);
118 			if (!err)
119 				old_prio = prio;
120 			igt_assert_eq(arg.value, old_prio);
121 		}
122 
123 		arg.value = 0;
124 		gem_context_set_param(fd, &arg);
125 
126 		if (flags & NEW_CTX)
127 			gem_context_destroy(fd, arg.ctx_id);
128 	}
129 
130 	igt_waitchildren();
131 	free(values);
132 }
133 
__batch_create(int i915,uint32_t offset)134 static uint32_t __batch_create(int i915, uint32_t offset)
135 {
136 	const uint32_t bbe = MI_BATCH_BUFFER_END;
137 	uint32_t handle;
138 
139 	handle = gem_create(i915, ALIGN(offset + 4, 4096));
140 	gem_write(i915, handle, offset, &bbe, sizeof(bbe));
141 
142 	return handle;
143 }
144 
batch_create(int i915)145 static uint32_t batch_create(int i915)
146 {
147 	return __batch_create(i915, 0);
148 }
149 
test_vm(int i915)150 static void test_vm(int i915)
151 {
152 	const uint64_t nonzero_offset = 48 << 20;
153 	struct drm_i915_gem_exec_object2 batch = {
154 		.handle = batch_create(i915),
155 	};
156 	struct drm_i915_gem_execbuffer2 eb = {
157 		.buffers_ptr = to_user_pointer(&batch),
158 		.buffer_count = 1,
159 	};
160 	struct drm_i915_gem_context_param arg = {
161 		.param = I915_CONTEXT_PARAM_VM,
162 	};
163 	uint32_t parent, child;
164 
165 	/*
166 	 * Proving 2 contexts share the same GTT is quite tricky as we have no
167 	 * means of directly comparing them (each handle returned to userspace
168 	 * is unique). What we do instead is rely on a quirk of execbuf that
169 	 * it does not try to move an VMA without good reason, and so that
170 	 * having used an object in one context, it will have the same address
171 	 * in the next context that shared the VM.
172 	 */
173 
174 	arg.value = -1ull;
175 	igt_require(__gem_context_set_param(i915, &arg) == -ENOENT);
176 
177 	parent = gem_context_create(i915);
178 	child = gem_context_create(i915);
179 
180 	/* Using implicit soft-pinning */
181 	eb.rsvd1 = parent;
182 	batch.offset = nonzero_offset;
183 	gem_execbuf(i915, &eb);
184 	igt_assert_eq_u64(batch.offset, nonzero_offset);
185 
186 	eb.rsvd1 = child;
187 	batch.offset = 0;
188 	gem_execbuf(i915, &eb);
189 	igt_assert_eq_u64(batch.offset, 0);
190 
191 	eb.rsvd1 = parent;
192 	gem_execbuf(i915, &eb);
193 	igt_assert_eq_u64(batch.offset, nonzero_offset);
194 
195 	arg.ctx_id = parent;
196 	gem_context_get_param(i915, &arg);
197 	gem_context_set_param(i915, &arg);
198 
199 	/* Still the same VM, so expect the old VMA again */
200 	batch.offset = 0;
201 	gem_execbuf(i915, &eb);
202 	igt_assert_eq_u64(batch.offset, nonzero_offset);
203 
204 	arg.ctx_id = child;
205 	gem_context_set_param(i915, &arg);
206 
207 	eb.rsvd1 = child;
208 	batch.offset = 0;
209 	gem_execbuf(i915, &eb);
210 	igt_assert_eq_u64(batch.offset, nonzero_offset);
211 
212 	gem_context_destroy(i915, child);
213 	gem_context_destroy(i915, parent);
214 
215 	/* both contexts destroyed, but we still keep hold of the vm */
216 	child = gem_context_create(i915);
217 
218 	arg.ctx_id = child;
219 	gem_context_set_param(i915, &arg);
220 
221 	eb.rsvd1 = child;
222 	batch.offset = 0;
223 	gem_execbuf(i915, &eb);
224 	igt_assert_eq_u64(batch.offset, nonzero_offset);
225 
226 	gem_context_destroy(i915, child);
227 	gem_vm_destroy(i915, arg.value);
228 
229 	gem_sync(i915, batch.handle);
230 	gem_close(i915, batch.handle);
231 }
232 
233 igt_main
234 {
235 	struct drm_i915_gem_context_param arg;
236 	int fd;
237 	uint32_t ctx;
238 
239 	memset(&arg, 0, sizeof(arg));
240 
241 	igt_fixture {
242 		fd = drm_open_driver_render(DRIVER_INTEL);
243 
244 		gem_require_contexts(fd);
245 		ctx = gem_context_create(fd);
246 
247 		arg.param = I915_CONTEXT_PARAM_BAN_PERIOD;
248 
249 		/* XXX start to enforce ban period returning -EINVAL when
250 		 * transition has been done */
251 		if (__gem_context_get_param(fd, &arg) == -EINVAL)
252 			arg.param = I915_CONTEXT_PARAM_BANNABLE;
253 	}
254 
255 	igt_subtest("basic") {
256 		arg.ctx_id = ctx;
257 		gem_context_get_param(fd, &arg);
258 		gem_context_set_param(fd, &arg);
259 	}
260 
261 	igt_subtest("basic-default") {
262 		arg.ctx_id = 0;
263 		gem_context_get_param(fd, &arg);
264 		gem_context_set_param(fd, &arg);
265 	}
266 
267 	igt_subtest("invalid-ctx-get") {
268 		arg.ctx_id = 2;
269 		igt_assert_eq(__gem_context_get_param(fd, &arg), -ENOENT);
270 	}
271 
272 	igt_subtest("invalid-ctx-set") {
273 		arg.ctx_id = ctx;
274 		gem_context_get_param(fd, &arg);
275 		arg.ctx_id = 2;
276 		igt_assert_eq(__gem_context_set_param(fd, &arg), -ENOENT);
277 	}
278 
279 	igt_subtest("invalid-size-get") {
280 		arg.ctx_id = ctx;
281 		arg.size = 8;
282 		gem_context_get_param(fd, &arg);
283 		igt_assert(arg.size == 0);
284 	}
285 
286 	igt_subtest("invalid-size-set") {
287 		arg.ctx_id = ctx;
288 		gem_context_get_param(fd, &arg);
289 		arg.size = 8;
290 		igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
291 		arg.size = 0;
292 	}
293 
294 	igt_subtest("non-root-set") {
295 		igt_fork(child, 1) {
296 			igt_drop_root();
297 
298 			arg.ctx_id = ctx;
299 			gem_context_get_param(fd, &arg);
300 			arg.value--;
301 			igt_assert_eq(__gem_context_set_param(fd, &arg), -EPERM);
302 		}
303 
304 		igt_waitchildren();
305 	}
306 
307 	igt_subtest("root-set") {
308 		arg.ctx_id = ctx;
309 		gem_context_get_param(fd, &arg);
310 		arg.value--;
311 		gem_context_set_param(fd, &arg);
312 	}
313 
314 	arg.param = I915_CONTEXT_PARAM_NO_ZEROMAP;
315 
316 	igt_subtest("non-root-set-no-zeromap") {
317 		igt_fork(child, 1) {
318 			igt_drop_root();
319 
320 			arg.ctx_id = ctx;
321 			gem_context_get_param(fd, &arg);
322 			arg.value--;
323 			gem_context_set_param(fd, &arg);
324 		}
325 
326 		igt_waitchildren();
327 	}
328 
329 	igt_subtest("root-set-no-zeromap-enabled") {
330 		arg.ctx_id = ctx;
331 		gem_context_get_param(fd, &arg);
332 		arg.value = 1;
333 		gem_context_set_param(fd, &arg);
334 	}
335 
336 	igt_subtest("root-set-no-zeromap-disabled") {
337 		arg.ctx_id = ctx;
338 		gem_context_get_param(fd, &arg);
339 		arg.value = 0;
340 		gem_context_set_param(fd, &arg);
341 	}
342 
343 	igt_subtest("vm")
344 		test_vm(fd);
345 
346 	arg.param = I915_CONTEXT_PARAM_PRIORITY;
347 
348 	igt_subtest("set-priority-not-supported") {
349 		igt_require(!gem_scheduler_has_ctx_priority(fd));
350 
351 		arg.ctx_id = ctx;
352 		arg.size = 0;
353 
354 		igt_assert_eq(__gem_context_set_param(fd, &arg), -ENODEV);
355 	}
356 
357 	igt_subtest_group {
358 		igt_fixture {
359 			igt_require(gem_scheduler_has_ctx_priority(fd));
360 		}
361 
362 		igt_subtest("get-priority-new-ctx") {
363 			struct drm_i915_gem_context_param local_arg = arg;
364 			uint32_t local_ctx = gem_context_create(fd);
365 
366 			local_arg.ctx_id = local_ctx;
367 
368 			gem_context_get_param(fd, &local_arg);
369 			igt_assert_eq(local_arg.value, I915_CONTEXT_DEFAULT_PRIORITY);
370 
371 			gem_context_destroy(fd, local_ctx);
372 		}
373 
374 		igt_subtest("set-priority-invalid-size") {
375 			struct drm_i915_gem_context_param local_arg = arg;
376 			local_arg.ctx_id = ctx;
377 			local_arg.value = 0;
378 			local_arg.size = ~0;
379 
380 			igt_assert_eq(__gem_context_set_param(fd, &local_arg), -EINVAL);
381 		}
382 
383 		igt_subtest("set-priority-range")
384 			set_priority(fd);
385 	}
386 
387 	/* I915_CONTEXT_PARAM_SSEU tests are located in gem_ctx_sseu.c */
388 
389 	arg.param = -1; /* Should be safely unused for a while */
390 
391 	igt_subtest("invalid-param-get") {
392 		arg.ctx_id = ctx;
393 		igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
394 	}
395 
396 	igt_subtest("invalid-param-set") {
397 		arg.ctx_id = ctx;
398 		igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
399 	}
400 
401 	igt_fixture
402 		close(fd);
403 }
404