/*
 * nasd_dux_basic.c
 *
 * Basic system support stuff
 *
 * Author: Jim Zelenka
 */
/*
 * Copyright (c) of Carnegie Mellon University, 1998,1999.
 *
 * Permission to reproduce, use, and prepare derivative works of
 * this software for internal use is granted provided the copyright
 * and "No Warranty" statements are included with all reproductions
 * and derivative works. This software may also be redistributed
 * without charge provided that the copyright and "No Warranty"
 * statements are included in all redistributions.
 *
 * NO WARRANTY. THIS SOFTWARE IS FURNISHED ON AN "AS IS" BASIS.
 * CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER
 * EXPRESSED OR IMPLIED AS TO THE MATTER INCLUDING, BUT NOT LIMITED
 * TO: WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY
 * OF RESULTS OR RESULTS OBTAINED FROM USE OF THIS SOFTWARE. CARNEGIE
 * MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT
 * TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.
 */


#include <nasd/nasd_options.h>
#include <nasd/nasd_types.h>
#include <nasd/nasd_mem.h>
#include <nasd/nasd_threadstuff.h>
#include <nasd/nasd_general.h>
#include <nasd/nasd_timer.h>
#include <nasd/nasd_sys.h>
#include <sys/time.h>
#include <sys/sysinfo.h>
#include <machine/rpb.h>
#include <machine/hal/hal_sysinfo.h>

#include <nasd/nasd_edrfs_types.h> /* for error codes */

#ifdef _KERNEL
#include <sys/kernel.h>
#endif /* _KERNEL */

#ifndef KERNEL
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <nlist.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <ctype.h>
#include <stdio.h>
#include <unistd.h>
#endif /* !KERNEL */

#if NASD_CMU_PDL > 0
#include <pdllib.h>
#else /* NASD_CMU_PDL > 0 */
#include <sys/param.h>
#endif /* NASD_CMU_PDL > 0 */

extern int syscall(int number, int arg, ...);

static nasd_timespec_t nasd_dux_last_time = {0,0};
static nasd_timespec_t nasd_dux_uniq_time = {0,0};

int
nasd_dux_nasd_status_to_errno(
  nasd_status_t  nasd_status)
{
  switch(nasd_status) {
    case NASD_SUCCESS:               return(ESUCCESS);
    case NASD_NO_MEM:                return(ENOMEM);
    case NASD_FAIL:                  return(EIO);
    case NASD_NO_MORE_OBJECTS:       return(ENFILE);
    case NASD_NO_SPACE:              return(ENOSPC);
    case NASD_BAD_DIGEST_REQ:
    case NASD_BAD_DIGEST_REPLY:
    case NASD_BAD_NONCE:
    case NASD_BAD_KEYTYPE:
    case NASD_WEAK_COOKIE:
    case NASD_WRONG_NI:
    case NASD_WRONG_DRIVE:
    case NASD_AUTHCHECK_FAILED:      return(EACCES);
    case NASD_BAD_IDENTIFIER:  
    case NASD_BAD_ATTR_SET:          return(EBADRPC);
    case NASD_OP_NOT_SUPPORTED:      return(ENOSYS);
    case NASD_BAD_TIMESPEC:          return(EBADRPC);
    case NASD_NO_FREE_IDS:           return(ENOSPC);
    case NASD_GUARD_CHECK_FAILED:    return(EBADRPC); /* XXX - should be something better here */
    case NASD_BAD_IO_LIST_LEN:       return(EBADRPC);
    case NASD_BAD_IO_LIST:           return(EBADRPC);

    case NASD_EDRFS_BAD_MARKER_VERF: return(EIO); /* XXX what to do here ??? */
    case NASD_EDRFS_STALE_ID:        return(ESTALE);
    case NASD_EDRFS_BAD_NAME:        return(ENOENT);
    case NASD_EDRFS_ALREADY_EXISTS:  return(EEXIST);
    case NASD_EDRFS_NOT_DIR:         return(ENOTDIR);
    case NASD_EDRFS_IS_DIR:          return(EISDIR);
    case NASD_EDRFS_DIR_NOT_EMPTY:   return(ENOTEMPTY);

    default: /* XXX - this should never happen */ /* I bet it does --jimz */
      return(EIO);
  }
}

void
nasd_gettime(
  nasd_timespec_t  *tm)
{
  struct timeval tv;

#ifdef KERNEL
  TIME_READ(tv);
#else /* KERNEL */
#if NASD_CMU_PDL > 0
  PDL_mmclock_gettime(&tv);
#else /* NASD_CMU_PDL > 0 */
  gettimeofday(&tv, NULL);
#endif /* NASD_CMU_PDL > 0 */
#endif /* KERNEL */
  tm->ts_sec = tv.tv_sec;
  tm->ts_nsec = tv.tv_usec*1000;
  if ((tm->ts_sec == nasd_dux_last_time.ts_sec)
    && (tm->ts_nsec == nasd_dux_last_time.ts_nsec))
  {
    nasd_dux_uniq_time.ts_nsec++;
    if (nasd_dux_uniq_time.ts_nsec >= NASD_NSEC_PER_SEC) {
      nasd_dux_uniq_time.ts_nsec -= NASD_NSEC_PER_SEC;
      nasd_dux_uniq_time.ts_sec++;
    }
    *tm = nasd_dux_uniq_time;
  }
  else {
    nasd_dux_last_time = *tm;
    nasd_dux_uniq_time = nasd_dux_last_time;
  }
}

#ifndef KERNEL

nasd_status_t
nasd_sys_init()
{
#if NASD_CMU_PDL > 0
  int ret;

  ret = PDL_get_clock_speed(&nasd_ctimer_ticks);
  if (ret) {
    perror("PDL_get_clock_speed");
    return(NASD_FAIL);
  }
#else /* NASD_CMU_PDL > 0 */
  char *kmemdevname, buf[sizeof(struct rpb)+8];
  char *memdevname, kernel_name[MAXPATHLEN+1];
  struct nlist nl[2], *np;
  unsigned long rpb_addr;
  nasd_status_t rc;
  int kfd, fd, ret;
  struct rpb rpb;
  off_t off;

  kmemdevname = "/dev/kmem";
  memdevname = "/dev/mem";

  np = &nl[0];
  bzero((char *)np, sizeof(nl));
  nl[0].n_name = "pmap_physhwrpb";
  nl[1].n_name = NULL;

  rc = NASD_SUCCESS;

  /* get running kernel name */
  bzero(kernel_name, MAXPATHLEN+1);
  kernel_name[0] = '/';
  ret = getsysinfo(GSI_BOOTEDFILE, &kernel_name[1], MAXPATHLEN, 0, 0);
  if (ret != 1) {
    /* cannot get booted kernel name */
    return(NASD_FAIL);
  }

  ret = nlist(kernel_name, np);
  if (ret) {
    /* cannot nlist kernel */
    return(NASD_FAIL);
  }

  if (np->n_type == 0) {
    /* didn't find the symbols we want */
    return(NASD_FAIL);
  }

  kfd = open(kmemdevname, O_RDONLY);
  if (kfd < 0) {
    perror(kmemdevname);
    return(NASD_FAIL);
  }
  fd = open(memdevname, O_RDONLY);
  if (fd < 0) {
    perror(memdevname);
    return(NASD_FAIL);
  }

  /*
   * pmap_physhwrpb is a variable in the kernel containing the physical
   * address of the hardware RPB. We'll just find that variable and
   * read it, then use that as a physical memory address to read the
   * rpb itself.
   */

  off = lseek(kfd, np->n_value, SEEK_SET);
  if (off != np->n_value) {
    /* cannot seek to address of hwrpb addr */
    return(NASD_FAIL);
  }

  ret = read(kfd, &rpb_addr, sizeof(rpb_addr));
  if (ret != sizeof(rpb_addr)) {
    /* cannot read address of hwrpb addr */
    rc = NASD_FAIL;
    goto isbad;
  }

  off = lseek(fd, rpb_addr, SEEK_SET);
  if (off != rpb_addr) {
    /* cannot seek to rpb addr */
    rc = NASD_FAIL;
    goto isbad;
  }

  ret = read(fd, &rpb, sizeof(rpb));
  if (ret != sizeof(rpb)) {
    /* cannot read rpb */
    rc = NASD_FAIL;
    goto isbad;
  }

  /*
   * One extra sanity check: the RPB is self-identifying.
   * This field is guaranteed to have the value
   * 0x0000004250525748, always.
   */
  if (rpb.rpb_string != 0x0000004250525748) {
    rc = NASD_FAIL;
    goto isbad;
  }

isbad:
  if (rc) {
    /*
     * rpb failed validation
     * most likely, the kernel binary has been modified since
     * it was booted
     */
    close(kfd);
    close(fd);
    return(rc);
  }

  nasd_ctimer_ticks = rpb.rpb_counter;

  close(kfd);
  close(fd);
#endif /* NASD_CMU_PDL > 0 */
  return(NASD_SUCCESS);
}

void
nasd_sys_shutdown()
{
#if NASD_CMU_PDL > 0
  PDL_shutdown();
#endif /* NASD_CMU_PDL > 0 */
}

#if NASD_CMU_PDL > 0
nasd_status_t
nasd_get_total_idle_time(
  nasd_timespec_t  *ts)
{
  struct timespec tt;
  int ret;

  ret = PDL_get_total_idle_time(&tt);
  if (ret)
    return(NASD_FAIL);

  ts->ts_sec = tt.tv_sec;
  ts->ts_nsec = tt.tv_nsec;

  return(NASD_SUCCESS);
}
#endif /* NASD_CMU_PDL > 0 */

#endif /* !KERNEL */

#ifdef KERNEL

extern int nasd_disable_threads_try_lock_test;
extern long lockmode;

nasd_uint64 nasd_ctimer_ticks;

/*
 * When MACH_SLOCKS is 0, or when lockmode is 0, all
 * locking primitives are no-ops. That's fine, but that
 * means that simple_lock_try() always returns nonzero
 * (lock acquired), which confuses the try_lock tester.
 * We just disable the tester in those cases.
 */

nasd_status_t
nasd_sys_init()
{
#if MACH_SLOCKS == 0
  nasd_disable_threads_try_lock_test = 1;
#else /* MACH_SLOCKS == 0 */
#ifdef __alpha
  if (lockmode == 0)
    nasd_disable_threads_try_lock_test = 1;
#endif /* __alpha */
#endif /* MACH_SLOCKS == 0 */
  return(NASD_SUCCESS);
}

void
nasd_sys_shutdown()
{
  /* nothing to do here */
}

nasd_status_t
nasd_sys_threads_init()
{
  /* nothing to do here */
  return(NASD_SUCCESS);
}

nasd_status_t
nasd_sys_threads_init_once()
{
  
  /* nothing to do here */
  return(NASD_SUCCESS);
}

void
nasd_sys_threads_shutdown()
{
  /* nothing to do here */
}

decl_simple_lock_data(,nasd_once_lock)

nasd_status_t
nasd_sys_mutex_init(m)
  decl_simple_lock_data(,*m)
{
  simple_lock_init(m);
  return(NASD_SUCCESS);
}

nasd_status_t
nasd_sys_mutex_destroy(m)
  decl_simple_lock_data(,*m)
{
  return(NASD_SUCCESS);
}

nasd_status_t
nasd_sys_cond_init(c)
  NASD_DECLARE_COND(*c)
{
  *c = 0; /* no reason */
  return(NASD_SUCCESS);
}

nasd_status_t
nasd_sys_cond_destroy(c)
  NASD_DECLARE_COND(*c)
{
  return(NASD_SUCCESS);
}

int
_nasd_once(
  nasd_once_t   *once_block,
  void         (*init_routine)(void),
  char          *file,
  int            line)
{
  int s;

  s = splextreme();
  simple_lock(&nasd_once_lock);
  if (once_block->done == 0) {
    once_block->done = 1;
    (*init_routine)();
  }
  simple_unlock(&nasd_once_lock);
  splx(s);
}

nasd_status_t
nasd_sys_threadattr_create(
  nasd_sys_threadattr_t  *attrp)
{
  attrp->dummy = 0;
  return(NASD_SUCCESS);
}

nasd_status_t
nasd_sys_threadattr_destroy(
  nasd_sys_threadattr_t  *attrp)
{
  return(NASD_SUCCESS);
}

nasd_status_t
nasd_sys_threadattr_setstacksize(
  nasd_sys_threadattr_t  *attrp,
  int                     size)
{
  return(NASD_OP_NOT_SUPPORTED);
}

nasd_status_t
nasd_sys_threadattr_getstacksize(
  nasd_sys_threadattr_t  *attrp,
  int                    *sizep)
{
  *sizep = KERNEL_STACK_SIZE;
  return(NASD_SUCCESS);
}

nasd_status_t
nasd_sys_thread_create_attr(
  nasd_sys_thread_t      *threadp,
  nasd_sys_threadattr_t  *attrp,
  nasd_threadfunc_t       func,
  nasd_threadarg_t        arg)
{
  *threadp = kernel_thread_w_arg(first_task, (void (*)())func, arg);
  if (*threadp == NULL)
    return(NASD_FAIL);
  return(NASD_SUCCESS);
}

nasd_status_t
nasd_sys_thread_create(
  nasd_sys_thread_t  *threadp,
  nasd_threadfunc_t   func,
  nasd_threadarg_t    arg)
{
  *threadp = kernel_thread_w_arg(first_task, (void (*)())func, arg);
  if (*threadp == NULL)
    return(NASD_FAIL);
  return(NASD_SUCCESS);
}

nasd_status_t
nasd_sys_thread_create_w_name(
  nasd_sys_thread_t  *threadp,
  nasd_threadfunc_t   func,
  nasd_threadarg_t    arg,
  char               *name)             /* ignored in this impl. */
{
  *threadp = kernel_thread_w_arg(first_task, (void (*)())func, arg);
  if (*threadp == NULL)
    return(NASD_FAIL);
  return(NASD_SUCCESS);
}

#if NASD_USE_DUX_RW_LOCKS > 0

nasd_status_t
nasd_rwlock_init(l)
  NASD_DECLARE_RWLOCK(*l)
{
  lock_setup(l, NULL, TRUE);
  return(NASD_SUCCESS);
}

nasd_status_t
nasd_rwlock_destroy(l)
  NASD_DECLARE_RWLOCK(*l)
{
  lock_terminate(l);
  return(NASD_SUCCESS);
}

#endif /* NASD_USE_DUX_RW_LOCKS > 0 */

#if TRACK_IDLE > 0
nasd_status_t
nasd_get_total_idle_time(
  nasd_timespec_t  *ts)
{
  extern timespec_t idle_time;

  ts->ts_sec = idle_time.tv_sec;
  ts->ts_nsec = idle_time.tv_nsec;
  return(NASD_SUCCESS);
}
#endif /* TRACK_IDLE > 0 */

#endif /* KERNEL */

nasd_status_t
nasd_get_clock_speed(
  nasd_uint64  *ticksp)
{
  *ticksp = nasd_ctimer_ticks;
  return(NASD_SUCCESS);
}

/* Local Variables:  */
/* indent-tabs-mode: nil */
/* tab-width: 2 */
/* End: */
