Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/arch/mips/kernel/branch.c
10817 views
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/fpu.h>
16
#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
*/
23
int __compute_return_epc(struct pt_regs *regs)
24
{
25
unsigned int __user *addr;
26
unsigned int bit, fcr31, dspcontrol;
27
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
*/
37
addr = (unsigned int __user *) epc;
38
if (__get_user(insn.word, addr)) {
39
force_sig(SIGSEGV, current);
40
return -EFAULT;
41
}
42
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
case bposge32_op:
103
if (!cpu_has_dsp)
104
goto sigill;
105
106
dspcontrol = rddsp(0x01);
107
108
if (dspcontrol >= 32) {
109
epc = epc + 4 + (insn.i_format.simmediate << 2);
110
} else
111
epc += 8;
112
regs->cp0_epc = epc;
113
break;
114
}
115
break;
116
117
/*
118
* These are unconditional and in j_format.
119
*/
120
case jal_op:
121
regs->regs[31] = regs->cp0_epc + 8;
122
case j_op:
123
epc += 4;
124
epc >>= 28;
125
epc <<= 28;
126
epc |= (insn.j_format.target << 2);
127
regs->cp0_epc = epc;
128
break;
129
130
/*
131
* These are conditional and in i_format.
132
*/
133
case beq_op:
134
case beql_op:
135
if (regs->regs[insn.i_format.rs] ==
136
regs->regs[insn.i_format.rt])
137
epc = epc + 4 + (insn.i_format.simmediate << 2);
138
else
139
epc += 8;
140
regs->cp0_epc = epc;
141
break;
142
143
case bne_op:
144
case bnel_op:
145
if (regs->regs[insn.i_format.rs] !=
146
regs->regs[insn.i_format.rt])
147
epc = epc + 4 + (insn.i_format.simmediate << 2);
148
else
149
epc += 8;
150
regs->cp0_epc = epc;
151
break;
152
153
case blez_op: /* not really i_format */
154
case blezl_op:
155
/* rt field assumed to be zero */
156
if ((long)regs->regs[insn.i_format.rs] <= 0)
157
epc = epc + 4 + (insn.i_format.simmediate << 2);
158
else
159
epc += 8;
160
regs->cp0_epc = epc;
161
break;
162
163
case bgtz_op:
164
case bgtzl_op:
165
/* rt field assumed to be zero */
166
if ((long)regs->regs[insn.i_format.rs] > 0)
167
epc = epc + 4 + (insn.i_format.simmediate << 2);
168
else
169
epc += 8;
170
regs->cp0_epc = epc;
171
break;
172
173
/*
174
* And now the FPA/cp1 branch instructions.
175
*/
176
case cop1_op:
177
preempt_disable();
178
if (is_fpu_owner())
179
asm volatile("cfc1\t%0,$31" : "=r" (fcr31));
180
else
181
fcr31 = current->thread.fpu.fcr31;
182
preempt_enable();
183
184
bit = (insn.i_format.rt >> 2);
185
bit += (bit != 0);
186
bit += 23;
187
switch (insn.i_format.rt & 3) {
188
case 0: /* bc1f */
189
case 2: /* bc1fl */
190
if (~fcr31 & (1 << bit))
191
epc = epc + 4 + (insn.i_format.simmediate << 2);
192
else
193
epc += 8;
194
regs->cp0_epc = epc;
195
break;
196
197
case 1: /* bc1t */
198
case 3: /* bc1tl */
199
if (fcr31 & (1 << bit))
200
epc = epc + 4 + (insn.i_format.simmediate << 2);
201
else
202
epc += 8;
203
regs->cp0_epc = epc;
204
break;
205
}
206
break;
207
#ifdef CONFIG_CPU_CAVIUM_OCTEON
208
case lwc2_op: /* This is bbit0 on Octeon */
209
if ((regs->regs[insn.i_format.rs] & (1ull<<insn.i_format.rt))
210
== 0)
211
epc = epc + 4 + (insn.i_format.simmediate << 2);
212
else
213
epc += 8;
214
regs->cp0_epc = epc;
215
break;
216
case ldc2_op: /* This is bbit032 on Octeon */
217
if ((regs->regs[insn.i_format.rs] &
218
(1ull<<(insn.i_format.rt+32))) == 0)
219
epc = epc + 4 + (insn.i_format.simmediate << 2);
220
else
221
epc += 8;
222
regs->cp0_epc = epc;
223
break;
224
case swc2_op: /* This is bbit1 on Octeon */
225
if (regs->regs[insn.i_format.rs] & (1ull<<insn.i_format.rt))
226
epc = epc + 4 + (insn.i_format.simmediate << 2);
227
else
228
epc += 8;
229
regs->cp0_epc = epc;
230
break;
231
case sdc2_op: /* This is bbit132 on Octeon */
232
if (regs->regs[insn.i_format.rs] &
233
(1ull<<(insn.i_format.rt+32)))
234
epc = epc + 4 + (insn.i_format.simmediate << 2);
235
else
236
epc += 8;
237
regs->cp0_epc = epc;
238
break;
239
#endif
240
}
241
242
return 0;
243
244
unaligned:
245
printk("%s: unaligned epc - sending SIGBUS.\n", current->comm);
246
force_sig(SIGBUS, current);
247
return -EFAULT;
248
249
sigill:
250
printk("%s: DSP branch but not DSP ASE - sending SIGBUS.\n", current->comm);
251
force_sig(SIGBUS, current);
252
return -EFAULT;
253
}
254
255