18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2012 Rabin Vincent <rabin at rab.in>
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/kernel.h>
78c2ecf20Sopenharmony_ci#include <linux/types.h>
88c2ecf20Sopenharmony_ci#include <linux/stddef.h>
98c2ecf20Sopenharmony_ci#include <linux/wait.h>
108c2ecf20Sopenharmony_ci#include <linux/uprobes.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include "../decode.h"
148c2ecf20Sopenharmony_ci#include "../decode-arm.h"
158c2ecf20Sopenharmony_ci#include "core.h"
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_cistatic int uprobes_substitute_pc(unsigned long *pinsn, u32 oregs)
188c2ecf20Sopenharmony_ci{
198c2ecf20Sopenharmony_ci	probes_opcode_t insn = __mem_to_opcode_arm(*pinsn);
208c2ecf20Sopenharmony_ci	probes_opcode_t temp;
218c2ecf20Sopenharmony_ci	probes_opcode_t mask;
228c2ecf20Sopenharmony_ci	int freereg;
238c2ecf20Sopenharmony_ci	u32 free = 0xffff;
248c2ecf20Sopenharmony_ci	u32 regs;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	for (regs = oregs; regs; regs >>= 4, insn >>= 4) {
278c2ecf20Sopenharmony_ci		if ((regs & 0xf) == REG_TYPE_NONE)
288c2ecf20Sopenharmony_ci			continue;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci		free &= ~(1 << (insn & 0xf));
318c2ecf20Sopenharmony_ci	}
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	/* No PC, no problem */
348c2ecf20Sopenharmony_ci	if (free & (1 << 15))
358c2ecf20Sopenharmony_ci		return 15;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	if (!free)
388c2ecf20Sopenharmony_ci		return -1;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	/*
418c2ecf20Sopenharmony_ci	 * fls instead of ffs ensures that for "ldrd r0, r1, [pc]" we would
428c2ecf20Sopenharmony_ci	 * pick LR instead of R1.
438c2ecf20Sopenharmony_ci	 */
448c2ecf20Sopenharmony_ci	freereg = free = fls(free) - 1;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	temp = __mem_to_opcode_arm(*pinsn);
478c2ecf20Sopenharmony_ci	insn = temp;
488c2ecf20Sopenharmony_ci	regs = oregs;
498c2ecf20Sopenharmony_ci	mask = 0xf;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	for (; regs; regs >>= 4, mask <<= 4, free <<= 4, temp >>= 4) {
528c2ecf20Sopenharmony_ci		if ((regs & 0xf) == REG_TYPE_NONE)
538c2ecf20Sopenharmony_ci			continue;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci		if ((temp & 0xf) != 15)
568c2ecf20Sopenharmony_ci			continue;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci		insn &= ~mask;
598c2ecf20Sopenharmony_ci		insn |= free & mask;
608c2ecf20Sopenharmony_ci	}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	*pinsn = __opcode_to_mem_arm(insn);
638c2ecf20Sopenharmony_ci	return freereg;
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic void uprobe_set_pc(struct arch_uprobe *auprobe,
678c2ecf20Sopenharmony_ci			  struct arch_uprobe_task *autask,
688c2ecf20Sopenharmony_ci			  struct pt_regs *regs)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	u32 pcreg = auprobe->pcreg;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	autask->backup = regs->uregs[pcreg];
738c2ecf20Sopenharmony_ci	regs->uregs[pcreg] = regs->ARM_pc + 8;
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic void uprobe_unset_pc(struct arch_uprobe *auprobe,
778c2ecf20Sopenharmony_ci			    struct arch_uprobe_task *autask,
788c2ecf20Sopenharmony_ci			    struct pt_regs *regs)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	/* PC will be taken care of by common code */
818c2ecf20Sopenharmony_ci	regs->uregs[auprobe->pcreg] = autask->backup;
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistatic void uprobe_aluwrite_pc(struct arch_uprobe *auprobe,
858c2ecf20Sopenharmony_ci			       struct arch_uprobe_task *autask,
868c2ecf20Sopenharmony_ci			       struct pt_regs *regs)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	u32 pcreg = auprobe->pcreg;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	alu_write_pc(regs->uregs[pcreg], regs);
918c2ecf20Sopenharmony_ci	regs->uregs[pcreg] = autask->backup;
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic void uprobe_write_pc(struct arch_uprobe *auprobe,
958c2ecf20Sopenharmony_ci			    struct arch_uprobe_task *autask,
968c2ecf20Sopenharmony_ci			    struct pt_regs *regs)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	u32 pcreg = auprobe->pcreg;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	load_write_pc(regs->uregs[pcreg], regs);
1018c2ecf20Sopenharmony_ci	regs->uregs[pcreg] = autask->backup;
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cienum probes_insn
1058c2ecf20Sopenharmony_cidecode_pc_ro(probes_opcode_t insn, struct arch_probes_insn *asi,
1068c2ecf20Sopenharmony_ci	     const struct decode_header *d)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	struct arch_uprobe *auprobe = container_of(asi, struct arch_uprobe,
1098c2ecf20Sopenharmony_ci						   asi);
1108c2ecf20Sopenharmony_ci	struct decode_emulate *decode = (struct decode_emulate *) d;
1118c2ecf20Sopenharmony_ci	u32 regs = decode->header.type_regs.bits >> DECODE_TYPE_BITS;
1128c2ecf20Sopenharmony_ci	int reg;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	reg = uprobes_substitute_pc(&auprobe->ixol[0], regs);
1158c2ecf20Sopenharmony_ci	if (reg == 15)
1168c2ecf20Sopenharmony_ci		return INSN_GOOD;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	if (reg == -1)
1198c2ecf20Sopenharmony_ci		return INSN_REJECTED;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	auprobe->pcreg = reg;
1228c2ecf20Sopenharmony_ci	auprobe->prehandler = uprobe_set_pc;
1238c2ecf20Sopenharmony_ci	auprobe->posthandler = uprobe_unset_pc;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	return INSN_GOOD;
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cienum probes_insn
1298c2ecf20Sopenharmony_cidecode_wb_pc(probes_opcode_t insn, struct arch_probes_insn *asi,
1308c2ecf20Sopenharmony_ci	     const struct decode_header *d, bool alu)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	struct arch_uprobe *auprobe = container_of(asi, struct arch_uprobe,
1338c2ecf20Sopenharmony_ci						   asi);
1348c2ecf20Sopenharmony_ci	enum probes_insn ret = decode_pc_ro(insn, asi, d);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	if (((insn >> 12) & 0xf) == 15)
1378c2ecf20Sopenharmony_ci		auprobe->posthandler = alu ? uprobe_aluwrite_pc
1388c2ecf20Sopenharmony_ci					   : uprobe_write_pc;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	return ret;
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cienum probes_insn
1448c2ecf20Sopenharmony_cidecode_rd12rn16rm0rs8_rwflags(probes_opcode_t insn,
1458c2ecf20Sopenharmony_ci			      struct arch_probes_insn *asi,
1468c2ecf20Sopenharmony_ci			      const struct decode_header *d)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	return decode_wb_pc(insn, asi, d, true);
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cienum probes_insn
1528c2ecf20Sopenharmony_cidecode_ldr(probes_opcode_t insn, struct arch_probes_insn *asi,
1538c2ecf20Sopenharmony_ci	   const struct decode_header *d)
1548c2ecf20Sopenharmony_ci{
1558c2ecf20Sopenharmony_ci	return decode_wb_pc(insn, asi, d, false);
1568c2ecf20Sopenharmony_ci}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_cienum probes_insn
1598c2ecf20Sopenharmony_ciuprobe_decode_ldmstm(probes_opcode_t insn,
1608c2ecf20Sopenharmony_ci		     struct arch_probes_insn *asi,
1618c2ecf20Sopenharmony_ci		     const struct decode_header *d)
1628c2ecf20Sopenharmony_ci{
1638c2ecf20Sopenharmony_ci	struct arch_uprobe *auprobe = container_of(asi, struct arch_uprobe,
1648c2ecf20Sopenharmony_ci						   asi);
1658c2ecf20Sopenharmony_ci	unsigned reglist = insn & 0xffff;
1668c2ecf20Sopenharmony_ci	int rn = (insn >> 16) & 0xf;
1678c2ecf20Sopenharmony_ci	int lbit = insn & (1 << 20);
1688c2ecf20Sopenharmony_ci	unsigned used = reglist | (1 << rn);
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	if (rn == 15)
1718c2ecf20Sopenharmony_ci		return INSN_REJECTED;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	if (!(used & (1 << 15)))
1748c2ecf20Sopenharmony_ci		return INSN_GOOD;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	if (used & (1 << 14))
1778c2ecf20Sopenharmony_ci		return INSN_REJECTED;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	/* Use LR instead of PC */
1808c2ecf20Sopenharmony_ci	insn ^= 0xc000;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	auprobe->pcreg = 14;
1838c2ecf20Sopenharmony_ci	auprobe->ixol[0] = __opcode_to_mem_arm(insn);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	auprobe->prehandler = uprobe_set_pc;
1868c2ecf20Sopenharmony_ci	if (lbit)
1878c2ecf20Sopenharmony_ci		auprobe->posthandler = uprobe_write_pc;
1888c2ecf20Sopenharmony_ci	else
1898c2ecf20Sopenharmony_ci		auprobe->posthandler = uprobe_unset_pc;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	return INSN_GOOD;
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ciconst union decode_action uprobes_probes_actions[] = {
1958c2ecf20Sopenharmony_ci	[PROBES_PRELOAD_IMM] = {.handler = probes_simulate_nop},
1968c2ecf20Sopenharmony_ci	[PROBES_PRELOAD_REG] = {.handler = probes_simulate_nop},
1978c2ecf20Sopenharmony_ci	[PROBES_BRANCH_IMM] = {.handler = simulate_blx1},
1988c2ecf20Sopenharmony_ci	[PROBES_MRS] = {.handler = simulate_mrs},
1998c2ecf20Sopenharmony_ci	[PROBES_BRANCH_REG] = {.handler = simulate_blx2bx},
2008c2ecf20Sopenharmony_ci	[PROBES_CLZ] = {.handler = probes_simulate_nop},
2018c2ecf20Sopenharmony_ci	[PROBES_SATURATING_ARITHMETIC] = {.handler = probes_simulate_nop},
2028c2ecf20Sopenharmony_ci	[PROBES_MUL1] = {.handler = probes_simulate_nop},
2038c2ecf20Sopenharmony_ci	[PROBES_MUL2] = {.handler = probes_simulate_nop},
2048c2ecf20Sopenharmony_ci	[PROBES_SWP] = {.handler = probes_simulate_nop},
2058c2ecf20Sopenharmony_ci	[PROBES_LDRSTRD] = {.decoder = decode_pc_ro},
2068c2ecf20Sopenharmony_ci	[PROBES_LOAD_EXTRA] = {.decoder = decode_pc_ro},
2078c2ecf20Sopenharmony_ci	[PROBES_LOAD] = {.decoder = decode_ldr},
2088c2ecf20Sopenharmony_ci	[PROBES_STORE_EXTRA] = {.decoder = decode_pc_ro},
2098c2ecf20Sopenharmony_ci	[PROBES_STORE] = {.decoder = decode_pc_ro},
2108c2ecf20Sopenharmony_ci	[PROBES_MOV_IP_SP] = {.handler = simulate_mov_ipsp},
2118c2ecf20Sopenharmony_ci	[PROBES_DATA_PROCESSING_REG] = {
2128c2ecf20Sopenharmony_ci		.decoder = decode_rd12rn16rm0rs8_rwflags},
2138c2ecf20Sopenharmony_ci	[PROBES_DATA_PROCESSING_IMM] = {
2148c2ecf20Sopenharmony_ci		.decoder = decode_rd12rn16rm0rs8_rwflags},
2158c2ecf20Sopenharmony_ci	[PROBES_MOV_HALFWORD] = {.handler = probes_simulate_nop},
2168c2ecf20Sopenharmony_ci	[PROBES_SEV] = {.handler = probes_simulate_nop},
2178c2ecf20Sopenharmony_ci	[PROBES_WFE] = {.handler = probes_simulate_nop},
2188c2ecf20Sopenharmony_ci	[PROBES_SATURATE] = {.handler = probes_simulate_nop},
2198c2ecf20Sopenharmony_ci	[PROBES_REV] = {.handler = probes_simulate_nop},
2208c2ecf20Sopenharmony_ci	[PROBES_MMI] = {.handler = probes_simulate_nop},
2218c2ecf20Sopenharmony_ci	[PROBES_PACK] = {.handler = probes_simulate_nop},
2228c2ecf20Sopenharmony_ci	[PROBES_EXTEND] = {.handler = probes_simulate_nop},
2238c2ecf20Sopenharmony_ci	[PROBES_EXTEND_ADD] = {.handler = probes_simulate_nop},
2248c2ecf20Sopenharmony_ci	[PROBES_MUL_ADD_LONG] = {.handler = probes_simulate_nop},
2258c2ecf20Sopenharmony_ci	[PROBES_MUL_ADD] = {.handler = probes_simulate_nop},
2268c2ecf20Sopenharmony_ci	[PROBES_BITFIELD] = {.handler = probes_simulate_nop},
2278c2ecf20Sopenharmony_ci	[PROBES_BRANCH] = {.handler = simulate_bbl},
2288c2ecf20Sopenharmony_ci	[PROBES_LDMSTM] = {.decoder = uprobe_decode_ldmstm}
2298c2ecf20Sopenharmony_ci};
230