A secondary weight or scale stream must be explicitly disabled or
configured when the hardware uses it. Otherwise, stale stream state from
a previous command stream can bypass buffer bounds validation.

Derive the active stream count from the NPU configuration on U85 and
from PARALLEL_MODE on U65. Require every active stream's length command,
while allowing a zero length to disable it.

Fixes: 5a5e9c0228e6 ("accel: Add Arm Ethos-U NPU driver")
Cc: [email protected]
Signed-off-by: Rob Herring (Arm) <[email protected]>
---
v4:
 - New patch
---
 drivers/accel/ethosu/ethosu_device.h |  4 +++
 drivers/accel/ethosu/ethosu_gem.c    | 60 +++++++++++++++++++++++++++++++-----
 2 files changed, 57 insertions(+), 7 deletions(-)

diff --git a/drivers/accel/ethosu/ethosu_device.h 
b/drivers/accel/ethosu/ethosu_device.h
index 135c33f57045..f41a8e0e1e1b 100644
--- a/drivers/accel/ethosu/ethosu_device.h
+++ b/drivers/accel/ethosu/ethosu_device.h
@@ -63,6 +63,7 @@ struct gen_pool;
 
 #define CONFIG_MACS_PER_CC_MASK        GENMASK(3, 0)
 #define CONFIG_CMD_STREAM_VER_MASK     GENMASK(7, 4)
+#define CONFIG_NUM_WD_MASK             GENMASK(13, 12)
 
 #define STATUS_STATE_RUNNING   BIT(0)
 #define STATUS_IRQ_RAISED      BIT(1)
@@ -130,6 +131,7 @@ enum ethosu_cmds {
        NPU_SET_KERNEL_WIDTH_M1 = 0x120,
        NPU_SET_KERNEL_HEIGHT_M1 = 0x121,
        NPU_SET_KERNEL_STRIDE = 0x122,
+       NPU_SET_PARALLEL_MODE = 0x123,  // U65 only
        NPU_SET_ACC_FORMAT = 0x124,
        NPU_SET_WEIGHT_REGION = 0x128,
        NPU_SET_SCALE_REGION = 0x129,
@@ -193,6 +195,8 @@ enum ethosu_cmds {
 
 #define NPU_ACC_FORMAT_INPUT_MASK      GENMASK(5, 4)
 #define NPU_ACC_INPUT_IFM2             2
+#define NPU_MACS_PER_CC_256            8
+#define NPU_PARALLEL_MODE_DUAL_CORE_DEPTH      1
 
 #define ETHOSU_SRAM_REGION     2       /* Matching Vela compiler */
 
diff --git a/drivers/accel/ethosu/ethosu_gem.c 
b/drivers/accel/ethosu/ethosu_gem.c
index 1935440fb815..21458aacfcad 100644
--- a/drivers/accel/ethosu/ethosu_gem.c
+++ b/drivers/accel/ethosu/ethosu_gem.c
@@ -165,6 +165,7 @@ struct cmd_state {
        DECLARE_BITMAP(cmd0, NPU_CMD0_REGS);
        DECLARE_BITMAP(cmd1, NPU_CMD1_REGS);
        bool acc_input_ifm2;
+       u8 parallel_mode;
        struct dma_state dma;
        struct buffer scale[2];
        struct buffer weight[4];
@@ -767,19 +768,45 @@ static int calc_sizes_resize(struct drm_device *ddev,
        return 0;
 }
 
+static int weight_streams(struct ethosu_device *edev, struct cmd_state *st,
+                         u8 *streams)
+{
+       if (ethosu_is_u65(edev)) {
+               if (FIELD_GET(CONFIG_MACS_PER_CC_MASK, edev->npu_info.config) ==
+                   NPU_MACS_PER_CC_256) {
+                       *streams = 1;
+                       return 0;
+               }
+
+               if (!cmd_state_reg_is_set(st, NPU_SET_PARALLEL_MODE))
+                       return -EINVAL;
+
+               *streams = st->parallel_mode + 1;
+       } else {
+               *streams = 1 << FIELD_GET(CONFIG_NUM_WD_MASK,
+                                          edev->npu_info.config);
+       }
+
+       return 0;
+}
+
 static int buffer_size(struct ethosu_validated_cmdstream_info *info,
                       struct cmd_state *st, struct buffer *buf, s8 region,
-                      u16 region_cmd, u16 base_cmd, u16 length_cmd, bool 
optional)
+                      u16 region_cmd, u16 base_cmd, u16 length_cmd, bool 
can_omit)
 {
        u64 end;
        bool base_set = cmd_state_reg_is_set(st, base_cmd);
        bool length_set = cmd_state_reg_is_set(st, length_cmd);
 
-       if (optional && !base_set && !length_set)
+       if (can_omit && !length_set)
                return 0;
 
        if (region < 0 || !cmd_state_reg_is_set(st, region_cmd) ||
-           !base_set || !length_set)
+           !length_set)
+               return -EINVAL;
+       if (!buf->length)
+               return 0;
+       if (!base_set)
                return -EINVAL;
 
        if (check_add_overflow(buf->base, (u64)buf->length, &end))
@@ -797,6 +824,7 @@ static int calc_sizes(struct drm_device *ddev,
 {
        struct ethosu_device *edev = to_ethosu_device(ddev);
        u64 len;
+       u8 streams;
        int ret;
 
        if (ifm) {
@@ -848,6 +876,10 @@ static int calc_sizes(struct drm_device *ddev,
        }
 
        if (weight) {
+               ret = weight_streams(edev, st, &streams);
+               if (ret)
+                       return ret;
+
                dev_dbg(ddev->dev, "op %d: W:%d:0x%llx-0x%llx\n",
                        op, st->weight[0].region, st->weight[0].base,
                        st->weight[0].base + st->weight[0].length - 1);
@@ -858,19 +890,23 @@ static int calc_sizes(struct drm_device *ddev,
 
                if (buffer_size(info, st, &st->weight[1], st->weight[0].region,
                                NPU_SET_WEIGHT_REGION, NPU_SET_WEIGHT1_BASE,
-                               NPU_SET_WEIGHT1_LENGTH, true) ||
+                               NPU_SET_WEIGHT1_LENGTH, streams < 2) ||
                    buffer_size(info, st, &st->weight[3], st->weight[0].region,
                                NPU_SET_WEIGHT_REGION, NPU_SET_WEIGHT3_BASE,
-                               NPU_SET_WEIGHT3_LENGTH, true))
+                               NPU_SET_WEIGHT3_LENGTH, streams < 4))
                        return -EINVAL;
                if (!ethosu_is_u65(edev) &&
                    buffer_size(info, st, &st->weight[2], st->weight[0].region,
                                NPU_SET_WEIGHT_REGION, NPU_SET_WEIGHT2_BASE,
-                               NPU_SET_WEIGHT2_LENGTH, true))
+                               NPU_SET_WEIGHT2_LENGTH, streams < 3))
                        return -EINVAL;
        }
 
        if (scale) {
+               ret = weight_streams(edev, st, &streams);
+               if (ret)
+                       return ret;
+
                dev_dbg(ddev->dev, "op %d: S:%d:0x%llx-0x%llx\n",
                        op, st->scale[0].region, st->scale[0].base,
                        st->scale[0].base + st->scale[0].length - 1);
@@ -882,7 +918,7 @@ static int calc_sizes(struct drm_device *ddev,
                if (ethosu_is_u65(edev) &&
                    buffer_size(info, st, &st->scale[1], st->scale[0].region,
                                NPU_SET_SCALE_REGION, NPU_SET_SCALE1_BASE,
-                               NPU_SET_SCALE1_LENGTH, true))
+                               NPU_SET_SCALE1_LENGTH, streams < 2))
                        return -EINVAL;
        }
 
@@ -1093,6 +1129,16 @@ static int ethosu_gem_cmdstream_copy_and_validate(struct 
drm_device *ddev,
                case NPU_SET_KERNEL_STRIDE:
                        st.ifm.stride_kernel = param;
                        break;
+               case NPU_SET_PARALLEL_MODE:
+                       if (!ethosu_is_u65(edev) ||
+                           param > NPU_PARALLEL_MODE_DUAL_CORE_DEPTH)
+                               return -EINVAL;
+                       if (FIELD_GET(CONFIG_MACS_PER_CC_MASK, 
edev->npu_info.config) ==
+                           NPU_MACS_PER_CC_256 &&
+                           param)
+                               return -EINVAL;
+                       st.parallel_mode = param;
+                       break;
                case NPU_SET_ACC_FORMAT:
                        if (!ethosu_is_u65(edev)) {
                                u32 acc_input = 
FIELD_GET(NPU_ACC_FORMAT_INPUT_MASK, param);

-- 
2.53.0

Reply via email to