text: /* QNX Neutrino specific low level interface, for the remote server
     for GDB.
     Copyright (C) 2009-2014 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 <stdarg.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_get_pid (ptid),
  	 ptid_get_lwp (ptid));
    if (nto_inferior.ctl_fd != -1
        && !ptid_equal (ptid, null_ptid)
        && !ptid_equal (ptid, minus_one_ptid))
      {
        pthread_t tid = ptid_get_lwp (ptid);
  
        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_build (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_build (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_build (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_get_lwp (ptid));
        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 passing arguments ALLARGS.  */
  
  static int
  nto_create_inferior (char *program, char **allargs)
  {
    struct inheritance inherit;
    pid_t pid;
    sigset_t set;
  
    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, allargs, 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_get_pid (ptid),
  	 ptid_get_lwp (ptid));
    if (SignalKill (0, ptid_get_pid (ptid), ptid_get_lwp (ptid),
  		  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_get_pid (ptid), &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_build (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;
    ptid_t ptid;
  
    TRACE ("%s (regno=%d)\n", __func__, regno);
    if (regno >= the_low_target.num_regs)
      return;
  
    if (current_inferior == NULL)
      {
        TRACE ("current_inferior is NULL\n");
        return;
      }
    ptid = thread_to_gdb_id (current_inferior);
    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;
    ptid_t ptid;
  
    TRACE ("%s (regno:%d)\n", __func__, regno);
  
    if (current_inferior == NULL)
      {
        TRACE ("current_inferior is NULL\n");
        return;
      }
    ptid = thread_to_gdb_id (current_inferior);
    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_build (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, len);
    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, len);
    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_inferior != NULL)
      {
        ptid_t ptid;
  
        ptid = thread_to_gdb_id (current_inferior);
        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_inferior != NULL)
      {
        ptid_t ptid;
  
        ptid = thread_to_gdb_id (current_inferior);
  
        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;
  }
  
  
  
  static struct target_ops nto_target_ops = {
    nto_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,
    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 */
  };
  
  
  /* 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);
    set_breakpoint_data (the_low_target.breakpoint,
  		       the_low_target.breakpoint_len);
  
    /* 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);
  }
  
  