Short summary of fixes pull:

dumb-buffer:
 - remove strict limits on buffer geometry
 
 ethosu:
 - reject unsupported NPU_OP_RESIZE
 - fix index of IFM region
 - fix weight index
 - fix overflows in DMA-size calculations
 - reject DMA commands with uninitialized length
 - fix OOB write in ethosu_gem_cmdstream_copy_and_validate
 
 imx:
 - fix kernel-doc warnings
 
 ivpu:
 - add overflow checks in firmware handling and get_info_ioctl
 
 v3d:
 - wait for pending L2T flush before cleaning caches
 - fix leak of vaddr
 - skip CSD when it has zeroed workgroups
 - fix ref counting in performance monitoring
 -----BEGIN PGP SIGNATURE-----
 
 iQFPBAABCgA5FiEEchf7rIzpz2NEoWjlaA3BHVMLeiMFAmoiedAbFIAAAAAABAAO
 bWFudTIsMi41KzEuMTIsMiwyAAoJEGgNwR1TC3ojlPgH/3dtzYxSDc/66kxFEdYw
 RUOemMjs+EbT8+NU9k0H4rI3IWur8IzOEBXvlVSGwe4J2RusqWDnKpbJij34VgZq
 524oSLk92Me+VmO3L3QE07OaZ233tOKYKV44CZraBcBVAl4NSnnmf2pV3yrddsBQ
 8vKJPmSCRMiQprkFYcyQc6YEbix/MVXMLRUxS0lNZzCtH5594Ft2ugD+4VMl1Bkd
 sMi8dMYQrsv2qsgRJhvZHOjT7E1mJuH6DLEn7jgvx3oO4ONZWb4kJ7clgozsLcZn
 hLFgtQMh8AR+lJaDshRVj5P+xlzgoGkM7eLEjSTQWv1VQ9EMw4/orOWpm55U9v17
 4gY=
 =lxiE
 -----END PGP SIGNATURE-----

Merge tag 'drm-misc-fixes-2026-06-05' of https://gitlab.freedesktop.org/drm/misc/kernel into drm-fixes

Short summary of fixes pull:

dumb-buffer:
- remove strict limits on buffer geometry

ethosu:
- reject unsupported NPU_OP_RESIZE
- fix index of IFM region
- fix weight index
- fix overflows in DMA-size calculations
- reject DMA commands with uninitialized length
- fix OOB write in ethosu_gem_cmdstream_copy_and_validate

imx:
- fix kernel-doc warnings

ivpu:
- add overflow checks in firmware handling and get_info_ioctl

v3d:
- wait for pending L2T flush before cleaning caches
- fix leak of vaddr
- skip CSD when it has zeroed workgroups
- fix ref counting in performance monitoring

Signed-off-by: Dave Airlie <airlied@redhat.com>

From: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patch.msgid.link/20260605072602.GA268798@linux.fritz.box
This commit is contained in:
Dave Airlie 2026-06-06 08:37:21 +10:00
commit f80cbe57ec
9 changed files with 98 additions and 24 deletions

View file

@ -2,6 +2,7 @@
/* Copyright 2025 Arm, Ltd. */
#include <linux/err.h>
#include <linux/overflow.h>
#include <linux/slab.h>
#include <drm/ethosu_accel.h>
@ -163,17 +164,30 @@ static u64 dma_length(struct ethosu_validated_cmdstream_info *info,
s8 mode = dma_st->mode;
u64 len = dma->len;
if (len == U64_MAX)
return U64_MAX;
if (mode >= 1) {
if (dma->stride[0] < 0 && (u64)(-dma->stride[0]) > len)
return U64_MAX;
len += dma->stride[0];
len *= dma_st->size0;
if (check_mul_overflow(len, (u64)dma_st->size0, &len))
return U64_MAX;
}
if (mode == 2) {
if (dma->stride[1] < 0 && (u64)(-dma->stride[1]) > len)
return U64_MAX;
len += dma->stride[1];
len *= dma_st->size1;
if (check_mul_overflow(len, (u64)dma_st->size1, &len))
return U64_MAX;
}
if (dma->region >= 0) {
u64 end;
if (check_add_overflow(len, dma->offset, &end))
return U64_MAX;
info->region_size[dma->region] = max(info->region_size[dma->region], end);
}
if (dma->region >= 0)
info->region_size[dma->region] = max(info->region_size[dma->region],
len + dma->offset);
return len;
}
@ -387,6 +401,8 @@ static int ethosu_gem_cmdstream_copy_and_validate(struct drm_device *ddev,
return -EFAULT;
i++;
if (i >= size / 4)
return -EINVAL;
bocmds[i] = cmds[1];
addr = cmd_to_addr(cmds);
}
@ -395,6 +411,8 @@ static int ethosu_gem_cmdstream_copy_and_validate(struct drm_device *ddev,
case NPU_OP_DMA_START:
srclen = dma_length(info, &st.dma, &st.dma.src);
dstlen = dma_length(info, &st.dma, &st.dma.dst);
if (srclen == U64_MAX || dstlen == U64_MAX)
return -EINVAL;
if (st.dma.dst.region >= 0)
info->output_region[st.dma.dst.region] = true;
@ -431,8 +449,7 @@ static int ethosu_gem_cmdstream_copy_and_validate(struct drm_device *ddev,
return ret;
break;
case NPU_OP_RESIZE: // U85 only
WARN_ON(1); // TODO
break;
return -EINVAL;
case NPU_SET_KERNEL_WIDTH_M1:
st.ifm.width = param;
break;
@ -464,7 +481,7 @@ static int ethosu_gem_cmdstream_copy_and_validate(struct drm_device *ddev,
st.ifm.broadcast = param;
break;
case NPU_SET_IFM_REGION:
st.ifm.region = param & 0x7f;
st.ifm.region = param & 0x7;
break;
case NPU_SET_IFM_WIDTH0_M1:
st.ifm.width0 = param;
@ -599,7 +616,7 @@ static int ethosu_gem_cmdstream_copy_and_validate(struct drm_device *ddev,
if (ethosu_is_u65(edev))
st.scale[1].length = cmds[1];
else
st.weight[1].length = cmds[1];
st.weight[2].length = cmds[1];
break;
case NPU_SET_WEIGHT3_BASE:
st.weight[3].base = addr;

View file

@ -259,6 +259,22 @@ static int ivpu_fw_parse(struct ivpu_device *vdev)
return -EINVAL;
}
if (!PAGE_ALIGNED(runtime_addr)) {
ivpu_err(vdev, "Runtime address 0x%llx not page aligned\n", runtime_addr);
return -EINVAL;
}
if (!PAGE_ALIGNED(runtime_size)) {
ivpu_err(vdev, "Runtime size %llu not page aligned\n", runtime_size);
return -EINVAL;
}
if (runtime_size < image_size) {
ivpu_err(vdev, "Runtime size too small: %llu, image size: %llu\n",
runtime_size, image_size);
return -EINVAL;
}
if (!ivpu_is_within_range(image_load_addr, image_size, &vdev->hw->ranges.runtime)) {
ivpu_err(vdev, "Invalid firmware load address: 0x%llx and size %llu\n",
image_load_addr, image_size);

View file

@ -98,6 +98,11 @@ static void fw_log_print_buffer(struct vpu_tracing_buffer_header *log, const cha
u32 log_start = only_new_msgs ? READ_ONCE(log->read_index) : 0;
u32 log_end = READ_ONCE(log->write_index);
if (log_start >= data_size)
log_start = 0;
if (log_end > data_size)
log_end = data_size;
if (log->wrap_count == log->read_wrap_count) {
if (log_end <= log_start) {
drm_printf(p, "==== %s \"%s\" log empty ====\n", prefix, log->name);

View file

@ -291,6 +291,13 @@ int ivpu_ms_get_info_ioctl(struct drm_device *dev, void *data, struct drm_file *
if (ret)
goto unlock;
if (info_size > ivpu_bo_size(bo)) {
ivpu_warn_ratelimited(vdev, "MS info overflow: %#llx > %#zx\n",
info_size, ivpu_bo_size(bo));
ret = -EOVERFLOW;
goto unlock;
}
if (args->buffer_size < info_size) {
ret = -ENOSPC;
goto unlock;

View file

@ -202,13 +202,6 @@ int drm_mode_create_dumb(struct drm_device *dev,
if (!args->width || !args->height || !args->bpp)
return -EINVAL;
/* Reject unreasonable inputs early. Dumb buffers are for software
* rendering; nothing legitimate needs more than 8192x8192 at 32bpp.
* This prevents overflows in downstream alignment helpers.
*/
if (args->width >= 8192 || args->height >= 8192 || args->bpp > 32)
return -EINVAL;
/* overflow checks for 32bit size calculations */
if (args->bpp > U32_MAX - 8)
return -EINVAL;

View file

@ -166,6 +166,7 @@ static int exp_approx_q(int x)
* dcss_scaler_gaussian_filter() - Generate gaussian prototype filter.
* @fc_q: fixed-point cutoff frequency normalized to range [0, 1]
* @use_5_taps: indicates whether to use 5 taps or 7 taps
* @phase0_identity: whether to override phase 0 coefficients with identity filter
* @coef: output filter coefficients
*/
static void dcss_scaler_gaussian_filter(int fc_q, bool use_5_taps,
@ -262,7 +263,9 @@ static void dcss_scaler_nearest_neighbor_filter(bool use_5_taps,
* @src_length: length of input
* @dst_length: length of output
* @use_5_taps: 0 for 7 taps per phase, 1 for 5 taps
* @phase0_identity: whether to override phase 0 coefficients with identity filter
* @coef: output coefficients
* @nn_interpolation: whether to use nearest neighbor instead of gaussian filter
*/
static void dcss_scaler_filter_design(int src_length, int dst_length,
bool use_5_taps, bool phase0_identity,

View file

@ -213,6 +213,14 @@ v3d_clean_caches(struct v3d_dev *v3d)
trace_v3d_cache_clean_begin(dev);
/* GFXH-1897: Ensure pending flushes complete before writing L2TCACTL */
if (v3d->ver < V3D_GEN_71) {
if (wait_for(!(V3D_CORE_READ(core, V3D_CTL_L2TCACTL) &
V3D_L2TCACTL_L2TFLS), 100)) {
drm_err(dev, "Timeout waiting for L2T clean\n");
}
}
V3D_CORE_WRITE(core, V3D_CTL_L2TCACTL, V3D_L2TCACTL_TMUWCF);
if (wait_for(!(V3D_CORE_READ(core, V3D_CTL_L2TCACTL) &
V3D_L2TCACTL_TMUWCF), 100)) {

View file

@ -309,8 +309,11 @@ static void v3d_perfmon_delete(struct v3d_file_priv *v3d_priv,
if (perfmon == v3d->active_perfmon)
v3d_perfmon_stop(v3d, perfmon, false);
/* If the global perfmon is being destroyed, set it to NULL */
cmpxchg(&v3d->global_perfmon, perfmon, NULL);
/* If the global perfmon is being destroyed, clean it and release
* the reference stashed in v3d_perfmon_set_global_ioctl().
*/
if (cmpxchg(&v3d->global_perfmon, perfmon, NULL) == perfmon)
v3d_perfmon_put(perfmon);
v3d_perfmon_put(perfmon);
}
@ -461,16 +464,27 @@ int v3d_perfmon_set_global_ioctl(struct drm_device *dev, void *data,
/* If the request is to clear the global performance monitor */
if (req->flags & DRM_V3D_PERFMON_CLEAR_GLOBAL) {
if (!v3d->global_perfmon)
struct v3d_perfmon *old;
/* DRM_V3D_PERFMON_CLEAR_GLOBAL doesn't check if
* v3d->global_perfmon == perfmon. Therefore, there
* is no need to keep perfmon's reference.
*/
v3d_perfmon_put(perfmon);
old = xchg(&v3d->global_perfmon, NULL);
if (!old)
return -EINVAL;
xchg(&v3d->global_perfmon, NULL);
v3d_perfmon_put(old);
return 0;
}
if (cmpxchg(&v3d->global_perfmon, NULL, perfmon))
if (cmpxchg(&v3d->global_perfmon, NULL, perfmon)) {
v3d_perfmon_put(perfmon);
return -EBUSY;
}
return 0;
}

View file

@ -352,6 +352,16 @@ v3d_csd_job_run(struct drm_sched_job *sched_job)
return NULL;
}
/* The HW interprets a workgroup size of 0 as 65536; however, the
* user-space driver exposes a maximum of 65535. Therefore, a 0 in
* any dimension means that we have no workgroups and the compute
* shader should not be dispatched.
*/
if (!V3D_GET_FIELD(job->args.cfg[0], V3D_CSD_QUEUED_CFG0_NUM_WGS_X) ||
!V3D_GET_FIELD(job->args.cfg[1], V3D_CSD_QUEUED_CFG1_NUM_WGS_Y) ||
!V3D_GET_FIELD(job->args.cfg[2], V3D_CSD_QUEUED_CFG2_NUM_WGS_Z))
return NULL;
v3d->queue[V3D_CSD].active_job = &job->base;
v3d_invalidate_caches(v3d);
@ -402,13 +412,13 @@ v3d_rewrite_csd_job_wg_counts_from_indirect(struct v3d_cpu_job *job)
wg_counts = (uint32_t *)(bo->vaddr + indirect_csd->offset);
if (wg_counts[0] == 0 || wg_counts[1] == 0 || wg_counts[2] == 0)
return;
args->cfg[0] = wg_counts[0] << V3D_CSD_CFG012_WG_COUNT_SHIFT;
args->cfg[1] = wg_counts[1] << V3D_CSD_CFG012_WG_COUNT_SHIFT;
args->cfg[2] = wg_counts[2] << V3D_CSD_CFG012_WG_COUNT_SHIFT;
if (wg_counts[0] == 0 || wg_counts[1] == 0 || wg_counts[2] == 0)
goto unmap_bo;
num_batches = DIV_ROUND_UP(indirect_csd->wg_size, 16) *
(wg_counts[0] * wg_counts[1] * wg_counts[2]);
@ -428,6 +438,7 @@ v3d_rewrite_csd_job_wg_counts_from_indirect(struct v3d_cpu_job *job)
}
}
unmap_bo:
v3d_put_bo_vaddr(indirect);
v3d_put_bo_vaddr(bo);
}