aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips/kernel/branch.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/mips/kernel/branch.c
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'arch/mips/kernel/branch.c')
-rw-r--r--arch/mips/kernel/branch.c199
1 files changed, 199 insertions, 0 deletions
diff --git a/arch/mips/kernel/branch.c b/arch/mips/kernel/branch.c
new file mode 100644
index 000000000000..01117e977a7f
--- /dev/null
+++ b/arch/mips/kernel/branch.c
@@ -0,0 +1,199 @@
1/*
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>
15#include <asm/inst.h>
16#include <asm/ptrace.h>
17#include <asm/uaccess.h>
18
19/*
20 * Compute the return address and do emulate branch simulation, if required.
21 */
22int __compute_return_epc(struct pt_regs *regs)
23{
24 unsigned int *addr, bit, fcr31;
25 long epc;
26 union mips_instruction insn;
27
28 epc = regs->cp0_epc;
29 if (epc & 3)
30 goto unaligned;
31
32 /*
33 * Read the instruction
34 */
35 addr = (unsigned int *) epc;
36 if (__get_user(insn.word, addr)) {
37 force_sig(SIGSEGV, current);
38 return -EFAULT;
39 }
40
41 regs->regs[0] = 0;
42 switch (insn.i_format.opcode) {
43 /*
44 * jr and jalr are in r_format format.
45 */
46 case spec_op:
47 switch (insn.r_format.func) {
48 case jalr_op:
49 regs->regs[insn.r_format.rd] = epc + 8;
50 /* Fall through */
51 case jr_op:
52 regs->cp0_epc = regs->regs[insn.r_format.rs];
53 break;
54 }
55 break;
56
57 /*
58 * This group contains:
59 * bltz_op, bgez_op, bltzl_op, bgezl_op,
60 * bltzal_op, bgezal_op, bltzall_op, bgezall_op.
61 */
62 case bcond_op:
63 switch (insn.i_format.rt) {
64 case bltz_op:
65 case bltzl_op:
66 if ((long)regs->regs[insn.i_format.rs] < 0)
67 epc = epc + 4 + (insn.i_format.simmediate << 2);
68 else
69 epc += 8;
70 regs->cp0_epc = epc;
71 break;
72
73 case bgez_op:
74 case bgezl_op:
75 if ((long)regs->regs[insn.i_format.rs] >= 0)
76 epc = epc + 4 + (insn.i_format.simmediate << 2);
77 else
78 epc += 8;
79 regs->cp0_epc = epc;
80 break;
81
82 case bltzal_op:
83 case bltzall_op:
84 regs->regs[31] = epc + 8;
85 if ((long)regs->regs[insn.i_format.rs] < 0)
86 epc = epc + 4 + (insn.i_format.simmediate << 2);
87 else
88 epc += 8;
89 regs->cp0_epc = epc;
90 break;
91
92 case bgezal_op:
93 case bgezall_op:
94 regs->regs[31] = epc + 8;
95 if ((long)regs->regs[insn.i_format.rs] >= 0)
96 epc = epc + 4 + (insn.i_format.simmediate << 2);
97 else
98 epc += 8;
99 regs->cp0_epc = epc;
100 break;
101 }
102 break;
103
104 /*
105 * These are unconditional and in j_format.
106 */
107 case jal_op:
108 regs->regs[31] = regs->cp0_epc + 8;
109 case j_op:
110 epc += 4;
111 epc >>= 28;
112 epc <<= 28;
113 epc |= (insn.j_format.target << 2);
114 regs->cp0_epc = epc;
115 break;
116
117 /*
118 * These are conditional and in i_format.
119 */
120 case beq_op:
121 case beql_op:
122 if (regs->regs[insn.i_format.rs] ==
123 regs->regs[insn.i_format.rt])
124 epc = epc + 4 + (insn.i_format.simmediate << 2);
125 else
126 epc += 8;
127 regs->cp0_epc = epc;
128 break;
129
130 case bne_op:
131 case bnel_op:
132 if (regs->regs[insn.i_format.rs] !=
133 regs->regs[insn.i_format.rt])
134 epc = epc + 4 + (insn.i_format.simmediate << 2);
135 else
136 epc += 8;
137 regs->cp0_epc = epc;
138 break;
139
140 case blez_op: /* not really i_format */
141 case blezl_op:
142 /* rt field assumed to be zero */
143 if ((long)regs->regs[insn.i_format.rs] <= 0)
144 epc = epc + 4 + (insn.i_format.simmediate << 2);
145 else
146 epc += 8;
147 regs->cp0_epc = epc;
148 break;
149
150 case bgtz_op:
151 case bgtzl_op:
152 /* rt field assumed to be zero */
153 if ((long)regs->regs[insn.i_format.rs] > 0)
154 epc = epc + 4 + (insn.i_format.simmediate << 2);
155 else
156 epc += 8;
157 regs->cp0_epc = epc;
158 break;
159
160 /*
161 * And now the FPA/cp1 branch instructions.
162 */
163 case cop1_op:
164 if (!cpu_has_fpu)
165 fcr31 = current->thread.fpu.soft.fcr31;
166 else
167 asm volatile("cfc1\t%0,$31" : "=r" (fcr31));
168 bit = (insn.i_format.rt >> 2);
169 bit += (bit != 0);
170 bit += 23;
171 switch (insn.i_format.rt) {
172 case 0: /* bc1f */
173 case 2: /* bc1fl */
174 if (~fcr31 & (1 << bit))
175 epc = epc + 4 + (insn.i_format.simmediate << 2);
176 else
177 epc += 8;
178 regs->cp0_epc = epc;
179 break;
180
181 case 1: /* bc1t */
182 case 3: /* bc1tl */
183 if (fcr31 & (1 << bit))
184 epc = epc + 4 + (insn.i_format.simmediate << 2);
185 else
186 epc += 8;
187 regs->cp0_epc = epc;
188 break;
189 }
190 break;
191 }
192
193 return 0;
194
195unaligned:
196 printk("%s: unaligned epc - sending SIGBUS.\n", current->comm);
197 force_sig(SIGBUS, current);
198 return -EFAULT;
199}