/*
 * BedBug Functions
 */

#include <ppcboot.h>
#include <command.h>
#include <linux/ctype.h>
#include <net.h>

#include <cmd_bedbug.h>
#include <bedbug/bedbug.h>
#include <bedbug/regs.h>
#include <bedbug/ppc.h>
#include <bedbug/elf.h>

#if (CONFIG_COMMANDS & CFG_CMD_BEDBUG)

#ifndef MAX
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#endif

extern void show_regs __P((struct pt_regs*));
extern int  run_command __P((const char*,bd_t*,int));
extern char console_buffer[];

ulong	      dis_last_addr = 0;        /* Last address disassembled   */
ulong	      dis_last_len = 20;        /* Default disassembler length */
CPU_DEBUG_CTX bug_ctx;                  /* Bedbug context structure    */


/* ======================================================================
 * PPCBOOT's puts function does not append a newline, so the bedbug stuff
 * will use this for the output of the dis/assembler.                    
 * ====================================================================== */

int bedbug_puts(const char *str)
{
  /* -------------------------------------------------- */

  printf( "%s\r\n", str );
  return 0;
} /* bedbug_puts */



/* ======================================================================
 * Initialize the bug_ctx structure used by the bedbug debugger.  This is
 * specific to the CPU since each has different debug registers and
 * settings.
 * ====================================================================== */

void bedbug_init( void )
{
  /* -------------------------------------------------- */

#if defined(CONFIG_4xx)
  void bedbug405_init( void );
  bedbug405_init();
#elif defined(CONFIG_MPC860)
  void bedbug860_init( void );
  bedbug860_init();
#endif

#if defined(CONFIG_MPC8240) || defined(CONFIG_MPC8260)
  /* Processors that are 603e core based */
  void bedbug603e_init( void );
  
  bedbug603e_init();
#endif

  return;
} /* bedbug_init */



/* ======================================================================
 * Entry point from the interpreter to the disassembler.  Repeated calls
 * will resume from the last disassembled address.
 * ====================================================================== */

void do_bedbug_dis (cmd_tbl_t *cmdtp, bd_t *bd, int flag, int argc,
		    char *argv[])
{
  ulong	addr;   /* Address to start disassembly from    */
  ulong	len;    /* # of instructions to disassemble     */
  /* -------------------------------------------------- */

  /* Setup to go from the last address if none is given */
  addr = dis_last_addr;
  len  = dis_last_len;
    
  if (argc < 2)
  {
    printf ("Usage:\n%s\n", cmdtp->usage);
    return;
  }
    
  if(( flag & CMD_FLAG_REPEAT ) == 0 )
  {
    /* New command */
    addr = simple_strtoul( argv[1], NULL, 16 );

    /* If an extra param is given then it is the length */
    if( argc > 2 )
      len = simple_strtoul( argv[2], NULL, 16 );
  }

  /* Run the disassembler */
  disppc( (unsigned char *)addr, 0, len, bedbug_puts, F_RADHEX );

  dis_last_addr = addr + (len * 4);
  dis_last_len = len;
} /* do_bedbug_dis */



/* ======================================================================
 * Entry point from the interpreter to the assembler.  Assembles
 * instructions in consecutive memory locations until a '.' (period) is
 * entered on a line by itself.
 * ====================================================================== */

void do_bedbug_asm (cmd_tbl_t *cmdtp, bd_t *bd, int flag, int argc,
		    char *argv[])
{
  long		mem_addr;               /* Address to assemble into     */
  unsigned long instr;                  /* Machine code for text        */
  char		prompt[ 15 ];           /* Prompt string for user input */
  int		asm_err;                /* Error code from the assembler*/
  /* -------------------------------------------------- */
		
  if (argc < 2)
  {
    printf ("Usage:\n%s\n", cmdtp->usage);
    return;
  }
    
  printf( "\nEnter '.' when done\n" );
  mem_addr = simple_strtoul( argv[ 1 ], NULL, 16 );

  while( 1 )
  {
    putc( '\n' );
    disppc( (unsigned char *)mem_addr, 0, 1, bedbug_puts, F_RADHEX );

    sprintf( prompt, "%08lx:    ", mem_addr );
    readline( prompt );

    if( console_buffer[ 0 ] && strcmp( console_buffer, "." ))
    {
      if(( instr = asmppc( mem_addr, console_buffer, &asm_err )) != 0 )
      {
	*(unsigned long *)mem_addr = instr;
	mem_addr += 4;
      }
      else
      {
	printf( "*** Error: %s ***\n", asm_error_str( asm_err ));
      }
    }
    else
    {
      break;
    }
  }
} /* do_bedbug_asm */



/* ======================================================================
 * Used to set a break point from the interpreter.  Simply calls into the
 * CPU-specific break point set routine.
 * ====================================================================== */

void do_bedbug_break (cmd_tbl_t *cmdtp, bd_t *bd, int flag, int argc,
                      char *argv[])
{
  /* -------------------------------------------------- */

  if( bug_ctx.do_break )
    (*bug_ctx.do_break)( cmdtp, bd, flag, argc, argv );
} /* do_bedbug_break */



/* ======================================================================
 * Called from the debug interrupt routine.  Simply calls the CPU-specific
 * breakpoint handling routine.
 * ====================================================================== */

void do_bedbug_breakpoint( struct pt_regs *regs )
{
  /* -------------------------------------------------- */

  if( bug_ctx.break_isr )
    (*bug_ctx.break_isr)( regs );
} /* do_bedbug_breakpoint */



/* ======================================================================
 * Called from the CPU-specific breakpoint handling routine.  Enter a
 * mini main loop until the stopped flag is cleared from the breakpoint
 * context.
 *
 * This handles the parts of the debugger that are common to all CPU's.
 * ====================================================================== */

void bedbug_main_loop( unsigned long addr, struct pt_regs *regs )
{
  int		len;                    /* Length of command line */
  int           flag;                   /* Command flags          */
  int           rc;                     /* Result from run_command*/
  char          prompt_str[ 20 ];       /* Prompt string          */
  static char   lastcommand[ CFG_CBSIZE ] = {0}; /* previous command */
  /* -------------------------------------------------- */

  if( bug_ctx.clear )
    (*bug_ctx.clear)( bug_ctx.current_bp );
  
  printf( "Breakpoint %d: ", bug_ctx.current_bp );
  disppc( (unsigned char *)addr, 0, 1, bedbug_puts, F_RADHEX );

  bug_ctx.stopped = 1;
  bug_ctx.regs = regs;

  sprintf( prompt_str, "BEDBUG.%d =>", bug_ctx.current_bp );

  /* A miniature main loop */
  while( bug_ctx.stopped )
  {
    len = readline( prompt_str );
    
    flag = 0;	/* assume no special flags for now */

    if (len > 0)
      strcpy( lastcommand, console_buffer );
    else if( len == 0 )
      flag |= CMD_FLAG_REPEAT;

    if (len == -1)
      printf ("<INTERRUPT>\n");
    else
      rc = run_command( lastcommand, bug_ctx.bd, flag );

    if (rc <= 0) {
      /* invalid command or not repeatable, forget it */
      lastcommand[0] = 0;
    }
  }

  bug_ctx.regs = NULL;
  bug_ctx.current_bp = 0;

  return;
} /* bedbug_main_loop */



/* ======================================================================
 * Interpreter command to continue from a breakpoint.  Just clears the
 * stopped flag in the context so that the breakpoint routine will
 * return.
 * ====================================================================== */

void do_bedbug_continue (cmd_tbl_t *cmdtp, bd_t *bd, int flag, int argc,
                         char *argv[])
{
  /* -------------------------------------------------- */

  if( ! bug_ctx.stopped )
  {
    printf( "Not at a breakpoint\n" );
    return;
  }
  
  bug_ctx.stopped = 0;
  return;
} /* do_bedbug_continue */



/* ======================================================================
 * Interpreter command to continue to the next instruction, stepping into
 * subroutines.  Works by calling the find_next_addr() routine to compute
 * the address passes control to the CPU-specific set breakpoint routine
 * for the current breakpoint number.
 * ====================================================================== */

void do_bedbug_step (cmd_tbl_t *cmdtp, bd_t *bd, int flag, int argc,
                     char *argv[])
{
  unsigned long addr;   /* Address to stop at */
  /* -------------------------------------------------- */

  if( ! bug_ctx.stopped )
  {
    printf( "Not at a breakpoint\n" );
    return;
  }

  if( !find_next_address( (unsigned char *)&addr, FALSE, bug_ctx.regs ))
    return;

  if( bug_ctx.set )
    (*bug_ctx.set)( bug_ctx.current_bp, addr );
  
  bug_ctx.stopped = 0;
  return;
} /* do_bedbug_step */



/* ======================================================================
 * Interpreter command to continue to the next instruction, stepping over
 * subroutines.  Works by calling the find_next_addr() routine to compute
 * the address passes control to the CPU-specific set breakpoint routine
 * for the current breakpoint number.
 * ====================================================================== */

void do_bedbug_next (cmd_tbl_t *cmdtp, bd_t *bd, int flag, int argc,
                     char *argv[])
{
  unsigned long addr;   /* Address to stop at */
  /* -------------------------------------------------- */

  if( ! bug_ctx.stopped )
  {
    printf( "Not at a breakpoint\n" );
    return;
  }

  if( !find_next_address( (unsigned char *)&addr, TRUE, bug_ctx.regs ))
    return;

  if( bug_ctx.set )
    (*bug_ctx.set)( bug_ctx.current_bp, addr );

  bug_ctx.stopped = 0;
  return;
} /* do_bedbug_next */



/* ======================================================================
 * Interpreter command to print the current stack.  This assumes an EABI
 * architecture, so it starts with GPR R1 and works back up the stack.
 * ====================================================================== */

void do_bedbug_stack (cmd_tbl_t *cmdtp, bd_t *bd, int flag, int argc,
                      char *argv[])
{
  unsigned long sp;             /* Stack pointer                */
  unsigned long func;           /* LR from stack                */
  int           depth;          /* Stack iteration level        */
  int           skip = 1;       /* Flag to skip the first entry */
  unsigned long top;            /* Top of memory address        */
  /* -------------------------------------------------- */

  if( ! bug_ctx.stopped )
  {
    printf( "Not at a breakpoint\n" );
    return;
  }

  top = bd->bi_memstart + bd->bi_memsize;
  depth = 0;

  printf( "Depth     PC\n" );
  printf( "-----  --------\n" );
  printf( "%5d  %08lx\n", depth++, bug_ctx.regs->nip );

  sp = bug_ctx.regs->gpr[ 1 ];
  func = *(unsigned long *)(sp+4);

  while(( func < top ) && ( sp < top ))
  {
    if( !skip )
      printf( "%5d  %08lx\n", depth++, func );
    else
      --skip;
    
    sp = *(unsigned long *)sp;
    func = *(unsigned long *)(sp+4);
  }

  return;
} /* do_bedbug_stack */



/* ======================================================================
 * Interpreter command to dump the registers.  Calls the CPU-specific
 * show registers routine.
 * ====================================================================== */

void do_bedbug_rdump (cmd_tbl_t *cmdtp, bd_t *bd, int flag, int argc,
                      char *argv[])
{
  /* -------------------------------------------------- */

  if( ! bug_ctx.stopped )
  {
    printf( "Not at a breakpoint\n" );
    return;
  }

  show_regs( bug_ctx.regs );
  return;
} /* do_bedbug_rdump */



/* ======================================================================
 * Interpreter command to boot an arbitrary ELF image from memory.
 * ====================================================================== */

void do_bootelf( cmd_tbl_t *cmdtp, bd_t *bd, int flag, int argc, char *argv[] )
{
  unsigned long	addr;   /* Address of the ELF image     */
  unsigned long	rc;     /* Return value from user code  */
  /* -------------------------------------------------- */

  if (argc < 2)
    addr = load_addr;
  else
    addr = simple_strtoul( argv[ 1 ], NULL, 16 );

  if( ! valid_elf_image( addr ))
    return;
  
  addr = load_elf_image( addr );

  printf ("## Starting application at 0x%08lx ...\n", addr);

  /*
   * pass address parameter as argv[0] (aka command name),
   * and all remaining args
   */
  rc = ((ulong (*)(bd_t *, int, char *[]))addr) (bd, --argc, &argv[1]);

  printf ("## Application terminated, rc = 0x%lx\n", rc);
} /* do_bootelf */



/* ======================================================================
 * Interpreter command to boot VxWorks from a memory image.  The image can
 * be either an ELF image or a raw binary.  Will attempt to setup the
 * bootline and other parameters correctly.
 * ====================================================================== */

void do_bootvx( cmd_tbl_t *cmdtp, bd_t *bd, int flag, int argc, char *argv[] )
{
  unsigned long	addr;                   /* Address of image            */
  unsigned long	bootaddr;               /* Address to put the bootline */
  char *	bootline;               /* Text of the bootline        */
  char *	tmp;                    /* Temporary char pointer      */
#if defined(CONFIG_4xx) || defined(CONFIG_IOP480)
  char          build_buf[ 80 ];        /* Buffer for building the bootline */
#endif
  /* -------------------------------------------------- */

  /* Check the loadaddr variable.  If we don't know where the image is
     then we're done. */
  
  if(( tmp = getenv( "loadaddr" )) != NULL )
  {
    addr = simple_strtoul( tmp, NULL, 16 );
  }
  else
  {
    printf( "No load address provided\n" );
    return;
  }

  /* Check to see if we need to tftp the image ourselves before starting */
  
  if(( argc == 2 ) && ( strcmp( argv[ 1 ], "tftp" ) == 0 ))
  {
    if( NetLoop( bd, TFTP ) == 0 )
      return;

    printf( "Automatic boot of VxWorks image at address 0x%08lx ... \n",
            addr );
  }

  /* This should equate to NV_RAM_ADRS + NV_BOOT_OFFSET + NV_ENET_OFFSET
     from the VxWorks BSP header files.  This will vary from board to board */

#if defined(CONFIG_WALNUT405)
  tmp = (char *)CFG_NVRAM_BASE_ADDR + 0x500;
  memcpy( (char *)tmp, (char *)&bd->bi_enetaddr[3], 3 );
#elif defined(CONFIG_CPCI405)
  tmp = (char *)CFG_NVRAM_BASE_ADDR + CFG_NVRAM_VXWORKS_OFFS;
  memcpy( (char *)tmp, (char *)&bd->bi_enetaddr[0], 6 );
#elif defined(CONFIG_OCRTC)
  tmp = (char *)CFG_ETHERNET_MAC_ADDR;
  memcpy( (char *)tmp, (char *)&bd->bi_enetaddr[0], 6 );
#else
  printf( "## Ethernet MAC address not copied to NV RAM\n" );
#endif
  
  /* Use bootaddr to find the location in memory that VxWorks will
     look for the bootline string.  The default value for PowerPC
     is LOCAL_MEM_LOCAL_ADRS + BOOT_LINE_OFFSET which defaults
     to 0x4200 */
  
  if(( tmp = getenv( "bootaddr" )) == NULL )
    bootaddr = 0x4200;
  else
    bootaddr = simple_strtoul( tmp, NULL, 16 );

  /* Check to see if the bootline is defined in the 'bootargs' parameter.
     If it is not defined, we may be able to construct the info */
  
  if(( bootline = getenv( "bootargs" )) != NULL )
  {
    memcpy( (void *)bootaddr, bootline, MAX( strlen( bootline ), 255 ));
  }
  else
  {
#if defined(CONFIG_4xx)
    sprintf( build_buf, "ibmEmac(0,0)" );

    if(( tmp = getenv( "hostname" )) != NULL )
    {
      sprintf( &build_buf[ strlen( build_buf - 1 ) ], "host:%s ", tmp );
    }
    else
    {
      sprintf( &build_buf[ strlen( build_buf - 1 ) ], ": " );
    }

    if(( tmp = getenv( "ipaddr" )) != NULL )
    {
      sprintf( &build_buf[ strlen( build_buf - 1 ) ], "e=%s ", tmp );
    }
    memcpy( (void *)bootaddr, build_buf, MAX( strlen( build_buf ), 255 ));
#elif defined(CONFIG_IOP480)
    sprintf( build_buf, "dc(0,0)" );

    if(( tmp = getenv( "hostname" )) != NULL )
    {
      sprintf( &build_buf[ strlen( build_buf - 1 ) ], "host:%s ", tmp );
    }
    else
    {
      sprintf( &build_buf[ strlen( build_buf - 1 ) ], ": " );
    }

    if(( tmp = getenv( "ipaddr" )) != NULL )
    {
      sprintf( &build_buf[ strlen( build_buf - 1 ) ], "e=%s ", tmp );
    }
    memcpy( (void *)bootaddr, build_buf, MAX( strlen( build_buf ), 255 ));
#else

    /* I'm not sure what the device should be for other
       PPC flavors, the hostname and ipaddr should be ok to just copy */
    
    printf( "No bootargs defined\n" );
    return;
#endif
  }

  /* If the data at the load address is an elf image, then treat it like
     an elf image.  Otherwise, assume that it is a binary image */
  
  if( valid_elf_image( addr ))
  {
    addr = load_elf_image( addr );
  }
  else
  {
    printf( "## Not an ELF image, assuming binary\n" );
    /* leave addr as load_addr */
  }

  printf ("## Starting vxWorks at 0x%08lx ...\n", addr);

  ((void (*)(void))addr)();

  printf ("## vxWorks terminated\n" );
} /* do_bootvx */



/* ======================================================================
 * Determine if a valid ELF image exists at the given memory location.
 * First looks at the ELF header magic field, the makes sure that it is
 * executable and makes sure that it is for a PowerPC.
 * ====================================================================== */

int valid_elf_image( unsigned long addr )
{
  Elf32_Ehdr *	ehdr;   /* Elf header structure pointer */
  /* -------------------------------------------------- */

  ehdr = (Elf32_Ehdr *)addr;

  if( ! IS_ELF( *ehdr )) {
    printf( "## No elf image at address 0x%08lx\n", addr );
    return 0;
  }

  if( ehdr->e_type != ET_EXEC ) {
    printf( "## Not a 32-bit elf image at address 0x%08lx\n", addr );
    return 0;
  }

  if( ehdr->e_machine != EM_PPC ) {
    printf( "## Not a PowerPC elf image at address 0x%08lx\n", addr );
    return 0;
  }

  return 1;
} /* valid_elf_image */



/* ======================================================================
 * A very simple elf loader, assumes the image is valid, returns the
 * entry point address.
 * ====================================================================== */

unsigned long load_elf_image( unsigned long addr )
{
  Elf32_Ehdr *		ehdr;           /* Elf header structure pointer     */
  Elf32_Shdr *		shdr;           /* Section header structure pointer */
  unsigned char *	strtab = 0;     /* String table pointer             */
  unsigned char *	image;          /* Binary image pointer             */
  int			i;              /* Loop counter                     */
  /* -------------------------------------------------- */
  
  ehdr = (Elf32_Ehdr *)addr;

  /* Find the section header string table for output info */
  shdr = (Elf32_Shdr *)(addr + ehdr->e_shoff +
			(ehdr->e_shstrndx * sizeof( Elf32_Shdr )));
  
  if( shdr->sh_type == SHT_STRTAB )
    strtab = ( unsigned char *)(addr + shdr->sh_offset);

  /* Load each appropriate section */
  for( i = 0; i < ehdr->e_shnum; ++i ) {
    shdr = (Elf32_Shdr *)(addr + ehdr->e_shoff + (i * sizeof( Elf32_Shdr )));

    if( !( shdr->sh_flags & SHF_ALLOC )
	|| shdr->sh_type == SHT_NOBITS
	|| shdr->sh_addr == 0
	|| shdr->sh_size == 0 )
      continue;

    if( strtab ) {
      printf( "Loading %s @ 0x%08lx (%ld bytes)\n",
	      &strtab[ shdr->sh_name ], (unsigned long)shdr->sh_addr,
              (long)shdr->sh_size );
    }
    
    image = (unsigned char *)addr + shdr->sh_offset;
    memcpy( (void *)shdr->sh_addr, (const void *)image, shdr->sh_size );
  }

  return ehdr->e_entry;
} /* load_elf_image */


/* ====================================================================== */
#endif	/* CFG_CMD_BEDBUG */


/*
 * Copyright (c) 2001 William L. Pitts
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are freely
 * permitted provided that the above copyright notice and this
 * paragraph and the following disclaimer are duplicated in all
 * such forms.
 * 
 * This software is provided "AS IS" and without any express or
 * implied warranties, including, without limitation, the implied
 * warranties of merchantability and fitness for a particular
 * purpose.
 */
