123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491 |
- /*
- * 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
|