MIPS: Make use of the ERETNC instruction on MIPS R6
authorMarkos Chandras <markos.chandras@imgtec.com>
Wed, 3 Dec 2014 12:37:32 +0000 (12:37 +0000)
committerMarkos Chandras <markos.chandras@imgtec.com>
Tue, 17 Feb 2015 15:37:37 +0000 (15:37 +0000)
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>
arch/mips/include/asm/switch_to.h
arch/mips/include/asm/thread_info.h
arch/mips/kernel/asm-offsets.c
arch/mips/kernel/entry.S
arch/mips/kernel/traps.c

index b928b6f898cd5266efe89465d2dc87089f8f357c..e92d6c4b5ed192305b0b1f1605481f745cfadb10 100644 (file)
@@ -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)                                    \
index 99eea59604e984b61907a5a6a82feeca9660a609..fb68fd2714fb7e41b3caf1ee3ee1e8aa6a8850e4 100644 (file)
@@ -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
index b1d84bd4efb3b4c500af92c01438aba5a89048dc..7b6c11aa1cae972ae9430ddf2f74adb934c041fd 100644 (file)
@@ -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);
index d5ab21c3fd123cf2b43b0adc737c4fe0d4d9e24f..af41ba6db9601d16540b945b0112e130bd9eb845 100644 (file)
@@ -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
@@ -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
index fc157322f609099b9aee73c1c9ef6371070bc695..afa447e5e97f3f8b7740c4025e848607a4e20f8c 100644 (file)
@@ -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;
                        }
                }