I've been poking at kgdb for ia64 ... here's what I have so far. Known problems: 1) Backtrace doesn't step back more than one function ... probably I'm giving some bad register information (or not enough register information). 2) gdb will set breakpoints, but won't clear them again: (gdb) br sys_fsync Breakpoint 1 at 0xa00000010019d8f0 (gdb) clear sys_fsync No breakpoint at sys_fsync. Probably lots more ... I haven't done much testing beyond finding that I can continue from the initial breakpoint, single-step, examine memory. Signed-off-by: Tony Luck <tony.luck@xxxxxxxxx> --- arch/ia64/Kconfig | 1 arch/ia64/include/asm/break.h | 1 arch/ia64/include/asm/kgdb.h | 85 ++++++++ arch/ia64/kernel/Makefile | 1 arch/ia64/kernel/entry.S | 9 arch/ia64/kernel/kgdb.c | 437 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 534 insertions(+) diff --git a/arch/ia64/Kconfig b/arch/ia64/Kconfig index 6bd91ed..ebf1696 100644 --- a/arch/ia64/Kconfig +++ b/arch/ia64/Kconfig @@ -23,6 +23,7 @@ config IA64 select HAVE_KRETPROBES select HAVE_DMA_ATTRS select HAVE_KVM + select HAVE_ARCH_KGDB select HAVE_ARCH_TRACEHOOK default y help diff --git a/arch/ia64/include/asm/break.h b/arch/ia64/include/asm/break.h index e90c40e..bd93aa1 100644 --- a/arch/ia64/include/asm/break.h +++ b/arch/ia64/include/asm/break.h @@ -14,6 +14,7 @@ #define __IA64_BREAK_KDB 0x80100 #define __IA64_BREAK_KPROBE 0x81000 /* .. 0x81fff */ #define __IA64_BREAK_JPROBE 0x82000 +#define __IA64_BREAK_KGDB 0x83000 /* .. 0x83fff */ /* * OS-specific break numbers: diff --git a/arch/ia64/include/asm/kgdb.h b/arch/ia64/include/asm/kgdb.h new file mode 100644 index 0000000..b084fa6 --- /dev/null +++ b/arch/ia64/include/asm/kgdb.h @@ -0,0 +1,85 @@ +#ifndef _ASM_IA64_KGDB_H +#define _ASM_IA64_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 8192 + +/* + * 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. + */ +enum regnames64 { + GDB_r1 = 1, + GDB_r2, + GDB_r3, + GDB_r8 = 8, + GDB_r9, + GDB_r10, + GDB_r11, + GDB_r12, + GDB_r13, + GDB_r14, + GDB_r15, + GDB_r16, + GDB_r17, + GDB_r18, + GDB_r19, + GDB_r20, + GDB_r21, + GDB_r22, + GDB_r23, + GDB_r24, + GDB_r25, + GDB_r26, + GDB_r27, + GDB_r28, + GDB_r29, + GDB_r30, + GDB_r31, + GDB_f6 = 140, + GDB_f7, + GDB_f8, + GDB_f9, + GDB_f10, + GDB_f11, + GDB_b0 = 448, + GDB_b6 = 454, + GDB_b7, + GDB_pr = 458, + GDB_cr_iip, + GDB_cr_ipsr, + GDB_ar_rsc = 478, + GDB_ar_bspstore = 480, + GDB_ar_rnat, + GDB_ar_csd = 487, + GDB_ar_ssd, + GDB_ar_ccv = 494, + GDB_ar_unat = 498, + GDB_ar_fpsr = 502, + GDB_ar_pfs = 526, +/* These are in pt_regs, but gdb doesn't have slots for them + GDB_cr_ifs, + GDB_loadrs, +*/ +}; + +#define NUMREGBYTES ((GDB_ar_pfs+1)*8) + +extern void arch_kgdb_breakpoint(void); + +#define BREAK_INSTR_SIZE 4 +#define CACHE_FLUSH_IS_SAFE 1 + +#endif /* _ASM_IA64_KGDB_H */ diff --git a/arch/ia64/kernel/Makefile b/arch/ia64/kernel/Makefile index c381ea9..8b926c7 100644 --- a/arch/ia64/kernel/Makefile +++ b/arch/ia64/kernel/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_CRASH_DUMP) += crash_dump.o obj-$(CONFIG_IA64_UNCACHED_ALLOCATOR) += uncached.o obj-$(CONFIG_AUDIT) += audit.o obj-$(CONFIG_PCI_MSI) += msi_ia64.o +obj-$(CONFIG_KGDB) += kgdb.o mca_recovery-y += mca_drv.o mca_drv_asm.o obj-$(CONFIG_IA64_MC_ERR_INJECT)+= err_inject.o diff --git a/arch/ia64/kernel/entry.S b/arch/ia64/kernel/entry.S index d435f4a..74a14fb 100644 --- a/arch/ia64/kernel/entry.S +++ b/arch/ia64/kernel/entry.S @@ -50,6 +50,15 @@ #include "minstate.h" +#ifdef CONFIG_KGDB +#include <asm/break.h> + +GLOBAL_ENTRY(arch_kgdb_breakpoint) + break __IA64_BREAK_KGDB + br.ret.sptk.many rp +END(arch_kgdb_breakpoint) +#endif + #ifdef __IA64_ASM_PARAVIRTUALIZED_NATIVE /* * execve() is special because in case of success, we need to diff --git a/arch/ia64/kernel/kgdb.c b/arch/ia64/kernel/kgdb.c new file mode 100644 index 0000000..02a6aaf --- /dev/null +++ b/arch/ia64/kernel/kgdb.c @@ -0,0 +1,437 @@ +/* + * 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@xxxxxxxxxxxxxx> + * 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@xxxxxxxxxxx> + * Updated by: Tom Rini <trini@xxxxxxxxxxxxxxxxxxx> + * Updated by: Jason Wessel <jason.wessel@xxxxxxxxxxxxx> + * Modified for 386 by Jim Kingdon, Cygnus Support. + * Origianl kgdb, compatibility with 2.1.xx kernel by + * David Grothe <dave@xxxxxxxx> + * Integrated into 2.2.5 kernel by Tigran Aivazian <tigran@xxxxxxx> + * X86_64 changes from Andi Kleen's patch merged by Jim Houston + * IA64 changes: Tony Luck + */ +#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 <linux/nmi.h> + +#include <asm/break.h> +#include <asm/sections.h> +#include <asm/system.h> + +u64 bkpt_iip; + +/** + * 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_r1] = regs->r1; + gdb_regs[GDB_r2] = regs->r2; + gdb_regs[GDB_r3] = regs->r3; + 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_regs[GDB_r16] = regs->r16; + gdb_regs[GDB_r17] = regs->r17; + gdb_regs[GDB_r18] = regs->r18; + gdb_regs[GDB_r19] = regs->r19; + gdb_regs[GDB_r20] = regs->r20; + gdb_regs[GDB_r21] = regs->r21; + gdb_regs[GDB_r22] = regs->r22; + gdb_regs[GDB_r23] = regs->r23; + gdb_regs[GDB_r24] = regs->r24; + gdb_regs[GDB_r25] = regs->r25; + gdb_regs[GDB_r26] = regs->r26; + gdb_regs[GDB_r27] = regs->r27; + gdb_regs[GDB_r28] = regs->r28; + gdb_regs[GDB_r29] = regs->r29; + gdb_regs[GDB_r30] = regs->r30; + gdb_regs[GDB_r31] = regs->r31; + gdb_regs[GDB_f6] = regs->f6.u.bits[0]; + gdb_regs[GDB_f6+1] = regs->f6.u.bits[1]; + gdb_regs[GDB_f7] = regs->f7.u.bits[0]; + gdb_regs[GDB_f7+1] = regs->f7.u.bits[1]; + gdb_regs[GDB_f8] = regs->f8.u.bits[0]; + gdb_regs[GDB_f8+1] = regs->f8.u.bits[1]; + gdb_regs[GDB_f9] = regs->f9.u.bits[0]; + gdb_regs[GDB_f9+1] = regs->f9.u.bits[1]; + gdb_regs[GDB_f10] = regs->f10.u.bits[0]; + gdb_regs[GDB_f10+1] = regs->f10.u.bits[1]; + gdb_regs[GDB_f11] = regs->f11.u.bits[0]; + gdb_regs[GDB_f11+1] = regs->f11.u.bits[1]; + gdb_regs[GDB_b0] = regs->b0; + gdb_regs[GDB_b6] = regs->b6; + gdb_regs[GDB_b7] = regs->b7; + gdb_regs[GDB_pr] = regs->pr; + gdb_regs[GDB_cr_iip] = regs->cr_iip; + gdb_regs[GDB_cr_ipsr] = regs->cr_ipsr; + gdb_regs[GDB_ar_rsc] = regs->ar_rsc; + gdb_regs[GDB_ar_bspstore] = regs->ar_bspstore; + gdb_regs[GDB_ar_rnat] = regs->ar_rnat; + gdb_regs[GDB_ar_csd] = regs->ar_csd; + gdb_regs[GDB_ar_ssd] = regs->ar_ssd; + gdb_regs[GDB_ar_ccv] = regs->ar_ccv; + gdb_regs[GDB_ar_unat] = regs->ar_unat; + gdb_regs[GDB_ar_fpsr] = regs->ar_fpsr; + gdb_regs[GDB_ar_pfs] = regs->ar_pfs; +} + +/** + * 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) +{ + if (!p) + return; + + pt_regs_to_gdb_regs(gdb_regs, task_pt_regs(p)); +} + +/** + * 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->r1 = gdb_regs[GDB_r1]; + regs->r2 = gdb_regs[GDB_r2]; + regs->r3 = gdb_regs[GDB_r3]; + 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->r16 = gdb_regs[GDB_r16]; + regs->r17 = gdb_regs[GDB_r17]; + regs->r18 = gdb_regs[GDB_r18]; + regs->r19 = gdb_regs[GDB_r19]; + regs->r20 = gdb_regs[GDB_r20]; + regs->r21 = gdb_regs[GDB_r21]; + regs->r22 = gdb_regs[GDB_r22]; + regs->r23 = gdb_regs[GDB_r23]; + regs->r24 = gdb_regs[GDB_r24]; + regs->r25 = gdb_regs[GDB_r25]; + regs->r26 = gdb_regs[GDB_r26]; + regs->r27 = gdb_regs[GDB_r27]; + regs->r28 = gdb_regs[GDB_r28]; + regs->r29 = gdb_regs[GDB_r29]; + regs->r30 = gdb_regs[GDB_r30]; + regs->r31 = gdb_regs[GDB_r31]; + regs->f6.u.bits[0] = gdb_regs[GDB_f6]; + regs->f6.u.bits[1] = gdb_regs[GDB_f6+1]; + regs->f7.u.bits[0] = gdb_regs[GDB_f7]; + regs->f7.u.bits[1] = gdb_regs[GDB_f7+1]; + regs->f8.u.bits[0] = gdb_regs[GDB_f8]; + regs->f8.u.bits[1] = gdb_regs[GDB_f8+1]; + regs->f9.u.bits[0] = gdb_regs[GDB_f9]; + regs->f9.u.bits[1] = gdb_regs[GDB_f9+1]; + regs->f10.u.bits[0] = gdb_regs[GDB_f10]; + regs->f10.u.bits[1] = gdb_regs[GDB_f10+1]; + regs->f11.u.bits[0] = gdb_regs[GDB_f11]; + regs->f11.u.bits[1] = gdb_regs[GDB_f11+1]; + regs->b0 = gdb_regs[GDB_b0]; + regs->b6 = gdb_regs[GDB_b6]; + regs->b7 = gdb_regs[GDB_b7]; + regs->pr = gdb_regs[GDB_pr]; + regs->cr_iip = gdb_regs[GDB_cr_iip]; + regs->cr_ipsr = gdb_regs[GDB_cr_ipsr]; + regs->ar_rsc = gdb_regs[GDB_ar_rsc]; + regs->ar_bspstore = gdb_regs[GDB_ar_bspstore]; + regs->ar_rnat = gdb_regs[GDB_ar_rnat]; + regs->ar_csd = gdb_regs[GDB_ar_csd]; + regs->ar_ssd = gdb_regs[GDB_ar_ssd]; + regs->ar_ccv = gdb_regs[GDB_ar_ccv]; + regs->ar_unat = gdb_regs[GDB_ar_unat]; + regs->ar_fpsr = gdb_regs[GDB_ar_fpsr]; + regs->ar_pfs = gdb_regs[GDB_ar_pfs]; +} + +#ifdef CONFIG_SMP +static void kgdb_call_nmi_hook(void *ignored) +{ + kgdb_nmicallback(raw_smp_processor_id(), NULL); +} + +/** + * kgdb_roundup_cpus - Get other CPUs into a holding pattern + * @flags: Current IRQ state + */ +void kgdb_roundup_cpus(unsigned long flags) +{ + local_irq_enable(); + smp_call_function(kgdb_call_nmi_hook, NULL, 0); + local_irq_disable(); +} +#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; + + 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->cr_iip = addr; + linux_regs->cr_ipsr &= ~IA64_PSR_RI; + switch (addr & 0xf) { + case 0x6: + linux_regs->cr_ipsr |= + (__IA64_UL(1) << IA64_PSR_RI_BIT); + break; + case 0xc: + linux_regs->cr_ipsr |= + (__IA64_UL(2) << IA64_PSR_RI_BIT); + break; + } + } + case 'D': + case 'k': + /* + * If we are at the start of arch_kgdb_breakpoint() we must + * step to the next instruction in the bundle, otherwise we + * would just re-execute the breakpoint. + */ + if (linux_regs->cr_iip == bkpt_iip && + (linux_regs->cr_ipsr & IA64_PSR_RI) == 0) + linux_regs->cr_ipsr |= + (__IA64_UL(1) << IA64_PSR_RI_BIT); + + /* clear the trace bit */ + linux_regs->cr_ipsr &= ~IA64_PSR_SS; + atomic_set(&kgdb_cpu_doing_single_step, -1); + + /* set the trace bit if we're stepping */ + if (remcomInBuffer[0] == 's') { + linux_regs->cr_ipsr |= IA64_PSR_SS; + kgdb_single_step = 1; + 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_BREAK: + /* err is break number from ia64_bad_break() */ + if ((args->err >> 12) != (__IA64_BREAK_KGDB >> 12)) + return NOTIFY_DONE; + case DIE_FAULT: + /* err is vector number from ia64_fault() */ + if (args->err == 36) + /*TODO?: single step */; + break; + 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) +{ + bkpt_iip = (u64)dereference_function_descriptor(arch_kgdb_breakpoint); + 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); +} + +/* + * Based on ideas from the kprobe code, we reserve 4096 breakpoint values + * so we can set a breakpoint in slot 1 without having to update across + * the 64-bit boundary in the middle of the instruction bundle. For + * symmetry we do the same even when setting a breakpoint in slot 0 + * or slot 2. This also means we only have to save 23 bits of old + * instruction information. + */ +static void patch(unsigned long addr, void *instr, void *saved) +{ + u64 *b = __va(ia64_tpa(addr & ~0xful)); + u32 newinst; + int idx, bit; + + switch (addr & 0xful) { + case 0x0: + idx = 0; bit = 23; + break; + case 0x6: + idx = 1; bit = 0; + break; + case 0xc: + idx = 1; bit = 41; + break; + default: + printk("kgdb patch - bad address: %lx\n", addr); + return; + } + if (saved) { + u32 old = (b[idx] >> bit) & 0x7ffffful; + memcpy(saved, &old, sizeof(old)); + } + memcpy(&newinst, instr, sizeof(newinst)); + b[idx] = (b[idx] & ~(0x7ffffful << bit)) | ((u64)newinst << bit); + + ia64_fc(&b[idx]); + ia64_sync_i(); + ia64_srlz_i(); +} + +int kgdb_arch_set_breakpoint(unsigned long addr, char *saved_instr) +{ + u32 bkpt = __IA64_BREAK_KGDB >> 12; + + patch(addr, &bkpt, saved_instr); + return 0; +} + +int kgdb_arch_remove_breakpoint(unsigned long addr, char *bundle) +{ + patch(addr, bundle, 0); + return 0; +} + +struct kgdb_arch arch_kgdb_ops = { + .flags = 0, +}; -- To unsubscribe from this list: send the line "unsubscribe linux-ia64" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html
![]() |