code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * arch/arm/probes/kprobes/actions-arm.c
 *
 * Copyright (C) 2006, 2007 Motorola Inc.
 */

/*
 * We do not have hardware single-stepping on ARM, This
 * effort is further complicated by the ARM not having a
 * "next PC" register.  Instructions that change the PC
 * can't be safely single-stepped in a MP environment, so
 * we have a lot of work to do:
 *
 * In the prepare phase:
 *   *) If it is an instruction that does anything
 *      with the CPU mode, we reject it for a kprobe.
 *      (This is out of laziness rather than need.  The
 *      instructions could be simulated.)
 *
 *   *) Otherwise, decode the instruction rewriting its
 *      registers to take fixed, ordered registers and
 *      setting a handler for it to run the instruction.
 *
 * In the execution phase by an instruction's handler:
 *
 *   *) If the PC is written to by the instruction, the
 *      instruction must be fully simulated in software.
 *
 *   *) Otherwise, a modified form of the instruction is
 *      directly executed.  Its handler calls the
 *      instruction in insn[0].  In insn[1] is a
 *      "mov pc, lr" to return.
 *
 *      Before calling, load up the reordered registers
 *      from the original instruction's registers.  If one
 *      of the original input registers is the PC, compute
 *      and adjust the appropriate input register.
 *
 *	After call completes, copy the output registers to
 *      the original instruction's original registers.
 *
 * We don't use a real breakpoint instruction since that
 * would have us in the kernel go from SVC mode to SVC
 * mode losing the link register.  Instead we use an
 * undefined instruction.  To simplify processing, the
 * undefined instruction used for kprobes must be reserved
 * exclusively for kprobes use.
 *
 * TODO: ifdef out some instruction decoding based on architecture.
 */

#include <linux/kernel.h>
#include <linux/kprobes.h>
#include <linux/ptrace.h>

#include "../decode-arm.h"
#include "core.h"
#include "checkers.h"

#if  __LINUX_ARM_ARCH__ >= 6
#define BLX(reg)	"blx	"reg"		\n\t"
#else
#define BLX(reg)	"mov	lr, pc		\n\t"	\
			"mov	pc, "reg"	\n\t"
#endif

static void __kprobes
emulate_ldrdstrd(probes_opcode_t insn,
	struct arch_probes_insn *asi, struct pt_regs *regs)
{
	unsigned long pc = regs->ARM_pc + 4;
	int rt = (insn >> 12) & 0xf;
	int rn = (insn >> 16) & 0xf;
	int rm = insn & 0xf;

	register unsigned long rtv asm("r0") = regs->uregs[rt];
	register unsigned long rt2v asm("r1") = regs->uregs[rt+1];
	register unsigned long rnv asm("r2") = (rn == 15) ? pc
							  : regs->uregs[rn];
	register unsigned long rmv asm("r3") = regs->uregs[rm];

	__asm__ __volatile__ (
		BLX("%[fn]")
		: "=r" (rtv), "=r" (rt2v), "=r" (rnv)
		: "0" (rtv), "1" (rt2v), "2" (rnv), "r" (rmv),
		  [fn] "r" (asi->insn_fn)
		: "lr", "memory", "cc"
	);

	regs->uregs[rt] = rtv;
	regs->uregs[rt+1] = rt2v;
	if (is_writeback(insn))
		regs->uregs[rn] = rnv;
}

static void __kprobes
emulate_ldr(probes_opcode_t insn,
	struct arch_probes_insn *asi, struct pt_regs *regs)
{
	unsigned long pc = regs->ARM_pc + 4;
	int rt = (insn >> 12) & 0xf;
	int rn = (insn >> 16) & 0xf;
	int rm = insn & 0xf;

	register unsigned long rtv asm("r0");
	register unsigned long rnv asm("r2") = (rn == 15) ? pc
							  : regs->uregs[rn];
	register unsigned long rmv asm("r3") = regs->uregs[rm];

	__asm__ __volatile__ (
		BLX("%[fn]")
		: "=r" (rtv), "=r" (rnv)
		: "1" (rnv), "r" (rmv), [fn] "r" (asi->insn_fn)
		: "lr", "memory", "cc"
	);

	if (rt == 15)
		load_write_pc(rtv, regs);
	else
		regs->uregs[rt] = rtv;

	if (is_writeback(insn))
		regs->uregs[rn] = rnv;
}

static void __kprobes
emulate_str(probes_opcode_t insn,
	struct arch_probes_insn *asi, struct pt_regs *regs)
{
	unsigned long rtpc = regs->ARM_pc - 4 + str_pc_offset;
	unsigned long rnpc = regs->ARM_pc + 4;
	int rt = (insn >> 12) & 0xf;
	int rn = (insn >> 16) & 0xf;
	int rm = insn & 0xf;

	register unsigned long rtv asm("r0") = (rt == 15) ? rtpc
							  : regs->uregs[rt];
	register unsigned long rnv asm("r2") = (rn == 15) ? rnpc
							  : regs->uregs[rn];
	register unsigned long rmv asm("r3") = regs->uregs[rm];

	__asm__ __volatile__ (
		BLX("%[fn]")
		: "=r" (rnv)
		: "r" (rtv), "0" (rnv), "r" (rmv), [fn] "r" (asi->insn_fn)
		: "lr", "memory", "cc"
	);

	if (is_writeback(insn))
		regs->uregs[rn] = rnv;
}

static void __kprobes
emulate_rd12rn16rm0rs8_rwflags(probes_opcode_t insn,
	struct arch_probes_insn *asi, struct pt_regs *regs)
{
	unsigned long pc = regs->ARM_pc + 4;
	int rd = (insn >> 12) & 0xf;
	int rn = (insn >> 16) & 0xf;
	int rm = insn & 0xf;
	int rs = (insn >> 8) & 0xf;

	register unsigned long rdv asm("r0") = regs->uregs[rd];
	register unsigned long rnv asm("r2") = (rn == 15) ? pc
							  : regs->uregs[rn];
	register unsigned long rmv asm("r3") = (rm == 15) ? pc
							  : regs->uregs[rm];
	register unsigned long rsv asm("r1") = regs->uregs[rs];
	unsigned long cpsr = regs->ARM_cpsr;

	__asm__ __volatile__ (
		"msr	cpsr_fs, %[cpsr]	\n\t"
		BLX("%[fn]")
		"mrs	%[cpsr], cpsr		\n\t"
		: "=r" (rdv), [cpsr] "=r" (cpsr)
		: "0" (rdv), "r" (rnv), "r" (rmv), "r" (rsv),
		  "1" (cpsr), [fn] "r" (asi->insn_fn)
		: "lr", "memory", "cc"
	);

	if (rd == 15)
		alu_write_pc(rdv, regs);
	else
		regs->uregs[rd] = rdv;
	regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
}

static void __kprobes
emulate_rd12rn16rm0_rwflags_nopc(probes_opcode_t insn,
	struct arch_probes_insn *asi, struct pt_regs *regs)
{
	int rd = (insn >> 12) & 0xf;
	int rn = (insn >> 16) & 0xf;
	int rm = insn & 0xf;

	register unsigned long rdv asm("r0") = regs->uregs[rd];
	register unsigned long rnv asm("r2") = regs->uregs[rn];
	register unsigned long rmv asm("r3") = regs->uregs[rm];
	unsigned long cpsr = regs->ARM_cpsr;

	__asm__ __volatile__ (
		"msr	cpsr_fs, %[cpsr]	\n\t"
		BLX("%[fn]")
		"mrs	%[cpsr], cpsr		\n\t"
		: "=r" (rdv), [cpsr] "=r" (cpsr)
		: "0" (rdv), "r" (rnv), "r" (rmv),
		  "1" (cpsr), [fn] "r" (asi->insn_fn)
		: "lr", "memory", "cc"
	);

	regs->uregs[rd] = rdv;
	regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
}

static void __kprobes
emulate_rd16rn12rm0rs8_rwflags_nopc(probes_opcode_t insn,
	struct arch_probes_insn *asi,
	struct pt_regs *regs)
{
	int rd = (insn >> 16) & 0xf;
	int rn = (insn >> 12) & 0xf;
	int rm = insn & 0xf;
	int rs = (insn >> 8) & 0xf;

	register unsigned long rdv asm("r2") = regs->uregs[rd];
	register unsigned long rnv asm("r0") = regs->uregs[rn];
	register unsigned long rmv asm("r3") = regs->uregs[rm];
	register unsigned long rsv asm("r1") = regs->uregs[rs];
	unsigned long cpsr = regs->ARM_cpsr;

	__asm__ __volatile__ (
		"msr	cpsr_fs, %[cpsr]	\n\t"
		BLX("%[fn]")
		"mrs	%[cpsr], cpsr		\n\t"
		: "=r" (rdv), [cpsr] "=r" (cpsr)
		: "0" (rdv), "r" (rnv), "r" (rmv), "r" (rsv),
		  "1" (cpsr), [fn] "r" (asi->insn_fn)
		: "lr", "memory", "cc"
	);

	regs->uregs[rd] = rdv;
	regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
}

static void __kprobes
emulate_rd12rm0_noflags_nopc(probes_opcode_t insn,
	struct arch_probes_insn *asi, struct pt_regs *regs)
{
	int rd = (insn >> 12) & 0xf;
	int rm = insn & 0xf;

	register unsigned long rdv asm("r0") = regs->uregs[rd];
	register unsigned long rmv asm("r3") = regs->uregs[rm];

	__asm__ __volatile__ (
		BLX("%[fn]")
		: "=r" (rdv)
		: "0" (rdv), "r" (rmv), [fn] "r" (asi->insn_fn)
		: "lr", "memory", "cc"
	);

	regs->uregs[rd] = rdv;
}

static void __kprobes
emulate_rdlo12rdhi16rn0rm8_rwflags_nopc(probes_opcode_t insn,
	struct arch_probes_insn *asi,
	struct pt_regs *regs)
{
	int rdlo = (insn >> 12) & 0xf;
	int rdhi = (insn >> 16) & 0xf;
	int rn = insn & 0xf;
	int rm = (insn >> 8) & 0xf;

	register unsigned long rdlov asm("r0") = regs->uregs[rdlo];
	register unsigned long rdhiv asm("r2") = regs->uregs[rdhi];
	register unsigned long rnv asm("r3") = regs->uregs[rn];
	register unsigned long rmv asm("r1") = regs->uregs[rm];
	unsigned long cpsr = regs->ARM_cpsr;

	__asm__ __volatile__ (
		"msr	cpsr_fs, %[cpsr]	\n\t"
		BLX("%[fn]")
		"mrs	%[cpsr], cpsr		\n\t"
		: "=r" (rdlov), "=r" (rdhiv), [cpsr] "=r" (cpsr)
		: "0" (rdlov), "1" (rdhiv), "r" (rnv), "r" (rmv),
		  "2" (cpsr), [fn] "r" (asi->insn_fn)
		: "lr", "memory", "cc"
	);

	regs->uregs[rdlo] = rdlov;
	regs->uregs[rdhi] = rdhiv;
	regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
}

const union decode_action kprobes_arm_actions[NUM_PROBES_ARM_ACTIONS] = {
	[PROBES_PRELOAD_IMM] = {.handler = probes_simulate_nop},
	[PROBES_PRELOAD_REG] = {.handler = probes_simulate_nop},
	[PROBES_BRANCH_IMM] = {.handler = simulate_blx1},
	[PROBES_MRS] = {.handler = simulate_mrs},
	[PROBES_BRANCH_REG] = {.handler = simulate_blx2bx},
	[PROBES_CLZ] = {.handler = emulate_rd12rm0_noflags_nopc},
	[PROBES_SATURATING_ARITHMETIC] = {
		.handler = emulate_rd12rn16rm0_rwflags_nopc},
	[PROBES_MUL1] = {.handler = emulate_rdlo12rdhi16rn0rm8_rwflags_nopc},
	[PROBES_MUL2] = {.handler = emulate_rd16rn12rm0rs8_rwflags_nopc},
	[PROBES_SWP] = {.handler = emulate_rd12rn16rm0_rwflags_nopc},
	[PROBES_LDRSTRD] = {.handler = emulate_ldrdstrd},
	[PROBES_LOAD_EXTRA] = {.handler = emulate_ldr},
	[PROBES_LOAD] = {.handler = emulate_ldr},
	[PROBES_STORE_EXTRA] = {.handler = emulate_str},
	[PROBES_STORE] = {.handler = emulate_str},
	[PROBES_MOV_IP_SP] = {.handler = simulate_mov_ipsp},
	[PROBES_DATA_PROCESSING_REG] = {
		.handler = emulate_rd12rn16rm0rs8_rwflags},
	[PROBES_DATA_PROCESSING_IMM] = {
		.handler = emulate_rd12rn16rm0rs8_rwflags},
	[PROBES_MOV_HALFWORD] = {.handler = emulate_rd12rm0_noflags_nopc},
	[PROBES_SEV] = {.handler = probes_emulate_none},
	[PROBES_WFE] = {.handler = probes_simulate_nop},
	[PROBES_SATURATE] = {.handler = emulate_rd12rn16rm0_rwflags_nopc},
	[PROBES_REV] = {.handler = emulate_rd12rm0_noflags_nopc},
	[PROBES_MMI] = {.handler = emulate_rd12rn16rm0_rwflags_nopc},
	[PROBES_PACK] = {.handler = emulate_rd12rn16rm0_rwflags_nopc},
	[PROBES_EXTEND] = {.handler = emulate_rd12rm0_noflags_nopc},
	[PROBES_EXTEND_ADD] = {.handler = emulate_rd12rn16rm0_rwflags_nopc},
	[PROBES_MUL_ADD_LONG] = {
		.handler = emulate_rdlo12rdhi16rn0rm8_rwflags_nopc},
	[PROBES_MUL_ADD] = {.handler = emulate_rd16rn12rm0rs8_rwflags_nopc},
	[PROBES_BITFIELD] = {.handler = emulate_rd12rm0_noflags_nopc},
	[PROBES_BRANCH] = {.handler = simulate_bbl},
	[PROBES_LDMSTM] = {.decoder = kprobe_decode_ldmstm}
};

const struct decode_checker *kprobes_arm_checkers[] = {arm_stack_checker, arm_regs_checker, NULL};


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * arch/arm/probes/kprobes/actions-common.c
 *
 * Copyright (C) 2011 Jon Medhurst <tixy@yxit.co.uk>.
 *
 * Some contents moved here from arch/arm/include/asm/kprobes-arm.c which is
 * Copyright (C) 2006, 2007 Motorola Inc.
 */

#include <linux/kernel.h>
#include <linux/kprobes.h>
#include <asm/opcodes.h>

#include "core.h"

static void __kprobes simulate_ldm1stm1(probes_opcode_t insn,
		struct arch_probes_insn *asi,
		struct pt_regs *regs)
{
	int rn = (insn >> 16) & 0xf;
	int lbit = insn & (1 << 20);
	int wbit = insn & (1 << 21);
	int ubit = insn & (1 << 23);
	int pbit = insn & (1 << 24);
	long *addr = (long *)regs->uregs[rn];
	int reg_bit_vector;
	int reg_count;

	reg_count = 0;
	reg_bit_vector = insn & 0xffff;
	while (reg_bit_vector) {
		reg_bit_vector &= (reg_bit_vector - 1);
		++reg_count;
	}

	if (!ubit)
		addr -= reg_count;
	addr += (!pbit == !ubit);

	reg_bit_vector = insn & 0xffff;
	while (reg_bit_vector) {
		int reg = __ffs(reg_bit_vector);
		reg_bit_vector &= (reg_bit_vector - 1);
		if (lbit)
			regs->uregs[reg] = *addr++;
		else
			*addr++ = regs->uregs[reg];
	}

	if (wbit) {
		if (!ubit)
			addr -= reg_count;
		addr -= (!pbit == !ubit);
		regs->uregs[rn] = (long)addr;
	}
}

static void __kprobes simulate_stm1_pc(probes_opcode_t insn,
	struct arch_probes_insn *asi,
	struct pt_regs *regs)
{
	unsigned long addr = regs->ARM_pc - 4;

	regs->ARM_pc = (long)addr + str_pc_offset;
	simulate_ldm1stm1(insn, asi, regs);
	regs->ARM_pc = (long)addr + 4;
}

static void __kprobes simulate_ldm1_pc(probes_opcode_t insn,
	struct arch_probes_insn *asi,
	struct pt_regs *regs)
{
	simulate_ldm1stm1(insn, asi, regs);
	load_write_pc(regs->ARM_pc, regs);
}

static void __kprobes
emulate_generic_r0_12_noflags(probes_opcode_t insn,
	struct arch_probes_insn *asi, struct pt_regs *regs)
{
	register void *rregs asm("r1") = regs;
	register void *rfn asm("lr") = asi->insn_fn;

	__asm__ __volatile__ (
ARM(		"stmdb	sp!, {%[regs], r11}	\n\t"	)
THUMB(		"stmdb	sp!, {%[regs], r7}	\n\t"	)
		"ldmia	%[regs], {r0-r12}	\n\t"
#if __LINUX_ARM_ARCH__ >= 6
		"blx	%[fn]			\n\t"
#else
		"str	%[fn], [sp, #-4]!	\n\t"
		"adr	lr, 1f			\n\t"
		"ldr	pc, [sp], #4		\n\t"
		"1:				\n\t"
#endif
		"ldr	lr, [sp], #4		\n\t" /* lr = regs */
		"stmia	lr, {r0-r12}		\n\t"
ARM(		"ldr	r11, [sp], #4		\n\t"	)
THUMB(		"ldr	r7, [sp], #4		\n\t"	)
		: [regs] "=r" (rregs), [fn] "=r" (rfn)
		: "0" (rregs), "1" (rfn)
		: "r0", "r2", "r3", "r4", "r5", "r6", ARM("r7") THUMB("r11"),
		  "r8", "r9", "r10", "r12", "memory", "cc"
		);
}

static void __kprobes
emulate_generic_r2_14_noflags(probes_opcode_t insn,
	struct arch_probes_insn *asi, struct pt_regs *regs)
{
	emulate_generic_r0_12_noflags(insn, asi,
		(struct pt_regs *)(regs->uregs+2));
}

static void __kprobes
emulate_ldm_r3_15(probes_opcode_t insn,
	struct arch_probes_insn *asi, struct pt_regs *regs)
{
	emulate_generic_r0_12_noflags(insn, asi,
		(struct pt_regs *)(regs->uregs+3));
	load_write_pc(regs->ARM_pc, regs);
}

enum probes_insn __kprobes
kprobe_decode_ldmstm(probes_opcode_t insn, struct arch_probes_insn *asi,
		const struct decode_header *h)
{
	probes_insn_handler_t *handler = 0;
	unsigned reglist = insn & 0xffff;
	int is_ldm = insn & 0x100000;
	int rn = (insn >> 16) & 0xf;

	if (rn <= 12 && (reglist & 0xe000) == 0) {
		/* Instruction only uses registers in the range R0..R12 */
		handler = emulate_generic_r0_12_noflags;

	} else if (rn >= 2 && (reglist & 0x8003) == 0) {
		/* Instruction only uses registers in the range R2..R14 */
		rn -= 2;
		reglist >>= 2;
		handler = emulate_generic_r2_14_noflags;

	} else if (rn >= 3 && (reglist & 0x0007) == 0) {
		/* Instruction only uses registers in the range R3..R15 */
		if (is_ldm && (reglist & 0x8000)) {
			rn -= 3;
			reglist >>= 3;
			handler = emulate_ldm_r3_15;
		}
	}

	if (handler) {
		/* We can emulate the instruction in (possibly) modified form */
		asi->insn[0] = __opcode_to_mem_arm((insn & 0xfff00000) |
						   (rn << 16) | reglist);
		asi->insn_handler = handler;
		return INSN_GOOD;
	}

	/* Fallback to slower simulation... */
	if (reglist & 0x8000)
		handler = is_ldm ? simulate_ldm1_pc : simulate_stm1_pc;
	else
		handler = simulate_ldm1stm1;
	asi->insn_handler = handler;
	return INSN_GOOD_NO_SLOT;
}

repo_name: angery/linux


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * arch/arm/probes/kprobes/actions-thumb.c
 *
 * Copyright (C) 2011 Jon Medhurst <tixy@yxit.co.uk>.
 */

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/ptrace.h>
#include <linux/kprobes.h>

#include "../decode-thumb.h"
#include "core.h"
#include "checkers.h"

/* These emulation encodings are functionally equivalent... */
#define t32_emulate_rd8rn16rm0ra12_noflags \
		t32_emulate_rdlo12rdhi8rn16rm0_noflags

/* t32 thumb actions */

static void __kprobes
t32_simulate_table_branch(probes_opcode_t insn,
		struct arch_probes_insn *asi, struct pt_regs *regs)
{
	unsigned long pc = regs->ARM_pc;
	int rn = (insn >> 16) & 0xf;
	int rm = insn & 0xf;

	unsigned long rnv = (rn == 15) ? pc : regs->uregs[rn];
	unsigned long rmv = regs->uregs[rm];
	unsigned int halfwords;

	if (insn & 0x10) /* TBH */
		halfwords = ((u16 *)rnv)[rmv];
	else /* TBB */
		halfwords = ((u8 *)rnv)[rmv];

	regs->ARM_pc = pc + 2 * halfwords;
}

static void __kprobes
t32_simulate_mrs(probes_opcode_t insn,
		struct arch_probes_insn *asi, struct pt_regs *regs)
{
	int rd = (insn >> 8) & 0xf;
	unsigned long mask = 0xf8ff03df; /* Mask out execution state */
	regs->uregs[rd] = regs->ARM_cpsr & mask;
}

static void __kprobes
t32_simulate_cond_branch(probes_opcode_t insn,
		struct arch_probes_insn *asi, struct pt_regs *regs)
{
	unsigned long pc = regs->ARM_pc;

	long offset = insn & 0x7ff;		/* imm11 */
	offset += (insn & 0x003f0000) >> 5;	/* imm6 */
	offset += (insn & 0x00002000) << 4;	/* J1 */
	offset += (insn & 0x00000800) << 7;	/* J2 */
	offset -= (insn & 0x04000000) >> 7;	/* Apply sign bit */

	regs->ARM_pc = pc + (offset * 2);
}

static enum probes_insn __kprobes
t32_decode_cond_branch(probes_opcode_t insn, struct arch_probes_insn *asi,
		const struct decode_header *d)
{
	int cc = (insn >> 22) & 0xf;
	asi->insn_check_cc = probes_condition_checks[cc];
	asi->insn_handler = t32_simulate_cond_branch;
	return INSN_GOOD_NO_SLOT;
}

static void __kprobes
t32_simulate_branch(probes_opcode_t insn,
		    struct arch_probes_insn *asi, struct pt_regs *regs)
{
	unsigned long pc = regs->ARM_pc;

	long offset = insn & 0x7ff;		/* imm11 */
	offset += (insn & 0x03ff0000) >> 5;	/* imm10 */
	offset += (insn & 0x00002000) << 9;	/* J1 */
	offset += (insn & 0x00000800) << 10;	/* J2 */
	if (insn & 0x04000000)
		offset -= 0x00800000; /* Apply sign bit */
	else
		offset ^= 0x00600000; /* Invert J1 and J2 */

	if (insn & (1 << 14)) {
		/* BL or BLX */
		regs->ARM_lr = regs->ARM_pc | 1;
		if (!(insn & (1 << 12))) {
			/* BLX so switch to ARM mode */
			regs->ARM_cpsr &= ~PSR_T_BIT;
			pc &= ~3;
		}
	}

	regs->ARM_pc = pc + (offset * 2);
}

static void __kprobes
t32_simulate_ldr_literal(probes_opcode_t insn,
		struct arch_probes_insn *asi, struct pt_regs *regs)
{
	unsigned long addr = regs->ARM_pc & ~3;
	int rt = (insn >> 12) & 0xf;
	unsigned long rtv;

	long offset = insn & 0xfff;
	if (insn & 0x00800000)
		addr += offset;
	else
		addr -= offset;

	if (insn & 0x00400000) {
		/* LDR */
		rtv = *(unsigned long *)addr;
		if (rt == 15) {
			bx_write_pc(rtv, regs);
			return;
		}
	} else if (insn & 0x00200000) {
		/* LDRH */
		if (insn & 0x01000000)
			rtv = *(s16 *)addr;
		else
			rtv = *(u16 *)addr;
	} else {
		/* LDRB */
		if (insn & 0x01000000)
			rtv = *(s8 *)addr;
		else
			rtv = *(u8 *)addr;
	}

	regs->uregs[rt] = rtv;
}

static enum probes_insn __kprobes
t32_decode_ldmstm(probes_opcode_t insn, struct arch_probes_insn *asi,
		const struct decode_header *d)
{
	enum probes_insn ret = kprobe_decode_ldmstm(insn, asi, d);

	/* Fixup modified instruction to have halfwords in correct order...*/
	insn = __mem_to_opcode_arm(asi->insn[0]);
	((u16 *)asi->insn)[0] = __opcode_to_mem_thumb16(insn >> 16);
	((u16 *)asi->insn)[1] = __opcode_to_mem_thumb16(insn & 0xffff);

	return ret;
}

static void __kprobes
t32_emulate_ldrdstrd(probes_opcode_t insn,
		struct arch_probes_insn *asi, struct pt_regs *regs)
{
	unsigned long pc = regs->ARM_pc & ~3;
	int rt1 = (insn >> 12) & 0xf;
	int rt2 = (insn >> 8) & 0xf;
	int rn = (insn >> 16) & 0xf;

	register unsigned long rt1v asm("r0") = regs->uregs[rt1];
	register unsigned long rt2v asm("r1") = regs->uregs[rt2];
	register unsigned long rnv asm("r2") = (rn == 15) ? pc
							  : regs->uregs[rn];

	__asm__ __volatile__ (
		"blx    %[fn]"
		: "=r" (rt1v), "=r" (rt2v), "=r" (rnv)
		: "0" (rt1v), "1" (rt2v), "2" (rnv), [fn] "r" (asi->insn_fn)
		: "lr", "memory", "cc"
	);

	if (rn != 15)
		regs->uregs[rn] = rnv; /* Writeback base register */
	regs->uregs[rt1] = rt1v;
	regs->uregs[rt2] = rt2v;
}

static void __kprobes
t32_emulate_ldrstr(probes_opcode_t insn,
		struct arch_probes_insn *asi, struct pt_regs *regs)
{
	int rt = (insn >> 12) & 0xf;
	int rn = (insn >> 16) & 0xf;
	int rm = insn & 0xf;

	register unsigned long rtv asm("r0") = regs->uregs[rt];
	register unsigned long rnv asm("r2") = regs->uregs[rn];
	register unsigned long rmv asm("r3") = regs->uregs[rm];

	__asm__ __volatile__ (
		"blx    %[fn]"
		: "=r" (rtv), "=r" (rnv)
		: "0" (rtv), "1" (rnv), "r" (rmv), [fn] "r" (asi->insn_fn)
		: "lr", "memory", "cc"
	);

	regs->uregs[rn] = rnv; /* Writeback base register */
	if (rt == 15) /* Can't be true for a STR as they aren't allowed */
		bx_write_pc(rtv, regs);
	else
		regs->uregs[rt] = rtv;
}

static void __kprobes
t32_emulate_rd8rn16rm0_rwflags(probes_opcode_t insn,
		struct arch_probes_insn *asi, struct pt_regs *regs)
{
	int rd = (insn >> 8) & 0xf;
	int rn = (insn >> 16) & 0xf;
	int rm = insn & 0xf;

	register unsigned long rdv asm("r1") = regs->uregs[rd];
	register unsigned long rnv asm("r2") = regs->uregs[rn];
	register unsigned long rmv asm("r3") = regs->uregs[rm];
	unsigned long cpsr = regs->ARM_cpsr;

	__asm__ __volatile__ (
		"msr	cpsr_fs, %[cpsr]	\n\t"
		"blx    %[fn]			\n\t"
		"mrs	%[cpsr], cpsr		\n\t"
		: "=r" (rdv), [cpsr] "=r" (cpsr)
		: "0" (rdv), "r" (rnv), "r" (rmv),
		  "1" (cpsr), [fn] "r" (asi->insn_fn)
		: "lr", "memory", "cc"
	);

	regs->uregs[rd] = rdv;
	regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
}

static void __kprobes
t32_emulate_rd8pc16_noflags(probes_opcode_t insn,
		struct arch_probes_insn *asi, struct pt_regs *regs)
{
	unsigned long pc = regs->ARM_pc;
	int rd = (insn >> 8) & 0xf;

	register unsigned long rdv asm("r1") = regs->uregs[rd];
	register unsigned long rnv asm("r2") = pc & ~3;

	__asm__ __volatile__ (
		"blx    %[fn]"
		: "=r" (rdv)
		: "0" (rdv), "r" (rnv), [fn] "r" (asi->insn_fn)
		: "lr", "memory", "cc"
	);

	regs->uregs[rd] = rdv;
}

static void __kprobes
t32_emulate_rd8rn16_noflags(probes_opcode_t insn,
		struct arch_probes_insn *asi, struct pt_regs *regs)
{
	int rd = (insn >> 8) & 0xf;
	int rn = (insn >> 16) & 0xf;

	register unsigned long rdv asm("r1") = regs->uregs[rd];
	register unsigned long rnv asm("r2") = regs->uregs[rn];

	__asm__ __volatile__ (
		"blx    %[fn]"
		: "=r" (rdv)
		: "0" (rdv), "r" (rnv), [fn] "r" (asi->insn_fn)
		: "lr", "memory", "cc"
	);

	regs->uregs[rd] = rdv;
}

static void __kprobes
t32_emulate_rdlo12rdhi8rn16rm0_noflags(probes_opcode_t insn,
		struct arch_probes_insn *asi,
		struct pt_regs *regs)
{
	int rdlo = (insn >> 12) & 0xf;
	int rdhi = (insn >> 8) & 0xf;
	int rn = (insn >> 16) & 0xf;
	int rm = insn & 0xf;

	register unsigned long rdlov asm("r0") = regs->uregs[rdlo];
	register unsigned long rdhiv asm("r1") = regs->uregs[rdhi];
	register unsigned long rnv asm("r2") = regs->uregs[rn];
	register unsigned long rmv asm("r3") = regs->uregs[rm];

	__asm__ __volatile__ (
		"blx    %[fn]"
		: "=r" (rdlov), "=r" (rdhiv)
		: "0" (rdlov), "1" (rdhiv), "r" (rnv), "r" (rmv),
		  [fn] "r" (asi->insn_fn)
		: "lr", "memory", "cc"
	);

	regs->uregs[rdlo] = rdlov;
	regs->uregs[rdhi] = rdhiv;
}
/* t16 thumb actions */

static void __kprobes
t16_simulate_bxblx(probes_opcode_t insn,
		struct arch_probes_insn *asi, struct pt_regs *regs)
{
	unsigned long pc = regs->ARM_pc + 2;
	int rm = (insn >> 3) & 0xf;
	unsigned long rmv = (rm == 15) ? pc : regs->uregs[rm];

	if (insn & (1 << 7)) /* BLX ? */
		regs->ARM_lr = regs->ARM_pc | 1;

	bx_write_pc(rmv, regs);
}

static void __kprobes
t16_simulate_ldr_literal(probes_opcode_t insn,
		struct arch_probes_insn *asi, struct pt_regs *regs)
{
	unsigned long *base = (unsigned long *)((regs->ARM_pc + 2) & ~3);
	long index = insn & 0xff;
	int rt = (insn >> 8) & 0x7;
	regs->uregs[rt] = base[index];
}

static void __kprobes
t16_simulate_ldrstr_sp_relative(probes_opcode_t insn,
		struct arch_probes_insn *asi, struct pt_regs *regs)
{
	unsigned long* base = (unsigned long *)regs->ARM_sp;
	long index = insn & 0xff;
	int rt = (insn >> 8) & 0x7;
	if (insn & 0x800) /* LDR */
		regs->uregs[rt] = base[index];
	else /* STR */
		base[index] = regs->uregs[rt];
}

static void __kprobes
t16_simulate_reladr(probes_opcode_t insn,
		struct arch_probes_insn *asi, struct pt_regs *regs)
{
	unsigned long base = (insn & 0x800) ? regs->ARM_sp
					    : ((regs->ARM_pc + 2) & ~3);
	long offset = insn & 0xff;
	int rt = (insn >> 8) & 0x7;
	regs->uregs[rt] = base + offset * 4;
}

static void __kprobes
t16_simulate_add_sp_imm(probes_opcode_t insn,
		struct arch_probes_insn *asi, struct pt_regs *regs)
{
	long imm = insn & 0x7f;
	if (insn & 0x80) /* SUB */
		regs->ARM_sp -= imm * 4;
	else /* ADD */
		regs->ARM_sp += imm * 4;
}

static void __kprobes
t16_simulate_cbz(probes_opcode_t insn,
		struct arch_probes_insn *asi, struct pt_regs *regs)
{
	int rn = insn & 0x7;
	probes_opcode_t nonzero = regs->uregs[rn] ? insn : ~insn;
	if (nonzero & 0x800) {
		long i = insn & 0x200;
		long imm5 = insn & 0xf8;
		unsigned long pc = regs->ARM_pc + 2;
		regs->ARM_pc = pc + (i >> 3) + (imm5 >> 2);
	}
}

static void __kprobes
t16_simulate_it(probes_opcode_t insn,
		struct arch_probes_insn *asi, struct pt_regs *regs)
{
	/*
	 * The 8 IT state bits are split into two parts in CPSR:
	 *	ITSTATE<1:0> are in CPSR<26:25>
	 *	ITSTATE<7:2> are in CPSR<15:10>
	 * The new IT state is in the lower byte of insn.
	 */
	unsigned long cpsr = regs->ARM_cpsr;
	cpsr &= ~PSR_IT_MASK;
	cpsr |= (insn & 0xfc) << 8;
	cpsr |= (insn & 0x03) << 25;
	regs->ARM_cpsr = cpsr;
}

static void __kprobes
t16_singlestep_it(probes_opcode_t insn,
		  struct arch_probes_insn *asi, struct pt_regs *regs)
{
	regs->ARM_pc += 2;
	t16_simulate_it(insn, asi, regs);
}

static enum probes_insn __kprobes
t16_decode_it(probes_opcode_t insn, struct arch_probes_insn *asi,
		const struct decode_header *d)
{
	asi->insn_singlestep = t16_singlestep_it;
	return INSN_GOOD_NO_SLOT;
}

static void __kprobes
t16_simulate_cond_branch(probes_opcode_t insn,
		struct arch_probes_insn *asi, struct pt_regs *regs)
{
	unsigned long pc = regs->ARM_pc + 2;
	long offset = insn & 0x7f;
	offset -= insn & 0x80; /* Apply sign bit */
	regs->ARM_pc = pc + (offset * 2);
}

static enum probes_insn __kprobes
t16_decode_cond_branch(probes_opcode_t insn, struct arch_probes_insn *asi,
		const struct decode_header *d)
{
	int cc = (insn >> 8) & 0xf;
	asi->insn_check_cc = probes_condition_checks[cc];
	asi->insn_handler = t16_simulate_cond_branch;
	return INSN_GOOD_NO_SLOT;
}

static void __kprobes
t16_simulate_branch(probes_opcode_t insn,
		   struct arch_probes_insn *asi, struct pt_regs *regs)
{
	unsigned long pc = regs->ARM_pc + 2;
	long offset = insn & 0x3ff;
	offset -= insn & 0x400; /* Apply sign bit */
	regs->ARM_pc = pc + (offset * 2);
}

static unsigned long __kprobes
t16_emulate_loregs(probes_opcode_t insn,
		   struct arch_probes_insn *asi, struct pt_regs *regs)
{
	unsigned long oldcpsr = regs->ARM_cpsr;
	unsigned long newcpsr;

	__asm__ __volatile__ (
		"msr	cpsr_fs, %[oldcpsr]	\n\t"
		"mov	r11, r7			\n\t"
		"ldmia	%[regs], {r0-r7}	\n\t"
		"blx	%[fn]			\n\t"
		"stmia	%[regs], {r0-r7}	\n\t"
		"mov	r7, r11			\n\t"
		"mrs	%[newcpsr], cpsr	\n\t"
		: [newcpsr] "=r" (newcpsr)
		: [oldcpsr] "r" (oldcpsr), [regs] "r" (regs),
		  [fn] "r" (asi->insn_fn)
		: "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r11",
		  "lr", "memory", "cc"
		);

	return (oldcpsr & ~APSR_MASK) | (newcpsr & APSR_MASK);
}

static void __kprobes
t16_emulate_loregs_rwflags(probes_opcode_t insn,
		struct arch_probes_insn *asi, struct pt_regs *regs)
{
	regs->ARM_cpsr = t16_emulate_loregs(insn, asi, regs);
}

static void __kprobes
t16_emulate_loregs_noitrwflags(probes_opcode_t insn,
		struct arch_probes_insn *asi, struct pt_regs *regs)
{
	unsigned long cpsr = t16_emulate_loregs(insn, asi, regs);
	if (!in_it_block(cpsr))
		regs->ARM_cpsr = cpsr;
}

static void __kprobes
t16_emulate_hiregs(probes_opcode_t insn,
		struct arch_probes_insn *asi, struct pt_regs *regs)
{
	unsigned long pc = regs->ARM_pc + 2;
	int rdn = (insn & 0x7) | ((insn & 0x80) >> 4);
	int rm = (insn >> 3) & 0xf;

	register unsigned long rdnv asm("r1");
	register unsigned long rmv asm("r0");
	unsigned long cpsr = regs->ARM_cpsr;

	rdnv = (rdn == 15) ? pc : regs->uregs[rdn];
	rmv = (rm == 15) ? pc : regs->uregs[rm];

	__asm__ __volatile__ (
		"msr	cpsr_fs, %[cpsr]	\n\t"
		"blx    %[fn]			\n\t"
		"mrs	%[cpsr], cpsr		\n\t"
		: "=r" (rdnv), [cpsr] "=r" (cpsr)
		: "0" (rdnv), "r" (rmv), "1" (cpsr), [fn] "r" (asi->insn_fn)
		: "lr", "memory", "cc"
	);

	if (rdn == 15)
		rdnv &= ~1;

	regs->uregs[rdn] = rdnv;
	regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
}

static enum probes_insn __kprobes
t16_decode_hiregs(probes_opcode_t insn, struct arch_probes_insn *asi,
		const struct decode_header *d)
{
	insn &= ~0x00ff;
	insn |= 0x001; /* Set Rdn = R1 and Rm = R0 */
	((u16 *)asi->insn)[0] = __opcode_to_mem_thumb16(insn);
	asi->insn_handler = t16_emulate_hiregs;
	return INSN_GOOD;
}

static void __kprobes
t16_emulate_push(probes_opcode_t insn,
		struct arch_probes_insn *asi, struct pt_regs *regs)
{
	__asm__ __volatile__ (
		"mov	r11, r7			\n\t"
		"ldr	r9, [%[regs], #13*4]	\n\t"
		"ldr	r8, [%[regs], #14*4]	\n\t"
		"ldmia	%[regs], {r0-r7}	\n\t"
		"blx	%[fn]			\n\t"
		"str	r9, [%[regs], #13*4]	\n\t"
		"mov	r7, r11			\n\t"
		:
		: [regs] "r" (regs), [fn] "r" (asi->insn_fn)
		: "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r8", "r9", "r11",
		  "lr", "memory", "cc"
		);
}

static enum probes_insn __kprobes
t16_decode_push(probes_opcode_t insn, struct arch_probes_insn *asi,
		const struct decode_header *d)
{
	/*
	 * To simulate a PUSH we use a Thumb-2 "STMDB R9!, {registers}"
	 * and call it with R9=SP and LR in the register list represented
	 * by R8.
	 */
	/* 1st half STMDB R9!,{} */
	((u16 *)asi->insn)[0] = __opcode_to_mem_thumb16(0xe929);
	/* 2nd half (register list) */
	((u16 *)asi->insn)[1] = __opcode_to_mem_thumb16(insn & 0x1ff);
	asi->insn_handler = t16_emulate_push;
	return INSN_GOOD;
}

static void __kprobes
t16_emulate_pop_nopc(probes_opcode_t insn,
		struct arch_probes_insn *asi, struct pt_regs *regs)
{
	__asm__ __volatile__ (
		"mov	r11, r7			\n\t"
		"ldr	r9, [%[regs], #13*4]	\n\t"
		"ldmia	%[regs], {r0-r7}	\n\t"
		"blx	%[fn]			\n\t"
		"stmia	%[regs], {r0-r7}	\n\t"
		"str	r9, [%[regs], #13*4]	\n\t"
		"mov	r7, r11			\n\t"
		:
		: [regs] "r" (regs), [fn] "r" (asi->insn_fn)
		: "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r9", "r11",
		  "lr", "memory", "cc"
		);
}

static void __kprobes
t16_emulate_pop_pc(probes_opcode_t insn,
		struct arch_probes_insn *asi, struct pt_regs *regs)
{
	register unsigned long pc asm("r8");

	__asm__ __volatile__ (
		"mov	r11, r7			\n\t"
		"ldr	r9, [%[regs], #13*4]	\n\t"
		"ldmia	%[regs], {r0-r7}	\n\t"
		"blx	%[fn]			\n\t"
		"stmia	%[regs], {r0-r7}	\n\t"
		"str	r9, [%[regs], #13*4]	\n\t"
		"mov	r7, r11			\n\t"
		: "=r" (pc)
		: [regs] "r" (regs), [fn] "r" (asi->insn_fn)
		: "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r9", "r11",
		  "lr", "memory", "cc"
		);

	bx_write_pc(pc, regs);
}

static enum probes_insn __kprobes
t16_decode_pop(probes_opcode_t insn, struct arch_probes_insn *asi,
		const struct decode_header *d)
{
	/*
	 * To simulate a POP we use a Thumb-2 "LDMDB R9!, {registers}"
	 * and call it with R9=SP and PC in the register list represented
	 * by R8.
	 */
	/* 1st half LDMIA R9!,{} */
	((u16 *)asi->insn)[0] = __opcode_to_mem_thumb16(0xe8b9);
	/* 2nd half (register list) */
	((u16 *)asi->insn)[1] = __opcode_to_mem_thumb16(insn & 0x1ff);
	asi->insn_handler = insn & 0x100 ? t16_emulate_pop_pc
					 : t16_emulate_pop_nopc;
	return INSN_GOOD;
}

const union decode_action kprobes_t16_actions[NUM_PROBES_T16_ACTIONS] = {
	[PROBES_T16_ADD_SP] = {.handler = t16_simulate_add_sp_imm},
	[PROBES_T16_CBZ] = {.handler = t16_simulate_cbz},
	[PROBES_T16_SIGN_EXTEND] = {.handler = t16_emulate_loregs_rwflags},
	[PROBES_T16_PUSH] = {.decoder = t16_decode_push},
	[PROBES_T16_POP] = {.decoder = t16_decode_pop},
	[PROBES_T16_SEV] = {.handler = probes_emulate_none},
	[PROBES_T16_WFE] = {.handler = probes_simulate_nop},
	[PROBES_T16_IT] = {.decoder = t16_decode_it},
	[PROBES_T16_CMP] = {.handler = t16_emulate_loregs_rwflags},
	[PROBES_T16_ADDSUB] = {.handler = t16_emulate_loregs_noitrwflags},
	[PROBES_T16_LOGICAL] = {.handler = t16_emulate_loregs_noitrwflags},
	[PROBES_T16_LDR_LIT] = {.handler = t16_simulate_ldr_literal},
	[PROBES_T16_BLX] = {.handler = t16_simulate_bxblx},
	[PROBES_T16_HIREGOPS] = {.decoder = t16_decode_hiregs},
	[PROBES_T16_LDRHSTRH] = {.handler = t16_emulate_loregs_rwflags},
	[PROBES_T16_LDRSTR] = {.handler = t16_simulate_ldrstr_sp_relative},
	[PROBES_T16_ADR] = {.handler = t16_simulate_reladr},
	[PROBES_T16_LDMSTM] = {.handler = t16_emulate_loregs_rwflags},
	[PROBES_T16_BRANCH_COND] = {.decoder = t16_decode_cond_branch},
	[PROBES_T16_BRANCH] = {.handler = t16_simulate_branch},
};

const union decode_action kprobes_t32_actions[NUM_PROBES_T32_ACTIONS] = {
	[PROBES_T32_LDMSTM] = {.decoder = t32_decode_ldmstm},
	[PROBES_T32_LDRDSTRD] = {.handler = t32_emulate_ldrdstrd},
	[PROBES_T32_TABLE_BRANCH] = {.handler = t32_simulate_table_branch},
	[PROBES_T32_TST] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
	[PROBES_T32_MOV] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
	[PROBES_T32_ADDSUB] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
	[PROBES_T32_LOGICAL] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
	[PROBES_T32_CMP] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
	[PROBES_T32_ADDWSUBW_PC] = {.handler = t32_emulate_rd8pc16_noflags,},
	[PROBES_T32_ADDWSUBW] = {.handler = t32_emulate_rd8rn16_noflags},
	[PROBES_T32_MOVW] = {.handler = t32_emulate_rd8rn16_noflags},
	[PROBES_T32_SAT] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
	[PROBES_T32_BITFIELD] = {.handler = t32_emulate_rd8rn16_noflags},
	[PROBES_T32_SEV] = {.handler = probes_emulate_none},
	[PROBES_T32_WFE] = {.handler = probes_simulate_nop},
	[PROBES_T32_MRS] = {.handler = t32_simulate_mrs},
	[PROBES_T32_BRANCH_COND] = {.decoder = t32_decode_cond_branch},
	[PROBES_T32_BRANCH] = {.handler = t32_simulate_branch},
	[PROBES_T32_PLDI] = {.handler = probes_simulate_nop},
	[PROBES_T32_LDR_LIT] = {.handler = t32_simulate_ldr_literal},
	[PROBES_T32_LDRSTR] = {.handler = t32_emulate_ldrstr},
	[PROBES_T32_SIGN_EXTEND] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
	[PROBES_T32_MEDIA] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
	[PROBES_T32_REVERSE] = {.handler = t32_emulate_rd8rn16_noflags},
	[PROBES_T32_MUL_ADD] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
	[PROBES_T32_MUL_ADD2] = {.handler = t32_emulate_rd8rn16rm0ra12_noflags},
	[PROBES_T32_MUL_ADD_LONG] = {
		.handler = t32_emulate_rdlo12rdhi8rn16rm0_noflags},
};

const struct decode_checker *kprobes_t32_checkers[] = {t32_stack_checker, NULL};
const struct decode_checker *kprobes_t16_checkers[] = {t16_stack_checker, NULL};


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * arch/arm/probes/kprobes/checkers-arm.c
 *
 * Copyright (C) 2014 Huawei Inc.
 */

#include <linux/kernel.h>
#include "../decode.h"
#include "../decode-arm.h"
#include "checkers.h"

static enum probes_insn __kprobes arm_check_stack(probes_opcode_t insn,
		struct arch_probes_insn *asi,
		const struct decode_header *h)
{
	/*
	 * PROBES_LDRSTRD, PROBES_LDMSTM, PROBES_STORE,
	 * PROBES_STORE_EXTRA may get here. Simply mark all normal
	 * insns as STACK_USE_NONE.
	 */
	static const union decode_item table[] = {
		/*
		 * 'STR{,D,B,H}, Rt, [Rn, Rm]' should be marked as UNKNOWN
		 * if Rn or Rm is SP.
		 *                                 x
		 * STR (register)	cccc 011x x0x0 xxxx xxxx xxxx xxxx xxxx
		 * STRB (register)	cccc 011x x1x0 xxxx xxxx xxxx xxxx xxxx
		 */
		DECODE_OR	(0x0e10000f, 0x0600000d),
		DECODE_OR	(0x0e1f0000, 0x060d0000),

		/*
		 *                                                     x
		 * STRD (register)	cccc 000x x0x0 xxxx xxxx xxxx 1111 xxxx
		 * STRH (register)	cccc 000x x0x0 xxxx xxxx xxxx 1011 xxxx
		 */
		DECODE_OR	(0x0e5000bf, 0x000000bd),
		DECODE_CUSTOM	(0x0e5f00b0, 0x000d00b0, STACK_USE_UNKNOWN),

		/*
		 * For PROBES_LDMSTM, only stmdx sp, [...] need to examine
		 *
		 * Bit B/A (bit 24) encodes arithmetic operation order. 1 means
		 * before, 0 means after.
		 * Bit I/D (bit 23) encodes arithmetic operation. 1 means
		 * increment, 0 means decrement.
		 *
		 * So:
		 *                              B I
		 *                              / /
		 *                              A D   | Rn |
		 * STMDX SP, [...]	cccc 100x 00x0 xxxx xxxx xxxx xxxx xxxx
		 */
		DECODE_CUSTOM	(0x0edf0000, 0x080d0000, STACK_USE_STMDX),

		/*                              P U W | Rn | Rt |     imm12    |*/
		/* STR (immediate)	cccc 010x x0x0 1101 xxxx xxxx xxxx xxxx */
		/* STRB (immediate)	cccc 010x x1x0 1101 xxxx xxxx xxxx xxxx */
		/*                              P U W | Rn | Rt |imm4|    |imm4|*/
		/* STRD (immediate)	cccc 000x x1x0 1101 xxxx xxxx 1111 xxxx */
		/* STRH (immediate)	cccc 000x x1x0 1101 xxxx xxxx 1011 xxxx */
		/*
		 * index = (P == '1'); add = (U == '1').
		 * Above insns with:
		 *    index == 0 (str{,d,h} rx, [sp], #+/-imm) or
		 *    add == 1 (str{,d,h} rx, [sp, #+<imm>])
		 * should be STACK_USE_NONE.
		 * Only str{,b,d,h} rx,[sp,#-n] (P == 1 and U == 0) are
		 * required to be examined.
		 */
		/* STR{,B} Rt,[SP,#-n]	cccc 0101 0xx0 1101 xxxx xxxx xxxx xxxx */
		DECODE_CUSTOM	(0x0f9f0000, 0x050d0000, STACK_USE_FIXED_XXX),

		/* STR{D,H} Rt,[SP,#-n]	cccc 0001 01x0 1101 xxxx xxxx 1x11 xxxx */
		DECODE_CUSTOM	(0x0fdf00b0, 0x014d00b0, STACK_USE_FIXED_X0X),

		/* fall through */
		DECODE_CUSTOM	(0, 0, STACK_USE_NONE),
		DECODE_END
	};

	return probes_decode_insn(insn, asi, table, false, false, stack_check_actions, NULL);
}

const struct decode_checker arm_stack_checker[NUM_PROBES_ARM_ACTIONS] = {
	[PROBES_LDRSTRD] = {.checker = arm_check_stack},
	[PROBES_STORE_EXTRA] = {.checker = arm_check_stack},
	[PROBES_STORE] = {.checker = arm_check_stack},
	[PROBES_LDMSTM] = {.checker = arm_check_stack},
};

static enum probes_insn __kprobes arm_check_regs_nouse(probes_opcode_t insn,
		struct arch_probes_insn *asi,
		const struct decode_header *h)
{
	asi->register_usage_flags = 0;
	return INSN_GOOD;
}

static enum probes_insn arm_check_regs_normal(probes_opcode_t insn,
		struct arch_probes_insn *asi,
		const struct decode_header *h)
{
	u32 regs = h->type_regs.bits >> DECODE_TYPE_BITS;
	int i;

	asi->register_usage_flags = 0;
	for (i = 0; i < 5; regs >>= 4, insn >>= 4, i++)
		if (regs & 0xf)
			asi->register_usage_flags |= 1 << (insn & 0xf);

	return INSN_GOOD;
}

static enum probes_insn arm_check_regs_ldmstm(probes_opcode_t insn,
		struct arch_probes_insn *asi,
		const struct decode_header *h)
{
	unsigned int reglist = insn & 0xffff;
	unsigned int rn = (insn >> 16) & 0xf;
	asi->register_usage_flags = reglist | (1 << rn);
	return INSN_GOOD;
}

static enum probes_insn arm_check_regs_mov_ip_sp(probes_opcode_t insn,
		struct arch_probes_insn *asi,
		const struct decode_header *h)
{
	/* Instruction is 'mov ip, sp' i.e. 'mov r12, r13' */
	asi->register_usage_flags = (1 << 12) | (1<< 13);
	return INSN_GOOD;
}

/*
 *                                    | Rn |Rt/d|         | Rm |
 * LDRD (register)      cccc 000x x0x0 xxxx xxxx xxxx 1101 xxxx
 * STRD (register)      cccc 000x x0x0 xxxx xxxx xxxx 1111 xxxx
 *                                    | Rn |Rt/d|         |imm4L|
 * LDRD (immediate)     cccc 000x x1x0 xxxx xxxx xxxx 1101 xxxx
 * STRD (immediate)     cccc 000x x1x0 xxxx xxxx xxxx 1111 xxxx
 *
 * Such instructions access Rt/d and its next register, so different
 * from others, a specific checker is required to handle this extra
 * implicit register usage.
 */
static enum probes_insn arm_check_regs_ldrdstrd(probes_opcode_t insn,
		struct arch_probes_insn *asi,
		const struct decode_header *h)
{
	int rdt = (insn >> 12) & 0xf;
	arm_check_regs_normal(insn, asi, h);
	asi->register_usage_flags |= 1 << (rdt + 1);
	return INSN_GOOD;
}

const struct decode_checker arm_regs_checker[NUM_PROBES_ARM_ACTIONS] = {
	[PROBES_MRS] = {.checker = arm_check_regs_normal},
	[PROBES_SATURATING_ARITHMETIC] = {.checker = arm_check_regs_normal},
	[PROBES_MUL1] = {.checker = arm_check_regs_normal},
	[PROBES_MUL2] = {.checker = arm_check_regs_normal},
	[PROBES_MUL_ADD_LONG] = {.checker = arm_check_regs_normal},
	[PROBES_MUL_ADD] = {.checker = arm_check_regs_normal},
	[PROBES_LOAD] = {.checker = arm_check_regs_normal},
	[PROBES_LOAD_EXTRA] = {.checker = arm_check_regs_normal},
	[PROBES_STORE] = {.checker = arm_check_regs_normal},
	[PROBES_STORE_EXTRA] = {.checker = arm_check_regs_normal},
	[PROBES_DATA_PROCESSING_REG] = {.checker = arm_check_regs_normal},
	[PROBES_DATA_PROCESSING_IMM] = {.checker = arm_check_regs_normal},
	[PROBES_SEV] = {.checker = arm_check_regs_nouse},
	[PROBES_WFE] = {.checker = arm_check_regs_nouse},
	[PROBES_SATURATE] = {.checker = arm_check_regs_normal},
	[PROBES_REV] = {.checker = arm_check_regs_normal},
	[PROBES_MMI] = {.checker = arm_check_regs_normal},
	[PROBES_PACK] = {.checker = arm_check_regs_normal},
	[PROBES_EXTEND] = {.checker = arm_check_regs_normal},
	[PROBES_EXTEND_ADD] = {.checker = arm_check_regs_normal},
	[PROBES_BITFIELD] = {.checker = arm_check_regs_normal},
	[PROBES_LDMSTM] = {.checker = arm_check_regs_ldmstm},
	[PROBES_MOV_IP_SP] = {.checker = arm_check_regs_mov_ip_sp},
	[PROBES_LDRSTRD] = {.checker = arm_check_regs_ldrdstrd},
};


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * arch/arm/probes/kprobes/checkers-common.c
 *
 * Copyright (C) 2014 Huawei Inc.
 */

#include <linux/kernel.h>
#include "../decode.h"
#include "../decode-arm.h"
#include "checkers.h"

enum probes_insn checker_stack_use_none(probes_opcode_t insn,
		struct arch_probes_insn *asi,
		const struct decode_header *h)
{
	asi->stack_space = 0;
	return INSN_GOOD_NO_SLOT;
}

enum probes_insn checker_stack_use_unknown(probes_opcode_t insn,
		struct arch_probes_insn *asi,
		const struct decode_header *h)
{
	asi->stack_space = -1;
	return INSN_GOOD_NO_SLOT;
}

#ifdef CONFIG_THUMB2_KERNEL
enum probes_insn checker_stack_use_imm_0xx(probes_opcode_t insn,
		struct arch_probes_insn *asi,
		const struct decode_header *h)
{
	int imm = insn & 0xff;
	asi->stack_space = imm;
	return INSN_GOOD_NO_SLOT;
}

/*
 * Different from other insn uses imm8, the real addressing offset of
 * STRD in T32 encoding should be imm8 * 4. See ARMARM description.
 */
enum probes_insn checker_stack_use_t32strd(probes_opcode_t insn,
		struct arch_probes_insn *asi,
		const struct decode_header *h)
{
	int imm = insn & 0xff;
	asi->stack_space = imm << 2;
	return INSN_GOOD_NO_SLOT;
}
#else
enum probes_insn checker_stack_use_imm_x0x(probes_opcode_t insn,
		struct arch_probes_insn *asi,
		const struct decode_header *h)
{
	int imm = ((insn & 0xf00) >> 4) + (insn & 0xf);
	asi->stack_space = imm;
	return INSN_GOOD_NO_SLOT;
}
#endif

enum probes_insn checker_stack_use_imm_xxx(probes_opcode_t insn,
		struct arch_probes_insn *asi,
		const struct decode_header *h)
{
	int imm = insn & 0xfff;
	asi->stack_space = imm;
	return INSN_GOOD_NO_SLOT;
}

enum probes_insn checker_stack_use_stmdx(probes_opcode_t insn,
		struct arch_probes_insn *asi,
		const struct decode_header *h)
{
	unsigned int reglist = insn & 0xffff;
	int pbit = insn & (1 << 24);
	asi->stack_space = (hweight32(reglist) - (!pbit ? 1 : 0)) * 4;

	return INSN_GOOD_NO_SLOT;
}

const union decode_action stack_check_actions[] = {
	[STACK_USE_NONE] = {.decoder = checker_stack_use_none},
	[STACK_USE_UNKNOWN] = {.decoder = checker_stack_use_unknown},
#ifdef CONFIG_THUMB2_KERNEL
	[STACK_USE_FIXED_0XX] = {.decoder = checker_stack_use_imm_0xx},
	[STACK_USE_T32STRD] = {.decoder = checker_stack_use_t32strd},
#else
	[STACK_USE_FIXED_X0X] = {.decoder = checker_stack_use_imm_x0x},
#endif
	[STACK_USE_FIXED_XXX] = {.decoder = checker_stack_use_imm_xxx},
	[STACK_USE_STMDX] = {.decoder = checker_stack_use_stmdx},
};


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * arch/arm/probes/kprobes/checkers-thumb.c
 *
 * Copyright (C) 2014 Huawei Inc.
 */

#include <linux/kernel.h>
#include "../decode.h"
#include "../decode-thumb.h"
#include "checkers.h"

static enum probes_insn __kprobes t32_check_stack(probes_opcode_t insn,
		struct arch_probes_insn *asi,
		const struct decode_header *h)
{
	/*
	 * PROBES_T32_LDMSTM, PROBES_T32_LDRDSTRD and PROBES_T32_LDRSTR
	 * may get here. Simply mark all normal insns as STACK_USE_NONE.
	 */
	static const union decode_item table[] = {

		/*
		 * First, filter out all ldr insns to make our life easier.
		 * Following load insns may come here:
		 * LDM, LDRD, LDR.
		 * In T32 encoding, bit 20 is enough for distinguishing
		 * load and store. All load insns have this bit set, when
		 * all store insns have this bit clear.
		 */
		DECODE_CUSTOM	(0x00100000, 0x00100000, STACK_USE_NONE),

		/*
		 * Mark all 'STR{,B,H}, Rt, [Rn, Rm]' as STACK_USE_UNKNOWN
		 * if Rn or Rm is SP. T32 doesn't encode STRD.
		 */
		/*                                 xx | Rn | Rt |         | Rm |*/
		/* STR (register)	1111 1000 0100 xxxx xxxx 0000 00xx xxxx */
		/* STRB (register)	1111 1000 0000 xxxx xxxx 0000 00xx xxxx */
		/* STRH (register)	1111 1000 0010 xxxx xxxx 0000 00xx xxxx */
		/* INVALID INSN		1111 1000 0110 xxxx xxxx 0000 00xx xxxx */
		/* By Introducing INVALID INSN, bit 21 and 22 can be ignored. */
		DECODE_OR	(0xff9f0fc0, 0xf80d0000),
		DECODE_CUSTOM	(0xff900fcf, 0xf800000d, STACK_USE_UNKNOWN),

		/*                                 xx | Rn | Rt | PUW|   imm8  |*/
		/* STR (imm 8)		1111 1000 0100 1101 xxxx 110x xxxx xxxx */
		/* STRB (imm 8)		1111 1000 0000 1101 xxxx 110x xxxx xxxx */
		/* STRH (imm 8)		1111 1000 0010 1101 xxxx 110x xxxx xxxx */
		/* INVALID INSN		1111 1000 0110 1101 xxxx 110x xxxx xxxx */
		/* Only consider U == 0 and P == 1: strx rx, [sp, #-<imm>] */
		DECODE_CUSTOM	(0xff9f0e00, 0xf80d0c00, STACK_USE_FIXED_0XX),

		/* For STR{,B,H} (imm 12), offset is always positive, so ignore them. */

		/*                              P U W | Rn | Rt | Rt2|   imm8  |*/
		/* STRD (immediate)	1110 1001 01x0 1101 xxxx xxxx xxxx xxxx */
		/*
		 * Only consider U == 0 and P == 1.
		 * Also note that STRD in T32 encoding is special:
		 * imm = ZeroExtend(imm8:'00', 32)
		 */
		DECODE_CUSTOM	(0xffdf0000, 0xe94d0000, STACK_USE_T32STRD),

		/*                                    | Rn | */
		/* STMDB		1110 1001 00x0 1101 xxxx xxxx xxxx xxxx */
		DECODE_CUSTOM	(0xffdf0000, 0xe90d0000, STACK_USE_STMDX),

		/* fall through */
		DECODE_CUSTOM	(0, 0, STACK_USE_NONE),
		DECODE_END
	};

	return probes_decode_insn(insn, asi, table, false, false, stack_check_actions, NULL);
}

const struct decode_checker t32_stack_checker[NUM_PROBES_T32_ACTIONS] = {
	[PROBES_T32_LDMSTM] = {.checker = t32_check_stack},
	[PROBES_T32_LDRDSTRD] = {.checker = t32_check_stack},
	[PROBES_T32_LDRSTR] = {.checker = t32_check_stack},
};

/*
 * See following comments. This insn must be 'push'.
 */
static enum probes_insn __kprobes t16_check_stack(probes_opcode_t insn,
		struct arch_probes_insn *asi,
		const struct decode_header *h)
{
	unsigned int reglist = insn & 0x1ff;
	asi->stack_space = hweight32(reglist) * 4;
	return INSN_GOOD;
}

/*
 * T16 encoding is simple: only the 'push' insn can need extra stack space.
 * Other insns, like str, can only use r0-r7 as Rn.
 */
const struct decode_checker t16_stack_checker[NUM_PROBES_T16_ACTIONS] = {
	[PROBES_T16_PUSH] = {.checker = t16_check_stack},
};


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * arch/arm/probes/kprobes/checkers.h
 *
 * Copyright (C) 2014 Huawei Inc.
 */
#ifndef _ARM_KERNEL_PROBES_CHECKERS_H
#define _ARM_KERNEL_PROBES_CHECKERS_H

#include <linux/kernel.h>
#include <linux/types.h>
#include "../decode.h"

extern probes_check_t checker_stack_use_none;
extern probes_check_t checker_stack_use_unknown;
#ifdef CONFIG_THUMB2_KERNEL
extern probes_check_t checker_stack_use_imm_0xx;
#else
extern probes_check_t checker_stack_use_imm_x0x;
#endif
extern probes_check_t checker_stack_use_imm_xxx;
extern probes_check_t checker_stack_use_stmdx;

enum {
	STACK_USE_NONE,
	STACK_USE_UNKNOWN,
#ifdef CONFIG_THUMB2_KERNEL
	STACK_USE_FIXED_0XX,
	STACK_USE_T32STRD,
#else
	STACK_USE_FIXED_X0X,
#endif
	STACK_USE_FIXED_XXX,
	STACK_USE_STMDX,
	NUM_STACK_USE_TYPES
};

extern const union decode_action stack_check_actions[];

#ifndef CONFIG_THUMB2_KERNEL
extern const struct decode_checker arm_stack_checker[];
extern const struct decode_checker arm_regs_checker[];
#else
#endif
extern const struct decode_checker t32_stack_checker[];
extern const struct decode_checker t16_stack_checker[];
#endif


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * arch/arm/kernel/kprobes.c
 *
 * Kprobes on ARM
 *
 * Abhishek Sagar <sagar.abhishek@gmail.com>
 * Copyright (C) 2006, 2007 Motorola Inc.
 *
 * Nicolas Pitre <nico@marvell.com>
 * Copyright (C) 2007 Marvell Ltd.
 */

#define pr_fmt(fmt) "kprobes: " fmt

#include <linux/kernel.h>
#include <linux/kprobes.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/stop_machine.h>
#include <linux/sched/debug.h>
#include <linux/stringify.h>
#include <asm/traps.h>
#include <asm/opcodes.h>
#include <asm/cacheflush.h>
#include <linux/percpu.h>
#include <linux/bug.h>
#include <asm/patch.h>
#include <asm/sections.h>

#include "../decode-arm.h"
#include "../decode-thumb.h"
#include "core.h"

#define MIN_STACK_SIZE(addr) 				\
	min((unsigned long)MAX_STACK_SIZE,		\
	    (unsigned long)current_thread_info() + THREAD_START_SP - (addr))

#define flush_insns(addr, size)				\
	flush_icache_range((unsigned long)(addr),	\
			   (unsigned long)(addr) +	\
			   (size))

DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL;
DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);

int __kprobes arch_prepare_kprobe(struct kprobe *p)
{
	kprobe_opcode_t insn;
	kprobe_opcode_t tmp_insn[MAX_INSN_SIZE];
	unsigned long addr = (unsigned long)p->addr;
	bool thumb;
	kprobe_decode_insn_t *decode_insn;
	const union decode_action *actions;
	int is;
	const struct decode_checker **checkers;

#ifdef CONFIG_THUMB2_KERNEL
	thumb = true;
	addr &= ~1; /* Bit 0 would normally be set to indicate Thumb code */
	insn = __mem_to_opcode_thumb16(((u16 *)addr)[0]);
	if (is_wide_instruction(insn)) {
		u16 inst2 = __mem_to_opcode_thumb16(((u16 *)addr)[1]);
		insn = __opcode_thumb32_compose(insn, inst2);
		decode_insn = thumb32_probes_decode_insn;
		actions = kprobes_t32_actions;
		checkers = kprobes_t32_checkers;
	} else {
		decode_insn = thumb16_probes_decode_insn;
		actions = kprobes_t16_actions;
		checkers = kprobes_t16_checkers;
	}
#else /* !CONFIG_THUMB2_KERNEL */
	thumb = false;
	if (addr & 0x3)
		return -EINVAL;
	insn = __mem_to_opcode_arm(*p->addr);
	decode_insn = arm_probes_decode_insn;
	actions = kprobes_arm_actions;
	checkers = kprobes_arm_checkers;
#endif

	p->opcode = insn;
	p->ainsn.insn = tmp_insn;

	switch ((*decode_insn)(insn, &p->ainsn, true, actions, checkers)) {
	case INSN_REJECTED:	/* not supported */
		return -EINVAL;

	case INSN_GOOD:		/* instruction uses slot */
		p->ainsn.insn = get_insn_slot();
		if (!p->ainsn.insn)
			return -ENOMEM;
		for (is = 0; is < MAX_INSN_SIZE; ++is)
			p->ainsn.insn[is] = tmp_insn[is];
		flush_insns(p->ainsn.insn,
				sizeof(p->ainsn.insn[0]) * MAX_INSN_SIZE);
		p->ainsn.insn_fn = (probes_insn_fn_t *)
					((uintptr_t)p->ainsn.insn | thumb);
		break;

	case INSN_GOOD_NO_SLOT:	/* instruction doesn't need insn slot */
		p->ainsn.insn = NULL;
		break;
	}

	/*
	 * Never instrument insn like 'str r0, [sp, +/-r1]'. Also, insn likes
	 * 'str r0, [sp, #-68]' should also be prohibited.
	 * See __und_svc.
	 */
	if ((p->ainsn.stack_space < 0) ||
			(p->ainsn.stack_space > MAX_STACK_SIZE))
		return -EINVAL;

	return 0;
}

void __kprobes arch_arm_kprobe(struct kprobe *p)
{
	unsigned int brkp;
	void *addr;

	if (IS_ENABLED(CONFIG_THUMB2_KERNEL)) {
		/* Remove any Thumb flag */
		addr = (void *)((uintptr_t)p->addr & ~1);

		if (is_wide_instruction(p->opcode))
			brkp = KPROBE_THUMB32_BREAKPOINT_INSTRUCTION;
		else
			brkp = KPROBE_THUMB16_BREAKPOINT_INSTRUCTION;
	} else {
		kprobe_opcode_t insn = p->opcode;

		addr = p->addr;
		brkp = KPROBE_ARM_BREAKPOINT_INSTRUCTION;

		if (insn >= 0xe0000000)
			brkp |= 0xe0000000;  /* Unconditional instruction */
		else
			brkp |= insn & 0xf0000000;  /* Copy condition from insn */
	}

	patch_text(addr, brkp);
}

/*
 * The actual disarming is done here on each CPU and synchronized using
 * stop_machine. This synchronization is necessary on SMP to avoid removing
 * a probe between the moment the 'Undefined Instruction' exception is raised
 * and the moment the exception handler reads the faulting instruction from
 * memory. It is also needed to atomically set the two half-words of a 32-bit
 * Thumb breakpoint.
 */
struct patch {
	void *addr;
	unsigned int insn;
};

static int __kprobes_remove_breakpoint(void *data)
{
	struct patch *p = data;
	__patch_text(p->addr, p->insn);
	return 0;
}

void __kprobes kprobes_remove_breakpoint(void *addr, unsigned int insn)
{
	struct patch p = {
		.addr = addr,
		.insn = insn,
	};
	stop_machine_cpuslocked(__kprobes_remove_breakpoint, &p,
				cpu_online_mask);
}

void __kprobes arch_disarm_kprobe(struct kprobe *p)
{
	kprobes_remove_breakpoint((void *)((uintptr_t)p->addr & ~1),
			p->opcode);
}

void __kprobes arch_remove_kprobe(struct kprobe *p)
{
	if (p->ainsn.insn) {
		free_insn_slot(p->ainsn.insn, 0);
		p->ainsn.insn = NULL;
	}
}

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

static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb)
{
	__this_cpu_write(current_kprobe, kcb->prev_kprobe.kp);
	kcb->kprobe_status = kcb->prev_kprobe.status;
}

static void __kprobes set_current_kprobe(struct kprobe *p)
{
	__this_cpu_write(current_kprobe, p);
}

static void __kprobes
singlestep_skip(struct kprobe *p, struct pt_regs *regs)
{
#ifdef CONFIG_THUMB2_KERNEL
	regs->ARM_cpsr = it_advance(regs->ARM_cpsr);
	if (is_wide_instruction(p->opcode))
		regs->ARM_pc += 4;
	else
		regs->ARM_pc += 2;
#else
	regs->ARM_pc += 4;
#endif
}

static inline void __kprobes
singlestep(struct kprobe *p, struct pt_regs *regs, struct kprobe_ctlblk *kcb)
{
	p->ainsn.insn_singlestep(p->opcode, &p->ainsn, regs);
}

/*
 * Called with IRQs disabled. IRQs must remain disabled from that point
 * all the way until processing this kprobe is complete.  The current
 * kprobes implementation cannot process more than one nested level of
 * kprobe, and that level is reserved for user kprobe handlers, so we can't
 * risk encountering a new kprobe in an interrupt handler.
 */
void __kprobes kprobe_handler(struct pt_regs *regs)
{
	struct kprobe *p, *cur;
	struct kprobe_ctlblk *kcb;

	kcb = get_kprobe_ctlblk();
	cur = kprobe_running();

#ifdef CONFIG_THUMB2_KERNEL
	/*
	 * First look for a probe which was registered using an address with
	 * bit 0 set, this is the usual situation for pointers to Thumb code.
	 * If not found, fallback to looking for one with bit 0 clear.
	 */
	p = get_kprobe((kprobe_opcode_t *)(regs->ARM_pc | 1));
	if (!p)
		p = get_kprobe((kprobe_opcode_t *)regs->ARM_pc);

#else /* ! CONFIG_THUMB2_KERNEL */
	p = get_kprobe((kprobe_opcode_t *)regs->ARM_pc);
#endif

	if (p) {
		if (!p->ainsn.insn_check_cc(regs->ARM_cpsr)) {
			/*
			 * Probe hit but conditional execution check failed,
			 * so just skip the instruction and continue as if
			 * nothing had happened.
			 * In this case, we can skip recursing check too.
			 */
			singlestep_skip(p, regs);
		} else if (cur) {
			/* Kprobe is pending, so we're recursing. */
			switch (kcb->kprobe_status) {
			case KPROBE_HIT_ACTIVE:
			case KPROBE_HIT_SSDONE:
			case KPROBE_HIT_SS:
				/* A pre- or post-handler probe got us here. */
				kprobes_inc_nmissed_count(p);
				save_previous_kprobe(kcb);
				set_current_kprobe(p);
				kcb->kprobe_status = KPROBE_REENTER;
				singlestep(p, regs, kcb);
				restore_previous_kprobe(kcb);
				break;
			case KPROBE_REENTER:
				/* A nested probe was hit in FIQ, it is a BUG */
				pr_warn("Failed to recover from reentered kprobes.\n");
				dump_kprobe(p);
				fallthrough;
			default:
				/* impossible cases */
				BUG();
			}
		} else {
			/* Probe hit and conditional execution check ok. */
			set_current_kprobe(p);
			kcb->kprobe_status = KPROBE_HIT_ACTIVE;

			/*
			 * If we have no pre-handler or it returned 0, we
			 * continue with normal processing. If we have a
			 * pre-handler and it returned non-zero, it will
			 * modify the execution path and no need to single
			 * stepping. Let's just reset current kprobe and exit.
			 */
			if (!p->pre_handler || !p->pre_handler(p, regs)) {
				kcb->kprobe_status = KPROBE_HIT_SS;
				singlestep(p, regs, kcb);
				if (p->post_handler) {
					kcb->kprobe_status = KPROBE_HIT_SSDONE;
					p->post_handler(p, regs, 0);
				}
			}
			reset_current_kprobe();
		}
	} else {
		/*
		 * The probe was removed and a race is in progress.
		 * There is nothing we can do about it.  Let's restart
		 * the instruction.  By the time we can restart, the
		 * real instruction will be there.
		 */
	}
}

static int __kprobes kprobe_trap_handler(struct pt_regs *regs, unsigned int instr)
{
	unsigned long flags;
	local_irq_save(flags);
	kprobe_handler(regs);
	local_irq_restore(flags);
	return 0;
}

int __kprobes kprobe_fault_handler(struct pt_regs *regs, unsigned int fsr)
{
	struct kprobe *cur = kprobe_running();
	struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();

	switch (kcb->kprobe_status) {
	case KPROBE_HIT_SS:
	case KPROBE_REENTER:
		/*
		 * We are here because the instruction being single
		 * stepped caused a page fault. We reset the current
		 * kprobe and the PC to point back to the probe address
		 * and allow the page fault handler to continue as a
		 * normal page fault.
		 */
		regs->ARM_pc = (long)cur->addr;
		if (kcb->kprobe_status == KPROBE_REENTER) {
			restore_previous_kprobe(kcb);
		} else {
			reset_current_kprobe();
		}
		break;
	}

	return 0;
}

int __kprobes kprobe_exceptions_notify(struct notifier_block *self,
				       unsigned long val, void *data)
{
	/*
	 * notify_die() is currently never called on ARM,
	 * so this callback is currently empty.
	 */
	return NOTIFY_DONE;
}

/*
 * When a retprobed function returns, trampoline_handler() is called,
 * calling the kretprobe's handler. We construct a struct pt_regs to
 * give a view of registers r0-r11, sp, lr, and pc to the user
 * return-handler. This is not a complete pt_regs structure, but that
 * should be enough for stacktrace from the return handler with or
 * without pt_regs.
 */
void __naked __kprobes __kretprobe_trampoline(void)
{
	__asm__ __volatile__ (
#ifdef CONFIG_FRAME_POINTER
		"ldr	lr, =__kretprobe_trampoline	\n\t"
	/* __kretprobe_trampoline makes a framepointer on pt_regs. */
#ifdef CONFIG_CC_IS_CLANG
		"stmdb	sp, {sp, lr, pc}	\n\t"
		"sub	sp, sp, #12		\n\t"
		/* In clang case, pt_regs->ip = lr. */
		"stmdb	sp!, {r0 - r11, lr}	\n\t"
		/* fp points regs->r11 (fp) */
		"add	fp, sp,	#44		\n\t"
#else /* !CONFIG_CC_IS_CLANG */
		/* In gcc case, pt_regs->ip = fp. */
		"stmdb	sp, {fp, sp, lr, pc}	\n\t"
		"sub	sp, sp, #16		\n\t"
		"stmdb	sp!, {r0 - r11}		\n\t"
		/* fp points regs->r15 (pc) */
		"add	fp, sp, #60		\n\t"
#endif /* CONFIG_CC_IS_CLANG */
#else /* !CONFIG_FRAME_POINTER */
		"sub	sp, sp, #16		\n\t"
		"stmdb	sp!, {r0 - r11}		\n\t"
#endif /* CONFIG_FRAME_POINTER */
		"mov	r0, sp			\n\t"
		"bl	trampoline_handler	\n\t"
		"mov	lr, r0			\n\t"
		"ldmia	sp!, {r0 - r11}		\n\t"
		"add	sp, sp, #16		\n\t"
#ifdef CONFIG_THUMB2_KERNEL
		"bx	lr			\n\t"
#else
		"mov	pc, lr			\n\t"
#endif
		: : : "memory");
}

/* Called from __kretprobe_trampoline */
static __used __kprobes void *trampoline_handler(struct pt_regs *regs)
{
	return (void *)kretprobe_trampoline_handler(regs, (void *)regs->ARM_fp);
}

void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
				      struct pt_regs *regs)
{
	ri->ret_addr = (kprobe_opcode_t *)regs->ARM_lr;
	ri->fp = (void *)regs->ARM_fp;

	/* Replace the return addr with trampoline addr. */
	regs->ARM_lr = (unsigned long)&__kretprobe_trampoline;
}

int __kprobes arch_trampoline_kprobe(struct kprobe *p)
{
	return 0;
}

#ifdef CONFIG_THUMB2_KERNEL

static struct undef_hook kprobes_thumb16_break_hook = {
	.instr_mask	= 0xffff,
	.instr_val	= KPROBE_THUMB16_BREAKPOINT_INSTRUCTION,
	.cpsr_mask	= MODE_MASK,
	.cpsr_val	= SVC_MODE,
	.fn		= kprobe_trap_handler,
};

static struct undef_hook kprobes_thumb32_break_hook = {
	.instr_mask	= 0xffffffff,
	.instr_val	= KPROBE_THUMB32_BREAKPOINT_INSTRUCTION,
	.cpsr_mask	= MODE_MASK,
	.cpsr_val	= SVC_MODE,
	.fn		= kprobe_trap_handler,
};

#else  /* !CONFIG_THUMB2_KERNEL */

static struct undef_hook kprobes_arm_break_hook = {
	.instr_mask	= 0x0fffffff,
	.instr_val	= KPROBE_ARM_BREAKPOINT_INSTRUCTION,
	.cpsr_mask	= MODE_MASK,
	.cpsr_val	= SVC_MODE,
	.fn		= kprobe_trap_handler,
};

#endif /* !CONFIG_THUMB2_KERNEL */

int __init arch_init_kprobes(void)
{
	arm_probes_decode_init();
#ifdef CONFIG_THUMB2_KERNEL
	register_undef_hook(&kprobes_thumb16_break_hook);
	register_undef_hook(&kprobes_thumb32_break_hook);
#else
	register_undef_hook(&kprobes_arm_break_hook);
#endif
	return 0;
}

bool arch_within_kprobe_blacklist(unsigned long addr)
{
	void *a = (void *)addr;

	return __in_irqentry_text(addr) ||
	       in_entry_text(addr) ||
	       in_idmap_text(addr) ||
	       memory_contains(__kprobes_text_start, __kprobes_text_end, a, 1);
}


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * arch/arm/kernel/kprobes.h
 *
 * Copyright (C) 2011 Jon Medhurst <tixy@yxit.co.uk>.
 *
 * Some contents moved here from arch/arm/include/asm/kprobes.h which is
 * Copyright (C) 2006, 2007 Motorola Inc.
 */

#ifndef _ARM_KERNEL_KPROBES_H
#define _ARM_KERNEL_KPROBES_H

#include <asm/kprobes.h>
#include "../decode.h"

/*
 * These undefined instructions must be unique and
 * reserved solely for kprobes' use.
 */
#define KPROBE_ARM_BREAKPOINT_INSTRUCTION	0x07f001f8
#define KPROBE_THUMB16_BREAKPOINT_INSTRUCTION	0xde18
#define KPROBE_THUMB32_BREAKPOINT_INSTRUCTION	0xf7f0a018

extern void kprobes_remove_breakpoint(void *addr, unsigned int insn);

enum probes_insn __kprobes
kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_probes_insn *asi,
		const struct decode_header *h);

typedef enum probes_insn (kprobe_decode_insn_t)(probes_opcode_t,
						struct arch_probes_insn *,
						bool,
						const union decode_action *,
						const struct decode_checker *[]);

#ifdef CONFIG_THUMB2_KERNEL

extern const union decode_action kprobes_t32_actions[];
extern const union decode_action kprobes_t16_actions[];
extern const struct decode_checker *kprobes_t32_checkers[];
extern const struct decode_checker *kprobes_t16_checkers[];
#else /* !CONFIG_THUMB2_KERNEL */

extern const union decode_action kprobes_arm_actions[];
extern const struct decode_checker *kprobes_arm_checkers[];

#endif

#endif /* _ARM_KERNEL_KPROBES_H */


code:
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 *  Kernel Probes Jump Optimization (Optprobes)
 *
 * Copyright (C) IBM Corporation, 2002, 2004
 * Copyright (C) Hitachi Ltd., 2012
 * Copyright (C) Huawei Inc., 2014
 */

#include <linux/kprobes.h>
#include <linux/jump_label.h>
#include <asm/kprobes.h>
#include <asm/cacheflush.h>
/* for arm_gen_branch */
#include <asm/insn.h>
/* for patch_text */
#include <asm/patch.h>

#include "core.h"

/*
 * See register_usage_flags. If the probed instruction doesn't use PC,
 * we can copy it into template and have it executed directly without
 * simulation or emulation.
 */
#define ARM_REG_PC	15
#define can_kprobe_direct_exec(m)	(!test_bit(ARM_REG_PC, &(m)))

/*
 * NOTE: the first sub and add instruction will be modified according
 * to the stack cost of the instruction.
 */
asm (
			".global optprobe_template_entry\n"
			"optprobe_template_entry:\n"
			".global optprobe_template_sub_sp\n"
			"optprobe_template_sub_sp:"
			"	sub	sp, sp, #0xff\n"
			"	stmia	sp, {r0 - r14} \n"
			".global optprobe_template_add_sp\n"
			"optprobe_template_add_sp:"
			"	add	r3, sp, #0xff\n"
			"	str	r3, [sp, #52]\n"
			"	mrs	r4, cpsr\n"
			"	str	r4, [sp, #64]\n"
			"	mov	r1, sp\n"
			"	ldr	r0, 1f\n"
			"	ldr	r2, 2f\n"
			/*
			 * AEABI requires an 8-bytes alignment stack. If
			 * SP % 8 != 0 (SP % 4 == 0 should be ensured),
			 * alloc more bytes here.
			 */
			"	and	r4, sp, #4\n"
			"	sub	sp, sp, r4\n"
#if __LINUX_ARM_ARCH__ >= 5
			"	blx	r2\n"
#else
			"	mov     lr, pc\n"
			"	mov	pc, r2\n"
#endif
			"	add	sp, sp, r4\n"
			"	ldr	r1, [sp, #64]\n"
			"	tst	r1, #"__stringify(PSR_T_BIT)"\n"
			"	ldrne	r2, [sp, #60]\n"
			"	orrne	r2, #1\n"
			"	strne	r2, [sp, #60] @ set bit0 of PC for thumb\n"
			"	msr	cpsr_cxsf, r1\n"
			".global optprobe_template_restore_begin\n"
			"optprobe_template_restore_begin:\n"
			"	ldmia	sp, {r0 - r15}\n"
			".global optprobe_template_restore_orig_insn\n"
			"optprobe_template_restore_orig_insn:\n"
			"	nop\n"
			".global optprobe_template_restore_end\n"
			"optprobe_template_restore_end:\n"
			"	nop\n"
			".global optprobe_template_val\n"
			"optprobe_template_val:\n"
			"1:	.long 0\n"
			".global optprobe_template_call\n"
			"optprobe_template_call:\n"
			"2:	.long 0\n"
			".global optprobe_template_end\n"
			"optprobe_template_end:\n");

#define TMPL_VAL_IDX \
	((unsigned long *)optprobe_template_val - (unsigned long *)optprobe_template_entry)
#define TMPL_CALL_IDX \
	((unsigned long *)optprobe_template_call - (unsigned long *)optprobe_template_entry)
#define TMPL_END_IDX \
	((unsigned long *)optprobe_template_end - (unsigned long *)optprobe_template_entry)
#define TMPL_ADD_SP \
	((unsigned long *)optprobe_template_add_sp - (unsigned long *)optprobe_template_entry)
#define TMPL_SUB_SP \
	((unsigned long *)optprobe_template_sub_sp - (unsigned long *)optprobe_template_entry)
#define TMPL_RESTORE_BEGIN \
	((unsigned long *)optprobe_template_restore_begin - (unsigned long *)optprobe_template_entry)
#define TMPL_RESTORE_ORIGN_INSN \
	((unsigned long *)optprobe_template_restore_orig_insn - (unsigned long *)optprobe_template_entry)
#define TMPL_RESTORE_END \
	((unsigned long *)optprobe_template_restore_end - (unsigned long *)optprobe_template_entry)

/*
 * ARM can always optimize an instruction when using ARM ISA, except
 * instructions like 'str r0, [sp, r1]' which store to stack and unable
 * to determine stack space consumption statically.
 */
int arch_prepared_optinsn(struct arch_optimized_insn *optinsn)
{
	return optinsn->insn != NULL;
}

/*
 * In ARM ISA, kprobe opt always replace one instruction (4 bytes
 * aligned and 4 bytes long). It is impossible to encounter another
 * kprobe in the address range. So always return 0.
 */
int arch_check_optimized_kprobe(struct optimized_kprobe *op)
{
	return 0;
}

/* Caller must ensure addr & 3 == 0 */
static int can_optimize(struct kprobe *kp)
{
	if (kp->ainsn.stack_space < 0)
		return 0;
	/*
	 * 255 is the biggest imm can be used in 'sub r0, r0, #<imm>'.
	 * Number larger than 255 needs special encoding.
	 */
	if (kp->ainsn.stack_space > 255 - sizeof(struct pt_regs))
		return 0;
	return 1;
}

/* Free optimized instruction slot */
static void
__arch_remove_optimized_kprobe(struct optimized_kprobe *op, int dirty)
{
	if (op->optinsn.insn) {
		free_optinsn_slot(op->optinsn.insn, dirty);
		op->optinsn.insn = NULL;
	}
}

extern void kprobe_handler(struct pt_regs *regs);

static void
optimized_callback(struct optimized_kprobe *op, struct pt_regs *regs)
{
	unsigned long flags;
	struct kprobe *p = &op->kp;
	struct kprobe_ctlblk *kcb;

	/* Save skipped registers */
	regs->ARM_pc = (unsigned long)op->kp.addr;
	regs->ARM_ORIG_r0 = ~0UL;

	local_irq_save(flags);
	kcb = get_kprobe_ctlblk();

	if (kprobe_running()) {
		kprobes_inc_nmissed_count(&op->kp);
	} else {
		__this_cpu_write(current_kprobe, &op->kp);
		kcb->kprobe_status = KPROBE_HIT_ACTIVE;
		opt_pre_handler(&op->kp, regs);
		__this_cpu_write(current_kprobe, NULL);
	}

	/*
	 * We singlestep the replaced instruction only when it can't be
	 * executed directly during restore.
	 */
	if (!p->ainsn.kprobe_direct_exec)
		op->kp.ainsn.insn_singlestep(p->opcode, &p->ainsn, regs);

	local_irq_restore(flags);
}
NOKPROBE_SYMBOL(optimized_callback)

int arch_prepare_optimized_kprobe(struct optimized_kprobe *op, struct kprobe *orig)
{
	kprobe_opcode_t *code;
	unsigned long rel_chk;
	unsigned long val;
	unsigned long stack_protect = sizeof(struct pt_regs);

	if (!can_optimize(orig))
		return -EILSEQ;

	code = get_optinsn_slot();
	if (!code)
		return -ENOMEM;

	/*
	 * Verify if the address gap is in 32MiB range, because this uses
	 * a relative jump.
	 *
	 * kprobe opt use a 'b' instruction to branch to optinsn.insn.
	 * According to ARM manual, branch instruction is:
	 *
	 *   31  28 27           24 23             0
	 *  +------+---+---+---+---+----------------+
	 *  | cond | 1 | 0 | 1 | 0 |      imm24     |
	 *  +------+---+---+---+---+----------------+
	 *
	 * imm24 is a signed 24 bits integer. The real branch offset is computed
	 * by: imm32 = SignExtend(imm24:'00', 32);
	 *
	 * So the maximum forward branch should be:
	 *   (0x007fffff << 2) = 0x01fffffc =  0x1fffffc
	 * The maximum backword branch should be:
	 *   (0xff800000 << 2) = 0xfe000000 = -0x2000000
	 *
	 * We can simply check (rel & 0xfe000003):
	 *  if rel is positive, (rel & 0xfe000000) shoule be 0
	 *  if rel is negitive, (rel & 0xfe000000) should be 0xfe000000
	 *  the last '3' is used for alignment checking.
	 */
	rel_chk = (unsigned long)((long)code -
			(long)orig->addr + 8) & 0xfe000003;

	if ((rel_chk != 0) && (rel_chk != 0xfe000000)) {
		/*
		 * Different from x86, we free code buf directly instead of
		 * calling __arch_remove_optimized_kprobe() because
		 * we have not fill any field in op.
		 */
		free_optinsn_slot(code, 0);
		return -ERANGE;
	}

	/* Copy arch-dep-instance from template. */
	memcpy(code, (unsigned long *)optprobe_template_entry,
			TMPL_END_IDX * sizeof(kprobe_opcode_t));

	/* Adjust buffer according to instruction. */
	BUG_ON(orig->ainsn.stack_space < 0);

	stack_protect += orig->ainsn.stack_space;

	/* Should have been filtered by can_optimize(). */
	BUG_ON(stack_protect > 255);

	/* Create a 'sub sp, sp, #<stack_protect>' */
	code[TMPL_SUB_SP] = __opcode_to_mem_arm(0xe24dd000 | stack_protect);
	/* Create a 'add r3, sp, #<stack_protect>' */
	code[TMPL_ADD_SP] = __opcode_to_mem_arm(0xe28d3000 | stack_protect);

	/* Set probe information */
	val = (unsigned long)op;
	code[TMPL_VAL_IDX] = val;

	/* Set probe function call */
	val = (unsigned long)optimized_callback;
	code[TMPL_CALL_IDX] = val;

	/* If possible, copy insn and have it executed during restore */
	orig->ainsn.kprobe_direct_exec = false;
	if (can_kprobe_direct_exec(orig->ainsn.register_usage_flags)) {
		kprobe_opcode_t final_branch = arm_gen_branch(
				(unsigned long)(&code[TMPL_RESTORE_END]),
				(unsigned long)(op->kp.addr) + 4);
		if (final_branch != 0) {
			/*
			 * Replace original 'ldmia sp, {r0 - r15}' with
			 * 'ldmia {r0 - r14}', restore all registers except pc.
			 */
			code[TMPL_RESTORE_BEGIN] = __opcode_to_mem_arm(0xe89d7fff);

			/* The original probed instruction */
			code[TMPL_RESTORE_ORIGN_INSN] = __opcode_to_mem_arm(orig->opcode);

			/* Jump back to next instruction */
			code[TMPL_RESTORE_END] = __opcode_to_mem_arm(final_branch);
			orig->ainsn.kprobe_direct_exec = true;
		}
	}

	flush_icache_range((unsigned long)code,
			   (unsigned long)(&code[TMPL_END_IDX]));

	/* Set op->optinsn.insn means prepared. */
	op->optinsn.insn = code;
	return 0;
}

void __kprobes arch_optimize_kprobes(struct list_head *oplist)
{
	struct optimized_kprobe *op, *tmp;

	list_for_each_entry_safe(op, tmp, oplist, list) {
		unsigned long insn;
		WARN_ON(kprobe_disabled(&op->kp));

		/*
		 * Backup instructions which will be replaced
		 * by jump address
		 */
		memcpy(op->optinsn.copied_insn, op->kp.addr,
				RELATIVEJUMP_SIZE);

		insn = arm_gen_branch((unsigned long)op->kp.addr,
				(unsigned long)op->optinsn.insn);
		BUG_ON(insn == 0);

		/*
		 * Make it a conditional branch if replaced insn
		 * is consitional
		 */
		insn = (__mem_to_opcode_arm(
			  op->optinsn.copied_insn[0]) & 0xf0000000) |
			(insn & 0x0fffffff);

		/*
		 * Similar to __arch_disarm_kprobe, operations which
		 * removing breakpoints must be wrapped by stop_machine
		 * to avoid racing.
		 */
		kprobes_remove_breakpoint(op->kp.addr, insn);

		list_del_init(&op->list);
	}
}

void arch_unoptimize_kprobe(struct optimized_kprobe *op)
{
	arch_arm_kprobe(&op->kp);
}

/*
 * Recover original instructions and breakpoints from relative jumps.
 * Caller must call with locking kprobe_mutex.
 */
void arch_unoptimize_kprobes(struct list_head *oplist,
			    struct list_head *done_list)
{
	struct optimized_kprobe *op, *tmp;

	list_for_each_entry_safe(op, tmp, oplist, list) {
		arch_unoptimize_kprobe(op);
		list_move(&op->list, done_list);
	}
}

int arch_within_optimized_kprobe(struct optimized_kprobe *op,
				 kprobe_opcode_t *addr)
{
	return (op->kp.addr <= addr &&
		op->kp.addr + (RELATIVEJUMP_SIZE / sizeof(kprobe_opcode_t)) > addr);

}

void arch_remove_optimized_kprobe(struct optimized_kprobe *op)
{
	__arch_remove_optimized_kprobe(op, 1);
}