1// Copyright 2016 The Fuchsia Authors
2// Copyright (c) 2009 Corey Tabaka
3// Copyright (c) 2014 Travis Geiselbrecht
4// Copyright (c) 2015 Intel Corporation
5//
6// Use of this source code is governed by a MIT-style
7// license that can be found in the LICENSE file or at
8// https://opensource.org/licenses/MIT
9
10#include <arch/x86.h>
11#include <arch/x86/descriptor.h>
12#include <arch/x86/feature.h>
13#include <arch/x86/mp.h>
14#include <arch/x86/registers.h>
15#include <arch/x86/x86intrin.h>
16#include <assert.h>
17#include <debug.h>
18#include <kernel/spinlock.h>
19#include <kernel/thread.h>
20#include <stdlib.h>
21#include <string.h>
22#include <sys/types.h>
23
24void arch_thread_initialize(thread_t* t, vaddr_t entry_point) {
25    // create a default stack frame on the stack
26    vaddr_t stack_top = t->stack.top;
27
28    // make sure the top of the stack is 16 byte aligned for ABI compliance
29    stack_top = ROUNDDOWN(stack_top, 16);
30    t->stack.top = stack_top;
31
32    // make sure we start the frame 8 byte unaligned (relative to the 16 byte alignment) because
33    // of the way the context switch will pop the return address off the stack. After the first
34    // context switch, this leaves the stack unaligned relative to how a called function expects it.
35    stack_top -= 8;
36    struct x86_64_context_switch_frame* frame = (struct x86_64_context_switch_frame*)(stack_top);
37
38    // Record a zero return address so that backtraces will stop here.
39    // Otherwise if heap debugging is on, and say there is 99..99 here,
40    // then the debugger could try to continue the backtrace from there.
41    memset((void*)stack_top, 0, 8);
42
43    // move down a frame size and zero it out
44    frame--;
45    memset(frame, 0, sizeof(*frame));
46
47    frame->rip = entry_point;
48
49    // initialize the saved extended register state
50    vaddr_t buf = ROUNDUP(((vaddr_t)t->arch.extended_register_buffer), 64);
51    __UNUSED size_t overhead = buf - (vaddr_t)t->arch.extended_register_buffer;
52    DEBUG_ASSERT(sizeof(t->arch.extended_register_buffer) - overhead >=
53                 x86_extended_register_size());
54    t->arch.extended_register_state = (vaddr_t*)buf;
55    x86_extended_register_init_state(t->arch.extended_register_state);
56
57    // set the stack pointer
58    t->arch.sp = (vaddr_t)frame;
59#if __has_feature(safe_stack)
60    t->arch.unsafe_sp =
61        ROUNDDOWN(t->stack.unsafe_base + t->stack.size, 16);
62#endif
63
64    // initialize the fs, gs and kernel bases to 0.
65    t->arch.fs_base = 0;
66    t->arch.gs_base = 0;
67}
68
69void arch_thread_construct_first(thread_t* t) {
70}
71
72void arch_dump_thread(thread_t* t) {
73    if (t->state != THREAD_RUNNING) {
74        dprintf(INFO, "\tarch: ");
75        dprintf(INFO, "sp %#" PRIxPTR "\n", t->arch.sp);
76    }
77}
78
79void* arch_thread_get_blocked_fp(struct thread* t) {
80    if (!WITH_FRAME_POINTERS)
81        return nullptr;
82
83    struct x86_64_context_switch_frame* frame = (struct x86_64_context_switch_frame*)t->arch.sp;
84
85    return (void*)frame->rbp;
86}
87
88__NO_SAFESTACK __attribute__((target("fsgsbase"))) void arch_context_switch(thread_t* oldthread, thread_t* newthread) {
89    x86_extended_register_context_switch(oldthread, newthread);
90
91    //printf("cs 0x%llx\n", kstack_top);
92
93    /* set the tss SP0 value to point at the top of our stack */
94    x86_set_tss_sp(newthread->stack.top);
95
96    /* Save the user fs_base register value.  The new rdfsbase instruction
97     * is much faster than reading the MSR, so use the former in
98     * preference. */
99    if (likely(g_x86_feature_fsgsbase)) {
100        oldthread->arch.fs_base = _readfsbase_u64();
101    } else {
102        oldthread->arch.fs_base = read_msr(X86_MSR_IA32_FS_BASE);
103    }
104
105    /* The segment selector registers can't be preserved across context
106     * switches in all cases, because some values get clobbered when
107     * returning from interrupts.  If an interrupt occurs when a userland
108     * process has set %fs = 1 (for example), the IRET instruction used for
109     * returning from the interrupt will reset %fs to 0.
110     *
111     * To prevent the segment selector register values from leaking between
112     * processes, we reset these registers across context switches. */
113    set_ds(0);
114    set_es(0);
115    set_fs(0);
116    if (get_gs() != 0) {
117        /* Assigning to %gs clobbers gs_base, so we must restore gs_base
118         * afterwards. */
119        DEBUG_ASSERT(arch_ints_disabled());
120        uintptr_t gs_base = (uintptr_t)x86_get_percpu();
121        set_gs(0);
122        write_msr(X86_MSR_IA32_GS_BASE, gs_base);
123    }
124
125    /* Restore fs_base and save+restore user gs_base.  Note that the user
126     * and kernel gs_base values have been swapped -- the user value is
127     * currently in KERNEL_GS_BASE. */
128    if (likely(g_x86_feature_fsgsbase)) {
129        /* There is no variant of the {rd,wr}gsbase instructions for
130         * accessing KERNEL_GS_BASE, so we wrap those in two swapgs
131         * instructions to get the same effect.  This is a little
132         * convoluted, but still faster than using the KERNEL_GS_BASE
133         * MSRs. */
134        __asm__ __volatile__(
135            "swapgs\n"
136            "rdgsbase %[old_value]\n"
137            "wrgsbase %[new_value]\n"
138            "swapgs\n"
139            : [old_value] "=&r"(oldthread->arch.gs_base)
140            : [new_value] "r"(newthread->arch.gs_base));
141
142        _writefsbase_u64(newthread->arch.fs_base);
143    } else {
144        oldthread->arch.gs_base = read_msr(X86_MSR_IA32_KERNEL_GS_BASE);
145        write_msr(X86_MSR_IA32_FS_BASE, newthread->arch.fs_base);
146        write_msr(X86_MSR_IA32_KERNEL_GS_BASE, newthread->arch.gs_base);
147    }
148
149#if __has_feature(safe_stack)
150    oldthread->arch.unsafe_sp = x86_read_gs_offset64(ZX_TLS_UNSAFE_SP_OFFSET);
151    x86_write_gs_offset64(ZX_TLS_UNSAFE_SP_OFFSET, newthread->arch.unsafe_sp);
152#endif
153
154    x86_64_context_switch(&oldthread->arch.sp, newthread->arch.sp);
155}
156