diff options
Diffstat (limited to 'arch/mips/kernel/process.c')
| -rw-r--r-- | arch/mips/kernel/process.c | 102 |
1 files changed, 61 insertions, 41 deletions
diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c index 5351e1f3950d..c5ff6bfe2825 100644 --- a/arch/mips/kernel/process.c +++ b/arch/mips/kernel/process.c | |||
| @@ -208,13 +208,13 @@ static inline int is_ra_save_ins(union mips_instruction *ip, int *poff) | |||
| 208 | * | 208 | * |
| 209 | * microMIPS is way more fun... | 209 | * microMIPS is way more fun... |
| 210 | */ | 210 | */ |
| 211 | if (mm_insn_16bit(ip->halfword[1])) { | 211 | if (mm_insn_16bit(ip->word >> 16)) { |
| 212 | switch (ip->mm16_r5_format.opcode) { | 212 | switch (ip->mm16_r5_format.opcode) { |
| 213 | case mm_swsp16_op: | 213 | case mm_swsp16_op: |
| 214 | if (ip->mm16_r5_format.rt != 31) | 214 | if (ip->mm16_r5_format.rt != 31) |
| 215 | return 0; | 215 | return 0; |
| 216 | 216 | ||
| 217 | *poff = ip->mm16_r5_format.simmediate; | 217 | *poff = ip->mm16_r5_format.imm; |
| 218 | *poff = (*poff << 2) / sizeof(ulong); | 218 | *poff = (*poff << 2) / sizeof(ulong); |
| 219 | return 1; | 219 | return 1; |
| 220 | 220 | ||
| @@ -287,7 +287,7 @@ static inline int is_jump_ins(union mips_instruction *ip) | |||
| 287 | * | 287 | * |
| 288 | * microMIPS is kind of more fun... | 288 | * microMIPS is kind of more fun... |
| 289 | */ | 289 | */ |
| 290 | if (mm_insn_16bit(ip->halfword[1])) { | 290 | if (mm_insn_16bit(ip->word >> 16)) { |
| 291 | if ((ip->mm16_r5_format.opcode == mm_pool16c_op && | 291 | if ((ip->mm16_r5_format.opcode == mm_pool16c_op && |
| 292 | (ip->mm16_r5_format.rt & mm_jr16_op) == mm_jr16_op)) | 292 | (ip->mm16_r5_format.rt & mm_jr16_op) == mm_jr16_op)) |
| 293 | return 1; | 293 | return 1; |
| @@ -313,9 +313,11 @@ static inline int is_jump_ins(union mips_instruction *ip) | |||
| 313 | #endif | 313 | #endif |
| 314 | } | 314 | } |
| 315 | 315 | ||
| 316 | static inline int is_sp_move_ins(union mips_instruction *ip) | 316 | static inline int is_sp_move_ins(union mips_instruction *ip, int *frame_size) |
| 317 | { | 317 | { |
| 318 | #ifdef CONFIG_CPU_MICROMIPS | 318 | #ifdef CONFIG_CPU_MICROMIPS |
| 319 | unsigned short tmp; | ||
| 320 | |||
| 319 | /* | 321 | /* |
| 320 | * addiusp -imm | 322 | * addiusp -imm |
| 321 | * addius5 sp,-imm | 323 | * addius5 sp,-imm |
| @@ -324,21 +326,40 @@ static inline int is_sp_move_ins(union mips_instruction *ip) | |||
| 324 | * | 326 | * |
| 325 | * microMIPS is not more fun... | 327 | * microMIPS is not more fun... |
| 326 | */ | 328 | */ |
| 327 | if (mm_insn_16bit(ip->halfword[1])) { | 329 | if (mm_insn_16bit(ip->word >> 16)) { |
| 328 | return (ip->mm16_r3_format.opcode == mm_pool16d_op && | 330 | if (ip->mm16_r3_format.opcode == mm_pool16d_op && |
| 329 | ip->mm16_r3_format.simmediate && mm_addiusp_func) || | 331 | ip->mm16_r3_format.simmediate & mm_addiusp_func) { |
| 330 | (ip->mm16_r5_format.opcode == mm_pool16d_op && | 332 | tmp = ip->mm_b0_format.simmediate >> 1; |
| 331 | ip->mm16_r5_format.rt == 29); | 333 | tmp = ((tmp & 0x1ff) ^ 0x100) - 0x100; |
| 334 | if ((tmp + 2) < 4) /* 0x0,0x1,0x1fe,0x1ff are special */ | ||
| 335 | tmp ^= 0x100; | ||
| 336 | *frame_size = -(signed short)(tmp << 2); | ||
| 337 | return 1; | ||
| 338 | } | ||
| 339 | if (ip->mm16_r5_format.opcode == mm_pool16d_op && | ||
| 340 | ip->mm16_r5_format.rt == 29) { | ||
| 341 | tmp = ip->mm16_r5_format.imm >> 1; | ||
| 342 | *frame_size = -(signed short)(tmp & 0xf); | ||
| 343 | return 1; | ||
| 344 | } | ||
| 345 | return 0; | ||
| 332 | } | 346 | } |
| 333 | 347 | ||
| 334 | return ip->mm_i_format.opcode == mm_addiu32_op && | 348 | if (ip->mm_i_format.opcode == mm_addiu32_op && |
| 335 | ip->mm_i_format.rt == 29 && ip->mm_i_format.rs == 29; | 349 | ip->mm_i_format.rt == 29 && ip->mm_i_format.rs == 29) { |
| 350 | *frame_size = -ip->i_format.simmediate; | ||
| 351 | return 1; | ||
| 352 | } | ||
| 336 | #else | 353 | #else |
| 337 | /* addiu/daddiu sp,sp,-imm */ | 354 | /* addiu/daddiu sp,sp,-imm */ |
| 338 | if (ip->i_format.rs != 29 || ip->i_format.rt != 29) | 355 | if (ip->i_format.rs != 29 || ip->i_format.rt != 29) |
| 339 | return 0; | 356 | return 0; |
| 340 | if (ip->i_format.opcode == addiu_op || ip->i_format.opcode == daddiu_op) | 357 | |
| 358 | if (ip->i_format.opcode == addiu_op || | ||
| 359 | ip->i_format.opcode == daddiu_op) { | ||
| 360 | *frame_size = -ip->i_format.simmediate; | ||
| 341 | return 1; | 361 | return 1; |
| 362 | } | ||
| 342 | #endif | 363 | #endif |
| 343 | return 0; | 364 | return 0; |
| 344 | } | 365 | } |
| @@ -348,7 +369,9 @@ static int get_frame_info(struct mips_frame_info *info) | |||
| 348 | bool is_mmips = IS_ENABLED(CONFIG_CPU_MICROMIPS); | 369 | bool is_mmips = IS_ENABLED(CONFIG_CPU_MICROMIPS); |
| 349 | union mips_instruction insn, *ip, *ip_end; | 370 | union mips_instruction insn, *ip, *ip_end; |
| 350 | const unsigned int max_insns = 128; | 371 | const unsigned int max_insns = 128; |
| 372 | unsigned int last_insn_size = 0; | ||
| 351 | unsigned int i; | 373 | unsigned int i; |
| 374 | bool saw_jump = false; | ||
| 352 | 375 | ||
| 353 | info->pc_offset = -1; | 376 | info->pc_offset = -1; |
| 354 | info->frame_size = 0; | 377 | info->frame_size = 0; |
| @@ -359,47 +382,44 @@ static int get_frame_info(struct mips_frame_info *info) | |||
| 359 | 382 | ||
| 360 | ip_end = (void *)ip + info->func_size; | 383 | ip_end = (void *)ip + info->func_size; |
| 361 | 384 | ||
| 362 | for (i = 0; i < max_insns && ip < ip_end; i++, ip++) { | 385 | for (i = 0; i < max_insns && ip < ip_end; i++) { |
| 386 | ip = (void *)ip + last_insn_size; | ||
| 363 | if (is_mmips && mm_insn_16bit(ip->halfword[0])) { | 387 | if (is_mmips && mm_insn_16bit(ip->halfword[0])) { |
| 364 | insn.halfword[0] = 0; | 388 | insn.word = ip->halfword[0] << 16; |
| 365 | insn.halfword[1] = ip->halfword[0]; | 389 | last_insn_size = 2; |
| 366 | } else if (is_mmips) { | 390 | } else if (is_mmips) { |
| 367 | insn.halfword[0] = ip->halfword[1]; | 391 | insn.word = ip->halfword[0] << 16 | ip->halfword[1]; |
| 368 | insn.halfword[1] = ip->halfword[0]; | 392 | last_insn_size = 4; |
| 369 | } else { | 393 | } else { |
| 370 | insn.word = ip->word; | 394 | insn.word = ip->word; |
| 395 | last_insn_size = 4; | ||
| 371 | } | 396 | } |
| 372 | 397 | ||
| 373 | if (is_jump_ins(&insn)) | ||
| 374 | break; | ||
| 375 | |||
| 376 | if (!info->frame_size) { | 398 | if (!info->frame_size) { |
| 377 | if (is_sp_move_ins(&insn)) | 399 | is_sp_move_ins(&insn, &info->frame_size); |
| 378 | { | 400 | continue; |
| 379 | #ifdef CONFIG_CPU_MICROMIPS | 401 | } else if (!saw_jump && is_jump_ins(ip)) { |
| 380 | if (mm_insn_16bit(ip->halfword[0])) | 402 | /* |
| 381 | { | 403 | * If we see a jump instruction, we are finished |
| 382 | unsigned short tmp; | 404 | * with the frame save. |
| 383 | 405 | * | |
| 384 | if (ip->halfword[0] & mm_addiusp_func) | 406 | * Some functions can have a shortcut return at |
| 385 | { | 407 | * the beginning of the function, so don't start |
| 386 | tmp = (((ip->halfword[0] >> 1) & 0x1ff) << 2); | 408 | * looking for jump instruction until we see the |
| 387 | info->frame_size = -(signed short)(tmp | ((tmp & 0x100) ? 0xfe00 : 0)); | 409 | * frame setup. |
| 388 | } else { | 410 | * |
| 389 | tmp = (ip->halfword[0] >> 1); | 411 | * The RA save instruction can get put into the |
| 390 | info->frame_size = -(signed short)(tmp & 0xf); | 412 | * delay slot of the jump instruction, so look |
| 391 | } | 413 | * at the next instruction, too. |
| 392 | ip = (void *) &ip->halfword[1]; | 414 | */ |
| 393 | ip--; | 415 | saw_jump = true; |
| 394 | } else | ||
| 395 | #endif | ||
| 396 | info->frame_size = - ip->i_format.simmediate; | ||
| 397 | } | ||
| 398 | continue; | 416 | continue; |
| 399 | } | 417 | } |
| 400 | if (info->pc_offset == -1 && | 418 | if (info->pc_offset == -1 && |
| 401 | is_ra_save_ins(&insn, &info->pc_offset)) | 419 | is_ra_save_ins(&insn, &info->pc_offset)) |
| 402 | break; | 420 | break; |
| 421 | if (saw_jump) | ||
| 422 | break; | ||
| 403 | } | 423 | } |
| 404 | if (info->frame_size && info->pc_offset >= 0) /* nested */ | 424 | if (info->frame_size && info->pc_offset >= 0) /* nested */ |
| 405 | return 0; | 425 | return 0; |
