/* QNX Neutrino specific low level interface, for the remote server
   for GDB.
   Copyright (C) 2009-2018 Free Software Foundation, Inc.

   This file is part of GDB.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */


#include "server.h"
#include "gdbthread.h"
#include "nto-low.h"
#include "hostio.h"

#include <limits.h>
#include <fcntl.h>
#include <spawn.h>
#include <sys/procfs.h>
#include <sys/auxv.h>
#include <sys/iomgr.h>
#include <sys/neutrino.h>


extern int using_threads;
int using_threads = 1;

const struct target_desc *nto_tdesc;

static void
nto_trace (const char *fmt, ...)
{
  va_list arg_list;

  if (debug_threads == 0)
    return;
  fprintf (stderr, "nto:");
  va_start (arg_list, fmt);
  vfprintf (stderr, fmt, arg_list);
  va_end (arg_list);
}

#define TRACE nto_trace

/* Structure holding neutrino specific information about
   inferior.  */

struct nto_inferior
{
  char nto_procfs_path[PATH_MAX];
  int ctl_fd;
  pid_t pid;
  int exit_signo; /* For tracking exit status.  */
};

static struct nto_inferior nto_inferior;

static void
init_nto_inferior (struct nto_inferior *nto_inferior)
{
  memset (nto_inferior, 0, sizeof (struct nto_inferior));
  nto_inferior->ctl_fd = -1;
  nto_inferior->pid = -1;
}

static void
do_detach (void)
{
  if (nto_inferior.ctl_fd != -1)
    {
      nto_trace ("Closing fd\n");
      close (nto_inferior.ctl_fd);
      init_nto_inferior (&nto_inferior);
    }
}

/* Set current thread. Return 1 on success, 0 otherwise.  */

static int
nto_set_thread (ptid_t ptid)
{
  int res = 0;

  TRACE ("%s pid: %d tid: %ld\n", __func__, ptid.pid (),
	 ptid.lwp ());
  if (nto_inferior.ctl_fd != -1
      && ptid != null_ptid
      && ptid != minus_one_ptid)
    {
      pthread_t tid = ptid.lwp ();

      if (EOK == devctl (nto_inferior.ctl_fd, DCMD_PROC_CURTHREAD, &tid,
	  sizeof (tid), 0))
	res = 1;
      else
	TRACE ("%s: Error: failed to set current thread\n", __func__);
    }
  return res;
}

/* This function will determine all alive threads.  Note that we do not list
   dead but unjoined threads even though they are still in the process' thread
   list.  

   NTO_INFERIOR must not be NULL.  */

static void
nto_find_new_threads (struct nto_inferior *nto_inferior)
{
  pthread_t tid;

  TRACE ("%s pid:%d\n", __func__, nto_inferior->pid);

  if (nto_inferior->ctl_fd == -1)
    return;

  for (tid = 1;; ++tid)
    {
      procfs_status status;
      ptid_t ptid;
      int err;

      status.tid = tid;
      err = devctl (nto_inferior->ctl_fd, DCMD_PROC_TIDSTATUS, &status,
		    sizeof (status), 0);

      if (err != EOK || status.tid == 0)
	break;

      /* All threads in between are gone.  */
      while (tid != status.tid || status.state == STATE_DEAD)
	{
	  struct thread_info *ti;

	  ptid = ptid_t (nto_inferior->pid, tid, 0);
	  ti = find_thread_ptid (ptid);
	  if (ti != NULL)
	    {
	      TRACE ("Removing thread %d\n", tid);
	      remove_thread (ti);
	    }
	  if (tid == status.tid)
	    break;
	  ++tid;
	}

      if (status.state != STATE_DEAD)
	{
	  TRACE ("Adding thread %d\n", tid);
	  ptid = ptid_t (nto_inferior->pid, tid, 0);
	  if (!find_thread_ptid (ptid))
	    add_thread (ptid, NULL);
	}
    }
}

/* Given pid, open procfs path.  */

static pid_t
do_attach (pid_t pid)
{
  procfs_status status;
  struct sigevent event;

  if (nto_inferior.ctl_fd != -1)
    {
      close (nto_inferior.ctl_fd);
      init_nto_inferior (&nto_inferior);
    }
  xsnprintf (nto_inferior.nto_procfs_path, PATH_MAX - 1, "/proc/%d/as", pid);
  nto_inferior.ctl_fd = open (nto_inferior.nto_procfs_path, O_RDWR);
  if (nto_inferior.ctl_fd == -1)
    {
      TRACE ("Failed to open %s\n", nto_inferior.nto_procfs_path);
      init_nto_inferior (&nto_inferior);
      return -1;
    }
  if (devctl (nto_inferior.ctl_fd, DCMD_PROC_STOP, &status, sizeof (status), 0)
      != EOK)
    {
      do_detach ();
      return -1;
    }
  nto_inferior.pid = pid;
  /* Define a sigevent for process stopped notification.  */
  event.sigev_notify = SIGEV_SIGNAL_THREAD;
  event.sigev_signo = SIGUSR1;
  event.sigev_code = 0;
  event.sigev_value.sival_ptr = NULL;
  event.sigev_priority = -1;
  devctl (nto_inferior.ctl_fd, DCMD_PROC_EVENT, &event, sizeof (event), 0);

  if (devctl (nto_inferior.ctl_fd, DCMD_PROC_STATUS, &status, sizeof (status),
	      0) == EOK
      && (status.flags & _DEBUG_FLAG_STOPPED))
    {
      ptid_t ptid;
      struct process_info *proc;

      kill (pid, SIGCONT);
      ptid = ptid_t (status.pid, status.tid, 0);
      the_low_target.arch_setup ();
      proc = add_process (status.pid, 1);
      proc->tdesc = nto_tdesc;
      TRACE ("Adding thread: pid=%d tid=%ld\n", status.pid,
	     ptid.lwp ());
      nto_find_new_threads (&nto_inferior);
    }
  else
    {
      do_detach ();
      return -1;
    }

  return pid;
}

/* Read or write LEN bytes from/to inferior's MEMADDR memory address
   into gdbservers's MYADDR buffer.  Return number of bytes actually
   transfered.  */

static int
nto_xfer_memory (off_t memaddr, unsigned char *myaddr, int len,
		 int dowrite)
{
  int nbytes = 0;

  if (lseek (nto_inferior.ctl_fd, memaddr, SEEK_SET) == memaddr)
    {
      if (dowrite)
	nbytes = write (nto_inferior.ctl_fd, myaddr, len);
      else
	nbytes = read (nto_inferior.ctl_fd, myaddr, len);
      if (nbytes < 0)
	nbytes = 0;
    }
  if (nbytes == 0)
    {
      int e = errno;
      TRACE ("Error in %s : errno=%d (%s)\n", __func__, e, strerror (e));
    }
  return nbytes;
}

/* Insert or remove breakpoint or watchpoint at address ADDR.
   TYPE can be one of Neutrino breakpoint types.  SIZE must be 0 for
   inserting the point, -1 for removing it.  

   Return 0 on success, 1 otherwise.  */

static int
nto_breakpoint (CORE_ADDR addr, int type, int size)
{
  procfs_break brk;

  brk.type = type;
  brk.addr = addr;
  brk.size = size;
  if (devctl (nto_inferior.ctl_fd, DCMD_PROC_BREAK, &brk, sizeof (brk), 0)
      != EOK)
    return 1;
  return 0;
}

/* Read auxiliary vector from inferior's initial stack into gdbserver's
   MYADDR buffer, up to LEN bytes.  

   Return number of bytes read.  */

static int
nto_read_auxv_from_initial_stack (CORE_ADDR initial_stack,
				  unsigned char *myaddr,
				  unsigned int len)
{
  int data_ofs = 0;
  int anint;
  unsigned int len_read = 0;

  /* Skip over argc, argv and envp... Comment from ldd.c:

     The startup frame is set-up so that we have:
     auxv
     NULL
     ...
     envp2
     envp1 <----- void *frame + (argc + 2) * sizeof(char *)
     NULL
     ...
     argv2
     argv1
     argc  <------ void * frame

     On entry to ldd, frame gives the address of argc on the stack.  */
  if (nto_xfer_memory (initial_stack, (unsigned char *)&anint,
		       sizeof (anint), 0) != sizeof (anint))
    return 0;

  /* Size of pointer is assumed to be 4 bytes (32 bit arch. ) */
  data_ofs += (anint + 2) * sizeof (void *); /* + 2 comes from argc itself and
						NULL terminating pointer in
						argv.  */

  /* Now loop over env table:  */
  while (nto_xfer_memory (initial_stack + data_ofs,
			  (unsigned char *)&anint, sizeof (anint), 0)
	 == sizeof (anint))
    {
      data_ofs += sizeof (anint);
      if (anint == 0)
	break;
    }
  initial_stack += data_ofs;

  memset (myaddr, 0, len);
  while (len_read <= len - sizeof (auxv_t))
    {
      auxv_t *auxv = (auxv_t *)myaddr;

      /* Search backwards until we have read AT_PHDR (num. 3),
	 AT_PHENT (num 4), AT_PHNUM (num 5)  */
      if (nto_xfer_memory (initial_stack, (unsigned char *)auxv,
			   sizeof (auxv_t), 0) == sizeof (auxv_t))
	{
	  if (auxv->a_type != AT_NULL)
	    {
	      auxv++;
	      len_read += sizeof (auxv_t);
	    }
	  if (auxv->a_type == AT_PHNUM) /* That's all we need.  */
	    break;
	  initial_stack += sizeof (auxv_t);
	}
      else
	break;
    }
  TRACE ("auxv: len_read: %d\n", len_read);
  return len_read;
}

/* Start inferior specified by PROGRAM, using PROGRAM_ARGS as its
   arguments.  */

static int
nto_create_inferior (const char *program,
		     const std::vector<char *> &program_args)
{
  struct inheritance inherit;
  pid_t pid;
  sigset_t set;
  std::string str_program_args = stringify_argv (program_args);

  TRACE ("%s %s\n", __func__, program);
  /* Clear any pending SIGUSR1's but keep the behavior the same.  */
  signal (SIGUSR1, signal (SIGUSR1, SIG_IGN));

  sigemptyset (&set);
  sigaddset (&set, SIGUSR1);
  sigprocmask (SIG_UNBLOCK, &set, NULL);

  memset (&inherit, 0, sizeof (inherit));
  inherit.flags |= SPAWN_SETGROUP | SPAWN_HOLD;
  inherit.pgroup = SPAWN_NEWPGROUP;
  pid = spawnp (program, 0, NULL, &inherit,
		(char *) str_program_args.c_str (), 0);
  sigprocmask (SIG_BLOCK, &set, NULL);

  if (pid == -1)
    return -1;

  if (do_attach (pid) != pid)
    return -1;

  return pid;
}

/* Attach to process PID.  */

static int
nto_attach (unsigned long pid)
{
  TRACE ("%s %ld\n", __func__, pid);
  if (do_attach (pid) != pid)
    error ("Unable to attach to %ld\n", pid);
  return 0;
}

/* Send signal to process PID.  */

static int
nto_kill (int pid)
{
  TRACE ("%s %d\n", __func__, pid);
  kill (pid, SIGKILL);
  do_detach ();
  return 0;
}

/* Detach from process PID.  */

static int
nto_detach (int pid)
{
  TRACE ("%s %d\n", __func__, pid);
  do_detach ();
  return 0;
}

static void
nto_mourn (struct process_info *process)
{
  remove_process (process);
}

/* Check if the given thread is alive.  

   Return 1 if alive, 0 otherwise.  */

static int
nto_thread_alive (ptid_t ptid)
{
  int res;

  TRACE ("%s pid:%d tid:%d\n", __func__, ptid.pid (),
	 ptid.lwp ());
  if (SignalKill (0, ptid.pid (), ptid.lwp (),
		  0, 0, 0) == -1)
    res = 0;
  else
    res = 1;
  TRACE ("%s: %s\n", __func__, res ? "yes" : "no");
  return res;
}

/* Resume inferior's execution.  */

static void
nto_resume (struct thread_resume *resume_info, size_t n)
{
  /* We can only work in all-stop mode.  */
  procfs_status status;
  procfs_run run;
  int err;

  TRACE ("%s\n", __func__);
  /* Workaround for aliasing rules violation. */
  sigset_t *run_fault = (sigset_t *) (void *) &run.fault;

  nto_set_thread (resume_info->thread);

  run.flags = _DEBUG_RUN_FAULT | _DEBUG_RUN_TRACE;
  if (resume_info->kind == resume_step)
    run.flags |= _DEBUG_RUN_STEP;
  run.flags |= _DEBUG_RUN_ARM;

  sigemptyset (run_fault);
  sigaddset (run_fault, FLTBPT);
  sigaddset (run_fault, FLTTRACE);
  sigaddset (run_fault, FLTILL);
  sigaddset (run_fault, FLTPRIV);
  sigaddset (run_fault, FLTBOUNDS);
  sigaddset (run_fault, FLTIOVF);
  sigaddset (run_fault, FLTIZDIV);
  sigaddset (run_fault, FLTFPE);
  sigaddset (run_fault, FLTPAGE);
  sigaddset (run_fault, FLTSTACK);
  sigaddset (run_fault, FLTACCESS);

  sigemptyset (&run.trace);
  if (resume_info->sig)
    {
      int signal_to_pass;

      devctl (nto_inferior.ctl_fd, DCMD_PROC_STATUS, &status, sizeof (status),
	      0);
      signal_to_pass = resume_info->sig;
      if (status.why & (_DEBUG_WHY_SIGNALLED | _DEBUG_WHY_FAULTED))
	{
	  if (signal_to_pass != status.info.si_signo)
	    {
	      kill (status.pid, signal_to_pass);
	      run.flags |= _DEBUG_RUN_CLRFLT | _DEBUG_RUN_CLRSIG;
	    }
	  else		/* Let it kill the program without telling us.  */
	    sigdelset (&run.trace, signal_to_pass);
	}
    }
  else
    run.flags |= _DEBUG_RUN_CLRSIG | _DEBUG_RUN_CLRFLT;

  sigfillset (&run.trace);

  regcache_invalidate ();

  err = devctl (nto_inferior.ctl_fd, DCMD_PROC_RUN, &run, sizeof (run), 0);
  if (err != EOK)
    TRACE ("Error: %d \"%s\"\n", err, strerror (err));
}

/* Wait for inferior's event.  

   Return ptid of thread that caused the event.  */

static ptid_t
nto_wait (ptid_t ptid,
	  struct target_waitstatus *ourstatus, int target_options)
{
  sigset_t set;
  siginfo_t info;
  procfs_status status;
  const int trace_mask = (_DEBUG_FLAG_TRACE_EXEC | _DEBUG_FLAG_TRACE_RD
			  | _DEBUG_FLAG_TRACE_WR | _DEBUG_FLAG_TRACE_MODIFY);

  TRACE ("%s\n", __func__);

  ourstatus->kind = TARGET_WAITKIND_SPURIOUS;

  sigemptyset (&set);
  sigaddset (&set, SIGUSR1);

  devctl (nto_inferior.ctl_fd, DCMD_PROC_STATUS, &status, sizeof (status), 0);
  while (!(status.flags & _DEBUG_FLAG_ISTOP))
    {
      sigwaitinfo (&set, &info);
      devctl (nto_inferior.ctl_fd, DCMD_PROC_STATUS, &status, sizeof (status),
	      0);
    }
  nto_find_new_threads (&nto_inferior);

  if (status.flags & _DEBUG_FLAG_SSTEP)
    {
      TRACE ("SSTEP\n");
      ourstatus->kind = TARGET_WAITKIND_STOPPED;
      ourstatus->value.sig = GDB_SIGNAL_TRAP;
    }
  /* Was it a breakpoint?  */
  else if (status.flags & trace_mask)
    {
      TRACE ("STOPPED\n");
      ourstatus->kind = TARGET_WAITKIND_STOPPED;
      ourstatus->value.sig = GDB_SIGNAL_TRAP;
    }
  else if (status.flags & _DEBUG_FLAG_ISTOP)
    {
      TRACE ("ISTOP\n");
      switch (status.why)
	{
	case _DEBUG_WHY_SIGNALLED:
	  TRACE ("  SIGNALLED\n");
	  ourstatus->kind = TARGET_WAITKIND_STOPPED;
	  ourstatus->value.sig =
	    gdb_signal_from_host (status.info.si_signo);
	  nto_inferior.exit_signo = ourstatus->value.sig;
	  break;
	case _DEBUG_WHY_FAULTED:
	  TRACE ("  FAULTED\n");
	  ourstatus->kind = TARGET_WAITKIND_STOPPED;
	  if (status.info.si_signo == SIGTRAP)
	    {
	      ourstatus->value.sig = 0;
	      nto_inferior.exit_signo = 0;
	    }
	  else
	    {
	      ourstatus->value.sig =
		gdb_signal_from_host (status.info.si_signo);
	      nto_inferior.exit_signo = ourstatus->value.sig;
	    }
	  break;

	case _DEBUG_WHY_TERMINATED:
	  {
	    int waitval = 0;

	    TRACE ("  TERMINATED\n");
	    waitpid (ptid.pid (), &waitval, WNOHANG);
	    if (nto_inferior.exit_signo)
	      {
		/* Abnormal death.  */
		ourstatus->kind = TARGET_WAITKIND_SIGNALLED;
		ourstatus->value.sig = nto_inferior.exit_signo;
	      }
	    else
	      {
		/* Normal death.  */
		ourstatus->kind = TARGET_WAITKIND_EXITED;
		ourstatus->value.integer = WEXITSTATUS (waitval);
	      }
	    nto_inferior.exit_signo = 0;
	    break;
	  }

	case _DEBUG_WHY_REQUESTED:
	  TRACE ("REQUESTED\n");
	  /* We are assuming a requested stop is due to a SIGINT.  */
	  ourstatus->kind = TARGET_WAITKIND_STOPPED;
	  ourstatus->value.sig = GDB_SIGNAL_INT;
	  nto_inferior.exit_signo = 0;
	  break;
	}
    }

  return ptid_t (status.pid, status.tid, 0);
}

/* Fetch inferior's registers for currently selected thread (CURRENT_INFERIOR).
   If REGNO is -1, fetch all registers, or REGNO register only otherwise.  */

static void
nto_fetch_registers (struct regcache *regcache, int regno)
{
  int regsize;
  procfs_greg greg;

  TRACE ("%s (regno=%d)\n", __func__, regno);
  if (regno >= the_low_target.num_regs)
    return;

  if (current_thread == NULL)
    {
      TRACE ("current_thread is NULL\n");
      return;
    }
  ptid_t ptid = ptid_of (current_thread);
  if (!nto_set_thread (ptid))
    return;

  if (devctl (nto_inferior.ctl_fd, DCMD_PROC_GETGREG, &greg, sizeof (greg),
	      &regsize) == EOK)
    {
      if (regno == -1) /* All registers. */
	{
	  for (regno = 0; regno != the_low_target.num_regs; ++regno)
	    {
	      const unsigned int registeroffset
		= the_low_target.register_offset (regno);
	      supply_register (regcache, regno,
			       ((char *)&greg) + registeroffset);
	    }
	}
      else
	{
	  const unsigned int registeroffset
	    = the_low_target.register_offset (regno);
	  if (registeroffset == -1)
	    return;
	  supply_register (regcache, regno, ((char *)&greg) + registeroffset);
	}
    }
  else
    TRACE ("ERROR reading registers from inferior.\n");
}

/* Store registers for currently selected thread (CURRENT_INFERIOR).  
   We always store all registers, regardless of REGNO.  */

static void
nto_store_registers (struct regcache *regcache, int regno)
{
  procfs_greg greg;
  int err;

  TRACE ("%s (regno:%d)\n", __func__, regno);

  if (current_thread == NULL)
    {
      TRACE ("current_thread is NULL\n");
      return;
    }
  ptid_t ptid = ptid_of (current_thread);
  if (!nto_set_thread (ptid))
    return;

  memset (&greg, 0, sizeof (greg));
  for  (regno = 0; regno != the_low_target.num_regs; ++regno)
    {
      const unsigned int regoffset
	= the_low_target.register_offset (regno);
      collect_register (regcache, regno, ((char *)&greg) + regoffset);
    }
  err = devctl (nto_inferior.ctl_fd, DCMD_PROC_SETGREG, &greg, sizeof (greg),
		0);
  if (err != EOK)
    TRACE ("Error: setting registers.\n");
}

/* Read LEN bytes from inferior's memory address MEMADDR into
   gdbserver's MYADDR buffer.  

   Return 0 on success -1 otherwise.  */

static int
nto_read_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len)
{
  TRACE ("%s memaddr:0x%08lx, len:%d\n", __func__, memaddr, len);

  if (nto_xfer_memory (memaddr, myaddr, len, 0) != len)
    {
      TRACE ("Failed to read memory\n");
      return -1;
    }

  return 0;
}

/* Write LEN bytes from gdbserver's buffer MYADDR into inferior's
   memory at address MEMADDR.  

   Return 0 on success -1 otherwise.  */

static int
nto_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len)
{
  int len_written;

  TRACE ("%s memaddr: 0x%08llx len: %d\n", __func__, memaddr, len);
  if ((len_written = nto_xfer_memory (memaddr, (unsigned char *)myaddr, len,
				      1))
      != len)
    {
      TRACE ("Wanted to write: %d but written: %d\n", len, len_written);
      return -1;
    }

  return 0;
}

/* Stop inferior.  We always stop all threads.  */

static void
nto_request_interrupt (void)
{
  TRACE ("%s\n", __func__);
  nto_set_thread (ptid_t (nto_inferior.pid, 1, 0));
  if (EOK != devctl (nto_inferior.ctl_fd, DCMD_PROC_STOP, NULL, 0, 0))
    TRACE ("Error stopping inferior.\n");
}

/* Read auxiliary vector from inferior's memory into gdbserver's buffer
   MYADDR.  We always read whole auxv.  
   
   Return number of bytes stored in MYADDR buffer, 0 if OFFSET > 0
   or -1 on error.  */

static int
nto_read_auxv (CORE_ADDR offset, unsigned char *myaddr, unsigned int len)
{
  int err;
  CORE_ADDR initial_stack;
  procfs_info procinfo;

  TRACE ("%s\n", __func__);
  if (offset > 0)
    return 0;

  err = devctl (nto_inferior.ctl_fd, DCMD_PROC_INFO, &procinfo,
		sizeof procinfo, 0);
  if (err != EOK)
    return -1;

  initial_stack = procinfo.initial_stack;

  return nto_read_auxv_from_initial_stack (initial_stack, myaddr, len);
}

static int
nto_supports_z_point_type (char z_type)
{
  switch (z_type)
    {
    case Z_PACKET_SW_BP:
    case Z_PACKET_HW_BP:
    case Z_PACKET_WRITE_WP:
    case Z_PACKET_READ_WP:
    case Z_PACKET_ACCESS_WP:
      return 1;
    default:
      return 0;
    }
}

/* Insert {break/watch}point at address ADDR.  SIZE is not used.  */

static int
nto_insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
		  int size, struct raw_breakpoint *bp)
{
  int wtype = _DEBUG_BREAK_HW; /* Always request HW.  */

  TRACE ("%s type:%c addr: 0x%08lx len:%d\n", __func__, (int)type, addr, size);
  switch (type)
    {
    case raw_bkpt_type_sw:
      wtype = _DEBUG_BREAK_EXEC;
      break;
    case raw_bkpt_type_hw:
      wtype |= _DEBUG_BREAK_EXEC;
      break;
    case raw_bkpt_type_write_wp:
      wtype |= _DEBUG_BREAK_RW;
      break;
    case raw_bkpt_type_read_wp:
      wtype |= _DEBUG_BREAK_RD;
      break;
    case raw_bkpt_type_access_wp:
      wtype |= _DEBUG_BREAK_RW;
      break;
    default:
      return 1; /* Not supported.  */
    }
  return nto_breakpoint (addr, wtype, 0);
}

/* Remove {break/watch}point at address ADDR.  SIZE is not used.  */

static int
nto_remove_point (enum raw_bkpt_type type, CORE_ADDR addr,
		  int size, struct raw_breakpoint *bp)
{
  int wtype = _DEBUG_BREAK_HW; /* Always request HW.  */

  TRACE ("%s type:%c addr: 0x%08lx len:%d\n", __func__, (int)type, addr, size);
  switch (type)
    {
    case raw_bkpt_type_sw:
      wtype = _DEBUG_BREAK_EXEC;
      break;
    case raw_bkpt_type_hw:
      wtype |= _DEBUG_BREAK_EXEC;
      break;
    case raw_bkpt_type_write_wp:
      wtype |= _DEBUG_BREAK_RW;
      break;
    case raw_bkpt_type_read_wp:
      wtype |= _DEBUG_BREAK_RD;
      break;
    case raw_bkpt_type_access_wp:
      wtype |= _DEBUG_BREAK_RW;
      break;
    default:
      return 1; /* Not supported.  */
    }
  return nto_breakpoint (addr, wtype, -1);
}

/* Check if the reason of stop for current thread (CURRENT_INFERIOR) is
   a watchpoint.

   Return 1 if stopped by watchpoint, 0 otherwise.  */

static int
nto_stopped_by_watchpoint (void)
{
  int ret = 0;

  TRACE ("%s\n", __func__);
  if (nto_inferior.ctl_fd != -1 && current_thread != NULL)
    {
      ptid_t ptid = ptid_of (current_thread);
      if (nto_set_thread (ptid))
	{
	  const int watchmask = _DEBUG_FLAG_TRACE_RD | _DEBUG_FLAG_TRACE_WR
				| _DEBUG_FLAG_TRACE_MODIFY;
	  procfs_status status;
	  int err;

	  err = devctl (nto_inferior.ctl_fd, DCMD_PROC_STATUS, &status,
			sizeof (status), 0);
	  if (err == EOK && (status.flags & watchmask))
	    ret = 1;
	}
    }
  TRACE ("%s: %s\n", __func__, ret ? "yes" : "no");
  return ret;
}

/* Get instruction pointer for CURRENT_INFERIOR thread.  

   Return inferior's instruction pointer value, or 0 on error.  */ 

static CORE_ADDR
nto_stopped_data_address (void)
{
  CORE_ADDR ret = (CORE_ADDR)0;

  TRACE ("%s\n", __func__);
  if (nto_inferior.ctl_fd != -1 && current_thread != NULL)
    {
      ptid_t ptid = ptid_of (current_thread);

      if (nto_set_thread (ptid))
	{
	  procfs_status status;

	  if (devctl (nto_inferior.ctl_fd, DCMD_PROC_STATUS, &status,
		      sizeof (status), 0) == EOK)
	    ret = status.ip;
	}
    }
  TRACE ("%s: 0x%08lx\n", __func__, ret);
  return ret;
}

/* We do not currently support non-stop.  */

static int
nto_supports_non_stop (void)
{
  TRACE ("%s\n", __func__);
  return 0;
}

/* Implementation of the target_ops method "sw_breakpoint_from_kind".  */

static const gdb_byte *
nto_sw_breakpoint_from_kind (int kind, int *size)
{
  *size = the_low_target.breakpoint_len;
  return the_low_target.breakpoint;
}


static struct target_ops nto_target_ops = {
  nto_create_inferior,
  NULL,  /* post_create_inferior */
  nto_attach,
  nto_kill,
  nto_detach,
  nto_mourn,
  NULL, /* nto_join */
  nto_thread_alive,
  nto_resume,
  nto_wait,
  nto_fetch_registers,
  nto_store_registers,
  NULL, /* prepare_to_access_memory */
  NULL, /* done_accessing_memory */
  nto_read_memory,
  nto_write_memory,
  NULL, /* nto_look_up_symbols */
  nto_request_interrupt,
  nto_read_auxv,
  nto_supports_z_point_type,
  nto_insert_point,
  nto_remove_point,
  NULL, /* stopped_by_sw_breakpoint */
  NULL, /* supports_stopped_by_sw_breakpoint */
  NULL, /* stopped_by_hw_breakpoint */
  NULL, /* supports_stopped_by_hw_breakpoint */
  target_can_do_hardware_single_step,
  nto_stopped_by_watchpoint,
  nto_stopped_data_address,
  NULL, /* nto_read_offsets */
  NULL, /* thread_db_set_tls_address */
  NULL,
  hostio_last_error_from_errno,
  NULL, /* nto_qxfer_osdata */
  NULL, /* xfer_siginfo */
  nto_supports_non_stop,
  NULL, /* async */
  NULL, /* start_non_stop */
  NULL, /* supports_multi_process */
  NULL, /* supports_fork_events */
  NULL, /* supports_vfork_events */
  NULL, /* supports_exec_events */
  NULL, /* handle_new_gdb_connection */
  NULL, /* handle_monitor_command */
  NULL, /* core_of_thread */
  NULL, /* read_loadmap */
  NULL, /* process_qsupported */
  NULL, /* supports_tracepoints */
  NULL, /* read_pc */
  NULL, /* write_pc */
  NULL, /* thread_stopped */
  NULL, /* get_tib_address */
  NULL, /* pause_all */
  NULL, /* unpause_all */
  NULL, /* stabilize_threads */
  NULL, /* install_fast_tracepoint_jump_pad */
  NULL, /* emit_ops */
  NULL, /* supports_disable_randomization */
  NULL, /* get_min_fast_tracepoint_insn_len */
  NULL, /* qxfer_libraries_svr4 */
  NULL, /* support_agent */
  NULL, /* enable_btrace */
  NULL, /* disable_btrace */
  NULL, /* read_btrace */
  NULL, /* read_btrace_conf */
  NULL, /* supports_range_stepping */
  NULL, /* pid_to_exec_file */
  NULL, /* multifs_open */
  NULL, /* multifs_unlink */
  NULL, /* multifs_readlink */
  NULL, /* breakpoint_kind_from_pc */
  nto_sw_breakpoint_from_kind,
};


/* Global function called by server.c.  Initializes QNX Neutrino
   gdbserver.  */

void
initialize_low (void)
{
  sigset_t set;

  TRACE ("%s\n", __func__);
  set_target_ops (&nto_target_ops);

  /* We use SIGUSR1 to gain control after we block waiting for a process.
     We use sigwaitevent to wait.  */
  sigemptyset (&set);
  sigaddset (&set, SIGUSR1);
  sigprocmask (SIG_BLOCK, &set, NULL);
}

