Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

MIPS Kprobes: Refactor branch emulation

This patch refactors MIPS branch emulation code so as to allow skipping
delay slot instruction in case of branch likely instructions when branch is
not taken. This is useful for keeping the code common for use cases like
kprobes where one would like to handle the branch instructions keeping the
delay slot instuction also in picture for branch likely instructions. Also
allow emulation when instruction to be decoded is not at pt_regs->cp0_epc
as in case of kprobes where pt_regs->cp0_epc points to the breakpoint
instruction.

The patch also exports the function for modules.

Signed-off-by: Maneesh Soni <manesoni@cisco.com>
Signed-off-by: Victor Kamensky <kamensky@cisco.com>
Cc: David Daney <david.daney@cavium.com>
Cc: ananth@in.ibm.com
Cc: linux-kernel@vger.kernel.org
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/2913/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>

authored by

Maneesh Soni and committed by
Ralf Baechle
d8d4e3ae 9233c1ee

+90 -45
+5
arch/mips/include/asm/branch.h
··· 9 9 #define _ASM_BRANCH_H 10 10 11 11 #include <asm/ptrace.h> 12 + #include <asm/inst.h> 12 13 13 14 static inline int delay_slot(struct pt_regs *regs) 14 15 { ··· 24 23 return regs->cp0_epc + 4; 25 24 } 26 25 26 + #define BRANCH_LIKELY_TAKEN 0x0001 27 + 27 28 extern int __compute_return_epc(struct pt_regs *regs); 29 + extern int __compute_return_epc_for_insn(struct pt_regs *regs, 30 + union mips_instruction insn); 28 31 29 32 static inline int compute_return_epc(struct pt_regs *regs) 30 33 {
+84 -44
arch/mips/kernel/branch.c
··· 9 9 #include <linux/kernel.h> 10 10 #include <linux/sched.h> 11 11 #include <linux/signal.h> 12 + #include <linux/module.h> 12 13 #include <asm/branch.h> 13 14 #include <asm/cpu.h> 14 15 #include <asm/cpu-features.h> ··· 18 17 #include <asm/ptrace.h> 19 18 #include <asm/uaccess.h> 20 19 21 - /* 22 - * Compute the return address and do emulate branch simulation, if required. 20 + /** 21 + * __compute_return_epc_for_insn - Computes the return address and do emulate 22 + * branch simulation, if required. 23 + * 24 + * @regs: Pointer to pt_regs 25 + * @insn: branch instruction to decode 26 + * @returns: -EFAULT on error and forces SIGBUS, and on success 27 + * returns 0 or BRANCH_LIKELY_TAKEN as appropriate after 28 + * evaluating the branch. 23 29 */ 24 - int __compute_return_epc(struct pt_regs *regs) 30 + int __compute_return_epc_for_insn(struct pt_regs *regs, 31 + union mips_instruction insn) 25 32 { 26 - unsigned int __user *addr; 27 33 unsigned int bit, fcr31, dspcontrol; 28 - long epc; 29 - union mips_instruction insn; 30 - 31 - epc = regs->cp0_epc; 32 - if (epc & 3) 33 - goto unaligned; 34 - 35 - /* 36 - * Read the instruction 37 - */ 38 - addr = (unsigned int __user *) epc; 39 - if (__get_user(insn.word, addr)) { 40 - force_sig(SIGSEGV, current); 41 - return -EFAULT; 42 - } 34 + long epc = regs->cp0_epc; 35 + int ret = 0; 43 36 44 37 switch (insn.i_format.opcode) { 45 38 /* ··· 59 64 switch (insn.i_format.rt) { 60 65 case bltz_op: 61 66 case bltzl_op: 62 - if ((long)regs->regs[insn.i_format.rs] < 0) 67 + if ((long)regs->regs[insn.i_format.rs] < 0) { 63 68 epc = epc + 4 + (insn.i_format.simmediate << 2); 64 - else 69 + if (insn.i_format.rt == bltzl_op) 70 + ret = BRANCH_LIKELY_TAKEN; 71 + } else 65 72 epc += 8; 66 73 regs->cp0_epc = epc; 67 74 break; 68 75 69 76 case bgez_op: 70 77 case bgezl_op: 71 - if ((long)regs->regs[insn.i_format.rs] >= 0) 78 + if ((long)regs->regs[insn.i_format.rs] >= 0) { 72 79 epc = epc + 4 + (insn.i_format.simmediate << 2); 73 - else 80 + if (insn.i_format.rt == bgezl_op) 81 + ret = BRANCH_LIKELY_TAKEN; 82 + } else 74 83 epc += 8; 75 84 regs->cp0_epc = epc; 76 85 break; ··· 82 83 case bltzal_op: 83 84 case bltzall_op: 84 85 regs->regs[31] = epc + 8; 85 - if ((long)regs->regs[insn.i_format.rs] < 0) 86 + if ((long)regs->regs[insn.i_format.rs] < 0) { 86 87 epc = epc + 4 + (insn.i_format.simmediate << 2); 87 - else 88 + if (insn.i_format.rt == bltzall_op) 89 + ret = BRANCH_LIKELY_TAKEN; 90 + } else 88 91 epc += 8; 89 92 regs->cp0_epc = epc; 90 93 break; ··· 94 93 case bgezal_op: 95 94 case bgezall_op: 96 95 regs->regs[31] = epc + 8; 97 - if ((long)regs->regs[insn.i_format.rs] >= 0) 96 + if ((long)regs->regs[insn.i_format.rs] >= 0) { 98 97 epc = epc + 4 + (insn.i_format.simmediate << 2); 99 - else 98 + if (insn.i_format.rt == bgezall_op) 99 + ret = BRANCH_LIKELY_TAKEN; 100 + } else 100 101 epc += 8; 101 102 regs->cp0_epc = epc; 102 103 break; 104 + 103 105 case bposge32_op: 104 106 if (!cpu_has_dsp) 105 107 goto sigill; ··· 137 133 case beq_op: 138 134 case beql_op: 139 135 if (regs->regs[insn.i_format.rs] == 140 - regs->regs[insn.i_format.rt]) 136 + regs->regs[insn.i_format.rt]) { 141 137 epc = epc + 4 + (insn.i_format.simmediate << 2); 142 - else 138 + if (insn.i_format.rt == beql_op) 139 + ret = BRANCH_LIKELY_TAKEN; 140 + } else 143 141 epc += 8; 144 142 regs->cp0_epc = epc; 145 143 break; ··· 149 143 case bne_op: 150 144 case bnel_op: 151 145 if (regs->regs[insn.i_format.rs] != 152 - regs->regs[insn.i_format.rt]) 146 + regs->regs[insn.i_format.rt]) { 153 147 epc = epc + 4 + (insn.i_format.simmediate << 2); 154 - else 148 + if (insn.i_format.rt == bnel_op) 149 + ret = BRANCH_LIKELY_TAKEN; 150 + } else 155 151 epc += 8; 156 152 regs->cp0_epc = epc; 157 153 break; ··· 161 153 case blez_op: /* not really i_format */ 162 154 case blezl_op: 163 155 /* rt field assumed to be zero */ 164 - if ((long)regs->regs[insn.i_format.rs] <= 0) 156 + if ((long)regs->regs[insn.i_format.rs] <= 0) { 165 157 epc = epc + 4 + (insn.i_format.simmediate << 2); 166 - else 158 + if (insn.i_format.rt == bnel_op) 159 + ret = BRANCH_LIKELY_TAKEN; 160 + } else 167 161 epc += 8; 168 162 regs->cp0_epc = epc; 169 163 break; ··· 173 163 case bgtz_op: 174 164 case bgtzl_op: 175 165 /* rt field assumed to be zero */ 176 - if ((long)regs->regs[insn.i_format.rs] > 0) 166 + if ((long)regs->regs[insn.i_format.rs] > 0) { 177 167 epc = epc + 4 + (insn.i_format.simmediate << 2); 178 - else 168 + if (insn.i_format.rt == bnel_op) 169 + ret = BRANCH_LIKELY_TAKEN; 170 + } else 179 171 epc += 8; 180 172 regs->cp0_epc = epc; 181 173 break; ··· 199 187 switch (insn.i_format.rt & 3) { 200 188 case 0: /* bc1f */ 201 189 case 2: /* bc1fl */ 202 - if (~fcr31 & (1 << bit)) 190 + if (~fcr31 & (1 << bit)) { 203 191 epc = epc + 4 + (insn.i_format.simmediate << 2); 204 - else 192 + if (insn.i_format.rt == 2) 193 + ret = BRANCH_LIKELY_TAKEN; 194 + } else 205 195 epc += 8; 206 196 regs->cp0_epc = epc; 207 197 break; 208 198 209 199 case 1: /* bc1t */ 210 200 case 3: /* bc1tl */ 211 - if (fcr31 & (1 << bit)) 201 + if (fcr31 & (1 << bit)) { 212 202 epc = epc + 4 + (insn.i_format.simmediate << 2); 213 - else 203 + if (insn.i_format.rt == 3) 204 + ret = BRANCH_LIKELY_TAKEN; 205 + } else 214 206 epc += 8; 215 207 regs->cp0_epc = epc; 216 208 break; ··· 255 239 #endif 256 240 } 257 241 258 - return 0; 242 + return ret; 243 + 244 + sigill: 245 + printk("%s: DSP branch but not DSP ASE - sending SIGBUS.\n", current->comm); 246 + force_sig(SIGBUS, current); 247 + return -EFAULT; 248 + } 249 + EXPORT_SYMBOL_GPL(__compute_return_epc_for_insn); 250 + 251 + int __compute_return_epc(struct pt_regs *regs) 252 + { 253 + unsigned int __user *addr; 254 + long epc; 255 + union mips_instruction insn; 256 + 257 + epc = regs->cp0_epc; 258 + if (epc & 3) 259 + goto unaligned; 260 + 261 + /* 262 + * Read the instruction 263 + */ 264 + addr = (unsigned int __user *) epc; 265 + if (__get_user(insn.word, addr)) { 266 + force_sig(SIGSEGV, current); 267 + return -EFAULT; 268 + } 269 + 270 + return __compute_return_epc_for_insn(regs, insn); 259 271 260 272 unaligned: 261 273 printk("%s: unaligned epc - sending SIGBUS.\n", current->comm); 262 274 force_sig(SIGBUS, current); 263 275 return -EFAULT; 264 276 265 - sigill: 266 - printk("%s: DSP branch but not DSP ASE - sending SIGBUS.\n", current->comm); 267 - force_sig(SIGBUS, current); 268 - return -EFAULT; 269 277 }
+1 -1
arch/mips/math-emu/cp1emu.c
··· 245 245 */ 246 246 emulpc = xcp->cp0_epc + 4; /* Snapshot emulation target */ 247 247 248 - if (__compute_return_epc(xcp)) { 248 + if (__compute_return_epc(xcp) < 0) { 249 249 #ifdef CP1DBG 250 250 printk("failed to emulate branch at %p\n", 251 251 (void *) (xcp->cp0_epc));