]> err.no Git - linux-2.6/commitdiff
x86: kgdb support
authorIngo Molnar <mingo@elte.hu>
Thu, 17 Apr 2008 18:05:37 +0000 (20:05 +0200)
committerIngo Molnar <mingo@elte.hu>
Thu, 17 Apr 2008 18:05:37 +0000 (20:05 +0200)
simplified and streamlined kgdb support on x86, both 32-bit and 64-bit,
based on patch from:

  Subject: kgdb: core-lite
  From: Jason Wessel <jason.wessel@windriver.com>

[ and countless other authors - see the patch for details. ]

Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Jason Wessel <jason.wessel@windriver.com>
Signed-off-by: Jan Kiszka <jan.kiszka@web.de>
Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
arch/x86/Kconfig
arch/x86/kernel/Makefile
arch/x86/kernel/kgdb.c [new file with mode: 0644]
include/asm-x86/kgdb.h [new file with mode: 0644]

index 6c70fed0f9a0f9e35cd027072a464b8c42b85434..5c4c8d7a46cc2a86e14658f1449f291e3e61fefe 100644 (file)
@@ -23,6 +23,7 @@ config X86
        select HAVE_KPROBES
        select HAVE_KRETPROBES
        select HAVE_KVM if ((X86_32 && !X86_VOYAGER && !X86_VISWS && !X86_NUMAQ) || X86_64)
+       select HAVE_ARCH_KGDB
 
 
 config GENERIC_LOCKBREAK
index 4eb5ce841106765256ba16c940b6d8c52ba638e6..4a4260c7f6729a9f8eaf518f260d6a8178b33263 100644 (file)
@@ -66,6 +66,7 @@ obj-$(CONFIG_MODULES)         += module_$(BITS).o
 obj-$(CONFIG_ACPI_SRAT)        += srat_32.o
 obj-$(CONFIG_EFI)              += efi.o efi_$(BITS).o efi_stub_$(BITS).o
 obj-$(CONFIG_DOUBLEFAULT)      += doublefault_32.o
+obj-$(CONFIG_KGDB)             += kgdb.o
 obj-$(CONFIG_VM86)             += vm86_32.o
 obj-$(CONFIG_EARLY_PRINTK)     += early_printk.o
 
diff --git a/arch/x86/kernel/kgdb.c b/arch/x86/kernel/kgdb.c
new file mode 100644 (file)
index 0000000..37194d6
--- /dev/null
@@ -0,0 +1,417 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ */
+
+/*
+ * Copyright (C) 2004 Amit S. Kale <amitkale@linsyssoft.com>
+ * Copyright (C) 2000-2001 VERITAS Software Corporation.
+ * Copyright (C) 2002 Andi Kleen, SuSE Labs
+ * Copyright (C) 2004 LinSysSoft Technologies Pvt. Ltd.
+ * Copyright (C) 2007 MontaVista Software, Inc.
+ * Copyright (C) 2007-2008 Jason Wessel, Wind River Systems, Inc.
+ */
+/****************************************************************************
+ *  Contributor:     Lake Stevens Instrument Division$
+ *  Written by:      Glenn Engel $
+ *  Updated by:             Amit Kale<akale@veritas.com>
+ *  Updated by:             Tom Rini <trini@kernel.crashing.org>
+ *  Updated by:             Jason Wessel <jason.wessel@windriver.com>
+ *  Modified for 386 by Jim Kingdon, Cygnus Support.
+ *  Origianl kgdb, compatibility with 2.1.xx kernel by
+ *  David Grothe <dave@gcom.com>
+ *  Integrated into 2.2.5 kernel by Tigran Aivazian <tigran@sco.com>
+ *  X86_64 changes from Andi Kleen's patch merged by Jim Houston
+ */
+#include <linux/spinlock.h>
+#include <linux/kdebug.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/ptrace.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/kgdb.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+
+#include <asm/apicdef.h>
+#include <asm/system.h>
+
+#ifdef CONFIG_X86_32
+# include <mach_ipi.h>
+#else
+# include <asm/mach_apic.h>
+#endif
+
+/*
+ * Put the error code here just in case the user cares:
+ */
+static int gdb_x86errcode;
+
+/*
+ * Likewise, the vector number here (since GDB only gets the signal
+ * number through the usual means, and that's not very specific):
+ */
+static int gdb_x86vector = -1;
+
+/**
+ *     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)
+{
+       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_PS]        = regs->flags;
+       gdb_regs[GDB_PC]        = regs->ip;
+#ifdef CONFIG_X86_32
+       gdb_regs[GDB_DS]        = regs->ds;
+       gdb_regs[GDB_ES]        = regs->es;
+       gdb_regs[GDB_CS]        = regs->cs;
+       gdb_regs[GDB_SS]        = __KERNEL_DS;
+       gdb_regs[GDB_FS]        = 0xFFFF;
+       gdb_regs[GDB_GS]        = 0xFFFF;
+#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;
+#endif
+       gdb_regs[GDB_SP]        = regs->sp;
+}
+
+/**
+ *     sleeping_thread_to_gdb_regs - Convert ptrace regs to GDB regs
+ *     @gdb_regs: A pointer to hold the registers in the order GDB wants.
+ *     @p: The &struct task_struct of the desired process.
+ *
+ *     Convert the register values of the sleeping process in @p to
+ *     the format that GDB expects.
+ *     This function is called when kgdb does not have access to the
+ *     &struct pt_regs and therefore it should fill the gdb registers
+ *     @gdb_regs with what has been saved in &struct thread_struct
+ *     thread field during switch_to.
+ */
+void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p)
+{
+       gdb_regs[GDB_AX]        = 0;
+       gdb_regs[GDB_BX]        = 0;
+       gdb_regs[GDB_CX]        = 0;
+       gdb_regs[GDB_DX]        = 0;
+       gdb_regs[GDB_SI]        = 0;
+       gdb_regs[GDB_DI]        = 0;
+       gdb_regs[GDB_BP]        = *(unsigned long *)p->thread.sp;
+#ifdef CONFIG_X86_32
+       gdb_regs[GDB_DS]        = __KERNEL_DS;
+       gdb_regs[GDB_ES]        = __KERNEL_DS;
+       gdb_regs[GDB_PS]        = 0;
+       gdb_regs[GDB_CS]        = __KERNEL_CS;
+       gdb_regs[GDB_PC]        = p->thread.ip;
+       gdb_regs[GDB_SS]        = __KERNEL_DS;
+       gdb_regs[GDB_FS]        = 0xFFFF;
+       gdb_regs[GDB_GS]        = 0xFFFF;
+#else
+       gdb_regs[GDB_PS]        = *(unsigned long *)(p->thread.sp + 8);
+       gdb_regs[GDB_PC]        = 0;
+       gdb_regs[GDB_R8]        = 0;
+       gdb_regs[GDB_R9]        = 0;
+       gdb_regs[GDB_R10]       = 0;
+       gdb_regs[GDB_R11]       = 0;
+       gdb_regs[GDB_R12]       = 0;
+       gdb_regs[GDB_R13]       = 0;
+       gdb_regs[GDB_R14]       = 0;
+       gdb_regs[GDB_R15]       = 0;
+#endif
+       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)
+{
+       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->flags             = gdb_regs[GDB_PS];
+       regs->ip                = gdb_regs[GDB_PC];
+#ifdef CONFIG_X86_32
+       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];
+#endif
+}
+
+/**
+ *     kgdb_post_primary_code - Save error vector/code numbers.
+ *     @regs: Original pt_regs.
+ *     @e_vector: Original error vector.
+ *     @err_code: Original error code.
+ *
+ *     This is needed on architectures which support SMP and KGDB.
+ *     This function is called after all the slave cpus have been put
+ *     to a know spin state and the primary CPU has control over KGDB.
+ */
+void kgdb_post_primary_code(struct pt_regs *regs, int e_vector, int err_code)
+{
+       /* primary processor is completely in the debugger */
+       gdb_x86vector = e_vector;
+       gdb_x86errcode = err_code;
+}
+
+#ifdef CONFIG_SMP
+/**
+ *     kgdb_roundup_cpus - Get other CPUs into a holding pattern
+ *     @flags: Current IRQ state
+ *
+ *     On SMP systems, we need to get the attention of the other CPUs
+ *     and get them be in a known state.  This should do what is needed
+ *     to get the other CPUs to call kgdb_wait(). Note that on some arches,
+ *     the NMI approach is not used for rounding up all the CPUs. For example,
+ *     in case of MIPS, smp_call_function() is used to roundup CPUs. In
+ *     this case, we have to make sure that interrupts are enabled before
+ *     calling smp_call_function(). The argument to this function is
+ *     the flags that will be used when restoring the interrupts. There is
+ *     local_irq_save() call before kgdb_roundup_cpus().
+ *
+ *     On non-SMP systems, this is not called.
+ */
+void kgdb_roundup_cpus(unsigned long flags)
+{
+       send_IPI_allbutself(APIC_DM_NMI);
+}
+#endif
+
+/**
+ *     kgdb_arch_handle_exception - Handle architecture specific GDB packets.
+ *     @vector: The error vector of the exception that happened.
+ *     @signo: The signal number of the exception that happened.
+ *     @err_code: The error code of the exception that happened.
+ *     @remcom_in_buffer: The buffer of the packet we have read.
+ *     @remcom_out_buffer: The buffer of %BUFMAX bytes to write a packet into.
+ *     @regs: The &struct pt_regs of the current process.
+ *
+ *     This function MUST handle the 'c' and 's' command packets,
+ *     as well packets to set / remove a hardware breakpoint, if used.
+ *     If there are additional packets which the hardware needs to handle,
+ *     they are handled here.  The code should return -1 if it wants to
+ *     process more packets, and a %0 or %1 if it wants to exit from the
+ *     kgdb callback.
+ */
+int kgdb_arch_handle_exception(int e_vector, int signo, int err_code,
+                              char *remcomInBuffer, char *remcomOutBuffer,
+                              struct pt_regs *linux_regs)
+{
+       unsigned long addr;
+       char *ptr;
+       int newPC;
+
+       switch (remcomInBuffer[0]) {
+       case 'c':
+       case 's':
+               /* try to read optional parameter, pc unchanged if no parm */
+               ptr = &remcomInBuffer[1];
+               if (kgdb_hex2long(&ptr, &addr))
+                       linux_regs->ip = addr;
+               newPC = linux_regs->ip;
+
+               /* clear the trace bit */
+               linux_regs->flags &= ~TF_MASK;
+               atomic_set(&kgdb_cpu_doing_single_step, -1);
+
+               /* set the trace bit if we're stepping */
+               if (remcomInBuffer[0] == 's') {
+                       linux_regs->flags |= TF_MASK;
+                       kgdb_single_step = 1;
+                       if (kgdb_contthread) {
+                               atomic_set(&kgdb_cpu_doing_single_step,
+                                          raw_smp_processor_id());
+                       }
+               }
+
+               return 0;
+       }
+
+       /* this means that we do not want to exit from the handler: */
+       return -1;
+}
+
+static inline int
+single_step_cont(struct pt_regs *regs, struct die_args *args)
+{
+       /*
+        * Single step exception from kernel space to user space so
+        * eat the exception and continue the process:
+        */
+       printk(KERN_ERR "KGDB: trap/step from kernel to user space, "
+                       "resuming...\n");
+       kgdb_arch_handle_exception(args->trapnr, args->signr,
+                                  args->err, "c", "", regs);
+
+       return NOTIFY_STOP;
+}
+
+static int __kgdb_notify(struct die_args *args, unsigned long cmd)
+{
+       struct pt_regs *regs = args->regs;
+
+       switch (cmd) {
+       case DIE_NMI:
+               if (atomic_read(&kgdb_active) != -1) {
+                       /* KGDB CPU roundup */
+                       kgdb_nmicallback(raw_smp_processor_id(), regs);
+                       return NOTIFY_STOP;
+               }
+               return NOTIFY_DONE;
+
+       case DIE_NMI_IPI:
+               if (atomic_read(&kgdb_active) != -1) {
+                       /* KGDB CPU roundup: */
+                       if (kgdb_nmicallback(raw_smp_processor_id(), regs))
+                               return NOTIFY_DONE;
+                       return NOTIFY_STOP;
+               }
+               return NOTIFY_DONE;
+
+       case DIE_NMIWATCHDOG:
+               if (atomic_read(&kgdb_active) != -1) {
+                       /* KGDB CPU roundup: */
+                       kgdb_nmicallback(raw_smp_processor_id(), regs);
+                       return NOTIFY_STOP;
+               }
+               /* Enter debugger: */
+               break;
+
+       case DIE_DEBUG:
+               if (atomic_read(&kgdb_cpu_doing_single_step) ==
+                       raw_smp_processor_id() &&
+                       user_mode(regs))
+                       return single_step_cont(regs, args);
+               /* fall through */
+       default:
+               if (user_mode(regs))
+                       return NOTIFY_DONE;
+       }
+
+       if (kgdb_handle_exception(args->trapnr, args->signr, args->err, 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,
+
+       /*
+        * Lowest-prio notifier priority, we want to be notified last:
+        */
+       .priority       = -INT_MAX,
+};
+
+/**
+ *     kgdb_arch_init - Perform any architecture specific initalization.
+ *
+ *     This function will handle the initalization of any architecture
+ *     specific callbacks.
+ */
+int kgdb_arch_init(void)
+{
+       return register_die_notifier(&kgdb_notifier);
+}
+
+/**
+ *     kgdb_arch_exit - Perform any architecture specific uninitalization.
+ *
+ *     This function will handle the uninitalization of any architecture
+ *     specific callbacks, for dynamic registration and unregistration.
+ */
+void kgdb_arch_exit(void)
+{
+       unregister_die_notifier(&kgdb_notifier);
+}
+
+/**
+ *
+ *     kgdb_skipexception - Bail out of KGDB when we've been triggered.
+ *     @exception: Exception vector number
+ *     @regs: Current &struct pt_regs.
+ *
+ *     On some architectures we need to skip a breakpoint exception when
+ *     it occurs after a breakpoint has been removed.
+ *
+ * Skip an int3 exception when it occurs after a breakpoint has been
+ * removed. Backtrack eip by 1 since the int3 would have caused it to
+ * increment by 1.
+ */
+int kgdb_skipexception(int exception, struct pt_regs *regs)
+{
+       if (exception == 3 && kgdb_isremovedbreak(regs->ip - 1)) {
+               regs->ip -= 1;
+               return 1;
+       }
+       return 0;
+}
+
+unsigned long kgdb_arch_pc(int exception, struct pt_regs *regs)
+{
+       if (exception == 3)
+               return instruction_pointer(regs) - 1;
+       return instruction_pointer(regs);
+}
+
+struct kgdb_arch arch_kgdb_ops = {
+       /* Breakpoint instruction: */
+       .gdb_bpt_instr          = { 0xcc },
+};
diff --git a/include/asm-x86/kgdb.h b/include/asm-x86/kgdb.h
new file mode 100644 (file)
index 0000000..484c475
--- /dev/null
@@ -0,0 +1,81 @@
+#ifndef _ASM_KGDB_H_
+#define _ASM_KGDB_H_
+
+/*
+ * Copyright (C) 2001-2004 Amit S. Kale
+ * Copyright (C) 2008 Wind River Systems, Inc.
+ */
+
+/*
+ * BUFMAX defines the maximum number of characters in inbound/outbound
+ * buffers at least NUMREGBYTES*2 are needed for register packets
+ * Longer buffer is needed to list all threads
+ */
+#define BUFMAX                 1024
+
+/*
+ *  Note that this register image is in a different order than
+ *  the register image that Linux produces at interrupt time.
+ *
+ *  Linux's register image is defined by struct pt_regs in ptrace.h.
+ *  Just why GDB uses a different order is a historical mystery.
+ */
+#ifdef CONFIG_X86_32
+enum regnames {
+       GDB_AX,                 /* 0 */
+       GDB_CX,                 /* 1 */
+       GDB_DX,                 /* 2 */
+       GDB_BX,                 /* 3 */
+       GDB_SP,                 /* 4 */
+       GDB_BP,                 /* 5 */
+       GDB_SI,                 /* 6 */
+       GDB_DI,                 /* 7 */
+       GDB_PC,                 /* 8 also known as eip */
+       GDB_PS,                 /* 9 also known as eflags */
+       GDB_CS,                 /* 10 */
+       GDB_SS,                 /* 11 */
+       GDB_DS,                 /* 12 */
+       GDB_ES,                 /* 13 */
+       GDB_FS,                 /* 14 */
+       GDB_GS,                 /* 15 */
+};
+#else /* ! CONFIG_X86_32 */
+enum regnames {
+       GDB_AX,                 /* 0 */
+       GDB_DX,                 /* 1 */
+       GDB_CX,                 /* 2 */
+       GDB_BX,                 /* 3 */
+       GDB_SI,                 /* 4 */
+       GDB_DI,                 /* 5 */
+       GDB_BP,                 /* 6 */
+       GDB_SP,                 /* 7 */
+       GDB_R8,                 /* 8 */
+       GDB_R9,                 /* 9 */
+       GDB_R10,                /* 10 */
+       GDB_R11,                /* 11 */
+       GDB_R12,                /* 12 */
+       GDB_R13,                /* 13 */
+       GDB_R14,                /* 14 */
+       GDB_R15,                /* 15 */
+       GDB_PC,                 /* 16 */
+       GDB_PS,                 /* 17 */
+};
+#endif /* CONFIG_X86_32 */
+
+/*
+ * Number of bytes of registers:
+ */
+#ifdef CONFIG_X86_32
+# define NUMREGBYTES           64
+#else
+# define NUMREGBYTES           ((GDB_PS+1)*8)
+#endif
+
+static inline void arch_kgdb_breakpoint(void)
+{
+       asm("   int $3");
+}
+#define BREAK_INSTR_SIZE       1
+#define CACHE_FLUSH_IS_SAFE    1
+
+#endif                         /* _ASM_KGDB_H_ */