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 }    ),
+};
+
 static const struct drm_i915_cmd_descriptor noop_desc =
        CMD(MI_NOOP, SMI, F, 1, S);
 
        { 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.
  */
        REG64_IDX(RING_TIMESTAMP, BLT_RING_BASE),
 };
 
+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
 #undef REG32
 
        { 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)
 {
        u32 client = cmd_header >> INSTR_CLIENT_SHIFT;
        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)
        int cmd_table_count;
        int ret;
 
-       if (!IS_GEN(engine->i915, 7))
+       if (!IS_GEN(engine->i915, 7) && !(IS_GEN(engine->i915, 9) &&
+                                         engine->class == COPY_ENGINE_CLASS))
                return;
 
        switch (engine->class) {
                        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 VIDEO_DECODE_CLASS:
                engine->get_cmd_length_mask = gen7_bsd_get_cmd_length_mask;
                break;
        case COPY_ENGINE_CLASS:
-               if (IS_HASWELL(engine->i915)) {
+               engine->get_cmd_length_mask = gen7_blt_get_cmd_length_mask;
+               if (IS_GEN(engine->i915, 9)) {
+                       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_table_count = ARRAY_SIZE(gen7_blt_cmd_table);
                }
 
-               if (IS_HASWELL(engine->i915)) {
+               if (IS_GEN(engine->i915, 9)) {
+                       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 VIDEO_ENHANCEMENT_CLASS:
                cmd_tables = hsw_vebox_cmd_table;
                }
 
                /*
-                * 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.
+                * We don't try to handle BATCH_BUFFER_START because it adds
+                * non-trivial complexity. Instead we abort the scan and return
+                * and error to indicate that the batch is unsafe.
                 */
                if (desc->cmd.value == MI_BATCH_BUFFER_START) {
                        ret = -EACCES;
         *    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;
 }