/* VxWorks coredump target, for GDB. 

This file implements new target code for gdb that allows a flat memory
dump of a vxworks system to be used as core dump that can be examined
using gdb.new target code for gdb that allows a flat memory
dump of a vxworks system to be used as core dump that can be examined
using gdb.    This allows for a simple post-mortem debug utility.

Written by Hwa-Jin Bae, bae@mail.com, Piedmont California

This file works as a 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 "frame.h"
#include "inferior.h"
#include "target.h"
#include "gdbcmd.h"
#include "language.h"
#include "symfile.h"
#include "objfiles.h"

#include <sys/types.h>
#include <unistd.h>

#include <sys/param.h>
#include <fcntl.h>
#include "gdb_string.h"

#include "gdbcore.h"
#include "symtab.h"

#include <ctype.h>
#include "gdb_stat.h"
#ifndef O_BINARY
#define O_BINARY 0
#endif

/* Prototypes for local functions */

extern int info_verbose;
extern int errno;

/* Whether to open exec and core files read-only or read-write.	 */

#define VXDUMP_MEM_START 0x8000000
#define VXDUMP_MEM_END 0x8400000

CORE_ADDR vxdump_start = VXDUMP_MEM_START;
CORE_ADDR vxdump_end   = VXDUMP_MEM_END;

int vxdump_core_dump_fd = -1;

/* Forward decl */

extern struct target_ops vxdump_ops;

/* ARGSUSED */
static void
vxdump_close (quitting)
     int quitting;
{
  close(vxdump_core_dump_fd);
  vxdump_core_dump_fd = -1;
}

void
vxdump_task_gather()
{
  struct minimal_symbol *ms;
  int tid;
  int next;
  int tmp;

  ms = lookup_minimal_symbol("activeQHead",0,0);
  if (!ms)
    {
      printf_unfiltered("vxdump_task_gather: can't get activeQHead\n");
      return;
    }
  vxdump_xfer_memory(ms->ginfo.value.address, &tid, 4, 0, 0);
  printf_unfiltered("vxdump_task_gather: activeQHead 0x%x\n", tid);

  vxdump_xfer_memory(tid, &next, 4, 0, 0);

  tid -= 32;

  inferior_pid = tid;
  flush_cached_frames ();
  registers_changed ();
  stop_pc = read_pc();
  select_frame(get_current_frame(), 0);

  add_thread(tid);

  while (next != 0)
    {
      tid = next - 32;
      add_thread(tid);
      if (vxdump_xfer_memory(next, &tmp, 4, 0, 0) == 0)
        break;
      next = tmp;
    }
}

/*  Process the first arg in ARGS as the new exec file.

    Note that we have to explicitly ignore additional args, since we can
    be called from file_command(), which also calls symbol_file_command()
    which can take multiple args. */

void
vxdump_file_command (args, from_tty)
     char *args;
     int from_tty;
{
  char **argv;
  char *filename;
  int fd;

  target_preopen (from_tty);

  /* Remove any previous exec file.  */
  unpush_target (&vxdump_ops);

  /* Now open and digest the file the user requested, if any.  */

  if (args)
    {
      /* Scan through the args and pick up the first non option arg
	 as the	filename.  */

      argv = buildargv (args);
      if (argv == NULL)
        nomem (0);

      make_cleanup (freeargv, (char *) argv);

      for (; (*argv != NULL) && (**argv == '-'); argv++) {;}
      if (*argv == NULL)
        error ("no vxdump core dump file name was specified");

      filename = tilde_expand (*argv);
      make_cleanup (free, filename);
      
      fd = open (filename,  O_RDONLY|O_BINARY, 0);
      if (fd < 0)
        perror_with_name (filename);

      vxdump_core_dump_fd = fd;

      printf_unfiltered("vxdump core dump file <%s> opened\n", filename);
      validate_files ();
      push_target (&vxdump_ops);
      vxdump_task_gather(); 
    }
  else if (from_tty)
    printf_unfiltered ("No vxdump core dump file now.\n");
}

int
vxdump_xfer_memory (memaddr, myaddr, len, write, target)
     CORE_ADDR memaddr;
     char *myaddr;
     int len;
     int write;
     struct target_ops *target;
{
  if (memaddr < vxdump_start || memaddr > vxdump_end)
    {
      printf_unfiltered("address 0x%x is out of range\n", memaddr);
      return 0;
    }
  if (lseek(vxdump_core_dump_fd, memaddr - vxdump_start, SEEK_SET) == -1)
    {
      printf_unfiltered("vxdump_xfer_memory: lseek error, errno=%d\n",
			errno);
      return 0;
    }
  if (read(vxdump_core_dump_fd, myaddr, len) == -1)
    {
      printf_unfiltered("vxdump_xfer_memory: read error, errno=%d\n",
			errno);
      return 0;
    }
  return len;
}

static void
vxdump_files_info (t)
     struct target_ops *t;
{
}

int
vxdump_thread_alive(int thread)
{
  return 1;
}

static void
vxdump_fetch_registers (regno)
     int regno;
{
  int i;
  char regs[REGISTER_BYTES];
  int reg_addr;

  memset (regs, 0, REGISTER_BYTES);

  reg_addr = inferior_pid + 0x118 + 20; /* offset into WIND_TCB */
  vxdump_xfer_memory(reg_addr, regs, REGISTER_BYTES);

  for (i = 0; i < NUM_REGS; i++)
    supply_register (i, &regs[REGISTER_BYTE(i)]);
}

/* If mourn is being called in all the right places, this could be say
   gdb internal error (since generic_mourn calls breakpoint_init_inferior). */

static int
ignore (addr, contents)
     CORE_ADDR addr;
     char *contents;
{
  return 0;
}


struct target_ops vxdump_ops = {
  "vxdump",			/* to_shortname	*/
  "vxdump core dump file",      /* to_longname */
  "Use a vxdump core dump file as a target.\n\
Specify the filename of the vxdump core dump file.", /* to_doc */
  vxdump_file_command,  /* to_open */
  vxdump_close,	        /* to_close */
  find_default_attach,		/* to_attach */
  0,				/* to_detach */
  0,				/* to_resume */
  0,				/* to_wait */
  vxdump_fetch_registers,       /* to_fetch_registers */
  0,				/* to_store_registers */
  0,				/* to_prepare_to_store */
  vxdump_xfer_memory,   /* to_xfer_memory */
  vxdump_files_info,		/* to_files_info */
  ignore,			/* to_insert_breakpoint	*/
  ignore,			/* to_remove_breakpoint	*/
  0,				/* to_terminal_init */
  0,				/* to_terminal_inferior	*/
  0,				/* to_terminal_ours_for_output */
  0,				/* to_terminal_ours */
  0,				/* to_terminal_info */
  0,				/* to_kill */
  0,				/* to_load */
  0,				/* to_lookup_symbol */
  find_default_create_inferior, /* to_create_inferior */
  0,				/* to_mourn_inferior */
  0,				/* to_can_run */
  0,				/* to_notice_signals */
  vxdump_thread_alive,  /* to_thread_alive */
  0,				/* to_stop */
  vxdump_stratum,		/* to_stratum */
  0,				/* to_next */
  0,				/* to_has_all_memory */
  1,				/* to_has_memory */
  1,				/* to_has_stack	*/
  1,				/* to_has_registers */
  0,				/* to_has_execution */
  0,				/* to_sections */
  0,				/* to_sections_end */
  OPS_MAGIC,			/* to_magic */
};

void
_initialize_vxdump()
{
  struct cmd_list_element *c;
  static struct cmd_list_element *task_cmd_list = NULL;
  extern struct cmd_list_element *cmdlist;

  c = add_cmd ("vxdump-file", class_files, vxdump_file_command,
	   "Use	FILE as vxdump core dump for getting contents of pure memory.\n\
If FILE cannot be found as specified, your execution directory path\n\
is searched for a command of that name.\n\
No arg means have no vxdump core dump file.", &cmdlist);
  c->completer = filename_completer;
  add_target (&vxdump_ops);
}
