Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

[S390] kprobes: single step cleanup



The saved interrupt mask and the saved control registers are only
relevant while single stepping is set up. A secondary kprobe while
kprobe single stepping is active may not occur. That makes is safe
to remove the save and restore of kprobe_saved_imask / kprobe_save_ctl
from save_previous_kprobe and restore_previous_kprobe.
Move all single step related code to two functions, enable_singlestep
and disable_singlestep.

Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 35f2aaa7
Loading
Loading
Loading
Loading
+0 −3
Original line number Original line Diff line number Diff line
@@ -72,9 +72,6 @@ struct ins_replace_args {
struct prev_kprobe {
struct prev_kprobe {
	struct kprobe *kp;
	struct kprobe *kp;
	unsigned long status;
	unsigned long status;
	unsigned long saved_psw;
	unsigned long kprobe_saved_imask;
	unsigned long kprobe_saved_ctl[3];
};
};


/* per-cpu kprobe control block */
/* per-cpu kprobe control block */
+36 −41
Original line number Original line Diff line number Diff line
@@ -198,51 +198,58 @@ void __kprobes arch_remove_kprobe(struct kprobe *p)
	}
	}
}
}


static void __kprobes prepare_singlestep(struct kprobe *p, struct pt_regs *regs)
static void __kprobes enable_singlestep(struct kprobe_ctlblk *kcb,
					struct pt_regs *regs,
					unsigned long ip)
{
{
	per_cr_bits kprobe_per_regs[1];
	per_cr_bits kprobe_per_regs[1];


	memset(kprobe_per_regs, 0, sizeof(per_cr_bits));
	regs->psw.addr = (unsigned long)p->ainsn.insn | PSW_ADDR_AMODE;

	/* Set up the per control reg info, will pass to lctl */
	/* Set up the per control reg info, will pass to lctl */
	memset(kprobe_per_regs, 0, sizeof(per_cr_bits));
	kprobe_per_regs[0].em_instruction_fetch = 1;
	kprobe_per_regs[0].em_instruction_fetch = 1;
	kprobe_per_regs[0].starting_addr = (unsigned long)p->ainsn.insn;
	kprobe_per_regs[0].starting_addr = ip;
	kprobe_per_regs[0].ending_addr = (unsigned long)p->ainsn.insn + 1;
	kprobe_per_regs[0].ending_addr = ip;

	/* Save control regs and psw mask */
	__ctl_store(kcb->kprobe_saved_ctl, 9, 11);
	kcb->kprobe_saved_imask = regs->psw.mask &
		(PSW_MASK_PER | PSW_MASK_IO | PSW_MASK_EXT);


	/* Set the PER control regs, turns on single step for this address */
	/* Set PER control regs, turns on single step for the given address */
	__ctl_load(kprobe_per_regs, 9, 11);
	__ctl_load(kprobe_per_regs, 9, 11);
	regs->psw.mask |= PSW_MASK_PER;
	regs->psw.mask |= PSW_MASK_PER;
	regs->psw.mask &= ~(PSW_MASK_IO | PSW_MASK_EXT);
	regs->psw.mask &= ~(PSW_MASK_IO | PSW_MASK_EXT);
	regs->psw.addr = ip | PSW_ADDR_AMODE;
}

static void __kprobes disable_singlestep(struct kprobe_ctlblk *kcb,
					 struct pt_regs *regs,
					 unsigned long ip)
{
	/* Restore control regs and psw mask, set new psw address */
	__ctl_load(kcb->kprobe_saved_ctl, 9, 11);
	regs->psw.mask &= ~PSW_MASK_PER;
	regs->psw.mask |= kcb->kprobe_saved_imask;
	regs->psw.addr = ip | PSW_ADDR_AMODE;
}
}



static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb)
static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb)
{
{
	kcb->prev_kprobe.kp = kprobe_running();
	kcb->prev_kprobe.kp = kprobe_running();
	kcb->prev_kprobe.status = kcb->kprobe_status;
	kcb->prev_kprobe.status = kcb->kprobe_status;
	kcb->prev_kprobe.kprobe_saved_imask = kcb->kprobe_saved_imask;
	memcpy(kcb->prev_kprobe.kprobe_saved_ctl, kcb->kprobe_saved_ctl,
					sizeof(kcb->kprobe_saved_ctl));
}
}


static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb)
static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb)
{
{
	__get_cpu_var(current_kprobe) = kcb->prev_kprobe.kp;
	__get_cpu_var(current_kprobe) = kcb->prev_kprobe.kp;
	kcb->kprobe_status = kcb->prev_kprobe.status;
	kcb->kprobe_status = kcb->prev_kprobe.status;
	kcb->kprobe_saved_imask = kcb->prev_kprobe.kprobe_saved_imask;
	memcpy(kcb->kprobe_saved_ctl, kcb->prev_kprobe.kprobe_saved_ctl,
					sizeof(kcb->kprobe_saved_ctl));
}
}


static void __kprobes set_current_kprobe(struct kprobe *p, struct pt_regs *regs,
static void __kprobes set_current_kprobe(struct kprobe *p, struct pt_regs *regs,
						struct kprobe_ctlblk *kcb)
						struct kprobe_ctlblk *kcb)
{
{
	__get_cpu_var(current_kprobe) = p;
	__get_cpu_var(current_kprobe) = p;
	/* Save the interrupt and per flags */
	kcb->kprobe_saved_imask = regs->psw.mask &
		(PSW_MASK_PER | PSW_MASK_IO | PSW_MASK_EXT);
	/* Save the control regs that govern PER */
	__ctl_store(kcb->kprobe_saved_ctl, 9, 11);
}
}


void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
@@ -282,7 +289,8 @@ static int __kprobes kprobe_handler(struct pt_regs *regs)
			save_previous_kprobe(kcb);
			save_previous_kprobe(kcb);
			set_current_kprobe(p, regs, kcb);
			set_current_kprobe(p, regs, kcb);
			kprobes_inc_nmissed_count(p);
			kprobes_inc_nmissed_count(p);
			prepare_singlestep(p, regs);
			enable_singlestep(kcb, regs,
					  (unsigned long) p->ainsn.insn);
			kcb->kprobe_status = KPROBE_REENTER;
			kcb->kprobe_status = KPROBE_REENTER;
			return 1;
			return 1;
		} else {
		} else {
@@ -311,7 +319,7 @@ static int __kprobes kprobe_handler(struct pt_regs *regs)
		return 1;
		return 1;


ss_probe:
ss_probe:
	prepare_singlestep(p, regs);
	enable_singlestep(kcb, regs, (unsigned long) p->ainsn.insn);
	kcb->kprobe_status = KPROBE_HIT_SS;
	kcb->kprobe_status = KPROBE_HIT_SS;
	return 1;
	return 1;


@@ -433,31 +441,20 @@ static int __kprobes trampoline_probe_handler(struct kprobe *p,
static void __kprobes resume_execution(struct kprobe *p, struct pt_regs *regs)
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;
	regs->psw.addr &= PSW_ADDR_INSN;


	if (p->ainsn.fixup & FIXUP_PSW_NORMAL)
	if (p->ainsn.fixup & FIXUP_PSW_NORMAL)
		regs->psw.addr = (unsigned long)p->addr +
		ip += (unsigned long) p->addr - (unsigned long) p->ainsn.insn;
				((unsigned long)regs->psw.addr -
				 (unsigned long)p->ainsn.insn);


	if (p->ainsn.fixup & FIXUP_BRANCH_NOT_TAKEN)
	if (p->ainsn.fixup & FIXUP_BRANCH_NOT_TAKEN)
		if ((unsigned long)regs->psw.addr -
		if (ip - (unsigned long) p->ainsn.insn == p->ainsn.ilen)
		    (unsigned long)p->ainsn.insn == p->ainsn.ilen)
			ip = (unsigned long) p->addr + p->ainsn.ilen;
			regs->psw.addr = (unsigned long)p->addr + p->ainsn.ilen;


	if (p->ainsn.fixup & FIXUP_RETURN_REGISTER)
	if (p->ainsn.fixup & FIXUP_RETURN_REGISTER)
		regs->gprs[p->ainsn.reg] = ((unsigned long)p->addr +
		regs->gprs[p->ainsn.reg] += (unsigned long) p->addr -
						(regs->gprs[p->ainsn.reg] -
					    (unsigned long) p->ainsn.insn;
						(unsigned long)p->ainsn.insn))
						| PSW_ADDR_AMODE;


	regs->psw.addr |= PSW_ADDR_AMODE;
	disable_singlestep(kcb, regs, ip);
	/* turn off PER mode */
	regs->psw.mask &= ~PSW_MASK_PER;
	/* Restore the original per control regs */
	__ctl_load(kcb->kprobe_saved_ctl, 9, 11);
	regs->psw.mask |= kcb->kprobe_saved_imask;
}
}


static int __kprobes post_kprobe_handler(struct pt_regs *regs)
static int __kprobes post_kprobe_handler(struct pt_regs *regs)
@@ -515,9 +512,7 @@ static int __kprobes kprobe_trap_handler(struct pt_regs *regs, int trapnr)
		 * and allow the page fault handler to continue as a
		 * and allow the page fault handler to continue as a
		 * normal page fault.
		 * normal page fault.
		 */
		 */
		regs->psw.addr = (unsigned long)cur->addr | PSW_ADDR_AMODE;
		disable_singlestep(kcb, regs, (unsigned long) cur->addr);
		regs->psw.mask &= ~PSW_MASK_PER;
		regs->psw.mask |= kcb->kprobe_saved_imask;
		if (kcb->kprobe_status == KPROBE_REENTER)
		if (kcb->kprobe_status == KPROBE_REENTER)
			restore_previous_kprobe(kcb);
			restore_previous_kprobe(kcb);
		else {
		else {