Re: [PATCH 02/13] drm/i915: Implement command buffer parsing logic

Daniel Vetter
2014-03-06T21:07:42Z

On Thu, Mar 06, 2014 at 03:10:50PM +0200, Jani Nikula wrote:
> On Tue, 18 Feb 2014, bradley.d.volkin@intel.com wrote:
> > From: Brad Volkin <bradley.d.volkin@intel.com>
> >
> > The command parser scans batch buffers submitted via execbuffer ioctls before
> > the driver submits them to hardware. At a high level, it looks for several
> > things:
> >
> > 1) Commands which are explicitly defined as privileged or which should only be
> > used by the kernel driver. The parser generally rejects such commands, with
> > the provision that it may allow some from the drm master process.
> > 2) Commands which access registers. To support correct/enhanced userspace
> > functionality, particularly certain OpenGL extensions, the parser provides a
> > whitelist of registers which userspace may safely access (for both normal and
> > drm master processes).
> > 3) Commands which access privileged memory (i.e. GGTT, HWS page, etc). The
> > parser always rejects such commands.
> >
> > See the overview comment in the source for more details.
> >
> > This patch only implements the logic. Subsequent patches will build the tables
> > that drive the parser.
> >
> > v2: Don't set the secure bit if the parser succeeds
> > Fail harder during init
> > Makefile cleanup
> > Kerneldoc cleanup
> > Clarify module param description
> > Convert ints to bools in a few places
> > Move client/subclient defs to i915_reg.h
> > Remove the bits_count field
> >
> > OTC-Tracker: AXIA-4631
> > Change-Id: I50b98c71c6655893291c78a2d1b8954577b37a30
> > Signed-off-by: Brad Volkin <bradley.d.volkin@intel.com>
> > ---
> > drivers/gpu/drm/i915/Makefile | 1 +
> > drivers/gpu/drm/i915/i915_cmd_parser.c | 485 +++++++++++++++++++++++++++++
> > drivers/gpu/drm/i915/i915_drv.h | 93 ++++++
> > drivers/gpu/drm/i915/i915_gem_execbuffer.c | 18 ++
> > drivers/gpu/drm/i915/i915_params.c | 5 +
> > drivers/gpu/drm/i915/i915_reg.h | 12 +
> > drivers/gpu/drm/i915/intel_ringbuffer.c | 2 +
> > drivers/gpu/drm/i915/intel_ringbuffer.h | 32 ++
> > 8 files changed, 648 insertions(+)
> > create mode 100644 drivers/gpu/drm/i915/i915_cmd_parser.c
> >
> > diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
> > index 4850494..3569122 100644
> > --- a/drivers/gpu/drm/i915/Makefile
> > +++ b/drivers/gpu/drm/i915/Makefile
> > @@ -14,6 +14,7 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o \
> > i915_gem_gtt.o \
> > i915_gem_stolen.o \
> > i915_gem_tiling.o \
> > + i915_cmd_parser.o \
> > i915_params.o \
> > i915_sysfs.o \
> > i915_trace_points.o \
> > diff --git a/drivers/gpu/drm/i915/i915_cmd_parser.c b/drivers/gpu/drm/i915/i915_cmd_parser.c
> > new file mode 100644
> > index 0000000..7a5756e
> > --- /dev/null
> > +++ b/drivers/gpu/drm/i915/i915_cmd_parser.c
> > @@ -0,0 +1,485 @@
> > +/*
> > + * Copyright © 2013 Intel Corporation
> > + *
> > + * Permission is hereby granted, free of charge, to any person obtaining a
> > + * copy of this software and associated documentation files (the "Software"),
> > + * to deal in the Software without restriction, including without limitation
> > + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> > + * and/or sell copies of the Software, and to permit persons to whom the
> > + * Software is furnished to do so, subject to the following conditions:
> > + *
> > + * The above copyright notice and this permission notice (including the next
> > + * paragraph) shall be included in all copies or substantial portions of the
> > + * Software.
> > + *
> > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> > + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> > + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
> > + * IN THE SOFTWARE.
> > + *
> > + * Authors:
> > + * Brad Volkin <bradley.d.volkin@intel.com>
> > + *
> > + */
> > +
> > +#include "i915_drv.h"
> > +
> > +/**
> > + * DOC: i915 batch buffer command parser
> > + *
> > + * Motivation:
> > + * Certain OpenGL features (e.g. transform feedback, performance monitoring)
> > + * require userspace code to submit batches containing commands such as
> > + * MI_LOAD_REGISTER_IMM to access various registers. Unfortunately, some
> > + * generations of the hardware will noop these commands in "unsecure" batches
> > + * (which includes all userspace batches submitted via i915) even though the
> > + * commands may be safe and represent the intended programming model of the
> > + * device.
> > + *
> > + * The software command parser is similar in operation to the command parsing
> > + * done in hardware for unsecure batches. However, the software parser allows
> > + * some operations that would be noop'd by hardware, if the parser determines
> > + * the operation is safe, and submits the batch as "secure" to prevent hardware
> > + * parsing.
> > + *
> > + * Threats:
> > + * At a high level, the hardware (and software) checks attempt to prevent
> > + * granting userspace undue privileges. There are three categories of privilege.
> > + *
> > + * First, commands which are explicitly defined as privileged or which should
> > + * only be used by the kernel driver. The parser generally rejects such
> > + * commands, though it may allow some from the drm master process.
> > + *
> > + * Second, commands which access registers. To support correct/enhanced
> > + * userspace functionality, particularly certain OpenGL extensions, the parser
> > + * provides a whitelist of registers which userspace may safely access (for both
> > + * normal and drm master processes).
> > + *
> > + * Third, commands which access privileged memory (i.e. GGTT, HWS page, etc).
> > + * The parser always rejects such commands.
> > + *
> > + * The majority of the problematic commands fall in the MI_* range, with only a
> > + * few specific commands on each ring (e.g. PIPE_CONTROL and MI_FLUSH_DW).
> > + *
> > + * Implementation:
> > + * Each ring maintains tables of commands and registers which the parser uses in
> > + * scanning batch buffers submitted to that ring.
> > + *
> > + * Since the set of commands that the parser must check for is significantly
> > + * smaller than the number of commands supported, the parser tables contain only
> > + * those commands required by the parser. This generally works because command
> > + * opcode ranges have standard command length encodings. So for commands that
> > + * the parser does not need to check, it can easily skip them. This is
> > + * implementated via a per-ring length decoding vfunc.
> > + *
> > + * Unfortunately, there are a number of commands that do not follow the standard
> > + * length encoding for their opcode range, primarily amongst the MI_* commands.
> > + * To handle this, the parser provides a way to define explicit "skip" entries
> > + * in the per-ring command tables.
> > + *
> > + * Other command table entries map fairly directly to high level categories
> > + * mentioned above: rejected, master-only, register whitelist. The parser
> > + * implements a number of checks, including the privileged memory checks, via a
> > + * general bitmasking mechanism.
> > + */
> > +
> > +static u32 gen7_render_get_cmd_length_mask(u32 cmd_header)
> > +{
> > + u32 client = (cmd_header & INSTR_CLIENT_MASK) >> INSTR_CLIENT_SHIFT;
> > + u32 subclient =
> > + (cmd_header & INSTR_SUBCLIENT_MASK) >> INSTR_SUBCLIENT_SHIFT;
> > +
> > + if (client == INSTR_MI_CLIENT)
> > + return 0x3F;
> > + else if (client == INSTR_RC_CLIENT) {
> > + if (subclient == INSTR_MEDIA_SUBCLIENT)
> > + return 0xFFFF;
> > + else
> > + return 0xFF;
> > + }
> > +
> > + DRM_DEBUG_DRIVER("CMD: Abnormal rcs cmd length! 0x%08X\n", cmd_header);
> > + return 0;
> > +}
> > +
> > +static u32 gen7_bsd_get_cmd_length_mask(u32 cmd_header)
> > +{
> > + u32 client = (cmd_header & INSTR_CLIENT_MASK) >> INSTR_CLIENT_SHIFT;
> > + u32 subclient =
> > + (cmd_header & INSTR_SUBCLIENT_MASK) >> INSTR_SUBCLIENT_SHIFT;
> > +
> > + if (client == INSTR_MI_CLIENT)
> > + return 0x3F;
> > + else if (client == INSTR_RC_CLIENT) {
> > + if (subclient == INSTR_MEDIA_SUBCLIENT)
> > + return 0xFFF;
> > + else
> > + return 0xFF;
> > + }
> > +
> > + DRM_DEBUG_DRIVER("CMD: Abnormal bsd cmd length! 0x%08X\n", cmd_header);
> > + return 0;
> > +}
> > +
> > +static u32 gen7_blt_get_cmd_length_mask(u32 cmd_header)
> > +{
> > + u32 client = (cmd_header & INSTR_CLIENT_MASK) >> INSTR_CLIENT_SHIFT;
> > +
> > + if (client == INSTR_MI_CLIENT)
> > + return 0x3F;
> > + else if (client == INSTR_BC_CLIENT)
> > + return 0xFF;
> > +
> > + DRM_DEBUG_DRIVER("CMD: Abnormal blt cmd length! 0x%08X\n", cmd_header);
> > + return 0;
> > +}
> > +
> > +static void validate_cmds_sorted(struct intel_ring_buffer *ring)
> > +{
> > + int i;
> > +
> > + if (!ring->cmd_tables || ring->cmd_table_count == 0)
> > + return;
> > +
> > + for (i = 0; i < ring->cmd_table_count; i++) {
> > + const struct drm_i915_cmd_table *table = &ring->cmd_tables[i];
> > + u32 previous = 0;
> > + int j;
> > +
> > + for (j = 0; j < table->count; j++) {
> > + const struct drm_i915_cmd_descriptor *desc =
> > + &table->table[i];
> > + u32 curr = desc->cmd.value & desc->cmd.mask;
> > +
> > + if (curr < previous)
> > + DRM_ERROR("CMD: table not sorted ring=%d table=%d entry=%d cmd=0x%08X prev=0x%08X\n",
> > + ring->id, i, j, curr, previous);
> > +
> > + previous = curr;
> > + }
> > + }
> > +}
> > +
> > +static void check_sorted(int ring_id, const u32 *reg_table, int reg_count)
> > +{
> > + int i;
> > + u32 previous = 0;
> > +
> > + for (i = 0; i < reg_count; i++) {
> > + u32 curr = reg_table[i];
> > +
> > + if (curr < previous)
> > + DRM_ERROR("CMD: table not sorted ring=%d entry=%d reg=0x%08X prev=0x%08X\n",
> > + ring_id, i, curr, previous);
> > +
> > + previous = curr;
> > + }
> > +}
> > +
> > +static void validate_regs_sorted(struct intel_ring_buffer *ring)
> > +{
> > + check_sorted(ring->id, ring->reg_table, ring->reg_count);
> > + check_sorted(ring->id, ring->master_reg_table, ring->master_reg_count);
> > +}
> > +
> > +/**
> > + * i915_cmd_parser_init_ring() - set cmd parser related fields for a ringbuffer
> > + * @ring: the ringbuffer to initialize
> > + *
> > + * Optionally initializes fields related to batch buffer command parsing in the
> > + * struct intel_ring_buffer based on whether the platform requires software
> > + * command parsing.
> > + */
> > +void i915_cmd_parser_init_ring(struct intel_ring_buffer *ring)
> > +{
> > + if (!IS_GEN7(ring->dev))
> > + return;
> > +
> > + switch (ring->id) {
> > + case RCS:
> > + ring->get_cmd_length_mask = gen7_render_get_cmd_length_mask;
> > + break;
> > + case VCS:
> > + ring->get_cmd_length_mask = gen7_bsd_get_cmd_length_mask;
> > + break;
> > + case BCS:
> > + ring->get_cmd_length_mask = gen7_blt_get_cmd_length_mask;
> > + break;
> > + case VECS:
> > + /* VECS can use the same length_mask function as VCS */
> > + ring->get_cmd_length_mask = gen7_bsd_get_cmd_length_mask;
> > + break;
> > + default:
> > + DRM_ERROR("CMD: cmd_parser_init with unknown ring: %d\n",
> > + ring->id);
> > + BUG();
> > + }
> > +
> > + validate_cmds_sorted(ring);
> > + validate_regs_sorted(ring);
>
> So if you come to rely on the tables being sorted later on, I'd like the
> above functions to return whether everything was okay or not, and BUG()
> here if not. This can be a follow-up, and *must* be added before doing
> anything that really requires the tables to be sorted.
>
> Reviewed-by: Jani Nikula <jani.nikula@intel.com>

Merged the first 2 patches from this series, thanks.
-Daniel
--
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch