mirror of
https://github.com/FEX-Emu/linux.git
synced 2025-01-10 19:43:29 +00:00
e11124d8ff
The r2_emul_return field in struct thread_info was used in order to take an alternate codepath when returning to userland, which (besides not implementing certain features) effectively used the eretnc instruction in place of eret. The difference is that eretnc doesn't clear LLBit, and therefore doesn't cause a linked load & store sequence to fail due to emulation like eret would. The reason eret would usually be used to clear LLBit is so that after context switching we ensure that a load performed by one task doesn't influence another task. However commit 7c151d3d5d7a ("MIPS: Make use of the ERETNC instruction on MIPS R6") which introduced the r2_emul_return field and conditional use of eretnc also for some reason began explicitly clearing LLBit during context switches - despite retaining the use of eret for everything but returns from the pre-r6 instruction emulation code. As LLBit is cleared upon context switches anyway, simplify this by using eretnc unconditionally for MIPSr6 kernels. This allows us to remove the 4 byte r2_emul_return boolean from struct thread_info, simplify the return to user code in entry.S and avoid the overhead of tracking & checking state which we don't need. Signed-off-by: Paul Burton <paul.burton@imgtec.com> Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/14408/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
177 lines
4.1 KiB
ArmAsm
177 lines
4.1 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/compiler.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
|
|
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
|
|
|
|
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 MIPS_ISA_LEVEL_RAW
|
|
jr.hb ra
|
|
nop
|
|
END(mips_ihb)
|
|
|
|
#endif /* CONFIG_CPU_MIPSR2 or CONFIG_CPU_MIPSR6 or CONFIG_MIPS_MT */
|