drm/i915: Add gen9 BCS cmdparsing
authorJon Bloomfield <jon.bloomfield@intel.com>
Mon, 23 Apr 2018 18:12:15 +0000 (11:12 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 12 Nov 2019 18:13:35 +0000 (19:13 +0100)
commit 0f2f39758341df70202ae1c42d5a1e4ee392b6d3 upstream.

For gen9 we enable cmdparsing on the BCS ring, specifically
to catch inadvertent accesses to sensitive registers

Unlike gen7/hsw, we use the parser only to block certain
registers. We can rely on h/w to block restricted commands,
so the command tables only provide enough info to allow the
parser to delineate each command, and identify commands that
access registers.

Note: This patch deliberately ignores checkpatch issues in
favour of matching the style of the surrounding code. We'll
correct the entire file in one go in a later patch.

v3: rebase (Mika)
v4: Add RING_TIMESTAMP registers to whitelist (Jon)

Signed-off-by: Jon Bloomfield <jon.bloomfield@intel.com>
Cc: Tony Luck <tony.luck@intel.com>
Cc: Dave Airlie <airlied@redhat.com>
Cc: Takashi Iwai <tiwai@suse.de>
Cc: Tyler Hicks <tyhicks@canonical.com>
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
Reviewed-by: Chris Wilson <chris.p.wilson@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/gpu/drm/i915/i915_cmd_parser.c
drivers/gpu/drm/i915/i915_dma.c
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/i915_gem_gtt.c
drivers/gpu/drm/i915/i915_reg.h

index bd11913518d48a81822ba5a777ca39db75bf34c9..d37c6d272a2edbdd47a5c86ae692800d6305d593 100644 (file)
@@ -346,6 +346,47 @@ static const struct drm_i915_cmd_descriptor hsw_blt_cmds[] = {
        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 }    ),
+};
+
 #undef CMD
 #undef SMI
 #undef S3D
@@ -389,6 +430,11 @@ static const struct drm_i915_cmd_table hsw_blt_ring_cmd_table[] = {
        { 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.
  */
@@ -422,6 +468,10 @@ struct drm_i915_reg_descriptor {
 #define REG64(addr)                                     \
        REG32(addr), REG32(addr + sizeof(u32))
 
+#define REG64_IDX(_reg, idx) \
+       { .addr = _reg(idx) }, \
+       { .addr = _reg ## _UDW(idx) }
+
 static const struct drm_i915_reg_descriptor gen7_render_regs[] = {
        REG64(GPGPU_THREADS_DISPATCHED),
        REG64(HS_INVOCATION_COUNT),
@@ -475,6 +525,29 @@ static const struct drm_i915_reg_descriptor gen7_blt_regs[] = {
        REG32(BCS_SWCTRL),
 };
 
+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
 
@@ -533,6 +606,17 @@ static u32 gen7_blt_get_cmd_length_mask(u32 cmd_header)
        return 0;
 }
 
+static u32 gen9_blt_get_cmd_length_mask(u32 cmd_header)
+{
+       u32 client = (cmd_header & INSTR_CLIENT_MASK) >> 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(struct intel_engine_cs *ring,
                                 const struct drm_i915_cmd_table *cmd_tables,
                                 int cmd_table_count)
@@ -672,7 +756,7 @@ int i915_cmd_parser_init_ring(struct intel_engine_cs *ring)
        int cmd_table_count;
        int ret;
 
-       if (!IS_GEN7(ring->dev))
+       if (!IS_GEN7(ring->dev) && !(IS_GEN9(ring->dev) && ring->id == BCS))
                return 0;
 
        switch (ring->id) {
@@ -697,7 +781,17 @@ int i915_cmd_parser_init_ring(struct intel_engine_cs *ring)
                ring->get_cmd_length_mask = gen7_bsd_get_cmd_length_mask;
                break;
        case BCS:
-               if (IS_HASWELL(ring->dev)) {
+               ring->get_cmd_length_mask = gen7_blt_get_cmd_length_mask;
+               if (IS_GEN9(ring->dev)) {
+                       cmd_tables = gen9_blt_cmd_table;
+                       cmd_table_count = ARRAY_SIZE(gen9_blt_cmd_table);
+                       ring->get_cmd_length_mask =
+                               gen9_blt_get_cmd_length_mask;
+
+                       /* BCS Engine unsafe without parser */
+                       ring->requires_cmd_parser = 1;
+               }
+               else if (IS_HASWELL(ring->dev)) {
                        cmd_tables = hsw_blt_ring_cmd_table;
                        cmd_table_count = ARRAY_SIZE(hsw_blt_ring_cmd_table);
                } else {
@@ -705,10 +799,14 @@ int i915_cmd_parser_init_ring(struct intel_engine_cs *ring)
                        cmd_table_count = ARRAY_SIZE(gen7_blt_cmd_table);
                }
 
-               ring->reg_table = gen7_blt_regs;
-               ring->reg_count = ARRAY_SIZE(gen7_blt_regs);
+               if (IS_GEN9(ring->dev)) {
+                       ring->reg_table = gen9_blt_regs;
+                       ring->reg_count = ARRAY_SIZE(gen9_blt_regs);
+               } else {
+                       ring->reg_table = gen7_blt_regs;
+                       ring->reg_count = ARRAY_SIZE(gen7_blt_regs);
+               }
 
-               ring->get_cmd_length_mask = gen7_blt_get_cmd_length_mask;
                break;
        case VECS:
                cmd_tables = hsw_vebox_cmd_table;
@@ -1082,9 +1180,9 @@ int i915_parse_cmds(struct intel_engine_cs *ring,
                }
 
                /*
-                * 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;
@@ -1106,7 +1204,7 @@ int i915_parse_cmds(struct intel_engine_cs *ring,
                }
 
                if (!check_cmd(ring, desc, cmd, length, &oacontrol_set)) {
-                       ret = -EINVAL;
+                       ret = CMDPARSER_USES_GGTT(ring->dev) ? -EINVAL : -EACCES;
                        break;
                }
 
@@ -1136,7 +1234,7 @@ int i915_parse_cmds(struct intel_engine_cs *ring,
  *
  * Return: the current version number of the cmd parser
  */
-int i915_cmd_parser_get_version(void)
+int i915_cmd_parser_get_version(struct drm_i915_private *dev_priv)
 {
        /*
         * Command parser version history
@@ -1148,6 +1246,7 @@ int i915_cmd_parser_get_version(void)
         * 3. Allow access to the GPGPU_THREADS_DISPATCHED register.
         * 4. L3 atomic chicken bits of HSW_SCRATCH1 and HSW_ROW_CHICKEN3.
         * 5. GPGPU dispatch compute indirect registers.
+        * 10. Gen9 only - Supports the new ppgtt based BLIT parser
         */
-       return 5;
+       return CMDPARSER_USES_GGTT(dev_priv) ? 5 : 10;
 }
index 816ece568ffcc4e4700a09a0ccb31d44fd3f73de..7b61078c2330c9be3cbc3ae8ea30ac1a9a5fa921 100644 (file)
@@ -145,7 +145,7 @@ static int i915_getparam(struct drm_device *dev, void *data,
                value = 1;
                break;
        case I915_PARAM_CMD_PARSER_VERSION:
-               value = i915_cmd_parser_get_version();
+               value = i915_cmd_parser_get_version(dev_priv);
                break;
        case I915_PARAM_HAS_COHERENT_PHYS_GTT:
                value = 1;
index 91ab878bd32dce5ee2971a8c5c0e0fb276e7bc47..6064ca4aaaef06119f7faa54978a616d0fb55cdd 100644 (file)
@@ -3285,7 +3285,7 @@ void i915_get_extra_instdone(struct drm_device *dev, uint32_t *instdone);
 const char *i915_cache_level_str(struct drm_i915_private *i915, int type);
 
 /* i915_cmd_parser.c */
-int i915_cmd_parser_get_version(void);
+int i915_cmd_parser_get_version(struct drm_i915_private *dev_priv);
 int i915_cmd_parser_init_ring(struct intel_engine_cs *ring);
 void i915_cmd_parser_fini_ring(struct intel_engine_cs *ring);
 bool i915_needs_cmd_parser(struct intel_engine_cs *ring);
index 974c309a93920bb3bc54d1ec58307de5a5a6cf3d..65a53ee398b8e7b1373ee10bd165f4e14456f538 100644 (file)
@@ -119,7 +119,8 @@ static int sanitize_enable_ppgtt(struct drm_device *dev, int enable_ppgtt)
            (enable_ppgtt == 0 || !has_aliasing_ppgtt))
                return 0;
 
-       if (enable_ppgtt == 1)
+       /* Full PPGTT is required by the Gen9 cmdparser */
+       if (enable_ppgtt == 1 && INTEL_INFO(dev)->gen != 9)
                return 1;
 
        if (enable_ppgtt == 2 && has_full_ppgtt)
index cace154bbdc0716b192a0268de600c8af1d15e32..e45e68c3dea257b33775771e904dfc9bad4faa79 100644 (file)
  */
 #define BCS_SWCTRL 0x22200
 
+/* There are 16 GPR registers */
+#define BCS_GPR(n)     (0x22600 + (n) * 8)
+#define BCS_GPR_UDW(n) (0x22600 + (n) * 8 + 4)
+
 #define GPGPU_THREADS_DISPATCHED        0x2290
 #define HS_INVOCATION_COUNT             0x2300
 #define DS_INVOCATION_COUNT             0x2308
@@ -1567,6 +1571,7 @@ enum skl_disp_power_wells {
 #define RING_IMR(base)         ((base)+0xa8)
 #define RING_HWSTAM(base)      ((base)+0x98)
 #define RING_TIMESTAMP(base)   ((base)+0x358)
+#define RING_TIMESTAMP_UDW(base) ((base) + 0x358 + 4)
 #define   TAIL_ADDR            0x001FFFF8
 #define   HEAD_WRAP_COUNT      0xFFE00000
 #define   HEAD_WRAP_ONE                0x00200000