// SPDX-License-Identifier: GPL-2.0-only /* * common.c - C code for kernel entry and exit * Copyright (c) 2015 Andrew Lutomirski * * Based on asm and ptrace code by many authors. The code here originated * in ptrace.c and signal.c. */ #include <linux/kernel.h> #include <linux/sched.h> #include <linux/sched/task_stack.h> #include <linux/entry-common.h> #include <linux/mm.h> #include <linux/smp.h> #include <linux/errno.h> #include <linux/ptrace.h> #include <linux/export.h> #include <linux/nospec.h> #include <linux/syscalls.h> #include <linux/uaccess.h> #include <linux/init.h> #ifdef CONFIG_XEN_PV #include <xen/xen-ops.h> #include <xen/events.h> #endif #include <asm/apic.h> #include <asm/desc.h> #include <asm/traps.h> #include <asm/vdso.h> #include <asm/cpufeature.h> #include <asm/fpu/api.h> #include <asm/nospec-branch.h> #include <asm/io_bitmap.h> #include <asm/syscall.h> #include <asm/irq_stack.h> #ifdef CONFIG_X86_64 static __always_inline bool do_syscall_x64(struct pt_regs *regs, int nr) { … } static __always_inline bool do_syscall_x32(struct pt_regs *regs, int nr) { … } /* Returns true to return using SYSRET, or false to use IRET */ __visible noinstr bool do_syscall_64(struct pt_regs *regs, int nr) { … } #endif #if defined(CONFIG_X86_32) || defined(CONFIG_IA32_EMULATION) static __always_inline int syscall_32_enter(struct pt_regs *regs) { … } #ifdef CONFIG_IA32_EMULATION bool __ia32_enabled __ro_after_init = !IS_ENABLED(…); static int ia32_emulation_override_cmdline(char *arg) { … } early_param(…); #endif /* * Invoke a 32-bit syscall. Called with IRQs on in CT_STATE_KERNEL. */ static __always_inline void do_syscall_32_irqs_on(struct pt_regs *regs, int nr) { … } #ifdef CONFIG_IA32_EMULATION static __always_inline bool int80_is_external(void) { … } /** * do_int80_emulation - 32-bit legacy syscall C entry from asm * * 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 */ __visible noinstr void do_int80_emulation(struct pt_regs *regs) { … } #ifdef CONFIG_X86_FRED /* * A FRED-specific INT80 handler is warranted for the follwing reasons: * * 1) As INT instructions and hardware interrupts are separate event * types, FRED does not preclude the use of vector 0x80 for external * interrupts. As a result, the FRED setup code does not reserve * vector 0x80 and calling int80_is_external() is not merely * suboptimal but actively incorrect: it could cause a system call * to be incorrectly ignored. * * 2) It is called only for handling vector 0x80 of event type * EVENT_TYPE_SWINT and will never be called to handle any external * interrupt (event type EVENT_TYPE_EXTINT). * * 3) FRED has separate entry flows depending on if the event came from * user space or kernel space, and because the kernel does not use * INT insns, the FRED kernel entry handler fred_entry_from_kernel() * falls through to fred_bad_type() if the event type is * EVENT_TYPE_SWINT, i.e., INT insns. So if the kernel is handling * an INT insn, it can only be from a user level. * * 4) int80_emulation() does a CLEAR_BRANCH_HISTORY. While FRED will * likely take a different approach if it is ever needed: it * probably belongs in either fred_intx()/ fred_other() or * asm_fred_entrypoint_user(), depending on if this ought to be done * for all entries from userspace or only system * calls. * * 5) INT $0x80 is the fast path for 32-bit system calls under FRED. */ DEFINE_FREDENTRY_RAW(int80_emulation) { … } #endif #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); add_random_kstack_offset(); /* * Subtlety here: if ptrace pokes something larger than 2^31-1 into * orig_ax, the int return value truncates it. This matches * the semantics of syscall_get_nr(). */ nr = syscall_enter_from_user_mode(regs, nr); instrumentation_begin(); do_syscall_32_irqs_on(regs, nr); instrumentation_end(); syscall_exit_to_user_mode(regs); } #endif /* !CONFIG_IA32_EMULATION */ static noinstr bool __do_fast_syscall_32(struct pt_regs *regs) { … } /* Returns true to return using SYSEXIT/SYSRETL, or false to use IRET */ __visible noinstr bool do_fast_syscall_32(struct pt_regs *regs) { … } /* Returns true to return using SYSEXIT/SYSRETL, or false to use IRET */ __visible noinstr bool do_SYSENTER_32(struct pt_regs *regs) { … } #endif SYSCALL_DEFINE0(…) { … #ifdef CONFIG_XEN_PV #ifndef CONFIG_PREEMPTION /* * Some hypercalls issued by the toolstack can take many 10s of * seconds. Allow tasks running hypercalls via the privcmd driver to * be voluntarily preempted even if full kernel preemption is * disabled. * * Such preemptible hypercalls are bracketed by * xen_preemptible_hcall_begin() and xen_preemptible_hcall_end() * calls. */ DEFINE_PER_CPU(bool, xen_in_preemptible_hcall); EXPORT_SYMBOL_GPL(xen_in_preemptible_hcall); /* * In case of scheduling the flag must be cleared and restored after * returning from schedule as the task might move to a different CPU. */ static __always_inline bool get_and_clear_inhcall(void) { bool inhcall = __this_cpu_read(xen_in_preemptible_hcall); __this_cpu_write(xen_in_preemptible_hcall, false); return inhcall; } static __always_inline void restore_inhcall(bool inhcall) { __this_cpu_write(xen_in_preemptible_hcall, inhcall); } #else static __always_inline bool get_and_clear_inhcall(void) { … } static __always_inline void restore_inhcall(bool inhcall) { … } #endif static void __xen_pv_evtchn_do_upcall(struct pt_regs *regs) { … } __visible noinstr void xen_pv_evtchn_do_upcall(struct pt_regs *regs) { … } #endif /* CONFIG_XEN_PV */