Kgdb
Table of Contents
1. Kgdb
1.1. Kgdb On Bullhead
1.1.1. 制作 usb-uart debug cable
https://pondof.fish/d/30 主要的几点:
- 需要 usb-ttl 的转换芯片
- 需要一个 4节(TRRS) 的 3.5mm 耳机接口, TRRS 依次对应于 usb-ttl 的 RX, TX, GND, VCC
1.1.2. patch kernel
1.1.2.1. patch 串口驱动以支持 poll_get and poll_put
msm_serial_hs_lite.c
diff --git a/drivers/tty/serial/msm_serial_hs_lite.c b/drivers/tty/serial/msm_serial_hs_lite.c index 87feac4ddc43..ae3a707e1ce2 100644 --- a/drivers/tty/serial/msm_serial_hs_lite.c +++ b/drivers/tty/serial/msm_serial_hs_lite.c @@ -821,6 +821,8 @@ static void msm_hsl_reset(struct uart_port *port) msm_hsl_write(port, RESET_BREAK_INT, regmap[vid][UARTDM_CR]); msm_hsl_write(port, RESET_CTS, regmap[vid][UARTDM_CR]); msm_hsl_write(port, RFR_LOW, regmap[vid][UARTDM_CR]); + /* Disable DM modes */ + msm_hsl_write(port, 0, regmap[vid][UARTDM_DMEN]); } static unsigned int msm_hsl_get_mctrl(struct uart_port *port) @@ -1355,6 +1357,136 @@ static void msm_hsl_power(struct uart_port *port, unsigned int state, } } +#ifdef CONFIG_CONSOLE_POLL + +/* defines from msm_serial.h */ +#define UARTDM_DMEN_RX_SC_ENABLE BIT(5) +#define UARTDM_DMEN_TX_SC_ENABLE BIT(4) + +#define UARTDM_RXFS_BUF_SHIFT 0x7 +#define UARTDM_RXFS_BUF_MASK 0x7 + +static int msm_poll_init(struct uart_port *port) +{ + unsigned int vid; + + vid = UART_TO_MSM(port)->ver_id; + + /* Enable single character mode on RX FIFO */ + /* A later patch from David Boyd suggests this causes problems... */ + /*msm_hsl_write(port, UARTDM_DMEN_RX_SC_ENABLE, regmap[vid][UARTDM_DMEN]);*/ + + return 0; +} + +static int msm_poll_get_char_dm(struct uart_port *port) +{ + int c; + static u32 slop; + static int count; + unsigned char *sp = (unsigned char *)&slop; + unsigned int vid; + + vid = UART_TO_MSM(port)->ver_id; + + /* Check if a previous read had more than one char */ + if (count) { + c = sp[sizeof(slop) - count]; + count--; + /* Or if FIFO is empty */ + } else if (!(msm_hsl_read(port, regmap[vid][UARTDM_SR]) & UARTDM_SR_RXRDY_BMSK)) { /* bit 0 */ + /* + * If RX packing buffer has less than a word, force stale to + * push contents into RX FIFO + */ + count = msm_hsl_read(port, regmap[vid][UARTDM_RXFS]); + + count = (count >> UARTDM_RXFS_BUF_SHIFT) & UARTDM_RXFS_BUF_MASK; + if (count) { + msm_hsl_write(port, FORCE_STALE_EVENT, regmap[vid][UARTDM_CR]); + slop = msm_hsl_read(port, regmap[vid][UARTDM_RF]); + c = sp[0]; + count--; + + msm_hsl_write(port, RESET_STALE_INT, regmap[vid][UARTDM_CR]); + msm_hsl_write(port, 0xFF, regmap[vid][UARTDM_DMRX]); + msm_hsl_write(port, STALE_EVENT_ENABLE, regmap[vid][UARTDM_CR]); + + } else { + c = NO_POLL_CHAR; + } + /* FIFO has a word */ + } else { + slop = msm_hsl_read(port, regmap[vid][UARTDM_RF]); + c = sp[0]; + count = sizeof(slop) - 1; + } + return c; +} + + +static int msm_poll_get_char(struct uart_port *port) +{ + u32 imr; + int c; + unsigned int vid; + + vid = UART_TO_MSM(port)->ver_id; + + /* Disable all interrupts */ + imr = msm_hsl_read(port, regmap[vid][UARTDM_IMR]); + msm_hsl_write(port, 0, regmap[vid][UARTDM_IMR]); + + + c = msm_poll_get_char_dm(port); + + + /* Enable interrupts */ + msm_hsl_write(port, imr, regmap[vid][UARTDM_IMR]); + return c; +} + +static void reset_dm_count(struct uart_port *port, int count) +{ + unsigned int vid; + + vid = UART_TO_MSM(port)->ver_id; + + wait_for_xmitr(port); + msm_hsl_write(port, count, regmap[vid][UARTDM_NCF_TX]); + msm_hsl_read(port, regmap[vid][UARTDM_NCF_TX]); +} + +static void msm_poll_put_char(struct uart_port *port, unsigned char c) +{ + u32 imr; + unsigned int vid; + + vid = UART_TO_MSM(port)->ver_id; + + /* Disable all interrupts */ + imr = msm_hsl_read(port, regmap[vid][UARTDM_IMR]); + msm_hsl_write(port, 0, regmap[vid][UARTDM_IMR]); + + /*if (msm_port->is_uartdm)*/ + reset_dm_count(port, 1); + + /* Wait until FIFO is empty */ + while (!(msm_hsl_read(port, regmap[vid][UARTDM_SR]) & UARTDM_SR_TXRDY_BMSK)) + cpu_relax(); + + /* Write a character */ + msm_hsl_write(port, c, regmap[vid][UARTDM_TF]); + + /* Wait until FIFO is empty */ + while (!(msm_hsl_read(port, regmap[vid][UARTDM_SR]) & UARTDM_SR_TXRDY_BMSK)) + cpu_relax(); + + /* Enable interrupts */ + msm_hsl_write(port, imr, regmap[vid][UARTDM_IMR]); + return; +} +#endif static struct uart_ops msm_hsl_uart_pops = { .tx_empty = msm_hsl_tx_empty, .set_mctrl = msm_hsl_set_mctrl, @@ -1373,6 +1505,11 @@ static struct uart_ops msm_hsl_uart_pops = { .config_port = msm_hsl_config_port, .verify_port = msm_hsl_verify_port, .pm = msm_hsl_power, + #ifdef CONFIG_CONSOLE_POLL + .poll_init = msm_poll_init, + .poll_get_char = msm_poll_get_char, + .poll_put_char = msm_poll_put_char, + #endif }; static struct msm_hsl_port msm_hsl_uart_ports[] = {
kdboc.c
diff --git a/drivers/tty/serial/kgdboc.c b/drivers/tty/serial/kgdboc.c index 10020547c60b..338c1ef7a9e8 100644 --- a/drivers/tty/serial/kgdboc.c +++ b/drivers/tty/serial/kgdboc.c @@ -29,6 +29,7 @@ static struct kgdb_io kgdboc_io_ops; static int configured = -1; static char config[MAX_CONFIG_LEN]; +static int config_retry_time = 0; static struct kparam_string kps = { .string = config, .maxlen = MAX_CONFIG_LEN, @@ -152,7 +153,26 @@ static void cleanup_kgdboc(void) if (configured == 1) kgdb_unregister_io_module(&kgdboc_io_ops); } +static int kgdbretry_option_setup(char *opt) +{ + if (strlen(opt) > MAX_CONFIG_LEN) { + printk(KERN_ERR "kgdbretry: config string too long\n"); + return -ENOSPC; + } + config_retry_time = simple_strtoul(opt, NULL, 10); + return 0; +} + +__setup("kgdbretry=", kgdbretry_option_setup); +static int configure_kgdboc(void); +static void ttycheck_func(struct work_struct *work) +{ + config_retry_time = 0; + configure_kgdboc(); +} + +static DECLARE_DELAYED_WORK(ttycheck_work, ttycheck_func); static int configure_kgdboc(void) { struct tty_driver *p; @@ -179,9 +199,16 @@ static int configure_kgdboc(void) goto do_register; p = tty_find_polling_driver(cptr, &tty_line); - if (!p) + if (!p) { + printk(KERN_INFO "kgdb will retry in %d secs\n", config_retry_time); + if (config_retry_time > 0) { + INIT_DELAYED_WORK(&ttycheck_work, ttycheck_func); + schedule_delayed_work(&ttycheck_work, config_retry_time * HZ); + return -ENODEV; + } goto noconfig; - + } + cons = console_drivers; while (cons) { int idx; @@ -335,5 +362,6 @@ module_init(init_kgdboc); module_exit(cleanup_kgdboc); module_param_call(kgdboc, param_set_kgdboc_var, param_get_string, &kps, 0644); MODULE_PARM_DESC(kgdboc, "<serial_device>[,baud]"); +MODULE_PARM_DESC(kgdbretry, "<delay in seconds> before retrying tty"); MODULE_DESCRIPTION("KGDB Console TTY Driver"); MODULE_LICENSE("GPL");
1.1.2.2. 修改 bullead_defconfig
CONFIG_CMDLINE=" kgdboc=ttyHSL0,115200" CONFIG_CMDLINE_EXTEND=y # CONFIG_STRICT_MEMORY_RWX 需要关掉, 否则无法设置 soft breakpoint CONFIG_STRICT_MEMORY_RWX=n CONFIG_DEBUG_RODATA=n CONFIG_FRAME_POINTER=y CONFIG_DEBUG_KERNEL=y CONFIG_DEBUG_INFO=y CONFIG_HAVE_ARCH_KGDB=y CONFIG_KGDB=y CONFIG_KGDB_SERIAL_CONSOLE=y CONFIG_KALLSYMS=y # CONFIG_MSM_WATCHDOG_V2 需要关掉避免 watchdog 重启 CONFIG_MSM_WATCHDOG_V2=n
1.1.3. 修改 boot.img 中的 kernel cmdline
#> abootimg -i out/target/product/bullhead/boot.img ... * cmdline = console=ttyHSL0,115200,n8 androidboot.hardware=bullhead boot_cpus=0-5 lpm_levels.sleep_disabled=1 msm_poweroff.download_mode=0 buildvariant=userdebug ... ; 给 cmdline 加上一条: kgdboc=ttyHSL0,115200 kgdbretry=2 #> abootimg -u out/target/product/bullhead/boot.img -c 'cmdline=console=ttyHSL0,115200,n8 kgdboc=ttyHSL0,115200 kgdbretry=2 androidboot.hardware=bullhead boot_cpus=0-5 lpm_levels.sleep_disabled=1 msm_poweroff.download_mode=0 buildvariant=userdebug'
1.1.4. patch gdb 以避免 `packet reply is too long` 的问题
--- gdb-7.3.1-orign/gdb/remote.c 2011-07-15 10:04:29.000000000 +0800 +++ gdb-7.3.1/gdb/remote.c 2011-12-27 18:37:34.319902796 +0800 @@ -5702,9 +5702,21 @@ buf_len = strlen (rs->buf); /* Further sanity checks, with knowledge of the architecture. */ +#if 0 if (buf_len > 2 * rsa->sizeof_g_packet) error (_("Remote 'g' packet reply is too long: %s"), rs->buf); - +#endif + if (buf_len > 2 * rsa->sizeof_g_packet) { + rsa->sizeof_g_packet = buf_len ; + for (i = 0; i < gdbarch_num_regs (gdbarch); i++) { + if (rsa->regs[i].pnum == -1) + continue; + if (rsa->regs[i].offset >= rsa->sizeof_g_packet) + rsa->regs[i].in_g_packet = 0; + else + rsa->regs[i].in_g_packet = 1; + } + } /* Save the size of the packet sent to us by the target. It is used as a heuristic when determining the max size of packets that the target can safely receive. */
patch 后使用 `./configure –target=aarch64-linux-gnu` 来编译 gdb
1.1.5. 测试
#> adb root #> adb shell 'echo -n g > /proc/sysrq-trigger' // sysrq 会阻塞, 需要在新的 shell 中执行 gdb #> sudo /usr/local/bin/aarch64-linux-gnu-gdb kernel/vmlinux (gdb) target remote /dev/ttyUSB0 Remote debugging using /dev/ttyUSB0 arch_kgdb_breakpoint () at /home/wei.sun/source/aosp-7.1/kernel/arch/arm64/include/asm/kgdb.h:32 32 asm ("brk %0" : : "I" (KDBG_COMPILED_DBG_BRK_IMM)); (gdb) b vfs_read Breakpoint 1 at 0xffffffc00018ba94: file fs/read_write.c, line 374. (gdb) c Continuing. [Switching to Thread 333] Thread 261 hit Breakpoint 1, vfs_read (file=0xffffffc06c225d00, buf=0x709f3be204 "<3>[ 34.003496] pn548_dev_read: no more interrupt after 2000ms (0)!", count=4067, pos=0xffffffc06a143ec0) at fs/read_write.c:374 374 { (gdb)
1.1.6. notes
- 不要同时使用 minicom 和 kgdb 操作串口, 除非使用 agent-proxy
- 相关链接 https://gist.github.com/jduck/caf1eddcd4c9ac27d818 http://tinylab.org/kgdb-debugging-kernel/ http://stackoverflow.com/questions/6697754/android-kernel-debugging https://github.com/ctxis/kgdb-android/tree/master/drivers/tty http://mathslinux.org/?p=130 https://sourceware.org/bugzilla/show_bug.cgi?id=13984