Fixes bug with run_current_uthread()
[akaros.git] / user / parlib / include / i686 / vcore.h
1 #ifndef PARLIB_ARCH_VCORE_H
2 #define PARLIB_ARCH_VCORE_H
3
4 #include <ros/common.h>
5 #include <ros/arch/trapframe.h>
6 #include <ros/procdata.h>
7 #include <ros/syscall.h>
8 #include <ros/arch/mmu.h>
9
10 extern __thread int __vcoreid;
11
12 /* Pops an ROS kernel-provided TF, reanabling notifications at the same time.
13  * A Userspace scheduler can call this when transitioning off the transition
14  * stack.
15  *
16  * Make sure you clear the notif_pending flag, and then check the queue before
17  * calling this.  If notif_pending is not clear, this will self_notify this
18  * core, since it should be because we missed a notification message while
19  * notifs were disabled. 
20  *
21  * Basically, it sets up the future stack pointer to have extra stuff after it,
22  * and then it pops the registers, then pops the new context's stack
23  * pointer.  Then it uses the extra stuff (the new PC is on the stack, the
24  * location of notif_disabled, and a clobbered work register) to enable notifs,
25  * make sure notif IPIs weren't pending, restore the work reg, and then "ret".
26  *
27  * This is what the target notif_tf's stack will look like (growing down):
28  *
29  * Target ESP -> |   u_thread's old stuff   | the future %esp, tf->tf_esp
30  *               |   new eip                | 0x04 below %esp (one slot is 0x04)
31  *               |   eflags space           | 0x08 below
32  *               |   eax save space         | 0x0c below
33  *               |   actual syscall         | 0x10 below (0x30 space)
34  *               |   *sysc ptr to syscall   | 0x40 below (0x10 + 0x30)
35  *               |   notif_pending_loc      | 0x44 below (0x10 + 0x30)
36  *               |   notif_disabled_loc     | 0x48 below (0x10 + 0x30)
37  *
38  * The important thing is that it can handle a notification after it enables
39  * notifications, and when it gets resumed it can ultimately run the new
40  * context.  Enough state is saved in the running context and stack to continue
41  * running.
42  *
43  * Related to that is whether or not our stack pointer is sufficiently far down
44  * so that restarting *this* code won't clobber shit we need later.  The way we
45  * do this is that we do any "stack jumping" after we enable interrupts/notifs.
46  * These jumps are when we directly modify esp, specifically in the down
47  * direction (subtracts).  Adds would be okay.
48  *
49  * Another related concern is the storage for sysc.  It used to be on the
50  * vcore's stack, but if an interrupt comes in before we use it, we trash the
51  * vcore's stack (and thus the storage for sysc!).  Instead, we put it on the
52  * stack of the user tf.  Moral: don't touch a vcore's stack with notifs
53  * enabled. */
54
55 /* Helper for writing the info we need later to the u_tf's stack.  Note, this
56  * could get fucked if the struct syscall isn't a multiple of 4-bytes.  Also,
57  * note this goes backwards, since memory reads up the stack. */
58 struct restart_helper {
59         uint32_t                                        notif_disab_loc;
60         uint32_t                                        notif_pend_loc;
61         struct syscall                          *sysc;
62         struct syscall                          local_sysc;
63         uint32_t                                        eax_save;
64         uint32_t                                        eflags;
65         uint32_t                                        eip;
66 };
67
68 static inline void pop_ros_tf(struct user_trapframe *tf, uint32_t vcoreid)
69 {
70         struct restart_helper *rst;
71         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
72         if (!tf->tf_cs) { /* sysenter TF.  esp and eip are in other regs. */
73                 tf->tf_esp = tf->tf_regs.reg_ebp;
74                 tf->tf_eip = tf->tf_regs.reg_edx;
75         }
76         /* The stuff we need to write will be below the current stack of the utf */
77         rst = (struct restart_helper*)((void*)tf->tf_esp -
78                                        sizeof(struct restart_helper));
79         /* Fill in the info we'll need later */
80         rst->notif_disab_loc = (uint32_t)&vcpd->notif_disabled;
81         rst->notif_pend_loc = (uint32_t)&vcpd->notif_pending;
82         rst->sysc = &rst->local_sysc;   /* point to the local one */
83         memset(rst->sysc, 0, sizeof(struct syscall));
84         /* Need to prep the async sysc in case we need to notify ourselves */
85         rst->sysc->num = SYS_self_notify;
86         rst->sysc->arg0 = vcoreid;      /* arg 1 & 2 already = 0 (null notif, no u_ne)*/
87         rst->sysc->arg3 = TRUE;         /* just a private VCPD notification */
88         rst->eax_save = 0;                      /* avoid bugs */
89         rst->eflags = tf->tf_eflags;
90         rst->eip = tf->tf_eip;
91
92         asm volatile ("movl %0,%%esp;        " /* jump esp to the utf */
93                       "popal;                " /* restore normal registers */
94                       "addl $0x24,%%esp;     " /* move to the esp slot in the tf */
95                       "popl %%esp;           " /* change to the utf's %esp */
96                       "subl $0x08,%%esp;     " /* move esp to below eax's slot */
97                       "pushl %%eax;          " /* save eax, will clobber soon */
98                                   "movl %2,%%eax;        " /* sizeof struct syscall */
99                                   "addl $0x0c,%%eax;     " /* more offset btw eax/notif_en_loc*/
100                       "subl %%eax,%%esp;     " /* move to notif_en_loc slot */
101                       "popl %%eax;           " /* load notif_disabled addr */
102                       "movb $0x00,(%%eax);   " /* enable notifications */
103                                   /* Need a wrmb() here so the write of enable_notif can't pass
104                                    * the read of notif_pending (racing with a potential
105                                    * cross-core call with proc_notify()). */
106                                   "lock addl $0,(%%esp); " /* LOCK is a CPU mb() */
107                                   /* From here down, we can get interrupted and restarted */
108                       "popl %%eax;           " /* get notif_pending status */
109                       "testb $0x01,(%%eax);  " /* test if a notif is pending */
110                       "jz 1f;                " /* if not pending, skip syscall */
111                       "movb $0x00,(%%eax);   " /* clear pending */
112                                   /* Actual syscall.  Note we don't wait on the async call */
113                       "popl %%eax;           " /* &sysc, trap arg0 */
114                       "pushl %%edx;          " /* save edx, will be trap arg1 */
115                       "movl $0x1,%%edx;      " /* sending one async syscall: arg1 */
116                       "int %1;               " /* fire the syscall */
117                       "popl %%edx;           " /* restore regs after syscall */
118                       "jmp 2f;               " /* skip 1:, already popped */
119                                   "1: popl %%eax;        " /* discard &sysc (on non-sc path) */
120                       "2: addl %2,%%esp;     " /* jump over the sysc (both paths) */
121                       "popl %%eax;           " /* restore tf's %eax */
122                                   "popfl;                " /* restore utf's eflags */
123                       "ret;                  " /* return to the new PC */
124                       :
125                       : "g"(tf), "i"(T_SYSCALL), "i"(sizeof(struct syscall))
126                       : "memory");
127 }
128
129 /* Like the regular pop_ros_tf, but this one doesn't check or clear
130  * notif_pending. */
131 static inline void pop_ros_tf_raw(struct user_trapframe *tf, uint32_t vcoreid)
132 {
133         struct restart_helper *rst;
134         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
135         if (!tf->tf_cs) { /* sysenter TF.  esp and eip are in other regs. */
136                 tf->tf_esp = tf->tf_regs.reg_ebp;
137                 tf->tf_eip = tf->tf_regs.reg_edx;
138         }
139         /* The stuff we need to write will be below the current stack of the utf */
140         rst = (struct restart_helper*)((void*)tf->tf_esp -
141                                        sizeof(struct restart_helper));
142         /* Fill in the info we'll need later */
143         rst->notif_disab_loc = (uint32_t)&vcpd->notif_disabled;
144         rst->eax_save = 0;                      /* avoid bugs */
145         rst->eflags = tf->tf_eflags;
146         rst->eip = tf->tf_eip;
147
148         asm volatile ("movl %0,%%esp;        " /* jump esp to the utf */
149                       "popal;                " /* restore normal registers */
150                       "addl $0x24,%%esp;     " /* move to the esp slot in the tf */
151                       "popl %%esp;           " /* change to the utf's %esp */
152                       "subl $0x08,%%esp;     " /* move esp to below eax's slot */
153                       "pushl %%eax;          " /* save eax, will clobber soon */
154                                   "movl %2,%%eax;        " /* sizeof struct syscall */
155                                   "addl $0x0c,%%eax;     " /* more offset btw eax/notif_en_loc*/
156                       "subl %%eax,%%esp;     " /* move to notif_en_loc slot */
157                       "popl %%eax;           " /* load notif_disabled addr */
158                       "movb $0x00,(%%eax);   " /* enable notifications */
159                                   /* Here's where we differ from the regular pop_ros_tf().  We
160                                    * do the same pops/esp moves, just to keep things similar
161                                    * and simple, but don't do test, clear notif_pending, or
162                                    * call a syscall. */
163                                   /* From here down, we can get interrupted and restarted */
164                       "popl %%eax;           " /* get notif_pending status */
165                                   "popl %%eax;           " /* discard &sysc (on non-sc path) */
166                       "addl %2,%%esp;        " /* jump over the sysc (both paths) */
167                       "popl %%eax;           " /* restore tf's %eax */
168                                   "popfl;                " /* restore utf's eflags */
169                       "ret;                  " /* return to the new PC */
170                       :
171                       : "g"(tf), "i"(T_SYSCALL), "i"(sizeof(struct syscall))
172                       : "memory");
173 }
174
175 /* Save the current context/registers into the given tf, setting the pc of the
176  * tf to the end of this function.  You only need to save that which you later
177  * restore with pop_ros_tf(). */
178 static inline void save_ros_tf(struct user_trapframe *tf)
179 {
180         memset(tf, 0, sizeof(struct user_trapframe)); /* sanity */
181         /* set CS and make sure eflags is okay */
182         tf->tf_cs = GD_UT | 3;
183         tf->tf_eflags = 0x00000200; /* interrupts enabled.  bare minimum eflags. */
184         /* Save the regs and the future esp. */
185         asm volatile("movl %%esp,(%0);       " /* save esp in it's slot*/
186                      "pushl %%eax;           " /* temp save eax */
187                      "leal 1f,%%eax;         " /* get future eip */
188                      "movl %%eax,(%1);       " /* store future eip */
189                      "popl %%eax;            " /* restore eax */
190                      "movl %2,%%esp;         " /* move to the beginning of the tf */
191                      "addl $0x20,%%esp;      " /* move to after the push_regs */
192                      "pushal;                " /* save regs */
193                      "addl $0x44,%%esp;      " /* move to esp slot */
194                      "popl %%esp;            " /* restore esp */
195                      "1:                     " /* where this tf will restart */
196                      : 
197                      : "g"(&tf->tf_esp), "g"(&tf->tf_eip), "g"(tf)
198                      : "eax", "memory", "cc");
199 }
200
201 /* This assumes a user_tf looks like a regular kernel trapframe */
202 static __inline void
203 init_user_tf(struct user_trapframe *u_tf, uint32_t entry_pt, uint32_t stack_top)
204 {
205         memset(u_tf, 0, sizeof(struct user_trapframe));
206         u_tf->tf_eip = entry_pt;
207         u_tf->tf_cs = GD_UT | 3;
208         u_tf->tf_esp = stack_top;
209 }
210
211 /* Reading from the LDT.  Could also use %gs, but that would require including
212  * half of libc's TLS header.  Sparc will probably ignore the vcoreid, so don't
213  * rely on it too much.  The intent of it is vcoreid is the caller's vcoreid,
214  * and that vcoreid might be in the TLS of the caller (it will be for transition
215  * stacks) and we could avoid a trap on x86 to sys_getvcoreid(). */
216 static inline void *get_tls_desc(uint32_t vcoreid)
217 {
218         return (void*)(__procdata.ldt[vcoreid].sd_base_31_24 << 24 |
219                        __procdata.ldt[vcoreid].sd_base_23_16 << 16 |
220                        __procdata.ldt[vcoreid].sd_base_15_0);
221 }
222
223 /* passing in the vcoreid, since it'll be in TLS of the caller */
224 static inline void set_tls_desc(void *tls_desc, uint32_t vcoreid)
225 {
226         /* Keep this technique in sync with sysdeps/ros/i386/tls.h */
227         segdesc_t tmp = SEG(STA_W, (uint32_t)tls_desc, 0xffffffff, 3);
228         __procdata.ldt[vcoreid] = tmp;
229
230         /* GS is still the same (should be!), but it needs to be reloaded to force a
231          * re-read of the LDT. */
232         uint32_t gs = (vcoreid << 3) | 0x07;
233         asm volatile("movl %0,%%gs" : : "r" (gs) : "memory");
234
235         __vcoreid = vcoreid;
236 }
237
238 // this is how we get our thread id on entry.
239 #define __vcore_id_on_entry \
240 ({ \
241         register int temp asm ("eax"); \
242         temp; \
243 })
244
245 /* For debugging. */
246 #include <stdio.h>
247 static __inline void print_trapframe(struct user_trapframe *tf)
248 {
249         printf("[user] TRAP frame %08p\n", tf);
250         printf("  edi  0x%08x\n", tf->tf_regs.reg_edi);
251         printf("  esi  0x%08x\n", tf->tf_regs.reg_esi);
252         printf("  ebp  0x%08x\n", tf->tf_regs.reg_ebp);
253         printf("  oesp 0x%08x\n", tf->tf_regs.reg_oesp);
254         printf("  ebx  0x%08x\n", tf->tf_regs.reg_ebx);
255         printf("  edx  0x%08x\n", tf->tf_regs.reg_edx);
256         printf("  ecx  0x%08x\n", tf->tf_regs.reg_ecx);
257         printf("  eax  0x%08x\n", tf->tf_regs.reg_eax);
258         printf("  gs   0x----%04x\n", tf->tf_gs);
259         printf("  fs   0x----%04x\n", tf->tf_fs);
260         printf("  es   0x----%04x\n", tf->tf_es);
261         printf("  ds   0x----%04x\n", tf->tf_ds);
262         printf("  trap 0x%08x\n", tf->tf_trapno);
263         printf("  err  0x%08x\n", tf->tf_err);
264         printf("  eip  0x%08x\n", tf->tf_eip);
265         printf("  cs   0x----%04x\n", tf->tf_cs);
266         printf("  flag 0x%08x\n", tf->tf_eflags);
267         printf("  esp  0x%08x\n", tf->tf_esp);
268         printf("  ss   0x----%04x\n", tf->tf_ss);
269 }
270
271 #endif /* PARLIB_ARCH_VCORE_H */