xref: /linux/arch/arm64/kernel/probes/simulate-insn.c (revision feafee284579d29537a5a56ba8f23894f0463f3d)
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 
set_x_reg(struct pt_regs * regs,int reg,u64 val)33 static inline void set_x_reg(struct pt_regs *regs, int reg, u64 val)
34 {
35 	pt_regs_write_reg(regs, reg, val);
36 }
37 
set_w_reg(struct pt_regs * regs,int reg,u64 val)38 static 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 
get_x_reg(struct pt_regs * regs,int reg)43 static inline u64 get_x_reg(struct pt_regs *regs, int reg)
44 {
45 	return pt_regs_read_reg(regs, reg);
46 }
47 
get_w_reg(struct pt_regs * regs,int reg)48 static 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 
update_lr(struct pt_regs * regs,long addr)53 static 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 
check_cbz(u32 opcode,struct pt_regs * regs)68 static 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 
check_cbnz(u32 opcode,struct pt_regs * regs)76 static 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 
check_tbz(u32 opcode,struct pt_regs * regs)84 static 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 
check_tbnz(u32 opcode,struct pt_regs * regs)92 static 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  */
103 void __kprobes
simulate_adr_adrp(u32 opcode,long addr,struct pt_regs * regs)104 simulate_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 
121 void __kprobes
simulate_b_bl(u32 opcode,long addr,struct pt_regs * regs)122 simulate_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 
133 void __kprobes
simulate_b_cond(u32 opcode,long addr,struct pt_regs * regs)134 simulate_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 
144 void __kprobes
simulate_br_blr(u32 opcode,long addr,struct pt_regs * regs)145 simulate_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 
157 void __kprobes
simulate_ret(u32 opcode,long addr,struct pt_regs * regs)158 simulate_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 
175 void __kprobes
simulate_cbz_cbnz(u32 opcode,long addr,struct pt_regs * regs)176 simulate_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 
190 void __kprobes
simulate_tbz_tbnz(u32 opcode,long addr,struct pt_regs * regs)191 simulate_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 
205 void __kprobes
simulate_ldr_literal(u32 opcode,long addr,struct pt_regs * regs)206 simulate_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 
221 void __kprobes
simulate_ldrsw_literal(u32 opcode,long addr,struct pt_regs * regs)222 simulate_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 
234 void __kprobes
simulate_nop(u32 opcode,long addr,struct pt_regs * regs)235 simulate_nop(u32 opcode, long addr, struct pt_regs *regs)
236 {
237 	arm64_skip_faulting_instruction(regs, AARCH64_INSN_SIZE);
238 }
239