blob: 6b5df8bfab85d0a97b503ad2ca441ff37d4c21d0 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * This file is subject to the terms and conditions of the GNU General Public
3 * License. See the file "COPYING" in the main directory of this archive
4 * for more details.
5 *
6 * Copyright (C) 1996, 97, 2000, 2001 by Ralf Baechle
7 * Copyright (C) 2001 MIPS Technologies, Inc.
8 */
9#include <linux/kernel.h>
10#include <linux/sched.h>
11#include <linux/signal.h>
12#include <asm/branch.h>
13#include <asm/cpu.h>
14#include <asm/cpu-features.h>
Ralf Baechle1d74f6b2005-05-09 13:16:07 +000015#include <asm/fpu.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070016#include <asm/inst.h>
17#include <asm/ptrace.h>
18#include <asm/uaccess.h>
19
20/*
21 * Compute the return address and do emulate branch simulation, if required.
22 */
23int __compute_return_epc(struct pt_regs *regs)
24{
Atsushi Nemoto5e0373b2007-07-13 23:02:42 +090025 unsigned int __user *addr;
26 unsigned int bit, fcr31, dspcontrol;
Linus Torvalds1da177e2005-04-16 15:20:36 -070027 long epc;
28 union mips_instruction insn;
29
30 epc = regs->cp0_epc;
31 if (epc & 3)
32 goto unaligned;
33
34 /*
35 * Read the instruction
36 */
Atsushi Nemoto5e0373b2007-07-13 23:02:42 +090037 addr = (unsigned int __user *) epc;
Linus Torvalds1da177e2005-04-16 15:20:36 -070038 if (__get_user(insn.word, addr)) {
39 force_sig(SIGSEGV, current);
40 return -EFAULT;
41 }
42
43 regs->regs[0] = 0;
44 switch (insn.i_format.opcode) {
45 /*
46 * jr and jalr are in r_format format.
47 */
48 case spec_op:
49 switch (insn.r_format.func) {
50 case jalr_op:
51 regs->regs[insn.r_format.rd] = epc + 8;
52 /* Fall through */
53 case jr_op:
54 regs->cp0_epc = regs->regs[insn.r_format.rs];
55 break;
56 }
57 break;
58
59 /*
60 * This group contains:
61 * bltz_op, bgez_op, bltzl_op, bgezl_op,
62 * bltzal_op, bgezal_op, bltzall_op, bgezall_op.
63 */
64 case bcond_op:
65 switch (insn.i_format.rt) {
66 case bltz_op:
67 case bltzl_op:
68 if ((long)regs->regs[insn.i_format.rs] < 0)
69 epc = epc + 4 + (insn.i_format.simmediate << 2);
70 else
71 epc += 8;
72 regs->cp0_epc = epc;
73 break;
74
75 case bgez_op:
76 case bgezl_op:
77 if ((long)regs->regs[insn.i_format.rs] >= 0)
78 epc = epc + 4 + (insn.i_format.simmediate << 2);
79 else
80 epc += 8;
81 regs->cp0_epc = epc;
82 break;
83
84 case bltzal_op:
85 case bltzall_op:
86 regs->regs[31] = epc + 8;
87 if ((long)regs->regs[insn.i_format.rs] < 0)
88 epc = epc + 4 + (insn.i_format.simmediate << 2);
89 else
90 epc += 8;
91 regs->cp0_epc = epc;
92 break;
93
94 case bgezal_op:
95 case bgezall_op:
96 regs->regs[31] = epc + 8;
97 if ((long)regs->regs[insn.i_format.rs] >= 0)
98 epc = epc + 4 + (insn.i_format.simmediate << 2);
99 else
100 epc += 8;
101 regs->cp0_epc = epc;
102 break;
Ralf Baechlee50c0a82005-05-31 11:49:19 +0000103 case bposge32_op:
104 if (!cpu_has_dsp)
105 goto sigill;
106
107 dspcontrol = rddsp(0x01);
108
109 if (dspcontrol >= 32) {
110 epc = epc + 4 + (insn.i_format.simmediate << 2);
111 } else
112 epc += 8;
113 regs->cp0_epc = epc;
114 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115 }
116 break;
117
118 /*
119 * These are unconditional and in j_format.
120 */
121 case jal_op:
122 regs->regs[31] = regs->cp0_epc + 8;
123 case j_op:
124 epc += 4;
125 epc >>= 28;
126 epc <<= 28;
127 epc |= (insn.j_format.target << 2);
128 regs->cp0_epc = epc;
129 break;
130
131 /*
132 * These are conditional and in i_format.
133 */
134 case beq_op:
135 case beql_op:
136 if (regs->regs[insn.i_format.rs] ==
137 regs->regs[insn.i_format.rt])
138 epc = epc + 4 + (insn.i_format.simmediate << 2);
139 else
140 epc += 8;
141 regs->cp0_epc = epc;
142 break;
143
144 case bne_op:
145 case bnel_op:
146 if (regs->regs[insn.i_format.rs] !=
147 regs->regs[insn.i_format.rt])
148 epc = epc + 4 + (insn.i_format.simmediate << 2);
149 else
150 epc += 8;
151 regs->cp0_epc = epc;
152 break;
153
154 case blez_op: /* not really i_format */
155 case blezl_op:
156 /* rt field assumed to be zero */
157 if ((long)regs->regs[insn.i_format.rs] <= 0)
158 epc = epc + 4 + (insn.i_format.simmediate << 2);
159 else
160 epc += 8;
161 regs->cp0_epc = epc;
162 break;
163
164 case bgtz_op:
165 case bgtzl_op:
166 /* rt field assumed to be zero */
167 if ((long)regs->regs[insn.i_format.rs] > 0)
168 epc = epc + 4 + (insn.i_format.simmediate << 2);
169 else
170 epc += 8;
171 regs->cp0_epc = epc;
172 break;
173
174 /*
175 * And now the FPA/cp1 branch instructions.
176 */
177 case cop1_op:
Ralf Baechle1d74f6b2005-05-09 13:16:07 +0000178 preempt_disable();
179 if (is_fpu_owner())
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180 asm volatile("cfc1\t%0,$31" : "=r" (fcr31));
Ralf Baechle1d74f6b2005-05-09 13:16:07 +0000181 else
Atsushi Nemotoeae89072006-05-16 01:26:03 +0900182 fcr31 = current->thread.fpu.fcr31;
Ralf Baechle1d74f6b2005-05-09 13:16:07 +0000183 preempt_enable();
184
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185 bit = (insn.i_format.rt >> 2);
186 bit += (bit != 0);
187 bit += 23;
Ralf Baechleee1cca12006-04-26 21:33:03 +0100188 switch (insn.i_format.rt & 3) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189 case 0: /* bc1f */
190 case 2: /* bc1fl */
191 if (~fcr31 & (1 << bit))
192 epc = epc + 4 + (insn.i_format.simmediate << 2);
193 else
194 epc += 8;
195 regs->cp0_epc = epc;
196 break;
197
198 case 1: /* bc1t */
199 case 3: /* bc1tl */
200 if (fcr31 & (1 << bit))
201 epc = epc + 4 + (insn.i_format.simmediate << 2);
202 else
203 epc += 8;
204 regs->cp0_epc = epc;
205 break;
206 }
207 break;
208 }
209
210 return 0;
211
212unaligned:
213 printk("%s: unaligned epc - sending SIGBUS.\n", current->comm);
214 force_sig(SIGBUS, current);
215 return -EFAULT;
Ralf Baechlee50c0a82005-05-31 11:49:19 +0000216
217sigill:
218 printk("%s: DSP branch but not DSP ASE - sending SIGBUS.\n", current->comm);
219 force_sig(SIGBUS, current);
220 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221}