Commit be5341eb authored by Thomas Gleixner's avatar Thomas Gleixner Committed by Dave Hansen
Browse files

x86/entry: Convert INT 0x80 emulation to IDTENTRY



There is no real reason to have a separate ASM entry point implementation
for the legacy INT 0x80 syscall emulation on 64-bit.

IDTENTRY provides all the functionality needed with the only difference
that it does not:

  - save the syscall number (AX) into pt_regs::orig_ax
  - set pt_regs::ax to -ENOSYS

Both can be done safely in the C code of an IDTENTRY before invoking any of
the syscall related functions which depend on this convention.

Aside of ASM code reduction this prepares for detecting and handling a
local APIC injected vector 0x80.

[ kirill.shutemov: More verbose comments ]
Suggested-by: default avatarLinus Torvalds <torvalds@linuxfoundation.org>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Signed-off-by: default avatarKirill A. Shutemov <kirill.shutemov@linux.intel.com>
Signed-off-by: default avatarDave Hansen <dave.hansen@linux.intel.com>
Reviewed-by: default avatarBorislav Petkov (AMD) <bp@alien8.de>
Cc: <stable@vger.kernel.org> # v6.0+
parent b82a8dbd
Loading
Loading
Loading
Loading
+57 −1
Original line number Diff line number Diff line
@@ -167,7 +167,62 @@ static __always_inline void do_syscall_32_irqs_on(struct pt_regs *regs, int nr)
	}
}

/* Handles int $0x80 */
#ifdef CONFIG_IA32_EMULATION
/**
 * int80_emulation - 32-bit legacy syscall entry
 *
 * This entry point can be used by 32-bit and 64-bit programs to perform
 * 32-bit system calls.  Instances of INT $0x80 can be found inline in
 * various programs and libraries.  It is also used by the vDSO's
 * __kernel_vsyscall fallback for hardware that doesn't support a faster
 * entry method.  Restarted 32-bit system calls also fall back to INT
 * $0x80 regardless of what instruction was originally used to do the
 * system call.
 *
 * This is considered a slow path.  It is not used by most libc
 * implementations on modern hardware except during process startup.
 *
 * The arguments for the INT $0x80 based syscall are on stack in the
 * pt_regs structure:
 *   eax:				system call number
 *   ebx, ecx, edx, esi, edi, ebp:	arg1 - arg 6
 */
DEFINE_IDTENTRY_RAW(int80_emulation)
{
	int nr;

	/* Establish kernel context. */
	enter_from_user_mode(regs);

	instrumentation_begin();
	add_random_kstack_offset();

	/*
	 * The low level idtentry code pushed -1 into regs::orig_ax
	 * and regs::ax contains the syscall number.
	 *
	 * User tracing code (ptrace or signal handlers) might assume
	 * that the regs::orig_ax contains a 32-bit number on invoking
	 * a 32-bit syscall.
	 *
	 * Establish the syscall convention by saving the 32bit truncated
	 * syscall number in regs::orig_ax and by invalidating regs::ax.
	 */
	regs->orig_ax = regs->ax & GENMASK(31, 0);
	regs->ax = -ENOSYS;

	nr = syscall_32_enter(regs);

	local_irq_enable();
	nr = syscall_enter_from_user_mode_work(regs, nr);
	do_syscall_32_irqs_on(regs, nr);

	instrumentation_end();
	syscall_exit_to_user_mode(regs);
}
#else /* CONFIG_IA32_EMULATION */

/* Handles int $0x80 on a 32bit kernel */
__visible noinstr void do_int80_syscall_32(struct pt_regs *regs)
{
	int nr = syscall_32_enter(regs);
@@ -186,6 +241,7 @@ __visible noinstr void do_int80_syscall_32(struct pt_regs *regs)
	instrumentation_end();
	syscall_exit_to_user_mode(regs);
}
#endif /* !CONFIG_IA32_EMULATION */

static noinstr bool __do_fast_syscall_32(struct pt_regs *regs)
{
+0 −77
Original line number Diff line number Diff line
@@ -275,80 +275,3 @@ SYM_INNER_LABEL(entry_SYSRETL_compat_end, SYM_L_GLOBAL)
	ANNOTATE_NOENDBR
	int3
SYM_CODE_END(entry_SYSCALL_compat)

/*
 * 32-bit legacy system call entry.
 *
 * 32-bit x86 Linux system calls traditionally used the INT $0x80
 * instruction.  INT $0x80 lands here.
 *
 * This entry point can be used by 32-bit and 64-bit programs to perform
 * 32-bit system calls.  Instances of INT $0x80 can be found inline in
 * various programs and libraries.  It is also used by the vDSO's
 * __kernel_vsyscall fallback for hardware that doesn't support a faster
 * entry method.  Restarted 32-bit system calls also fall back to INT
 * $0x80 regardless of what instruction was originally used to do the
 * system call.
 *
 * This is considered a slow path.  It is not used by most libc
 * implementations on modern hardware except during process startup.
 *
 * Arguments:
 * eax  system call number
 * ebx  arg1
 * ecx  arg2
 * edx  arg3
 * esi  arg4
 * edi  arg5
 * ebp  arg6
 */
SYM_CODE_START(entry_INT80_compat)
	UNWIND_HINT_ENTRY
	ENDBR
	/*
	 * Interrupts are off on entry.
	 */
	ASM_CLAC			/* Do this early to minimize exposure */
	ALTERNATIVE "swapgs", "", X86_FEATURE_XENPV

	/*
	 * User tracing code (ptrace or signal handlers) might assume that
	 * the saved RAX contains a 32-bit number when we're invoking a 32-bit
	 * syscall.  Just in case the high bits are nonzero, zero-extend
	 * the syscall number.  (This could almost certainly be deleted
	 * with no ill effects.)
	 */
	movl	%eax, %eax

	/* switch to thread stack expects orig_ax and rdi to be pushed */
	pushq	%rax			/* pt_regs->orig_ax */

	/* Need to switch before accessing the thread stack. */
	SWITCH_TO_KERNEL_CR3 scratch_reg=%rax

	/* In the Xen PV case we already run on the thread stack. */
	ALTERNATIVE "", "jmp .Lint80_keep_stack", X86_FEATURE_XENPV

	movq	%rsp, %rax
	movq	PER_CPU_VAR(pcpu_hot + X86_top_of_stack), %rsp

	pushq	5*8(%rax)		/* regs->ss */
	pushq	4*8(%rax)		/* regs->rsp */
	pushq	3*8(%rax)		/* regs->eflags */
	pushq	2*8(%rax)		/* regs->cs */
	pushq	1*8(%rax)		/* regs->ip */
	pushq	0*8(%rax)		/* regs->orig_ax */
.Lint80_keep_stack:

	PUSH_AND_CLEAR_REGS rax=$-ENOSYS
	UNWIND_HINT_REGS

	cld

	IBRS_ENTER
	UNTRAIN_RET

	movq	%rsp, %rdi
	call	do_int80_syscall_32
	jmp	swapgs_restore_regs_and_return_to_usermode
SYM_CODE_END(entry_INT80_compat)
+4 −0
Original line number Diff line number Diff line
@@ -569,6 +569,10 @@ DECLARE_IDTENTRY_RAW(X86_TRAP_UD, exc_invalid_op);
DECLARE_IDTENTRY_RAW(X86_TRAP_BP,		exc_int3);
DECLARE_IDTENTRY_RAW_ERRORCODE(X86_TRAP_PF,	exc_page_fault);

#if defined(CONFIG_IA32_EMULATION)
DECLARE_IDTENTRY_RAW(IA32_SYSCALL_VECTOR,	int80_emulation);
#endif

#ifdef CONFIG_X86_MCE
#ifdef CONFIG_X86_64
DECLARE_IDTENTRY_MCE(X86_TRAP_MC,	exc_machine_check);
+0 −4
Original line number Diff line number Diff line
@@ -32,10 +32,6 @@ void entry_SYSCALL_compat(void);
void entry_SYSCALL_compat_safe_stack(void);
void entry_SYSRETL_compat_unsafe_stack(void);
void entry_SYSRETL_compat_end(void);
void entry_INT80_compat(void);
#ifdef CONFIG_XEN_PV
void xen_entry_INT80_compat(void);
#endif
#else /* !CONFIG_IA32_EMULATION */
#define entry_SYSCALL_compat NULL
#define entry_SYSENTER_compat NULL
+1 −1
Original line number Diff line number Diff line
@@ -121,7 +121,7 @@ static const __initconst struct idt_data def_idts[] = {

static const struct idt_data ia32_idt[] __initconst = {
#if defined(CONFIG_IA32_EMULATION)
	SYSG(IA32_SYSCALL_VECTOR,	entry_INT80_compat),
	SYSG(IA32_SYSCALL_VECTOR,	asm_int80_emulation),
#elif defined(CONFIG_X86_32)
	SYSG(IA32_SYSCALL_VECTOR,	entry_INT80_32),
#endif
Loading