Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions drivers/tee/tee_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,42 @@ static int tee_ioctl_shm_alloc(struct tee_context *ctx,
return ret;
}

static int tee_ioctl_shm_register_fd(struct tee_context *ctx,
struct tee_ioctl_shm_register_fd_data __user *udata)
{
struct tee_ioctl_shm_register_fd_data data;
struct tee_shm *shm;
long ret;

if (copy_from_user(&data, udata, sizeof(data)))
return -EFAULT;

/* Currently no input flags are supported */
if (data.flags)
return -EINVAL;

shm = tee_shm_register_fd(ctx, data.fd);
if (IS_ERR_OR_NULL(shm))
return -EINVAL;

data.id = shm->id;
data.flags = shm->flags;
data.size = shm->size;

if (copy_to_user(udata, &data, sizeof(data)))
ret = -EFAULT;
else
ret = tee_shm_get_fd(shm);

/*
* When user space closes the file descriptor the shared memory
* should be freed or if tee_shm_get_fd() failed then it will
* be freed immediately.
*/
tee_shm_put(shm);
return ret;
}

static int params_from_user(struct tee_context *ctx, struct tee_param *params,
size_t num_params,
struct tee_ioctl_param __user *uparams)
Expand Down Expand Up @@ -599,6 +635,8 @@ static long tee_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
return tee_ioctl_version(ctx, uarg);
case TEE_IOC_SHM_ALLOC:
return tee_ioctl_shm_alloc(ctx, uarg);
case TEE_IOC_SHM_REGISTER_FD:
return tee_ioctl_shm_register_fd(ctx, uarg);
case TEE_IOC_OPEN_SESSION:
return tee_ioctl_open_session(ctx, uarg);
case TEE_IOC_INVOKE:
Expand Down
136 changes: 127 additions & 9 deletions drivers/tee/tee_shm.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,25 +20,44 @@
#include <linux/tee_drv.h>
#include "tee_private.h"

/* extra references appended to shm object for registered shared memory */
struct tee_shm_dmabuf_ref {
struct tee_shm shm;
struct dma_buf *dmabuf;
struct dma_buf_attachment *attach;
struct sg_table *sgt;
};

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be better to have a struct like:

struct tee_shm_dmabuf_ref {
     struct tee_shm;
     struct dma_buf *dmabuf;
     struct dma_buf_attachment *attach;
     struct sg_table *sgt
};

and use container_of to go from a pointer to a struct tee_shm to a pointer to a struct tee_shm_dmabuf_ref.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok


static void tee_shm_release(struct tee_shm *shm)
{
struct tee_device *teedev = shm->teedev;
struct tee_shm_pool_mgr *poolm;

mutex_lock(&teedev->mutex);
idr_remove(&teedev->idr, shm->id);
if (shm->ctx)
list_del(&shm->link);
mutex_unlock(&teedev->mutex);

if (shm->flags & TEE_SHM_DMA_BUF)
poolm = &teedev->pool->dma_buf_mgr;
else
poolm = &teedev->pool->private_mgr;
if (shm->flags & TEE_SHM_EXT_DMA_BUF) {
struct tee_shm_dmabuf_ref *ref;

poolm->ops->free(poolm, shm);
kfree(shm);
ref = container_of(shm, struct tee_shm_dmabuf_ref, shm);
dma_buf_unmap_attachment(ref->attach, ref->sgt,
DMA_BIDIRECTIONAL);
dma_buf_detach(shm->dmabuf, ref->attach);
dma_buf_put(ref->dmabuf);
} else {
struct tee_shm_pool_mgr *poolm;

if (shm->flags & TEE_SHM_DMA_BUF)
poolm = &teedev->pool->dma_buf_mgr;
else
poolm = &teedev->pool->private_mgr;

poolm->ops->free(poolm, shm);
}

kfree(shm);
tee_device_put(teedev);
}

Expand Down Expand Up @@ -190,17 +209,110 @@ struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags)
}
EXPORT_SYMBOL_GPL(tee_shm_alloc);

struct tee_shm *tee_shm_register_fd(struct tee_context *ctx, int fd)
{
struct tee_shm_dmabuf_ref *ref;
void *rc;
DEFINE_DMA_BUF_EXPORT_INFO(exp_info);

if (!tee_device_get(ctx->teedev))
return ERR_PTR(-EINVAL);

ref = kzalloc(sizeof(*ref), GFP_KERNEL);
if (!ref) {
rc = ERR_PTR(-ENOMEM);
goto err;
}

ref->shm.ctx = ctx;
ref->shm.teedev = ctx->teedev;
ref->shm.id = -1;

ref->dmabuf = dma_buf_get(fd);
if (!ref->dmabuf) {
rc = ERR_PTR(-EINVAL);
goto err;
}

ref->attach = dma_buf_attach(ref->dmabuf, &ref->shm.teedev->dev);
if (IS_ERR_OR_NULL(ref->attach)) {
rc = ERR_PTR(-EINVAL);
goto err;
}

ref->sgt = dma_buf_map_attachment(ref->attach, DMA_BIDIRECTIONAL);
if (IS_ERR_OR_NULL(ref->sgt)) {
rc = ERR_PTR(-EINVAL);
goto err;
}

if (sg_nents(ref->sgt->sgl) != 1) {
rc = ERR_PTR(-EINVAL);
goto err;
}

ref->shm.paddr = sg_dma_address(ref->sgt->sgl);
ref->shm.size = sg_dma_len(ref->sgt->sgl);
ref->shm.flags = TEE_SHM_DMA_BUF | TEE_SHM_EXT_DMA_BUF;

mutex_lock(&ref->shm.teedev->mutex);
ref->shm.id = idr_alloc(&ref->shm.teedev->idr, &ref->shm,
1, 0, GFP_KERNEL);
mutex_unlock(&ref->shm.teedev->mutex);
if (ref->shm.id < 0) {
rc = ERR_PTR(ref->shm.id);
goto err;
}

/* export a dmabuf to later get a userland ref */
exp_info.ops = &tee_shm_dma_buf_ops;
exp_info.size = ref->shm.size;
exp_info.flags = O_RDWR;
exp_info.priv = &ref->shm;

ref->shm.dmabuf = dma_buf_export(&exp_info);
if (IS_ERR(ref->shm.dmabuf)) {
rc = ERR_PTR(-EINVAL);
goto err;
}

mutex_lock(&ref->shm.teedev->mutex);
list_add_tail(&ref->shm.link, &ctx->list_shm);
mutex_unlock(&ref->shm.teedev->mutex);

return &ref->shm;

err:
if (ref) {
if (ref->shm.id >= 0) {
mutex_lock(&ctx->teedev->mutex);
idr_remove(&ctx->teedev->idr, ref->shm.id);
mutex_unlock(&ctx->teedev->mutex);
}
if (ref->sgt)
dma_buf_unmap_attachment(ref->attach, ref->sgt,
DMA_BIDIRECTIONAL);
if (ref->attach)
dma_buf_detach(ref->dmabuf, ref->attach);
if (ref->dmabuf)
dma_buf_put(ref->dmabuf);
}
kfree(ref);
tee_device_put(ctx->teedev);
return rc;
}
EXPORT_SYMBOL_GPL(tee_shm_register_fd);

/**
* tee_shm_get_fd() - Increase reference count and return file descriptor
* @shm: Shared memory handle
* @returns user space file descriptor to shared memory
*/
int tee_shm_get_fd(struct tee_shm *shm)
{
u32 req_flags = TEE_SHM_MAPPED | TEE_SHM_DMA_BUF;
int fd;

if ((shm->flags & req_flags) != req_flags)
if (!(shm->flags & TEE_SHM_DMA_BUF))
return -EINVAL;

fd = dma_buf_fd(shm->dmabuf, O_CLOEXEC);
Expand Down Expand Up @@ -238,6 +350,8 @@ EXPORT_SYMBOL_GPL(tee_shm_free);
*/
int tee_shm_va2pa(struct tee_shm *shm, void *va, phys_addr_t *pa)
{
if (!(shm->flags & TEE_SHM_MAPPED))
return -EINVAL;
/* Check that we're in the range of the shm */
if ((char *)va < (char *)shm->kaddr)
return -EINVAL;
Expand All @@ -258,6 +372,8 @@ EXPORT_SYMBOL_GPL(tee_shm_va2pa);
*/
int tee_shm_pa2va(struct tee_shm *shm, phys_addr_t pa, void **va)
{
if (!(shm->flags & TEE_SHM_MAPPED))
return -EINVAL;
/* Check that we're in the range of the shm */
if (pa < shm->paddr)
return -EINVAL;
Expand All @@ -284,6 +400,8 @@ EXPORT_SYMBOL_GPL(tee_shm_pa2va);
*/
void *tee_shm_get_va(struct tee_shm *shm, size_t offs)
{
if (!(shm->flags & TEE_SHM_MAPPED))
return ERR_PTR(-EINVAL);
if (offs >= shm->size)
return ERR_PTR(-EINVAL);
return (char *)shm->kaddr + offs;
Expand Down
15 changes: 13 additions & 2 deletions include/linux/tee_drv.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@
* specific TEE driver.
*/

#define TEE_SHM_MAPPED 0x1 /* Memory mapped by the kernel */
#define TEE_SHM_DMA_BUF 0x2 /* Memory with dma-buf handle */
#define TEE_SHM_MAPPED BIT(0) /* Memory mapped by the kernel */
#define TEE_SHM_DMA_BUF BIT(1) /* Memory with dma-buf handle */
#define TEE_SHM_EXT_DMA_BUF BIT(2) /* Memory with dma-buf handle */

struct tee_device;
struct tee_shm;
Expand Down Expand Up @@ -211,6 +212,16 @@ void *tee_get_drvdata(struct tee_device *teedev);
*/
struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags);

/**
* tee_shm_register_fd() - Register shared memory from file descriptor
*
* @ctx: Context that allocates the shared memory
* @fd: shared memory file descriptor reference.
*
* @returns a pointer to 'struct tee_shm'
*/
struct tee_shm *tee_shm_register_fd(struct tee_context *ctx, int fd);

/**
* tee_shm_free() - Free shared memory
* @shm: Handle to shared memory to free
Expand Down
29 changes: 29 additions & 0 deletions include/uapi/linux/tee.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,35 @@ struct tee_ioctl_shm_alloc_data {
#define TEE_IOC_SHM_ALLOC _IOWR(TEE_IOC_MAGIC, TEE_IOC_BASE + 1, \
struct tee_ioctl_shm_alloc_data)

/**
* struct tee_ioctl_shm_register_fd_data - Shared memory registering argument
* @fd: [in] file descriptor identifying the shared memory
* @size: [out] Size of shared memory to allocate
* @flags: [in] Flags to/from allocation.
* @id: [out] Identifier of the shared memory
*
* The flags field should currently be zero as input. Updated by the call
* with actual flags as defined by TEE_IOCTL_SHM_* above.
* This structure is used as argument for TEE_IOC_SHM_ALLOC below.
*/
struct tee_ioctl_shm_register_fd_data {
__s64 fd;
__u64 size;
__u32 flags;
__s32 id;
} __aligned(8);

/**
* TEE_IOC_SHM_REGISTER_FD - register a shared memory from a file descriptor
*
* Returns a file descriptor on success or < 0 on failure
*
* The returned file descriptor refers to the shared memory object in kernel
* land. The shared memory is freed when the descriptor is closed.
*/
#define TEE_IOC_SHM_REGISTER_FD _IOWR(TEE_IOC_MAGIC, TEE_IOC_BASE + 8, \
struct tee_ioctl_shm_register_fd_data)

/**
* struct tee_ioctl_buf_data - Variable sized buffer
* @buf_ptr: [in] A __user pointer to a buffer
Expand Down