Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jwessel...
Linus Torvalds [Thu, 5 Aug 2010 22:59:48 +0000 (15:59 -0700)]
* 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jwessel/linux-2.6-kgdb:
  debug_core,kdb: fix crash when arch does not have single step
  kgdb,x86: use macro HBP_NUM to replace magic number 4
  kgdb,mips: remove unused kgdb_cpu_doing_single_step operations
  mm,kdb,kgdb: Add a debug reference for the kdb kmap usage
  KGDB: Remove set but unused newPC
  ftrace,kdb: Allow dumping a specific cpu's buffer with ftdump
  ftrace,kdb: Extend kdb to be able to dump the ftrace buffer
  kgdb,powerpc: Replace hardcoded offset by BREAK_INSTR_SIZE
  arm,kgdb: Add ability to trap into debugger on notify_die
  gdbstub: do not directly use dbg_reg_def[] in gdb_cmd_reg_set()
  gdbstub: Implement gdbserial 'p' and 'P' packets
  kgdb,arm: Individual register get/set for arm
  kgdb,mips: Individual register get/set for mips
  kgdb,x86: Individual register get/set for x86
  kgdb,kdb: individual register set and and get API
  gdbstub: Optimize kgdb's "thread:" response for the gdb serial protocol
  kgdb: remove custom hex_to_bin()implementation

1  2 
kernel/debug/debug_core.c
kernel/debug/gdbstub.c

@@@ -6,7 -6,7 +6,7 @@@
   * Copyright (C) 2000-2001 VERITAS Software Corporation.
   * Copyright (C) 2002-2004 Timesys Corporation
   * Copyright (C) 2003-2004 Amit S. Kale <amitkale@linsyssoft.com>
 - * Copyright (C) 2004 Pavel Machek <pavel@suse.cz>
 + * Copyright (C) 2004 Pavel Machek <pavel@ucw.cz>
   * Copyright (C) 2004-2006 Tom Rini <trini@kernel.crashing.org>
   * Copyright (C) 2004-2006 LinSysSoft Technologies Pvt. Ltd.
   * Copyright (C) 2005-2009 Wind River Systems, Inc.
@@@ -605,6 -605,8 +605,8 @@@ cpu_master_loop
                if (dbg_kdb_mode) {
                        kgdb_connected = 1;
                        error = kdb_stub(ks);
+                       if (error == -1)
+                               continue;
                        kgdb_connected = 0;
                } else {
                        error = gdb_serial_stub(ks);
diff --combined kernel/debug/gdbstub.c
@@@ -6,7 -6,7 +6,7 @@@
   * Copyright (C) 2000-2001 VERITAS Software Corporation.
   * Copyright (C) 2002-2004 Timesys Corporation
   * Copyright (C) 2003-2004 Amit S. Kale <amitkale@linsyssoft.com>
 - * Copyright (C) 2004 Pavel Machek <pavel@suse.cz>
 + * Copyright (C) 2004 Pavel Machek <pavel@ucw.cz>
   * Copyright (C) 2004-2006 Tom Rini <trini@kernel.crashing.org>
   * Copyright (C) 2004-2006 LinSysSoft Technologies Pvt. Ltd.
   * Copyright (C) 2005-2009 Wind River Systems, Inc.
@@@ -52,17 -52,6 +52,6 @@@ static unsigned long         gdb_regs[(NUMREGB
   * GDB remote protocol parser:
   */
  
- static int hex(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;
- }
  #ifdef CONFIG_KGDB_KDB
  static int gdbstub_read_wait(void)
  {
@@@ -123,8 -112,8 +112,8 @@@ static void get_packet(char *buffer
                buffer[count] = 0;
  
                if (ch == '#') {
-                       xmitcsum = hex(gdbstub_read_wait()) << 4;
-                       xmitcsum += hex(gdbstub_read_wait());
+                       xmitcsum = hex_to_bin(gdbstub_read_wait()) << 4;
+                       xmitcsum += hex_to_bin(gdbstub_read_wait());
  
                        if (checksum != xmitcsum)
                                /* failed checksum */
@@@ -236,7 -225,7 +225,7 @@@ void gdbstub_msg_write(const char *s, i
   * buf.  Return a pointer to the last char put in buf (null). May
   * return an error.
   */
- int kgdb_mem2hex(char *mem, char *buf, int count)
+ char *kgdb_mem2hex(char *mem, char *buf, int count)
  {
        char *tmp;
        int err;
        tmp = buf + count;
  
        err = probe_kernel_read(tmp, mem, count);
-       if (!err) {
-               while (count > 0) {
-                       buf = pack_hex_byte(buf, *tmp);
-                       tmp++;
-                       count--;
-               }
-               *buf = 0;
+       if (err)
+               return NULL;
+       while (count > 0) {
+               buf = pack_hex_byte(buf, *tmp);
+               tmp++;
+               count--;
        }
+       *buf = 0;
  
-       return err;
+       return buf;
  }
  
  /*
@@@ -280,8 -268,8 +268,8 @@@ int kgdb_hex2mem(char *buf, char *mem, 
        tmp_hex = tmp_raw - 1;
        while (tmp_hex >= buf) {
                tmp_raw--;
-               *tmp_raw = hex(*tmp_hex--);
-               *tmp_raw |= hex(*tmp_hex--) << 4;
+               *tmp_raw = hex_to_bin(*tmp_hex--);
+               *tmp_raw |= hex_to_bin(*tmp_hex--) << 4;
        }
  
        return probe_kernel_write(mem, tmp_raw, count);
@@@ -304,7 -292,7 +292,7 @@@ int kgdb_hex2long(char **ptr, unsigned 
                (*ptr)++;
        }
        while (**ptr) {
-               hex_val = hex(**ptr);
+               hex_val = hex_to_bin(**ptr);
                if (hex_val < 0)
                        break;
  
@@@ -339,6 -327,32 +327,32 @@@ static int kgdb_ebin2mem(char *buf, cha
        return probe_kernel_write(mem, c, size);
  }
  
+ #if DBG_MAX_REG_NUM > 0
+ void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+ {
+       int i;
+       int idx = 0;
+       char *ptr = (char *)gdb_regs;
+       for (i = 0; i < DBG_MAX_REG_NUM; i++) {
+               dbg_get_reg(i, ptr + idx, regs);
+               idx += dbg_reg_def[i].size;
+       }
+ }
+ void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+ {
+       int i;
+       int idx = 0;
+       char *ptr = (char *)gdb_regs;
+       for (i = 0; i < DBG_MAX_REG_NUM; i++) {
+               dbg_set_reg(i, ptr + idx, regs);
+               idx += dbg_reg_def[i].size;
+       }
+ }
+ #endif /* DBG_MAX_REG_NUM > 0 */
  /* Write memory due to an 'M' or 'X' packet. */
  static int write_mem_msg(int binary)
  {
@@@ -378,28 -392,31 +392,31 @@@ static void error_packet(char *pkt, in
   * remapped to negative TIDs.
   */
  
- #define BUF_THREAD_ID_SIZE    16
+ #define BUF_THREAD_ID_SIZE    8
  
  static char *pack_threadid(char *pkt, unsigned char *id)
  {
-       char *limit;
+       unsigned char *limit;
+       int lzero = 1;
+       limit = id + (BUF_THREAD_ID_SIZE / 2);
+       while (id < limit) {
+               if (!lzero || *id != 0) {
+                       pkt = pack_hex_byte(pkt, *id);
+                       lzero = 0;
+               }
+               id++;
+       }
  
-       limit = pkt + BUF_THREAD_ID_SIZE;
-       while (pkt < limit)
-               pkt = pack_hex_byte(pkt, *id++);
+       if (lzero)
+               pkt = pack_hex_byte(pkt, 0);
  
        return pkt;
  }
  
  static void int_to_threadref(unsigned char *id, int value)
  {
-       unsigned char *scan;
-       int i = 4;
-       scan = (unsigned char *)id;
-       while (i--)
-               *scan++ = 0;
-       put_unaligned_be32(value, scan);
+       put_unaligned_be32(value, id);
  }
  
  static struct task_struct *getthread(struct pt_regs *regs, int tid)
@@@ -463,8 -480,7 +480,7 @@@ static void gdb_cmd_status(struct kgdb_
        pack_hex_byte(&remcom_out_buffer[1], ks->signo);
  }
  
- /* Handle the 'g' get registers request */
- static void gdb_cmd_getregs(struct kgdb_state *ks)
+ static void gdb_get_regs_helper(struct kgdb_state *ks)
  {
        struct task_struct *thread;
        void *local_debuggerinfo;
                 */
                sleeping_thread_to_gdb_regs(gdb_regs, thread);
        }
+ }
+ /* Handle the 'g' get registers request */
+ static void gdb_cmd_getregs(struct kgdb_state *ks)
+ {
+       gdb_get_regs_helper(ks);
        kgdb_mem2hex((char *)gdb_regs, remcom_out_buffer, NUMREGBYTES);
  }
  
@@@ -527,13 -549,13 +549,13 @@@ static void gdb_cmd_memread(struct kgdb
        char *ptr = &remcom_in_buffer[1];
        unsigned long length;
        unsigned long addr;
-       int err;
+       char *err;
  
        if (kgdb_hex2long(&ptr, &addr) > 0 && *ptr++ == ',' &&
                                        kgdb_hex2long(&ptr, &length) > 0) {
                err = kgdb_mem2hex((char *)addr, remcom_out_buffer, length);
-               if (err)
-                       error_packet(remcom_out_buffer, err);
+               if (!err)
+                       error_packet(remcom_out_buffer, -EINVAL);
        } else {
                error_packet(remcom_out_buffer, -EINVAL);
        }
@@@ -550,6 -572,60 +572,60 @@@ static void gdb_cmd_memwrite(struct kgd
                strcpy(remcom_out_buffer, "OK");
  }
  
+ #if DBG_MAX_REG_NUM > 0
+ static char *gdb_hex_reg_helper(int regnum, char *out)
+ {
+       int i;
+       int offset = 0;
+       for (i = 0; i < regnum; i++)
+               offset += dbg_reg_def[i].size;
+       return kgdb_mem2hex((char *)gdb_regs + offset, out,
+                           dbg_reg_def[i].size);
+ }
+ /* Handle the 'p' individual regster get */
+ static void gdb_cmd_reg_get(struct kgdb_state *ks)
+ {
+       unsigned long regnum;
+       char *ptr = &remcom_in_buffer[1];
+       kgdb_hex2long(&ptr, &regnum);
+       if (regnum >= DBG_MAX_REG_NUM) {
+               error_packet(remcom_out_buffer, -EINVAL);
+               return;
+       }
+       gdb_get_regs_helper(ks);
+       gdb_hex_reg_helper(regnum, remcom_out_buffer);
+ }
+ /* Handle the 'P' individual regster set */
+ static void gdb_cmd_reg_set(struct kgdb_state *ks)
+ {
+       unsigned long regnum;
+       char *ptr = &remcom_in_buffer[1];
+       int i = 0;
+       kgdb_hex2long(&ptr, &regnum);
+       if (*ptr++ != '=' ||
+           !(!kgdb_usethread || kgdb_usethread == current) ||
+           !dbg_get_reg(regnum, gdb_regs, ks->linux_regs)) {
+               error_packet(remcom_out_buffer, -EINVAL);
+               return;
+       }
+       memset(gdb_regs, 0, sizeof(gdb_regs));
+       while (i < sizeof(gdb_regs) * 2)
+               if (hex_to_bin(ptr[i]) >= 0)
+                       i++;
+               else
+                       break;
+       i = i / 2;
+       kgdb_hex2mem(ptr, (char *)gdb_regs, i);
+       dbg_set_reg(regnum, gdb_regs, ks->linux_regs);
+       strcpy(remcom_out_buffer, "OK");
+ }
+ #endif /* DBG_MAX_REG_NUM > 0 */
  /* Handle the 'X' memory binary write bytes */
  static void gdb_cmd_binwrite(struct kgdb_state *ks)
  {
@@@ -612,7 -688,7 +688,7 @@@ static void gdb_cmd_query(struct kgdb_s
  {
        struct task_struct *g;
        struct task_struct *p;
-       unsigned char thref[8];
+       unsigned char thref[BUF_THREAD_ID_SIZE];
        char *ptr;
        int i;
        int cpu;
                        for_each_online_cpu(cpu) {
                                ks->thr_query = 0;
                                int_to_threadref(thref, -cpu - 2);
-                               pack_threadid(ptr, thref);
-                               ptr += BUF_THREAD_ID_SIZE;
+                               ptr = pack_threadid(ptr, thref);
                                *(ptr++) = ',';
                                i++;
                        }
                do_each_thread(g, p) {
                        if (i >= ks->thr_query && !finished) {
                                int_to_threadref(thref, p->pid);
-                               pack_threadid(ptr, thref);
-                               ptr += BUF_THREAD_ID_SIZE;
+                               ptr = pack_threadid(ptr, thref);
                                *(ptr++) = ',';
                                ks->thr_query++;
                                if (ks->thr_query % KGDB_MAX_THREAD_QUERY == 0)
@@@ -858,11 -932,14 +932,14 @@@ int gdb_serial_stub(struct kgdb_state *
        int error = 0;
        int tmp;
  
-       /* Clear the out buffer. */
+       /* Initialize comm buffer and globals. */
        memset(remcom_out_buffer, 0, sizeof(remcom_out_buffer));
+       kgdb_usethread = kgdb_info[ks->cpu].task;
+       ks->kgdb_usethreadid = shadow_pid(kgdb_info[ks->cpu].task->pid);
+       ks->pass_exception = 0;
  
        if (kgdb_connected) {
-               unsigned char thref[8];
+               unsigned char thref[BUF_THREAD_ID_SIZE];
                char *ptr;
  
                /* Reply to host that an exception has occurred */
                put_packet(remcom_out_buffer);
        }
  
-       kgdb_usethread = kgdb_info[ks->cpu].task;
-       ks->kgdb_usethreadid = shadow_pid(kgdb_info[ks->cpu].task->pid);
-       ks->pass_exception = 0;
        while (1) {
                error = 0;
  
                case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA..AA */
                        gdb_cmd_memwrite(ks);
                        break;
+ #if DBG_MAX_REG_NUM > 0
+               case 'p': /* pXX Return gdb register XX (in hex) */
+                       gdb_cmd_reg_get(ks);
+                       break;
+               case 'P': /* PXX=aaaa Set gdb register XX to aaaa (in hex) */
+                       gdb_cmd_reg_set(ks);
+                       break;
+ #endif /* DBG_MAX_REG_NUM > 0 */
                case 'X': /* XAA..AA,LLLL: Write LLLL bytes at address AA..AA */
                        gdb_cmd_binwrite(ks);
                        break;