diff options
-rw-r--r-- | ChangeLog | 5 | ||||
-rw-r--r-- | runtime/uprobes/uprobes_ppc64.c | 36 | ||||
-rw-r--r-- | runtime/uprobes/uprobes_ppc64.h | 70 | ||||
-rw-r--r-- | runtime/uprobes/uprobes_s390.c | 208 | ||||
-rw-r--r-- | runtime/uprobes/uprobes_s390.h | 85 |
5 files changed, 404 insertions, 0 deletions
@@ -1,3 +1,8 @@ +2007-10-10 Jim Keniston <jkenisto@us.ibm.com> + + * runtime/uprobes/uprobes_ppc64.[ch]: Added + * runtime/uprobes/uprobes_s390.[ch]: Added + 2007-10-09 Jim Keniston <jkenisto@us.ibm.com> PR 5083 diff --git a/runtime/uprobes/uprobes_ppc64.c b/runtime/uprobes/uprobes_ppc64.c new file mode 100644 index 00000000..d80676e9 --- /dev/null +++ b/runtime/uprobes/uprobes_ppc64.c @@ -0,0 +1,36 @@ +/* + * Userspace 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 + */ +/* + * In versions of uprobes built in the SystemTap runtime, this file + * is #included at the end of uprobes.c. + */ + +/* + * Replace the return address with the trampoline address. Returns + * the original return address. + */ +static +unsigned long arch_hijack_uret_addr(unsigned long trampoline_address, + struct pt_regs *regs, struct uprobe_task *utask) +{ + unsigned long orig_ret_addr = regs->link; + regs->link = trampoline_address; + return orig_ret_addr; +} diff --git a/runtime/uprobes/uprobes_ppc64.h b/runtime/uprobes/uprobes_ppc64.h new file mode 100644 index 00000000..619ba324 --- /dev/null +++ b/runtime/uprobes/uprobes_ppc64.h @@ -0,0 +1,70 @@ +#ifndef _ASM_UPROBES_H +#define _ASM_UPROBES_H +/* + * Userspace 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 + */ +#include <linux/types.h> +#include <linux/ptrace.h> +#include <linux/signal.h> + +#define BREAKPOINT_SIGNAL SIGTRAP +#define SSTEP_SIGNAL SIGTRAP + +/* Normally defined in Kconfig */ +#undef CONFIG_UPROBES_SSOL +#define CONFIG_URETPROBES 1 + +typedef unsigned int uprobe_opcode_t; +#define BREAKPOINT_INSTRUCTION 0x7fe00008 /* trap */ +#define BP_INSN_SIZE 4 +#define MAX_UINSN_BYTES 4 +#define SLOT_IP 32 /* instruction pointer slot from include/asm/elf.h */ + +/* Architecture specific switch for where the IP points after a bp hit */ +#define ARCH_BP_INST_PTR(inst_ptr) (inst_ptr) + +struct uprobe_probept; +struct uprobe_task; + +static inline int arch_validate_probed_insn(struct uprobe_probept *ppt) +{ + return 0; +} + +/* On powerpc, nip points to the trap. */ +static inline unsigned long arch_get_probept(struct pt_regs *regs) +{ + return (unsigned long)(regs->nip); +} + +static inline void arch_reset_ip_for_sstep(struct pt_regs *regs) +{ +} + +#ifdef CONFIG_URETPROBES +static inline void arch_restore_uret_addr(unsigned long ret_addr, + struct pt_regs *regs) +{ + regs->nip = ret_addr; +} +#endif /* CONFIG_URETPROBES */ + +static unsigned long arch_hijack_uret_addr(unsigned long trampoline_addr, + struct pt_regs *regs, struct uprobe_task *utask); +#endif /* _ASM_UPROBES_H */ diff --git a/runtime/uprobes/uprobes_s390.c b/runtime/uprobes/uprobes_s390.c new file mode 100644 index 00000000..f97dd408 --- /dev/null +++ b/runtime/uprobes/uprobes_s390.c @@ -0,0 +1,208 @@ +/* + * Userspace Probes (UProbes) + * arch/s390/uprobes/uprobes_s390.c + * + * 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) IBM Corporation, 2006 + */ +/* + * In versions of uprobes built in the SystemTap runtime, this file + * is #included at the end of uprobes.c. + */ +#include <linux/uaccess.h> + +/* adapted from s390/kernel/kprobes.c is_prohibited_opcode() */ +/* TODO More instructions?? Should floating point inst be added?? */ +static int prohibited_opcode(uprobe_opcode_t *instruction) +{ + switch (*(__u8 *) instruction) { + case 0x0c: /* bassm */ + case 0x0b: /* bsm */ + case 0x83: /* diag */ + case 0x44: /* ex */ + return -EINVAL; + } + switch (*(__u16 *) instruction) { + case 0x0101: /* pr */ + case 0xb25a: /* bsa */ + case 0xb240: /* bakr */ + case 0xb258: /* bsg */ + case 0xb218: /* pc */ + case 0xb228: /* pt */ + return -EINVAL; + } + return 0; +} + +static +int arch_validate_probed_insn(struct uprobe_probept *ppt) +{ + if (ppt->vaddr & 0x01) { + printk("Attempt to register uprobe at an unaligned address\n"); + return -EPERM; + } + + /* Make sure the probe isn't going on a difficult instruction */ + if (prohibited_opcode((uprobe_opcode_t *) ppt->insn)) + return -EPERM; + + return 0; +} + +/* + * Get an instruction slot from the process's SSOL area, containing the + * instruction at ppt's probepoint. Point the psw.addr at that slot, in + * preparation for single-stepping out of line. + */ +static +void uprobe_pre_ssout(struct uprobe_task *utask, struct uprobe_probept *ppt, + struct pt_regs *regs) +{ + struct uprobe_ssol_slot *slot; + + slot = uprobe_get_insn_slot(ppt); + if (!slot) { + utask->doomed = 1; + return; + } + regs->psw.addr = (long)slot->insn; + utask->singlestep_addr = regs->psw.addr; +} + +static +void uprobe_post_ssout(struct uprobe_task *utask, struct uprobe_probept *ppt, + struct pt_regs *regs) +{ + int ilen, fixup, reg; + unsigned long copy_ins_addr = utask->singlestep_addr; + unsigned long orig_ins_addr = ppt->vaddr; + + up_read(&ppt->slot->rwsem); + + /* default fixup method */ + fixup = FIXUP_PSW_NORMAL;; + + /* get r1 operand */ + reg = (*ppt->insn & 0xf0) >> 4; + + /* save the instruction length (pop 5-5) in bytes */ + switch (*(__u8 *) (ppt->insn) >> 6) { + case 0: + ilen = 2; + break; + case 1: + case 2: + ilen = 4; + break; + case 3: + ilen = 6; + break; + default: + ilen = 0; + BUG(); + } + + + switch (*(__u8 *) ppt->insn) { + case 0x05: /* balr */ + case 0x0d: /* basr */ + fixup = FIXUP_RETURN_REGISTER; + /* if r2 = 0, no branch will be taken */ + if ((*ppt->insn & 0x0f) == 0) + fixup |= FIXUP_BRANCH_NOT_TAKEN; + break; + case 0x06: /* bctr */ + case 0x07: /* bcr */ + fixup = FIXUP_BRANCH_NOT_TAKEN; + break; + case 0x45: /* bal */ + case 0x4d: /* bas */ + fixup = FIXUP_RETURN_REGISTER; + break; + case 0x47: /* bc */ + case 0x46: /* bct */ + case 0x86: /* bxh */ + case 0x87: /* bxle */ + fixup = FIXUP_BRANCH_NOT_TAKEN; + break; + case 0x82: /* lpsw */ + fixup = FIXUP_NOT_REQUIRED; + break; + case 0xb2: /* lpswe */ + if (*(((__u8 *) ppt->insn) + 1) == 0xb2) { + fixup = FIXUP_NOT_REQUIRED; + } + break; + case 0xa7: /* bras */ + if ((*ppt->insn & 0x0f) == 0x05) { + fixup |= FIXUP_RETURN_REGISTER; + } + break; + case 0xc0: + if ((*ppt->insn & 0x0f) == 0x00 /* larl */ + || (*ppt->insn & 0x0f) == 0x05) /* brasl */ + fixup |= FIXUP_RETURN_REGISTER; + break; + case 0xeb: + if (*(((__u8 *) ppt->insn) + 5 ) == 0x44 || /* bxhg */ + *(((__u8 *) ppt->insn) + 5) == 0x45) {/* bxleg */ + fixup = FIXUP_BRANCH_NOT_TAKEN; + } + break; + case 0xe3: /* bctg */ + if (*(((__u8 *) ppt->insn) + 5) == 0x46) { + fixup = FIXUP_BRANCH_NOT_TAKEN; + } + break; + } + + /* do the fixup and adjust psw as needed */ + regs->psw.addr &= PSW_ADDR_INSN; + + if (fixup & FIXUP_PSW_NORMAL) + regs->psw.addr = orig_ins_addr + regs->psw.addr - + copy_ins_addr; + + if (fixup & FIXUP_BRANCH_NOT_TAKEN) + if (regs->psw.addr - copy_ins_addr == ilen) + regs->psw.addr = orig_ins_addr + ilen; + + if (fixup & FIXUP_RETURN_REGISTER) + regs->gprs[reg] = (orig_ins_addr + (regs->gprs[reg] - + copy_ins_addr)) | PSW_ADDR_AMODE; + + regs->psw.addr |= PSW_ADDR_AMODE; +} + + +/* + * Replace the return address with the trampoline address. Returns + * the original return address. + */ +static +unsigned long arch_hijack_uret_addr(unsigned long trampoline_address, + struct pt_regs *regs, struct uprobe_task *utask) +{ + unsigned long orig_ret_addr; +#ifdef CONFIG_COMPAT + if (test_tsk_thread_flag(utask->tsk, TIF_31BIT)) + orig_ret_addr = regs->gprs[14]&0x7FFFFFFFUL; + else +#endif + orig_ret_addr = regs->gprs[14]; + regs->gprs[14] = trampoline_address; + return orig_ret_addr; +} diff --git a/runtime/uprobes/uprobes_s390.h b/runtime/uprobes/uprobes_s390.h new file mode 100644 index 00000000..4f3a8187 --- /dev/null +++ b/runtime/uprobes/uprobes_s390.h @@ -0,0 +1,85 @@ +#ifndef _ASM_UPROBES_H +#define _ASM_UPROBES_H +/* + * Userspace Probes (UProbes) + * include/asm-s390/uprobes.h + * + * Adapted from include/asm-i386/uprobes.h by: + * David Wilder <dwilder.us.ibm.com> 2007 + * + * 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) IBM Corporation, 2006 + */ +#include <linux/types.h> +#include <linux/ptrace.h> + +/* Normally defined in Kconfig */ +#define CONFIG_URETPROBES 1 +#define CONFIG_UPROBES_SSOL 1 + +typedef u16 uprobe_opcode_t; + +#define BREAKPOINT_INSTRUCTION 0x0002 +#define BP_INSN_SIZE 2 +#define MAX_UINSN_BYTES 6 + +#define BREAKPOINT_SIGNAL SIGILL +#define SSTEP_SIGNAL SIGTRAP + +#ifdef CONFIG_COMPAT +#define SLOT_IP (test_tsk_thread_flag(current, TIF_31BIT) ? 0x04 : 0x08) +#else +#define SLOT_IP 0x08 +#endif + +#define FIXUP_PSW_NORMAL 0x08 +#define FIXUP_BRANCH_NOT_TAKEN 0x04 +#define FIXUP_RETURN_REGISTER 0x02 +#define FIXUP_NOT_REQUIRED 0x01 + +/* Architecture specific switch for where the IP points after a bp hit */ +#define ARCH_BP_INST_PTR(inst_ptr) (inst_ptr - BP_INSN_SIZE) + +struct uprobe_probept; +struct uprobe_task; +static int arch_validate_probed_insn(struct uprobe_probept *ppt); + +/* + * On s390, a trap leaves the instruction pointer pointing past the + * trap instruction. + */ +static inline unsigned long arch_get_probept(struct pt_regs *regs) +{ + return (unsigned long) (regs->psw.addr - BP_INSN_SIZE); +} + +static inline void arch_reset_ip_for_sstep(struct pt_regs *regs) +{ + regs->psw.addr -= BP_INSN_SIZE; +} + +#ifdef CONFIG_URETPROBES +static inline void arch_restore_uret_addr(unsigned long ret_addr, + struct pt_regs *regs) +{ + regs->psw.addr = ret_addr; +} + +static unsigned long arch_hijack_uret_addr(unsigned long trampoline_addr, + struct pt_regs *regs, struct uprobe_task *utask); + +#endif /* CONFIG_URETPROBES */ +#endif /* _ASM_UPROBES_H */ |