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"
8750e76b4SChris Wilson #include "gt/intel_engine_user.h"
9baea429dSTvrtko Ursulin #include "gt/intel_gt.h"
10112ed2d3SChris Wilson #include "i915_selftest.h"
11112ed2d3SChris Wilson #include "intel_reset.h"
12112ed2d3SChris Wilson 
13112ed2d3SChris Wilson #include "selftests/igt_flush_test.h"
14112ed2d3SChris Wilson #include "selftests/igt_reset.h"
15112ed2d3SChris Wilson #include "selftests/igt_spinner.h"
16112ed2d3SChris Wilson #include "selftests/mock_drm.h"
17112ed2d3SChris Wilson 
1810be98a7SChris Wilson #include "gem/selftests/igt_gem_utils.h"
1910be98a7SChris Wilson #include "gem/selftests/mock_context.h"
2010be98a7SChris Wilson 
21112ed2d3SChris Wilson static const struct wo_register {
22112ed2d3SChris Wilson 	enum intel_platform platform;
23112ed2d3SChris Wilson 	u32 reg;
24112ed2d3SChris Wilson } wo_registers[] = {
25112ed2d3SChris Wilson 	{ INTEL_GEMINILAKE, 0x731c }
26112ed2d3SChris Wilson };
27112ed2d3SChris Wilson 
28112ed2d3SChris Wilson struct wa_lists {
29112ed2d3SChris Wilson 	struct i915_wa_list gt_wa_list;
30112ed2d3SChris Wilson 	struct {
31112ed2d3SChris Wilson 		struct i915_wa_list wa_list;
32fde93886STvrtko Ursulin 		struct i915_wa_list ctx_wa_list;
33112ed2d3SChris Wilson 	} engine[I915_NUM_ENGINES];
34112ed2d3SChris Wilson };
35112ed2d3SChris Wilson 
36112ed2d3SChris Wilson static void
37112ed2d3SChris Wilson reference_lists_init(struct drm_i915_private *i915, struct wa_lists *lists)
38112ed2d3SChris Wilson {
39112ed2d3SChris Wilson 	struct intel_engine_cs *engine;
40112ed2d3SChris Wilson 	enum intel_engine_id id;
41112ed2d3SChris Wilson 
42112ed2d3SChris Wilson 	memset(lists, 0, sizeof(*lists));
43112ed2d3SChris Wilson 
443e1f0a51SJohn Harrison 	wa_init_start(&lists->gt_wa_list, "GT_REF", "global");
45112ed2d3SChris Wilson 	gt_init_workarounds(i915, &lists->gt_wa_list);
46112ed2d3SChris Wilson 	wa_init_finish(&lists->gt_wa_list);
47112ed2d3SChris Wilson 
48112ed2d3SChris Wilson 	for_each_engine(engine, i915, id) {
49112ed2d3SChris Wilson 		struct i915_wa_list *wal = &lists->engine[id].wa_list;
50112ed2d3SChris Wilson 
513e1f0a51SJohn Harrison 		wa_init_start(wal, "REF", engine->name);
52112ed2d3SChris Wilson 		engine_init_workarounds(engine, wal);
53112ed2d3SChris Wilson 		wa_init_finish(wal);
54fde93886STvrtko Ursulin 
55fde93886STvrtko Ursulin 		__intel_engine_init_ctx_wa(engine,
56fde93886STvrtko Ursulin 					   &lists->engine[id].ctx_wa_list,
573e1f0a51SJohn Harrison 					   "CTX_REF");
58112ed2d3SChris Wilson 	}
59112ed2d3SChris Wilson }
60112ed2d3SChris Wilson 
61112ed2d3SChris Wilson static void
62112ed2d3SChris Wilson reference_lists_fini(struct drm_i915_private *i915, struct wa_lists *lists)
63112ed2d3SChris Wilson {
64112ed2d3SChris Wilson 	struct intel_engine_cs *engine;
65112ed2d3SChris Wilson 	enum intel_engine_id id;
66112ed2d3SChris Wilson 
67112ed2d3SChris Wilson 	for_each_engine(engine, i915, id)
68112ed2d3SChris Wilson 		intel_wa_list_free(&lists->engine[id].wa_list);
69112ed2d3SChris Wilson 
70112ed2d3SChris Wilson 	intel_wa_list_free(&lists->gt_wa_list);
71112ed2d3SChris Wilson }
72112ed2d3SChris Wilson 
73112ed2d3SChris Wilson static struct drm_i915_gem_object *
74112ed2d3SChris Wilson read_nonprivs(struct i915_gem_context *ctx, struct intel_engine_cs *engine)
75112ed2d3SChris Wilson {
76112ed2d3SChris Wilson 	const u32 base = engine->mmio_base;
77112ed2d3SChris Wilson 	struct drm_i915_gem_object *result;
78112ed2d3SChris Wilson 	struct i915_request *rq;
79112ed2d3SChris Wilson 	struct i915_vma *vma;
80112ed2d3SChris Wilson 	u32 srm, *cs;
81112ed2d3SChris Wilson 	int err;
82112ed2d3SChris Wilson 	int i;
83112ed2d3SChris Wilson 
84112ed2d3SChris Wilson 	result = i915_gem_object_create_internal(engine->i915, PAGE_SIZE);
85112ed2d3SChris Wilson 	if (IS_ERR(result))
86112ed2d3SChris Wilson 		return result;
87112ed2d3SChris Wilson 
88112ed2d3SChris Wilson 	i915_gem_object_set_cache_coherency(result, I915_CACHE_LLC);
89112ed2d3SChris Wilson 
90112ed2d3SChris Wilson 	cs = i915_gem_object_pin_map(result, I915_MAP_WB);
91112ed2d3SChris Wilson 	if (IS_ERR(cs)) {
92112ed2d3SChris Wilson 		err = PTR_ERR(cs);
93112ed2d3SChris Wilson 		goto err_obj;
94112ed2d3SChris Wilson 	}
95112ed2d3SChris Wilson 	memset(cs, 0xc5, PAGE_SIZE);
96112ed2d3SChris Wilson 	i915_gem_object_flush_map(result);
97112ed2d3SChris Wilson 	i915_gem_object_unpin_map(result);
98112ed2d3SChris Wilson 
99ba4134a4STvrtko Ursulin 	vma = i915_vma_instance(result, &engine->gt->ggtt->vm, NULL);
100112ed2d3SChris Wilson 	if (IS_ERR(vma)) {
101112ed2d3SChris Wilson 		err = PTR_ERR(vma);
102112ed2d3SChris Wilson 		goto err_obj;
103112ed2d3SChris Wilson 	}
104112ed2d3SChris Wilson 
105112ed2d3SChris Wilson 	err = i915_vma_pin(vma, 0, 0, PIN_GLOBAL);
106112ed2d3SChris Wilson 	if (err)
107112ed2d3SChris Wilson 		goto err_obj;
108112ed2d3SChris Wilson 
10946472b3eSChris Wilson 	rq = igt_request_alloc(ctx, engine);
110112ed2d3SChris Wilson 	if (IS_ERR(rq)) {
111112ed2d3SChris Wilson 		err = PTR_ERR(rq);
112112ed2d3SChris Wilson 		goto err_pin;
113112ed2d3SChris Wilson 	}
114112ed2d3SChris Wilson 
1156951e589SChris Wilson 	i915_vma_lock(vma);
116112ed2d3SChris Wilson 	err = i915_vma_move_to_active(vma, rq, EXEC_OBJECT_WRITE);
1176951e589SChris Wilson 	i915_vma_unlock(vma);
118112ed2d3SChris Wilson 	if (err)
119112ed2d3SChris Wilson 		goto err_req;
120112ed2d3SChris Wilson 
121112ed2d3SChris Wilson 	srm = MI_STORE_REGISTER_MEM | MI_SRM_LRM_GLOBAL_GTT;
122112ed2d3SChris Wilson 	if (INTEL_GEN(ctx->i915) >= 8)
123112ed2d3SChris Wilson 		srm++;
124112ed2d3SChris Wilson 
125112ed2d3SChris Wilson 	cs = intel_ring_begin(rq, 4 * RING_MAX_NONPRIV_SLOTS);
126112ed2d3SChris Wilson 	if (IS_ERR(cs)) {
127112ed2d3SChris Wilson 		err = PTR_ERR(cs);
128112ed2d3SChris Wilson 		goto err_req;
129112ed2d3SChris Wilson 	}
130112ed2d3SChris Wilson 
131112ed2d3SChris Wilson 	for (i = 0; i < RING_MAX_NONPRIV_SLOTS; i++) {
132112ed2d3SChris Wilson 		*cs++ = srm;
133112ed2d3SChris Wilson 		*cs++ = i915_mmio_reg_offset(RING_FORCE_TO_NONPRIV(base, i));
134112ed2d3SChris Wilson 		*cs++ = i915_ggtt_offset(vma) + sizeof(u32) * i;
135112ed2d3SChris Wilson 		*cs++ = 0;
136112ed2d3SChris Wilson 	}
137112ed2d3SChris Wilson 	intel_ring_advance(rq, cs);
138112ed2d3SChris Wilson 
139112ed2d3SChris Wilson 	i915_request_add(rq);
140112ed2d3SChris Wilson 	i915_vma_unpin(vma);
141112ed2d3SChris Wilson 
142112ed2d3SChris Wilson 	return result;
143112ed2d3SChris Wilson 
144112ed2d3SChris Wilson err_req:
145112ed2d3SChris Wilson 	i915_request_add(rq);
146112ed2d3SChris Wilson err_pin:
147112ed2d3SChris Wilson 	i915_vma_unpin(vma);
148112ed2d3SChris Wilson err_obj:
149112ed2d3SChris Wilson 	i915_gem_object_put(result);
150112ed2d3SChris Wilson 	return ERR_PTR(err);
151112ed2d3SChris Wilson }
152112ed2d3SChris Wilson 
153112ed2d3SChris Wilson static u32
154112ed2d3SChris Wilson get_whitelist_reg(const struct intel_engine_cs *engine, unsigned int i)
155112ed2d3SChris Wilson {
156112ed2d3SChris Wilson 	i915_reg_t reg = i < engine->whitelist.count ?
157112ed2d3SChris Wilson 			 engine->whitelist.list[i].reg :
158112ed2d3SChris Wilson 			 RING_NOPID(engine->mmio_base);
159112ed2d3SChris Wilson 
160112ed2d3SChris Wilson 	return i915_mmio_reg_offset(reg);
161112ed2d3SChris Wilson }
162112ed2d3SChris Wilson 
163112ed2d3SChris Wilson static void
164112ed2d3SChris Wilson print_results(const struct intel_engine_cs *engine, const u32 *results)
165112ed2d3SChris Wilson {
166112ed2d3SChris Wilson 	unsigned int i;
167112ed2d3SChris Wilson 
168112ed2d3SChris Wilson 	for (i = 0; i < RING_MAX_NONPRIV_SLOTS; i++) {
169112ed2d3SChris Wilson 		u32 expected = get_whitelist_reg(engine, i);
170112ed2d3SChris Wilson 		u32 actual = results[i];
171112ed2d3SChris Wilson 
172112ed2d3SChris Wilson 		pr_info("RING_NONPRIV[%d]: expected 0x%08x, found 0x%08x\n",
173112ed2d3SChris Wilson 			i, expected, actual);
174112ed2d3SChris Wilson 	}
175112ed2d3SChris Wilson }
176112ed2d3SChris Wilson 
177112ed2d3SChris Wilson static int check_whitelist(struct i915_gem_context *ctx,
178112ed2d3SChris Wilson 			   struct intel_engine_cs *engine)
179112ed2d3SChris Wilson {
180112ed2d3SChris Wilson 	struct drm_i915_gem_object *results;
181cb823ed9SChris Wilson 	struct intel_wedge_me wedge;
182112ed2d3SChris Wilson 	u32 *vaddr;
183112ed2d3SChris Wilson 	int err;
184112ed2d3SChris Wilson 	int i;
185112ed2d3SChris Wilson 
186112ed2d3SChris Wilson 	results = read_nonprivs(ctx, engine);
187112ed2d3SChris Wilson 	if (IS_ERR(results))
188112ed2d3SChris Wilson 		return PTR_ERR(results);
189112ed2d3SChris Wilson 
190112ed2d3SChris Wilson 	err = 0;
1916951e589SChris Wilson 	i915_gem_object_lock(results);
192cb823ed9SChris Wilson 	intel_wedge_on_timeout(&wedge, &ctx->i915->gt, HZ / 5) /* safety net! */
193112ed2d3SChris Wilson 		err = i915_gem_object_set_to_cpu_domain(results, false);
1946951e589SChris Wilson 	i915_gem_object_unlock(results);
195cb823ed9SChris Wilson 	if (intel_gt_is_wedged(&ctx->i915->gt))
196112ed2d3SChris Wilson 		err = -EIO;
197112ed2d3SChris Wilson 	if (err)
198112ed2d3SChris Wilson 		goto out_put;
199112ed2d3SChris Wilson 
200112ed2d3SChris Wilson 	vaddr = i915_gem_object_pin_map(results, I915_MAP_WB);
201112ed2d3SChris Wilson 	if (IS_ERR(vaddr)) {
202112ed2d3SChris Wilson 		err = PTR_ERR(vaddr);
203112ed2d3SChris Wilson 		goto out_put;
204112ed2d3SChris Wilson 	}
205112ed2d3SChris Wilson 
206112ed2d3SChris Wilson 	for (i = 0; i < RING_MAX_NONPRIV_SLOTS; i++) {
207112ed2d3SChris Wilson 		u32 expected = get_whitelist_reg(engine, i);
208112ed2d3SChris Wilson 		u32 actual = vaddr[i];
209112ed2d3SChris Wilson 
210112ed2d3SChris Wilson 		if (expected != actual) {
211112ed2d3SChris Wilson 			print_results(engine, vaddr);
212112ed2d3SChris Wilson 			pr_err("Invalid RING_NONPRIV[%d], expected 0x%08x, found 0x%08x\n",
213112ed2d3SChris Wilson 			       i, expected, actual);
214112ed2d3SChris Wilson 
215112ed2d3SChris Wilson 			err = -EINVAL;
216112ed2d3SChris Wilson 			break;
217112ed2d3SChris Wilson 		}
218112ed2d3SChris Wilson 	}
219112ed2d3SChris Wilson 
220112ed2d3SChris Wilson 	i915_gem_object_unpin_map(results);
221112ed2d3SChris Wilson out_put:
222112ed2d3SChris Wilson 	i915_gem_object_put(results);
223112ed2d3SChris Wilson 	return err;
224112ed2d3SChris Wilson }
225112ed2d3SChris Wilson 
226112ed2d3SChris Wilson static int do_device_reset(struct intel_engine_cs *engine)
227112ed2d3SChris Wilson {
228cb823ed9SChris Wilson 	intel_gt_reset(engine->gt, engine->mask, "live_workarounds");
229112ed2d3SChris Wilson 	return 0;
230112ed2d3SChris Wilson }
231112ed2d3SChris Wilson 
232112ed2d3SChris Wilson static int do_engine_reset(struct intel_engine_cs *engine)
233112ed2d3SChris Wilson {
234cb823ed9SChris Wilson 	return intel_engine_reset(engine, "live_workarounds");
235112ed2d3SChris Wilson }
236112ed2d3SChris Wilson 
237112ed2d3SChris Wilson static int
238112ed2d3SChris Wilson switch_to_scratch_context(struct intel_engine_cs *engine,
239112ed2d3SChris Wilson 			  struct igt_spinner *spin)
240112ed2d3SChris Wilson {
241112ed2d3SChris Wilson 	struct i915_gem_context *ctx;
242f277bc0cSChris Wilson 	struct intel_context *ce;
243112ed2d3SChris Wilson 	struct i915_request *rq;
244112ed2d3SChris Wilson 	intel_wakeref_t wakeref;
245112ed2d3SChris Wilson 	int err = 0;
246112ed2d3SChris Wilson 
247112ed2d3SChris Wilson 	ctx = kernel_context(engine->i915);
248112ed2d3SChris Wilson 	if (IS_ERR(ctx))
249112ed2d3SChris Wilson 		return PTR_ERR(ctx);
250112ed2d3SChris Wilson 
251112ed2d3SChris Wilson 	GEM_BUG_ON(i915_gem_context_is_bannable(ctx));
252112ed2d3SChris Wilson 
253f1c4d157SChris Wilson 	ce = i915_gem_context_get_engine(ctx, engine->legacy_idx);
254f277bc0cSChris Wilson 	GEM_BUG_ON(IS_ERR(ce));
255f277bc0cSChris Wilson 
256112ed2d3SChris Wilson 	rq = ERR_PTR(-ENODEV);
257c447ff7dSDaniele Ceraolo Spurio 	with_intel_runtime_pm(&engine->i915->runtime_pm, wakeref)
258f277bc0cSChris Wilson 		rq = igt_spinner_create_request(spin, ce, MI_NOOP);
259112ed2d3SChris Wilson 
260f277bc0cSChris Wilson 	intel_context_put(ce);
261112ed2d3SChris Wilson 	kernel_context_close(ctx);
262112ed2d3SChris Wilson 
263112ed2d3SChris Wilson 	if (IS_ERR(rq)) {
264112ed2d3SChris Wilson 		spin = NULL;
265112ed2d3SChris Wilson 		err = PTR_ERR(rq);
266112ed2d3SChris Wilson 		goto err;
267112ed2d3SChris Wilson 	}
268112ed2d3SChris Wilson 
269112ed2d3SChris Wilson 	i915_request_add(rq);
270112ed2d3SChris Wilson 
271112ed2d3SChris Wilson 	if (spin && !igt_wait_for_spinner(spin, rq)) {
272112ed2d3SChris Wilson 		pr_err("Spinner failed to start\n");
273112ed2d3SChris Wilson 		err = -ETIMEDOUT;
274112ed2d3SChris Wilson 	}
275112ed2d3SChris Wilson 
276112ed2d3SChris Wilson err:
277112ed2d3SChris Wilson 	if (err && spin)
278112ed2d3SChris Wilson 		igt_spinner_end(spin);
279112ed2d3SChris Wilson 
280112ed2d3SChris Wilson 	return err;
281112ed2d3SChris Wilson }
282112ed2d3SChris Wilson 
283112ed2d3SChris Wilson static int check_whitelist_across_reset(struct intel_engine_cs *engine,
284112ed2d3SChris Wilson 					int (*reset)(struct intel_engine_cs *),
285112ed2d3SChris Wilson 					const char *name)
286112ed2d3SChris Wilson {
287112ed2d3SChris Wilson 	struct drm_i915_private *i915 = engine->i915;
288cf3bd1a0SChris Wilson 	struct i915_gem_context *ctx, *tmp;
289112ed2d3SChris Wilson 	struct igt_spinner spin;
290112ed2d3SChris Wilson 	intel_wakeref_t wakeref;
291112ed2d3SChris Wilson 	int err;
292112ed2d3SChris Wilson 
2933e1f0a51SJohn Harrison 	pr_info("Checking %d whitelisted registers on %s (RING_NONPRIV) [%s]\n",
2943e1f0a51SJohn Harrison 		engine->whitelist.count, engine->name, name);
295112ed2d3SChris Wilson 
296112ed2d3SChris Wilson 	ctx = kernel_context(i915);
297112ed2d3SChris Wilson 	if (IS_ERR(ctx))
298112ed2d3SChris Wilson 		return PTR_ERR(ctx);
299112ed2d3SChris Wilson 
300f277bc0cSChris Wilson 	err = igt_spinner_init(&spin, engine->gt);
301cf3bd1a0SChris Wilson 	if (err)
302cf3bd1a0SChris Wilson 		goto out_ctx;
303cf3bd1a0SChris Wilson 
304112ed2d3SChris Wilson 	err = check_whitelist(ctx, engine);
305112ed2d3SChris Wilson 	if (err) {
306112ed2d3SChris Wilson 		pr_err("Invalid whitelist *before* %s reset!\n", name);
307cf3bd1a0SChris Wilson 		goto out_spin;
308112ed2d3SChris Wilson 	}
309112ed2d3SChris Wilson 
310112ed2d3SChris Wilson 	err = switch_to_scratch_context(engine, &spin);
311112ed2d3SChris Wilson 	if (err)
312cf3bd1a0SChris Wilson 		goto out_spin;
313112ed2d3SChris Wilson 
314c447ff7dSDaniele Ceraolo Spurio 	with_intel_runtime_pm(&i915->runtime_pm, wakeref)
315112ed2d3SChris Wilson 		err = reset(engine);
316112ed2d3SChris Wilson 
317112ed2d3SChris Wilson 	igt_spinner_end(&spin);
318112ed2d3SChris Wilson 
319112ed2d3SChris Wilson 	if (err) {
320112ed2d3SChris Wilson 		pr_err("%s reset failed\n", name);
321cf3bd1a0SChris Wilson 		goto out_spin;
322112ed2d3SChris Wilson 	}
323112ed2d3SChris Wilson 
324112ed2d3SChris Wilson 	err = check_whitelist(ctx, engine);
325112ed2d3SChris Wilson 	if (err) {
326112ed2d3SChris Wilson 		pr_err("Whitelist not preserved in context across %s reset!\n",
327112ed2d3SChris Wilson 		       name);
328cf3bd1a0SChris Wilson 		goto out_spin;
329112ed2d3SChris Wilson 	}
330112ed2d3SChris Wilson 
331cf3bd1a0SChris Wilson 	tmp = kernel_context(i915);
332cf3bd1a0SChris Wilson 	if (IS_ERR(tmp)) {
333cf3bd1a0SChris Wilson 		err = PTR_ERR(tmp);
334cf3bd1a0SChris Wilson 		goto out_spin;
335cf3bd1a0SChris Wilson 	}
336112ed2d3SChris Wilson 	kernel_context_close(ctx);
337cf3bd1a0SChris Wilson 	ctx = tmp;
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);
343cf3bd1a0SChris Wilson 		goto out_spin;
344112ed2d3SChris Wilson 	}
345112ed2d3SChris Wilson 
346cf3bd1a0SChris Wilson out_spin:
347cf3bd1a0SChris Wilson 	igt_spinner_fini(&spin);
348cf3bd1a0SChris Wilson out_ctx:
349112ed2d3SChris Wilson 	kernel_context_close(ctx);
350112ed2d3SChris Wilson 	return err;
351112ed2d3SChris Wilson }
352112ed2d3SChris Wilson 
353112ed2d3SChris Wilson static struct i915_vma *create_batch(struct i915_gem_context *ctx)
354112ed2d3SChris Wilson {
355112ed2d3SChris Wilson 	struct drm_i915_gem_object *obj;
356112ed2d3SChris Wilson 	struct i915_vma *vma;
357112ed2d3SChris Wilson 	int err;
358112ed2d3SChris Wilson 
359112ed2d3SChris Wilson 	obj = i915_gem_object_create_internal(ctx->i915, 16 * PAGE_SIZE);
360112ed2d3SChris Wilson 	if (IS_ERR(obj))
361112ed2d3SChris Wilson 		return ERR_CAST(obj);
362112ed2d3SChris Wilson 
363e568ac38SChris Wilson 	vma = i915_vma_instance(obj, ctx->vm, NULL);
364112ed2d3SChris Wilson 	if (IS_ERR(vma)) {
365112ed2d3SChris Wilson 		err = PTR_ERR(vma);
366112ed2d3SChris Wilson 		goto err_obj;
367112ed2d3SChris Wilson 	}
368112ed2d3SChris Wilson 
369112ed2d3SChris Wilson 	err = i915_vma_pin(vma, 0, 0, PIN_USER);
370112ed2d3SChris Wilson 	if (err)
371112ed2d3SChris Wilson 		goto err_obj;
372112ed2d3SChris Wilson 
373112ed2d3SChris Wilson 	return vma;
374112ed2d3SChris Wilson 
375112ed2d3SChris Wilson err_obj:
376112ed2d3SChris Wilson 	i915_gem_object_put(obj);
377112ed2d3SChris Wilson 	return ERR_PTR(err);
378112ed2d3SChris Wilson }
379112ed2d3SChris Wilson 
380112ed2d3SChris Wilson static u32 reg_write(u32 old, u32 new, u32 rsvd)
381112ed2d3SChris Wilson {
382112ed2d3SChris Wilson 	if (rsvd == 0x0000ffff) {
383112ed2d3SChris Wilson 		old &= ~(new >> 16);
384112ed2d3SChris Wilson 		old |= new & (new >> 16);
385112ed2d3SChris Wilson 	} else {
386112ed2d3SChris Wilson 		old &= ~rsvd;
387112ed2d3SChris Wilson 		old |= new & rsvd;
388112ed2d3SChris Wilson 	}
389112ed2d3SChris Wilson 
390112ed2d3SChris Wilson 	return old;
391112ed2d3SChris Wilson }
392112ed2d3SChris Wilson 
393112ed2d3SChris Wilson static bool wo_register(struct intel_engine_cs *engine, u32 reg)
394112ed2d3SChris Wilson {
395112ed2d3SChris Wilson 	enum intel_platform platform = INTEL_INFO(engine->i915)->platform;
396112ed2d3SChris Wilson 	int i;
397112ed2d3SChris Wilson 
3981e2b7f49SJohn Harrison 	if ((reg & RING_FORCE_TO_NONPRIV_ACCESS_MASK) ==
3991e2b7f49SJohn Harrison 	     RING_FORCE_TO_NONPRIV_ACCESS_WR)
4001e2b7f49SJohn Harrison 		return true;
4011e2b7f49SJohn Harrison 
402112ed2d3SChris Wilson 	for (i = 0; i < ARRAY_SIZE(wo_registers); i++) {
403112ed2d3SChris Wilson 		if (wo_registers[i].platform == platform &&
404112ed2d3SChris Wilson 		    wo_registers[i].reg == reg)
405112ed2d3SChris Wilson 			return true;
406112ed2d3SChris Wilson 	}
407112ed2d3SChris Wilson 
408112ed2d3SChris Wilson 	return false;
409112ed2d3SChris Wilson }
410112ed2d3SChris Wilson 
411767662bcSRobert M. Fosha static bool ro_register(u32 reg)
412767662bcSRobert M. Fosha {
4131e2b7f49SJohn Harrison 	if ((reg & RING_FORCE_TO_NONPRIV_ACCESS_MASK) ==
4141e2b7f49SJohn Harrison 	     RING_FORCE_TO_NONPRIV_ACCESS_RD)
415767662bcSRobert M. Fosha 		return true;
416767662bcSRobert M. Fosha 
417767662bcSRobert M. Fosha 	return false;
418767662bcSRobert M. Fosha }
419767662bcSRobert M. Fosha 
420767662bcSRobert M. Fosha static int whitelist_writable_count(struct intel_engine_cs *engine)
421767662bcSRobert M. Fosha {
422767662bcSRobert M. Fosha 	int count = engine->whitelist.count;
423767662bcSRobert M. Fosha 	int i;
424767662bcSRobert M. Fosha 
425767662bcSRobert M. Fosha 	for (i = 0; i < engine->whitelist.count; i++) {
426767662bcSRobert M. Fosha 		u32 reg = i915_mmio_reg_offset(engine->whitelist.list[i].reg);
427767662bcSRobert M. Fosha 
428767662bcSRobert M. Fosha 		if (ro_register(reg))
429767662bcSRobert M. Fosha 			count--;
430767662bcSRobert M. Fosha 	}
431767662bcSRobert M. Fosha 
432767662bcSRobert M. Fosha 	return count;
433767662bcSRobert M. Fosha }
434767662bcSRobert M. Fosha 
435112ed2d3SChris Wilson static int check_dirty_whitelist(struct i915_gem_context *ctx,
436112ed2d3SChris Wilson 				 struct intel_engine_cs *engine)
437112ed2d3SChris Wilson {
438112ed2d3SChris Wilson 	const u32 values[] = {
439112ed2d3SChris Wilson 		0x00000000,
440112ed2d3SChris Wilson 		0x01010101,
441112ed2d3SChris Wilson 		0x10100101,
442112ed2d3SChris Wilson 		0x03030303,
443112ed2d3SChris Wilson 		0x30300303,
444112ed2d3SChris Wilson 		0x05050505,
445112ed2d3SChris Wilson 		0x50500505,
446112ed2d3SChris Wilson 		0x0f0f0f0f,
447112ed2d3SChris Wilson 		0xf00ff00f,
448112ed2d3SChris Wilson 		0x10101010,
449112ed2d3SChris Wilson 		0xf0f01010,
450112ed2d3SChris Wilson 		0x30303030,
451112ed2d3SChris Wilson 		0xa0a03030,
452112ed2d3SChris Wilson 		0x50505050,
453112ed2d3SChris Wilson 		0xc0c05050,
454112ed2d3SChris Wilson 		0xf0f0f0f0,
455112ed2d3SChris Wilson 		0x11111111,
456112ed2d3SChris Wilson 		0x33333333,
457112ed2d3SChris Wilson 		0x55555555,
458112ed2d3SChris Wilson 		0x0000ffff,
459112ed2d3SChris Wilson 		0x00ff00ff,
460112ed2d3SChris Wilson 		0xff0000ff,
461112ed2d3SChris Wilson 		0xffff00ff,
462112ed2d3SChris Wilson 		0xffffffff,
463112ed2d3SChris Wilson 	};
464112ed2d3SChris Wilson 	struct i915_vma *scratch;
465112ed2d3SChris Wilson 	struct i915_vma *batch;
466112ed2d3SChris Wilson 	int err = 0, i, v;
467112ed2d3SChris Wilson 	u32 *cs, *results;
468112ed2d3SChris Wilson 
469e568ac38SChris Wilson 	scratch = create_scratch(ctx->vm, 2 * ARRAY_SIZE(values) + 1);
470112ed2d3SChris Wilson 	if (IS_ERR(scratch))
471112ed2d3SChris Wilson 		return PTR_ERR(scratch);
472112ed2d3SChris Wilson 
473112ed2d3SChris Wilson 	batch = create_batch(ctx);
474112ed2d3SChris Wilson 	if (IS_ERR(batch)) {
475112ed2d3SChris Wilson 		err = PTR_ERR(batch);
476112ed2d3SChris Wilson 		goto out_scratch;
477112ed2d3SChris Wilson 	}
478112ed2d3SChris Wilson 
479112ed2d3SChris Wilson 	for (i = 0; i < engine->whitelist.count; i++) {
480112ed2d3SChris Wilson 		u32 reg = i915_mmio_reg_offset(engine->whitelist.list[i].reg);
481112ed2d3SChris Wilson 		u64 addr = scratch->node.start;
482112ed2d3SChris Wilson 		struct i915_request *rq;
483112ed2d3SChris Wilson 		u32 srm, lrm, rsvd;
484112ed2d3SChris Wilson 		u32 expect;
485112ed2d3SChris Wilson 		int idx;
486aee20aaeSJohn Harrison 		bool ro_reg;
487112ed2d3SChris Wilson 
488112ed2d3SChris Wilson 		if (wo_register(engine, reg))
489112ed2d3SChris Wilson 			continue;
490112ed2d3SChris Wilson 
491aee20aaeSJohn Harrison 		ro_reg = ro_register(reg);
492767662bcSRobert M. Fosha 
493112ed2d3SChris Wilson 		srm = MI_STORE_REGISTER_MEM;
494112ed2d3SChris Wilson 		lrm = MI_LOAD_REGISTER_MEM;
495112ed2d3SChris Wilson 		if (INTEL_GEN(ctx->i915) >= 8)
496112ed2d3SChris Wilson 			lrm++, srm++;
497112ed2d3SChris Wilson 
498112ed2d3SChris Wilson 		pr_debug("%s: Writing garbage to %x\n",
499112ed2d3SChris Wilson 			 engine->name, reg);
500112ed2d3SChris Wilson 
501112ed2d3SChris Wilson 		cs = i915_gem_object_pin_map(batch->obj, I915_MAP_WC);
502112ed2d3SChris Wilson 		if (IS_ERR(cs)) {
503112ed2d3SChris Wilson 			err = PTR_ERR(cs);
504112ed2d3SChris Wilson 			goto out_batch;
505112ed2d3SChris Wilson 		}
506112ed2d3SChris Wilson 
507112ed2d3SChris Wilson 		/* SRM original */
508112ed2d3SChris Wilson 		*cs++ = srm;
509112ed2d3SChris Wilson 		*cs++ = reg;
510112ed2d3SChris Wilson 		*cs++ = lower_32_bits(addr);
511112ed2d3SChris Wilson 		*cs++ = upper_32_bits(addr);
512112ed2d3SChris Wilson 
513112ed2d3SChris Wilson 		idx = 1;
514112ed2d3SChris Wilson 		for (v = 0; v < ARRAY_SIZE(values); v++) {
515112ed2d3SChris Wilson 			/* LRI garbage */
516112ed2d3SChris Wilson 			*cs++ = MI_LOAD_REGISTER_IMM(1);
517112ed2d3SChris Wilson 			*cs++ = reg;
518112ed2d3SChris Wilson 			*cs++ = values[v];
519112ed2d3SChris Wilson 
520112ed2d3SChris Wilson 			/* SRM result */
521112ed2d3SChris Wilson 			*cs++ = srm;
522112ed2d3SChris Wilson 			*cs++ = reg;
523112ed2d3SChris Wilson 			*cs++ = lower_32_bits(addr + sizeof(u32) * idx);
524112ed2d3SChris Wilson 			*cs++ = upper_32_bits(addr + sizeof(u32) * idx);
525112ed2d3SChris Wilson 			idx++;
526112ed2d3SChris Wilson 		}
527112ed2d3SChris Wilson 		for (v = 0; v < ARRAY_SIZE(values); v++) {
528112ed2d3SChris Wilson 			/* LRI garbage */
529112ed2d3SChris Wilson 			*cs++ = MI_LOAD_REGISTER_IMM(1);
530112ed2d3SChris Wilson 			*cs++ = reg;
531112ed2d3SChris Wilson 			*cs++ = ~values[v];
532112ed2d3SChris Wilson 
533112ed2d3SChris Wilson 			/* SRM result */
534112ed2d3SChris Wilson 			*cs++ = srm;
535112ed2d3SChris Wilson 			*cs++ = reg;
536112ed2d3SChris Wilson 			*cs++ = lower_32_bits(addr + sizeof(u32) * idx);
537112ed2d3SChris Wilson 			*cs++ = upper_32_bits(addr + sizeof(u32) * idx);
538112ed2d3SChris Wilson 			idx++;
539112ed2d3SChris Wilson 		}
540112ed2d3SChris Wilson 		GEM_BUG_ON(idx * sizeof(u32) > scratch->size);
541112ed2d3SChris Wilson 
542112ed2d3SChris Wilson 		/* LRM original -- don't leave garbage in the context! */
543112ed2d3SChris Wilson 		*cs++ = lrm;
544112ed2d3SChris Wilson 		*cs++ = reg;
545112ed2d3SChris Wilson 		*cs++ = lower_32_bits(addr);
546112ed2d3SChris Wilson 		*cs++ = upper_32_bits(addr);
547112ed2d3SChris Wilson 
548112ed2d3SChris Wilson 		*cs++ = MI_BATCH_BUFFER_END;
549112ed2d3SChris Wilson 
550112ed2d3SChris Wilson 		i915_gem_object_flush_map(batch->obj);
551112ed2d3SChris Wilson 		i915_gem_object_unpin_map(batch->obj);
552baea429dSTvrtko Ursulin 		intel_gt_chipset_flush(engine->gt);
553112ed2d3SChris Wilson 
55446472b3eSChris Wilson 		rq = igt_request_alloc(ctx, engine);
555112ed2d3SChris Wilson 		if (IS_ERR(rq)) {
556112ed2d3SChris Wilson 			err = PTR_ERR(rq);
557112ed2d3SChris Wilson 			goto out_batch;
558112ed2d3SChris Wilson 		}
559112ed2d3SChris Wilson 
560112ed2d3SChris Wilson 		if (engine->emit_init_breadcrumb) { /* Be nice if we hang */
561112ed2d3SChris Wilson 			err = engine->emit_init_breadcrumb(rq);
562112ed2d3SChris Wilson 			if (err)
563112ed2d3SChris Wilson 				goto err_request;
564112ed2d3SChris Wilson 		}
565112ed2d3SChris Wilson 
566112ed2d3SChris Wilson 		err = engine->emit_bb_start(rq,
567112ed2d3SChris Wilson 					    batch->node.start, PAGE_SIZE,
568112ed2d3SChris Wilson 					    0);
569112ed2d3SChris Wilson 		if (err)
570112ed2d3SChris Wilson 			goto err_request;
571112ed2d3SChris Wilson 
572112ed2d3SChris Wilson err_request:
573112ed2d3SChris Wilson 		i915_request_add(rq);
574112ed2d3SChris Wilson 		if (err)
575112ed2d3SChris Wilson 			goto out_batch;
576112ed2d3SChris Wilson 
5772f530945SChris Wilson 		if (i915_request_wait(rq, 0, HZ / 5) < 0) {
578112ed2d3SChris Wilson 			pr_err("%s: Futzing %x timedout; cancelling test\n",
579112ed2d3SChris Wilson 			       engine->name, reg);
580cb823ed9SChris Wilson 			intel_gt_set_wedged(&ctx->i915->gt);
581112ed2d3SChris Wilson 			err = -EIO;
582112ed2d3SChris Wilson 			goto out_batch;
583112ed2d3SChris Wilson 		}
584112ed2d3SChris Wilson 
585112ed2d3SChris Wilson 		results = i915_gem_object_pin_map(scratch->obj, I915_MAP_WB);
586112ed2d3SChris Wilson 		if (IS_ERR(results)) {
587112ed2d3SChris Wilson 			err = PTR_ERR(results);
588112ed2d3SChris Wilson 			goto out_batch;
589112ed2d3SChris Wilson 		}
590112ed2d3SChris Wilson 
591112ed2d3SChris Wilson 		GEM_BUG_ON(values[ARRAY_SIZE(values) - 1] != 0xffffffff);
592aee20aaeSJohn Harrison 		if (!ro_reg) {
593aee20aaeSJohn Harrison 			/* detect write masking */
594aee20aaeSJohn Harrison 			rsvd = results[ARRAY_SIZE(values)];
595112ed2d3SChris Wilson 			if (!rsvd) {
596112ed2d3SChris Wilson 				pr_err("%s: Unable to write to whitelisted register %x\n",
597112ed2d3SChris Wilson 				       engine->name, reg);
598112ed2d3SChris Wilson 				err = -EINVAL;
599112ed2d3SChris Wilson 				goto out_unpin;
600112ed2d3SChris Wilson 			}
601aee20aaeSJohn Harrison 		}
602112ed2d3SChris Wilson 
603112ed2d3SChris Wilson 		expect = results[0];
604112ed2d3SChris Wilson 		idx = 1;
605112ed2d3SChris Wilson 		for (v = 0; v < ARRAY_SIZE(values); v++) {
606aee20aaeSJohn Harrison 			if (ro_reg)
607aee20aaeSJohn Harrison 				expect = results[0];
608aee20aaeSJohn Harrison 			else
609112ed2d3SChris Wilson 				expect = reg_write(expect, values[v], rsvd);
610aee20aaeSJohn Harrison 
611112ed2d3SChris Wilson 			if (results[idx] != expect)
612112ed2d3SChris Wilson 				err++;
613112ed2d3SChris Wilson 			idx++;
614112ed2d3SChris Wilson 		}
615112ed2d3SChris Wilson 		for (v = 0; v < ARRAY_SIZE(values); v++) {
616aee20aaeSJohn Harrison 			if (ro_reg)
617aee20aaeSJohn Harrison 				expect = results[0];
618aee20aaeSJohn Harrison 			else
619112ed2d3SChris Wilson 				expect = reg_write(expect, ~values[v], rsvd);
620aee20aaeSJohn Harrison 
621112ed2d3SChris Wilson 			if (results[idx] != expect)
622112ed2d3SChris Wilson 				err++;
623112ed2d3SChris Wilson 			idx++;
624112ed2d3SChris Wilson 		}
625112ed2d3SChris Wilson 		if (err) {
626112ed2d3SChris Wilson 			pr_err("%s: %d mismatch between values written to whitelisted register [%x], and values read back!\n",
627112ed2d3SChris Wilson 			       engine->name, err, reg);
628112ed2d3SChris Wilson 
629aee20aaeSJohn Harrison 			if (ro_reg)
630aee20aaeSJohn Harrison 				pr_info("%s: Whitelisted read-only register: %x, original value %08x\n",
631aee20aaeSJohn Harrison 					engine->name, reg, results[0]);
632aee20aaeSJohn Harrison 			else
633112ed2d3SChris Wilson 				pr_info("%s: Whitelisted register: %x, original value %08x, rsvd %08x\n",
634112ed2d3SChris Wilson 					engine->name, reg, results[0], rsvd);
635112ed2d3SChris Wilson 
636112ed2d3SChris Wilson 			expect = results[0];
637112ed2d3SChris Wilson 			idx = 1;
638112ed2d3SChris Wilson 			for (v = 0; v < ARRAY_SIZE(values); v++) {
639112ed2d3SChris Wilson 				u32 w = values[v];
640112ed2d3SChris Wilson 
641aee20aaeSJohn Harrison 				if (ro_reg)
642aee20aaeSJohn Harrison 					expect = results[0];
643aee20aaeSJohn Harrison 				else
644112ed2d3SChris Wilson 					expect = reg_write(expect, w, rsvd);
645112ed2d3SChris Wilson 				pr_info("Wrote %08x, read %08x, expect %08x\n",
646112ed2d3SChris Wilson 					w, results[idx], expect);
647112ed2d3SChris Wilson 				idx++;
648112ed2d3SChris Wilson 			}
649112ed2d3SChris Wilson 			for (v = 0; v < ARRAY_SIZE(values); v++) {
650112ed2d3SChris Wilson 				u32 w = ~values[v];
651112ed2d3SChris Wilson 
652aee20aaeSJohn Harrison 				if (ro_reg)
653aee20aaeSJohn Harrison 					expect = results[0];
654aee20aaeSJohn Harrison 				else
655112ed2d3SChris Wilson 					expect = reg_write(expect, w, rsvd);
656112ed2d3SChris Wilson 				pr_info("Wrote %08x, read %08x, expect %08x\n",
657112ed2d3SChris Wilson 					w, results[idx], expect);
658112ed2d3SChris Wilson 				idx++;
659112ed2d3SChris Wilson 			}
660112ed2d3SChris Wilson 
661112ed2d3SChris Wilson 			err = -EINVAL;
662112ed2d3SChris Wilson 		}
663112ed2d3SChris Wilson out_unpin:
664112ed2d3SChris Wilson 		i915_gem_object_unpin_map(scratch->obj);
665112ed2d3SChris Wilson 		if (err)
666112ed2d3SChris Wilson 			break;
667112ed2d3SChris Wilson 	}
668112ed2d3SChris Wilson 
669112ed2d3SChris Wilson 	if (igt_flush_test(ctx->i915, I915_WAIT_LOCKED))
670112ed2d3SChris Wilson 		err = -EIO;
671112ed2d3SChris Wilson out_batch:
672112ed2d3SChris Wilson 	i915_vma_unpin_and_release(&batch, 0);
673112ed2d3SChris Wilson out_scratch:
674112ed2d3SChris Wilson 	i915_vma_unpin_and_release(&scratch, 0);
675112ed2d3SChris Wilson 	return err;
676112ed2d3SChris Wilson }
677112ed2d3SChris Wilson 
678112ed2d3SChris Wilson static int live_dirty_whitelist(void *arg)
679112ed2d3SChris Wilson {
680112ed2d3SChris Wilson 	struct drm_i915_private *i915 = arg;
681112ed2d3SChris Wilson 	struct intel_engine_cs *engine;
682112ed2d3SChris Wilson 	struct i915_gem_context *ctx;
683112ed2d3SChris Wilson 	enum intel_engine_id id;
684112ed2d3SChris Wilson 	intel_wakeref_t wakeref;
685112ed2d3SChris Wilson 	struct drm_file *file;
686112ed2d3SChris Wilson 	int err = 0;
687112ed2d3SChris Wilson 
688112ed2d3SChris Wilson 	/* Can the user write to the whitelisted registers? */
689112ed2d3SChris Wilson 
690112ed2d3SChris Wilson 	if (INTEL_GEN(i915) < 7) /* minimum requirement for LRI, SRM, LRM */
691112ed2d3SChris Wilson 		return 0;
692112ed2d3SChris Wilson 
693d858d569SDaniele Ceraolo Spurio 	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
694112ed2d3SChris Wilson 
695112ed2d3SChris Wilson 	mutex_unlock(&i915->drm.struct_mutex);
696112ed2d3SChris Wilson 	file = mock_file(i915);
697112ed2d3SChris Wilson 	mutex_lock(&i915->drm.struct_mutex);
698112ed2d3SChris Wilson 	if (IS_ERR(file)) {
699112ed2d3SChris Wilson 		err = PTR_ERR(file);
700112ed2d3SChris Wilson 		goto out_rpm;
701112ed2d3SChris Wilson 	}
702112ed2d3SChris Wilson 
703112ed2d3SChris Wilson 	ctx = live_context(i915, file);
704112ed2d3SChris Wilson 	if (IS_ERR(ctx)) {
705112ed2d3SChris Wilson 		err = PTR_ERR(ctx);
706112ed2d3SChris Wilson 		goto out_file;
707112ed2d3SChris Wilson 	}
708112ed2d3SChris Wilson 
709112ed2d3SChris Wilson 	for_each_engine(engine, i915, id) {
710112ed2d3SChris Wilson 		if (engine->whitelist.count == 0)
711112ed2d3SChris Wilson 			continue;
712112ed2d3SChris Wilson 
713112ed2d3SChris Wilson 		err = check_dirty_whitelist(ctx, engine);
714112ed2d3SChris Wilson 		if (err)
715112ed2d3SChris Wilson 			goto out_file;
716112ed2d3SChris Wilson 	}
717112ed2d3SChris Wilson 
718112ed2d3SChris Wilson out_file:
719112ed2d3SChris Wilson 	mutex_unlock(&i915->drm.struct_mutex);
720112ed2d3SChris Wilson 	mock_file_free(i915, file);
721112ed2d3SChris Wilson 	mutex_lock(&i915->drm.struct_mutex);
722112ed2d3SChris Wilson out_rpm:
723d858d569SDaniele Ceraolo Spurio 	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
724112ed2d3SChris Wilson 	return err;
725112ed2d3SChris Wilson }
726112ed2d3SChris Wilson 
727112ed2d3SChris Wilson static int live_reset_whitelist(void *arg)
728112ed2d3SChris Wilson {
729112ed2d3SChris Wilson 	struct drm_i915_private *i915 = arg;
730112ed2d3SChris Wilson 	struct intel_engine_cs *engine = i915->engine[RCS0];
731112ed2d3SChris Wilson 	int err = 0;
732112ed2d3SChris Wilson 
733112ed2d3SChris Wilson 	/* If we reset the gpu, we should not lose the RING_NONPRIV */
734112ed2d3SChris Wilson 
735112ed2d3SChris Wilson 	if (!engine || engine->whitelist.count == 0)
736112ed2d3SChris Wilson 		return 0;
737112ed2d3SChris Wilson 
738cb823ed9SChris Wilson 	igt_global_reset_lock(&i915->gt);
739112ed2d3SChris Wilson 
740112ed2d3SChris Wilson 	if (intel_has_reset_engine(i915)) {
741112ed2d3SChris Wilson 		err = check_whitelist_across_reset(engine,
742112ed2d3SChris Wilson 						   do_engine_reset,
743112ed2d3SChris Wilson 						   "engine");
744112ed2d3SChris Wilson 		if (err)
745112ed2d3SChris Wilson 			goto out;
746112ed2d3SChris Wilson 	}
747112ed2d3SChris Wilson 
748112ed2d3SChris Wilson 	if (intel_has_gpu_reset(i915)) {
749112ed2d3SChris Wilson 		err = check_whitelist_across_reset(engine,
750112ed2d3SChris Wilson 						   do_device_reset,
751112ed2d3SChris Wilson 						   "device");
752112ed2d3SChris Wilson 		if (err)
753112ed2d3SChris Wilson 			goto out;
754112ed2d3SChris Wilson 	}
755112ed2d3SChris Wilson 
756112ed2d3SChris Wilson out:
757cb823ed9SChris Wilson 	igt_global_reset_unlock(&i915->gt);
758112ed2d3SChris Wilson 	return err;
759112ed2d3SChris Wilson }
760112ed2d3SChris Wilson 
761112ed2d3SChris Wilson static int read_whitelisted_registers(struct i915_gem_context *ctx,
762112ed2d3SChris Wilson 				      struct intel_engine_cs *engine,
763112ed2d3SChris Wilson 				      struct i915_vma *results)
764112ed2d3SChris Wilson {
765112ed2d3SChris Wilson 	struct i915_request *rq;
766112ed2d3SChris Wilson 	int i, err = 0;
767112ed2d3SChris Wilson 	u32 srm, *cs;
768112ed2d3SChris Wilson 
76946472b3eSChris Wilson 	rq = igt_request_alloc(ctx, engine);
770112ed2d3SChris Wilson 	if (IS_ERR(rq))
771112ed2d3SChris Wilson 		return PTR_ERR(rq);
772112ed2d3SChris Wilson 
773112ed2d3SChris Wilson 	srm = MI_STORE_REGISTER_MEM;
774112ed2d3SChris Wilson 	if (INTEL_GEN(ctx->i915) >= 8)
775112ed2d3SChris Wilson 		srm++;
776112ed2d3SChris Wilson 
777112ed2d3SChris Wilson 	cs = intel_ring_begin(rq, 4 * engine->whitelist.count);
778112ed2d3SChris Wilson 	if (IS_ERR(cs)) {
779112ed2d3SChris Wilson 		err = PTR_ERR(cs);
780112ed2d3SChris Wilson 		goto err_req;
781112ed2d3SChris Wilson 	}
782112ed2d3SChris Wilson 
783112ed2d3SChris Wilson 	for (i = 0; i < engine->whitelist.count; i++) {
784112ed2d3SChris Wilson 		u64 offset = results->node.start + sizeof(u32) * i;
785767662bcSRobert M. Fosha 		u32 reg = i915_mmio_reg_offset(engine->whitelist.list[i].reg);
786767662bcSRobert M. Fosha 
7871e2b7f49SJohn Harrison 		/* Clear access permission field */
7881e2b7f49SJohn Harrison 		reg &= ~RING_FORCE_TO_NONPRIV_ACCESS_MASK;
789112ed2d3SChris Wilson 
790112ed2d3SChris Wilson 		*cs++ = srm;
791767662bcSRobert M. Fosha 		*cs++ = reg;
792112ed2d3SChris Wilson 		*cs++ = lower_32_bits(offset);
793112ed2d3SChris Wilson 		*cs++ = upper_32_bits(offset);
794112ed2d3SChris Wilson 	}
795112ed2d3SChris Wilson 	intel_ring_advance(rq, cs);
796112ed2d3SChris Wilson 
797112ed2d3SChris Wilson err_req:
798112ed2d3SChris Wilson 	i915_request_add(rq);
799112ed2d3SChris Wilson 
8002f530945SChris Wilson 	if (i915_request_wait(rq, 0, HZ / 5) < 0)
801112ed2d3SChris Wilson 		err = -EIO;
802112ed2d3SChris Wilson 
803112ed2d3SChris Wilson 	return err;
804112ed2d3SChris Wilson }
805112ed2d3SChris Wilson 
806112ed2d3SChris Wilson static int scrub_whitelisted_registers(struct i915_gem_context *ctx,
807112ed2d3SChris Wilson 				       struct intel_engine_cs *engine)
808112ed2d3SChris Wilson {
809112ed2d3SChris Wilson 	struct i915_request *rq;
810112ed2d3SChris Wilson 	struct i915_vma *batch;
811112ed2d3SChris Wilson 	int i, err = 0;
812112ed2d3SChris Wilson 	u32 *cs;
813112ed2d3SChris Wilson 
814112ed2d3SChris Wilson 	batch = create_batch(ctx);
815112ed2d3SChris Wilson 	if (IS_ERR(batch))
816112ed2d3SChris Wilson 		return PTR_ERR(batch);
817112ed2d3SChris Wilson 
818112ed2d3SChris Wilson 	cs = i915_gem_object_pin_map(batch->obj, I915_MAP_WC);
819112ed2d3SChris Wilson 	if (IS_ERR(cs)) {
820112ed2d3SChris Wilson 		err = PTR_ERR(cs);
821112ed2d3SChris Wilson 		goto err_batch;
822112ed2d3SChris Wilson 	}
823112ed2d3SChris Wilson 
824767662bcSRobert M. Fosha 	*cs++ = MI_LOAD_REGISTER_IMM(whitelist_writable_count(engine));
825112ed2d3SChris Wilson 	for (i = 0; i < engine->whitelist.count; i++) {
826767662bcSRobert M. Fosha 		u32 reg = i915_mmio_reg_offset(engine->whitelist.list[i].reg);
827767662bcSRobert M. Fosha 
828767662bcSRobert M. Fosha 		if (ro_register(reg))
829767662bcSRobert M. Fosha 			continue;
830767662bcSRobert M. Fosha 
831767662bcSRobert M. Fosha 		*cs++ = reg;
832112ed2d3SChris Wilson 		*cs++ = 0xffffffff;
833112ed2d3SChris Wilson 	}
834112ed2d3SChris Wilson 	*cs++ = MI_BATCH_BUFFER_END;
835112ed2d3SChris Wilson 
836112ed2d3SChris Wilson 	i915_gem_object_flush_map(batch->obj);
837baea429dSTvrtko Ursulin 	intel_gt_chipset_flush(engine->gt);
838112ed2d3SChris Wilson 
83946472b3eSChris Wilson 	rq = igt_request_alloc(ctx, engine);
840112ed2d3SChris Wilson 	if (IS_ERR(rq)) {
841112ed2d3SChris Wilson 		err = PTR_ERR(rq);
842112ed2d3SChris Wilson 		goto err_unpin;
843112ed2d3SChris Wilson 	}
844112ed2d3SChris Wilson 
845112ed2d3SChris Wilson 	if (engine->emit_init_breadcrumb) { /* Be nice if we hang */
846112ed2d3SChris Wilson 		err = engine->emit_init_breadcrumb(rq);
847112ed2d3SChris Wilson 		if (err)
848112ed2d3SChris Wilson 			goto err_request;
849112ed2d3SChris Wilson 	}
850112ed2d3SChris Wilson 
851112ed2d3SChris Wilson 	/* Perform the writes from an unprivileged "user" batch */
852112ed2d3SChris Wilson 	err = engine->emit_bb_start(rq, batch->node.start, 0, 0);
853112ed2d3SChris Wilson 
854112ed2d3SChris Wilson err_request:
855112ed2d3SChris Wilson 	i915_request_add(rq);
8562f530945SChris Wilson 	if (i915_request_wait(rq, 0, HZ / 5) < 0)
857112ed2d3SChris Wilson 		err = -EIO;
858112ed2d3SChris Wilson 
859112ed2d3SChris Wilson err_unpin:
860112ed2d3SChris Wilson 	i915_gem_object_unpin_map(batch->obj);
861112ed2d3SChris Wilson err_batch:
862112ed2d3SChris Wilson 	i915_vma_unpin_and_release(&batch, 0);
863112ed2d3SChris Wilson 	return err;
864112ed2d3SChris Wilson }
865112ed2d3SChris Wilson 
866112ed2d3SChris Wilson struct regmask {
867112ed2d3SChris Wilson 	i915_reg_t reg;
868112ed2d3SChris Wilson 	unsigned long gen_mask;
869112ed2d3SChris Wilson };
870112ed2d3SChris Wilson 
871112ed2d3SChris Wilson static bool find_reg(struct drm_i915_private *i915,
872112ed2d3SChris Wilson 		     i915_reg_t reg,
873112ed2d3SChris Wilson 		     const struct regmask *tbl,
874112ed2d3SChris Wilson 		     unsigned long count)
875112ed2d3SChris Wilson {
876112ed2d3SChris Wilson 	u32 offset = i915_mmio_reg_offset(reg);
877112ed2d3SChris Wilson 
878112ed2d3SChris Wilson 	while (count--) {
879112ed2d3SChris Wilson 		if (INTEL_INFO(i915)->gen_mask & tbl->gen_mask &&
880112ed2d3SChris Wilson 		    i915_mmio_reg_offset(tbl->reg) == offset)
881112ed2d3SChris Wilson 			return true;
882112ed2d3SChris Wilson 		tbl++;
883112ed2d3SChris Wilson 	}
884112ed2d3SChris Wilson 
885112ed2d3SChris Wilson 	return false;
886112ed2d3SChris Wilson }
887112ed2d3SChris Wilson 
888112ed2d3SChris Wilson static bool pardon_reg(struct drm_i915_private *i915, i915_reg_t reg)
889112ed2d3SChris Wilson {
890112ed2d3SChris Wilson 	/* Alas, we must pardon some whitelists. Mistakes already made */
891112ed2d3SChris Wilson 	static const struct regmask pardon[] = {
892112ed2d3SChris Wilson 		{ GEN9_CTX_PREEMPT_REG, INTEL_GEN_MASK(9, 9) },
893112ed2d3SChris Wilson 		{ GEN8_L3SQCREG4, INTEL_GEN_MASK(9, 9) },
894112ed2d3SChris Wilson 	};
895112ed2d3SChris Wilson 
896112ed2d3SChris Wilson 	return find_reg(i915, reg, pardon, ARRAY_SIZE(pardon));
897112ed2d3SChris Wilson }
898112ed2d3SChris Wilson 
899112ed2d3SChris Wilson static bool result_eq(struct intel_engine_cs *engine,
900112ed2d3SChris Wilson 		      u32 a, u32 b, i915_reg_t reg)
901112ed2d3SChris Wilson {
902112ed2d3SChris Wilson 	if (a != b && !pardon_reg(engine->i915, reg)) {
903112ed2d3SChris Wilson 		pr_err("Whitelisted register 0x%4x not context saved: A=%08x, B=%08x\n",
904112ed2d3SChris Wilson 		       i915_mmio_reg_offset(reg), a, b);
905112ed2d3SChris Wilson 		return false;
906112ed2d3SChris Wilson 	}
907112ed2d3SChris Wilson 
908112ed2d3SChris Wilson 	return true;
909112ed2d3SChris Wilson }
910112ed2d3SChris Wilson 
911112ed2d3SChris Wilson static bool writeonly_reg(struct drm_i915_private *i915, i915_reg_t reg)
912112ed2d3SChris Wilson {
913112ed2d3SChris Wilson 	/* Some registers do not seem to behave and our writes unreadable */
914112ed2d3SChris Wilson 	static const struct regmask wo[] = {
915112ed2d3SChris Wilson 		{ GEN9_SLICE_COMMON_ECO_CHICKEN1, INTEL_GEN_MASK(9, 9) },
916112ed2d3SChris Wilson 	};
917112ed2d3SChris Wilson 
918112ed2d3SChris Wilson 	return find_reg(i915, reg, wo, ARRAY_SIZE(wo));
919112ed2d3SChris Wilson }
920112ed2d3SChris Wilson 
921112ed2d3SChris Wilson static bool result_neq(struct intel_engine_cs *engine,
922112ed2d3SChris Wilson 		       u32 a, u32 b, i915_reg_t reg)
923112ed2d3SChris Wilson {
924112ed2d3SChris Wilson 	if (a == b && !writeonly_reg(engine->i915, reg)) {
925112ed2d3SChris Wilson 		pr_err("Whitelist register 0x%4x:%08x was unwritable\n",
926112ed2d3SChris Wilson 		       i915_mmio_reg_offset(reg), a);
927112ed2d3SChris Wilson 		return false;
928112ed2d3SChris Wilson 	}
929112ed2d3SChris Wilson 
930112ed2d3SChris Wilson 	return true;
931112ed2d3SChris Wilson }
932112ed2d3SChris Wilson 
933112ed2d3SChris Wilson static int
934112ed2d3SChris Wilson check_whitelisted_registers(struct intel_engine_cs *engine,
935112ed2d3SChris Wilson 			    struct i915_vma *A,
936112ed2d3SChris Wilson 			    struct i915_vma *B,
937112ed2d3SChris Wilson 			    bool (*fn)(struct intel_engine_cs *engine,
938112ed2d3SChris Wilson 				       u32 a, u32 b,
939112ed2d3SChris Wilson 				       i915_reg_t reg))
940112ed2d3SChris Wilson {
941112ed2d3SChris Wilson 	u32 *a, *b;
942112ed2d3SChris Wilson 	int i, err;
943112ed2d3SChris Wilson 
944112ed2d3SChris Wilson 	a = i915_gem_object_pin_map(A->obj, I915_MAP_WB);
945112ed2d3SChris Wilson 	if (IS_ERR(a))
946112ed2d3SChris Wilson 		return PTR_ERR(a);
947112ed2d3SChris Wilson 
948112ed2d3SChris Wilson 	b = i915_gem_object_pin_map(B->obj, I915_MAP_WB);
949112ed2d3SChris Wilson 	if (IS_ERR(b)) {
950112ed2d3SChris Wilson 		err = PTR_ERR(b);
951112ed2d3SChris Wilson 		goto err_a;
952112ed2d3SChris Wilson 	}
953112ed2d3SChris Wilson 
954112ed2d3SChris Wilson 	err = 0;
955112ed2d3SChris Wilson 	for (i = 0; i < engine->whitelist.count; i++) {
956361b6905SLionel Landwerlin 		const struct i915_wa *wa = &engine->whitelist.list[i];
957361b6905SLionel Landwerlin 
9581e2b7f49SJohn Harrison 		if (i915_mmio_reg_offset(wa->reg) &
9591e2b7f49SJohn Harrison 		    RING_FORCE_TO_NONPRIV_ACCESS_RD)
960361b6905SLionel Landwerlin 			continue;
961361b6905SLionel Landwerlin 
962361b6905SLionel Landwerlin 		if (!fn(engine, a[i], b[i], wa->reg))
963112ed2d3SChris Wilson 			err = -EINVAL;
964112ed2d3SChris Wilson 	}
965112ed2d3SChris Wilson 
966112ed2d3SChris Wilson 	i915_gem_object_unpin_map(B->obj);
967112ed2d3SChris Wilson err_a:
968112ed2d3SChris Wilson 	i915_gem_object_unpin_map(A->obj);
969112ed2d3SChris Wilson 	return err;
970112ed2d3SChris Wilson }
971112ed2d3SChris Wilson 
972112ed2d3SChris Wilson static int live_isolated_whitelist(void *arg)
973112ed2d3SChris Wilson {
974112ed2d3SChris Wilson 	struct drm_i915_private *i915 = arg;
975112ed2d3SChris Wilson 	struct {
976112ed2d3SChris Wilson 		struct i915_gem_context *ctx;
977112ed2d3SChris Wilson 		struct i915_vma *scratch[2];
978112ed2d3SChris Wilson 	} client[2] = {};
979112ed2d3SChris Wilson 	struct intel_engine_cs *engine;
980112ed2d3SChris Wilson 	enum intel_engine_id id;
981112ed2d3SChris Wilson 	int i, err = 0;
982112ed2d3SChris Wilson 
983112ed2d3SChris Wilson 	/*
984112ed2d3SChris Wilson 	 * Check that a write into a whitelist register works, but
985112ed2d3SChris Wilson 	 * invisible to a second context.
986112ed2d3SChris Wilson 	 */
987112ed2d3SChris Wilson 
988112ed2d3SChris Wilson 	if (!intel_engines_has_context_isolation(i915))
989112ed2d3SChris Wilson 		return 0;
990112ed2d3SChris Wilson 
991e568ac38SChris Wilson 	if (!i915->kernel_context->vm)
992112ed2d3SChris Wilson 		return 0;
993112ed2d3SChris Wilson 
994112ed2d3SChris Wilson 	for (i = 0; i < ARRAY_SIZE(client); i++) {
995112ed2d3SChris Wilson 		struct i915_gem_context *c;
996112ed2d3SChris Wilson 
997112ed2d3SChris Wilson 		c = kernel_context(i915);
998112ed2d3SChris Wilson 		if (IS_ERR(c)) {
999112ed2d3SChris Wilson 			err = PTR_ERR(c);
1000112ed2d3SChris Wilson 			goto err;
1001112ed2d3SChris Wilson 		}
1002112ed2d3SChris Wilson 
1003e568ac38SChris Wilson 		client[i].scratch[0] = create_scratch(c->vm, 1024);
1004112ed2d3SChris Wilson 		if (IS_ERR(client[i].scratch[0])) {
1005112ed2d3SChris Wilson 			err = PTR_ERR(client[i].scratch[0]);
1006112ed2d3SChris Wilson 			kernel_context_close(c);
1007112ed2d3SChris Wilson 			goto err;
1008112ed2d3SChris Wilson 		}
1009112ed2d3SChris Wilson 
1010e568ac38SChris Wilson 		client[i].scratch[1] = create_scratch(c->vm, 1024);
1011112ed2d3SChris Wilson 		if (IS_ERR(client[i].scratch[1])) {
1012112ed2d3SChris Wilson 			err = PTR_ERR(client[i].scratch[1]);
1013112ed2d3SChris Wilson 			i915_vma_unpin_and_release(&client[i].scratch[0], 0);
1014112ed2d3SChris Wilson 			kernel_context_close(c);
1015112ed2d3SChris Wilson 			goto err;
1016112ed2d3SChris Wilson 		}
1017112ed2d3SChris Wilson 
1018112ed2d3SChris Wilson 		client[i].ctx = c;
1019112ed2d3SChris Wilson 	}
1020112ed2d3SChris Wilson 
1021112ed2d3SChris Wilson 	for_each_engine(engine, i915, id) {
1022767662bcSRobert M. Fosha 		if (!whitelist_writable_count(engine))
1023112ed2d3SChris Wilson 			continue;
1024112ed2d3SChris Wilson 
1025112ed2d3SChris Wilson 		/* Read default values */
1026112ed2d3SChris Wilson 		err = read_whitelisted_registers(client[0].ctx, engine,
1027112ed2d3SChris Wilson 						 client[0].scratch[0]);
1028112ed2d3SChris Wilson 		if (err)
1029112ed2d3SChris Wilson 			goto err;
1030112ed2d3SChris Wilson 
1031112ed2d3SChris Wilson 		/* Try to overwrite registers (should only affect ctx0) */
1032112ed2d3SChris Wilson 		err = scrub_whitelisted_registers(client[0].ctx, engine);
1033112ed2d3SChris Wilson 		if (err)
1034112ed2d3SChris Wilson 			goto err;
1035112ed2d3SChris Wilson 
1036112ed2d3SChris Wilson 		/* Read values from ctx1, we expect these to be defaults */
1037112ed2d3SChris Wilson 		err = read_whitelisted_registers(client[1].ctx, engine,
1038112ed2d3SChris Wilson 						 client[1].scratch[0]);
1039112ed2d3SChris Wilson 		if (err)
1040112ed2d3SChris Wilson 			goto err;
1041112ed2d3SChris Wilson 
1042112ed2d3SChris Wilson 		/* Verify that both reads return the same default values */
1043112ed2d3SChris Wilson 		err = check_whitelisted_registers(engine,
1044112ed2d3SChris Wilson 						  client[0].scratch[0],
1045112ed2d3SChris Wilson 						  client[1].scratch[0],
1046112ed2d3SChris Wilson 						  result_eq);
1047112ed2d3SChris Wilson 		if (err)
1048112ed2d3SChris Wilson 			goto err;
1049112ed2d3SChris Wilson 
1050112ed2d3SChris Wilson 		/* Read back the updated values in ctx0 */
1051112ed2d3SChris Wilson 		err = read_whitelisted_registers(client[0].ctx, engine,
1052112ed2d3SChris Wilson 						 client[0].scratch[1]);
1053112ed2d3SChris Wilson 		if (err)
1054112ed2d3SChris Wilson 			goto err;
1055112ed2d3SChris Wilson 
1056112ed2d3SChris Wilson 		/* User should be granted privilege to overwhite regs */
1057112ed2d3SChris Wilson 		err = check_whitelisted_registers(engine,
1058112ed2d3SChris Wilson 						  client[0].scratch[0],
1059112ed2d3SChris Wilson 						  client[0].scratch[1],
1060112ed2d3SChris Wilson 						  result_neq);
1061112ed2d3SChris Wilson 		if (err)
1062112ed2d3SChris Wilson 			goto err;
1063112ed2d3SChris Wilson 	}
1064112ed2d3SChris Wilson 
1065112ed2d3SChris Wilson err:
1066112ed2d3SChris Wilson 	for (i = 0; i < ARRAY_SIZE(client); i++) {
1067112ed2d3SChris Wilson 		if (!client[i].ctx)
1068112ed2d3SChris Wilson 			break;
1069112ed2d3SChris Wilson 
1070112ed2d3SChris Wilson 		i915_vma_unpin_and_release(&client[i].scratch[1], 0);
1071112ed2d3SChris Wilson 		i915_vma_unpin_and_release(&client[i].scratch[0], 0);
1072112ed2d3SChris Wilson 		kernel_context_close(client[i].ctx);
1073112ed2d3SChris Wilson 	}
1074112ed2d3SChris Wilson 
1075112ed2d3SChris Wilson 	if (igt_flush_test(i915, I915_WAIT_LOCKED))
1076112ed2d3SChris Wilson 		err = -EIO;
1077112ed2d3SChris Wilson 
1078112ed2d3SChris Wilson 	return err;
1079112ed2d3SChris Wilson }
1080112ed2d3SChris Wilson 
1081fde93886STvrtko Ursulin static bool
1082fde93886STvrtko Ursulin verify_wa_lists(struct i915_gem_context *ctx, struct wa_lists *lists,
1083fde93886STvrtko Ursulin 		const char *str)
1084112ed2d3SChris Wilson {
1085fde93886STvrtko Ursulin 	struct drm_i915_private *i915 = ctx->i915;
1086fde93886STvrtko Ursulin 	struct i915_gem_engines_iter it;
1087fde93886STvrtko Ursulin 	struct intel_context *ce;
1088112ed2d3SChris Wilson 	bool ok = true;
1089112ed2d3SChris Wilson 
1090112ed2d3SChris Wilson 	ok &= wa_list_verify(&i915->uncore, &lists->gt_wa_list, str);
1091112ed2d3SChris Wilson 
1092f277bc0cSChris Wilson 	for_each_gem_engine(ce, i915_gem_context_engines(ctx), it) {
1093fde93886STvrtko Ursulin 		enum intel_engine_id id = ce->engine->id;
1094fde93886STvrtko Ursulin 
1095fde93886STvrtko Ursulin 		ok &= engine_wa_list_verify(ce,
1096112ed2d3SChris Wilson 					    &lists->engine[id].wa_list,
1097112ed2d3SChris Wilson 					    str) == 0;
1098fde93886STvrtko Ursulin 
1099fde93886STvrtko Ursulin 		ok &= engine_wa_list_verify(ce,
1100fde93886STvrtko Ursulin 					    &lists->engine[id].ctx_wa_list,
1101fde93886STvrtko Ursulin 					    str) == 0;
1102112ed2d3SChris Wilson 	}
1103112ed2d3SChris Wilson 
1104112ed2d3SChris Wilson 	return ok;
1105112ed2d3SChris Wilson }
1106112ed2d3SChris Wilson 
1107112ed2d3SChris Wilson static int
1108fde93886STvrtko Ursulin live_gpu_reset_workarounds(void *arg)
1109112ed2d3SChris Wilson {
1110112ed2d3SChris Wilson 	struct drm_i915_private *i915 = arg;
1111fde93886STvrtko Ursulin 	struct i915_gem_context *ctx;
1112112ed2d3SChris Wilson 	intel_wakeref_t wakeref;
1113112ed2d3SChris Wilson 	struct wa_lists lists;
1114112ed2d3SChris Wilson 	bool ok;
1115112ed2d3SChris Wilson 
1116112ed2d3SChris Wilson 	if (!intel_has_gpu_reset(i915))
1117112ed2d3SChris Wilson 		return 0;
1118112ed2d3SChris Wilson 
1119fde93886STvrtko Ursulin 	ctx = kernel_context(i915);
1120fde93886STvrtko Ursulin 	if (IS_ERR(ctx))
1121fde93886STvrtko Ursulin 		return PTR_ERR(ctx);
1122fde93886STvrtko Ursulin 
1123f277bc0cSChris Wilson 	i915_gem_context_lock_engines(ctx);
1124f277bc0cSChris Wilson 
1125112ed2d3SChris Wilson 	pr_info("Verifying after GPU reset...\n");
1126112ed2d3SChris Wilson 
1127cb823ed9SChris Wilson 	igt_global_reset_lock(&i915->gt);
1128d858d569SDaniele Ceraolo Spurio 	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
1129112ed2d3SChris Wilson 
1130112ed2d3SChris Wilson 	reference_lists_init(i915, &lists);
1131112ed2d3SChris Wilson 
1132fde93886STvrtko Ursulin 	ok = verify_wa_lists(ctx, &lists, "before reset");
1133112ed2d3SChris Wilson 	if (!ok)
1134112ed2d3SChris Wilson 		goto out;
1135112ed2d3SChris Wilson 
1136cb823ed9SChris Wilson 	intel_gt_reset(&i915->gt, ALL_ENGINES, "live_workarounds");
1137112ed2d3SChris Wilson 
1138fde93886STvrtko Ursulin 	ok = verify_wa_lists(ctx, &lists, "after reset");
1139112ed2d3SChris Wilson 
1140112ed2d3SChris Wilson out:
1141f277bc0cSChris Wilson 	i915_gem_context_unlock_engines(ctx);
1142fde93886STvrtko Ursulin 	kernel_context_close(ctx);
1143112ed2d3SChris Wilson 	reference_lists_fini(i915, &lists);
1144d858d569SDaniele Ceraolo Spurio 	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
1145cb823ed9SChris Wilson 	igt_global_reset_unlock(&i915->gt);
1146112ed2d3SChris Wilson 
1147112ed2d3SChris Wilson 	return ok ? 0 : -ESRCH;
1148112ed2d3SChris Wilson }
1149112ed2d3SChris Wilson 
1150112ed2d3SChris Wilson static int
1151fde93886STvrtko Ursulin live_engine_reset_workarounds(void *arg)
1152112ed2d3SChris Wilson {
1153112ed2d3SChris Wilson 	struct drm_i915_private *i915 = arg;
1154f277bc0cSChris Wilson 	struct i915_gem_engines_iter it;
1155112ed2d3SChris Wilson 	struct i915_gem_context *ctx;
1156f277bc0cSChris Wilson 	struct intel_context *ce;
1157112ed2d3SChris Wilson 	struct igt_spinner spin;
1158112ed2d3SChris Wilson 	struct i915_request *rq;
1159112ed2d3SChris Wilson 	intel_wakeref_t wakeref;
1160112ed2d3SChris Wilson 	struct wa_lists lists;
1161112ed2d3SChris Wilson 	int ret = 0;
1162112ed2d3SChris Wilson 
1163112ed2d3SChris Wilson 	if (!intel_has_reset_engine(i915))
1164112ed2d3SChris Wilson 		return 0;
1165112ed2d3SChris Wilson 
1166112ed2d3SChris Wilson 	ctx = kernel_context(i915);
1167112ed2d3SChris Wilson 	if (IS_ERR(ctx))
1168112ed2d3SChris Wilson 		return PTR_ERR(ctx);
1169112ed2d3SChris Wilson 
1170cb823ed9SChris Wilson 	igt_global_reset_lock(&i915->gt);
1171d858d569SDaniele Ceraolo Spurio 	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
1172112ed2d3SChris Wilson 
1173112ed2d3SChris Wilson 	reference_lists_init(i915, &lists);
1174112ed2d3SChris Wilson 
1175f277bc0cSChris Wilson 	for_each_gem_engine(ce, i915_gem_context_lock_engines(ctx), it) {
1176f277bc0cSChris Wilson 		struct intel_engine_cs *engine = ce->engine;
1177112ed2d3SChris Wilson 		bool ok;
1178112ed2d3SChris Wilson 
1179112ed2d3SChris Wilson 		pr_info("Verifying after %s reset...\n", engine->name);
1180112ed2d3SChris Wilson 
1181fde93886STvrtko Ursulin 		ok = verify_wa_lists(ctx, &lists, "before reset");
1182112ed2d3SChris Wilson 		if (!ok) {
1183112ed2d3SChris Wilson 			ret = -ESRCH;
1184112ed2d3SChris Wilson 			goto err;
1185112ed2d3SChris Wilson 		}
1186112ed2d3SChris Wilson 
1187cb823ed9SChris Wilson 		intel_engine_reset(engine, "live_workarounds");
1188112ed2d3SChris Wilson 
1189fde93886STvrtko Ursulin 		ok = verify_wa_lists(ctx, &lists, "after idle reset");
1190112ed2d3SChris Wilson 		if (!ok) {
1191112ed2d3SChris Wilson 			ret = -ESRCH;
1192112ed2d3SChris Wilson 			goto err;
1193112ed2d3SChris Wilson 		}
1194112ed2d3SChris Wilson 
1195f277bc0cSChris Wilson 		ret = igt_spinner_init(&spin, engine->gt);
1196112ed2d3SChris Wilson 		if (ret)
1197112ed2d3SChris Wilson 			goto err;
1198112ed2d3SChris Wilson 
1199f277bc0cSChris Wilson 		rq = igt_spinner_create_request(&spin, ce, MI_NOOP);
1200112ed2d3SChris Wilson 		if (IS_ERR(rq)) {
1201112ed2d3SChris Wilson 			ret = PTR_ERR(rq);
1202112ed2d3SChris Wilson 			igt_spinner_fini(&spin);
1203112ed2d3SChris Wilson 			goto err;
1204112ed2d3SChris Wilson 		}
1205112ed2d3SChris Wilson 
1206112ed2d3SChris Wilson 		i915_request_add(rq);
1207112ed2d3SChris Wilson 
1208112ed2d3SChris Wilson 		if (!igt_wait_for_spinner(&spin, rq)) {
1209112ed2d3SChris Wilson 			pr_err("Spinner failed to start\n");
1210112ed2d3SChris Wilson 			igt_spinner_fini(&spin);
1211112ed2d3SChris Wilson 			ret = -ETIMEDOUT;
1212112ed2d3SChris Wilson 			goto err;
1213112ed2d3SChris Wilson 		}
1214112ed2d3SChris Wilson 
1215cb823ed9SChris Wilson 		intel_engine_reset(engine, "live_workarounds");
1216112ed2d3SChris Wilson 
1217112ed2d3SChris Wilson 		igt_spinner_end(&spin);
1218112ed2d3SChris Wilson 		igt_spinner_fini(&spin);
1219112ed2d3SChris Wilson 
1220fde93886STvrtko Ursulin 		ok = verify_wa_lists(ctx, &lists, "after busy reset");
1221112ed2d3SChris Wilson 		if (!ok) {
1222112ed2d3SChris Wilson 			ret = -ESRCH;
1223112ed2d3SChris Wilson 			goto err;
1224112ed2d3SChris Wilson 		}
1225112ed2d3SChris Wilson 	}
1226112ed2d3SChris Wilson err:
1227f277bc0cSChris Wilson 	i915_gem_context_unlock_engines(ctx);
1228112ed2d3SChris Wilson 	reference_lists_fini(i915, &lists);
1229d858d569SDaniele Ceraolo Spurio 	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
1230cb823ed9SChris Wilson 	igt_global_reset_unlock(&i915->gt);
1231112ed2d3SChris Wilson 	kernel_context_close(ctx);
1232112ed2d3SChris Wilson 
1233112ed2d3SChris Wilson 	igt_flush_test(i915, I915_WAIT_LOCKED);
1234112ed2d3SChris Wilson 
1235112ed2d3SChris Wilson 	return ret;
1236112ed2d3SChris Wilson }
1237112ed2d3SChris Wilson 
1238112ed2d3SChris Wilson int intel_workarounds_live_selftests(struct drm_i915_private *i915)
1239112ed2d3SChris Wilson {
1240112ed2d3SChris Wilson 	static const struct i915_subtest tests[] = {
1241112ed2d3SChris Wilson 		SUBTEST(live_dirty_whitelist),
1242112ed2d3SChris Wilson 		SUBTEST(live_reset_whitelist),
1243112ed2d3SChris Wilson 		SUBTEST(live_isolated_whitelist),
1244fde93886STvrtko Ursulin 		SUBTEST(live_gpu_reset_workarounds),
1245fde93886STvrtko Ursulin 		SUBTEST(live_engine_reset_workarounds),
1246112ed2d3SChris Wilson 	};
1247112ed2d3SChris Wilson 	int err;
1248112ed2d3SChris Wilson 
1249cb823ed9SChris Wilson 	if (intel_gt_is_wedged(&i915->gt))
1250112ed2d3SChris Wilson 		return 0;
1251112ed2d3SChris Wilson 
1252112ed2d3SChris Wilson 	mutex_lock(&i915->drm.struct_mutex);
1253112ed2d3SChris Wilson 	err = i915_subtests(tests, i915);
1254112ed2d3SChris Wilson 	mutex_unlock(&i915->drm.struct_mutex);
1255112ed2d3SChris Wilson 
1256112ed2d3SChris Wilson 	return err;
1257112ed2d3SChris Wilson }
1258