[Intel-gfx] [PATCH] drm/i915/selftests: Verify whitelist of context registers

Chris Wilson chris at chris-wilson.co.uk
Wed Apr 24 10:00:03 UTC 2019


Quoting Tvrtko Ursulin (2019-04-16 15:50:27)
> 
> On 16/04/2019 10:12, Chris Wilson wrote:
> > The RING_NONPRIV allows us to add registers to a whitelist that allows
> > userspace to modify them. Ideally such registers should be safe and
> > saved within the context such that they do not impact system behaviour
> > for other users. This selftest verifies that those registers we do add
> > are (a) then writable by userspace and (b) only affect a single client.
> > 
> > Opens:
> > - Is GEN9_SLICE_COMMON_ECO_CHICKEN1 really write-only?
> > 
> > Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
> > Cc: Tvrtko Ursulin <tvrtko.ursulin at intel.com>
> > ---
> > Compile fix for writeonly_reg()
> > ---
> >   .../drm/i915/selftests/intel_workarounds.c    | 320 ++++++++++++++++++
> >   1 file changed, 320 insertions(+)
> > 
> > diff --git a/drivers/gpu/drm/i915/selftests/intel_workarounds.c b/drivers/gpu/drm/i915/selftests/intel_workarounds.c
> > index a363748a7a4f..f40e0937ec96 100644
> > --- a/drivers/gpu/drm/i915/selftests/intel_workarounds.c
> > +++ b/drivers/gpu/drm/i915/selftests/intel_workarounds.c
> > @@ -700,6 +700,325 @@ static int live_reset_whitelist(void *arg)
> >       return err;
> >   }
> >   
> > +static int read_whitelisted_registers(struct i915_gem_context *ctx,
> > +                                   struct intel_engine_cs *engine,
> > +                                   struct i915_vma *results)
> > +{
> > +     intel_wakeref_t wakeref;
> > +     struct i915_request *rq;
> > +     u32 srm, *cs;
> > +     int err, i;
> > +
> > +     rq = ERR_PTR(-ENODEV);
> > +     with_intel_runtime_pm(engine->i915, wakeref)
> > +             rq = i915_request_alloc(engine, ctx);
> > +     if (IS_ERR(rq))
> > +             return PTR_ERR(rq);
> > +
> > +     err = i915_vma_move_to_active(results, rq, EXEC_OBJECT_WRITE);
> > +     if (err)
> > +             goto err_req;
> 
> Why do you track the vma? There's a wait below and in scrub you even 
> flush manually.

copy-and-paste.

> > +     srm = MI_STORE_REGISTER_MEM;
> > +     if (INTEL_GEN(ctx->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 = results->node.start + sizeof(u32) * i;
> > +
> > +             *cs++ = srm;
> > +             *cs++ = i915_mmio_reg_offset(engine->whitelist.list[i].reg);
> > +             *cs++ = lower_32_bits(offset);
> > +             *cs++ = upper_32_bits(offset);
> > +     }
> > +     intel_ring_advance(rq, cs);
> > +
> > +err_req:
> > +     i915_request_add(rq);
> > +
> > +     if (i915_request_wait(rq, I915_WAIT_LOCKED, HZ / 5) < 0)
> > +             err = -EIO;
> > +
> > +     return err;
> > +}
> > +
> > +static int scrub_whitelisted_registers(struct i915_gem_context *ctx,
> > +                                    struct intel_engine_cs *engine)
> > +{
> > +     intel_wakeref_t wakeref;
> > +     struct i915_request *rq;
> > +     struct i915_vma *batch;
> > +     int i, err;
> > +     u32 *cs;
> > +
> > +     batch = create_batch(ctx);
> > +     if (IS_ERR(batch))
> > +             return PTR_ERR(batch);
> > +
> > +     cs = i915_gem_object_pin_map(batch->obj, I915_MAP_WC);
> > +     if (IS_ERR(cs)) {
> > +             err = PTR_ERR(cs);
> > +             goto err_batch;
> > +     }
> > +
> > +     *cs++ = MI_LOAD_REGISTER_IMM(engine->whitelist.count);
> > +     for (i = 0; i < engine->whitelist.count; i++) {
> > +             *cs++ = i915_mmio_reg_offset(engine->whitelist.list[i].reg);
> > +             *cs++ = 0xffffffff;
> > +     }
> > +     *cs++ = MI_BATCH_BUFFER_END;
> > +
> > +     i915_gem_object_flush_map(batch->obj);
> > +     i915_gem_chipset_flush(ctx->i915);
> > +
> > +     rq = ERR_PTR(-ENODEV);
> > +     with_intel_runtime_pm(engine->i915, wakeref)
> > +             rq = i915_request_alloc(engine, ctx);
> > +     if (IS_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 = engine->emit_bb_start(rq, batch->node.start, 0, 0);
> 
> Why is batch needed here when everything else is happy to run from the ring?

Because we are playing with HW registers, I want reset to work safely.
So long as we have init_breadcrumb, the payload scrubbing should work.
Ok.

> > +static bool writeonly_reg(struct drm_i915_private *i915, i915_reg_t reg)
> > +{
> > +     static const struct {
> > +             i915_reg_t reg;
> > +             unsigned long gen_mask;
> > +     } wo[] = {
> > +             { GEN9_SLICE_COMMON_ECO_CHICKEN1, INTEL_GEN_MASK(9, 9) },
> > +     };
> 
> What is the difference between pardoned and whitelisted?

Pardoned is for non-context registers that are already exposed as
RING_NONPRIV ABI. Mistakes already made.

writeonly is for a register that doesn't seem to stick but everyone
believes is a crucial w/a for glk.

> > +     u32 offset = i915_mmio_reg_offset(reg);
> > +     int i;
> > +
> > +     for (i = 0; i < ARRAY_SIZE(wo); i++) {
> > +             if (INTEL_INFO(i915)->gen_mask & wo[i].gen_mask &&
> > +                 i915_mmio_reg_offset(wo[i].reg) == offset)
> > +                     return true;
> > +     }
> > +
> > +     return false;
> 
> You could consolidate into one find_reg which takes the array.

Next up, you'll be saying we could bsearch by addr! :)

> > +static int neq_whitelisted_registers(struct i915_vma *A,
> > +                                  struct i915_vma *B,
> > +                                  struct intel_engine_cs *engine)
> > +{
> > +     u32 *a, *b;
> > +     int i, err;
> > +
> > +     a = i915_gem_object_pin_map(A->obj, I915_MAP_WB);
> > +     if (IS_ERR(a))
> > +             return PTR_ERR(a);
> > +
> > +     b = i915_gem_object_pin_map(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++) {
> > +             if (a[i] == b[i] &&
> > +                 !writeonly_reg(engine->i915, engine->whitelist.list[i].reg)) {
> > +                     pr_err("[%d] Whitelist register 0x%4x:%08x was unwritable\n",
> > +                            i, i915_mmio_reg_offset(engine->whitelist.list[i].reg), a[i]);
> > +                     err = -EINVAL;
> > +             }
> > +     }
> > +
> > +     i915_gem_object_unpin_map(B->obj);
> > +err_a:
> > +     i915_gem_object_unpin_map(A->obj);
> > +     return err;
> > +}
> 
> eq and neq could also be cheaply consolidated into one taking table and 
> error message.

And op. The preamble is the same, the loop innards are not, but we can
just make a callback.
-Chris


More information about the Intel-gfx mailing list