Hello Jens,

On 19 March 2015 at 15:30, Jérôme Forissier <jerome.forissier@linaro.org> wrote:


On 03/19/2015 11:23 AM, Jens Wiklander wrote:
> Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
> ---
> The generic TEE subsystem compiles and the devices are created when the
> kernel boots. Apart from that nothing is tested. I've added a stubbed
> OP-TEE driver which registers two devices where one is supposed to be used
> by tee-supplicant and the other by clients.
>
> There's two kinds of shared memory, global dma-buf based, and driver
> private without dma-buf. The global dma-buf based shared memory is intended
> to be used when sharing memory from user space to Secure OS, typically
> what's passed in memrefs. The driver private shared memory is only shared
> between the driver and Secure OS, this is typically where pointers to the
> other arguments are stored to prevent user space from both changing
> pointers and also to not leak kernel pointers to user space. The driver
> private shared memory is expected to be only small buffers < 4KiB.
>
> ---
>  drivers/Kconfig                |   2 +
>  drivers/Makefile               |   1 +
>  drivers/sec-hw/Kconfig         |  33 +++++
>  drivers/sec-hw/Makefile        |   3 +
>  drivers/sec-hw/optee/Kconfig   |  22 ++++
>  drivers/sec-hw/optee/Makefile  |   1 +
>  drivers/sec-hw/optee/optee.c   | 174 +++++++++++++++++++++++++
>  drivers/sec-hw/tee.c           | 233 ++++++++++++++++++++++++++++++++++
>  drivers/sec-hw/tee_private.h   |  39 ++++++
>  drivers/sec-hw/tee_shm.c       | 279 +++++++++++++++++++++++++++++++++++++++++
>  include/linux/sec-hw/tee_drv.h | 155 +++++++++++++++++++++++
>  11 files changed, 942 insertions(+)
>  create mode 100644 drivers/sec-hw/Kconfig
>  create mode 100644 drivers/sec-hw/Makefile
>  create mode 100644 drivers/sec-hw/optee/Kconfig
>  create mode 100644 drivers/sec-hw/optee/Makefile
>  create mode 100644 drivers/sec-hw/optee/optee.c
>  create mode 100644 drivers/sec-hw/tee.c
>  create mode 100644 drivers/sec-hw/tee_private.h
>  create mode 100644 drivers/sec-hw/tee_shm.c
>  create mode 100644 include/linux/sec-hw/tee_drv.h
>
> diff --git a/drivers/Kconfig b/drivers/Kconfig
> index c0cc96b..4dd892b 100644
> --- a/drivers/Kconfig
> +++ b/drivers/Kconfig
> @@ -182,4 +182,6 @@ source "drivers/thunderbolt/Kconfig"
>
>  source "drivers/android/Kconfig"
>
> +source "drivers/sec-hw/Kconfig"
> +
>  endmenu
> diff --git a/drivers/Makefile b/drivers/Makefile
> index 527a6da..f4403d1 100644
> --- a/drivers/Makefile
> +++ b/drivers/Makefile
> @@ -165,3 +165,4 @@ obj-$(CONFIG_RAS)         += ras/
>  obj-$(CONFIG_THUNDERBOLT)    += thunderbolt/
>  obj-$(CONFIG_CORESIGHT)              += coresight/
>  obj-$(CONFIG_ANDROID)                += android/
> +obj-y                                += sec-hw/
> diff --git a/drivers/sec-hw/Kconfig b/drivers/sec-hw/Kconfig
> new file mode 100644
> index 0000000..ad2e5db
> --- /dev/null
> +++ b/drivers/sec-hw/Kconfig
> @@ -0,0 +1,33 @@
> +#
> +# Copyright (c) 2015, Linaro Limited
> +#
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License Version 2 as
> +# published by the Free Software Foundation.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License along
> +# with this program.  If not, see <http://www.gnu.org/licenses/>
> +
> +# Generic Trusted Execution Environment Configuration
> +config TEE_GENERIC
> +     bool "Trusted Execution Environment support"
> +     default n
> +     select DMA_SHARED_BUFFER
> +     help
> +       This implements a generic interface towards a Trusted Execution
> +       Environment (TEE).
> +
> +if TEE_GENERIC
> +
> +menu "TEE drivers"
> +
> +source "drivers/sec-hw/optee/Kconfig"
> +
> +endmenu
> +
> +endif
> diff --git a/drivers/sec-hw/Makefile b/drivers/sec-hw/Makefile
> new file mode 100644
> index 0000000..1c3d990
> --- /dev/null
> +++ b/drivers/sec-hw/Makefile
> @@ -0,0 +1,3 @@
> +obj-$(CONFIG_TEE_GENERIC) += tee.o
> +obj-$(CONFIG_TEE_GENERIC) += tee_shm.o
> +obj-$(CONFIG_TEE_OPTEE) += optee/
> diff --git a/drivers/sec-hw/optee/Kconfig b/drivers/sec-hw/optee/Kconfig
> new file mode 100644
> index 0000000..e7d639e
> --- /dev/null
> +++ b/drivers/sec-hw/optee/Kconfig
> @@ -0,0 +1,22 @@
> +#
> +# Copyright (c) 2015, Linaro Limited
> +#
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License Version 2 as
> +# published by the Free Software Foundation.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License along
> +# with this program.  If not, see <http://www.gnu.org/licenses/>
> +
> +# OP-TEE Trusted Execution Environment Configuration
> +config TEE_OPTEE
> +     tristate "OP-TEE"
> +     default n
> +     depends on TEE_GENERIC
> +     help
> +       This implements the OP-TEE Trusted Execution Environment (TEE) driver.
> diff --git a/drivers/sec-hw/optee/Makefile b/drivers/sec-hw/optee/Makefile
> new file mode 100644
> index 0000000..11a3dfc
> --- /dev/null
> +++ b/drivers/sec-hw/optee/Makefile
> @@ -0,0 +1 @@
> +obj-y += optee.o
> diff --git a/drivers/sec-hw/optee/optee.c b/drivers/sec-hw/optee/optee.c
> new file mode 100644
> index 0000000..4a5ce23
> --- /dev/null
> +++ b/drivers/sec-hw/optee/optee.c
> @@ -0,0 +1,174 @@
> +/*
> + * Copyright (c) 2015, Linaro Limited
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/sec-hw/tee_drv.h>
> +
> +#define DRIVER_NAME "tee-optee"
> +
> +struct optee {
> +     struct tee_device *supp_teedev;
> +     struct tee_device *teedev;
> +     struct device *dev;
> +};
> +
> +static void optee_get_version(struct tee_filp *teefilp,
> +             struct tee_version *version)
> +{
> +}
> +
> +static int optee_open(struct tee_filp *teefilp)
> +{
> +     return -EINVAL;
> +}
> +
> +static int optee_release(struct tee_filp *teefilp)
> +{
> +     return -EINVAL;
> +}
> +
> +static int optee_cmd(struct tee_filp *teefilp, void __user *buf, size_t len)
> +{
> +     return -EINVAL;
> +}
> +
> +static int optee_shm_share(struct tee_filp *teefilp, struct tee_shm *shm)
> +{
> +     return -ENOENT;
> +}
> +
> +static void optee_shm_unshare(struct tee_filp *teefilp, struct tee_shm *shm)
> +{
> +}
> +
> +static struct tee_driver_ops optee_ops = {
> +     .get_version = optee_get_version,
> +     .open = optee_open,
> +     .release = optee_release,
> +     .cmd = optee_cmd,
> +     .shm_share = optee_shm_share,
> +     .shm_unshare = optee_shm_unshare,
> +};
> +
> +static struct tee_desc optee_desc = {
> +     .name = DRIVER_NAME,
> +     .ops = &optee_ops,
> +     .owner = THIS_MODULE,
> +};
> +
> +static int optee_supp_cmd(struct tee_filp *teefilp, void __user *buf,
> +                     size_t len)
> +{
> +     return -EINVAL;
> +}
> +
> +static struct tee_driver_ops optee_supp_ops = {
> +     .get_version = optee_get_version,
> +     .open = optee_open,
> +     .release = optee_release,
> +     .cmd = optee_supp_cmd,
> +     .shm_share = optee_shm_share,
> +     .shm_unshare = optee_shm_unshare,
> +};
> +
> +static struct tee_desc optee_supp_desc = {
> +     .name = DRIVER_NAME,
> +     .ops = &optee_supp_ops,
> +     .owner = THIS_MODULE,
> +     .flags = TEE_DESC_PRIVILEDGED,

TEE_DESC_PRIVILEGED

> +};
> +
> +static int optee_probe(struct platform_device *pdev)
> +{
> +     struct optee *optee;
> +     int ret;
> +
> +     optee = devm_kzalloc(&pdev->dev, sizeof(*optee), GFP_KERNEL);
> +     if (!optee)
> +             return -ENOMEM;
> +
> +     optee->dev = &pdev->dev;
> +
> +     optee->teedev = tee_register(&optee_desc, &pdev->dev, optee);
> +     if (!optee->teedev) {
> +             dev_err(&pdev->dev, "could not register OP-TEE TEE driver\n");

OP-TEE driver

> +             ret = -EINVAL;
> +             goto err;
> +     }
> +
> +     optee->supp_teedev = tee_register(&optee_supp_desc, &pdev->dev, optee);
> +     if (!optee->teedev) {
> +             dev_err(&pdev->dev,
> +                     "could not register supplicant OP-TEE TEE driver\n");
> +             ret = -EINVAL;
> +             goto err;
> +     }
> +
> +     platform_set_drvdata(pdev, optee);
> +
> +     dev_info(&pdev->dev, "initialized OP-TEE TEE driver\n");
> +     return 0;
> +err:
> +     if (optee->teedev)
> +             tee_unregister(optee->teedev);
> +     devm_kfree(&pdev->dev, optee);
> +     return ret;
> +}
> +
> +static int optee_remove(struct platform_device *pdev)
> +{
> +     struct optee *optee = platform_get_drvdata(pdev);
> +
> +     tee_unregister(optee->teedev);
> +     tee_unregister(optee->supp_teedev);
> +
> +     return 0;
> +}
> +
> +
> +static const struct of_device_id optee_match[] = {
> +     { .compatible = "tee-optee" },
> +     {},
> +};
> +
> +static struct platform_driver optee_driver = {
> +     .driver = {
> +             .name = DRIVER_NAME,
> +             .of_match_table = optee_match,
> +     },
> +     .probe = optee_probe,
> +     .remove = optee_remove,
> +};
> +
> +static int __init optee_init(void)
> +{
> +     pr_info("%s", __func__);
> +
> +     return platform_driver_register(&optee_driver);
> +}
> +
> +static void __exit optee_exit(void)
> +{
> +     pr_info("%s", __func__);
> +     platform_driver_unregister(&optee_driver);
> +}
> +
> +module_init(optee_init);
> +module_exit(optee_exit);
> +
> +MODULE_AUTHOR("Linaro");
> +MODULE_DESCRIPTION("OP-TEE TEE driver");
> +MODULE_SUPPORTED_DEVICE("");
> +MODULE_VERSION("1.0");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/sec-hw/tee.c b/drivers/sec-hw/tee.c
> new file mode 100644
> index 0000000..a361e84
> --- /dev/null
> +++ b/drivers/sec-hw/tee.c
> @@ -0,0 +1,233 @@
> +/*
> + * Copyright (c) 2015, Linaro Limited
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +#include <linux/device.h>
> +#include <linux/fs.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/sec-hw/tee_drv.h>
> +#include <asm/uaccess.h>
> +#include "tee_private.h"
> +
> +static int tee_open(struct inode *inode, struct file *filp)
> +{
> +     int ret;
> +     struct tee_device *teedev;
> +     struct tee_filp *teefilp;
> +
> +     teedev = container_of(filp->private_data, struct tee_device, miscdev);
> +     teefilp = kzalloc(sizeof(*teefilp), GFP_KERNEL);
> +     if (!teefilp)
> +             return -ENOMEM;
> +
> +     teefilp->teedev = teedev;
> +     filp->private_data = teefilp;
> +     ret = teedev->desc->ops->open(teefilp);
> +     if (ret)
> +             kfree(teefilp);
> +     return ret;
> +}
> +
> +static int tee_release(struct inode *inode, struct file *filp)
> +{
> +     struct tee_filp *teefilp = filp->private_data;
> +
> +     return teefilp->teedev->desc->ops->release(teefilp);
> +}
> +
> +static long tee_ioctl_version(struct tee_filp *teefilp,
> +             struct tee_version __user *uvers)
> +{
> +     struct tee_version vers;
> +
> +     memset(&vers, 0, sizeof(vers));
> +     vers.gen_version = TEE_VERSION;
> +     teefilp->teedev->desc->ops->get_version(teefilp, &vers);
> +
> +     return copy_to_user(uvers, &vers, sizeof(vers));
> +}
> +
> +static long tee_ioctl_cmd(struct tee_filp *teefilp,
> +             struct tee_cmd_data __user *ucmd)
> +{
> +     long ret;
> +     struct tee_cmd_data cmd;
> +     void __user *buf_ptr;
> +
> +     ret = copy_from_user(&cmd, ucmd, sizeof(cmd));
> +     if (ret)
> +             return ret;
> +
> +     buf_ptr = (void __user *)(uintptr_t)cmd.buf_ptr;
> +     return teefilp->teedev->desc->ops->cmd(teefilp, buf_ptr, cmd.buf_len);
> +}
> +
> +static long tee_ioctl_shm_alloc(struct tee_filp *teefilp,
> +             struct tee_shm_alloc_data __user *udata)
> +{
> +     long ret;
> +     struct tee_shm_alloc_data data;
> +     struct tee_shm *shm;
> +
> +     if (copy_from_user(&data, udata, sizeof(data)))
> +             return -EFAULT;
> +
> +     /* These are implicit in a user space request */
> +     data.flags |= TEE_SHM_MAPPED | TEE_SHM_GLOBAL_DMA_BUF;

So, the user is not supposed to set them in the ioctl command? Or it
doesn't matter? (I think it's not very good to leave a choice to the
application so I would either check they are set or on the contrary make
them explicitely reserved and check they are zero).

> +
> +     shm = tee_shm_alloc(teefilp->teedev, data.flags, data.size);
> +     if (IS_ERR(shm)) {
> +             ret = PTR_ERR(shm);
> +             goto err;
> +     }
> +
> +     data.flags = shm->flags;
> +     data.fd = tee_shm_fd(shm);
> +     if (data.fd < 0) {
> +             ret = data.fd;
> +             goto err;
> +     }
> +
> +     if (copy_to_user(udata, &data, sizeof(data))) {
> +             ret = -EFAULT;
> +             goto err;
> +     }
> +     return 0;
> +err:
> +     tee_shm_free(shm);
> +     return ret;
> +}
> +
> +static long tee_ioctl_mem_share(struct tee_filp *teefilp,
> +             struct tee_mem_share_data __user *udata)
> +{
> +     /* Not supported yet */
> +     return -ENOENT;
> +}
> +
> +static long tee_ioctl_mem_unshare(struct tee_filp *teefilp,
> +             struct tee_mem_share_data __user *udata)
> +{
> +     /* Not supported yet */
> +     return -ENOENT;
> +}
> +
> +static long tee_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> +{
> +     struct tee_filp *teefilp = filp->private_data;
> +     void __user *uarg = (void __user *)arg;
> +
> +     switch (cmd) {
> +     case TEE_IOC_VERSION:
> +             return tee_ioctl_version(teefilp, uarg);
> +     case TEE_IOC_CMD:
> +             return tee_ioctl_cmd(teefilp, uarg);
> +     case TEE_IOC_SHM_ALLOC:
> +             return tee_ioctl_shm_alloc(teefilp, uarg);
> +     case TEE_IOC_MEM_SHARE:
> +             return tee_ioctl_mem_share(teefilp, uarg);
> +     case TEE_IOC_MEM_UNSHARE:
> +             return tee_ioctl_mem_unshare(teefilp, uarg);
> +     default:
> +             return -EINVAL;
> +     }
> +}
> +
> +
> +static const struct file_operations tee_fops = {
> +     .owner = THIS_MODULE,
> +     .open = tee_open,
> +     .release = tee_release,
> +     .unlocked_ioctl = tee_ioctl
> +};
> +
> +struct tee_device *tee_register(const struct tee_desc *teedesc,
> +                     struct device *dev, void *driver_data)
> +{
> +     static atomic_t device_no = ATOMIC_INIT(-1);
> +     static atomic_t priv_device_no = ATOMIC_INIT(-1);
> +     struct tee_device *teedev;
> +     int ret;
> +
> +     if (!teedesc || !teedesc->name || !dev)
> +             return NULL;
> +
> +     teedev = kzalloc(sizeof(*teedev), GFP_KERNEL);
> +     if (!teedev) {
> +             dev_err(dev, "failed to alloc struct tee_device\n");
> +             return NULL;
> +     }
> +
> +     teedev->dev = dev;
> +     teedev->desc = teedesc;
> +     teedev->driver_data = driver_data;
> +
> +     if (teedesc->flags & TEE_DESC_PRIVILEDGED)
> +             snprintf(teedev->name, sizeof(teedev->name),
> +                      "teepriv%d", atomic_inc_return(&priv_device_no));
> +     else
> +             snprintf(teedev->name, sizeof(teedev->name),
> +                      "tee%d", atomic_inc_return(&device_no));
> +
> +     teedev->miscdev.parent = dev;
> +     teedev->miscdev.minor = MISC_DYNAMIC_MINOR;
> +     teedev->miscdev.name = teedev->name;
> +     teedev->miscdev.fops = &tee_fops;
> +
> +     ret = misc_register(&teedev->miscdev);
> +     if (ret) {
> +             dev_err(dev, "misc_register() failed name=\"%s\"\n",
> +                     teedev->name);
> +             goto err;
> +     }
> +
> +     INIT_LIST_HEAD(&teedev->list_shm);
> +     mutex_init(&teedev->mutex);
> +
> +     dev_set_drvdata(teedev->miscdev.this_device, teedev);
> +
> +     dev_info(dev, "register misc device \"%s\" (minor=%d)\n",
> +              dev_name(teedev->miscdev.this_device), teedev->miscdev.minor);
> +
> +     return teedev;
> +err:
> +     kfree(teedev);
> +     return NULL;
> +}
> +EXPORT_SYMBOL_GPL(tee_register);
> +
> +void tee_unregister(struct tee_device *teedev)
> +{
> +     if (!teedev)
> +             return;
> +
> +     dev_info(teedev->dev, "unregister misc device \"%s\" (minor=%d)\n",
> +              dev_name(teedev->miscdev.this_device), teedev->miscdev.minor);
> +     misc_deregister(&teedev->miscdev);
> +     /* TODO finish this function */
> +}
> +EXPORT_SYMBOL_GPL(tee_unregister);
> +
> +void *tee_get_drvdata(struct tee_device *teedev)
> +{
> +     return teedev->driver_data;
> +}
> +EXPORT_SYMBOL_GPL(tee_get_drvdata);
> +
> +static int __init tee_init(void)
> +{
> +     pr_info("initialized tee subsystem\n");
> +     return 0;
> +}
> +
> +core_initcall(tee_init);
> diff --git a/drivers/sec-hw/tee_private.h b/drivers/sec-hw/tee_private.h
> new file mode 100644
> index 0000000..2879b12
> --- /dev/null
> +++ b/drivers/sec-hw/tee_private.h
> @@ -0,0 +1,39 @@
> +/* * Copyright (c) 2015, Linaro Limited
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +#ifndef TEE_PRIVATE_H
> +#define TEE_PRIVATE_H
> +
> +#define TEE_MAX_DEV_NAME_LEN 32
> +struct tee_device {
> +     char name[TEE_MAX_DEV_NAME_LEN];
> +     const struct tee_desc *desc;
> +     struct device *dev;
> +     struct miscdevice miscdev;
> +     struct list_head list_shm;
> +     struct mutex mutex;
> +     void *driver_data;
> +};
> +
> +struct tee_shm {
> +     struct list_head list_node;
> +     struct tee_device *teedev;
> +     dma_addr_t paddr;
> +     void *kaddr;
> +     size_t size;
> +     struct dma_buf *dmabuf;
> +     u32 flags;
> +};
> +
> +int tee_shm_fd(struct tee_shm *shm);
> +
> +#endif /*TEE_PRIVATE_H*/
> diff --git a/drivers/sec-hw/tee_shm.c b/drivers/sec-hw/tee_shm.c
> new file mode 100644
> index 0000000..894d7e8
> --- /dev/null
> +++ b/drivers/sec-hw/tee_shm.c
> @@ -0,0 +1,279 @@
> +/*
> + * Copyright (c) 2015, Linaro Limited
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +#include <linux/device.h>
> +#include <linux/dma-buf.h>
> +#include <linux/slab.h>
> +#include <linux/sec-hw/tee_drv.h>
> +#include "tee_private.h"
> +
> +static DEFINE_MUTEX(teeshm_list_mutex);
> +static LIST_HEAD(teeshm_list);
> +
> +static void tee_shm_release(struct tee_shm *shm);
> +
> +static struct sg_table *tee_shm_dmabuf_map_dma_buf(struct dma_buf_attachment
> +                     *attach, enum dma_data_direction dir)
> +{
> +        return NULL;
> +}
> +
> +static void tee_shm_dmabuf_unmap_dma_buf(struct dma_buf_attachment *attach,
> +                     struct sg_table *table, enum dma_data_direction dir)
> +{
> +}
> +
> +static void tee_shm_dmabuf_release(struct dma_buf *dmabuf)
> +{
> +     struct tee_shm *shm = dmabuf->priv;
> +
> +     tee_shm_release(shm);
> +}
> +
> +static void *tee_shm_dmabuf_kmap_atomic(struct dma_buf *dmabuf,
> +                     unsigned long pgnum)
> +{
> +     return NULL;
> +}
> +
> +static void *tee_shm_dmabuf_kmap(struct dma_buf *dmabuf, unsigned long pgnum)
> +{
> +     return NULL;
> +}
> +
> +static int tee_shm_dmabuf_mmap(struct dma_buf *dmabuf,
> +                     struct vm_area_struct *vma)
> +{
> +     struct tee_shm *shm = dmabuf->priv;
> +     size_t size = vma->vm_end - vma->vm_start;
> +
> +     return remap_pfn_range(vma, vma->vm_start, shm->paddr >> PAGE_SHIFT,
> +                            size, vma->vm_page_prot);
> +}
> +
> +static struct dma_buf_ops tee_shm_dma_buf_ops = {
> +     .map_dma_buf = tee_shm_dmabuf_map_dma_buf,
> +     .unmap_dma_buf = tee_shm_dmabuf_unmap_dma_buf,
> +     .release = tee_shm_dmabuf_release,
> +     .kmap_atomic = tee_shm_dmabuf_kmap_atomic,
> +     .kmap = tee_shm_dmabuf_kmap,
> +     .mmap = tee_shm_dmabuf_mmap,
> +};
> +
> +struct tee_shm *tee_shm_alloc(struct tee_device *teedev, size_t size,
> +                     u32 flags)
> +{
> +     struct tee_shm *shm;
> +     void *ret;
> +     struct mutex *mutex;
> +     struct list_head *list_shm;
> +
> +     if (!(flags & TEE_SHM_MAPPED)) {
> +             dev_err(teedev->dev, "only mapped allocations supported\n");
> +             return ERR_PTR(-EINVAL);
> +     }
> +
> +     if ((flags & ~(TEE_SHM_MAPPED|TEE_SHM_GLOBAL_DMA_BUF))) {
> +             dev_err(teedev->dev, "invalid shm flags 0x%x", flags);
> +             return ERR_PTR(-EINVAL);
> +     }
> +
> +     shm = kzalloc(sizeof(struct tee_shm), GFP_KERNEL);
> +     if (!shm) {
> +             dev_err(teedev->dev, "failed to allocate struct tee_shm\n");
> +             return ERR_PTR(-ENOMEM);
> +     }
> +
> +     shm->flags = flags;
> +
> +     if (flags & TEE_SHM_GLOBAL_DMA_BUF) {
> +             int order = get_order(size);
> +
> +             shm->size = (1 << order) << PAGE_SHIFT;
Should we save the original size (the requested one)?

 
> +             shm->kaddr = (void *)__get_free_pages(GFP_KERNEL, order);
> +             if (!shm->kaddr) {
> +                     dev_err(teedev->dev,
> +                             "failed to get order %d pages for shared memory\n",
> +                             order);
> +                     ret = ERR_PTR(-ENOMEM);
> +                     goto err;
> +             }
> +             /* Clear it to not leak information */
> +             memset(shm->kaddr, 0, shm->size);
> +
> +             shm->dmabuf = dma_buf_export(shm, &tee_shm_dma_buf_ops,
> +                                          shm->size, O_RDWR, NULL);
> +             if (IS_ERR(shm->dmabuf)) {
> +                     ret = ERR_CAST(shm->dmabuf);
> +                     goto err;
> +             }
> +
> +             mutex = &teeshm_list_mutex;
> +             list_shm = &teeshm_list;
> +     } else {
> +             shm->size = size;
> +             shm->kaddr = kzalloc(size, GFP_KERNEL);
> +             if (!shm->kaddr) {
> +                     dev_err(teedev->dev,
> +                             "failed to allocate %zu bytes of shared memory\n",
> +                             size);
> +                     ret = ERR_PTR(-ENOMEM);
> +                     goto err;
> +             }
> +
> +             mutex = &teedev->mutex;
> +             list_shm = &teedev->list_shm;
> +     }
> +
> +     mutex_lock(mutex);
> +     list_add_tail(&shm->list_node, list_shm);
> +     mutex_unlock(mutex);
> +
> +     shm->paddr = virt_to_phys(shm->kaddr);
> +
> +     return shm;
> +err:
> +     if (shm->kaddr) {
> +             if (shm->flags & TEE_SHM_GLOBAL_DMA_BUF)
> +                     free_pages((unsigned long)shm->kaddr, get_order(size));
> +             else
> +                     kfree(shm->kaddr);
> +     }
> +     kfree(shm);
> +     return ret;
> +}
> +EXPORT_SYMBOL_GPL(tee_shm_alloc);
> +
> +int tee_shm_fd(struct tee_shm *shm)
> +{
> +     u32 req_flags = TEE_SHM_MAPPED | TEE_SHM_GLOBAL_DMA_BUF;
> +
> +     if ((shm->flags & req_flags) != req_flags)
> +             return -EINVAL;
> +
> +     return dma_buf_fd(shm->dmabuf, O_CLOEXEC);
> +}
> +
> +static void tee_shm_release(struct tee_shm *shm)
> +{
> +     struct tee_device *teedev = shm->teedev;
> +
> +     if (shm->flags & TEE_SHM_GLOBAL_DMA_BUF) {
> +             free_pages((unsigned long)shm->kaddr, get_order(shm->size));
> +             mutex_lock(&teeshm_list_mutex);
> +             list_del(&shm->list_node);
> +             mutex_unlock(&teeshm_list_mutex);
> +     } else {
> +             kfree(shm->kaddr);
> +             mutex_lock(&teedev->mutex);
> +             list_del(&shm->list_node);
> +             mutex_unlock(&teedev->mutex);
> +     }
> +
> +     kfree(shm);
> +}
> +
> +void tee_shm_free(struct tee_shm *shm)
> +{
> +     /*
> +      * dma_buf_put() decreases the dmabuf reference counter and will
> +      * call tee_shm_release() when the last reference is gone.
> +      *
> +      * In the case of anonymous memory we call tee_shm_release directly
> +      * instead at it doesn't have a reference counter.
> +      */
> +     if (shm->flags & TEE_SHM_GLOBAL_DMA_BUF)
> +             dma_buf_put(shm->dmabuf);
> +     else
> +             tee_shm_release(shm);
> +}
> +EXPORT_SYMBOL_GPL(tee_shm_free);
> +
> +static bool cmp_key_va(struct tee_shm *shm, uintptr_t va)
> +{
> +     uintptr_t shm_va = (uintptr_t)shm->kaddr;
> +
> +     return (va >= shm_va) && (va < (shm_va + shm->size));
> +}
> +
> +static bool cmp_key_pa(struct tee_shm *shm, uintptr_t pa)
> +{
> +     return (pa >= shm->paddr) && (pa < (shm->paddr + shm->size));
> +}
> +
> +static struct tee_shm *tee_shm_find_by_key(struct tee_device *teedev, u32 flags,
> +                     bool (*cmp)(struct tee_shm *shm, uintptr_t key),
> +                     uintptr_t key)
> +{
> +     struct tee_shm *ret = NULL;
> +     struct tee_shm *shm;
> +     struct mutex *mutex;
> +     struct list_head *list_shm;
> +
> +     if (flags & TEE_SHM_GLOBAL_DMA_BUF) {
> +             mutex = &teeshm_list_mutex;
> +             list_shm = &teeshm_list;
> +     } else {
> +             mutex = &teedev->mutex;
> +             list_shm = &teedev->list_shm;
> +     }
> +
> +     mutex_lock(mutex);
> +     list_for_each_entry(shm, list_shm, list_node) {
> +             if (cmp(shm, key)) {
> +                     ret = shm;
> +                     break;
> +             }
> +     }
> +     mutex_unlock(mutex);
> +
> +     return ret;
> +}
> +
> +struct tee_shm *tee_shm_find_by_va(struct tee_device *teedev, u32 flags,
> +                     void *va)
> +{
> +     return tee_shm_find_by_key(teedev, flags, cmp_key_va, (uintptr_t)va);
> +}
> +EXPORT_SYMBOL_GPL(tee_shm_find_by_va);
> +
> +struct tee_shm *tee_shm_find_by_pa(struct tee_device *teedev, u32 flags,
> +                     dma_addr_t pa)
> +{
> +     return tee_shm_find_by_key(teedev, flags, cmp_key_pa, pa);
> +}
> +EXPORT_SYMBOL_GPL(tee_shm_find_by_pa);
> +
> +int tee_shm_va2pa(struct tee_shm *smh, void *va, dma_addr_t *pa)
shm. same below
 
> +{
> +     /*
> +      * Since we only have TEE_SHM_MAPPED shared memory using
> +      * __get_free_pages() we don't need "shm" for the moment, but with
> +      * other allocators for the shared memory this may change.
> +      */
> +     *pa = virt_to_phys(va);
> +     return 0;
> +}
> +EXPORT_SYMBOL_GPL(tee_shm_va2pa);
> +
> +int tee_shm_pa2va(struct tee_shm *smh, dma_addr_t pa, void **va)
> +{
> +     /*
> +      * Since we only have TEE_SHM_MAPPED shared memory using
> +      * __get_free_pages() we don't need "shm" for the moment, but with
> +      * other allocators for the shared memory this may change.
> +      */
> +     *va = phys_to_virt(pa);
> +     return 0;
> +}
> +EXPORT_SYMBOL_GPL(tee_shm_pa2va);
> diff --git a/include/linux/sec-hw/tee_drv.h b/include/linux/sec-hw/tee_drv.h
> new file mode 100644
> index 0000000..16ca13b
> --- /dev/null
> +++ b/include/linux/sec-hw/tee_drv.h
> @@ -0,0 +1,155 @@
> +/* * Copyright (c) 2015, Linaro Limited
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#ifndef __TEE_DRV_H
> +#define __TEE_DRV_H
> +
> +#include <linux/types.h>
> +#include <linux/list.h>
> +#include <linux/miscdevice.h>
> +#include <linux/sec-hw/tee.h>
> +
> +/*
> + * The file describes the API provided by the generic TEE driver to the
> + * specific TEE driver.
> + */
> +
> +struct tee_device;
> +struct tee_shm;
> +
> +
> +/**
> + * struct tee_filp - driver specific file pointer data
> + * @teedev:  pointer to this drivers struct tee_device
> + * @filp_data:       driver specific file pointer data, managed by the driver
> + */
> +struct tee_filp {
> +     struct tee_device *teedev;
> +     void *filp_data;
> +};
> +
> +/**
> + * struct tee_driver_ops - driver operations vtable
> + * @get_version:     fills in spec_version and uuid of supplied
> + *                   struct tee_version
> + * @open:            called when the device file is opened
> + * @release:         release this open file
> + * @cmd:             process a command from user space
> + * @shm_share:               share some memory with Secure OS
> + * @shm_unshare:     unshare some memory with Secure OS
> + */
> +struct tee_driver_ops {
> +     void (*get_version)(struct tee_filp *teefilp,
> +                         struct tee_version *version);
> +     int (*open)(struct tee_filp *teefilp);
> +     int (*release)(struct tee_filp *teefilp);
> +     int (*cmd)(struct tee_filp *teefilp, void __user *buf, size_t len);
> +     int (*shm_share)(struct tee_filp *teefilp, struct tee_shm *shm);
> +     void (*shm_unshare)(struct tee_filp *teefilp, struct tee_shm *shm);
> +};
> +
> +/**
> + * struct tee_desc - Describes the TEE driver to the subsystem
> + * @name:    name of driver
> + * @ops:     driver operations vtable
> + * @owner:   module providing the driver
> + * @flags:   Extra properties of driver, defined by TEE_DESC_* below
> + */
> +#define TEE_DESC_PRIVILEDGED 0x1
> +struct tee_desc {
> +     const char *name;
> +     const struct tee_driver_ops *ops;
> +     struct module *owner;
> +     u32 flags;
> +};
> +
> +
> +/**
> + * tee_register() - Register a specific TEE driver
> + * @dev:
> + * @ops: Operations on a specific TEE device
Wrong list of arguments.
 
> + *
> + * Once the specific driver has been probed it registers in the generic
> + * driver with this function.
> + *
> + * @returns a pointer to struct tee_device
> + */
> +struct tee_device *tee_register(const struct tee_desc *teedesc,
> +                     struct device *dev, void *driver_data);
> +
> +/**
> + * tee_unregister() - Unregister a specific TEE driver
> + * @teedev:
> + */
> +void tee_unregister(struct tee_device *teedev);
> +
> +/**
> + * tee_get_drvdata() - Return driver_data pointer
> + * @returns the driver_data pointer supplied to tee_register().
> + */
> +void *tee_get_drvdata(struct tee_device *teedev);
> +
> +/**
> + * tee_shm_alloc() - Allocate shared memory
> + * @teedev:
> + * @size:
> + * @flags:
> + * @returns a pointer to struct tee_shm
> + */
> +struct tee_shm *tee_shm_alloc(struct tee_device *teedev, size_t size,
> +                     u32 flags);
> +
> +/**
> + * tee_shm_free() - Free shared memory
> + * @shm:
> + */
> +void tee_shm_free(struct tee_shm *shm);
> +
> +/**
> + * tee_shm_find_by_va() - Find a shared memory handle by a virtual address
> + * @teedev:
> + * @flags:
> + * @va:
> + * @returns a pointer to struct tee_shm
> + */
> +struct tee_shm *tee_shm_find_by_va(struct tee_device *teedev, u32 flags,
> +                     void *va);
> +/**
> + * tee_shm_find_by_pa() - Find a shared memory handle by a physical address
> + * @teedev:
> + * @flags:
> + * @pa:
> + * @returns a pointer to struct tee_shm
> + */
> +struct tee_shm *tee_shm_find_by_pa(struct tee_device *teedev, u32 flags,
> +                     dma_addr_t pa);
> +
> +/**
> + * tee_shm_va2pa() - Get physical address of a virtual address
> + * @shm:
> + * @va:
> + * @pa:
> + * @returns 0 on success and < 0 on failure
> + */
> +int tee_shm_va2pa(struct tee_shm *smh, void *va, dma_addr_t *pa);
> +
> +/**
> + * tee_shm_pa2va() - Get virtual address of a physical address
> + * @shm:
> + * @pa:
> + * @va:
> + * @returns 0 on success and < 0 on failure
> + */
> +int tee_shm_pa2va(struct tee_shm *smh, dma_addr_t pa, void **va);
> +
> +#endif /*__TEE_DRV_H*/
>

_______________________________________________
Tee-dev mailing list
Tee-dev@lists.linaro.org
http://lists.linaro.org/mailman/listinfo/tee-dev