diff options
Diffstat (limited to 'arch/mips/kernel/branch.c')
-rw-r--r-- | arch/mips/kernel/branch.c | 199 |
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 | */ | ||
22 | int __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 | |||
195 | unaligned: | ||
196 | printk("%s: unaligned epc - sending SIGBUS.\n", current->comm); | ||
197 | force_sig(SIGBUS, current); | ||
198 | return -EFAULT; | ||
199 | } | ||