Skip to contentSkip to conversation

accel: ethosu: Fix element size accounting for cmd stream validation

PATCH In review Rob Herring (Arm) 3 participants 5 messages Jul 20 – Jul 21

Subsystems (2)
lore
Share & tools
lore

Rob Herring (Arm)

author maintainer
#0 lore raw

PATCH 1/2 accel: ethosu: Fix element size accounting for cmd stream validation

There are 2 issues with the element size handling in the command stream validation which result in too small of a size calculated when the element size is 16/32/64 bits.

For NHWC format, the element size is simply missing from the calculation.

The bitfield for the element size is different between IFM/IFM2 and OFM. IFM and IFM2 encode the precision in parameter bits 2:3, while OFM uses bits 1:2.

Fixes 5a5e9c0228e6 ("accel: Add Arm Ethos-U NPU driver")
---
 drivers/accel/ethosu/ethosu_gem.c | 19 ++++++++++---------
 1 file changed, 10 insertions(+), 9 deletions(-)
drivers/accel/ethosu/ethosu_gem.c +10 -9
index 3401883e207f..1daff32610c7 100644
@@ -194,7 +194,7 @@ static u64 dma_length(struct ethosu_validated_cmdstream_info *info,
 
 static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info,
 			      struct feat_matrix *fm,
-			      u32 x, u32 y, u32 c)
+			      u32 x, u32 y, u32 c, bool ofm)
 {
 	u32 element_size, storage = fm->precision >> 14;
 	int tile = 0;
@@ -231,10 +231,11 @@ static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info,
 
 	switch ((fm->precision >> 6) & 0x3) { // format
 	case 0: //nhwc:
-		addr += x * fm->stride_x + c;
+		element_size = BIT((fm->precision >> (ofm ? 1 : 2)) & 0x3);
+		addr += x * fm->stride_x + c * element_size;
 		break;
 	case 1: //nhcwb16:
-		element_size = BIT((fm->precision >> 1) & 0x3);
+		element_size = BIT((fm->precision >> (ofm ? 1 : 2)) & 0x3);
 
 		addr += (c / 16) * fm->stride_c + (16 * x + (c & 0xf)) * element_size;
 		break;
@@ -268,7 +269,7 @@ static int calc_sizes(struct drm_device *ddev,
 			return -EINVAL;
 
 		len = feat_matrix_length(info, &st->ifm, ifm_width,
-					 ifm_height, st->ifm.depth);
+					 ifm_height, st->ifm.depth, false);
 		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)
@@ -277,7 +278,7 @@ static int calc_sizes(struct drm_device *ddev,
 
 	if (ifm2) {
 		len = feat_matrix_length(info, &st->ifm2, st->ifm.depth,
-					 0, st->ofm.depth);
+					 0, st->ofm.depth, false);
 		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)
@@ -309,7 +310,7 @@ static int calc_sizes(struct drm_device *ddev,
 	}
 
 	len = feat_matrix_length(info, &st->ofm, st->ofm.width,
-				 st->ofm.height[2], st->ofm.depth);
+				 st->ofm.height[2], st->ofm.depth, true);
 	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)
@@ -333,7 +334,7 @@ static int calc_sizes_elemwise(struct drm_device *ddev,
 		depth = st->ifm.broadcast & 0x4 ? 0 : st->ofm.depth;
 
 		len = feat_matrix_length(info, &st->ifm, width,
-					 height, depth);
+					 height, depth, false);
 		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)
@@ -346,7 +347,7 @@ static int calc_sizes_elemwise(struct drm_device *ddev,
 		depth = st->ifm2.broadcast & 0x4 ? 0 : st->ofm.depth;
 
 		len = feat_matrix_length(info, &st->ifm2, width,
-					 height, depth);
+					 height, depth, false);
 		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)
@@ -354,7 +355,7 @@ static int calc_sizes_elemwise(struct drm_device *ddev,
 	}
 
 	len = feat_matrix_length(info, &st->ofm, st->ofm.width,
-				 st->ofm.height[2], st->ofm.depth);
+				 st->ofm.height[2], st->ofm.depth, true);
 	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)
Signature · 1 line
2.53.0
author maintainer
#1 lore raw

PATCH 2/2 accel: ethosu: Handle U85 internal chaining buffer

The Ethos-U85 supports an internal chaining buffer as temporary storage between some operations. When chaining is activated, the IFM/OFM region setting selects a chaining buffer rather than a region, and the IFM/OFM base addresses don't matter. In this case, the feature matrix size calculations should be skipped. Otherwise, the command stream will be intermittently rejected depending on prior feature matrix base addresses.

Fixes 5a5e9c0228e6 ("accel: Add Arm Ethos-U NPU driver")
---
 drivers/accel/ethosu/ethosu_gem.c | 35 +++++++++++++++++++++++--------
 1 file changed, 26 insertions(+), 9 deletions(-)
drivers/accel/ethosu/ethosu_gem.c +26 -9
index 1daff32610c7..d50fed64d4d9 100644
@@ -192,7 +192,15 @@ static u64 dma_length(struct ethosu_validated_cmdstream_info *info,
 	return len;
 }
 
-static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info,
+static bool feat_matrix_chained(struct ethosu_device *edev, struct feat_matrix *fm)
+{
+	u32 storage = fm->precision >> 14;
+
+	return !ethosu_is_u65(edev) && storage == 2;
+}
+
+static u64 feat_matrix_length(struct ethosu_device *edev,
+			      struct ethosu_validated_cmdstream_info *info,
 			      struct feat_matrix *fm,
 			      u32 x, u32 y, u32 c, bool ofm)
 {
@@ -203,6 +211,9 @@ static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info,
 	if (fm->region < 0)
 		return U64_MAX;
 
+	if (feat_matrix_chained(edev, fm))
+		return 0;
+
 	switch (storage) {
 	case 0:
 		if (x >= fm->width0 + 1) {
@@ -223,6 +234,8 @@ static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info,
 			tile = 1;
 		}
 		break;
+	default:
+		return U64_MAX;
 	}
 	if (fm->base[tile] == U64_MAX)
 		return U64_MAX;
@@ -251,6 +264,7 @@ static int calc_sizes(struct drm_device *ddev,
 		      u16 op, struct cmd_state *st,
 		      bool ifm, bool ifm2, bool weight, bool scale)
 {
+	struct ethosu_device *edev = to_ethosu_device(ddev);
 	u64 len;
 
 	if (ifm) {
@@ -268,7 +282,7 @@ static int calc_sizes(struct drm_device *ddev,
 		if (ifm_height < 0 || ifm_width < 0)
 			return -EINVAL;
 
-		len = feat_matrix_length(info, &st->ifm, ifm_width,
+		len = feat_matrix_length(edev, info, &st->ifm, ifm_width,
 					 ifm_height, st->ifm.depth, false);
 		dev_dbg(ddev->dev, "op %d: IFM:%d:0x%llx-0x%llx\n",
 			op, st->ifm.region, st->ifm.base[0], len);
@@ -277,7 +291,7 @@ static int calc_sizes(struct drm_device *ddev,
 	}
 
 	if (ifm2) {
-		len = feat_matrix_length(info, &st->ifm2, st->ifm.depth,
+		len = feat_matrix_length(edev, info, &st->ifm2, st->ifm.depth,
 					 0, st->ofm.depth, false);
 		dev_dbg(ddev->dev, "op %d: IFM2:%d:0x%llx-0x%llx\n",
 			op, st->ifm2.region, st->ifm2.base[0], len);
@@ -309,13 +323,14 @@ static int calc_sizes(struct drm_device *ddev,
 			    st->scale[0].base + st->scale[0].length);
 	}
 
-	len = feat_matrix_length(info, &st->ofm, st->ofm.width,
+	len = feat_matrix_length(edev, info, &st->ofm, st->ofm.width,
 				 st->ofm.height[2], st->ofm.depth, true);
 	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;
-	info->output_region[st->ofm.region] = true;
+	if (!feat_matrix_chained(edev, &st->ofm))
+		info->output_region[st->ofm.region] = true;
 
 	return 0;
 }
@@ -325,6 +340,7 @@ static int calc_sizes_elemwise(struct drm_device *ddev,
 			       u16 op, struct cmd_state *st,
 			       bool ifm, bool ifm2)
 {
+	struct ethosu_device *edev = to_ethosu_device(ddev);
 	u32 height, width, depth;
 	u64 len;
 
@@ -333,7 +349,7 @@ static int calc_sizes_elemwise(struct drm_device *ddev,
 		width = st->ifm.broadcast & 0x2 ? 0 : st->ofm.width;
 		depth = st->ifm.broadcast & 0x4 ? 0 : st->ofm.depth;
 
-		len = feat_matrix_length(info, &st->ifm, width,
+		len = feat_matrix_length(edev, info, &st->ifm, width,
 					 height, depth, false);
 		dev_dbg(ddev->dev, "op %d: IFM:%d:0x%llx-0x%llx\n",
 			op, st->ifm.region, st->ifm.base[0], len);
@@ -346,7 +362,7 @@ 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(info, &st->ifm2, width,
+		len = feat_matrix_length(edev, info, &st->ifm2, width,
 					 height, depth, false);
 		dev_dbg(ddev->dev, "op %d: IFM2:%d:0x%llx-0x%llx\n",
 			op, st->ifm2.region, st->ifm2.base[0], len);
@@ -354,13 +370,14 @@ static int calc_sizes_elemwise(struct drm_device *ddev,
 			return -EINVAL;
 	}
 
-	len = feat_matrix_length(info, &st->ofm, st->ofm.width,
+	len = feat_matrix_length(edev, info, &st->ofm, st->ofm.width,
 				 st->ofm.height[2], st->ofm.depth, true);
 	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;
-	info->output_region[st->ofm.region] = true;
+	if (!feat_matrix_chained(edev, &st->ofm))
+		info->output_region[st->ofm.region] = true;
 
 	return 0;
 }
Signature · 1 line
2.53.0
maintainer Acked-by on v1 2/2
#2 lore raw

Re: [PATCH 2/2] accel: ethosu: Handle U85 internal chaining buffer

Quoted 11 lines · Rob Herring (Arm)

The Ethos-U85 supports an internal chaining buffer as temporary storage between some operations. When chaining is activated, the IFM/OFM region setting selects a chaining buffer rather than a region, and the IFM/OFM base addresses don't matter. In this case, the feature matrix size calculations should be skipped. Otherwise, the command stream will be intermittently rejected depending on prior feature matrix base addresses.

Fixes 5a5e9c0228e6 ("accel: Add Arm Ethos-U NPU driver")
Quoted 133 lines
---
 drivers/accel/ethosu/ethosu_gem.c | 35 +++++++++++++++++++++++--------
 1 file changed, 26 insertions(+), 9 deletions(-)
drivers/accel/ethosu/ethosu_gem.c +26 -10
index 1daff32610c7..d50fed64d4d9 100644
@@ -192,7 +192,15 @@ static u64 dma_length(struct ethosu_validated_cmdstream_info *info,
        return len;
 }
 
-static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info,
+static bool feat_matrix_chained(struct ethosu_device *edev, struct feat_matrix *fm)
+{
+       u32 storage = fm->precision >> 14;
+
+       return !ethosu_is_u65(edev) && storage == 2;
+}
+
+static u64 feat_matrix_length(struct ethosu_device *edev,
+                             struct ethosu_validated_cmdstream_info *info,
                              struct feat_matrix *fm,
                              u32 x, u32 y, u32 c, bool ofm)
 {
@@ -203,6 +211,9 @@ static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info,
        if (fm->region < 0)
                return U64_MAX;
 
+       if (feat_matrix_chained(edev, fm))
+               return 0;
+
        switch (storage) {
        case 0:
                if (x >= fm->width0 + 1) {
@@ -223,6 +234,8 @@ static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info,
                        tile = 1;
                }
                break;
+       default:
+               return U64_MAX;
        }
        if (fm->base[tile] == U64_MAX)
                return U64_MAX;
@@ -251,6 +264,7 @@ static int calc_sizes(struct drm_device *ddev,
                      u16 op, struct cmd_state *st,
                      bool ifm, bool ifm2, bool weight, bool scale)
 {
+       struct ethosu_device *edev = to_ethosu_device(ddev);
        u64 len;
 
        if (ifm) {
@@ -268,7 +282,7 @@ static int calc_sizes(struct drm_device *ddev,
                if (ifm_height < 0 || ifm_width < 0)
                        return -EINVAL;
 
-               len = feat_matrix_length(info, &st->ifm, ifm_width,
+               len = feat_matrix_length(edev, info, &st->ifm, ifm_width,
                                         ifm_height, st->ifm.depth, false);
                dev_dbg(ddev->dev, "op %d: IFM:%d:0x%llx-0x%llx\n",
                        op, st->ifm.region, st->ifm.base[0], len);
@@ -277,7 +291,7 @@ static int calc_sizes(struct drm_device *ddev,
        }
 
        if (ifm2) {
-               len = feat_matrix_length(info, &st->ifm2, st->ifm.depth,
+               len = feat_matrix_length(edev, info, &st->ifm2, st->ifm.depth,
                                         0, st->ofm.depth, false);
                dev_dbg(ddev->dev, "op %d: IFM2:%d:0x%llx-0x%llx\n",
                        op, st->ifm2.region, st->ifm2.base[0], len);
@@ -309,13 +323,14 @@ static int calc_sizes(struct drm_device *ddev,
                            st->scale[0].base + st->scale[0].length);
        }
 
-       len = feat_matrix_length(info, &st->ofm, st->ofm.width,
+       len = feat_matrix_length(edev, info, &st->ofm, st->ofm.width,
                                 st->ofm.height[2], st->ofm.depth, true);
        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;
-       info->output_region[st->ofm.region] = true;
+       if (!feat_matrix_chained(edev, &st->ofm))
+               info->output_region[st->ofm.region] = true;
 
        return 0;
 }
@@ -325,6 +340,7 @@ static int calc_sizes_elemwise(struct drm_device *ddev,
                               u16 op, struct cmd_state *st,
                               bool ifm, bool ifm2)
 {
+       struct ethosu_device *edev = to_ethosu_device(ddev);
        u32 height, width, depth;
        u64 len;
 
@@ -333,7 +349,7 @@ static int calc_sizes_elemwise(struct drm_device *ddev,
                width = st->ifm.broadcast & 0x2 ? 0 : st->ofm.width;
                depth = st->ifm.broadcast & 0x4 ? 0 : st->ofm.depth;
 
-               len = feat_matrix_length(info, &st->ifm, width,
+               len = feat_matrix_length(edev, info, &st->ifm, width,
                                         height, depth, false);
                dev_dbg(ddev->dev, "op %d: IFM:%d:0x%llx-0x%llx\n",
                        op, st->ifm.region, st->ifm.base[0], len);
@@ -346,7 +362,7 @@ 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(info, &st->ifm2, width,
+               len = feat_matrix_length(edev, info, &st->ifm2, width,
                                         height, depth, false);
                dev_dbg(ddev->dev, "op %d: IFM2:%d:0x%llx-0x%llx\n",
                        op, st->ifm2.region, st->ifm2.base[0], len);
@@ -354,13 +370,14 @@ static int calc_sizes_elemwise(struct drm_device *ddev,
                        return -EINVAL;
        }
 
-       len = feat_matrix_length(info, &st->ofm, st->ofm.width,
+       len = feat_matrix_length(edev, info, &st->ofm, st->ofm.width,
                                 st->ofm.height[2], st->ofm.depth, true);
        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;
-       info->output_region[st->ofm.region] = true;
+       if (!feat_matrix_chained(edev, &st->ofm))
+               info->output_region[st->ofm.region] = true;
 
        return 0;
 }
--

2.53.0

#3 lore raw
In reply to Rob Herring (Arm)
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

Pre-existing issues:
- [Critical] The command stream validation silently ignores unsupported/unknown formats (values 2 and 3), under-calculating the required buffer size and allowing out-of-bounds memory accesses.
- [Critical] Buffer bounds validation incorrectly evaluates the memory footprint only at the maximum dimension indices, missing active tiles and failing on negative strides or overflows. --

--- Patch [1]: [PATCH 1/2] accel: ethosu: Fix element size accounting for cmd stream validation --- commit 295d16016ba05ed08c0ba3586bc6d83af9ebb17a
Author: Rob Herring (Arm) <[email protected]>

accel: ethosu: Fix element size accounting for cmd stream validation

There are 2 issues with the element size handling in the command stream validation which result in too small of a size calculated when the element size is 16/32/64 bits.

For NHWC format, the element size is simply missing from the calculation.

drivers/accel/ethosu/ethosu_gem.c static u64 dma_length(struct ethosu_validated_cmdstream_info *info,

Quoted 5 lines
drivers/accel/ethosu/ethosu_gem.c +0 -0
index 3401883e207fc..1daff32610c71 100644
@@ -194,7 +194,7 @@ static u64 dma_length(struct ethosu_validated_cmdstream_info *info,

[Severity: Critical]
This is a pre-existing issue, but does the buffer bounds validation in feat_matrix_length() correctly evaluate the memory footprint for all tiles and negative strides?

Looking at the calculation earlier in the function:

	switch (storage) {
	case 0:
		if (x >= fm->width0 + 1) {
			x -= fm->width0 + 1;
			tile += 1;
		}
		if (y >= fm->height[tile] + 1) {
			y -= fm->height[tile] + 1;
			tile += 2;
		}
		break;
	case 1:
		if (y >= fm->height[1] + 1) {
			y -= fm->height[1] + 1;
			tile = 2;
		} else if (y >= fm->height[0] + 1) {
			y -= fm->height[0] + 1;
			tile = 1;
		}
		break;

By evaluating the maximum memory requirement only at the maximum y dimension, the driver seems to fail validating accesses at y=0, which would result in a higher address if stride_y is negative.

Additionally, when a tensor spans multiple tiles (e.g. tile = 2), the validation logic checks only the last tile, entirely skipping bounds checks for the earlier tiles. Could a userspace process bypass validation by setting these unchecked bases to out-of-bounds addresses, leading to out-of-bounds DMA?

Quoted 7 lines
drivers/accel/ethosu/ethosu_gem.c +1 -1
 static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info,
 			      struct feat_matrix *fm,
-			      u32 x, u32 y, u32 c)
+			      u32 x, u32 y, u32 c, bool ofm)
 {
 	u32 element_size, storage = fm->precision >> 14;
 	int tile = 0;

[ ... ]

drivers/accel/ethosu/ethosu_gem.c static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info, · line 232

Quoted 3 lines
drivers/accel/ethosu/ethosu_gem.c +0 -0
@@ -231,10 +231,11 @@ static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info,
 
 	switch ((fm->precision >> 6) & 0x3) { // format

[Severity: Critical]
This isn't a bug introduced by this patch, but what happens here if a userspace process submits a command stream with a precision format of 2 or 3?

Since the switch lacks handling for cases 2 and 3, it seems to silently fall through and bypass adding the x and c components to the address offset. Does this cause the validation routine to compute a falsely small memory footprint, potentially allowing the command to execute on a smaller-than-required buffer and perform out-of-bounds DMA access?

Quoted 11 lines
drivers/accel/ethosu/ethosu_gem.c +3 -2
 	case 0: //nhwc:
-		addr += x * fm->stride_x + c;
+		element_size = BIT((fm->precision >> (ofm ? 1 : 2)) & 0x3);
+		addr += x * fm->stride_x + c * element_size;
 		break;
 	case 1: //nhcwb16:
-		element_size = BIT((fm->precision >> 1) & 0x3);
+		element_size = BIT((fm->precision >> (ofm ? 1 : 2)) & 0x3);
 
 		addr += (c / 16) * fm->stride_c + (16 * x + (c & 0xf)) * element_size;
 		break;

[ ... ]

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
maintainer Acked-by on v1 1/2
#4 lore raw
In reply to Rob Herring (Arm)
Quoted 8 lines · Rob Herring (Arm)

There are 2 issues with the element size handling in the command stream validation which result in too small of a size calculated when the element size is 16/32/64 bits.

For NHWC format, the element size is simply missing from the calculation.

Quoted 6 lines

The bitfield for the element size is different between IFM/IFM2 and OFM. IFM and IFM2 encode the precision in parameter bits 2:3, while OFM uses bits 1:2.

Fixes 5a5e9c0228e6 ("accel: Add Arm Ethos-U NPU driver")
Quoted 88 lines
---
 drivers/accel/ethosu/ethosu_gem.c | 19 ++++++++++---------
 1 file changed, 10 insertions(+), 9 deletions(-)
drivers/accel/ethosu/ethosu_gem.c +10 -10
index 3401883e207f..1daff32610c7 100644
@@ -194,7 +194,7 @@ static u64 dma_length(struct ethosu_validated_cmdstream_info *info,
 
 static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info,
                              struct feat_matrix *fm,
-                             u32 x, u32 y, u32 c)
+                             u32 x, u32 y, u32 c, bool ofm)
 {
        u32 element_size, storage = fm->precision >> 14;
        int tile = 0;
@@ -231,10 +231,11 @@ static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info,
 
        switch ((fm->precision >> 6) & 0x3) { // format
        case 0: //nhwc:
-               addr += x * fm->stride_x + c;
+               element_size = BIT((fm->precision >> (ofm ? 1 : 2)) & 0x3);
+               addr += x * fm->stride_x + c * element_size;
                break;
        case 1: //nhcwb16:
-               element_size = BIT((fm->precision >> 1) & 0x3);
+               element_size = BIT((fm->precision >> (ofm ? 1 : 2)) & 0x3);
 
                addr += (c / 16) * fm->stride_c + (16 * x + (c & 0xf)) * element_size;
                break;
@@ -268,7 +269,7 @@ static int calc_sizes(struct drm_device *ddev,
                        return -EINVAL;
 
                len = feat_matrix_length(info, &st->ifm, ifm_width,
-                                        ifm_height, st->ifm.depth);
+                                        ifm_height, st->ifm.depth, false);
                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)
@@ -277,7 +278,7 @@ static int calc_sizes(struct drm_device *ddev,
 
        if (ifm2) {
                len = feat_matrix_length(info, &st->ifm2, st->ifm.depth,
-                                        0, st->ofm.depth);
+                                        0, st->ofm.depth, false);
                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)
@@ -309,7 +310,7 @@ static int calc_sizes(struct drm_device *ddev,
        }
 
        len = feat_matrix_length(info, &st->ofm, st->ofm.width,
-                                st->ofm.height[2], st->ofm.depth);
+                                st->ofm.height[2], st->ofm.depth, true);
        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)
@@ -333,7 +334,7 @@ static int calc_sizes_elemwise(struct drm_device *ddev,
                depth = st->ifm.broadcast & 0x4 ? 0 : st->ofm.depth;
 
                len = feat_matrix_length(info, &st->ifm, width,
-                                        height, depth);
+                                        height, depth, false);
                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)
@@ -346,7 +347,7 @@ static int calc_sizes_elemwise(struct drm_device *ddev,
                depth = st->ifm2.broadcast & 0x4 ? 0 : st->ofm.depth;
 
                len = feat_matrix_length(info, &st->ifm2, width,
-                                        height, depth);
+                                        height, depth, false);
                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)
@@ -354,7 +355,7 @@ static int calc_sizes_elemwise(struct drm_device *ddev,
        }
 
        len = feat_matrix_length(info, &st->ofm, st->ofm.width,
-                                st->ofm.height[2], st->ofm.depth);
+                                st->ofm.height[2], st->ofm.depth, true);
        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)
--

2.53.0