1/* 2 * Copyright 2018, Jérôme Duval, jerome.duval@gmail.com. 3 * Copyright 2012, Alex Smith, alex@alex-smith.me.uk. 4 * Distributed under the terms of the MIT License. 5 */ 6 7 8#include <asm_defs.h> 9 10#include <thread_types.h> 11 12#include <arch/x86/descriptors.h> 13#include <arch/x86/arch_altcodepatch.h> 14#include <arch/x86/arch_cpu.h> 15#include <arch/x86/arch_kernel.h> 16#define COMMPAGE_COMPAT 17#include <commpage_defs.h> 18 19#include "asm_offsets.h" 20#include "syscall_numbers.h" 21#include "syscall_table.h" 22 23 24// Push the remainder of the interrupt frame onto the stack. 25#define PUSH_IFRAME_BOTTOM(iframeType) \ 26 push %rax; /* orig_rax */ \ 27 push %rax; \ 28 push %rbx; \ 29 push %rcx; \ 30 push %rdx; \ 31 push %rdi; \ 32 push %rsi; \ 33 push %rbp; \ 34 push %r8; \ 35 push %r9; \ 36 push %r10; \ 37 push %r11; \ 38 push %r12; \ 39 push %r13; \ 40 push %r14; \ 41 push %r15; \ 42 pushq $0; \ 43 push $iframeType; 44 45 46// Restore the interrupt frame. 47#define RESTORE_IFRAME() \ 48 add $16, %rsp; \ 49 pop %r15; \ 50 pop %r14; \ 51 pop %r13; \ 52 pop %r12; \ 53 pop %r11; \ 54 pop %r10; \ 55 pop %r9; \ 56 pop %r8; \ 57 pop %rbp; \ 58 pop %rsi; \ 59 pop %rdi; \ 60 pop %rdx; \ 61 pop %rcx; \ 62 pop %rbx; \ 63 pop %rax; \ 64 addq $24, %rsp; 65 66 67// The macros below require R12 to contain the current thread pointer. R12 is 68// callee-save so will be preserved through all function calls and only needs 69// to be obtained once. R13 is used to store the system call start time, will 70// also be preserved. 71 72#define LOCK_THREAD_TIME() \ 73 leaq THREAD_time_lock(%r12), %rdi; \ 74 call acquire_spinlock; 75 76#define UNLOCK_THREAD_TIME() \ 77 leaq THREAD_time_lock(%r12), %rdi; \ 78 call release_spinlock; \ 79 80#define UPDATE_THREAD_USER_TIME() \ 81 LOCK_THREAD_TIME() \ 82 \ 83 call system_time; \ 84 \ 85 /* Preserve system_time for post syscall debug */ \ 86 movq %rax, %r13; \ 87 \ 88 /* thread->user_time += now - thread->last_time; */ \ 89 subq THREAD_last_time(%r12), %rax; \ 90 addq %rax, THREAD_user_time(%r12); \ 91 \ 92 /* thread->last_time = now; */ \ 93 movq %r13, THREAD_last_time(%r12); \ 94 \ 95 /* thread->in_kernel = true; */ \ 96 movb $1, THREAD_in_kernel(%r12); \ 97 \ 98 UNLOCK_THREAD_TIME() 99 100#define UPDATE_THREAD_KERNEL_TIME() \ 101 LOCK_THREAD_TIME() \ 102 \ 103 call system_time; \ 104 movq %rax, %r13; \ 105 \ 106 /* thread->kernel_time += now - thread->last_time; */ \ 107 subq THREAD_last_time(%r12), %rax; \ 108 addq %rax, THREAD_kernel_time(%r12); \ 109 \ 110 /* thread->last_time = now; */ \ 111 movq %r13, THREAD_last_time(%r12); \ 112 \ 113 /* thread->in_kernel = false; */ \ 114 movb $0, THREAD_in_kernel(%r12); \ 115 \ 116 UNLOCK_THREAD_TIME() 117 118#define STOP_USER_DEBUGGING() \ 119 testl $(THREAD_FLAGS_BREAKPOINTS_INSTALLED \ 120 | THREAD_FLAGS_SINGLE_STEP), THREAD_flags(%r12); \ 121 jz 1f; \ 122 call x86_exit_user_debug_at_kernel_entry; \ 123 1: 124 125#define CLEAR_FPU_STATE() \ 126 pxor %xmm0, %xmm0; \ 127 pxor %xmm1, %xmm1; \ 128 pxor %xmm2, %xmm2; \ 129 pxor %xmm3, %xmm3; \ 130 pxor %xmm4, %xmm4; \ 131 pxor %xmm5, %xmm5; \ 132 pxor %xmm6, %xmm6; \ 133 pxor %xmm7, %xmm7; \ 134 pxor %xmm8, %xmm8; \ 135 pxor %xmm9, %xmm9; \ 136 pxor %xmm10, %xmm10; \ 137 pxor %xmm11, %xmm11; \ 138 pxor %xmm12, %xmm12; \ 139 pxor %xmm13, %xmm13; \ 140 pxor %xmm14, %xmm14; \ 141 pxor %xmm15, %xmm15 142 143 144// SYSCALL entry point. 145FUNCTION(x86_64_syscall32_entry): 146 // TODO: implement for AMD SYSCALL 147 sysret 148FUNCTION_END(x86_64_syscall32_entry) 149 150 151// SYSENTER entry point. 152// ecx - user esp 153FUNCTION(x86_64_sysenter32_entry): 154 swapgs 155 156 // Set up an iframe on the stack (ECX = saved ESP). 157 push $USER_DATA_SELECTOR // ss 158 // zero extend %ecx 159 movl %ecx, %ecx 160 push %rcx // rsp 161 pushfq // flags 162 orl $(1 << 9), (%rsp) // set the IF (interrupts) bit 163 push $USER32_CODE_SELECTOR // cs 164 165 movq %gs:0, %rdx 166 movq THREAD_team(%rdx), %rdx 167 movq TEAM_commpage_address(%rdx), %rdx 168 ASM_STAC 169 add 4 * COMMPAGE_ENTRY_X86_SYSCALL(%rdx), %rdx 170 ASM_CLAC 171 add $4, %rdx // sysenter is at offset 2, 2 bytes long 172 push %rdx // ip 173 174 push $0 // error_code 175 push $99 // vector 176 PUSH_IFRAME_BOTTOM(IFRAME_TYPE_SYSCALL) 177 178 cld 179 180 // Frame pointer is the iframe. 181 movq %rsp, %rbp 182 andq $~15, %rsp 183 184 // Preserve call number (R14 is callee-save), get thread pointer. 185 movq %rax, %r14 186 movq %gs:0, %r12 187 188 STOP_USER_DEBUGGING() 189 UPDATE_THREAD_USER_TIME() 190 191 // No longer need interrupts disabled. 192 sti 193 194 // Check whether the syscall number is valid. 195 cmpq $SYSCALL_COUNT, %r14 196 jae .Lsyscall_return 197 198 // Get the system call table entry. Note I'm hardcoding the shift because 199 // sizeof(syscall_info) is 16 and scale factors of 16 aren't supported, 200 // so can't just do leaq kSyscallInfos(, %rax, SYSCALL_INFO_sizeof). 201 movq %r14, %rax 202 shlq $4, %rax 203 leaq kSyscallCompatInfos(, %rax, 1), %rax 204 205 // Restore the arguments from the stack. 206 movq SYSCALL_INFO_parameter_size(%rax), %rcx 207 208 // Get the address to copy from. 209 movq IFRAME_user_sp(%rbp), %rsi 210 addq $4, %rsi 211 movabs $(USER_BASE + USER_SIZE), %rdx 212 cmp %rdx, %rsi 213 jae .Lbad_syscall_args 214 215 // Make space on the stack for the double size. 216 shlq $1, %rcx 217 cmpq $48, %rcx 218 ja .Lprepare_stack 219 movq $48, %rcx 220.Lprepare_stack: 221 subq %rcx, %rsp 222 andq $~15, %rsp 223 movq %rsp, %rdi 224 225 // Get the extended system call table entry. 226 movq %r14, %r15 227 imulq $ EXTENDED_SYSCALL_INFO_sizeof, %r15 228 leaq kExtendedSyscallCompatInfos(, %r15, 1), %r15 229 xor %rcx, %rcx 230 movl EXTENDED_SYSCALL_INFO_parameter_count(%r15), %ecx 231 leaq EXTENDED_SYSCALL_INFO_parameters(%r15), %r15 232 233 // Set a fault handler. 234 movq $.Lbad_syscall_args, THREAD_fault_handler(%r12) 235 236 ASM_STAC 237 238 jmp 2f 239 // Copy them by doublewords. 2401: 241 // Advance to next parameter 242 addq $ SYSCALL_PARAMETER_INFO_sizeof, %r15 243 subq $1, %rcx 2442: 245 cmpq $0, %rcx 246 je 4f 247 movsd 248 cmpl $0x8, SYSCALL_PARAMETER_INFO_used_size(%r15) 249 je 3f 250 movl $0, (%rdi) 251 addq $4, %rdi 252 jmp 1b 2533: 254 // Copy the next doubleword 255 movsd 256 jmp 1b 2574: 258 ASM_CLAC 259 movq $0, THREAD_fault_handler(%r12) 260 261.Lperform_syscall: 262 testl $THREAD_FLAGS_DEBUGGER_INSTALLED, THREAD_flags(%r12) 263 jnz .Lpre_syscall_debug 264 265.Lpre_syscall_debug_done: 266 // arguments on the stack, copy in the registers 267 pop %rdi 268 pop %rsi 269 pop %rdx 270 pop %rcx 271 pop %r8 272 pop %r9 273 274 // TODO: pre-syscall tracing 275 276 // Call the function and save its return value. 277 call *SYSCALL_INFO_function(%rax) 278 movq %rax, %rdx 279 movq %rax, IFRAME_ax(%rbp) 280 shrq $32, %rdx 281 movq %rdx, IFRAME_dx(%rbp) 282 283 // TODO: post-syscall tracing 284 285.Lsyscall_return: 286 // Restore the original stack pointer and return. 287 movq %rbp, %rsp 288 289 testl $(THREAD_FLAGS_DEBUGGER_INSTALLED | THREAD_FLAGS_SIGNALS_PENDING \ 290 | THREAD_FLAGS_DEBUG_THREAD | THREAD_FLAGS_BREAKPOINTS_DEFINED \ 291 | THREAD_FLAGS_TRAP_FOR_CORE_DUMP \ 292 | THREAD_FLAGS_64_BIT_SYSCALL_RETURN \ 293 | THREAD_FLAGS_RESTART_SYSCALL | THREAD_FLAGS_SYSCALL_RESTARTED) \ 294 , THREAD_flags(%r12) 295 jnz .Lpost_syscall_work 296 297 cli 298 299 UPDATE_THREAD_KERNEL_TIME() 300 301 // If we've just restored a signal frame, use the IRET path. 302 cmpq $SYSCALL_RESTORE_SIGNAL_FRAME, %r14 303 je .Lrestore_fpu 304 305 CLEAR_FPU_STATE() 306 307 // Restore the iframe and RCX/RDX for SYSRET. 308 RESTORE_IFRAME() 309 pop %rdx 310 addq $8, %rsp 311 andl $~0x200,(%rsp) 312 popfq 313 pop %rcx 314 315 // Restore previous GS base and return. 316 swapgs 317 sti 318 sysexit 319 320 321.Lpre_syscall_debug: 322 // preserve registers 323 push %rdi 324 push %rsi 325 326 // user_debug_pre_syscall expects a pointer to a block of arguments, need 327 // to push the register arguments onto the stack. 328 movq %r14, %rdi // syscall number 329 movq 0x10(%rsp), %rsi 330 push %rax 331 call user_debug_pre_syscall 332 pop %rax 333 334 // restore registers 335 pop %rsi 336 pop %rdi 337 jmp .Lpre_syscall_debug_done 338 339.Lpost_syscall_work: 340 // Clear the restarted flag. 341 testl $(THREAD_FLAGS_64_BIT_SYSCALL_RETURN \ 342 | THREAD_FLAGS_SYSCALL_RESTARTED), THREAD_flags(%r12) 343 jz 2f 3441: 345 movl THREAD_flags(%r12), %eax 346 movl %eax, %edx 347 andl $~(THREAD_FLAGS_64_BIT_SYSCALL_RETURN \ 348 | THREAD_FLAGS_SYSCALL_RESTARTED), %edx 349 lock 350 cmpxchgl %edx, THREAD_flags(%r12) 351 jnz 1b 3522: 353 testl $THREAD_FLAGS_DEBUGGER_INSTALLED, THREAD_flags(%r12) 354 jz 1f 355 356 // Post-syscall debugging. Same as above, need a block of arguments. 357 // TODO: restore arguments from the stack 358 push IFRAME_r9(%rbp) 359 push IFRAME_r8(%rbp) 360 push IFRAME_r10(%rbp) 361 push IFRAME_dx(%rbp) 362 push IFRAME_si(%rbp) 363 push IFRAME_di(%rbp) 364 movq %r14, %rdi // syscall number 365 movq %rsp, %rsi 366 movq IFRAME_ax(%rbp), %rdx // return value 367 movq %r13, %rcx // start time, preserved earlier 368 call user_debug_post_syscall 369 addq $48, %rsp 3701: 371 // Do we need to handle signals? 372 testl $(THREAD_FLAGS_SIGNALS_PENDING | THREAD_FLAGS_DEBUG_THREAD \ 373 | THREAD_FLAGS_TRAP_FOR_CORE_DUMP) \ 374 , THREAD_flags(%r12) 375 jnz .Lpost_syscall_handle_signals 376 cli 377 call thread_at_kernel_exit_no_signals 378 379.Lpost_syscall_work_done: 380 // Handle syscall restarting. 381 testl $THREAD_FLAGS_RESTART_SYSCALL, THREAD_flags(%r12) 382 jz 1f 383 movq %rsp, %rdi 384 call x86_restart_syscall 3851: 386 // Install breakpoints, if defined. 387 testl $THREAD_FLAGS_BREAKPOINTS_DEFINED, THREAD_flags(%r12) 388 jz 1f 389 movq %rbp, %rdi 390 call x86_init_user_debug_at_kernel_exit 3911: 392 // On this return path it is possible that the frame has been modified, 393 // for example to execute a signal handler. In this case it is safer to 394 // return via IRET. 395 CLEAR_FPU_STATE() 396 jmp .Liret 397 398.Lrestore_fpu: 399 movq IFRAME_fpu(%rbp), %rax 400 fxrstorq (%rax) 401.Liret: 402 // Restore the saved registers. 403 RESTORE_IFRAME() 404 405 // Restore the previous GS base and return. 406 swapgs 407 iretq 408 409.Lpost_syscall_handle_signals: 410 call thread_at_kernel_exit 411 jmp .Lpost_syscall_work_done 412 413.Lbad_syscall_args: 414 movq $0, THREAD_fault_handler(%r12) 415 movq %rbp, %rsp 416 jmp .Lsyscall_return 417FUNCTION_END(x86_64_sysenter32_entry) 418 419 420/* thread exit stub */ 421// TODO: build with the x86 compiler 422FUNCTION(x86_sysenter32_userspace_thread_exit): 423 .byte 0x50 // push %eax 424 mov $SYSCALL_EXIT_THREAD, %eax 425 .byte 0x89,0xe1 // mov %esp, %ecx 426 sysenter 427FUNCTION_END(x86_sysenter32_userspace_thread_exit) 428SYMBOL(x86_sysenter32_userspace_thread_exit_end): 429 430