18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci#include <linux/ptrace.h>
38c2ecf20Sopenharmony_ci#include <linux/sched.h>
48c2ecf20Sopenharmony_ci#include <linux/sched/task_stack.h>
58c2ecf20Sopenharmony_ci#include <linux/export.h>
68c2ecf20Sopenharmony_ci#include <asm/syscall.h>
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_cistatic int collect_syscall(struct task_struct *target, struct syscall_info *info)
98c2ecf20Sopenharmony_ci{
108c2ecf20Sopenharmony_ci	unsigned long args[6] = { };
118c2ecf20Sopenharmony_ci	struct pt_regs *regs;
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci	if (!try_get_task_stack(target)) {
148c2ecf20Sopenharmony_ci		/* Task has no stack, so the task isn't in a syscall. */
158c2ecf20Sopenharmony_ci		memset(info, 0, sizeof(*info));
168c2ecf20Sopenharmony_ci		info->data.nr = -1;
178c2ecf20Sopenharmony_ci		return 0;
188c2ecf20Sopenharmony_ci	}
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci	regs = task_pt_regs(target);
218c2ecf20Sopenharmony_ci	if (unlikely(!regs)) {
228c2ecf20Sopenharmony_ci		put_task_stack(target);
238c2ecf20Sopenharmony_ci		return -EAGAIN;
248c2ecf20Sopenharmony_ci	}
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	info->sp = user_stack_pointer(regs);
278c2ecf20Sopenharmony_ci	info->data.instruction_pointer = instruction_pointer(regs);
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	info->data.nr = syscall_get_nr(target, regs);
308c2ecf20Sopenharmony_ci	if (info->data.nr != -1L)
318c2ecf20Sopenharmony_ci		syscall_get_arguments(target, regs, args);
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	info->data.args[0] = args[0];
348c2ecf20Sopenharmony_ci	info->data.args[1] = args[1];
358c2ecf20Sopenharmony_ci	info->data.args[2] = args[2];
368c2ecf20Sopenharmony_ci	info->data.args[3] = args[3];
378c2ecf20Sopenharmony_ci	info->data.args[4] = args[4];
388c2ecf20Sopenharmony_ci	info->data.args[5] = args[5];
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	put_task_stack(target);
418c2ecf20Sopenharmony_ci	return 0;
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci/**
458c2ecf20Sopenharmony_ci * task_current_syscall - Discover what a blocked task is doing.
468c2ecf20Sopenharmony_ci * @target:		thread to examine
478c2ecf20Sopenharmony_ci * @info:		structure with the following fields:
488c2ecf20Sopenharmony_ci *			 .sp        - filled with user stack pointer
498c2ecf20Sopenharmony_ci *			 .data.nr   - filled with system call number or -1
508c2ecf20Sopenharmony_ci *			 .data.args - filled with @maxargs system call arguments
518c2ecf20Sopenharmony_ci *			 .data.instruction_pointer - filled with user PC
528c2ecf20Sopenharmony_ci *
538c2ecf20Sopenharmony_ci * If @target is blocked in a system call, returns zero with @info.data.nr
548c2ecf20Sopenharmony_ci * set to the call's number and @info.data.args filled in with its
558c2ecf20Sopenharmony_ci * arguments. Registers not used for system call arguments may not be available
568c2ecf20Sopenharmony_ci * and it is not kosher to use &struct user_regset calls while the system
578c2ecf20Sopenharmony_ci * call is still in progress.  Note we may get this result if @target
588c2ecf20Sopenharmony_ci * has finished its system call but not yet returned to user mode, such
598c2ecf20Sopenharmony_ci * as when it's stopped for signal handling or syscall exit tracing.
608c2ecf20Sopenharmony_ci *
618c2ecf20Sopenharmony_ci * If @target is blocked in the kernel during a fault or exception,
628c2ecf20Sopenharmony_ci * returns zero with *@info.data.nr set to -1 and does not fill in
638c2ecf20Sopenharmony_ci * @info.data.args. If so, it's now safe to examine @target using
648c2ecf20Sopenharmony_ci * &struct user_regset get() calls as long as we're sure @target won't return
658c2ecf20Sopenharmony_ci * to user mode.
668c2ecf20Sopenharmony_ci *
678c2ecf20Sopenharmony_ci * Returns -%EAGAIN if @target does not remain blocked.
688c2ecf20Sopenharmony_ci */
698c2ecf20Sopenharmony_ciint task_current_syscall(struct task_struct *target, struct syscall_info *info)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	long state;
728c2ecf20Sopenharmony_ci	unsigned long ncsw;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	if (target == current)
758c2ecf20Sopenharmony_ci		return collect_syscall(target, info);
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	state = target->state;
788c2ecf20Sopenharmony_ci	if (unlikely(!state))
798c2ecf20Sopenharmony_ci		return -EAGAIN;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	ncsw = wait_task_inactive(target, state);
828c2ecf20Sopenharmony_ci	if (unlikely(!ncsw) ||
838c2ecf20Sopenharmony_ci	    unlikely(collect_syscall(target, info)) ||
848c2ecf20Sopenharmony_ci	    unlikely(wait_task_inactive(target, state) != ncsw))
858c2ecf20Sopenharmony_ci		return -EAGAIN;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	return 0;
888c2ecf20Sopenharmony_ci}
89