diff options
Diffstat (limited to 'drivers/gud')
40 files changed, 9081 insertions, 0 deletions
diff --git a/drivers/gud/Kconfig b/drivers/gud/Kconfig new file mode 100644 index 0000000..a5c25aa --- /dev/null +++ b/drivers/gud/Kconfig @@ -0,0 +1,31 @@ +# +# MobiCore configuration +# +config MOBICORE_SUPPORT + tristate "Linux MobiCore Support" + depends on ARM_TRUSTZONE + ---help--- + Enable Linux Kernel MobiCore Support + +config MOBICORE_DEBUG + bool "MobiCore Module debug mode" + depends on MOBICORE_SUPPORT + ---help--- + Enable Debug mode in the MobiCore Driver. + It enables printing information about mobicore operations + +config MOBICORE_VERBOSE + bool "MobiCore Module verbose debug mode" + depends on MOBICORE_DEBUG + ---help--- + Enable Verbose Debug mode in the MobiCore Driver. + It enables printing extra information about mobicore operations + Beware: this is only useful for debuging deep in the driver because + it prints too much logs + + +config MOBICORE_API + tristate "Linux MobiCore API" + depends on MOBICORE_SUPPORT + ---help--- + Enable Linux Kernel MobiCore API diff --git a/drivers/gud/Makefile b/drivers/gud/Makefile new file mode 100644 index 0000000..bd5c0b2 --- /dev/null +++ b/drivers/gud/Makefile @@ -0,0 +1,32 @@ +# +# Makefile for the kernel mobicore drivers +# +GUD_ROOT_FOLDER := drivers/gud/ +# add our modules to kernel. +obj-$(CONFIG_MOBICORE_API) += mcKernelApi.o +obj-$(CONFIG_MOBICORE_SUPPORT) += mcDrvModule.o + +mcDrvModule-objs := MobiCoreDriver/mcDrvModule.o + +mcKernelApi-objs := MobiCoreKernelApi/main.o \ + MobiCoreKernelApi/clientlib.o \ + MobiCoreKernelApi/device.o \ + MobiCoreKernelApi/session.o \ + MobiCoreKernelApi/connection.o + +# Release mode by default +ccflags-y := -DNDEBUG +ccflags-y += -Wno-declaration-after-statement + +ccflags-$(CONFIG_MOBICORE_DEBUG) += -DDEBUG +ccflags-$(CONFIG_MOBICORE_VERBOSE) += -DDEBUG_VERBOSE + +# Choose one platform from the folder +MOBICORE_PLATFORM := $(shell (ls -1 $(GUD_ROOT_FOLDER)/MobiCoreDriver/platforms | tail -1) ) +# Use the available platform folder +ccflags-y += -I$(GUD_ROOT_FOLDER)/MobiCoreDriver/platforms/$(MOBICORE_PLATFORM) + + +ccflags-y += -I$(GUD_ROOT_FOLDER)/MobiCoreDriver/public +ccflags-y += -I$(GUD_ROOT_FOLDER)/include +ccflags-y += -I$(GUD_ROOT_FOLDER)/include/Mci
\ No newline at end of file diff --git a/drivers/gud/MobiCoreDriver/buildTag.h b/drivers/gud/MobiCoreDriver/buildTag.h new file mode 100644 index 0000000..28dae56 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/buildTag.h @@ -0,0 +1 @@ +#define MOBICORE_COMPONENT_BUILD_TAG "*** SAMSUNG V008 release ###" diff --git a/drivers/gud/MobiCoreDriver/mcDrvModule.c b/drivers/gud/MobiCoreDriver/mcDrvModule.c new file mode 100644 index 0000000..8e1c07f --- /dev/null +++ b/drivers/gud/MobiCoreDriver/mcDrvModule.c @@ -0,0 +1,3047 @@ +/** MobiCore driver module.(interface to the secure world SWD) + * @addtogroup MobiCore_Driver_Kernel_Module + * @{ + * @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 "mcDrvModule.h" +#include "mcDrvModuleLinuxApi.h" +#include "mcDrvModuleAndroid.h" +#include "mcDrvModuleFc.h" +#include "public/mcKernelApi.h" +#include "Mci/mcimcp.h" +#include "buildTag.h" + +/** MobiCore interrupt context data */ +static struct mcDrvKModCtx mcDrvKModCtx; + +/** MobiCore MCI information */ +static uint32_t mciBase = 0; +static mcpBuffer_ptr mcpBuffer = NULL; +/* +############################################################################# +## +## Convenience functions for Linux API functions +## +#############################################################################*/ +static int gotoCpu0(void); +static int gotoAllCpu(void) __attribute__ ((unused)); + +/*----------------------------------------------------------------------------*/ +static void initAndAddToList( + struct list_head *pItem, + struct list_head *pListHead +) +{ + INIT_LIST_HEAD(pItem); + + list_add(pItem, pListHead); +} + +/*----------------------------------------------------------------------------*/ +/** check if CPU supports the ARM TrustZone Security Extensions + * @return int TRUE or FALSE */ +static int hasSecurityExtensions( + 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 isSecureMode( + 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 isUserlandCallerPrivileged( + void +) { + MCDRV_DBG_VERBOSE("enter %u\n", current_euid()); + /* 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 unlockPagefromWsmL2Table( + struct page *pPage +){ + /* REV axh: check if we should do this. */ + SetPageDirty(pPage); + + /* release page, old api was page_cache_release() */ + ClearPageReserved(pPage); + put_page(pPage); +} + +/*----------------------------------------------------------------------------*/ +/* convert L2 PTE to page pointer */ +static struct page *l2PteToPage( + pte_t pte +) { + void *physPageAddr = (void *)((unsigned int)pte & PAGE_MASK); + unsigned int pfn = addrToPfn(physPageAddr); + struct page *pPage = pfn_to_page(pfn); + return pPage; +} + +/*----------------------------------------------------------------------------*/ +/* convert page pointer to L2 PTE */ +static pte_t pageToL2Pte( + struct page *pPage +) +{ + unsigned int pfn = page_to_pfn(pPage); + void *physAddr = pfnToAddr(pfn); + pte_t pte = (pte_t)((unsigned int)physAddr & PAGE_MASK); + return pte; +} + + +/*----------------------------------------------------------------------------*/ +static inline int lockUserPages( + struct task_struct *pTask, + void *virtStartPageAddr, + int nrOfPages, + struct page **pages +) +{ + int ret = 0; + int lockedPages = 0; + unsigned int i; + + do { + + /* lock user pages, must hold the mmap_sem to do this. */ + down_read(&(pTask->mm->mmap_sem)); + lockedPages = get_user_pages( + pTask, + pTask->mm, + (unsigned long)virtStartPageAddr, + nrOfPages, + 1, /* write access */ + 0, /* they say drivers should always + pass 0 here..... */ + pages, + NULL); /* we don't need the VMAs */ + up_read(&(pTask->mm->mmap_sem)); + + /* could as lock all pages? */ + if (lockedPages != nrOfPages) { + MCDRV_DBG_ERROR( + "get_user_pages() failed, " + "lockedPages=%d\n", + lockedPages); + ret = -ENOMEM; + /* check if an error has been returned. */ + if (lockedPages < 0) { + ret = lockedPages; + lockedPages = 0; + } + break; + } + + /* do cache maintenance on locked pages. */ + for (i = 0; i < nrOfPages; i++) + flush_dcache_page(pages[i]); + + } while (FALSE); + + + if (0 != ret) { + /* release all locked pages. */ + MCDRV_ASSERT(0 <= lockedPages); + for (i = 0; i < lockedPages; i++) + put_page(pages[i]); + } + + return ret; + +} + +/* +############################################################################# +## +## Driver implementation functions +## +#############################################################################*/ + +#ifdef MC_MEM_TRACES +/** MobiCore log previous char */ +static uint32_t mcLogPos = 0; +/** MobiCore log buffer structure */ +static struct mcTraceBuf *mcLogBuf = NULL; +static DEFINE_MUTEX(log_mutex); +/*----------------------------------------------------------------------------*/ +static void mobicore_read_log( + void +) { + uint32_t write_pos; + char *buff, *last_char; + + if (mcLogBuf == NULL) + return; + + write_pos = mcLogBuf->write_pos; + buff = mcLogBuf->buff + mcLogPos; + last_char = mcLogBuf->buff + write_pos; + + /* Nothing to do! */ + if(write_pos == mcLogPos) + return; + mutex_lock(&log_mutex); + while( buff != last_char) { + printk("%c", *(buff++)); + /* Wrap around */ + if(buff >= (char*)mcLogBuf + PAGE_SIZE) + buff = mcLogBuf->buff; + } + mcLogPos = write_pos; + mutex_unlock(&log_mutex); +} + +/*----------------------------------------------------------------------------*/ +/** + * Setup mobicore kernel log + */ +static void mcKernelModule_setupLog( + void +) { + void *logPage; + unsigned long physLogPage; + union fcGeneric fcLog; + int ret; + + /* We need to go to CPU0 because we are going to do some SMC calls and + * they will otherwise fail because the Mobicore Monitor resides on + * CPU0(for Cortex A9 and lower) */ + ret = gotoCpu0(); + if (0 != ret) { + MCDRV_DBG("changing core failed!\n"); + return; + } + + mcLogPos = 0; + do { + if (!(logPage = (void *)get_zeroed_page(GFP_KERNEL))) { + MCDRV_DBG_ERROR("Failed to get page for logger!"); + break; + } + physLogPage = virt_to_phys(logPage); + mcLogBuf = logPage; + + memset(&fcLog, 0, sizeof(fcLog)); + fcLog.asIn.cmd = MC_FC_NWD_TRACE; + fcLog.asIn.param[0] = physLogPage; + fcLog.asIn.param[1] = PAGE_SIZE; + + MCDRV_DBG("fcLog virtPage=%p phyLogPage=%p ", logPage, (void*)physLogPage); + mcFastCall(&fcLog); + MCDRV_DBG("fcInfo out ret=0x%08x", fcLog.asOut.ret); + + if (fcLog.asOut.ret) { + MCDRV_DBG_ERROR("Mobicore shared traces setup failed!"); + free_page((unsigned long)logPage); + mcLogBuf = NULL; + break; + } + } while(FALSE); + + /* Reset the mask of the current process to All cpus */ + gotoAllCpu(); + + MCDRV_DBG_VERBOSE("fcLog Logger version %u\n", mcLogBuf->version); +} +#endif //#ifdef MC_MEM_TRACES + + +/*----------------------------------------------------------------------------*/ +/* check if caller is MobiCore Daemon */ +static unsigned int isCallerMcDaemon( + struct mcInstance *pInstance +) +{ + return ((NULL != pInstance) + && (mcDrvKModCtx.daemonInst == pInstance)); +} + + +/*----------------------------------------------------------------------------*/ +static struct mcInstance *getInstance( + struct file *pFile +) { + MCDRV_ASSERT(NULL != pFile); + + return (struct mcInstance *)(pFile->private_data); +} + + +/*----------------------------------------------------------------------------*/ +/* get a unique ID */ +static unsigned int getMcKModUniqueId( + void +) +{ + return (unsigned int)atomic_inc_return( + &(mcDrvKModCtx.uniqueCounter)); +} + + +/*----------------------------------------------------------------------------*/ +/* get a unique ID */ +static struct l2Table *getL2TableKernelVirt( + struct mcL2TablesDescr *pDescr +) +{ + MCDRV_ASSERT(NULL != pDescr); + MCDRV_ASSERT(NULL != pDescr->pChunk); + MCDRV_ASSERT(NULL != pDescr->pChunk->kernelVirt); + return &(pDescr->pChunk->kernelVirt->table[pDescr->idx]); +} + +/*----------------------------------------------------------------------------*/ +/* get a unique ID */ +static struct l2Table *getL2TablePhys( + struct mcL2TablesDescr *pDescr +) +{ + MCDRV_ASSERT(NULL != pDescr); + MCDRV_ASSERT(NULL != pDescr->pChunk); + MCDRV_ASSERT(NULL != pDescr->pChunk->phys); + return &(pDescr->pChunk->phys->table[pDescr->idx]); +} + +/*----------------------------------------------------------------------------*/ +/* get a unique ID */ +static unsigned int isWsmL2InUse( + struct mcL2TablesDescr *pWsmL2Descr +) +{ + return (0 != (pWsmL2Descr->flags & + (MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_APP + | MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_MC))); +} + + + +/*----------------------------------------------------------------------------*/ +static struct mcL2TablesDescr *findWsmL2ByHandle( + unsigned int handle +) { + struct mcL2TablesDescr *pTmpDescr; + struct mcL2TablesDescr *pWsmL2TableDescr = NULL; + + list_for_each_entry( + pTmpDescr, + &(mcDrvKModCtx.wsmL2Descriptors), + list + ) { + if (handle == pTmpDescr->handle) { + pWsmL2TableDescr = pTmpDescr; + break; + } + } + + return pWsmL2TableDescr; +} + +/* +############################################################################# +## +## L2 Table Pool +## +#############################################################################*/ + +/*----------------------------------------------------------------------------*/ +static struct mcL2TablesDescr *allocateWsmL2TableContainer( + struct mcInstance *pInstance +) { + int ret = 0; + struct mcL2TablesChunk *pWsmL2TablesChunk = NULL; + struct mcL2Page *pWsmL2Page = NULL; + struct mcL2TablesDescr *pWsmL2TableDescr = NULL; + struct page *pPage; + unsigned int i = 0; + + do { + /* allocate a WSM L2 descriptor */ + pWsmL2TableDescr = kmalloc(sizeof(*pWsmL2TableDescr), + GFP_KERNEL); + if (NULL == pWsmL2TableDescr) { + ret = -ENOMEM; + MCDRV_DBG_ERROR("out of memory\n"); + break; + } + /* clean */ + memset(pWsmL2TableDescr, 0, sizeof(*pWsmL2TableDescr)); + pWsmL2TableDescr->handle = getMcKModUniqueId(); + pWsmL2TableDescr->pInstance = pInstance; + + /* add to global list. */ + initAndAddToList( + &(pWsmL2TableDescr->list), + &(mcDrvKModCtx.wsmL2Descriptors)); + + /* walk though list to find free chunk. */ + list_for_each_entry( + pWsmL2TablesChunk, + &(mcDrvKModCtx.wsmL2Chunks), + list + ) { + for (i = 0; i < MC_DRV_KMOD_L2_TABLE_PER_PAGES; i++) { + if (0 == + (pWsmL2TablesChunk->usageBitmap + & (1U << i)) + ) { + /* found a chunk, pL2TablesChunk and i + are set. */ + pWsmL2Page = pWsmL2TablesChunk->kernelVirt; + break; + } + } + if (NULL != pWsmL2Page) + break; + } /* end while */ + + if (NULL == pWsmL2Page) { + pWsmL2Page = (struct mcL2Page *)get_zeroed_page(GFP_KERNEL); + if (NULL == pWsmL2Page) { + ret = -ENOMEM; + break; + } + + /* Actually, locking is not necessary, because kernel + memory is not supposed to get swapped out. But + we play safe.... */ + pPage = virt_to_page(pWsmL2Page); + SetPageReserved(pPage); + + /* allocate a descriptor */ + pWsmL2TablesChunk = kmalloc(sizeof(*pWsmL2TablesChunk), + GFP_KERNEL); + if (NULL == pWsmL2TablesChunk) { + kfree(pWsmL2Page); + ret = -ENOMEM; + break; + } + /* initialize */ + memset(pWsmL2TablesChunk, 0, + sizeof(*pWsmL2TablesChunk)); + + pWsmL2TablesChunk->kernelVirt = pWsmL2Page; + pWsmL2TablesChunk->pPage = pPage; + pWsmL2TablesChunk->phys = (void *)virt_to_phys(pWsmL2Page); + + /* init add to list. */ + initAndAddToList( + &(pWsmL2TablesChunk->list), + &(mcDrvKModCtx.wsmL2Chunks)); + + /* use first table */ + i = 0; + } + + /* set chunk usage */ + pWsmL2TablesChunk->usageBitmap |= (1U << i); + + + /* set chunk reference */ + pWsmL2TableDescr->pChunk = pWsmL2TablesChunk; + pWsmL2TableDescr->idx = i; + + MCDRV_DBG_VERBOSE("allocateWsmL2TableContainer():chunkPhys=%p,idx=%d\n", + pWsmL2TablesChunk->phys, i); + + } while (FALSE); + + if (0 != ret) { + if (NULL != pWsmL2TableDescr) { + /* remove from list */ + list_del(&(pWsmL2TablesChunk->list)); + /* free memory */ + kfree(pWsmL2TableDescr); + pWsmL2TableDescr = NULL; + } + } + + return pWsmL2TableDescr; +} + +/*----------------------------------------------------------------------------*/ +static void freeWsmL2TableContainer( + struct mcL2TablesDescr *pL2TableDescr +) +{ + struct mcL2TablesChunk *pWsmL2TablesChunk; + unsigned int idx; + + MCDRV_ASSERT(NULL != pL2TableDescr); + + pWsmL2TablesChunk = pL2TableDescr->pChunk; + MCDRV_ASSERT(NULL != pWsmL2TablesChunk); + + /* clean usage flag */ + idx = pL2TableDescr->idx; + MCDRV_ASSERT(MC_DRV_KMOD_L2_TABLE_PER_PAGES > idx); + pWsmL2TablesChunk->usageBitmap &= ~(1U << idx); + + /* if nobody uses this chunk, we can release it. */ + if (0 == pWsmL2TablesChunk->usageBitmap) { + MCDRV_ASSERT(NULL != pWsmL2TablesChunk->pPage); + ClearPageReserved(pWsmL2TablesChunk->pPage); + + MCDRV_ASSERT(NULL != pWsmL2TablesChunk->kernelVirt); + free_page((unsigned long)pWsmL2TablesChunk->kernelVirt); + + /* remove from list */ + list_del(&(pWsmL2TablesChunk->list)); + + /* free memory */ + kfree(pWsmL2TablesChunk); + } + + return; +} + + + +/*----------------------------------------------------------------------------*/ +/** + * Create a L2 table in a WSM container that has been allocates previously. + * + * @param pTask pointer to task owning WSM + * @param wsmBuffer user space WSM start + * @param wsmLen WSM length + * @param pL2TableDescr Pointer to L2 table details + */ +static int createWsmL2Table( + struct task_struct *pTask, + void *wsmBuffer, + unsigned int wsmLen, + struct mcL2TablesDescr *pL2TableDescr +) +{ + int ret = 0; + unsigned int i, nrOfPages; + void *virtAddrPage; + struct page *pPage; + struct l2Table *pL2Table; + struct page **pL2TableAsArrayOfPointersToPage; + + /* pTask can be null when called from kernel space + MCDRV_ASSERT(NULL != pTask); */ + MCDRV_ASSERT(NULL != wsmBuffer); + MCDRV_ASSERT(0 != wsmLen); + MCDRV_ASSERT(NULL != pL2TableDescr); + + MCDRV_DBG_VERBOSE("WSM addr=0x%p, len=0x%08x\n", wsmBuffer, wsmLen); + + /* Check if called from kernel space wsmBuffer is actually + * vmalloced or not */ + if (pTask == NULL && !is_vmalloc_addr(wsmBuffer)) { + MCDRV_DBG_ERROR("WSM addr is not a vmalloc address"); + return -EINVAL; + } + + pL2Table = getL2TableKernelVirt(pL2TableDescr); + /* 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. */ + pL2TableAsArrayOfPointersToPage = (struct page **)pL2Table; + + do { + + /* no size > 1Mib supported */ + if (wsmLen > SZ_1M) { + MCDRV_DBG_ERROR("size > 1 MiB\n"); + ret = -EINVAL; + break; + } + + /* calculate page usage */ + virtAddrPage = getPageStart(wsmBuffer); + nrOfPages = getNrOfPagesForBuffer(wsmBuffer, wsmLen); + + + MCDRV_DBG_VERBOSE("virtAddr pageStart=0x%p,pages=%d\n", + virtAddrPage, + nrOfPages); + + /* L2 table can hold max 1MiB in 256 pages. */ + if (SZ_1M < (nrOfPages*PAGE_SIZE)) { + MCDRV_DBG_ERROR("WSM paged exceed 1 MiB\n"); + ret = -EINVAL; + break; + } + + /* Request comes from user space */ + if (pTask != 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 = lockUserPages( + pTask, + virtAddrPage, + nrOfPages, + pL2TableAsArrayOfPointersToPage); + if (0 != ret) { + MCDRV_DBG_ERROR("lockUserPages() failed\n"); + break; + } + } + /* Request comes from kernel space(vmalloc buffer) */ + else { + void *uaddr = wsmBuffer; + for (i = 0; i < nrOfPages; i++) { + pPage = vmalloc_to_page(uaddr); + if (!pPage) { + MCDRV_DBG_ERROR( + "vmalloc_to_Page()" + " failed to map address\n"); + ret = -EINVAL; + break; + } + get_page(pPage); + /* Lock the page in memory, it can't be swapped + * out */ + SetPageReserved(pPage); + pL2TableAsArrayOfPointersToPage[i] = pPage; + uaddr += PAGE_SIZE; + } + } + + pL2TableDescr->nrOfPages = nrOfPages; + pL2TableDescr->flags |= MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_APP; + + /* create L2 Table entries. "pL2Page->table" contains a list of + page pointers here. For a proper cleanup we have to ensure that + the following code either works and "pL2table" contains a valid + L2 table - or fails and "pL2Page->table" contains the list of + page pointers. Any mixed contents will make cleanup difficult.*/ + + for (i = 0; i < nrOfPages; i++) { + pte_t pte; + pPage = pL2TableAsArrayOfPointersToPage[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 = pageToL2Pte(pPage) + | 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 + ; + + pL2Table->tableEntries[i] = pte; + MCDRV_DBG_VERBOSE("L2 entry %d: 0x%08x\n", i, + (unsigned int)(pte)); + } + + /* ensure rest of table is empty */ + while (i < 255) + pL2Table->tableEntries[i++] = (pte_t)0; + + } while (FALSE); + + return ret; +} + + +/*----------------------------------------------------------------------------*/ +/** + * Remove a L2 table in a WSM container. Afterwards the container may be + * released. + * + * @param pL2TableDescr Pointer to L2 table details + */ + +static void destroyWsmL2Table( + struct mcL2TablesDescr *pL2TableDescr +) +{ + unsigned int i; + struct l2Table *pL2Table; + + MCDRV_ASSERT(NULL != pL2TableDescr); + /* this should not happen, as we have no empty tables. */ + MCDRV_ASSERT(!isWsmL2InUse(pL2TableDescr)); + + /* found the table, now release the resources. */ + MCDRV_DBG_VERBOSE("clear L2 table, physBase=%p, nrOfPages=%d\n", + getL2TablePhys(pL2TableDescr), + pL2TableDescr->nrOfPages); + + pL2Table = getL2TableKernelVirt(pL2TableDescr); + + /* release all locked user space pages */ + for (i = 0; i < pL2TableDescr->nrOfPages; i++) { + /* convert physical entries from L2 table to page pointers */ + pte_t pte = + getL2TableKernelVirt(pL2TableDescr)->tableEntries[i]; + struct page *pPage = l2PteToPage(pte); + unlockPagefromWsmL2Table(pPage); + } + + /* remember that all pages have been freed */ + pL2TableDescr->nrOfPages = 0; + + return; +} + + +/* +############################################################################# +## +## Helper functions +## +#############################################################################*/ +/*----------------------------------------------------------------------------*/ +#define FREE_FROM_SWD TRUE +#define FREE_FROM_NWD FALSE +static void freeWsmL2Table( + struct mcL2TablesDescr *pWsmL2TableDescr, + unsigned int isSwd +) +{ + if (isSwd) { + pWsmL2TableDescr->flags &= + ~MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_MC; + } else { + pWsmL2TableDescr->flags &= + ~MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_APP; + pWsmL2TableDescr->pInstance = NULL; + } + + /* release if Nwd and Swd/MC do no longer use it. */ + if (isWsmL2InUse(pWsmL2TableDescr)) { + MCDRV_DBG_WARN( + "WSM L2 table still in use: physBase=%p, " + "nrOfPages=%d\n", + getL2TablePhys(pWsmL2TableDescr), + pWsmL2TableDescr->nrOfPages); + } else { + destroyWsmL2Table(pWsmL2TableDescr); + freeWsmL2TableContainer(pWsmL2TableDescr); + + list_del(&(pWsmL2TableDescr->list)); + + kfree(pWsmL2TableDescr); + } + return; +} + +/*----------------------------------------------------------------------------*/ +/** Allocate L2 table and map buffer into it. That is, create respective table + entries must hold Semaphore mcDrvKModCtx.wsmL2Sem */ +static struct mcL2TablesDescr *newWsmL2Table( + struct mcInstance *pInstance, + struct task_struct *pTask, + void *wsmBuffer, + unsigned int wsmLen +) { + int ret = 0; + struct mcL2TablesDescr *pL2TableDescr; + + do { + pL2TableDescr = allocateWsmL2TableContainer(pInstance); + if (NULL == pL2TableDescr) { + MCDRV_DBG_ERROR( + "allocateWsmL2TableContainer() failed\n"); + break; + } + + /* create the L2 page for the WSM */ + ret = createWsmL2Table( + pTask, + wsmBuffer, + wsmLen, + pL2TableDescr); + if (0 != ret) { + MCDRV_DBG_ERROR("createWsmL2Table() failed\n"); + freeWsmL2Table(pL2TableDescr, FREE_FROM_NWD); + pL2TableDescr = NULL; + break; + } + + } while (FALSE); + + + return pL2TableDescr; +} + +/* +############################################################################# +## +## IoCtl handler +## +#############################################################################*/ + +/** + * Map a virtual memory buffer structure to Mobicore + * @param pInstance + * @param addr address of the buffer(NB it must be kernel virtual!) + * @param len buffer length + * @param pHandle pointer to handle + * @param physWsmL2Table pointer to physical L2 table(?) + * + * @return 0 if no error + * + */ +/*----------------------------------------------------------------------------*/ +int mobicore_map_vmem( + struct mcInstance *pInstance, + void *addr, + uint32_t len, + uint32_t *pHandle, + void **physWsmL2Table +) +{ + int ret = 0; + struct mcL2TablesDescr *pWsmL2TableDescr = NULL; + MCDRV_ASSERT(NULL != pInstance); + + MCDRV_DBG_VERBOSE("enter\n"); + + do { + if (0 == len) { + MCDRV_DBG_ERROR("len=0 is not supported!\n"); + ret = -EINVAL; + break; + } + + /* try to get the semaphore */ + ret = down_interruptible(&(mcDrvKModCtx.wsmL2Sem)); + if (0 != ret) { + MCDRV_DBG_ERROR("down_interruptible() failed with %d\n", ret); + ret = -ERESTARTSYS; + break; + } + + do { + pWsmL2TableDescr = newWsmL2Table( + pInstance, + NULL, + addr, + len); + + if (NULL == pWsmL2TableDescr) { + MCDRV_DBG_ERROR("newWsmL2Table() failed\n"); + ret = -EINVAL; + break; + } + + /* set response */ + *pHandle = pWsmL2TableDescr->handle; + *physWsmL2Table = + (void *)getL2TablePhys(pWsmL2TableDescr); + MCDRV_DBG_VERBOSE("handle: %d, phys=%p\n", + *pHandle, + (void *)(*physWsmL2Table)); + + } while (FALSE); + + /* release semaphore */ + up(&(mcDrvKModCtx.wsmL2Sem)); + + } while (FALSE); + + MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret); + + return ret; +} +EXPORT_SYMBOL(mobicore_map_vmem); +/*----------------------------------------------------------------------------*/ +/** + * + * @param pInstance + * @param arg + * + * @return 0 if no error + * + */ +static int handleIoCtlAppRegisterWsmL2( + struct mcInstance *pInstance, + union mcIoCtlAppRegWsmL2Params *pUserParams +) +{ + int ret = 0; + union mcIoCtlAppRegWsmL2Params params; + struct mcL2TablesDescr *pWsmL2TableDescr = NULL; + struct pid *pPidStruct = NULL; + struct task_struct *pTask = current; + MCDRV_ASSERT(NULL != pInstance); + + MCDRV_DBG_VERBOSE("enter\n"); + + do { + /* get use parameters */ + ret = copy_from_user( + &(params.in), + &(pUserParams->in), + sizeof(params.in)); + if (0 != ret) { + MCDRV_DBG_ERROR("copy_from_user() failed\n"); + break; + } + + /* daemon can do this for another task. */ + if (0 != params.in.pid) { + MCDRV_DBG_ERROR("pid != 0 unsupported\n"); + ret = -EINVAL; + break; + /* + MCDRV_DBG("PID=%d\n", params.in.pid); + if (isCallerMcDaemon(pInstance)) + { + MCDRV_DBG_ERROR( + "pid != 0 only allowed fore daemon\n"); + ret = -EFAULT; + break; + } + + Don't use find_vpid(), as this requires holding some + locks. Better user find_get_pid(), which is take care of + the locks internally. + pPidStruct = find_get_pid(params.in.pid); + if (NULL != pPidStruct) + { + MCDRV_DBG_ERROR("find_get_pid() failed\n"); + ret = -EFAULT; + break; + } + now we have a unique reference to another task. We must + release this reference using put_pid() when we are done + pTask = pid_task(pPidStruct, PIDTYPE_PID); + if (NULL != pTask) + { + MCDRV_DBG_ERROR("pid_task() failed\n"); + ret = -EFAULT; + break; + } + */ + } + if (0 == params.in.len) { + MCDRV_DBG_ERROR("len=0 is not supported!\n"); + ret = -EINVAL; + break; + } + + /* try to get the semaphore */ + ret = down_interruptible(&(mcDrvKModCtx.wsmL2Sem)); + if (0 != ret) { + MCDRV_DBG_ERROR("down_interruptible() failed with %d\n", ret); + ret = -ERESTARTSYS; + break; + } + + do { + pWsmL2TableDescr = newWsmL2Table( + pInstance, + pTask, + (void *)(params.in.buffer), + params.in.len); + + if (NULL == pWsmL2TableDescr) { + MCDRV_DBG_ERROR("newWsmL2Table() failed\n"); + ret = -EINVAL; + break; + } + + /* if the daemon does this, we set the MC lock */ + if (isCallerMcDaemon(pInstance)) + pWsmL2TableDescr->flags |= + MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_MC; + + /* set response */ + memset(¶ms.out, 0, sizeof(params.out)); + params.out.handle = pWsmL2TableDescr->handle; + /* TODO: return the physical address for daemon only, + otherwise set NULL */ + /* if (isCallerMcDaemon(pInstance))... */ + params.out.physWsmL2Table = + (uint32_t)getL2TablePhys(pWsmL2TableDescr); + + MCDRV_DBG_VERBOSE("handle: %d, phys=%p\n", + params.out.handle, + (void *)(params.out.physWsmL2Table)); + + + /* copy L2Table to user space */ + ret = copy_to_user( + &(pUserParams->out), + &(params.out), + sizeof(params.out)); + if (0 != ret) { + MCDRV_DBG_ERROR("copy_to_user() failed\n"); + + /* free the table again, as app does not know + about anything. */ + if (isCallerMcDaemon(pInstance)) { + pWsmL2TableDescr->flags &= + ~MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_MC; + } + freeWsmL2Table(pWsmL2TableDescr, FREE_FROM_NWD); + pWsmL2TableDescr = NULL; + break; + } + + } while (FALSE); + + /* release semaphore */ + up(&(mcDrvKModCtx.wsmL2Sem)); + + } while (FALSE); + + + + /* release PID struct reference */ + if (NULL != pPidStruct) + put_pid(pPidStruct); + + + MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret); + + return ret; +} + + +/*----------------------------------------------------------------------------*/ +/** + * Unmap a virtual memory buffer from mobicore + * @param pInstance + * @param handle + * + * @return 0 if no error + * + */ +int mobicore_unmap_vmem( + struct mcInstance *pInstance, + uint32_t handle +) +{ + int ret = 0; + struct mcL2TablesDescr *pWsmL2TableDescr = NULL; + + MCDRV_ASSERT(NULL != pInstance); + MCDRV_DBG_VERBOSE("enter\n"); + + do { + /* try to get the semaphore */ + ret = down_interruptible(&(mcDrvKModCtx.wsmL2Sem)); + if (0 != ret) { + MCDRV_DBG_ERROR("processOpenSession() failed with %d\n", ret); + ret = -ERESTARTSYS; + break; + } + + do { + pWsmL2TableDescr = findWsmL2ByHandle(handle); + if (NULL == pWsmL2TableDescr) { + ret = -EINVAL; + MCDRV_DBG_ERROR("entry not found\n"); + break; + } + + if (pInstance != pWsmL2TableDescr->pInstance) { + ret = -EINVAL; + MCDRV_DBG_ERROR("instance does no own it\n"); + break; + } + + /* free table (if no further locks exist) */ + freeWsmL2Table(pWsmL2TableDescr, FREE_FROM_NWD); + pWsmL2TableDescr = NULL; + /* there are no out parameters */ + } while (FALSE); + /* release semaphore */ + up(&(mcDrvKModCtx.wsmL2Sem)); + + } while (FALSE); + + MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret); + + return ret; +} +EXPORT_SYMBOL(mobicore_unmap_vmem); +/*----------------------------------------------------------------------------*/ +/** + * + * @param pInstance + * @param arg + * + * @return 0 if no error + * + */ +static int handleIoCtlAppUnregisterWsmL2( + struct mcInstance *pInstance, + struct mcIoCtlAppUnregWsmL2Params *pUserParams +) +{ + int ret = 0; + struct mcIoCtlAppUnregWsmL2Params params; + struct mcL2TablesDescr *pWsmL2TableDescr = NULL; + + MCDRV_ASSERT(NULL != pInstance); + MCDRV_DBG_VERBOSE("enter\n"); + + do { + ret = copy_from_user( + &(params.in), + &(pUserParams->in), + sizeof(params.in)); + + if (0 != ret) { + MCDRV_DBG_ERROR("copy_from_user\n"); + break; + } + + /* try to get the semaphore */ + ret = down_interruptible(&(mcDrvKModCtx.wsmL2Sem)); + if (0 != ret) { + MCDRV_DBG_ERROR("down_interruptible() failed with %d\n", ret); + ret = -ERESTARTSYS; + break; + } + + do { + /* daemon can do this for another task. */ + if (0 != params.in.pid) { + MCDRV_DBG_ERROR("pid != 0 unsupported\n"); + ret = -EINVAL; + break; + } + + pWsmL2TableDescr = findWsmL2ByHandle(params.in.handle); + if (NULL == pWsmL2TableDescr) { + ret = -EINVAL; + MCDRV_DBG_ERROR("entry not found\n"); + break; + } + + if (isCallerMcDaemon(pInstance)) { + /* if daemon does this, we have to release the + MobiCore lock. */ + pWsmL2TableDescr->flags &= + ~MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_MC; + } else if (pInstance != pWsmL2TableDescr->pInstance) { + ret = -EINVAL; + MCDRV_DBG_ERROR("instance does no own it\n"); + break; + } + + /* free table (if no further locks exist) */ + freeWsmL2Table(pWsmL2TableDescr, FREE_FROM_NWD); + pWsmL2TableDescr = NULL; + + /* there are no out parameters */ + + } while (FALSE); + + /* release semaphore */ + up(&(mcDrvKModCtx.wsmL2Sem)); + + } while (FALSE); + + MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret); + + return ret; +} + + +/*----------------------------------------------------------------------------*/ +static int handleIoCtlDaemonLockWsmL2( + struct mcInstance *pInstance, + struct mcIoCtlDaemonLockWsmL2Params *pUserParams +) +{ + int ret = 0; + struct mcIoCtlDaemonLockWsmL2Params params; + struct mcL2TablesDescr *pWsmL2TableDescr = NULL; + + MCDRV_ASSERT(NULL != pInstance); + MCDRV_DBG_VERBOSE("enter\n"); + + do { + if (!isCallerMcDaemon(pInstance)) { + MCDRV_DBG_ERROR("caller not MobiCore Daemon\n"); + ret = -EFAULT; + break; + } + + ret = copy_from_user( + &(params.in), + &(pUserParams->in), + sizeof(params.in)); + + if (0 != ret) { + MCDRV_DBG_ERROR("copy_from_user\n"); + break; + } + /* try to get the semaphore */ + ret = down_interruptible(&(mcDrvKModCtx.wsmL2Sem)); + if (0 != ret) { + MCDRV_DBG_ERROR("down_interruptible() failed with %d\n", ret); + ret = -ERESTARTSYS; + break; + } + + do { + pWsmL2TableDescr = findWsmL2ByHandle(params.in.handle); + if (NULL == pWsmL2TableDescr) { + ret = -EINVAL; + MCDRV_DBG_ERROR("entry not found\n"); + break; + } + if (pInstance != pWsmL2TableDescr->pInstance) { + ret = -EINVAL; + MCDRV_DBG_ERROR("instance does no own it\n"); + break; + } + + /* lock entry */ + if (0 != (pWsmL2TableDescr->flags & + MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_MC)) { + MCDRV_DBG_WARN("entry already locked\n"); + } + pWsmL2TableDescr->flags |= + MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_MC; + + /* prepare response */ + memset(&(params.out), 0, sizeof(params.out)); + params.out.physWsmL2Table = + (uint32_t)getL2TablePhys(pWsmL2TableDescr); + + /* copy to user space */ + ret = copy_to_user( + &(pUserParams->out), + &(params.out), + sizeof(params.out)); + if (0 != ret) { + MCDRV_DBG_ERROR("copy_to_user() failed\n"); + + /* undo, as userspace did not get it. */ + pWsmL2TableDescr->flags |= + MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_MC; + break; + } + + } while (FALSE); + + /* release semaphore */ + up(&(mcDrvKModCtx.wsmL2Sem)); + + } while (FALSE); + + MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret); + + return ret; +} + + +/*----------------------------------------------------------------------------*/ +static int handleIoCtlDaemonUnlockWsmL2( + struct mcInstance *pInstance, + struct mcIoCtlDaemonUnlockWsmL2Params *pUserParams +) +{ + int ret = 0; + struct mcIoCtlDaemonUnlockWsmL2Params params; + struct mcL2TablesDescr *pWsmL2TableDescr = NULL; + + MCDRV_ASSERT(NULL != pInstance); + MCDRV_DBG_VERBOSE("enter\n"); + + do { + if (!isCallerMcDaemon(pInstance)) { + MCDRV_DBG_ERROR("caller not MobiCore Daemon\n"); + ret = -EFAULT; + break; + } + + ret = copy_from_user( + &(params.in), + &(pUserParams->in), + sizeof(params.in)); + + if (0 != ret) { + MCDRV_DBG_ERROR("copy_from_user\n"); + break; + } + /* try to get the semaphore */ + ret = down_interruptible(&(mcDrvKModCtx.wsmL2Sem)); + if (0 != ret) { + MCDRV_DBG_ERROR("down_interruptible() failed with %d\n", ret); + ret = -ERESTARTSYS; + break; + } + + do { + pWsmL2TableDescr = findWsmL2ByHandle(params.in.handle); + if (NULL == pWsmL2TableDescr) { + ret = -EINVAL; + MCDRV_DBG_ERROR("entry not found\n"); + break; + } + if (pInstance != pWsmL2TableDescr->pInstance) { + ret = -EINVAL; + MCDRV_DBG_ERROR("instance does no own it\n"); + break; + } + + /* lock entry */ + if (0 == (pWsmL2TableDescr->flags & + MC_WSM_L2_CONTAINER_WSM_LOCKED_BY_MC)) { + MCDRV_DBG_WARN("entry is not locked locked\n"); + } + + /* free table (if no further locks exist) */ + freeWsmL2Table(pWsmL2TableDescr, FREE_FROM_SWD); + pWsmL2TableDescr = NULL; + + /* there are no out parameters */ + + } while (FALSE); + + } while (FALSE); + + + MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret); + + return ret; +} + +/*----------------------------------------------------------------------------*/ +static inline void freeContinguousPages( + void *addr, + unsigned int size +) +{ + /* clears the reserved bit of each page and frees this page */ + struct page *pPage = virt_to_page(addr); + int i; + for (i = 0; i < size; i++) { + MCDRV_DBG_VERBOSE("free page at 0x%p\n", pPage); + ClearPageReserved(pPage); + pPage++; + } + /* REV luh: see man kmalloc */ + free_pages((unsigned long)addr, sizeToOrder(size)); +} + +/*----------------------------------------------------------------------------*/ +/** + * Free a WSM buffer allocated with mobicore_allocate_wsm + * @param pInstance + * @param handle handle of the buffer + * + * @return 0 if no error + * + */ +int mobicore_free( + struct mcInstance *pInstance, + uint32_t handle +) +{ + int ret = 0; + struct mc_tuple *pTuple; + unsigned int i; + + do { + /* search for the given address in the tuple list */ + for (i = 0; i < MC_DRV_KMOD_TUPLE_NR; i++) { + pTuple = &(pInstance->tuple[i]); + if (pTuple->handle == handle) + break; + } + if (MC_DRV_KMOD_TUPLE_NR == i) { + MCDRV_DBG_ERROR("tuple not found\n"); + ret = -EFAULT; + break; + } + + MCDRV_DBG_VERBOSE("physAddr=0x%p, virtAddr=0x%p\n", + pTuple->physAddr, pTuple->virtKernelAddr); + + freeContinguousPages(pTuple->virtKernelAddr, pTuple->numPages); + + memset(pTuple, 0, sizeof(*pTuple)); + + /* there are no out parameters */ + + } while (FALSE); + + + return ret; +} +EXPORT_SYMBOL(mobicore_free); +/*----------------------------------------------------------------------------*/ + +/** + * + * @param pInstance + * @param arg + * + * @return 0 if no error + * + */ +static int handleIoCtlFree( + struct mcInstance *pInstance, + union mcIoCtltoFreeParams *pUserParams +) +{ + int ret = 0; + union mcIoCtltoFreeParams params; + + + MCDRV_ASSERT(NULL != pInstance); + MCDRV_DBG_VERBOSE("enter\n"); + + do { + ret = copy_from_user( + &(params.in), + &(pUserParams->in), + sizeof(params.in)); + + if (0 != ret) { + MCDRV_DBG_ERROR("copy_from_user\n"); + break; + } + + /* daemon can do this for another task. */ + if (0 != params.in.pid) { + MCDRV_DBG_ERROR("pid != 0 unsupported\n"); + ret = -EINVAL; + break; + } + + ret = mobicore_free(pInstance, params.in.handle); + + /* there are no out parameters */ + + } while (FALSE); + + MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret); + + return ret; + +} + + +/*----------------------------------------------------------------------------*/ +/** + * + * @param pInstance + * @param arg + * + * @return 0 if no error + * + */ +static int handleIoCtlInfo( + struct mcInstance *pInstance, + union mcIoCtlInfoParams *pUserParams +) +{ + int ret = 0; + union mcIoCtlInfoParams params; + union mcFcInfo fcInfo; + + + MCDRV_ASSERT(NULL != pInstance); + MCDRV_DBG_VERBOSE("enter\n"); + + do { + /* only the MobiCore Daemon is allowed to call this function */ + if (!isCallerMcDaemon(pInstance)) { + MCDRV_DBG_ERROR("caller not MobiCore Daemon\n"); + ret = -EFAULT; + break; + } + + ret = copy_from_user( + &(params.in), + &(pUserParams->in), + sizeof(params.in)); + + if (0 != ret) { + MCDRV_DBG_ERROR("copy_from_user\n"); + break; + } + + + memset(&fcInfo, 0, sizeof(fcInfo)); + fcInfo.asIn.cmd = MC_FC_INFO; + fcInfo.asIn.extInfoId = params.in.extInfoId; + + MCDRV_DBG( + "fcInfo in cmd=0x%08x, extInfoid=0x%08x " + "rfu=(0x%08x, 0x%08x)\n", + fcInfo.asIn.cmd, + fcInfo.asIn.extInfoId, + fcInfo.asIn.rfu[0], + fcInfo.asIn.rfu[1]); + + mcFastCall(&(fcInfo.asGeneric)); + + MCDRV_DBG( + "fcInfo out resp=0x%08x, ret=0x%08x " + "state=0x%08x, extInfo=0x%08x\n", + fcInfo.asOut.resp, + fcInfo.asOut.ret, + fcInfo.asOut.state, + fcInfo.asOut.extInfo); + + ret = convertFcRet(fcInfo.asOut.ret); + if (0 != ret) + break; + + memset(&(params.out), 0, sizeof(params.out)); + params.out.state = fcInfo.asOut.state; + params.out.extInfo = fcInfo.asOut.extInfo; + + ret = copy_to_user( + &(pUserParams->out), + &(params.out), + sizeof(params.out)); + + if (0 != ret) { + MCDRV_DBG_ERROR("copy_to_user\n"); + break; + } + } while (FALSE); + + MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret); + + return ret; +} + +/*----------------------------------------------------------------------------*/ +/** + * + * @param pInstance + * @param arg + * + * @return 0 if no error + * + */ +static int handleIoCtlYield( + struct mcInstance *pInstance +) +{ + int ret = 0; + union mcFcSYield fcSYield; + + MCDRV_ASSERT(NULL != pInstance); + + /* Can't allow Yields while preparing to sleep */ + if(mcpBuffer->mcFlags.sleepMode.SleepReq == MC_FLAG_REQ_TO_SLEEP) + return ret; + + /* avoid putting debug output here, as we do this very often */ + /* MCDRV_DBG("enter\n"); */ + + do { + /* only the MobiCore Daemon is allowed to call this function */ + if (!isCallerMcDaemon(pInstance)) { + MCDRV_DBG_ERROR("caller not MobiCore Daemon\n"); + ret = -EFAULT; + break; + } + + memset(&fcSYield, 0, sizeof(fcSYield)); + fcSYield.asIn.cmd = MC_SMC_N_YIELD; + mcFastCall(&(fcSYield.asGeneric)); + ret = convertFcRet(fcSYield.asOut.ret); + if (0 != ret) + break; + + } while (FALSE); + + /* MCDRV_DBG("exit with %d/0x%08X\n", ret, ret); */ + + return ret; +} + +/*----------------------------------------------------------------------------*/ +/** + * handle ioctl and call common notify + * + * @param pInstance + * @param arg + * + * @return 0 if no error + * + */ +static int handleIoCtlNSIQ( + struct mcInstance *pInstance, + unsigned long arg +) +{ + int ret = 0; + + MCDRV_ASSERT(NULL != pInstance); + + /* Can't allow Yields while preparing to sleep */ + if(mcpBuffer->mcFlags.sleepMode.SleepReq == MC_FLAG_REQ_TO_SLEEP) + return ret; + + /* avoid putting debug output here, as we do this very often */ + //MCDRV_DBG("enter\n"); + /* only the MobiCore Daemon is allowed to call this function */ + if (!isCallerMcDaemon(pInstance)) { + MCDRV_DBG_ERROR("caller not MobiCore Daemon\n"); + return -EFAULT; + } + + do { + union mcFcNSIQ fcNSIQ; + memset(&fcNSIQ, 0, sizeof(fcNSIQ)); + fcNSIQ.asIn.cmd = MC_SMC_N_SIQ; + mcFastCall(&(fcNSIQ.asGeneric)); + ret = convertFcRet(fcNSIQ.asOut.ret); + if (0 != ret) + break; + } while (FALSE); + + /* MCDRV_DBG("exit with %d/0x%08X\n", ret, ret); */ + + return ret; +} + +/*----------------------------------------------------------------------------*/ +/** + * + * @param pInstance + * @param arg + * + * @return 0 if no error + * + */ +static int handleIoCtlDumpStatus( + struct mcInstance *pInstance, + unsigned long arg +) +{ + int ret = 0; + int i = 0; + union mcFcInfo fcInfo; + + MCDRV_ASSERT(NULL != pInstance); + MCDRV_DBG_VERBOSE("enter\n"); + + do { + /* anybody with root access can do this. */ + if (!isUserlandCallerPrivileged()) { + MCDRV_DBG_ERROR("caller must have root privileges\n"); + ret = -EFAULT; + break; + } + + /* loop extInfo */ + while (TRUE) { + memset(&fcInfo, 0, sizeof(fcInfo)); + fcInfo.asIn.cmd = MC_FC_INFO; + fcInfo.asIn.extInfoId = i; + + MCDRV_DBG( + "fcInfo in cmd=0x%08x, extInfoid=0x%08x " + "rfu=(0x%08x, 0x%08x)\n", + fcInfo.asIn.cmd, + fcInfo.asIn.extInfoId, + fcInfo.asIn.rfu[0], + fcInfo.asIn.rfu[1]); + + mcFastCall(&(fcInfo.asGeneric)); + + MCDRV_DBG( + "fcInfo out resp=0x%08x, ret=0x%08x " + "state=0x%08x, extInfo=0x%08x\n", + fcInfo.asOut.resp, + fcInfo.asOut.ret, + fcInfo.asOut.state, + fcInfo.asOut.extInfo); + + ret = convertFcRet(fcInfo.asOut.ret); + if (0 != ret) + break; + + MCDRV_DBG("state=%08X, idx=%02d: extInfo=%08X\n", + fcInfo.asOut.state, i, fcInfo.asOut.extInfo); + i++; + }; + + if (0 != ret) + break; + + + } while (FALSE); + + MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret); + + return ret; +} + +/*----------------------------------------------------------------------------*/ +/** + * + * @param pInstance + * @param arg + * + * @return 0 if no error + * + */ +static int handleIoCtlInit( + struct mcInstance *pInstance, + union mcIoCtlInitParams *pUserParams +) +{ + int ret = 0; + union mcIoCtlInitParams params; + union mcFcInit fcInit; + + MCDRV_ASSERT(NULL != pInstance); + MCDRV_DBG_VERBOSE("enter\n"); + + do { + /* only the MobiCore Daemon is allowed to call this function */ + if (!isCallerMcDaemon(pInstance)) { + MCDRV_DBG_ERROR("caller not MobiCore Daemon\n"); + ret = -EFAULT; + break; + } + + ret = copy_from_user( + &(params.in), + &(pUserParams->in), + sizeof(params.in)); + if (0 != ret) { + MCDRV_DBG_ERROR("copy_from_user failed\n"); + break; + } + + memset(&fcInit, 0, sizeof(fcInit)); + + fcInit.asIn.cmd = MC_FC_INIT; + /* base address of mci buffer 4KB aligned */ + fcInit.asIn.base = (uint32_t)params.in.base; + /* notification buffer start/length [16:16] [start, length] */ + fcInit.asIn.nqInfo = (params.in.nqOffset << 16) + | (params.in.nqLength & 0xFFFF); + /* mcp buffer start/length [16:16] [start, length] */ + fcInit.asIn.mcpInfo = (params.in.mcpOffset << 16) + | (params.in.mcpLength & 0xFFFF); + + /* Set KMOD notification queue to start of MCI + mciInfo was already set up in mmap */ + if (!mciBase) { + MCDRV_DBG_ERROR("No MCI set yet.\n"); + return -EFAULT; + } else { + /* Save the information for later usage in the module */ + mcpBuffer = (void*)mciBase + params.in.mcpOffset; + } + MCDRV_DBG("in cmd=0x%08x, base=0x%08x, " + "nqInfo=0x%08x, mcpInfo=0x%08x\n", + fcInit.asIn.cmd, + fcInit.asIn.base, + fcInit.asIn.nqInfo, + fcInit.asIn.mcpInfo); + + mcFastCall(&(fcInit.asGeneric)); + + MCDRV_DBG("out cmd=0x%08x, ret=0x%08x rfu=(0x%08x, 0x%08x)\n", + fcInit.asOut.resp, + fcInit.asOut.ret, + fcInit.asOut.rfu[0], + fcInit.asOut.rfu[1]); + + MCDRV_DBG("MCP addr=%p IDLE=%d\n", mcpBuffer, mcpBuffer->mcFlags.schedule); + + ret = convertFcRet(fcInit.asOut.ret); + if (0 != ret) + break; + + /* no ioctl response parameters */ + + } while (FALSE); + + MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret); + + return ret; +} + +/*----------------------------------------------------------------------------*/ +/** + * + * @param pInstance + * @param arg + * + * @return 0 if no error + * + */ +static int handleIoCtlFcExecute( + struct mcInstance *pInstance, + union mcIoCtlFcExecuteParams *pUserParams +) +{ + int ret = 0; + union mcIoCtlFcExecuteParams params; + union fcGeneric fcParams; + + MCDRV_ASSERT(NULL != pInstance); + MCDRV_DBG_VERBOSE("enter\n"); + + do { + /* only the MobiCore Daemon is allowed to call this function */ + if (!isCallerMcDaemon(pInstance)) { + MCDRV_DBG_ERROR("caller not MobiCore Daemon\n"); + ret = -EFAULT; + break; + } + + ret = copy_from_user( + &(params.in), + &(pUserParams->in), + sizeof(params.in)); + if (0 != ret) { + MCDRV_DBG_ERROR("copy_from_user failed\n"); + break; + } + + fcParams.asIn.cmd = -4;/*FC_EXECUTE */ + fcParams.asIn.param[0] = params.in.physStartAddr; + fcParams.asIn.param[1] = params.in.length; + fcParams.asIn.param[2] = 0; + + MCDRV_DBG("in cmd=0x%08x, startAddr=0x%08x, length=0x%08x\n", + fcParams.asIn.cmd, + fcParams.asIn.param[0], + fcParams.asIn.param[1]); + + mcFastCall(&fcParams); + + MCDRV_DBG("out cmd=0x%08x, ret=0x%08x rfu=(0x%08x, 0x%08x)\n", + fcParams.asOut.resp, + fcParams.asOut.ret, + fcParams.asOut.param[0], + fcParams.asOut.param[1]); + + ret = convertFcRet(fcParams.asOut.ret); + if (0 != ret) + break; + + /* no ioctl response parameters */ + + } while (FALSE); + + MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret); + + return ret; +} + +/*----------------------------------------------------------------------------*/ +/** + * + * @param pInstance + * @param arg + * + * @return 0 if no error + * + */ +#define MC_MAKE_VERSION(major, minor) \ + (((major & 0x0000ffff) << 16) | (minor & 0x0000ffff)) + +static int handleIoCtlGetVersion( + struct mcInstance *pInstance, + struct mcIoCtlGetVersionParams *pUserParams +) +{ + int ret = 0; + struct mcIoCtlGetVersionParams params = { + { + MC_MAKE_VERSION(MCDRVMODULEAPI_VERSION_MAJOR, + MCDRVMODULEAPI_VERSION_MINOR) + } + }; + + MCDRV_ASSERT(NULL != pInstance); + 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( + &(pUserParams->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 pInode pointer to inode + * @param pFile pointer to file + * @param cmd command + * @param arg + * + * @return int 0 for OK and an errno in case of error + */ +static long mcKernelModule_ioctl( + struct file *pFile, + unsigned int cmd, + unsigned long arg +) +{ + int ret; + struct mcInstance *pInstance = getInstance(pFile); + + MCDRV_ASSERT(NULL != pInstance); + + switch (cmd) { + /*--------------------------------------------------------------------*/ + case MC_DRV_KMOD_IOCTL_DUMP_STATUS: + ret = handleIoCtlDumpStatus( + pInstance, + arg); + break; + + /*--------------------------------------------------------------------*/ + case MC_DRV_KMOD_IOCTL_FC_INIT: + ret = handleIoCtlInit( + pInstance, + (union mcIoCtlInitParams *)arg); + break; + /*--------------------------------------------------------------------*/ + case MC_DRV_KMOD_IOCTL_FC_INFO: + ret = handleIoCtlInfo( + pInstance, + (union mcIoCtlInfoParams *)arg); + break; + + /*--------------------------------------------------------------------*/ + case MC_DRV_KMOD_IOCTL_FC_YIELD: + ret = handleIoCtlYield( + pInstance); + break; + + /*--------------------------------------------------------------------*/ + case MC_DRV_KMOD_IOCTL_FC_NSIQ: + ret = handleIoCtlNSIQ( + pInstance, + arg); + break; + + /*--------------------------------------------------------------------*/ + case MC_DRV_KMOD_IOCTL_DAEMON_LOCK_WSM_L2: + ret = handleIoCtlDaemonLockWsmL2( + pInstance, + (struct mcIoCtlDaemonLockWsmL2Params *)arg); + break; + + /*--------------------------------------------------------------------*/ + case MC_DRV_KMOD_IOCTL_DAEMON_UNLOCK_WSM_L2: + ret = handleIoCtlDaemonUnlockWsmL2( + pInstance, + (struct mcIoCtlDaemonUnlockWsmL2Params *)arg); + break; + + /*--------------------------------------------------------------------*/ + case MC_DRV_KMOD_IOCTL_FREE: + /* called by ClientLib */ + ret = handleIoCtlFree( + pInstance, + (union mcIoCtltoFreeParams *)arg); + break; + + /*--------------------------------------------------------------------*/ + case MC_DRV_KMOD_IOCTL_APP_REGISTER_WSM_L2: + /* called by ClientLib */ + ret = handleIoCtlAppRegisterWsmL2( + pInstance, + (union mcIoCtlAppRegWsmL2Params *)arg); + break; + + /*--------------------------------------------------------------------*/ + case MC_DRV_KMOD_IOCTL_APP_UNREGISTER_WSM_L2: + /* called by ClientLib */ + ret = handleIoCtlAppUnregisterWsmL2( + pInstance, + (struct mcIoCtlAppUnregWsmL2Params *)arg); + break; + + /*--------------------------------------------------------------------*/ + case MC_DRV_KMOD_IOCTL_FC_EXECUTE: + ret = handleIoCtlFcExecute( + pInstance, + (union mcIoCtlFcExecuteParams *)arg); + break; + + /*--------------------------------------------------------------------*/ + case MC_DRV_KMOD_IOCTL_GET_VERSION: + ret = handleIoCtlGetVersion( + pInstance, + (struct mcIoCtlGetVersionParams *)arg); + break; + + /*--------------------------------------------------------------------*/ + default: + MCDRV_DBG_ERROR("unsupported cmd=%d\n", cmd); + ret = -EFAULT; + break; + + } /* end switch(cmd) */ + +#ifdef MC_MEM_TRACES + mobicore_read_log(); +#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 *pFile + * @param *pBuffer buffer where to copy to(userspace) + * @param count number of requested data + * @param *pPos not used + * @return ssize_t ok case: number of copied data + * error case: return errno + */ +static ssize_t mcKernelModule_read( + struct file *pFile, + char *pBuffer, + size_t bufferLen, + loff_t *pPos +) +{ + int ret = 0, ssiqCounter; + size_t retLen = 0; + struct mcInstance *pInstance = getInstance(pFile); + + MCDRV_ASSERT(NULL != pInstance); + + /* 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 (!isCallerMcDaemon(pInstance)) { + MCDRV_DBG_ERROR("caller not MobiCore Daemon\n"); + ret = -EFAULT; + break; + } + + if (bufferLen < sizeof(unsigned int)) { + MCDRV_DBG_ERROR("invalid length\n"); + ret = (ssize_t)(-EINVAL); + break; + } + + for (;;) { + if (down_interruptible(&mcDrvKModCtx.daemonCtx.sem)) { + MCDRV_DBG_VERBOSE("read interrupted\n"); + ret = (ssize_t)-ERESTARTSYS; + break; + } + + ssiqCounter = atomic_read(&(mcDrvKModCtx.ssiqCtx.counter)); + MCDRV_DBG_VERBOSE("ssiqCounter=%i, ctx.counter=%i\n", + ssiqCounter, + mcDrvKModCtx.daemonCtx.ssiqCounter); + + if (ssiqCounter != mcDrvKModCtx.daemonCtx.ssiqCounter) { + /* read data and exit loop without + error */ + mcDrvKModCtx.daemonCtx.ssiqCounter = + ssiqCounter; + ret = 0; + break; + } + + /* end loop if non-blocking */ + if (0 != (pFile->f_flags & O_NONBLOCK)) { + MCDRV_DBG_ERROR("non-blocking read\n"); + ret = (ssize_t)(-EAGAIN); + break; + } + + if (0 != signal_pending(current)) { + MCDRV_DBG_VERBOSE("received signal.\n"); + ret = (ssize_t)(-ERESTARTSYS); + break; + } + + } + + /* we are here if an event occurred or we had an + error.*/ + if (0 != ret) + break; + + /* read data and exit loop */ + ret = copy_to_user( + pBuffer, + &(mcDrvKModCtx.daemonCtx.ssiqCounter), + sizeof(unsigned int)); + + + if (0 != ret) { + MCDRV_DBG_ERROR("copy_to_user failed\n"); + ret = (ssize_t)(-EFAULT); + break; + } + + retLen = sizeof(s32); + + } while (FALSE); + + /* avoid debug on non-error. */ + if (0 == ret) + 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 pInstance instance + * @param requestedSize size of the WSM + * @param pHandle pointer where the handle will be saved + * @param pKernelVirtAddr pointer for the kernel virtual address + * @param pPhysAddr pointer for the physical address + * + * @return error code or 0 for success + */ +int mobicore_allocate_wsm( + struct mcInstance *pInstance, + unsigned long requestedSize, + uint32_t *pHandle, + void **pKernelVirtAddr, + void **pPhysAddr +) +{ + unsigned int i; + unsigned int order; + unsigned long allocatedSize; + int ret = 0; + struct mc_tuple *pTuple = 0; + void *kernelVirtAddr; + void *physAddr; + + MCDRV_ASSERT(NULL != pInstance); + MCDRV_DBG("%s (size=%ld)\n", __func__, requestedSize); + + order = sizeToOrder(requestedSize); + if (INVALID_ORDER == order) { + MCDRV_DBG_ERROR( + "size to order converting failed for size %ld\n", + requestedSize); + return INVALID_ORDER; + } + + allocatedSize = (1<<order)*PAGE_SIZE; + + MCDRV_DBG("size %ld -> order %d --> %ld (2^n pages)\n", + requestedSize, order, allocatedSize); + + do { + /* Usual Wsm request, allocate tuple. */ + /* search for a free entry in the tuple list + * REV axh: serialize this over multiple instances. */ + for (i = 0; i < MC_DRV_KMOD_TUPLE_NR; i++) { + pTuple = &(pInstance->tuple[i]); + if (0 == pTuple->handle) { + pTuple->handle = getMcKModUniqueId(); + break; + } + } + if (MC_DRV_KMOD_TUPLE_NR == i) { + MCDRV_DBG_ERROR("no free tuple\n"); + ret = -EFAULT; + break; + } + + /* Common code for all allocation paths */ + kernelVirtAddr = (void *)__get_free_pages(GFP_USER | __GFP_COMP, order); + if (NULL == kernelVirtAddr) { + MCDRV_DBG_ERROR("get_free_pages failed\n"); + ret = -ENOMEM; + break; + } + + /* Get physical address to instance data */ + physAddr = (void *)virt_to_phys(kernelVirtAddr); + /* TODO: check for INVALID_ADDRESS? */ + + MCDRV_DBG( + "allocated phys=0x%p - 0x%p, " + "size=%ld, kernelVirt=0x%p, handle=%d\n", + physAddr, + (void *)((unsigned int)physAddr+allocatedSize), + allocatedSize, kernelVirtAddr, pTuple->handle); + + /* Usual Wsm request, allocate tuple. + * Also, we never free a persistent Tci */ + pTuple->physAddr = physAddr; + pTuple->virtKernelAddr = kernelVirtAddr; + pTuple->virtUserAddr = kernelVirtAddr; + pTuple->numPages = (1U << order); + *pHandle = pTuple->handle; + *pKernelVirtAddr = kernelVirtAddr; + *pPhysAddr = physAddr; + + } 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 pFile + * @param pVmArea + * pVmArea.pg_offset != 0 is mapping of MCI is requested + * + * @return 0 if OK or -ENOMEM in case of error. + */ +static int mcKernelModule_mmap( + struct file *pFile, + struct vm_area_struct *pVmArea +) +{ + unsigned int i; + unsigned int order; + void *kernelVirtAddr = 0; + void *physAddr = 0; + unsigned long requestedSize = + pVmArea->vm_end - pVmArea->vm_start; + unsigned long allocatedSize; + int ret = 0; + struct mc_tuple *pTuple = 0; + unsigned int handle = 0; + struct mcInstance *pInstance = getInstance(pFile); + unsigned int request = pVmArea->vm_pgoff * 4096; +#if defined(DEBUG) + bool release = false; +#else + bool release = true; +#endif + + MCDRV_ASSERT(NULL != pInstance); + MCDRV_DBG("enter (vmaStart=0x%p, size=%ld, request=0x%x, mci=0x%x)\n", + (void *)pVmArea->vm_start, + requestedSize, + request, + mciBase); + + order = sizeToOrder(requestedSize); + if (INVALID_ORDER == order) { + MCDRV_DBG_ERROR( + "size to order converting failed for size %ld\n", + requestedSize); + return -ENOMEM; + } + + allocatedSize = (1<<order)*PAGE_SIZE; + + MCDRV_DBG("size %ld -> order %d --> %ld (2^n pages)\n", + requestedSize, order, allocatedSize); + + do { + /* Daemon tries to get an existing MCI */ + if ((MC_DRV_KMOD_MMAP_MCI == request) && (mciBase != 0)) { + MCDRV_DBG("Request MCI, it is at (%x)\n", + mciBase); + + if (!isCallerMcDaemon(pInstance)) { + ret = -EPERM; + break; + } + kernelVirtAddr = (void *)mciBase; + physAddr = (void *)virt_to_phys(kernelVirtAddr); + } else { + /* Usual Wsm request, allocate tuple. */ + if (MC_DRV_KMOD_MMAP_WSM == request) { + /* search for a free entry in the tuple list + REV axh: serialize this over multiple instances. */ + for (i = 0; i < MC_DRV_KMOD_TUPLE_NR; i++) { + pTuple = &(pInstance->tuple[i]); + if (0 == pTuple->handle) { + pTuple->handle = getMcKModUniqueId(); + break; + } + } + if (MC_DRV_KMOD_TUPLE_NR == i) { + MCDRV_DBG_ERROR("no free tuple\n"); + ret = -EFAULT; + break; + } + } else { + if (request <= MC_DRV_KMOD_MMAP_PERSISTENTWSM || release) { + /* Special Wsm request + --> only Daemon is allowed */ + if (!isCallerMcDaemon(pInstance)) { + ret = -EPERM; + break; + } + } + } + if (request <= MC_DRV_KMOD_MMAP_PERSISTENTWSM) { + /* Common code for all allocation paths + * get physical address, */ + kernelVirtAddr = (void *)__get_free_pages( + GFP_USER | __GFP_COMP, order); + if (NULL == kernelVirtAddr) { + MCDRV_DBG_ERROR("get_free_pages failed\n"); + ret = -ENOMEM; + break; + } + if (MC_DRV_KMOD_MMAP_WSM == request) + handle = pTuple->handle; + /* Get physical address to instance data */ + /* TODO: check for INVALID_ADDRESS? */ + physAddr = (void *)virt_to_phys(kernelVirtAddr); + } else { +#if defined(DEBUG) + /*kernelVirtAddr = phys_to_virt(request); + kernelVirtAddr = ioremap(request,requestedSize);*/ + physAddr = (void *)request; + kernelVirtAddr = phys_to_virt(request); + /* NOTE: request != virt_to_phys(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, kernelVirt=0x%p, handle=%d\n", + physAddr, + (void *)((unsigned int)physAddr+allocatedSize), + allocatedSize, kernelVirtAddr, handle); + + pVmArea->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( + pVmArea, + (pVmArea->vm_start), + addrToPfn(physAddr), + requestedSize, + pVmArea->vm_page_prot); + if (0 != ret) { + 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 (!((MC_DRV_KMOD_MMAP_MCI == request) && + (mciBase != 0))) + freeContinguousPages( + kernelVirtAddr, + (1U << order)); + break; + } + + /* Usual Wsm request, allocate tuple. + 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 (MC_DRV_KMOD_MMAP_WSM == request) { + pTuple->physAddr = physAddr; + pTuple->virtKernelAddr = kernelVirtAddr; + pTuple->virtUserAddr = (void *)(pVmArea->vm_start); + pTuple->numPages = (1U << order); + } + + /* set response in allocated buffer */ + { + struct mcMmapResp *mmapResp = + (struct mcMmapResp *)kernelVirtAddr; + /* TODO: do this for daemon only, otherwise set NULL */ + mmapResp->physAddr = (uint32_t)physAddr; + mmapResp->handle = handle; + if ((MC_DRV_KMOD_MMAP_MCI == request) && + (0 != mciBase)) { + mmapResp->isReused = 1; + } else + mmapResp->isReused = 0; + } + + /* store MCI pointer */ + if ((MC_DRV_KMOD_MMAP_MCI == request) && (0 == mciBase)) { + mciBase = (uint32_t)kernelVirtAddr; + MCDRV_DBG("MCI base set to 0x%x\n", mciBase); + } + } while (FALSE); + + MCDRV_DBG_VERBOSE("exit with %d/0x%08X\n", ret, ret); + + return (int)ret; +} + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 28) +/*----------------------------------------------------------------------------*/ +/** + * Force migration of current task to CPU0(where the monitor resides) + * + * @return Error code or 0 for success + */ +static int gotoCpu0( + 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 (0 != ret) + 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 gotoCpu0) + * + * @return Error code or 0 for success + */ +static int gotoAllCpu( + 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 (0 != ret) + 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 gotoCpu0(void) +{ + return 0; +} + +static int gotoAllCpu(void) +{ + return 0; +} +#endif + +/*----------------------------------------------------------------------------*/ +/** + * Initialize a new mobicore API instance object + * + * @return Instance or NULL if no allocation was possible. + */ +struct mcInstance *mobicore_open( + void +) { + struct mcInstance *pInstance; + pid_t pidVnr; + + pInstance = kzalloc(sizeof(*pInstance), GFP_KERNEL); + if (NULL == pInstance) + return NULL; + + /* get a unique ID for this instance (PIDs are not unique) */ + pInstance->handle = getMcKModUniqueId(); + + /* 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 */ + pidVnr = task_pid_vnr(current); + pInstance->pidVnr = pidVnr; + + return pInstance; +} +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 pInode + * @param pFile + * @return 0 if OK or -ENOMEM if no allocation was possible. + */ +static int mcKernelModule_open( + struct inode *pInode, + struct file *pFile +) +{ + struct mcInstance *pInstance; + int ret = 0; + + MCDRV_DBG_VERBOSE("enter\n"); + + do { + pInstance = mobicore_open(); + if (pInstance == NULL) + return -ENOMEM; + + /* check if Daemon. We simply assume that the first to open us + with root privileges must be the daemon. */ + if ((isUserlandCallerPrivileged()) + && (NULL == mcDrvKModCtx.daemonInst)) { + MCDRV_DBG("accept this as MobiCore Daemon\n"); + + /* Set the caller's CPU mask to CPU0*/ + ret = gotoCpu0(); + if (0 != ret) { + MCDRV_DBG("changing core failed!\n"); + break; + } + + mcDrvKModCtx.daemonInst = pInstance; + sema_init(&mcDrvKModCtx.daemonCtx.sem, 0); + /* init ssiq event counter */ + mcDrvKModCtx.daemonCtx.ssiqCounter = + atomic_read(&(mcDrvKModCtx.ssiqCtx.counter)); + } + + /* store instance data reference */ + pFile->private_data = pInstance; + + /* 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 pInstance instance + * @return 0 if Ok or -E ERROR + */ +int mobicore_release( + struct mcInstance *pInstance +) +{ + int ret = 0; + unsigned int idxTuple; + struct mcL2TablesDescr *pWsmL2Descr, *pTemp; + + do { + /* try to get the semaphore */ + ret = down_interruptible(&(mcDrvKModCtx.wsmL2Sem)); + if (0 != ret) { + 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( + pWsmL2Descr, + pTemp, + &(mcDrvKModCtx.wsmL2Descriptors), + list + ) { + if (pWsmL2Descr->pInstance == pInstance) { + MCDRV_DBG_WARN( + "trying to release WSM L2: " + "physBase=%p ,nrOfPages=%d\n", + getL2TablePhys(pWsmL2Descr), + pWsmL2Descr->nrOfPages); + + /* unlock app usage and free if MobiCore + does not use it */ + freeWsmL2Table(pWsmL2Descr, FREE_FROM_NWD); + } + } /* end while */ + + /* release semaphore */ + up(&(mcDrvKModCtx.wsmL2Sem)); + } + + + + /* release all mapped data */ + for (idxTuple = 0; + idxTuple < MC_DRV_KMOD_TUPLE_NR; + idxTuple++) { + struct mc_tuple *pTuple = &(pInstance->tuple[idxTuple]); + + if (0 != pTuple->virtUserAddr) { + freeContinguousPages( + pTuple->virtKernelAddr, + pTuple->numPages); + } + } + + /* release instance context */ + kfree(pInstance); + } 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 pInode + * @param pFile + * + * @return 0 + */ +static int mcKernelModule_release( + struct inode *pInode, + struct file *pFile +) +{ + int ret = 0; + struct mcInstance *pInstance = getInstance(pFile); + + MCDRV_DBG_VERBOSE("enter\n"); + + + do { + /* check if daemon closes us. */ + if (isCallerMcDaemon(pInstance)) { + /* TODO: cleanup? + * mcDrvKModCtx.wsmL2Descriptors remains */ + MCDRV_DBG_WARN("WARNING: MobiCore Daemon died\n"); + mcDrvKModCtx.daemonInst = NULL; + } + + ret = mobicore_release(pInstance); + + } 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 *pContext pointer to registered device data + * + * @return IRQ_HANDLED + */ +static irqreturn_t mcKernelModule_intrSSIQ( + int intr, + void *pContext +) +{ + irqreturn_t ret = IRQ_NONE; + + /* we know the context. */ + MCDRV_ASSERT(&mcDrvKModCtx == pContext); + + do { + if (MC_INTR_SSIQ != intr) { + /* this should not happen, as we did no register for any + other interrupt. Fir 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(&(mcDrvKModCtx.ssiqCtx.counter)); + + /* signal the daemon */ + up(&mcDrvKModCtx.daemonCtx.sem); + + + ret = IRQ_HANDLED; + + } while (FALSE); + + return ret; +} + +/*----------------------------------------------------------------------------*/ +/** function table structure of this device driver. */ +static const struct file_operations mcKernelModule_fileOperations = { + .owner = THIS_MODULE, /**< driver owner */ + .open = mcKernelModule_open, /**< driver open function */ + .release = mcKernelModule_release, /**< driver release function*/ + .unlocked_ioctl = mcKernelModule_ioctl, /**< driver ioctl function */ + .mmap = mcKernelModule_mmap, /**< driver mmap function */ + .read = mcKernelModule_read, /**< driver read function */ +}; + +/*----------------------------------------------------------------------------*/ +/** registration structure as miscdevice. */ +static struct miscdevice mcKernelModule_device = { + .name = MC_DRV_MOD_DEVNODE, /**< device name */ + .minor = MISC_DYNAMIC_MINOR, /**< device minor number */ + /** device interface function structure */ + .fops = &mcKernelModule_fileOperations, +}; + +#ifdef CONFIG_PM_RUNTIME + +static struct timer_list resume_timer; + +static void mobicore_resume_handler(unsigned long data) +{ + if(!mciBase || !mcpBuffer) + return; + + mcpBuffer->mcFlags.sleepMode.SleepReq = MC_FLAG_NO_SLEEP_REQ; +} + +static void mobicore_suspend_handler(struct work_struct *work) +{ + union fcGeneric fcSleep; +#ifdef MC_MEM_TRACES + mobicore_read_log(); +#endif + memset(&fcSleep, 0, sizeof(fcSleep)); + fcSleep.asIn.cmd = MC_SMC_N_SIQ; + + mcpBuffer->mcFlags.sleepMode.SleepReq = MC_FLAG_REQ_TO_SLEEP; + mcFastCall(&(fcSleep)); +} + +DECLARE_WORK(mobicore_suspend_work, mobicore_suspend_handler); + +static int mobicore_suspend_notifier(struct notifier_block *nb, + unsigned long event, void* dummy) +{ +#ifdef MC_MEM_TRACES + mobicore_read_log(); +#endif + /* We have noting to say if MobiCore is not initialized*/ + if(!mciBase || !mcpBuffer) + return 0; + + switch (event) + { + case PM_SUSPEND_PREPARE: + /* We can't go to sleep if MobiCore is not IDLE or not Ready to sleep */ + if (!(mcpBuffer->mcFlags.sleepMode.ReadyToSleep & MC_STATE_READY_TO_SLEEP)) + { + MCDRV_DBG("Suspend IDLE=%d!\n", mcpBuffer->mcFlags.schedule); + MCDRV_DBG("Suspend REQ=%d!\n", mcpBuffer->mcFlags.sleepMode.SleepReq); + MCDRV_DBG("Suspend Ready=%d!\n", mcpBuffer->mcFlags.sleepMode.ReadyToSleep); + schedule_work_on(0, &mobicore_suspend_work); + MCDRV_DBG("Don't allow SLEEP!"); + return NOTIFY_BAD; + } + MCDRV_DBG("Suspend IDLE=%d!\n", mcpBuffer->mcFlags.schedule); + MCDRV_DBG("Suspend REQ=%d!\n", mcpBuffer->mcFlags.sleepMode.SleepReq); + MCDRV_DBG("Suspend Ready=%d!\n", mcpBuffer->mcFlags.sleepMode.ReadyToSleep); + cancel_work_sync(&mobicore_suspend_work); + mod_timer(&resume_timer, 0); + break; + case PM_POST_SUSPEND : + MCDRV_DBG("POST-Sleep request %d in!\n", mcpBuffer->mcFlags.sleepMode.SleepReq); + mod_timer(&resume_timer, jiffies + msecs_to_jiffies(1000) ); + break; + default: + break; + } + return 0; +} + +static struct notifier_block mobicore_notif_block = { + .notifier_call = mobicore_suspend_notifier, +}; +#endif /* CONFIG_PM_RUNTIME */ + +/*----------------------------------------------------------------------------*/ +/** + * 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 mcKernelModule_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); + MCDRV_DBG("%s\n",MOBICORE_COMPONENT_BUILD_TAG); + + do { + /* Hardware does not support ARM TrustZone -> Cannot continue! */ + if (!hasSecurityExtensions()) { + MCDRV_DBG_ERROR("Hardware does't support ARM TrustZone!\n"); + ret = -ENODEV; + break; + } + + /* Running in secure mode -> Cannot load the driver! */ + if (isSecureMode()) { + MCDRV_DBG_ERROR("Running in secure MODE!\n"); + ret = -ENODEV; + break; + } + +#ifdef MC_MEM_TRACES + /* setupLog won't fail, it eats up any errors */ + mcKernelModule_setupLog(); +#endif + mcDrvKModCtx.daemonInst = NULL; + sema_init(&mcDrvKModCtx.daemonCtx.sem, 0); + /* set up S-SIQ interrupt handler */ + ret = request_irq( + MC_INTR_SSIQ, + mcKernelModule_intrSSIQ, + IRQF_TRIGGER_RISING, + MC_DRV_MOD_DEVNODE, + &mcDrvKModCtx); + if (0 != ret) { + MCDRV_DBG_ERROR("interrupt request failed\n"); + break; + } + + ret = misc_register(&mcKernelModule_device); + if (0 != ret) { + MCDRV_DBG_ERROR("device register failed\n"); + break; + } +#ifdef CONFIG_PM_RUNTIME + setup_timer( &resume_timer, mobicore_resume_handler, 0 ); + if ((ret = register_pm_notifier(&mobicore_notif_block))) { + MCDRV_DBG_ERROR("device pm register failed\n"); + break; + } +#endif //CONFIG_PM_RUNTIME + /* initialize event counter for signaling of an IRQ to zero */ + atomic_set(&(mcDrvKModCtx.ssiqCtx.counter), 0); + + /* init list for WSM L2 chunks. */ + INIT_LIST_HEAD(&(mcDrvKModCtx.wsmL2Chunks)); + + /* L2 table descriptor list. */ + INIT_LIST_HEAD(&(mcDrvKModCtx.wsmL2Descriptors)); + + sema_init(&(mcDrvKModCtx.wsmL2Sem), 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(&(mcDrvKModCtx.uniqueCounter), 1); + + 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 mcKernelModule_exit( + void +) +{ + struct mcL2TablesDescr *pWsmL2Descr; + + MCDRV_DBG_VERBOSE("enter\n"); + + /* Check if some WSM is still in use. */ + list_for_each_entry( + pWsmL2Descr, + &(mcDrvKModCtx.wsmL2Descriptors), + list + ) { + MCDRV_DBG_WARN( + "WSM L2 still in use: physBase=%p ,nrOfPages=%d\n", + getL2TablePhys(pWsmL2Descr), + pWsmL2Descr->nrOfPages); + } /* end while */ + +#ifdef CONFIG_PM_RUNTIME + if (unregister_pm_notifier(&mobicore_notif_block)) { + MCDRV_DBG_ERROR("device pm unregister failed\n"); + } + del_timer(&resume_timer); +#endif + + free_irq(MC_INTR_SSIQ, &mcDrvKModCtx); + + misc_deregister(&mcKernelModule_device); +#ifdef MC_MEM_TRACES + if (mcLogBuf) { + free_page((unsigned long)mcLogBuf); + } +#endif + MCDRV_DBG_VERBOSE("exit"); +} + + +/*----------------------------------------------------------------------------*/ +/* Linux Driver Module Macros */ +module_init(mcKernelModule_init); +module_exit(mcKernelModule_exit); +MODULE_AUTHOR("Giesecke & Devrient GmbH"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("MobiCore driver"); + +/** @} */ diff --git a/drivers/gud/MobiCoreDriver/mcDrvModule.h b/drivers/gud/MobiCoreDriver/mcDrvModule.h new file mode 100644 index 0000000..ac2e650 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/mcDrvModule.h @@ -0,0 +1,218 @@ +/** + * Header file of MobiCore Driver Kernel Module. + * + * @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_KMOD_H_ +#define _MC_DRV_KMOD_H_ + +#include "mcDrvModuleLinuxApi.h" +#include "public/mcDrvModuleApi.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 + +/** Number of address allocations for one driver instance. */ +#define MC_DRV_KMOD_TUPLE_NR 16 + +/** Number of pages for L2 tables. There are 4 table in each page. */ +#define MC_DRV_KMOD_L2_TABLE_PER_PAGES 4 +#define MC_DRV_KMOD_L2_TABLE_PAGES 8 + +struct l2Table { + pte_t tableEntries[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) + +/** + * tuple list entry. + * It describes the tuple, physical Kernel start address to the virtual Client + * address. The virtual kernel address is added for a simpler search algorithm. + */ +struct mc_tuple { + unsigned int handle; /* unique handle */ + void *virtUserAddr; /**< virtual User start address */ + void *virtKernelAddr; /**< virtual Kernel start address */ + void *physAddr; /**< physical start address */ + unsigned int numPages; /**< number of pages */ +}; + +/** + * Driver instance data. + */ +struct mcInstance { + /** unique handle */ + unsigned int handle; + /** process that opened this instance */ + pid_t pidVnr; + struct { + /** number of pages */ + unsigned int numPages; + /** virtual start address kernel address space generated + by mmap command */ + void *virtAddr; + /** physical start address kernel address space generated + by mmap command */ + void *physAddr; + } map; + /** tuple list for mmap generated address space and + its virtual client address */ + struct mc_tuple tuple[MC_DRV_KMOD_TUPLE_NR]; +}; + + + +/** data structure of 4 L2 tables within one 4kb page*/ +struct mcL2Page { + struct l2Table table[MC_DRV_KMOD_L2_TABLE_PER_PAGES]; +}; + + +/** bookkeeping data structure that manages 4 L2 tables in one page */ +struct mcL2TablesChunk { + struct list_head list; + unsigned int usageBitmap; /**< usage bitmap */ + struct mcL2Page *kernelVirt; /**< kernel virtual address */ + struct mcL2Page *phys; /**< physical address */ + struct page *pPage; /**< pointer to page struct */ +}; + +/** bookkeeping data structure that binds one L2 table + to its chunk(page) and its user */ +struct mcL2TablesDescr { + struct list_head list; + unsigned int handle; + unsigned int flags; + struct mcInstance *pInstance; + struct mcL2TablesChunk *pChunk; + unsigned int idx; + unsigned int nrOfPages; +}; + +#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 mcSSiqCtx { + atomic_t counter; /**< S-SIQ interrupt counter */ +}; + + +/** MobiCore Daemon context data. */ +struct mcDaemonCtx { + struct semaphore sem; /**< event semaphore */ + struct fasync_struct *pAsyncQueue; + unsigned int ssiqCounter; /**< event counter */ +}; + + +/** MobiCore Driver Kernel Module context data. */ +struct mcDrvKModCtx { + atomic_t uniqueCounter; /**< ever incrementing counter */ + struct mcSSiqCtx ssiqCtx; /**< S-SIQ interrupt context */ + struct mcDaemonCtx daemonCtx; /**< MobiCore Daemon context */ + struct mcInstance *daemonInst; /**< pointer to instance of daemon */ + struct list_head wsmL2Chunks; /**< Backing store for L2 tables */ + struct list_head wsmL2Descriptors; /**< Bookkeeping for L2 tables */ + struct semaphore wsmL2Sem; /**< semaphore to synchronize access to + above lists */ +}; + +/** MobiCore internal trace buffer structure. */ +struct mcTraceBuf { + 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 */ +}; + +#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/MobiCoreDriver/mcDrvModuleAndroid.h b/drivers/gud/MobiCoreDriver/mcDrvModuleAndroid.h new file mode 100644 index 0000000..f107011 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/mcDrvModuleAndroid.h @@ -0,0 +1,33 @@ +/** + * 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 + */ +#define AID_ROOT 0 /* traditional unix root user */ +#define AID_SYSTEM 1000 /* system server */ +#define AID_MISC 9998 /* access to misc storage */ +#define AID_NOBODY 9999 +#define AID_APP 10000 /* first app user */ + +#endif /* _MC_DRV_MODULE_ANDROID_H_ */ +/** @} */ diff --git a/drivers/gud/MobiCoreDriver/mcDrvModuleFc.h b/drivers/gud/MobiCoreDriver/mcDrvModuleFc.h new file mode 100644 index 0000000..6bfdd6b --- /dev/null +++ b/drivers/gud/MobiCoreDriver/mcDrvModuleFc.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 "mcDrvModule.h" + +/** + * MobiCore SMCs + */ +enum mcSmcCodes { + 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 mcFastCallCodes { + 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 mcFastCallsResult { + 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 fcGeneric { + struct { + uint32_t cmd; + uint32_t param[3]; + } asIn; + struct { + uint32_t resp; + uint32_t ret; + uint32_t param[2]; + } asOut; +}; + + +/** fast call init */ +union mcFcInit { + union fcGeneric asGeneric; + struct { + uint32_t cmd; + uint32_t base; + uint32_t nqInfo; + uint32_t mcpInfo; + } asIn; + struct { + uint32_t resp; + uint32_t ret; + uint32_t rfu[2]; + } asOut; +}; + + +/** fast call info parameters */ +union mcFcInfo { + union fcGeneric asGeneric; + struct { + uint32_t cmd; + uint32_t extInfoId; + uint32_t rfu[2]; + } asIn; + struct { + uint32_t resp; + uint32_t ret; + uint32_t state; + uint32_t extInfo; + } asOut; +}; + + +/** fast call S-Yield parameters */ +union mcFcSYield { + union fcGeneric asGeneric; + struct { + uint32_t cmd; + uint32_t rfu[3]; + } asIn; + struct { + uint32_t resp; + uint32_t ret; + uint32_t rfu[2]; + } asOut; +}; + + +/** fast call N-SIQ parameters */ +union mcFcNSIQ { + union fcGeneric asGeneric; + struct { + uint32_t cmd; + uint32_t rfu[3]; + } asIn; + struct { + uint32_t resp; + uint32_t ret; + uint32_t rfu[2]; + } asOut; +}; + + +/*----------------------------------------------------------------------------*/ +/** + * fast call to MobiCore + * + * @param pFcGeneric pointer to fast call data + */ +static inline void mcFastCall( + union fcGeneric *pFcGeneric +) +{ + MCDRV_ASSERT(pFcGeneric != NULL); + /* We only expect to make smc calls on CPU0 otherwise something wrong + * will happen */ + MCDRV_ASSERT(raw_smp_processor_id() == 0); + dsb(); +#ifdef MC_SMC_FASTCALL + { + int ret = 0; + MCDRV_DBG("Going into SCM()"); + ret = smc_fastcall((void *)pFcGeneric, sizeof(*pFcGeneric)); + MCDRV_DBG("Coming from SCM, scm_call=%i, resp=%d/0x%x\n", + ret, + pFcGeneric->asOut.resp, pFcGeneric->asOut.resp); + } +#else + { + /* SVC expect values in r0-r3 */ + register u32 reg0 __asm__("r0") = pFcGeneric->asIn.cmd; + register u32 reg1 __asm__("r1") = pFcGeneric->asIn.param[0]; + register u32 reg2 __asm__("r2") = pFcGeneric->asIn.param[1]; + register u32 reg3 __asm__("r3") = pFcGeneric->asIn.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 */ + pFcGeneric->asOut.resp = reg0; + pFcGeneric->asOut.ret = reg1; + pFcGeneric->asOut.param[0] = reg2; + pFcGeneric->asOut.param[1] = reg3; + } +#endif +} + + +/*----------------------------------------------------------------------------*/ +/** + * convert fast call return code to linux driver module error code + * + */ +static inline int convertFcRet( + 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/MobiCoreDriver/mcDrvModuleLinuxApi.h b/drivers/gud/MobiCoreDriver/mcDrvModuleLinuxApi.h new file mode 100644 index 0000000..fa5e732 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/mcDrvModuleLinuxApi.h @@ -0,0 +1,188 @@ +/** + * 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/slab.h> +#include <linux/suspend.h> +#include <linux/workqueue.h> +#include <linux/timer.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 *getPageStart( + 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 getOffsetInPage( + void *addr +) +{ + return (unsigned int)(((unsigned long)(addr)) & (~PAGE_MASK)); +} + +/*----------------------------------------------------------------------------*/ +/* get number of pages for a given buffer. */ +static inline unsigned int getNrOfPagesForBuffer( + void *addrStart, /* may be null */ + unsigned int len +) +{ + /* calculate used number of pages. Example: + offset+size newSize+PAGE_SIZE-1 nrOfPages + 0 4095 0 + 1 4096 1 + 4095 8190 1 + 4096 8191 1 + 4097 8192 2 */ + + return (getOffsetInPage(addrStart) + 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 sizeToOrder( + unsigned int size +) +{ + unsigned int order = INVALID_ORDER; + + if (0 != size) { + /* 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( + getNrOfPagesForBuffer(NULL, size)); + + /* there is a size overflow in getNrOfPagesForBuffer 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 addrToPfn( + 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 *pfnToAddr( + 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/MobiCoreDriver/platforms/EXYNOS_4X12_STD/platform.h b/drivers/gud/MobiCoreDriver/platforms/EXYNOS_4X12_STD/platform.h new file mode 100644 index 0000000..ff65761 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/platforms/EXYNOS_4X12_STD/platform.h @@ -0,0 +1,33 @@ +/** + * 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. */ +#define MC_INTR_SSIQ (32 + 15) + +/** Enable mobicore mem traces */ +#define MC_MEM_TRACES + +/** Change to Android UID check */ +#define MC_ANDROID_UID_CHECK + +#endif /* _MC_DRV_PLATFORM_H_ */ +/** @} */ diff --git a/drivers/gud/MobiCoreDriver/public/mcDrvModuleApi.h b/drivers/gud/MobiCoreDriver/public/mcDrvModuleApi.h new file mode 100644 index 0000000..6ac92cb --- /dev/null +++ b/drivers/gud/MobiCoreDriver/public/mcDrvModuleApi.h @@ -0,0 +1,283 @@ +/** @addtogroup MCD_MCDIMPL_KMOD + * @{ + * Interface to Mobicore Driver Kernel Module. + * + * <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 --> + */ + +#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 mcIoCtlInitParams { + struct { + /** base address of mci buffer 4KB align */ + uint32_t base; + /** notification buffer start/length [16:16] [start, length] */ + uint32_t nqOffset; + /** length of notification queue */ + uint32_t nqLength; + /** mcp buffer start/length [16:16] [start, length] */ + uint32_t mcpOffset; + /** length of mcp buffer */ + uint32_t mcpLength; + } in; + struct { + /* nothing */ + } out; +}; + + +/** + * Data exchange structure of the MC_DRV_MODULE_INFO ioctl command. + * INFO request data to the SWD + */ +union mcIoCtlInfoParams { + struct { + uint32_t extInfoId; /**< extended info ID */ + } in; + struct { + uint32_t state; /**< state */ + uint32_t extInfo; /**< 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 mcMmapMemtype { + MC_DRV_KMOD_MMAP_WSM = 0, + MC_DRV_KMOD_MMAP_MCI = 4096, + MC_DRV_KMOD_MMAP_PERSISTENTWSM = 8192 +}; + +struct mcMmapResp { + uint32_t handle; /**< WSN handle */ + uint32_t physAddr; /**< physical address of WSM (or NULL) */ + bool isReused; /**< if WSM memory was reused, or new allocated */ +}; + +/** + * Data exchange structure of the MC_DRV_KMOD_IOCTL_FREE ioctl command. + */ +union mcIoCtltoFreeParams { + 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 mcIoCtlAppRegWsmL2Params { + 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 physWsmL2Table; /* physical address of the L2 table */ + } out; +}; + + +/** + * Data exchange structure of the MC_DRV_KMOD_IOCTL_APP_UNREGISTER_WSM_L2 + * command. + */ +struct mcIoCtlAppUnregWsmL2Params { + 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 mcIoCtlDaemonLockWsmL2Params { + struct { + uint32_t handle; /**< driver handle for locked memory */ + } in; + struct { + uint32_t physWsmL2Table; + } out; +}; + + +/** + * Data exchange structure of the MC_DRV_KMOD_IOCTL_DAEMON_UNLOCK_WSM_L2 + * command. + */ +struct mcIoCtlDaemonUnlockWsmL2Params { + 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 mcIoCtlFcExecuteParams { + struct { + uint32_t physStartAddr;/**< base address of mobicore binary */ + uint32_t length; /**< length of DDR area */ + } in; + struct { + /* nothing */ + } out; +}; + +/** + * Data exchange structure of the MC_DRV_MODULE_GET_VERSION ioctl command. + */ +struct mcIoCtlGetVersionParams { + struct { + uint32_t kernelModuleVersion; + } 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 mcKModIoClt { + + /* + * 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: mcIoCtlAppRegWsmL2Params + */ + 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: mcIoCtlAppUnRegWsmL2Params + */ + 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: mcIoCtlGetVersionParams + */ + MC_DRV_KMOD_IOCTL_GET_VERSION = 224, +}; + + +#endif /* _MC_DRV_MODULEAPI_H_ */ +/** @} */ diff --git a/drivers/gud/MobiCoreDriver/public/mcKernelApi.h b/drivers/gud/MobiCoreDriver/public/mcKernelApi.h new file mode 100644 index 0000000..e496851 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/public/mcKernelApi.h @@ -0,0 +1,96 @@ +/** @addtogroup MCD_MCDIMPL_KMOD + * @{ + * Interface to Mobicore Driver Kernel Module inside Kernel. + * + * <!-- 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 mcInstance; + +/** + * Initialize a new mobicore API instance object + * + * @return Instance or NULL if no allocation was possible. + */ +struct mcInstance *mobicore_open( + void +); + +/** + * Release a mobicore instance object and all objects related to it + * @param pInstance instance + * @return 0 if Ok or -E ERROR + */ +int mobicore_release( + struct mcInstance *pInstance +); + +/** + * Free a WSM buffer allocated with mobicore_allocate_wsm + * @param pInstance + * @param handle handle of the buffer + * + * @return 0 if no error + * + */ +int mobicore_allocate_wsm( + struct mcInstance *pInstance, + unsigned long requestedSize, + uint32_t *pHandle, + void **pKernelVirtAddr, + void **pPhysAddr +); + +/** + * Free a WSM buffer allocated with mobicore_allocate_wsm + * @param pInstance + * @param handle handle of the buffer + * + * @return 0 if no error + * + */ +int mobicore_free( + struct mcInstance *pInstance, + uint32_t handle +); + +/** + * Map a virtual memory buffer structure to Mobicore + * @param pInstance + * @param addr address of the buffer(NB it must be kernel virtual!) + * @param len buffer length + * @param pHandle pointer to handle + * @param physWsmL2Table pointer to physical L2 table(?) + * + * @return 0 if no error + * + */ +int mobicore_map_vmem( + struct mcInstance *pInstance, + void *addr, + uint32_t len, + uint32_t *pHandle, + void **physWsmL2Table +); + +/** + * Unmap a virtual memory buffer from mobicore + * @param pInstance + * @param handle + * + * @return 0 if no error + * + */ +int mobicore_unmap_vmem( + struct mcInstance *pInstance, + uint32_t handle +); +#endif /* _MOBICORE_KERNELMODULE_API_H_ */ +/** @} */ diff --git a/drivers/gud/MobiCoreDriver/public/version.h b/drivers/gud/MobiCoreDriver/public/version.h new file mode 100644 index 0000000..a9189f6 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/public/version.h @@ -0,0 +1,12 @@ +/** @addtogroup MCD_MCDIMPL_KMOD + * @{ + * <!-- Copyright Giesecke & Devrient GmbH 2010-2012 --> + */ + +#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/MobiCoreKernelApi/clientlib.c b/drivers/gud/MobiCoreKernelApi/clientlib.c new file mode 100644 index 0000000..3b31a36 --- /dev/null +++ b/drivers/gud/MobiCoreKernelApi/clientlib.c @@ -0,0 +1,1133 @@ +#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> + +//TODO: this should be included from their own repos +#include "public/MobiCoreDriverApi.h" +#include "public/MobiCoreDriverCmd.h" +#include "device.h" +#include "session.h" + +/* device list */ +LIST_HEAD(devices); + +//------------------------------------------------------------------------------ +static mcore_device_t *resolveDeviceId( + uint32_t deviceId +) { + mcore_device_t *tmp; + struct list_head *pos; + + // Get mcore_device_t for deviceId + list_for_each(pos, &devices) { + tmp=list_entry(pos, mcore_device_t, list); + if (tmp->deviceId == deviceId) { + return tmp; + } + } + return NULL; +} + + +//------------------------------------------------------------------------------ +static void addDevice( + mcore_device_t *device +) { + list_add_tail(&(device->list), &devices); +} + + +//------------------------------------------------------------------------------ +static bool removeDevice( + uint32_t deviceId +) { + mcore_device_t *tmp; + struct list_head *pos, *q; + + list_for_each_safe(pos, q, &devices) { + tmp=list_entry(pos, mcore_device_t, list); + if (tmp->deviceId == deviceId) { + list_del(pos); + mcore_device_cleanup(tmp); + return true; + } + } + return false; +} + + +//------------------------------------------------------------------------------ +mcResult_t mcOpenDevice( + uint32_t deviceId +) { + mcResult_t mcResult = MC_DRV_OK; + connection_t *devCon = NULL; + //static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + + MCDRV_DBG_VERBOSE("===%s()===", __func__); + + //pthread_mutex_lock(&mutex); // Enter critical section + + do + { + mcore_device_t *device = resolveDeviceId(deviceId); + if (NULL != device) + { + MCDRV_DBG_ERROR("Device %d already opened", deviceId); + mcResult = MC_DRV_ERR_INVALID_OPERATION; + break; + } + + // Open new connection to device + devCon = connection_new(); + if (!connection_connect(devCon, MC_DAEMON_PID)) + { + MCDRV_DBG_ERROR("Could not setup netlink connection to PID %u", + MC_DAEMON_PID); + mcResult = MC_DRV_ERR_DAEMON_UNREACHABLE; + break; + } + + // Forward device open to the daemon and read result + mcDrvCmdOpenDevice_t mcDrvCmdOpenDevice = { + // C++ does not support C99 designated initializers + /* .header = */ { + /* .commandId = */ MC_DRV_CMD_OPEN_DEVICE + }, + /* .payload = */ { + /* .deviceId = */ deviceId + } + }; + + int len = connection_writeData( + devCon, + &mcDrvCmdOpenDevice, + sizeof(mcDrvCmdOpenDevice)); + if (len < 0) + { + MCDRV_DBG_ERROR("CMD_OPEN_DEVICE writeCmd failed, ret=%d", len); + mcResult = MC_DRV_ERR_DAEMON_UNREACHABLE; + break; + } + + mcDrvResponseHeader_t rspHeader; + len = connection_readDataBlock( + devCon, + &rspHeader, + sizeof(rspHeader)); + if (len != sizeof(rspHeader)) + { + MCDRV_DBG_ERROR("CMD_OPEN_DEVICE readRsp failed, ret=%d", len); + mcResult = MC_DRV_ERR_DAEMON_UNREACHABLE; + break; + } + if (MC_DRV_RSP_OK != rspHeader.responseId) + { + MCDRV_DBG_ERROR("CMD_OPEN_DEVICE failed, respId=%d", + rspHeader.responseId); + switch(rspHeader.responseId) + { + case MC_DRV_RSP_PAYLOAD_LENGTH_ERROR: + mcResult = MC_DRV_ERR_DAEMON_UNREACHABLE; + break; + case MC_DRV_INVALID_DEVICE_NAME: + mcResult = MC_DRV_ERR_UNKNOWN_DEVICE; + break; + case MC_DRV_RSP_DEVICE_ALREADY_OPENED: + default: + mcResult = MC_DRV_ERR_INVALID_OPERATION; + break; + } + break; + } + + // there is no payload to read + + device = mcore_device_create(deviceId, devCon); + 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); + mcResult = MC_DRV_ERR_INVALID_DEVICE_FILE; + break; + } + + addDevice(device); + + } while (false); + + if (mcResult != MC_DRV_OK) + { + connection_cleanup(devCon); + } + + //pthread_mutex_unlock(&mutex); // Exit critical section + + return mcResult; +} +EXPORT_SYMBOL(mcOpenDevice); + +//------------------------------------------------------------------------------ +mcResult_t mcCloseDevice( + uint32_t deviceId +) { + mcResult_t mcResult = MC_DRV_OK; + //static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + + MCDRV_DBG_VERBOSE("===%s()===", __func__); + + //pthread_mutex_lock(&mutex); // Enter critical section + do + { + mcore_device_t *device = resolveDeviceId(deviceId); + if (NULL == device) + { + MCDRV_DBG_ERROR("Device not found"); + mcResult = MC_DRV_ERR_UNKNOWN_DEVICE; + break; + } + connection_t *devCon = device->connection; + + // Return if not all sessions have been closed + if (mcore_device_hasSessions(device)) + { + MCDRV_DBG_ERROR("cannot close with sessions still pending"); + mcResult = MC_DRV_ERR_SESSION_PENDING; + break; + } + + mcDrvCmdCloseDevice_t mcDrvCmdCloseDevice = { + // C++ does not support C99 designated initializers + /* .header = */ { + /* .commandId = */ MC_DRV_CMD_CLOSE_DEVICE + } + }; + int len = connection_writeData( + devCon, + &mcDrvCmdCloseDevice, + sizeof(mcDrvCmdCloseDevice)); + // ignore error, but log details + if (len < 0) + { + MCDRV_DBG_ERROR("CMD_CLOSE_DEVICE writeCmd failed, ret=%d", len); + mcResult = MC_DRV_ERR_DAEMON_UNREACHABLE; + } + + mcDrvResponseHeader_t rspHeader; + len = connection_readDataBlock( + devCon, + &rspHeader, + sizeof(rspHeader)); + if (len != sizeof(rspHeader)) + { + MCDRV_DBG_ERROR("CMD_CLOSE_DEVICE readResp failed, ret=%d", len); + mcResult = MC_DRV_ERR_DAEMON_UNREACHABLE; + break; + } + + if (MC_DRV_RSP_OK != rspHeader.responseId) + { + MCDRV_DBG_ERROR("CMD_CLOSE_DEVICE failed, respId=%d", + rspHeader.responseId); + mcResult = MC_DRV_ERR_DAEMON_UNREACHABLE; + break; + } + + removeDevice(deviceId); + + } while (false); + + //pthread_mutex_unlock(&mutex); // Exit critical section + + return mcResult; +} +EXPORT_SYMBOL(mcCloseDevice); + +//------------------------------------------------------------------------------ +mcResult_t mcOpenSession( + mcSessionHandle_t *session, + const mcUuid_t *uuid, + uint8_t *tci, + uint32_t len +) { + mcResult_t mcResult = MC_DRV_OK; + //static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + + MCDRV_DBG_VERBOSE("===%s()===", __func__); + + //pthread_mutex_lock(&mutex); // Enter critical section + + do + { + if (NULL == session) + { + MCDRV_DBG_ERROR("Session is null"); + mcResult = MC_DRV_ERR_INVALID_PARAMETER; + break; + } + if (NULL == uuid) + { + MCDRV_DBG_ERROR("UUID is null"); + mcResult = MC_DRV_ERR_INVALID_PARAMETER; + break; + } + if (NULL == tci) + { + MCDRV_DBG_ERROR("TCI is null"); + mcResult = 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); + mcResult = MC_DRV_ERR_INVALID_PARAMETER; + break; + } + + // Get the device associated with the given session + mcore_device_t *device = resolveDeviceId(session->deviceId); + if (NULL == device) + { + MCDRV_DBG_ERROR("Device not found"); + mcResult = MC_DRV_ERR_UNKNOWN_DEVICE; + break; + } + connection_t *devCon = device->connection; + + // Get the physical address of the given TCI + wsm_ptr pWsm = mcore_device_findContiguousWsm(device, tci); + if (NULL == pWsm) + { + MCDRV_DBG_ERROR("Could not resolve physical address of TCI"); + mcResult = MC_DRV_ERR_INVALID_PARAMETER; + break; + } + + if (pWsm->len < len) + { + MCDRV_DBG_ERROR("length is more than allocated TCI"); + mcResult = MC_DRV_ERR_INVALID_PARAMETER; + break; + } + + // Prepare open session command + mcDrvCmdOpenSession_t cmdOpenSession = { + // C++ does not support C99 designated initializers + /* .header = */ { + /* .commandId = */ MC_DRV_CMD_OPEN_SESSION + }, + /* .payload = */ { + /* .deviceId = */ session->deviceId, + /* .uuid = */ *uuid, + /* .tci = */ (uint32_t)pWsm->physAddr, + /* .len = */ len + } + }; + + // Transmit command data + + int len = connection_writeData( + devCon, + &cmdOpenSession, + sizeof(cmdOpenSession)); + if (sizeof(cmdOpenSession) != len) + { + MCDRV_DBG_ERROR("CMD_OPEN_SESSION writeData failed, ret=%d", len); + mcResult = MC_DRV_ERR_DAEMON_UNREACHABLE; + break; + } + + // Read command response + + // read header first + mcDrvResponseHeader_t rspHeader; + len = connection_readDataBlock( + devCon, + &rspHeader, + sizeof(rspHeader)); + if (sizeof(rspHeader) != len) + { + MCDRV_DBG_ERROR("CMD_OPEN_SESSION readResp failed, ret=%d", len); + mcResult = MC_DRV_ERR_DAEMON_UNREACHABLE; + break; + } + + if (MC_DRV_RSP_OK != rspHeader.responseId) + { + MCDRV_DBG_ERROR("CMD_OPEN_SESSION failed, respId=%d", + rspHeader.responseId); + switch(rspHeader.responseId) + { + case MC_DRV_RSP_TRUSTLET_NOT_FOUND: + mcResult = 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: + mcResult = MC_DRV_ERR_DAEMON_UNREACHABLE; + break; + } + break; + } + + // read payload + mcDrvRspOpenSessionPayload_t rspOpenSessionPayload; + len = connection_readDataBlock( + devCon, + &rspOpenSessionPayload, + sizeof(rspOpenSessionPayload)); + if (sizeof(rspOpenSessionPayload) != len) + { + MCDRV_DBG_ERROR("CMD_OPEN_SESSION readPayload failed, ret=%d", len); + mcResult = MC_DRV_ERR_DAEMON_UNREACHABLE; + break; + } + + // Register session with handle + session->sessionId = rspOpenSessionPayload.sessionId; + + // Set up second channel for notifications + connection_t *sessionConnection = connection_new(); + //TODO: no real need to connect here? + if (!connection_connect(sessionConnection, MC_DAEMON_PID)) + { + MCDRV_DBG_ERROR("Could not setup netlink connection to PID %u", + MC_DAEMON_PID); + connection_cleanup(sessionConnection); + mcResult = MC_DRV_ERR_DAEMON_UNREACHABLE; + break; + } + + //TODO CONTINOUE HERE !!!! FIX RW RETURN HANDLING!!!! + + // Write command to use channel for notifications + mcDrvCmdNqConnect_t cmdNqConnect = { + // C++ does not support C99 designated initializers + /* .header = */ { + /* .commandId = */ MC_DRV_CMD_NQ_CONNECT + }, + /* .payload = */ { + /* .deviceId = */ session->deviceId, + /* .sessionId = */ session->sessionId, + /* .deviceSessionId = */ rspOpenSessionPayload.deviceSessionId, + /* .sessionMagic = */ rspOpenSessionPayload.sessionMagic + } + }; + connection_writeData(sessionConnection, + &cmdNqConnect, + sizeof(cmdNqConnect)); + + + // Read command response, header first + len = connection_readDataBlock( + sessionConnection, + &rspHeader, + sizeof(rspHeader)); + if (sizeof(rspHeader) != len) + { + MCDRV_DBG_ERROR("CMD_NQ_CONNECT readRsp failed, ret=%d", len); + connection_cleanup(sessionConnection); + mcResult = MC_DRV_ERR_DAEMON_UNREACHABLE; + break; + } + + if (MC_DRV_RSP_OK != rspHeader.responseId) + { + MCDRV_DBG_ERROR("CMD_NQ_CONNECT failed, respId=%d", + rspHeader.responseId); + connection_cleanup(sessionConnection); + mcResult = MC_DRV_ERR_NQ_FAILED; + break; + } + + // there is no payload. + + // Session has been established, new session object must be created + mcore_device_createNewSession( + device, + session->sessionId, + sessionConnection); + + } while (false); + + //pthread_mutex_unlock(&mutex); // Exit critical section + + return mcResult; +} +EXPORT_SYMBOL(mcOpenSession); + +//------------------------------------------------------------------------------ +mcResult_t mcCloseSession( + mcSessionHandle_t *session +) { + mcResult_t mcResult = MC_DRV_OK; + //static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + + MCDRV_DBG_VERBOSE("===%s()===", __func__); + + //pthread_mutex_lock(&mutex); // Enter critical section + + do + { + if (NULL == session) + { + MCDRV_DBG_ERROR("Session is null"); + mcResult = MC_DRV_ERR_INVALID_PARAMETER; + break; + } + + mcore_device_t *device = resolveDeviceId(session->deviceId); + if (NULL == device) + { + MCDRV_DBG_ERROR("Device not found"); + mcResult = MC_DRV_ERR_UNKNOWN_DEVICE; + break; + } + connection_t *devCon = device->connection; + + session_t *nqSession = mcore_device_resolveSessionId(device, + session->sessionId); + if (NULL == nqSession) + { + MCDRV_DBG_ERROR("Session not found"); + mcResult = MC_DRV_ERR_UNKNOWN_SESSION; + break; + } + + // Write close session command + mcDrvCmdCloseSession_t cmdCloseSession = { + // C++ does not support C99 designated initializers + /* .header = */ { + /* .commandId = */ MC_DRV_CMD_CLOSE_SESSION + }, + /* .payload = */ { + /* .sessionId = */ session->sessionId, + } + }; + connection_writeData( + devCon, + &cmdCloseSession, + sizeof(cmdCloseSession)); + + // Read command response + mcDrvResponseHeader_t rspHeader; + int len = connection_readDataBlock( + devCon, + &rspHeader, + sizeof(rspHeader)); + if (sizeof(rspHeader) != len) + { + MCDRV_DBG_ERROR("CMD_CLOSE_SESSION readRsp failed, ret=%d", len); + mcResult = MC_DRV_ERR_DAEMON_UNREACHABLE; + break; + } + + if (MC_DRV_RSP_OK != rspHeader.responseId) + { + MCDRV_DBG_ERROR("CMD_CLOSE_SESSION failed, respId=%d", + rspHeader.responseId); + mcResult = MC_DRV_ERR_UNKNOWN_DEVICE; + break; + } + + mcore_device_removeSession(device, session->sessionId); + mcResult = MC_DRV_OK; + + } while (false); + + //pthread_mutex_unlock(&mutex); // Exit critical section + + return mcResult; +} +EXPORT_SYMBOL(mcCloseSession); + +//------------------------------------------------------------------------------ +mcResult_t mcNotify( + mcSessionHandle_t *session +) { + mcResult_t mcResult = MC_DRV_OK; + + MCDRV_DBG_VERBOSE("===%s()===", __func__); + + do + { + if (NULL == session) + { + MCDRV_DBG_ERROR("Session is null"); + mcResult = MC_DRV_ERR_INVALID_PARAMETER; + break; + } + + mcore_device_t *device = resolveDeviceId(session->deviceId); + if (NULL == device) + { + MCDRV_DBG_ERROR("Device not found"); + mcResult = MC_DRV_ERR_UNKNOWN_DEVICE; + break; + } + connection_t *devCon = device->connection; + + session_t *nqsession = mcore_device_resolveSessionId(device, + session->sessionId); + if (NULL == nqsession) + { + MCDRV_DBG_ERROR("Session not found"); + mcResult = MC_DRV_ERR_UNKNOWN_SESSION; + break; + } + + mcDrvCmdNotify_t cmdNotify = { + // C++ does not support C99 designated initializers + /* .header = */ { + /* .commandId = */ MC_DRV_CMD_NOTIFY + }, + /* .payload = */ { + /* .sessionId = */ session->sessionId, + } + }; + + connection_writeData( + devCon, + &cmdNotify, + sizeof(cmdNotify)); + + // Daemon will not return a response + + } while(false); + + return mcResult; +} +EXPORT_SYMBOL(mcNotify); + +//------------------------------------------------------------------------------ +mcResult_t mcWaitNotification( + mcSessionHandle_t *session, + int32_t timeout +) { + mcResult_t mcResult = MC_DRV_OK; + + MCDRV_DBG_VERBOSE("===%s()===", __func__); + + do + { + if (NULL == session) + { + mcResult = MC_DRV_ERR_INVALID_PARAMETER; + break; + } + + mcore_device_t *device = resolveDeviceId(session->deviceId); + if (NULL == device) + { + MCDRV_DBG_ERROR("Device not found"); + mcResult = MC_DRV_ERR_UNKNOWN_DEVICE; + break; + } + + session_t *nqSession = mcore_device_resolveSessionId(device, + session->sessionId); + if (NULL == nqSession) + { + MCDRV_DBG_ERROR("Session not found"); + mcResult = MC_DRV_ERR_UNKNOWN_SESSION; + break; + } + + connection_t * nqconnection = nqSession->notificationConnection; + uint32_t count = 0; + + // Read notification queue till it's empty + for(;;) + { + notification_t notification; + ssize_t numRead = connection_readData( + nqconnection, + ¬ification, + sizeof(notification_t), + timeout); + //Exit on timeout in first run + //Later runs have timeout set to 0. -2 means, there is no more data. + if (0 == count && -2 == numRead) + { + MCDRV_DBG_ERROR("read timeout"); + mcResult = 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 (numRead != sizeof(notification_t)) + { + if (0 == count) + { + //failure in first read, notify it + mcResult = MC_DRV_ERR_NOTIFICATION; + MCDRV_DBG_ERROR("read notification failed, %i bytes received", + (int)numRead); + break; + } + else + { + // Read of the n-th notification failed/timeout. We don't + // tell the caller, as we got valid notifications before. + mcResult = MC_DRV_OK; + break; + } + } + + count++; + MCDRV_DBG_VERBOSE("readNq count=%d, SessionID=%d, Payload=%d", + count, notification.sessionId, notification.payload); + + if (0 != notification.payload) + { + // Session end point died -> store exit code + session_setErrorInfo(nqSession, notification.payload); + + mcResult = MC_DRV_INFO_NOTIFICATION; + break; + } + } // for(;;) + + } while (false); + + return mcResult; +} +EXPORT_SYMBOL(mcWaitNotification); + +//------------------------------------------------------------------------------ +mcResult_t mcMallocWsm( + uint32_t deviceId, + uint32_t align, + uint32_t len, + uint8_t **wsm, + uint32_t wsmFlags +) { + mcResult_t mcResult = MC_DRV_ERR_UNKNOWN; + //static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + + MCDRV_DBG_VERBOSE("===%s()===", __func__); + + //pthread_mutex_lock(&mutex); + + do + { + mcore_device_t *device = resolveDeviceId(deviceId); + if (NULL == device) + { + MCDRV_DBG_ERROR("Device not found"); + mcResult = MC_DRV_ERR_UNKNOWN_DEVICE; + break; + } + if(NULL == wsm) + { + mcResult = MC_DRV_ERR_INVALID_PARAMETER; + break; + } + + wsm_ptr pWsm = mcore_device_allocateContiguousWsm(device, len); + if (NULL == pWsm) + { + MCDRV_DBG_ERROR("Allocation of WSM failed"); + mcResult = MC_DRV_ERR_NO_FREE_MEMORY; + break; + } + + *wsm = (uint8_t*)pWsm->virtAddr; + mcResult = MC_DRV_OK; + + } while (false); + + //pthread_mutex_unlock(&mutex); // Exit critical section + + return mcResult; +} +EXPORT_SYMBOL(mcMallocWsm); + +//------------------------------------------------------------------------------ +mcResult_t mcFreeWsm( + uint32_t deviceId, + uint8_t *wsm +) { + mcResult_t mcResult = MC_DRV_ERR_UNKNOWN; + mcore_device_t *device; + + //static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + + MCDRV_DBG_VERBOSE("===%s()===", __func__); + + //pthread_mutex_lock(&mutex); // Enter critical section + + do { + + // Get the device associated wit the given session + device = resolveDeviceId(deviceId); + if (NULL == device) + { + MCDRV_DBG_ERROR("mcFreeWsm(): Device not found"); + mcResult = MC_DRV_ERR_UNKNOWN_DEVICE; + break; + } + + // find WSM object + wsm_ptr pWsm = mcore_device_findContiguousWsm(device, wsm); + if (NULL == pWsm) + { + MCDRV_DBG_ERROR("mcFreeWsm(): unknown address"); + mcResult = MC_DRV_ERR_INVALID_PARAMETER; + break; + } + + // Free the given virtual address + if (!mcore_device_freeContiguousWsm(device, pWsm)) + { + MCDRV_DBG_ERROR("mcFreeWsm(): Free of virtual address failed"); + mcResult = MC_DRV_ERR_FREE_MEMORY_FAILED; + break; + } + mcResult = MC_DRV_OK; + + } while (false); + + //pthread_mutex_unlock(&mutex); // Exit critical section + + return mcResult; +} +EXPORT_SYMBOL(mcFreeWsm); + +//------------------------------------------------------------------------------ +mcResult_t mcMap( + mcSessionHandle_t *sessionHandle, + void *buf, + uint32_t bufLen, + mcBulkMap_t *mapInfo +) { + mcResult_t mcResult = MC_DRV_ERR_UNKNOWN; + //static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + + MCDRV_DBG_VERBOSE("===%s()===", __func__); + + //pthread_mutex_lock(&mutex); // Enter critical section + + do + { + if (NULL == sessionHandle) + { + MCDRV_DBG_ERROR("sessionHandle is null"); + mcResult = MC_DRV_ERR_INVALID_PARAMETER; + break; + } + if (NULL == mapInfo) + { + MCDRV_DBG_ERROR("mapInfo is null"); + mcResult = MC_DRV_ERR_INVALID_PARAMETER; + break; + } + if (NULL == buf) + { + MCDRV_DBG_ERROR("buf is null"); + mcResult = MC_DRV_ERR_INVALID_PARAMETER; + break; + } + + // Determine device the session belongs to + mcore_device_t *device = resolveDeviceId(sessionHandle->deviceId); + if (NULL == device) { + MCDRV_DBG_ERROR("Device not found"); + mcResult = MC_DRV_ERR_UNKNOWN_DEVICE; + break; + } + connection_t *devCon = device->connection; + + // Get session + session_t *session = mcore_device_resolveSessionId(device, + sessionHandle->sessionId); + if (NULL == session) + { + MCDRV_DBG_ERROR("Session not found"); + mcResult = MC_DRV_ERR_UNKNOWN_SESSION; + break; + } + + // Register mapped bulk buffer to Kernel Module and keep mapped + // bulk buffer in mind + bulkBufferDescriptor_t *bulkBuf = session_addBulkBuf(session, buf, + bufLen); + if (NULL == bulkBuf) + { + MCDRV_DBG_ERROR("Error mapping bulk buffer"); + mcResult = MC_DRV_ERR_BULK_MAPPING; + break; + } + + + // Prepare map command + mcDrvCmdMapBulkMem_t mcDrvCmdMapBulkMem = { + // C++ does not support C99 designated initializers + /* .header = */ { + /* .commandId = */ MC_DRV_CMD_MAP_BULK_BUF + }, + /* .payload = */ { + /* .sessionId = */ session->sessionId, + /* .pAddrL2 = */ (uint32_t)bulkBuf->physAddrWsmL2, + /* .offsetPayload = */ (uint32_t)(bulkBuf->virtAddr) & 0xFFF, + /* .lenBulkMem = */ bulkBuf->len + } + }; + + // Transmit map command to MobiCore device + connection_writeData( + devCon, + &mcDrvCmdMapBulkMem, + sizeof(mcDrvCmdMapBulkMem)); + + // Read command response + mcDrvResponseHeader_t rspHeader; + int len = connection_readDataBlock( + devCon, + &rspHeader, + sizeof(rspHeader)); + if (sizeof(rspHeader) != len) + { + MCDRV_DBG_ERROR("CMD_MAP_BULK_BUF readRsp failed, ret=%d", len); + mcResult = MC_DRV_ERR_DAEMON_UNREACHABLE; + break; + } + + if (MC_DRV_RSP_OK != rspHeader.responseId) + { + MCDRV_DBG_ERROR("CMD_MAP_BULK_BUF failed, respId=%d", + rspHeader.responseId); + // REV We ignore Daemon Error code because client cannot + // handle it anyhow. + mcResult = MC_DRV_ERR_DAEMON_UNREACHABLE; + + // Unregister mapped bulk buffer from Kernel Module and + // remove mapped bulk buffer from session maintenance + if (!session_removeBulkBuf(session, buf)) + { + // Removing of bulk buffer not possible + MCDRV_DBG_ERROR("Unregistering of bulk memory from " + "Kernel Module failed"); + } + break; + } + + mcDrvRspMapBulkMemPayload_t rspMapBulkMemPayload; + connection_readDataBlock( + devCon, + &rspMapBulkMemPayload, + sizeof(rspMapBulkMemPayload)); + + // Set mapping info for Trustlet + mapInfo->sVirtualAddr = (void *)(rspMapBulkMemPayload.secureVirtualAdr); + mapInfo->sVirtualLen = bufLen; + mcResult = MC_DRV_OK; + + } while (false); + + //pthread_mutex_unlock(&mutex); // Exit critical section + + return mcResult; +} +EXPORT_SYMBOL(mcMap); + +//------------------------------------------------------------------------------ +mcResult_t mcUnmap( + mcSessionHandle_t *sessionHandle, + void *buf, + mcBulkMap_t *mapInfo +) { + mcResult_t mcResult = MC_DRV_ERR_UNKNOWN; + //static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + + MCDRV_DBG_VERBOSE("===%s()===", __func__); + + //pthread_mutex_lock(&mutex); // Enter critical section + + do + { + if (NULL == sessionHandle) + { + MCDRV_DBG_ERROR("sessionHandle is null"); + mcResult = MC_DRV_ERR_INVALID_PARAMETER; + break; + } + if (NULL == mapInfo) + { + MCDRV_DBG_ERROR("mapInfo is null"); + mcResult = MC_DRV_ERR_INVALID_PARAMETER; + break; + } + if (NULL == buf) + { + MCDRV_DBG_ERROR("buf is null"); + mcResult = MC_DRV_ERR_INVALID_PARAMETER; + break; + } + + // Determine device the session belongs to + mcore_device_t *device = resolveDeviceId(sessionHandle->deviceId); + if (NULL == device) + { + MCDRV_DBG_ERROR("Device not found"); + mcResult = MC_DRV_ERR_UNKNOWN_DEVICE; + break; + } + connection_t *devCon = device->connection; + + // Get session + session_t *session = mcore_device_resolveSessionId(device, + sessionHandle->sessionId); + if (NULL == session) + { + MCDRV_DBG_ERROR("Session not found"); + mcResult = MC_DRV_ERR_UNKNOWN_SESSION; + break; + } + + // Prepare unmap command + mcDrvCmdUnmapBulkMem_t cmdUnmapBulkMem = { + // C++ does not support C99 designated initializers + /* .header = */ { + /* .commandId = */ MC_DRV_CMD_UNMAP_BULK_BUF + }, + /* .payload = */ { + /* .sessionId = */ session->sessionId, + /* .secureVirtualAdr = */ (uint32_t)(mapInfo->sVirtualAddr), + /* .lenBulkMem = mapInfo->sVirtualLen*/ + } + }; + + connection_writeData( + devCon, + &cmdUnmapBulkMem, + sizeof(cmdUnmapBulkMem)); + + // Read command response + mcDrvResponseHeader_t rspHeader; + int len = connection_readDataBlock( + devCon, + &rspHeader, + sizeof(rspHeader)); + if (sizeof(rspHeader) != len) + { + MCDRV_DBG_ERROR("CMD_UNMAP_BULK_BUF readRsp failed, ret=%d", len); + mcResult = MC_DRV_ERR_DAEMON_UNREACHABLE; + break; + } + + if (MC_DRV_RSP_OK != rspHeader.responseId) + { + MCDRV_DBG_ERROR("CMD_UNMAP_BULK_BUF failed, respId=%d", + rspHeader.responseId); + // REV We ignore Daemon Error code because client + // cannot handle it anyhow. + mcResult = MC_DRV_ERR_DAEMON_UNREACHABLE; + break; + } + + mcDrvRspUnmapBulkMemPayload_t rspUnmapBulkMemPayload; + connection_readDataBlock( + devCon, + &rspUnmapBulkMemPayload, + sizeof(rspUnmapBulkMemPayload)); + + // REV axh: what about check the payload? + + // Unregister mapped bulk buffer from Kernel Module and remove mapped + // bulk buffer from session maintenance + if (!session_removeBulkBuf(session, buf)) + { + // Removing of bulk buffer not possible + MCDRV_DBG_ERROR("Unregistering of bulk memory from " + "Kernel Module failed"); + mcResult = MC_DRV_ERR_BULK_UNMAPPING; + break; + } + + mcResult = MC_DRV_OK; + + } while (false); + + //pthread_mutex_unlock(&mutex); // Exit critical section + + return mcResult; +} +EXPORT_SYMBOL(mcUnmap); + +//------------------------------------------------------------------------------ +mcResult_t mcGetSessionErrorCode( + mcSessionHandle_t *session, + int32_t *lastErr +) { + mcResult_t mcResult = MC_DRV_OK; + + MCDRV_DBG_VERBOSE("===%s()===", __func__); + + do + { + if (NULL == session || NULL == lastErr) + { + mcResult = MC_DRV_ERR_INVALID_PARAMETER; + break; + } + + // Get device + mcore_device_t *device = resolveDeviceId(session->deviceId); + if (NULL == device) + { + MCDRV_DBG_ERROR("mcGetSessionErrorCode(): Device not found"); + mcResult = MC_DRV_ERR_UNKNOWN_DEVICE; + break; + } + + // Get session + session_t *nqsession = mcore_device_resolveSessionId(device, + session->sessionId); + if (NULL == nqsession) + { + MCDRV_DBG_ERROR("mcGetSessionErrorCode(): Session not found"); + mcResult = MC_DRV_ERR_UNKNOWN_SESSION; + break; + } + + // get session error code from session + *lastErr = session_getLastErr(nqsession); + + } while (false); + + return mcResult; +} +EXPORT_SYMBOL(mcGetSessionErrorCode); + +//------------------------------------------------------------------------------ +mcResult_t mcDriverCtrl( + mcDriverCtrl_t param, + uint8_t *data, + uint32_t len +) { + MCDRV_DBG_WARN("not implemented"); + return MC_DRV_ERR_NOT_IMPLEMENTED; +} +EXPORT_SYMBOL(mcDriverCtrl); + +//------------------------------------------------------------------------------ +mcResult_t mcManage( + uint32_t deviceId, + uint8_t *data, + uint32_t len +) { + MCDRV_DBG_WARN("not implemented"); + return MC_DRV_ERR_NOT_IMPLEMENTED; +} +EXPORT_SYMBOL(mcManage); diff --git a/drivers/gud/MobiCoreKernelApi/common.h b/drivers/gud/MobiCoreKernelApi/common.h new file mode 100644 index 0000000..1593904 --- /dev/null +++ b/drivers/gud/MobiCoreKernelApi/common.h @@ -0,0 +1,93 @@ +/** + * + * Common data types + * + * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2011 --> + */ +#ifndef COMMON_H +#define COMMON_H + +#include "connection.h" +#include "mci.h" + +void mcapi_insert_connection( + connection_t *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/MobiCoreKernelApi/connection.c b/drivers/gud/MobiCoreKernelApi/connection.c new file mode 100644 index 0000000..cc801cb --- /dev/null +++ b/drivers/gud/MobiCoreKernelApi/connection.c @@ -0,0 +1,218 @@ +/** @addtogroup MCD_MCDIMPL_DAEMON_SRV + * @{ + * @file + * + * Connection data. + * + * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2011 --> + */ +#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" + +//------------------------------------------------------------------------------ +connection_t *connection_new( + void +) { + connection_t *conn = kzalloc(sizeof(connection_t), GFP_KERNEL); + conn->sequenceMagic = mcapi_unique_id(); + sema_init(&conn->dataSem, 1); + // No data available + sema_init(&conn->dataAvailableSem, 0); + + mcapi_insert_connection(conn); + return conn; +} + +//------------------------------------------------------------------------------ +connection_t *connection_create( + int socketDescriptor, + pid_t dest +) { + connection_t *conn = connection_new(); + + conn->peerPid = dest; + return conn; +} + + +//------------------------------------------------------------------------------ +void connection_cleanup( + connection_t *conn +) { + if (!conn) + return; + + kfree_skb(conn->skb); + + mcapi_remove_connection(conn->sequenceMagic); + kfree(conn); +} + + +//------------------------------------------------------------------------------ +bool connection_connect( + connection_t *conn, + pid_t dest +) { + // Nothing to connect + conn->peerPid = dest; + return true; +} + +//------------------------------------------------------------------------------ +size_t connection_readDataMsg( + connection_t *conn, + void *buffer, + uint32_t len +) { + size_t ret = -1; + MCDRV_DBG_VERBOSE("reading connection data %u, connection data left %u", + len, conn->dataLen); + // trying to read more than the left data + if (len > conn->dataLen) + { + ret = conn->dataLen; + memcpy(buffer, conn->dataStart, conn->dataLen); + conn->dataLen = 0; + } + else + { + ret = len; + memcpy(buffer, conn->dataStart, len); + conn->dataLen -= len; + conn->dataStart += len; + } + + if (conn->dataLen == 0) + { + conn->dataStart = NULL; + kfree_skb(conn->skb); + conn->skb = NULL; + } + MCDRV_DBG_VERBOSE("read %u", ret); + return ret; +} + +//------------------------------------------------------------------------------ +size_t connection_readDataBlock( + connection_t *conn, + void *buffer, + uint32_t len +) { + return connection_readData(conn, buffer, len, -1); +} + + +//------------------------------------------------------------------------------ +size_t connection_readData( + connection_t *conn, + void *buffer, + uint32_t len, + int32_t timeout +) { + size_t ret = 0; + + MCDRV_ASSERT(NULL != buffer); + MCDRV_ASSERT(NULL != conn->socketDescriptor); + + MCDRV_DBG_VERBOSE("read data len = %u for PID = %u", + len, conn->sequenceMagic); + + // Wait until data is available or timeout + //msecs_to_jiffies(-1) -> wait forever for the sem + if(down_timeout(&(conn->dataAvailableSem), msecs_to_jiffies(timeout))){ + MCDRV_DBG_VERBOSE("Timeout while trying to read the data sem"); + return -2; + } + + if(down_interruptible(&(conn->dataSem))){ + MCDRV_DBG_ERROR("interrupted while trying to read the data sem"); + return -1; + } + // Have data, use it + if (conn->dataLen > 0) { + ret = connection_readDataMsg(conn, buffer, len); + } + up(&(conn->dataSem)); + + // There is still some data left + if(conn->dataLen > 0) + up(&conn->dataAvailableSem); + + return ret; +} + +//------------------------------------------------------------------------------ +size_t connection_writeData( + connection_t *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->sequenceMagic); + do { + skb = nlmsg_new(NLMSG_SPACE(len), GFP_KERNEL); + if (!skb) { + ret = -1; + break; + } + + nlh = nlmsg_put(skb, 0, conn->sequenceMagic, 2, + NLMSG_LENGTH(len), NLM_F_REQUEST); + if (!nlh) { + ret = -1; + break; + } + memcpy(NLMSG_DATA(nlh), buffer, len); + + netlink_unicast(conn->socketDescriptor, skb, + conn->peerPid, MSG_DONTWAIT); + ret = len; + } while(0); + + if(!ret && skb != NULL){ + kfree_skb(skb); + } + + return ret; +} + +int connection_process( + connection_t *conn, + struct sk_buff *skb +) +{ + //is down_timeout a better choice? + if(down_interruptible(&(conn->dataSem))){ + MCDRV_DBG_ERROR("Interrupted while getting the data semaphore!"); + return -1; + } + + kfree_skb(conn->skb); + + /* Get a reference to the incomming skb */ + conn->skb = skb_get(skb); + if(conn->skb) { + conn->dataMsg = nlmsg_hdr(conn->skb); + conn->dataLen = NLMSG_PAYLOAD(conn->dataMsg, 0); + conn->dataStart = NLMSG_DATA(conn->dataMsg); + up(&(conn->dataAvailableSem)); + } + up(&(conn->dataSem)); + return 0; +} +/** @} */ diff --git a/drivers/gud/MobiCoreKernelApi/connection.h b/drivers/gud/MobiCoreKernelApi/connection.h new file mode 100644 index 0000000..ba7f509 --- /dev/null +++ b/drivers/gud/MobiCoreKernelApi/connection.h @@ -0,0 +1,120 @@ +/** @addtogroup MCD_MCDIMPL_DAEMON_SRV + * @{ + * @file + * + * Connection data. + * + * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2011 --> + */ +#ifndef CONNECTION_H_ +#define CONNECTION_H_ + +#include <linux/semaphore.h> + +#include <stddef.h> +#include <stdbool.h> + +#define MAX_PAYLOAD_SIZE 128 + +typedef struct { + struct sock *socketDescriptor; /**< Netlink socket */ + uint32_t sequenceMagic; /**< Random? magic to match requests/answers */ + + struct nlmsghdr *dataMsg; + uint32_t dataLen; /**< How much connection data is left */ + void *dataStart; /**< Start pointer of remaining data */ + struct sk_buff *skb; + + struct semaphore dataSem; /**< Data protection semaphore */ + struct semaphore dataAvailableSem; /**< Data protection semaphore */ + + pid_t selfPid; /**< PID address used for local connection */ + pid_t peerPid; /**< Remote PID for connection */ + + struct list_head list; /**< The list param for using the kernel lists*/ +} connection_t; + +connection_t* connection_new( + void +); + +connection_t* connection_create( + int socketDescriptor, + pid_t dest +); + +void connection_cleanup( + connection_t *conn +); + +/** + * Connect to destination. + * + * @param Destination pointer. + * @return true on success. + */ +bool connection_connect( + connection_t *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_readDataBlock( + connection_t *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_readData( + connection_t *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_writeData( + connection_t *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( + connection_t *conn, + struct sk_buff *skb +); + +typedef struct list_head connectionVector_t; + +#endif /* CONNECTION_H_ */ + +/** @} */ diff --git a/drivers/gud/MobiCoreKernelApi/device.c b/drivers/gud/MobiCoreKernelApi/device.c new file mode 100644 index 0000000..c7882a4 --- /dev/null +++ b/drivers/gud/MobiCoreKernelApi/device.c @@ -0,0 +1,260 @@ +/** @addtogroup MCD_IMPL_LIB + * @{ + * @file + * + * Client library device management. + * + * Device and Trustlet Session management Funtions. + * + * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2011 --> + */ +#include <linux/list.h> +#include <linux/slab.h> +#include "mcKernelApi.h" +#include "public/MobiCoreDriverApi.h" + +#include "device.h" +#include "common.h" + +//------------------------------------------------------------------------------ +wsm_ptr wsm_create( + addr_t virtAddr, + uint32_t len, + uint32_t handle, + addr_t physAddr //= NULL this may be unknown, so is can be omitted. + ) +{ + wsm_ptr pWsm = kzalloc(sizeof(wsm_t), GFP_KERNEL); + pWsm->virtAddr = virtAddr; + pWsm->len = len; + pWsm->handle = handle; + pWsm->physAddr = physAddr; + return pWsm; +} + + +//------------------------------------------------------------------------------ +mcore_device_t* mcore_device_create( + uint32_t deviceId, + connection_t *connection +) { + mcore_device_t *dev = kzalloc(sizeof(mcore_device_t), GFP_KERNEL); + dev->deviceId = deviceId; + dev->connection = connection; + + INIT_LIST_HEAD(&dev->sessionVector); + INIT_LIST_HEAD(&dev->wsmL2Vector); + + return dev; +} + + +//------------------------------------------------------------------------------ +void mcore_device_cleanup( + mcore_device_t *dev +) { + session_t *tmp; + wsm_ptr pWsm; + 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->sessionVector) { + tmp=list_entry(pos, session_t, list); + list_del(pos); + session_cleanup(tmp); + } + + // Free all allocated WSM descriptors + list_for_each_safe(pos, q, &dev->wsmL2Vector) { + pWsm=list_entry(pos, wsm_t, list); + //mcKMod_free(dev->pInstance, pWsm->handle); + list_del(pos); + kfree(pWsm); + } + connection_cleanup(dev->connection); + + mcore_device_close(dev); + kfree(dev); +} + + +//------------------------------------------------------------------------------ +bool mcore_device_open( + mcore_device_t *dev, + const char *deviceName +) { + dev->pInstance = mobicore_open(); + return (dev->pInstance != NULL); +} + + +//------------------------------------------------------------------------------ +void mcore_device_close( + mcore_device_t *dev +) { + mobicore_release(dev->pInstance); +} + + +//------------------------------------------------------------------------------ +bool mcore_device_hasSessions( + mcore_device_t *dev +) { + return !list_empty(&dev->sessionVector); +} + + +//------------------------------------------------------------------------------ +bool mcore_device_createNewSession( + mcore_device_t *dev, + uint32_t sessionId, + connection_t *connection +) { + // Check if sessionId already exists + if (mcore_device_resolveSessionId(dev, sessionId)) + { + MCDRV_DBG_ERROR(" session %u already exists", sessionId); + return false; + } + session_t *session = session_create(sessionId, dev->pInstance, connection); + list_add_tail(&(session->list), &(dev->sessionVector)); + return true; +} + + +//------------------------------------------------------------------------------ +bool mcore_device_removeSession( + mcore_device_t *dev, + uint32_t sessionId +) { + bool ret = false; + session_t *tmp; + struct list_head *pos, *q; + + list_for_each_safe(pos, q, &dev->sessionVector) { + tmp=list_entry(pos, session_t, list); + if (tmp->sessionId == sessionId) { + list_del(pos); + session_cleanup(tmp); + ret = true; + break; + } + } + return ret; +} + + +//------------------------------------------------------------------------------ +session_t *mcore_device_resolveSessionId( + mcore_device_t *dev, + uint32_t sessionId +) { + session_t *ret = NULL; + session_t *tmp; + struct list_head *pos; + + + // Get session_t for sessionId + list_for_each(pos, &dev->sessionVector) { + tmp=list_entry(pos, session_t, list); + if (tmp->sessionId == sessionId) { + ret = tmp; + break; + } + } + return ret; +} + + +//------------------------------------------------------------------------------ +wsm_ptr mcore_device_allocateContiguousWsm( + mcore_device_t *dev, + uint32_t len +) { + wsm_ptr pWsm = NULL; + do + { + if (0 == len) + { + break; + } + + // Allocate shared memory + addr_t virtAddr; + uint32_t handle; + addr_t physAddr; + int ret = mobicore_allocate_wsm(dev->pInstance, + len, + &handle, + &virtAddr, + &physAddr); + if (0 != ret) + { + break; + } + + // Register (vaddr,paddr) with device + pWsm = wsm_create(virtAddr, len, handle,physAddr); + + list_add_tail(&(pWsm->list), &(dev->wsmL2Vector)); + + } while (0); + + // Return pointer to the allocated memory + return pWsm; +} + + +//------------------------------------------------------------------------------ +bool mcore_device_freeContiguousWsm( + mcore_device_t *dev, + wsm_ptr pWsm +) { + bool ret = false; + wsm_t *tmp; + struct list_head *pos; + + list_for_each(pos, &dev->wsmL2Vector) { + tmp=list_entry(pos, wsm_t, list); + if (tmp == pWsm) { + ret = true; + break; + } + } + + if (ret) + { + MCDRV_DBG_VERBOSE("freeWsm virtAddr=0x%p, handle=%d", + pWsm->virtAddr,pWsm->handle); + + // ignore return code + mobicore_free(dev->pInstance, pWsm->handle); + + list_del(pos); + kfree(pWsm); + } + return ret; +} + + +//------------------------------------------------------------------------------ +wsm_ptr mcore_device_findContiguousWsm( + mcore_device_t *dev, + addr_t virtAddr +) { + wsm_ptr pWsm; + struct list_head *pos; + + list_for_each(pos, &dev->wsmL2Vector) { + pWsm=list_entry(pos, wsm_t, list); + if (virtAddr == pWsm->virtAddr) { + return pWsm; + } + } + + return NULL; +} + +/** @} */ diff --git a/drivers/gud/MobiCoreKernelApi/device.h b/drivers/gud/MobiCoreKernelApi/device.h new file mode 100644 index 0000000..c348eb5 --- /dev/null +++ b/drivers/gud/MobiCoreKernelApi/device.h @@ -0,0 +1,131 @@ +/** @addtogroup MCD_IMPL_LIB + * @{ + * @file + * + * Client library device management. + * + * Device and Trustlet Session management Functions. + * + * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2011 --> + */ +#ifndef DEVICE_H_ +#define DEVICE_H_ + +#include <linux/list.h> + +#include "connection.h" +#include "session.h" +#include "wsm.h" + + +typedef struct { + sessionVector_t sessionVector; /**< MobiCore Trustlet session associated with the device */ + wsmVector_t wsmL2Vector; /**< WSM L2 Table */ + + uint32_t deviceId; /**< Device identifier */ + connection_t *connection; /**< The device connection */ + struct mcInstance *pInstance; /**< MobiCore Driver instance */ + + struct list_head list; /**< The list param for using the kernel lists*/ +} mcore_device_t; + +mcore_device_t *mcore_device_create( + uint32_t deviceId, + connection_t *connection +); + +void mcore_device_cleanup( + 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( + mcore_device_t *dev, + const char *deviceName +); + +/** + * Closes the device. + */ +void mcore_device_close( + 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_hasSessions( + mcore_device_t *dev +); + +/** + * Add a session to the device. + * @param sessionId session ID + * @param connection session connection + */ +bool mcore_device_createNewSession( + mcore_device_t *dev, + uint32_t sessionId, + connection_t *connection +); + +/** + * Remove the specified session from the device. + * The session object will be destroyed and all resources associated with it will be freed. + * + * @param sessionId Session of the session to remove. + * @return true if a session has been found and removed. + */ +bool mcore_device_removeSession( + mcore_device_t *dev, + uint32_t sessionId +); + +/** + * Get as session object for a given session ID. + * @param sessionId Identified of a previously opened session. + * @return Session object if available or NULL if no session has been found. + */ +session_t *mcore_device_resolveSessionId( + mcore_device_t *dev, + uint32_t sessionId +); + +/** + * 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. + */ +wsm_ptr mcore_device_allocateContiguousWsm( + 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_freeContiguousWsm( + mcore_device_t *dev, + wsm_ptr pWsm +); + +/** + * Get a WSM object for a given virtual address. + * @param vaddr The virtual address which has been allocate with mcMallocWsm() in advance. + * @return the WSM object or NULL if no address has been found. + */ +wsm_ptr mcore_device_findContiguousWsm( + mcore_device_t *dev, + addr_t virtAddr +); + +#endif /* DEVICE_H_ */ + +/** @} */ diff --git a/drivers/gud/MobiCoreKernelApi/main.c b/drivers/gud/MobiCoreKernelApi/main.c new file mode 100644 index 0000000..e210483 --- /dev/null +++ b/drivers/gud/MobiCoreKernelApi/main.c @@ -0,0 +1,170 @@ +#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 mcKernelApiCtx +{ + struct sock *sk; + struct list_head peers; + atomic_t counter; +}; + +struct mcKernelApiCtx* modCtx = NULL; + +/*----------------------------------------------------------------------------*/ +/* get a unique ID */ +unsigned int mcapi_unique_id( + void +) +{ + return (unsigned int)atomic_inc_return( + &(modCtx->counter)); +} + + +//------------------------------------------------------------------------------ +static connection_t* mcapi_find_connection( + uint32_t seq +) +{ + connection_t *tmp; + struct list_head *pos; + + // Get session_t for sessionId + list_for_each(pos, &modCtx->peers) { + tmp=list_entry(pos, connection_t, list); + if (tmp->sequenceMagic == seq) { + return tmp; + } + } + + return NULL; +} + +//------------------------------------------------------------------------------ +void mcapi_insert_connection( + connection_t *connection +) +{ + list_add_tail(&(connection->list), &(modCtx->peers)); + connection->socketDescriptor = modCtx->sk; +} + +void mcapi_remove_connection( + uint32_t seq +) +{ + connection_t *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, &modCtx->peers) { + tmp=list_entry(pos, connection_t, list); + if (tmp->sequenceMagic == seq) { + list_del(pos); + break; + } + } +} + +//------------------------------------------------------------------------------ +static int mcapi_process( + struct sk_buff *skb, + struct nlmsghdr *nlh +) +{ + connection_t *c; + int length; + int seq; + pid_t pid; + + 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 ); + + c = mcapi_find_connection(seq); + if(!c){ + MCDRV_ERROR("Invalid incomming connection - seq=%u!", seq); + return -1; + } + + // Pass the buffer to the appropriate connection + connection_process(c, skb); + + return 0; +} + +//------------------------------------------------------------------------------ +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"); + + modCtx = kzalloc(sizeof(struct mcKernelApiCtx), GFP_KERNEL); + + /* start kernel thread */ + modCtx->sk = netlink_kernel_create(&init_net, MC_DAEMON_NETLINK, 0, + mcapi_callback, NULL, THIS_MODULE); + + if (!modCtx->sk) { + MCDRV_ERROR("register of recieve handler failed"); + return -EFAULT; + } + + INIT_LIST_HEAD(&modCtx->peers); + return 0; +} + +static void __exit mcapi_exit(void) +{ + printk(KERN_INFO "Unloading Mobicore API module.\n"); + + if (modCtx->sk != NULL) + { + netlink_kernel_release(modCtx->sk); + modCtx->sk = NULL; + } + kfree(modCtx); + modCtx = NULL; +} + +module_init(mcapi_init); +module_exit(mcapi_exit); + +MODULE_AUTHOR("Giesecke & Devrient GmbH"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("MobiCore API driver");
\ No newline at end of file diff --git a/drivers/gud/MobiCoreKernelApi/public/MobiCoreDriverApi.h b/drivers/gud/MobiCoreKernelApi/public/MobiCoreDriverApi.h new file mode 100644 index 0000000..460eb4b --- /dev/null +++ b/drivers/gud/MobiCoreKernelApi/public/MobiCoreDriverApi.h @@ -0,0 +1,386 @@ +/** + * @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 2010 - 2011 --> + */ +#ifndef MCDRIVER_H_ +#define MCDRIVER_H_ + +#define __MC_CLIENT_LIB_API + +#include "mcUuid.h" + +/** + * Return values of MobiCore driver functions. + */ +typedef enum +{ + MC_DRV_OK = 0, /**< Function call succeeded. */ + MC_DRV_NO_NOTIFICATION = 1, /**< No notification available. */ + MC_DRV_ERR_NOTIFICATION = 2, /**< Error during notification on communication level. */ + MC_DRV_ERR_NOT_IMPLEMENTED = 3, /**< Function not implemented. */ + MC_DRV_ERR_OUT_OF_RESOURCES = 4, /**< No more resources available. */ + MC_DRV_ERR_INIT = 5, /**< Driver initialization failed. */ + MC_DRV_ERR_UNKNOWN = 6, /**< Unknown error. */ + MC_DRV_ERR_UNKNOWN_DEVICE = 7, /**< The specified device is unknown. */ + MC_DRV_ERR_UNKNOWN_SESSION = 8, /**< The specified session is unknown. */ + MC_DRV_ERR_INVALID_OPERATION = 9, /**< The specified operation is not allowed. */ + MC_DRV_ERR_INVALID_RESPONSE = 10, /**< The response header from the MC is invalid. */ + MC_DRV_ERR_TIMEOUT = 11, /**< Function call timed out. */ + MC_DRV_ERR_NO_FREE_MEMORY = 12, /**< Can not allocate additional memory. */ + MC_DRV_ERR_FREE_MEMORY_FAILED = 13, /**< Free memory failed. */ + MC_DRV_ERR_SESSION_PENDING = 14, /**< Still some open sessions pending. */ + MC_DRV_ERR_DAEMON_UNREACHABLE = 15, /**< MC daemon not reachable */ + MC_DRV_ERR_INVALID_DEVICE_FILE = 16, /**< The device file of the kernel module could not be opened. */ + MC_DRV_ERR_INVALID_PARAMETER = 17, /**< Invalid parameter. */ + MC_DRV_ERR_KERNEL_MODULE = 18, /**< Unspecified error from Kernel Module*/ + MC_DRV_ERR_BULK_MAPPING = 19, /**< Error during mapping of additional bulk memory to session. */ + MC_DRV_ERR_BULK_UNMAPPING = 20, /**< Error during unmapping of additional bulk memory to session. */ + MC_DRV_INFO_NOTIFICATION = 21, /**< Notification received, exit code available. */ + MC_DRV_ERR_NQ_FAILED = 22 /**< Set up of NWd connection failed. */ +}mcResult_t; + + +/** + * Driver control command. + */ +typedef enum { + MC_CTRL_GET_VERSION = 1 /**< Return the driver version */ +}mcDriverCtrl_t; + + +/** 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. + */ +typedef struct { + uint32_t sessionId; /**< MobiCore session ID */ + uint32_t deviceId; /**< Device ID the session belongs to */ +} mcSessionHandle_t; + +/** 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 mcMap(). + * 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. + */ +typedef struct { + void *sVirtualAddr; /**< The virtual address of the Bulk buffer regarding the address space of the Trustlet, already includes a possible offset! */ + uint32_t sVirtualLen; /**< Length of the mapped Bulk buffer */ +} mcBulkMap_t; + + +#define MC_DEVICE_ID_DEFAULT 0 /**< The default device ID */ +#define MC_INFINITE_TIMEOUT ((int32_t)(-1)) /**< Wait infinite for a response of the MC. */ +#define MC_NO_TIMEOUT 0 /**< Do not wait for a response of the MC. */ +#define MC_MAX_TCI_LEN 0x100000 /**< TCI/DCI must not exceed 1MiB */ + + + +/** Open a new connection to a MobiCore device. + * + * mcOpenDevice() 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] deviceId 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 deviceId 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 mcResult_t mcOpenDevice( + uint32_t deviceId +); + +/** 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] deviceId 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 mcResult_t mcCloseDevice( + uint32_t deviceId +); + +/** 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.deviceId 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] tciLen 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 mcResult_t mcOpenSession( + mcSessionHandle_t *session, + const mcUuid_t *uuid, + uint8_t *tci, + uint32_t tciLen +); + +/** Close a Trustlet session. + * + * Closes the specified MobiCore session. The call will block until the session has been closed. + * + * @pre Device deviceId 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 mcResult_t mcCloseSession( + mcSessionHandle_t *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 mcWaitNotification(). + * @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 mcResult_t mcNotify( + mcSessionHandle_t *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 mcGetSessionErrorCode(). + * @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 mcResult_t mcWaitNotification( + mcSessionHandle_t *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] deviceId 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] wsmFlags Platform specific flags describing the memory to be allocated. + * + * @attention: align and wsmFlags 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 mcResult_t mcMallocWsm( + uint32_t deviceId, + uint32_t align, + uint32_t len, + uint8_t **wsm, + uint32_t wsmFlags +); + +/** + * Free a block of world shared memory (WSM). + * The MC driver will free a block of world shared memory (WSM) previously allocated with + * mcMallocWsm(). The caller has to assure that the address handed over to the driver + * is a valid WSM address. + * + * @param [in] deviceId 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 mcResult_t mcFreeWsm( + uint32_t deviceId, + 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 deviceId and the sessionId. 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] mapInfo 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 mcResult_t mcMap( + mcSessionHandle_t *session, + void *buf, + uint32_t len, + mcBulkMap_t *mapInfo +); + +/** + * 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 mcUnmap! + * + * @param [in] session Session handle with information of the deviceId and the sessionId. 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] mapInfo Information structure about the mapped Bulk buffer between the TLC (Nwd) and + * the TL (Swd). + * @attention The clientlib currently ignores the len field in mapInfo. + * + * @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 mcResult_t mcUnmap( + mcSessionHandle_t *session, + void *buf, + mcBulkMap_t *mapInfo +); + + +/** + * @attention: Not implemented. + * Execute driver specific command. + * mcDriverCtrl() 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 mcResult_t mcDriverCtrl( + mcDriverCtrl_t param, + uint8_t *data, + uint32_t len +); + +/** + * @attention: Not implemented. + * Execute application management command. + * mcManage() shall be used to exchange application management commands with the MobiCore. + * The MobiCore Application Management Protocol is described in [MCAMP]. + * + * @param [in] deviceId 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 mcResult_t mcManage( + uint32_t deviceId, + 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 deviceId and the sessionId. + * @param [out] lastErr >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 mcResult_t mcGetSessionErrorCode( + mcSessionHandle_t *session, + int32_t *lastErr +); + +#endif /** MCDRIVER_H_ */ + +/** @} */ diff --git a/drivers/gud/MobiCoreKernelApi/public/MobiCoreDriverCmd.h b/drivers/gud/MobiCoreKernelApi/public/MobiCoreDriverCmd.h new file mode 100644 index 0000000..ae23d75 --- /dev/null +++ b/drivers/gud/MobiCoreKernelApi/public/MobiCoreDriverCmd.h @@ -0,0 +1,266 @@ +/** @addtogroup MCD_MCDIMPL_DAEMON + * @{ + * @file + * + * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2011 --> + */ +#ifndef MCDAEMON_H_ +#define MCDAEMON_H_ + + + + +#define SOCK_PATH "/dev/socket/mcdaemon" +#include "mcUuid.h" + +typedef enum { + 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 +} mcDrvCmd_t; + + +typedef enum { + 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, +} mcDrvRsp_t; + + +typedef struct { + uint32_t commandId; +} mcDrvCommandHeader_t, *mcDrvCommandHeader_ptr; + +typedef struct { + uint32_t responseId; +} mcDrvResponseHeader_t, *mcDrvResponseHeader_ptr; + +#define MC_DEVICE_ID_DEFAULT 0 /**< The default device ID */ + + +//-------------------------------------------------------------- +typedef struct{ + uint32_t deviceId; +} mcDrvCmdOpenDevicePayload_t, *mcDrvCmdOpenDevicePayload_ptr; + +typedef struct{ + mcDrvCommandHeader_t header; + mcDrvCmdOpenDevicePayload_t payload; +} mcDrvCmdOpenDevice_t, *mcDrvCmdOpenDevice_ptr; + + +typedef struct{ + // empty +} mcDrvRspOpenDevicePayload_t, *mcDrvRspOpenDevicePayload_ptr; + +typedef struct{ + mcDrvResponseHeader_t header; + mcDrvRspOpenDevicePayload_t payload; +} mcDrvRspOpenDevice_t, *mcDrvRspOpenDevice_ptr; + + +//-------------------------------------------------------------- +typedef struct{ + mcDrvCommandHeader_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)) +} mcDrvCmdCloseDevice_t, *mcDrvCmdCloseDevice_ptr; + + +typedef struct{ + // empty +} mcDrvRspCloseDevicePayload_t, *mcDrvRspCloseDevicePayload_ptr; + +typedef struct{ + mcDrvResponseHeader_t header; + mcDrvRspCloseDevicePayload_t payload; +} mcDrvRspCloseDevice_t, *mcDrvRspCloseDevice_ptr; + + +//-------------------------------------------------------------- +typedef struct{ + uint32_t deviceId; + mcUuid_t uuid; + uint32_t tci; + uint32_t len; +} mcDrvCmdOpenSessionPayload_t, *mcDrvCmdOpenSessionPayload_ptr; + +typedef struct{ + mcDrvCommandHeader_t header; + mcDrvCmdOpenSessionPayload_t payload; +} mcDrvCmdOpenSession_t, *mcDrvCmdOpenSession_ptr; + + +typedef struct{ + uint32_t deviceId; + uint32_t sessionId; + uint32_t deviceSessionId; + uint32_t mcResult; + uint32_t sessionMagic; +} mcDrvRspOpenSessionPayload_t, *mcDrvRspOpenSessionPayload_ptr; + +typedef struct{ + mcDrvResponseHeader_t header; + mcDrvRspOpenSessionPayload_t payload; +} mcDrvRspOpenSession_t, *mcDrvRspOpenSession_ptr; + + +//-------------------------------------------------------------- +typedef struct{ + uint32_t sessionId; +} mcDrvCmdCloseSessionPayload_t, *mcDrvCmdCloseSessionPayload_ptr; + +typedef struct{ + mcDrvCommandHeader_t header; + mcDrvCmdCloseSessionPayload_t payload; +} mcDrvCmdCloseSession_t, *mcDrvCmdCloseSession_ptr; + + +typedef struct{ + // empty +} mcDrvRspCloseSessionPayload_t, *mcDrvRspCloseSessionPayload_ptr; + +typedef struct{ + mcDrvResponseHeader_t header; + mcDrvRspCloseSessionPayload_t payload; +} mcDrvRspCloseSession_t, *mcDrvRspCloseSession_ptr; + + +//-------------------------------------------------------------- +typedef struct{ + uint32_t sessionId; +} mcDrvCmdNotifyPayload_t, *mcDrvCmdNotifyPayload_ptr; + +typedef struct{ + mcDrvCommandHeader_t header; + mcDrvCmdNotifyPayload_t payload; +} mcDrvCmdNotify_t, *mcDrvCmdNotify_ptr; + + +typedef struct{ + // empty +} mcDrvRspNotifyPayload_t, *mcDrvRspNotifyPayload_ptr; + +typedef struct{ + mcDrvResponseHeader_t header; + mcDrvRspNotifyPayload_t payload; +} mcDrvRspNotify_t, *mcDrvRspNotify_ptr; + + +//-------------------------------------------------------------- +typedef struct{ + uint32_t sessionId; + uint32_t pAddrL2; + uint32_t offsetPayload; + uint32_t lenBulkMem; +} mcDrvCmdMapBulkMemPayload_t, *mcDrvCmdMapBulkMemPayload_ptr; + +typedef struct{ + mcDrvCommandHeader_t header; + mcDrvCmdMapBulkMemPayload_t payload; +} mcDrvCmdMapBulkMem_t, *mcDrvCmdMapBulkMem_ptr; + + +typedef struct{ + uint32_t sessionId; + uint32_t secureVirtualAdr; + uint32_t mcResult; +} mcDrvRspMapBulkMemPayload_t, *mcDrvRspMapBulkMemPayload_ptr; + +typedef struct{ + mcDrvResponseHeader_t header; + mcDrvRspMapBulkMemPayload_t payload; +} mcDrvRspMapBulkMem_t, *mcDrvRspMapBulkMem_ptr; + + +//-------------------------------------------------------------- +typedef struct{ + uint32_t sessionId; + uint32_t secureVirtualAdr; + uint32_t lenBulkMem; +} mcDrvCmdUnmapBulkMemPayload_t, *mcDrvCmdUnmapBulkMemPayload_ptr; + +typedef struct{ + mcDrvCommandHeader_t header; + mcDrvCmdUnmapBulkMemPayload_t payload; +} mcDrvCmdUnmapBulkMem_t, *mcDrvCmdUnmapBulkMem_ptr; + + +typedef struct{ + uint32_t responseId; + uint32_t sessionId; + uint32_t mcResult; +} mcDrvRspUnmapBulkMemPayload_t, *mcDrvRspUnmapBulkMemPayload_ptr; + +typedef struct{ + mcDrvResponseHeader_t header; + mcDrvRspUnmapBulkMemPayload_t payload; +} mcDrvRspUnmapBulkMem_t, *mcDrvRspUnmapBulkMem_ptr; + + +//-------------------------------------------------------------- +typedef struct { + uint32_t deviceId; + uint32_t sessionId; + uint32_t deviceSessionId; + uint32_t sessionMagic; //Random data +} mcDrvCmdNqConnectPayload_t, *mcDrvCmdNqConnectPayload_ptr; + +typedef struct { + mcDrvCommandHeader_t header; + mcDrvCmdNqConnectPayload_t payload; +} mcDrvCmdNqConnect_t, *mcDrvCmdNqConnect_ptr; + + +typedef struct { + // empty; +} mcDrvRspNqConnectPayload_t, *mcDrvRspNqConnectPayload_ptr; + +typedef struct{ + mcDrvResponseHeader_t header; + mcDrvRspNqConnectPayload_t payload; +} mcDrvRspNqConnect_t, *mcDrvRspNqConnect_ptr; + + +//-------------------------------------------------------------- +typedef union { + mcDrvCommandHeader_t header; + mcDrvCmdOpenDevice_t mcDrvCmdOpenDevice; + mcDrvCmdCloseDevice_t mcDrvCmdCloseDevice; + mcDrvCmdOpenSession_t mcDrvCmdOpenSession; + mcDrvCmdCloseSession_t mcDrvCmdCloseSession; + mcDrvCmdNqConnect_t mcDrvCmdNqConnect; + mcDrvCmdNotify_t mcDrvCmdNotify; + mcDrvCmdMapBulkMem_t mcDrvCmdMapBulkMem; + mcDrvCmdUnmapBulkMem_t mcDrvCmdUnmapBulkMem; +} mcDrvCommand_t, *mcDrvCommand_ptr; + +typedef union { + mcDrvResponseHeader_t header; + mcDrvRspOpenDevice_t mcDrvRspOpenDevice; + mcDrvRspCloseDevice_t mcDrvRspCloseDevice; + mcDrvRspOpenSession_t mcDrvRspOpenSession; + mcDrvRspCloseSession_t mcDrvRspCloseSession; + mcDrvRspNqConnect_t mcDrvRspNqConnect; + mcDrvRspNotify_t mcDrvRspNotify; + mcDrvRspMapBulkMem_t mcDrvRspMapBulkMem; + mcDrvRspUnmapBulkMem_t mcDrvRspUnmapBulkMem; +} mcDrvResponse_t, *mcDrvResponse_ptr; + +#endif /* MCDAEMON_H_ */ + +/** @} */ diff --git a/drivers/gud/MobiCoreKernelApi/session.c b/drivers/gud/MobiCoreKernelApi/session.c new file mode 100644 index 0000000..3c21ac8 --- /dev/null +++ b/drivers/gud/MobiCoreKernelApi/session.c @@ -0,0 +1,202 @@ +/** @addtogroup MCD_IMPL_LIB + * @{ + * @file + * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2011 --> + */ +#include <linux/types.h> +#include <linux/slab.h> +#include "mcKernelApi.h" +#include "public/MobiCoreDriverApi.h" + +#include "session.h" + +//------------------------------------------------------------------------------ +bulkBufferDescriptor_t* bulkBufferDescriptor_create( + addr_t virtAddr, + uint32_t len, + uint32_t handle, + addr_t physAddrWsmL2 +) { + bulkBufferDescriptor_t *desc = kzalloc(sizeof(bulkBufferDescriptor_t), + GFP_KERNEL); + desc->virtAddr = virtAddr; + desc->len = len; + desc->handle = handle; + desc->physAddrWsmL2 = physAddrWsmL2; + return desc; +} + +//------------------------------------------------------------------------------ +session_t *session_create( + uint32_t sessionId, + void *pInstance, + connection_t *connection +) { + session_t *session = kzalloc(sizeof(session_t), GFP_KERNEL); + session->sessionId = sessionId; + session->pInstance = pInstance; + session->notificationConnection = connection; + + session->sessionInfo.lastErr = SESSION_ERR_NO; + session->sessionInfo.state = SESSION_STATE_INITIAL; + + INIT_LIST_HEAD(&(session->bulkBufferDescriptors)); + return session; +} + + +//------------------------------------------------------------------------------ +void session_cleanup( + session_t *session +) { + bulkBufferDescriptor_t *pBlkBufDescr; + struct list_head *pos, *q; + + // Unmap still mapped buffers + list_for_each_safe(pos, q, &session->bulkBufferDescriptors) { + pBlkBufDescr=list_entry(pos, bulkBufferDescriptor_t, list); + + MCDRV_DBG_VERBOSE("Physical Address of L2 Table = 0x%X, handle= %d", + (unsigned int)pBlkBufDescr->physAddrWsmL2, + pBlkBufDescr->handle); + + // ignore any error, as we cannot do anything in this case. + int ret = mobicore_unmap_vmem(session->pInstance, + pBlkBufDescr->handle); + if (0 != ret) + { + MCDRV_DBG_ERROR("mobicore_unmap_vmem failed: %d",ret); + } + + list_del(pos); + kfree(pBlkBufDescr); + } + + // Finally delete notification connection + connection_cleanup(session->notificationConnection); + kfree(session); +} + + +//------------------------------------------------------------------------------ +void session_setErrorInfo( + session_t *session, + int32_t err +) { + session->sessionInfo.lastErr = err; +} + + +//------------------------------------------------------------------------------ +int32_t session_getLastErr( + session_t *session +) { + return session->sessionInfo.lastErr; +} + + +//------------------------------------------------------------------------------ +bulkBufferDescriptor_t* session_addBulkBuf( + session_t *session, + addr_t buf, + uint32_t len +) { + bulkBufferDescriptor_t* blkBufDescr = NULL; + bulkBufferDescriptor_t *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->bulkBufferDescriptors) { + tmp=list_entry(pos, bulkBufferDescriptor_t, list); + if (tmp->virtAddr == buf) + { + return NULL; + } + } + + do + { + // Prepare the interface structure for memory registration in + // Kernel Module + addr_t pPhysWsmL2; + uint32_t handle; + + int ret = mobicore_map_vmem(session->pInstance, + buf, + len, + &handle, + &pPhysWsmL2); + + if (0 != ret) { + 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)pPhysWsmL2, + handle); + + // Create new descriptor + blkBufDescr = bulkBufferDescriptor_create( + buf, + len, + handle, + pPhysWsmL2); + + // Add to vector of descriptors + list_add_tail(&(blkBufDescr->list), &(session->bulkBufferDescriptors)); + } while (0); + + return blkBufDescr; +} + + +//------------------------------------------------------------------------------ +bool session_removeBulkBuf( + session_t *session, + addr_t virtAddr +) { + bool ret = true; + bulkBufferDescriptor_t *pBlkBufDescr = NULL; + bulkBufferDescriptor_t *tmp; + struct list_head *pos, *q; + + MCDRV_DBG_VERBOSE("Virtual Address = 0x%X", (unsigned int) virtAddr); + + // Search and remove bulk buffer descriptor + list_for_each_safe(pos, q, &session->bulkBufferDescriptors) { + tmp=list_entry(pos, bulkBufferDescriptor_t, list); + if (tmp->virtAddr == virtAddr) + { + pBlkBufDescr = tmp; + list_del(pos); + break; + } + } + + if (NULL == pBlkBufDescr) + { + MCDRV_DBG_ERROR("Virtual Address not found"); + ret = false; + } + else + { + MCDRV_DBG_VERBOSE("WsmL2 phys=0x%X, handle=%d", + (unsigned int)pBlkBufDescr->physAddrWsmL2, pBlkBufDescr->handle); + + // ignore any error, as we cannot do anything + int ret = mobicore_unmap_vmem(session->pInstance, + pBlkBufDescr->handle); + if (0 != ret) + { + MCDRV_DBG_ERROR("mobicore_unmap_vmem failed: %d",ret); + } + + kfree(pBlkBufDescr); + } + + return ret; +} + +/** @} */ diff --git a/drivers/gud/MobiCoreKernelApi/session.h b/drivers/gud/MobiCoreKernelApi/session.h new file mode 100644 index 0000000..e0e8d34 --- /dev/null +++ b/drivers/gud/MobiCoreKernelApi/session.h @@ -0,0 +1,131 @@ +/** @addtogroup MCD_IMPL_LIB + * @{ + * @file + * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2011 --> + */ +#ifndef SESSION_H_ +#define SESSION_H_ + +#include "common.h" + +#include <linux/list.h> +#include "connection.h" + + +typedef struct { + addr_t virtAddr; /**< The virtual address of the Bulk buffer*/ + uint32_t len; /**< Length of the Bulk buffer*/ + uint32_t handle; + addr_t physAddrWsmL2; /**< The physical address of the L2 table of the Bulk buffer*/ + struct list_head list; /**< The list param for using the kernel lists*/ +} bulkBufferDescriptor_t; + +bulkBufferDescriptor_t* bulkBufferDescriptor_create( + addr_t virtAddr, + uint32_t len, + uint32_t handle, + addr_t physAddrWsmL2 +); + +typedef struct list_head bulkBufferDescrVector_t; + +/** Session states. + * At the moment not used !!. + */ +typedef enum +{ + SESSION_STATE_INITIAL, + SESSION_STATE_OPEN, + SESSION_STATE_TRUSTLET_DEAD +} sessionState_t; + +#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. + */ +typedef struct { + sessionState_t state; /**< Session state */ + int32_t lastErr; /**< Last error of session */ +} sessionInformation_t; + + +typedef struct { + struct mcInstance *pInstance; + bulkBufferDescrVector_t bulkBufferDescriptors; /**< Descriptors of additional bulk buffer of a session */ + sessionInformation_t sessionInfo; /**< Informations about session */ + + uint32_t sessionId; + connection_t *notificationConnection; + + struct list_head list; /**< The list param for using the kernel lists*/ +} session_t; + +session_t* session_create( + uint32_t sessionId, + void *pInstance, + connection_t *connection +); + +void session_cleanup( + session_t *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. + */ +bulkBufferDescriptor_t * session_addBulkBuf( + session_t *session, + addr_t 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_removeBulkBuf( + session_t *session, + addr_t buf +); + +/** + * Set additional error information of the last error that occured. + * + * @param errorCode The actual error. + */ +void session_setErrorInfo( + session_t *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_getLastErr( + session_t *session +); + + +typedef struct list_head sessionVector_t; + +#endif /* SESSION_H_ */ + +/** @} */ diff --git a/drivers/gud/MobiCoreKernelApi/wsm.h b/drivers/gud/MobiCoreKernelApi/wsm.h new file mode 100644 index 0000000..e30e45c --- /dev/null +++ b/drivers/gud/MobiCoreKernelApi/wsm.h @@ -0,0 +1,34 @@ +/** @addtogroup MCD_MCDIMPL_DAEMON_SRV + * @{ + * @file + * + * World shared memory definitions. + * + * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2011 --> + */ +#ifndef WSM_H_ +#define WSM_H_ + +#include "common.h" +#include <linux/list.h> + +typedef struct { + addr_t virtAddr; + uint32_t len; + uint32_t handle; + addr_t physAddr; + struct list_head list; +} wsm_t; + +typedef wsm_t *wsm_ptr; +typedef struct list_head wsmVector_t; + +wsm_ptr wsm_create( + addr_t virtAddr, + uint32_t len, + uint32_t handle, + addr_t physAddr //= NULL this may be unknown, so is can be omitted. +); +#endif /* WSM_H_ */ + +/** @} */ diff --git a/drivers/gud/include/Mci/mci.h b/drivers/gud/include/Mci/mci.h new file mode 100644 index 0000000..4758474 --- /dev/null +++ b/drivers/gud/include/Mci/mci.h @@ -0,0 +1,72 @@ +/** @mainpage MobiCore Control Interface - MCI + * + * <h2>Introduction</h2> + * The MobiCore Control Interface (MCI) is the interface for integrating G&D MobiCore technology into the + * rich operating system running in the non-secure part of an ARM TrustZone enabled platform. + * + * <h2>Interface overview</h2> + * The Structure of the MobiCore control interface is depicted in the figure below: + * @image html DoxyOverviewMci500x.png "MobiCore control interface" + * @image latex DoxyOverviewMci500x.png "MobiCore control interface" width=12cm + * + * The MCI is composed of the following interfaces: + * <ul> + * + * <li><b>MobiCore control protocol (MCP) interface.</b></li><br> + * The MCP interface is responsible for the main communicating with the MobiCore. This involves sending commands for starting + * and stopping of Trustlets as well as checking their corresponding answers. MCP information is exchanged in a + * world shared memory buffer which needs to be initially established between NWd and SWd using the FastCall interface.<br> + * + * <li><b>Notification queue interface.</b></li><br> + * 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). Therefore the TLC writes + * a notification including the session ID to the buffer. The driver informs the MobiCore + * about the availability of a notification with the use of a SIQ. On the secure side the Runtime Management + * notifies the Trustlet, according to the given session ID, about the availability of new data. + * The same mechanism is used vice versa for writing data back to the None-secure world. + * + * <li><b>FastCall interface.</b></li><br> + * The FastCall interface of the MobiCore system is used to transfer control from the Non-secure World (NWd) to the + * Secure World (SWd) and back. There are three mechanisms the NWd shall use to interact with the MobiCore Monitor: + * FastCall, N-SIQ and NQ-IRQ (Notification IRQ). FastCall and N-SIQ operations are used to hand over control + * to the MobiCore. Both functions make use of the SMC [ARM11] operation. + * + * </ul> + * + * You can find more information about the interfaces in the respective modules description. + * + * <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>2009-06-25</td><td>0.1</td><td>Initial Release</td></tr> + * <tr><td>2009-07-01</td><td>0.2</td><td>Major rewrite</td></tr> + * <tr><td>2009-08-06</td><td>0.3</td><td>Added documentation for FastCall helper functions</td></tr> + * <tr><td>2009-09-10</td><td>0.4</td><td>Update of constant naming. Modification of doxygen config.</td></tr> + * <tr><td>2010-03-09</td><td>0.5</td><td>Added fastCallPower() helper function for MC_FC_POWER.</td></tr> + * <tr><td>2010-05-10</td><td>0.6</td><td>Restructuring of load format header.</td></tr> + * <tr><td>2011-07-19</td><td>0.7</td><td>update to reflect current code changes.</td></tr> + * </table> + * + * + * @file + * @defgroup FCI FastCall Interface + * + * @defgroup NQ Notification Queue + * + * @defgroup MCP MobiCore Control Protocol + * + * + * <!-- Copyright Giesecke & Devrient GmbH 2009-2012 --> + */ +#ifndef MCI_H_ +#define MCI_H_ + +#include "version.h" +#include "mcifc.h" +#include "mcinq.h" +#include "mcimcp.h" + +#endif /** MCI_H_ */ + +/** @} */ diff --git a/drivers/gud/include/Mci/mcifc.h b/drivers/gud/include/Mci/mcifc.h new file mode 100644 index 0000000..4a2e26b --- /dev/null +++ b/drivers/gud/include/Mci/mcifc.h @@ -0,0 +1,103 @@ +/** @addtogroup FCI + * @{ + * @file + * FastCall declarations. + * + * Holds the functions for SIQ, YIELD and FastCall for switching to the secure world. + * <!-- Copyright Giesecke & Devrient GmbH 2009-2012 --> + */ +#ifndef MCIFC_H_ +#define MCIFC_H_ + +/** @name MobiCore FastCall Defines + * Defines for the two different FastCall's. + */ +/** @{ */ + +// --- global ---- +#define MC_FC_INVALID ((uint32_t) 0 ) /**< Invalid FastCall ID */ +#define MC_FC_INIT ((uint32_t)(-1)) /**< Initializing FastCall. */ +#define MC_FC_INFO ((uint32_t)(-2)) /**< Info FastCall. */ + +// following defines are currently frozen, so they will candidate for later big-change +// --- sleep modes --- +#define MC_FC_SLEEP ((uint32_t)(-3)) /**< enter power-sleep */ +#define MC_FC_AFTR ((uint32_t)(-5)) /**< enter AFTR-sleep (called from core-0) */ +// --- wake-up access --- +#define MC_FC_CORE_X_WAKEUP ((uint32_t)(-4)) /**< wakeup/boot core-x (optional core-number in r1, not "0" ) */ +#define MC_FC_C15_RESUME ((uint32_t)(-11)) /**< Write power control & diag registers */ +// --- L2 cache access --- +#define MC_FC_L2X0_CTRL ((uint32_t)(-21)) /**< Write to L2X0 control register */ +#define MC_FC_L2X0_SETUP1 ((uint32_t)(-22)) /**< Setup L2X0 register - part 1 */ +#define MC_FC_L2X0_SETUP2 ((uint32_t)(-23)) /**< Setup L2X0 register - part 2 */ +#define MC_FC_L2X0_INVALL ((uint32_t)(-24)) /**< Invalidate all L2 cache */ +#define MC_FC_L2X0_DEBUG ((uint32_t)(-25)) /**< Write L2X0 debug register */ +// --- MEM traces --- +#define MC_FC_MEM_TRACE ((uint32_t)(-31)) /**< Enable SWd tracing via memory */ +// --- write access to CP15 regs --- +#define MC_FC_CP15_REG ((uint32_t)(-101)) /**< general CP15/cache register update */ +// --- store value in sDDRRAM --- +#define MC_FC_STORE_BINFO ((uint32_t)(-201)) /**< write a 32bit value in secure DDRRAM in incremented art (max 2kB) */ + +#define MC_FC_MAX_ID ((uint32_t)(0xFFFF0000)) /**< Maximum allowed FastCall ID */ + +// r1 is requested status (0,1,2), on return r2 holds this status value + +/** @} */ + +/** @name MobiCore SMC Defines + * Defines the different secure monitor calls (SMC) for world switching. + * @{ */ +#define MC_SMC_N_YIELD 0x3 /**< Yield to switch from NWd to SWd. */ +#define MC_SMC_N_SIQ 0x4 /**< SIQ to switch from NWd to SWd. */ +/** @} */ + +/** @name MobiCore status + * MobiCore status information. + * @{ */ +#define MC_STATUS_NOT_INITIALIZED 0 /**< MobiCore is not yet initialized. FastCall FcInit() has to be used function to set up MobiCore.*/ +#define MC_STATUS_BAD_INIT 1 /**< Bad parameters have been passed in FcInit(). */ +#define MC_STATUS_INITIALIZED 2 /**< MobiCore did initialize properly. */ +#define MC_STATUS_HALT 3 /**< MobiCore kernel halted due to an unrecoverable exception. Further information is available extended info */ +/** @} */ + +/** @name Extended Info Identifiers + * Extended info parameters for MC_FC_INFO to obtain further information depending on MobiCore state. + * @{ */ +#define MC_EXT_INFO_ID_MCI_VERSION 0 /**< Version of the MobiCore Control Interface (MCI) */ +#define MC_EXT_INFO_ID_FLAGS 1 /**< MobiCore control flags */ +#define MC_EXT_INFO_ID_HALT_CODE 2 /**< MobiCore halt condition code */ +#define MC_EXT_INFO_ID_HALT_IP 3 /**< MobiCore halt condition instruction pointer */ +#define MC_EXT_INFO_ID_FAULT_CNT 4 /**< MobiCore fault counter */ +#define MC_EXT_INFO_ID_FAULT_CAUSE 5 /**< MobiCore last fault cause */ +#define MC_EXT_INFO_ID_FAULT_META 6 /**< MobiCore last fault meta */ +#define MC_EXT_INFO_ID_FAULT_THREAD 7 /**< MobiCore last fault threadid */ +#define MC_EXT_INFO_ID_FAULT_IP 8 /**< MobiCore last fault instruction pointer */ +#define MC_EXT_INFO_ID_FAULT_SP 9 /**< MobiCore last fault stack pointer */ +#define MC_EXT_INFO_ID_FAULT_ARCH_DFSR 10 /**< MobiCore last fault ARM arch information */ +#define MC_EXT_INFO_ID_FAULT_ARCH_ADFSR 11 /**< MobiCore last fault ARM arch information */ +#define MC_EXT_INFO_ID_FAULT_ARCH_DFAR 12 /**< MobiCore last fault ARM arch information */ +#define MC_EXT_INFO_ID_FAULT_ARCH_IFSR 13 /**< MobiCore last fault ARM arch information */ +#define MC_EXT_INFO_ID_FAULT_ARCH_AIFSR 14 /**< MobiCore last fault ARM arch information */ +#define MC_EXT_INFO_ID_FAULT_ARCH_IFAR 15 /**< MobiCore last fault ARM arch information */ +#define MC_EXT_INFO_ID_MC_CONFIGURED 16 /**< MobiCore configured by Daemon via fc_init flag */ +#define MC_EXT_INFO_ID_MC_SCHED_STATUS 17 /**< MobiCore scheduling status: idle/non-idle */ +#define MC_EXT_INFO_ID_MC_STATUS 18 /**< MobiCore runtime status: initialized, halted */ +#define MC_EXT_INFO_ID_MC_EXC_PARTNER 19 /**< MobiCore exception handler last partner */ +#define MC_EXT_INFO_ID_MC_EXC_IPCPEER 20 /**< MobiCore exception handler last peer */ +#define MC_EXT_INFO_ID_MC_EXC_IPCMSG 21 /**< MobiCore exception handler last IPC message */ +#define MC_EXT_INFO_ID_MC_EXC_IPCDATA 22 /**< MobiCore exception handler last IPC data */ + +/** @} */ + +/** @name FastCall return values + * Return values of the MobiCore FastCalls. + * @{ */ +#define MC_FC_RET_OK 0 /**< No error. Everything worked fine. */ +#define MC_FC_RET_ERR_INVALID 1 /**< FastCall was not successful. */ +#define MC_FC_RET_ERR_ALREADY_INITIALIZED 5 /**< MobiCore has already been initialized. */ +/** @} */ + +#endif /** MCIFC_H_ */ + +/** @} */ diff --git a/drivers/gud/include/Mci/mcifcfunc.h b/drivers/gud/include/Mci/mcifcfunc.h new file mode 100644 index 0000000..f095303 --- /dev/null +++ b/drivers/gud/include/Mci/mcifcfunc.h @@ -0,0 +1,163 @@ +/** @addtogroup FCI + * @{ + * @file + * Declaration of FastCall helper functions. + * + * @attention Helper functions are mostly RealView (ARM CC) specific. + * + * Holds the functions for SIQ, YIELD and FastCall for switching to the secure world. + * <!-- Copyright Giesecke & Devrient GmbH 2009-2012 --> + */ + +#ifndef MCIFCFUNC_H_ +#define MCIFCFUNC_H_ + +#include "mcifc.h" +/** + * Execute a secure monitor call (SMC). + * + * @param mode SMC mode affects the way SMC is handled + * + * @attention This function shall not be used directly. Use N_Siq() or Yield() instead. + */ +__smc(0) void smc(int32_t mode); + +/** + * N-SIQ switch from NWd to SWd. + * Execution will continue in the SWd. The notification queue will be drained by the MC4 and MC4 system schedules its services. + */ +inline void N_Siq(void) { smc(MC_SMC_N_SIQ); } + +/** + * Yield switch from NWd to SWd. + * Execution will continue in the SWd without scheduling MC4 services. + */ +inline void Yield(void) { smc(MC_SMC_N_YIELD); } + +/** Wrapper structure for parameter passing in registers. + * This structure is used as a "wrapper" return value for functions that + * return data in the registers r0 to r3. With the RealView compiler such + * function are declare as: _value_in_regs reg_r0_r1_r2_r3_t foo() + + */ +typedef struct { + uint32_t r0; + uint32_t r1; + uint32_t r2; + uint32_t r3; +} reg_r0_r1_r2_r3_t; + +/** Parameterized SMC for FastCalls. + * @attention This function shall not be used directly. + */ +__smc(0) __value_in_regs reg_r0_r1_r2_r3_t smcFc( + uint32_t r0, + uint32_t r1, + uint32_t r2, + uint32_t r3 +); + +/** FastCall helper function. + * @attention This function shall not be used directly. + */ +inline static __value_in_regs reg_r0_r1_r2_r3_t fastCall( + uint32_t r0, + uint32_t r1, + uint32_t r2, + uint32_t r3 +) { + return smcFc(r0,r1,r2,r3); +} + +/** + * Initialize the MobiCore. + * The FcMc4init FastCall shall be used to set up the MCI. The function passes the message buffers used in the MCI to the MC4 system. + * As long as the buffers are not set up the MC4 message passing mechanisms (notifications, MCP commands) are not available. + * NQ buffer and MCP buffer as well as length calculations are described in the "MobiCore4 Driver Interface Specification". + * <br> The fastCallInit() will not check the parameters for validity. Instead the MC4 will perform a check on first usage of the parameters. + * + * @image html DoxyMciBuffer.png "MCI buffer" + * @image latex DoxyMciBuffer.png "MCI buffer" width=12cm + * + * @param base Physical start address of the MCI buffer. Must be 4kB aligned. + * @param nqOffset Offset in bytes to the beginning of the NQ buffer. + * @param nqLength Length of the NQ buffer in bytes. + * @param mcpOffset Offset in bytes to the beginning of the MCP buffer. + * @param mcpLength Length of the MCP buffer in bytes + * + */ +inline static uint32_t fastCallInit( + uint8_t *base, + uint32_t nqOffset, + uint32_t nqLength, + uint32_t mcpOffset, + uint32_t mcpLength +) { + + reg_r0_r1_r2_r3_t ret; + + ret = fastCall( + MC_FC_INIT, + (uint32_t)base, + ((nqOffset << 16) | (nqLength & 0xFFFF)), + ((mcpOffset << 16) | (mcpLength & 0xFFFF)) ); + + + return ret.r1; +} + + +/** Get status information about MobiCore. + * The FcMcGetInfo FastCall provides information about the current state of the MobiCore. + * In certain states extended information is provided. + * + * @param extInfoId Extended info word to be obtained. + * @param mc4state Current state of the MobiCore. + * @param extInfo Extended information depending on state. + */ +inline static uint32_t fastCallGetInfo( + uint32_t extInfoId, + uint32_t *mc4state, + uint32_t *extInfo +) { + reg_r0_r1_r2_r3_t ret; + + ret = fastCall(MC_FC_INFO,extInfoId,0,0); + + if (MC_FC_RET_OK == ret.r1) + { + *mc4state = ret.r2; + *extInfo = ret.r3; + } + + return ret.r1; +} + +/** + * Power management. + * The power management FastCall is platform specific. + * + * @param param0 platform specific parameter. + * @param param1 platform specific parameter. + * @param param2 platform specific parameter. + */ +inline static uint32_t fastCallPower( + uint32_t param0, + uint32_t param1, + uint32_t param2 +) { + + reg_r0_r1_r2_r3_t ret; + + ret = fastCall( + MC_FC_POWER, + param0, + param1, + param2 ); + + return ret.r1; +} + +#endif /* MCIFCFUNC_H_ */ +/** + * @}*/ diff --git a/drivers/gud/include/Mci/mcimcp.h b/drivers/gud/include/Mci/mcimcp.h new file mode 100644 index 0000000..da947bc --- /dev/null +++ b/drivers/gud/include/Mci/mcimcp.h @@ -0,0 +1,377 @@ +/** @addtogroup MCP + * @{ + * The MCP defines commands and responses which are used to control the MobiCore system. + * MCP information is exchanged in a world share memory buffer which has been established prior between NWd + * and SWd using the FastCall interface. The buffer needs to be provided by the MobiCore driver and is utilized + * to send MCP commands to the MobiCore as well as receiving responses from the MobiCore. + * The command of the normal world will be overwritten with the response from the secure side. + * + * @file + * MCP command interface definitions. + * + * <!-- Copyright Giesecke & Devrient GmbH 2009-2012 --> + */ +#ifndef MCP_H_ +#define MCP_H_ + +#include "mcUuid.h" +#include "mcLoadFormat.h" +#include "mcVersionInfo.h" + +/** MobiCore Return Code Defines. + * List of the possible MobiCore return codes. + */ +typedef enum { + MC_MCP_RET_OK = 0, /**< Memory has successfully been mapped. */ + MC_MCP_RET_ERR_INVALID_SESSION = 1, /**< The session ID is invalid. */ + MC_MCP_RET_ERR_UNKNOWN_UUID = 2, /**< The UUID of the Trustlet is unknown. */ + MC_MCP_RET_ERR_UNKNOWN_DRIVER_ID = 3, /**< The ID of the driver is unknown. */ + MC_MCP_RET_ERR_NO_MORE_SESSIONS = 4, /**< No more session are allowed. */ + MC_MCP_RET_ERR_CONTAINER_INVALID = 5, /**< The container is invalid. */ + MC_MCP_RET_ERR_TRUSTLET_INVALID = 6, /**< The Trustlet is invalid. */ + MC_MCP_RET_ERR_ALREADY_MAPPED = 7, /**< The memory block has already been mapped before. */ + MC_MCP_RET_ERR_INVALID_PARAM = 8, /**< Alignment or length error in the command parameters. */ + MC_MCP_RET_ERR_OUT_OF_RESOURCES = 9, /**< No space left in the virtual address space of the session. */ + MC_MCP_RET_ERR_INVALID_WSM = 10, /**< WSM type unknown or broken WSM */ + MC_MCP_RET_ERR_UNKNOWN = 11, /**< unknown error. */ + MC_MCP_RET_ERR_INVALID_MAPPING_LENGTH = 12, /**< Lenght of map invalid */ + MC_MCP_RET_ERR_MAPPING_TARGET = 13, /**< Map can only be applied to Trustlet session */ + MC_MCP_RET_ERR_OUT_OF_CRYPTO_RESSOURCES = 14, /**< Couldn't open crypto session. */ + MC_MCP_RET_ERR_SIGNATURE_VERIFICATION_FAILED = 15, /**< System Trustlet signature verification failed. */ + MC_MCP_RET_ERR_WRONG_PUBLIC_KEY = 16, /**< System Trustlet public key is wrong. */ + MC_MCP_RET_ERR_CONTAINER_TYPE_MISMATCH = 17, /**< Wrong containter type(s). */ + MC_MCP_RET_ERR_CONTAINER_LOCKED = 18, /**< Container is locked (or not activated). */ + MC_MCP_RET_ERR_SP_NO_CHILD = 19, /**< SPID is not registered with root container. */ + MC_MCP_RET_ERR_TL_NO_CHILD = 20, /**< UUID is not registered with sp container. */ + MC_MCP_RET_ERR_UNWRAP_ROOT_FAILED = 21, /**< Unwrapping of root container failed. */ + MC_MCP_RET_ERR_UNWRAP_SP_FAILED = 22, /**< Unwrapping of service provider container failed. */ + MC_MCP_RET_ERR_UNWRAP_TRUSTLET_FAILED = 23, /**< Unwrapping of Trustlet container failed. */ + MC_MCP_RET_ERR_CONTAINER_VERSION_MISMATCH = 24, /**< Container version mismatch. */ + + /* used for command verification */ + MC_MCP_RET_ERR_UNKNOWN_COMMAND = 50, /**< The command is unknown. */ + MC_MCP_RET_ERR_INVALID_DATA = 51 /**< The command data is invalid. */ +} mcpResult_t; + +/** Possible MCP Command IDs + * Command ID must be between 0 and 0x7FFFFFFF. + */ +typedef enum { + MC_MCP_CMD_ID_INVALID = 0x00000000, /**< Invalid command ID. */ + // Session commands + MC_MCP_CMD_OPEN_SESSION = 0x00000001, /**< Open a session to a service. */ + MC_MCP_CMD_CLOSE_SESSION = 0x00000003, /**< Close an existing service session. */ + MC_MCP_CMD_MAP = 0x00000004, /**< Map a block of WSM to a session. */ + MC_MCP_CMD_UNMAP = 0x00000005, /**< Unmap a block of WSM from a session. */ + MC_MCP_CMD_SUSPEND = 0x00000006, /**< Prepare MobiCore for suspend. */ + MC_MCP_CMD_RESUME = 0x00000007, /**< Resume MobiCore from suspension. */ + MC_MCP_CMD_DONATE_RAM = 0x00000008, /**< Donate RAM to MobiCore. */ + MC_MCP_CMD_GET_MOBICORE_VERSION = 0x00000009, /**< Get MobiCore version information. */ +} mcpCmdId_t; + + +#define FLAG_RESPONSE (1U << 31) /**< Flag to indicate that this is the response to a MCP command. */ + + +/** Types of WSM known to the MobiCore. + */ +typedef enum { + WSM_INVALID = 0, /**< Invalid memory type */ + WSM_CONTIGUOUS = 1, /**< Reference to WSM points to a contiguous region of pages. */ + WSM_L2 = 2, /**< Reference to WSM points to an L2 table describing the memory region to share */ +}wsmType_t; + +/** Types of RAM known to the MobiCore. + */ +typedef enum { + RAM_INVALID = 0, /**< Invalid memory type */ + RAM_GENERIC = 1, /**< Generic RAM of no special type. */ +}ramType_t; + +/** Command header. + * It just contains the command ID. Only values specified in mcpCmdId_t are allowed as command IDs. + * If the command ID is unspecified the MobiCore returns an empty response with the result set to MC_MCP_RET_ERR_UNKNOWN_COMMAND . + */ +typedef struct { + mcpCmdId_t cmdId; /**< Command ID of the command */ +} commandHeader_t, *commandHeader_ptr; + +/** Response header. + * MobiCore will reply to every MCP command with an MCP response. Like the MCP command the response consists of a + * header followed by response data. The response is written to the same memory location as the MCP command. + */ +typedef struct { + uint32_t rspId; /**< Command ID | FLAG_RESPONSE. */ + mcpResult_t result; /**< Result informs about the execution result of the command associated with the response. */ +} responseHeader_t, *responseHeader_ptr; + + + +/** @defgroup CMD MCP Commands + * @{ */ + +/** @defgroup ASMCMD Administrative Commands + * @{ */ + +/** @defgroup MCPDONATERAM DONATE_RAM + * Donate NWd RAM to MobiCore. + * This is a debug feature that is not available in release version. + * + * @{ */ + +/** Donate RAM Command */ +typedef struct { + commandHeader_t cmdHeader; /**< Command header. */ + ramType_t ramType; /**< Type of RAM used for memory pool */ + uint32_t adrBuffer; /**< Physical address of the page range*/ + uint32_t numPages; /**< Number of pages contained in the donation. */ +} mcpCmdDonateRam_t, *mcpCmdDonateRam_ptr; + +/** Donate RAM Command Response */ +typedef struct { + responseHeader_t rspHeader; /**< Response header. */ +} mcpRspDonateRam_t, *mcpRspDonateRam_ptr; +/** @} */ /* End MCPDONATERAM */ + + +/** @defgroup MCPGETMOBICOREVERSION GET_MOBICORE_VERSION + * Get MobiCore version info. + * + * @{ */ + +/** Get MobiCore Version Command. */ +typedef struct { + commandHeader_t cmdHeader; /** Command header. */ +} mcpCmdGetMobiCoreVersion_t, *mcpCmdGetMobiCoreVersion_ptr; + +/** Get MobiCore Version Command Response. */ +typedef struct { + responseHeader_t rspHeader; /** Response header. */ + mcVersionInfo_t versionInfo; /** MobiCore version info. */ +} mcpRspGetMobiCoreVersion_t, *mcpRspGetMobiCoreVersion_ptr; + +/** @} *//* End MCPGETMOBICOREVERSION */ + +/** @} *//* End ASMCMD */ + + +/** @defgroup POWERCMD Power Management Commands + * @{ */ + +/** @defgroup MCPSUSPEND SUSPEND + * Prepare MobiCore suspension. + * This command allows MobiCore and MobiCore drivers to release or clean resources and save device state. + * + * @{ */ + +/** Suspend Command */ +typedef struct { + commandHeader_t cmdHeader; /**< Command header. */ +} mcpCmdSuspend_t, *mcpCmdSuspend_ptr; + +/** Suspend Command Response */ +typedef struct { + responseHeader_t rspHeader; /**< Response header. */ +} mcpRspSuspend_t, *mcpRspSuspend_ptr; +/** @} *//* End MCPSUSPEND */ + + +/** @defgroup MCPRESUME RESUME + * Resume MobiCore from suspension. + * This command allows MobiCore and MobiCore drivers to reinitialize hardware affected by suspension. + * + * @{ */ + +/** Resume Command */ +typedef struct { + commandHeader_t cmdHeader; /**< Command header. */ +} mcpCmdResume_t, *mcpCmdResume_ptr; + +/** Resume Command Response */ +typedef struct { + responseHeader_t rspHeader; /**< Response header. */ +} mcpRspResume_t, *mcpRspResume_ptr; + +/** @} *//* End MCPRESUME */ + +/** @} *//* End POWERCMD */ + + + +/** @defgroup SESSCMD Session Management Commands + * @{ */ + +/** @defgroup MCPOPEN OPEN + * Load and open a session to a Trustlet. + * The OPEN command loads Trustlet data to the MobiCore context and opens a session to the Trustlet. + * If wsmTypeLoadData is WSM_INVALID MobiCore tries to start a pre-installed Trustlet + * associated with the uuid passed. + * The uuid passed must match the uuid contained in the load data (if available). + * On success, MobiCore returns the session ID which can be used for further communication. + * @{ */ + +/** Open Command */ +typedef struct { + commandHeader_t cmdHeader; /**< Command header. */ + mcUuid_t uuid; /**< Byte array containing the service UUID. */ + wsmType_t wsmTypeTci; /**< Type of WSM used for the TCI */ + uint32_t adrTciBuffer; /**< Physical address of the TCI */ + uint32_t ofsTciBuffer; /**< Offset to the data. */ + uint32_t lenTciBuffer; /**< Length of the TCI. */ + wsmType_t wsmTypeLoadData; /**< Type of the memory containing the data to load. */ + uint32_t adrLoadData; /**< Physical address of the data to load. */ + uint32_t ofsLoadData; /**< Offset to the data. */ + uint32_t lenLoadData; /**< Length of the data to load. */ + mclfHeader_t tlHeader; /**< Service header. */ +} mcpCmdOpen_t, *mcpCmdOpen_ptr; + +/** Open Command Response */ +typedef struct { + responseHeader_t rspHeader; /**< Response header. */ + uint32_t sessionId; /**< Session ID used for further communication. */ +} mcpRspOpen_t, *mcpRspOpen_ptr; + +/** @} *//* End MCPOPEN */ + + +/** @defgroup MCPCLOSE CLOSE + * Close an existing session to a Trustlet. + * The CLOSE command terminates a session and frees all resources in the MobiCore system which + * are currently occupied by the session. Before closing the session, the MobiCore runtime + * management waits until all pending operations, like calls to drivers, invoked by the Trustlet + * have been terminated. + * Mapped memory will automatically be unmapped from the MobiCore context. The NWd is responsible for + * processing the freed memory according to the Rich-OS needs. + * + * @{ */ + +/** Close Command */ +typedef struct { + commandHeader_t cmdHeader; /**< Command header. */ + uint32_t sessionId; /**< Session ID. */ +} mcpCmdClose_t, *mcpCmdClose_ptr; + +/** Close Command Response */ +typedef struct { + responseHeader_t rspHeader; /**< Response header. */ +} mcpRspClose_t, *mcpRspClose_ptr; + +/** @} *//* End MCPCLOSE */ + + +/** @defgroup MCPMAP MAP + * Map a portion of memory to a session. + * The MAP command provides a block of memory to the context of a service. + * The memory then becomes world-shared memory (WSM). + * The WSM can either be normal anonymous memory from malloc() or be a + * block of page aligned, contiguous memory. + * The only allowed memory type here is WSM_L2. + * @{ */ + +/** Map Command */ +typedef struct { + commandHeader_t cmdHeader; /**< Command header. */ + uint32_t sessionId; /**< Session ID of a valid session */ + wsmType_t wsmType; /**< Type of WSM used of the memory*/ + uint32_t adrBuffer; /**< Physical address of the memory */ + uint32_t ofsBuffer; /**< Offset to the payload. */ + uint32_t lenBuffer; /**< Length of the buffer. */ +} mcpCmdMap_t, *mcpCmdMap_ptr; + +#define MCP_MAP_MAX 0x100000 /**< Maximum allowed length for MCP map. */ + +/** Map Command Response */ +typedef struct { + responseHeader_t rspHeader; /**< Response header. */ + uint32_t secureVirtualAdr; /**< Virtual address in the context of the service the WSM is mapped to, already includes a possible offset! */ +} mcpRspMap_t, *mcpRspMap_ptr; + +/** @} *//*End MCPMAP */ + + +/** @defgroup MCPUNMAP UNMAP + * Unmap a portion of world-shared memory from a session. + * The UNMAP command is used to unmap a previously mapped block of + * world shared memory from the context of a session. + * + * Attention: The memory block will be immediately unmapped from the specified session. + * If the service is still accessing the memory, the service will trigger a segmentation fault. + * @{ */ + +/** Unmap Command */ +typedef struct { + commandHeader_t cmdHeader; /**< Command header. */ + uint32_t sessionId; /**< Session ID of a valid session */ + wsmType_t wsmType; /**< Type of WSM used of the memory*/ + uint32_t secureVirtualAdr; /**< Virtual address in the context of the service the WSM has been mapped to, already includes a possible offset! */ + uint32_t lenVirtualBuffer; /**< Length of the virtual buffer. */ +} mcpCmdUnmap_t, *mcpCmdUnmap_ptr; + +/** Unmap Command Response */ +typedef struct { + responseHeader_t rspHeader; /**< Response header. */ +} mcpRspUnmap_t, *mcpRspUnmap_ptr; + +/** @} *//* End MCPUNMAP */ + +/** @} *//* End SESSCMD */ + +/** @} *//* End CMD */ + +/** Structure of the MCP buffer. */ +typedef union { + commandHeader_t cmdHeader; /**< Command header. */ + responseHeader_t rspHeader; /**< Response header. */ + mcpCmdOpen_t cmdOpen; /**< Load and open service. */ + mcpRspOpen_t rspOpen; /**< Response to load and open service. */ + mcpCmdClose_t cmdClose; /**< Close command. */ + mcpRspClose_t rspClose; /**< Response to close command. */ + mcpCmdMap_t cmdMap; /**< Map WSM to service context. */ + mcpRspMap_t rspMap; /**< Response to MAP command. */ + mcpCmdUnmap_t cmdUnmap; /**< Unmap WSM from service context. */ + mcpRspUnmap_t rspUnmap; /**< Response to UNMAP command. */ + mcpCmdSuspend_t cmdSuspend; /**< Suspend MobiCore. */ + mcpRspSuspend_t rspSuspend; /**< Response to SUSPEND command. */ + mcpCmdResume_t cmdResume; /**< Resume MobiCore. */ + mcpRspResume_t rspResume; /**< Response to RESUME command. */ + mcpCmdDonateRam_t cmdDonateRam; /**< Donate RAM to MobiCore. */ + mcpRspDonateRam_t rspDonateRam; /**< Response to DONATE_RAM command. */ + mcpCmdGetMobiCoreVersion_t cmdGetMobiCoreVersion; /**< Get MobiCore Version command. */ + mcpRspGetMobiCoreVersion_t rspGetMobiCoreVersion; /**< Response to GET_MOBICORE_VERSION command. */ +} mcpMessage_t, *mcpMessage_ptr; + + +#define MIN_MCP_LEN sizeof(mcpMessage_t) /**< Minimum MCP buffer length (in bytes). */ + +#define MC_FLAG_NO_SLEEP_REQ 0 +#define MC_FLAG_REQ_TO_SLEEP 1 + +#define MC_STATE_NORMAL_EXECUTION 0 +#define MC_STATE_READY_TO_SLEEP 1 + +typedef struct { + uint16_t SleepReq; + uint16_t ReadyToSleep; +} mcSleepMod_t, *mcSleepMod_ptr; + +/** MobiCore status flags */ +typedef struct { + uint32_t schedule; /**< Scheduling hint: if <> MC_FLAG_SCHEDULE_IDLE, MobiCore should be scheduled by the NWd */ + mcSleepMod_t sleepMode; /**< */ + uint32_t RFU2; /**< Reserved for future use: Must not be interpreted */ + uint32_t RFU3; /**< Reserved for future use: Must not be interpreted */ +} mcFlags_t, *mcFlags_ptr; + +#define MC_FLAG_SCHEDULE_IDLE 0 /**< MobiCore is idle. No scheduling required. */ +#define MC_FLAG_SCHEDULE_NON_IDLE 1 /**< MobiCore is non idle, scheduling is required. */ + + + +/** MCP buffer structure */ +typedef struct { + mcFlags_t mcFlags; /**< MobiCore Flags */ + mcpMessage_t mcpMessage; /**< MCP message buffer */ +} mcpBuffer_t, *mcpBuffer_ptr; + +/** @} */ +#endif /* MCP_H_ */ diff --git a/drivers/gud/include/Mci/mcinq.h b/drivers/gud/include/Mci/mcinq.h new file mode 100644 index 0000000..51a0df4 --- /dev/null +++ b/drivers/gud/include/Mci/mcinq.h @@ -0,0 +1,84 @@ +/** @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 --> + */ +#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. + * @{ */ +#define MIN_NQ_LEN (MIN_NQ_ELEM * sizeof(notification_t)) /**< Minimum notification length (in bytes). */ +#define MAX_NQ_LEN (MAX_NQ_ELEM * sizeof(notification_t)) /**< Maximum notification length (in bytes). */ +/** @} */ + +/** \name Session ID Defines + * Standard Session IDs. + * @{ */ +#define SID_MCP 0 /**< MCP session ID is used when directly communicating with the MobiCore (e.g. for starting and stopping of trustlets). */ +#define SID_INVALID 0xffffffff /**< Invalid session id is returned in case of an error. */ +/** @} */ + +/** Notification data structure. */ +typedef struct{ + uint32_t sessionId; /**< Session ID. */ + int32_t payload; /**< Additional notification information. */ +} notification_t; + +/** 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. + */ +typedef enum { + ERR_INVALID_EXIT_CODE = -1, /**< task terminated, but exit code is invalid */ + ERR_SESSION_CLOSE = -2, /**< task terminated due to session end, no exit code available */ + ERR_INVALID_OPERATION = -3, /**< task terminated due to invalid operation */ + ERR_INVALID_SID = -4, /**< session ID is unknown */ + ERR_SID_NOT_ACTIVE = -5 /**< session is not active */ +} notificationPayload_t; + +/** Declaration of the notification queue header. + * layout as specified in the data structure specification. + */ +typedef struct { + uint32_t writeCnt; /**< Write counter. */ + uint32_t readCnt; /**< Read counter. */ + uint32_t queueSize; /**< Queue size. */ +} notificationQueueHeader_t; + +/** 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). + */ +typedef struct { + notificationQueueHeader_t hdr; /**< Queue header. */ + notification_t notification[MIN_NQ_ELEM]; /**< Notification elements. */ +} notificationQueue_t; + +#endif /** NQ_H_ */ + +/** @} */ diff --git a/drivers/gud/include/Mci/version.h b/drivers/gud/include/Mci/version.h new file mode 100644 index 0000000..1cbf0ae --- /dev/null +++ b/drivers/gud/include/Mci/version.h @@ -0,0 +1,10 @@ +/** + * <!-- Copyright Giesecke & Devrient GmbH 2009-2012 --> + */ +#ifndef MCI_VERSION_H_ +#define MCI_VERSION_H_ + +#define MCI_VERSION_MAJOR 0 +#define MCI_VERSION_MINOR 3 + +#endif /** MCI_VERSION_H_ */ diff --git a/drivers/gud/include/mcContainer.h b/drivers/gud/include/mcContainer.h new file mode 100644 index 0000000..b794b80 --- /dev/null +++ b/drivers/gud/include/mcContainer.h @@ -0,0 +1,251 @@ +/** @addtogroup MC_CONTAINER mcContainer - Containers for MobiCore Content Management. + * @ingroup MC_DATA_TYPES + * @{ + * + * <!-- Copyright Giesecke & Devrient GmbH 2009-2012 --> + */ +#ifndef MC_CONTAINER_H_ +#define MC_CONTAINER_H_ + +#include <stdint.h> + +#include "mcRootid.h" +#include "mcSpid.h" +#include "mcUuid.h" +#include "mcSo.h" +#include "mcSuid.h" + +#define CONTAINER_VERSION_MAJOR 2 +#define CONTAINER_VERSION_MINOR 0 + +#define MC_CONT_SYMMETRIC_KEY_SIZE 32 +#define MC_CONT_PUBLIC_KEY_SIZE 320 +#define MC_CONT_CHILDREN_COUNT 16 +#define MC_DATA_CONT_MAX_DATA_SIZE 2048 +#define MC_TLT_CODE_HASH_SIZE 32 + +#define MC_BYTES_TO_WORDS(bytes) ( (bytes) / sizeof(uint32_t) ) +#define MC_ENUM_32BIT_SPACER ((int32_t)-1) + +typedef uint32_t mcContVersion_t; + +/** Personalization Data ID. */ +typedef struct { + uint32_t data; +} mcPid_t; + +typedef struct { + uint32_t keydata[MC_BYTES_TO_WORDS(MC_CONT_SYMMETRIC_KEY_SIZE)]; +} mcSymmetricKey_t; + +typedef struct { + uint32_t keydata[MC_BYTES_TO_WORDS(MC_CONT_PUBLIC_KEY_SIZE)]; +} mcPublicKey_t; + +typedef mcSpid_t spChild_t[MC_CONT_CHILDREN_COUNT]; + +typedef mcUuid_t mcUuidChild_t[MC_CONT_CHILDREN_COUNT]; + +/** Content management container states. + */ +typedef enum { + /** Container state unregistered. */ + MC_CONT_STATE_UNREGISTERED = 0, + /** Container is registered. */ + MC_CONT_STATE_REGISTERED = 1, + /** Container is activated. */ + MC_CONT_STATE_ACTIVATED = 2, + /** Container is locked by root. */ + MC_CONT_STATE_ROOT_LOCKED = 3, + /** Container is locked by service provider. */ + MC_CONT_STATE_SP_LOCKED = 4, + /** Container is locked by root and service provider. */ + MC_CONT_STATE_ROOT_SP_LOCKED = 5, + /** Dummy: ensure that enum is 32 bits wide. */ + MC_CONT_ATTRIB_SPACER = MC_ENUM_32BIT_SPACER +} mcContainerState_t; + +/** Content management container attributes. + */ +typedef struct { + mcContainerState_t state; +} mcContainerAttribs_t; + +/** Container types. */ +typedef enum { + /** SOC container. */ + CONT_TYPE_SOC = 0, + /** Root container. */ + CONT_TYPE_ROOT, + /** Service provider container. */ + CONT_TYPE_SP, + /** Trustlet container. */ + CONT_TYPE_TLCON, + /** Service provider data. */ + CONT_TYPE_SPDATA, + /** Trustlet data. */ + CONT_TYPE_TLDATA +} contType_t; + + +/** @defgroup MC_CONTAINER_CRYPTO_OBJECTS Container secrets. + * Data that is stored encrypted within the container. + * @{ */ + +/** SoC secret */ +typedef struct { + mcSymmetricKey_t kSocAuth; +} mcCoSocCont_t; + +/** */ +typedef struct { + mcSymmetricKey_t kRootAuth; +} mcCoRootCont_t; + +/** */ +typedef struct { + mcSymmetricKey_t kSpAuth; +} mcCoSpCont_t; + +/** */ +typedef struct { + mcSymmetricKey_t kTl; +} mcCoTltCont_t; + +/** */ +typedef struct { + uint8_t data[MC_DATA_CONT_MAX_DATA_SIZE]; +} mcCoDataCont_t; + +/** */ +typedef union { + mcSpid_t spid; + mcUuid_t uuid; +} mcCid_t; + +/** @} */ + +/** @defgroup MC_CONTAINER_CONTAINER_OBJECTS Container definitions. + * Container type definitions. + * @{ */ + +/** SoC Container */ +typedef struct { + contType_t type; + uint32_t version; + mcContainerAttribs_t attribs; + mcSuid_t suid; + /* Secrets. */ + mcCoSocCont_t co; +} mcSocCont_t; + +/** */ +typedef struct { + contType_t type; + uint32_t version; + mcContainerAttribs_t attribs; + mcSuid_t suid; + mcRootid_t rootid; + spChild_t children; + /* Secrets. */ + mcCoRootCont_t co; +} mcRootCont_t; + +/** */ +typedef struct { + contType_t type; + uint32_t version; + mcContainerAttribs_t attribs; + mcSpid_t spid; + mcUuidChild_t children; + /* Secrets. */ + mcCoSpCont_t co; +} mcSpCont_t; + +/** */ +typedef struct { + contType_t type; + uint32_t version; + mcContainerAttribs_t attribs; + mcSpid_t parent; + mcUuid_t uuid; + /* Secrets. */ + mcCoTltCont_t co; +} mcTltCont_t; + +/** */ +typedef struct { + contType_t type; + uint32_t version; + mcUuid_t uuid; + mcPid_t pid; + /* Secrets. */ + mcCoDataCont_t co; +} mcDataCont_t; + +/** @} */ + +/** Calculates the total size of the secure object hash and padding for a given + * container. + * @param contTotalSize Total size of the container (sum of plain and encrypted + * parts). + * @param contCoSize Size/length of the encrypted container part ("crypto + * object"). + * @return Total size of hash and padding for given container. + */ +#define SO_CONT_HASH_AND_PAD_SIZE(contTotalSize, contCoSize) ( \ + MC_SO_SIZE((contTotalSize) - (contCoSize), (contCoSize)) \ + - sizeof(mcSoHeader_t) \ + - (contTotalSize) ) + +/** @defgroup MC_CONTAINER_SECURE_OBJECTS Containers in secure objects. + * Secure objects wrapping different containers. + * @{ */ + +/** Authentication token */ +typedef struct { + mcSoHeader_t soHeader; + mcSocCont_t coSoc; + uint8_t hashAndPad[SO_CONT_HASH_AND_PAD_SIZE(sizeof(mcSocCont_t), sizeof(mcCoSocCont_t))]; +} mcSoAuthTokenCont_t; + +/** Root container */ +typedef struct { + mcSoHeader_t soHeader; + mcRootCont_t cont; + uint8_t hashAndPad[SO_CONT_HASH_AND_PAD_SIZE(sizeof(mcRootCont_t), sizeof(mcCoRootCont_t))]; +} mcSoRootCont_t; + +/** */ +typedef struct { + mcSoHeader_t soHeader; + mcSpCont_t cont; + uint8_t hashAndPad[SO_CONT_HASH_AND_PAD_SIZE(sizeof(mcSpCont_t), sizeof(mcCoSpCont_t))]; +} mcSoSpCont_t; + +/** */ +typedef struct { + mcSoHeader_t soHeader; + mcTltCont_t cont; + uint8_t hashAndPad[SO_CONT_HASH_AND_PAD_SIZE(sizeof(mcTltCont_t), sizeof(mcCoTltCont_t))]; +} mcSoTltCont_t; + +/** */ +typedef struct { + mcSoHeader_t soHeader; + mcDataCont_t cont; + uint8_t hashAndPad[SO_CONT_HASH_AND_PAD_SIZE(sizeof(mcDataCont_t), sizeof(mcCoDataCont_t))]; +} mcSoDataCont_t; + +/** */ +typedef struct { + mcSoRootCont_t soRoot; + mcSoSpCont_t soSp; + mcSoTltCont_t soTlt; +} mcSoContainerPath_t; + +/** @} */ + +#endif // MC_CONTAINER_H_ + +/** @} */ diff --git a/drivers/gud/include/mcDriverId.h b/drivers/gud/include/mcDriverId.h new file mode 100644 index 0000000..51c317b --- /dev/null +++ b/drivers/gud/include/mcDriverId.h @@ -0,0 +1,40 @@ +/** + * @file + * Driver ID definition. + * + * <!-- Copyright Giesecke & Devrient GmbH 2011-2012 --> + */ + +#ifndef RTMDRVID_H_ +#define RTMDRVID_H_ + +#define MC_DRV_VENDOR_ID_SHIFT (16) +#define MC_DRV_VENDOR_ID_MASK (0xFFFF << MC_DRV_VENDOR_ID_SHIFT) +#define MC_DRV_NUMBER_MASK (0x0000FFFF) + +/** MobiCore vendor IDs. */ +typedef enum { + MC_DRV_VENDOR_ID_GD = 0 << MC_DRV_VENDOR_ID_SHIFT, +} mcDrvVendorId_t; + +/** MobiCore GD driver numbers. */ +typedef enum { + MC_DRV_NUMBER_INVALID = 0, + MC_DRV_NUMBER_CRYPTO = 1, + MC_DRV_NUMBER_KEYPAD = 2, + /** Last GD driver number reserved for pre-installed drivers. + * GD driver numbers up to this constant may not be used for loadable drivers. */ + MC_DRV_NUMBER_LAST_PRE_INSTALLED = 100, +} mcDrvNumber_t; + +/** MobiCore driver IDs for Trustlets. */ +typedef enum { + MC_DRV_ID_INVALID = MC_DRV_VENDOR_ID_GD | MC_DRV_NUMBER_INVALID, + MC_DRV_ID_CRYPTO = MC_DRV_VENDOR_ID_GD | MC_DRV_NUMBER_CRYPTO, + MC_DRV_ID_KEYPAD = MC_DRV_VENDOR_ID_GD | MC_DRV_NUMBER_KEYPAD, + /** Last GD driver ID reserved for pre-installed drivers. + * GD driver IDs up to this constant may not be used for loadable drivers. */ + MC_DRV_ID_LAST_PRE_INSTALLED = MC_DRV_VENDOR_ID_GD | MC_DRV_NUMBER_LAST_PRE_INSTALLED, +} mcDriverId_t; + +#endif /* RTMDRVID_H_ */ diff --git a/drivers/gud/include/mcLoadFormat.h b/drivers/gud/include/mcLoadFormat.h new file mode 100644 index 0000000..2e75abd --- /dev/null +++ b/drivers/gud/include/mcLoadFormat.h @@ -0,0 +1,169 @@ +/** + * @defgroup MCLF MobiCore Load Format + * + * @defgroup MCLF_VER MCLF Versions + * @ingroup MCLF + * + * @addtogroup MCLF + * @{ + * + * MobiCore Load Format declarations. + * + * Holds the definitions for the layout of MobiCore Trustlet Blob. + * <!-- Copyright Giesecke & Devrient GmbH 2009-2012 --> + */ +#ifndef MCLOADFORMAT_H_ +#define MCLOADFORMAT_H_ + +#include "mcUuid.h" +#include "mcDriverId.h" + +#define MCLF_VERSION_MAJOR 2 +#define MCLF_VERSION_MINOR 0 + +#define MC_SERVICE_HEADER_MAGIC_BE ((uint32_t)('M'|('C'<<8)|('L'<<16)|('F'<<24))) /**< "MCLF" in big endian integer representation */ +#define MC_SERVICE_HEADER_MAGIC_LE ((uint32_t)(('M'<<24)|('C'<<16)|('L'<<8)|'F')) /**< "MCLF" in little endian integer representation */ +#define MC_SERVICE_HEADER_MAGIC_STR "MCLF" /**< "MCLF" as string */ + +/** @name MCLF flags */ +/*@{*/ +#define MC_SERVICE_HEADER_FLAGS_PERMANENT (1U << 0) /**< Loaded service cannot be unloaded from MobiCore. */ +#define MC_SERVICE_HEADER_FLAGS_NO_CONTROL_INTERFACE (1U << 1) /**< Service has no WSM control interface. */ +/*@}*/ + +#if !defined(ADDR_T_DEFINED) +#define ADDR_T_DEFINED +typedef void* addr_t; /**< an address, can be physical or virtual */ +#endif // !defined(ADDR_T_DEFINED) + +/** Service type. + * The service type defines the type of executable. + */ +typedef enum { + SERVICE_TYPE_ILLEGAL = 0, /**< Service type is invalid. */ + SERVICE_TYPE_DRIVER = 1, /**< Service is a driver. */ + SERVICE_TYPE_SP_TRUSTLET = 2, /**< Service is a Trustlet. */ + SERVICE_TYPE_SYSTEM_TRUSTLET = 3 /**< Service is a system Trustlet. */ +} serviceType_t; + +/** + * Memory types. + */ +typedef enum { + MCLF_MEM_TYPE_INTERNAL_PREFERRED = 0, /**< If available use internal memory; otherwise external memory. */ + MCLF_MEM_TYPE_INTERNAL = 1, /**< Internal memory must be used for executing the service. */ + MCLF_MEM_TYPE_EXTERNAL = 2, /**< External memory must be used for executing the service. */ +} memType_t; + +/** + * Descriptor for a memory segment. + */ +typedef struct { + addr_t start; /**< Virtual start address. */ + uint32_t len; /**< Length of the segment in bytes. */ +} segmentDescriptor_t, *segmentDescriptor_ptr; + +/** + * MCLF intro for data structure identification. + * Must be the first element of a valid MCLF file. + */ +typedef struct { + uint32_t magic; /**< Header magic value ASCII "MCLF". */ + uint32_t version; /**< Version of the MCLF header structure. */ +} mclfIntro_t, *mclfIntro_ptr; + +/** @} */ + + +// Version 1 ///////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup MCLF_VER_V1 MCLF Version 1 + * @ingroup MCLF_VER + * + * @addtogroup MCLF_VER_V1 + * @{ + */ + +/** + * Version 1 MCLF header. + */ +typedef struct { + mclfIntro_t intro; /**< MCLF header start with the mandatory intro. */ + uint32_t flags; /**< Service flags. */ + memType_t memType; /**< Type of memory the service must be executed from. */ + serviceType_t serviceType; /**< Type of service. */ + + uint32_t numInstances; /**< Number of instances which can be run simultaneously. */ + mcUuid_t uuid; /**< Loadable service unique identifier (UUID). */ + mcDriverId_t driverId; /**< If the serviceType is SERVICE_TYPE_DRIVER the Driver ID is used. */ + uint32_t numThreads; /**< + * <pre> + * <br>Number of threads (N) in a service depending on service type.<br> + * + * SERVICE_TYPE_SP_TRUSTLET: N = 1 + * SERVICE_TYPE_SYSTEM_TRUSTLET: N = 1 + * SERVICE_TYPE_DRIVER: N >= 1 + * </pre> + */ + segmentDescriptor_t text; /**< Virtual text segment. */ + segmentDescriptor_t data; /**< Virtual data segment. */ + uint32_t bssLen; /**< Length of the BSS segment in bytes. MUST be at least 8 byte. */ + addr_t entry; /**< Virtual start address of service code. */ +} mclfHeaderV1_t, *mclfHeaderV1_ptr; +/** @} */ + +// Version 2 ///////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup MCLF_VER_V2 MCLF Version 2 + * @ingroup MCLF_VER + * + * @addtogroup MCLF_VER_V2 + * @{ + */ + +/** + * Version 2 MCLF header. + */ +typedef struct { + mclfIntro_t intro; /**< MCLF header start with the mandatory intro. */ + uint32_t flags; /**< Service flags. */ + memType_t memType; /**< Type of memory the service must be executed from. */ + serviceType_t serviceType; /**< Type of service. */ + + uint32_t numInstances; /**< Number of instances which can be run simultaneously. */ + mcUuid_t uuid; /**< Loadable service unique identifier (UUID). */ + mcDriverId_t driverId; /**< If the serviceType is SERVICE_TYPE_DRIVER the Driver ID is used. */ + uint32_t numThreads; /**< + * <pre> + * <br>Number of threads (N) in a service depending on service type.<br> + * + * SERVICE_TYPE_SP_TRUSTLET: N = 1 + * SERVICE_TYPE_SYSTEM_TRUSTLET: N = 1 + * SERVICE_TYPE_DRIVER: N >= 1 + * </pre> + */ + segmentDescriptor_t text; /**< Virtual text segment. */ + segmentDescriptor_t data; /**< Virtual data segment. */ + uint32_t bssLen; /**< Length of the BSS segment in bytes. MUST be at least 8 byte. */ + addr_t entry; /**< Virtual start address of service code. */ + uint32_t serviceVersion; /**< Version of the interface the driver exports. */ +} mclfHeaderV2_t, *mclfHeaderV2_ptr; +/** @} */ + + +// Version 1 and 2 /////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * @addtogroup MCLF + * @{ + */ + +/** MCLF header */ +typedef union { + mclfIntro_t intro; /**< Intro for data structure identification. */ + mclfHeaderV1_t mclfHeaderV1; /**< Version 1 header */ + mclfHeaderV2_t mclfHeaderV2; /**< Version 2 header */ +} mclfHeader_t, *mclfHeader_ptr; + +#endif /* MCLOADFORMAT_H_ */ + +/** @} */ diff --git a/drivers/gud/include/mcRootid.h b/drivers/gud/include/mcRootid.h new file mode 100644 index 0000000..53e7be7 --- /dev/null +++ b/drivers/gud/include/mcRootid.h @@ -0,0 +1,28 @@ +/** + * @addtogroup MC_ROOTID mcRootid - Root container id. + * + * Global definition of root ID. + * + * <!-- Copyright Giesecke & Devrient GmbH 2011-2012 --> + * @ingroup MC_DATA_TYPES + * @{ + */ + +#ifndef MC_ROOTID_H_ +#define MC_ROOTID_H_ + +/** Root Identifier type. */ +typedef uint32_t mcRootid_t; + +/** Reserved root id value 1. */ +static const mcRootid_t MC_ROOTID_RESERVED1 = 0; + +/** Reserved root id value 2. */ +static const mcRootid_t MC_ROOTID_RESERVED2 = 0xFFFFFFFF; + +/** Root id for system applications. */ +static const mcRootid_t MC_ROOTID_SYSTEM = 0xFFFFFFFE; + +#endif // MC_ROOTID_H_ + +/** @} */ diff --git a/drivers/gud/include/mcSo.h b/drivers/gud/include/mcSo.h new file mode 100644 index 0000000..19698e8 --- /dev/null +++ b/drivers/gud/include/mcSo.h @@ -0,0 +1,139 @@ +/** + * @defgroup MC_DATA_TYPES MobiCore generic data types + * + * @addtogroup MC_SO mcSo - Secure objects definitions. + * <!-- Copyright Giesecke & Devrient GmbH 2011-2012 --> + * @ingroup MC_DATA_TYPES + * @{ + * + */ + +#ifndef MC_SO_H_ +#define MC_SO_H_ + +#include "mcUuid.h" +#include "mcSpid.h" + +#define SO_VERSION_MAJOR 2 +#define SO_VERSION_MINOR 0 + +#define MC_ENUM_32BIT_SPACER ((int32_t)-1) + +/** Secure object type. */ +typedef enum { + /** Regular secure object. */ + MC_SO_TYPE_REGULAR = 0x00000001, + /** Dummy to ensure that enum is 32 bit wide. */ + MC_SO_TYPE_DUMMY = MC_ENUM_32BIT_SPACER, +} mcSoType_t; + + +/** Secure object context. + * A context defines which key to use to encrypt/decrypt a secure object. + */ +typedef enum { + /** Trustlet context. */ + MC_SO_CONTEXT_TLT = 0x00000001, + /** Service provider context. */ + MC_SO_CONTEXT_SP = 0x00000002, + /** Device context. */ + MC_SO_CONTEXT_DEVICE = 0x00000003, + /** Dummy to ensure that enum is 32 bit wide. */ + MC_SO_CONTEXT_DUMMY = MC_ENUM_32BIT_SPACER, +} mcSoContext_t; + +/** Secure object lifetime. + * A lifetime defines how long a secure object is valid. + */ +typedef enum { + /** SO does not expire. */ + MC_SO_LIFETIME_PERMANENT = 0x00000000, + /** SO expires on reboot (coldboot). */ + MC_SO_LIFETIME_POWERCYCLE = 0x00000001, + /** SO expires when Trustlet is closed. */ + MC_SO_LIFETIME_SESSION = 0x00000002, + /** Dummy to ensure that enum is 32 bit wide. */ + MC_SO_LIFETIME_DUMMY = MC_ENUM_32BIT_SPACER, +} mcSoLifeTime_t; + +/** Service provider Trustlet id. + * The combination of service provider id and Trustlet UUID forms a unique + * Trustlet identifier. + */ +typedef struct { + /** Service provider id. */ + mcSpid_t spid; + /** Trustlet UUID. */ + mcUuid_t uuid; +} tlApiSpTrustletId_t; + +/** Secure object header. + * A secure object header introduces a secure object. + * Layout of a secure object: + * <pre> + * <code> + * + * +--------+------------------+------------------+--------+---------+ + * | Header | plain-data | encrypted-data | hash | padding | + * +--------+------------------+------------------+--------+---------+ + * + * /--------/---- plainLen ----/-- encryptedLen --/-- 32 --/- 1..16 -/ + * + * /----------------- toBeHashedLen --------------/ + * + * /---------- toBeEncryptedLen ---------/ + * + * /--------------------------- totalSoSize -------------------------/ + * + * </code> + * </pre> + */ +typedef struct { + /** Type of secure object. */ + uint32_t type; + /** Secure object version. */ + uint32_t version; + /** Secure object context. */ + mcSoContext_t context; + /** Secure object lifetime. */ + mcSoLifeTime_t lifetime; + /** Producer Trustlet id. */ + tlApiSpTrustletId_t producer; + /** Length of unencrypted user data (after the header). */ + uint32_t plainLen; + /** Length of encrypted user data (after unencrypted data, excl. checksum + * and excl. padding bytes). */ + uint32_t encryptedLen; +} mcSoHeader_t; + +#endif // MC_SO_H_ + +/** Maximum size of the payload (plain length + encrypted length) of a secure object. */ +#define MC_SO_PAYLOAD_MAX_SIZE 1000000 + +/** Block size of encryption algorithm used for secure objects. */ +#define MC_SO_ENCRYPT_BLOCK_SIZE 16 + +/** Maximum number of ISO padding bytes. */ +#define MC_SO_MAX_PADDING_SIZE (MC_SO_ENCRYPT_BLOCK_SIZE) + +/** Size of hash used for secure objects. */ +#define MC_SO_HASH_SIZE 32 + +/** Calculates gross size of cryptogram within secure object including ISO padding bytes. */ +#define MC_SO_ENCRYPT_PADDED_SIZE(netsize) ( (netsize) + \ + MC_SO_MAX_PADDING_SIZE - (netsize) % MC_SO_MAX_PADDING_SIZE ) + +/** Calculates the total size of a secure object. + * @param plainLen Length of plain text part within secure object. + * @param encryptedLen Length of encrypted part within secure object (excl. + * hash, padding). + * @return Total (gross) size of the secure object or 0 if given parameters are + * illegal or would lead to a secure object of invalid size. + */ +#define MC_SO_SIZE(plainLen, encryptedLen) ( \ + ((plainLen) + (encryptedLen) < (encryptedLen) || (plainLen) + (encryptedLen) > MC_SO_PAYLOAD_MAX_SIZE) ? 0 : \ + sizeof(mcSoHeader_t) + (plainLen) + MC_SO_ENCRYPT_PADDED_SIZE((encryptedLen) + MC_SO_HASH_SIZE) \ +) + +/** @} */ diff --git a/drivers/gud/include/mcSpid.h b/drivers/gud/include/mcSpid.h new file mode 100644 index 0000000..56b6c7c --- /dev/null +++ b/drivers/gud/include/mcSpid.h @@ -0,0 +1,26 @@ +/** + * @addtogroup MC_SPID mcSpid - service provider ID. + * + * <!-- Copyright Giesecke & Devrient GmbH 2011-2012 --> + * @ingroup MC_DATA_TYPES + * @{ + */ + +#ifndef MC_SPID_H_ +#define MC_SPID_H_ + +/** Service provider Identifier type. */ +typedef uint32_t mcSpid_t; + +/** SPID value used as free marker in root containers. */ +static const mcSpid_t MC_SPID_FREE = 0xFFFFFFFF; + +/** Reserved SPID value. */ +static const mcSpid_t MC_SPID_RESERVED = 0; + +/** SPID for system applications. */ +static const mcSpid_t MC_SPID_SYSTEM = 0xFFFFFFFE; + +#endif // MC_SPID_H_ + +/** @} */ diff --git a/drivers/gud/include/mcSuid.h b/drivers/gud/include/mcSuid.h new file mode 100644 index 0000000..8137039 --- /dev/null +++ b/drivers/gud/include/mcSuid.h @@ -0,0 +1,28 @@ +/** + * @addtogroup MC_SUID mcSuid - SoC unique ID. + * + * <!-- Copyright Giesecke & Devrient GmbH 2011-2012 --> + * @ingroup MC_DATA_TYPES + * @{ + */ + +#ifndef MC_SUID_H_ +#define MC_SUID_H_ + +/** Length of SUID. */ +#define MC_SUID_LEN 16 + +/** Platform specific device identifier (serial number of the chip). */ +typedef struct { + uint8_t data[MC_SUID_LEN - sizeof(uint32_t)]; +} suidData_t; + +/** Soc unique identifier type. */ +typedef struct { + uint32_t sipId; /**< Silicon Provider ID to be set during build. */ + suidData_t suidData; +} mcSuid_t; + +#endif // MC_SUID_H_ + +/** @} */ diff --git a/drivers/gud/include/mcUuid.h b/drivers/gud/include/mcUuid.h new file mode 100644 index 0000000..f59ab2a --- /dev/null +++ b/drivers/gud/include/mcUuid.h @@ -0,0 +1,48 @@ +/** + * @addtogroup MC_UUID mcUuid - Universally Unique Identifier. + * + * <!-- Copyright Giesecke & Devrient GmbH 2011-2012 --> + * @ingroup MC_DATA_TYPES + * @{ + */ + +#ifndef MC_UUID_H_ +#define MC_UUID_H_ + +#define UUID_TYPE + +/** Universally Unique Identifier (UUID) according to ISO/IEC 11578. */ +typedef struct { + uint8_t value[16]; /**< Value of the UUID. */ +} mcUuid_t, *mcUuid_ptr; + +/** 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 mcUuid_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 mcUuid_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 mcUuid_t MC_UUID_SYSTEM = { + MC_UUID_SYSTEM_DEFINE +}; + +#endif // MC_UUID_H_ + +/** @} */ diff --git a/drivers/gud/include/mcVersionHelper.h b/drivers/gud/include/mcVersionHelper.h new file mode 100644 index 0000000..41d3053 --- /dev/null +++ b/drivers/gud/include/mcVersionHelper.h @@ -0,0 +1,170 @@ +/** @addtogroup MC_RTM + * @{ + * MobiCore Version Helper Macros + * + * <!-- Copyright Giesecke & Devrient GmbH 2009-2012 --> + */ +#include <stdio.h> + +//lint -emacro(*,MC_CHECK_VERSION) Disable all warnings for this macro. +//lint -emacro(*,MC_MAKE_VERSION) Disable all warnings for this macro. +//lint -emacro(*,MC_GET_MAJOR_VERSION) Disable all warnings for this macro. +//lint -emacro(*,MC_GET_MINOR_VERSION) Disable all warnings for this macro. +//lint -emacro(*,MC_GET_MINOR_VERSION) Disable all warnings for this macro. +//lint -emacro(*,ASSERT_VERSION_IMPLEMENTATION) Disable all warnings for this macro. +//lint -esym(*,Actual_*) Disable all warnings for these functions. + +/** Create a version number given major and minor numbers. */ +#define MC_MAKE_VERSION(major,minor) \ + ( (((major) & 0xffff) << 16) |\ + ((minor) & 0xffff)) + +/** Get major version number from complete version. */ +#define MC_GET_MAJOR_VERSION(version) ((version) >> 16) + +/** Get minor version number from complete version. */ +#define MC_GET_MINOR_VERSION(version) ((version) & 0xffff) + +// Asserts expression at compile-time (to be used outside a function body). +#define ASSERT_VERSION_IMPLEMENTATION(comp, versionpart, requiredV, actualV, expression) \ + extern int Actual_##comp##_##versionpart##_VERSION_##actualV##_does_not_match_required_version_##requiredV[(expression) ? 0:-1] + +#define ASSERT_VERSION_EVALUATOR(comp, versionpart, requiredV, actualV, expression) \ + ASSERT_VERSION_IMPLEMENTATION(comp, versionpart, requiredV, actualV, expression) + +#define ASSERT_VERSION(required, comparator, comp, versionpart) \ + ASSERT_VERSION_EVALUATOR(comp, versionpart, required, comp ##_VERSION_## versionpart, required comparator comp ##_VERSION_## versionpart) + +/** Checks at compile-time that an interface version provided by component + * 'comp' is identical to the required version of a component using this interface. + * Note! This check is useful for components that IMPLEMENT a particular + * interface to be alerted of changes to the interface which are likely to + * require adaptations in the implementation. */ +#define MC_CHECK_VERSION_EQUALS(comp, major, minor) \ + ASSERT_VERSION(major, ==, comp, MAJOR); \ + ASSERT_VERSION(minor, ==, comp, MINOR); + +/** Checks at compile-time that an interface version provided by component 'comp' meets the + * required version of a component using this interface. */ +#define MC_CHECK_VERSION_STATIC(comp, majorRequired, minorRequired) \ + ASSERT_VERSION(majorRequired, ==, comp, MAJOR); \ + ASSERT_VERSION(minorRequired, <=, comp, MINOR); + +/** Version check helper macro for an interface consumer against an interface + * provider. + * @param comp Name of Interface to check. + * @param majorRequired Required major version of interface provider. + * @param minorRequired Required minor version of interface provider. + * Performs a compile-time interface version check that comp_VERSION_MAJOR + * equals majorRequired and that comp_VERSION_MINOR is at least minorRequired. + * On success, compilation goes through. + * On error, compilation breaks, telling the component that did not match in the + * error message. + * + * Additionally, a function is created: + * + * checkVersionOk##component(uint32_t version, char** errmsg) + * + * Compares version against majorRequired and minorRequired. + * Additionally, it creates a message string that can be printed out using printf("%s", errmsg). + * It returns either only the actual version, or on mismatch, actual and required version. + * + * @param version[in] component version as returned by layer-specific getVersion. + * @param errmsg[out] a message string that contains a log. + * + */ +#if !defined(NDEBUG) +#define MC_CHECK_VERSION(comp, majorRequired, minorRequired) \ + MC_CHECK_VERSION_STATIC(comp, majorRequired, minorRequired) \ + static uint32_t checkVersionOk##comp(uint32_t version, char** errmsg) { \ + static char msgBuf[100]; \ + uint32_t major = MC_GET_MAJOR_VERSION(version); \ + uint32_t minor = MC_GET_MINOR_VERSION(version); \ + uint32_t ret = 0; \ + *errmsg = msgBuf; \ + if ((major == majorRequired) && (minor >= minorRequired)) { \ + snprintf(msgBuf, sizeof(msgBuf), \ + #comp " version is %u.%u", major, minor); \ + ret = 1; \ + } else { \ + snprintf(msgBuf, sizeof(msgBuf), \ + #comp " version error. Got: %u.%u, want >= %u.%u", major, minor, majorRequired, minorRequired); \ + } \ + msgBuf[sizeof(msgBuf) - 1] = '\0'; \ + return ret; \ + } +#else +#define MC_CHECK_VERSION(comp, majorRequired, minorRequired) \ + MC_CHECK_VERSION_STATIC(comp, majorRequired, minorRequired) \ + static uint32_t checkVersionOk##comp(uint32_t version, char** errmsg) { \ + uint32_t major = MC_GET_MAJOR_VERSION(version); \ + uint32_t minor = MC_GET_MINOR_VERSION(version); \ + *errmsg = NULL; \ + if ((major == majorRequired) && (minor >= minorRequired)) { \ + return 1; \ + }; \ + return 0; \ + } +#endif + +/** Version check helper macro for version checks of a data object version + * against an data object consumer. + * + * @param comp Name of Interface to check. + * @param majorRequired Major data object version supported by component. + * @param minorRequired Minor data object version supported by component. + * Performs a compile-time interface version check that comp_VERSION_MAJOR + * equals majorRequired and that comp_VERSION_MINOR is at least minorRequired. + * On success, compilation goes through. + * On error, compilation breaks, telling the component that did not match in the + * error message. + * + * Additionally, the following function is created: + * + * checkVersionOkDataObject##component(uint32_t version, char** errmsg) + * + * This function checks that the data object version is compatible with the + * interface version; that is, the major version of the data object must match + * exactly and the minor version of the data object MUST BE LESS OR EQUAL to the + * required interface version. + * Additionally, it creates a message string that can be printed out using printf("%s", errmsg). + * It returns either only the actual version, or on mismatch, actual and + * provided version. + * + * @param version[in] Data object version of data object. + * @param errmsg[out] a message string that contains a log. + * + */ +#if !defined(NDEBUG) +#define MC_CHECK_DATA_OBJECT_VERSION(comp, majorRequired, minorRequired) \ + MC_CHECK_VERSION_STATIC(comp, majorRequired, minorRequired) \ + static uint32_t checkVersionOkDataObject##comp(uint32_t version, char** errmsg) { \ + static char msgBuf[100]; \ + uint32_t major = MC_GET_MAJOR_VERSION(version); \ + uint32_t minor = MC_GET_MINOR_VERSION(version); \ + uint32_t ret = 0; \ + *errmsg = msgBuf; \ + if ((major == majorRequired) && (minor <= minorRequired)) { \ + snprintf(msgBuf, sizeof(msgBuf), \ + #comp " version is %u.%u", major, minor); \ + ret = 1; \ + } else { \ + snprintf(msgBuf, sizeof(msgBuf), \ + #comp " version error. Got: %u.%u, want <= %u.%u", major, minor, majorRequired, minorRequired); \ + } \ + msgBuf[sizeof(msgBuf) - 1] = '\0'; \ + return ret; \ + } +#else +#define MC_CHECK_DATA_OBJECT_VERSION(comp, majorRequired, minorRequired) \ + MC_CHECK_VERSION_STATIC(comp, majorRequired, minorRequired) \ + static uint32_t checkVersionOkDataObject##comp(uint32_t version, char** errmsg) { \ + uint32_t major = MC_GET_MAJOR_VERSION(version); \ + uint32_t minor = MC_GET_MINOR_VERSION(version); \ + *errmsg = NULL; \ + if ((major == majorRequired) && (minor <= minorRequired)) { \ + return 1; \ + }; \ + return 0; \ + } +#endif diff --git a/drivers/gud/include/mcVersionInfo.h b/drivers/gud/include/mcVersionInfo.h new file mode 100644 index 0000000..f513698 --- /dev/null +++ b/drivers/gud/include/mcVersionInfo.h @@ -0,0 +1,28 @@ +/** @addtogroup MC_RTM + * @{ + * MobiCore Version Information + * + * <!-- Copyright Giesecke & Devrient GmbH 2009-2012 --> + */ + +#ifndef MCVERSIONINFO_H_ +#define MCVERSIONINFO_H_ + +/** Length of MobiCore product ID string. */ +#define MC_PRODUCT_ID_LEN 64 + +/** Global MobiCore Version Information. + */ +typedef struct { + char productId[MC_PRODUCT_ID_LEN]; /** < Product ID of Mobicore; zero-terminated */ + uint32_t versionMci; /** < Version of Mobicore Control Interface */ + uint32_t versionSo; /** < Version of Secure Objects */ + uint32_t versionMclf; /** < Version of MobiCore Load Format */ + uint32_t versionContainer; /** < Version of MobiCore Container Format */ + uint32_t versionMcConfig; /** < Version of MobiCore Configuration Block Format */ + uint32_t versionTlApi; /** < Version of MobiCore Trustlet API Implementation */ + uint32_t versionDrApi; /** < Version of MobiCore Driver API Implementation */ + uint32_t versionCmp; /** < Version of Content Management Protocol */ +} mcVersionInfo_t; + +#endif /** MCVERSIONINFO_H_ */ |