#include "memory.h"

#include "driver_log.h"
#include "dr_qsee.h"
#include "kaslr.h"

#include "qsee_oem_buffer.h"
#include "qsee_services.h"
#include "qsee_core.h"

#if defined(CONFIG_KASLR_V2)
# define KASLR_OEMBUF_OFFSET    0x0
#else
# define KASLR_OEMBUF_OFFSET    sizeof(dmv_bl_status_t)
#endif

/**
 * Legacy Samsung bootloader structures
 */
typedef struct {
  uint16_t magic_str;
  uint16_t used_size;
} secure_param_header_t;

typedef struct dmv_bl_status_s {
  secure_param_header_t header;
  uint32_t odin_flag;
  uint32_t boot_mode;
  uint32_t security_mode;
  uint32_t mode_num;
} __attribute__ ((packed)) dmv_bl_status_t;

typedef struct kaslr_bl_status_s {
  secure_param_header_t header;
  Kaslr kaslr;
} __attribute__ ((packed)) kaslr_bl_status_t;


PaTzResult PlatformVirtToPhys64(const void *virt, PhysicalAddress *phys) {
  if (!phys) {
    LOG_E("Invalid arguments\n");
    return PA_TZ_GENERAL_ERROR;
  }

  /*
   * If provided address is shared between Client and Driver we should use
   * client virtual driver to get physical address
   */
  for (size_t i = 0; i < sizeof(g_client_buffer) / sizeof(g_client_buffer[0]); ++i) {
    const RegisteredClientBuffer * const buffer = &g_client_buffer[i];
    if (virt == (void *)buffer->driver_addr) {
      *phys = (uintptr_t)buffer->client_addr;
      return PA_TZ_SUCCESS;
    }
  }

  *phys = (uintptr_t)virt;

  return PA_TZ_SUCCESS;
}

/**
 * For QSEE we do not map trustlet (client) memory directly. It was done on
 * client side during command serialization. This code just check existing
 * memory translations and return driver address.
 */
PaTzResult PlatformSysMapTrustlet(const void *virt_trustlet, size_t size,
                                  MemoryAccessType type, void **virt_driver) {
  if (!virt_trustlet || !virt_driver) {
    LOG_E("Invalid arguments.\n");
    LOG_D("virt_trustlet: 0x%x, virt_driver: 0x%x.\n", virt_trustlet, virt_driver);
    return PA_TZ_GENERAL_ERROR;
  }

  for (size_t i = 0; i < sizeof(g_client_buffer) / sizeof(g_client_buffer[0]); ++i) {
    const RegisteredClientBuffer * const buffer = &g_client_buffer[i];
    if (virt_trustlet == (void *)buffer->client_addr && size <= buffer->size) {
      *virt_driver = buffer->driver_addr;
      return PA_TZ_SUCCESS;
    }
  }

  return PA_TZ_GENERAL_ERROR;
}

PaTzResult PlatformSysUnmapTrustlet(const void *virt_driver, size_t size) {
  // Will be unmap automatically
  return PA_TZ_SUCCESS;
}

PaTzResult PlatformReadOemBuffer(uint32_t offset, size_t size, void *out) {
  int ret = qsee_read_oem_buffer(offset, out, size);

  return (ret == QSEE_OEM_BUFFER_SUCCESS ? PA_TZ_SUCCESS : PA_TZ_GENERAL_ERROR);
}

PaTzResult KernelGetKaslrOffset(uint32_t *kaslr_offset) {
  if (!kaslr_offset) {
    LOG_E("Invalid arguments.\n");
    return PA_TZ_GENERAL_ERROR;
  }

  kaslr_bl_status_t bl_secure_info = {0};

  PaTzResult result = PlatformReadOemBuffer(KASLR_OEMBUF_OFFSET,
      sizeof(kaslr_bl_status_t), &bl_secure_info);
  if (result != PA_TZ_SUCCESS) {
    LOG_E("Can not read KASLR.\n");
    return result;
  }

  if (bl_secure_info.kaslr.magic != kKaslrMagicCode) {
    LOG_I("KASLR Magic code is NOT found.\n");
    *kaslr_offset = 0;
  } else {
    LOG_I("KASLR Magic code is found.\n");
    LOG_D("KASLR Offset is 0x%08x.\n", bl_secure_info.kaslr.offset);
    *kaslr_offset = bl_secure_info.kaslr.offset;
  }

  return PA_TZ_SUCCESS;
}

PaTzResult PlatformCheckNonSecureRegion(PhysicalAddress phys_addr, uint32_t region_size) {
  char res = qsee_is_ns_range((void *)(uintptr_t)phys_addr, region_size);
  if (res) {
    return PA_TZ_SUCCESS;
  } else {
    LOG_E("qsee_is_ns_range() failed.\n");
    LOG_D("Received result: %c\n", res);
    return PA_TZ_GENERAL_ERROR;
  }
}
