/* Native-dependent code for ptx 4.0
   Copyright 1988, 1989, 1991, 1992 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 2 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, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.  */

#include "defs.h"
#include "inferior.h"
#include "gdbcore.h"
#include <sys/procfs.h>
#include <sys/ptrace.h>
#include <sys/param.h>
#include <fcntl.h>

/* Prototypes for supply_gregset etc. */
#include "gregset.h"

/*  Given a pointer to a general register set in /proc format (gregset_t *),
   unpack the register contents and supply them as gdb's idea of the current
   register values. */

void
supply_gregset (gregset_t *gregsetp)
{
  supply_register (EAX_REGNUM, (char *) &(*gregsetp)[EAX]);
  supply_register (EDX_REGNUM, (char *) &(*gregsetp)[EDX]);
  supply_register (ECX_REGNUM, (char *) &(*gregsetp)[ECX]);
  supply_register (EBX_REGNUM, (char *) &(*gregsetp)[EBX]);
  supply_register (ESI_REGNUM, (char *) &(*gregsetp)[ESI]);
  supply_register (EDI_REGNUM, (char *) &(*gregsetp)[EDI]);
  supply_register (ESP_REGNUM, (char *) &(*gregsetp)[UESP]);
  supply_register (EBP_REGNUM, (char *) &(*gregsetp)[EBP]);
  supply_register (EIP_REGNUM, (char *) &(*gregsetp)[EIP]);
  supply_register (EFLAGS_REGNUM, (char *) &(*gregsetp)[EFL]);
}

void
fill_gregset (gregset_t *gregsetp, int regno)
{
  int regi;

  for (regi = 0; regi < NUM_REGS; regi++)
    {
      if ((regno == -1) || (regno == regi))
	{
	  (*gregsetp)[regi] = *(greg_t *) & registers[REGISTER_BYTE (regi)];
	}
    }
}

/*  Given a pointer to a floating point register set in /proc format
   (fpregset_t *), unpack the register contents and supply them as gdb's
   idea of the current floating point register values. */

void
supply_fpregset (fpregset_t *fpregsetp)
{
  supply_fpu_registers ((struct fpusave *) &fpregsetp->fp_reg_set);
  supply_fpa_registers ((struct fpasave *) &fpregsetp->f_wregs);
}

/*  Given a pointer to a floating point register set in /proc format
   (fpregset_t *), update the register specified by REGNO from gdb's idea
   of the current floating point register set.  If REGNO is -1, update
   them all. */

void
fill_fpregset (fpregset_t *fpregsetp, int regno)
{
  int regi;
  char *to;
  char *from;

  /* FIXME: see m68k-tdep.c for an example, for the m68k. */
}

/*
 * This doesn't quite do the same thing as the procfs.c version, but give
 * it the same name so we don't have to put an ifdef in solib.c.
 */
/* this could use elf_interpreter() from elfread.c */
int
proc_iterate_over_mappings (int (*func) (int, CORE_ADDR))
{
  vaddr_t curseg, memptr;
  pt_vseg_t pv;
  int rv, cmperr;
  sec_ptr interp_sec;
  char *interp_content;
  int interp_fd, funcstat;
  unsigned int size;
  char buf1[NBPG], buf2[NBPG];

  /*
   * The following is really vile.  We can get the name of the
   * shared library from the exec_bfd, and we can get a list of
   * each virtual memory segment, but there is no simple way to
   * find the mapped segment from the shared library (ala
   * procfs's PIOCOPENMEM).  As a pretty nasty kludge, we
   * compare the virtual memory segment to the contents of the
   * .interp file.  If they match, we assume that we've got the
   * right one.
   */

  /*
   * TODO: for attach, use XPT_OPENT to get the executable, in
   * case we're attached without knowning the executable's
   * filename.
   */

#ifdef VERBOSE_DEBUG
  printf ("proc_iter\n");
#endif
  interp_sec = bfd_get_section_by_name (exec_bfd, ".interp");
  if (!interp_sec)
    {
      return 0;
    }

  size = bfd_section_size (exec_bfd, interp_sec);
  interp_content = alloca (size);
  if (0 == bfd_get_section_contents (exec_bfd, interp_sec,
				     interp_content, (file_ptr) 0, size))
    {
      return 0;
    }

#ifdef VERBOSE_DEBUG
  printf ("proc_iter: \"%s\"\n", interp_content);
#endif
  interp_fd = open (interp_content, O_RDONLY, 0);
  if (-1 == interp_fd)
    {
      return 0;
    }

  curseg = 0;
  while (1)
    {
      rv = ptrace (PT_NEXT_VSEG, inferior_pid, &pv, curseg);
#ifdef VERBOSE_DEBUG
      printf ("PT_NEXT_VSEG: rv %d errno %d\n", rv, errno);
#endif
      if (-1 == rv)
	break;
      if (0 == rv)
	break;
#ifdef VERBOSE_DEBUG
      printf ("pv.pv_start 0x%x pv_size 0x%x pv_prot 0x%x\n",
	      pv.pv_start, pv.pv_size, pv.pv_prot);
#endif
      curseg = pv.pv_start + pv.pv_size;

      rv = lseek (interp_fd, 0, SEEK_SET);
      if (-1 == rv)
	{
	  perror ("lseek");
	  close (interp_fd);
	  return 0;
	}
      for (memptr = pv.pv_start; memptr < pv.pv_start + pv.pv_size;
	   memptr += NBPG)
	{
#ifdef VERBOSE_DEBUG
	  printf ("memptr 0x%x\n", memptr);
#endif
	  rv = read (interp_fd, buf1, NBPG);
	  if (-1 == rv)
	    {
	      perror ("read");
	      close (interp_fd);
	      return 0;
	    }
	  rv = ptrace (PT_RDATA_PAGE, inferior_pid, buf2,
		       memptr);
	  if (-1 == rv)
	    {
	      perror ("ptrace");
	      close (interp_fd);
	      return 0;
	    }
	  cmperr = memcmp (buf1, buf2, NBPG);
	  if (cmperr)
	    break;
	}
      if (0 == cmperr)
	{
	  /* this is it */
	  funcstat = (*func) (interp_fd, pv.pv_start);
	  break;
	}
    }
  close (interp_fd);
  return 0;
}
