/**
 * @file teec_param_utils.c
 * @brief Utility functionality for parsing/filling protocol data in NWd
 * implementation
 * @author Iaroslav Makarchuk (i.makarchuk@samsung.com)
 * @date Created Oct 3, 2016
 * @par In Samsung Ukraine R&D Center (SURC) under a contract between
 * @par LLC "Samsung Electronics Ukraine Company" (Kiev, Ukraine) and
 * @par "Samsung Elecrtronics Co", Ltd (Seoul, Republic of Korea)
 * @par Copyright: (c) Samsung Electronics Co, Ltd 2015. All rights reserved.
 *
 * This software is proprietary of Samsung Electronics.
 * No part of this software, either material or conceptual may be copied
 * or distributed, transmitted, transcribed, stored in a retrieval system
 * or translated into any human or computer language in any form by any means,
 * electronic, mechanical, manual or otherwise, or disclosed to third parties
 * without the express written permission of Samsung Electronics.
 */
#include <teec_param_utils.h>

#include <errno.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <teec_common.h>

#include <protocol.h>

void U64ToPtr(U64 *u64, void **ptr) {
  if(!u64 || !ptr) {
    return;
  }
#if ( __WORDSIZE == 64 )
  *ptr = (void *)u64->val;
#else
  *ptr =(void *)u64->low;
#endif
}
void PtrToU64(void *ptr,U64 *u64) {
  if(!u64 || !ptr) {
    return;
  }
#if ( __WORDSIZE == 64 )
  u64->val = (uint64_t)ptr;
#else
  u64->low = (uint32_t) ptr;
#endif
}


uint32_t GetParamTypeByFlags(uint32_t flags) {
  uint32_t param_type = 0;

  if ((flags & TEEC_MEM_INPUT) && (flags & TEEC_MEM_OUTPUT)) {
    param_type = TEEC_MEMREF_TEMP_INOUT;
  } else if (flags & TEEC_MEM_INPUT) {
    param_type = TEEC_MEMREF_TEMP_INPUT;
  } else if (flags & TEEC_MEM_OUTPUT) {
    param_type = TEEC_MEMREF_TEMP_OUTPUT;
  }

  return param_type;
}

inline uint32_t GetParamType(uint32_t param_index, uint32_t param_types) {
  uint32_t mask = 0xF << (param_index * 4);

  return (param_types & mask) >> (param_index * 4);
}

static inline void SetParamType(uint32_t param_index, uint32_t *param_types, uint32_t param_type) {
  uint32_t mask = param_type << (param_index * 4);
  *param_types &= ~(0xF << (param_index * 4));
  *param_types |= mask;
}

TEEC_Result FillCommandArgs(ProtocolCmd *command, TEEC_Operation *operation, void *ta_session) {
  TEEC_Result ret = TEEC_SUCCESS;
  uint32_t i = 0;
  PlatformClientImpl *client_impl = GetPlatformClientImpl();

  if (!command || !operation) {
    ret = TEEC_ERROR_BAD_PARAMETERS;
    goto exit;
  }

  for (i = 0; i < PARAMS_NUM; i++) {
    switch (GetParamType(i, operation->paramTypes)) {
      case TEEC_VALUE_INPUT:
      case TEEC_VALUE_INOUT: {
        command->params[i].value.a = operation->params[i].value.a;
        command->params[i].value.b = operation->params[i].value.b;
        break;
      }
      case TEEC_MEMREF_TEMP_INPUT:
      case TEEC_MEMREF_TEMP_INOUT: {
        if (operation->params[i].tmpref.size >
            sizeof(command->param_buffers[i])) {
          ret = TEEC_ERROR_EXCESS_DATA;
          goto exit;
        }

        command->params[i].memref.buffer = 0;
        memcpy((void *)command->param_buffers[i],
               operation->params[i].tmpref.buffer,
               operation->params[i].tmpref.size);

        command->params[i].memref.size = operation->params[i].tmpref.size;
        break;
      }
      case TEEC_MEMREF_TEMP_OUTPUT: {
        if (operation->params[i].tmpref.size >
            sizeof(command->param_buffers[i])) {
          ret = TEEC_ERROR_EXCESS_DATA;
          goto exit;
        }
        command->params[i].memref.buffer = 0;
        command->params[i].memref.size = operation->params[i].tmpref.size;
        break;
      }
      case TEEC_NONE:
      case TEEC_VALUE_OUTPUT: {
        break;
      }
      case TEEC_MEMREF_PARTIAL_OUTPUT: {
        command->params[i].memref.buffer = 0;
        if (client_impl && client_impl->MapBuffer) {
          command->params[i].memref.buffer =
            client_impl->MapBuffer(ta_session,
                                   (char *)operation->params[i].memref.parent->buffer +
                                     operation->params[i].memref.offset,
                                   operation->params[i].memref.size);
        }

        if (!command->params[i].memref.buffer) {
          if (operation->params[i].memref.size >
                sizeof(command->param_buffers[i])) {
            ret = TEEC_ERROR_EXCESS_DATA;
            goto exit;
          }
        }

        command->params[i].memref.size = operation->params[i].memref.size;
        // We need intermediate variables to avoid compilation warning when passing
        // a pointer to the field of packed structure.
        uint32_t param_types = command->param_types;
        SetParamType(i, &param_types, TEEC_MEMREF_TEMP_OUTPUT);
        command->param_types = param_types;
        break;
      }
      case TEEC_MEMREF_PARTIAL_INPUT:
      case TEEC_MEMREF_PARTIAL_INOUT: {
        /* Try to use platfrom mapping capabilities */
        command->params[i].memref.buffer = 0;
        if (client_impl && client_impl->MapBuffer) {
          command->params[i].memref.buffer =
            client_impl->MapBuffer(ta_session,
                                   (char *)operation->params[i].memref.parent->buffer +
                                     operation->params[i].memref.offset,
                                   operation->params[i].memref.size);
        }

        /* If platform cannot map, fallback to copy: */
        if (!command->params[i].memref.buffer) {
          if (operation->params[i].memref.size >
              sizeof(command->param_buffers[i])) {
            ret = TEEC_ERROR_EXCESS_DATA;
            goto exit;
          }

          memcpy((void *)command->param_buffers[i],
                 (char *)operation->params[i].memref.parent->buffer +
                   operation->params[i].memref.offset,
                 operation->params[i].memref.size);
        }

        command->params[i].memref.size = operation->params[i].memref.size;
        // We need intermediate variables to avoid compilation warning when passing
        // a pointer to the field of packed structure.
        uint32_t param_types = command->param_types;
        SetParamType(i, &param_types, TEEC_MEMREF_TEMP_INOUT);
        command->param_types = param_types;
        break;
      }
      case TEEC_MEMREF_WHOLE: {
        if (!GetParamTypeByFlags(operation->params[i].memref.parent->flags)) {
          ret = TEEC_ERROR_BAD_PARAMETERS;
          goto exit;
        }

        command->params[i].memref.buffer = 0;
        /* Try to use platfrom mapping capabilities */
        if (client_impl && client_impl->MapBuffer) {
          command->params[i].memref.buffer =
            client_impl->MapBuffer(ta_session,
                                   operation->params[i].memref.parent->buffer,
                                   operation->params[i].memref.parent->size);
        }

        /* If platform cannot map, fallback to copy: */
        if (!command->params[i].memref.buffer) {
          if (operation->params[i].memref.parent->size >
              sizeof(command->param_buffers[i])) {
            ret = TEEC_ERROR_EXCESS_DATA;
            goto exit;
          }

          memcpy((void *)command->param_buffers[i],
                 operation->params[i].memref.parent->buffer,
                 operation->params[i].memref.parent->size);
        }

        command->params[i].memref.size = operation->params[i].memref.parent->size;
        // We need intermediate variables to avoid compilation warning when passing
        // a pointer to the field of packed structure.
        uint32_t param_types = command->param_types;
        SetParamType(i, &param_types,
                     GetParamTypeByFlags(operation->params[i].memref.parent->flags));
        command->param_types = param_types;
        break;
      }
      default: {
        ret = TEEC_ERROR_BAD_PARAMETERS;
        goto exit;
      }
    }
  }

exit:
  return ret;
}

TEEC_Result FillOperationArgs(TEEC_Operation *operation, ProtocolCmd *command) {
  TEEC_Result ret = TEEC_SUCCESS;
  uint32_t i = 0;

  if (!operation || !command) {
    ret = TEEC_ERROR_BAD_PARAMETERS;
    goto exit;
  }

  for (i = 0; i < PARAMS_NUM; i++) {
    switch (GetParamType(i, operation->paramTypes)) {
      case TEEC_VALUE_OUTPUT:
      case TEEC_VALUE_INOUT: {
        operation->params[i].value.a = command->params[i].value.a;
        operation->params[i].value.b = command->params[i].value.b;
        break;
      }
      case TEEC_MEMREF_TEMP_OUTPUT:
      case TEEC_MEMREF_TEMP_INOUT: {
        if (command->params[i].memref.size <=
            operation->params[i].tmpref.size) {
          memcpy(operation->params[i].tmpref.buffer,
                 command->param_buffers[i],
                 command->params[i].memref.size);
        } else {
          ret = TEEC_ERROR_SHORT_BUFFER;
        }
        operation->params[i].tmpref.size = command->params[i].memref.size;
        break;
      }
      case TEEC_NONE:
      case TEEC_MEMREF_TEMP_INPUT:
      case TEEC_MEMREF_PARTIAL_INPUT:
      case TEEC_VALUE_INPUT: {
        break;
      }
      case TEEC_MEMREF_WHOLE: {
        /* Perform copy if there was no mapping */
        if (!command->params[i].memref.buffer &&
            operation->params[i].memref.parent->flags & TEEC_MEM_OUTPUT) {
          if (command->params[i].memref.size <=
              operation->params[i].memref.parent->size) {
            memcpy(operation->params[i].memref.parent->buffer,
                   command->param_buffers[i],
                   command->params[i].memref.size);
          } else {
            ret = TEEC_ERROR_SHORT_BUFFER;
          }
          operation->params[i].memref.size = command->params[i].memref.size;
        }
        break;
      }
      case TEEC_MEMREF_PARTIAL_OUTPUT:
      case TEEC_MEMREF_PARTIAL_INOUT: {
        /* Perform copy if there was no mapping */
        if (!command->params[i].memref.buffer) {
          if (command->params[i].memref.size <=
              operation->params[i].memref.size) {
            memcpy((char *)operation->params[i].memref.parent->buffer +
                   operation->params[i].memref.offset,
                   command->param_buffers[i],
                   command->params[i].memref.size);
          } else {
            ret = TEEC_ERROR_SHORT_BUFFER;
          }
        }
        operation->params[i].memref.size = command->params[i].memref.size;
        break;
      }
      default: {
        ret = TEEC_ERROR_BAD_PARAMETERS;
        goto exit;
      }
    }
  }

exit:
  return ret;
}

