From patchwork Thu May 14 13:44:39 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "K.Prasad" X-Patchwork-Id: 27208 Return-Path: X-Original-To: patchwork-incoming@bilbo.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from ozlabs.org (ozlabs.org [203.10.76.45]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client CN "mx.ozlabs.org", Issuer "CA Cert Signing Authority" (verified OK)) by bilbo.ozlabs.org (Postfix) with ESMTPS id B7E18B707E for ; Thu, 14 May 2009 23:46:30 +1000 (EST) Received: by ozlabs.org (Postfix) id C65CDDE57A; Thu, 14 May 2009 23:45:09 +1000 (EST) Delivered-To: patchwork-incoming@ozlabs.org Received: from ozlabs.org (localhost [127.0.0.1]) by ozlabs.org (Postfix) with ESMTP id C470ADE578 for ; Thu, 14 May 2009 23:45:09 +1000 (EST) X-Original-To: linuxppc-dev@ozlabs.org Delivered-To: linuxppc-dev@ozlabs.org Received: from e28smtp02.in.ibm.com (e28smtp02.in.ibm.com [59.145.155.2]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client CN "e28smtp02.in.ibm.com", Issuer "Equifax" (verified OK)) by ozlabs.org (Postfix) with ESMTPS id 627A6DDFF2 for ; Thu, 14 May 2009 23:44:47 +1000 (EST) Received: from d28relay02.in.ibm.com (d28relay02.in.ibm.com [9.184.220.59]) by e28smtp02.in.ibm.com (8.13.1/8.13.1) with ESMTP id n4EDihXD011349 for ; Thu, 14 May 2009 19:14:43 +0530 Received: from d28av04.in.ibm.com (d28av04.in.ibm.com [9.184.220.66]) by d28relay02.in.ibm.com (8.13.8/8.13.8/NCO v9.2) with ESMTP id n4EDihdk2150610 for ; Thu, 14 May 2009 19:14:43 +0530 Received: from d28av04.in.ibm.com (loopback [127.0.0.1]) by d28av04.in.ibm.com (8.13.1/8.13.3) with ESMTP id n4EDigRt010959 for ; Thu, 14 May 2009 23:44:43 +1000 Received: from in.ibm.com ([9.77.212.231]) by d28av04.in.ibm.com (8.13.1/8.12.11) with ESMTP id n4EDie44010910 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Thu, 14 May 2009 23:44:41 +1000 Date: Thu, 14 May 2009 19:14:39 +0530 From: "K.Prasad" To: linuxppc-dev@ozlabs.org Subject: [RFC Patch 2/6] Introduce PPC64 specific Hardware Breakpoint interfaces Message-ID: <20090514134439.GC14229@in.ibm.com> References: <20090514133312.360702378@prasadkr_t60p.in.ibm.com> MIME-Version: 1.0 Content-Disposition: inline; filename=ppc64_arch_hwbkpt_implementation_02 User-Agent: Mutt/1.5.18 (2008-05-17) Cc: Michael Neuling , Benjamin Herrenschmidt , Alan Stern , paulus@samba.org, "K.Prasad" , Roland McGrath X-BeenThere: linuxppc-dev@ozlabs.org X-Mailman-Version: 2.1.11 Precedence: list List-Id: Linux on PowerPC Developers Mail List List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: linuxppc-dev-bounces+patchwork-incoming=ozlabs.org@ozlabs.org Errors-To: linuxppc-dev-bounces+patchwork-incoming=ozlabs.org@ozlabs.org Introduce PPC64 implementation for the generic hardware breakpoint interfaces defined in kernel/hw_breakpoint.c. Enable the HAVE_HW_BREAKPOINT flag and the Makefile. Signed-off-by: K.Prasad --- arch/powerpc/Kconfig | 1 arch/powerpc/kernel/Makefile | 2 arch/powerpc/kernel/hw_breakpoint.c | 281 ++++++++++++++++++++++++++++++++++++ 3 files changed, 283 insertions(+), 1 deletion(-) Index: linux-2.6-tip.hbkpt/arch/powerpc/Kconfig =================================================================== --- linux-2.6-tip.hbkpt.orig/arch/powerpc/Kconfig +++ linux-2.6-tip.hbkpt/arch/powerpc/Kconfig @@ -125,6 +125,7 @@ config PPC select USE_GENERIC_SMP_HELPERS if SMP select HAVE_OPROFILE select HAVE_SYSCALL_WRAPPERS if PPC64 + select HAVE_HW_BREAKPOINT if PPC64 config EARLY_PRINTK bool Index: linux-2.6-tip.hbkpt/arch/powerpc/kernel/Makefile =================================================================== --- linux-2.6-tip.hbkpt.orig/arch/powerpc/kernel/Makefile +++ linux-2.6-tip.hbkpt/arch/powerpc/kernel/Makefile @@ -33,7 +33,7 @@ obj-$(CONFIG_PPC64) += setup_64.o sys_p signal_64.o ptrace32.o \ paca.o cpu_setup_ppc970.o \ cpu_setup_pa6t.o \ - firmware.o nvram_64.o + firmware.o nvram_64.o hw_breakpoint.o obj64-$(CONFIG_RELOCATABLE) += reloc_64.o obj-$(CONFIG_PPC64) += vdso64/ obj-$(CONFIG_ALTIVEC) += vecemu.o vector.o Index: linux-2.6-tip.hbkpt/arch/powerpc/kernel/hw_breakpoint.c =================================================================== --- /dev/null +++ linux-2.6-tip.hbkpt/arch/powerpc/kernel/hw_breakpoint.c @@ -0,0 +1,281 @@ +/* + * 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 of the License, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) 2009 IBM Corporation + */ + +/* + * HW_breakpoint: a unified kernel/user-space hardware breakpoint facility, + * using the CPU's debug registers. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* Store the kernel-space breakpoint address value */ +static unsigned long kdabr; + +/* + * Temporarily stores address for DABR before it is written by the + * single-step handler routine + */ +static DEFINE_PER_CPU(unsigned long, dabr_data); + +void arch_update_kernel_hw_breakpoint(void *unused) +{ + struct hw_breakpoint *bp; + + /* Check if there is nothing to update */ + if (hbp_kernel_pos == HBP_NUM) + return; + bp = hbp_kernel[hbp_kernel_pos]; + if (bp == NULL) + kdabr = 0; + else + kdabr = bp->info.address | bp->info.type | DABR_TRANSLATION; + set_dabr(kdabr); +} + +/* + * Install the thread breakpoints in their debug registers. + */ +void arch_install_thread_hw_breakpoint(struct task_struct *tsk) +{ + set_dabr(tsk->thread.dabr); +} + +/* + * Install the debug register values for just the kernel, no thread. + */ +void arch_uninstall_thread_hw_breakpoint() +{ + set_dabr(0); +} + +/* + * Check for virtual address in user space. + */ +static int arch_check_va_in_userspace(unsigned long va) +{ + return (!(is_kernel_addr(va))); +} + +/* + * Check for virtual address in kernel space. + */ +static int arch_check_va_in_kernelspace(unsigned long va) +{ + return is_kernel_addr(va); +} + +/* + * Store a breakpoint's encoded address, length, and type. + */ +int arch_store_info(struct hw_breakpoint *bp) +{ + /* + * User-space requests will always have the address field populated + * For kernel-addresses, either the address or symbol name can be + * specified. + */ + if (bp->info.name) + bp->info.address = (unsigned long) + kallsyms_lookup_name(bp->info.name); + if (bp->info.address) + return 0; + return -EINVAL; +} + +/* + * Validate the arch-specific HW Breakpoint register settings + */ +int arch_validate_hwbkpt_settings(struct hw_breakpoint *bp, + struct task_struct *tsk) +{ + int ret = -EINVAL; + + if (!bp) + return ret; + + switch (bp->info.type) { + case DABR_DATA_READ: + break; + case DABR_DATA_WRITE: + break; + case DABR_DATA_RW: + break; + default: + return ret; + } + + if (bp->triggered) + ret = arch_store_info(bp); + + /* Check for double word alignment - 8 bytes */ + if (bp->info.address & HW_BREAKPOINT_ALIGN) + return -EINVAL; + + /* Check that the virtual address is in the proper range */ + if (tsk) { + if (!arch_check_va_in_userspace(bp->info.address)) + return -EFAULT; + } else { + if (!arch_check_va_in_kernelspace(bp->info.address)) + return -EFAULT; + } + + return ret; +} + +void arch_update_user_hw_breakpoint(int pos, struct task_struct *tsk) +{ + struct thread_struct *thread = &(tsk->thread); + struct hw_breakpoint *bp = thread->hbp[0]; + + if (bp) + thread->dabr = bp->info.address | bp->info.type | + DABR_TRANSLATION; + else + thread->dabr = 0; +} + +void arch_flush_thread_hw_breakpoint(struct task_struct *tsk) +{ + struct thread_struct *thread = &(tsk->thread); + + thread->dabr = 0; +} + +/* + * Handle debug exception notifications. + */ +int __kprobes hw_breakpoint_handler(struct die_args *args) +{ + int rc = NOTIFY_STOP; + struct hw_breakpoint *bp; + struct pt_regs *regs = args->regs; + unsigned long dar; + int cpu, stepped, is_kernel; + + /* Disable breakpoints during exception handling */ + set_dabr(0); + + dar = regs->dar & (~HW_BREAKPOINT_ALIGN); + is_kernel = (dar >= TASK_SIZE) ? 1 : 0; + + if (is_kernel) + bp = hbp_kernel[0]; + else { + bp = current->thread.hbp[0]; + /* Lazy debug register switching */ + if (!bp) + return rc; + rc = NOTIFY_DONE; + } + + (bp->triggered)(bp, regs); + + cpu = get_cpu(); + if (is_kernel) + per_cpu(dabr_data, cpu) = kdabr; + else + per_cpu(dabr_data, cpu) = current->thread.dabr; + + stepped = emulate_step(regs, regs->nip); + /* + * Single-step the causative instruction manually if + * emulate_step() could not execute it + */ + if (stepped == 0) { + regs->msr |= MSR_SE; + goto out; + } + + set_dabr(per_cpu(dabr_data, cpu)); +out: + /* Enable pre-emption only if single-stepping is finished */ + if (stepped) + put_cpu_no_resched(); + return rc; +} + +/* + * Handle single-step exceptions following a DABR hit. + */ +int __kprobes single_step_dabr_instruction(struct die_args *args) +{ + struct pt_regs *regs = args->regs; + int cpu = get_cpu(); + int ret = NOTIFY_DONE; + siginfo_t info; + unsigned long this_dabr_data = per_cpu(dabr_data, cpu); + + /* + * Check if we are single-stepping as a result of a + * previous HW Breakpoint exception + */ + if (this_dabr_data == 0) + goto out; + + regs->msr &= ~MSR_SE; + /* Deliver signal to user-space */ + if (this_dabr_data < TASK_SIZE) { + info.si_signo = SIGTRAP; + info.si_errno = 0; + info.si_code = TRAP_HWBKPT; + info.si_addr = (void __user *)(per_cpu(dabr_data, cpu)); + force_sig_info(SIGTRAP, &info, current); + } + + set_dabr(this_dabr_data); + per_cpu(dabr_data, cpu) = 0; + ret = NOTIFY_STOP; + put_cpu_no_resched(); + +out: + put_cpu_no_resched(); + return ret; +} + +/* + * Handle debug exception notifications. + */ +int __kprobes hw_breakpoint_exceptions_notify( + struct notifier_block *unused, unsigned long val, void *data) +{ + int ret = NOTIFY_DONE; + + switch (val) { + case DIE_DABR_MATCH: + ret = hw_breakpoint_handler(data); + break; + case DIE_SSTEP: + ret = single_step_dabr_instruction(data); + break; + } + + return ret; +}