aboutsummaryrefslogtreecommitdiff
path: root/drivers/misc/mediatek/gud
diff options
context:
space:
mode:
authorfire855 <thefire855@gmail.com>2017-02-24 17:48:24 +0100
committerMister Oyster <oysterized@gmail.com>2017-04-11 10:59:46 +0200
commit504261abd2b3a69cb609ef9ccf4e58ae9ccad566 (patch)
treeeb5caf24cfcb12a7dbb2dbe38eaa1e68d4c26a3c /drivers/misc/mediatek/gud
parentd547e0f39015f8e8ec1dba8bd9d66c1beb24eb41 (diff)
Update m4u, smi and gud drivers
Backported from 3.18 MM kernel
Diffstat (limited to 'drivers/misc/mediatek/gud')
-rw-r--r--drivers/misc/mediatek/gud/302a/Makefile (renamed from drivers/misc/mediatek/gud/mt6735/Makefile)0
-rw-r--r--[-rwxr-xr-x]drivers/misc/mediatek/gud/302a/gud/Kconfig (renamed from drivers/misc/mediatek/gud/mt6735/gud/Kconfig)0
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/Makefile57
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/api.c (renamed from drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/api.c)0
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/arm.h (renamed from drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/arm.h)0
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/debug.h (renamed from drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/debug.h)0
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/fastcall.h (renamed from drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/fastcall.h)0
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/logging.c (renamed from drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/logging.c)0
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/logging.h (renamed from drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/logging.h)0
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/main.c (renamed from drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/main.c)11
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/main.h (renamed from drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/main.h)0
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/mem.c (renamed from drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/mem.c)0
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/mem.h (renamed from drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/mem.h)0
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/ops.c (renamed from drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/ops.c)0
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/ops.h (renamed from drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/ops.h)0
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/platform.h (renamed from drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/platform.h)2
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/pm.c (renamed from drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/pm.c)0
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/pm.h (renamed from drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/pm.h)0
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/public/mc_kernel_api.h (renamed from drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/public/mc_kernel_api.h)0
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/public/mc_linux.h (renamed from drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/public/mc_linux.h)0
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/public/version.h (renamed from drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/public/version.h)0
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/MobiCoreKernelApi/clientlib.c (renamed from drivers/misc/mediatek/gud/mt6735/gud/MobiCoreKernelApi/clientlib.c)28
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/MobiCoreKernelApi/common.h (renamed from drivers/misc/mediatek/gud/mt6735/gud/MobiCoreKernelApi/common.h)3
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/MobiCoreKernelApi/connection.c (renamed from drivers/misc/mediatek/gud/mt6735/gud/MobiCoreKernelApi/connection.c)50
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/MobiCoreKernelApi/connection.h (renamed from drivers/misc/mediatek/gud/mt6735/gud/MobiCoreKernelApi/connection.h)2
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/MobiCoreKernelApi/device.c (renamed from drivers/misc/mediatek/gud/mt6735/gud/MobiCoreKernelApi/device.c)89
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/MobiCoreKernelApi/device.h (renamed from drivers/misc/mediatek/gud/mt6735/gud/MobiCoreKernelApi/device.h)4
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/MobiCoreKernelApi/include/mcinq.h (renamed from drivers/misc/mediatek/gud/mt6735/gud/MobiCoreKernelApi/include/mcinq.h)7
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/MobiCoreKernelApi/include/mcuuid.h (renamed from drivers/misc/mediatek/gud/mt6735/gud/MobiCoreKernelApi/include/mcuuid.h)0
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/MobiCoreKernelApi/main.c (renamed from drivers/misc/mediatek/gud/mt6735/gud/MobiCoreKernelApi/main.c)4
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/MobiCoreKernelApi/public/mobicore_driver_api.h (renamed from drivers/misc/mediatek/gud/mt6735/gud/MobiCoreKernelApi/public/mobicore_driver_api.h)0
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/MobiCoreKernelApi/public/mobicore_driver_cmd.h (renamed from drivers/misc/mediatek/gud/mt6735/gud/MobiCoreKernelApi/public/mobicore_driver_cmd.h)0
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/MobiCoreKernelApi/session.c (renamed from drivers/misc/mediatek/gud/mt6735/gud/MobiCoreKernelApi/session.c)31
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/MobiCoreKernelApi/session.h (renamed from drivers/misc/mediatek/gud/mt6735/gud/MobiCoreKernelApi/session.h)2
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/MobiCoreKernelApi/wsm.h (renamed from drivers/misc/mediatek/gud/mt6735/gud/MobiCoreKernelApi/wsm.h)0
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/TlcTui/Out/Public/tui_ioctl.h48
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/TlcTui/build_tag.h (renamed from drivers/misc/mediatek/gud/mt6735/gud/build_tag.h)2
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/TlcTui/inc/dciTui.h108
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/TlcTui/inc/t-base-tui.h38
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/TlcTui/main.c164
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/TlcTui/public/tui_ioctl.h48
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/TlcTui/tlcTui.c380
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/TlcTui/tlcTui.h22
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/TlcTui/trustedui.c131
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/TlcTui/tui-hal.h28
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/TlcTui/tui-hal_mt6735.c344
-rw-r--r--drivers/misc/mediatek/gud/302a/gud/build_tag.h15
-rw-r--r--drivers/misc/mediatek/gud/302c/Makefile7
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/Kconfig42
-rw-r--r--[-rwxr-xr-x]drivers/misc/mediatek/gud/302c/gud/Makefile (renamed from drivers/misc/mediatek/gud/mt6735/gud/Makefile)13
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/api.c118
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/arm.h87
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/debug.h64
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/fastcall.h258
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/logging.c384
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/logging.h30
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/main.c1733
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/main.h155
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/mem.c813
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/mem.h148
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/ops.c421
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/ops.h37
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/platform.h52
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/pm.c307
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/pm.h47
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/public/mc_kernel_api.h88
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/public/mc_linux.h217
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/public/version.h20
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/clientlib.c1079
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/common.h83
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/connection.c199
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/connection.h61
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/device.c259
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/device.h67
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/include/mcinq.h110
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/include/mcuuid.h24
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/main.c215
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/public/mobicore_driver_api.h399
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/public/mobicore_driver_cmd.h250
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/session.c225
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/session.h148
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/wsm.h30
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/TlcTui/Out/Public/tui_ioctl.h48
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/TlcTui/build_tag.h15
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/TlcTui/inc/dciTui.h108
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/TlcTui/inc/t-base-tui.h38
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/TlcTui/main.c173
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/TlcTui/public/tui_ioctl.h48
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/TlcTui/tlcTui.c380
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/TlcTui/tlcTui.h23
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/TlcTui/trustedui.c131
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/TlcTui/tui-hal.h28
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/TlcTui/tui-hal_mt6735.c414
-rw-r--r--drivers/misc/mediatek/gud/302c/gud/build_tag.h15
-rw-r--r--drivers/misc/mediatek/gud/Kconfig68
-rw-r--r--[-rwxr-xr-x]drivers/misc/mediatek/gud/Makefile15
-rw-r--r--drivers/misc/mediatek/gud/Makefile.include17
97 files changed, 11243 insertions, 86 deletions
diff --git a/drivers/misc/mediatek/gud/mt6735/Makefile b/drivers/misc/mediatek/gud/302a/Makefile
index 4937fb49c..4937fb49c 100644
--- a/drivers/misc/mediatek/gud/mt6735/Makefile
+++ b/drivers/misc/mediatek/gud/302a/Makefile
diff --git a/drivers/misc/mediatek/gud/mt6735/gud/Kconfig b/drivers/misc/mediatek/gud/302a/gud/Kconfig
index 9d210a7a4..9d210a7a4 100755..100644
--- a/drivers/misc/mediatek/gud/mt6735/gud/Kconfig
+++ b/drivers/misc/mediatek/gud/302a/gud/Kconfig
diff --git a/drivers/misc/mediatek/gud/302a/gud/Makefile b/drivers/misc/mediatek/gud/302a/gud/Makefile
new file mode 100644
index 000000000..e752c0e54
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302a/gud/Makefile
@@ -0,0 +1,57 @@
+#
+# Makefile for the kernel mobicore drivers
+#
+
+ifneq ($(MTK_ROOT_BUILD),)
+include $(MTK_ROOT_BUILD)/Makefile
+endif
+
+GUD_ROOT_FOLDER := $(dir $(lastword $(MAKEFILE_LIST)))
+# add our modules to kernel.
+obj-$(CONFIG_TRUSTONIC_TEE_SUPPORT) += mcKernelApi.o
+obj-$(CONFIG_TRUSTONIC_TEE_SUPPORT) += mcDrvModule.o
+obj-$(CONFIG_TRUSTONIC_TRUSTED_UI) += TlcTui.o
+
+mcDrvModule-objs := MobiCoreDriver/logging.o \
+ MobiCoreDriver/ops.o \
+ MobiCoreDriver/mem.o \
+ MobiCoreDriver/api.o \
+ MobiCoreDriver/pm.o \
+ MobiCoreDriver/main.o
+
+mcKernelApi-objs := MobiCoreKernelApi/main.o \
+ MobiCoreKernelApi/clientlib.o \
+ MobiCoreKernelApi/device.o \
+ MobiCoreKernelApi/session.o \
+ MobiCoreKernelApi/connection.o
+
+TlcTui-objs := TlcTui/main.o \
+ TlcTui/tlcTui.o \
+ TlcTui/trustedui.o \
+ TlcTui/tui-hal_$(MTK_PLATFORM).o
+
+# Release mode by default
+ccflags-y := -DNDEBUG
+ccflags-y += -Wno-declaration-after-statement
+#ccflags-y += -Wno-error=date-time
+
+ccflags-$(CONFIG_MOBICORE_DEBUG) += -DDEBUG
+ccflags-$(CONFIG_MOBICORE_VERBOSE) += -DDEBUG_VERBOSE
+
+# Choose one platform from the folder
+#MOBICORE_PLATFORM := $(shell (ls -1 $(GUD_ROOT_FOLDER)MobiCoreDriver/platforms | tail -1) )
+ccflags-y += -DMC_NETLINK_COMPAT_V37
+
+# Use the available platform folder
+#ccflags-y += -I$(GUD_ROOT_FOLDER)MobiCoreDriver/platforms/$(MOBICORE_PLATFORM)
+# MobiCore Driver includes
+ccflags-y += -I$(GUD_ROOT_FOLDER)MobiCoreDriver/public
+# MobiCore KernelApi required incldes
+ccflags-y += -I$(GUD_ROOT_FOLDER)MobiCoreKernelApi/include \
+ -I$(GUD_ROOT_FOLDER)MobiCoreKernelApi/public
+
+# MobiCore TlcTui required includes
+ccflags-y += -I$(GUD_ROOT_FOLDER)/TlcTui \
+ -I$(GUD_ROOT_FOLDER)/TlcTui/inc \
+ -I$(GUD_ROOT_FOLDER)/TlcTui/public \
+ include
diff --git a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/api.c b/drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/api.c
index 354f5ddff..354f5ddff 100644
--- a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/api.c
+++ b/drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/api.c
diff --git a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/arm.h b/drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/arm.h
index 8c9fc37ee..8c9fc37ee 100644
--- a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/arm.h
+++ b/drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/arm.h
diff --git a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/debug.h b/drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/debug.h
index 52362b346..52362b346 100644
--- a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/debug.h
+++ b/drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/debug.h
diff --git a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/fastcall.h b/drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/fastcall.h
index b438d7244..b438d7244 100644
--- a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/fastcall.h
+++ b/drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/fastcall.h
diff --git a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/logging.c b/drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/logging.c
index 044e297df..044e297df 100644
--- a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/logging.c
+++ b/drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/logging.c
diff --git a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/logging.h b/drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/logging.h
index a3cbca21c..a3cbca21c 100644
--- a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/logging.h
+++ b/drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/logging.h
diff --git a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/main.c b/drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/main.c
index 74ddcee71..6c51fafe5 100644
--- a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/main.c
+++ b/drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/main.c
@@ -112,9 +112,11 @@ static struct mc_instance *get_instance(struct file *file)
uint32_t mc_get_new_handle(void)
{
+ static DEFINE_MUTEX(local_mutex);
uint32_t handle;
struct mc_buffer *buffer;
- /* assumption ctx.bufs_lock mutex is locked */
+
+ mutex_lock(&local_mutex);
retry:
handle = atomic_inc_return(&ctx.handle_counter);
/* The handle must leave 12 bits (PAGE_SHIFT) for the 12 LSBs to be
@@ -130,6 +132,7 @@ retry:
if (buffer->handle == handle)
goto retry;
}
+ mutex_unlock(&local_mutex);
return handle;
}
@@ -1509,7 +1512,7 @@ out:
* This device is installed and registered as cdev, then interrupt and
* queue handling is set up
*/
-static unsigned int mobicore_irq_id = MC_INTR_SSIQ;
+static unsigned int mobicore_irq_id = MC_INTR_SSIQ;
static int __init mobicore_init(void)
{
int ret = 0;
@@ -1524,7 +1527,7 @@ static int __init mobicore_init(void)
/* Do not remove or change the following trace.
* The string "MobiCore" is used to detect if <t-base is in of the image
*/
- dev_info(mcd, "MobiCore Driver, Build: " __TIMESTAMP__ "\n");
+ dev_info(mcd, "MobiCore Driver, Build: " "\n");
dev_info(mcd, "MobiCore mcDrvModuleApi version is %i.%i\n",
MCDRVMODULEAPI_VERSION_MAJOR,
MCDRVMODULEAPI_VERSION_MINOR);
@@ -1615,8 +1618,8 @@ free_pm:
#ifdef MC_PM_RUNTIME
mc_pm_free();
free_isr:
- free_irq(mobicore_irq_id, &ctx);
#endif
+ free_irq(MC_INTR_SSIQ, &ctx);
err_req_irq:
mc_fastcall_destroy();
error:
diff --git a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/main.h b/drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/main.h
index 32ffb95e1..32ffb95e1 100644
--- a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/main.h
+++ b/drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/main.h
diff --git a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/mem.c b/drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/mem.c
index d65a91fee..d65a91fee 100644
--- a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/mem.c
+++ b/drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/mem.c
diff --git a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/mem.h b/drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/mem.h
index c4b6715f2..c4b6715f2 100644
--- a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/mem.h
+++ b/drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/mem.h
diff --git a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/ops.c b/drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/ops.c
index ad9e9e243..ad9e9e243 100644
--- a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/ops.c
+++ b/drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/ops.c
diff --git a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/ops.h b/drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/ops.h
index 30458a37d..30458a37d 100644
--- a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/ops.h
+++ b/drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/ops.h
diff --git a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/platform.h b/drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/platform.h
index 9f59b380d..e08a84215 100644
--- a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/platform.h
+++ b/drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/platform.h
@@ -34,7 +34,7 @@
#define MC_PM_RUNTIME
#endif
-// #define TBASE_CORE_SWITCHER
+#define TBASE_CORE_SWITCHER
/* Values of MPIDR regs in cpu0, cpu1, cpu2, cpu3*/
#define CPU_IDS {0x0000, 0x0001, 0x0002, 0x0003, 0x0100, 0x0101, 0x0102, 0x0103}
#define COUNT_OF_CPUS 8
diff --git a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/pm.c b/drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/pm.c
index e3ea6b530..e3ea6b530 100644
--- a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/pm.c
+++ b/drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/pm.c
diff --git a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/pm.h b/drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/pm.h
index 6581425a7..6581425a7 100644
--- a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/pm.h
+++ b/drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/pm.h
diff --git a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/public/mc_kernel_api.h b/drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/public/mc_kernel_api.h
index 96805fda1..96805fda1 100644
--- a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/public/mc_kernel_api.h
+++ b/drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/public/mc_kernel_api.h
diff --git a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/public/mc_linux.h b/drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/public/mc_linux.h
index b9c4934d5..b9c4934d5 100644
--- a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/public/mc_linux.h
+++ b/drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/public/mc_linux.h
diff --git a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/public/version.h b/drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/public/version.h
index 8db48a09b..8db48a09b 100644
--- a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreDriver/public/version.h
+++ b/drivers/misc/mediatek/gud/302a/gud/MobiCoreDriver/public/version.h
diff --git a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreKernelApi/clientlib.c b/drivers/misc/mediatek/gud/302a/gud/MobiCoreKernelApi/clientlib.c
index a951e696f..39f81d3dc 100644
--- a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreKernelApi/clientlib.c
+++ b/drivers/misc/mediatek/gud/302a/gud/MobiCoreKernelApi/clientlib.c
@@ -29,6 +29,8 @@
/* device list */
LIST_HEAD(devices);
+/* lock used to prevent concurrent add/delete action on the device list */
+struct mutex device_mutex;
atomic_t device_usage = ATOMIC_INIT(0);
static struct mcore_device_t *resolve_device_id(uint32_t device_id)
@@ -37,33 +39,45 @@ static struct mcore_device_t *resolve_device_id(uint32_t device_id)
struct list_head *pos;
/* Get mcore_device_t for device_id */
+ mutex_lock(&device_mutex);
list_for_each(pos, &devices) {
tmp = list_entry(pos, struct mcore_device_t, list);
- if (tmp->device_id == device_id)
+ if (tmp->device_id == device_id) {
+ mutex_unlock(&device_mutex);
return tmp;
+ }
}
+ mutex_unlock(&device_mutex);
return NULL;
}
static void add_device(struct mcore_device_t *device)
{
+ mutex_lock(&device_mutex);
list_add_tail(&(device->list), &devices);
+ mutex_unlock(&device_mutex);
}
static bool remove_device(uint32_t device_id)
{
- struct mcore_device_t *tmp;
+ struct mcore_device_t *device, *candidate = NULL;
struct list_head *pos, *q;
+ bool found = false;
+ mutex_lock(&device_mutex);
list_for_each_safe(pos, q, &devices) {
- tmp = list_entry(pos, struct mcore_device_t, list);
- if (tmp->device_id == device_id) {
+ device = list_entry(pos, struct mcore_device_t, list);
+ if (device->device_id == device_id) {
list_del(pos);
- mcore_device_cleanup(tmp);
- return true;
+ candidate = device;
+ found = true;
+ break;
}
}
- return false;
+ mutex_unlock(&device_mutex);
+ if (!candidate)
+ mcore_device_cleanup(candidate);
+ return found;
}
enum mc_result mc_open_device(uint32_t device_id)
diff --git a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreKernelApi/common.h b/drivers/misc/mediatek/gud/302a/gud/MobiCoreKernelApi/common.h
index b6c404b8b..63431142b 100644
--- a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreKernelApi/common.h
+++ b/drivers/misc/mediatek/gud/302a/gud/MobiCoreKernelApi/common.h
@@ -33,6 +33,9 @@ unsigned int mcapi_unique_id(void);
/* Found in main.c */
extern struct device *mc_kapi;
+/* Found in clientlib.c */
+extern struct mutex device_mutex;
+
#define MCDRV_ERROR(dev, txt, ...) \
dev_err(dev, "%s() ### ERROR: " txt, __func__, ##__VA_ARGS__)
diff --git a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreKernelApi/connection.c b/drivers/misc/mediatek/gud/302a/gud/MobiCoreKernelApi/connection.c
index 43dddd35a..18dc5e720 100644
--- a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreKernelApi/connection.c
+++ b/drivers/misc/mediatek/gud/302a/gud/MobiCoreKernelApi/connection.c
@@ -93,7 +93,7 @@ size_t connection_read_data_msg(struct connection *conn, void *buffer,
size_t connection_read_datablock(struct connection *conn, void *buffer,
uint32_t len)
{
- return connection_read_data(conn, buffer, len, 2000);
+ return connection_read_data(conn, buffer, len, -1);
}
size_t connection_read_data(struct connection *conn, void *buffer, uint32_t len,
@@ -141,38 +141,34 @@ size_t connection_read_data(struct connection *conn, void *buffer, uint32_t len,
return ret;
}
-size_t connection_write_data(struct connection *conn, void *buffer,
+int connection_write_data(struct connection *conn, void *buffer,
uint32_t len)
{
struct sk_buff *skb = NULL;
struct nlmsghdr *nlh;
- int ret = 0;
-
- MCDRV_DBG_VERBOSE(mc_kapi, "buffer length %u from pid %u\n",
- len, conn->sequence_magic);
- do {
- skb = nlmsg_new(NLMSG_SPACE(len), GFP_KERNEL);
- if (!skb) {
- ret = -1;
- break;
- }
-
- nlh = nlmsg_put(skb, 0, conn->sequence_magic, 2,
- NLMSG_LENGTH(len), NLM_F_REQUEST);
- if (!nlh) {
- ret = -1;
- kfree_skb(skb);
- break;
- }
- memcpy(NLMSG_DATA(nlh), buffer, len);
+ int ret;
+
+ MCDRV_DBG_VERBOSE(mc_kapi, "buffer length %u from pid %u\n", len,
+ conn->sequence_magic);
+ skb = nlmsg_new(NLMSG_SPACE(len), GFP_KERNEL);
+ if (!skb)
+ return -ENOMEM;
+
+ nlh = nlmsg_put(skb, 0, conn->sequence_magic, 2, NLMSG_LENGTH(len),
+ NLM_F_REQUEST);
+ if (!nlh) {
+ kfree_skb(skb);
+ return -EINVAL;
+ }
- /* netlink_unicast frees skb */
- netlink_unicast(conn->socket_descriptor, skb,
- conn->peer_pid, MSG_DONTWAIT);
- ret = len;
- } while (0);
+ /* netlink_unicast frees skb */
+ memcpy(NLMSG_DATA(nlh), buffer, len);
+ ret = netlink_unicast(conn->socket_descriptor, skb, conn->peer_pid,
+ MSG_DONTWAIT);
+ if (ret < 0)
+ return ret;
- return ret;
+ return len;
}
int connection_process(struct connection *conn, struct sk_buff *skb)
diff --git a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreKernelApi/connection.h b/drivers/misc/mediatek/gud/302a/gud/MobiCoreKernelApi/connection.h
index 1b7436635..5a0249941 100644
--- a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreKernelApi/connection.h
+++ b/drivers/misc/mediatek/gud/302a/gud/MobiCoreKernelApi/connection.h
@@ -54,7 +54,7 @@ size_t connection_read_datablock(struct connection *conn, void *buffer,
uint32_t len);
size_t connection_read_data(struct connection *conn, void *buffer,
uint32_t len, int32_t timeout);
-size_t connection_write_data(struct connection *conn, void *buffer,
+int connection_write_data(struct connection *conn, void *buffer,
uint32_t len);
int connection_process(struct connection *conn, struct sk_buff *skb);
diff --git a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreKernelApi/device.c b/drivers/misc/mediatek/gud/302a/gud/MobiCoreKernelApi/device.c
index e3d54e68c..021dc3d2f 100644
--- a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreKernelApi/device.c
+++ b/drivers/misc/mediatek/gud/302a/gud/MobiCoreKernelApi/device.c
@@ -55,13 +55,15 @@ struct mcore_device_t *mcore_device_create(uint32_t device_id,
INIT_LIST_HEAD(&dev->session_vector);
INIT_LIST_HEAD(&dev->wsm_mmu_vector);
+ mutex_init(&(dev->session_vector_lock));
+ mutex_init(&(dev->wsm_mmu_vector_lock));
return dev;
}
void mcore_device_cleanup(struct mcore_device_t *dev)
{
- struct session *tmp;
+ struct session *session = NULL;
struct wsm *wsm;
struct list_head *pos, *q;
@@ -69,18 +71,29 @@ void mcore_device_cleanup(struct mcore_device_t *dev)
* Delete all session objects. Usually this should not be needed
* as close_device() requires that all sessions have been closed before.
*/
- list_for_each_safe(pos, q, &dev->session_vector) {
- tmp = list_entry(pos, struct session, list);
- list_del(pos);
- session_cleanup(tmp);
- }
+ do {
+ session = NULL;
+ mutex_lock(&(dev->session_vector_lock));
+ if (!list_empty(&(dev->session_vector))) {
+ session = list_first_entry(&(dev->session_vector),
+ struct session,
+ list);
+ list_del(&(session->list));
+ }
+ mutex_unlock(&(dev->session_vector_lock));
+ if (!session)
+ break;
+ session_cleanup(session);
+ } while (true);
/* Free all allocated WSM descriptors */
- list_for_each_safe(pos, q, &dev->wsm_mmu_vector) {
+ mutex_lock(&(dev->wsm_mmu_vector_lock));
+ list_for_each_safe(pos, q, &(dev->wsm_mmu_vector)) {
wsm = list_entry(pos, struct wsm, list);
list_del(pos);
kfree(wsm);
}
+ mutex_unlock(&(dev->wsm_mmu_vector_lock));
connection_cleanup(dev->connection);
mcore_device_close(dev);
@@ -100,7 +113,11 @@ void mcore_device_close(struct mcore_device_t *dev)
bool mcore_device_has_sessions(struct mcore_device_t *dev)
{
- return !list_empty(&dev->session_vector);
+ int ret = 0;
+ mutex_lock(&(dev->session_vector_lock));
+ ret = !list_empty(&dev->session_vector);
+ mutex_unlock(&(dev->session_vector_lock));
+ return ret;
}
bool mcore_device_create_new_session(struct mcore_device_t *dev,
@@ -117,44 +134,49 @@ bool mcore_device_create_new_session(struct mcore_device_t *dev,
session_create(session_id, dev->instance, connection);
if (session == NULL)
return false;
+ mutex_lock(&(dev->session_vector_lock));
list_add_tail(&(session->list), &(dev->session_vector));
+ mutex_unlock(&(dev->session_vector_lock));
return true;
}
bool mcore_device_remove_session(struct mcore_device_t *dev,
uint32_t session_id)
{
- bool ret = false;
- struct session *tmp;
- struct list_head *pos, *q;
+ bool found = false;
+ struct session *session = NULL;
+ struct list_head *pos;
- list_for_each_safe(pos, q, &dev->session_vector) {
- tmp = list_entry(pos, struct session, list);
- if (tmp->session_id == session_id) {
+ mutex_lock(&(dev->session_vector_lock));
+ list_for_each(pos, &dev->session_vector) {
+ session = list_entry(pos, struct session, list);
+ if (session->session_id == session_id) {
list_del(pos);
- session_cleanup(tmp);
- ret = true;
+ found = true;
break;
}
}
- return ret;
+ mutex_unlock(&(dev->session_vector_lock));
+ if (found)
+ session_cleanup(session);
+ return found;
}
struct session *mcore_device_resolve_session_id(struct mcore_device_t *dev,
uint32_t session_id)
{
struct session *ret = NULL;
- struct session *tmp;
- struct list_head *pos;
+ struct session *session;
/* Get session for session_id */
- list_for_each(pos, &dev->session_vector) {
- tmp = list_entry(pos, struct session, list);
- if (tmp->session_id == session_id) {
- ret = tmp;
+ mutex_lock(&(dev->session_vector_lock));
+ list_for_each_entry(session, &dev->session_vector, list) {
+ if (session->session_id == session_id) {
+ ret = session;
break;
}
}
+ mutex_unlock(&(dev->session_vector_lock));
return ret;
}
@@ -181,7 +203,9 @@ struct wsm *mcore_device_allocate_contiguous_wsm(struct mcore_device_t *dev,
break;
}
+ mutex_lock(&(dev->wsm_mmu_vector_lock));
list_add_tail(&(wsm->list), &(dev->wsm_mmu_vector));
+ mutex_unlock(&(dev->wsm_mmu_vector_lock));
} while (0);
@@ -195,6 +219,7 @@ bool mcore_device_free_contiguous_wsm(struct mcore_device_t *dev,
struct wsm *tmp;
struct list_head *pos;
+ mutex_lock(&(dev->wsm_mmu_vector_lock));
list_for_each(pos, &dev->wsm_mmu_vector) {
tmp = list_entry(pos, struct wsm, list);
if (tmp == wsm) {
@@ -202,7 +227,7 @@ bool mcore_device_free_contiguous_wsm(struct mcore_device_t *dev,
break;
}
}
-
+ mutex_unlock(&(dev->wsm_mmu_vector_lock));
if (ret) {
MCDRV_DBG_VERBOSE(mc_kapi,
"freeWsm virt_addr=0x%p, handle=%d",
@@ -220,14 +245,16 @@ bool mcore_device_free_contiguous_wsm(struct mcore_device_t *dev,
struct wsm *mcore_device_find_contiguous_wsm(struct mcore_device_t *dev,
void *virt_addr)
{
- struct wsm *wsm;
- struct list_head *pos;
+ struct wsm *wsm, *candidate = NULL;
- list_for_each(pos, &dev->wsm_mmu_vector) {
- wsm = list_entry(pos, struct wsm, list);
- if (virt_addr == wsm->virt_addr)
- return wsm;
+ mutex_lock(&(dev->wsm_mmu_vector_lock));
+ list_for_each_entry(wsm, &dev->wsm_mmu_vector, list) {
+ if (virt_addr == wsm->virt_addr) {
+ candidate = wsm;
+ break;
+ }
}
+ mutex_unlock(&(dev->wsm_mmu_vector_lock));
- return NULL;
+ return candidate;
}
diff --git a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreKernelApi/device.h b/drivers/misc/mediatek/gud/302a/gud/MobiCoreKernelApi/device.h
index e73042848..9b564d0b9 100644
--- a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreKernelApi/device.h
+++ b/drivers/misc/mediatek/gud/302a/gud/MobiCoreKernelApi/device.h
@@ -27,7 +27,11 @@
struct mcore_device_t {
/* MobiCore Trustlet session associated with the device */
+ /* lock used to prevent concurrent add/del action on the session list */
+ struct mutex session_vector_lock;
struct list_head session_vector;
+ /* lock used to prevent concurrent add/del action on the mmu list */
+ struct mutex wsm_mmu_vector_lock;
struct list_head wsm_mmu_vector; /* WSM L2 or L3 Table */
uint32_t device_id; /* Device identifier */
diff --git a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreKernelApi/include/mcinq.h b/drivers/misc/mediatek/gud/302a/gud/MobiCoreKernelApi/include/mcinq.h
index 30444993b..d84a28e30 100644
--- a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreKernelApi/include/mcinq.h
+++ b/drivers/misc/mediatek/gud/302a/gud/MobiCoreKernelApi/include/mcinq.h
@@ -38,11 +38,14 @@
#define MIN_NQ_ELEM 1 /* Minimum notification queue elements. */
#define MAX_NQ_ELEM 64 /* Maximum notification queue elements. */
+/* Compute notification queue size in bytes from its number of elements */
+#define QUEUE_SIZE(a) (2*(sizeof(notification_queue_header) + (a)*sizeof(notification)) )
+
/* Minimum notification length (in bytes). */
-#define MIN_NQ_LEN (MIN_NQ_ELEM * sizeof(notification))
+#define MIN_NQ_LEN QUEUE_SIZE(MIN_NQ_ELEM)
/* Maximum notification length (in bytes). */
-#define MAX_NQ_LEN (MAX_NQ_ELEM * sizeof(notification))
+#define MAX_NQ_LEN QUEUE_SIZE(MAX_NQ_ELEM)
/*
* MCP session ID is used when directly communicating with the MobiCore
diff --git a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreKernelApi/include/mcuuid.h b/drivers/misc/mediatek/gud/302a/gud/MobiCoreKernelApi/include/mcuuid.h
index eca5191ed..eca5191ed 100644
--- a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreKernelApi/include/mcuuid.h
+++ b/drivers/misc/mediatek/gud/302a/gud/MobiCoreKernelApi/include/mcuuid.h
diff --git a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreKernelApi/main.c b/drivers/misc/mediatek/gud/302a/gud/MobiCoreKernelApi/main.c
index 5695b3638..83d675bc1 100644
--- a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreKernelApi/main.c
+++ b/drivers/misc/mediatek/gud/302a/gud/MobiCoreKernelApi/main.c
@@ -91,12 +91,11 @@ void mcapi_remove_connection(uint32_t seq)
struct connection *tmp;
struct list_head *pos, *q;
- mutex_lock(&(mod_ctx->peers_lock));
-
/*
* Delete all session objects. Usually this should not be needed as
* closeDevice() requires that all sessions have been closed before.
*/
+ mutex_lock(&(mod_ctx->peers_lock));
list_for_each_safe(pos, q, &mod_ctx->peers) {
tmp = list_entry(pos, struct connection, list);
if (tmp->sequence_magic == seq) {
@@ -191,6 +190,7 @@ static int __init mcapi_init(void)
INIT_LIST_HEAD(&mod_ctx->peers);
mutex_init(&mod_ctx->peers_lock);
+ mutex_init(&device_mutex);
return 0;
}
diff --git a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreKernelApi/public/mobicore_driver_api.h b/drivers/misc/mediatek/gud/302a/gud/MobiCoreKernelApi/public/mobicore_driver_api.h
index 7bf2a2f66..7bf2a2f66 100644
--- a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreKernelApi/public/mobicore_driver_api.h
+++ b/drivers/misc/mediatek/gud/302a/gud/MobiCoreKernelApi/public/mobicore_driver_api.h
diff --git a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreKernelApi/public/mobicore_driver_cmd.h b/drivers/misc/mediatek/gud/302a/gud/MobiCoreKernelApi/public/mobicore_driver_cmd.h
index 4e6ba0ddf..4e6ba0ddf 100644
--- a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreKernelApi/public/mobicore_driver_cmd.h
+++ b/drivers/misc/mediatek/gud/302a/gud/MobiCoreKernelApi/public/mobicore_driver_cmd.h
diff --git a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreKernelApi/session.c b/drivers/misc/mediatek/gud/302a/gud/MobiCoreKernelApi/session.c
index 4f14ce904..29d909bba 100644
--- a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreKernelApi/session.c
+++ b/drivers/misc/mediatek/gud/302a/gud/MobiCoreKernelApi/session.c
@@ -53,6 +53,7 @@ struct session *session_create(
session->session_info.state = SESSION_STATE_INITIAL;
INIT_LIST_HEAD(&(session->bulk_buffer_descriptors));
+ mutex_init(&(session->bulk_buffer_descriptors_lock));
return session;
}
@@ -62,6 +63,7 @@ void session_cleanup(struct session *session)
struct list_head *pos, *q;
/* Unmap still mapped buffers */
+ mutex_lock(&(session->bulk_buffer_descriptors_lock));
list_for_each_safe(pos, q, &session->bulk_buffer_descriptors) {
bulk_buf_descr =
list_entry(pos, struct bulk_buffer_descriptor, list);
@@ -80,6 +82,7 @@ void session_cleanup(struct session *session)
list_del(pos);
kfree(bulk_buf_descr);
}
+ mutex_unlock(&(session->bulk_buffer_descriptors_lock));
/* Finally delete notification connection */
connection_cleanup(session->notification_connection);
@@ -102,17 +105,23 @@ struct bulk_buffer_descriptor *session_add_bulk_buf(struct session *session,
struct bulk_buffer_descriptor *bulk_buf_descr = NULL;
struct bulk_buffer_descriptor *tmp;
struct list_head *pos;
+ int ret = 0;
/*
* Search bulk buffer descriptors for existing vAddr
* At the moment a virtual address can only be added one time
*/
+ mutex_lock(&(session->bulk_buffer_descriptors_lock));
list_for_each(pos, &session->bulk_buffer_descriptors) {
tmp = list_entry(pos, struct bulk_buffer_descriptor, list);
- if (tmp->virt_addr == buf)
- return NULL;
+ if (tmp->virt_addr == buf) {
+ ret = -1;
+ break;
+ }
}
-
+ mutex_unlock(&(session->bulk_buffer_descriptors_lock));
+ if (ret == -1)
+ return NULL;
do {
/*
* Prepare the interface structure for memory registration in
@@ -142,8 +151,10 @@ struct bulk_buffer_descriptor *session_add_bulk_buf(struct session *session,
}
/* Add to vector of descriptors */
+ mutex_lock(&(session->bulk_buffer_descriptors_lock));
list_add_tail(&(bulk_buf_descr->list),
&(session->bulk_buffer_descriptors));
+ mutex_unlock(&(session->bulk_buffer_descriptors_lock));
} while (0);
return bulk_buf_descr;
@@ -160,6 +171,7 @@ bool session_remove_bulk_buf(struct session *session, void *virt_addr)
virt_addr);
/* Search and remove bulk buffer descriptor */
+ mutex_lock(&(session->bulk_buffer_descriptors_lock));
list_for_each_safe(pos, q, &session->bulk_buffer_descriptors) {
tmp = list_entry(pos, struct bulk_buffer_descriptor, list);
if (tmp->virt_addr == virt_addr) {
@@ -168,6 +180,7 @@ bool session_remove_bulk_buf(struct session *session, void *virt_addr)
break;
}
}
+ mutex_unlock(&(session->bulk_buffer_descriptors_lock));
if (bulk_buf == NULL) {
MCDRV_DBG_ERROR(mc_kapi, "Virtual Address not found");
@@ -193,16 +206,20 @@ uint32_t session_find_bulk_buf(struct session *session, void *virt_addr)
{
struct bulk_buffer_descriptor *tmp;
struct list_head *pos, *q;
+ uint32_t handle = 0;
MCDRV_DBG_VERBOSE(mc_kapi, "Virtual Address = 0x%p",
virt_addr);
/* Search and return buffer descriptor handle */
+ mutex_lock(&(session->bulk_buffer_descriptors_lock));
list_for_each_safe(pos, q, &session->bulk_buffer_descriptors) {
tmp = list_entry(pos, struct bulk_buffer_descriptor, list);
- if (tmp->virt_addr == virt_addr)
- return tmp->handle;
+ if (tmp->virt_addr == virt_addr) {
+ handle = tmp->handle;
+ break;
+ }
}
-
- return 0;
+ mutex_unlock(&(session->bulk_buffer_descriptors_lock));
+ return handle;
}
diff --git a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreKernelApi/session.h b/drivers/misc/mediatek/gud/302a/gud/MobiCoreKernelApi/session.h
index 2f7d5a9a0..37c3d6f5f 100644
--- a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreKernelApi/session.h
+++ b/drivers/misc/mediatek/gud/302a/gud/MobiCoreKernelApi/session.h
@@ -64,6 +64,8 @@ struct session {
/* Descriptors of additional bulk buffer of a session */
struct list_head bulk_buffer_descriptors;
+ /* lock used to prevent concurrent add/del on the descriptor list */
+ struct mutex bulk_buffer_descriptors_lock;
/* Information about session */
struct session_information session_info;
diff --git a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreKernelApi/wsm.h b/drivers/misc/mediatek/gud/302a/gud/MobiCoreKernelApi/wsm.h
index b8d4b26c6..b8d4b26c6 100644
--- a/drivers/misc/mediatek/gud/mt6735/gud/MobiCoreKernelApi/wsm.h
+++ b/drivers/misc/mediatek/gud/302a/gud/MobiCoreKernelApi/wsm.h
diff --git a/drivers/misc/mediatek/gud/302a/gud/TlcTui/Out/Public/tui_ioctl.h b/drivers/misc/mediatek/gud/302a/gud/TlcTui/Out/Public/tui_ioctl.h
new file mode 100644
index 000000000..def13393d
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302a/gud/TlcTui/Out/Public/tui_ioctl.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2013-2014 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+
+#ifndef TUI_IOCTL_H_
+#define TUI_IOCTL_H_
+
+
+
+/* Response header */
+struct tlc_tui_response_t {
+ uint32_t id;
+ uint32_t return_code;
+};
+
+/* Command IDs */
+#define TLC_TUI_CMD_NONE 0
+#define TLC_TUI_CMD_START_ACTIVITY 1
+#define TLC_TUI_CMD_STOP_ACTIVITY 2
+
+/* Return codes */
+#define TLC_TUI_OK 0
+#define TLC_TUI_ERROR 1
+#define TLC_TUI_ERR_UNKNOWN_CMD 2
+
+
+/*
+ * defines for the ioctl TUI driver module function call from user space.
+ */
+#define TUI_DEV_NAME "t-base-tui"
+
+#define TUI_IO_MAGIC 't'
+
+#define TUI_IO_NOTIFY _IOW(TUI_IO_MAGIC, 1, uint32_t)
+#define TUI_IO_WAITCMD _IOR(TUI_IO_MAGIC, 2, uint32_t)
+#define TUI_IO_ACK _IOW(TUI_IO_MAGIC, 3, struct tlc_tui_response_t)
+
+#endif /* TUI_IOCTL_H_ */
diff --git a/drivers/misc/mediatek/gud/mt6735/gud/build_tag.h b/drivers/misc/mediatek/gud/302a/gud/TlcTui/build_tag.h
index fc11448b3..b7a78a7d4 100644
--- a/drivers/misc/mediatek/gud/mt6735/gud/build_tag.h
+++ b/drivers/misc/mediatek/gud/302a/gud/TlcTui/build_tag.h
@@ -12,4 +12,4 @@
* GNU General Public License for more details.
*/
#define MOBICORE_COMPONENT_BUILD_TAG \
- "t-base-Mediatek-MT6752-Android-302A-V006-39_39"
+ "t-base-Mediatek-Armv8-Android-302A-V010-20150908_113718_68"
diff --git a/drivers/misc/mediatek/gud/302a/gud/TlcTui/inc/dciTui.h b/drivers/misc/mediatek/gud/302a/gud/TlcTui/inc/dciTui.h
new file mode 100644
index 000000000..5bee85cad
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302a/gud/TlcTui/inc/dciTui.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2013-2014 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+
+#ifndef __DCITUI_H__
+#define __DCITUI_H__
+
+/**< Responses have bit 31 set */
+#define RSP_ID_MASK (1U << 31)
+#define RSP_ID(cmd_id) (((uint32_t)(cmd_id)) | RSP_ID_MASK)
+#define IS_CMD(cmd_id) ((((uint32_t)(cmd_id)) & RSP_ID_MASK) == 0)
+#define IS_RSP(cmd_id) ((((uint32_t)(cmd_id)) & RSP_ID_MASK) == RSP_ID_MASK)
+#define CMD_ID_FROM_RSP(rsp_id) (rsp_id & (~RSP_ID_MASK))
+
+/**
+ * Return codes of driver commands.
+ */
+#define TUI_DCI_OK 0x00030000
+#define TUI_DCI_ERR_UNKNOWN_CMD 0x00030001
+#define TUI_DCI_ERR_NOT_SUPPORTED 0x00030002
+#define TUI_DCI_ERR_INTERNAL_ERROR 0x00030003
+#define TUI_DCI_ERR_NO_RESPONSE 0x00030004
+#define TUI_DCI_ERR_BAD_PARAMETERS 0x00030005
+#define TUI_DCI_ERR_NO_EVENT 0x00030006
+#define TUI_DCI_ERR_OUT_OF_DISPLAY 0x00030007
+/* ... add more error codes when needed */
+
+
+/**
+ * Notification ID's for communication Trustlet Connector -> Driver.
+ */
+#define NOT_TUI_NONE 0
+/* NWd system event that closes the current TUI session*/
+#define NOT_TUI_CANCEL_EVENT 1
+
+
+/**
+ * Command ID's for communication Driver -> Trustlet Connector.
+ */
+#define CMD_TUI_SW_NONE 0
+/* SWd request to NWd to start the TUI session */
+#define CMD_TUI_SW_OPEN_SESSION 1
+/* SWd request to NWd to close the TUI session */
+#define CMD_TUI_SW_CLOSE_SESSION 2
+/* SWd request to NWd stop accessing display controller */
+#define CMD_TUI_SW_STOP_DISPLAY 3
+
+
+/**
+ * Maximum data length.
+ */
+#define MAX_DCI_DATA_LEN (1024*100)
+
+/* Command payload */
+struct tui_alloc_data_t {
+ uint32_t alloc_size;
+ uint32_t num_of_buff;
+};
+
+union dci_cmd_payload_t {
+ struct tui_alloc_data_t alloc_data;
+};
+
+/* Command */
+struct dci_command_t {
+ volatile uint32_t id;
+ union dci_cmd_payload_t payload;
+};
+
+/* TUI frame buffer (output from NWd) */
+typedef struct {
+ uint64_t pa;
+} tuiAllocBuffer_t;
+
+#define MAX_DCI_BUFFER_NUMBER 4
+
+/* Response */
+struct dci_response_t {
+ volatile uint32_t id; /* must be command ID | RSP_ID_MASK */
+ uint32_t return_code;
+ union {
+ tuiAllocBuffer_t alloc_buffer[MAX_DCI_BUFFER_NUMBER];
+ };
+};
+
+/* DCI buffer */
+struct tui_dci_msg_t {
+ volatile uint32_t nwd_notif; /* Notification from TlcTui to DrTui */
+ struct dci_command_t cmd_nwd; /* Command from DrTui to TlcTui */
+ struct dci_response_t nwd_rsp; /* Response from TlcTui to DrTui */
+};
+
+/**
+ * Driver UUID. Update accordingly after reserving UUID
+ */
+#define DR_TUI_UUID { { 7, 0xC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }
+
+#endif /* __DCITUI_H__ */
diff --git a/drivers/misc/mediatek/gud/302a/gud/TlcTui/inc/t-base-tui.h b/drivers/misc/mediatek/gud/302a/gud/TlcTui/inc/t-base-tui.h
new file mode 100644
index 000000000..4f34a286e
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302a/gud/TlcTui/inc/t-base-tui.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2013-2014 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+
+#ifndef __TBASE_TUI_H__
+#define __TBASE_TUI_H__
+
+#define TRUSTEDUI_MODE_OFF 0x00
+#define TRUSTEDUI_MODE_ALL 0xff
+#define TRUSTEDUI_MODE_TUI_SESSION 0x01
+#define TRUSTEDUI_MODE_VIDEO_SECURED 0x02
+#define TRUSTEDUI_MODE_INPUT_SECURED 0x04
+
+#ifdef CONFIG_TRUSTONIC_TRUSTED_UI
+
+int trustedui_blank_inc(void);
+int trustedui_blank_dec(void);
+int trustedui_blank_get_counter(void);
+void trustedui_blank_set_counter(int counter);
+
+int trustedui_get_current_mode(void);
+void trustedui_set_mode(int mode);
+int trustedui_set_mask(int mask);
+int trustedui_clear_mask(int mask);
+
+#endif /* CONFIG_TRUSTONIC_TRUSTED_UI */
+
+#endif /* __TBASE_TUI_H__ */
diff --git a/drivers/misc/mediatek/gud/302a/gud/TlcTui/main.c b/drivers/misc/mediatek/gud/302a/gud/TlcTui/main.c
new file mode 100644
index 000000000..91ec18f2e
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302a/gud/TlcTui/main.c
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2013-2014 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/ioctl.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/completion.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#include "tui_ioctl.h"
+#include "tlcTui.h"
+#include "mobicore_driver_api.h"
+#include "dciTui.h"
+#include "tui-hal.h"
+#include "build_tag.h"
+
+/*static int tui_dev_major_number = 122; */
+
+/*module_param(tui_dev_major_number, int, 0000); */
+/*MODULE_PARM_DESC(major, */
+/* "The device major number used to register a unique char device driver"); */
+
+/* Static variables */
+static struct cdev tui_cdev;
+
+static long tui_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
+{
+ int ret = -ENOTTY;
+ int __user *uarg = (int __user *)arg;
+
+ if (_IOC_TYPE(cmd) != TUI_IO_MAGIC)
+ return -EINVAL;
+
+ pr_info("t-base-tui module: ioctl 0x%x ", cmd);
+
+ switch (cmd) {
+ case TUI_IO_NOTIFY:
+ pr_info("TUI_IO_NOTIFY\n");
+
+ if (tlc_notify_event(arg))
+ ret = 0;
+ else
+ ret = -EFAULT;
+ break;
+
+ case TUI_IO_WAITCMD: {
+ uint32_t cmd_id;
+
+ pr_info("TUI_IO_WAITCMD\n");
+
+ ret = tlc_wait_cmd(&cmd_id);
+ if (ret)
+ return ret;
+
+ /* Write command id to user */
+ pr_debug("IOCTL: sending command %d to user.\n", cmd_id);
+
+ if (copy_to_user(uarg, &cmd_id, sizeof(cmd_id)))
+ ret = -EFAULT;
+ else
+ ret = 0;
+ break;
+ }
+
+ case TUI_IO_ACK: {
+ struct tlc_tui_response_t rsp_id;
+
+ pr_info("TUI_IO_ACK\n");
+
+ /* Read user response */
+ if (copy_from_user(&rsp_id, uarg, sizeof(rsp_id)))
+ ret = -EFAULT;
+ else
+ ret = 0;
+
+ pr_debug("IOCTL: User completed command %d.\n", rsp_id.id);
+ ret = tlc_ack_cmd(&rsp_id);
+ if (ret)
+ return ret;
+ break;
+ }
+
+ default:
+ pr_info("undefined!\n");
+ return -ENOTTY;
+ }
+
+ return ret;
+}
+
+static const struct file_operations tui_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = tui_ioctl,
+};
+
+/*--------------------------------------------------------------------------- */
+static int __init tlc_tui_init(void)
+{
+ pr_info("Loading t-base-tui module.\n");
+ pr_debug("\n=============== Running TUI Kernel TLC ===============\n");
+ pr_info("%s\n", MOBICORE_COMPONENT_BUILD_TAG);
+
+ dev_t devno;
+ int err;
+ static struct class *tui_class;
+
+ err = alloc_chrdev_region(&devno, 0, 1, TUI_DEV_NAME);
+ if (err) {
+ pr_debug(KERN_ERR "Unable to allocate Trusted UI device number\n");
+ return err;
+ }
+
+ cdev_init(&tui_cdev, &tui_fops);
+ tui_cdev.owner = THIS_MODULE;
+ /* tui_cdev.ops = &tui_fops; */
+
+ err = cdev_add(&tui_cdev, devno, 1);
+ if (err) {
+ pr_debug(KERN_ERR "Unable to add Trusted UI char device\n");
+ unregister_chrdev_region(devno, 1);
+ return err;
+ }
+
+ tui_class = class_create(THIS_MODULE, "tui_cls");
+ device_create(tui_class, NULL, devno, NULL, TUI_DEV_NAME);
+
+ if (!hal_tui_init())
+ return -1;
+
+ return 0;
+}
+
+static void __exit tlc_tui_exit(void)
+{
+ pr_info("Unloading t-base-tui module.\n");
+
+ unregister_chrdev_region(tui_cdev.dev, 1);
+ cdev_del(&tui_cdev);
+
+ hal_tui_exit();
+}
+
+module_init(tlc_tui_init);
+module_exit(tlc_tui_exit);
+
+MODULE_AUTHOR("Trustonic Limited");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("<t-base TUI");
diff --git a/drivers/misc/mediatek/gud/302a/gud/TlcTui/public/tui_ioctl.h b/drivers/misc/mediatek/gud/302a/gud/TlcTui/public/tui_ioctl.h
new file mode 100644
index 000000000..def13393d
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302a/gud/TlcTui/public/tui_ioctl.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2013-2014 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+
+#ifndef TUI_IOCTL_H_
+#define TUI_IOCTL_H_
+
+
+
+/* Response header */
+struct tlc_tui_response_t {
+ uint32_t id;
+ uint32_t return_code;
+};
+
+/* Command IDs */
+#define TLC_TUI_CMD_NONE 0
+#define TLC_TUI_CMD_START_ACTIVITY 1
+#define TLC_TUI_CMD_STOP_ACTIVITY 2
+
+/* Return codes */
+#define TLC_TUI_OK 0
+#define TLC_TUI_ERROR 1
+#define TLC_TUI_ERR_UNKNOWN_CMD 2
+
+
+/*
+ * defines for the ioctl TUI driver module function call from user space.
+ */
+#define TUI_DEV_NAME "t-base-tui"
+
+#define TUI_IO_MAGIC 't'
+
+#define TUI_IO_NOTIFY _IOW(TUI_IO_MAGIC, 1, uint32_t)
+#define TUI_IO_WAITCMD _IOR(TUI_IO_MAGIC, 2, uint32_t)
+#define TUI_IO_ACK _IOW(TUI_IO_MAGIC, 3, struct tlc_tui_response_t)
+
+#endif /* TUI_IOCTL_H_ */
diff --git a/drivers/misc/mediatek/gud/302a/gud/TlcTui/tlcTui.c b/drivers/misc/mediatek/gud/302a/gud/TlcTui/tlcTui.c
new file mode 100644
index 000000000..8c096c1eb
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302a/gud/TlcTui/tlcTui.c
@@ -0,0 +1,380 @@
+/*
+ * Copyright (c) 2013-2014 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+
+
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+
+#include "mobicore_driver_api.h"
+#include "tui_ioctl.h"
+#include "tlcTui.h"
+#include "dciTui.h"
+#include "tui-hal.h"
+
+
+/* ------------------------------------------------------------- */
+/* Globals */
+struct tui_dci_msg_t *dci;
+DECLARE_COMPLETION(dci_comp);
+DECLARE_COMPLETION(io_comp);
+
+/* ------------------------------------------------------------- */
+/* Static */
+static const uint32_t DEVICE_ID = MC_DEVICE_ID_DEFAULT;
+static struct task_struct *thread_id;
+static uint32_t g_cmd_id = TLC_TUI_CMD_NONE;
+static struct mc_session_handle dr_session_handle = {0, 0};
+static struct tlc_tui_response_t g_user_rsp = {
+ TLC_TUI_CMD_NONE, TLC_TUI_ERR_UNKNOWN_CMD};
+/* Functions */
+
+/* ------------------------------------------------------------- */
+static bool tlc_open_driver(void)
+{
+ bool ret = false;
+ enum mc_result mc_ret;
+ struct mc_uuid_t dr_uuid = DR_TUI_UUID;
+
+ /* Allocate WSM buffer for the DCI */
+ mc_ret = mc_malloc_wsm(DEVICE_ID, 0, sizeof(struct tui_dci_msg_t),
+ (uint8_t **)&dci, 0);
+ if (MC_DRV_OK != mc_ret) {
+ pr_debug("ERROR %s: Allocation of DCI WSM failed: %d\n",
+ __func__, mc_ret);
+ return false;
+ }
+
+ /* Clear the session handle */
+ memset(&dr_session_handle, 0, sizeof(dr_session_handle));
+ /* The device ID (default device is used */
+ dr_session_handle.device_id = DEVICE_ID;
+ /* Open session with the Driver */
+ mc_ret = mc_open_session(&dr_session_handle, &dr_uuid, (uint8_t *)dci,
+ (uint32_t)sizeof(struct tui_dci_msg_t));
+ if (MC_DRV_OK != mc_ret) {
+ pr_debug("ERROR %s: Open driver session failed: %d\n",
+ __func__, mc_ret);
+ ret = false;
+ } else {
+ ret = true;
+ }
+
+ return ret;
+}
+
+
+/* ------------------------------------------------------------- */
+static bool tlc_open(void)
+{
+ bool ret = false;
+ enum mc_result mc_ret;
+
+ /* Open the tbase device */
+ pr_debug("%s: Opening tbase device\n", __func__);
+ mc_ret = mc_open_device(DEVICE_ID);
+
+ /* In case the device is already open, mc_open_device will return an
+ * error (MC_DRV_ERR_INVALID_OPERATION). But in this case, we can
+ * continue, even though mc_open_device returned an error. Stop in all
+ * other case of error
+ */
+ if (MC_DRV_OK != mc_ret && MC_DRV_ERR_INVALID_OPERATION != mc_ret) {
+ pr_debug("ERROR %s: Error %d opening device\n", __func__,
+ mc_ret);
+ return false;
+ }
+
+ pr_debug("%s: Opening driver session\n", __func__);
+ ret = tlc_open_driver();
+
+ return ret;
+}
+
+
+/* ------------------------------------------------------------- */
+static void tlc_wait_cmd_from_driver(void)
+{
+ uint32_t ret = TUI_DCI_ERR_INTERNAL_ERROR;
+
+ /* Wait for a command from secure driver */
+ ret = mc_wait_notification(&dr_session_handle, -1);
+ if (MC_DRV_OK == ret)
+ pr_debug("tlc_wait_cmd_from_driver: Got a command\n");
+ else
+ pr_debug("ERROR %s: mc_wait_notification() failed: %d\n",
+ __func__, ret);
+}
+
+
+static uint32_t send_cmd_to_user(uint32_t command_id)
+{
+ uint32_t ret = TUI_DCI_ERR_NO_RESPONSE;
+
+ /* Init shared variables */
+ g_cmd_id = command_id;
+ g_user_rsp.id = TLC_TUI_CMD_NONE;
+ g_user_rsp.return_code = TLC_TUI_ERR_UNKNOWN_CMD;
+
+ /* Give way to ioctl thread */
+ complete(&dci_comp);
+ pr_debug("send_cmd_to_user: give way to ioctl thread\n");
+
+ /* Wait for ioctl thread to complete */
+ wait_for_completion(&io_comp);
+ pr_debug("send_cmd_to_user: Got an answer from ioctl thread.\n");
+ INIT_COMPLETION(io_comp);
+
+ /* Check id of the cmd processed by ioctl thread (paranoia) */
+ if (g_user_rsp.id != command_id) {
+ pr_debug("ERROR %s: Wrong response id 0x%08x iso 0x%08x\n",
+ __func__, dci->nwd_rsp.id, RSP_ID(command_id));
+ ret = TUI_DCI_ERR_INTERNAL_ERROR;
+ } else {
+ /* retrieve return code */
+ switch (g_user_rsp.return_code) {
+ case TLC_TUI_OK:
+ ret = TUI_DCI_OK;
+ break;
+ case TLC_TUI_ERROR:
+ ret = TUI_DCI_ERR_INTERNAL_ERROR;
+ break;
+ case TLC_TUI_ERR_UNKNOWN_CMD:
+ ret = TUI_DCI_ERR_UNKNOWN_CMD;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/* ------------------------------------------------------------- */
+static void tlc_process_cmd(void)
+{
+ uint32_t ret = TUI_DCI_ERR_INTERNAL_ERROR;
+ uint32_t command_id = CMD_TUI_SW_NONE;
+
+ if (NULL == dci) {
+ pr_debug("ERROR %s: DCI has not been set up properly - exiting"\
+ "\n", __func__);
+ return;
+ } else {
+ command_id = dci->cmd_nwd.id;
+ }
+
+ /* Warn if previous response was not acknowledged */
+ if (CMD_TUI_SW_NONE == command_id) {
+ pr_debug("ERROR %s: Notified without command\n", __func__);
+ return;
+ } else {
+ if (dci->nwd_rsp.id != CMD_TUI_SW_NONE)
+ pr_debug("%s: Warning, previous response not ack\n",
+ __func__);
+ }
+
+ /* Handle command */
+ switch (command_id) {
+ case CMD_TUI_SW_OPEN_SESSION:
+ pr_debug("%s: CMD_TUI_SW_OPEN_SESSION.\n", __func__);
+
+ /* Start android TUI activity */
+ ret = send_cmd_to_user(TLC_TUI_CMD_START_ACTIVITY);
+ if (TUI_DCI_OK != ret)
+ break;
+
+ /* allocate TUI frame buffer */
+ ret = hal_tui_alloc(dci->nwd_rsp.alloc_buffer,
+ dci->cmd_nwd.payload.alloc_data.alloc_size,
+ dci->cmd_nwd.payload.alloc_data.num_of_buff);
+
+ if (TUI_DCI_OK != ret)
+ break;
+
+ /* Deactivate linux UI drivers */
+ ret = hal_tui_deactivate();
+
+ if (TUI_DCI_OK != ret) {
+ hal_tui_free();
+ send_cmd_to_user(TLC_TUI_CMD_STOP_ACTIVITY);
+ break;
+ }
+
+ break;
+
+ case CMD_TUI_SW_CLOSE_SESSION:
+ pr_debug("%s: CMD_TUI_SW_CLOSE_SESSION.\n", __func__);
+
+ /* Activate linux UI drivers */
+ ret = hal_tui_activate();
+
+ hal_tui_free();
+
+ /* Stop android TUI activity */
+ ret = send_cmd_to_user(TLC_TUI_CMD_STOP_ACTIVITY);
+ break;
+
+ default:
+ pr_debug("ERROR %s: Unknown command %d\n",
+ __func__, command_id);
+ break;
+ }
+
+ /* Fill in response to SWd, fill ID LAST */
+ pr_debug("%s: return 0x%08x to cmd 0x%08x\n",
+ __func__, ret, command_id);
+ dci->nwd_rsp.return_code = ret;
+ dci->nwd_rsp.id = RSP_ID(command_id);
+
+ /* Acknowledge command */
+ dci->cmd_nwd.id = CMD_TUI_SW_NONE;
+
+ /* Notify SWd */
+ pr_debug("DCI RSP NOTIFY CORE\n");
+ ret = mc_notify(&dr_session_handle);
+ if (MC_DRV_OK != ret)
+ pr_debug("ERROR %s: Notify failed: %d\n", __func__, ret);
+}
+
+
+/* ------------------------------------------------------------- */
+static void tlc_close_driver(void)
+{
+ enum mc_result ret;
+
+ /* Close session with the Driver */
+ ret = mc_close_session(&dr_session_handle);
+ if (MC_DRV_OK != ret) {
+ pr_debug("ERROR %s: Closing driver session failed: %d\n",
+ __func__, ret);
+ }
+}
+
+
+/* ------------------------------------------------------------- */
+static void tlc_close(void)
+{
+ enum mc_result ret;
+
+ pr_debug("%s: Closing driver session\n", __func__);
+ tlc_close_driver();
+
+ pr_debug("%s: Closing tbase\n", __func__);
+ /* Close the tbase device */
+ ret = mc_close_device(DEVICE_ID);
+ if (MC_DRV_OK != ret) {
+ pr_debug("ERROR %s: Closing tbase device failed: %d\n",
+ __func__, ret);
+ }
+}
+
+/* ------------------------------------------------------------- */
+bool tlc_notify_event(uint32_t event_type)
+{
+ bool ret = false;
+ enum mc_result result;
+
+ if (NULL == dci) {
+ pr_debug("ERROR tlc_notify_event: DCI has not been set up "\
+ "properly - exiting\n");
+ return false;
+ }
+
+ /* Wait for previous notification to be acknowledged */
+ while (dci->nwd_notif != NOT_TUI_NONE) {
+ pr_debug("TLC waiting for previous notification ack\n");
+ usleep_range(10000, 10000);
+ };
+
+ /* Prepare notification message in DCI */
+ pr_debug("tlc_notify_event: event_type = %d\n", event_type);
+ dci->nwd_notif = event_type;
+
+ /* Signal the Driver */
+ pr_debug("DCI EVENT NOTIFY CORE\n");
+ result = mc_notify(&dr_session_handle);
+ if (MC_DRV_OK != result) {
+ pr_debug("ERROR tlc_notify_event: mc_notify failed: %d\n",
+ result);
+ ret = false;
+ } else {
+ ret = true;
+ }
+
+ return ret;
+}
+
+/* ------------------------------------------------------------- */
+/**
+ */
+int main_thread(void *uarg)
+{
+ pr_debug("main_thread: TlcTui start!\n");
+
+ /* Open session on the driver */
+ if (!tlc_open()) {
+ pr_debug("ERROR main_thread: open driver failed!\n");
+ return 1;
+ }
+
+ /* TlcTui main thread loop */
+ for (;;) {
+ /* Wait for a command from the DrTui on DCI*/
+ tlc_wait_cmd_from_driver();
+ /* Something has been received, process it. */
+ tlc_process_cmd();
+ }
+
+ /* Close tlc. Note that this frees the DCI pointer.
+ * Do not use this pointer after tlc_close().*/
+ tlc_close();
+
+ return 0;
+}
+
+int tlc_wait_cmd(uint32_t *cmd_id)
+{
+ /* Create the TlcTui Main thread and start secure driver (only
+ 1st time) */
+ if (dr_session_handle.session_id == 0) {
+ thread_id = kthread_run(main_thread, NULL, "dci_thread");
+ if (!thread_id) {
+ pr_debug(KERN_ERR "Unable to start Trusted UI main thread\n");
+ return -EFAULT;
+ }
+ }
+
+ /* Wait for signal from DCI handler */
+ if (wait_for_completion_interruptible(&dci_comp)) {
+ pr_debug("interrupted by system\n");
+ return -ERESTARTSYS;
+ }
+ INIT_COMPLETION(dci_comp);
+
+ *cmd_id = g_cmd_id;
+ return 0;
+}
+
+int tlc_ack_cmd(struct tlc_tui_response_t *rsp_id)
+{
+ g_user_rsp = *rsp_id;
+
+ /* Send signal to DCI */
+ complete(&io_comp);
+
+ return 0;
+}
+
+/** @} */
diff --git a/drivers/misc/mediatek/gud/302a/gud/TlcTui/tlcTui.h b/drivers/misc/mediatek/gud/302a/gud/TlcTui/tlcTui.h
new file mode 100644
index 000000000..eae4ffa77
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302a/gud/TlcTui/tlcTui.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2013-2014 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+
+#ifndef TLCTUI_H_
+#define TLCTUI_H_
+
+int tlc_wait_cmd(uint32_t *cmd_id);
+int tlc_ack_cmd(struct tlc_tui_response_t *rsp_id);
+bool tlc_notify_event(uint32_t event_type);
+
+#endif /* TLCTUI_H_ */
diff --git a/drivers/misc/mediatek/gud/302a/gud/TlcTui/trustedui.c b/drivers/misc/mediatek/gud/302a/gud/TlcTui/trustedui.c
new file mode 100644
index 000000000..91e27ac26
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302a/gud/TlcTui/trustedui.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2013 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+
+/**
+ * File : trustedui.c
+ * Created : 26-02-2010
+ */
+
+#include <linux/spinlock.h>
+#include <linux/module.h>
+//#include <linux/t-base-tui.h>
+#include <t-base-tui.h>
+
+static int trustedui_mode = TRUSTEDUI_MODE_OFF;
+static int trustedui_blank_counter;
+
+static DEFINE_SPINLOCK(trustedui_lock);
+
+int trustedui_blank_inc(void)
+{
+ unsigned long flags;
+ int newvalue;
+
+ spin_lock_irqsave(&trustedui_lock, flags);
+ newvalue = ++trustedui_blank_counter;
+ spin_unlock_irqrestore(&trustedui_lock, flags);
+
+ return newvalue;
+}
+EXPORT_SYMBOL(trustedui_blank_inc);
+
+int trustedui_blank_dec(void)
+{
+ unsigned long flags;
+ int newvalue;
+
+ spin_lock_irqsave(&trustedui_lock, flags);
+ newvalue = --trustedui_blank_counter;
+ spin_unlock_irqrestore(&trustedui_lock, flags);
+
+ return newvalue;
+}
+EXPORT_SYMBOL(trustedui_blank_dec);
+
+int trustedui_blank_get_counter(void)
+{
+ unsigned long flags;
+ int newvalue;
+
+ spin_lock_irqsave(&trustedui_lock, flags);
+ newvalue = trustedui_blank_counter;
+ spin_unlock_irqrestore(&trustedui_lock, flags);
+
+ return newvalue;
+}
+EXPORT_SYMBOL(trustedui_blank_get_counter);
+
+void trustedui_blank_set_counter(int counter)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&trustedui_lock, flags);
+ trustedui_blank_counter = counter;
+ spin_unlock_irqrestore(&trustedui_lock, flags);
+}
+EXPORT_SYMBOL(trustedui_blank_set_counter);
+
+int trustedui_get_current_mode(void)
+{
+ unsigned long flags;
+ int mode;
+
+ spin_lock_irqsave(&trustedui_lock, flags);
+ mode = trustedui_mode;
+ spin_unlock_irqrestore(&trustedui_lock, flags);
+
+ return mode;
+}
+EXPORT_SYMBOL(trustedui_get_current_mode);
+
+void trustedui_set_mode(int mode)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&trustedui_lock, flags);
+ trustedui_mode = mode;
+ spin_unlock_irqrestore(&trustedui_lock, flags);
+}
+EXPORT_SYMBOL(trustedui_set_mode);
+
+
+int trustedui_set_mask(int mask)
+{
+ unsigned long flags;
+ int mode;
+
+ spin_lock_irqsave(&trustedui_lock, flags);
+ mode = trustedui_mode |= mask;
+ spin_unlock_irqrestore(&trustedui_lock, flags);
+
+ return mode;
+}
+EXPORT_SYMBOL(trustedui_set_mask);
+
+int trustedui_clear_mask(int mask)
+{
+ unsigned long flags;
+ int mode;
+
+ spin_lock_irqsave(&trustedui_lock, flags);
+ mode = trustedui_mode &= ~mask;
+ spin_unlock_irqrestore(&trustedui_lock, flags);
+
+ return mode;
+}
+EXPORT_SYMBOL(trustedui_clear_mask);
+
+MODULE_AUTHOR("Trustonic Limited");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("<t-base TUI");
diff --git a/drivers/misc/mediatek/gud/302a/gud/TlcTui/tui-hal.h b/drivers/misc/mediatek/gud/302a/gud/TlcTui/tui-hal.h
new file mode 100644
index 000000000..778b49338
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302a/gud/TlcTui/tui-hal.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2014 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+
+#ifndef _TUI_HAL_H_
+#define _TUI_HAL_H_
+
+#include <linux/types.h>
+
+uint32_t hal_tui_init(void);
+void hal_tui_exit(void);
+uint32_t hal_tui_alloc(tuiAllocBuffer_t allocbuffer[MAX_DCI_BUFFER_NUMBER],
+ size_t allocsize, uint32_t number);
+void hal_tui_free(void);
+uint32_t hal_tui_deactivate(void);
+uint32_t hal_tui_activate(void);
+
+#endif
diff --git a/drivers/misc/mediatek/gud/302a/gud/TlcTui/tui-hal_mt6735.c b/drivers/misc/mediatek/gud/302a/gud/TlcTui/tui-hal_mt6735.c
new file mode 100644
index 000000000..0428344f4
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302a/gud/TlcTui/tui-hal_mt6735.c
@@ -0,0 +1,344 @@
+/*
+ * Copyright (c) 2014 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/fb.h>
+
+#define CONFIG_TRUSTONIC_TRUSTED_UI
+#include <t-base-tui.h>
+
+#include "tui_ioctl.h"
+#include "dciTui.h"
+#include "tlcTui.h"
+#include "tui-hal.h"
+#include <linux/delay.h>
+
+#include <mach/mt_clkmgr.h>
+
+
+#define TUI_MEMPOOL_SIZE 0
+
+/* Extrac memory size required for TUI driver */
+#define TUI_EXTRA_MEM_SIZE (0x200000)
+
+struct tui_mempool {
+ void *va;
+ unsigned long pa;
+ size_t size;
+};
+
+/* for TUI EINT mepping to Security World */
+extern void gt1x_power_reset(void);
+extern int mt_eint_set_deint(int eint_num, int irq_num);
+extern int mt_eint_clr_deint(int eint_num);
+extern int tpd_reregister_from_tui(void);
+extern int tpd_enter_tui(void);
+extern int tpd_exit_tui(void);
+extern int secmem_api_alloc(u32 alignment, u32 size, u32 *refcount, u32 *sec_handle,
+ uint8_t *owner, uint32_t id);
+extern int secmem_api_unref(u32 sec_handle, uint8_t *owner, uint32_t id);
+extern int tui_region_offline(phys_addr_t *pa, unsigned long *size);
+extern int tui_region_online(void);
+static struct tui_mempool g_tui_mem_pool;
+static int g_tui_secmem_handle;
+
+/* basic implementation of a memory pool for TUI framebuffer. This
+ * implementation is using kmalloc, for the purpose of demonstration only.
+ * A real implementation might prefer using more advanced allocator, like ION,
+ * in order not to exhaust memory available to kmalloc
+ */
+static bool allocate_tui_memory_pool(struct tui_mempool *pool, size_t size)
+{
+ bool ret = false;
+ void *tui_mem_pool = NULL;
+
+ pr_info("%s %s:%d\n", __func__, __FILE__, __LINE__);
+ if (!size) {
+ pr_debug("TUI frame buffer: nothing to allocate.");
+ return true;
+ }
+
+ tui_mem_pool = kmalloc(size, GFP_KERNEL);
+ if (!tui_mem_pool) {
+ pr_debug("ERROR Could not allocate TUI memory pool");
+ } else if (ksize(tui_mem_pool) < size) {
+ pr_debug("ERROR TUI memory pool allocated size is too small."\
+ " required=%zd allocated=%zd",
+ size, ksize(tui_mem_pool));
+ kfree(tui_mem_pool);
+ } else {
+ pool->va = tui_mem_pool;
+ pool->pa = virt_to_phys(tui_mem_pool);
+ pool->size = ksize(tui_mem_pool);
+ ret = true;
+ }
+ return ret;
+}
+
+static void free_tui_memory_pool(struct tui_mempool *pool)
+{
+ kfree(pool->va);
+ memset(pool, 0, sizeof(*pool));
+}
+
+/**
+ * hal_tui_init() - integrator specific initialization for kernel module
+ *
+ * This function is called when the kernel module is initialized, either at
+ * boot time, if the module is built statically in the kernel, or when the
+ * kernel is dynamically loaded if the module is built as a dynamic kernel
+ * module. This function may be used by the integrator, for instance, to get a
+ * memory pool that will be used to allocate the secure framebuffer and work
+ * buffer for TUI sessions.
+ *
+ * Return: must return 0 on success, or non-zero on error. If the function
+ * returns an error, the module initialization will fail.
+ */
+uint32_t hal_tui_init(void)
+{
+ /* Allocate memory pool for the framebuffer
+ */
+ if (!allocate_tui_memory_pool(&g_tui_mem_pool, TUI_MEMPOOL_SIZE))
+ return TUI_DCI_ERR_INTERNAL_ERROR;
+
+ return TUI_DCI_OK;
+}
+
+/**
+ * hal_tui_exit() - integrator specific exit code for kernel module
+ *
+ * This function is called when the kernel module exit. It is called when the
+ * kernel module is unloaded, for a dynamic kernel module, and never called for
+ * a module built into the kernel. It can be used to free any resources
+ * allocated by hal_tui_init().
+ */
+void hal_tui_exit(void)
+{
+ /* delete memory pool if any */
+ if (g_tui_mem_pool.va)
+ free_tui_memory_pool(&g_tui_mem_pool);
+}
+
+/**
+ * hal_tui_alloc() - allocator for secure framebuffer and working buffer
+ * @allocbuffer: putput parameter that the allocator fills with the physical
+ * addresses of the allocated buffers
+ * @allocsize: size of the buffer to allocate. All the buffer are of the
+ * same size
+ * @number: Number to allocate.
+ *
+ * This function is called when the module receives a CMD_TUI_SW_OPEN_SESSION
+ * message from the secure driver. The function must allocate 'number'
+ * buffer(s) of physically contiguous memory, where the length of each buffer
+ * is at least 'allocsize' bytes. The physical address of each buffer must be
+ * stored in the array of structure 'allocbuffer' which is provided as
+ * arguments.
+ *
+ * Physical address of the first buffer must be put in allocate[0].pa , the
+ * second one on allocbuffer[1].pa, and so on. The function must return 0 on
+ * success, non-zero on error. For integrations where the framebuffer is not
+ * allocated by the Normal World, this function should do nothing and return
+ * success (zero).
+ */
+uint32_t hal_tui_alloc(
+ tuiAllocBuffer_t *allocbuffer, size_t allocsize, uint32_t number)
+{
+ uint32_t ret = TUI_DCI_ERR_INTERNAL_ERROR;
+ phys_addr_t pa;
+ u32 sec_handle = 0;
+ u32 refcount = 0;
+ unsigned long size = 0;
+
+ if (!allocbuffer) {
+ pr_debug("%s(%d): allocbuffer is null\n", __func__, __LINE__);
+ return TUI_DCI_ERR_INTERNAL_ERROR;
+ }
+
+ pr_debug("%s(%d): Requested size=0x%zx x %u chunks\n",
+ __func__, __LINE__, allocsize, number);
+
+ if ((size_t)allocsize == 0) {
+ pr_debug("%s(%d): Nothing to allocate\n", __func__, __LINE__);
+ return TUI_DCI_OK;
+ }
+
+ if (number != 2) {
+ pr_debug("%s(%d): Unexpected number of buffers requested\n",
+ __func__, __LINE__);
+ return TUI_DCI_ERR_INTERNAL_ERROR;
+ }
+
+ /*ret = secmem_api_alloc(4096, allocsize*number+TUI_EXTRA_MEM_SIZE, &refcount,
+ &sec_handle, __func__, __LINE__);*/
+ ret = tui_region_offline(&pa, &size);
+ if (ret) {
+ pr_err("%s(%d): tui_region_offline failed!\n",
+ __func__, __LINE__);
+ return TUI_DCI_ERR_INTERNAL_ERROR;
+ }
+
+ if (ret == 0) {
+ g_tui_secmem_handle = pa;
+ allocbuffer[0].pa = (uint64_t) pa;
+ allocbuffer[1].pa = (uint64_t) (pa + allocsize);
+ } else {
+ return TUI_DCI_ERR_INTERNAL_ERROR;
+ }
+ pr_debug("tui pa=0x%x, size=0x%lx", (uint32_t)pa, size);
+
+ pr_debug("tui-hal allocasize=%ld number=%d, extra=%d\n", allocsize, number, TUI_EXTRA_MEM_SIZE);
+ pr_debug("%s(%d): allocated at %llx\n", __func__, __LINE__,
+ allocbuffer[0].pa);
+ pr_debug("%s(%d): allocated at %llx\n", __func__, __LINE__,
+ allocbuffer[1].pa);
+
+ pr_debug("%s: sec_handle=%x ret=%d", __func__, sec_handle, (int)ret);
+ return TUI_DCI_OK;
+
+#if 0
+ if ((size_t)(allocsize*number) <= g_tui_mem_pool.size) {
+ /* requested buffer fits in the memory pool */
+ allocbuffer[0].pa = (uint64_t) g_tui_mem_pool.pa;
+ allocbuffer[1].pa = (uint64_t) (g_tui_mem_pool.pa +
+ g_tui_mem_pool.size/2);
+ pr_debug("%s(%d): allocated at %llx\n", __func__, __LINE__,
+ allocbuffer[0].pa);
+ pr_debug("%s(%d): allocated at %llx\n", __func__, __LINE__,
+ allocbuffer[1].pa);
+ ret = TUI_DCI_OK;
+ } else {
+ /* requested buffer is bigger than the memory pool, return an
+ error */
+ pr_debug("%s(%d): Memory pool too small\n", __func__, __LINE__);
+ ret = TUI_DCI_ERR_INTERNAL_ERROR;
+ }
+
+ return ret;
+#endif
+}
+
+/**
+ * hal_tui_free() - free memory allocated by hal_tui_alloc()
+ *
+ * This function is called at the end of the TUI session, when the TUI module
+ * receives the CMD_TUI_SW_CLOSE_SESSION message. The function should free the
+ * buffers allocated by hal_tui_alloc(...).
+ */
+void hal_tui_free(void)
+{
+ pr_info("[TUI-HAL] hal_tui_free()\n");
+ if (g_tui_secmem_handle) {
+ //secmem_api_unref(g_tui_secmem_handle, __func__, __LINE__);
+ tui_region_online();
+ g_tui_secmem_handle = 0;
+ }
+}
+
+/**
+ * hal_tui_deactivate() - deactivate Normal World display and input
+ *
+ * This function should stop the Normal World display and, if necessary, Normal
+ * World input. It is called when a TUI session is opening, before the Secure
+ * World takes control of display and input.
+ *
+ * Return: must return 0 on success, non-zero otherwise.
+ */
+
+extern int display_enter_tui();
+extern int display_exit_tui();
+
+uint32_t hal_tui_deactivate(void)
+{
+ int ret = TUI_DCI_OK, tmp;
+ pr_info("[TUI-HAL] hal_tui_deactivate()\n");
+ /* Set linux TUI flag */
+ trustedui_set_mask(TRUSTEDUI_MODE_TUI_SESSION);
+ pr_info("TDDP/[TUI-HAL] %s()\n", __func__);
+ /*
+ * Stop NWd display here. After this function returns, SWd will take
+ * control of the display and input. Therefore the NWd should no longer
+ * access it
+ * This can be done by calling the fb_blank(FB_BLANK_POWERDOWN) function
+ * on the appropriate framebuffer device
+ */
+ tpd_enter_tui();
+ mt_eint_set_deint(10, 187);
+ enable_clock(MT_CG_PERI_I2C0, "i2c");
+ enable_clock(MT_CG_PERI_I2C1, "i2c");
+ enable_clock(MT_CG_PERI_I2C2, "i2c");
+ enable_clock(MT_CG_PERI_I2C3, "i2c");
+ enable_clock(MT_CG_PERI_APDMA, "i2c");
+
+ //gt1x_power_reset();
+
+ tmp = display_enter_tui();
+ if(tmp) {
+ pr_debug("TDDP/[TUI-HAL] %s() failed because display\n", __func__);
+ ret = TUI_DCI_ERR_OUT_OF_DISPLAY;
+ }
+
+
+ trustedui_set_mask(TRUSTEDUI_MODE_VIDEO_SECURED|
+ TRUSTEDUI_MODE_INPUT_SECURED);
+
+ pr_info("TDDP/[TUI-HAL] %s()\n", __func__);
+
+ return ret;
+}
+
+/**
+ * hal_tui_activate() - restore Normal World display and input after a TUI
+ * session
+ *
+ * This function should enable Normal World display and, if necessary, Normal
+ * World input. It is called after a TUI session, after the Secure World has
+ * released the display and input.
+ *
+ * Return: must return 0 on success, non-zero otherwise.
+ */
+uint32_t hal_tui_activate(void)
+{
+ pr_info("[TUI-HAL] hal_tui_activate()\n");
+ /* Protect NWd */
+ trustedui_clear_mask(TRUSTEDUI_MODE_VIDEO_SECURED|
+ TRUSTEDUI_MODE_INPUT_SECURED);
+
+ pr_info("TDDP %s()\n", __func__);
+
+ /*
+ * Restart NWd display here. TUI session has ended, and therefore the
+ * SWd will no longer use display and input.
+ * This can be done by calling the fb_blank(FB_BLANK_UNBLANK) function
+ * on the appropriate framebuffer device
+ */
+ /* Clear linux TUI flag */
+ mt_eint_clr_deint(10);
+ tpd_exit_tui();
+ tpd_reregister_from_tui();
+ //gt1x_power_reset();
+
+ disable_clock(MT_CG_PERI_I2C0, "i2c");
+ disable_clock(MT_CG_PERI_I2C1, "i2c");
+ disable_clock(MT_CG_PERI_I2C2, "i2c");
+ disable_clock(MT_CG_PERI_I2C3, "i2c");
+ disable_clock(MT_CG_PERI_APDMA, "i2c");
+
+ display_exit_tui();
+
+
+ trustedui_set_mode(TRUSTEDUI_MODE_OFF);
+
+ return TUI_DCI_OK;
+}
+
diff --git a/drivers/misc/mediatek/gud/302a/gud/build_tag.h b/drivers/misc/mediatek/gud/302a/gud/build_tag.h
new file mode 100644
index 000000000..b7a78a7d4
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302a/gud/build_tag.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2013-2014 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+#define MOBICORE_COMPONENT_BUILD_TAG \
+ "t-base-Mediatek-Armv8-Android-302A-V010-20150908_113718_68"
diff --git a/drivers/misc/mediatek/gud/302c/Makefile b/drivers/misc/mediatek/gud/302c/Makefile
new file mode 100644
index 000000000..4937fb49c
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/Makefile
@@ -0,0 +1,7 @@
+#ccflags-y += -Werror
+
+ifeq ($(CONFIG_ARM64), y)
+obj-$(CONFIG_TRUSTONIC_TEE_SUPPORT) += gud/
+else
+obj-$(CONFIG_TRUSTONIC_TEE_SUPPORT) += gud/
+endif
diff --git a/drivers/misc/mediatek/gud/302c/gud/Kconfig b/drivers/misc/mediatek/gud/302c/gud/Kconfig
new file mode 100644
index 000000000..164a34f36
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/Kconfig
@@ -0,0 +1,42 @@
+#
+# MobiCore configuration
+#
+config MOBICORE_DRIVER
+ tristate "MobiCore Driver"
+ depends on ARM
+ ---help---
+ Enable Linux Kernel MobiCore Support
+
+config MOBICORE_DEBUG
+ bool "MobiCore Module debug mode"
+ depends on MOBICORE_DRIVER
+ ---help---
+ Enable Debug mode in the MobiCore Driver.
+ It enables printing information about mobicore operations
+
+config MOBICORE_VERBOSE
+ bool "MobiCore Module verbose debug mode"
+ depends on MOBICORE_DEBUG
+ ---help---
+ Enable Verbose Debug mode in the MobiCore Driver.
+ It enables printing extra information about mobicore operations
+ Beware: this is only useful for debuging deep in the driver because
+ it prints too much logs
+
+config MOBICORE_API
+ tristate "Linux MobiCore API"
+ depends on MOBICORE_DRIVER
+ ---help---
+ Enable Linux Kernel MobiCore API
+
+config TRUSTONIC_TRUSTED_UI
+ tristate "<t-base TUI"
+ depends on MOBICORE_API
+ ---help---
+ Enable <t-base Trusted User Interface
+
+config TRUSTONIC_TRUSTED_UI_FB_BLANK
+ bool "<t-base TUI with fb_blank"
+ depends on TRUSTONIC_TRUSTED_UI
+ ---help---
+ Blank the framebuffer before starting a TUI session
diff --git a/drivers/misc/mediatek/gud/mt6735/gud/Makefile b/drivers/misc/mediatek/gud/302c/gud/Makefile
index c5711bed8..4480019b5 100755..100644
--- a/drivers/misc/mediatek/gud/mt6735/gud/Makefile
+++ b/drivers/misc/mediatek/gud/302c/gud/Makefile
@@ -10,6 +10,7 @@ GUD_ROOT_FOLDER := $(dir $(lastword $(MAKEFILE_LIST)))
# add our modules to kernel.
obj-$(CONFIG_TRUSTONIC_TEE_SUPPORT) += mcKernelApi.o
obj-$(CONFIG_TRUSTONIC_TEE_SUPPORT) += mcDrvModule.o
+obj-$(CONFIG_TRUSTONIC_TRUSTED_UI) += TlcTui.o
mcDrvModule-objs := MobiCoreDriver/logging.o \
MobiCoreDriver/ops.o \
@@ -24,6 +25,11 @@ mcKernelApi-objs := MobiCoreKernelApi/main.o \
MobiCoreKernelApi/session.o \
MobiCoreKernelApi/connection.o
+TlcTui-objs := TlcTui/main.o \
+ TlcTui/tlcTui.o \
+ TlcTui/trustedui.o \
+ TlcTui/tui-hal_$(MTK_PLATFORM).o
+
# Release mode by default
ccflags-y := -DNDEBUG
ccflags-y += -Wno-declaration-after-statement
@@ -39,6 +45,11 @@ ccflags-y += -DMC_NETLINK_COMPAT_V37
#ccflags-y += -I$(GUD_ROOT_FOLDER)MobiCoreDriver/platforms/$(MOBICORE_PLATFORM)
# MobiCore Driver includes
ccflags-y += -I$(GUD_ROOT_FOLDER)MobiCoreDriver/public
-# MobiCore KernelApi required incldes
+# MobiCore KernelApi required includes
ccflags-y += -I$(GUD_ROOT_FOLDER)MobiCoreKernelApi/include \
-I$(GUD_ROOT_FOLDER)MobiCoreKernelApi/public
+
+# MobiCore TlcTui required includes
+ccflags-y += -I$(GUD_ROOT_FOLDER)/TlcTui \
+ -I$(GUD_ROOT_FOLDER)/TlcTui/inc \
+ -I$(GUD_ROOT_FOLDER)/TlcTui/public
diff --git a/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/api.c b/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/api.c
new file mode 100644
index 000000000..354f5ddff
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/api.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2013 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+#include <linux/module.h>
+
+#include "main.h"
+#include "mem.h"
+#include "debug.h"
+
+int mobicore_map_vmem(struct mc_instance *instance, void *addr,
+ uint32_t len, uint32_t *handle)
+{
+ phys_addr_t phys;
+ return mc_register_wsm_mmu(instance, addr, len,
+ handle, &phys);
+}
+EXPORT_SYMBOL(mobicore_map_vmem);
+
+/*
+ * Unmap a virtual memory buffer from mobicore
+ * @param instance
+ * @param handle
+ *
+ * @return 0 if no error
+ *
+ */
+int mobicore_unmap_vmem(struct mc_instance *instance, uint32_t handle)
+{
+ return mc_unregister_wsm_mmu(instance, handle);
+}
+EXPORT_SYMBOL(mobicore_unmap_vmem);
+
+/*
+ * Free a WSM buffer allocated with mobicore_allocate_wsm
+ * @param instance
+ * @param handle handle of the buffer
+ *
+ * @return 0 if no error
+ *
+ */
+int mobicore_free_wsm(struct mc_instance *instance, uint32_t handle)
+{
+ return mc_free_buffer(instance, handle);
+}
+EXPORT_SYMBOL(mobicore_free_wsm);
+
+
+/*
+ * Allocate WSM for given instance
+ *
+ * @param instance instance
+ * @param requested_size size of the WSM
+ * @param handle pointer where the handle will be saved
+ * @param virt_kernel_addr pointer for the kernel virtual address
+ *
+ * @return error code or 0 for success
+ */
+int mobicore_allocate_wsm(struct mc_instance *instance,
+ unsigned long requested_size, uint32_t *handle, void **virt_kernel_addr)
+{
+ struct mc_buffer *buffer = NULL;
+
+ /* Setup the WSM buffer structure! */
+ if (mc_get_buffer(instance, &buffer, requested_size))
+ return -EFAULT;
+
+ *handle = buffer->handle;
+ *virt_kernel_addr = buffer->addr;
+ return 0;
+}
+EXPORT_SYMBOL(mobicore_allocate_wsm);
+
+/*
+ * Initialize a new mobicore API instance object
+ *
+ * @return Instance or NULL if no allocation was possible.
+ */
+struct mc_instance *mobicore_open(void)
+{
+ struct mc_instance *instance = mc_alloc_instance();
+ if (instance)
+ instance->admin = true;
+ return instance;
+}
+EXPORT_SYMBOL(mobicore_open);
+
+/*
+ * Release a mobicore instance object and all objects related to it
+ * @param instance instance
+ * @return 0 if Ok or -E ERROR
+ */
+int mobicore_release(struct mc_instance *instance)
+{
+ return mc_release_instance(instance);
+}
+EXPORT_SYMBOL(mobicore_release);
+
+/*
+ * Test if mobicore can sleep
+ *
+ * @return true if mobicore can sleep, false if it can't sleep
+ */
+bool mobicore_sleep_ready(void)
+{
+ return mc_sleep_ready();
+}
+EXPORT_SYMBOL(mobicore_sleep_ready);
+
diff --git a/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/arm.h b/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/arm.h
new file mode 100644
index 000000000..8c9fc37ee
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/arm.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2013 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+#ifndef _MC_ARM_H_
+#define _MC_ARM_H_
+
+#include "debug.h"
+
+#ifdef CONFIG_ARM64
+inline bool has_security_extensions(void)
+{
+ return true;
+}
+
+inline bool is_secure_mode(void)
+{
+ return false;
+}
+#else
+/*
+ * ARM Trustzone specific masks and modes
+ * Vanilla Linux is unaware of TrustZone extension.
+ * I.e. arch/arm/include/asm/ptrace.h does not define monitor mode.
+ * Also TZ bits in cpuid are not defined, ARM port uses magic numbers,
+ * see arch/arm/kernel/setup.c
+ */
+#define ARM_MONITOR_MODE (0x16) /*(0b10110)*/
+#define ARM_SECURITY_EXTENSION_MASK (0x30)
+
+/* check if CPU supports the ARM TrustZone Security Extensions */
+inline bool has_security_extensions(void)
+{
+ u32 fea = 0;
+ asm volatile(
+ "mrc p15, 0, %[fea], cr0, cr1, 0" :
+ [fea]"=r" (fea));
+
+ MCDRV_DBG_VERBOSE(mcd, "CPU Features: 0x%X", fea);
+
+ /*
+ * If the CPU features ID has 0 for security features then the CPU
+ * doesn't support TrustZone at all!
+ */
+ if ((fea & ARM_SECURITY_EXTENSION_MASK) == 0)
+ return false;
+
+ return true;
+}
+
+/* check if running in secure mode */
+inline bool is_secure_mode(void)
+{
+ u32 cpsr = 0;
+ u32 nsacr = 0;
+
+ asm volatile(
+ "mrc p15, 0, %[nsacr], cr1, cr1, 2\n"
+ "mrs %[cpsr], cpsr\n" :
+ [nsacr]"=r" (nsacr),
+ [cpsr]"=r"(cpsr));
+
+ MCDRV_DBG_VERBOSE(mcd, "CPRS.M = set to 0x%X\n", cpsr & MODE_MASK);
+ MCDRV_DBG_VERBOSE(mcd, "SCR.NS = set to 0x%X\n", nsacr);
+
+ /*
+ * If the NSACR contains the reset value(=0) then most likely we are
+ * running in Secure MODE.
+ * If the cpsr mode is set to monitor mode then we cannot load!
+ */
+ if (nsacr == 0 || ((cpsr & MODE_MASK) == ARM_MONITOR_MODE))
+ return true;
+
+ return false;
+}
+#endif
+
+#endif /* _MC_ARM_H_ */
diff --git a/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/debug.h b/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/debug.h
new file mode 100644
index 000000000..52362b346
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/debug.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2013 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+#ifndef _MC_DEBUG_H_
+#define _MC_DEBUG_H_
+/* Found in main.c */
+extern struct device *mcd;
+
+#define MCDRV_DBG_ERROR(dev, txt, ...) \
+ dev_err(dev, "MobiCore %s() ### ERROR: " txt "\n", \
+ __func__, \
+ ##__VA_ARGS__)
+
+/* dummy function helper macro. */
+#define DUMMY_FUNCTION() do {} while (0)
+
+#if defined(DEBUG)
+
+/* #define DEBUG_VERBOSE */
+#if defined(DEBUG_VERBOSE)
+#define MCDRV_DBG_VERBOSE MCDRV_DBG
+#else
+#define MCDRV_DBG_VERBOSE(...) DUMMY_FUNCTION()
+#endif
+
+#define MCDRV_DBG(dev, txt, ...) \
+ dev_info(dev, "MobiCore %s(): " txt "\n", \
+ __func__, \
+ ##__VA_ARGS__)
+
+#define MCDRV_DBG_WARN(dev, txt, ...) \
+ dev_warn(dev, "MobiCore %s() WARNING: " txt "\n", \
+ __func__, \
+ ##__VA_ARGS__)
+
+#define MCDRV_ASSERT(cond) \
+ do { \
+ if (unlikely(!(cond))) { \
+ panic("Assertion failed: %s:%d\n", \
+ __FILE__, __LINE__); \
+ } \
+ } while (0)
+
+#else
+
+#define MCDRV_DBG_VERBOSE(...) DUMMY_FUNCTION()
+#define MCDRV_DBG(...) DUMMY_FUNCTION()
+#define MCDRV_DBG_WARN(...) DUMMY_FUNCTION()
+
+#define MCDRV_ASSERT(...) DUMMY_FUNCTION()
+
+#endif /* [not] defined(DEBUG) */
+
+#endif /* _MC_DEBUG_H_ */
diff --git a/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/fastcall.h b/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/fastcall.h
new file mode 100644
index 000000000..b438d7244
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/fastcall.h
@@ -0,0 +1,258 @@
+/*
+ * Copyright (c) 2013-2014 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+#ifndef _MC_FASTCALL_H_
+#define _MC_FASTCALL_H_
+
+#include "debug.h"
+#include "platform.h"
+
+/* Use the arch_extension sec pseudo op before switching to secure world */
+#if defined(__GNUC__) && \
+ defined(__GNUC_MINOR__) && \
+ defined(__GNUC_PATCHLEVEL__) && \
+ ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)) \
+ >= 40502
+#ifndef CONFIG_ARM64
+#define MC_ARCH_EXTENSION_SEC
+#endif
+#endif
+
+/*
+ * MobiCore SMCs
+ */
+#define MC_SMC_N_YIELD 0x3 /* Yield to switch from NWd to SWd. */
+#define MC_SMC_N_SIQ 0x4 /* SIQ to switch from NWd to SWd. */
+
+/*
+ * MobiCore fast calls. See MCI documentation
+ */
+#ifdef MC_AARCH32_FC
+
+#define MC_FC_STD64_BASE ((uint32_t)0xFF000000)
+/**< Initializing FastCall. */
+#define MC_FC_INIT (MC_FC_STD64_BASE+1)
+/**< Info FastCall. */
+#define MC_FC_INFO (MC_FC_STD64_BASE+2)
+/**< Enable SWd tracing via memory */
+#define MC_FC_NWD_TRACE (MC_FC_STD64_BASE+10)
+#ifdef TBASE_CORE_SWITCHER
+/**< Core switching fastcall */
+#define MC_FC_SWITCH_CORE (MC_FC_STD64_BASE+54)
+#endif
+
+#else
+
+#define MC_FC_INIT -1
+#define MC_FC_INFO -2
+#define MC_FC_NWD_TRACE -31
+#ifdef TBASE_CORE_SWITCHER
+#define MC_FC_SWITCH_CORE 0x84000005
+#endif
+#endif
+
+/*
+ * return code for fast calls
+ */
+#define MC_FC_RET_OK 0
+#define MC_FC_RET_ERR_INVALID 1
+#define MC_FC_RET_ERR_ALREADY_INITIALIZED 5
+
+
+/* structure wrappers for specific fastcalls */
+
+/* generic fast call parameters */
+union fc_generic {
+ struct {
+ uint32_t cmd;
+ uint32_t param[3];
+ } as_in;
+ struct {
+ uint32_t resp;
+ uint32_t ret;
+ uint32_t param[2];
+ } as_out;
+};
+
+/* fast call init */
+union mc_fc_init {
+ union fc_generic as_generic;
+ struct {
+ uint32_t cmd;
+ uint32_t base;
+ uint32_t nq_info;
+ uint32_t mcp_info;
+ } as_in;
+ struct {
+ uint32_t resp;
+ uint32_t ret;
+ uint32_t rfu[2];
+ } as_out;
+};
+
+/* fast call info parameters */
+union mc_fc_info {
+ union fc_generic as_generic;
+ struct {
+ uint32_t cmd;
+ uint32_t ext_info_id;
+ uint32_t rfu[2];
+ } as_in;
+ struct {
+ uint32_t resp;
+ uint32_t ret;
+ uint32_t state;
+ uint32_t ext_info;
+ } as_out;
+};
+
+#ifdef TBASE_CORE_SWITCHER
+/* fast call switch Core parameters */
+union mc_fc_swich_core {
+ union fc_generic as_generic;
+ struct {
+ uint32_t cmd;
+ uint32_t core_id;
+ uint32_t rfu[2];
+ } as_in;
+ struct {
+ uint32_t resp;
+ uint32_t ret;
+ uint32_t state;
+ uint32_t ext_info;
+ } as_out;
+};
+#endif
+/*
+ * _smc() - fast call to MobiCore
+ *
+ * @data: pointer to fast call data
+ */
+#ifdef CONFIG_ARM64
+static inline long _smc(void *data)
+{
+ int ret = 0;
+
+ if (data == NULL)
+ return -EPERM;
+
+ {
+ union fc_generic *fc_generic = data;
+ /* SMC expect values in x0-x3 */
+ register u64 reg0 __asm__("x0") = fc_generic->as_in.cmd;
+ register u64 reg1 __asm__("x1") = fc_generic->as_in.param[0];
+ register u64 reg2 __asm__("x2") = fc_generic->as_in.param[1];
+ register u64 reg3 __asm__("x3") = fc_generic->as_in.param[2];
+
+ /* According to AARCH64 SMC Calling Convention (ARM DEN 0028A),
+ section 3.1 : registers x4-x17 are unpredictable/scratch
+ registers. So we have to make sure that the compiler does not
+ allocate any of those registers by letting him know that the
+ asm code might clobber them */
+ __asm__ volatile (
+ "smc #0\n"
+ : "+r"(reg0), "+r"(reg1), "+r"(reg2), "+r"(reg3) : :
+ "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12",
+ "x13", "x14", "x15", "x16", "x17"
+ );
+
+
+ /* set response */
+ fc_generic->as_out.resp = reg0;
+ fc_generic->as_out.ret = reg1;
+ fc_generic->as_out.param[0] = reg2;
+ fc_generic->as_out.param[1] = reg3;
+ }
+
+ return ret;
+}
+
+#else
+static inline long _smc(void *data)
+{
+ int ret = 0;
+
+ if (data == NULL)
+ return -EPERM;
+
+ #ifdef MC_SMC_FASTCALL
+ {
+ ret = smc_fastcall(data, sizeof(union fc_generic));
+ }
+ #else
+ {
+ union fc_generic *fc_generic = data;
+ /* SMC expect values in r0-r3 */
+ register u32 reg0 __asm__("r0") = fc_generic->as_in.cmd;
+ register u32 reg1 __asm__("r1") = fc_generic->as_in.param[0];
+ register u32 reg2 __asm__("r2") = fc_generic->as_in.param[1];
+ register u32 reg3 __asm__("r3") = fc_generic->as_in.param[2];
+
+ __asm__ volatile (
+#ifdef MC_ARCH_EXTENSION_SEC
+ /* This pseudo op is supported and required from
+ * binutils 2.21 on */
+ ".arch_extension sec\n"
+#endif
+ "smc #0\n"
+ : "+r"(reg0), "+r"(reg1), "+r"(reg2), "+r"(reg3)
+ );
+
+
+#if defined(__ARM_VE_A9X4_QEMU__) || defined(__ARM_GOLDFISH_QEMU__)
+ /* Qemu does not return to the address following the SMC
+ * instruction so we have to insert several nop instructions to
+ * workaround this Qemu bug. */
+ __asm__ volatile (
+ "nop\n"
+ "nop\n"
+ "nop\n"
+ "nop"
+ );
+#endif
+
+ /* set response */
+ fc_generic->as_out.resp = reg0;
+ fc_generic->as_out.ret = reg1;
+ fc_generic->as_out.param[0] = reg2;
+ fc_generic->as_out.param[1] = reg3;
+ }
+ #endif
+ return ret;
+}
+#endif
+
+/*
+ * convert fast call return code to linux driver module error code
+ */
+static inline int convert_fc_ret(uint32_t sret)
+{
+ int ret = -EFAULT;
+
+ switch (sret) {
+ case MC_FC_RET_OK:
+ ret = 0;
+ break;
+ case MC_FC_RET_ERR_INVALID:
+ ret = -EINVAL;
+ break;
+ case MC_FC_RET_ERR_ALREADY_INITIALIZED:
+ ret = -EBUSY;
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+#endif /* _MC_FASTCALL_H_ */
diff --git a/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/logging.c b/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/logging.c
new file mode 100644
index 000000000..6091a4d51
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/logging.c
@@ -0,0 +1,384 @@
+/*
+ * Copyright (c) 2013-2014 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+/*
+ * MobiCore Driver Logging Subsystem.
+ *
+ * The logging subsystem provides the interface between the Mobicore trace
+ * buffer and the Linux log
+ */
+#include <linux/miscdevice.h>
+#include <linux/moduleparam.h>
+#include <linux/kthread.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/device.h>
+
+#include "main.h"
+#include "debug.h"
+#include "ops.h"
+#include "logging.h"
+
+/* Default length of the log ring buffer 256KB*/
+#define LOG_BUF_SIZE (64 * PAGE_SIZE)
+
+/* Max Len of a log line for printing */
+#define LOG_LINE_SIZE 256
+
+static uint32_t log_size = LOG_BUF_SIZE;
+
+module_param(log_size, uint, 0);
+MODULE_PARM_DESC(log_size, "Size of the MobiCore log ringbuffer(256KB def)");
+
+/* Definitions for log version 2 */
+#define LOG_TYPE_MASK (0x0007)
+#define LOG_TYPE_CHAR 0
+#define LOG_TYPE_INTEGER 1
+/* Field length */
+#define LOG_LENGTH_MASK (0x00F8)
+#define LOG_LENGTH_SHIFT 3
+/* Extra attributes */
+#define LOG_EOL (0x0100)
+#define LOG_INTEGER_DECIMAL (0x0200)
+#define LOG_INTEGER_SIGNED (0x0400)
+
+struct logmsg_struct {
+ uint16_t ctrl; /* Type and format of data */
+ uint16_t source; /* Unique value for each event source */
+ uint32_t log_data; /* Value, if any */
+};
+
+static uint16_t prev_source; /* Previous Log source */
+static uint32_t log_pos; /* MobiCore log previous position */
+static struct mc_trace_buf *log_buf; /* MobiCore log buffer structure */
+struct task_struct *log_thread; /* Log Thread task structure */
+static char *log_line; /* Log Line buffer */
+static uint32_t log_line_len; /* Log Line buffer current length */
+static int thread_err;
+
+#ifdef CONFIG_MT_TRUSTONIC_TEE_DEBUGFS
+extern uint8_t trustonic_swd_debug;
+#endif
+static void log_eol(uint16_t source)
+{
+ if (!strnlen(log_line, LOG_LINE_SIZE)) {
+ /* In case a TA tries to print a 0x0 */
+ log_line_len = 0;
+ return;
+ }
+#ifdef CONFIG_MT_TRUSTONIC_TEE_DEBUGFS
+ if (trustonic_swd_debug) {
+#endif
+ /* MobiCore Userspace */
+ if (prev_source)
+ pr_debug("%03x|%s\n", prev_source, log_line);
+ /* MobiCore kernel */
+ else
+ pr_debug("%s\n", log_line);
+#ifdef CONFIG_MT_TRUSTONIC_TEE_DEBUGFS
+ }
+#endif
+
+ log_line_len = 0;
+ log_line[0] = 0;
+}
+
+/*
+ * Collect chars in log_line buffer and output the buffer when it is full.
+ * No locking needed because only "mobicore_log" thread updates this buffer.
+ */
+static void log_char(char ch, uint16_t source)
+{
+ if (ch == '\n' || ch == '\r') {
+ log_eol(source);
+ return;
+ }
+
+ if (log_line_len >= LOG_LINE_SIZE - 1 || source != prev_source)
+ log_eol(source);
+
+
+ log_line[log_line_len] = ch;
+ log_line[log_line_len + 1] = 0;
+ log_line_len++;
+ prev_source = source;
+}
+
+static const uint8_t HEX2ASCII[16] = {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+
+static void dbg_raw_nro(uint32_t format, uint32_t value, uint16_t source)
+{
+ int digits = 1;
+ uint32_t base = (format & LOG_INTEGER_DECIMAL) ? 10 : 16;
+ int width = (format & LOG_LENGTH_MASK) >> LOG_LENGTH_SHIFT;
+ int negative = 0;
+ uint32_t digit_base = 1;
+
+ if ((format & LOG_INTEGER_SIGNED) != 0 && ((signed int)value) < 0) {
+ negative = 1;
+ value = (uint32_t)(-(signed int)value);
+ width--;
+ }
+
+ /* Find length and divider to get largest digit */
+ while (value / digit_base >= base) {
+ digit_base *= base;
+ digits++;
+ }
+
+ if (width > digits) {
+ char ch = (base == 10) ? ' ' : '0';
+ while (width > digits) {
+ log_char(ch, source);
+ width--;
+ }
+ }
+
+ if (negative)
+ log_char('-', source);
+
+ while (digits-- > 0) {
+ uint32_t d = value / digit_base;
+ log_char(HEX2ASCII[d], source);
+ value = value - d * digit_base;
+ digit_base /= base;
+ }
+}
+
+static void log_msg(struct logmsg_struct *msg)
+{
+ switch (msg->ctrl & LOG_TYPE_MASK) {
+ case LOG_TYPE_CHAR: {
+ uint32_t ch;
+ ch = msg->log_data;
+ while (ch != 0) {
+ log_char(ch & 0xFF, msg->source);
+ ch >>= 8;
+ }
+ break;
+ }
+ case LOG_TYPE_INTEGER: {
+ dbg_raw_nro(msg->ctrl, msg->log_data, msg->source);
+ break;
+ }
+ default:
+ break;
+ }
+ if (msg->ctrl & LOG_EOL)
+ log_eol(msg->source);
+}
+
+static uint32_t process_log(void)
+{
+ char *last_msg = log_buf->buff + log_buf->write_pos;
+ char *buff = log_buf->buff + log_pos;
+
+ while (buff != last_msg) {
+ log_msg((struct logmsg_struct *)buff);
+ buff += sizeof(struct logmsg_struct);
+ /* Wrap around */
+ if ((buff + sizeof(struct logmsg_struct)) >
+ ((char *)log_buf + log_size))
+ buff = log_buf->buff;
+ }
+ return buff - log_buf->buff;
+}
+
+static void log_exit(void)
+{
+ union fc_generic fc_log;
+
+ memset(&fc_log, 0, sizeof(fc_log));
+ fc_log.as_in.cmd = MC_FC_NWD_TRACE;
+
+ MCDRV_DBG(mcd, "Unregister the trace buffer");
+ mc_fastcall(&fc_log);
+ MCDRV_DBG(mcd, "fc_log out ret=0x%08x", fc_log.as_out.ret);
+
+ if (fc_log.as_out.ret == 0) {
+ free_pages((unsigned long)log_buf, get_order(log_size));
+ log_buf = NULL;
+ }
+}
+
+/* log_worker() - Worker thread processing the log_buf buffer. */
+static int log_worker(void *p)
+{
+ int ret = 0;
+ if (log_buf == NULL) {
+ ret = -EFAULT;
+ goto err_kthread;
+ }
+
+ while (!kthread_should_stop()) {
+ if (log_buf->write_pos == log_pos)
+ schedule_timeout_interruptible(MAX_SCHEDULE_TIMEOUT);
+
+ switch (log_buf->version) {
+ case 2:
+ log_pos = process_log();
+ break;
+ default:
+ MCDRV_DBG_ERROR(mcd, "Unknown Mobicore log data");
+ log_pos = log_buf->write_pos;
+ /*
+ * Stop the thread as we have no idea what
+ * happens next
+ */
+ ret = -EFAULT;
+ goto err_kthread;
+ }
+ }
+err_kthread:
+ MCDRV_DBG(mcd, "Logging thread stopped!");
+ thread_err = ret;
+ /* Wait until the next kthread_stop() is called, if it was already
+ * called we just slip through, if there is an error signal it and
+ * wait to get the signal */
+ set_current_state(TASK_INTERRUPTIBLE);
+ while (!kthread_should_stop()) {
+ schedule();
+ set_current_state(TASK_INTERRUPTIBLE);
+ }
+ set_current_state(TASK_RUNNING);
+
+ log_exit();
+
+ return ret;
+}
+
+/*
+ * Wake up the log reader thread
+ * This should be called from the places where calls into MobiCore have
+ * generated some logs(eg, yield, SIQ...)
+ */
+void mobicore_log_read(void)
+{
+ if (log_thread == NULL || IS_ERR(log_thread))
+ return;
+
+ /* The thread itself is in some error condition so just get
+ * rid of it */
+ if (thread_err != 0) {
+ kthread_stop(log_thread);
+ log_thread = NULL;
+ return;
+ }
+
+ wake_up_process(log_thread);
+}
+
+/*
+ * Setup MobiCore kernel log. It assumes it's running on CORE 0!
+ * The fastcall will complain is that is not the case!
+ */
+long mobicore_log_setup(void)
+{
+ phys_addr_t phys_log_buf;
+ union fc_generic fc_log;
+ struct sched_param param = { .sched_priority = 1 };
+
+ long ret;
+ log_pos = 0;
+ log_buf = NULL;
+ log_thread = NULL;
+ log_line = NULL;
+ log_line_len = 0;
+ prev_source = 0;
+ thread_err = 0;
+
+ /* Sanity check for the log size */
+ if (log_size < PAGE_SIZE)
+ return -EFAULT;
+ else
+ log_size = PAGE_ALIGN(log_size);
+
+ log_line = kzalloc(LOG_LINE_SIZE, GFP_KERNEL);
+ if (IS_ERR(log_line)) {
+ MCDRV_DBG_ERROR(mcd, "failed to allocate log line!");
+ return -ENOMEM;
+ }
+
+ log_thread = kthread_create(log_worker, NULL, "mc_log");
+ if (IS_ERR(log_thread)) {
+ MCDRV_DBG_ERROR(mcd, "MobiCore log thread creation failed!");
+ ret = -EFAULT;
+ goto err_free_line;
+ }
+
+ sched_setscheduler(log_thread, SCHED_IDLE, &param);
+ /*
+ * We are going to map this buffer into virtual address space in SWd.
+ * To reduce complexity there, we use a contiguous buffer.
+ */
+ log_buf = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
+ get_order(log_size));
+ if (!log_buf) {
+ MCDRV_DBG_ERROR(mcd, "Failed to get page for logger!");
+ ret = -ENOMEM;
+ goto err_stop_kthread;
+ }
+ phys_log_buf = virt_to_phys(log_buf);
+
+ memset(&fc_log, 0, sizeof(fc_log));
+ fc_log.as_in.cmd = MC_FC_NWD_TRACE;
+ fc_log.as_in.param[0] = (uint32_t)phys_log_buf;
+#ifdef CONFIG_PHYS_ADDR_T_64BIT
+ fc_log.as_in.param[1] = (uint32_t)(phys_log_buf >> 32);
+#endif
+ fc_log.as_in.param[2] = log_size;
+
+ MCDRV_DBG(mcd, "fc_log virt=%p phys=0x%llX",
+ log_buf, (u64)phys_log_buf);
+ mc_fastcall(&fc_log);
+ MCDRV_DBG(mcd, "fc_log out ret=0x%08x", fc_log.as_out.ret);
+
+ /* If the setup failed we must free the memory allocated */
+ if (fc_log.as_out.ret) {
+ MCDRV_DBG_ERROR(mcd, "MobiCore shared traces setup failed!");
+ free_pages((unsigned long)log_buf, get_order(log_size));
+ log_buf = NULL;
+ ret = -EIO;
+ goto err_stop_kthread;
+ }
+
+ set_task_state(log_thread, TASK_INTERRUPTIBLE);
+
+ MCDRV_DBG(mcd, "fc_log Logger version %u", log_buf->version);
+ return 0;
+
+err_stop_kthread:
+ kthread_stop(log_thread);
+ log_thread = NULL;
+err_free_line:
+ kfree(log_line);
+ log_line = NULL;
+ return ret;
+}
+
+/*
+ * Free kernel log components.
+ * ATTN: We can't free the log buffer because it's also in use by MobiCore and
+ * even if the module is unloaded MobiCore is still running.
+ */
+void mobicore_log_free(void)
+{
+ if (log_thread && !IS_ERR(log_thread)) {
+ /* We don't really care what the thread returns for exit */
+ kthread_stop(log_thread);
+ }
+
+ kfree(log_line);
+}
diff --git a/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/logging.h b/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/logging.h
new file mode 100644
index 000000000..a3cbca21c
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/logging.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2013 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+#ifndef _MC_LOGGING_H_
+#define _MC_LOGGING_H_
+
+/* MobiCore internal trace buffer structure. */
+struct mc_trace_buf {
+ uint32_t version; /* version of trace buffer */
+ uint32_t length; /* length of allocated buffer(includes header) */
+ uint32_t write_pos; /* last write position */
+ char buff[1]; /* start of the log buffer */
+};
+
+/* MobiCore internal trace log setup. */
+void mobicore_log_read(void);
+long mobicore_log_setup(void);
+void mobicore_log_free(void);
+
+#endif /* _MC_LOGGING_H_ */
diff --git a/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/main.c b/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/main.c
new file mode 100644
index 000000000..62b1d49ee
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/main.c
@@ -0,0 +1,1733 @@
+/*
+ * Copyright (c) 2013-2015 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+/*
+ * MobiCore Driver Kernel Module.
+ *
+ * This driver represents the command proxy on the lowest layer, from the
+ * secure world to the non secure world, and vice versa.
+
+ * This driver offers IOCTL commands, for access to the secure world, and has
+ * the interface from the secure world to the normal world.
+ * The access to the driver is possible with a file descriptor,
+ * which has to be created by the fd = open(/dev/mobicore) command or
+ * fd = open(/dev/mobicore-user)
+ */
+#include <linux/miscdevice.h>
+#include <linux/interrupt.h>
+#include <linux/highmem.h>
+#include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/ioctl.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/completion.h>
+#include <linux/fdtable.h>
+#include <linux/cdev.h>
+#ifdef CONFIG_OF
+#include <linux/of_irq.h>
+#endif
+#ifdef CONFIG_MT_TRUSTONIC_TEE_DEBUGFS
+#include <linux/debugfs.h>
+#endif
+#include <net/net_namespace.h>
+#include <net/sock.h>
+#include <net/tcp_states.h>
+#include <net/af_unix.h>
+
+#include "main.h"
+#include "fastcall.h"
+
+#include "arm.h"
+#include "mem.h"
+#include "ops.h"
+#include "pm.h"
+#include "debug.h"
+#include "logging.h"
+#include "build_tag.h"
+
+/* Define a MobiCore device structure for use with dev_debug() etc */
+struct device_driver mcd_debug_name = {
+ .name = "MobiCore"
+};
+
+struct device mcd_debug_subname = {
+ .driver = &mcd_debug_name
+};
+
+struct device *mcd = &mcd_debug_subname;
+
+/* We need 2 devices for admin and user interface*/
+#define MC_DEV_MAX 2
+
+/* Need to discover a chrdev region for the driver */
+static dev_t mc_dev_admin, mc_dev_user;
+struct cdev mc_admin_cdev, mc_user_cdev;
+/* Device class for the driver assigned major */
+static struct class *mc_device_class;
+
+#ifndef FMODE_PATH
+ #define FMODE_PATH 0x0
+#endif
+
+static struct sock *__get_socket(struct file *filp)
+{
+ struct sock *u_sock = NULL;
+ struct inode *inode = filp->f_path.dentry->d_inode;
+
+ /*
+ * Socket ?
+ */
+ if (S_ISSOCK(inode->i_mode) && !(filp->f_mode & FMODE_PATH)) {
+ struct socket *sock = SOCKET_I(inode);
+ struct sock *s = sock->sk;
+
+ /*
+ * PF_UNIX ?
+ */
+ if (s && sock->ops && sock->ops->family == PF_UNIX)
+ u_sock = s;
+ }
+ return u_sock;
+}
+
+
+/* MobiCore interrupt context data */
+static struct mc_context ctx;
+
+/* Get process context from file pointer */
+static struct mc_instance *get_instance(struct file *file)
+{
+ return (struct mc_instance *)(file->private_data);
+}
+
+extern struct mc_mmu_table *find_mmu_table(unsigned int handle);
+uint32_t mc_get_new_handle(void)
+{
+ uint32_t handle;
+ struct mc_buffer *buffer;
+ struct mc_mmu_table *table;
+
+
+ mutex_lock(&ctx.cont_bufs_lock);
+retry:
+ handle = atomic_inc_return(&ctx.handle_counter);
+ /* The handle must leave 12 bits (PAGE_SHIFT) for the 12 LSBs to be
+ * zero, as mmap requires the offset to be page-aligned, plus 1 bit for
+ * the MSB to be 0 too, so mmap does not see the offset as negative
+ * and fail.
+ */
+ if ((handle << (PAGE_SHIFT+1)) == 0) {
+ atomic_set(&ctx.handle_counter, 1);
+ handle = 1;
+ }
+ list_for_each_entry(buffer, &ctx.cont_bufs, list) {
+ if (buffer->handle == handle)
+ goto retry;
+ }
+
+ /* here we assume table_lock is already taken. */
+ table = find_mmu_table(handle);
+ if (table != NULL)
+ goto retry;
+
+ mutex_unlock(&ctx.cont_bufs_lock);
+
+ return handle;
+}
+
+/* Clears the reserved bit of each page and frees the pages */
+static inline void free_continguous_pages(void *addr, unsigned int order)
+{
+ int i;
+ struct page *page = virt_to_page(addr);
+ for (i = 0; i < (1<<order); i++) {
+ MCDRV_DBG_VERBOSE(mcd, "free page at 0x%p", page);
+ clear_bit(PG_reserved, &page->flags);
+ page++;
+ }
+
+ MCDRV_DBG_VERBOSE(mcd, "freeing addr:%p, order:%x", addr, order);
+ free_pages((unsigned long)addr, order);
+}
+
+/* Frees the memory associated with a buffer */
+static int free_buffer(struct mc_buffer *buffer)
+{
+ if (buffer->handle == 0)
+ return -EINVAL;
+
+ if (buffer->addr == 0)
+ return -EINVAL;
+
+ if (!atomic_dec_and_test(&buffer->usage)) {
+ MCDRV_DBG_VERBOSE(mcd, "Could not free %u, usage=%d",
+ buffer->handle,
+ atomic_read(&(buffer->usage)));
+ return 0;
+ }
+
+ MCDRV_DBG_VERBOSE(mcd,
+ "h=%u phy=0x%llx, kaddr=0x%p len=%u buf=%p usage=%d",
+ buffer->handle, (u64)buffer->phys, buffer->addr,
+ buffer->len, buffer, atomic_read(&(buffer->usage)));
+
+ list_del(&buffer->list);
+
+ free_continguous_pages(buffer->addr, buffer->order);
+ kfree(buffer);
+ return 0;
+}
+
+static uint32_t mc_find_cont_wsm_addr(struct mc_instance *instance, void *uaddr,
+ void **addr, uint32_t len)
+{
+ int ret = 0;
+ struct mc_buffer *buffer;
+
+ if (WARN(!instance, "No instance data available"))
+ return -EFAULT;
+
+ mutex_lock(&instance->lock);
+
+ mutex_lock(&ctx.cont_bufs_lock);
+
+ /* search for the given handle in the buffers list */
+ list_for_each_entry(buffer, &ctx.cont_bufs, list) {
+ if (buffer->uaddr == uaddr && buffer->len == len) {
+ *addr = buffer->addr;
+ goto found;
+ }
+ }
+
+ /* Coundn't find the buffer */
+ ret = -EINVAL;
+
+found:
+ mutex_unlock(&ctx.cont_bufs_lock);
+ mutex_unlock(&instance->lock);
+
+ return ret;
+}
+
+bool mc_check_owner_fd(struct mc_instance *instance, int32_t fd)
+{
+#ifndef __ARM_VE_A9X4_STD__
+ struct file *fp;
+ struct sock *s;
+ struct files_struct *files;
+ struct task_struct *peer = NULL;
+ bool ret = false;
+
+ MCDRV_DBG_VERBOSE(mcd, "Finding wsm for fd = %d", fd);
+ if (!instance)
+ return false;
+
+ if (is_daemon(instance))
+ return true;
+
+ rcu_read_lock();
+ fp = fcheck_files(current->files, fd);
+ if (fp == NULL)
+ goto out;
+ s = __get_socket(fp);
+ if (s)
+ peer = get_pid_task(s->sk_peer_pid, PIDTYPE_PID);
+
+ if (peer) {
+ task_lock(peer);
+ files = peer->files;
+ if (!files)
+ goto out;
+ for (fd = 0; fd < files_fdtable(files)->max_fds; fd++) {
+ fp = fcheck_files(files, fd);
+ if (!fp)
+ continue;
+ if (fp->private_data == instance) {
+ ret = true;
+ break;
+ }
+ }
+ } else {
+ MCDRV_DBG(mcd, "Owner not found!");
+ }
+out:
+ if (peer) {
+ task_unlock(peer);
+ put_task_struct(peer);
+ }
+ rcu_read_unlock();
+ if (!ret)
+ MCDRV_DBG(mcd, "Owner not found!");
+ return ret;
+#else
+ return true;
+#endif
+}
+static uint32_t mc_find_cont_wsm(struct mc_instance *instance, uint32_t handle,
+ int32_t fd, phys_addr_t *phys, uint32_t *len)
+{
+ int ret = 0;
+ struct mc_buffer *buffer;
+
+ if (WARN(!instance, "No instance data available"))
+ return -EFAULT;
+
+ if (WARN_ON(!is_daemon(instance))) {
+ MCDRV_DBG_ERROR(mcd, "caller not MobiCore Daemon");
+ return -EPERM;
+ }
+
+ mutex_lock(&instance->lock);
+
+ mutex_lock(&ctx.cont_bufs_lock);
+
+ /* search for the given handle in the buffers list */
+ list_for_each_entry(buffer, &ctx.cont_bufs, list) {
+ if (buffer->handle == handle) {
+ if (mc_check_owner_fd(buffer->instance, fd)) {
+ *phys = buffer->phys;
+ *len = buffer->len;
+ goto found;
+ } else {
+ break;
+ }
+ }
+ }
+
+ /* Couldn't find the buffer */
+ ret = -EINVAL;
+
+found:
+ mutex_unlock(&ctx.cont_bufs_lock);
+ mutex_unlock(&instance->lock);
+
+ return ret;
+}
+
+/*
+ * __free_buffer - Free a WSM buffer allocated with mobicore_allocate_wsm
+ *
+ * @instance
+ * @handle handle of the buffer
+ *
+ * Returns 0 if no error
+ *
+ */
+static int __free_buffer(struct mc_instance *instance, uint32_t handle,
+ bool unlock)
+{
+ int ret = 0;
+ struct mc_buffer *buffer;
+#ifndef MC_VM_UNMAP
+ struct mm_struct *mm = current->mm;
+#endif
+
+ if (WARN(!instance, "No instance data available"))
+ return -EFAULT;
+
+ mutex_lock(&ctx.cont_bufs_lock);
+ /* search for the given handle in the buffers list */
+ list_for_each_entry(buffer, &ctx.cont_bufs, list) {
+ if (buffer->handle == handle)
+ goto found_buffer;
+ }
+ ret = -EINVAL;
+ goto err;
+found_buffer:
+ if (!is_daemon(instance) && buffer->instance != instance) {
+ ret = -EPERM;
+ goto err;
+ }
+ mutex_unlock(&ctx.cont_bufs_lock);
+ /* Only unmap if the request is coming from the user space and
+ * it hasn't already been unmapped */
+ if (!unlock && buffer->uaddr != NULL) {
+#ifndef MC_VM_UNMAP
+ /* do_munmap must be done with mm->mmap_sem taken */
+ down_write(&mm->mmap_sem);
+ ret = do_munmap(mm,
+ (long unsigned int)buffer->uaddr,
+ buffer->len);
+ up_write(&mm->mmap_sem);
+
+#else
+ ret = vm_munmap((long unsigned int)buffer->uaddr, buffer->len);
+#endif
+ if (ret < 0) {
+ /* Something is not right if we end up here, better not
+ * clean the buffer so we just leak memory instead of
+ * creating security issues */
+ MCDRV_DBG_ERROR(mcd, "Memory can't be unmapped");
+ return -EINVAL;
+ }
+ }
+
+ mutex_lock(&ctx.cont_bufs_lock);
+ /* search for the given handle in the buffers list */
+ list_for_each_entry(buffer, &ctx.cont_bufs, list) {
+ if (buffer->handle == handle)
+ goto del_buffer;
+ }
+ ret = -EINVAL;
+ goto err;
+
+del_buffer:
+ if (is_daemon(instance) || buffer->instance == instance)
+ ret = free_buffer(buffer);
+ else
+ ret = -EPERM;
+err:
+ mutex_unlock(&ctx.cont_bufs_lock);
+ return ret;
+}
+
+int mc_free_buffer(struct mc_instance *instance, uint32_t handle)
+{
+ int ret = 0;
+
+ if (WARN(!instance, "No instance data available"))
+ return -EFAULT;
+
+ mutex_lock(&instance->lock);
+
+ ret = __free_buffer(instance, handle, false);
+ mutex_unlock(&instance->lock);
+ return ret;
+}
+
+
+int mc_get_buffer(struct mc_instance *instance,
+ struct mc_buffer **buffer, unsigned long len)
+{
+ struct mc_buffer *cbuffer = NULL;
+ void *addr = 0;
+ phys_addr_t phys = 0;
+ unsigned int order;
+#if defined(DEBUG_VERBOSE)
+ unsigned long allocated_size;
+#endif
+ int ret = 0;
+
+ if (WARN(!instance, "No instance data available"))
+ return -EFAULT;
+
+ if (len == 0) {
+ MCDRV_DBG_WARN(mcd, "cannot allocate size 0");
+ return -ENOMEM;
+ }
+
+ order = get_order(len);
+ if (order > MAX_ORDER) {
+ MCDRV_DBG_WARN(mcd, "Buffer size too large");
+ return -ENOMEM;
+ }
+#if defined(DEBUG_VERBOSE)
+ allocated_size = (1 << order) * PAGE_SIZE;
+#endif
+
+ if (mutex_lock_interruptible(&instance->lock))
+ return -ERESTARTSYS;
+
+ /* allocate a new buffer. */
+ cbuffer = kzalloc(sizeof(*cbuffer), GFP_KERNEL);
+ if (!cbuffer) {
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ MCDRV_DBG_VERBOSE(mcd, "size %ld -> order %d --> %ld (2^n pages)",
+ len, order, allocated_size);
+
+ addr = (void *)__get_free_pages(GFP_USER | __GFP_ZERO, order);
+ if (!addr) {
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ phys = virt_to_phys(addr);
+ cbuffer->handle = mc_get_new_handle();
+ cbuffer->phys = phys;
+ cbuffer->addr = addr;
+ cbuffer->order = order;
+ cbuffer->len = len;
+ cbuffer->instance = instance;
+ cbuffer->uaddr = 0;
+ /* Refcount +1 because the TLC is requesting it */
+ atomic_set(&cbuffer->usage, 1);
+
+ INIT_LIST_HEAD(&cbuffer->list);
+ mutex_lock(&ctx.cont_bufs_lock);
+ list_add(&cbuffer->list, &ctx.cont_bufs);
+ mutex_unlock(&ctx.cont_bufs_lock);
+
+ MCDRV_DBG_VERBOSE(mcd,
+ "phy=0x%llx-0x%llx, kaddr=0x%p h=%d buf=%p usage=%d",
+ (u64)phys,
+ (u64)(phys+allocated_size),
+ addr, cbuffer->handle,
+ cbuffer, atomic_read(&(cbuffer->usage)));
+ *buffer = cbuffer;
+
+end:
+ if (ret)
+ kfree(cbuffer);
+
+ mutex_unlock(&instance->lock);
+ return ret;
+}
+
+/*
+ * __lock_buffer() - Locks a contiguous buffer - +1 refcount.
+ * Assumes the instance lock is already taken!
+ */
+static int __lock_buffer(struct mc_instance *instance, uint32_t handle)
+{
+ int ret = 0;
+ struct mc_buffer *buffer;
+
+ if (WARN(!instance, "No instance data available"))
+ return -EFAULT;
+
+ if (WARN_ON(!is_daemon(instance))) {
+ MCDRV_DBG_ERROR(mcd, "caller not MobiCore Daemon");
+ return -EPERM;
+ }
+
+ mutex_lock(&ctx.cont_bufs_lock);
+ /* search for the given handle in the buffers list */
+ list_for_each_entry(buffer, &ctx.cont_bufs, list) {
+ if (buffer->handle == handle) {
+ atomic_inc(&buffer->usage);
+ MCDRV_DBG_VERBOSE(mcd, "handle=%u phy=0x%llx usage=%d",
+ buffer->handle, (u64)buffer->phys,
+ atomic_read(&(buffer->usage)));
+ goto unlock;
+ }
+ }
+ ret = -EINVAL;
+
+unlock:
+ mutex_unlock(&ctx.cont_bufs_lock);
+ return ret;
+}
+
+static phys_addr_t get_mci_base_phys(unsigned int len)
+{
+ if (ctx.mci_base.phys) {
+ return ctx.mci_base.phys;
+ } else {
+ unsigned int order = get_order(len);
+ ctx.mcp = NULL;
+ ctx.mci_base.order = order;
+ ctx.mci_base.addr =
+ (void *)__get_free_pages(GFP_USER | __GFP_ZERO, order);
+ if (ctx.mci_base.addr == NULL) {
+ MCDRV_DBG_WARN(mcd, "get_free_pages failed");
+ memset(&ctx.mci_base, 0, sizeof(ctx.mci_base));
+ return 0;
+ }
+ ctx.mci_base.phys = virt_to_phys(ctx.mci_base.addr);
+ return ctx.mci_base.phys;
+ }
+}
+
+/*
+ * Create a MMU table from a virtual memory buffer which can be vmalloc
+ * or user space virtual memory
+ */
+int mc_register_wsm_mmu(struct mc_instance *instance,
+ void *buffer, uint32_t len,
+ uint32_t *handle, phys_addr_t *phys)
+{
+ int ret = 0;
+ struct mc_mmu_table *table = NULL;
+ struct task_struct *task = current;
+ void *kbuff = NULL;
+
+ uint32_t index;
+ uint64_t *mmu_table = NULL;
+ uint32_t nb_of_1mb_section;
+ unsigned int offset;
+ unsigned int page_number;
+ unsigned int *handles = NULL;
+ unsigned int tmp_len;
+
+ if (WARN(!instance, "No instance data available"))
+ return -EFAULT;
+
+ if (len == 0) {
+ MCDRV_DBG_ERROR(mcd, "len=0 is not supported!");
+ return -EINVAL;
+ }
+
+
+ /* The offset of the buffer*/
+ offset = (unsigned int)
+ (((unsigned long)(buffer)) & (~PAGE_MASK));
+
+ MCDRV_DBG_VERBOSE(mcd, "buffer: %p, len=%08x offset=%d",
+ buffer, len, offset);
+
+ /* Number of 4k pages required */
+ page_number = (offset + len) / PAGE_SIZE;
+ if (((offset + len) & (~PAGE_MASK)) != 0)
+ page_number++;
+
+ /* Number of 1mb sections */
+ nb_of_1mb_section = (page_number * PAGE_SIZE) / SZ_1M;
+ if (((page_number * PAGE_SIZE) & (SZ_1M - 1)) != 0)
+ nb_of_1mb_section++;
+
+ /* since for both non-LPAE and LPAE cases we use uint64_t records
+ * for the fake table we don't support more than 512 MB TA size
+ */
+ if (nb_of_1mb_section > SZ_4K / sizeof(uint64_t)) {
+ MCDRV_DBG_ERROR(mcd, "fake L1 table size too big");
+ return -ENOMEM;
+ }
+ MCDRV_DBG_VERBOSE(mcd, "nb_of_1mb_section=%d", nb_of_1mb_section);
+ if (nb_of_1mb_section > 1) {
+ /* WSM buffer with size greater than 1Mb
+ * is available for open session command
+ * from the Daemon only
+ */
+ if (!is_daemon(instance)) {
+ MCDRV_DBG_ERROR(mcd, "caller not MobiCore Daemon");
+ return -EPERM;
+ }
+ MCDRV_DBG_VERBOSE(mcd, "allocate %d L2 table",
+ nb_of_1mb_section);
+ mmu_table = (uint64_t *)get_zeroed_page(GFP_KERNEL);
+ MCDRV_DBG_VERBOSE(mcd, "mmu_table = 0x%p", mmu_table);
+ if (mmu_table == NULL) {
+ MCDRV_DBG_ERROR(mcd,
+ "fake L1 table alloc. failed");
+ return -ENOMEM;
+ }
+ }
+
+ if (!mc_find_cont_wsm_addr(instance, buffer, &kbuff, len)) {
+ buffer = kbuff;
+ task = NULL;
+ }
+
+ /* This array is used to free mmu tables in case of any error */
+ handles = kmalloc(sizeof(unsigned int)*nb_of_1mb_section,
+ GFP_KERNEL | __GFP_ZERO);
+ if (handles == NULL) {
+ MCDRV_DBG_ERROR(mcd, "auxiliary handles array alloc. failed");
+ ret = -ENOMEM;
+ goto err;
+ }
+ /* Each L1 record refers 1MB piece of TA blob
+ * for both non-LPAE and LPAE modes
+ */
+
+ tmp_len = (len + offset > SZ_1M) ? (SZ_1M - offset) : len;
+ for (index = 0; index < nb_of_1mb_section; index++) {
+ table = mc_alloc_mmu_table(instance, task, buffer, tmp_len, 0);
+
+ if (IS_ERR(table)) {
+ MCDRV_DBG_ERROR(mcd, "mc_alloc_mmu_table() failed");
+ ret = -EINVAL;
+ goto err;
+ }
+ handles[index] = table->handle;
+
+ if (mmu_table != NULL) {
+ MCDRV_DBG_VERBOSE(mcd, "fake L1 %p add L2 descr 0x%llX",
+ mmu_table + index,
+ (u64)table->phys);
+ mmu_table[index] = table->phys;
+ }
+
+ buffer += tmp_len;
+ len -= tmp_len;
+ tmp_len = (len > SZ_1M) ? SZ_1M : len;
+ }
+ if (mmu_table != NULL) {
+ MCDRV_DBG_VERBOSE(mcd, "fake L1 buffer: %p, len=%zu",
+ mmu_table,
+ nb_of_1mb_section*sizeof(uint64_t));
+
+ table = mc_alloc_mmu_table(
+ instance,
+ NULL,
+ mmu_table,
+ nb_of_1mb_section*sizeof(uint64_t),
+ MC_MMU_TABLE_TYPE_WSM_FAKE_L1);
+ if (IS_ERR(table)) {
+ MCDRV_DBG_ERROR(mcd, "mc_alloc_mmu_table() failed");
+ ret = -EINVAL;
+ goto err;
+ }
+ }
+
+ /* set response */
+ *handle = table->handle;
+ /* WARNING: daemon shouldn't know this either, but live with it */
+ if (is_daemon(instance))
+ *phys = table->phys;
+ else
+ *phys = 0;
+
+ MCDRV_DBG_VERBOSE(mcd, "handle: %d, phys=0x%llX",
+ *handle, (u64)(*phys));
+
+ MCDRV_DBG_VERBOSE(mcd, "exit with %d/0x%08X", ret, ret);
+
+ kfree(handles);
+
+ return ret;
+
+err:
+ if (handles != NULL) {
+ for (index = 0; index < nb_of_1mb_section; index++)
+ mc_free_mmu_table(instance, handles[index]);
+ kfree(handles);
+ }
+ free_page((unsigned long)mmu_table);
+ return ret;
+}
+
+int mc_unregister_wsm_mmu(struct mc_instance *instance, uint32_t handle)
+{
+ int ret = 0;
+
+ if (WARN(!instance, "No instance data available"))
+ return -EFAULT;
+
+ /* free table (if no further locks exist) */
+ mc_free_mmu_table(instance, handle);
+
+ return ret;
+}
+/* Lock the object from handle, it could be a WSM MMU table or a cont buffer! */
+static int mc_lock_handle(struct mc_instance *instance, uint32_t handle)
+{
+ int ret = 0;
+
+ if (WARN(!instance, "No instance data available"))
+ return -EFAULT;
+
+ if (WARN_ON(!is_daemon(instance))) {
+ MCDRV_DBG_ERROR(mcd, "caller not MobiCore Daemon");
+ return -EPERM;
+ }
+
+ mutex_lock(&instance->lock);
+ ret = mc_lock_mmu_table(instance, handle);
+
+ /* Handle was not a MMU table but a cont buffer */
+ if (ret == -EINVAL) {
+ /* Call the non locking variant! */
+ ret = __lock_buffer(instance, handle);
+ }
+
+ mutex_unlock(&instance->lock);
+
+ return ret;
+}
+
+static int mc_unlock_handle(struct mc_instance *instance, uint32_t handle)
+{
+ int ret = 0;
+
+ if (WARN(!instance, "No instance data available"))
+ return -EFAULT;
+
+ if (WARN_ON(!is_daemon(instance))) {
+ MCDRV_DBG_ERROR(mcd, "caller not MobiCore Daemon");
+ return -EPERM;
+ }
+
+ mutex_lock(&instance->lock);
+ ret = mc_free_mmu_table(instance, handle);
+
+ /* Not a MMU table, then it must be a buffer */
+ if (ret == -EINVAL) {
+ /* Call the non locking variant! */
+ ret = __free_buffer(instance, handle, true);
+ }
+ mutex_unlock(&instance->lock);
+
+ return ret;
+}
+
+static phys_addr_t mc_find_wsm_mmu(struct mc_instance *instance,
+ uint32_t handle, int32_t fd)
+{
+ if (WARN(!instance, "No instance data available"))
+ return 0;
+
+ if (WARN_ON(!is_daemon(instance))) {
+ MCDRV_DBG_ERROR(mcd, "caller not MobiCore Daemon");
+ return 0;
+ }
+
+ return mc_find_mmu_table(handle, fd);
+}
+
+static int mc_clean_wsm_mmu(struct mc_instance *instance)
+{
+ if (WARN(!instance, "No instance data available"))
+ return -EFAULT;
+
+ if (WARN_ON(!is_daemon(instance))) {
+ MCDRV_DBG_ERROR(mcd, "caller not MobiCore Daemon");
+ return -EPERM;
+ }
+
+ mc_clean_mmu_tables();
+
+ return 0;
+}
+
+static int mc_fd_mmap(struct file *file, struct vm_area_struct *vmarea)
+{
+ struct mc_instance *instance = get_instance(file);
+ unsigned long len = vmarea->vm_end - vmarea->vm_start;
+ uint32_t handle = vmarea->vm_pgoff;
+ struct mc_buffer *buffer = 0;
+ int ret = 0;
+
+ MCDRV_DBG_VERBOSE(mcd, "start=0x%p, size=%ld, offset=%ld, mci=0x%llX",
+ (void *)vmarea->vm_start, len, vmarea->vm_pgoff,
+ (u64)ctx.mci_base.phys);
+
+ if (WARN(!instance, "No instance data available"))
+ return -EFAULT;
+
+ if (len == 0) {
+ MCDRV_DBG_ERROR(mcd, "cannot allocate size 0");
+ return -ENOMEM;
+ }
+ if (handle) {
+ mutex_lock(&ctx.cont_bufs_lock);
+
+ /* search for the buffer list. */
+ list_for_each_entry(buffer, &ctx.cont_bufs, list) {
+ /* Only allow mapping if the client owns it!*/
+ if (buffer->handle == handle &&
+ buffer->instance == instance) {
+ /* We shouldn't do remap with larger size */
+ if (buffer->len > len)
+ break;
+ /* We can't allow mapping the buffer twice */
+ if (!buffer->uaddr)
+ goto found;
+ else
+ break;
+ }
+ }
+ /* Nothing found return */
+ mutex_unlock(&ctx.cont_bufs_lock);
+ MCDRV_DBG_ERROR(mcd, "handle not found");
+ return -EINVAL;
+
+found:
+ buffer->uaddr = (void *)vmarea->vm_start;
+ vmarea->vm_flags |= VM_IO;
+ /*
+ * Convert kernel address to user address. Kernel address begins
+ * at PAGE_OFFSET, user address range is below PAGE_OFFSET.
+ * Remapping the area is always done, so multiple mappings
+ * of one region are possible. Now remap kernel address
+ * space into user space
+ */
+ ret = (int)remap_pfn_range(vmarea, vmarea->vm_start,
+ page_to_pfn(virt_to_page(buffer->addr)),
+ buffer->len, vmarea->vm_page_prot);
+ /* If the remap failed then don't mark this buffer as marked
+ * since the unmaping will also fail */
+ if (ret)
+ buffer->uaddr = NULL;
+ mutex_unlock(&ctx.cont_bufs_lock);
+ } else {
+ if (!is_daemon(instance))
+ return -EPERM;
+
+ if (!ctx.mci_base.addr)
+ return -EFAULT;
+
+ vmarea->vm_flags |= VM_IO;
+ /* Convert kernel address to user address. Kernel address begins
+ * at PAGE_OFFSET, user address range is below PAGE_OFFSET.
+ * Remapping the area is always done, so multiple mappings
+ * of one region are possible. Now remap kernel address
+ * space into user space */
+ ret = (int)remap_pfn_range(vmarea, vmarea->vm_start,
+ page_to_pfn(virt_to_page(ctx.mci_base.addr)),
+ len, vmarea->vm_page_prot);
+ }
+
+ MCDRV_DBG_VERBOSE(mcd, "exit with %d/0x%08X", ret, ret);
+
+ return ret;
+}
+
+static inline int ioctl_check_pointer(unsigned int cmd, int __user *uarg)
+{
+ int err = 0;
+ if (_IOC_DIR(cmd) & _IOC_READ)
+ err = !access_ok(VERIFY_WRITE, uarg, _IOC_SIZE(cmd));
+ else if (_IOC_DIR(cmd) & _IOC_WRITE)
+ err = !access_ok(VERIFY_READ, uarg, _IOC_SIZE(cmd));
+ if (err)
+ return -EFAULT;
+
+ return 0;
+}
+
+/*
+ * mc_fd_user_ioctl() - Will be called from user space as ioctl(..)
+ * @file pointer to file
+ * @cmd command
+ * @arg arguments
+ *
+ * Returns 0 for OK and an errno in case of error
+ */
+static long mc_fd_user_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct mc_instance *instance = get_instance(file);
+ int __user *uarg = (int __user *)arg;
+ int ret = -EINVAL;
+
+ if (WARN(!instance, "No instance data available"))
+ return -EFAULT;
+
+ if (ioctl_check_pointer(cmd, uarg))
+ return -EFAULT;
+
+ switch (cmd) {
+ case MC_IO_FREE:
+ ret = mc_free_buffer(instance, (uint32_t)arg);
+ break;
+
+ /* 32/64 bit interface compatiblity notice:
+ * mc_ioctl_reg_wsm has been defined with the buffer parameter
+ * as void* which means that the size and layout of the structure
+ * are different between 32 and 64 bit variants.
+ * However our 64 bit Linux driver must be able to service both
+ * 32 and 64 bit clients so we have to allow both IOCTLs. Though
+ * we have a bit of copy paste code we provide maximum backwards
+ * compatiblity */
+ case MC_IO_REG_WSM:{
+ struct mc_ioctl_reg_wsm reg;
+ phys_addr_t phys = 0;
+ if (copy_from_user(&reg, uarg, sizeof(reg)))
+ return -EFAULT;
+
+ ret = mc_register_wsm_mmu(instance,
+ (void *)(uintptr_t)reg.buffer,
+ reg.len, &reg.handle, &phys);
+ reg.table_phys = phys;
+
+ if (!ret) {
+ if (copy_to_user(uarg, &reg, sizeof(reg))) {
+ ret = -EFAULT;
+ mc_unregister_wsm_mmu(instance, reg.handle);
+ }
+ }
+ break;
+ }
+ case MC_COMPAT_REG_WSM:{
+ struct mc_compat_ioctl_reg_wsm reg;
+ phys_addr_t phys = 0;
+ if (copy_from_user(&reg, uarg, sizeof(reg)))
+ return -EFAULT;
+
+ ret = mc_register_wsm_mmu(instance,
+ (void *)(uintptr_t)reg.buffer,
+ reg.len, &reg.handle, &phys);
+ reg.table_phys = phys;
+
+ if (!ret) {
+ if (copy_to_user(uarg, &reg, sizeof(reg))) {
+ ret = -EFAULT;
+ mc_unregister_wsm_mmu(instance, reg.handle);
+ }
+ }
+ break;
+ }
+ case MC_IO_UNREG_WSM:
+ ret = mc_unregister_wsm_mmu(instance, (uint32_t)arg);
+ break;
+
+ case MC_IO_VERSION:
+ ret = put_user(mc_get_version(), uarg);
+ if (ret)
+ MCDRV_DBG_ERROR(mcd,
+ "IOCTL_GET_VERSION failed to put data");
+ break;
+
+ case MC_IO_MAP_WSM:{
+ struct mc_ioctl_map map;
+ struct mc_buffer *buffer = 0;
+ if (copy_from_user(&map, uarg, sizeof(map)))
+ return -EFAULT;
+
+ /* Setup the WSM buffer structure! */
+ if (mc_get_buffer(instance, &buffer, map.len))
+ return -EFAULT;
+
+ map.handle = buffer->handle;
+ /* Trick: to keep the same interface with the user space, store
+ the handle in the physical address.
+ It is given back with the offset when mmap() is called. */
+ map.phys_addr = buffer->handle << PAGE_SHIFT;
+ map.reused = 0;
+ if (copy_to_user(uarg, &map, sizeof(map)))
+ ret = -EFAULT;
+ else
+ ret = 0;
+ break;
+ }
+ default:
+ MCDRV_DBG_ERROR(mcd, "unsupported cmd=0x%x", cmd);
+ ret = -ENOIOCTLCMD;
+ break;
+
+ } /* end switch(cmd) */
+
+#ifdef MC_MEM_TRACES
+ mobicore_log_read();
+#endif
+
+ return (int)ret;
+}
+
+static long mc_fd_admin_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct mc_instance *instance = get_instance(file);
+ int __user *uarg = (int __user *)arg;
+ int ret = -EINVAL;
+
+ if (WARN(!instance, "No instance data available"))
+ return -EFAULT;
+
+ if (WARN_ON(!is_daemon(instance))) {
+ MCDRV_DBG_ERROR(mcd, "caller not MobiCore Daemon");
+ return -EPERM;
+ }
+
+ if (ioctl_check_pointer(cmd, uarg))
+ return -EFAULT;
+
+ switch (cmd) {
+ case MC_IO_INIT: {
+ struct mc_ioctl_init init;
+ ctx.mcp = NULL;
+ if (!ctx.mci_base.phys) {
+ MCDRV_DBG_ERROR(mcd,
+ "Cannot init MobiCore without MCI!");
+ return -EINVAL;
+ }
+ if (copy_from_user(&init, uarg, sizeof(init)))
+ return -EFAULT;
+
+ ctx.mcp = ctx.mci_base.addr + init.mcp_offset;
+ ret = mc_init(ctx.mci_base.phys, init.nq_length,
+ init.mcp_offset, init.mcp_length);
+ break;
+ }
+ case MC_IO_INFO: {
+ struct mc_ioctl_info info;
+ if (copy_from_user(&info, uarg, sizeof(info)))
+ return -EFAULT;
+
+ ret = mc_info(info.ext_info_id, &info.state,
+ &info.ext_info);
+
+ if (!ret) {
+ if (copy_to_user(uarg, &info, sizeof(info)))
+ ret = -EFAULT;
+ }
+ break;
+ }
+ case MC_IO_YIELD:
+ ret = mc_yield();
+ break;
+
+ case MC_IO_NSIQ:
+ ret = mc_nsiq();
+ break;
+
+ case MC_IO_LOCK_WSM: {
+ ret = mc_lock_handle(instance, (uint32_t)arg);
+ break;
+ }
+ case MC_IO_UNLOCK_WSM:
+ ret = mc_unlock_handle(instance, (uint32_t)arg);
+ break;
+ case MC_IO_CLEAN_WSM:
+ ret = mc_clean_wsm_mmu(instance);
+ break;
+ case MC_IO_RESOLVE_WSM: {
+ phys_addr_t phys;
+ struct mc_ioctl_resolv_wsm wsm;
+ if (copy_from_user(&wsm, uarg, sizeof(wsm)))
+ return -EFAULT;
+ phys = mc_find_wsm_mmu(instance, wsm.handle, wsm.fd);
+ if (!phys)
+ return -EINVAL;
+
+ wsm.phys = phys;
+ if (copy_to_user(uarg, &wsm, sizeof(wsm)))
+ return -EFAULT;
+ ret = 0;
+ break;
+ }
+ case MC_IO_RESOLVE_CONT_WSM: {
+ struct mc_ioctl_resolv_cont_wsm cont_wsm;
+ phys_addr_t phys = 0;
+ uint32_t len = 0;
+ if (copy_from_user(&cont_wsm, uarg, sizeof(cont_wsm)))
+ return -EFAULT;
+ ret = mc_find_cont_wsm(instance, cont_wsm.handle, cont_wsm.fd,
+ &phys, &len);
+ if (!ret) {
+ cont_wsm.phys = phys;
+ cont_wsm.length = len;
+ if (copy_to_user(uarg, &cont_wsm, sizeof(cont_wsm)))
+ ret = -EFAULT;
+ }
+ break;
+ }
+ case MC_IO_MAP_MCI:{
+ struct mc_ioctl_map map;
+ phys_addr_t phys_addr;
+ if (copy_from_user(&map, uarg, sizeof(map)))
+ return -EFAULT;
+
+ map.reused = (ctx.mci_base.phys != 0);
+ phys_addr = get_mci_base_phys(map.len);
+ if (!phys_addr) {
+ MCDRV_DBG_ERROR(mcd, "Failed to setup MCI buffer!");
+ return -EFAULT;
+ }
+ map.phys_addr = 0;
+ if (copy_to_user(uarg, &map, sizeof(map)))
+ ret = -EFAULT;
+ ret = 0;
+ break;
+ }
+ case MC_IO_LOG_SETUP: {
+#ifdef MC_MEM_TRACES
+ ret = mobicore_log_setup();
+#endif
+ break;
+ }
+
+ /* The rest is handled commonly by user IOCTL */
+ default:
+ ret = mc_fd_user_ioctl(file, cmd, arg);
+ } /* end switch(cmd) */
+
+#ifdef MC_MEM_TRACES
+ mobicore_log_read();
+#endif
+
+ return (int)ret;
+}
+
+/*
+ * mc_fd_read() - This will be called from user space as read(...)
+ * @file: file pointer
+ * @buffer: buffer where to copy to(userspace)
+ * @buffer_len: number of requested data
+ * @pos: not used
+ *
+ * The read function is blocking until a interrupt occurs. In that case the
+ * event counter is copied into user space and the function is finished.
+ *
+ * If OK this function returns the number of copied data otherwise it returns
+ * errno
+ */
+static ssize_t mc_fd_read(struct file *file, char *buffer, size_t buffer_len,
+ loff_t *pos)
+{
+ int ret = 0, ssiq_counter;
+ struct mc_instance *instance = get_instance(file);
+
+ if (WARN(!instance, "No instance data available"))
+ return -EFAULT;
+
+ /* avoid debug output on non-error, because this is call quite often */
+ MCDRV_DBG_VERBOSE(mcd, "enter");
+
+ /* only the MobiCore Daemon is allowed to call this function */
+ if (WARN_ON(!is_daemon(instance))) {
+ MCDRV_DBG_ERROR(mcd, "caller not MobiCore Daemon");
+ return -EPERM;
+ }
+
+ if (buffer_len < sizeof(unsigned int)) {
+ MCDRV_DBG_ERROR(mcd, "invalid length");
+ return -EINVAL;
+ }
+
+ for (;;) {
+ if (wait_for_completion_interruptible(&ctx.isr_comp)) {
+ MCDRV_DBG_VERBOSE(mcd, "read interrupted");
+ return -ERESTARTSYS;
+ }
+
+ ssiq_counter = atomic_read(&ctx.isr_counter);
+ MCDRV_DBG_VERBOSE(mcd, "ssiq_counter=%i, ctx.counter=%i",
+ ssiq_counter, ctx.evt_counter);
+
+ if (ssiq_counter != ctx.evt_counter) {
+ /* read data and exit loop without error */
+ ctx.evt_counter = ssiq_counter;
+ ret = 0;
+ break;
+ }
+
+ /* end loop if non-blocking */
+ if (file->f_flags & O_NONBLOCK) {
+ MCDRV_DBG_ERROR(mcd, "non-blocking read");
+ return -EAGAIN;
+ }
+
+ if (signal_pending(current)) {
+ MCDRV_DBG_VERBOSE(mcd, "received signal.");
+ return -ERESTARTSYS;
+ }
+ }
+
+ /* read data and exit loop */
+ ret = copy_to_user(buffer, &ctx.evt_counter, sizeof(unsigned int));
+
+ if (ret != 0) {
+ MCDRV_DBG_ERROR(mcd, "copy_to_user failed");
+ return -EFAULT;
+ }
+
+ ret = sizeof(unsigned int);
+
+ return (ssize_t)ret;
+}
+
+/*
+ * Initialize a new mobicore API instance object
+ *
+ * @return Instance or NULL if no allocation was possible.
+ */
+struct mc_instance *mc_alloc_instance(void)
+{
+ struct mc_instance *instance;
+
+ instance = kzalloc(sizeof(*instance), GFP_KERNEL);
+ if (instance == NULL)
+ return NULL;
+
+ /* get a unique ID for this instance (PIDs are not unique) */
+ instance->handle = atomic_inc_return(&ctx.instance_counter);
+
+ mutex_init(&instance->lock);
+
+ return instance;
+}
+
+#if defined(TBASE_CORE_SWITCHER) && defined(DEBUG)
+static ssize_t mc_fd_write(struct file *file, const char __user *buffer,
+ size_t buffer_len, loff_t *x)
+{
+ uint32_t cpu_new;
+ /* we only consider one digit */
+ char buf[2];
+ struct mc_instance *instance = get_instance(file);
+
+ if (WARN(!instance, "No instance data available"))
+ return -EFAULT;
+
+ /* Invalid data, nothing to do */
+ if (buffer_len < 1)
+ return -EINVAL;
+
+ /* Invalid data, nothing to do */
+ if (copy_from_user(buf, buffer, min(sizeof(buf), buffer_len)))
+ return -EFAULT;
+
+ if (buf[0] == 'n') {
+ mc_nsiq();
+ /* If it's a digit then switch cores */
+ } else if ((buf[0] >= '0') && (buf[0] <= '9')) {
+ cpu_new = buf[0] - '0';
+ if (cpu_new <= 8) {
+ MCDRV_DBG_VERBOSE(mcd, "Set Active Cpu: %d\n", cpu_new);
+ mc_switch_core(cpu_new);
+ }
+ } else {
+ return -EINVAL;
+ }
+
+ return buffer_len;
+}
+#endif
+
+/*
+ * Release a mobicore instance object and all objects related to it
+ * @instance: instance
+ * Returns 0 if Ok or -E ERROR
+ */
+int mc_release_instance(struct mc_instance *instance)
+{
+ struct mc_buffer *buffer, *tmp;
+
+ if (WARN(!instance, "No instance data available"))
+ return -EFAULT;
+
+ mutex_lock(&instance->lock);
+ mc_clear_mmu_tables(instance);
+
+ mutex_lock(&ctx.cont_bufs_lock);
+ /* release all mapped data */
+
+ /* Check if some buffers are orphaned. */
+ list_for_each_entry_safe(buffer, tmp, &ctx.cont_bufs, list) {
+ /* It's safe here to only call free_buffer() without unmapping
+ * because mmap() takes a refcount to the file's fd so only
+ * time we end up here is when everything has been unmapped or
+ * the process called exit() */
+ if (buffer->instance == instance) {
+ buffer->instance = NULL;
+ free_buffer(buffer);
+ }
+ }
+ mutex_unlock(&ctx.cont_bufs_lock);
+
+ mutex_unlock(&instance->lock);
+
+ /* release instance context */
+ kfree(instance);
+
+ return 0;
+}
+
+/*
+ * mc_fd_user_open() - Will be called from user space as fd = open(...)
+ * A set of internal instance data are created and initialized.
+ *
+ * @inode
+ * @file
+ * Returns 0 if OK or -ENOMEM if no allocation was possible.
+ */
+static int mc_fd_user_open(struct inode *inode, struct file *file)
+{
+ struct mc_instance *instance;
+
+ MCDRV_DBG_VERBOSE(mcd, "enter");
+
+ instance = mc_alloc_instance();
+ if (instance == NULL)
+ return -ENOMEM;
+
+ /* store instance data reference */
+ file->private_data = instance;
+
+ return 0;
+}
+
+static int mc_fd_admin_open(struct inode *inode, struct file *file)
+{
+ struct mc_instance *instance;
+
+ /*
+ * The daemon is already set so we can't allow anybody else to open
+ * the admin interface.
+ */
+ if (ctx.daemon_inst) {
+ MCDRV_DBG_ERROR(mcd, "Daemon is already connected");
+ return -EPERM;
+ }
+ /* Setup the usual variables */
+ if (mc_fd_user_open(inode, file))
+ return -ENOMEM;
+ instance = get_instance(file);
+
+ MCDRV_DBG(mcd, "accept this as MobiCore Daemon");
+
+ ctx.daemon_inst = instance;
+ ctx.daemon = current;
+ instance->admin = true;
+ init_completion(&ctx.isr_comp);
+ /* init ssiq event counter */
+ ctx.evt_counter = atomic_read(&(ctx.isr_counter));
+
+ return 0;
+}
+
+/*
+ * mc_fd_release() - This function will be called from user space as close(...)
+ * The instance data are freed and the associated memory pages are unreserved.
+ *
+ * @inode
+ * @file
+ *
+ * Returns 0
+ */
+static int mc_fd_release(struct inode *inode, struct file *file)
+{
+ int ret = 0;
+ struct mc_instance *instance = get_instance(file);
+
+ MCDRV_DBG_VERBOSE(mcd, "enter");
+
+ if (WARN(!instance, "No instance data available"))
+ return -EFAULT;
+
+ /* check if daemon closes us. */
+ if (is_daemon(instance)) {
+ MCDRV_DBG_WARN(mcd, "MobiCore Daemon died");
+ ctx.daemon_inst = NULL;
+ ctx.daemon = NULL;
+ }
+
+ ret = mc_release_instance(instance);
+
+ /*
+ * ret is quite irrelevant here as most apps don't care about the
+ * return value from close() and it's quite difficult to recover
+ */
+ MCDRV_DBG_VERBOSE(mcd, "exit with %d/0x%08X", ret, ret);
+
+ return (int)ret;
+}
+
+/*
+ * This function represents the interrupt function of the mcDrvModule.
+ * It signals by incrementing of an event counter and the start of the read
+ * waiting queue, the read function a interrupt has occurred.
+ */
+static irqreturn_t mc_ssiq_isr(int intr, void *context)
+{
+ /* increment interrupt event counter */
+ atomic_inc(&(ctx.isr_counter));
+
+ /* signal the daemon */
+ complete(&ctx.isr_comp);
+#ifdef MC_MEM_TRACES
+ mobicore_log_read();
+#endif
+ return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_MT_TRUSTONIC_TEE_DEBUGFS
+uint8_t trustonic_swd_debug;
+static ssize_t debugfs_read(struct file *filep, char __user *buf, size_t len, loff_t *ppos)
+{
+ char mybuf[2];
+
+ if (*ppos != 0)
+ return 0;
+ mybuf[0] = trustonic_swd_debug + '0';
+ mybuf[1] = '\n';
+ if (copy_to_user(buf, mybuf + *ppos, 2))
+ return -EFAULT;
+ *ppos = 2;
+ return 2;
+}
+
+static ssize_t debugfs_write(struct file *filep, const char __user *buf, size_t len, loff_t *ppos)
+{
+ uint8_t val=0;
+
+ if (len >=2) {
+ if (!copy_from_user(&val, &buf[0], 1))
+ if (val >= '0' && val <= '9') {
+ trustonic_swd_debug = val - '0';
+ }
+ return len;
+ }
+
+ return -EFAULT;
+}
+
+const struct file_operations debug_fops = {
+ .read = debugfs_read,
+ .write = debugfs_write
+};
+#endif
+
+/* function table structure of this device driver. */
+static const struct file_operations mc_admin_fops = {
+ .owner = THIS_MODULE,
+ .open = mc_fd_admin_open,
+ .release = mc_fd_release,
+ .unlocked_ioctl = mc_fd_admin_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = mc_fd_admin_ioctl,
+#endif
+ .mmap = mc_fd_mmap,
+ .read = mc_fd_read,
+};
+
+/* function table structure of this device driver. */
+static const struct file_operations mc_user_fops = {
+ .owner = THIS_MODULE,
+ .open = mc_fd_user_open,
+ .release = mc_fd_release,
+ .unlocked_ioctl = mc_fd_user_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = mc_fd_user_ioctl,
+#endif
+ .mmap = mc_fd_mmap,
+#if defined(TBASE_CORE_SWITCHER) && defined(DEBUG)
+ .write = mc_fd_write,
+#endif
+};
+
+static int create_devices(void)
+{
+ int ret = 0;
+
+ cdev_init(&mc_admin_cdev, &mc_admin_fops);
+ cdev_init(&mc_user_cdev, &mc_user_fops);
+
+ mc_device_class = class_create(THIS_MODULE, "mobicore");
+ if (IS_ERR(mc_device_class)) {
+ MCDRV_DBG_ERROR(mcd, "failed to create device class");
+ ret = PTR_ERR(mc_device_class);
+ goto out;
+ }
+
+ ret = alloc_chrdev_region(&mc_dev_admin, 0, MC_DEV_MAX, "mobicore");
+ if (ret < 0) {
+ MCDRV_DBG_ERROR(mcd, "failed to allocate char dev region");
+ goto error;
+ }
+ mc_dev_user = MKDEV(MAJOR(mc_dev_admin), 1);
+
+ MCDRV_DBG_VERBOSE(mcd, "%s: dev %d", "mobicore", MAJOR(mc_dev_admin));
+
+ /* First the ADMIN node */
+ ret = cdev_add(&mc_admin_cdev, mc_dev_admin, 1);
+ if (ret != 0) {
+ MCDRV_DBG_ERROR(mcd, "admin device register failed");
+ goto error;
+ }
+ mc_admin_cdev.owner = THIS_MODULE;
+ device_create(mc_device_class, NULL, mc_dev_admin, NULL,
+ MC_ADMIN_DEVNODE);
+
+ /* Then the user node */
+
+ ret = cdev_add(&mc_user_cdev, mc_dev_user, 1);
+ if (ret != 0) {
+ MCDRV_DBG_ERROR(mcd, "user device register failed");
+ goto error_unregister;
+ }
+ mc_user_cdev.owner = THIS_MODULE;
+ device_create(mc_device_class, NULL, mc_dev_user, NULL,
+ MC_USER_DEVNODE);
+
+ goto out;
+error_unregister:
+ device_destroy(mc_device_class, mc_dev_admin);
+ device_destroy(mc_device_class, mc_dev_user);
+
+ cdev_del(&mc_admin_cdev);
+ cdev_del(&mc_user_cdev);
+ unregister_chrdev_region(mc_dev_admin, MC_DEV_MAX);
+error:
+ class_destroy(mc_device_class);
+out:
+ return ret;
+}
+
+/*
+ * This function is called the kernel during startup or by a insmod command.
+ * This device is installed and registered as cdev, then interrupt and
+ * queue handling is set up
+ */
+static unsigned int mobicore_irq_id = MC_INTR_SSIQ;
+static int __init mobicore_init(void)
+{
+ int ret = 0;
+ dev_set_name(mcd, "mcd");
+#ifdef CONFIG_MT_TRUSTONIC_TEE_DEBUGFS
+ struct dentry *debug_root;
+#endif
+#ifdef CONFIG_OF
+ struct device_node *node;
+#if 0
+ unsigned int irq_info[3] = {0, 0, 0};
+#endif
+#endif
+
+ /* Do not remove or change the following trace.
+ * The string "MobiCore" is used to detect if <t-base is in of the image
+ */
+ dev_info(mcd, "MobiCore Driver, Build: " "\n");
+ dev_info(mcd, "MobiCore mcDrvModuleApi version is %i.%i\n",
+ MCDRVMODULEAPI_VERSION_MAJOR,
+ MCDRVMODULEAPI_VERSION_MINOR);
+#ifdef MOBICORE_COMPONENT_BUILD_TAG
+ dev_info(mcd, "MobiCore %s\n", MOBICORE_COMPONENT_BUILD_TAG);
+#endif
+ /* Hardware does not support ARM TrustZone -> Cannot continue! */
+ if (!has_security_extensions()) {
+ MCDRV_DBG_ERROR(mcd,
+ "Hardware doesn't support ARM TrustZone!");
+ return -ENODEV;
+ }
+
+ /* Running in secure mode -> Cannot load the driver! */
+ if (is_secure_mode()) {
+ MCDRV_DBG_ERROR(mcd, "Running in secure MODE!");
+ return -ENODEV;
+ }
+
+ ret = mc_fastcall_init(&ctx);
+ if (ret)
+ goto error;
+
+ init_completion(&ctx.isr_comp);
+
+ /* initialize event counter for signaling of an IRQ to zero */
+ atomic_set(&ctx.isr_counter, 0);
+
+#ifdef CONFIG_OF
+ node = of_find_compatible_node(NULL, NULL, "trustonic,mobicore");
+#if 0
+ if (of_property_read_u32_array(node, "interrupts", irq_info, ARRAY_SIZE(irq_info))) {
+ MCDRV_DBG_ERROR(mcd,
+ "Fail to get SSIQ id from device tree!");
+ return -ENODEV;
+ }
+ mobicore_irq_id = irq_info[1];
+#else
+ mobicore_irq_id = irq_of_parse_and_map(node, 0);
+#endif
+ MCDRV_DBG_VERBOSE(mcd, "Interrupt from device tree is %d\n", mobicore_irq_id);
+#endif
+
+ /* set up S-SIQ interrupt handler ************************/
+ ret = request_irq(mobicore_irq_id, mc_ssiq_isr, IRQF_TRIGGER_RISING,
+ MC_ADMIN_DEVNODE, &ctx);
+ if (ret != 0) {
+ MCDRV_DBG_ERROR(mcd, "interrupt request failed");
+ goto err_req_irq;
+ }
+
+#ifdef MC_PM_RUNTIME
+ ret = mc_pm_initialize(&ctx);
+ if (ret != 0) {
+ MCDRV_DBG_ERROR(mcd, "Power Management init failed!");
+ goto free_isr;
+ }
+#endif
+
+ ret = create_devices();
+ if (ret != 0)
+ goto free_pm;
+
+ ret = mc_init_mmu_tables();
+
+#ifdef MC_CRYPTO_CLOCK_MANAGEMENT
+ ret = mc_pm_clock_initialize();
+#endif
+
+ /*
+ * initialize unique number counters which we can use for
+ * handles. We start with 1 instead of 0.
+ */
+ atomic_set(&ctx.handle_counter, 1);
+ atomic_set(&ctx.instance_counter, 1);
+
+ /* init list for contiguous buffers */
+ INIT_LIST_HEAD(&ctx.cont_bufs);
+
+ /* init lock for the buffers list */
+ mutex_init(&ctx.cont_bufs_lock);
+
+ memset(&ctx.mci_base, 0, sizeof(ctx.mci_base));
+ MCDRV_DBG(mcd, "initialized");
+#ifdef CONFIG_MT_TRUSTONIC_TEE_DEBUGFS
+ debug_root = debugfs_create_dir("trustonic", NULL);
+ if (debug_root) {
+ if (!debugfs_create_file("swd_debug", 0644, debug_root, NULL, &debug_fops)) {
+ MCDRV_DBG_ERROR(mcd, "Create trustonic debugfs swd_debug failed!");
+ }
+ } else {
+ MCDRV_DBG_ERROR(mcd, "Create trustonic debugfs directory failed!");
+ }
+#endif
+ return 0;
+
+free_pm:
+#ifdef MC_PM_RUNTIME
+ mc_pm_free();
+free_isr:
+#endif
+ free_irq(MC_INTR_SSIQ, &ctx);
+err_req_irq:
+ mc_fastcall_destroy();
+error:
+ return ret;
+}
+
+/*
+ * This function removes this device driver from the Linux device manager .
+ */
+static void __exit mobicore_exit(void)
+{
+ MCDRV_DBG_VERBOSE(mcd, "enter");
+#ifdef MC_MEM_TRACES
+ mobicore_log_free();
+#endif
+
+ mc_release_mmu_tables();
+
+#ifdef MC_PM_RUNTIME
+ mc_pm_free();
+#endif
+
+ device_destroy(mc_device_class, mc_dev_admin);
+ device_destroy(mc_device_class, mc_dev_user);
+ class_destroy(mc_device_class);
+ unregister_chrdev_region(mc_dev_admin, MC_DEV_MAX);
+
+ free_irq(mobicore_irq_id, &ctx);
+
+ mc_fastcall_destroy();
+
+#ifdef MC_CRYPTO_CLOCK_MANAGEMENT
+ mc_pm_clock_finalize();
+#endif
+
+ MCDRV_DBG_VERBOSE(mcd, "exit");
+}
+
+bool mc_sleep_ready(void)
+{
+#ifdef MC_PM_RUNTIME
+ return mc_pm_sleep_ready();
+#else
+ return true;
+#endif
+}
+
+/* Linux Driver Module Macros */
+module_init(mobicore_init);
+module_exit(mobicore_exit);
+MODULE_AUTHOR("Trustonic Limited");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MobiCore driver");
diff --git a/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/main.h b/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/main.h
new file mode 100644
index 000000000..23c8fea16
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/main.h
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2013-2015 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+#ifndef _MC_MAIN_H_
+#define _MC_MAIN_H_
+
+#include <asm/pgtable.h>
+#include <linux/semaphore.h>
+#include <linux/completion.h>
+#include <linux/mutex.h>
+
+#include "public/mc_linux.h"
+/* Platform specific settings */
+#include "platform.h"
+
+#define MC_VERSION(major, minor) \
+ (((major & 0x0000ffff) << 16) | (minor & 0x0000ffff))
+
+/* Instance data for MobiCore Daemon and TLCs. */
+struct mc_instance {
+ /* lock for the instance */
+ struct mutex lock;
+ /* unique handle */
+ unsigned int handle;
+ bool admin;
+};
+
+/*
+ * Contiguous buffer allocated to TLCs.
+ * These buffers are uses as world shared memory (wsm) and shared with
+ * secure world.
+ * The virtual kernel address is added for a simpler search algorithm.
+ */
+struct mc_buffer {
+ struct list_head list;
+ /* unique handle */
+ unsigned int handle;
+ /* Number of references kept to this buffer */
+ atomic_t usage;
+ /* virtual Kernel start address */
+ void *addr;
+ /* virtual Userspace start address */
+ void *uaddr;
+ /* physical start address */
+ phys_addr_t phys;
+ /* order of number of pages */
+ unsigned int order;
+ uint32_t len;
+ struct mc_instance *instance;
+};
+
+/* MobiCore Driver Kernel Module context data. */
+struct mc_context {
+ /* MobiCore MCI information */
+ struct mc_buffer mci_base;
+ /* MobiCore MCP buffer */
+ struct mc_mcp_buffer *mcp;
+ /* event completion */
+ struct completion isr_comp;
+ /* isr event counter */
+ unsigned int evt_counter;
+ atomic_t isr_counter;
+ /* ever incrementing counters */
+ atomic_t handle_counter;
+ atomic_t instance_counter;
+ /* pointer to instance of daemon */
+ struct mc_instance *daemon_inst;
+ /* pointer to instance of daemon */
+ struct task_struct *daemon;
+ /* General list of contiguous buffers allocated by the kernel */
+ struct list_head cont_bufs;
+ /* Lock for the list of contiguous buffers */
+ struct mutex cont_bufs_lock;
+};
+
+struct mc_sleep_mode {
+ uint16_t sleep_req;
+ uint16_t ready_to_sleep;
+};
+
+/* MobiCore is idle. No scheduling required. */
+#define SCHEDULE_IDLE 0
+/* MobiCore is non idle, scheduling is required. */
+#define SCHEDULE_NON_IDLE 1
+
+/* MobiCore status flags */
+struct mc_flags {
+ /*
+ * Scheduling hint: if <> SCHEDULE_IDLE, MobiCore should
+ * be scheduled by the NWd
+ */
+ uint32_t schedule;
+ /* State of sleep protocol */
+ struct mc_sleep_mode sleep_mode;
+ /* Reserved for future use: Must not be interpreted */
+ uint32_t rfu[2];
+};
+
+/* MCP buffer structure */
+struct mc_mcp_buffer {
+ /* MobiCore Flags */
+ struct mc_flags flags;
+ uint32_t rfu; /* MCP message buffer - ignore */
+};
+
+/* check if caller is MobiCore Daemon */
+static inline bool is_daemon(struct mc_instance *instance)
+{
+ if (!instance)
+ return false;
+ return instance->admin;
+}
+
+
+/* Initialize a new mobicore API instance object */
+struct mc_instance *mc_alloc_instance(void);
+/* Release a mobicore instance object and all objects related to it */
+int mc_release_instance(struct mc_instance *instance);
+
+/*
+ * mc_register_wsm_mmu() - Create a MMU table from a virtual memory buffer which
+ * can be vmalloc or user space virtual memory
+ */
+int mc_register_wsm_mmu(struct mc_instance *instance,
+ void *buffer, uint32_t len,
+ uint32_t *handle, phys_addr_t *phys);
+/* Unregister the buffer mapped above */
+int mc_unregister_wsm_mmu(struct mc_instance *instance, uint32_t handle);
+
+/* Allocate one mc_buffer of contiguous space */
+int mc_get_buffer(struct mc_instance *instance,
+ struct mc_buffer **buffer, unsigned long len);
+/* Free the buffer allocated above */
+int mc_free_buffer(struct mc_instance *instance, uint32_t handle);
+
+/* Check if the other end of the fd owns instance */
+bool mc_check_owner_fd(struct mc_instance *instance, int32_t fd);
+
+/* Get a unique handle */
+uint32_t mc_get_new_handle(void);
+
+/* Test if sleep is possible */
+bool mc_sleep_ready(void);
+
+#endif /* _MC_MAIN_H_ */
diff --git a/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/mem.c b/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/mem.c
new file mode 100644
index 000000000..d65a91fee
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/mem.c
@@ -0,0 +1,813 @@
+/*
+ * Copyright (c) 2013-2015 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+/*
+ * MobiCore Driver Kernel Module.
+ *
+ * This module is written as a Linux device driver.
+ * This driver represents the command proxy on the lowest layer, from the
+ * secure world to the non secure world, and vice versa.
+ * This driver is located in the non secure world (Linux).
+ * This driver offers IOCTL commands, for access to the secure world, and has
+ * the interface from the secure world to the normal world.
+ * The access to the driver is possible with a file descriptor,
+ * which has to be created by the fd = open(/dev/mobicore) command.
+ */
+#include "main.h"
+#include "debug.h"
+#include "mem.h"
+
+#include <linux/highmem.h>
+#include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/pagemap.h>
+#include <linux/device.h>
+
+#ifdef LPAE_SUPPORT
+#define MMU_TYPE_PAGE (3 << 0)
+#define MMU_BUFFERABLE (1 << 2) /* AttrIndx[0] */
+#define MMU_CACHEABLE (1 << 3) /* AttrIndx[1] */
+#define MMU_NS (1 << 5)
+#define MMU_AP_RW_ALL (1 << 6) /* AP[2:1], RW, at any privilege level */
+#define MMU_EXT_SHARED (3 << 8) /* SH[1:0], inner shareable */
+#define MMU_EXT_AF (1 << 10) /* Access Flag */
+#define MMU_EXT_NG (1 << 11)
+#define MMU_EXT_XN (((uint64_t)1) << 54) /* XN */
+#define MMU_PHYS_MASK (((1ULL << 40) - 1) & ~((1ULL << 12) - 1))
+#else
+#define MMU_TYPE_EXT (3 << 0) /* v5 */
+#define MMU_TYPE_SMALL (2 << 0)
+#define MMU_BUFFERABLE (1 << 2)
+#define MMU_CACHEABLE (1 << 3)
+#define MMU_EXT_AP0 (1 << 4)
+#define MMU_EXT_AP1 (2 << 4)
+#define MMU_EXT_TEX(x) ((x) << 6) /* v5 */
+#define MMU_EXT_SHARED (1 << 10) /* v6 */
+#define MMU_EXT_NG (1 << 11) /* v6 */
+#define MMU_PHYS_MASK 0xFFFFF000
+#endif
+
+/* MobiCore memory context data */
+struct mc_mem_context mem_ctx;
+
+static inline void release_page(struct page *page)
+{
+ set_bit(PG_dirty, &page->flags);
+
+ page_cache_release(page);
+}
+
+static int lock_pages(struct task_struct *task, void *virt_start_page_addr,
+ int pages_no, struct page **pages)
+{
+ int locked_pages;
+
+ /* lock user pages, must hold the mmap_sem to do this. */
+ down_read(&(task->mm->mmap_sem));
+ locked_pages = get_user_pages(
+ task,
+ task->mm,
+ (unsigned long)virt_start_page_addr,
+ pages_no,
+ 1, /* write access */
+ 0,
+ pages,
+ NULL);
+ up_read(&(task->mm->mmap_sem));
+
+ /* check if we could lock all pages. */
+ if (locked_pages != pages_no) {
+ MCDRV_DBG_ERROR(mcd, "get_user_pages() failed, locked_pages=%d",
+ locked_pages);
+ if (locked_pages > 0) {
+ /* release all locked pages. */
+ release_pages(pages, locked_pages, 0);
+ }
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+/* Get kernel pointer to shared MMU table given a per-process reference */
+static void *get_mmu_table_kernel_virt(struct mc_mmu_table *table)
+{
+ if (WARN(!table, "Invalid MMU table"))
+ return NULL;
+
+ if (WARN(!table->set, "Invalid MMU table set"))
+ return NULL;
+
+ if (WARN(!table->set->kernel_virt, "Invalid MMU pointer"))
+ return NULL;
+
+ return &(table->set->kernel_virt->table[table->idx]);
+}
+
+/*
+ * Search the list of used MMU tables and return the one with the handle.
+ * Assumes the table_lock is taken.
+ */
+struct mc_mmu_table *find_mmu_table(unsigned int handle)
+{
+ struct mc_mmu_table *table;
+
+ list_for_each_entry(table, &mem_ctx.mmu_tables, list) {
+ if (table->handle == handle)
+ return table;
+ }
+ return NULL;
+}
+
+/*
+ * Search the list of used MMU tables and return the one with the handle.
+ * Assumes the table_lock is taken.
+ */
+struct mc_mmu_table *find_mmu_table_by_phys_addr(phys_addr_t phys)
+{
+ struct mc_mmu_table *table;
+
+ list_for_each_entry(table, &mem_ctx.mmu_tables, list) {
+ if (table->phys == phys)
+ return table;
+ }
+ return NULL;
+}
+
+
+/*
+ * Allocate a new MMU table store plus MMU_TABLES_PER_PAGE in the MMU free
+ * tables list. Assumes the table_lock is already taken by the caller above.
+ */
+static int alloc_mmu_table_store(void)
+{
+ unsigned long store;
+ struct mc_mmu_tables_set *mmutable_set;
+ struct mc_mmu_table *mmutable, *mmutable2;
+ struct page *page;
+ int ret = 0, i;
+ /* temp list for holding the MMU tables */
+ LIST_HEAD(temp);
+
+ store = get_zeroed_page(GFP_KERNEL);
+ if (!store)
+ return -ENOMEM;
+
+ /*
+ * Actually, locking is not necessary, because kernel
+ * memory is not supposed to get swapped out. But we
+ * play safe....
+ */
+ page = virt_to_page(store);
+ set_bit(PG_reserved, &page->flags);
+
+ /* add all the descriptors to the free descriptors list */
+ mmutable_set = kmalloc(sizeof(*mmutable_set), GFP_KERNEL | __GFP_ZERO);
+ if (mmutable_set == NULL) {
+ ret = -ENOMEM;
+ goto free_store;
+ }
+ /* initialize */
+ mmutable_set->kernel_virt = (void *)store;
+ mmutable_set->page = page;
+ mmutable_set->phys = virt_to_phys((void *)store);
+ /* the set is not yet used */
+ atomic_set(&mmutable_set->used_tables, 0);
+
+ /* init add to list. */
+ INIT_LIST_HEAD(&(mmutable_set->list));
+ list_add(&mmutable_set->list, &mem_ctx.mmu_tables_sets);
+
+ for (i = 0; i < MMU_TABLES_PER_PAGE; i++) {
+ /* allocate a WSM MMU descriptor */
+ mmutable = kmalloc(sizeof(*mmutable), GFP_KERNEL | __GFP_ZERO);
+ if (mmutable == NULL) {
+ ret = -ENOMEM;
+ MCDRV_DBG_ERROR(mcd, "out of memory");
+ /* Free the full temp list and the store in this case */
+ goto free_temp_list;
+ }
+
+ /* set set reference */
+ mmutable->set = mmutable_set;
+ mmutable->idx = i;
+ mmutable->virt = get_mmu_table_kernel_virt(mmutable);
+ mmutable->phys = mmutable_set->phys+i*sizeof(struct mmutable);
+ atomic_set(&mmutable->usage, 0);
+
+ /* add to temp list. */
+ INIT_LIST_HEAD(&mmutable->list);
+ list_add_tail(&mmutable->list, &temp);
+ }
+
+ /*
+ * If everything went ok then merge the temp list with the global
+ * free list
+ */
+ list_splice_tail(&temp, &mem_ctx.free_mmu_tables);
+ return 0;
+free_temp_list:
+ list_for_each_entry_safe(mmutable, mmutable2, &temp, list) {
+ kfree(mmutable);
+ }
+
+ list_del(&mmutable_set->list);
+
+free_store:
+ free_page(store);
+ return ret;
+}
+
+/*
+ * Get a MMU table from the free tables list or allocate a new one and
+ * initialize it. Assumes the table_lock is already taken.
+ */
+static struct mc_mmu_table *alloc_mmu_table(struct mc_instance *instance)
+{
+ int ret = 0;
+ struct mc_mmu_table *table = NULL;
+
+ if (list_empty(&mem_ctx.free_mmu_tables)) {
+ ret = alloc_mmu_table_store();
+ if (ret) {
+ MCDRV_DBG_ERROR(mcd, "Failed to allocate new store!");
+ return ERR_PTR(-ENOMEM);
+ }
+ /* if it's still empty something wrong has happened */
+ if (list_empty(&mem_ctx.free_mmu_tables)) {
+ MCDRV_DBG_ERROR(mcd,
+ "Free list not updated correctly!");
+ return ERR_PTR(-EFAULT);
+ }
+ }
+
+ /* get a WSM MMU descriptor */
+ table = list_first_entry(&mem_ctx.free_mmu_tables,
+ struct mc_mmu_table, list);
+ if (table == NULL) {
+ MCDRV_DBG_ERROR(mcd, "out of memory");
+ return ERR_PTR(-ENOMEM);
+ }
+ /* Move it to the used MMU tables list */
+ list_move_tail(&table->list, &mem_ctx.mmu_tables);
+
+ table->handle = mc_get_new_handle();
+ table->owner = instance;
+
+ atomic_inc(&table->set->used_tables);
+ atomic_inc(&table->usage);
+
+ MCDRV_DBG_VERBOSE(mcd,
+ "chunkPhys=0x%llX, idx=%d, usage=%d, owner=%p",
+ (u64)table->set->phys, table->idx,
+ atomic_read(&(table->usage)), table->owner);
+
+ return table;
+}
+
+/*
+ * Frees the object associated with a MMU table. Initially the object is moved
+ * to the free tables list, but if all the 4 lists of the store are free
+ * then the store is also released.
+ * Assumes the table_lock is already taken.
+ */
+static void free_mmu_table(struct mc_mmu_table *table)
+{
+ struct mc_mmu_tables_set *mmutable_set;
+
+ if (WARN(!table, "Invalid table"))
+ return;
+
+ mmutable_set = table->set;
+ if (WARN(!mmutable_set, "Invalid table set"))
+ return;
+
+ list_move_tail(&table->list, &mem_ctx.free_mmu_tables);
+
+ /* if nobody uses this set, we can release it. */
+ if (atomic_dec_and_test(&mmutable_set->used_tables)) {
+ struct mc_mmu_table *tmp;
+
+ /* remove from list */
+ list_del(&mmutable_set->list);
+ /*
+ * All the MMU tables are in the free list for this set
+ * so we can just remove them from there
+ */
+ list_for_each_entry_safe(table, tmp, &mem_ctx.free_mmu_tables,
+ list) {
+ if (table->set == mmutable_set) {
+ list_del(&table->list);
+ kfree(table);
+ }
+ } /* end while */
+
+ /*
+ * We shouldn't recover from this since it was some data
+ * corruption before
+ */
+ BUG_ON(!mmutable_set->page);
+ clear_bit(PG_reserved, &(mmutable_set->page)->flags);
+
+
+ BUG_ON(!mmutable_set->kernel_virt);
+ free_page((unsigned long)mmutable_set->kernel_virt);
+
+ kfree(mmutable_set);
+ }
+}
+
+/*
+ * Create a MMU table in a WSM container that has been allocates previously.
+ * Assumes the table lock is already taken or there is no need to take like
+ * when first creating the MMU table the full list is locked.
+ *
+ * @task pointer to task owning WSM
+ * @wsm_buffer user space WSM start
+ * @wsm_len WSM length
+ * @table Pointer to MMU table details
+ */
+static int map_buffer(struct task_struct *task, void *wsm_buffer,
+ unsigned int wsm_len, struct mc_mmu_table *table)
+{
+ int ret = 0;
+ unsigned int i, nr_of_pages;
+ /* start address of the 4 KiB page of wsm_buffer */
+ void *virt_addr_page;
+ struct page *page;
+ struct mmutable *mmutable;
+ struct page **mmutable_as_array_of_pointers_to_page;
+ /* page offset in wsm buffer */
+ unsigned int offset;
+
+ if (WARN(!wsm_buffer, "Invalid WSM buffer pointer"))
+ return -EINVAL;
+
+ if (WARN(wsm_len == 0, "Invalid WSM buffer length"))
+ return -EINVAL;
+
+ if (WARN(!table, "Invalid mapping table for WSM"))
+ return -EINVAL;
+
+ /* no size > 1Mib supported */
+ if (wsm_len > SZ_1M) {
+ MCDRV_DBG_ERROR(mcd, "size > 1 MiB");
+ return -EINVAL;
+ }
+
+ MCDRV_DBG_VERBOSE(mcd, "WSM addr=0x%p, len=0x%08x", wsm_buffer,
+ wsm_len);
+
+ /* calculate page usage */
+ virt_addr_page = (void *)(((unsigned long)(wsm_buffer)) & PAGE_MASK);
+ offset = (unsigned int) (((unsigned long)(wsm_buffer)) & (~PAGE_MASK));
+ nr_of_pages = PAGE_ALIGN(offset + wsm_len) / PAGE_SIZE;
+
+ MCDRV_DBG_VERBOSE(mcd, "virt addr page start=0x%p, pages=%d",
+ virt_addr_page, nr_of_pages);
+
+ /* MMU table can hold max 1MiB in 256 pages. */
+ if ((nr_of_pages * PAGE_SIZE) > SZ_1M) {
+ MCDRV_DBG_ERROR(mcd, "WSM paged exceed 1 MiB");
+ return -EINVAL;
+ }
+
+ mmutable = table->virt;
+ /*
+ * We use the memory for the MMU table to hold the pointer
+ * and convert them later. This works, as everything comes
+ * down to a 32 bit value.
+ */
+ mmutable_as_array_of_pointers_to_page = (struct page **)mmutable;
+
+ /* Request comes from user space */
+ if (task != NULL && !is_vmalloc_addr(wsm_buffer)) {
+ /*
+ * lock user page in memory, so they do not get swapped
+ * out.
+ * REV axh: Kernel 2.6.27 added a new get_user_pages_fast()
+ * function, maybe it is called fast_gup() in some versions.
+ * handle user process doing a fork().
+ * Child should not get things.
+ * http://osdir.com/ml/linux-media/2009-07/msg00813.html
+ * http://lwn.net/Articles/275808/
+ */
+ ret = lock_pages(task, virt_addr_page, nr_of_pages,
+ mmutable_as_array_of_pointers_to_page);
+ if (ret != 0) {
+ MCDRV_DBG_ERROR(mcd, "lock_user_pages() failed");
+ return ret;
+ }
+ }
+ /* Request comes from kernel space(cont buffer) */
+ else if (task == NULL && !is_vmalloc_addr(wsm_buffer)) {
+ void *uaddr = wsm_buffer;
+ for (i = 0; i < nr_of_pages; i++) {
+ page = virt_to_page(uaddr);
+ if (!page) {
+ MCDRV_DBG_ERROR(mcd, "failed to map address");
+ return -EINVAL;
+ }
+ get_page(page);
+ mmutable_as_array_of_pointers_to_page[i] = page;
+ uaddr += PAGE_SIZE;
+ }
+ }
+ /* Request comes from kernel space(vmalloc buffer) */
+ else {
+ void *uaddr = wsm_buffer;
+ for (i = 0; i < nr_of_pages; i++) {
+ page = vmalloc_to_page(uaddr);
+ if (!page) {
+ MCDRV_DBG_ERROR(mcd, "failed to map address");
+ return -EINVAL;
+ }
+ get_page(page);
+ mmutable_as_array_of_pointers_to_page[i] = page;
+ uaddr += PAGE_SIZE;
+ }
+ }
+
+ table->pages = nr_of_pages;
+
+ /*
+ * create MMU Table entries.
+ * used_mmutable->table contains a list of page pointers here.
+ * For a proper cleanup we have to ensure that the following
+ * code either works and used_mmutable contains a valid MMU table
+ * - or fails and used_mmutable->table contains the list of page
+ * pointers.
+ * Any mixed contents will make cleanup difficult.
+ */
+#if defined(CONFIG_ARM64) && !defined(LPAE_SUPPORT)
+ /*
+ * When NWd pointers are 64bits and SWd pte 32bits we need to fill the
+ * table from 0.
+ */
+ i = 0;
+#else
+ /*
+ * Fill the table in reverse order as the table is used as input and
+ * output.
+ */
+ i = MC_ARM_MMU_TABLE_ENTRIES-1;
+#endif
+ do {
+ if (i < nr_of_pages) {
+#ifdef LPAE_SUPPORT
+ uint64_t pte;
+#else
+ uint32_t pte;
+#endif
+ page = mmutable_as_array_of_pointers_to_page[i];
+
+ if (!page) {
+ MCDRV_DBG_ERROR(mcd, "page address is null");
+ return -EFAULT;
+ }
+ /*
+ * create MMU table entry, see ARM MMU docu for details
+ * about flags stored in the lowest 12 bits.
+ * As a side reference, the Article
+ * "ARM's multiply-mapped memory mess"
+ * found in the collection at
+ * http://lwn.net/Articles/409032/
+ * is also worth reading.
+ */
+ pte = page_to_phys(page);
+#ifdef LPAE_SUPPORT
+ pte |= MMU_EXT_XN
+ | MMU_EXT_NG
+ | MMU_EXT_AF
+ | MMU_AP_RW_ALL
+ | MMU_NS
+ | MMU_CACHEABLE | MMU_BUFFERABLE
+ | MMU_TYPE_PAGE;
+#else
+ pte |= MMU_EXT_AP1 | MMU_EXT_AP0
+ | MMU_CACHEABLE | MMU_BUFFERABLE
+ | MMU_TYPE_SMALL | MMU_TYPE_EXT | MMU_EXT_NG;
+#endif /* LPAE_SUPPORT */
+ /*
+ * Linux uses different mappings for SMP systems(the
+ * sharing flag is set for the pte. In order not to
+ * confuse things too much in Mobicore make sure the
+ * shared buffers have the same flags.
+ * This should also be done in SWD side
+ */
+#ifdef CONFIG_SMP
+#ifdef LPAE_SUPPORT
+ pte |= MMU_EXT_SHARED;
+#else
+ pte |= MMU_EXT_SHARED | MMU_EXT_TEX(1);
+#endif /* LPAE_SUPPORT */
+#endif /* CONFIG_SMP */
+
+ mmutable->table_entries[i] = pte;
+ MCDRV_DBG_VERBOSE(mcd, "MMU entry %d: 0x%llx, virt %p",
+ i, (u64)(pte), page);
+ MCDRV_DBG_VERBOSE(mcd, "MMU entry: virt %p",
+ phys_to_virt(pte&MMU_PHYS_MASK));
+ } else {
+ /* ensure rest of table is empty */
+ mmutable->table_entries[i] = 0;
+ }
+#if defined(CONFIG_ARM64) && !defined(LPAE_SUPPORT)
+ } while (++i < MC_ARM_MMU_TABLE_ENTRIES);
+#else
+ } while (i-- != 0);
+#endif
+
+ return ret;
+}
+
+/*
+ * Remove a MMU table in a WSM container. Afterwards the container may be
+ * released. Assumes the table_lock and the lock is taken.
+ */
+static void unmap_buffers(struct mc_mmu_table *table)
+{
+ struct mmutable *mmutable;
+ int i;
+
+ if (WARN_ON(!table))
+ return;
+
+ /* found the table, now release the resources. */
+ MCDRV_DBG_VERBOSE(mcd,
+ "releasing %d referenced pages of table phys=0x%llX",
+ table->pages, (u64)table->phys);
+
+ mmutable = table->virt;
+
+ /* release all locked user space pages */
+ for (i = 0; i < table->pages; i++) {
+ /* convert physical entries from MMU table to page pointers */
+ struct page *page = pte_page(mmutable->table_entries[i]);
+ MCDRV_DBG_VERBOSE(mcd, "MMU entry %d: 0x%llx, virt %p", i,
+ (u64)(mmutable->table_entries[i]), page);
+ BUG_ON(!page);
+ release_page(page);
+ }
+
+ /* remember that all pages have been freed */
+ table->pages = 0;
+}
+
+/* Delete a used MMU table. Assumes the table_lock and the lock is taken */
+static void unmap_mmu_table(struct mc_mmu_table *table)
+{
+ /* Check if it's not locked by other processes too! */
+ if (!atomic_dec_and_test(&table->usage)) {
+ MCDRV_DBG_VERBOSE(mcd,
+ "table phys=%llx still in use (usage now %d)",
+ (u64)table->phys,
+ atomic_read(&(table->usage)));
+ return;
+ }
+
+ /* release if Nwd and Swd/MC do no longer use it. */
+ unmap_buffers(table);
+ free_mmu_table(table);
+}
+
+int mc_free_mmu_table(struct mc_instance *instance, uint32_t handle)
+{
+ struct mc_mmu_table *table;
+ struct mc_mmu_table *table2;
+ int ret = 0;
+
+ if (WARN(!instance, "No instance data available"))
+ return -EFAULT;
+
+ mutex_lock(&mem_ctx.table_lock);
+ table = find_mmu_table(handle);
+
+ if (table == NULL) {
+ MCDRV_DBG_VERBOSE(mcd, "entry not found");
+ ret = -EINVAL;
+ goto err_unlock;
+ }
+ if (instance == table->owner) {
+ /* Prevent double free */
+ table->owner = NULL;
+ } else if (!is_daemon(instance)) {
+ MCDRV_DBG_ERROR(mcd, "instance does not own it");
+ ret = -EPERM;
+ goto err_unlock;
+ }
+
+ /* clean-up in the case of fake L1 table:
+ * we need to unmap all sub-tables and
+ * the buffer referred by the fake table
+ */
+ if (table->type&MC_MMU_TABLE_TYPE_WSM_FAKE_L1) {
+ int i = 0;
+ uint64_t *va;
+ /* first and only record of the fake table
+ * contains physical address of the buffer
+ */
+#ifdef LPAE_SUPPORT
+ uint64_t *pte = (uint64_t *)table->virt;
+#else
+ uint32_t *pte = (uint32_t *)table->virt;
+#endif /* LPAE_SUPPORT */
+ /* convert it to virtual address */
+ va = phys_to_virt((*pte)&MMU_PHYS_MASK);
+ MCDRV_DBG_VERBOSE(mcd, "va = 0x%p", va);
+ /* loop through uin64_t buffer records */
+ if (va != NULL) {
+ while ((va[i] != 0) &&
+ (i < (SZ_4K / sizeof(uint64_t)))) {
+ MCDRV_DBG_VERBOSE(
+ mcd,
+ "phys. addr of sub-table [%d] = 0x%llX",
+ i,
+ (u64)va[i]);
+ table2 = find_mmu_table_by_phys_addr(
+ (phys_addr_t)va[i]);
+ MCDRV_DBG_VERBOSE(mcd, "table2 = 0x%p", table2);
+ if (table2 != NULL)
+ unmap_mmu_table(table2);
+ i++;
+ }
+ free_page((unsigned long)va);
+ }
+ }
+
+ /* free table (if no further locks exist) */
+ unmap_mmu_table(table);
+err_unlock:
+ mutex_unlock(&mem_ctx.table_lock);
+
+ return ret;
+}
+
+int mc_lock_mmu_table(struct mc_instance *instance, uint32_t handle)
+{
+ int ret = 0;
+ struct mc_mmu_table *table = NULL;
+
+ if (WARN(!instance, "No instance data available"))
+ return -EFAULT;
+
+ mutex_lock(&mem_ctx.table_lock);
+ table = find_mmu_table(handle);
+
+ if (table == NULL) {
+ MCDRV_DBG_VERBOSE(mcd, "entry not found %u", handle);
+ ret = -EINVAL;
+ goto table_err;
+ }
+ if (instance != table->owner && !is_daemon(instance)) {
+ MCDRV_DBG_ERROR(mcd, "instance does no own it");
+ ret = -EPERM;
+ goto table_err;
+ }
+
+ /* lock entry */
+ atomic_inc(&table->usage);
+table_err:
+ mutex_unlock(&mem_ctx.table_lock);
+ return ret;
+}
+/*
+ * Allocate MMU table and map buffer into it.
+ * That is, create respective table entries.
+ */
+struct mc_mmu_table *mc_alloc_mmu_table(struct mc_instance *instance,
+ struct task_struct *task, void *wsm_buffer, unsigned int wsm_len,
+ unsigned int type)
+{
+ int ret = 0;
+ struct mc_mmu_table *table;
+
+ if (WARN(!instance, "No instance data available"))
+ return ERR_PTR(-EFAULT);
+
+ mutex_lock(&mem_ctx.table_lock);
+ table = alloc_mmu_table(instance);
+ if (IS_ERR(table)) {
+ MCDRV_DBG_ERROR(mcd, "alloc_mmu_table() failed");
+ ret = -ENOMEM;
+ goto err_no_mem;
+ }
+
+ /* create the MMU page for the WSM */
+ ret = map_buffer(task, wsm_buffer, wsm_len, table);
+
+ if (ret != 0) {
+ MCDRV_DBG_ERROR(mcd, "map_buffer() failed");
+ unmap_mmu_table(table);
+ goto err_no_mem;
+ }
+ table->type = type;
+ MCDRV_DBG_VERBOSE(mcd,
+ "mapped buffer %p to table with handle %d @ 0x%llX",
+ wsm_buffer, table->handle, (u64)table->phys);
+
+ mutex_unlock(&mem_ctx.table_lock);
+ return table;
+err_no_mem:
+ mutex_unlock(&mem_ctx.table_lock);
+ return ERR_PTR(ret);
+}
+
+phys_addr_t mc_find_mmu_table(uint32_t handle, int32_t fd)
+{
+ phys_addr_t ret = 0;
+ struct mc_mmu_table *table = NULL;
+
+ mutex_lock(&mem_ctx.table_lock);
+ table = find_mmu_table(handle);
+
+ if (table == NULL) {
+ MCDRV_DBG_ERROR(mcd, "entry not found %u", handle);
+ ret = 0;
+ goto table_err;
+ }
+
+ /* It's safe here not to lock the instance since the owner of
+ * the table will be cleared only with the table lock taken */
+ if (!mc_check_owner_fd(table->owner, fd)) {
+ MCDRV_DBG_ERROR(mcd, "not valid owner %u", handle);
+ ret = 0;
+ goto table_err;
+ }
+
+ ret = table->phys;
+table_err:
+ mutex_unlock(&mem_ctx.table_lock);
+ return ret;
+}
+
+void mc_clean_mmu_tables(void)
+{
+ struct mc_mmu_table *table, *tmp;
+
+ mutex_lock(&mem_ctx.table_lock);
+ /* Check if some WSM is orphaned. */
+ list_for_each_entry_safe(table, tmp, &mem_ctx.mmu_tables, list) {
+ if (table->owner == NULL) {
+ MCDRV_DBG_VERBOSE(mcd,
+ "WSM table phys=0x%llX pages=%d",
+ (u64)table->phys, table->pages);
+ unmap_mmu_table(table);
+ }
+ }
+ mutex_unlock(&mem_ctx.table_lock);
+}
+
+void mc_clear_mmu_tables(struct mc_instance *instance)
+{
+ struct mc_mmu_table *table, *tmp;
+
+ mutex_lock(&mem_ctx.table_lock);
+ /* Check if some WSM is still in use. */
+ list_for_each_entry_safe(table, tmp, &mem_ctx.mmu_tables, list) {
+ if (table->owner == instance) {
+ MCDRV_DBG_VERBOSE(mcd, "WSM table phys=0x%llX pages=%d",
+ (u64)table->phys, table->pages);
+ /* unlock app usage and free or mark it as orphan */
+ table->owner = NULL;
+ unmap_mmu_table(table);
+ }
+ }
+ mutex_unlock(&mem_ctx.table_lock);
+}
+
+int mc_init_mmu_tables(void)
+{
+ /* init list for WSM MMU chunks. */
+ INIT_LIST_HEAD(&mem_ctx.mmu_tables_sets);
+
+ /* MMU table descriptor list. */
+ INIT_LIST_HEAD(&mem_ctx.mmu_tables);
+
+ /* MMU free table descriptor list. */
+ INIT_LIST_HEAD(&mem_ctx.free_mmu_tables);
+
+ mutex_init(&mem_ctx.table_lock);
+
+ return 0;
+}
+
+void mc_release_mmu_tables(void)
+{
+ struct mc_mmu_table *table;
+ /* Check if some WSM is still in use. */
+ list_for_each_entry(table, &mem_ctx.mmu_tables, list) {
+ WARN(1, "WSM MMU still in use: phys=0x%llX ,nr_of_pages=%d",
+ (u64)table->phys, table->pages);
+ }
+}
diff --git a/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/mem.h b/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/mem.h
new file mode 100644
index 000000000..c4b6715f2
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/mem.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2013-2015 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+#ifndef _MC_MEM_H_
+#define _MC_MEM_H_
+
+#ifdef LPAE_SUPPORT
+/*
+ * Number of page table entries in one MMU table. This is ARM specific, an
+ * MMU table covers 2 MiB by using 512 entries referring to 4KiB pages each.
+ */
+#define MC_ARM_MMU_TABLE_ENTRIES 512
+
+/* ARM level 3 (MMU) table with 512 entries. Size: 4k */
+struct mmutable {
+ uint64_t table_entries[MC_ARM_MMU_TABLE_ENTRIES];
+};
+
+/* There is 1 table in each page. */
+#define MMU_TABLES_PER_PAGE 1
+#else
+/*
+ * MobiCore specific page tables for world shared memory.
+ * Linux uses shadow page tables, see arch/arm/include/asm/pgtable-2level.
+ * MobiCore uses the default ARM format.
+ *
+ * Number of page table entries in one MMU table. This is ARM specific, an
+ * MMU table covers 1 MiB by using 256 entries referring to 4KiB pages each.
+ */
+#define MC_ARM_MMU_TABLE_ENTRIES 256
+
+/* ARM level 2 (MMU) table with 256 entries. Size: 1k */
+struct mmutable {
+ uint32_t table_entries[MC_ARM_MMU_TABLE_ENTRIES];
+};
+
+/* There are 4 tables in each page. */
+#define MMU_TABLES_PER_PAGE 4
+#endif
+
+/* mc_mmu_table type flags */
+#define MC_MMU_TABLE_TYPE_WSM_FAKE_L1 1
+
+
+/* Store for four MMU tables in one 4kb page*/
+struct mc_mmu_table_store {
+ struct mmutable table[MMU_TABLES_PER_PAGE];
+};
+
+/* Usage and maintenance information about mc_mmu_table_store */
+struct mc_mmu_tables_set {
+ struct list_head list;
+ /* kernel virtual address */
+ struct mc_mmu_table_store *kernel_virt;
+ /* physical address */
+ phys_addr_t phys;
+ /* pointer to page struct */
+ struct page *page;
+ /* How many pages from this set are used */
+ atomic_t used_tables;
+};
+
+/*
+ * MMU table allocated to the Daemon or a TLC describing a world shared
+ * buffer.
+ * When users map a malloc()ed area into SWd, a MMU table is allocated.
+ * In addition, the area of maximum 1MB virtual address space is mapped into
+ * the MMU table and a handle for this table is returned to the user.
+ */
+struct mc_mmu_table {
+ struct list_head list;
+ /* Table lock */
+ struct mutex lock;
+ /* handle as communicated to user mode */
+ unsigned int handle;
+ /* Number of references kept to this MMU table */
+ atomic_t usage;
+ /* owner of this MMU table */
+ struct mc_instance *owner;
+ /* set describing where our MMU table is stored */
+ struct mc_mmu_tables_set *set;
+ /* index into MMU table set */
+ unsigned int idx;
+ /* size of buffer */
+ unsigned int pages;
+ /* virtual address*/
+ void *virt;
+ /* physical address */
+ phys_addr_t phys;
+ /* type of mmu table */
+ unsigned int type;
+};
+
+/* MobiCore Driver Memory context data. */
+struct mc_mem_context {
+ struct mc_instance *daemon_inst;
+ /* Backing store for MMU tables */
+ struct list_head mmu_tables_sets;
+ /* Bookkeeping for used MMU tables */
+ struct list_head mmu_tables;
+ /* Bookkeeping for free MMU tables */
+ struct list_head free_mmu_tables;
+ /* semaphore to synchronize access to above lists */
+ struct mutex table_lock;
+};
+
+/*
+ * Allocate MMU table and map buffer into it.
+ * That is, create respective table entries.
+ */
+struct mc_mmu_table *mc_alloc_mmu_table(struct mc_instance *instance,
+ struct task_struct *task, void *wsm_buffer, unsigned int wsm_len,
+ unsigned int type);
+
+/* Delete all the MMU tables associated with an instance */
+void mc_clear_mmu_tables(struct mc_instance *instance);
+
+/* Release all orphaned MMU tables */
+void mc_clean_mmu_tables(void);
+
+/* Delete a used MMU table. */
+int mc_free_mmu_table(struct mc_instance *instance, uint32_t handle);
+
+/*
+ * Lock a MMU table - the daemon adds +1 to refcount of the MMU table
+ * marking it in use by SWD so it doesn't get released when the TLC dies.
+ */
+int mc_lock_mmu_table(struct mc_instance *instance, uint32_t handle);
+
+/* Return the phys address of MMU table. */
+phys_addr_t mc_find_mmu_table(uint32_t handle, int32_t fd);
+/* Release all used MMU tables to Linux memory space */
+void mc_release_mmu_tables(void);
+
+/* Initialize all MMU tables structure */
+int mc_init_mmu_tables(void);
+
+#endif /* _MC_MEM_H_ */
diff --git a/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/ops.c b/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/ops.c
new file mode 100644
index 000000000..ad9e9e243
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/ops.c
@@ -0,0 +1,421 @@
+/*
+ * Copyright (c) 2013-2014 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+/*
+ * MobiCore Driver Kernel Module.
+ *
+ * This module is written as a Linux device driver.
+ * This driver represents the command proxy on the lowest layer, from the
+ * secure world to the non secure world, and vice versa.
+ * This driver is located in the non secure world (Linux).
+ * This driver offers IOCTL commands, for access to the secure world, and has
+ * the interface from the secure world to the normal world.
+ * The access to the driver is possible with a file descriptor,
+ * which has to be created by the fd = open(/dev/mobicore) command.
+ */
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/workqueue.h>
+#include <linux/cpu.h>
+#include <linux/moduleparam.h>
+
+
+#include "main.h"
+#include "fastcall.h"
+#include "ops.h"
+#include "mem.h"
+#include "pm.h"
+#include "debug.h"
+
+/* MobiCore context data */
+static struct mc_context *ctx;
+#ifdef TBASE_CORE_SWITCHER
+static uint32_t active_cpu;
+
+#ifdef TEST
+ /*
+ * Normal world <t-base core info for testing.
+ */
+
+ module_param(active_cpu, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ MODULE_PARM_DESC(active_cpu, "Active <t-base Core");
+#endif
+
+
+static int mobicore_cpu_callback(struct notifier_block *nfb,
+ unsigned long action, void *hcpu);
+static struct notifier_block mobicore_cpu_notifer = {
+ .notifier_call = mobicore_cpu_callback,
+};
+#endif
+
+static inline long smc(union fc_generic *fc)
+{
+ /* If we request sleep yields must be filtered out as they
+ * make no sense */
+ if (ctx->mcp)
+ if (ctx->mcp->flags.sleep_mode.sleep_req) {
+ if (fc->as_in.cmd == MC_SMC_N_YIELD)
+ return MC_FC_RET_ERR_INVALID;
+ }
+ return _smc(fc);
+}
+
+struct fastcall_work {
+#ifdef MC_FASTCALL_WORKER_THREAD
+ struct kthread_work work;
+#else
+ struct work_struct work;
+#endif
+ void *data;
+};
+
+#ifdef MC_FASTCALL_WORKER_THREAD
+static void fastcall_work_func(struct kthread_work *work);
+#else
+static void fastcall_work_func(struct work_struct *work);
+#endif
+
+
+#ifdef MC_FASTCALL_WORKER_THREAD
+
+static struct task_struct *fastcall_thread;
+static DEFINE_KTHREAD_WORKER(fastcall_worker);
+
+bool mc_fastcall(void *data)
+{
+ struct fastcall_work fc_work = {
+ KTHREAD_WORK_INIT(fc_work.work, fastcall_work_func),
+ .data = data,
+ };
+
+ if (!queue_kthread_work(&fastcall_worker, &fc_work.work))
+ return false;
+ flush_kthread_work(&fc_work.work);
+ return true;
+}
+
+int mc_fastcall_init(struct mc_context *context)
+{
+ int ret = 0;
+ ctx = context;
+
+ fastcall_thread = kthread_create(kthread_worker_fn, &fastcall_worker,
+ "mc_fastcall");
+ if (IS_ERR(fastcall_thread)) {
+ ret = PTR_ERR(fastcall_thread);
+ fastcall_thread = NULL;
+ MCDRV_DBG_ERROR(mcd, "cannot create fastcall wq (%d)", ret);
+ return ret;
+ }
+
+ wake_up_process(fastcall_thread);
+
+ /* this thread MUST run on CPU 0 at startup */
+ set_cpus_allowed(fastcall_thread, CPU_MASK_CPU0);
+#ifdef TBASE_CORE_SWITCHER
+ register_cpu_notifier(&mobicore_cpu_notifer);
+#endif
+ return 0;
+}
+
+void mc_fastcall_destroy(void)
+{
+ if (!IS_ERR_OR_NULL(fastcall_thread)) {
+ kthread_stop(fastcall_thread);
+ fastcall_thread = NULL;
+ }
+}
+#else
+
+bool mc_fastcall(void *data)
+{
+ struct fastcall_work work = {
+ .data = data,
+ };
+ INIT_WORK(&work.work, fastcall_work_func);
+ if (!schedule_work_on(0, &work.work))
+ return false;
+ flush_work(&work.work);
+ return true;
+}
+
+int mc_fastcall_init(struct mc_context *context)
+{
+ ctx = context;
+ return 0;
+};
+
+void mc_fastcall_destroy(void) {};
+#endif
+
+#ifdef MC_FASTCALL_WORKER_THREAD
+static void fastcall_work_func(struct kthread_work *work)
+#else
+static void fastcall_work_func(struct work_struct *work)
+#endif
+{
+ struct fastcall_work *fc_work =
+ container_of(work, struct fastcall_work, work);
+ union fc_generic *fc_generic = fc_work->data;
+#ifdef TBASE_CORE_SWITCHER
+ uint32_t cpu_swap = 0, new_cpu;
+ uint32_t cpu_id[] = CPU_IDS;
+#endif
+
+#ifdef MC_CRYPTO_CLOCK_MANAGEMENT
+ mc_pm_clock_enable();
+#endif
+
+
+ if (fc_generic == NULL)
+ return;
+#ifdef TBASE_CORE_SWITCHER
+ if (fc_generic->as_in.cmd == MC_FC_SWITCH_CORE) {
+ cpu_swap = 1;
+ new_cpu = fc_generic->as_in.param[0];
+ fc_generic->as_in.param[0] = cpu_id[fc_generic->as_in.param[0]];
+ }
+#endif
+ smc(fc_work->data);
+#ifdef TBASE_CORE_SWITCHER
+ if (cpu_swap) {
+ if (fc_generic->as_out.ret == 0) {
+ cpumask_t cpu;
+ active_cpu = new_cpu;
+ MCDRV_DBG(mcd, "CoreSwap ok %d -> %d\n",
+ raw_smp_processor_id(), active_cpu);
+ cpumask_clear(&cpu);
+ cpumask_set_cpu(active_cpu, &cpu);
+#ifdef MC_FASTCALL_WORKER_THREAD
+ set_cpus_allowed(fastcall_thread, cpu);
+#endif
+ } else {
+ MCDRV_DBG(mcd, "CoreSwap failed %d -> %d\n",
+ raw_smp_processor_id(),
+ fc_generic->as_in.param[0]);
+ }
+ }
+#endif
+#ifdef MC_CRYPTO_CLOCK_MANAGEMENT
+ mc_pm_clock_disable();
+#endif
+}
+
+int mc_info(uint32_t ext_info_id, uint32_t *state, uint32_t *ext_info)
+{
+ int ret = 0;
+ union mc_fc_info fc_info;
+
+ MCDRV_DBG_VERBOSE(mcd, "enter");
+
+ memset(&fc_info, 0, sizeof(fc_info));
+ fc_info.as_in.cmd = MC_FC_INFO;
+ fc_info.as_in.ext_info_id = ext_info_id;
+
+ MCDRV_DBG(mcd, "<- cmd=0x%08x, ext_info_id=0x%08x",
+ fc_info.as_in.cmd, fc_info.as_in.ext_info_id);
+
+ mc_fastcall(&(fc_info.as_generic));
+
+ MCDRV_DBG(mcd,
+ "-> r=0x%08x ret=0x%08x state=0x%08x ext_info=0x%08x",
+ fc_info.as_out.resp,
+ fc_info.as_out.ret,
+ fc_info.as_out.state,
+ fc_info.as_out.ext_info);
+
+ ret = convert_fc_ret(fc_info.as_out.ret);
+
+ *state = fc_info.as_out.state;
+ *ext_info = fc_info.as_out.ext_info;
+
+ MCDRV_DBG_VERBOSE(mcd, "exit with %d/0x%08X", ret, ret);
+
+ return ret;
+}
+
+#ifdef TBASE_CORE_SWITCHER
+
+uint32_t mc_active_core(void)
+{
+ return active_cpu;
+}
+
+int mc_switch_core(uint32_t core_num)
+{
+ int32_t ret = 0;
+ union mc_fc_swich_core fc_switch_core;
+
+ if (!cpu_online(core_num))
+ return 1;
+
+ MCDRV_DBG_VERBOSE(mcd, "enter\n");
+
+ memset(&fc_switch_core, 0, sizeof(fc_switch_core));
+ fc_switch_core.as_in.cmd = MC_FC_SWITCH_CORE;
+
+ if (core_num < COUNT_OF_CPUS)
+ fc_switch_core.as_in.core_id = core_num;
+ else
+ fc_switch_core.as_in.core_id = 0;
+
+ MCDRV_DBG(mcd,
+ "<- cmd=0x%08x, core_id=0x%08x\n",
+ fc_switch_core.as_in.cmd,
+ fc_switch_core.as_in.core_id);
+ MCDRV_DBG(mcd,
+ "<- core_num=0x%08x, active_cpu=0x%08x\n",
+ core_num, active_cpu);
+ mc_fastcall(&(fc_switch_core.as_generic));
+
+ ret = convert_fc_ret(fc_switch_core.as_out.ret);
+
+ MCDRV_DBG_VERBOSE(mcd, "exit with %d/0x%08X\n", ret, ret);
+
+ return ret;
+}
+
+void mc_cpu_offfline(int cpu)
+{
+ if (active_cpu == cpu) {
+ int i;
+ /* Chose the first online CPU and switch! */
+ for_each_online_cpu(i) {
+ if (i == cpu) {
+ MCDRV_DBG(mcd, "Skipping CPU %d\n", cpu);
+ continue;
+ }
+ MCDRV_DBG(mcd, "CPU %d is dying, switching to %d\n",
+ cpu, i);
+ mc_switch_core(i);
+ break;
+ }
+ } else {
+ MCDRV_DBG(mcd, "not active CPU, no action taken\n");
+ }
+}
+
+static int mobicore_cpu_callback(struct notifier_block *nfb,
+ unsigned long action, void *hcpu)
+{
+ unsigned int cpu = (unsigned long)hcpu;
+
+ switch (action) {
+ case CPU_DOWN_PREPARE:
+ case CPU_DOWN_PREPARE_FROZEN:
+ dev_info(mcd, "Cpu %u is going to die\n", cpu);
+ mc_cpu_offfline(cpu);
+ break;
+ case CPU_DEAD:
+ case CPU_DEAD_FROZEN:
+ dev_info(mcd, "Cpu %u is dead\n", cpu);
+ break;
+ }
+ return NOTIFY_OK;
+}
+#endif
+
+/* Yield to MobiCore */
+int mc_yield(void)
+{
+ int ret = 0;
+ union fc_generic yield;
+
+ /* MCDRV_DBG_VERBOSE(mcd, "enter"); */
+ memset(&yield, 0, sizeof(yield));
+ yield.as_in.cmd = MC_SMC_N_YIELD;
+ mc_fastcall(&yield);
+ ret = convert_fc_ret(yield.as_out.ret);
+
+ return ret;
+}
+
+/* call common notify */
+int mc_nsiq(void)
+{
+ int ret = 0;
+ union fc_generic nsiq;
+ MCDRV_DBG_VERBOSE(mcd, "enter");
+ memset(&nsiq, 0, sizeof(nsiq));
+ nsiq.as_in.cmd = MC_SMC_N_SIQ;
+ mc_fastcall(&nsiq);
+ ret = convert_fc_ret(nsiq.as_out.ret);
+ return ret;
+}
+
+/* call common notify */
+int _nsiq(void)
+{
+ int ret = 0;
+ union fc_generic nsiq;
+ MCDRV_DBG_VERBOSE(mcd, "enter");
+ memset(&nsiq, 0, sizeof(nsiq));
+ nsiq.as_in.cmd = MC_SMC_N_SIQ;
+ _smc(&nsiq);
+ ret = convert_fc_ret(nsiq.as_out.ret);
+ return ret;
+}
+
+/* Call the INIT fastcall to setup MobiCore initialization */
+int mc_init(phys_addr_t base, uint32_t nq_length,
+ uint32_t mcp_offset, uint32_t mcp_length)
+{
+ int ret = 0;
+ union mc_fc_init fc_init;
+ uint64_t base_addr = (uint64_t)base;
+ uint32_t base_high = (uint32_t)(base_addr >> 32);
+
+ MCDRV_DBG_VERBOSE(mcd, "enter");
+
+ memset(&fc_init, 0, sizeof(fc_init));
+
+ fc_init.as_in.cmd = MC_FC_INIT;
+ /* base address of mci buffer 4KB aligned */
+ fc_init.as_in.base = (uint32_t)base_addr;
+ /* notification buffer start/length [16:16] [start, length] */
+ fc_init.as_in.nq_info = ((base_high & 0xFFFF) << 16) |
+ (nq_length & 0xFFFF);
+ /* mcp buffer start/length [16:16] [start, length] */
+ fc_init.as_in.mcp_info = (mcp_offset << 16) | (mcp_length & 0xFFFF);
+
+ /*
+ * Set KMOD notification queue to start of MCI
+ * mciInfo was already set up in mmap
+ */
+ MCDRV_DBG(mcd,
+ "cmd=0x%08x, base=0x%08x,nq_info=0x%08x, mcp_info=0x%08x",
+ fc_init.as_in.cmd, fc_init.as_in.base, fc_init.as_in.nq_info,
+ fc_init.as_in.mcp_info);
+ mc_fastcall(&fc_init.as_generic);
+ MCDRV_DBG(mcd, "out cmd=0x%08x, ret=0x%08x", fc_init.as_out.resp,
+ fc_init.as_out.ret);
+
+ ret = convert_fc_ret(fc_init.as_out.ret);
+
+ MCDRV_DBG_VERBOSE(mcd, "exit with %d/0x%08X", ret, ret);
+
+ return ret;
+}
+
+/* Return MobiCore driver version */
+uint32_t mc_get_version(void)
+{
+ MCDRV_DBG(mcd, "MobiCore driver version is %i.%i",
+ MCDRVMODULEAPI_VERSION_MAJOR,
+ MCDRVMODULEAPI_VERSION_MINOR);
+
+ return MC_VERSION(MCDRVMODULEAPI_VERSION_MAJOR,
+ MCDRVMODULEAPI_VERSION_MINOR);
+}
diff --git a/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/ops.h b/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/ops.h
new file mode 100644
index 000000000..30458a37d
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/ops.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2013 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+#ifndef _MC_OPS_H_
+#define _MC_OPS_H_
+
+#include <linux/workqueue.h>
+#include "fastcall.h"
+
+int mc_yield(void);
+int mc_nsiq(void);
+int _nsiq(void);
+uint32_t mc_get_version(void);
+
+int mc_info(uint32_t ext_info_id, uint32_t *state, uint32_t *ext_info);
+int mc_init(phys_addr_t base, uint32_t nq_length, uint32_t mcp_offset,
+ uint32_t mcp_length);
+#ifdef TBASE_CORE_SWITCHER
+int mc_switch_core(uint32_t core_num);
+#endif
+
+bool mc_fastcall(void *data);
+
+int mc_fastcall_init(struct mc_context *context);
+void mc_fastcall_destroy(void);
+
+#endif /* _MC_OPS_H_ */
diff --git a/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/platform.h b/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/platform.h
new file mode 100644
index 000000000..4ceb928b8
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/platform.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2013 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+/*
+ * Header file of MobiCore Driver Kernel Module Platform
+ * specific structures
+ *
+ * Internal structures of the McDrvModule
+ *
+ * Header file the MobiCore Driver Kernel Module,
+ * its internal structures and defines.
+ */
+#ifndef _MC_DRV_PLATFORM_H_
+#define _MC_DRV_PLATFORM_H_
+
+/* MobiCore Interrupt. */
+#define MC_INTR_SSIQ 280
+
+/* Enable mobicore mem traces */
+#define MC_MEM_TRACES
+
+/* Enable Runtime Power Management */
+#ifdef CONFIG_PM_RUNTIME
+ #define MC_PM_RUNTIME
+#endif
+
+#define TBASE_CORE_SWITCHER
+/* Values of MPIDR regs in cpu0, cpu1, cpu2, cpu3*/
+#define CPU_IDS {0x0000, 0x0001, 0x0002, 0x0003, 0x0100, 0x0101, 0x0102, 0x0103}
+#define COUNT_OF_CPUS CONFIG_NR_CPUS
+
+/* Enable Fastcall worker thread */
+#define MC_FASTCALL_WORKER_THREAD
+
+#if !defined(CONFIG_ARCH_MT6580)
+/* Enable LPAE */
+#define LPAE_SUPPORT
+/* Enable AARCH32 Fast call IDs */
+#define MC_AARCH32_FC
+#endif
+
+#endif /* _MC_DRV_PLATFORM_H_ */
diff --git a/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/pm.c b/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/pm.c
new file mode 100644
index 000000000..e3ea6b530
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/pm.c
@@ -0,0 +1,307 @@
+/*
+ * Copyright (c) 2013 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+/*
+ * MobiCore Driver Kernel Module.
+ * This module is written as a Linux device driver.
+ * This driver represents the command proxy on the lowest layer, from the
+ * secure world to the non secure world, and vice versa.
+ * This driver is located in the non secure world (Linux).
+ * This driver offers IOCTL commands, for access to the secure world, and has
+ * the interface from the secure world to the normal world.
+ * The access to the driver is possible with a file descriptor,
+ * which has to be created by the fd = open(/dev/mobicore) command.
+ */
+#include <linux/module.h>
+#include <linux/timer.h>
+#include <linux/suspend.h>
+#include <linux/device.h>
+
+#include "main.h"
+#include "pm.h"
+#include "fastcall.h"
+#include "ops.h"
+#include "logging.h"
+#include "debug.h"
+
+#ifdef MC_CRYPTO_CLOCK_MANAGEMENT
+ #include <linux/clk.h>
+ #include <linux/err.h>
+
+ struct clk *mc_ce_iface_clk = NULL;
+ struct clk *mc_ce_core_clk = NULL;
+ struct clk *mc_ce_bus_clk = NULL;
+#endif /* MC_CRYPTO_CLOCK_MANAGEMENT */
+
+#ifdef MC_PM_RUNTIME
+
+static struct mc_context *ctx;
+
+static bool sleep_ready(void)
+{
+ if (!ctx->mcp)
+ return false;
+
+ if (!(ctx->mcp->flags.sleep_mode.ready_to_sleep & READY_TO_SLEEP))
+ return false;
+
+ return true;
+}
+
+static void mc_suspend_handler(struct work_struct *work)
+{
+ if (!ctx->mcp)
+ return;
+
+ ctx->mcp->flags.sleep_mode.sleep_req = REQ_TO_SLEEP;
+ _nsiq();
+}
+DECLARE_WORK(suspend_work, mc_suspend_handler);
+
+static inline void dump_sleep_params(struct mc_flags *flags)
+{
+ MCDRV_DBG(mcd, "MobiCore IDLE=%d!", flags->schedule);
+ MCDRV_DBG(mcd,
+ "MobiCore Request Sleep=%d!", flags->sleep_mode.sleep_req);
+ MCDRV_DBG(mcd,
+ "MobiCore Sleep Ready=%d!", flags->sleep_mode.ready_to_sleep);
+}
+
+static int mc_suspend_notifier(struct notifier_block *nb,
+ unsigned long event, void *dummy)
+{
+ struct mc_mcp_buffer *mcp = ctx->mcp;
+ /* We have noting to say if MobiCore is not initialized */
+ if (!mcp)
+ return 0;
+
+#ifdef MC_MEM_TRACES
+ mobicore_log_read();
+#endif
+
+ switch (event) {
+ case PM_SUSPEND_PREPARE:
+ /*
+ * Make sure we have finished all the work otherwise
+ * we end up in a race condition
+ */
+ cancel_work_sync(&suspend_work);
+ /*
+ * We can't go to sleep if MobiCore is not IDLE
+ * or not Ready to sleep
+ */
+ dump_sleep_params(&mcp->flags);
+ if (!sleep_ready()) {
+ ctx->mcp->flags.sleep_mode.sleep_req = REQ_TO_SLEEP;
+ schedule_work_on(0, &suspend_work);
+ flush_work(&suspend_work);
+ if (!sleep_ready()) {
+ dump_sleep_params(&mcp->flags);
+ ctx->mcp->flags.sleep_mode.sleep_req = 0;
+ MCDRV_DBG_ERROR(mcd, "MobiCore can't SLEEP!");
+ return NOTIFY_BAD;
+ }
+ }
+ break;
+ case PM_POST_SUSPEND:
+ MCDRV_DBG(mcd, "Resume MobiCore system!");
+ ctx->mcp->flags.sleep_mode.sleep_req = 0;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static struct notifier_block mc_notif_block = {
+ .notifier_call = mc_suspend_notifier,
+};
+
+#ifdef MC_BL_NOTIFIER
+
+static int bl_switcher_notifier_handler(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ unsigned int mpidr, cpu, cluster;
+ struct mc_mcp_buffer *mcp = ctx->mcp;
+
+ if (!mcp)
+ return 0;
+
+ asm volatile ("mrc\tp15, 0, %0, c0, c0, 5" : "=r" (mpidr));
+ cpu = mpidr & 0x3;
+ cluster = (mpidr >> 8) & 0xf;
+ MCDRV_DBG(mcd, "%s switching!!, cpu: %u, Out=%u",
+ (event == SWITCH_ENTER ? "Before" : "After"), cpu, cluster);
+
+ if (cpu != 0)
+ return 0;
+
+ switch (event) {
+ case SWITCH_ENTER:
+ if (!sleep_ready()) {
+ ctx->mcp->flags.sleep_mode.sleep_req = REQ_TO_SLEEP;
+ _nsiq();
+ /* By this time we should be ready for sleep or we are
+ * in the middle of something important */
+ if (!sleep_ready()) {
+ dump_sleep_params(&mcp->flags);
+ MCDRV_DBG(mcd,
+ "MobiCore: Don't allow switch!");
+ ctx->mcp->flags.sleep_mode.sleep_req = 0;
+ return -EPERM;
+ }
+ }
+ break;
+ case SWITCH_EXIT:
+ ctx->mcp->flags.sleep_mode.sleep_req = 0;
+ break;
+ default:
+ MCDRV_DBG(mcd, "MobiCore: Unknown switch event!");
+ }
+
+ return 0;
+}
+
+static struct notifier_block switcher_nb = {
+ .notifier_call = bl_switcher_notifier_handler,
+};
+#endif
+
+int mc_pm_initialize(struct mc_context *context)
+{
+ int ret = 0;
+
+ ctx = context;
+
+ ret = register_pm_notifier(&mc_notif_block);
+ if (ret)
+ MCDRV_DBG_ERROR(mcd, "device pm register failed");
+#ifdef MC_BL_NOTIFIER
+ if (register_bL_swicher_notifier(&switcher_nb))
+ MCDRV_DBG_ERROR(mcd,
+ "Failed to register to bl_switcher_notifier");
+#endif
+
+ return ret;
+}
+
+int mc_pm_free(void)
+{
+ int ret = unregister_pm_notifier(&mc_notif_block);
+ if (ret)
+ MCDRV_DBG_ERROR(mcd, "device pm unregister failed");
+#ifdef MC_BL_NOTIFIER
+ ret = unregister_bL_swicher_notifier(&switcher_nb);
+ if (ret)
+ MCDRV_DBG_ERROR(mcd, "device bl unregister failed");
+#endif
+ return ret;
+}
+
+bool mc_pm_sleep_ready(void)
+{
+ if (ctx == 0)
+ return true;
+ return sleep_ready();
+}
+#endif /* MC_PM_RUNTIME */
+
+#ifdef MC_CRYPTO_CLOCK_MANAGEMENT
+
+int mc_pm_clock_initialize(void)
+{
+ int ret = 0;
+
+ /* Get core clk */
+ mc_ce_core_clk = clk_get(mcd, "core_clk");
+ if (IS_ERR(mc_ce_core_clk)) {
+ ret = PTR_ERR(mc_ce_core_clk);
+ MCDRV_DBG_ERROR(mcd, "cannot get core clock");
+ goto error;
+ }
+ /* Get Interface clk */
+ mc_ce_iface_clk = clk_get(mcd, "iface_clk");
+ if (IS_ERR(mc_ce_iface_clk)) {
+ clk_put(mc_ce_core_clk);
+ ret = PTR_ERR(mc_ce_iface_clk);
+ MCDRV_DBG_ERROR(mcd, "cannot get iface clock");
+ goto error;
+ }
+ /* Get AXI clk */
+ mc_ce_bus_clk = clk_get(mcd, "bus_clk");
+ if (IS_ERR(mc_ce_bus_clk)) {
+ clk_put(mc_ce_iface_clk);
+ clk_put(mc_ce_core_clk);
+ ret = PTR_ERR(mc_ce_bus_clk);
+ MCDRV_DBG_ERROR(mcd, "cannot get AXI bus clock");
+ goto error;
+ }
+ return ret;
+
+error:
+ mc_ce_core_clk = NULL;
+ mc_ce_iface_clk = NULL;
+ mc_ce_bus_clk = NULL;
+
+ return ret;
+}
+
+void mc_pm_clock_finalize(void)
+{
+ if (mc_ce_iface_clk != NULL)
+ clk_put(mc_ce_iface_clk);
+
+ if (mc_ce_core_clk != NULL)
+ clk_put(mc_ce_core_clk);
+
+ if (mc_ce_bus_clk != NULL)
+ clk_put(mc_ce_bus_clk);
+}
+
+int mc_pm_clock_enable(void)
+{
+ int rc = 0;
+
+ rc = clk_prepare_enable(mc_ce_core_clk);
+ if (rc) {
+ MCDRV_DBG_ERROR(mcd, "cannot enable clock");
+ } else {
+ rc = clk_prepare_enable(mc_ce_iface_clk);
+ if (rc) {
+ clk_disable_unprepare(mc_ce_core_clk);
+ MCDRV_DBG_ERROR(mcd, "cannot enable clock");
+ } else {
+ rc = clk_prepare_enable(mc_ce_bus_clk);
+ if (rc) {
+ clk_disable_unprepare(mc_ce_iface_clk);
+ MCDRV_DBG_ERROR(mcd, "cannot enable clock");
+ }
+ }
+ }
+ return rc;
+}
+
+void mc_pm_clock_disable(void)
+{
+ if (mc_ce_iface_clk != NULL)
+ clk_disable_unprepare(mc_ce_iface_clk);
+
+ if (mc_ce_core_clk != NULL)
+ clk_disable_unprepare(mc_ce_core_clk);
+
+ if (mc_ce_bus_clk != NULL)
+ clk_disable_unprepare(mc_ce_bus_clk);
+}
+
+#endif /* MC_CRYPTO_CLOCK_MANAGEMENT */
diff --git a/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/pm.h b/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/pm.h
new file mode 100644
index 000000000..6581425a7
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/pm.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2013 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+#ifndef _MC_PM_H_
+#define _MC_PM_H_
+
+#include "main.h"
+#ifdef MC_BL_NOTIFIER
+#include <asm/bL_switcher.h>
+#endif
+
+
+#define NO_SLEEP_REQ 0
+#define REQ_TO_SLEEP 1
+
+#define NORMAL_EXECUTION 0
+#define READY_TO_SLEEP 1
+
+/* How much time after resume the daemon should backoff */
+#define DAEMON_BACKOFF_TIME 500
+
+/* Initialize Power Management */
+int mc_pm_initialize(struct mc_context *context);
+/* Free all Power Management resources*/
+int mc_pm_free(void);
+/* Initialize secure crypto clocks */
+int mc_pm_clock_initialize(void);
+/* Free secure crypto clocks */
+void mc_pm_clock_finalize(void);
+/* Enable secure crypto clocks */
+int mc_pm_clock_enable(void);
+/* Disable secure crypto clocks */
+void mc_pm_clock_disable(void);
+/* Test if sleep is possible */
+bool mc_pm_sleep_ready(void);
+
+#endif /* _MC_PM_H_ */
diff --git a/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/public/mc_kernel_api.h b/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/public/mc_kernel_api.h
new file mode 100644
index 000000000..96805fda1
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/public/mc_kernel_api.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2013 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+/*
+ * Interface to be used by module MobiCoreKernelAPI.
+ */
+#ifndef _MC_KERNEL_API_H_
+#define _MC_KERNEL_API_H_
+
+struct mc_instance;
+
+/*
+ * mobicore_open() - Initialize a new MobiCore API instance object
+ *
+ * Returns a MobiCore Instance or NULL if no allocation was possible.
+ */
+struct mc_instance *mobicore_open(void);
+
+/*
+ * mobicore_release() - Release a MobiCore instance object
+ * @instance: MobiCore instance
+ *
+ * Returns 0 if Ok or -E ERROR
+ */
+int mobicore_release(struct mc_instance *instance);
+
+/*
+ * mobicore_allocate_wsm() - Allocate MobiCore WSM
+ * @instance: instance data for MobiCore Daemon and TLCs
+ * @requested_size: memory size requested in bytes
+ * @handle: pointer to handle
+ * @kernel_virt_addr: virtual user start address
+ *
+ * Returns 0 if OK
+ */
+int mobicore_allocate_wsm(struct mc_instance *instance,
+ unsigned long requested_size, uint32_t *handle,
+ void **virt_kernel_addr);
+
+/*
+ * mobicore_free() - Free a WSM buffer allocated with mobicore_allocate_wsm
+ * @instance: instance data for MobiCore Daemon and TLCs
+ * @handle: handle of the buffer
+ *
+ * Returns 0 if OK
+ */
+int mobicore_free_wsm(struct mc_instance *instance, uint32_t handle);
+
+/*
+ * mobicore_map_vmem() - Map a virtual memory buffer structure to Mobicore
+ * @instance: instance data for MobiCore Daemon and TLCs
+ * @addr: address of the buffer (NB it must be kernel virtual!)
+ * @len: buffer length (in bytes)
+ * @handle: unique handle
+ *
+ * Returns 0 if no error
+ */
+int mobicore_map_vmem(struct mc_instance *instance, void *addr,
+ uint32_t len, uint32_t *handle);
+
+/*
+ * mobicore_unmap_vmem() - Unmap a virtual memory buffer from MobiCore
+ * @instance: instance data for MobiCore Daemon and TLCs
+ * @handle: unique handle
+ *
+ * Returns 0 if no error
+ */
+int mobicore_unmap_vmem(struct mc_instance *instance, uint32_t handle);
+
+/*
+ * mobicore_sleep_ready() - Test if mobicore can sleep
+ *
+ * Returns true if mobicore can sleep, false if it can't sleep
+ */
+bool mobicore_sleep_ready(void);
+
+
+#endif /* _MC_KERNEL_API_H_ */
diff --git a/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/public/mc_linux.h b/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/public/mc_linux.h
new file mode 100644
index 000000000..b9c4934d5
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/public/mc_linux.h
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2013-2014 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+/*
+ * The <t-base Driver Kernel Module is a Linux device driver, which represents
+ * the command proxy on the lowest layer to the secure world (Swd). Additional
+ * services like memory allocation via mmap and generation of a MMU tables for
+ * given virtual memory are also supported. IRQ functionality receives
+ * information from the SWd in the non secure world (NWd).
+ * As customary the driver is handled as Linux device driver with "open",
+ * "close" and "ioctl" commands. Access to the driver is possible after the
+ * devices "/dev/mobicore" and "/dev/mobicore-user" have been created.
+ */
+
+#ifndef _MC_LINUX_H_
+#define _MC_LINUX_H_
+
+#include "version.h"
+
+#ifndef __KERNEL__
+#include <stdint.h>
+#endif
+
+#define MC_ADMIN_DEVNODE "mobicore"
+#define MC_USER_DEVNODE "mobicore-user"
+
+/*
+ * Data exchange structure of the MC_DRV_MODULE_INIT ioctl command.
+ * INIT request data to SWD
+ */
+struct mc_ioctl_init {
+ /* length of notification queue */
+ uint32_t nq_length;
+ /* mcp buffer start/length [16:16] [start, length] */
+ uint32_t mcp_offset;
+ /* length of mcp buffer */
+ uint32_t mcp_length;
+};
+
+/*
+ * Data exchange structure of the MC_DRV_MODULE_INFO ioctl command.
+ * INFO request data to the SWD
+ */
+struct mc_ioctl_info {
+ uint32_t ext_info_id; /* extended info ID */
+ uint32_t state; /* state */
+ uint32_t ext_info; /* extended info */
+};
+
+/*
+ * Data exchange structure of the MC_IO_MAP_WSM and MC_IO_MAP_MCI commands.
+ *
+ * Allocate a contiguous memory buffer for a process.
+ * The physical address can be used as for later calls to mmap.
+ * The handle can be used to communicate about this buffer to the Daemon.
+ * For MC_IO_MAP_MCI command, the reused field indicates that MCI was set up
+ * already. I.e. Daemon was restarted.
+ */
+struct mc_ioctl_map {
+ uint32_t len; /* Buffer length */
+ uint32_t handle; /* WSM handle */
+ uint64_t phys_addr; /* physical address of WSM (or 0) */
+ uint32_t rfu;
+ bool reused; /* if WSM memory was reused, or new allocated */
+};
+
+/*
+ * Data exchange structure of the MC_IO_REG_WSM command.
+ *
+ * Allocates a physical MMU table and maps the buffer into this page.
+ * Returns the physical address of the MMU table.
+ * The page alignment will be created and the appropriated pSize and pOffsetMMU
+ * will be modified to the used values.
+ *
+ * We assume the 64 bit compatible one to be the default and the
+ * 32 bit one to be the compat one but we must serve both of them.
+ */
+struct mc_compat_ioctl_reg_wsm {
+ uint32_t buffer; /* base address of the virtual address */
+ uint32_t len; /* size of the virtual address space */
+ uint32_t pid; /* process id */
+ uint32_t handle; /* driver handle for locked memory */
+ uint64_t table_phys; /* physical address of the MMU table */
+};
+
+struct mc_ioctl_reg_wsm {
+ uint64_t buffer; /* base address of the virtual address */
+ uint32_t len; /* size of the virtual address space */
+ uint32_t pid; /* process id */
+ uint32_t handle; /* driver handle for locked memory */
+ uint64_t table_phys;/* physical address of the MMU table */
+};
+
+/*
+ * Data exchange structure of the MC_IO_RESOLVE_CONT_WSM ioctl command.
+ */
+struct mc_ioctl_resolv_cont_wsm {
+ /* driver handle for buffer */
+ uint32_t handle;
+ /* length memory */
+ uint32_t length;
+ /* base address of memory */
+ uint64_t phys;
+ /* fd to owner of the buffer */
+ int32_t fd;
+};
+
+/*
+ * Data exchange structure of the MC_IO_RESOLVE_WSM ioctl command.
+ */
+struct mc_ioctl_resolv_wsm {
+ /* driver handle for buffer */
+ uint32_t handle;
+ /* fd to owner of the buffer */
+ int32_t fd;
+ /* base address of memory */
+ uint64_t phys;
+};
+
+
+/*
+ * defines for the ioctl mobicore driver module function call from user space.
+ */
+/* MobiCore IOCTL magic number */
+#define MC_IOC_MAGIC 'M'
+
+#define MC_IO_INIT _IOWR(MC_IOC_MAGIC, 0, struct mc_ioctl_init)
+#define MC_IO_INFO _IOWR(MC_IOC_MAGIC, 1, struct mc_ioctl_info)
+#define MC_IO_VERSION _IOR(MC_IOC_MAGIC, 2, uint32_t)
+/*
+ * ioctl parameter to send the YIELD command to the SWD.
+ * Only possible in Privileged Mode.
+ * ioctl(fd, MC_DRV_MODULE_YIELD)
+ */
+#define MC_IO_YIELD _IO(MC_IOC_MAGIC, 3)
+/*
+ * ioctl parameter to send the NSIQ signal to the SWD.
+ * Only possible in Privileged Mode
+ * ioctl(fd, MC_DRV_MODULE_NSIQ)
+ */
+#define MC_IO_NSIQ _IO(MC_IOC_MAGIC, 4)
+/*
+ * Free's memory which is formerly allocated by the driver's mmap
+ * command. The parameter must be this mmaped address.
+ * The internal instance data regarding to this address are deleted as
+ * well as each according memory page and its appropriated reserved bit
+ * is cleared (ClearPageReserved).
+ * Usage: ioctl(fd, MC_DRV_MODULE_FREE, &address) with address being of
+ * type long address
+ */
+#define MC_IO_FREE _IO(MC_IOC_MAGIC, 5)
+/*
+ * Creates a MMU Table of the given base address and the size of the
+ * data.
+ * Parameter: mc_ioctl_reg_wsm
+ *
+ * Since the end ID is also based on the size of the structure it is
+ * safe to use the same ID(6) for both
+ */
+#define MC_IO_REG_WSM _IOWR(MC_IOC_MAGIC, 6, struct mc_ioctl_reg_wsm)
+#define MC_COMPAT_REG_WSM _IOWR(MC_IOC_MAGIC, 6, \
+ struct mc_compat_ioctl_reg_wsm)
+
+#define MC_IO_UNREG_WSM _IO(MC_IOC_MAGIC, 7)
+#define MC_IO_LOCK_WSM _IO(MC_IOC_MAGIC, 8)
+#define MC_IO_UNLOCK_WSM _IO(MC_IOC_MAGIC, 9)
+
+/*
+ * Allocate contiguous memory for a process for later mapping with mmap.
+ * MC_IO_MAP_WSM usual operation, pages are registered in
+ * device structure and freed later.
+ * MC_IO_MAP_MCI get Instance of MCI, allocates or mmaps
+ * the MCI to daemon
+ */
+#define MC_IO_MAP_WSM _IOWR(MC_IOC_MAGIC, 11, struct mc_ioctl_map)
+#define MC_IO_MAP_MCI _IOWR(MC_IOC_MAGIC, 12, struct mc_ioctl_map)
+
+/*
+ * Clean orphaned WSM buffers. Only available to the daemon and should
+ * only be carried out if the TLC crashes or otherwise calls exit() in
+ * an unexpected manner.
+ * The clean is needed together with the lock/unlock mechanism so the daemon
+ * has clear control of the mapped buffers so it can close a Trustlet before
+ * release all the WSM buffers, otherwise the Trustlet would be able to write
+ * to possibly kernel memory areas
+ */
+#define MC_IO_CLEAN_WSM _IO(MC_IOC_MAGIC, 14)
+
+/*
+ * Get MMU phys address of a buffer handle allocated to the user.
+ * Only available to the daemon.
+ */
+#define MC_IO_RESOLVE_WSM _IOWR(MC_IOC_MAGIC, 15, \
+ struct mc_ioctl_resolv_wsm)
+
+/*
+ * Get the phys address & length of a allocated contiguous buffer.
+ * Only available to the daemon */
+#define MC_IO_RESOLVE_CONT_WSM _IOWR(MC_IOC_MAGIC, 16, \
+ struct mc_ioctl_resolv_cont_wsm)
+
+/*
+ * Setup the mem traces when called.
+ * Only available to the daemon */
+#define MC_IO_LOG_SETUP _IO(MC_IOC_MAGIC, 17)
+
+#endif /* _MC_LINUX_H_ */
diff --git a/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/public/version.h b/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/public/version.h
new file mode 100644
index 000000000..8db48a09b
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/MobiCoreDriver/public/version.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2013-2014 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+#ifndef _MC_DRV_VERSION_H_
+#define _MC_DRV_VERSION_H_
+
+#define MCDRVMODULEAPI_VERSION_MAJOR 1
+#define MCDRVMODULEAPI_VERSION_MINOR 1
+
+#endif /* _MC_DRV_VERSION_H_ */
diff --git a/drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/clientlib.c b/drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/clientlib.c
new file mode 100644
index 000000000..17ecd5e82
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/clientlib.c
@@ -0,0 +1,1079 @@
+/*
+ * Copyright (c) 2013-2015 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/netlink.h>
+#include <net/sock.h>
+#include <net/net_namespace.h>
+#include <linux/list.h>
+
+#include "public/mobicore_driver_api.h"
+#include "public/mobicore_driver_cmd.h"
+#include "include/mcinq.h"
+#include "device.h"
+#include "session.h"
+
+/* device list */
+LIST_HEAD(devices);
+/* lock used to prevent concurrent add/delete action on the device list */
+struct mutex device_mutex;
+atomic_t device_usage = ATOMIC_INIT(0);
+
+static struct mcore_device_t *resolve_device_id(uint32_t device_id)
+{
+ struct mcore_device_t *tmp;
+ struct list_head *pos;
+
+ /* Get mcore_device_t for device_id */
+ mutex_lock(&device_mutex);
+ list_for_each(pos, &devices) {
+ tmp = list_entry(pos, struct mcore_device_t, list);
+ if (tmp->device_id == device_id) {
+ mutex_unlock(&device_mutex);
+ return tmp;
+ }
+ }
+ mutex_unlock(&device_mutex);
+ return NULL;
+}
+
+static void add_device(struct mcore_device_t *device)
+{
+ mutex_lock(&device_mutex);
+ list_add_tail(&(device->list), &devices);
+ mutex_unlock(&device_mutex);
+}
+
+static bool remove_device(uint32_t device_id)
+{
+ struct mcore_device_t *device, *candidate = NULL;
+ struct list_head *pos, *q;
+ bool found = false;
+
+ mutex_lock(&device_mutex);
+ list_for_each_safe(pos, q, &devices) {
+ device = list_entry(pos, struct mcore_device_t, list);
+ if (device->device_id == device_id) {
+ list_del(pos);
+ candidate = device;
+ found = true;
+ break;
+ }
+ }
+ mutex_unlock(&device_mutex);
+ if (!candidate)
+ mcore_device_cleanup(candidate);
+ return found;
+}
+
+enum mc_result mc_open_device(uint32_t device_id)
+{
+ enum mc_result mc_result = MC_DRV_OK;
+ struct connection *dev_con = NULL;
+
+ MCDRV_DBG_VERBOSE(mc_kapi, "===%s()===", __func__);
+
+ do {
+ struct mcore_device_t *device = resolve_device_id(device_id);
+ if (device != NULL) {
+ MCDRV_DBG(mc_kapi,
+ "Device %d already opened\n", device_id);
+ atomic_inc(&device_usage);
+ mc_result = MC_DRV_OK;
+ break;
+ }
+
+ /* Open new connection to device */
+ dev_con = connection_new();
+ if (dev_con == NULL) {
+ mc_result = MC_DRV_ERR_NO_FREE_MEMORY;
+ break;
+ }
+
+ if (!connection_connect(dev_con, MC_DAEMON_PID)) {
+ MCDRV_DBG_ERROR(
+ mc_kapi,
+ "Could not setup netlink connection to PID %u",
+ MC_DAEMON_PID);
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ break;
+ }
+
+ /* Forward device open to the daemon and read result */
+ struct mc_drv_cmd_open_device_t mc_drv_cmd_open_device = {
+ {
+ MC_DRV_CMD_OPEN_DEVICE
+ },
+ {
+ device_id
+ }
+ };
+
+ int len = connection_write_data(
+ dev_con,
+ &mc_drv_cmd_open_device,
+ sizeof(struct mc_drv_cmd_open_device_t));
+ if (len < 0) {
+ MCDRV_DBG_ERROR(mc_kapi,
+ "CMD_OPEN_DEVICE writeCmd failed %d",
+ len);
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ break;
+ }
+
+ struct mc_drv_response_header_t rsp_header;
+ memset(&rsp_header, 0, sizeof(rsp_header));
+ len = connection_read_datablock(
+ dev_con,
+ &rsp_header,
+ sizeof(rsp_header));
+ if (len != sizeof(rsp_header)) {
+ MCDRV_DBG_ERROR(mc_kapi,
+ "CMD_OPEN_DEVICE readRsp failed %d",
+ len);
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ break;
+ }
+ if (rsp_header.response_id != MC_DRV_RSP_OK) {
+ MCDRV_DBG_ERROR(mc_kapi,
+ "CMD_OPEN_DEVICE failed, respId=%d",
+ rsp_header.response_id);
+ switch (rsp_header.response_id) {
+ case MC_DRV_RSP_PAYLOAD_LENGTH_ERROR:
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ break;
+ case MC_DRV_INVALID_DEVICE_NAME:
+ mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
+ break;
+ case MC_DRV_RSP_DEVICE_ALREADY_OPENED:
+ default:
+ mc_result = MC_DRV_ERR_INVALID_OPERATION;
+ break;
+ }
+ break;
+ }
+
+ /* there is no payload to read */
+
+ device = mcore_device_create(device_id, dev_con);
+ if (device == NULL) {
+ mc_result = MC_DRV_ERR_NO_FREE_MEMORY;
+ break;
+ }
+ if (!mcore_device_open(device, MC_DRV_MOD_DEVNODE_FULLPATH)) {
+ mcore_device_cleanup(device);
+ MCDRV_DBG_ERROR(mc_kapi,
+ "could not open device file: %s",
+ MC_DRV_MOD_DEVNODE_FULLPATH);
+ mc_result = MC_DRV_ERR_INVALID_DEVICE_FILE;
+ break;
+ }
+
+ add_device(device);
+ atomic_inc(&device_usage);
+
+ } while (false);
+
+ if (mc_result != MC_DRV_OK)
+ connection_cleanup(dev_con);
+
+ return mc_result;
+}
+EXPORT_SYMBOL(mc_open_device);
+
+enum mc_result mc_close_device(uint32_t device_id)
+{
+ enum mc_result mc_result = MC_DRV_OK;
+
+ MCDRV_DBG_VERBOSE(mc_kapi, "===%s()===", __func__);
+
+ do {
+ struct mcore_device_t *device = resolve_device_id(device_id);
+ if (device == NULL) {
+ MCDRV_DBG_ERROR(mc_kapi, "Device not found");
+ mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
+ break;
+ }
+ /* Check if it's not used by other modules */
+ if (!atomic_dec_and_test(&device_usage)) {
+ mc_result = MC_DRV_OK;
+ break;
+ }
+
+ struct connection *dev_con = device->connection;
+
+ /* Return if not all sessions have been closed */
+ if (mcore_device_has_sessions(device)) {
+ MCDRV_DBG_ERROR(mc_kapi,
+ "cannot close with sessions pending");
+ mc_result = MC_DRV_ERR_SESSION_PENDING;
+ break;
+ }
+
+ struct mc_drv_cmd_close_device_t mc_drv_cmd_close_device = {
+ {
+ MC_DRV_CMD_CLOSE_DEVICE
+ }
+ };
+ int len = connection_write_data(
+ dev_con,
+ &mc_drv_cmd_close_device,
+ sizeof(struct mc_drv_cmd_close_device_t));
+ /* ignore error, but log details */
+ if (len < 0) {
+ MCDRV_DBG_ERROR(mc_kapi,
+ "CMD_CLOSE_DEVICE writeCmd failed %d",
+ len);
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ }
+
+ struct mc_drv_response_header_t rsp_header;
+ memset(&rsp_header, 0, sizeof(rsp_header));
+ len = connection_read_datablock(
+ dev_con,
+ &rsp_header,
+ sizeof(rsp_header));
+ if (len != sizeof(rsp_header)) {
+ MCDRV_DBG_ERROR(mc_kapi,
+ "CMD_CLOSE_DEVICE readResp failed %d",
+ len);
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ break;
+ }
+
+ if (rsp_header.response_id != MC_DRV_RSP_OK) {
+ MCDRV_DBG_ERROR(mc_kapi,
+ "CMD_CLOSE_DEVICE failed, respId=%d",
+ rsp_header.response_id);
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ break;
+ }
+
+ remove_device(device_id);
+
+ } while (false);
+
+ return mc_result;
+}
+EXPORT_SYMBOL(mc_close_device);
+
+enum mc_result mc_open_session(struct mc_session_handle *session,
+ const struct mc_uuid_t *uuid,
+ uint8_t *tci, uint32_t tci_len)
+{
+ enum mc_result mc_result = MC_DRV_OK;
+
+ MCDRV_DBG_VERBOSE(mc_kapi, "===%s()===", __func__);
+
+ do {
+ if (session == NULL) {
+ MCDRV_DBG_ERROR(mc_kapi, "Session is null");
+ mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+ break;
+ }
+ if (uuid == NULL) {
+ MCDRV_DBG_ERROR(mc_kapi, "UUID is null");
+ mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+ break;
+ }
+ if (tci == NULL) {
+ MCDRV_DBG_ERROR(mc_kapi, "TCI is null");
+ mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+ break;
+ }
+ if (tci_len > MC_MAX_TCI_LEN) {
+ MCDRV_DBG_ERROR(mc_kapi, "TCI length is longer than %d",
+ MC_MAX_TCI_LEN);
+ mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+ break;
+ }
+
+ /* Get the device associated with the given session */
+ struct mcore_device_t *device =
+ resolve_device_id(session->device_id);
+ if (device == NULL) {
+ MCDRV_DBG_ERROR(mc_kapi, "Device not found");
+ mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
+ break;
+ }
+ struct connection *dev_con = device->connection;
+
+ /* Get the wsm of the given TCI */
+ struct wsm *wsm =
+ mcore_device_find_contiguous_wsm(device, tci);
+ if (wsm == NULL) {
+ MCDRV_DBG_ERROR(mc_kapi,
+ "Could not resolve TCI address ");
+ mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+ break;
+ }
+
+ if (wsm->len < tci_len) {
+ MCDRV_DBG_ERROR(mc_kapi,
+ "length is more than allocated TCI");
+ mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+ break;
+ }
+
+ /* Prepare open session command */
+ struct mc_drv_cmd_open_session_t cmd_open_session = {
+ {
+ MC_DRV_CMD_OPEN_SESSION
+ },
+ {
+ session->device_id,
+ *uuid,
+ (uint32_t)((uintptr_t)(wsm->virt_addr) & 0xFFF),
+ wsm->handle,
+ tci_len
+ }
+ };
+
+ /* Transmit command data */
+ int len = connection_write_data(dev_con,
+ &cmd_open_session,
+ sizeof(cmd_open_session));
+ if (len != sizeof(cmd_open_session)) {
+ MCDRV_DBG_ERROR(mc_kapi,
+ "CMD_OPEN_SESSION writeData failed %d",
+ len);
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ break;
+ }
+
+ /* Read command response */
+ struct mc_drv_rsp_open_session_t rsp_open_session;
+ memset(&rsp_open_session, 0, sizeof(rsp_open_session));
+
+ /* read whole response, to prevent being interrupted
+ between header and payload */
+ len = connection_read_datablock(dev_con,
+ &rsp_open_session,
+ sizeof(rsp_open_session));
+
+ /* header processing */
+ if (len < sizeof(rsp_open_session.header)) {
+ MCDRV_DBG_ERROR(mc_kapi,
+ "CMD_OPEN_SESSION readResp failed %d",
+ len);
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ break;
+ }
+
+ if (rsp_open_session.header.response_id != MC_DRV_RSP_OK) {
+ MCDRV_DBG_ERROR(mc_kapi,
+ "CMD_OPEN_SESSION failed, respId=%d",
+ rsp_open_session.header.response_id);
+ switch (rsp_open_session.header.response_id) {
+ case MC_DRV_RSP_TRUSTLET_NOT_FOUND:
+ mc_result = MC_DRV_ERR_INVALID_DEVICE_FILE;
+ break;
+ case MC_DRV_RSP_PAYLOAD_LENGTH_ERROR:
+ case MC_DRV_RSP_DEVICE_NOT_OPENED:
+ case MC_DRV_RSP_FAILED:
+ default:
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ break;
+ }
+ break;
+ }
+
+ /* payload */
+ len -= sizeof(rsp_open_session.header);
+ if (len != sizeof(rsp_open_session.payload)) {
+ MCDRV_DBG_ERROR(mc_kapi,
+ "CMD_OPEN_SESSION readPayload fail %d",
+ len);
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ break;
+ }
+
+ /* Register session with handle */
+ session->session_id = rsp_open_session.payload.session_id;
+
+ /* Set up second channel for notifications */
+ struct connection *session_connection = connection_new();
+ if (session_connection == NULL) {
+ mc_result = MC_DRV_ERR_NO_FREE_MEMORY;
+ break;
+ }
+
+ if (!connection_connect(session_connection, MC_DAEMON_PID)) {
+ MCDRV_DBG_ERROR(
+ mc_kapi,
+ "Could not setup netlink connection to PID %u",
+ MC_DAEMON_PID);
+ connection_cleanup(session_connection);
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ break;
+ }
+
+ /* Write command to use channel for notifications */
+ struct mc_drv_cmd_nqconnect_t cmd_nqconnect = {
+ {
+ MC_DRV_CMD_NQ_CONNECT
+ },
+ {
+ session->device_id,
+ session->session_id,
+ rsp_open_session.payload.device_session_id,
+ rsp_open_session.payload.session_magic
+ }
+ };
+ connection_write_data(session_connection,
+ &cmd_nqconnect,
+ sizeof(cmd_nqconnect));
+
+ /* Read command response, header first */
+ struct mc_drv_response_header_t rsp_header;
+ len = connection_read_datablock(session_connection,
+ &rsp_header,
+ sizeof(rsp_header));
+ if (len != sizeof(rsp_header)) {
+ MCDRV_DBG_ERROR(mc_kapi,
+ "CMD_NQ_CONNECT readRsp failed %d",
+ len);
+ connection_cleanup(session_connection);
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ break;
+ }
+
+ if (rsp_header.response_id != MC_DRV_RSP_OK) {
+ MCDRV_DBG_ERROR(mc_kapi,
+ "CMD_NQ_CONNECT failed, respId=%d",
+ rsp_header.response_id);
+ connection_cleanup(session_connection);
+ mc_result = MC_DRV_ERR_NQ_FAILED;
+ break;
+ }
+
+ /* there is no payload. */
+
+ /* Session established, new session object must be created */
+ if (!mcore_device_create_new_session(device,
+ session->session_id,
+ session_connection)) {
+ connection_cleanup(session_connection);
+ mc_result = MC_DRV_ERR_NO_FREE_MEMORY;
+ break;
+ }
+
+ } while (false);
+
+ return mc_result;
+}
+EXPORT_SYMBOL(mc_open_session);
+
+enum mc_result mc_close_session(struct mc_session_handle *session)
+{
+ enum mc_result mc_result = MC_DRV_OK;
+
+ MCDRV_DBG_VERBOSE(mc_kapi, "===%s()===", __func__);
+
+ do {
+ if (session == NULL) {
+ MCDRV_DBG_ERROR(mc_kapi, "Session is null");
+ mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+ break;
+ }
+
+ struct mcore_device_t *device =
+ resolve_device_id(session->device_id);
+ if (device == NULL) {
+ MCDRV_DBG_ERROR(mc_kapi, "Device not found");
+ mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
+ break;
+ }
+ struct connection *dev_con = device->connection;
+
+ struct session *nq_session =
+ mcore_device_resolve_session_id(device,
+ session->session_id);
+
+ if (nq_session == NULL) {
+ MCDRV_DBG_ERROR(mc_kapi, "Session not found");
+ mc_result = MC_DRV_ERR_UNKNOWN_SESSION;
+ break;
+ }
+
+ /* Write close session command */
+ struct mc_drv_cmd_close_session_t cmd_close_session = {
+ {
+ MC_DRV_CMD_CLOSE_SESSION
+ },
+ {
+ session->session_id,
+ }
+ };
+ connection_write_data(dev_con,
+ &cmd_close_session,
+ sizeof(cmd_close_session));
+
+ /* Read command response */
+ struct mc_drv_response_header_t rsp_header;
+ memset(&rsp_header, 0, sizeof(rsp_header));
+ int len = connection_read_datablock(dev_con,
+ &rsp_header,
+ sizeof(rsp_header));
+ if (len != sizeof(rsp_header)) {
+ MCDRV_DBG_ERROR(mc_kapi,
+ "CMD_CLOSE_SESSION readRsp failed %d",
+ len);
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ break;
+ }
+
+ if (rsp_header.response_id != MC_DRV_RSP_OK) {
+ MCDRV_DBG_ERROR(mc_kapi,
+ "CMD_CLOSE_SESSION failed, respId=%d",
+ rsp_header.response_id);
+ mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
+ break;
+ }
+
+ mcore_device_remove_session(device, session->session_id);
+ mc_result = MC_DRV_OK;
+
+ } while (false);
+
+ return mc_result;
+}
+EXPORT_SYMBOL(mc_close_session);
+
+enum mc_result mc_notify(struct mc_session_handle *session)
+{
+ enum mc_result mc_result = MC_DRV_OK;
+
+ MCDRV_DBG_VERBOSE(mc_kapi, "===%s()===", __func__);
+
+ do {
+ if (session == NULL) {
+ MCDRV_DBG_ERROR(mc_kapi, "Session is null");
+ mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+ break;
+ }
+
+ struct mcore_device_t *device =
+ resolve_device_id(session->device_id);
+ if (device == NULL) {
+ MCDRV_DBG_ERROR(mc_kapi, "Device not found");
+ mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
+ break;
+ }
+ struct connection *dev_con = device->connection;
+
+ struct session *nqsession =
+ mcore_device_resolve_session_id(device, session->session_id);
+ if (nqsession == NULL) {
+ MCDRV_DBG_ERROR(mc_kapi, "Session not found");
+ mc_result = MC_DRV_ERR_UNKNOWN_SESSION;
+ break;
+ }
+
+ struct mc_drv_cmd_notify_t cmd_notify = {
+ {
+ MC_DRV_CMD_NOTIFY
+ },
+ {
+ session->session_id
+ }
+ };
+
+ connection_write_data(dev_con,
+ &cmd_notify,
+ sizeof(cmd_notify));
+
+ /* Daemon will not return a response */
+
+ } while (false);
+
+ return mc_result;
+}
+EXPORT_SYMBOL(mc_notify);
+
+enum mc_result mc_wait_notification(struct mc_session_handle *session,
+ int32_t timeout)
+{
+ enum mc_result mc_result = MC_DRV_OK;
+
+ MCDRV_DBG_VERBOSE(mc_kapi, "===%s()===", __func__);
+
+ do {
+ if (session == NULL) {
+ mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+ break;
+ }
+
+ struct mcore_device_t *device =
+ resolve_device_id(session->device_id);
+ if (device == NULL) {
+ MCDRV_DBG_ERROR(mc_kapi, "Device not found");
+ mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
+ break;
+ }
+
+ struct session *nq_session =
+ mcore_device_resolve_session_id(device,
+ session->session_id);
+ if (nq_session == NULL) {
+ MCDRV_DBG_ERROR(mc_kapi, "Session not found");
+ mc_result = MC_DRV_ERR_UNKNOWN_SESSION;
+ break;
+ }
+
+ struct connection *nqconnection =
+ nq_session->notification_connection;
+ uint32_t count = 0;
+
+ /* Read notification queue till it's empty */
+ for (;;) {
+ struct notification notification;
+ memset(&notification, 0, sizeof(notification));
+ ssize_t num_read =
+ connection_read_data(nqconnection,
+ &notification,
+ sizeof(notification),
+ timeout);
+ /*
+ * Exit on timeout in first run. Later runs have
+ * timeout set to 0.
+ * -2 means, there is no more data.
+ */
+ if (count == 0 && num_read == -2) {
+ MCDRV_DBG_ERROR(mc_kapi, "read timeout");
+ mc_result = MC_DRV_ERR_TIMEOUT;
+ break;
+ }
+ /*
+ * After first notification the queue will be
+ * drained, Thus we set no timeout for the
+ * following reads
+ */
+ timeout = 0;
+
+ if (num_read != sizeof(struct notification)) {
+ if (count == 0) {
+ /* failure in first read, notify it */
+ mc_result = MC_DRV_ERR_NOTIFICATION;
+ MCDRV_DBG_ERROR(
+ mc_kapi,
+ "read notification failed, "
+ "%i bytes received", (int)num_read);
+ break;
+ } else {
+ /*
+ * Read of the n-th notification
+ * failed/timeout. We don't tell the
+ * caller, as we got valid notifications
+ * before.
+ */
+ mc_result = MC_DRV_OK;
+ break;
+ }
+ }
+
+ count++;
+ MCDRV_DBG_VERBOSE(mc_kapi,
+ "count=%d, SessionID=%d, Payload=%d",
+ count,
+ notification.session_id,
+ notification.payload);
+
+ if (notification.payload != 0) {
+ /* Session end point died -> store exit code */
+ session_set_error_info(nq_session,
+ notification.payload);
+
+ mc_result = MC_DRV_INFO_NOTIFICATION;
+ break;
+ }
+ } /* for(;;) */
+
+ } while (false);
+
+ return mc_result;
+}
+EXPORT_SYMBOL(mc_wait_notification);
+
+enum mc_result mc_malloc_wsm(uint32_t device_id, uint32_t align, uint32_t len,
+ uint8_t **wsm, uint32_t wsm_flags)
+{
+ enum mc_result mc_result = MC_DRV_ERR_UNKNOWN;
+
+ MCDRV_DBG_VERBOSE(mc_kapi, "===%s()===", __func__);
+
+ do {
+ struct mcore_device_t *device = resolve_device_id(device_id);
+ if (device == NULL) {
+ MCDRV_DBG_ERROR(mc_kapi, "Device not found");
+ mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
+ break;
+ }
+ if (wsm == NULL) {
+ mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+ break;
+ }
+
+ struct wsm *wsm_stack =
+ mcore_device_allocate_contiguous_wsm(device, len);
+ if (wsm_stack == NULL) {
+ MCDRV_DBG_ERROR(mc_kapi, "Allocation of WSM failed");
+ mc_result = MC_DRV_ERR_NO_FREE_MEMORY;
+ break;
+ }
+
+ *wsm = (uint8_t *)wsm_stack->virt_addr;
+ mc_result = MC_DRV_OK;
+
+ } while (false);
+
+ return mc_result;
+}
+EXPORT_SYMBOL(mc_malloc_wsm);
+
+enum mc_result mc_free_wsm(uint32_t device_id, uint8_t *wsm)
+{
+ enum mc_result mc_result = MC_DRV_ERR_UNKNOWN;
+ struct mcore_device_t *device;
+
+
+ MCDRV_DBG_VERBOSE(mc_kapi, "===%s()===", __func__);
+
+ do {
+ /* Get the device associated wit the given session */
+ device = resolve_device_id(device_id);
+ if (device == NULL) {
+ MCDRV_DBG_ERROR(mc_kapi, "Device not found");
+ mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
+ break;
+ }
+
+ /* find WSM object */
+ struct wsm *wsm_stack =
+ mcore_device_find_contiguous_wsm(device, wsm);
+ if (wsm_stack == NULL) {
+ MCDRV_DBG_ERROR(mc_kapi, "unknown address");
+ mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+ break;
+ }
+
+ /* Free the given virtual address */
+ if (!mcore_device_free_contiguous_wsm(device, wsm_stack)) {
+ MCDRV_DBG_ERROR(mc_kapi,
+ "Free of virtual address failed");
+ mc_result = MC_DRV_ERR_FREE_MEMORY_FAILED;
+ break;
+ }
+ mc_result = MC_DRV_OK;
+
+ } while (false);
+
+ return mc_result;
+}
+EXPORT_SYMBOL(mc_free_wsm);
+
+enum mc_result mc_map(struct mc_session_handle *session_handle, void *buf,
+ uint32_t buf_len, struct mc_bulk_map *map_info)
+{
+ enum mc_result mc_result = MC_DRV_ERR_UNKNOWN;
+
+ MCDRV_DBG_VERBOSE(mc_kapi, "===%s()===", __func__);
+
+ do {
+ if (session_handle == NULL) {
+ MCDRV_DBG_ERROR(mc_kapi, "session_handle is null");
+ mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+ break;
+ }
+ if (map_info == NULL) {
+ MCDRV_DBG_ERROR(mc_kapi, "map_info is null");
+ mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+ break;
+ }
+ if (buf == NULL) {
+ MCDRV_DBG_ERROR(mc_kapi, "buf is null");
+ mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+ break;
+ }
+
+ /* Determine device the session belongs to */
+ struct mcore_device_t *device =
+ resolve_device_id(session_handle->device_id);
+
+ if (device == NULL) {
+ MCDRV_DBG_ERROR(mc_kapi, "Device not found");
+ mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
+ break;
+ }
+ struct connection *dev_con = device->connection;
+
+ /* Get session */
+ uint32_t session_id = session_handle->session_id;
+ struct session *session =
+ mcore_device_resolve_session_id(device,
+ session_id);
+ if (session == NULL) {
+ MCDRV_DBG_ERROR(mc_kapi, "Session not found");
+ mc_result = MC_DRV_ERR_UNKNOWN_SESSION;
+ break;
+ }
+
+ /*
+ * Register mapped bulk buffer to Kernel Module and keep mapped
+ * bulk buffer in mind
+ */
+ struct bulk_buffer_descriptor *bulk_buf =
+ session_add_bulk_buf(session, buf, buf_len);
+ if (bulk_buf == NULL) {
+ MCDRV_DBG_ERROR(mc_kapi, "Error mapping bulk buffer");
+ mc_result = MC_DRV_ERR_BULK_MAPPING;
+ break;
+ }
+
+ /* Prepare map command */
+ uintptr_t offset = (uintptr_t)(bulk_buf->virt_addr) & 0xFFF;
+ struct mc_drv_cmd_map_bulk_mem_t mc_drv_cmd_map_bulk_mem = {
+ {
+ MC_DRV_CMD_MAP_BULK_BUF
+ },
+ {
+ session->session_id,
+ bulk_buf->handle,
+ 0,
+ (uint32_t)offset,
+ bulk_buf->len
+ }
+ };
+
+ /* Transmit map command to MobiCore device */
+ connection_write_data(dev_con,
+ &mc_drv_cmd_map_bulk_mem,
+ sizeof(mc_drv_cmd_map_bulk_mem));
+
+ /* Read command response */
+ struct mc_drv_response_header_t rsp_header;
+ memset(&rsp_header, 0, sizeof(rsp_header));
+ int len = connection_read_datablock(dev_con,
+ &rsp_header,
+ sizeof(rsp_header));
+ if (len != sizeof(rsp_header)) {
+ MCDRV_DBG_ERROR(mc_kapi,
+ "CMD_MAP_BULK_BUF readRsp failed %d",
+ len);
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ break;
+ }
+
+ if (rsp_header.response_id != MC_DRV_RSP_OK) {
+ MCDRV_DBG_ERROR(mc_kapi,
+ "CMD_MAP_BULK_BUF failed, respId=%d",
+ rsp_header.response_id);
+
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+
+ /*
+ * Unregister mapped bulk buffer from Kernel Module and
+ * remove mapped bulk buffer from session maintenance
+ */
+ if (!session_remove_bulk_buf(session, buf)) {
+ /* Removing of bulk buffer not possible */
+ MCDRV_DBG_ERROR(mc_kapi,
+ "Unreg of bulk memory failed");
+ }
+ break;
+ }
+
+ struct mc_drv_rsp_map_bulk_mem_payload_t
+ rsp_map_bulk_mem_payload;
+ memset(&rsp_map_bulk_mem_payload, 0,
+ sizeof(rsp_map_bulk_mem_payload));
+ connection_read_datablock(dev_con,
+ &rsp_map_bulk_mem_payload,
+ sizeof(rsp_map_bulk_mem_payload));
+
+ /* Set mapping info for Trustlet */
+ map_info->secure_virt_addr =
+ rsp_map_bulk_mem_payload.secure_virtual_adr;
+ map_info->secure_virt_len = buf_len;
+ mc_result = MC_DRV_OK;
+
+ } while (false);
+
+ return mc_result;
+}
+EXPORT_SYMBOL(mc_map);
+
+enum mc_result mc_unmap(struct mc_session_handle *session_handle, void *buf,
+ struct mc_bulk_map *map_info)
+{
+ enum mc_result mc_result = MC_DRV_ERR_UNKNOWN;
+
+ MCDRV_DBG_VERBOSE(mc_kapi, "===%s()===", __func__);
+
+ do {
+ if (session_handle == NULL) {
+ MCDRV_DBG_ERROR(mc_kapi, "session_handle is null");
+ mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+ break;
+ }
+ if (map_info == NULL) {
+ MCDRV_DBG_ERROR(mc_kapi, "map_info is null");
+ mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+ break;
+ }
+ if (buf == NULL) {
+ MCDRV_DBG_ERROR(mc_kapi, "buf is null");
+ mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+ break;
+ }
+
+ /* Determine device the session belongs to */
+ struct mcore_device_t *device =
+ resolve_device_id(session_handle->device_id);
+ if (device == NULL) {
+ MCDRV_DBG_ERROR(mc_kapi, "Device not found");
+ mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
+ break;
+ }
+ struct connection *dev_con = device->connection;
+
+ /* Get session */
+ uint32_t session_id = session_handle->session_id;
+ struct session *session =
+ mcore_device_resolve_session_id(device,
+ session_id);
+ if (session == NULL) {
+ MCDRV_DBG_ERROR(mc_kapi, "Session not found");
+ mc_result = MC_DRV_ERR_UNKNOWN_SESSION;
+ break;
+ }
+
+ uint32_t handle = session_find_bulk_buf(session, buf);
+ if (handle == 0) {
+ MCDRV_DBG_ERROR(mc_kapi, "Buffer not found");
+ mc_result = MC_DRV_ERR_BULK_UNMAPPING;
+ break;
+ }
+
+
+ /* Prepare unmap command */
+ struct mc_drv_cmd_unmap_bulk_mem_t cmd_unmap_bulk_mem = {
+ {
+ MC_DRV_CMD_UNMAP_BULK_BUF
+ },
+ {
+ session->session_id,
+ handle,
+ map_info->secure_virt_addr,
+ map_info->secure_virt_len
+ }
+ };
+
+ connection_write_data(dev_con,
+ &cmd_unmap_bulk_mem,
+ sizeof(cmd_unmap_bulk_mem));
+
+ /* Read command response */
+ struct mc_drv_response_header_t rsp_header;
+ memset(&rsp_header, 0, sizeof(rsp_header));
+ int len = connection_read_datablock(dev_con,
+ &rsp_header,
+ sizeof(rsp_header));
+ if (len != sizeof(rsp_header)) {
+ MCDRV_DBG_ERROR(mc_kapi,
+ "CMD_UNMAP_BULK_BUF readRsp failed %d",
+ len);
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ break;
+ }
+
+ if (rsp_header.response_id != MC_DRV_RSP_OK) {
+ MCDRV_DBG_ERROR(mc_kapi,
+ "CMD_UNMAP_BULK_BUF failed, respId=%d",
+ rsp_header.response_id);
+
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ break;
+ }
+
+ /*struct mc_drv_rsp_unmap_bulk_mem_payload_t
+ rsp_unmap_bulk_mem_payload;
+ connection_read_datablock(dev_con,
+ &rsp_unmap_bulk_mem_payload,
+ sizeof(rsp_unmap_bulk_mem_payload));*/
+
+ /*
+ * Unregister mapped bulk buffer from Kernel Module and
+ * remove mapped bulk buffer from session maintenance
+ */
+ if (!session_remove_bulk_buf(session, buf)) {
+ /* Removing of bulk buffer not possible */
+ MCDRV_DBG_ERROR(mc_kapi,
+ "Unregistering of bulk memory failed");
+ mc_result = MC_DRV_ERR_BULK_UNMAPPING;
+ break;
+ }
+
+ mc_result = MC_DRV_OK;
+
+ } while (false);
+
+ return mc_result;
+}
+EXPORT_SYMBOL(mc_unmap);
+
+enum mc_result mc_get_session_error_code(struct mc_session_handle *session,
+ int32_t *last_error)
+{
+ enum mc_result mc_result = MC_DRV_OK;
+
+ MCDRV_DBG_VERBOSE(mc_kapi, "===%s()===", __func__);
+
+ do {
+ if (session == NULL || last_error == NULL) {
+ mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+ break;
+ }
+
+ /* Get device */
+ struct mcore_device_t *device =
+ resolve_device_id(session->device_id);
+ if (device == NULL) {
+ MCDRV_DBG_ERROR(mc_kapi, "Device not found");
+ mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
+ break;
+ }
+
+ /* Get session */
+ uint32_t session_id = session->session_id;
+ struct session *nqsession =
+ mcore_device_resolve_session_id(device,
+ session_id);
+ if (nqsession == NULL) {
+ MCDRV_DBG_ERROR(mc_kapi, "Session not found");
+ mc_result = MC_DRV_ERR_UNKNOWN_SESSION;
+ break;
+ }
+
+ *last_error = session_get_last_err(nqsession);
+
+ } while (false);
+
+ return mc_result;
+}
+EXPORT_SYMBOL(mc_get_session_error_code);
+
diff --git a/drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/common.h b/drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/common.h
new file mode 100644
index 000000000..7904e2bef
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/common.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2013-2015 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+/*
+ * Common data types for use by the MobiCore Kernel API Driver
+ */
+#ifndef _MC_KAPI_COMMON_H
+#define _MC_KAPI_COMMON_H
+
+#include "connection.h"
+#include "mcinq.h"
+
+void mcapi_insert_connection(struct connection *connection);
+void mcapi_remove_connection(uint32_t seq);
+unsigned int mcapi_unique_id(void);
+
+#define MC_DAEMON_PID 0xFFFFFFFF
+#define MC_DRV_MOD_DEVNODE_FULLPATH "/dev/mobicore"
+
+/* dummy function helper macro */
+#define DUMMY_FUNCTION() do {} while (0)
+
+/* Found in main.c */
+extern struct device *mc_kapi;
+
+/* Found in clientlib.c */
+extern struct mutex device_mutex;
+
+#define MCDRV_ERROR(dev, txt, ...) \
+ dev_err(dev, "%s() ### ERROR: " txt, __func__, ##__VA_ARGS__)
+
+#if defined(DEBUG)
+
+/* #define DEBUG_VERBOSE */
+#if defined(DEBUG_VERBOSE)
+#define MCDRV_DBG_VERBOSE MCDRV_DBG
+#else
+#define MCDRV_DBG_VERBOSE(...) DUMMY_FUNCTION()
+#endif
+
+#define MCDRV_DBG(dev, txt, ...) \
+ dev_info(dev, "%s(): " txt, __func__, ##__VA_ARGS__)
+
+#define MCDRV_DBG_WARN(dev, txt, ...) \
+ dev_warn(dev, "%s() WARNING: " txt, __func__, ##__VA_ARGS__)
+
+#define MCDRV_DBG_ERROR(dev, txt, ...) \
+ dev_err(dev, "%s() ### ERROR: " txt, __func__, ##__VA_ARGS__)
+
+#define MCDRV_ASSERT(cond) \
+ do { \
+ if (unlikely(!(cond))) { \
+ panic("mc_kernelapi Assertion failed: %s:%d\n", \
+ __FILE__, __LINE__); \
+ } \
+ } while (0)
+
+#elif defined(NDEBUG)
+
+#define MCDRV_DBG_VERBOSE(...) DUMMY_FUNCTION()
+#define MCDRV_DBG(...) DUMMY_FUNCTION()
+#define MCDRV_DBG_WARN(...) DUMMY_FUNCTION()
+#define MCDRV_DBG_ERROR(...) DUMMY_FUNCTION()
+
+#define MCDRV_ASSERT(...) DUMMY_FUNCTION()
+
+#else
+#error "Define DEBUG or NDEBUG"
+#endif /* [not] defined(DEBUG_MCMODULE) */
+
+#define assert(expr) MCDRV_ASSERT(expr)
+
+#endif /* _MC_KAPI_COMMON_H */
diff --git a/drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/connection.c b/drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/connection.c
new file mode 100644
index 000000000..7f6ca1fa7
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/connection.c
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2013-2015 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/netlink.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/semaphore.h>
+#include <linux/time.h>
+#include <net/sock.h>
+#include <net/net_namespace.h>
+
+#include "connection.h"
+#include "common.h"
+
+/* Define the initial state of the Data Available Semaphore */
+#define SEM_NO_DATA_AVAILABLE 0
+
+struct connection *connection_new(void)
+{
+ struct connection *conn;
+
+ conn = kzalloc(sizeof(*conn), GFP_KERNEL);
+ if (conn == NULL) {
+ MCDRV_DBG_ERROR(mc_kapi, "Allocation failure");
+ return NULL;
+ }
+ conn->sequence_magic = mcapi_unique_id();
+ mutex_init(&conn->data_lock);
+ sema_init(&conn->data_available_sem, SEM_NO_DATA_AVAILABLE);
+
+ mcapi_insert_connection(conn);
+ return conn;
+}
+
+void connection_cleanup(struct connection *conn)
+{
+ if (!conn)
+ return;
+
+ kfree_skb(conn->skb);
+
+ mcapi_remove_connection(conn->sequence_magic);
+ kfree(conn);
+}
+
+bool connection_connect(struct connection *conn, pid_t dest)
+{
+ /* Nothing to connect */
+ conn->peer_pid = dest;
+ return true;
+}
+
+size_t connection_read_data_msg(struct connection *conn, void *buffer,
+ uint32_t len)
+{
+ size_t ret = -1;
+ MCDRV_DBG_VERBOSE(mc_kapi,
+ "reading connection data %u, connection data left %u",
+ len, conn->data_len);
+ /* trying to read more than the left data */
+ if (len > conn->data_len) {
+ ret = conn->data_len;
+ memcpy(buffer, conn->data_start, conn->data_len);
+ conn->data_len = 0;
+ } else {
+ ret = len;
+ memcpy(buffer, conn->data_start, len);
+ conn->data_len -= len;
+ conn->data_start += len;
+ }
+
+ if (conn->data_len == 0) {
+ conn->data_start = NULL;
+ kfree_skb(conn->skb);
+ conn->skb = NULL;
+ }
+ MCDRV_DBG_VERBOSE(mc_kapi, "read %zu", ret);
+ return ret;
+}
+
+size_t connection_read_datablock(struct connection *conn, void *buffer,
+ uint32_t len)
+{
+ return connection_read_data(conn, buffer, len, -1);
+}
+
+size_t connection_read_data(struct connection *conn, void *buffer, uint32_t len,
+ int32_t timeout)
+{
+ size_t ret = 0;
+
+ MCDRV_ASSERT(buffer != NULL);
+ MCDRV_ASSERT(conn->socket_descriptor != NULL);
+
+ MCDRV_DBG_VERBOSE(mc_kapi, "read data len = %u for PID = %u",
+ len, conn->sequence_magic);
+ do {
+ /*
+ * Wait until data is available or timeout
+ * msecs_to_jiffies(-1) -> wait forever for the sem
+ */
+ if (down_timeout(&(conn->data_available_sem),
+ msecs_to_jiffies(timeout))) {
+ MCDRV_DBG_VERBOSE(mc_kapi,
+ "Timeout reading the data sem");
+ ret = -2;
+ break;
+ }
+
+ if (mutex_lock_interruptible(&(conn->data_lock))) {
+ MCDRV_DBG_ERROR(mc_kapi,
+ "interrupted reading the data sem");
+ ret = -1;
+ break;
+ }
+
+ /* Have data, use it */
+ if (conn->data_len > 0)
+ ret = connection_read_data_msg(conn, buffer, len);
+
+ mutex_unlock(&(conn->data_lock));
+
+ /* There is still some data left */
+ if (conn->data_len > 0)
+ up(&conn->data_available_sem);
+
+ } while (0);
+
+ return ret;
+}
+
+int connection_write_data(struct connection *conn, void *buffer,
+ uint32_t len)
+{
+ struct sk_buff *skb = NULL;
+ struct nlmsghdr *nlh;
+ int ret;
+
+ MCDRV_DBG_VERBOSE(mc_kapi, "buffer length %u from pid %u\n", len,
+ conn->sequence_magic);
+ skb = nlmsg_new(len, GFP_KERNEL);
+ if (!skb)
+ return -ENOMEM;
+
+ nlh = nlmsg_put(skb, 0, conn->sequence_magic, 2, len,
+ NLM_F_REQUEST);
+ if (!nlh) {
+ kfree_skb(skb);
+ return -EINVAL;
+ }
+
+ /* netlink_unicast frees skb */
+ memcpy(NLMSG_DATA(nlh), buffer, len);
+ ret = netlink_unicast(conn->socket_descriptor, skb, conn->peer_pid,
+ MSG_DONTWAIT);
+ if (ret < 0)
+ return ret;
+
+ return len;
+}
+
+int connection_process(struct connection *conn, struct sk_buff *skb)
+{
+ int ret = 0;
+ do {
+ if (mutex_lock_interruptible(&(conn->data_lock))) {
+ MCDRV_DBG_ERROR(mc_kapi,
+ "Interrupted getting data semaphore!");
+ ret = -1;
+ break;
+ }
+
+ kfree_skb(conn->skb);
+
+ /* Get a reference to the incoming skb */
+ conn->skb = skb_get(skb);
+ if (conn->skb) {
+ conn->data_msg = nlmsg_hdr(conn->skb);
+ conn->data_len = NLMSG_PAYLOAD(conn->data_msg, 0);
+ conn->data_start = NLMSG_DATA(conn->data_msg);
+ up(&(conn->data_available_sem));
+ }
+ mutex_unlock(&(conn->data_lock));
+ ret = 0;
+ } while (0);
+ return ret;
+}
diff --git a/drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/connection.h b/drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/connection.h
new file mode 100644
index 000000000..04fa1f707
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/connection.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2013-2015 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+#ifndef _MC_KAPI_CONNECTION_H_
+#define _MC_KAPI_CONNECTION_H_
+
+#include <linux/semaphore.h>
+#include <linux/mutex.h>
+
+#include <stddef.h>
+#include <stdbool.h>
+
+struct connection {
+ /* Netlink socket */
+ struct sock *socket_descriptor;
+ /* Random? magic to match requests/answers */
+ uint32_t sequence_magic;
+
+ struct nlmsghdr *data_msg;
+ /* How much connection data is left */
+ uint32_t data_len;
+ /* Start pointer of remaining data */
+ void *data_start;
+ struct sk_buff *skb;
+
+ /* Data protection lock */
+ struct mutex data_lock;
+ /* Data protection semaphore */
+ struct semaphore data_available_sem;
+
+ /* PID address used for local connection */
+ pid_t self_pid;
+ /* Remote PID for connection */
+ pid_t peer_pid;
+
+ /* The list param for using the kernel lists */
+ struct list_head list;
+};
+
+struct connection *connection_new(void);
+void connection_cleanup(struct connection *conn);
+bool connection_connect(struct connection *conn, pid_t dest);
+size_t connection_read_datablock(struct connection *conn, void *buffer,
+ uint32_t len);
+size_t connection_read_data(struct connection *conn, void *buffer,
+ uint32_t len, int32_t timeout);
+int connection_write_data(struct connection *conn, void *buffer,
+ uint32_t len);
+int connection_process(struct connection *conn, struct sk_buff *skb);
+
+#endif /* _MC_KAPI_CONNECTION_H_ */
diff --git a/drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/device.c b/drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/device.c
new file mode 100644
index 000000000..7795c7e95
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/device.c
@@ -0,0 +1,259 @@
+/*
+ * Copyright (c) 2013-2015 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+/*
+ * MobiCore client library device management.
+ *
+ * Device and Trustlet Session management Functions.
+ */
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include "mc_kernel_api.h"
+#include "public/mobicore_driver_api.h"
+
+#include "device.h"
+#include "common.h"
+
+static struct wsm *wsm_create(void *virt_addr, uint32_t len, uint32_t handle)
+{
+ struct wsm *wsm;
+
+ wsm = kzalloc(sizeof(*wsm), GFP_KERNEL);
+ if (wsm == NULL) {
+ MCDRV_DBG_ERROR(mc_kapi, "Allocation failure");
+ return NULL;
+ }
+ wsm->virt_addr = virt_addr;
+ wsm->len = len;
+ wsm->handle = handle;
+ return wsm;
+}
+
+struct mcore_device_t *mcore_device_create(uint32_t device_id,
+ struct connection *connection)
+{
+ struct mcore_device_t *dev;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (dev == NULL) {
+ MCDRV_DBG_ERROR(mc_kapi, "Allocation failure");
+ return NULL;
+ }
+ dev->device_id = device_id;
+ dev->connection = connection;
+
+ INIT_LIST_HEAD(&dev->session_vector);
+ INIT_LIST_HEAD(&dev->wsm_mmu_vector);
+ mutex_init(&(dev->session_vector_lock));
+ mutex_init(&(dev->wsm_mmu_vector_lock));
+
+ return dev;
+}
+
+void mcore_device_cleanup(struct mcore_device_t *dev)
+{
+ struct session *session = NULL;
+ struct wsm *wsm;
+ struct list_head *pos, *q;
+
+ /*
+ * Delete all session objects. Usually this should not be needed
+ * as close_device() requires that all sessions have been closed before.
+ */
+ do {
+ session = NULL;
+ mutex_lock(&(dev->session_vector_lock));
+ if (!list_empty(&(dev->session_vector))) {
+ session = list_first_entry(&(dev->session_vector),
+ struct session, list);
+ list_del(&(session->list));
+ }
+ mutex_unlock(&(dev->session_vector_lock));
+ if (!session)
+ break;
+ session_cleanup(session);
+ } while (true);
+
+ /* Free all allocated WSM descriptors */
+ mutex_lock(&(dev->wsm_mmu_vector_lock));
+ list_for_each_safe(pos, q, &(dev->wsm_mmu_vector)) {
+ wsm = list_entry(pos, struct wsm, list);
+ list_del(pos);
+ kfree(wsm);
+ }
+ mutex_unlock(&(dev->wsm_mmu_vector_lock));
+ connection_cleanup(dev->connection);
+
+ mcore_device_close(dev);
+ kfree(dev);
+}
+
+bool mcore_device_open(struct mcore_device_t *dev, const char *device_name)
+{
+ dev->instance = mobicore_open();
+ return (dev->instance != NULL);
+}
+
+void mcore_device_close(struct mcore_device_t *dev)
+{
+ mobicore_release(dev->instance);
+}
+
+bool mcore_device_has_sessions(struct mcore_device_t *dev)
+{
+ int ret = 0;
+ mutex_lock(&(dev->session_vector_lock));
+ ret = !list_empty(&dev->session_vector);
+ mutex_unlock(&(dev->session_vector_lock));
+ return ret;
+}
+
+bool mcore_device_create_new_session(struct mcore_device_t *dev,
+ uint32_t session_id,
+ struct connection *connection)
+{
+ /* Check if session_id already exists */
+ if (mcore_device_resolve_session_id(dev, session_id)) {
+ MCDRV_DBG_ERROR(mc_kapi,
+ " session %u already exists", session_id);
+ return false;
+ }
+ struct session *session =
+ session_create(session_id, dev->instance, connection);
+ if (session == NULL)
+ return false;
+ mutex_lock(&(dev->session_vector_lock));
+ list_add_tail(&(session->list), &(dev->session_vector));
+ mutex_unlock(&(dev->session_vector_lock));
+ return true;
+}
+
+bool mcore_device_remove_session(struct mcore_device_t *dev,
+ uint32_t session_id)
+{
+ bool found = false;
+ struct session *session = NULL;
+ struct list_head *pos;
+
+ mutex_lock(&(dev->session_vector_lock));
+ list_for_each(pos, &dev->session_vector) {
+ session = list_entry(pos, struct session, list);
+ if (session->session_id == session_id) {
+ list_del(pos);
+ found = true;
+ break;
+ }
+ }
+ mutex_unlock(&(dev->session_vector_lock));
+ if (found)
+ session_cleanup(session);
+ return found;
+}
+
+struct session *mcore_device_resolve_session_id(struct mcore_device_t *dev,
+ uint32_t session_id)
+{
+ struct session *ret = NULL;
+ struct session *session;
+
+ /* Get session for session_id */
+ mutex_lock(&(dev->session_vector_lock));
+ list_for_each_entry(session, &dev->session_vector, list) {
+ if (session->session_id == session_id) {
+ ret = session;
+ break;
+ }
+ }
+ mutex_unlock(&(dev->session_vector_lock));
+ return ret;
+}
+
+struct wsm *mcore_device_allocate_contiguous_wsm(struct mcore_device_t *dev,
+ uint32_t len)
+{
+ struct wsm *wsm = NULL;
+ do {
+ if (len == 0)
+ break;
+
+ /* Allocate shared memory */
+ void *virt_addr;
+ uint32_t handle;
+ int ret = mobicore_allocate_wsm(dev->instance, len, &handle,
+ &virt_addr);
+ if (ret != 0)
+ break;
+
+ /* Register (vaddr) with device */
+ wsm = wsm_create(virt_addr, len, handle);
+ if (wsm == NULL) {
+ mobicore_free_wsm(dev->instance, handle);
+ break;
+ }
+
+ mutex_lock(&(dev->wsm_mmu_vector_lock));
+ list_add_tail(&(wsm->list), &(dev->wsm_mmu_vector));
+ mutex_unlock(&(dev->wsm_mmu_vector_lock));
+
+ } while (0);
+
+ return wsm;
+}
+
+bool mcore_device_free_contiguous_wsm(struct mcore_device_t *dev,
+ struct wsm *wsm)
+{
+ bool ret = false;
+ struct wsm *tmp;
+ struct list_head *pos;
+
+ mutex_lock(&(dev->wsm_mmu_vector_lock));
+ list_for_each(pos, &dev->wsm_mmu_vector) {
+ tmp = list_entry(pos, struct wsm, list);
+ if (tmp == wsm) {
+ ret = true;
+ break;
+ }
+ }
+ mutex_unlock(&(dev->wsm_mmu_vector_lock));
+ if (ret) {
+ MCDRV_DBG_VERBOSE(mc_kapi,
+ "freeWsm virt_addr=0x%p, handle=%d",
+ wsm->virt_addr, wsm->handle);
+
+ /* ignore return code */
+ mobicore_free_wsm(dev->instance, wsm->handle);
+
+ list_del(pos);
+ kfree(wsm);
+ }
+ return ret;
+}
+
+struct wsm *mcore_device_find_contiguous_wsm(struct mcore_device_t *dev,
+ void *virt_addr)
+{
+ struct wsm *wsm, *candidate = NULL;
+
+ mutex_lock(&(dev->wsm_mmu_vector_lock));
+ list_for_each_entry(wsm, &dev->wsm_mmu_vector, list) {
+ if (virt_addr == wsm->virt_addr) {
+ candidate = wsm;
+ break;
+ }
+ }
+ mutex_unlock(&(dev->wsm_mmu_vector_lock));
+
+ return candidate;
+}
diff --git a/drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/device.h b/drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/device.h
new file mode 100644
index 000000000..8387aac60
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/device.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2013-2015 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+/*
+ * MobiCore client library device management.
+ *
+ * Device and Trustlet Session management Functions.
+ */
+#ifndef _MC_KAPI_DEVICE_H_
+#define _MC_KAPI_DEVICE_H_
+
+#include <linux/list.h>
+
+#include "connection.h"
+#include "session.h"
+#include "wsm.h"
+
+struct mcore_device_t {
+ /* MobiCore Trustlet session associated with the device */
+/* lock used to prevent concurrent add/del action on the session list */
+ struct mutex session_vector_lock;
+ struct list_head session_vector;
+/* lock used to prevent concurrent add/del action on the mmu table list */
+ struct mutex wsm_mmu_vector_lock;
+ struct list_head wsm_mmu_vector; /* WSM L2 or L3 Table */
+
+ uint32_t device_id; /* Device identifier */
+ struct connection *connection; /* The device connection */
+ struct mc_instance *instance; /* MobiCore Driver instance */
+
+ /* The list param for using the kernel lists */
+ struct list_head list;
+};
+
+struct mcore_device_t *mcore_device_create(
+ uint32_t device_id, struct connection *connection);
+void mcore_device_cleanup(struct mcore_device_t *dev);
+
+
+bool mcore_device_open(struct mcore_device_t *dev, const char *device_name);
+void mcore_device_close(struct mcore_device_t *dev);
+bool mcore_device_has_sessions(struct mcore_device_t *dev);
+bool mcore_device_create_new_session(
+ struct mcore_device_t *dev, uint32_t session_id,
+ struct connection *connection);
+bool mcore_device_remove_session(
+ struct mcore_device_t *dev, uint32_t session_id);
+struct session *mcore_device_resolve_session_id(
+ struct mcore_device_t *dev, uint32_t session_id);
+struct wsm *mcore_device_allocate_contiguous_wsm(
+ struct mcore_device_t *dev, uint32_t len);
+bool mcore_device_free_contiguous_wsm(
+ struct mcore_device_t *dev, struct wsm *wsm);
+struct wsm *mcore_device_find_contiguous_wsm(
+ struct mcore_device_t *dev, void *virt_addr);
+
+#endif /* _MC_KAPI_DEVICE_H_ */
diff --git a/drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/include/mcinq.h b/drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/include/mcinq.h
new file mode 100644
index 000000000..a12696e04
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/include/mcinq.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2013 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+/*
+ * Notifications inform the MobiCore runtime environment that information is
+ * pending in a WSM buffer.
+ *
+ * The Trustlet Connector (TLC) and the corresponding Trustlet also utilize
+ * this buffer to notify each other about new data within the
+ * Trustlet Connector Interface (TCI).
+ *
+ * The buffer is set up as a queue, which means that more than one
+ * notification can be written to the buffer before the switch to the other
+ * world is performed. Each side therefore facilitates an incoming and an
+ * outgoing queue for communication with the other side.
+ *
+ * Notifications hold the session ID, which is used to reference the
+ * communication partner in the other world.
+ * So if, e.g., the TLC in the normal world wants to notify his Trustlet
+ * about new data in the TLC buffer
+ *
+ * Notification queue declarations.
+ */
+#ifndef _MCINQ_H_
+#define _MCINQ_H_
+
+/* Minimum and maximum count of elements in the notification queue */
+#define MIN_NQ_ELEM 1 /* Minimum notification queue elements. */
+#define MAX_NQ_ELEM 64 /* Maximum notification queue elements. */
+
+/* Compute notification queue size in bytes from its number of elements */
+#define NQ_SIZE(n) (2*(sizeof(struct notification_queue_header) \
+ + (n)*sizeof(struct notification)))
+
+/** \name NQ Length Defines
+ * Note that there is one queue for NWd->SWd and one queue for SWd->NWd
+ */
+/** Minimum size for the notification queue data structure */
+#define MIN_NQ_LEN NQ_SIZE(MIN_NQ_ELEM)
+/** Maximum size for the notification queue data structure */
+#define MAX_NQ_LEN NQ_SIZE(MAX_NQ_ELEM)
+
+/*
+ * MCP session ID is used when directly communicating with the MobiCore
+ * (e.g. for starting and stopping of Trustlets).
+ */
+#define SID_MCP 0
+/* Invalid session id is returned in case of an error. */
+#define SID_INVALID 0xffffffff
+
+/* Notification data structure. */
+struct notification {
+ uint32_t session_id; /* Session ID. */
+ int32_t payload; /* Additional notification info */
+};
+
+/*
+ * Notification payload codes.
+ * 0 indicated a plain simple notification,
+ * a positive value is a termination reason from the task,
+ * a negative value is a termination reason from MobiCore.
+ * Possible negative values are given below.
+ */
+enum notification_payload {
+ /* task terminated, but exit code is invalid */
+ ERR_INVALID_EXIT_CODE = -1,
+ /* task terminated due to session end, no exit code available */
+ ERR_SESSION_CLOSE = -2,
+ /* task terminated due to invalid operation */
+ ERR_INVALID_OPERATION = -3,
+ /* session ID is unknown */
+ ERR_INVALID_SID = -4,
+ /* session is not active */
+ ERR_SID_NOT_ACTIVE = -5
+};
+
+/*
+ * Declaration of the notification queue header.
+ * Layout as specified in the data structure specification.
+ */
+struct notification_queue_header {
+ uint32_t write_cnt; /* Write counter. */
+ uint32_t read_cnt; /* Read counter. */
+ uint32_t queue_size; /* Queue size. */
+};
+
+/*
+ * Queue struct which defines a queue object.
+ * The queue struct is accessed by the queue<operation> type of
+ * function. elementCnt must be a power of two and the power needs
+ * to be smaller than power of uint32_t (obviously 32).
+ */
+struct notification_queue {
+ /* Queue header. */
+ struct notification_queue_header hdr;
+ /* Notification elements. */
+ struct notification notification[MIN_NQ_ELEM];
+};
+
+#endif /* _MCINQ_H_ */
diff --git a/drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/include/mcuuid.h b/drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/include/mcuuid.h
new file mode 100644
index 000000000..eca5191ed
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/include/mcuuid.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2013-2014 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+#ifndef _MCUUID_H_
+#define _MCUUID_H_
+
+#define UUID_TYPE
+
+/* Universally Unique Identifier (UUID) according to ISO/IEC 11578. */
+struct mc_uuid_t {
+ uint8_t value[16]; /* Value of the UUID. */
+};
+
+#endif /* _MCUUID_H_ */
diff --git a/drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/main.c b/drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/main.c
new file mode 100644
index 000000000..5c1635ebe
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/main.c
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2013-2015 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/netlink.h>
+#include <linux/kthread.h>
+#include <linux/device.h>
+#include <net/sock.h>
+
+#include <linux/list.h>
+
+#include "connection.h"
+#include "common.h"
+
+#define MC_DAEMON_NETLINK 17
+
+struct mc_kernelapi_ctx {
+ struct sock *sk;
+ struct list_head peers;
+ atomic_t counter;
+ struct mutex peers_lock; /* peers lock */
+};
+
+struct mc_kernelapi_ctx *mod_ctx;
+
+/* Define a MobiCore Kernel API device structure for use with dev_debug() etc */
+struct device_driver mc_kernel_api_name = {
+ .name = "mckernelapi"
+};
+
+struct device mc_kernel_api_subname = {
+ .init_name = "", /* Set to 'mcapi' at mcapi_init() time */
+ .driver = &mc_kernel_api_name
+};
+
+struct device *mc_kapi = &mc_kernel_api_subname;
+
+/* get a unique ID */
+unsigned int mcapi_unique_id(void)
+{
+ return (unsigned int)atomic_inc_return(&(mod_ctx->counter));
+}
+
+static struct connection *mcapi_find_connection(uint32_t seq)
+{
+ struct connection *tmp, *conn = NULL;
+ struct list_head *pos;
+
+ mutex_lock(&(mod_ctx->peers_lock));
+
+ /* Get session for session_id */
+ list_for_each(pos, &mod_ctx->peers) {
+ tmp = list_entry(pos, struct connection, list);
+ if (tmp->sequence_magic == seq) {
+ conn = tmp;
+ break;
+ }
+ }
+
+ mutex_unlock(&(mod_ctx->peers_lock));
+
+ return conn;
+}
+
+void mcapi_insert_connection(struct connection *connection)
+{
+ mutex_lock(&(mod_ctx->peers_lock));
+
+ list_add_tail(&(connection->list), &(mod_ctx->peers));
+ connection->socket_descriptor = mod_ctx->sk;
+
+ mutex_unlock(&(mod_ctx->peers_lock));
+}
+
+void mcapi_remove_connection(uint32_t seq)
+{
+ struct connection *tmp;
+ struct list_head *pos, *q;
+
+ /*
+ * Delete all session objects. Usually this should not be needed as
+ * closeDevice() requires that all sessions have been closed before.
+ */
+ mutex_lock(&(mod_ctx->peers_lock));
+ list_for_each_safe(pos, q, &mod_ctx->peers) {
+ tmp = list_entry(pos, struct connection, list);
+ if (tmp->sequence_magic == seq) {
+ list_del(pos);
+ break;
+ }
+ }
+
+ mutex_unlock(&(mod_ctx->peers_lock));
+}
+
+static int mcapi_process(struct sk_buff *skb, struct nlmsghdr *nlh)
+{
+ struct connection *c;
+ int seq;
+ int ret;
+
+ seq = nlh->nlmsg_seq;
+ MCDRV_DBG_VERBOSE(mc_kapi, "nlmsg len %d type %d pid 0x%X seq %d\n",
+ nlh->nlmsg_len, nlh->nlmsg_type, nlh->nlmsg_pid, seq);
+ do {
+ c = mcapi_find_connection(seq);
+ if (!c) {
+ MCDRV_ERROR(mc_kapi,
+ "Invalid incoming connection - seq=%u!",
+ seq);
+ ret = -1;
+ break;
+ }
+
+ /* Pass the buffer to the appropriate connection */
+ connection_process(c, skb);
+
+ ret = 0;
+ } while (false);
+ return ret;
+}
+
+static void mcapi_callback(struct sk_buff *skb)
+{
+ struct nlmsghdr *nlh = nlmsg_hdr(skb);
+ int len = skb->len;
+ int err = 0;
+
+ while (NLMSG_OK(nlh, len)) {
+ err = mcapi_process(skb, nlh);
+
+ /* if err or if this message says it wants a response */
+ if (err || (nlh->nlmsg_flags & NLM_F_ACK))
+ netlink_ack(skb, nlh, err);
+
+ nlh = NLMSG_NEXT(nlh, len);
+ }
+}
+
+static int __init mcapi_init(void)
+{
+#if defined MC_NETLINK_COMPAT || defined MC_NETLINK_COMPAT_V37
+ struct netlink_kernel_cfg cfg = {
+ .input = mcapi_callback,
+ };
+#endif
+
+ dev_set_name(mc_kapi, "mcapi");
+
+ dev_info(mc_kapi, "Mobicore API module initialized!\n");
+
+ mod_ctx = kzalloc(sizeof(struct mc_kernelapi_ctx), GFP_KERNEL);
+ if (mod_ctx == NULL) {
+ MCDRV_DBG_ERROR(mc_kapi, "Allocation failure");
+ return -ENOMEM;
+ }
+#ifdef MC_NETLINK_COMPAT_V37
+ mod_ctx->sk = netlink_kernel_create(&init_net, MC_DAEMON_NETLINK,
+ &cfg);
+#elif defined MC_NETLINK_COMPAT
+ mod_ctx->sk = netlink_kernel_create(&init_net, MC_DAEMON_NETLINK,
+ THIS_MODULE, &cfg);
+#else
+ /* start kernel thread */
+ mod_ctx->sk = netlink_kernel_create(&init_net, MC_DAEMON_NETLINK, 0,
+ mcapi_callback, NULL, THIS_MODULE);
+#endif
+
+ if (!mod_ctx->sk) {
+ MCDRV_ERROR(mc_kapi, "register of receive handler failed");
+ kfree(mod_ctx);
+ mod_ctx = NULL;
+ return -EFAULT;
+ }
+
+ INIT_LIST_HEAD(&mod_ctx->peers);
+
+ mutex_init(&mod_ctx->peers_lock);
+ mutex_init(&device_mutex);
+
+ return 0;
+}
+
+static void __exit mcapi_exit(void)
+{
+ dev_info(mc_kapi, "Unloading Mobicore API module.\n");
+
+ if (mod_ctx->sk != NULL) {
+ netlink_kernel_release(mod_ctx->sk);
+ mod_ctx->sk = NULL;
+ }
+ kfree(mod_ctx);
+ mod_ctx = NULL;
+}
+
+module_init(mcapi_init);
+module_exit(mcapi_exit);
+
+MODULE_AUTHOR("Trustonic Limited");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MobiCore API driver");
diff --git a/drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/public/mobicore_driver_api.h b/drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/public/mobicore_driver_api.h
new file mode 100644
index 000000000..7bf2a2f66
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/public/mobicore_driver_api.h
@@ -0,0 +1,399 @@
+/*
+ * Copyright (c) 2013 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+/*
+ * MobiCore Driver API.
+ *
+ * The MobiCore (MC) Driver API provides access functions to the MobiCore
+ * runtime environment and the contained Trustlets.
+ */
+#ifndef _MOBICORE_DRIVER_API_H_
+#define _MOBICORE_DRIVER_API_H_
+
+#define __MC_CLIENT_LIB_API
+
+#include "mcuuid.h"
+
+/*
+ * Return values of MobiCore driver functions.
+ */
+enum mc_result {
+ /* Function call succeeded. */
+ MC_DRV_OK = 0,
+ /* No notification available. */
+ MC_DRV_NO_NOTIFICATION = 1,
+ /* Error during notification on communication level. */
+ MC_DRV_ERR_NOTIFICATION = 2,
+ /* Function not implemented. */
+ MC_DRV_ERR_NOT_IMPLEMENTED = 3,
+ /* No more resources available. */
+ MC_DRV_ERR_OUT_OF_RESOURCES = 4,
+ /* Driver initialization failed. */
+ MC_DRV_ERR_INIT = 5,
+ /* Unknown error. */
+ MC_DRV_ERR_UNKNOWN = 6,
+ /* The specified device is unknown. */
+ MC_DRV_ERR_UNKNOWN_DEVICE = 7,
+ /* The specified session is unknown.*/
+ MC_DRV_ERR_UNKNOWN_SESSION = 8,
+ /* The specified operation is not allowed. */
+ MC_DRV_ERR_INVALID_OPERATION = 9,
+ /* The response header from the MC is invalid. */
+ MC_DRV_ERR_INVALID_RESPONSE = 10,
+ /* Function call timed out. */
+ MC_DRV_ERR_TIMEOUT = 11,
+ /* Can not allocate additional memory. */
+ MC_DRV_ERR_NO_FREE_MEMORY = 12,
+ /* Free memory failed. */
+ MC_DRV_ERR_FREE_MEMORY_FAILED = 13,
+ /* Still some open sessions pending. */
+ MC_DRV_ERR_SESSION_PENDING = 14,
+ /* MC daemon not reachable */
+ MC_DRV_ERR_DAEMON_UNREACHABLE = 15,
+ /* The device file of the kernel module could not be opened. */
+ MC_DRV_ERR_INVALID_DEVICE_FILE = 16,
+ /* Invalid parameter. */
+ MC_DRV_ERR_INVALID_PARAMETER = 17,
+ /* Unspecified error from Kernel Module*/
+ MC_DRV_ERR_KERNEL_MODULE = 18,
+ /* Error during mapping of additional bulk memory to session. */
+ MC_DRV_ERR_BULK_MAPPING = 19,
+ /* Error during unmapping of additional bulk memory to session. */
+ MC_DRV_ERR_BULK_UNMAPPING = 20,
+ /* Notification received, exit code available. */
+ MC_DRV_INFO_NOTIFICATION = 21,
+ /* Set up of NWd connection failed. */
+ MC_DRV_ERR_NQ_FAILED = 22
+};
+
+/*
+ * Driver control command.
+ */
+enum mc_driver_ctrl {
+ /* Return the driver version */
+ MC_CTRL_GET_VERSION = 1
+};
+
+/*
+ * Structure of Session Handle, includes the Session ID and the Device ID the
+ * Session belongs to.
+ * The session handle will be used for session-based MobiCore communication.
+ * It will be passed to calls which address a communication end point in the
+ * MobiCore environment.
+ */
+struct mc_session_handle {
+ uint32_t session_id; /* MobiCore session ID */
+ uint32_t device_id; /* Device ID the session belongs to */
+};
+
+/*
+ * Information structure about additional mapped Bulk buffer between the
+ * Trustlet Connector (NWd) and the Trustlet (SWd). This structure is
+ * initialized from a Trustlet Connector by calling mc_map().
+ * In order to use the memory within a Trustlet the Trustlet Connector has to
+ * inform the Trustlet with the content of this structure via the TCI.
+ */
+struct mc_bulk_map {
+ /* The virtual address of the Bulk buffer regarding the address space
+ * of the Trustlet, already includes a possible offset! */
+ uint32_t secure_virt_addr;
+ uint32_t secure_virt_len; /* Length of the mapped Bulk buffer */
+};
+
+/* The default device ID */
+#define MC_DEVICE_ID_DEFAULT 0
+/* Wait infinite for a response of the MC. */
+#define MC_INFINITE_TIMEOUT ((int32_t)(-1))
+/* Do not wait for a response of the MC. */
+#define MC_NO_TIMEOUT 0
+/* TCI/DCI must not exceed 1MiB */
+#define MC_MAX_TCI_LEN 0x100000
+
+/**
+ * mc_open_device() - Open a new connection to a MobiCore device.
+ * @device_id: Identifier for the MobiCore device to be used.
+ * MC_DEVICE_ID_DEFAULT refers to the default device.
+ *
+ * Initializes all device specific resources required to communicate with a
+ * MobiCore instance located on the specified device in the system. If the
+ * device does not exist the function will return MC_DRV_ERR_UNKNOWN_DEVICE.
+ *
+ * Return codes:
+ * MC_DRV_OK: operation completed successfully
+ * MC_DRV_ERR_INVALID_OPERATION: device already opened
+ * MC_DRV_ERR_DAEMON_UNREACHABLE: problems with daemon
+ * MC_DRV_ERR_UNKNOWN_DEVICE: device_id unknown
+ * MC_DRV_ERR_INVALID_DEVICE_FILE: kernel module under /dev/mobicore
+ * cannot be opened
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_open_device(uint32_t device_id);
+
+/**
+ * mc_close_device() - Close the connection to a MobiCore device.
+ * @device_id: Identifier for the MobiCore device.
+ *
+ * When closing a device, active sessions have to be closed beforehand.
+ * Resources associated with the device will be released.
+ * The device may be opened again after it has been closed.
+ *
+ * MC_DEVICE_ID_DEFAULT refers to the default device.
+ *
+ * Return codes:
+ * MC_DRV_OK: operation completed successfully
+ * MC_DRV_ERR_UNKNOWN_DEVICE: device id is invalid
+ * MC_DRV_ERR_SESSION_PENDING: a session is still open
+ * MC_DRV_ERR_DAEMON_UNREACHABLE: problems with daemon occur
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_close_device(uint32_t device_id);
+
+/**
+ * mc_open_session() - Open a new session to a Trustlet.
+ * @session: On success, the session data will be returned
+ * @uuid: UUID of the Trustlet to be opened
+ * @tci: TCI buffer for communicating with the Trustlet
+ * @tci_len: Length of the TCI buffer. Maximum allowed value
+ * is MC_MAX_TCI_LEN
+ *
+ * The Trustlet with the given UUID has to be available in the flash filesystem.
+ *
+ * Write MCP open message to buffer and notify MobiCore about the availability
+ * of a new command.
+ *
+ * Waits till the MobiCore responses with the new session ID (stored in the MCP
+ * buffer).
+ *
+ * Note that session.device_id has to be the device id of an opened device.
+ *
+ * Return codes:
+ * MC_DRV_OK: operation completed successfully
+ * MC_DRV_INVALID_PARAMETER: session parameter is invalid
+ * MC_DRV_ERR_UNKNOWN_DEVICE: device id is invalid
+ * MC_DRV_ERR_DAEMON_UNREACHABLE: problems with daemon socket occur
+ * MC_DRV_ERR_NQ_FAILED: daemon returns an error
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_open_session(
+ struct mc_session_handle *session, const struct mc_uuid_t *uuid,
+ uint8_t *tci, uint32_t tci_len);
+
+/**
+ * mc_close_session() - Close a Trustlet session.
+ * @session: Session to be closed.
+ *
+ * Closes the specified MobiCore session. The call will block until the
+ * session has been closed.
+ *
+ * Device device_id has to be opened in advance.
+ *
+ * Return codes:
+ * MC_DRV_OK: operation completed successfully
+ * MC_DRV_INVALID_PARAMETER: session parameter is invalid
+ * MC_DRV_ERR_UNKNOWN_SESSION: session id is invalid
+ * MC_DRV_ERR_UNKNOWN_DEVICE: device id of session is invalid
+ * MC_DRV_ERR_DAEMON_UNREACHABLE: problems with daemon occur
+ * MC_DRV_ERR_INVALID_DEVICE_FILE: daemon cannot open Trustlet file
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_close_session(
+ struct mc_session_handle *session);
+
+/**
+ * mc_notify() - Notify a session.
+ * @session: The session to be notified.
+ *
+ * Notifies the session end point about available message data.
+ * If the session parameter is correct, notify will always succeed.
+ * Corresponding errors can only be received by mc_wait_notification().
+ *
+ * A session has to be opened in advance.
+ *
+ * Return codes:
+ * MC_DRV_OK: operation completed successfully
+ * MC_DRV_INVALID_PARAMETER: session parameter is invalid
+ * MC_DRV_ERR_UNKNOWN_SESSION: session id is invalid
+ * MC_DRV_ERR_UNKNOWN_DEVICE: device id of session is invalid
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_notify(struct mc_session_handle *session);
+
+/**
+ * mc_wait_notification() - Wait for a notification.
+ * @session: The session the notification should correspond to.
+ * @timeout: Time in milliseconds to wait
+ * (MC_NO_TIMEOUT : direct return, > 0 : milliseconds,
+ * MC_INFINITE_TIMEOUT : wait infinitely)
+ *
+ * Wait for a notification issued by the MobiCore for a specific session.
+ * The timeout parameter specifies the number of milliseconds the call will wait
+ * for a notification.
+ *
+ * If the caller passes 0 as timeout value the call will immediately return.
+ * If timeout value is below 0 the call will block until a notification for the
+ * session has been received.
+ *
+ * If timeout is below 0, call will block.
+ *
+ * Caller has to trust the other side to send a notification to wake him up
+ * again.
+ *
+ * Return codes:
+ * MC_DRV_OK: operation completed successfully
+ * MC_DRV_ERR_TIMEOUT: no notification arrived in time
+ * MC_DRV_INFO_NOTIFICATION: a problem with the session was
+ * encountered. Get more details with
+ * mc_get_session_error_code()
+ * MC_DRV_ERR_NOTIFICATION: a problem with the socket occurred
+ * MC_DRV_INVALID_PARAMETER: a parameter is invalid
+ * MC_DRV_ERR_UNKNOWN_SESSION: session id is invalid
+ * MC_DRV_ERR_UNKNOWN_DEVICE: device id of session is invalid
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_wait_notification(
+ struct mc_session_handle *session, int32_t timeout);
+
+/**
+ * mc_malloc_wsm() - Allocate a block of world shared memory (WSM).
+ * @device_id: The ID of an opened device to retrieve the WSM from.
+ * @align: The alignment (number of pages) of the memory block
+ * (e.g. 0x00000001 for 4kb).
+ * @len: Length of the block in bytes.
+ * @wsm: Virtual address of the world shared memory block.
+ * @wsm_flags: Platform specific flags describing the memory to
+ * be allocated.
+ *
+ * The MC driver allocates a contiguous block of memory which can be used as
+ * WSM.
+ * This implicates that the allocated memory is aligned according to the
+ * alignment parameter.
+ *
+ * Always returns a buffer of size WSM_SIZE aligned to 4K.
+ *
+ * Align and wsm_flags are currently ignored
+ *
+ * Return codes:
+ * MC_DRV_OK: operation completed successfully
+ * MC_DRV_INVALID_PARAMETER: a parameter is invalid
+ * MC_DRV_ERR_UNKNOWN_DEVICE: device id is invalid
+ * MC_DRV_ERR_NO_FREE_MEMORY: no more contiguous memory is
+ * available in this size or for this
+ * process
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_malloc_wsm(
+ uint32_t device_id,
+ uint32_t align,
+ uint32_t len,
+ uint8_t **wsm,
+ uint32_t wsm_flags
+);
+
+/**
+ * mc_free_wsm() - Free a block of world shared memory (WSM).
+ * @device_id: The ID to which the given address belongs
+ * @wsm: Address of WSM block to be freed
+ *
+ * The MC driver will free a block of world shared memory (WSM) previously
+ * allocated with mc_malloc_wsm(). The caller has to assure that the address
+ * handed over to the driver is a valid WSM address.
+ *
+ * Return codes:
+ * MC_DRV_OK: operation completed successfully
+ * MC_DRV_INVALID_PARAMETER: a parameter is invalid
+ * MC_DRV_ERR_UNKNOWN_DEVICE: when device id is invalid
+ * MC_DRV_ERR_FREE_MEMORY_FAILED: on failure
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_free_wsm(uint32_t device_id,
+ uint8_t *wsm);
+
+/**
+ *mc_map() - Map additional bulk buffer between a Trustlet Connector (TLC)
+ * and the Trustlet (TL) for a session
+ * @session: Session handle with information of the device_id and
+ * the session_id. The given buffer is mapped to the
+ * session specified in the sessionHandle
+ * @buf: Virtual address of a memory portion (relative to TLC)
+ * to be shared with the Trustlet, already includes a
+ * possible offset!
+ * @len: length of buffer block in bytes.
+ * @map_info: Information structure about the mapped Bulk buffer
+ * between the TLC (NWd) and the TL (SWd).
+ *
+ * Memory allocated in user space of the TLC can be mapped as additional
+ * communication channel (besides TCI) to the Trustlet. Limitation of the
+ * Trustlet memory structure apply: only 6 chunks can be mapped with a maximum
+ * chunk size of 1 MiB each.
+ *
+ * It is up to the application layer (TLC) to inform the Trustlet
+ * about the additional mapped bulk memory.
+ *
+ * Return codes:
+ * MC_DRV_OK: operation completed successfully
+ * MC_DRV_INVALID_PARAMETER: a parameter is invalid
+ * MC_DRV_ERR_UNKNOWN_SESSION: session id is invalid
+ * MC_DRV_ERR_UNKNOWN_DEVICE: device id of session is invalid
+ * MC_DRV_ERR_DAEMON_UNREACHABLE: problems with daemon occur
+ * MC_DRV_ERR_BULK_MAPPING: buf is already uses as bulk buffer or
+ * when registering the buffer failed
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_map(
+ struct mc_session_handle *session, void *buf, uint32_t len,
+ struct mc_bulk_map *map_info);
+
+/**
+ * mc_unmap() - Remove additional mapped bulk buffer between Trustlet Connector
+ * (TLC) and the Trustlet (TL) for a session
+ * @session: Session handle with information of the device_id and
+ * the session_id. The given buffer is unmapped from the
+ * session specified in the sessionHandle.
+ * @buf: Virtual address of a memory portion (relative to TLC)
+ * shared with the TL, already includes a possible offset!
+ * @map_info: Information structure about the mapped Bulk buffer
+ * between the TLC (NWd) and the TL (SWd)
+ *
+ * The bulk buffer will immediately be unmapped from the session context.
+ *
+ * The application layer (TLC) must inform the TL about unmapping of the
+ * additional bulk memory before calling mc_unmap!
+ *
+ * The clientlib currently ignores the len field in map_info.
+ *
+ * Return codes:
+ * MC_DRV_OK: operation completed successfully
+ * MC_DRV_INVALID_PARAMETER: a parameter is invalid
+ * MC_DRV_ERR_UNKNOWN_SESSION: session id is invalid
+ * MC_DRV_ERR_UNKNOWN_DEVICE: device id of session is invalid
+ * MC_DRV_ERR_DAEMON_UNREACHABLE: problems with daemon occur
+ * MC_DRV_ERR_BULK_UNMAPPING: buf was not registered earlier
+ * or when unregistering failed
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_unmap(
+ struct mc_session_handle *session, void *buf,
+ struct mc_bulk_map *map_info);
+
+/**
+ * mc_get_session_error_code() - Get additional error information of the last
+ * error that occurred on a session.
+ * @session: Session handle with information of the device_id and
+ * the session_id
+ * @last_error: >0 Trustlet has terminated itself with this value,
+ * <0 Trustlet is dead because of an error within the
+ * MobiCore (e.g. Kernel exception). See also MCI
+ * definition.
+ *
+ * After the request the stored error code will be deleted.
+ *
+ * Return codes:
+ * MC_DRV_OK: operation completed successfully
+ * MC_DRV_INVALID_PARAMETER: a parameter is invalid
+ * MC_DRV_ERR_UNKNOWN_SESSION: session id is invalid
+ * MC_DRV_ERR_UNKNOWN_DEVICE: device id of session is invalid
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_get_session_error_code(
+ struct mc_session_handle *session, int32_t *last_error);
+
+#endif /* _MOBICORE_DRIVER_API_H_ */
diff --git a/drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/public/mobicore_driver_cmd.h b/drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/public/mobicore_driver_cmd.h
new file mode 100644
index 000000000..4e6ba0ddf
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/public/mobicore_driver_cmd.h
@@ -0,0 +1,250 @@
+/*
+ * Copyright (c) 2013 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+#ifndef _MOBICORE_DRIVER_CMD_H_
+#define _MOBICORE_DRIVER_CMD_H_
+
+#include "mcuuid.h"
+
+enum mc_drv_cmd_t {
+ MC_DRV_CMD_PING = 0,
+ MC_DRV_CMD_GET_INFO = 1,
+ MC_DRV_CMD_OPEN_DEVICE = 2,
+ MC_DRV_CMD_CLOSE_DEVICE = 3,
+ MC_DRV_CMD_NQ_CONNECT = 4,
+ MC_DRV_CMD_OPEN_SESSION = 5,
+ MC_DRV_CMD_CLOSE_SESSION = 6,
+ MC_DRV_CMD_NOTIFY = 7,
+ MC_DRV_CMD_MAP_BULK_BUF = 8,
+ MC_DRV_CMD_UNMAP_BULK_BUF = 9
+};
+
+
+enum mc_drv_rsp_t {
+ MC_DRV_RSP_OK = 0,
+ MC_DRV_RSP_FAILED = 1,
+ MC_DRV_RSP_DEVICE_NOT_OPENED = 2,
+ MC_DRV_RSP_DEVICE_ALREADY_OPENED = 3,
+ MC_DRV_RSP_COMMAND_NOT_ALLOWED = 4,
+ MC_DRV_INVALID_DEVICE_NAME = 5,
+ MC_DRV_RSP_MAP_BULK_ERRO = 6,
+ MC_DRV_RSP_TRUSTLET_NOT_FOUND = 7,
+ MC_DRV_RSP_PAYLOAD_LENGTH_ERROR = 8,
+};
+
+
+struct mc_drv_command_header_t {
+ uint32_t command_id;
+};
+
+struct mc_drv_response_header_t {
+ uint32_t response_id;
+};
+
+#define MC_DEVICE_ID_DEFAULT 0 /* The default device ID */
+
+struct mc_drv_cmd_open_device_payload_t {
+ uint32_t device_id;
+};
+
+struct mc_drv_cmd_open_device_t {
+ struct mc_drv_command_header_t header;
+ struct mc_drv_cmd_open_device_payload_t payload;
+};
+
+
+struct mc_drv_rsp_open_device_payload_t {
+ /* empty */
+};
+
+struct mc_drv_rsp_open_device_t {
+ struct mc_drv_response_header_t header;
+ struct mc_drv_rsp_open_device_payload_t payload;
+};
+
+struct mc_drv_cmd_close_device_t {
+ struct mc_drv_command_header_t header;
+ /*
+ * no payload here because close has none.
+ * If we use an empty struct, C++ will count it as 4 bytes.
+ * This will write too much into the socket at write(cmd,sizeof(cmd))
+ */
+};
+
+
+struct mc_drv_rsp_close_device_payload_t {
+ /* empty */
+};
+
+struct mc_drv_rsp_close_device_t {
+ struct mc_drv_response_header_t header;
+ struct mc_drv_rsp_close_device_payload_t payload;
+};
+
+struct mc_drv_cmd_open_session_payload_t {
+ uint32_t device_id;
+ struct mc_uuid_t uuid;
+ uint32_t tci;
+ uint32_t handle;
+ uint32_t len;
+};
+
+struct mc_drv_cmd_open_session_t {
+ struct mc_drv_command_header_t header;
+ struct mc_drv_cmd_open_session_payload_t payload;
+};
+
+
+struct mc_drv_rsp_open_session_payload_t {
+ uint32_t session_id;
+ uint32_t device_session_id;
+ uint32_t session_magic;
+};
+
+struct mc_drv_rsp_open_session_t {
+ struct mc_drv_response_header_t header;
+ struct mc_drv_rsp_open_session_payload_t payload;
+};
+
+struct mc_drv_cmd_close_session_payload_t {
+ uint32_t session_id;
+};
+
+struct mc_drv_cmd_close_session_t {
+ struct mc_drv_command_header_t header;
+ struct mc_drv_cmd_close_session_payload_t payload;
+};
+
+
+struct mc_drv_rsp_close_session_payload_t {
+ /* empty */
+};
+
+struct mc_drv_rsp_close_session_t {
+ struct mc_drv_response_header_t header;
+ struct mc_drv_rsp_close_session_payload_t payload;
+};
+
+struct mc_drv_cmd_notify_payload_t {
+ uint32_t session_id;
+};
+
+struct mc_drv_cmd_notify_t {
+ struct mc_drv_command_header_t header;
+ struct mc_drv_cmd_notify_payload_t payload;
+};
+
+
+struct mc_drv_rsp_notify_payload_t {
+ /* empty */
+};
+
+struct mc_drv_rsp_notify_t {
+ struct mc_drv_response_header_t header;
+ struct mc_drv_rsp_notify_payload_t payload;
+};
+
+struct mc_drv_cmd_map_bulk_mem_payload_t {
+ uint32_t session_id;
+ uint32_t handle;
+ uint32_t rfu;
+ uint32_t offset_payload;
+ uint32_t len_bulk_mem;
+};
+
+struct mc_drv_cmd_map_bulk_mem_t {
+ struct mc_drv_command_header_t header;
+ struct mc_drv_cmd_map_bulk_mem_payload_t payload;
+};
+
+
+struct mc_drv_rsp_map_bulk_mem_payload_t {
+ uint32_t session_id;
+ uint32_t secure_virtual_adr;
+};
+
+struct mc_drv_rsp_map_bulk_mem_t {
+ struct mc_drv_response_header_t header;
+ struct mc_drv_rsp_map_bulk_mem_payload_t payload;
+};
+
+struct mc_drv_cmd_unmap_bulk_mem_payload_t {
+ uint32_t session_id;
+ uint32_t handle;
+ uint32_t secure_virtual_adr;
+ uint32_t len_bulk_mem;
+};
+
+struct mc_drv_cmd_unmap_bulk_mem_t {
+ struct mc_drv_command_header_t header;
+ struct mc_drv_cmd_unmap_bulk_mem_payload_t payload;
+};
+
+
+struct mc_drv_rsp_unmap_bulk_mem_payload_t {
+ uint32_t response_id;
+ uint32_t session_id;
+};
+
+struct mc_drv_rsp_unmap_bulk_mem_t {
+ struct mc_drv_response_header_t header;
+ struct mc_drv_rsp_unmap_bulk_mem_payload_t payload;
+};
+
+struct mc_drv_cmd_nqconnect_payload_t {
+ uint32_t device_id;
+ uint32_t session_id;
+ uint32_t device_session_id;
+ uint32_t session_magic; /* Random data */
+};
+
+struct mc_drv_cmd_nqconnect_t {
+ struct mc_drv_command_header_t header;
+ struct mc_drv_cmd_nqconnect_payload_t payload;
+};
+
+
+struct mc_drv_rsp_nqconnect_payload_t {
+ /* empty; */
+};
+
+struct mc_drv_rsp_nqconnect_t {
+ struct mc_drv_response_header_t header;
+ struct mc_drv_rsp_nqconnect_payload_t payload;
+};
+
+union mc_drv_command_t {
+ struct mc_drv_command_header_t header;
+ struct mc_drv_cmd_open_device_t mc_drv_cmd_open_device;
+ struct mc_drv_cmd_close_device_t mc_drv_cmd_close_device;
+ struct mc_drv_cmd_open_session_t mc_drv_cmd_open_session;
+ struct mc_drv_cmd_close_session_t mc_drv_cmd_close_session;
+ struct mc_drv_cmd_nqconnect_t mc_drv_cmd_nqconnect;
+ struct mc_drv_cmd_notify_t mc_drv_cmd_notify;
+ struct mc_drv_cmd_map_bulk_mem_t mc_drv_cmd_map_bulk_mem;
+ struct mc_drv_cmd_unmap_bulk_mem_t mc_drv_cmd_unmap_bulk_mem;
+};
+
+union mc_drv_response_t {
+ struct mc_drv_response_header_t header;
+ struct mc_drv_rsp_open_device_t mc_drv_rsp_open_device;
+ struct mc_drv_rsp_close_device_t mc_drv_rsp_close_device;
+ struct mc_drv_rsp_open_session_t mc_drv_rsp_open_session;
+ struct mc_drv_rsp_close_session_t mc_drv_rsp_close_session;
+ struct mc_drv_rsp_nqconnect_t mc_drv_rsp_nqconnect;
+ struct mc_drv_rsp_notify_t mc_drv_rsp_notify;
+ struct mc_drv_rsp_map_bulk_mem_t mc_drv_rsp_map_bulk_mem;
+ struct mc_drv_rsp_unmap_bulk_mem_t mc_drv_rsp_unmap_bulk_mem;
+};
+
+#endif /* _MOBICORE_DRIVER_CMD_H_ */
diff --git a/drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/session.c b/drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/session.c
new file mode 100644
index 000000000..639564673
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/session.c
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 2013-2015 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include "mc_kernel_api.h"
+#include "public/mobicore_driver_api.h"
+
+#include "session.h"
+
+struct bulk_buffer_descriptor *bulk_buffer_descriptor_create(
+ void *virt_addr, uint32_t len, uint32_t handle)
+{
+ struct bulk_buffer_descriptor *desc;
+
+ desc = kzalloc(sizeof(*desc), GFP_KERNEL);
+ if (desc == NULL) {
+ MCDRV_DBG_ERROR(mc_kapi, "Allocation failure");
+ return NULL;
+ }
+ desc->virt_addr = virt_addr;
+ desc->len = len;
+ desc->handle = handle;
+
+ return desc;
+}
+
+struct session *session_create(
+ uint32_t session_id, void *instance, struct connection *connection)
+{
+ struct session *session;
+
+ session = kzalloc(sizeof(*session), GFP_KERNEL);
+ if (session == NULL) {
+ MCDRV_DBG_ERROR(mc_kapi, "Allocation failure");
+ return NULL;
+ }
+ session->session_id = session_id;
+ session->instance = instance;
+ session->notification_connection = connection;
+ session->session_info.last_error = SESSION_ERR_NO;
+ session->session_info.state = SESSION_STATE_INITIAL;
+
+ INIT_LIST_HEAD(&(session->bulk_buffer_descriptors));
+ mutex_init(&(session->bulk_buffer_descriptors_lock));
+ return session;
+}
+
+void session_cleanup(struct session *session)
+{
+ struct bulk_buffer_descriptor *bulk_buf_descr;
+ struct list_head *pos, *q;
+
+ /* Unmap still mapped buffers */
+ mutex_lock(&(session->bulk_buffer_descriptors_lock));
+ list_for_each_safe(pos, q, &session->bulk_buffer_descriptors) {
+ bulk_buf_descr =
+ list_entry(pos, struct bulk_buffer_descriptor, list);
+
+ MCDRV_DBG_VERBOSE(mc_kapi,
+ "handle= %d",
+ bulk_buf_descr->handle);
+
+ /* ignore any error, as we cannot do anything in this case. */
+ int ret = mobicore_unmap_vmem(session->instance,
+ bulk_buf_descr->handle);
+ if (ret != 0)
+ MCDRV_DBG_ERROR(mc_kapi,
+ "mobicore_unmap_vmem failed: %d", ret);
+
+ list_del(pos);
+ kfree(bulk_buf_descr);
+ }
+ mutex_unlock(&(session->bulk_buffer_descriptors_lock));
+
+ /* Finally delete notification connection */
+ connection_cleanup(session->notification_connection);
+ kfree(session);
+}
+
+void session_set_error_info(struct session *session, int32_t err)
+{
+ session->session_info.last_error = err;
+}
+
+int32_t session_get_last_err(struct session *session)
+{
+ return session->session_info.last_error;
+}
+
+struct bulk_buffer_descriptor *session_add_bulk_buf(struct session *session,
+ void *buf, uint32_t len)
+{
+ struct bulk_buffer_descriptor *bulk_buf_descr = NULL;
+ struct bulk_buffer_descriptor *tmp;
+ struct list_head *pos;
+ int ret = 0;
+
+ /*
+ * Search bulk buffer descriptors for existing vAddr
+ * At the moment a virtual address can only be added one time
+ */
+ mutex_lock(&(session->bulk_buffer_descriptors_lock));
+ list_for_each(pos, &session->bulk_buffer_descriptors) {
+ tmp = list_entry(pos, struct bulk_buffer_descriptor, list);
+ if (tmp->virt_addr == buf) {
+ ret = -1;
+ break;
+ }
+ }
+ mutex_unlock(&(session->bulk_buffer_descriptors_lock));
+ if (ret == -1)
+ return NULL;
+ do {
+ /*
+ * Prepare the interface structure for memory registration in
+ * Kernel Module
+ */
+ uint32_t handle;
+
+ int ret = mobicore_map_vmem(session->instance, buf, len,
+ &handle);
+
+ if (ret != 0) {
+ MCDRV_DBG_ERROR(mc_kapi,
+ "mobicore_map_vmem failed, ret=%d",
+ ret);
+ break;
+ }
+
+ MCDRV_DBG_VERBOSE(mc_kapi, "handle=%d", handle);
+
+ /* Create new descriptor */
+ bulk_buf_descr =
+ bulk_buffer_descriptor_create(buf, len, handle);
+ if (bulk_buf_descr == NULL) {
+ /* Discard the returned value */
+ (void)mobicore_unmap_vmem(session->instance, handle);
+ break;
+ }
+
+ /* Add to vector of descriptors */
+ mutex_lock(&(session->bulk_buffer_descriptors_lock));
+ list_add_tail(&(bulk_buf_descr->list),
+ &(session->bulk_buffer_descriptors));
+ mutex_unlock(&(session->bulk_buffer_descriptors_lock));
+ } while (0);
+
+ return bulk_buf_descr;
+}
+
+bool session_remove_bulk_buf(struct session *session, void *virt_addr)
+{
+ bool ret = true;
+ struct bulk_buffer_descriptor *bulk_buf = NULL;
+ struct bulk_buffer_descriptor *tmp;
+ struct list_head *pos, *q;
+
+ MCDRV_DBG_VERBOSE(mc_kapi, "Virtual Address = 0x%p",
+ virt_addr);
+
+ /* Search and remove bulk buffer descriptor */
+ mutex_lock(&(session->bulk_buffer_descriptors_lock));
+ list_for_each_safe(pos, q, &session->bulk_buffer_descriptors) {
+ tmp = list_entry(pos, struct bulk_buffer_descriptor, list);
+ if (tmp->virt_addr == virt_addr) {
+ bulk_buf = tmp;
+ list_del(pos);
+ break;
+ }
+ }
+ mutex_unlock(&(session->bulk_buffer_descriptors_lock));
+
+ if (bulk_buf == NULL) {
+ MCDRV_DBG_ERROR(mc_kapi, "Virtual Address not found");
+ ret = false;
+ } else {
+ MCDRV_DBG_VERBOSE(mc_kapi, "Wsm handle=%d",
+ bulk_buf->handle);
+
+ /* ignore any error, as we cannot do anything */
+ int ret = mobicore_unmap_vmem(session->instance,
+ bulk_buf->handle);
+ if (ret != 0)
+ MCDRV_DBG_ERROR(mc_kapi,
+ "mobicore_unmap_vmem failed: %d", ret);
+
+ kfree(bulk_buf);
+ }
+
+ return ret;
+}
+
+uint32_t session_find_bulk_buf(struct session *session, void *virt_addr)
+{
+ struct bulk_buffer_descriptor *tmp;
+ struct list_head *pos, *q;
+ uint32_t handle = 0;
+
+ MCDRV_DBG_VERBOSE(mc_kapi, "Virtual Address = 0x%p",
+ virt_addr);
+
+ /* Search and return buffer descriptor handle */
+ mutex_lock(&(session->bulk_buffer_descriptors_lock));
+ list_for_each_safe(pos, q, &session->bulk_buffer_descriptors) {
+ tmp = list_entry(pos, struct bulk_buffer_descriptor, list);
+ if (tmp->virt_addr == virt_addr) {
+ handle = tmp->handle;
+ break;
+ }
+ }
+ mutex_unlock(&(session->bulk_buffer_descriptors_lock));
+ return handle;
+}
diff --git a/drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/session.h b/drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/session.h
new file mode 100644
index 000000000..0babd60c2
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/session.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2013-2015 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+#ifndef _MC_KAPI_SESSION_H_
+#define _MC_KAPI_SESSION_H_
+
+#include "common.h"
+
+#include <linux/list.h>
+#include "connection.h"
+
+
+struct bulk_buffer_descriptor {
+ void *virt_addr; /* The VA of the Bulk buffer */
+ uint32_t len; /* Length of the Bulk buffer */
+ uint32_t handle;
+
+ /* The list param for using the kernel lists*/
+ struct list_head list;
+};
+
+struct bulk_buffer_descriptor *bulk_buffer_descriptor_create(
+ void *virt_addr,
+ uint32_t len,
+ uint32_t handle
+);
+
+/*
+ * Session states.
+ * At the moment not used !!
+ */
+enum session_state {
+ SESSION_STATE_INITIAL,
+ SESSION_STATE_OPEN,
+ SESSION_STATE_TRUSTLET_DEAD
+};
+
+#define SESSION_ERR_NO 0 /* No session error */
+
+/*
+ * Session information structure.
+ * The information structure is used to hold the state of the session, which
+ * will limit further actions for the session.
+ * Also the last error code will be stored till it's read.
+ */
+struct session_information {
+ enum session_state state; /* Session state */
+ int32_t last_error; /* Last error of session */
+};
+
+
+struct session {
+ struct mc_instance *instance;
+
+ /* Descriptors of additional bulk buffer of a session */
+ struct list_head bulk_buffer_descriptors;
+/* lock used to prevent concurrent add/delete action on the descriptor list */
+ struct mutex bulk_buffer_descriptors_lock;
+
+ /* Information about session */
+ struct session_information session_info;
+
+ uint32_t session_id;
+ struct connection *notification_connection;
+
+ /* The list param for using the kernel lists */
+ struct list_head list;
+};
+
+struct session *session_create(
+ uint32_t session_id,
+ void *instance,
+ struct connection *connection
+);
+
+void session_cleanup(struct session *session);
+
+/*
+ * session_add_bulk_buf() - Add address information of additional bulk
+ * buffer memory to session and register virtual
+ * memory in kernel module
+ * @session: Session information structure
+ * @buf: The virtual address of bulk buffer.
+ * @len: Length of bulk buffer.
+ *
+ * The virtual address can only be added one time. If the virtual
+ * address already exist, NULL is returned.
+ *
+ * On success the actual Bulk buffer descriptor with all address information
+ * is returned, NULL if an error occurs.
+ */
+struct bulk_buffer_descriptor *session_add_bulk_buf(
+ struct session *session, void *buf, uint32_t len);
+
+/*
+ * session_remove_bulk_buf() - Remove address information of additional bulk
+ * buffer memory from session and unregister
+ * virtual memory in kernel module
+ * @session: Session information structure
+ * @buf: The virtual address of the bulk buffer
+ *
+ * Returns true on success
+ */
+bool session_remove_bulk_buf(struct session *session, void *buf);
+
+
+/*
+ * session_find_bulk_buf() - Find the handle of the bulk buffer for this
+ * session
+ *
+ * @session: Session information structure
+ * @buf: The virtual address of bulk buffer.
+ *
+ * On success the actual Bulk buffer handle is returned, 0
+ * if an error occurs.
+ */
+uint32_t session_find_bulk_buf(struct session *session, void *virt_addr);
+
+/*
+ * session_set_error_info() - Set additional error information of the last
+ * error that occurred.
+ * @session: Session information structure
+ * @err: The actual error
+ */
+void session_set_error_info(struct session *session, int32_t err);
+
+/*
+ * session_get_last_err() - Get additional error information of the last
+ * error that occurred.
+ * @session: Session information structure
+ *
+ * After request the information is set to SESSION_ERR_NO.
+ *
+ * Returns the last stored error code or SESSION_ERR_NO
+ */
+int32_t session_get_last_err(struct session *session);
+
+#endif /* _MC_KAPI_SESSION_H_ */
diff --git a/drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/wsm.h b/drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/wsm.h
new file mode 100644
index 000000000..b8d4b26c6
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/MobiCoreKernelApi/wsm.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2013 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+/*
+ * World shared memory definitions.
+ */
+#ifndef _MC_KAPI_WSM_H_
+#define _MC_KAPI_WSM_H_
+
+#include "common.h"
+#include <linux/list.h>
+
+struct wsm {
+ void *virt_addr;
+ uint32_t len;
+ uint32_t handle;
+ struct list_head list;
+};
+
+#endif /* _MC_KAPI_WSM_H_ */
diff --git a/drivers/misc/mediatek/gud/302c/gud/TlcTui/Out/Public/tui_ioctl.h b/drivers/misc/mediatek/gud/302c/gud/TlcTui/Out/Public/tui_ioctl.h
new file mode 100644
index 000000000..def13393d
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/TlcTui/Out/Public/tui_ioctl.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2013-2014 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+
+#ifndef TUI_IOCTL_H_
+#define TUI_IOCTL_H_
+
+
+
+/* Response header */
+struct tlc_tui_response_t {
+ uint32_t id;
+ uint32_t return_code;
+};
+
+/* Command IDs */
+#define TLC_TUI_CMD_NONE 0
+#define TLC_TUI_CMD_START_ACTIVITY 1
+#define TLC_TUI_CMD_STOP_ACTIVITY 2
+
+/* Return codes */
+#define TLC_TUI_OK 0
+#define TLC_TUI_ERROR 1
+#define TLC_TUI_ERR_UNKNOWN_CMD 2
+
+
+/*
+ * defines for the ioctl TUI driver module function call from user space.
+ */
+#define TUI_DEV_NAME "t-base-tui"
+
+#define TUI_IO_MAGIC 't'
+
+#define TUI_IO_NOTIFY _IOW(TUI_IO_MAGIC, 1, uint32_t)
+#define TUI_IO_WAITCMD _IOR(TUI_IO_MAGIC, 2, uint32_t)
+#define TUI_IO_ACK _IOW(TUI_IO_MAGIC, 3, struct tlc_tui_response_t)
+
+#endif /* TUI_IOCTL_H_ */
diff --git a/drivers/misc/mediatek/gud/302c/gud/TlcTui/build_tag.h b/drivers/misc/mediatek/gud/302c/gud/TlcTui/build_tag.h
new file mode 100644
index 000000000..9eda58638
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/TlcTui/build_tag.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2013-2014 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+#define MOBICORE_COMPONENT_BUILD_TAG \
+ "t-base-Mediatek-Armv8-Android-302C-V005-20151127_180020_32"
diff --git a/drivers/misc/mediatek/gud/302c/gud/TlcTui/inc/dciTui.h b/drivers/misc/mediatek/gud/302c/gud/TlcTui/inc/dciTui.h
new file mode 100644
index 000000000..5bee85cad
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/TlcTui/inc/dciTui.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2013-2014 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+
+#ifndef __DCITUI_H__
+#define __DCITUI_H__
+
+/**< Responses have bit 31 set */
+#define RSP_ID_MASK (1U << 31)
+#define RSP_ID(cmd_id) (((uint32_t)(cmd_id)) | RSP_ID_MASK)
+#define IS_CMD(cmd_id) ((((uint32_t)(cmd_id)) & RSP_ID_MASK) == 0)
+#define IS_RSP(cmd_id) ((((uint32_t)(cmd_id)) & RSP_ID_MASK) == RSP_ID_MASK)
+#define CMD_ID_FROM_RSP(rsp_id) (rsp_id & (~RSP_ID_MASK))
+
+/**
+ * Return codes of driver commands.
+ */
+#define TUI_DCI_OK 0x00030000
+#define TUI_DCI_ERR_UNKNOWN_CMD 0x00030001
+#define TUI_DCI_ERR_NOT_SUPPORTED 0x00030002
+#define TUI_DCI_ERR_INTERNAL_ERROR 0x00030003
+#define TUI_DCI_ERR_NO_RESPONSE 0x00030004
+#define TUI_DCI_ERR_BAD_PARAMETERS 0x00030005
+#define TUI_DCI_ERR_NO_EVENT 0x00030006
+#define TUI_DCI_ERR_OUT_OF_DISPLAY 0x00030007
+/* ... add more error codes when needed */
+
+
+/**
+ * Notification ID's for communication Trustlet Connector -> Driver.
+ */
+#define NOT_TUI_NONE 0
+/* NWd system event that closes the current TUI session*/
+#define NOT_TUI_CANCEL_EVENT 1
+
+
+/**
+ * Command ID's for communication Driver -> Trustlet Connector.
+ */
+#define CMD_TUI_SW_NONE 0
+/* SWd request to NWd to start the TUI session */
+#define CMD_TUI_SW_OPEN_SESSION 1
+/* SWd request to NWd to close the TUI session */
+#define CMD_TUI_SW_CLOSE_SESSION 2
+/* SWd request to NWd stop accessing display controller */
+#define CMD_TUI_SW_STOP_DISPLAY 3
+
+
+/**
+ * Maximum data length.
+ */
+#define MAX_DCI_DATA_LEN (1024*100)
+
+/* Command payload */
+struct tui_alloc_data_t {
+ uint32_t alloc_size;
+ uint32_t num_of_buff;
+};
+
+union dci_cmd_payload_t {
+ struct tui_alloc_data_t alloc_data;
+};
+
+/* Command */
+struct dci_command_t {
+ volatile uint32_t id;
+ union dci_cmd_payload_t payload;
+};
+
+/* TUI frame buffer (output from NWd) */
+typedef struct {
+ uint64_t pa;
+} tuiAllocBuffer_t;
+
+#define MAX_DCI_BUFFER_NUMBER 4
+
+/* Response */
+struct dci_response_t {
+ volatile uint32_t id; /* must be command ID | RSP_ID_MASK */
+ uint32_t return_code;
+ union {
+ tuiAllocBuffer_t alloc_buffer[MAX_DCI_BUFFER_NUMBER];
+ };
+};
+
+/* DCI buffer */
+struct tui_dci_msg_t {
+ volatile uint32_t nwd_notif; /* Notification from TlcTui to DrTui */
+ struct dci_command_t cmd_nwd; /* Command from DrTui to TlcTui */
+ struct dci_response_t nwd_rsp; /* Response from TlcTui to DrTui */
+};
+
+/**
+ * Driver UUID. Update accordingly after reserving UUID
+ */
+#define DR_TUI_UUID { { 7, 0xC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }
+
+#endif /* __DCITUI_H__ */
diff --git a/drivers/misc/mediatek/gud/302c/gud/TlcTui/inc/t-base-tui.h b/drivers/misc/mediatek/gud/302c/gud/TlcTui/inc/t-base-tui.h
new file mode 100644
index 000000000..4f34a286e
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/TlcTui/inc/t-base-tui.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2013-2014 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+
+#ifndef __TBASE_TUI_H__
+#define __TBASE_TUI_H__
+
+#define TRUSTEDUI_MODE_OFF 0x00
+#define TRUSTEDUI_MODE_ALL 0xff
+#define TRUSTEDUI_MODE_TUI_SESSION 0x01
+#define TRUSTEDUI_MODE_VIDEO_SECURED 0x02
+#define TRUSTEDUI_MODE_INPUT_SECURED 0x04
+
+#ifdef CONFIG_TRUSTONIC_TRUSTED_UI
+
+int trustedui_blank_inc(void);
+int trustedui_blank_dec(void);
+int trustedui_blank_get_counter(void);
+void trustedui_blank_set_counter(int counter);
+
+int trustedui_get_current_mode(void);
+void trustedui_set_mode(int mode);
+int trustedui_set_mask(int mask);
+int trustedui_clear_mask(int mask);
+
+#endif /* CONFIG_TRUSTONIC_TRUSTED_UI */
+
+#endif /* __TBASE_TUI_H__ */
diff --git a/drivers/misc/mediatek/gud/302c/gud/TlcTui/main.c b/drivers/misc/mediatek/gud/302c/gud/TlcTui/main.c
new file mode 100644
index 000000000..8cb82108e
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/TlcTui/main.c
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2013-2014 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/ioctl.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/completion.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#include "tui_ioctl.h"
+#include "tlcTui.h"
+#include "mobicore_driver_api.h"
+#include "dciTui.h"
+#include "tui-hal.h"
+#include "build_tag.h"
+
+/*static int tui_dev_major_number = 122; */
+
+/*module_param(tui_dev_major_number, int, 0000); */
+/*MODULE_PARM_DESC(major, */
+/* "The device major number used to register a unique char device driver"); */
+
+/* Static variables */
+static struct cdev tui_cdev;
+
+static long tui_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
+{
+ int ret = -ENOTTY;
+ int __user *uarg = (int __user *)arg;
+
+ if (_IOC_TYPE(cmd) != TUI_IO_MAGIC)
+ return -EINVAL;
+
+ pr_info("t-base-tui module: ioctl 0x%x ", cmd);
+
+ switch (cmd) {
+ case TUI_IO_NOTIFY:
+ pr_info("TUI_IO_NOTIFY\n");
+
+ if (tlc_notify_event(arg))
+ ret = 0;
+ else
+ ret = -EFAULT;
+ break;
+
+ case TUI_IO_WAITCMD: {
+ uint32_t cmd_id;
+
+ pr_info("TUI_IO_WAITCMD\n");
+
+ ret = tlc_wait_cmd(&cmd_id);
+ if (ret)
+ return ret;
+
+ /* Write command id to user */
+ pr_debug("IOCTL: sending command %d to user.\n", cmd_id);
+
+ if (copy_to_user(uarg, &cmd_id, sizeof(cmd_id)))
+ ret = -EFAULT;
+ else
+ ret = 0;
+
+ /* Reset the value of the command, to ensure that commands sent
+ * due to interrupted wait_for_completion are TLC_TUI_CMD_NONE.
+ */
+ reset_global_command_id();
+
+ break;
+ }
+
+ case TUI_IO_ACK: {
+ struct tlc_tui_response_t rsp_id;
+
+ pr_info("TUI_IO_ACK\n");
+
+ /* Read user response */
+ if (copy_from_user(&rsp_id, uarg, sizeof(rsp_id)))
+ ret = -EFAULT;
+ else
+ ret = 0;
+
+ pr_debug("IOCTL: User completed command %d.\n", rsp_id.id);
+ ret = tlc_ack_cmd(&rsp_id);
+ if (ret)
+ return ret;
+ break;
+ }
+
+ default:
+ pr_info("undefined!\n");
+ return -ENOTTY;
+ }
+
+ return ret;
+}
+
+static const struct file_operations tui_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = tui_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = tui_ioctl,
+#endif
+};
+
+/*--------------------------------------------------------------------------- */
+static int __init tlc_tui_init(void)
+{
+ pr_info("Loading t-base-tui module.\n");
+ pr_debug("\n=============== Running TUI Kernel TLC ===============\n");
+ pr_info("%s\n", MOBICORE_COMPONENT_BUILD_TAG);
+
+ dev_t devno;
+ int err;
+ static struct class *tui_class;
+
+ err = alloc_chrdev_region(&devno, 0, 1, TUI_DEV_NAME);
+ if (err) {
+ pr_debug(KERN_ERR "Unable to allocate Trusted UI device number\n");
+ return err;
+ }
+
+ cdev_init(&tui_cdev, &tui_fops);
+ tui_cdev.owner = THIS_MODULE;
+ /* tui_cdev.ops = &tui_fops; */
+
+ err = cdev_add(&tui_cdev, devno, 1);
+ if (err) {
+ pr_debug(KERN_ERR "Unable to add Trusted UI char device\n");
+ unregister_chrdev_region(devno, 1);
+ return err;
+ }
+
+ tui_class = class_create(THIS_MODULE, "tui_cls");
+ device_create(tui_class, NULL, devno, NULL, TUI_DEV_NAME);
+
+ if (!hal_tui_init())
+ return -1;
+
+ return 0;
+}
+
+static void __exit tlc_tui_exit(void)
+{
+ pr_info("Unloading t-base-tui module.\n");
+
+ unregister_chrdev_region(tui_cdev.dev, 1);
+ cdev_del(&tui_cdev);
+
+ hal_tui_exit();
+}
+
+module_init(tlc_tui_init);
+module_exit(tlc_tui_exit);
+
+MODULE_AUTHOR("Trustonic Limited");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("<t-base TUI");
diff --git a/drivers/misc/mediatek/gud/302c/gud/TlcTui/public/tui_ioctl.h b/drivers/misc/mediatek/gud/302c/gud/TlcTui/public/tui_ioctl.h
new file mode 100644
index 000000000..def13393d
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/TlcTui/public/tui_ioctl.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2013-2014 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+
+#ifndef TUI_IOCTL_H_
+#define TUI_IOCTL_H_
+
+
+
+/* Response header */
+struct tlc_tui_response_t {
+ uint32_t id;
+ uint32_t return_code;
+};
+
+/* Command IDs */
+#define TLC_TUI_CMD_NONE 0
+#define TLC_TUI_CMD_START_ACTIVITY 1
+#define TLC_TUI_CMD_STOP_ACTIVITY 2
+
+/* Return codes */
+#define TLC_TUI_OK 0
+#define TLC_TUI_ERROR 1
+#define TLC_TUI_ERR_UNKNOWN_CMD 2
+
+
+/*
+ * defines for the ioctl TUI driver module function call from user space.
+ */
+#define TUI_DEV_NAME "t-base-tui"
+
+#define TUI_IO_MAGIC 't'
+
+#define TUI_IO_NOTIFY _IOW(TUI_IO_MAGIC, 1, uint32_t)
+#define TUI_IO_WAITCMD _IOR(TUI_IO_MAGIC, 2, uint32_t)
+#define TUI_IO_ACK _IOW(TUI_IO_MAGIC, 3, struct tlc_tui_response_t)
+
+#endif /* TUI_IOCTL_H_ */
diff --git a/drivers/misc/mediatek/gud/302c/gud/TlcTui/tlcTui.c b/drivers/misc/mediatek/gud/302c/gud/TlcTui/tlcTui.c
new file mode 100644
index 000000000..7ea5f1bc0
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/TlcTui/tlcTui.c
@@ -0,0 +1,380 @@
+/*
+ * Copyright (c) 2013-2014 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+
+
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+
+#include "mobicore_driver_api.h"
+#include "tui_ioctl.h"
+#include "tlcTui.h"
+#include "dciTui.h"
+#include "tui-hal.h"
+
+
+/* ------------------------------------------------------------- */
+/* Globals */
+struct tui_dci_msg_t *dci;
+DECLARE_COMPLETION(dci_comp);
+DECLARE_COMPLETION(io_comp);
+
+/* ------------------------------------------------------------- */
+/* Static */
+static const uint32_t DEVICE_ID = MC_DEVICE_ID_DEFAULT;
+static struct task_struct *thread_id;
+static uint32_t g_cmd_id = TLC_TUI_CMD_NONE;
+static struct mc_session_handle dr_session_handle = {0, 0};
+static struct tlc_tui_response_t g_user_rsp = {
+ TLC_TUI_CMD_NONE, TLC_TUI_ERR_UNKNOWN_CMD};
+/* Functions */
+
+/* ------------------------------------------------------------- */
+static bool tlc_open_driver(void)
+{
+ bool ret = false;
+ enum mc_result mc_ret;
+ struct mc_uuid_t dr_uuid = DR_TUI_UUID;
+
+ /* Allocate WSM buffer for the DCI */
+ mc_ret = mc_malloc_wsm(DEVICE_ID, 0, sizeof(struct tui_dci_msg_t),
+ (uint8_t **)&dci, 0);
+ if (MC_DRV_OK != mc_ret) {
+ pr_debug("ERROR %s: Allocation of DCI WSM failed: %d\n",
+ __func__, mc_ret);
+ return false;
+ }
+
+ /* Clear the session handle */
+ memset(&dr_session_handle, 0, sizeof(dr_session_handle));
+ /* The device ID (default device is used */
+ dr_session_handle.device_id = DEVICE_ID;
+ /* Open session with the Driver */
+ mc_ret = mc_open_session(&dr_session_handle, &dr_uuid, (uint8_t *)dci,
+ (uint32_t)sizeof(struct tui_dci_msg_t));
+ if (MC_DRV_OK != mc_ret) {
+ pr_debug("ERROR %s: Open driver session failed: %d\n",
+ __func__, mc_ret);
+ ret = false;
+ } else {
+ ret = true;
+ }
+
+ return ret;
+}
+
+
+/* ------------------------------------------------------------- */
+static bool tlc_open(void)
+{
+ bool ret = false;
+ enum mc_result mc_ret;
+
+ /* Open the tbase device */
+ pr_debug("%s: Opening tbase device\n", __func__);
+ mc_ret = mc_open_device(DEVICE_ID);
+
+ /* In case the device is already open, mc_open_device will return an
+ * error (MC_DRV_ERR_INVALID_OPERATION). But in this case, we can
+ * continue, even though mc_open_device returned an error. Stop in all
+ * other case of error
+ */
+ if (MC_DRV_OK != mc_ret && MC_DRV_ERR_INVALID_OPERATION != mc_ret) {
+ pr_debug("ERROR %s: Error %d opening device\n", __func__,
+ mc_ret);
+ return false;
+ }
+
+ pr_debug("%s: Opening driver session\n", __func__);
+ ret = tlc_open_driver();
+
+ return ret;
+}
+
+
+/* ------------------------------------------------------------- */
+static void tlc_wait_cmd_from_driver(void)
+{
+ uint32_t ret = TUI_DCI_ERR_INTERNAL_ERROR;
+
+ /* Wait for a command from secure driver */
+ ret = mc_wait_notification(&dr_session_handle, -1);
+ if (MC_DRV_OK == ret)
+ pr_debug("tlc_wait_cmd_from_driver: Got a command\n");
+ else
+ pr_debug("ERROR %s: mc_wait_notification() failed: %d\n",
+ __func__, ret);
+}
+
+
+static uint32_t send_cmd_to_user(uint32_t command_id)
+{
+ uint32_t ret = TUI_DCI_ERR_NO_RESPONSE;
+
+ /* Init shared variables */
+ g_cmd_id = command_id;
+ g_user_rsp.id = TLC_TUI_CMD_NONE;
+ g_user_rsp.return_code = TLC_TUI_ERR_UNKNOWN_CMD;
+
+ /* Give way to ioctl thread */
+ complete(&dci_comp);
+ pr_debug("send_cmd_to_user: give way to ioctl thread\n");
+
+ /* Wait for ioctl thread to complete */
+ wait_for_completion(&io_comp);
+ pr_debug("send_cmd_to_user: Got an answer from ioctl thread.\n");
+ reinit_completion(&io_comp);
+
+ /* Check id of the cmd processed by ioctl thread (paranoia) */
+ if (g_user_rsp.id != command_id) {
+ pr_debug("ERROR %s: Wrong response id 0x%08x iso 0x%08x\n",
+ __func__, dci->nwd_rsp.id, RSP_ID(command_id));
+ ret = TUI_DCI_ERR_INTERNAL_ERROR;
+ } else {
+ /* retrieve return code */
+ switch (g_user_rsp.return_code) {
+ case TLC_TUI_OK:
+ ret = TUI_DCI_OK;
+ break;
+ case TLC_TUI_ERROR:
+ ret = TUI_DCI_ERR_INTERNAL_ERROR;
+ break;
+ case TLC_TUI_ERR_UNKNOWN_CMD:
+ ret = TUI_DCI_ERR_UNKNOWN_CMD;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/* ------------------------------------------------------------- */
+static void tlc_process_cmd(void)
+{
+ uint32_t ret = TUI_DCI_ERR_INTERNAL_ERROR;
+ uint32_t command_id = CMD_TUI_SW_NONE;
+
+ if (NULL == dci) {
+ pr_debug("ERROR %s: DCI has not been set up properly - exiting"\
+ "\n", __func__);
+ return;
+ } else {
+ command_id = dci->cmd_nwd.id;
+ }
+
+ /* Warn if previous response was not acknowledged */
+ if (CMD_TUI_SW_NONE == command_id) {
+ pr_debug("ERROR %s: Notified without command\n", __func__);
+ return;
+ } else {
+ if (dci->nwd_rsp.id != CMD_TUI_SW_NONE)
+ pr_debug("%s: Warning, previous response not ack\n",
+ __func__);
+ }
+
+ /* Handle command */
+ switch (command_id) {
+ case CMD_TUI_SW_OPEN_SESSION:
+ pr_debug("%s: CMD_TUI_SW_OPEN_SESSION.\n", __func__);
+
+ /* Start android TUI activity */
+ ret = send_cmd_to_user(TLC_TUI_CMD_START_ACTIVITY);
+ if (TUI_DCI_OK != ret)
+ break;
+
+ /* allocate TUI frame buffer */
+ ret = hal_tui_alloc(dci->nwd_rsp.alloc_buffer,
+ dci->cmd_nwd.payload.alloc_data.alloc_size,
+ dci->cmd_nwd.payload.alloc_data.num_of_buff);
+
+ if (TUI_DCI_OK != ret)
+ break;
+
+ /* Deactivate linux UI drivers */
+ ret = hal_tui_deactivate();
+
+ if (TUI_DCI_OK != ret) {
+ hal_tui_free();
+ send_cmd_to_user(TLC_TUI_CMD_STOP_ACTIVITY);
+ break;
+ }
+
+ break;
+
+ case CMD_TUI_SW_CLOSE_SESSION:
+ pr_debug("%s: CMD_TUI_SW_CLOSE_SESSION.\n", __func__);
+
+ /* Activate linux UI drivers */
+ ret = hal_tui_activate();
+
+ hal_tui_free();
+
+ /* Stop android TUI activity */
+ ret = send_cmd_to_user(TLC_TUI_CMD_STOP_ACTIVITY);
+ break;
+
+ default:
+ pr_debug("ERROR %s: Unknown command %d\n",
+ __func__, command_id);
+ break;
+ }
+
+ /* Fill in response to SWd, fill ID LAST */
+ pr_debug("%s: return 0x%08x to cmd 0x%08x\n",
+ __func__, ret, command_id);
+ dci->nwd_rsp.return_code = ret;
+ dci->nwd_rsp.id = RSP_ID(command_id);
+
+ /* Acknowledge command */
+ dci->cmd_nwd.id = CMD_TUI_SW_NONE;
+
+ /* Notify SWd */
+ pr_debug("DCI RSP NOTIFY CORE\n");
+ ret = mc_notify(&dr_session_handle);
+ if (MC_DRV_OK != ret)
+ pr_debug("ERROR %s: Notify failed: %d\n", __func__, ret);
+}
+
+
+/* ------------------------------------------------------------- */
+static void tlc_close_driver(void)
+{
+ enum mc_result ret;
+
+ /* Close session with the Driver */
+ ret = mc_close_session(&dr_session_handle);
+ if (MC_DRV_OK != ret) {
+ pr_debug("ERROR %s: Closing driver session failed: %d\n",
+ __func__, ret);
+ }
+}
+
+
+/* ------------------------------------------------------------- */
+static void tlc_close(void)
+{
+ enum mc_result ret;
+
+ pr_debug("%s: Closing driver session\n", __func__);
+ tlc_close_driver();
+
+ pr_debug("%s: Closing tbase\n", __func__);
+ /* Close the tbase device */
+ ret = mc_close_device(DEVICE_ID);
+ if (MC_DRV_OK != ret) {
+ pr_debug("ERROR %s: Closing tbase device failed: %d\n",
+ __func__, ret);
+ }
+}
+
+void reset_global_command_id(void)
+{
+ g_cmd_id = TLC_TUI_CMD_NONE;
+}
+
+/* ------------------------------------------------------------- */
+bool tlc_notify_event(uint32_t event_type)
+{
+ bool ret = false;
+ enum mc_result result;
+
+ if (NULL == dci) {
+ pr_debug("ERROR tlc_notify_event: DCI has not been set up "\
+ "properly - exiting\n");
+ return false;
+ }
+
+ /* Prepare notification message in DCI */
+ pr_debug("tlc_notify_event: event_type = %d\n", event_type);
+ dci->nwd_notif = event_type;
+
+ /* Signal the Driver */
+ pr_debug("DCI EVENT NOTIFY CORE\n");
+ result = mc_notify(&dr_session_handle);
+ if (MC_DRV_OK != result) {
+ pr_debug("ERROR tlc_notify_event: mc_notify failed: %d\n",
+ result);
+ ret = false;
+ } else {
+ ret = true;
+ }
+
+ return ret;
+}
+
+/* ------------------------------------------------------------- */
+/**
+ */
+int main_thread(void *uarg)
+{
+ pr_debug("main_thread: TlcTui start!\n");
+
+ /* Open session on the driver */
+ if (!tlc_open()) {
+ pr_debug("ERROR main_thread: open driver failed!\n");
+ return 1;
+ }
+
+ /* TlcTui main thread loop */
+ for (;;) {
+ /* Wait for a command from the DrTui on DCI*/
+ tlc_wait_cmd_from_driver();
+ /* Something has been received, process it. */
+ tlc_process_cmd();
+ }
+
+ /* Close tlc. Note that this frees the DCI pointer.
+ * Do not use this pointer after tlc_close().*/
+ tlc_close();
+
+ return 0;
+}
+
+int tlc_wait_cmd(uint32_t *cmd_id)
+{
+ /* Create the TlcTui Main thread and start secure driver (only
+ 1st time) */
+ if (dr_session_handle.session_id == 0) {
+ thread_id = kthread_run(main_thread, NULL, "dci_thread");
+ if (!thread_id) {
+ pr_debug(KERN_ERR "Unable to start Trusted UI main thread\n");
+ return -EFAULT;
+ }
+ }
+
+ /* Wait for signal from DCI handler */
+ /* In case of an interrupted sys call, return with -EINTR */
+ if (wait_for_completion_interruptible(&dci_comp)) {
+ pr_debug("interrupted by system\n");
+ return -ERESTARTSYS;
+ }
+ reinit_completion(&dci_comp);
+
+ *cmd_id = g_cmd_id;
+ return 0;
+}
+
+int tlc_ack_cmd(struct tlc_tui_response_t *rsp_id)
+{
+ g_user_rsp = *rsp_id;
+
+ /* Send signal to DCI */
+ complete(&io_comp);
+
+ return 0;
+}
+
+/** @} */
diff --git a/drivers/misc/mediatek/gud/302c/gud/TlcTui/tlcTui.h b/drivers/misc/mediatek/gud/302c/gud/TlcTui/tlcTui.h
new file mode 100644
index 000000000..bd8eb3036
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/TlcTui/tlcTui.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2013-2014 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+
+#ifndef TLCTUI_H_
+#define TLCTUI_H_
+
+void reset_global_command_id(void);
+int tlc_wait_cmd(uint32_t *cmd_id);
+int tlc_ack_cmd(struct tlc_tui_response_t *rsp_id);
+bool tlc_notify_event(uint32_t event_type);
+
+#endif /* TLCTUI_H_ */
diff --git a/drivers/misc/mediatek/gud/302c/gud/TlcTui/trustedui.c b/drivers/misc/mediatek/gud/302c/gud/TlcTui/trustedui.c
new file mode 100644
index 000000000..91e27ac26
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/TlcTui/trustedui.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2013 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+
+/**
+ * File : trustedui.c
+ * Created : 26-02-2010
+ */
+
+#include <linux/spinlock.h>
+#include <linux/module.h>
+//#include <linux/t-base-tui.h>
+#include <t-base-tui.h>
+
+static int trustedui_mode = TRUSTEDUI_MODE_OFF;
+static int trustedui_blank_counter;
+
+static DEFINE_SPINLOCK(trustedui_lock);
+
+int trustedui_blank_inc(void)
+{
+ unsigned long flags;
+ int newvalue;
+
+ spin_lock_irqsave(&trustedui_lock, flags);
+ newvalue = ++trustedui_blank_counter;
+ spin_unlock_irqrestore(&trustedui_lock, flags);
+
+ return newvalue;
+}
+EXPORT_SYMBOL(trustedui_blank_inc);
+
+int trustedui_blank_dec(void)
+{
+ unsigned long flags;
+ int newvalue;
+
+ spin_lock_irqsave(&trustedui_lock, flags);
+ newvalue = --trustedui_blank_counter;
+ spin_unlock_irqrestore(&trustedui_lock, flags);
+
+ return newvalue;
+}
+EXPORT_SYMBOL(trustedui_blank_dec);
+
+int trustedui_blank_get_counter(void)
+{
+ unsigned long flags;
+ int newvalue;
+
+ spin_lock_irqsave(&trustedui_lock, flags);
+ newvalue = trustedui_blank_counter;
+ spin_unlock_irqrestore(&trustedui_lock, flags);
+
+ return newvalue;
+}
+EXPORT_SYMBOL(trustedui_blank_get_counter);
+
+void trustedui_blank_set_counter(int counter)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&trustedui_lock, flags);
+ trustedui_blank_counter = counter;
+ spin_unlock_irqrestore(&trustedui_lock, flags);
+}
+EXPORT_SYMBOL(trustedui_blank_set_counter);
+
+int trustedui_get_current_mode(void)
+{
+ unsigned long flags;
+ int mode;
+
+ spin_lock_irqsave(&trustedui_lock, flags);
+ mode = trustedui_mode;
+ spin_unlock_irqrestore(&trustedui_lock, flags);
+
+ return mode;
+}
+EXPORT_SYMBOL(trustedui_get_current_mode);
+
+void trustedui_set_mode(int mode)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&trustedui_lock, flags);
+ trustedui_mode = mode;
+ spin_unlock_irqrestore(&trustedui_lock, flags);
+}
+EXPORT_SYMBOL(trustedui_set_mode);
+
+
+int trustedui_set_mask(int mask)
+{
+ unsigned long flags;
+ int mode;
+
+ spin_lock_irqsave(&trustedui_lock, flags);
+ mode = trustedui_mode |= mask;
+ spin_unlock_irqrestore(&trustedui_lock, flags);
+
+ return mode;
+}
+EXPORT_SYMBOL(trustedui_set_mask);
+
+int trustedui_clear_mask(int mask)
+{
+ unsigned long flags;
+ int mode;
+
+ spin_lock_irqsave(&trustedui_lock, flags);
+ mode = trustedui_mode &= ~mask;
+ spin_unlock_irqrestore(&trustedui_lock, flags);
+
+ return mode;
+}
+EXPORT_SYMBOL(trustedui_clear_mask);
+
+MODULE_AUTHOR("Trustonic Limited");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("<t-base TUI");
diff --git a/drivers/misc/mediatek/gud/302c/gud/TlcTui/tui-hal.h b/drivers/misc/mediatek/gud/302c/gud/TlcTui/tui-hal.h
new file mode 100644
index 000000000..778b49338
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/TlcTui/tui-hal.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2014 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+
+#ifndef _TUI_HAL_H_
+#define _TUI_HAL_H_
+
+#include <linux/types.h>
+
+uint32_t hal_tui_init(void);
+void hal_tui_exit(void);
+uint32_t hal_tui_alloc(tuiAllocBuffer_t allocbuffer[MAX_DCI_BUFFER_NUMBER],
+ size_t allocsize, uint32_t number);
+void hal_tui_free(void);
+uint32_t hal_tui_deactivate(void);
+uint32_t hal_tui_activate(void);
+
+#endif
diff --git a/drivers/misc/mediatek/gud/302c/gud/TlcTui/tui-hal_mt6735.c b/drivers/misc/mediatek/gud/302c/gud/TlcTui/tui-hal_mt6735.c
new file mode 100644
index 000000000..7b93981f6
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/TlcTui/tui-hal_mt6735.c
@@ -0,0 +1,414 @@
+/*
+ * Copyright (c) 2014 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/fb.h>
+
+#include <t-base-tui.h>
+
+#include "tui_ioctl.h"
+#include "dciTui.h"
+#include "tlcTui.h"
+#include "tui-hal.h"
+#include <linux/delay.h>
+
+#include <mach/mt_clkmgr.h>
+
+
+#define TUI_MEMPOOL_SIZE 0
+
+/* Extrac memory size required for TUI driver */
+#define TUI_EXTRA_MEM_SIZE (0x200000)
+
+struct tui_mempool {
+ void *va;
+ unsigned long pa;
+ size_t size;
+};
+
+/* for TUI EINT mepping to Security World */
+extern void gt1x_power_reset(void);
+extern int mt_eint_set_deint(int eint_num, int irq_num);
+extern int mt_eint_clr_deint(int eint_num);
+extern int tpd_reregister_from_tui(void);
+extern int tpd_enter_tui(void);
+extern int tpd_exit_tui(void);
+extern int i2c_tui_enable_clock(void);
+extern int i2c_tui_disable_clock(void);
+#ifndef CONFIG_CMA
+extern int secmem_api_alloc_pa(u32 alignment, u32 size, u32 *refcount, u32 *sec_handle,
+ const uint8_t *owner, uint32_t id);
+#endif
+extern int secmem_api_unref_pa(u32 sec_handle, const uint8_t *owner, uint32_t id);
+extern int tui_region_offline(phys_addr_t *pa, unsigned long *size);
+extern int tui_region_online(void);
+static struct tui_mempool g_tui_mem_pool;
+static u32 g_tui_secmem_handle;
+extern int display_enter_tui(void);
+extern int display_exit_tui(void);
+
+
+/* basic implementation of a memory pool for TUI framebuffer. This
+ * implementation is using kmalloc, for the purpose of demonstration only.
+ * A real implementation might prefer using more advanced allocator, like ION,
+ * in order not to exhaust memory available to kmalloc
+ */
+static bool allocate_tui_memory_pool(struct tui_mempool *pool, size_t size)
+{
+ bool ret = false;
+ void *tui_mem_pool = NULL;
+
+ pr_info("%s %s:%d\n", __func__, __FILE__, __LINE__);
+ if (!size) {
+ pr_debug("TUI frame buffer: nothing to allocate.");
+ return true;
+ }
+
+ tui_mem_pool = kmalloc(size, GFP_KERNEL);
+ if (!tui_mem_pool) {
+ pr_debug("ERROR Could not allocate TUI memory pool");
+ } else if (ksize(tui_mem_pool) < size) {
+ pr_debug("ERROR TUI memory pool allocated size is too small."\
+ " required=%zd allocated=%zd",
+ size, ksize(tui_mem_pool));
+ kfree(tui_mem_pool);
+ } else {
+ pool->va = tui_mem_pool;
+ pool->pa = virt_to_phys(tui_mem_pool);
+ pool->size = ksize(tui_mem_pool);
+ ret = true;
+ }
+ return ret;
+}
+
+static void free_tui_memory_pool(struct tui_mempool *pool)
+{
+ kfree(pool->va);
+ memset(pool, 0, sizeof(*pool));
+}
+
+/**
+ * hal_tui_init() - integrator specific initialization for kernel module
+ *
+ * This function is called when the kernel module is initialized, either at
+ * boot time, if the module is built statically in the kernel, or when the
+ * kernel is dynamically loaded if the module is built as a dynamic kernel
+ * module. This function may be used by the integrator, for instance, to get a
+ * memory pool that will be used to allocate the secure framebuffer and work
+ * buffer for TUI sessions.
+ *
+ * Return: must return 0 on success, or non-zero on error. If the function
+ * returns an error, the module initialization will fail.
+ */
+uint32_t hal_tui_init(void)
+{
+ /* Allocate memory pool for the framebuffer
+ */
+ if (!allocate_tui_memory_pool(&g_tui_mem_pool, TUI_MEMPOOL_SIZE))
+ return TUI_DCI_ERR_INTERNAL_ERROR;
+
+ return TUI_DCI_OK;
+}
+
+/**
+ * hal_tui_exit() - integrator specific exit code for kernel module
+ *
+ * This function is called when the kernel module exit. It is called when the
+ * kernel module is unloaded, for a dynamic kernel module, and never called for
+ * a module built into the kernel. It can be used to free any resources
+ * allocated by hal_tui_init().
+ */
+void hal_tui_exit(void)
+{
+ /* delete memory pool if any */
+ if (g_tui_mem_pool.va)
+ free_tui_memory_pool(&g_tui_mem_pool);
+}
+
+/**
+ * hal_tui_alloc() - allocator for secure framebuffer and working buffer
+ * @allocbuffer: putput parameter that the allocator fills with the physical
+ * addresses of the allocated buffers
+ * @allocsize: size of the buffer to allocate. All the buffer are of the
+ * same size
+ * @number: Number to allocate.
+ *
+ * This function is called when the module receives a CMD_TUI_SW_OPEN_SESSION
+ * message from the secure driver. The function must allocate 'number'
+ * buffer(s) of physically contiguous memory, where the length of each buffer
+ * is at least 'allocsize' bytes. The physical address of each buffer must be
+ * stored in the array of structure 'allocbuffer' which is provided as
+ * arguments.
+ *
+ * Physical address of the first buffer must be put in allocate[0].pa , the
+ * second one on allocbuffer[1].pa, and so on. The function must return 0 on
+ * success, non-zero on error. For integrations where the framebuffer is not
+ * allocated by the Normal World, this function should do nothing and return
+ * success (zero).
+ */
+uint32_t hal_tui_alloc(
+ tuiAllocBuffer_t *allocbuffer, size_t allocsize, uint32_t number)
+{
+ uint32_t ret = TUI_DCI_ERR_INTERNAL_ERROR;
+#ifndef CONFIG_CMA
+ u32 refcount = 0;
+ u32 sec_pa = 0;
+#else
+ phys_addr_t pa = 0;
+ unsigned long size = 0;
+#endif
+
+ if (!allocbuffer) {
+ pr_debug("%s(%d): allocbuffer is null\n", __func__, __LINE__);
+ return TUI_DCI_ERR_INTERNAL_ERROR;
+ }
+
+ pr_debug("%s(%d): Requested size=0x%zx x %u chunks\n",
+ __func__, __LINE__, allocsize, number);
+
+ if ((size_t)allocsize == 0) {
+ pr_debug("%s(%d): Nothing to allocate\n", __func__, __LINE__);
+ return TUI_DCI_OK;
+ }
+
+ if (number != 2) {
+ pr_debug("%s(%d): Unexpected number of buffers requested\n",
+ __func__, __LINE__);
+ return TUI_DCI_ERR_INTERNAL_ERROR;
+ }
+
+#ifndef CONFIG_CMA
+ ret = secmem_api_alloc_pa(4096, allocsize*number+TUI_EXTRA_MEM_SIZE, &refcount,
+ &sec_pa, __func__, __LINE__);
+ pr_err("%s: sec_pa=%x ret=%d", __func__, sec_pa, (int)ret);
+ if (ret) {
+ pr_err("%s(%d): secmem_api_alloc failed! ret=%d\n",
+ __func__, __LINE__, ret);
+ return TUI_DCI_ERR_INTERNAL_ERROR;
+ }
+#else
+ ret = tui_region_offline(&pa, &size);
+ pr_debug("tui pa=0x%x, size=0x%lx", (uint32_t)pa, size);
+ if (ret) {
+ pr_err("%s(%d): tui_region_offline failed!\n",
+ __func__, __LINE__);
+ return TUI_DCI_ERR_INTERNAL_ERROR;
+ }
+#endif
+
+ if (ret == 0) {
+#ifndef CONFIG_CMA
+ g_tui_secmem_handle = sec_pa;
+ allocbuffer[0].pa = (uint64_t) sec_pa;
+ allocbuffer[1].pa = (uint64_t) (sec_pa + allocsize);
+#else
+ g_tui_secmem_handle = (u32)pa;
+ allocbuffer[0].pa = (uint64_t) pa;
+ allocbuffer[1].pa = (uint64_t) (pa + allocsize);
+#endif
+ } else {
+ return TUI_DCI_ERR_INTERNAL_ERROR;
+ }
+
+ pr_debug("tui-hal allocasize=%ld number=%d, extra=%d\n", allocsize, number, TUI_EXTRA_MEM_SIZE);
+ pr_debug("%s(%d): allocated at %llx\n", __func__, __LINE__,
+ allocbuffer[0].pa);
+ pr_debug("%s(%d): allocated at %llx\n", __func__, __LINE__,
+ allocbuffer[1].pa);
+
+ return TUI_DCI_OK;
+
+#if 0
+ if ((size_t)(allocsize*number) <= g_tui_mem_pool.size) {
+ /* requested buffer fits in the memory pool */
+ allocbuffer[0].pa = (uint64_t) g_tui_mem_pool.pa;
+ allocbuffer[1].pa = (uint64_t) (g_tui_mem_pool.pa +
+ g_tui_mem_pool.size/2);
+ pr_debug("%s(%d): allocated at %llx\n", __func__, __LINE__,
+ allocbuffer[0].pa);
+ pr_debug("%s(%d): allocated at %llx\n", __func__, __LINE__,
+ allocbuffer[1].pa);
+ ret = TUI_DCI_OK;
+ } else {
+ /* requested buffer is bigger than the memory pool, return an
+ error */
+ pr_debug("%s(%d): Memory pool too small\n", __func__, __LINE__);
+ ret = TUI_DCI_ERR_INTERNAL_ERROR;
+ }
+
+ return ret;
+#endif
+}
+
+/**
+ * hal_tui_free() - free memory allocated by hal_tui_alloc()
+ *
+ * This function is called at the end of the TUI session, when the TUI module
+ * receives the CMD_TUI_SW_CLOSE_SESSION message. The function should free the
+ * buffers allocated by hal_tui_alloc(...).
+ */
+void hal_tui_free(void)
+{
+ pr_info("[TUI-HAL] hal_tui_free()\n");
+ if (g_tui_secmem_handle) {
+#ifndef CONFIG_CMA
+ secmem_api_unref_pa(g_tui_secmem_handle, __func__, __LINE__);
+#else
+ tui_region_online();
+#endif
+ g_tui_secmem_handle = 0;
+ }
+}
+
+/**
+ * hal_tui_deactivate() - deactivate Normal World display and input
+ *
+ * This function should stop the Normal World display and, if necessary, Normal
+ * World input. It is called when a TUI session is opening, before the Secure
+ * World takes control of display and input.
+ *
+ * Return: must return 0 on success, non-zero otherwise.
+ */
+
+uint32_t hal_tui_deactivate(void)
+{
+ int ret = TUI_DCI_OK, tmp;
+ pr_info("[TUI-HAL] hal_tui_deactivate()\n");
+ /* Set linux TUI flag */
+ trustedui_set_mask(TRUSTEDUI_MODE_TUI_SESSION);
+ pr_info("TDDP/[TUI-HAL] %s()\n", __func__);
+ /*
+ * Stop NWd display here. After this function returns, SWd will take
+ * control of the display and input. Therefore the NWd should no longer
+ * access it
+ * This can be done by calling the fb_blank(FB_BLANK_POWERDOWN) function
+ * on the appropriate framebuffer device
+ */
+
+ tpd_enter_tui();
+#if 0
+ enable_clock(MT_CG_PERI_I2C0, "i2c");
+ enable_clock(MT_CG_PERI_I2C1, "i2c");
+ enable_clock(MT_CG_PERI_I2C2, "i2c");
+ enable_clock(MT_CG_PERI_I2C3, "i2c");
+ enable_clock(MT_CG_PERI_APDMA, "i2c");
+#endif
+ i2c_tui_enable_clock();
+
+ //gt1x_power_reset();
+
+ tmp = display_enter_tui();
+ if(tmp) {
+ pr_debug("TDDP/[TUI-HAL] %s() failed because display\n", __func__);
+ ret = TUI_DCI_ERR_OUT_OF_DISPLAY;
+ }
+
+
+ trustedui_set_mask(TRUSTEDUI_MODE_VIDEO_SECURED|
+ TRUSTEDUI_MODE_INPUT_SECURED);
+
+ pr_info("TDDP/[TUI-HAL] %s()\n", __func__);
+
+ return ret;
+}
+
+/**
+ * hal_tui_activate() - restore Normal World display and input after a TUI
+ * session
+ *
+ * This function should enable Normal World display and, if necessary, Normal
+ * World input. It is called after a TUI session, after the Secure World has
+ * released the display and input.
+ *
+ * Return: must return 0 on success, non-zero otherwise.
+ */
+uint32_t hal_tui_activate(void)
+{
+ pr_info("[TUI-HAL] hal_tui_activate()\n");
+ /* Protect NWd */
+ trustedui_clear_mask(TRUSTEDUI_MODE_VIDEO_SECURED|
+ TRUSTEDUI_MODE_INPUT_SECURED);
+
+ pr_info("TDDP %s()\n", __func__);
+
+ /*
+ * Restart NWd display here. TUI session has ended, and therefore the
+ * SWd will no longer use display and input.
+ * This can be done by calling the fb_blank(FB_BLANK_UNBLANK) function
+ * on the appropriate framebuffer device
+ */
+ /* Clear linux TUI flag */
+
+ tpd_exit_tui();
+#if 0
+ disable_clock(MT_CG_PERI_I2C0, "i2c");
+ disable_clock(MT_CG_PERI_I2C1, "i2c");
+ disable_clock(MT_CG_PERI_I2C2, "i2c");
+ disable_clock(MT_CG_PERI_I2C3, "i2c");
+ disable_clock(MT_CG_PERI_APDMA, "i2c");
+#endif
+ i2c_tui_disable_clock();
+
+ display_exit_tui();
+
+
+ trustedui_set_mode(TRUSTEDUI_MODE_OFF);
+
+ return TUI_DCI_OK;
+}
+
+int __weak tui_region_offline(phys_addr_t *pa, unsigned long *size)
+{
+ return -1;
+}
+
+int __weak tui_region_online(void)
+{
+ return 0;
+}
+
+int __weak tpd_reregister_from_tui(void)
+{
+ return 0;
+}
+
+int __weak tpd_enter_tui(void)
+{
+ return 0;
+}
+
+int __weak tpd_exit_tui(void)
+{
+ return 0;
+}
+
+int __weak display_enter_tui(void)
+{
+ return 0;
+}
+
+int __weak display_exit_tui(void)
+{
+ return 0;
+}
+
+int __weak i2c_tui_enable_clock(void)
+{
+ return 0;
+}
+
+int __weak i2c_tui_disable_clock(void)
+{
+ return 0;
+}
diff --git a/drivers/misc/mediatek/gud/302c/gud/build_tag.h b/drivers/misc/mediatek/gud/302c/gud/build_tag.h
new file mode 100644
index 000000000..9eda58638
--- /dev/null
+++ b/drivers/misc/mediatek/gud/302c/gud/build_tag.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2013-2014 TRUSTONIC LIMITED
+ * All Rights Reserved.
+ *
+ * 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.
+ */
+#define MOBICORE_COMPONENT_BUILD_TAG \
+ "t-base-Mediatek-Armv8-Android-302C-V005-20151127_180020_32"
diff --git a/drivers/misc/mediatek/gud/Kconfig b/drivers/misc/mediatek/gud/Kconfig
new file mode 100644
index 000000000..fabdcd50e
--- /dev/null
+++ b/drivers/misc/mediatek/gud/Kconfig
@@ -0,0 +1,68 @@
+#
+# TRUSTONIC TEE configuration
+#
+config TRUSTONIC_TEE_SUPPORT
+ bool "Enable Trustonic TEE Support"
+ default n
+ ---help---
+ Enable Trustonic TEE Support
+
+config TRUSTONIC_TEE_VERSION
+ string "TRUSTONIC TEE Version"
+ depends on TRUSTONIC_TEE_SUPPORT
+ default "302c" if (ARCH_MT6735 || ARCH_MT6735M || ARCH_MT6753 || ARCH_MT6580)
+
+config MT_TRUSTONIC_TEE_DEBUGFS
+ bool "Enable Trustonic TEE debugfs"
+ default n
+ ---help---
+ Enable Trustonic TEE debugfs
+
+#
+# MobiCore configuration
+#
+config MOBICORE_DRIVER
+ tristate "MobiCore Driver"
+ depends on TRUSTONIC_TEE_SUPPORT
+ default n
+ ---help---
+ Enable Linux Kernel MobiCore Support
+
+config MOBICORE_DEBUG
+ bool "MobiCore Module debug mode"
+ depends on MOBICORE_DRIVER
+ default n
+ ---help---
+ Enable Debug mode in the MobiCore Driver.
+ It enables printing information about mobicore operations
+
+config MOBICORE_VERBOSE
+ bool "MobiCore Module verbose debug mode"
+ depends on MOBICORE_DEBUG
+ default n
+ ---help---
+ Enable Verbose Debug mode in the MobiCore Driver.
+ It enables printing extra information about mobicore operations
+ Beware: this is only useful for debuging deep in the driver because
+ it prints too much logs
+
+config MOBICORE_API
+ tristate "Linux MobiCore API"
+ depends on MOBICORE_DRIVER
+ default n
+ ---help---
+ Enable Linux Kernel MobiCore API
+
+config TRUSTONIC_TRUSTED_UI
+ tristate "<t-base TUI"
+ depends on TRUSTONIC_TEE_SUPPORT
+ default n
+ ---help---
+ Enable <t-base Trusted User Interface
+
+config TRUSTONIC_TRUSTED_UI_FB_BLANK
+ bool "<t-base TUI with fb_blank"
+ depends on TRUSTONIC_TRUSTED_UI
+ default n
+ ---help---
+ Blank the framebuffer before starting a TUI session
diff --git a/drivers/misc/mediatek/gud/Makefile b/drivers/misc/mediatek/gud/Makefile
index 5975a0a82..df7d19c0f 100755..100644
--- a/drivers/misc/mediatek/gud/Makefile
+++ b/drivers/misc/mediatek/gud/Makefile
@@ -1,2 +1,13 @@
-
-obj-$(CONFIG_TRUSTONIC_TEE_SUPPORT) += $(subst ",,$(CONFIG_MTK_PLATFORM))/
+ifeq ($(CONFIG_TRUSTONIC_TEE_SUPPORT),y)
+ ifeq (,$(filter $(CONFIG_MTK_PLATFORM), "mt6582" "mt6592"))
+ # armv8
+ ifeq ($(CONFIG_TRUSTONIC_TEE_VERSION), "")
+ obj-$(CONFIG_TRUSTONIC_TEE_SUPPORT) += 302a/
+ else
+ obj-$(CONFIG_TRUSTONIC_TEE_SUPPORT) += $(subst ",,$(CONFIG_TRUSTONIC_TEE_VERSION))/
+ endif
+ else
+ # armv7
+ obj-$(CONFIG_TRUSTONIC_TEE_SUPPORT) += $(subst ",,$(CONFIG_MTK_PLATFORM))/
+ endif
+endif
diff --git a/drivers/misc/mediatek/gud/Makefile.include b/drivers/misc/mediatek/gud/Makefile.include
new file mode 100644
index 000000000..aa0a4fd1a
--- /dev/null
+++ b/drivers/misc/mediatek/gud/Makefile.include
@@ -0,0 +1,17 @@
+ifeq ($(CONFIG_TRUSTONIC_TEE_SUPPORT),y)
+ ifeq (,$(filter $(CONFIG_MTK_PLATFORM), "mt6582" "mt6592"))
+ # armv8
+ ifeq ($(CONFIG_TRUSTONIC_TEE_VERSION), "")
+ # use default version
+ ccflags-y += -I$(srctree)/drivers/misc/mediatek/gud/302a/gud/MobiCoreKernelApi/include \
+ -I$(srctree)/drivers/misc/mediatek/gud/302a/gud/MobiCoreKernelApi/public
+ else
+ ccflags-y += -I$(srctree)/drivers/misc/mediatek/gud/$(CONFIG_TRUSTONIC_TEE_VERSION)/gud/MobiCoreKernelApi/include \
+ -I$(srctree)/drivers/misc/mediatek/gud/$(CONFIG_TRUSTONIC_TEE_VERSION)/gud/MobiCoreKernelApi/public
+ endif
+ else
+ # armv7
+ ccflags-y += -I$(srctree)/drivers/misc/mediatek/gud/$(MTK_PLATFORM)/gud/MobiCoreKernelApi/include \
+ -I$(srctree)/drivers/misc/mediatek/gud/$(MTK_PLATFORM)/gud/MobiCoreKernelApi/public
+ endif
+endif