Hi,
Here's version 4 of the generic TEE subsystem patch set.
Changes in V4: * Broken out the stubbed OP-TEE driver into a separate patch * Addressed review comments * Added a few more functions to tee_drv.h * Updated tee.h
Changes in V3: * Some small changes in the "tee: add user space interface" * New patch for the subsystem implementation
Jens Wiklander (3): tee: add user space interface tee: generic TEE subsystem tee: add OP-TEE driver
Documentation/ioctl/ioctl-number.txt | 1 + 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 | 201 +++++++++++++++++ drivers/sec-hw/tee.c | 243 +++++++++++++++++++++ drivers/sec-hw/tee_private.h | 42 ++++ drivers/sec-hw/tee_shm.c | 403 +++++++++++++++++++++++++++++++++++ include/linux/sec-hw/tee.h | 180 ++++++++++++++++ include/linux/sec-hw/tee_drv.h | 214 +++++++++++++++++++ 13 files changed, 1346 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.h create mode 100644 include/linux/sec-hw/tee_drv.h
Signed-off-by: Jens Wiklander jens.wiklander@linaro.org --- Changes for V4: * Avoid potential clashes with GP defines * Group each IOCTL definition with the struct used as argument * Rename each struct used as IOCTL arguemnt to 'struct tee_ioctl_..."
Changes for V3: * Defines the flags for struct tee_shm_alloc_data flags field * Changed line in ioctl-number to "Generic TEE subsystem" instead --- Documentation/ioctl/ioctl-number.txt | 1 + include/linux/sec-hw/tee.h | 180 +++++++++++++++++++++++++++++++++++ 2 files changed, 181 insertions(+) create mode 100644 include/linux/sec-hw/tee.h
diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index 8136e1f..6e9bd04 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt @@ -301,6 +301,7 @@ Code Seq#(hex) Include File Comments 0xA3 80-8F Port ACL in development: mailto:tlewis@mindspring.com 0xA3 90-9F linux/dtlk.h +0xA4 00-1F linux/sec-hw/tee.h Generic TEE subsystem 0xAB 00-1F linux/nbd.h 0xAC 00-1F linux/raw.h 0xAD 00 Netfilter device in development: diff --git a/include/linux/sec-hw/tee.h b/include/linux/sec-hw/tee.h new file mode 100644 index 0000000..f1af46b --- /dev/null +++ b/include/linux/sec-hw/tee.h @@ -0,0 +1,180 @@ +/* + * 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_H +#define __TEE_H + +#include <linux/ioctl.h> +#include <linux/types.h> + +/* + * This file describes the API provided by the generic TEE driver to user + * space + */ + + +/* Helpers to make the ioctl defines */ +#define TEE_IOC_MAGIC 0xa4 +#define TEE_IOC_BASE 0 +#define _TEE_IOR(nr, size) _IOR(TEE_IOC_MAGIC, TEE_IOC_BASE + (nr), size) +#define _TEE_IOWR(nr, size) _IOWR(TEE_IOC_MAGIC, TEE_IOC_BASE + (nr), size) +#define _TEE_IOW(nr, size) _IOW(TEE_IOC_MAGIC, TEE_IOC_BASE + (nr), size) + +/* + * Version of the generic TEE subsystem, if it doesn't match what's + * returned by TEE_IOC_VERSION this header is not in sync with the kernel. + */ +#define TEE_SUBSYS_VERSION 1 + + +/* Flags relating to shared memory */ +#define TEE_IOCTL_SHM_MAPPED 0x1 /* memory mapped in normal world */ +#define TEE_IOCTL_SHM_DMA_BUF 0x2 /* dma-buf handle on shared memory */ + +/** + * struct tee_version - TEE versions + * @gen_version: [out] Generic TEE driver version + * @spec_version: [out] Specific TEE driver version + * @uuid: [out] Specific TEE driver uuid, zero if not used + * + * Identifies the generic TEE driver, and the specific TEE driver. + * Used as argument for TEE_IOC_VERSION below. + */ +struct tee_ioctl_version { + uint32_t gen_version; + uint32_t spec_version; + uint8_t uuid[16]; +}; +/** + * TEE_IOC_VERSION - query version of drivers + * + * Takes a tee_version struct and returns with the version numbers filled in. + */ +#define TEE_IOC_VERSION _TEE_IOR(0, struct tee_ioctl_version) + +/** + * struct tee_cmd_data - Opaque command argument + * @buf_ptr: [in] A __user pointer to a command buffer + * @buf_len: [in] Length of the buffer above + * + * Opaque command data which is passed on to the specific driver. The command + * buffer doesn't have to reside in shared memory. + * Used as argument for TEE_IOC_CMD below. + */ +struct tee_ioctl_cmd_data { + uint64_t buf_ptr; + uint64_t buf_len; +}; +/** + * TEE_IOC_CMD - pass a command to the specific TEE driver + * + * Takes tee_cmd_data struct which is passed to the specific TEE driver. + */ +#define TEE_IOC_CMD _TEE_IOR(1, struct tee_ioctl_cmd_data) + +/** + * struct tee_shm_alloc_data - Shared memory allocate argument + * @size: [in/out] Size of shared memory to allocate + * @flags: [in/out] Flags to/from allocation. + * @fd: [out] dma_buf file descriptor 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_alloc_data { + uint64_t size; + uint32_t flags; + int32_t fd; +}; +/** + * TEE_IOC_SHM_ALLOC - allocate shared memory + * + * Allocates shared memory between the user space process and secure OS. + * The returned file descriptor is used to map the shared memory into user + * space. The shared memory is freed when the descriptor is closed and the + * memory is unmapped. + */ +#define TEE_IOC_SHM_ALLOC _TEE_IOWR(2, struct tee_ioctl_shm_alloc_data) + +/** + * struct tee_mem_buf - share user space memory with Secure OS + * @ptr: A __user pointer to memory to share + * @size: Size of the memory to share + * Used in 'struct tee_mem_share_data' below. + */ +struct tee_ioctl_mem_buf { + uint64_t ptr; + uint64_t size; +}; + +/** + * struct tee_mem_dma_buf - share foreign dma_buf memory + * @fd: dma_buf file descriptor + * @pad: padding, set to zero by caller + * Used in 'struct tee_mem_share_data' below. + */ +struct tee_ioctl_mem_dma_buf { + int32_t fd; + uint32_t pad; +}; + +/** + * struct tee_mem_share_data - share memory with Secure OS + * @buf: [in] share user space memory + * @dma_buf: [in] share foreign dma_buf memory + * @flags: [in/out] Flags to/from sharing. + * @pad: [in/out] Padding, set to zero by caller + * + * The bits in @flags are defined by TEE_IOCTL_SHM_* above, undefined bits + * should be seto to zero as input. If TEE_IOCTL_SHM_DMA_BUF is set in the + * flags field use the dma_buf field, else the buf field in the union. + * + * Used as argument for TEE_IOC_MEM_SHARE and TEE_IOC_MEM_UNSHARE below. + */ +struct tee_ioctl_mem_share_data { + union { + struct tee_ioctl_mem_buf buf; + struct tee_ioctl_mem_dma_buf dma_buf; + }; + uint32_t flags; + uint32_t pad; +}; + +/** + * TEE_IOC_MEM_SHARE - share a portion of user space memory with secure OS + * + * Shares a portion of user space memory with secure OS. + */ +#define TEE_IOC_MEM_SHARE _TEE_IOWR(3, struct tee_ioctl_mem_share_data) + +/** + * TEE_IOC_MEM_UNSHARE - unshares a portion shared user space memory + * + * Unshares a portion of previously shared user space memory. + */ +#define TEE_IOC_MEM_UNSHARE _TEE_IOW(4, struct tee_ioctl_mem_share_data) + +/* + * Five syscalls are used when communicating with the generic TEE driver. + * open(): opens the device associated with the driver + * ioctl(): as described above operating on the file descripto from open() + * close(): two cases + * - closes the device file descriptor + * - closes a file descriptor connected to allocated shared memory + * mmap(): maps shared memory into user space + * munmap(): unmaps previously shared memory + */ + +#endif /*__TEE_H*/
Signed-off-by: Jens Wiklander jens.wiklander@linaro.org --- Changes since V3: * Moved stubbed OP-TEE driver to separate patch * Requires all flags to be zero for TEE_IOC_SHM_ALLOC * Frees all shared memory allocations done on the file descriptor to the specific driver when the file descriptor is closed. * Use phys_addr_t instead of dma_addr_t * Adds some more functions to tee_drv.h
V3: 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 | 31 ++++ drivers/sec-hw/Makefile | 2 + drivers/sec-hw/tee.c | 243 +++++++++++++++++++++++++ drivers/sec-hw/tee_private.h | 42 +++++ drivers/sec-hw/tee_shm.c | 403 +++++++++++++++++++++++++++++++++++++++++ include/linux/sec-hw/tee_drv.h | 214 ++++++++++++++++++++++ 8 files changed, 938 insertions(+) create mode 100644 drivers/sec-hw/Kconfig create mode 100644 drivers/sec-hw/Makefile 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..a3292b0 --- /dev/null +++ b/drivers/sec-hw/Kconfig @@ -0,0 +1,31 @@ +# +# 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" + +endmenu + +endif diff --git a/drivers/sec-hw/Makefile b/drivers/sec-hw/Makefile new file mode 100644 index 0000000..d1b29cf --- /dev/null +++ b/drivers/sec-hw/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_TEE_GENERIC) += tee.o +obj-$(CONFIG_TEE_GENERIC) += tee_shm.o diff --git a/drivers/sec-hw/tee.c b/drivers/sec-hw/tee.c new file mode 100644 index 0000000..9cb91d9 --- /dev/null +++ b/drivers/sec-hw/tee.c @@ -0,0 +1,243 @@ +/* + * 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; + + /* Free all shm:s related to this teefilp */ + tee_shm_free_by_teefilp(teefilp); + + teefilp->teedev->desc->ops->release(teefilp); + return 0; +} + +static long tee_ioctl_version(struct tee_filp *teefilp, + struct tee_ioctl_version __user *uvers) +{ + struct tee_ioctl_version vers; + + memset(&vers, 0, sizeof(vers)); + vers.gen_version = TEE_SUBSYS_VERSION; + teefilp->teedev->desc->ops->get_version(teefilp, &vers.spec_version, + vers.uuid); + + return copy_to_user(uvers, &vers, sizeof(vers)); +} + +static long tee_ioctl_cmd(struct tee_filp *teefilp, + struct tee_ioctl_cmd_data __user *ucmd) +{ + long ret; + struct tee_ioctl_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_ioctl_shm_alloc_data __user *udata) +{ + long ret; + struct tee_ioctl_shm_alloc_data data; + struct tee_shm *shm; + + if (copy_from_user(&data, udata, sizeof(data))) + return -EFAULT; + + /* Currently no input flags are supported */ + if (data.flags) + return -EINVAL; + + shm = tee_shm_alloc(teefilp->teedev, teefilp, data.size, + TEE_SHM_MAPPED | TEE_SHM_DMA_BUF); + if (IS_ERR(shm)) + return PTR_ERR(shm); + + ret = teefilp->teedev->desc->ops->shm_share(shm); + if (ret) + goto err; + + data.flags = shm->flags; + data.size = shm->size; + 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_ioctl_mem_share_data __user *udata) +{ + /* Not supported yet */ + return -ENOENT; +} + +static long tee_ioctl_mem_unshare(struct tee_filp *teefilp, + struct tee_ioctl_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_PRIVILEGED) + 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..7b1668f --- /dev/null +++ b/drivers/sec-hw/tee_private.h @@ -0,0 +1,42 @@ +/* * 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; + struct tee_filp *teefilp; + phys_addr_t paddr; + void *kaddr; + size_t size; + struct dma_buf *dmabuf; + u32 flags; +}; + +int tee_shm_fd(struct tee_shm *shm); +void tee_shm_free_by_teefilp(struct tee_filp *teefilp); + + +#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..885c753 --- /dev/null +++ b/drivers/sec-hw/tee_shm.c @@ -0,0 +1,403 @@ +/* + * 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 LIST_HEAD(teeshm_pending_free_list); + +static void tee_shm_release(struct tee_shm *shm); + +static struct sg_table *tee_shm_op_map_dma_buf(struct dma_buf_attachment + *attach, enum dma_data_direction dir) +{ + return NULL; +} + +static void tee_shm_op_unmap_dma_buf(struct dma_buf_attachment *attach, + struct sg_table *table, enum dma_data_direction dir) +{ +} + +static void tee_shm_op_release(struct dma_buf *dmabuf) +{ + struct tee_shm *shm = dmabuf->priv; + + tee_shm_release(shm); +} + +static void *tee_shm_op_kmap_atomic(struct dma_buf *dmabuf, + unsigned long pgnum) +{ + return NULL; +} + +static void *tee_shm_op_kmap(struct dma_buf *dmabuf, unsigned long pgnum) +{ + return NULL; +} + +static int tee_shm_op_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_op_map_dma_buf, + .unmap_dma_buf = tee_shm_op_unmap_dma_buf, + .release = tee_shm_op_release, + .kmap_atomic = tee_shm_op_kmap_atomic, + .kmap = tee_shm_op_kmap, + .mmap = tee_shm_op_mmap, +}; + +struct tee_shm *tee_shm_alloc(struct tee_device *teedev, + struct tee_filp *teefilp, 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_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_DMA_BUF) { + int order = get_order(size); + + /* Global shm's must have a teefilp attached */ + if (!teefilp) { + ret = ERR_PTR(-EINVAL); + goto err; + } + + shm->teefilp = teefilp; + shm->size = (1 << order) << PAGE_SHIFT; + /* Request zeroed pages to not leak information */ + shm->kaddr = (void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO, + 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; + } + + 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 { + /* Driver private shm's must not have a teefilp attached */ + if (teefilp) { + ret = ERR_PTR(-EINVAL); + goto err; + } + 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; + } + + shm->teedev = teedev; + shm->paddr = virt_to_phys(shm->kaddr); + + if (flags & TEE_SHM_DMA_BUF) { + /* + * Only call share on global shm:s, as the driver private + * shm:s always originates from the driver itself. + */ + int rc = teedev->desc->ops->shm_share(shm); + if (rc) { + ret = ERR_PTR(rc); + goto err; + } + } + + mutex_lock(mutex); + list_add_tail(&shm->list_node, list_shm); + mutex_unlock(mutex); + + + + return shm; +err: + if (shm->kaddr) { + if (shm->flags & TEE_SHM_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_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_DMA_BUF) { + /* Only unshare global shm:s */ + shm->teedev->desc->ops->shm_unshare(shm); + + 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_by_teefilp(struct tee_filp *teefilp) +{ + struct tee_shm *shm; + struct tee_shm *tmp; + LIST_HEAD(tmp_list); + + /* + * Move all matching shm:s to a temporary list + */ + mutex_lock(&teeshm_list_mutex); + list_for_each_entry_safe(shm, tmp, &teeshm_list, list_node) { + if (shm->teefilp == teefilp) { + list_del(&shm->list_node); + list_add_tail(&shm->list_node, &tmp_list); + } + } + mutex_unlock(&teeshm_list_mutex); + + /* + * For each shm in the temporary list move it to the pending free + * list and call tee_shm_free(). Once the ref_count is 0 the shm + * will be removed from this list. + * + * Since the 'struct tee_filp' is about to be freed (the reason + * this function was called) set the teefilp to NULL. The only + * purpose of the teefilp in 'struct tee_shm' is to be able to find + * all shm:s related to a teefilp. + */ + while (true) { + mutex_lock(&teeshm_list_mutex); + shm = list_first_entry_or_null(&tmp_list, + struct tee_shm, list_node); + if (shm) { + list_del(&shm->list_node); + list_add_tail(&shm->list_node, + &teeshm_pending_free_list); + shm->teefilp = NULL; + } + mutex_unlock(&teeshm_list_mutex); + if (!shm) + break; + tee_shm_free(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_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_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, + phys_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 *shm, void *va, phys_addr_t *pa) +{ + /* Check that we're in the range of the shm */ + if ((char *)va < (char *)shm->kaddr) + return -EINVAL; + if ((char *)va >= ((char *)shm->kaddr + shm->size)) + return -EINVAL; + + if (pa) + *pa = virt_to_phys(va); + return 0; +} +EXPORT_SYMBOL_GPL(tee_shm_va2pa); + +int tee_shm_pa2va(struct tee_shm *shm, phys_addr_t pa, void **va) +{ + /* Check that we're in the range of the shm */ + if (pa < shm->paddr) + return -EINVAL; + if (pa >= (shm->paddr + shm->size)) + return -EINVAL; + + if (va) + *va = phys_to_virt(pa); + return 0; +} +EXPORT_SYMBOL_GPL(tee_shm_pa2va); + +void *tee_shm_get_va(struct tee_shm *shm, size_t offs) +{ + if (offs >= shm->size) + return ERR_PTR(-EINVAL); + return shm->kaddr; +} +EXPORT_SYMBOL_GPL(tee_shm_get_va); + +int tee_shm_get_pa(struct tee_shm *shm, size_t offs, phys_addr_t *pa) +{ + if (offs >= shm->size) + return -EINVAL; + if (pa) + *pa = shm->paddr + offs; + return 0; +} +EXPORT_SYMBOL_GPL(tee_shm_get_pa); + +static bool is_shm_dma_buf(struct dma_buf *dmabuf) +{ + return dmabuf->ops == &tee_shm_dma_buf_ops; +} + +struct tee_shm *tee_shm_get_from_fd(int fd) +{ + struct dma_buf *dmabuf = dma_buf_get(fd); + + if (IS_ERR(dmabuf)) + return ERR_CAST(dmabuf); + + if (!is_shm_dma_buf(dmabuf)) + return ERR_PTR(-EINVAL); + return dmabuf->priv; +} +EXPORT_SYMBOL_GPL(tee_shm_get_from_fd); + +void tee_shm_put(struct tee_shm *shm) +{ + if (shm->flags & TEE_SHM_DMA_BUF) + dma_buf_put(shm->dmabuf); +} +EXPORT_SYMBOL_GPL(tee_shm_put); diff --git a/include/linux/sec-hw/tee_drv.h b/include/linux/sec-hw/tee_drv.h new file mode 100644 index 0000000..8693363 --- /dev/null +++ b/include/linux/sec-hw/tee_drv.h @@ -0,0 +1,214 @@ +/* + * 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. + */ + +#define TEE_SHM_MAPPED 0x1 /* Memory mapped by the kernel */ +#define TEE_SHM_DMA_BUF 0x2 /* Memory with dma-buf handle */ + +#define TEE_UUID_SIZE 16 + +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: returns version of driver + * @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, u32 *version, u8 *uuid); + int (*open)(struct tee_filp *teefilp); + void (*release)(struct tee_filp *teefilp); + int (*cmd)(struct tee_filp *teefilp, void __user *buf, size_t len); + int (*shm_share)(struct tee_shm *shm); + void (*shm_unshare)(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_PRIVILEGED 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 + * @teedesc: Descriptor for this driver + * @dev: Parent device for this driver + * @driver_data: Private driver data for this driver + * + * 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: Driver to unregister + */ +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: Driver that allocates the shared memory + * @teefilp: TEE file pointer when allocating global shared memory, must be + * NULL for driver private shared memory. + * @size: Requested size of shared memory + * @flags: Flags setting properties for the requested shared memory. + * + * Memory allocated as global shared memory is automatically freed when the + * TEE file pointer is closed. The @flags field uses the bits defined by + * TEE_SHM_* above. TEE_SHM_MAPPED must currently always be set. If + * TEE_SHM_DMA_BUF global shared memory will be allocated and associated + * with a dma-buf handle, else driver private memory. + * + * @returns a pointer to 'struct tee_shm' + */ +struct tee_shm *tee_shm_alloc(struct tee_device *teedev, + struct tee_filp *teefilp, size_t size, u32 flags); + +/** + * tee_shm_free() - Free shared memory + * @shm: Handle to shared memory to free + */ +void tee_shm_free(struct tee_shm *shm); + +/** + * tee_shm_find_by_va() - Find a shared memory handle by a virtual address + * @teedev: The device that owns the shared memory + * @flags: Select which type of shared memory to locate, if + * TEE_SHM_DMA_BUF global shared memory else driver private + * shared memory. + * @va: Virtual address covered by the shared memory + * @returns a Handle to shared memory + */ +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: The device that owns the shared memory + * @flags: Select which type of shared memory to locate, if + * TEE_SHM_DMA_BUF global shared memory else driver private + * shared memory. + * @pa: Physical address covered by the shared memory + * @returns a Handle to shared memory + */ +struct tee_shm *tee_shm_find_by_pa(struct tee_device *teedev, u32 flags, + phys_addr_t pa); + +/** + * tee_shm_va2pa() - Get physical address of a virtual address + * @shm: Shared memory handle + * @va: Virtual address to tranlsate + * @pa: Returned physical address + * @returns 0 on success and < 0 on failure + */ +int tee_shm_va2pa(struct tee_shm *shm, void *va, phys_addr_t *pa); + +/** + * tee_shm_pa2va() - Get virtual address of a physical address + * @shm: Shared memory handle + * @pa: Physical address to tranlsate + * @va: Returned virtual address + * @returns 0 on success and < 0 on failure + */ +int tee_shm_pa2va(struct tee_shm *shm, phys_addr_t pa, void **va); + +/** + * tee_shm_get_size() - Get size of a shared memory + * @returns the size of the shared memory + */ +size_t tee_shm_get_size(struct tee_shm *shm); + +/** + * tee_shm_get_va() - Get virtual address of a shared memory plus an offset + * @shm: Shared memory handle + * @offs: Offset from start of this shared memory + * @returns virtual address of the shared memory + offs if offs is within + * the bounds of this shared memory, else an ERR_PTR + */ +void *tee_shm_get_va(struct tee_shm *shm, size_t offs); + +/** + * tee_shm_get_pa() - Get physical address of a shared memory plus an offset + * @shm: Shared memory handle + * @offs: Offset from start of this shared memory + * @pa: Physical address to return + * @returns 0 if offs is within the bounds of this shared memory, else an + * error code. + */ +int tee_shm_get_pa(struct tee_shm *shm, size_t offs, phys_addr_t *pa); + +/** + * tee_shm_get_from_fd() - Get a shared memory handle from a file descriptor + * @fd: A user space file descriptor + * + * This function increases the reference counter on the shared memory and + * returns a handle. + * @returns handle to shared memory + */ +struct tee_shm *tee_shm_get_from_fd(int fd); + +/** + * tee_shm_put() - Decrease reference count on a shared memory handle + * @shm: Shared memory handle + */ +void tee_shm_put(struct tee_shm *shm); + +#endif /*__TEE_DRV_H*/
Hi,
On Wed, Mar 25, 2015 at 12:01:40PM +0100, Jens Wiklander wrote:
+static long tee_ioctl_shm_alloc(struct tee_filp *teefilp,
struct tee_ioctl_shm_alloc_data __user *udata)
+{
- long ret;
- struct tee_ioctl_shm_alloc_data data;
- struct tee_shm *shm;
- if (copy_from_user(&data, udata, sizeof(data)))
return -EFAULT;
- /* Currently no input flags are supported */
- if (data.flags)
return -EINVAL;
- shm = tee_shm_alloc(teefilp->teedev, teefilp, data.size,
TEE_SHM_MAPPED | TEE_SHM_DMA_BUF);
- if (IS_ERR(shm))
return PTR_ERR(shm);
- ret = teefilp->teedev->desc->ops->shm_share(shm);
I was thinking a bit when it comes to this function. Now we start by allocating memory (tee_shm_alloc) and then we let the specific driver get a chance to do something with the shared memory we just allocated.
Wouldn't it be nicer to turn things around? I.e, first ask the specific driver if it wants to do something and then if needed call the tee_shm_alloc function? I.e, let the specific driver have a chance to override the default implementation?
For example, lets say that a certain specific driver needs to deal with some shared memory on it's own. So use cases could be, for example: a) The specific driver handles shared memory entirely on its own. b) The specific driver give hints (by settings some flags) to the generic driver. I.e, something like this.
Pseudo... /* This call updates "some_flag" in shm */ ret = teefilp->teedev->desc->ops->shm_share(shm);
u32 flags = TEE_SHM_MAPPED | TEE_SHM_DMA_BUF;
switch (shm.some_flag) { case HANDLED_BY_SPECIFIC: /* * Don't call tee_shm_alloc(), since the specific driver * already has allocated shared memory and don't want the * generic driver to do the same. */ break;
case FEATURE_X_REQUIRED: flags |= DO_SOME_MORE_MAGIC; /* Fall through ... */
case NO_FLAG_SET: default: /* Simply call the generic shm alloc function */ shm = tee_shm_alloc(teefilp->teedev, teefilp, data.size, flags); }
Comments? Thoughts?
- if (ret)
goto err;
- data.flags = shm->flags;
- data.size = shm->size;
- 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;
+}
On Wed, Mar 25, 2015 at 04:44:27PM +0100, Joakim Bech wrote:
Hi,
On Wed, Mar 25, 2015 at 12:01:40PM +0100, Jens Wiklander wrote:
+static long tee_ioctl_shm_alloc(struct tee_filp *teefilp,
struct tee_ioctl_shm_alloc_data __user *udata)
+{
- long ret;
- struct tee_ioctl_shm_alloc_data data;
- struct tee_shm *shm;
- if (copy_from_user(&data, udata, sizeof(data)))
return -EFAULT;
- /* Currently no input flags are supported */
- if (data.flags)
return -EINVAL;
- shm = tee_shm_alloc(teefilp->teedev, teefilp, data.size,
TEE_SHM_MAPPED | TEE_SHM_DMA_BUF);
- if (IS_ERR(shm))
return PTR_ERR(shm);
- ret = teefilp->teedev->desc->ops->shm_share(shm);
I was thinking a bit when it comes to this function. Now we start by allocating memory (tee_shm_alloc) and then we let the specific driver get a chance to do something with the shared memory we just allocated.
Wouldn't it be nicer to turn things around? I.e, first ask the specific driver if it wants to do something and then if needed call the tee_shm_alloc function? I.e, let the specific driver have a chance to override the default implementation?
For example, lets say that a certain specific driver needs to deal with some shared memory on it's own. So use cases could be, for example: a) The specific driver handles shared memory entirely on its own. b) The specific driver give hints (by settings some flags) to the generic driver. I.e, something like this.
Pseudo... /* This call updates "some_flag" in shm */ ret = teefilp->teedev->desc->ops->shm_share(shm);
u32 flags = TEE_SHM_MAPPED | TEE_SHM_DMA_BUF;
switch (shm.some_flag) { case HANDLED_BY_SPECIFIC: /* * Don't call tee_shm_alloc(), since the specific driver * already has allocated shared memory and don't want the * generic driver to do the same. */ break;
case FEATURE_X_REQUIRED: flags |= DO_SOME_MORE_MAGIC; /* Fall through ... */
case NO_FLAG_SET: default: /* Simply call the generic shm alloc function */ shm = tee_shm_alloc(teefilp->teedev, teefilp, data.size, flags); }
Comments? Thoughts?
The original purpose of the "shm_share" callback was to give the specific driver a chance to notify the Secure OS that there's another block of shared memory.
If we want to override the default shared memory allocation in the generic driver I see two other options: 1. Use CMA and assign a specific CMA context to the specific device with dma_declare_contiguous(). We'll need to update the current shm alloc code in the TEE subsystem to use the DMA-API instead (as far as I can tell, that's how CMA is used), but that has more or less been in the plan. 2. We extend to api towards the specific driver allowing it to register its own shared memory allocator.
I would prefer option 1 as it uses services already available in the kernel.
- if (ret)
goto err;
- data.flags = shm->flags;
- data.size = shm->size;
- 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;
+}
-- Regards, Joakim
Adds mostly stubbed OP-TEE driver
Signed-off-by: Jens Wiklander jens.wiklander@linaro.org --- Changes since V3: * Moved stubbed OP-TEE driver into this patch * Adds "-clnt" suffix to name client OP-TEE descriptor * Adds "-supp" suffix to name supplicant OP-TEE descriptor --- drivers/sec-hw/Kconfig | 2 + drivers/sec-hw/Makefile | 1 + drivers/sec-hw/optee/Kconfig | 22 +++++ drivers/sec-hw/optee/Makefile | 1 + drivers/sec-hw/optee/optee.c | 201 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 227 insertions(+) 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
diff --git a/drivers/sec-hw/Kconfig b/drivers/sec-hw/Kconfig index a3292b0..ad2e5db 100644 --- a/drivers/sec-hw/Kconfig +++ b/drivers/sec-hw/Kconfig @@ -26,6 +26,8 @@ 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 index d1b29cf..1c3d990 100644 --- a/drivers/sec-hw/Makefile +++ b/drivers/sec-hw/Makefile @@ -1,2 +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..f60f061 --- /dev/null +++ b/drivers/sec-hw/optee/optee.c @@ -0,0 +1,201 @@ +/* + * 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/slab.h> +#include <linux/sec-hw/tee_drv.h> + +#define DRIVER_NAME "tee-optee" +#define OPTEE_VERSION 1 + +struct optee { + struct tee_device *supp_teedev; + struct tee_device *teedev; + struct device *dev; +}; + +struct optee_filp_state { + int dummy; +}; + +static struct optee_filp_state *optee_new_filp_state(void) +{ + struct optee_filp_state *filpstate; + + filpstate = kzalloc(sizeof(struct optee_filp_state), GFP_KERNEL); + return filpstate; +} + +static void optee_destroy_filp_state(struct optee_filp_state *filpstate) +{ + if (filpstate) + kzfree(filpstate); +} + +static void optee_get_version(struct tee_filp *teefilp, + u32 *version, u8 *uuid) +{ + *version = OPTEE_VERSION; + memset(uuid, 0, TEE_UUID_SIZE); +} + +static int optee_open(struct tee_filp *teefilp) +{ + teefilp->filp_data = optee_new_filp_state(); + if (!teefilp->filp_data) + return -ENOMEM; + return 0; +} + +static void optee_release(struct tee_filp *teefilp) +{ + optee_destroy_filp_state(teefilp->filp_data); + teefilp->filp_data = NULL; +} + +static int optee_cmd(struct tee_filp *teefilp, void __user *buf, size_t len) +{ + return -EINVAL; +} + +static int optee_shm_share(struct tee_shm *shm) +{ + /* No special action needed to share memory with OP-TEE */ + return 0; +} + +static void optee_shm_unshare(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 "-clnt", + .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 "-supp", + .ops = &optee_supp_ops, + .owner = THIS_MODULE, + .flags = 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 client driver\n"); + 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 driver\n"); + ret = -EINVAL; + goto err; + } + + platform_set_drvdata(pdev, optee); + + dev_info(&pdev->dev, "initialized 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");
Hi all, As Joakim proposed, I have committed Jens’ patches (v4) to our development github. This way it should be easier for all to follow development and do testing.
The repository is: https://github.com/TrustZoneGenericDriver/linux I have created a "tee-dev" branch where these first patches will go. When we start having something we all agree on, I am happy with using master for the patches that we intend to commit to LKML.
Please write me if you have any doubts/requests.
Best, Javier
On 25 Mar 2015, at 12:01, Jens Wiklander jens.wiklander@linaro.org wrote:
Adds mostly stubbed OP-TEE driver
Signed-off-by: Jens Wiklander jens.wiklander@linaro.org
Changes since V3:
- Moved stubbed OP-TEE driver into this patch
- Adds "-clnt" suffix to name client OP-TEE descriptor
- Adds "-supp" suffix to name supplicant OP-TEE descriptor
drivers/sec-hw/Kconfig | 2 + drivers/sec-hw/Makefile | 1 + drivers/sec-hw/optee/Kconfig | 22 +++++ drivers/sec-hw/optee/Makefile | 1 + drivers/sec-hw/optee/optee.c | 201 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 227 insertions(+) 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
diff --git a/drivers/sec-hw/Kconfig b/drivers/sec-hw/Kconfig index a3292b0..ad2e5db 100644 --- a/drivers/sec-hw/Kconfig +++ b/drivers/sec-hw/Kconfig @@ -26,6 +26,8 @@ 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 index d1b29cf..1c3d990 100644 --- a/drivers/sec-hw/Makefile +++ b/drivers/sec-hw/Makefile @@ -1,2 +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..f60f061 --- /dev/null +++ b/drivers/sec-hw/optee/optee.c @@ -0,0 +1,201 @@ +/*
- 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/slab.h> +#include <linux/sec-hw/tee_drv.h>
+#define DRIVER_NAME "tee-optee" +#define OPTEE_VERSION 1
+struct optee {
- struct tee_device *supp_teedev;
- struct tee_device *teedev;
- struct device *dev;
+};
+struct optee_filp_state {
- int dummy;
+};
+static struct optee_filp_state *optee_new_filp_state(void) +{
- struct optee_filp_state *filpstate;
- filpstate = kzalloc(sizeof(struct optee_filp_state), GFP_KERNEL);
- return filpstate;
+}
+static void optee_destroy_filp_state(struct optee_filp_state *filpstate) +{
- if (filpstate)
kzfree(filpstate);
+}
+static void optee_get_version(struct tee_filp *teefilp,
u32 *version, u8 *uuid)
+{
- *version = OPTEE_VERSION;
- memset(uuid, 0, TEE_UUID_SIZE);
+}
+static int optee_open(struct tee_filp *teefilp) +{
- teefilp->filp_data = optee_new_filp_state();
- if (!teefilp->filp_data)
return -ENOMEM;
- return 0;
+}
+static void optee_release(struct tee_filp *teefilp) +{
- optee_destroy_filp_state(teefilp->filp_data);
- teefilp->filp_data = NULL;
+}
+static int optee_cmd(struct tee_filp *teefilp, void __user *buf, size_t len) +{
- return -EINVAL;
+}
+static int optee_shm_share(struct tee_shm *shm) +{
- /* No special action needed to share memory with OP-TEE */
- return 0;
+}
+static void optee_shm_unshare(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 "-clnt",
- .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 "-supp",
- .ops = &optee_supp_ops,
- .owner = THIS_MODULE,
- .flags = 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 client driver\n");
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 driver\n");
ret = -EINVAL;
goto err;
- }
- platform_set_drvdata(pdev, optee);
- dev_info(&pdev->dev, "initialized 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");
1.9.1
Tee-dev mailing list Tee-dev@lists.linaro.org http://lists.linaro.org/mailman/listinfo/tee-dev
Hi all,
First, thanks Jens for giving the first step with this patchset :)
A few comments on the interface; I wanted to write before sending a patch with what I propose.
I think that the ioctl is clean and reflects what we discussed. However, the fact that ioctls directly map to the specific drivers defeats the purpose of enabling kernel submodules to access TEE trusted services; user space applications use the ioctls, but they are inaccessible to kernel submodules.
I propose that the ioctl, after taking user space parameters calls an internal set of operations that are also available to the rest of the kernel. You can see how I implemented this in branch “tz_driver_first”; this is what I originally sent to LKLM. Note that here I only have internal operations (tz_open, tz_transmit, etc.), not the ioctls.
There are a couple of challenges in terms of how to maintain a generic session and send generic commands, since each TEE can use different internal structures. The way I chose to solve it is in this patchset too.
I will work on porting what I did on top of Jens’ patches and send something over the day.
Best,
Javier
On 25 Mar 2015, at 12:01, Jens Wiklander jens.wiklander@linaro.org wrote:
Hi,
Here's version 4 of the generic TEE subsystem patch set.
Changes in V4:
- Broken out the stubbed OP-TEE driver into a separate patch
- Addressed review comments
- Added a few more functions to tee_drv.h
- Updated tee.h
Changes in V3:
- Some small changes in the "tee: add user space interface"
- New patch for the subsystem implementation
Jens Wiklander (3): tee: add user space interface tee: generic TEE subsystem tee: add OP-TEE driver
Documentation/ioctl/ioctl-number.txt | 1 + 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 | 201 +++++++++++++++++ drivers/sec-hw/tee.c | 243 +++++++++++++++++++++ drivers/sec-hw/tee_private.h | 42 ++++ drivers/sec-hw/tee_shm.c | 403 +++++++++++++++++++++++++++++++++++ include/linux/sec-hw/tee.h | 180 ++++++++++++++++ include/linux/sec-hw/tee_drv.h | 214 +++++++++++++++++++ 13 files changed, 1346 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.h create mode 100644 include/linux/sec-hw/tee_drv.h
-- 1.9.1
Tee-dev mailing list Tee-dev@lists.linaro.org http://lists.linaro.org/mailman/listinfo/tee-dev
Javier