1112ed2d3SChris Wilson /*
2112ed2d3SChris Wilson  * SPDX-License-Identifier: MIT
3112ed2d3SChris Wilson  *
4112ed2d3SChris Wilson  * Copyright © 2018 Intel Corporation
5112ed2d3SChris Wilson  */
6112ed2d3SChris Wilson 
710be98a7SChris Wilson #include "gem/i915_gem_pm.h"
8112ed2d3SChris Wilson #include "i915_selftest.h"
9112ed2d3SChris Wilson #include "intel_reset.h"
10112ed2d3SChris Wilson 
11112ed2d3SChris Wilson #include "selftests/igt_flush_test.h"
12112ed2d3SChris Wilson #include "selftests/igt_reset.h"
13112ed2d3SChris Wilson #include "selftests/igt_spinner.h"
14112ed2d3SChris Wilson #include "selftests/igt_wedge_me.h"
15112ed2d3SChris Wilson #include "selftests/mock_drm.h"
16112ed2d3SChris Wilson 
1710be98a7SChris Wilson #include "gem/selftests/igt_gem_utils.h"
1810be98a7SChris Wilson #include "gem/selftests/mock_context.h"
1910be98a7SChris Wilson 
20112ed2d3SChris Wilson static const struct wo_register {
21112ed2d3SChris Wilson 	enum intel_platform platform;
22112ed2d3SChris Wilson 	u32 reg;
23112ed2d3SChris Wilson } wo_registers[] = {
24112ed2d3SChris Wilson 	{ INTEL_GEMINILAKE, 0x731c }
25112ed2d3SChris Wilson };
26112ed2d3SChris Wilson 
27fde93886STvrtko Ursulin #define REF_NAME_MAX (INTEL_ENGINE_CS_MAX_NAME + 8)
28112ed2d3SChris Wilson struct wa_lists {
29112ed2d3SChris Wilson 	struct i915_wa_list gt_wa_list;
30112ed2d3SChris Wilson 	struct {
31112ed2d3SChris Wilson 		char name[REF_NAME_MAX];
32112ed2d3SChris Wilson 		struct i915_wa_list wa_list;
33fde93886STvrtko Ursulin 		struct i915_wa_list ctx_wa_list;
34112ed2d3SChris Wilson 	} engine[I915_NUM_ENGINES];
35112ed2d3SChris Wilson };
36112ed2d3SChris Wilson 
37112ed2d3SChris Wilson static void
38112ed2d3SChris Wilson reference_lists_init(struct drm_i915_private *i915, struct wa_lists *lists)
39112ed2d3SChris Wilson {
40112ed2d3SChris Wilson 	struct intel_engine_cs *engine;
41112ed2d3SChris Wilson 	enum intel_engine_id id;
42112ed2d3SChris Wilson 
43112ed2d3SChris Wilson 	memset(lists, 0, sizeof(*lists));
44112ed2d3SChris Wilson 
45112ed2d3SChris Wilson 	wa_init_start(&lists->gt_wa_list, "GT_REF");
46112ed2d3SChris Wilson 	gt_init_workarounds(i915, &lists->gt_wa_list);
47112ed2d3SChris Wilson 	wa_init_finish(&lists->gt_wa_list);
48112ed2d3SChris Wilson 
49112ed2d3SChris Wilson 	for_each_engine(engine, i915, id) {
50112ed2d3SChris Wilson 		struct i915_wa_list *wal = &lists->engine[id].wa_list;
51112ed2d3SChris Wilson 		char *name = lists->engine[id].name;
52112ed2d3SChris Wilson 
53112ed2d3SChris Wilson 		snprintf(name, REF_NAME_MAX, "%s_REF", engine->name);
54112ed2d3SChris Wilson 
55112ed2d3SChris Wilson 		wa_init_start(wal, name);
56112ed2d3SChris Wilson 		engine_init_workarounds(engine, wal);
57112ed2d3SChris Wilson 		wa_init_finish(wal);
58fde93886STvrtko Ursulin 
59fde93886STvrtko Ursulin 		snprintf(name, REF_NAME_MAX, "%s_CTX_REF", engine->name);
60fde93886STvrtko Ursulin 
61fde93886STvrtko Ursulin 		__intel_engine_init_ctx_wa(engine,
62fde93886STvrtko Ursulin 					   &lists->engine[id].ctx_wa_list,
63fde93886STvrtko Ursulin 					   name);
64112ed2d3SChris Wilson 	}
65112ed2d3SChris Wilson }
66112ed2d3SChris Wilson 
67112ed2d3SChris Wilson static void
68112ed2d3SChris Wilson reference_lists_fini(struct drm_i915_private *i915, struct wa_lists *lists)
69112ed2d3SChris Wilson {
70112ed2d3SChris Wilson 	struct intel_engine_cs *engine;
71112ed2d3SChris Wilson 	enum intel_engine_id id;
72112ed2d3SChris Wilson 
73112ed2d3SChris Wilson 	for_each_engine(engine, i915, id)
74112ed2d3SChris Wilson 		intel_wa_list_free(&lists->engine[id].wa_list);
75112ed2d3SChris Wilson 
76112ed2d3SChris Wilson 	intel_wa_list_free(&lists->gt_wa_list);
77112ed2d3SChris Wilson }
78112ed2d3SChris Wilson 
79112ed2d3SChris Wilson static struct drm_i915_gem_object *
80112ed2d3SChris Wilson read_nonprivs(struct i915_gem_context *ctx, struct intel_engine_cs *engine)
81112ed2d3SChris Wilson {
82112ed2d3SChris Wilson 	const u32 base = engine->mmio_base;
83112ed2d3SChris Wilson 	struct drm_i915_gem_object *result;
84112ed2d3SChris Wilson 	struct i915_request *rq;
85112ed2d3SChris Wilson 	struct i915_vma *vma;
86112ed2d3SChris Wilson 	u32 srm, *cs;
87112ed2d3SChris Wilson 	int err;
88112ed2d3SChris Wilson 	int i;
89112ed2d3SChris Wilson 
90112ed2d3SChris Wilson 	result = i915_gem_object_create_internal(engine->i915, PAGE_SIZE);
91112ed2d3SChris Wilson 	if (IS_ERR(result))
92112ed2d3SChris Wilson 		return result;
93112ed2d3SChris Wilson 
94112ed2d3SChris Wilson 	i915_gem_object_set_cache_coherency(result, I915_CACHE_LLC);
95112ed2d3SChris Wilson 
96112ed2d3SChris Wilson 	cs = i915_gem_object_pin_map(result, I915_MAP_WB);
97112ed2d3SChris Wilson 	if (IS_ERR(cs)) {
98112ed2d3SChris Wilson 		err = PTR_ERR(cs);
99112ed2d3SChris Wilson 		goto err_obj;
100112ed2d3SChris Wilson 	}
101112ed2d3SChris Wilson 	memset(cs, 0xc5, PAGE_SIZE);
102112ed2d3SChris Wilson 	i915_gem_object_flush_map(result);
103112ed2d3SChris Wilson 	i915_gem_object_unpin_map(result);
104112ed2d3SChris Wilson 
105112ed2d3SChris Wilson 	vma = i915_vma_instance(result, &engine->i915->ggtt.vm, NULL);
106112ed2d3SChris Wilson 	if (IS_ERR(vma)) {
107112ed2d3SChris Wilson 		err = PTR_ERR(vma);
108112ed2d3SChris Wilson 		goto err_obj;
109112ed2d3SChris Wilson 	}
110112ed2d3SChris Wilson 
111112ed2d3SChris Wilson 	err = i915_vma_pin(vma, 0, 0, PIN_GLOBAL);
112112ed2d3SChris Wilson 	if (err)
113112ed2d3SChris Wilson 		goto err_obj;
114112ed2d3SChris Wilson 
11546472b3eSChris Wilson 	rq = igt_request_alloc(ctx, engine);
116112ed2d3SChris Wilson 	if (IS_ERR(rq)) {
117112ed2d3SChris Wilson 		err = PTR_ERR(rq);
118112ed2d3SChris Wilson 		goto err_pin;
119112ed2d3SChris Wilson 	}
120112ed2d3SChris Wilson 
1216951e589SChris Wilson 	i915_vma_lock(vma);
122112ed2d3SChris Wilson 	err = i915_vma_move_to_active(vma, rq, EXEC_OBJECT_WRITE);
1236951e589SChris Wilson 	i915_vma_unlock(vma);
124112ed2d3SChris Wilson 	if (err)
125112ed2d3SChris Wilson 		goto err_req;
126112ed2d3SChris Wilson 
127112ed2d3SChris Wilson 	srm = MI_STORE_REGISTER_MEM | MI_SRM_LRM_GLOBAL_GTT;
128112ed2d3SChris Wilson 	if (INTEL_GEN(ctx->i915) >= 8)
129112ed2d3SChris Wilson 		srm++;
130112ed2d3SChris Wilson 
131112ed2d3SChris Wilson 	cs = intel_ring_begin(rq, 4 * RING_MAX_NONPRIV_SLOTS);
132112ed2d3SChris Wilson 	if (IS_ERR(cs)) {
133112ed2d3SChris Wilson 		err = PTR_ERR(cs);
134112ed2d3SChris Wilson 		goto err_req;
135112ed2d3SChris Wilson 	}
136112ed2d3SChris Wilson 
137112ed2d3SChris Wilson 	for (i = 0; i < RING_MAX_NONPRIV_SLOTS; i++) {
138112ed2d3SChris Wilson 		*cs++ = srm;
139112ed2d3SChris Wilson 		*cs++ = i915_mmio_reg_offset(RING_FORCE_TO_NONPRIV(base, i));
140112ed2d3SChris Wilson 		*cs++ = i915_ggtt_offset(vma) + sizeof(u32) * i;
141112ed2d3SChris Wilson 		*cs++ = 0;
142112ed2d3SChris Wilson 	}
143112ed2d3SChris Wilson 	intel_ring_advance(rq, cs);
144112ed2d3SChris Wilson 
145112ed2d3SChris Wilson 	i915_request_add(rq);
146112ed2d3SChris Wilson 	i915_vma_unpin(vma);
147112ed2d3SChris Wilson 
148112ed2d3SChris Wilson 	return result;
149112ed2d3SChris Wilson 
150112ed2d3SChris Wilson err_req:
151112ed2d3SChris Wilson 	i915_request_add(rq);
152112ed2d3SChris Wilson err_pin:
153112ed2d3SChris Wilson 	i915_vma_unpin(vma);
154112ed2d3SChris Wilson err_obj:
155112ed2d3SChris Wilson 	i915_gem_object_put(result);
156112ed2d3SChris Wilson 	return ERR_PTR(err);
157112ed2d3SChris Wilson }
158112ed2d3SChris Wilson 
159112ed2d3SChris Wilson static u32
160112ed2d3SChris Wilson get_whitelist_reg(const struct intel_engine_cs *engine, unsigned int i)
161112ed2d3SChris Wilson {
162112ed2d3SChris Wilson 	i915_reg_t reg = i < engine->whitelist.count ?
163112ed2d3SChris Wilson 			 engine->whitelist.list[i].reg :
164112ed2d3SChris Wilson 			 RING_NOPID(engine->mmio_base);
165112ed2d3SChris Wilson 
166112ed2d3SChris Wilson 	return i915_mmio_reg_offset(reg);
167112ed2d3SChris Wilson }
168112ed2d3SChris Wilson 
169112ed2d3SChris Wilson static void
170112ed2d3SChris Wilson print_results(const struct intel_engine_cs *engine, const u32 *results)
171112ed2d3SChris Wilson {
172112ed2d3SChris Wilson 	unsigned int i;
173112ed2d3SChris Wilson 
174112ed2d3SChris Wilson 	for (i = 0; i < RING_MAX_NONPRIV_SLOTS; i++) {
175112ed2d3SChris Wilson 		u32 expected = get_whitelist_reg(engine, i);
176112ed2d3SChris Wilson 		u32 actual = results[i];
177112ed2d3SChris Wilson 
178112ed2d3SChris Wilson 		pr_info("RING_NONPRIV[%d]: expected 0x%08x, found 0x%08x\n",
179112ed2d3SChris Wilson 			i, expected, actual);
180112ed2d3SChris Wilson 	}
181112ed2d3SChris Wilson }
182112ed2d3SChris Wilson 
183112ed2d3SChris Wilson static int check_whitelist(struct i915_gem_context *ctx,
184112ed2d3SChris Wilson 			   struct intel_engine_cs *engine)
185112ed2d3SChris Wilson {
186112ed2d3SChris Wilson 	struct drm_i915_gem_object *results;
187112ed2d3SChris Wilson 	struct igt_wedge_me wedge;
188112ed2d3SChris Wilson 	u32 *vaddr;
189112ed2d3SChris Wilson 	int err;
190112ed2d3SChris Wilson 	int i;
191112ed2d3SChris Wilson 
192112ed2d3SChris Wilson 	results = read_nonprivs(ctx, engine);
193112ed2d3SChris Wilson 	if (IS_ERR(results))
194112ed2d3SChris Wilson 		return PTR_ERR(results);
195112ed2d3SChris Wilson 
196112ed2d3SChris Wilson 	err = 0;
1976951e589SChris Wilson 	i915_gem_object_lock(results);
198112ed2d3SChris Wilson 	igt_wedge_on_timeout(&wedge, ctx->i915, HZ / 5) /* a safety net! */
199112ed2d3SChris Wilson 		err = i915_gem_object_set_to_cpu_domain(results, false);
2006951e589SChris Wilson 	i915_gem_object_unlock(results);
201112ed2d3SChris Wilson 	if (i915_terminally_wedged(ctx->i915))
202112ed2d3SChris Wilson 		err = -EIO;
203112ed2d3SChris Wilson 	if (err)
204112ed2d3SChris Wilson 		goto out_put;
205112ed2d3SChris Wilson 
206112ed2d3SChris Wilson 	vaddr = i915_gem_object_pin_map(results, I915_MAP_WB);
207112ed2d3SChris Wilson 	if (IS_ERR(vaddr)) {
208112ed2d3SChris Wilson 		err = PTR_ERR(vaddr);
209112ed2d3SChris Wilson 		goto out_put;
210112ed2d3SChris Wilson 	}
211112ed2d3SChris Wilson 
212112ed2d3SChris Wilson 	for (i = 0; i < RING_MAX_NONPRIV_SLOTS; i++) {
213112ed2d3SChris Wilson 		u32 expected = get_whitelist_reg(engine, i);
214112ed2d3SChris Wilson 		u32 actual = vaddr[i];
215112ed2d3SChris Wilson 
216112ed2d3SChris Wilson 		if (expected != actual) {
217112ed2d3SChris Wilson 			print_results(engine, vaddr);
218112ed2d3SChris Wilson 			pr_err("Invalid RING_NONPRIV[%d], expected 0x%08x, found 0x%08x\n",
219112ed2d3SChris Wilson 			       i, expected, actual);
220112ed2d3SChris Wilson 
221112ed2d3SChris Wilson 			err = -EINVAL;
222112ed2d3SChris Wilson 			break;
223112ed2d3SChris Wilson 		}
224112ed2d3SChris Wilson 	}
225112ed2d3SChris Wilson 
226112ed2d3SChris Wilson 	i915_gem_object_unpin_map(results);
227112ed2d3SChris Wilson out_put:
228112ed2d3SChris Wilson 	i915_gem_object_put(results);
229112ed2d3SChris Wilson 	return err;
230112ed2d3SChris Wilson }
231112ed2d3SChris Wilson 
232112ed2d3SChris Wilson static int do_device_reset(struct intel_engine_cs *engine)
233112ed2d3SChris Wilson {
234112ed2d3SChris Wilson 	i915_reset(engine->i915, engine->mask, "live_workarounds");
235112ed2d3SChris Wilson 	return 0;
236112ed2d3SChris Wilson }
237112ed2d3SChris Wilson 
238112ed2d3SChris Wilson static int do_engine_reset(struct intel_engine_cs *engine)
239112ed2d3SChris Wilson {
240112ed2d3SChris Wilson 	return i915_reset_engine(engine, "live_workarounds");
241112ed2d3SChris Wilson }
242112ed2d3SChris Wilson 
243112ed2d3SChris Wilson static int
244112ed2d3SChris Wilson switch_to_scratch_context(struct intel_engine_cs *engine,
245112ed2d3SChris Wilson 			  struct igt_spinner *spin)
246112ed2d3SChris Wilson {
247112ed2d3SChris Wilson 	struct i915_gem_context *ctx;
248112ed2d3SChris Wilson 	struct i915_request *rq;
249112ed2d3SChris Wilson 	intel_wakeref_t wakeref;
250112ed2d3SChris Wilson 	int err = 0;
251112ed2d3SChris Wilson 
252112ed2d3SChris Wilson 	ctx = kernel_context(engine->i915);
253112ed2d3SChris Wilson 	if (IS_ERR(ctx))
254112ed2d3SChris Wilson 		return PTR_ERR(ctx);
255112ed2d3SChris Wilson 
256112ed2d3SChris Wilson 	GEM_BUG_ON(i915_gem_context_is_bannable(ctx));
257112ed2d3SChris Wilson 
258112ed2d3SChris Wilson 	rq = ERR_PTR(-ENODEV);
259c447ff7dSDaniele Ceraolo Spurio 	with_intel_runtime_pm(&engine->i915->runtime_pm, wakeref)
260112ed2d3SChris Wilson 		rq = igt_spinner_create_request(spin, ctx, engine, MI_NOOP);
261112ed2d3SChris Wilson 
262112ed2d3SChris Wilson 	kernel_context_close(ctx);
263112ed2d3SChris Wilson 
264112ed2d3SChris Wilson 	if (IS_ERR(rq)) {
265112ed2d3SChris Wilson 		spin = NULL;
266112ed2d3SChris Wilson 		err = PTR_ERR(rq);
267112ed2d3SChris Wilson 		goto err;
268112ed2d3SChris Wilson 	}
269112ed2d3SChris Wilson 
270112ed2d3SChris Wilson 	i915_request_add(rq);
271112ed2d3SChris Wilson 
272112ed2d3SChris Wilson 	if (spin && !igt_wait_for_spinner(spin, rq)) {
273112ed2d3SChris Wilson 		pr_err("Spinner failed to start\n");
274112ed2d3SChris Wilson 		err = -ETIMEDOUT;
275112ed2d3SChris Wilson 	}
276112ed2d3SChris Wilson 
277112ed2d3SChris Wilson err:
278112ed2d3SChris Wilson 	if (err && spin)
279112ed2d3SChris Wilson 		igt_spinner_end(spin);
280112ed2d3SChris Wilson 
281112ed2d3SChris Wilson 	return err;
282112ed2d3SChris Wilson }
283112ed2d3SChris Wilson 
284112ed2d3SChris Wilson static int check_whitelist_across_reset(struct intel_engine_cs *engine,
285112ed2d3SChris Wilson 					int (*reset)(struct intel_engine_cs *),
286112ed2d3SChris Wilson 					const char *name)
287112ed2d3SChris Wilson {
288112ed2d3SChris Wilson 	struct drm_i915_private *i915 = engine->i915;
289112ed2d3SChris Wilson 	struct i915_gem_context *ctx;
290112ed2d3SChris Wilson 	struct igt_spinner spin;
291112ed2d3SChris Wilson 	intel_wakeref_t wakeref;
292112ed2d3SChris Wilson 	int err;
293112ed2d3SChris Wilson 
294112ed2d3SChris Wilson 	pr_info("Checking %d whitelisted registers (RING_NONPRIV) [%s]\n",
295112ed2d3SChris Wilson 		engine->whitelist.count, name);
296112ed2d3SChris Wilson 
297112ed2d3SChris Wilson 	err = igt_spinner_init(&spin, i915);
298112ed2d3SChris Wilson 	if (err)
299112ed2d3SChris Wilson 		return err;
300112ed2d3SChris Wilson 
301112ed2d3SChris Wilson 	ctx = kernel_context(i915);
302112ed2d3SChris Wilson 	if (IS_ERR(ctx))
303112ed2d3SChris Wilson 		return PTR_ERR(ctx);
304112ed2d3SChris Wilson 
305112ed2d3SChris Wilson 	err = check_whitelist(ctx, engine);
306112ed2d3SChris Wilson 	if (err) {
307112ed2d3SChris Wilson 		pr_err("Invalid whitelist *before* %s reset!\n", name);
308112ed2d3SChris Wilson 		goto out;
309112ed2d3SChris Wilson 	}
310112ed2d3SChris Wilson 
311112ed2d3SChris Wilson 	err = switch_to_scratch_context(engine, &spin);
312112ed2d3SChris Wilson 	if (err)
313112ed2d3SChris Wilson 		goto out;
314112ed2d3SChris Wilson 
315c447ff7dSDaniele Ceraolo Spurio 	with_intel_runtime_pm(&i915->runtime_pm, wakeref)
316112ed2d3SChris Wilson 		err = reset(engine);
317112ed2d3SChris Wilson 
318112ed2d3SChris Wilson 	igt_spinner_end(&spin);
319112ed2d3SChris Wilson 	igt_spinner_fini(&spin);
320112ed2d3SChris Wilson 
321112ed2d3SChris Wilson 	if (err) {
322112ed2d3SChris Wilson 		pr_err("%s reset failed\n", name);
323112ed2d3SChris Wilson 		goto out;
324112ed2d3SChris Wilson 	}
325112ed2d3SChris Wilson 
326112ed2d3SChris Wilson 	err = check_whitelist(ctx, engine);
327112ed2d3SChris Wilson 	if (err) {
328112ed2d3SChris Wilson 		pr_err("Whitelist not preserved in context across %s reset!\n",
329112ed2d3SChris Wilson 		       name);
330112ed2d3SChris Wilson 		goto out;
331112ed2d3SChris Wilson 	}
332112ed2d3SChris Wilson 
333112ed2d3SChris Wilson 	kernel_context_close(ctx);
334112ed2d3SChris Wilson 
335112ed2d3SChris Wilson 	ctx = kernel_context(i915);
336112ed2d3SChris Wilson 	if (IS_ERR(ctx))
337112ed2d3SChris Wilson 		return PTR_ERR(ctx);
338112ed2d3SChris Wilson 
339112ed2d3SChris Wilson 	err = check_whitelist(ctx, engine);
340112ed2d3SChris Wilson 	if (err) {
341112ed2d3SChris Wilson 		pr_err("Invalid whitelist *after* %s reset in fresh context!\n",
342112ed2d3SChris Wilson 		       name);
343112ed2d3SChris Wilson 		goto out;
344112ed2d3SChris Wilson 	}
345112ed2d3SChris Wilson 
346112ed2d3SChris Wilson out:
347112ed2d3SChris Wilson 	kernel_context_close(ctx);
348112ed2d3SChris Wilson 	return err;
349112ed2d3SChris Wilson }
350112ed2d3SChris Wilson 
351112ed2d3SChris Wilson static struct i915_vma *create_batch(struct i915_gem_context *ctx)
352112ed2d3SChris Wilson {
353112ed2d3SChris Wilson 	struct drm_i915_gem_object *obj;
354112ed2d3SChris Wilson 	struct i915_vma *vma;
355112ed2d3SChris Wilson 	int err;
356112ed2d3SChris Wilson 
357112ed2d3SChris Wilson 	obj = i915_gem_object_create_internal(ctx->i915, 16 * PAGE_SIZE);
358112ed2d3SChris Wilson 	if (IS_ERR(obj))
359112ed2d3SChris Wilson 		return ERR_CAST(obj);
360112ed2d3SChris Wilson 
361e568ac38SChris Wilson 	vma = i915_vma_instance(obj, ctx->vm, NULL);
362112ed2d3SChris Wilson 	if (IS_ERR(vma)) {
363112ed2d3SChris Wilson 		err = PTR_ERR(vma);
364112ed2d3SChris Wilson 		goto err_obj;
365112ed2d3SChris Wilson 	}
366112ed2d3SChris Wilson 
367112ed2d3SChris Wilson 	err = i915_vma_pin(vma, 0, 0, PIN_USER);
368112ed2d3SChris Wilson 	if (err)
369112ed2d3SChris Wilson 		goto err_obj;
370112ed2d3SChris Wilson 
371112ed2d3SChris Wilson 	return vma;
372112ed2d3SChris Wilson 
373112ed2d3SChris Wilson err_obj:
374112ed2d3SChris Wilson 	i915_gem_object_put(obj);
375112ed2d3SChris Wilson 	return ERR_PTR(err);
376112ed2d3SChris Wilson }
377112ed2d3SChris Wilson 
378112ed2d3SChris Wilson static u32 reg_write(u32 old, u32 new, u32 rsvd)
379112ed2d3SChris Wilson {
380112ed2d3SChris Wilson 	if (rsvd == 0x0000ffff) {
381112ed2d3SChris Wilson 		old &= ~(new >> 16);
382112ed2d3SChris Wilson 		old |= new & (new >> 16);
383112ed2d3SChris Wilson 	} else {
384112ed2d3SChris Wilson 		old &= ~rsvd;
385112ed2d3SChris Wilson 		old |= new & rsvd;
386112ed2d3SChris Wilson 	}
387112ed2d3SChris Wilson 
388112ed2d3SChris Wilson 	return old;
389112ed2d3SChris Wilson }
390112ed2d3SChris Wilson 
391112ed2d3SChris Wilson static bool wo_register(struct intel_engine_cs *engine, u32 reg)
392112ed2d3SChris Wilson {
393112ed2d3SChris Wilson 	enum intel_platform platform = INTEL_INFO(engine->i915)->platform;
394112ed2d3SChris Wilson 	int i;
395112ed2d3SChris Wilson 
396112ed2d3SChris Wilson 	for (i = 0; i < ARRAY_SIZE(wo_registers); i++) {
397112ed2d3SChris Wilson 		if (wo_registers[i].platform == platform &&
398112ed2d3SChris Wilson 		    wo_registers[i].reg == reg)
399112ed2d3SChris Wilson 			return true;
400112ed2d3SChris Wilson 	}
401112ed2d3SChris Wilson 
402112ed2d3SChris Wilson 	return false;
403112ed2d3SChris Wilson }
404112ed2d3SChris Wilson 
405767662bcSRobert M. Fosha static bool ro_register(u32 reg)
406767662bcSRobert M. Fosha {
407767662bcSRobert M. Fosha 	if (reg & RING_FORCE_TO_NONPRIV_RD)
408767662bcSRobert M. Fosha 		return true;
409767662bcSRobert M. Fosha 
410767662bcSRobert M. Fosha 	return false;
411767662bcSRobert M. Fosha }
412767662bcSRobert M. Fosha 
413767662bcSRobert M. Fosha static int whitelist_writable_count(struct intel_engine_cs *engine)
414767662bcSRobert M. Fosha {
415767662bcSRobert M. Fosha 	int count = engine->whitelist.count;
416767662bcSRobert M. Fosha 	int i;
417767662bcSRobert M. Fosha 
418767662bcSRobert M. Fosha 	for (i = 0; i < engine->whitelist.count; i++) {
419767662bcSRobert M. Fosha 		u32 reg = i915_mmio_reg_offset(engine->whitelist.list[i].reg);
420767662bcSRobert M. Fosha 
421767662bcSRobert M. Fosha 		if (ro_register(reg))
422767662bcSRobert M. Fosha 			count--;
423767662bcSRobert M. Fosha 	}
424767662bcSRobert M. Fosha 
425767662bcSRobert M. Fosha 	return count;
426767662bcSRobert M. Fosha }
427767662bcSRobert M. Fosha 
428112ed2d3SChris Wilson static int check_dirty_whitelist(struct i915_gem_context *ctx,
429112ed2d3SChris Wilson 				 struct intel_engine_cs *engine)
430112ed2d3SChris Wilson {
431112ed2d3SChris Wilson 	const u32 values[] = {
432112ed2d3SChris Wilson 		0x00000000,
433112ed2d3SChris Wilson 		0x01010101,
434112ed2d3SChris Wilson 		0x10100101,
435112ed2d3SChris Wilson 		0x03030303,
436112ed2d3SChris Wilson 		0x30300303,
437112ed2d3SChris Wilson 		0x05050505,
438112ed2d3SChris Wilson 		0x50500505,
439112ed2d3SChris Wilson 		0x0f0f0f0f,
440112ed2d3SChris Wilson 		0xf00ff00f,
441112ed2d3SChris Wilson 		0x10101010,
442112ed2d3SChris Wilson 		0xf0f01010,
443112ed2d3SChris Wilson 		0x30303030,
444112ed2d3SChris Wilson 		0xa0a03030,
445112ed2d3SChris Wilson 		0x50505050,
446112ed2d3SChris Wilson 		0xc0c05050,
447112ed2d3SChris Wilson 		0xf0f0f0f0,
448112ed2d3SChris Wilson 		0x11111111,
449112ed2d3SChris Wilson 		0x33333333,
450112ed2d3SChris Wilson 		0x55555555,
451112ed2d3SChris Wilson 		0x0000ffff,
452112ed2d3SChris Wilson 		0x00ff00ff,
453112ed2d3SChris Wilson 		0xff0000ff,
454112ed2d3SChris Wilson 		0xffff00ff,
455112ed2d3SChris Wilson 		0xffffffff,
456112ed2d3SChris Wilson 	};
457112ed2d3SChris Wilson 	struct i915_vma *scratch;
458112ed2d3SChris Wilson 	struct i915_vma *batch;
459112ed2d3SChris Wilson 	int err = 0, i, v;
460112ed2d3SChris Wilson 	u32 *cs, *results;
461112ed2d3SChris Wilson 
462e568ac38SChris Wilson 	scratch = create_scratch(ctx->vm, 2 * ARRAY_SIZE(values) + 1);
463112ed2d3SChris Wilson 	if (IS_ERR(scratch))
464112ed2d3SChris Wilson 		return PTR_ERR(scratch);
465112ed2d3SChris Wilson 
466112ed2d3SChris Wilson 	batch = create_batch(ctx);
467112ed2d3SChris Wilson 	if (IS_ERR(batch)) {
468112ed2d3SChris Wilson 		err = PTR_ERR(batch);
469112ed2d3SChris Wilson 		goto out_scratch;
470112ed2d3SChris Wilson 	}
471112ed2d3SChris Wilson 
472112ed2d3SChris Wilson 	for (i = 0; i < engine->whitelist.count; i++) {
473112ed2d3SChris Wilson 		u32 reg = i915_mmio_reg_offset(engine->whitelist.list[i].reg);
474112ed2d3SChris Wilson 		u64 addr = scratch->node.start;
475112ed2d3SChris Wilson 		struct i915_request *rq;
476112ed2d3SChris Wilson 		u32 srm, lrm, rsvd;
477112ed2d3SChris Wilson 		u32 expect;
478112ed2d3SChris Wilson 		int idx;
479112ed2d3SChris Wilson 
480112ed2d3SChris Wilson 		if (wo_register(engine, reg))
481112ed2d3SChris Wilson 			continue;
482112ed2d3SChris Wilson 
483767662bcSRobert M. Fosha 		if (ro_register(reg))
484767662bcSRobert M. Fosha 			continue;
485767662bcSRobert M. Fosha 
486112ed2d3SChris Wilson 		srm = MI_STORE_REGISTER_MEM;
487112ed2d3SChris Wilson 		lrm = MI_LOAD_REGISTER_MEM;
488112ed2d3SChris Wilson 		if (INTEL_GEN(ctx->i915) >= 8)
489112ed2d3SChris Wilson 			lrm++, srm++;
490112ed2d3SChris Wilson 
491112ed2d3SChris Wilson 		pr_debug("%s: Writing garbage to %x\n",
492112ed2d3SChris Wilson 			 engine->name, reg);
493112ed2d3SChris Wilson 
494112ed2d3SChris Wilson 		cs = i915_gem_object_pin_map(batch->obj, I915_MAP_WC);
495112ed2d3SChris Wilson 		if (IS_ERR(cs)) {
496112ed2d3SChris Wilson 			err = PTR_ERR(cs);
497112ed2d3SChris Wilson 			goto out_batch;
498112ed2d3SChris Wilson 		}
499112ed2d3SChris Wilson 
500112ed2d3SChris Wilson 		/* SRM original */
501112ed2d3SChris Wilson 		*cs++ = srm;
502112ed2d3SChris Wilson 		*cs++ = reg;
503112ed2d3SChris Wilson 		*cs++ = lower_32_bits(addr);
504112ed2d3SChris Wilson 		*cs++ = upper_32_bits(addr);
505112ed2d3SChris Wilson 
506112ed2d3SChris Wilson 		idx = 1;
507112ed2d3SChris Wilson 		for (v = 0; v < ARRAY_SIZE(values); v++) {
508112ed2d3SChris Wilson 			/* LRI garbage */
509112ed2d3SChris Wilson 			*cs++ = MI_LOAD_REGISTER_IMM(1);
510112ed2d3SChris Wilson 			*cs++ = reg;
511112ed2d3SChris Wilson 			*cs++ = values[v];
512112ed2d3SChris Wilson 
513112ed2d3SChris Wilson 			/* SRM result */
514112ed2d3SChris Wilson 			*cs++ = srm;
515112ed2d3SChris Wilson 			*cs++ = reg;
516112ed2d3SChris Wilson 			*cs++ = lower_32_bits(addr + sizeof(u32) * idx);
517112ed2d3SChris Wilson 			*cs++ = upper_32_bits(addr + sizeof(u32) * idx);
518112ed2d3SChris Wilson 			idx++;
519112ed2d3SChris Wilson 		}
520112ed2d3SChris Wilson 		for (v = 0; v < ARRAY_SIZE(values); v++) {
521112ed2d3SChris Wilson 			/* LRI garbage */
522112ed2d3SChris Wilson 			*cs++ = MI_LOAD_REGISTER_IMM(1);
523112ed2d3SChris Wilson 			*cs++ = reg;
524112ed2d3SChris Wilson 			*cs++ = ~values[v];
525112ed2d3SChris Wilson 
526112ed2d3SChris Wilson 			/* SRM result */
527112ed2d3SChris Wilson 			*cs++ = srm;
528112ed2d3SChris Wilson 			*cs++ = reg;
529112ed2d3SChris Wilson 			*cs++ = lower_32_bits(addr + sizeof(u32) * idx);
530112ed2d3SChris Wilson 			*cs++ = upper_32_bits(addr + sizeof(u32) * idx);
531112ed2d3SChris Wilson 			idx++;
532112ed2d3SChris Wilson 		}
533112ed2d3SChris Wilson 		GEM_BUG_ON(idx * sizeof(u32) > scratch->size);
534112ed2d3SChris Wilson 
535112ed2d3SChris Wilson 		/* LRM original -- don't leave garbage in the context! */
536112ed2d3SChris Wilson 		*cs++ = lrm;
537112ed2d3SChris Wilson 		*cs++ = reg;
538112ed2d3SChris Wilson 		*cs++ = lower_32_bits(addr);
539112ed2d3SChris Wilson 		*cs++ = upper_32_bits(addr);
540112ed2d3SChris Wilson 
541112ed2d3SChris Wilson 		*cs++ = MI_BATCH_BUFFER_END;
542112ed2d3SChris Wilson 
543112ed2d3SChris Wilson 		i915_gem_object_flush_map(batch->obj);
544112ed2d3SChris Wilson 		i915_gem_object_unpin_map(batch->obj);
545112ed2d3SChris Wilson 		i915_gem_chipset_flush(ctx->i915);
546112ed2d3SChris Wilson 
54746472b3eSChris Wilson 		rq = igt_request_alloc(ctx, engine);
548112ed2d3SChris Wilson 		if (IS_ERR(rq)) {
549112ed2d3SChris Wilson 			err = PTR_ERR(rq);
550112ed2d3SChris Wilson 			goto out_batch;
551112ed2d3SChris Wilson 		}
552112ed2d3SChris Wilson 
553112ed2d3SChris Wilson 		if (engine->emit_init_breadcrumb) { /* Be nice if we hang */
554112ed2d3SChris Wilson 			err = engine->emit_init_breadcrumb(rq);
555112ed2d3SChris Wilson 			if (err)
556112ed2d3SChris Wilson 				goto err_request;
557112ed2d3SChris Wilson 		}
558112ed2d3SChris Wilson 
559112ed2d3SChris Wilson 		err = engine->emit_bb_start(rq,
560112ed2d3SChris Wilson 					    batch->node.start, PAGE_SIZE,
561112ed2d3SChris Wilson 					    0);
562112ed2d3SChris Wilson 		if (err)
563112ed2d3SChris Wilson 			goto err_request;
564112ed2d3SChris Wilson 
565112ed2d3SChris Wilson err_request:
566112ed2d3SChris Wilson 		i915_request_add(rq);
567112ed2d3SChris Wilson 		if (err)
568112ed2d3SChris Wilson 			goto out_batch;
569112ed2d3SChris Wilson 
570112ed2d3SChris Wilson 		if (i915_request_wait(rq, I915_WAIT_LOCKED, HZ / 5) < 0) {
571112ed2d3SChris Wilson 			pr_err("%s: Futzing %x timedout; cancelling test\n",
572112ed2d3SChris Wilson 			       engine->name, reg);
573112ed2d3SChris Wilson 			i915_gem_set_wedged(ctx->i915);
574112ed2d3SChris Wilson 			err = -EIO;
575112ed2d3SChris Wilson 			goto out_batch;
576112ed2d3SChris Wilson 		}
577112ed2d3SChris Wilson 
578112ed2d3SChris Wilson 		results = i915_gem_object_pin_map(scratch->obj, I915_MAP_WB);
579112ed2d3SChris Wilson 		if (IS_ERR(results)) {
580112ed2d3SChris Wilson 			err = PTR_ERR(results);
581112ed2d3SChris Wilson 			goto out_batch;
582112ed2d3SChris Wilson 		}
583112ed2d3SChris Wilson 
584112ed2d3SChris Wilson 		GEM_BUG_ON(values[ARRAY_SIZE(values) - 1] != 0xffffffff);
585112ed2d3SChris Wilson 		rsvd = results[ARRAY_SIZE(values)]; /* detect write masking */
586112ed2d3SChris Wilson 		if (!rsvd) {
587112ed2d3SChris Wilson 			pr_err("%s: Unable to write to whitelisted register %x\n",
588112ed2d3SChris Wilson 			       engine->name, reg);
589112ed2d3SChris Wilson 			err = -EINVAL;
590112ed2d3SChris Wilson 			goto out_unpin;
591112ed2d3SChris Wilson 		}
592112ed2d3SChris Wilson 
593112ed2d3SChris Wilson 		expect = results[0];
594112ed2d3SChris Wilson 		idx = 1;
595112ed2d3SChris Wilson 		for (v = 0; v < ARRAY_SIZE(values); v++) {
596112ed2d3SChris Wilson 			expect = reg_write(expect, values[v], rsvd);
597112ed2d3SChris Wilson 			if (results[idx] != expect)
598112ed2d3SChris Wilson 				err++;
599112ed2d3SChris Wilson 			idx++;
600112ed2d3SChris Wilson 		}
601112ed2d3SChris Wilson 		for (v = 0; v < ARRAY_SIZE(values); v++) {
602112ed2d3SChris Wilson 			expect = reg_write(expect, ~values[v], rsvd);
603112ed2d3SChris Wilson 			if (results[idx] != expect)
604112ed2d3SChris Wilson 				err++;
605112ed2d3SChris Wilson 			idx++;
606112ed2d3SChris Wilson 		}
607112ed2d3SChris Wilson 		if (err) {
608112ed2d3SChris Wilson 			pr_err("%s: %d mismatch between values written to whitelisted register [%x], and values read back!\n",
609112ed2d3SChris Wilson 			       engine->name, err, reg);
610112ed2d3SChris Wilson 
611112ed2d3SChris Wilson 			pr_info("%s: Whitelisted register: %x, original value %08x, rsvd %08x\n",
612112ed2d3SChris Wilson 				engine->name, reg, results[0], rsvd);
613112ed2d3SChris Wilson 
614112ed2d3SChris Wilson 			expect = results[0];
615112ed2d3SChris Wilson 			idx = 1;
616112ed2d3SChris Wilson 			for (v = 0; v < ARRAY_SIZE(values); v++) {
617112ed2d3SChris Wilson 				u32 w = values[v];
618112ed2d3SChris Wilson 
619112ed2d3SChris Wilson 				expect = reg_write(expect, w, rsvd);
620112ed2d3SChris Wilson 				pr_info("Wrote %08x, read %08x, expect %08x\n",
621112ed2d3SChris Wilson 					w, results[idx], expect);
622112ed2d3SChris Wilson 				idx++;
623112ed2d3SChris Wilson 			}
624112ed2d3SChris Wilson 			for (v = 0; v < ARRAY_SIZE(values); v++) {
625112ed2d3SChris Wilson 				u32 w = ~values[v];
626112ed2d3SChris Wilson 
627112ed2d3SChris Wilson 				expect = reg_write(expect, w, rsvd);
628112ed2d3SChris Wilson 				pr_info("Wrote %08x, read %08x, expect %08x\n",
629112ed2d3SChris Wilson 					w, results[idx], expect);
630112ed2d3SChris Wilson 				idx++;
631112ed2d3SChris Wilson 			}
632112ed2d3SChris Wilson 
633112ed2d3SChris Wilson 			err = -EINVAL;
634112ed2d3SChris Wilson 		}
635112ed2d3SChris Wilson out_unpin:
636112ed2d3SChris Wilson 		i915_gem_object_unpin_map(scratch->obj);
637112ed2d3SChris Wilson 		if (err)
638112ed2d3SChris Wilson 			break;
639112ed2d3SChris Wilson 	}
640112ed2d3SChris Wilson 
641112ed2d3SChris Wilson 	if (igt_flush_test(ctx->i915, I915_WAIT_LOCKED))
642112ed2d3SChris Wilson 		err = -EIO;
643112ed2d3SChris Wilson out_batch:
644112ed2d3SChris Wilson 	i915_vma_unpin_and_release(&batch, 0);
645112ed2d3SChris Wilson out_scratch:
646112ed2d3SChris Wilson 	i915_vma_unpin_and_release(&scratch, 0);
647112ed2d3SChris Wilson 	return err;
648112ed2d3SChris Wilson }
649112ed2d3SChris Wilson 
650112ed2d3SChris Wilson static int live_dirty_whitelist(void *arg)
651112ed2d3SChris Wilson {
652112ed2d3SChris Wilson 	struct drm_i915_private *i915 = arg;
653112ed2d3SChris Wilson 	struct intel_engine_cs *engine;
654112ed2d3SChris Wilson 	struct i915_gem_context *ctx;
655112ed2d3SChris Wilson 	enum intel_engine_id id;
656112ed2d3SChris Wilson 	intel_wakeref_t wakeref;
657112ed2d3SChris Wilson 	struct drm_file *file;
658112ed2d3SChris Wilson 	int err = 0;
659112ed2d3SChris Wilson 
660112ed2d3SChris Wilson 	/* Can the user write to the whitelisted registers? */
661112ed2d3SChris Wilson 
662112ed2d3SChris Wilson 	if (INTEL_GEN(i915) < 7) /* minimum requirement for LRI, SRM, LRM */
663112ed2d3SChris Wilson 		return 0;
664112ed2d3SChris Wilson 
665d858d569SDaniele Ceraolo Spurio 	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
666112ed2d3SChris Wilson 
667112ed2d3SChris Wilson 	mutex_unlock(&i915->drm.struct_mutex);
668112ed2d3SChris Wilson 	file = mock_file(i915);
669112ed2d3SChris Wilson 	mutex_lock(&i915->drm.struct_mutex);
670112ed2d3SChris Wilson 	if (IS_ERR(file)) {
671112ed2d3SChris Wilson 		err = PTR_ERR(file);
672112ed2d3SChris Wilson 		goto out_rpm;
673112ed2d3SChris Wilson 	}
674112ed2d3SChris Wilson 
675112ed2d3SChris Wilson 	ctx = live_context(i915, file);
676112ed2d3SChris Wilson 	if (IS_ERR(ctx)) {
677112ed2d3SChris Wilson 		err = PTR_ERR(ctx);
678112ed2d3SChris Wilson 		goto out_file;
679112ed2d3SChris Wilson 	}
680112ed2d3SChris Wilson 
681112ed2d3SChris Wilson 	for_each_engine(engine, i915, id) {
682112ed2d3SChris Wilson 		if (engine->whitelist.count == 0)
683112ed2d3SChris Wilson 			continue;
684112ed2d3SChris Wilson 
685112ed2d3SChris Wilson 		err = check_dirty_whitelist(ctx, engine);
686112ed2d3SChris Wilson 		if (err)
687112ed2d3SChris Wilson 			goto out_file;
688112ed2d3SChris Wilson 	}
689112ed2d3SChris Wilson 
690112ed2d3SChris Wilson out_file:
691112ed2d3SChris Wilson 	mutex_unlock(&i915->drm.struct_mutex);
692112ed2d3SChris Wilson 	mock_file_free(i915, file);
693112ed2d3SChris Wilson 	mutex_lock(&i915->drm.struct_mutex);
694112ed2d3SChris Wilson out_rpm:
695d858d569SDaniele Ceraolo Spurio 	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
696112ed2d3SChris Wilson 	return err;
697112ed2d3SChris Wilson }
698112ed2d3SChris Wilson 
699112ed2d3SChris Wilson static int live_reset_whitelist(void *arg)
700112ed2d3SChris Wilson {
701112ed2d3SChris Wilson 	struct drm_i915_private *i915 = arg;
702112ed2d3SChris Wilson 	struct intel_engine_cs *engine = i915->engine[RCS0];
703112ed2d3SChris Wilson 	int err = 0;
704112ed2d3SChris Wilson 
705112ed2d3SChris Wilson 	/* If we reset the gpu, we should not lose the RING_NONPRIV */
706112ed2d3SChris Wilson 
707112ed2d3SChris Wilson 	if (!engine || engine->whitelist.count == 0)
708112ed2d3SChris Wilson 		return 0;
709112ed2d3SChris Wilson 
710112ed2d3SChris Wilson 	igt_global_reset_lock(i915);
711112ed2d3SChris Wilson 
712112ed2d3SChris Wilson 	if (intel_has_reset_engine(i915)) {
713112ed2d3SChris Wilson 		err = check_whitelist_across_reset(engine,
714112ed2d3SChris Wilson 						   do_engine_reset,
715112ed2d3SChris Wilson 						   "engine");
716112ed2d3SChris Wilson 		if (err)
717112ed2d3SChris Wilson 			goto out;
718112ed2d3SChris Wilson 	}
719112ed2d3SChris Wilson 
720112ed2d3SChris Wilson 	if (intel_has_gpu_reset(i915)) {
721112ed2d3SChris Wilson 		err = check_whitelist_across_reset(engine,
722112ed2d3SChris Wilson 						   do_device_reset,
723112ed2d3SChris Wilson 						   "device");
724112ed2d3SChris Wilson 		if (err)
725112ed2d3SChris Wilson 			goto out;
726112ed2d3SChris Wilson 	}
727112ed2d3SChris Wilson 
728112ed2d3SChris Wilson out:
729112ed2d3SChris Wilson 	igt_global_reset_unlock(i915);
730112ed2d3SChris Wilson 	return err;
731112ed2d3SChris Wilson }
732112ed2d3SChris Wilson 
733112ed2d3SChris Wilson static int read_whitelisted_registers(struct i915_gem_context *ctx,
734112ed2d3SChris Wilson 				      struct intel_engine_cs *engine,
735112ed2d3SChris Wilson 				      struct i915_vma *results)
736112ed2d3SChris Wilson {
737112ed2d3SChris Wilson 	struct i915_request *rq;
738112ed2d3SChris Wilson 	int i, err = 0;
739112ed2d3SChris Wilson 	u32 srm, *cs;
740112ed2d3SChris Wilson 
74146472b3eSChris Wilson 	rq = igt_request_alloc(ctx, engine);
742112ed2d3SChris Wilson 	if (IS_ERR(rq))
743112ed2d3SChris Wilson 		return PTR_ERR(rq);
744112ed2d3SChris Wilson 
745112ed2d3SChris Wilson 	srm = MI_STORE_REGISTER_MEM;
746112ed2d3SChris Wilson 	if (INTEL_GEN(ctx->i915) >= 8)
747112ed2d3SChris Wilson 		srm++;
748112ed2d3SChris Wilson 
749112ed2d3SChris Wilson 	cs = intel_ring_begin(rq, 4 * engine->whitelist.count);
750112ed2d3SChris Wilson 	if (IS_ERR(cs)) {
751112ed2d3SChris Wilson 		err = PTR_ERR(cs);
752112ed2d3SChris Wilson 		goto err_req;
753112ed2d3SChris Wilson 	}
754112ed2d3SChris Wilson 
755112ed2d3SChris Wilson 	for (i = 0; i < engine->whitelist.count; i++) {
756112ed2d3SChris Wilson 		u64 offset = results->node.start + sizeof(u32) * i;
757767662bcSRobert M. Fosha 		u32 reg = i915_mmio_reg_offset(engine->whitelist.list[i].reg);
758767662bcSRobert M. Fosha 
759767662bcSRobert M. Fosha 		/* Clear RD only and WR only flags */
760767662bcSRobert M. Fosha 		reg &= ~(RING_FORCE_TO_NONPRIV_RD | RING_FORCE_TO_NONPRIV_WR);
761112ed2d3SChris Wilson 
762112ed2d3SChris Wilson 		*cs++ = srm;
763767662bcSRobert M. Fosha 		*cs++ = reg;
764112ed2d3SChris Wilson 		*cs++ = lower_32_bits(offset);
765112ed2d3SChris Wilson 		*cs++ = upper_32_bits(offset);
766112ed2d3SChris Wilson 	}
767112ed2d3SChris Wilson 	intel_ring_advance(rq, cs);
768112ed2d3SChris Wilson 
769112ed2d3SChris Wilson err_req:
770112ed2d3SChris Wilson 	i915_request_add(rq);
771112ed2d3SChris Wilson 
772112ed2d3SChris Wilson 	if (i915_request_wait(rq, I915_WAIT_LOCKED, HZ / 5) < 0)
773112ed2d3SChris Wilson 		err = -EIO;
774112ed2d3SChris Wilson 
775112ed2d3SChris Wilson 	return err;
776112ed2d3SChris Wilson }
777112ed2d3SChris Wilson 
778112ed2d3SChris Wilson static int scrub_whitelisted_registers(struct i915_gem_context *ctx,
779112ed2d3SChris Wilson 				       struct intel_engine_cs *engine)
780112ed2d3SChris Wilson {
781112ed2d3SChris Wilson 	struct i915_request *rq;
782112ed2d3SChris Wilson 	struct i915_vma *batch;
783112ed2d3SChris Wilson 	int i, err = 0;
784112ed2d3SChris Wilson 	u32 *cs;
785112ed2d3SChris Wilson 
786112ed2d3SChris Wilson 	batch = create_batch(ctx);
787112ed2d3SChris Wilson 	if (IS_ERR(batch))
788112ed2d3SChris Wilson 		return PTR_ERR(batch);
789112ed2d3SChris Wilson 
790112ed2d3SChris Wilson 	cs = i915_gem_object_pin_map(batch->obj, I915_MAP_WC);
791112ed2d3SChris Wilson 	if (IS_ERR(cs)) {
792112ed2d3SChris Wilson 		err = PTR_ERR(cs);
793112ed2d3SChris Wilson 		goto err_batch;
794112ed2d3SChris Wilson 	}
795112ed2d3SChris Wilson 
796767662bcSRobert M. Fosha 	*cs++ = MI_LOAD_REGISTER_IMM(whitelist_writable_count(engine));
797112ed2d3SChris Wilson 	for (i = 0; i < engine->whitelist.count; i++) {
798767662bcSRobert M. Fosha 		u32 reg = i915_mmio_reg_offset(engine->whitelist.list[i].reg);
799767662bcSRobert M. Fosha 
800767662bcSRobert M. Fosha 		if (ro_register(reg))
801767662bcSRobert M. Fosha 			continue;
802767662bcSRobert M. Fosha 
803767662bcSRobert M. Fosha 		*cs++ = reg;
804112ed2d3SChris Wilson 		*cs++ = 0xffffffff;
805112ed2d3SChris Wilson 	}
806112ed2d3SChris Wilson 	*cs++ = MI_BATCH_BUFFER_END;
807112ed2d3SChris Wilson 
808112ed2d3SChris Wilson 	i915_gem_object_flush_map(batch->obj);
809112ed2d3SChris Wilson 	i915_gem_chipset_flush(ctx->i915);
810112ed2d3SChris Wilson 
81146472b3eSChris Wilson 	rq = igt_request_alloc(ctx, engine);
812112ed2d3SChris Wilson 	if (IS_ERR(rq)) {
813112ed2d3SChris Wilson 		err = PTR_ERR(rq);
814112ed2d3SChris Wilson 		goto err_unpin;
815112ed2d3SChris Wilson 	}
816112ed2d3SChris Wilson 
817112ed2d3SChris Wilson 	if (engine->emit_init_breadcrumb) { /* Be nice if we hang */
818112ed2d3SChris Wilson 		err = engine->emit_init_breadcrumb(rq);
819112ed2d3SChris Wilson 		if (err)
820112ed2d3SChris Wilson 			goto err_request;
821112ed2d3SChris Wilson 	}
822112ed2d3SChris Wilson 
823112ed2d3SChris Wilson 	/* Perform the writes from an unprivileged "user" batch */
824112ed2d3SChris Wilson 	err = engine->emit_bb_start(rq, batch->node.start, 0, 0);
825112ed2d3SChris Wilson 
826112ed2d3SChris Wilson err_request:
827112ed2d3SChris Wilson 	i915_request_add(rq);
828112ed2d3SChris Wilson 	if (i915_request_wait(rq, I915_WAIT_LOCKED, HZ / 5) < 0)
829112ed2d3SChris Wilson 		err = -EIO;
830112ed2d3SChris Wilson 
831112ed2d3SChris Wilson err_unpin:
832112ed2d3SChris Wilson 	i915_gem_object_unpin_map(batch->obj);
833112ed2d3SChris Wilson err_batch:
834112ed2d3SChris Wilson 	i915_vma_unpin_and_release(&batch, 0);
835112ed2d3SChris Wilson 	return err;
836112ed2d3SChris Wilson }
837112ed2d3SChris Wilson 
838112ed2d3SChris Wilson struct regmask {
839112ed2d3SChris Wilson 	i915_reg_t reg;
840112ed2d3SChris Wilson 	unsigned long gen_mask;
841112ed2d3SChris Wilson };
842112ed2d3SChris Wilson 
843112ed2d3SChris Wilson static bool find_reg(struct drm_i915_private *i915,
844112ed2d3SChris Wilson 		     i915_reg_t reg,
845112ed2d3SChris Wilson 		     const struct regmask *tbl,
846112ed2d3SChris Wilson 		     unsigned long count)
847112ed2d3SChris Wilson {
848112ed2d3SChris Wilson 	u32 offset = i915_mmio_reg_offset(reg);
849112ed2d3SChris Wilson 
850112ed2d3SChris Wilson 	while (count--) {
851112ed2d3SChris Wilson 		if (INTEL_INFO(i915)->gen_mask & tbl->gen_mask &&
852112ed2d3SChris Wilson 		    i915_mmio_reg_offset(tbl->reg) == offset)
853112ed2d3SChris Wilson 			return true;
854112ed2d3SChris Wilson 		tbl++;
855112ed2d3SChris Wilson 	}
856112ed2d3SChris Wilson 
857112ed2d3SChris Wilson 	return false;
858112ed2d3SChris Wilson }
859112ed2d3SChris Wilson 
860112ed2d3SChris Wilson static bool pardon_reg(struct drm_i915_private *i915, i915_reg_t reg)
861112ed2d3SChris Wilson {
862112ed2d3SChris Wilson 	/* Alas, we must pardon some whitelists. Mistakes already made */
863112ed2d3SChris Wilson 	static const struct regmask pardon[] = {
864112ed2d3SChris Wilson 		{ GEN9_CTX_PREEMPT_REG, INTEL_GEN_MASK(9, 9) },
865112ed2d3SChris Wilson 		{ GEN8_L3SQCREG4, INTEL_GEN_MASK(9, 9) },
866112ed2d3SChris Wilson 	};
867112ed2d3SChris Wilson 
868112ed2d3SChris Wilson 	return find_reg(i915, reg, pardon, ARRAY_SIZE(pardon));
869112ed2d3SChris Wilson }
870112ed2d3SChris Wilson 
871112ed2d3SChris Wilson static bool result_eq(struct intel_engine_cs *engine,
872112ed2d3SChris Wilson 		      u32 a, u32 b, i915_reg_t reg)
873112ed2d3SChris Wilson {
874112ed2d3SChris Wilson 	if (a != b && !pardon_reg(engine->i915, reg)) {
875112ed2d3SChris Wilson 		pr_err("Whitelisted register 0x%4x not context saved: A=%08x, B=%08x\n",
876112ed2d3SChris Wilson 		       i915_mmio_reg_offset(reg), a, b);
877112ed2d3SChris Wilson 		return false;
878112ed2d3SChris Wilson 	}
879112ed2d3SChris Wilson 
880112ed2d3SChris Wilson 	return true;
881112ed2d3SChris Wilson }
882112ed2d3SChris Wilson 
883112ed2d3SChris Wilson static bool writeonly_reg(struct drm_i915_private *i915, i915_reg_t reg)
884112ed2d3SChris Wilson {
885112ed2d3SChris Wilson 	/* Some registers do not seem to behave and our writes unreadable */
886112ed2d3SChris Wilson 	static const struct regmask wo[] = {
887112ed2d3SChris Wilson 		{ GEN9_SLICE_COMMON_ECO_CHICKEN1, INTEL_GEN_MASK(9, 9) },
888112ed2d3SChris Wilson 	};
889112ed2d3SChris Wilson 
890112ed2d3SChris Wilson 	return find_reg(i915, reg, wo, ARRAY_SIZE(wo));
891112ed2d3SChris Wilson }
892112ed2d3SChris Wilson 
893112ed2d3SChris Wilson static bool result_neq(struct intel_engine_cs *engine,
894112ed2d3SChris Wilson 		       u32 a, u32 b, i915_reg_t reg)
895112ed2d3SChris Wilson {
896112ed2d3SChris Wilson 	if (a == b && !writeonly_reg(engine->i915, reg)) {
897112ed2d3SChris Wilson 		pr_err("Whitelist register 0x%4x:%08x was unwritable\n",
898112ed2d3SChris Wilson 		       i915_mmio_reg_offset(reg), a);
899112ed2d3SChris Wilson 		return false;
900112ed2d3SChris Wilson 	}
901112ed2d3SChris Wilson 
902112ed2d3SChris Wilson 	return true;
903112ed2d3SChris Wilson }
904112ed2d3SChris Wilson 
905112ed2d3SChris Wilson static int
906112ed2d3SChris Wilson check_whitelisted_registers(struct intel_engine_cs *engine,
907112ed2d3SChris Wilson 			    struct i915_vma *A,
908112ed2d3SChris Wilson 			    struct i915_vma *B,
909112ed2d3SChris Wilson 			    bool (*fn)(struct intel_engine_cs *engine,
910112ed2d3SChris Wilson 				       u32 a, u32 b,
911112ed2d3SChris Wilson 				       i915_reg_t reg))
912112ed2d3SChris Wilson {
913112ed2d3SChris Wilson 	u32 *a, *b;
914112ed2d3SChris Wilson 	int i, err;
915112ed2d3SChris Wilson 
916112ed2d3SChris Wilson 	a = i915_gem_object_pin_map(A->obj, I915_MAP_WB);
917112ed2d3SChris Wilson 	if (IS_ERR(a))
918112ed2d3SChris Wilson 		return PTR_ERR(a);
919112ed2d3SChris Wilson 
920112ed2d3SChris Wilson 	b = i915_gem_object_pin_map(B->obj, I915_MAP_WB);
921112ed2d3SChris Wilson 	if (IS_ERR(b)) {
922112ed2d3SChris Wilson 		err = PTR_ERR(b);
923112ed2d3SChris Wilson 		goto err_a;
924112ed2d3SChris Wilson 	}
925112ed2d3SChris Wilson 
926112ed2d3SChris Wilson 	err = 0;
927112ed2d3SChris Wilson 	for (i = 0; i < engine->whitelist.count; i++) {
928112ed2d3SChris Wilson 		if (!fn(engine, a[i], b[i], engine->whitelist.list[i].reg))
929112ed2d3SChris Wilson 			err = -EINVAL;
930112ed2d3SChris Wilson 	}
931112ed2d3SChris Wilson 
932112ed2d3SChris Wilson 	i915_gem_object_unpin_map(B->obj);
933112ed2d3SChris Wilson err_a:
934112ed2d3SChris Wilson 	i915_gem_object_unpin_map(A->obj);
935112ed2d3SChris Wilson 	return err;
936112ed2d3SChris Wilson }
937112ed2d3SChris Wilson 
938112ed2d3SChris Wilson static int live_isolated_whitelist(void *arg)
939112ed2d3SChris Wilson {
940112ed2d3SChris Wilson 	struct drm_i915_private *i915 = arg;
941112ed2d3SChris Wilson 	struct {
942112ed2d3SChris Wilson 		struct i915_gem_context *ctx;
943112ed2d3SChris Wilson 		struct i915_vma *scratch[2];
944112ed2d3SChris Wilson 	} client[2] = {};
945112ed2d3SChris Wilson 	struct intel_engine_cs *engine;
946112ed2d3SChris Wilson 	enum intel_engine_id id;
947112ed2d3SChris Wilson 	int i, err = 0;
948112ed2d3SChris Wilson 
949112ed2d3SChris Wilson 	/*
950112ed2d3SChris Wilson 	 * Check that a write into a whitelist register works, but
951112ed2d3SChris Wilson 	 * invisible to a second context.
952112ed2d3SChris Wilson 	 */
953112ed2d3SChris Wilson 
954112ed2d3SChris Wilson 	if (!intel_engines_has_context_isolation(i915))
955112ed2d3SChris Wilson 		return 0;
956112ed2d3SChris Wilson 
957e568ac38SChris Wilson 	if (!i915->kernel_context->vm)
958112ed2d3SChris Wilson 		return 0;
959112ed2d3SChris Wilson 
960112ed2d3SChris Wilson 	for (i = 0; i < ARRAY_SIZE(client); i++) {
961112ed2d3SChris Wilson 		struct i915_gem_context *c;
962112ed2d3SChris Wilson 
963112ed2d3SChris Wilson 		c = kernel_context(i915);
964112ed2d3SChris Wilson 		if (IS_ERR(c)) {
965112ed2d3SChris Wilson 			err = PTR_ERR(c);
966112ed2d3SChris Wilson 			goto err;
967112ed2d3SChris Wilson 		}
968112ed2d3SChris Wilson 
969e568ac38SChris Wilson 		client[i].scratch[0] = create_scratch(c->vm, 1024);
970112ed2d3SChris Wilson 		if (IS_ERR(client[i].scratch[0])) {
971112ed2d3SChris Wilson 			err = PTR_ERR(client[i].scratch[0]);
972112ed2d3SChris Wilson 			kernel_context_close(c);
973112ed2d3SChris Wilson 			goto err;
974112ed2d3SChris Wilson 		}
975112ed2d3SChris Wilson 
976e568ac38SChris Wilson 		client[i].scratch[1] = create_scratch(c->vm, 1024);
977112ed2d3SChris Wilson 		if (IS_ERR(client[i].scratch[1])) {
978112ed2d3SChris Wilson 			err = PTR_ERR(client[i].scratch[1]);
979112ed2d3SChris Wilson 			i915_vma_unpin_and_release(&client[i].scratch[0], 0);
980112ed2d3SChris Wilson 			kernel_context_close(c);
981112ed2d3SChris Wilson 			goto err;
982112ed2d3SChris Wilson 		}
983112ed2d3SChris Wilson 
984112ed2d3SChris Wilson 		client[i].ctx = c;
985112ed2d3SChris Wilson 	}
986112ed2d3SChris Wilson 
987112ed2d3SChris Wilson 	for_each_engine(engine, i915, id) {
988767662bcSRobert M. Fosha 		if (!whitelist_writable_count(engine))
989112ed2d3SChris Wilson 			continue;
990112ed2d3SChris Wilson 
991112ed2d3SChris Wilson 		/* Read default values */
992112ed2d3SChris Wilson 		err = read_whitelisted_registers(client[0].ctx, engine,
993112ed2d3SChris Wilson 						 client[0].scratch[0]);
994112ed2d3SChris Wilson 		if (err)
995112ed2d3SChris Wilson 			goto err;
996112ed2d3SChris Wilson 
997112ed2d3SChris Wilson 		/* Try to overwrite registers (should only affect ctx0) */
998112ed2d3SChris Wilson 		err = scrub_whitelisted_registers(client[0].ctx, engine);
999112ed2d3SChris Wilson 		if (err)
1000112ed2d3SChris Wilson 			goto err;
1001112ed2d3SChris Wilson 
1002112ed2d3SChris Wilson 		/* Read values from ctx1, we expect these to be defaults */
1003112ed2d3SChris Wilson 		err = read_whitelisted_registers(client[1].ctx, engine,
1004112ed2d3SChris Wilson 						 client[1].scratch[0]);
1005112ed2d3SChris Wilson 		if (err)
1006112ed2d3SChris Wilson 			goto err;
1007112ed2d3SChris Wilson 
1008112ed2d3SChris Wilson 		/* Verify that both reads return the same default values */
1009112ed2d3SChris Wilson 		err = check_whitelisted_registers(engine,
1010112ed2d3SChris Wilson 						  client[0].scratch[0],
1011112ed2d3SChris Wilson 						  client[1].scratch[0],
1012112ed2d3SChris Wilson 						  result_eq);
1013112ed2d3SChris Wilson 		if (err)
1014112ed2d3SChris Wilson 			goto err;
1015112ed2d3SChris Wilson 
1016112ed2d3SChris Wilson 		/* Read back the updated values in ctx0 */
1017112ed2d3SChris Wilson 		err = read_whitelisted_registers(client[0].ctx, engine,
1018112ed2d3SChris Wilson 						 client[0].scratch[1]);
1019112ed2d3SChris Wilson 		if (err)
1020112ed2d3SChris Wilson 			goto err;
1021112ed2d3SChris Wilson 
1022112ed2d3SChris Wilson 		/* User should be granted privilege to overwhite regs */
1023112ed2d3SChris Wilson 		err = check_whitelisted_registers(engine,
1024112ed2d3SChris Wilson 						  client[0].scratch[0],
1025112ed2d3SChris Wilson 						  client[0].scratch[1],
1026112ed2d3SChris Wilson 						  result_neq);
1027112ed2d3SChris Wilson 		if (err)
1028112ed2d3SChris Wilson 			goto err;
1029112ed2d3SChris Wilson 	}
1030112ed2d3SChris Wilson 
1031112ed2d3SChris Wilson err:
1032112ed2d3SChris Wilson 	for (i = 0; i < ARRAY_SIZE(client); i++) {
1033112ed2d3SChris Wilson 		if (!client[i].ctx)
1034112ed2d3SChris Wilson 			break;
1035112ed2d3SChris Wilson 
1036112ed2d3SChris Wilson 		i915_vma_unpin_and_release(&client[i].scratch[1], 0);
1037112ed2d3SChris Wilson 		i915_vma_unpin_and_release(&client[i].scratch[0], 0);
1038112ed2d3SChris Wilson 		kernel_context_close(client[i].ctx);
1039112ed2d3SChris Wilson 	}
1040112ed2d3SChris Wilson 
1041112ed2d3SChris Wilson 	if (igt_flush_test(i915, I915_WAIT_LOCKED))
1042112ed2d3SChris Wilson 		err = -EIO;
1043112ed2d3SChris Wilson 
1044112ed2d3SChris Wilson 	return err;
1045112ed2d3SChris Wilson }
1046112ed2d3SChris Wilson 
1047fde93886STvrtko Ursulin static bool
1048fde93886STvrtko Ursulin verify_wa_lists(struct i915_gem_context *ctx, struct wa_lists *lists,
1049fde93886STvrtko Ursulin 		const char *str)
1050112ed2d3SChris Wilson {
1051fde93886STvrtko Ursulin 	struct drm_i915_private *i915 = ctx->i915;
1052fde93886STvrtko Ursulin 	struct i915_gem_engines_iter it;
1053fde93886STvrtko Ursulin 	struct intel_context *ce;
1054112ed2d3SChris Wilson 	bool ok = true;
1055112ed2d3SChris Wilson 
1056112ed2d3SChris Wilson 	ok &= wa_list_verify(&i915->uncore, &lists->gt_wa_list, str);
1057112ed2d3SChris Wilson 
1058fde93886STvrtko Ursulin 	for_each_gem_engine(ce, i915_gem_context_lock_engines(ctx), it) {
1059fde93886STvrtko Ursulin 		enum intel_engine_id id = ce->engine->id;
1060fde93886STvrtko Ursulin 
1061fde93886STvrtko Ursulin 		ok &= engine_wa_list_verify(ce,
1062112ed2d3SChris Wilson 					    &lists->engine[id].wa_list,
1063112ed2d3SChris Wilson 					    str) == 0;
1064fde93886STvrtko Ursulin 
1065fde93886STvrtko Ursulin 		ok &= engine_wa_list_verify(ce,
1066fde93886STvrtko Ursulin 					    &lists->engine[id].ctx_wa_list,
1067fde93886STvrtko Ursulin 					    str) == 0;
1068112ed2d3SChris Wilson 	}
1069fde93886STvrtko Ursulin 	i915_gem_context_unlock_engines(ctx);
1070112ed2d3SChris Wilson 
1071112ed2d3SChris Wilson 	return ok;
1072112ed2d3SChris Wilson }
1073112ed2d3SChris Wilson 
1074112ed2d3SChris Wilson static int
1075fde93886STvrtko Ursulin live_gpu_reset_workarounds(void *arg)
1076112ed2d3SChris Wilson {
1077112ed2d3SChris Wilson 	struct drm_i915_private *i915 = arg;
1078fde93886STvrtko Ursulin 	struct i915_gem_context *ctx;
1079112ed2d3SChris Wilson 	intel_wakeref_t wakeref;
1080112ed2d3SChris Wilson 	struct wa_lists lists;
1081112ed2d3SChris Wilson 	bool ok;
1082112ed2d3SChris Wilson 
1083112ed2d3SChris Wilson 	if (!intel_has_gpu_reset(i915))
1084112ed2d3SChris Wilson 		return 0;
1085112ed2d3SChris Wilson 
1086fde93886STvrtko Ursulin 	ctx = kernel_context(i915);
1087fde93886STvrtko Ursulin 	if (IS_ERR(ctx))
1088fde93886STvrtko Ursulin 		return PTR_ERR(ctx);
1089fde93886STvrtko Ursulin 
1090112ed2d3SChris Wilson 	pr_info("Verifying after GPU reset...\n");
1091112ed2d3SChris Wilson 
1092112ed2d3SChris Wilson 	igt_global_reset_lock(i915);
1093d858d569SDaniele Ceraolo Spurio 	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
1094112ed2d3SChris Wilson 
1095112ed2d3SChris Wilson 	reference_lists_init(i915, &lists);
1096112ed2d3SChris Wilson 
1097fde93886STvrtko Ursulin 	ok = verify_wa_lists(ctx, &lists, "before reset");
1098112ed2d3SChris Wilson 	if (!ok)
1099112ed2d3SChris Wilson 		goto out;
1100112ed2d3SChris Wilson 
1101112ed2d3SChris Wilson 	i915_reset(i915, ALL_ENGINES, "live_workarounds");
1102112ed2d3SChris Wilson 
1103fde93886STvrtko Ursulin 	ok = verify_wa_lists(ctx, &lists, "after reset");
1104112ed2d3SChris Wilson 
1105112ed2d3SChris Wilson out:
1106fde93886STvrtko Ursulin 	kernel_context_close(ctx);
1107112ed2d3SChris Wilson 	reference_lists_fini(i915, &lists);
1108d858d569SDaniele Ceraolo Spurio 	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
1109112ed2d3SChris Wilson 	igt_global_reset_unlock(i915);
1110112ed2d3SChris Wilson 
1111112ed2d3SChris Wilson 	return ok ? 0 : -ESRCH;
1112112ed2d3SChris Wilson }
1113112ed2d3SChris Wilson 
1114112ed2d3SChris Wilson static int
1115fde93886STvrtko Ursulin live_engine_reset_workarounds(void *arg)
1116112ed2d3SChris Wilson {
1117112ed2d3SChris Wilson 	struct drm_i915_private *i915 = arg;
1118112ed2d3SChris Wilson 	struct intel_engine_cs *engine;
1119112ed2d3SChris Wilson 	struct i915_gem_context *ctx;
1120112ed2d3SChris Wilson 	struct igt_spinner spin;
1121112ed2d3SChris Wilson 	enum intel_engine_id id;
1122112ed2d3SChris Wilson 	struct i915_request *rq;
1123112ed2d3SChris Wilson 	intel_wakeref_t wakeref;
1124112ed2d3SChris Wilson 	struct wa_lists lists;
1125112ed2d3SChris Wilson 	int ret = 0;
1126112ed2d3SChris Wilson 
1127112ed2d3SChris Wilson 	if (!intel_has_reset_engine(i915))
1128112ed2d3SChris Wilson 		return 0;
1129112ed2d3SChris Wilson 
1130112ed2d3SChris Wilson 	ctx = kernel_context(i915);
1131112ed2d3SChris Wilson 	if (IS_ERR(ctx))
1132112ed2d3SChris Wilson 		return PTR_ERR(ctx);
1133112ed2d3SChris Wilson 
1134112ed2d3SChris Wilson 	igt_global_reset_lock(i915);
1135d858d569SDaniele Ceraolo Spurio 	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
1136112ed2d3SChris Wilson 
1137112ed2d3SChris Wilson 	reference_lists_init(i915, &lists);
1138112ed2d3SChris Wilson 
1139112ed2d3SChris Wilson 	for_each_engine(engine, i915, id) {
1140112ed2d3SChris Wilson 		bool ok;
1141112ed2d3SChris Wilson 
1142112ed2d3SChris Wilson 		pr_info("Verifying after %s reset...\n", engine->name);
1143112ed2d3SChris Wilson 
1144fde93886STvrtko Ursulin 		ok = verify_wa_lists(ctx, &lists, "before reset");
1145112ed2d3SChris Wilson 		if (!ok) {
1146112ed2d3SChris Wilson 			ret = -ESRCH;
1147112ed2d3SChris Wilson 			goto err;
1148112ed2d3SChris Wilson 		}
1149112ed2d3SChris Wilson 
1150112ed2d3SChris Wilson 		i915_reset_engine(engine, "live_workarounds");
1151112ed2d3SChris Wilson 
1152fde93886STvrtko Ursulin 		ok = verify_wa_lists(ctx, &lists, "after idle reset");
1153112ed2d3SChris Wilson 		if (!ok) {
1154112ed2d3SChris Wilson 			ret = -ESRCH;
1155112ed2d3SChris Wilson 			goto err;
1156112ed2d3SChris Wilson 		}
1157112ed2d3SChris Wilson 
1158112ed2d3SChris Wilson 		ret = igt_spinner_init(&spin, i915);
1159112ed2d3SChris Wilson 		if (ret)
1160112ed2d3SChris Wilson 			goto err;
1161112ed2d3SChris Wilson 
1162112ed2d3SChris Wilson 		rq = igt_spinner_create_request(&spin, ctx, engine, MI_NOOP);
1163112ed2d3SChris Wilson 		if (IS_ERR(rq)) {
1164112ed2d3SChris Wilson 			ret = PTR_ERR(rq);
1165112ed2d3SChris Wilson 			igt_spinner_fini(&spin);
1166112ed2d3SChris Wilson 			goto err;
1167112ed2d3SChris Wilson 		}
1168112ed2d3SChris Wilson 
1169112ed2d3SChris Wilson 		i915_request_add(rq);
1170112ed2d3SChris Wilson 
1171112ed2d3SChris Wilson 		if (!igt_wait_for_spinner(&spin, rq)) {
1172112ed2d3SChris Wilson 			pr_err("Spinner failed to start\n");
1173112ed2d3SChris Wilson 			igt_spinner_fini(&spin);
1174112ed2d3SChris Wilson 			ret = -ETIMEDOUT;
1175112ed2d3SChris Wilson 			goto err;
1176112ed2d3SChris Wilson 		}
1177112ed2d3SChris Wilson 
1178112ed2d3SChris Wilson 		i915_reset_engine(engine, "live_workarounds");
1179112ed2d3SChris Wilson 
1180112ed2d3SChris Wilson 		igt_spinner_end(&spin);
1181112ed2d3SChris Wilson 		igt_spinner_fini(&spin);
1182112ed2d3SChris Wilson 
1183fde93886STvrtko Ursulin 		ok = verify_wa_lists(ctx, &lists, "after busy reset");
1184112ed2d3SChris Wilson 		if (!ok) {
1185112ed2d3SChris Wilson 			ret = -ESRCH;
1186112ed2d3SChris Wilson 			goto err;
1187112ed2d3SChris Wilson 		}
1188112ed2d3SChris Wilson 	}
1189112ed2d3SChris Wilson 
1190112ed2d3SChris Wilson err:
1191112ed2d3SChris Wilson 	reference_lists_fini(i915, &lists);
1192d858d569SDaniele Ceraolo Spurio 	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
1193112ed2d3SChris Wilson 	igt_global_reset_unlock(i915);
1194112ed2d3SChris Wilson 	kernel_context_close(ctx);
1195112ed2d3SChris Wilson 
1196112ed2d3SChris Wilson 	igt_flush_test(i915, I915_WAIT_LOCKED);
1197112ed2d3SChris Wilson 
1198112ed2d3SChris Wilson 	return ret;
1199112ed2d3SChris Wilson }
1200112ed2d3SChris Wilson 
1201112ed2d3SChris Wilson int intel_workarounds_live_selftests(struct drm_i915_private *i915)
1202112ed2d3SChris Wilson {
1203112ed2d3SChris Wilson 	static const struct i915_subtest tests[] = {
1204112ed2d3SChris Wilson 		SUBTEST(live_dirty_whitelist),
1205112ed2d3SChris Wilson 		SUBTEST(live_reset_whitelist),
1206112ed2d3SChris Wilson 		SUBTEST(live_isolated_whitelist),
1207fde93886STvrtko Ursulin 		SUBTEST(live_gpu_reset_workarounds),
1208fde93886STvrtko Ursulin 		SUBTEST(live_engine_reset_workarounds),
1209112ed2d3SChris Wilson 	};
1210112ed2d3SChris Wilson 	int err;
1211112ed2d3SChris Wilson 
1212112ed2d3SChris Wilson 	if (i915_terminally_wedged(i915))
1213112ed2d3SChris Wilson 		return 0;
1214112ed2d3SChris Wilson 
1215112ed2d3SChris Wilson 	mutex_lock(&i915->drm.struct_mutex);
1216112ed2d3SChris Wilson 	err = i915_subtests(tests, i915);
1217112ed2d3SChris Wilson 	mutex_unlock(&i915->drm.struct_mutex);
1218112ed2d3SChris Wilson 
1219112ed2d3SChris Wilson 	return err;
1220112ed2d3SChris Wilson }
1221