All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH i-g-t 1/7] lib/igt_dummyload: Send batch as first
@ 2019-11-13 15:49 ` Mika Kuoppala
  0 siblings, 0 replies; 44+ messages in thread
From: Mika Kuoppala @ 2019-11-13 15:49 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

To simplify emitting the recursive batch, make batch
always the first object on the execbuf list.

v2: set handles early, poll_ptr indecency (Chris)
v3: allow dep with poll
v4: fix gem_exec_schedule
v5: rebase
v6: rebase
v6: gem_ctx_shared
v7: conditional close of poll handle

Cc: Chris Wilson <chris@chris-wilson.co.uk>
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
---
 lib/igt_dummyload.c            | 111 +++++++++++++++++----------------
 lib/igt_dummyload.h            |   8 ++-
 tests/i915/gem_exec_balancer.c |   8 +--
 tests/i915/gem_spin_batch.c    |  13 ++--
 4 files changed, 73 insertions(+), 67 deletions(-)

diff --git a/lib/igt_dummyload.c b/lib/igt_dummyload.c
index b9e239db..c079bd04 100644
--- a/lib/igt_dummyload.c
+++ b/lib/igt_dummyload.c
@@ -63,6 +63,7 @@
 #define MI_ARB_CHK (0x5 << 23)
 
 static const int BATCH_SIZE = 4096;
+static const int POLL_SIZE = 4096;
 static const int LOOP_START_OFFSET = 64;
 
 static IGT_LIST_HEAD(spin_list);
@@ -72,16 +73,23 @@ static int
 emit_recursive_batch(igt_spin_t *spin,
 		     int fd, const struct igt_spin_factory *opts)
 {
-#define SCRATCH 0
+
 #define BATCH IGT_SPIN_BATCH
+#define POLL 1
+#define DEP     2
 	const int gen = intel_gen(intel_get_drm_devid(fd));
-	struct drm_i915_gem_relocation_entry relocs[2], *r;
+	struct drm_i915_gem_exec_object2 * const batch =
+		&spin->obj[BATCH];
+	struct drm_i915_gem_exec_object2 * const poll =
+		&spin->obj[POLL];
+	struct drm_i915_gem_exec_object2 * const dep =
+		&spin->obj[DEP];
+	struct drm_i915_gem_relocation_entry relocs[4], *r;
 	struct drm_i915_gem_execbuffer2 *execbuf;
-	struct drm_i915_gem_exec_object2 *obj;
 	unsigned int flags[GEM_MAX_ENGINES];
 	unsigned int nengine;
 	int fence_fd = -1;
-	uint32_t *cs, *batch;
+	uint32_t *cs, *batch_start;
 	int i;
 
 	nengine = 0;
@@ -103,64 +111,48 @@ emit_recursive_batch(igt_spin_t *spin,
 	memset(&spin->execbuf, 0, sizeof(spin->execbuf));
 	execbuf = &spin->execbuf;
 	memset(spin->obj, 0, sizeof(spin->obj));
-	obj = spin->obj;
 	memset(relocs, 0, sizeof(relocs));
 
-	obj[BATCH].handle = gem_create(fd, BATCH_SIZE);
-	batch = __gem_mmap__wc(fd, obj[BATCH].handle,
+	batch->handle = gem_create(fd, BATCH_SIZE);
+	spin->handle = batch->handle;
+
+	batch_start = __gem_mmap__wc(fd, batch->handle,
 				     0, BATCH_SIZE, PROT_WRITE);
-	if (!batch)
-		batch = gem_mmap__gtt(fd, obj[BATCH].handle,
+	if (!batch_start)
+		batch_start = gem_mmap__gtt(fd, batch->handle,
 					    BATCH_SIZE, PROT_WRITE);
-
-	gem_set_domain(fd, obj[BATCH].handle,
+	gem_set_domain(fd, batch->handle,
 		       I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
 	execbuf->buffer_count++;
-	cs = batch;
-
-	if (opts->dependency) {
-		igt_assert(!(opts->flags & IGT_SPIN_POLL_RUN));
-
-		r = &relocs[obj[BATCH].relocation_count++];
-
-		/* dummy write to dependency */
-		obj[SCRATCH].handle = opts->dependency;
-		r->presumed_offset = 0;
-		r->target_handle = obj[SCRATCH].handle;
-		r->offset = sizeof(uint32_t) * 1020;
-		r->delta = 0;
-		r->read_domains = I915_GEM_DOMAIN_RENDER;
-		r->write_domain = I915_GEM_DOMAIN_RENDER;
+	cs = batch_start;
 
+	poll->handle = gem_create(fd, POLL_SIZE);
+	spin->poll_handle = poll->handle;
 	execbuf->buffer_count++;
-	} else if (opts->flags & IGT_SPIN_POLL_RUN) {
-		r = &relocs[obj[BATCH].relocation_count++];
 
-		igt_assert(!opts->dependency);
+	if (opts->flags & IGT_SPIN_POLL_RUN) {
+		r = &relocs[batch->relocation_count++];
 
 		if (gen == 4 || gen == 5) {
 			execbuf->flags |= I915_EXEC_SECURE;
 			igt_require(__igt_device_set_master(fd) == 0);
 		}
 
-		spin->poll_handle = gem_create(fd, 4096);
-		obj[SCRATCH].handle = spin->poll_handle;
-
-		if (__gem_set_caching(fd, spin->poll_handle,
+		if (__gem_set_caching(fd, poll->handle,
 				      I915_CACHING_CACHED) == 0)
-			spin->poll = gem_mmap__cpu(fd, spin->poll_handle,
-						   0, 4096,
+			spin->poll = gem_mmap__cpu(fd, poll->handle,
+						   0, POLL_SIZE,
 						   PROT_READ | PROT_WRITE);
 		else
-			spin->poll = gem_mmap__wc(fd, spin->poll_handle,
-						  0, 4096,
+			spin->poll = gem_mmap__wc(fd, poll->handle,
+						  0, POLL_SIZE,
 						  PROT_READ | PROT_WRITE);
 
 		igt_assert_eq(spin->poll[SPIN_POLL_START_IDX], 0);
 
 		/* batch is first */
-		r->presumed_offset = 4096;
-		r->target_handle = obj[SCRATCH].handle;
+		r->presumed_offset = BATCH_SIZE;
+		r->target_handle = poll->handle;
 		r->offset = sizeof(uint32_t) * 1;
 		r->delta = sizeof(uint32_t) * SPIN_POLL_START_IDX;
 
@@ -179,14 +171,25 @@ emit_recursive_batch(igt_spin_t *spin,
 		}
 
 		*cs++ = 1;
+	}
+
+	if (opts->dependency) {
+		r = &relocs[batch->relocation_count++];
+
+		/* dummy write to dependency */
+		dep->handle = opts->dependency;
+		r->presumed_offset = BATCH_SIZE + POLL_SIZE;
+		r->target_handle = dep->handle;
+		r->offset = sizeof(uint32_t) * 1020;
+		r->delta = 0;
+		r->read_domains = I915_GEM_DOMAIN_RENDER;
+		r->write_domain = I915_GEM_DOMAIN_RENDER;
 
 		execbuf->buffer_count++;
 	}
 
-	spin->handle = obj[BATCH].handle;
-
-	igt_assert_lt(cs - batch, LOOP_START_OFFSET / sizeof(*cs));
-	spin->condition = batch + LOOP_START_OFFSET / sizeof(*cs);
+	igt_assert_lt(cs - batch_start, LOOP_START_OFFSET / sizeof(*cs));
+	spin->condition = batch_start + LOOP_START_OFFSET / sizeof(*cs);
 	cs = spin->condition;
 
 	/* Allow ourselves to be preempted */
@@ -208,9 +211,9 @@ emit_recursive_batch(igt_spin_t *spin,
 		cs += 1000;
 
 	/* recurse */
-	r = &relocs[obj[BATCH].relocation_count++];
-	r->target_handle = obj[BATCH].handle;
-	r->offset = (cs + 1 - batch) * sizeof(*cs);
+	r = &relocs[batch->relocation_count++];
+	r->target_handle = batch->handle;
+	r->offset = (cs + 1 - batch_start) * sizeof(*cs);
 	r->read_domains = I915_GEM_DOMAIN_COMMAND;
 	r->delta = LOOP_START_OFFSET;
 	if (gen >= 8) {
@@ -227,10 +230,10 @@ emit_recursive_batch(igt_spin_t *spin,
 		*cs = r->delta;
 		cs++;
 	}
-	obj[BATCH].relocs_ptr = to_user_pointer(relocs);
+	batch->relocs_ptr = to_user_pointer(relocs);
 
-	execbuf->buffers_ptr = to_user_pointer(obj +
-					       (2 - execbuf->buffer_count));
+	execbuf->buffers_ptr = to_user_pointer(spin->obj);
+	execbuf->flags |= I915_EXEC_BATCH_FIRST;
 	execbuf->rsvd1 = opts->ctx;
 
 	if (opts->flags & IGT_SPIN_FENCE_OUT)
@@ -264,10 +267,9 @@ emit_recursive_batch(igt_spin_t *spin,
 		}
 	}
 
-	igt_assert_lt(cs - batch, BATCH_SIZE / sizeof(*cs));
+	igt_assert_lt(cs - batch_start, BATCH_SIZE / sizeof(*cs));
 
-	/* Make it easier for callers to resubmit. */
-	for (i = 0; i < ARRAY_SIZE(spin->obj); i++) {
+	for (i = 0; i < execbuf->buffer_count; i++) {
 		spin->obj[i].relocation_count = 0;
 		spin->obj[i].relocs_ptr = 0;
 		spin->obj[i].flags = EXEC_OBJECT_PINNED;
@@ -445,10 +447,11 @@ void igt_spin_free(int fd, igt_spin_t *spin)
 	gem_munmap((void *)((unsigned long)spin->condition & (~4095UL)),
 		   BATCH_SIZE);
 
-	if (spin->poll) {
+	if (spin->poll)
 		gem_munmap(spin->poll, 4096);
+
+	if (spin->poll_handle)
 		gem_close(fd, spin->poll_handle);
-	}
 
 	if (spin->handle)
 		gem_close(fd, spin->handle);
diff --git a/lib/igt_dummyload.h b/lib/igt_dummyload.h
index 421ca183..de5781d7 100644
--- a/lib/igt_dummyload.h
+++ b/lib/igt_dummyload.h
@@ -34,6 +34,7 @@
 
 typedef struct igt_spin {
 	unsigned int handle;
+
 	timer_t timer;
 	struct igt_list_head link;
 
@@ -41,9 +42,12 @@ typedef struct igt_spin {
 	uint32_t cmd_precondition;
 
 	int out_fence;
-	struct drm_i915_gem_exec_object2 obj[2];
-#define IGT_SPIN_BATCH   1
+	struct drm_i915_gem_exec_object2 obj[3];
+#define IGT_SPIN_BATCH   0
+#define IGT_SPIN_POLL    1
+
 	struct drm_i915_gem_execbuffer2 execbuf;
+
 	uint32_t poll_handle;
 	uint32_t *poll;
 #define SPIN_POLL_START_IDX 0
diff --git a/tests/i915/gem_exec_balancer.c b/tests/i915/gem_exec_balancer.c
index 70c4529b..19df406c 100644
--- a/tests/i915/gem_exec_balancer.c
+++ b/tests/i915/gem_exec_balancer.c
@@ -718,7 +718,7 @@ static uint32_t create_semaphore_to_spinner(int i915, igt_spin_t *spin)
 	cs = map = gem_mmap__cpu(i915, handle, 0, 4096, PROT_WRITE);
 
 	/* Wait until the spinner is running */
-	addr = spin->obj[0].offset + 4 * SPIN_POLL_START_IDX;
+	addr = spin->obj[IGT_SPIN_POLL].offset + 4 * SPIN_POLL_START_IDX;
 	*cs++ = MI_SEMAPHORE_WAIT |
 		MI_SEMAPHORE_POLL |
 		MI_SEMAPHORE_SAD_NEQ_SDD |
@@ -797,9 +797,9 @@ static void bonded_slice(int i915)
 		igt_spin_reset(spin);
 
 		/* igt_spin_t poll and batch obj must be laid out as we expect */
-		igt_assert_eq(IGT_SPIN_BATCH, 1);
-		obj[0] = spin->obj[0];
-		obj[1] = spin->obj[1];
+		igt_assert_eq(IGT_SPIN_BATCH, 0);
+		obj[0] = spin->obj[IGT_SPIN_POLL];
+		obj[1] = spin->obj[IGT_SPIN_BATCH];
 		obj[2].handle = create_semaphore_to_spinner(i915, spin);
 
 		eb.buffers_ptr = to_user_pointer(obj);
diff --git a/tests/i915/gem_spin_batch.c b/tests/i915/gem_spin_batch.c
index c67f015f..04707fdc 100644
--- a/tests/i915/gem_spin_batch.c
+++ b/tests/i915/gem_spin_batch.c
@@ -78,12 +78,9 @@ static void spin_resubmit(int fd, const struct intel_execution_engine2 *e2,
 		gem_context_create(fd) : ctx0;
 	igt_spin_t *spin = __igt_spin_new(fd, .ctx = ctx0, .engine = e2->flags);
 	const struct intel_execution_engine2 *other;
+	struct drm_i915_gem_execbuffer2 eb = spin->execbuf;
 
-	struct drm_i915_gem_execbuffer2 eb = {
-		.buffer_count = 1,
-		.buffers_ptr = to_user_pointer(&spin->obj[IGT_SPIN_BATCH]),
-		.rsvd1 = ctx1,
-	};
+	eb.rsvd1 = ctx1;
 
 	igt_assert(gem_context_has_engine_map(fd, 0) ||
 		   !(flags & RESUBMIT_ALL_ENGINES));
@@ -97,11 +94,13 @@ static void spin_resubmit(int fd, const struct intel_execution_engine2 *e2,
 			if (gem_engine_is_equal(other, e2))
 				continue;
 
-			eb.flags = other->flags;
+			eb.flags &= ~(I915_EXEC_RING_MASK | I915_EXEC_BSD_MASK);
+			eb.flags |= other->flags;
 			gem_execbuf(fd, &eb);
 		}
 	} else {
-		eb.flags = e2->flags;
+		eb.flags &= ~(I915_EXEC_RING_MASK | I915_EXEC_BSD_MASK);
+		eb.flags |= e2->flags;
 		gem_execbuf(fd, &eb);
 	}
 
-- 
2.17.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [Intel-gfx] [PATCH i-g-t 1/7] lib/igt_dummyload: Send batch as first
@ 2019-11-13 15:49 ` Mika Kuoppala
  0 siblings, 0 replies; 44+ messages in thread
From: Mika Kuoppala @ 2019-11-13 15:49 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

To simplify emitting the recursive batch, make batch
always the first object on the execbuf list.

v2: set handles early, poll_ptr indecency (Chris)
v3: allow dep with poll
v4: fix gem_exec_schedule
v5: rebase
v6: rebase
v6: gem_ctx_shared
v7: conditional close of poll handle

Cc: Chris Wilson <chris@chris-wilson.co.uk>
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
---
 lib/igt_dummyload.c            | 111 +++++++++++++++++----------------
 lib/igt_dummyload.h            |   8 ++-
 tests/i915/gem_exec_balancer.c |   8 +--
 tests/i915/gem_spin_batch.c    |  13 ++--
 4 files changed, 73 insertions(+), 67 deletions(-)

diff --git a/lib/igt_dummyload.c b/lib/igt_dummyload.c
index b9e239db..c079bd04 100644
--- a/lib/igt_dummyload.c
+++ b/lib/igt_dummyload.c
@@ -63,6 +63,7 @@
 #define MI_ARB_CHK (0x5 << 23)
 
 static const int BATCH_SIZE = 4096;
+static const int POLL_SIZE = 4096;
 static const int LOOP_START_OFFSET = 64;
 
 static IGT_LIST_HEAD(spin_list);
@@ -72,16 +73,23 @@ static int
 emit_recursive_batch(igt_spin_t *spin,
 		     int fd, const struct igt_spin_factory *opts)
 {
-#define SCRATCH 0
+
 #define BATCH IGT_SPIN_BATCH
+#define POLL 1
+#define DEP     2
 	const int gen = intel_gen(intel_get_drm_devid(fd));
-	struct drm_i915_gem_relocation_entry relocs[2], *r;
+	struct drm_i915_gem_exec_object2 * const batch =
+		&spin->obj[BATCH];
+	struct drm_i915_gem_exec_object2 * const poll =
+		&spin->obj[POLL];
+	struct drm_i915_gem_exec_object2 * const dep =
+		&spin->obj[DEP];
+	struct drm_i915_gem_relocation_entry relocs[4], *r;
 	struct drm_i915_gem_execbuffer2 *execbuf;
-	struct drm_i915_gem_exec_object2 *obj;
 	unsigned int flags[GEM_MAX_ENGINES];
 	unsigned int nengine;
 	int fence_fd = -1;
-	uint32_t *cs, *batch;
+	uint32_t *cs, *batch_start;
 	int i;
 
 	nengine = 0;
@@ -103,64 +111,48 @@ emit_recursive_batch(igt_spin_t *spin,
 	memset(&spin->execbuf, 0, sizeof(spin->execbuf));
 	execbuf = &spin->execbuf;
 	memset(spin->obj, 0, sizeof(spin->obj));
-	obj = spin->obj;
 	memset(relocs, 0, sizeof(relocs));
 
-	obj[BATCH].handle = gem_create(fd, BATCH_SIZE);
-	batch = __gem_mmap__wc(fd, obj[BATCH].handle,
+	batch->handle = gem_create(fd, BATCH_SIZE);
+	spin->handle = batch->handle;
+
+	batch_start = __gem_mmap__wc(fd, batch->handle,
 				     0, BATCH_SIZE, PROT_WRITE);
-	if (!batch)
-		batch = gem_mmap__gtt(fd, obj[BATCH].handle,
+	if (!batch_start)
+		batch_start = gem_mmap__gtt(fd, batch->handle,
 					    BATCH_SIZE, PROT_WRITE);
-
-	gem_set_domain(fd, obj[BATCH].handle,
+	gem_set_domain(fd, batch->handle,
 		       I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
 	execbuf->buffer_count++;
-	cs = batch;
-
-	if (opts->dependency) {
-		igt_assert(!(opts->flags & IGT_SPIN_POLL_RUN));
-
-		r = &relocs[obj[BATCH].relocation_count++];
-
-		/* dummy write to dependency */
-		obj[SCRATCH].handle = opts->dependency;
-		r->presumed_offset = 0;
-		r->target_handle = obj[SCRATCH].handle;
-		r->offset = sizeof(uint32_t) * 1020;
-		r->delta = 0;
-		r->read_domains = I915_GEM_DOMAIN_RENDER;
-		r->write_domain = I915_GEM_DOMAIN_RENDER;
+	cs = batch_start;
 
+	poll->handle = gem_create(fd, POLL_SIZE);
+	spin->poll_handle = poll->handle;
 	execbuf->buffer_count++;
-	} else if (opts->flags & IGT_SPIN_POLL_RUN) {
-		r = &relocs[obj[BATCH].relocation_count++];
 
-		igt_assert(!opts->dependency);
+	if (opts->flags & IGT_SPIN_POLL_RUN) {
+		r = &relocs[batch->relocation_count++];
 
 		if (gen == 4 || gen == 5) {
 			execbuf->flags |= I915_EXEC_SECURE;
 			igt_require(__igt_device_set_master(fd) == 0);
 		}
 
-		spin->poll_handle = gem_create(fd, 4096);
-		obj[SCRATCH].handle = spin->poll_handle;
-
-		if (__gem_set_caching(fd, spin->poll_handle,
+		if (__gem_set_caching(fd, poll->handle,
 				      I915_CACHING_CACHED) == 0)
-			spin->poll = gem_mmap__cpu(fd, spin->poll_handle,
-						   0, 4096,
+			spin->poll = gem_mmap__cpu(fd, poll->handle,
+						   0, POLL_SIZE,
 						   PROT_READ | PROT_WRITE);
 		else
-			spin->poll = gem_mmap__wc(fd, spin->poll_handle,
-						  0, 4096,
+			spin->poll = gem_mmap__wc(fd, poll->handle,
+						  0, POLL_SIZE,
 						  PROT_READ | PROT_WRITE);
 
 		igt_assert_eq(spin->poll[SPIN_POLL_START_IDX], 0);
 
 		/* batch is first */
-		r->presumed_offset = 4096;
-		r->target_handle = obj[SCRATCH].handle;
+		r->presumed_offset = BATCH_SIZE;
+		r->target_handle = poll->handle;
 		r->offset = sizeof(uint32_t) * 1;
 		r->delta = sizeof(uint32_t) * SPIN_POLL_START_IDX;
 
@@ -179,14 +171,25 @@ emit_recursive_batch(igt_spin_t *spin,
 		}
 
 		*cs++ = 1;
+	}
+
+	if (opts->dependency) {
+		r = &relocs[batch->relocation_count++];
+
+		/* dummy write to dependency */
+		dep->handle = opts->dependency;
+		r->presumed_offset = BATCH_SIZE + POLL_SIZE;
+		r->target_handle = dep->handle;
+		r->offset = sizeof(uint32_t) * 1020;
+		r->delta = 0;
+		r->read_domains = I915_GEM_DOMAIN_RENDER;
+		r->write_domain = I915_GEM_DOMAIN_RENDER;
 
 		execbuf->buffer_count++;
 	}
 
-	spin->handle = obj[BATCH].handle;
-
-	igt_assert_lt(cs - batch, LOOP_START_OFFSET / sizeof(*cs));
-	spin->condition = batch + LOOP_START_OFFSET / sizeof(*cs);
+	igt_assert_lt(cs - batch_start, LOOP_START_OFFSET / sizeof(*cs));
+	spin->condition = batch_start + LOOP_START_OFFSET / sizeof(*cs);
 	cs = spin->condition;
 
 	/* Allow ourselves to be preempted */
@@ -208,9 +211,9 @@ emit_recursive_batch(igt_spin_t *spin,
 		cs += 1000;
 
 	/* recurse */
-	r = &relocs[obj[BATCH].relocation_count++];
-	r->target_handle = obj[BATCH].handle;
-	r->offset = (cs + 1 - batch) * sizeof(*cs);
+	r = &relocs[batch->relocation_count++];
+	r->target_handle = batch->handle;
+	r->offset = (cs + 1 - batch_start) * sizeof(*cs);
 	r->read_domains = I915_GEM_DOMAIN_COMMAND;
 	r->delta = LOOP_START_OFFSET;
 	if (gen >= 8) {
@@ -227,10 +230,10 @@ emit_recursive_batch(igt_spin_t *spin,
 		*cs = r->delta;
 		cs++;
 	}
-	obj[BATCH].relocs_ptr = to_user_pointer(relocs);
+	batch->relocs_ptr = to_user_pointer(relocs);
 
-	execbuf->buffers_ptr = to_user_pointer(obj +
-					       (2 - execbuf->buffer_count));
+	execbuf->buffers_ptr = to_user_pointer(spin->obj);
+	execbuf->flags |= I915_EXEC_BATCH_FIRST;
 	execbuf->rsvd1 = opts->ctx;
 
 	if (opts->flags & IGT_SPIN_FENCE_OUT)
@@ -264,10 +267,9 @@ emit_recursive_batch(igt_spin_t *spin,
 		}
 	}
 
-	igt_assert_lt(cs - batch, BATCH_SIZE / sizeof(*cs));
+	igt_assert_lt(cs - batch_start, BATCH_SIZE / sizeof(*cs));
 
-	/* Make it easier for callers to resubmit. */
-	for (i = 0; i < ARRAY_SIZE(spin->obj); i++) {
+	for (i = 0; i < execbuf->buffer_count; i++) {
 		spin->obj[i].relocation_count = 0;
 		spin->obj[i].relocs_ptr = 0;
 		spin->obj[i].flags = EXEC_OBJECT_PINNED;
@@ -445,10 +447,11 @@ void igt_spin_free(int fd, igt_spin_t *spin)
 	gem_munmap((void *)((unsigned long)spin->condition & (~4095UL)),
 		   BATCH_SIZE);
 
-	if (spin->poll) {
+	if (spin->poll)
 		gem_munmap(spin->poll, 4096);
+
+	if (spin->poll_handle)
 		gem_close(fd, spin->poll_handle);
-	}
 
 	if (spin->handle)
 		gem_close(fd, spin->handle);
diff --git a/lib/igt_dummyload.h b/lib/igt_dummyload.h
index 421ca183..de5781d7 100644
--- a/lib/igt_dummyload.h
+++ b/lib/igt_dummyload.h
@@ -34,6 +34,7 @@
 
 typedef struct igt_spin {
 	unsigned int handle;
+
 	timer_t timer;
 	struct igt_list_head link;
 
@@ -41,9 +42,12 @@ typedef struct igt_spin {
 	uint32_t cmd_precondition;
 
 	int out_fence;
-	struct drm_i915_gem_exec_object2 obj[2];
-#define IGT_SPIN_BATCH   1
+	struct drm_i915_gem_exec_object2 obj[3];
+#define IGT_SPIN_BATCH   0
+#define IGT_SPIN_POLL    1
+
 	struct drm_i915_gem_execbuffer2 execbuf;
+
 	uint32_t poll_handle;
 	uint32_t *poll;
 #define SPIN_POLL_START_IDX 0
diff --git a/tests/i915/gem_exec_balancer.c b/tests/i915/gem_exec_balancer.c
index 70c4529b..19df406c 100644
--- a/tests/i915/gem_exec_balancer.c
+++ b/tests/i915/gem_exec_balancer.c
@@ -718,7 +718,7 @@ static uint32_t create_semaphore_to_spinner(int i915, igt_spin_t *spin)
 	cs = map = gem_mmap__cpu(i915, handle, 0, 4096, PROT_WRITE);
 
 	/* Wait until the spinner is running */
-	addr = spin->obj[0].offset + 4 * SPIN_POLL_START_IDX;
+	addr = spin->obj[IGT_SPIN_POLL].offset + 4 * SPIN_POLL_START_IDX;
 	*cs++ = MI_SEMAPHORE_WAIT |
 		MI_SEMAPHORE_POLL |
 		MI_SEMAPHORE_SAD_NEQ_SDD |
@@ -797,9 +797,9 @@ static void bonded_slice(int i915)
 		igt_spin_reset(spin);
 
 		/* igt_spin_t poll and batch obj must be laid out as we expect */
-		igt_assert_eq(IGT_SPIN_BATCH, 1);
-		obj[0] = spin->obj[0];
-		obj[1] = spin->obj[1];
+		igt_assert_eq(IGT_SPIN_BATCH, 0);
+		obj[0] = spin->obj[IGT_SPIN_POLL];
+		obj[1] = spin->obj[IGT_SPIN_BATCH];
 		obj[2].handle = create_semaphore_to_spinner(i915, spin);
 
 		eb.buffers_ptr = to_user_pointer(obj);
diff --git a/tests/i915/gem_spin_batch.c b/tests/i915/gem_spin_batch.c
index c67f015f..04707fdc 100644
--- a/tests/i915/gem_spin_batch.c
+++ b/tests/i915/gem_spin_batch.c
@@ -78,12 +78,9 @@ static void spin_resubmit(int fd, const struct intel_execution_engine2 *e2,
 		gem_context_create(fd) : ctx0;
 	igt_spin_t *spin = __igt_spin_new(fd, .ctx = ctx0, .engine = e2->flags);
 	const struct intel_execution_engine2 *other;
+	struct drm_i915_gem_execbuffer2 eb = spin->execbuf;
 
-	struct drm_i915_gem_execbuffer2 eb = {
-		.buffer_count = 1,
-		.buffers_ptr = to_user_pointer(&spin->obj[IGT_SPIN_BATCH]),
-		.rsvd1 = ctx1,
-	};
+	eb.rsvd1 = ctx1;
 
 	igt_assert(gem_context_has_engine_map(fd, 0) ||
 		   !(flags & RESUBMIT_ALL_ENGINES));
@@ -97,11 +94,13 @@ static void spin_resubmit(int fd, const struct intel_execution_engine2 *e2,
 			if (gem_engine_is_equal(other, e2))
 				continue;
 
-			eb.flags = other->flags;
+			eb.flags &= ~(I915_EXEC_RING_MASK | I915_EXEC_BSD_MASK);
+			eb.flags |= other->flags;
 			gem_execbuf(fd, &eb);
 		}
 	} else {
-		eb.flags = e2->flags;
+		eb.flags &= ~(I915_EXEC_RING_MASK | I915_EXEC_BSD_MASK);
+		eb.flags |= e2->flags;
 		gem_execbuf(fd, &eb);
 	}
 
-- 
2.17.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH i-g-t 2/7] igt: Use COND_BBEND for busy spinning on gen9
@ 2019-11-13 15:49   ` Mika Kuoppala
  0 siblings, 0 replies; 44+ messages in thread
From: Mika Kuoppala @ 2019-11-13 15:49 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev, Joonas Lahtinen

From: Jon Bloomfield <jon.bloomfield@intel.com>

gen9+ introduces a cmdparser for the BLT engine which copies the
incoming BB to a kmd owned buffer for submission (to prevent changes
being made after the bb has been safely scanned). This breaks the
spin functionality because it relies on changing the submitted spin
buffers in order to terminate them.

Instead, for gen9+, we change the semantics by introducing a COND_BB_END
into the infinite loop, to wait until a memory flag (in anothe bo) is
cleared.

v2: Correct nop length to avoid overwriting bb_end instr when using
    a dependency bo (cork)
v3: fix conflicts on igt_dummyload (Mika)
v4: s/bool running/uint32_t running, fix r->delta (Mika)
v5: remove overzealous assert (Mika)
v6: rebase on top of lib changes (Mika)
v7: rework on top of public igt lib changes (Mika)
v8: rebase

Signed-off-by: Jon Bloomfield <jon.bloomfield@intel.com> (v2)
Cc: Joonas Lahtinen <joonas.lahtinen@intel.com>
Cc: Rodrigo Vivi <rodrigo.vivi@intel.com>
Cc: Mika Kuoppala <mika.kuoppala@intel.com>
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
---
 lib/i830_reg.h                          |  2 +-
 lib/igt_dummyload.c                     | 46 +++++++++++++++++++++++--
 lib/igt_dummyload.h                     |  3 ++
 lib/intel_reg.h                         |  3 ++
 tests/i915/gem_double_irq_loop.c        |  2 --
 tests/i915/gem_write_read_ring_switch.c |  2 +-
 6 files changed, 52 insertions(+), 6 deletions(-)

diff --git a/lib/i830_reg.h b/lib/i830_reg.h
index a57691c7..41020256 100644
--- a/lib/i830_reg.h
+++ b/lib/i830_reg.h
@@ -43,7 +43,7 @@
 /* broadwater flush bits */
 #define BRW_MI_GLOBAL_SNAPSHOT_RESET   (1 << 3)
 
-#define MI_COND_BATCH_BUFFER_END	(0x36<<23 | 1)
+#define MI_COND_BATCH_BUFFER_END	(0x36 << 23)
 #define MI_DO_COMPARE			(1<<21)
 
 #define MI_BATCH_BUFFER_END	(0xA << 23)
diff --git a/lib/igt_dummyload.c b/lib/igt_dummyload.c
index c079bd04..a88c8582 100644
--- a/lib/igt_dummyload.c
+++ b/lib/igt_dummyload.c
@@ -130,7 +130,15 @@ emit_recursive_batch(igt_spin_t *spin,
 	spin->poll_handle = poll->handle;
 	execbuf->buffer_count++;
 
-	if (opts->flags & IGT_SPIN_POLL_RUN) {
+	/*
+	 * For gen9+ we use a conditional loop rather than an
+	 * infinite loop, because we are unable to modify the
+	 * BB's once they have been scanned by the cmdparser
+	 * We share the poll buffer for the conditional test
+	 * and is always the first buffer in the batch list
+	 */
+
+	if (gen >= 9 || (opts->flags & IGT_SPIN_POLL_RUN)) {
 		r = &relocs[batch->relocation_count++];
 
 		if (gen == 4 || gen == 5) {
@@ -150,6 +158,13 @@ emit_recursive_batch(igt_spin_t *spin,
 
 		igt_assert_eq(spin->poll[SPIN_POLL_START_IDX], 0);
 
+		/*
+		 * 2nd word used to control conditional end, and is cleared
+		 * to terminate batch. Must be >=1 to spin
+		 */
+		spin->poll[SPIN_POLL_END_IDX] = 1;
+		__sync_synchronize();
+
 		/* batch is first */
 		r->presumed_offset = BATCH_SIZE;
 		r->target_handle = poll->handle;
@@ -208,7 +223,26 @@ emit_recursive_batch(igt_spin_t *spin,
 	 * trouble. See https://bugs.freedesktop.org/show_bug.cgi?id=102262
 	 */
 	if (!(opts->flags & IGT_SPIN_FAST))
-		cs += 1000;
+		cs += 960;
+
+	igt_assert_eq(spin->using_cond_end, 0);
+	if (gen >= 9) {
+		r = &relocs[batch->relocation_count++];
+
+		/* batch is first, so poll is second */
+		r->presumed_offset = BATCH_SIZE;
+		r->target_handle = poll->handle;
+		r->offset = (cs + 2 - batch_start) * sizeof(*cs);
+		r->read_domains = I915_GEM_DOMAIN_COMMAND;
+		r->delta = SPIN_POLL_END_IDX * sizeof(*cs);
+
+		*cs++ = MI_COND_BATCH_BUFFER_END | 1 << 21 | 2;
+		*cs++ = 0; /* Reference value */
+		*cs++ = r->presumed_offset + r->delta;
+		*cs++ = 0;
+
+		spin->using_cond_end = 1;
+	}
 
 	/* recurse */
 	r = &relocs[batch->relocation_count++];
@@ -401,6 +435,9 @@ void igt_spin_set_timeout(igt_spin_t *spin, int64_t ns)
  */
 void igt_spin_reset(igt_spin_t *spin)
 {
+	if (spin->using_cond_end)
+		spin->poll[SPIN_POLL_END_IDX] = 1;
+
 	if (igt_spin_has_poll(spin))
 		spin->poll[SPIN_POLL_START_IDX] = 0;
 
@@ -419,7 +456,12 @@ void igt_spin_end(igt_spin_t *spin)
 	if (!spin)
 		return;
 
+	if (spin->using_cond_end) {
+		spin->poll[SPIN_POLL_END_IDX] = 0;
+	} else {
 		*spin->condition = MI_BATCH_BUFFER_END;
+	}
+
 	__sync_synchronize();
 }
 
diff --git a/lib/igt_dummyload.h b/lib/igt_dummyload.h
index de5781d7..e9310b4e 100644
--- a/lib/igt_dummyload.h
+++ b/lib/igt_dummyload.h
@@ -51,6 +51,9 @@ typedef struct igt_spin {
 	uint32_t poll_handle;
 	uint32_t *poll;
 #define SPIN_POLL_START_IDX 0
+#define SPIN_POLL_END_IDX 1
+
+	bool using_cond_end;
 } igt_spin_t;
 
 struct igt_spin_factory {
diff --git a/lib/intel_reg.h b/lib/intel_reg.h
index 069440cb..10ca760a 100644
--- a/lib/intel_reg.h
+++ b/lib/intel_reg.h
@@ -2593,6 +2593,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 #define MI_BATCH_BUFFER		((0x30 << 23) | 1)
 #define MI_BATCH_BUFFER_START	(0x31 << 23)
 #define MI_BATCH_BUFFER_END	(0xA << 23)
+#define MI_COND_BATCH_BUFFER_END	(0x36 << 23)
+#define MI_DO_COMPARE                   (1<<21)
+
 #define MI_BATCH_NON_SECURE		(1)
 #define MI_BATCH_NON_SECURE_I965	(1 << 8)
 #define MI_BATCH_NON_SECURE_HSW		(1<<13) /* Additional bit for RCS */
diff --git a/tests/i915/gem_double_irq_loop.c b/tests/i915/gem_double_irq_loop.c
index b326fc58..f17f61c1 100644
--- a/tests/i915/gem_double_irq_loop.c
+++ b/tests/i915/gem_double_irq_loop.c
@@ -52,8 +52,6 @@ static drm_intel_bo *target_buffer, *blt_bo;
 IGT_TEST_DESCRIPTION("Basic check for missed IRQs on blt ring.");
 
 
-#define MI_COND_BATCH_BUFFER_END	(0x36<<23 | 1)
-#define MI_DO_COMPARE			(1<<21)
 static void
 dummy_reloc_loop(void)
 {
diff --git a/tests/i915/gem_write_read_ring_switch.c b/tests/i915/gem_write_read_ring_switch.c
index ef229cc5..095c13c3 100644
--- a/tests/i915/gem_write_read_ring_switch.c
+++ b/tests/i915/gem_write_read_ring_switch.c
@@ -115,7 +115,7 @@ static void run_test(int ring)
 	 * otherwise the obj->last_write_seqno will be updated. */
 	if (ring == I915_EXEC_RENDER) {
 		BEGIN_BATCH(4, 1);
-		OUT_BATCH(MI_COND_BATCH_BUFFER_END | MI_DO_COMPARE);
+		OUT_BATCH(MI_COND_BATCH_BUFFER_END | MI_DO_COMPARE | 1);
 		OUT_BATCH(0xffffffff); /* compare dword */
 		OUT_RELOC(target_bo, I915_GEM_DOMAIN_RENDER, 0, 0);
 		OUT_BATCH(MI_NOOP);
-- 
2.17.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [Intel-gfx] [PATCH i-g-t 2/7] igt: Use COND_BBEND for busy spinning on gen9
@ 2019-11-13 15:49   ` Mika Kuoppala
  0 siblings, 0 replies; 44+ messages in thread
From: Mika Kuoppala @ 2019-11-13 15:49 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev, Joonas Lahtinen

From: Jon Bloomfield <jon.bloomfield@intel.com>

gen9+ introduces a cmdparser for the BLT engine which copies the
incoming BB to a kmd owned buffer for submission (to prevent changes
being made after the bb has been safely scanned). This breaks the
spin functionality because it relies on changing the submitted spin
buffers in order to terminate them.

Instead, for gen9+, we change the semantics by introducing a COND_BB_END
into the infinite loop, to wait until a memory flag (in anothe bo) is
cleared.

v2: Correct nop length to avoid overwriting bb_end instr when using
    a dependency bo (cork)
v3: fix conflicts on igt_dummyload (Mika)
v4: s/bool running/uint32_t running, fix r->delta (Mika)
v5: remove overzealous assert (Mika)
v6: rebase on top of lib changes (Mika)
v7: rework on top of public igt lib changes (Mika)
v8: rebase

Signed-off-by: Jon Bloomfield <jon.bloomfield@intel.com> (v2)
Cc: Joonas Lahtinen <joonas.lahtinen@intel.com>
Cc: Rodrigo Vivi <rodrigo.vivi@intel.com>
Cc: Mika Kuoppala <mika.kuoppala@intel.com>
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
---
 lib/i830_reg.h                          |  2 +-
 lib/igt_dummyload.c                     | 46 +++++++++++++++++++++++--
 lib/igt_dummyload.h                     |  3 ++
 lib/intel_reg.h                         |  3 ++
 tests/i915/gem_double_irq_loop.c        |  2 --
 tests/i915/gem_write_read_ring_switch.c |  2 +-
 6 files changed, 52 insertions(+), 6 deletions(-)

diff --git a/lib/i830_reg.h b/lib/i830_reg.h
index a57691c7..41020256 100644
--- a/lib/i830_reg.h
+++ b/lib/i830_reg.h
@@ -43,7 +43,7 @@
 /* broadwater flush bits */
 #define BRW_MI_GLOBAL_SNAPSHOT_RESET   (1 << 3)
 
-#define MI_COND_BATCH_BUFFER_END	(0x36<<23 | 1)
+#define MI_COND_BATCH_BUFFER_END	(0x36 << 23)
 #define MI_DO_COMPARE			(1<<21)
 
 #define MI_BATCH_BUFFER_END	(0xA << 23)
diff --git a/lib/igt_dummyload.c b/lib/igt_dummyload.c
index c079bd04..a88c8582 100644
--- a/lib/igt_dummyload.c
+++ b/lib/igt_dummyload.c
@@ -130,7 +130,15 @@ emit_recursive_batch(igt_spin_t *spin,
 	spin->poll_handle = poll->handle;
 	execbuf->buffer_count++;
 
-	if (opts->flags & IGT_SPIN_POLL_RUN) {
+	/*
+	 * For gen9+ we use a conditional loop rather than an
+	 * infinite loop, because we are unable to modify the
+	 * BB's once they have been scanned by the cmdparser
+	 * We share the poll buffer for the conditional test
+	 * and is always the first buffer in the batch list
+	 */
+
+	if (gen >= 9 || (opts->flags & IGT_SPIN_POLL_RUN)) {
 		r = &relocs[batch->relocation_count++];
 
 		if (gen == 4 || gen == 5) {
@@ -150,6 +158,13 @@ emit_recursive_batch(igt_spin_t *spin,
 
 		igt_assert_eq(spin->poll[SPIN_POLL_START_IDX], 0);
 
+		/*
+		 * 2nd word used to control conditional end, and is cleared
+		 * to terminate batch. Must be >=1 to spin
+		 */
+		spin->poll[SPIN_POLL_END_IDX] = 1;
+		__sync_synchronize();
+
 		/* batch is first */
 		r->presumed_offset = BATCH_SIZE;
 		r->target_handle = poll->handle;
@@ -208,7 +223,26 @@ emit_recursive_batch(igt_spin_t *spin,
 	 * trouble. See https://bugs.freedesktop.org/show_bug.cgi?id=102262
 	 */
 	if (!(opts->flags & IGT_SPIN_FAST))
-		cs += 1000;
+		cs += 960;
+
+	igt_assert_eq(spin->using_cond_end, 0);
+	if (gen >= 9) {
+		r = &relocs[batch->relocation_count++];
+
+		/* batch is first, so poll is second */
+		r->presumed_offset = BATCH_SIZE;
+		r->target_handle = poll->handle;
+		r->offset = (cs + 2 - batch_start) * sizeof(*cs);
+		r->read_domains = I915_GEM_DOMAIN_COMMAND;
+		r->delta = SPIN_POLL_END_IDX * sizeof(*cs);
+
+		*cs++ = MI_COND_BATCH_BUFFER_END | 1 << 21 | 2;
+		*cs++ = 0; /* Reference value */
+		*cs++ = r->presumed_offset + r->delta;
+		*cs++ = 0;
+
+		spin->using_cond_end = 1;
+	}
 
 	/* recurse */
 	r = &relocs[batch->relocation_count++];
@@ -401,6 +435,9 @@ void igt_spin_set_timeout(igt_spin_t *spin, int64_t ns)
  */
 void igt_spin_reset(igt_spin_t *spin)
 {
+	if (spin->using_cond_end)
+		spin->poll[SPIN_POLL_END_IDX] = 1;
+
 	if (igt_spin_has_poll(spin))
 		spin->poll[SPIN_POLL_START_IDX] = 0;
 
@@ -419,7 +456,12 @@ void igt_spin_end(igt_spin_t *spin)
 	if (!spin)
 		return;
 
+	if (spin->using_cond_end) {
+		spin->poll[SPIN_POLL_END_IDX] = 0;
+	} else {
 		*spin->condition = MI_BATCH_BUFFER_END;
+	}
+
 	__sync_synchronize();
 }
 
diff --git a/lib/igt_dummyload.h b/lib/igt_dummyload.h
index de5781d7..e9310b4e 100644
--- a/lib/igt_dummyload.h
+++ b/lib/igt_dummyload.h
@@ -51,6 +51,9 @@ typedef struct igt_spin {
 	uint32_t poll_handle;
 	uint32_t *poll;
 #define SPIN_POLL_START_IDX 0
+#define SPIN_POLL_END_IDX 1
+
+	bool using_cond_end;
 } igt_spin_t;
 
 struct igt_spin_factory {
diff --git a/lib/intel_reg.h b/lib/intel_reg.h
index 069440cb..10ca760a 100644
--- a/lib/intel_reg.h
+++ b/lib/intel_reg.h
@@ -2593,6 +2593,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 #define MI_BATCH_BUFFER		((0x30 << 23) | 1)
 #define MI_BATCH_BUFFER_START	(0x31 << 23)
 #define MI_BATCH_BUFFER_END	(0xA << 23)
+#define MI_COND_BATCH_BUFFER_END	(0x36 << 23)
+#define MI_DO_COMPARE                   (1<<21)
+
 #define MI_BATCH_NON_SECURE		(1)
 #define MI_BATCH_NON_SECURE_I965	(1 << 8)
 #define MI_BATCH_NON_SECURE_HSW		(1<<13) /* Additional bit for RCS */
diff --git a/tests/i915/gem_double_irq_loop.c b/tests/i915/gem_double_irq_loop.c
index b326fc58..f17f61c1 100644
--- a/tests/i915/gem_double_irq_loop.c
+++ b/tests/i915/gem_double_irq_loop.c
@@ -52,8 +52,6 @@ static drm_intel_bo *target_buffer, *blt_bo;
 IGT_TEST_DESCRIPTION("Basic check for missed IRQs on blt ring.");
 
 
-#define MI_COND_BATCH_BUFFER_END	(0x36<<23 | 1)
-#define MI_DO_COMPARE			(1<<21)
 static void
 dummy_reloc_loop(void)
 {
diff --git a/tests/i915/gem_write_read_ring_switch.c b/tests/i915/gem_write_read_ring_switch.c
index ef229cc5..095c13c3 100644
--- a/tests/i915/gem_write_read_ring_switch.c
+++ b/tests/i915/gem_write_read_ring_switch.c
@@ -115,7 +115,7 @@ static void run_test(int ring)
 	 * otherwise the obj->last_write_seqno will be updated. */
 	if (ring == I915_EXEC_RENDER) {
 		BEGIN_BATCH(4, 1);
-		OUT_BATCH(MI_COND_BATCH_BUFFER_END | MI_DO_COMPARE);
+		OUT_BATCH(MI_COND_BATCH_BUFFER_END | MI_DO_COMPARE | 1);
 		OUT_BATCH(0xffffffff); /* compare dword */
 		OUT_RELOC(target_bo, I915_GEM_DOMAIN_RENDER, 0, 0);
 		OUT_BATCH(MI_NOOP);
-- 
2.17.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [igt-dev] [PATCH i-g-t 2/7] igt: Use COND_BBEND for busy spinning on gen9
@ 2019-11-13 15:49   ` Mika Kuoppala
  0 siblings, 0 replies; 44+ messages in thread
From: Mika Kuoppala @ 2019-11-13 15:49 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev, Joonas Lahtinen, Jon Bloomfield, Rodrigo Vivi

From: Jon Bloomfield <jon.bloomfield@intel.com>

gen9+ introduces a cmdparser for the BLT engine which copies the
incoming BB to a kmd owned buffer for submission (to prevent changes
being made after the bb has been safely scanned). This breaks the
spin functionality because it relies on changing the submitted spin
buffers in order to terminate them.

Instead, for gen9+, we change the semantics by introducing a COND_BB_END
into the infinite loop, to wait until a memory flag (in anothe bo) is
cleared.

v2: Correct nop length to avoid overwriting bb_end instr when using
    a dependency bo (cork)
v3: fix conflicts on igt_dummyload (Mika)
v4: s/bool running/uint32_t running, fix r->delta (Mika)
v5: remove overzealous assert (Mika)
v6: rebase on top of lib changes (Mika)
v7: rework on top of public igt lib changes (Mika)
v8: rebase

Signed-off-by: Jon Bloomfield <jon.bloomfield@intel.com> (v2)
Cc: Joonas Lahtinen <joonas.lahtinen@intel.com>
Cc: Rodrigo Vivi <rodrigo.vivi@intel.com>
Cc: Mika Kuoppala <mika.kuoppala@intel.com>
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
---
 lib/i830_reg.h                          |  2 +-
 lib/igt_dummyload.c                     | 46 +++++++++++++++++++++++--
 lib/igt_dummyload.h                     |  3 ++
 lib/intel_reg.h                         |  3 ++
 tests/i915/gem_double_irq_loop.c        |  2 --
 tests/i915/gem_write_read_ring_switch.c |  2 +-
 6 files changed, 52 insertions(+), 6 deletions(-)

diff --git a/lib/i830_reg.h b/lib/i830_reg.h
index a57691c7..41020256 100644
--- a/lib/i830_reg.h
+++ b/lib/i830_reg.h
@@ -43,7 +43,7 @@
 /* broadwater flush bits */
 #define BRW_MI_GLOBAL_SNAPSHOT_RESET   (1 << 3)
 
-#define MI_COND_BATCH_BUFFER_END	(0x36<<23 | 1)
+#define MI_COND_BATCH_BUFFER_END	(0x36 << 23)
 #define MI_DO_COMPARE			(1<<21)
 
 #define MI_BATCH_BUFFER_END	(0xA << 23)
diff --git a/lib/igt_dummyload.c b/lib/igt_dummyload.c
index c079bd04..a88c8582 100644
--- a/lib/igt_dummyload.c
+++ b/lib/igt_dummyload.c
@@ -130,7 +130,15 @@ emit_recursive_batch(igt_spin_t *spin,
 	spin->poll_handle = poll->handle;
 	execbuf->buffer_count++;
 
-	if (opts->flags & IGT_SPIN_POLL_RUN) {
+	/*
+	 * For gen9+ we use a conditional loop rather than an
+	 * infinite loop, because we are unable to modify the
+	 * BB's once they have been scanned by the cmdparser
+	 * We share the poll buffer for the conditional test
+	 * and is always the first buffer in the batch list
+	 */
+
+	if (gen >= 9 || (opts->flags & IGT_SPIN_POLL_RUN)) {
 		r = &relocs[batch->relocation_count++];
 
 		if (gen == 4 || gen == 5) {
@@ -150,6 +158,13 @@ emit_recursive_batch(igt_spin_t *spin,
 
 		igt_assert_eq(spin->poll[SPIN_POLL_START_IDX], 0);
 
+		/*
+		 * 2nd word used to control conditional end, and is cleared
+		 * to terminate batch. Must be >=1 to spin
+		 */
+		spin->poll[SPIN_POLL_END_IDX] = 1;
+		__sync_synchronize();
+
 		/* batch is first */
 		r->presumed_offset = BATCH_SIZE;
 		r->target_handle = poll->handle;
@@ -208,7 +223,26 @@ emit_recursive_batch(igt_spin_t *spin,
 	 * trouble. See https://bugs.freedesktop.org/show_bug.cgi?id=102262
 	 */
 	if (!(opts->flags & IGT_SPIN_FAST))
-		cs += 1000;
+		cs += 960;
+
+	igt_assert_eq(spin->using_cond_end, 0);
+	if (gen >= 9) {
+		r = &relocs[batch->relocation_count++];
+
+		/* batch is first, so poll is second */
+		r->presumed_offset = BATCH_SIZE;
+		r->target_handle = poll->handle;
+		r->offset = (cs + 2 - batch_start) * sizeof(*cs);
+		r->read_domains = I915_GEM_DOMAIN_COMMAND;
+		r->delta = SPIN_POLL_END_IDX * sizeof(*cs);
+
+		*cs++ = MI_COND_BATCH_BUFFER_END | 1 << 21 | 2;
+		*cs++ = 0; /* Reference value */
+		*cs++ = r->presumed_offset + r->delta;
+		*cs++ = 0;
+
+		spin->using_cond_end = 1;
+	}
 
 	/* recurse */
 	r = &relocs[batch->relocation_count++];
@@ -401,6 +435,9 @@ void igt_spin_set_timeout(igt_spin_t *spin, int64_t ns)
  */
 void igt_spin_reset(igt_spin_t *spin)
 {
+	if (spin->using_cond_end)
+		spin->poll[SPIN_POLL_END_IDX] = 1;
+
 	if (igt_spin_has_poll(spin))
 		spin->poll[SPIN_POLL_START_IDX] = 0;
 
@@ -419,7 +456,12 @@ void igt_spin_end(igt_spin_t *spin)
 	if (!spin)
 		return;
 
+	if (spin->using_cond_end) {
+		spin->poll[SPIN_POLL_END_IDX] = 0;
+	} else {
 		*spin->condition = MI_BATCH_BUFFER_END;
+	}
+
 	__sync_synchronize();
 }
 
diff --git a/lib/igt_dummyload.h b/lib/igt_dummyload.h
index de5781d7..e9310b4e 100644
--- a/lib/igt_dummyload.h
+++ b/lib/igt_dummyload.h
@@ -51,6 +51,9 @@ typedef struct igt_spin {
 	uint32_t poll_handle;
 	uint32_t *poll;
 #define SPIN_POLL_START_IDX 0
+#define SPIN_POLL_END_IDX 1
+
+	bool using_cond_end;
 } igt_spin_t;
 
 struct igt_spin_factory {
diff --git a/lib/intel_reg.h b/lib/intel_reg.h
index 069440cb..10ca760a 100644
--- a/lib/intel_reg.h
+++ b/lib/intel_reg.h
@@ -2593,6 +2593,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 #define MI_BATCH_BUFFER		((0x30 << 23) | 1)
 #define MI_BATCH_BUFFER_START	(0x31 << 23)
 #define MI_BATCH_BUFFER_END	(0xA << 23)
+#define MI_COND_BATCH_BUFFER_END	(0x36 << 23)
+#define MI_DO_COMPARE                   (1<<21)
+
 #define MI_BATCH_NON_SECURE		(1)
 #define MI_BATCH_NON_SECURE_I965	(1 << 8)
 #define MI_BATCH_NON_SECURE_HSW		(1<<13) /* Additional bit for RCS */
diff --git a/tests/i915/gem_double_irq_loop.c b/tests/i915/gem_double_irq_loop.c
index b326fc58..f17f61c1 100644
--- a/tests/i915/gem_double_irq_loop.c
+++ b/tests/i915/gem_double_irq_loop.c
@@ -52,8 +52,6 @@ static drm_intel_bo *target_buffer, *blt_bo;
 IGT_TEST_DESCRIPTION("Basic check for missed IRQs on blt ring.");
 
 
-#define MI_COND_BATCH_BUFFER_END	(0x36<<23 | 1)
-#define MI_DO_COMPARE			(1<<21)
 static void
 dummy_reloc_loop(void)
 {
diff --git a/tests/i915/gem_write_read_ring_switch.c b/tests/i915/gem_write_read_ring_switch.c
index ef229cc5..095c13c3 100644
--- a/tests/i915/gem_write_read_ring_switch.c
+++ b/tests/i915/gem_write_read_ring_switch.c
@@ -115,7 +115,7 @@ static void run_test(int ring)
 	 * otherwise the obj->last_write_seqno will be updated. */
 	if (ring == I915_EXEC_RENDER) {
 		BEGIN_BATCH(4, 1);
-		OUT_BATCH(MI_COND_BATCH_BUFFER_END | MI_DO_COMPARE);
+		OUT_BATCH(MI_COND_BATCH_BUFFER_END | MI_DO_COMPARE | 1);
 		OUT_BATCH(0xffffffff); /* compare dword */
 		OUT_RELOC(target_bo, I915_GEM_DOMAIN_RENDER, 0, 0);
 		OUT_BATCH(MI_NOOP);
-- 
2.17.1

_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH i-g-t 3/7] lib/i915: Add query to detect if engine accepts only ro batches
@ 2019-11-13 15:49   ` Mika Kuoppala
  0 siblings, 0 replies; 44+ messages in thread
From: Mika Kuoppala @ 2019-11-13 15:49 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev, Kuoppala

From: "Kuoppala, Mika" <mika.kuoppala@intel.com>

If cmd parser is mandatory, batch can't be modified post execbuf.
Some tests rely on modifying batch post execbuf. Give those
tests a method to query if those modifications ever reach
the actual engine command stream.

v2: pull in the test changes, doh
v3: class based query
v4: rebase

Signed-off-by: Kuoppala, Mika <mika.kuoppala@intel.com>
---
 lib/i915/gem_submission.c      | 62 ++++++++++++++++++++++++++++++++++
 lib/i915/gem_submission.h      |  2 ++
 tests/i915/gem_busy.c          |  7 +++-
 tests/i915/gem_exec_async.c    |  3 ++
 tests/i915/gem_exec_await.c    |  7 +++-
 tests/i915/gem_exec_fence.c    |  8 +++++
 tests/i915/gem_exec_latency.c  |  7 ++++
 tests/i915/gem_exec_nop.c      |  4 ++-
 tests/i915/gem_exec_schedule.c |  6 +++-
 tests/i915/gem_exec_whisper.c  |  4 ++-
 tests/prime_busy.c             |  3 ++
 tests/prime_vgem.c             |  6 ++++
 12 files changed, 114 insertions(+), 5 deletions(-)

diff --git a/lib/i915/gem_submission.c b/lib/i915/gem_submission.c
index 4f946493..9bdf28bc 100644
--- a/lib/i915/gem_submission.c
+++ b/lib/i915/gem_submission.c
@@ -64,6 +64,22 @@ static bool has_semaphores(int fd, int dir)
 	return val;
 }
 
+static int cmd_parser_version(int fd)
+{
+	int val = 0;
+	struct drm_i915_getparam gp = {
+		gp.param = I915_PARAM_CMD_PARSER_VERSION,
+		gp.value = &val,
+	};
+
+	if (ioctl(fd, DRM_IOCTL_I915_GETPARAM, &gp) < 0) {
+		val = -errno;
+		igt_assert(val < 0);
+	}
+
+	return val;
+}
+
 /**
  * gem_submission_method:
  * @fd: open i915 drm file descriptor
@@ -253,3 +269,49 @@ void gem_require_blitter(int i915)
 {
 	igt_require(gem_has_blitter(i915));
 }
+
+static bool gem_engine_has_immutable_submission(int i915, int class)
+{
+	const int gen = intel_gen(intel_get_drm_devid(i915));
+        int parser_version;
+
+	parser_version = cmd_parser_version(i915);
+
+	if (parser_version < 0)
+		return false;
+
+	if (gen == 9 && class == I915_ENGINE_CLASS_COPY && parser_version > 9)
+		return true;
+
+	return false;
+}
+
+
+/**
+ * gem_class_has_mutable_submission:
+ * @i915: open i915 drm file descriptor
+ * @class: engine class
+ *
+ * Returns boolean value if the engine class allows batch modifications
+ * post execbuf.
+ */
+
+bool
+gem_class_has_mutable_submission(int fd, int class)
+{
+	return !gem_engine_has_immutable_submission(fd, class);
+}
+
+/**
+ * gem_engine_has_mutable_submission:
+ * @i915: open i915 drm file descriptor
+ * @engine: the engine (I915_EXEC_RING id) of target
+ *
+ * Returns boolean value if the engine allows batch modifications
+ * post execbuf.
+ */
+bool gem_engine_has_mutable_submission(int i915, unsigned int engine)
+{
+	return gem_class_has_mutable_submission(i915,
+						gem_execbuf_flags_to_engine_class(engine));
+}
diff --git a/lib/i915/gem_submission.h b/lib/i915/gem_submission.h
index 6deb7e2d..acd24bcb 100644
--- a/lib/i915/gem_submission.h
+++ b/lib/i915/gem_submission.h
@@ -34,6 +34,8 @@ void gem_submission_print_method(int fd);
 bool gem_has_semaphores(int fd);
 bool gem_has_execlists(int fd);
 bool gem_has_guc_submission(int fd);
+bool gem_engine_has_mutable_submission(int fd, unsigned int engine);
+bool gem_class_has_mutable_submission(int fd, int class);
 
 int gem_cmdparser_version(int i915, uint32_t engine);
 static inline bool gem_has_cmdparser(int i915, uint32_t engine)
diff --git a/tests/i915/gem_busy.c b/tests/i915/gem_busy.c
index a8388149..b84e2bd1 100644
--- a/tests/i915/gem_busy.c
+++ b/tests/i915/gem_busy.c
@@ -243,6 +243,10 @@ static void one(int fd, const struct intel_execution_engine2 *e, unsigned test_f
 	i++;
 
 	igt_assert(i < size/sizeof(*batch));
+
+	if ((test_flags & HANG) == 0)
+		igt_require(gem_class_has_mutable_submission(fd, e->class));
+
 	igt_require(__gem_execbuf(fd, &execbuf) == 0);
 
 	__gem_busy(fd, obj[SCRATCH].handle, &read[SCRATCH], &write[SCRATCH]);
@@ -256,7 +260,8 @@ static void one(int fd, const struct intel_execution_engine2 *e, unsigned test_f
 			    e2->instance == e->instance)
 				continue;
 
-			if (!gem_class_can_store_dword(fd, e2->class))
+			if (!gem_class_can_store_dword(fd, e2->class) ||
+			    !gem_class_has_mutable_submission(fd, e2->class))
 				continue;
 
 			igt_debug("Testing %s in parallel\n", e2->name);
diff --git a/tests/i915/gem_exec_async.c b/tests/i915/gem_exec_async.c
index d83e9f6d..b9859ffa 100644
--- a/tests/i915/gem_exec_async.c
+++ b/tests/i915/gem_exec_async.c
@@ -138,6 +138,9 @@ static void one(int fd, unsigned ring, uint32_t flags)
 	execbuf.buffers_ptr = to_user_pointer(obj);
 	execbuf.buffer_count = 2;
 	execbuf.flags = ring | flags;
+
+	igt_require(gem_engine_has_mutable_submission(fd, ring));
+
 	igt_require(__gem_execbuf(fd, &execbuf) == 0);
 	gem_close(fd, obj[BATCH].handle);
 
diff --git a/tests/i915/gem_exec_await.c b/tests/i915/gem_exec_await.c
index 33e2ee45..7817b483 100644
--- a/tests/i915/gem_exec_await.c
+++ b/tests/i915/gem_exec_await.c
@@ -78,8 +78,13 @@ static void wide(int fd, int ring_size, int timeout, unsigned int flags)
 	double time;
 
 	nengine = 0;
-	for_each_physical_engine(e, fd)
+	for_each_physical_engine(e, fd) {
+		if (!gem_engine_has_mutable_submission(fd, eb_ring(e)))
+			continue;
+
 		engines[nengine++] = eb_ring(e);
+	}
+
 	igt_require(nengine);
 
 	exec = calloc(nengine, sizeof(*exec));
diff --git a/tests/i915/gem_exec_fence.c b/tests/i915/gem_exec_fence.c
index 002120bf..fbb11ab2 100644
--- a/tests/i915/gem_exec_fence.c
+++ b/tests/i915/gem_exec_fence.c
@@ -129,6 +129,9 @@ static void test_fence_busy(int fd, unsigned ring, unsigned flags)
 	uint32_t *batch;
 	int fence, i, timeout;
 
+	if ((flags & HANG) == 0)
+		igt_require(gem_engine_has_mutable_submission(fd, ring));
+
 	gem_quiescent_gpu(fd);
 
 	memset(&execbuf, 0, sizeof(execbuf));
@@ -265,6 +268,8 @@ static void test_fence_busy_all(int fd, unsigned flags)
 	for_each_engine(e, fd) {
 		int fence, new;
 
+		if ((flags & HANG) == 0)
+			igt_require(gem_engine_has_mutable_submission(fd, eb_ring(e)));
 		execbuf.flags = eb_ring(e) | LOCAL_EXEC_FENCE_OUT;
 		execbuf.rsvd2 = -1;
 		gem_execbuf_wr(fd, &execbuf);
@@ -321,6 +326,9 @@ static void test_fence_await(int fd, unsigned ring, unsigned flags)
 	uint32_t *out;
 	int i;
 
+	if ((flags & HANG) == 0)
+		igt_require(gem_engine_has_mutable_submission(fd, ring));
+
 	igt_require(gem_can_store_dword(fd, 0));
 
 	out = gem_mmap__wc(fd, scratch, 0, 4096, PROT_WRITE);
diff --git a/tests/i915/gem_exec_latency.c b/tests/i915/gem_exec_latency.c
index 3d99182a..e4c0c862 100644
--- a/tests/i915/gem_exec_latency.c
+++ b/tests/i915/gem_exec_latency.c
@@ -128,6 +128,8 @@ static void latency_on_ring(int fd,
 	double gpu_latency;
 	int i, j;
 
+	igt_require(gem_engine_has_mutable_submission(fd, ring));
+
 	reg = (volatile uint32_t *)((volatile char *)igt_global_mmio + RCS_TIMESTAMP);
 
 	memset(&execbuf, 0, sizeof(execbuf));
@@ -271,6 +273,8 @@ static void latency_from_ring(int fd,
 	uint32_t ctx[2] = {};
 	int i, j;
 
+	igt_require(gem_engine_has_mutable_submission(fd, ring));
+
 	if (flags & PREEMPT) {
 		ctx[0] = gem_context_create(fd);
 		gem_context_set_priority(fd, ctx[0], -1023);
@@ -316,6 +320,9 @@ static void latency_from_ring(int fd,
 		igt_spin_t *spin = NULL;
 		IGT_CORK_HANDLE(c);
 
+		if (!gem_engine_has_mutable_submission(fd, eb_ring(e)))
+			continue;
+
 		gem_set_domain(fd, obj[2].handle,
 			       I915_GEM_DOMAIN_GTT,
 			       I915_GEM_DOMAIN_GTT);
diff --git a/tests/i915/gem_exec_nop.c b/tests/i915/gem_exec_nop.c
index 2ad8aeea..dbedb356 100644
--- a/tests/i915/gem_exec_nop.c
+++ b/tests/i915/gem_exec_nop.c
@@ -123,6 +123,7 @@ static void poll_ring(int fd, unsigned engine, const char *name, int timeout)
 
 	gem_require_ring(fd, engine);
 	igt_require(gem_can_store_dword(fd, engine));
+	igt_require(gem_engine_has_mutable_submission(fd, engine));
 
 	memset(&obj, 0, sizeof(obj));
 	obj.handle = gem_create(fd, 4096);
@@ -234,7 +235,8 @@ static void poll_sequential(int fd, const char *name, int timeout)
 
 	nengine = 0;
 	for_each_physical_engine(e, fd) {
-		if (!gem_can_store_dword(fd, eb_ring(e)))
+		if (!gem_can_store_dword(fd, eb_ring(e)) ||
+		    !gem_engine_has_mutable_submission(fd, eb_ring(e)))
 			continue;
 
 		engines[nengine++] = eb_ring(e);
diff --git a/tests/i915/gem_exec_schedule.c b/tests/i915/gem_exec_schedule.c
index 5c15f177..ca6bef6a 100644
--- a/tests/i915/gem_exec_schedule.c
+++ b/tests/i915/gem_exec_schedule.c
@@ -652,7 +652,8 @@ static void semaphore_noskip(int i915)
 		igt_spin_t *chain, *spin;
 
 		if (eb_ring(inner) == eb_ring(outer) ||
-		    !gem_can_store_dword(i915, eb_ring(inner)))
+		    !gem_can_store_dword(i915, eb_ring(inner)) ||
+		    !gem_engine_has_mutable_submission(i915, eb_ring(inner)))
 			continue;
 
 		chain = __igt_spin_new(i915, .engine = eb_ring(outer));
@@ -1157,6 +1158,9 @@ static void deep(int fd, unsigned ring)
 	int dep_nreq;
 	int n;
 
+	igt_require(gem_can_store_dword(fd, ring));
+	igt_require(gem_engine_has_mutable_submission(fd, ring));
+
 	ctx = malloc(sizeof(*ctx) * MAX_CONTEXTS);
 	for (n = 0; n < MAX_CONTEXTS; n++) {
 		ctx[n] = gem_context_create(fd);
diff --git a/tests/i915/gem_exec_whisper.c b/tests/i915/gem_exec_whisper.c
index a3384760..45cc1437 100644
--- a/tests/i915/gem_exec_whisper.c
+++ b/tests/i915/gem_exec_whisper.c
@@ -204,13 +204,15 @@ static void whisper(int fd, unsigned engine, unsigned flags)
 	nengine = 0;
 	if (engine == ALL_ENGINES) {
 		for_each_physical_engine(e, fd) {
-			if (gem_can_store_dword(fd, eb_ring(e)))
+			if (gem_can_store_dword(fd, eb_ring(e)) &&
+			    gem_engine_has_mutable_submission(fd, eb_ring(e)))
 				engines[nengine++] = eb_ring(e);
 		}
 	} else {
 		igt_assert(!(flags & ALL));
 		igt_require(gem_has_ring(fd, engine));
 		igt_require(gem_can_store_dword(fd, engine));
+		igt_require(gem_engine_has_mutable_submission(fd, engine));
 		engines[nengine++] = engine;
 	}
 	igt_require(nengine);
diff --git a/tests/prime_busy.c b/tests/prime_busy.c
index 8ee23bb3..bb29216e 100644
--- a/tests/prime_busy.c
+++ b/tests/prime_busy.c
@@ -53,6 +53,9 @@ static void busy(int fd, unsigned ring, unsigned flags)
 	uint32_t *batch, *bbe;
 	int i, count, timeout;
 
+	if ((flags & HANG) == 0)
+		igt_require(gem_engine_has_mutable_submission(fd, ring));
+
 	gem_quiescent_gpu(fd);
 
 	memset(&execbuf, 0, sizeof(execbuf));
diff --git a/tests/prime_vgem.c b/tests/prime_vgem.c
index 2b21ff41..04cc913d 100644
--- a/tests/prime_vgem.c
+++ b/tests/prime_vgem.c
@@ -342,6 +342,8 @@ static void work(int i915, int dmabuf, unsigned ring, uint32_t flags)
 	uint32_t *batch, *bbe;
 	int i, count;
 
+	igt_require(gem_engine_has_mutable_submission(i915, ring));
+
 	memset(&execbuf, 0, sizeof(execbuf));
 	execbuf.buffers_ptr = (uintptr_t)obj;
 	execbuf.buffer_count = 2;
@@ -850,6 +852,7 @@ igt_main
 			      e->name) {
 			gem_require_ring(i915, eb_ring(e));
 			igt_require(gem_can_store_dword(i915, eb_ring(e)));
+			igt_require(gem_engine_has_mutable_submission(i915, eb_ring(e)));
 
 			gem_quiescent_gpu(i915);
 			test_sync(i915, vgem, e->exec_id, e->flags);
@@ -862,6 +865,7 @@ igt_main
 			      e->name) {
 			gem_require_ring(i915, eb_ring(e));
 			igt_require(gem_can_store_dword(i915, eb_ring(e)));
+			igt_require(gem_engine_has_mutable_submission(i915, eb_ring(e)));
 
 			gem_quiescent_gpu(i915);
 			test_busy(i915, vgem, e->exec_id, e->flags);
@@ -874,6 +878,7 @@ igt_main
 			      e->name) {
 			gem_require_ring(i915, eb_ring(e));
 			igt_require(gem_can_store_dword(i915, eb_ring(e)));
+			igt_require(gem_engine_has_mutable_submission(i915, eb_ring(e)));
 
 			gem_quiescent_gpu(i915);
 			test_wait(i915, vgem, e->exec_id, e->flags);
@@ -897,6 +902,7 @@ igt_main
 					e->name) {
 				gem_require_ring(i915, eb_ring(e));
 				igt_require(gem_can_store_dword(i915, eb_ring(e)));
+				igt_require(gem_engine_has_mutable_submission(i915, eb_ring(e)));
 
 				gem_quiescent_gpu(i915);
 				test_fence_wait(i915, vgem, e->exec_id, e->flags);
-- 
2.17.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [Intel-gfx] [PATCH i-g-t 3/7] lib/i915: Add query to detect if engine accepts only ro batches
@ 2019-11-13 15:49   ` Mika Kuoppala
  0 siblings, 0 replies; 44+ messages in thread
From: Mika Kuoppala @ 2019-11-13 15:49 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev, Kuoppala

From: "Kuoppala, Mika" <mika.kuoppala@intel.com>

If cmd parser is mandatory, batch can't be modified post execbuf.
Some tests rely on modifying batch post execbuf. Give those
tests a method to query if those modifications ever reach
the actual engine command stream.

v2: pull in the test changes, doh
v3: class based query
v4: rebase

Signed-off-by: Kuoppala, Mika <mika.kuoppala@intel.com>
---
 lib/i915/gem_submission.c      | 62 ++++++++++++++++++++++++++++++++++
 lib/i915/gem_submission.h      |  2 ++
 tests/i915/gem_busy.c          |  7 +++-
 tests/i915/gem_exec_async.c    |  3 ++
 tests/i915/gem_exec_await.c    |  7 +++-
 tests/i915/gem_exec_fence.c    |  8 +++++
 tests/i915/gem_exec_latency.c  |  7 ++++
 tests/i915/gem_exec_nop.c      |  4 ++-
 tests/i915/gem_exec_schedule.c |  6 +++-
 tests/i915/gem_exec_whisper.c  |  4 ++-
 tests/prime_busy.c             |  3 ++
 tests/prime_vgem.c             |  6 ++++
 12 files changed, 114 insertions(+), 5 deletions(-)

diff --git a/lib/i915/gem_submission.c b/lib/i915/gem_submission.c
index 4f946493..9bdf28bc 100644
--- a/lib/i915/gem_submission.c
+++ b/lib/i915/gem_submission.c
@@ -64,6 +64,22 @@ static bool has_semaphores(int fd, int dir)
 	return val;
 }
 
+static int cmd_parser_version(int fd)
+{
+	int val = 0;
+	struct drm_i915_getparam gp = {
+		gp.param = I915_PARAM_CMD_PARSER_VERSION,
+		gp.value = &val,
+	};
+
+	if (ioctl(fd, DRM_IOCTL_I915_GETPARAM, &gp) < 0) {
+		val = -errno;
+		igt_assert(val < 0);
+	}
+
+	return val;
+}
+
 /**
  * gem_submission_method:
  * @fd: open i915 drm file descriptor
@@ -253,3 +269,49 @@ void gem_require_blitter(int i915)
 {
 	igt_require(gem_has_blitter(i915));
 }
+
+static bool gem_engine_has_immutable_submission(int i915, int class)
+{
+	const int gen = intel_gen(intel_get_drm_devid(i915));
+        int parser_version;
+
+	parser_version = cmd_parser_version(i915);
+
+	if (parser_version < 0)
+		return false;
+
+	if (gen == 9 && class == I915_ENGINE_CLASS_COPY && parser_version > 9)
+		return true;
+
+	return false;
+}
+
+
+/**
+ * gem_class_has_mutable_submission:
+ * @i915: open i915 drm file descriptor
+ * @class: engine class
+ *
+ * Returns boolean value if the engine class allows batch modifications
+ * post execbuf.
+ */
+
+bool
+gem_class_has_mutable_submission(int fd, int class)
+{
+	return !gem_engine_has_immutable_submission(fd, class);
+}
+
+/**
+ * gem_engine_has_mutable_submission:
+ * @i915: open i915 drm file descriptor
+ * @engine: the engine (I915_EXEC_RING id) of target
+ *
+ * Returns boolean value if the engine allows batch modifications
+ * post execbuf.
+ */
+bool gem_engine_has_mutable_submission(int i915, unsigned int engine)
+{
+	return gem_class_has_mutable_submission(i915,
+						gem_execbuf_flags_to_engine_class(engine));
+}
diff --git a/lib/i915/gem_submission.h b/lib/i915/gem_submission.h
index 6deb7e2d..acd24bcb 100644
--- a/lib/i915/gem_submission.h
+++ b/lib/i915/gem_submission.h
@@ -34,6 +34,8 @@ void gem_submission_print_method(int fd);
 bool gem_has_semaphores(int fd);
 bool gem_has_execlists(int fd);
 bool gem_has_guc_submission(int fd);
+bool gem_engine_has_mutable_submission(int fd, unsigned int engine);
+bool gem_class_has_mutable_submission(int fd, int class);
 
 int gem_cmdparser_version(int i915, uint32_t engine);
 static inline bool gem_has_cmdparser(int i915, uint32_t engine)
diff --git a/tests/i915/gem_busy.c b/tests/i915/gem_busy.c
index a8388149..b84e2bd1 100644
--- a/tests/i915/gem_busy.c
+++ b/tests/i915/gem_busy.c
@@ -243,6 +243,10 @@ static void one(int fd, const struct intel_execution_engine2 *e, unsigned test_f
 	i++;
 
 	igt_assert(i < size/sizeof(*batch));
+
+	if ((test_flags & HANG) == 0)
+		igt_require(gem_class_has_mutable_submission(fd, e->class));
+
 	igt_require(__gem_execbuf(fd, &execbuf) == 0);
 
 	__gem_busy(fd, obj[SCRATCH].handle, &read[SCRATCH], &write[SCRATCH]);
@@ -256,7 +260,8 @@ static void one(int fd, const struct intel_execution_engine2 *e, unsigned test_f
 			    e2->instance == e->instance)
 				continue;
 
-			if (!gem_class_can_store_dword(fd, e2->class))
+			if (!gem_class_can_store_dword(fd, e2->class) ||
+			    !gem_class_has_mutable_submission(fd, e2->class))
 				continue;
 
 			igt_debug("Testing %s in parallel\n", e2->name);
diff --git a/tests/i915/gem_exec_async.c b/tests/i915/gem_exec_async.c
index d83e9f6d..b9859ffa 100644
--- a/tests/i915/gem_exec_async.c
+++ b/tests/i915/gem_exec_async.c
@@ -138,6 +138,9 @@ static void one(int fd, unsigned ring, uint32_t flags)
 	execbuf.buffers_ptr = to_user_pointer(obj);
 	execbuf.buffer_count = 2;
 	execbuf.flags = ring | flags;
+
+	igt_require(gem_engine_has_mutable_submission(fd, ring));
+
 	igt_require(__gem_execbuf(fd, &execbuf) == 0);
 	gem_close(fd, obj[BATCH].handle);
 
diff --git a/tests/i915/gem_exec_await.c b/tests/i915/gem_exec_await.c
index 33e2ee45..7817b483 100644
--- a/tests/i915/gem_exec_await.c
+++ b/tests/i915/gem_exec_await.c
@@ -78,8 +78,13 @@ static void wide(int fd, int ring_size, int timeout, unsigned int flags)
 	double time;
 
 	nengine = 0;
-	for_each_physical_engine(e, fd)
+	for_each_physical_engine(e, fd) {
+		if (!gem_engine_has_mutable_submission(fd, eb_ring(e)))
+			continue;
+
 		engines[nengine++] = eb_ring(e);
+	}
+
 	igt_require(nengine);
 
 	exec = calloc(nengine, sizeof(*exec));
diff --git a/tests/i915/gem_exec_fence.c b/tests/i915/gem_exec_fence.c
index 002120bf..fbb11ab2 100644
--- a/tests/i915/gem_exec_fence.c
+++ b/tests/i915/gem_exec_fence.c
@@ -129,6 +129,9 @@ static void test_fence_busy(int fd, unsigned ring, unsigned flags)
 	uint32_t *batch;
 	int fence, i, timeout;
 
+	if ((flags & HANG) == 0)
+		igt_require(gem_engine_has_mutable_submission(fd, ring));
+
 	gem_quiescent_gpu(fd);
 
 	memset(&execbuf, 0, sizeof(execbuf));
@@ -265,6 +268,8 @@ static void test_fence_busy_all(int fd, unsigned flags)
 	for_each_engine(e, fd) {
 		int fence, new;
 
+		if ((flags & HANG) == 0)
+			igt_require(gem_engine_has_mutable_submission(fd, eb_ring(e)));
 		execbuf.flags = eb_ring(e) | LOCAL_EXEC_FENCE_OUT;
 		execbuf.rsvd2 = -1;
 		gem_execbuf_wr(fd, &execbuf);
@@ -321,6 +326,9 @@ static void test_fence_await(int fd, unsigned ring, unsigned flags)
 	uint32_t *out;
 	int i;
 
+	if ((flags & HANG) == 0)
+		igt_require(gem_engine_has_mutable_submission(fd, ring));
+
 	igt_require(gem_can_store_dword(fd, 0));
 
 	out = gem_mmap__wc(fd, scratch, 0, 4096, PROT_WRITE);
diff --git a/tests/i915/gem_exec_latency.c b/tests/i915/gem_exec_latency.c
index 3d99182a..e4c0c862 100644
--- a/tests/i915/gem_exec_latency.c
+++ b/tests/i915/gem_exec_latency.c
@@ -128,6 +128,8 @@ static void latency_on_ring(int fd,
 	double gpu_latency;
 	int i, j;
 
+	igt_require(gem_engine_has_mutable_submission(fd, ring));
+
 	reg = (volatile uint32_t *)((volatile char *)igt_global_mmio + RCS_TIMESTAMP);
 
 	memset(&execbuf, 0, sizeof(execbuf));
@@ -271,6 +273,8 @@ static void latency_from_ring(int fd,
 	uint32_t ctx[2] = {};
 	int i, j;
 
+	igt_require(gem_engine_has_mutable_submission(fd, ring));
+
 	if (flags & PREEMPT) {
 		ctx[0] = gem_context_create(fd);
 		gem_context_set_priority(fd, ctx[0], -1023);
@@ -316,6 +320,9 @@ static void latency_from_ring(int fd,
 		igt_spin_t *spin = NULL;
 		IGT_CORK_HANDLE(c);
 
+		if (!gem_engine_has_mutable_submission(fd, eb_ring(e)))
+			continue;
+
 		gem_set_domain(fd, obj[2].handle,
 			       I915_GEM_DOMAIN_GTT,
 			       I915_GEM_DOMAIN_GTT);
diff --git a/tests/i915/gem_exec_nop.c b/tests/i915/gem_exec_nop.c
index 2ad8aeea..dbedb356 100644
--- a/tests/i915/gem_exec_nop.c
+++ b/tests/i915/gem_exec_nop.c
@@ -123,6 +123,7 @@ static void poll_ring(int fd, unsigned engine, const char *name, int timeout)
 
 	gem_require_ring(fd, engine);
 	igt_require(gem_can_store_dword(fd, engine));
+	igt_require(gem_engine_has_mutable_submission(fd, engine));
 
 	memset(&obj, 0, sizeof(obj));
 	obj.handle = gem_create(fd, 4096);
@@ -234,7 +235,8 @@ static void poll_sequential(int fd, const char *name, int timeout)
 
 	nengine = 0;
 	for_each_physical_engine(e, fd) {
-		if (!gem_can_store_dword(fd, eb_ring(e)))
+		if (!gem_can_store_dword(fd, eb_ring(e)) ||
+		    !gem_engine_has_mutable_submission(fd, eb_ring(e)))
 			continue;
 
 		engines[nengine++] = eb_ring(e);
diff --git a/tests/i915/gem_exec_schedule.c b/tests/i915/gem_exec_schedule.c
index 5c15f177..ca6bef6a 100644
--- a/tests/i915/gem_exec_schedule.c
+++ b/tests/i915/gem_exec_schedule.c
@@ -652,7 +652,8 @@ static void semaphore_noskip(int i915)
 		igt_spin_t *chain, *spin;
 
 		if (eb_ring(inner) == eb_ring(outer) ||
-		    !gem_can_store_dword(i915, eb_ring(inner)))
+		    !gem_can_store_dword(i915, eb_ring(inner)) ||
+		    !gem_engine_has_mutable_submission(i915, eb_ring(inner)))
 			continue;
 
 		chain = __igt_spin_new(i915, .engine = eb_ring(outer));
@@ -1157,6 +1158,9 @@ static void deep(int fd, unsigned ring)
 	int dep_nreq;
 	int n;
 
+	igt_require(gem_can_store_dword(fd, ring));
+	igt_require(gem_engine_has_mutable_submission(fd, ring));
+
 	ctx = malloc(sizeof(*ctx) * MAX_CONTEXTS);
 	for (n = 0; n < MAX_CONTEXTS; n++) {
 		ctx[n] = gem_context_create(fd);
diff --git a/tests/i915/gem_exec_whisper.c b/tests/i915/gem_exec_whisper.c
index a3384760..45cc1437 100644
--- a/tests/i915/gem_exec_whisper.c
+++ b/tests/i915/gem_exec_whisper.c
@@ -204,13 +204,15 @@ static void whisper(int fd, unsigned engine, unsigned flags)
 	nengine = 0;
 	if (engine == ALL_ENGINES) {
 		for_each_physical_engine(e, fd) {
-			if (gem_can_store_dword(fd, eb_ring(e)))
+			if (gem_can_store_dword(fd, eb_ring(e)) &&
+			    gem_engine_has_mutable_submission(fd, eb_ring(e)))
 				engines[nengine++] = eb_ring(e);
 		}
 	} else {
 		igt_assert(!(flags & ALL));
 		igt_require(gem_has_ring(fd, engine));
 		igt_require(gem_can_store_dword(fd, engine));
+		igt_require(gem_engine_has_mutable_submission(fd, engine));
 		engines[nengine++] = engine;
 	}
 	igt_require(nengine);
diff --git a/tests/prime_busy.c b/tests/prime_busy.c
index 8ee23bb3..bb29216e 100644
--- a/tests/prime_busy.c
+++ b/tests/prime_busy.c
@@ -53,6 +53,9 @@ static void busy(int fd, unsigned ring, unsigned flags)
 	uint32_t *batch, *bbe;
 	int i, count, timeout;
 
+	if ((flags & HANG) == 0)
+		igt_require(gem_engine_has_mutable_submission(fd, ring));
+
 	gem_quiescent_gpu(fd);
 
 	memset(&execbuf, 0, sizeof(execbuf));
diff --git a/tests/prime_vgem.c b/tests/prime_vgem.c
index 2b21ff41..04cc913d 100644
--- a/tests/prime_vgem.c
+++ b/tests/prime_vgem.c
@@ -342,6 +342,8 @@ static void work(int i915, int dmabuf, unsigned ring, uint32_t flags)
 	uint32_t *batch, *bbe;
 	int i, count;
 
+	igt_require(gem_engine_has_mutable_submission(i915, ring));
+
 	memset(&execbuf, 0, sizeof(execbuf));
 	execbuf.buffers_ptr = (uintptr_t)obj;
 	execbuf.buffer_count = 2;
@@ -850,6 +852,7 @@ igt_main
 			      e->name) {
 			gem_require_ring(i915, eb_ring(e));
 			igt_require(gem_can_store_dword(i915, eb_ring(e)));
+			igt_require(gem_engine_has_mutable_submission(i915, eb_ring(e)));
 
 			gem_quiescent_gpu(i915);
 			test_sync(i915, vgem, e->exec_id, e->flags);
@@ -862,6 +865,7 @@ igt_main
 			      e->name) {
 			gem_require_ring(i915, eb_ring(e));
 			igt_require(gem_can_store_dword(i915, eb_ring(e)));
+			igt_require(gem_engine_has_mutable_submission(i915, eb_ring(e)));
 
 			gem_quiescent_gpu(i915);
 			test_busy(i915, vgem, e->exec_id, e->flags);
@@ -874,6 +878,7 @@ igt_main
 			      e->name) {
 			gem_require_ring(i915, eb_ring(e));
 			igt_require(gem_can_store_dword(i915, eb_ring(e)));
+			igt_require(gem_engine_has_mutable_submission(i915, eb_ring(e)));
 
 			gem_quiescent_gpu(i915);
 			test_wait(i915, vgem, e->exec_id, e->flags);
@@ -897,6 +902,7 @@ igt_main
 					e->name) {
 				gem_require_ring(i915, eb_ring(e));
 				igt_require(gem_can_store_dword(i915, eb_ring(e)));
+				igt_require(gem_engine_has_mutable_submission(i915, eb_ring(e)));
 
 				gem_quiescent_gpu(i915);
 				test_fence_wait(i915, vgem, e->exec_id, e->flags);
-- 
2.17.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [igt-dev] [PATCH i-g-t 3/7] lib/i915: Add query to detect if engine accepts only ro batches
@ 2019-11-13 15:49   ` Mika Kuoppala
  0 siblings, 0 replies; 44+ messages in thread
From: Mika Kuoppala @ 2019-11-13 15:49 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev, Kuoppala

From: "Kuoppala, Mika" <mika.kuoppala@intel.com>

If cmd parser is mandatory, batch can't be modified post execbuf.
Some tests rely on modifying batch post execbuf. Give those
tests a method to query if those modifications ever reach
the actual engine command stream.

v2: pull in the test changes, doh
v3: class based query
v4: rebase

Signed-off-by: Kuoppala, Mika <mika.kuoppala@intel.com>
---
 lib/i915/gem_submission.c      | 62 ++++++++++++++++++++++++++++++++++
 lib/i915/gem_submission.h      |  2 ++
 tests/i915/gem_busy.c          |  7 +++-
 tests/i915/gem_exec_async.c    |  3 ++
 tests/i915/gem_exec_await.c    |  7 +++-
 tests/i915/gem_exec_fence.c    |  8 +++++
 tests/i915/gem_exec_latency.c  |  7 ++++
 tests/i915/gem_exec_nop.c      |  4 ++-
 tests/i915/gem_exec_schedule.c |  6 +++-
 tests/i915/gem_exec_whisper.c  |  4 ++-
 tests/prime_busy.c             |  3 ++
 tests/prime_vgem.c             |  6 ++++
 12 files changed, 114 insertions(+), 5 deletions(-)

diff --git a/lib/i915/gem_submission.c b/lib/i915/gem_submission.c
index 4f946493..9bdf28bc 100644
--- a/lib/i915/gem_submission.c
+++ b/lib/i915/gem_submission.c
@@ -64,6 +64,22 @@ static bool has_semaphores(int fd, int dir)
 	return val;
 }
 
+static int cmd_parser_version(int fd)
+{
+	int val = 0;
+	struct drm_i915_getparam gp = {
+		gp.param = I915_PARAM_CMD_PARSER_VERSION,
+		gp.value = &val,
+	};
+
+	if (ioctl(fd, DRM_IOCTL_I915_GETPARAM, &gp) < 0) {
+		val = -errno;
+		igt_assert(val < 0);
+	}
+
+	return val;
+}
+
 /**
  * gem_submission_method:
  * @fd: open i915 drm file descriptor
@@ -253,3 +269,49 @@ void gem_require_blitter(int i915)
 {
 	igt_require(gem_has_blitter(i915));
 }
+
+static bool gem_engine_has_immutable_submission(int i915, int class)
+{
+	const int gen = intel_gen(intel_get_drm_devid(i915));
+        int parser_version;
+
+	parser_version = cmd_parser_version(i915);
+
+	if (parser_version < 0)
+		return false;
+
+	if (gen == 9 && class == I915_ENGINE_CLASS_COPY && parser_version > 9)
+		return true;
+
+	return false;
+}
+
+
+/**
+ * gem_class_has_mutable_submission:
+ * @i915: open i915 drm file descriptor
+ * @class: engine class
+ *
+ * Returns boolean value if the engine class allows batch modifications
+ * post execbuf.
+ */
+
+bool
+gem_class_has_mutable_submission(int fd, int class)
+{
+	return !gem_engine_has_immutable_submission(fd, class);
+}
+
+/**
+ * gem_engine_has_mutable_submission:
+ * @i915: open i915 drm file descriptor
+ * @engine: the engine (I915_EXEC_RING id) of target
+ *
+ * Returns boolean value if the engine allows batch modifications
+ * post execbuf.
+ */
+bool gem_engine_has_mutable_submission(int i915, unsigned int engine)
+{
+	return gem_class_has_mutable_submission(i915,
+						gem_execbuf_flags_to_engine_class(engine));
+}
diff --git a/lib/i915/gem_submission.h b/lib/i915/gem_submission.h
index 6deb7e2d..acd24bcb 100644
--- a/lib/i915/gem_submission.h
+++ b/lib/i915/gem_submission.h
@@ -34,6 +34,8 @@ void gem_submission_print_method(int fd);
 bool gem_has_semaphores(int fd);
 bool gem_has_execlists(int fd);
 bool gem_has_guc_submission(int fd);
+bool gem_engine_has_mutable_submission(int fd, unsigned int engine);
+bool gem_class_has_mutable_submission(int fd, int class);
 
 int gem_cmdparser_version(int i915, uint32_t engine);
 static inline bool gem_has_cmdparser(int i915, uint32_t engine)
diff --git a/tests/i915/gem_busy.c b/tests/i915/gem_busy.c
index a8388149..b84e2bd1 100644
--- a/tests/i915/gem_busy.c
+++ b/tests/i915/gem_busy.c
@@ -243,6 +243,10 @@ static void one(int fd, const struct intel_execution_engine2 *e, unsigned test_f
 	i++;
 
 	igt_assert(i < size/sizeof(*batch));
+
+	if ((test_flags & HANG) == 0)
+		igt_require(gem_class_has_mutable_submission(fd, e->class));
+
 	igt_require(__gem_execbuf(fd, &execbuf) == 0);
 
 	__gem_busy(fd, obj[SCRATCH].handle, &read[SCRATCH], &write[SCRATCH]);
@@ -256,7 +260,8 @@ static void one(int fd, const struct intel_execution_engine2 *e, unsigned test_f
 			    e2->instance == e->instance)
 				continue;
 
-			if (!gem_class_can_store_dword(fd, e2->class))
+			if (!gem_class_can_store_dword(fd, e2->class) ||
+			    !gem_class_has_mutable_submission(fd, e2->class))
 				continue;
 
 			igt_debug("Testing %s in parallel\n", e2->name);
diff --git a/tests/i915/gem_exec_async.c b/tests/i915/gem_exec_async.c
index d83e9f6d..b9859ffa 100644
--- a/tests/i915/gem_exec_async.c
+++ b/tests/i915/gem_exec_async.c
@@ -138,6 +138,9 @@ static void one(int fd, unsigned ring, uint32_t flags)
 	execbuf.buffers_ptr = to_user_pointer(obj);
 	execbuf.buffer_count = 2;
 	execbuf.flags = ring | flags;
+
+	igt_require(gem_engine_has_mutable_submission(fd, ring));
+
 	igt_require(__gem_execbuf(fd, &execbuf) == 0);
 	gem_close(fd, obj[BATCH].handle);
 
diff --git a/tests/i915/gem_exec_await.c b/tests/i915/gem_exec_await.c
index 33e2ee45..7817b483 100644
--- a/tests/i915/gem_exec_await.c
+++ b/tests/i915/gem_exec_await.c
@@ -78,8 +78,13 @@ static void wide(int fd, int ring_size, int timeout, unsigned int flags)
 	double time;
 
 	nengine = 0;
-	for_each_physical_engine(e, fd)
+	for_each_physical_engine(e, fd) {
+		if (!gem_engine_has_mutable_submission(fd, eb_ring(e)))
+			continue;
+
 		engines[nengine++] = eb_ring(e);
+	}
+
 	igt_require(nengine);
 
 	exec = calloc(nengine, sizeof(*exec));
diff --git a/tests/i915/gem_exec_fence.c b/tests/i915/gem_exec_fence.c
index 002120bf..fbb11ab2 100644
--- a/tests/i915/gem_exec_fence.c
+++ b/tests/i915/gem_exec_fence.c
@@ -129,6 +129,9 @@ static void test_fence_busy(int fd, unsigned ring, unsigned flags)
 	uint32_t *batch;
 	int fence, i, timeout;
 
+	if ((flags & HANG) == 0)
+		igt_require(gem_engine_has_mutable_submission(fd, ring));
+
 	gem_quiescent_gpu(fd);
 
 	memset(&execbuf, 0, sizeof(execbuf));
@@ -265,6 +268,8 @@ static void test_fence_busy_all(int fd, unsigned flags)
 	for_each_engine(e, fd) {
 		int fence, new;
 
+		if ((flags & HANG) == 0)
+			igt_require(gem_engine_has_mutable_submission(fd, eb_ring(e)));
 		execbuf.flags = eb_ring(e) | LOCAL_EXEC_FENCE_OUT;
 		execbuf.rsvd2 = -1;
 		gem_execbuf_wr(fd, &execbuf);
@@ -321,6 +326,9 @@ static void test_fence_await(int fd, unsigned ring, unsigned flags)
 	uint32_t *out;
 	int i;
 
+	if ((flags & HANG) == 0)
+		igt_require(gem_engine_has_mutable_submission(fd, ring));
+
 	igt_require(gem_can_store_dword(fd, 0));
 
 	out = gem_mmap__wc(fd, scratch, 0, 4096, PROT_WRITE);
diff --git a/tests/i915/gem_exec_latency.c b/tests/i915/gem_exec_latency.c
index 3d99182a..e4c0c862 100644
--- a/tests/i915/gem_exec_latency.c
+++ b/tests/i915/gem_exec_latency.c
@@ -128,6 +128,8 @@ static void latency_on_ring(int fd,
 	double gpu_latency;
 	int i, j;
 
+	igt_require(gem_engine_has_mutable_submission(fd, ring));
+
 	reg = (volatile uint32_t *)((volatile char *)igt_global_mmio + RCS_TIMESTAMP);
 
 	memset(&execbuf, 0, sizeof(execbuf));
@@ -271,6 +273,8 @@ static void latency_from_ring(int fd,
 	uint32_t ctx[2] = {};
 	int i, j;
 
+	igt_require(gem_engine_has_mutable_submission(fd, ring));
+
 	if (flags & PREEMPT) {
 		ctx[0] = gem_context_create(fd);
 		gem_context_set_priority(fd, ctx[0], -1023);
@@ -316,6 +320,9 @@ static void latency_from_ring(int fd,
 		igt_spin_t *spin = NULL;
 		IGT_CORK_HANDLE(c);
 
+		if (!gem_engine_has_mutable_submission(fd, eb_ring(e)))
+			continue;
+
 		gem_set_domain(fd, obj[2].handle,
 			       I915_GEM_DOMAIN_GTT,
 			       I915_GEM_DOMAIN_GTT);
diff --git a/tests/i915/gem_exec_nop.c b/tests/i915/gem_exec_nop.c
index 2ad8aeea..dbedb356 100644
--- a/tests/i915/gem_exec_nop.c
+++ b/tests/i915/gem_exec_nop.c
@@ -123,6 +123,7 @@ static void poll_ring(int fd, unsigned engine, const char *name, int timeout)
 
 	gem_require_ring(fd, engine);
 	igt_require(gem_can_store_dword(fd, engine));
+	igt_require(gem_engine_has_mutable_submission(fd, engine));
 
 	memset(&obj, 0, sizeof(obj));
 	obj.handle = gem_create(fd, 4096);
@@ -234,7 +235,8 @@ static void poll_sequential(int fd, const char *name, int timeout)
 
 	nengine = 0;
 	for_each_physical_engine(e, fd) {
-		if (!gem_can_store_dword(fd, eb_ring(e)))
+		if (!gem_can_store_dword(fd, eb_ring(e)) ||
+		    !gem_engine_has_mutable_submission(fd, eb_ring(e)))
 			continue;
 
 		engines[nengine++] = eb_ring(e);
diff --git a/tests/i915/gem_exec_schedule.c b/tests/i915/gem_exec_schedule.c
index 5c15f177..ca6bef6a 100644
--- a/tests/i915/gem_exec_schedule.c
+++ b/tests/i915/gem_exec_schedule.c
@@ -652,7 +652,8 @@ static void semaphore_noskip(int i915)
 		igt_spin_t *chain, *spin;
 
 		if (eb_ring(inner) == eb_ring(outer) ||
-		    !gem_can_store_dword(i915, eb_ring(inner)))
+		    !gem_can_store_dword(i915, eb_ring(inner)) ||
+		    !gem_engine_has_mutable_submission(i915, eb_ring(inner)))
 			continue;
 
 		chain = __igt_spin_new(i915, .engine = eb_ring(outer));
@@ -1157,6 +1158,9 @@ static void deep(int fd, unsigned ring)
 	int dep_nreq;
 	int n;
 
+	igt_require(gem_can_store_dword(fd, ring));
+	igt_require(gem_engine_has_mutable_submission(fd, ring));
+
 	ctx = malloc(sizeof(*ctx) * MAX_CONTEXTS);
 	for (n = 0; n < MAX_CONTEXTS; n++) {
 		ctx[n] = gem_context_create(fd);
diff --git a/tests/i915/gem_exec_whisper.c b/tests/i915/gem_exec_whisper.c
index a3384760..45cc1437 100644
--- a/tests/i915/gem_exec_whisper.c
+++ b/tests/i915/gem_exec_whisper.c
@@ -204,13 +204,15 @@ static void whisper(int fd, unsigned engine, unsigned flags)
 	nengine = 0;
 	if (engine == ALL_ENGINES) {
 		for_each_physical_engine(e, fd) {
-			if (gem_can_store_dword(fd, eb_ring(e)))
+			if (gem_can_store_dword(fd, eb_ring(e)) &&
+			    gem_engine_has_mutable_submission(fd, eb_ring(e)))
 				engines[nengine++] = eb_ring(e);
 		}
 	} else {
 		igt_assert(!(flags & ALL));
 		igt_require(gem_has_ring(fd, engine));
 		igt_require(gem_can_store_dword(fd, engine));
+		igt_require(gem_engine_has_mutable_submission(fd, engine));
 		engines[nengine++] = engine;
 	}
 	igt_require(nengine);
diff --git a/tests/prime_busy.c b/tests/prime_busy.c
index 8ee23bb3..bb29216e 100644
--- a/tests/prime_busy.c
+++ b/tests/prime_busy.c
@@ -53,6 +53,9 @@ static void busy(int fd, unsigned ring, unsigned flags)
 	uint32_t *batch, *bbe;
 	int i, count, timeout;
 
+	if ((flags & HANG) == 0)
+		igt_require(gem_engine_has_mutable_submission(fd, ring));
+
 	gem_quiescent_gpu(fd);
 
 	memset(&execbuf, 0, sizeof(execbuf));
diff --git a/tests/prime_vgem.c b/tests/prime_vgem.c
index 2b21ff41..04cc913d 100644
--- a/tests/prime_vgem.c
+++ b/tests/prime_vgem.c
@@ -342,6 +342,8 @@ static void work(int i915, int dmabuf, unsigned ring, uint32_t flags)
 	uint32_t *batch, *bbe;
 	int i, count;
 
+	igt_require(gem_engine_has_mutable_submission(i915, ring));
+
 	memset(&execbuf, 0, sizeof(execbuf));
 	execbuf.buffers_ptr = (uintptr_t)obj;
 	execbuf.buffer_count = 2;
@@ -850,6 +852,7 @@ igt_main
 			      e->name) {
 			gem_require_ring(i915, eb_ring(e));
 			igt_require(gem_can_store_dword(i915, eb_ring(e)));
+			igt_require(gem_engine_has_mutable_submission(i915, eb_ring(e)));
 
 			gem_quiescent_gpu(i915);
 			test_sync(i915, vgem, e->exec_id, e->flags);
@@ -862,6 +865,7 @@ igt_main
 			      e->name) {
 			gem_require_ring(i915, eb_ring(e));
 			igt_require(gem_can_store_dword(i915, eb_ring(e)));
+			igt_require(gem_engine_has_mutable_submission(i915, eb_ring(e)));
 
 			gem_quiescent_gpu(i915);
 			test_busy(i915, vgem, e->exec_id, e->flags);
@@ -874,6 +878,7 @@ igt_main
 			      e->name) {
 			gem_require_ring(i915, eb_ring(e));
 			igt_require(gem_can_store_dword(i915, eb_ring(e)));
+			igt_require(gem_engine_has_mutable_submission(i915, eb_ring(e)));
 
 			gem_quiescent_gpu(i915);
 			test_wait(i915, vgem, e->exec_id, e->flags);
@@ -897,6 +902,7 @@ igt_main
 					e->name) {
 				gem_require_ring(i915, eb_ring(e));
 				igt_require(gem_can_store_dword(i915, eb_ring(e)));
+				igt_require(gem_engine_has_mutable_submission(i915, eb_ring(e)));
 
 				gem_quiescent_gpu(i915);
 				test_fence_wait(i915, vgem, e->exec_id, e->flags);
-- 
2.17.1

_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH i-g-t 4/7] tests/i915: Skip if secure batches is not available
@ 2019-11-13 15:49   ` Mika Kuoppala
  0 siblings, 0 replies; 44+ messages in thread
From: Mika Kuoppala @ 2019-11-13 15:49 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev, Kuoppala

From: "Kuoppala, Mika" <mika.kuoppala@intel.com>

If we can't do secure execbuf, there is no point in trying.

Signed-off-by: Kuoppala, Mika <mika.kuoppala@intel.com>
---
 tests/i915/gem_exec_params.c   | 16 ++++++++++++++++
 tests/i915/gem_mocs_settings.c | 14 ++++++++++++++
 tests/perf_pmu.c               | 11 +++++++++++
 3 files changed, 41 insertions(+)

diff --git a/tests/i915/gem_exec_params.c b/tests/i915/gem_exec_params.c
index 8f15e645..c5517b07 100644
--- a/tests/i915/gem_exec_params.c
+++ b/tests/i915/gem_exec_params.c
@@ -193,6 +193,19 @@ static void test_batch_first(int fd)
 	gem_close(fd, obj[0].handle);
 }
 
+static int has_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);
+
+	return v;
+}
+
 struct drm_i915_gem_execbuffer2 execbuf;
 struct drm_i915_gem_exec_object2 gem_exec[1];
 uint32_t batch[2] = {MI_BATCH_BUFFER_END};
@@ -340,6 +353,8 @@ igt_main
 	}
 
 	igt_subtest("secure-non-root") {
+		igt_require(has_secure_batches(fd));
+
 		igt_fork(child, 1) {
 			igt_drop_root();
 
@@ -352,6 +367,7 @@ igt_main
 
 	igt_subtest("secure-non-master") {
 		igt_require(__igt_device_set_master(fd) == 0); /* Requires root privilege */
+		igt_require(has_secure_batches(fd));
 
 		igt_device_drop_master(fd);
 		execbuf.flags = I915_EXEC_RENDER | I915_EXEC_SECURE;
diff --git a/tests/i915/gem_mocs_settings.c b/tests/i915/gem_mocs_settings.c
index fc2ccb21..82eb8a3f 100644
--- a/tests/i915/gem_mocs_settings.c
+++ b/tests/i915/gem_mocs_settings.c
@@ -225,6 +225,19 @@ static uint32_t get_engine_base(int fd, uint32_t engine)
 	}
 }
 
+static int has_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);
+
+	return v;
+}
+
 #define MI_STORE_REGISTER_MEM_64_BIT_ADDR	((0x24 << 23) | 2)
 
 static int create_read_batch(struct drm_i915_gem_relocation_entry *reloc,
@@ -566,6 +579,7 @@ igt_main
 		igt_require_gem(fd);
 		gem_require_mocs_registers(fd);
 		igt_require(get_mocs_settings(fd, &table, false));
+		igt_require(has_secure_batches(fd));
 	}
 
 	for (e = intel_execution_engines; e->name; e++) {
diff --git a/tests/perf_pmu.c b/tests/perf_pmu.c
index e2bd2cc5..296d04c6 100644
--- a/tests/perf_pmu.c
+++ b/tests/perf_pmu.c
@@ -893,6 +893,16 @@ static int wait_vblank(int fd, union drm_wait_vblank *vbl)
 	return err;
 }
 
+static int has_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);
+	return v;
+}
+
 static void
 event_wait(int gem_fd, const struct intel_execution_engine2 *e)
 {
@@ -910,6 +920,7 @@ event_wait(int gem_fd, const struct intel_execution_engine2 *e)
 
 	devid = intel_get_drm_devid(gem_fd);
 	igt_require(intel_gen(devid) >= 7);
+	igt_require(has_secure_batches(fd));
 	igt_skip_on(IS_VALLEYVIEW(devid) || IS_CHERRYVIEW(devid));
 
 	kmstest_set_vt_graphics_mode();
-- 
2.17.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [Intel-gfx] [PATCH i-g-t 4/7] tests/i915: Skip if secure batches is not available
@ 2019-11-13 15:49   ` Mika Kuoppala
  0 siblings, 0 replies; 44+ messages in thread
From: Mika Kuoppala @ 2019-11-13 15:49 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev, Kuoppala

From: "Kuoppala, Mika" <mika.kuoppala@intel.com>

If we can't do secure execbuf, there is no point in trying.

Signed-off-by: Kuoppala, Mika <mika.kuoppala@intel.com>
---
 tests/i915/gem_exec_params.c   | 16 ++++++++++++++++
 tests/i915/gem_mocs_settings.c | 14 ++++++++++++++
 tests/perf_pmu.c               | 11 +++++++++++
 3 files changed, 41 insertions(+)

diff --git a/tests/i915/gem_exec_params.c b/tests/i915/gem_exec_params.c
index 8f15e645..c5517b07 100644
--- a/tests/i915/gem_exec_params.c
+++ b/tests/i915/gem_exec_params.c
@@ -193,6 +193,19 @@ static void test_batch_first(int fd)
 	gem_close(fd, obj[0].handle);
 }
 
+static int has_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);
+
+	return v;
+}
+
 struct drm_i915_gem_execbuffer2 execbuf;
 struct drm_i915_gem_exec_object2 gem_exec[1];
 uint32_t batch[2] = {MI_BATCH_BUFFER_END};
@@ -340,6 +353,8 @@ igt_main
 	}
 
 	igt_subtest("secure-non-root") {
+		igt_require(has_secure_batches(fd));
+
 		igt_fork(child, 1) {
 			igt_drop_root();
 
@@ -352,6 +367,7 @@ igt_main
 
 	igt_subtest("secure-non-master") {
 		igt_require(__igt_device_set_master(fd) == 0); /* Requires root privilege */
+		igt_require(has_secure_batches(fd));
 
 		igt_device_drop_master(fd);
 		execbuf.flags = I915_EXEC_RENDER | I915_EXEC_SECURE;
diff --git a/tests/i915/gem_mocs_settings.c b/tests/i915/gem_mocs_settings.c
index fc2ccb21..82eb8a3f 100644
--- a/tests/i915/gem_mocs_settings.c
+++ b/tests/i915/gem_mocs_settings.c
@@ -225,6 +225,19 @@ static uint32_t get_engine_base(int fd, uint32_t engine)
 	}
 }
 
+static int has_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);
+
+	return v;
+}
+
 #define MI_STORE_REGISTER_MEM_64_BIT_ADDR	((0x24 << 23) | 2)
 
 static int create_read_batch(struct drm_i915_gem_relocation_entry *reloc,
@@ -566,6 +579,7 @@ igt_main
 		igt_require_gem(fd);
 		gem_require_mocs_registers(fd);
 		igt_require(get_mocs_settings(fd, &table, false));
+		igt_require(has_secure_batches(fd));
 	}
 
 	for (e = intel_execution_engines; e->name; e++) {
diff --git a/tests/perf_pmu.c b/tests/perf_pmu.c
index e2bd2cc5..296d04c6 100644
--- a/tests/perf_pmu.c
+++ b/tests/perf_pmu.c
@@ -893,6 +893,16 @@ static int wait_vblank(int fd, union drm_wait_vblank *vbl)
 	return err;
 }
 
+static int has_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);
+	return v;
+}
+
 static void
 event_wait(int gem_fd, const struct intel_execution_engine2 *e)
 {
@@ -910,6 +920,7 @@ event_wait(int gem_fd, const struct intel_execution_engine2 *e)
 
 	devid = intel_get_drm_devid(gem_fd);
 	igt_require(intel_gen(devid) >= 7);
+	igt_require(has_secure_batches(fd));
 	igt_skip_on(IS_VALLEYVIEW(devid) || IS_CHERRYVIEW(devid));
 
 	kmstest_set_vt_graphics_mode();
-- 
2.17.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [igt-dev] [PATCH i-g-t 4/7] tests/i915: Skip if secure batches is not available
@ 2019-11-13 15:49   ` Mika Kuoppala
  0 siblings, 0 replies; 44+ messages in thread
From: Mika Kuoppala @ 2019-11-13 15:49 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev, Kuoppala

From: "Kuoppala, Mika" <mika.kuoppala@intel.com>

If we can't do secure execbuf, there is no point in trying.

Signed-off-by: Kuoppala, Mika <mika.kuoppala@intel.com>
---
 tests/i915/gem_exec_params.c   | 16 ++++++++++++++++
 tests/i915/gem_mocs_settings.c | 14 ++++++++++++++
 tests/perf_pmu.c               | 11 +++++++++++
 3 files changed, 41 insertions(+)

diff --git a/tests/i915/gem_exec_params.c b/tests/i915/gem_exec_params.c
index 8f15e645..c5517b07 100644
--- a/tests/i915/gem_exec_params.c
+++ b/tests/i915/gem_exec_params.c
@@ -193,6 +193,19 @@ static void test_batch_first(int fd)
 	gem_close(fd, obj[0].handle);
 }
 
+static int has_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);
+
+	return v;
+}
+
 struct drm_i915_gem_execbuffer2 execbuf;
 struct drm_i915_gem_exec_object2 gem_exec[1];
 uint32_t batch[2] = {MI_BATCH_BUFFER_END};
@@ -340,6 +353,8 @@ igt_main
 	}
 
 	igt_subtest("secure-non-root") {
+		igt_require(has_secure_batches(fd));
+
 		igt_fork(child, 1) {
 			igt_drop_root();
 
@@ -352,6 +367,7 @@ igt_main
 
 	igt_subtest("secure-non-master") {
 		igt_require(__igt_device_set_master(fd) == 0); /* Requires root privilege */
+		igt_require(has_secure_batches(fd));
 
 		igt_device_drop_master(fd);
 		execbuf.flags = I915_EXEC_RENDER | I915_EXEC_SECURE;
diff --git a/tests/i915/gem_mocs_settings.c b/tests/i915/gem_mocs_settings.c
index fc2ccb21..82eb8a3f 100644
--- a/tests/i915/gem_mocs_settings.c
+++ b/tests/i915/gem_mocs_settings.c
@@ -225,6 +225,19 @@ static uint32_t get_engine_base(int fd, uint32_t engine)
 	}
 }
 
+static int has_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);
+
+	return v;
+}
+
 #define MI_STORE_REGISTER_MEM_64_BIT_ADDR	((0x24 << 23) | 2)
 
 static int create_read_batch(struct drm_i915_gem_relocation_entry *reloc,
@@ -566,6 +579,7 @@ igt_main
 		igt_require_gem(fd);
 		gem_require_mocs_registers(fd);
 		igt_require(get_mocs_settings(fd, &table, false));
+		igt_require(has_secure_batches(fd));
 	}
 
 	for (e = intel_execution_engines; e->name; e++) {
diff --git a/tests/perf_pmu.c b/tests/perf_pmu.c
index e2bd2cc5..296d04c6 100644
--- a/tests/perf_pmu.c
+++ b/tests/perf_pmu.c
@@ -893,6 +893,16 @@ static int wait_vblank(int fd, union drm_wait_vblank *vbl)
 	return err;
 }
 
+static int has_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);
+	return v;
+}
+
 static void
 event_wait(int gem_fd, const struct intel_execution_engine2 *e)
 {
@@ -910,6 +920,7 @@ event_wait(int gem_fd, const struct intel_execution_engine2 *e)
 
 	devid = intel_get_drm_devid(gem_fd);
 	igt_require(intel_gen(devid) >= 7);
+	igt_require(has_secure_batches(fd));
 	igt_skip_on(IS_VALLEYVIEW(devid) || IS_CHERRYVIEW(devid));
 
 	kmstest_set_vt_graphics_mode();
-- 
2.17.1

_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH i-g-t 5/7] Add tests/gem_blt_parse
@ 2019-11-13 15:49   ` Mika Kuoppala
  0 siblings, 0 replies; 44+ messages in thread
From: Mika Kuoppala @ 2019-11-13 15:49 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

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 ++++++++++++++++++++++++++
 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, &regs[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

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [Intel-gfx] [PATCH i-g-t 5/7] Add tests/gem_blt_parse
@ 2019-11-13 15:49   ` Mika Kuoppala
  0 siblings, 0 replies; 44+ messages in thread
From: Mika Kuoppala @ 2019-11-13 15:49 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

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 ++++++++++++++++++++++++++
 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, &regs[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

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [igt-dev] [PATCH i-g-t 5/7] Add tests/gem_blt_parse
@ 2019-11-13 15:49   ` Mika Kuoppala
  0 siblings, 0 replies; 44+ messages in thread
From: Mika Kuoppala @ 2019-11-13 15:49 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

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 ++++++++++++++++++++++++++
 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, &regs[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

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH i-g-t 6/7] lib/igt_aux: Add helper to query suspend-to-mem modes
@ 2019-11-13 15:49   ` Mika Kuoppala
  0 siblings, 0 replies; 44+ messages in thread
From: Mika Kuoppala @ 2019-11-13 15:49 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

From: Imre Deak <imre.deak@intel.com>

Add a helper to query the supported and currently selected
suspend-to-mem modes.

v2:
- Fix for old kernels where the mem_sleep sysfs file didn't yet exist.

Signed-off-by: Imre Deak <imre.deak@intel.com>
---
 lib/igt_aux.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/igt_aux.h | 24 +++++++++++++++
 2 files changed, 105 insertions(+)

diff --git a/lib/igt_aux.c b/lib/igt_aux.c
index 578f8579..f2cb3247 100644
--- a/lib/igt_aux.c
+++ b/lib/igt_aux.c
@@ -827,6 +827,87 @@ static uint32_t get_supported_suspend_states(int power_dir)
 	return state_mask;
 }
 
+static const char *suspend_to_mem_mode_name[] = {
+	[SUSPEND_TO_MEM_S2IDLE] = "s2idle",
+	[SUSPEND_TO_MEM_SHALLOW] = "shallow",
+	[SUSPEND_TO_MEM_DEEP] = "deep",
+};
+
+static uint32_t
+get_supported_suspend_to_mem_modes(int power_dir,
+				   enum igt_suspend_to_mem_mode *selected_mode)
+{
+	char *modes;
+	char *mode_name;
+	uint32_t mode_mask;
+	bool selected_found = false;
+
+	/*
+	 * Other modes than deep sleep were only introduced with the mem_sleep
+	 * sysfs file.
+	 */
+	modes = igt_sysfs_get(power_dir, "mem_sleep");
+	if (!modes) {
+		*selected_mode = SUSPEND_TO_MEM_DEEP;
+		return 1 << SUSPEND_TO_MEM_DEEP;
+	}
+
+	mode_mask = 0;
+	for (mode_name = strtok(modes, " "); mode_name;
+	     mode_name = strtok(NULL, " ")) {
+		enum igt_suspend_to_mem_mode mode;
+		bool selected = false;
+
+		if (mode_name[0] == '[') {
+			char *e = &mode_name[strlen(mode_name) - 1];
+
+			igt_assert(!selected_found);
+			igt_assert(*e == ']');
+			mode_name++;
+			*e = '\0';
+
+			selected = true;
+		}
+
+		for (mode = SUSPEND_TO_MEM_S2IDLE;
+		     mode < SUSPEND_TO_MEM_NUM;
+		     mode++)
+			if (strcmp(mode_name,
+				   suspend_to_mem_mode_name[mode]) == 0)
+				break;
+		igt_assert(mode < SUSPEND_TO_MEM_NUM);
+		mode_mask |= 1 << mode;
+		if (selected) {
+			selected_found = true;
+			if (selected_mode)
+				*selected_mode = mode;
+		}
+	}
+
+	igt_assert(selected_found);
+
+	free(modes);
+
+	return mode_mask;
+}
+
+/**
+ * igt_get_suspend_to_mem_mode:
+ *
+ * Returns the currently selected @igt_suspend_to_mem_mode.
+ */
+enum igt_suspend_to_mem_mode igt_get_suspend_to_mem_mode(void)
+{
+	int power_dir;
+	enum igt_suspend_to_mem_mode mode;
+
+	igt_require((power_dir = open("/sys/power", O_RDONLY)) >= 0);
+	(void)get_supported_suspend_to_mem_modes(power_dir, &mode);
+	close(power_dir);
+
+	return mode;
+}
+
 /**
  * igt_system_suspend_autoresume:
  * @state: an #igt_suspend_state, the target suspend state
diff --git a/lib/igt_aux.h b/lib/igt_aux.h
index 04d22904..7ccaa8c4 100644
--- a/lib/igt_aux.h
+++ b/lib/igt_aux.h
@@ -184,10 +184,34 @@ enum igt_suspend_test {
 	SUSPEND_TEST_NUM,
 };
 
+/**
+ * igt_suspend_to_mem_mode:
+ * @SUSPEND_TO_MEM_S2IDLE: suspend to mem maps to @SUSPEND_STATE_FREEZE
+ * @SUSPEND_TO_MEM_SHALLOW: suspend to mem maps to @SUSPEND_STATE_STANDBY
+ * @SUSPEND_TO_MEM_DEEP: suspend to mem will target the ACPI S3 state
+ *
+ * The target system state when suspending to mem:
+ * - Suspending with @SUSPEND_STATE_MEM/@SUSPEND_TO_MEM_S2IDLE is equivalent to
+ *   suspending with @SUSPEND_STATE_FREEZE.
+ * - Suspending with @SUSPEND_STATE_MEM/@SUSPEND_TO_MEM_SHALLOW is equivalent to
+ *   suspending with @SUSPEND_STATE_STANDBY.
+ * - Suspending with @SUSPEND_STATE_MEM/@SUSPEND_TO_MEM_DEEP will target the
+ *   ACPI S3 state.
+ */
+enum igt_suspend_to_mem_mode {
+	SUSPEND_TO_MEM_S2IDLE,
+	SUSPEND_TO_MEM_SHALLOW,
+	SUSPEND_TO_MEM_DEEP,
+
+	/*< private >*/
+	SUSPEND_TO_MEM_NUM,
+};
+
 void igt_system_suspend_autoresume(enum igt_suspend_state state,
 				   enum igt_suspend_test test);
 void igt_set_autoresume_delay(int delay_secs);
 int igt_get_autoresume_delay(enum igt_suspend_state state);
+enum igt_suspend_to_mem_mode igt_get_suspend_to_mem_mode(void);
 
 /* dropping priviledges */
 void igt_drop_root(void);
-- 
2.17.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [Intel-gfx] [PATCH i-g-t 6/7] lib/igt_aux: Add helper to query suspend-to-mem modes
@ 2019-11-13 15:49   ` Mika Kuoppala
  0 siblings, 0 replies; 44+ messages in thread
From: Mika Kuoppala @ 2019-11-13 15:49 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

From: Imre Deak <imre.deak@intel.com>

Add a helper to query the supported and currently selected
suspend-to-mem modes.

v2:
- Fix for old kernels where the mem_sleep sysfs file didn't yet exist.

Signed-off-by: Imre Deak <imre.deak@intel.com>
---
 lib/igt_aux.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/igt_aux.h | 24 +++++++++++++++
 2 files changed, 105 insertions(+)

diff --git a/lib/igt_aux.c b/lib/igt_aux.c
index 578f8579..f2cb3247 100644
--- a/lib/igt_aux.c
+++ b/lib/igt_aux.c
@@ -827,6 +827,87 @@ static uint32_t get_supported_suspend_states(int power_dir)
 	return state_mask;
 }
 
+static const char *suspend_to_mem_mode_name[] = {
+	[SUSPEND_TO_MEM_S2IDLE] = "s2idle",
+	[SUSPEND_TO_MEM_SHALLOW] = "shallow",
+	[SUSPEND_TO_MEM_DEEP] = "deep",
+};
+
+static uint32_t
+get_supported_suspend_to_mem_modes(int power_dir,
+				   enum igt_suspend_to_mem_mode *selected_mode)
+{
+	char *modes;
+	char *mode_name;
+	uint32_t mode_mask;
+	bool selected_found = false;
+
+	/*
+	 * Other modes than deep sleep were only introduced with the mem_sleep
+	 * sysfs file.
+	 */
+	modes = igt_sysfs_get(power_dir, "mem_sleep");
+	if (!modes) {
+		*selected_mode = SUSPEND_TO_MEM_DEEP;
+		return 1 << SUSPEND_TO_MEM_DEEP;
+	}
+
+	mode_mask = 0;
+	for (mode_name = strtok(modes, " "); mode_name;
+	     mode_name = strtok(NULL, " ")) {
+		enum igt_suspend_to_mem_mode mode;
+		bool selected = false;
+
+		if (mode_name[0] == '[') {
+			char *e = &mode_name[strlen(mode_name) - 1];
+
+			igt_assert(!selected_found);
+			igt_assert(*e == ']');
+			mode_name++;
+			*e = '\0';
+
+			selected = true;
+		}
+
+		for (mode = SUSPEND_TO_MEM_S2IDLE;
+		     mode < SUSPEND_TO_MEM_NUM;
+		     mode++)
+			if (strcmp(mode_name,
+				   suspend_to_mem_mode_name[mode]) == 0)
+				break;
+		igt_assert(mode < SUSPEND_TO_MEM_NUM);
+		mode_mask |= 1 << mode;
+		if (selected) {
+			selected_found = true;
+			if (selected_mode)
+				*selected_mode = mode;
+		}
+	}
+
+	igt_assert(selected_found);
+
+	free(modes);
+
+	return mode_mask;
+}
+
+/**
+ * igt_get_suspend_to_mem_mode:
+ *
+ * Returns the currently selected @igt_suspend_to_mem_mode.
+ */
+enum igt_suspend_to_mem_mode igt_get_suspend_to_mem_mode(void)
+{
+	int power_dir;
+	enum igt_suspend_to_mem_mode mode;
+
+	igt_require((power_dir = open("/sys/power", O_RDONLY)) >= 0);
+	(void)get_supported_suspend_to_mem_modes(power_dir, &mode);
+	close(power_dir);
+
+	return mode;
+}
+
 /**
  * igt_system_suspend_autoresume:
  * @state: an #igt_suspend_state, the target suspend state
diff --git a/lib/igt_aux.h b/lib/igt_aux.h
index 04d22904..7ccaa8c4 100644
--- a/lib/igt_aux.h
+++ b/lib/igt_aux.h
@@ -184,10 +184,34 @@ enum igt_suspend_test {
 	SUSPEND_TEST_NUM,
 };
 
+/**
+ * igt_suspend_to_mem_mode:
+ * @SUSPEND_TO_MEM_S2IDLE: suspend to mem maps to @SUSPEND_STATE_FREEZE
+ * @SUSPEND_TO_MEM_SHALLOW: suspend to mem maps to @SUSPEND_STATE_STANDBY
+ * @SUSPEND_TO_MEM_DEEP: suspend to mem will target the ACPI S3 state
+ *
+ * The target system state when suspending to mem:
+ * - Suspending with @SUSPEND_STATE_MEM/@SUSPEND_TO_MEM_S2IDLE is equivalent to
+ *   suspending with @SUSPEND_STATE_FREEZE.
+ * - Suspending with @SUSPEND_STATE_MEM/@SUSPEND_TO_MEM_SHALLOW is equivalent to
+ *   suspending with @SUSPEND_STATE_STANDBY.
+ * - Suspending with @SUSPEND_STATE_MEM/@SUSPEND_TO_MEM_DEEP will target the
+ *   ACPI S3 state.
+ */
+enum igt_suspend_to_mem_mode {
+	SUSPEND_TO_MEM_S2IDLE,
+	SUSPEND_TO_MEM_SHALLOW,
+	SUSPEND_TO_MEM_DEEP,
+
+	/*< private >*/
+	SUSPEND_TO_MEM_NUM,
+};
+
 void igt_system_suspend_autoresume(enum igt_suspend_state state,
 				   enum igt_suspend_test test);
 void igt_set_autoresume_delay(int delay_secs);
 int igt_get_autoresume_delay(enum igt_suspend_state state);
+enum igt_suspend_to_mem_mode igt_get_suspend_to_mem_mode(void);
 
 /* dropping priviledges */
 void igt_drop_root(void);
-- 
2.17.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [igt-dev] [PATCH i-g-t 6/7] lib/igt_aux: Add helper to query suspend-to-mem modes
@ 2019-11-13 15:49   ` Mika Kuoppala
  0 siblings, 0 replies; 44+ messages in thread
From: Mika Kuoppala @ 2019-11-13 15:49 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

From: Imre Deak <imre.deak@intel.com>

Add a helper to query the supported and currently selected
suspend-to-mem modes.

v2:
- Fix for old kernels where the mem_sleep sysfs file didn't yet exist.

Signed-off-by: Imre Deak <imre.deak@intel.com>
---
 lib/igt_aux.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/igt_aux.h | 24 +++++++++++++++
 2 files changed, 105 insertions(+)

diff --git a/lib/igt_aux.c b/lib/igt_aux.c
index 578f8579..f2cb3247 100644
--- a/lib/igt_aux.c
+++ b/lib/igt_aux.c
@@ -827,6 +827,87 @@ static uint32_t get_supported_suspend_states(int power_dir)
 	return state_mask;
 }
 
+static const char *suspend_to_mem_mode_name[] = {
+	[SUSPEND_TO_MEM_S2IDLE] = "s2idle",
+	[SUSPEND_TO_MEM_SHALLOW] = "shallow",
+	[SUSPEND_TO_MEM_DEEP] = "deep",
+};
+
+static uint32_t
+get_supported_suspend_to_mem_modes(int power_dir,
+				   enum igt_suspend_to_mem_mode *selected_mode)
+{
+	char *modes;
+	char *mode_name;
+	uint32_t mode_mask;
+	bool selected_found = false;
+
+	/*
+	 * Other modes than deep sleep were only introduced with the mem_sleep
+	 * sysfs file.
+	 */
+	modes = igt_sysfs_get(power_dir, "mem_sleep");
+	if (!modes) {
+		*selected_mode = SUSPEND_TO_MEM_DEEP;
+		return 1 << SUSPEND_TO_MEM_DEEP;
+	}
+
+	mode_mask = 0;
+	for (mode_name = strtok(modes, " "); mode_name;
+	     mode_name = strtok(NULL, " ")) {
+		enum igt_suspend_to_mem_mode mode;
+		bool selected = false;
+
+		if (mode_name[0] == '[') {
+			char *e = &mode_name[strlen(mode_name) - 1];
+
+			igt_assert(!selected_found);
+			igt_assert(*e == ']');
+			mode_name++;
+			*e = '\0';
+
+			selected = true;
+		}
+
+		for (mode = SUSPEND_TO_MEM_S2IDLE;
+		     mode < SUSPEND_TO_MEM_NUM;
+		     mode++)
+			if (strcmp(mode_name,
+				   suspend_to_mem_mode_name[mode]) == 0)
+				break;
+		igt_assert(mode < SUSPEND_TO_MEM_NUM);
+		mode_mask |= 1 << mode;
+		if (selected) {
+			selected_found = true;
+			if (selected_mode)
+				*selected_mode = mode;
+		}
+	}
+
+	igt_assert(selected_found);
+
+	free(modes);
+
+	return mode_mask;
+}
+
+/**
+ * igt_get_suspend_to_mem_mode:
+ *
+ * Returns the currently selected @igt_suspend_to_mem_mode.
+ */
+enum igt_suspend_to_mem_mode igt_get_suspend_to_mem_mode(void)
+{
+	int power_dir;
+	enum igt_suspend_to_mem_mode mode;
+
+	igt_require((power_dir = open("/sys/power", O_RDONLY)) >= 0);
+	(void)get_supported_suspend_to_mem_modes(power_dir, &mode);
+	close(power_dir);
+
+	return mode;
+}
+
 /**
  * igt_system_suspend_autoresume:
  * @state: an #igt_suspend_state, the target suspend state
diff --git a/lib/igt_aux.h b/lib/igt_aux.h
index 04d22904..7ccaa8c4 100644
--- a/lib/igt_aux.h
+++ b/lib/igt_aux.h
@@ -184,10 +184,34 @@ enum igt_suspend_test {
 	SUSPEND_TEST_NUM,
 };
 
+/**
+ * igt_suspend_to_mem_mode:
+ * @SUSPEND_TO_MEM_S2IDLE: suspend to mem maps to @SUSPEND_STATE_FREEZE
+ * @SUSPEND_TO_MEM_SHALLOW: suspend to mem maps to @SUSPEND_STATE_STANDBY
+ * @SUSPEND_TO_MEM_DEEP: suspend to mem will target the ACPI S3 state
+ *
+ * The target system state when suspending to mem:
+ * - Suspending with @SUSPEND_STATE_MEM/@SUSPEND_TO_MEM_S2IDLE is equivalent to
+ *   suspending with @SUSPEND_STATE_FREEZE.
+ * - Suspending with @SUSPEND_STATE_MEM/@SUSPEND_TO_MEM_SHALLOW is equivalent to
+ *   suspending with @SUSPEND_STATE_STANDBY.
+ * - Suspending with @SUSPEND_STATE_MEM/@SUSPEND_TO_MEM_DEEP will target the
+ *   ACPI S3 state.
+ */
+enum igt_suspend_to_mem_mode {
+	SUSPEND_TO_MEM_S2IDLE,
+	SUSPEND_TO_MEM_SHALLOW,
+	SUSPEND_TO_MEM_DEEP,
+
+	/*< private >*/
+	SUSPEND_TO_MEM_NUM,
+};
+
 void igt_system_suspend_autoresume(enum igt_suspend_state state,
 				   enum igt_suspend_test test);
 void igt_set_autoresume_delay(int delay_secs);
 int igt_get_autoresume_delay(enum igt_suspend_state state);
+enum igt_suspend_to_mem_mode igt_get_suspend_to_mem_mode(void);
 
 /* dropping priviledges */
 void igt_drop_root(void);
-- 
2.17.1

_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH i-g-t 7/7] test/i915: Add i915_rc6_ctx_corruption
@ 2019-11-13 15:49   ` Mika Kuoppala
  0 siblings, 0 replies; 44+ messages in thread
From: Mika Kuoppala @ 2019-11-13 15:49 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

From: Imre Deak <imre.deak@intel.com>

Add a test to exercise the kernel's mechanism to detection of RC6
context corruptions, take the necessary action in response (disable
RC6 and runtime PM) and recover when possible (after system
suspend/resume).

v2:
- Skip test on non-existing engines.
- Fix for old kernels where the command parser returned EINVAL
  instead of EACCESS for a banned privilidged command.

Signed-off-by: Imre Deak <imre.deak@intel.com>
---
 tests/Makefile.sources               |   3 +
 tests/i915/i915_rc6_ctx_corruption.c | 196 +++++++++++++++++++++++++++
 tests/meson.build                    |   1 +
 3 files changed, 200 insertions(+)
 create mode 100644 tests/i915/i915_rc6_ctx_corruption.c

diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index 75c79edb..c0f401c7 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -529,6 +529,9 @@ i915_pm_sseu_SOURCES = i915/i915_pm_sseu.c
 TESTS_progs += i915_query
 i915_query_SOURCES = i915/i915_query.c
 
+TESTS_progs += i915_rc6_ctx_corruption
+i915_rc6_ctx_corruption_SOURCES = i915/i915_rc6_ctx_corruption.c
+
 TESTS_progs += i915_selftest
 i915_selftest_SOURCES = i915/i915_selftest.c
 
diff --git a/tests/i915/i915_rc6_ctx_corruption.c b/tests/i915/i915_rc6_ctx_corruption.c
new file mode 100644
index 00000000..a3326307
--- /dev/null
+++ b/tests/i915/i915_rc6_ctx_corruption.c
@@ -0,0 +1,196 @@
+/*
+ * Copyright © 2019 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 <stdint.h>
+
+#include "igt.h"
+#include "igt_gt.h"
+#include "igt_device.h"
+
+#define CTX_INFO_REG			0xA300
+
+static int drm_fd;
+static struct pci_device *pci_dev;
+static uint32_t devid;
+
+static int read_reg_on_engine(const struct intel_execution_engine2 *engine,
+			      int reg_address, uint32_t *reg_val)
+{
+	const bool r64b = intel_gen(devid) >= 8;
+	struct drm_i915_gem_exec_object2 obj[2];
+	struct drm_i915_gem_relocation_entry reloc[1];
+	struct drm_i915_gem_execbuffer2 execbuf;
+	uint32_t *batch;
+	uint32_t *dst_buf;
+	int ret;
+	int i;
+
+	memset(obj, 0, sizeof(obj));
+	obj[0].handle = gem_create(drm_fd, 4096);
+	obj[1].handle = gem_create(drm_fd, 4096);
+	obj[1].relocs_ptr = to_user_pointer(reloc);
+	obj[1].relocation_count = 1;
+
+	batch = gem_mmap__cpu(drm_fd, obj[1].handle, 0, 4096, PROT_WRITE);
+	gem_set_domain(drm_fd, obj[1].handle,
+		       I915_GEM_DOMAIN_CPU, I915_GEM_DOMAIN_CPU);
+
+	i = 0;
+	batch[i++] = 0x24 << 23 | (1 + r64b); /* SRM */
+	batch[i++] = reg_address;
+	reloc[0].target_handle = obj[0].handle;
+	reloc[0].presumed_offset = obj[0].offset;
+	reloc[0].offset = i * sizeof(uint32_t);
+	reloc[0].delta = 0;
+	reloc[0].read_domains = I915_GEM_DOMAIN_RENDER;
+	reloc[0].write_domain = I915_GEM_DOMAIN_RENDER;
+	batch[i++] = reloc[0].delta;
+	if (r64b)
+		batch[i++] = 0;
+
+	batch[i++] = MI_BATCH_BUFFER_END;
+	munmap(batch, 4096);
+
+	memset(&execbuf, 0, sizeof(execbuf));
+	execbuf.buffers_ptr = to_user_pointer(obj);
+	execbuf.buffer_count = 2;
+	execbuf.flags = engine->flags;
+
+	ret = __gem_execbuf(drm_fd, &execbuf);
+	gem_close(drm_fd, obj[1].handle);
+
+	if (ret)
+		goto err;
+
+	if (reg_val) {
+		dst_buf = gem_mmap__cpu(drm_fd, obj[0].handle, 0, 4096,
+					PROT_READ);
+		gem_set_domain(drm_fd, obj[0].handle, I915_GEM_DOMAIN_CPU, 0);
+
+		*reg_val = dst_buf[0];
+		munmap(dst_buf, 4096);
+	}
+
+err:
+	gem_close(drm_fd, obj[0].handle);
+
+	return ret;
+}
+
+static bool need_ctx_corruption_wa(void)
+{
+	return IS_BROADWELL(devid) || intel_gen(devid) == 9;
+}
+
+static void test_read_on_engine(const struct intel_execution_engine2 *engine)
+{
+	int ret;
+
+	gem_require_ring(drm_fd, engine->flags);
+
+	/*
+	 * Recovery is only possible via an S3 suspend/resume or reboot, so
+	 * skip the test if there's no means to recover. Currently this is
+	 * only the case when the selected suspend-to-mem mode is 'deep' (S3).
+	 */
+	igt_require(!need_ctx_corruption_wa() ||
+		    igt_get_suspend_to_mem_mode() == SUSPEND_TO_MEM_DEEP);
+
+	ret = read_reg_on_engine(engine, CTX_INFO_REG, NULL);
+
+	/*
+	 * Do we have the blitter engine parser in place, catching the bad
+	 * SRM?
+	 */
+	if ((ret == -EACCES || ret == -EINVAL) &&
+	    engine->class == I915_ENGINE_CLASS_COPY)
+		return;
+
+	igt_assert_eq(ret, 0);
+
+	/*
+	 * On a plarform not affected by the corruption issue we are done here
+	 * if not yet hung.
+	 */
+	if (!need_ctx_corruption_wa())
+		return;
+
+	/* Let it suspend if it can. */
+	sleep(1);
+
+	/*
+	 * Due to detection of the corruption, runtime suspend should be
+	 * disabled now.
+	 */
+	igt_assert(igt_wait_for_pm_status(IGT_RUNTIME_PM_STATUS_ACTIVE));
+
+	/*
+	 * Let's check now if system suspend/resume recovers from the problem
+	 * as it should.
+	 */
+	igt_system_suspend_autoresume(SUSPEND_STATE_MEM, SUSPEND_TEST_NONE);
+	igt_assert(igt_wait_for_pm_status(IGT_RUNTIME_PM_STATUS_SUSPENDED));
+}
+
+static void setup_environment(void)
+{
+	drmModeRes *res;
+
+	drm_fd = drm_open_driver(DRIVER_INTEL);
+	igt_require_gem(drm_fd);
+	pci_dev = intel_get_pci_device();
+	devid = pci_dev->device_id;
+
+	igt_require(igt_setup_runtime_pm());
+
+	kmstest_set_vt_graphics_mode();
+
+	res = drmModeGetResources(drm_fd);
+	igt_assert(res);
+
+	kmstest_unset_all_crtcs(drm_fd, res);
+
+	igt_require(igt_wait_for_pm_status(IGT_RUNTIME_PM_STATUS_SUSPENDED));
+}
+
+static void cleanup_environment(void)
+{
+	close(drm_fd);
+}
+
+igt_main {
+	const struct intel_execution_engine2 *e;
+
+	igt_fixture {
+		setup_environment();
+	}
+
+	for (e = intel_execution_engines2; e->name; e++) {
+		igt_subtest_f("basic-%s", e->name)
+			test_read_on_engine(e);
+	}
+
+	igt_fixture {
+		cleanup_environment();
+	}
+}
diff --git a/tests/meson.build b/tests/meson.build
index 43899b95..56107695 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -237,6 +237,7 @@ i915_progs = [
 	'i915_pm_dc',
 	'i915_pm_rps',
 	'i915_pm_sseu',
+	'i915_rc6_ctx_corruption',
 	'i915_query',
 	'i915_selftest',
 	'i915_suspend',
-- 
2.17.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [Intel-gfx] [PATCH i-g-t 7/7] test/i915: Add i915_rc6_ctx_corruption
@ 2019-11-13 15:49   ` Mika Kuoppala
  0 siblings, 0 replies; 44+ messages in thread
From: Mika Kuoppala @ 2019-11-13 15:49 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

From: Imre Deak <imre.deak@intel.com>

Add a test to exercise the kernel's mechanism to detection of RC6
context corruptions, take the necessary action in response (disable
RC6 and runtime PM) and recover when possible (after system
suspend/resume).

v2:
- Skip test on non-existing engines.
- Fix for old kernels where the command parser returned EINVAL
  instead of EACCESS for a banned privilidged command.

Signed-off-by: Imre Deak <imre.deak@intel.com>
---
 tests/Makefile.sources               |   3 +
 tests/i915/i915_rc6_ctx_corruption.c | 196 +++++++++++++++++++++++++++
 tests/meson.build                    |   1 +
 3 files changed, 200 insertions(+)
 create mode 100644 tests/i915/i915_rc6_ctx_corruption.c

diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index 75c79edb..c0f401c7 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -529,6 +529,9 @@ i915_pm_sseu_SOURCES = i915/i915_pm_sseu.c
 TESTS_progs += i915_query
 i915_query_SOURCES = i915/i915_query.c
 
+TESTS_progs += i915_rc6_ctx_corruption
+i915_rc6_ctx_corruption_SOURCES = i915/i915_rc6_ctx_corruption.c
+
 TESTS_progs += i915_selftest
 i915_selftest_SOURCES = i915/i915_selftest.c
 
diff --git a/tests/i915/i915_rc6_ctx_corruption.c b/tests/i915/i915_rc6_ctx_corruption.c
new file mode 100644
index 00000000..a3326307
--- /dev/null
+++ b/tests/i915/i915_rc6_ctx_corruption.c
@@ -0,0 +1,196 @@
+/*
+ * Copyright © 2019 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 <stdint.h>
+
+#include "igt.h"
+#include "igt_gt.h"
+#include "igt_device.h"
+
+#define CTX_INFO_REG			0xA300
+
+static int drm_fd;
+static struct pci_device *pci_dev;
+static uint32_t devid;
+
+static int read_reg_on_engine(const struct intel_execution_engine2 *engine,
+			      int reg_address, uint32_t *reg_val)
+{
+	const bool r64b = intel_gen(devid) >= 8;
+	struct drm_i915_gem_exec_object2 obj[2];
+	struct drm_i915_gem_relocation_entry reloc[1];
+	struct drm_i915_gem_execbuffer2 execbuf;
+	uint32_t *batch;
+	uint32_t *dst_buf;
+	int ret;
+	int i;
+
+	memset(obj, 0, sizeof(obj));
+	obj[0].handle = gem_create(drm_fd, 4096);
+	obj[1].handle = gem_create(drm_fd, 4096);
+	obj[1].relocs_ptr = to_user_pointer(reloc);
+	obj[1].relocation_count = 1;
+
+	batch = gem_mmap__cpu(drm_fd, obj[1].handle, 0, 4096, PROT_WRITE);
+	gem_set_domain(drm_fd, obj[1].handle,
+		       I915_GEM_DOMAIN_CPU, I915_GEM_DOMAIN_CPU);
+
+	i = 0;
+	batch[i++] = 0x24 << 23 | (1 + r64b); /* SRM */
+	batch[i++] = reg_address;
+	reloc[0].target_handle = obj[0].handle;
+	reloc[0].presumed_offset = obj[0].offset;
+	reloc[0].offset = i * sizeof(uint32_t);
+	reloc[0].delta = 0;
+	reloc[0].read_domains = I915_GEM_DOMAIN_RENDER;
+	reloc[0].write_domain = I915_GEM_DOMAIN_RENDER;
+	batch[i++] = reloc[0].delta;
+	if (r64b)
+		batch[i++] = 0;
+
+	batch[i++] = MI_BATCH_BUFFER_END;
+	munmap(batch, 4096);
+
+	memset(&execbuf, 0, sizeof(execbuf));
+	execbuf.buffers_ptr = to_user_pointer(obj);
+	execbuf.buffer_count = 2;
+	execbuf.flags = engine->flags;
+
+	ret = __gem_execbuf(drm_fd, &execbuf);
+	gem_close(drm_fd, obj[1].handle);
+
+	if (ret)
+		goto err;
+
+	if (reg_val) {
+		dst_buf = gem_mmap__cpu(drm_fd, obj[0].handle, 0, 4096,
+					PROT_READ);
+		gem_set_domain(drm_fd, obj[0].handle, I915_GEM_DOMAIN_CPU, 0);
+
+		*reg_val = dst_buf[0];
+		munmap(dst_buf, 4096);
+	}
+
+err:
+	gem_close(drm_fd, obj[0].handle);
+
+	return ret;
+}
+
+static bool need_ctx_corruption_wa(void)
+{
+	return IS_BROADWELL(devid) || intel_gen(devid) == 9;
+}
+
+static void test_read_on_engine(const struct intel_execution_engine2 *engine)
+{
+	int ret;
+
+	gem_require_ring(drm_fd, engine->flags);
+
+	/*
+	 * Recovery is only possible via an S3 suspend/resume or reboot, so
+	 * skip the test if there's no means to recover. Currently this is
+	 * only the case when the selected suspend-to-mem mode is 'deep' (S3).
+	 */
+	igt_require(!need_ctx_corruption_wa() ||
+		    igt_get_suspend_to_mem_mode() == SUSPEND_TO_MEM_DEEP);
+
+	ret = read_reg_on_engine(engine, CTX_INFO_REG, NULL);
+
+	/*
+	 * Do we have the blitter engine parser in place, catching the bad
+	 * SRM?
+	 */
+	if ((ret == -EACCES || ret == -EINVAL) &&
+	    engine->class == I915_ENGINE_CLASS_COPY)
+		return;
+
+	igt_assert_eq(ret, 0);
+
+	/*
+	 * On a plarform not affected by the corruption issue we are done here
+	 * if not yet hung.
+	 */
+	if (!need_ctx_corruption_wa())
+		return;
+
+	/* Let it suspend if it can. */
+	sleep(1);
+
+	/*
+	 * Due to detection of the corruption, runtime suspend should be
+	 * disabled now.
+	 */
+	igt_assert(igt_wait_for_pm_status(IGT_RUNTIME_PM_STATUS_ACTIVE));
+
+	/*
+	 * Let's check now if system suspend/resume recovers from the problem
+	 * as it should.
+	 */
+	igt_system_suspend_autoresume(SUSPEND_STATE_MEM, SUSPEND_TEST_NONE);
+	igt_assert(igt_wait_for_pm_status(IGT_RUNTIME_PM_STATUS_SUSPENDED));
+}
+
+static void setup_environment(void)
+{
+	drmModeRes *res;
+
+	drm_fd = drm_open_driver(DRIVER_INTEL);
+	igt_require_gem(drm_fd);
+	pci_dev = intel_get_pci_device();
+	devid = pci_dev->device_id;
+
+	igt_require(igt_setup_runtime_pm());
+
+	kmstest_set_vt_graphics_mode();
+
+	res = drmModeGetResources(drm_fd);
+	igt_assert(res);
+
+	kmstest_unset_all_crtcs(drm_fd, res);
+
+	igt_require(igt_wait_for_pm_status(IGT_RUNTIME_PM_STATUS_SUSPENDED));
+}
+
+static void cleanup_environment(void)
+{
+	close(drm_fd);
+}
+
+igt_main {
+	const struct intel_execution_engine2 *e;
+
+	igt_fixture {
+		setup_environment();
+	}
+
+	for (e = intel_execution_engines2; e->name; e++) {
+		igt_subtest_f("basic-%s", e->name)
+			test_read_on_engine(e);
+	}
+
+	igt_fixture {
+		cleanup_environment();
+	}
+}
diff --git a/tests/meson.build b/tests/meson.build
index 43899b95..56107695 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -237,6 +237,7 @@ i915_progs = [
 	'i915_pm_dc',
 	'i915_pm_rps',
 	'i915_pm_sseu',
+	'i915_rc6_ctx_corruption',
 	'i915_query',
 	'i915_selftest',
 	'i915_suspend',
-- 
2.17.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* Re: [PATCH i-g-t 1/7] lib/igt_dummyload: Send batch as first
@ 2019-11-13 15:57   ` Chris Wilson
  0 siblings, 0 replies; 44+ messages in thread
From: Chris Wilson @ 2019-11-13 15:57 UTC (permalink / raw)
  To: Mika Kuoppala, intel-gfx; +Cc: igt-dev

Quoting Mika Kuoppala (2019-11-13 15:49:07)
> To simplify emitting the recursive batch, make batch
> always the first object on the execbuf list.

Requires v4.13. Useful to leave as a note. Fwiw, looks like 3.16 and
4.14 are still rotting away slowly.
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [Intel-gfx] [PATCH i-g-t 1/7] lib/igt_dummyload: Send batch as first
@ 2019-11-13 15:57   ` Chris Wilson
  0 siblings, 0 replies; 44+ messages in thread
From: Chris Wilson @ 2019-11-13 15:57 UTC (permalink / raw)
  To: Mika Kuoppala, intel-gfx; +Cc: igt-dev

Quoting Mika Kuoppala (2019-11-13 15:49:07)
> To simplify emitting the recursive batch, make batch
> always the first object on the execbuf list.

Requires v4.13. Useful to leave as a note. Fwiw, looks like 3.16 and
4.14 are still rotting away slowly.
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [igt-dev] [PATCH i-g-t 2/7] igt: Use COND_BBEND for busy spinning on gen9
@ 2019-11-13 16:02     ` Chris Wilson
  0 siblings, 0 replies; 44+ messages in thread
From: Chris Wilson @ 2019-11-13 16:02 UTC (permalink / raw)
  To: Mika Kuoppala, intel-gfx; +Cc: igt-dev, Joonas Lahtinen

Quoting Mika Kuoppala (2019-11-13 15:49:08)
> From: Jon Bloomfield <jon.bloomfield@intel.com>
> 
> gen9+ introduces a cmdparser for the BLT engine which copies the
> incoming BB to a kmd owned buffer for submission (to prevent changes
> being made after the bb has been safely scanned). This breaks the
> spin functionality because it relies on changing the submitted spin
> buffers in order to terminate them.
> 
> Instead, for gen9+, we change the semantics by introducing a COND_BB_END
> into the infinite loop, to wait until a memory flag (in anothe bo) is
> cleared.
> 
> v2: Correct nop length to avoid overwriting bb_end instr when using
>     a dependency bo (cork)
> v3: fix conflicts on igt_dummyload (Mika)
> v4: s/bool running/uint32_t running, fix r->delta (Mika)
> v5: remove overzealous assert (Mika)
> v6: rebase on top of lib changes (Mika)
> v7: rework on top of public igt lib changes (Mika)
> v8: rebase
> 
> Signed-off-by: Jon Bloomfield <jon.bloomfield@intel.com> (v2)
> Cc: Joonas Lahtinen <joonas.lahtinen@intel.com>
> Cc: Rodrigo Vivi <rodrigo.vivi@intel.com>
> Cc: Mika Kuoppala <mika.kuoppala@intel.com>
> Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
> ---
>  lib/i830_reg.h                          |  2 +-
>  lib/igt_dummyload.c                     | 46 +++++++++++++++++++++++--
>  lib/igt_dummyload.h                     |  3 ++
>  lib/intel_reg.h                         |  3 ++
>  tests/i915/gem_double_irq_loop.c        |  2 --
>  tests/i915/gem_write_read_ring_switch.c |  2 +-
>  6 files changed, 52 insertions(+), 6 deletions(-)
> 
> diff --git a/lib/i830_reg.h b/lib/i830_reg.h
> index a57691c7..41020256 100644
> --- a/lib/i830_reg.h
> +++ b/lib/i830_reg.h
> @@ -43,7 +43,7 @@
>  /* broadwater flush bits */
>  #define BRW_MI_GLOBAL_SNAPSHOT_RESET   (1 << 3)
>  
> -#define MI_COND_BATCH_BUFFER_END       (0x36<<23 | 1)
> +#define MI_COND_BATCH_BUFFER_END       (0x36 << 23)
>  #define MI_DO_COMPARE                  (1<<21)
>  
>  #define MI_BATCH_BUFFER_END    (0xA << 23)
> diff --git a/lib/igt_dummyload.c b/lib/igt_dummyload.c
> index c079bd04..a88c8582 100644
> --- a/lib/igt_dummyload.c
> +++ b/lib/igt_dummyload.c
> @@ -130,7 +130,15 @@ emit_recursive_batch(igt_spin_t *spin,
>         spin->poll_handle = poll->handle;
>         execbuf->buffer_count++;
>  
> -       if (opts->flags & IGT_SPIN_POLL_RUN) {
> +       /*
> +        * For gen9+ we use a conditional loop rather than an
> +        * infinite loop, because we are unable to modify the
> +        * BB's once they have been scanned by the cmdparser
> +        * We share the poll buffer for the conditional test
> +        * and is always the first buffer in the batch list
> +        */
> +
> +       if (gen >= 9 || (opts->flags & IGT_SPIN_POLL_RUN)) {

Nah, you could just sample the batch buffer rather than always adding
the poll buffer (since the cmdparser implicitly creates the second buffer
for you). Using the comparison value of MI_BATCH_BUFFER_END you wouldn't
even have to worry about altering callers.
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [Intel-gfx] [igt-dev] [PATCH i-g-t 2/7] igt: Use COND_BBEND for busy spinning on gen9
@ 2019-11-13 16:02     ` Chris Wilson
  0 siblings, 0 replies; 44+ messages in thread
From: Chris Wilson @ 2019-11-13 16:02 UTC (permalink / raw)
  To: Mika Kuoppala, intel-gfx; +Cc: igt-dev, Joonas Lahtinen

Quoting Mika Kuoppala (2019-11-13 15:49:08)
> From: Jon Bloomfield <jon.bloomfield@intel.com>
> 
> gen9+ introduces a cmdparser for the BLT engine which copies the
> incoming BB to a kmd owned buffer for submission (to prevent changes
> being made after the bb has been safely scanned). This breaks the
> spin functionality because it relies on changing the submitted spin
> buffers in order to terminate them.
> 
> Instead, for gen9+, we change the semantics by introducing a COND_BB_END
> into the infinite loop, to wait until a memory flag (in anothe bo) is
> cleared.
> 
> v2: Correct nop length to avoid overwriting bb_end instr when using
>     a dependency bo (cork)
> v3: fix conflicts on igt_dummyload (Mika)
> v4: s/bool running/uint32_t running, fix r->delta (Mika)
> v5: remove overzealous assert (Mika)
> v6: rebase on top of lib changes (Mika)
> v7: rework on top of public igt lib changes (Mika)
> v8: rebase
> 
> Signed-off-by: Jon Bloomfield <jon.bloomfield@intel.com> (v2)
> Cc: Joonas Lahtinen <joonas.lahtinen@intel.com>
> Cc: Rodrigo Vivi <rodrigo.vivi@intel.com>
> Cc: Mika Kuoppala <mika.kuoppala@intel.com>
> Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
> ---
>  lib/i830_reg.h                          |  2 +-
>  lib/igt_dummyload.c                     | 46 +++++++++++++++++++++++--
>  lib/igt_dummyload.h                     |  3 ++
>  lib/intel_reg.h                         |  3 ++
>  tests/i915/gem_double_irq_loop.c        |  2 --
>  tests/i915/gem_write_read_ring_switch.c |  2 +-
>  6 files changed, 52 insertions(+), 6 deletions(-)
> 
> diff --git a/lib/i830_reg.h b/lib/i830_reg.h
> index a57691c7..41020256 100644
> --- a/lib/i830_reg.h
> +++ b/lib/i830_reg.h
> @@ -43,7 +43,7 @@
>  /* broadwater flush bits */
>  #define BRW_MI_GLOBAL_SNAPSHOT_RESET   (1 << 3)
>  
> -#define MI_COND_BATCH_BUFFER_END       (0x36<<23 | 1)
> +#define MI_COND_BATCH_BUFFER_END       (0x36 << 23)
>  #define MI_DO_COMPARE                  (1<<21)
>  
>  #define MI_BATCH_BUFFER_END    (0xA << 23)
> diff --git a/lib/igt_dummyload.c b/lib/igt_dummyload.c
> index c079bd04..a88c8582 100644
> --- a/lib/igt_dummyload.c
> +++ b/lib/igt_dummyload.c
> @@ -130,7 +130,15 @@ emit_recursive_batch(igt_spin_t *spin,
>         spin->poll_handle = poll->handle;
>         execbuf->buffer_count++;
>  
> -       if (opts->flags & IGT_SPIN_POLL_RUN) {
> +       /*
> +        * For gen9+ we use a conditional loop rather than an
> +        * infinite loop, because we are unable to modify the
> +        * BB's once they have been scanned by the cmdparser
> +        * We share the poll buffer for the conditional test
> +        * and is always the first buffer in the batch list
> +        */
> +
> +       if (gen >= 9 || (opts->flags & IGT_SPIN_POLL_RUN)) {

Nah, you could just sample the batch buffer rather than always adding
the poll buffer (since the cmdparser implicitly creates the second buffer
for you). Using the comparison value of MI_BATCH_BUFFER_END you wouldn't
even have to worry about altering callers.
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [igt-dev] [PATCH i-g-t 5/7] Add tests/gem_blt_parse
@ 2019-11-13 16:14     ` Chris Wilson
  0 siblings, 0 replies; 44+ messages in thread
From: Chris Wilson @ 2019-11-13 16:14 UTC (permalink / raw)
  To: Mika Kuoppala, intel-gfx; +Cc: igt-dev

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, &regs[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
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [Intel-gfx] [igt-dev] [PATCH i-g-t 5/7] Add tests/gem_blt_parse
@ 2019-11-13 16:14     ` Chris Wilson
  0 siblings, 0 replies; 44+ messages in thread
From: Chris Wilson @ 2019-11-13 16:14 UTC (permalink / raw)
  To: Mika Kuoppala, intel-gfx; +Cc: igt-dev

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, &regs[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
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [igt-dev] [PATCH i-g-t 5/7] Add tests/gem_blt_parse
@ 2019-11-13 16:14     ` Chris Wilson
  0 siblings, 0 replies; 44+ messages in thread
From: Chris Wilson @ 2019-11-13 16:14 UTC (permalink / raw)
  To: Mika Kuoppala, intel-gfx; +Cc: igt-dev

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, &regs[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
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH i-g-t 7/7] test/i915: Add i915_rc6_ctx_corruption
@ 2019-11-13 16:18     ` Chris Wilson
  0 siblings, 0 replies; 44+ messages in thread
From: Chris Wilson @ 2019-11-13 16:18 UTC (permalink / raw)
  To: Mika Kuoppala, intel-gfx; +Cc: igt-dev

Quoting Mika Kuoppala (2019-11-13 15:49:13)
> From: Imre Deak <imre.deak@intel.com>
> 
> Add a test to exercise the kernel's mechanism to detection of RC6
> context corruptions, take the necessary action in response (disable
> RC6 and runtime PM) and recover when possible (after system
> suspend/resume).
> 
> v2:
> - Skip test on non-existing engines.
> - Fix for old kernels where the command parser returned EINVAL
>   instead of EACCESS for a banned privilidged command.
> 
> Signed-off-by: Imre Deak <imre.deak@intel.com>
> ---
>  tests/Makefile.sources               |   3 +
>  tests/i915/i915_rc6_ctx_corruption.c | 196 +++++++++++++++++++++++++++
>  tests/meson.build                    |   1 +
>  3 files changed, 200 insertions(+)
>  create mode 100644 tests/i915/i915_rc6_ctx_corruption.c
> 
> diff --git a/tests/Makefile.sources b/tests/Makefile.sources
> index 75c79edb..c0f401c7 100644
> --- a/tests/Makefile.sources
> +++ b/tests/Makefile.sources
> @@ -529,6 +529,9 @@ i915_pm_sseu_SOURCES = i915/i915_pm_sseu.c
>  TESTS_progs += i915_query
>  i915_query_SOURCES = i915/i915_query.c
>  
> +TESTS_progs += i915_rc6_ctx_corruption
> +i915_rc6_ctx_corruption_SOURCES = i915/i915_rc6_ctx_corruption.c
> +
>  TESTS_progs += i915_selftest
>  i915_selftest_SOURCES = i915/i915_selftest.c
>  
> diff --git a/tests/i915/i915_rc6_ctx_corruption.c b/tests/i915/i915_rc6_ctx_corruption.c
> new file mode 100644
> index 00000000..a3326307
> --- /dev/null
> +++ b/tests/i915/i915_rc6_ctx_corruption.c
> @@ -0,0 +1,196 @@
> +/*
> + * Copyright © 2019 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 <stdint.h>
> +
> +#include "igt.h"
> +#include "igt_gt.h"
> +#include "igt_device.h"
> +
> +#define CTX_INFO_REG                   0xA300
> +
> +static int drm_fd;
> +static struct pci_device *pci_dev;
> +static uint32_t devid;
> +
> +static int read_reg_on_engine(const struct intel_execution_engine2 *engine,
> +                             int reg_address, uint32_t *reg_val)
> +{
> +       const bool r64b = intel_gen(devid) >= 8;
> +       struct drm_i915_gem_exec_object2 obj[2];
> +       struct drm_i915_gem_relocation_entry reloc[1];
> +       struct drm_i915_gem_execbuffer2 execbuf;
> +       uint32_t *batch;
> +       uint32_t *dst_buf;
> +       int ret;
> +       int i;
> +
> +       memset(obj, 0, sizeof(obj));
> +       obj[0].handle = gem_create(drm_fd, 4096);
> +       obj[1].handle = gem_create(drm_fd, 4096);
> +       obj[1].relocs_ptr = to_user_pointer(reloc);
> +       obj[1].relocation_count = 1;
> +
> +       batch = gem_mmap__cpu(drm_fd, obj[1].handle, 0, 4096, PROT_WRITE);
> +       gem_set_domain(drm_fd, obj[1].handle,
> +                      I915_GEM_DOMAIN_CPU, I915_GEM_DOMAIN_CPU);
> +
> +       i = 0;
> +       batch[i++] = 0x24 << 23 | (1 + r64b); /* SRM */
> +       batch[i++] = reg_address;
> +       reloc[0].target_handle = obj[0].handle;
> +       reloc[0].presumed_offset = obj[0].offset;
> +       reloc[0].offset = i * sizeof(uint32_t);
> +       reloc[0].delta = 0;
> +       reloc[0].read_domains = I915_GEM_DOMAIN_RENDER;
> +       reloc[0].write_domain = I915_GEM_DOMAIN_RENDER;
> +       batch[i++] = reloc[0].delta;
> +       if (r64b)
> +               batch[i++] = 0;
> +
> +       batch[i++] = MI_BATCH_BUFFER_END;
> +       munmap(batch, 4096);
> +
> +       memset(&execbuf, 0, sizeof(execbuf));
> +       execbuf.buffers_ptr = to_user_pointer(obj);
> +       execbuf.buffer_count = 2;
> +       execbuf.flags = engine->flags;
> +
> +       ret = __gem_execbuf(drm_fd, &execbuf);
> +       gem_close(drm_fd, obj[1].handle);
> +
> +       if (ret)
> +               goto err;
> +
> +       if (reg_val) {
> +               dst_buf = gem_mmap__cpu(drm_fd, obj[0].handle, 0, 4096,
> +                                       PROT_READ);
> +               gem_set_domain(drm_fd, obj[0].handle, I915_GEM_DOMAIN_CPU, 0);
> +
> +               *reg_val = dst_buf[0];
> +               munmap(dst_buf, 4096);
> +       }
> +
> +err:
> +       gem_close(drm_fd, obj[0].handle);
> +
> +       return ret;
> +}
> +
> +static bool need_ctx_corruption_wa(void)
> +{
> +       return IS_BROADWELL(devid) || intel_gen(devid) == 9;

That's a little self servicing and doesn't help find bugs where you
weren't expecting. The test would seem to be useless at predicting new
failures.

This should just be a selftest imo.
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [Intel-gfx] [PATCH i-g-t 7/7] test/i915: Add i915_rc6_ctx_corruption
@ 2019-11-13 16:18     ` Chris Wilson
  0 siblings, 0 replies; 44+ messages in thread
From: Chris Wilson @ 2019-11-13 16:18 UTC (permalink / raw)
  To: Mika Kuoppala, intel-gfx; +Cc: igt-dev

Quoting Mika Kuoppala (2019-11-13 15:49:13)
> From: Imre Deak <imre.deak@intel.com>
> 
> Add a test to exercise the kernel's mechanism to detection of RC6
> context corruptions, take the necessary action in response (disable
> RC6 and runtime PM) and recover when possible (after system
> suspend/resume).
> 
> v2:
> - Skip test on non-existing engines.
> - Fix for old kernels where the command parser returned EINVAL
>   instead of EACCESS for a banned privilidged command.
> 
> Signed-off-by: Imre Deak <imre.deak@intel.com>
> ---
>  tests/Makefile.sources               |   3 +
>  tests/i915/i915_rc6_ctx_corruption.c | 196 +++++++++++++++++++++++++++
>  tests/meson.build                    |   1 +
>  3 files changed, 200 insertions(+)
>  create mode 100644 tests/i915/i915_rc6_ctx_corruption.c
> 
> diff --git a/tests/Makefile.sources b/tests/Makefile.sources
> index 75c79edb..c0f401c7 100644
> --- a/tests/Makefile.sources
> +++ b/tests/Makefile.sources
> @@ -529,6 +529,9 @@ i915_pm_sseu_SOURCES = i915/i915_pm_sseu.c
>  TESTS_progs += i915_query
>  i915_query_SOURCES = i915/i915_query.c
>  
> +TESTS_progs += i915_rc6_ctx_corruption
> +i915_rc6_ctx_corruption_SOURCES = i915/i915_rc6_ctx_corruption.c
> +
>  TESTS_progs += i915_selftest
>  i915_selftest_SOURCES = i915/i915_selftest.c
>  
> diff --git a/tests/i915/i915_rc6_ctx_corruption.c b/tests/i915/i915_rc6_ctx_corruption.c
> new file mode 100644
> index 00000000..a3326307
> --- /dev/null
> +++ b/tests/i915/i915_rc6_ctx_corruption.c
> @@ -0,0 +1,196 @@
> +/*
> + * Copyright © 2019 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 <stdint.h>
> +
> +#include "igt.h"
> +#include "igt_gt.h"
> +#include "igt_device.h"
> +
> +#define CTX_INFO_REG                   0xA300
> +
> +static int drm_fd;
> +static struct pci_device *pci_dev;
> +static uint32_t devid;
> +
> +static int read_reg_on_engine(const struct intel_execution_engine2 *engine,
> +                             int reg_address, uint32_t *reg_val)
> +{
> +       const bool r64b = intel_gen(devid) >= 8;
> +       struct drm_i915_gem_exec_object2 obj[2];
> +       struct drm_i915_gem_relocation_entry reloc[1];
> +       struct drm_i915_gem_execbuffer2 execbuf;
> +       uint32_t *batch;
> +       uint32_t *dst_buf;
> +       int ret;
> +       int i;
> +
> +       memset(obj, 0, sizeof(obj));
> +       obj[0].handle = gem_create(drm_fd, 4096);
> +       obj[1].handle = gem_create(drm_fd, 4096);
> +       obj[1].relocs_ptr = to_user_pointer(reloc);
> +       obj[1].relocation_count = 1;
> +
> +       batch = gem_mmap__cpu(drm_fd, obj[1].handle, 0, 4096, PROT_WRITE);
> +       gem_set_domain(drm_fd, obj[1].handle,
> +                      I915_GEM_DOMAIN_CPU, I915_GEM_DOMAIN_CPU);
> +
> +       i = 0;
> +       batch[i++] = 0x24 << 23 | (1 + r64b); /* SRM */
> +       batch[i++] = reg_address;
> +       reloc[0].target_handle = obj[0].handle;
> +       reloc[0].presumed_offset = obj[0].offset;
> +       reloc[0].offset = i * sizeof(uint32_t);
> +       reloc[0].delta = 0;
> +       reloc[0].read_domains = I915_GEM_DOMAIN_RENDER;
> +       reloc[0].write_domain = I915_GEM_DOMAIN_RENDER;
> +       batch[i++] = reloc[0].delta;
> +       if (r64b)
> +               batch[i++] = 0;
> +
> +       batch[i++] = MI_BATCH_BUFFER_END;
> +       munmap(batch, 4096);
> +
> +       memset(&execbuf, 0, sizeof(execbuf));
> +       execbuf.buffers_ptr = to_user_pointer(obj);
> +       execbuf.buffer_count = 2;
> +       execbuf.flags = engine->flags;
> +
> +       ret = __gem_execbuf(drm_fd, &execbuf);
> +       gem_close(drm_fd, obj[1].handle);
> +
> +       if (ret)
> +               goto err;
> +
> +       if (reg_val) {
> +               dst_buf = gem_mmap__cpu(drm_fd, obj[0].handle, 0, 4096,
> +                                       PROT_READ);
> +               gem_set_domain(drm_fd, obj[0].handle, I915_GEM_DOMAIN_CPU, 0);
> +
> +               *reg_val = dst_buf[0];
> +               munmap(dst_buf, 4096);
> +       }
> +
> +err:
> +       gem_close(drm_fd, obj[0].handle);
> +
> +       return ret;
> +}
> +
> +static bool need_ctx_corruption_wa(void)
> +{
> +       return IS_BROADWELL(devid) || intel_gen(devid) == 9;

That's a little self servicing and doesn't help find bugs where you
weren't expecting. The test would seem to be useless at predicting new
failures.

This should just be a selftest imo.
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [igt-dev] [Intel-gfx] [PATCH i-g-t 7/7] test/i915: Add i915_rc6_ctx_corruption
@ 2019-11-13 16:18     ` Chris Wilson
  0 siblings, 0 replies; 44+ messages in thread
From: Chris Wilson @ 2019-11-13 16:18 UTC (permalink / raw)
  To: Mika Kuoppala, intel-gfx; +Cc: igt-dev

Quoting Mika Kuoppala (2019-11-13 15:49:13)
> From: Imre Deak <imre.deak@intel.com>
> 
> Add a test to exercise the kernel's mechanism to detection of RC6
> context corruptions, take the necessary action in response (disable
> RC6 and runtime PM) and recover when possible (after system
> suspend/resume).
> 
> v2:
> - Skip test on non-existing engines.
> - Fix for old kernels where the command parser returned EINVAL
>   instead of EACCESS for a banned privilidged command.
> 
> Signed-off-by: Imre Deak <imre.deak@intel.com>
> ---
>  tests/Makefile.sources               |   3 +
>  tests/i915/i915_rc6_ctx_corruption.c | 196 +++++++++++++++++++++++++++
>  tests/meson.build                    |   1 +
>  3 files changed, 200 insertions(+)
>  create mode 100644 tests/i915/i915_rc6_ctx_corruption.c
> 
> diff --git a/tests/Makefile.sources b/tests/Makefile.sources
> index 75c79edb..c0f401c7 100644
> --- a/tests/Makefile.sources
> +++ b/tests/Makefile.sources
> @@ -529,6 +529,9 @@ i915_pm_sseu_SOURCES = i915/i915_pm_sseu.c
>  TESTS_progs += i915_query
>  i915_query_SOURCES = i915/i915_query.c
>  
> +TESTS_progs += i915_rc6_ctx_corruption
> +i915_rc6_ctx_corruption_SOURCES = i915/i915_rc6_ctx_corruption.c
> +
>  TESTS_progs += i915_selftest
>  i915_selftest_SOURCES = i915/i915_selftest.c
>  
> diff --git a/tests/i915/i915_rc6_ctx_corruption.c b/tests/i915/i915_rc6_ctx_corruption.c
> new file mode 100644
> index 00000000..a3326307
> --- /dev/null
> +++ b/tests/i915/i915_rc6_ctx_corruption.c
> @@ -0,0 +1,196 @@
> +/*
> + * Copyright © 2019 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 <stdint.h>
> +
> +#include "igt.h"
> +#include "igt_gt.h"
> +#include "igt_device.h"
> +
> +#define CTX_INFO_REG                   0xA300
> +
> +static int drm_fd;
> +static struct pci_device *pci_dev;
> +static uint32_t devid;
> +
> +static int read_reg_on_engine(const struct intel_execution_engine2 *engine,
> +                             int reg_address, uint32_t *reg_val)
> +{
> +       const bool r64b = intel_gen(devid) >= 8;
> +       struct drm_i915_gem_exec_object2 obj[2];
> +       struct drm_i915_gem_relocation_entry reloc[1];
> +       struct drm_i915_gem_execbuffer2 execbuf;
> +       uint32_t *batch;
> +       uint32_t *dst_buf;
> +       int ret;
> +       int i;
> +
> +       memset(obj, 0, sizeof(obj));
> +       obj[0].handle = gem_create(drm_fd, 4096);
> +       obj[1].handle = gem_create(drm_fd, 4096);
> +       obj[1].relocs_ptr = to_user_pointer(reloc);
> +       obj[1].relocation_count = 1;
> +
> +       batch = gem_mmap__cpu(drm_fd, obj[1].handle, 0, 4096, PROT_WRITE);
> +       gem_set_domain(drm_fd, obj[1].handle,
> +                      I915_GEM_DOMAIN_CPU, I915_GEM_DOMAIN_CPU);
> +
> +       i = 0;
> +       batch[i++] = 0x24 << 23 | (1 + r64b); /* SRM */
> +       batch[i++] = reg_address;
> +       reloc[0].target_handle = obj[0].handle;
> +       reloc[0].presumed_offset = obj[0].offset;
> +       reloc[0].offset = i * sizeof(uint32_t);
> +       reloc[0].delta = 0;
> +       reloc[0].read_domains = I915_GEM_DOMAIN_RENDER;
> +       reloc[0].write_domain = I915_GEM_DOMAIN_RENDER;
> +       batch[i++] = reloc[0].delta;
> +       if (r64b)
> +               batch[i++] = 0;
> +
> +       batch[i++] = MI_BATCH_BUFFER_END;
> +       munmap(batch, 4096);
> +
> +       memset(&execbuf, 0, sizeof(execbuf));
> +       execbuf.buffers_ptr = to_user_pointer(obj);
> +       execbuf.buffer_count = 2;
> +       execbuf.flags = engine->flags;
> +
> +       ret = __gem_execbuf(drm_fd, &execbuf);
> +       gem_close(drm_fd, obj[1].handle);
> +
> +       if (ret)
> +               goto err;
> +
> +       if (reg_val) {
> +               dst_buf = gem_mmap__cpu(drm_fd, obj[0].handle, 0, 4096,
> +                                       PROT_READ);
> +               gem_set_domain(drm_fd, obj[0].handle, I915_GEM_DOMAIN_CPU, 0);
> +
> +               *reg_val = dst_buf[0];
> +               munmap(dst_buf, 4096);
> +       }
> +
> +err:
> +       gem_close(drm_fd, obj[0].handle);
> +
> +       return ret;
> +}
> +
> +static bool need_ctx_corruption_wa(void)
> +{
> +       return IS_BROADWELL(devid) || intel_gen(devid) == 9;

That's a little self servicing and doesn't help find bugs where you
weren't expecting. The test would seem to be useless at predicting new
failures.

This should just be a selftest imo.
-Chris
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [igt-dev] [PATCH i-g-t 5/7] Add tests/gem_blt_parse
@ 2019-11-13 16:37       ` Mika Kuoppala
  0 siblings, 0 replies; 44+ messages in thread
From: Mika Kuoppala @ 2019-11-13 16:37 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx; +Cc: igt-dev

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, &regs[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
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [Intel-gfx] [igt-dev] [PATCH i-g-t 5/7] Add tests/gem_blt_parse
@ 2019-11-13 16:37       ` Mika Kuoppala
  0 siblings, 0 replies; 44+ messages in thread
From: Mika Kuoppala @ 2019-11-13 16:37 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx; +Cc: igt-dev

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, &regs[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
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [igt-dev] [PATCH i-g-t 5/7] Add tests/gem_blt_parse
@ 2019-11-13 16:37       ` Mika Kuoppala
  0 siblings, 0 replies; 44+ messages in thread
From: Mika Kuoppala @ 2019-11-13 16:37 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx; +Cc: igt-dev

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, &regs[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
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [igt-dev] [PATCH i-g-t 2/7] igt: Use COND_BBEND for busy spinning on gen9
@ 2019-11-13 17:19       ` Mika Kuoppala
  0 siblings, 0 replies; 44+ messages in thread
From: Mika Kuoppala @ 2019-11-13 17:19 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx; +Cc: igt-dev, Joonas Lahtinen

Chris Wilson <chris@chris-wilson.co.uk> writes:

> Quoting Mika Kuoppala (2019-11-13 15:49:08)
>> From: Jon Bloomfield <jon.bloomfield@intel.com>
>> 
>> gen9+ introduces a cmdparser for the BLT engine which copies the
>> incoming BB to a kmd owned buffer for submission (to prevent changes
>> being made after the bb has been safely scanned). This breaks the
>> spin functionality because it relies on changing the submitted spin
>> buffers in order to terminate them.
>> 
>> Instead, for gen9+, we change the semantics by introducing a COND_BB_END
>> into the infinite loop, to wait until a memory flag (in anothe bo) is
>> cleared.
>> 
>> v2: Correct nop length to avoid overwriting bb_end instr when using
>>     a dependency bo (cork)
>> v3: fix conflicts on igt_dummyload (Mika)
>> v4: s/bool running/uint32_t running, fix r->delta (Mika)
>> v5: remove overzealous assert (Mika)
>> v6: rebase on top of lib changes (Mika)
>> v7: rework on top of public igt lib changes (Mika)
>> v8: rebase
>> 
>> Signed-off-by: Jon Bloomfield <jon.bloomfield@intel.com> (v2)
>> Cc: Joonas Lahtinen <joonas.lahtinen@intel.com>
>> Cc: Rodrigo Vivi <rodrigo.vivi@intel.com>
>> Cc: Mika Kuoppala <mika.kuoppala@intel.com>
>> Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
>> ---
>>  lib/i830_reg.h                          |  2 +-
>>  lib/igt_dummyload.c                     | 46 +++++++++++++++++++++++--
>>  lib/igt_dummyload.h                     |  3 ++
>>  lib/intel_reg.h                         |  3 ++
>>  tests/i915/gem_double_irq_loop.c        |  2 --
>>  tests/i915/gem_write_read_ring_switch.c |  2 +-
>>  6 files changed, 52 insertions(+), 6 deletions(-)
>> 
>> diff --git a/lib/i830_reg.h b/lib/i830_reg.h
>> index a57691c7..41020256 100644
>> --- a/lib/i830_reg.h
>> +++ b/lib/i830_reg.h
>> @@ -43,7 +43,7 @@
>>  /* broadwater flush bits */
>>  #define BRW_MI_GLOBAL_SNAPSHOT_RESET   (1 << 3)
>>  
>> -#define MI_COND_BATCH_BUFFER_END       (0x36<<23 | 1)
>> +#define MI_COND_BATCH_BUFFER_END       (0x36 << 23)
>>  #define MI_DO_COMPARE                  (1<<21)
>>  
>>  #define MI_BATCH_BUFFER_END    (0xA << 23)
>> diff --git a/lib/igt_dummyload.c b/lib/igt_dummyload.c
>> index c079bd04..a88c8582 100644
>> --- a/lib/igt_dummyload.c
>> +++ b/lib/igt_dummyload.c
>> @@ -130,7 +130,15 @@ emit_recursive_batch(igt_spin_t *spin,
>>         spin->poll_handle = poll->handle;
>>         execbuf->buffer_count++;
>>  
>> -       if (opts->flags & IGT_SPIN_POLL_RUN) {
>> +       /*
>> +        * For gen9+ we use a conditional loop rather than an
>> +        * infinite loop, because we are unable to modify the
>> +        * BB's once they have been scanned by the cmdparser
>> +        * We share the poll buffer for the conditional test
>> +        * and is always the first buffer in the batch list
>> +        */
>> +
>> +       if (gen >= 9 || (opts->flags & IGT_SPIN_POLL_RUN)) {
>
> Nah, you could just sample the batch buffer rather than always adding
> the poll buffer (since the cmdparser implicitly creates the second buffer
> for you). Using the comparison value of MI_BATCH_BUFFER_END you wouldn't
> even have to worry about altering callers.

That is a neat trick! bb start is greater than bb end so
we have that opportunity. Might lead also to much less fixing
on mutables.

-Mika
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [Intel-gfx] [igt-dev] [PATCH i-g-t 2/7] igt: Use COND_BBEND for busy spinning on gen9
@ 2019-11-13 17:19       ` Mika Kuoppala
  0 siblings, 0 replies; 44+ messages in thread
From: Mika Kuoppala @ 2019-11-13 17:19 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx; +Cc: igt-dev, Joonas Lahtinen

Chris Wilson <chris@chris-wilson.co.uk> writes:

> Quoting Mika Kuoppala (2019-11-13 15:49:08)
>> From: Jon Bloomfield <jon.bloomfield@intel.com>
>> 
>> gen9+ introduces a cmdparser for the BLT engine which copies the
>> incoming BB to a kmd owned buffer for submission (to prevent changes
>> being made after the bb has been safely scanned). This breaks the
>> spin functionality because it relies on changing the submitted spin
>> buffers in order to terminate them.
>> 
>> Instead, for gen9+, we change the semantics by introducing a COND_BB_END
>> into the infinite loop, to wait until a memory flag (in anothe bo) is
>> cleared.
>> 
>> v2: Correct nop length to avoid overwriting bb_end instr when using
>>     a dependency bo (cork)
>> v3: fix conflicts on igt_dummyload (Mika)
>> v4: s/bool running/uint32_t running, fix r->delta (Mika)
>> v5: remove overzealous assert (Mika)
>> v6: rebase on top of lib changes (Mika)
>> v7: rework on top of public igt lib changes (Mika)
>> v8: rebase
>> 
>> Signed-off-by: Jon Bloomfield <jon.bloomfield@intel.com> (v2)
>> Cc: Joonas Lahtinen <joonas.lahtinen@intel.com>
>> Cc: Rodrigo Vivi <rodrigo.vivi@intel.com>
>> Cc: Mika Kuoppala <mika.kuoppala@intel.com>
>> Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
>> ---
>>  lib/i830_reg.h                          |  2 +-
>>  lib/igt_dummyload.c                     | 46 +++++++++++++++++++++++--
>>  lib/igt_dummyload.h                     |  3 ++
>>  lib/intel_reg.h                         |  3 ++
>>  tests/i915/gem_double_irq_loop.c        |  2 --
>>  tests/i915/gem_write_read_ring_switch.c |  2 +-
>>  6 files changed, 52 insertions(+), 6 deletions(-)
>> 
>> diff --git a/lib/i830_reg.h b/lib/i830_reg.h
>> index a57691c7..41020256 100644
>> --- a/lib/i830_reg.h
>> +++ b/lib/i830_reg.h
>> @@ -43,7 +43,7 @@
>>  /* broadwater flush bits */
>>  #define BRW_MI_GLOBAL_SNAPSHOT_RESET   (1 << 3)
>>  
>> -#define MI_COND_BATCH_BUFFER_END       (0x36<<23 | 1)
>> +#define MI_COND_BATCH_BUFFER_END       (0x36 << 23)
>>  #define MI_DO_COMPARE                  (1<<21)
>>  
>>  #define MI_BATCH_BUFFER_END    (0xA << 23)
>> diff --git a/lib/igt_dummyload.c b/lib/igt_dummyload.c
>> index c079bd04..a88c8582 100644
>> --- a/lib/igt_dummyload.c
>> +++ b/lib/igt_dummyload.c
>> @@ -130,7 +130,15 @@ emit_recursive_batch(igt_spin_t *spin,
>>         spin->poll_handle = poll->handle;
>>         execbuf->buffer_count++;
>>  
>> -       if (opts->flags & IGT_SPIN_POLL_RUN) {
>> +       /*
>> +        * For gen9+ we use a conditional loop rather than an
>> +        * infinite loop, because we are unable to modify the
>> +        * BB's once they have been scanned by the cmdparser
>> +        * We share the poll buffer for the conditional test
>> +        * and is always the first buffer in the batch list
>> +        */
>> +
>> +       if (gen >= 9 || (opts->flags & IGT_SPIN_POLL_RUN)) {
>
> Nah, you could just sample the batch buffer rather than always adding
> the poll buffer (since the cmdparser implicitly creates the second buffer
> for you). Using the comparison value of MI_BATCH_BUFFER_END you wouldn't
> even have to worry about altering callers.

That is a neat trick! bb start is greater than bb end so
we have that opportunity. Might lead also to much less fixing
on mutables.

-Mika
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [igt-dev] [PATCH i-g-t 2/7] igt: Use COND_BBEND for busy spinning on gen9
@ 2019-11-13 17:19       ` Mika Kuoppala
  0 siblings, 0 replies; 44+ messages in thread
From: Mika Kuoppala @ 2019-11-13 17:19 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx
  Cc: igt-dev, Joonas Lahtinen, Jon Bloomfield, Rodrigo Vivi

Chris Wilson <chris@chris-wilson.co.uk> writes:

> Quoting Mika Kuoppala (2019-11-13 15:49:08)
>> From: Jon Bloomfield <jon.bloomfield@intel.com>
>> 
>> gen9+ introduces a cmdparser for the BLT engine which copies the
>> incoming BB to a kmd owned buffer for submission (to prevent changes
>> being made after the bb has been safely scanned). This breaks the
>> spin functionality because it relies on changing the submitted spin
>> buffers in order to terminate them.
>> 
>> Instead, for gen9+, we change the semantics by introducing a COND_BB_END
>> into the infinite loop, to wait until a memory flag (in anothe bo) is
>> cleared.
>> 
>> v2: Correct nop length to avoid overwriting bb_end instr when using
>>     a dependency bo (cork)
>> v3: fix conflicts on igt_dummyload (Mika)
>> v4: s/bool running/uint32_t running, fix r->delta (Mika)
>> v5: remove overzealous assert (Mika)
>> v6: rebase on top of lib changes (Mika)
>> v7: rework on top of public igt lib changes (Mika)
>> v8: rebase
>> 
>> Signed-off-by: Jon Bloomfield <jon.bloomfield@intel.com> (v2)
>> Cc: Joonas Lahtinen <joonas.lahtinen@intel.com>
>> Cc: Rodrigo Vivi <rodrigo.vivi@intel.com>
>> Cc: Mika Kuoppala <mika.kuoppala@intel.com>
>> Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
>> ---
>>  lib/i830_reg.h                          |  2 +-
>>  lib/igt_dummyload.c                     | 46 +++++++++++++++++++++++--
>>  lib/igt_dummyload.h                     |  3 ++
>>  lib/intel_reg.h                         |  3 ++
>>  tests/i915/gem_double_irq_loop.c        |  2 --
>>  tests/i915/gem_write_read_ring_switch.c |  2 +-
>>  6 files changed, 52 insertions(+), 6 deletions(-)
>> 
>> diff --git a/lib/i830_reg.h b/lib/i830_reg.h
>> index a57691c7..41020256 100644
>> --- a/lib/i830_reg.h
>> +++ b/lib/i830_reg.h
>> @@ -43,7 +43,7 @@
>>  /* broadwater flush bits */
>>  #define BRW_MI_GLOBAL_SNAPSHOT_RESET   (1 << 3)
>>  
>> -#define MI_COND_BATCH_BUFFER_END       (0x36<<23 | 1)
>> +#define MI_COND_BATCH_BUFFER_END       (0x36 << 23)
>>  #define MI_DO_COMPARE                  (1<<21)
>>  
>>  #define MI_BATCH_BUFFER_END    (0xA << 23)
>> diff --git a/lib/igt_dummyload.c b/lib/igt_dummyload.c
>> index c079bd04..a88c8582 100644
>> --- a/lib/igt_dummyload.c
>> +++ b/lib/igt_dummyload.c
>> @@ -130,7 +130,15 @@ emit_recursive_batch(igt_spin_t *spin,
>>         spin->poll_handle = poll->handle;
>>         execbuf->buffer_count++;
>>  
>> -       if (opts->flags & IGT_SPIN_POLL_RUN) {
>> +       /*
>> +        * For gen9+ we use a conditional loop rather than an
>> +        * infinite loop, because we are unable to modify the
>> +        * BB's once they have been scanned by the cmdparser
>> +        * We share the poll buffer for the conditional test
>> +        * and is always the first buffer in the batch list
>> +        */
>> +
>> +       if (gen >= 9 || (opts->flags & IGT_SPIN_POLL_RUN)) {
>
> Nah, you could just sample the batch buffer rather than always adding
> the poll buffer (since the cmdparser implicitly creates the second buffer
> for you). Using the comparison value of MI_BATCH_BUFFER_END you wouldn't
> even have to worry about altering callers.

That is a neat trick! bb start is greater than bb end so
we have that opportunity. Might lead also to much less fixing
on mutables.

-Mika
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH i-g-t 3/7] lib/i915: Add query to detect if engine accepts only ro batches
@ 2019-11-13 23:44     ` Chris Wilson
  0 siblings, 0 replies; 44+ messages in thread
From: Chris Wilson @ 2019-11-13 23:44 UTC (permalink / raw)
  To: Mika Kuoppala, intel-gfx; +Cc: igt-dev, Kuoppala

Quoting Mika Kuoppala (2019-11-13 15:49:09)
> From: "Kuoppala, Mika" <mika.kuoppala@intel.com>
> 
> If cmd parser is mandatory, batch can't be modified post execbuf.
> Some tests rely on modifying batch post execbuf. Give those
> tests a method to query if those modifications ever reach
> the actual engine command stream.
> 
> v2: pull in the test changes, doh
> v3: class based query
> v4: rebase
> 
> Signed-off-by: Kuoppala, Mika <mika.kuoppala@intel.com>
> ---
>  lib/i915/gem_submission.c      | 62 ++++++++++++++++++++++++++++++++++
>  lib/i915/gem_submission.h      |  2 ++
>  tests/i915/gem_busy.c          |  7 +++-
>  tests/i915/gem_exec_async.c    |  3 ++
>  tests/i915/gem_exec_await.c    |  7 +++-
>  tests/i915/gem_exec_fence.c    |  8 +++++
>  tests/i915/gem_exec_latency.c  |  7 ++++
>  tests/i915/gem_exec_nop.c      |  4 ++-
>  tests/i915/gem_exec_schedule.c |  6 +++-
>  tests/i915/gem_exec_whisper.c  |  4 ++-
>  tests/prime_busy.c             |  3 ++
>  tests/prime_vgem.c             |  6 ++++
>  12 files changed, 114 insertions(+), 5 deletions(-)
> 
> diff --git a/lib/i915/gem_submission.c b/lib/i915/gem_submission.c
> index 4f946493..9bdf28bc 100644
> --- a/lib/i915/gem_submission.c
> +++ b/lib/i915/gem_submission.c
> @@ -64,6 +64,22 @@ static bool has_semaphores(int fd, int dir)
>         return val;
>  }
>  
> +static int cmd_parser_version(int fd)

gem_cmdparser_version() ?

Drop the engine parameter as it turns out you didn't need it.
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [Intel-gfx] [PATCH i-g-t 3/7] lib/i915: Add query to detect if engine accepts only ro batches
@ 2019-11-13 23:44     ` Chris Wilson
  0 siblings, 0 replies; 44+ messages in thread
From: Chris Wilson @ 2019-11-13 23:44 UTC (permalink / raw)
  To: Mika Kuoppala, intel-gfx; +Cc: igt-dev, Kuoppala

Quoting Mika Kuoppala (2019-11-13 15:49:09)
> From: "Kuoppala, Mika" <mika.kuoppala@intel.com>
> 
> If cmd parser is mandatory, batch can't be modified post execbuf.
> Some tests rely on modifying batch post execbuf. Give those
> tests a method to query if those modifications ever reach
> the actual engine command stream.
> 
> v2: pull in the test changes, doh
> v3: class based query
> v4: rebase
> 
> Signed-off-by: Kuoppala, Mika <mika.kuoppala@intel.com>
> ---
>  lib/i915/gem_submission.c      | 62 ++++++++++++++++++++++++++++++++++
>  lib/i915/gem_submission.h      |  2 ++
>  tests/i915/gem_busy.c          |  7 +++-
>  tests/i915/gem_exec_async.c    |  3 ++
>  tests/i915/gem_exec_await.c    |  7 +++-
>  tests/i915/gem_exec_fence.c    |  8 +++++
>  tests/i915/gem_exec_latency.c  |  7 ++++
>  tests/i915/gem_exec_nop.c      |  4 ++-
>  tests/i915/gem_exec_schedule.c |  6 +++-
>  tests/i915/gem_exec_whisper.c  |  4 ++-
>  tests/prime_busy.c             |  3 ++
>  tests/prime_vgem.c             |  6 ++++
>  12 files changed, 114 insertions(+), 5 deletions(-)
> 
> diff --git a/lib/i915/gem_submission.c b/lib/i915/gem_submission.c
> index 4f946493..9bdf28bc 100644
> --- a/lib/i915/gem_submission.c
> +++ b/lib/i915/gem_submission.c
> @@ -64,6 +64,22 @@ static bool has_semaphores(int fd, int dir)
>         return val;
>  }
>  
> +static int cmd_parser_version(int fd)

gem_cmdparser_version() ?

Drop the engine parameter as it turns out you didn't need it.
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [igt-dev] [Intel-gfx] [PATCH i-g-t 3/7] lib/i915: Add query to detect if engine accepts only ro batches
@ 2019-11-13 23:44     ` Chris Wilson
  0 siblings, 0 replies; 44+ messages in thread
From: Chris Wilson @ 2019-11-13 23:44 UTC (permalink / raw)
  To: Mika Kuoppala, intel-gfx; +Cc: igt-dev, Kuoppala

Quoting Mika Kuoppala (2019-11-13 15:49:09)
> From: "Kuoppala, Mika" <mika.kuoppala@intel.com>
> 
> If cmd parser is mandatory, batch can't be modified post execbuf.
> Some tests rely on modifying batch post execbuf. Give those
> tests a method to query if those modifications ever reach
> the actual engine command stream.
> 
> v2: pull in the test changes, doh
> v3: class based query
> v4: rebase
> 
> Signed-off-by: Kuoppala, Mika <mika.kuoppala@intel.com>
> ---
>  lib/i915/gem_submission.c      | 62 ++++++++++++++++++++++++++++++++++
>  lib/i915/gem_submission.h      |  2 ++
>  tests/i915/gem_busy.c          |  7 +++-
>  tests/i915/gem_exec_async.c    |  3 ++
>  tests/i915/gem_exec_await.c    |  7 +++-
>  tests/i915/gem_exec_fence.c    |  8 +++++
>  tests/i915/gem_exec_latency.c  |  7 ++++
>  tests/i915/gem_exec_nop.c      |  4 ++-
>  tests/i915/gem_exec_schedule.c |  6 +++-
>  tests/i915/gem_exec_whisper.c  |  4 ++-
>  tests/prime_busy.c             |  3 ++
>  tests/prime_vgem.c             |  6 ++++
>  12 files changed, 114 insertions(+), 5 deletions(-)
> 
> diff --git a/lib/i915/gem_submission.c b/lib/i915/gem_submission.c
> index 4f946493..9bdf28bc 100644
> --- a/lib/i915/gem_submission.c
> +++ b/lib/i915/gem_submission.c
> @@ -64,6 +64,22 @@ static bool has_semaphores(int fd, int dir)
>         return val;
>  }
>  
> +static int cmd_parser_version(int fd)

gem_cmdparser_version() ?

Drop the engine parameter as it turns out you didn't need it.
-Chris
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH i-g-t 3/7] lib/i915: Add query to detect if engine accepts only ro batches
@ 2019-11-13 23:55     ` Chris Wilson
  0 siblings, 0 replies; 44+ messages in thread
From: Chris Wilson @ 2019-11-13 23:55 UTC (permalink / raw)
  To: Mika Kuoppala, intel-gfx; +Cc: igt-dev, Kuoppala

Quoting Mika Kuoppala (2019-11-13 15:49:09)
> diff --git a/tests/i915/gem_exec_schedule.c b/tests/i915/gem_exec_schedule.c
> index 5c15f177..ca6bef6a 100644
> --- a/tests/i915/gem_exec_schedule.c
> +++ b/tests/i915/gem_exec_schedule.c
> @@ -652,7 +652,8 @@ static void semaphore_noskip(int i915)
>                 igt_spin_t *chain, *spin;
>  
>                 if (eb_ring(inner) == eb_ring(outer) ||
> -                   !gem_can_store_dword(i915, eb_ring(inner)))
> +                   !gem_can_store_dword(i915, eb_ring(inner)) ||
> +                   !gem_engine_has_mutable_submission(i915, eb_ring(inner)))
>                         continue;
>  
>                 chain = __igt_spin_new(i915, .engine = eb_ring(outer));
> @@ -1157,6 +1158,9 @@ static void deep(int fd, unsigned ring)
>         int dep_nreq;
>         int n;
>  
> +       igt_require(gem_can_store_dword(fd, ring));
> +       igt_require(gem_engine_has_mutable_submission(fd, ring));
> +
>         ctx = malloc(sizeof(*ctx) * MAX_CONTEXTS);
>         for (n = 0; n < MAX_CONTEXTS; n++) {
>                 ctx[n] = gem_context_create(fd);

Works, no need to skip.

> diff --git a/tests/i915/gem_exec_whisper.c b/tests/i915/gem_exec_whisper.c
> index a3384760..45cc1437 100644
> --- a/tests/i915/gem_exec_whisper.c
> +++ b/tests/i915/gem_exec_whisper.c
> @@ -204,13 +204,15 @@ static void whisper(int fd, unsigned engine, unsigned flags)
>         nengine = 0;
>         if (engine == ALL_ENGINES) {
>                 for_each_physical_engine(e, fd) {
> -                       if (gem_can_store_dword(fd, eb_ring(e)))
> +                       if (gem_can_store_dword(fd, eb_ring(e)) &&
> +                           gem_engine_has_mutable_submission(fd, eb_ring(e)))
>                                 engines[nengine++] = eb_ring(e);
>                 }
>         } else {
>                 igt_assert(!(flags & ALL));
>                 igt_require(gem_has_ring(fd, engine));
>                 igt_require(gem_can_store_dword(fd, engine));
> +               igt_require(gem_engine_has_mutable_submission(fd, engine));
>                 engines[nengine++] = engine;
>         }
>         igt_require(nengine);

You should be able to handle the whisper; it's one of the basic does the
kernel work at all. All it is testing is relocation and scheduling...

Worksforme.
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [Intel-gfx] [PATCH i-g-t 3/7] lib/i915: Add query to detect if engine accepts only ro batches
@ 2019-11-13 23:55     ` Chris Wilson
  0 siblings, 0 replies; 44+ messages in thread
From: Chris Wilson @ 2019-11-13 23:55 UTC (permalink / raw)
  To: Mika Kuoppala, intel-gfx; +Cc: igt-dev, Kuoppala

Quoting Mika Kuoppala (2019-11-13 15:49:09)
> diff --git a/tests/i915/gem_exec_schedule.c b/tests/i915/gem_exec_schedule.c
> index 5c15f177..ca6bef6a 100644
> --- a/tests/i915/gem_exec_schedule.c
> +++ b/tests/i915/gem_exec_schedule.c
> @@ -652,7 +652,8 @@ static void semaphore_noskip(int i915)
>                 igt_spin_t *chain, *spin;
>  
>                 if (eb_ring(inner) == eb_ring(outer) ||
> -                   !gem_can_store_dword(i915, eb_ring(inner)))
> +                   !gem_can_store_dword(i915, eb_ring(inner)) ||
> +                   !gem_engine_has_mutable_submission(i915, eb_ring(inner)))
>                         continue;
>  
>                 chain = __igt_spin_new(i915, .engine = eb_ring(outer));
> @@ -1157,6 +1158,9 @@ static void deep(int fd, unsigned ring)
>         int dep_nreq;
>         int n;
>  
> +       igt_require(gem_can_store_dword(fd, ring));
> +       igt_require(gem_engine_has_mutable_submission(fd, ring));
> +
>         ctx = malloc(sizeof(*ctx) * MAX_CONTEXTS);
>         for (n = 0; n < MAX_CONTEXTS; n++) {
>                 ctx[n] = gem_context_create(fd);

Works, no need to skip.

> diff --git a/tests/i915/gem_exec_whisper.c b/tests/i915/gem_exec_whisper.c
> index a3384760..45cc1437 100644
> --- a/tests/i915/gem_exec_whisper.c
> +++ b/tests/i915/gem_exec_whisper.c
> @@ -204,13 +204,15 @@ static void whisper(int fd, unsigned engine, unsigned flags)
>         nengine = 0;
>         if (engine == ALL_ENGINES) {
>                 for_each_physical_engine(e, fd) {
> -                       if (gem_can_store_dword(fd, eb_ring(e)))
> +                       if (gem_can_store_dword(fd, eb_ring(e)) &&
> +                           gem_engine_has_mutable_submission(fd, eb_ring(e)))
>                                 engines[nengine++] = eb_ring(e);
>                 }
>         } else {
>                 igt_assert(!(flags & ALL));
>                 igt_require(gem_has_ring(fd, engine));
>                 igt_require(gem_can_store_dword(fd, engine));
> +               igt_require(gem_engine_has_mutable_submission(fd, engine));
>                 engines[nengine++] = engine;
>         }
>         igt_require(nengine);

You should be able to handle the whisper; it's one of the basic does the
kernel work at all. All it is testing is relocation and scheduling...

Worksforme.
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [igt-dev] [Intel-gfx] [PATCH i-g-t 3/7] lib/i915: Add query to detect if engine accepts only ro batches
@ 2019-11-13 23:55     ` Chris Wilson
  0 siblings, 0 replies; 44+ messages in thread
From: Chris Wilson @ 2019-11-13 23:55 UTC (permalink / raw)
  To: Mika Kuoppala, intel-gfx; +Cc: igt-dev, Kuoppala

Quoting Mika Kuoppala (2019-11-13 15:49:09)
> diff --git a/tests/i915/gem_exec_schedule.c b/tests/i915/gem_exec_schedule.c
> index 5c15f177..ca6bef6a 100644
> --- a/tests/i915/gem_exec_schedule.c
> +++ b/tests/i915/gem_exec_schedule.c
> @@ -652,7 +652,8 @@ static void semaphore_noskip(int i915)
>                 igt_spin_t *chain, *spin;
>  
>                 if (eb_ring(inner) == eb_ring(outer) ||
> -                   !gem_can_store_dword(i915, eb_ring(inner)))
> +                   !gem_can_store_dword(i915, eb_ring(inner)) ||
> +                   !gem_engine_has_mutable_submission(i915, eb_ring(inner)))
>                         continue;
>  
>                 chain = __igt_spin_new(i915, .engine = eb_ring(outer));
> @@ -1157,6 +1158,9 @@ static void deep(int fd, unsigned ring)
>         int dep_nreq;
>         int n;
>  
> +       igt_require(gem_can_store_dword(fd, ring));
> +       igt_require(gem_engine_has_mutable_submission(fd, ring));
> +
>         ctx = malloc(sizeof(*ctx) * MAX_CONTEXTS);
>         for (n = 0; n < MAX_CONTEXTS; n++) {
>                 ctx[n] = gem_context_create(fd);

Works, no need to skip.

> diff --git a/tests/i915/gem_exec_whisper.c b/tests/i915/gem_exec_whisper.c
> index a3384760..45cc1437 100644
> --- a/tests/i915/gem_exec_whisper.c
> +++ b/tests/i915/gem_exec_whisper.c
> @@ -204,13 +204,15 @@ static void whisper(int fd, unsigned engine, unsigned flags)
>         nengine = 0;
>         if (engine == ALL_ENGINES) {
>                 for_each_physical_engine(e, fd) {
> -                       if (gem_can_store_dword(fd, eb_ring(e)))
> +                       if (gem_can_store_dword(fd, eb_ring(e)) &&
> +                           gem_engine_has_mutable_submission(fd, eb_ring(e)))
>                                 engines[nengine++] = eb_ring(e);
>                 }
>         } else {
>                 igt_assert(!(flags & ALL));
>                 igt_require(gem_has_ring(fd, engine));
>                 igt_require(gem_can_store_dword(fd, engine));
> +               igt_require(gem_engine_has_mutable_submission(fd, engine));
>                 engines[nengine++] = engine;
>         }
>         igt_require(nengine);

You should be able to handle the whisper; it's one of the basic does the
kernel work at all. All it is testing is relocation and scheduling...

Worksforme.
-Chris
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [igt-dev] [PATCH i-g-t 5/7] Add tests/gem_blt_parse
@ 2019-11-14  8:41     ` Daniel Vetter
  0 siblings, 0 replies; 44+ messages in thread
From: Daniel Vetter @ 2019-11-14  8:41 UTC (permalink / raw)
  To: Mika Kuoppala; +Cc: igt-dev, intel-gfx

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, &regs[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

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [Intel-gfx] [igt-dev] [PATCH i-g-t 5/7] Add tests/gem_blt_parse
@ 2019-11-14  8:41     ` Daniel Vetter
  0 siblings, 0 replies; 44+ messages in thread
From: Daniel Vetter @ 2019-11-14  8:41 UTC (permalink / raw)
  To: Mika Kuoppala; +Cc: igt-dev, intel-gfx

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, &regs[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

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [igt-dev] [PATCH i-g-t 5/7] Add tests/gem_blt_parse
@ 2019-11-14  8:41     ` Daniel Vetter
  0 siblings, 0 replies; 44+ messages in thread
From: Daniel Vetter @ 2019-11-14  8:41 UTC (permalink / raw)
  To: Mika Kuoppala; +Cc: igt-dev, intel-gfx

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, &regs[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

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

^ permalink raw reply	[flat|nested] 44+ messages in thread

end of thread, other threads:[~2019-11-14  8:41 UTC | newest]

Thread overview: 44+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-11-13 15:49 [PATCH i-g-t 1/7] lib/igt_dummyload: Send batch as first Mika Kuoppala
2019-11-13 15:49 ` [Intel-gfx] " Mika Kuoppala
2019-11-13 15:49 ` [PATCH i-g-t 2/7] igt: Use COND_BBEND for busy spinning on gen9 Mika Kuoppala
2019-11-13 15:49   ` [igt-dev] " Mika Kuoppala
2019-11-13 15:49   ` [Intel-gfx] " Mika Kuoppala
2019-11-13 16:02   ` [igt-dev] " Chris Wilson
2019-11-13 16:02     ` [Intel-gfx] " Chris Wilson
2019-11-13 17:19     ` Mika Kuoppala
2019-11-13 17:19       ` Mika Kuoppala
2019-11-13 17:19       ` [Intel-gfx] " Mika Kuoppala
2019-11-13 15:49 ` [PATCH i-g-t 3/7] lib/i915: Add query to detect if engine accepts only ro batches Mika Kuoppala
2019-11-13 15:49   ` [igt-dev] " Mika Kuoppala
2019-11-13 15:49   ` [Intel-gfx] " Mika Kuoppala
2019-11-13 23:44   ` Chris Wilson
2019-11-13 23:44     ` [igt-dev] [Intel-gfx] " Chris Wilson
2019-11-13 23:44     ` Chris Wilson
2019-11-13 23:55   ` Chris Wilson
2019-11-13 23:55     ` [igt-dev] [Intel-gfx] " Chris Wilson
2019-11-13 23:55     ` Chris Wilson
2019-11-13 15:49 ` [PATCH i-g-t 4/7] tests/i915: Skip if secure batches is not available Mika Kuoppala
2019-11-13 15:49   ` [igt-dev] " Mika Kuoppala
2019-11-13 15:49   ` [Intel-gfx] " Mika Kuoppala
2019-11-13 15:49 ` [PATCH i-g-t 5/7] Add tests/gem_blt_parse Mika Kuoppala
2019-11-13 15:49   ` [igt-dev] " Mika Kuoppala
2019-11-13 15:49   ` [Intel-gfx] " Mika Kuoppala
2019-11-13 16:14   ` [igt-dev] " Chris Wilson
2019-11-13 16:14     ` Chris Wilson
2019-11-13 16:14     ` [Intel-gfx] " Chris Wilson
2019-11-13 16:37     ` Mika Kuoppala
2019-11-13 16:37       ` Mika Kuoppala
2019-11-13 16:37       ` [Intel-gfx] " Mika Kuoppala
2019-11-14  8:41   ` Daniel Vetter
2019-11-14  8:41     ` Daniel Vetter
2019-11-14  8:41     ` [Intel-gfx] " Daniel Vetter
2019-11-13 15:49 ` [PATCH i-g-t 6/7] lib/igt_aux: Add helper to query suspend-to-mem modes Mika Kuoppala
2019-11-13 15:49   ` [igt-dev] " Mika Kuoppala
2019-11-13 15:49   ` [Intel-gfx] " Mika Kuoppala
2019-11-13 15:49 ` [PATCH i-g-t 7/7] test/i915: Add i915_rc6_ctx_corruption Mika Kuoppala
2019-11-13 15:49   ` [Intel-gfx] " Mika Kuoppala
2019-11-13 16:18   ` Chris Wilson
2019-11-13 16:18     ` [igt-dev] [Intel-gfx] " Chris Wilson
2019-11-13 16:18     ` Chris Wilson
2019-11-13 15:57 ` [PATCH i-g-t 1/7] lib/igt_dummyload: Send batch as first Chris Wilson
2019-11-13 15:57   ` [Intel-gfx] " Chris Wilson

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.