Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755452AbYAQOci (ORCPT ); Thu, 17 Jan 2008 09:32:38 -0500 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1751440AbYAQOc3 (ORCPT ); Thu, 17 Jan 2008 09:32:29 -0500 Received: from gecko.sbs.de ([194.138.37.40]:18790 "EHLO gecko.sbs.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752564AbYAQOc1 (ORCPT ); Thu, 17 Jan 2008 09:32:27 -0500 Message-ID: <478F66F6.40107@siemens.com> Date: Thu, 17 Jan 2008 15:32:22 +0100 From: Jan Kiszka User-Agent: Thunderbird 2.0.0.9 (X11/20070801) MIME-Version: 1.0 To: Jason Wessel CC: Linux Kernel Mailing List Subject: [PATCH] kgdb:unify x86-kgdb Content-Type: text/plain; charset=ISO-8859-15 Content-Transfer-Encoding: 7bit Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 39188 Lines: 1530 diff -up arch/x86/kernel/kgdb_32.c arch/x86/kernel/kgdb_64.c screamed for unification. Here it is. Signed-off-by: Jan Kiszka --- arch/x86/kernel/Makefile_32 | 2 arch/x86/kernel/Makefile_64 | 2 arch/x86/kernel/kgdb.c | 561 ++++++++++++++++++++++++++++++++++++++++++++ arch/x86/kernel/kgdb_32.c | 414 -------------------------------- arch/x86/kernel/kgdb_64.c | 495 -------------------------------------- 5 files changed, 563 insertions(+), 911 deletions(-) Index: b/arch/x86/kernel/Makefile_32 =================================================================== --- a/arch/x86/kernel/Makefile_32 +++ b/arch/x86/kernel/Makefile_32 @@ -37,7 +37,7 @@ obj-y += sysenter_32.o vsyscall_32.o obj-$(CONFIG_ACPI_SRAT) += srat_32.o obj-$(CONFIG_EFI) += efi_32.o efi_stub_32.o obj-$(CONFIG_DOUBLEFAULT) += doublefault_32.o -obj-$(CONFIG_KGDB) += kgdb_32.o kgdb-jmp_32.o +obj-$(CONFIG_KGDB) += kgdb.o kgdb-jmp_32.o obj-$(CONFIG_VM86) += vm86_32.o obj-$(CONFIG_EARLY_PRINTK) += early_printk.o obj-$(CONFIG_HPET_TIMER) += hpet.o Index: b/arch/x86/kernel/Makefile_64 =================================================================== --- a/arch/x86/kernel/Makefile_64 +++ b/arch/x86/kernel/Makefile_64 @@ -31,7 +31,7 @@ obj-$(CONFIG_GART_IOMMU) += pci-gart_64. obj-$(CONFIG_CALGARY_IOMMU) += pci-calgary_64.o tce_64.o obj-$(CONFIG_SWIOTLB) += pci-swiotlb_64.o obj-$(CONFIG_KPROBES) += kprobes_64.o -obj-$(CONFIG_KGDB) += kgdb_64.o kgdb-jmp_64.o +obj-$(CONFIG_KGDB) += kgdb.o kgdb-jmp_64.o obj-$(CONFIG_X86_PM_TIMER) += pmtimer_64.o obj-$(CONFIG_X86_VSMP) += vsmp_64.o obj-$(CONFIG_K8_NB) += k8.o Index: b/arch/x86/kernel/kgdb.c =================================================================== --- /dev/null +++ b/arch/x86/kernel/kgdb.c @@ -0,0 +1,561 @@ +/* + * arch/x86/kernel/kgdb.c + * + * Copyright (C) 2000-2001 VERITAS Software Corporation. + * Copyright (C) 2002 Andi Kleen, SuSE Labs + * Copyright (C) 2004 Amit S. Kale + * Copyright (C) 2004 LinSysSoft Technologies Pvt. Ltd. + * Copyright (C) 2007 Jason Wessel, Wind River Systems, Inc. + * Copyright (C) 2007 MontaVista Software, Inc. + * + * + * Contributors at various stages not listed above: + * Lake Stevens Instrument Division (Glenn Engel) + * Tom Rini + * Jim Kingdon, Cygnus Support. + * David Grothe + * Tigran Aivazian + * Jim Houston + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include /* for linux pt_regs struct */ +#include +#include +#include +#ifdef CONFIG_X86_32 +#include +#else /* !CONFIG_X86_32 */ +#include +#endif /* !CONFIG_X86_32 */ + +/* Put the error code here just in case the user cares. */ +int gdb_x86_errcode; +/* 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_vector = -1; + +#ifdef CONFIG_X86_32 +void regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs) +{ + gdb_regs[_EAX] = regs->eax; + gdb_regs[_EBX] = regs->ebx; + gdb_regs[_ECX] = regs->ecx; + gdb_regs[_EDX] = regs->edx; + gdb_regs[_ESI] = regs->esi; + gdb_regs[_EDI] = regs->edi; + gdb_regs[_EBP] = regs->ebp; + gdb_regs[_DS] = regs->xds; + gdb_regs[_ES] = regs->xes; + gdb_regs[_PS] = regs->eflags; + gdb_regs[_CS] = regs->xcs; + gdb_regs[_PC] = regs->eip; + gdb_regs[_ESP] = (int)(®s->esp); + gdb_regs[_SS] = __KERNEL_DS; + gdb_regs[_FS] = 0xFFFF; + gdb_regs[_GS] = 0xFFFF; +} + +/* + * Extracts ebp, esp and eip values understandable by gdb from the values + * saved by switch_to. + * thread.esp points to ebp. flags and ebp are pushed in switch_to hence esp + * prior to entering switch_to is 8 greater then the value that is saved. + * If switch_to changes, change following code appropriately. + */ +void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p) +{ + gdb_regs[_EAX] = 0; + gdb_regs[_EBX] = 0; + gdb_regs[_ECX] = 0; + gdb_regs[_EDX] = 0; + gdb_regs[_ESI] = 0; + gdb_regs[_EDI] = 0; + gdb_regs[_EBP] = *(unsigned long *)p->thread.esp; + gdb_regs[_DS] = __KERNEL_DS; + gdb_regs[_ES] = __KERNEL_DS; + gdb_regs[_PS] = 0; + gdb_regs[_CS] = __KERNEL_CS; + gdb_regs[_PC] = p->thread.eip; + gdb_regs[_ESP] = p->thread.esp; + gdb_regs[_SS] = __KERNEL_DS; + gdb_regs[_FS] = 0xFFFF; + gdb_regs[_GS] = 0xFFFF; +} + +void gdb_regs_to_regs(unsigned long *gdb_regs, struct pt_regs *regs) +{ + regs->eax = gdb_regs[_EAX]; + regs->ebx = gdb_regs[_EBX]; + regs->ecx = gdb_regs[_ECX]; + regs->edx = gdb_regs[_EDX]; + regs->esi = gdb_regs[_ESI]; + regs->edi = gdb_regs[_EDI]; + regs->ebp = gdb_regs[_EBP]; + regs->xds = gdb_regs[_DS]; + regs->xes = gdb_regs[_ES]; + regs->eflags = gdb_regs[_PS]; + regs->xcs = gdb_regs[_CS]; + regs->eip = gdb_regs[_PC]; +} + +#else /* CONFIG_X86_64 */ + +void 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; +} + +extern void thread_return(void); +void 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] = *(unsigned long *)p->thread.rsp; + gdb_regs[_PS] = *(unsigned long *)(p->thread.rsp + 8); + gdb_regs[_PC] = (unsigned long)&thread_return; + 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; +} + +void 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]; +} +#endif /* CONFIG_X86_64 */ + +static struct hw_breakpoint { + unsigned enabled; + unsigned type; + unsigned len; + unsigned long addr; +} breakinfo[4] = { + { .enabled = 0 }, + { .enabled = 0 }, + { .enabled = 0 }, + { .enabled = 0 }, +}; + +static void kgdb_correct_hw_break(void) +{ + int breakno; + int breakbit; + int correctit = 0; + unsigned long dr7; + + get_debugreg(dr7, 7); + for (breakno = 0; breakno < 4; 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) << + ((breakno << 2) + 16); + switch (breakno) { + case 0: + set_debugreg(breakinfo[0].addr, 0); + break; + + case 1: + set_debugreg(breakinfo[1].addr, 1); + break; + + case 2: + set_debugreg(breakinfo[2].addr, 2); + break; + + case 3: + set_debugreg(breakinfo[3].addr, 3); + break; + } + } else if ((dr7 & breakbit) && !breakinfo[breakno].enabled) { + correctit = 1; + dr7 &= ~breakbit; + dr7 &= ~(0xf0000 << (breakno << 2)); + } + } + if (correctit) + set_debugreg(dr7, 7); +} + +static int kgdb_remove_hw_break(unsigned long addr, int len, + enum kgdb_bptype bptype) +{ + int i; + + for (i = 0; i < 4; i++) + if (breakinfo[i].addr == addr && breakinfo[i].enabled) + break; + if (i == 4) + return -1; + + breakinfo[i].enabled = 0; + return 0; +} + +static void kgdb_remove_all_hw_break(void) +{ + int i; + + for (i = 0; i < 4; i++) + memset(&breakinfo[i], 0, sizeof(struct hw_breakpoint)); +} + +static int kgdb_set_hw_break(unsigned long addr, int len, + enum kgdb_bptype bptype) +{ + int i; + unsigned type; + + for (i = 0; i < 4; i++) + if (!breakinfo[i].enabled) + break; + if (i == 4) + return -1; + + switch (bptype) { + case bp_hardware_breakpoint: + type = 0; + len = 1; + break; + case bp_write_watchpoint: + type = 1; + break; + case bp_access_watchpoint: + type = 3; + break; + default: + return -1; + } + + if (len == 1 || len == 2 || len == 4) + breakinfo[i].len = len - 1; + else + return -1; + + breakinfo[i].enabled = 1; + breakinfo[i].addr = addr; + breakinfo[i].type = type; + return 0; +} + +void kgdb_disable_hw_debug(struct pt_regs *regs) +{ + /* Disable hardware debugging while we are in kgdb */ + set_debugreg(0UL, 7); +} + +void kgdb_post_master_code(struct pt_regs *regs, int e_vector, int err_code) +{ + /* Master processor is completely in the debugger */ + gdb_x86_vector = e_vector; + gdb_x86_errcode = err_code; +} + +#ifdef CONFIG_SMP +void kgdb_roundup_cpus(unsigned long flags) +{ + send_IPI_allbutself(APIC_DM_NMI); +} +#endif + +int kgdb_arch_handle_exception(int e_vector, int signo, int err_code, + char *remcom_in_buffer, char *remcom_out_buffer, + struct pt_regs *linux_regs) +{ + unsigned long addr; + char *ptr; + int newPC; + unsigned long dr6; + + switch (remcom_in_buffer[0]) { + case 'c': + case 's': + /* try to read optional parameter, pc unchanged if no parm */ + ptr = &remcom_in_buffer[1]; + if (kgdb_hex2long(&ptr, &addr)) + instruction_pointer(linux_regs) = addr; + newPC = instruction_pointer(linux_regs); + + /* clear the trace bit */ + linux_regs->eflags &= ~TF_MASK; + atomic_set(&cpu_doing_single_step, -1); + + /* set the trace bit if we're stepping */ + if (remcom_in_buffer[0] == 's') { + linux_regs->eflags |= TF_MASK; + debugger_step = 1; + if (kgdb_contthread) + atomic_set(&cpu_doing_single_step, + raw_smp_processor_id()); + + } + + get_debugreg(dr6, 6); + if (!(dr6 & 0x4000)) { + int breakno; + + for (breakno = 0; breakno < 4; ++breakno) { + if (dr6 & (1 << breakno) && + breakinfo[breakno].type == 0) { + /* Set restore flag */ + linux_regs->eflags |= X86_EFLAGS_RF; + break; + } + } + } + set_debugreg(0UL, 6); + kgdb_correct_hw_break(); + + return 0; + } + /* this means that we do not want to exit from the handler */ + return -1; +} + +#ifdef CONFIG_X86_64 +static struct pt_regs *in_interrupt_stack(unsigned long rsp, int cpu) +{ + struct pt_regs *regs = NULL; + unsigned long end = (unsigned long)cpu_pda(cpu)->irqstackptr; + + if (rsp <= end && rsp >= end - IRQSTACKSIZE + 8) + regs = *(((struct pt_regs **)end) - 1); + + return regs; +} + +static struct pt_regs *in_exception_stack(unsigned long rsp, int cpu) +{ + struct tss_struct *init_tss = &__get_cpu_var(init_tss); + struct pt_regs *regs; + 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) { + regs = (void *) init_tss[cpu].ist[i] + EXCEPTION_STKSZ; + return regs - 1; + } + + return NULL; +} + +void kgdb_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 = raw_smp_processor_id(); + stregs = in_interrupt_stack(regs->rsp, cpu); + if (stregs) + kgdb_mem2hex(intr_desc, buffer, strlen(intr_desc)); + else { + stregs = in_exception_stack(regs->rsp, cpu); + if (stregs) + kgdb_mem2hex(exc_desc, buffer, strlen(exc_desc)); + } +} + +struct task_struct *kgdb_get_shadow_thread(struct pt_regs *regs, int threadid) +{ + struct pt_regs *stregs; + int cpu = raw_smp_processor_id(); + + stregs = in_interrupt_stack(regs->rsp, cpu); + if (stregs) + return current; + else { + stregs = in_exception_stack(regs->rsp, cpu); + if (stregs) + return current; + } + + return NULL; +} + +struct pt_regs *kgdb_shadow_regs(struct pt_regs *regs, int threadid) +{ + struct pt_regs *stregs; + int cpu = raw_smp_processor_id(); + + stregs = in_interrupt_stack(regs->rsp, cpu); + if (stregs) + return stregs; + else { + stregs = in_exception_stack(regs->rsp, cpu); + if (stregs) + return stregs; + } + + return NULL; +} +#endif /* CONFIG_X86_64 */ + +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 notifier_block *self, unsigned long cmd, + void *ptr) +{ + struct die_args *args = ptr; + struct pt_regs *regs = args->regs; + + switch (cmd) { + case DIE_NMI: + if (atomic_read(&debugger_active)) { + /* KGDB CPU roundup */ + kgdb_nmihook(raw_smp_processor_id(), regs); + return NOTIFY_STOP; + } + return NOTIFY_DONE; + case DIE_NMI_IPI: + if (atomic_read(&debugger_active)) { + /* KGDB CPU roundup */ + if (kgdb_nmihook(raw_smp_processor_id(), regs)) + return NOTIFY_DONE; + return NOTIFY_STOP; + } + return NOTIFY_DONE; + case DIE_NMIWATCHDOG: + if (atomic_read(&debugger_active)) { + /* KGDB CPU roundup */ + kgdb_nmihook(raw_smp_processor_id(), regs); + return NOTIFY_STOP; + } + /* Enter debugger */ + break; + case DIE_DEBUG: + if (atomic_read(&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_may_fault) { + kgdb_fault_longjmp(kgdb_fault_jmp_regs); + return NOTIFY_STOP; + } + + if (kgdb_handle_exception(args->trapnr, args->signr, + args->err, regs)) + return NOTIFY_DONE; + + return NOTIFY_STOP; +} + +static struct notifier_block kgdb_notifier = { + .notifier_call = kgdb_notify, + .priority = 0x7fffffff, /* we need to be notified first */ +}; + +int kgdb_arch_init(void) +{ + register_die_notifier(&kgdb_notifier); + return 0; +} + +/* + * 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(instruction_pointer(regs) - 1)) { + instruction_pointer(regs) -= 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 = { + .gdb_bpt_instr = {0xcc}, + .flags = KGDB_HW_BREAKPOINT, +#ifdef CONFIG_X86_64 + .shadowth = 1, +#endif + .set_hw_breakpoint = kgdb_set_hw_break, + .remove_hw_breakpoint = kgdb_remove_hw_break, + .remove_all_hw_break = kgdb_remove_all_hw_break, + .correct_hw_break = kgdb_correct_hw_break, +}; Index: b/arch/x86/kernel/kgdb_32.c =================================================================== --- a/arch/x86/kernel/kgdb_32.c +++ /dev/null @@ -1,414 +0,0 @@ -/* - * - * 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) 2000-2001 VERITAS Software Corporation. - * Copyright (C) 2007 Wind River Systems, Inc. - * Copyright (C) 2007 MontaVista Software, Inc. - */ -/* - * Contributor: Lake Stevens Instrument Division$ - * Written by: Glenn Engel $ - * Updated by: Amit Kale - * Updated by: Tom Rini - * Updated by: Jason Wessel - * Modified for 386 by Jim Kingdon, Cygnus Support. - * Origianl kgdb, compatibility with 2.1.xx kernel by - * David Grothe - * Additional support from Tigran Aivazian - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include /* for linux pt_regs struct */ -#include -#include -#include -#include -#include - -#include "mach_ipi.h" - -/* Put the error code here just in case the user cares. */ -int gdb_i386errcode; -/* Likewise, the vector number here (since GDB only gets the signal - number through the usual means, and that's not very specific). */ -int gdb_i386vector = -1; - -void regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs) -{ - gdb_regs[_EAX] = regs->eax; - gdb_regs[_EBX] = regs->ebx; - gdb_regs[_ECX] = regs->ecx; - gdb_regs[_EDX] = regs->edx; - gdb_regs[_ESI] = regs->esi; - gdb_regs[_EDI] = regs->edi; - gdb_regs[_EBP] = regs->ebp; - gdb_regs[_DS] = regs->xds; - gdb_regs[_ES] = regs->xes; - gdb_regs[_PS] = regs->eflags; - gdb_regs[_CS] = regs->xcs; - gdb_regs[_PC] = regs->eip; - gdb_regs[_ESP] = (int)(®s->esp); - gdb_regs[_SS] = __KERNEL_DS; - gdb_regs[_FS] = 0xFFFF; - gdb_regs[_GS] = 0xFFFF; -} - -/* - * Extracts ebp, esp and eip values understandable by gdb from the values - * saved by switch_to. - * thread.esp points to ebp. flags and ebp are pushed in switch_to hence esp - * prior to entering switch_to is 8 greater then the value that is saved. - * If switch_to changes, change following code appropriately. - */ -void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p) -{ - gdb_regs[_EAX] = 0; - gdb_regs[_EBX] = 0; - gdb_regs[_ECX] = 0; - gdb_regs[_EDX] = 0; - gdb_regs[_ESI] = 0; - gdb_regs[_EDI] = 0; - gdb_regs[_EBP] = *(unsigned long *)p->thread.esp; - gdb_regs[_DS] = __KERNEL_DS; - gdb_regs[_ES] = __KERNEL_DS; - gdb_regs[_PS] = 0; - gdb_regs[_CS] = __KERNEL_CS; - gdb_regs[_PC] = p->thread.eip; - gdb_regs[_ESP] = p->thread.esp; - gdb_regs[_SS] = __KERNEL_DS; - gdb_regs[_FS] = 0xFFFF; - gdb_regs[_GS] = 0xFFFF; -} - -void gdb_regs_to_regs(unsigned long *gdb_regs, struct pt_regs *regs) -{ - regs->eax = gdb_regs[_EAX]; - regs->ebx = gdb_regs[_EBX]; - regs->ecx = gdb_regs[_ECX]; - regs->edx = gdb_regs[_EDX]; - regs->esi = gdb_regs[_ESI]; - regs->edi = gdb_regs[_EDI]; - regs->ebp = gdb_regs[_EBP]; - regs->xds = gdb_regs[_DS]; - regs->xes = gdb_regs[_ES]; - regs->eflags = gdb_regs[_PS]; - regs->xcs = gdb_regs[_CS]; - regs->eip = gdb_regs[_PC]; -} - -static struct hw_breakpoint { - unsigned enabled; - unsigned type; - unsigned len; - unsigned addr; -} breakinfo[4] = { - { .enabled = 0 }, - { .enabled = 0 }, - { .enabled = 0 }, - { .enabled = 0 }, -}; - -static void kgdb_correct_hw_break(void) -{ - int breakno; - int breakbit; - int correctit = 0; - unsigned long dr7; - - get_debugreg(dr7, 7); - for (breakno = 0; breakno < 4; 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) << - ((breakno << 2) + 16); - switch (breakno) { - case 0: - set_debugreg(breakinfo[0].addr, 0); - break; - - case 1: - set_debugreg(breakinfo[1].addr, 1); - break; - - case 2: - set_debugreg(breakinfo[2].addr, 2); - break; - - case 3: - set_debugreg(breakinfo[3].addr, 3); - break; - } - } else if ((dr7 & breakbit) && !breakinfo[breakno].enabled) { - correctit = 1; - dr7 &= ~breakbit; - dr7 &= ~(0xf0000 << (breakno << 2)); - } - } - if (correctit) - set_debugreg(dr7, 7); -} - -static int kgdb_remove_hw_break(unsigned long addr, int len, - enum kgdb_bptype bptype) -{ - int i; - - for (i = 0; i < 4; i++) - if (breakinfo[i].addr == addr && breakinfo[i].enabled) - break; - if (i == 4) - return -1; - - breakinfo[i].enabled = 0; - return 0; -} - -static void kgdb_remove_all_hw_break(void) -{ - int i; - - for (i = 0; i < 4; i++) - memset(&breakinfo[i], 0, sizeof(struct hw_breakpoint)); -} - -static int kgdb_set_hw_break(unsigned long addr, int len, - enum kgdb_bptype bptype) -{ - int i; - unsigned type; - - for (i = 0; i < 4; i++) - if (!breakinfo[i].enabled) - break; - if (i == 4) - return -1; - - switch (bptype) { - case bp_hardware_breakpoint: - type = 0; - len = 1; - break; - case bp_write_watchpoint: - type = 1; - break; - case bp_access_watchpoint: - type = 3; - break; - default: - return -1; - } - - if (len == 1 || len == 2 || len == 4) - breakinfo[i].len = len - 1; - else - return -1; - - breakinfo[i].enabled = 1; - breakinfo[i].addr = addr; - breakinfo[i].type = type; - return 0; -} - -void kgdb_disable_hw_debug(struct pt_regs *regs) -{ - /* Disable hardware debugging while we are in kgdb */ - set_debugreg(0, 7); -} - -void kgdb_post_master_code(struct pt_regs *regs, int e_vector, int err_code) -{ - /* Master processor is completely in the debugger */ - gdb_i386vector = e_vector; - gdb_i386errcode = err_code; -} - -#ifdef CONFIG_SMP -void kgdb_roundup_cpus(unsigned long flags) -{ - send_IPI_allbutself(APIC_DM_NMI); -} -#endif - -int kgdb_arch_handle_exception(int e_vector, int signo, - int err_code, char *remcom_in_buffer, - char *remcom_out_buffer, - struct pt_regs *linux_regs) -{ - long addr; - char *ptr; - int newPC; - int dr6; - - switch (remcom_in_buffer[0]) { - case 'c': - case 's': - /* try to read optional parameter, pc unchanged if no parm */ - ptr = &remcom_in_buffer[1]; - if (kgdb_hex2long(&ptr, &addr)) - linux_regs->eip = addr; - newPC = linux_regs->eip; - - /* clear the trace bit */ - linux_regs->eflags &= ~TF_MASK; - atomic_set(&cpu_doing_single_step, -1); - - /* set the trace bit if we're stepping */ - if (remcom_in_buffer[0] == 's') { - linux_regs->eflags |= TF_MASK; - debugger_step = 1; - atomic_set(&cpu_doing_single_step, - raw_smp_processor_id()); - } - - get_debugreg(dr6, 6); - if (!(dr6 & 0x4000)) { - int breakno; - for (breakno = 0; breakno < 4; ++breakno) { - if (dr6 & (1 << breakno) && - breakinfo[breakno].type == 0) { - /* Set restore flag */ - linux_regs->eflags |= X86_EFLAGS_RF; - break; - } - } - } - set_debugreg(0, 6); - kgdb_correct_hw_break(); - - 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 notifier_block *self, unsigned long cmd, - void *ptr) -{ - struct die_args *args = ptr; - struct pt_regs *regs = args->regs; - - switch (cmd) { - case DIE_NMI: - if (atomic_read(&debugger_active)) { - /* KGDB CPU roundup */ - kgdb_nmihook(raw_smp_processor_id(), regs); - return NOTIFY_STOP; - } - return NOTIFY_DONE; - case DIE_NMI_IPI: - if (atomic_read(&debugger_active)) { - /* KGDB CPU roundup */ - if (kgdb_nmihook(raw_smp_processor_id(), regs)) - return NOTIFY_DONE; - return NOTIFY_STOP; - } - return NOTIFY_DONE; - case DIE_NMIWATCHDOG: - if (atomic_read(&debugger_active)) { - /* KGDB CPU roundup */ - kgdb_nmihook(raw_smp_processor_id(), regs); - return NOTIFY_STOP; - } - /* Enter debugger */ - break; - case DIE_DEBUG: - if (atomic_read(&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_may_fault) { - kgdb_fault_longjmp(kgdb_fault_jmp_regs); - return NOTIFY_STOP; - } - - if (kgdb_handle_exception(args->trapnr, args->signr, - args->err, regs)) - return NOTIFY_DONE; - - return NOTIFY_STOP; -} - -static struct notifier_block kgdb_notifier = { - .notifier_call = kgdb_notify, -}; - -int kgdb_arch_init(void) -{ - register_die_notifier(&kgdb_notifier); - return 0; -} - -/* - * 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->eip - 1)) { - regs->eip -= 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 = { - .gdb_bpt_instr = {0xcc}, - .flags = KGDB_HW_BREAKPOINT, - .set_hw_breakpoint = kgdb_set_hw_break, - .remove_hw_breakpoint = kgdb_remove_hw_break, - .remove_all_hw_break = kgdb_remove_all_hw_break, - .correct_hw_break = kgdb_correct_hw_break, -}; Index: b/arch/x86/kernel/kgdb_64.c =================================================================== --- a/arch/x86/kernel/kgdb_64.c +++ /dev/null @@ -1,495 +0,0 @@ -/* - * - * 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 - * Copyright (C) 2004 LinSysSoft Technologies Pvt. Ltd. - * Copyright (C) 2007 Jason Wessel, Wind River Systems, Inc. - * Copyright (C) 2007 MontaVista Software, Inc. - */ -/**************************************************************************** - * Contributor: Lake Stevens Instrument Division$ - * Written by: Glenn Engel $ - * Updated by: Amit Kale - * 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 - * X86_64 changes from Andi Kleen's patch merged by Jim Houston - */ - -#include -#include -#include -#include -#include -#include -#include -#include /* for linux pt_regs struct */ -#include -#include -#include -#include -#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; - -void 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; -} - -extern void thread_return(void); -void 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] = *(unsigned long *)p->thread.rsp; - gdb_regs[_PS] = *(unsigned long *)(p->thread.rsp + 8); - gdb_regs[_PC] = (unsigned long)&thread_return; - 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; -} - -void 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]; -} /* gdb_regs_to_regs */ - -static struct hw_breakpoint { - unsigned enabled; - unsigned type; - unsigned len; - unsigned long addr; -} breakinfo[4] = { - { .enabled = 0 }, - { .enabled = 0 }, - { .enabled = 0 }, - { .enabled = 0 }, -}; - -static void kgdb_correct_hw_break(void) -{ - int breakno; - int breakbit; - int correctit = 0; - unsigned long dr7; - - get_debugreg(dr7, 7); - for (breakno = 0; breakno < 4; 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) << - ((breakno << 2) + 16); - switch (breakno) { - case 0: - set_debugreg(breakinfo[0].addr, 0); - break; - - case 1: - set_debugreg(breakinfo[1].addr, 1); - break; - - case 2: - set_debugreg(breakinfo[2].addr, 2); - break; - - case 3: - set_debugreg(breakinfo[3].addr, 3); - break; - } - } else if ((dr7 & breakbit) && !breakinfo[breakno].enabled) { - correctit = 1; - dr7 &= ~breakbit; - dr7 &= ~(0xf0000 << (breakno << 2)); - } - } - if (correctit) - set_debugreg(dr7, 7); -} - -static int kgdb_remove_hw_break(unsigned long addr, int len, - enum kgdb_bptype bptype) -{ - int i; - - for (i = 0; i < 4; i++) - if (breakinfo[i].addr == addr && breakinfo[i].enabled) - break; - if (i == 4) - return -1; - - breakinfo[i].enabled = 0; - return 0; -} - -static void kgdb_remove_all_hw_break(void) -{ - int i; - - for (i = 0; i < 4; i++) - memset(&breakinfo[i], 0, sizeof(struct hw_breakpoint)); -} - -static int kgdb_set_hw_break(unsigned long addr, int len, - enum kgdb_bptype bptype) -{ - int i; - unsigned type; - - for (i = 0; i < 4; i++) - if (!breakinfo[i].enabled) - break; - if (i == 4) - return -1; - - switch (bptype) { - case bp_hardware_breakpoint: - type = 0; - len = 1; - break; - case bp_write_watchpoint: - type = 1; - break; - case bp_access_watchpoint: - type = 3; - break; - default: - return -1; - } - - if (len == 1 || len == 2 || len == 4) - breakinfo[i].len = len - 1; - else - return -1; - - breakinfo[i].enabled = 1; - breakinfo[i].addr = addr; - breakinfo[i].type = type; - return 0; -} - -void kgdb_disable_hw_debug(struct pt_regs *regs) -{ - /* Disable hardware debugging while we are in kgdb */ - set_debugreg(0UL, 7); -} - -void kgdb_post_master_code(struct pt_regs *regs, int e_vector, int err_code) -{ - /* Master processor is completely in the debugger */ - gdb_x86_64vector = e_vector; - gdb_x86_64errcode = err_code; -} - -void kgdb_roundup_cpus(unsigned long flags) -{ - send_IPI_allbutself(APIC_DM_NMI); -} - -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; - unsigned long dr6; - - 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->rip = addr; - newPC = linux_regs->rip; - - /* clear the trace bit */ - linux_regs->eflags &= ~TF_MASK; - atomic_set(&cpu_doing_single_step, -1); - - /* set the trace bit if we're stepping */ - if (remcomInBuffer[0] == 's') { - linux_regs->eflags |= TF_MASK; - debugger_step = 1; - if (kgdb_contthread) - atomic_set(&cpu_doing_single_step, - raw_smp_processor_id()); - - } - - get_debugreg(dr6, 6); - if (!(dr6 & 0x4000)) { - int breakno; - - for (breakno = 0; breakno < 4; ++breakno) { - if (dr6 & (1 << breakno) && - breakinfo[breakno].type == 0) { - /* Set restore flag */ - linux_regs->eflags |= X86_EFLAGS_RF; - break; - } - } - } - set_debugreg(0UL, 6); - kgdb_correct_hw_break(); - - return 0; - } - /* this means that we do not want to exit from the handler */ - return -1; -} - -static struct pt_regs *in_interrupt_stack(unsigned long rsp, int cpu) -{ - struct pt_regs *regs = NULL; - unsigned long end = (unsigned long)cpu_pda(cpu)->irqstackptr; - - if (rsp <= end && rsp >= end - IRQSTACKSIZE + 8) - regs = *(((struct pt_regs **)end) - 1); - - return regs; -} - -static struct pt_regs *in_exception_stack(unsigned long rsp, int cpu) -{ - struct tss_struct *init_tss = &__get_cpu_var(init_tss); - struct pt_regs *regs; - 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) { - regs = (void *) init_tss[cpu].ist[i] + EXCEPTION_STKSZ; - return regs - 1; - } - - return NULL; -} - -void kgdb_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 = raw_smp_processor_id(); - stregs = in_interrupt_stack(regs->rsp, cpu); - if (stregs) - kgdb_mem2hex(intr_desc, buffer, strlen(intr_desc)); - else { - stregs = in_exception_stack(regs->rsp, cpu); - if (stregs) - kgdb_mem2hex(exc_desc, buffer, strlen(exc_desc)); - } -} - -struct task_struct *kgdb_get_shadow_thread(struct pt_regs *regs, int threadid) -{ - struct pt_regs *stregs; - int cpu = raw_smp_processor_id(); - - stregs = in_interrupt_stack(regs->rsp, cpu); - if (stregs) - return current; - else { - stregs = in_exception_stack(regs->rsp, cpu); - if (stregs) - return current; - } - - return NULL; -} - -struct pt_regs *kgdb_shadow_regs(struct pt_regs *regs, int threadid) -{ - struct pt_regs *stregs; - int cpu = raw_smp_processor_id(); - - stregs = in_interrupt_stack(regs->rsp, cpu); - if (stregs) - return stregs; - else { - stregs = in_exception_stack(regs->rsp, cpu); - if (stregs) - return stregs; - } - - return NULL; -} - -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 notifier_block *self, unsigned long cmd, - void *ptr) -{ - struct die_args *args = ptr; - struct pt_regs *regs = args->regs; - - switch (cmd) { - case DIE_NMI: - if (atomic_read(&debugger_active)) { - /* KGDB CPU roundup */ - kgdb_nmihook(raw_smp_processor_id(), regs); - return NOTIFY_STOP; - } - return NOTIFY_DONE; - case DIE_NMI_IPI: - if (atomic_read(&debugger_active)) { - /* KGDB CPU roundup */ - if (kgdb_nmihook(raw_smp_processor_id(), regs)) - return NOTIFY_DONE; - return NOTIFY_STOP; - } - return NOTIFY_DONE; - case DIE_NMIWATCHDOG: - if (atomic_read(&debugger_active)) { - /* KGDB CPU roundup */ - kgdb_nmihook(raw_smp_processor_id(), regs); - return NOTIFY_STOP; - } - /* Enter debugger */ - break; - case DIE_DEBUG: - if (atomic_read(&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_may_fault) { - kgdb_fault_longjmp(kgdb_fault_jmp_regs); - return NOTIFY_STOP; - } - - if (kgdb_handle_exception(args->trapnr, args->signr, - args->err, regs)) - return NOTIFY_DONE; - - return NOTIFY_STOP; -} - -static struct notifier_block kgdb_notifier = { - .notifier_call = kgdb_notify, - .priority = 0x7fffffff, /* we need to be notified first */ -}; - -int kgdb_arch_init(void) -{ - register_die_notifier(&kgdb_notifier); - return 0; -} - -/* - * 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->rip - 1)) { - regs->rip -= 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 = { - .gdb_bpt_instr = {0xcc}, - .flags = KGDB_HW_BREAKPOINT, - .shadowth = 1, - .set_hw_breakpoint = kgdb_set_hw_break, - .remove_hw_breakpoint = kgdb_remove_hw_break, - .remove_all_hw_break = kgdb_remove_all_hw_break, - .correct_hw_break = kgdb_correct_hw_break, -}; -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/