/*
 * Copyright (c) 2011 The Native Client Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

/*
 * NaCl tests for simple syscalls not using newlib
 */

#define CHECK_ERRNO 1

#include "native_client/src/trusted/service_runtime/include/sys/nacl_syscalls.h"

/*
 * These must come after <sys/nacl_syscalls.h> when using nacl-newlib
 * otherwise we get errors about conflicting types.  This is a bug in
 * nacl-newlib.
 */
#include <sys/mman.h>
#include <unistd.h>

/* NOTE: defining CHECK_ERRNO pulls in some newlib magic (reent.h, etc.) */
#if defined(CHECK_ERRNO)
#include <errno.h>
#endif


int mystrlen(const char* s) {
  int count = 0;
  while(*s++) ++count;
  return count;
}

#define myprint(s) write(1, s, mystrlen(s))

void hextochar(int n, char buffer[9]) {
  int i;
  buffer[8] = 0;

  for (i=0; i < 8; ++i) {
    int nibble = 0xf & (n >> (4 * (7 - i)));
    if (nibble <= 9) {
      buffer[i] = nibble + '0';
    } else {
      buffer[i] = nibble - 10 + 'A';
    }
  }
}

void PrintInt(int i) {
  char buffer[16];

  hextochar(i, buffer);
  myprint(buffer);
  myprint("\n");
}

void Error(const char* message) {
  myprint("ERROR: ");
  myprint(message);
  _exit(-1);
}

#if defined(CHECK_ERRNO)

void CheckErrno(int expected) {
  char buffer[16];

  myprint("\nerrno\n");
  hextochar(errno, buffer);
  myprint(buffer);
  myprint("\n");
  if (expected != errno) Error("bad errno value\n");
}

#else

void CheckErrno(int expected) {
  expected = expected;
}

#endif

const int kExitOk = 69;
const int kMmapBase     = 0xa00000;
const int kMmapSize     = 0x10000;
const int kInvalidFileDescriptor = 100;

/*
 * This is defined by the linker as the address of the end of our data segment.
 * That's where the break starts out by default.
 */
extern char end;

int main(void) {
  /*
   * Round up to the end of the page that's our last initial data page.
   * Then add 10MB for good measure to be out of the way of any allocations
   * that might have been done before we got here.
   */
  const int sysbrkBase = ((((intptr_t) &end) + 0xffff) & -0x10000) + (10 << 20);
  void *map_result;
  int  i;

  CheckErrno(0);

  myprint("\nnull_syscall()\n");
  null_syscall();

  myprint("\nsysbreak()\n");
  i = (int) sysbrk(0);
  PrintInt(i);
  if (0 == i) Error("bad sysbrk() value\n");

  if (sysbrkBase < i) {
    Error("INTERNAL ERROR: sysbrkBase too small\n");
  }

  myprint("\nsysbrk()\n");
  i = (int) sysbrk((void *) sysbrkBase);
  PrintInt(i);
  if (sysbrkBase != i) Error("bad sysbrk() value\n");

  myprint("\nmmap()\n");
  i = (int) mmap(0, kMmapSize,
                 PROT_READ, MAP_ANONYMOUS | MAP_SHARED,
                 -1, 0);
  PrintInt(i);
  if (-1 == i) Error("bad mmap() value\n");

  myprint("\nmunmap()\n");
  i = (int) munmap((void *)i, kMmapSize);
  PrintInt(i);
  if (0 != i) Error("bad munmap() value\n");

  myprint("\nmmap()\n");
  map_result = mmap((void *)kMmapBase, kMmapSize,
                    PROT_READ, MAP_ANONYMOUS | MAP_SHARED,
                    -1, 0);
  i = (int) map_result;
  PrintInt(i);
  /*
   * It is an ERROR to expect i to be kMapBase.  Since the mmap
   * syscall were not made with MAP_FIXED, the kernel is free to use
   * the provided address as a hint -- or to ignore it altogether.
   * Exactly what behavior we implement may vary from release to
   * release -- thus, the only reasonable value to compare this with
   * is MAP_FAILED.
   */
  if (MAP_FAILED == map_result) Error("bad: mmap() failed\n");

  myprint("\nmunmap()\n");
  i = (int) munmap(map_result, kMmapSize);
  PrintInt(i);
  if (0 != i) Error("bad munmap() value\n");

  CheckErrno(0);

  /* Some expect failures */
  myprint("\nclose()\n");
  i = (int) close(kInvalidFileDescriptor);
  PrintInt(i);
  if (~0 != i) Error("bad close value\n");
  CheckErrno(EBADF);

  myprint("\nclock()\n");
  i = clock();
  PrintInt(i);
  if (0 > i) Error("bad clock value\n");

  myprint("before _exit()\n");
  _exit(kExitOk);

  return 0;
}
