x86_64: GS base work
[akaros.git] / kern / arch / x86 / process64.c
1 #include <arch/arch.h>
2 #include <trap.h>
3 #include <process.h>
4 #include <pmap.h>
5 #include <smp.h>
6
7 #include <string.h>
8 #include <assert.h>
9 #include <stdio.h>
10
11 /* TODO: handle user and kernel contexts */
12 void proc_pop_ctx(struct user_context *ctx)
13 {
14         struct hw_trapframe *tf = &ctx->tf.hw_tf;
15         assert(ctx->type == ROS_HW_CTX);
16
17         /* Bug with this whole idea (TODO: (TLSV))*/
18         /* Load the LDT for this process.  Slightly ghetto doing it here. */
19         /* copy-in and check the LDT location.  the segmentation hardware writes the
20          * accessed bit, so we want the memory to be in the user-writeable area. */
21         segdesc_t *ldt = current->procdata->ldt;
22         ldt = (segdesc_t*)MIN((uintptr_t)ldt, UWLIM - LDT_SIZE);
23         /* Only set up the ldt if a pointer to the ldt actually exists */
24 #if 0 /* think about how to do TLS.  need better seg macros too */
25         if(ldt != NULL) {
26                 segdesc_t *my_gdt = per_cpu_info[core_id()].gdt;
27                 /* TODO: 64b issues here.  need to redo this anyways.  Considering how
28                  * slow userspace TLS changes are (70ns), I might opt for just changing
29                  * FS base, either via fast syscall or in userspace on newer versions */
30                 segdesc_t ldt_temp = SEG_SYS(STS_LDT, (uint32_t)ldt, LDT_SIZE, 3);
31                 my_gdt[GD_LDT >> 3] = ldt_temp;
32                 asm volatile("lldt %%ax" :: "a"(GD_LDT));
33         }
34 #endif
35
36         /* In case they are enabled elsewhere.  We can't take an interrupt in these
37          * routines, due to how they play with the kernel stack pointer. */
38         disable_irq();
39         write_msr(MSR_GS_BASE, (uint64_t)tf->tf_gsbase);
40         write_msr(MSR_FS_BASE, (uint64_t)tf->tf_fsbase);
41         /* If the process entered the kernel via sysenter, we need to leave via
42          * sysexit.  sysenter trapframes have 0 for a CS, which is pushed in
43          * sysenter_handler. */
44         if (tf->tf_cs) {
45                 asm volatile ("movq %0, %%rsp;          "
46                               "popq %%rax;              "
47                               "popq %%rbx;              "
48                               "popq %%rcx;              "
49                               "popq %%rdx;              "
50                               "popq %%rbp;              "
51                               "popq %%rsi;              "
52                               "popq %%rdi;              "
53                               "popq %%r8;               "
54                               "popq %%r9;               "
55                               "popq %%r10;              "
56                               "popq %%r11;              "
57                               "popq %%r12;              "
58                               "popq %%r13;              "
59                               "popq %%r14;              "
60                               "popq %%r15;              "
61                               "addq $0x10, %%rsp;       "
62                               "iretq                    "
63                               : : "g" (&tf->tf_rax) : "memory");
64                 panic("iret failed");  /* mostly to placate the compiler */
65         } else {
66                 /* Return path of sysexit.  See sysenter_handler's asm for details.
67                  * One difference is that this tf could be somewhere other than a stack
68                  * (like in a struct proc).  We need to make sure esp is valid once
69                  * interrupts are turned on (which would happen on popfl normally), so
70                  * we need to save and restore a decent esp (the current one).  We need
71                  * a place to save it that is accessible after we change the stack
72                  * pointer to the tf *and* that is specific to this core/instance of
73                  * sysexit.  The simplest and nicest is to use the tf_esp, which we
74                  * can just pop.  Incidentally, the value in oesp would work too.
75                  * To prevent popfl from turning interrupts on, we hack the tf's eflags
76                  * so that we have a chance to change esp to a good value before
77                  * interrupts are enabled.  The other option would be to throw away the
78                  * eflags, but that's less desirable. */
79                 tf->tf_rflags &= !FL_IF;
80                 tf->tf_rsp = read_sp();
81 //              asm volatile ("movl %0,%%esp;           "
82 //                            "popal;                   "
83 //                            "popl %%gs;               "
84 //                            "popl %%fs;               "
85 //                            "popl %%es;               "
86 //                            "popl %%ds;               "
87 //                            "addl $0x10,%%esp;        "
88 //                            "popfl;                   "
89 //                            "movl %%ebp,%%ecx;        "
90 //                            "popl %%esp;              "
91 //                            "sti;                     "
92 //                            "sysexit                  "
93 //                            : : "g" (&tf->tf_rax) : "memory");
94                 // keep in mind, we can take an interrupt in here (depending on what GS
95                 // tricks there are)
96                 panic("sysexit failed");  /* mostly to placate your mom */
97         }
98 }
99
100 /* TODO: consider using a SW context */
101 void proc_init_ctx(struct user_context *ctx, uint32_t vcoreid, uintptr_t entryp,
102                    uintptr_t stack_top)
103 {
104         struct hw_trapframe *tf = &ctx->tf.hw_tf;
105         ctx->type = ROS_HW_CTX;
106
107         memset(tf,0,sizeof(*tf));
108
109         /* Set up appropriate initial values for the segment registers.
110          * GD_UD is the user data segment selector in the GDT, and
111          * GD_UT is the user text segment selector (see inc/memlayout.h).
112          * The low 2 bits of each segment register contains the
113          * Requestor Privilege Level (RPL); 3 means user mode. */
114         tf->tf_ss = GD_UD | 3;
115         tf->tf_rsp = stack_top-64;
116         tf->tf_cs = GD_UT | 3;
117         /* set the env's EFLAGSs to have interrupts enabled */
118         tf->tf_rflags |= 0x00000200; // bit 9 is the interrupts-enabled
119
120         tf->tf_rip = entryp;
121
122         /* Coupled closely with user's entry.S.  id is the vcoreid, which entry.S
123          * uses to determine what to do.  vcoreid == 0 is the main core/context. */
124         tf->tf_rax = vcoreid;
125 }
126
127 /* TODO: handle both HW and SW contexts */
128 void proc_secure_ctx(struct user_context *ctx)
129 {
130         struct hw_trapframe *tf = &ctx->tf.hw_tf;
131         ctx->type = ROS_HW_CTX;
132         /* we normally don't need to set the non-CS regs, but they could be
133          * gibberish and cause a GPF.  gs can still be gibberish, but we don't
134          * necessarily know what it ought to be (we could check, but that's a pain).
135          * the code protecting the kernel from TLS related things ought to be able
136          * to handle GPFs on popping gs. TODO: (TLSV) */
137         //tf->tf_fs = 0;
138         //tf->tf_gs = whatevs.  ignoring this.
139         tf->tf_ss = GD_UD | 3;
140         tf->tf_cs ? GD_UT | 3 : 0; // can be 0 for sysenter TFs.
141         tf->tf_rflags |= 0x00000200; // bit 9 is the interrupts-enabled
142 }
143
144 /* Called when we are currently running an address space on our core and want to
145  * abandon it.  We need a known good pgdir before releasing the old one.  We
146  * decref, since current no longer tracks the proc (and current no longer
147  * protects the cr3).  We also need to clear out the TLS registers (before
148  * unmapping the address space!) */
149 void __abandon_core(void)
150 {
151         struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
152         asm volatile ("movw %%ax,%%gs; lldt %%ax" :: "a"(0));
153         lcr3(boot_cr3);
154         proc_decref(pcpui->cur_proc);
155         pcpui->cur_proc = 0;
156 }