diff options
author | Chris Wilson <chris@chris-wilson.co.uk> | 2017-11-08 14:38:50 +0000 |
---|---|---|
committer | Chris Wilson <chris@chris-wilson.co.uk> | 2018-03-15 10:52:37 +0000 |
commit | 44196e8b513975ed450a555897fa84e5594bc300 (patch) | |
tree | b26dfeb08ee1e4bbbcb908fd94204695a99982bf | |
parent | d17869a3e7e058b52963e2ee3f708ba759be42fb (diff) |
validation
-rw-r--r-- | tests/Makefile.sources | 2 | ||||
-rw-r--r-- | tests/gem_ctx_validation.c | 468 | ||||
-rw-r--r-- | tests/gem_exec_validation.c | 402 |
3 files changed, 872 insertions, 0 deletions
diff --git a/tests/Makefile.sources b/tests/Makefile.sources index 13fe126a..89bd310f 100644 --- a/tests/Makefile.sources +++ b/tests/Makefile.sources @@ -65,6 +65,7 @@ TESTS_progs = \ gem_ctx_shared \ gem_ctx_switch \ gem_ctx_thrash \ + gem_ctx_validation \ gem_double_irq_loop \ gem_eio \ gem_evict_alignment \ @@ -96,6 +97,7 @@ TESTS_progs = \ gem_exec_store \ gem_exec_suspend \ gem_exec_whisper \ + gem_exec_validation \ gem_fd_exhaustion \ gem_fence_thrash \ gem_fence_upload \ diff --git a/tests/gem_ctx_validation.c b/tests/gem_ctx_validation.c new file mode 100644 index 00000000..5fae9deb --- /dev/null +++ b/tests/gem_ctx_validation.c @@ -0,0 +1,468 @@ +/* + * Copyright © 2017 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 "igt_dummyload.h" + +#define MAX_REG 0x40000 + +#define ALL ~0u +#define GEN_RANGE(x, y) ((ALL >> (32 - (y - x + 1))) << x) +#define GEN6 (ALL << 6) +#define GEN7 (ALL << 7) +#define GEN8 (ALL << 8) +#define GEN9 (ALL << 9) + +static const struct named_register { + const char *name; + unsigned int gen_mask; + uint32_t offset; + uint32_t count; +} ignore_registers[] = { + { "RCS timestamp", GEN6, 0x2358 }, + { "VCS0 timestamp", GEN7, 0x12358 }, + { "VCS1 timestamp", GEN7, 0x1c358 }, + { "BCS timestamp", GEN7, 0x22358 }, + { "VECS timestamp", GEN8, 0x1a358 }, + {} +}; + +static bool ignore_register(uint32_t reg) +{ + for (const struct named_register *r = ignore_registers; r->name; r++) { + unsigned int width = r->count ? 4*r->count : 4; + if (reg >= r->offset && reg < r->offset + width) + return true; + } + + return false; +} + +static void save_reg(int i915, + uint32_t ctx, unsigned int engine, uint32_t reg, + uint32_t scratch) +{ + const int gen = intel_gen(intel_get_drm_devid(i915)); + const int r64b = gen >= 8; + struct drm_i915_gem_exec_object2 obj[2]; + struct drm_i915_gem_relocation_entry reloc; + struct drm_i915_gem_execbuffer2 execbuf; + uint32_t *batch; + int i; + + memset(obj, 0, sizeof(obj)); + obj[0].handle = scratch; + obj[1].handle = gem_create(i915, 4096); + obj[1].relocs_ptr = to_user_pointer(&reloc); + obj[1].relocation_count = 1; + + batch = gem_mmap__cpu(i915, obj[1].handle, 0, 4096, PROT_WRITE); + gem_set_domain(i915, obj[1].handle, + I915_GEM_DOMAIN_CPU, I915_GEM_DOMAIN_CPU); + + i = 0; + batch[i++] = 0x24 << 23 | (1 + r64b); /* SRM */ + batch[i++] = reg; + reloc.target_handle = obj[0].handle; + reloc.presumed_offset = obj[0].offset; + reloc.offset = i * sizeof(uint32_t); + reloc.delta = 0; + reloc.read_domains = I915_GEM_DOMAIN_RENDER; + reloc.write_domain = I915_GEM_DOMAIN_RENDER; + batch[i++] = 0; + batch[i++] = 0; + batch[i++] = MI_BATCH_BUFFER_END; + + igt_assert(i <= 4096 / sizeof(batch[0])); + munmap(batch, 4096); + + memset(&execbuf, 0, sizeof(execbuf)); + execbuf.buffers_ptr = to_user_pointer(obj); + execbuf.buffer_count = 2; + execbuf.flags = engine; + execbuf.rsvd1 = ctx; + gem_execbuf(i915, &execbuf); + gem_close(i915, obj[1].handle); +} + +static void restore_reg(int i915, + uint32_t ctx, unsigned int engine, uint32_t reg, + uint32_t scratch) +{ + const int gen = intel_gen(intel_get_drm_devid(i915)); + const int r64b = gen >= 8; + struct drm_i915_gem_exec_object2 obj[2]; + struct drm_i915_gem_relocation_entry reloc; + struct drm_i915_gem_execbuffer2 execbuf; + uint32_t *batch; + int i; + + memset(obj, 0, sizeof(obj)); + obj[0].handle = scratch; + obj[1].handle = gem_create(i915, 4096); + obj[1].relocs_ptr = to_user_pointer(&reloc); + obj[1].relocation_count = 1; + + batch = gem_mmap__cpu(i915, obj[1].handle, 0, 4096, PROT_WRITE); + gem_set_domain(i915, obj[1].handle, + I915_GEM_DOMAIN_CPU, I915_GEM_DOMAIN_CPU); + + i = 0; + batch[i++] = 0x29 << 23 | (1 + r64b); /* LRM */ + batch[i++] = reg; + reloc.target_handle = obj[0].handle; + reloc.presumed_offset = obj[0].offset; + reloc.offset = i * sizeof(uint32_t); + reloc.delta = 0; + reloc.read_domains = I915_GEM_DOMAIN_RENDER; + reloc.write_domain = 0; + batch[i++] = 0; + batch[i++] = 0; + batch[i++] = MI_BATCH_BUFFER_END; + + igt_assert(i <= 4096 / sizeof(batch[0])); + munmap(batch, 4096); + + memset(&execbuf, 0, sizeof(execbuf)); + execbuf.buffers_ptr = to_user_pointer(obj); + execbuf.buffer_count = 2; + execbuf.flags = engine; + execbuf.rsvd1 = ctx; + gem_execbuf(i915, &execbuf); + gem_close(i915, obj[1].handle); +} + +static void tag(int i915, + uint32_t ctx, unsigned int engine, uint32_t reg, + uint32_t scratch, unsigned int step) +{ + const int gen = intel_gen(intel_get_drm_devid(i915)); + const int r64b = gen >= 8; + struct drm_i915_gem_exec_object2 obj[2]; + struct drm_i915_gem_relocation_entry reloc; + struct drm_i915_gem_execbuffer2 execbuf; + uint32_t *batch; + int i; + + memset(obj, 0, sizeof(obj)); + obj[0].handle = scratch; + obj[1].handle = gem_create(i915, 4096); + obj[1].relocs_ptr = to_user_pointer(&reloc); + obj[1].relocation_count = 1; + + batch = gem_mmap__cpu(i915, obj[1].handle, 0, 4096, PROT_WRITE); + gem_set_domain(i915, obj[1].handle, + I915_GEM_DOMAIN_CPU, I915_GEM_DOMAIN_CPU); + + i = 0; + batch[i++] = 0x20 << 23 | (1 + r64b); + reloc.target_handle = obj[0].handle; + reloc.presumed_offset = obj[0].offset; + reloc.offset = i * sizeof(uint32_t); + reloc.delta = step * sizeof(uint32_t); + reloc.read_domains = I915_GEM_DOMAIN_RENDER; + reloc.write_domain = I915_GEM_DOMAIN_RENDER; + batch[i++] = reloc.delta; + if (r64b) + batch[i++] = 0; + batch[i++] = reg; + batch[i++] = MI_BATCH_BUFFER_END; + + igt_assert(i <= 4096 / sizeof(batch[0])); + munmap(batch, 4096); + + memset(&execbuf, 0, sizeof(execbuf)); + execbuf.buffers_ptr = to_user_pointer(obj); + execbuf.buffer_count = 2; + execbuf.flags = engine; + execbuf.rsvd1 = ctx; + gem_execbuf(i915, &execbuf); + gem_close(i915, obj[1].handle); +} + +static void overwrite_reg(int i915, + uint32_t ctx, unsigned int engine, uint32_t reg, + uint32_t scratch, uint32_t value, int step) +{ + const int gen = intel_gen(intel_get_drm_devid(i915)); + const int r64b = gen >= 8; + struct drm_i915_gem_exec_object2 obj[2]; + struct drm_i915_gem_relocation_entry reloc; + struct drm_i915_gem_execbuffer2 execbuf; + uint32_t *batch; + int i; + + memset(obj, 0, sizeof(obj)); + obj[0].handle = scratch; + obj[1].handle = gem_create(i915, 4096); + obj[1].relocs_ptr = to_user_pointer(&reloc); + obj[1].relocation_count = 1; + + batch = gem_mmap__cpu(i915, obj[1].handle, 0, 4096, PROT_WRITE); + gem_set_domain(i915, obj[1].handle, + I915_GEM_DOMAIN_CPU, I915_GEM_DOMAIN_CPU); + + i = 0; + batch[i++] = 0x22 << 23 | 1; /* LRI */ + batch[i++] = reg; + batch[i++] = value; + + batch[i++] = 0x24 << 23 | (1 + r64b); /* SRM */ + batch[i++] = reg; + reloc.target_handle = obj[0].handle; + reloc.presumed_offset = obj[0].offset; + reloc.offset = i * sizeof(uint32_t); + reloc.delta = step * sizeof(uint32_t); + reloc.read_domains = I915_GEM_DOMAIN_RENDER; + reloc.write_domain = I915_GEM_DOMAIN_RENDER; + batch[i++] = reloc.delta; + batch[i++] = 0; + + batch[i++] = MI_BATCH_BUFFER_END; + + igt_assert(i <= 4096 / sizeof(batch[0])); + munmap(batch, 4096); + + memset(&execbuf, 0, sizeof(execbuf)); + execbuf.buffers_ptr = to_user_pointer(obj); + execbuf.buffer_count = 2; + execbuf.flags = engine; + execbuf.rsvd1 = ctx; + gem_execbuf(i915, &execbuf); + gem_close(i915, obj[1].handle); +} + +__attribute__((format(__printf__,2,3))) +static void dbg(FILE *file, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(file, fmt, ap); + va_end(ap); + + fflush(file); + fsync(fileno(file)); +} + +#define NONE 0 +#define SINGLE 0x1 +#define MANY 0x2 +static void validation(FILE *file, + int i915, unsigned int engine, uint32_t reg, + unsigned int flags) +{ + static const uint32_t values[] = { + 0x00000000, + 0xffffffff, + 0xcccccccc, + 0x33333333, + 0x3c3c3c3c, + 0xc3c3c3c3, + 0x55555555, + 0xaaaaaaaa, + 0xa5a5a5a5, + 0x5a5a5a5a, + 0xdeadbeef + }; + uint32_t scratch = gem_create(i915, 4096); + uint32_t *results = gem_mmap__cpu(i915, scratch, 0, 4096, PROT_READ); + + for (; reg < MAX_REG; reg += 4) { + const char *pass; + igt_spin_t *spin; + uint32_t ctx = 0; + + dbg(file, "0x%05x: ", reg); + + if (flags & MANY) + ctx = gem_context_create(i915); + spin = igt_spin_batch_new(i915, ctx, engine, 0); + tag(i915, ctx, engine, ~reg, scratch, 0); + save_reg(i915, ctx, engine, reg, scratch); + if (flags & MANY) { + gem_context_destroy(i915, ctx); + ctx = 0; + } + + if (flags & SINGLE) + ctx = gem_context_create(i915); + for (unsigned long v = 0; v < ARRAY_SIZE(values); v++) { + if (flags & MANY) + ctx = gem_context_create(i915); + tag(i915, ctx, engine, ~reg, scratch, v + 1); + overwrite_reg(i915, ctx, engine, reg, + scratch, values[v], v + 1); + if (flags & MANY) { + gem_context_destroy(i915, ctx); + ctx = 0; + } + } + if (flags & SINGLE) { + gem_context_destroy(i915, ctx); + ctx = 0; + } + + if (flags & MANY) + ctx = gem_context_create(i915); + restore_reg(i915, ctx, engine, reg, scratch); + tag(i915, ctx, engine, reg, scratch, ARRAY_SIZE(values) + 1); + tag(i915, ctx, engine, ~reg, scratch, ARRAY_SIZE(values) + 2); + igt_spin_batch_free(i915, spin); + if (flags & MANY) { + gem_context_destroy(i915, ctx); + ctx = 0; + } + + gem_set_domain(i915, scratch, I915_GEM_DOMAIN_CPU, 0); + igt_assert_eq_u32(results[ARRAY_SIZE(values) + 1], reg); + igt_assert_eq_u32(results[ARRAY_SIZE(values) + 2], ~reg); + + if (ignore_register(reg)) { + pass = "ignored "; + } else { + unsigned access = 0; +#define ZERO 0x1 +#define READ 0x2 +#define WRITE 0x4 +#define PARTIAL 0x8 +#define GARBAGE 0x10 + fprintf(file, "%08x ", results[0]); + for (unsigned long v = 0; v < ARRAY_SIZE(values); v++) { + uint32_t result = results[v + 1]; + const char *t; + + if (result == ~reg) { + t = ""; + } else if (result == values[v] && + results[0] != values[v]) { + t = "w"; + access |= WRITE; + } else if ((result & 0xffff )== (values[v] & 0xffff) && + results[0] != values[v]) { + t = "p"; + access |= PARTIAL; + } else if (result == 0) { + t = ""; + access |= ZERO; + } else if (result == results[0]) { + t = "r"; + access |= READ; + } else { + t = "!"; + access |= GARBAGE; + } + fprintf(file, "%08x%s ", result, t); + } + + if (access == 0) { + pass = "noaccess"; + } else if (access & GARBAGE) { + pass = "garbage "; + } else if (access & WRITE) { + pass = "write "; + } else if (access & PARTIAL) { + pass = "partial "; + } else if (access & ZERO) { + pass = "zero "; + } else { + pass = "read "; + } + } + + dbg(file, "%s\n", pass); + igt_info("0x%05x %s\r", reg, pass); + fflush(stdout); + } + + munmap(results, 4096); + gem_close(i915, scratch); +} + +igt_main +{ + static const struct phase { + const char *name; + unsigned int flags; + } phases[] = { + { "none", 0 }, + { "single", SINGLE }, + { "many", MANY }, + {} + }; + int i915 = -1; + + igt_fixture { + i915 = drm_open_driver(DRIVER_INTEL); + igt_require_gem(i915); + + gem_context_destroy(i915, gem_context_create(i915)); + } + + for (const struct intel_execution_engine *e = + intel_execution_engines; e->name; e++) { + unsigned int engine = e->exec_id | e->flags; + for (const struct phase *p = phases; p->name; p++) { + igt_subtest_f("%s-%s", e->name, p->name) { + uint32_t start = 0; + bool eol = true; + char buf[128]; + FILE *file; + + gem_require_ring(i915, engine); + + snprintf(buf, sizeof(buf), + "gem_ctx_validation.%s.%s", e->name, p->name); + file = fopen(buf, "r"); + if (file) { + char *line = NULL; + size_t len = 0; + + while (getline(&line, &len, file) != -1) { + uint32_t tmp; + if (sscanf(line, "%x:", &tmp) == 1) + start = tmp; + eol = strchr(line, '\n'); + } + fclose(file); + free(line); + + if (!eol) + start += 4; + + igt_info("%s: Continuing from 0x%x\n", + e->name, start); + } + + file = fopen(buf, "a"); + igt_assert(file); + if (!eol) + fprintf(file, " crash\n"); + + validation(file, i915, engine, start, p->flags); + + fclose(file); + } + } + } +} diff --git a/tests/gem_exec_validation.c b/tests/gem_exec_validation.c new file mode 100644 index 00000000..153ed749 --- /dev/null +++ b/tests/gem_exec_validation.c @@ -0,0 +1,402 @@ +/* + * Copyright © 2017 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 "igt_dummyload.h" + +#define MAX_REG 0x40000 +#define NUM_REGS (MAX_REG / sizeof(uint32_t)) + +#define PAGE_ALIGN(x) ALIGN(x, 4096) + +#define DIRTY1 0x1 +#define DIRTY2 0x2 + +enum { + RCS_MASK = 0x1, + BCS_MASK = 0x2, + VCS_MASK = 0x4, + VECS_MASK = 0x8, +}; + +#define ALL ~0u +#define GEN_RANGE(x, y) ((ALL >> (32 - (y - x + 1))) << x) +#define GEN6 (ALL << 6) +#define GEN7 (ALL << 7) +#define GEN8 (ALL << 8) +#define GEN9 (ALL << 9) + +#define NOCTX 0 + +#define LAST_KNOWN_GEN 10 + +static const struct named_register { + const char *name; + unsigned int gen_mask; + unsigned int engine_mask; + uint32_t offset; + uint32_t count; +} safe_registers[] = { + { "NOPID", NOCTX, RCS_MASK, 0x2094 }, + { "MI_PREDICATE_RESULT_2", NOCTX, RCS_MASK, 0x23bc }, + { "INSTPM", GEN8, RCS_MASK, 0x20c0 }, + { "IA_VERTICES_COUNT", GEN6, RCS_MASK, 0x2310, 2 }, + { "IA_PRIMITIVES_COUNT", GEN6, RCS_MASK, 0x2318, 2 }, + { "VS_INVOCATION_COUNT", GEN6, RCS_MASK, 0x2320, 2 }, + { "HS_INVOCATION_COUNT", GEN6, RCS_MASK, 0x2300, 2 }, + { "DS_INVOCATION_COUNT", GEN6, RCS_MASK, 0x2308, 2 }, + { "GS_INVOCATION_COUNT", GEN6, RCS_MASK, 0x2328, 2 }, + { "GS_PRIMITIVES_COUNT", GEN6, RCS_MASK, 0x2330, 2 }, + { "CL_INVOCATION_COUNT", GEN6, RCS_MASK, 0x2338, 2 }, + { "CL_PRIMITIVES_COUNT", GEN6, RCS_MASK, 0x2340, 2 }, + { "PS_INVOCATION_COUNT_0", GEN6, RCS_MASK, 0x22c8, 2 }, + { "PS_DEPTH_COUNT_0", GEN6, RCS_MASK, 0x22d8, 2 }, + { "GPUGPU_DISPATCHDIMX", GEN8, RCS_MASK, 0x2500 }, + { "GPUGPU_DISPATCHDIMY", GEN8, RCS_MASK, 0x2504 }, + { "GPUGPU_DISPATCHDIMZ", GEN8, RCS_MASK, 0x2508 }, + { "MI_PREDICATE_SRC0", GEN8, RCS_MASK, 0x2400, 2 }, + { "MI_PREDICATE_SRC1", GEN8, RCS_MASK, 0x2408, 2 }, + { "MI_PREDICATE_DATA", GEN8, RCS_MASK, 0x2410, 2 }, + { "MI_PRED_RESULT", GEN8, RCS_MASK, 0x2418 }, + { "3DPRIM_END_OFFSET", GEN6, RCS_MASK, 0x2420 }, + { "3DPRIM_START_VERTEX", GEN6, RCS_MASK, 0x2430 }, + { "3DPRIM_VERTEX_COUNT", GEN6, RCS_MASK, 0x2434 }, + { "3DPRIM_INSTANCE_COUNT", GEN6, RCS_MASK, 0x2438 }, + { "3DPRIM_START_INSTANCE", GEN6, RCS_MASK, 0x243c }, + { "3DPRIM_BASE_VERTEX", GEN6, RCS_MASK, 0x2440 }, + { "GPGPU_THREADS_DISPATCHED", GEN8, RCS_MASK, 0x2290, 2 }, + { "PS_INVOCATION_COUNT_1", GEN8, RCS_MASK, 0x22f0, 2 }, + { "PS_DEPTH_COUNT_1", GEN8, RCS_MASK, 0x22f8, 2 }, + { "BB_OFFSET", GEN8, RCS_MASK, 0x2158 }, + { "MI_PREDICATE_RESULT_1", GEN8, RCS_MASK, 0x241c }, + { "CS_GPR", GEN8, RCS_MASK, 0x2600, 32 }, + { "OA_CTX_CONTROL", GEN8, RCS_MASK, 0x2360 }, + { "OACTXID", GEN8, RCS_MASK, 0x2364 }, + { "PS_INVOCATION_COUNT_2", GEN8, RCS_MASK, 0x2448, 2 }, + { "PS_DEPTH_COUNT_2", GEN8, RCS_MASK, 0x2450, 2 }, + { "Cache_Mode_0", GEN7, RCS_MASK, 0x7000 }, + { "Cache_Mode_1", GEN7, RCS_MASK, 0x7004 }, + { "GT_MODE", GEN8, RCS_MASK, 0x7008 }, + { "L3_Config", GEN7, RCS_MASK, 0x7034 }, + { "TD_CTL", GEN8, RCS_MASK, 0xe400 }, + { "TD_CTL2", GEN8, RCS_MASK, 0xe404 }, + { "SO_NUM_PRIMS_WRITEN0", GEN6, RCS_MASK, 0x5200, 2 }, + { "SO_NUM_PRIMS_WRITEN1", GEN6, RCS_MASK, 0x5208, 2 }, + { "SO_NUM_PRIMS_WRITEN2", GEN6, RCS_MASK, 0x5210, 2 }, + { "SO_NUM_PRIMS_WRITEN3", GEN6, RCS_MASK, 0x5218, 2 }, + { "SO_PRIM_STORAGE_NEEDED0", GEN6, RCS_MASK, 0x5240, 2 }, + { "SO_PRIM_STORAGE_NEEDED1", GEN6, RCS_MASK, 0x5248, 2 }, + { "SO_PRIM_STORAGE_NEEDED2", GEN6, RCS_MASK, 0x5250, 2 }, + { "SO_PRIM_STORAGE_NEEDED3", GEN6, RCS_MASK, 0x5258, 2 }, + { "SO_WRITE_OFFSET0", GEN7, RCS_MASK, 0x5280 }, + { "SO_WRITE_OFFSET1", GEN7, RCS_MASK, 0x5284 }, + { "SO_WRITE_OFFSET2", GEN7, RCS_MASK, 0x5288 }, + { "SO_WRITE_OFFSET3", GEN7, RCS_MASK, 0x528c }, + { "OA_CONTROL", NOCTX, RCS_MASK, 0x2b00 }, + { "PERF_CNT_1", NOCTX, RCS_MASK, 0x91b8, 2 }, + { "PERF_CNT_2", NOCTX, RCS_MASK, 0x91c0, 2 }, + + /* Privileged (enabled by w/a + FORCE_TO_NONPRIV) */ + { "CTX_PREEMPT", NOCTX /* GEN_RANGE(9, 10) */, RCS_MASK, 0x2248 }, + { "CS_CHICKEN1", GEN_RANGE(9, 10), RCS_MASK, 0x2580 }, + { "HDC_CHICKEN1", GEN_RANGE(9, 10), RCS_MASK, 0x7304 }, + { "L3SQREG1", GEN8, RCS_MASK, 0xb010 }, + + { "BCS_GPR", GEN9, BCS_MASK, 0x22600, 32 }, + { "BCS_SWCTRL", GEN8, BCS_MASK, 0x22200 }, + + { "VCS0_GPR", GEN9, VCS_MASK, 0x12600, 32 }, + { "MFC_VDBOX1", NOCTX, VCS_MASK, 0x12800, 64 }, + + { "VCS1_GPR", GEN9, VCS_MASK, 0x1c600, 32 }, + { "MFC_VDBOX2", NOCTX, VCS_MASK, 0x1c800, 64 }, + + { "VECS_GPR", GEN9, VECS_MASK, 0x1a600, 32 }, + + {} +}, ignore_registers[] = { + { "RCS timestamp", GEN6, RCS_MASK, 0x2358 }, + { "VCS0 timestamp", GEN7, VCS_MASK, 0x12358 }, + { "VCS1 timestamp", GEN7, VCS_MASK, 0x1c358 }, + { "BCS timestamp", GEN7, BCS_MASK, 0x22358 }, + { "VECS timestamp", GEN8, VECS_MASK, 0x1a358 }, + {} +}; + +static const char *register_name(uint32_t reg, char *buf, size_t len) +{ + for (const struct named_register *r = safe_registers; r->name; r++) { + unsigned int width = r->count ? 4*r->count : 4; + if (reg >= r->offset && reg < r->offset + width) { + if (r->count <= 1) + return r->name; + + snprintf(buf, len, "%s[%d]", + r->name, (reg - r->offset)/4); + return buf; + } + } + + return "unknown"; +} + +static bool ignore_register(uint32_t reg) +{ + for (const struct named_register *r = ignore_registers; r->name; r++) { + unsigned int width = r->count ? 4*r->count : 4; + if (reg >= r->offset && reg < r->offset + width) + return true; + } + + return false; +} + +static bool safe_register(uint32_t reg) +{ + /* XXX Check gen */ + for (const struct named_register *r = safe_registers; r->name; r++) { + unsigned int width = r->count ? 4*r->count : 4; + if (reg >= r->offset && reg < r->offset + width) + return true; + } + + return false; +} + +static uint32_t overwrite_reg(int i915, unsigned int engine, unsigned int reg, + const uint32_t *values, int count) +{ + const int gen = intel_gen(intel_get_drm_devid(i915)); + const bool r64b = gen >= 8; + struct drm_i915_gem_exec_object2 obj[2]; + struct drm_i915_gem_relocation_entry reloc[count + 2]; + struct drm_i915_gem_execbuffer2 execbuf; + uint32_t *batch; + int i, j; + + memset(obj, 0, sizeof(obj)); + obj[0].handle = gem_create(i915, 4096); + obj[1].handle = gem_create(i915, 4096); + obj[1].relocs_ptr = to_user_pointer(reloc); + + batch = gem_mmap__cpu(i915, obj[1].handle, 0, 4096, PROT_WRITE); + gem_set_domain(i915, obj[1].handle, + I915_GEM_DOMAIN_CPU, I915_GEM_DOMAIN_CPU); + + i = j = 0; + batch[i++] = 0x24 << 23 | (1 + r64b); /* SRM */ + batch[i++] = reg; + reloc[j].target_handle = obj[0].handle; + reloc[j].presumed_offset = obj[0].offset; + reloc[j].offset = i * sizeof(uint32_t); + reloc[j].delta = 0; + reloc[j].read_domains = I915_GEM_DOMAIN_RENDER; + reloc[j].write_domain = I915_GEM_DOMAIN_RENDER; + batch[i++] = reloc[j++].delta; + if (r64b) + batch[i++] = 0; + + for (int n = 0; n < count; n++) { + batch[i++] = 0x22 << 23 | 1; /* LRI */ + batch[i++] = reg; + batch[i++] = values[n]; + + batch[i++] = 0x24 << 23 | (1 + r64b); /* SRM */ + batch[i++] = reg; + reloc[j].target_handle = obj[0].handle; + reloc[j].presumed_offset = obj[0].offset; + reloc[j].offset = i * sizeof(uint32_t); + reloc[j].delta = (n + 1) * sizeof(uint32_t); + reloc[j].read_domains = I915_GEM_DOMAIN_RENDER; + reloc[j].write_domain = I915_GEM_DOMAIN_RENDER; + batch[i++] = reloc[j++].delta; + if (r64b) + batch[i++] = 0; + } + + batch[i++] = 0x29 << 23 | (1 + r64b); /* LRM */ + batch[i++] = reg; + reloc[j].target_handle = obj[0].handle; + reloc[j].presumed_offset = 0; + reloc[j].offset = i * sizeof(uint32_t); + reloc[j].delta = 0; + reloc[j].read_domains = I915_GEM_DOMAIN_RENDER; + reloc[j].write_domain = 0; + batch[i++] = reloc[j++].delta; + if (r64b) + batch[i++] = 0; + + batch[i++] = MI_BATCH_BUFFER_END; + obj[1].relocation_count = j; + + igt_assert(i <= 4096 / sizeof(batch[0])); + munmap(batch, 4096); + + memset(&execbuf, 0, sizeof(execbuf)); + execbuf.buffers_ptr = to_user_pointer(obj); + execbuf.buffer_count = 2; + execbuf.flags = engine; + gem_execbuf(i915, &execbuf); + gem_close(i915, obj[1].handle); + + return obj[0].handle; +} + +static bool compare_result(int i915, uint32_t results, int n, unsigned int reg) +{ + bool result = true; + char buf[80]; + uint32_t *r; + + if (ignore_register(reg)) + return true; + + r = gem_mmap__cpu(i915, results, 0, 4096, PROT_READ); + gem_set_domain(i915, results, I915_GEM_DOMAIN_CPU, 0); + if (r[0] != r[n] && !safe_register(reg)) { + igt_warn("Register 0x%04x (%s): A=%08x B=%08x\n", + reg, + register_name(reg, buf, sizeof(buf)), + r[0], r[n]); + result = false; + } + munmap(r, 4096); + + return result; +} + +__attribute__((format(__printf__,2,3))) +static void dbg(FILE *file, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(file, fmt, ap); + va_end(ap); + + fflush(file); + fsync(fileno(file)); +} + +static void validation(FILE *file, + int i915, unsigned int engine, unsigned int reg) +{ + static const uint32_t values[] = { + 0x00000000, + 0xffffffff, + 0xcccccccc, + 0x33333333, + 0x3c3c3c3c, + 0xc3c3c3c3, + 0x55555555, + 0xaaaaaaaa, + 0xa5a5a5a5, + 0x5a5a5a5a, + 0xdeadbeef + }; + + for (; reg < MAX_REG; reg += 4) { + const char *pass = "ok", *eol = "\r"; + uint32_t res; + + dbg(file, "0x%05x: ", reg); + + res = overwrite_reg(i915, engine, reg, + values, ARRAY_SIZE(values)); + for (unsigned long v = 0; v < ARRAY_SIZE(values); v++) { + const char *out; + + if (compare_result(i915, res, v + 1, reg)) { + out = "+ "; + } else { + out = "! "; + pass = "bad"; + eol = "\n"; + } + dbg(file, "%08x%s", values[v], out); + + } + gem_close(i915, res); + + dbg(file, "%s\n", pass); + igt_info("0x%05x %s%s", reg, pass, eol); + fflush(stdout); + } +} + +igt_main +{ + int i915 = -1; + + igt_fixture { + i915 = drm_open_driver(DRIVER_INTEL); + igt_require_gem(i915); + + gem_context_destroy(i915, gem_context_create(i915)); + } + + for (const struct intel_execution_engine *e = + intel_execution_engines; e->name; e++) { + unsigned int engine = e->exec_id | e->flags; + igt_subtest_f("%s", e->name) { + uint32_t start = 0; + bool eol = true; + char buf[128]; + FILE *file; + + gem_require_ring(i915, engine); + + snprintf(buf, sizeof(buf), + "gem_exec_validation.%s", e->name); + file = fopen(buf, "r"); + if (file) { + char *line = NULL; + size_t len = 0; + + while (getline(&line, &len, file) != -1) { + uint32_t tmp; + if (sscanf(line, "%x:", &tmp) == 1) + start = tmp; + eol = strchr(line, '\n'); + } + fclose(file); + free(line); + + if (!eol) + start += 4; + + igt_info("%s: Continuing from 0x%x\n", + e->name, start); + } + + file = fopen(buf, "a"); + igt_assert(file); + if (!eol) + fprintf(file, " crash\n"); + + validation(file, i915, engine, start); + + fclose(file); + } + } +} |