summaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorMarkos Chandras <markos.chandras@imgtec.com>2014-12-03 07:37:32 -0500
committerMarkos Chandras <markos.chandras@imgtec.com>2015-02-17 10:37:37 -0500
commit7c151d3d5d7a032e08dbe86ad6088622391bf13e (patch)
tree362d5ffcf8fea7c26373750efb106ca1e41c275f /arch
parentb0a668fb2038d846a466c7a16a358d874002b697 (diff)
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>
Diffstat (limited to 'arch')
-rw-r--r--arch/mips/include/asm/switch_to.h9
-rw-r--r--arch/mips/include/asm/thread_info.h2
-rw-r--r--arch/mips/kernel/asm-offsets.c1
-rw-r--r--arch/mips/kernel/entry.S18
-rw-r--r--arch/mips/kernel/traps.c2
5 files changed, 28 insertions, 4 deletions
diff --git a/arch/mips/include/asm/switch_to.h b/arch/mips/include/asm/switch_to.h
index b928b6f898cd..e92d6c4b5ed1 100644
--- a/arch/mips/include/asm/switch_to.h
+++ b/arch/mips/include/asm/switch_to.h
@@ -75,9 +75,12 @@ do { \
75#endif 75#endif
76 76
77#define __clear_software_ll_bit() \ 77#define __clear_software_ll_bit() \
78do { \ 78do { if (cpu_has_rw_llb) { \
79 if (!__builtin_constant_p(cpu_has_llsc) || !cpu_has_llsc) \ 79 write_c0_lladdr(0); \
80 ll_bit = 0; \ 80 } else { \
81 if (!__builtin_constant_p(cpu_has_llsc) || !cpu_has_llsc)\
82 ll_bit = 0; \
83 } \
81} while (0) 84} while (0)
82 85
83#define switch_to(prev, next, last) \ 86#define switch_to(prev, next, last) \
diff --git a/arch/mips/include/asm/thread_info.h b/arch/mips/include/asm/thread_info.h
index 99eea59604e9..fb68fd2714fb 100644
--- a/arch/mips/include/asm/thread_info.h
+++ b/arch/mips/include/asm/thread_info.h
@@ -28,7 +28,7 @@ struct thread_info {
28 unsigned long tp_value; /* thread pointer */ 28 unsigned long tp_value; /* thread pointer */
29 __u32 cpu; /* current CPU */ 29 __u32 cpu; /* current CPU */
30 int preempt_count; /* 0 => preemptable, <0 => BUG */ 30 int preempt_count; /* 0 => preemptable, <0 => BUG */
31 31 int r2_emul_return; /* 1 => Returning from R2 emulator */
32 mm_segment_t addr_limit; /* 32 mm_segment_t addr_limit; /*
33 * thread address space limit: 33 * thread address space limit:
34 * 0x7fffffff for user-thead 34 * 0x7fffffff for user-thead
diff --git a/arch/mips/kernel/asm-offsets.c b/arch/mips/kernel/asm-offsets.c
index b1d84bd4efb3..7b6c11aa1cae 100644
--- a/arch/mips/kernel/asm-offsets.c
+++ b/arch/mips/kernel/asm-offsets.c
@@ -97,6 +97,7 @@ void output_thread_info_defines(void)
97 OFFSET(TI_TP_VALUE, thread_info, tp_value); 97 OFFSET(TI_TP_VALUE, thread_info, tp_value);
98 OFFSET(TI_CPU, thread_info, cpu); 98 OFFSET(TI_CPU, thread_info, cpu);
99 OFFSET(TI_PRE_COUNT, thread_info, preempt_count); 99 OFFSET(TI_PRE_COUNT, thread_info, preempt_count);
100 OFFSET(TI_R2_EMUL_RET, thread_info, r2_emul_return);
100 OFFSET(TI_ADDR_LIMIT, thread_info, addr_limit); 101 OFFSET(TI_ADDR_LIMIT, thread_info, addr_limit);
101 OFFSET(TI_RESTART_BLOCK, thread_info, restart_block); 102 OFFSET(TI_RESTART_BLOCK, thread_info, restart_block);
102 OFFSET(TI_REGS, thread_info, regs); 103 OFFSET(TI_REGS, thread_info, regs);
diff --git a/arch/mips/kernel/entry.S b/arch/mips/kernel/entry.S
index d5ab21c3fd12..af41ba6db960 100644
--- a/arch/mips/kernel/entry.S
+++ b/arch/mips/kernel/entry.S
@@ -46,6 +46,11 @@ resume_userspace:
46 local_irq_disable # make sure we dont miss an 46 local_irq_disable # make sure we dont miss an
47 # interrupt setting need_resched 47 # interrupt setting need_resched
48 # between sampling and return 48 # between sampling and return
49#ifdef CONFIG_MIPSR2_TO_R6_EMULATOR
50 lw k0, TI_R2_EMUL_RET($28)
51 bnez k0, restore_all_from_r2_emul
52#endif
53
49 LONG_L a2, TI_FLAGS($28) # current->work 54 LONG_L a2, TI_FLAGS($28) # current->work
50 andi t0, a2, _TIF_WORK_MASK # (ignoring syscall_trace) 55 andi t0, a2, _TIF_WORK_MASK # (ignoring syscall_trace)
51 bnez t0, work_pending 56 bnez t0, work_pending
@@ -114,6 +119,19 @@ restore_partial: # restore partial frame
114 RESTORE_SP_AND_RET 119 RESTORE_SP_AND_RET
115 .set at 120 .set at
116 121
122#ifdef CONFIG_MIPSR2_TO_R6_EMULATOR
123restore_all_from_r2_emul: # restore full frame
124 .set noat
125 sw zero, TI_R2_EMUL_RET($28) # reset it
126 RESTORE_TEMP
127 RESTORE_AT
128 RESTORE_STATIC
129 RESTORE_SOME
130 LONG_L sp, PT_R29(sp)
131 eretnc
132 .set at
133#endif
134
117work_pending: 135work_pending:
118 andi t0, a2, _TIF_NEED_RESCHED # a2 is preloaded with TI_FLAGS 136 andi t0, a2, _TIF_NEED_RESCHED # a2 is preloaded with TI_FLAGS
119 beqz t0, work_notifysig 137 beqz t0, work_notifysig
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c
index fc157322f609..afa447e5e97f 100644
--- a/arch/mips/kernel/traps.c
+++ b/arch/mips/kernel/traps.c
@@ -1039,12 +1039,14 @@ asmlinkage void do_ri(struct pt_regs *regs)
1039 switch (status) { 1039 switch (status) {
1040 case 0: 1040 case 0:
1041 case SIGEMT: 1041 case SIGEMT:
1042 task_thread_info(current)->r2_emul_return = 1;
1042 return; 1043 return;
1043 case SIGILL: 1044 case SIGILL:
1044 goto no_r2_instr; 1045 goto no_r2_instr;
1045 default: 1046 default:
1046 process_fpemu_return(status, 1047 process_fpemu_return(status,
1047 &current->thread.cp0_baduaddr); 1048 &current->thread.cp0_baduaddr);
1049 task_thread_info(current)->r2_emul_return = 1;
1048 return; 1050 return;
1049 } 1051 }
1050 } 1052 }