//==========================================================================
//
//      monitor.c
//
//      Monitor shell and main routines for CygMON the Wonder Monitor
//
//==========================================================================
//####COPYRIGHTBEGIN####
//                                                                          
// -------------------------------------------                              
// The contents of this file are subject to the Red Hat eCos Public License 
// Version 1.1 (the "License"); you may not use this file except in         
// compliance with the License.  You may obtain a copy of the License at    
// http://www.redhat.com/                                                   
//                                                                          
// Software distributed under the License is distributed on an "AS IS"      
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the 
// License for the specific language governing rights and limitations under 
// the License.                                                             
//                                                                          
// The Original Code is eCos - Embedded Configurable Operating System,      
// released September 30, 1998.                                             
//                                                                          
// The Initial Developer of the Original Code is Red Hat.                   
// Portions created by Red Hat are                                          
// Copyright (C) 1998, 1999, 2000 Red Hat, Inc.                             
// All Rights Reserved.                                                     
// -------------------------------------------                              
//                                                                          
//####COPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s):    
// Contributors: gthomas
// Date:         1999-10-20
// Purpose:      Monitor shell and main routines for CygMON the Wonder Monitor
// Description:  
//               
//
//####DESCRIPTIONEND####
//
//=========================================================================

/* Platform-independent code for cygmon */

#include <stdlib.h>
#include <unistd.h>
#ifdef HAVE_BSP
#include <bsp/bsp.h>
#include <bsp/cpu.h>
#include <bsp/hex-utils.h>
#endif
#include <monitor.h>
#ifdef HAVE_BSP
#include "cpu_info.h"
#endif
#include <ledit.h>
#include <signal.h>
#include <string.h>
#include <ctype.h>

#if USE_CYGMON_PROTOTYPES
/* Use common prototypes */
/* Some of the composed board.h files compose these
   prototypes redundently, but if they dont,
   these are the common definitions */
#include "fmt_util.h"   /* Interface to string formatting utilities */
#include "tservice.h"   /* Interface to target specific services */
#include "generic-stub.h" /* from libstub */
#endif /* USE_CYGMON_PROTOTYPES */

static int  cygmon_handle_exception (int sigval);
static void monitor_take_control (void);

#if CYGMON_SYSTEM_SERVICES
extern int process_syscall (int syscall_num);
#endif

int stub_is_active = 0;
mem_addr_t last_pc;

#ifdef HAVE_BSP
static int mon_dbg_handler(int exc_nr, void *regs);
static int mon_kill_handler(int exc_nr, void *regs);

#define __is_breakpoint_function() (get_pc() == (target_register_t)bsp_breakinsn)

/* Global pointer to current set of saved registers. */
void *mon_saved_regs;

/* Original BSP debug vector replaced by monitor. */
static bsp_handler_t old_dbg_vec;

#else
static int handle_signal (int signal_val);

__PFI user_signal_handler = NULL;
#endif

#ifdef __ECOS__
extern (*__process_exception_vec)(int);
#endif


#ifdef MONITOR_CONTROL_INTERRUPTS
/* This is set if the user wants interrupts enabled in the monitor. */
static int monitor_interrupts_enabled;
#endif


#if NOMAIN
int monitor_main (int argc, char **argv) /* Suppress default main() junk */
#else
int main (int argc, char **argv)     
#endif
{
#ifdef HAVE_BSP
  int  cur_port;
  struct bsp_comm_info comm_info;
#else
  /* Set up exception handling traps */
  initialize_stub ();
#endif

  initialize_mon ();

#ifdef HAVE_BSP
  /* get info on debug channel */
  cur_port = bsp_set_debug_comm(-1);
  bsp_sysinfo(BSP_INFO_COMM, cur_port, &comm_info);

  /*
   * If we're using a network interface, don't install
   * the cygmon vectors. Currently, we only support stub
   * mode over a network connection.
   */
  if (comm_info.kind != BSP_COMM_ENET)
    {
      xprintf("\n");
      version ();

      /* replace original BSP debug and kill handler with ours */
      old_dbg_vec = bsp_install_dbg_handler(mon_dbg_handler);
      (void)bsp_install_kill_handler(mon_kill_handler);
    }
  else
    {
      /* This forces the console to use the gdb channel. */
      bsp_set_console_comm(cur_port);
    }
#else
  xprintf("\n");
  version ();

  __process_exception_vec = cygmon_handle_exception;
  __process_exit_vec = monitor_take_control;
#if CYGMON_SYSTEM_SERVICES
  __process_syscall_vec = process_syscall;
#endif  
  __process_signal_vec = handle_signal;
  __init_vec = install_breakpoints;
  __cleanup_vec = clear_breakpoints;
#endif

#if 0
#ifdef __ECOS__
  __process_exception_vec = cygmon_handle_exception;
#endif
#endif

  while (1)
    {
      breakpoint ();
      if (switch_to_stub_flag)
        {
	  xprintf("Switching to stub\n");
	  switch_to_stub_flag = 0;
	}
    }

  /* never reached */
  exit (0);
}


/* Transfer control to gdb stub */
 
int 
transfer_to_stub ()
{
  /* Return back to the exception handler, but the exception handler
     should invoke the stub's exception handler instead of ours. */
#ifdef HAVE_BSP
  (void)bsp_install_dbg_handler(old_dbg_vec);
#else
  __switch_to_stub ();
#endif

  /* The stub is now active. */
  stub_is_active = 1;
  return -1;
}

void
clear_user_state (void)
{
#ifdef HAS_TIMER
  if (__timer_enabled ())
    __settimer (0, 0);
#endif

  clear_breakpoints ();

#ifdef HAVE_BSP
  bsp_singlestep_cleanup(mon_saved_regs);
#else
  user_signal_handler = NULL;
  __clear_single_step ();
#endif
}

static void
monitor_take_control (void)
{
  stub_is_active = 0;

#ifdef HAVE_BSP
  /* replace original BSP debug trap handler with ours */
  (void)bsp_install_dbg_handler(mon_dbg_handler);
#else
  clear_user_state ();
  __process_exception_vec = cygmon_handle_exception;
#endif
}


#ifdef MONITOR_CONTROL_INTERRUPTS
void
monitor_enable_interrupts (void)
{
  monitor_interrupts_enabled = 1;
  enable_interrupts ();
}

void
monitor_disable_interrupts (void)
{
  monitor_interrupts_enabled = 0;
  disable_interrupts ();
}

int
monitor_interrupt_state (void)
{
  return monitor_interrupts_enabled;
}
#endif

static int
cygmon_handle_exception (int sigval)
{
  target_register_t pc;

#ifdef MONITOR_CONTROL_INTERRUPTS
  if (monitor_interrupts_enabled)
    {
      if (! __in_interrupt)
	enable_interrupts ();
    }
#endif

#ifdef TARGET_EXCEPTION_CODE
  TARGET_EXCEPTION_CODE
#endif

#ifndef HAVE_BSP
  if (sigval != SIGKILL)
    if (handle_signal (sigval) == 0)
      return 0;
#endif

  clear_user_state ();

  /* We may want to tweak the PC to point at the faulting instruction,
     for example. (breakpoints on x86). */
#ifdef TARGET_ADJUST_PC
  TARGET_ADJUST_PC
#endif

  pc = get_pc();
  MAKE_STD_ADDR (pc, &last_pc);

  if ((sigval == SIGTRAP) && __is_breakpoint_function())
    {
      /*
       * This is the initial breakpoint inserted by the BSP
       * Don't print anything for this as it is confusing
       */
    }
  else
    {
      if (sigval == SIGTRAP)
          xprintf ("Hit breakpoint");
      else
          xprintf ("Got signal %d", sigval);

      xprintf (" at 0x%s\n", int2str (pc, 16, sizeof (target_register_t) * 2));

#ifdef DISASSEMBLER
      if (!__is_breakpoint_function ())
	{
	  do_dis (&last_pc);
	  flush_dis ();
	}
#endif
    }

  monitor_take_control ();

  return monitor_loop ();
}

#ifndef HAVE_BSP
/* Returns 0 if the program should restart at the point at which the
   signal was received, -1 otherwise. */
static int
handle_signal (int signal)
{
  if (signal == SIGKILL)
    return -1;

  if (user_signal_handler != NULL)
    {
      int result = user_signal_handler (signal);
      switch (result)
	{
	  /* Don't ignore potential hardware signals. */
	case 3:
	  if (signal == SIGSEGV || signal == SIGBUS || signal == SIGFPE
	      || signal == SIGTRAP || signal == SIGILL)
	    return -1;

	case 0:
	  return 0;

	default:
	case 1:
	case 2:
	  return -1;
	}
    }
    return -1;
}
#endif


void
version (void)
{
#ifdef HAVE_BSP
  struct bsp_platform_info platform;
  struct bsp_mem_info      mem;
  int                      i;
  unsigned long            u, totmem, topmem;
#endif
  extern char *build_date;

  xprintf ("Cygmon, the Cygnus ROM monitor.\n");
  xprintf ("Copyright(c) 1997, 1998, 1999, 2000 Red Hat\n\n");
  xprintf ("Version: %s\nThis image was built on %s\n\n",
	   VERSION, build_date);

#ifdef HAVE_BSP
  bsp_sysinfo(BSP_INFO_PLATFORM, &platform);

  totmem = topmem = 0;
  i = 0;
  while (bsp_sysinfo(BSP_INFO_MEMORY, i++, &mem) == 0)
    {
      if (mem.kind == BSP_MEM_RAM)
        {
          totmem += mem.nbytes;
          u = (unsigned long)mem.virt_start + mem.nbytes;
	  if (u > topmem)
	    topmem = u;
        }
    }

  xprintf("CPU: %s\n", platform.cpu);
  xprintf("Board: %s\n", platform.board);
  if (*(platform.extra))
      xprintf("%s\n", platform.extra);
  xprintf("Total RAM: %d bytes\n", totmem);
  xprintf("Top of RAM: 0x%x\n", topmem);
#endif
}


#ifdef HAVE_BSP
static int
mon_kill_handler(int exc_nr, void *regs)
{
    monitor_take_control();
    return 1;
}


static int
mon_dbg_handler(int exc_nr, void *regs)
{
    int sig;
    unsigned long cur_pc;

    mon_saved_regs = regs;
    sig = bsp_get_signal(exc_nr, regs);

    cygmon_handle_exception(sig);

    cur_pc = bsp_get_pc(regs);
    if (cur_pc == (unsigned long)bsp_breakinsn)
	bsp_skip_instruction(regs);

    if (!stub_is_active)
	install_breakpoints();

    return 1;
}
#endif
