364 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			364 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			C
		
	
	
	
| /*
 | |
|  * Copyright (c) 1996 Cygnus Support
 | |
|  *
 | |
|  * The authors hereby grant permission to use, copy, modify, distribute,
 | |
|  * and license this software and its documentation for any purpose, provided
 | |
|  * that existing copyright notices are retained in all copies and that this
 | |
|  * notice is included verbatim in any distributions. No written agreement,
 | |
|  * license, or royalty fee is required for any of the authorized uses.
 | |
|  * Modifications to this software may be copyrighted by their authors
 | |
|  * and need not follow the licensing terms described here, provided that
 | |
|  * the new terms are clearly indicated on the first page of each file where
 | |
|  * they apply.
 | |
|  */
 | |
| 
 | |
| #include <string.h>
 | |
| #include <signal.h>
 | |
| #include "debug.h"
 | |
| #include "asm.h"
 | |
| #include "slite.h"
 | |
| 
 | |
| extern unsigned long rdtbr();
 | |
| extern struct trap_entry fltr_proto;
 | |
| extern void trap_low();
 | |
| exception_t default_trap_hook = trap_low;
 | |
| void target_reset();
 | |
| void flush_i_cache();
 | |
| char *target_read_registers(unsigned long *);
 | |
| char *target_write_registers(unsigned long *);
 | |
| char *target_dump_state(unsigned long *);
 | |
| 
 | |
| #define NUMREGS 72
 | |
| 
 | |
| /* Number of bytes of registers.  */
 | |
| #define NUMREGBYTES (NUMREGS * 4)
 | |
| 
 | |
| enum regnames {G0, G1, G2, G3, G4, G5, G6, G7,
 | |
| 		 O0, O1, O2, O3, O4, O5, SP, O7,
 | |
| 		 L0, L1, L2, L3, L4, L5, L6, L7,
 | |
| 		 I0, I1, I2, I3, I4, I5, FP, I7,
 | |
| 
 | |
| 		 F0, F1, F2, F3, F4, F5, F6, F7,
 | |
| 		 F8, F9, F10, F11, F12, F13, F14, F15,
 | |
| 		 F16, F17, F18, F19, F20, F21, F22, F23,
 | |
| 		 F24, F25, F26, F27, F28, F29, F30, F31,
 | |
| 		 Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR };
 | |
| 
 | |
| /*
 | |
|  * Each entry in the trap vector occupies four words, typically a jump
 | |
|  * to the processing routine.
 | |
|  */
 | |
| struct trap_entry {
 | |
|   unsigned sethi_filler:10;
 | |
|   unsigned sethi_imm22:22;
 | |
|   unsigned jmpl_filler:19;
 | |
|   unsigned jmpl_simm13:13;
 | |
|   unsigned long filler[2];
 | |
| };
 | |
| 
 | |
| /*
 | |
|  * This table contains the mapping between SPARC hardware trap types, and
 | |
|  * signals, which are primarily what GDB understands.  It also indicates
 | |
|  * which hardware traps we need to commandeer when initializing the stub.
 | |
|  */
 | |
| struct trap_info hard_trap_info[] = {
 | |
|   {1, SIGSEGV},			/* instruction access error */
 | |
|   {2, SIGILL},			/* privileged instruction */
 | |
|   {3, SIGILL},			/* illegal instruction */
 | |
|   {4, SIGEMT},			/* fp disabled */
 | |
|   {36, SIGEMT},			/* cp disabled */
 | |
|   {7, SIGBUS},			/* mem address not aligned */
 | |
|   {9, SIGSEGV},			/* data access exception */
 | |
|   {10, SIGEMT},			/* tag overflow */
 | |
|   {128+1, SIGTRAP},		/* ta 1 - normal breakpoint instruction */
 | |
|   {0, 0}			/* Must be last */
 | |
| };
 | |
| 
 | |
| extern struct trap_entry fltr_proto;
 | |
| void
 | |
| exception_handler (int tt, unsigned long routine)
 | |
| {
 | |
|   struct trap_entry *tb;        /* Trap vector base address */
 | |
| 
 | |
|   DEBUG (1, "Entering exception_handler()");
 | |
|   if (tt != 256) {
 | |
|     tb = (struct trap_entry *) (rdtbr() & ~0xfff);
 | |
|   } else {
 | |
|     tt = 255;
 | |
|     tb = (struct trap_entry *) 0;
 | |
|   }
 | |
| 
 | |
|   tb[tt] = fltr_proto;
 | |
| 
 | |
|   tb[tt].sethi_imm22 = routine >> 10;
 | |
|   tb[tt].jmpl_simm13 = routine & 0x3ff;
 | |
| 
 | |
|   DEBUG (1, "Leaving exception_handler()");
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * This is so we can trap a memory fault when reading or writing
 | |
|  * directly to memory.
 | |
|  */
 | |
| void
 | |
| set_mem_fault_trap(enable)
 | |
|      int enable;
 | |
| {
 | |
|   extern void fltr_set_mem_err();
 | |
| 
 | |
|   DEBUG (1, "Entering set_mem_fault_trap()");
 | |
| 
 | |
|   mem_err = 0;
 | |
| 
 | |
|   if (enable)
 | |
|     exception_handler(9, (unsigned long)fltr_set_mem_err);
 | |
|   else
 | |
|     exception_handler(9, (unsigned long)trap_low);
 | |
| 
 | |
|   DEBUG (1, "Leaving set_mem_fault_trap()");
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * This function does all command procesing for interfacing to gdb.  It
 | |
|  * returns 1 if you should skip the instruction at the trap address, 0
 | |
|  * otherwise.
 | |
|  */
 | |
| extern void breakinst();
 | |
| 
 | |
| void
 | |
| handle_exception (registers)
 | |
|      unsigned long *registers;
 | |
| {
 | |
|   int sigval;
 | |
| 
 | |
|   /* First, we must force all of the windows to be spilled out */
 | |
| 
 | |
|   DEBUG (1, "Entering handle_exception()");
 | |
| 
 | |
| /*  asm("mov %g0, %wim ; nop; nop; nop"); */
 | |
|   asm("	save %sp, -64, %sp	\n\
 | |
| 	save %sp, -64, %sp	\n\
 | |
| 	save %sp, -64, %sp	\n\
 | |
| 	save %sp, -64, %sp	\n\
 | |
| 	save %sp, -64, %sp	\n\
 | |
| 	save %sp, -64, %sp	\n\
 | |
| 	save %sp, -64, %sp	\n\
 | |
| 	save %sp, -64, %sp	\n\
 | |
| 	restore			\n\
 | |
| 	restore			\n\
 | |
| 	restore			\n\
 | |
| 	restore			\n\
 | |
| 	restore			\n\
 | |
| 	restore			\n\
 | |
| 	restore			\n\
 | |
| 	restore			\n\
 | |
| ");
 | |
| 
 | |
|   if (registers[PC] == (unsigned long)breakinst) {
 | |
|     registers[PC] = registers[NPC];
 | |
|     registers[NPC] += 4;
 | |
|   }
 | |
| 
 | |
|   /* get the last know signal number from the trap register */
 | |
|   sigval = computeSignal((registers[TBR] >> 4) & 0xff);
 | |
| 
 | |
|   /* call the main command processing loop for gdb */
 | |
|   gdb_event_loop (sigval, registers);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * This function will generate a breakpoint exception.  It is used at the
 | |
|  * beginning of a program to sync up with a debugger and can be used
 | |
|  * otherwise as a quick means to stop program execution and "break" into
 | |
|  * the debugger.
 | |
|  */
 | |
| void
 | |
| breakpoint()
 | |
| {
 | |
|   DEBUG (1, "Entering breakpoint()");
 | |
| 
 | |
|   if (!initialized)
 | |
|     return;
 | |
| 
 | |
|   asm("	.globl " STRINGSYM(breakinst) "		\n\
 | |
| 	" STRINGSYM(breakinst) ": ta 128+1	\n\
 | |
| 	nop					\n\
 | |
| 	nop					\n\
 | |
|       ");
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * This is just a test vector for debugging excpetions.
 | |
|  */
 | |
| void
 | |
| bad_trap(tt)
 | |
| int tt;
 | |
| {
 | |
|   print ("Got a bad trap #");
 | |
|   outbyte (tt);
 | |
|   outbyte ('\n');
 | |
|   asm("ta 0		\n\
 | |
| 	nop		\n\
 | |
| 	nop		\n\
 | |
|       ");
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * This is just a test vector for debugging excpetions.
 | |
|  */
 | |
| void
 | |
| soft_trap(tt)
 | |
| int tt;
 | |
| {
 | |
|   print ("Got a soft trap #");
 | |
|   outbyte (tt);
 | |
|   outbyte ('\n');
 | |
|   asm("ta 0		\n\
 | |
| 	nop		\n\
 | |
| 	nop		\n\
 | |
|       ");
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Flush the instruction cache.  We need to do this for the debugger stub so
 | |
|  * that breakpoints, et. al. become visible to the instruction stream after
 | |
|  * storing them in memory.
 | |
|  * 
 | |
|  * For the sparclite, we need to do something here, but for a standard
 | |
|  * sparc (which SIS simulates), we don't.
 | |
|  */
 | |
| 
 | |
| void
 | |
| flush_i_cache ()
 | |
| {
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * This will reset the processor, so we never return from here.
 | |
|  */
 | |
| void
 | |
| target_reset()
 | |
| {
 | |
|   asm ("call 0		\n\
 | |
| 	nop ");
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * g - read registers.
 | |
|  *	no params.
 | |
|  *	returns a vector of words, size is NUM_REGS.
 | |
|  */
 | |
| char *
 | |
| target_read_registers(unsigned long *registers)
 | |
| {
 | |
|   char *ptr;
 | |
|   unsigned long *sp;
 | |
|  
 | |
|   DEBUG (1, "In target_read_registers()");
 | |
| 
 | |
|   ptr = packet_out_buf;
 | |
|   ptr = mem2hex((char *)registers, ptr, 16 * 4, 0); /* G & O regs */
 | |
|   ptr = mem2hex((unsigned char *)(sp + 0), ptr, 16 * 4, 0); /* L & I regs */
 | |
|   memset(ptr, '0', 32 * 8); /* Floating point */
 | |
|   mem2hex((char *)®isters[Y],
 | |
| 	  ptr + 32 * 4 * 2,
 | |
| 	  8 * 4,
 | |
| 	  0);		/* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */
 | |
|   return (ptr);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * G - write registers.
 | |
|  *	param is a vector of words, size is NUM_REGS.
 | |
|  *	returns an OK or an error number.
 | |
|  */
 | |
| char *
 | |
| target_write_registers(unsigned long *registers)
 | |
| {	
 | |
|   unsigned char *ptr;
 | |
|   unsigned long *sp;
 | |
|   unsigned long *newsp, psr;
 | |
| 
 | |
|   DEBUG (1, "In target_write_registers()");
 | |
| 
 | |
|   psr = registers[PSR];
 | |
|   
 | |
|   ptr = &packet_in_buf[1];
 | |
| 
 | |
|   hex2mem(ptr, (char *)registers, 16 * 4, 0); /* G & O regs */
 | |
|   hex2mem(ptr + 16 * 4 * 2, (unsigned char *)(sp + 0), 16 * 4, 0); /* L & I regs */
 | |
|   hex2mem(ptr + 64 * 4 * 2, (char *)®isters[Y],
 | |
| 	  8 * 4, 0);	/* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */
 | |
|   
 | |
|   /*
 | |
|    * see if the stack pointer has moved.  If so, then copy the saved
 | |
|    * locals and ins to the new location.  This keeps the window
 | |
|    * overflow and underflow routines happy.
 | |
|    */
 | |
|   
 | |
|   newsp = (unsigned long *)registers[SP];
 | |
|   if (sp != newsp)
 | |
|     sp = memcpy(newsp, sp, 16 * 4);
 | |
|   
 | |
|   /* Don't allow CWP to be modified. */
 | |
|   
 | |
|   if (psr != registers[PSR])
 | |
|     registers[PSR] = (psr & 0x1f) | (registers[PSR] & ~0x1f);
 | |
|   
 | |
|   return (ptr);
 | |
| }
 | |
| 
 | |
| char *
 | |
| target_dump_state(unsigned long *registers)
 | |
| {
 | |
|   int tt;			/* Trap type */
 | |
|   int sigval;
 | |
|   char *ptr;
 | |
|   unsigned long *sp;
 | |
| 
 | |
|   DEBUG (1, "In target_dump_state()");
 | |
| 
 | |
|   sp = (unsigned long *)registers[SP];
 | |
| 
 | |
|   tt = (registers[TBR] >> 4) & 0xff;
 | |
| 
 | |
|   /* reply to host that an exception has occurred */
 | |
|   sigval = computeSignal(tt);
 | |
|   ptr = packet_out_buf;
 | |
| 
 | |
|   *ptr++ = 'T';
 | |
|   *ptr++ = hexchars[sigval >> 4];
 | |
|   *ptr++ = hexchars[sigval & 0xf];
 | |
| 
 | |
|   *ptr++ = hexchars[PC >> 4];
 | |
|   *ptr++ = hexchars[PC & 0xf];
 | |
|   *ptr++ = ':';
 | |
|   ptr = mem2hex((unsigned char *)®isters[PC], ptr, 4, 0);
 | |
|   *ptr++ = ';';
 | |
| 
 | |
|   *ptr++ = hexchars[FP >> 4];
 | |
|   *ptr++ = hexchars[FP & 0xf];
 | |
|   *ptr++ = ':';
 | |
|   ptr = mem2hex((unsigned char *)(sp + 8 + 6), ptr, 4, 0); /* FP */
 | |
|   *ptr++ = ';';
 | |
| 
 | |
|   *ptr++ = hexchars[SP >> 4];
 | |
|   *ptr++ = hexchars[SP & 0xf];
 | |
|   *ptr++ = ':';
 | |
|   ptr = mem2hex((unsigned char *)&sp, ptr, 4, 0);
 | |
|   *ptr++ = ';';
 | |
| 
 | |
|   *ptr++ = hexchars[NPC >> 4];
 | |
| 
 | |
|   return (packet_out_buf);
 | |
| }
 | |
| 
 | |
| void
 | |
| write_pc(unsigned long *registers, unsigned long addr)
 | |
| {
 | |
|   DEBUG (1, "In write_pc");
 | |
| 
 | |
|   registers[PC] = addr;
 | |
|   registers[NPC] = addr + 4;
 | |
| }
 |