📄 gdb-stub.c
字号:
/* gdb-stub.c: FRV GDB stub * * Copyright (C) 2003,4 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * - Derived from Linux/MIPS version, Copyright (C) 1995 Andreas Busse * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. *//* * 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 BREAK instruction. * * * 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 * * c Resume at current address SNN ( signal NN) * cAA..AA Continue at address AA..AA SNN * * s Step one instruction SNN * sAA..AA Step one instruction from AA..AA SNN * * k kill * * ? What was the last sigval ? SNN (signal NN) * * bBB..BB Set baud rate to BB..BB OK or BNN, then sets * baud rate * * 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 * * * ============== * MORE EXAMPLES: * ============== * * For reference -- the following are the steps that one * company took (RidgeRun Inc) to get remote gdb debugging * going. In this scenario the host machine was a PC and the * target platform was a Galileo EVB64120A MIPS evaluation * board. * * Step 1: * First download gdb-5.0.tar.gz from the internet. * and then build/install the package. * * Example: * $ tar zxf gdb-5.0.tar.gz * $ cd gdb-5.0 * $ ./configure --target=frv-elf-gdb * $ make * $ frv-elf-gdb * * Step 2: * Configure linux for remote debugging and build it. * * Example: * $ cd ~/linux * $ make menuconfig <go to "Kernel Hacking" and turn on remote debugging> * $ make dep; make vmlinux * * Step 3: * Download the kernel to the remote target and start * the kernel running. It will promptly halt and wait * for the host gdb session to connect. It does this * since the "Kernel Hacking" option has defined * CONFIG_REMOTE_DEBUG which in turn enables your calls * to: * set_debug_traps(); * breakpoint(); * * Step 4: * Start the gdb session on the host. * * Example: * $ frv-elf-gdb vmlinux * (gdb) set remotebaud 115200 * (gdb) target remote /dev/ttyS1 * ...at this point you are connected to * the remote target and can use gdb * in the normal fasion. Setting * breakpoints, single stepping, * printing variables, etc. * */#include <linux/string.h>#include <linux/kernel.h>#include <linux/signal.h>#include <linux/sched.h>#include <linux/mm.h>#include <linux/console.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/nmi.h>#include <asm/pgtable.h>#include <asm/system.h>#include <asm/gdb-stub.h>#define LEDS(x) do { /* *(u32*)0xe1200004 = ~(x); mb(); */ } while(0)#undef GDBSTUB_DEBUG_PROTOCOLextern void debug_to_serial(const char *p, int n);extern void gdbstub_console_write(struct console *co, const char *p, unsigned n);extern volatile uint32_t __break_error_detect[3]; /* ESFR1, ESR15, EAR15 */extern struct user_context __break_user_context;struct __debug_amr { unsigned long L, P;} __attribute__((aligned(8)));struct __debug_mmu { struct { unsigned long hsr0, pcsr, esr0, ear0, epcr0;#ifdef CONFIG_MMU unsigned long tplr, tppr, tpxr, cxnr;#endif } regs; struct __debug_amr iamr[16]; struct __debug_amr damr[16];#ifdef CONFIG_MMU struct __debug_amr tlb[64*2];#endif};static struct __debug_mmu __debug_mmu;/* * BUFMAX defines the maximum number of characters in inbound/outbound buffers * at least NUMREGBYTES*2 are needed for register packets */#define BUFMAX 2048#define BREAK_INSN 0x801000c0 /* use "break" as bkpt */static const char gdbstub_banner[] = "Linux/FR-V GDB Stub (c) RedHat 2003\n";volatile u8 gdbstub_rx_buffer[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));volatile u32 gdbstub_rx_inp = 0;volatile u32 gdbstub_rx_outp = 0;volatile u8 gdbstub_rx_overflow = 0;u8 gdbstub_rx_unget = 0;/* set with GDB whilst running to permit step through exceptions */extern volatile u32 __attribute__((section(".bss"))) gdbstub_trace_through_exceptions;static char input_buffer[BUFMAX];static char output_buffer[BUFMAX];static const char hexchars[] = "0123456789abcdef";static const char *regnames[] = { "PSR ", "ISR ", "CCR ", "CCCR", "LR ", "LCR ", "PC ", "_stt", "sys ", "GR8*", "GNE0", "GNE1", "IACH", "IACL", "TBR ", "SP ", "FP ", "GR3 ", "GR4 ", "GR5 ", "GR6 ", "GR7 ", "GR8 ", "GR9 ", "GR10", "GR11", "GR12", "GR13", "GR14", "GR15", "GR16", "GR17", "GR18", "GR19", "GR20", "GR21", "GR22", "GR23", "GR24", "GR25", "GR26", "GR27", "EFRM", "CURR", "GR30", "BFRM"};struct gdbstub_bkpt { unsigned long addr; /* address of breakpoint */ unsigned len; /* size of breakpoint */ uint32_t originsns[7]; /* original instructions */};static struct gdbstub_bkpt gdbstub_bkpts[256];/* * local prototypes */static void gdbstub_recv_packet(char *buffer);static int gdbstub_send_packet(char *buffer);static int gdbstub_compute_signal(unsigned long tbr);static int hex(unsigned char ch);static int hexToInt(char **ptr, unsigned long *intValue);static unsigned char *mem2hex(const void *mem, char *buf, int count, int may_fault);static char *hex2mem(const char *buf, void *_mem, int count);/* * Convert ch from a hex digit to an int */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;}void gdbstub_printk(const char *fmt, ...){ static char buf[1024]; va_list args; int len; /* Emit the output into the temporary buffer */ va_start(args, fmt); len = vsnprintf(buf, sizeof(buf), fmt, args); va_end(args); debug_to_serial(buf, len);}static inline char *gdbstub_strcpy(char *dst, const char *src){ int loop = 0; while ((dst[loop] = src[loop])) loop++; return dst;}static void gdbstub_purge_cache(void){ asm volatile(" dcef @(gr0,gr0),#1 \n" " icei @(gr0,gr0),#1 \n" " membar \n" " bar \n" );}/*****************************************************************************//* * scan for the sequence $<data>#<checksum> */static void gdbstub_recv_packet(char *buffer){ unsigned char checksum; unsigned char xmitcsum; unsigned char ch; int count, i, ret, error; for (;;) { /* wait around for the start character, ignore all other characters */ do { gdbstub_rx_char(&ch, 0); } while (ch != '$'); checksum = 0; xmitcsum = -1; count = 0; error = 0; /* now, read until a # or end of buffer is found */ while (count < BUFMAX) { ret = gdbstub_rx_char(&ch, 0); if (ret < 0) error = ret; if (ch == '#') break; checksum += ch; buffer[count] = ch; count++; } if (error == -EIO) { gdbstub_proto("### GDB Rx Error - Skipping packet ###\n"); gdbstub_proto("### GDB Tx NAK\n"); gdbstub_tx_char('-'); continue; } if (count >= BUFMAX || error) continue; buffer[count] = 0; /* read the checksum */ ret = gdbstub_rx_char(&ch, 0); if (ret < 0) error = ret; xmitcsum = hex(ch) << 4; ret = gdbstub_rx_char(&ch, 0); if (ret < 0) error = ret; xmitcsum |= hex(ch); if (error) { if (error == -EIO) gdbstub_proto("### GDB Rx Error - Skipping packet\n"); gdbstub_proto("### GDB Tx NAK\n"); gdbstub_tx_char('-'); continue; } /* check the checksum */ if (checksum != xmitcsum) { gdbstub_proto("### GDB Tx NAK\n"); gdbstub_tx_char('-'); /* failed checksum */ continue; } gdbstub_proto("### GDB Rx '$%s#%02x' ###\n", buffer, checksum); gdbstub_proto("### GDB Tx ACK\n"); gdbstub_tx_char('+'); /* successful transfer */ /* if a sequence char is present, reply the sequence ID */ if (buffer[2] == ':') { gdbstub_tx_char(buffer[0]); gdbstub_tx_char(buffer[1]); /* remove sequence chars from buffer */ count = 0; while (buffer[count]) count++; for (i=3; i <= count; i++) buffer[i - 3] = buffer[i]; } break; }} /* end gdbstub_recv_packet() *//*****************************************************************************//* * send the packet in buffer. * - return 0 if successfully ACK'd * - return 1 if abandoned due to new incoming packet */static int gdbstub_send_packet(char *buffer){ unsigned char checksum; int count; unsigned char ch; /* $<packet info>#<checksum> */ gdbstub_proto("### GDB Tx '%s' ###\n", buffer); do { gdbstub_tx_char('$'); checksum = 0; count = 0; while ((ch = buffer[count]) != 0) { gdbstub_tx_char(ch); checksum += ch; count += 1; } gdbstub_tx_char('#'); gdbstub_tx_char(hexchars[checksum >> 4]); gdbstub_tx_char(hexchars[checksum & 0xf]); } while (gdbstub_rx_char(&ch,0),#ifdef GDBSTUB_DEBUG_PROTOCOL ch=='-' && (gdbstub_proto("### GDB Rx NAK\n"),0), ch!='-' && ch!='+' && (gdbstub_proto("### GDB Rx ??? %02x\n",ch),0),#endif ch!='+' && ch!='$'); if (ch=='+') { gdbstub_proto("### GDB Rx ACK\n"); return 0; } gdbstub_proto("### GDB Tx Abandoned\n"); gdbstub_rx_unget = ch; return 1;} /* end gdbstub_send_packet() *//* * While we find nice hex chars, build an int. * Return number of chars processed. */static int hexToInt(char **ptr, unsigned long *_value){ int count = 0, ch; *_value = 0; while (**ptr) { ch = hex(**ptr); if (ch < 0) break; *_value = (*_value << 4) | ((uint8_t) ch & 0xf); count++; (*ptr)++; } return count;}/*****************************************************************************//* * probe an address to see whether it maps to anything */static inline int gdbstub_addr_probe(const void *vaddr){#ifdef CONFIG_MMU unsigned long paddr; asm("lrad %1,%0,#1,#0,#0" : "=r"(paddr) : "r"(vaddr)); if (!(paddr & xAMPRx_V)) return 0;#endif return 1;} /* end gdbstub_addr_probe() */#ifdef CONFIG_MMUstatic unsigned long __saved_dampr, __saved_damlr;static inline unsigned long gdbstub_virt_to_pte(unsigned long vaddr){ pgd_t *pgd; pud_t *pud; pmd_t *pmd; pte_t *pte; unsigned long val, dampr5; pgd = (pgd_t *) __get_DAMLR(3) + pgd_index(vaddr); pud = pud_offset(pgd, vaddr); pmd = pmd_offset(pud, vaddr); if (pmd_bad(*pmd) || !pmd_present(*pmd)) return 0; /* make sure dampr5 maps to the correct pmd */ dampr5 = __get_DAMPR(5); val = pmd_val(*pmd); __set_DAMPR(5, val | xAMPRx_L | xAMPRx_SS_16Kb | xAMPRx_S | xAMPRx_C | xAMPRx_V); /* now its safe to access pmd */ pte = (pte_t *)__get_DAMLR(5) + __pte_index(vaddr); if (pte_present(*pte)) val = pte_val(*pte); else val = 0; /* restore original dampr5 */ __set_DAMPR(5, dampr5); return val;}#endifstatic inline int gdbstub_addr_map(const void *vaddr){#ifdef CONFIG_MMU unsigned long pte; __saved_dampr = __get_DAMPR(2); __saved_damlr = __get_DAMLR(2);#endif if (gdbstub_addr_probe(vaddr)) return 1;#ifdef CONFIG_MMU pte = gdbstub_virt_to_pte((unsigned long) vaddr); if (pte) { __set_DAMPR(2, pte); __set_DAMLR(2, (unsigned long) vaddr & PAGE_MASK); return 1; }#endif return 0;}static inline void gdbstub_addr_unmap(void){#ifdef CONFIG_MMU __set_DAMPR(2, __saved_dampr); __set_DAMLR(2, __saved_damlr);#endif}/* * access potentially dodgy memory through a potentially dodgy pointer */static inline int gdbstub_read_dword(const void *addr, uint32_t *_res){ unsigned long brr; uint32_t res; if (!gdbstub_addr_map(addr)) return 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -