📄 kgdb_stub.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_THREADstatic 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -