| 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
| 2 | /* |
| 3 | * Access to user system call parameters and results |
| 4 | * |
| 5 | * Copyright IBM Corp. 2008 |
| 6 | * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com) |
| 7 | */ |
| 8 | |
| 9 | #ifndef _ASM_SYSCALL_H |
| 10 | #define _ASM_SYSCALL_H 1 |
| 11 | |
| 12 | #include <uapi/linux/audit.h> |
| 13 | #include <linux/sched.h> |
| 14 | #include <linux/err.h> |
| 15 | #include <asm/ptrace.h> |
| 16 | |
| 17 | extern const sys_call_ptr_t sys_call_table[]; |
| 18 | extern const sys_call_ptr_t sys_call_table_emu[]; |
| 19 | |
| 20 | static inline long syscall_get_nr(struct task_struct *task, |
| 21 | struct pt_regs *regs) |
| 22 | { |
| 23 | return test_pt_regs_flag(regs, PIF_SYSCALL) ? |
| 24 | (regs->int_code & 0xffff) : -1; |
| 25 | } |
| 26 | |
| 27 | static inline void syscall_set_nr(struct task_struct *task, |
| 28 | struct pt_regs *regs, |
| 29 | int nr) |
| 30 | { |
| 31 | /* |
| 32 | * Unlike syscall_get_nr(), syscall_set_nr() can be called only when |
| 33 | * the target task is stopped for tracing on entering syscall, so |
| 34 | * there is no need to have the same check syscall_get_nr() has. |
| 35 | */ |
| 36 | regs->int_code = (regs->int_code & ~0xffff) | (nr & 0xffff); |
| 37 | } |
| 38 | |
| 39 | static inline void syscall_rollback(struct task_struct *task, |
| 40 | struct pt_regs *regs) |
| 41 | { |
| 42 | regs->gprs[2] = regs->orig_gpr2; |
| 43 | } |
| 44 | |
| 45 | static inline long syscall_get_error(struct task_struct *task, |
| 46 | struct pt_regs *regs) |
| 47 | { |
| 48 | unsigned long error = regs->gprs[2]; |
| 49 | #ifdef CONFIG_COMPAT |
| 50 | if (test_tsk_thread_flag(task, TIF_31BIT)) { |
| 51 | /* |
| 52 | * Sign-extend the value so (int)-EFOO becomes (long)-EFOO |
| 53 | * and will match correctly in comparisons. |
| 54 | */ |
| 55 | error = (long)(int)error; |
| 56 | } |
| 57 | #endif |
| 58 | return IS_ERR_VALUE(error) ? error : 0; |
| 59 | } |
| 60 | |
| 61 | static inline long syscall_get_return_value(struct task_struct *task, |
| 62 | struct pt_regs *regs) |
| 63 | { |
| 64 | return regs->gprs[2]; |
| 65 | } |
| 66 | |
| 67 | static inline void syscall_set_return_value(struct task_struct *task, |
| 68 | struct pt_regs *regs, |
| 69 | int error, long val) |
| 70 | { |
| 71 | set_pt_regs_flag(regs, PIF_SYSCALL_RET_SET); |
| 72 | regs->gprs[2] = error ? error : val; |
| 73 | } |
| 74 | |
| 75 | static inline void syscall_get_arguments(struct task_struct *task, |
| 76 | struct pt_regs *regs, |
| 77 | unsigned long *args) |
| 78 | { |
| 79 | unsigned long mask = -1UL; |
| 80 | |
| 81 | #ifdef CONFIG_COMPAT |
| 82 | if (test_tsk_thread_flag(task, TIF_31BIT)) |
| 83 | mask = 0xffffffff; |
| 84 | #endif |
| 85 | for (int i = 1; i < 6; i++) |
| 86 | args[i] = regs->gprs[2 + i] & mask; |
| 87 | |
| 88 | args[0] = regs->orig_gpr2 & mask; |
| 89 | } |
| 90 | |
| 91 | static inline void syscall_set_arguments(struct task_struct *task, |
| 92 | struct pt_regs *regs, |
| 93 | const unsigned long *args) |
| 94 | { |
| 95 | regs->orig_gpr2 = args[0]; |
| 96 | for (int n = 1; n < 6; n++) |
| 97 | regs->gprs[2 + n] = args[n]; |
| 98 | } |
| 99 | |
| 100 | static inline int syscall_get_arch(struct task_struct *task) |
| 101 | { |
| 102 | #ifdef CONFIG_COMPAT |
| 103 | if (test_tsk_thread_flag(task, TIF_31BIT)) |
| 104 | return AUDIT_ARCH_S390; |
| 105 | #endif |
| 106 | return AUDIT_ARCH_S390X; |
| 107 | } |
| 108 | |
| 109 | static inline bool arch_syscall_is_vdso_sigreturn(struct pt_regs *regs) |
| 110 | { |
| 111 | return false; |
| 112 | } |
| 113 | |
| 114 | #define SYSCALL_FMT_0 |
| 115 | #define SYSCALL_FMT_1 , "0" (r2) |
| 116 | #define SYSCALL_FMT_2 , "d" (r3) SYSCALL_FMT_1 |
| 117 | #define SYSCALL_FMT_3 , "d" (r4) SYSCALL_FMT_2 |
| 118 | #define SYSCALL_FMT_4 , "d" (r5) SYSCALL_FMT_3 |
| 119 | #define SYSCALL_FMT_5 , "d" (r6) SYSCALL_FMT_4 |
| 120 | #define SYSCALL_FMT_6 , "d" (r7) SYSCALL_FMT_5 |
| 121 | |
| 122 | #define SYSCALL_PARM_0 |
| 123 | #define SYSCALL_PARM_1 , long arg1 |
| 124 | #define SYSCALL_PARM_2 SYSCALL_PARM_1, long arg2 |
| 125 | #define SYSCALL_PARM_3 SYSCALL_PARM_2, long arg3 |
| 126 | #define SYSCALL_PARM_4 SYSCALL_PARM_3, long arg4 |
| 127 | #define SYSCALL_PARM_5 SYSCALL_PARM_4, long arg5 |
| 128 | #define SYSCALL_PARM_6 SYSCALL_PARM_5, long arg6 |
| 129 | |
| 130 | #define SYSCALL_REGS_0 |
| 131 | #define SYSCALL_REGS_1 \ |
| 132 | register long r2 asm("2") = arg1 |
| 133 | #define SYSCALL_REGS_2 \ |
| 134 | SYSCALL_REGS_1; \ |
| 135 | register long r3 asm("3") = arg2 |
| 136 | #define SYSCALL_REGS_3 \ |
| 137 | SYSCALL_REGS_2; \ |
| 138 | register long r4 asm("4") = arg3 |
| 139 | #define SYSCALL_REGS_4 \ |
| 140 | SYSCALL_REGS_3; \ |
| 141 | register long r5 asm("5") = arg4 |
| 142 | #define SYSCALL_REGS_5 \ |
| 143 | SYSCALL_REGS_4; \ |
| 144 | register long r6 asm("6") = arg5 |
| 145 | #define SYSCALL_REGS_6 \ |
| 146 | SYSCALL_REGS_5; \ |
| 147 | register long r7 asm("7") = arg6 |
| 148 | |
| 149 | #define GENERATE_SYSCALL_FUNC(nr) \ |
| 150 | static __always_inline \ |
| 151 | long syscall##nr(unsigned long syscall SYSCALL_PARM_##nr) \ |
| 152 | { \ |
| 153 | register unsigned long r1 asm ("1") = syscall; \ |
| 154 | register long rc asm ("2"); \ |
| 155 | SYSCALL_REGS_##nr; \ |
| 156 | \ |
| 157 | asm volatile ( \ |
| 158 | " svc 0\n" \ |
| 159 | : "=d" (rc) \ |
| 160 | : "d" (r1) SYSCALL_FMT_##nr \ |
| 161 | : "memory"); \ |
| 162 | return rc; \ |
| 163 | } |
| 164 | |
| 165 | GENERATE_SYSCALL_FUNC(0) |
| 166 | GENERATE_SYSCALL_FUNC(1) |
| 167 | GENERATE_SYSCALL_FUNC(2) |
| 168 | GENERATE_SYSCALL_FUNC(3) |
| 169 | GENERATE_SYSCALL_FUNC(4) |
| 170 | GENERATE_SYSCALL_FUNC(5) |
| 171 | GENERATE_SYSCALL_FUNC(6) |
| 172 | |
| 173 | #endif /* _ASM_SYSCALL_H */ |