Kgdb

Table of Contents

1. Kgdb

1.1. Kgdb On Bullhead

1.1.1. 制作 usb-uart debug cable

https://pondof.fish/d/30 主要的几点:

  1. 需要 usb-ttl 的转换芯片
  2. 需要一个 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)

Author: [email protected]
Date: 2017-12-06 Wed 00:00
Last updated: 2019-07-12 Fri 15:11

知识共享许可协议