aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'arch/mips/kernel/process.c')
-rw-r--r--arch/mips/kernel/process.c166
1 files changed, 112 insertions, 54 deletions
diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c
index 44a6f25e902e..8c26ecac930d 100644
--- a/arch/mips/kernel/process.c
+++ b/arch/mips/kernel/process.c
@@ -32,6 +32,7 @@
32#include <asm/cpu.h> 32#include <asm/cpu.h>
33#include <asm/dsp.h> 33#include <asm/dsp.h>
34#include <asm/fpu.h> 34#include <asm/fpu.h>
35#include <asm/irq.h>
35#include <asm/msa.h> 36#include <asm/msa.h>
36#include <asm/pgtable.h> 37#include <asm/pgtable.h>
37#include <asm/mipsregs.h> 38#include <asm/mipsregs.h>
@@ -191,11 +192,9 @@ struct mips_frame_info {
191#define J_TARGET(pc,target) \ 192#define J_TARGET(pc,target) \
192 (((unsigned long)(pc) & 0xf0000000) | ((target) << 2)) 193 (((unsigned long)(pc) & 0xf0000000) | ((target) << 2))
193 194
194static inline int is_ra_save_ins(union mips_instruction *ip) 195static inline int is_ra_save_ins(union mips_instruction *ip, int *poff)
195{ 196{
196#ifdef CONFIG_CPU_MICROMIPS 197#ifdef CONFIG_CPU_MICROMIPS
197 union mips_instruction mmi;
198
199 /* 198 /*
200 * swsp ra,offset 199 * swsp ra,offset
201 * swm16 reglist,offset(sp) 200 * swm16 reglist,offset(sp)
@@ -205,29 +204,71 @@ static inline int is_ra_save_ins(union mips_instruction *ip)
205 * 204 *
206 * microMIPS is way more fun... 205 * microMIPS is way more fun...
207 */ 206 */
208 if (mm_insn_16bit(ip->halfword[0])) { 207 if (mm_insn_16bit(ip->halfword[1])) {
209 mmi.word = (ip->halfword[0] << 16); 208 switch (ip->mm16_r5_format.opcode) {
210 return (mmi.mm16_r5_format.opcode == mm_swsp16_op && 209 case mm_swsp16_op:
211 mmi.mm16_r5_format.rt == 31) || 210 if (ip->mm16_r5_format.rt != 31)
212 (mmi.mm16_m_format.opcode == mm_pool16c_op && 211 return 0;
213 mmi.mm16_m_format.func == mm_swm16_op); 212
213 *poff = ip->mm16_r5_format.simmediate;
214 *poff = (*poff << 2) / sizeof(ulong);
215 return 1;
216
217 case mm_pool16c_op:
218 switch (ip->mm16_m_format.func) {
219 case mm_swm16_op:
220 *poff = ip->mm16_m_format.imm;
221 *poff += 1 + ip->mm16_m_format.rlist;
222 *poff = (*poff << 2) / sizeof(ulong);
223 return 1;
224
225 default:
226 return 0;
227 }
228
229 default:
230 return 0;
231 }
214 } 232 }
215 else { 233
216 mmi.halfword[0] = ip->halfword[1]; 234 switch (ip->i_format.opcode) {
217 mmi.halfword[1] = ip->halfword[0]; 235 case mm_sw32_op:
218 return (mmi.mm_m_format.opcode == mm_pool32b_op && 236 if (ip->i_format.rs != 29)
219 mmi.mm_m_format.rd > 9 && 237 return 0;
220 mmi.mm_m_format.base == 29 && 238 if (ip->i_format.rt != 31)
221 mmi.mm_m_format.func == mm_swm32_func) || 239 return 0;
222 (mmi.i_format.opcode == mm_sw32_op && 240
223 mmi.i_format.rs == 29 && 241 *poff = ip->i_format.simmediate / sizeof(ulong);
224 mmi.i_format.rt == 31); 242 return 1;
243
244 case mm_pool32b_op:
245 switch (ip->mm_m_format.func) {
246 case mm_swm32_func:
247 if (ip->mm_m_format.rd < 0x10)
248 return 0;
249 if (ip->mm_m_format.base != 29)
250 return 0;
251
252 *poff = ip->mm_m_format.simmediate;
253 *poff += (ip->mm_m_format.rd & 0xf) * sizeof(u32);
254 *poff /= sizeof(ulong);
255 return 1;
256 default:
257 return 0;
258 }
259
260 default:
261 return 0;
225 } 262 }
226#else 263#else
227 /* sw / sd $ra, offset($sp) */ 264 /* sw / sd $ra, offset($sp) */
228 return (ip->i_format.opcode == sw_op || ip->i_format.opcode == sd_op) && 265 if ((ip->i_format.opcode == sw_op || ip->i_format.opcode == sd_op) &&
229 ip->i_format.rs == 29 && 266 ip->i_format.rs == 29 && ip->i_format.rt == 31) {
230 ip->i_format.rt == 31; 267 *poff = ip->i_format.simmediate / sizeof(ulong);
268 return 1;
269 }
270
271 return 0;
231#endif 272#endif
232} 273}
233 274
@@ -242,13 +283,16 @@ static inline int is_jump_ins(union mips_instruction *ip)
242 * 283 *
243 * microMIPS is kind of more fun... 284 * microMIPS is kind of more fun...
244 */ 285 */
245 union mips_instruction mmi; 286 if (mm_insn_16bit(ip->halfword[1])) {
246 287 if ((ip->mm16_r5_format.opcode == mm_pool16c_op &&
247 mmi.word = (ip->halfword[0] << 16); 288 (ip->mm16_r5_format.rt & mm_jr16_op) == mm_jr16_op))
289 return 1;
290 return 0;
291 }
248 292
249 if ((mmi.mm16_r5_format.opcode == mm_pool16c_op && 293 if (ip->j_format.opcode == mm_j32_op)
250 (mmi.mm16_r5_format.rt & mm_jr16_op) == mm_jr16_op) || 294 return 1;
251 ip->j_format.opcode == mm_jal32_op) 295 if (ip->j_format.opcode == mm_jal32_op)
252 return 1; 296 return 1;
253 if (ip->r_format.opcode != mm_pool32a_op || 297 if (ip->r_format.opcode != mm_pool32a_op ||
254 ip->r_format.func != mm_pool32axf_op) 298 ip->r_format.func != mm_pool32axf_op)
@@ -276,15 +320,13 @@ static inline int is_sp_move_ins(union mips_instruction *ip)
276 * 320 *
277 * microMIPS is not more fun... 321 * microMIPS is not more fun...
278 */ 322 */
279 if (mm_insn_16bit(ip->halfword[0])) { 323 if (mm_insn_16bit(ip->halfword[1])) {
280 union mips_instruction mmi; 324 return (ip->mm16_r3_format.opcode == mm_pool16d_op &&
281 325 ip->mm16_r3_format.simmediate && mm_addiusp_func) ||
282 mmi.word = (ip->halfword[0] << 16); 326 (ip->mm16_r5_format.opcode == mm_pool16d_op &&
283 return (mmi.mm16_r3_format.opcode == mm_pool16d_op && 327 ip->mm16_r5_format.rt == 29);
284 mmi.mm16_r3_format.simmediate && mm_addiusp_func) ||
285 (mmi.mm16_r5_format.opcode == mm_pool16d_op &&
286 mmi.mm16_r5_format.rt == 29);
287 } 328 }
329
288 return ip->mm_i_format.opcode == mm_addiu32_op && 330 return ip->mm_i_format.opcode == mm_addiu32_op &&
289 ip->mm_i_format.rt == 29 && ip->mm_i_format.rs == 29; 331 ip->mm_i_format.rt == 29 && ip->mm_i_format.rs == 29;
290#else 332#else
@@ -299,30 +341,36 @@ static inline int is_sp_move_ins(union mips_instruction *ip)
299 341
300static int get_frame_info(struct mips_frame_info *info) 342static int get_frame_info(struct mips_frame_info *info)
301{ 343{
302#ifdef CONFIG_CPU_MICROMIPS 344 bool is_mmips = IS_ENABLED(CONFIG_CPU_MICROMIPS);
303 union mips_instruction *ip = (void *) (((char *) info->func) - 1); 345 union mips_instruction insn, *ip, *ip_end;
304#else 346 const unsigned int max_insns = 128;
305 union mips_instruction *ip = info->func; 347 unsigned int i;
306#endif
307 unsigned max_insns = info->func_size / sizeof(union mips_instruction);
308 unsigned i;
309 348
310 info->pc_offset = -1; 349 info->pc_offset = -1;
311 info->frame_size = 0; 350 info->frame_size = 0;
312 351
352 ip = (void *)msk_isa16_mode((ulong)info->func);
313 if (!ip) 353 if (!ip)
314 goto err; 354 goto err;
315 355
316 if (max_insns == 0) 356 ip_end = (void *)ip + info->func_size;
317 max_insns = 128U; /* unknown function size */
318 max_insns = min(128U, max_insns);
319 357
320 for (i = 0; i < max_insns; i++, ip++) { 358 for (i = 0; i < max_insns && ip < ip_end; i++, ip++) {
359 if (is_mmips && mm_insn_16bit(ip->halfword[0])) {
360 insn.halfword[0] = 0;
361 insn.halfword[1] = ip->halfword[0];
362 } else if (is_mmips) {
363 insn.halfword[0] = ip->halfword[1];
364 insn.halfword[1] = ip->halfword[0];
365 } else {
366 insn.word = ip->word;
367 }
321 368
322 if (is_jump_ins(ip)) 369 if (is_jump_ins(&insn))
323 break; 370 break;
371
324 if (!info->frame_size) { 372 if (!info->frame_size) {
325 if (is_sp_move_ins(ip)) 373 if (is_sp_move_ins(&insn))
326 { 374 {
327#ifdef CONFIG_CPU_MICROMIPS 375#ifdef CONFIG_CPU_MICROMIPS
328 if (mm_insn_16bit(ip->halfword[0])) 376 if (mm_insn_16bit(ip->halfword[0]))
@@ -345,11 +393,9 @@ static int get_frame_info(struct mips_frame_info *info)
345 } 393 }
346 continue; 394 continue;
347 } 395 }
348 if (info->pc_offset == -1 && is_ra_save_ins(ip)) { 396 if (info->pc_offset == -1 &&
349 info->pc_offset = 397 is_ra_save_ins(&insn, &info->pc_offset))
350 ip->i_format.simmediate / sizeof(long);
351 break; 398 break;
352 }
353 } 399 }
354 if (info->frame_size && info->pc_offset >= 0) /* nested */ 400 if (info->frame_size && info->pc_offset >= 0) /* nested */
355 return 0; 401 return 0;
@@ -507,7 +553,19 @@ EXPORT_SYMBOL(unwind_stack_by_address);
507unsigned long unwind_stack(struct task_struct *task, unsigned long *sp, 553unsigned long unwind_stack(struct task_struct *task, unsigned long *sp,
508 unsigned long pc, unsigned long *ra) 554 unsigned long pc, unsigned long *ra)
509{ 555{
510 unsigned long stack_page = (unsigned long)task_stack_page(task); 556 unsigned long stack_page = 0;
557 int cpu;
558
559 for_each_possible_cpu(cpu) {
560 if (on_irq_stack(cpu, *sp)) {
561 stack_page = (unsigned long)irq_stack[cpu];
562 break;
563 }
564 }
565
566 if (!stack_page)
567 stack_page = (unsigned long)task_stack_page(task);
568
511 return unwind_stack_by_address(stack_page, sp, pc, ra); 569 return unwind_stack_by_address(stack_page, sp, pc, ra);
512} 570}
513#endif 571#endif