2 * Copyright 2012, Alex Smith, alex@alex-smith.me.uk.
3 * Distributed under the terms of the MIT License.
9 #include <thread_types.h>
11 #include <arch/x86/descriptors.h>
12 #include <arch/x86/arch_cpu.h>
13 #include <arch/x86/arch_kernel.h>
15 #include "asm_offsets.h"
16 #include "syscall_numbers.h"
17 #include "syscall_table.h"
20 // Push the remainder of the interrupt frame onto the stack.
21 #define PUSH_IFRAME_BOTTOM(iframeType) \
22 push %rax; /* orig_rax */ \
42 // Restore the interrupt frame.
43 #define RESTORE_IFRAME() \
63 // The macros below require R12 to contain the current thread pointer. R12 is
64 // callee-save so will be preserved through all function calls and only needs
65 // to be obtained once. R13 is used to store the system call start time, will
68 #define LOCK_THREAD_TIME() \
69 leaq THREAD_time_lock(%r12), %rdi; \
70 call acquire_spinlock;
72 #define UNLOCK_THREAD_TIME() \
73 leaq THREAD_time_lock(%r12), %rdi; \
74 call release_spinlock; \
76 #define UPDATE_THREAD_USER_TIME() \
81 /* Preserve system_time for post syscall debug */ \
84 /* thread->user_time += now - thread->last_time; */ \
85 subq THREAD_last_time(%r12), %rax; \
86 addq %rax, THREAD_user_time(%r12); \
88 /* thread->last_time = now; */ \
89 movq %r13, THREAD_last_time(%r12); \
91 /* thread->in_kernel = true; */ \
92 movb $1, THREAD_in_kernel(%r12); \
96 #define UPDATE_THREAD_KERNEL_TIME() \
102 /* thread->kernel_time += now - thread->last_time; */ \
103 subq THREAD_last_time(%r12), %rax; \
104 addq %rax, THREAD_kernel_time(%r12); \
106 /* thread->last_time = now; */ \
107 movq %r13, THREAD_last_time(%r12); \
109 /* thread->in_kernel = false; */ \
110 movb $0, THREAD_in_kernel(%r12); \
114 #define STOP_USER_DEBUGGING() \
115 testl $(THREAD_FLAGS_BREAKPOINTS_INSTALLED \
116 | THREAD_FLAGS_SINGLE_STEP), THREAD_flags(%r12); \
118 call x86_exit_user_debug_at_kernel_entry; \
121 #define CLEAR_FPU_STATE() \
132 pxor %xmm10, %xmm10; \
133 pxor %xmm11, %xmm11; \
134 pxor %xmm12, %xmm12; \
135 pxor %xmm13, %xmm13; \
136 pxor %xmm14, %xmm14; \
139 // The following code defines the interrupt service routines for all 256
140 // interrupts. It creates a block of handlers, each 16 bytes, that the IDT
141 // initialization code just loops through.
143 // Interrupt with no error code, pushes a 0 error code.
144 #define DEFINE_ISR(nr) \
150 // Interrupt with an error code.
151 #define DEFINE_ISR_E(nr) \
156 // Array of interrupt service routines.
159 // Exceptions (0-19) and reserved interrupts (20-31).
193 // User-defined ISRs (32-255) - none take an error code.
201 // Common interrupt handling code.
202 STATIC_FUNCTION(int_bottom):
203 // Coming from user-mode requires special handling.
207 // Push the rest of the interrupt frame to the stack.
208 PUSH_IFRAME_BOTTOM(IFRAME_TYPE_OTHER)
212 // Frame pointer is the iframe.
215 // Set the RF (resume flag) in RFLAGS. This prevents an instruction
216 // breakpoint on the instruction we're returning to to trigger a debug
218 orq $X86_EFLAGS_RESUME, IFRAME_flags(%rbp)
224 // Call the interrupt handler.
226 movq IFRAME_vector(%rbp), %rax
227 call *gInterruptHandlerTable(, %rax, 8)
232 // Restore the saved registers.
236 FUNCTION_END(int_bottom)
239 // Handler for an interrupt that occurred in user-mode.
240 STATIC_FUNCTION(int_bottom_user):
241 // Load the kerrnel GS segment base.
244 // Push the rest of the interrupt frame to the stack.
245 PUSH_IFRAME_BOTTOM(IFRAME_TYPE_OTHER)
248 // Frame pointer is the iframe.
254 movq %rsp, IFRAME_fpu(%rbp)
256 // Set the RF (resume flag) in RFLAGS. This prevents an instruction
257 // breakpoint on the instruction we're returning to to trigger a debug
259 orq $X86_EFLAGS_RESUME, IFRAME_flags(%rbp)
261 // Get thread pointer.
264 STOP_USER_DEBUGGING()
265 UPDATE_THREAD_USER_TIME()
267 // Call the interrupt handler.
269 movq IFRAME_vector(%rbp), %rax
270 call *gInterruptHandlerTable(, %rax, 8)
272 // If there are no signals pending or we're not debugging, we can avoid
273 // most of the work here, just need to update the kernel time.
274 testl $(THREAD_FLAGS_DEBUGGER_INSTALLED | THREAD_FLAGS_SIGNALS_PENDING \
275 | THREAD_FLAGS_DEBUG_THREAD | THREAD_FLAGS_BREAKPOINTS_DEFINED \
276 | THREAD_FLAGS_TRAP_FOR_CORE_DUMP) \
278 jnz .Lkernel_exit_work
282 UPDATE_THREAD_KERNEL_TIME()
287 // Restore the saved registers.
290 // Restore the previous GS base and return.
295 // Slow path for return to userland.
297 // Do we need to handle signals?
298 testl $(THREAD_FLAGS_SIGNALS_PENDING | THREAD_FLAGS_DEBUG_THREAD \
299 | THREAD_FLAGS_TRAP_FOR_CORE_DUMP) \
301 jnz .Lkernel_exit_handle_signals
303 call thread_at_kernel_exit_no_signals
305 .Lkernel_exit_work_done:
306 // Install breakpoints, if defined.
307 testl $THREAD_FLAGS_BREAKPOINTS_DEFINED, THREAD_flags(%r12)
310 call x86_init_user_debug_at_kernel_exit
315 // Restore the saved registers.
318 // Restore the previous GS base and return.
322 .Lkernel_exit_handle_signals:
323 // thread_at_kernel_exit requires interrupts to be enabled, it will disable
326 call thread_at_kernel_exit
327 jmp .Lkernel_exit_work_done
328 FUNCTION_END(int_bottom_user)
331 // SYSCALL entry point.
332 FUNCTION(x86_64_syscall_entry):
333 // Upon entry, RSP still points at the user stack. Load the kernel GS
334 // segment base address, which points at the current thread's arch_thread
335 // structure. This contains our kernel stack pointer and a temporary
336 // scratch space to store the user stack pointer in before we can push it
339 movq %rsp, %gs:ARCH_THREAD_user_rsp
340 movq %gs:ARCH_THREAD_syscall_rsp, %rsp
342 // Set up an iframe on the stack (R11 = saved RFLAGS, RCX = saved RIP).
343 push $USER_DATA_SELECTOR // ss
344 push %gs:ARCH_THREAD_user_rsp // rsp
346 push $USER_CODE_SELECTOR // cs
348 push $0 // error_code
350 PUSH_IFRAME_BOTTOM(IFRAME_TYPE_SYSCALL)
354 // Frame pointer is the iframe.
358 // Preserve call number (R14 is callee-save), get thread pointer.
362 STOP_USER_DEBUGGING()
363 UPDATE_THREAD_USER_TIME()
365 // No longer need interrupts disabled.
368 // Check whether the syscall number is valid.
369 cmpq $SYSCALL_COUNT, %r14
372 // Get the system call table entry. Note I'm hardcoding the shift because
373 // sizeof(syscall_info) is 16 and scale factors of 16 aren't supported,
374 // so can't just do leaq kSyscallInfos(, %rax, SYSCALL_INFO_sizeof).
377 leaq kSyscallInfos(, %rax, 1), %rax
379 // Check the number of call arguments, greater than 6 (6 * 8 = 48) requires
381 movq SYSCALL_INFO_parameter_size(%rax), %rcx
383 ja .Lsyscall_stack_args
386 testl $THREAD_FLAGS_DEBUGGER_INSTALLED, THREAD_flags(%r12)
387 jnz .Lpre_syscall_debug
389 .Lpre_syscall_debug_done:
390 // Restore the arguments from the iframe. UPDATE_THREAD_USER_TIME() makes
391 // 2 function calls which means they may have been overwritten. Note that
392 // argument 4 is in R10 on the frame rather than RCX as RCX is used by
394 movq IFRAME_di(%rbp), %rdi
395 movq IFRAME_si(%rbp), %rsi
396 movq IFRAME_dx(%rbp), %rdx
397 movq IFRAME_r10(%rbp), %rcx
398 movq IFRAME_r8(%rbp), %r8
399 movq IFRAME_r9(%rbp), %r9
401 // TODO: pre-syscall tracing
403 // Call the function and save its return value.
404 call *SYSCALL_INFO_function(%rax)
405 movq %rax, IFRAME_ax(%rbp)
407 // TODO: post-syscall tracing
410 // Restore the original stack pointer and return.
413 // Clear the restarted flag.
414 testl $THREAD_FLAGS_SYSCALL_RESTARTED, THREAD_flags(%r12)
417 movl THREAD_flags(%r12), %eax
419 andl $~THREAD_FLAGS_SYSCALL_RESTARTED, %edx
421 cmpxchgl %edx, THREAD_flags(%r12)
424 testl $(THREAD_FLAGS_DEBUGGER_INSTALLED | THREAD_FLAGS_SIGNALS_PENDING \
425 | THREAD_FLAGS_DEBUG_THREAD | THREAD_FLAGS_BREAKPOINTS_DEFINED \
426 | THREAD_FLAGS_TRAP_FOR_CORE_DUMP | THREAD_FLAGS_RESTART_SYSCALL) \
428 jnz .Lpost_syscall_work
432 UPDATE_THREAD_KERNEL_TIME()
434 // If we've just restored a signal frame, use the IRET path.
435 cmpq $SYSCALL_RESTORE_SIGNAL_FRAME, %r14
440 // Restore the iframe and RCX/R11 for SYSRET.
447 // Restore previous GS base and return.
452 // user_debug_pre_syscall expects a pointer to a block of arguments, need
453 // to push the register arguments onto the stack.
456 push IFRAME_r10(%rbp)
460 movq %r14, %rdi // syscall number
463 call user_debug_pre_syscall
466 jmp .Lpre_syscall_debug_done
469 testl $THREAD_FLAGS_DEBUGGER_INSTALLED, THREAD_flags(%r12)
472 // Post-syscall debugging. Same as above, need a block of arguments.
475 push IFRAME_r10(%rbp)
479 movq %r14, %rdi // syscall number
481 movq IFRAME_ax(%rbp), %rdx // return value
482 movq %r13, %rcx // start time, preserved earlier
483 call user_debug_post_syscall
486 // Do we need to handle signals?
487 testl $(THREAD_FLAGS_SIGNALS_PENDING | THREAD_FLAGS_DEBUG_THREAD \
488 | THREAD_FLAGS_TRAP_FOR_CORE_DUMP) \
490 jnz .Lpost_syscall_handle_signals
492 call thread_at_kernel_exit_no_signals
494 .Lpost_syscall_work_done:
495 // Handle syscall restarting.
496 testl $THREAD_FLAGS_RESTART_SYSCALL, THREAD_flags(%r12)
499 call x86_restart_syscall
501 // Install breakpoints, if defined.
502 testl $THREAD_FLAGS_BREAKPOINTS_DEFINED, THREAD_flags(%r12)
505 call x86_init_user_debug_at_kernel_exit
507 // On this return path it is possible that the frame has been modified,
508 // for example to execute a signal handler. In this case it is safer to
514 movq IFRAME_fpu(%rbp), %rax
517 // Restore the saved registers.
520 // Restore the previous GS base and return.
524 .Lpost_syscall_handle_signals:
525 call thread_at_kernel_exit
526 jmp .Lpost_syscall_work_done
528 .Lsyscall_stack_args:
529 // Some arguments are on the stack, work out what we need to copy. 6
530 // arguments (48 bytes) are already in registers.
531 // RAX = syscall table entry address, RCX = argument size.
534 // Get the address to copy from.
535 movq IFRAME_user_sp(%rbp), %rsi
537 movabs $(USER_BASE + USER_SIZE), %rdx
539 jae .Lbad_syscall_args
541 // Make space on the stack.
546 // Set a fault handler.
547 movq $.Lbad_syscall_args, THREAD_fault_handler(%r12)
549 // Copy them by quadwords.
553 movq $0, THREAD_fault_handler(%r12)
556 jmp .Lperform_syscall
559 movq $0, THREAD_fault_handler(%r12)
562 FUNCTION_END(x86_64_syscall_entry)
565 /*! \fn void x86_return_to_userland(iframe* frame)
566 \brief Returns to the userland environment given by \a frame.
568 Before returning to userland all potentially necessary kernel exit work is
571 \a frame must point to a location somewhere on the caller's stack (e.g. a
573 The function must be called with interrupts disabled.
575 \param frame The iframe defining the userland environment.
577 FUNCTION(x86_return_to_userland):
581 // Perform kernel exit work.
583 testl $(THREAD_FLAGS_DEBUGGER_INSTALLED | THREAD_FLAGS_SIGNALS_PENDING \
584 | THREAD_FLAGS_DEBUG_THREAD | THREAD_FLAGS_BREAKPOINTS_DEFINED \
585 | THREAD_FLAGS_TRAP_FOR_CORE_DUMP) \
587 jnz .Luserland_return_work
589 // update the thread's kernel time and return
590 UPDATE_THREAD_KERNEL_TIME()
592 // Restore the frame and return.
596 .Luserland_return_work:
597 // Slow path for return to userland.
599 // Do we need to handle signals?
600 testl $(THREAD_FLAGS_SIGNALS_PENDING | THREAD_FLAGS_DEBUG_THREAD \
601 | THREAD_FLAGS_TRAP_FOR_CORE_DUMP) \
603 jnz .Luserland_return_handle_signals
605 call thread_at_kernel_exit_no_signals
607 .Luserland_return_work_done:
608 // Install breakpoints, if defined.
609 testl $THREAD_FLAGS_BREAKPOINTS_DEFINED, THREAD_flags(%r12)
612 call x86_init_user_debug_at_kernel_exit
614 // Restore the saved registers.
617 // Restore the previous GS base and return.
620 .Luserland_return_handle_signals:
621 // thread_at_kernel_exit requires interrupts to be enabled, it will disable
624 call thread_at_kernel_exit
625 jmp .Luserland_return_work_done
626 FUNCTION_END(x86_return_to_userland)