📄 kgdb-stub.c
字号:
/**************************************************************************** * Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $ * * Module name: remcom.c $ * Revision: 1.34 $ * Date: 91/03/09 12:29:49 $ * Contributor: Lake Stevens Instrument Division$ * * Description: low level support for gdb debugger. $ * * Considerations: only works on target hardware $ * * Written by: Glenn Engel $ * ModuleState: Experimental $ * * NOTES: See Below $ * * Modified for 386 by Jim Kingdon, Cygnus Support. * * To enable debugger support, two things need to happen. One, 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. * Two, a breakpoint needs to be generated to begin communication. This * is most easily accomplished by a call to breakpoint(). Breakpoint() * simulates a breakpoint by executing a trap #1. * * The external function exceptionHandler() is * used to attach a specific handler to a specific 386 vector number. * It should use the same privilege level it runs at. It should * install it as an interrupt gate so that interrupts are masked * while the handler runs. * * Because gdb will sometimes write to the stack area to execute function * calls, this program cannot rely on using the supervisor stack so it * uses it's own stack area reserved in the int array remcomStack. * ************* * * 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 * * All commands and responses are sent with a packet which includes a * checksum. A packet consists of * * $<packet info>#<checksum>. * * where * <packet info> :: <characters representing the command or response> * <checksum> :: < two hex digits computed as modulo 256 sum of <packetinfo>> * * When a packet is received, it is first acknowledged with either '+' or '-'. * '+' indicates a successful transfer. '-' indicates a failed transfer. * * Example: * * Host: Reply: * $m0,10#2a +$00010203040506070809101112131415#42 * ****************************************************************************//* * * 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". *//* * ARM port Copyright (c) 2002 MontaVista Software, Inc * * Authors: George Davis <davis_g@mvista.com> * Deepak Saxena <dsaxena@mvista.com> * * * See Documentation/ARM/kgdb for information on porting to a new board * * tabstop=3 to make this readable */#include <linux/config.h>#include <linux/types.h>#include <linux/kernel.h>#include <linux/signal.h>#include <linux/sched.h>#include <linux/mm.h>#include <linux/spinlock.h>#include <linux/personality.h>#include <linux/ptrace.h>#include <linux/elf.h>#include <linux/interrupt.h>#include <linux/init.h>#include <asm/atomic.h>#include <asm/io.h>#include <asm/pgtable.h>#include <asm/system.h>#include <asm/uaccess.h>#include <asm/unistd.h>#include <asm/kgdb.h>#ifdef CONFIG_MAGIC_SYSRQ#include <linux/sysrq.h>#endif#ifdef CONFIG_DEBUG_LL// #define printascii(x, args...) printk(x, ## args)// #undef printascii// #define printascii(x, args...)extern void printascii(const char *);extern void printhex8(unsigned int);#else#define printascii(s)#define printhex8(i)#endif#define GDB_MAXREGS (16 + 8 * 3 + 1 + 1)#define GDB_REGBYTES (GDB_MAXREGS << 2)#define BUFMAX 2048#define PC_REGNUM 0x0f#define LR_REGNUM 0x0e#define SP_REGNUM 0x0d/* External functions */extern struct pt_regs *get_task_registers(const struct task_struct *);/* Forward declarations */static unsigned long get_next_pc(struct pt_regs *);/* Globals */int kgdb_fault_expected = 0; /* Boolean to ignore bus errs (i.e. in GDB) */int kgdb_enabled = 0;/* Locals */static char remote_debug = 0;static const char hexchars[]="0123456789abcdef";static int fault_jmp_buf[32]; /* Jump buffer for kgdb_setjmp/longjmp */static char remcomInBuffer[BUFMAX];static char remcomOutBuffer[BUFMAX];static int kgdb_initialized = 0;static volatile unsigned int *step_addr = NULL;static unsigned int step_instr = 0;static struct pt_regs kgdb_regs;static unsigned int gdb_regs[GDB_MAXREGS];#ifdef CONFIG_KGDB_THREADstatic struct task_struct *trapped_thread;static struct task_struct *current_thread;typedef unsigned char threadref[8];#define BUF_THREAD_ID_SIZE 16#endif/* * Various conversion functions */static int hex(unsigned 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);}static unsigned char * mem2hex(const char *mem, char *buf, int count){ /* Accessing 16-bit and 32-bit objects in a single * load instruction is required to avoid bad side * effects for some IO registers. */ if ((count == 2) && (((long)mem & 1) == 0)) { unsigned short tmp_s = cpu_to_be16(*(unsigned short *)mem); mem += 2; *buf++ = hexchars[(tmp_s >> 12) & 0xf]; *buf++ = hexchars[(tmp_s >> 8) & 0xf]; *buf++ = hexchars[(tmp_s >> 4) & 0xf]; *buf++ = hexchars[tmp_s & 0xf]; } else if ((count == 4) && (((long)mem & 3) == 0)) { unsigned int tmp_l = cpu_to_be32(*(unsigned int *)mem); mem += 4; *buf++ = hexchars[(tmp_l >> 28) & 0xf]; *buf++ = hexchars[(tmp_l >> 24) & 0xf]; *buf++ = hexchars[(tmp_l >> 20) & 0xf]; *buf++ = hexchars[(tmp_l >> 16) & 0xf]; *buf++ = hexchars[(tmp_l >> 12) & 0xf]; *buf++ = hexchars[(tmp_l >> 8) & 0xf]; *buf++ = hexchars[(tmp_l >> 4) & 0xf]; *buf++ = hexchars[tmp_l & 0xf]; } else { unsigned char ch; while (count-- > 0) { ch = *mem++; *buf++ = hexchars[ch >> 4]; *buf++ = hexchars[ch & 0xf]; } } *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 * hex2mem(char *buf, char *mem, int count){ /* Accessing 16-bit and 32-bit objects in a single * store instruction is required to avoid bad side * effects for some IO registers. */ if ((count == 2) && (((long)mem & 1) == 0)) { unsigned short tmp_s = hex(*buf++) << 12; tmp_s |= hex(*buf++) << 8; tmp_s |= hex(*buf++) << 4; tmp_s |= hex(*buf++); *(unsigned short *)mem = be16_to_cpu(tmp_s); mem += 2; } else if ((count == 4) && (((long)mem & 3) == 0)) { unsigned int tmp_l = hex(*buf++) << 28; tmp_l |= hex(*buf++) << 24; tmp_l |= hex(*buf++) << 20; tmp_l |= hex(*buf++) << 16; tmp_l |= hex(*buf++) << 12; tmp_l |= hex(*buf++) << 8; tmp_l |= hex(*buf++) << 4; tmp_l |= hex(*buf++); *(unsigned int *)mem = be32_to_cpu(tmp_l); mem += 4; } else { int i; unsigned char ch; for (i=0; i<count; i++) { ch = hex(*buf++) << 4; ch |= hex(*buf++); *mem++ = ch; } } return mem;}/* 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 *ebin2mem(const char *buf, char *mem, int count){ for (; count > 0; count--, buf++) { if (*buf == 0x7d) *mem++ = *(++buf) ^ 0x20; else *mem++ = *buf; } return mem;}/* * While we find nice hex chars, build an int. * Return number of chars processed. */static int hex2int(char **ptr, int *intValue){ int numChars = 0; int hexValue; *intValue = 0; while (**ptr) { hexValue = hex(**ptr); if (hexValue < 0) break; *intValue = (*intValue << 4) | hexValue; numChars ++; (*ptr)++; } return (numChars);}/* Make a local copy of the registers passed into the handler (bletch) */static void kregs2gregs(const struct pt_regs *kregs, int *gregs){ int regno; /* Initialize to zero */ for (regno = 0; regno < GDB_MAXREGS; regno++) gregs[regno] = 0; gregs[0] = kregs->ARM_r0; gregs[1] = kregs->ARM_r1; gregs[2] = kregs->ARM_r2; gregs[3] = kregs->ARM_r3; gregs[4] = kregs->ARM_r4; gregs[5] = kregs->ARM_r5; gregs[6] = kregs->ARM_r6; gregs[7] = kregs->ARM_r7; gregs[8] = kregs->ARM_r8; gregs[9] = kregs->ARM_r9; gregs[10] = kregs->ARM_r10; gregs[11] = kregs->ARM_fp; gregs[12] = kregs->ARM_ip; gregs[13] = kregs->ARM_sp; gregs[14] = kregs->ARM_lr; gregs[15] = kregs->ARM_pc; gregs[GDB_MAXREGS - 1] = kregs->ARM_cpsr;}/* Copy local gdb registers back to kgdb regs, for later copy to kernel */static void gregs2kregs(const int *gregs, struct pt_regs *kregs){ kregs->ARM_r0 = gregs[0]; kregs->ARM_r1 = gregs[1]; kregs->ARM_r2 = gregs[2]; kregs->ARM_r3 = gregs[3]; kregs->ARM_r4 = gregs[4]; kregs->ARM_r5 = gregs[5]; kregs->ARM_r6 = gregs[6]; kregs->ARM_r7 = gregs[7]; kregs->ARM_r8 = gregs[8]; kregs->ARM_r9 = gregs[9]; kregs->ARM_r10 = gregs[10]; kregs->ARM_fp = gregs[11]; kregs->ARM_ip = gregs[12]; kregs->ARM_sp = gregs[13]; kregs->ARM_lr = gregs[14]; kregs->ARM_pc = gregs[15]; kregs->ARM_cpsr = gregs[GDB_MAXREGS - 1];}#ifdef CONFIG_KGDB_THREAD/* Make a local copy of registers from the specified thread */static void tregs2gregs(const struct task_struct *task, int *gregs){ int regno; struct pt_regs *tregs; /* Initialize to zero */ for (regno = 0; regno < GDB_MAXREGS; regno++) gregs[regno] = 0; /* Just making sure... */ if (task == NULL) return;#if 0 /* REVISIT */ /* A new fork has pt_regs on the stack from a fork() call */ if (task->thread->save.pc == (unsigned long)ret_from_fork) { struct pt_regs *tregs; kregs = (struct pt_regs*)task->thread->save; gregs[0] = tregs->ARM_r0; gregs[1] = tregs->ARM_r1; gregs[2] = tregs->ARM_r2; gregs[3] = tregs->ARM_r3; gregs[4] = tregs->ARM_r4; gregs[5] = tregs->ARM_r5; gregs[6] = tregs->ARM_r6; gregs[7] = tregs->ARM_r7; gregs[8] = tregs->ARM_r8; gregs[9] = tregs->ARM_r9; gregs[10] = tregs->ARM_r10; gregs[11] = tregs->ARM_fp; gregs[12] = tregs->ARM_ip; gregs[13] = tregs->ARM_sp; gregs[14] = tregs->ARM_lr; gregs[15] = tregs->ARM_pc; gregs[GDB_MAXREGS - 1] = tregs->ARM_cpsr; }#endif /* Otherwise, we have only some registers from switch_to() */ tregs = get_task_registers(task); gregs[0] = tregs->ARM_r0; /* Not really valid? */ gregs[1] = tregs->ARM_r1; /* " " */ gregs[2] = tregs->ARM_r2; /* " " */ gregs[3] = tregs->ARM_r3; /* " " */ gregs[4] = tregs->ARM_r4; gregs[5] = tregs->ARM_r5; gregs[6] = tregs->ARM_r6; gregs[7] = tregs->ARM_r7; gregs[8] = tregs->ARM_r8; gregs[9] = tregs->ARM_r9; gregs[10] = tregs->ARM_r10; gregs[11] = tregs->ARM_fp; gregs[12] = tregs->ARM_ip; gregs[13] = tregs->ARM_sp; gregs[14] = tregs->ARM_lr; gregs[15] = tregs->ARM_pc; gregs[GDB_MAXREGS - 1] = tregs->ARM_cpsr;}static char * pack_hex_byte(char *pkt, int byte){ *pkt++ = hexchars[(byte >> 4) & 0xf]; *pkt++ = hexchars[(byte & 0xf)]; return pkt;}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;}static void int_to_threadref(threadref * id, const int value){ unsigned char *scan = (unsigned char *) id; int i = 4;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -