diff -Naurp linux-2.6.0-1-kgdb-2.0/arch/x86_64/ia32/ia32entry.S linux-2.6.0-1-kgdb-2.0-x86_64/arch/x86_64/ia32/ia32entry.S --- linux-2.6.0-1-kgdb-2.0/arch/x86_64/ia32/ia32entry.S 2004-01-07 14:52:19.000000000 +0530 +++ linux-2.6.0-1-kgdb-2.0-x86_64/arch/x86_64/ia32/ia32entry.S 2004-01-07 14:53:12.000000000 +0530 @@ -45,6 +45,7 @@ * with the int 0x80 path. */ ENTRY(ia32_cstar_target) + .cfi_startproc swapgs movl %esp,%r8d movq %gs:pda_kernelstack,%rsp @@ -105,6 +106,7 @@ cstar_tracesys: cstar_badarg: movq $-EFAULT,%rax jmp cstar_sysret + .cfi_endproc /* * Emulated IA32 system calls via int 0x80. @@ -128,6 +130,7 @@ cstar_badarg: */ ENTRY(ia32_syscall) + .cfi_startproc swapgs sti movl %eax,%eax @@ -168,6 +171,7 @@ ni_syscall: quiet_ni_syscall: movq $-ENOSYS,%rax ret + .cfi_endproc .macro PTREGSCALL label, func .globl \label @@ -188,6 +192,7 @@ quiet_ni_syscall: PTREGSCALL stub32_rt_sigsuspend, sys_rt_sigsuspend ENTRY(ia32_ptregs_common) + .cfi_startproc popq %r11 SAVE_REST movq %r11, %r15 @@ -198,6 +203,7 @@ ENTRY(ia32_ptregs_common) je int_ret_from_sys_call /* misbalances the call/ret stack. sorry */ pushq %r11 ret + .cfi_endproc .data .align 8 diff -Naurp linux-2.6.0-1-kgdb-2.0/arch/x86_64/Kconfig linux-2.6.0-1-kgdb-2.0-x86_64/arch/x86_64/Kconfig --- linux-2.6.0-1-kgdb-2.0/arch/x86_64/Kconfig 2004-01-07 14:52:19.000000000 +0530 +++ linux-2.6.0-1-kgdb-2.0-x86_64/arch/x86_64/Kconfig 2004-01-07 14:53:12.000000000 +0530 @@ -520,8 +520,43 @@ config IOMMU_LEAK Add a simple leak tracer to the IOMMU code. This is useful when you are debugging a buggy device driver that leaks IOMMU mappings. -#config X86_REMOTE_DEBUG -# bool "kgdb debugging stub" +config KGDB + bool "KGDB: kernel debugging with remote gdb" + depends on DEBUG_KERNEL + select DEBUG_INFO + select FRAME_POINTER + help + If you say Y here, it will be possible to remotely debug the + kernel using gdb. This enlarges your kernel image disk size by + several megabytes and requires a machine with more than 128 MB + RAM to avoid excessive linking time. + Documentation of kernel debugger available at + http://kgdb.sourceforge.net + This is only useful for kernel hackers. If unsure, say N. + +config KGDB_THREAD + bool "KGDB: Thread analysis" + depends on KGDB + help + With thread analysis enabled, gdb can talk to kgdb stub to list + threads and to get stack trace for a thread. This option also enables + some code which helps gdb get exact status of thread. Thread analysis + adds some overhead to schedule and down functions. You can disable + this option if you do not want to compromise on speed. + +config KGDB_CONSOLE + bool "KGDB: Console messages through gdb" + depends on KGDB + help + If you say Y here, console messages will appear through gdb. + Other consoles such as tty or ttyS will continue to work as usual. + +config KGDB_8250 + bool "KGDB: On generic serial port (8250)" + depends on KGDB + help + Uses generic serial port (8250) for kgdb. + endmenu diff -Naurp linux-2.6.0-1-kgdb-2.0/arch/x86_64/kernel/entry.S linux-2.6.0-1-kgdb-2.0-x86_64/arch/x86_64/kernel/entry.S --- linux-2.6.0-1-kgdb-2.0/arch/x86_64/kernel/entry.S 2003-11-24 07:01:45.000000000 +0530 +++ linux-2.6.0-1-kgdb-2.0-x86_64/arch/x86_64/kernel/entry.S 2004-01-07 14:53:12.000000000 +0530 @@ -82,22 +82,53 @@ /* push in order ss, rsp, eflags, cs, rip */ xorq %rax, %rax pushq %rax /* ss */ + .cfi_adjust_cfa_offset 8 pushq %rax /* rsp */ + .cfi_adjust_cfa_offset 8 + .cfi_offset rip,0 pushq $(1<<9) /* eflags - interrupts on */ + .cfi_adjust_cfa_offset 8 pushq $__KERNEL_CS /* cs */ + .cfi_adjust_cfa_offset 8 pushq \child_rip /* rip */ + .cfi_adjust_cfa_offset 8 + .cfi_offset rip,0 pushq %rax /* orig rax */ + .cfi_adjust_cfa_offset 8 .endm .macro UNFAKE_STACK_FRAME addq $8*6, %rsp + .cfi_adjust_cfa_offset -(6*8) .endm + .macro CFI_DEFAULT_STACK + .cfi_adjust_cfa_offset (SS) + .cfi_offset r15,R15-SS + .cfi_offset r14,R14-SS + .cfi_offset r13,R13-SS + .cfi_offset r12,R12-SS + .cfi_offset rbp,RBP-SS + .cfi_offset rbx,RBX-SS + .cfi_offset r11,R11-SS + .cfi_offset r10,R10-SS + .cfi_offset r9,R9-SS + .cfi_offset r8,R8-SS + .cfi_offset rax,RAX-SS + .cfi_offset rcx,RCX-SS + .cfi_offset rdx,RDX-SS + .cfi_offset rsi,RSI-SS + .cfi_offset rdi,RDI-SS + .cfi_offset rsp,RSP-SS + .cfi_offset rip,RIP-SS + .endm /* * A newly forked process directly context switches into this. */ /* rdi: prev */ ENTRY(ret_from_fork) + .cfi_startproc + CFI_DEFAULT_STACK call schedule_tail GET_THREAD_INFO(%rcx) bt $TIF_SYSCALL_TRACE,threadinfo_flags(%rcx) @@ -115,6 +146,7 @@ rff_trace: call syscall_trace GET_THREAD_INFO(%rcx) jmp rff_action + .cfi_endproc /* * System call entry. Upto 6 arguments in registers are supported. @@ -144,6 +176,7 @@ rff_trace: */ ENTRY(system_call) + .cfi_startproc swapgs movq %rsp,%gs:pda_oldrsp movq %gs:pda_kernelstack,%rsp @@ -186,7 +219,7 @@ sysret_careful: jnc sysret_signal sti pushq %rdi - call schedule + call user_schedule popq %rdi jmp sysret_check @@ -255,7 +288,7 @@ int_careful: jnc int_very_careful sti pushq %rdi - call schedule + call user_schedule popq %rdi jmp int_with_check @@ -283,6 +316,7 @@ int_signal: int_restore_rest: RESTORE_REST jmp int_with_check + .cfi_endproc /* * Certain special system calls that need to save a complete full stack frame. @@ -303,7 +337,9 @@ int_restore_rest: PTREGSCALL stub_iopl, sys_iopl ENTRY(ptregscall_common) + .cfi_startproc popq %r11 + .cfi_adjust_cfa_offset -8 SAVE_REST movq %r11, %r15 FIXUP_TOP_OF_STACK %r11 @@ -312,10 +348,14 @@ ENTRY(ptregscall_common) movq %r15, %r11 RESTORE_REST pushq %r11 + .cfi_adjust_cfa_offset 8 ret + .cfi_endproc ENTRY(stub_execve) + .cfi_startproc popq %r11 + .cfi_adjust_cfa_offset -8 SAVE_REST movq %r11, %r15 FIXUP_TOP_OF_STACK %r11 @@ -330,15 +370,18 @@ ENTRY(stub_execve) ret exec_32bit: + .cfi_adjust_cfa_offset REST_SKIP movq %rax,RAX(%rsp) RESTORE_REST jmp int_ret_from_sys_call + .cfi_endproc /* * sigreturn is special because it needs to restore all registers on return. * This cannot be done with SYSRET, so use the IRET return path instead. */ ENTRY(stub_rt_sigreturn) + .cfi_startproc addq $8, %rsp SAVE_REST FIXUP_TOP_OF_STACK %r11 @@ -346,6 +389,7 @@ ENTRY(stub_rt_sigreturn) movq %rax,RAX(%rsp) # fixme, this could be done at the higher layer RESTORE_REST jmp int_ret_from_sys_call + .cfi_endproc /* * Interrupt entry/exit. @@ -357,10 +401,20 @@ ENTRY(stub_rt_sigreturn) /* 0(%rsp): interrupt number */ .macro interrupt func + .cfi_startproc simple + .cfi_def_cfa_offset (SS-ORIG_RAX) + .cfi_offset rsp,(RSP-SS) + .cfi_offset rip,(RIP-SS) cld -#ifdef CONFIG_X86_REMOTE_DEBUG +#ifdef CONFIG_KGDB SAVE_ALL movq %rsp,%rdi + /* + * Setup a stack frame pointer. This allows gdb to trace + * back to the original stack. + */ + movq %rsp,%rbp + .cfi_def_cfa_register rbp #else SAVE_ARGS leaq -ARGOFFSET(%rsp),%rdi # arg1 for handler @@ -382,6 +436,9 @@ ret_from_intr: popq %rdi cli subl $1,%gs:pda_irqcount +#ifdef CONFIG_KGDB + movq RBP(%rdi),%rbp +#endif leaq ARGOFFSET(%rdi),%rsp exit_intr: GET_THREAD_INFO(%rcx) @@ -425,7 +482,7 @@ retint_careful: jnc retint_signal sti pushq %rdi - call schedule + call user_schedule popq %rdi GET_THREAD_INFO(%rcx) cli @@ -465,6 +522,7 @@ retint_kernel: movl $0,threadinfo_preempt_count(%rcx) jmp exit_intr #endif + .cfi_endproc /* * APIC interrupts. @@ -473,6 +531,7 @@ retint_kernel: pushq $\num-256 interrupt \func jmp ret_from_intr + .cfi_endproc .endm #ifdef CONFIG_SMP @@ -518,24 +577,43 @@ ENTRY(spurious_interrupt) * and the exception handler in %rax. */ ENTRY(error_entry) + .cfi_startproc simple + .cfi_def_cfa_offset (SS-RDI) + .cfi_rel_offset rsp,(RSP-RDI) + .cfi_rel_offset rip,(RIP-RDI) /* rdi slot contains rax, oldrax contains error code */ cld subq $14*8,%rsp + .cfi_adjust_cfa_offset (14*8) movq %rsi,13*8(%rsp) + .cfi_rel_offset rsi,RSI movq 14*8(%rsp),%rsi /* load rax from rdi slot */ movq %rdx,12*8(%rsp) + .cfi_rel_offset rdx,RDX movq %rcx,11*8(%rsp) + .cfi_rel_offset rcx,RCX movq %rsi,10*8(%rsp) /* store rax */ + .cfi_rel_offset rax,RAX movq %r8, 9*8(%rsp) + .cfi_rel_offset r8,R8 movq %r9, 8*8(%rsp) + .cfi_rel_offset r9,R9 movq %r10,7*8(%rsp) + .cfi_rel_offset r10,R10 movq %r11,6*8(%rsp) + .cfi_rel_offset r11,R11 movq %rbx,5*8(%rsp) + .cfi_rel_offset rbx,RBX movq %rbp,4*8(%rsp) + .cfi_rel_offset rbp,RBP movq %r12,3*8(%rsp) + .cfi_rel_offset r12,R12 movq %r13,2*8(%rsp) + .cfi_rel_offset r13,R13 movq %r14,1*8(%rsp) + .cfi_rel_offset r14,R14 movq %r15,(%rsp) + .cfi_rel_offset r15,R15 xorl %ebx,%ebx testl $3,CS(%rsp) je error_kernelspace @@ -561,6 +639,7 @@ error_exit: swapgs RESTORE_ARGS 0,8,0 iretq + .cfi_endproc error_kernelspace: incl %ebx @@ -615,6 +694,7 @@ bad_gs: * rdi: fn, rsi: arg, rdx: flags */ ENTRY(kernel_thread) + .cfi_startproc FAKE_STACK_FRAME $child_rip SAVE_ALL @@ -642,6 +722,8 @@ ENTRY(kernel_thread) RESTORE_ALL UNFAKE_STACK_FRAME ret + .cfi_endproc + child_rip: /* @@ -671,6 +753,7 @@ child_rip: * rdi: name, rsi: argv, rdx: envp, fake frame on the stack */ ENTRY(execve) + .cfi_startproc FAKE_STACK_FRAME $0 SAVE_ALL call sys_execve @@ -681,6 +764,7 @@ ENTRY(execve) RESTORE_ARGS UNFAKE_STACK_FRAME ret + .cfi_endproc ENTRY(page_fault) errorentry do_page_fault @@ -692,6 +776,7 @@ ENTRY(simd_coprocessor_error) zeroentry do_simd_coprocessor_error ENTRY(device_not_available) + .cfi_startproc pushq $-1 #error code SAVE_ALL movl $1,%ebx @@ -706,11 +791,13 @@ ENTRY(device_not_available) cmoveq %rcx,%rdx call *%rdx jmp error_exit + .cfi_endproc ENTRY(debug) zeroentry do_debug ENTRY(nmi) + .cfi_startproc pushq $-1 SAVE_ALL /* NMI could happen inside the critical section of a swapgs, @@ -731,6 +818,7 @@ ENTRY(nmi) swapgs 2: RESTORE_ALL 8 iretq + .cfi_endproc ENTRY(int3) zeroentry do_int3 @@ -780,3 +868,38 @@ ENTRY(machine_check) ENTRY(call_debug) zeroentry do_call_debug +#ifdef CONFIG_KGDB_THREAD +ENTRY(kern_schedule) + subq $21*8,%rsp + movq %rax,10*8(%rsp) + + movq %rsp,%rax + addq $21*8,%rax + movq %rax, 19*8(%rsp) + + pushfq + popq 18*8(%rsp) + + movq 21*8(%rsp),%rax + movq %rax,16*8(%rsp) + + movq %rdi,14*8(%rsp) + movq %rsi,13*8(%rsp) + movq %rdx,12*8(%rsp) + movq %rcx,11*8(%rsp) + + movq %r8, 9*8(%rsp) + movq %r9, 8*8(%rsp) + movq %r10,7*8(%rsp) + movq %r11,6*8(%rsp) + movq %rbx,5*8(%rsp) + movq %rbp,4*8(%rsp) + movq %r12,3*8(%rsp) + movq %r13,2*8(%rsp) + movq %r14,1*8(%rsp) + movq %r15,(%rsp) + call kern_do_schedule + movq 10*8(%rsp), %rax + addq $21*8,%rsp + ret +#endif diff -Naurp linux-2.6.0-1-kgdb-2.0/arch/x86_64/kernel/Makefile linux-2.6.0-1-kgdb-2.0-x86_64/arch/x86_64/kernel/Makefile --- linux-2.6.0-1-kgdb-2.0/arch/x86_64/kernel/Makefile 2004-01-07 14:52:19.000000000 +0530 +++ linux-2.6.0-1-kgdb-2.0-x86_64/arch/x86_64/kernel/Makefile 2004-01-07 14:53:12.000000000 +0530 @@ -24,6 +24,7 @@ obj-$(CONFIG_GART_IOMMU) += pci-gart.o a obj-$(CONFIG_DUMMY_IOMMU) += pci-nommu.o pci-dma.o obj-$(CONFIG_MODULES) += module.o +obj-$(CONFIG_KGDB) += x86_64-stub.o obj-y += topology.o diff -Naurp linux-2.6.0-1-kgdb-2.0/arch/x86_64/kernel/nmi.c linux-2.6.0-1-kgdb-2.0-x86_64/arch/x86_64/kernel/nmi.c --- linux-2.6.0-1-kgdb-2.0/arch/x86_64/kernel/nmi.c 2003-11-24 07:01:53.000000000 +0530 +++ linux-2.6.0-1-kgdb-2.0-x86_64/arch/x86_64/kernel/nmi.c 2004-01-07 14:53:12.000000000 +0530 @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -317,13 +318,26 @@ void nmi_watchdog_tick (struct pt_regs * int sum, cpu = safe_smp_processor_id(); sum = read_pda(apic_timer_irqs); - if (last_irq_sums[cpu] == sum) { + if (atomic_read(&debugger_active)) { + + /* + * The machine is in debugger, hold this cpu if already + * not held. + */ + debugger_nmihook(cpu, regs); + alert_counter[cpu] = 0; + + } else if (last_irq_sums[cpu] == sum) { + /* * Ayiee, looks like this CPU is stuck ... * wait a few IRQs (5 seconds) before doing the oops ... */ alert_counter[cpu]++; if (alert_counter[cpu] == 5*nmi_hz) { + + CHK_DEBUGGER(2,SIGSEGV,0,regs,) + if (notify_die(DIE_NMI, "nmi", regs, reason, 2, SIGINT) == NOTIFY_BAD) { alert_counter[cpu] = 0; return; diff -Naurp linux-2.6.0-1-kgdb-2.0/arch/x86_64/kernel/traps.c linux-2.6.0-1-kgdb-2.0-x86_64/arch/x86_64/kernel/traps.c --- linux-2.6.0-1-kgdb-2.0/arch/x86_64/kernel/traps.c 2004-01-07 14:52:20.000000000 +0530 +++ linux-2.6.0-1-kgdb-2.0-x86_64/arch/x86_64/kernel/traps.c 2004-01-07 14:53:12.000000000 +0530 @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -363,6 +364,7 @@ void __die(const char * str, struct pt_r void die(const char * str, struct pt_regs * regs, long err) { + CHK_DEBUGGER(1,SIGTRAP,err,regs,) oops_begin(); handle_BUG(regs); __die(str, regs, err); @@ -438,6 +440,7 @@ static void do_trap(int trapnr, int sign #define DO_ERROR(trapnr, signr, str, name) \ asmlinkage void do_##name(struct pt_regs * regs, long error_code) \ { \ + CHK_DEBUGGER(1,SIGTRAP,error_code,regs,return) \ if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) == NOTIFY_BAD) \ return; \ do_trap(trapnr, signr, str, regs, error_code, NULL); \ @@ -616,7 +619,7 @@ asmlinkage void do_debug(struct pt_regs tsk->thread.debugreg6 = condition; /* Mask out spurious TF errors due to lazy TF clearing */ - if (condition & DR_STEP) { + if (condition & DR_STEP && !debugger_step) { /* * The TF error should be masked out only if the current * process is not traced and if the TRAP flag has been set @@ -645,6 +648,7 @@ asmlinkage void do_debug(struct pt_regs force_sig_info(SIGTRAP, &info, tsk); clear_dr7: asm volatile("movq %0,%%db7"::"r"(0UL)); + CHK_DEBUGGER(1,SIGTRAP,error_code,regs,return) notify_die(DIE_DEBUG, "debug", regs, error_code, 1, SIGTRAP); return; diff -Naurp linux-2.6.0-1-kgdb-2.0/arch/x86_64/kernel/vmlinux.lds.S linux-2.6.0-1-kgdb-2.0-x86_64/arch/x86_64/kernel/vmlinux.lds.S --- linux-2.6.0-1-kgdb-2.0/arch/x86_64/kernel/vmlinux.lds.S 2004-01-07 14:52:20.000000000 +0530 +++ linux-2.6.0-1-kgdb-2.0-x86_64/arch/x86_64/kernel/vmlinux.lds.S 2004-01-07 14:53:12.000000000 +0530 @@ -143,7 +143,7 @@ SECTIONS /* Sections to be discarded */ /DISCARD/ : { *(.exitcall.exit) - *(.eh_frame) + /* *(.eh_frame) */ } /* DWARF 2 */ diff -Naurp linux-2.6.0-1-kgdb-2.0/arch/x86_64/kernel/x86_64-stub.c linux-2.6.0-1-kgdb-2.0-x86_64/arch/x86_64/kernel/x86_64-stub.c --- linux-2.6.0-1-kgdb-2.0/arch/x86_64/kernel/x86_64-stub.c 1970-01-01 05:30:00.000000000 +0530 +++ linux-2.6.0-1-kgdb-2.0-x86_64/arch/x86_64/kernel/x86_64-stub.c 2004-01-07 14:53:12.000000000 +0530 @@ -0,0 +1,541 @@ +/* + * + * 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 + * Copyright (C) 2000-2001 VERITAS Software Corporation. + * Copyright (C) 2002 Andi Kleen, SuSE Labs + */ +/**************************************************************************** + * Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $ + * + * Module name: remcom.c $ + * Revision: 1.34 $ + * Date: 91/03/09 12:29:49 $ + * Contributor: Lake Stevens Instrument Division$ + * + * Description: low level support for gdb debugger. $ + * + * Considerations: only works on target hardware $ + * + * Written by: Glenn Engel $ + * Updated by: Amit Kale + * ModuleState: Experimental $ + * + * NOTES: See Below $ + * + * Modified for 386 by Jim Kingdon, Cygnus Support. + * Origianl kgdb, compatibility with 2.1.xx kernel by David Grothe + * Integrated into 2.2.5 kernel by Tigran Aivazian + * thread support, + * support for multiple processors, + * support for ia-32(x86) hardware debugging, + * Console support, + * handling nmi watchdog + * Amit S. Kale ( amitkale@emsyssoft.com ) + * + * X86_64 changes from Andi Kleen's patch merged by Jim Houston + * + * + * To enable debugger support, two things need to happen. One, a + * call to set_debug_traps() is necessary in order to allow any breakpoints + * or error conditions to be properly intercepted and reported to gdb. + * Two, a breakpoint needs to be generated to begin communication. This + * is most easily accomplished by a call to breakpoint(). Breakpoint() + * simulates a breakpoint by executing an int 3. + * + ************* + * + * The following gdb commands are supported: + * + * command function Return value + * + * g return the value of the CPU registers hex data or ENN + * G set the value of the CPU registers OK or ENN + * + * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN + * + * c Resume at current address SNN ( signal NN) + * cAA..AA Continue at address AA..AA SNN + * + * s Step one instruction SNN + * sAA..AA Step one instruction from AA..AA SNN + * + * k kill + * + * ? What was the last sigval ? SNN (signal NN) + * + * All commands and responses are sent with a packet which includes a + * checksum. A packet consists of + * + * $#. + * + * where + * :: + * :: < two hex digits computed as modulo 256 sum of > + * + * When a packet is received, it is first acknowledged with either '+' or '-'. + * '+' indicates a successful transfer. '-' indicates a failed transfer. + * + * Example: + * + * Host: Reply: + * $m0,10#2a +$00010203040506070809101112131415#42 + * + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include /* for linux pt_regs struct */ +#include +#ifdef CONFIG_GDB_CONSOLE +#include +#endif +#include +#include + +/* Put the error code here just in case the user cares. */ +int gdb_x86_64errcode; +/* Likewise, the vector number here (since GDB only gets the signal + number through the usual means, and that's not very specific). */ +int gdb_x86_64vector = -1; + +#if KGDB_MAX_NO_CPUS != 8 +#error change the definition of slavecpulocks +#endif + +static void x86_64_regs_to_gdb_regs(unsigned long *gdb_regs, + struct pt_regs *regs) +{ + gdb_regs[_RAX] = regs->rax; + gdb_regs[_RBX] = regs->rbx; + gdb_regs[_RCX] = regs->rcx; + gdb_regs[_RDX] = regs->rdx; + gdb_regs[_RSI] = regs->rsi; + gdb_regs[_RDI] = regs->rdi; + gdb_regs[_RBP] = regs->rbp; + gdb_regs[ _PS] = regs->eflags; + gdb_regs[ _PC] = regs->rip; + gdb_regs[ _R8] = regs->r8; + gdb_regs[ _R9] = regs->r9; + gdb_regs[_R10] = regs->r10; + gdb_regs[_R11] = regs->r11; + gdb_regs[_R12] = regs->r12; + gdb_regs[_R13] = regs->r13; + gdb_regs[_R14] = regs->r14; + gdb_regs[_R15] = regs->r15; + gdb_regs[_RSP] = regs->rsp; +} + +struct task_struct *__switch_to(struct task_struct *prev_p, + struct task_struct *next_p); +static void x86_64_sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, + struct task_struct *p) +{ + gdb_regs[_RAX] = 0; + gdb_regs[_RBX] = 0; + gdb_regs[_RCX] = 0; + gdb_regs[_RDX] = 0; + gdb_regs[_RSI] = 0; + gdb_regs[_RDI] = 0; + gdb_regs[_RBP] = 0; + gdb_regs[ _PS] = 0; + gdb_regs[ _PC] = (unsigned long) __switch_to; + gdb_regs[ _R8] = 0; + gdb_regs[ _R9] = 0; + gdb_regs[_R10] = 0; + gdb_regs[_R11] = 0; + gdb_regs[_R12] = 0; + gdb_regs[_R13] = 0; + gdb_regs[_R14] = 0; + gdb_regs[_R15] = 0; + gdb_regs[_RSP] = p->thread.rsp; +} + +static void x86_64_gdb_regs_to_regs(unsigned long *gdb_regs, + struct pt_regs *regs) +{ + regs->rax = gdb_regs[_RAX] ; + regs->rbx = gdb_regs[_RBX] ; + regs->rcx = gdb_regs[_RCX] ; + regs->rdx = gdb_regs[_RDX] ; + regs->rsi = gdb_regs[_RSI] ; + regs->rdi = gdb_regs[_RDI] ; + regs->rbp = gdb_regs[_RBP] ; + regs->eflags= gdb_regs[ _PS] ; + regs->rip = gdb_regs[ _PC] ; + regs->r8 = gdb_regs[ _R8] ; + regs->r9 = gdb_regs[ _R9] ; + regs->r10 = gdb_regs[ _R10] ; + regs->r11 = gdb_regs[ _R11] ; + regs->r12 = gdb_regs[ _R12] ; + regs->r13 = gdb_regs[ _R13] ; + regs->r14 = gdb_regs[ _R14] ; + regs->r15 = gdb_regs[ _R15] ; +#if 0 /* can't change these */ + regs->rsp = gdb_regs[_RSP] ; + regs->ss = gdb_regs[ _SS] ; + regs->fs = gdb_regs[ _FS] ; + regs->gs = gdb_regs[ _GS] ; +#endif + +} /* gdb_regs_to_regs */ + + +struct hw_breakpoint { + unsigned enabled; + unsigned type; + unsigned len; + unsigned long addr; +} breakinfo[4] = { { +enabled:0}, { +enabled:0}, { +enabled:0}, { +enabled:0}}; + +void x86_64_correct_hw_break(void) +{ + int breakno; + int correctit; + int breakbit; + unsigned long dr7; + + asm volatile ("movq %%db7, %0\n":"=r" (dr7) :); + do { + unsigned long addr0, addr1, addr2, addr3; + asm volatile ("movq %%db0, %0\n" + "movq %%db1, %1\n" + "movq %%db2, %2\n" + "movq %%db3, %3\n":"=r" (addr0), "=r"(addr1), + "=r"(addr2), "=r"(addr3):); + } while (0); + correctit = 0; + for (breakno = 0; breakno < 3; breakno++) { + breakbit = 2 << (breakno << 1); + if (!(dr7 & breakbit) && breakinfo[breakno].enabled) { + correctit = 1; + dr7 |= breakbit; + dr7 &= ~(0xf0000 << (breakno << 2)); + dr7 |= (((breakinfo[breakno].len << 2) | + breakinfo[breakno].type) << 16) << + (breakno << 2); + switch (breakno) { + case 0: + asm volatile ("movq %0, %%dr0\n"::"r" + (breakinfo[breakno].addr)); + break; + + case 1: + asm volatile ("movq %0, %%dr1\n"::"r" + (breakinfo[breakno].addr)); + break; + + case 2: + asm volatile ("movq %0, %%dr2\n"::"r" + (breakinfo[breakno].addr)); + break; + + case 3: + asm volatile ("movq %0, %%dr3\n"::"r" + (breakinfo[breakno].addr)); + break; + } + } else if ((dr7 & breakbit) && !breakinfo[breakno].enabled) { + correctit = 1; + dr7 &= ~breakbit; + dr7 &= ~(0xf0000 << (breakno << 2)); + } + } + if (correctit) { + asm volatile ("movq %0, %%db7\n"::"r" (dr7)); + } +} + +int x86_64_remove_hw_break(unsigned long addr, int type) +{ + int i, idx = -1; + for (i = 0; i < 4; i ++) { + if (breakinfo[i].addr == addr && breakinfo[i].enabled) { + idx = i; + break; + } + } + if (idx == -1) + return -1; + + breakinfo[idx].enabled = 0; + return 0; +} + +int x86_64_set_hw_break(unsigned long addr, int type) +{ + int i, idx = -1; + for (i = 0; i < 4; i ++) { + if (!breakinfo[i].enabled) { + idx = i; + break; + } + } + if (idx == -1) + return -1; + + breakinfo[idx].enabled = 1; + breakinfo[idx].type = type; + breakinfo[idx].len = 1; + breakinfo[idx].addr = addr; + return 0; +} + +int remove_hw_break(unsigned breakno) +{ + if (!breakinfo[breakno].enabled) { + return -1; + } + breakinfo[breakno].enabled = 0; + return 0; +} + +int set_hw_break(unsigned breakno, + unsigned type, unsigned len, unsigned addr) +{ + if (breakinfo[breakno].enabled) { + return -1; + } + breakinfo[breakno].enabled = 1; + breakinfo[breakno].type = type; + breakinfo[breakno].len = len; + breakinfo[breakno].addr = addr; + return 0; +} + +static void x86_64_printexceptioninfo(int exceptionNo, int errorcode, char *buffer) +{ + unsigned long dr6; + int i; + switch (exceptionNo) { + case 1: /* debug exception */ + break; + case 3: /* breakpoint */ + sprintf(buffer, "Software breakpoint"); + return; + default: + sprintf(buffer, "Details not available"); + return; + } + asm volatile ("movq %%db6, %0\n":"=r" (dr6) + :); + if (dr6 & 0x4000) { + sprintf(buffer, "Single step"); + return; + } + for (i = 0; i < 4; ++i) { + if (dr6 & (1 << i)) { + sprintf(buffer, "Hardware breakpoint %d", i); + return; + } + } + sprintf(buffer, "Unknown trap"); + return; +} + +static void x86_64_disable_hw_debug(struct pt_regs *regs) +{ + /* Disable hardware debugging while we are in kgdb */ + asm volatile("movq %0,%%db7": /* no output */ : "r"(0UL)); +} + +static void x86_64_post_master_code(struct pt_regs *regs, int eVector, int err_code) +{ + /* Master processor is completely in the debugger */ + gdb_x86_64vector = eVector; + gdb_x86_64errcode = err_code; +} +static int x86_64_handle_exception(int exceptionVector, int signo, int err_code, + char *remcomInBuffer, char *remcomOutBuffer, + struct pt_regs *linux_regs) +{ + unsigned long addr, length; + unsigned long breakno, breaktype; + char *ptr; + int newPC; + unsigned long dr6; + + switch (remcomInBuffer[0]) { + case 'c': + case 's': + if (kgdb_contthread && kgdb_contthread != current) { + strcpy(remcomOutBuffer, "E00"); + break; + } + + kgdb_contthread = NULL; + + /* try to read optional parameter, pc unchanged if no parm */ + ptr = &remcomInBuffer[1]; + if (kgdb_hexToLong(&ptr, &addr)) { + linux_regs->rip = addr; + } + newPC = linux_regs->rip; + + /* clear the trace bit */ + linux_regs->eflags &= 0xfffffeff; + + /* set the trace bit if we're stepping */ + if (remcomInBuffer[0] == 's') { + linux_regs->eflags |= 0x100; + debugger_step = 1; + } + + asm volatile ("movq %%db6, %0\n" : "=r" (dr6)); + if (!(dr6 & 0x4000)) { + for (breakno = 0; breakno < 4; ++breakno) { + if (dr6 & (1 << breakno)) { + if (breakinfo[breakno].type == 0) { + /* Set restore flag */ + linux_regs->eflags |= 0x10000; + break; + } + } + } + } + x86_64_correct_hw_break(); + asm volatile ("movq %0, %%db6\n"::"r" (0UL)); + + return (0); + + case 'Y': + ptr = &remcomInBuffer[1]; + kgdb_hexToLong(&ptr, &breakno); + ptr++; + kgdb_hexToLong(&ptr, &breaktype); + ptr++; + kgdb_hexToLong(&ptr, &length); + ptr++; + kgdb_hexToLong(&ptr, &addr); + if (set_hw_break(breakno & 0x3, breaktype & 0x3, + length & 0x3, addr) == 0) { + strcpy(remcomOutBuffer, "OK"); + } else { + strcpy(remcomOutBuffer, "ERROR"); + } + break; + + /* Remove hardware breakpoint */ + case 'y': + ptr = &remcomInBuffer[1]; + kgdb_hexToLong(&ptr, &breakno); + if (remove_hw_break(breakno & 0x3) == 0) { + strcpy(remcomOutBuffer, "OK"); + } else { + strcpy(remcomOutBuffer, "ERROR"); + } + break; + + } /* switch */ + return -1; /* this means that we do not want to exit from the handler */ +} + +static int x86_64_kgdb_init(void) +{ + return 0; +} + +static struct pt_regs *in_interrupt_stack(unsigned long rsp, int cpu) +{ + struct pt_regs *regs; + unsigned long end = (unsigned long) cpu_pda[cpu].irqstackptr; + if (rsp <= end && + rsp >= end - IRQSTACKSIZE + 8) { + regs = *(((struct pt_regs **)end) -1); + return regs; + } + return NULL; +} + +static struct pt_regs *in_exception_stack(unsigned long rsp, int cpu) +{ + int i; + for (i = 0; i < N_EXCEPTION_STACKS; i++) + if (rsp >= init_tss[cpu].ist[i] && + rsp <= init_tss[cpu].ist[i] + EXCEPTION_STKSZ) { + struct pt_regs *r = (void *)init_tss[cpu].ist[i] + EXCEPTION_STKSZ; + return r-1; + } + return NULL; +} + +static void x86_64_shadowinfo(struct pt_regs *regs, char *buffer, unsigned threadid) +{ + static char intr_desc[] = "Stack at interrupt entrypoint"; + static char exc_desc[] = "Stack at exception entrypoint"; + struct pt_regs *stregs; + int cpu = hard_smp_processor_id(); + + if ((stregs = in_interrupt_stack(regs->rsp, cpu))) { + kgdb_mem2hex(intr_desc, buffer, strlen(intr_desc), 0); + } else if ((stregs = in_exception_stack(regs->rsp, cpu))) { + kgdb_mem2hex(exc_desc, buffer, strlen(exc_desc), 0); + } +} + +static struct task_struct *x86_64_get_shadow_thread(struct pt_regs *regs, int threadid) +{ + struct pt_regs *stregs; + int cpu = hard_smp_processor_id(); + + if ((stregs = in_interrupt_stack(regs->rsp, cpu))) { + return current; + } else if ((stregs = in_exception_stack(regs->rsp, cpu))) { + return current; + } + return NULL; +} + +static struct pt_regs *x86_64_shadow_regs(struct pt_regs *regs, int threadid) +{ + struct pt_regs *stregs; + int cpu = hard_smp_processor_id(); + + if ((stregs = in_interrupt_stack(regs->rsp, cpu))) { + return stregs; + } else if ((stregs = in_exception_stack(regs->rsp, cpu))) { + return stregs; + } + return NULL; +} +struct kgdb_arch arch_kgdb_ops = { + .gdb_bpt_instr = {0xcc}, + .flags = KGDB_HW_BREAKPOINT, + .shadowth = 1, + .kgdb_init = x86_64_kgdb_init, + .regs_to_gdb_regs = x86_64_regs_to_gdb_regs, + .sleeping_thread_to_gdb_regs = x86_64_sleeping_thread_to_gdb_regs, + .gdb_regs_to_regs = x86_64_gdb_regs_to_regs, + .printexceptioninfo = x86_64_printexceptioninfo, + .disable_hw_debug = x86_64_disable_hw_debug, + .post_master_code = x86_64_post_master_code, + .handle_exception = x86_64_handle_exception, + .set_break = x86_64_set_hw_break, + .remove_break = x86_64_remove_hw_break, + .correct_hw_break = x86_64_correct_hw_break, + .shadowinfo = x86_64_shadowinfo, + .get_shadow_thread = x86_64_get_shadow_thread, + .shadow_regs = x86_64_shadow_regs, +}; diff -Naurp linux-2.6.0-1-kgdb-2.0/arch/x86_64/lib/thunk.S linux-2.6.0-1-kgdb-2.0-x86_64/arch/x86_64/lib/thunk.S --- linux-2.6.0-1-kgdb-2.0/arch/x86_64/lib/thunk.S 2003-11-24 07:01:53.000000000 +0530 +++ linux-2.6.0-1-kgdb-2.0-x86_64/arch/x86_64/lib/thunk.S 2004-01-07 14:53:12.000000000 +0530 @@ -15,18 +15,22 @@ .macro thunk name,func .globl \name \name: + .cfi_startproc SAVE_ARGS call \func jmp restore + .cfi_endproc .endm /* rdi: arg1 ... normal C conventions. rax is passed from C. */ .macro thunk_retrax name,func .globl \name \name: + .cfi_startproc SAVE_ARGS call \func jmp restore_norax + .cfi_endproc .endm @@ -43,13 +47,20 @@ thunk_retrax __down_failed_trylock,__down_trylock thunk __up_wakeup,__up + /* SAVE_ARGS below is used only for the .cfi directives it contains. */ + .cfi_startproc + SAVE_ARGS restore: RESTORE_ARGS ret + .cfi_endproc + .cfi_startproc + SAVE_ARGS restore_norax: RESTORE_ARGS 1 ret + .cfi_endproc #ifdef CONFIG_SMP /* Support for read/write spinlocks. */ diff -Naurp linux-2.6.0-1-kgdb-2.0/arch/x86_64/mm/fault.c linux-2.6.0-1-kgdb-2.0-x86_64/arch/x86_64/mm/fault.c --- linux-2.6.0-1-kgdb-2.0/arch/x86_64/mm/fault.c 2004-01-07 14:52:20.000000000 +0530 +++ linux-2.6.0-1-kgdb-2.0-x86_64/arch/x86_64/mm/fault.c 2004-01-07 14:53:12.000000000 +0530 @@ -23,6 +23,7 @@ #include /* For unblank_screen() */ #include #include +#include #include #include @@ -248,6 +249,11 @@ asmlinkage void do_page_fault(struct pt_ return; again: + if (debugger_memerr_expected) { + /* This fault was caused by memory access through a debugger. + * Don't handle it like user accesses */ + goto no_context; + } down_read(&mm->mmap_sem); vma = find_vma(mm, address); @@ -362,6 +368,7 @@ no_context: if (is_prefetch(regs, address)) return; + CHK_DEBUGGER(14, SIGSEGV,error_code, regs,) /* * Oops. The kernel tried to access some bad page. We'll have to diff -Naurp linux-2.6.0-1-kgdb-2.0/include/asm-x86_64/calling.h linux-2.6.0-1-kgdb-2.0-x86_64/include/asm-x86_64/calling.h --- linux-2.6.0-1-kgdb-2.0/include/asm-x86_64/calling.h 2004-01-07 14:52:20.000000000 +0530 +++ linux-2.6.0-1-kgdb-2.0-x86_64/include/asm-x86_64/calling.h 2004-01-07 14:53:12.000000000 +0530 @@ -31,20 +31,36 @@ #define ARGOFFSET R11 #define SWFRAME ORIG_RAX + + + .macro SAVE_ARGS addskip=0,norcx=0 + subq $9*8+\addskip,%rsp +// XCFI_ADJUST_CFA_OFFSET 9*8+\addskip + .cfi_adjust_cfa_offset 9*8+\addskip movq %rdi,8*8(%rsp) +// XCFI_OFFSET rdi,8*8-(9*8+\addskip) + .cfi_offset rdi,8*8-(9*8+\addskip) movq %rsi,7*8(%rsp) + .cfi_offset rsi,7*8-(9*8+\addskip) movq %rdx,6*8(%rsp) + .cfi_offset rdx,6*8-(9*8+\addskip) .if \norcx .else movq %rcx,5*8(%rsp) + .cfi_offset rcx,5*8-(9*8+\addskip) .endif movq %rax,4*8(%rsp) + .cfi_offset rax,4*8-(9*8+\addskip) movq %r8,3*8(%rsp) + .cfi_offset r8,3*8-(9*8+\addskip) movq %r9,2*8(%rsp) + .cfi_offset r9,2*8-(9*8+\addskip) movq %r10,1*8(%rsp) + .cfi_offset r10,1*8-(9*8+\addskip) movq %r11,(%rsp) + .cfi_offset r11,-(9*8+\addskip) .endm #define ARG_SKIP 9*8 @@ -69,6 +85,7 @@ movq 8*8(%rsp),%rdi .if ARG_SKIP+\addskip > 0 addq $ARG_SKIP+\addskip,%rsp + .cfi_adjust_cfa_offset -(ARG_SKIP+\addskip) .endif .endm @@ -87,12 +104,19 @@ #define REST_SKIP 6*8 .macro SAVE_REST subq $REST_SKIP,%rsp + .cfi_adjust_cfa_offset REST_SKIP movq %rbx,5*8(%rsp) + .cfi_offset rbx,5*8-(REST_SKIP) movq %rbp,4*8(%rsp) + .cfi_offset rbp,4*8-(REST_SKIP) movq %r12,3*8(%rsp) + .cfi_offset r12,3*8-(REST_SKIP) movq %r13,2*8(%rsp) + .cfi_offset r13,2*8-(REST_SKIP) movq %r14,1*8(%rsp) + .cfi_offset r14,1*8-(REST_SKIP) movq %r15,(%rsp) + .cfi_offset r15,0*8-(REST_SKIP) .endm .macro RESTORE_REST @@ -103,6 +127,7 @@ movq 4*8(%rsp),%rbp movq 5*8(%rsp),%rbx addq $REST_SKIP,%rsp + .cfi_adjust_cfa_offset -(REST_SKIP) .endm .macro SAVE_ALL diff -Naurp linux-2.6.0-1-kgdb-2.0/include/asm-x86_64/dwarf2.h linux-2.6.0-1-kgdb-2.0-x86_64/include/asm-x86_64/dwarf2.h --- linux-2.6.0-1-kgdb-2.0/include/asm-x86_64/dwarf2.h 2003-11-24 07:00:56.000000000 +0530 +++ linux-2.6.0-1-kgdb-2.0-x86_64/include/asm-x86_64/dwarf2.h 2004-01-07 14:53:12.000000000 +0530 @@ -14,8 +14,10 @@ away for older version. */ +#define CONFIG_CFI_BINUTILS 1 #ifdef CONFIG_CFI_BINUTILS +#if 0 #define CFI_STARTPROC .cfi_startproc #define CFI_ENDPROC .cfi_endproc #define CFI_DEF_CFA .cfi_def_cfa @@ -24,6 +26,16 @@ #define CFI_ADJUST_CFA_OFFSET .cfi_adjust_cfa_offset #define CFI_OFFSET .cfi_offset #define CFI_REL_OFFSET .cfi_rel_offset +#else + .macro CFI_STARTPROC ; .cfi_startproc ; .endm + .macro CFI_ENDPROC ; .cfi_endproc ; .endm + .macro CFI_DEF_CFA a,b ; .cfi_def_cfa \a,\b ; .endm + .macro CFI_DEF_CFA_REGISTER a ; .cfi_def_cfa_register \a ; .endm + .macro CFI_DEF_CFA_OFFSET a ; .cfi_def_cfa_offset \a; .endm + .macro CFI_ADJUST_CFA_OFFSET a ; .cfi_adjust_cfa_offset \a ; .endm + .macro CFI_OFFSET a,b ; .cfi_offset \a,\b; .endm + .macro CFI_REL_OFFSET a,b ; .cfi_rel_offset \a,\b; .endm +#endif #else diff -Naurp linux-2.6.0-1-kgdb-2.0/include/asm-x86_64/kgdb.h linux-2.6.0-1-kgdb-2.0-x86_64/include/asm-x86_64/kgdb.h --- linux-2.6.0-1-kgdb-2.0/include/asm-x86_64/kgdb.h 1970-01-01 05:30:00.000000000 +0530 +++ linux-2.6.0-1-kgdb-2.0-x86_64/include/asm-x86_64/kgdb.h 2004-01-07 14:53:12.000000000 +0530 @@ -0,0 +1,53 @@ +#ifndef _ASM_KGDB_H_ +#define _ASM_KGDB_H_ + +/* + * Copyright (C) 2001-2004 Amit S. Kale + */ + +#include + +/* gdb locks */ +#define KGDB_MAX_NO_CPUS 8 + +/************************************************************************/ +/* 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. + */ + +enum regnames {_RAX, + _RDX, + _RCX, + _RBX, + _RSI, + _RDI, + _RBP, + _RSP, + _R8, + _R9, + _R10, + _R11, + _R12, + _R13, + _R14, + _R15, + _PC, + _PS, + _LASTREG=_PS }; + +/* Number of bytes of registers. */ +#define NUMREGBYTES (_LASTREG*8) + +#define BREAKPOINT() asm(" int $3"); +#define BREAK_INSTR_SIZE 1 + +#endif /* _ASM_KGDB_H_ */ diff -Naurp linux-2.6.0-1-kgdb-2.0/include/asm-x86_64/processor.h linux-2.6.0-1-kgdb-2.0-x86_64/include/asm-x86_64/processor.h --- linux-2.6.0-1-kgdb-2.0/include/asm-x86_64/processor.h 2004-01-07 14:52:20.000000000 +0530 +++ linux-2.6.0-1-kgdb-2.0-x86_64/include/asm-x86_64/processor.h 2004-01-07 14:53:12.000000000 +0530 @@ -252,6 +252,7 @@ struct thread_struct { unsigned long *io_bitmap_ptr; /* cached TLS descriptors. */ u64 tls_array[GDT_ENTRY_TLS_ENTRIES]; + void *debuggerinfo; }; #define INIT_THREAD {} diff -Naurp linux-2.6.0-1-kgdb-2.0/include/asm-x86_64/system.h linux-2.6.0-1-kgdb-2.0-x86_64/include/asm-x86_64/system.h --- linux-2.6.0-1-kgdb-2.0/include/asm-x86_64/system.h 2004-01-07 14:52:20.000000000 +0530 +++ linux-2.6.0-1-kgdb-2.0-x86_64/include/asm-x86_64/system.h 2004-01-07 14:53:12.000000000 +0530 @@ -19,7 +19,7 @@ #define __SAVE(reg,offset) "movq %%" #reg ",(14-" #offset ")*8(%%rsp)\n\t" #define __RESTORE(reg,offset) "movq (14-" #offset ")*8(%%rsp),%%" #reg "\n\t" -#ifdef CONFIG_X86_REMOTE_DEBUG +/* #ifdef CONFIG_KGDB */ /* full frame for the debug stub */ /* Should be replaced with a dwarf2 cie/fde description, then gdb could @@ -42,6 +42,7 @@ struct save_context_frame { unsigned long flags; }; +#if 0 #define SAVE_CONTEXT \ "pushfq\n\t" \ "subq $14*8,%%rsp\n\t" \