x86_64: kernel trap/interrupt handling
[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         /*
40          * If the process entered the kernel via sysenter, we need to leave via
41          * sysexit.  sysenter trapframes have 0 for a CS, which is pushed in
42          * sysenter_handler.
43          */
44         if(tf->tf_cs) {
45                 /*
46                  * Restores the register values in the Trapframe with the 'iret'
47                  * instruction.  This exits the kernel and starts executing some
48                  * environment's code.  This function does not return.
49                  */
50 //              asm volatile ("movl %0,%%esp;           "
51 //                            "popal;                   "
52 //                            "popl %%gs;               "
53 //                            "popl %%fs;               "
54 //                            "popl %%es;               "
55 //                            "popl %%ds;               "
56 //                            "addl $0x8,%%esp;         "
57 //                            "iret                     "
58 //                            : : "g" (tf) : "memory");
59                 panic("iret failed");  /* mostly to placate the compiler */
60         } else {
61                 /* Return path of sysexit.  See sysenter_handler's asm for details.
62                  * One difference is that this tf could be somewhere other than a stack
63                  * (like in a struct proc).  We need to make sure esp is valid once
64                  * interrupts are turned on (which would happen on popfl normally), so
65                  * we need to save and restore a decent esp (the current one).  We need
66                  * a place to save it that is accessible after we change the stack
67                  * pointer to the tf *and* that is specific to this core/instance of
68                  * sysexit.  The simplest and nicest is to use the tf_esp, which we
69                  * can just pop.  Incidentally, the value in oesp would work too.
70                  * To prevent popfl from turning interrupts on, we hack the tf's eflags
71                  * so that we have a chance to change esp to a good value before
72                  * interrupts are enabled.  The other option would be to throw away the
73                  * eflags, but that's less desirable. */
74                 tf->tf_rflags &= !FL_IF;
75                 tf->tf_rsp = read_sp();
76 //              asm volatile ("movl %0,%%esp;           "
77 //                            "popal;                   "
78 //                            "popl %%gs;               "
79 //                            "popl %%fs;               "
80 //                            "popl %%es;               "
81 //                            "popl %%ds;               "
82 //                            "addl $0x10,%%esp;        "
83 //                            "popfl;                   "
84 //                            "movl %%ebp,%%ecx;        "
85 //                            "popl %%esp;              "
86 //                            "sti;                     "
87 //                            "sysexit                  "
88 //                            : : "g" (tf) : "memory");
89                 panic("sysexit failed");  /* mostly to placate your mom */
90         }
91 }
92
93 /* TODO: consider using a SW context */
94 void proc_init_ctx(struct user_context *ctx, uint32_t vcoreid, uintptr_t entryp,
95                    uintptr_t stack_top)
96 {
97         struct hw_trapframe *tf = &ctx->tf.hw_tf;
98         ctx->type = ROS_HW_CTX;
99
100         memset(tf,0,sizeof(*tf));
101
102         /* Set up appropriate initial values for the segment registers.
103          * GD_UD is the user data segment selector in the GDT, and
104          * GD_UT is the user text segment selector (see inc/memlayout.h).
105          * The low 2 bits of each segment register contains the
106          * Requestor Privilege Level (RPL); 3 means user mode. */
107         tf->tf_ss = GD_UD | 3;
108         tf->tf_rsp = stack_top-64;
109         tf->tf_cs = GD_UT | 3;
110         /* set the env's EFLAGSs to have interrupts enabled */
111         tf->tf_rflags |= 0x00000200; // bit 9 is the interrupts-enabled
112
113         tf->tf_rip = entryp;
114
115         /* Coupled closely with user's entry.S.  id is the vcoreid, which entry.S
116          * uses to determine what to do.  vcoreid == 0 is the main core/context. */
117         tf->tf_rax = vcoreid;
118 }
119
120 /* TODO: handle both HW and SW contexts */
121 void proc_secure_ctx(struct user_context *ctx)
122 {
123         struct hw_trapframe *tf = &ctx->tf.hw_tf;
124         ctx->type = ROS_HW_CTX;
125         /* we normally don't need to set the non-CS regs, but they could be
126          * gibberish and cause a GPF.  gs can still be gibberish, but we don't
127          * necessarily know what it ought to be (we could check, but that's a pain).
128          * the code protecting the kernel from TLS related things ought to be able
129          * to handle GPFs on popping gs. TODO: (TLSV) */
130         //tf->tf_fs = 0;
131         //tf->tf_gs = whatevs.  ignoring this.
132         tf->tf_ss = GD_UD | 3;
133         tf->tf_cs ? GD_UT | 3 : 0; // can be 0 for sysenter TFs.
134         tf->tf_rflags |= 0x00000200; // bit 9 is the interrupts-enabled
135 }
136
137 /* Called when we are currently running an address space on our core and want to
138  * abandon it.  We need a known good pgdir before releasing the old one.  We
139  * decref, since current no longer tracks the proc (and current no longer
140  * protects the cr3).  We also need to clear out the TLS registers (before
141  * unmapping the address space!) */
142 void __abandon_core(void)
143 {
144         struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
145         asm volatile ("movw %%ax,%%gs; lldt %%ax" :: "a"(0));
146         lcr3(boot_cr3);
147         proc_decref(pcpui->cur_proc);
148         pcpui->cur_proc = 0;
149 }