summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Wilson <chris@chris-wilson.co.uk>2017-11-08 14:38:50 +0000
committerChris Wilson <chris@chris-wilson.co.uk>2018-03-15 10:52:37 +0000
commit44196e8b513975ed450a555897fa84e5594bc300 (patch)
treeb26dfeb08ee1e4bbbcb908fd94204695a99982bf
parentd17869a3e7e058b52963e2ee3f708ba759be42fb (diff)
validation
-rw-r--r--tests/Makefile.sources2
-rw-r--r--tests/gem_ctx_validation.c468
-rw-r--r--tests/gem_exec_validation.c402
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);
+ }
+ }
+}