diff options
author | kenistoj <kenistoj> | 2007-10-10 23:31:37 +0000 |
---|---|---|
committer | kenistoj <kenistoj> | 2007-10-10 23:31:37 +0000 |
commit | 4d3b883963b7ba3452ac641b633c89dcb92f0ef1 (patch) | |
tree | 4925aca604996e464c2ee8e292dc96071ee7dfec /runtime/uprobes/uprobes_s390.c | |
parent | ff0232d1767c1a2ef4bdfbf58d84ef8f6787be55 (diff) | |
download | systemtap-steved-4d3b883963b7ba3452ac641b633c89dcb92f0ef1.tar.gz systemtap-steved-4d3b883963b7ba3452ac641b633c89dcb92f0ef1.tar.xz systemtap-steved-4d3b883963b7ba3452ac641b633c89dcb92f0ef1.zip |
* runtime/uprobes/uprobes_ppc64.[ch]: Added
* runtime/uprobes/uprobes_s390.[ch]: Added
Diffstat (limited to 'runtime/uprobes/uprobes_s390.c')
-rw-r--r-- | runtime/uprobes/uprobes_s390.c | 208 |
1 files changed, 208 insertions, 0 deletions
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; +} |