| /* |
| * Copyright © 2015 Intel Corporation |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice (including the next |
| * paragraph) shall be included in all copies or substantial portions of the |
| * Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
| * IN THE SOFTWARE. |
| * |
| * Authors: |
| * Daniel Vetter <daniel.vetter@ffwll.ch> |
| */ |
| |
| #include <fcntl.h> |
| #include <limits.h> |
| |
| #include "igt.h" |
| #include "i915/gem_vm.h" |
| |
| IGT_TEST_DESCRIPTION("Basic test for context set/get param input validation."); |
| |
| #define NEW_CTX BIT(0) |
| #define USER BIT(1) |
| |
| static void set_priority(int i915) |
| { |
| static const int64_t test_values[] = { |
| /* Test space too big, pick significant values */ |
| INT_MIN, |
| |
| I915_CONTEXT_MIN_USER_PRIORITY - 1, |
| I915_CONTEXT_MIN_USER_PRIORITY, |
| I915_CONTEXT_MIN_USER_PRIORITY + 1, |
| |
| I915_CONTEXT_DEFAULT_PRIORITY - 1, |
| I915_CONTEXT_DEFAULT_PRIORITY, |
| I915_CONTEXT_DEFAULT_PRIORITY + 1, |
| |
| I915_CONTEXT_MAX_USER_PRIORITY - 1, |
| I915_CONTEXT_MAX_USER_PRIORITY, |
| I915_CONTEXT_MAX_USER_PRIORITY + 1, |
| |
| INT_MAX |
| }; |
| unsigned int size; |
| int64_t *values; |
| |
| igt_require(getuid() == 0); |
| |
| size = ARRAY_SIZE(test_values); |
| values = malloc(sizeof(test_values) * 8); |
| igt_assert(values); |
| |
| for (unsigned i = 0; i < size; i++) { |
| values[i + 0*size] = test_values[i]; |
| values[i + 1*size] = test_values[i] | (uint64_t)1 << 32; |
| values[i + 2*size] = test_values[i] | (uint64_t)rand() << 32; |
| values[i + 3*size] = test_values[i] ^ rand(); |
| values[i + 4*size] = rand() % (I915_CONTEXT_MAX_USER_PRIORITY - I915_CONTEXT_MIN_USER_PRIORITY) + I915_CONTEXT_MIN_USER_PRIORITY; |
| values[i + 5*size] = rand(); |
| values[i + 6*size] = rand() | (uint64_t)rand() << 32; |
| values[i + 7*size] = (uint64_t)test_values[i] << 32; |
| } |
| size *= 8; |
| |
| igt_permute_array(values, size, igt_exchange_int64); |
| |
| igt_fork(flags, NEW_CTX | USER) { |
| int fd = gem_reopen_driver(i915); |
| struct drm_i915_gem_context_param arg = { |
| .param = I915_CONTEXT_PARAM_PRIORITY, |
| .ctx_id = flags & NEW_CTX ? gem_context_create(fd) : 0, |
| }; |
| int64_t old_prio; |
| |
| if (flags & USER) { |
| igt_debug("Dropping root privilege\n"); |
| igt_drop_root(); |
| } |
| |
| gem_context_get_param(fd, &arg); |
| old_prio = arg.value; |
| |
| for (unsigned i = 0; i < size; i++) { |
| int64_t prio = values[i]; |
| int expected = 0; |
| int err; |
| |
| arg.value = prio; |
| |
| if (flags & USER && |
| prio > I915_CONTEXT_DEFAULT_PRIORITY) |
| expected = -EPERM; |
| |
| if (prio < I915_CONTEXT_MIN_USER_PRIORITY || |
| prio > I915_CONTEXT_MAX_USER_PRIORITY) |
| expected = -EINVAL; |
| |
| err =__gem_context_set_param(fd, &arg); |
| igt_assert_f(err == expected, |
| "Priority requested %" PRId64 " with flags %x, expected result %d, returned %d\n", |
| prio, flags, expected, err); |
| |
| gem_context_get_param(fd, &arg); |
| if (!err) |
| old_prio = prio; |
| igt_assert_eq(arg.value, old_prio); |
| } |
| |
| arg.value = 0; |
| gem_context_set_param(fd, &arg); |
| |
| if (flags & NEW_CTX) |
| gem_context_destroy(fd, arg.ctx_id); |
| } |
| |
| igt_waitchildren(); |
| free(values); |
| } |
| |
| static uint32_t __batch_create(int i915, uint32_t offset) |
| { |
| const uint32_t bbe = MI_BATCH_BUFFER_END; |
| uint32_t handle; |
| |
| handle = gem_create(i915, ALIGN(offset + 4, 4096)); |
| gem_write(i915, handle, offset, &bbe, sizeof(bbe)); |
| |
| return handle; |
| } |
| |
| static uint32_t batch_create(int i915) |
| { |
| return __batch_create(i915, 0); |
| } |
| |
| static void test_vm(int i915) |
| { |
| const uint64_t nonzero_offset = 48 << 20; |
| struct drm_i915_gem_exec_object2 batch = { |
| .handle = batch_create(i915), |
| }; |
| struct drm_i915_gem_execbuffer2 eb = { |
| .buffers_ptr = to_user_pointer(&batch), |
| .buffer_count = 1, |
| }; |
| struct drm_i915_gem_context_param arg = { |
| .param = I915_CONTEXT_PARAM_VM, |
| }; |
| uint32_t parent, child; |
| |
| /* |
| * Proving 2 contexts share the same GTT is quite tricky as we have no |
| * means of directly comparing them (each handle returned to userspace |
| * is unique). What we do instead is rely on a quirk of execbuf that |
| * it does not try to move an VMA without good reason, and so that |
| * having used an object in one context, it will have the same address |
| * in the next context that shared the VM. |
| */ |
| |
| arg.value = -1ull; |
| igt_require(__gem_context_set_param(i915, &arg) == -ENOENT); |
| |
| parent = gem_context_create(i915); |
| child = gem_context_create(i915); |
| |
| /* Using implicit soft-pinning */ |
| eb.rsvd1 = parent; |
| batch.offset = nonzero_offset; |
| gem_execbuf(i915, &eb); |
| igt_assert_eq_u64(batch.offset, nonzero_offset); |
| |
| eb.rsvd1 = child; |
| batch.offset = 0; |
| gem_execbuf(i915, &eb); |
| igt_assert_eq_u64(batch.offset, 0); |
| |
| eb.rsvd1 = parent; |
| gem_execbuf(i915, &eb); |
| igt_assert_eq_u64(batch.offset, nonzero_offset); |
| |
| arg.ctx_id = parent; |
| gem_context_get_param(i915, &arg); |
| gem_context_set_param(i915, &arg); |
| |
| /* Still the same VM, so expect the old VMA again */ |
| batch.offset = 0; |
| gem_execbuf(i915, &eb); |
| igt_assert_eq_u64(batch.offset, nonzero_offset); |
| |
| arg.ctx_id = child; |
| gem_context_set_param(i915, &arg); |
| |
| eb.rsvd1 = child; |
| batch.offset = 0; |
| gem_execbuf(i915, &eb); |
| igt_assert_eq_u64(batch.offset, nonzero_offset); |
| |
| gem_context_destroy(i915, child); |
| gem_context_destroy(i915, parent); |
| |
| /* both contexts destroyed, but we still keep hold of the vm */ |
| child = gem_context_create(i915); |
| |
| arg.ctx_id = child; |
| gem_context_set_param(i915, &arg); |
| |
| eb.rsvd1 = child; |
| batch.offset = 0; |
| gem_execbuf(i915, &eb); |
| igt_assert_eq_u64(batch.offset, nonzero_offset); |
| |
| gem_context_destroy(i915, child); |
| gem_vm_destroy(i915, arg.value); |
| |
| gem_sync(i915, batch.handle); |
| gem_close(i915, batch.handle); |
| } |
| |
| igt_main |
| { |
| struct drm_i915_gem_context_param arg; |
| int fd; |
| uint32_t ctx; |
| |
| memset(&arg, 0, sizeof(arg)); |
| |
| igt_fixture { |
| fd = drm_open_driver_render(DRIVER_INTEL); |
| |
| gem_require_contexts(fd); |
| ctx = gem_context_create(fd); |
| |
| arg.param = I915_CONTEXT_PARAM_BAN_PERIOD; |
| |
| /* XXX start to enforce ban period returning -EINVAL when |
| * transition has been done */ |
| if (__gem_context_get_param(fd, &arg) == -EINVAL) |
| arg.param = I915_CONTEXT_PARAM_BANNABLE; |
| } |
| |
| igt_subtest("basic") { |
| arg.ctx_id = ctx; |
| gem_context_get_param(fd, &arg); |
| gem_context_set_param(fd, &arg); |
| } |
| |
| igt_subtest("basic-default") { |
| arg.ctx_id = 0; |
| gem_context_get_param(fd, &arg); |
| gem_context_set_param(fd, &arg); |
| } |
| |
| igt_subtest("invalid-ctx-get") { |
| arg.ctx_id = 2; |
| igt_assert_eq(__gem_context_get_param(fd, &arg), -ENOENT); |
| } |
| |
| igt_subtest("invalid-ctx-set") { |
| arg.ctx_id = ctx; |
| gem_context_get_param(fd, &arg); |
| arg.ctx_id = 2; |
| igt_assert_eq(__gem_context_set_param(fd, &arg), -ENOENT); |
| } |
| |
| igt_subtest("invalid-size-get") { |
| arg.ctx_id = ctx; |
| arg.size = 8; |
| gem_context_get_param(fd, &arg); |
| igt_assert(arg.size == 0); |
| } |
| |
| igt_subtest("invalid-size-set") { |
| arg.ctx_id = ctx; |
| gem_context_get_param(fd, &arg); |
| arg.size = 8; |
| igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL); |
| arg.size = 0; |
| } |
| |
| igt_subtest("non-root-set") { |
| igt_fork(child, 1) { |
| igt_drop_root(); |
| |
| arg.ctx_id = ctx; |
| gem_context_get_param(fd, &arg); |
| arg.value--; |
| igt_assert_eq(__gem_context_set_param(fd, &arg), -EPERM); |
| } |
| |
| igt_waitchildren(); |
| } |
| |
| igt_subtest("root-set") { |
| arg.ctx_id = ctx; |
| gem_context_get_param(fd, &arg); |
| arg.value--; |
| gem_context_set_param(fd, &arg); |
| } |
| |
| arg.param = I915_CONTEXT_PARAM_NO_ZEROMAP; |
| |
| igt_subtest("non-root-set-no-zeromap") { |
| igt_fork(child, 1) { |
| igt_drop_root(); |
| |
| arg.ctx_id = ctx; |
| gem_context_get_param(fd, &arg); |
| arg.value--; |
| gem_context_set_param(fd, &arg); |
| } |
| |
| igt_waitchildren(); |
| } |
| |
| igt_subtest("root-set-no-zeromap-enabled") { |
| arg.ctx_id = ctx; |
| gem_context_get_param(fd, &arg); |
| arg.value = 1; |
| gem_context_set_param(fd, &arg); |
| } |
| |
| igt_subtest("root-set-no-zeromap-disabled") { |
| arg.ctx_id = ctx; |
| gem_context_get_param(fd, &arg); |
| arg.value = 0; |
| gem_context_set_param(fd, &arg); |
| } |
| |
| igt_subtest("vm") |
| test_vm(fd); |
| |
| arg.param = I915_CONTEXT_PARAM_PRIORITY; |
| |
| igt_subtest("set-priority-not-supported") { |
| igt_require(!gem_scheduler_has_ctx_priority(fd)); |
| |
| arg.ctx_id = ctx; |
| arg.size = 0; |
| |
| igt_assert_eq(__gem_context_set_param(fd, &arg), -ENODEV); |
| } |
| |
| igt_subtest_group { |
| igt_fixture { |
| igt_require(gem_scheduler_has_ctx_priority(fd)); |
| } |
| |
| igt_subtest("get-priority-new-ctx") { |
| struct drm_i915_gem_context_param local_arg = arg; |
| uint32_t local_ctx = gem_context_create(fd); |
| |
| local_arg.ctx_id = local_ctx; |
| |
| gem_context_get_param(fd, &local_arg); |
| igt_assert_eq(local_arg.value, I915_CONTEXT_DEFAULT_PRIORITY); |
| |
| gem_context_destroy(fd, local_ctx); |
| } |
| |
| igt_subtest("set-priority-invalid-size") { |
| struct drm_i915_gem_context_param local_arg = arg; |
| local_arg.ctx_id = ctx; |
| local_arg.value = 0; |
| local_arg.size = ~0; |
| |
| igt_assert_eq(__gem_context_set_param(fd, &local_arg), -EINVAL); |
| } |
| |
| igt_subtest("set-priority-range") |
| set_priority(fd); |
| } |
| |
| /* I915_CONTEXT_PARAM_SSEU tests are located in gem_ctx_sseu.c */ |
| |
| arg.param = -1; /* Should be safely unused for a while */ |
| |
| igt_subtest("invalid-param-get") { |
| arg.ctx_id = ctx; |
| igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL); |
| } |
| |
| igt_subtest("invalid-param-set") { |
| arg.ctx_id = ctx; |
| igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL); |
| } |
| |
| igt_fixture |
| close(fd); |
| } |