| 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | /* |
| 3 | * S390 version |
| 4 | * Copyright IBM Corp. 1999, 2000 |
| 5 | * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), |
| 6 | * Thomas Spatzier (tspat@de.ibm.com) |
| 7 | * |
| 8 | * Derived from "arch/i386/kernel/sys_i386.c" |
| 9 | * |
| 10 | * This file contains various random system calls that |
| 11 | * have a non-standard calling sequence on the Linux/s390 |
| 12 | * platform. |
| 13 | */ |
| 14 | |
| 15 | #include <linux/cpufeature.h> |
| 16 | #include <linux/errno.h> |
| 17 | #include <linux/sched.h> |
| 18 | #include <linux/mm.h> |
| 19 | #include <linux/fs.h> |
| 20 | #include <linux/smp.h> |
| 21 | #include <linux/sem.h> |
| 22 | #include <linux/msg.h> |
| 23 | #include <linux/shm.h> |
| 24 | #include <linux/stat.h> |
| 25 | #include <linux/syscalls.h> |
| 26 | #include <linux/mman.h> |
| 27 | #include <linux/file.h> |
| 28 | #include <linux/utsname.h> |
| 29 | #include <linux/personality.h> |
| 30 | #include <linux/unistd.h> |
| 31 | #include <linux/ipc.h> |
| 32 | #include <linux/uaccess.h> |
| 33 | #include <linux/string.h> |
| 34 | #include <linux/thread_info.h> |
| 35 | #include <linux/entry-common.h> |
| 36 | |
| 37 | #include <asm/ptrace.h> |
| 38 | #include <asm/vtime.h> |
| 39 | |
| 40 | #include "entry.h" |
| 41 | |
| 42 | #define __SYSCALL(nr, sym) long __s390x_##sym(struct pt_regs *); |
| 43 | #include <asm/syscall_table.h> |
| 44 | #undef __SYSCALL |
| 45 | |
| 46 | #define __SYSCALL(nr, sym) [nr] = (__s390x_##sym), |
| 47 | const sys_call_ptr_t sys_call_table[__NR_syscalls] = { |
| 48 | #include <asm/syscall_table.h> |
| 49 | }; |
| 50 | #undef __SYSCALL |
| 51 | |
| 52 | #ifdef CONFIG_SYSVIPC |
| 53 | /* |
| 54 | * sys_ipc() is the de-multiplexer for the SysV IPC calls. |
| 55 | */ |
| 56 | SYSCALL_DEFINE5(s390_ipc, uint, call, int, first, unsigned long, second, |
| 57 | unsigned long, third, void __user *, ptr) |
| 58 | { |
| 59 | if (call >> 16) |
| 60 | return -EINVAL; |
| 61 | /* The s390 sys_ipc variant has only five parameters instead of six |
| 62 | * like the generic variant. The only difference is the handling of |
| 63 | * the SEMTIMEDOP subcall where on s390 the third parameter is used |
| 64 | * as a pointer to a struct timespec where the generic variant uses |
| 65 | * the fifth parameter. |
| 66 | * Therefore we can call the generic variant by simply passing the |
| 67 | * third parameter also as fifth parameter. |
| 68 | */ |
| 69 | return ksys_ipc(call, first, second, third, ptr, fifth: third); |
| 70 | } |
| 71 | #endif /* CONFIG_SYSVIPC */ |
| 72 | |
| 73 | SYSCALL_DEFINE1(s390_personality, unsigned int, personality) |
| 74 | { |
| 75 | unsigned int ret = current->personality; |
| 76 | |
| 77 | if (personality(current->personality) == PER_LINUX32 && |
| 78 | personality(personality) == PER_LINUX) |
| 79 | personality |= PER_LINUX32; |
| 80 | |
| 81 | if (personality != 0xffffffff) |
| 82 | set_personality(personality); |
| 83 | |
| 84 | if (personality(ret) == PER_LINUX32) |
| 85 | ret &= ~PER_LINUX32; |
| 86 | |
| 87 | return ret; |
| 88 | } |
| 89 | |
| 90 | SYSCALL_DEFINE0(ni_syscall) |
| 91 | { |
| 92 | return -ENOSYS; |
| 93 | } |
| 94 | |
| 95 | void noinstr __do_syscall(struct pt_regs *regs, int per_trap) |
| 96 | { |
| 97 | unsigned long nr; |
| 98 | |
| 99 | add_random_kstack_offset(); |
| 100 | enter_from_user_mode(regs); |
| 101 | regs->psw = get_lowcore()->svc_old_psw; |
| 102 | regs->int_code = get_lowcore()->svc_int_code; |
| 103 | update_timer_sys(); |
| 104 | if (cpu_has_bear()) |
| 105 | current->thread.last_break = regs->last_break; |
| 106 | local_irq_enable(); |
| 107 | regs->orig_gpr2 = regs->gprs[2]; |
| 108 | if (unlikely(per_trap)) |
| 109 | set_thread_flag(TIF_PER_TRAP); |
| 110 | regs->flags = 0; |
| 111 | set_pt_regs_flag(regs, PIF_SYSCALL); |
| 112 | nr = regs->int_code & 0xffff; |
| 113 | if (likely(!nr)) { |
| 114 | nr = regs->gprs[1] & 0xffff; |
| 115 | regs->int_code &= ~0xffffUL; |
| 116 | regs->int_code |= nr; |
| 117 | } |
| 118 | regs->gprs[2] = nr; |
| 119 | if (nr == __NR_restart_syscall && !(current->restart_block.arch_data & 1)) { |
| 120 | regs->psw.addr = current->restart_block.arch_data; |
| 121 | current->restart_block.arch_data = 1; |
| 122 | } |
| 123 | nr = syscall_enter_from_user_mode_work(regs, syscall: nr); |
| 124 | /* |
| 125 | * In the s390 ptrace ABI, both the syscall number and the return value |
| 126 | * use gpr2. However, userspace puts the syscall number either in the |
| 127 | * svc instruction itself, or uses gpr1. To make at least skipping syscalls |
| 128 | * work, the ptrace code sets PIF_SYSCALL_RET_SET, which is checked here |
| 129 | * and if set, the syscall will be skipped. |
| 130 | */ |
| 131 | if (unlikely(test_and_clear_pt_regs_flag(regs, PIF_SYSCALL_RET_SET))) |
| 132 | goto out; |
| 133 | regs->gprs[2] = -ENOSYS; |
| 134 | if (likely(nr < NR_syscalls)) |
| 135 | regs->gprs[2] = sys_call_table[nr](regs); |
| 136 | out: |
| 137 | syscall_exit_to_user_mode(regs); |
| 138 | } |
| 139 | |