| // SPDX-License-Identifier: GPL-2.0 | |
| /* | |
| * Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) | |
| */ | |
| #include <linux/kernel.h> | |
| #include <linux/ptrace.h> | |
| #include <linux/seccomp.h> | |
| #include <kern_util.h> | |
| #include <sysdep/ptrace.h> | |
| #include <sysdep/ptrace_user.h> | |
| #include <linux/time-internal.h> | |
| #include <asm/syscall.h> | |
| #include <asm/unistd.h> | |
| #include <asm/delay.h> | |
| void handle_syscall(struct uml_pt_regs *r) | |
| { | |
| struct pt_regs *regs = container_of(r, struct pt_regs, regs); | |
| int syscall; | |
| /* Initialize the syscall number and default return value. */ | |
| UPT_SYSCALL_NR(r) = PT_SYSCALL_NR(r->gp); | |
| PT_REGS_SET_SYSCALL_RETURN(regs, -ENOSYS); | |
| if (syscall_trace_enter(regs)) | |
| goto out; | |
| /* Do the seccomp check after ptrace; failures should be fast. */ | |
| if (!seccomp_permit_syscall()) | |
| goto out; | |
| syscall = UPT_SYSCALL_NR(r); | |
| /* | |
| * If no time passes, then sched_yield may not actually yield, causing | |
| * broken spinlock implementations in userspace (ASAN) to hang for long | |
| * periods of time. Advance the time-travel clock a bit so sched_yield() | |
| * in a loop (like ping does for small intervals) also works correctly. | |
| */ | |
| if ((time_travel_mode == TT_MODE_INFCPU || | |
| time_travel_mode == TT_MODE_EXTERNAL) && | |
| syscall == __NR_sched_yield) | |
| time_travel_ndelay(1000); | |
| if (syscall >= 0 && syscall < __NR_syscalls) { | |
| unsigned long ret; | |
| ret = (*sys_call_table[syscall])(UPT_SYSCALL_ARG1(®s->regs), | |
| UPT_SYSCALL_ARG2(®s->regs), | |
| UPT_SYSCALL_ARG3(®s->regs), | |
| UPT_SYSCALL_ARG4(®s->regs), | |
| UPT_SYSCALL_ARG5(®s->regs), | |
| UPT_SYSCALL_ARG6(®s->regs)); | |
| PT_REGS_SET_SYSCALL_RETURN(regs, ret); | |
| /* | |
| * An error value here can be some form of -ERESTARTSYS | |
| * and then we'd just loop. Make any error syscalls take | |
| * some time, so that it won't just loop if something is | |
| * not ready, and hopefully other things will make some | |
| * progress. | |
| */ | |
| if (IS_ERR_VALUE(ret) && | |
| (time_travel_mode == TT_MODE_INFCPU || | |
| time_travel_mode == TT_MODE_EXTERNAL)) { | |
| um_udelay(1); | |
| schedule(); | |
| } | |
| } | |
| out: | |
| syscall_trace_leave(regs); | |
| } |