aboutsummaryrefslogtreecommitdiff
path: root/drivers/tee
diff options
context:
space:
mode:
authorShujuan Chen <shujuan.chen@stericsson.com>2010-06-15 17:27:11 +0200
committerMian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com>2010-10-23 13:29:45 +0200
commitad83d2addfa2686c67dcce030d76d939082b9a3b (patch)
tree7acf2e3dd843c7d25b138c121aaba936690b9745 /drivers/tee
parent9b92f5f8d7a2043ac5593439761c8976552a3cc3 (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/Kconfig12
-rw-r--r--drivers/tee/Makefile16
-rw-r--r--drivers/tee/ta/Makefile8
-rw-r--r--drivers/tee/ta/tee_ta_start_modem.h48
-rw-r--r--drivers/tee/ta/tee_ta_start_modem_svp.c56
-rw-r--r--drivers/tee/tee_driver.c404
-rw-r--r--drivers/tee/tee_service.c16
-rw-r--r--drivers/tee/tee_service_svp.c65
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;
+}