#include <asm_defs.h>
#include <thread_types.h>
#include <arch/x86/descriptors.h>
#include <arch/x86/arch_altcodepatch.h>
#include <arch/x86/arch_cpu.h>
#include <arch/x86/arch_kernel.h>
#include "asm_offsets.h"
#include "syscall_numbers.h"
#include "syscall_table.h"
#define PUSH_IFRAME_BOTTOM(iframeType) \
push %rax; \
push %rax; \
push %rbx; \
push %rcx; \
push %rdx; \
push %rdi; \
push %rsi; \
push %rbp; \
push %r8; \
push %r9; \
push %r10; \
push %r11; \
push %r12; \
push %r13; \
push %r14; \
push %r15; \
pushq $0; \
push $iframeType;
#define RESTORE_IFRAME() \
add $16, %rsp; \
pop %r15; \
pop %r14; \
pop %r13; \
pop %r12; \
pop %r11; \
pop %r10; \
pop %r9; \
pop %r8; \
pop %rbp; \
pop %rsi; \
pop %rdi; \
pop %rdx; \
pop %rcx; \
pop %rbx; \
pop %rax; \
addq $24, %rsp;
#define LOCK_THREAD_TIME() \
leaq THREAD_time_lock(%r12), %rdi; \
call acquire_spinlock;
#define UNLOCK_THREAD_TIME() \
leaq THREAD_time_lock(%r12), %rdi; \
call release_spinlock; \
#define UPDATE_THREAD_USER_TIME() \
LOCK_THREAD_TIME() \
\
call system_time; \
movq %rax, %r13; \
\
\
subq THREAD_last_time(%r12), %rax; \
addq %rax, THREAD_user_time(%r12); \
\
\
movq %r13, THREAD_last_time(%r12); \
\
\
movb $1, THREAD_in_kernel(%r12); \
\
UNLOCK_THREAD_TIME()
#define UPDATE_THREAD_KERNEL_TIME() \
LOCK_THREAD_TIME() \
\
call system_time; \
movq %rax, %r13; \
\
\
subq THREAD_last_time(%r12), %rax; \
addq %rax, THREAD_kernel_time(%r12); \
\
\
movq %r13, THREAD_last_time(%r12); \
\
\
movb $0, THREAD_in_kernel(%r12); \
\
UNLOCK_THREAD_TIME()
#define STOP_USER_DEBUGGING() \
testl $(THREAD_FLAGS_BREAKPOINTS_INSTALLED \
| THREAD_FLAGS_SINGLE_STEP), THREAD_flags(%r12); \
jz 1f; \
call x86_exit_user_debug_at_kernel_entry; \
1:
#define CLEAR_FPU_STATE() \
pxor %xmm0, %xmm0; \
pxor %xmm1, %xmm1; \
pxor %xmm2, %xmm2; \
pxor %xmm3, %xmm3; \
pxor %xmm4, %xmm4; \
pxor %xmm5, %xmm5; \
pxor %xmm6, %xmm6; \
pxor %xmm7, %xmm7; \
pxor %xmm8, %xmm8; \
pxor %xmm9, %xmm9; \
pxor %xmm10, %xmm10; \
pxor %xmm11, %xmm11; \
pxor %xmm12, %xmm12; \
pxor %xmm13, %xmm13; \
pxor %xmm14, %xmm14; \
pxor %xmm15, %xmm15
#define DEFINE_ISR(nr) \
.align 16; \
ASM_CLAC \
push $0; \
push $nr; \
jmp intr_bottom;
#define DEFINE_ISR_E(nr) \
.align 16; \
ASM_CLAC \
push $nr; \
jmp intr_bottom;
.align 16
SYMBOL(isr_array):
DEFINE_ISR(0)
DEFINE_ISR(1)
DEFINE_ISR(2)
DEFINE_ISR(3)
DEFINE_ISR(4)
DEFINE_ISR(5)
DEFINE_ISR(6)
DEFINE_ISR(7)
DEFINE_ISR_E(8)
DEFINE_ISR(9)
DEFINE_ISR_E(10)
DEFINE_ISR_E(11)
DEFINE_ISR_E(12)
DEFINE_ISR_E(13)
DEFINE_ISR_E(14)
DEFINE_ISR(15)
DEFINE_ISR(16)
DEFINE_ISR_E(17)
DEFINE_ISR(18)
DEFINE_ISR(19)
DEFINE_ISR(20)
DEFINE_ISR(21)
DEFINE_ISR(22)
DEFINE_ISR(23)
DEFINE_ISR(24)
DEFINE_ISR(25)
DEFINE_ISR(26)
DEFINE_ISR(27)
DEFINE_ISR(28)
DEFINE_ISR(29)
DEFINE_ISR(30)
DEFINE_ISR(31)
.Lintr = 32
.rept 224
DEFINE_ISR(.Lintr)
.Lintr = .Lintr+1
.endr
STATIC_FUNCTION(intr_bottom):
testl $3, 24(%rsp)
jnz intr_bottom_user
PUSH_IFRAME_BOTTOM(IFRAME_TYPE_OTHER)
cld
movq %rsp, %rbp
orq $X86_EFLAGS_RESUME, IFRAME_flags(%rbp)
andq $~63, %rsp
movq (gFPUSaveLength), %rcx
subq %rcx, %rsp
leaq (%rsp), %rdi
shrq $3, %rcx
movq $0, %rax
rep stosq
movl (gXsaveMask), %eax
movl (gXsaveMask+4), %edx
movq %rsp, %rdi
CODEPATCH_START
fxsaveq (%rdi)
CODEPATCH_END(ALTCODEPATCH_TAG_XSAVE)
movq %rbp, %rdi
movq IFRAME_vector(%rbp), %rax
call *gInterruptHandlerTable(, %rax, 8)
movl (gXsaveMask), %eax
movl (gXsaveMask+4), %edx
movq %rsp, %rdi
CODEPATCH_START
fxrstorq (%rdi)
CODEPATCH_END(ALTCODEPATCH_TAG_XRSTOR)
movq %rbp, %rsp
RESTORE_IFRAME()
iretq
FUNCTION_END(intr_bottom)
STATIC_FUNCTION(intr_bottom_user):
swapgs
lfence
PUSH_IFRAME_BOTTOM(IFRAME_TYPE_OTHER)
cld
movq %rsp, %rbp
andq $~15, %rsp
movq %gs:0, %r12
movl (gXsaveMask), %eax
movl (gXsaveMask+4), %edx
leaq THREAD_user_fpu_state(%r12), %rdi
CODEPATCH_START
fxsaveq (%rdi)
CODEPATCH_END(ALTCODEPATCH_TAG_XSAVE)
movq %rdi, IFRAME_fpu(%rbp)
orq $X86_EFLAGS_RESUME, IFRAME_flags(%rbp)
STOP_USER_DEBUGGING()
UPDATE_THREAD_USER_TIME()
movq %rbp, %rdi
movq IFRAME_vector(%rbp), %rax
call *gInterruptHandlerTable(, %rax, 8)
testl $(THREAD_FLAGS_DEBUGGER_INSTALLED | THREAD_FLAGS_SIGNALS_PENDING \
| THREAD_FLAGS_DEBUG_THREAD | THREAD_FLAGS_BREAKPOINTS_DEFINED \
| THREAD_FLAGS_TRAP_FOR_CORE_DUMP) \
, THREAD_flags(%r12)
jnz .Lkernel_exit_work
cli
UPDATE_THREAD_KERNEL_TIME()
movl (gXsaveMask), %eax
movl (gXsaveMask+4), %edx
leaq THREAD_user_fpu_state(%r12), %rdi
CODEPATCH_START
fxrstorq (%rdi)
CODEPATCH_END(ALTCODEPATCH_TAG_XRSTOR)
movq %rbp, %rsp
RESTORE_IFRAME()
swapgs
iretq
.Lkernel_exit_work:
testl $(THREAD_FLAGS_SIGNALS_PENDING | THREAD_FLAGS_DEBUG_THREAD \
| THREAD_FLAGS_TRAP_FOR_CORE_DUMP) \
, THREAD_flags(%r12)
jnz .Lkernel_exit_handle_signals
cli
call thread_at_kernel_exit_no_signals
.Lkernel_exit_work_done:
testl $THREAD_FLAGS_BREAKPOINTS_DEFINED, THREAD_flags(%r12)
jz 1f
movq %rbp, %rdi
call x86_init_user_debug_at_kernel_exit
1:
movl (gXsaveMask), %eax
movl (gXsaveMask+4), %edx
leaq THREAD_user_fpu_state(%r12), %rdi
CODEPATCH_START
fxrstorq (%rdi)
CODEPATCH_END(ALTCODEPATCH_TAG_XRSTOR)
movq %rbp, %rsp
RESTORE_IFRAME()
swapgs
iretq
.Lkernel_exit_handle_signals:
sti
call thread_at_kernel_exit
jmp .Lkernel_exit_work_done
FUNCTION_END(intr_bottom_user)
FUNCTION(x86_64_syscall_entry):
swapgs
movq %rsp, %gs:ARCH_THREAD_user_rsp
movq %gs:ARCH_THREAD_syscall_rsp, %rsp
sub $8, %rsp
push $USER_DATA_SELECTOR
push %gs:ARCH_THREAD_user_rsp
push %r11
push $USER_CODE_SELECTOR
push %rcx
push $0
push $99
PUSH_IFRAME_BOTTOM(IFRAME_TYPE_SYSCALL)
cld
movq %rsp, %rbp
movq %rax, %r14
movq %gs:0, %r12
STOP_USER_DEBUGGING()
UPDATE_THREAD_USER_TIME()
sti
cmpq $SYSCALL_COUNT, %r14
jae .Lsyscall_return
movq %r14, %rax
shlq $4, %rax
leaq kSyscallInfos(, %rax, 1), %rax
movq SYSCALL_INFO_parameter_size(%rax), %rcx
cmpq $48, %rcx
ja .Lsyscall_stack_args
.Lperform_syscall:
testl $THREAD_FLAGS_DEBUGGER_INSTALLED, THREAD_flags(%r12)
jnz .Lpre_syscall_debug
.Lpre_syscall_debug_done:
movq IFRAME_di(%rbp), %rdi
movq IFRAME_si(%rbp), %rsi
movq IFRAME_dx(%rbp), %rdx
movq IFRAME_r10(%rbp), %rcx
movq IFRAME_r8(%rbp), %r8
movq IFRAME_r9(%rbp), %r9
call *SYSCALL_INFO_function(%rax)
movq %rax, IFRAME_ax(%rbp)
.Lsyscall_return:
movq %rbp, %rsp
testl $THREAD_FLAGS_SYSCALL_RESTARTED, THREAD_flags(%r12)
jz 2f
1:
movl THREAD_flags(%r12), %eax
movl %eax, %edx
andl $~THREAD_FLAGS_SYSCALL_RESTARTED, %edx
lock
cmpxchgl %edx, THREAD_flags(%r12)
jnz 1b
2:
testl $(THREAD_FLAGS_DEBUGGER_INSTALLED | THREAD_FLAGS_SIGNALS_PENDING \
| THREAD_FLAGS_DEBUG_THREAD | THREAD_FLAGS_BREAKPOINTS_DEFINED \
| THREAD_FLAGS_TRAP_FOR_CORE_DUMP | THREAD_FLAGS_RESTART_SYSCALL) \
, THREAD_flags(%r12)
jnz .Lpost_syscall_work
cli
UPDATE_THREAD_KERNEL_TIME()
cmpq $SYSCALL_RESTORE_SIGNAL_FRAME, %r14
je .Lrestore_fpu
CLEAR_FPU_STATE()
RESTORE_IFRAME()
pop %rcx
addq $8, %rsp
pop %r11
pop %rsp
swapgs
sysretq
.Lpre_syscall_debug:
push IFRAME_r9(%rbp)
push IFRAME_r8(%rbp)
push IFRAME_r10(%rbp)
push IFRAME_dx(%rbp)
push IFRAME_si(%rbp)
push IFRAME_di(%rbp)
movq %r14, %rdi
movq %rsp, %rsi
subq $8, %rsp
push %rax
call user_debug_pre_syscall
pop %rax
addq $56, %rsp
jmp .Lpre_syscall_debug_done
.Lpost_syscall_work:
testl $THREAD_FLAGS_DEBUGGER_INSTALLED, THREAD_flags(%r12)
jz 1f
push IFRAME_r9(%rbp)
push IFRAME_r8(%rbp)
push IFRAME_r10(%rbp)
push IFRAME_dx(%rbp)
push IFRAME_si(%rbp)
push IFRAME_di(%rbp)
movq %r14, %rdi
movq %rsp, %rsi
movq IFRAME_ax(%rbp), %rdx
call user_debug_post_syscall
addq $48, %rsp
1:
testl $(THREAD_FLAGS_SIGNALS_PENDING | THREAD_FLAGS_DEBUG_THREAD \
| THREAD_FLAGS_TRAP_FOR_CORE_DUMP) \
, THREAD_flags(%r12)
jnz .Lpost_syscall_handle_signals
cli
call thread_at_kernel_exit_no_signals
.Lpost_syscall_work_done:
testl $THREAD_FLAGS_RESTART_SYSCALL, THREAD_flags(%r12)
jz 1f
movq %rsp, %rdi
call x86_restart_syscall
1:
testl $THREAD_FLAGS_BREAKPOINTS_DEFINED, THREAD_flags(%r12)
jz 1f
movq %rbp, %rdi
call x86_init_user_debug_at_kernel_exit
1:
CLEAR_FPU_STATE()
jmp .Liret
.Lrestore_fpu:
movq IFRAME_fpu(%rbp), %rdi
movl (gXsaveMask), %eax
movl (gXsaveMask+4), %edx
CODEPATCH_START
fxrstorq (%rdi)
CODEPATCH_END(ALTCODEPATCH_TAG_XRSTOR)
.Liret:
RESTORE_IFRAME()
swapgs
iretq
.Lpost_syscall_handle_signals:
call thread_at_kernel_exit
jmp .Lpost_syscall_work_done
.Lsyscall_stack_args:
subq $48, %rcx
movq IFRAME_user_sp(%rbp), %rsi
addq $8, %rsi
movabs $(USER_BASE + USER_SIZE), %rdx
cmp %rdx, %rsi
jae .Lbad_syscall_args
subq %rcx, %rsp
andq $~15, %rsp
movq %rsp, %rdi
movq $.Lbad_syscall_args, THREAD_fault_handler(%r12)
ASM_STAC
shrq $3, %rcx
rep
movsq
ASM_CLAC
movq $0, THREAD_fault_handler(%r12)
jmp .Lperform_syscall
.Lbad_syscall_args:
movq $0, THREAD_fault_handler(%r12)
movq %rbp, %rsp
jmp .Lsyscall_return
FUNCTION_END(x86_64_syscall_entry)
FUNCTION(x86_return_to_userland):
movq %rdi, %rbp
movq %rbp, %rsp
movq %gs:0, %r12
testl $(THREAD_FLAGS_DEBUGGER_INSTALLED | THREAD_FLAGS_SIGNALS_PENDING \
| THREAD_FLAGS_DEBUG_THREAD | THREAD_FLAGS_BREAKPOINTS_DEFINED \
| THREAD_FLAGS_TRAP_FOR_CORE_DUMP) \
, THREAD_flags(%r12)
jnz .Luserland_return_work
UPDATE_THREAD_KERNEL_TIME()
CLEAR_FPU_STATE()
RESTORE_IFRAME()
swapgs
iretq
.Luserland_return_work:
testl $(THREAD_FLAGS_SIGNALS_PENDING | THREAD_FLAGS_DEBUG_THREAD \
| THREAD_FLAGS_TRAP_FOR_CORE_DUMP) \
, THREAD_flags(%r12)
jnz .Luserland_return_handle_signals
cli
call thread_at_kernel_exit_no_signals
.Luserland_return_work_done:
testl $THREAD_FLAGS_BREAKPOINTS_DEFINED, THREAD_flags(%r12)
jz 1f
movq %rbp, %rdi
call x86_init_user_debug_at_kernel_exit
1:
CLEAR_FPU_STATE()
RESTORE_IFRAME()
swapgs
iretq
.Luserland_return_handle_signals:
sti
call thread_at_kernel_exit
jmp .Luserland_return_work_done
FUNCTION_END(x86_return_to_userland)