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