| 
									
										
										
										
											2012-08-23 21:31:32 +00:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * User-space Probes (UProbes) for powerpc | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * 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 IBM Corporation, 2007-2012 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Adapted from the x86 port by Ananth N Mavinakayanahalli <ananth@in.ibm.com> | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | #include <linux/kernel.h>
 | 
					
						
							|  |  |  | #include <linux/sched.h>
 | 
					
						
							|  |  |  | #include <linux/ptrace.h>
 | 
					
						
							|  |  |  | #include <linux/uprobes.h>
 | 
					
						
							|  |  |  | #include <linux/uaccess.h>
 | 
					
						
							|  |  |  | #include <linux/kdebug.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <asm/sstep.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define UPROBE_TRAP_NR	UINT_MAX
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * arch_uprobe_analyze_insn | 
					
						
							|  |  |  |  * @mm: the probed address space. | 
					
						
							|  |  |  |  * @arch_uprobe: the probepoint information. | 
					
						
							|  |  |  |  * @addr: vaddr to probe. | 
					
						
							|  |  |  |  * Return 0 on success or a -ve number on error. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, | 
					
						
							|  |  |  | 		struct mm_struct *mm, unsigned long addr) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (addr & 0x03) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * We currently don't support a uprobe on an already | 
					
						
							|  |  |  | 	 * existing breakpoint instruction underneath | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if (is_trap(auprobe->ainsn)) | 
					
						
							|  |  |  | 		return -ENOTSUPP; | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * arch_uprobe_pre_xol - prepare to execute out of line. | 
					
						
							|  |  |  |  * @auprobe: the probepoint information. | 
					
						
							|  |  |  |  * @regs: reflects the saved user state of current task. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct arch_uprobe_task *autask = ¤t->utask->autask; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	autask->saved_trap_nr = current->thread.trap_nr; | 
					
						
							|  |  |  | 	current->thread.trap_nr = UPROBE_TRAP_NR; | 
					
						
							|  |  |  | 	regs->nip = current->utask->xol_vaddr; | 
					
						
							| 
									
										
										
										
											2012-10-28 16:55:36 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	user_enable_single_step(current); | 
					
						
							| 
									
										
										
										
											2012-08-23 21:31:32 +00:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * uprobe_get_swbp_addr - compute address of swbp given post-swbp regs | 
					
						
							|  |  |  |  * @regs: Reflects the saved state of the task after it has hit a breakpoint | 
					
						
							|  |  |  |  * instruction. | 
					
						
							|  |  |  |  * Return the address of the breakpoint instruction. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | unsigned long uprobe_get_swbp_addr(struct pt_regs *regs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return instruction_pointer(regs); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * If xol insn itself traps and generates a signal (SIGILL/SIGSEGV/etc), | 
					
						
							|  |  |  |  * then detect the case where a singlestepped instruction jumps back to its | 
					
						
							|  |  |  |  * own address. It is assumed that anything like do_page_fault/do_trap/etc | 
					
						
							|  |  |  |  * sets thread.trap_nr != UINT_MAX. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * arch_uprobe_pre_xol/arch_uprobe_post_xol save/restore thread.trap_nr, | 
					
						
							|  |  |  |  * arch_uprobe_xol_was_trapped() simply checks that ->trap_nr is not equal to | 
					
						
							|  |  |  |  * UPROBE_TRAP_NR == UINT_MAX set by arch_uprobe_pre_xol(). | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | bool arch_uprobe_xol_was_trapped(struct task_struct *t) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (t->thread.trap_nr != UPROBE_TRAP_NR) | 
					
						
							|  |  |  | 		return true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Called after single-stepping. To avoid the SMP problems that can | 
					
						
							|  |  |  |  * occur when we temporarily put back the original opcode to | 
					
						
							|  |  |  |  * single-step, we single-stepped a copy of the instruction. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This function prepares to resume execution after the single-step. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct uprobe_task *utask = current->utask; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	WARN_ON_ONCE(current->thread.trap_nr != UPROBE_TRAP_NR); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	current->thread.trap_nr = utask->autask.saved_trap_nr; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * On powerpc, except for loads and stores, most instructions | 
					
						
							|  |  |  | 	 * including ones that alter code flow (branches, calls, returns) | 
					
						
							|  |  |  | 	 * are emulated in the kernel. We get here only if the emulation | 
					
						
							|  |  |  | 	 * support doesn't exist and have to fix-up the next instruction | 
					
						
							|  |  |  | 	 * to be executed. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	regs->nip = utask->vaddr + MAX_UINSN_BYTES; | 
					
						
							| 
									
										
										
										
											2012-10-28 16:55:36 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	user_disable_single_step(current); | 
					
						
							| 
									
										
										
										
											2012-08-23 21:31:32 +00:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* callback routine for handling exceptions. */ | 
					
						
							|  |  |  | int arch_uprobe_exception_notify(struct notifier_block *self, | 
					
						
							|  |  |  | 				unsigned long val, void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct die_args *args = data; | 
					
						
							|  |  |  | 	struct pt_regs *regs = args->regs; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* regs == NULL is a kernel bug */ | 
					
						
							|  |  |  | 	if (WARN_ON(!regs)) | 
					
						
							|  |  |  | 		return NOTIFY_DONE; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* We are only interested in userspace traps */ | 
					
						
							|  |  |  | 	if (!user_mode(regs)) | 
					
						
							|  |  |  | 		return NOTIFY_DONE; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (val) { | 
					
						
							|  |  |  | 	case DIE_BPT: | 
					
						
							|  |  |  | 		if (uprobe_pre_sstep_notifier(regs)) | 
					
						
							|  |  |  | 			return NOTIFY_STOP; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case DIE_SSTEP: | 
					
						
							|  |  |  | 		if (uprobe_post_sstep_notifier(regs)) | 
					
						
							|  |  |  | 			return NOTIFY_STOP; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return NOTIFY_DONE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * This function gets called when XOL instruction either gets trapped or | 
					
						
							|  |  |  |  * the thread has a fatal signal, so reset the instruction pointer to its | 
					
						
							|  |  |  |  * probed address. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct uprobe_task *utask = current->utask; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	current->thread.trap_nr = utask->autask.saved_trap_nr; | 
					
						
							|  |  |  | 	instruction_pointer_set(regs, utask->vaddr); | 
					
						
							| 
									
										
										
										
											2012-10-28 16:55:36 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	user_disable_single_step(current); | 
					
						
							| 
									
										
										
										
											2012-08-23 21:31:32 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * See if the instruction can be emulated. | 
					
						
							|  |  |  |  * Returns true if instruction was emulated, false otherwise. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * emulate_step() returns 1 if the insn was successfully emulated. | 
					
						
							|  |  |  | 	 * For all other cases, we need to single-step in hardware. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	ret = emulate_step(regs, auprobe->ainsn); | 
					
						
							|  |  |  | 	if (ret > 0) | 
					
						
							|  |  |  | 		return true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return false; | 
					
						
							|  |  |  | } |