Message ID | 20191113154913.8787-5-mika.kuoppala@linux.intel.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | [i-g-t,1/7] lib/igt_dummyload: Send batch as first | expand |
Quoting Mika Kuoppala (2019-11-13 15:49:11) > From: Mika Kuoppala <mika.kuoppala@intel.com> > > For testing blitter engine command parser on gen9. > > v2: bad jump offset > v3: rebase > v4: improve bb start and subcase it > v5: fix presumed offsets (Jon) > > Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com> > --- > tests/Makefile.sources | 3 + > tests/i915/gem_blt_parse.c | 997 ++++++++++++++++++++++++++ I was thinking gem_exec_parse_blt.c so the relationship with the previous gem_exec_parse is clearer (both that this cmdparser, and that this is a subset of the execbuf ABI). > tests/intel-ci/fast-feedback.testlist | 1 + > tests/meson.build | 1 + > 4 files changed, 1002 insertions(+) > create mode 100644 tests/i915/gem_blt_parse.c > > diff --git a/tests/Makefile.sources b/tests/Makefile.sources > index abf1e2fc..75c79edb 100644 > --- a/tests/Makefile.sources > +++ b/tests/Makefile.sources > @@ -244,6 +244,9 @@ gem_exec_params_SOURCES = i915/gem_exec_params.c > TESTS_progs += gem_exec_parse > gem_exec_parse_SOURCES = i915/gem_exec_parse.c > > +TESTS_progs += gem_blt_parse > +gem_blt_parse_SOURCES = i915/gem_blt_parse.c > + > TESTS_progs += gem_exec_reloc > gem_exec_reloc_SOURCES = i915/gem_exec_reloc.c > > diff --git a/tests/i915/gem_blt_parse.c b/tests/i915/gem_blt_parse.c > new file mode 100644 > index 00000000..607afba9 > --- /dev/null > +++ b/tests/i915/gem_blt_parse.c > @@ -0,0 +1,997 @@ > +/* > + * Copyright © 2018 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. > + * > + */ > + > +#include "igt.h" > +#include <stdlib.h> > +#include <stdint.h> > +#include <stdio.h> > +#include <errno.h> > + > +#include <drm.h> > + > +#ifndef I915_PARAM_CMD_PARSER_VERSION > +#define I915_PARAM_CMD_PARSER_VERSION 28 > +#endif > + > +#ifndef I915_PARAM_HAS_SECURE_BATCHES > +#define I915_PARAM_HAS_SECURE_BATCHES 23 > +#endif Now pulled in from uapi/i915_drm.h > + > +/* To help craft commands known to be invalid across all engines */ > +#define INSTR_CLIENT_SHIFT 29 > +#define INSTR_INVALID_CLIENT 0x7 > + > +#define MI_LOAD_REGISTER_REG (0x2a << 23) > +#define MI_STORE_REGISTER_MEM (0x24 << 23) > +#define MI_ARB_ON_OFF (0x8 << 23) > +#define MI_USER_INTERRUPT (0x02 << 23) > +#define MI_FLUSH_DW (0x26 << 23) > +#define MI_ARB_CHECK (0x05 << 23) > +#define MI_REPORT_HEAD (0x07 << 23) > +#define MI_SUSPEND_FLUSH (0x0b << 23) > +#define MI_LOAD_SCAN_LINES_EXCL (0x13 << 23) > +#define MI_UPDATE_GTT (0x23 << 23) > + > +#define BCS_SWCTRL 0x22200 > +#define BCS_GPR_BASE 0x22600 > +#define BCS_GPR(n) (0x22600 + (n) * 8) > +#define BCS_GPR_UDW(n) (0x22600 + (n) * 8 + 4) > + > +#define HANDLE_SIZE 4096 > + > +static int parser_version; > + > +static int command_parser_version(int fd) > +{ > + int version = -1; > + drm_i915_getparam_t gp; > + > + gp.param = I915_PARAM_CMD_PARSER_VERSION; > + gp.value = &version; > + > + if (drmIoctl(fd, DRM_IOCTL_I915_GETPARAM, &gp) == 0) > + return version; > + > + return -1; > +} > + > +static int __exec_batch_patched(int fd, int engine, > + uint32_t cmd_bo, const uint32_t *cmds, int size, > + uint32_t target_bo, uint64_t target_offset, uint64_t target_delta) > +{ > + struct drm_i915_gem_execbuffer2 execbuf; > + struct drm_i915_gem_exec_object2 obj[2]; > + struct drm_i915_gem_relocation_entry reloc[1]; > + > + gem_write(fd, cmd_bo, 0, cmds, size); > + > + memset(obj, 0, sizeof(obj)); > + obj[0].handle = target_bo; > + obj[1].handle = cmd_bo; > + > + memset(reloc, 0, sizeof(reloc)); > + reloc[0].offset = target_offset; > + reloc[0].target_handle = target_bo; > + reloc[0].delta = target_delta; > + reloc[0].read_domains = I915_GEM_DOMAIN_COMMAND; > + reloc[0].write_domain = I915_GEM_DOMAIN_COMMAND; > + reloc[0].presumed_offset = -1; > + > + obj[1].relocs_ptr = to_user_pointer(reloc); > + obj[1].relocation_count = 1; > + > + memset(&execbuf, 0, sizeof(execbuf)); > + execbuf.buffers_ptr = to_user_pointer(obj); > + execbuf.buffer_count = 2; > + execbuf.batch_len = size; > + execbuf.flags = engine; > + > + return __gem_execbuf(fd, &execbuf); > +} > + > +static void exec_batch_patched(int fd, int engine, > + uint32_t cmd_bo, const uint32_t *cmds, > + int size, int patch_offset, > + long int expected_value) > +{ > + const uint32_t target_bo = gem_create(fd, HANDLE_SIZE); > + uint64_t actual_value = 0; > + long int ret; > + > + ret = __exec_batch_patched(fd, engine, cmd_bo, cmds, size, target_bo, patch_offset, 0); > + if (ret) { > + igt_assert_lt(ret, 0); > + gem_close(fd, target_bo); > + igt_assert_eq(ret, expected_value); > + return; > + } > + > + gem_sync(fd, cmd_bo); What evil is this covering up? > + > + gem_read(fd, target_bo, 0, &actual_value, sizeof(actual_value)); > + > + gem_close(fd, target_bo); > + > + igt_assert_eq(actual_value, expected_value); > +} > + > +static int __exec_batch(int fd, int engine, uint32_t cmd_bo, > + const uint32_t *cmds, int size) > +{ > + struct drm_i915_gem_execbuffer2 execbuf; > + struct drm_i915_gem_exec_object2 obj[1]; > + int ret; > + > + gem_write(fd, cmd_bo, 0, cmds, size); > + > + memset(obj, 0, sizeof(obj)); > + obj[0].handle = cmd_bo; > + > + memset(&execbuf, 0, sizeof(execbuf)); > + execbuf.buffers_ptr = to_user_pointer(obj); > + execbuf.buffer_count = 1; > + execbuf.batch_len = size; > + execbuf.flags = engine; > + > + ret = __gem_execbuf(fd, &execbuf); > + > + if (!ret) > + gem_sync(fd, cmd_bo); More evil? > + > + return ret; > +} > + > +#if 0 > +static void print_batch(const uint32_t *cmds, const uint32_t sz) > +{ > + const int commands = sz / 4; > + int i; > + > + igt_info("Batch size %d\n", sz); > + for (i = 0; i < commands; i++) > + igt_info("0x%08x: 0x%08x\n", i, cmds[i]); > +} > +#else > +#define print_batch(cmds, size) > +#endif > + > +#define exec_batch(fd, engine, bo, cmds, sz, expected) \ > + print_batch(cmds, sz); \ > + igt_assert_eq(__exec_batch(fd, engine, bo, cmds, sz), expected) > + > +static void exec_split_batch(int fd, int engine, const uint32_t *cmds, > + int size, int expected_ret) > +{ > + struct drm_i915_gem_execbuffer2 execbuf; > + struct drm_i915_gem_exec_object2 obj[1]; > + uint32_t cmd_bo; > + const uint32_t noop[1024] = { 0 }; > + const int alloc_size = 4096 * 2; > + const int actual_start_offset = 4096-sizeof(uint32_t); > + > + /* Allocate and fill a 2-page batch with noops */ > + cmd_bo = gem_create(fd, alloc_size); > + gem_write(fd, cmd_bo, 0, noop, sizeof(noop)); > + gem_write(fd, cmd_bo, 4096, noop, sizeof(noop)); > + > + /* Write the provided commands such that the first dword > + * of the command buffer is the last dword of the first > + * page (i.e. the command is split across the two pages). > + */ > + gem_write(fd, cmd_bo, actual_start_offset, cmds, size); > + > + memset(obj, 0, sizeof(obj)); > + obj[0].handle = cmd_bo; > + > + memset(&execbuf, 0, sizeof(execbuf)); > + execbuf.buffers_ptr = to_user_pointer(obj); > + execbuf.buffer_count = 1; > + /* NB: We want batch_start_offset and batch_len to point to the block > + * of the actual commands (i.e. at the last dword of the first page), > + * but have to adjust both the start offset and length to meet the > + * kernel driver's requirements on the alignment of those fields. > + */ > + execbuf.batch_start_offset = actual_start_offset & ~0x7; > + execbuf.batch_len = > + ALIGN(size + actual_start_offset - execbuf.batch_start_offset, > + 0x8); > + execbuf.flags = engine; > + > + igt_assert_eq(__gem_execbuf(fd, &execbuf), expected_ret); > + > + gem_sync(fd, cmd_bo); Yet more. > + gem_close(fd, cmd_bo); > +} > + > +static void exec_batch_chained(int fd, int engine, > + uint32_t cmd_bo, const uint32_t *cmds, > + int size, int patch_offset, > + uint64_t expected_value, > + int expected_return) > +{ > + struct drm_i915_gem_execbuffer2 execbuf; > + struct drm_i915_gem_exec_object2 obj[3]; > + struct drm_i915_gem_relocation_entry reloc[1]; > + struct drm_i915_gem_relocation_entry first_level_reloc; > + > + const uint32_t target_bo = gem_create(fd, 4096); > + const uint32_t first_level_bo = gem_create(fd, 4096); > + uint64_t actual_value = 0; > + int ret; > + > + const uint32_t first_level_cmds[] = { > + MI_BATCH_BUFFER_START | MI_BATCH_NON_SECURE_I965 | 1, > + 0, > + 0, > + MI_BATCH_BUFFER_END, > + }; > + > + gem_write(fd, first_level_bo, 0, > + first_level_cmds, sizeof(first_level_cmds)); > + gem_write(fd, cmd_bo, 0, cmds, size); > + > + memset(obj, 0, sizeof(obj)); > + obj[0].handle = target_bo; > + obj[1].handle = cmd_bo; > + obj[2].handle = first_level_bo; > + > + memset(reloc, 0, sizeof(reloc)); > + reloc[0].offset = patch_offset; > + reloc[0].delta = 0; > + reloc[0].target_handle = target_bo; > + reloc[0].read_domains = I915_GEM_DOMAIN_COMMAND; > + reloc[0].write_domain = I915_GEM_DOMAIN_COMMAND; > + reloc[0].presumed_offset = -1; > + > + obj[1].relocation_count = 1; > + obj[1].relocs_ptr = to_user_pointer(&reloc); > + > + memset(&first_level_reloc, 0, sizeof(first_level_reloc)); > + first_level_reloc.offset = 4; > + first_level_reloc.delta = 0; > + first_level_reloc.target_handle = cmd_bo; > + first_level_reloc.read_domains = I915_GEM_DOMAIN_COMMAND; > + first_level_reloc.write_domain = 0; > + obj[2].relocation_count = 1; > + obj[2].relocs_ptr = to_user_pointer(&first_level_reloc); > + > + memset(&execbuf, 0, sizeof(execbuf)); > + execbuf.buffers_ptr = to_user_pointer(obj); > + execbuf.buffer_count = 3; > + execbuf.batch_len = sizeof(first_level_cmds); > + execbuf.flags = engine; > + > + ret = __gem_execbuf(fd, &execbuf); > + if (expected_return && ret == expected_return) > + goto out; > + > + gem_sync(fd, cmd_bo); What are you doing??? > + > + gem_read(fd,target_bo, 0, &actual_value, sizeof(actual_value)); > + > +out: > + if (!expected_return) > + igt_assert_eq(expected_value, actual_value); > + else > + igt_assert_neq(expected_value, actual_value); > + > + gem_close(fd, first_level_bo); > + gem_close(fd, target_bo); > +} > + > +static void test_secure_batches(const int fd) > +{ > + int v = -1; > + drm_i915_getparam_t gp; > + > + gp.param = I915_PARAM_HAS_SECURE_BATCHES; > + gp.value = &v; > + > + igt_assert_eq(drmIoctl(fd, DRM_IOCTL_I915_GETPARAM, &gp), 0); > + igt_assert_eq(v, 0); > + > + igt_assert(!gem_engine_has_mutable_submission(fd, I915_EXEC_BLT)); > +} > + > +struct cmd { > + uint32_t cmd; > + int len; > + const char *name; > +}; > + > +#define CMD(C, L) { .cmd = (C), .len = (L), .name = #C } > +#define CMD_N(C) { .cmd = (C), .len = 1, .name = #C } > + > +static const struct cmd allowed_cmds[] = { > + CMD_N(MI_NOOP), > + CMD_N(MI_USER_INTERRUPT), > + CMD_N(MI_WAIT_FOR_EVENT), > + CMD(MI_FLUSH_DW, 5), > + CMD_N(MI_ARB_CHECK), > + CMD_N(MI_REPORT_HEAD), > + CMD_N(MI_FLUSH), > + CMD_N(MI_ARB_ON_OFF), > + CMD_N(MI_SUSPEND_FLUSH), > + CMD(MI_LOAD_SCAN_LINES_INCL, 2), > + CMD(MI_LOAD_SCAN_LINES_EXCL, 2), > +}; > + > +static uint32_t *inject_cmd(uint32_t *batch, const uint32_t cmd, int len) > +{ > + int i = 0; > + > + batch[i++] = cmd; > + > + while (--len) > + batch[i++] = 0; > + > + return &batch[i]; > +} > + > +static unsigned long batch_num_cmds(const uint32_t * const batch_start, > + const uint32_t * const batch_end) > +{ > + igt_assert_lte((unsigned long)batch_start, (unsigned long)batch_end); > + > + return batch_end - batch_start; > +} > + > +static unsigned long batch_bytes(const uint32_t * const batch_start, > + const uint32_t * const batch_end) > +{ > + const unsigned long bytes = batch_num_cmds(batch_start, batch_end) * 4; > + > + igt_assert(!(bytes & 0x7)); > + > + return bytes; > +} > + > +static void test_allowed_all(const int fd, const uint32_t handle) > +{ > + uint32_t batch[4096]; > + uint32_t *b = &batch[0]; > + > + for (int i = 0; i < ARRAY_SIZE(allowed_cmds); i++) > + b = inject_cmd(b, allowed_cmds[i].cmd, > + allowed_cmds[i].len); > + > + if (!(batch_num_cmds(batch, b) % 2)) > + b = inject_cmd(b, MI_NOOP, 1); > + > + b = inject_cmd(b, MI_BATCH_BUFFER_END, 1); > + > + exec_batch(fd, I915_EXEC_BLT, handle, batch, batch_bytes(batch, b), 0); > +} > + > +static void test_allowed_single(const int fd, const uint32_t handle) > +{ > + uint32_t batch[4096]; > + int ret; > + > + for (int i = 0; i < ARRAY_SIZE(allowed_cmds); i++) { > + uint32_t *b = &batch[0]; > + > + b = inject_cmd(b, allowed_cmds[i].cmd, > + allowed_cmds[i].len); > + > + if (!(batch_num_cmds(batch, b) % 2)) > + b = inject_cmd(b, MI_NOOP, 1); > + > + b = inject_cmd(b, MI_BATCH_BUFFER_END, 1); > + > + ret = __exec_batch(fd, I915_EXEC_BLT, handle, > + batch, batch_bytes(batch, b)); > + > + igt_assert_eq(ret, 0); > + }; > +} > + > +static void test_bb_secure(const int fd, const uint32_t handle) > +{ > + struct drm_i915_gem_execbuffer2 execbuf; > + struct drm_i915_gem_exec_object2 obj[1]; > + struct drm_i915_gem_relocation_entry reloc[1]; > + int ret; > + > + const uint32_t batch_secure[] = { > + MI_BATCH_BUFFER_START | 1, > + 12, > + 0, > + MI_NOOP, > + MI_NOOP, > + MI_BATCH_BUFFER_END, > + }; > + > + gem_write(fd, handle, 0, batch_secure, sizeof(batch_secure)); > + > + memset(obj, 0, sizeof(obj)); > + obj[0].handle = handle; > + > + memset(reloc, 0, sizeof(reloc)); > + reloc[0].offset = 1 * sizeof(uint32_t); > + reloc[0].target_handle = handle; > + reloc[0].delta = 4 * sizeof(uint32_t); > + reloc[0].read_domains = I915_GEM_DOMAIN_COMMAND; > + reloc[0].write_domain = 0; > + reloc[0].presumed_offset = -1; > + > + obj[0].relocs_ptr = to_user_pointer(reloc); > + obj[0].relocation_count = 1; > + > + memset(&execbuf, 0, sizeof(execbuf)); > + execbuf.buffers_ptr = to_user_pointer(obj); > + execbuf.buffer_count = 1; > + execbuf.batch_len = sizeof(batch_secure); > + execbuf.flags = I915_EXEC_BLT; > + > + ret = __gem_execbuf(fd, &execbuf); > + > + igt_assert_eq(ret, -EACCES); > +} > + > +#define BB_START_PARAM 0 > +#define BB_START_OUT 1 > +#define BB_START_CMD 2 > + > +static void test_bb_start(const int fd, const uint32_t handle, int test) > +{ > + struct drm_i915_gem_execbuffer2 execbuf; > + struct drm_i915_gem_exec_object2 obj[2]; > + struct drm_i915_gem_relocation_entry reloc[3]; > + const uint32_t target_bo = gem_create(fd, 4096); > + uint32_t *dst; > + int ret; > + unsigned int jump_off; > + > + const uint32_t batch[] = { > + MI_NOOP, > + MI_NOOP, > + MI_NOOP, > + MI_NOOP, > + MI_STORE_DWORD_IMM, > + 0, > + 0, > + 1, > + MI_STORE_DWORD_IMM, > + 4, > + 0, > + 2, > + MI_COND_BATCH_BUFFER_END | 1, > + 0, > + 0, > + 0, > + MI_BATCH_BUFFER_START | MI_BATCH_NON_SECURE_I965 | 1, > + 0, > + 0, > + MI_BATCH_BUFFER_END, > + }; > + > + igt_require(gem_can_store_dword(fd, I915_EXEC_BLT)); > + > + switch (test) { > + case BB_START_PARAM: > + jump_off = 5 * sizeof(uint32_t); > + break; > + case BB_START_CMD: > + jump_off = 8 * sizeof(uint32_t); > + break; > + default: > + jump_off = 0xf00d0000; > + } > + > + gem_write(fd, handle, 0, batch, sizeof(batch)); > + > + memset(obj, 0, sizeof(obj)); > + obj[0].handle = target_bo; > + obj[1].handle = handle; > + > + memset(reloc, 0, sizeof(reloc)); > + reloc[0].offset = 5 * sizeof(uint32_t); > + reloc[0].target_handle = obj[0].handle; > + reloc[0].delta = 0; > + reloc[0].read_domains = I915_GEM_DOMAIN_COMMAND; > + reloc[0].write_domain = I915_GEM_DOMAIN_COMMAND; > + reloc[0].presumed_offset = -1; > + > + reloc[1].offset = 9 * sizeof(uint32_t); > + reloc[1].target_handle = obj[0].handle; > + reloc[1].delta = 1 * sizeof(uint32_t); > + reloc[1].read_domains = I915_GEM_DOMAIN_COMMAND; > + reloc[1].write_domain = I915_GEM_DOMAIN_COMMAND; > + reloc[1].presumed_offset = -1; > + > + reloc[2].offset = 17 * sizeof(uint32_t); > + reloc[2].target_handle = obj[1].handle; > + reloc[2].delta = jump_off; > + reloc[2].read_domains = I915_GEM_DOMAIN_COMMAND; > + reloc[2].write_domain = 0; > + reloc[2].presumed_offset = -1; > + > + obj[1].relocs_ptr = to_user_pointer(reloc); > + obj[1].relocation_count = 3; > + > + memset(&execbuf, 0, sizeof(execbuf)); > + execbuf.buffers_ptr = to_user_pointer(obj); > + execbuf.buffer_count = 2; > + execbuf.batch_len = sizeof(batch); > + execbuf.flags = I915_EXEC_BLT; > + > + dst = gem_mmap__cpu(fd, obj[0].handle, 0, 4096, > + PROT_READ | PROT_WRITE); > + > + igt_assert_eq(dst[0], 0); > + igt_assert_eq(dst[1], 0); > + > + ret = __gem_execbuf(fd, &execbuf); > + > + switch (test) { > + case BB_START_PARAM: > + igt_assert_eq(ret, -EINVAL); > + break; > + case BB_START_CMD: > + igt_assert_eq(ret, 0); > + > + while (READ_ONCE(dst[0]) == 0) > + ; > + > + while (READ_ONCE(dst[1]) == 0) > + ; > + > + igt_assert_eq(dst[0], 1); > + igt_assert_eq(dst[1], 2); > + > + igt_info("values now %x %x\n", dst[0], dst[1]); > + > + dst[0] = 0; > + > + igt_info("values now %x %x\n", dst[0], dst[1]); > + > + gem_sync(fd, handle); > + > + igt_info("values now %x %x\n", dst[0], dst[1]); > + > + igt_assert_eq(dst[0], 0); > + igt_assert_eq(dst[1], 2); > + > + break; > + > + case BB_START_OUT: > + igt_assert_eq(ret, -EINVAL); > + break; > + } > + > + gem_sync(fd, handle); > + gem_munmap(dst, 4096); > + gem_close(fd, target_bo); > +} > + > +static void test_bb_chained(const int fd, const uint32_t handle) > +{ > + const uint32_t batch[] = { > + (0x20 << 23) | 2, /* MI_STORE_DATA_IMM */ > + 0, > + 0, > + 0xbaadf00d, > + MI_NOOP, > + MI_BATCH_BUFFER_END, > + }; > + > + exec_batch_chained(fd, I915_EXEC_RENDER, > + handle, > + batch, sizeof(batch), > + 4, > + 0xbaadf00d, > + 0); > + > + exec_batch_chained(fd, I915_EXEC_BLT, > + handle, > + batch, sizeof(batch), > + 4, > + 0xbaadf00d, > + EPERM); > +} > + > +static void test_cmd_crossing_page(const int fd, const uint32_t handle) > +{ > + const uint32_t lri_ok[] = { > + MI_LOAD_REGISTER_IMM, > + BCS_GPR(0), > + 0xbaadf00d, > + MI_BATCH_BUFFER_END, > + }; > + const uint32_t store_reg[] = { > + MI_STORE_REGISTER_MEM | (4 - 2), > + BCS_GPR(0), > + 0, /* reloc */ > + 0, /* reloc */ > + MI_NOOP, > + MI_BATCH_BUFFER_END, > + }; > + > + exec_split_batch(fd, I915_EXEC_BLT, > + lri_ok, sizeof(lri_ok), > + 0); > + > + exec_batch_patched(fd, I915_EXEC_BLT, handle, > + store_reg, sizeof(store_reg), > + 2 * sizeof(uint32_t), /* reloc */ > + 0xbaadf00d); > +} > + > +static void test_invalid_length(const int fd, const uint32_t handle) > +{ > + const uint32_t ok_val = 0xbaadf00d; > + const uint32_t bad_val = 0xf00dbaad; > + const uint32_t noops[8192] = { 0, }; > + > + const uint32_t lri_ok[] = { > + MI_LOAD_REGISTER_IMM, > + BCS_GPR(0), > + ok_val, > + MI_BATCH_BUFFER_END, > + }; > + > + const uint32_t lri_bad[] = { > + MI_LOAD_REGISTER_IMM, > + BCS_GPR(0), > + bad_val, > + MI_BATCH_BUFFER_END, > + }; > + > + const uint32_t store_reg[] = { > + MI_STORE_REGISTER_MEM | (4 - 2), > + BCS_GPR(0), > + 0, /* reloc */ > + 0, /* reloc */ > + MI_NOOP, > + MI_BATCH_BUFFER_END, > + }; > + > + exec_batch(fd, I915_EXEC_BLT, handle, > + lri_ok, sizeof(lri_ok), > + 0); > + > + exec_batch_patched(fd, I915_EXEC_BLT, handle, > + store_reg, sizeof(store_reg), > + 2 * sizeof(uint32_t), /* reloc */ > + ok_val); > + > + exec_batch(fd, I915_EXEC_BLT, handle, > + lri_bad, 0, > + 0); > + > + exec_batch_patched(fd, I915_EXEC_BLT, handle, > + store_reg, sizeof(store_reg), > + 2 * sizeof(uint32_t), /* reloc */ > + ok_val); > + > + exec_batch(fd, I915_EXEC_BLT, handle, > + lri_ok, 4096, > + 0); > + > + igt_assert_eq(__gem_write(fd, handle, 0, noops, 4097), -EINVAL); > +} > + > +struct reg { > + uint32_t addr; > + uint32_t mask; > + bool masked_write; > + bool privileged; > +}; > + > +#define REG_M(ADDR, MASK, WM, P) { (ADDR), (MASK), (WM), (P) } > +#define REG(ADDR) REG_M(ADDR, 0xffffffff, false, false) > +#define REG_P(ADDR) REG_M(ADDR, 0xffffffff, false, true) > + > +static const struct reg regs[] = { > + REG_M(BCS_SWCTRL, 0x3, true, false), > + REG(BCS_GPR(0)), > + REG(BCS_GPR_UDW(0)), > + REG(BCS_GPR(1)), > + REG(BCS_GPR_UDW(1)), > + REG(BCS_GPR(2)), > + REG(BCS_GPR_UDW(2)), > + REG(BCS_GPR(3)), > + REG(BCS_GPR_UDW(3)), > + REG(BCS_GPR(4)), > + REG(BCS_GPR_UDW(4)), > + REG(BCS_GPR(5)), > + REG(BCS_GPR_UDW(5)), > + REG(BCS_GPR(6)), > + REG(BCS_GPR_UDW(6)), > + REG(BCS_GPR(7)), > + REG(BCS_GPR_UDW(7)), > + REG(BCS_GPR(8)), > + REG(BCS_GPR_UDW(8)), > + REG(BCS_GPR(9)), > + REG(BCS_GPR_UDW(9)), > + REG(BCS_GPR(10)), > + REG(BCS_GPR_UDW(10)), > + REG(BCS_GPR(11)), > + REG(BCS_GPR_UDW(11)), > + REG(BCS_GPR(12)), > + REG(BCS_GPR_UDW(12)), > + REG(BCS_GPR(13)), > + REG(BCS_GPR_UDW(13)), > + REG(BCS_GPR(14)), > + REG(BCS_GPR_UDW(14)), > + REG(BCS_GPR(15)), > + REG(BCS_GPR_UDW(15)), > + > + REG_P(0), > + REG_P(200000), > + > + REG_P(BCS_SWCTRL - 1), > + REG_P(BCS_SWCTRL - 2), > + REG_P(BCS_SWCTRL - 3), > + REG_P(BCS_SWCTRL - 4), > + REG_P(BCS_SWCTRL + 4), > + > + REG_P(BCS_GPR(0) - 1), > + REG_P(BCS_GPR(0) - 2), > + REG_P(BCS_GPR(0) - 3), > + REG_P(BCS_GPR(0) - 4), > + REG_P(BCS_GPR_UDW(15) + 4), > +}; > + > +static void test_register(const int fd, const uint32_t handle, > + const struct reg *r) > +{ > + const uint32_t lri_zero[] = { > + MI_LOAD_REGISTER_IMM, > + r->addr, > + r->masked_write ? 0xffff0000 : 0, > + MI_BATCH_BUFFER_END, > + }; > + > + const uint32_t lri_mask[] = { > + MI_LOAD_REGISTER_IMM, > + r->addr, > + r->masked_write ? (r->mask << 16) | r->mask : r->mask, > + MI_BATCH_BUFFER_END, > + }; > + > + const uint32_t store_reg[] = { > + MI_STORE_REGISTER_MEM | (4 - 2), > + r->addr, > + 0, /* reloc */ > + 0, /* reloc */ > + MI_NOOP, > + MI_BATCH_BUFFER_END, > + }; > + > + exec_batch(fd, I915_EXEC_BLT, handle, > + lri_mask, sizeof(lri_mask), > + r->privileged ? -EACCES : 0); > + > + exec_batch_patched(fd, I915_EXEC_BLT, handle, > + store_reg, sizeof(store_reg), > + 2 * sizeof(uint32_t), /* reloc */ > + r->privileged ? -EACCES : r->mask); > + > + exec_batch(fd, I915_EXEC_BLT, handle, > + lri_zero, sizeof(lri_zero), > + r->privileged ? -EACCES : 0); > + > + exec_batch_patched(fd, I915_EXEC_BLT, handle, > + store_reg, sizeof(store_reg), > + 2 * sizeof(uint32_t), /* reloc */ > + r->privileged ? -EACCES : 0); > +} > + > +static void test_valid_registers(const int fd, const uint32_t handle) > +{ > + int i; > + > + for (i = 0; i < ARRAY_SIZE(regs); i++) > + test_register(fd, handle, ®s[i]); > +} > + > +static long int read_reg(const int fd, const uint32_t handle, > + const uint32_t addr) > +{ > + const uint32_t store_reg[] = { > + MI_STORE_REGISTER_MEM | (4 - 2), > + addr, > + 0, /* reloc */ > + 0, /* reloc */ > + MI_NOOP, > + MI_BATCH_BUFFER_END, > + }; > + uint32_t target_bo; > + uint32_t value; > + long int ret; > + > + target_bo = gem_create(fd, HANDLE_SIZE); > + > + ret = __exec_batch_patched(fd, I915_EXEC_BLT, handle, > + store_reg, sizeof(store_reg), > + target_bo, 2 * sizeof(uint32_t), 0); > + > + if (ret) { > + igt_assert_lt(ret, 0); > + gem_close(fd, target_bo); > + return ret; > + } > + > + gem_sync(fd, handle); > + > + gem_read(fd, target_bo, 0, &value, sizeof(value)); > + > + gem_close(fd, target_bo); > + > + return value; > +} > + > +static int write_reg(const int fd, const uint32_t handle, > + const uint32_t addr, const uint32_t val) > +{ > + const uint32_t lri[] = { > + MI_LOAD_REGISTER_IMM, > + addr, > + val, > + MI_BATCH_BUFFER_END, > + }; > + > + return __exec_batch(fd, I915_EXEC_BLT, handle, > + lri, sizeof(lri)); > +} > + > +static void test_unaligned_access(const int fd, const uint32_t handle) > +{ > + const uint32_t addr = BCS_GPR(4); > + const uint32_t val = 0xbaadfead; > + const uint32_t pre = 0x12345678; > + const uint32_t post = 0x87654321; > + > + igt_assert_eq(write_reg(fd, handle, addr - 4, pre), 0); > + igt_assert_eq(write_reg(fd, handle, addr, val), 0); > + igt_assert_eq(write_reg(fd, handle, addr + 4, post), 0); > + > + igt_assert_eq(read_reg(fd, handle, addr - 4), pre); > + igt_assert_eq(read_reg(fd, handle, addr), val); > + igt_assert_eq(read_reg(fd, handle, addr + 4), post); > + > + for (int i = 0; i < 4; i++) { > + igt_assert_eq(write_reg(fd, handle, addr + i, val), 0); > + igt_assert_eq(read_reg(fd, handle, addr), val); > + > + igt_assert_eq(read_reg(fd, handle, addr + 1), val); > + igt_assert_eq(read_reg(fd, handle, addr + 2), val); > + igt_assert_eq(read_reg(fd, handle, addr + 3), val); > + igt_assert_eq(read_reg(fd, handle, addr + 4), post); > + igt_assert_eq(read_reg(fd, handle, addr - 3), pre); > + igt_assert_eq(read_reg(fd, handle, addr - 2), pre); > + igt_assert_eq(read_reg(fd, handle, addr - 1), pre); > + } > +} > + > +igt_main > +{ > + uint32_t handle; > + int fd; > + > + igt_fixture { > + fd = drm_open_driver(DRIVER_INTEL); > + igt_require_gem(fd); > + gem_require_blitter(fd); > + > + parser_version = command_parser_version(fd); > + /* igt_require(parser_version == 10); */ > + > + igt_require(gem_uses_ppgtt(fd)); > + igt_require(gem_has_blt(fd)); > + igt_require(intel_gen(intel_get_drm_devid(fd)) == 9); > + > + handle = gem_create(fd, HANDLE_SIZE); > + > + igt_fork_hang_detector(fd); > + } > + > + igt_subtest("secure-batches") > + test_secure_batches(fd); > + > + igt_subtest("allowed-all") > + test_allowed_all(fd, handle); > + > + igt_subtest("allowed-single") > + test_allowed_single(fd, handle); > + > + igt_subtest("bb-start-param") > + test_bb_start(fd, handle, BB_START_PARAM); > + > + igt_subtest("bb-start-out") > + test_bb_start(fd, handle, BB_START_OUT); > + > + igt_subtest("bb-secure") > + test_bb_secure(fd, handle); > + > + igt_subtest("bb-chained") > + test_bb_chained(fd, handle); > + > + igt_subtest("cmd-crossing-page") > + test_cmd_crossing_page(fd, handle); > + > + igt_subtest("batch-without-end") { > + const uint32_t noop[1024] = { 0 }; > + > + exec_batch(fd, I915_EXEC_BLT, handle, > + noop, sizeof(noop), > + -EINVAL); > + } > + > + igt_subtest("batch-zero-length") { > + const uint32_t noop[] = { 0, MI_BATCH_BUFFER_END }; > + > + exec_batch(fd, I915_EXEC_BLT, handle, > + noop, 0, > + -EINVAL); > + } > + > + igt_subtest("batch-invalid-length") > + test_invalid_length(fd, handle); > + > + igt_subtest("basic-rejected") { > + const uint32_t invalid_cmd[] = { > + INSTR_INVALID_CLIENT << INSTR_CLIENT_SHIFT, > + MI_BATCH_BUFFER_END, > + }; > + const uint32_t invalid_set_context[] = { > + MI_SET_CONTEXT | 32, /* invalid length */ > + MI_BATCH_BUFFER_END, > + }; > + > + exec_batch(fd, I915_EXEC_BLT, handle, > + invalid_cmd, sizeof(invalid_cmd), > + -EINVAL); > + > + exec_batch(fd, I915_EXEC_BLT, handle, > + invalid_set_context, sizeof(invalid_set_context), > + -EINVAL); > + } > + > + igt_subtest("valid-registers") > + test_valid_registers(fd, handle); > + > + igt_subtest("unaligned-access") > + test_unaligned_access(fd, handle); > + > + igt_subtest_group { > + igt_hang_t hang; > + > + igt_fixture igt_allow_hang(fd, 0, 0); > + > + igt_subtest("bb-start-cmd") > + test_bb_start(fd, handle, BB_START_CMD); > + > + igt_fixture igt_disallow_hang(fd, hang); > + } > + > + igt_fixture { > + igt_stop_hang_detector(); > + gem_close(fd, handle); > + > + close(fd); > + } > +} So why do you have so many unexplained syncs? -Chris
Chris Wilson <chris@chris-wilson.co.uk> writes: > Quoting Mika Kuoppala (2019-11-13 15:49:11) >> From: Mika Kuoppala <mika.kuoppala@intel.com> >> >> For testing blitter engine command parser on gen9. >> >> v2: bad jump offset >> v3: rebase >> v4: improve bb start and subcase it >> v5: fix presumed offsets (Jon) >> >> Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com> >> --- >> tests/Makefile.sources | 3 + >> tests/i915/gem_blt_parse.c | 997 ++++++++++++++++++++++++++ > > I was thinking gem_exec_parse_blt.c so the relationship with the > previous gem_exec_parse is clearer (both that this cmdparser, and that > this is a subset of the execbuf ABI). > >> tests/intel-ci/fast-feedback.testlist | 1 + >> tests/meson.build | 1 + >> 4 files changed, 1002 insertions(+) >> create mode 100644 tests/i915/gem_blt_parse.c >> >> diff --git a/tests/Makefile.sources b/tests/Makefile.sources >> index abf1e2fc..75c79edb 100644 >> --- a/tests/Makefile.sources >> +++ b/tests/Makefile.sources >> @@ -244,6 +244,9 @@ gem_exec_params_SOURCES = i915/gem_exec_params.c >> TESTS_progs += gem_exec_parse >> gem_exec_parse_SOURCES = i915/gem_exec_parse.c >> >> +TESTS_progs += gem_blt_parse >> +gem_blt_parse_SOURCES = i915/gem_blt_parse.c >> + >> TESTS_progs += gem_exec_reloc >> gem_exec_reloc_SOURCES = i915/gem_exec_reloc.c >> >> diff --git a/tests/i915/gem_blt_parse.c b/tests/i915/gem_blt_parse.c >> new file mode 100644 >> index 00000000..607afba9 >> --- /dev/null >> +++ b/tests/i915/gem_blt_parse.c >> @@ -0,0 +1,997 @@ >> +/* >> + * Copyright © 2018 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. >> + * >> + */ >> + >> +#include "igt.h" >> +#include <stdlib.h> >> +#include <stdint.h> >> +#include <stdio.h> >> +#include <errno.h> >> + >> +#include <drm.h> >> + >> +#ifndef I915_PARAM_CMD_PARSER_VERSION >> +#define I915_PARAM_CMD_PARSER_VERSION 28 >> +#endif >> + >> +#ifndef I915_PARAM_HAS_SECURE_BATCHES >> +#define I915_PARAM_HAS_SECURE_BATCHES 23 >> +#endif > > Now pulled in from uapi/i915_drm.h > >> + >> +/* To help craft commands known to be invalid across all engines */ >> +#define INSTR_CLIENT_SHIFT 29 >> +#define INSTR_INVALID_CLIENT 0x7 >> + >> +#define MI_LOAD_REGISTER_REG (0x2a << 23) >> +#define MI_STORE_REGISTER_MEM (0x24 << 23) >> +#define MI_ARB_ON_OFF (0x8 << 23) >> +#define MI_USER_INTERRUPT (0x02 << 23) >> +#define MI_FLUSH_DW (0x26 << 23) >> +#define MI_ARB_CHECK (0x05 << 23) >> +#define MI_REPORT_HEAD (0x07 << 23) >> +#define MI_SUSPEND_FLUSH (0x0b << 23) >> +#define MI_LOAD_SCAN_LINES_EXCL (0x13 << 23) >> +#define MI_UPDATE_GTT (0x23 << 23) >> + >> +#define BCS_SWCTRL 0x22200 >> +#define BCS_GPR_BASE 0x22600 >> +#define BCS_GPR(n) (0x22600 + (n) * 8) >> +#define BCS_GPR_UDW(n) (0x22600 + (n) * 8 + 4) >> + >> +#define HANDLE_SIZE 4096 >> + >> +static int parser_version; >> + >> +static int command_parser_version(int fd) >> +{ >> + int version = -1; >> + drm_i915_getparam_t gp; >> + >> + gp.param = I915_PARAM_CMD_PARSER_VERSION; >> + gp.value = &version; >> + >> + if (drmIoctl(fd, DRM_IOCTL_I915_GETPARAM, &gp) == 0) >> + return version; >> + >> + return -1; >> +} >> + >> +static int __exec_batch_patched(int fd, int engine, >> + uint32_t cmd_bo, const uint32_t *cmds, int size, >> + uint32_t target_bo, uint64_t target_offset, uint64_t target_delta) >> +{ >> + struct drm_i915_gem_execbuffer2 execbuf; >> + struct drm_i915_gem_exec_object2 obj[2]; >> + struct drm_i915_gem_relocation_entry reloc[1]; >> + >> + gem_write(fd, cmd_bo, 0, cmds, size); >> + >> + memset(obj, 0, sizeof(obj)); >> + obj[0].handle = target_bo; >> + obj[1].handle = cmd_bo; >> + >> + memset(reloc, 0, sizeof(reloc)); >> + reloc[0].offset = target_offset; >> + reloc[0].target_handle = target_bo; >> + reloc[0].delta = target_delta; >> + reloc[0].read_domains = I915_GEM_DOMAIN_COMMAND; >> + reloc[0].write_domain = I915_GEM_DOMAIN_COMMAND; >> + reloc[0].presumed_offset = -1; >> + >> + obj[1].relocs_ptr = to_user_pointer(reloc); >> + obj[1].relocation_count = 1; >> + >> + memset(&execbuf, 0, sizeof(execbuf)); >> + execbuf.buffers_ptr = to_user_pointer(obj); >> + execbuf.buffer_count = 2; >> + execbuf.batch_len = size; >> + execbuf.flags = engine; >> + >> + return __gem_execbuf(fd, &execbuf); >> +} >> + >> +static void exec_batch_patched(int fd, int engine, >> + uint32_t cmd_bo, const uint32_t *cmds, >> + int size, int patch_offset, >> + long int expected_value) >> +{ >> + const uint32_t target_bo = gem_create(fd, HANDLE_SIZE); >> + uint64_t actual_value = 0; >> + long int ret; >> + >> + ret = __exec_batch_patched(fd, engine, cmd_bo, cmds, size, target_bo, patch_offset, 0); >> + if (ret) { >> + igt_assert_lt(ret, 0); >> + gem_close(fd, target_bo); >> + igt_assert_eq(ret, expected_value); >> + return; >> + } >> + >> + gem_sync(fd, cmd_bo); > > What evil is this covering up? > >> + >> + gem_read(fd, target_bo, 0, &actual_value, sizeof(actual_value)); >> + >> + gem_close(fd, target_bo); >> + >> + igt_assert_eq(actual_value, expected_value); >> +} >> + >> +static int __exec_batch(int fd, int engine, uint32_t cmd_bo, >> + const uint32_t *cmds, int size) >> +{ >> + struct drm_i915_gem_execbuffer2 execbuf; >> + struct drm_i915_gem_exec_object2 obj[1]; >> + int ret; >> + >> + gem_write(fd, cmd_bo, 0, cmds, size); >> + >> + memset(obj, 0, sizeof(obj)); >> + obj[0].handle = cmd_bo; >> + >> + memset(&execbuf, 0, sizeof(execbuf)); >> + execbuf.buffers_ptr = to_user_pointer(obj); >> + execbuf.buffer_count = 1; >> + execbuf.batch_len = size; >> + execbuf.flags = engine; >> + >> + ret = __gem_execbuf(fd, &execbuf); >> + >> + if (!ret) >> + gem_sync(fd, cmd_bo); > > More evil? > >> + >> + return ret; >> +} >> + >> +#if 0 >> +static void print_batch(const uint32_t *cmds, const uint32_t sz) >> +{ >> + const int commands = sz / 4; >> + int i; >> + >> + igt_info("Batch size %d\n", sz); >> + for (i = 0; i < commands; i++) >> + igt_info("0x%08x: 0x%08x\n", i, cmds[i]); >> +} >> +#else >> +#define print_batch(cmds, size) >> +#endif >> + >> +#define exec_batch(fd, engine, bo, cmds, sz, expected) \ >> + print_batch(cmds, sz); \ >> + igt_assert_eq(__exec_batch(fd, engine, bo, cmds, sz), expected) >> + >> +static void exec_split_batch(int fd, int engine, const uint32_t *cmds, >> + int size, int expected_ret) >> +{ >> + struct drm_i915_gem_execbuffer2 execbuf; >> + struct drm_i915_gem_exec_object2 obj[1]; >> + uint32_t cmd_bo; >> + const uint32_t noop[1024] = { 0 }; >> + const int alloc_size = 4096 * 2; >> + const int actual_start_offset = 4096-sizeof(uint32_t); >> + >> + /* Allocate and fill a 2-page batch with noops */ >> + cmd_bo = gem_create(fd, alloc_size); >> + gem_write(fd, cmd_bo, 0, noop, sizeof(noop)); >> + gem_write(fd, cmd_bo, 4096, noop, sizeof(noop)); >> + >> + /* Write the provided commands such that the first dword >> + * of the command buffer is the last dword of the first >> + * page (i.e. the command is split across the two pages). >> + */ >> + gem_write(fd, cmd_bo, actual_start_offset, cmds, size); >> + >> + memset(obj, 0, sizeof(obj)); >> + obj[0].handle = cmd_bo; >> + >> + memset(&execbuf, 0, sizeof(execbuf)); >> + execbuf.buffers_ptr = to_user_pointer(obj); >> + execbuf.buffer_count = 1; >> + /* NB: We want batch_start_offset and batch_len to point to the block >> + * of the actual commands (i.e. at the last dword of the first page), >> + * but have to adjust both the start offset and length to meet the >> + * kernel driver's requirements on the alignment of those fields. >> + */ >> + execbuf.batch_start_offset = actual_start_offset & ~0x7; >> + execbuf.batch_len = >> + ALIGN(size + actual_start_offset - execbuf.batch_start_offset, >> + 0x8); >> + execbuf.flags = engine; >> + >> + igt_assert_eq(__gem_execbuf(fd, &execbuf), expected_ret); >> + >> + gem_sync(fd, cmd_bo); > > Yet more. > >> + gem_close(fd, cmd_bo); >> +} >> + >> +static void exec_batch_chained(int fd, int engine, >> + uint32_t cmd_bo, const uint32_t *cmds, >> + int size, int patch_offset, >> + uint64_t expected_value, >> + int expected_return) >> +{ >> + struct drm_i915_gem_execbuffer2 execbuf; >> + struct drm_i915_gem_exec_object2 obj[3]; >> + struct drm_i915_gem_relocation_entry reloc[1]; >> + struct drm_i915_gem_relocation_entry first_level_reloc; >> + >> + const uint32_t target_bo = gem_create(fd, 4096); >> + const uint32_t first_level_bo = gem_create(fd, 4096); >> + uint64_t actual_value = 0; >> + int ret; >> + >> + const uint32_t first_level_cmds[] = { >> + MI_BATCH_BUFFER_START | MI_BATCH_NON_SECURE_I965 | 1, >> + 0, >> + 0, >> + MI_BATCH_BUFFER_END, >> + }; >> + >> + gem_write(fd, first_level_bo, 0, >> + first_level_cmds, sizeof(first_level_cmds)); >> + gem_write(fd, cmd_bo, 0, cmds, size); >> + >> + memset(obj, 0, sizeof(obj)); >> + obj[0].handle = target_bo; >> + obj[1].handle = cmd_bo; >> + obj[2].handle = first_level_bo; >> + >> + memset(reloc, 0, sizeof(reloc)); >> + reloc[0].offset = patch_offset; >> + reloc[0].delta = 0; >> + reloc[0].target_handle = target_bo; >> + reloc[0].read_domains = I915_GEM_DOMAIN_COMMAND; >> + reloc[0].write_domain = I915_GEM_DOMAIN_COMMAND; >> + reloc[0].presumed_offset = -1; >> + >> + obj[1].relocation_count = 1; >> + obj[1].relocs_ptr = to_user_pointer(&reloc); >> + >> + memset(&first_level_reloc, 0, sizeof(first_level_reloc)); >> + first_level_reloc.offset = 4; >> + first_level_reloc.delta = 0; >> + first_level_reloc.target_handle = cmd_bo; >> + first_level_reloc.read_domains = I915_GEM_DOMAIN_COMMAND; >> + first_level_reloc.write_domain = 0; >> + obj[2].relocation_count = 1; >> + obj[2].relocs_ptr = to_user_pointer(&first_level_reloc); >> + >> + memset(&execbuf, 0, sizeof(execbuf)); >> + execbuf.buffers_ptr = to_user_pointer(obj); >> + execbuf.buffer_count = 3; >> + execbuf.batch_len = sizeof(first_level_cmds); >> + execbuf.flags = engine; >> + >> + ret = __gem_execbuf(fd, &execbuf); >> + if (expected_return && ret == expected_return) >> + goto out; >> + >> + gem_sync(fd, cmd_bo); > > What are you doing??? > >> + >> + gem_read(fd,target_bo, 0, &actual_value, sizeof(actual_value)); >> + >> +out: >> + if (!expected_return) >> + igt_assert_eq(expected_value, actual_value); >> + else >> + igt_assert_neq(expected_value, actual_value); >> + >> + gem_close(fd, first_level_bo); >> + gem_close(fd, target_bo); >> +} >> + >> +static void test_secure_batches(const int fd) >> +{ >> + int v = -1; >> + drm_i915_getparam_t gp; >> + >> + gp.param = I915_PARAM_HAS_SECURE_BATCHES; >> + gp.value = &v; >> + >> + igt_assert_eq(drmIoctl(fd, DRM_IOCTL_I915_GETPARAM, &gp), 0); >> + igt_assert_eq(v, 0); >> + >> + igt_assert(!gem_engine_has_mutable_submission(fd, I915_EXEC_BLT)); >> +} >> + >> +struct cmd { >> + uint32_t cmd; >> + int len; >> + const char *name; >> +}; >> + >> +#define CMD(C, L) { .cmd = (C), .len = (L), .name = #C } >> +#define CMD_N(C) { .cmd = (C), .len = 1, .name = #C } >> + >> +static const struct cmd allowed_cmds[] = { >> + CMD_N(MI_NOOP), >> + CMD_N(MI_USER_INTERRUPT), >> + CMD_N(MI_WAIT_FOR_EVENT), >> + CMD(MI_FLUSH_DW, 5), >> + CMD_N(MI_ARB_CHECK), >> + CMD_N(MI_REPORT_HEAD), >> + CMD_N(MI_FLUSH), >> + CMD_N(MI_ARB_ON_OFF), >> + CMD_N(MI_SUSPEND_FLUSH), >> + CMD(MI_LOAD_SCAN_LINES_INCL, 2), >> + CMD(MI_LOAD_SCAN_LINES_EXCL, 2), >> +}; >> + >> +static uint32_t *inject_cmd(uint32_t *batch, const uint32_t cmd, int len) >> +{ >> + int i = 0; >> + >> + batch[i++] = cmd; >> + >> + while (--len) >> + batch[i++] = 0; >> + >> + return &batch[i]; >> +} >> + >> +static unsigned long batch_num_cmds(const uint32_t * const batch_start, >> + const uint32_t * const batch_end) >> +{ >> + igt_assert_lte((unsigned long)batch_start, (unsigned long)batch_end); >> + >> + return batch_end - batch_start; >> +} >> + >> +static unsigned long batch_bytes(const uint32_t * const batch_start, >> + const uint32_t * const batch_end) >> +{ >> + const unsigned long bytes = batch_num_cmds(batch_start, batch_end) * 4; >> + >> + igt_assert(!(bytes & 0x7)); >> + >> + return bytes; >> +} >> + >> +static void test_allowed_all(const int fd, const uint32_t handle) >> +{ >> + uint32_t batch[4096]; >> + uint32_t *b = &batch[0]; >> + >> + for (int i = 0; i < ARRAY_SIZE(allowed_cmds); i++) >> + b = inject_cmd(b, allowed_cmds[i].cmd, >> + allowed_cmds[i].len); >> + >> + if (!(batch_num_cmds(batch, b) % 2)) >> + b = inject_cmd(b, MI_NOOP, 1); >> + >> + b = inject_cmd(b, MI_BATCH_BUFFER_END, 1); >> + >> + exec_batch(fd, I915_EXEC_BLT, handle, batch, batch_bytes(batch, b), 0); >> +} >> + >> +static void test_allowed_single(const int fd, const uint32_t handle) >> +{ >> + uint32_t batch[4096]; >> + int ret; >> + >> + for (int i = 0; i < ARRAY_SIZE(allowed_cmds); i++) { >> + uint32_t *b = &batch[0]; >> + >> + b = inject_cmd(b, allowed_cmds[i].cmd, >> + allowed_cmds[i].len); >> + >> + if (!(batch_num_cmds(batch, b) % 2)) >> + b = inject_cmd(b, MI_NOOP, 1); >> + >> + b = inject_cmd(b, MI_BATCH_BUFFER_END, 1); >> + >> + ret = __exec_batch(fd, I915_EXEC_BLT, handle, >> + batch, batch_bytes(batch, b)); >> + >> + igt_assert_eq(ret, 0); >> + }; >> +} >> + >> +static void test_bb_secure(const int fd, const uint32_t handle) >> +{ >> + struct drm_i915_gem_execbuffer2 execbuf; >> + struct drm_i915_gem_exec_object2 obj[1]; >> + struct drm_i915_gem_relocation_entry reloc[1]; >> + int ret; >> + >> + const uint32_t batch_secure[] = { >> + MI_BATCH_BUFFER_START | 1, >> + 12, >> + 0, >> + MI_NOOP, >> + MI_NOOP, >> + MI_BATCH_BUFFER_END, >> + }; >> + >> + gem_write(fd, handle, 0, batch_secure, sizeof(batch_secure)); >> + >> + memset(obj, 0, sizeof(obj)); >> + obj[0].handle = handle; >> + >> + memset(reloc, 0, sizeof(reloc)); >> + reloc[0].offset = 1 * sizeof(uint32_t); >> + reloc[0].target_handle = handle; >> + reloc[0].delta = 4 * sizeof(uint32_t); >> + reloc[0].read_domains = I915_GEM_DOMAIN_COMMAND; >> + reloc[0].write_domain = 0; >> + reloc[0].presumed_offset = -1; >> + >> + obj[0].relocs_ptr = to_user_pointer(reloc); >> + obj[0].relocation_count = 1; >> + >> + memset(&execbuf, 0, sizeof(execbuf)); >> + execbuf.buffers_ptr = to_user_pointer(obj); >> + execbuf.buffer_count = 1; >> + execbuf.batch_len = sizeof(batch_secure); >> + execbuf.flags = I915_EXEC_BLT; >> + >> + ret = __gem_execbuf(fd, &execbuf); >> + >> + igt_assert_eq(ret, -EACCES); >> +} >> + >> +#define BB_START_PARAM 0 >> +#define BB_START_OUT 1 >> +#define BB_START_CMD 2 >> + >> +static void test_bb_start(const int fd, const uint32_t handle, int test) >> +{ >> + struct drm_i915_gem_execbuffer2 execbuf; >> + struct drm_i915_gem_exec_object2 obj[2]; >> + struct drm_i915_gem_relocation_entry reloc[3]; >> + const uint32_t target_bo = gem_create(fd, 4096); >> + uint32_t *dst; >> + int ret; >> + unsigned int jump_off; >> + >> + const uint32_t batch[] = { >> + MI_NOOP, >> + MI_NOOP, >> + MI_NOOP, >> + MI_NOOP, >> + MI_STORE_DWORD_IMM, >> + 0, >> + 0, >> + 1, >> + MI_STORE_DWORD_IMM, >> + 4, >> + 0, >> + 2, >> + MI_COND_BATCH_BUFFER_END | 1, >> + 0, >> + 0, >> + 0, >> + MI_BATCH_BUFFER_START | MI_BATCH_NON_SECURE_I965 | 1, >> + 0, >> + 0, >> + MI_BATCH_BUFFER_END, >> + }; >> + >> + igt_require(gem_can_store_dword(fd, I915_EXEC_BLT)); >> + >> + switch (test) { >> + case BB_START_PARAM: >> + jump_off = 5 * sizeof(uint32_t); >> + break; >> + case BB_START_CMD: >> + jump_off = 8 * sizeof(uint32_t); >> + break; >> + default: >> + jump_off = 0xf00d0000; >> + } >> + >> + gem_write(fd, handle, 0, batch, sizeof(batch)); >> + >> + memset(obj, 0, sizeof(obj)); >> + obj[0].handle = target_bo; >> + obj[1].handle = handle; >> + >> + memset(reloc, 0, sizeof(reloc)); >> + reloc[0].offset = 5 * sizeof(uint32_t); >> + reloc[0].target_handle = obj[0].handle; >> + reloc[0].delta = 0; >> + reloc[0].read_domains = I915_GEM_DOMAIN_COMMAND; >> + reloc[0].write_domain = I915_GEM_DOMAIN_COMMAND; >> + reloc[0].presumed_offset = -1; >> + >> + reloc[1].offset = 9 * sizeof(uint32_t); >> + reloc[1].target_handle = obj[0].handle; >> + reloc[1].delta = 1 * sizeof(uint32_t); >> + reloc[1].read_domains = I915_GEM_DOMAIN_COMMAND; >> + reloc[1].write_domain = I915_GEM_DOMAIN_COMMAND; >> + reloc[1].presumed_offset = -1; >> + >> + reloc[2].offset = 17 * sizeof(uint32_t); >> + reloc[2].target_handle = obj[1].handle; >> + reloc[2].delta = jump_off; >> + reloc[2].read_domains = I915_GEM_DOMAIN_COMMAND; >> + reloc[2].write_domain = 0; >> + reloc[2].presumed_offset = -1; >> + >> + obj[1].relocs_ptr = to_user_pointer(reloc); >> + obj[1].relocation_count = 3; >> + >> + memset(&execbuf, 0, sizeof(execbuf)); >> + execbuf.buffers_ptr = to_user_pointer(obj); >> + execbuf.buffer_count = 2; >> + execbuf.batch_len = sizeof(batch); >> + execbuf.flags = I915_EXEC_BLT; >> + >> + dst = gem_mmap__cpu(fd, obj[0].handle, 0, 4096, >> + PROT_READ | PROT_WRITE); >> + >> + igt_assert_eq(dst[0], 0); >> + igt_assert_eq(dst[1], 0); >> + >> + ret = __gem_execbuf(fd, &execbuf); >> + >> + switch (test) { >> + case BB_START_PARAM: >> + igt_assert_eq(ret, -EINVAL); >> + break; >> + case BB_START_CMD: >> + igt_assert_eq(ret, 0); >> + >> + while (READ_ONCE(dst[0]) == 0) >> + ; >> + >> + while (READ_ONCE(dst[1]) == 0) >> + ; >> + >> + igt_assert_eq(dst[0], 1); >> + igt_assert_eq(dst[1], 2); >> + >> + igt_info("values now %x %x\n", dst[0], dst[1]); >> + >> + dst[0] = 0; >> + >> + igt_info("values now %x %x\n", dst[0], dst[1]); >> + >> + gem_sync(fd, handle); >> + >> + igt_info("values now %x %x\n", dst[0], dst[1]); >> + >> + igt_assert_eq(dst[0], 0); >> + igt_assert_eq(dst[1], 2); >> + >> + break; >> + >> + case BB_START_OUT: >> + igt_assert_eq(ret, -EINVAL); >> + break; >> + } >> + >> + gem_sync(fd, handle); >> + gem_munmap(dst, 4096); >> + gem_close(fd, target_bo); >> +} >> + >> +static void test_bb_chained(const int fd, const uint32_t handle) >> +{ >> + const uint32_t batch[] = { >> + (0x20 << 23) | 2, /* MI_STORE_DATA_IMM */ >> + 0, >> + 0, >> + 0xbaadf00d, >> + MI_NOOP, >> + MI_BATCH_BUFFER_END, >> + }; >> + >> + exec_batch_chained(fd, I915_EXEC_RENDER, >> + handle, >> + batch, sizeof(batch), >> + 4, >> + 0xbaadf00d, >> + 0); >> + >> + exec_batch_chained(fd, I915_EXEC_BLT, >> + handle, >> + batch, sizeof(batch), >> + 4, >> + 0xbaadf00d, >> + EPERM); >> +} >> + >> +static void test_cmd_crossing_page(const int fd, const uint32_t handle) >> +{ >> + const uint32_t lri_ok[] = { >> + MI_LOAD_REGISTER_IMM, >> + BCS_GPR(0), >> + 0xbaadf00d, >> + MI_BATCH_BUFFER_END, >> + }; >> + const uint32_t store_reg[] = { >> + MI_STORE_REGISTER_MEM | (4 - 2), >> + BCS_GPR(0), >> + 0, /* reloc */ >> + 0, /* reloc */ >> + MI_NOOP, >> + MI_BATCH_BUFFER_END, >> + }; >> + >> + exec_split_batch(fd, I915_EXEC_BLT, >> + lri_ok, sizeof(lri_ok), >> + 0); >> + >> + exec_batch_patched(fd, I915_EXEC_BLT, handle, >> + store_reg, sizeof(store_reg), >> + 2 * sizeof(uint32_t), /* reloc */ >> + 0xbaadf00d); >> +} >> + >> +static void test_invalid_length(const int fd, const uint32_t handle) >> +{ >> + const uint32_t ok_val = 0xbaadf00d; >> + const uint32_t bad_val = 0xf00dbaad; >> + const uint32_t noops[8192] = { 0, }; >> + >> + const uint32_t lri_ok[] = { >> + MI_LOAD_REGISTER_IMM, >> + BCS_GPR(0), >> + ok_val, >> + MI_BATCH_BUFFER_END, >> + }; >> + >> + const uint32_t lri_bad[] = { >> + MI_LOAD_REGISTER_IMM, >> + BCS_GPR(0), >> + bad_val, >> + MI_BATCH_BUFFER_END, >> + }; >> + >> + const uint32_t store_reg[] = { >> + MI_STORE_REGISTER_MEM | (4 - 2), >> + BCS_GPR(0), >> + 0, /* reloc */ >> + 0, /* reloc */ >> + MI_NOOP, >> + MI_BATCH_BUFFER_END, >> + }; >> + >> + exec_batch(fd, I915_EXEC_BLT, handle, >> + lri_ok, sizeof(lri_ok), >> + 0); >> + >> + exec_batch_patched(fd, I915_EXEC_BLT, handle, >> + store_reg, sizeof(store_reg), >> + 2 * sizeof(uint32_t), /* reloc */ >> + ok_val); >> + >> + exec_batch(fd, I915_EXEC_BLT, handle, >> + lri_bad, 0, >> + 0); >> + >> + exec_batch_patched(fd, I915_EXEC_BLT, handle, >> + store_reg, sizeof(store_reg), >> + 2 * sizeof(uint32_t), /* reloc */ >> + ok_val); >> + >> + exec_batch(fd, I915_EXEC_BLT, handle, >> + lri_ok, 4096, >> + 0); >> + >> + igt_assert_eq(__gem_write(fd, handle, 0, noops, 4097), -EINVAL); >> +} >> + >> +struct reg { >> + uint32_t addr; >> + uint32_t mask; >> + bool masked_write; >> + bool privileged; >> +}; >> + >> +#define REG_M(ADDR, MASK, WM, P) { (ADDR), (MASK), (WM), (P) } >> +#define REG(ADDR) REG_M(ADDR, 0xffffffff, false, false) >> +#define REG_P(ADDR) REG_M(ADDR, 0xffffffff, false, true) >> + >> +static const struct reg regs[] = { >> + REG_M(BCS_SWCTRL, 0x3, true, false), >> + REG(BCS_GPR(0)), >> + REG(BCS_GPR_UDW(0)), >> + REG(BCS_GPR(1)), >> + REG(BCS_GPR_UDW(1)), >> + REG(BCS_GPR(2)), >> + REG(BCS_GPR_UDW(2)), >> + REG(BCS_GPR(3)), >> + REG(BCS_GPR_UDW(3)), >> + REG(BCS_GPR(4)), >> + REG(BCS_GPR_UDW(4)), >> + REG(BCS_GPR(5)), >> + REG(BCS_GPR_UDW(5)), >> + REG(BCS_GPR(6)), >> + REG(BCS_GPR_UDW(6)), >> + REG(BCS_GPR(7)), >> + REG(BCS_GPR_UDW(7)), >> + REG(BCS_GPR(8)), >> + REG(BCS_GPR_UDW(8)), >> + REG(BCS_GPR(9)), >> + REG(BCS_GPR_UDW(9)), >> + REG(BCS_GPR(10)), >> + REG(BCS_GPR_UDW(10)), >> + REG(BCS_GPR(11)), >> + REG(BCS_GPR_UDW(11)), >> + REG(BCS_GPR(12)), >> + REG(BCS_GPR_UDW(12)), >> + REG(BCS_GPR(13)), >> + REG(BCS_GPR_UDW(13)), >> + REG(BCS_GPR(14)), >> + REG(BCS_GPR_UDW(14)), >> + REG(BCS_GPR(15)), >> + REG(BCS_GPR_UDW(15)), >> + >> + REG_P(0), >> + REG_P(200000), >> + >> + REG_P(BCS_SWCTRL - 1), >> + REG_P(BCS_SWCTRL - 2), >> + REG_P(BCS_SWCTRL - 3), >> + REG_P(BCS_SWCTRL - 4), >> + REG_P(BCS_SWCTRL + 4), >> + >> + REG_P(BCS_GPR(0) - 1), >> + REG_P(BCS_GPR(0) - 2), >> + REG_P(BCS_GPR(0) - 3), >> + REG_P(BCS_GPR(0) - 4), >> + REG_P(BCS_GPR_UDW(15) + 4), >> +}; >> + >> +static void test_register(const int fd, const uint32_t handle, >> + const struct reg *r) >> +{ >> + const uint32_t lri_zero[] = { >> + MI_LOAD_REGISTER_IMM, >> + r->addr, >> + r->masked_write ? 0xffff0000 : 0, >> + MI_BATCH_BUFFER_END, >> + }; >> + >> + const uint32_t lri_mask[] = { >> + MI_LOAD_REGISTER_IMM, >> + r->addr, >> + r->masked_write ? (r->mask << 16) | r->mask : r->mask, >> + MI_BATCH_BUFFER_END, >> + }; >> + >> + const uint32_t store_reg[] = { >> + MI_STORE_REGISTER_MEM | (4 - 2), >> + r->addr, >> + 0, /* reloc */ >> + 0, /* reloc */ >> + MI_NOOP, >> + MI_BATCH_BUFFER_END, >> + }; >> + >> + exec_batch(fd, I915_EXEC_BLT, handle, >> + lri_mask, sizeof(lri_mask), >> + r->privileged ? -EACCES : 0); >> + >> + exec_batch_patched(fd, I915_EXEC_BLT, handle, >> + store_reg, sizeof(store_reg), >> + 2 * sizeof(uint32_t), /* reloc */ >> + r->privileged ? -EACCES : r->mask); >> + >> + exec_batch(fd, I915_EXEC_BLT, handle, >> + lri_zero, sizeof(lri_zero), >> + r->privileged ? -EACCES : 0); >> + >> + exec_batch_patched(fd, I915_EXEC_BLT, handle, >> + store_reg, sizeof(store_reg), >> + 2 * sizeof(uint32_t), /* reloc */ >> + r->privileged ? -EACCES : 0); >> +} >> + >> +static void test_valid_registers(const int fd, const uint32_t handle) >> +{ >> + int i; >> + >> + for (i = 0; i < ARRAY_SIZE(regs); i++) >> + test_register(fd, handle, ®s[i]); >> +} >> + >> +static long int read_reg(const int fd, const uint32_t handle, >> + const uint32_t addr) >> +{ >> + const uint32_t store_reg[] = { >> + MI_STORE_REGISTER_MEM | (4 - 2), >> + addr, >> + 0, /* reloc */ >> + 0, /* reloc */ >> + MI_NOOP, >> + MI_BATCH_BUFFER_END, >> + }; >> + uint32_t target_bo; >> + uint32_t value; >> + long int ret; >> + >> + target_bo = gem_create(fd, HANDLE_SIZE); >> + >> + ret = __exec_batch_patched(fd, I915_EXEC_BLT, handle, >> + store_reg, sizeof(store_reg), >> + target_bo, 2 * sizeof(uint32_t), 0); >> + >> + if (ret) { >> + igt_assert_lt(ret, 0); >> + gem_close(fd, target_bo); >> + return ret; >> + } >> + >> + gem_sync(fd, handle); >> + >> + gem_read(fd, target_bo, 0, &value, sizeof(value)); >> + >> + gem_close(fd, target_bo); >> + >> + return value; >> +} >> + >> +static int write_reg(const int fd, const uint32_t handle, >> + const uint32_t addr, const uint32_t val) >> +{ >> + const uint32_t lri[] = { >> + MI_LOAD_REGISTER_IMM, >> + addr, >> + val, >> + MI_BATCH_BUFFER_END, >> + }; >> + >> + return __exec_batch(fd, I915_EXEC_BLT, handle, >> + lri, sizeof(lri)); >> +} >> + >> +static void test_unaligned_access(const int fd, const uint32_t handle) >> +{ >> + const uint32_t addr = BCS_GPR(4); >> + const uint32_t val = 0xbaadfead; >> + const uint32_t pre = 0x12345678; >> + const uint32_t post = 0x87654321; >> + >> + igt_assert_eq(write_reg(fd, handle, addr - 4, pre), 0); >> + igt_assert_eq(write_reg(fd, handle, addr, val), 0); >> + igt_assert_eq(write_reg(fd, handle, addr + 4, post), 0); >> + >> + igt_assert_eq(read_reg(fd, handle, addr - 4), pre); >> + igt_assert_eq(read_reg(fd, handle, addr), val); >> + igt_assert_eq(read_reg(fd, handle, addr + 4), post); >> + >> + for (int i = 0; i < 4; i++) { >> + igt_assert_eq(write_reg(fd, handle, addr + i, val), 0); >> + igt_assert_eq(read_reg(fd, handle, addr), val); >> + >> + igt_assert_eq(read_reg(fd, handle, addr + 1), val); >> + igt_assert_eq(read_reg(fd, handle, addr + 2), val); >> + igt_assert_eq(read_reg(fd, handle, addr + 3), val); >> + igt_assert_eq(read_reg(fd, handle, addr + 4), post); >> + igt_assert_eq(read_reg(fd, handle, addr - 3), pre); >> + igt_assert_eq(read_reg(fd, handle, addr - 2), pre); >> + igt_assert_eq(read_reg(fd, handle, addr - 1), pre); >> + } >> +} >> + >> +igt_main >> +{ >> + uint32_t handle; >> + int fd; >> + >> + igt_fixture { >> + fd = drm_open_driver(DRIVER_INTEL); >> + igt_require_gem(fd); >> + gem_require_blitter(fd); >> + >> + parser_version = command_parser_version(fd); >> + /* igt_require(parser_version == 10); */ >> + >> + igt_require(gem_uses_ppgtt(fd)); >> + igt_require(gem_has_blt(fd)); >> + igt_require(intel_gen(intel_get_drm_devid(fd)) == 9); >> + >> + handle = gem_create(fd, HANDLE_SIZE); >> + >> + igt_fork_hang_detector(fd); >> + } >> + >> + igt_subtest("secure-batches") >> + test_secure_batches(fd); >> + >> + igt_subtest("allowed-all") >> + test_allowed_all(fd, handle); >> + >> + igt_subtest("allowed-single") >> + test_allowed_single(fd, handle); >> + >> + igt_subtest("bb-start-param") >> + test_bb_start(fd, handle, BB_START_PARAM); >> + >> + igt_subtest("bb-start-out") >> + test_bb_start(fd, handle, BB_START_OUT); >> + >> + igt_subtest("bb-secure") >> + test_bb_secure(fd, handle); >> + >> + igt_subtest("bb-chained") >> + test_bb_chained(fd, handle); >> + >> + igt_subtest("cmd-crossing-page") >> + test_cmd_crossing_page(fd, handle); >> + >> + igt_subtest("batch-without-end") { >> + const uint32_t noop[1024] = { 0 }; >> + >> + exec_batch(fd, I915_EXEC_BLT, handle, >> + noop, sizeof(noop), >> + -EINVAL); >> + } >> + >> + igt_subtest("batch-zero-length") { >> + const uint32_t noop[] = { 0, MI_BATCH_BUFFER_END }; >> + >> + exec_batch(fd, I915_EXEC_BLT, handle, >> + noop, 0, >> + -EINVAL); >> + } >> + >> + igt_subtest("batch-invalid-length") >> + test_invalid_length(fd, handle); >> + >> + igt_subtest("basic-rejected") { >> + const uint32_t invalid_cmd[] = { >> + INSTR_INVALID_CLIENT << INSTR_CLIENT_SHIFT, >> + MI_BATCH_BUFFER_END, >> + }; >> + const uint32_t invalid_set_context[] = { >> + MI_SET_CONTEXT | 32, /* invalid length */ >> + MI_BATCH_BUFFER_END, >> + }; >> + >> + exec_batch(fd, I915_EXEC_BLT, handle, >> + invalid_cmd, sizeof(invalid_cmd), >> + -EINVAL); >> + >> + exec_batch(fd, I915_EXEC_BLT, handle, >> + invalid_set_context, sizeof(invalid_set_context), >> + -EINVAL); >> + } >> + >> + igt_subtest("valid-registers") >> + test_valid_registers(fd, handle); >> + >> + igt_subtest("unaligned-access") >> + test_unaligned_access(fd, handle); >> + >> + igt_subtest_group { >> + igt_hang_t hang; >> + >> + igt_fixture igt_allow_hang(fd, 0, 0); >> + >> + igt_subtest("bb-start-cmd") >> + test_bb_start(fd, handle, BB_START_CMD); >> + >> + igt_fixture igt_disallow_hang(fd, hang); >> + } >> + >> + igt_fixture { >> + igt_stop_hang_detector(); >> + gem_close(fd, handle); >> + >> + close(fd); >> + } >> +} > > So why do you have so many unexplained syncs? Have to check if gem_exec_parse.c has them. I remember using it as a skeleton for this. I can't explain them so lets throw them out. -Mika > -Chris
On Wed, Nov 13, 2019 at 05:49:11PM +0200, Mika Kuoppala wrote: > From: Mika Kuoppala <mika.kuoppala@intel.com> > > For testing blitter engine command parser on gen9. > > v2: bad jump offset > v3: rebase > v4: improve bb start and subcase it > v5: fix presumed offsets (Jon) > > Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com> Since it was only fixed last minute (I've heard) I think we need a testcase with more than 32 instructions for backjumps in the batch. Because we don't clear the bits correctly on 64 platforms, or well didn't in the original set of patches: Author: Ben Hutchings <ben@decadent.org.uk> Date: Mon Nov 11 08:13:24 2019 -0800 drm/i915/cmdparser: Fix jump whitelist clearing Might need to play around a bit since my brain isn't on yet and I'm babbling garbage potentially :-) But imo we should have a testcase for this oversight. -Daniel > --- > tests/Makefile.sources | 3 + > tests/i915/gem_blt_parse.c | 997 ++++++++++++++++++++++++++ > tests/intel-ci/fast-feedback.testlist | 1 + > tests/meson.build | 1 + > 4 files changed, 1002 insertions(+) > create mode 100644 tests/i915/gem_blt_parse.c > > diff --git a/tests/Makefile.sources b/tests/Makefile.sources > index abf1e2fc..75c79edb 100644 > --- a/tests/Makefile.sources > +++ b/tests/Makefile.sources > @@ -244,6 +244,9 @@ gem_exec_params_SOURCES = i915/gem_exec_params.c > TESTS_progs += gem_exec_parse > gem_exec_parse_SOURCES = i915/gem_exec_parse.c > > +TESTS_progs += gem_blt_parse > +gem_blt_parse_SOURCES = i915/gem_blt_parse.c > + > TESTS_progs += gem_exec_reloc > gem_exec_reloc_SOURCES = i915/gem_exec_reloc.c > > diff --git a/tests/i915/gem_blt_parse.c b/tests/i915/gem_blt_parse.c > new file mode 100644 > index 00000000..607afba9 > --- /dev/null > +++ b/tests/i915/gem_blt_parse.c > @@ -0,0 +1,997 @@ > +/* > + * Copyright © 2018 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. > + * > + */ > + > +#include "igt.h" > +#include <stdlib.h> > +#include <stdint.h> > +#include <stdio.h> > +#include <errno.h> > + > +#include <drm.h> > + > +#ifndef I915_PARAM_CMD_PARSER_VERSION > +#define I915_PARAM_CMD_PARSER_VERSION 28 > +#endif > + > +#ifndef I915_PARAM_HAS_SECURE_BATCHES > +#define I915_PARAM_HAS_SECURE_BATCHES 23 > +#endif > + > +/* To help craft commands known to be invalid across all engines */ > +#define INSTR_CLIENT_SHIFT 29 > +#define INSTR_INVALID_CLIENT 0x7 > + > +#define MI_LOAD_REGISTER_REG (0x2a << 23) > +#define MI_STORE_REGISTER_MEM (0x24 << 23) > +#define MI_ARB_ON_OFF (0x8 << 23) > +#define MI_USER_INTERRUPT (0x02 << 23) > +#define MI_FLUSH_DW (0x26 << 23) > +#define MI_ARB_CHECK (0x05 << 23) > +#define MI_REPORT_HEAD (0x07 << 23) > +#define MI_SUSPEND_FLUSH (0x0b << 23) > +#define MI_LOAD_SCAN_LINES_EXCL (0x13 << 23) > +#define MI_UPDATE_GTT (0x23 << 23) > + > +#define BCS_SWCTRL 0x22200 > +#define BCS_GPR_BASE 0x22600 > +#define BCS_GPR(n) (0x22600 + (n) * 8) > +#define BCS_GPR_UDW(n) (0x22600 + (n) * 8 + 4) > + > +#define HANDLE_SIZE 4096 > + > +static int parser_version; > + > +static int command_parser_version(int fd) > +{ > + int version = -1; > + drm_i915_getparam_t gp; > + > + gp.param = I915_PARAM_CMD_PARSER_VERSION; > + gp.value = &version; > + > + if (drmIoctl(fd, DRM_IOCTL_I915_GETPARAM, &gp) == 0) > + return version; > + > + return -1; > +} > + > +static int __exec_batch_patched(int fd, int engine, > + uint32_t cmd_bo, const uint32_t *cmds, int size, > + uint32_t target_bo, uint64_t target_offset, uint64_t target_delta) > +{ > + struct drm_i915_gem_execbuffer2 execbuf; > + struct drm_i915_gem_exec_object2 obj[2]; > + struct drm_i915_gem_relocation_entry reloc[1]; > + > + gem_write(fd, cmd_bo, 0, cmds, size); > + > + memset(obj, 0, sizeof(obj)); > + obj[0].handle = target_bo; > + obj[1].handle = cmd_bo; > + > + memset(reloc, 0, sizeof(reloc)); > + reloc[0].offset = target_offset; > + reloc[0].target_handle = target_bo; > + reloc[0].delta = target_delta; > + reloc[0].read_domains = I915_GEM_DOMAIN_COMMAND; > + reloc[0].write_domain = I915_GEM_DOMAIN_COMMAND; > + reloc[0].presumed_offset = -1; > + > + obj[1].relocs_ptr = to_user_pointer(reloc); > + obj[1].relocation_count = 1; > + > + memset(&execbuf, 0, sizeof(execbuf)); > + execbuf.buffers_ptr = to_user_pointer(obj); > + execbuf.buffer_count = 2; > + execbuf.batch_len = size; > + execbuf.flags = engine; > + > + return __gem_execbuf(fd, &execbuf); > +} > + > +static void exec_batch_patched(int fd, int engine, > + uint32_t cmd_bo, const uint32_t *cmds, > + int size, int patch_offset, > + long int expected_value) > +{ > + const uint32_t target_bo = gem_create(fd, HANDLE_SIZE); > + uint64_t actual_value = 0; > + long int ret; > + > + ret = __exec_batch_patched(fd, engine, cmd_bo, cmds, size, target_bo, patch_offset, 0); > + if (ret) { > + igt_assert_lt(ret, 0); > + gem_close(fd, target_bo); > + igt_assert_eq(ret, expected_value); > + return; > + } > + > + gem_sync(fd, cmd_bo); > + > + gem_read(fd, target_bo, 0, &actual_value, sizeof(actual_value)); > + > + gem_close(fd, target_bo); > + > + igt_assert_eq(actual_value, expected_value); > +} > + > +static int __exec_batch(int fd, int engine, uint32_t cmd_bo, > + const uint32_t *cmds, int size) > +{ > + struct drm_i915_gem_execbuffer2 execbuf; > + struct drm_i915_gem_exec_object2 obj[1]; > + int ret; > + > + gem_write(fd, cmd_bo, 0, cmds, size); > + > + memset(obj, 0, sizeof(obj)); > + obj[0].handle = cmd_bo; > + > + memset(&execbuf, 0, sizeof(execbuf)); > + execbuf.buffers_ptr = to_user_pointer(obj); > + execbuf.buffer_count = 1; > + execbuf.batch_len = size; > + execbuf.flags = engine; > + > + ret = __gem_execbuf(fd, &execbuf); > + > + if (!ret) > + gem_sync(fd, cmd_bo); > + > + return ret; > +} > + > +#if 0 > +static void print_batch(const uint32_t *cmds, const uint32_t sz) > +{ > + const int commands = sz / 4; > + int i; > + > + igt_info("Batch size %d\n", sz); > + for (i = 0; i < commands; i++) > + igt_info("0x%08x: 0x%08x\n", i, cmds[i]); > +} > +#else > +#define print_batch(cmds, size) > +#endif > + > +#define exec_batch(fd, engine, bo, cmds, sz, expected) \ > + print_batch(cmds, sz); \ > + igt_assert_eq(__exec_batch(fd, engine, bo, cmds, sz), expected) > + > +static void exec_split_batch(int fd, int engine, const uint32_t *cmds, > + int size, int expected_ret) > +{ > + struct drm_i915_gem_execbuffer2 execbuf; > + struct drm_i915_gem_exec_object2 obj[1]; > + uint32_t cmd_bo; > + const uint32_t noop[1024] = { 0 }; > + const int alloc_size = 4096 * 2; > + const int actual_start_offset = 4096-sizeof(uint32_t); > + > + /* Allocate and fill a 2-page batch with noops */ > + cmd_bo = gem_create(fd, alloc_size); > + gem_write(fd, cmd_bo, 0, noop, sizeof(noop)); > + gem_write(fd, cmd_bo, 4096, noop, sizeof(noop)); > + > + /* Write the provided commands such that the first dword > + * of the command buffer is the last dword of the first > + * page (i.e. the command is split across the two pages). > + */ > + gem_write(fd, cmd_bo, actual_start_offset, cmds, size); > + > + memset(obj, 0, sizeof(obj)); > + obj[0].handle = cmd_bo; > + > + memset(&execbuf, 0, sizeof(execbuf)); > + execbuf.buffers_ptr = to_user_pointer(obj); > + execbuf.buffer_count = 1; > + /* NB: We want batch_start_offset and batch_len to point to the block > + * of the actual commands (i.e. at the last dword of the first page), > + * but have to adjust both the start offset and length to meet the > + * kernel driver's requirements on the alignment of those fields. > + */ > + execbuf.batch_start_offset = actual_start_offset & ~0x7; > + execbuf.batch_len = > + ALIGN(size + actual_start_offset - execbuf.batch_start_offset, > + 0x8); > + execbuf.flags = engine; > + > + igt_assert_eq(__gem_execbuf(fd, &execbuf), expected_ret); > + > + gem_sync(fd, cmd_bo); > + gem_close(fd, cmd_bo); > +} > + > +static void exec_batch_chained(int fd, int engine, > + uint32_t cmd_bo, const uint32_t *cmds, > + int size, int patch_offset, > + uint64_t expected_value, > + int expected_return) > +{ > + struct drm_i915_gem_execbuffer2 execbuf; > + struct drm_i915_gem_exec_object2 obj[3]; > + struct drm_i915_gem_relocation_entry reloc[1]; > + struct drm_i915_gem_relocation_entry first_level_reloc; > + > + const uint32_t target_bo = gem_create(fd, 4096); > + const uint32_t first_level_bo = gem_create(fd, 4096); > + uint64_t actual_value = 0; > + int ret; > + > + const uint32_t first_level_cmds[] = { > + MI_BATCH_BUFFER_START | MI_BATCH_NON_SECURE_I965 | 1, > + 0, > + 0, > + MI_BATCH_BUFFER_END, > + }; > + > + gem_write(fd, first_level_bo, 0, > + first_level_cmds, sizeof(first_level_cmds)); > + gem_write(fd, cmd_bo, 0, cmds, size); > + > + memset(obj, 0, sizeof(obj)); > + obj[0].handle = target_bo; > + obj[1].handle = cmd_bo; > + obj[2].handle = first_level_bo; > + > + memset(reloc, 0, sizeof(reloc)); > + reloc[0].offset = patch_offset; > + reloc[0].delta = 0; > + reloc[0].target_handle = target_bo; > + reloc[0].read_domains = I915_GEM_DOMAIN_COMMAND; > + reloc[0].write_domain = I915_GEM_DOMAIN_COMMAND; > + reloc[0].presumed_offset = -1; > + > + obj[1].relocation_count = 1; > + obj[1].relocs_ptr = to_user_pointer(&reloc); > + > + memset(&first_level_reloc, 0, sizeof(first_level_reloc)); > + first_level_reloc.offset = 4; > + first_level_reloc.delta = 0; > + first_level_reloc.target_handle = cmd_bo; > + first_level_reloc.read_domains = I915_GEM_DOMAIN_COMMAND; > + first_level_reloc.write_domain = 0; > + obj[2].relocation_count = 1; > + obj[2].relocs_ptr = to_user_pointer(&first_level_reloc); > + > + memset(&execbuf, 0, sizeof(execbuf)); > + execbuf.buffers_ptr = to_user_pointer(obj); > + execbuf.buffer_count = 3; > + execbuf.batch_len = sizeof(first_level_cmds); > + execbuf.flags = engine; > + > + ret = __gem_execbuf(fd, &execbuf); > + if (expected_return && ret == expected_return) > + goto out; > + > + gem_sync(fd, cmd_bo); > + > + gem_read(fd,target_bo, 0, &actual_value, sizeof(actual_value)); > + > +out: > + if (!expected_return) > + igt_assert_eq(expected_value, actual_value); > + else > + igt_assert_neq(expected_value, actual_value); > + > + gem_close(fd, first_level_bo); > + gem_close(fd, target_bo); > +} > + > +static void test_secure_batches(const int fd) > +{ > + int v = -1; > + drm_i915_getparam_t gp; > + > + gp.param = I915_PARAM_HAS_SECURE_BATCHES; > + gp.value = &v; > + > + igt_assert_eq(drmIoctl(fd, DRM_IOCTL_I915_GETPARAM, &gp), 0); > + igt_assert_eq(v, 0); > + > + igt_assert(!gem_engine_has_mutable_submission(fd, I915_EXEC_BLT)); > +} > + > +struct cmd { > + uint32_t cmd; > + int len; > + const char *name; > +}; > + > +#define CMD(C, L) { .cmd = (C), .len = (L), .name = #C } > +#define CMD_N(C) { .cmd = (C), .len = 1, .name = #C } > + > +static const struct cmd allowed_cmds[] = { > + CMD_N(MI_NOOP), > + CMD_N(MI_USER_INTERRUPT), > + CMD_N(MI_WAIT_FOR_EVENT), > + CMD(MI_FLUSH_DW, 5), > + CMD_N(MI_ARB_CHECK), > + CMD_N(MI_REPORT_HEAD), > + CMD_N(MI_FLUSH), > + CMD_N(MI_ARB_ON_OFF), > + CMD_N(MI_SUSPEND_FLUSH), > + CMD(MI_LOAD_SCAN_LINES_INCL, 2), > + CMD(MI_LOAD_SCAN_LINES_EXCL, 2), > +}; > + > +static uint32_t *inject_cmd(uint32_t *batch, const uint32_t cmd, int len) > +{ > + int i = 0; > + > + batch[i++] = cmd; > + > + while (--len) > + batch[i++] = 0; > + > + return &batch[i]; > +} > + > +static unsigned long batch_num_cmds(const uint32_t * const batch_start, > + const uint32_t * const batch_end) > +{ > + igt_assert_lte((unsigned long)batch_start, (unsigned long)batch_end); > + > + return batch_end - batch_start; > +} > + > +static unsigned long batch_bytes(const uint32_t * const batch_start, > + const uint32_t * const batch_end) > +{ > + const unsigned long bytes = batch_num_cmds(batch_start, batch_end) * 4; > + > + igt_assert(!(bytes & 0x7)); > + > + return bytes; > +} > + > +static void test_allowed_all(const int fd, const uint32_t handle) > +{ > + uint32_t batch[4096]; > + uint32_t *b = &batch[0]; > + > + for (int i = 0; i < ARRAY_SIZE(allowed_cmds); i++) > + b = inject_cmd(b, allowed_cmds[i].cmd, > + allowed_cmds[i].len); > + > + if (!(batch_num_cmds(batch, b) % 2)) > + b = inject_cmd(b, MI_NOOP, 1); > + > + b = inject_cmd(b, MI_BATCH_BUFFER_END, 1); > + > + exec_batch(fd, I915_EXEC_BLT, handle, batch, batch_bytes(batch, b), 0); > +} > + > +static void test_allowed_single(const int fd, const uint32_t handle) > +{ > + uint32_t batch[4096]; > + int ret; > + > + for (int i = 0; i < ARRAY_SIZE(allowed_cmds); i++) { > + uint32_t *b = &batch[0]; > + > + b = inject_cmd(b, allowed_cmds[i].cmd, > + allowed_cmds[i].len); > + > + if (!(batch_num_cmds(batch, b) % 2)) > + b = inject_cmd(b, MI_NOOP, 1); > + > + b = inject_cmd(b, MI_BATCH_BUFFER_END, 1); > + > + ret = __exec_batch(fd, I915_EXEC_BLT, handle, > + batch, batch_bytes(batch, b)); > + > + igt_assert_eq(ret, 0); > + }; > +} > + > +static void test_bb_secure(const int fd, const uint32_t handle) > +{ > + struct drm_i915_gem_execbuffer2 execbuf; > + struct drm_i915_gem_exec_object2 obj[1]; > + struct drm_i915_gem_relocation_entry reloc[1]; > + int ret; > + > + const uint32_t batch_secure[] = { > + MI_BATCH_BUFFER_START | 1, > + 12, > + 0, > + MI_NOOP, > + MI_NOOP, > + MI_BATCH_BUFFER_END, > + }; > + > + gem_write(fd, handle, 0, batch_secure, sizeof(batch_secure)); > + > + memset(obj, 0, sizeof(obj)); > + obj[0].handle = handle; > + > + memset(reloc, 0, sizeof(reloc)); > + reloc[0].offset = 1 * sizeof(uint32_t); > + reloc[0].target_handle = handle; > + reloc[0].delta = 4 * sizeof(uint32_t); > + reloc[0].read_domains = I915_GEM_DOMAIN_COMMAND; > + reloc[0].write_domain = 0; > + reloc[0].presumed_offset = -1; > + > + obj[0].relocs_ptr = to_user_pointer(reloc); > + obj[0].relocation_count = 1; > + > + memset(&execbuf, 0, sizeof(execbuf)); > + execbuf.buffers_ptr = to_user_pointer(obj); > + execbuf.buffer_count = 1; > + execbuf.batch_len = sizeof(batch_secure); > + execbuf.flags = I915_EXEC_BLT; > + > + ret = __gem_execbuf(fd, &execbuf); > + > + igt_assert_eq(ret, -EACCES); > +} > + > +#define BB_START_PARAM 0 > +#define BB_START_OUT 1 > +#define BB_START_CMD 2 > + > +static void test_bb_start(const int fd, const uint32_t handle, int test) > +{ > + struct drm_i915_gem_execbuffer2 execbuf; > + struct drm_i915_gem_exec_object2 obj[2]; > + struct drm_i915_gem_relocation_entry reloc[3]; > + const uint32_t target_bo = gem_create(fd, 4096); > + uint32_t *dst; > + int ret; > + unsigned int jump_off; > + > + const uint32_t batch[] = { > + MI_NOOP, > + MI_NOOP, > + MI_NOOP, > + MI_NOOP, > + MI_STORE_DWORD_IMM, > + 0, > + 0, > + 1, > + MI_STORE_DWORD_IMM, > + 4, > + 0, > + 2, > + MI_COND_BATCH_BUFFER_END | 1, > + 0, > + 0, > + 0, > + MI_BATCH_BUFFER_START | MI_BATCH_NON_SECURE_I965 | 1, > + 0, > + 0, > + MI_BATCH_BUFFER_END, > + }; > + > + igt_require(gem_can_store_dword(fd, I915_EXEC_BLT)); > + > + switch (test) { > + case BB_START_PARAM: > + jump_off = 5 * sizeof(uint32_t); > + break; > + case BB_START_CMD: > + jump_off = 8 * sizeof(uint32_t); > + break; > + default: > + jump_off = 0xf00d0000; > + } > + > + gem_write(fd, handle, 0, batch, sizeof(batch)); > + > + memset(obj, 0, sizeof(obj)); > + obj[0].handle = target_bo; > + obj[1].handle = handle; > + > + memset(reloc, 0, sizeof(reloc)); > + reloc[0].offset = 5 * sizeof(uint32_t); > + reloc[0].target_handle = obj[0].handle; > + reloc[0].delta = 0; > + reloc[0].read_domains = I915_GEM_DOMAIN_COMMAND; > + reloc[0].write_domain = I915_GEM_DOMAIN_COMMAND; > + reloc[0].presumed_offset = -1; > + > + reloc[1].offset = 9 * sizeof(uint32_t); > + reloc[1].target_handle = obj[0].handle; > + reloc[1].delta = 1 * sizeof(uint32_t); > + reloc[1].read_domains = I915_GEM_DOMAIN_COMMAND; > + reloc[1].write_domain = I915_GEM_DOMAIN_COMMAND; > + reloc[1].presumed_offset = -1; > + > + reloc[2].offset = 17 * sizeof(uint32_t); > + reloc[2].target_handle = obj[1].handle; > + reloc[2].delta = jump_off; > + reloc[2].read_domains = I915_GEM_DOMAIN_COMMAND; > + reloc[2].write_domain = 0; > + reloc[2].presumed_offset = -1; > + > + obj[1].relocs_ptr = to_user_pointer(reloc); > + obj[1].relocation_count = 3; > + > + memset(&execbuf, 0, sizeof(execbuf)); > + execbuf.buffers_ptr = to_user_pointer(obj); > + execbuf.buffer_count = 2; > + execbuf.batch_len = sizeof(batch); > + execbuf.flags = I915_EXEC_BLT; > + > + dst = gem_mmap__cpu(fd, obj[0].handle, 0, 4096, > + PROT_READ | PROT_WRITE); > + > + igt_assert_eq(dst[0], 0); > + igt_assert_eq(dst[1], 0); > + > + ret = __gem_execbuf(fd, &execbuf); > + > + switch (test) { > + case BB_START_PARAM: > + igt_assert_eq(ret, -EINVAL); > + break; > + case BB_START_CMD: > + igt_assert_eq(ret, 0); > + > + while (READ_ONCE(dst[0]) == 0) > + ; > + > + while (READ_ONCE(dst[1]) == 0) > + ; > + > + igt_assert_eq(dst[0], 1); > + igt_assert_eq(dst[1], 2); > + > + igt_info("values now %x %x\n", dst[0], dst[1]); > + > + dst[0] = 0; > + > + igt_info("values now %x %x\n", dst[0], dst[1]); > + > + gem_sync(fd, handle); > + > + igt_info("values now %x %x\n", dst[0], dst[1]); > + > + igt_assert_eq(dst[0], 0); > + igt_assert_eq(dst[1], 2); > + > + break; > + > + case BB_START_OUT: > + igt_assert_eq(ret, -EINVAL); > + break; > + } > + > + gem_sync(fd, handle); > + gem_munmap(dst, 4096); > + gem_close(fd, target_bo); > +} > + > +static void test_bb_chained(const int fd, const uint32_t handle) > +{ > + const uint32_t batch[] = { > + (0x20 << 23) | 2, /* MI_STORE_DATA_IMM */ > + 0, > + 0, > + 0xbaadf00d, > + MI_NOOP, > + MI_BATCH_BUFFER_END, > + }; > + > + exec_batch_chained(fd, I915_EXEC_RENDER, > + handle, > + batch, sizeof(batch), > + 4, > + 0xbaadf00d, > + 0); > + > + exec_batch_chained(fd, I915_EXEC_BLT, > + handle, > + batch, sizeof(batch), > + 4, > + 0xbaadf00d, > + EPERM); > +} > + > +static void test_cmd_crossing_page(const int fd, const uint32_t handle) > +{ > + const uint32_t lri_ok[] = { > + MI_LOAD_REGISTER_IMM, > + BCS_GPR(0), > + 0xbaadf00d, > + MI_BATCH_BUFFER_END, > + }; > + const uint32_t store_reg[] = { > + MI_STORE_REGISTER_MEM | (4 - 2), > + BCS_GPR(0), > + 0, /* reloc */ > + 0, /* reloc */ > + MI_NOOP, > + MI_BATCH_BUFFER_END, > + }; > + > + exec_split_batch(fd, I915_EXEC_BLT, > + lri_ok, sizeof(lri_ok), > + 0); > + > + exec_batch_patched(fd, I915_EXEC_BLT, handle, > + store_reg, sizeof(store_reg), > + 2 * sizeof(uint32_t), /* reloc */ > + 0xbaadf00d); > +} > + > +static void test_invalid_length(const int fd, const uint32_t handle) > +{ > + const uint32_t ok_val = 0xbaadf00d; > + const uint32_t bad_val = 0xf00dbaad; > + const uint32_t noops[8192] = { 0, }; > + > + const uint32_t lri_ok[] = { > + MI_LOAD_REGISTER_IMM, > + BCS_GPR(0), > + ok_val, > + MI_BATCH_BUFFER_END, > + }; > + > + const uint32_t lri_bad[] = { > + MI_LOAD_REGISTER_IMM, > + BCS_GPR(0), > + bad_val, > + MI_BATCH_BUFFER_END, > + }; > + > + const uint32_t store_reg[] = { > + MI_STORE_REGISTER_MEM | (4 - 2), > + BCS_GPR(0), > + 0, /* reloc */ > + 0, /* reloc */ > + MI_NOOP, > + MI_BATCH_BUFFER_END, > + }; > + > + exec_batch(fd, I915_EXEC_BLT, handle, > + lri_ok, sizeof(lri_ok), > + 0); > + > + exec_batch_patched(fd, I915_EXEC_BLT, handle, > + store_reg, sizeof(store_reg), > + 2 * sizeof(uint32_t), /* reloc */ > + ok_val); > + > + exec_batch(fd, I915_EXEC_BLT, handle, > + lri_bad, 0, > + 0); > + > + exec_batch_patched(fd, I915_EXEC_BLT, handle, > + store_reg, sizeof(store_reg), > + 2 * sizeof(uint32_t), /* reloc */ > + ok_val); > + > + exec_batch(fd, I915_EXEC_BLT, handle, > + lri_ok, 4096, > + 0); > + > + igt_assert_eq(__gem_write(fd, handle, 0, noops, 4097), -EINVAL); > +} > + > +struct reg { > + uint32_t addr; > + uint32_t mask; > + bool masked_write; > + bool privileged; > +}; > + > +#define REG_M(ADDR, MASK, WM, P) { (ADDR), (MASK), (WM), (P) } > +#define REG(ADDR) REG_M(ADDR, 0xffffffff, false, false) > +#define REG_P(ADDR) REG_M(ADDR, 0xffffffff, false, true) > + > +static const struct reg regs[] = { > + REG_M(BCS_SWCTRL, 0x3, true, false), > + REG(BCS_GPR(0)), > + REG(BCS_GPR_UDW(0)), > + REG(BCS_GPR(1)), > + REG(BCS_GPR_UDW(1)), > + REG(BCS_GPR(2)), > + REG(BCS_GPR_UDW(2)), > + REG(BCS_GPR(3)), > + REG(BCS_GPR_UDW(3)), > + REG(BCS_GPR(4)), > + REG(BCS_GPR_UDW(4)), > + REG(BCS_GPR(5)), > + REG(BCS_GPR_UDW(5)), > + REG(BCS_GPR(6)), > + REG(BCS_GPR_UDW(6)), > + REG(BCS_GPR(7)), > + REG(BCS_GPR_UDW(7)), > + REG(BCS_GPR(8)), > + REG(BCS_GPR_UDW(8)), > + REG(BCS_GPR(9)), > + REG(BCS_GPR_UDW(9)), > + REG(BCS_GPR(10)), > + REG(BCS_GPR_UDW(10)), > + REG(BCS_GPR(11)), > + REG(BCS_GPR_UDW(11)), > + REG(BCS_GPR(12)), > + REG(BCS_GPR_UDW(12)), > + REG(BCS_GPR(13)), > + REG(BCS_GPR_UDW(13)), > + REG(BCS_GPR(14)), > + REG(BCS_GPR_UDW(14)), > + REG(BCS_GPR(15)), > + REG(BCS_GPR_UDW(15)), > + > + REG_P(0), > + REG_P(200000), > + > + REG_P(BCS_SWCTRL - 1), > + REG_P(BCS_SWCTRL - 2), > + REG_P(BCS_SWCTRL - 3), > + REG_P(BCS_SWCTRL - 4), > + REG_P(BCS_SWCTRL + 4), > + > + REG_P(BCS_GPR(0) - 1), > + REG_P(BCS_GPR(0) - 2), > + REG_P(BCS_GPR(0) - 3), > + REG_P(BCS_GPR(0) - 4), > + REG_P(BCS_GPR_UDW(15) + 4), > +}; > + > +static void test_register(const int fd, const uint32_t handle, > + const struct reg *r) > +{ > + const uint32_t lri_zero[] = { > + MI_LOAD_REGISTER_IMM, > + r->addr, > + r->masked_write ? 0xffff0000 : 0, > + MI_BATCH_BUFFER_END, > + }; > + > + const uint32_t lri_mask[] = { > + MI_LOAD_REGISTER_IMM, > + r->addr, > + r->masked_write ? (r->mask << 16) | r->mask : r->mask, > + MI_BATCH_BUFFER_END, > + }; > + > + const uint32_t store_reg[] = { > + MI_STORE_REGISTER_MEM | (4 - 2), > + r->addr, > + 0, /* reloc */ > + 0, /* reloc */ > + MI_NOOP, > + MI_BATCH_BUFFER_END, > + }; > + > + exec_batch(fd, I915_EXEC_BLT, handle, > + lri_mask, sizeof(lri_mask), > + r->privileged ? -EACCES : 0); > + > + exec_batch_patched(fd, I915_EXEC_BLT, handle, > + store_reg, sizeof(store_reg), > + 2 * sizeof(uint32_t), /* reloc */ > + r->privileged ? -EACCES : r->mask); > + > + exec_batch(fd, I915_EXEC_BLT, handle, > + lri_zero, sizeof(lri_zero), > + r->privileged ? -EACCES : 0); > + > + exec_batch_patched(fd, I915_EXEC_BLT, handle, > + store_reg, sizeof(store_reg), > + 2 * sizeof(uint32_t), /* reloc */ > + r->privileged ? -EACCES : 0); > +} > + > +static void test_valid_registers(const int fd, const uint32_t handle) > +{ > + int i; > + > + for (i = 0; i < ARRAY_SIZE(regs); i++) > + test_register(fd, handle, ®s[i]); > +} > + > +static long int read_reg(const int fd, const uint32_t handle, > + const uint32_t addr) > +{ > + const uint32_t store_reg[] = { > + MI_STORE_REGISTER_MEM | (4 - 2), > + addr, > + 0, /* reloc */ > + 0, /* reloc */ > + MI_NOOP, > + MI_BATCH_BUFFER_END, > + }; > + uint32_t target_bo; > + uint32_t value; > + long int ret; > + > + target_bo = gem_create(fd, HANDLE_SIZE); > + > + ret = __exec_batch_patched(fd, I915_EXEC_BLT, handle, > + store_reg, sizeof(store_reg), > + target_bo, 2 * sizeof(uint32_t), 0); > + > + if (ret) { > + igt_assert_lt(ret, 0); > + gem_close(fd, target_bo); > + return ret; > + } > + > + gem_sync(fd, handle); > + > + gem_read(fd, target_bo, 0, &value, sizeof(value)); > + > + gem_close(fd, target_bo); > + > + return value; > +} > + > +static int write_reg(const int fd, const uint32_t handle, > + const uint32_t addr, const uint32_t val) > +{ > + const uint32_t lri[] = { > + MI_LOAD_REGISTER_IMM, > + addr, > + val, > + MI_BATCH_BUFFER_END, > + }; > + > + return __exec_batch(fd, I915_EXEC_BLT, handle, > + lri, sizeof(lri)); > +} > + > +static void test_unaligned_access(const int fd, const uint32_t handle) > +{ > + const uint32_t addr = BCS_GPR(4); > + const uint32_t val = 0xbaadfead; > + const uint32_t pre = 0x12345678; > + const uint32_t post = 0x87654321; > + > + igt_assert_eq(write_reg(fd, handle, addr - 4, pre), 0); > + igt_assert_eq(write_reg(fd, handle, addr, val), 0); > + igt_assert_eq(write_reg(fd, handle, addr + 4, post), 0); > + > + igt_assert_eq(read_reg(fd, handle, addr - 4), pre); > + igt_assert_eq(read_reg(fd, handle, addr), val); > + igt_assert_eq(read_reg(fd, handle, addr + 4), post); > + > + for (int i = 0; i < 4; i++) { > + igt_assert_eq(write_reg(fd, handle, addr + i, val), 0); > + igt_assert_eq(read_reg(fd, handle, addr), val); > + > + igt_assert_eq(read_reg(fd, handle, addr + 1), val); > + igt_assert_eq(read_reg(fd, handle, addr + 2), val); > + igt_assert_eq(read_reg(fd, handle, addr + 3), val); > + igt_assert_eq(read_reg(fd, handle, addr + 4), post); > + igt_assert_eq(read_reg(fd, handle, addr - 3), pre); > + igt_assert_eq(read_reg(fd, handle, addr - 2), pre); > + igt_assert_eq(read_reg(fd, handle, addr - 1), pre); > + } > +} > + > +igt_main > +{ > + uint32_t handle; > + int fd; > + > + igt_fixture { > + fd = drm_open_driver(DRIVER_INTEL); > + igt_require_gem(fd); > + gem_require_blitter(fd); > + > + parser_version = command_parser_version(fd); > + /* igt_require(parser_version == 10); */ > + > + igt_require(gem_uses_ppgtt(fd)); > + igt_require(gem_has_blt(fd)); > + igt_require(intel_gen(intel_get_drm_devid(fd)) == 9); > + > + handle = gem_create(fd, HANDLE_SIZE); > + > + igt_fork_hang_detector(fd); > + } > + > + igt_subtest("secure-batches") > + test_secure_batches(fd); > + > + igt_subtest("allowed-all") > + test_allowed_all(fd, handle); > + > + igt_subtest("allowed-single") > + test_allowed_single(fd, handle); > + > + igt_subtest("bb-start-param") > + test_bb_start(fd, handle, BB_START_PARAM); > + > + igt_subtest("bb-start-out") > + test_bb_start(fd, handle, BB_START_OUT); > + > + igt_subtest("bb-secure") > + test_bb_secure(fd, handle); > + > + igt_subtest("bb-chained") > + test_bb_chained(fd, handle); > + > + igt_subtest("cmd-crossing-page") > + test_cmd_crossing_page(fd, handle); > + > + igt_subtest("batch-without-end") { > + const uint32_t noop[1024] = { 0 }; > + > + exec_batch(fd, I915_EXEC_BLT, handle, > + noop, sizeof(noop), > + -EINVAL); > + } > + > + igt_subtest("batch-zero-length") { > + const uint32_t noop[] = { 0, MI_BATCH_BUFFER_END }; > + > + exec_batch(fd, I915_EXEC_BLT, handle, > + noop, 0, > + -EINVAL); > + } > + > + igt_subtest("batch-invalid-length") > + test_invalid_length(fd, handle); > + > + igt_subtest("basic-rejected") { > + const uint32_t invalid_cmd[] = { > + INSTR_INVALID_CLIENT << INSTR_CLIENT_SHIFT, > + MI_BATCH_BUFFER_END, > + }; > + const uint32_t invalid_set_context[] = { > + MI_SET_CONTEXT | 32, /* invalid length */ > + MI_BATCH_BUFFER_END, > + }; > + > + exec_batch(fd, I915_EXEC_BLT, handle, > + invalid_cmd, sizeof(invalid_cmd), > + -EINVAL); > + > + exec_batch(fd, I915_EXEC_BLT, handle, > + invalid_set_context, sizeof(invalid_set_context), > + -EINVAL); > + } > + > + igt_subtest("valid-registers") > + test_valid_registers(fd, handle); > + > + igt_subtest("unaligned-access") > + test_unaligned_access(fd, handle); > + > + igt_subtest_group { > + igt_hang_t hang; > + > + igt_fixture igt_allow_hang(fd, 0, 0); > + > + igt_subtest("bb-start-cmd") > + test_bb_start(fd, handle, BB_START_CMD); > + > + igt_fixture igt_disallow_hang(fd, hang); > + } > + > + igt_fixture { > + igt_stop_hang_detector(); > + gem_close(fd, handle); > + > + close(fd); > + } > +} > diff --git a/tests/intel-ci/fast-feedback.testlist b/tests/intel-ci/fast-feedback.testlist > index 9dd27b42..a3c66b42 100644 > --- a/tests/intel-ci/fast-feedback.testlist > +++ b/tests/intel-ci/fast-feedback.testlist > @@ -203,3 +203,4 @@ igt@i915_module_load@reload > igt@i915_module_load@reload-no-display > igt@i915_module_load@reload-with-fault-injection > igt@i915_pm_rpm@module-reload > +igt@gem_blt_parse@bb-start #expected hang so last > diff --git a/tests/meson.build b/tests/meson.build > index 98f2db55..43899b95 100644 > --- a/tests/meson.build > +++ b/tests/meson.build > @@ -148,6 +148,7 @@ i915_progs = [ > 'gem_exec_parallel', > 'gem_exec_params', > 'gem_exec_parse', > + 'gem_blt_parse', > 'gem_exec_reloc', > 'gem_exec_reuse', > 'gem_exec_schedule', > -- > 2.17.1 > > _______________________________________________ > igt-dev mailing list > igt-dev@lists.freedesktop.org > https://lists.freedesktop.org/mailman/listinfo/igt-dev
diff --git a/tests/Makefile.sources b/tests/Makefile.sources index abf1e2fc..75c79edb 100644 --- a/tests/Makefile.sources +++ b/tests/Makefile.sources @@ -244,6 +244,9 @@ gem_exec_params_SOURCES = i915/gem_exec_params.c TESTS_progs += gem_exec_parse gem_exec_parse_SOURCES = i915/gem_exec_parse.c +TESTS_progs += gem_blt_parse +gem_blt_parse_SOURCES = i915/gem_blt_parse.c + TESTS_progs += gem_exec_reloc gem_exec_reloc_SOURCES = i915/gem_exec_reloc.c diff --git a/tests/i915/gem_blt_parse.c b/tests/i915/gem_blt_parse.c new file mode 100644 index 00000000..607afba9 --- /dev/null +++ b/tests/i915/gem_blt_parse.c @@ -0,0 +1,997 @@ +/* + * Copyright © 2018 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. + * + */ + +#include "igt.h" +#include <stdlib.h> +#include <stdint.h> +#include <stdio.h> +#include <errno.h> + +#include <drm.h> + +#ifndef I915_PARAM_CMD_PARSER_VERSION +#define I915_PARAM_CMD_PARSER_VERSION 28 +#endif + +#ifndef I915_PARAM_HAS_SECURE_BATCHES +#define I915_PARAM_HAS_SECURE_BATCHES 23 +#endif + +/* To help craft commands known to be invalid across all engines */ +#define INSTR_CLIENT_SHIFT 29 +#define INSTR_INVALID_CLIENT 0x7 + +#define MI_LOAD_REGISTER_REG (0x2a << 23) +#define MI_STORE_REGISTER_MEM (0x24 << 23) +#define MI_ARB_ON_OFF (0x8 << 23) +#define MI_USER_INTERRUPT (0x02 << 23) +#define MI_FLUSH_DW (0x26 << 23) +#define MI_ARB_CHECK (0x05 << 23) +#define MI_REPORT_HEAD (0x07 << 23) +#define MI_SUSPEND_FLUSH (0x0b << 23) +#define MI_LOAD_SCAN_LINES_EXCL (0x13 << 23) +#define MI_UPDATE_GTT (0x23 << 23) + +#define BCS_SWCTRL 0x22200 +#define BCS_GPR_BASE 0x22600 +#define BCS_GPR(n) (0x22600 + (n) * 8) +#define BCS_GPR_UDW(n) (0x22600 + (n) * 8 + 4) + +#define HANDLE_SIZE 4096 + +static int parser_version; + +static int command_parser_version(int fd) +{ + int version = -1; + drm_i915_getparam_t gp; + + gp.param = I915_PARAM_CMD_PARSER_VERSION; + gp.value = &version; + + if (drmIoctl(fd, DRM_IOCTL_I915_GETPARAM, &gp) == 0) + return version; + + return -1; +} + +static int __exec_batch_patched(int fd, int engine, + uint32_t cmd_bo, const uint32_t *cmds, int size, + uint32_t target_bo, uint64_t target_offset, uint64_t target_delta) +{ + struct drm_i915_gem_execbuffer2 execbuf; + struct drm_i915_gem_exec_object2 obj[2]; + struct drm_i915_gem_relocation_entry reloc[1]; + + gem_write(fd, cmd_bo, 0, cmds, size); + + memset(obj, 0, sizeof(obj)); + obj[0].handle = target_bo; + obj[1].handle = cmd_bo; + + memset(reloc, 0, sizeof(reloc)); + reloc[0].offset = target_offset; + reloc[0].target_handle = target_bo; + reloc[0].delta = target_delta; + reloc[0].read_domains = I915_GEM_DOMAIN_COMMAND; + reloc[0].write_domain = I915_GEM_DOMAIN_COMMAND; + reloc[0].presumed_offset = -1; + + obj[1].relocs_ptr = to_user_pointer(reloc); + obj[1].relocation_count = 1; + + memset(&execbuf, 0, sizeof(execbuf)); + execbuf.buffers_ptr = to_user_pointer(obj); + execbuf.buffer_count = 2; + execbuf.batch_len = size; + execbuf.flags = engine; + + return __gem_execbuf(fd, &execbuf); +} + +static void exec_batch_patched(int fd, int engine, + uint32_t cmd_bo, const uint32_t *cmds, + int size, int patch_offset, + long int expected_value) +{ + const uint32_t target_bo = gem_create(fd, HANDLE_SIZE); + uint64_t actual_value = 0; + long int ret; + + ret = __exec_batch_patched(fd, engine, cmd_bo, cmds, size, target_bo, patch_offset, 0); + if (ret) { + igt_assert_lt(ret, 0); + gem_close(fd, target_bo); + igt_assert_eq(ret, expected_value); + return; + } + + gem_sync(fd, cmd_bo); + + gem_read(fd, target_bo, 0, &actual_value, sizeof(actual_value)); + + gem_close(fd, target_bo); + + igt_assert_eq(actual_value, expected_value); +} + +static int __exec_batch(int fd, int engine, uint32_t cmd_bo, + const uint32_t *cmds, int size) +{ + struct drm_i915_gem_execbuffer2 execbuf; + struct drm_i915_gem_exec_object2 obj[1]; + int ret; + + gem_write(fd, cmd_bo, 0, cmds, size); + + memset(obj, 0, sizeof(obj)); + obj[0].handle = cmd_bo; + + memset(&execbuf, 0, sizeof(execbuf)); + execbuf.buffers_ptr = to_user_pointer(obj); + execbuf.buffer_count = 1; + execbuf.batch_len = size; + execbuf.flags = engine; + + ret = __gem_execbuf(fd, &execbuf); + + if (!ret) + gem_sync(fd, cmd_bo); + + return ret; +} + +#if 0 +static void print_batch(const uint32_t *cmds, const uint32_t sz) +{ + const int commands = sz / 4; + int i; + + igt_info("Batch size %d\n", sz); + for (i = 0; i < commands; i++) + igt_info("0x%08x: 0x%08x\n", i, cmds[i]); +} +#else +#define print_batch(cmds, size) +#endif + +#define exec_batch(fd, engine, bo, cmds, sz, expected) \ + print_batch(cmds, sz); \ + igt_assert_eq(__exec_batch(fd, engine, bo, cmds, sz), expected) + +static void exec_split_batch(int fd, int engine, const uint32_t *cmds, + int size, int expected_ret) +{ + struct drm_i915_gem_execbuffer2 execbuf; + struct drm_i915_gem_exec_object2 obj[1]; + uint32_t cmd_bo; + const uint32_t noop[1024] = { 0 }; + const int alloc_size = 4096 * 2; + const int actual_start_offset = 4096-sizeof(uint32_t); + + /* Allocate and fill a 2-page batch with noops */ + cmd_bo = gem_create(fd, alloc_size); + gem_write(fd, cmd_bo, 0, noop, sizeof(noop)); + gem_write(fd, cmd_bo, 4096, noop, sizeof(noop)); + + /* Write the provided commands such that the first dword + * of the command buffer is the last dword of the first + * page (i.e. the command is split across the two pages). + */ + gem_write(fd, cmd_bo, actual_start_offset, cmds, size); + + memset(obj, 0, sizeof(obj)); + obj[0].handle = cmd_bo; + + memset(&execbuf, 0, sizeof(execbuf)); + execbuf.buffers_ptr = to_user_pointer(obj); + execbuf.buffer_count = 1; + /* NB: We want batch_start_offset and batch_len to point to the block + * of the actual commands (i.e. at the last dword of the first page), + * but have to adjust both the start offset and length to meet the + * kernel driver's requirements on the alignment of those fields. + */ + execbuf.batch_start_offset = actual_start_offset & ~0x7; + execbuf.batch_len = + ALIGN(size + actual_start_offset - execbuf.batch_start_offset, + 0x8); + execbuf.flags = engine; + + igt_assert_eq(__gem_execbuf(fd, &execbuf), expected_ret); + + gem_sync(fd, cmd_bo); + gem_close(fd, cmd_bo); +} + +static void exec_batch_chained(int fd, int engine, + uint32_t cmd_bo, const uint32_t *cmds, + int size, int patch_offset, + uint64_t expected_value, + int expected_return) +{ + struct drm_i915_gem_execbuffer2 execbuf; + struct drm_i915_gem_exec_object2 obj[3]; + struct drm_i915_gem_relocation_entry reloc[1]; + struct drm_i915_gem_relocation_entry first_level_reloc; + + const uint32_t target_bo = gem_create(fd, 4096); + const uint32_t first_level_bo = gem_create(fd, 4096); + uint64_t actual_value = 0; + int ret; + + const uint32_t first_level_cmds[] = { + MI_BATCH_BUFFER_START | MI_BATCH_NON_SECURE_I965 | 1, + 0, + 0, + MI_BATCH_BUFFER_END, + }; + + gem_write(fd, first_level_bo, 0, + first_level_cmds, sizeof(first_level_cmds)); + gem_write(fd, cmd_bo, 0, cmds, size); + + memset(obj, 0, sizeof(obj)); + obj[0].handle = target_bo; + obj[1].handle = cmd_bo; + obj[2].handle = first_level_bo; + + memset(reloc, 0, sizeof(reloc)); + reloc[0].offset = patch_offset; + reloc[0].delta = 0; + reloc[0].target_handle = target_bo; + reloc[0].read_domains = I915_GEM_DOMAIN_COMMAND; + reloc[0].write_domain = I915_GEM_DOMAIN_COMMAND; + reloc[0].presumed_offset = -1; + + obj[1].relocation_count = 1; + obj[1].relocs_ptr = to_user_pointer(&reloc); + + memset(&first_level_reloc, 0, sizeof(first_level_reloc)); + first_level_reloc.offset = 4; + first_level_reloc.delta = 0; + first_level_reloc.target_handle = cmd_bo; + first_level_reloc.read_domains = I915_GEM_DOMAIN_COMMAND; + first_level_reloc.write_domain = 0; + obj[2].relocation_count = 1; + obj[2].relocs_ptr = to_user_pointer(&first_level_reloc); + + memset(&execbuf, 0, sizeof(execbuf)); + execbuf.buffers_ptr = to_user_pointer(obj); + execbuf.buffer_count = 3; + execbuf.batch_len = sizeof(first_level_cmds); + execbuf.flags = engine; + + ret = __gem_execbuf(fd, &execbuf); + if (expected_return && ret == expected_return) + goto out; + + gem_sync(fd, cmd_bo); + + gem_read(fd,target_bo, 0, &actual_value, sizeof(actual_value)); + +out: + if (!expected_return) + igt_assert_eq(expected_value, actual_value); + else + igt_assert_neq(expected_value, actual_value); + + gem_close(fd, first_level_bo); + gem_close(fd, target_bo); +} + +static void test_secure_batches(const int fd) +{ + int v = -1; + drm_i915_getparam_t gp; + + gp.param = I915_PARAM_HAS_SECURE_BATCHES; + gp.value = &v; + + igt_assert_eq(drmIoctl(fd, DRM_IOCTL_I915_GETPARAM, &gp), 0); + igt_assert_eq(v, 0); + + igt_assert(!gem_engine_has_mutable_submission(fd, I915_EXEC_BLT)); +} + +struct cmd { + uint32_t cmd; + int len; + const char *name; +}; + +#define CMD(C, L) { .cmd = (C), .len = (L), .name = #C } +#define CMD_N(C) { .cmd = (C), .len = 1, .name = #C } + +static const struct cmd allowed_cmds[] = { + CMD_N(MI_NOOP), + CMD_N(MI_USER_INTERRUPT), + CMD_N(MI_WAIT_FOR_EVENT), + CMD(MI_FLUSH_DW, 5), + CMD_N(MI_ARB_CHECK), + CMD_N(MI_REPORT_HEAD), + CMD_N(MI_FLUSH), + CMD_N(MI_ARB_ON_OFF), + CMD_N(MI_SUSPEND_FLUSH), + CMD(MI_LOAD_SCAN_LINES_INCL, 2), + CMD(MI_LOAD_SCAN_LINES_EXCL, 2), +}; + +static uint32_t *inject_cmd(uint32_t *batch, const uint32_t cmd, int len) +{ + int i = 0; + + batch[i++] = cmd; + + while (--len) + batch[i++] = 0; + + return &batch[i]; +} + +static unsigned long batch_num_cmds(const uint32_t * const batch_start, + const uint32_t * const batch_end) +{ + igt_assert_lte((unsigned long)batch_start, (unsigned long)batch_end); + + return batch_end - batch_start; +} + +static unsigned long batch_bytes(const uint32_t * const batch_start, + const uint32_t * const batch_end) +{ + const unsigned long bytes = batch_num_cmds(batch_start, batch_end) * 4; + + igt_assert(!(bytes & 0x7)); + + return bytes; +} + +static void test_allowed_all(const int fd, const uint32_t handle) +{ + uint32_t batch[4096]; + uint32_t *b = &batch[0]; + + for (int i = 0; i < ARRAY_SIZE(allowed_cmds); i++) + b = inject_cmd(b, allowed_cmds[i].cmd, + allowed_cmds[i].len); + + if (!(batch_num_cmds(batch, b) % 2)) + b = inject_cmd(b, MI_NOOP, 1); + + b = inject_cmd(b, MI_BATCH_BUFFER_END, 1); + + exec_batch(fd, I915_EXEC_BLT, handle, batch, batch_bytes(batch, b), 0); +} + +static void test_allowed_single(const int fd, const uint32_t handle) +{ + uint32_t batch[4096]; + int ret; + + for (int i = 0; i < ARRAY_SIZE(allowed_cmds); i++) { + uint32_t *b = &batch[0]; + + b = inject_cmd(b, allowed_cmds[i].cmd, + allowed_cmds[i].len); + + if (!(batch_num_cmds(batch, b) % 2)) + b = inject_cmd(b, MI_NOOP, 1); + + b = inject_cmd(b, MI_BATCH_BUFFER_END, 1); + + ret = __exec_batch(fd, I915_EXEC_BLT, handle, + batch, batch_bytes(batch, b)); + + igt_assert_eq(ret, 0); + }; +} + +static void test_bb_secure(const int fd, const uint32_t handle) +{ + struct drm_i915_gem_execbuffer2 execbuf; + struct drm_i915_gem_exec_object2 obj[1]; + struct drm_i915_gem_relocation_entry reloc[1]; + int ret; + + const uint32_t batch_secure[] = { + MI_BATCH_BUFFER_START | 1, + 12, + 0, + MI_NOOP, + MI_NOOP, + MI_BATCH_BUFFER_END, + }; + + gem_write(fd, handle, 0, batch_secure, sizeof(batch_secure)); + + memset(obj, 0, sizeof(obj)); + obj[0].handle = handle; + + memset(reloc, 0, sizeof(reloc)); + reloc[0].offset = 1 * sizeof(uint32_t); + reloc[0].target_handle = handle; + reloc[0].delta = 4 * sizeof(uint32_t); + reloc[0].read_domains = I915_GEM_DOMAIN_COMMAND; + reloc[0].write_domain = 0; + reloc[0].presumed_offset = -1; + + obj[0].relocs_ptr = to_user_pointer(reloc); + obj[0].relocation_count = 1; + + memset(&execbuf, 0, sizeof(execbuf)); + execbuf.buffers_ptr = to_user_pointer(obj); + execbuf.buffer_count = 1; + execbuf.batch_len = sizeof(batch_secure); + execbuf.flags = I915_EXEC_BLT; + + ret = __gem_execbuf(fd, &execbuf); + + igt_assert_eq(ret, -EACCES); +} + +#define BB_START_PARAM 0 +#define BB_START_OUT 1 +#define BB_START_CMD 2 + +static void test_bb_start(const int fd, const uint32_t handle, int test) +{ + struct drm_i915_gem_execbuffer2 execbuf; + struct drm_i915_gem_exec_object2 obj[2]; + struct drm_i915_gem_relocation_entry reloc[3]; + const uint32_t target_bo = gem_create(fd, 4096); + uint32_t *dst; + int ret; + unsigned int jump_off; + + const uint32_t batch[] = { + MI_NOOP, + MI_NOOP, + MI_NOOP, + MI_NOOP, + MI_STORE_DWORD_IMM, + 0, + 0, + 1, + MI_STORE_DWORD_IMM, + 4, + 0, + 2, + MI_COND_BATCH_BUFFER_END | 1, + 0, + 0, + 0, + MI_BATCH_BUFFER_START | MI_BATCH_NON_SECURE_I965 | 1, + 0, + 0, + MI_BATCH_BUFFER_END, + }; + + igt_require(gem_can_store_dword(fd, I915_EXEC_BLT)); + + switch (test) { + case BB_START_PARAM: + jump_off = 5 * sizeof(uint32_t); + break; + case BB_START_CMD: + jump_off = 8 * sizeof(uint32_t); + break; + default: + jump_off = 0xf00d0000; + } + + gem_write(fd, handle, 0, batch, sizeof(batch)); + + memset(obj, 0, sizeof(obj)); + obj[0].handle = target_bo; + obj[1].handle = handle; + + memset(reloc, 0, sizeof(reloc)); + reloc[0].offset = 5 * sizeof(uint32_t); + reloc[0].target_handle = obj[0].handle; + reloc[0].delta = 0; + reloc[0].read_domains = I915_GEM_DOMAIN_COMMAND; + reloc[0].write_domain = I915_GEM_DOMAIN_COMMAND; + reloc[0].presumed_offset = -1; + + reloc[1].offset = 9 * sizeof(uint32_t); + reloc[1].target_handle = obj[0].handle; + reloc[1].delta = 1 * sizeof(uint32_t); + reloc[1].read_domains = I915_GEM_DOMAIN_COMMAND; + reloc[1].write_domain = I915_GEM_DOMAIN_COMMAND; + reloc[1].presumed_offset = -1; + + reloc[2].offset = 17 * sizeof(uint32_t); + reloc[2].target_handle = obj[1].handle; + reloc[2].delta = jump_off; + reloc[2].read_domains = I915_GEM_DOMAIN_COMMAND; + reloc[2].write_domain = 0; + reloc[2].presumed_offset = -1; + + obj[1].relocs_ptr = to_user_pointer(reloc); + obj[1].relocation_count = 3; + + memset(&execbuf, 0, sizeof(execbuf)); + execbuf.buffers_ptr = to_user_pointer(obj); + execbuf.buffer_count = 2; + execbuf.batch_len = sizeof(batch); + execbuf.flags = I915_EXEC_BLT; + + dst = gem_mmap__cpu(fd, obj[0].handle, 0, 4096, + PROT_READ | PROT_WRITE); + + igt_assert_eq(dst[0], 0); + igt_assert_eq(dst[1], 0); + + ret = __gem_execbuf(fd, &execbuf); + + switch (test) { + case BB_START_PARAM: + igt_assert_eq(ret, -EINVAL); + break; + case BB_START_CMD: + igt_assert_eq(ret, 0); + + while (READ_ONCE(dst[0]) == 0) + ; + + while (READ_ONCE(dst[1]) == 0) + ; + + igt_assert_eq(dst[0], 1); + igt_assert_eq(dst[1], 2); + + igt_info("values now %x %x\n", dst[0], dst[1]); + + dst[0] = 0; + + igt_info("values now %x %x\n", dst[0], dst[1]); + + gem_sync(fd, handle); + + igt_info("values now %x %x\n", dst[0], dst[1]); + + igt_assert_eq(dst[0], 0); + igt_assert_eq(dst[1], 2); + + break; + + case BB_START_OUT: + igt_assert_eq(ret, -EINVAL); + break; + } + + gem_sync(fd, handle); + gem_munmap(dst, 4096); + gem_close(fd, target_bo); +} + +static void test_bb_chained(const int fd, const uint32_t handle) +{ + const uint32_t batch[] = { + (0x20 << 23) | 2, /* MI_STORE_DATA_IMM */ + 0, + 0, + 0xbaadf00d, + MI_NOOP, + MI_BATCH_BUFFER_END, + }; + + exec_batch_chained(fd, I915_EXEC_RENDER, + handle, + batch, sizeof(batch), + 4, + 0xbaadf00d, + 0); + + exec_batch_chained(fd, I915_EXEC_BLT, + handle, + batch, sizeof(batch), + 4, + 0xbaadf00d, + EPERM); +} + +static void test_cmd_crossing_page(const int fd, const uint32_t handle) +{ + const uint32_t lri_ok[] = { + MI_LOAD_REGISTER_IMM, + BCS_GPR(0), + 0xbaadf00d, + MI_BATCH_BUFFER_END, + }; + const uint32_t store_reg[] = { + MI_STORE_REGISTER_MEM | (4 - 2), + BCS_GPR(0), + 0, /* reloc */ + 0, /* reloc */ + MI_NOOP, + MI_BATCH_BUFFER_END, + }; + + exec_split_batch(fd, I915_EXEC_BLT, + lri_ok, sizeof(lri_ok), + 0); + + exec_batch_patched(fd, I915_EXEC_BLT, handle, + store_reg, sizeof(store_reg), + 2 * sizeof(uint32_t), /* reloc */ + 0xbaadf00d); +} + +static void test_invalid_length(const int fd, const uint32_t handle) +{ + const uint32_t ok_val = 0xbaadf00d; + const uint32_t bad_val = 0xf00dbaad; + const uint32_t noops[8192] = { 0, }; + + const uint32_t lri_ok[] = { + MI_LOAD_REGISTER_IMM, + BCS_GPR(0), + ok_val, + MI_BATCH_BUFFER_END, + }; + + const uint32_t lri_bad[] = { + MI_LOAD_REGISTER_IMM, + BCS_GPR(0), + bad_val, + MI_BATCH_BUFFER_END, + }; + + const uint32_t store_reg[] = { + MI_STORE_REGISTER_MEM | (4 - 2), + BCS_GPR(0), + 0, /* reloc */ + 0, /* reloc */ + MI_NOOP, + MI_BATCH_BUFFER_END, + }; + + exec_batch(fd, I915_EXEC_BLT, handle, + lri_ok, sizeof(lri_ok), + 0); + + exec_batch_patched(fd, I915_EXEC_BLT, handle, + store_reg, sizeof(store_reg), + 2 * sizeof(uint32_t), /* reloc */ + ok_val); + + exec_batch(fd, I915_EXEC_BLT, handle, + lri_bad, 0, + 0); + + exec_batch_patched(fd, I915_EXEC_BLT, handle, + store_reg, sizeof(store_reg), + 2 * sizeof(uint32_t), /* reloc */ + ok_val); + + exec_batch(fd, I915_EXEC_BLT, handle, + lri_ok, 4096, + 0); + + igt_assert_eq(__gem_write(fd, handle, 0, noops, 4097), -EINVAL); +} + +struct reg { + uint32_t addr; + uint32_t mask; + bool masked_write; + bool privileged; +}; + +#define REG_M(ADDR, MASK, WM, P) { (ADDR), (MASK), (WM), (P) } +#define REG(ADDR) REG_M(ADDR, 0xffffffff, false, false) +#define REG_P(ADDR) REG_M(ADDR, 0xffffffff, false, true) + +static const struct reg regs[] = { + REG_M(BCS_SWCTRL, 0x3, true, false), + REG(BCS_GPR(0)), + REG(BCS_GPR_UDW(0)), + REG(BCS_GPR(1)), + REG(BCS_GPR_UDW(1)), + REG(BCS_GPR(2)), + REG(BCS_GPR_UDW(2)), + REG(BCS_GPR(3)), + REG(BCS_GPR_UDW(3)), + REG(BCS_GPR(4)), + REG(BCS_GPR_UDW(4)), + REG(BCS_GPR(5)), + REG(BCS_GPR_UDW(5)), + REG(BCS_GPR(6)), + REG(BCS_GPR_UDW(6)), + REG(BCS_GPR(7)), + REG(BCS_GPR_UDW(7)), + REG(BCS_GPR(8)), + REG(BCS_GPR_UDW(8)), + REG(BCS_GPR(9)), + REG(BCS_GPR_UDW(9)), + REG(BCS_GPR(10)), + REG(BCS_GPR_UDW(10)), + REG(BCS_GPR(11)), + REG(BCS_GPR_UDW(11)), + REG(BCS_GPR(12)), + REG(BCS_GPR_UDW(12)), + REG(BCS_GPR(13)), + REG(BCS_GPR_UDW(13)), + REG(BCS_GPR(14)), + REG(BCS_GPR_UDW(14)), + REG(BCS_GPR(15)), + REG(BCS_GPR_UDW(15)), + + REG_P(0), + REG_P(200000), + + REG_P(BCS_SWCTRL - 1), + REG_P(BCS_SWCTRL - 2), + REG_P(BCS_SWCTRL - 3), + REG_P(BCS_SWCTRL - 4), + REG_P(BCS_SWCTRL + 4), + + REG_P(BCS_GPR(0) - 1), + REG_P(BCS_GPR(0) - 2), + REG_P(BCS_GPR(0) - 3), + REG_P(BCS_GPR(0) - 4), + REG_P(BCS_GPR_UDW(15) + 4), +}; + +static void test_register(const int fd, const uint32_t handle, + const struct reg *r) +{ + const uint32_t lri_zero[] = { + MI_LOAD_REGISTER_IMM, + r->addr, + r->masked_write ? 0xffff0000 : 0, + MI_BATCH_BUFFER_END, + }; + + const uint32_t lri_mask[] = { + MI_LOAD_REGISTER_IMM, + r->addr, + r->masked_write ? (r->mask << 16) | r->mask : r->mask, + MI_BATCH_BUFFER_END, + }; + + const uint32_t store_reg[] = { + MI_STORE_REGISTER_MEM | (4 - 2), + r->addr, + 0, /* reloc */ + 0, /* reloc */ + MI_NOOP, + MI_BATCH_BUFFER_END, + }; + + exec_batch(fd, I915_EXEC_BLT, handle, + lri_mask, sizeof(lri_mask), + r->privileged ? -EACCES : 0); + + exec_batch_patched(fd, I915_EXEC_BLT, handle, + store_reg, sizeof(store_reg), + 2 * sizeof(uint32_t), /* reloc */ + r->privileged ? -EACCES : r->mask); + + exec_batch(fd, I915_EXEC_BLT, handle, + lri_zero, sizeof(lri_zero), + r->privileged ? -EACCES : 0); + + exec_batch_patched(fd, I915_EXEC_BLT, handle, + store_reg, sizeof(store_reg), + 2 * sizeof(uint32_t), /* reloc */ + r->privileged ? -EACCES : 0); +} + +static void test_valid_registers(const int fd, const uint32_t handle) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(regs); i++) + test_register(fd, handle, ®s[i]); +} + +static long int read_reg(const int fd, const uint32_t handle, + const uint32_t addr) +{ + const uint32_t store_reg[] = { + MI_STORE_REGISTER_MEM | (4 - 2), + addr, + 0, /* reloc */ + 0, /* reloc */ + MI_NOOP, + MI_BATCH_BUFFER_END, + }; + uint32_t target_bo; + uint32_t value; + long int ret; + + target_bo = gem_create(fd, HANDLE_SIZE); + + ret = __exec_batch_patched(fd, I915_EXEC_BLT, handle, + store_reg, sizeof(store_reg), + target_bo, 2 * sizeof(uint32_t), 0); + + if (ret) { + igt_assert_lt(ret, 0); + gem_close(fd, target_bo); + return ret; + } + + gem_sync(fd, handle); + + gem_read(fd, target_bo, 0, &value, sizeof(value)); + + gem_close(fd, target_bo); + + return value; +} + +static int write_reg(const int fd, const uint32_t handle, + const uint32_t addr, const uint32_t val) +{ + const uint32_t lri[] = { + MI_LOAD_REGISTER_IMM, + addr, + val, + MI_BATCH_BUFFER_END, + }; + + return __exec_batch(fd, I915_EXEC_BLT, handle, + lri, sizeof(lri)); +} + +static void test_unaligned_access(const int fd, const uint32_t handle) +{ + const uint32_t addr = BCS_GPR(4); + const uint32_t val = 0xbaadfead; + const uint32_t pre = 0x12345678; + const uint32_t post = 0x87654321; + + igt_assert_eq(write_reg(fd, handle, addr - 4, pre), 0); + igt_assert_eq(write_reg(fd, handle, addr, val), 0); + igt_assert_eq(write_reg(fd, handle, addr + 4, post), 0); + + igt_assert_eq(read_reg(fd, handle, addr - 4), pre); + igt_assert_eq(read_reg(fd, handle, addr), val); + igt_assert_eq(read_reg(fd, handle, addr + 4), post); + + for (int i = 0; i < 4; i++) { + igt_assert_eq(write_reg(fd, handle, addr + i, val), 0); + igt_assert_eq(read_reg(fd, handle, addr), val); + + igt_assert_eq(read_reg(fd, handle, addr + 1), val); + igt_assert_eq(read_reg(fd, handle, addr + 2), val); + igt_assert_eq(read_reg(fd, handle, addr + 3), val); + igt_assert_eq(read_reg(fd, handle, addr + 4), post); + igt_assert_eq(read_reg(fd, handle, addr - 3), pre); + igt_assert_eq(read_reg(fd, handle, addr - 2), pre); + igt_assert_eq(read_reg(fd, handle, addr - 1), pre); + } +} + +igt_main +{ + uint32_t handle; + int fd; + + igt_fixture { + fd = drm_open_driver(DRIVER_INTEL); + igt_require_gem(fd); + gem_require_blitter(fd); + + parser_version = command_parser_version(fd); + /* igt_require(parser_version == 10); */ + + igt_require(gem_uses_ppgtt(fd)); + igt_require(gem_has_blt(fd)); + igt_require(intel_gen(intel_get_drm_devid(fd)) == 9); + + handle = gem_create(fd, HANDLE_SIZE); + + igt_fork_hang_detector(fd); + } + + igt_subtest("secure-batches") + test_secure_batches(fd); + + igt_subtest("allowed-all") + test_allowed_all(fd, handle); + + igt_subtest("allowed-single") + test_allowed_single(fd, handle); + + igt_subtest("bb-start-param") + test_bb_start(fd, handle, BB_START_PARAM); + + igt_subtest("bb-start-out") + test_bb_start(fd, handle, BB_START_OUT); + + igt_subtest("bb-secure") + test_bb_secure(fd, handle); + + igt_subtest("bb-chained") + test_bb_chained(fd, handle); + + igt_subtest("cmd-crossing-page") + test_cmd_crossing_page(fd, handle); + + igt_subtest("batch-without-end") { + const uint32_t noop[1024] = { 0 }; + + exec_batch(fd, I915_EXEC_BLT, handle, + noop, sizeof(noop), + -EINVAL); + } + + igt_subtest("batch-zero-length") { + const uint32_t noop[] = { 0, MI_BATCH_BUFFER_END }; + + exec_batch(fd, I915_EXEC_BLT, handle, + noop, 0, + -EINVAL); + } + + igt_subtest("batch-invalid-length") + test_invalid_length(fd, handle); + + igt_subtest("basic-rejected") { + const uint32_t invalid_cmd[] = { + INSTR_INVALID_CLIENT << INSTR_CLIENT_SHIFT, + MI_BATCH_BUFFER_END, + }; + const uint32_t invalid_set_context[] = { + MI_SET_CONTEXT | 32, /* invalid length */ + MI_BATCH_BUFFER_END, + }; + + exec_batch(fd, I915_EXEC_BLT, handle, + invalid_cmd, sizeof(invalid_cmd), + -EINVAL); + + exec_batch(fd, I915_EXEC_BLT, handle, + invalid_set_context, sizeof(invalid_set_context), + -EINVAL); + } + + igt_subtest("valid-registers") + test_valid_registers(fd, handle); + + igt_subtest("unaligned-access") + test_unaligned_access(fd, handle); + + igt_subtest_group { + igt_hang_t hang; + + igt_fixture igt_allow_hang(fd, 0, 0); + + igt_subtest("bb-start-cmd") + test_bb_start(fd, handle, BB_START_CMD); + + igt_fixture igt_disallow_hang(fd, hang); + } + + igt_fixture { + igt_stop_hang_detector(); + gem_close(fd, handle); + + close(fd); + } +} diff --git a/tests/intel-ci/fast-feedback.testlist b/tests/intel-ci/fast-feedback.testlist index 9dd27b42..a3c66b42 100644 --- a/tests/intel-ci/fast-feedback.testlist +++ b/tests/intel-ci/fast-feedback.testlist @@ -203,3 +203,4 @@ igt@i915_module_load@reload igt@i915_module_load@reload-no-display igt@i915_module_load@reload-with-fault-injection igt@i915_pm_rpm@module-reload +igt@gem_blt_parse@bb-start #expected hang so last diff --git a/tests/meson.build b/tests/meson.build index 98f2db55..43899b95 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -148,6 +148,7 @@ i915_progs = [ 'gem_exec_parallel', 'gem_exec_params', 'gem_exec_parse', + 'gem_blt_parse', 'gem_exec_reloc', 'gem_exec_reuse', 'gem_exec_schedule',