[S390] kprobes: instruction fixup
authorMartin Schwidefsky <schwidefsky@de.ibm.com>
Wed, 5 Jan 2011 11:47:19 +0000 (12:47 +0100)
committerMartin Schwidefsky <sky@mschwide.boeblingen.de.ibm.com>
Wed, 5 Jan 2011 11:47:23 +0000 (12:47 +0100)
Determine instruction fixup details in resume_execution, no need to do
it beforehand. Remove fixup, ilen and reg from arch_specific_insn.

Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
arch/s390/include/asm/kprobes.h
arch/s390/kernel/kprobes.c

index e45b3d3..787c6a8 100644 (file)
@@ -59,9 +59,6 @@ typedef u16 kprobe_opcode_t;
 struct arch_specific_insn {
        /* copy of original instruction */
        kprobe_opcode_t *insn;
-       int fixup;
-       int ilen;
-       int reg;
 };
 
 struct prev_kprobe {
@@ -83,8 +80,6 @@ struct kprobe_ctlblk {
 
 void arch_remove_kprobe(struct kprobe *p);
 void kretprobe_trampoline(void);
-int  is_prohibited_opcode(kprobe_opcode_t *instruction);
-void get_instruction_type(struct arch_specific_insn *ainsn);
 
 int kprobe_fault_handler(struct pt_regs *regs, int trapnr);
 int kprobe_exceptions_notify(struct notifier_block *self,
index 1e75ec5..fcbc258 100644 (file)
@@ -37,29 +37,9 @@ DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);
 
 struct kretprobe_blackpoint kretprobe_blacklist[] = {{NULL, NULL}};
 
-int __kprobes arch_prepare_kprobe(struct kprobe *p)
+static int __kprobes is_prohibited_opcode(kprobe_opcode_t *insn)
 {
-       /* Make sure the probe isn't going on a difficult instruction */
-       if (is_prohibited_opcode((kprobe_opcode_t *) p->addr))
-               return -EINVAL;
-
-       if ((unsigned long)p->addr & 0x01)
-               return -EINVAL;
-
-       /* Use the get_insn_slot() facility for correctness */
-       if (!(p->ainsn.insn = get_insn_slot()))
-               return -ENOMEM;
-
-       memcpy(p->ainsn.insn, p->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t));
-
-       get_instruction_type(&p->ainsn);
-       p->opcode = *p->addr;
-       return 0;
-}
-
-int __kprobes is_prohibited_opcode(kprobe_opcode_t *instruction)
-{
-       switch (*(__u8 *) instruction) {
+       switch (insn[0] >> 8) {
        case 0x0c:      /* bassm */
        case 0x0b:      /* bsm   */
        case 0x83:      /* diag  */
@@ -68,7 +48,7 @@ int __kprobes is_prohibited_opcode(kprobe_opcode_t *instruction)
        case 0xad:      /* stosm */
                return -EINVAL;
        }
-       switch (*(__u16 *) instruction) {
+       switch (insn[0]) {
        case 0x0101:    /* pr    */
        case 0xb25a:    /* bsa   */
        case 0xb240:    /* bakr  */
@@ -81,80 +61,79 @@ int __kprobes is_prohibited_opcode(kprobe_opcode_t *instruction)
        return 0;
 }
 
-void __kprobes get_instruction_type(struct arch_specific_insn *ainsn)
+static int __kprobes get_fixup_type(kprobe_opcode_t *insn)
 {
        /* default fixup method */
-       ainsn->fixup = FIXUP_PSW_NORMAL;
-
-       /* save r1 operand */
-       ainsn->reg = (*ainsn->insn & 0xf0) >> 4;
-
-       /* save the instruction length (pop 5-5) in bytes */
-       switch (*(__u8 *) (ainsn->insn) >> 6) {
-       case 0:
-               ainsn->ilen = 2;
-               break;
-       case 1:
-       case 2:
-               ainsn->ilen = 4;
-               break;
-       case 3:
-               ainsn->ilen = 6;
-               break;
-       }
+       int fixup = FIXUP_PSW_NORMAL;
 
-       switch (*(__u8 *) ainsn->insn) {
+       switch (insn[0] >> 8) {
        case 0x05:      /* balr */
        case 0x0d:      /* basr */
-               ainsn->fixup = FIXUP_RETURN_REGISTER;
+               fixup = FIXUP_RETURN_REGISTER;
                /* if r2 = 0, no branch will be taken */
-               if ((*ainsn->insn & 0x0f) == 0)
-                       ainsn->fixup |= FIXUP_BRANCH_NOT_TAKEN;
+               if ((insn[0] & 0x0f) == 0)
+                       fixup |= FIXUP_BRANCH_NOT_TAKEN;
                break;
        case 0x06:      /* bctr */
        case 0x07:      /* bcr  */
-               ainsn->fixup = FIXUP_BRANCH_NOT_TAKEN;
+               fixup = FIXUP_BRANCH_NOT_TAKEN;
                break;
        case 0x45:      /* bal  */
        case 0x4d:      /* bas  */
-               ainsn->fixup = FIXUP_RETURN_REGISTER;
+               fixup = FIXUP_RETURN_REGISTER;
                break;
        case 0x47:      /* bc   */
        case 0x46:      /* bct  */
        case 0x86:      /* bxh  */
        case 0x87:      /* bxle */
-               ainsn->fixup = FIXUP_BRANCH_NOT_TAKEN;
+               fixup = FIXUP_BRANCH_NOT_TAKEN;
                break;
        case 0x82:      /* lpsw */
-               ainsn->fixup = FIXUP_NOT_REQUIRED;
+               fixup = FIXUP_NOT_REQUIRED;
                break;
        case 0xb2:      /* lpswe */
-               if (*(((__u8 *) ainsn->insn) + 1) == 0xb2) {
-                       ainsn->fixup = FIXUP_NOT_REQUIRED;
-               }
+               if ((insn[0] & 0xff) == 0xb2)
+                       fixup = FIXUP_NOT_REQUIRED;
                break;
        case 0xa7:      /* bras */
-               if ((*ainsn->insn & 0x0f) == 0x05) {
-                       ainsn->fixup |= FIXUP_RETURN_REGISTER;
-               }
+               if ((insn[0] & 0x0f) == 0x05)
+                       fixup |= FIXUP_RETURN_REGISTER;
                break;
        case 0xc0:
-               if ((*ainsn->insn & 0x0f) == 0x00  /* larl  */
-                       || (*ainsn->insn & 0x0f) == 0x05) /* brasl */
-               ainsn->fixup |= FIXUP_RETURN_REGISTER;
+               if ((insn[0] & 0x0f) == 0x00 || /* larl  */
+                   (insn[0] & 0x0f) == 0x05)   /* brasl */
+               fixup |= FIXUP_RETURN_REGISTER;
                break;
        case 0xeb:
-               if (*(((__u8 *) ainsn->insn) + 5 ) == 0x44 ||   /* bxhg  */
-                       *(((__u8 *) ainsn->insn) + 5) == 0x45) {/* bxleg */
-                       ainsn->fixup = FIXUP_BRANCH_NOT_TAKEN;
-               }
+               if ((insn[2] & 0xff) == 0x44 || /* bxhg  */
+                   (insn[2] & 0xff) == 0x45)   /* bxleg */
+                       fixup = FIXUP_BRANCH_NOT_TAKEN;
                break;
        case 0xe3:      /* bctg */
-               if (*(((__u8 *) ainsn->insn) + 5) == 0x46) {
-                       ainsn->fixup = FIXUP_BRANCH_NOT_TAKEN;
-               }
+               if ((insn[2] & 0xff) == 0x46)
+                       fixup = FIXUP_BRANCH_NOT_TAKEN;
                break;
        }
+       return fixup;
+}
+
+int __kprobes arch_prepare_kprobe(struct kprobe *p)
+{
+       if ((unsigned long) p->addr & 0x01)
+               return -EINVAL;
+
+       /* Make sure the probe isn't going on a difficult instruction */
+       if (is_prohibited_opcode((kprobe_opcode_t *) p->addr))
+               return -EINVAL;
+
+       /* Use the get_insn_slot() facility for correctness */
+       if (!(p->ainsn.insn = get_insn_slot()))
+               return -ENOMEM;
+
+       p->opcode = *p->addr;
+       memcpy(p->ainsn.insn, p->addr, ((p->opcode >> 14) + 3) & -2);
+
+       return 0;
 }
 
 struct ins_replace_args {
@@ -444,17 +423,22 @@ static void __kprobes resume_execution(struct kprobe *p, struct pt_regs *regs)
 {
        struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
        unsigned long ip = regs->psw.addr & PSW_ADDR_INSN;
+       int fixup = get_fixup_type(p->ainsn.insn);
 
-       if (p->ainsn.fixup & FIXUP_PSW_NORMAL)
+       if (fixup & FIXUP_PSW_NORMAL)
                ip += (unsigned long) p->addr - (unsigned long) p->ainsn.insn;
 
-       if (p->ainsn.fixup & FIXUP_BRANCH_NOT_TAKEN)
-               if (ip - (unsigned long) p->ainsn.insn == p->ainsn.ilen)
-                       ip = (unsigned long) p->addr + p->ainsn.ilen;
+       if (fixup & FIXUP_BRANCH_NOT_TAKEN) {
+               int ilen = ((p->ainsn.insn[0] >> 14) + 3) & -2;
+               if (ip - (unsigned long) p->ainsn.insn == ilen)
+                       ip = (unsigned long) p->addr + ilen;
+       }
 
-       if (p->ainsn.fixup & FIXUP_RETURN_REGISTER)
-               regs->gprs[p->ainsn.reg] += (unsigned long) p->addr -
-                                           (unsigned long) p->ainsn.insn;
+       if (fixup & FIXUP_RETURN_REGISTER) {
+               int reg = (p->ainsn.insn[0] & 0xf0) >> 4;
+               regs->gprs[reg] += (unsigned long) p->addr -
+                                  (unsigned long) p->ainsn.insn;
+       }
 
        disable_singlestep(kcb, regs, ip);
 }