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 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 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 43 static inline u64 get_x_reg(struct pt_regs *regs, int reg) 44 { 45 return pt_regs_read_reg(regs, reg); 46 } 47 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 235 simulate_nop(u32 opcode, long addr, struct pt_regs *regs) 236 { 237 arm64_skip_faulting_instruction(regs, AARCH64_INSN_SIZE); 238 } 239