summaryrefslogtreecommitdiff
path: root/security/tf_driver/scxlnx_conn.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/tf_driver/scxlnx_conn.c')
-rw-r--r--security/tf_driver/scxlnx_conn.c1530
1 files changed, 1530 insertions, 0 deletions
diff --git a/security/tf_driver/scxlnx_conn.c b/security/tf_driver/scxlnx_conn.c
new file mode 100644
index 000000000000..cac8e0e795e2
--- /dev/null
+++ b/security/tf_driver/scxlnx_conn.c
@@ -0,0 +1,1530 @@
+/*
+ * Copyright (c) 2006-2010 Trusted Logic S.A.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <asm/atomic.h>
+#include <linux/uaccess.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/types.h>
+
+#include "s_version.h"
+
+#include "scx_protocol.h"
+#include "scxlnx_defs.h"
+#include "scxlnx_util.h"
+#include "scxlnx_comm.h"
+#include "scxlnx_conn.h"
+
+#ifdef CONFIG_TF_ZEBRA
+#include "scx_public_crypto.h"
+#endif
+
+/*----------------------------------------------------------------------------
+ * Management of the shared memory blocks.
+ *
+ * Shared memory blocks are the blocks registered through
+ * the commands REGISTER_SHARED_MEMORY and POWER_MANAGEMENT
+ *----------------------------------------------------------------------------*/
+
+/**
+ * Unmaps a shared memory
+ **/
+static void SCXLNXConnUnmapShmem(
+ struct SCXLNX_CONNECTION *pConn,
+ struct SCXLNX_SHMEM_DESC *pShmemDesc,
+ u32 nFullCleanup)
+{
+ /* check pShmemDesc contains a descriptor */
+ if (pShmemDesc == NULL)
+ return;
+
+ dprintk(KERN_DEBUG "SCXLNXConnUnmapShmem(%p)\n", pShmemDesc);
+
+retry:
+ mutex_lock(&(pConn->sharedMemoriesMutex));
+ if (atomic_read(&pShmemDesc->nRefCnt) > 1) {
+ /*
+ * Shared mem still in use, wait for other operations completion
+ * before actually unmapping it.
+ */
+ dprintk(KERN_INFO "Descriptor in use\n");
+ mutex_unlock(&(pConn->sharedMemoriesMutex));
+ schedule();
+ goto retry;
+ }
+
+ SCXLNXCommReleaseSharedMemory(
+ &(pConn->sAllocationContext),
+ pShmemDesc,
+ nFullCleanup);
+
+ list_del(&(pShmemDesc->list));
+
+ if ((pShmemDesc->nType == SCXLNX_SHMEM_TYPE_REGISTERED_SHMEM) ||
+ (nFullCleanup != 0)) {
+ internal_kfree(pShmemDesc);
+
+ atomic_dec(&(pConn->nShmemAllocated));
+ } else {
+ /*
+ * This is a preallocated shared memory, add to free list
+ * Since the device context is unmapped last, it is
+ * always the first element of the free list if no
+ * device context has been created
+ */
+ pShmemDesc->hIdentifier = 0;
+ list_add(&(pShmemDesc->list), &(pConn->sFreeSharedMemoryList));
+ }
+
+ mutex_unlock(&(pConn->sharedMemoriesMutex));
+}
+
+
+/**
+ * Find the first available slot for a new block of shared memory
+ * and map the user buffer.
+ * Update the pDescriptors to L1 descriptors
+ * Update the pBufferStartOffset and pBufferSize fields
+ * pShmemDesc is updated to the mapped shared memory descriptor
+ **/
+static int SCXLNXConnMapShmem(
+ struct SCXLNX_CONNECTION *pConn,
+ u32 nBufferVAddr,
+ /* flags for read-write access rights on the memory */
+ u32 nFlags,
+ bool bInUserSpace,
+ u32 pDescriptors[SCX_MAX_COARSE_PAGES],
+ u32 *pBufferStartOffset,
+ u32 *pBufferSize,
+ struct SCXLNX_SHMEM_DESC **ppShmemDesc,
+ u32 *pnDescriptorCount)
+{
+ struct SCXLNX_SHMEM_DESC *pShmemDesc = NULL;
+ int nError;
+
+ dprintk(KERN_INFO "SCXLNXConnMapShmem(%p, %p, flags = 0x%08x)\n",
+ pConn,
+ (void *) nBufferVAddr,
+ nFlags);
+
+ mutex_lock(&(pConn->sharedMemoriesMutex));
+
+ /*
+ * Check the list of free shared memory
+ * is not empty
+ */
+ if (list_empty(&(pConn->sFreeSharedMemoryList))) {
+ if (atomic_read(&(pConn->nShmemAllocated)) ==
+ SCXLNX_SHMEM_MAX_COUNT) {
+ printk(KERN_ERR "SCXLNXConnMapShmem(%p):"
+ " maximum shared memories already registered\n",
+ pConn);
+ nError = -ENOMEM;
+ goto error;
+ }
+
+ atomic_inc(&(pConn->nShmemAllocated));
+
+ /* no descriptor available, allocate a new one */
+
+ pShmemDesc = (struct SCXLNX_SHMEM_DESC *) internal_kmalloc(
+ sizeof(*pShmemDesc), GFP_KERNEL);
+ if (pShmemDesc == NULL) {
+ printk(KERN_ERR "SCXLNXConnMapShmem(%p):"
+ " failed to allocate descriptor\n",
+ pConn);
+ nError = -ENOMEM;
+ goto error;
+ }
+
+ /* Initialize the structure */
+ pShmemDesc->nType = SCXLNX_SHMEM_TYPE_REGISTERED_SHMEM;
+ atomic_set(&pShmemDesc->nRefCnt, 1);
+ INIT_LIST_HEAD(&(pShmemDesc->list));
+ } else {
+ /* take the first free shared memory descriptor */
+ pShmemDesc = list_entry(pConn->sFreeSharedMemoryList.next,
+ struct SCXLNX_SHMEM_DESC, list);
+ list_del(&(pShmemDesc->list));
+ }
+
+ /* Add the descriptor to the used list */
+ list_add(&(pShmemDesc->list), &(pConn->sUsedSharedMemoryList));
+
+ nError = SCXLNXCommFillDescriptorTable(
+ &(pConn->sAllocationContext),
+ pShmemDesc,
+ nBufferVAddr,
+ pConn->ppVmas,
+ pDescriptors,
+ pBufferSize,
+ pBufferStartOffset,
+ bInUserSpace,
+ nFlags,
+ pnDescriptorCount);
+
+ if (nError != 0) {
+ dprintk(KERN_ERR "SCXLNXConnMapShmem(%p):"
+ " SCXLNXCommFillDescriptorTable failed with error "
+ "code %d!\n",
+ pConn,
+ nError);
+ goto error;
+ }
+ pShmemDesc->pBuffer = (u8 *) nBufferVAddr;
+
+ /*
+ * Successful completion.
+ */
+ *ppShmemDesc = pShmemDesc;
+ mutex_unlock(&(pConn->sharedMemoriesMutex));
+ dprintk(KERN_DEBUG "SCXLNXConnMapShmem: success\n");
+ return 0;
+
+
+ /*
+ * Error handling.
+ */
+error:
+ mutex_unlock(&(pConn->sharedMemoriesMutex));
+ dprintk(KERN_ERR "SCXLNXConnMapShmem: failure with error code %d\n",
+ nError);
+
+ SCXLNXConnUnmapShmem(
+ pConn,
+ pShmemDesc,
+ 0);
+
+ return nError;
+}
+
+
+
+/* This function is a copy of the find_vma() function
+in linux kernel 2.6.15 version with some fixes :
+ - memory block may end on vm_end
+ - check the full memory block is in the memory area
+ - guarantee NULL is returned if no memory area is found */
+struct vm_area_struct *SCXLNXConnFindVma(struct mm_struct *mm,
+ unsigned long addr, unsigned long size)
+{
+ struct vm_area_struct *vma = NULL;
+
+ dprintk(KERN_INFO
+ "SCXLNXConnFindVma addr=0x%lX size=0x%lX\n", addr, size);
+
+ if (mm) {
+ /* Check the cache first. */
+ /* (Cache hit rate is typically around 35%.) */
+ vma = mm->mmap_cache;
+ if (!(vma && vma->vm_end >= (addr+size) &&
+ vma->vm_start <= addr)) {
+ struct rb_node *rb_node;
+
+ rb_node = mm->mm_rb.rb_node;
+ vma = NULL;
+
+ while (rb_node) {
+ struct vm_area_struct *vma_tmp;
+
+ vma_tmp = rb_entry(rb_node,
+ struct vm_area_struct, vm_rb);
+
+ dprintk(KERN_INFO
+ "vma_tmp->vm_start=0x%lX"
+ "vma_tmp->vm_end=0x%lX\n",
+ vma_tmp->vm_start,
+ vma_tmp->vm_end);
+
+ if (vma_tmp->vm_end >= (addr+size)) {
+ vma = vma_tmp;
+ if (vma_tmp->vm_start <= addr)
+ break;
+
+ rb_node = rb_node->rb_left;
+ } else {
+ rb_node = rb_node->rb_right;
+ }
+ }
+
+ if (vma)
+ mm->mmap_cache = vma;
+ if (rb_node == NULL)
+ vma = NULL;
+ }
+ }
+ return vma;
+}
+
+static int SCXLNXConnValidateSharedMemoryBlockAndFlags(
+ void *pSharedMemory,
+ u32 nSharedMemorySize,
+ u32 nFlags)
+{
+ struct vm_area_struct *vma;
+ unsigned long nSharedMemory = (unsigned long) pSharedMemory;
+ u32 nChunk;
+
+ if (nSharedMemorySize == 0)
+ /* This is always valid */
+ return 0;
+
+ if ((nSharedMemory + nSharedMemorySize) < nSharedMemory)
+ /* Overflow */
+ return -EINVAL;
+
+ down_read(&current->mm->mmap_sem);
+
+ /*
+ * When looking for a memory address, split buffer into chunks of
+ * size=PAGE_SIZE.
+ */
+ nChunk = PAGE_SIZE - (nSharedMemory & (PAGE_SIZE-1));
+ if (nChunk > nSharedMemorySize)
+ nChunk = nSharedMemorySize;
+
+ do {
+ vma = SCXLNXConnFindVma(current->mm, nSharedMemory, nChunk);
+
+ if (vma == NULL)
+ goto error;
+
+ if (nFlags & SCX_SHMEM_TYPE_READ)
+ if (!(vma->vm_flags & VM_READ))
+ goto error;
+ if (nFlags & SCX_SHMEM_TYPE_WRITE)
+ if (!(vma->vm_flags & VM_WRITE))
+ goto error;
+
+ nSharedMemorySize -= nChunk;
+ nSharedMemory += nChunk;
+ nChunk = (nSharedMemorySize <= PAGE_SIZE ?
+ nSharedMemorySize : PAGE_SIZE);
+ } while (nSharedMemorySize != 0);
+
+ up_read(&current->mm->mmap_sem);
+ return 0;
+
+error:
+ up_read(&current->mm->mmap_sem);
+ dprintk(KERN_ERR "SCXLNXConnValidateSharedMemoryBlockAndFlags: "
+ "return error\n");
+ return -EFAULT;
+}
+
+
+static int SCXLNXConnMapTempShMem(struct SCXLNX_CONNECTION *pConn,
+ struct SCX_COMMAND_PARAM_TEMP_MEMREF *pTempMemRef,
+ u32 nParamType,
+ struct SCXLNX_SHMEM_DESC **ppShmemDesc)
+{
+ u32 nFlags;
+ u32 nError = S_SUCCESS;
+
+ dprintk(KERN_INFO "SCXLNXConnMapTempShMem(%p, "
+ "0x%08x[size=0x%08x], offset=0x%08x)\n",
+ pConn,
+ pTempMemRef->nDescriptor,
+ pTempMemRef->nSize,
+ pTempMemRef->nOffset);
+
+ switch (nParamType) {
+ case SCX_PARAM_TYPE_MEMREF_TEMP_INPUT:
+ nFlags = SCX_SHMEM_TYPE_READ;
+ break;
+ case SCX_PARAM_TYPE_MEMREF_TEMP_OUTPUT:
+ nFlags = SCX_SHMEM_TYPE_WRITE;
+ break;
+ case SCX_PARAM_TYPE_MEMREF_TEMP_INOUT:
+ nFlags = SCX_SHMEM_TYPE_WRITE | SCX_SHMEM_TYPE_READ;
+ break;
+ default:
+ nError = -EINVAL;
+ goto error;
+ }
+
+ if (pTempMemRef->nDescriptor == 0) {
+ /* NULL tmpref */
+ pTempMemRef->nOffset = 0;
+ *ppShmemDesc = NULL;
+ } else if ((pTempMemRef->nDescriptor != 0) &&
+ (pTempMemRef->nSize == 0)) {
+ /* Empty tmpref */
+ pTempMemRef->nOffset = pTempMemRef->nDescriptor;
+ pTempMemRef->nDescriptor = 0;
+ pTempMemRef->nSize = 0;
+ *ppShmemDesc = NULL;
+ } else {
+ /* Map the temp shmem block */
+
+ u32 nSharedMemDescriptors[SCX_MAX_COARSE_PAGES];
+ u32 nDescriptorCount;
+
+ nError = SCXLNXConnValidateSharedMemoryBlockAndFlags(
+ (void *) pTempMemRef->nDescriptor,
+ pTempMemRef->nSize,
+ nFlags);
+ if (nError != 0)
+ goto error;
+
+ nError = SCXLNXConnMapShmem(
+ pConn,
+ pTempMemRef->nDescriptor,
+ nFlags,
+ true,
+ nSharedMemDescriptors,
+ &(pTempMemRef->nOffset),
+ &(pTempMemRef->nSize),
+ ppShmemDesc,
+ &nDescriptorCount);
+ pTempMemRef->nDescriptor = nSharedMemDescriptors[0];
+ }
+
+error:
+ return nError;
+}
+
+/*
+ * Clean up a list of shared memory descriptors.
+ */
+static void SCXLNXSharedMemoryCleanupList(
+ struct SCXLNX_CONNECTION *pConn,
+ struct list_head *pList)
+{
+ while (!list_empty(pList)) {
+ struct SCXLNX_SHMEM_DESC *pShmemDesc;
+
+ pShmemDesc = list_entry(pList->next, struct SCXLNX_SHMEM_DESC,
+ list);
+
+ SCXLNXConnUnmapShmem(pConn, pShmemDesc, 1);
+ }
+}
+
+
+/*
+ * Clean up the shared memory information in the connection.
+ * Releases all allocated pages.
+ */
+void SCXLNXConnCleanupSharedMemory(struct SCXLNX_CONNECTION *pConn)
+{
+ /* clean up the list of used and free descriptors.
+ * done outside the mutex, because SCXLNXConnUnmapShmem already
+ * mutex()ed
+ */
+ SCXLNXSharedMemoryCleanupList(pConn,
+ &pConn->sUsedSharedMemoryList);
+ SCXLNXSharedMemoryCleanupList(pConn,
+ &pConn->sFreeSharedMemoryList);
+
+ mutex_lock(&(pConn->sharedMemoriesMutex));
+
+ /* Free the Vmas page */
+ if (pConn->ppVmas) {
+ internal_free_page((unsigned long) pConn->ppVmas);
+ pConn->ppVmas = NULL;
+ }
+
+ SCXLNXReleaseCoarsePageTableAllocator(
+ &(pConn->sAllocationContext));
+
+ mutex_unlock(&(pConn->sharedMemoriesMutex));
+}
+
+
+/*
+ * Initialize the shared memory in a connection.
+ * Allocates the minimum memory to be provided
+ * for shared memory management
+ */
+int SCXLNXConnInitSharedMemory(struct SCXLNX_CONNECTION *pConn)
+{
+ int nError;
+ int nSharedMemoryDescriptorIndex;
+ int nCoarsePageIndex;
+
+ /*
+ * We only need to initialize special elements and attempt to allocate
+ * the minimum shared memory descriptors we want to support
+ */
+
+ mutex_init(&(pConn->sharedMemoriesMutex));
+ INIT_LIST_HEAD(&(pConn->sFreeSharedMemoryList));
+ INIT_LIST_HEAD(&(pConn->sUsedSharedMemoryList));
+ atomic_set(&(pConn->nShmemAllocated), 0);
+
+ SCXLNXInitializeCoarsePageTableAllocator(
+ &(pConn->sAllocationContext));
+
+
+ /*
+ * Preallocate 3 pages to increase the chances that a connection
+ * succeeds in allocating shared mem
+ */
+ for (nSharedMemoryDescriptorIndex = 0;
+ nSharedMemoryDescriptorIndex < 3;
+ nSharedMemoryDescriptorIndex++) {
+ struct SCXLNX_SHMEM_DESC *pShmemDesc =
+ (struct SCXLNX_SHMEM_DESC *) internal_kmalloc(
+ sizeof(*pShmemDesc), GFP_KERNEL);
+
+ if (pShmemDesc == NULL) {
+ printk(KERN_ERR "SCXLNXConnInitSharedMemory(%p):"
+ " failed to pre allocate descriptor %d\n",
+ pConn,
+ nSharedMemoryDescriptorIndex);
+ nError = -ENOMEM;
+ goto error;
+ }
+
+ for (nCoarsePageIndex = 0;
+ nCoarsePageIndex < SCX_MAX_COARSE_PAGES;
+ nCoarsePageIndex++) {
+ struct SCXLNX_COARSE_PAGE_TABLE *pCoarsePageTable;
+
+ pCoarsePageTable = SCXLNXAllocateCoarsePageTable(
+ &(pConn->sAllocationContext),
+ SCXLNX_PAGE_DESCRIPTOR_TYPE_PREALLOCATED);
+
+ if (pCoarsePageTable == NULL) {
+ printk(KERN_ERR "SCXLNXConnInitSharedMemory(%p)"
+ ": descriptor %d coarse page %d - "
+ "SCXLNXConnAllocateCoarsePageTable() "
+ "failed\n",
+ pConn,
+ nSharedMemoryDescriptorIndex,
+ nCoarsePageIndex);
+ nError = -ENOMEM;
+ goto error;
+ }
+
+ pShmemDesc->pCoarsePageTable[nCoarsePageIndex] =
+ pCoarsePageTable;
+ }
+ pShmemDesc->nNumberOfCoarsePageTables = 0;
+
+ pShmemDesc->nType = SCXLNX_SHMEM_TYPE_PREALLOC_REGISTERED_SHMEM;
+ atomic_set(&pShmemDesc->nRefCnt, 1);
+
+ /*
+ * add this preallocated descriptor to the list of free
+ * descriptors Keep the device context specific one at the
+ * beginning of the list
+ */
+ INIT_LIST_HEAD(&(pShmemDesc->list));
+ list_add_tail(&(pShmemDesc->list),
+ &(pConn->sFreeSharedMemoryList));
+ }
+
+ /* allocate memory for the vmas structure */
+ pConn->ppVmas =
+ (struct vm_area_struct **) internal_get_zeroed_page(GFP_KERNEL);
+ if (pConn->ppVmas == NULL) {
+ printk(KERN_ERR "SCXLNXConnInitSharedMemory(%p):"
+ " ppVmas - failed to get_zeroed_page\n",
+ pConn);
+ nError = -ENOMEM;
+ goto error;
+ }
+
+ return 0;
+
+error:
+ SCXLNXConnCleanupSharedMemory(pConn);
+ return nError;
+}
+
+/*----------------------------------------------------------------------------
+ * Connection operations to the Secure World
+ *----------------------------------------------------------------------------*/
+
+int SCXLNXConnCreateDeviceContext(
+ struct SCXLNX_CONNECTION *pConn)
+{
+ union SCX_COMMAND_MESSAGE sMessage;
+ union SCX_ANSWER_MESSAGE sAnswer;
+ int nError = 0;
+
+ dprintk(KERN_INFO "SCXLNXConnCreateDeviceContext(%p)\n",
+ pConn);
+
+ sMessage.sCreateDeviceContextMessage.nMessageType =
+ SCX_MESSAGE_TYPE_CREATE_DEVICE_CONTEXT;
+ sMessage.sCreateDeviceContextMessage.nMessageSize =
+ (sizeof(struct SCX_COMMAND_CREATE_DEVICE_CONTEXT)
+ - sizeof(struct SCX_COMMAND_HEADER))/sizeof(u32);
+ sMessage.sCreateDeviceContextMessage.nOperationID = (u32) &sAnswer;
+ sMessage.sCreateDeviceContextMessage.nDeviceContextID = (u32) pConn;
+
+ nError = SCXLNXCommSendReceive(
+ &pConn->pDevice->sm,
+ &sMessage,
+ &sAnswer,
+ pConn,
+ true);
+
+ if ((nError != 0) ||
+ (sAnswer.sCreateDeviceContextAnswer.nErrorCode != S_SUCCESS))
+ goto error;
+
+ /*
+ * CREATE_DEVICE_CONTEXT succeeded,
+ * store device context handler and update connection status
+ */
+ pConn->hDeviceContext =
+ sAnswer.sCreateDeviceContextAnswer.hDeviceContext;
+ spin_lock(&(pConn->stateLock));
+ pConn->nState = SCXLNX_CONN_STATE_VALID_DEVICE_CONTEXT;
+ spin_unlock(&(pConn->stateLock));
+
+ /* successful completion */
+ dprintk(KERN_INFO "SCXLNXConnCreateDeviceContext(%p):"
+ " hDeviceContext=0x%08x\n",
+ pConn,
+ sAnswer.sCreateDeviceContextAnswer.hDeviceContext);
+ return 0;
+
+error:
+ if (nError != 0) {
+ dprintk(KERN_ERR "SCXLNXConnCreateDeviceContext failed with "
+ "error %d\n", nError);
+ } else {
+ /*
+ * We sent a DeviceCreateContext. The state is now
+ * SCXLNX_CONN_STATE_CREATE_DEVICE_CONTEXT_SENT It has to be
+ * reset if we ever want to send a DeviceCreateContext again
+ */
+ spin_lock(&(pConn->stateLock));
+ pConn->nState = SCXLNX_CONN_STATE_NO_DEVICE_CONTEXT;
+ spin_unlock(&(pConn->stateLock));
+ dprintk(KERN_ERR "SCXLNXConnCreateDeviceContext failed with "
+ "nErrorCode 0x%08X\n",
+ sAnswer.sCreateDeviceContextAnswer.nErrorCode);
+ if (sAnswer.sCreateDeviceContextAnswer.nErrorCode ==
+ S_ERROR_OUT_OF_MEMORY)
+ nError = -ENOMEM;
+ else
+ nError = -EFAULT;
+ }
+
+ return nError;
+}
+
+/* Check that the current application belongs to the
+ * requested GID */
+static bool SCXLNXConnCheckGID(gid_t nRequestedGID)
+{
+ if (nRequestedGID == current_egid()) {
+ return true;
+ } else {
+ u32 nSize;
+ u32 i;
+ /* Look in the supplementary GIDs */
+ get_group_info(GROUP_INFO);
+ nSize = GROUP_INFO->ngroups;
+ for (i = 0; i < nSize; i++)
+ if (nRequestedGID == GROUP_AT(GROUP_INFO , i))
+ return true;
+ }
+ return false;
+}
+
+/*
+ * Opens a client session to the Secure World
+ */
+int SCXLNXConnOpenClientSession(
+ struct SCXLNX_CONNECTION *pConn,
+ union SCX_COMMAND_MESSAGE *pMessage,
+ union SCX_ANSWER_MESSAGE *pAnswer)
+{
+ int nError = 0;
+ struct SCXLNX_SHMEM_DESC *pShmemDesc[4] = {NULL};
+ u32 i;
+
+ dprintk(KERN_INFO "SCXLNXConnOpenClientSession(%p)\n", pConn);
+
+ /*
+ * Initialize the message size with no login data. This will be later
+ * adjusted the the cases below
+ */
+ pMessage->sOpenClientSessionMessage.nMessageSize =
+ (sizeof(struct SCX_COMMAND_OPEN_CLIENT_SESSION) - 20
+ - sizeof(struct SCX_COMMAND_HEADER))/4;
+
+ switch (pMessage->sOpenClientSessionMessage.nLoginType) {
+ case SCX_LOGIN_PUBLIC:
+ /* Nothing to do */
+ break;
+
+ case SCX_LOGIN_USER:
+ /*
+ * Send the EUID of the calling application in the login data.
+ * Update message size.
+ */
+ *(u32 *) &pMessage->sOpenClientSessionMessage.sLoginData =
+ current_euid();
+#ifndef CONFIG_ANDROID
+ pMessage->sOpenClientSessionMessage.nLoginType =
+ (u32) SCX_LOGIN_USER_LINUX_EUID;
+#else
+ pMessage->sOpenClientSessionMessage.nLoginType =
+ (u32) SCX_LOGIN_USER_ANDROID_EUID;
+#endif
+
+ /* Added one word */
+ pMessage->sOpenClientSessionMessage.nMessageSize += 1;
+ break;
+
+ case SCX_LOGIN_GROUP: {
+ /* Check requested GID */
+ gid_t nRequestedGID =
+ *(u32 *) pMessage->sOpenClientSessionMessage.sLoginData;
+
+ if (!SCXLNXConnCheckGID(nRequestedGID)) {
+ dprintk(KERN_ERR "SCXLNXConnOpenClientSession(%p) "
+ "SCX_LOGIN_GROUP: requested GID (0x%x) does "
+ "not match real eGID (0x%x)"
+ "or any of the supplementary GIDs\n",
+ pConn, nRequestedGID, current_egid());
+ nError = -EACCES;
+ goto error;
+ }
+#ifndef CONFIG_ANDROID
+ pMessage->sOpenClientSessionMessage.nLoginType =
+ SCX_LOGIN_GROUP_LINUX_GID;
+#else
+ pMessage->sOpenClientSessionMessage.nLoginType =
+ SCX_LOGIN_GROUP_ANDROID_GID;
+#endif
+
+ pMessage->sOpenClientSessionMessage.nMessageSize += 1; /* GID */
+ break;
+ }
+
+#ifndef CONFIG_ANDROID
+ case SCX_LOGIN_APPLICATION: {
+ /*
+ * Compute SHA-1 hash of the application fully-qualified path
+ * name. Truncate the hash to 16 bytes and send it as login
+ * data. Update message size.
+ */
+ u8 pSHA1Hash[SHA1_DIGEST_SIZE];
+
+ nError = SCXLNXConnHashApplicationPathAndData(pSHA1Hash,
+ NULL, 0);
+ if (nError != 0) {
+ dprintk(KERN_ERR "SCXLNXConnOpenClientSession: "
+ "error in SCXLNXConnHashApplicationPath"
+ "AndData\n");
+ goto error;
+ }
+ memcpy(&pMessage->sOpenClientSessionMessage.sLoginData,
+ pSHA1Hash, 16);
+ pMessage->sOpenClientSessionMessage.nLoginType =
+ SCX_LOGIN_APPLICATION_LINUX_PATH_SHA1_HASH;
+ /* 16 bytes */
+ pMessage->sOpenClientSessionMessage.nMessageSize += 4;
+ break;
+ }
+#else
+ case SCX_LOGIN_APPLICATION:
+ /*
+ * Send the real UID of the calling application in the login
+ * data. Update message size.
+ */
+ *(u32 *) &pMessage->sOpenClientSessionMessage.sLoginData =
+ current_uid();
+
+ pMessage->sOpenClientSessionMessage.nLoginType =
+ (u32) SCX_LOGIN_APPLICATION_ANDROID_UID;
+
+ /* Added one word */
+ pMessage->sOpenClientSessionMessage.nMessageSize += 1;
+ break;
+#endif
+
+#ifndef CONFIG_ANDROID
+ case SCX_LOGIN_APPLICATION_USER: {
+ /*
+ * Compute SHA-1 hash of the concatenation of the application
+ * fully-qualified path name and the EUID of the calling
+ * application. Truncate the hash to 16 bytes and send it as
+ * login data. Update message size.
+ */
+ u8 pSHA1Hash[SHA1_DIGEST_SIZE];
+
+ nError = SCXLNXConnHashApplicationPathAndData(pSHA1Hash,
+ (u8 *) &(current_euid()), sizeof(current_euid()));
+ if (nError != 0) {
+ dprintk(KERN_ERR "SCXLNXConnOpenClientSession: "
+ "error in SCXLNXConnHashApplicationPath"
+ "AndData\n");
+ goto error;
+ }
+ memcpy(&pMessage->sOpenClientSessionMessage.sLoginData,
+ pSHA1Hash, 16);
+ pMessage->sOpenClientSessionMessage.nLoginType =
+ SCX_LOGIN_APPLICATION_USER_LINUX_PATH_EUID_SHA1_HASH;
+
+ /* 16 bytes */
+ pMessage->sOpenClientSessionMessage.nMessageSize += 4;
+
+ break;
+ }
+#else
+ case SCX_LOGIN_APPLICATION_USER:
+ /*
+ * Send the real UID and the EUID of the calling application in
+ * the login data. Update message size.
+ */
+ *(u32 *) &pMessage->sOpenClientSessionMessage.sLoginData =
+ current_uid();
+ *(u32 *) &pMessage->sOpenClientSessionMessage.sLoginData[4] =
+ current_euid();
+
+ pMessage->sOpenClientSessionMessage.nLoginType =
+ SCX_LOGIN_APPLICATION_USER_ANDROID_UID_EUID;
+
+ /* Added two words */
+ pMessage->sOpenClientSessionMessage.nMessageSize += 2;
+ break;
+#endif
+
+#ifndef CONFIG_ANDROID
+ case SCX_LOGIN_APPLICATION_GROUP: {
+ /*
+ * Check requested GID. Compute SHA-1 hash of the concatenation
+ * of the application fully-qualified path name and the
+ * requested GID. Update message size
+ */
+ gid_t nRequestedGID;
+ u8 pSHA1Hash[SHA1_DIGEST_SIZE];
+
+ nRequestedGID = *(u32 *) &pMessage->sOpenClientSessionMessage.
+ sLoginData;
+
+ if (!SCXLNXConnCheckGID(nRequestedGID)) {
+ dprintk(KERN_ERR "SCXLNXConnOpenClientSession(%p) "
+ "SCX_LOGIN_APPLICATION_GROUP: requested GID (0x%x) "
+ "does not match real eGID (0x%x)"
+ "or any of the supplementary GIDs\n",
+ pConn, nRequestedGID, current_egid());
+ nError = -EACCES;
+ goto error;
+ }
+
+ nError = SCXLNXConnHashApplicationPathAndData(pSHA1Hash,
+ &nRequestedGID, sizeof(u32));
+ if (nError != 0) {
+ dprintk(KERN_ERR "SCXLNXConnOpenClientSession: "
+ "error in SCXLNXConnHashApplicationPath"
+ "AndData\n");
+ goto error;
+ }
+
+ memcpy(&pMessage->sOpenClientSessionMessage.sLoginData,
+ pSHA1Hash, 16);
+ pMessage->sOpenClientSessionMessage.nLoginType =
+ SCX_LOGIN_APPLICATION_GROUP_LINUX_PATH_GID_SHA1_HASH;
+
+ /* 16 bytes */
+ pMessage->sOpenClientSessionMessage.nMessageSize += 4;
+ break;
+ }
+#else
+ case SCX_LOGIN_APPLICATION_GROUP: {
+ /*
+ * Check requested GID. Send the real UID and the requested GID
+ * in the login data. Update message size.
+ */
+ gid_t nRequestedGID;
+
+ nRequestedGID = *(u32 *) &pMessage->sOpenClientSessionMessage.
+ sLoginData;
+
+ if (!SCXLNXConnCheckGID(nRequestedGID)) {
+ dprintk(KERN_ERR "SCXLNXConnOpenClientSession(%p) "
+ "SCX_LOGIN_APPLICATION_GROUP: requested GID (0x%x) "
+ "does not match real eGID (0x%x)"
+ "or any of the supplementary GIDs\n",
+ pConn, nRequestedGID, current_egid());
+ nError = -EACCES;
+ goto error;
+ }
+
+ *(u32 *) &pMessage->sOpenClientSessionMessage.sLoginData =
+ current_uid();
+ *(u32 *) &pMessage->sOpenClientSessionMessage.sLoginData[4] =
+ nRequestedGID;
+
+ pMessage->sOpenClientSessionMessage.nLoginType =
+ SCX_LOGIN_APPLICATION_GROUP_ANDROID_UID_GID;
+
+ /* Added two words */
+ pMessage->sOpenClientSessionMessage.nMessageSize += 2;
+
+ break;
+ }
+#endif
+
+ case SCX_LOGIN_PRIVILEGED:
+ /*
+ * Check that calling application either hash EUID=0 or has
+ * EGID=0
+ */
+ if (current_euid() != 0 && current_egid() != 0) {
+ dprintk(KERN_ERR "SCXLNXConnOpenClientSession: "
+ " user %d, group %d not allowed to open "
+ "session with SCX_LOGIN_PRIVILEGED\n",
+ current_euid(), current_egid());
+ nError = -EACCES;
+ goto error;
+ }
+ pMessage->sOpenClientSessionMessage.nLoginType =
+ SCX_LOGIN_PRIVILEGED;
+ break;
+
+ case SCX_LOGIN_AUTHENTICATION: {
+ /*
+ * Compute SHA-1 hash of the application binary
+ * Send this hash as the login data (20 bytes)
+ */
+
+ u8 *pHash;
+ pHash = &(pMessage->sOpenClientSessionMessage.sLoginData[0]);
+
+ nError = SCXLNXConnGetCurrentProcessHash(pHash);
+ if (nError != 0) {
+ dprintk(KERN_ERR "SCXLNXConnOpenClientSession: "
+ "error in SCXLNXConnGetCurrentProcessHash\n");
+ goto error;
+ }
+ pMessage->sOpenClientSessionMessage.nLoginType =
+ SCX_LOGIN_AUTHENTICATION_BINARY_SHA1_HASH;
+
+ /* 20 bytes */
+ pMessage->sOpenClientSessionMessage.nMessageSize += 5;
+ break;
+ }
+
+ default:
+ dprintk(KERN_ERR "SCXLNXConnOpenClientSession: "
+ "unknown nLoginType(%08X)\n",
+ pMessage->sOpenClientSessionMessage.nLoginType);
+ nError = -EOPNOTSUPP;
+ goto error;
+ }
+
+ /* Map the temporary memory references */
+ for (i = 0; i < 4; i++) {
+ int nParamType;
+ nParamType = SCX_GET_PARAM_TYPE(
+ pMessage->sOpenClientSessionMessage.nParamTypes, i);
+ if ((nParamType & (SCX_PARAM_TYPE_MEMREF_FLAG |
+ SCX_PARAM_TYPE_REGISTERED_MEMREF_FLAG))
+ == SCX_PARAM_TYPE_MEMREF_FLAG) {
+ /* Map temp mem ref */
+ nError = SCXLNXConnMapTempShMem(pConn,
+ &pMessage->sOpenClientSessionMessage.
+ sParams[i].sTempMemref,
+ nParamType,
+ &pShmemDesc[i]);
+ if (nError != 0) {
+ dprintk(KERN_ERR "SCXLNXConnOpenClientSession: "
+ "unable to map temporary memory block "
+ "(%08X)\n", nError);
+ goto error;
+ }
+ }
+ }
+
+ /* Fill the handle of the Device Context */
+ pMessage->sOpenClientSessionMessage.hDeviceContext =
+ pConn->hDeviceContext;
+
+ nError = SCXLNXCommSendReceive(
+ &pConn->pDevice->sm,
+ pMessage,
+ pAnswer,
+ pConn,
+ true);
+
+error:
+ /* Unmap the temporary memory references */
+ for (i = 0; i < 4; i++)
+ if (pShmemDesc[i] != NULL)
+ SCXLNXConnUnmapShmem(pConn, pShmemDesc[i], 0);
+
+ if (nError != 0)
+ dprintk(KERN_ERR "SCXLNXConnOpenClientSession returns %d\n",
+ nError);
+ else
+ dprintk(KERN_ERR "SCXLNXConnOpenClientSession returns "
+ "nErrorCode 0x%08X\n",
+ pAnswer->sOpenClientSessionAnswer.nErrorCode);
+
+ return nError;
+}
+
+
+/*
+ * Closes a client session from the Secure World
+ */
+int SCXLNXConnCloseClientSession(
+ struct SCXLNX_CONNECTION *pConn,
+ union SCX_COMMAND_MESSAGE *pMessage,
+ union SCX_ANSWER_MESSAGE *pAnswer)
+{
+ int nError = 0;
+
+ dprintk(KERN_DEBUG "SCXLNXConnCloseClientSession(%p)\n", pConn);
+
+ pMessage->sCloseClientSessionMessage.nMessageSize =
+ (sizeof(struct SCX_COMMAND_CLOSE_CLIENT_SESSION) -
+ sizeof(struct SCX_COMMAND_HEADER)) / 4;
+ pMessage->sCloseClientSessionMessage.hDeviceContext =
+ pConn->hDeviceContext;
+
+ nError = SCXLNXCommSendReceive(
+ &pConn->pDevice->sm,
+ pMessage,
+ pAnswer,
+ pConn,
+ true);
+
+ if (nError != 0)
+ dprintk(KERN_ERR "SCXLNXConnCloseClientSession returns %d\n",
+ nError);
+ else
+ dprintk(KERN_ERR "SCXLNXConnCloseClientSession returns "
+ "nError 0x%08X\n",
+ pAnswer->sCloseClientSessionAnswer.nErrorCode);
+
+ return nError;
+}
+
+
+/*
+ * Registers a shared memory to the Secure World
+ */
+int SCXLNXConnRegisterSharedMemory(
+ struct SCXLNX_CONNECTION *pConn,
+ union SCX_COMMAND_MESSAGE *pMessage,
+ union SCX_ANSWER_MESSAGE *pAnswer)
+{
+ int nError = 0;
+ struct SCXLNX_SHMEM_DESC *pShmemDesc = NULL;
+
+ dprintk(KERN_INFO "SCXLNXConnRegisterSharedMemory(%p) "
+ "%p[0x%08X][0x%08x]\n",
+ pConn,
+ (void *) pMessage->sRegisterSharedMemoryMessage.
+ nSharedMemDescriptors[0],
+ pMessage->sRegisterSharedMemoryMessage.nSharedMemSize,
+ (u32)pMessage->sRegisterSharedMemoryMessage.nMemoryFlags);
+
+ nError = SCXLNXConnValidateSharedMemoryBlockAndFlags(
+ (void *) pMessage->sRegisterSharedMemoryMessage.
+ nSharedMemDescriptors[0],
+ pMessage->sRegisterSharedMemoryMessage.nSharedMemSize,
+ (u32)pMessage->sRegisterSharedMemoryMessage.nMemoryFlags);
+ if (nError != 0)
+ goto error;
+
+ /* Initialize nMessageSize with no descriptors */
+ pMessage->sRegisterSharedMemoryMessage.nMessageSize
+ = (sizeof(struct SCX_COMMAND_REGISTER_SHARED_MEMORY) -
+ sizeof(struct SCX_COMMAND_HEADER)) / 4;
+
+ /* Map the shmem block and update the message */
+ if (pMessage->sRegisterSharedMemoryMessage.nSharedMemSize == 0) {
+ /* Empty shared mem */
+ pMessage->sRegisterSharedMemoryMessage.nSharedMemStartOffset =
+ pMessage->sRegisterSharedMemoryMessage.
+ nSharedMemDescriptors[0];
+ } else {
+ u32 nDescriptorCount;
+ nError = SCXLNXConnMapShmem(
+ pConn,
+ pMessage->sRegisterSharedMemoryMessage.
+ nSharedMemDescriptors[0],
+ pMessage->sRegisterSharedMemoryMessage.nMemoryFlags,
+ true,
+ pMessage->sRegisterSharedMemoryMessage.
+ nSharedMemDescriptors,
+ &(pMessage->sRegisterSharedMemoryMessage.
+ nSharedMemStartOffset),
+ &(pMessage->sRegisterSharedMemoryMessage.
+ nSharedMemSize),
+ &pShmemDesc,
+ &nDescriptorCount);
+ if (nError != 0) {
+ dprintk(KERN_ERR "SCXLNXConnRegisterSharedMemory: "
+ "unable to map shared memory block\n");
+ goto error;
+ }
+ pMessage->sRegisterSharedMemoryMessage.nMessageSize +=
+ nDescriptorCount;
+ }
+
+ /*
+ * write the correct device context handle and the address of the shared
+ * memory descriptor in the message
+ */
+ pMessage->sRegisterSharedMemoryMessage.hDeviceContext =
+ pConn->hDeviceContext;
+ pMessage->sRegisterSharedMemoryMessage.nBlockID = (u32) pShmemDesc;
+
+ /* Send the updated message */
+ nError = SCXLNXCommSendReceive(
+ &pConn->pDevice->sm,
+ pMessage,
+ pAnswer,
+ pConn,
+ true);
+
+ if ((nError != 0) ||
+ (pAnswer->sRegisterSharedMemoryAnswer.nErrorCode
+ != S_SUCCESS)) {
+ dprintk(KERN_ERR "SCXLNXConnRegisterSharedMemory: "
+ "operation failed. Unmap block\n");
+ goto error;
+ }
+
+ /* Saves the block handle returned by the secure world */
+ if (pShmemDesc != NULL)
+ pShmemDesc->hIdentifier =
+ pAnswer->sRegisterSharedMemoryAnswer.hBlock;
+
+ /* successful completion */
+ dprintk(KERN_INFO "SCXLNXConnRegisterSharedMemory(%p):"
+ " nBlockID=0x%08x hBlock=0x%08x\n",
+ pConn, pMessage->sRegisterSharedMemoryMessage.nBlockID,
+ pAnswer->sRegisterSharedMemoryAnswer.hBlock);
+ return 0;
+
+ /* error completion */
+error:
+ SCXLNXConnUnmapShmem(
+ pConn,
+ pShmemDesc,
+ 0);
+
+ if (nError != 0)
+ dprintk(KERN_ERR "SCXLNXConnRegisterSharedMemory returns %d\n",
+ nError);
+ else
+ dprintk(KERN_ERR "SCXLNXConnRegisterSharedMemory returns "
+ "nErrorCode 0x%08X\n",
+ pAnswer->sRegisterSharedMemoryAnswer.nErrorCode);
+
+ return nError;
+}
+
+
+/*
+ * Releases a shared memory from the Secure World
+ */
+int SCXLNXConnReleaseSharedMemory(
+ struct SCXLNX_CONNECTION *pConn,
+ union SCX_COMMAND_MESSAGE *pMessage,
+ union SCX_ANSWER_MESSAGE *pAnswer)
+{
+ int nError = 0;
+
+ dprintk(KERN_DEBUG "SCXLNXConnReleaseSharedMemory(%p)\n", pConn);
+
+ pMessage->sReleaseSharedMemoryMessage.nMessageSize =
+ (sizeof(struct SCX_COMMAND_RELEASE_SHARED_MEMORY) -
+ sizeof(struct SCX_COMMAND_HEADER)) / 4;
+ pMessage->sReleaseSharedMemoryMessage.hDeviceContext =
+ pConn->hDeviceContext;
+
+ nError = SCXLNXCommSendReceive(
+ &pConn->pDevice->sm,
+ pMessage,
+ pAnswer,
+ pConn,
+ true);
+
+ if ((nError != 0) ||
+ (pAnswer->sReleaseSharedMemoryAnswer.nErrorCode != S_SUCCESS))
+ goto error;
+
+ /* Use nBlockID to get back the pointer to pShmemDesc */
+ SCXLNXConnUnmapShmem(
+ pConn,
+ (struct SCXLNX_SHMEM_DESC *)
+ pAnswer->sReleaseSharedMemoryAnswer.nBlockID,
+ 0);
+
+ /* successful completion */
+ dprintk(KERN_INFO "SCXLNXConnReleaseSharedMemory(%p):"
+ " nBlockID=0x%08x hBlock=0x%08x\n",
+ pConn, pAnswer->sReleaseSharedMemoryAnswer.nBlockID,
+ pMessage->sReleaseSharedMemoryMessage.hBlock);
+ return 0;
+
+
+error:
+ if (nError != 0)
+ dprintk(KERN_ERR "SCXLNXConnReleaseSharedMemory returns %d\n",
+ nError);
+ else
+ dprintk(KERN_ERR "SCXLNXConnReleaseSharedMemory returns "
+ "nChannelStatus 0x%08X\n",
+ pAnswer->sReleaseSharedMemoryAnswer.nErrorCode);
+
+ return nError;
+
+}
+
+
+/*
+ * Invokes a client command to the Secure World
+ */
+int SCXLNXConnInvokeClientCommand(
+ struct SCXLNX_CONNECTION *pConn,
+ union SCX_COMMAND_MESSAGE *pMessage,
+ union SCX_ANSWER_MESSAGE *pAnswer)
+{
+ int nError = 0;
+ struct SCXLNX_SHMEM_DESC *pShmemDesc[4] = {NULL};
+ int i;
+
+ dprintk(KERN_INFO "SCXLNXConnInvokeClientCommand(%p)\n", pConn);
+
+ pMessage->sReleaseSharedMemoryMessage.nMessageSize =
+ (sizeof(struct SCX_COMMAND_INVOKE_CLIENT_COMMAND) -
+ sizeof(struct SCX_COMMAND_HEADER)) / 4;
+
+#ifdef CONFIG_TF_ZEBRA
+ nError = SCXPublicCryptoTryShortcutedUpdate(pConn,
+ (struct SCX_COMMAND_INVOKE_CLIENT_COMMAND *) pMessage,
+ (struct SCX_ANSWER_INVOKE_CLIENT_COMMAND *) pAnswer);
+ if (nError == 0)
+ return nError;
+#endif
+
+ /* Map the tmprefs */
+ for (i = 0; i < 4; i++) {
+ int nParamType = SCX_GET_PARAM_TYPE(
+ pMessage->sInvokeClientCommandMessage.nParamTypes, i);
+
+ if ((nParamType & (SCX_PARAM_TYPE_MEMREF_FLAG |
+ SCX_PARAM_TYPE_REGISTERED_MEMREF_FLAG))
+ == SCX_PARAM_TYPE_MEMREF_FLAG) {
+ /* A temporary memref: map it */
+ nError = SCXLNXConnMapTempShMem(pConn,
+ &pMessage->sInvokeClientCommandMessage.
+ sParams[i].sTempMemref,
+ nParamType, &pShmemDesc[i]);
+ if (nError != 0) {
+ dprintk(KERN_ERR
+ "SCXLNXConnInvokeClientCommand: "
+ "unable to map temporary memory "
+ "block\n (%08X)", nError);
+ goto error;
+ }
+ }
+ }
+
+ pMessage->sInvokeClientCommandMessage.hDeviceContext =
+ pConn->hDeviceContext;
+
+ nError = SCXLNXCommSendReceive(&pConn->pDevice->sm, pMessage,
+ pAnswer, pConn, true);
+
+error:
+ /* Unmap de temp mem refs */
+ for (i = 0; i < 4; i++) {
+ if (pShmemDesc[i] != NULL) {
+ dprintk(KERN_INFO "SCXLNXConnInvokeClientCommand: "
+ "UnMapTempMemRef %d\n ", i);
+
+ SCXLNXConnUnmapShmem(pConn, pShmemDesc[i], 0);
+ }
+ }
+
+ if (nError != 0)
+ dprintk(KERN_ERR "SCXLNXConnInvokeClientCommand returns %d\n",
+ nError);
+ else
+ dprintk(KERN_ERR "SCXLNXConnInvokeClientCommand returns "
+ "nErrorCode 0x%08X\n",
+ pAnswer->sInvokeClientCommandAnswer.nErrorCode);
+
+ return nError;
+}
+
+
+/*
+ * Cancels a client command from the Secure World
+ */
+int SCXLNXConnCancelClientCommand(
+ struct SCXLNX_CONNECTION *pConn,
+ union SCX_COMMAND_MESSAGE *pMessage,
+ union SCX_ANSWER_MESSAGE *pAnswer)
+{
+ int nError = 0;
+
+ dprintk(KERN_DEBUG "SCXLNXConnCancelClientCommand(%p)\n", pConn);
+
+ pMessage->sCancelClientOperationMessage.hDeviceContext =
+ pConn->hDeviceContext;
+ pMessage->sCancelClientOperationMessage.nMessageSize =
+ (sizeof(struct SCX_COMMAND_CANCEL_CLIENT_OPERATION) -
+ sizeof(struct SCX_COMMAND_HEADER)) / 4;
+
+ nError = SCXLNXCommSendReceive(
+ &pConn->pDevice->sm,
+ pMessage,
+ pAnswer,
+ pConn,
+ true);
+
+ if ((nError != 0) ||
+ (pAnswer->sCancelClientOperationAnswer.nErrorCode != S_SUCCESS))
+ goto error;
+
+
+ /* successful completion */
+ return 0;
+
+error:
+ if (nError != 0)
+ dprintk(KERN_ERR "SCXLNXConnCancelClientCommand returns %d\n",
+ nError);
+ else
+ dprintk(KERN_ERR "SCXLNXConnCancelClientCommand returns "
+ "nChannelStatus 0x%08X\n",
+ pAnswer->sCancelClientOperationAnswer.nErrorCode);
+
+ return nError;
+}
+
+
+
+/*
+ * Destroys a device context from the Secure World
+ */
+int SCXLNXConnDestroyDeviceContext(
+ struct SCXLNX_CONNECTION *pConn)
+{
+ int nError;
+ /*
+ * AFY: better use the specialized SCX_COMMAND_DESTROY_DEVICE_CONTEXT
+ * structure: this will save stack
+ */
+ union SCX_COMMAND_MESSAGE sMessage;
+ union SCX_ANSWER_MESSAGE sAnswer;
+
+ dprintk(KERN_INFO "SCXLNXConnDestroyDeviceContext(%p)\n", pConn);
+
+ BUG_ON(pConn == NULL);
+
+ sMessage.sHeader.nMessageType = SCX_MESSAGE_TYPE_DESTROY_DEVICE_CONTEXT;
+ sMessage.sHeader.nMessageSize =
+ (sizeof(struct SCX_COMMAND_DESTROY_DEVICE_CONTEXT) -
+ sizeof(struct SCX_COMMAND_HEADER))/sizeof(u32);
+
+ /*
+ * fill in the device context handler
+ * it is guarantied that the first shared memory descriptor describes
+ * the device context
+ */
+ sMessage.sDestroyDeviceContextMessage.hDeviceContext =
+ pConn->hDeviceContext;
+
+ nError = SCXLNXCommSendReceive(
+ &pConn->pDevice->sm,
+ &sMessage,
+ &sAnswer,
+ pConn,
+ false);
+
+ if ((nError != 0) ||
+ (sAnswer.sDestroyDeviceContextAnswer.nErrorCode != S_SUCCESS))
+ goto error;
+
+ spin_lock(&(pConn->stateLock));
+ pConn->nState = SCXLNX_CONN_STATE_NO_DEVICE_CONTEXT;
+ spin_unlock(&(pConn->stateLock));
+
+ /* successful completion */
+ dprintk(KERN_INFO "SCXLNXConnDestroyDeviceContext(%p)\n",
+ pConn);
+ return 0;
+
+error:
+ if (nError != 0) {
+ dprintk(KERN_ERR "SCXLNXConnDestroyDeviceContext failed with "
+ "error %d\n", nError);
+ } else {
+ dprintk(KERN_ERR "SCXLNXConnDestroyDeviceContext failed with "
+ "nErrorCode 0x%08X\n",
+ sAnswer.sDestroyDeviceContextAnswer.nErrorCode);
+ if (sAnswer.sDestroyDeviceContextAnswer.nErrorCode ==
+ S_ERROR_OUT_OF_MEMORY)
+ nError = -ENOMEM;
+ else
+ nError = -EFAULT;
+ }
+
+ return nError;
+}
+
+
+/*----------------------------------------------------------------------------
+ * Connection initialization and cleanup operations
+ *----------------------------------------------------------------------------*/
+
+/*
+ * Opens a connection to the specified device.
+ *
+ * The placeholder referenced by ppConn is set to the address of the
+ * new connection; it is set to NULL upon failure.
+ *
+ * Returns zero upon successful completion, or an appropriate error code upon
+ * failure.
+ */
+int SCXLNXConnOpen(struct SCXLNX_DEVICE *pDevice,
+ struct file *file,
+ struct SCXLNX_CONNECTION **ppConn)
+{
+ int nError;
+ struct SCXLNX_CONNECTION *pConn = NULL;
+
+ dprintk(KERN_INFO "SCXLNXConnOpen(%p, %p)\n", file, ppConn);
+
+ /*
+ * Allocate and initialize the connection.
+ * kmalloc only allocates sizeof(*pConn) virtual memory
+ */
+ pConn = (struct SCXLNX_CONNECTION *) internal_kmalloc(sizeof(*pConn),
+ GFP_KERNEL);
+ if (pConn == NULL) {
+ printk(KERN_ERR "SCXLNXConnOpen(): "
+ "Out of memory for connection!\n");
+ nError = -ENOMEM;
+ goto error;
+ }
+
+ memset(pConn, 0, sizeof(*pConn));
+
+ INIT_LIST_HEAD(&(pConn->list));
+ pConn->nState = SCXLNX_CONN_STATE_NO_DEVICE_CONTEXT;
+ pConn->pDevice = pDevice;
+ spin_lock_init(&(pConn->stateLock));
+ atomic_set(&(pConn->nPendingOpCounter), 0);
+
+ /*
+ * Initialize the shared memory
+ */
+ nError = SCXLNXConnInitSharedMemory(pConn);
+ if (nError != 0)
+ goto error;
+
+#ifdef CONFIG_TF_ZEBRA
+ /*
+ * Initialize CUS specifics
+ */
+ SCXPublicCryptoInitDeviceContext(pConn);
+#endif
+
+ /*
+ * Successful completion.
+ */
+
+ *ppConn = pConn;
+
+ dprintk(KERN_INFO "SCXLNXConnOpen(): Success (pConn=%p)\n", pConn);
+ return 0;
+
+ /*
+ * Error handling.
+ */
+
+error:
+ dprintk(KERN_ERR "SCXLNXConnOpen(): Failure (error %d)\n", nError);
+ /* Deallocate the descriptor pages if necessary */
+ internal_kfree(pConn);
+ *ppConn = NULL;
+ return nError;
+}
+
+
+/*
+ * Closes the specified connection.
+ *
+ * Upon return, the connection referenced by pConn has been destroyed and cannot
+ * be used anymore.
+ *
+ * This function does nothing if pConn is set to NULL.
+ */
+void SCXLNXConnClose(struct SCXLNX_CONNECTION *pConn)
+{
+ int nError;
+ enum SCXLNX_CONN_STATE nState;
+
+ dprintk(KERN_DEBUG "SCXLNXConnClose(%p)\n", pConn);
+
+ if (pConn == NULL)
+ return;
+
+ /*
+ * Assumption: Linux guarantees that no other operation is in progress
+ * and that no other operation will be started when close is called
+ */
+ BUG_ON(atomic_read(&(pConn->nPendingOpCounter)) != 0);
+
+ /*
+ * Exchange a Destroy Device Context message if needed.
+ */
+ spin_lock(&(pConn->stateLock));
+ nState = pConn->nState;
+ spin_unlock(&(pConn->stateLock));
+ if (nState == SCXLNX_CONN_STATE_VALID_DEVICE_CONTEXT) {
+ /*
+ * A DestroyDeviceContext operation was not performed. Do it
+ * now.
+ */
+ nError = SCXLNXConnDestroyDeviceContext(pConn);
+ if (nError != 0)
+ /* avoid cleanup if destroy device context fails */
+ goto error;
+ }
+
+ /*
+ * Clean up the shared memory
+ */
+ SCXLNXConnCleanupSharedMemory(pConn);
+
+ internal_kfree(pConn);
+
+ return;
+
+error:
+ dprintk(KERN_DEBUG "SCXLNXConnClose(%p) failed with error code %d\n",
+ pConn, nError);
+}
+