| // Copyright 2016 The Fuchsia Authors |
| // Copyright (c) 2009 Corey Tabaka |
| // Copyright (c) 2014 Travis Geiselbrecht |
| // Copyright (c) 2015 Intel Corporation |
| // |
| // Use of this source code is governed by a MIT-style |
| // license that can be found in the LICENSE file or at |
| // https://opensource.org/licenses/MIT |
| |
| #include <arch/x86.h> |
| #include <arch/x86/descriptor.h> |
| #include <arch/x86/feature.h> |
| #include <arch/x86/mp.h> |
| #include <arch/x86/registers.h> |
| #include <arch/x86/x86intrin.h> |
| #include <assert.h> |
| #include <debug.h> |
| #include <kernel/spinlock.h> |
| #include <kernel/thread.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/types.h> |
| |
| void arch_thread_initialize(thread_t* t, vaddr_t entry_point) { |
| // create a default stack frame on the stack |
| vaddr_t stack_top = t->stack.top; |
| |
| // make sure the top of the stack is 16 byte aligned for ABI compliance |
| stack_top = ROUNDDOWN(stack_top, 16); |
| t->stack.top = stack_top; |
| |
| // make sure we start the frame 8 byte unaligned (relative to the 16 byte alignment) because |
| // of the way the context switch will pop the return address off the stack. After the first |
| // context switch, this leaves the stack unaligned relative to how a called function expects it. |
| stack_top -= 8; |
| struct x86_64_context_switch_frame* frame = (struct x86_64_context_switch_frame*)(stack_top); |
| |
| // Record a zero return address so that backtraces will stop here. |
| // Otherwise if heap debugging is on, and say there is 99..99 here, |
| // then the debugger could try to continue the backtrace from there. |
| memset((void*)stack_top, 0, 8); |
| |
| // move down a frame size and zero it out |
| frame--; |
| memset(frame, 0, sizeof(*frame)); |
| |
| frame->rip = entry_point; |
| |
| // initialize the saved extended register state |
| vaddr_t buf = ROUNDUP(((vaddr_t)t->arch.extended_register_buffer), 64); |
| __UNUSED size_t overhead = buf - (vaddr_t)t->arch.extended_register_buffer; |
| DEBUG_ASSERT(sizeof(t->arch.extended_register_buffer) - overhead >= |
| x86_extended_register_size()); |
| t->arch.extended_register_state = (vaddr_t*)buf; |
| x86_extended_register_init_state(t->arch.extended_register_state); |
| |
| // set the stack pointer |
| t->arch.sp = (vaddr_t)frame; |
| #if __has_feature(safe_stack) |
| t->arch.unsafe_sp = |
| ROUNDDOWN(t->stack.unsafe_base + t->stack.size, 16); |
| #endif |
| |
| // initialize the fs, gs and kernel bases to 0. |
| t->arch.fs_base = 0; |
| t->arch.gs_base = 0; |
| |
| // Initialize the debug registers to a valid initial state. |
| t->arch.track_debug_state = false; |
| for (size_t i = 0; i < 4; i++) { |
| t->arch.debug_state.dr[i] = 0; |
| } |
| t->arch.debug_state.dr6 = ~X86_DR6_USER_MASK; |
| t->arch.debug_state.dr7 = ~X86_DR7_USER_MASK; |
| } |
| |
| void arch_thread_construct_first(thread_t* t) { |
| } |
| |
| void arch_dump_thread(thread_t* t) { |
| if (t->state != THREAD_RUNNING) { |
| dprintf(INFO, "\tarch: "); |
| dprintf(INFO, "sp %#" PRIxPTR "\n", t->arch.sp); |
| } |
| } |
| |
| void* arch_thread_get_blocked_fp(struct thread* t) { |
| if (!WITH_FRAME_POINTERS) |
| return nullptr; |
| |
| struct x86_64_context_switch_frame* frame = (struct x86_64_context_switch_frame*)t->arch.sp; |
| |
| return (void*)frame->rbp; |
| } |
| |
| __NO_SAFESTACK __attribute__((target("fsgsbase"))) |
| void arch_context_switch(thread_t* oldthread, thread_t* newthread) { |
| x86_extended_register_context_switch(oldthread, newthread); |
| |
| x86_debug_state_context_switch(oldthread, newthread); |
| |
| //printf("cs 0x%llx\n", kstack_top); |
| |
| /* set the tss SP0 value to point at the top of our stack */ |
| x86_set_tss_sp(newthread->stack.top); |
| |
| /* Save the user fs_base register value. The new rdfsbase instruction |
| * is much faster than reading the MSR, so use the former in |
| * preference. */ |
| if (likely(g_x86_feature_fsgsbase)) { |
| oldthread->arch.fs_base = _readfsbase_u64(); |
| } else { |
| oldthread->arch.fs_base = read_msr(X86_MSR_IA32_FS_BASE); |
| } |
| |
| /* The segment selector registers can't be preserved across context |
| * switches in all cases, because some values get clobbered when |
| * returning from interrupts. If an interrupt occurs when a userland |
| * process has set %fs = 1 (for example), the IRET instruction used for |
| * returning from the interrupt will reset %fs to 0. |
| * |
| * To prevent the segment selector register values from leaking between |
| * processes, we reset these registers across context switches. */ |
| set_ds(0); |
| set_es(0); |
| set_fs(0); |
| if (get_gs() != 0) { |
| /* Assigning to %gs clobbers gs_base, so we must restore gs_base |
| * afterwards. */ |
| DEBUG_ASSERT(arch_ints_disabled()); |
| uintptr_t gs_base = (uintptr_t)x86_get_percpu(); |
| set_gs(0); |
| write_msr(X86_MSR_IA32_GS_BASE, gs_base); |
| } |
| |
| /* Restore fs_base and save+restore user gs_base. Note that the user |
| * and kernel gs_base values have been swapped -- the user value is |
| * currently in KERNEL_GS_BASE. */ |
| if (likely(g_x86_feature_fsgsbase)) { |
| /* There is no variant of the {rd,wr}gsbase instructions for |
| * accessing KERNEL_GS_BASE, so we wrap those in two swapgs |
| * instructions to get the same effect. This is a little |
| * convoluted, but still faster than using the KERNEL_GS_BASE |
| * MSRs. */ |
| __asm__ __volatile__( |
| "swapgs\n" |
| "rdgsbase %[old_value]\n" |
| "wrgsbase %[new_value]\n" |
| "swapgs\n" |
| : [old_value] "=&r"(oldthread->arch.gs_base) |
| : [new_value] "r"(newthread->arch.gs_base)); |
| |
| _writefsbase_u64(newthread->arch.fs_base); |
| } else { |
| oldthread->arch.gs_base = read_msr(X86_MSR_IA32_KERNEL_GS_BASE); |
| write_msr(X86_MSR_IA32_FS_BASE, newthread->arch.fs_base); |
| write_msr(X86_MSR_IA32_KERNEL_GS_BASE, newthread->arch.gs_base); |
| } |
| |
| #if __has_feature(safe_stack) |
| oldthread->arch.unsafe_sp = x86_read_gs_offset64(ZX_TLS_UNSAFE_SP_OFFSET); |
| x86_write_gs_offset64(ZX_TLS_UNSAFE_SP_OFFSET, newthread->arch.unsafe_sp); |
| #endif |
| |
| x86_64_context_switch(&oldthread->arch.sp, newthread->arch.sp); |
| } |
| |
| void x86_debug_state_context_switch(thread_t *old_thread, thread_t *new_thread) { |
| // If the new thread has debug state, then install it, replacing the current contents. |
| if (unlikely(new_thread->arch.track_debug_state)) { |
| // NOTE: There is no enable debug state call, as x86 doesn't have a global enable/disable |
| // switch, but rather enables particular registers through DR7. These registers are |
| // selected by userspace (and filtered by zircon) in the thread_write_state state |
| // syscall. |
| // |
| // This means that just writing the thread debug state into the CPU is enough to |
| // activate the debug functionality. |
| x86_write_hw_debug_regs(&new_thread->arch.debug_state); |
| return; |
| } |
| |
| // If the old thread had debug state running and the new one doesn't use it, disable the |
| // debug capabilities. |
| if (unlikely(old_thread->arch.track_debug_state)) { |
| x86_disable_debug_state(); |
| } |
| } |