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