Commit ba640a59 authored by Martin Schwidefsky's avatar Martin Schwidefsky Committed by Martin Schwidefsky
Browse files

[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: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 5a8b589f
Loading
Loading
Loading
Loading
+0 −5
Original line number Diff line number Diff line
@@ -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,
+57 −73
Original line number Diff line number Diff line
@@ -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)
{
	/* 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)
static int __kprobes is_prohibited_opcode(kprobe_opcode_t *insn)
{
	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 -
	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);
}