acbfeb76cbe08b559a26cdeaccd4956fbb8b5985
[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         /* If the process entered the kernel via sysenter, we need to leave via
40          * sysexit.  sysenter trapframes have 0 for a CS, which is pushed in
41          * sysenter_handler. */
42         if (tf->tf_cs) {
43                 asm volatile ("movq %0, %%rsp;          "
44                               "popq %%rax;              "
45                               "popq %%rbx;              "
46                               "popq %%rcx;              "
47                               "popq %%rdx;              "
48                               "popq %%rbp;              "
49                               "popq %%rsi;              "
50                               "popq %%rdi;              "
51                               "popq %%r8;               "
52                               "popq %%r9;               "
53                               "popq %%r10;              "
54                               "popq %%r11;              "
55                               "popq %%r12;              "
56                               "popq %%r13;              "
57                               "popq %%r14;              "
58                               "popq %%r15;              "
59                               "movw 0x4(%%rsp), %%gs;   "
60                               "movw 0x6(%%rsp), %%fs;   "
61                               "addq $0x10, %%rsp;       "
62                               "iretq                    "
63                               : : "g" (tf) : "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) : "memory");
94                 panic("sysexit failed");  /* mostly to placate your mom */
95         }
96 }
97
98 /* TODO: consider using a SW context */
99 void proc_init_ctx(struct user_context *ctx, uint32_t vcoreid, uintptr_t entryp,
100                    uintptr_t stack_top)
101 {
102         struct hw_trapframe *tf = &ctx->tf.hw_tf;
103         ctx->type = ROS_HW_CTX;
104
105         memset(tf,0,sizeof(*tf));
106
107         /* Set up appropriate initial values for the segment registers.
108          * GD_UD is the user data segment selector in the GDT, and
109          * GD_UT is the user text segment selector (see inc/memlayout.h).
110          * The low 2 bits of each segment register contains the
111          * Requestor Privilege Level (RPL); 3 means user mode. */
112         tf->tf_ss = GD_UD | 3;
113         tf->tf_rsp = stack_top-64;
114         tf->tf_cs = GD_UT | 3;
115         /* set the env's EFLAGSs to have interrupts enabled */
116         tf->tf_rflags |= 0x00000200; // bit 9 is the interrupts-enabled
117
118         tf->tf_rip = entryp;
119
120         /* Coupled closely with user's entry.S.  id is the vcoreid, which entry.S
121          * uses to determine what to do.  vcoreid == 0 is the main core/context. */
122         tf->tf_rax = vcoreid;
123 }
124
125 /* TODO: handle both HW and SW contexts */
126 void proc_secure_ctx(struct user_context *ctx)
127 {
128         struct hw_trapframe *tf = &ctx->tf.hw_tf;
129         ctx->type = ROS_HW_CTX;
130         /* we normally don't need to set the non-CS regs, but they could be
131          * gibberish and cause a GPF.  gs can still be gibberish, but we don't
132          * necessarily know what it ought to be (we could check, but that's a pain).
133          * the code protecting the kernel from TLS related things ought to be able
134          * to handle GPFs on popping gs. TODO: (TLSV) */
135         //tf->tf_fs = 0;
136         //tf->tf_gs = whatevs.  ignoring this.
137         tf->tf_ss = GD_UD | 3;
138         tf->tf_cs ? GD_UT | 3 : 0; // can be 0 for sysenter TFs.
139         tf->tf_rflags |= 0x00000200; // bit 9 is the interrupts-enabled
140 }
141
142 /* Called when we are currently running an address space on our core and want to
143  * abandon it.  We need a known good pgdir before releasing the old one.  We
144  * decref, since current no longer tracks the proc (and current no longer
145  * protects the cr3).  We also need to clear out the TLS registers (before
146  * unmapping the address space!) */
147 void __abandon_core(void)
148 {
149         struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
150         asm volatile ("movw %%ax,%%gs; lldt %%ax" :: "a"(0));
151         lcr3(boot_cr3);
152         proc_decref(pcpui->cur_proc);
153         pcpui->cur_proc = 0;
154 }