mirror of
https://github.com/torvalds/linux.git
synced 2024-12-29 06:12:08 +00:00
7c151d3d5d
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>
194 lines
4.4 KiB
ArmAsm
194 lines
4.4 KiB
ArmAsm
/*
|
|
* This file is subject to the terms and conditions of the GNU General Public
|
|
* License. See the file "COPYING" in the main directory of this archive
|
|
* for more details.
|
|
*
|
|
* Copyright (C) 1994 - 2000, 2001, 2003 Ralf Baechle
|
|
* Copyright (C) 1999, 2000 Silicon Graphics, Inc.
|
|
* Copyright (C) 2001 MIPS Technologies, Inc.
|
|
*/
|
|
|
|
#include <asm/asm.h>
|
|
#include <asm/asmmacro.h>
|
|
#include <asm/regdef.h>
|
|
#include <asm/mipsregs.h>
|
|
#include <asm/stackframe.h>
|
|
#include <asm/isadep.h>
|
|
#include <asm/thread_info.h>
|
|
#include <asm/war.h>
|
|
|
|
#ifndef CONFIG_PREEMPT
|
|
#define resume_kernel restore_all
|
|
#else
|
|
#define __ret_from_irq ret_from_exception
|
|
#endif
|
|
|
|
.text
|
|
.align 5
|
|
#ifndef CONFIG_PREEMPT
|
|
FEXPORT(ret_from_exception)
|
|
local_irq_disable # preempt stop
|
|
b __ret_from_irq
|
|
#endif
|
|
FEXPORT(ret_from_irq)
|
|
LONG_S s0, TI_REGS($28)
|
|
FEXPORT(__ret_from_irq)
|
|
/*
|
|
* We can be coming here from a syscall done in the kernel space,
|
|
* e.g. a failed kernel_execve().
|
|
*/
|
|
resume_userspace_check:
|
|
LONG_L t0, PT_STATUS(sp) # returning to kernel mode?
|
|
andi t0, t0, KU_USER
|
|
beqz t0, resume_kernel
|
|
|
|
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
|
|
j restore_all
|
|
|
|
#ifdef CONFIG_PREEMPT
|
|
resume_kernel:
|
|
local_irq_disable
|
|
lw t0, TI_PRE_COUNT($28)
|
|
bnez t0, restore_all
|
|
need_resched:
|
|
LONG_L t0, TI_FLAGS($28)
|
|
andi t1, t0, _TIF_NEED_RESCHED
|
|
beqz t1, restore_all
|
|
LONG_L t0, PT_STATUS(sp) # Interrupts off?
|
|
andi t0, 1
|
|
beqz t0, restore_all
|
|
jal preempt_schedule_irq
|
|
b need_resched
|
|
#endif
|
|
|
|
FEXPORT(ret_from_kernel_thread)
|
|
jal schedule_tail # a0 = struct task_struct *prev
|
|
move a0, s1
|
|
jal s0
|
|
j syscall_exit
|
|
|
|
FEXPORT(ret_from_fork)
|
|
jal schedule_tail # a0 = struct task_struct *prev
|
|
|
|
FEXPORT(syscall_exit)
|
|
local_irq_disable # make sure need_resched and
|
|
# signals dont change between
|
|
# sampling and return
|
|
LONG_L a2, TI_FLAGS($28) # current->work
|
|
li t0, _TIF_ALLWORK_MASK
|
|
and t0, a2, t0
|
|
bnez t0, syscall_exit_work
|
|
|
|
restore_all: # restore full frame
|
|
.set noat
|
|
RESTORE_TEMP
|
|
RESTORE_AT
|
|
RESTORE_STATIC
|
|
restore_partial: # restore partial frame
|
|
#ifdef CONFIG_TRACE_IRQFLAGS
|
|
SAVE_STATIC
|
|
SAVE_AT
|
|
SAVE_TEMP
|
|
LONG_L v0, PT_STATUS(sp)
|
|
#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX)
|
|
and v0, ST0_IEP
|
|
#else
|
|
and v0, ST0_IE
|
|
#endif
|
|
beqz v0, 1f
|
|
jal trace_hardirqs_on
|
|
b 2f
|
|
1: jal trace_hardirqs_off
|
|
2:
|
|
RESTORE_TEMP
|
|
RESTORE_AT
|
|
RESTORE_STATIC
|
|
#endif
|
|
RESTORE_SOME
|
|
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
|
|
work_resched:
|
|
jal schedule
|
|
|
|
local_irq_disable # make sure need_resched and
|
|
# signals dont change between
|
|
# sampling and return
|
|
LONG_L a2, TI_FLAGS($28)
|
|
andi t0, a2, _TIF_WORK_MASK # is there any work to be done
|
|
# other than syscall tracing?
|
|
beqz t0, restore_all
|
|
andi t0, a2, _TIF_NEED_RESCHED
|
|
bnez t0, work_resched
|
|
|
|
work_notifysig: # deal with pending signals and
|
|
# notify-resume requests
|
|
move a0, sp
|
|
li a1, 0
|
|
jal do_notify_resume # a2 already loaded
|
|
j resume_userspace_check
|
|
|
|
FEXPORT(syscall_exit_partial)
|
|
local_irq_disable # make sure need_resched doesn't
|
|
# change between and return
|
|
LONG_L a2, TI_FLAGS($28) # current->work
|
|
li t0, _TIF_ALLWORK_MASK
|
|
and t0, a2
|
|
beqz t0, restore_partial
|
|
SAVE_STATIC
|
|
syscall_exit_work:
|
|
LONG_L t0, PT_STATUS(sp) # returning to kernel mode?
|
|
andi t0, t0, KU_USER
|
|
beqz t0, resume_kernel
|
|
li t0, _TIF_WORK_SYSCALL_EXIT
|
|
and t0, a2 # a2 is preloaded with TI_FLAGS
|
|
beqz t0, work_pending # trace bit set?
|
|
local_irq_enable # could let syscall_trace_leave()
|
|
# call schedule() instead
|
|
move a0, sp
|
|
jal syscall_trace_leave
|
|
b resume_userspace
|
|
|
|
#if defined(CONFIG_CPU_MIPSR2) || defined(CONFIG_CPU_MIPSR6) || \
|
|
defined(CONFIG_MIPS_MT)
|
|
|
|
/*
|
|
* MIPS32R2 Instruction Hazard Barrier - must be called
|
|
*
|
|
* For C code use the inline version named instruction_hazard().
|
|
*/
|
|
LEAF(mips_ihb)
|
|
.set mips32r2
|
|
jr.hb ra
|
|
nop
|
|
END(mips_ihb)
|
|
|
|
#endif /* CONFIG_CPU_MIPSR2 or CONFIG_CPU_MIPSR6 or CONFIG_MIPS_MT */
|