An application needs a process on the DSP to run its own code, and
that process has to be torn down when the application goes away. Add
the requests that create and release one, and the ioctl that creates
it.

Creating a process hands the DSP an ELF image, which the caller names
by buffer handle, together with a block of memory the DSP loads the
image into. That memory is allocated per session and kept until the
process is released, since the DSP uses it for as long as the process
runs. A process created again on the same session replaces the first,
so the memory held for the previous one is dropped first.

A signed process runs with the DSP's trust in the signature, so
creating one requires CAP_SYS_ADMIN; only an unsigned process may be
created without it.

Releasing is sent when a session closes, if the device is still there
to receive it. Once the DSP has torn the process down, the memory held
for it is freed.

Every request after a successful create is directed at the new
protection domain, so the session remembers which one it is in and the
message built for a request carries it. Requests addressed to the
initialisation handle carry session ID 0, since the session has no
process yet to identify.

Signed-off-by: Ekansh Gupta <[email protected]>
---
 drivers/accel/qda/qda_drv.c     |  16 +++-
 drivers/accel/qda/qda_drv.h     |  11 +++
 drivers/accel/qda/qda_fastrpc.c | 202 ++++++++++++++++++++++++++++++++++++++--
 drivers/accel/qda/qda_fastrpc.h |  48 +++++++++-
 drivers/accel/qda/qda_ioctl.c   |  59 +++++++++++-
 drivers/accel/qda/qda_ioctl.h   |   1 +
 include/uapi/drm/qda_accel.h    |  28 ++++++
 7 files changed, 354 insertions(+), 11 deletions(-)

diff --git a/drivers/accel/qda/qda_drv.c b/drivers/accel/qda/qda_drv.c
index 7d44342d3f67..983c6a3dcfb4 100644
--- a/drivers/accel/qda/qda_drv.c
+++ b/drivers/accel/qda/qda_drv.c
@@ -13,6 +13,7 @@
 
 #include "qda_drv.h"
 #include "qda_fastrpc.h"
+#include "qda_gem.h"
 #include "qda_ioctl.h"
 #include "qda_prime.h"
 
@@ -72,6 +73,9 @@ static void qda_session_release(struct kref *ref)
 
        qda_session_msg_buf_pool_destroy(session);
 
+       if (session->init_mem_gem_obj)
+               drm_gem_object_put(&session->init_mem_gem_obj->base);
+
        if (session->assigned_iommu_dev)
                qda_memory_manager_release_device(session->assigned_iommu_dev);
 
@@ -101,7 +105,16 @@ EXPORT_SYMBOL_FOR_MODULES(qda_session_put, 
"qda_fastrpc_shim");
 
 static void qda_postclose(struct drm_device *dev, struct drm_file *file)
 {
-       qda_session_put(file->driver_priv);
+       struct qda_session *session = file->driver_priv;
+       int idx;
+
+       /* Only a session that created a process has one to release */
+       if (READ_ONCE(session->init_mem_gem_obj) && drm_dev_enter(dev, &idx)) {
+               qda_release_dsp_process(session->qda_dev, session);
+               drm_dev_exit(idx);
+       }
+
+       qda_session_put(session);
 }
 
 DEFINE_DRM_ACCEL_FOPS(qda_accel_fops);
@@ -111,6 +124,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_REMOTE_INVOKE, qda_ioctl_invoke, 0),
+       DRM_IOCTL_DEF_DRV(QDA_REMOTE_SESSION_CREATE, qda_ioctl_init_create, 0),
 };
 
 static const struct drm_driver qda_drm_driver = {
diff --git a/drivers/accel/qda/qda_drv.h b/drivers/accel/qda/qda_drv.h
index 80a8569b311a..2f98fd83fdda 100644
--- a/drivers/accel/qda/qda_drv.h
+++ b/drivers/accel/qda/qda_drv.h
@@ -56,6 +56,14 @@ struct qda_session {
        struct qda_iommu_device *assigned_iommu_dev;
        /** @remote_session_id: Identifies this session to the DSP */
        int remote_session_id;
+       /** @pd: Protection domain this session's requests are directed at */
+       int pd;
+       /**
+        * @init_mem_gem_obj: Memory the DSP loaded this session's process
+        * into, held for as long as the process runs. Swapped with xchg(), as
+        * ioctls on one file can run concurrently.
+        */
+       struct qda_gem_obj *init_mem_gem_obj;
        /**
         * @msg_buf_pool: Invoke header buffers this session is done with,
         * kept for its later invocations to reuse.
@@ -137,4 +145,7 @@ struct qda_session *qda_session_alloc(struct qda_dev *qdev);
 void qda_session_get(struct qda_session *session);
 void qda_session_put(struct qda_session *session);
 
+/* DSP process management */
+int qda_release_dsp_process(struct qda_dev *qdev, struct qda_session *session);
+
 #endif /* __QDA_DRV_H__ */
diff --git a/drivers/accel/qda/qda_fastrpc.c b/drivers/accel/qda/qda_fastrpc.c
index 693dd6f035d2..4672405ea4b7 100644
--- a/drivers/accel/qda/qda_fastrpc.c
+++ b/drivers/accel/qda/qda_fastrpc.c
@@ -1,9 +1,12 @@
 // SPDX-License-Identifier: GPL-2.0-only
 // Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
 #include <linux/bitfield.h>
+#include <linux/capability.h>
 #include <linux/dma-mapping.h>
 #include <linux/mm.h>
+#include <linux/sched.h>
 #include <linux/slab.h>
+#include <linux/string.h>
 #include <linux/uaccess.h>
 #include <drm/drm_gem.h>
 #include <drm/drm_print.h>
@@ -245,6 +248,9 @@ void qda_fastrpc_context_free(struct kref *ref)
        if (ctx->msg_gem_obj)
                qda_session_msg_buf_put(ctx->session, &ctx->msg_gem_obj->base);
 
+       if (ctx->init_mem_gem_obj)
+               drm_gem_object_put(&ctx->init_mem_gem_obj->base);
+
        if (ctx->session)
                qda_session_put(ctx->session);
 
@@ -301,10 +307,54 @@ struct qda_fastrpc_invoke_ctx 
*qda_fastrpc_context_alloc(struct qda_dev *qdev)
        return ctx;
 }
 
+/*
+ * Allocate one DMA-coherent object to hold every buffer the driver builds for
+ * one of its own requests, so they cost a single page rather than one each.
+ * The memory is cleared, so output slots start out clean.
+ */
+static struct qda_gem_obj *alloc_kernel_gem(struct qda_fastrpc_invoke_ctx 
*ctx, size_t size)
+{
+       struct qda_dev *qdev = ctx->qdev;
+       struct drm_gem_object *gem_obj;
+
+       gem_obj = qda_gem_create_object(&qdev->drm_dev, qdev->iommu_mgr, size, 
ctx->session);
+       if (IS_ERR(gem_obj))
+               return ERR_CAST(gem_obj);
+
+       memset(to_qda_gem_obj(gem_obj)->virt, 0, to_qda_gem_obj(gem_obj)->size);
+
+       return to_qda_gem_obj(gem_obj);
+}
+
+/* The context releases whatever has been allocated here if a later step fails 
*/
+static int alloc_kernel_args(struct qda_fastrpc_invoke_ctx *ctx, u32 nargs)
+{
+       ctx->args = kzalloc_objs(*ctx->args, nargs);
+       if (!ctx->args)
+               return -ENOMEM;
+
+       ctx->gem_objs = kzalloc_objs(*ctx->gem_objs, nargs);
+       if (!ctx->gem_objs)
+               return -ENOMEM;
+
+       return 0;
+}
+
+/* Describe the region at @ptr in @kernel_gem as argument @idx, taking a 
reference on it */
+static void assign_kernel_gem_arg(struct qda_fastrpc_invoke_ctx *ctx, int idx,
+                                 struct qda_gem_obj *kernel_gem, void *ptr, 
size_t len)
+{
+       ctx->args[idx].offset = (u8 *)ptr - (u8 *)kernel_gem->virt;
+       ctx->args[idx].length = len;
+       drm_gem_object_get(&kernel_gem->base);
+       ctx->gem_objs[idx] = &kernel_gem->base;
+}
+
 /*
  * Resolve the address of an in/out buffer argument. The buffer is either a
- * GEM object the legacy shim already resolved into ctx->gem_objs[i], or a GEM
- * handle looked up in the calling session's own handle table. For a handle,
+ * GEM object already resolved into ctx->gem_objs[i], by the legacy shim or by
+ * the driver for its own requests, or a GEM handle looked up in the calling
+ * session's own handle table. For a handle,
  * the offset and length come from user space and are checked against the
  * buffer's real size: a region described as larger than its buffer would let
  * the DSP read and write past the end of it, into whatever else is mapped in
@@ -321,7 +371,7 @@ static int process_msg_buffer(struct qda_fastrpc_invoke_ctx 
*ctx, int i,
        u64 vma_offset = 0;
        int err;
 
-       if (ctx->gem_objs[i]) {
+       if (ctx->gem_objs[i] && ctx->legacy) {
                /*
                 * The legacy ABI predates GEM handles, so the offset field
                 * holds an address in the caller's own mapping of the buffer,
@@ -341,6 +391,17 @@ static int process_msg_buffer(struct 
qda_fastrpc_invoke_ctx *ctx, int i,
                return 0;
        }
 
+       if (ctx->gem_objs[i]) {
+               /* Built by the driver, so the offset is a byte offset into the 
buffer */
+               qda_gem_obj = to_qda_gem_obj(ctx->gem_objs[i]);
+               unaligned_addr = qda_gem_obj->dma_addr + ctx->args[i].offset;
+               rpra[i].buf.addr = unaligned_addr;
+               pages[i].addr = unaligned_addr & PAGE_MASK;
+               pages[i].size = calculate_page_aligned_size(unaligned_addr, 
len);
+
+               return 0;
+       }
+
        if (!ctx->args[i].handle)
                return -EINVAL;
 
@@ -511,9 +572,14 @@ int qda_fastrpc_invoke_pack(struct qda_fastrpc_invoke_ctx 
*ctx, struct qda_msg *
        /* Ensure the payload is visible to the DSP before the doorbell */
        dma_wmb();
 
-       msg->fastrpc.remote_session_id = ctx->session->remote_session_id;
+       /* The session has no process yet to identify while one is being 
created */
+       if (ctx->handle == QDA_FASTRPC_INIT_HANDLE)
+               msg->fastrpc.remote_session_id = 0;
+       else
+               msg->fastrpc.remote_session_id = 
ctx->session->remote_session_id;
+
        msg->fastrpc.tid = current->pid;
-       msg->fastrpc.ctx = ctx->ctxid | QDA_USER_PD;
+       msg->fastrpc.ctx = ctx->ctxid | ctx->pd;
        msg->fastrpc.handle = ctx->handle;
        msg->fastrpc.sc = ctx->sc;
        msg->fastrpc.addr = msg->phys;
@@ -639,6 +705,9 @@ int qda_fastrpc_invoke_legacy(struct qda_dev *qdev, struct 
qda_session *session,
        /* Dropped by qda_fastrpc_context_free(), as for the accel UAPI path */
        qda_session_get(session);
        ctx->session = session;
+       ctx->type = QDA_FASTRPC_RMID_INVOKE_DYNAMIC;
+       ctx->legacy = true;
+       ctx->pd = READ_ONCE(session->pd);
        ctx->handle = handle;
        ctx->sc = sc;
 
@@ -688,6 +757,110 @@ int qda_fastrpc_invoke_legacy(struct qda_dev *qdev, 
struct qda_session *session,
        return err;
 }
 
+/*
+ * INIT_RELEASE packed layout:
+ *   [0] remote_session_id     sizeof(int)     IN
+ */
+static int qda_fastrpc_prepare_args_release_process(struct 
qda_fastrpc_invoke_ctx *ctx)
+{
+       struct qda_gem_obj *kernel_gem;
+       int err;
+
+       ctx->sc = QDA_FASTRPC_SCALARS(QDA_FASTRPC_RMID_INIT_RELEASE, 1, 0);
+       ctx->handle = QDA_FASTRPC_INIT_HANDLE;
+
+       err = alloc_kernel_args(ctx, 1);
+       if (err)
+               return err;
+
+       kernel_gem = alloc_kernel_gem(ctx, sizeof(int));
+       if (IS_ERR(kernel_gem))
+               return PTR_ERR(kernel_gem);
+
+       *(int *)kernel_gem->virt = ctx->session->remote_session_id;
+       assign_kernel_gem_arg(ctx, 0, kernel_gem, kernel_gem->virt, 
sizeof(int));
+       drm_gem_object_put(&kernel_gem->base);
+
+       return 0;
+}
+
+/*
+ * INIT_CREATE / INIT_CREATE_ATTR packed layout:
+ *   [0] inbuf sizeof(qda_fastrpc_create_process_inbuf)        IN
+ *   [1] comm  TASK_COMM_LEN                                   IN
+ *   [2] ELF   buffer named by the caller's handle, not packed IN
+ *   [3] pages sizeof(qda_phy_page), the process image memory  IN
+ *
+ * The attributes and signature length travel in @inbuf. The _ATTR method is
+ * used when any attribute is set, as in drivers/misc/fastrpc.c.
+ */
+static int qda_fastrpc_prepare_args_init_create(struct qda_fastrpc_invoke_ctx 
*ctx, void *argp)
+{
+       struct drm_qda_init_create *init = argp;
+       struct qda_fastrpc_create_process_inbuf *inbuf;
+       struct qda_dev *qdev = ctx->qdev;
+       struct drm_gem_object *gem_obj;
+       struct qda_gem_obj *kernel_gem;
+       u8 *gv;
+       int err;
+
+       const size_t off_inbuf = 0;
+       const size_t off_comm = off_inbuf + sizeof(*inbuf);
+       const size_t off_pages = off_comm + TASK_COMM_LEN;
+       const size_t total_size = off_pages + sizeof(struct qda_phy_page);
+
+       if (init->file || init->filelen > QDA_FASTRPC_INIT_FILELEN_MAX)
+               return -EINVAL;
+
+       /* A signed process runs with the DSP's trust in the signature */
+       if (!(init->attrs & QDA_INIT_ATTR_UNSIGNED_MODULE) && 
!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       /* The DSP loads the image into this memory and keeps using it while 
the process runs */
+       gem_obj = qda_gem_create_object(&qdev->drm_dev, qdev->iommu_mgr,
+                                       QDA_FASTRPC_INIT_FILELEN_MAX, 
ctx->session);
+       if (IS_ERR(gem_obj))
+               return PTR_ERR(gem_obj);
+
+       ctx->init_mem_gem_obj = to_qda_gem_obj(gem_obj);
+
+       ctx->sc = init->attrs ? 
QDA_FASTRPC_SCALARS(QDA_FASTRPC_RMID_INIT_CREATE_ATTR, 4, 0)
+                             : 
QDA_FASTRPC_SCALARS(QDA_FASTRPC_RMID_INIT_CREATE, 4, 0);
+       ctx->handle = QDA_FASTRPC_INIT_HANDLE;
+
+       err = alloc_kernel_args(ctx, 4);
+       if (err)
+               return err;
+
+       kernel_gem = alloc_kernel_gem(ctx, total_size);
+       if (IS_ERR(kernel_gem))
+               return PTR_ERR(kernel_gem);
+       gv = kernel_gem->virt;
+
+       inbuf = (struct qda_fastrpc_create_process_inbuf *)(gv + off_inbuf);
+       inbuf->remote_session_id = ctx->session->remote_session_id;
+       inbuf->filelen = init->filelen;
+       inbuf->pageslen = 1;
+       inbuf->attrs = init->attrs;
+       inbuf->siglen = init->siglen;
+
+       strscpy(gv + off_comm, current->comm, TASK_COMM_LEN);
+       inbuf->namelen = strlen(gv + off_comm) + 1;
+
+       setup_pages_from_gem_obj(ctx->init_mem_gem_obj, (struct qda_phy_page 
*)(gv + off_pages));
+
+       assign_kernel_gem_arg(ctx, 0, kernel_gem, inbuf, sizeof(*inbuf));
+       assign_kernel_gem_arg(ctx, 1, kernel_gem, gv + off_comm, 
inbuf->namelen);
+       /* The image starts at the beginning of the buffer the caller named */
+       ctx->args[2].length = init->filelen;
+       ctx->args[2].handle = init->filehandle;
+       assign_kernel_gem_arg(ctx, 3, kernel_gem, gv + off_pages, sizeof(struct 
qda_phy_page));
+
+       drm_gem_object_put(&kernel_gem->base);
+
+       return 0;
+}
+
 /*
  * INVOKE_DYNAMIC: the argument descriptors are supplied by user space, which
  * is also responsible for having imported every buffer to a GEM handle.
@@ -740,8 +913,23 @@ int qda_fastrpc_prepare_args(struct qda_fastrpc_invoke_ctx 
*ctx,
         */
        qda_session_get(session);
        ctx->session = session;
-
-       err = qda_fastrpc_prepare_args_invoke(ctx, argp);
+       ctx->pd = READ_ONCE(session->pd);
+
+       switch (ctx->type) {
+       case QDA_FASTRPC_RMID_INIT_RELEASE:
+               err = qda_fastrpc_prepare_args_release_process(ctx);
+               break;
+       case QDA_FASTRPC_RMID_INIT_CREATE:
+               /* The new process is in the user PD, and so is the request 
creating it */
+               ctx->pd = QDA_USER_PD;
+               err = qda_fastrpc_prepare_args_init_create(ctx, argp);
+               break;
+       case QDA_FASTRPC_RMID_INVOKE_DYNAMIC:
+               err = qda_fastrpc_prepare_args_invoke(ctx, argp);
+               break;
+       default:
+               return -EINVAL;
+       }
        if (err)
                return err;
 
diff --git a/drivers/accel/qda/qda_fastrpc.h b/drivers/accel/qda/qda_fastrpc.h
index 3243bd1065c6..eaebc4e99676 100644
--- a/drivers/accel/qda/qda_fastrpc.h
+++ b/drivers/accel/qda/qda_fastrpc.h
@@ -33,14 +33,32 @@ struct qda_session;
                                                 
QDA_REMOTE_SCALARS_INHANDLES(sc) + \
                                                 
QDA_REMOTE_SCALARS_OUTHANDLES(sc))
 
+/* Encodes a method ID and the in/out buffer counts; handle counts are zero */
+#define QDA_FASTRPC_SCALARS(method, in, out)   ((((method) & 0x1f) << 24) | \
+                                                (((in) & 0xff) << 16) |     \
+                                                (((out) & 0xff) << 8))
+
 /* FastRPC configuration constants */
 #define QDA_FASTRPC_ALIGN              128     /* Alignment the DSP expects of 
the payload */
 #define QDA_FASTRPC_MAX_HANDLELIST     16      /* Handles the DSP can ask to 
release */
 #define QDA_FASTRPC_MAX_CRCLIST                64      /* CRC list entries */
 
-/* Protection domain a session's invocations run in */
+/* Methods of the initialisation handle */
+#define QDA_FASTRPC_RMID_INIT_RELEASE          1
+#define QDA_FASTRPC_RMID_INIT_CREATE           6
+#define QDA_FASTRPC_RMID_INIT_CREATE_ATTR      7
+#define QDA_FASTRPC_RMID_INVOKE_DYNAMIC                0xFFFFFFFFU
+
+/* Handle the initialisation methods are addressed to */
+#define QDA_FASTRPC_INIT_HANDLE                0x1
+
+/* Protection domains a request can be directed at */
+#define QDA_ROOT_PD                    0
 #define QDA_USER_PD                    1
 
+/* Largest ELF image a process may be created from */
+#define QDA_FASTRPC_INIT_FILELEN_MAX   (4 * 1024 * 1024)
+
 /* Idle invoke header buffers a session keeps for reuse */
 #define QDA_MSG_BUF_POOL_MAX           8
 
@@ -111,6 +129,26 @@ struct qda_invoke_buf {
        u32 pgidx;
 };
 
+/**
+ * struct qda_fastrpc_create_process_inbuf - Input buffer for process creation
+ *
+ * Layout the DSP expects; do not reorder.
+ */
+struct qda_fastrpc_create_process_inbuf {
+       /** @remote_session_id: Session the process belongs to */
+       int remote_session_id;
+       /** @namelen: Length of the process name including the NUL */
+       u32 namelen;
+       /** @filelen: Length of the ELF image in bytes */
+       u32 filelen;
+       /** @pageslen: Number of physical page descriptors */
+       u32 pageslen;
+       /** @attrs: Process attribute flags */
+       u32 attrs;
+       /** @siglen: Length of the signature data in bytes */
+       u32 siglen;
+};
+
 /**
  * struct qda_fastrpc_msg - FastRPC wire message for remote invocations
  *
@@ -164,6 +202,12 @@ struct qda_fastrpc_invoke_ctx {
        u64 ctxid;
        /** @retval: Status the DSP reported, or an error if it never answered 
*/
        int retval;
+       /** @pd: Protection domain, OR'ed into the low bits of @ctxid when 
packed */
+       int pd;
+       /** @type: Method being invoked (QDA_FASTRPC_RMID_*) */
+       u32 type;
+       /** @legacy: Arguments came from the legacy shim, where offsets are 
user VAs */
+       bool legacy;
        /** @nscalars: Total number of scalar arguments */
        int nscalars;
        /** @nbufs: Number of buffer arguments; the rest are handle arguments */
@@ -180,6 +224,8 @@ struct qda_fastrpc_invoke_ctx {
        struct drm_gem_object **gem_objs;
        /** @msg_gem_obj: GEM object backing the message payload buffer */
        struct qda_gem_obj *msg_gem_obj;
+       /** @init_mem_gem_obj: Memory the DSP loads a new process into */
+       struct qda_gem_obj *init_mem_gem_obj;
        /** @pages: Array of physical page descriptors inside the payload */
        struct qda_phy_page *pages;
        /** @work: Completed once the DSP has answered */
diff --git a/drivers/accel/qda/qda_ioctl.c b/drivers/accel/qda/qda_ioctl.c
index e9f4151e344a..f51ad0bc9e9c 100644
--- a/drivers/accel/qda/qda_ioctl.c
+++ b/drivers/accel/qda/qda_ioctl.c
@@ -75,7 +75,22 @@ int qda_ioctl_gem_mmap_offset(struct drm_device *dev, void 
*data, struct drm_fil
        return drm_gem_dumb_map_offset(file_priv, dev, args->handle, 
&args->offset);
 }
 
-static int qda_fastrpc_invoke(void *data, struct qda_session *session)
+/*
+ * A process created again replaces the first, so the memory held for the
+ * previous one is dropped. Swapped atomically, as ioctls on one file can run
+ * concurrently.
+ */
+static void qda_session_set_init_mem(struct qda_session *session, struct 
qda_gem_obj *mem)
+{
+       struct qda_gem_obj *old;
+
+       drm_gem_object_get(&mem->base);
+       old = xchg(&session->init_mem_gem_obj, mem);
+       if (old)
+               drm_gem_object_put(&old->base);
+}
+
+static int qda_fastrpc_invoke(u32 type, void *data, struct qda_session 
*session)
 {
        struct qda_dev *qdev = session->qda_dev;
        struct qda_fastrpc_invoke_ctx *ctx;
@@ -85,11 +100,26 @@ static int qda_fastrpc_invoke(void *data, struct 
qda_session *session)
        if (IS_ERR(ctx))
                return PTR_ERR(ctx);
 
+       ctx->type = type;
+
        err = qda_fastrpc_prepare_args(ctx, session, data);
        if (err)
                goto out_put;
 
        err = qda_fastrpc_invoke_common(qdev, ctx);
+       if (err)
+               goto out_put;
+
+       /*
+        * The session only changes once the DSP has accepted the request, so a
+        * failed create leaves the previous process, if any, as it was. The
+        * memory is freed with the session, which the invocation keeps alive
+        * until it is done, so a release needs no reference of its own.
+        */
+       if (type == QDA_FASTRPC_RMID_INIT_CREATE) {
+               qda_session_set_init_mem(session, ctx->init_mem_gem_obj);
+               WRITE_ONCE(session->pd, QDA_USER_PD);
+       }
 
 out_put:
        kref_put(&ctx->refcount, qda_fastrpc_context_free);
@@ -97,6 +127,31 @@ static int qda_fastrpc_invoke(void *data, struct 
qda_session *session)
        return err;
 }
 
+/**
+ * qda_ioctl_init_create() - Create a DSP process
+ * @dev: DRM device structure
+ * @data: User-space data (struct drm_qda_init_create)
+ * @file_priv: DRM file private data
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int qda_ioctl_init_create(struct drm_device *dev, void *data, struct drm_file 
*file_priv)
+{
+       return qda_fastrpc_invoke(QDA_FASTRPC_RMID_INIT_CREATE, data, 
file_priv->driver_priv);
+}
+
+/**
+ * qda_release_dsp_process() - Tear down a session's DSP process
+ * @qdev: QDA device structure
+ * @session: Session whose process is released
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int qda_release_dsp_process(struct qda_dev *qdev, struct qda_session *session)
+{
+       return qda_fastrpc_invoke(QDA_FASTRPC_RMID_INIT_RELEASE, NULL, session);
+}
+
 /**
  * qda_ioctl_invoke() - Run a method on the DSP
  * @dev: DRM device structure
@@ -107,5 +162,5 @@ static int qda_fastrpc_invoke(void *data, struct 
qda_session *session)
  */
 int qda_ioctl_invoke(struct drm_device *dev, void *data, struct drm_file 
*file_priv)
 {
-       return qda_fastrpc_invoke(data, file_priv->driver_priv);
+       return qda_fastrpc_invoke(QDA_FASTRPC_RMID_INVOKE_DYNAMIC, data, 
file_priv->driver_priv);
 }
diff --git a/drivers/accel/qda/qda_ioctl.h b/drivers/accel/qda/qda_ioctl.h
index 3bb9cfd98370..da67f863c03c 100644
--- a/drivers/accel/qda/qda_ioctl.h
+++ b/drivers/accel/qda/qda_ioctl.h
@@ -12,5 +12,6 @@ int qda_ioctl_query(struct drm_device *dev, void *data, 
struct drm_file *file_pr
 int qda_ioctl_gem_create(struct drm_device *dev, void *data, struct drm_file 
*file_priv);
 int qda_ioctl_gem_mmap_offset(struct drm_device *dev, void *data, struct 
drm_file *file_priv);
 int qda_ioctl_invoke(struct drm_device *dev, void *data, struct drm_file 
*file_priv);
+int qda_ioctl_init_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 c21bc5b2446c..75e67c5f5923 100644
--- a/include/uapi/drm/qda_accel.h
+++ b/include/uapi/drm/qda_accel.h
@@ -16,6 +16,7 @@ extern "C" {
 #define DRM_QDA_GEM_CREATE             0x01
 #define DRM_QDA_GEM_MMAP_OFFSET                0x02
 #define DRM_QDA_REMOTE_INVOKE          0x03
+#define DRM_QDA_REMOTE_SESSION_CREATE  0x04
 
 #define DRM_IOCTL_QDA_QUERY            DRM_IOWR(DRM_COMMAND_BASE + 
DRM_QDA_QUERY, \
                                         struct drm_qda_query)
@@ -25,6 +26,9 @@ extern "C" {
                                         struct drm_qda_gem_mmap_offset)
 #define DRM_IOCTL_QDA_REMOTE_INVOKE    DRM_IOWR(DRM_COMMAND_BASE + 
DRM_QDA_REMOTE_INVOKE, \
                                         struct drm_qda_invoke_args)
+#define DRM_IOCTL_QDA_REMOTE_SESSION_CREATE                                    
\
+       DRM_IOWR(DRM_COMMAND_BASE + DRM_QDA_REMOTE_SESSION_CREATE,              
\
+                struct drm_qda_init_create)
 
 /* Query type definitions for drm_qda_query */
 #define QDA_QUERY_DSP_NAME             1
@@ -109,6 +113,30 @@ struct drm_qda_invoke_args {
        __u64 args;
 };
 
+/* Process attribute flags for drm_qda_init_create.attrs */
+#define QDA_INIT_ATTR_UNSIGNED_MODULE  (1U << 3)
+
+/**
+ * struct drm_qda_init_create - DSP process creation parameters
+ * @filelen: Length of the ELF image in bytes
+ * @filehandle: Handle of the buffer holding the ELF image
+ * @attrs: Process attribute flags
+ * @siglen: Length of the signature data in bytes
+ * @file: Reserved, must be zero
+ *
+ * Creates a process on the DSP for this file to run in. Without
+ * QDA_INIT_ATTR_UNSIGNED_MODULE in @attrs the process is created signed, which
+ * requires CAP_SYS_ADMIN. Creating a process again on the same file replaces
+ * the first.
+ */
+struct drm_qda_init_create {
+       __u32 filelen;
+       __u32 filehandle;
+       __u32 attrs;
+       __u32 siglen;
+       __u64 file;
+};
+
 #if defined(__cplusplus)
 }
 #endif

-- 
2.34.1

Reply via email to