MIPS: Make use of the ERETNC instruction on MIPS R6
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>
This commit is contained in:
parent
b0a668fb20
commit
7c151d3d5d
|
@ -75,9 +75,12 @@ do { \
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define __clear_software_ll_bit() \
|
#define __clear_software_ll_bit() \
|
||||||
do { \
|
do { if (cpu_has_rw_llb) { \
|
||||||
if (!__builtin_constant_p(cpu_has_llsc) || !cpu_has_llsc) \
|
write_c0_lladdr(0); \
|
||||||
ll_bit = 0; \
|
} else { \
|
||||||
|
if (!__builtin_constant_p(cpu_has_llsc) || !cpu_has_llsc)\
|
||||||
|
ll_bit = 0; \
|
||||||
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define switch_to(prev, next, last) \
|
#define switch_to(prev, next, last) \
|
||||||
|
|
|
@ -28,7 +28,7 @@ struct thread_info {
|
||||||
unsigned long tp_value; /* thread pointer */
|
unsigned long tp_value; /* thread pointer */
|
||||||
__u32 cpu; /* current CPU */
|
__u32 cpu; /* current CPU */
|
||||||
int preempt_count; /* 0 => preemptable, <0 => BUG */
|
int preempt_count; /* 0 => preemptable, <0 => BUG */
|
||||||
|
int r2_emul_return; /* 1 => Returning from R2 emulator */
|
||||||
mm_segment_t addr_limit; /*
|
mm_segment_t addr_limit; /*
|
||||||
* thread address space limit:
|
* thread address space limit:
|
||||||
* 0x7fffffff for user-thead
|
* 0x7fffffff for user-thead
|
||||||
|
|
|
@ -97,6 +97,7 @@ void output_thread_info_defines(void)
|
||||||
OFFSET(TI_TP_VALUE, thread_info, tp_value);
|
OFFSET(TI_TP_VALUE, thread_info, tp_value);
|
||||||
OFFSET(TI_CPU, thread_info, cpu);
|
OFFSET(TI_CPU, thread_info, cpu);
|
||||||
OFFSET(TI_PRE_COUNT, thread_info, preempt_count);
|
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_ADDR_LIMIT, thread_info, addr_limit);
|
||||||
OFFSET(TI_RESTART_BLOCK, thread_info, restart_block);
|
OFFSET(TI_RESTART_BLOCK, thread_info, restart_block);
|
||||||
OFFSET(TI_REGS, thread_info, regs);
|
OFFSET(TI_REGS, thread_info, regs);
|
||||||
|
|
|
@ -46,6 +46,11 @@ resume_userspace:
|
||||||
local_irq_disable # make sure we dont miss an
|
local_irq_disable # make sure we dont miss an
|
||||||
# interrupt setting need_resched
|
# interrupt setting need_resched
|
||||||
# between sampling and return
|
# 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
|
LONG_L a2, TI_FLAGS($28) # current->work
|
||||||
andi t0, a2, _TIF_WORK_MASK # (ignoring syscall_trace)
|
andi t0, a2, _TIF_WORK_MASK # (ignoring syscall_trace)
|
||||||
bnez t0, work_pending
|
bnez t0, work_pending
|
||||||
|
@ -114,6 +119,19 @@ restore_partial: # restore partial frame
|
||||||
RESTORE_SP_AND_RET
|
RESTORE_SP_AND_RET
|
||||||
.set at
|
.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:
|
work_pending:
|
||||||
andi t0, a2, _TIF_NEED_RESCHED # a2 is preloaded with TI_FLAGS
|
andi t0, a2, _TIF_NEED_RESCHED # a2 is preloaded with TI_FLAGS
|
||||||
beqz t0, work_notifysig
|
beqz t0, work_notifysig
|
||||||
|
|
|
@ -1039,12 +1039,14 @@ asmlinkage void do_ri(struct pt_regs *regs)
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 0:
|
case 0:
|
||||||
case SIGEMT:
|
case SIGEMT:
|
||||||
|
task_thread_info(current)->r2_emul_return = 1;
|
||||||
return;
|
return;
|
||||||
case SIGILL:
|
case SIGILL:
|
||||||
goto no_r2_instr;
|
goto no_r2_instr;
|
||||||
default:
|
default:
|
||||||
process_fpemu_return(status,
|
process_fpemu_return(status,
|
||||||
¤t->thread.cp0_baduaddr);
|
¤t->thread.cp0_baduaddr);
|
||||||
|
task_thread_info(current)->r2_emul_return = 1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue