diff options
| author | fire855 <thefire855@gmail.com> | 2017-02-24 17:48:24 +0100 |
|---|---|---|
| committer | Mister Oyster <oysterized@gmail.com> | 2017-04-11 10:59:46 +0200 |
| commit | 504261abd2b3a69cb609ef9ccf4e58ae9ccad566 (patch) | |
| tree | eb5caf24cfcb12a7dbb2dbe38eaa1e68d4c26a3c /drivers/misc/mediatek/gud | |
| parent | d547e0f39015f8e8ec1dba8bd9d66c1beb24eb41 (diff) | |
Update m4u, smi and gud drivers
Backported from 3.18 MM kernel
Diffstat (limited to 'drivers/misc/mediatek/gud')
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, ¶m); + /* + * 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(®, uarg, sizeof(reg))) + return -EFAULT; + + ret = mc_register_wsm_mmu(instance, + (void *)(uintptr_t)reg.buffer, + reg.len, ®.handle, &phys); + reg.table_phys = phys; + + if (!ret) { + if (copy_to_user(uarg, ®, 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(®, uarg, sizeof(reg))) + return -EFAULT; + + ret = mc_register_wsm_mmu(instance, + (void *)(uintptr_t)reg.buffer, + reg.len, ®.handle, &phys); + reg.table_phys = phys; + + if (!ret) { + if (copy_to_user(uarg, ®, 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(¬ification, 0, sizeof(notification)); + ssize_t num_read = + connection_read_data(nqconnection, + ¬ification, + 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 |
