Skip to content

Commit

Permalink
MIPS: Make use of the ERETNC instruction on MIPS R6
Browse files Browse the repository at this point in the history
The ERETNC instruction, introduced in MIPS R5, is similar to the ERET
one, except it does not clear the LLB bit in the LLADDR register.
This feature is necessary to safely emulate R2 LL/SC instructions.
However, on context switches, we need to clear the LLAddr/LLB bit
in order to make sure that an SC instruction from the new thread
will never succeed if it happens to interrupt an LL operation on the
same address from the previous thread.

Signed-off-by: Markos Chandras <markos.chandras@imgtec.com>
  • Loading branch information
Markos Chandras committed Feb 17, 2015
1 parent b0a668f commit 7c151d3
Show file tree
Hide file tree
Showing 5 changed files with 28 additions and 4 deletions.
9 changes: 6 additions & 3 deletions arch/mips/include/asm/switch_to.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,12 @@ do { \
#endif

#define __clear_software_ll_bit() \
do { \
if (!__builtin_constant_p(cpu_has_llsc) || !cpu_has_llsc) \
ll_bit = 0; \
do { if (cpu_has_rw_llb) { \
write_c0_lladdr(0); \
} else { \
if (!__builtin_constant_p(cpu_has_llsc) || !cpu_has_llsc)\
ll_bit = 0; \
} \
} while (0)

#define switch_to(prev, next, last) \
Expand Down
2 changes: 1 addition & 1 deletion arch/mips/include/asm/thread_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ struct thread_info {
unsigned long tp_value; /* thread pointer */
__u32 cpu; /* current CPU */
int preempt_count; /* 0 => preemptable, <0 => BUG */

int r2_emul_return; /* 1 => Returning from R2 emulator */
mm_segment_t addr_limit; /*
* thread address space limit:
* 0x7fffffff for user-thead
Expand Down
1 change: 1 addition & 0 deletions arch/mips/kernel/asm-offsets.c
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ void output_thread_info_defines(void)
OFFSET(TI_TP_VALUE, thread_info, tp_value);
OFFSET(TI_CPU, thread_info, cpu);
OFFSET(TI_PRE_COUNT, thread_info, preempt_count);
OFFSET(TI_R2_EMUL_RET, thread_info, r2_emul_return);
OFFSET(TI_ADDR_LIMIT, thread_info, addr_limit);
OFFSET(TI_RESTART_BLOCK, thread_info, restart_block);
OFFSET(TI_REGS, thread_info, regs);
Expand Down
18 changes: 18 additions & 0 deletions arch/mips/kernel/entry.S
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ resume_userspace:
local_irq_disable # make sure we dont miss an
# interrupt setting need_resched
# between sampling and return
#ifdef CONFIG_MIPSR2_TO_R6_EMULATOR
lw k0, TI_R2_EMUL_RET($28)
bnez k0, restore_all_from_r2_emul
#endif

LONG_L a2, TI_FLAGS($28) # current->work
andi t0, a2, _TIF_WORK_MASK # (ignoring syscall_trace)
bnez t0, work_pending
Expand Down Expand Up @@ -114,6 +119,19 @@ restore_partial: # restore partial frame
RESTORE_SP_AND_RET
.set at

#ifdef CONFIG_MIPSR2_TO_R6_EMULATOR
restore_all_from_r2_emul: # restore full frame
.set noat
sw zero, TI_R2_EMUL_RET($28) # reset it
RESTORE_TEMP
RESTORE_AT
RESTORE_STATIC
RESTORE_SOME
LONG_L sp, PT_R29(sp)
eretnc
.set at
#endif

work_pending:
andi t0, a2, _TIF_NEED_RESCHED # a2 is preloaded with TI_FLAGS
beqz t0, work_notifysig
Expand Down
2 changes: 2 additions & 0 deletions arch/mips/kernel/traps.c
Original file line number Diff line number Diff line change
Expand Up @@ -1039,12 +1039,14 @@ asmlinkage void do_ri(struct pt_regs *regs)
switch (status) {
case 0:
case SIGEMT:
task_thread_info(current)->r2_emul_return = 1;
return;
case SIGILL:
goto no_r2_instr;
default:
process_fpemu_return(status,
&current->thread.cp0_baduaddr);
task_thread_info(current)->r2_emul_return = 1;
return;
}
}
Expand Down

0 comments on commit 7c151d3

Please sign in to comment.