1 /* SPDX-License-Identifier: MIT */ 2 3 #ifndef __INTERNAL__LIBURING_SYSCALL_H 4 #error "This file should be included from src/syscall.h (liburing)" 5 #endif 6 7 #ifndef LIBURING_ARCH_X86_SYSCALL_H 8 #define LIBURING_ARCH_X86_SYSCALL_H 9 10 #if defined(__x86_64__) 11 /** 12 * Note for syscall registers usage (x86-64): 13 * - %rax is the syscall number. 14 * - %rax is also the return value. 15 * - %rdi is the 1st argument. 16 * - %rsi is the 2nd argument. 17 * - %rdx is the 3rd argument. 18 * - %r10 is the 4th argument (**yes it's %r10, not %rcx!**). 19 * - %r8 is the 5th argument. 20 * - %r9 is the 6th argument. 21 * 22 * `syscall` instruction will clobber %r11 and %rcx. 23 * 24 * After the syscall returns to userspace: 25 * - %r11 will contain %rflags. 26 * - %rcx will contain the return address. 27 * 28 * IOW, after the syscall returns to userspace: 29 * %r11 == %rflags and %rcx == %rip. 30 */ 31 32 #define __do_syscall0(NUM) ({ \ 33 intptr_t rax; \ 34 \ 35 __asm__ volatile( \ 36 "syscall" \ 37 : "=a"(rax) /* %rax */ \ 38 : "a"(NUM) /* %rax */ \ 39 : "rcx", "r11", "memory" \ 40 ); \ 41 rax; \ 42 }) 43 44 #define __do_syscall1(NUM, ARG1) ({ \ 45 intptr_t rax; \ 46 \ 47 __asm__ volatile( \ 48 "syscall" \ 49 : "=a"(rax) /* %rax */ \ 50 : "a"((NUM)), /* %rax */ \ 51 "D"((ARG1)) /* %rdi */ \ 52 : "rcx", "r11", "memory" \ 53 ); \ 54 rax; \ 55 }) 56 57 #define __do_syscall2(NUM, ARG1, ARG2) ({ \ 58 intptr_t rax; \ 59 \ 60 __asm__ volatile( \ 61 "syscall" \ 62 : "=a"(rax) /* %rax */ \ 63 : "a"((NUM)), /* %rax */ \ 64 "D"((ARG1)), /* %rdi */ \ 65 "S"((ARG2)) /* %rsi */ \ 66 : "rcx", "r11", "memory" \ 67 ); \ 68 rax; \ 69 }) 70 71 #define __do_syscall3(NUM, ARG1, ARG2, ARG3) ({ \ 72 intptr_t rax; \ 73 \ 74 __asm__ volatile( \ 75 "syscall" \ 76 : "=a"(rax) /* %rax */ \ 77 : "a"((NUM)), /* %rax */ \ 78 "D"((ARG1)), /* %rdi */ \ 79 "S"((ARG2)), /* %rsi */ \ 80 "d"((ARG3)) /* %rdx */ \ 81 : "rcx", "r11", "memory" \ 82 ); \ 83 rax; \ 84 }) 85 86 #define __do_syscall4(NUM, ARG1, ARG2, ARG3, ARG4) ({ \ 87 intptr_t rax; \ 88 register __typeof__(ARG4) __r10 __asm__("r10") = (ARG4); \ 89 \ 90 __asm__ volatile( \ 91 "syscall" \ 92 : "=a"(rax) /* %rax */ \ 93 : "a"((NUM)), /* %rax */ \ 94 "D"((ARG1)), /* %rdi */ \ 95 "S"((ARG2)), /* %rsi */ \ 96 "d"((ARG3)), /* %rdx */ \ 97 "r"(__r10) /* %r10 */ \ 98 : "rcx", "r11", "memory" \ 99 ); \ 100 rax; \ 101 }) 102 103 #define __do_syscall5(NUM, ARG1, ARG2, ARG3, ARG4, ARG5) ({ \ 104 intptr_t rax; \ 105 register __typeof__(ARG4) __r10 __asm__("r10") = (ARG4); \ 106 register __typeof__(ARG5) __r8 __asm__("r8") = (ARG5); \ 107 \ 108 __asm__ volatile( \ 109 "syscall" \ 110 : "=a"(rax) /* %rax */ \ 111 : "a"((NUM)), /* %rax */ \ 112 "D"((ARG1)), /* %rdi */ \ 113 "S"((ARG2)), /* %rsi */ \ 114 "d"((ARG3)), /* %rdx */ \ 115 "r"(__r10), /* %r10 */ \ 116 "r"(__r8) /* %r8 */ \ 117 : "rcx", "r11", "memory" \ 118 ); \ 119 rax; \ 120 }) 121 122 #define __do_syscall6(NUM, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6) ({ \ 123 intptr_t rax; \ 124 register __typeof__(ARG4) __r10 __asm__("r10") = (ARG4); \ 125 register __typeof__(ARG5) __r8 __asm__("r8") = (ARG5); \ 126 register __typeof__(ARG6) __r9 __asm__("r9") = (ARG6); \ 127 \ 128 __asm__ volatile( \ 129 "syscall" \ 130 : "=a"(rax) /* %rax */ \ 131 : "a"((NUM)), /* %rax */ \ 132 "D"((ARG1)), /* %rdi */ \ 133 "S"((ARG2)), /* %rsi */ \ 134 "d"((ARG3)), /* %rdx */ \ 135 "r"(__r10), /* %r10 */ \ 136 "r"(__r8), /* %r8 */ \ 137 "r"(__r9) /* %r9 */ \ 138 : "rcx", "r11", "memory" \ 139 ); \ 140 rax; \ 141 }) 142 143 #include "../syscall-defs.h" 144 145 #else /* #if defined(__x86_64__) */ 146 147 #ifdef CONFIG_NOLIBC 148 /** 149 * Note for syscall registers usage (x86, 32-bit): 150 * - %eax is the syscall number. 151 * - %eax is also the return value. 152 * - %ebx is the 1st argument. 153 * - %ecx is the 2nd argument. 154 * - %edx is the 3rd argument. 155 * - %esi is the 4th argument. 156 * - %edi is the 5th argument. 157 * - %ebp is the 6th argument. 158 */ 159 160 #define __do_syscall0(NUM) ({ \ 161 intptr_t eax; \ 162 \ 163 __asm__ volatile( \ 164 "int $0x80" \ 165 : "=a"(eax) /* %eax */ \ 166 : "a"(NUM) /* %eax */ \ 167 : "memory" \ 168 ); \ 169 eax; \ 170 }) 171 172 #define __do_syscall1(NUM, ARG1) ({ \ 173 intptr_t eax; \ 174 \ 175 __asm__ volatile( \ 176 "int $0x80" \ 177 : "=a"(eax) /* %eax */ \ 178 : "a"(NUM), /* %eax */ \ 179 "b"((ARG1)) /* %ebx */ \ 180 : "memory" \ 181 ); \ 182 eax; \ 183 }) 184 185 #define __do_syscall2(NUM, ARG1, ARG2) ({ \ 186 intptr_t eax; \ 187 \ 188 __asm__ volatile( \ 189 "int $0x80" \ 190 : "=a" (eax) /* %eax */ \ 191 : "a"(NUM), /* %eax */ \ 192 "b"((ARG1)), /* %ebx */ \ 193 "c"((ARG2)) /* %ecx */ \ 194 : "memory" \ 195 ); \ 196 eax; \ 197 }) 198 199 #define __do_syscall3(NUM, ARG1, ARG2, ARG3) ({ \ 200 intptr_t eax; \ 201 \ 202 __asm__ volatile( \ 203 "int $0x80" \ 204 : "=a" (eax) /* %eax */ \ 205 : "a"(NUM), /* %eax */ \ 206 "b"((ARG1)), /* %ebx */ \ 207 "c"((ARG2)), /* %ecx */ \ 208 "d"((ARG3)) /* %edx */ \ 209 : "memory" \ 210 ); \ 211 eax; \ 212 }) 213 214 #define __do_syscall4(NUM, ARG1, ARG2, ARG3, ARG4) ({ \ 215 intptr_t eax; \ 216 \ 217 __asm__ volatile( \ 218 "int $0x80" \ 219 : "=a" (eax) /* %eax */ \ 220 : "a"(NUM), /* %eax */ \ 221 "b"((ARG1)), /* %ebx */ \ 222 "c"((ARG2)), /* %ecx */ \ 223 "d"((ARG3)), /* %edx */ \ 224 "S"((ARG4)) /* %esi */ \ 225 : "memory" \ 226 ); \ 227 eax; \ 228 }) 229 230 #define __do_syscall5(NUM, ARG1, ARG2, ARG3, ARG4, ARG5) ({ \ 231 intptr_t eax; \ 232 \ 233 __asm__ volatile( \ 234 "int $0x80" \ 235 : "=a" (eax) /* %eax */ \ 236 : "a"(NUM), /* %eax */ \ 237 "b"((ARG1)), /* %ebx */ \ 238 "c"((ARG2)), /* %ecx */ \ 239 "d"((ARG3)), /* %edx */ \ 240 "S"((ARG4)), /* %esi */ \ 241 "D"((ARG5)) /* %edi */ \ 242 : "memory" \ 243 ); \ 244 eax; \ 245 }) 246 247 248 /* 249 * On i386, the 6th argument of syscall goes in %ebp. However, both Clang 250 * and GCC cannot use %ebp in the clobber list and in the "r" constraint 251 * without using -fomit-frame-pointer. To make it always available for 252 * any kind of compilation, the below workaround is implemented: 253 * 254 * 1) Push the 6-th argument. 255 * 2) Push %ebp. 256 * 3) Load the 6-th argument from 4(%esp) to %ebp. 257 * 4) Do the syscall (int $0x80). 258 * 5) Pop %ebp (restore the old value of %ebp). 259 * 6) Add %esp by 4 (undo the stack pointer). 260 * 261 * WARNING: 262 * Don't use register variables for __do_syscall6(), there is a known 263 * GCC bug that results in an endless loop. 264 * 265 * BugLink: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105032 266 * 267 */ 268 #define __do_syscall6(NUM, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6) ({ \ 269 intptr_t eax = (intptr_t)(NUM); \ 270 intptr_t arg6 = (intptr_t)(ARG6); /* Always in memory */ \ 271 __asm__ volatile ( \ 272 "pushl %[_arg6]\n\t" \ 273 "pushl %%ebp\n\t" \ 274 "movl 4(%%esp),%%ebp\n\t" \ 275 "int $0x80\n\t" \ 276 "popl %%ebp\n\t" \ 277 "addl $4,%%esp" \ 278 : "+a"(eax) /* %eax */ \ 279 : "b"(ARG1), /* %ebx */ \ 280 "c"(ARG2), /* %ecx */ \ 281 "d"(ARG3), /* %edx */ \ 282 "S"(ARG4), /* %esi */ \ 283 "D"(ARG5), /* %edi */ \ 284 [_arg6]"m"(arg6) /* memory */ \ 285 : "memory", "cc" \ 286 ); \ 287 eax; \ 288 }) 289 290 #include "../syscall-defs.h" 291 292 #else /* #ifdef CONFIG_NOLIBC */ 293 294 #include "../generic/syscall.h" 295 296 #endif /* #ifdef CONFIG_NOLIBC */ 297 298 #endif /* #if defined(__x86_64__) */ 299 300 #endif /* #ifndef LIBURING_ARCH_X86_SYSCALL_H */ 301