// Copyright 2013 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Platform-specific code for QNX goes here. For the POSIX-compatible
// parts the implementation is in platform-posix.cc.

#include <backtrace.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <sys/types.h>
#include <ucontext.h>

// QNX requires memory pages to be marked as executable.
// Otherwise, the OS raises an exception when executing code in that page.
#include <errno.h>
#include <fcntl.h>      // open
#include <stdarg.h>
#include <strings.h>    // index
#include <sys/mman.h>   // mmap & munmap
#include <sys/procfs.h>
#include <sys/stat.h>   // open
#include <unistd.h>     // sysconf

#include <cmath>

#undef MAP_TYPE

#include "src/base/macros.h"
#include "src/base/platform/platform-posix-time.h"
#include "src/base/platform/platform-posix.h"
#include "src/base/platform/platform.h"

namespace v8 {
namespace base {

// 0 is never a valid thread id on Qnx since tids and pids share a
// name space and pid 0 is reserved (see man 2 kill).
static const pthread_t kNoThread = (pthread_t) 0;


#ifdef __arm__

bool OS::ArmUsingHardFloat() {
  // GCC versions 4.6 and above define __ARM_PCS or __ARM_PCS_VFP to specify
  // the Floating Point ABI used (PCS stands for Procedure Call Standard).
  // We use these as well as a couple of other defines to statically determine
  // what FP ABI used.
  // GCC versions 4.4 and below don't support hard-fp.
  // GCC versions 4.5 may support hard-fp without defining __ARM_PCS or
  // __ARM_PCS_VFP.

#define GCC_VERSION (__GNUC__ * 10000                                          \
                     + __GNUC_MINOR__ * 100                                    \
                     + __GNUC_PATCHLEVEL__)
#if GCC_VERSION >= 40600
#if defined(__ARM_PCS_VFP)
  return true;
#else
  return false;
#endif

#elif GCC_VERSION < 40500
  return false;

#else
#if defined(__ARM_PCS_VFP)
  return true;
#elif defined(__ARM_PCS) || defined(__SOFTFP__) || defined(__SOFTFP) || \
      !defined(__VFP_FP__)
  return false;
#else
#error "Your version of GCC does not report the FP ABI compiled for."          \
       "Please report it on this issue"                                        \
       "http://code.google.com/p/v8/issues/detail?id=2140"

#endif
#endif
#undef GCC_VERSION
}

#endif  // __arm__

TimezoneCache* OS::CreateTimezoneCache() {
  return new PosixDefaultTimezoneCache();
}

std::vector<OS::SharedLibraryAddress> OS::GetSharedLibraryAddresses() {
  std::vector<SharedLibraryAddress> result;
  procfs_mapinfo *mapinfos = nullptr, *mapinfo;
  int proc_fd, num, i;

  struct {
    procfs_debuginfo info;
    char buff[PATH_MAX];
  } map;

  char buf[PATH_MAX + 1];
  snprintf(buf, PATH_MAX + 1, "/proc/%d/as", getpid());

  if ((proc_fd = open(buf, O_RDONLY)) == -1) {
    close(proc_fd);
    return result;
  }

  /* Get the number of map entries.  */
  if (devctl(proc_fd, DCMD_PROC_MAPINFO, nullptr, 0, &num) != EOK) {
    close(proc_fd);
    return result;
  }

  mapinfos =
      reinterpret_cast<procfs_mapinfo*>(malloc(num * sizeof(procfs_mapinfo)));
  if (mapinfos == nullptr) {
    close(proc_fd);
    return result;
  }

  /* Fill the map entries.  */
  if (devctl(proc_fd, DCMD_PROC_PAGEDATA, mapinfos,
             num * sizeof(procfs_mapinfo), &num) != EOK) {
    free(mapinfos);
    close(proc_fd);
    return result;
  }

  for (i = 0; i < num; i++) {
    mapinfo = mapinfos + i;
    if (mapinfo->flags & MAP_ELF) {
      map.info.vaddr = mapinfo->vaddr;
      if (devctl(proc_fd, DCMD_PROC_MAPDEBUG, &map, sizeof(map), 0) != EOK) {
        continue;
      }
      result.push_back(SharedLibraryAddress(map.info.path, mapinfo->vaddr,
                                            mapinfo->vaddr + mapinfo->size));
    }
  }
  free(mapinfos);
  close(proc_fd);
  return result;
}

void OS::SignalCodeMovingGC() {}

void OS::AdjustSchedulingParams() {}

std::optional<OS::MemoryRange> OS::GetFirstFreeMemoryRangeWithin(
    OS::Address boundary_start, OS::Address boundary_end, size_t minimum_size,
    size_t alignment) {
  return std::nullopt;
}

}  // namespace base
}  // namespace v8
