Commit eacf4c02 authored by Christophe Leroy's avatar Christophe Leroy Committed by Michael Ellerman
Browse files

powerpc: Enable OPTPROBES on PPC32



For that, create a 32 bits version of patch_imm64_load_insns()
and create a patch_imm_load_insns() which calls
patch_imm32_load_insns() on PPC32 and patch_imm64_load_insns()
on PPC64.

Adapt optprobes_head.S for PPC32. Use PPC_LL/PPC_STL macros instead
of raw ld/std, opt out things linked to paca and use stmw/lmw to
save/restore registers.

Signed-off-by: default avatarChristophe Leroy <christophe.leroy@csgroup.eu>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/bad58c66859b2a475c0ad516b53164ae3b4853cd.1618927318.git.christophe.leroy@csgroup.eu
parent 693557eb
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -229,7 +229,7 @@ config PPC
	select HAVE_MOD_ARCH_SPECIFIC
	select HAVE_NMI				if PERF_EVENTS || (PPC64 && PPC_BOOK3S)
	select HAVE_HARDLOCKUP_DETECTOR_ARCH	if PPC64 && PPC_BOOK3S && SMP
	select HAVE_OPTPROBES			if PPC64
	select HAVE_OPTPROBES
	select HAVE_PERF_EVENTS
	select HAVE_PERF_EVENTS_NMI		if PPC64
	select HAVE_HARDLOCKUP_DETECTOR_PERF	if PERF_EVENTS && HAVE_PERF_EVENTS_NMI && !HAVE_HARDLOCKUP_DETECTOR_ARCH
+21 −3
Original line number Diff line number Diff line
@@ -141,11 +141,21 @@ void arch_remove_optimized_kprobe(struct optimized_kprobe *op)
	}
}

static void patch_imm32_load_insns(unsigned long val, int reg, kprobe_opcode_t *addr)
{
	patch_instruction((struct ppc_inst *)addr,
			  ppc_inst(PPC_RAW_LIS(reg, IMM_H(val))));
	addr++;

	patch_instruction((struct ppc_inst *)addr,
			  ppc_inst(PPC_RAW_ORI(reg, reg, IMM_L(val))));
}

/*
 * Generate instructions to load provided immediate 64-bit value
 * to register 'reg' and patch these instructions at 'addr'.
 */
static void patch_imm64_load_insns(unsigned long val, int reg, kprobe_opcode_t *addr)
static void patch_imm64_load_insns(unsigned long long val, int reg, kprobe_opcode_t *addr)
{
	/* lis reg,(op)@highest */
	patch_instruction((struct ppc_inst *)addr,
@@ -177,6 +187,14 @@ static void patch_imm64_load_insns(unsigned long val, int reg, kprobe_opcode_t *
				   ___PPC_RS(reg) | (val & 0xffff)));
}

static void patch_imm_load_insns(unsigned long val, int reg, kprobe_opcode_t *addr)
{
	if (IS_ENABLED(CONFIG_PPC64))
		patch_imm64_load_insns(val, reg, addr);
	else
		patch_imm32_load_insns(val, reg, addr);
}

int arch_prepare_optimized_kprobe(struct optimized_kprobe *op, struct kprobe *p)
{
	struct ppc_inst branch_op_callback, branch_emulate_step, temp;
@@ -230,7 +248,7 @@ int arch_prepare_optimized_kprobe(struct optimized_kprobe *op, struct kprobe *p)
	 * Fixup the template with instructions to:
	 * 1. load the address of the actual probepoint
	 */
	patch_imm64_load_insns((unsigned long)op, 3, buff + TMPL_OP_IDX);
	patch_imm_load_insns((unsigned long)op, 3, buff + TMPL_OP_IDX);

	/*
	 * 2. branch to optimized_callback() and emulate_step()
@@ -264,7 +282,7 @@ int arch_prepare_optimized_kprobe(struct optimized_kprobe *op, struct kprobe *p)
	 * 3. load instruction to be emulated into relevant register, and
	 */
	temp = ppc_inst_read((struct ppc_inst *)p->ainsn.insn);
	patch_imm64_load_insns(ppc_inst_as_ulong(temp), 4, buff + TMPL_INSN_IDX);
	patch_imm_load_insns(ppc_inst_as_ulong(temp), 4, buff + TMPL_INSN_IDX);

	/*
	 * 4. branch back from trampoline
+34 −31
Original line number Diff line number Diff line
@@ -9,6 +9,16 @@
#include <asm/ptrace.h>
#include <asm/asm-offsets.h>

#ifdef CONFIG_PPC64
#define SAVE_30GPRS(base) SAVE_10GPRS(2,base); SAVE_10GPRS(12,base); SAVE_10GPRS(22,base)
#define REST_30GPRS(base) REST_10GPRS(2,base); REST_10GPRS(12,base); REST_10GPRS(22,base)
#define TEMPLATE_FOR_IMM_LOAD_INSNS	nop; nop; nop; nop; nop
#else
#define SAVE_30GPRS(base) stmw	r2, GPR2(base)
#define REST_30GPRS(base) lmw	r2, GPR2(base)
#define TEMPLATE_FOR_IMM_LOAD_INSNS	nop; nop; nop
#endif

#define	OPT_SLOT_SIZE	65536

	.balign	4
@@ -30,39 +40,41 @@ optinsn_slot:
	.global optprobe_template_entry
optprobe_template_entry:
	/* Create an in-memory pt_regs */
	stdu	r1,-INT_FRAME_SIZE(r1)
	PPC_STLU	r1,-INT_FRAME_SIZE(r1)
	SAVE_GPR(0,r1)
	/* Save the previous SP into stack */
	addi	r0,r1,INT_FRAME_SIZE
	std	r0,GPR1(r1)
	SAVE_10GPRS(2,r1)
	SAVE_10GPRS(12,r1)
	SAVE_10GPRS(22,r1)
	PPC_STL	r0,GPR1(r1)
	SAVE_30GPRS(r1)
	/* Save SPRS */
	mfmsr	r5
	std	r5,_MSR(r1)
	PPC_STL	r5,_MSR(r1)
	li	r5,0x700
	std	r5,_TRAP(r1)
	PPC_STL	r5,_TRAP(r1)
	li	r5,0
	std	r5,ORIG_GPR3(r1)
	std	r5,RESULT(r1)
	PPC_STL	r5,ORIG_GPR3(r1)
	PPC_STL	r5,RESULT(r1)
	mfctr	r5
	std	r5,_CTR(r1)
	PPC_STL	r5,_CTR(r1)
	mflr	r5
	std	r5,_LINK(r1)
	PPC_STL	r5,_LINK(r1)
	mfspr	r5,SPRN_XER
	std	r5,_XER(r1)
	PPC_STL	r5,_XER(r1)
	mfcr	r5
	std	r5,_CCR(r1)
	PPC_STL	r5,_CCR(r1)
#ifdef CONFIG_PPC64
	lbz     r5,PACAIRQSOFTMASK(r13)
	std     r5,SOFTE(r1)
#endif

	/*
	 * We may get here from a module, so load the kernel TOC in r2.
	 * The original TOC gets restored when pt_regs is restored
	 * further below.
	 */
#ifdef CONFIG_PPC64
	ld	r2,PACATOC(r13)
#endif

	.global optprobe_template_op_address
optprobe_template_op_address:
@@ -70,11 +82,8 @@ optprobe_template_op_address:
	 * Parameters to optimized_callback():
	 * 1. optimized_kprobe structure in r3
	 */
	nop
	nop
	nop
	nop
	nop
	TEMPLATE_FOR_IMM_LOAD_INSNS

	/* 2. pt_regs pointer in r4 */
	addi	r4,r1,STACK_FRAME_OVERHEAD

@@ -92,11 +101,7 @@ optprobe_template_call_handler:
	.global optprobe_template_insn
optprobe_template_insn:
	/* 2, Pass instruction to be emulated in r4 */
	nop
	nop
	nop
	nop
	nop
	TEMPLATE_FOR_IMM_LOAD_INSNS

	.global optprobe_template_call_emulate
optprobe_template_call_emulate:
@@ -107,20 +112,18 @@ optprobe_template_call_emulate:
	 * All done.
	 * Now, restore the registers...
	 */
	ld	r5,_MSR(r1)
	PPC_LL	r5,_MSR(r1)
	mtmsr	r5
	ld	r5,_CTR(r1)
	PPC_LL	r5,_CTR(r1)
	mtctr	r5
	ld	r5,_LINK(r1)
	PPC_LL	r5,_LINK(r1)
	mtlr	r5
	ld	r5,_XER(r1)
	PPC_LL	r5,_XER(r1)
	mtxer	r5
	ld	r5,_CCR(r1)
	PPC_LL	r5,_CCR(r1)
	mtcr	r5
	REST_GPR(0,r1)
	REST_10GPRS(2,r1)
	REST_10GPRS(12,r1)
	REST_10GPRS(22,r1)
	REST_30GPRS(r1)
	/* Restore the previous SP */
	addi	r1,r1,INT_FRAME_SIZE