FILE
simulate-insn.c
File preview with syntax highlighting.
linux-6.19.14/arch/arm64/kernel/probes/simulate-insn.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.