Blame view
arch/mips/kernel/branch.c
6.68 KB
1da177e4c Linux-2.6.12-rc2 |
1 2 3 4 5 6 7 8 9 10 11 |
/* * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * * Copyright (C) 1996, 97, 2000, 2001 by Ralf Baechle * Copyright (C) 2001 MIPS Technologies, Inc. */ #include <linux/kernel.h> #include <linux/sched.h> #include <linux/signal.h> |
d8d4e3ae0 MIPS Kprobes: Ref... |
12 |
#include <linux/module.h> |
1da177e4c Linux-2.6.12-rc2 |
13 14 15 |
#include <asm/branch.h> #include <asm/cpu.h> #include <asm/cpu-features.h> |
1d74f6bc8 __compute_return_... |
16 |
#include <asm/fpu.h> |
1da177e4c Linux-2.6.12-rc2 |
17 18 19 |
#include <asm/inst.h> #include <asm/ptrace.h> #include <asm/uaccess.h> |
d8d4e3ae0 MIPS Kprobes: Ref... |
20 21 22 23 24 25 26 27 28 |
/** * __compute_return_epc_for_insn - Computes the return address and do emulate * branch simulation, if required. * * @regs: Pointer to pt_regs * @insn: branch instruction to decode * @returns: -EFAULT on error and forces SIGBUS, and on success * returns 0 or BRANCH_LIKELY_TAKEN as appropriate after * evaluating the branch. |
1da177e4c Linux-2.6.12-rc2 |
29 |
*/ |
d8d4e3ae0 MIPS Kprobes: Ref... |
30 31 |
int __compute_return_epc_for_insn(struct pt_regs *regs, union mips_instruction insn) |
1da177e4c Linux-2.6.12-rc2 |
32 |
{ |
5e0373b8e [MIPS] Add some _... |
33 |
unsigned int bit, fcr31, dspcontrol; |
d8d4e3ae0 MIPS Kprobes: Ref... |
34 35 |
long epc = regs->cp0_epc; int ret = 0; |
1da177e4c Linux-2.6.12-rc2 |
36 |
|
1da177e4c Linux-2.6.12-rc2 |
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
switch (insn.i_format.opcode) { /* * jr and jalr are in r_format format. */ case spec_op: switch (insn.r_format.func) { case jalr_op: regs->regs[insn.r_format.rd] = epc + 8; /* Fall through */ case jr_op: regs->cp0_epc = regs->regs[insn.r_format.rs]; break; } break; /* * This group contains: * bltz_op, bgez_op, bltzl_op, bgezl_op, * bltzal_op, bgezal_op, bltzall_op, bgezall_op. */ case bcond_op: switch (insn.i_format.rt) { case bltz_op: case bltzl_op: |
d8d4e3ae0 MIPS Kprobes: Ref... |
61 |
if ((long)regs->regs[insn.i_format.rs] < 0) { |
1da177e4c Linux-2.6.12-rc2 |
62 |
epc = epc + 4 + (insn.i_format.simmediate << 2); |
d8d4e3ae0 MIPS Kprobes: Ref... |
63 64 65 |
if (insn.i_format.rt == bltzl_op) ret = BRANCH_LIKELY_TAKEN; } else |
1da177e4c Linux-2.6.12-rc2 |
66 67 68 69 70 71 |
epc += 8; regs->cp0_epc = epc; break; case bgez_op: case bgezl_op: |
d8d4e3ae0 MIPS Kprobes: Ref... |
72 |
if ((long)regs->regs[insn.i_format.rs] >= 0) { |
1da177e4c Linux-2.6.12-rc2 |
73 |
epc = epc + 4 + (insn.i_format.simmediate << 2); |
d8d4e3ae0 MIPS Kprobes: Ref... |
74 75 76 |
if (insn.i_format.rt == bgezl_op) ret = BRANCH_LIKELY_TAKEN; } else |
1da177e4c Linux-2.6.12-rc2 |
77 78 79 80 81 82 83 |
epc += 8; regs->cp0_epc = epc; break; case bltzal_op: case bltzall_op: regs->regs[31] = epc + 8; |
d8d4e3ae0 MIPS Kprobes: Ref... |
84 |
if ((long)regs->regs[insn.i_format.rs] < 0) { |
1da177e4c Linux-2.6.12-rc2 |
85 |
epc = epc + 4 + (insn.i_format.simmediate << 2); |
d8d4e3ae0 MIPS Kprobes: Ref... |
86 87 88 |
if (insn.i_format.rt == bltzall_op) ret = BRANCH_LIKELY_TAKEN; } else |
1da177e4c Linux-2.6.12-rc2 |
89 90 91 92 93 94 95 |
epc += 8; regs->cp0_epc = epc; break; case bgezal_op: case bgezall_op: regs->regs[31] = epc + 8; |
d8d4e3ae0 MIPS Kprobes: Ref... |
96 |
if ((long)regs->regs[insn.i_format.rs] >= 0) { |
1da177e4c Linux-2.6.12-rc2 |
97 |
epc = epc + 4 + (insn.i_format.simmediate << 2); |
d8d4e3ae0 MIPS Kprobes: Ref... |
98 99 100 |
if (insn.i_format.rt == bgezall_op) ret = BRANCH_LIKELY_TAKEN; } else |
1da177e4c Linux-2.6.12-rc2 |
101 102 103 |
epc += 8; regs->cp0_epc = epc; break; |
d8d4e3ae0 MIPS Kprobes: Ref... |
104 |
|
e50c0a8fa Support the MIPS3... |
105 106 107 108 109 110 111 112 113 114 115 116 |
case bposge32_op: if (!cpu_has_dsp) goto sigill; dspcontrol = rddsp(0x01); if (dspcontrol >= 32) { epc = epc + 4 + (insn.i_format.simmediate << 2); } else epc += 8; regs->cp0_epc = epc; break; |
1da177e4c Linux-2.6.12-rc2 |
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
} break; /* * These are unconditional and in j_format. */ case jal_op: regs->regs[31] = regs->cp0_epc + 8; case j_op: epc += 4; epc >>= 28; epc <<= 28; epc |= (insn.j_format.target << 2); regs->cp0_epc = epc; break; /* * These are conditional and in i_format. */ case beq_op: case beql_op: if (regs->regs[insn.i_format.rs] == |
d8d4e3ae0 MIPS Kprobes: Ref... |
139 |
regs->regs[insn.i_format.rt]) { |
1da177e4c Linux-2.6.12-rc2 |
140 |
epc = epc + 4 + (insn.i_format.simmediate << 2); |
d8d4e3ae0 MIPS Kprobes: Ref... |
141 142 143 |
if (insn.i_format.rt == beql_op) ret = BRANCH_LIKELY_TAKEN; } else |
1da177e4c Linux-2.6.12-rc2 |
144 145 146 147 148 149 150 |
epc += 8; regs->cp0_epc = epc; break; case bne_op: case bnel_op: if (regs->regs[insn.i_format.rs] != |
d8d4e3ae0 MIPS Kprobes: Ref... |
151 |
regs->regs[insn.i_format.rt]) { |
1da177e4c Linux-2.6.12-rc2 |
152 |
epc = epc + 4 + (insn.i_format.simmediate << 2); |
d8d4e3ae0 MIPS Kprobes: Ref... |
153 154 155 |
if (insn.i_format.rt == bnel_op) ret = BRANCH_LIKELY_TAKEN; } else |
1da177e4c Linux-2.6.12-rc2 |
156 157 158 159 160 161 162 |
epc += 8; regs->cp0_epc = epc; break; case blez_op: /* not really i_format */ case blezl_op: /* rt field assumed to be zero */ |
d8d4e3ae0 MIPS Kprobes: Ref... |
163 |
if ((long)regs->regs[insn.i_format.rs] <= 0) { |
1da177e4c Linux-2.6.12-rc2 |
164 |
epc = epc + 4 + (insn.i_format.simmediate << 2); |
d8d4e3ae0 MIPS Kprobes: Ref... |
165 166 167 |
if (insn.i_format.rt == bnel_op) ret = BRANCH_LIKELY_TAKEN; } else |
1da177e4c Linux-2.6.12-rc2 |
168 169 170 171 172 173 174 |
epc += 8; regs->cp0_epc = epc; break; case bgtz_op: case bgtzl_op: /* rt field assumed to be zero */ |
d8d4e3ae0 MIPS Kprobes: Ref... |
175 |
if ((long)regs->regs[insn.i_format.rs] > 0) { |
1da177e4c Linux-2.6.12-rc2 |
176 |
epc = epc + 4 + (insn.i_format.simmediate << 2); |
d8d4e3ae0 MIPS Kprobes: Ref... |
177 178 179 |
if (insn.i_format.rt == bnel_op) ret = BRANCH_LIKELY_TAKEN; } else |
1da177e4c Linux-2.6.12-rc2 |
180 181 182 183 184 185 186 187 |
epc += 8; regs->cp0_epc = epc; break; /* * And now the FPA/cp1 branch instructions. */ case cop1_op: |
1d74f6bc8 __compute_return_... |
188 189 |
preempt_disable(); if (is_fpu_owner()) |
1da177e4c Linux-2.6.12-rc2 |
190 |
asm volatile("cfc1\t%0,$31" : "=r" (fcr31)); |
1d74f6bc8 __compute_return_... |
191 |
else |
eae89076e [MIPS] Unify mips... |
192 |
fcr31 = current->thread.fpu.fcr31; |
1d74f6bc8 __compute_return_... |
193 |
preempt_enable(); |
1da177e4c Linux-2.6.12-rc2 |
194 195 196 |
bit = (insn.i_format.rt >> 2); bit += (bit != 0); bit += 23; |
ee1cca1b0 [MIPS] Fix branch... |
197 |
switch (insn.i_format.rt & 3) { |
1da177e4c Linux-2.6.12-rc2 |
198 199 |
case 0: /* bc1f */ case 2: /* bc1fl */ |
d8d4e3ae0 MIPS Kprobes: Ref... |
200 |
if (~fcr31 & (1 << bit)) { |
1da177e4c Linux-2.6.12-rc2 |
201 |
epc = epc + 4 + (insn.i_format.simmediate << 2); |
d8d4e3ae0 MIPS Kprobes: Ref... |
202 203 204 |
if (insn.i_format.rt == 2) ret = BRANCH_LIKELY_TAKEN; } else |
1da177e4c Linux-2.6.12-rc2 |
205 206 207 208 209 210 |
epc += 8; regs->cp0_epc = epc; break; case 1: /* bc1t */ case 3: /* bc1tl */ |
d8d4e3ae0 MIPS Kprobes: Ref... |
211 |
if (fcr31 & (1 << bit)) { |
1da177e4c Linux-2.6.12-rc2 |
212 |
epc = epc + 4 + (insn.i_format.simmediate << 2); |
d8d4e3ae0 MIPS Kprobes: Ref... |
213 214 215 |
if (insn.i_format.rt == 3) ret = BRANCH_LIKELY_TAKEN; } else |
1da177e4c Linux-2.6.12-rc2 |
216 217 218 219 220 |
epc += 8; regs->cp0_epc = epc; break; } break; |
126336f06 MIPS: Compute br... |
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 |
#ifdef CONFIG_CPU_CAVIUM_OCTEON case lwc2_op: /* This is bbit0 on Octeon */ if ((regs->regs[insn.i_format.rs] & (1ull<<insn.i_format.rt)) == 0) epc = epc + 4 + (insn.i_format.simmediate << 2); else epc += 8; regs->cp0_epc = epc; break; case ldc2_op: /* This is bbit032 on Octeon */ if ((regs->regs[insn.i_format.rs] & (1ull<<(insn.i_format.rt+32))) == 0) epc = epc + 4 + (insn.i_format.simmediate << 2); else epc += 8; regs->cp0_epc = epc; break; case swc2_op: /* This is bbit1 on Octeon */ if (regs->regs[insn.i_format.rs] & (1ull<<insn.i_format.rt)) epc = epc + 4 + (insn.i_format.simmediate << 2); else epc += 8; regs->cp0_epc = epc; break; case sdc2_op: /* This is bbit132 on Octeon */ if (regs->regs[insn.i_format.rs] & (1ull<<(insn.i_format.rt+32))) epc = epc + 4 + (insn.i_format.simmediate << 2); else epc += 8; regs->cp0_epc = epc; break; #endif |
1da177e4c Linux-2.6.12-rc2 |
254 |
} |
d8d4e3ae0 MIPS Kprobes: Ref... |
255 |
return ret; |
1da177e4c Linux-2.6.12-rc2 |
256 |
|
d8d4e3ae0 MIPS Kprobes: Ref... |
257 258 259 |
sigill: printk("%s: DSP branch but not DSP ASE - sending SIGBUS. ", current->comm); |
1da177e4c Linux-2.6.12-rc2 |
260 261 |
force_sig(SIGBUS, current); return -EFAULT; |
d8d4e3ae0 MIPS Kprobes: Ref... |
262 263 |
} EXPORT_SYMBOL_GPL(__compute_return_epc_for_insn); |
e50c0a8fa Support the MIPS3... |
264 |
|
d8d4e3ae0 MIPS Kprobes: Ref... |
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 |
int __compute_return_epc(struct pt_regs *regs) { unsigned int __user *addr; long epc; union mips_instruction insn; epc = regs->cp0_epc; if (epc & 3) goto unaligned; /* * Read the instruction */ addr = (unsigned int __user *) epc; if (__get_user(insn.word, addr)) { force_sig(SIGSEGV, current); return -EFAULT; } return __compute_return_epc_for_insn(regs, insn); unaligned: printk("%s: unaligned epc - sending SIGBUS. ", current->comm); |
e50c0a8fa Support the MIPS3... |
289 290 |
force_sig(SIGBUS, current); return -EFAULT; |
d8d4e3ae0 MIPS Kprobes: Ref... |
291 |
|
1da177e4c Linux-2.6.12-rc2 |
292 |
} |