untrusted comment: verify with openbsd-66-base.pub
RWSvK/c+cFe24AwHGsot6zZZCHIO+QLeZX4ZBpO7LUH9YYXYz1xdklMNqZ9gpKzNLtmu86rDGqY3U8tWyuZx+fk3EsBB3PYnWwk=

OpenBSD 6.6 errata 007, November 22, 2019:

A local user could cause the system to hang by reading specific
registers when Intel Gen8/Gen9 graphics hardware is in a low power state.

A local user could perform writes to memory that should be blocked with
Intel Gen9 graphics hardware.

Apply by doing:
   signify -Vep /etc/signify/openbsd-66-base.pub -x 007_inteldrm.patch.sig \
       -m - | (cd /usr/src && patch -p0)

And then rebuild and install a new kernel:
   KK=`sysctl -n kern.osversion | cut -d# -f1`
   cd /usr/src/sys/arch/`machine`/compile/$KK
   make obj
   make config
   make
   make install

Index: sys/dev/pci/drm/i915/i915_cmd_parser.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/drm/i915/i915_cmd_parser.c,v
retrieving revision 1.2
diff -u -p -r1.2 i915_cmd_parser.c
--- sys/dev/pci/drm/i915/i915_cmd_parser.c      14 Apr 2019 10:14:51 -0000      1.2
+++ sys/dev/pci/drm/i915/i915_cmd_parser.c      20 Nov 2019 07:02:22 -0000
@@ -51,13 +51,11 @@
 * 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.
+ * only be used by the kernel driver. The parser rejects such commands
 *
 * 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).
+ * provides a whitelist of registers which userspace may safely access
 *
 * Third, commands which access privileged memory (i.e. GGTT, HWS page, etc).
 * The parser always rejects such commands.
@@ -82,9 +80,9 @@
 * in the per-engine 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.
+ * mentioned above: rejected, register whitelist. The parser implements a number
+ * of checks, including the privileged memory checks, via a general bitmasking
+ * mechanism.
 */

/*
@@ -102,8 +100,6 @@ struct drm_i915_cmd_descriptor {
        * CMD_DESC_REJECT: The command is never allowed
        * CMD_DESC_REGISTER: The command should be checked against the
        *                    register whitelist for the appropriate ring
-        * CMD_DESC_MASTER: The command is allowed if the submitting process
-        *                  is the DRM master
        */
       u32 flags;
#define CMD_DESC_FIXED    (1<<0)
@@ -111,7 +107,6 @@ struct drm_i915_cmd_descriptor {
#define CMD_DESC_REJECT   (1<<2)
#define CMD_DESC_REGISTER (1<<3)
#define CMD_DESC_BITMASK  (1<<4)
-#define CMD_DESC_MASTER   (1<<5)

       /*
        * The command's unique identification bits and the bitmask to get them.
@@ -192,7 +187,7 @@ struct drm_i915_cmd_table {
#define CMD(op, opm, f, lm, fl, ...)                           \
       {                                                       \
               .flags = (fl) | ((f) ? CMD_DESC_FIXED : 0),     \
-               .cmd = { (op), ~0u << (opm) },                  \
+               .cmd = { (op & ~0u << (opm)), ~0u << (opm) },   \
               .length = { (lm) },                             \
               __VA_ARGS__                                     \
       }
@@ -207,14 +202,13 @@ struct drm_i915_cmd_table {
#define R CMD_DESC_REJECT
#define W CMD_DESC_REGISTER
#define B CMD_DESC_BITMASK
-#define M CMD_DESC_MASTER

/*            Command                          Mask   Fixed Len   Action
             ---------------------------------------------------------- */
-static const struct drm_i915_cmd_descriptor common_cmds[] = {
+static const struct drm_i915_cmd_descriptor gen7_common_cmds[] = {
       CMD(  MI_NOOP,                          SMI,    F,  1,      S  ),
       CMD(  MI_USER_INTERRUPT,                SMI,    F,  1,      R  ),
-       CMD(  MI_WAIT_FOR_EVENT,                SMI,    F,  1,      M  ),
+       CMD(  MI_WAIT_FOR_EVENT,                SMI,    F,  1,      R  ),
       CMD(  MI_ARB_CHECK,                     SMI,    F,  1,      S  ),
       CMD(  MI_REPORT_HEAD,                   SMI,    F,  1,      S  ),
       CMD(  MI_SUSPEND_FLUSH,                 SMI,    F,  1,      S  ),
@@ -244,7 +238,7 @@ static const struct drm_i915_cmd_descrip
       CMD(  MI_BATCH_BUFFER_START,            SMI,   !F,  0xFF,   S  ),
};

-static const struct drm_i915_cmd_descriptor render_cmds[] = {
+static const struct drm_i915_cmd_descriptor gen7_render_cmds[] = {
       CMD(  MI_FLUSH,                         SMI,    F,  1,      S  ),
       CMD(  MI_ARB_ON_OFF,                    SMI,    F,  1,      R  ),
       CMD(  MI_PREDICATE,                     SMI,    F,  1,      S  ),
@@ -311,7 +305,7 @@ static const struct drm_i915_cmd_descrip
       CMD(  MI_URB_ATOMIC_ALLOC,              SMI,    F,  1,      S  ),
       CMD(  MI_SET_APPID,                     SMI,    F,  1,      S  ),
       CMD(  MI_RS_CONTEXT,                    SMI,    F,  1,      S  ),
-       CMD(  MI_LOAD_SCAN_LINES_INCL,          SMI,   !F,  0x3F,   M  ),
+       CMD(  MI_LOAD_SCAN_LINES_INCL,          SMI,   !F,  0x3F,   R  ),
       CMD(  MI_LOAD_SCAN_LINES_EXCL,          SMI,   !F,  0x3F,   R  ),
       CMD(  MI_LOAD_REGISTER_REG,             SMI,   !F,  0xFF,   W,
             .reg = { .offset = 1, .mask = 0x007FFFFC, .step = 1 }    ),
@@ -328,7 +322,7 @@ static const struct drm_i915_cmd_descrip
       CMD(  GFX_OP_3DSTATE_BINDING_TABLE_EDIT_PS,  S3D,   !F,  0x1FF,  S  ),
};

-static const struct drm_i915_cmd_descriptor video_cmds[] = {
+static const struct drm_i915_cmd_descriptor gen7_video_cmds[] = {
       CMD(  MI_ARB_ON_OFF,                    SMI,    F,  1,      R  ),
       CMD(  MI_SET_APPID,                     SMI,    F,  1,      S  ),
       CMD(  MI_STORE_DWORD_IMM,               SMI,   !F,  0xFF,   B,
@@ -372,7 +366,7 @@ static const struct drm_i915_cmd_descrip
       CMD(  MFX_WAIT,                         SMFX,   F,  1,      S  ),
};

-static const struct drm_i915_cmd_descriptor vecs_cmds[] = {
+static const struct drm_i915_cmd_descriptor gen7_vecs_cmds[] = {
       CMD(  MI_ARB_ON_OFF,                    SMI,    F,  1,      R  ),
       CMD(  MI_SET_APPID,                     SMI,    F,  1,      S  ),
       CMD(  MI_STORE_DWORD_IMM,               SMI,   !F,  0xFF,   B,
@@ -410,7 +404,7 @@ static const struct drm_i915_cmd_descrip
             }},                                                      ),
};

-static const struct drm_i915_cmd_descriptor blt_cmds[] = {
+static const struct drm_i915_cmd_descriptor gen7_blt_cmds[] = {
       CMD(  MI_DISPLAY_FLIP,                  SMI,   !F,  0xFF,   R  ),
       CMD(  MI_STORE_DWORD_IMM,               SMI,   !F,  0x3FF,  B,
             .bits = {{
@@ -444,10 +438,64 @@ static const struct drm_i915_cmd_descrip
};

static const struct drm_i915_cmd_descriptor hsw_blt_cmds[] = {
-       CMD(  MI_LOAD_SCAN_LINES_INCL,          SMI,   !F,  0x3F,   M  ),
+       CMD(  MI_LOAD_SCAN_LINES_INCL,          SMI,   !F,  0x3F,   R  ),
       CMD(  MI_LOAD_SCAN_LINES_EXCL,          SMI,   !F,  0x3F,   R  ),
};

+/*
+ * For Gen9 we can still rely on the h/w to enforce cmd security, and only
+ * need to re-enforce the register access checks. We therefore only need to
+ * teach the cmdparser how to find the end of each command, and identify
+ * register accesses. The table doesn't need to reject any commands, and so
+ * the only commands listed here are:
+ *   1) Those that touch registers
+ *   2) Those that do not have the default 8-bit length
+ *
+ * Note that the default MI length mask chosen for this table is 0xFF, not
+ * the 0x3F used on older devices. This is because the vast majority of MI
+ * cmds on Gen9 use a standard 8-bit Length field.
+ * All the Gen9 blitter instructions are standard 0xFF length mask, and
+ * none allow access to non-general registers, so in fact no BLT cmds are
+ * included in the table at all.
+ *
+ */
+static const struct drm_i915_cmd_descriptor gen9_blt_cmds[] = {
+       CMD(  MI_NOOP,                          SMI,    F,  1,      S  ),
+       CMD(  MI_USER_INTERRUPT,                SMI,    F,  1,      S  ),
+       CMD(  MI_WAIT_FOR_EVENT,                SMI,    F,  1,      S  ),
+       CMD(  MI_FLUSH,                         SMI,    F,  1,      S  ),
+       CMD(  MI_ARB_CHECK,                     SMI,    F,  1,      S  ),
+       CMD(  MI_REPORT_HEAD,                   SMI,    F,  1,      S  ),
+       CMD(  MI_ARB_ON_OFF,                    SMI,    F,  1,      S  ),
+       CMD(  MI_SUSPEND_FLUSH,                 SMI,    F,  1,      S  ),
+       CMD(  MI_LOAD_SCAN_LINES_INCL,          SMI,   !F,  0x3F,   S  ),
+       CMD(  MI_LOAD_SCAN_LINES_EXCL,          SMI,   !F,  0x3F,   S  ),
+       CMD(  MI_STORE_DWORD_IMM,               SMI,   !F,  0x3FF,  S  ),
+       CMD(  MI_LOAD_REGISTER_IMM(1),          SMI,   !F,  0xFF,   W,
+             .reg = { .offset = 1, .mask = 0x007FFFFC, .step = 2 }    ),
+       CMD(  MI_UPDATE_GTT,                    SMI,   !F,  0x3FF,  S  ),
+       CMD(  MI_STORE_REGISTER_MEM_GEN8,       SMI,    F,  4,      W,
+             .reg = { .offset = 1, .mask = 0x007FFFFC }               ),
+       CMD(  MI_FLUSH_DW,                      SMI,   !F,  0x3F,   S  ),
+       CMD(  MI_LOAD_REGISTER_MEM_GEN8,        SMI,    F,  4,      W,
+             .reg = { .offset = 1, .mask = 0x007FFFFC }               ),
+       CMD(  MI_LOAD_REGISTER_REG,             SMI,    !F,  0xFF,  W,
+             .reg = { .offset = 1, .mask = 0x007FFFFC, .step = 1 }    ),
+
+       /*
+        * We allow BB_START but apply further checks. We just sanitize the
+        * basic fields here.
+        */
+#define MI_BB_START_OPERAND_MASK   GENMASK(SMI-1, 0)
+#define MI_BB_START_OPERAND_EXPECT (MI_BATCH_PPGTT_HSW | 1)
+       CMD(  MI_BATCH_BUFFER_START_GEN8,       SMI,    !F,  0xFF,  B,
+             .bits = {{
+                       .offset = 0,
+                       .mask = MI_BB_START_OPERAND_MASK,
+                       .expected = MI_BB_START_OPERAND_EXPECT,
+             }},                                                      ),
+};
+
static const struct drm_i915_cmd_descriptor noop_desc =
       CMD(MI_NOOP, SMI, F, 1, S);

@@ -461,40 +509,44 @@ static const struct drm_i915_cmd_descrip
#undef R
#undef W
#undef B
-#undef M

-static const struct drm_i915_cmd_table gen7_render_cmds[] = {
-       { common_cmds, ARRAY_SIZE(common_cmds) },
-       { render_cmds, ARRAY_SIZE(render_cmds) },
+static const struct drm_i915_cmd_table gen7_render_cmd_table[] = {
+       { gen7_common_cmds, ARRAY_SIZE(gen7_common_cmds) },
+       { gen7_render_cmds, ARRAY_SIZE(gen7_render_cmds) },
};

-static const struct drm_i915_cmd_table hsw_render_ring_cmds[] = {
-       { common_cmds, ARRAY_SIZE(common_cmds) },
-       { render_cmds, ARRAY_SIZE(render_cmds) },
+static const struct drm_i915_cmd_table hsw_render_ring_cmd_table[] = {
+       { gen7_common_cmds, ARRAY_SIZE(gen7_common_cmds) },
+       { gen7_render_cmds, ARRAY_SIZE(gen7_render_cmds) },
       { hsw_render_cmds, ARRAY_SIZE(hsw_render_cmds) },
};

-static const struct drm_i915_cmd_table gen7_video_cmds[] = {
-       { common_cmds, ARRAY_SIZE(common_cmds) },
-       { video_cmds, ARRAY_SIZE(video_cmds) },
+static const struct drm_i915_cmd_table gen7_video_cmd_table[] = {
+       { gen7_common_cmds, ARRAY_SIZE(gen7_common_cmds) },
+       { gen7_video_cmds, ARRAY_SIZE(gen7_video_cmds) },
};

-static const struct drm_i915_cmd_table hsw_vebox_cmds[] = {
-       { common_cmds, ARRAY_SIZE(common_cmds) },
-       { vecs_cmds, ARRAY_SIZE(vecs_cmds) },
+static const struct drm_i915_cmd_table hsw_vebox_cmd_table[] = {
+       { gen7_common_cmds, ARRAY_SIZE(gen7_common_cmds) },
+       { gen7_vecs_cmds, ARRAY_SIZE(gen7_vecs_cmds) },
};

-static const struct drm_i915_cmd_table gen7_blt_cmds[] = {
-       { common_cmds, ARRAY_SIZE(common_cmds) },
-       { blt_cmds, ARRAY_SIZE(blt_cmds) },
+static const struct drm_i915_cmd_table gen7_blt_cmd_table[] = {
+       { gen7_common_cmds, ARRAY_SIZE(gen7_common_cmds) },
+       { gen7_blt_cmds, ARRAY_SIZE(gen7_blt_cmds) },
};

-static const struct drm_i915_cmd_table hsw_blt_ring_cmds[] = {
-       { common_cmds, ARRAY_SIZE(common_cmds) },
-       { blt_cmds, ARRAY_SIZE(blt_cmds) },
+static const struct drm_i915_cmd_table hsw_blt_ring_cmd_table[] = {
+       { gen7_common_cmds, ARRAY_SIZE(gen7_common_cmds) },
+       { gen7_blt_cmds, ARRAY_SIZE(gen7_blt_cmds) },
       { hsw_blt_cmds, ARRAY_SIZE(hsw_blt_cmds) },
};

+static const struct drm_i915_cmd_table gen9_blt_cmd_table[] = {
+       { gen9_blt_cmds, ARRAY_SIZE(gen9_blt_cmds) },
+};
+
+
/*
 * Register whitelists, sorted by increasing register offset.
 */
@@ -610,17 +662,27 @@ static const struct drm_i915_reg_descrip
       REG64_IDX(RING_TIMESTAMP, BLT_RING_BASE),
};

-static const struct drm_i915_reg_descriptor ivb_master_regs[] = {
-       REG32(FORCEWAKE_MT),
-       REG32(DERRMR),
-       REG32(GEN7_PIPE_DE_LOAD_SL(PIPE_A)),
-       REG32(GEN7_PIPE_DE_LOAD_SL(PIPE_B)),
-       REG32(GEN7_PIPE_DE_LOAD_SL(PIPE_C)),
-};
-
-static const struct drm_i915_reg_descriptor hsw_master_regs[] = {
-       REG32(FORCEWAKE_MT),
-       REG32(DERRMR),
+static const struct drm_i915_reg_descriptor gen9_blt_regs[] = {
+       REG64_IDX(RING_TIMESTAMP, RENDER_RING_BASE),
+       REG64_IDX(RING_TIMESTAMP, BSD_RING_BASE),
+       REG32(BCS_SWCTRL),
+       REG64_IDX(RING_TIMESTAMP, BLT_RING_BASE),
+       REG64_IDX(BCS_GPR, 0),
+       REG64_IDX(BCS_GPR, 1),
+       REG64_IDX(BCS_GPR, 2),
+       REG64_IDX(BCS_GPR, 3),
+       REG64_IDX(BCS_GPR, 4),
+       REG64_IDX(BCS_GPR, 5),
+       REG64_IDX(BCS_GPR, 6),
+       REG64_IDX(BCS_GPR, 7),
+       REG64_IDX(BCS_GPR, 8),
+       REG64_IDX(BCS_GPR, 9),
+       REG64_IDX(BCS_GPR, 10),
+       REG64_IDX(BCS_GPR, 11),
+       REG64_IDX(BCS_GPR, 12),
+       REG64_IDX(BCS_GPR, 13),
+       REG64_IDX(BCS_GPR, 14),
+       REG64_IDX(BCS_GPR, 15),
};

#undef REG64
@@ -629,28 +691,27 @@ static const struct drm_i915_reg_descrip
struct drm_i915_reg_table {
       const struct drm_i915_reg_descriptor *regs;
       int num_regs;
-       bool master;
};

static const struct drm_i915_reg_table ivb_render_reg_tables[] = {
-       { gen7_render_regs, ARRAY_SIZE(gen7_render_regs), false },
-       { ivb_master_regs, ARRAY_SIZE(ivb_master_regs), true },
+       { gen7_render_regs, ARRAY_SIZE(gen7_render_regs) },
};

static const struct drm_i915_reg_table ivb_blt_reg_tables[] = {
-       { gen7_blt_regs, ARRAY_SIZE(gen7_blt_regs), false },
-       { ivb_master_regs, ARRAY_SIZE(ivb_master_regs), true },
+       { gen7_blt_regs, ARRAY_SIZE(gen7_blt_regs) },
};

static const struct drm_i915_reg_table hsw_render_reg_tables[] = {
-       { gen7_render_regs, ARRAY_SIZE(gen7_render_regs), false },
-       { hsw_render_regs, ARRAY_SIZE(hsw_render_regs), false },
-       { hsw_master_regs, ARRAY_SIZE(hsw_master_regs), true },
+       { gen7_render_regs, ARRAY_SIZE(gen7_render_regs) },
+       { hsw_render_regs, ARRAY_SIZE(hsw_render_regs) },
};

static const struct drm_i915_reg_table hsw_blt_reg_tables[] = {
-       { gen7_blt_regs, ARRAY_SIZE(gen7_blt_regs), false },
-       { hsw_master_regs, ARRAY_SIZE(hsw_master_regs), true },
+       { gen7_blt_regs, ARRAY_SIZE(gen7_blt_regs) },
+};
+
+static const struct drm_i915_reg_table gen9_blt_reg_tables[] = {
+       { gen9_blt_regs, ARRAY_SIZE(gen9_blt_regs) },
};

static u32 gen7_render_get_cmd_length_mask(u32 cmd_header)
@@ -708,6 +769,17 @@ static u32 gen7_blt_get_cmd_length_mask(
       return 0;
}

+static u32 gen9_blt_get_cmd_length_mask(u32 cmd_header)
+{
+       u32 client = cmd_header >> INSTR_CLIENT_SHIFT;
+
+       if (client == INSTR_MI_CLIENT || client == INSTR_BC_CLIENT)
+               return 0xFF;
+
+       DRM_DEBUG_DRIVER("CMD: Abnormal blt cmd length! 0x%08X\n", cmd_header);
+       return 0;
+}
+
static bool validate_cmds_sorted(const struct intel_engine_cs *engine,
                                const struct drm_i915_cmd_table *cmd_tables,
                                int cmd_table_count)
@@ -865,18 +937,19 @@ void intel_engine_init_cmd_parser(struct
       int cmd_table_count;
       int ret;

-       if (!IS_GEN7(engine->i915))
+       if (!IS_GEN7(engine->i915) && !(IS_GEN9(engine->i915) &&
+                                       engine->id == BCS))
               return;

       switch (engine->id) {
       case RCS:
               if (IS_HASWELL(engine->i915)) {
-                       cmd_tables = hsw_render_ring_cmds;
+                       cmd_tables = hsw_render_ring_cmd_table;
                       cmd_table_count =
-                               ARRAY_SIZE(hsw_render_ring_cmds);
+                               ARRAY_SIZE(hsw_render_ring_cmd_table);
               } else {
-                       cmd_tables = gen7_render_cmds;
-                       cmd_table_count = ARRAY_SIZE(gen7_render_cmds);
+                       cmd_tables = gen7_render_cmd_table;
+                       cmd_table_count = ARRAY_SIZE(gen7_render_cmd_table);
               }

               if (IS_HASWELL(engine->i915)) {
@@ -886,36 +959,46 @@ void intel_engine_init_cmd_parser(struct
                       engine->reg_tables = ivb_render_reg_tables;
                       engine->reg_table_count = ARRAY_SIZE(ivb_render_reg_tables);
               }
-
               engine->get_cmd_length_mask = gen7_render_get_cmd_length_mask;
               break;
       case VCS:
-               cmd_tables = gen7_video_cmds;
-               cmd_table_count = ARRAY_SIZE(gen7_video_cmds);
+               cmd_tables = gen7_video_cmd_table;
+               cmd_table_count = ARRAY_SIZE(gen7_video_cmd_table);
               engine->get_cmd_length_mask = gen7_bsd_get_cmd_length_mask;
               break;
       case BCS:
-               if (IS_HASWELL(engine->i915)) {
-                       cmd_tables = hsw_blt_ring_cmds;
-                       cmd_table_count = ARRAY_SIZE(hsw_blt_ring_cmds);
+               engine->get_cmd_length_mask = gen7_blt_get_cmd_length_mask;
+               if (IS_GEN9(engine->i915)) {
+                       cmd_tables = gen9_blt_cmd_table;
+                       cmd_table_count = ARRAY_SIZE(gen9_blt_cmd_table);
+                       engine->get_cmd_length_mask =
+                               gen9_blt_get_cmd_length_mask;
+
+                       /* BCS Engine unsafe without parser */
+                       engine->flags |= I915_ENGINE_REQUIRES_CMD_PARSER;
+               } else if (IS_HASWELL(engine->i915)) {
+                       cmd_tables = hsw_blt_ring_cmd_table;
+                       cmd_table_count = ARRAY_SIZE(hsw_blt_ring_cmd_table);
               } else {
-                       cmd_tables = gen7_blt_cmds;
-                       cmd_table_count = ARRAY_SIZE(gen7_blt_cmds);
+                       cmd_tables = gen7_blt_cmd_table;
+                       cmd_table_count = ARRAY_SIZE(gen7_blt_cmd_table);
               }

-               if (IS_HASWELL(engine->i915)) {
+               if (IS_GEN9(engine->i915)) {
+                       engine->reg_tables = gen9_blt_reg_tables;
+                       engine->reg_table_count =
+                               ARRAY_SIZE(gen9_blt_reg_tables);
+               } else if (IS_HASWELL(engine->i915)) {
                       engine->reg_tables = hsw_blt_reg_tables;
                       engine->reg_table_count = ARRAY_SIZE(hsw_blt_reg_tables);
               } else {
                       engine->reg_tables = ivb_blt_reg_tables;
                       engine->reg_table_count = ARRAY_SIZE(ivb_blt_reg_tables);
               }
-
-               engine->get_cmd_length_mask = gen7_blt_get_cmd_length_mask;
               break;
       case VECS:
-               cmd_tables = hsw_vebox_cmds;
-               cmd_table_count = ARRAY_SIZE(hsw_vebox_cmds);
+               cmd_tables = hsw_vebox_cmd_table;
+               cmd_table_count = ARRAY_SIZE(hsw_vebox_cmd_table);
               /* VECS can use the same length_mask function as VCS */
               engine->get_cmd_length_mask = gen7_bsd_get_cmd_length_mask;
               break;
@@ -941,7 +1024,7 @@ void intel_engine_init_cmd_parser(struct
               return;
       }

-       engine->flags |= I915_ENGINE_NEEDS_CMD_PARSER;
+       engine->flags |= I915_ENGINE_USING_CMD_PARSER;
}

/**
@@ -953,7 +1036,7 @@ void intel_engine_init_cmd_parser(struct
 */
void intel_engine_cleanup_cmd_parser(struct intel_engine_cs *engine)
{
-       if (!intel_engine_needs_cmd_parser(engine))
+       if (!intel_engine_using_cmd_parser(engine))
               return;

       fini_hash_table(engine);
@@ -1027,22 +1110,16 @@ __find_reg(const struct drm_i915_reg_des
}

static const struct drm_i915_reg_descriptor *
-find_reg(const struct intel_engine_cs *engine, bool is_master, u32 addr)
+find_reg(const struct intel_engine_cs *engine, u32 addr)
{
       const struct drm_i915_reg_table *table = engine->reg_tables;
+       const struct drm_i915_reg_descriptor *reg = NULL;
       int count = engine->reg_table_count;

-       for (; count > 0; ++table, --count) {
-               if (!table->master || is_master) {
-                       const struct drm_i915_reg_descriptor *reg;
-
-                       reg = __find_reg(table->regs, table->num_regs, addr);
-                       if (reg != NULL)
-                               return reg;
-               }
-       }
+       for (; !reg && (count > 0); ++table, --count)
+               reg = __find_reg(table->regs, table->num_regs, addr);

-       return NULL;
+       return reg;
}

/* Returns a vmap'd pointer to dst_obj, which the caller must unmap */
@@ -1127,8 +1204,7 @@ unpin_src:

static bool check_cmd(const struct intel_engine_cs *engine,
                     const struct drm_i915_cmd_descriptor *desc,
-                     const u32 *cmd, u32 length,
-                     const bool is_master)
+                     const u32 *cmd, u32 length)
{
       if (desc->flags & CMD_DESC_SKIP)
               return true;
@@ -1138,12 +1214,6 @@ static bool check_cmd(const struct intel
               return false;
       }

-       if ((desc->flags & CMD_DESC_MASTER) && !is_master) {
-               DRM_DEBUG_DRIVER("CMD: Rejected master-only command: 0x%08X\n",
-                                *cmd);
-               return false;
-       }
-
       if (desc->flags & CMD_DESC_REGISTER) {
               /*
                * Get the distance between individual register offset
@@ -1157,7 +1227,7 @@ static bool check_cmd(const struct intel
                    offset += step) {
                       const u32 reg_addr = cmd[offset] & desc->reg.mask;
                       const struct drm_i915_reg_descriptor *reg =
-                               find_reg(engine, is_master, reg_addr);
+                               find_reg(engine, reg_addr);

                       if (!reg) {
                               DRM_DEBUG_DRIVER("CMD: Rejected register 0x%08X in command: 0x%08X (%s)\n",
@@ -1235,16 +1305,112 @@ static bool check_cmd(const struct intel
       return true;
}

+static int check_bbstart(const struct i915_gem_context *ctx,
+                        u32 *cmd, u32 offset, u32 length,
+                        u32 batch_len,
+                        u64 batch_start,
+                        u64 shadow_batch_start)
+{
+       u64 jump_offset, jump_target;
+       u32 target_cmd_offset, target_cmd_index;
+
+       /* For igt compatibility on older platforms */
+       if (CMDPARSER_USES_GGTT(ctx->i915)) {
+               DRM_DEBUG("CMD: Rejecting BB_START for ggtt based submission\n");
+               return -EACCES;
+       }
+
+       if (length != 3) {
+               DRM_DEBUG("CMD: Recursive BB_START with bad length(%u)\n",
+                         length);
+               return -EINVAL;
+       }
+
+       jump_target = *(u64*)(cmd+1);
+       jump_offset = jump_target - batch_start;
+
+       /*
+        * Any underflow of jump_target is guaranteed to be outside the range
+        * of a u32, so >= test catches both too large and too small
+        */
+       if (jump_offset >= batch_len) {
+               DRM_DEBUG("CMD: BB_START to 0x%llx jumps out of BB\n",
+                         jump_target);
+               return -EINVAL;
+       }
+
+       /*
+        * This cannot overflow a u32 because we already checked jump_offset
+        * is within the BB, and the batch_len is a u32
+        */
+       target_cmd_offset = lower_32_bits(jump_offset);
+       target_cmd_index = target_cmd_offset / sizeof(u32);
+
+       *(u64*)(cmd + 1) = shadow_batch_start + target_cmd_offset;
+
+       if (target_cmd_index == offset)
+               return 0;
+
+       if (ctx->jump_whitelist_cmds <= target_cmd_index) {
+               DRM_DEBUG("CMD: Rejecting BB_START - truncated whitelist array\n");
+               return -EINVAL;
+       } else if (!test_bit(target_cmd_index, ctx->jump_whitelist)) {
+               DRM_DEBUG("CMD: BB_START to 0x%llx not a previously executed cmd\n",
+                         jump_target);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void init_whitelist(struct i915_gem_context *ctx, u32 batch_len)
+{
+       const u32 batch_cmds = DIV_ROUND_UP(batch_len, sizeof(u32));
+       const u32 exact_size = BITS_TO_LONGS(batch_cmds);
+       u32 next_size = BITS_TO_LONGS(roundup_pow_of_two(batch_cmds));
+       unsigned long *next_whitelist;
+
+       if (CMDPARSER_USES_GGTT(ctx->i915))
+               return;
+
+       if (batch_cmds <= ctx->jump_whitelist_cmds) {
+               bitmap_zero(ctx->jump_whitelist, batch_cmds);
+               return;
+       }
+
+again:
+       next_whitelist = kcalloc(next_size, sizeof(long), GFP_KERNEL);
+       if (next_whitelist) {
+               kfree(ctx->jump_whitelist);
+               ctx->jump_whitelist = next_whitelist;
+               ctx->jump_whitelist_cmds =
+                       next_size * BITS_PER_BYTE * sizeof(long);
+               return;
+       }
+
+       if (next_size > exact_size) {
+               next_size = exact_size;
+               goto again;
+       }
+
+       DRM_DEBUG("CMD: Failed to extend whitelist. BB_START may be disallowed\n");
+       bitmap_zero(ctx->jump_whitelist, ctx->jump_whitelist_cmds);
+
+       return;
+}
+
#define LENGTH_BIAS 2

/**
 * i915_parse_cmds() - parse a submitted batch buffer for privilege violations
+ * @ctx: the context in which the batch is to execute
 * @engine: the engine on which the batch is to execute
 * @batch_obj: the batch buffer in question
- * @shadow_batch_obj: copy of the batch buffer in question
+ * @batch_start: Canonical base address of batch
 * @batch_start_offset: byte offset in the batch at which execution starts
 * @batch_len: length of the commands in batch_obj
- * @is_master: is the submitting process the drm master?
+ * @shadow_batch_obj: copy of the batch buffer in question
+ * @shadow_batch_start: Canonical base address of shadow_batch_obj
 *
 * Parses the specified batch buffer looking for privilege violations as
 * described in the overview.
@@ -1252,14 +1418,17 @@ static bool check_cmd(const struct intel
 * Return: non-zero if the parser finds violations or otherwise fails; -EACCES
 * if the batch appears legal but should use hardware parsing
 */
-int intel_engine_cmd_parser(struct intel_engine_cs *engine,
+
+int intel_engine_cmd_parser(struct i915_gem_context *ctx,
+                           struct intel_engine_cs *engine,
                           struct drm_i915_gem_object *batch_obj,
-                           struct drm_i915_gem_object *shadow_batch_obj,
+                           u64 batch_start,
                           u32 batch_start_offset,
                           u32 batch_len,
-                           bool is_master)
+                           struct drm_i915_gem_object *shadow_batch_obj,
+                           u64 shadow_batch_start)
{
-       u32 *cmd, *batch_end;
+       u32 *cmd, *batch_end, offset = 0;
       struct drm_i915_cmd_descriptor default_desc = noop_desc;
       const struct drm_i915_cmd_descriptor *desc = &default_desc;
       bool needs_clflush_after = false;
@@ -1273,6 +1442,8 @@ int intel_engine_cmd_parser(struct intel
               return PTR_ERR(cmd);
       }

+       init_whitelist(ctx, batch_len);
+
       /*
        * We use the batch length as size because the shadow object is as
        * large or larger and copy_batch() will write MI_NOPs to the extra
@@ -1282,31 +1453,15 @@ int intel_engine_cmd_parser(struct intel
       do {
               u32 length;

-               if (*cmd == MI_BATCH_BUFFER_END) {
-                       if (needs_clflush_after) {
-                               void *ptr = page_mask_bits(shadow_batch_obj->mm.mapping);
-                               drm_clflush_virt_range(ptr,
-                                                      (void *)(cmd + 1) - ptr);
-                       }
+               if (*cmd == MI_BATCH_BUFFER_END)
                       break;
-               }

               desc = find_cmd(engine, *cmd, desc, &default_desc);
               if (!desc) {
                       DRM_DEBUG_DRIVER("CMD: Unrecognized command: 0x%08X\n",
                                        *cmd);
                       ret = -EINVAL;
-                       break;
-               }
-
-               /*
-                * If the batch buffer contains a chained batch, return an
-                * error that tells the caller to abort and dispatch the
-                * workload as a non-secure batch.
-                */
-               if (desc->cmd.value == MI_BATCH_BUFFER_START) {
-                       ret = -EACCES;
-                       break;
+                       goto err;
               }

               if (desc->flags & CMD_DESC_FIXED)
@@ -1320,22 +1475,43 @@ int intel_engine_cmd_parser(struct intel
                                        length,
                                        batch_end - cmd);
                       ret = -EINVAL;
-                       break;
+                       goto err;
               }

-               if (!check_cmd(engine, desc, cmd, length, is_master)) {
+               if (!check_cmd(engine, desc, cmd, length)) {
                       ret = -EACCES;
+                       goto err;
+               }
+
+               if (desc->cmd.value == MI_BATCH_BUFFER_START) {
+                       ret = check_bbstart(ctx, cmd, offset, length,
+                                           batch_len, batch_start,
+                                           shadow_batch_start);
+
+                       if (ret)
+                               goto err;
                       break;
               }

+               if (ctx->jump_whitelist_cmds > offset)
+                       set_bit(offset, ctx->jump_whitelist);
+
               cmd += length;
+               offset += length;
               if  (cmd >= batch_end) {
                       DRM_DEBUG_DRIVER("CMD: Got to the end of the buffer w/o a BBE cmd!\n");
                       ret = -EINVAL;
-                       break;
+                       goto err;
               }
       } while (1);

+       if (needs_clflush_after) {
+               void *ptr = page_mask_bits(shadow_batch_obj->mm.mapping);
+
+               drm_clflush_virt_range(ptr, (void *)(cmd + 1) - ptr);
+       }
+
+err:
       i915_gem_object_unpin_map(shadow_batch_obj);
       return ret;
}
@@ -1357,7 +1533,7 @@ int i915_cmd_parser_get_version(struct d

       /* If the command parser is not enabled, report 0 - unsupported */
       for_each_engine(engine, dev_priv, id) {
-               if (intel_engine_needs_cmd_parser(engine)) {
+               if (intel_engine_using_cmd_parser(engine)) {
                       active = true;
                       break;
               }
@@ -1382,6 +1558,7 @@ int i915_cmd_parser_get_version(struct d
        *    the parser enabled.
        * 9. Don't whitelist or handle oacontrol specially, as ownership
        *    for oacontrol state is moving to i915-perf.
+        * 10. Support for Gen9 BCS Parsing
        */
-       return 9;
+       return 10;
}
Index: sys/dev/pci/drm/i915/i915_drv.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/drm/i915/i915_drv.c,v
retrieving revision 1.122
diff -u -p -r1.122 i915_drv.c
--- sys/dev/pci/drm/i915/i915_drv.c     7 Sep 2019 00:51:40 -0000       1.122
+++ sys/dev/pci/drm/i915/i915_drv.c     20 Nov 2019 07:02:22 -0000
@@ -369,7 +369,7 @@ static int i915_getparam_ioctl(struct dr
               value = HAS_LEGACY_SEMAPHORES(dev_priv);
               break;
       case I915_PARAM_HAS_SECURE_BATCHES:
-               value = capable(CAP_SYS_ADMIN);
+               value = HAS_SECURE_BATCHES(dev_priv) && capable(CAP_SYS_ADMIN);
               break;
       case I915_PARAM_CMD_PARSER_VERSION:
               value = i915_cmd_parser_get_version(dev_priv);
@@ -1821,6 +1821,7 @@ static int i915_drm_suspend_late(struct
       i915_gem_suspend_late(dev_priv);

       intel_display_set_init_power(dev_priv, false);
+       i915_rc6_ctx_wa_suspend(dev_priv);
       intel_uncore_suspend(dev_priv);

       /*
@@ -2048,6 +2049,8 @@ static int i915_drm_resume_early(struct
               intel_power_domains_init_hw(dev_priv, true);
       else
               intel_display_set_init_power(dev_priv, true);
+
+       i915_rc6_ctx_wa_resume(dev_priv);

       intel_engines_sanitize(dev_priv);

Index: sys/dev/pci/drm/i915/i915_drv.h
===================================================================
RCS file: /cvs/src/sys/dev/pci/drm/i915/i915_drv.h,v
retrieving revision 1.84
diff -u -p -r1.84 i915_drv.h
--- sys/dev/pci/drm/i915/i915_drv.h     24 Aug 2019 01:43:12 -0000      1.84
+++ sys/dev/pci/drm/i915/i915_drv.h     20 Nov 2019 07:02:22 -0000
@@ -902,6 +902,7 @@ struct intel_rps {

struct intel_rc6 {
       bool enabled;
+       bool ctx_corrupted;
       u64 prev_hw_residency[4];
       u64 cur_residency[4];
};
@@ -2657,6 +2658,12 @@ intel_info(const struct drm_i915_private
#define IS_GEN9_LP(dev_priv)   (IS_GEN9(dev_priv) && IS_LP(dev_priv))
#define IS_GEN9_BC(dev_priv)   (IS_GEN9(dev_priv) && !IS_LP(dev_priv))

+/*
+ * The Gen7 cmdparser copies the scanned buffer to the ggtt for execution
+ * All later gens can run the final buffer from the ppgtt
+ */
+#define CMDPARSER_USES_GGTT(dev_priv) IS_GEN7(dev_priv)
+
#define ENGINE_MASK(id)        BIT(id)
#define RENDER_RING    ENGINE_MASK(RCS)
#define BSD_RING       ENGINE_MASK(VCS)
@@ -2678,6 +2685,8 @@ intel_info(const struct drm_i915_private

#define HAS_LEGACY_SEMAPHORES(dev_priv) IS_GEN7(dev_priv)

+#define HAS_SECURE_BATCHES(dev_priv) (INTEL_GEN(dev_priv) < 6)
+
#define HAS_LLC(dev_priv)      ((dev_priv)->info.has_llc)
#define HAS_SNOOP(dev_priv)    ((dev_priv)->info.has_snoop)
#define HAS_EDRAM(dev_priv)    (!!((dev_priv)->edram_cap & EDRAM_ENABLED))
@@ -2710,10 +2719,12 @@ intel_info(const struct drm_i915_private
/* Early gen2 have a totally busted CS tlb and require pinned batches. */
#define HAS_BROKEN_CS_TLB(dev_priv)    (IS_I830(dev_priv) || IS_I845G(dev_priv))

+#define NEEDS_RC6_CTX_CORRUPTION_WA(dev_priv)  \
+       (IS_BROADWELL(dev_priv) || INTEL_GEN(dev_priv) == 9)
+
/* WaRsDisableCoarsePowerGating:skl,cnl */
#define NEEDS_WaRsDisableCoarsePowerGating(dev_priv) \
-       (IS_CANNONLAKE(dev_priv) || \
-        IS_SKL_GT3(dev_priv) || IS_SKL_GT4(dev_priv))
+       (IS_CANNONLAKE(dev_priv) || INTEL_GEN(dev_priv) == 9)

#define HAS_GMBUS_IRQ(dev_priv) (INTEL_GEN(dev_priv) >= 4)
#define HAS_GMBUS_BURST_READ(dev_priv) (INTEL_GEN(dev_priv) >= 10 || \
@@ -3108,6 +3119,14 @@ i915_gem_object_ggtt_pin(struct drm_i915
                        u64 alignment,
                        u64 flags);

+struct i915_vma * __must_check
+i915_gem_object_pin(struct drm_i915_gem_object *obj,
+                   struct i915_address_space *vm,
+                   const struct i915_ggtt_view *view,
+                   u64 size,
+                   u64 alignment,
+                   u64 flags);
+
int i915_gem_object_unbind(struct drm_i915_gem_object *obj);
void i915_gem_release_mmap(struct drm_i915_gem_object *obj);

@@ -3507,12 +3526,14 @@ const char *i915_cache_level_str(struct
int i915_cmd_parser_get_version(struct drm_i915_private *dev_priv);
void intel_engine_init_cmd_parser(struct intel_engine_cs *engine);
void intel_engine_cleanup_cmd_parser(struct intel_engine_cs *engine);
-int intel_engine_cmd_parser(struct intel_engine_cs *engine,
+int intel_engine_cmd_parser(struct i915_gem_context *cxt,
+                           struct intel_engine_cs *engine,
                           struct drm_i915_gem_object *batch_obj,
-                           struct drm_i915_gem_object *shadow_batch_obj,
+                           u64 user_batch_start,
                           u32 batch_start_offset,
                           u32 batch_len,
-                           bool is_master);
+                           struct drm_i915_gem_object *shadow_batch_obj,
+                           u64 shadow_batch_start);

/* i915_perf.c */
extern void i915_perf_init(struct drm_i915_private *dev_priv);
Index: sys/dev/pci/drm/i915/i915_gem.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/drm/i915/i915_gem.c,v
retrieving revision 1.122
diff -u -p -r1.122 i915_gem.c
--- sys/dev/pci/drm/i915/i915_gem.c     16 Sep 2019 15:22:46 -0000      1.122
+++ sys/dev/pci/drm/i915/i915_gem.c     20 Nov 2019 07:02:22 -0000
@@ -174,6 +174,11 @@ static u32 __i915_gem_park(struct drm_i9
       if (INTEL_GEN(i915) >= 6)
               gen6_rps_idle(i915);

+       if (NEEDS_RC6_CTX_CORRUPTION_WA(i915)) {
+               i915_rc6_ctx_wa_check(i915);
+               intel_uncore_forcewake_put(i915, FORCEWAKE_ALL);
+       }
+
       intel_display_power_put(i915, POWER_DOMAIN_GT_IRQ);

       intel_runtime_pm_put(i915);
@@ -220,6 +225,9 @@ void i915_gem_unpark(struct drm_i915_pri
        */
       intel_display_power_get(i915, POWER_DOMAIN_GT_IRQ);

+       if (NEEDS_RC6_CTX_CORRUPTION_WA(i915))
+               intel_uncore_forcewake_get(i915, FORCEWAKE_ALL);
+
       i915->gt.awake = true;
       if (unlikely(++i915->gt.epoch == 0)) /* keep 0 as invalid */
               i915->gt.epoch = 1;
@@ -4844,6 +4852,20 @@ i915_gem_object_ggtt_pin(struct drm_i915
{
       struct drm_i915_private *dev_priv = to_i915(obj->base.dev);
       struct i915_address_space *vm = &dev_priv->ggtt.vm;
+
+       return i915_gem_object_pin(obj, vm, view, size, alignment,
+                                  flags | PIN_GLOBAL);
+}
+
+struct i915_vma *
+i915_gem_object_pin(struct drm_i915_gem_object *obj,
+                   struct i915_address_space *vm,
+                   const struct i915_ggtt_view *view,
+                   u64 size,
+                   u64 alignment,
+                   u64 flags)
+{
+       struct drm_i915_private *dev_priv = to_i915(obj->base.dev);
       struct i915_vma *vma;
       int ret;

@@ -4907,7 +4929,7 @@ i915_gem_object_ggtt_pin(struct drm_i915
                       return ERR_PTR(ret);
       }

-       ret = i915_vma_pin(vma, size, alignment, flags | PIN_GLOBAL);
+       ret = i915_vma_pin(vma, size, alignment, flags);
       if (ret)
               return ERR_PTR(ret);

Index: sys/dev/pci/drm/i915/i915_gem_context.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/drm/i915/i915_gem_context.c,v
retrieving revision 1.16
diff -u -p -r1.16 i915_gem_context.c
--- sys/dev/pci/drm/i915/i915_gem_context.c     14 Apr 2019 10:14:51 -0000      1.16
+++ sys/dev/pci/drm/i915/i915_gem_context.c     20 Nov 2019 07:02:22 -0000
@@ -128,6 +128,8 @@ static void i915_gem_context_free(struct

       i915_ppgtt_put(ctx->ppgtt);

+       kfree(ctx->jump_whitelist);
+
       for (n = 0; n < ARRAY_SIZE(ctx->__engine); n++) {
               struct intel_context *ce = &ctx->__engine[n];

@@ -350,6 +352,9 @@ __create_hw_context(struct drm_i915_priv
               ctx->ggtt_offset_bias = dev_priv->guc.ggtt_pin_bias;
       else
               ctx->ggtt_offset_bias = I915_GTT_PAGE_SIZE;
+
+       ctx->jump_whitelist = NULL;
+       ctx->jump_whitelist_cmds = 0;

       return ctx;

Index: sys/dev/pci/drm/i915/i915_gem_context.h
===================================================================
RCS file: /cvs/src/sys/dev/pci/drm/i915/i915_gem_context.h,v
retrieving revision 1.1
diff -u -p -r1.1 i915_gem_context.h
--- sys/dev/pci/drm/i915/i915_gem_context.h     14 Apr 2019 10:14:51 -0000      1.1
+++ sys/dev/pci/drm/i915/i915_gem_context.h     20 Nov 2019 07:02:22 -0000
@@ -187,6 +187,12 @@ struct i915_gem_context {
       /** remap_slice: Bitmask of cache lines that need remapping */
       u8 remap_slice;

+       /** jump_whitelist: Bit array for tracking cmds during cmdparsing */
+       unsigned long *jump_whitelist;
+
+       /** jump_whitelist_cmds: No of cmd slots available */
+       u32 jump_whitelist_cmds;
+
       /** handles_vma: rbtree to look up our context specific obj/vma for
        * the user handle. (user handles are per fd, but the binding is
        * per vm, which may be one per context or shared with the global GTT)
Index: sys/dev/pci/drm/i915/i915_gem_execbuffer.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/drm/i915/i915_gem_execbuffer.c,v
retrieving revision 1.45
diff -u -p -r1.45 i915_gem_execbuffer.c
--- sys/dev/pci/drm/i915/i915_gem_execbuffer.c  14 Apr 2019 10:14:51 -0000      1.45
+++ sys/dev/pci/drm/i915/i915_gem_execbuffer.c  20 Nov 2019 07:02:22 -0000
@@ -315,7 +315,9 @@ static inline u64 gen8_noncanonical_addr

static inline bool eb_use_cmdparser(const struct i915_execbuffer *eb)
{
-       return intel_engine_needs_cmd_parser(eb->engine) && eb->batch_len;
+       return intel_engine_requires_cmd_parser(eb->engine) ||
+               (intel_engine_using_cmd_parser(eb->engine) &&
+                eb->args->batch_len);
}

static int eb_create(struct i915_execbuffer *eb)
@@ -1923,10 +1925,38 @@ static int i915_reset_gen7_sol_offsets(s
       return 0;
}

-static struct i915_vma *eb_parse(struct i915_execbuffer *eb, bool is_master)
+static struct i915_vma *
+shadow_batch_pin(struct i915_execbuffer *eb, struct drm_i915_gem_object *obj)
+{
+       struct drm_i915_private *dev_priv = eb->i915;
+       struct i915_address_space *vm;
+       u64 flags;
+
+       /*
+        * PPGTT backed shadow buffers must be mapped RO, to prevent
+        * post-scan tampering
+        */
+       if (CMDPARSER_USES_GGTT(dev_priv)) {
+               flags = PIN_GLOBAL;
+               vm = &dev_priv->ggtt.vm;
+       } else if (eb->vm->has_read_only) {
+               flags = PIN_USER;
+               vm = eb->vm;
+               i915_gem_object_set_readonly(obj);
+       } else {
+               DRM_DEBUG("Cannot prevent post-scan tampering without RO capable vm\n");
+               return ERR_PTR(-EINVAL);
+       }
+
+       return i915_gem_object_pin(obj, vm, NULL, 0, 0, flags);
+}
+
+static struct i915_vma *eb_parse(struct i915_execbuffer *eb)
{
       struct drm_i915_gem_object *shadow_batch_obj;
       struct i915_vma *vma;
+       u64 batch_start;
+       u64 shadow_batch_start;
       int err;

       shadow_batch_obj = i915_gem_batch_pool_get(&eb->engine->batch_pool,
@@ -1934,29 +1964,54 @@ static struct i915_vma *eb_parse(struct
       if (IS_ERR(shadow_batch_obj))
               return ERR_CAST(shadow_batch_obj);

-       err = intel_engine_cmd_parser(eb->engine,
+       vma = shadow_batch_pin(eb, shadow_batch_obj);
+       if (IS_ERR(vma))
+               goto out;
+
+       batch_start = gen8_canonical_addr(eb->batch->node.start) +
+                     eb->batch_start_offset;
+
+       shadow_batch_start = gen8_canonical_addr(vma->node.start);
+
+       err = intel_engine_cmd_parser(eb->ctx,
+                                     eb->engine,
                                     eb->batch->obj,
-                                     shadow_batch_obj,
+                                     batch_start,
                                     eb->batch_start_offset,
                                     eb->batch_len,
-                                     is_master);
+                                     shadow_batch_obj,
+                                     shadow_batch_start);
+
       if (err) {
-               if (err == -EACCES) /* unhandled chained batch */
+               i915_vma_unpin(vma);
+
+               /*
+                * Unsafe GGTT-backed buffers can still be submitted safely
+                * as non-secure.
+                * For PPGTT backing however, we have no choice but to forcibly
+                * reject unsafe buffers
+                */
+               if (CMDPARSER_USES_GGTT(eb->i915) && (err == -EACCES))
+                       /* Execute original buffer non-secure */
                       vma = NULL;
               else
                       vma = ERR_PTR(err);
-               goto out;
-       }

-       vma = i915_gem_object_ggtt_pin(shadow_batch_obj, NULL, 0, 0, 0);
-       if (IS_ERR(vma))
               goto out;
+       }

       eb->vma[eb->buffer_count] = i915_vma_get(vma);
       eb->flags[eb->buffer_count] =
               __EXEC_OBJECT_HAS_PIN | __EXEC_OBJECT_HAS_REF;
       vma->exec_flags = &eb->flags[eb->buffer_count];
       eb->buffer_count++;
+       eb->batch_start_offset = 0;
+       eb->batch = vma;
+
+       /* eb->batch_len unchanged */
+
+       if (CMDPARSER_USES_GGTT(eb->i915))
+               eb->batch_flags |= I915_DISPATCH_SECURE;

out:
       i915_gem_object_unpin_pages(shadow_batch_obj);
@@ -2209,6 +2264,7 @@ i915_gem_do_execbuffer(struct drm_device
                      struct drm_i915_gem_exec_object2 *exec,
                      struct drm_syncobj **fences)
{
+       struct drm_i915_private *i915 = to_i915(dev);
       struct i915_execbuffer eb;
       struct dma_fence *in_fence = NULL;
       struct sync_file *out_fence = NULL;
@@ -2219,7 +2275,7 @@ i915_gem_do_execbuffer(struct drm_device
       BUILD_BUG_ON(__EXEC_OBJECT_INTERNAL_FLAGS &
                    ~__EXEC_OBJECT_UNKNOWN_FLAGS);

-       eb.i915 = to_i915(dev);
+       eb.i915 = i915;
       eb.file = file;
       eb.args = args;
       if (DBG_FORCE_RELOC || !(args->flags & I915_EXEC_NO_RELOC))
@@ -2241,8 +2297,15 @@ i915_gem_do_execbuffer(struct drm_device

       eb.batch_flags = 0;
       if (args->flags & I915_EXEC_SECURE) {
+               if (INTEL_GEN(i915) >= 11)
+                       return -ENODEV;
+
+               /* Return -EPERM to trigger fallback code on old binaries. */
+               if (!HAS_SECURE_BATCHES(i915))
+                       return -EPERM;
+
               if (!drm_is_current_master(file) || !capable(CAP_SYS_ADMIN))
-                   return -EPERM;
+                       return -EPERM;

               eb.batch_flags |= I915_DISPATCH_SECURE;
       }
@@ -2329,33 +2392,18 @@ i915_gem_do_execbuffer(struct drm_device
               goto err_vma;
       }

+       if (eb.batch_len == 0)
+               eb.batch_len = eb.batch->size - eb.batch_start_offset;
+
       if (eb_use_cmdparser(&eb)) {
               struct i915_vma *vma;

-               vma = eb_parse(&eb, drm_is_current_master(file));
+               vma = eb_parse(&eb);
               if (IS_ERR(vma)) {
                       err = PTR_ERR(vma);
                       goto err_vma;
               }
-
-               if (vma) {
-                       /*
-                        * Batch parsed and accepted:
-                        *
-                        * Set the DISPATCH_SECURE bit to remove the NON_SECURE
-                        * bit from MI_BATCH_BUFFER_START commands issued in
-                        * the dispatch_execbuffer implementations. We
-                        * specifically don't want that set on batches the
-                        * command parser has accepted.
-                        */
-                       eb.batch_flags |= I915_DISPATCH_SECURE;
-                       eb.batch_start_offset = 0;
-                       eb.batch = vma;
-               }
       }
-
-       if (eb.batch_len == 0)
-               eb.batch_len = eb.batch->size - eb.batch_start_offset;

       /*
        * snb/ivb/vlv conflate the "batch in ppgtt" bit with the "non-secure
Index: sys/dev/pci/drm/i915/i915_gem_gtt.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/drm/i915/i915_gem_gtt.c,v
retrieving revision 1.17
diff -u -p -r1.17 i915_gem_gtt.c
--- sys/dev/pci/drm/i915/i915_gem_gtt.c 14 Apr 2019 10:14:51 -0000      1.17
+++ sys/dev/pci/drm/i915/i915_gem_gtt.c 20 Nov 2019 07:02:22 -0000
@@ -158,7 +158,8 @@ int intel_sanitize_enable_ppgtt(struct d
       if (enable_ppgtt == 0 && INTEL_GEN(dev_priv) < 9)
               return 0;

-       if (enable_ppgtt == 1)
+       /* Full PPGTT is required by the Gen9 cmdparser */
+       if (enable_ppgtt == 1 && INTEL_GEN(dev_priv) != 9)
               return 1;

       if (enable_ppgtt == 2 && has_full_ppgtt)
Index: sys/dev/pci/drm/i915/i915_reg.h
===================================================================
RCS file: /cvs/src/sys/dev/pci/drm/i915/i915_reg.h,v
retrieving revision 1.17
diff -u -p -r1.17 i915_reg.h
--- sys/dev/pci/drm/i915/i915_reg.h     16 Sep 2019 15:07:03 -0000      1.17
+++ sys/dev/pci/drm/i915/i915_reg.h     20 Nov 2019 07:02:22 -0000
@@ -387,6 +387,8 @@ static inline bool i915_mmio_reg_valid(i
#define   ECOCHK_PPGTT_WT_HSW          (0x2 << 3)
#define   ECOCHK_PPGTT_WB_HSW          (0x3 << 3)

+#define GEN8_RC6_CTX_INFO              _MMIO(0x8504)
+
#define GAC_ECO_BITS                   _MMIO(0x14090)
#define   ECOBITS_SNB_BIT              (1 << 13)
#define   ECOBITS_PPGTT_CACHE64B       (3 << 8)
@@ -471,6 +473,10 @@ static inline bool i915_mmio_reg_valid(i
 */
#define BCS_SWCTRL _MMIO(0x22200)

+/* There are 16 GPR registers */
+#define BCS_GPR(n)     _MMIO(0x22600 + (n) * 8)
+#define BCS_GPR_UDW(n) _MMIO(0x22600 + (n) * 8 + 4)
+
#define GPGPU_THREADS_DISPATCHED        _MMIO(0x2290)
#define GPGPU_THREADS_DISPATCHED_UDW   _MMIO(0x2290 + 4)
#define HS_INVOCATION_COUNT             _MMIO(0x2300)
@@ -7004,6 +7010,10 @@ enum {
#define SKL_CSR_DC3_DC5_COUNT  _MMIO(0x80030)
#define SKL_CSR_DC5_DC6_COUNT  _MMIO(0x8002C)
#define BXT_CSR_DC3_DC5_COUNT  _MMIO(0x80038)
+
+/* Display Internal Timeout Register */
+#define RM_TIMEOUT             _MMIO(0x42060)
+#define  MMIO_TIMEOUT_US(us)   ((us) << 0)

/* interrupts */
#define DE_MASTER_IRQ_CONTROL   (1 << 31)
Index: sys/dev/pci/drm/i915/intel_drv.h
===================================================================
RCS file: /cvs/src/sys/dev/pci/drm/i915/intel_drv.h,v
retrieving revision 1.12
diff -u -p -r1.12 intel_drv.h
--- sys/dev/pci/drm/i915/intel_drv.h    14 Apr 2019 10:14:52 -0000      1.12
+++ sys/dev/pci/drm/i915/intel_drv.h    20 Nov 2019 07:02:22 -0000
@@ -2064,6 +2064,9 @@ void intel_sanitize_gt_powersave(struct
void intel_enable_gt_powersave(struct drm_i915_private *dev_priv);
void intel_disable_gt_powersave(struct drm_i915_private *dev_priv);
void intel_suspend_gt_powersave(struct drm_i915_private *dev_priv);
+bool i915_rc6_ctx_wa_check(struct drm_i915_private *i915);
+void i915_rc6_ctx_wa_suspend(struct drm_i915_private *i915);
+void i915_rc6_ctx_wa_resume(struct drm_i915_private *i915);
void gen6_rps_busy(struct drm_i915_private *dev_priv);
void gen6_rps_reset_ei(struct drm_i915_private *dev_priv);
void gen6_rps_idle(struct drm_i915_private *dev_priv);
Index: sys/dev/pci/drm/i915/intel_pm.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/drm/i915/intel_pm.c,v
retrieving revision 1.43
diff -u -p -r1.43 intel_pm.c
--- sys/dev/pci/drm/i915/intel_pm.c     14 Apr 2019 10:14:52 -0000      1.43
+++ sys/dev/pci/drm/i915/intel_pm.c     20 Nov 2019 07:02:22 -0000
@@ -116,6 +116,14 @@ static void bxt_init_clock_gating(struct
        */
       I915_WRITE(GEN9_CLKGATE_DIS_0, I915_READ(GEN9_CLKGATE_DIS_0) |
                  PWM1_GATING_DIS | PWM2_GATING_DIS);
+
+       /*
+        * Lower the display internal timeout.
+        * This is needed to avoid any hard hangs when DSI port PLL
+        * is off and a MMIO access is attempted by any privilege
+        * application, using batch buffers or any other means.
+        */
+       I915_WRITE(RM_TIMEOUT, MMIO_TIMEOUT_US(950));
}

static void glk_init_clock_gating(struct drm_i915_private *dev_priv)
@@ -8201,6 +8209,95 @@ static void intel_init_emon(struct drm_i
       dev_priv->ips.corr = (lcfuse & LCFUSE_HIV_MASK);
}

+static bool i915_rc6_ctx_corrupted(struct drm_i915_private *dev_priv)
+{
+       return !I915_READ(GEN8_RC6_CTX_INFO);
+}
+
+static void i915_rc6_ctx_wa_init(struct drm_i915_private *i915)
+{
+       if (!NEEDS_RC6_CTX_CORRUPTION_WA(i915))
+               return;
+
+       if (i915_rc6_ctx_corrupted(i915)) {
+               DRM_INFO("RC6 context corrupted, disabling runtime power management\n");
+               i915->gt_pm.rc6.ctx_corrupted = true;
+               intel_runtime_pm_get(i915);
+       }
+}
+
+static void i915_rc6_ctx_wa_cleanup(struct drm_i915_private *i915)
+{
+       if (i915->gt_pm.rc6.ctx_corrupted) {
+               intel_runtime_pm_put(i915);
+               i915->gt_pm.rc6.ctx_corrupted = false;
+       }
+}
+
+/**
+ * i915_rc6_ctx_wa_suspend - system suspend sequence for the RC6 CTX WA
+ * @i915: i915 device
+ *
+ * Perform any steps needed to clean up the RC6 CTX WA before system suspend.
+ */
+void i915_rc6_ctx_wa_suspend(struct drm_i915_private *i915)
+{
+       if (i915->gt_pm.rc6.ctx_corrupted)
+               intel_runtime_pm_put(i915);
+}
+
+/**
+ * i915_rc6_ctx_wa_resume - system resume sequence for the RC6 CTX WA
+ * @i915: i915 device
+ *
+ * Perform any steps needed to re-init the RC6 CTX WA after system resume.
+ */
+void i915_rc6_ctx_wa_resume(struct drm_i915_private *i915)
+{
+       if (!i915->gt_pm.rc6.ctx_corrupted)
+               return;
+
+       if (i915_rc6_ctx_corrupted(i915)) {
+               intel_runtime_pm_get(i915);
+               return;
+       }
+
+       DRM_INFO("RC6 context restored, re-enabling runtime power management\n");
+       i915->gt_pm.rc6.ctx_corrupted = false;
+}
+
+static void intel_disable_rc6(struct drm_i915_private *dev_priv);
+
+/**
+ * i915_rc6_ctx_wa_check - check for a new RC6 CTX corruption
+ * @i915: i915 device
+ *
+ * Check if an RC6 CTX corruption has happened since the last check and if so
+ * disable RC6 and runtime power management.
+ *
+ * Return false if no context corruption has happened since the last call of
+ * this function, true otherwise.
+*/
+bool i915_rc6_ctx_wa_check(struct drm_i915_private *i915)
+{
+       if (!NEEDS_RC6_CTX_CORRUPTION_WA(i915))
+               return false;
+
+       if (i915->gt_pm.rc6.ctx_corrupted)
+               return false;
+
+       if (!i915_rc6_ctx_corrupted(i915))
+               return false;
+
+       DRM_NOTE("RC6 context corruption, disabling runtime power management\n");
+
+       intel_disable_rc6(i915);
+       i915->gt_pm.rc6.ctx_corrupted = true;
+       intel_runtime_pm_get_noresume(i915);
+
+       return true;
+}
+
void intel_init_gt_powersave(struct drm_i915_private *dev_priv)
{
       struct intel_rps *rps = &dev_priv->gt_pm.rps;
@@ -8216,6 +8313,8 @@ void intel_init_gt_powersave(struct drm_

       mutex_lock(&dev_priv->pcu_lock);

+       i915_rc6_ctx_wa_init(dev_priv);
+
       /* Initialize RPS limits (for userspace) */
       if (IS_CHERRYVIEW(dev_priv))
               cherryview_init_gt_powersave(dev_priv);
@@ -8262,6 +8361,8 @@ void intel_cleanup_gt_powersave(struct d
       if (IS_VALLEYVIEW(dev_priv))
               valleyview_cleanup_gt_powersave(dev_priv);

+       i915_rc6_ctx_wa_cleanup(dev_priv);
+
       if (!HAS_RC6(dev_priv))
               intel_runtime_pm_put(dev_priv);
}
@@ -8306,7 +8407,7 @@ static inline void intel_disable_llc_pst
       i915->gt_pm.llc_pstate.enabled = false;
}

-static void intel_disable_rc6(struct drm_i915_private *dev_priv)
+static void __intel_disable_rc6(struct drm_i915_private *dev_priv)
{
       lockdep_assert_held(&dev_priv->pcu_lock);

@@ -8325,6 +8426,13 @@ static void intel_disable_rc6(struct drm
       dev_priv->gt_pm.rc6.enabled = false;
}

+static void intel_disable_rc6(struct drm_i915_private *dev_priv)
+{
+       mutex_lock(&dev_priv->pcu_lock);
+       __intel_disable_rc6(dev_priv);
+       mutex_unlock(&dev_priv->pcu_lock);
+}
+
static void intel_disable_rps(struct drm_i915_private *dev_priv)
{
       lockdep_assert_held(&dev_priv->pcu_lock);
@@ -8350,7 +8458,7 @@ void intel_disable_gt_powersave(struct d
{
       mutex_lock(&dev_priv->pcu_lock);

-       intel_disable_rc6(dev_priv);
+       __intel_disable_rc6(dev_priv);
       intel_disable_rps(dev_priv);
       if (HAS_LLC(dev_priv))
               intel_disable_llc_pstate(dev_priv);
@@ -8375,6 +8483,9 @@ static void intel_enable_rc6(struct drm_
       lockdep_assert_held(&dev_priv->pcu_lock);

       if (dev_priv->gt_pm.rc6.enabled)
+               return;
+
+       if (dev_priv->gt_pm.rc6.ctx_corrupted)
               return;

       if (IS_CHERRYVIEW(dev_priv))
Index: sys/dev/pci/drm/i915/intel_ringbuffer.h
===================================================================
RCS file: /cvs/src/sys/dev/pci/drm/i915/intel_ringbuffer.h,v
retrieving revision 1.9
diff -u -p -r1.9 intel_ringbuffer.h
--- sys/dev/pci/drm/i915/intel_ringbuffer.h     14 Apr 2019 10:14:52 -0000      1.9
+++ sys/dev/pci/drm/i915/intel_ringbuffer.h     20 Nov 2019 07:02:22 -0000
@@ -590,9 +590,10 @@ struct intel_engine_cs {

       struct intel_engine_hangcheck hangcheck;

-#define I915_ENGINE_NEEDS_CMD_PARSER BIT(0)
-#define I915_ENGINE_SUPPORTS_STATS   BIT(1)
-#define I915_ENGINE_HAS_PREEMPTION   BIT(2)
+#define I915_ENGINE_USING_CMD_PARSER   BIT(0)
+#define I915_ENGINE_SUPPORTS_STATS     BIT(1)
+#define I915_ENGINE_HAS_PREEMPTION     BIT(2)
+#define I915_ENGINE_REQUIRES_CMD_PARSER        BIT(3)
       unsigned int flags;

       /*
@@ -653,9 +654,15 @@ struct intel_engine_cs {
};

static inline bool
-intel_engine_needs_cmd_parser(const struct intel_engine_cs *engine)
+intel_engine_using_cmd_parser(const struct intel_engine_cs *engine)
{
-       return engine->flags & I915_ENGINE_NEEDS_CMD_PARSER;
+       return engine->flags & I915_ENGINE_USING_CMD_PARSER;
+}
+
+static inline bool
+intel_engine_requires_cmd_parser(const struct intel_engine_cs *engine)
+{
+       return engine->flags & I915_ENGINE_REQUIRES_CMD_PARSER;
}

static inline bool