Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
		
			
				
	
	
		
			1491 lines
		
	
	
	
		
			36 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1491 lines
		
	
	
	
		
			36 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * May be copied or modified under the terms of the GNU General Public
 | 
						|
 * License.  See linux/COPYING for more information.
 | 
						|
 *
 | 
						|
 * Containes extracts from code by Glenn Engel, Jim Kingdon,
 | 
						|
 * David Grothe <dave@gcom.com>, Tigran Aivazian <tigran@sco.com>,
 | 
						|
 * Amit S. Kale <akale@veritas.com>,  William Gatliff <bgat@open-widgets.com>,
 | 
						|
 * Ben Lee, Steve Chamberlain and Benoit Miller <fulg@iname.com>.
 | 
						|
 * 
 | 
						|
 * This version by Henry Bell <henry.bell@st.com>
 | 
						|
 * Minor modifications by Jeremy Siegel <jsiegel@mvista.com>
 | 
						|
 * 
 | 
						|
 * Contains low-level support for remote debug using GDB. 
 | 
						|
 *
 | 
						|
 * To enable debugger support, two things need to happen. A call to
 | 
						|
 * set_debug_traps() is necessary in order to allow any breakpoints
 | 
						|
 * or error conditions to be properly intercepted and reported to gdb.
 | 
						|
 * A breakpoint also needs to be generated to begin communication.  This
 | 
						|
 * is most easily accomplished by a call to breakpoint() which does
 | 
						|
 * a trapa if the initialisation phase has been successfully completed.
 | 
						|
 *
 | 
						|
 * In this case, set_debug_traps() is not used to "take over" exceptions;
 | 
						|
 * other kernel code is modified instead to enter the kgdb functions here
 | 
						|
 * when appropriate (see entry.S for breakpoint traps and NMI interrupts,
 | 
						|
 * see traps.c for kernel error exceptions).
 | 
						|
 *
 | 
						|
 * The following gdb commands are supported:
 | 
						|
 *
 | 
						|
 *    Command       Function                               Return value
 | 
						|
 *
 | 
						|
 *    g             return the value of the CPU registers  hex data or ENN
 | 
						|
 *    G             set the value of the CPU registers     OK or ENN
 | 
						|
 *
 | 
						|
 *    mAA..AA,LLLL  Read LLLL bytes at address AA..AA      hex data or ENN
 | 
						|
 *    MAA..AA,LLLL: Write LLLL bytes at address AA.AA      OK or ENN
 | 
						|
 *    XAA..AA,LLLL: Same, but data is binary (not hex)     OK or ENN
 | 
						|
 *
 | 
						|
 *    c             Resume at current address              SNN   ( signal NN)
 | 
						|
 *    cAA..AA       Continue at address AA..AA             SNN
 | 
						|
 *    CNN;          Resume at current address with signal  SNN
 | 
						|
 *    CNN;AA..AA    Resume at address AA..AA with signal   SNN
 | 
						|
 *
 | 
						|
 *    s             Step one instruction                   SNN
 | 
						|
 *    sAA..AA       Step one instruction from AA..AA       SNN
 | 
						|
 *    SNN;          Step one instruction with signal       SNN
 | 
						|
 *    SNNAA..AA     Step one instruction from AA..AA w/NN  SNN
 | 
						|
 *
 | 
						|
 *    k             kill (Detach GDB)
 | 
						|
 *
 | 
						|
 *    d             Toggle debug flag
 | 
						|
 *    D             Detach GDB 
 | 
						|
 *
 | 
						|
 *    Hct           Set thread t for operations,           OK or ENN
 | 
						|
 *                  c = 'c' (step, cont), c = 'g' (other
 | 
						|
 *                  operations)
 | 
						|
 *
 | 
						|
 *    qC            Query current thread ID                QCpid
 | 
						|
 *    qfThreadInfo  Get list of current threads (first)    m<id>
 | 
						|
 *    qsThreadInfo   "    "  "     "      "   (subsequent)
 | 
						|
 *    qOffsets      Get section offsets                  Text=x;Data=y;Bss=z
 | 
						|
 * 
 | 
						|
 *    TXX           Find if thread XX is alive             OK or ENN
 | 
						|
 *    ?             What was the last sigval ?             SNN   (signal NN)
 | 
						|
 *    O             Output to GDB console
 | 
						|
 *
 | 
						|
 * Remote communication protocol.
 | 
						|
 *
 | 
						|
 *    A debug packet whose contents are <data> is encapsulated for
 | 
						|
 *    transmission in the form:
 | 
						|
 *
 | 
						|
 *       $ <data> # CSUM1 CSUM2
 | 
						|
 *
 | 
						|
 *       <data> must be ASCII alphanumeric and cannot include characters
 | 
						|
 *       '$' or '#'.  If <data> starts with two characters followed by
 | 
						|
 *       ':', then the existing stubs interpret this as a sequence number.
 | 
						|
 *
 | 
						|
 *       CSUM1 and CSUM2 are ascii hex representation of an 8-bit 
 | 
						|
 *       checksum of <data>, the most significant nibble is sent first.
 | 
						|
 *       the hex digits 0-9,a-f are used.
 | 
						|
 *
 | 
						|
 *    Receiver responds with:
 | 
						|
 *
 | 
						|
 *       +       - if CSUM is correct and ready for next packet
 | 
						|
 *       -       - if CSUM is incorrect
 | 
						|
 *
 | 
						|
 * Responses can be run-length encoded to save space.  A '*' means that
 | 
						|
 * the next character is an ASCII encoding giving a repeat count which
 | 
						|
 * stands for that many repititions of the character preceding the '*'.
 | 
						|
 * The encoding is n+29, yielding a printable character where n >=3 
 | 
						|
 * (which is where RLE starts to win).  Don't use an n > 126. 
 | 
						|
 *
 | 
						|
 * So "0* " means the same as "0000".
 | 
						|
 */
 | 
						|
 | 
						|
#include <linux/string.h>
 | 
						|
#include <linux/kernel.h>
 | 
						|
#include <linux/sched.h>
 | 
						|
#include <linux/smp.h>
 | 
						|
#include <linux/spinlock.h>
 | 
						|
#include <linux/delay.h>
 | 
						|
#include <linux/linkage.h>
 | 
						|
#include <linux/init.h>
 | 
						|
 | 
						|
#include <asm/system.h>
 | 
						|
#include <asm/current.h>
 | 
						|
#include <asm/signal.h>
 | 
						|
#include <asm/pgtable.h>
 | 
						|
#include <asm/ptrace.h>
 | 
						|
#include <asm/kgdb.h>
 | 
						|
 | 
						|
#ifdef CONFIG_SH_KGDB_CONSOLE
 | 
						|
#include <linux/console.h>
 | 
						|
#endif
 | 
						|
 | 
						|
/* Function pointers for linkage */
 | 
						|
kgdb_debug_hook_t *kgdb_debug_hook;
 | 
						|
kgdb_bus_error_hook_t *kgdb_bus_err_hook;
 | 
						|
 | 
						|
int (*kgdb_getchar)(void);
 | 
						|
void (*kgdb_putchar)(int);
 | 
						|
 | 
						|
static void put_debug_char(int c)
 | 
						|
{
 | 
						|
	if (!kgdb_putchar)
 | 
						|
		return;
 | 
						|
	(*kgdb_putchar)(c);
 | 
						|
}
 | 
						|
static int get_debug_char(void)
 | 
						|
{
 | 
						|
	if (!kgdb_getchar)
 | 
						|
		return -1;
 | 
						|
	return (*kgdb_getchar)();
 | 
						|
}
 | 
						|
 | 
						|
/* Num chars in in/out bound buffers, register packets need NUMREGBYTES * 2 */
 | 
						|
#define BUFMAX 1024
 | 
						|
#define NUMREGBYTES (MAXREG*4)
 | 
						|
#define OUTBUFMAX (NUMREGBYTES*2+512)
 | 
						|
 | 
						|
enum regs {
 | 
						|
	R0 = 0, R1,  R2,  R3,   R4,   R5,  R6, R7,
 | 
						|
	R8, R9, R10, R11, R12,  R13,  R14, R15,
 | 
						|
	PC, PR, GBR, VBR, MACH, MACL, SR,
 | 
						|
	/*  */
 | 
						|
	MAXREG
 | 
						|
};
 | 
						|
 | 
						|
static unsigned int registers[MAXREG];
 | 
						|
struct kgdb_regs trap_registers;
 | 
						|
 | 
						|
char kgdb_in_gdb_mode;
 | 
						|
char in_nmi;			/* Set during NMI to prevent reentry */
 | 
						|
int kgdb_nofault;		/* Boolean to ignore bus errs (i.e. in GDB) */
 | 
						|
int kgdb_enabled = 1;		/* Default to enabled, cmdline can disable */
 | 
						|
int kgdb_halt;
 | 
						|
 | 
						|
/* Exposed for user access */
 | 
						|
struct task_struct *kgdb_current;
 | 
						|
unsigned int kgdb_g_imask;
 | 
						|
int kgdb_trapa_val;
 | 
						|
int kgdb_excode;
 | 
						|
 | 
						|
/* Default values for SCI (can override via kernel args in setup.c) */
 | 
						|
#ifndef CONFIG_KGDB_DEFPORT
 | 
						|
#define CONFIG_KGDB_DEFPORT 1
 | 
						|
#endif
 | 
						|
 | 
						|
#ifndef CONFIG_KGDB_DEFBAUD
 | 
						|
#define CONFIG_KGDB_DEFBAUD 115200
 | 
						|
#endif
 | 
						|
 | 
						|
#if defined(CONFIG_KGDB_DEFPARITY_E)
 | 
						|
#define CONFIG_KGDB_DEFPARITY 'E'
 | 
						|
#elif defined(CONFIG_KGDB_DEFPARITY_O)
 | 
						|
#define CONFIG_KGDB_DEFPARITY 'O'
 | 
						|
#else /* CONFIG_KGDB_DEFPARITY_N */
 | 
						|
#define CONFIG_KGDB_DEFPARITY 'N'
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef CONFIG_KGDB_DEFBITS_7
 | 
						|
#define CONFIG_KGDB_DEFBITS '7'
 | 
						|
#else /* CONFIG_KGDB_DEFBITS_8 */
 | 
						|
#define CONFIG_KGDB_DEFBITS '8'
 | 
						|
#endif
 | 
						|
 | 
						|
/* SCI/UART settings, used in kgdb_console_setup() */
 | 
						|
int  kgdb_portnum = CONFIG_KGDB_DEFPORT;
 | 
						|
int  kgdb_baud = CONFIG_KGDB_DEFBAUD;
 | 
						|
char kgdb_parity = CONFIG_KGDB_DEFPARITY;
 | 
						|
char kgdb_bits = CONFIG_KGDB_DEFBITS;
 | 
						|
 | 
						|
/* Jump buffer for setjmp/longjmp */
 | 
						|
static jmp_buf rem_com_env;
 | 
						|
 | 
						|
/* TRA differs sh3/4 */
 | 
						|
#if defined(CONFIG_CPU_SH3)
 | 
						|
#define TRA 0xffffffd0
 | 
						|
#elif defined(CONFIG_CPU_SH4)
 | 
						|
#define TRA 0xff000020
 | 
						|
#endif
 | 
						|
 | 
						|
/* Macros for single step instruction identification */
 | 
						|
#define OPCODE_BT(op)         (((op) & 0xff00) == 0x8900)
 | 
						|
#define OPCODE_BF(op)         (((op) & 0xff00) == 0x8b00)
 | 
						|
#define OPCODE_BTF_DISP(op)   (((op) & 0x80) ? (((op) | 0xffffff80) << 1) : \
 | 
						|
			      (((op) & 0x7f ) << 1))
 | 
						|
#define OPCODE_BFS(op)        (((op) & 0xff00) == 0x8f00)
 | 
						|
#define OPCODE_BTS(op)        (((op) & 0xff00) == 0x8d00)
 | 
						|
#define OPCODE_BRA(op)        (((op) & 0xf000) == 0xa000)
 | 
						|
#define OPCODE_BRA_DISP(op)   (((op) & 0x800) ? (((op) | 0xfffff800) << 1) : \
 | 
						|
			      (((op) & 0x7ff) << 1))
 | 
						|
#define OPCODE_BRAF(op)       (((op) & 0xf0ff) == 0x0023)
 | 
						|
#define OPCODE_BRAF_REG(op)   (((op) & 0x0f00) >> 8)
 | 
						|
#define OPCODE_BSR(op)        (((op) & 0xf000) == 0xb000)
 | 
						|
#define OPCODE_BSR_DISP(op)   (((op) & 0x800) ? (((op) | 0xfffff800) << 1) : \
 | 
						|
			      (((op) & 0x7ff) << 1))
 | 
						|
#define OPCODE_BSRF(op)       (((op) & 0xf0ff) == 0x0003)
 | 
						|
#define OPCODE_BSRF_REG(op)   (((op) >> 8) & 0xf)
 | 
						|
#define OPCODE_JMP(op)        (((op) & 0xf0ff) == 0x402b)
 | 
						|
#define OPCODE_JMP_REG(op)    (((op) >> 8) & 0xf)
 | 
						|
#define OPCODE_JSR(op)        (((op) & 0xf0ff) == 0x400b)
 | 
						|
#define OPCODE_JSR_REG(op)    (((op) >> 8) & 0xf)
 | 
						|
#define OPCODE_RTS(op)        ((op) == 0xb)
 | 
						|
#define OPCODE_RTE(op)        ((op) == 0x2b)
 | 
						|
 | 
						|
#define SR_T_BIT_MASK           0x1
 | 
						|
#define STEP_OPCODE             0xc320
 | 
						|
#define BIOS_CALL_TRAP          0x3f
 | 
						|
 | 
						|
/* Exception codes as per SH-4 core manual */
 | 
						|
#define ADDRESS_ERROR_LOAD_VEC   7
 | 
						|
#define ADDRESS_ERROR_STORE_VEC  8
 | 
						|
#define TRAP_VEC                 11
 | 
						|
#define INVALID_INSN_VEC         12
 | 
						|
#define INVALID_SLOT_VEC         13
 | 
						|
#define NMI_VEC                  14
 | 
						|
#define USER_BREAK_VEC           15
 | 
						|
#define SERIAL_BREAK_VEC         58
 | 
						|
 | 
						|
/* Misc static */
 | 
						|
static int stepped_address;
 | 
						|
static short stepped_opcode;
 | 
						|
static const char hexchars[] = "0123456789abcdef";
 | 
						|
static char in_buffer[BUFMAX];
 | 
						|
static char out_buffer[OUTBUFMAX];
 | 
						|
 | 
						|
static void kgdb_to_gdb(const char *s);
 | 
						|
 | 
						|
#ifdef CONFIG_KGDB_THREAD
 | 
						|
static struct task_struct *trapped_thread;
 | 
						|
static struct task_struct *current_thread;
 | 
						|
typedef unsigned char threadref[8];
 | 
						|
#define BUF_THREAD_ID_SIZE 16
 | 
						|
#endif
 | 
						|
 | 
						|
/* Return addr as a real volatile address */
 | 
						|
static inline unsigned int ctrl_inl(const unsigned long addr)
 | 
						|
{
 | 
						|
	return *(volatile unsigned long *) addr;
 | 
						|
}
 | 
						|
 | 
						|
/* Correctly set *addr using volatile */
 | 
						|
static inline void ctrl_outl(const unsigned int b, unsigned long addr)
 | 
						|
{
 | 
						|
	*(volatile unsigned long *) addr = b;
 | 
						|
}
 | 
						|
 | 
						|
/* Get high hex bits */
 | 
						|
static char highhex(const int x)
 | 
						|
{
 | 
						|
	return hexchars[(x >> 4) & 0xf];
 | 
						|
}
 | 
						|
 | 
						|
/* Get low hex bits */
 | 
						|
static char lowhex(const int x)
 | 
						|
{
 | 
						|
	return hexchars[x & 0xf];
 | 
						|
}
 | 
						|
 | 
						|
/* Convert ch to hex */
 | 
						|
static int hex(const char ch)
 | 
						|
{
 | 
						|
	if ((ch >= 'a') && (ch <= 'f'))
 | 
						|
		return (ch - 'a' + 10);
 | 
						|
	if ((ch >= '0') && (ch <= '9'))
 | 
						|
		return (ch - '0');
 | 
						|
	if ((ch >= 'A') && (ch <= 'F'))
 | 
						|
		return (ch - 'A' + 10);
 | 
						|
	return (-1);
 | 
						|
}
 | 
						|
 | 
						|
/* Convert the memory pointed to by mem into hex, placing result in buf.
 | 
						|
   Returns a pointer to the last char put in buf (null) */
 | 
						|
static char *mem_to_hex(const char *mem, char *buf, const int count)
 | 
						|
{
 | 
						|
	int i;
 | 
						|
	int ch;
 | 
						|
	unsigned short s_val;
 | 
						|
	unsigned long l_val;
 | 
						|
 | 
						|
	/* Check for 16 or 32 */
 | 
						|
	if (count == 2 && ((long) mem & 1) == 0) {
 | 
						|
		s_val = *(unsigned short *) mem;
 | 
						|
		mem = (char *) &s_val;
 | 
						|
	} else if (count == 4 && ((long) mem & 3) == 0) {
 | 
						|
		l_val = *(unsigned long *) mem;
 | 
						|
		mem = (char *) &l_val;
 | 
						|
	}
 | 
						|
	for (i = 0; i < count; i++) {
 | 
						|
		ch = *mem++;
 | 
						|
		*buf++ = highhex(ch);
 | 
						|
		*buf++ = lowhex(ch);
 | 
						|
	}
 | 
						|
	*buf = 0;
 | 
						|
	return (buf);
 | 
						|
}
 | 
						|
 | 
						|
/* Convert the hex array pointed to by buf into binary, to be placed in mem.
 | 
						|
   Return a pointer to the character after the last byte written */
 | 
						|
static char *hex_to_mem(const char *buf, char *mem, const int count)
 | 
						|
{
 | 
						|
	int i;
 | 
						|
	unsigned char ch;
 | 
						|
 | 
						|
	for (i = 0; i < count; i++) {
 | 
						|
		ch = hex(*buf++) << 4;
 | 
						|
		ch = ch + hex(*buf++);
 | 
						|
		*mem++ = ch;
 | 
						|
	}
 | 
						|
	return (mem);
 | 
						|
}
 | 
						|
 | 
						|
/* While finding valid hex chars, convert to an integer, then return it */
 | 
						|
static int hex_to_int(char **ptr, int *int_value)
 | 
						|
{
 | 
						|
	int num_chars = 0;
 | 
						|
	int hex_value;
 | 
						|
 | 
						|
	*int_value = 0;
 | 
						|
 | 
						|
	while (**ptr) {
 | 
						|
		hex_value = hex(**ptr);
 | 
						|
		if (hex_value >= 0) {
 | 
						|
			*int_value = (*int_value << 4) | hex_value;
 | 
						|
			num_chars++;
 | 
						|
		} else
 | 
						|
			break;
 | 
						|
		(*ptr)++;
 | 
						|
	}
 | 
						|
	return num_chars;
 | 
						|
}
 | 
						|
 | 
						|
/*  Copy the binary array pointed to by buf into mem.  Fix $, #,
 | 
						|
    and 0x7d escaped with 0x7d.  Return a pointer to the character 
 | 
						|
    after the last byte written. */
 | 
						|
static char *ebin_to_mem(const char *buf, char *mem, int count)
 | 
						|
{
 | 
						|
	for (; count > 0; count--, buf++) {
 | 
						|
		if (*buf == 0x7d)
 | 
						|
			*mem++ = *(++buf) ^ 0x20;
 | 
						|
		else
 | 
						|
			*mem++ = *buf;
 | 
						|
	}
 | 
						|
	return mem;
 | 
						|
}
 | 
						|
 | 
						|
/* Pack a hex byte */
 | 
						|
static char *pack_hex_byte(char *pkt, int byte)
 | 
						|
{
 | 
						|
	*pkt++ = hexchars[(byte >> 4) & 0xf];
 | 
						|
	*pkt++ = hexchars[(byte & 0xf)];
 | 
						|
	return pkt;
 | 
						|
}
 | 
						|
 | 
						|
#ifdef CONFIG_KGDB_THREAD
 | 
						|
 | 
						|
/* Pack a thread ID */
 | 
						|
static char *pack_threadid(char *pkt, threadref * id)
 | 
						|
{
 | 
						|
	char *limit;
 | 
						|
	unsigned char *altid;
 | 
						|
 | 
						|
	altid = (unsigned char *) id;
 | 
						|
 | 
						|
	limit = pkt + BUF_THREAD_ID_SIZE;
 | 
						|
	while (pkt < limit)
 | 
						|
		pkt = pack_hex_byte(pkt, *altid++);
 | 
						|
	return pkt;
 | 
						|
}
 | 
						|
 | 
						|
/* Convert an integer into our threadref */
 | 
						|
static void int_to_threadref(threadref * id, const int value)
 | 
						|
{
 | 
						|
	unsigned char *scan = (unsigned char *) id;
 | 
						|
	int i = 4;
 | 
						|
 | 
						|
	while (i--)
 | 
						|
		*scan++ = 0;
 | 
						|
 | 
						|
	*scan++ = (value >> 24) & 0xff;
 | 
						|
	*scan++ = (value >> 16) & 0xff;
 | 
						|
	*scan++ = (value >> 8) & 0xff;
 | 
						|
	*scan++ = (value & 0xff);
 | 
						|
}
 | 
						|
 | 
						|
/* Return a task structure ptr for a particular pid */
 | 
						|
static struct task_struct *get_thread(int pid)
 | 
						|
{
 | 
						|
	struct task_struct *thread;
 | 
						|
 | 
						|
	/* Use PID_MAX w/gdb for pid 0 */
 | 
						|
	if (pid == PID_MAX) pid = 0;
 | 
						|
 | 
						|
	/* First check via PID */
 | 
						|
	thread = find_task_by_pid(pid);
 | 
						|
 | 
						|
	if (thread)
 | 
						|
		return thread;
 | 
						|
 | 
						|
	/* Start at the start */
 | 
						|
	thread = init_tasks[0];
 | 
						|
 | 
						|
	/* Walk along the linked list of tasks */
 | 
						|
	do {
 | 
						|
		if (thread->pid == pid)
 | 
						|
			return thread;
 | 
						|
		thread = thread->next_task;
 | 
						|
	} while (thread != init_tasks[0]);
 | 
						|
 | 
						|
	return NULL;
 | 
						|
}
 | 
						|
 | 
						|
#endif /* CONFIG_KGDB_THREAD */
 | 
						|
 | 
						|
/* Scan for the start char '$', read the packet and check the checksum */
 | 
						|
static void get_packet(char *buffer, int buflen)
 | 
						|
{
 | 
						|
	unsigned char checksum;
 | 
						|
	unsigned char xmitcsum;
 | 
						|
	int i;
 | 
						|
	int count;
 | 
						|
	char ch;
 | 
						|
 | 
						|
	do {
 | 
						|
		/* Ignore everything until the start character */
 | 
						|
		while ((ch = get_debug_char()) != '$');
 | 
						|
 | 
						|
		checksum = 0;
 | 
						|
		xmitcsum = -1;
 | 
						|
		count = 0;
 | 
						|
 | 
						|
		/* Now, read until a # or end of buffer is found */
 | 
						|
		while (count < (buflen - 1)) {
 | 
						|
			ch = get_debug_char();
 | 
						|
 | 
						|
			if (ch == '#')
 | 
						|
				break;
 | 
						|
 | 
						|
			checksum = checksum + ch;
 | 
						|
			buffer[count] = ch;
 | 
						|
			count = count + 1;
 | 
						|
		}
 | 
						|
 | 
						|
		buffer[count] = 0;
 | 
						|
 | 
						|
		/* Continue to read checksum following # */
 | 
						|
		if (ch == '#') {
 | 
						|
			xmitcsum = hex(get_debug_char()) << 4;
 | 
						|
			xmitcsum += hex(get_debug_char());
 | 
						|
 | 
						|
			/* Checksum */
 | 
						|
			if (checksum != xmitcsum)
 | 
						|
				put_debug_char('-');	/* Failed checksum */
 | 
						|
			else {
 | 
						|
				/* Ack successful transfer */
 | 
						|
				put_debug_char('+');
 | 
						|
 | 
						|
				/* If a sequence char is present, reply 
 | 
						|
				   the sequence ID */
 | 
						|
				if (buffer[2] == ':') {
 | 
						|
					put_debug_char(buffer[0]);
 | 
						|
					put_debug_char(buffer[1]);
 | 
						|
 | 
						|
					/* Remove sequence chars from buffer */
 | 
						|
					count = strlen(buffer);
 | 
						|
					for (i = 3; i <= count; i++)
 | 
						|
						buffer[i - 3] = buffer[i];
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	while (checksum != xmitcsum);	/* Keep trying while we fail */
 | 
						|
}
 | 
						|
 | 
						|
/* Send the packet in the buffer with run-length encoding */
 | 
						|
static void put_packet(char *buffer)
 | 
						|
{
 | 
						|
	int checksum;
 | 
						|
	char *src;
 | 
						|
	int runlen;
 | 
						|
	int encode;
 | 
						|
 | 
						|
	do {
 | 
						|
		src = buffer;
 | 
						|
		put_debug_char('$');
 | 
						|
		checksum = 0;
 | 
						|
 | 
						|
		/* Continue while we still have chars left */
 | 
						|
		while (*src) {
 | 
						|
			/* Check for runs up to 99 chars long */
 | 
						|
			for (runlen = 1; runlen < 99; runlen++) {
 | 
						|
				if (src[0] != src[runlen])
 | 
						|
					break;
 | 
						|
			}
 | 
						|
 | 
						|
			if (runlen > 3) {
 | 
						|
				/* Got a useful amount, send encoding */
 | 
						|
				encode = runlen + ' ' - 4;
 | 
						|
				put_debug_char(*src);   checksum += *src;
 | 
						|
				put_debug_char('*');    checksum += '*';
 | 
						|
				put_debug_char(encode); checksum += encode;
 | 
						|
				src += runlen;
 | 
						|
			} else {
 | 
						|
				/* Otherwise just send the current char */
 | 
						|
				put_debug_char(*src);   checksum += *src;
 | 
						|
				src += 1;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		/* '#' Separator, put high and low components of checksum */
 | 
						|
		put_debug_char('#');
 | 
						|
		put_debug_char(highhex(checksum));
 | 
						|
		put_debug_char(lowhex(checksum));
 | 
						|
	}
 | 
						|
	while ((get_debug_char()) != '+');	/* While no ack */
 | 
						|
}
 | 
						|
 | 
						|
/* A bus error has occurred - perform a longjmp to return execution and
 | 
						|
   allow handling of the error */
 | 
						|
static void kgdb_handle_bus_error(void)
 | 
						|
{
 | 
						|
	longjmp(rem_com_env, 1);
 | 
						|
}
 | 
						|
 | 
						|
/* Translate SH-3/4 exception numbers to unix-like signal values */
 | 
						|
static int compute_signal(const int excep_code)
 | 
						|
{
 | 
						|
	int sigval;
 | 
						|
 | 
						|
	switch (excep_code) {
 | 
						|
 | 
						|
	case INVALID_INSN_VEC:
 | 
						|
	case INVALID_SLOT_VEC:
 | 
						|
		sigval = SIGILL;
 | 
						|
		break;
 | 
						|
	case ADDRESS_ERROR_LOAD_VEC:
 | 
						|
	case ADDRESS_ERROR_STORE_VEC:
 | 
						|
		sigval = SIGSEGV;
 | 
						|
		break;
 | 
						|
 | 
						|
	case SERIAL_BREAK_VEC:
 | 
						|
	case NMI_VEC:
 | 
						|
		sigval = SIGINT;
 | 
						|
		break;
 | 
						|
 | 
						|
	case USER_BREAK_VEC:
 | 
						|
	case TRAP_VEC:
 | 
						|
		sigval = SIGTRAP;
 | 
						|
		break;
 | 
						|
 | 
						|
	default:
 | 
						|
		sigval = SIGBUS;	/* "software generated" */
 | 
						|
		break;
 | 
						|
	}
 | 
						|
 | 
						|
	return (sigval);
 | 
						|
}
 | 
						|
 | 
						|
/* Make a local copy of the registers passed into the handler (bletch) */
 | 
						|
static void kgdb_regs_to_gdb_regs(const struct kgdb_regs *regs,
 | 
						|
				  int *gdb_regs)
 | 
						|
{
 | 
						|
	gdb_regs[R0] = regs->regs[R0];
 | 
						|
	gdb_regs[R1] = regs->regs[R1];
 | 
						|
	gdb_regs[R2] = regs->regs[R2];
 | 
						|
	gdb_regs[R3] = regs->regs[R3];
 | 
						|
	gdb_regs[R4] = regs->regs[R4];
 | 
						|
	gdb_regs[R5] = regs->regs[R5];
 | 
						|
	gdb_regs[R6] = regs->regs[R6];
 | 
						|
	gdb_regs[R7] = regs->regs[R7];
 | 
						|
	gdb_regs[R8] = regs->regs[R8];
 | 
						|
	gdb_regs[R9] = regs->regs[R9];
 | 
						|
	gdb_regs[R10] = regs->regs[R10];
 | 
						|
	gdb_regs[R11] = regs->regs[R11];
 | 
						|
	gdb_regs[R12] = regs->regs[R12];
 | 
						|
	gdb_regs[R13] = regs->regs[R13];
 | 
						|
	gdb_regs[R14] = regs->regs[R14];
 | 
						|
	gdb_regs[R15] = regs->regs[R15];
 | 
						|
	gdb_regs[PC] = regs->pc;
 | 
						|
	gdb_regs[PR] = regs->pr;
 | 
						|
	gdb_regs[GBR] = regs->gbr;
 | 
						|
	gdb_regs[MACH] = regs->mach;
 | 
						|
	gdb_regs[MACL] = regs->macl;
 | 
						|
	gdb_regs[SR] = regs->sr;
 | 
						|
	gdb_regs[VBR] = regs->vbr;
 | 
						|
}
 | 
						|
 | 
						|
/* Copy local gdb registers back to kgdb regs, for later copy to kernel */
 | 
						|
static void gdb_regs_to_kgdb_regs(const int *gdb_regs,
 | 
						|
				  struct kgdb_regs *regs)
 | 
						|
{
 | 
						|
	regs->regs[R0] = gdb_regs[R0];
 | 
						|
	regs->regs[R1] = gdb_regs[R1];
 | 
						|
	regs->regs[R2] = gdb_regs[R2];
 | 
						|
	regs->regs[R3] = gdb_regs[R3];
 | 
						|
	regs->regs[R4] = gdb_regs[R4];
 | 
						|
	regs->regs[R5] = gdb_regs[R5];
 | 
						|
	regs->regs[R6] = gdb_regs[R6];
 | 
						|
	regs->regs[R7] = gdb_regs[R7];
 | 
						|
	regs->regs[R8] = gdb_regs[R8];
 | 
						|
	regs->regs[R9] = gdb_regs[R9];
 | 
						|
	regs->regs[R10] = gdb_regs[R10];
 | 
						|
	regs->regs[R11] = gdb_regs[R11];
 | 
						|
	regs->regs[R12] = gdb_regs[R12];
 | 
						|
	regs->regs[R13] = gdb_regs[R13];
 | 
						|
	regs->regs[R14] = gdb_regs[R14];
 | 
						|
	regs->regs[R15] = gdb_regs[R15];
 | 
						|
	regs->pc = gdb_regs[PC];
 | 
						|
	regs->pr = gdb_regs[PR];
 | 
						|
	regs->gbr = gdb_regs[GBR];
 | 
						|
	regs->mach = gdb_regs[MACH];
 | 
						|
	regs->macl = gdb_regs[MACL];
 | 
						|
	regs->sr = gdb_regs[SR];
 | 
						|
	regs->vbr = gdb_regs[VBR];
 | 
						|
}
 | 
						|
 | 
						|
#ifdef CONFIG_KGDB_THREAD
 | 
						|
/* Make a local copy of registers from the specified thread */
 | 
						|
asmlinkage void ret_from_fork(void);
 | 
						|
static void thread_regs_to_gdb_regs(const struct task_struct *thread,
 | 
						|
				    int *gdb_regs)
 | 
						|
{
 | 
						|
	int regno;
 | 
						|
	int *tregs;
 | 
						|
 | 
						|
	/* Initialize to zero */
 | 
						|
	for (regno = 0; regno < MAXREG; regno++)
 | 
						|
		gdb_regs[regno] = 0;
 | 
						|
 | 
						|
	/* Just making sure... */
 | 
						|
	if (thread == NULL)
 | 
						|
		return;
 | 
						|
 | 
						|
	/* A new fork has pt_regs on the stack from a fork() call */
 | 
						|
	if (thread->thread.pc == (unsigned long)ret_from_fork) {
 | 
						|
 | 
						|
		int vbr_val;
 | 
						|
		struct pt_regs *kregs;
 | 
						|
		kregs = (struct pt_regs*)thread->thread.sp;
 | 
						|
 | 
						|
		gdb_regs[R0] = kregs->regs[R0];
 | 
						|
		gdb_regs[R1] = kregs->regs[R1];
 | 
						|
		gdb_regs[R2] = kregs->regs[R2];
 | 
						|
		gdb_regs[R3] = kregs->regs[R3];
 | 
						|
		gdb_regs[R4] = kregs->regs[R4];
 | 
						|
		gdb_regs[R5] = kregs->regs[R5];
 | 
						|
		gdb_regs[R6] = kregs->regs[R6];
 | 
						|
		gdb_regs[R7] = kregs->regs[R7];
 | 
						|
		gdb_regs[R8] = kregs->regs[R8];
 | 
						|
		gdb_regs[R9] = kregs->regs[R9];
 | 
						|
		gdb_regs[R10] = kregs->regs[R10];
 | 
						|
		gdb_regs[R11] = kregs->regs[R11];
 | 
						|
		gdb_regs[R12] = kregs->regs[R12];
 | 
						|
		gdb_regs[R13] = kregs->regs[R13];
 | 
						|
		gdb_regs[R14] = kregs->regs[R14];
 | 
						|
		gdb_regs[R15] = kregs->regs[R15];
 | 
						|
		gdb_regs[PC] = kregs->pc;
 | 
						|
		gdb_regs[PR] = kregs->pr;
 | 
						|
		gdb_regs[GBR] = kregs->gbr;
 | 
						|
		gdb_regs[MACH] = kregs->mach;
 | 
						|
		gdb_regs[MACL] = kregs->macl;
 | 
						|
		gdb_regs[SR] = kregs->sr;
 | 
						|
 | 
						|
		asm("stc vbr, %0":"=r"(vbr_val));
 | 
						|
		gdb_regs[VBR] = vbr_val;
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Otherwise, we have only some registers from switch_to() */
 | 
						|
	tregs = (int *)thread->thread.sp;
 | 
						|
	gdb_regs[R15] = (int)tregs;
 | 
						|
	gdb_regs[R14] = *tregs++;
 | 
						|
	gdb_regs[R13] = *tregs++;
 | 
						|
	gdb_regs[R12] = *tregs++;
 | 
						|
	gdb_regs[R11] = *tregs++;
 | 
						|
	gdb_regs[R10] = *tregs++;
 | 
						|
	gdb_regs[R9] = *tregs++;
 | 
						|
	gdb_regs[R8] = *tregs++;
 | 
						|
	gdb_regs[PR] = *tregs++;
 | 
						|
	gdb_regs[GBR] = *tregs++;
 | 
						|
	gdb_regs[PC] = thread->thread.pc;
 | 
						|
}
 | 
						|
#endif /* CONFIG_KGDB_THREAD */
 | 
						|
 | 
						|
/* Calculate the new address for after a step */
 | 
						|
static short *get_step_address(void)
 | 
						|
{
 | 
						|
	short op = *(short *) trap_registers.pc;
 | 
						|
	long addr;
 | 
						|
 | 
						|
	/* BT */
 | 
						|
	if (OPCODE_BT(op)) {
 | 
						|
		if (trap_registers.sr & SR_T_BIT_MASK)
 | 
						|
			addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op);
 | 
						|
		else
 | 
						|
			addr = trap_registers.pc + 2;
 | 
						|
	}
 | 
						|
 | 
						|
	/* BTS */
 | 
						|
	else if (OPCODE_BTS(op)) {
 | 
						|
		if (trap_registers.sr & SR_T_BIT_MASK)
 | 
						|
			addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op);
 | 
						|
		else
 | 
						|
			addr = trap_registers.pc + 4;	/* Not in delay slot */
 | 
						|
	}
 | 
						|
 | 
						|
	/* BF */
 | 
						|
	else if (OPCODE_BF(op)) {
 | 
						|
		if (!(trap_registers.sr & SR_T_BIT_MASK))
 | 
						|
			addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op);
 | 
						|
		else
 | 
						|
			addr = trap_registers.pc + 2;
 | 
						|
	}
 | 
						|
 | 
						|
	/* BFS */
 | 
						|
	else if (OPCODE_BFS(op)) {
 | 
						|
		if (!(trap_registers.sr & SR_T_BIT_MASK))
 | 
						|
			addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op);
 | 
						|
		else
 | 
						|
			addr = trap_registers.pc + 4;	/* Not in delay slot */
 | 
						|
	}
 | 
						|
 | 
						|
	/* BRA */
 | 
						|
	else if (OPCODE_BRA(op))
 | 
						|
		addr = trap_registers.pc + 4 + OPCODE_BRA_DISP(op);
 | 
						|
 | 
						|
	/* BRAF */
 | 
						|
	else if (OPCODE_BRAF(op))
 | 
						|
		addr = trap_registers.pc + 4
 | 
						|
		    + trap_registers.regs[OPCODE_BRAF_REG(op)];
 | 
						|
 | 
						|
	/* BSR */
 | 
						|
	else if (OPCODE_BSR(op))
 | 
						|
		addr = trap_registers.pc + 4 + OPCODE_BSR_DISP(op);
 | 
						|
 | 
						|
	/* BSRF */
 | 
						|
	else if (OPCODE_BSRF(op))
 | 
						|
		addr = trap_registers.pc + 4
 | 
						|
		    + trap_registers.regs[OPCODE_BSRF_REG(op)];
 | 
						|
 | 
						|
	/* JMP */
 | 
						|
	else if (OPCODE_JMP(op))
 | 
						|
		addr = trap_registers.regs[OPCODE_JMP_REG(op)];
 | 
						|
 | 
						|
	/* JSR */
 | 
						|
	else if (OPCODE_JSR(op))
 | 
						|
		addr = trap_registers.regs[OPCODE_JSR_REG(op)];
 | 
						|
 | 
						|
	/* RTS */
 | 
						|
	else if (OPCODE_RTS(op))
 | 
						|
		addr = trap_registers.pr;
 | 
						|
 | 
						|
	/* RTE */
 | 
						|
	else if (OPCODE_RTE(op))
 | 
						|
		addr = trap_registers.regs[15];
 | 
						|
 | 
						|
	/* Other */
 | 
						|
	else
 | 
						|
		addr = trap_registers.pc + 2;
 | 
						|
 | 
						|
	kgdb_flush_icache_range(addr, addr + 2);
 | 
						|
	return (short *) addr;
 | 
						|
}
 | 
						|
 | 
						|
/* Set up a single-step.  Replace the instruction immediately after the 
 | 
						|
   current instruction (i.e. next in the expected flow of control) with a
 | 
						|
   trap instruction, so that returning will cause only a single instruction
 | 
						|
   to be executed. Note that this model is slightly broken for instructions
 | 
						|
   with delay slots (e.g. B[TF]S, BSR, BRA etc), where both the branch
 | 
						|
   and the instruction in the delay slot will be executed. */
 | 
						|
static void do_single_step(void)
 | 
						|
{
 | 
						|
	unsigned short *addr = 0;
 | 
						|
 | 
						|
	/* Determine where the target instruction will send us to */
 | 
						|
	addr = get_step_address();
 | 
						|
	stepped_address = (int)addr;
 | 
						|
 | 
						|
	/* Replace it */
 | 
						|
	stepped_opcode = *(short *)addr;
 | 
						|
	*addr = STEP_OPCODE;
 | 
						|
 | 
						|
	/* Flush and return */
 | 
						|
	kgdb_flush_icache_range((long) addr, (long) addr + 2);
 | 
						|
	return;
 | 
						|
}
 | 
						|
 | 
						|
/* Undo a single step */
 | 
						|
static void undo_single_step(void)
 | 
						|
{
 | 
						|
	/* If we have stepped, put back the old instruction */
 | 
						|
	/* Use stepped_address in case we stopped elsewhere */
 | 
						|
	if (stepped_opcode != 0) {
 | 
						|
		*(short*)stepped_address = stepped_opcode;
 | 
						|
		kgdb_flush_icache_range(stepped_address, stepped_address + 2);
 | 
						|
	}
 | 
						|
	stepped_opcode = 0;
 | 
						|
}
 | 
						|
 | 
						|
/* Send a signal message */
 | 
						|
static void send_signal_msg(const int signum)
 | 
						|
{
 | 
						|
#ifndef CONFIG_KGDB_THREAD
 | 
						|
	out_buffer[0] = 'S';
 | 
						|
	out_buffer[1] = highhex(signum);
 | 
						|
	out_buffer[2] = lowhex(signum);
 | 
						|
	out_buffer[3] = 0;
 | 
						|
	put_packet(out_buffer);
 | 
						|
#else /* CONFIG_KGDB_THREAD */
 | 
						|
	int threadid;
 | 
						|
	threadref thref;
 | 
						|
	char *out = out_buffer;
 | 
						|
	const char *tstring = "thread";
 | 
						|
 | 
						|
	*out++ = 'T';
 | 
						|
	*out++ = highhex(signum);
 | 
						|
	*out++ = lowhex(signum);
 | 
						|
 | 
						|
	while (*tstring) {
 | 
						|
		*out++ = *tstring++;
 | 
						|
	}
 | 
						|
	*out++ = ':';
 | 
						|
 | 
						|
	threadid = trapped_thread->pid;
 | 
						|
	if (threadid == 0) threadid = PID_MAX;
 | 
						|
	int_to_threadref(&thref, threadid);
 | 
						|
	pack_threadid(out, &thref);
 | 
						|
	out += BUF_THREAD_ID_SIZE;
 | 
						|
	*out++ = ';';
 | 
						|
 | 
						|
	*out = 0;
 | 
						|
	put_packet(out_buffer);
 | 
						|
#endif /* CONFIG_KGDB_THREAD */
 | 
						|
}
 | 
						|
 | 
						|
/* Reply that all was well */
 | 
						|
static void send_ok_msg(void)
 | 
						|
{
 | 
						|
	strcpy(out_buffer, "OK");
 | 
						|
	put_packet(out_buffer);
 | 
						|
}
 | 
						|
 | 
						|
/* Reply that an error occurred */
 | 
						|
static void send_err_msg(void)
 | 
						|
{
 | 
						|
	strcpy(out_buffer, "E01");
 | 
						|
	put_packet(out_buffer);
 | 
						|
}
 | 
						|
 | 
						|
/* Empty message indicates unrecognised command */
 | 
						|
static void send_empty_msg(void)
 | 
						|
{
 | 
						|
	put_packet("");
 | 
						|
}
 | 
						|
 | 
						|
/* Read memory due to 'm' message */
 | 
						|
static void read_mem_msg(void)
 | 
						|
{
 | 
						|
	char *ptr;
 | 
						|
	int addr;
 | 
						|
	int length;
 | 
						|
 | 
						|
	/* Jmp, disable bus error handler */
 | 
						|
	if (setjmp(rem_com_env) == 0) {
 | 
						|
 | 
						|
		kgdb_nofault = 1;
 | 
						|
 | 
						|
		/* Walk through, have m<addr>,<length> */
 | 
						|
		ptr = &in_buffer[1];
 | 
						|
		if (hex_to_int(&ptr, &addr) && (*ptr++ == ','))
 | 
						|
			if (hex_to_int(&ptr, &length)) {
 | 
						|
				ptr = 0;
 | 
						|
				if (length * 2 > OUTBUFMAX)
 | 
						|
					length = OUTBUFMAX / 2;
 | 
						|
				mem_to_hex((char *) addr, out_buffer, length);
 | 
						|
			}
 | 
						|
		if (ptr)
 | 
						|
			send_err_msg();
 | 
						|
		else
 | 
						|
			put_packet(out_buffer);
 | 
						|
	} else
 | 
						|
		send_err_msg();
 | 
						|
 | 
						|
	/* Restore bus error handler */
 | 
						|
	kgdb_nofault = 0;
 | 
						|
}
 | 
						|
 | 
						|
/* Write memory due to 'M' or 'X' message */
 | 
						|
static void write_mem_msg(int binary)
 | 
						|
{
 | 
						|
	char *ptr;
 | 
						|
	int addr;
 | 
						|
	int length;
 | 
						|
 | 
						|
	if (setjmp(rem_com_env) == 0) {
 | 
						|
 | 
						|
		kgdb_nofault = 1;
 | 
						|
 | 
						|
		/* Walk through, have M<addr>,<length>:<data> */
 | 
						|
		ptr = &in_buffer[1];
 | 
						|
		if (hex_to_int(&ptr, &addr) && (*ptr++ == ','))
 | 
						|
			if (hex_to_int(&ptr, &length) && (*ptr++ == ':')) {
 | 
						|
				if (binary)
 | 
						|
					ebin_to_mem(ptr, (char*)addr, length);
 | 
						|
				else
 | 
						|
					hex_to_mem(ptr, (char*)addr, length);
 | 
						|
				kgdb_flush_icache_range(addr, addr + length);
 | 
						|
				ptr = 0;
 | 
						|
				send_ok_msg();
 | 
						|
			}
 | 
						|
		if (ptr)
 | 
						|
			send_err_msg();
 | 
						|
	} else
 | 
						|
		send_err_msg();
 | 
						|
 | 
						|
	/* Restore bus error handler */
 | 
						|
	kgdb_nofault = 0;
 | 
						|
}
 | 
						|
 | 
						|
/* Continue message  */
 | 
						|
static void continue_msg(void)
 | 
						|
{
 | 
						|
	/* Try to read optional parameter, PC unchanged if none */
 | 
						|
	char *ptr = &in_buffer[1];
 | 
						|
	int addr;
 | 
						|
 | 
						|
	if (hex_to_int(&ptr, &addr))
 | 
						|
		trap_registers.pc = addr;
 | 
						|
}
 | 
						|
 | 
						|
/* Continue message with signal */
 | 
						|
static void continue_with_sig_msg(void)
 | 
						|
{
 | 
						|
	int signal;
 | 
						|
	char *ptr = &in_buffer[1];
 | 
						|
	int addr;
 | 
						|
 | 
						|
	/* Report limitation */
 | 
						|
	kgdb_to_gdb("Cannot force signal in kgdb, continuing anyway.\n");
 | 
						|
 | 
						|
	/* Signal */
 | 
						|
	hex_to_int(&ptr, &signal);
 | 
						|
	if (*ptr == ';')
 | 
						|
		ptr++;
 | 
						|
 | 
						|
	/* Optional address */
 | 
						|
	if (hex_to_int(&ptr, &addr))
 | 
						|
		trap_registers.pc = addr;
 | 
						|
}
 | 
						|
 | 
						|
/* Step message */
 | 
						|
static void step_msg(void)
 | 
						|
{
 | 
						|
	continue_msg();
 | 
						|
	do_single_step();
 | 
						|
}
 | 
						|
 | 
						|
/* Step message with signal */
 | 
						|
static void step_with_sig_msg(void)
 | 
						|
{
 | 
						|
	continue_with_sig_msg();
 | 
						|
	do_single_step();
 | 
						|
}
 | 
						|
 | 
						|
/* Send register contents */
 | 
						|
static void send_regs_msg(void)
 | 
						|
{
 | 
						|
#ifdef CONFIG_KGDB_THREAD
 | 
						|
	if (!current_thread)
 | 
						|
		kgdb_regs_to_gdb_regs(&trap_registers, registers);
 | 
						|
	else
 | 
						|
		thread_regs_to_gdb_regs(current_thread, registers);
 | 
						|
#else
 | 
						|
	kgdb_regs_to_gdb_regs(&trap_registers, registers);
 | 
						|
#endif
 | 
						|
 | 
						|
	mem_to_hex((char *) registers, out_buffer, NUMREGBYTES);
 | 
						|
	put_packet(out_buffer);
 | 
						|
}
 | 
						|
 | 
						|
/* Set register contents - currently can't set other thread's registers */
 | 
						|
static void set_regs_msg(void)
 | 
						|
{
 | 
						|
#ifdef CONFIG_KGDB_THREAD
 | 
						|
	if (!current_thread) {
 | 
						|
#endif
 | 
						|
		kgdb_regs_to_gdb_regs(&trap_registers, registers);
 | 
						|
		hex_to_mem(&in_buffer[1], (char *) registers, NUMREGBYTES);
 | 
						|
		gdb_regs_to_kgdb_regs(registers, &trap_registers);
 | 
						|
		send_ok_msg();
 | 
						|
#ifdef CONFIG_KGDB_THREAD
 | 
						|
	} else
 | 
						|
		send_err_msg();
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
#ifdef CONFIG_KGDB_THREAD
 | 
						|
 | 
						|
/* Set the status for a thread */
 | 
						|
void set_thread_msg(void)
 | 
						|
{
 | 
						|
	int threadid;
 | 
						|
	struct task_struct *thread = NULL;
 | 
						|
	char *ptr;
 | 
						|
 | 
						|
	switch (in_buffer[1]) {
 | 
						|
 | 
						|
       	/* To select which thread for gG etc messages, i.e. supported */
 | 
						|
	case 'g':
 | 
						|
 | 
						|
		ptr = &in_buffer[2];
 | 
						|
		hex_to_int(&ptr, &threadid);
 | 
						|
		thread = get_thread(threadid);
 | 
						|
 | 
						|
		/* If we haven't found it */
 | 
						|
		if (!thread) {
 | 
						|
			send_err_msg();
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		/* Set current_thread (or not) */
 | 
						|
		if (thread == trapped_thread)
 | 
						|
			current_thread = NULL;
 | 
						|
		else
 | 
						|
			current_thread = thread;
 | 
						|
		send_ok_msg();
 | 
						|
		break;
 | 
						|
 | 
						|
	/* To select which thread for cCsS messages, i.e. unsupported */
 | 
						|
	case 'c':
 | 
						|
		send_ok_msg();
 | 
						|
		break;
 | 
						|
 | 
						|
	default:
 | 
						|
		send_empty_msg();
 | 
						|
		break;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/* Is a thread alive? */
 | 
						|
static void thread_status_msg(void)
 | 
						|
{
 | 
						|
	char *ptr;
 | 
						|
	int threadid;
 | 
						|
	struct task_struct *thread = NULL;
 | 
						|
 | 
						|
	ptr = &in_buffer[1];
 | 
						|
	hex_to_int(&ptr, &threadid);
 | 
						|
	thread = get_thread(threadid);
 | 
						|
	if (thread)
 | 
						|
		send_ok_msg();
 | 
						|
	else
 | 
						|
		send_err_msg();
 | 
						|
}
 | 
						|
/* Send the current thread ID */
 | 
						|
static void thread_id_msg(void)
 | 
						|
{
 | 
						|
	int threadid;
 | 
						|
	threadref thref;
 | 
						|
 | 
						|
	out_buffer[0] = 'Q';
 | 
						|
	out_buffer[1] = 'C';
 | 
						|
 | 
						|
	if (current_thread)
 | 
						|
		threadid = current_thread->pid;
 | 
						|
	else if (trapped_thread)
 | 
						|
		threadid = trapped_thread->pid;
 | 
						|
	else /* Impossible, but just in case! */
 | 
						|
	{
 | 
						|
		send_err_msg();
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Translate pid 0 to PID_MAX for gdb */
 | 
						|
	if (threadid == 0) threadid = PID_MAX;
 | 
						|
 | 
						|
	int_to_threadref(&thref, threadid);
 | 
						|
	pack_threadid(out_buffer + 2, &thref);
 | 
						|
	out_buffer[2 + BUF_THREAD_ID_SIZE] = '\0';
 | 
						|
	put_packet(out_buffer);
 | 
						|
}
 | 
						|
 | 
						|
/* Send thread info */
 | 
						|
static void thread_info_msg(void)
 | 
						|
{
 | 
						|
	struct task_struct *thread = NULL;
 | 
						|
	int threadid;
 | 
						|
	char *pos;
 | 
						|
	threadref thref;
 | 
						|
 | 
						|
	/* Start with 'm' */
 | 
						|
	out_buffer[0] = 'm';
 | 
						|
	pos = &out_buffer[1];
 | 
						|
 | 
						|
	/* For all possible thread IDs - this will overrun if > 44 threads! */
 | 
						|
	/* Start at 1 and include PID_MAX (since GDB won't use pid 0...) */
 | 
						|
	for (threadid = 1; threadid <= PID_MAX; threadid++) {
 | 
						|
 | 
						|
		read_lock(&tasklist_lock);
 | 
						|
		thread = get_thread(threadid);
 | 
						|
		read_unlock(&tasklist_lock);
 | 
						|
 | 
						|
		/* If it's a valid thread */
 | 
						|
		if (thread) {
 | 
						|
			int_to_threadref(&thref, threadid);
 | 
						|
			pack_threadid(pos, &thref);
 | 
						|
			pos += BUF_THREAD_ID_SIZE;
 | 
						|
			*pos++ = ',';
 | 
						|
		}
 | 
						|
	}
 | 
						|
	*--pos = 0;		/* Lose final comma */
 | 
						|
	put_packet(out_buffer);
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
/* Return printable info for gdb's 'info threads' command */
 | 
						|
static void thread_extra_info_msg(void)
 | 
						|
{
 | 
						|
	int threadid;
 | 
						|
	struct task_struct *thread = NULL;
 | 
						|
	char buffer[20], *ptr;
 | 
						|
	int i;
 | 
						|
 | 
						|
	/* Extract thread ID */
 | 
						|
	ptr = &in_buffer[17];
 | 
						|
	hex_to_int(&ptr, &threadid);
 | 
						|
	thread = get_thread(threadid);
 | 
						|
 | 
						|
	/* If we don't recognise it, say so */
 | 
						|
	if (thread == NULL)
 | 
						|
		strcpy(buffer, "(unknown)");
 | 
						|
	else
 | 
						|
		strcpy(buffer, thread->comm);
 | 
						|
 | 
						|
	/* Construct packet */
 | 
						|
	for (i = 0, ptr = out_buffer; buffer[i]; i++)
 | 
						|
		ptr = pack_hex_byte(ptr, buffer[i]);
 | 
						|
 | 
						|
	if (thread->thread.pc == (unsigned long)ret_from_fork) {
 | 
						|
		strcpy(buffer, "<new fork>");
 | 
						|
		for (i = 0; buffer[i]; i++)
 | 
						|
			ptr = pack_hex_byte(ptr, buffer[i]);
 | 
						|
	}
 | 
						|
 | 
						|
	*ptr = '\0';
 | 
						|
	put_packet(out_buffer);
 | 
						|
}
 | 
						|
 | 
						|
/* Handle all qFooBarBaz messages - have to use an if statement as
 | 
						|
   opposed to a switch because q messages can have > 1 char id. */
 | 
						|
static void query_msg(void)
 | 
						|
{
 | 
						|
	const char *q_start = &in_buffer[1];
 | 
						|
 | 
						|
	/* qC = return current thread ID */
 | 
						|
	if (strncmp(q_start, "C", 1) == 0)
 | 
						|
		thread_id_msg();
 | 
						|
 | 
						|
	/* qfThreadInfo = query all threads (first) */
 | 
						|
	else if (strncmp(q_start, "fThreadInfo", 11) == 0)
 | 
						|
		thread_info_msg();
 | 
						|
 | 
						|
	/* qsThreadInfo = query all threads (subsequent). We know we have sent
 | 
						|
	   them all after the qfThreadInfo message, so there are no to send */
 | 
						|
	else if (strncmp(q_start, "sThreadInfo", 11) == 0)
 | 
						|
		put_packet("l");	/* el = last */
 | 
						|
 | 
						|
	/* qThreadExtraInfo = supply printable information per thread */
 | 
						|
	else if (strncmp(q_start, "ThreadExtraInfo", 15) == 0)
 | 
						|
		thread_extra_info_msg();
 | 
						|
 | 
						|
	/* Unsupported - empty message as per spec */
 | 
						|
	else
 | 
						|
		send_empty_msg();
 | 
						|
}
 | 
						|
#endif /* CONFIG_KGDB_THREAD */
 | 
						|
 | 
						|
/*
 | 
						|
 * Bring up the ports..
 | 
						|
 */
 | 
						|
static int kgdb_serial_setup(void)
 | 
						|
{
 | 
						|
	extern int kgdb_console_setup(struct console *co, char *options);
 | 
						|
	struct console dummy;
 | 
						|
 | 
						|
	kgdb_console_setup(&dummy, 0);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* The command loop, read and act on requests */
 | 
						|
static void kgdb_command_loop(const int excep_code, const int trapa_value)
 | 
						|
{
 | 
						|
	int sigval;
 | 
						|
 | 
						|
	if (excep_code == NMI_VEC) {
 | 
						|
#ifndef CONFIG_KGDB_NMI
 | 
						|
		KGDB_PRINTK("Ignoring unexpected NMI?\n");
 | 
						|
		return;
 | 
						|
#else /* CONFIG_KGDB_NMI */
 | 
						|
		if (!kgdb_enabled) {
 | 
						|
			kgdb_enabled = 1;
 | 
						|
			kgdb_init();
 | 
						|
		}
 | 
						|
#endif /* CONFIG_KGDB_NMI */
 | 
						|
	}
 | 
						|
 | 
						|
	/* Ignore if we're disabled */
 | 
						|
	if (!kgdb_enabled)
 | 
						|
		return;
 | 
						|
 | 
						|
#ifdef CONFIG_KGDB_THREAD
 | 
						|
	/* Until GDB specifies a thread */
 | 
						|
	current_thread = NULL;
 | 
						|
	trapped_thread = current;
 | 
						|
#endif
 | 
						|
 | 
						|
	/* Enter GDB mode (e.g. after detach) */
 | 
						|
	if (!kgdb_in_gdb_mode) {
 | 
						|
		/* Do serial setup, notify user, issue preemptive ack */
 | 
						|
		kgdb_serial_setup();
 | 
						|
		KGDB_PRINTK("Waiting for GDB (on %s%d at %d baud)\n",
 | 
						|
			    (kgdb_porttype ? kgdb_porttype->name : ""),
 | 
						|
			    kgdb_portnum, kgdb_baud);
 | 
						|
		kgdb_in_gdb_mode = 1;
 | 
						|
		put_debug_char('+');
 | 
						|
	}
 | 
						|
 | 
						|
	/* Reply to host that an exception has occurred */
 | 
						|
	sigval = compute_signal(excep_code);
 | 
						|
	send_signal_msg(sigval);
 | 
						|
 | 
						|
	/* TRAP_VEC exception indicates a software trap inserted in place of
 | 
						|
	   code by GDB so back up PC by one instruction, as this instruction
 | 
						|
	   will later be replaced by its original one.  Do NOT do this for
 | 
						|
	   trap 0xff, since that indicates a compiled-in breakpoint which
 | 
						|
	   will not be replaced (and we would retake the trap forever) */
 | 
						|
	if ((excep_code == TRAP_VEC) && (trapa_value != (0xff << 2))) {
 | 
						|
		trap_registers.pc -= 2;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Undo any stepping we may have done */
 | 
						|
	undo_single_step();
 | 
						|
 | 
						|
	while (1) {
 | 
						|
 | 
						|
		out_buffer[0] = 0;
 | 
						|
		get_packet(in_buffer, BUFMAX);
 | 
						|
 | 
						|
		/* Examine first char of buffer to see what we need to do */
 | 
						|
		switch (in_buffer[0]) {
 | 
						|
 | 
						|
		case '?':	/* Send which signal we've received */
 | 
						|
			send_signal_msg(sigval);
 | 
						|
			break;
 | 
						|
 | 
						|
		case 'g':	/* Return the values of the CPU registers */
 | 
						|
			send_regs_msg();
 | 
						|
			break;
 | 
						|
 | 
						|
		case 'G':	/* Set the value of the CPU registers */
 | 
						|
			set_regs_msg();
 | 
						|
			break;
 | 
						|
 | 
						|
		case 'm':	/* Read LLLL bytes address AA..AA */
 | 
						|
			read_mem_msg();
 | 
						|
			break;
 | 
						|
 | 
						|
		case 'M':	/* Write LLLL bytes address AA..AA, ret OK */
 | 
						|
			write_mem_msg(0);	/* 0 = data in hex */
 | 
						|
			break;
 | 
						|
 | 
						|
		case 'X':	/* Write LLLL bytes esc bin address AA..AA */
 | 
						|
			if (kgdb_bits == '8')
 | 
						|
				write_mem_msg(1); /* 1 = data in binary */
 | 
						|
			else
 | 
						|
				send_empty_msg();
 | 
						|
			break;
 | 
						|
 | 
						|
		case 'C':	/* Continue, signum included, we ignore it */
 | 
						|
			continue_with_sig_msg();
 | 
						|
			return;
 | 
						|
 | 
						|
		case 'c':	/* Continue at address AA..AA (optional) */
 | 
						|
			continue_msg();
 | 
						|
			return;
 | 
						|
 | 
						|
		case 'S':	/* Step, signum included, we ignore it */
 | 
						|
			step_with_sig_msg();
 | 
						|
			return;
 | 
						|
 | 
						|
		case 's':	/* Step one instruction from AA..AA */
 | 
						|
			step_msg();
 | 
						|
			return;
 | 
						|
 | 
						|
#ifdef CONFIG_KGDB_THREAD
 | 
						|
 | 
						|
		case 'H':	/* Task related */
 | 
						|
			set_thread_msg();
 | 
						|
			break;
 | 
						|
 | 
						|
		case 'T':	/* Query thread status */
 | 
						|
			thread_status_msg();
 | 
						|
			break;
 | 
						|
 | 
						|
		case 'q':	/* Handle query - currently thread-related */
 | 
						|
			query_msg();
 | 
						|
			break;
 | 
						|
#endif
 | 
						|
 | 
						|
		case 'k':	/* 'Kill the program' with a kernel ? */
 | 
						|
			break;
 | 
						|
 | 
						|
		case 'D':	/* Detach from program, send reply OK */
 | 
						|
			kgdb_in_gdb_mode = 0;
 | 
						|
			send_ok_msg();
 | 
						|
			get_debug_char();
 | 
						|
			return;
 | 
						|
 | 
						|
		default:
 | 
						|
			send_empty_msg();
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/* There has been an exception, most likely a breakpoint. */
 | 
						|
void kgdb_handle_exception(struct pt_regs *regs)
 | 
						|
{
 | 
						|
	int excep_code, vbr_val;
 | 
						|
	int count;
 | 
						|
	int trapa_value = ctrl_inl(TRA);
 | 
						|
 | 
						|
	/* Copy kernel regs (from stack) */
 | 
						|
	for (count = 0; count < 16; count++)
 | 
						|
		trap_registers.regs[count] = regs->regs[count];
 | 
						|
	trap_registers.pc = regs->pc;
 | 
						|
	trap_registers.pr = regs->pr;
 | 
						|
	trap_registers.sr = regs->sr;
 | 
						|
	trap_registers.gbr = regs->gbr;
 | 
						|
	trap_registers.mach = regs->mach;
 | 
						|
	trap_registers.macl = regs->macl;
 | 
						|
 | 
						|
	asm("stc vbr, %0":"=r"(vbr_val));
 | 
						|
	trap_registers.vbr = vbr_val;
 | 
						|
 | 
						|
	/* Get excode for command loop call, user access */
 | 
						|
	asm("stc r2_bank, %0":"=r"(excep_code));
 | 
						|
	kgdb_excode = excep_code;
 | 
						|
 | 
						|
	/* Other interesting environment items for reference */
 | 
						|
	asm("stc r6_bank, %0":"=r"(kgdb_g_imask));
 | 
						|
	kgdb_current = current;
 | 
						|
	kgdb_trapa_val = trapa_value;
 | 
						|
 | 
						|
	/* Act on the exception */
 | 
						|
	kgdb_command_loop(excep_code >> 5, trapa_value);
 | 
						|
 | 
						|
	kgdb_current = NULL;
 | 
						|
 | 
						|
	/* Copy back the (maybe modified) registers */
 | 
						|
	for (count = 0; count < 16; count++)
 | 
						|
		regs->regs[count] = trap_registers.regs[count];
 | 
						|
	regs->pc = trap_registers.pc;
 | 
						|
	regs->pr = trap_registers.pr;
 | 
						|
	regs->sr = trap_registers.sr;
 | 
						|
	regs->gbr = trap_registers.gbr;
 | 
						|
	regs->mach = trap_registers.mach;
 | 
						|
	regs->macl = trap_registers.macl;
 | 
						|
 | 
						|
	vbr_val = trap_registers.vbr;
 | 
						|
	asm("ldc %0, vbr": :"r"(vbr_val));
 | 
						|
 | 
						|
	return;
 | 
						|
}
 | 
						|
 | 
						|
/* Trigger a breakpoint by function */
 | 
						|
void breakpoint(void)
 | 
						|
{
 | 
						|
	if (!kgdb_enabled) {
 | 
						|
		kgdb_enabled = 1;
 | 
						|
		kgdb_init();
 | 
						|
	}
 | 
						|
	BREAKPOINT();
 | 
						|
}
 | 
						|
 | 
						|
/* Initialise the KGDB data structures and serial configuration */
 | 
						|
int kgdb_init(void)
 | 
						|
{
 | 
						|
	if (!kgdb_enabled)
 | 
						|
		return 1;
 | 
						|
 | 
						|
	in_nmi = 0;
 | 
						|
	kgdb_nofault = 0;
 | 
						|
	stepped_opcode = 0;
 | 
						|
	kgdb_in_gdb_mode = 0;
 | 
						|
 | 
						|
	if (kgdb_serial_setup() != 0) {
 | 
						|
		KGDB_PRINTK("serial setup error\n");
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Init ptr to exception handler */
 | 
						|
	kgdb_debug_hook = kgdb_handle_exception;
 | 
						|
	kgdb_bus_err_hook = kgdb_handle_bus_error;
 | 
						|
 | 
						|
	/* Enter kgdb now if requested, or just report init done */
 | 
						|
	if (kgdb_halt) {
 | 
						|
		kgdb_in_gdb_mode = 1;
 | 
						|
		put_debug_char('+');
 | 
						|
		breakpoint();
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		KGDB_PRINTK("stub is initialized.\n");
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* Make function available for "user messages"; console will use it too. */
 | 
						|
 | 
						|
char gdbmsgbuf[BUFMAX];
 | 
						|
#define MAXOUT ((BUFMAX-2)/2)
 | 
						|
 | 
						|
static void kgdb_msg_write(const char *s, unsigned count)
 | 
						|
{
 | 
						|
	int i;
 | 
						|
	int wcount;
 | 
						|
	char *bufptr;
 | 
						|
 | 
						|
	/* 'O'utput */
 | 
						|
	gdbmsgbuf[0] = 'O';
 | 
						|
 | 
						|
	/* Fill and send buffers... */
 | 
						|
	while (count > 0) {
 | 
						|
		bufptr = gdbmsgbuf + 1;
 | 
						|
 | 
						|
		/* Calculate how many this time */
 | 
						|
		wcount = (count > MAXOUT) ? MAXOUT : count;
 | 
						|
		
 | 
						|
		/* Pack in hex chars */
 | 
						|
		for (i = 0; i < wcount; i++)
 | 
						|
			bufptr = pack_hex_byte(bufptr, s[i]);
 | 
						|
		*bufptr = '\0';
 | 
						|
 | 
						|
		/* Move up */
 | 
						|
		s += wcount;
 | 
						|
		count -= wcount;
 | 
						|
 | 
						|
		/* Write packet */
 | 
						|
		put_packet(gdbmsgbuf);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void kgdb_to_gdb(const char *s)
 | 
						|
{
 | 
						|
	kgdb_msg_write(s, strlen(s));
 | 
						|
}
 | 
						|
 | 
						|
#ifdef CONFIG_SH_KGDB_CONSOLE
 | 
						|
void kgdb_console_write(struct console *co, const char *s, unsigned count)
 | 
						|
{
 | 
						|
	/* Bail if we're not talking to GDB */
 | 
						|
	if (!kgdb_in_gdb_mode)
 | 
						|
		return;
 | 
						|
 | 
						|
	kgdb_msg_write(s, count);
 | 
						|
}
 | 
						|
#endif
 |