xref: /openbmc/linux/lib/syscall.c (revision 2f064a59)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2bbc69863SRoland McGrath #include <linux/ptrace.h>
3bbc69863SRoland McGrath #include <linux/sched.h>
468db0cf1SIngo Molnar #include <linux/sched/task_stack.h>
58bc3bcc9SPaul Gortmaker #include <linux/export.h>
6bbc69863SRoland McGrath #include <asm/syscall.h>
7bbc69863SRoland McGrath 
collect_syscall(struct task_struct * target,struct syscall_info * info)8631b7abaSSteven Rostedt (Red Hat) static int collect_syscall(struct task_struct *target, struct syscall_info *info)
9bbc69863SRoland McGrath {
104f134b89SWilly Tarreau 	unsigned long args[6] = { };
11aa1f1a63SAndy Lutomirski 	struct pt_regs *regs;
12aa1f1a63SAndy Lutomirski 
13aa1f1a63SAndy Lutomirski 	if (!try_get_task_stack(target)) {
14aa1f1a63SAndy Lutomirski 		/* Task has no stack, so the task isn't in a syscall. */
15631b7abaSSteven Rostedt (Red Hat) 		memset(info, 0, sizeof(*info));
16631b7abaSSteven Rostedt (Red Hat) 		info->data.nr = -1;
17aa1f1a63SAndy Lutomirski 		return 0;
18aa1f1a63SAndy Lutomirski 	}
19aa1f1a63SAndy Lutomirski 
20aa1f1a63SAndy Lutomirski 	regs = task_pt_regs(target);
21aa1f1a63SAndy Lutomirski 	if (unlikely(!regs)) {
22aa1f1a63SAndy Lutomirski 		put_task_stack(target);
23bbc69863SRoland McGrath 		return -EAGAIN;
24aa1f1a63SAndy Lutomirski 	}
25bbc69863SRoland McGrath 
26631b7abaSSteven Rostedt (Red Hat) 	info->sp = user_stack_pointer(regs);
27631b7abaSSteven Rostedt (Red Hat) 	info->data.instruction_pointer = instruction_pointer(regs);
28bbc69863SRoland McGrath 
29631b7abaSSteven Rostedt (Red Hat) 	info->data.nr = syscall_get_nr(target, regs);
30631b7abaSSteven Rostedt (Red Hat) 	if (info->data.nr != -1L)
314f134b89SWilly Tarreau 		syscall_get_arguments(target, regs, args);
324f134b89SWilly Tarreau 
334f134b89SWilly Tarreau 	info->data.args[0] = args[0];
344f134b89SWilly Tarreau 	info->data.args[1] = args[1];
354f134b89SWilly Tarreau 	info->data.args[2] = args[2];
364f134b89SWilly Tarreau 	info->data.args[3] = args[3];
374f134b89SWilly Tarreau 	info->data.args[4] = args[4];
384f134b89SWilly Tarreau 	info->data.args[5] = args[5];
39bbc69863SRoland McGrath 
40aa1f1a63SAndy Lutomirski 	put_task_stack(target);
41bbc69863SRoland McGrath 	return 0;
42bbc69863SRoland McGrath }
43bbc69863SRoland McGrath 
44bbc69863SRoland McGrath /**
45bbc69863SRoland McGrath  * task_current_syscall - Discover what a blocked task is doing.
46bbc69863SRoland McGrath  * @target:		thread to examine
47631b7abaSSteven Rostedt (Red Hat)  * @info:		structure with the following fields:
48631b7abaSSteven Rostedt (Red Hat)  *			 .sp        - filled with user stack pointer
49631b7abaSSteven Rostedt (Red Hat)  *			 .data.nr   - filled with system call number or -1
50631b7abaSSteven Rostedt (Red Hat)  *			 .data.args - filled with @maxargs system call arguments
51631b7abaSSteven Rostedt (Red Hat)  *			 .data.instruction_pointer - filled with user PC
52bbc69863SRoland McGrath  *
53631b7abaSSteven Rostedt (Red Hat)  * If @target is blocked in a system call, returns zero with @info.data.nr
54408a93a2SRandy Dunlap  * set to the call's number and @info.data.args filled in with its
55631b7abaSSteven Rostedt (Red Hat)  * arguments. Registers not used for system call arguments may not be available
56631b7abaSSteven Rostedt (Red Hat)  * and it is not kosher to use &struct user_regset calls while the system
57bbc69863SRoland McGrath  * call is still in progress.  Note we may get this result if @target
58bbc69863SRoland McGrath  * has finished its system call but not yet returned to user mode, such
59bbc69863SRoland McGrath  * as when it's stopped for signal handling or syscall exit tracing.
60bbc69863SRoland McGrath  *
61bbc69863SRoland McGrath  * If @target is blocked in the kernel during a fault or exception,
62631b7abaSSteven Rostedt (Red Hat)  * returns zero with *@info.data.nr set to -1 and does not fill in
63631b7abaSSteven Rostedt (Red Hat)  * @info.data.args. If so, it's now safe to examine @target using
64631b7abaSSteven Rostedt (Red Hat)  * &struct user_regset get() calls as long as we're sure @target won't return
65631b7abaSSteven Rostedt (Red Hat)  * to user mode.
66bbc69863SRoland McGrath  *
67bbc69863SRoland McGrath  * Returns -%EAGAIN if @target does not remain blocked.
68bbc69863SRoland McGrath  */
task_current_syscall(struct task_struct * target,struct syscall_info * info)69631b7abaSSteven Rostedt (Red Hat) int task_current_syscall(struct task_struct *target, struct syscall_info *info)
70bbc69863SRoland McGrath {
71bbc69863SRoland McGrath 	unsigned long ncsw;
72*2f064a59SPeter Zijlstra 	unsigned int state;
73bbc69863SRoland McGrath 
74bbc69863SRoland McGrath 	if (target == current)
75631b7abaSSteven Rostedt (Red Hat) 		return collect_syscall(target, info);
76bbc69863SRoland McGrath 
77*2f064a59SPeter Zijlstra 	state = READ_ONCE(target->__state);
78bbc69863SRoland McGrath 	if (unlikely(!state))
79bbc69863SRoland McGrath 		return -EAGAIN;
80bbc69863SRoland McGrath 
81bbc69863SRoland McGrath 	ncsw = wait_task_inactive(target, state);
82bbc69863SRoland McGrath 	if (unlikely(!ncsw) ||
83631b7abaSSteven Rostedt (Red Hat) 	    unlikely(collect_syscall(target, info)) ||
84bbc69863SRoland McGrath 	    unlikely(wait_task_inactive(target, state) != ncsw))
85bbc69863SRoland McGrath 		return -EAGAIN;
86bbc69863SRoland McGrath 
87bbc69863SRoland McGrath 	return 0;
88bbc69863SRoland McGrath }
89