/*
 * Copyright (c) Qualcomm Technologies, Inc.
 * All Rights Reserved.
 * Confidential and Proprietary - Qualcomm Technologies, Inc.
 */

#include "IMemRegion.h"
#include "MemObj.h"
#include "TZCom.h"
#include "alog.h"
#include "cdefs.h"
#include "map_trusted_application.h"
#include "object.h"
#include "qtee_init.h"  // QTEE off-target environment initialization
#include "smci_example_app.h"
#include "stringl.h"

#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>

/* Similar to Android LOG_TAG, define a tag that appears when logging from this
 * application. */
static char const LOG_TAG[] = "SMCInvoke_Example_CA";

/* Define PATH_MAX for TAs if not already defined. */
#if !defined(PATH_MAX)
#define PATH_MAX 4096
#endif

/* This function builds the path to our TA using strlcpy(), using the TA name
 * EXAMPLE_TA_NAME and path TA_PATH_DEFINE - these are defined in the TA and CA SConscripts.
 *
 * Returns the path on success and NULL on failure.
 */
static char const *create_image_path(const char *testTAName)
{
  static char imageName[PATH_MAX];

  if (strlcpy(imageName, TA_PATH_DEFINE, sizeof(imageName)) >= sizeof(imageName)) {
    goto bail;
  }

  if (strlcat(imageName, testTAName, sizeof(imageName)) >= sizeof(imageName)) {
    goto bail;
  }

  if (strlcat(imageName, ".so", sizeof(imageName)) >= sizeof(imageName)) {
    goto bail;
  }

  return imageName;

bail:
  ALOGE("TA image path does not fit in buffer with size: %zu!", sizeof(imageName));
  return NULL;
}

int main(void)
{
  int ret = Object_ERROR;         // Return value for qtee_emu calls
  int exampleRet = Object_ERROR;  // Return value for these examples

  Object serviceTAMemObj = Object_NULL;  // We'll use this with IAppLoader_loadFromRegion later

  const char *imageName = NULL;
  ta_image_data img_data[2] = {{0}, {0}};

  /*
   * Initialize QTEEEmu.
   *
   * A null pointer can be passed to use the default settings. Alternatively, a path to a JSON file
   * specifying desired settings can be provided. See 'qtee_init.h'.
   */
  ret = qtee_emu_init(NULL);
  if (ret) {
    ALOGE("QTEE Emu initialization failed with error: %d!", ret);
    return ret;
  }

  ALOGD("QTEE Emu initialized.");

  /* Load TAs into memory for loading via IAppLoader.
   * Before we can load a TA, we have to load it into a buffer. We need to load our main TA,
   * and the TA that hosts our IAdder service. */

  ALOGD("Creating Example TA buffer.");

  /* TA_PATH_DEFINE is defined in the offtarget client app builder. */
  imageName = create_image_path(EXAMPLE_TA_NAME);
  if (!imageName) {
    ALOGE("Failed to create TA image path!");
    ret = Object_ERROR;
    goto main_cleanup;
  }

  /* Here we call map_trusted_application, which maps the TA specified in the path we created into a buffer. */
  ret = map_trusted_application(imageName, &img_data[EXAMPLE_TA]);

  /* Cleanup if mapping the TA failed. */
  if (ret) {
    ALOGE("Error mapping example TA!");
    memset(&img_data[EXAMPLE_TA], 0, sizeof(img_data[EXAMPLE_TA]));
    ret = Object_ERROR;
    goto main_cleanup;
  }

  /* As well as loading TAs directly from a buffer, we can also load them from a memory region. */
  ALOGD("Creating Example Service TA memory object.");
  imageName = create_image_path(EXAMPLE_SERVICE_TA_NAME);
  if (!imageName) {
    ALOGE("Failed to create TA image path!");
    ret = Object_ERROR;
    goto main_cleanup;
  }

  ret = map_trusted_application(imageName, &img_data[EXAMPLE_SERVICE_TA]);

  if (ret) {
    ALOGE("Error mapping example service TA!");
    memset(&img_data[EXAMPLE_SERVICE_TA], 0, sizeof(img_data[EXAMPLE_SERVICE_TA]));
    ret = Object_ERROR;
    goto main_cleanup;
  }

  /* Create a memory object containing the service TA. */
  serviceTAMemObj = MemObj_new(img_data[EXAMPLE_SERVICE_TA].buffer,
                               img_data[EXAMPLE_SERVICE_TA].buffer_size,
                               IMemRegion_PERM_R, true);

  if (Object_isNull(serviceTAMemObj)) {
    ALOGE("MemObj allocation failed!");
    ret = Object_ERROR_MEM;
    goto main_cleanup;
  }

  /* Run the SMCInvoke TA example, and then deinit the SDK after it returns.
   * We pass the example TA's buffer and size inside img_data, but we do not need to do the
   * same for the MemObj containing our example service TA. */
  exampleRet = run_smcinvoke_ta_example(&img_data[EXAMPLE_TA], serviceTAMemObj);

  /* Now that we've given the serviceTAMemObj to run_smcinvoke_ta_example, we do not need it
   * anymore and can free it inside the called function. Here we just need to set the reference
   * to null. */
  serviceTAMemObj = Object_NULL;

main_cleanup:

  if (exampleRet) {
    ALOGE("Errors were encountered during execution: %d!", exampleRet);
  } else {
    ALOGD("CA executed successfully!");
  }

  ret = qtee_emu_deinit();
  if (ret) {
    ALOGE("Error occurred during QTEE Emu deinit: %d!", ret);
  }
  /* Unmap our TAs */

  for (size_t i = 0; i < C_LENGTHOF(img_data); i++) {
      unmap_trusted_application(&img_data[i]);
  }

  return (ret || exampleRet);
}

