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>
next 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®