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

16 files changed:
arch/arm/include/asm/kgdb.h
arch/arm/kernel/kgdb.c
arch/mips/include/asm/kgdb.h
arch/mips/kernel/kgdb.c
arch/powerpc/kernel/kgdb.c
arch/x86/include/asm/kgdb.h
arch/x86/kernel/kgdb.c
include/linux/kgdb.h
kernel/debug/debug_core.c
kernel/debug/gdbstub.c
kernel/debug/kdb/kdb_main.c
kernel/trace/Makefile
kernel/trace/trace.c
kernel/trace/trace.h
kernel/trace/trace_kdb.c [new file with mode: 0644]
mm/highmem.c

index 67af4b8..0826599 100644 (file)
@@ -70,11 +70,11 @@ extern int kgdb_fault_expected;
 #define _GP_REGS               16
 #define _FP_REGS               8
 #define _EXTRA_REGS            2
-#define GDB_MAX_REGS           (_GP_REGS + (_FP_REGS * 3) + _EXTRA_REGS)
+#define DBG_MAX_REG_NUM                (_GP_REGS + (_FP_REGS * 3) + _EXTRA_REGS)
 
 #define KGDB_MAX_NO_CPUS       1
 #define BUFMAX                 400
-#define NUMREGBYTES            (GDB_MAX_REGS << 2)
+#define NUMREGBYTES            (DBG_MAX_REG_NUM << 2)
 #define NUMCRITREGBYTES                (32 << 2)
 
 #define _R0                    0
@@ -93,7 +93,7 @@ extern int kgdb_fault_expected;
 #define _SPT                   13
 #define _LR                    14
 #define _PC                    15
-#define _CPSR                  (GDB_MAX_REGS - 1)
+#define _CPSR                  (DBG_MAX_REG_NUM - 1)
 
 /*
  * So that we can denote the end of a frame for tracing,
index c868a88..778c2f7 100644 (file)
  *           Deepak Saxena <dsaxena@plexity.net>
  */
 #include <linux/irq.h>
+#include <linux/kdebug.h>
 #include <linux/kgdb.h>
 #include <asm/traps.h>
 
-/* Make a local copy of the registers passed into the handler (bletch) */
-void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *kernel_regs)
+struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] =
 {
-       int regno;
-
-       /* Initialize all to zero. */
-       for (regno = 0; regno < GDB_MAX_REGS; regno++)
-               gdb_regs[regno] = 0;
+       { "r0", 4, offsetof(struct pt_regs, ARM_r0)},
+       { "r1", 4, offsetof(struct pt_regs, ARM_r1)},
+       { "r2", 4, offsetof(struct pt_regs, ARM_r2)},
+       { "r3", 4, offsetof(struct pt_regs, ARM_r3)},
+       { "r4", 4, offsetof(struct pt_regs, ARM_r4)},
+       { "r5", 4, offsetof(struct pt_regs, ARM_r5)},
+       { "r6", 4, offsetof(struct pt_regs, ARM_r6)},
+       { "r7", 4, offsetof(struct pt_regs, ARM_r7)},
+       { "r8", 4, offsetof(struct pt_regs, ARM_r8)},
+       { "r9", 4, offsetof(struct pt_regs, ARM_r9)},
+       { "r10", 4, offsetof(struct pt_regs, ARM_r10)},
+       { "fp", 4, offsetof(struct pt_regs, ARM_fp)},
+       { "ip", 4, offsetof(struct pt_regs, ARM_ip)},
+       { "sp", 4, offsetof(struct pt_regs, ARM_sp)},
+       { "lr", 4, offsetof(struct pt_regs, ARM_lr)},
+       { "pc", 4, offsetof(struct pt_regs, ARM_pc)},
+       { "f0", 12, -1 },
+       { "f1", 12, -1 },
+       { "f2", 12, -1 },
+       { "f3", 12, -1 },
+       { "f4", 12, -1 },
+       { "f5", 12, -1 },
+       { "f6", 12, -1 },
+       { "f7", 12, -1 },
+       { "fps", 4, -1 },
+       { "cpsr", 4, offsetof(struct pt_regs, ARM_cpsr)},
+};
 
-       gdb_regs[_R0]           = kernel_regs->ARM_r0;
-       gdb_regs[_R1]           = kernel_regs->ARM_r1;
-       gdb_regs[_R2]           = kernel_regs->ARM_r2;
-       gdb_regs[_R3]           = kernel_regs->ARM_r3;
-       gdb_regs[_R4]           = kernel_regs->ARM_r4;
-       gdb_regs[_R5]           = kernel_regs->ARM_r5;
-       gdb_regs[_R6]           = kernel_regs->ARM_r6;
-       gdb_regs[_R7]           = kernel_regs->ARM_r7;
-       gdb_regs[_R8]           = kernel_regs->ARM_r8;
-       gdb_regs[_R9]           = kernel_regs->ARM_r9;
-       gdb_regs[_R10]          = kernel_regs->ARM_r10;
-       gdb_regs[_FP]           = kernel_regs->ARM_fp;
-       gdb_regs[_IP]           = kernel_regs->ARM_ip;
-       gdb_regs[_SPT]          = kernel_regs->ARM_sp;
-       gdb_regs[_LR]           = kernel_regs->ARM_lr;
-       gdb_regs[_PC]           = kernel_regs->ARM_pc;
-       gdb_regs[_CPSR]         = kernel_regs->ARM_cpsr;
+char *dbg_get_reg(int regno, void *mem, struct pt_regs *regs)
+{
+       if (regno >= DBG_MAX_REG_NUM || regno < 0)
+               return NULL;
+
+       if (dbg_reg_def[regno].offset != -1)
+               memcpy(mem, (void *)regs + dbg_reg_def[regno].offset,
+                      dbg_reg_def[regno].size);
+       else
+               memset(mem, 0, dbg_reg_def[regno].size);
+       return dbg_reg_def[regno].name;
 }
 
-/* Copy local gdb registers back to kgdb regs, for later copy to kernel */
-void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *kernel_regs)
+int dbg_set_reg(int regno, void *mem, struct pt_regs *regs)
 {
-       kernel_regs->ARM_r0     = gdb_regs[_R0];
-       kernel_regs->ARM_r1     = gdb_regs[_R1];
-       kernel_regs->ARM_r2     = gdb_regs[_R2];
-       kernel_regs->ARM_r3     = gdb_regs[_R3];
-       kernel_regs->ARM_r4     = gdb_regs[_R4];
-       kernel_regs->ARM_r5     = gdb_regs[_R5];
-       kernel_regs->ARM_r6     = gdb_regs[_R6];
-       kernel_regs->ARM_r7     = gdb_regs[_R7];
-       kernel_regs->ARM_r8     = gdb_regs[_R8];
-       kernel_regs->ARM_r9     = gdb_regs[_R9];
-       kernel_regs->ARM_r10    = gdb_regs[_R10];
-       kernel_regs->ARM_fp     = gdb_regs[_FP];
-       kernel_regs->ARM_ip     = gdb_regs[_IP];
-       kernel_regs->ARM_sp     = gdb_regs[_SPT];
-       kernel_regs->ARM_lr     = gdb_regs[_LR];
-       kernel_regs->ARM_pc     = gdb_regs[_PC];
-       kernel_regs->ARM_cpsr   = gdb_regs[_CPSR];
+       if (regno >= DBG_MAX_REG_NUM || regno < 0)
+               return -EINVAL;
+
+       if (dbg_reg_def[regno].offset != -1)
+               memcpy((void *)regs + dbg_reg_def[regno].offset, mem,
+                      dbg_reg_def[regno].size);
+       return 0;
 }
 
 void
@@ -176,6 +181,33 @@ void kgdb_roundup_cpus(unsigned long flags)
        local_irq_disable();
 }
 
+static int __kgdb_notify(struct die_args *args, unsigned long cmd)
+{
+       struct pt_regs *regs = args->regs;
+
+       if (kgdb_handle_exception(1, args->signr, cmd, regs))
+               return NOTIFY_DONE;
+       return NOTIFY_STOP;
+}
+static int
+kgdb_notify(struct notifier_block *self, unsigned long cmd, void *ptr)
+{
+       unsigned long flags;
+       int ret;
+
+       local_irq_save(flags);
+       ret = __kgdb_notify(ptr, cmd);
+       local_irq_restore(flags);
+
+       return ret;
+}
+
+static struct notifier_block kgdb_notifier = {
+       .notifier_call  = kgdb_notify,
+       .priority       = -INT_MAX,
+};
+
+
 /**
  *     kgdb_arch_init - Perform any architecture specific initalization.
  *
@@ -184,6 +216,11 @@ void kgdb_roundup_cpus(unsigned long flags)
  */
 int kgdb_arch_init(void)
 {
+       int ret = register_die_notifier(&kgdb_notifier);
+
+       if (ret != 0)
+               return ret;
+
        register_undef_hook(&kgdb_brkpt_hook);
        register_undef_hook(&kgdb_compiled_brkpt_hook);
 
@@ -200,6 +237,7 @@ void kgdb_arch_exit(void)
 {
        unregister_undef_hook(&kgdb_brkpt_hook);
        unregister_undef_hook(&kgdb_compiled_brkpt_hook);
+       unregister_die_notifier(&kgdb_notifier);
 }
 
 /*
index 19002d6..e6c0b0e 100644 (file)
@@ -8,28 +8,27 @@
 #if (_MIPS_ISA == _MIPS_ISA_MIPS1) || (_MIPS_ISA == _MIPS_ISA_MIPS2) || \
        (_MIPS_ISA == _MIPS_ISA_MIPS32)
 
-#define KGDB_GDB_REG_SIZE 32
+#define KGDB_GDB_REG_SIZE      32
+#define GDB_SIZEOF_REG         sizeof(u32)
 
 #elif (_MIPS_ISA == _MIPS_ISA_MIPS3) || (_MIPS_ISA == _MIPS_ISA_MIPS4) || \
        (_MIPS_ISA == _MIPS_ISA_MIPS64)
 
 #ifdef CONFIG_32BIT
-#define KGDB_GDB_REG_SIZE 32
+#define KGDB_GDB_REG_SIZE      32
+#define GDB_SIZEOF_REG         sizeof(u32)
 #else /* CONFIG_CPU_32BIT */
-#define KGDB_GDB_REG_SIZE 64
+#define KGDB_GDB_REG_SIZE      64
+#define GDB_SIZEOF_REG         sizeof(u64)
 #endif
 #else
 #error "Need to set KGDB_GDB_REG_SIZE for MIPS ISA"
 #endif /* _MIPS_ISA */
 
 #define BUFMAX                 2048
-#if (KGDB_GDB_REG_SIZE == 32)
-#define NUMREGBYTES            (90*sizeof(u32))
-#define NUMCRITREGBYTES                (12*sizeof(u32))
-#else
-#define NUMREGBYTES            (90*sizeof(u64))
-#define NUMCRITREGBYTES                (12*sizeof(u64))
-#endif
+#define DBG_MAX_REG_NUM                72
+#define NUMREGBYTES            (DBG_MAX_REG_NUM * sizeof(GDB_SIZEOF_REG))
+#define NUMCRITREGBYTES                (12 * sizeof(GDB_SIZEOF_REG))
 #define BREAK_INSTR_SIZE       4
 #define CACHE_FLUSH_IS_SAFE    0
 
index 9b78ff6..1f4e2fa 100644 (file)
@@ -50,6 +50,151 @@ static struct hard_trap_info {
        { 0, 0}                 /* Must be last */
 };
 
+struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] =
+{
+       { "zero", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[0]) },
+       { "at", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[1]) },
+       { "v0", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[2]) },
+       { "v1", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[3]) },
+       { "a0", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[4]) },
+       { "a1", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[5]) },
+       { "a2", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[6]) },
+       { "a3", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[7]) },
+       { "t0", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[8]) },
+       { "t1", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[9]) },
+       { "t2", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[10]) },
+       { "t3", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[11]) },
+       { "t4", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[12]) },
+       { "t5", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[13]) },
+       { "t6", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[14]) },
+       { "t7", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[15]) },
+       { "s0", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[16]) },
+       { "s1", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[17]) },
+       { "s2", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[18]) },
+       { "s3", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[19]) },
+       { "s4", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[20]) },
+       { "s5", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[21]) },
+       { "s6", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[22]) },
+       { "s7", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[23]) },
+       { "t8", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[24]) },
+       { "t9", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[25]) },
+       { "k0", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[26]) },
+       { "k1", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[27]) },
+       { "gp", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[28]) },
+       { "sp", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[29]) },
+       { "s8", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[30]) },
+       { "ra", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[31]) },
+       { "sr", GDB_SIZEOF_REG, offsetof(struct pt_regs, cp0_status) },
+       { "lo", GDB_SIZEOF_REG, offsetof(struct pt_regs, lo) },
+       { "hi", GDB_SIZEOF_REG, offsetof(struct pt_regs, hi) },
+       { "bad", GDB_SIZEOF_REG, offsetof(struct pt_regs, cp0_badvaddr) },
+       { "cause", GDB_SIZEOF_REG, offsetof(struct pt_regs, cp0_cause) },
+       { "pc", GDB_SIZEOF_REG, offsetof(struct pt_regs, cp0_epc) },
+       { "f0", GDB_SIZEOF_REG, 0 },
+       { "f1", GDB_SIZEOF_REG, 1 },
+       { "f2", GDB_SIZEOF_REG, 2 },
+       { "f3", GDB_SIZEOF_REG, 3 },
+       { "f4", GDB_SIZEOF_REG, 4 },
+       { "f5", GDB_SIZEOF_REG, 5 },
+       { "f6", GDB_SIZEOF_REG, 6 },
+       { "f7", GDB_SIZEOF_REG, 7 },
+       { "f8", GDB_SIZEOF_REG, 8 },
+       { "f9", GDB_SIZEOF_REG, 9 },
+       { "f10", GDB_SIZEOF_REG, 10 },
+       { "f11", GDB_SIZEOF_REG, 11 },
+       { "f12", GDB_SIZEOF_REG, 12 },
+       { "f13", GDB_SIZEOF_REG, 13 },
+       { "f14", GDB_SIZEOF_REG, 14 },
+       { "f15", GDB_SIZEOF_REG, 15 },
+       { "f16", GDB_SIZEOF_REG, 16 },
+       { "f17", GDB_SIZEOF_REG, 17 },
+       { "f18", GDB_SIZEOF_REG, 18 },
+       { "f19", GDB_SIZEOF_REG, 19 },
+       { "f20", GDB_SIZEOF_REG, 20 },
+       { "f21", GDB_SIZEOF_REG, 21 },
+       { "f22", GDB_SIZEOF_REG, 22 },
+       { "f23", GDB_SIZEOF_REG, 23 },
+       { "f24", GDB_SIZEOF_REG, 24 },
+       { "f25", GDB_SIZEOF_REG, 25 },
+       { "f26", GDB_SIZEOF_REG, 26 },
+       { "f27", GDB_SIZEOF_REG, 27 },
+       { "f28", GDB_SIZEOF_REG, 28 },
+       { "f29", GDB_SIZEOF_REG, 29 },
+       { "f30", GDB_SIZEOF_REG, 30 },
+       { "f31", GDB_SIZEOF_REG, 31 },
+       { "fsr", GDB_SIZEOF_REG, 0 },
+       { "fir", GDB_SIZEOF_REG, 0 },
+};
+
+int dbg_set_reg(int regno, void *mem, struct pt_regs *regs)
+{
+       int fp_reg;
+
+       if (regno < 0 || regno >= DBG_MAX_REG_NUM)
+               return -EINVAL;
+
+       if (dbg_reg_def[regno].offset != -1 && regno < 38) {
+               memcpy((void *)regs + dbg_reg_def[regno].offset, mem,
+                      dbg_reg_def[regno].size);
+       } else if (current && dbg_reg_def[regno].offset != -1 && regno < 72) {
+               /* FP registers 38 -> 69 */
+               if (!(regs->cp0_status & ST0_CU1))
+                       return 0;
+               if (regno == 70) {
+                       /* Process the fcr31/fsr (register 70) */
+                       memcpy((void *)&current->thread.fpu.fcr31, mem,
+                              dbg_reg_def[regno].size);
+                       goto out_save;
+               } else if (regno == 71) {
+                       /* Ignore the fir (register 71) */
+                       goto out_save;
+               }
+               fp_reg = dbg_reg_def[regno].offset;
+               memcpy((void *)&current->thread.fpu.fpr[fp_reg], mem,
+                      dbg_reg_def[regno].size);
+out_save:
+               restore_fp(current);
+       }
+
+       return 0;
+}
+
+char *dbg_get_reg(int regno, void *mem, struct pt_regs *regs)
+{
+       int fp_reg;
+
+       if (regno >= DBG_MAX_REG_NUM || regno < 0)
+               return NULL;
+
+       if (dbg_reg_def[regno].offset != -1 && regno < 38) {
+               /* First 38 registers */
+               memcpy(mem, (void *)regs + dbg_reg_def[regno].offset,
+                      dbg_reg_def[regno].size);
+       } else if (current && dbg_reg_def[regno].offset != -1 && regno < 72) {
+               /* FP registers 38 -> 69 */
+               if (!(regs->cp0_status & ST0_CU1))
+                       goto out;
+               save_fp(current);
+               if (regno == 70) {
+                       /* Process the fcr31/fsr (register 70) */
+                       memcpy(mem, (void *)&current->thread.fpu.fcr31,
+                              dbg_reg_def[regno].size);
+                       goto out;
+               } else if (regno == 71) {
+                       /* Ignore the fir (register 71) */
+                       memset(mem, 0, dbg_reg_def[regno].size);
+                       goto out;
+               }
+               fp_reg = dbg_reg_def[regno].offset;
+               memcpy(mem, (void *)&current->thread.fpu.fpr[fp_reg],
+                      dbg_reg_def[regno].size);
+       }
+
+out:
+       return dbg_reg_def[regno].name;
+
+}
+
 void arch_kgdb_breakpoint(void)
 {
        __asm__ __volatile__(
@@ -84,64 +229,6 @@ static int compute_signal(int tt)
        return SIGHUP;          /* default for things we don't know about */
 }
 
-void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs)
-{
-       int reg;
-
-#if (KGDB_GDB_REG_SIZE == 32)
-       u32 *ptr = (u32 *)gdb_regs;
-#else
-       u64 *ptr = (u64 *)gdb_regs;
-#endif
-
-       for (reg = 0; reg < 32; reg++)
-               *(ptr++) = regs->regs[reg];
-
-       *(ptr++) = regs->cp0_status;
-       *(ptr++) = regs->lo;
-       *(ptr++) = regs->hi;
-       *(ptr++) = regs->cp0_badvaddr;
-       *(ptr++) = regs->cp0_cause;
-       *(ptr++) = regs->cp0_epc;
-
-       /* FP REGS */
-       if (!(current && (regs->cp0_status & ST0_CU1)))
-               return;
-
-       save_fp(current);
-       for (reg = 0; reg < 32; reg++)
-               *(ptr++) = current->thread.fpu.fpr[reg];
-}
-
-void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs)
-{
-       int reg;
-
-#if (KGDB_GDB_REG_SIZE == 32)
-       const u32 *ptr = (u32 *)gdb_regs;
-#else
-       const u64 *ptr = (u64 *)gdb_regs;
-#endif
-
-       for (reg = 0; reg < 32; reg++)
-               regs->regs[reg] = *(ptr++);
-
-       regs->cp0_status = *(ptr++);
-       regs->lo = *(ptr++);
-       regs->hi = *(ptr++);
-       regs->cp0_badvaddr = *(ptr++);
-       regs->cp0_cause = *(ptr++);
-       regs->cp0_epc = *(ptr++);
-
-       /* FP REGS from current */
-       if (!(current && (regs->cp0_status & ST0_CU1)))
-               return;
-
-       for (reg = 0; reg < 32; reg++)
-               current->thread.fpu.fpr[reg] = *(ptr++);
-       restore_fp(current);
-}
-
 /*
  * Similar to regs_to_gdb_regs() except that process is sleeping and so
  * we may not be able to get all the info.
@@ -242,7 +329,7 @@ static struct notifier_block kgdb_notifier = {
 };
 
 /*
- * Handle the 's' and 'c' commands
+ * Handle the 'c' command
  */
 int kgdb_arch_handle_exception(int vector, int signo, int err_code,
                               char *remcom_in_buffer, char *remcom_out_buffer,
@@ -250,20 +337,14 @@ int kgdb_arch_handle_exception(int vector, int signo, int err_code,
 {
        char *ptr;
        unsigned long address;
-       int cpu = smp_processor_id();
 
        switch (remcom_in_buffer[0]) {
-       case 's':
        case 'c':
                /* handle the optional parameter */
                ptr = &remcom_in_buffer[1];
                if (kgdb_hex2long(&ptr, &address))
                        regs->cp0_epc = address;
 
-               atomic_set(&kgdb_cpu_doing_single_step, -1);
-               if (remcom_in_buffer[0] == 's')
-                       atomic_set(&kgdb_cpu_doing_single_step, cpu);
-
                return 0;
        }
 
index 82a7b22..7f61a3a 100644 (file)
@@ -129,7 +129,7 @@ static int kgdb_handle_breakpoint(struct pt_regs *regs)
                return 0;
 
        if (*(u32 *) (regs->nip) == *(u32 *) (&arch_kgdb_ops.gdb_bpt_instr))
-               regs->nip += 4;
+               regs->nip += BREAK_INSTR_SIZE;
 
        return 1;
 }
index 006da36..396f5b5 100644 (file)
@@ -39,9 +39,11 @@ enum regnames {
        GDB_FS,                 /* 14 */
        GDB_GS,                 /* 15 */
 };
+#define GDB_ORIG_AX            41
+#define DBG_MAX_REG_NUM                16
 #define NUMREGBYTES            ((GDB_GS+1)*4)
 #else /* ! CONFIG_X86_32 */
-enum regnames64 {
+enum regnames {
        GDB_AX,                 /* 0 */
        GDB_BX,                 /* 1 */
        GDB_CX,                 /* 2 */
@@ -59,15 +61,15 @@ enum regnames64 {
        GDB_R14,                /* 14 */
        GDB_R15,                /* 15 */
        GDB_PC,                 /* 16 */
+       GDB_PS,                 /* 17 */
+       GDB_CS,                 /* 18 */
+       GDB_SS,                 /* 19 */
 };
-
-enum regnames32 {
-       GDB_PS = 34,
-       GDB_CS,
-       GDB_SS,
-};
-#define NUMREGBYTES            ((GDB_SS+1)*4)
-#endif /* CONFIG_X86_32 */
+#define GDB_ORIG_AX            57
+#define DBG_MAX_REG_NUM                20
+/* 17 64 bit regs and 3 32 bit regs */
+#define NUMREGBYTES            ((17 * 8) + (3 * 4))
+#endif /* ! CONFIG_X86_32 */
 
 static inline void arch_kgdb_breakpoint(void)
 {
index 01ab17a..ef10940 100644 (file)
 #include <asm/system.h>
 #include <asm/apic.h>
 
-/**
- *     pt_regs_to_gdb_regs - Convert ptrace regs to GDB regs
- *     @gdb_regs: A pointer to hold the registers in the order GDB wants.
- *     @regs: The &struct pt_regs of the current process.
- *
- *     Convert the pt_regs in @regs into the format for registers that
- *     GDB expects, stored in @gdb_regs.
- */
-void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] =
 {
-#ifndef CONFIG_X86_32
-       u32 *gdb_regs32 = (u32 *)gdb_regs;
+#ifdef CONFIG_X86_32
+       { "ax", 4, offsetof(struct pt_regs, ax) },
+       { "cx", 4, offsetof(struct pt_regs, cx) },
+       { "dx", 4, offsetof(struct pt_regs, dx) },
+       { "bx", 4, offsetof(struct pt_regs, bx) },
+       { "sp", 4, offsetof(struct pt_regs, sp) },
+       { "bp", 4, offsetof(struct pt_regs, bp) },
+       { "si", 4, offsetof(struct pt_regs, si) },
+       { "di", 4, offsetof(struct pt_regs, di) },
+       { "ip", 4, offsetof(struct pt_regs, ip) },
+       { "flags", 4, offsetof(struct pt_regs, flags) },
+       { "cs", 4, offsetof(struct pt_regs, cs) },
+       { "ss", 4, offsetof(struct pt_regs, ss) },
+       { "ds", 4, offsetof(struct pt_regs, ds) },
+       { "es", 4, offsetof(struct pt_regs, es) },
+       { "fs", 4, -1 },
+       { "gs", 4, -1 },
+#else
+       { "ax", 8, offsetof(struct pt_regs, ax) },
+       { "bx", 8, offsetof(struct pt_regs, bx) },
+       { "cx", 8, offsetof(struct pt_regs, cx) },
+       { "dx", 8, offsetof(struct pt_regs, dx) },
+       { "si", 8, offsetof(struct pt_regs, dx) },
+       { "di", 8, offsetof(struct pt_regs, di) },
+       { "bp", 8, offsetof(struct pt_regs, bp) },
+       { "sp", 8, offsetof(struct pt_regs, sp) },
+       { "r8", 8, offsetof(struct pt_regs, r8) },
+       { "r9", 8, offsetof(struct pt_regs, r9) },
+       { "r10", 8, offsetof(struct pt_regs, r10) },
+       { "r11", 8, offsetof(struct pt_regs, r11) },
+       { "r12", 8, offsetof(struct pt_regs, r12) },
+       { "r13", 8, offsetof(struct pt_regs, r13) },
+       { "r14", 8, offsetof(struct pt_regs, r14) },
+       { "r15", 8, offsetof(struct pt_regs, r15) },
+       { "ip", 8, offsetof(struct pt_regs, ip) },
+       { "flags", 4, offsetof(struct pt_regs, flags) },
+       { "cs", 4, offsetof(struct pt_regs, cs) },
+       { "ss", 4, offsetof(struct pt_regs, ss) },
 #endif
-       gdb_regs[GDB_AX]        = regs->ax;
-       gdb_regs[GDB_BX]        = regs->bx;
-       gdb_regs[GDB_CX]        = regs->cx;
-       gdb_regs[GDB_DX]        = regs->dx;
-       gdb_regs[GDB_SI]        = regs->si;
-       gdb_regs[GDB_DI]        = regs->di;
-       gdb_regs[GDB_BP]        = regs->bp;
-       gdb_regs[GDB_PC]        = regs->ip;
+};
+
+int dbg_set_reg(int regno, void *mem, struct pt_regs *regs)
+{
+       if (
 #ifdef CONFIG_X86_32
-       gdb_regs[GDB_PS]        = regs->flags;
-       gdb_regs[GDB_DS]        = regs->ds;
-       gdb_regs[GDB_ES]        = regs->es;
-       gdb_regs[GDB_CS]        = regs->cs;
-       gdb_regs[GDB_FS]        = 0xFFFF;
-       gdb_regs[GDB_GS]        = 0xFFFF;
-       if (user_mode_vm(regs)) {
-               gdb_regs[GDB_SS] = regs->ss;
-               gdb_regs[GDB_SP] = regs->sp;
-       } else {
-               gdb_regs[GDB_SS] = __KERNEL_DS;
-               gdb_regs[GDB_SP] = kernel_stack_pointer(regs);
+           regno == GDB_SS || regno == GDB_FS || regno == GDB_GS ||
+#endif
+           regno == GDB_SP || regno == GDB_ORIG_AX)
+               return 0;
+
+       if (dbg_reg_def[regno].offset != -1)
+               memcpy((void *)regs + dbg_reg_def[regno].offset, mem,
+                      dbg_reg_def[regno].size);
+       return 0;
+}
+
+char *dbg_get_reg(int regno, void *mem, struct pt_regs *regs)
+{
+       if (regno == GDB_ORIG_AX) {
+               memcpy(mem, &regs->orig_ax, sizeof(regs->orig_ax));
+               return "orig_ax";
        }
-#else
-       gdb_regs[GDB_R8]        = regs->r8;
-       gdb_regs[GDB_R9]        = regs->r9;
-       gdb_regs[GDB_R10]       = regs->r10;
-       gdb_regs[GDB_R11]       = regs->r11;
-       gdb_regs[GDB_R12]       = regs->r12;
-       gdb_regs[GDB_R13]       = regs->r13;
-       gdb_regs[GDB_R14]       = regs->r14;
-       gdb_regs[GDB_R15]       = regs->r15;
-       gdb_regs32[GDB_PS]      = regs->flags;
-       gdb_regs32[GDB_CS]      = regs->cs;
-       gdb_regs32[GDB_SS]      = regs->ss;
-       gdb_regs[GDB_SP]        = kernel_stack_pointer(regs);
+       if (regno >= DBG_MAX_REG_NUM || regno < 0)
+               return NULL;
+
+       if (dbg_reg_def[regno].offset != -1)
+               memcpy(mem, (void *)regs + dbg_reg_def[regno].offset,
+                      dbg_reg_def[regno].size);
+
+       switch (regno) {
+#ifdef CONFIG_X86_32
+       case GDB_SS:
+               if (!user_mode_vm(regs))
+                       *(unsigned long *)mem = __KERNEL_DS;
+               break;
+       case GDB_SP:
+               if (!user_mode_vm(regs))
+                       *(unsigned long *)mem = kernel_stack_pointer(regs);
+               break;
+       case GDB_GS:
+       case GDB_FS:
+               *(unsigned long *)mem = 0xFFFF;
+               break;
 #endif
+       }
+       return dbg_reg_def[regno].name;
 }
 
 /**
@@ -150,54 +189,13 @@ void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p)
        gdb_regs[GDB_SP]        = p->thread.sp;
 }
 
-/**
- *     gdb_regs_to_pt_regs - Convert GDB regs to ptrace regs.
- *     @gdb_regs: A pointer to hold the registers we've received from GDB.
- *     @regs: A pointer to a &struct pt_regs to hold these values in.
- *
- *     Convert the GDB regs in @gdb_regs into the pt_regs, and store them
- *     in @regs.
- */
-void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs)
-{
-#ifndef CONFIG_X86_32
-       u32 *gdb_regs32 = (u32 *)gdb_regs;
-#endif
-       regs->ax                = gdb_regs[GDB_AX];
-       regs->bx                = gdb_regs[GDB_BX];
-       regs->cx                = gdb_regs[GDB_CX];
-       regs->dx                = gdb_regs[GDB_DX];
-       regs->si                = gdb_regs[GDB_SI];
-       regs->di                = gdb_regs[GDB_DI];
-       regs->bp                = gdb_regs[GDB_BP];
-       regs->ip                = gdb_regs[GDB_PC];
-#ifdef CONFIG_X86_32
-       regs->flags             = gdb_regs[GDB_PS];
-       regs->ds                = gdb_regs[GDB_DS];
-       regs->es                = gdb_regs[GDB_ES];
-       regs->cs                = gdb_regs[GDB_CS];
-#else
-       regs->r8                = gdb_regs[GDB_R8];
-       regs->r9                = gdb_regs[GDB_R9];
-       regs->r10               = gdb_regs[GDB_R10];
-       regs->r11               = gdb_regs[GDB_R11];
-       regs->r12               = gdb_regs[GDB_R12];
-       regs->r13               = gdb_regs[GDB_R13];
-       regs->r14               = gdb_regs[GDB_R14];
-       regs->r15               = gdb_regs[GDB_R15];
-       regs->flags             = gdb_regs32[GDB_PS];
-       regs->cs                = gdb_regs32[GDB_CS];
-       regs->ss                = gdb_regs32[GDB_SS];
-#endif
-}
-
 static struct hw_breakpoint {
        unsigned                enabled;
        unsigned long           addr;
        int                     len;
        int                     type;
        struct perf_event       **pev;
-} breakinfo[4];
+} breakinfo[HBP_NUM];
 
 static unsigned long early_dr7;
 
@@ -205,7 +203,7 @@ static void kgdb_correct_hw_break(void)
 {
        int breakno;
 
-       for (breakno = 0; breakno < 4; breakno++) {
+       for (breakno = 0; breakno < HBP_NUM; breakno++) {
                struct perf_event *bp;
                struct arch_hw_breakpoint *info;
                int val;
@@ -292,10 +290,10 @@ kgdb_remove_hw_break(unsigned long addr, int len, enum kgdb_bptype bptype)
 {
        int i;
 
-       for (i = 0; i < 4; i++)
+       for (i = 0; i < HBP_NUM; i++)
                if (breakinfo[i].addr == addr && breakinfo[i].enabled)
                        break;
-       if (i == 4)
+       if (i == HBP_NUM)
                return -1;
 
        if (hw_break_release_slot(i)) {
@@ -313,7 +311,7 @@ static void kgdb_remove_all_hw_break(void)
        int cpu = raw_smp_processor_id();
        struct perf_event *bp;
 
-       for (i = 0; i < 4; i++) {
+       for (i = 0; i < HBP_NUM; i++) {
                if (!breakinfo[i].enabled)
                        continue;
                bp = *per_cpu_ptr(breakinfo[i].pev, cpu);
@@ -333,10 +331,10 @@ kgdb_set_hw_break(unsigned long addr, int len, enum kgdb_bptype bptype)
 {
        int i;
 
-       for (i = 0; i < 4; i++)
+       for (i = 0; i < HBP_NUM; i++)
                if (!breakinfo[i].enabled)
                        break;
-       if (i == 4)
+       if (i == HBP_NUM)
                return -1;
 
        switch (bptype) {
@@ -397,7 +395,7 @@ void kgdb_disable_hw_debug(struct pt_regs *regs)
 
        /* Disable hardware debugging while we are in kgdb: */
        set_debugreg(0UL, 7);
-       for (i = 0; i < 4; i++) {
+       for (i = 0; i < HBP_NUM; i++) {
                if (!breakinfo[i].enabled)
                        continue;
                if (dbg_is_early) {
@@ -458,7 +456,6 @@ int kgdb_arch_handle_exception(int e_vector, int signo, int err_code,
 {
        unsigned long addr;
        char *ptr;
-       int newPC;
 
        switch (remcomInBuffer[0]) {
        case 'c':
@@ -469,8 +466,6 @@ int kgdb_arch_handle_exception(int e_vector, int signo, int err_code,
                        linux_regs->ip = addr;
        case 'D':
        case 'k':
-               newPC = linux_regs->ip;
-
                /* clear the trace bit */
                linux_regs->flags &= ~X86_EFLAGS_TF;
                atomic_set(&kgdb_cpu_doing_single_step, -1);
@@ -645,7 +640,7 @@ void kgdb_arch_late(void)
        attr.bp_len = HW_BREAKPOINT_LEN_1;
        attr.bp_type = HW_BREAKPOINT_W;
        attr.disabled = 1;
-       for (i = 0; i < 4; i++) {
+       for (i = 0; i < HBP_NUM; i++) {
                if (breakinfo[i].pev)
                        continue;
                breakinfo[i].pev = register_wide_hw_breakpoint(&attr, NULL);
index 9340f34..cc96f0f 100644 (file)
@@ -90,6 +90,19 @@ struct kgdb_bkpt {
        enum kgdb_bpstate       state;
 };
 
+struct dbg_reg_def_t {
+       char *name;
+       int size;
+       int offset;
+};
+
+#ifndef DBG_MAX_REG_NUM
+#define DBG_MAX_REG_NUM 0
+#else
+extern struct dbg_reg_def_t dbg_reg_def[];
+extern char *dbg_get_reg(int regno, void *mem, struct pt_regs *regs);
+extern int dbg_set_reg(int regno, void *mem, struct pt_regs *regs);
+#endif
 #ifndef KGDB_MAX_BREAKPOINTS
 # define KGDB_MAX_BREAKPOINTS  1000
 #endif
@@ -281,7 +294,7 @@ extern void kgdb_unregister_io_module(struct kgdb_io *local_kgdb_io_ops);
 extern struct kgdb_io *dbg_io_ops;
 
 extern int kgdb_hex2long(char **ptr, unsigned long *long_val);
-extern int kgdb_mem2hex(char *mem, char *buf, int count);
+extern char *kgdb_mem2hex(char *mem, char *buf, int count);
 extern int kgdb_hex2mem(char *buf, char *mem, int count);
 
 extern int kgdb_isremovedbreak(unsigned long addr);
index 51d14fe..3c2d497 100644 (file)
@@ -605,6 +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);
index 6e81fd5..481a7bd 100644 (file)
@@ -52,17 +52,6 @@ static unsigned long         gdb_regs[(NUMREGBYTES +
  * 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 @@ 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 @@ void gdbstub_msg_write(const char *s, int len)
  * 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;
@@ -248,17 +237,16 @@ int kgdb_mem2hex(char *mem, char *buf, int count)
        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 @@ int kgdb_hex2mem(char *buf, char *mem, int count)
        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 @@ int kgdb_hex2long(char **ptr, unsigned long *long_val)
                (*ptr)++;
        }
        while (**ptr) {
-               hex_val = hex(**ptr);
+               hex_val = hex_to_bin(**ptr);
                if (hex_val < 0)
                        break;
 
@@ -339,6 +327,32 @@ static int kgdb_ebin2mem(char *buf, char *mem, int count)
        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 @@ static void error_packet(char *pkt, int error)
  * 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 @@ static void gdb_cmd_status(struct kgdb_state *ks)
        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;
@@ -505,6 +521,12 @@ static void gdb_cmd_getregs(struct kgdb_state *ks)
                 */
                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 @@ static void gdb_cmd_memread(struct kgdb_state *ks)
        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 @@ static void gdb_cmd_memwrite(struct kgdb_state *ks)
                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 @@ static void gdb_cmd_query(struct kgdb_state *ks)
 {
        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;
@@ -632,8 +708,7 @@ static void gdb_cmd_query(struct kgdb_state *ks)
                        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++;
                        }
@@ -642,8 +717,7 @@ static void gdb_cmd_query(struct kgdb_state *ks)
                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 @@ int gdb_serial_stub(struct kgdb_state *ks)
        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 */
@@ -876,10 +953,6 @@ int gdb_serial_stub(struct kgdb_state *ks)
                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;
 
@@ -904,6 +977,14 @@ int gdb_serial_stub(struct kgdb_state *ks)
                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;
index ebe4a28..8577e45 100644 (file)
@@ -312,7 +312,7 @@ int kdbgetularg(const char *arg, unsigned long *value)
 
        if (endp == arg) {
                /*
-                * Try base 16, for us folks too lazy to type the
+                * Also try base 16, for us folks too lazy to type the
                 * leading 0x...
                 */
                val = simple_strtoul(arg, &endp, 16);
@@ -325,6 +325,25 @@ int kdbgetularg(const char *arg, unsigned long *value)
        return 0;
 }
 
+int kdbgetu64arg(const char *arg, u64 *value)
+{
+       char *endp;
+       u64 val;
+
+       val = simple_strtoull(arg, &endp, 0);
+
+       if (endp == arg) {
+
+               val = simple_strtoull(arg, &endp, 16);
+               if (endp == arg)
+                       return KDB_BADINT;
+       }
+
+       *value = val;
+
+       return 0;
+}
+
 /*
  * kdb_set - This function implements the 'set' command.  Alter an
  *     existing environment variable or create a new one.
@@ -1770,11 +1789,65 @@ static int kdb_go(int argc, const char **argv)
  */
 static int kdb_rd(int argc, const char **argv)
 {
-       int diag = kdb_check_regs();
-       if (diag)
-               return diag;
+       int len = kdb_check_regs();
+#if DBG_MAX_REG_NUM > 0
+       int i;
+       char *rname;
+       int rsize;
+       u64 reg64;
+       u32 reg32;
+       u16 reg16;
+       u8 reg8;
+
+       if (len)
+               return len;
+
+       for (i = 0; i < DBG_MAX_REG_NUM; i++) {
+               rsize = dbg_reg_def[i].size * 2;
+               if (rsize > 16)
+                       rsize = 2;
+               if (len + strlen(dbg_reg_def[i].name) + 4 + rsize > 80) {
+                       len = 0;
+                       kdb_printf("\n");
+               }
+               if (len)
+                       len += kdb_printf("  ");
+               switch(dbg_reg_def[i].size * 8) {
+               case 8:
+                       rname = dbg_get_reg(i, &reg8, kdb_current_regs);
+                       if (!rname)
+                               break;
+                       len += kdb_printf("%s: %02x", rname, reg8);
+                       break;
+               case 16:
+                       rname = dbg_get_reg(i, &reg16, kdb_current_regs);
+                       if (!rname)
+                               break;
+                       len += kdb_printf("%s: %04x", rname, reg16);
+                       break;
+               case 32:
+                       rname = dbg_get_reg(i, &reg32, kdb_current_regs);
+                       if (!rname)
+                               break;
+                       len += kdb_printf("%s: %08x", rname, reg32);
+                       break;
+               case 64:
+                       rname = dbg_get_reg(i, &reg64, kdb_current_regs);
+                       if (!rname)
+                               break;
+                       len += kdb_printf("%s: %016llx", rname, reg64);
+                       break;
+               default:
+                       len += kdb_printf("%s: ??", dbg_reg_def[i].name);
+               }
+       }
+       kdb_printf("\n");
+#else
+       if (len)
+               return len;
 
        kdb_dumpregs(kdb_current_regs);
+#endif
        return 0;
 }
 
@@ -1782,32 +1855,67 @@ static int kdb_rd(int argc, const char **argv)
  * kdb_rm - This function implements the 'rm' (register modify)  command.
  *     rm register-name new-contents
  * Remarks:
- *     Currently doesn't allow modification of control or
- *     debug registers.
+ *     Allows register modification with the same restrictions as gdb
  */
 static int kdb_rm(int argc, const char **argv)
 {
+#if DBG_MAX_REG_NUM > 0
        int diag;
-       int ind = 0;
-       unsigned long contents;
+       const char *rname;
+       int i;
+       u64 reg64;
+       u32 reg32;
+       u16 reg16;
+       u8 reg8;
 
        if (argc != 2)
                return KDB_ARGCOUNT;
        /*
         * Allow presence or absence of leading '%' symbol.
         */
-       if (argv[1][0] == '%')
-               ind = 1;
+       rname = argv[1];
+       if (*rname == '%')
+               rname++;
 
-       diag = kdbgetularg(argv[2], &contents);
+       diag = kdbgetu64arg(argv[2], &reg64);
        if (diag)
                return diag;
 
        diag = kdb_check_regs();
        if (diag)
                return diag;
+
+       diag = KDB_BADREG;
+       for (i = 0; i < DBG_MAX_REG_NUM; i++) {
+               if (strcmp(rname, dbg_reg_def[i].name) == 0) {
+                       diag = 0;
+                       break;
+               }
+       }
+       if (!diag) {
+               switch(dbg_reg_def[i].size * 8) {
+               case 8:
+                       reg8 = reg64;
+                       dbg_set_reg(i, &reg8, kdb_current_regs);
+                       break;
+               case 16:
+                       reg16 = reg64;
+                       dbg_set_reg(i, &reg16, kdb_current_regs);
+                       break;
+               case 32:
+                       reg32 = reg64;
+                       dbg_set_reg(i, &reg32, kdb_current_regs);
+                       break;
+               case 64:
+                       dbg_set_reg(i, &reg64, kdb_current_regs);
+                       break;
+               }
+       }
+       return diag;
+#else
        kdb_printf("ERROR: Register set currently not implemented\n");
-       return 0;
+    return 0;
+#endif
 }
 
 #if defined(CONFIG_MAGIC_SYSRQ)
index ffb1a5b..4215530 100644 (file)
@@ -57,5 +57,8 @@ obj-$(CONFIG_EVENT_TRACING) += trace_events_filter.o
 obj-$(CONFIG_KPROBE_EVENT) += trace_kprobe.o
 obj-$(CONFIG_KSYM_TRACER) += trace_ksym.o
 obj-$(CONFIG_EVENT_TRACING) += power-traces.o
+ifeq ($(CONFIG_TRACING),y)
+obj-$(CONFIG_KGDB_KDB) += trace_kdb.o
+endif
 
 libftrace-y := ftrace.o
index 086d363..d6736b9 100644 (file)
@@ -101,10 +101,7 @@ static inline void ftrace_enable_cpu(void)
        preempt_enable();
 }
 
-static cpumask_var_t __read_mostly     tracing_buffer_mask;
-
-#define for_each_tracing_cpu(cpu)      \
-       for_each_cpu(cpu, tracing_buffer_mask)
+cpumask_var_t __read_mostly    tracing_buffer_mask;
 
 /*
  * ftrace_dump_on_oops - variable to dump ftrace buffer on oops
@@ -1539,11 +1536,6 @@ int trace_vprintk(unsigned long ip, const char *fmt, va_list args)
 }
 EXPORT_SYMBOL_GPL(trace_vprintk);
 
-enum trace_file_type {
-       TRACE_FILE_LAT_FMT      = 1,
-       TRACE_FILE_ANNOTATE     = 2,
-};
-
 static void trace_iterator_increment(struct trace_iterator *iter)
 {
        /* Don't allow ftrace to trace into the ring buffers */
@@ -1641,7 +1633,7 @@ struct trace_entry *trace_find_next_entry(struct trace_iterator *iter,
 }
 
 /* Find the next real entry, and increment the iterator to the next entry */
-static void *find_next_entry_inc(struct trace_iterator *iter)
+void *trace_find_next_entry_inc(struct trace_iterator *iter)
 {
        iter->ent = __find_next_entry(iter, &iter->cpu,
                                      &iter->lost_events, &iter->ts);
@@ -1676,19 +1668,19 @@ static void *s_next(struct seq_file *m, void *v, loff_t *pos)
                return NULL;
 
        if (iter->idx < 0)
-               ent = find_next_entry_inc(iter);
+               ent = trace_find_next_entry_inc(iter);
        else
                ent = iter;
 
        while (ent && iter->idx < i)
-               ent = find_next_entry_inc(iter);
+               ent = trace_find_next_entry_inc(iter);
 
        iter->pos = *pos;
 
        return ent;
 }
 
-static void tracing_iter_reset(struct trace_iterator *iter, int cpu)
+void tracing_iter_reset(struct trace_iterator *iter, int cpu)
 {
        struct trace_array *tr = iter->tr;
        struct ring_buffer_event *event;
@@ -2049,7 +2041,7 @@ int trace_empty(struct trace_iterator *iter)
 }
 
 /*  Called with trace_event_read_lock() held. */
-static enum print_line_t print_trace_line(struct trace_iterator *iter)
+enum print_line_t print_trace_line(struct trace_iterator *iter)
 {
        enum print_line_t ret;
 
@@ -3211,7 +3203,7 @@ waitagain:
 
        trace_event_read_lock();
        trace_access_lock(iter->cpu_file);
-       while (find_next_entry_inc(iter) != NULL) {
+       while (trace_find_next_entry_inc(iter) != NULL) {
                enum print_line_t ret;
                int len = iter->seq.len;
 
@@ -3294,7 +3286,7 @@ tracing_fill_pipe_page(size_t rem, struct trace_iterator *iter)
                if (ret != TRACE_TYPE_NO_CONSUME)
                        trace_consume(iter);
                rem -= count;
-               if (!find_next_entry_inc(iter)) {
+               if (!trace_find_next_entry_inc(iter))   {
                        rem = 0;
                        iter->ent = NULL;
                        break;
@@ -3350,7 +3342,7 @@ static ssize_t tracing_splice_read_pipe(struct file *filp,
        if (ret <= 0)
                goto out_err;
 
-       if (!iter->ent && !find_next_entry_inc(iter)) {
+       if (!iter->ent && !trace_find_next_entry_inc(iter)) {
                ret = -EFAULT;
                goto out_err;
        }
@@ -4414,7 +4406,7 @@ static struct notifier_block trace_die_notifier = {
  */
 #define KERN_TRACE             KERN_EMERG
 
-static void
+void
 trace_printk_seq(struct trace_seq *s)
 {
        /* Probably should print a warning here. */
@@ -4429,6 +4421,13 @@ trace_printk_seq(struct trace_seq *s)
        trace_seq_init(s);
 }
 
+void trace_init_global_iter(struct trace_iterator *iter)
+{
+       iter->tr = &global_trace;
+       iter->trace = current_trace;
+       iter->cpu_file = TRACE_PIPE_ALL_CPU;
+}
+
 static void
 __ftrace_dump(bool disable_tracing, enum ftrace_dump_mode oops_dump_mode)
 {
@@ -4454,8 +4453,10 @@ __ftrace_dump(bool disable_tracing, enum ftrace_dump_mode oops_dump_mode)
        if (disable_tracing)
                ftrace_kill();
 
+       trace_init_global_iter(&iter);
+
        for_each_tracing_cpu(cpu) {
-               atomic_inc(&global_trace.data[cpu]->disabled);
+               atomic_inc(&iter.tr->data[cpu]->disabled);
        }
 
        old_userobj = trace_flags & TRACE_ITER_SYM_USEROBJ;
@@ -4504,7 +4505,7 @@ __ftrace_dump(bool disable_tracing, enum ftrace_dump_mode oops_dump_mode)
                iter.iter_flags |= TRACE_FILE_LAT_FMT;
                iter.pos = -1;
 
-               if (find_next_entry_inc(&iter) != NULL) {
+               if (trace_find_next_entry_inc(&iter) != NULL) {
                        int ret;
 
                        ret = print_trace_line(&iter);
@@ -4526,7 +4527,7 @@ __ftrace_dump(bool disable_tracing, enum ftrace_dump_mode oops_dump_mode)
                trace_flags |= old_userobj;
 
                for_each_tracing_cpu(cpu) {
-                       atomic_dec(&global_trace.data[cpu]->disabled);
+                       atomic_dec(&iter.tr->data[cpu]->disabled);
                }
                tracing_on();
        }
index 2cd9639..0605fc0 100644 (file)
@@ -338,6 +338,14 @@ struct trace_entry *tracing_get_trace_entry(struct trace_array *tr,
 struct trace_entry *trace_find_next_entry(struct trace_iterator *iter,
                                          int *ent_cpu, u64 *ent_ts);
 
+int trace_empty(struct trace_iterator *iter);
+
+void *trace_find_next_entry_inc(struct trace_iterator *iter);
+
+void trace_init_global_iter(struct trace_iterator *iter);
+
+void tracing_iter_reset(struct trace_iterator *iter, int cpu);
+
 void default_wait_pipe(struct trace_iterator *iter);
 void poll_wait_pipe(struct trace_iterator *iter);
 
@@ -380,6 +388,15 @@ void tracing_start_sched_switch_record(void);
 int register_tracer(struct tracer *type);
 void unregister_tracer(struct tracer *type);
 int is_tracing_stopped(void);
+enum trace_file_type {
+       TRACE_FILE_LAT_FMT      = 1,
+       TRACE_FILE_ANNOTATE     = 2,
+};
+
+extern cpumask_var_t __read_mostly tracing_buffer_mask;
+
+#define for_each_tracing_cpu(cpu)      \
+       for_each_cpu(cpu, tracing_buffer_mask)
 
 extern int process_new_ksym_entry(char *ksymname, int op, unsigned long addr);
 
@@ -471,6 +488,8 @@ trace_array_vprintk(struct trace_array *tr,
                    unsigned long ip, const char *fmt, va_list args);
 int trace_array_printk(struct trace_array *tr,
                       unsigned long ip, const char *fmt, ...);
+void trace_printk_seq(struct trace_seq *s);
+enum print_line_t print_trace_line(struct trace_iterator *iter);
 
 extern unsigned long trace_flags;
 
diff --git a/kernel/trace/trace_kdb.c b/kernel/trace/trace_kdb.c
new file mode 100644 (file)
index 0000000..7b8ecd7
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * kdb helper for dumping the ftrace buffer
+ *
+ * Copyright (C) 2010 Jason Wessel <jason.wessel@windriver.com>
+ *
+ * ftrace_dump_buf based on ftrace_dump:
+ * Copyright (C) 2007-2008 Steven Rostedt <srostedt@redhat.com>
+ * Copyright (C) 2008 Ingo Molnar <mingo@redhat.com>
+ *
+ */
+#include <linux/init.h>
+#include <linux/kgdb.h>
+#include <linux/kdb.h>
+#include <linux/ftrace.h>
+
+#include "../debug/kdb/kdb_private.h"
+#include "trace.h"
+#include "trace_output.h"
+
+static void ftrace_dump_buf(int skip_lines, long cpu_file)
+{
+       /* use static because iter can be a bit big for the stack */
+       static struct trace_iterator iter;
+       unsigned int old_userobj;
+       int cnt = 0, cpu;
+
+       trace_init_global_iter(&iter);
+
+       for_each_tracing_cpu(cpu) {
+               atomic_inc(&iter.tr->data[cpu]->disabled);
+       }
+
+       old_userobj = trace_flags;
+
+       /* don't look at user memory in panic mode */
+       trace_flags &= ~TRACE_ITER_SYM_USEROBJ;
+
+       kdb_printf("Dumping ftrace buffer:\n");
+
+       /* reset all but tr, trace, and overruns */
+       memset(&iter.seq, 0,
+                  sizeof(struct trace_iterator) -
+                  offsetof(struct trace_iterator, seq));
+       iter.iter_flags |= TRACE_FILE_LAT_FMT;
+       iter.pos = -1;
+
+       if (cpu_file == TRACE_PIPE_ALL_CPU) {
+               for_each_tracing_cpu(cpu) {
+                       iter.buffer_iter[cpu] =
+                       ring_buffer_read_prepare(iter.tr->buffer, cpu);
+                       ring_buffer_read_start(iter.buffer_iter[cpu]);
+                       tracing_iter_reset(&iter, cpu);
+               }
+       } else {
+               iter.cpu_file = cpu_file;
+               iter.buffer_iter[cpu_file] =
+                       ring_buffer_read_prepare(iter.tr->buffer, cpu_file);
+               ring_buffer_read_start(iter.buffer_iter[cpu_file]);
+               tracing_iter_reset(&iter, cpu_file);
+       }
+       if (!trace_empty(&iter))
+               trace_find_next_entry_inc(&iter);
+       while (!trace_empty(&iter)) {
+               if (!cnt)
+                       kdb_printf("---------------------------------\n");
+               cnt++;
+
+               if (trace_find_next_entry_inc(&iter) != NULL && !skip_lines)
+                       print_trace_line(&iter);
+               if (!skip_lines)
+                       trace_printk_seq(&iter.seq);
+               else
+                       skip_lines--;
+               if (KDB_FLAG(CMD_INTERRUPT))
+                       goto out;
+       }
+
+       if (!cnt)
+               kdb_printf("   (ftrace buffer empty)\n");
+       else
+               kdb_printf("---------------------------------\n");
+
+out:
+       trace_flags = old_userobj;
+
+       for_each_tracing_cpu(cpu) {
+               atomic_dec(&iter.tr->data[cpu]->disabled);
+       }
+
+       for_each_tracing_cpu(cpu)
+               if (iter.buffer_iter[cpu])
+                       ring_buffer_read_finish(iter.buffer_iter[cpu]);
+}
+
+/*
+ * kdb_ftdump - Dump the ftrace log buffer
+ */
+static int kdb_ftdump(int argc, const char **argv)
+{
+       int skip_lines = 0;
+       long cpu_file;
+       char *cp;
+
+       if (argc > 2)
+               return KDB_ARGCOUNT;
+
+       if (argc) {
+               skip_lines = simple_strtol(argv[1], &cp, 0);
+               if (*cp)
+                       skip_lines = 0;
+       }
+
+       if (argc == 2) {
+               cpu_file = simple_strtol(argv[2], &cp, 0);
+               if (*cp || cpu_file >= NR_CPUS || cpu_file < 0 ||
+                   !cpu_online(cpu_file))
+                       return KDB_BADINT;
+       } else {
+               cpu_file = TRACE_PIPE_ALL_CPU;
+       }
+
+       kdb_trap_printk++;
+       ftrace_dump_buf(skip_lines, cpu_file);
+       kdb_trap_printk--;
+
+       return 0;
+}
+
+static __init int kdb_ftrace_register(void)
+{
+       kdb_register_repeat("ftdump", kdb_ftdump, "[skip_#lines] [cpu]",
+                           "Dump ftrace log", 0, KDB_REPEAT_NONE);
+       return 0;
+}
+
+late_initcall(kdb_ftrace_register);
index 66baa20..7a0aa1b 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/init.h>
 #include <linux/hash.h>
 #include <linux/highmem.h>
+#include <linux/kgdb.h>
 #include <asm/tlbflush.h>
 
 /*
@@ -470,6 +471,12 @@ void debug_kmap_atomic(enum km_type type)
                        warn_count--;
                }
        }
+#ifdef CONFIG_KGDB_KDB
+       if (unlikely(type == KM_KDB && atomic_read(&kgdb_active) == -1)) {
+               WARN_ON(1);
+               warn_count--;
+       }
+#endif /* CONFIG_KGDB_KDB */
 }
 
 #endif