blob: 56aea5f526a7dbcef839b0ca352d58b745196923 [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{
25 unsigned int *addr, bit, fcr31;
26 long epc;
27 union mips_instruction insn;
28
29 epc = regs->cp0_epc;
30 if (epc & 3)
31 goto unaligned;
32
33 /*
34 * Read the instruction
35 */
36 addr = (unsigned int *) epc;
37 if (__get_user(insn.word, addr)) {
38 force_sig(SIGSEGV, current);
39 return -EFAULT;
40 }
41
42 regs->regs[0] = 0;
43 switch (insn.i_format.opcode) {
44 /*
45 * jr and jalr are in r_format format.
46 */
47 case spec_op:
48 switch (insn.r_format.func) {
49 case jalr_op:
50 regs->regs[insn.r_format.rd] = epc + 8;
51 /* Fall through */
52 case jr_op:
53 regs->cp0_epc = regs->regs[insn.r_format.rs];
54 break;
55 }
56 break;
57
58 /*
59 * This group contains:
60 * bltz_op, bgez_op, bltzl_op, bgezl_op,
61 * bltzal_op, bgezal_op, bltzall_op, bgezall_op.
62 */
63 case bcond_op:
64 switch (insn.i_format.rt) {
65 case bltz_op:
66 case bltzl_op:
67 if ((long)regs->regs[insn.i_format.rs] < 0)
68 epc = epc + 4 + (insn.i_format.simmediate << 2);
69 else
70 epc += 8;
71 regs->cp0_epc = epc;
72 break;
73
74 case bgez_op:
75 case bgezl_op:
76 if ((long)regs->regs[insn.i_format.rs] >= 0)
77 epc = epc + 4 + (insn.i_format.simmediate << 2);
78 else
79 epc += 8;
80 regs->cp0_epc = epc;
81 break;
82
83 case bltzal_op:
84 case bltzall_op:
85 regs->regs[31] = epc + 8;
86 if ((long)regs->regs[insn.i_format.rs] < 0)
87 epc = epc + 4 + (insn.i_format.simmediate << 2);
88 else
89 epc += 8;
90 regs->cp0_epc = epc;
91 break;
92
93 case bgezal_op:
94 case bgezall_op:
95 regs->regs[31] = epc + 8;
96 if ((long)regs->regs[insn.i_format.rs] >= 0)
97 epc = epc + 4 + (insn.i_format.simmediate << 2);
98 else
99 epc += 8;
100 regs->cp0_epc = epc;
101 break;
102 }
103 break;
104
105 /*
106 * These are unconditional and in j_format.
107 */
108 case jal_op:
109 regs->regs[31] = regs->cp0_epc + 8;
110 case j_op:
111 epc += 4;
112 epc >>= 28;
113 epc <<= 28;
114 epc |= (insn.j_format.target << 2);
115 regs->cp0_epc = epc;
116 break;
117
118 /*
119 * These are conditional and in i_format.
120 */
121 case beq_op:
122 case beql_op:
123 if (regs->regs[insn.i_format.rs] ==
124 regs->regs[insn.i_format.rt])
125 epc = epc + 4 + (insn.i_format.simmediate << 2);
126 else
127 epc += 8;
128 regs->cp0_epc = epc;
129 break;
130
131 case bne_op:
132 case bnel_op:
133 if (regs->regs[insn.i_format.rs] !=
134 regs->regs[insn.i_format.rt])
135 epc = epc + 4 + (insn.i_format.simmediate << 2);
136 else
137 epc += 8;
138 regs->cp0_epc = epc;
139 break;
140
141 case blez_op: /* not really i_format */
142 case blezl_op:
143 /* rt field assumed to be zero */
144 if ((long)regs->regs[insn.i_format.rs] <= 0)
145 epc = epc + 4 + (insn.i_format.simmediate << 2);
146 else
147 epc += 8;
148 regs->cp0_epc = epc;
149 break;
150
151 case bgtz_op:
152 case bgtzl_op:
153 /* rt field assumed to be zero */
154 if ((long)regs->regs[insn.i_format.rs] > 0)
155 epc = epc + 4 + (insn.i_format.simmediate << 2);
156 else
157 epc += 8;
158 regs->cp0_epc = epc;
159 break;
160
161 /*
162 * And now the FPA/cp1 branch instructions.
163 */
164 case cop1_op:
Ralf Baechle1d74f6b2005-05-09 13:16:07 +0000165 preempt_disable();
166 if (is_fpu_owner())
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167 asm volatile("cfc1\t%0,$31" : "=r" (fcr31));
Ralf Baechle1d74f6b2005-05-09 13:16:07 +0000168 else
169 fcr31 = current->thread.fpu.hard.fcr31;
170 preempt_enable();
171
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172 bit = (insn.i_format.rt >> 2);
173 bit += (bit != 0);
174 bit += 23;
175 switch (insn.i_format.rt) {
176 case 0: /* bc1f */
177 case 2: /* bc1fl */
178 if (~fcr31 & (1 << bit))
179 epc = epc + 4 + (insn.i_format.simmediate << 2);
180 else
181 epc += 8;
182 regs->cp0_epc = epc;
183 break;
184
185 case 1: /* bc1t */
186 case 3: /* bc1tl */
187 if (fcr31 & (1 << bit))
188 epc = epc + 4 + (insn.i_format.simmediate << 2);
189 else
190 epc += 8;
191 regs->cp0_epc = epc;
192 break;
193 }
194 break;
195 }
196
197 return 0;
198
199unaligned:
200 printk("%s: unaligned epc - sending SIGBUS.\n", current->comm);
201 force_sig(SIGBUS, current);
202 return -EFAULT;
203}