1 | /* SPDX-License-Identifier: GPL-2.0 */ |
2 | /* |
3 | * syscall_wrapper.h - x86 specific wrappers to syscall definitions |
4 | */ |
5 | |
6 | #ifndef _ASM_X86_SYSCALL_WRAPPER_H |
7 | #define _ASM_X86_SYSCALL_WRAPPER_H |
8 | |
9 | /* Mapping of registers to parameters for syscalls on x86-64 and x32 */ |
10 | #define SC_X86_64_REGS_TO_ARGS(x, ...) \ |
11 | __MAP(x,__SC_ARGS \ |
12 | ,,regs->di,,regs->si,,regs->dx \ |
13 | ,,regs->r10,,regs->r8,,regs->r9) \ |
14 | |
15 | /* Mapping of registers to parameters for syscalls on i386 */ |
16 | #define SC_IA32_REGS_TO_ARGS(x, ...) \ |
17 | __MAP(x,__SC_ARGS \ |
18 | ,,(unsigned int)regs->bx,,(unsigned int)regs->cx \ |
19 | ,,(unsigned int)regs->dx,,(unsigned int)regs->si \ |
20 | ,,(unsigned int)regs->di,,(unsigned int)regs->bp) |
21 | |
22 | #ifdef CONFIG_IA32_EMULATION |
23 | /* |
24 | * For IA32 emulation, we need to handle "compat" syscalls *and* create |
25 | * additional wrappers (aptly named __ia32_sys_xyzzy) which decode the |
26 | * ia32 regs in the proper order for shared or "common" syscalls. As some |
27 | * syscalls may not be implemented, we need to expand COND_SYSCALL in |
28 | * kernel/sys_ni.c and SYS_NI in kernel/time/posix-stubs.c to cover this |
29 | * case as well. |
30 | */ |
31 | #define __IA32_COMPAT_SYS_STUBx(x, name, ...) \ |
32 | asmlinkage long __ia32_compat_sys##name(const struct pt_regs *regs);\ |
33 | ALLOW_ERROR_INJECTION(__ia32_compat_sys##name, ERRNO); \ |
34 | asmlinkage long __ia32_compat_sys##name(const struct pt_regs *regs)\ |
35 | { \ |
36 | return __se_compat_sys##name(SC_IA32_REGS_TO_ARGS(x,__VA_ARGS__));\ |
37 | } \ |
38 | |
39 | #define __IA32_SYS_STUBx(x, name, ...) \ |
40 | asmlinkage long __ia32_sys##name(const struct pt_regs *regs); \ |
41 | ALLOW_ERROR_INJECTION(__ia32_sys##name, ERRNO); \ |
42 | asmlinkage long __ia32_sys##name(const struct pt_regs *regs) \ |
43 | { \ |
44 | return __se_sys##name(SC_IA32_REGS_TO_ARGS(x,__VA_ARGS__));\ |
45 | } |
46 | |
47 | /* |
48 | * To keep the naming coherent, re-define SYSCALL_DEFINE0 to create an alias |
49 | * named __ia32_sys_*() |
50 | */ |
51 | #define SYSCALL_DEFINE0(sname) \ |
52 | SYSCALL_METADATA(_##sname, 0); \ |
53 | asmlinkage long __x64_sys_##sname(void); \ |
54 | ALLOW_ERROR_INJECTION(__x64_sys_##sname, ERRNO); \ |
55 | SYSCALL_ALIAS(__ia32_sys_##sname, __x64_sys_##sname); \ |
56 | asmlinkage long __x64_sys_##sname(void) |
57 | |
58 | #define COND_SYSCALL(name) \ |
59 | cond_syscall(__x64_sys_##name); \ |
60 | cond_syscall(__ia32_sys_##name) |
61 | |
62 | #define SYS_NI(name) \ |
63 | SYSCALL_ALIAS(__x64_sys_##name, sys_ni_posix_timers); \ |
64 | SYSCALL_ALIAS(__ia32_sys_##name, sys_ni_posix_timers) |
65 | |
66 | #else /* CONFIG_IA32_EMULATION */ |
67 | #define __IA32_COMPAT_SYS_STUBx(x, name, ...) |
68 | #define __IA32_SYS_STUBx(x, fullname, name, ...) |
69 | #endif /* CONFIG_IA32_EMULATION */ |
70 | |
71 | |
72 | #ifdef CONFIG_X86_X32 |
73 | /* |
74 | * For the x32 ABI, we need to create a stub for compat_sys_*() which is aware |
75 | * of the x86-64-style parameter ordering of x32 syscalls. The syscalls common |
76 | * with x86_64 obviously do not need such care. |
77 | */ |
78 | #define __X32_COMPAT_SYS_STUBx(x, name, ...) \ |
79 | asmlinkage long __x32_compat_sys##name(const struct pt_regs *regs);\ |
80 | ALLOW_ERROR_INJECTION(__x32_compat_sys##name, ERRNO); \ |
81 | asmlinkage long __x32_compat_sys##name(const struct pt_regs *regs)\ |
82 | { \ |
83 | return __se_compat_sys##name(SC_X86_64_REGS_TO_ARGS(x,__VA_ARGS__));\ |
84 | } \ |
85 | |
86 | #else /* CONFIG_X86_X32 */ |
87 | #define __X32_COMPAT_SYS_STUBx(x, name, ...) |
88 | #endif /* CONFIG_X86_X32 */ |
89 | |
90 | |
91 | #ifdef CONFIG_COMPAT |
92 | /* |
93 | * Compat means IA32_EMULATION and/or X86_X32. As they use a different |
94 | * mapping of registers to parameters, we need to generate stubs for each |
95 | * of them. |
96 | */ |
97 | #define COMPAT_SYSCALL_DEFINEx(x, name, ...) \ |
98 | static long __se_compat_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)); \ |
99 | static inline long __do_compat_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));\ |
100 | __IA32_COMPAT_SYS_STUBx(x, name, __VA_ARGS__) \ |
101 | __X32_COMPAT_SYS_STUBx(x, name, __VA_ARGS__) \ |
102 | static long __se_compat_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \ |
103 | { \ |
104 | return __do_compat_sys##name(__MAP(x,__SC_DELOUSE,__VA_ARGS__));\ |
105 | } \ |
106 | static inline long __do_compat_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) |
107 | |
108 | /* |
109 | * As some compat syscalls may not be implemented, we need to expand |
110 | * COND_SYSCALL_COMPAT in kernel/sys_ni.c and COMPAT_SYS_NI in |
111 | * kernel/time/posix-stubs.c to cover this case as well. |
112 | */ |
113 | #define COND_SYSCALL_COMPAT(name) \ |
114 | cond_syscall(__ia32_compat_sys_##name); \ |
115 | cond_syscall(__x32_compat_sys_##name) |
116 | |
117 | #define COMPAT_SYS_NI(name) \ |
118 | SYSCALL_ALIAS(__ia32_compat_sys_##name, sys_ni_posix_timers); \ |
119 | SYSCALL_ALIAS(__x32_compat_sys_##name, sys_ni_posix_timers) |
120 | |
121 | #endif /* CONFIG_COMPAT */ |
122 | |
123 | |
124 | /* |
125 | * Instead of the generic __SYSCALL_DEFINEx() definition, this macro takes |
126 | * struct pt_regs *regs as the only argument of the syscall stub named |
127 | * __x64_sys_*(). It decodes just the registers it needs and passes them on to |
128 | * the __se_sys_*() wrapper performing sign extension and then to the |
129 | * __do_sys_*() function doing the actual job. These wrappers and functions |
130 | * are inlined (at least in very most cases), meaning that the assembly looks |
131 | * as follows (slightly re-ordered for better readability): |
132 | * |
133 | * <__x64_sys_recv>: <-- syscall with 4 parameters |
134 | * callq <__fentry__> |
135 | * |
136 | * mov 0x70(%rdi),%rdi <-- decode regs->di |
137 | * mov 0x68(%rdi),%rsi <-- decode regs->si |
138 | * mov 0x60(%rdi),%rdx <-- decode regs->dx |
139 | * mov 0x38(%rdi),%rcx <-- decode regs->r10 |
140 | * |
141 | * xor %r9d,%r9d <-- clear %r9 |
142 | * xor %r8d,%r8d <-- clear %r8 |
143 | * |
144 | * callq __sys_recvfrom <-- do the actual work in __sys_recvfrom() |
145 | * which takes 6 arguments |
146 | * |
147 | * cltq <-- extend return value to 64-bit |
148 | * retq <-- return |
149 | * |
150 | * This approach avoids leaking random user-provided register content down |
151 | * the call chain. |
152 | * |
153 | * If IA32_EMULATION is enabled, this macro generates an additional wrapper |
154 | * named __ia32_sys_*() which decodes the struct pt_regs *regs according |
155 | * to the i386 calling convention (bx, cx, dx, si, di, bp). |
156 | */ |
157 | #define __SYSCALL_DEFINEx(x, name, ...) \ |
158 | asmlinkage long __x64_sys##name(const struct pt_regs *regs); \ |
159 | ALLOW_ERROR_INJECTION(__x64_sys##name, ERRNO); \ |
160 | static long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)); \ |
161 | static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));\ |
162 | asmlinkage long __x64_sys##name(const struct pt_regs *regs) \ |
163 | { \ |
164 | return __se_sys##name(SC_X86_64_REGS_TO_ARGS(x,__VA_ARGS__));\ |
165 | } \ |
166 | __IA32_SYS_STUBx(x, name, __VA_ARGS__) \ |
167 | static long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \ |
168 | { \ |
169 | long ret = __do_sys##name(__MAP(x,__SC_CAST,__VA_ARGS__));\ |
170 | __MAP(x,__SC_TEST,__VA_ARGS__); \ |
171 | __PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__)); \ |
172 | return ret; \ |
173 | } \ |
174 | static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) |
175 | |
176 | /* |
177 | * As the generic SYSCALL_DEFINE0() macro does not decode any parameters for |
178 | * obvious reasons, and passing struct pt_regs *regs to it in %rdi does not |
179 | * hurt, we only need to re-define it here to keep the naming congruent to |
180 | * SYSCALL_DEFINEx() -- which is essential for the COND_SYSCALL() and SYS_NI() |
181 | * macros to work correctly. |
182 | */ |
183 | #ifndef SYSCALL_DEFINE0 |
184 | #define SYSCALL_DEFINE0(sname) \ |
185 | SYSCALL_METADATA(_##sname, 0); \ |
186 | asmlinkage long __x64_sys_##sname(void); \ |
187 | ALLOW_ERROR_INJECTION(__x64_sys_##sname, ERRNO); \ |
188 | asmlinkage long __x64_sys_##sname(void) |
189 | #endif |
190 | |
191 | #ifndef COND_SYSCALL |
192 | #define COND_SYSCALL(name) cond_syscall(__x64_sys_##name) |
193 | #endif |
194 | |
195 | #ifndef SYS_NI |
196 | #define SYS_NI(name) SYSCALL_ALIAS(__x64_sys_##name, sys_ni_posix_timers); |
197 | #endif |
198 | |
199 | |
200 | /* |
201 | * For VSYSCALLS, we need to declare these three syscalls with the new |
202 | * pt_regs-based calling convention for in-kernel use. |
203 | */ |
204 | struct pt_regs; |
205 | asmlinkage long __x64_sys_getcpu(const struct pt_regs *regs); |
206 | asmlinkage long __x64_sys_gettimeofday(const struct pt_regs *regs); |
207 | asmlinkage long __x64_sys_time(const struct pt_regs *regs); |
208 | |
209 | #endif /* _ASM_X86_SYSCALL_WRAPPER_H */ |
210 | |