|
@@ -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);
|
|
|
}
|