| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379 |
- // SPDX-License-Identifier: MIT
- /*
- * Copyright © 2018 Intel Corporation
- */
- #include "gem/i915_gem_internal.h"
- #include "gem/i915_gem_pm.h"
- #include "gt/intel_engine_user.h"
- #include "gt/intel_gt.h"
- #include "i915_selftest.h"
- #include "intel_reset.h"
- #include "selftests/igt_flush_test.h"
- #include "selftests/igt_reset.h"
- #include "selftests/igt_spinner.h"
- #include "selftests/intel_scheduler_helpers.h"
- #include "selftests/mock_drm.h"
- #include "gem/selftests/igt_gem_utils.h"
- #include "gem/selftests/mock_context.h"
- static const struct wo_register {
- enum intel_platform platform;
- u32 reg;
- } wo_registers[] = {
- { INTEL_GEMINILAKE, 0x731c }
- };
- struct wa_lists {
- struct i915_wa_list gt_wa_list;
- struct {
- struct i915_wa_list wa_list;
- struct i915_wa_list ctx_wa_list;
- } engine[I915_NUM_ENGINES];
- };
- static int request_add_sync(struct i915_request *rq, int err)
- {
- i915_request_get(rq);
- i915_request_add(rq);
- if (i915_request_wait(rq, 0, HZ / 5) < 0)
- err = -EIO;
- i915_request_put(rq);
- return err;
- }
- static int request_add_spin(struct i915_request *rq, struct igt_spinner *spin)
- {
- int err = 0;
- i915_request_get(rq);
- i915_request_add(rq);
- if (spin && !igt_wait_for_spinner(spin, rq))
- err = -ETIMEDOUT;
- i915_request_put(rq);
- return err;
- }
- static void
- reference_lists_init(struct intel_gt *gt, struct wa_lists *lists)
- {
- struct intel_engine_cs *engine;
- enum intel_engine_id id;
- memset(lists, 0, sizeof(*lists));
- wa_init_start(&lists->gt_wa_list, gt, "GT_REF", "global");
- gt_init_workarounds(gt, &lists->gt_wa_list);
- wa_init_finish(&lists->gt_wa_list);
- for_each_engine(engine, gt, id) {
- struct i915_wa_list *wal = &lists->engine[id].wa_list;
- wa_init_start(wal, gt, "REF", engine->name);
- engine_init_workarounds(engine, wal);
- wa_init_finish(wal);
- __intel_engine_init_ctx_wa(engine,
- &lists->engine[id].ctx_wa_list,
- "CTX_REF");
- }
- }
- static void
- reference_lists_fini(struct intel_gt *gt, struct wa_lists *lists)
- {
- struct intel_engine_cs *engine;
- enum intel_engine_id id;
- for_each_engine(engine, gt, id)
- intel_wa_list_free(&lists->engine[id].wa_list);
- intel_wa_list_free(&lists->gt_wa_list);
- }
- static struct drm_i915_gem_object *
- read_nonprivs(struct intel_context *ce)
- {
- struct intel_engine_cs *engine = ce->engine;
- const u32 base = engine->mmio_base;
- struct drm_i915_gem_object *result;
- struct i915_request *rq;
- struct i915_vma *vma;
- u32 srm, *cs;
- int err;
- int i;
- result = i915_gem_object_create_internal(engine->i915, PAGE_SIZE);
- if (IS_ERR(result))
- return result;
- i915_gem_object_set_cache_coherency(result, I915_CACHE_LLC);
- cs = i915_gem_object_pin_map_unlocked(result, I915_MAP_WB);
- if (IS_ERR(cs)) {
- err = PTR_ERR(cs);
- goto err_obj;
- }
- memset(cs, 0xc5, PAGE_SIZE);
- i915_gem_object_flush_map(result);
- i915_gem_object_unpin_map(result);
- vma = i915_vma_instance(result, &engine->gt->ggtt->vm, NULL);
- if (IS_ERR(vma)) {
- err = PTR_ERR(vma);
- goto err_obj;
- }
- err = i915_vma_pin(vma, 0, 0, PIN_GLOBAL);
- if (err)
- goto err_obj;
- rq = intel_context_create_request(ce);
- if (IS_ERR(rq)) {
- err = PTR_ERR(rq);
- goto err_pin;
- }
- err = igt_vma_move_to_active_unlocked(vma, rq, EXEC_OBJECT_WRITE);
- if (err)
- goto err_req;
- srm = MI_STORE_REGISTER_MEM | MI_SRM_LRM_GLOBAL_GTT;
- if (GRAPHICS_VER(engine->i915) >= 8)
- srm++;
- cs = intel_ring_begin(rq, 4 * RING_MAX_NONPRIV_SLOTS);
- if (IS_ERR(cs)) {
- err = PTR_ERR(cs);
- goto err_req;
- }
- for (i = 0; i < RING_MAX_NONPRIV_SLOTS; i++) {
- *cs++ = srm;
- *cs++ = i915_mmio_reg_offset(RING_FORCE_TO_NONPRIV(base, i));
- *cs++ = i915_ggtt_offset(vma) + sizeof(u32) * i;
- *cs++ = 0;
- }
- intel_ring_advance(rq, cs);
- i915_request_add(rq);
- i915_vma_unpin(vma);
- return result;
- err_req:
- i915_request_add(rq);
- err_pin:
- i915_vma_unpin(vma);
- err_obj:
- i915_gem_object_put(result);
- return ERR_PTR(err);
- }
- static u32
- get_whitelist_reg(const struct intel_engine_cs *engine, unsigned int i)
- {
- i915_reg_t reg = i < engine->whitelist.count ?
- engine->whitelist.list[i].reg :
- RING_NOPID(engine->mmio_base);
- return i915_mmio_reg_offset(reg);
- }
- static void
- print_results(const struct intel_engine_cs *engine, const u32 *results)
- {
- unsigned int i;
- for (i = 0; i < RING_MAX_NONPRIV_SLOTS; i++) {
- u32 expected = get_whitelist_reg(engine, i);
- u32 actual = results[i];
- pr_info("RING_NONPRIV[%d]: expected 0x%08x, found 0x%08x\n",
- i, expected, actual);
- }
- }
- static int check_whitelist(struct intel_context *ce)
- {
- struct intel_engine_cs *engine = ce->engine;
- struct drm_i915_gem_object *results;
- struct intel_wedge_me wedge;
- u32 *vaddr;
- int err;
- int i;
- results = read_nonprivs(ce);
- if (IS_ERR(results))
- return PTR_ERR(results);
- err = 0;
- i915_gem_object_lock(results, NULL);
- intel_wedge_on_timeout(&wedge, engine->gt, HZ / 5) /* safety net! */
- err = i915_gem_object_set_to_cpu_domain(results, false);
- if (intel_gt_is_wedged(engine->gt))
- err = -EIO;
- if (err)
- goto out_put;
- vaddr = i915_gem_object_pin_map(results, I915_MAP_WB);
- if (IS_ERR(vaddr)) {
- err = PTR_ERR(vaddr);
- goto out_put;
- }
- for (i = 0; i < RING_MAX_NONPRIV_SLOTS; i++) {
- u32 expected = get_whitelist_reg(engine, i);
- u32 actual = vaddr[i];
- if (expected != actual) {
- print_results(engine, vaddr);
- pr_err("Invalid RING_NONPRIV[%d], expected 0x%08x, found 0x%08x\n",
- i, expected, actual);
- err = -EINVAL;
- break;
- }
- }
- i915_gem_object_unpin_map(results);
- out_put:
- i915_gem_object_unlock(results);
- i915_gem_object_put(results);
- return err;
- }
- static int do_device_reset(struct intel_engine_cs *engine)
- {
- intel_gt_reset(engine->gt, engine->mask, "live_workarounds");
- return 0;
- }
- static int do_engine_reset(struct intel_engine_cs *engine)
- {
- return intel_engine_reset(engine, "live_workarounds");
- }
- static int do_guc_reset(struct intel_engine_cs *engine)
- {
- /* Currently a no-op as the reset is handled by GuC */
- return 0;
- }
- static int
- switch_to_scratch_context(struct intel_engine_cs *engine,
- struct igt_spinner *spin,
- struct i915_request **rq)
- {
- struct intel_context *ce;
- int err = 0;
- ce = intel_context_create(engine);
- if (IS_ERR(ce))
- return PTR_ERR(ce);
- *rq = igt_spinner_create_request(spin, ce, MI_NOOP);
- intel_context_put(ce);
- if (IS_ERR(*rq)) {
- spin = NULL;
- err = PTR_ERR(*rq);
- goto err;
- }
- err = request_add_spin(*rq, spin);
- err:
- if (err && spin)
- igt_spinner_end(spin);
- return err;
- }
- static int check_whitelist_across_reset(struct intel_engine_cs *engine,
- int (*reset)(struct intel_engine_cs *),
- const char *name)
- {
- struct intel_context *ce, *tmp;
- struct igt_spinner spin;
- struct i915_request *rq;
- intel_wakeref_t wakeref;
- int err;
- pr_info("Checking %d whitelisted registers on %s (RING_NONPRIV) [%s]\n",
- engine->whitelist.count, engine->name, name);
- ce = intel_context_create(engine);
- if (IS_ERR(ce))
- return PTR_ERR(ce);
- err = igt_spinner_init(&spin, engine->gt);
- if (err)
- goto out_ctx;
- err = check_whitelist(ce);
- if (err) {
- pr_err("Invalid whitelist *before* %s reset!\n", name);
- goto out_spin;
- }
- err = switch_to_scratch_context(engine, &spin, &rq);
- if (err)
- goto out_spin;
- /* Ensure the spinner hasn't aborted */
- if (i915_request_completed(rq)) {
- pr_err("%s spinner failed to start\n", name);
- err = -ETIMEDOUT;
- goto out_spin;
- }
- with_intel_runtime_pm(engine->uncore->rpm, wakeref)
- err = reset(engine);
- /* Ensure the reset happens and kills the engine */
- if (err == 0)
- err = intel_selftest_wait_for_rq(rq);
- igt_spinner_end(&spin);
- if (err) {
- pr_err("%s reset failed\n", name);
- goto out_spin;
- }
- err = check_whitelist(ce);
- if (err) {
- pr_err("Whitelist not preserved in context across %s reset!\n",
- name);
- goto out_spin;
- }
- tmp = intel_context_create(engine);
- if (IS_ERR(tmp)) {
- err = PTR_ERR(tmp);
- goto out_spin;
- }
- intel_context_put(ce);
- ce = tmp;
- err = check_whitelist(ce);
- if (err) {
- pr_err("Invalid whitelist *after* %s reset in fresh context!\n",
- name);
- goto out_spin;
- }
- out_spin:
- igt_spinner_fini(&spin);
- out_ctx:
- intel_context_put(ce);
- return err;
- }
- static struct i915_vma *create_batch(struct i915_address_space *vm)
- {
- struct drm_i915_gem_object *obj;
- struct i915_vma *vma;
- int err;
- obj = i915_gem_object_create_internal(vm->i915, 16 * PAGE_SIZE);
- if (IS_ERR(obj))
- return ERR_CAST(obj);
- vma = i915_vma_instance(obj, vm, NULL);
- if (IS_ERR(vma)) {
- err = PTR_ERR(vma);
- goto err_obj;
- }
- err = i915_vma_pin(vma, 0, 0, PIN_USER);
- if (err)
- goto err_obj;
- return vma;
- err_obj:
- i915_gem_object_put(obj);
- return ERR_PTR(err);
- }
- static u32 reg_write(u32 old, u32 new, u32 rsvd)
- {
- if (rsvd == 0x0000ffff) {
- old &= ~(new >> 16);
- old |= new & (new >> 16);
- } else {
- old &= ~rsvd;
- old |= new & rsvd;
- }
- return old;
- }
- static bool wo_register(struct intel_engine_cs *engine, u32 reg)
- {
- enum intel_platform platform = INTEL_INFO(engine->i915)->platform;
- int i;
- if ((reg & RING_FORCE_TO_NONPRIV_ACCESS_MASK) ==
- RING_FORCE_TO_NONPRIV_ACCESS_WR)
- return true;
- for (i = 0; i < ARRAY_SIZE(wo_registers); i++) {
- if (wo_registers[i].platform == platform &&
- wo_registers[i].reg == reg)
- return true;
- }
- return false;
- }
- static bool timestamp(const struct intel_engine_cs *engine, u32 reg)
- {
- reg = (reg - engine->mmio_base) & ~RING_FORCE_TO_NONPRIV_ACCESS_MASK;
- switch (reg) {
- case 0x358:
- case 0x35c:
- case 0x3a8:
- return true;
- default:
- return false;
- }
- }
- static bool ro_register(u32 reg)
- {
- if ((reg & RING_FORCE_TO_NONPRIV_ACCESS_MASK) ==
- RING_FORCE_TO_NONPRIV_ACCESS_RD)
- return true;
- return false;
- }
- static int whitelist_writable_count(struct intel_engine_cs *engine)
- {
- int count = engine->whitelist.count;
- int i;
- for (i = 0; i < engine->whitelist.count; i++) {
- u32 reg = i915_mmio_reg_offset(engine->whitelist.list[i].reg);
- if (ro_register(reg))
- count--;
- }
- return count;
- }
- static int check_dirty_whitelist(struct intel_context *ce)
- {
- const u32 values[] = {
- 0x00000000,
- 0x01010101,
- 0x10100101,
- 0x03030303,
- 0x30300303,
- 0x05050505,
- 0x50500505,
- 0x0f0f0f0f,
- 0xf00ff00f,
- 0x10101010,
- 0xf0f01010,
- 0x30303030,
- 0xa0a03030,
- 0x50505050,
- 0xc0c05050,
- 0xf0f0f0f0,
- 0x11111111,
- 0x33333333,
- 0x55555555,
- 0x0000ffff,
- 0x00ff00ff,
- 0xff0000ff,
- 0xffff00ff,
- 0xffffffff,
- };
- struct intel_engine_cs *engine = ce->engine;
- struct i915_vma *scratch;
- struct i915_vma *batch;
- int err = 0, i, v, sz;
- u32 *cs, *results;
- sz = (2 * ARRAY_SIZE(values) + 1) * sizeof(u32);
- scratch = __vm_create_scratch_for_read_pinned(ce->vm, sz);
- if (IS_ERR(scratch))
- return PTR_ERR(scratch);
- batch = create_batch(ce->vm);
- if (IS_ERR(batch)) {
- err = PTR_ERR(batch);
- goto out_scratch;
- }
- for (i = 0; i < engine->whitelist.count; i++) {
- u32 reg = i915_mmio_reg_offset(engine->whitelist.list[i].reg);
- struct i915_gem_ww_ctx ww;
- u64 addr = i915_vma_offset(scratch);
- struct i915_request *rq;
- u32 srm, lrm, rsvd;
- u32 expect;
- int idx;
- bool ro_reg;
- if (wo_register(engine, reg))
- continue;
- if (timestamp(engine, reg))
- continue; /* timestamps are expected to autoincrement */
- ro_reg = ro_register(reg);
- i915_gem_ww_ctx_init(&ww, false);
- retry:
- cs = NULL;
- err = i915_gem_object_lock(scratch->obj, &ww);
- if (!err)
- err = i915_gem_object_lock(batch->obj, &ww);
- if (!err)
- err = intel_context_pin_ww(ce, &ww);
- if (err)
- goto out;
- cs = i915_gem_object_pin_map(batch->obj, I915_MAP_WC);
- if (IS_ERR(cs)) {
- err = PTR_ERR(cs);
- goto out_ctx;
- }
- results = i915_gem_object_pin_map(scratch->obj, I915_MAP_WB);
- if (IS_ERR(results)) {
- err = PTR_ERR(results);
- goto out_unmap_batch;
- }
- /* Clear non priv flags */
- reg &= RING_FORCE_TO_NONPRIV_ADDRESS_MASK;
- srm = MI_STORE_REGISTER_MEM;
- lrm = MI_LOAD_REGISTER_MEM;
- if (GRAPHICS_VER(engine->i915) >= 8)
- lrm++, srm++;
- pr_debug("%s: Writing garbage to %x\n",
- engine->name, reg);
- /* SRM original */
- *cs++ = srm;
- *cs++ = reg;
- *cs++ = lower_32_bits(addr);
- *cs++ = upper_32_bits(addr);
- idx = 1;
- for (v = 0; v < ARRAY_SIZE(values); v++) {
- /* LRI garbage */
- *cs++ = MI_LOAD_REGISTER_IMM(1);
- *cs++ = reg;
- *cs++ = values[v];
- /* SRM result */
- *cs++ = srm;
- *cs++ = reg;
- *cs++ = lower_32_bits(addr + sizeof(u32) * idx);
- *cs++ = upper_32_bits(addr + sizeof(u32) * idx);
- idx++;
- }
- for (v = 0; v < ARRAY_SIZE(values); v++) {
- /* LRI garbage */
- *cs++ = MI_LOAD_REGISTER_IMM(1);
- *cs++ = reg;
- *cs++ = ~values[v];
- /* SRM result */
- *cs++ = srm;
- *cs++ = reg;
- *cs++ = lower_32_bits(addr + sizeof(u32) * idx);
- *cs++ = upper_32_bits(addr + sizeof(u32) * idx);
- idx++;
- }
- GEM_BUG_ON(idx * sizeof(u32) > scratch->size);
- /* LRM original -- don't leave garbage in the context! */
- *cs++ = lrm;
- *cs++ = reg;
- *cs++ = lower_32_bits(addr);
- *cs++ = upper_32_bits(addr);
- *cs++ = MI_BATCH_BUFFER_END;
- i915_gem_object_flush_map(batch->obj);
- i915_gem_object_unpin_map(batch->obj);
- intel_gt_chipset_flush(engine->gt);
- cs = NULL;
- rq = i915_request_create(ce);
- if (IS_ERR(rq)) {
- err = PTR_ERR(rq);
- goto out_unmap_scratch;
- }
- if (engine->emit_init_breadcrumb) { /* Be nice if we hang */
- err = engine->emit_init_breadcrumb(rq);
- if (err)
- goto err_request;
- }
- err = i915_vma_move_to_active(batch, rq, 0);
- if (err)
- goto err_request;
- err = i915_vma_move_to_active(scratch, rq,
- EXEC_OBJECT_WRITE);
- if (err)
- goto err_request;
- err = engine->emit_bb_start(rq,
- i915_vma_offset(batch), PAGE_SIZE,
- 0);
- if (err)
- goto err_request;
- err_request:
- err = request_add_sync(rq, err);
- if (err) {
- pr_err("%s: Futzing %x timedout; cancelling test\n",
- engine->name, reg);
- intel_gt_set_wedged(engine->gt);
- goto out_unmap_scratch;
- }
- GEM_BUG_ON(values[ARRAY_SIZE(values) - 1] != 0xffffffff);
- if (!ro_reg) {
- /* detect write masking */
- rsvd = results[ARRAY_SIZE(values)];
- if (!rsvd) {
- pr_err("%s: Unable to write to whitelisted register %x\n",
- engine->name, reg);
- err = -EINVAL;
- goto out_unmap_scratch;
- }
- } else {
- rsvd = 0;
- }
- expect = results[0];
- idx = 1;
- for (v = 0; v < ARRAY_SIZE(values); v++) {
- if (ro_reg)
- expect = results[0];
- else
- expect = reg_write(expect, values[v], rsvd);
- if (results[idx] != expect)
- err++;
- idx++;
- }
- for (v = 0; v < ARRAY_SIZE(values); v++) {
- if (ro_reg)
- expect = results[0];
- else
- expect = reg_write(expect, ~values[v], rsvd);
- if (results[idx] != expect)
- err++;
- idx++;
- }
- if (err) {
- pr_err("%s: %d mismatch between values written to whitelisted register [%x], and values read back!\n",
- engine->name, err, reg);
- if (ro_reg)
- pr_info("%s: Whitelisted read-only register: %x, original value %08x\n",
- engine->name, reg, results[0]);
- else
- pr_info("%s: Whitelisted register: %x, original value %08x, rsvd %08x\n",
- engine->name, reg, results[0], rsvd);
- expect = results[0];
- idx = 1;
- for (v = 0; v < ARRAY_SIZE(values); v++) {
- u32 w = values[v];
- if (ro_reg)
- expect = results[0];
- else
- expect = reg_write(expect, w, rsvd);
- pr_info("Wrote %08x, read %08x, expect %08x\n",
- w, results[idx], expect);
- idx++;
- }
- for (v = 0; v < ARRAY_SIZE(values); v++) {
- u32 w = ~values[v];
- if (ro_reg)
- expect = results[0];
- else
- expect = reg_write(expect, w, rsvd);
- pr_info("Wrote %08x, read %08x, expect %08x\n",
- w, results[idx], expect);
- idx++;
- }
- err = -EINVAL;
- }
- out_unmap_scratch:
- i915_gem_object_unpin_map(scratch->obj);
- out_unmap_batch:
- if (cs)
- i915_gem_object_unpin_map(batch->obj);
- out_ctx:
- intel_context_unpin(ce);
- out:
- if (err == -EDEADLK) {
- err = i915_gem_ww_ctx_backoff(&ww);
- if (!err)
- goto retry;
- }
- i915_gem_ww_ctx_fini(&ww);
- if (err)
- break;
- }
- if (igt_flush_test(engine->i915))
- err = -EIO;
- i915_vma_unpin_and_release(&batch, 0);
- out_scratch:
- i915_vma_unpin_and_release(&scratch, 0);
- return err;
- }
- static int live_dirty_whitelist(void *arg)
- {
- struct intel_gt *gt = arg;
- struct intel_engine_cs *engine;
- enum intel_engine_id id;
- /* Can the user write to the whitelisted registers? */
- if (GRAPHICS_VER(gt->i915) < 7) /* minimum requirement for LRI, SRM, LRM */
- return 0;
- for_each_engine(engine, gt, id) {
- struct intel_context *ce;
- int err;
- if (engine->whitelist.count == 0)
- continue;
- ce = intel_context_create(engine);
- if (IS_ERR(ce))
- return PTR_ERR(ce);
- err = check_dirty_whitelist(ce);
- intel_context_put(ce);
- if (err)
- return err;
- }
- return 0;
- }
- static int live_reset_whitelist(void *arg)
- {
- struct intel_gt *gt = arg;
- struct intel_engine_cs *engine;
- enum intel_engine_id id;
- int err = 0;
- /* If we reset the gpu, we should not lose the RING_NONPRIV */
- igt_global_reset_lock(gt);
- for_each_engine(engine, gt, id) {
- if (engine->whitelist.count == 0)
- continue;
- if (intel_has_reset_engine(gt)) {
- if (intel_engine_uses_guc(engine)) {
- struct intel_selftest_saved_policy saved;
- int err2;
- err = intel_selftest_modify_policy(engine, &saved,
- SELFTEST_SCHEDULER_MODIFY_FAST_RESET);
- if (err)
- goto out;
- err = check_whitelist_across_reset(engine,
- do_guc_reset,
- "guc");
- err2 = intel_selftest_restore_policy(engine, &saved);
- if (err == 0)
- err = err2;
- } else {
- err = check_whitelist_across_reset(engine,
- do_engine_reset,
- "engine");
- }
- if (err)
- goto out;
- }
- if (intel_has_gpu_reset(gt)) {
- err = check_whitelist_across_reset(engine,
- do_device_reset,
- "device");
- if (err)
- goto out;
- }
- }
- out:
- igt_global_reset_unlock(gt);
- return err;
- }
- static int read_whitelisted_registers(struct intel_context *ce,
- struct i915_vma *results)
- {
- struct intel_engine_cs *engine = ce->engine;
- struct i915_request *rq;
- int i, err = 0;
- u32 srm, *cs;
- rq = intel_context_create_request(ce);
- if (IS_ERR(rq))
- return PTR_ERR(rq);
- err = igt_vma_move_to_active_unlocked(results, rq, EXEC_OBJECT_WRITE);
- if (err)
- goto err_req;
- srm = MI_STORE_REGISTER_MEM;
- if (GRAPHICS_VER(engine->i915) >= 8)
- srm++;
- cs = intel_ring_begin(rq, 4 * engine->whitelist.count);
- if (IS_ERR(cs)) {
- err = PTR_ERR(cs);
- goto err_req;
- }
- for (i = 0; i < engine->whitelist.count; i++) {
- u64 offset = i915_vma_offset(results) + sizeof(u32) * i;
- u32 reg = i915_mmio_reg_offset(engine->whitelist.list[i].reg);
- /* Clear non priv flags */
- reg &= RING_FORCE_TO_NONPRIV_ADDRESS_MASK;
- *cs++ = srm;
- *cs++ = reg;
- *cs++ = lower_32_bits(offset);
- *cs++ = upper_32_bits(offset);
- }
- intel_ring_advance(rq, cs);
- err_req:
- return request_add_sync(rq, err);
- }
- static int scrub_whitelisted_registers(struct intel_context *ce)
- {
- struct intel_engine_cs *engine = ce->engine;
- struct i915_request *rq;
- struct i915_vma *batch;
- int i, err = 0;
- u32 *cs;
- batch = create_batch(ce->vm);
- if (IS_ERR(batch))
- return PTR_ERR(batch);
- cs = i915_gem_object_pin_map_unlocked(batch->obj, I915_MAP_WC);
- if (IS_ERR(cs)) {
- err = PTR_ERR(cs);
- goto err_batch;
- }
- *cs++ = MI_LOAD_REGISTER_IMM(whitelist_writable_count(engine));
- for (i = 0; i < engine->whitelist.count; i++) {
- u32 reg = i915_mmio_reg_offset(engine->whitelist.list[i].reg);
- if (ro_register(reg))
- continue;
- /* Clear non priv flags */
- reg &= RING_FORCE_TO_NONPRIV_ADDRESS_MASK;
- *cs++ = reg;
- *cs++ = 0xffffffff;
- }
- *cs++ = MI_BATCH_BUFFER_END;
- i915_gem_object_flush_map(batch->obj);
- intel_gt_chipset_flush(engine->gt);
- rq = intel_context_create_request(ce);
- if (IS_ERR(rq)) {
- err = PTR_ERR(rq);
- goto err_unpin;
- }
- if (engine->emit_init_breadcrumb) { /* Be nice if we hang */
- err = engine->emit_init_breadcrumb(rq);
- if (err)
- goto err_request;
- }
- err = igt_vma_move_to_active_unlocked(batch, rq, 0);
- if (err)
- goto err_request;
- /* Perform the writes from an unprivileged "user" batch */
- err = engine->emit_bb_start(rq, i915_vma_offset(batch), 0, 0);
- err_request:
- err = request_add_sync(rq, err);
- err_unpin:
- i915_gem_object_unpin_map(batch->obj);
- err_batch:
- i915_vma_unpin_and_release(&batch, 0);
- return err;
- }
- struct regmask {
- i915_reg_t reg;
- u8 graphics_ver;
- };
- static bool find_reg(struct drm_i915_private *i915,
- i915_reg_t reg,
- const struct regmask *tbl,
- unsigned long count)
- {
- u32 offset = i915_mmio_reg_offset(reg);
- while (count--) {
- if (GRAPHICS_VER(i915) == tbl->graphics_ver &&
- i915_mmio_reg_offset(tbl->reg) == offset)
- return true;
- tbl++;
- }
- return false;
- }
- static bool pardon_reg(struct drm_i915_private *i915, i915_reg_t reg)
- {
- /* Alas, we must pardon some whitelists. Mistakes already made */
- static const struct regmask pardon[] = {
- { GEN9_CTX_PREEMPT_REG, 9 },
- { _MMIO(0xb118), 9 }, /* GEN8_L3SQCREG4 */
- };
- return find_reg(i915, reg, pardon, ARRAY_SIZE(pardon));
- }
- static bool result_eq(struct intel_engine_cs *engine,
- u32 a, u32 b, i915_reg_t reg)
- {
- if (a != b && !pardon_reg(engine->i915, reg)) {
- pr_err("Whitelisted register 0x%4x not context saved: A=%08x, B=%08x\n",
- i915_mmio_reg_offset(reg), a, b);
- return false;
- }
- return true;
- }
- static bool writeonly_reg(struct drm_i915_private *i915, i915_reg_t reg)
- {
- /* Some registers do not seem to behave and our writes unreadable */
- static const struct regmask wo[] = {
- { GEN9_SLICE_COMMON_ECO_CHICKEN1, 9 },
- };
- return find_reg(i915, reg, wo, ARRAY_SIZE(wo));
- }
- static bool result_neq(struct intel_engine_cs *engine,
- u32 a, u32 b, i915_reg_t reg)
- {
- if (a == b && !writeonly_reg(engine->i915, reg)) {
- pr_err("Whitelist register 0x%4x:%08x was unwritable\n",
- i915_mmio_reg_offset(reg), a);
- return false;
- }
- return true;
- }
- static int
- check_whitelisted_registers(struct intel_engine_cs *engine,
- struct i915_vma *A,
- struct i915_vma *B,
- bool (*fn)(struct intel_engine_cs *engine,
- u32 a, u32 b,
- i915_reg_t reg))
- {
- u32 *a, *b;
- int i, err;
- a = i915_gem_object_pin_map_unlocked(A->obj, I915_MAP_WB);
- if (IS_ERR(a))
- return PTR_ERR(a);
- b = i915_gem_object_pin_map_unlocked(B->obj, I915_MAP_WB);
- if (IS_ERR(b)) {
- err = PTR_ERR(b);
- goto err_a;
- }
- err = 0;
- for (i = 0; i < engine->whitelist.count; i++) {
- const struct i915_wa *wa = &engine->whitelist.list[i];
- if (i915_mmio_reg_offset(wa->reg) &
- RING_FORCE_TO_NONPRIV_ACCESS_RD)
- continue;
- if (!fn(engine, a[i], b[i], wa->reg))
- err = -EINVAL;
- }
- i915_gem_object_unpin_map(B->obj);
- err_a:
- i915_gem_object_unpin_map(A->obj);
- return err;
- }
- static int live_isolated_whitelist(void *arg)
- {
- struct intel_gt *gt = arg;
- struct {
- struct i915_vma *scratch[2];
- } client[2] = {};
- struct intel_engine_cs *engine;
- enum intel_engine_id id;
- int i, err = 0;
- /*
- * Check that a write into a whitelist register works, but
- * invisible to a second context.
- */
- if (!intel_engines_has_context_isolation(gt->i915))
- return 0;
- for (i = 0; i < ARRAY_SIZE(client); i++) {
- client[i].scratch[0] =
- __vm_create_scratch_for_read_pinned(gt->vm, 4096);
- if (IS_ERR(client[i].scratch[0])) {
- err = PTR_ERR(client[i].scratch[0]);
- goto err;
- }
- client[i].scratch[1] =
- __vm_create_scratch_for_read_pinned(gt->vm, 4096);
- if (IS_ERR(client[i].scratch[1])) {
- err = PTR_ERR(client[i].scratch[1]);
- i915_vma_unpin_and_release(&client[i].scratch[0], 0);
- goto err;
- }
- }
- for_each_engine(engine, gt, id) {
- struct intel_context *ce[2];
- if (!engine->kernel_context->vm)
- continue;
- if (!whitelist_writable_count(engine))
- continue;
- ce[0] = intel_context_create(engine);
- if (IS_ERR(ce[0])) {
- err = PTR_ERR(ce[0]);
- break;
- }
- ce[1] = intel_context_create(engine);
- if (IS_ERR(ce[1])) {
- err = PTR_ERR(ce[1]);
- intel_context_put(ce[0]);
- break;
- }
- /* Read default values */
- err = read_whitelisted_registers(ce[0], client[0].scratch[0]);
- if (err)
- goto err_ce;
- /* Try to overwrite registers (should only affect ctx0) */
- err = scrub_whitelisted_registers(ce[0]);
- if (err)
- goto err_ce;
- /* Read values from ctx1, we expect these to be defaults */
- err = read_whitelisted_registers(ce[1], client[1].scratch[0]);
- if (err)
- goto err_ce;
- /* Verify that both reads return the same default values */
- err = check_whitelisted_registers(engine,
- client[0].scratch[0],
- client[1].scratch[0],
- result_eq);
- if (err)
- goto err_ce;
- /* Read back the updated values in ctx0 */
- err = read_whitelisted_registers(ce[0], client[0].scratch[1]);
- if (err)
- goto err_ce;
- /* User should be granted privilege to overwhite regs */
- err = check_whitelisted_registers(engine,
- client[0].scratch[0],
- client[0].scratch[1],
- result_neq);
- err_ce:
- intel_context_put(ce[1]);
- intel_context_put(ce[0]);
- if (err)
- break;
- }
- err:
- for (i = 0; i < ARRAY_SIZE(client); i++) {
- i915_vma_unpin_and_release(&client[i].scratch[1], 0);
- i915_vma_unpin_and_release(&client[i].scratch[0], 0);
- }
- if (igt_flush_test(gt->i915))
- err = -EIO;
- return err;
- }
- static bool
- verify_wa_lists(struct intel_gt *gt, struct wa_lists *lists,
- const char *str)
- {
- struct intel_engine_cs *engine;
- enum intel_engine_id id;
- bool ok = true;
- ok &= wa_list_verify(gt, &lists->gt_wa_list, str);
- for_each_engine(engine, gt, id) {
- struct intel_context *ce;
- ce = intel_context_create(engine);
- if (IS_ERR(ce))
- return false;
- ok &= engine_wa_list_verify(ce,
- &lists->engine[id].wa_list,
- str) == 0;
- ok &= engine_wa_list_verify(ce,
- &lists->engine[id].ctx_wa_list,
- str) == 0;
- intel_context_put(ce);
- }
- return ok;
- }
- static int
- live_gpu_reset_workarounds(void *arg)
- {
- struct intel_gt *gt = arg;
- intel_wakeref_t wakeref;
- struct wa_lists *lists;
- bool ok;
- if (!intel_has_gpu_reset(gt))
- return 0;
- lists = kzalloc_obj(*lists);
- if (!lists)
- return -ENOMEM;
- pr_info("Verifying after GPU reset...\n");
- igt_global_reset_lock(gt);
- wakeref = intel_runtime_pm_get(gt->uncore->rpm);
- reference_lists_init(gt, lists);
- ok = verify_wa_lists(gt, lists, "before reset");
- if (!ok)
- goto out;
- intel_gt_reset(gt, ALL_ENGINES, "live_workarounds");
- ok = verify_wa_lists(gt, lists, "after reset");
- out:
- reference_lists_fini(gt, lists);
- intel_runtime_pm_put(gt->uncore->rpm, wakeref);
- igt_global_reset_unlock(gt);
- kfree(lists);
- return ok ? 0 : -ESRCH;
- }
- static int
- live_engine_reset_workarounds(void *arg)
- {
- struct intel_gt *gt = arg;
- struct intel_engine_cs *engine;
- enum intel_engine_id id;
- struct intel_context *ce;
- struct igt_spinner spin;
- struct i915_request *rq;
- intel_wakeref_t wakeref;
- struct wa_lists *lists;
- int ret = 0;
- if (!intel_has_reset_engine(gt))
- return 0;
- lists = kzalloc_obj(*lists);
- if (!lists)
- return -ENOMEM;
- igt_global_reset_lock(gt);
- wakeref = intel_runtime_pm_get(gt->uncore->rpm);
- reference_lists_init(gt, lists);
- for_each_engine(engine, gt, id) {
- struct intel_selftest_saved_policy saved;
- bool using_guc = intel_engine_uses_guc(engine);
- bool ok;
- int ret2;
- pr_info("Verifying after %s reset...\n", engine->name);
- ret = intel_selftest_modify_policy(engine, &saved,
- SELFTEST_SCHEDULER_MODIFY_FAST_RESET);
- if (ret)
- break;
- ce = intel_context_create(engine);
- if (IS_ERR(ce)) {
- ret = PTR_ERR(ce);
- goto restore;
- }
- if (!using_guc) {
- ok = verify_wa_lists(gt, lists, "before reset");
- if (!ok) {
- ret = -ESRCH;
- goto err;
- }
- ret = intel_engine_reset(engine, "live_workarounds:idle");
- if (ret) {
- pr_err("%s: Reset failed while idle\n", engine->name);
- goto err;
- }
- ok = verify_wa_lists(gt, lists, "after idle reset");
- if (!ok) {
- ret = -ESRCH;
- goto err;
- }
- }
- ret = igt_spinner_init(&spin, engine->gt);
- if (ret)
- goto err;
- rq = igt_spinner_create_request(&spin, ce, MI_NOOP);
- if (IS_ERR(rq)) {
- ret = PTR_ERR(rq);
- igt_spinner_fini(&spin);
- goto err;
- }
- ret = request_add_spin(rq, &spin);
- if (ret) {
- pr_err("%s: Spinner failed to start\n", engine->name);
- igt_spinner_fini(&spin);
- goto err;
- }
- /* Ensure the spinner hasn't aborted */
- if (i915_request_completed(rq)) {
- ret = -ETIMEDOUT;
- goto skip;
- }
- if (!using_guc) {
- ret = intel_engine_reset(engine, "live_workarounds:active");
- if (ret) {
- pr_err("%s: Reset failed on an active spinner\n",
- engine->name);
- igt_spinner_fini(&spin);
- goto err;
- }
- }
- /* Ensure the reset happens and kills the engine */
- if (ret == 0)
- ret = intel_selftest_wait_for_rq(rq);
- skip:
- igt_spinner_end(&spin);
- igt_spinner_fini(&spin);
- ok = verify_wa_lists(gt, lists, "after busy reset");
- if (!ok)
- ret = -ESRCH;
- err:
- intel_context_put(ce);
- restore:
- ret2 = intel_selftest_restore_policy(engine, &saved);
- if (ret == 0)
- ret = ret2;
- if (ret)
- break;
- }
- reference_lists_fini(gt, lists);
- intel_runtime_pm_put(gt->uncore->rpm, wakeref);
- igt_global_reset_unlock(gt);
- kfree(lists);
- igt_flush_test(gt->i915);
- return ret;
- }
- int intel_workarounds_live_selftests(struct drm_i915_private *i915)
- {
- static const struct i915_subtest tests[] = {
- SUBTEST(live_dirty_whitelist),
- SUBTEST(live_reset_whitelist),
- SUBTEST(live_isolated_whitelist),
- SUBTEST(live_gpu_reset_workarounds),
- SUBTEST(live_engine_reset_workarounds),
- };
- if (intel_gt_is_wedged(to_gt(i915)))
- return 0;
- return intel_gt_live_subtests(tests, to_gt(i915));
- }
|