162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#include <linux/context_tracking.h>
462306a36Sopenharmony_ci#include <linux/entry-common.h>
562306a36Sopenharmony_ci#include <linux/resume_user_mode.h>
662306a36Sopenharmony_ci#include <linux/highmem.h>
762306a36Sopenharmony_ci#include <linux/jump_label.h>
862306a36Sopenharmony_ci#include <linux/kmsan.h>
962306a36Sopenharmony_ci#include <linux/livepatch.h>
1062306a36Sopenharmony_ci#include <linux/audit.h>
1162306a36Sopenharmony_ci#include <linux/tick.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include "common.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define CREATE_TRACE_POINTS
1662306a36Sopenharmony_ci#include <trace/events/syscalls.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci/* See comment for enter_from_user_mode() in entry-common.h */
1962306a36Sopenharmony_cistatic __always_inline void __enter_from_user_mode(struct pt_regs *regs)
2062306a36Sopenharmony_ci{
2162306a36Sopenharmony_ci	arch_enter_from_user_mode(regs);
2262306a36Sopenharmony_ci	lockdep_hardirqs_off(CALLER_ADDR0);
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	CT_WARN_ON(__ct_state() != CONTEXT_USER);
2562306a36Sopenharmony_ci	user_exit_irqoff();
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	instrumentation_begin();
2862306a36Sopenharmony_ci	kmsan_unpoison_entry_regs(regs);
2962306a36Sopenharmony_ci	trace_hardirqs_off_finish();
3062306a36Sopenharmony_ci	instrumentation_end();
3162306a36Sopenharmony_ci}
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_civoid noinstr enter_from_user_mode(struct pt_regs *regs)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	__enter_from_user_mode(regs);
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic inline void syscall_enter_audit(struct pt_regs *regs, long syscall)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	if (unlikely(audit_context())) {
4162306a36Sopenharmony_ci		unsigned long args[6];
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci		syscall_get_arguments(current, regs, args);
4462306a36Sopenharmony_ci		audit_syscall_entry(syscall, args[0], args[1], args[2], args[3]);
4562306a36Sopenharmony_ci	}
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic long syscall_trace_enter(struct pt_regs *regs, long syscall,
4962306a36Sopenharmony_ci				unsigned long work)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	long ret = 0;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	/*
5462306a36Sopenharmony_ci	 * Handle Syscall User Dispatch.  This must comes first, since
5562306a36Sopenharmony_ci	 * the ABI here can be something that doesn't make sense for
5662306a36Sopenharmony_ci	 * other syscall_work features.
5762306a36Sopenharmony_ci	 */
5862306a36Sopenharmony_ci	if (work & SYSCALL_WORK_SYSCALL_USER_DISPATCH) {
5962306a36Sopenharmony_ci		if (syscall_user_dispatch(regs))
6062306a36Sopenharmony_ci			return -1L;
6162306a36Sopenharmony_ci	}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	/* Handle ptrace */
6462306a36Sopenharmony_ci	if (work & (SYSCALL_WORK_SYSCALL_TRACE | SYSCALL_WORK_SYSCALL_EMU)) {
6562306a36Sopenharmony_ci		ret = ptrace_report_syscall_entry(regs);
6662306a36Sopenharmony_ci		if (ret || (work & SYSCALL_WORK_SYSCALL_EMU))
6762306a36Sopenharmony_ci			return -1L;
6862306a36Sopenharmony_ci	}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	/* Do seccomp after ptrace, to catch any tracer changes. */
7162306a36Sopenharmony_ci	if (work & SYSCALL_WORK_SECCOMP) {
7262306a36Sopenharmony_ci		ret = __secure_computing(NULL);
7362306a36Sopenharmony_ci		if (ret == -1L)
7462306a36Sopenharmony_ci			return ret;
7562306a36Sopenharmony_ci	}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	/* Either of the above might have changed the syscall number */
7862306a36Sopenharmony_ci	syscall = syscall_get_nr(current, regs);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	if (unlikely(work & SYSCALL_WORK_SYSCALL_TRACEPOINT))
8162306a36Sopenharmony_ci		trace_sys_enter(regs, syscall);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	syscall_enter_audit(regs, syscall);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	return ret ? : syscall;
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic __always_inline long
8962306a36Sopenharmony_ci__syscall_enter_from_user_work(struct pt_regs *regs, long syscall)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	unsigned long work = READ_ONCE(current_thread_info()->syscall_work);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	if (work & SYSCALL_WORK_ENTER)
9462306a36Sopenharmony_ci		syscall = syscall_trace_enter(regs, syscall, work);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	return syscall;
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cilong syscall_enter_from_user_mode_work(struct pt_regs *regs, long syscall)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	return __syscall_enter_from_user_work(regs, syscall);
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cinoinstr long syscall_enter_from_user_mode(struct pt_regs *regs, long syscall)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	long ret;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	__enter_from_user_mode(regs);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	instrumentation_begin();
11162306a36Sopenharmony_ci	local_irq_enable();
11262306a36Sopenharmony_ci	ret = __syscall_enter_from_user_work(regs, syscall);
11362306a36Sopenharmony_ci	instrumentation_end();
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	return ret;
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cinoinstr void syscall_enter_from_user_mode_prepare(struct pt_regs *regs)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	__enter_from_user_mode(regs);
12162306a36Sopenharmony_ci	instrumentation_begin();
12262306a36Sopenharmony_ci	local_irq_enable();
12362306a36Sopenharmony_ci	instrumentation_end();
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci/* See comment for exit_to_user_mode() in entry-common.h */
12762306a36Sopenharmony_cistatic __always_inline void __exit_to_user_mode(void)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	instrumentation_begin();
13062306a36Sopenharmony_ci	trace_hardirqs_on_prepare();
13162306a36Sopenharmony_ci	lockdep_hardirqs_on_prepare();
13262306a36Sopenharmony_ci	instrumentation_end();
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	user_enter_irqoff();
13562306a36Sopenharmony_ci	arch_exit_to_user_mode();
13662306a36Sopenharmony_ci	lockdep_hardirqs_on(CALLER_ADDR0);
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_civoid noinstr exit_to_user_mode(void)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	__exit_to_user_mode();
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci/* Workaround to allow gradual conversion of architecture code */
14562306a36Sopenharmony_civoid __weak arch_do_signal_or_restart(struct pt_regs *regs) { }
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic unsigned long exit_to_user_mode_loop(struct pt_regs *regs,
14862306a36Sopenharmony_ci					    unsigned long ti_work)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	/*
15162306a36Sopenharmony_ci	 * Before returning to user space ensure that all pending work
15262306a36Sopenharmony_ci	 * items have been completed.
15362306a36Sopenharmony_ci	 */
15462306a36Sopenharmony_ci	while (ti_work & EXIT_TO_USER_MODE_WORK) {
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci		local_irq_enable_exit_to_user(ti_work);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci		if (ti_work & _TIF_NEED_RESCHED)
15962306a36Sopenharmony_ci			schedule();
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci		if (ti_work & _TIF_UPROBE)
16262306a36Sopenharmony_ci			uprobe_notify_resume(regs);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci		if (ti_work & _TIF_PATCH_PENDING)
16562306a36Sopenharmony_ci			klp_update_patch_state(current);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci		if (ti_work & (_TIF_SIGPENDING | _TIF_NOTIFY_SIGNAL))
16862306a36Sopenharmony_ci			arch_do_signal_or_restart(regs);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci		if (ti_work & _TIF_NOTIFY_RESUME)
17162306a36Sopenharmony_ci			resume_user_mode_work(regs);
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci		/* Architecture specific TIF work */
17462306a36Sopenharmony_ci		arch_exit_to_user_mode_work(regs, ti_work);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci		/*
17762306a36Sopenharmony_ci		 * Disable interrupts and reevaluate the work flags as they
17862306a36Sopenharmony_ci		 * might have changed while interrupts and preemption was
17962306a36Sopenharmony_ci		 * enabled above.
18062306a36Sopenharmony_ci		 */
18162306a36Sopenharmony_ci		local_irq_disable_exit_to_user();
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci		/* Check if any of the above work has queued a deferred wakeup */
18462306a36Sopenharmony_ci		tick_nohz_user_enter_prepare();
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci		ti_work = read_thread_flags();
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	/* Return the latest work state for arch_exit_to_user_mode() */
19062306a36Sopenharmony_ci	return ti_work;
19162306a36Sopenharmony_ci}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_cistatic void exit_to_user_mode_prepare(struct pt_regs *regs)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	unsigned long ti_work;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	lockdep_assert_irqs_disabled();
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	/* Flush pending rcuog wakeup before the last need_resched() check */
20062306a36Sopenharmony_ci	tick_nohz_user_enter_prepare();
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	ti_work = read_thread_flags();
20362306a36Sopenharmony_ci	if (unlikely(ti_work & EXIT_TO_USER_MODE_WORK))
20462306a36Sopenharmony_ci		ti_work = exit_to_user_mode_loop(regs, ti_work);
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	arch_exit_to_user_mode_prepare(regs, ti_work);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	/* Ensure that kernel state is sane for a return to userspace */
20962306a36Sopenharmony_ci	kmap_assert_nomap();
21062306a36Sopenharmony_ci	lockdep_assert_irqs_disabled();
21162306a36Sopenharmony_ci	lockdep_sys_exit();
21262306a36Sopenharmony_ci}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci/*
21562306a36Sopenharmony_ci * If SYSCALL_EMU is set, then the only reason to report is when
21662306a36Sopenharmony_ci * SINGLESTEP is set (i.e. PTRACE_SYSEMU_SINGLESTEP).  This syscall
21762306a36Sopenharmony_ci * instruction has been already reported in syscall_enter_from_user_mode().
21862306a36Sopenharmony_ci */
21962306a36Sopenharmony_cistatic inline bool report_single_step(unsigned long work)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	if (work & SYSCALL_WORK_SYSCALL_EMU)
22262306a36Sopenharmony_ci		return false;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	return work & SYSCALL_WORK_SYSCALL_EXIT_TRAP;
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic void syscall_exit_work(struct pt_regs *regs, unsigned long work)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	bool step;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	/*
23262306a36Sopenharmony_ci	 * If the syscall was rolled back due to syscall user dispatching,
23362306a36Sopenharmony_ci	 * then the tracers below are not invoked for the same reason as
23462306a36Sopenharmony_ci	 * the entry side was not invoked in syscall_trace_enter(): The ABI
23562306a36Sopenharmony_ci	 * of these syscalls is unknown.
23662306a36Sopenharmony_ci	 */
23762306a36Sopenharmony_ci	if (work & SYSCALL_WORK_SYSCALL_USER_DISPATCH) {
23862306a36Sopenharmony_ci		if (unlikely(current->syscall_dispatch.on_dispatch)) {
23962306a36Sopenharmony_ci			current->syscall_dispatch.on_dispatch = false;
24062306a36Sopenharmony_ci			return;
24162306a36Sopenharmony_ci		}
24262306a36Sopenharmony_ci	}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	audit_syscall_exit(regs);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	if (work & SYSCALL_WORK_SYSCALL_TRACEPOINT)
24762306a36Sopenharmony_ci		trace_sys_exit(regs, syscall_get_return_value(current, regs));
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	step = report_single_step(work);
25062306a36Sopenharmony_ci	if (step || work & SYSCALL_WORK_SYSCALL_TRACE)
25162306a36Sopenharmony_ci		ptrace_report_syscall_exit(regs, step);
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci/*
25562306a36Sopenharmony_ci * Syscall specific exit to user mode preparation. Runs with interrupts
25662306a36Sopenharmony_ci * enabled.
25762306a36Sopenharmony_ci */
25862306a36Sopenharmony_cistatic void syscall_exit_to_user_mode_prepare(struct pt_regs *regs)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	unsigned long work = READ_ONCE(current_thread_info()->syscall_work);
26162306a36Sopenharmony_ci	unsigned long nr = syscall_get_nr(current, regs);
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	CT_WARN_ON(ct_state() != CONTEXT_KERNEL);
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_PROVE_LOCKING)) {
26662306a36Sopenharmony_ci		if (WARN(irqs_disabled(), "syscall %lu left IRQs disabled", nr))
26762306a36Sopenharmony_ci			local_irq_enable();
26862306a36Sopenharmony_ci	}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	rseq_syscall(regs);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	/*
27362306a36Sopenharmony_ci	 * Do one-time syscall specific work. If these work items are
27462306a36Sopenharmony_ci	 * enabled, we want to run them exactly once per syscall exit with
27562306a36Sopenharmony_ci	 * interrupts enabled.
27662306a36Sopenharmony_ci	 */
27762306a36Sopenharmony_ci	if (unlikely(work & SYSCALL_WORK_EXIT))
27862306a36Sopenharmony_ci		syscall_exit_work(regs, work);
27962306a36Sopenharmony_ci}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_cistatic __always_inline void __syscall_exit_to_user_mode_work(struct pt_regs *regs)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci	syscall_exit_to_user_mode_prepare(regs);
28462306a36Sopenharmony_ci	local_irq_disable_exit_to_user();
28562306a36Sopenharmony_ci	exit_to_user_mode_prepare(regs);
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_civoid syscall_exit_to_user_mode_work(struct pt_regs *regs)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	__syscall_exit_to_user_mode_work(regs);
29162306a36Sopenharmony_ci}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci__visible noinstr void syscall_exit_to_user_mode(struct pt_regs *regs)
29462306a36Sopenharmony_ci{
29562306a36Sopenharmony_ci	instrumentation_begin();
29662306a36Sopenharmony_ci	__syscall_exit_to_user_mode_work(regs);
29762306a36Sopenharmony_ci	instrumentation_end();
29862306a36Sopenharmony_ci	__exit_to_user_mode();
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_cinoinstr void irqentry_enter_from_user_mode(struct pt_regs *regs)
30262306a36Sopenharmony_ci{
30362306a36Sopenharmony_ci	__enter_from_user_mode(regs);
30462306a36Sopenharmony_ci}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_cinoinstr void irqentry_exit_to_user_mode(struct pt_regs *regs)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	instrumentation_begin();
30962306a36Sopenharmony_ci	exit_to_user_mode_prepare(regs);
31062306a36Sopenharmony_ci	instrumentation_end();
31162306a36Sopenharmony_ci	__exit_to_user_mode();
31262306a36Sopenharmony_ci}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_cinoinstr irqentry_state_t irqentry_enter(struct pt_regs *regs)
31562306a36Sopenharmony_ci{
31662306a36Sopenharmony_ci	irqentry_state_t ret = {
31762306a36Sopenharmony_ci		.exit_rcu = false,
31862306a36Sopenharmony_ci	};
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	if (user_mode(regs)) {
32162306a36Sopenharmony_ci		irqentry_enter_from_user_mode(regs);
32262306a36Sopenharmony_ci		return ret;
32362306a36Sopenharmony_ci	}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	/*
32662306a36Sopenharmony_ci	 * If this entry hit the idle task invoke ct_irq_enter() whether
32762306a36Sopenharmony_ci	 * RCU is watching or not.
32862306a36Sopenharmony_ci	 *
32962306a36Sopenharmony_ci	 * Interrupts can nest when the first interrupt invokes softirq
33062306a36Sopenharmony_ci	 * processing on return which enables interrupts.
33162306a36Sopenharmony_ci	 *
33262306a36Sopenharmony_ci	 * Scheduler ticks in the idle task can mark quiescent state and
33362306a36Sopenharmony_ci	 * terminate a grace period, if and only if the timer interrupt is
33462306a36Sopenharmony_ci	 * not nested into another interrupt.
33562306a36Sopenharmony_ci	 *
33662306a36Sopenharmony_ci	 * Checking for rcu_is_watching() here would prevent the nesting
33762306a36Sopenharmony_ci	 * interrupt to invoke ct_irq_enter(). If that nested interrupt is
33862306a36Sopenharmony_ci	 * the tick then rcu_flavor_sched_clock_irq() would wrongfully
33962306a36Sopenharmony_ci	 * assume that it is the first interrupt and eventually claim
34062306a36Sopenharmony_ci	 * quiescent state and end grace periods prematurely.
34162306a36Sopenharmony_ci	 *
34262306a36Sopenharmony_ci	 * Unconditionally invoke ct_irq_enter() so RCU state stays
34362306a36Sopenharmony_ci	 * consistent.
34462306a36Sopenharmony_ci	 *
34562306a36Sopenharmony_ci	 * TINY_RCU does not support EQS, so let the compiler eliminate
34662306a36Sopenharmony_ci	 * this part when enabled.
34762306a36Sopenharmony_ci	 */
34862306a36Sopenharmony_ci	if (!IS_ENABLED(CONFIG_TINY_RCU) && is_idle_task(current)) {
34962306a36Sopenharmony_ci		/*
35062306a36Sopenharmony_ci		 * If RCU is not watching then the same careful
35162306a36Sopenharmony_ci		 * sequence vs. lockdep and tracing is required
35262306a36Sopenharmony_ci		 * as in irqentry_enter_from_user_mode().
35362306a36Sopenharmony_ci		 */
35462306a36Sopenharmony_ci		lockdep_hardirqs_off(CALLER_ADDR0);
35562306a36Sopenharmony_ci		ct_irq_enter();
35662306a36Sopenharmony_ci		instrumentation_begin();
35762306a36Sopenharmony_ci		kmsan_unpoison_entry_regs(regs);
35862306a36Sopenharmony_ci		trace_hardirqs_off_finish();
35962306a36Sopenharmony_ci		instrumentation_end();
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci		ret.exit_rcu = true;
36262306a36Sopenharmony_ci		return ret;
36362306a36Sopenharmony_ci	}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	/*
36662306a36Sopenharmony_ci	 * If RCU is watching then RCU only wants to check whether it needs
36762306a36Sopenharmony_ci	 * to restart the tick in NOHZ mode. rcu_irq_enter_check_tick()
36862306a36Sopenharmony_ci	 * already contains a warning when RCU is not watching, so no point
36962306a36Sopenharmony_ci	 * in having another one here.
37062306a36Sopenharmony_ci	 */
37162306a36Sopenharmony_ci	lockdep_hardirqs_off(CALLER_ADDR0);
37262306a36Sopenharmony_ci	instrumentation_begin();
37362306a36Sopenharmony_ci	kmsan_unpoison_entry_regs(regs);
37462306a36Sopenharmony_ci	rcu_irq_enter_check_tick();
37562306a36Sopenharmony_ci	trace_hardirqs_off_finish();
37662306a36Sopenharmony_ci	instrumentation_end();
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	return ret;
37962306a36Sopenharmony_ci}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_civoid raw_irqentry_exit_cond_resched(void)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	if (!preempt_count()) {
38462306a36Sopenharmony_ci		/* Sanity check RCU and thread stack */
38562306a36Sopenharmony_ci		rcu_irq_exit_check_preempt();
38662306a36Sopenharmony_ci		if (IS_ENABLED(CONFIG_DEBUG_ENTRY))
38762306a36Sopenharmony_ci			WARN_ON_ONCE(!on_thread_stack());
38862306a36Sopenharmony_ci		if (need_resched())
38962306a36Sopenharmony_ci			preempt_schedule_irq();
39062306a36Sopenharmony_ci	}
39162306a36Sopenharmony_ci}
39262306a36Sopenharmony_ci#ifdef CONFIG_PREEMPT_DYNAMIC
39362306a36Sopenharmony_ci#if defined(CONFIG_HAVE_PREEMPT_DYNAMIC_CALL)
39462306a36Sopenharmony_ciDEFINE_STATIC_CALL(irqentry_exit_cond_resched, raw_irqentry_exit_cond_resched);
39562306a36Sopenharmony_ci#elif defined(CONFIG_HAVE_PREEMPT_DYNAMIC_KEY)
39662306a36Sopenharmony_ciDEFINE_STATIC_KEY_TRUE(sk_dynamic_irqentry_exit_cond_resched);
39762306a36Sopenharmony_civoid dynamic_irqentry_exit_cond_resched(void)
39862306a36Sopenharmony_ci{
39962306a36Sopenharmony_ci	if (!static_branch_unlikely(&sk_dynamic_irqentry_exit_cond_resched))
40062306a36Sopenharmony_ci		return;
40162306a36Sopenharmony_ci	raw_irqentry_exit_cond_resched();
40262306a36Sopenharmony_ci}
40362306a36Sopenharmony_ci#endif
40462306a36Sopenharmony_ci#endif
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_cinoinstr void irqentry_exit(struct pt_regs *regs, irqentry_state_t state)
40762306a36Sopenharmony_ci{
40862306a36Sopenharmony_ci	lockdep_assert_irqs_disabled();
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	/* Check whether this returns to user mode */
41162306a36Sopenharmony_ci	if (user_mode(regs)) {
41262306a36Sopenharmony_ci		irqentry_exit_to_user_mode(regs);
41362306a36Sopenharmony_ci	} else if (!regs_irqs_disabled(regs)) {
41462306a36Sopenharmony_ci		/*
41562306a36Sopenharmony_ci		 * If RCU was not watching on entry this needs to be done
41662306a36Sopenharmony_ci		 * carefully and needs the same ordering of lockdep/tracing
41762306a36Sopenharmony_ci		 * and RCU as the return to user mode path.
41862306a36Sopenharmony_ci		 */
41962306a36Sopenharmony_ci		if (state.exit_rcu) {
42062306a36Sopenharmony_ci			instrumentation_begin();
42162306a36Sopenharmony_ci			/* Tell the tracer that IRET will enable interrupts */
42262306a36Sopenharmony_ci			trace_hardirqs_on_prepare();
42362306a36Sopenharmony_ci			lockdep_hardirqs_on_prepare();
42462306a36Sopenharmony_ci			instrumentation_end();
42562306a36Sopenharmony_ci			ct_irq_exit();
42662306a36Sopenharmony_ci			lockdep_hardirqs_on(CALLER_ADDR0);
42762306a36Sopenharmony_ci			return;
42862306a36Sopenharmony_ci		}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci		instrumentation_begin();
43162306a36Sopenharmony_ci		if (IS_ENABLED(CONFIG_PREEMPTION))
43262306a36Sopenharmony_ci			irqentry_exit_cond_resched();
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci		/* Covers both tracing and lockdep */
43562306a36Sopenharmony_ci		trace_hardirqs_on();
43662306a36Sopenharmony_ci		instrumentation_end();
43762306a36Sopenharmony_ci	} else {
43862306a36Sopenharmony_ci		/*
43962306a36Sopenharmony_ci		 * IRQ flags state is correct already. Just tell RCU if it
44062306a36Sopenharmony_ci		 * was not watching on entry.
44162306a36Sopenharmony_ci		 */
44262306a36Sopenharmony_ci		if (state.exit_rcu)
44362306a36Sopenharmony_ci			ct_irq_exit();
44462306a36Sopenharmony_ci	}
44562306a36Sopenharmony_ci}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ciirqentry_state_t noinstr irqentry_nmi_enter(struct pt_regs *regs)
44862306a36Sopenharmony_ci{
44962306a36Sopenharmony_ci	irqentry_state_t irq_state;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	irq_state.lockdep = lockdep_hardirqs_enabled();
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	__nmi_enter();
45462306a36Sopenharmony_ci	lockdep_hardirqs_off(CALLER_ADDR0);
45562306a36Sopenharmony_ci	lockdep_hardirq_enter();
45662306a36Sopenharmony_ci	ct_nmi_enter();
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	instrumentation_begin();
45962306a36Sopenharmony_ci	kmsan_unpoison_entry_regs(regs);
46062306a36Sopenharmony_ci	trace_hardirqs_off_finish();
46162306a36Sopenharmony_ci	ftrace_nmi_enter();
46262306a36Sopenharmony_ci	instrumentation_end();
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	return irq_state;
46562306a36Sopenharmony_ci}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_civoid noinstr irqentry_nmi_exit(struct pt_regs *regs, irqentry_state_t irq_state)
46862306a36Sopenharmony_ci{
46962306a36Sopenharmony_ci	instrumentation_begin();
47062306a36Sopenharmony_ci	ftrace_nmi_exit();
47162306a36Sopenharmony_ci	if (irq_state.lockdep) {
47262306a36Sopenharmony_ci		trace_hardirqs_on_prepare();
47362306a36Sopenharmony_ci		lockdep_hardirqs_on_prepare();
47462306a36Sopenharmony_ci	}
47562306a36Sopenharmony_ci	instrumentation_end();
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	ct_nmi_exit();
47862306a36Sopenharmony_ci	lockdep_hardirq_exit();
47962306a36Sopenharmony_ci	if (irq_state.lockdep)
48062306a36Sopenharmony_ci		lockdep_hardirqs_on(CALLER_ADDR0);
48162306a36Sopenharmony_ci	__nmi_exit();
48262306a36Sopenharmony_ci}
483