File Index
Files and directories
Sign in
FILE

simulate-insn.c

File preview with syntax highlighting.

linux-6.19.14/arch/arm64/kernel/probes/simulate-insn.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * arch/arm64/kernel/probes/simulate-insn.c
  4 *
  5 * Copyright (C) 2013 Linaro Limited.
  6 */
  7
  8#include <linux/bitops.h>
  9#include <linux/kernel.h>
 10#include <linux/kprobes.h>
 11
 12#include <asm/ptrace.h>
 13#include <asm/traps.h>
 14
 15#include "simulate-insn.h"
 16#include "asm/gcs.h"
 17
 18#define bbl_displacement(insn)		\
 19	sign_extend32(((insn) & 0x3ffffff) << 2, 27)
 20
 21#define bcond_displacement(insn)	\
 22	sign_extend32(((insn >> 5) & 0x7ffff) << 2, 20)
 23
 24#define cbz_displacement(insn)	\
 25	sign_extend32(((insn >> 5) & 0x7ffff) << 2, 20)
 26
 27#define tbz_displacement(insn)	\
 28	sign_extend32(((insn >> 5) & 0x3fff) << 2, 15)
 29
 30#define ldr_displacement(insn)	\
 31	sign_extend32(((insn >> 5) & 0x7ffff) << 2, 20)
 32
 33static inline void set_x_reg(struct pt_regs *regs, int reg, u64 val)
 34{
 35	pt_regs_write_reg(regs, reg, val);
 36}
 37
 38static inline void set_w_reg(struct pt_regs *regs, int reg, u64 val)
 39{
 40	pt_regs_write_reg(regs, reg, lower_32_bits(val));
 41}
 42
 43static inline u64 get_x_reg(struct pt_regs *regs, int reg)
 44{
 45	return pt_regs_read_reg(regs, reg);
 46}
 47
 48static inline u32 get_w_reg(struct pt_regs *regs, int reg)
 49{
 50	return lower_32_bits(pt_regs_read_reg(regs, reg));
 51}
 52
 53static inline int update_lr(struct pt_regs *regs, long addr)
 54{
 55	int err = 0;
 56
 57	if (user_mode(regs) && task_gcs_el0_enabled(current)) {
 58		push_user_gcs(addr, &err);
 59		if (err) {
 60			force_sig(SIGSEGV);
 61			return err;
 62		}
 63	}
 64	procedure_link_pointer_set(regs, addr);
 65	return err;
 66}
 67
 68static bool __kprobes check_cbz(u32 opcode, struct pt_regs *regs)
 69{
 70	int xn = opcode & 0x1f;
 71
 72	return (opcode & (1 << 31)) ?
 73	    (get_x_reg(regs, xn) == 0) : (get_w_reg(regs, xn) == 0);
 74}
 75
 76static bool __kprobes check_cbnz(u32 opcode, struct pt_regs *regs)
 77{
 78	int xn = opcode & 0x1f;
 79
 80	return (opcode & (1 << 31)) ?
 81	    (get_x_reg(regs, xn) != 0) : (get_w_reg(regs, xn) != 0);
 82}
 83
 84static bool __kprobes check_tbz(u32 opcode, struct pt_regs *regs)
 85{
 86	int xn = opcode & 0x1f;
 87	int bit_pos = ((opcode & (1 << 31)) >> 26) | ((opcode >> 19) & 0x1f);
 88
 89	return ((get_x_reg(regs, xn) >> bit_pos) & 0x1) == 0;
 90}
 91
 92static bool __kprobes check_tbnz(u32 opcode, struct pt_regs *regs)
 93{
 94	int xn = opcode & 0x1f;
 95	int bit_pos = ((opcode & (1 << 31)) >> 26) | ((opcode >> 19) & 0x1f);
 96
 97	return ((get_x_reg(regs, xn) >> bit_pos) & 0x1) != 0;
 98}
 99
100/*
101 * instruction simulation functions
102 */
103void __kprobes
104simulate_adr_adrp(u32 opcode, long addr, struct pt_regs *regs)
105{
106	long imm, xn, val;
107
108	xn = opcode & 0x1f;
109	imm = ((opcode >> 3) & 0x1ffffc) | ((opcode >> 29) & 0x3);
110	imm = sign_extend64(imm, 20);
111	if (opcode & 0x80000000)
112		val = (imm<<12) + (addr & 0xfffffffffffff000);
113	else
114		val = imm + addr;
115
116	set_x_reg(regs, xn, val);
117
118	instruction_pointer_set(regs, instruction_pointer(regs) + 4);
119}
120
121void __kprobes
122simulate_b_bl(u32 opcode, long addr, struct pt_regs *regs)
123{
124	int disp = bbl_displacement(opcode);
125
126	if (opcode & (1 << 31))
127		if (update_lr(regs, addr + 4))
128			return;
129
130	instruction_pointer_set(regs, addr + disp);
131}
132
133void __kprobes
134simulate_b_cond(u32 opcode, long addr, struct pt_regs *regs)
135{
136	int disp = 4;
137
138	if (aarch32_opcode_cond_checks[opcode & 0xf](regs->pstate & 0xffffffff))
139		disp = bcond_displacement(opcode);
140
141	instruction_pointer_set(regs, addr + disp);
142}
143
144void __kprobes
145simulate_br_blr(u32 opcode, long addr, struct pt_regs *regs)
146{
147	int xn = (opcode >> 5) & 0x1f;
148	u64 b_target = get_x_reg(regs, xn);
149
150	if (((opcode >> 21) & 0x3) == 1)
151		if (update_lr(regs, addr + 4))
152			return;
153
154	instruction_pointer_set(regs, b_target);
155}
156
157void __kprobes
158simulate_ret(u32 opcode, long addr, struct pt_regs *regs)
159{
160	u64 ret_addr;
161	int err = 0;
162	int xn = (opcode >> 5) & 0x1f;
163	u64 r_target = get_x_reg(regs, xn);
164
165	if (user_mode(regs) && task_gcs_el0_enabled(current)) {
166		ret_addr = pop_user_gcs(&err);
167		if (err || ret_addr != r_target) {
168			force_sig(SIGSEGV);
169			return;
170		}
171	}
172	instruction_pointer_set(regs, r_target);
173}
174
175void __kprobes
176simulate_cbz_cbnz(u32 opcode, long addr, struct pt_regs *regs)
177{
178	int disp = 4;
179
180	if (opcode & (1 << 24)) {
181		if (check_cbnz(opcode, regs))
182			disp = cbz_displacement(opcode);
183	} else {
184		if (check_cbz(opcode, regs))
185			disp = cbz_displacement(opcode);
186	}
187	instruction_pointer_set(regs, addr + disp);
188}
189
190void __kprobes
191simulate_tbz_tbnz(u32 opcode, long addr, struct pt_regs *regs)
192{
193	int disp = 4;
194
195	if (opcode & (1 << 24)) {
196		if (check_tbnz(opcode, regs))
197			disp = tbz_displacement(opcode);
198	} else {
199		if (check_tbz(opcode, regs))
200			disp = tbz_displacement(opcode);
201	}
202	instruction_pointer_set(regs, addr + disp);
203}
204
205void __kprobes
206simulate_ldr_literal(u32 opcode, long addr, struct pt_regs *regs)
207{
208	unsigned long load_addr;
209	int xn = opcode & 0x1f;
210
211	load_addr = addr + ldr_displacement(opcode);
212
213	if (opcode & (1 << 30))	/* x0-x30 */
214		set_x_reg(regs, xn, READ_ONCE(*(u64 *)load_addr));
215	else			/* w0-w30 */
216		set_w_reg(regs, xn, READ_ONCE(*(u32 *)load_addr));
217
218	instruction_pointer_set(regs, instruction_pointer(regs) + 4);
219}
220
221void __kprobes
222simulate_ldrsw_literal(u32 opcode, long addr, struct pt_regs *regs)
223{
224	unsigned long load_addr;
225	int xn = opcode & 0x1f;
226
227	load_addr = addr + ldr_displacement(opcode);
228
229	set_x_reg(regs, xn, READ_ONCE(*(s32 *)load_addr));
230
231	instruction_pointer_set(regs, instruction_pointer(regs) + 4);
232}
233
234void __kprobes
235simulate_nop(u32 opcode, long addr, struct pt_regs *regs)
236{
237	arm64_skip_faulting_instruction(regs, AARCH64_INSN_SIZE);
238}
~/ Read-only file preview.