[PATCH RFC 15/18] accel/qda: Add FastRPC DSP process creation support
From: Ekansh Gupta
Date: Mon Feb 23 2026 - 14:26:35 EST
Add support for creating a DSP process through the QDA FastRPC
interface. A new DRM_QDA_INIT_CREATE ioctl accepts a qda_init_create
structure describing the executable image, process attributes and
optional signature. The driver allocates a GEM-backed initialization
buffer, prepares a fastrpc_create_process_inbuf and a single
fastrpc_phy_page entry pointing to the initialization memory and
packages these into a set of FastRPC arguments.
The FastRPC core gains FASTRPC_RMID_INIT_CREATE and
FASTRPC_RMID_INIT_CREATE_ATTR method identifiers along with a
fastrpc_prepare_args_init_create() helper that reads the
qda_init_create parameters from user space, validates the ELF length,
optionally verifies a GEM handle for the image and fills a
FASTRPC_CREATE_PROCESS_NARGS-sized fastrpc_invoke_args array. The
scalars value is built from the FastRPC method id and buffer counts
so that the existing overlap and packing logic can treat process
creation like any other call.
On the IOCTL side qda_ioctl_create() forwards requests to
fastrpc_invoke() with the INIT_CREATE method id, ensuring that the
message buffer, per-process initialization memory and RPMsg
transport are reused for process creation in the same way as attach,
release and dynamic invocation. This patch lays the groundwork for
loading and running DSP user PDs under the QDA driver.
Signed-off-by: Ekansh Gupta <ekansh.gupta@xxxxxxxxxxxxxxxx>
---
drivers/accel/qda/qda_drv.c | 1 +
drivers/accel/qda/qda_drv.h | 2 +
drivers/accel/qda/qda_fastrpc.c | 109 ++++++++++++++++++++++++++++++++++++++++
drivers/accel/qda/qda_fastrpc.h | 31 ++++++++++++
drivers/accel/qda/qda_ioctl.c | 28 ++++++++++-
drivers/accel/qda/qda_ioctl.h | 13 +++++
include/uapi/drm/qda_accel.h | 29 ++++++++++-
7 files changed, 211 insertions(+), 2 deletions(-)
diff --git a/drivers/accel/qda/qda_drv.c b/drivers/accel/qda/qda_drv.c
index f94f780ea50a..2b080d5d51c5 100644
--- a/drivers/accel/qda/qda_drv.c
+++ b/drivers/accel/qda/qda_drv.c
@@ -162,6 +162,7 @@ static const struct drm_ioctl_desc qda_ioctls[] = {
DRM_IOCTL_DEF_DRV(QDA_GEM_CREATE, qda_ioctl_gem_create, 0),
DRM_IOCTL_DEF_DRV(QDA_GEM_MMAP_OFFSET, qda_ioctl_gem_mmap_offset, 0),
DRM_IOCTL_DEF_DRV(QDA_INIT_ATTACH, qda_ioctl_attach, 0),
+ DRM_IOCTL_DEF_DRV(QDA_INIT_CREATE, qda_ioctl_create, 0),
DRM_IOCTL_DEF_DRV(QDA_INVOKE, qda_ioctl_invoke, 0),
};
diff --git a/drivers/accel/qda/qda_drv.h b/drivers/accel/qda/qda_drv.h
index bb1d1e82036a..950e8d44995d 100644
--- a/drivers/accel/qda/qda_drv.h
+++ b/drivers/accel/qda/qda_drv.h
@@ -48,6 +48,8 @@ struct qda_user {
u32 client_id;
/* Back-pointer to device structure */
struct qda_dev *qda_dev;
+ /* GEM object for PD initialization memory */
+ struct qda_gem_obj *init_mem_gem_obj;
};
/**
diff --git a/drivers/accel/qda/qda_fastrpc.c b/drivers/accel/qda/qda_fastrpc.c
index a48b255ffb1b..f03dcf7e21e4 100644
--- a/drivers/accel/qda/qda_fastrpc.c
+++ b/drivers/accel/qda/qda_fastrpc.c
@@ -487,6 +487,36 @@ int fastrpc_internal_invoke_unpack(struct fastrpc_invoke_context *ctx,
return err;
}
+static void setup_create_process_args(struct fastrpc_invoke_args *args,
+ struct fastrpc_create_process_inbuf *inbuf,
+ struct qda_init_create *init,
+ struct fastrpc_phy_page *pages)
+{
+ args[0].ptr = (u64)(uintptr_t)inbuf;
+ args[0].length = sizeof(*inbuf);
+ args[0].fd = -1;
+
+ args[1].ptr = (u64)(uintptr_t)current->comm;
+ args[1].length = inbuf->namelen;
+ args[1].fd = -1;
+
+ args[2].ptr = (u64)init->file;
+ args[2].length = inbuf->filelen;
+ args[2].fd = init->filefd;
+
+ args[3].ptr = (u64)(uintptr_t)pages;
+ args[3].length = 1 * sizeof(*pages);
+ args[3].fd = -1;
+
+ args[4].ptr = (u64)(uintptr_t)&inbuf->attrs;
+ args[4].length = sizeof(inbuf->attrs);
+ args[4].fd = -1;
+
+ args[5].ptr = (u64)(uintptr_t)&inbuf->siglen;
+ args[5].length = sizeof(inbuf->siglen);
+ args[5].fd = -1;
+}
+
static int fastrpc_prepare_args_init_attach(struct fastrpc_invoke_context *ctx)
{
struct fastrpc_invoke_args *args;
@@ -554,6 +584,80 @@ static int fastrpc_prepare_args_invoke(struct fastrpc_invoke_context *ctx, char
return 0;
}
+static int fastrpc_prepare_args_init_create(struct fastrpc_invoke_context *ctx, char __user *argp)
+{
+ struct qda_init_create init;
+ struct fastrpc_invoke_args *args;
+ struct fastrpc_create_process_inbuf *inbuf;
+ int err;
+ u32 sc;
+ struct drm_gem_object *file_gem_obj = NULL;
+
+ args = kcalloc(FASTRPC_CREATE_PROCESS_NARGS, sizeof(*args), GFP_KERNEL);
+ if (!args)
+ return -ENOMEM;
+
+ ctx->input_pages = kcalloc(1, sizeof(*ctx->input_pages), GFP_KERNEL);
+ if (!ctx->input_pages) {
+ err = -ENOMEM;
+ goto err_free_args;
+ }
+
+ ctx->inbuf = kcalloc(1, sizeof(*inbuf), GFP_KERNEL);
+ if (!ctx->inbuf) {
+ err = -ENOMEM;
+ goto err_free_input_pages;
+ }
+ inbuf = ctx->inbuf;
+
+ err = copy_from_user_or_kernel(&init, argp, sizeof(init));
+ if (err)
+ goto err_free_inbuf;
+
+ if (init.filelen > INIT_FILELEN_MAX) {
+ err = -EINVAL;
+ goto err_free_inbuf;
+ }
+ inbuf->client_id = ctx->client_id;
+ inbuf->namelen = strlen(current->comm) + 1;
+ inbuf->filelen = init.filelen;
+ inbuf->pageslen = 1;
+ inbuf->attrs = init.attrs;
+ inbuf->siglen = init.siglen;
+
+ setup_pages_from_gem_obj(ctx->init_mem_gem_obj, &ctx->input_pages[0]);
+
+ if (init.filelen && init.filefd) {
+ err = get_gem_obj_from_handle(ctx->file_priv, init.filefd, &file_gem_obj);
+ if (err) {
+ err = -EINVAL;
+ goto err_free_inbuf;
+ }
+ drm_gem_object_put(file_gem_obj);
+ }
+
+ setup_create_process_args(args, inbuf, &init, ctx->input_pages);
+
+ sc = FASTRPC_SCALARS(FASTRPC_RMID_INIT_CREATE, 4, 0);
+ if (init.attrs)
+ sc = FASTRPC_SCALARS(FASTRPC_RMID_INIT_CREATE_ATTR, 4, 0);
+ ctx->sc = sc;
+ ctx->args = args;
+ ctx->handle = FASTRPC_INIT_HANDLE;
+
+ return 0;
+
+err_free_inbuf:
+ kfree(ctx->inbuf);
+ ctx->inbuf = NULL;
+err_free_input_pages:
+ kfree(ctx->input_pages);
+ ctx->input_pages = NULL;
+err_free_args:
+ kfree(args);
+ return err;
+}
+
int fastrpc_prepare_args(struct fastrpc_invoke_context *ctx, char __user *argp)
{
int err;
@@ -569,6 +673,11 @@ int fastrpc_prepare_args(struct fastrpc_invoke_context *ctx, char __user *argp)
case FASTRPC_RMID_INVOKE_DYNAMIC:
err = fastrpc_prepare_args_invoke(ctx, argp);
break;
+ case FASTRPC_RMID_INIT_CREATE:
+ case FASTRPC_RMID_INIT_CREATE_ATTR:
+ ctx->pd = USER_PD;
+ err = fastrpc_prepare_args_init_create(ctx, argp);
+ break;
default:
return -EINVAL;
}
diff --git a/drivers/accel/qda/qda_fastrpc.h b/drivers/accel/qda/qda_fastrpc.h
index bcadf9437a36..a8deb7efec86 100644
--- a/drivers/accel/qda/qda_fastrpc.h
+++ b/drivers/accel/qda/qda_fastrpc.h
@@ -122,6 +122,27 @@ struct fastrpc_invoke_buf {
u32 pgidx;
};
+/**
+ * struct fastrpc_create_process_inbuf - Input buffer for process creation
+ *
+ * This structure defines the input buffer format for creating a new
+ * process on the remote DSP.
+ */
+struct fastrpc_create_process_inbuf {
+ /* Client identifier for the session */
+ int client_id;
+ /* Length of the process name string */
+ u32 namelen;
+ /* Length of the shell file */
+ u32 filelen;
+ /* Length of the pages list */
+ u32 pageslen;
+ /* Process attributes flags */
+ u32 attrs;
+ /* Length of the signature data */
+ u32 siglen;
+};
+
/**
* struct qda_msg - Message structure for FastRPC communication
*
@@ -226,6 +247,8 @@ struct fastrpc_invoke_context {
struct qda_gem_obj *msg_gem_obj;
/* DRM file private data */
struct drm_file *file_priv;
+ /* GEM object for PD initialization memory */
+ struct qda_gem_obj *init_mem_gem_obj;
/* Pointer to request buffer */
void *req;
/* Pointer to response buffer */
@@ -237,6 +260,8 @@ struct fastrpc_invoke_context {
/* Remote Method ID table - identifies initialization and control operations */
#define FASTRPC_RMID_INIT_ATTACH 0 /* Attach to DSP session */
#define FASTRPC_RMID_INIT_RELEASE 1 /* Release DSP session */
+#define FASTRPC_RMID_INIT_CREATE 6 /* Create DSP process */
+#define FASTRPC_RMID_INIT_CREATE_ATTR 7 /* Create DSP process with attributes */
#define FASTRPC_RMID_INVOKE_DYNAMIC 0xFFFFFFFF /* Dynamic method invocation */
/* Common handle for initialization operations */
@@ -244,6 +269,12 @@ struct fastrpc_invoke_context {
/* Protection Domain(PD) ids */
#define ROOT_PD (0)
+#define USER_PD (1)
+
+/* Number of arguments for process creation */
+#define FASTRPC_CREATE_PROCESS_NARGS 6
+/* Maximum initialization file size (4MB) */
+#define INIT_FILELEN_MAX (4 * 1024 * 1024)
/**
* fastrpc_context_free - Free an invocation context
diff --git a/drivers/accel/qda/qda_ioctl.c b/drivers/accel/qda/qda_ioctl.c
index e90aceabd30d..477112ad6664 100644
--- a/drivers/accel/qda/qda_ioctl.c
+++ b/drivers/accel/qda/qda_ioctl.c
@@ -122,7 +122,7 @@ static int fastrpc_invoke(int type, struct drm_device *dev, void *data,
struct fastrpc_invoke_context *ctx;
struct drm_gem_object *gem_obj;
int err;
- size_t hdr_size;
+ size_t hdr_size, initmem_size = 4 * 1024 * 1024;
err = qda_validate_and_get_context(dev, file_priv, &qdev, &qda_user);
if (err)
@@ -142,6 +142,22 @@ static int fastrpc_invoke(int type, struct drm_device *dev, void *data,
ctx->file_priv = file_priv;
ctx->client_id = qda_user->client_id;
+ if (type == FASTRPC_RMID_INIT_CREATE) {
+ struct drm_gem_object *gem_obj;
+
+ gem_obj = qda_gem_create_object(qdev->drm_dev, qdev->drm_priv->iommu_mgr,
+ initmem_size, file_priv);
+ if (IS_ERR(gem_obj)) {
+ err = PTR_ERR(gem_obj);
+ goto err_context_free;
+ }
+
+ ctx->init_mem_gem_obj = to_qda_gem_obj(gem_obj);
+ qda_user->init_mem_gem_obj = ctx->init_mem_gem_obj;
+ } else if (type == FASTRPC_RMID_INIT_RELEASE) {
+ ctx->init_mem_gem_obj = qda_user->init_mem_gem_obj;
+ }
+
err = fastrpc_prepare_args(ctx, (char __user *)data);
if (err)
goto err_context_free;
@@ -177,6 +193,11 @@ static int fastrpc_invoke(int type, struct drm_device *dev, void *data,
goto err_context_free;
err_context_free:
+ if (type == FASTRPC_RMID_INIT_RELEASE && qda_user->init_mem_gem_obj) {
+ drm_gem_object_put(&qda_user->init_mem_gem_obj->base);
+ qda_user->init_mem_gem_obj = NULL;
+ }
+
fastrpc_context_put_id(ctx, qdev);
kref_put(&ctx->refcount, fastrpc_context_free);
@@ -197,3 +218,8 @@ int qda_ioctl_invoke(struct drm_device *dev, void *data, struct drm_file *file_p
{
return fastrpc_invoke(FASTRPC_RMID_INVOKE_DYNAMIC, dev, data, file_priv);
}
+
+int qda_ioctl_create(struct drm_device *dev, void *data, struct drm_file *file_priv)
+{
+ return fastrpc_invoke(FASTRPC_RMID_INIT_CREATE, dev, data, file_priv);
+}
diff --git a/drivers/accel/qda/qda_ioctl.h b/drivers/accel/qda/qda_ioctl.h
index e186c5183171..181ed50b19dc 100644
--- a/drivers/accel/qda/qda_ioctl.h
+++ b/drivers/accel/qda/qda_ioctl.h
@@ -76,4 +76,17 @@ int fastrpc_release_current_dsp_process(struct qda_dev *qdev, struct drm_file *f
*/
int qda_ioctl_invoke(struct drm_device *dev, void *data, struct drm_file *file_priv);
+/**
+ * qda_ioctl_create - Create a DSP process
+ * @dev: DRM device structure
+ * @data: User-space data containing process creation parameters
+ * @file_priv: DRM file private data
+ *
+ * This IOCTL handler creates a new process on the DSP, loading the
+ * specified executable and initializing its runtime environment.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int qda_ioctl_create(struct drm_device *dev, void *data, struct drm_file *file_priv);
+
#endif /* _QDA_IOCTL_H */
diff --git a/include/uapi/drm/qda_accel.h b/include/uapi/drm/qda_accel.h
index 01072a9d0a91..2b7f500db52c 100644
--- a/include/uapi/drm/qda_accel.h
+++ b/include/uapi/drm/qda_accel.h
@@ -22,7 +22,8 @@ extern "C" {
#define DRM_QDA_GEM_CREATE 0x01
#define DRM_QDA_GEM_MMAP_OFFSET 0x02
#define DRM_QDA_INIT_ATTACH 0x03
-/* Indexes 0x04 to 0x06 are reserved for other requests */
+#define DRM_QDA_INIT_CREATE 0x04
+/* Indexes 0x05-0x06 are reserved for other requests */
#define DRM_QDA_INVOKE 0x07
/*
@@ -38,6 +39,8 @@ extern "C" {
#define DRM_IOCTL_QDA_GEM_MMAP_OFFSET DRM_IOWR(DRM_COMMAND_BASE + DRM_QDA_GEM_MMAP_OFFSET, \
struct drm_qda_gem_mmap_offset)
#define DRM_IOCTL_QDA_INIT_ATTACH DRM_IO(DRM_COMMAND_BASE + DRM_QDA_INIT_ATTACH)
+#define DRM_IOCTL_QDA_INIT_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_QDA_INIT_CREATE, \
+ struct qda_init_create)
#define DRM_IOCTL_QDA_INVOKE DRM_IOWR(DRM_COMMAND_BASE + DRM_QDA_INVOKE, \
struct qda_invoke_args)
@@ -116,6 +119,30 @@ struct qda_invoke_args {
__u64 args;
};
+/**
+ * struct qda_init_create - Accelerator process initialization parameters
+ * @filelen: Length of the ELF file in bytes
+ * @filefd: File descriptor containing the ELF file
+ * @attrs: Process attributes flags
+ * @siglen: Length of signature data in bytes
+ * @file: Pointer to ELF file data if not using filefd
+ *
+ * This structure is used with DRM_IOCTL_QDA_INIT_CREATE to initialize
+ * a new process on the accelerator. The process code is provided either
+ * via a file descriptor (filefd, typically a GEM object) or a direct
+ * pointer (file). Set file to 0 if using filefd.
+ *
+ * The attrs field contains bit flags for debug mode, privileged execution,
+ * and other process attributes.
+ */
+struct qda_init_create {
+ __u32 filelen;
+ __s32 filefd;
+ __u32 attrs;
+ __u32 siglen;
+ __u64 file;
+};
+
#if defined(__cplusplus)
}
#endif
--
2.34.1