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;
|
|
}
|