Merge "mobicore: initial import of kernel driver" into msm-3.0
diff --git a/drivers/gud/mobicore_driver/build_tag.h b/drivers/gud/mobicore_driver/build_tag.h
new file mode 100644
index 0000000..43541bb
--- /dev/null
+++ b/drivers/gud/mobicore_driver/build_tag.h
@@ -0,0 +1,29 @@
+/**
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2012-2012 -->
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#define MOBICORE_COMPONENT_BUILD_TAG "*** GC_MSM8960_Release_V010 ###"
diff --git a/drivers/gud/mobicore_driver/logging.c b/drivers/gud/mobicore_driver/logging.c
new file mode 100644
index 0000000..eb44c8a
--- /dev/null
+++ b/drivers/gud/mobicore_driver/logging.c
@@ -0,0 +1,336 @@
+/** MobiCore driver module.(interface to the secure world SWD)
+ * @addtogroup MCD_MCDIMPL_KMOD_LOGGING MobiCore Driver Logging Subsystem.
+ * @ingroup MCD_MCDIMPL_KMOD
+ * @{
+ * @file
+ * MobiCore Driver Logging Subsystem.
+ * The logging subsytem provides the interface between the Mobicore trace
+ * buffer and the Linux log
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009-2012 -->
+ *
+ * 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.
+ */
+#include "mc_drv_module.h"
+#include "mc_drv_module_linux_api.h"
+#include "mc_drv_module_fastcalls.h"
+
+/* Default len 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 "
+ "(or 256KB default).");
+
+/*----------------------------------------------------------------------------*/
+/* 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 {
+ /* Type and format of data */
+ uint16_t ctrl;
+ /* Unique value for each event source */
+ uint16_t source;
+ /* Value, if any */
+ uint32_t log_data;
+};
+
+/** MobiCore log previous position */
+static uint32_t log_pos;
+/** MobiCore log buffer structure */
+static struct mc_trace_buf *log_buf;
+/** Log Thread task structure */
+struct task_struct *log_thread;
+/** Log Line buffer */
+static char *log_line;
+
+static void log_msg(struct logmsg_struct *msg);
+
+/*----------------------------------------------------------------------------*/
+static void log_eol(void)
+{
+ if (!strnlen(log_line, LOG_LINE_SIZE))
+ return;
+ printk(KERN_INFO "%s\n", log_line);
+ log_line[0] = 0;
+}
+/*----------------------------------------------------------------------------*/
+/**
+ * Put a char to the log line if there is enough space if not then also
+ * output the line. Assume nobody else is updating the line! */
+static void log_char(char ch)
+{
+ uint32_t len;
+ if (ch == '\n' || ch == '\r') {
+ log_eol();
+ return;
+ }
+
+ if (strnlen(log_line, LOG_LINE_SIZE) >= LOG_LINE_SIZE - 1) {
+ printk(KERN_INFO "%s\n", log_line);
+ log_line[0] = 0;
+ }
+
+ len = strnlen(log_line, LOG_LINE_SIZE);
+ log_line[len] = ch;
+ log_line[len + 1] = 0;
+}
+
+/*----------------------------------------------------------------------------*/
+/**
+ * Put a string to the log line if there is enough space if not then also
+ * output the line. Assume nobody else is updating the line! */
+static void log_str(const char *s)
+{
+ int i;
+ for (i = 0; i < strnlen(s, LOG_LINE_SIZE); i++)
+ log_char(s[i]);
+}
+
+/*----------------------------------------------------------------------------*/
+static uint32_t process_v1log(void)
+{
+ char *last_char = log_buf->buff + log_buf->write_pos;
+ char *buff = log_buf->buff + log_pos;
+ while (buff != last_char) {
+ log_char(*(buff++));
+ /* Wrap around */
+ if (buff - (char *)log_buf >= log_size)
+ buff = log_buf->buff;
+ }
+ return buff - log_buf->buff;
+}
+
+/*----------------------------------------------------------------------------*/
+static uint32_t process_v2log(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 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)
+{
+ int digits = 1;
+ uint32_t base = (format & LOG_INTEGER_DECIMAL) ? 10 : 16;
+ int width = (format & LOG_LENGTH_MASK) >> LOG_LENGTH_SHIFT;
+ int negative = FALSE;
+ uint32_t digit_base = 1;
+
+ if ((format & LOG_INTEGER_SIGNED) != 0 && ((signed int)value) < 0) {
+ negative = TRUE;
+ 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);
+ width--;
+ }
+ }
+
+ if (negative)
+ log_char('-');
+
+ while (digits-- > 0) {
+ uint32_t d = value / digit_base;
+ log_char(HEX2ASCII[d]);
+ value = value - d * digit_base;
+ digit_base /= base;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+static void log_msg(struct logmsg_struct *msg)
+{
+ unsigned char msgtxt[5];
+ int mpos = 0;
+ switch (msg->ctrl & LOG_TYPE_MASK) {
+ case LOG_TYPE_CHAR: {
+ uint32_t ch;
+ ch = msg->log_data;
+ while (ch != 0) {
+ msgtxt[mpos++] = ch&0xFF;
+ ch >>= 8;
+ }
+ msgtxt[mpos] = 0;
+ log_str(msgtxt);
+ break;
+ }
+ case LOG_TYPE_INTEGER: {
+ dbg_raw_nro(msg->ctrl, msg->log_data);
+ break;
+ }
+ default:
+ break;
+ }
+ if (msg->ctrl & LOG_EOL)
+ log_eol();
+}
+
+/*----------------------------------------------------------------------------*/
+static int log_worker(void *p)
+{
+ if (log_buf == NULL)
+ return -EFAULT;
+
+ /* The thread should have never started */
+ if (log_buf == NULL)
+ return -EFAULT;
+
+ while (!kthread_should_stop()) {
+ if (log_buf->write_pos == log_pos)
+ schedule_timeout_interruptible(MAX_SCHEDULE_TIMEOUT);
+
+ switch (log_buf->version) {
+ case 1:
+ log_pos = process_v1log();
+ break;
+ case 2:
+ log_pos = process_v2log();
+ break;
+ default:
+ MCDRV_DBG_ERROR("Unknown Mobicore log data "
+ "version %d logging disabled.",
+ log_buf->version);
+ log_pos = log_buf->write_pos;
+ /* Stop the thread as we have no idea what
+ * happens next */
+ return -EFAULT;
+ }
+ }
+ MCDRV_DBG("Logging thread stopped!");
+ return 0;
+}
+
+
+/*----------------------------------------------------------------------------*/
+/**
+ * Wakeup 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;
+
+ 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 *data)
+{
+ unsigned long phys_log_buf;
+ union fc_generic fc_log;
+
+ log_pos = 0;
+ log_buf = NULL;
+ log_thread = NULL;
+ log_line = NULL;
+
+ /* Sanity check for the log size */
+ if (log_size < PAGE_SIZE)
+ return -EFAULT;
+ else
+ log_size =
+ get_nr_of_pages_for_buffer(NULL, log_size) * PAGE_SIZE;
+
+ log_line = kzalloc(LOG_LINE_SIZE, GFP_KERNEL);
+ if (IS_ERR(log_line)) {
+ MCDRV_DBG_ERROR("failed to allocate log line!");
+ return -ENOMEM;
+ }
+
+ log_thread = kthread_create(log_worker, NULL, "mobicore_log");
+ if (IS_ERR(log_thread)) {
+ MCDRV_DBG_ERROR("mobicore log thread creation failed!");
+ return -EFAULT;
+ }
+
+ log_pos = 0;
+ log_buf = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
+ size_to_order(log_size));
+ if (!log_buf) {
+ MCDRV_DBG_ERROR("Failed to get page for logger!");
+ return -ENOMEM;
+ }
+ 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] = phys_log_buf;
+ fc_log.as_in.param[1] = log_size;
+
+ MCDRV_DBG("fc_log virt=%p phys=%p ", log_buf, (void *)phys_log_buf);
+ mc_fastcall(&fc_log);
+ MCDRV_DBG("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("MobiCore shared traces setup failed!");
+ kthread_stop(log_thread);
+ free_pages((unsigned long)log_buf, size_to_order(log_size));
+
+ log_buf = NULL;
+ log_thread = NULL;
+ return -EIO;
+ }
+
+ MCDRV_DBG("fc_log Logger version %u\n", log_buf->version);
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+/**
+ * Free kernel log componenets.
+ * 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/gud/mobicore_driver/main.c b/drivers/gud/mobicore_driver/main.c
new file mode 100644
index 0000000..d1c67f6
--- /dev/null
+++ b/drivers/gud/mobicore_driver/main.c
@@ -0,0 +1,2868 @@
+/** MobiCore driver module.(interface to the secure world SWD)
+ * @addtogroup MCD_MCDIMPL_KMOD_IMPL
+ * @{
+ * @file
+ * 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.
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009-2012 -->
+ *
+ * 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.
+ */
+
+#include "mc_drv_module.h"
+#include "mc_drv_module_linux_api.h"
+#include "mc_drv_module_android.h"
+#include "mc_drv_module_fastcalls.h"
+#include "public/mc_kernel_api.h"
+
+/* Initial value for the daemon sempahore signaling */
+#define DAEMON_SEM_VAL 0
+
+/** MobiCore interrupt context data */
+static struct mc_drv_kmod_ctx mc_drv_kmod_ctx;
+
+/** MobiCore MCI information */
+static uint32_t mci_base;
+/*
+#############################################################################
+##
+## Convenience functions for Linux API functions
+##
+#############################################################################*/
+static int goto_cpu0(void);
+static int goto_all_cpu(void) __attribute__ ((unused));
+
+
+/*----------------------------------------------------------------------------*/
+static void init_and_add_to_list(
+ struct list_head *item,
+ struct list_head *list_head
+)
+{
+ INIT_LIST_HEAD(item);
+
+ list_add(item, list_head);
+}
+
+/*----------------------------------------------------------------------------*/
+/** check if CPU supports the ARM TrustZone Security Extensions
+ * @return int TRUE or FALSE */
+static int has_security_extensions(
+ void
+)
+{
+ u32 fea = 0;
+ asm volatile(
+ "mrc p15, 0, %[fea], cr0, cr1, 0" :
+ [fea]"=r" (fea));
+
+ MCDRV_DBG_VERBOSE("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 0;
+
+ return 1;
+}
+
+/*----------------------------------------------------------------------------*/
+/** check if running in secure mode
+ * @return int TRUE or FALSE */
+static int is_secure_mode(
+ void
+)
+{
+ u32 cpsr = 0, 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("CPRS.M = set to 0x%X\n", cpsr & ARM_CPSR_MASK);
+ MCDRV_DBG_VERBOSE("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 & ARM_CPSR_MASK) == ARM_MONITOR_MODE))
+ return 1;
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+/** check if userland caller is privileged (aka has "root" access rights).
+ @return int TRUE or FALSE */
+static int is_userland_caller_privileged(
+ void
+) {
+ /* For some platforms we cannot run the Daemon as root - for Android
+ * compliance tests it is not allowed, thus we assume the daemon is ran
+ * as the system user.
+ * In Android the system user for daemons has no particular capabilities
+ * other than a fixed UID: AID_SYSTEM 1000
+ * The actual number is guaranteed to be the same in all Android systems
+ * so we will take it for granted: see android_filesystem_config.h in
+ * the Android source tree for all UIDs and their meaning:
+ * http://android-dls.com/wiki/index.php?title=Android_UIDs_and_GIDs
+ */
+#ifdef MC_ANDROID_UID_CHECK
+ return current_euid() <= AID_SYSTEM;
+#else
+ /* capable should cover all possibilities, root or sudo, uid checking
+ * was not very reliable */
+ return capable(CAP_SYS_ADMIN);
+#endif
+}
+
+
+
+/*----------------------------------------------------------------------------*/
+static void unlock_page_from_used_l2_table(
+ struct page *page
+){
+ /* REV axh: check if we should do this. */
+ SetPageDirty(page);
+
+ /* release page, old api was page_cache_release() */
+ ClearPageReserved(page);
+ put_page(page);
+}
+
+/*----------------------------------------------------------------------------*/
+/* convert L2 PTE to page pointer */
+static struct page *l2_pte_to_page(
+ pte_t pte
+) {
+ void *phys_page_addr = (void *)((unsigned int)pte & PAGE_MASK);
+ unsigned int pfn = addr_to_pfn(phys_page_addr);
+ struct page *page = pfn_to_page(pfn);
+ return page;
+}
+
+/*----------------------------------------------------------------------------*/
+/* convert page pointer to L2 PTE */
+static pte_t page_to_l2_pte(
+ struct page *page
+)
+{
+ unsigned int pfn = page_to_pfn(page);
+ void *phys_addr = pfn_to_addr(pfn);
+ pte_t pte = (pte_t)((unsigned int)phys_addr & PAGE_MASK);
+ return pte;
+}
+
+
+/*----------------------------------------------------------------------------*/
+static inline int lock_user_pages(
+ struct task_struct *task,
+ void *virt_start_page_addr,
+ int nr_of_pages,
+ struct page **pages
+)
+{
+ int ret = 0;
+ int locked_pages = 0;
+ unsigned int i;
+
+ do {
+
+ /* 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,
+ nr_of_pages,
+ 1, /* write access */
+ 0, /* they say drivers should always
+ pass 0 here..... */
+ pages,
+ NULL); /* we don't need the VMAs */
+ up_read(&(task->mm->mmap_sem));
+
+ /* could as lock all pages? */
+ if (locked_pages != nr_of_pages) {
+ MCDRV_DBG_ERROR(
+ "get_user_pages() failed, "
+ "locked_pages=%d\n",
+ locked_pages);
+ ret = -ENOMEM;
+ /* check if an error has been returned. */
+ if (locked_pages < 0) {
+ ret = locked_pages;
+ locked_pages = 0;
+ }
+ break;
+ }
+
+ /* do cache maintenance on locked pages. */
+ for (i = 0; i < nr_of_pages; i++)
+ flush_dcache_page(pages[i]);
+
+ } while (FALSE);
+
+
+ if (ret != 0) {
+ /* release all locked pages. */
+ MCDRV_ASSERT(locked_pages >= 0);
+ for (i = 0; i < locked_pages; i++)
+ put_page(pages[i]);
+ }
+
+ return ret;
+
+}
+
+/*
+#############################################################################
+##
+## Driver implementation functions
+##
+#############################################################################*/
+/*----------------------------------------------------------------------------*/
+/* check if caller is MobiCore Daemon */
+static unsigned int is_caller_mc_daemon(
+ struct mc_instance *instance
+)
+{
+ return ((instance != NULL)
+ && (mc_drv_kmod_ctx.daemon_inst == instance));
+}
+
+
+/*----------------------------------------------------------------------------*/
+/* Get process context from file pointer */
+static struct mc_instance *get_instance(
+ struct file *file
+) {
+ MCDRV_ASSERT(file != NULL);
+
+ return (struct mc_instance *)(file->private_data);
+}
+
+
+/*----------------------------------------------------------------------------*/
+/* Get a unique ID */
+static unsigned int get_mc_kmod_unique_id(
+ void
+)
+{
+ return (unsigned int)atomic_inc_return(
+ &(mc_drv_kmod_ctx.unique_counter));
+}
+
+
+/*----------------------------------------------------------------------------*/
+/* Get kernel pointer to shared L2 table given a per-process reference */
+static struct l2table *get_l2_table_kernel_virt(
+ struct mc_used_l2_table *used_l2table
+)
+{
+ MCDRV_ASSERT(used_l2table != NULL);
+ MCDRV_ASSERT(used_l2table->set != NULL);
+ MCDRV_ASSERT(used_l2table->set->kernel_virt != NULL);
+ return &(used_l2table->set->kernel_virt->table[used_l2table->idx]);
+}
+
+/*----------------------------------------------------------------------------*/
+/* Get physical address of a shared L2 table given a per-process reference */
+static struct l2table *get_l2_table_phys(
+ struct mc_used_l2_table *used_l2table
+)
+{
+ MCDRV_ASSERT(used_l2table != NULL);
+ MCDRV_ASSERT(used_l2table->set != NULL);
+ MCDRV_ASSERT(used_l2table->set->phys != NULL);
+ return &(used_l2table->set->phys->table[used_l2table->idx]);
+}
+
+/*----------------------------------------------------------------------------*/
+static unsigned int is_in_use_used_l2_table(
+ struct mc_used_l2_table *used_l2table
+)
+{
+ return ((used_l2table->flags &
+ (MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_APP
+ | MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_MC)) != 0);
+}
+
+
+
+/*----------------------------------------------------------------------------*/
+static struct mc_used_l2_table *find_used_l2_table_by_handle(
+ unsigned int handle
+) {
+ struct mc_used_l2_table *used_l2table;
+ struct mc_used_l2_table *used_l2table_with_handle = NULL;
+
+ list_for_each_entry(
+ used_l2table,
+ &(mc_drv_kmod_ctx.mc_used_l2_tables),
+ list
+ ) {
+ if (handle == used_l2table->handle) {
+ used_l2table_with_handle = used_l2table;
+ break;
+ }
+ }
+
+ return used_l2table_with_handle;
+}
+
+/*
+#############################################################################
+##
+## L2 Table Pool
+##
+#############################################################################*/
+
+/*----------------------------------------------------------------------------*/
+static struct mc_used_l2_table *allocate_used_l2_table(
+ struct mc_instance *instance
+) {
+ int ret = 0;
+ struct mc_l2_table_store *l2table_store = NULL;
+ struct mc_l2_tables_set *l2table_set = NULL;
+ struct mc_used_l2_table *used_l2table = NULL;
+ struct page *page;
+ unsigned int i = 0;
+
+ do {
+ /* allocate a WSM L2 descriptor */
+ used_l2table = kmalloc(sizeof(*used_l2table), GFP_KERNEL);
+ if (used_l2table == NULL) {
+ ret = -ENOMEM;
+ MCDRV_DBG_ERROR("out of memory\n");
+ break;
+ }
+ /* clean */
+ memset(used_l2table, 0, sizeof(*used_l2table));
+ used_l2table->handle = get_mc_kmod_unique_id();
+ used_l2table->owner = instance;
+
+ /* add to global list. */
+ init_and_add_to_list(
+ &(used_l2table->list),
+ &(mc_drv_kmod_ctx.mc_used_l2_tables));
+
+ /* walk though list to find free set. */
+ list_for_each_entry(
+ l2table_set,
+ &(mc_drv_kmod_ctx.mc_l2_tables_sets),
+ list
+ ) {
+ for (i = 0; i < MC_DRV_KMOD_L2_TABLE_PER_PAGES; i++) {
+ if ((l2table_set->usage_bitmap & (1U << i))
+ == 0) {
+ /* found a set,
+ l2table_set and i are set. */
+ l2table_store =
+ l2table_set->kernel_virt;
+ break;
+ }
+ }
+ if (l2table_store != NULL)
+ break;
+ } /* end while */
+
+ if (l2table_store == NULL) {
+ l2table_store = (struct mc_l2_table_store *)
+ get_zeroed_page(GFP_KERNEL);
+ if (l2table_store == NULL) {
+ ret = -ENOMEM;
+ break;
+ }
+
+ /* Actually, locking is not necessary, because kernel
+ memory is not supposed to get swapped out. But
+ we play safe.... */
+ page = virt_to_page(l2table_store);
+ SetPageReserved(page);
+
+ /* allocate a descriptor */
+ l2table_set = kmalloc(sizeof(*l2table_set), GFP_KERNEL);
+ if (l2table_set == NULL) {
+ kfree(l2table_store);
+ ret = -ENOMEM;
+ break;
+ }
+ /* initialize */
+ memset(l2table_set, 0, sizeof(*l2table_set));
+
+ l2table_set->kernel_virt = l2table_store;
+ l2table_set->page = page;
+ l2table_set->phys = (void *)virt_to_phys(l2table_store);
+
+ /* init add to list. */
+ init_and_add_to_list(
+ &(l2table_set->list),
+ &(mc_drv_kmod_ctx.mc_l2_tables_sets));
+
+ /* use first table */
+ i = 0;
+ }
+
+ /* set set usage */
+ l2table_set->usage_bitmap |= (1U << i);
+
+ /* set set reference */
+ used_l2table->set = l2table_set;
+ used_l2table->idx = i;
+
+ MCDRV_DBG_VERBOSE(
+ "chunkPhys=%p,idx=%d\n",
+ l2table_set->phys, i);
+
+ } while (FALSE);
+
+ if (ret != 0) {
+ if (used_l2table != NULL) {
+ /* remove from list */
+ list_del(&(l2table_set->list));
+ /* free memory */
+ kfree(used_l2table);
+ used_l2table = NULL;
+ }
+ }
+
+ return used_l2table;
+}
+
+/*----------------------------------------------------------------------------*/
+static void free_used_l2_table(
+ struct mc_used_l2_table *used_l2table
+)
+{
+ struct mc_l2_tables_set *l2table_set;
+ unsigned int idx;
+
+ MCDRV_ASSERT(used_l2table != NULL);
+
+ l2table_set = used_l2table->set;
+ MCDRV_ASSERT(l2table_set != NULL);
+
+ /* clean usage flag */
+ idx = used_l2table->idx;
+ MCDRV_ASSERT(idx < MC_DRV_KMOD_L2_TABLE_PER_PAGES);
+ l2table_set->usage_bitmap &= ~(1U << idx);
+
+ /* if nobody uses this set, we can release it. */
+ if (l2table_set->usage_bitmap == 0) {
+ MCDRV_ASSERT(l2table_set->page != NULL);
+ ClearPageReserved(l2table_set->page);
+
+ MCDRV_ASSERT(l2table_set->kernel_virt != NULL);
+ free_page((unsigned long)l2table_set->kernel_virt);
+
+ /* remove from list */
+ list_del(&(l2table_set->list));
+
+ /* free memory */
+ kfree(l2table_set);
+ }
+
+ return;
+}
+
+
+
+/*----------------------------------------------------------------------------*/
+/**
+ * Create a L2 table in a WSM container that has been allocates previously.
+ *
+ * @param task pointer to task owning WSM
+ * @param wsm_buffer user space WSM start
+ * @param wsm_len WSM length
+ * @param used_l2table Pointer to L2 table details
+ */
+static int map_buffer_into_used_l2_table(
+ struct task_struct *task,
+ void *wsm_buffer,
+ unsigned int wsm_len,
+ struct mc_used_l2_table *used_l2table
+)
+{
+ int ret = 0;
+ unsigned int i, nr_of_pages;
+ void *virt_addr_page;
+ struct page *page;
+ struct l2table *l2table;
+ struct page **l2table_as_array_of_pointers_to_page;
+
+ /* task can be null when called from kernel space */
+ MCDRV_ASSERT(wsm_buffer != NULL);
+ MCDRV_ASSERT(wsm_len != 0);
+ MCDRV_ASSERT(used_l2table != NULL);
+
+ MCDRV_DBG_VERBOSE("WSM addr=0x%p, len=0x%08x\n", wsm_buffer, wsm_len);
+
+ /* Check if called from kernel space wsm_buffer is actually
+ * vmalloced or not */
+ if (task == NULL && !is_vmalloc_addr(wsm_buffer)) {
+ MCDRV_DBG_ERROR("WSM addr is not a vmalloc address");
+ return -EINVAL;
+ }
+
+ l2table = get_l2_table_kernel_virt(used_l2table);
+ /* We use the memory for the L2 table to hold the pointer
+ and convert them later. This works, as everything comes
+ down to a 32 bit value. */
+ l2table_as_array_of_pointers_to_page = (struct page **)l2table;
+
+ do {
+
+ /* no size > 1Mib supported */
+ if (wsm_len > SZ_1M) {
+ MCDRV_DBG_ERROR("size > 1 MiB\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ /* calculate page usage */
+ virt_addr_page = get_page_start(wsm_buffer);
+ nr_of_pages = get_nr_of_pages_for_buffer(wsm_buffer, wsm_len);
+
+
+ MCDRV_DBG_VERBOSE("virt addr pageStart=0x%p,pages=%d\n",
+ virt_addr_page,
+ nr_of_pages);
+
+ /* L2 table can hold max 1MiB in 256 pages. */
+ if ((nr_of_pages*PAGE_SIZE) > SZ_1M) {
+ MCDRV_DBG_ERROR("WSM paged exceed 1 MiB\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ /* Request comes from user space */
+ if (task != NULL) {
+ /* 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_user_pages(
+ task,
+ virt_addr_page,
+ nr_of_pages,
+ l2table_as_array_of_pointers_to_page);
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("lock_user_pages() failed\n");
+ break;
+ }
+ }
+ /* 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(
+ "vmalloc_to_Page()"
+ " failed to map address\n");
+ ret = -EINVAL;
+ break;
+ }
+ get_page(page);
+ /* Lock the page in memory, it can't be swapped
+ * out */
+ SetPageReserved(page);
+ l2table_as_array_of_pointers_to_page[i] = page;
+ uaddr += PAGE_SIZE;
+ }
+ }
+
+ used_l2table->nr_of_pages = nr_of_pages;
+ used_l2table->flags |= MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_APP;
+
+ /* create L2 Table entries. used_l2table->table contains a list
+ of page pointers here. For a proper cleanup we have to ensure
+ that the following code either works and used_l2table contains
+ a valid L2 table - or fails and used_l2table->table contains the
+ list of page pointers. Any mixed contents will make cleanup
+ difficult.*/
+
+ for (i = 0; i < nr_of_pages; i++) {
+ pte_t pte;
+ page = l2table_as_array_of_pointers_to_page[i];
+
+ /* create L2 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 at
+ http://lwn.net/Articles/409032/ is also worth reading.*/
+ pte = page_to_l2_pte(page)
+ | L2_FLAG_AP1 | L2_FLAG_AP0
+ | L2_FLAG_C | L2_FLAG_B
+ | L2_FLAG_SMALL | L2_FLAG_SMALL_XN
+ /* 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
+ | L2_FLAG_S | L2_FLAG_SMALL_TEX0
+#endif
+ ;
+
+ l2table->table_entries[i] = pte;
+ MCDRV_DBG_VERBOSE("L2 entry %d: 0x%08x\n", i,
+ (unsigned int)(pte));
+ }
+
+ /* ensure rest of table is empty */
+ while (i < 255)
+ l2table->table_entries[i++] = (pte_t)0;
+
+ } while (FALSE);
+
+ return ret;
+}
+
+
+/*----------------------------------------------------------------------------*/
+/**
+ * Remove a L2 table in a WSM container. Afterwards the container may be
+ * released.
+ *
+ * @param used_l2table Pointer to L2 table details
+ */
+
+static void unmap_buffers_from_used_l2_table(
+ struct mc_used_l2_table *used_l2table
+)
+{
+ unsigned int i;
+ struct l2table *l2table;
+
+ MCDRV_ASSERT(used_l2table != NULL);
+ /* this should not happen, as we have no empty tables. */
+ MCDRV_ASSERT(!is_in_use_used_l2_table(used_l2table));
+
+ /* found the table, now release the resources. */
+ MCDRV_DBG_VERBOSE("clear L2 table, phys_base=%p, nr_of_pages=%d\n",
+ get_l2_table_phys(used_l2table),
+ used_l2table->nr_of_pages);
+
+ l2table = get_l2_table_kernel_virt(used_l2table);
+
+ /* release all locked user space pages */
+ for (i = 0; i < used_l2table->nr_of_pages; i++) {
+ /* convert physical entries from L2 table to page pointers */
+ pte_t pte = get_l2_table_kernel_virt(used_l2table)->
+ table_entries[i];
+ struct page *page = l2_pte_to_page(pte);
+ unlock_page_from_used_l2_table(page);
+ }
+
+ /* remember that all pages have been freed */
+ used_l2table->nr_of_pages = 0;
+
+ return;
+}
+
+
+/*
+#############################################################################
+##
+## Helper functions
+##
+#############################################################################*/
+/*----------------------------------------------------------------------------*/
+#define FREE_FROM_SWD TRUE
+#define FREE_FROM_NWD FALSE
+/** Delete a used l2 table. */
+static void delete_used_l2_table(
+ struct mc_used_l2_table *used_l2table,
+ unsigned int is_swd
+)
+{
+ if (is_swd) {
+ used_l2table->flags &=
+ ~MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_MC;
+ } else {
+ used_l2table->flags &=
+ ~MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_APP;
+ used_l2table->owner = NULL;
+ }
+
+ /* release if Nwd and Swd/MC do no longer use it. */
+ if (is_in_use_used_l2_table(used_l2table)) {
+ MCDRV_DBG_WARN(
+ "WSM L2 table still in use: physBase=%p, "
+ "nr_of_pages=%d\n",
+ get_l2_table_phys(used_l2table),
+ used_l2table->nr_of_pages);
+ } else {
+ unmap_buffers_from_used_l2_table(used_l2table);
+ free_used_l2_table(used_l2table);
+
+ list_del(&(used_l2table->list));
+
+ kfree(used_l2table);
+ }
+ return;
+}
+
+/*----------------------------------------------------------------------------*/
+/** Allocate L2 table and map buffer into it. That is, create respective table
+ entries. Must hold Semaphore mc_drv_kmod_ctx.wsm_l2_sem */
+static struct mc_used_l2_table *new_used_l2_table(
+ struct mc_instance *instance,
+ struct task_struct *task,
+ void *wsm_buffer,
+ unsigned int wsm_len
+) {
+ int ret = 0;
+ struct mc_used_l2_table *used_l2table;
+
+ do {
+ used_l2table = allocate_used_l2_table(instance);
+ if (used_l2table == NULL) {
+ MCDRV_DBG_ERROR(
+ "allocate_used_l2_table() failed\n");
+ break;
+ }
+
+ /* create the L2 page for the WSM */
+ ret = map_buffer_into_used_l2_table(
+ task,
+ wsm_buffer,
+ wsm_len,
+ used_l2table);
+ if (ret != 0) {
+ MCDRV_DBG_ERROR(
+ "map_buffer_into_used_l2_table() failed\n");
+ delete_used_l2_table(used_l2table, FREE_FROM_NWD);
+ used_l2table = NULL;
+ break;
+ }
+
+ } while (FALSE);
+
+
+ return used_l2table;
+}
+
+/*
+#############################################################################
+##
+## IoCtl handler
+##
+#############################################################################*/
+
+/**
+ * Map a virtual memory buffer structure to Mobicore
+ * @param instance
+ * @param addr address of the buffer(NB it must be kernel virtual!)
+ * @param len buffer length
+ * @param handle pointer to handle
+ * @param phys_wsm_l2_table pointer to physical L2 table(?)
+ *
+ * @return 0 if no error
+ *
+ */
+/*----------------------------------------------------------------------------*/
+int mobicore_map_vmem(
+ struct mc_instance *instance,
+ void *addr,
+ uint32_t len,
+ uint32_t *handle,
+ void **phys_wsm_l2_table
+)
+{
+ int ret = 0;
+ struct mc_used_l2_table *used_l2table = NULL;
+ MCDRV_ASSERT(instance != NULL);
+
+ MCDRV_DBG_VERBOSE("enter\n");
+
+ do {
+ if (len == 0) {
+ MCDRV_DBG_ERROR("len=0 is not supported!\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ /* try to get the semaphore */
+ ret = down_interruptible(&(mc_drv_kmod_ctx.wsm_l2_sem));
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("down_interruptible() failed with %d\n",
+ ret);
+ ret = -ERESTARTSYS;
+ break;
+ }
+
+ do {
+ used_l2table = new_used_l2_table(
+ instance,
+ NULL,
+ addr,
+ len);
+
+ if (used_l2table == NULL) {
+ MCDRV_DBG_ERROR("new_used_l2_table() failed\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ /* set response */
+ *handle = used_l2table->handle;
+ *phys_wsm_l2_table =
+ (void *)get_l2_table_phys(used_l2table);
+ MCDRV_DBG_VERBOSE("handle: %d, phys=%p\n",
+ *handle,
+ (void *)(*phys_wsm_l2_table));
+
+ } while (FALSE);
+
+ /* release semaphore */
+ up(&(mc_drv_kmod_ctx.wsm_l2_sem));
+
+ } while (FALSE);
+
+ MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL(mobicore_map_vmem);
+/*----------------------------------------------------------------------------*/
+/**
+ *
+ * @param instance
+ * @param arg
+ *
+ * @return 0 if no error
+ *
+ */
+static int handle_ioctl_app_register_wsm_l2(
+ struct mc_instance *instance,
+ union mc_ioctl_app_reg_wsm_l2_params *user_params
+)
+{
+ int ret = 0;
+ union mc_ioctl_app_reg_wsm_l2_params params;
+ struct mc_used_l2_table *used_l2table = NULL;
+ struct pid *pid_struct = NULL;
+ struct task_struct *task = current;
+
+ MCDRV_ASSERT(instance != NULL);
+
+ MCDRV_DBG_VERBOSE("enter\n");
+
+ do {
+ /* get use parameters */
+ ret = copy_from_user(
+ &(params.in),
+ &(user_params->in),
+ sizeof(params.in));
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("copy_from_user() failed\n");
+ break;
+ }
+
+ /* daemon can do this for another task. */
+ if (params.in.pid != 0) {
+ MCDRV_DBG_ERROR("pid != 0 unsupported\n");
+ ret = -EINVAL;
+ break;
+ }
+ if (params.in.len == 0) {
+ MCDRV_DBG_ERROR("len=0 is not supported!\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ /* try to get the semaphore */
+ ret = down_interruptible(&(mc_drv_kmod_ctx.wsm_l2_sem));
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("down_interruptible() failed with %d\n",
+ ret);
+ ret = -ERESTARTSYS;
+ break;
+ }
+
+ do {
+ used_l2table = new_used_l2_table(
+ instance,
+ task,
+ (void *)(params.in.buffer),
+ params.in.len);
+
+ if (used_l2table == NULL) {
+ MCDRV_DBG_ERROR("new_used_l2_table() failed\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ /* if the daemon does this, we set the MC lock */
+ if (is_caller_mc_daemon(instance))
+ used_l2table->flags |=
+ MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_MC;
+
+ /* set response */
+ memset(¶ms.out, 0, sizeof(params.out));
+ params.out.handle = used_l2table->handle;
+ /* TODO: return the physical address for daemon only,
+ otherwise set NULL */
+ params.out.phys_wsm_l2_table =
+ (uint32_t)get_l2_table_phys(used_l2table);
+
+ MCDRV_DBG_VERBOSE("handle: %d, phys=%p\n",
+ params.out.handle,
+ (void *)(params.out.phys_wsm_l2_table));
+
+
+ /* copy L2Table to user space */
+ ret = copy_to_user(
+ &(user_params->out),
+ &(params.out),
+ sizeof(params.out));
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("copy_to_user() failed\n");
+
+ /* free the table again, as app does not know
+ about anything. */
+ if (is_caller_mc_daemon(instance)) {
+ used_l2table->flags &=
+ ~MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_MC;
+ }
+ delete_used_l2_table(used_l2table,
+ FREE_FROM_NWD);
+ used_l2table = NULL;
+ break;
+ }
+
+ } while (FALSE);
+
+ /* release semaphore */
+ up(&(mc_drv_kmod_ctx.wsm_l2_sem));
+
+ } while (FALSE);
+
+
+
+ /* release PID struct reference */
+ if (pid_struct != NULL)
+ put_pid(pid_struct);
+
+
+ MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+ return ret;
+}
+
+
+/*----------------------------------------------------------------------------*/
+/**
+ * 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
+)
+{
+ int ret = 0;
+ struct mc_used_l2_table *used_l2table = NULL;
+
+ MCDRV_ASSERT(instance != NULL);
+ MCDRV_DBG_VERBOSE("enter\n");
+
+ do {
+ /* try to get the semaphore */
+ ret = down_interruptible(&(mc_drv_kmod_ctx.wsm_l2_sem));
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("processOpenSession() failed with %d\n",
+ ret);
+ ret = -ERESTARTSYS;
+ break;
+ }
+
+ do {
+ used_l2table = find_used_l2_table_by_handle(handle);
+ if (used_l2table == NULL) {
+ ret = -EINVAL;
+ MCDRV_DBG_ERROR("entry not found\n");
+ break;
+ }
+
+ if (instance != used_l2table->owner) {
+ ret = -EINVAL;
+ MCDRV_DBG_ERROR("instance does no own it\n");
+ break;
+ }
+
+ /* free table (if no further locks exist) */
+ delete_used_l2_table(used_l2table, FREE_FROM_NWD);
+ used_l2table = NULL;
+ /* there are no out parameters */
+ } while (FALSE);
+ /* release semaphore */
+ up(&(mc_drv_kmod_ctx.wsm_l2_sem));
+
+ } while (FALSE);
+
+ MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL(mobicore_unmap_vmem);
+/*----------------------------------------------------------------------------*/
+/**
+ *
+ * @param instance
+ * @param arg
+ *
+ * @return 0 if no error
+ *
+ */
+static int handle_ioctl_app_unregister_wsm_l2(
+ struct mc_instance *instance,
+ struct mc_ioctl_app_unreg_wsm_l2_params *user_params
+)
+{
+ int ret = 0;
+ struct mc_ioctl_app_unreg_wsm_l2_params params;
+ struct mc_used_l2_table *used_l2table = NULL;
+
+ MCDRV_ASSERT(instance != NULL);
+ MCDRV_DBG_VERBOSE("enter\n");
+
+ do {
+ ret = copy_from_user(
+ &(params.in),
+ &(user_params->in),
+ sizeof(params.in));
+
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("copy_from_user\n");
+ break;
+ }
+
+ /* try to get the semaphore */
+ ret = down_interruptible(&(mc_drv_kmod_ctx.wsm_l2_sem));
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("down_interruptible() failed with %d\n",
+ ret);
+ ret = -ERESTARTSYS;
+ break;
+ }
+
+ do {
+ /* daemon can do this for another task. */
+ if (params.in.pid != 0) {
+ MCDRV_DBG_ERROR("pid != 0 unsupported\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ used_l2table =
+ find_used_l2_table_by_handle(params.in.handle);
+ if (used_l2table == NULL) {
+ ret = -EINVAL;
+ MCDRV_DBG_ERROR("entry not found\n");
+ break;
+ }
+
+ if (is_caller_mc_daemon(instance)) {
+ /* if daemon does this, we have to release the
+ MobiCore lock. */
+ used_l2table->flags &=
+ ~MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_MC;
+ } else if (instance != used_l2table->owner) {
+ ret = -EINVAL;
+ MCDRV_DBG_ERROR("instance does no own it\n");
+ break;
+ }
+
+ /* free table (if no further locks exist) */
+ delete_used_l2_table(used_l2table, FREE_FROM_NWD);
+ used_l2table = NULL;
+
+ /* there are no out parameters */
+
+ } while (FALSE);
+
+ /* release semaphore */
+ up(&(mc_drv_kmod_ctx.wsm_l2_sem));
+
+ } while (FALSE);
+
+ MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+ return ret;
+}
+
+
+/*----------------------------------------------------------------------------*/
+static int handle_ioctl_daemon_lock_wsm_l2(
+ struct mc_instance *instance,
+ struct mc_ioctl_daemon_lock_wsm_l2_params *user_params
+)
+{
+ int ret = 0;
+ struct mc_ioctl_daemon_lock_wsm_l2_params params;
+ struct mc_used_l2_table *used_l2table = NULL;
+
+ MCDRV_ASSERT(instance != NULL);
+ MCDRV_DBG_VERBOSE("enter\n");
+
+ do {
+ if (!is_caller_mc_daemon(instance)) {
+ MCDRV_DBG_ERROR("caller not MobiCore Daemon\n");
+ ret = -EFAULT;
+ break;
+ }
+
+ ret = copy_from_user(
+ &(params.in),
+ &(user_params->in),
+ sizeof(params.in));
+
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("copy_from_user\n");
+ break;
+ }
+ /* try to get the semaphore */
+ ret = down_interruptible(&(mc_drv_kmod_ctx.wsm_l2_sem));
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("down_interruptible() failed with %d\n",
+ ret);
+ ret = -ERESTARTSYS;
+ break;
+ }
+
+ do {
+ used_l2table =
+ find_used_l2_table_by_handle(params.in.handle);
+ if (used_l2table == NULL) {
+ ret = -EINVAL;
+ MCDRV_DBG_ERROR("entry not found\n");
+ break;
+ }
+ if (instance != used_l2table->owner) {
+ ret = -EINVAL;
+ MCDRV_DBG_ERROR("instance does no own it\n");
+ break;
+ }
+
+ /* lock entry */
+ if ((used_l2table->flags &
+ MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_MC) != 0) {
+ MCDRV_DBG_WARN("entry already locked\n");
+ }
+ used_l2table->flags |=
+ MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_MC;
+
+ /* prepare response */
+ memset(&(params.out), 0, sizeof(params.out));
+ params.out.phys_wsm_l2_table =
+ (uint32_t)get_l2_table_phys(used_l2table);
+
+ /* copy to user space */
+ ret = copy_to_user(
+ &(user_params->out),
+ &(params.out),
+ sizeof(params.out));
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("copy_to_user() failed\n");
+
+ /* undo, as userspace did not get it. */
+ used_l2table->flags |=
+ MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_MC;
+ break;
+ }
+
+ } while (FALSE);
+
+ /* release semaphore */
+ up(&(mc_drv_kmod_ctx.wsm_l2_sem));
+
+ } while (FALSE);
+
+ MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+ return ret;
+}
+
+
+/*----------------------------------------------------------------------------*/
+static int handle_ioctl_daemon_unlock_wsm_l2(
+ struct mc_instance *instance,
+ struct mc_ioctl_daemon_unlock_wsm_l2_params *user_params
+)
+{
+ int ret = 0;
+ struct mc_ioctl_daemon_unlock_wsm_l2_params params;
+ struct mc_used_l2_table *used_l2table = NULL;
+
+ MCDRV_ASSERT(instance != NULL);
+ MCDRV_DBG_VERBOSE("enter\n");
+
+ do {
+ if (!is_caller_mc_daemon(instance)) {
+ MCDRV_DBG_ERROR("caller not MobiCore Daemon\n");
+ ret = -EFAULT;
+ break;
+ }
+
+ ret = copy_from_user(
+ &(params.in),
+ &(user_params->in),
+ sizeof(params.in));
+
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("copy_from_user\n");
+ break;
+ }
+ /* try to get the semaphore */
+ ret = down_interruptible(&(mc_drv_kmod_ctx.wsm_l2_sem));
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("down_interruptible() failed with %d\n",
+ ret);
+ ret = -ERESTARTSYS;
+ break;
+ }
+
+ do {
+ used_l2table =
+ find_used_l2_table_by_handle(params.in.handle);
+ if (used_l2table == NULL) {
+ ret = -EINVAL;
+ MCDRV_DBG_ERROR("entry not found\n");
+ break;
+ }
+ if (instance != used_l2table->owner) {
+ ret = -EINVAL;
+ MCDRV_DBG_ERROR("instance does no own it\n");
+ break;
+ }
+
+ /* lock entry */
+ if ((used_l2table->flags &
+ MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_MC) == 0) {
+ MCDRV_DBG_WARN("entry is not locked locked\n");
+ }
+
+ /* free table (if no further locks exist) */
+ delete_used_l2_table(used_l2table, FREE_FROM_SWD);
+ used_l2table = NULL;
+
+ /* there are no out parameters */
+
+ } while (FALSE);
+
+ } while (FALSE);
+
+
+ MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/** Clears the reserved bit of each page and frees the pages */
+static inline void free_continguous_pages(
+ void *addr,
+ unsigned int size
+)
+{
+ struct page *page = virt_to_page(addr);
+ int i;
+ for (i = 0; i < size; i++) {
+ MCDRV_DBG_VERBOSE("free page at 0x%p\n", page);
+ ClearPageReserved(page);
+ page++;
+ }
+ /* REV luh: see man kmalloc */
+ free_pages((unsigned long)addr, size_to_order(size));
+}
+
+/*----------------------------------------------------------------------------*/
+/**
+ * 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(
+ struct mc_instance *instance,
+ uint32_t handle
+)
+{
+ int ret = 0;
+ unsigned int i;
+ struct mc_contg_buffer *contg_buffer;
+
+ do {
+ /* search for the given address in the contg_buffers list */
+ for (i = 0; i < MC_DRV_KMOD_CONTG_BUFFER_MAX; i++) {
+ contg_buffer = &(instance->contg_buffers[i]);
+ if (contg_buffer->handle == handle)
+ break;
+ }
+ if (i == MC_DRV_KMOD_CONTG_BUFFER_MAX) {
+ MCDRV_DBG_ERROR("contigous buffer not found\n");
+ ret = -EFAULT;
+ break;
+ }
+
+ MCDRV_DBG_VERBOSE("phys_addr=0x%p, virt_addr=0x%p\n",
+ contg_buffer->phys_addr,
+ contg_buffer->virt_kernel_addr);
+
+ free_continguous_pages(contg_buffer->virt_kernel_addr,
+ contg_buffer->num_pages);
+
+ memset(contg_buffer, 0, sizeof(*contg_buffer));
+
+ /* there are no out parameters */
+
+ } while (FALSE);
+
+
+ return ret;
+}
+EXPORT_SYMBOL(mobicore_free);
+/*----------------------------------------------------------------------------*/
+
+/**
+ *
+ * @param instance
+ * @param arg
+ *
+ * @return 0 if no error
+ *
+ */
+static int handle_ioctl_free(
+ struct mc_instance *instance,
+ union mc_ioctl_free_params *user_params
+)
+{
+ int ret = 0;
+ union mc_ioctl_free_params params;
+
+
+ MCDRV_ASSERT(instance != NULL);
+ MCDRV_DBG_VERBOSE("enter\n");
+
+ do {
+ ret = copy_from_user(
+ &(params.in),
+ &(user_params->in),
+ sizeof(params.in));
+
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("copy_from_user\n");
+ break;
+ }
+
+ /* daemon can do this for another task. */
+ if (params.in.pid != 0) {
+ MCDRV_DBG_ERROR("pid != 0 unsupported\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ ret = mobicore_free(instance, params.in.handle);
+
+ /* there are no out parameters */
+
+ } while (FALSE);
+
+ MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+ return ret;
+
+}
+
+
+/*----------------------------------------------------------------------------*/
+/**
+ *
+ * @param instance
+ * @param arg
+ *
+ * @return 0 if no error
+ *
+ */
+static int handle_ioctl_info(
+ struct mc_instance *instance,
+ union mc_ioctl_info_params *user_params
+)
+{
+ int ret = 0;
+ union mc_ioctl_info_params params;
+ union mc_fc_info fc_info;
+
+
+ MCDRV_ASSERT(instance != NULL);
+ MCDRV_DBG_VERBOSE("enter\n");
+
+ do {
+ /* only the MobiCore Daemon is allowed to call this function */
+ if (!is_caller_mc_daemon(instance)) {
+ MCDRV_DBG_ERROR("caller not MobiCore Daemon\n");
+ ret = -EFAULT;
+ break;
+ }
+
+ ret = copy_from_user(
+ &(params.in),
+ &(user_params->in),
+ sizeof(params.in));
+
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("copy_from_user\n");
+ break;
+ }
+
+
+ memset(&fc_info, 0, sizeof(fc_info));
+ fc_info.as_in.cmd = MC_FC_INFO;
+ fc_info.as_in.ext_info_id = params.in.ext_info_id;
+
+ MCDRV_DBG(
+ "fc_info in cmd=0x%08x, ext_info_id=0x%08x "
+ "rfu=(0x%08x, 0x%08x)\n",
+ fc_info.as_in.cmd,
+ fc_info.as_in.ext_info_id,
+ fc_info.as_in.rfu[0],
+ fc_info.as_in.rfu[1]);
+
+ mc_fastcall(&(fc_info.as_generic));
+
+ MCDRV_DBG(
+ "fc_info out resp=0x%08x, ret=0x%08x "
+ "state=0x%08x, ext_info=0x%08x\n",
+ 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);
+ if (ret != 0)
+ break;
+
+ memset(&(params.out), 0, sizeof(params.out));
+ params.out.state = fc_info.as_out.state;
+ params.out.ext_info = fc_info.as_out.ext_info;
+
+ ret = copy_to_user(
+ &(user_params->out),
+ &(params.out),
+ sizeof(params.out));
+
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("copy_to_user\n");
+ break;
+ }
+ } while (FALSE);
+
+ MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/**
+ *
+ * @param instance
+ * @param arg
+ *
+ * @return 0 if no error
+ *
+ */
+static int handle_ioctl_yield(
+ struct mc_instance *instance
+)
+{
+ int ret = 0;
+ union mc_fc_s_yield fc_s_yield;
+
+ MCDRV_ASSERT(instance != NULL);
+
+ /* avoid putting debug output here, as we do this very often */
+ MCDRV_DBG_VERBOSE("enter\n");
+
+ do {
+ /* only the MobiCore Daemon is allowed to call this function */
+ if (!is_caller_mc_daemon(instance)) {
+ MCDRV_DBG_ERROR("caller not MobiCore Daemon\n");
+ ret = -EFAULT;
+ break;
+ }
+
+ memset(&fc_s_yield, 0, sizeof(fc_s_yield));
+ fc_s_yield.as_in.cmd = MC_SMC_N_YIELD;
+ mc_fastcall(&(fc_s_yield.as_generic));
+ ret = convert_fc_ret(fc_s_yield.as_out.ret);
+ if (ret != 0)
+ break;
+
+ } while (FALSE);
+
+ MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/**
+ * handle ioctl and call common notify
+ *
+ * @param instance
+ * @param arg
+ *
+ * @return 0 if no error
+ *
+ */
+static int handle_ioctl_nsiq(
+ struct mc_instance *instance,
+ unsigned long arg
+)
+{
+ int ret = 0;
+
+ MCDRV_ASSERT(instance != NULL);
+
+ /* avoid putting debug output here, as we do this very often */
+ MCDRV_DBG_VERBOSE("enter\n");
+ /* only the MobiCore Daemon is allowed to call this function */
+ if (!is_caller_mc_daemon(instance)) {
+ MCDRV_DBG_ERROR("caller not MobiCore Daemon\n");
+ return -EFAULT;
+ }
+
+ do {
+ union mc_fc_nsiq fc_nsiq;
+ memset(&fc_nsiq, 0, sizeof(fc_nsiq));
+ fc_nsiq.as_in.cmd = MC_SMC_N_SIQ;
+ mc_fastcall(&(fc_nsiq.as_generic));
+ ret = convert_fc_ret(fc_nsiq.as_out.ret);
+ if (ret != 0)
+ break;
+ } while (FALSE);
+
+ MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/**
+ *
+ * @param instance
+ * @param arg
+ *
+ * @return 0 if no error
+ *
+ */
+static int handle_ioctl_dump_status(
+ struct mc_instance *instance,
+ unsigned long arg
+)
+{
+ int ret = 0;
+ int i = 0;
+ union mc_fc_info fc_info;
+
+ MCDRV_ASSERT(instance != NULL);
+ MCDRV_DBG_VERBOSE("enter\n");
+
+ do {
+ /* anybody with root access can do this. */
+ if (!is_userland_caller_privileged()) {
+ MCDRV_DBG_ERROR("caller must have root privileges\n");
+ ret = -EFAULT;
+ break;
+ }
+
+ /* loop ext_info */
+ while (TRUE) {
+ memset(&fc_info, 0, sizeof(fc_info));
+ fc_info.as_in.cmd = MC_FC_INFO;
+ fc_info.as_in.ext_info_id = i;
+
+ MCDRV_DBG(
+ "fc_info in cmd=0x%08x, ext_info_id=0x%08x "
+ "rfu=(0x%08x, 0x%08x)\n",
+ fc_info.as_in.cmd,
+ fc_info.as_in.ext_info_id,
+ fc_info.as_in.rfu[0],
+ fc_info.as_in.rfu[1]);
+
+ mc_fastcall(&(fc_info.as_generic));
+
+ MCDRV_DBG(
+ "fc_info out resp=0x%08x, ret=0x%08x "
+ "state=0x%08x, ext_info=0x%08x\n",
+ 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);
+ if (ret != 0)
+ break;
+
+ MCDRV_DBG("state=%08X, idx=%02d: ext_info=%08X\n",
+ fc_info.as_out.state,
+ i,
+ fc_info.as_out.ext_info);
+ i++;
+ };
+
+ if (ret != 0)
+ break;
+
+
+ } while (FALSE);
+
+ MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/**
+ *
+ * @param instance
+ * @param arg
+ *
+ * @return 0 if no error
+ *
+ */
+static int handle_ioctl_init(
+ struct mc_instance *instance,
+ union mc_ioctl_init_params *user_params
+)
+{
+ int ret = 0;
+ union mc_ioctl_init_params params;
+ union mc_fc_init fc_init;
+
+ MCDRV_ASSERT(instance != NULL);
+ MCDRV_DBG_VERBOSE("enter\n");
+
+ do {
+ /* only the MobiCore Daemon is allowed to call this function */
+ if (!is_caller_mc_daemon(instance)) {
+ MCDRV_DBG_ERROR("caller not MobiCore Daemon\n");
+ ret = -EFAULT;
+ break;
+ }
+
+ ret = copy_from_user(
+ &(params.in),
+ &(user_params->in),
+ sizeof(params.in));
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("copy_from_user failed\n");
+ break;
+ }
+
+ 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)params.in.base;
+ /* notification buffer start/length [16:16] [start, length] */
+ fc_init.as_in.nq_info = (params.in.nq_offset << 16)
+ | (params.in.nq_length & 0xFFFF);
+ /* mcp buffer start/length [16:16] [start, length] */
+ fc_init.as_in.mcp_info = (params.in.mcp_offset << 16)
+ | (params.in.mcp_length & 0xFFFF);
+
+ /* Set KMOD notification queue to start of MCI
+ mciInfo was already set up in mmap */
+ if (!mci_base) {
+ MCDRV_DBG_ERROR("No MCI set yet.\n");
+ return -EFAULT;
+ }
+ MCDRV_DBG("in cmd=0x%08x, base=0x%08x, "
+ "nq_info=0x%08x, mcp_info=0x%08x\n",
+ 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("out cmd=0x%08x, ret=0x%08x rfu=(0x%08x, 0x%08x)\n",
+ fc_init.as_out.resp,
+ fc_init.as_out.ret,
+ fc_init.as_out.rfu[0],
+ fc_init.as_out.rfu[1]);
+
+ ret = convert_fc_ret(fc_init.as_out.ret);
+ if (ret != 0)
+ break;
+
+ /* no ioctl response parameters */
+
+ } while (FALSE);
+
+ MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/**
+ *
+ * @param instance
+ * @param arg
+ *
+ * @return 0 if no error
+ *
+ */
+static int handle_ioctl_fc_execute(
+ struct mc_instance *instance,
+ union mc_ioctl_fc_execute_params *user_params
+)
+{
+ int ret = 0;
+ union mc_ioctl_fc_execute_params params;
+ union fc_generic fc_params;
+
+ MCDRV_ASSERT(instance != NULL);
+ MCDRV_DBG_VERBOSE("enter\n");
+
+ do {
+ /* only the MobiCore Daemon is allowed to call this function */
+ if (!is_caller_mc_daemon(instance)) {
+ MCDRV_DBG_ERROR("caller not MobiCore Daemon\n");
+ ret = -EFAULT;
+ break;
+ }
+
+ ret = copy_from_user(
+ &(params.in),
+ &(user_params->in),
+ sizeof(params.in));
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("copy_from_user failed\n");
+ break;
+ }
+
+ fc_params.as_in.cmd = -4;/*FC_EXECUTE */
+ fc_params.as_in.param[0] = params.in.phys_start_addr;
+ fc_params.as_in.param[1] = params.in.length;
+ fc_params.as_in.param[2] = 0;
+
+ MCDRV_DBG("in cmd=0x%08x, startAddr=0x%08x, length=0x%08x\n",
+ fc_params.as_in.cmd,
+ fc_params.as_in.param[0],
+ fc_params.as_in.param[1]);
+
+ mc_fastcall(&fc_params);
+
+ MCDRV_DBG("out cmd=0x%08x, ret=0x%08x rfu=(0x%08x, 0x%08x)\n",
+ fc_params.as_out.resp,
+ fc_params.as_out.ret,
+ fc_params.as_out.param[0],
+ fc_params.as_out.param[1]);
+
+ ret = convert_fc_ret(fc_params.as_out.ret);
+ if (ret != 0)
+ break;
+
+ /* no ioctl response parameters */
+
+ } while (FALSE);
+
+ MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+#define MC_MAKE_VERSION(major, minor) \
+ (((major & 0x0000ffff) << 16) | (minor & 0x0000ffff))
+/**
+ *
+ * @param instance
+ * @param arg
+ *
+ * @return 0 if no error
+ *
+ */
+static int handle_ioctl_get_version(
+ struct mc_instance *instance,
+ struct mc_ioctl_get_version_params *user_params
+)
+{
+ int ret = 0;
+ struct mc_ioctl_get_version_params params = {
+ {
+ MC_MAKE_VERSION(MCDRVMODULEAPI_VERSION_MAJOR,
+ MCDRVMODULEAPI_VERSION_MINOR)
+ }
+ };
+
+ MCDRV_ASSERT(instance != NULL);
+ MCDRV_DBG_VERBOSE("enter\n");
+
+ do {
+ MCDRV_DBG("mcDrvModuleApi version is %i.%i\n",
+ MCDRVMODULEAPI_VERSION_MAJOR,
+ MCDRVMODULEAPI_VERSION_MINOR);
+
+ /* no ioctl response parameters */
+ ret = copy_to_user(
+ &(user_params->out),
+ &(params.out),
+ sizeof(params.out));
+ if (ret != 0)
+ MCDRV_DBG_ERROR("copy_to_user() failed\n");
+
+ } while (FALSE);
+
+ MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/**
+ * This function will be called from user space as ioctl(...).
+ * @param file pointer to file
+ * @param cmd command
+ * @param arg arguments
+ *
+ * @return int 0 for OK and an errno in case of error
+ */
+static long mc_kernel_module_ioctl(
+ struct file *file,
+ unsigned int cmd,
+ unsigned long arg
+)
+{
+ int ret;
+ struct mc_instance *instance = get_instance(file);
+
+ MCDRV_ASSERT(instance != NULL);
+
+ switch (cmd) {
+ /*--------------------------------------------------------------------*/
+ case MC_DRV_KMOD_IOCTL_DUMP_STATUS:
+ ret = handle_ioctl_dump_status(
+ instance,
+ arg);
+ break;
+
+ /*--------------------------------------------------------------------*/
+ case MC_DRV_KMOD_IOCTL_FC_INIT:
+ ret = handle_ioctl_init(
+ instance,
+ (union mc_ioctl_init_params *)arg);
+ break;
+ /*--------------------------------------------------------------------*/
+ case MC_DRV_KMOD_IOCTL_FC_INFO:
+ ret = handle_ioctl_info(
+ instance,
+ (union mc_ioctl_info_params *)arg);
+ break;
+
+ /*--------------------------------------------------------------------*/
+ case MC_DRV_KMOD_IOCTL_FC_YIELD:
+ ret = handle_ioctl_yield(
+ instance);
+ break;
+
+ /*--------------------------------------------------------------------*/
+ case MC_DRV_KMOD_IOCTL_FC_NSIQ:
+ ret = handle_ioctl_nsiq(
+ instance,
+ arg);
+ break;
+
+ /*--------------------------------------------------------------------*/
+ case MC_DRV_KMOD_IOCTL_DAEMON_LOCK_WSM_L2:
+ ret = handle_ioctl_daemon_lock_wsm_l2(
+ instance,
+ (struct mc_ioctl_daemon_lock_wsm_l2_params *)arg);
+ break;
+
+ /*--------------------------------------------------------------------*/
+ case MC_DRV_KMOD_IOCTL_DAEMON_UNLOCK_WSM_L2:
+ ret = handle_ioctl_daemon_unlock_wsm_l2(
+ instance,
+ (struct mc_ioctl_daemon_unlock_wsm_l2_params *)arg);
+ break;
+
+ /*--------------------------------------------------------------------*/
+ case MC_DRV_KMOD_IOCTL_FREE:
+ /* called by ClientLib */
+ ret = handle_ioctl_free(
+ instance,
+ (union mc_ioctl_free_params *)arg);
+ break;
+
+ /*--------------------------------------------------------------------*/
+ case MC_DRV_KMOD_IOCTL_APP_REGISTER_WSM_L2:
+ /* called by ClientLib */
+ ret = handle_ioctl_app_register_wsm_l2(
+ instance,
+ (union mc_ioctl_app_reg_wsm_l2_params *)arg);
+ break;
+
+ /*--------------------------------------------------------------------*/
+ case MC_DRV_KMOD_IOCTL_APP_UNREGISTER_WSM_L2:
+ /* called by ClientLib */
+ ret = handle_ioctl_app_unregister_wsm_l2(
+ instance,
+ (struct mc_ioctl_app_unreg_wsm_l2_params *)arg);
+ break;
+
+ /*--------------------------------------------------------------------*/
+ case MC_DRV_KMOD_IOCTL_FC_EXECUTE:
+ ret = handle_ioctl_fc_execute(
+ instance,
+ (union mc_ioctl_fc_execute_params *)arg);
+ break;
+
+ /*--------------------------------------------------------------------*/
+ case MC_DRV_KMOD_IOCTL_GET_VERSION:
+ ret = handle_ioctl_get_version(
+ instance,
+ (struct mc_ioctl_get_version_params *)arg);
+ break;
+
+ /*--------------------------------------------------------------------*/
+ default:
+ MCDRV_DBG_ERROR("unsupported cmd=%d\n", cmd);
+ ret = -EFAULT;
+ break;
+
+ } /* end switch(cmd) */
+
+#ifdef MC_MEM_TRACES
+ mobicore_log_read();
+#endif
+
+ return (int)ret;
+}
+
+
+/*----------------------------------------------------------------------------*/
+/**
+ * This function will be called from user space as read(...).
+ * 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.
+ * @param *file
+ * @param *buffer buffer where to copy to(userspace)
+ * @param buffer_len number of requested data
+ * @param *pos not used
+ * @return ssize_t ok case: number of copied data
+ * error case: return errno
+ */
+static ssize_t mc_kernel_module_read(
+ struct file *file,
+ char *buffer,
+ size_t buffer_len,
+ loff_t *pos
+)
+{
+ int ret = 0, ssiq_counter;
+ size_t retLen = 0;
+ struct mc_instance *instance = get_instance(file);
+
+ MCDRV_ASSERT(instance != NULL);
+
+ /* avoid debug output on non-error, because this is call quite often */
+ MCDRV_DBG_VERBOSE("enter\n");
+
+ do {
+ /* only the MobiCore Daemon is allowed to call this function */
+ if (!is_caller_mc_daemon(instance)) {
+ MCDRV_DBG_ERROR("caller not MobiCore Daemon\n");
+ ret = -EFAULT;
+ break;
+ }
+
+ if (buffer_len < sizeof(unsigned int)) {
+ MCDRV_DBG_ERROR("invalid length\n");
+ ret = (ssize_t)(-EINVAL);
+ break;
+ }
+
+ for (;;) {
+ if (down_interruptible(
+ &mc_drv_kmod_ctx.daemon_ctx.sem)) {
+ MCDRV_DBG_VERBOSE("read interrupted\n");
+ ret = (ssize_t)-ERESTARTSYS;
+ break;
+ }
+
+ ssiq_counter = atomic_read(
+ &(mc_drv_kmod_ctx.ssiq_ctx.counter));
+ MCDRV_DBG_VERBOSE("ssiq_counter=%i, ctx.counter=%i\n",
+ ssiq_counter,
+ mc_drv_kmod_ctx.daemon_ctx.ssiq_counter);
+
+ if (ssiq_counter !=
+ mc_drv_kmod_ctx.daemon_ctx.ssiq_counter) {
+ /* read data and exit loop without
+ error */
+ mc_drv_kmod_ctx.daemon_ctx.ssiq_counter =
+ ssiq_counter;
+ ret = 0;
+ break;
+ }
+
+ /* end loop if non-blocking */
+ if ((file->f_flags & O_NONBLOCK) != 0) {
+ MCDRV_DBG_ERROR("non-blocking read\n");
+ ret = (ssize_t)(-EAGAIN);
+ break;
+ }
+
+ if (signal_pending(current) != 0) {
+ MCDRV_DBG_VERBOSE("received signal.\n");
+ ret = (ssize_t)(-ERESTARTSYS);
+ break;
+ }
+
+ }
+
+ /* we are here if an event occurred or we had an
+ error.*/
+ if (ret != 0)
+ break;
+
+ /* read data and exit loop */
+ ret = copy_to_user(
+ buffer,
+ &(mc_drv_kmod_ctx.daemon_ctx.ssiq_counter),
+ sizeof(unsigned int));
+
+
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("copy_to_user failed\n");
+ ret = (ssize_t)(-EFAULT);
+ break;
+ }
+
+ retLen = sizeof(s32);
+
+ } while (FALSE);
+
+ /* avoid debug on non-error. */
+ if (ret == 0)
+ ret = (size_t)retLen;
+ else
+ MCDRV_DBG("exit with %d/0x%08X\n", ret, ret);
+
+ return (ssize_t)ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/**
+ * 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
+ * @param phys_addr pointer for the physical 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,
+ void **phys_addr
+)
+{
+ unsigned int i;
+ unsigned int order;
+ unsigned long allocated_size;
+ int ret = 0;
+ struct mc_contg_buffer *contg_buffer = 0;
+ void *virt_kernel_addr_stack;
+ void *phys_addr_stack;
+
+ MCDRV_ASSERT(instance != NULL);
+ MCDRV_DBG("%s (size=%ld)\n", __func__, requested_size);
+
+ order = size_to_order(requested_size);
+ if (order == INVALID_ORDER) {
+ MCDRV_DBG_ERROR(
+ "size to order converting failed for size %ld\n",
+ requested_size);
+ return INVALID_ORDER;
+ }
+
+ allocated_size = (1<<order)*PAGE_SIZE;
+
+ MCDRV_DBG("size %ld -> order %d --> %ld (2^n pages)\n",
+ requested_size, order, allocated_size);
+
+ do {
+ /* Usual Wsm request, allocate contigous buffer. */
+ /* search for a free entry in the wsm buffer list
+ * REV axh: serialize this over multiple instances. */
+ for (i = 0; i < MC_DRV_KMOD_CONTG_BUFFER_MAX; i++) {
+ contg_buffer = &(instance->contg_buffers[i]);
+ if (contg_buffer->handle == 0) {
+ contg_buffer->handle = get_mc_kmod_unique_id();
+ break;
+ }
+ }
+ if (i == MC_DRV_KMOD_CONTG_BUFFER_MAX) {
+ MCDRV_DBG_ERROR("no free contigous buffer\n");
+ ret = -EFAULT;
+ break;
+ }
+
+ /* Common code for all allocation paths */
+ virt_kernel_addr_stack = (void *)__get_free_pages(
+ GFP_USER | __GFP_COMP,
+ order);
+ if (virt_kernel_addr_stack == NULL) {
+ MCDRV_DBG_ERROR("get_free_pages failed\n");
+ ret = -ENOMEM;
+ break;
+ }
+
+ /* Get physical address to instance data */
+ phys_addr_stack = (void *)virt_to_phys(virt_kernel_addr_stack);
+ /* TODO: check for INVALID_ADDRESS? */
+
+ MCDRV_DBG(
+ "allocated phys=0x%p - 0x%p, "
+ "size=%ld, kernel_virt=0x%p, handle=%d\n",
+ phys_addr_stack,
+ (void *)((unsigned int)phys_addr_stack+allocated_size),
+ allocated_size,
+ virt_kernel_addr_stack,
+ contg_buffer->handle);
+
+ /* Usual Wsm request, allocate contg_buffer.
+ * Also, we never free a persistent Tci */
+ contg_buffer->phys_addr = phys_addr_stack;
+ contg_buffer->virt_kernel_addr = virt_kernel_addr_stack;
+ contg_buffer->virt_user_addr = virt_kernel_addr_stack;
+ contg_buffer->num_pages = (1U << order);
+ *handle = contg_buffer->handle;
+ *virt_kernel_addr = virt_kernel_addr_stack;
+ *phys_addr = phys_addr_stack;
+
+ } while (FALSE);
+
+ MCDRV_DBG_VERBOSE("%s: exit with 0x%08X\n", __func__, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL(mobicore_allocate_wsm);
+
+
+/*----------------------------------------------------------------------------*/
+/**
+ * This function will be called from user space as address = mmap(...).
+ *
+ * @param file
+ * @param vmarea
+ * vmarea.pg_offset != 0 is mapping of MCI is requested
+ *
+ * @return 0 if OK or -ENOMEM in case of error.
+ */
+static int mc_kernel_module_mmap(
+ struct file *file,
+ struct vm_area_struct *vmarea
+)
+{
+ unsigned int i;
+ unsigned int order;
+ void *virt_kernel_addr_stack = 0;
+ void *phys_addr = 0;
+ unsigned long requested_size =
+ vmarea->vm_end - vmarea->vm_start;
+ unsigned long allocated_size;
+ int ret = 0;
+ struct mc_contg_buffer *contg_buffer = 0;
+ unsigned int handle = 0;
+ struct mc_instance *instance = get_instance(file);
+ unsigned int request = vmarea->vm_pgoff * 4096;
+#if defined(DEBUG)
+ bool release = false;
+#else
+ bool release = true;
+#endif
+
+ MCDRV_ASSERT(instance != NULL);
+ MCDRV_DBG("enter (vmaStart=0x%p, size=%ld, request=0x%x, mci=0x%x)\n",
+ (void *)vmarea->vm_start,
+ requested_size,
+ request,
+ mci_base);
+
+ order = size_to_order(requested_size);
+ if (order == INVALID_ORDER) {
+ MCDRV_DBG_ERROR(
+ "size to order converting failed for size %ld\n",
+ requested_size);
+ return -ENOMEM;
+ }
+
+ allocated_size = (1<<order)*PAGE_SIZE;
+
+ MCDRV_DBG("size %ld -> order %d --> %ld (2^n pages)\n",
+ requested_size, order, allocated_size);
+
+ do {
+ /* Daemon tries to get an existing MCI */
+ if ((request == MC_DRV_KMOD_MMAP_MCI) && (mci_base != 0)) {
+ MCDRV_DBG("Request MCI, it is at (%x)\n", mci_base);
+
+ if (!is_caller_mc_daemon(instance)) {
+ ret = -EPERM;
+ break;
+ }
+ virt_kernel_addr_stack = (void *)mci_base;
+ phys_addr =
+ (void *)virt_to_phys(virt_kernel_addr_stack);
+ } else {
+ /* Usual Wsm request, allocate buffer. */
+ if (request == MC_DRV_KMOD_MMAP_WSM) {
+ /* search for a free entry in the buffer list
+ REV axh: serialize this over multiple instances.
+ */
+ for (i = 0; i < MC_DRV_KMOD_CONTG_BUFFER_MAX;
+ i++) {
+ contg_buffer =
+ &(instance->contg_buffers[i]);
+ if (contg_buffer->handle == 0) {
+ contg_buffer->handle =
+ get_mc_kmod_unique_id();
+ break;
+ }
+ }
+ if (i == MC_DRV_KMOD_CONTG_BUFFER_MAX) {
+ MCDRV_DBG_ERROR(
+ "no free contigous buffer\n");
+ ret = -EFAULT;
+ break;
+ }
+ } else {
+ if (request <= MC_DRV_KMOD_MMAP_PERSISTENTWSM
+ || release) {
+ /* Special Wsm request
+ --> only Daemon is allowed */
+ if (!is_caller_mc_daemon(instance)) {
+ ret = -EPERM;
+ break;
+ }
+ }
+ }
+ if (request <= MC_DRV_KMOD_MMAP_PERSISTENTWSM) {
+ /* Common code for all allocation paths
+ * get physical address, */
+ virt_kernel_addr_stack =
+ (void *)__get_free_pages(
+ GFP_USER | __GFP_COMP,
+ order);
+ if (virt_kernel_addr_stack == NULL) {
+ MCDRV_DBG_ERROR(
+ "get_free_pages failed\n");
+ ret = -ENOMEM;
+ break;
+ }
+ if (request == MC_DRV_KMOD_MMAP_WSM)
+ handle = contg_buffer->handle;
+ /* Get physical address to instance data */
+ /* TODO: check for INVALID_ADDRESS? */
+ phys_addr = (void *)virt_to_phys(
+ virt_kernel_addr_stack);
+ } else {
+#if defined(DEBUG)
+ phys_addr = (void *)request;
+ virt_kernel_addr_stack = phys_to_virt(request);
+#endif
+ }
+ }
+ /* Common code for all mmap calls:
+ * map page to user
+ * store data in page */
+
+ MCDRV_DBG("allocated phys=0x%p - 0x%p, "
+ "size=%ld, kernel_virt=0x%p, handle=%d\n",
+ phys_addr,
+ (void *)((unsigned int)phys_addr+allocated_size),
+ allocated_size, virt_kernel_addr_stack, handle);
+
+ vmarea->vm_flags |= VM_RESERVED;
+ /* 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),
+ addr_to_pfn(phys_addr),
+ requested_size,
+ vmarea->vm_page_prot);
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("remapPfnRange failed\n");
+
+ /* free allocated pages when mmap fails, however, do not
+ do it, when daemon tried to get an MCI that
+ existed */
+ if (!((request == MC_DRV_KMOD_MMAP_MCI) &&
+ (mci_base != 0)))
+ free_continguous_pages(virt_kernel_addr_stack,
+ (1U << order));
+ break;
+ }
+
+ /* Usual Wsm request, allocate contg_buffer.
+ When requesting Mci, we do not associate the page with
+ the process.
+ Note: we also never free the Mci
+ Also, we never free a persistent Tci */
+ if (request == MC_DRV_KMOD_MMAP_WSM) {
+ contg_buffer->phys_addr = phys_addr;
+ contg_buffer->virt_kernel_addr = virt_kernel_addr_stack;
+ contg_buffer->virt_user_addr =
+ (void *)(vmarea->vm_start);
+ contg_buffer->num_pages = (1U << order);
+ }
+
+ /* set response in allocated buffer */
+ {
+ struct mc_mmap_resp *mmapResp =
+ (struct mc_mmap_resp *)virt_kernel_addr_stack;
+ /* TODO: do this for daemon only, otherwise set NULL */
+ mmapResp->phys_addr = (uint32_t)phys_addr;
+ mmapResp->handle = handle;
+ if ((request == MC_DRV_KMOD_MMAP_MCI) &&
+ (mci_base != 0)) {
+ mmapResp->is_reused = 1;
+ } else
+ mmapResp->is_reused = 0;
+ }
+
+ /* store MCI pointer */
+ if ((request == MC_DRV_KMOD_MMAP_MCI) && (mci_base == 0)) {
+ mci_base = (uint32_t)virt_kernel_addr_stack;
+ MCDRV_DBG("MCI base set to 0x%x\n", mci_base);
+ }
+ } while (FALSE);
+
+ MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+ return (int)ret;
+}
+
+#ifdef CONFIG_SMP
+/*----------------------------------------------------------------------------*/
+/**
+ * Force migration of current task to CPU0(where the monitor resides)
+ *
+ * @return Error code or 0 for success
+ */
+static int goto_cpu0(
+ void
+)
+{
+ int ret = 0;
+ struct cpumask mask = CPU_MASK_CPU0;
+
+ MCDRV_DBG_VERBOSE("System has %d CPU's, we are on CPU #%d\n"
+ "\tBinding this process to CPU #0.\n"
+ "\tactive mask is %lx, setting it to mask=%lx\n",
+ nr_cpu_ids,
+ raw_smp_processor_id(),
+ cpu_active_mask->bits[0],
+ mask.bits[0]);
+ ret = set_cpus_allowed_ptr(current, &mask);
+ if (ret != 0)
+ MCDRV_DBG_ERROR("set_cpus_allowed_ptr=%d.\n", ret);
+ MCDRV_DBG_VERBOSE("And now we are on CPU #%d\n",
+ raw_smp_processor_id());
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/**
+ * Restore CPU mask for current to ALL Cpus(reverse of goto_cpu0)
+ *
+ * @return Error code or 0 for success
+ */
+static int goto_all_cpu(
+ void
+)
+{
+ int ret = 0;
+
+ struct cpumask mask = CPU_MASK_ALL;
+
+ MCDRV_DBG_VERBOSE("System has %d CPU's, we are on CPU #%d\n"
+ "\tBinding this process to CPU #0.\n"
+ "\tactive mask is %lx, setting it to mask=%lx\n",
+ nr_cpu_ids,
+ raw_smp_processor_id(),
+ cpu_active_mask->bits[0],
+ mask.bits[0]);
+ ret = set_cpus_allowed_ptr(current, &mask);
+ if (ret != 0)
+ MCDRV_DBG_ERROR("set_cpus_allowed_ptr=%d.\n", ret);
+ MCDRV_DBG_VERBOSE("And now we are on CPU #%d\n",
+ raw_smp_processor_id());
+
+ return ret;
+}
+
+#else
+static int goto_cpu0(void)
+{
+ return 0;
+}
+
+static int goto_all_cpu(void)
+{
+ return 0;
+}
+#endif
+
+/*----------------------------------------------------------------------------*/
+/**
+ * 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;
+ pid_t pid_vnr;
+
+ instance = kzalloc(sizeof(*instance), GFP_KERNEL);
+ if (instance == NULL)
+ return NULL;
+
+ /* get a unique ID for this instance (PIDs are not unique) */
+ instance->handle = get_mc_kmod_unique_id();
+
+ /* get the PID of the calling process. We avoid using
+ * current->pid directly, as 2.6.24 introduced PID
+ * namespaces. See also http://lwn.net/Articles/259217 */
+ pid_vnr = task_pid_vnr(current);
+ instance->pid_vnr = pid_vnr;
+
+ return instance;
+}
+EXPORT_SYMBOL(mobicore_open);
+
+/*----------------------------------------------------------------------------*/
+/**
+ * This function will be called from user space as fd = open(...).
+ * A set of internal instance data are created and initialized.
+ *
+ * @param inode
+ * @param file
+ * @return 0 if OK or -ENOMEM if no allocation was possible.
+ */
+static int mc_kernel_module_open(
+ struct inode *inode,
+ struct file *file
+)
+{
+ struct mc_instance *instance;
+ int ret = 0;
+
+ MCDRV_DBG_VERBOSE("enter\n");
+
+ do {
+ instance = mobicore_open();
+ if (instance == NULL)
+ return -ENOMEM;
+
+ /* check if Daemon. We simply assume that the first to open us
+ with root privileges must be the daemon. */
+ if ((is_userland_caller_privileged())
+ && (mc_drv_kmod_ctx.daemon_inst == NULL)) {
+ MCDRV_DBG("accept this as MobiCore Daemon\n");
+
+ /* Set the caller's CPU mask to CPU0*/
+ ret = goto_cpu0();
+ if (ret != 0) {
+ mobicore_release(instance);
+ file->private_data = NULL;
+ MCDRV_DBG("changing core failed!\n");
+ break;
+ }
+
+ mc_drv_kmod_ctx.daemon_inst = instance;
+ sema_init(&mc_drv_kmod_ctx.daemon_ctx.sem,
+ DAEMON_SEM_VAL);
+ /* init ssiq event counter */
+ mc_drv_kmod_ctx.daemon_ctx.ssiq_counter =
+ atomic_read(
+ &(mc_drv_kmod_ctx.ssiq_ctx.counter));
+
+#ifdef MC_MEM_TRACES
+ /* The traces have to be setup on CPU-0 since we must
+ * do a fastcall to MobiCore. */
+ if (!mci_base)
+ /* Do the work only if MCI base is not
+ * initialized properly */
+ work_on_cpu(0, mobicore_log_setup, NULL);
+#endif
+ }
+
+ /* store instance data reference */
+ file->private_data = instance;
+
+ /* TODO axh: link all instances to allow clean up? */
+
+ } while (FALSE);
+
+ MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+ return (int)ret;
+
+}
+
+/*----------------------------------------------------------------------------*/
+/**
+ * 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
+)
+{
+ int ret = 0;
+ int i;
+ struct mc_used_l2_table *used_l2table, *used_l2table_temp;
+
+ do {
+ /* try to get the semaphore */
+ ret = down_interruptible(&(mc_drv_kmod_ctx.wsm_l2_sem));
+ if (ret != 0) {
+ MCDRV_DBG_ERROR(
+ "down_interruptible() failed with %d\n", ret);
+ /* TODO: can be block here? */
+ ret = -ERESTARTSYS;
+ } else {
+ /* Check if some WSM is still in use. */
+ list_for_each_entry_safe(
+ used_l2table,
+ used_l2table_temp,
+ &(mc_drv_kmod_ctx.mc_used_l2_tables),
+ list
+ ) {
+ if (used_l2table->owner == instance) {
+ MCDRV_DBG_WARN(
+ "trying to release WSM L2: "
+ "physBase=%p ,nr_of_pages=%d\n",
+ get_l2_table_phys(used_l2table),
+ used_l2table->nr_of_pages);
+
+ /* unlock app usage and free if MobiCore
+ does not use it */
+ delete_used_l2_table(used_l2table,
+ FREE_FROM_NWD);
+ }
+ } /* end while */
+
+ /* release semaphore */
+ up(&(mc_drv_kmod_ctx.wsm_l2_sem));
+ }
+
+
+ /* release all mapped data */
+ for (i = 0; i < MC_DRV_KMOD_CONTG_BUFFER_MAX; i++) {
+ struct mc_contg_buffer *contg_buffer =
+ &(instance->contg_buffers[i]);
+
+ if (contg_buffer->virt_user_addr != 0) {
+ free_continguous_pages(
+ contg_buffer->virt_kernel_addr,
+ contg_buffer->num_pages);
+ }
+ }
+
+ /* release instance context */
+ kfree(instance);
+ } while (FALSE);
+
+ return ret;
+}
+EXPORT_SYMBOL(mobicore_release);
+
+/*----------------------------------------------------------------------------*/
+/**
+ * This function will be called from user space as close(...).
+ * The instance data are freed and the associated memory pages are unreserved.
+ *
+ * @param inode
+ * @param file
+ *
+ * @return 0
+ */
+static int mc_kernel_module_release(
+ struct inode *inode,
+ struct file *file
+)
+{
+ int ret = 0;
+ struct mc_instance *instance = get_instance(file);
+
+ MCDRV_DBG_VERBOSE("enter\n");
+
+ do {
+ /* check if daemon closes us. */
+ if (is_caller_mc_daemon(instance)) {
+ /* TODO: cleanup?
+ * mc_drv_kmod_ctx.mc_used_l2_tables remains */
+ MCDRV_DBG_WARN("WARNING: MobiCore Daemon died\n");
+ mc_drv_kmod_ctx.daemon_inst = NULL;
+ }
+
+ ret = mobicore_release(instance);
+
+ } while (FALSE);
+
+ MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", 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.
+ *
+ * @param intr
+ * @param *context pointer to registered device data
+ *
+ * @return IRQ_HANDLED
+ */
+static irqreturn_t mc_kernel_module_intr_ssiq(
+ int intr,
+ void *context
+)
+{
+ irqreturn_t ret = IRQ_NONE;
+
+ /* we know the context. */
+ MCDRV_ASSERT(&mc_drv_kmod_ctx == context);
+
+ do {
+ if (intr != MC_INTR_SSIQ) {
+ /* this should not happen, as we did no register for any
+ other interrupt. For debugging, we print a
+ message, but continue */
+ MCDRV_DBG_WARN(
+ "unknown interrupt %d, expecting only %d\n",
+ intr, MC_INTR_SSIQ);
+ }
+ MCDRV_DBG_VERBOSE("received interrupt %d\n",
+ intr);
+
+ /* increment interrupt event counter */
+ atomic_inc(&(mc_drv_kmod_ctx.ssiq_ctx.counter));
+
+ /* signal the daemon */
+ up(&mc_drv_kmod_ctx.daemon_ctx.sem);
+
+
+ ret = IRQ_HANDLED;
+
+ } while (FALSE);
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/** function table structure of this device driver. */
+static const struct file_operations mc_kernel_module_file_operations = {
+ .owner = THIS_MODULE, /**< driver owner */
+ .open = mc_kernel_module_open, /**< driver open function */
+ .release = mc_kernel_module_release, /**< driver release function*/
+ .unlocked_ioctl = mc_kernel_module_ioctl, /**< driver ioctl function */
+ .mmap = mc_kernel_module_mmap, /**< driver mmap function */
+ .read = mc_kernel_module_read, /**< driver read function */
+};
+
+/*----------------------------------------------------------------------------*/
+/** registration structure as miscdevice. */
+static struct miscdevice mc_kernel_module_device = {
+ .name = MC_DRV_MOD_DEVNODE, /**< device name */
+ .minor = MISC_DYNAMIC_MINOR, /**< device minor number */
+ /** device interface function structure */
+ .fops = &mc_kernel_module_file_operations,
+};
+
+
+/*----------------------------------------------------------------------------*/
+/**
+ * This function is called the kernel during startup or by a insmod command.
+ * This device is installed and registered as miscdevice, then interrupt and
+ * queue handling is set up
+ *
+ * @return 0 for no error or -EIO if registration fails
+ */
+static int __init mc_kernel_module_init(
+ void
+)
+{
+ int ret = 0;
+
+ MCDRV_DBG("enter (Build " __TIMESTAMP__ ")\n");
+ MCDRV_DBG("mcDrvModuleApi version is %i.%i\n",
+ MCDRVMODULEAPI_VERSION_MAJOR,
+ MCDRVMODULEAPI_VERSION_MINOR);
+#ifdef MOBICORE_COMPONENT_BUILD_TAG
+ MCDRV_DBG("%s\n", MOBICORE_COMPONENT_BUILD_TAG);
+#endif
+ do {
+ /* Hardware does not support ARM TrustZone
+ -> Cannot continue! */
+ if (!has_security_extensions()) {
+ MCDRV_DBG_ERROR(
+ "Hardware does't support ARM TrustZone!\n");
+ ret = -ENODEV;
+ break;
+ }
+
+ /* Running in secure mode -> Cannot load the driver! */
+ if (is_secure_mode()) {
+ MCDRV_DBG_ERROR("Running in secure MODE!\n");
+ ret = -ENODEV;
+ break;
+ }
+
+ sema_init(&mc_drv_kmod_ctx.daemon_ctx.sem, DAEMON_SEM_VAL);
+ /* set up S-SIQ interrupt handler */
+ ret = request_irq(
+ MC_INTR_SSIQ,
+ mc_kernel_module_intr_ssiq,
+ IRQF_TRIGGER_RISING,
+ MC_DRV_MOD_DEVNODE,
+ &mc_drv_kmod_ctx);
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("interrupt request failed\n");
+ break;
+ }
+
+ ret = misc_register(&mc_kernel_module_device);
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("device register failed\n");
+ break;
+ }
+
+ /* initialize event counter for signaling of an IRQ to zero */
+ atomic_set(&(mc_drv_kmod_ctx.ssiq_ctx.counter), 0);
+
+ /* init list for WSM L2 chunks. */
+ INIT_LIST_HEAD(&(mc_drv_kmod_ctx.mc_l2_tables_sets));
+
+ /* L2 table descriptor list. */
+ INIT_LIST_HEAD(&(mc_drv_kmod_ctx.mc_used_l2_tables));
+
+ sema_init(&(mc_drv_kmod_ctx.wsm_l2_sem), 1);
+
+ /* initialize unique number counter which we can use for
+ handles. It is limited to 2^32, but this should be
+ enough to be roll-over safe for us. We start with 1
+ instead of 0. */
+ atomic_set(&(mc_drv_kmod_ctx.unique_counter), 1);
+
+ mci_base = 0;
+ MCDRV_DBG("initialized\n");
+
+ ret = 0;
+
+ } while (FALSE);
+
+ MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret);
+
+ return (int)ret;
+}
+
+
+
+/*----------------------------------------------------------------------------*/
+/**
+ * This function removes this device driver from the Linux device manager .
+ */
+static void __exit mc_kernel_module_exit(
+ void
+)
+{
+ struct mc_used_l2_table *used_l2table;
+
+ MCDRV_DBG_VERBOSE("enter\n");
+
+ mobicore_log_free();
+
+ /* Check if some WSM is still in use. */
+ list_for_each_entry(
+ used_l2table,
+ &(mc_drv_kmod_ctx.mc_used_l2_tables),
+ list
+ ) {
+ MCDRV_DBG_WARN(
+ "WSM L2 still in use: physBase=%p ,nr_of_pages=%d\n",
+ get_l2_table_phys(used_l2table),
+ used_l2table->nr_of_pages);
+ } /* end while */
+
+ free_irq(MC_INTR_SSIQ, &mc_drv_kmod_ctx);
+
+ misc_deregister(&mc_kernel_module_device);
+ MCDRV_DBG_VERBOSE("exit");
+}
+
+
+/*----------------------------------------------------------------------------*/
+/* Linux Driver Module Macros */
+module_init(mc_kernel_module_init);
+module_exit(mc_kernel_module_exit);
+MODULE_AUTHOR("Giesecke & Devrient GmbH");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MobiCore driver");
+
+/** @} */
+
diff --git a/drivers/gud/mobicore_driver/mc_drv_module.h b/drivers/gud/mobicore_driver/mc_drv_module.h
new file mode 100644
index 0000000..8b402d6
--- /dev/null
+++ b/drivers/gud/mobicore_driver/mc_drv_module.h
@@ -0,0 +1,238 @@
+/**
+ * Header file of MobiCore Driver Kernel Module.
+ *
+ * @addtogroup MCD_MCDIMPL_KMOD_IMPL
+ * @{
+ * Internal structures of the McDrvModule
+ * @file
+ *
+ * Header file the MobiCore Driver Kernel Module,
+ * its internal structures and defines.
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009-2012 -->
+ *
+ * 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.
+ */
+
+#ifndef _MC_DRV_KMOD_H_
+#define _MC_DRV_KMOD_H_
+
+#include "mc_drv_module_linux_api.h"
+#include "public/mc_drv_module_api.h"
+/** Platform specific settings */
+#include "platform.h"
+
+/** ARM Specific masks and modes */
+#define ARM_CPSR_MASK 0x1F
+#define ARM_MONITOR_MODE 0b10110
+#define ARM_SECURITY_EXTENSION_MASK 0x30
+
+/**
+ * Number of page table entries in one L2 table. This is ARM specific, an
+ * L2 table covers 1 MiB by using 256 entry referring to 4KiB pages each.
+ */
+#define MC_ARM_L2_TABLE_ENTRIES 256
+
+/** Maximum number of contiguous buffer allocations for one driver instance. */
+#define MC_DRV_KMOD_CONTG_BUFFER_MAX 16
+
+/** Number of pages for L2 tables. There are 4 table in each page. */
+#define MC_DRV_KMOD_L2_TABLE_PER_PAGES 4
+
+/** ARM level 2 (L2) table with 256 entries. Size: 1k */
+struct l2table {
+ pte_t table_entries[MC_ARM_L2_TABLE_ENTRIES];
+};
+
+#define INVALID_ADDRESS ((void *)(-1))
+
+/** ARM L2 PTE bits */
+#define L2_FLAG_SMALL_XN (1U << 0)
+#define L2_FLAG_SMALL (1U << 1)
+#define L2_FLAG_B (1U << 2)
+#define L2_FLAG_C (1U << 3)
+#define L2_FLAG_AP0 (1U << 4)
+#define L2_FLAG_AP1 (1U << 5)
+#define L2_FLAG_SMALL_TEX0 (1U << 6)
+#define L2_FLAG_SMALL_TEX1 (1U << 7)
+#define L2_FLAG_SMALL_TEX2 (1U << 8)
+#define L2_FLAG_APX (1U << 9)
+#define L2_FLAG_S (1U << 10)
+#define L2_FLAG_NG (1U << 11)
+
+/**
+ * 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_contg_buffer {
+ unsigned int handle; /* unique handle */
+ void *virt_user_addr; /**< virtual User start address */
+ void *virt_kernel_addr; /**< virtual Kernel start address */
+ void *phys_addr; /**< physical start address */
+ unsigned int num_pages; /**< number of pages */
+};
+
+/** Instance data for MobiCore Daemon and TLCs. */
+struct mc_instance {
+ /** unique handle */
+ unsigned int handle;
+ /** process that opened this instance */
+ pid_t pid_vnr;
+ /** buffer list for mmap generated address space and
+ its virtual client address */
+ struct mc_contg_buffer contg_buffers[MC_DRV_KMOD_CONTG_BUFFER_MAX];
+};
+
+/** Store for four L2 tables in one 4kb page*/
+struct mc_l2_table_store {
+ struct l2table table[MC_DRV_KMOD_L2_TABLE_PER_PAGES];
+};
+
+/** Usage and maintenance information about mc_l2_table_store */
+struct mc_l2_tables_set {
+ struct list_head list;
+ unsigned int usage_bitmap; /**< usage bitmap */
+ struct mc_l2_table_store *kernel_virt; /**< kernel virtual address */
+ struct mc_l2_table_store *phys; /**< physical address */
+ struct page *page; /**< pointer to page struct */
+};
+
+/**
+ * L2 table allocated to the Daemon or a TLC describing a world shared buffer.
+ * When users map a malloc()ed area into SWd, a L2 table is allocated.
+ * In addition, the area of maximum 1MB virtual address space is mapped into
+ * the L2 table and a handle for this table is returned to the user.
+ */
+struct mc_used_l2_table {
+ struct list_head list;
+
+ /** handle as communicated to user mode */
+ unsigned int handle;
+ unsigned int flags;
+
+ /** owner of this L2 table */
+ struct mc_instance *owner;
+
+ /** set describing where our L2 table is stored */
+ struct mc_l2_tables_set *set;
+
+ /** index into L2 table set */
+ unsigned int idx;
+
+ /** size of buffer */
+ unsigned int nr_of_pages;
+};
+
+#define MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_APP (1U << 0)
+#define MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_MC (1U << 1)
+
+
+/** MobiCore S-SIQ interrupt context data. */
+struct mc_ssiq_ctx {
+ /** S-SIQ interrupt counter */
+ atomic_t counter;
+};
+
+/** MobiCore Daemon context data. */
+struct mc_daemon_ctx {
+ /** event semaphore */
+ struct semaphore sem;
+ struct fasync_struct *async_queue;
+ /** event counter */
+ unsigned int ssiq_counter;
+};
+
+/** MobiCore Driver Kernel Module context data. */
+struct mc_drv_kmod_ctx {
+
+ /** ever incrementing counter */
+ atomic_t unique_counter;
+
+ /** S-SIQ interrupt context */
+ struct mc_ssiq_ctx ssiq_ctx;
+
+ /** MobiCore Daemon context */
+ struct mc_daemon_ctx daemon_ctx;
+
+ /** pointer to instance of daemon */
+ struct mc_instance *daemon_inst;
+
+ /** Backing store for L2 tables */
+ struct list_head mc_l2_tables_sets;
+
+ /** Bookkeeping for used L2 tables */
+ struct list_head mc_used_l2_tables;
+
+ /** semaphore to synchronize access to above lists */
+ struct semaphore wsm_l2_sem;
+};
+
+/** 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);
+
+#define MCDRV_DBG_ERROR(txt, ...) \
+ printk(KERN_ERR "mcDrvKMod [%d] %s() ### ERROR: " txt, \
+ task_pid_vnr(current), \
+ __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(txt, ...) \
+ printk(KERN_INFO "mcDrvKMod [%d on CPU%d] %s(): " txt, \
+ task_pid_vnr(current), \
+ raw_smp_processor_id(), \
+ __func__, \
+ ##__VA_ARGS__)
+
+#define MCDRV_DBG_WARN(txt, ...) \
+ printk(KERN_WARNING "mcDrvKMod [%d] %s() WARNING: " txt, \
+ task_pid_vnr(current), \
+ __func__, \
+ ##__VA_ARGS__)
+
+#define MCDRV_ASSERT(cond) \
+ do { \
+ if (unlikely(!(cond))) { \
+ panic("mcDrvKMod 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_DRV_KMOD_H_ */
+/** @} */
diff --git a/drivers/gud/mobicore_driver/mc_drv_module_android.h b/drivers/gud/mobicore_driver/mc_drv_module_android.h
new file mode 100644
index 0000000..319509f
--- /dev/null
+++ b/drivers/gud/mobicore_driver/mc_drv_module_android.h
@@ -0,0 +1,37 @@
+/**
+ * Header file of MobiCore Driver Kernel Module.
+ *
+ * @addtogroup MobiCore_Driver_Kernel_Module
+ * @{
+ * Android specific defines
+ * @file
+ *
+ * Android specific defines
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009-2012 -->
+ *
+ * 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.
+ */
+
+#ifndef _MC_DRV_MODULE_ANDROID_H_
+#define _MC_DRV_MODULE_ANDROID_H_
+
+/* Defines needed to identify the Daemon in Android systems
+ * For the full list see:
+ * platform_system_core/include/private/android_filesystem_config.h in the
+ * Android source tree
+ */
+/* traditional unix root user */
+#define AID_ROOT 0
+/* system server */
+#define AID_SYSTEM 1000
+/* access to misc storage */
+#define AID_MISC 9998
+#define AID_NOBODY 9999
+/* first app user */
+#define AID_APP 10000
+
+#endif /* _MC_DRV_MODULE_ANDROID_H_ */
+/** @} */
diff --git a/drivers/gud/mobicore_driver/mc_drv_module_fastcalls.h b/drivers/gud/mobicore_driver/mc_drv_module_fastcalls.h
new file mode 100644
index 0000000..d058043
--- /dev/null
+++ b/drivers/gud/mobicore_driver/mc_drv_module_fastcalls.h
@@ -0,0 +1,227 @@
+/**
+ * Header file of MobiCore Driver Kernel Module.
+ *
+ * @addtogroup MobiCore_Driver_Kernel_Module
+ * @{
+ * Internal structures of the McDrvModule
+ * @file
+ *
+ * MobiCore Fast Call interface
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009-2012 -->
+ *
+ * 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.
+ */
+
+#ifndef _MC_DRV_MODULE_FC_H_
+#define _MC_DRV_MODULE_FC_H_
+
+#include "mc_drv_module.h"
+
+/**
+ * MobiCore SMCs
+ */
+enum mc_smc_codes {
+ MC_SMC_N_YIELD = 0x3, /**< Yield to switch from NWd to SWd. */
+ MC_SMC_N_SIQ = 0x4 /**< SIQ to switch from NWd to SWd. */
+};
+
+/**
+ * MobiCore fast calls. See MCI documentation
+ */
+enum mc_fast_call_codes {
+ MC_FC_INIT = -1,
+ MC_FC_INFO = -2,
+ MC_FC_POWER = -3,
+ MC_FC_DUMP = -4,
+ MC_FC_NWD_TRACE = -31 /**< Mem trace setup fastcall */
+};
+
+/**
+ * return code for fast calls
+ */
+enum mc_fast_calls_result {
+ MC_FC_RET_OK = 0,
+ MC_FC_RET_ERR_INVALID = 1,
+ 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;
+};
+
+
+/** fast call S-Yield parameters */
+union mc_fc_s_yield {
+ union fc_generic as_generic;
+ struct {
+ uint32_t cmd;
+ uint32_t rfu[3];
+ } as_in;
+ struct {
+ uint32_t resp;
+ uint32_t ret;
+ uint32_t rfu[2];
+ } as_out;
+};
+
+
+/** fast call N-SIQ parameters */
+union mc_fc_nsiq {
+ union fc_generic as_generic;
+ struct {
+ uint32_t cmd;
+ uint32_t rfu[3];
+ } as_in;
+ struct {
+ uint32_t resp;
+ uint32_t ret;
+ uint32_t rfu[2];
+ } as_out;
+};
+
+
+/*----------------------------------------------------------------------------*/
+/**
+ * fast call to MobiCore
+ *
+ * @param fc_generic pointer to fast call data
+ */
+static inline void mc_fastcall(
+ union fc_generic *fc_generic
+)
+{
+ MCDRV_ASSERT(fc_generic != NULL);
+ /* We only expect to make smc calls on CPU0 otherwise something wrong
+ * will happen */
+ MCDRV_ASSERT(raw_smp_processor_id() == 0);
+ mb();
+#ifdef MC_SMC_FASTCALL
+ {
+ int ret = 0;
+ MCDRV_DBG("Going into SCM()");
+ ret = smc_fastcall((void *)fc_generic, sizeof(*fc_generic));
+ MCDRV_DBG("Coming from SCM, scm_call=%i, resp=%d/0x%x\n",
+ ret,
+ fc_generic->as_out.resp, fc_generic->as_out.resp);
+ }
+#else
+ {
+ /* SVC 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];
+
+ /* one of the famous preprocessor hacks to stingitize things.*/
+#define __STR2(x) #x
+#define __STR(x) __STR2(x)
+
+ /* compiler does not support certain instructions
+ "SMC": secure monitor call.*/
+#define ASM_ARM_SMC 0xE1600070
+ /* "BPKT": debugging breakpoint. We keep this, as is comes
+ quite handy for debugging. */
+#define ASM_ARM_BPKT 0xE1200070
+#define ASM_THUMB_BPKT 0xBE00
+
+
+ __asm__ volatile (
+ ".word " __STR(ASM_ARM_SMC) "\n"
+ : "+r"(reg0), "+r"(reg1), "+r"(reg2), "+r"(reg3)
+ );
+
+ /* 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
+}
+
+
+/*----------------------------------------------------------------------------*/
+/**
+ * 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;
+ } /* end switch( sret ) */
+ return ret;
+}
+
+#endif /* _MC_DRV_MODULE_FC_H_ */
+/** @} */
diff --git a/drivers/gud/mobicore_driver/mc_drv_module_linux_api.h b/drivers/gud/mobicore_driver/mc_drv_module_linux_api.h
new file mode 100644
index 0000000..b2a99f1
--- /dev/null
+++ b/drivers/gud/mobicore_driver/mc_drv_module_linux_api.h
@@ -0,0 +1,187 @@
+/**
+ * Header file of MobiCore Driver Kernel Module.
+ *
+ * @addtogroup MobiCore_Driver_Kernel_Module
+ * @{
+ * Wrapper for Linux API
+ * @file
+ *
+ * Some convenient wrappers for memory functions
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009-2012 -->
+ *
+ * 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.
+ */
+
+#ifndef _MC_DRV_MODULE_LINUX_API_H_
+#define _MC_DRV_MODULE_LINUX_API_H_
+
+#include <linux/version.h>
+#include <linux/miscdevice.h>
+#include <linux/interrupt.h>
+#include <linux/highmem.h>
+#include <linux/kthread.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <asm/sizes.h>
+#include <asm/pgtable.h>
+#include <linux/semaphore.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+
+
+/* make some nice types */
+#if !defined(TRUE)
+#define TRUE (1 == 1)
+#endif
+
+#if !defined(FALSE)
+#define FALSE (1 != 1)
+#endif
+
+
+/* Linux GCC modifiers */
+#if !defined(__init)
+#warning "missing definition: __init"
+/* define a dummy */
+#define __init
+#endif
+
+
+#if !defined(__exit)
+#warning "missing definition: __exit"
+/* define a dummy */
+#define __exit
+#endif
+
+
+#if !defined(__must_check)
+#warning "missing definition: __must_check"
+/* define a dummy */
+#define __must_check
+#endif
+
+
+#if !defined(__user)
+#warning "missing definition: __user"
+/* define a dummy */
+#define __user
+#endif
+
+#define INVALID_ORDER ((unsigned int)(-1))
+
+/*----------------------------------------------------------------------------*/
+/* get start address of the 4 KiB page where the given addres is located in. */
+static inline void *get_page_start(
+ void *addr
+)
+{
+ return (void *)(((unsigned long)(addr)) & PAGE_MASK);
+}
+
+/*----------------------------------------------------------------------------*/
+/* get offset into the 4 KiB page where the given addres is located in. */
+static inline unsigned int get_offset_in_page(
+ void *addr
+)
+{
+ return (unsigned int)(((unsigned long)(addr)) & (~PAGE_MASK));
+}
+
+/*----------------------------------------------------------------------------*/
+/* get number of pages for a given buffer. */
+static inline unsigned int get_nr_of_pages_for_buffer(
+ void *addr_start, /* may be null */
+ unsigned int len
+)
+{
+ /* calculate used number of pages. Example:
+ offset+size newSize+PAGE_SIZE-1 nr_of_pages
+ 0 4095 0
+ 1 4096 1
+ 4095 8190 1
+ 4096 8191 1
+ 4097 8192 2 */
+
+ return (get_offset_in_page(addr_start) + len + PAGE_SIZE-1) / PAGE_SIZE;
+}
+
+
+/*----------------------------------------------------------------------------*/
+/**
+ * convert a given size to page order, which is equivalent to finding log_2(x).
+ * The maximum for order was 5 in Linux 2.0 corresponding to 32 pages.
+ * Later versions allow 9 corresponding to 512 pages, which is 2 MB on
+ * most platforms). Anyway, the bigger order is, the more likely it is
+ * that the allocation will fail.
+ * Size 0 1 4097 8193 12289 24577 28673 40961 61441
+ * Pages - 1 2 3 4 7 8 15 16
+ * Order INVALID_ORDER 0 1 1 2 2 3 3 4
+ *
+ * @param size
+ * @return order
+ */
+static inline unsigned int size_to_order(
+ unsigned int size
+)
+{
+ unsigned int order = INVALID_ORDER;
+
+ if (size != 0) {
+ /* ARMv5 as a CLZ instruction which count the leading zeros of
+ the binary representation of a value. It return a value
+ between 0 and 32.
+ Value 0 1 2 3 4 5 6 7 8 9 10 ...
+ CLZ 32 31 30 30 29 29 29 29 28 28 28 ...
+
+ We have excluded Size==0 before, so this is safe. */
+ order = __builtin_clz(
+ get_nr_of_pages_for_buffer(NULL, size));
+
+ /* there is a size overflow in get_nr_of_pages_for_buffer when
+ * the size is too large */
+ if (unlikely(order > 31))
+ return INVALID_ORDER;
+ order = 31 - order;
+
+ /* above algorithm rounds down: clz(5)=2 instead of 3 */
+ /* quick correction to fix it: */
+ if (((1<<order)*PAGE_SIZE) < size)
+ order++;
+ }
+ return order;
+}
+
+/* magic linux macro */
+#if !defined(list_for_each_entry)
+/* stop compiler */
+#error "missing macro: list_for_each_entry()"
+/* define a dummy */
+#define list_for_each_entry(a, b, c) if (0)
+#endif
+
+/*----------------------------------------------------------------------------*/
+/* return the page frame number of an address */
+static inline unsigned int addr_to_pfn(
+ void *addr
+)
+{
+ /* there is no real API for this */
+ return ((unsigned int)(addr)) >> PAGE_SHIFT;
+}
+
+
+/*----------------------------------------------------------------------------*/
+/* return the address of a page frame number */
+static inline void *pfn_to_addr(
+ unsigned int pfn
+)
+{
+ /* there is no real API for this */
+ return (void *)(pfn << PAGE_SHIFT);
+}
+
+#endif /* _MC_DRV_MODULE_LINUX_API_H_ */
+/** @} */
diff --git a/drivers/gud/mobicore_driver/platforms/MSM8960_SURF_STD/platform.h b/drivers/gud/mobicore_driver/platforms/MSM8960_SURF_STD/platform.h
new file mode 100644
index 0000000..7034cb0
--- /dev/null
+++ b/drivers/gud/mobicore_driver/platforms/MSM8960_SURF_STD/platform.h
@@ -0,0 +1,50 @@
+/**
+ * Header file of MobiCore Driver Kernel Module Platform
+ * specific structures
+ *
+ * @addtogroup MobiCore_Driver_Kernel_Module
+ * @{
+ * Internal structures of the McDrvModule
+ * @file
+ *
+ * Header file the MobiCore Driver Kernel Module,
+ * its internal structures and defines.
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009-2012 -->
+ *
+ * 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.
+ */
+
+#ifndef _MC_DRV_PLATFORM_H_
+#define _MC_DRV_PLATFORM_H_
+
+/** MobiCore Interrupt for Qualcomm */
+#define MC_INTR_SSIQ 218
+
+/** Use SMC for fastcalls */
+#define MC_SMC_FASTCALL
+
+
+/*--------------- Implementation -------------- */
+#include <mach/scm.h>
+/* from following file */
+#define SCM_SVC_MOBICORE 250
+#define SCM_CMD_MOBICORE 1
+
+extern int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len,
+ void *resp_buf, size_t resp_len);
+
+static inline int smc_fastcall(void *fc_generic, size_t size)
+{
+ return scm_call(SCM_SVC_MOBICORE, SCM_CMD_MOBICORE,
+ fc_generic, size,
+ fc_generic, size);
+}
+
+/** Enable mobicore mem traces */
+#define MC_MEM_TRACES
+
+#endif /* _MC_DRV_PLATFORM_H_ */
+/** @} */
diff --git a/drivers/gud/mobicore_driver/public/mc_drv_module_api.h b/drivers/gud/mobicore_driver/public/mc_drv_module_api.h
new file mode 100644
index 0000000..59366f3
--- /dev/null
+++ b/drivers/gud/mobicore_driver/public/mc_drv_module_api.h
@@ -0,0 +1,311 @@
+/** @addtogroup MCD_MCDIMPL_KMOD_API Mobicore Driver Module API
+ * @ingroup MCD_MCDIMPL_KMOD
+ * @{
+ * Interface to Mobicore Driver Kernel Module.
+ * @file
+ *
+ * <h2>Introduction</h2>
+ * The MobiCore 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 L2 tables for
+ * given virtual memory are also supported. IRQ functionallity 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
+ * device "/dev/mobicore" has been opened.
+ * The MobiCore Driver Kernel Module must be installed via
+ * "insmod mcDrvModule.ko".
+ *
+ *
+ * <h2>Version history</h2>
+ * <table class="customtab">
+ * <tr><td width="100px"><b>Date</b></td><td width="80px"><b>Version</b></td>
+ * <td><b>Changes</b></td></tr>
+ * <tr><td>2010-05-25</td><td>0.1</td><td>Initial Release</td></tr>
+ * </table>
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2010-2012 -->
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _MC_DRV_MODULEAPI_H_
+#define _MC_DRV_MODULEAPI_H_
+
+#include "version.h"
+
+#define MC_DRV_MOD_DEVNODE "mobicore"
+#define MC_DRV_MOD_DEVNODE_FULLPATH "/dev/" MC_DRV_MOD_DEVNODE
+
+/**
+ * Data exchange structure of the MC_DRV_MODULE_INIT ioctl command.
+ * INIT request data to SWD
+ */
+union mc_ioctl_init_params {
+ struct {
+ /** base address of mci buffer 4KB align */
+ uint32_t base;
+ /** notification buffer start/length [16:16] [start, length] */
+ uint32_t nq_offset;
+ /** 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;
+ } in;
+ struct {
+ /* nothing */
+ } out;
+};
+
+
+/**
+ * Data exchange structure of the MC_DRV_MODULE_INFO ioctl command.
+ * INFO request data to the SWD
+ */
+union mc_ioctl_info_params {
+ struct {
+ uint32_t ext_info_id; /**< extended info ID */
+ } in;
+ struct {
+ uint32_t state; /**< state */
+ uint32_t ext_info; /**< extended info */
+ } out;
+};
+
+/**
+ * Mmap allocates and maps contiguous memory into a process.
+ * We use the third parameter, void *offset, to distinguish between some cases
+ * offset = MC_DRV_KMOD_MMAP_WSM usual operation, pages are registered in
+ device structure and freed later.
+ * offset = MC_DRV_KMOD_MMAP_MCI get Instance of MCI, allocates or mmaps
+ the MCI to daemon
+ * offset = MC_DRV_KMOD_MMAP_PERSISTENTWSM special operation, without
+ registration of pages
+ *
+ * In mmap(), the offset specifies which of several device I/O pages is
+ * requested. Linux only transfers the page number, i.e. the upper 20 bits to
+ * kernel module. Therefore we define our special offsets as multiples of page
+ * size.
+ */
+enum mc_mmap_memtype {
+ MC_DRV_KMOD_MMAP_WSM = 0,
+ MC_DRV_KMOD_MMAP_MCI = 4096,
+ MC_DRV_KMOD_MMAP_PERSISTENTWSM = 8192
+};
+
+struct mc_mmap_resp {
+ uint32_t handle; /**< WSN handle */
+ uint32_t phys_addr; /**< physical address of WSM (or NULL) */
+ bool is_reused; /**< if WSM memory was reused, or new allocated */
+};
+
+/**
+ * Data exchange structure of the MC_DRV_KMOD_IOCTL_FREE ioctl command.
+ */
+union mc_ioctl_free_params {
+ struct {
+ uint32_t handle; /**< driver handle */
+ uint32_t pid; /**< process id */
+ } in;
+ struct {
+ /* nothing */
+ } out;
+};
+
+
+/**
+ * Data exchange structure of the MC_DRV_KMOD_IOCTL_APP_REGISTER_WSM_L2 command.
+ *
+ * Allocates a physical L2 table and maps the buffer into this page.
+ * Returns the physical address of the L2 table.
+ * The page alignment will be created and the appropriated pSize and pOffsetL2
+ * will be modified to the used values.
+ */
+union mc_ioctl_app_reg_wsm_l2_params {
+ struct {
+ uint32_t buffer; /**< base address of the virtual address */
+ uint32_t len; /**< size of the virtual address space */
+ uint32_t pid; /**< process id */
+ } in;
+ struct {
+ uint32_t handle; /**< driver handle for locked memory */
+ uint32_t phys_wsm_l2_table; /* physical address of the L2 table */
+ } out;
+};
+
+
+/**
+ * Data exchange structure of the MC_DRV_KMOD_IOCTL_APP_UNREGISTER_WSM_L2
+ * command.
+ */
+struct mc_ioctl_app_unreg_wsm_l2_params {
+ struct {
+ uint32_t handle; /**< driver handle for locked memory */
+ uint32_t pid; /**< process id */
+ } in;
+ struct {
+ /* nothing */
+ } out;
+};
+
+
+/**
+ * Data exchange structure of the MC_DRV_KMOD_IOCTL_DAEMON_LOCK_WSM_L2 command.
+ */
+struct mc_ioctl_daemon_lock_wsm_l2_params {
+ struct {
+ uint32_t handle; /**< driver handle for locked memory */
+ } in;
+ struct {
+ uint32_t phys_wsm_l2_table;
+ } out;
+};
+
+
+/**
+ * Data exchange structure of the MC_DRV_KMOD_IOCTL_DAEMON_UNLOCK_WSM_L2
+ * command.
+ */
+struct mc_ioctl_daemon_unlock_wsm_l2_params {
+ struct {
+ uint32_t handle; /**< driver handle for locked memory */
+ } in;
+ struct {
+ /* nothing */
+ } out;
+};
+
+/**
+ * Data exchange structure of the MC_DRV_MODULE_FC_EXECUTE ioctl command.
+ */
+union mc_ioctl_fc_execute_params {
+ struct {
+ /**< base address of mobicore binary */
+ uint32_t phys_start_addr;
+ /**< length of DDR area */
+ uint32_t length;
+ } in;
+ struct {
+ /* nothing */
+ } out;
+};
+
+/**
+ * Data exchange structure of the MC_DRV_MODULE_GET_VERSION ioctl command.
+ */
+struct mc_ioctl_get_version_params {
+ struct {
+ uint32_t kernel_module_version;
+ } out;
+};
+
+/* @defgroup Mobicore_Driver_Kernel_Module_Interface IOCTL */
+
+
+
+
+/* TODO: use IOCTL macros like _IOWR. See Documentation/ioctl/ioctl-number.txt,
+ Documentation/ioctl/ioctl-decoding.txt */
+/**
+ * defines for the ioctl mobicore driver module function call from user space.
+ */
+enum mc_kmod_ioctl {
+
+ /*
+ * get detailed MobiCore Status
+ */
+ MC_DRV_KMOD_IOCTL_DUMP_STATUS = 200,
+
+ /*
+ * initialize MobiCore
+ */
+ MC_DRV_KMOD_IOCTL_FC_INIT = 201,
+
+ /*
+ * get MobiCore status
+ */
+ MC_DRV_KMOD_IOCTL_FC_INFO = 202,
+
+ /**
+ * ioctl parameter to send the YIELD command to the SWD.
+ * Only possible in Privileged Mode.
+ * ioctl(fd, MC_DRV_MODULE_YIELD)
+ */
+ MC_DRV_KMOD_IOCTL_FC_YIELD = 203,
+ /**
+ * ioctl parameter to send the NSIQ signal to the SWD.
+ * Only possible in Privileged Mode
+ * ioctl(fd, MC_DRV_MODULE_NSIQ)
+ */
+ MC_DRV_KMOD_IOCTL_FC_NSIQ = 204,
+ /**
+ * ioctl parameter to tzbsp to start Mobicore binary from DDR.
+ * Only possible in Privileged Mode
+ * ioctl(fd, MC_DRV_KMOD_IOCTL_FC_EXECUTE)
+ */
+ MC_DRV_KMOD_IOCTL_FC_EXECUTE = 205,
+
+ /**
+ * 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 beeing of
+ * type long address
+ */
+ MC_DRV_KMOD_IOCTL_FREE = 218,
+
+ /**
+ * Creates a L2 Table of the given base address and the size of the
+ * data.
+ * Parameter: mc_ioctl_app_reg_wsm_l2_params
+ */
+ MC_DRV_KMOD_IOCTL_APP_REGISTER_WSM_L2 = 220,
+
+ /**
+ * Frees the L2 table created by a MC_DRV_KMOD_IOCTL_APP_REGISTER_WSM_L2
+ * ioctl.
+ * Parameter: mc_ioctl_app_unreg_wsm_l2_params
+ */
+ MC_DRV_KMOD_IOCTL_APP_UNREGISTER_WSM_L2 = 221,
+
+
+ /* TODO: comment this. */
+ MC_DRV_KMOD_IOCTL_DAEMON_LOCK_WSM_L2 = 222,
+ MC_DRV_KMOD_IOCTL_DAEMON_UNLOCK_WSM_L2 = 223,
+
+ /**
+ * Return kernel driver version.
+ * Parameter: mc_ioctl_get_version_params
+ */
+ MC_DRV_KMOD_IOCTL_GET_VERSION = 224,
+};
+
+
+#endif /* _MC_DRV_MODULEAPI_H_ */
+/** @} */
diff --git a/drivers/gud/mobicore_driver/public/mc_kernel_api.h b/drivers/gud/mobicore_driver/public/mc_kernel_api.h
new file mode 100644
index 0000000..fdfc618
--- /dev/null
+++ b/drivers/gud/mobicore_driver/public/mc_kernel_api.h
@@ -0,0 +1,100 @@
+/** @addtogroup MCD_MCDIMPL_KMOD_KAPI Mobicore Driver Module API inside Kernel.
+ * @ingroup MCD_MCDIMPL_KMOD
+ * @{
+ * Interface to Mobicore Driver Kernel Module inside Kernel.
+ * @file
+ *
+ * Interface to be used by module MobiCoreKernelAPI.
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2010-2012 -->
+ *
+ * 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.
+ */
+
+#ifndef _MOBICORE_KERNELMODULE_API_H_
+#define _MOBICORE_KERNELMODULE_API_H_
+
+struct mc_instance;
+
+/**
+ * Initialize a new mobicore API instance object
+ *
+ * @return Instance or NULL if no allocation was possible.
+ */
+struct mc_instance *mobicore_open(
+ void
+);
+
+/**
+ * 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
+);
+
+/**
+ * Free a WSM buffer allocated with mobicore_allocate_wsm
+ * @param instance
+ * @param handle handle of the buffer
+ *
+ * @return 0 if no error
+ *
+ */
+int mobicore_allocate_wsm(
+ struct mc_instance *instance,
+ unsigned long requested_size,
+ uint32_t *handle,
+ void **kernel_virt_addr,
+ void **phys_addr
+);
+
+/**
+ * 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(
+ struct mc_instance *instance,
+ uint32_t handle
+);
+
+/**
+ * Map a virtual memory buffer structure to Mobicore
+ * @param instance
+ * @param addr address of the buffer(NB it must be kernel virtual!)
+ * @param len buffer length
+ * @param handle pointer to handle
+ * @param phys_wsm_l2_table pointer to physical L2 table(?)
+ *
+ * @return 0 if no error
+ *
+ */
+int mobicore_map_vmem(
+ struct mc_instance *instance,
+ void *addr,
+ uint32_t len,
+ uint32_t *handle,
+ void **phys_wsm_l2_table
+);
+
+/**
+ * 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
+);
+#endif /* _MOBICORE_KERNELMODULE_API_H_ */
+/** @} */
diff --git a/drivers/gud/mobicore_driver/public/version.h b/drivers/gud/mobicore_driver/public/version.h
new file mode 100644
index 0000000..9b2dbca
--- /dev/null
+++ b/drivers/gud/mobicore_driver/public/version.h
@@ -0,0 +1,36 @@
+/** @addtogroup MCD_MCDIMPL_KMOD
+ * @{
+ * <!-- Copyright Giesecke & Devrient GmbH 2010-2012 -->
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _MC_DRV_VERSION_H_
+#define _MC_DRV_VERSION_H_
+
+#define MCDRVMODULEAPI_VERSION_MAJOR 0
+#define MCDRVMODULEAPI_VERSION_MINOR 1
+
+#endif /* _MC_DRV_VERSION_H_ */
diff --git a/drivers/gud/mobicore_kernelapi/clientlib.c b/drivers/gud/mobicore_kernelapi/clientlib.c
new file mode 100644
index 0000000..13826f2
--- /dev/null
+++ b/drivers/gud/mobicore_kernelapi/clientlib.c
@@ -0,0 +1,1093 @@
+/**
+ * MobiCore KernelApi module
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009-2012 -->
+ *
+ * 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.
+ */
+#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 "device.h"
+#include "session.h"
+
+/* device list */
+LIST_HEAD(devices);
+
+/*----------------------------------------------------------------------------*/
+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 */
+ list_for_each(pos, &devices) {
+ tmp = list_entry(pos, struct mcore_device_t, list);
+ if (tmp->device_id == device_id)
+ return tmp;
+ }
+ return NULL;
+}
+
+
+/*----------------------------------------------------------------------------*/
+static void add_device(
+ struct mcore_device_t *device
+) {
+ list_add_tail(&(device->list), &devices);
+}
+
+
+/*----------------------------------------------------------------------------*/
+static bool remove_device(
+ uint32_t device_id
+) {
+ struct mcore_device_t *tmp;
+ struct list_head *pos, *q;
+
+ list_for_each_safe(pos, q, &devices) {
+ tmp = list_entry(pos, struct mcore_device_t, list);
+ if (tmp->device_id == device_id) {
+ list_del(pos);
+ mcore_device_cleanup(tmp);
+ return true;
+ }
+ }
+ return false;
+}
+
+
+/*----------------------------------------------------------------------------*/
+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("===%s()===", __func__);
+
+ /* Enter critical section */
+
+ do {
+ struct mcore_device_t *device = resolve_device_id(device_id);
+ if (device != NULL) {
+ MCDRV_DBG_ERROR("Device %d already opened", device_id);
+ mc_result = MC_DRV_ERR_INVALID_OPERATION;
+ break;
+ }
+
+ /* Open new connection to device */
+ dev_con = connection_new();
+ if (!connection_connect(dev_con, MC_DAEMON_PID)) {
+ MCDRV_DBG_ERROR(
+ "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 = {
+ /* C++ does not support C99 designated initializers */
+ /* .header = */ {
+ /* .command_id = */ MC_DRV_CMD_OPEN_DEVICE
+ },
+ /* .payload = */ {
+ /* .device_id = */ 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("CMD_OPEN_DEVICE writeCmd failed "
+ "ret=%d", len);
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ break;
+ }
+
+ struct mc_drv_response_header_t rsp_header;
+ len = connection_read_datablock(
+ dev_con,
+ &rsp_header,
+ sizeof(rsp_header));
+ if (len != sizeof(rsp_header)) {
+ MCDRV_DBG_ERROR("CMD_OPEN_DEVICE readRsp failed "
+ "ret=%d", len);
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ break;
+ }
+ if (rsp_header.response_id != MC_DRV_RSP_OK) {
+ MCDRV_DBG_ERROR("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 (!mcore_device_open(device, MC_DRV_MOD_DEVNODE_FULLPATH)) {
+ mcore_device_cleanup(device);
+ MCDRV_DBG_ERROR("could not open device file: %s",
+ MC_DRV_MOD_DEVNODE_FULLPATH);
+ mc_result = MC_DRV_ERR_INVALID_DEVICE_FILE;
+ break;
+ }
+
+ add_device(device);
+
+ } while (false);
+
+ if (mc_result != MC_DRV_OK)
+ connection_cleanup(dev_con);
+
+ /* Exit critical section */
+
+ 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("===%s()===", __func__);
+
+ /* Enter critical section */
+ do {
+ struct mcore_device_t *device = resolve_device_id(device_id);
+ if (device == NULL) {
+ MCDRV_DBG_ERROR("Device not found");
+ mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
+ break;
+ }
+ struct connection *dev_con = device->connection;
+
+ /* Return if not all sessions have been closed */
+ if (mcore_device_has_sessions(device)) {
+ MCDRV_DBG_ERROR("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 = {
+ /* C++ does not support C99 designated initializers */
+ /* .header = */ {
+ /* .command_id = */ 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("CMD_CLOSE_DEVICE writeCmd failed "
+ "ret=%d", len);
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ }
+
+ struct mc_drv_response_header_t rsp_header;
+ len = connection_read_datablock(
+ dev_con,
+ &rsp_header,
+ sizeof(rsp_header));
+ if (len != sizeof(rsp_header)) {
+ MCDRV_DBG_ERROR("CMD_CLOSE_DEVICE readResp failed "
+ " ret=%d", len);
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ break;
+ }
+
+ if (rsp_header.response_id != MC_DRV_RSP_OK) {
+ MCDRV_DBG_ERROR("CMD_CLOSE_DEVICE failed, respId=%d",
+ rsp_header.response_id);
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ break;
+ }
+
+ remove_device(device_id);
+
+ } while (false);
+
+ /* Exit critical section */
+
+ 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 len
+) {
+ enum mc_result mc_result = MC_DRV_OK;
+
+ MCDRV_DBG_VERBOSE("===%s()===", __func__);
+
+ /* Enter critical section */
+
+ do {
+ if (session == NULL) {
+ MCDRV_DBG_ERROR("Session is null");
+ mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+ break;
+ }
+ if (uuid == NULL) {
+ MCDRV_DBG_ERROR("UUID is null");
+ mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+ break;
+ }
+ if (tci == NULL) {
+ MCDRV_DBG_ERROR("TCI is null");
+ mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+ break;
+ }
+ if (len > MC_MAX_TCI_LEN) {
+ MCDRV_DBG_ERROR("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("Device not found");
+ mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
+ break;
+ }
+ struct connection *dev_con = device->connection;
+
+ /* Get the physical address of the given TCI */
+ struct wsm *wsm =
+ mcore_device_find_contiguous_wsm(device, tci);
+ if (wsm == NULL) {
+ MCDRV_DBG_ERROR("Could not resolve TCI phy address ");
+ mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+ break;
+ }
+
+ if (wsm->len < len) {
+ MCDRV_DBG_ERROR("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 cmdOpenSession = {
+ /* C++ does not support C99 designated initializers */
+ /* .header = */ {
+ /* .command_id = */ MC_DRV_CMD_OPEN_SESSION
+ },
+ /* .payload = */ {
+ /* .device_id = */ session->device_id,
+ /* .uuid = */ *uuid,
+ /* .tci = */ (uint32_t)wsm->phys_addr,
+ /* .len = */ len
+ }
+ };
+
+ /* Transmit command data */
+
+ int len = connection_write_data(
+ dev_con,
+ &cmdOpenSession,
+ sizeof(cmdOpenSession));
+ if (len != sizeof(cmdOpenSession)) {
+ MCDRV_DBG_ERROR("CMD_OPEN_SESSION writeData failed "
+ "ret=%d", len);
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ break;
+ }
+
+ /* Read command response */
+
+ /* read header first */
+ struct mc_drv_response_header_t rsp_header;
+ len = connection_read_datablock(
+ dev_con,
+ &rsp_header,
+ sizeof(rsp_header));
+ if (len != sizeof(rsp_header)) {
+ MCDRV_DBG_ERROR("CMD_OPEN_SESSION readResp failed "
+ " ret=%d", len);
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ break;
+ }
+
+ if (rsp_header.response_id != MC_DRV_RSP_OK) {
+ MCDRV_DBG_ERROR("CMD_OPEN_SESSION failed, respId=%d",
+ rsp_header.response_id);
+ switch (rsp_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;
+ }
+
+ /* read payload */
+ struct mc_drv_rsp_open_session_payload_t
+ rsp_open_session_payload;
+ len = connection_read_datablock(
+ dev_con,
+ &rsp_open_session_payload,
+ sizeof(rsp_open_session_payload));
+ if (len != sizeof(rsp_open_session_payload)) {
+ MCDRV_DBG_ERROR("CMD_OPEN_SESSION readPayload failed "
+ "ret=%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();
+ /*TODO: no real need to connect here? */
+ if (!connection_connect(session_connection, MC_DAEMON_PID)) {
+ MCDRV_DBG_ERROR(
+ "Could not setup netlink connection to PID %u",
+ MC_DAEMON_PID);
+ connection_cleanup(session_connection);
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ break;
+ }
+
+ /*TODO CONTINOUE HERE !!!! FIX RW RETURN HANDLING!!!! */
+
+ /* Write command to use channel for notifications */
+ struct mc_drv_cmd_nqconnect_t cmd_nqconnect = {
+ /* C++ does not support C99 designated initializers */
+ /* .header = */ {
+ /* .command_id = */ MC_DRV_CMD_NQ_CONNECT
+ },
+ /* .payload = */ {
+ /* .device_id = */ session->device_id,
+ /* .session_id = */ session->session_id,
+ /* .device_session_id = */
+ rsp_open_session_payload.device_session_id,
+ /* .session_magic = */
+ rsp_open_session_payload.session_magic
+ }
+ };
+ connection_write_data(session_connection,
+ &cmd_nqconnect,
+ sizeof(cmd_nqconnect));
+
+ /* Read command response, header first */
+ len = connection_read_datablock(
+ session_connection,
+ &rsp_header,
+ sizeof(rsp_header));
+ if (len != sizeof(rsp_header)) {
+ MCDRV_DBG_ERROR("CMD_NQ_CONNECT readRsp failed "
+ "ret=%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("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 */
+ mcore_device_create_new_session(
+ device,
+ session->session_id,
+ session_connection);
+
+ } while (false);
+
+ /* Exit critical section */
+
+ 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("===%s()===", __func__);
+
+ /* Enter critical section */
+
+ do {
+ if (session == NULL) {
+ MCDRV_DBG_ERROR("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("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("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 = {
+ /* C++ does not support C99 designated initializers */
+ /* .header = */ {
+ /* .command_id = */ MC_DRV_CMD_CLOSE_SESSION
+ },
+ /* .payload = */ {
+ /* .session_id = */ 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;
+ int len = connection_read_datablock(
+ dev_con,
+ &rsp_header,
+ sizeof(rsp_header));
+ if (len != sizeof(rsp_header)) {
+ MCDRV_DBG_ERROR("CMD_CLOSE_SESSION readRsp failed "
+ "ret=%d", len);
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ break;
+ }
+
+ if (rsp_header.response_id != MC_DRV_RSP_OK) {
+ MCDRV_DBG_ERROR("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);
+
+ /* Exit critical section */
+
+ 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("===%s()===", __func__);
+
+ do {
+ if (session == NULL) {
+ MCDRV_DBG_ERROR("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("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("Session not found");
+ mc_result = MC_DRV_ERR_UNKNOWN_SESSION;
+ break;
+ }
+
+ struct mc_drv_cmd_notify_t cmd_notify = {
+ /* C++ does not support C99 designated initializers */
+ /* .header = */ {
+ /* .command_id = */ MC_DRV_CMD_NOTIFY
+ },
+ /* .payload = */ {
+ /* .session_id = */ 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("===%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("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("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;
+ 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("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(
+ "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("readNq 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("===%s()===", __func__);
+
+ /* Enter critical section */
+
+ do {
+ struct mcore_device_t *device = resolve_device_id(device_id);
+ if (device == NULL) {
+ MCDRV_DBG_ERROR("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("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);
+
+ /* Exit critical section */
+
+ 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("===%s()===", __func__);
+
+ /* Enter critical section */
+
+ do {
+
+ /* Get the device associated wit the given session */
+ device = resolve_device_id(device_id);
+ if (device == NULL) {
+ MCDRV_DBG_ERROR("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("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("Free of virtual address failed");
+ mc_result = MC_DRV_ERR_FREE_MEMORY_FAILED;
+ break;
+ }
+ mc_result = MC_DRV_OK;
+
+ } while (false);
+
+ /* Exit critical section */
+
+ 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("===%s()===", __func__);
+
+ /* Enter critical section */
+
+ do {
+ if (session_handle == NULL) {
+ MCDRV_DBG_ERROR("session_handle is null");
+ mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+ break;
+ }
+ if (map_info == NULL) {
+ MCDRV_DBG_ERROR("map_info is null");
+ mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+ break;
+ }
+ if (buf == NULL) {
+ MCDRV_DBG_ERROR("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("Device not found");
+ mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
+ break;
+ }
+ struct connection *dev_con = device->connection;
+
+ /* Get session */
+ struct session *session =
+ mcore_device_resolve_session_id(device,
+ session_handle->session_id);
+ if (session == NULL) {
+ MCDRV_DBG_ERROR("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("Error mapping bulk buffer");
+ mc_result = MC_DRV_ERR_BULK_MAPPING;
+ break;
+ }
+
+ /* Prepare map command */
+ struct mc_drv_cmd_map_bulk_mem_t mc_drv_cmd_map_bulk_mem = {
+ /* C++ does not support C99 designated initializers */
+ /* .header = */ {
+ /* .command_id = */ MC_DRV_CMD_MAP_BULK_BUF
+ },
+ /* .payload = */ {
+ /* .session_id = */ session->session_id,
+ /* .phys_addr_l2; = */
+ (uint32_t)bulk_buf->phys_addr_wsm_l2,
+ /* .offset_payload = */
+ (uint32_t)(bulk_buf->virt_addr) & 0xFFF,
+ /* .len_bulk_mem = */ 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;
+ int len = connection_read_datablock(
+ dev_con,
+ &rsp_header,
+ sizeof(rsp_header));
+ if (len != sizeof(rsp_header)) {
+ MCDRV_DBG_ERROR("CMD_MAP_BULK_BUF readRsp failed, "
+ "ret=%d", len);
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ break;
+ }
+
+ if (rsp_header.response_id != MC_DRV_RSP_OK) {
+ MCDRV_DBG_ERROR("CMD_MAP_BULK_BUF failed, respId=%d",
+ rsp_header.response_id);
+ /* REV We ignore Daemon Error code because client cannot
+ handle it anyhow. */
+ 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("Unregistering of bulk memory"
+ "from Kernel Module failed");
+ }
+ break;
+ }
+
+ struct mc_drv_rsp_map_bulk_mem_payload_t
+ 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 =
+ (void *)(rsp_map_bulk_mem_payload.secure_virtual_adr);
+ map_info->secure_virt_len = buf_len;
+ mc_result = MC_DRV_OK;
+
+ } while (false);
+
+ /* Exit critical section */
+
+ 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("===%s()===", __func__);
+
+ /* Enter critical section */
+
+ do {
+ if (session_handle == NULL) {
+ MCDRV_DBG_ERROR("session_handle is null");
+ mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+ break;
+ }
+ if (map_info == NULL) {
+ MCDRV_DBG_ERROR("map_info is null");
+ mc_result = MC_DRV_ERR_INVALID_PARAMETER;
+ break;
+ }
+ if (buf == NULL) {
+ MCDRV_DBG_ERROR("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("Device not found");
+ mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
+ break;
+ }
+ struct connection *dev_con = device->connection;
+
+ /* Get session */
+ struct session *session =
+ mcore_device_resolve_session_id(device,
+ session_handle->session_id);
+ if (session == NULL) {
+ MCDRV_DBG_ERROR("Session not found");
+ mc_result = MC_DRV_ERR_UNKNOWN_SESSION;
+ break;
+ }
+
+ /* Prepare unmap command */
+ struct mc_drv_cmd_unmap_bulk_mem_t cmd_unmap_bulk_mem = {
+ /* .header = */ {
+ /* .command_id = */
+ MC_DRV_CMD_UNMAP_BULK_BUF
+ },
+ /* .payload = */ {
+ /* .session_id = */ session->session_id,
+ /* .secure_virtual_adr = */
+ (uint32_t)(map_info->secure_virt_addr),
+ /* .len_bulk_mem =
+ 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;
+ int len = connection_read_datablock(
+ dev_con,
+ &rsp_header,
+ sizeof(rsp_header));
+ if (len != sizeof(rsp_header)) {
+ MCDRV_DBG_ERROR("CMD_UNMAP_BULK_BUF readRsp failed, "
+ "ret=%d", len);
+ mc_result = MC_DRV_ERR_DAEMON_UNREACHABLE;
+ break;
+ }
+
+ if (rsp_header.response_id != MC_DRV_RSP_OK) {
+ MCDRV_DBG_ERROR("CMD_UNMAP_BULK_BUF failed, respId=%d",
+ rsp_header.response_id);
+ /* REV We ignore Daemon Error code because client
+ cannot handle it anyhow. */
+ 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));
+
+ /* REV axh: what about check the 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("Unregistering of bulk memory from "
+ "Kernel Module failed");
+ mc_result = MC_DRV_ERR_BULK_UNMAPPING;
+ break;
+ }
+
+ mc_result = MC_DRV_OK;
+
+ } while (false);
+
+ /* Exit critical section */
+
+ 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("===%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("Device not found");
+ mc_result = MC_DRV_ERR_UNKNOWN_DEVICE;
+ break;
+ }
+
+ /* Get session */
+ struct session *nqsession =
+ mcore_device_resolve_session_id(device, session->session_id);
+ if (nqsession == NULL) {
+ MCDRV_DBG_ERROR("Session not found");
+ mc_result = MC_DRV_ERR_UNKNOWN_SESSION;
+ break;
+ }
+
+ /* get session error code from session */
+ *last_error = session_get_last_err(nqsession);
+
+ } while (false);
+
+ return mc_result;
+}
+EXPORT_SYMBOL(mc_get_session_error_code);
+
+/*----------------------------------------------------------------------------*/
+enum mc_result mc_driver_ctrl(
+ enum mc_driver_ctrl param,
+ uint8_t *data,
+ uint32_t len
+) {
+ MCDRV_DBG_WARN("not implemented");
+ return MC_DRV_ERR_NOT_IMPLEMENTED;
+}
+EXPORT_SYMBOL(mc_driver_ctrl);
+
+/*----------------------------------------------------------------------------*/
+enum mc_result mc_manage(
+ uint32_t device_id,
+ uint8_t *data,
+ uint32_t len
+) {
+ MCDRV_DBG_WARN("not implemented");
+ return MC_DRV_ERR_NOT_IMPLEMENTED;
+}
+EXPORT_SYMBOL(mc_manage);
+
diff --git a/drivers/gud/mobicore_kernelapi/common.h b/drivers/gud/mobicore_kernelapi/common.h
new file mode 100644
index 0000000..2a73474
--- /dev/null
+++ b/drivers/gud/mobicore_kernelapi/common.h
@@ -0,0 +1,97 @@
+/**
+ *
+ * Common data types
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2012 -->
+ *
+ * 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.
+ */
+#ifndef COMMON_H
+#define 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)
+
+#define MCDRV_ERROR(txt, ...) \
+ printk(KERN_ERR "mcKernelApi %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(txt, ...) \
+ printk(KERN_INFO "mcKernelApi %s(): " txt, \
+ __func__, \
+ ##__VA_ARGS__)
+
+#define MCDRV_DBG_WARN(txt, ...) \
+ printk(KERN_WARNING "mcKernelApi %s() WARNING: " txt, \
+ __func__, \
+ ##__VA_ARGS__)
+
+#define MCDRV_DBG_ERROR(txt, ...) \
+ printk(KERN_ERR "mcKernelApi %s() ### ERROR: " txt, \
+ __func__, \
+ ##__VA_ARGS__)
+
+
+#define MCDRV_ASSERT(cond) \
+ do { \
+ if (unlikely(!(cond))) { \
+ panic("mcKernelApi 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 LOG_I MCDRV_DBG_VERBOSE
+#define LOG_W MCDRV_DBG_WARN
+#define LOG_E MCDRV_DBG_ERROR
+
+
+#define assert(expr) MCDRV_ASSERT(expr)
+
+#endif /* COMMON_H */
+
+/** @} */
diff --git a/drivers/gud/mobicore_kernelapi/connection.c b/drivers/gud/mobicore_kernelapi/connection.c
new file mode 100644
index 0000000..9048ae8
--- /dev/null
+++ b/drivers/gud/mobicore_kernelapi/connection.c
@@ -0,0 +1,229 @@
+/** @addtogroup MCD_MCDIMPL_DAEMON_SRV
+ * @{
+ * @file
+ *
+ * Connection data.
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2012 -->
+ *
+ * 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.
+ */
+#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 = kzalloc(sizeof(struct connection),
+ GFP_KERNEL);
+ conn->sequence_magic = mcapi_unique_id();
+ mutex_init(&conn->data_lock);
+ /* No data available */
+ sema_init(&conn->data_available_sem, SEM_NO_DATA_AVAILABLE);
+
+ mcapi_insert_connection(conn);
+ return conn;
+}
+
+/*----------------------------------------------------------------------------*/
+struct connection *connection_create(
+ int socket_descriptor,
+ pid_t dest
+) {
+ struct connection *conn = connection_new();
+
+ conn->peer_pid = dest;
+ 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_readDataMsg(
+ struct connection *conn,
+ void *buffer,
+ uint32_t len
+) {
+ size_t ret = -1;
+ MCDRV_DBG_VERBOSE("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("read %u", 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("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("Timeout reading the data sem");
+ ret = -2;
+ break;
+ }
+
+ if (mutex_lock_interruptible(&(conn->data_lock))) {
+ MCDRV_DBG_ERROR("interrupted reading the data sem");
+ ret = -1;
+ break;
+ }
+ /* Have data, use it */
+ if (conn->data_len > 0)
+ ret = connection_readDataMsg(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;
+}
+
+/*----------------------------------------------------------------------------*/
+size_t 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("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;
+ break;
+ }
+ memcpy(NLMSG_DATA(nlh), buffer, len);
+
+ netlink_unicast(conn->socket_descriptor, skb,
+ conn->peer_pid, MSG_DONTWAIT);
+ ret = len;
+ } while (0);
+
+ if (!ret && skb != NULL)
+ kfree_skb(skb);
+
+ return ret;
+}
+
+int connection_process(
+ struct connection *conn,
+ struct sk_buff *skb
+)
+{
+ int ret = 0;
+ do {
+ if (mutex_lock_interruptible(&(conn->data_lock))) {
+ MCDRV_DBG_ERROR("Interrupted getting data semaphore!");
+ ret = -1;
+ break;
+ }
+
+ kfree_skb(conn->skb);
+
+ /* Get a reference to the incomming 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/gud/mobicore_kernelapi/connection.h b/drivers/gud/mobicore_kernelapi/connection.h
new file mode 100644
index 0000000..0b468e6
--- /dev/null
+++ b/drivers/gud/mobicore_kernelapi/connection.h
@@ -0,0 +1,122 @@
+/** @addtogroup MCD_MCDIMPL_DAEMON_SRV
+ * @{
+ * @file
+ *
+ * Connection data.
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2012 -->
+ *
+ * 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.
+ */
+#ifndef CONNECTION_H_
+#define CONNECTION_H_
+
+#include <linux/semaphore.h>
+
+#include <stddef.h>
+#include <stdbool.h>
+
+#define MAX_PAYLOAD_SIZE 128
+
+struct connection {
+ struct sock *socket_descriptor; /**< Netlink socket */
+ uint32_t sequence_magic; /**< Random? magic to match requests/answers */
+
+ struct nlmsghdr *data_msg;
+ uint32_t data_len; /**< How much connection data is left */
+ void *data_start; /**< Start pointer of remaining data */
+ struct sk_buff *skb;
+
+ struct mutex data_lock; /**< Data protection lock */
+ struct semaphore data_available_sem; /**< Data protection semaphore */
+
+ pid_t self_pid; /**< PID address used for local connection */
+ pid_t peer_pid; /**< Remote PID for connection */
+
+ struct list_head list; /**< The list param for using the kernel lists*/
+};
+
+struct connection *connection_new(
+ void
+);
+
+struct connection *connection_create(
+ int socket_descriptor,
+ pid_t dest
+);
+
+void connection_cleanup(
+ struct connection *conn
+);
+
+/**
+ * Connect to destination.
+ *
+ * @param Destination pointer.
+ * @return true on success.
+ */
+bool connection_connect(
+ struct connection *conn,
+ pid_t dest
+);
+
+
+/**
+ * Read bytes from the connection.
+ *
+ * @param buffer Pointer to destination buffer.
+ * @param len Number of bytes to read.
+ * @return Number of bytes read.
+ */
+size_t connection_read_datablock(
+ struct connection *conn,
+ void *buffer,
+ uint32_t len
+);
+/**
+ * Read bytes from the connection.
+ *
+ * @param buffer Pointer to destination buffer.
+ * @param len Number of bytes to read.
+ * @param timeout Timeout in milliseconds
+ * @return Number of bytes read.
+ * @return -1 if select() failed (returned -1)
+ * @return -2 if no data available, i.e. timeout
+ */
+size_t connection_read_data(
+ struct connection *conn,
+ void *buffer,
+ uint32_t len,
+ int32_t timeout
+);
+
+/**
+ * Write bytes to the connection.
+ *
+ * @param buffer Pointer to source buffer.
+ * @param len Number of bytes to read.
+ * @return Number of bytes written.
+ */
+size_t connection_write_data(
+ struct connection *conn,
+ void *buffer,
+ uint32_t len
+);
+
+/**
+ * Write bytes to the connection.
+ *
+ * @param buffer Pointer to source buffer.
+ * @param len Number of bytes to read.
+ * @return Number of bytes written.
+ */
+int connection_process(
+ struct connection *conn,
+ struct sk_buff *skb
+);
+
+#endif /* CONNECTION_H_ */
+
+/** @} */
diff --git a/drivers/gud/mobicore_kernelapi/device.c b/drivers/gud/mobicore_kernelapi/device.c
new file mode 100644
index 0000000..dbeee6a
--- /dev/null
+++ b/drivers/gud/mobicore_kernelapi/device.c
@@ -0,0 +1,257 @@
+/** @addtogroup MCD_IMPL_LIB
+ * @{
+ * @file
+ *
+ * Client library device management.
+ *
+ * Device and Trustlet Session management Funtions.
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2012 -->
+ *
+ * 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.
+ */
+#include <linux/list.h>
+#include <linux/slab.h>
+#include "mc_kernel_api.h"
+#include "public/mobicore_driver_api.h"
+
+#include "device.h"
+#include "common.h"
+
+/*----------------------------------------------------------------------------*/
+struct wsm *wsm_create(
+ void *virt_addr,
+ uint32_t len,
+ uint32_t handle,
+ void *phys_addr /*= NULL this may be unknown, so is can be omitted.*/
+ )
+{
+ struct wsm *wsm = kzalloc(sizeof(struct wsm), GFP_KERNEL);
+ wsm->virt_addr = virt_addr;
+ wsm->len = len;
+ wsm->handle = handle;
+ wsm->phys_addr = phys_addr;
+ return wsm;
+}
+
+
+/*----------------------------------------------------------------------------*/
+struct mcore_device_t *mcore_device_create(
+ uint32_t device_id,
+ struct connection *connection
+) {
+ struct mcore_device_t *dev =
+ kzalloc(sizeof(struct mcore_device_t), GFP_KERNEL);
+ dev->device_id = device_id;
+ dev->connection = connection;
+
+ INIT_LIST_HEAD(&dev->session_vector);
+ INIT_LIST_HEAD(&dev->wsm_l2_vector);
+
+ return dev;
+}
+
+
+/*----------------------------------------------------------------------------*/
+void mcore_device_cleanup(
+ struct mcore_device_t *dev
+) {
+ struct session *tmp;
+ struct wsm *wsm;
+ 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.*/
+ list_for_each_safe(pos, q, &dev->session_vector) {
+ tmp = list_entry(pos, struct session, list);
+ list_del(pos);
+ session_cleanup(tmp);
+ }
+
+ /* Free all allocated WSM descriptors */
+ list_for_each_safe(pos, q, &dev->wsm_l2_vector) {
+ wsm = list_entry(pos, struct wsm, list);
+ /* mcKMod_free(dev->instance, wsm->handle); */
+ list_del(pos);
+ kfree(wsm);
+ }
+ connection_cleanup(dev->connection);
+
+ mcore_device_close(dev);
+ kfree(dev);
+}
+
+
+/*----------------------------------------------------------------------------*/
+bool mcore_device_open(
+ struct mcore_device_t *dev,
+ const char *deviceName
+) {
+ 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
+) {
+ return !list_empty(&dev->session_vector);
+}
+
+
+/*----------------------------------------------------------------------------*/
+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(" session %u already exists", session_id);
+ return false;
+ }
+ struct session *session = session_create(session_id, dev->instance,
+ connection);
+ list_add_tail(&(session->list), &(dev->session_vector));
+ 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;
+
+ list_for_each_safe(pos, q, &dev->session_vector) {
+ tmp = list_entry(pos, struct session, list);
+ if (tmp->session_id == session_id) {
+ list_del(pos);
+ session_cleanup(tmp);
+ ret = true;
+ break;
+ }
+ }
+ return ret;
+}
+
+
+/*----------------------------------------------------------------------------*/
+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;
+
+
+ /* 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;
+ break;
+ }
+ }
+ 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;
+ void *phys_addr;
+ int ret = mobicore_allocate_wsm(dev->instance,
+ len,
+ &handle,
+ &virt_addr,
+ &phys_addr);
+ if (ret != 0)
+ break;
+
+ /* Register (vaddr,paddr) with device */
+ wsm = wsm_create(virt_addr, len, handle, phys_addr);
+
+ list_add_tail(&(wsm->list), &(dev->wsm_l2_vector));
+
+ } while (0);
+
+ /* Return pointer to the allocated memory */
+ 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;
+
+ list_for_each(pos, &dev->wsm_l2_vector) {
+ tmp = list_entry(pos, struct wsm, list);
+ if (tmp == wsm) {
+ ret = true;
+ break;
+ }
+ }
+
+ if (ret) {
+ MCDRV_DBG_VERBOSE("freeWsm virt_addr=0x%p, handle=%d",
+ wsm->virt_addr, wsm->handle);
+
+ /* ignore return code */
+ mobicore_free(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;
+ struct list_head *pos;
+
+ list_for_each(pos, &dev->wsm_l2_vector) {
+ wsm = list_entry(pos, struct wsm, list);
+ if (virt_addr == wsm->virt_addr)
+ return wsm;
+ }
+
+ return NULL;
+}
+
+/** @} */
diff --git a/drivers/gud/mobicore_kernelapi/device.h b/drivers/gud/mobicore_kernelapi/device.h
new file mode 100644
index 0000000..f40d993
--- /dev/null
+++ b/drivers/gud/mobicore_kernelapi/device.h
@@ -0,0 +1,139 @@
+/** @addtogroup MCD_IMPL_LIB
+ * @{
+ * @file
+ *
+ * Client library device management.
+ *
+ * Device and Trustlet Session management Functions.
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2012 -->
+ *
+ * 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.
+ */
+#ifndef DEVICE_H_
+#define DEVICE_H_
+
+#include <linux/list.h>
+
+#include "connection.h"
+#include "session.h"
+#include "wsm.h"
+
+
+struct mcore_device_t {
+ struct list_head session_vector; /**< MobiCore Trustlet session
+ associated with the device */
+ struct list_head wsm_l2_vector; /**< WSM L2 Table */
+
+ uint32_t device_id; /**< Device identifier */
+ struct connection *connection; /**< The device connection */
+ struct mc_instance *instance; /**< MobiCore Driver instance */
+
+ struct list_head list; /**< The list param for using the kernel lists*/
+};
+
+struct mcore_device_t *mcore_device_create(
+ uint32_t device_id,
+ struct connection *connection
+);
+
+void mcore_device_cleanup(
+ struct mcore_device_t *dev
+);
+
+/**
+ * Open the device.
+ * @param deviceName Name of the kernel modules device file.
+ * @return true if the device has been opened successfully
+ */
+bool mcore_device_open(
+ struct mcore_device_t *dev,
+ const char *deviceName
+);
+
+/**
+ * Closes the device.
+ */
+void mcore_device_close(
+ struct mcore_device_t *dev
+);
+
+/**
+ * Check if the device has open sessions.
+ * @return true if the device has one or more open sessions.
+ */
+bool mcore_device_has_sessions(
+ struct mcore_device_t *dev
+);
+
+/**
+ * Add a session to the device.
+ * @param session_id session ID
+ * @param connection session connection
+ */
+bool mcore_device_create_new_session(
+ struct mcore_device_t *dev,
+ uint32_t session_id,
+ struct connection *connection
+);
+
+/**
+ * Remove the specified session from the device.
+ * The session object will be destroyed and all resources associated with it
+ * will be freed.
+ *
+ * @param session_id Session of the session to remove.
+ * @return true if a session has been found and removed.
+ */
+bool mcore_device_remove_session(
+ struct mcore_device_t *dev,
+ uint32_t session_id
+);
+
+/**
+ * Get as session object for a given session ID.
+ * @param session_id Identified of a previously opened session.
+ * @return Session object if available or NULL if no session has been found.
+ */
+struct session *mcore_device_resolve_session_id(
+ struct mcore_device_t *dev,
+ uint32_t session_id
+);
+
+/**
+ * Allocate a block of contiguous WSM.
+ * @param len The virtual address to be registered.
+ * @return The virtual address of the allocated memory or NULL if no memory
+ * is available.
+ */
+struct wsm *mcore_device_allocate_contiguous_wsm(
+ struct mcore_device_t *dev,
+ uint32_t len
+);
+
+/**
+ * Unregister a vaddr from a device.
+ * @param vaddr The virtual address to be registered.
+ * @param paddr The physical address to be registered.
+ */
+bool mcore_device_free_contiguous_wsm(
+ struct mcore_device_t *dev,
+ struct wsm *wsm
+);
+
+/**
+ * Get a WSM object for a given virtual address.
+ * @param vaddr The virtual address which has been allocate with mc_malloc_wsm()
+ * in advance.
+ * @return the WSM object or NULL if no address has been found.
+ */
+struct wsm *mcore_device_find_contiguous_wsm(
+ struct mcore_device_t *dev,
+ void *virt_addr
+);
+
+#endif /* DEVICE_H_ */
+
+/** @} */
diff --git a/drivers/gud/mobicore_kernelapi/include/mcinq.h b/drivers/gud/mobicore_kernelapi/include/mcinq.h
new file mode 100644
index 0000000..3cb82be
--- /dev/null
+++ b/drivers/gud/mobicore_kernelapi/include/mcinq.h
@@ -0,0 +1,125 @@
+/** @addtogroup NQ
+ * @{
+ * 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
+ *
+ * @file
+ * Notification queue declarations.
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009-2012 -->
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef NQ_H_
+#define NQ_H_
+
+/** \name NQ Size Defines
+ * 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. */
+/** @} */
+
+/** \name NQ Length Defines
+ * Minimum and maximum notification queue length.
+ * @{ */
+/**< Minimum notification length (in bytes). */
+#define MIN_NQ_LEN (MIN_NQ_ELEM * sizeof(notification))
+/**< Maximum notification length (in bytes). */
+#define MAX_NQ_LEN (MAX_NQ_ELEM * sizeof(notification))
+/** @} */
+
+/** \name Session ID Defines
+ * Standard Session IDs.
+ * @{ */
+/**< 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 information. */
+};
+
+/** 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 /** NQ_H_ */
+
+/** @} */
diff --git a/drivers/gud/mobicore_kernelapi/include/mcuuid.h b/drivers/gud/mobicore_kernelapi/include/mcuuid.h
new file mode 100644
index 0000000..b72acb8
--- /dev/null
+++ b/drivers/gud/mobicore_kernelapi/include/mcuuid.h
@@ -0,0 +1,74 @@
+/**
+ * @addtogroup MC_UUID mcUuid - Universally Unique Identifier.
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2011-2012 -->
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @ingroup MC_DATA_TYPES
+ * @{
+ */
+
+#ifndef MC_UUID_H_
+#define MC_UUID_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. */
+};
+
+/** UUID value used as free marker in service provider containers. */
+#define MC_UUID_FREE_DEFINE \
+ { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }
+
+static const struct mc_uuid_t MC_UUID_FREE = {
+ MC_UUID_FREE_DEFINE
+};
+
+/** Reserved UUID. */
+#define MC_UUID_RESERVED_DEFINE \
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
+
+static const struct mc_uuid_t MC_UUID_RESERVED = {
+ MC_UUID_RESERVED_DEFINE
+};
+
+/** UUID for system applications. */
+#define MC_UUID_SYSTEM_DEFINE \
+ { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE }
+
+static const struct mc_uuid_t MC_UUID_SYSTEM = {
+ MC_UUID_SYSTEM_DEFINE
+};
+
+#endif /* MC_UUID_H_ */
+
+/** @} */
+
diff --git a/drivers/gud/mobicore_kernelapi/main.c b/drivers/gud/mobicore_kernelapi/main.c
new file mode 100644
index 0000000..62997f7
--- /dev/null
+++ b/drivers/gud/mobicore_kernelapi/main.c
@@ -0,0 +1,181 @@
+/**
+ * MobiCore KernelApi module
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009-2012 -->
+ *
+ * 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.
+ */
+#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 <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 mc_kernelapi_ctx *mod_ctx; /* = NULL; */
+
+/*----------------------------------------------------------------------------*/
+/* 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;
+ struct list_head *pos;
+
+ /* Get session for session_id */
+ list_for_each(pos, &mod_ctx->peers) {
+ tmp = list_entry(pos, struct connection, list);
+ if (tmp->sequence_magic == seq)
+ return tmp;
+ }
+
+ return NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+void mcapi_insert_connection(
+ struct connection *connection
+)
+{
+ list_add_tail(&(connection->list), &(mod_ctx->peers));
+ connection->socket_descriptor = mod_ctx->sk;
+}
+
+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.*/
+ 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;
+ }
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+static int mcapi_process(
+ struct sk_buff *skb,
+ struct nlmsghdr *nlh
+)
+{
+ struct connection *c;
+ int length;
+ int seq;
+ pid_t pid;
+ int ret;
+
+ pid = nlh->nlmsg_pid;
+ length = nlh->nlmsg_len;
+ seq = nlh->nlmsg_seq;
+ MCDRV_DBG_VERBOSE("nlmsg len %d type %d pid 0x%X seq %d\n",
+ length, nlh->nlmsg_type, pid, seq);
+ do {
+ c = mcapi_find_connection(seq);
+ if (!c) {
+ MCDRV_ERROR("Invalid incomming 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)
+{
+ printk(KERN_INFO "Mobicore API module initialized!\n");
+
+ mod_ctx = kzalloc(sizeof(struct mc_kernelapi_ctx), GFP_KERNEL);
+
+ /* start kernel thread */
+ mod_ctx->sk = netlink_kernel_create(&init_net, MC_DAEMON_NETLINK, 0,
+ mcapi_callback, NULL, THIS_MODULE);
+
+ if (!mod_ctx->sk) {
+ MCDRV_ERROR("register of recieve handler failed");
+ return -EFAULT;
+ }
+
+ INIT_LIST_HEAD(&mod_ctx->peers);
+ return 0;
+}
+
+static void __exit mcapi_exit(void)
+{
+ printk(KERN_INFO "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("Giesecke & Devrient GmbH");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MobiCore API driver");
diff --git a/drivers/gud/mobicore_kernelapi/public/mobicore_driver_api.h b/drivers/gud/mobicore_kernelapi/public/mobicore_driver_api.h
new file mode 100644
index 0000000..ccfb2e5
--- /dev/null
+++ b/drivers/gud/mobicore_kernelapi/public/mobicore_driver_api.h
@@ -0,0 +1,483 @@
+/**
+ * @defgroup MCD_API MobiCore Driver API
+ * @addtogroup MCD_API
+ * @{
+ *
+ * @if DOXYGEN_MCDRV_API
+ * @mainpage MobiCore Driver API.
+ * @endif
+ *
+ * MobiCore Driver API.
+ *
+ * The MobiCore (MC) Driver API provides access functions to the MobiCore
+ * runtime environment and the contained Trustlets.
+ *
+ * @image html DoxyOverviewDrvApi500x.png
+ * @image latex DoxyOverviewDrvApi500x.png "MobiCore Overview" width=12cm
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2012 -->
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef MCDRIVER_H_
+#define MCDRIVER_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 {
+ MC_CTRL_GET_VERSION = 1 /**< Return the driver version */
+};
+
+
+/** 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! */
+ void *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
+
+
+
+/** Open a new connection to a MobiCore device.
+ *
+ * mc_open_device() initializes all device specific resources required to
+ * communicate with an 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.
+ *
+ * @param [in] device_id Identifier for the MobiCore device to be used.
+ * MC_DEVICE_ID_DEFAULT refers to the default device.
+ *
+ * @return MC_DRV_OK if operation has been successfully completed.
+ * @return MC_DRV_ERR_INVALID_OPERATION if device already opened.
+ * @return MC_DRV_ERR_DAEMON_UNREACHABLE when problems with daemon occur.
+ * @return MC_DRV_ERR_UNKNOWN_DEVICE when device_id is unknown.
+ * @return MC_DRV_ERR_INVALID_DEVICE_FILE if kernel module under
+ * /dev/mobicore cannot be opened
+ *
+ * Uses a Mutex.
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_open_device(
+ uint32_t device_id
+);
+
+/** Close the connection to a 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.
+ *
+ * @param [in] device_id Identifier for the MobiCore device.
+ * MC_DEVICE_ID_DEFAULT refers to the default device.
+ *
+ * @return MC_DRV_OK if operation has been successfully completed.
+ * @return MC_DRV_ERR_UNKNOWN_DEVICE when device id is invalid.
+ * @return MC_DRV_ERR_SESSION_PENDING when a session is still open.
+ * @return MC_DRV_ERR_DAEMON_UNREACHABLE when problems with daemon occur.
+ *
+ * Uses a Mutex.
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_close_device(
+ uint32_t device_id
+);
+
+/** Open a new session to a Trustlet. 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).
+ *
+ * @param [in,out] session On success, the session data will be returned.
+ * Note that session.device_id has to be the device id of an opened device.
+ * @param [in] uuid UUID of the Trustlet to be opened.
+ * @param [in] tci TCI buffer for communicating with the trustlet.
+ * @param [in] tci_len Length of the TCI buffer. Maximum allowed value
+ * is MC_MAX_TCI_LEN.
+ *
+ * @return MC_DRV_OK if operation has been successfully completed.
+ * @return MC_DRV_INVALID_PARAMETER if session parameter is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_DEVICE when device id is invalid.
+ * @return MC_DRV_ERR_DAEMON_UNREACHABLE when problems with daemon socket occur.
+ * @return MC_DRV_ERR_UNKNOWN_DEVICE when daemon returns an error.
+ *
+ * Uses a Mutex.
+ */
+__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
+);
+
+/** Close a Trustlet session.
+ *
+ * Closes the specified MobiCore session. The call will block until the
+ * session has been closed.
+ *
+ * @pre Device device_id has to be opened in advance.
+ *
+ * @param [in] session Session to be closed.
+ *
+ * @return MC_DRV_OK if operation has been successfully completed.
+ * @return MC_DRV_INVALID_PARAMETER if session parameter is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_SESSION when session id is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_DEVICE when device id of session is invalid.
+ * @return MC_DRV_ERR_DAEMON_UNREACHABLE when problems with daemon occur.
+ * @return MC_DRV_ERR_INVALID_DEVICE_FILE when daemon cannot open trustlet file.
+ *
+ * Uses a Mutex.
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_close_session(
+ struct mc_session_handle *session
+);
+
+/** Notify a session.
+ * 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().
+ * @pre A session has to be opened in advance.
+ *
+ * @param session The session to be notified.
+ *
+ * @return MC_DRV_OK if operation has been successfully completed.
+ * @return MC_DRV_INVALID_PARAMETER if session parameter is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_SESSION when session id is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_DEVICE when device id of session is invalid.
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_notify(
+ struct mc_session_handle *session
+);
+
+/** Wait for a notification.
+ *
+ * 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.
+ *
+ * @attention if timeout is below 0, call will block:
+ * Caller has to trust the other side to send a notification to wake him up
+ * again.
+ *
+ * @param [in] session The session the notification should correspond to.
+ * @param [in] timeout Time in milliseconds to wait
+ * (MC_NO_TIMEOUT : direct return, > 0 : milliseconds,
+ * MC_INFINITE_TIMEOUT : wait infinitely)
+ *
+ * @return MC_DRV_OK if notification is available.
+ * @return MC_DRV_ERR_TIMEOUT if no notification arrived in time.
+ * @return MC_DRV_INFO_NOTIFICATION if a problem with the session was
+ * encountered. Get more details with mc_get_session_error_code().
+ * @return MC_DRV_ERR_NOTIFICATION if a problem with the socket occurred.
+ * @return MC_DRV_INVALID_PARAMETER if a parameter is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_SESSION when session id is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_DEVICE when device id of session is invalid.
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_wait_notification(
+ struct mc_session_handle *session,
+ int32_t timeout
+);
+
+/**
+ * Allocate a block of world shared memory (WSM).
+ * 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.
+ *
+ * @param [in] device_id The ID of an opened device to retrieve the WSM from.
+ * @param [in] align The alignment (number of pages) of the memory block
+ * (e.g. 0x00000001 for 4kb).
+ * @param [in] len Length of the block in bytes.
+ * @param [out] wsm Virtual address of the world shared memory block.
+ * @param [in] wsm_flags Platform specific flags describing the memory to
+ * be allocated.
+ *
+ * @attention: align and wsm_flags are currently ignored
+ *
+ * @return MC_DRV_OK if operation has been successfully completed.
+ * @return MC_DRV_INVALID_PARAMETER if a parameter is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_DEVICE when device id is invalid.
+ * @return MC_DRV_ERR_NO_FREE_MEMORY if no more contiguous memory is available
+ * in this size or for this process.
+ *
+ * Uses a Mutex.
+ */
+__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
+);
+
+/**
+ * Free a block of world shared memory (WSM).
+ * 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.
+ *
+ * @param [in] device_id The ID to which the given address belongs.
+ * @param [in] wsm Address of WSM block to be freed.
+ *
+ * @return MC_DRV_OK if operation has been successfully completed.
+ * @return MC_DRV_INVALID_PARAMETER if a parameter is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_DEVICE when device id is invalid.
+ * @return MC_DRV_ERR_FREE_MEMORY_FAILED on failures.
+ *
+ * Uses a Mutex.
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_free_wsm(
+ uint32_t device_id,
+ uint8_t *wsm
+);
+
+/**
+ * Map additional bulk buffer between a Trustlet Connector (TLC) and
+ * the Trustlet (TL) for a session.
+ * 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.
+ *
+ * @attention It is up to the application layer (TLC) to inform the Trustlet
+ * about the additional mapped bulk memory.
+ *
+ * @param [in] 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.
+ * @param [in] buf Virtual address of a memory portion (relative to TLC)
+ * to be shared with the Trustlet, already includes a possible offset!
+ * @param [in] len length of buffer block in bytes.
+ * @param [out] map_info Information structure about the mapped Bulk buffer
+ * between the TLC (Nwd) and
+ * the TL (Swd).
+ *
+ * @return MC_DRV_OK if operation has been successfully completed.
+ * @return MC_DRV_INVALID_PARAMETER if a parameter is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_SESSION when session id is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_DEVICE when device id of session is invalid.
+ * @return MC_DRV_ERR_DAEMON_UNREACHABLE when problems with daemon occur.
+ * @return MC_DRV_ERR_BULK_MAPPING when buf is already uses as bulk buffer or
+ * when registering the buffer failed.
+ *
+ * Uses a Mutex.
+ */
+__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
+);
+
+/**
+ * Remove additional mapped bulk buffer between Trustlet Connector (TLC)
+ * and the Trustlet (TL) for a session.
+ *
+ * @attention The bulk buffer will immediately be unmapped from the session
+ * context.
+ * @attention The application layer (TLC) must inform the TL about unmapping
+ * of the additional bulk memory before calling mc_unmap!
+ *
+ * @param [in] 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.
+ * @param [in] buf Virtual address of a memory portion (relative to TLC)
+ * shared with the TL, already includes a possible offset!
+ * @param [in] map_info Information structure about the mapped Bulk buffer
+ * between the TLC (Nwd) and
+ * the TL (Swd).
+ * @attention The clientlib currently ignores the len field in map_info.
+ *
+ * @return MC_DRV_OK if operation has been successfully completed.
+ * @return MC_DRV_INVALID_PARAMETER if a parameter is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_SESSION when session id is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_DEVICE when device id of session is invalid.
+ * @return MC_DRV_ERR_DAEMON_UNREACHABLE when problems with daemon occur.
+ * @return MC_DRV_ERR_BULK_UNMAPPING when buf was not registered earlier
+ * or when unregistering failed.
+ *
+ * Uses a Mutex.
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_unmap(
+ struct mc_session_handle *session,
+ void *buf,
+ struct mc_bulk_map *map_info
+);
+
+
+/**
+ * @attention: Not implemented.
+ * Execute driver specific command.
+ * mc_driver_ctrl() can be used to execute driver specific commands.
+ * Besides the control command MC_CTRL_GET_VERSION commands are implementation
+ * specific.
+ * Please refer to the corresponding specification of the driver manufacturer.
+ *
+ * @param [in] param Command ID of the command to be executed.
+ * @param [in, out] data Command data and response depending on command.
+ * @param [in] len Length of the data block.
+ *
+ * @return MC_DRV_ERR_NOT_IMPLEMENTED.
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_driver_ctrl(
+ enum mc_driver_ctrl param,
+ uint8_t *data,
+ uint32_t len
+);
+
+/**
+ * @attention: Not implemented.
+ * Execute application management command.
+ * mc_manage() shall be used to exchange application management commands with
+ * the MobiCore.
+ * The MobiCore Application Management Protocol is described in [MCAMP].
+ *
+ * @param [in] device_id Identifier for the MobiCore device to be used.
+ * NULL refers to the default device.
+ * @param [in, out] data Command data/response data depending on command.
+ * @param [in] len Length of the data block.
+ *
+ * @return MC_DRV_ERR_NOT_IMPLEMENTED.
+ */
+__MC_CLIENT_LIB_API enum mc_result mc_manage(
+ uint32_t device_id,
+ uint8_t *data,
+ uint32_t len
+);
+
+/**
+ * Get additional error information of the last error that occured on a session.
+ * After the request the stored error code will be deleted.
+ *
+ * @param [in] session Session handle with information of the device_id and
+ * the session_id.
+ * @param [out] 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.
+ *
+ * @return MC_DRV_OK if operation has been successfully completed.
+ * @return MC_DRV_INVALID_PARAMETER if a parameter is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_SESSION when session id is invalid.
+ * @return MC_DRV_ERR_UNKNOWN_DEVICE when 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 /** MCDRIVER_H_ */
+
+/** @} */
diff --git a/drivers/gud/mobicore_kernelapi/public/mobicore_driver_cmd.h b/drivers/gud/mobicore_kernelapi/public/mobicore_driver_cmd.h
new file mode 100644
index 0000000..9ff7989
--- /dev/null
+++ b/drivers/gud/mobicore_kernelapi/public/mobicore_driver_cmd.h
@@ -0,0 +1,289 @@
+/** @addtogroup MCD_MCDIMPL_DAEMON
+ * @{
+ * @file
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2012 -->
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef MCDAEMON_H_
+#define MCDAEMON_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 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 device_id;
+ uint32_t session_id;
+ uint32_t device_session_id;
+ uint32_t mc_result;
+ 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 phys_addr_l2;
+ 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;
+ uint32_t mc_result;
+};
+
+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 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;
+ uint32_t mc_result;
+};
+
+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 /* MCDAEMON_H_ */
+
+/** @} */
diff --git a/drivers/gud/mobicore_kernelapi/session.c b/drivers/gud/mobicore_kernelapi/session.c
new file mode 100644
index 0000000..e62b4b3
--- /dev/null
+++ b/drivers/gud/mobicore_kernelapi/session.c
@@ -0,0 +1,202 @@
+/** @addtogroup MCD_IMPL_LIB
+ * @{
+ * @file
+ * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2012 -->
+ *
+ * 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.
+ */
+#include <linux/types.h>
+#include <linux/slab.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,
+ void *phys_addr_wsm_l2
+) {
+ struct bulk_buffer_descriptor *desc =
+ kzalloc(sizeof(struct bulk_buffer_descriptor), GFP_KERNEL);
+ desc->virt_addr = virt_addr;
+ desc->len = len;
+ desc->handle = handle;
+ desc->phys_addr_wsm_l2 = phys_addr_wsm_l2;
+ return desc;
+}
+
+/*****************************************************************************/
+struct session *session_create(
+ uint32_t session_id,
+ void *instance,
+ struct connection *connection
+) {
+ struct session *session =
+ kzalloc(sizeof(struct session), GFP_KERNEL);
+ 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));
+ return session;
+}
+
+
+/*****************************************************************************/
+void session_cleanup(
+ struct session *session
+) {
+ struct bulk_buffer_descriptor *bulk_buf_descr;
+ struct list_head *pos, *q;
+
+ /* Unmap still mapped buffers */
+ list_for_each_safe(pos, q, &session->bulk_buffer_descriptors) {
+ bulk_buf_descr =
+ list_entry(pos, struct bulk_buffer_descriptor, list);
+
+ MCDRV_DBG_VERBOSE("Physical Address of L2 Table = 0x%X, "
+ "handle= %d",
+ (unsigned int)bulk_buf_descr->phys_addr_wsm_l2,
+ 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("mobicore_unmap_vmem failed: %d", ret);
+
+ list_del(pos);
+ kfree(bulk_buf_descr);
+ }
+
+ /* 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;
+
+ /* Search bulk buffer descriptors for existing vAddr
+ At the moment a virtual address can only be added one time */
+ list_for_each(pos, &session->bulk_buffer_descriptors) {
+ tmp = list_entry(pos, struct bulk_buffer_descriptor, list);
+ if (tmp->virt_addr == buf)
+ return NULL;
+ }
+
+ do {
+ /* Prepare the interface structure for memory registration in
+ Kernel Module */
+ void *l2_table_phys;
+ uint32_t handle;
+
+ int ret = mobicore_map_vmem(session->instance,
+ buf,
+ len,
+ &handle,
+ &l2_table_phys);
+
+ if (ret != 0) {
+ MCDRV_DBG_ERROR("mobicore_map_vmem failed, ret=%d",
+ ret);
+ break;
+ }
+
+ MCDRV_DBG_VERBOSE("Physical Address of L2 Table = 0x%X, "
+ "handle=%d",
+ (unsigned int)l2_table_phys,
+ handle);
+
+ /* Create new descriptor */
+ bulk_buf_descr = bulk_buffer_descriptor_create(
+ buf,
+ len,
+ handle,
+ l2_table_phys);
+
+ /* Add to vector of descriptors */
+ list_add_tail(&(bulk_buf_descr->list),
+ &(session->bulk_buffer_descriptors));
+ } 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_descr = NULL;
+ struct bulk_buffer_descriptor *tmp;
+ struct list_head *pos, *q;
+
+ MCDRV_DBG_VERBOSE("Virtual Address = 0x%X", (unsigned int) virt_addr);
+
+ /* Search and remove bulk buffer descriptor */
+ 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_descr = tmp;
+ list_del(pos);
+ break;
+ }
+ }
+
+ if (bulk_buf_descr == NULL) {
+ MCDRV_DBG_ERROR("Virtual Address not found");
+ ret = false;
+ } else {
+ MCDRV_DBG_VERBOSE("WsmL2 phys=0x%X, handle=%d",
+ (unsigned int)bulk_buf_descr->phys_addr_wsm_l2,
+ bulk_buf_descr->handle);
+
+ /* ignore any error, as we cannot do anything */
+ int ret = mobicore_unmap_vmem(session->instance,
+ bulk_buf_descr->handle);
+ if (ret != 0)
+ MCDRV_DBG_ERROR("mobicore_unmap_vmem failed: %d", ret);
+
+ kfree(bulk_buf_descr);
+ }
+
+ return ret;
+}
+
+/** @} */
diff --git a/drivers/gud/mobicore_kernelapi/session.h b/drivers/gud/mobicore_kernelapi/session.h
new file mode 100644
index 0000000..9a53740
--- /dev/null
+++ b/drivers/gud/mobicore_kernelapi/session.h
@@ -0,0 +1,136 @@
+/** @addtogroup MCD_IMPL_LIB
+ * @{
+ * @file
+ * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2012 -->
+ *
+ * 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.
+ */
+#ifndef SESSION_H_
+#define SESSION_H_
+
+#include "common.h"
+
+#include <linux/list.h>
+#include "connection.h"
+
+
+struct bulk_buffer_descriptor {
+ void *virt_addr;/**< The virtual address of the Bulk buffer*/
+ uint32_t len; /**< Length of the Bulk buffer*/
+ uint32_t handle;
+ void *phys_addr_wsm_l2; /**< The physical address of the
+ L2 table of the Bulk buffer*/
+ struct list_head list; /**< The list param for using the kernel lists*/
+};
+
+struct bulk_buffer_descriptor *bulk_buffer_descriptor_create(
+ void *virt_addr,
+ uint32_t len,
+ uint32_t handle,
+ void *phys_addr_wsm_l2
+);
+
+/** 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;
+ /**< Informations 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
+);
+
+/**
+ * Add address information of additional bulk buffer memory to session and
+ * register virtual memory in kernel module.
+ *
+ * @attention The virtual address can only be added one time. If the virtual
+ * address already exist, NULL is returned.
+ *
+ * @param buf The virtual address of bulk buffer.
+ * @param len Length of bulk buffer.
+ *
+ * @return On success the actual Bulk buffer descriptor with all address
+ * information is retured, NULL if an error occurs.
+ */
+struct bulk_buffer_descriptor *session_add_bulk_buf(
+ struct session *session,
+ void *buf,
+ uint32_t len
+);
+
+/**
+ * Remove address information of additional bulk buffer memory from session and
+ * unregister virtual memory in kernel module
+ *
+ * @param buf The virtual address of the bulk buffer.
+ *
+ * @return true on success.
+ */
+bool session_remove_bulk_buf(
+ struct session *session,
+ void *buf
+);
+
+/**
+ * Set additional error information of the last error that occured.
+ *
+ * @param errorCode The actual error.
+ */
+void session_set_error_info(
+ struct session *session,
+ int32_t err
+);
+
+/**
+ * Get additional error information of the last error that occured.
+ *
+ * @attention After request the information is set to SESSION_ERR_NO.
+ *
+ * @return Last stored error code or SESSION_ERR_NO.
+ */
+int32_t session_get_last_err(
+ struct session *session
+);
+
+#endif /* SESSION_H_ */
+
+/** @} */
diff --git a/drivers/gud/mobicore_kernelapi/wsm.h b/drivers/gud/mobicore_kernelapi/wsm.h
new file mode 100644
index 0000000..6877c53
--- /dev/null
+++ b/drivers/gud/mobicore_kernelapi/wsm.h
@@ -0,0 +1,35 @@
+/** @addtogroup MCD_MCDIMPL_DAEMON_SRV
+ * @{
+ * @file
+ *
+ * World shared memory definitions.
+ *
+ * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2012 -->
+ *
+ * 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.
+ */
+#ifndef WSM_H_
+#define WSM_H_
+
+#include "common.h"
+#include <linux/list.h>
+
+struct wsm {
+ void *virt_addr;
+ uint32_t len;
+ uint32_t handle;
+ void *phys_addr;
+ struct list_head list;
+};
+
+struct wsm *wsm_create(
+ void *virt_addr,
+ uint32_t len,
+ uint32_t handle,
+ void *phys_addr /*= NULL this may be unknown, so is can be omitted.*/
+);
+#endif /* WSM_H_ */
+
+/** @} */