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