mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Konrad Dybcio <konradybcio@kernel.org>
To: "Rob Clark" <robin.clark@oss.qualcomm.com>,
	"Dmitry Baryshkov" <lumag@kernel.org>,
	"Abhinav Kumar" <abhinav.kumar@linux.dev>,
	"Jessica Zhang" <jesszhan0024@gmail.com>,
	"Sean Paul" <sean@poorly.run>,
	"Marijn Suijten" <marijn.suijten@somainline.org>,
	"David Airlie" <airlied@gmail.com>,
	"Simona Vetter" <simona@ffwll.ch>,
	"Miguel Ojeda" <ojeda@kernel.org>,
	"Boqun Feng" <boqun@kernel.org>, "Gary Guo" <gary@garyguo.net>,
	"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
	"Benno Lossin" <lossin@kernel.org>,
	"Andreas Hindborg" <a.hindborg@kernel.org>,
	"Alice Ryhl" <aliceryhl@google.com>,
	"Trevor Gross" <tmgross@umich.edu>,
	"Danilo Krummrich" <dakr@kernel.org>,
	"Daniel Almeida" <daniel.almeida@collabora.com>,
	"Tamir Duberstein" <tamird@kernel.org>,
	"Alexandre Courbot" <acourbot@nvidia.com>,
	"Onur Özkan" <work@onurozkan.dev>
Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org,
	 dri-devel@lists.freedesktop.org,
	freedreno@lists.freedesktop.org,  rust-for-linux@vger.kernel.org,
	 Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Subject: [PATCH RFC] drm/msm: move VM_BIND op validation to Rust
Date: Mon, 26 Oct 2026 12:40:02 +0100	[thread overview]
Message-ID: <20261026-topic-vm_bind_rs-v1-1-6581465f2428@oss.qualcomm.com> (raw)

From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>

Much of the VM handling code is a headache to read and maintain.
There's a soup of mixed-signedness/size types which need to be compared
and added and getting that wrong in C is very easy.

The "(range + obj_offset) > obj->size" check could wrap for a large
obj_offset, which let an out-of-range map be accepted and then mapped
as nothing. Oxidize it and use checked_add() to fix that.

This commit is a "get the foot in the door" type change to make sure
that folks building kernels for Qualcomm SoCs have their Rust setup
ready. It may/should be extended to the wider mm code once the
appropriate abstractions are finalized.

Worth noting, the C UERR() macro expands to dev_dbg() and the Rust
implementation for that currently doesn't support dynamic_debug.

The DRM_BRIDGE dependency is dropped because including RUST exposed
some spaghetti dependencies ending at DEBUGFS and it's enabled anyway.

Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
---
(if this is rejected we may just patch the C code).

Notably, this will regress some ancient configurations:
-- arch/arm/Kconfig
    select HAVE_RUST if CPU_LITTLE_ENDIAN && CPU_32v7 && !KASAN

Smoke-tested on Glymur
---
 drivers/gpu/drm/msm/Kconfig        |   2 +-
 drivers/gpu/drm/msm/Makefile       |   1 +
 drivers/gpu/drm/msm/msm_gem_vma.c  |  66 +++------------
 drivers/gpu/drm/msm/msm_vm_bind.h  |  16 ++++
 drivers/gpu/drm/msm/msm_vm_bind.rs | 160 +++++++++++++++++++++++++++++++++++++
 rust/uapi/uapi_helper.h            |   1 +
 6 files changed, 188 insertions(+), 58 deletions(-)

diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig
index ed1c4775d1e9..f5c4749f8e95 100644
--- a/drivers/gpu/drm/msm/Kconfig
+++ b/drivers/gpu/drm/msm/Kconfig
@@ -3,6 +3,7 @@
 config DRM_MSM
 	tristate "MSM DRM"
 	depends on DRM
+	depends on RUST
 	depends on ARCH_QCOM || SOC_IMX5 || COMPILE_TEST
 	depends on COMMON_CLK
 	depends on IOMMU_SUPPORT
@@ -63,7 +64,6 @@ config DRM_MSM_VALIDATE_XML
 config DRM_MSM_KMS
 	def_bool n
 	depends on DRM_MSM
-	select DRM_BRIDGE
 	select DRM_BRIDGE_CONNECTOR
 	select DRM_CLIENT_SELECTION
 	select DRM_DISPLAY_HELPER
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index 1e246616f83f..826759a820a0 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -129,6 +129,7 @@ msm-y += \
 	msm_ringbuffer.o \
 	msm_submitqueue.o \
 	msm_syncobj.o \
+	msm_vm_bind.o \
 	msm_gpu_tracepoints.o \
 
 msm-$(CONFIG_DRM_MSM_KMS) += \
diff --git a/drivers/gpu/drm/msm/msm_gem_vma.c b/drivers/gpu/drm/msm/msm_gem_vma.c
index f687a629629d..3c453b034e8f 100644
--- a/drivers/gpu/drm/msm/msm_gem_vma.c
+++ b/drivers/gpu/drm/msm/msm_gem_vma.c
@@ -14,6 +14,7 @@
 #include "msm_gpu.h"
 #include "msm_mmu.h"
 #include "msm_syncobj.h"
+#include "msm_vm_bind.h"
 
 #define vm_dbg(fmt, ...) pr_debug("%s:%d: "fmt"\n", __func__, __LINE__, ##__VA_ARGS__)
 
@@ -981,23 +982,12 @@ vm_bind_job_create(struct drm_device *dev, struct drm_file *file, struct drm_gpu
 	return job;
 }
 
-static bool invalid_alignment(uint64_t addr)
-{
-	/*
-	 * Technically this is about GPU alignment, not CPU alignment.  But
-	 * I've not seen any qcom SoC where the SMMU does not support the
-	 * CPU's smallest page size.
-	 */
-	return !PAGE_ALIGNED(addr);
-}
-
 static int
 lookup_op(struct msm_vm_bind_job *job, const struct drm_msm_vm_bind_op *op)
 {
 	struct drm_device *dev = job->vm->drm;
 	struct msm_drm_private *priv = dev->dev_private;
 	int i = job->nr_ops++;
-	int ret = 0;
 
 	job->ops[i].op = op->op;
 	job->ops[i].handle = op->handle;
@@ -1006,48 +996,11 @@ lookup_op(struct msm_vm_bind_job *job, const struct drm_msm_vm_bind_op *op)
 	job->ops[i].range = op->range;
 	job->ops[i].flags = op->flags;
 
-	if (op->flags & ~MSM_VM_BIND_OP_FLAGS)
-		ret = UERR(EINVAL, dev, "invalid flags: %x\n", op->flags);
-
-	if (invalid_alignment(op->iova))
-		ret = UERR(EINVAL, dev, "invalid address: %016llx\n", op->iova);
-
-	if (invalid_alignment(op->obj_offset))
-		ret = UERR(EINVAL, dev, "invalid bo_offset: %016llx\n", op->obj_offset);
-
-	if (invalid_alignment(op->range))
-		ret = UERR(EINVAL, dev, "invalid range: %016llx\n", op->range);
-
-	if (!drm_gpuvm_range_valid(job->vm, op->iova, op->range))
-		ret = UERR(EINVAL, dev, "invalid range: %016llx, %016llx\n", op->iova, op->range);
-
-	/*
-	 * MAP must specify a valid handle.  But the handle MBZ for
-	 * UNMAP or MAP_NULL.
-	 */
-	if (op->op == MSM_VM_BIND_OP_MAP) {
-		if (!op->handle)
-			ret = UERR(EINVAL, dev, "invalid handle\n");
-	} else if (op->handle) {
-		ret = UERR(EINVAL, dev, "handle must be zero\n");
-	}
-
-	switch (op->op) {
-	case MSM_VM_BIND_OP_MAP:
-	case MSM_VM_BIND_OP_MAP_NULL:
-	case MSM_VM_BIND_OP_UNMAP:
-		break;
-	default:
-		ret = UERR(EINVAL, dev, "invalid op: %u\n", op->op);
-		break;
-	}
-
-	if ((op->op == MSM_VM_BIND_OP_MAP_NULL) &&
-	    !adreno_smmu_has_prr(priv->gpu)) {
-		ret = UERR(EINVAL, dev, "PRR not supported\n");
-	}
-
-	return ret;
+	return msm_vm_bind_check_op(dev->dev, op->op, op->handle, op->obj_offset,
+				    op->iova, op->range, op->flags, PAGE_SIZE,
+				    drm_gpuvm_range_valid(job->vm, op->iova, op->range),
+				    op->op == MSM_VM_BIND_OP_MAP_NULL &&
+				    adreno_smmu_has_prr(priv->gpu));
 }
 
 /*
@@ -1118,11 +1071,10 @@ vm_bind_job_lookup_ops(struct msm_vm_bind_job *job, struct drm_msm_vm_bind *args
 		op->obj = obj;
 		cnt++;
 
-		if ((op->range + op->obj_offset) > obj->size) {
-			ret = UERR(EINVAL, dev, "invalid range: %016llx + %016llx > %016zx\n",
-				   op->range, op->obj_offset, obj->size);
+		ret = msm_vm_bind_check_bo_range(dev->dev, op->obj_offset, op->range,
+						 obj->size);
+		if (ret)
 			goto out_unlock;
-		}
 	}
 
 	*nr_bos = cnt;
diff --git a/drivers/gpu/drm/msm/msm_vm_bind.h b/drivers/gpu/drm/msm/msm_vm_bind.h
new file mode 100644
index 000000000000..e8aa6adb9181
--- /dev/null
+++ b/drivers/gpu/drm/msm/msm_vm_bind.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef __MSM_VM_BIND_H__
+#define __MSM_VM_BIND_H__
+
+#include <linux/types.h>
+
+struct device;
+
+int msm_vm_bind_check_op(struct device *dev, u32 op, u32 handle,
+			 u64 obj_offset, u64 iova, u64 range, u32 flags,
+			 u64 page_size, bool range_valid, bool prr_supported);
+int msm_vm_bind_check_bo_range(struct device *dev, u64 obj_offset, u64 range,
+			       u64 obj_size);
+
+#endif /* __MSM_VM_BIND_H__ */
diff --git a/drivers/gpu/drm/msm/msm_vm_bind.rs b/drivers/gpu/drm/msm/msm_vm_bind.rs
new file mode 100644
index 000000000000..164208e0a1b4
--- /dev/null
+++ b/drivers/gpu/drm/msm/msm_vm_bind.rs
@@ -0,0 +1,160 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//! VM_BIND operation validation for the MSM driver.
+
+use kernel::{
+    bindings,
+    device::Device,
+    error::code::EINVAL,
+    prelude::*,
+    uapi, //
+};
+
+/// Validates a single VM_BIND operation.
+///
+/// Returns 0 if the operation is valid. Otherwise logs the reason and returns
+/// `-EINVAL`.
+///
+/// `page_size` is the granularity that `iova`, `obj_offset` and `range` must be
+/// aligned to. It is the GPU's alignment rather than the CPU's, but no known qcom
+/// SMMU lacks support for the CPU's smallest page size.
+/// `range_valid` is the result of `drm_gpuvm_range_valid()` for this operation.
+/// `prr_supported` is only consulted for `MSM_VM_BIND_OP_MAP_NULL`.
+///
+/// # Safety
+///
+/// `dev` must point to a live `struct device`.
+#[no_mangle]
+pub unsafe extern "C" fn msm_vm_bind_check_op(
+    dev: *mut bindings::device,
+    op: u32,
+    handle: u32,
+    obj_offset: u64,
+    iova: u64,
+    range: u64,
+    flags: u32,
+    page_size: u64,
+    range_valid: bool,
+    prr_supported: bool,
+) -> c_int {
+    // SAFETY: The caller guarantees that `dev` is a live `struct device`.
+    let dev: &Device = unsafe { Device::from_raw(dev) };
+
+    match check_op(
+        dev,
+        op,
+        handle,
+        obj_offset,
+        iova,
+        range,
+        flags,
+        page_size,
+        range_valid,
+        prr_supported,
+    ) {
+        Ok(()) => 0,
+        Err(e) => e.to_errno(),
+    }
+}
+
+fn check_op(
+    dev: &Device,
+    op: u32,
+    handle: u32,
+    obj_offset: u64,
+    iova: u64,
+    range: u64,
+    flags: u32,
+    page_size: u64,
+    range_valid: bool,
+    prr_supported: bool,
+) -> Result {
+    let aligned = |addr: u64| page_size.is_power_of_two() && (addr & (page_size - 1)) == 0;
+
+    if flags & !uapi::MSM_VM_BIND_OP_FLAGS != 0 {
+        dev_dbg!(dev, "invalid flags: {:x}\n", flags);
+        return Err(EINVAL);
+    }
+
+    if !aligned(iova) {
+        dev_dbg!(dev, "invalid address: {:016x}\n", iova);
+        return Err(EINVAL);
+    }
+
+    if !aligned(obj_offset) {
+        dev_dbg!(dev, "invalid bo_offset: {:016x}\n", obj_offset);
+        return Err(EINVAL);
+    }
+
+    if !aligned(range) {
+        dev_dbg!(dev, "invalid range: {:016x}\n", range);
+        return Err(EINVAL);
+    }
+
+    if !range_valid {
+        dev_dbg!(dev, "invalid range: {:016x}, {:016x}\n", iova, range);
+        return Err(EINVAL);
+    }
+
+    // MAP must name a handle; UNMAP and MAP_NULL must not.
+    match op {
+        uapi::MSM_VM_BIND_OP_MAP => {
+            if handle == 0 {
+                dev_dbg!(dev, "invalid handle\n");
+                return Err(EINVAL);
+            }
+        }
+        uapi::MSM_VM_BIND_OP_UNMAP | uapi::MSM_VM_BIND_OP_MAP_NULL => {
+            if handle != 0 {
+                dev_dbg!(dev, "handle must be zero\n");
+                return Err(EINVAL);
+            }
+        }
+        _ => {
+            dev_dbg!(dev, "invalid op: {}\n", op);
+            return Err(EINVAL);
+        }
+    }
+
+    if op == uapi::MSM_VM_BIND_OP_MAP_NULL && !prr_supported {
+        dev_dbg!(dev, "PRR not supported\n");
+        return Err(EINVAL);
+    }
+
+    Ok(())
+}
+
+/// Checks that `[obj_offset, obj_offset + range)` lies within an object of
+/// `obj_size` bytes.
+///
+/// Returns 0 if it does. Otherwise logs the reason and returns `-EINVAL`.
+///
+/// # Safety
+///
+/// `dev` must point to a live `struct device`.
+#[no_mangle]
+pub unsafe extern "C" fn msm_vm_bind_check_bo_range(
+    dev: *mut bindings::device,
+    obj_offset: u64,
+    range: u64,
+    obj_size: u64,
+) -> c_int {
+    // SAFETY: The caller guarantees that `dev` is a live `struct device`.
+    let dev: &Device = unsafe { Device::from_raw(dev) };
+
+    let fits = obj_offset
+        .checked_add(range)
+        .is_some_and(|end| end <= obj_size);
+
+    if fits {
+        return 0;
+    }
+
+    dev_dbg!(
+        dev,
+        "invalid range: {:016x} + {:016x} > {:016x}\n",
+        range,
+        obj_offset,
+        obj_size
+    );
+    EINVAL.to_errno()
+}
diff --git a/rust/uapi/uapi_helper.h b/rust/uapi/uapi_helper.h
index 489748ef642c..38741220270a 100644
--- a/rust/uapi/uapi_helper.h
+++ b/rust/uapi/uapi_helper.h
@@ -7,6 +7,7 @@
  */
 
 #include <uapi/drm/drm.h>
+#include <uapi/drm/msm_drm.h>
 #include <uapi/drm/nova_drm.h>
 #include <uapi/drm/panthor_drm.h>
 #include <uapi/linux/android/binder.h>

---
base-commit: 20a33c6b6cbe7e4805dfb6cd68816937443aa148
change-id: 20261026-topic-vm_bind_rs-5551b8014396

Best regards,
--  
Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>


             reply	other threads:[~2026-10-26 11:40 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-26 11:40 Konrad Dybcio [this message]
2026-10-26 14:39 ` Rob Clark
2026-10-26 15:07   ` Konrad Dybcio
2026-10-26 15:26 ` Rob Clark
2026-10-26 15:30   ` Konrad Dybcio
2026-10-26 15:33     ` Rob Clark

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261026-topic-vm_bind_rs-v1-1-6581465f2428@oss.qualcomm.com \
    --to=konradybcio@kernel.org \
    --cc=a.hindborg@kernel.org \
    --cc=abhinav.kumar@linux.dev \
    --cc=acourbot@nvidia.com \
    --cc=airlied@gmail.com \
    --cc=aliceryhl@google.com \
    --cc=bjorn3_gh@protonmail.com \
    --cc=boqun@kernel.org \
    --cc=dakr@kernel.org \
    --cc=daniel.almeida@collabora.com \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=freedreno@lists.freedesktop.org \
    --cc=gary@garyguo.net \
    --cc=jesszhan0024@gmail.com \
    --cc=konrad.dybcio@oss.qualcomm.com \
    --cc=linux-arm-msm@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=lossin@kernel.org \
    --cc=lumag@kernel.org \
    --cc=marijn.suijten@somainline.org \
    --cc=ojeda@kernel.org \
    --cc=robin.clark@oss.qualcomm.com \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=sean@poorly.run \
    --cc=simona@ffwll.ch \
    --cc=tamird@kernel.org \
    --cc=tmgross@umich.edu \
    --cc=work@onurozkan.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®