[PATCH v2 10/19] accel: ethosu: Validate all feature map tiles
From: Rob Herring (Arm)
Date: Fri Sep 04 2026 - 20:47:26 EST
The command-stream validator checked only the final feature-map
coordinate. For tiled tensors, this can leave an earlier tile base
address unchecked even though the operation accesses it.
Check the final coordinate of every tile touched by an operation. Also
treat U65 feature maps as 2x2 tiled: its precision rounding bits are not
the U85 storage encoding.
Fixes: 5a5e9c0228e6 ("accel: Add Arm Ethos-U NPU driver")
Cc: stable@xxxxxxxxxxxxxxx
Assisted-by: LLM
Signed-off-by: Rob Herring (Arm) <robh@xxxxxxxxxx>
---
v2:
- no changes
---
drivers/accel/ethosu/ethosu_gem.c | 125 +++++++++++++++++++++++++++++---------
1 file changed, 97 insertions(+), 28 deletions(-)
diff --git a/drivers/accel/ethosu/ethosu_gem.c b/drivers/accel/ethosu/ethosu_gem.c
index c913c95e48ae..c54496fa08f4 100644
--- a/drivers/accel/ethosu/ethosu_gem.c
+++ b/drivers/accel/ethosu/ethosu_gem.c
@@ -423,6 +423,74 @@ static u64 feat_matrix_length(struct ethosu_device *edev,
return addr;
}
+static int feat_matrix_check_location(struct ethosu_device *edev,
+ struct ethosu_validated_cmdstream_info *info,
+ struct cmd_state *st, struct feat_matrix *fm,
+ enum feat_matrix_type type, u32 x, u32 y,
+ u32 c, bool ofm, u64 *max_len)
+{
+ u64 len;
+
+ len = feat_matrix_length(edev, info, st, fm, type, x, y, c, ofm);
+ if (len == U64_MAX)
+ return -EINVAL;
+
+ *max_len = max(*max_len, len);
+ return 0;
+}
+
+static int feat_matrix_size(struct ethosu_device *edev,
+ struct ethosu_validated_cmdstream_info *info,
+ struct cmd_state *st, struct feat_matrix *fm,
+ enum feat_matrix_type type,
+ u32 x, u32 y, u32 c, bool ofm, u64 *max_len)
+{
+ u32 storage = ethosu_is_u65(edev) ? 0 : fm->precision >> 14;
+ int ret;
+
+ *max_len = 0;
+
+ if (ethosu_is_u65(edev) || storage == 0) {
+ for (int xi = 0; xi < 2; xi++) {
+ for (int yi = 0; yi < 2; yi++) {
+ ret = feat_matrix_check_location(edev, info, st, fm, type,
+ xi ? x : 0,
+ yi ? y : 0, c, ofm,
+ max_len);
+ if (ret)
+ return ret;
+ }
+ }
+ return 0;
+ }
+
+ if (storage == 1) {
+ ret = feat_matrix_check_location(edev, info, st, fm, type, x, 0, c,
+ ofm, max_len);
+ if (ret)
+ return ret;
+ if (fm->height[0] < fm->height[1] && fm->height[1] <= y) {
+ ret = feat_matrix_check_location(edev, info, st, fm, type, x,
+ fm->height[1], c, ofm,
+ max_len);
+ if (ret)
+ return ret;
+ }
+ if (fm->height[1] < y) {
+ ret = feat_matrix_check_location(edev, info, st, fm, type, x,
+ fm->height[1] + 1, c, ofm,
+ max_len);
+ if (ret)
+ return ret;
+ }
+ return feat_matrix_check_location(edev, info, st, fm, type, x, y, c,
+ ofm, max_len);
+ }
+
+ return feat_matrix_check_location(edev, info, st, fm, type, x, y, c, ofm,
+ max_len);
+}
+
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)
@@ -453,6 +521,7 @@ static int calc_sizes(struct drm_device *ddev,
{
struct ethosu_device *edev = to_ethosu_device(ddev);
u64 len;
+ int ret;
if (ifm) {
if (!cmd_state_reg_is_set(st, NPU_SET_KERNEL_WIDTH_M1) ||
@@ -475,23 +544,22 @@ static int calc_sizes(struct drm_device *ddev,
if (ifm_height < 0 || ifm_width < 0)
return -EINVAL;
- len = feat_matrix_length(edev, info, st, &st->ifm,
- FEAT_MATRIX_IFM, ifm_width, ifm_height,
- st->ifm.depth, false);
+ ret = feat_matrix_size(edev, info, st, &st->ifm, FEAT_MATRIX_IFM,
+ ifm_width, ifm_height, st->ifm.depth, false,
+ &len);
dev_dbg(ddev->dev, "op %d: IFM:%d:0x%llx-0x%llx\n",
op, st->ifm.region, st->ifm.base[0], len);
- if (len == U64_MAX)
- return -EINVAL;
+ if (ret)
+ return ret;
}
if (ifm2) {
- len = feat_matrix_length(edev, info, st, &st->ifm2,
- FEAT_MATRIX_IFM2, st->ifm.depth, 0,
- st->ofm.depth, false);
+ ret = feat_matrix_size(edev, info, st, &st->ifm2, FEAT_MATRIX_IFM2,
+ st->ifm.depth, 0, st->ofm.depth, false, &len);
dev_dbg(ddev->dev, "op %d: IFM2:%d:0x%llx-0x%llx\n",
op, st->ifm2.region, st->ifm2.base[0], len);
- if (len == U64_MAX)
- return -EINVAL;
+ if (ret)
+ return ret;
}
if (weight) {
@@ -533,13 +601,13 @@ static int calc_sizes(struct drm_device *ddev,
return -EINVAL;
}
- len = feat_matrix_length(edev, info, st, &st->ofm, FEAT_MATRIX_OFM,
- st->ofm.width, st->ofm.height[2], st->ofm.depth,
- true);
+ ret = feat_matrix_size(edev, info, st, &st->ofm, FEAT_MATRIX_OFM,
+ st->ofm.width, st->ofm.height[2], st->ofm.depth,
+ true, &len);
dev_dbg(ddev->dev, "op %d: OFM:%d:0x%llx-0x%llx\n",
op, st->ofm.region, st->ofm.base[0], len);
- if (len == U64_MAX)
- return -EINVAL;
+ if (ret)
+ return ret;
if (!feat_matrix_chained(edev, &st->ofm))
info->output_region[st->ofm.region] = true;
@@ -554,18 +622,19 @@ static int calc_sizes_elemwise(struct drm_device *ddev,
struct ethosu_device *edev = to_ethosu_device(ddev);
u32 height, width, depth;
u64 len;
+ int ret;
if (ifm) {
height = st->ifm.broadcast & 0x1 ? 0 : st->ofm.height[2];
width = st->ifm.broadcast & 0x2 ? 0 : st->ofm.width;
depth = st->ifm.broadcast & 0x4 ? 0 : st->ofm.depth;
- len = feat_matrix_length(edev, info, st, &st->ifm,
- FEAT_MATRIX_IFM, width, height, depth, false);
+ ret = feat_matrix_size(edev, info, st, &st->ifm, FEAT_MATRIX_IFM,
+ width, height, depth, false, &len);
dev_dbg(ddev->dev, "op %d: IFM:%d:0x%llx-0x%llx\n",
op, st->ifm.region, st->ifm.base[0], len);
- if (len == U64_MAX)
- return -EINVAL;
+ if (ret)
+ return ret;
}
if (ifm2) {
@@ -573,21 +642,21 @@ static int calc_sizes_elemwise(struct drm_device *ddev,
width = st->ifm2.broadcast & 0x2 ? 0 : st->ofm.width;
depth = st->ifm2.broadcast & 0x4 ? 0 : st->ofm.depth;
- len = feat_matrix_length(edev, info, st, &st->ifm2,
- FEAT_MATRIX_IFM2, width, height, depth, false);
+ ret = feat_matrix_size(edev, info, st, &st->ifm2, FEAT_MATRIX_IFM2,
+ width, height, depth, false, &len);
dev_dbg(ddev->dev, "op %d: IFM2:%d:0x%llx-0x%llx\n",
op, st->ifm2.region, st->ifm2.base[0], len);
- if (len == U64_MAX)
- return -EINVAL;
+ if (ret)
+ return ret;
}
- len = feat_matrix_length(edev, info, st, &st->ofm, FEAT_MATRIX_OFM,
- st->ofm.width, st->ofm.height[2], st->ofm.depth,
- true);
+ ret = feat_matrix_size(edev, info, st, &st->ofm, FEAT_MATRIX_OFM,
+ st->ofm.width, st->ofm.height[2], st->ofm.depth,
+ true, &len);
dev_dbg(ddev->dev, "op %d: OFM:%d:0x%llx-0x%llx\n",
op, st->ofm.region, st->ofm.base[0], len);
- if (len == U64_MAX)
- return -EINVAL;
+ if (ret)
+ return ret;
if (!feat_matrix_chained(edev, &st->ofm))
info->output_region[st->ofm.region] = true;
--
2.53.0