Blame view

arch/mips/kernel/branch.c 6.68 KB
1da177e4c   Linus Torvalds   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   Maneesh Soni   MIPS Kprobes: Ref...
12
  #include <linux/module.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
13
14
15
  #include <asm/branch.h>
  #include <asm/cpu.h>
  #include <asm/cpu-features.h>
1d74f6bc8   Ralf Baechle   __compute_return_...
16
  #include <asm/fpu.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17
18
19
  #include <asm/inst.h>
  #include <asm/ptrace.h>
  #include <asm/uaccess.h>
d8d4e3ae0   Maneesh Soni   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   Linus Torvalds   Linux-2.6.12-rc2
29
   */
d8d4e3ae0   Maneesh Soni   MIPS Kprobes: Ref...
30
31
  int __compute_return_epc_for_insn(struct pt_regs *regs,
  				   union mips_instruction insn)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
32
  {
5e0373b8e   Atsushi Nemoto   [MIPS] Add some _...
33
  	unsigned int bit, fcr31, dspcontrol;
d8d4e3ae0   Maneesh Soni   MIPS Kprobes: Ref...
34
35
  	long epc = regs->cp0_epc;
  	int ret = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
36

1da177e4c   Linus Torvalds   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   Maneesh Soni   MIPS Kprobes: Ref...
61
  			if ((long)regs->regs[insn.i_format.rs] < 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
62
  				epc = epc + 4 + (insn.i_format.simmediate << 2);
d8d4e3ae0   Maneesh Soni   MIPS Kprobes: Ref...
63
64
65
  				if (insn.i_format.rt == bltzl_op)
  					ret = BRANCH_LIKELY_TAKEN;
  			} else
1da177e4c   Linus Torvalds   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   Maneesh Soni   MIPS Kprobes: Ref...
72
  			if ((long)regs->regs[insn.i_format.rs] >= 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
73
  				epc = epc + 4 + (insn.i_format.simmediate << 2);
d8d4e3ae0   Maneesh Soni   MIPS Kprobes: Ref...
74
75
76
  				if (insn.i_format.rt == bgezl_op)
  					ret = BRANCH_LIKELY_TAKEN;
  			} else
1da177e4c   Linus Torvalds   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   Maneesh Soni   MIPS Kprobes: Ref...
84
  			if ((long)regs->regs[insn.i_format.rs] < 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
85
  				epc = epc + 4 + (insn.i_format.simmediate << 2);
d8d4e3ae0   Maneesh Soni   MIPS Kprobes: Ref...
86
87
88
  				if (insn.i_format.rt == bltzall_op)
  					ret = BRANCH_LIKELY_TAKEN;
  			} else
1da177e4c   Linus Torvalds   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   Maneesh Soni   MIPS Kprobes: Ref...
96
  			if ((long)regs->regs[insn.i_format.rs] >= 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
97
  				epc = epc + 4 + (insn.i_format.simmediate << 2);
d8d4e3ae0   Maneesh Soni   MIPS Kprobes: Ref...
98
99
100
  				if (insn.i_format.rt == bgezall_op)
  					ret = BRANCH_LIKELY_TAKEN;
  			} else
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
101
102
103
  				epc += 8;
  			regs->cp0_epc = epc;
  			break;
d8d4e3ae0   Maneesh Soni   MIPS Kprobes: Ref...
104

e50c0a8fa   Ralf Baechle   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   Linus Torvalds   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   Maneesh Soni   MIPS Kprobes: Ref...
139
  		    regs->regs[insn.i_format.rt]) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
140
  			epc = epc + 4 + (insn.i_format.simmediate << 2);
d8d4e3ae0   Maneesh Soni   MIPS Kprobes: Ref...
141
142
143
  			if (insn.i_format.rt == beql_op)
  				ret = BRANCH_LIKELY_TAKEN;
  		} else
1da177e4c   Linus Torvalds   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   Maneesh Soni   MIPS Kprobes: Ref...
151
  		    regs->regs[insn.i_format.rt]) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
152
  			epc = epc + 4 + (insn.i_format.simmediate << 2);
d8d4e3ae0   Maneesh Soni   MIPS Kprobes: Ref...
153
154
155
  			if (insn.i_format.rt == bnel_op)
  				ret = BRANCH_LIKELY_TAKEN;
  		} else
1da177e4c   Linus Torvalds   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   Maneesh Soni   MIPS Kprobes: Ref...
163
  		if ((long)regs->regs[insn.i_format.rs] <= 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
164
  			epc = epc + 4 + (insn.i_format.simmediate << 2);
d8d4e3ae0   Maneesh Soni   MIPS Kprobes: Ref...
165
166
167
  			if (insn.i_format.rt == bnel_op)
  				ret = BRANCH_LIKELY_TAKEN;
  		} else
1da177e4c   Linus Torvalds   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   Maneesh Soni   MIPS Kprobes: Ref...
175
  		if ((long)regs->regs[insn.i_format.rs] > 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
176
  			epc = epc + 4 + (insn.i_format.simmediate << 2);
d8d4e3ae0   Maneesh Soni   MIPS Kprobes: Ref...
177
178
179
  			if (insn.i_format.rt == bnel_op)
  				ret = BRANCH_LIKELY_TAKEN;
  		} else
1da177e4c   Linus Torvalds   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   Ralf Baechle   __compute_return_...
188
189
  		preempt_disable();
  		if (is_fpu_owner())
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
190
  			asm volatile("cfc1\t%0,$31" : "=r" (fcr31));
1d74f6bc8   Ralf Baechle   __compute_return_...
191
  		else
eae89076e   Atsushi Nemoto   [MIPS] Unify mips...
192
  			fcr31 = current->thread.fpu.fcr31;
1d74f6bc8   Ralf Baechle   __compute_return_...
193
  		preempt_enable();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
194
195
196
  		bit = (insn.i_format.rt >> 2);
  		bit += (bit != 0);
  		bit += 23;
ee1cca1b0   Ralf Baechle   [MIPS] Fix branch...
197
  		switch (insn.i_format.rt & 3) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
198
199
  		case 0:	/* bc1f */
  		case 2:	/* bc1fl */
d8d4e3ae0   Maneesh Soni   MIPS Kprobes: Ref...
200
  			if (~fcr31 & (1 << bit)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
201
  				epc = epc + 4 + (insn.i_format.simmediate << 2);
d8d4e3ae0   Maneesh Soni   MIPS Kprobes: Ref...
202
203
204
  				if (insn.i_format.rt == 2)
  					ret = BRANCH_LIKELY_TAKEN;
  			} else
1da177e4c   Linus Torvalds   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   Maneesh Soni   MIPS Kprobes: Ref...
211
  			if (fcr31 & (1 << bit)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
212
  				epc = epc + 4 + (insn.i_format.simmediate << 2);
d8d4e3ae0   Maneesh Soni   MIPS Kprobes: Ref...
213
214
215
  				if (insn.i_format.rt == 3)
  					ret = BRANCH_LIKELY_TAKEN;
  			} else
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
216
217
218
219
220
  				epc += 8;
  			regs->cp0_epc = epc;
  			break;
  		}
  		break;
126336f06   David Daney   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   Linus Torvalds   Linux-2.6.12-rc2
254
  	}
d8d4e3ae0   Maneesh Soni   MIPS Kprobes: Ref...
255
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
256

d8d4e3ae0   Maneesh Soni   MIPS Kprobes: Ref...
257
258
259
  sigill:
  	printk("%s: DSP branch but not DSP ASE - sending SIGBUS.
  ", current->comm);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
260
261
  	force_sig(SIGBUS, current);
  	return -EFAULT;
d8d4e3ae0   Maneesh Soni   MIPS Kprobes: Ref...
262
263
  }
  EXPORT_SYMBOL_GPL(__compute_return_epc_for_insn);
e50c0a8fa   Ralf Baechle   Support the MIPS3...
264

d8d4e3ae0   Maneesh Soni   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   Ralf Baechle   Support the MIPS3...
289
290
  	force_sig(SIGBUS, current);
  	return -EFAULT;
d8d4e3ae0   Maneesh Soni   MIPS Kprobes: Ref...
291

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
292
  }