diff options
author | Shujuan Chen <shujuan.chen@stericsson.com> | 2010-06-15 17:27:11 +0200 |
---|---|---|
committer | Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com> | 2010-10-23 13:29:45 +0200 |
commit | ad83d2addfa2686c67dcce030d76d939082b9a3b (patch) | |
tree | 7acf2e3dd843c7d25b138c121aaba936690b9745 /drivers/tee | |
parent | 9b92f5f8d7a2043ac5593439761c8976552a3cc3 (diff) |
add trusted execution environment (tee) driver
Signed-off-by: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com>
Change-Id: I5ef78d16c8a79177638ef96e4fe25f4d6d0cbf35
Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/2572
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
Diffstat (limited to 'drivers/tee')
-rw-r--r-- | drivers/tee/Kconfig | 12 | ||||
-rw-r--r-- | drivers/tee/Makefile | 16 | ||||
-rw-r--r-- | drivers/tee/ta/Makefile | 8 | ||||
-rw-r--r-- | drivers/tee/ta/tee_ta_start_modem.h | 48 | ||||
-rw-r--r-- | drivers/tee/ta/tee_ta_start_modem_svp.c | 56 | ||||
-rw-r--r-- | drivers/tee/tee_driver.c | 404 | ||||
-rw-r--r-- | drivers/tee/tee_service.c | 16 | ||||
-rw-r--r-- | drivers/tee/tee_service_svp.c | 65 |
8 files changed, 625 insertions, 0 deletions
diff --git a/drivers/tee/Kconfig b/drivers/tee/Kconfig new file mode 100644 index 00000000000..3056a916600 --- /dev/null +++ b/drivers/tee/Kconfig @@ -0,0 +1,12 @@ +# +# Copyright (C) ST-Ericsson SA 2010 +# Author: Martin Hovang (martin.xm.hovang@stericsson.com) +# License terms: GNU General Public License (GPL) version 2 +# + +# Trursted Execution Environment Configuration +config TEE_SUPPORT + bool "Trusted Execution Environment Support" + default y + ---help--- + This enables the Trusted Execution Environment (TEE) support. diff --git a/drivers/tee/Makefile b/drivers/tee/Makefile new file mode 100644 index 00000000000..8203426fd79 --- /dev/null +++ b/drivers/tee/Makefile @@ -0,0 +1,16 @@ +# +# Copyright (C) ST-Ericsson SA 2010 +# Author: Martin Hovang (martin.xm.hovang@stericsson.com) +# License terms: GNU General Public License (GPL) version 2 +# + +EXTRA_CFLAGS = -O0 + +ifdef CONFIG_MACH_U5500_SIMULATOR + obj-$(CONFIG_TEE_SUPPORT) += tee_service_svp.o +else + obj-$(CONFIG_TEE_SUPPORT) += tee_service.o +endif + +obj-$(CONFIG_TEE_SUPPORT) += tee_driver.o +obj-$(CONFIG_TEE_SUPPORT) += ta/ diff --git a/drivers/tee/ta/Makefile b/drivers/tee/ta/Makefile new file mode 100644 index 00000000000..70775154e18 --- /dev/null +++ b/drivers/tee/ta/Makefile @@ -0,0 +1,8 @@ +# +# Copyright (C) ST-Ericsson SA 2010 +# Author: Martin Hovang (martin.xm.hovang@stericsson.com) +# License terms: GNU General Public License (GPL) version 2 +# + +obj-y += tee_ta_start_modem_svp.o + diff --git a/drivers/tee/ta/tee_ta_start_modem.h b/drivers/tee/ta/tee_ta_start_modem.h new file mode 100644 index 00000000000..648f0e0827e --- /dev/null +++ b/drivers/tee/ta/tee_ta_start_modem.h @@ -0,0 +1,48 @@ +/* + * Data types and interface for TEE application for starting the modem. + * + * Copyright (C) ST-Ericsson SA 2010 + * Author: Shujuan Chen <shujuan.chen@stericsson.com> + * License terms: GNU General Public License (GPL) version 2 + */ + +#ifndef TEE_TA_START_MODEM_H +#define TEE_TA_START_MODEM_H + +#define COMMAND_ID_START_MODEM 0x00000001 + +#define UUID_TEE_TA_START_MODEM_LOW 0x8AD94107 +#define UUID_TEE_TA_START_MODEM_MID 0x6E50 +#define UUID_TEE_TA_START_MODEM_HIGH 0x418E +#define UUID_TEE_TA_START_MODEM_CLOCKSEQ \ + {0xB1, 0x14, 0x75, 0x7D, 0x60, 0x21, 0xBD, 0x36} + +struct mcore_segment_descr { + void *segment; + void *hash; + size_t size; +}; + +struct access_image_descr { + void *elf_hdr; + void *pgm_hdr_tbl; + void *signature; + unsigned long nbr_segment; + struct mcore_segment_descr *descr; +}; + +/* TODO: To be redefined with only info needed by Secure world. */ +struct tee_ta_start_modem { + void *access_mem_start; + size_t shared_mem_size; + size_t access_private_mem_size; + struct access_image_descr access_image_descr; +}; + +/** + * This is the function to handle the modem release. + */ +int tee_ta_start_modem(struct tee_ta_start_modem *data); + +#endif + diff --git a/drivers/tee/ta/tee_ta_start_modem_svp.c b/drivers/tee/ta/tee_ta_start_modem_svp.c new file mode 100644 index 00000000000..2ab2adb82b2 --- /dev/null +++ b/drivers/tee/ta/tee_ta_start_modem_svp.c @@ -0,0 +1,56 @@ +/* + * Trusted application for starting the modem. + * + * Copyright (C) ST-Ericsson SA 2010 + * Author: Shujuan Chen <shujuan.chen@stericsson.com> + * License terms: GNU General Public License (GPL) version 2 + */ + +#include <linux/err.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/elf.h> +#include <mach/hardware.h> + +#include "tee_ta_start_modem.h" + +static int reset_modem(unsigned long modem_start_addr) +{ + unsigned char *base = ioremap(ACCCON_BASE_SEC, 0x2FF); + if (!base) + return -ENOMEM; + + printk(KERN_INFO "[reset_modem] Setting modem start address!\n"); + writel(base + (ACCCON_CPUVEC_RESET_ADDR_OFFSET/sizeof(uint32_t)), + modem_start_addr); + + printk(KERN_INFO "[reset_modem] resetting the modem!\n"); + writel(base + (ACCCON_ACC_CPU_CTRL_OFFSET/sizeof(uint32_t)), 1); + + iounmap(base); + + return 0; +} + +int tee_ta_start_modem(struct tee_ta_start_modem *data) +{ + int ret = 0; + struct elfhdr *elfhdr; + unsigned char *vaddr; + + vaddr = ioremap((unsigned long)data->access_image_descr.elf_hdr, + sizeof(struct elfhdr)); + if (!vaddr) + return -ENOMEM; + + elfhdr = (struct elfhdr *)readl(vaddr); + printk(KERN_INFO "Reading in kernel:elfhdr 0x%x:elfhdr->entry=0x%x\n", + (uint32_t)elfhdr, (uint32_t)elfhdr->e_entry); + + printk(KERN_INFO "[tee_ta_start_modem] reset modem()...\n"); + ret = reset_modem(elfhdr->e_entry); + + iounmap(vaddr); + + return ret; +} diff --git a/drivers/tee/tee_driver.c b/drivers/tee/tee_driver.c new file mode 100644 index 00000000000..5f9ea5744e1 --- /dev/null +++ b/drivers/tee/tee_driver.c @@ -0,0 +1,404 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Martin Hovang <martin.xm.hovang@stericsson.com> + * Author: Joakim Bech <joakim.xx.bech@stericsson.com> + * License terms: GNU General Public License (GPL) version 2 + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/cdev.h> +#include <linux/mutex.h> +#include <linux/miscdevice.h> +#include <linux/uaccess.h> +#include <linux/tee.h> + +#define TEED_NAME "tee" + +#define TEED_STATE_OPEN_DEV 0 +#define TEED_STATE_OPEN_SESSION 1 + +static int tee_open(struct inode *inode, struct file *file); +static int tee_release(struct inode *inode, struct file *file); +static int tee_read(struct file *filp, char __user *buffer, + size_t length, loff_t *offset); +static int tee_write(struct file *filp, const char __user *buffer, + size_t length, loff_t *offset); + +static inline void set_emsg(struct tee_session *ts, u32 msg) +{ + ts->err = msg; + ts->origin = TEED_ORIGIN_DRIVER; +} + +static int copy_ta(struct tee_session *ts, + struct tee_write *buf) +{ + ts->ta = kmalloc(buf->ta_size, GFP_KERNEL); + if (ts->ta == NULL) { + printk(KERN_INFO "[%s] error, out of memory (ta)\n", + __func__); + set_emsg(ts, TEED_ERROR_OUT_OF_MEMORY); + return -ENOMEM; + } + + ts->ta_size = buf->ta_size; + + memcpy(ts->ta, buf->ta, buf->ta_size); + return 0; +} + +static int copy_uuid(struct tee_session *ts, + struct tee_write *buf) +{ + ts->uuid = kmalloc(sizeof(struct tee_uuid), GFP_KERNEL); + + if (ts->uuid == NULL) { + printk(KERN_INFO "[%s] error, out of memory (uuid)\n", + __func__); + set_emsg(ts, TEED_ERROR_OUT_OF_MEMORY); + return -ENOMEM; + } + + memcpy(ts->uuid, buf->uuid, sizeof(struct tee_uuid)); + + return 0; +} + +static int open_tee_device(struct tee_session *ts, + struct tee_write *buf) +{ + int ret; + + if (buf->id != TEED_OPEN_SESSION) { + set_emsg(ts, TEED_ERROR_BAD_STATE); + return -EINVAL; + } + + if (buf->ta) { + ret = copy_ta(ts, buf); + } else if (buf->uuid) { + ret = copy_uuid(ts, buf); + } else { + set_emsg(ts, TEED_ERROR_COMMUNICATION); + return -EINVAL; + } + + ts->id = 0; + ts->state = TEED_STATE_OPEN_SESSION; + return ret; +} + +static int invoke_command(struct tee_session *ts, + struct tee_write *kbuf, + struct tee_write __user *ubuf) +{ + int i; + int ret = 0; + struct tee_operation *ubuf_op = + (struct tee_operation *)kbuf->inData; + + ts->idata = kmalloc(sizeof(struct tee_operation), GFP_KERNEL); + + if (!ts->idata) { + if (ts->idata == NULL) { + printk(KERN_INFO "[%s] error, out of memory " + "(idata)\n", __func__); + set_emsg(ts, TEED_ERROR_OUT_OF_MEMORY); + ret = -ENOMEM; + goto err; + } + } + + /* Copy memrefs to kernel space */ + ts->idata->flags = ubuf_op->flags; + ts->cmd = kbuf->cmd; + + for (i = 0; i < 4; ++i) { + if (ubuf_op->flags & (1 << i)) { + ts->idata->shm[i].buffer = + kmalloc(ubuf_op->shm[i].size, + GFP_KERNEL); + + if (!ts->idata->shm[i].buffer) { + printk(KERN_ERR "[%s] out of memory\n", + __func__); + set_emsg(ts, TEED_ERROR_OUT_OF_MEMORY); + ret = -ENOMEM; + goto err; + } + /* + * Copy all shared memory operations to a local + * kernel buffer. + */ + memcpy(ts->idata->shm[i].buffer, + ubuf_op->shm[i].buffer, + ubuf_op->shm[i].size); + + ts->idata->shm[i].size = ubuf_op->shm[i].size; + ts->idata->shm[i].flags = ubuf_op->shm[i].flags; + + /* Secure world expects physical addresses. */ + ts->idata->shm[i].buffer = + virt_to_phys(ts->idata->shm[i].buffer); + } else { + ts->idata->shm[i].buffer = NULL; + ts->idata->shm[i].size = 0; + ts->idata->shm[i].flags = 0; + } + } + + /* To call secure world */ + if (call_sec_world(ts)) { + ret = -EINVAL; + goto err; + } + + /* + * Convert physical addresses back to virtual address so the kernel can + * free the buffers when closing the session. + */ + for (i = 0; i < 4; ++i) { + if (ubuf_op->flags & (1 << i)) { + ts->idata->shm[i].buffer = + phys_to_virt(ts->idata->shm[i].buffer); + } + } + + for (i = 0; i < 4; ++i) { + if (ubuf_op->flags & (1 << i)) { + u32 bytes_left = copy_to_user(ubuf_op->shm[i].buffer, + ts->idata->shm[i].buffer, + ts->idata->shm[i].size); + if (bytes_left != 0) { + printk(KERN_ERR "[%s] Failed to copy result to " + "user space (%d bytes left).\n", __func__, bytes_left); + } + } + } + +err: + if (ret) { + for (i = 0; i < 4; ++i) + kfree(ts->idata->shm[i].buffer); + + kfree(ts->idata); + } + + return ret; +} + +static int tee_open(struct inode *inode, struct file *filp) +{ + struct tee_session *ts; + + filp->private_data = kmalloc(sizeof(struct tee_session), GFP_KERNEL); + + if (filp->private_data == NULL) + return -ENOMEM; + + ts = (struct tee_session *) (filp->private_data); + ts->state = TEED_STATE_OPEN_DEV; + ts->err = TEED_SUCCESS; + ts->origin = TEED_ORIGIN_DRIVER; + ts->sync = kmalloc(sizeof(struct mutex), GFP_KERNEL); + mutex_init(ts->sync); + ts->ta = NULL; + ts->uuid = NULL; + ts->id = 0; + ts->idata = NULL; + ts->ta_size = 0; + ts->err = TEED_SUCCESS; + + return 0; +} + +static int tee_release(struct inode *inode, struct file *filp) +{ + struct tee_session *ts; + int i; + + ts = (struct tee_session *) (filp->private_data); + + if (ts != NULL) { + if (ts->idata) { + for (i = 0; i < 4; ++i) { + kfree(ts->idata->shm[i].buffer); + ts->idata->shm[i].buffer = NULL; + } + + } + + kfree(ts->idata); + ts->idata = NULL; + + kfree(ts->sync); + ts->sync = NULL; + + kfree(ts->ta); + ts->ta = NULL; + } + + kfree(filp->private_data); + filp->private_data = NULL; + + return 0; +} + +/* + * Called when a process, which already opened the dev file, attempts + * to read from it. This function gets the current status of the session. + */ +static int tee_read(struct file *filp, char __user *buffer, + size_t length, loff_t *offset) +{ + struct tee_read buf; + struct tee_session *ts; + + if (length != sizeof(struct tee_read)) { + printk(KERN_INFO "[%s] error, incorrect input length\n", + __func__); + return -EINVAL; + } + + ts = (struct tee_session *) (filp->private_data); + + if (ts == NULL || ts->sync == NULL) { + printk(KERN_INFO "[%s] error, private_data not initialized\n", + __func__); + return -EINVAL; + } + + mutex_lock(ts->sync); + + buf.id = ts->err; + buf.origin = ts->origin; + + mutex_unlock(ts->sync); + + if (copy_to_user(buffer, &buf, length)) { + printk(KERN_INFO "[%s] error, copy_to_user failed!\n", + __func__); + return -EINVAL; + } + + return length; +} + +/* + * Called when a process writes to a dev file + */ +static int tee_write(struct file *filp, const char __user *buffer, + size_t length, loff_t *offset) +{ + struct tee_write buf; + struct tee_session *ts; + int ret = length; + + if (length != sizeof(struct tee_write)) { + printk(KERN_INFO "[%s] error, incorrect input length\n", + __func__); + return -EINVAL; + } + + if (copy_from_user(&buf, buffer, length)) { + printk(KERN_INFO "[%s] error, tee_session copy_from_user " + "failed\n", __func__); + return -EINVAL; + } + + ts = (struct tee_session *) (filp->private_data); + + if (ts == NULL || ts->sync == NULL) { + printk(KERN_INFO "[%s] error, private_data not initialized\n", + __func__); + return -EINVAL; + } + + mutex_lock(ts->sync); + + switch (ts->state) { + case TEED_STATE_OPEN_DEV: + ret = open_tee_device(ts, &buf); + break; + + case TEED_STATE_OPEN_SESSION: + switch (buf.id) { + case TEED_INVOKE: + ret = invoke_command(ts, &buf, + (struct tee_write *)buffer); + break; + + case TEED_CLOSE_SESSION: + /* no caching implemented yet... */ + kfree(ts->ta); + ts->ta = NULL; + + ts->state = TEED_STATE_OPEN_DEV; + break; + + default: + set_emsg(ts, TEED_ERROR_BAD_PARAMETERS); + ret = -EINVAL; + } + break; + default: + printk(KERN_INFO "[%s] unknown state\n", __func__); + set_emsg(ts, TEED_ERROR_BAD_STATE); + ret = -EINVAL; + } + + /* + * We expect that ret has value zero when reaching the end here. If + * it has any other value some error must have occured. + */ + if (!ret) + ret = length; + else + ret = -EINVAL; + + mutex_unlock(ts->sync); + + return ret; +} + +static const struct file_operations tee_fops = { + .owner = THIS_MODULE, + .read = tee_read, + .write = tee_write, + .open = tee_open, + .release = tee_release, +}; + +static struct miscdevice tee_dev = { + MISC_DYNAMIC_MINOR, + TEED_NAME, + &tee_fops +}; + +static int __init tee_init(void) +{ + int err = 0; + + err = misc_register(&tee_dev); + + if (err) { + printk(KERN_ERR "[%s] error %d adding character device TEE\n", + __func__, err); + } + + return err; +} + +static void __exit tee_exit(void) +{ + misc_deregister(&tee_dev); +} + +module_init(tee_init); +module_exit(tee_exit); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("Trusted Execution Enviroment driver"); diff --git a/drivers/tee/tee_service.c b/drivers/tee/tee_service.c new file mode 100644 index 00000000000..47dcdcd5f0d --- /dev/null +++ b/drivers/tee/tee_service.c @@ -0,0 +1,16 @@ +/* + * TEE service to handle the calls to trusted applications. + * + * Copyright (C) ST-Ericsson SA 2010 + * Author: Joakim Bech <joakim.xx.bech@stericsson.com> + * License terms: GNU General Public License (GPL) version 2 + */ +#include <linux/kernel.h> +#include <linux/tee.h> + +int __weak call_sec_world(struct tee_session *ts) +{ + printk(KERN_INFO "[%s] Generic call_sec_world called!\n", __func__); + + return 0; +} diff --git a/drivers/tee/tee_service_svp.c b/drivers/tee/tee_service_svp.c new file mode 100644 index 00000000000..a5bd3254f30 --- /dev/null +++ b/drivers/tee/tee_service_svp.c @@ -0,0 +1,65 @@ +/* + * TEE service to handle the calls to trusted applications in SVP. + * + * Copyright (C) ST-Ericsson SA 2010 + * Author: Shujuan Chen <shujuan.chen@stericsson.com> + * License terms: GNU General Public License (GPL) version 2 + */ + +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/tee.h> + +#include "ta/tee_ta_start_modem.h" + +static int cmp_uuid_start_modem(struct tee_uuid *uuid) +{ + int ret = -EINVAL; + + if (uuid == NULL) + return ret; + + /* This handles the calls to TA for start the modem */ + if ((uuid->timeLow == UUID_TEE_TA_START_MODEM_LOW) && + (uuid->timeMid == UUID_TEE_TA_START_MODEM_MID) && + (uuid->timeHiAndVersion == UUID_TEE_TA_START_MODEM_HIGH)) { + + uint8_t clockSeqAndNode[TEE_UUID_CLOCK_SIZE] = + UUID_TEE_TA_START_MODEM_CLOCKSEQ; + + ret = memcmp(uuid->clockSeqAndNode, clockSeqAndNode, + TEE_UUID_CLOCK_SIZE); + } + + return ret; +} + +int call_sec_world(struct tee_session *ts) +{ + int ret = -EINVAL; + + printk(KERN_INFO "call_sec_world() is called!\n"); + + if (ts == NULL) + return ret; + + if (!cmp_uuid_start_modem(ts->uuid)) { + switch (ts->cmd) { + case COMMAND_ID_START_MODEM: + ret = tee_ta_start_modem((struct tee_ta_start_modem *) + ts->its); + if (ret) { + ts->err = TEED_ERROR_GENERIC; + ts->origin = TEED_ORIGIN_TEE_APPLICATION; + printk(KERN_INFO + "tee_ta_start_modem() failed!\n"); + } + default: + break; + } + } + + /* TODO: to handle more trusted applications. */ + + return ret; +} |