[S390] kprobes: instruction fixup

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>
This commit is contained in:
Martin Schwidefsky 2011-01-05 12:47:19 +01:00 committed by Martin Schwidefsky
parent 5a8b589f8a
commit ba640a5915
2 changed files with 57 additions and 78 deletions

View File

@ -59,9 +59,6 @@ typedef u16 kprobe_opcode_t;
struct arch_specific_insn { struct arch_specific_insn {
/* copy of original instruction */ /* copy of original instruction */
kprobe_opcode_t *insn; kprobe_opcode_t *insn;
int fixup;
int ilen;
int reg;
}; };
struct prev_kprobe { struct prev_kprobe {
@ -83,8 +80,6 @@ struct kprobe_ctlblk {
void arch_remove_kprobe(struct kprobe *p); void arch_remove_kprobe(struct kprobe *p);
void kretprobe_trampoline(void); 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_fault_handler(struct pt_regs *regs, int trapnr);
int kprobe_exceptions_notify(struct notifier_block *self, int kprobe_exceptions_notify(struct notifier_block *self,

View File

@ -37,29 +37,9 @@ DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);
struct kretprobe_blackpoint kretprobe_blacklist[] = {{NULL, NULL}}; 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 */ switch (insn[0] >> 8) {
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) {
case 0x0c: /* bassm */ case 0x0c: /* bassm */
case 0x0b: /* bsm */ case 0x0b: /* bsm */
case 0x83: /* diag */ case 0x83: /* diag */
@ -68,7 +48,7 @@ int __kprobes is_prohibited_opcode(kprobe_opcode_t *instruction)
case 0xad: /* stosm */ case 0xad: /* stosm */
return -EINVAL; return -EINVAL;
} }
switch (*(__u16 *) instruction) { switch (insn[0]) {
case 0x0101: /* pr */ case 0x0101: /* pr */
case 0xb25a: /* bsa */ case 0xb25a: /* bsa */
case 0xb240: /* bakr */ case 0xb240: /* bakr */
@ -81,80 +61,79 @@ int __kprobes is_prohibited_opcode(kprobe_opcode_t *instruction)
return 0; 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 */ /* default fixup method */
ainsn->fixup = FIXUP_PSW_NORMAL; int fixup = FIXUP_PSW_NORMAL;
/* save r1 operand */ switch (insn[0] >> 8) {
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;
}
switch (*(__u8 *) ainsn->insn) {
case 0x05: /* balr */ case 0x05: /* balr */
case 0x0d: /* basr */ case 0x0d: /* basr */
ainsn->fixup = FIXUP_RETURN_REGISTER; fixup = FIXUP_RETURN_REGISTER;
/* if r2 = 0, no branch will be taken */ /* if r2 = 0, no branch will be taken */
if ((*ainsn->insn & 0x0f) == 0) if ((insn[0] & 0x0f) == 0)
ainsn->fixup |= FIXUP_BRANCH_NOT_TAKEN; fixup |= FIXUP_BRANCH_NOT_TAKEN;
break; break;
case 0x06: /* bctr */ case 0x06: /* bctr */
case 0x07: /* bcr */ case 0x07: /* bcr */
ainsn->fixup = FIXUP_BRANCH_NOT_TAKEN; fixup = FIXUP_BRANCH_NOT_TAKEN;
break; break;
case 0x45: /* bal */ case 0x45: /* bal */
case 0x4d: /* bas */ case 0x4d: /* bas */
ainsn->fixup = FIXUP_RETURN_REGISTER; fixup = FIXUP_RETURN_REGISTER;
break; break;
case 0x47: /* bc */ case 0x47: /* bc */
case 0x46: /* bct */ case 0x46: /* bct */
case 0x86: /* bxh */ case 0x86: /* bxh */
case 0x87: /* bxle */ case 0x87: /* bxle */
ainsn->fixup = FIXUP_BRANCH_NOT_TAKEN; fixup = FIXUP_BRANCH_NOT_TAKEN;
break; break;
case 0x82: /* lpsw */ case 0x82: /* lpsw */
ainsn->fixup = FIXUP_NOT_REQUIRED; fixup = FIXUP_NOT_REQUIRED;
break; break;
case 0xb2: /* lpswe */ case 0xb2: /* lpswe */
if (*(((__u8 *) ainsn->insn) + 1) == 0xb2) { if ((insn[0] & 0xff) == 0xb2)
ainsn->fixup = FIXUP_NOT_REQUIRED; fixup = FIXUP_NOT_REQUIRED;
}
break; break;
case 0xa7: /* bras */ case 0xa7: /* bras */
if ((*ainsn->insn & 0x0f) == 0x05) { if ((insn[0] & 0x0f) == 0x05)
ainsn->fixup |= FIXUP_RETURN_REGISTER; fixup |= FIXUP_RETURN_REGISTER;
}
break; break;
case 0xc0: case 0xc0:
if ((*ainsn->insn & 0x0f) == 0x00 /* larl */ if ((insn[0] & 0x0f) == 0x00 || /* larl */
|| (*ainsn->insn & 0x0f) == 0x05) /* brasl */ (insn[0] & 0x0f) == 0x05) /* brasl */
ainsn->fixup |= FIXUP_RETURN_REGISTER; fixup |= FIXUP_RETURN_REGISTER;
break; break;
case 0xeb: case 0xeb:
if (*(((__u8 *) ainsn->insn) + 5 ) == 0x44 || /* bxhg */ if ((insn[2] & 0xff) == 0x44 || /* bxhg */
*(((__u8 *) ainsn->insn) + 5) == 0x45) {/* bxleg */ (insn[2] & 0xff) == 0x45) /* bxleg */
ainsn->fixup = FIXUP_BRANCH_NOT_TAKEN; fixup = FIXUP_BRANCH_NOT_TAKEN;
}
break; break;
case 0xe3: /* bctg */ case 0xe3: /* bctg */
if (*(((__u8 *) ainsn->insn) + 5) == 0x46) { if ((insn[2] & 0xff) == 0x46)
ainsn->fixup = FIXUP_BRANCH_NOT_TAKEN; fixup = FIXUP_BRANCH_NOT_TAKEN;
}
break; 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 { 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(); struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
unsigned long ip = regs->psw.addr & PSW_ADDR_INSN; 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; ip += (unsigned long) p->addr - (unsigned long) p->ainsn.insn;
if (p->ainsn.fixup & FIXUP_BRANCH_NOT_TAKEN) if (fixup & FIXUP_BRANCH_NOT_TAKEN) {
if (ip - (unsigned long) p->ainsn.insn == p->ainsn.ilen) int ilen = ((p->ainsn.insn[0] >> 14) + 3) & -2;
ip = (unsigned long) p->addr + p->ainsn.ilen; if (ip - (unsigned long) p->ainsn.insn == ilen)
ip = (unsigned long) p->addr + ilen;
}
if (p->ainsn.fixup & FIXUP_RETURN_REGISTER) if (fixup & FIXUP_RETURN_REGISTER) {
regs->gprs[p->ainsn.reg] += (unsigned long) p->addr - int reg = (p->ainsn.insn[0] & 0xf0) >> 4;
(unsigned long) p->ainsn.insn; regs->gprs[reg] += (unsigned long) p->addr -
(unsigned long) p->ainsn.insn;
}
disable_singlestep(kcb, regs, ip); disable_singlestep(kcb, regs, ip);
} }