Add parlib/common.h
[akaros.git] / user / parlib / include / x86 / vcore32.h
1 #ifndef PARLIB_ARCH_VCORE32_H
2 #define PARLIB_ARCH_VCORE32_H
3
4 #ifndef PARLIB_ARCH_VCORE_H
5 #error "Do not include include vcore32.h directly"
6 #endif
7
8 #include <parlib/common.h>
9 #include <ros/trapframe.h>
10 #include <ros/procdata.h>
11 #include <ros/syscall.h>
12 #include <ros/arch/mmu.h>
13 #include <sys/tls.h>
14
15 __BEGIN_DECLS
16
17 /* Here's how the HW popping works:  It sets up the future stack pointer to
18  * have extra stuff after it, and then it pops the registers, then pops the new
19  * context's stack pointer.  Then it uses the extra stuff (the new PC is on the
20  * stack, the location of notif_disabled, and a clobbered work register) to
21  * enable notifs, make sure notif IPIs weren't pending, restore the work reg,
22  * and then "ret".
23  *
24  * This is what the target uthread's stack will look like (growing down):
25  *
26  * Target ESP -> |   u_thread's old stuff   | the future %esp, tf->tf_esp
27  *               |   new eip                | 0x04 below %esp (one slot is 0x04)
28  *               |   eflags space           | 0x08 below
29  *               |   eax save space         | 0x0c below
30  *               |   actual syscall         | 0x10 below (0x30 space)
31  *               |   *sysc ptr to syscall   | 0x40 below (0x10 + 0x30)
32  *               |   notif_pending_loc      | 0x44 below (0x10 + 0x30)
33  *               |   notif_disabled_loc     | 0x48 below (0x10 + 0x30)
34  *
35  * The important thing is that it can handle a notification after it enables
36  * notifications, and when it gets resumed it can ultimately run the new
37  * context.  Enough state is saved in the running context and stack to continue
38  * running.
39  *
40  * Related to that is whether or not our stack pointer is sufficiently far down
41  * so that restarting *this* code won't clobber shit we need later.  The way we
42  * do this is that we do any "stack jumping" before we enable interrupts/notifs.
43  * These jumps are when we directly modify esp, specifically in the down
44  * direction (subtracts).  Adds would be okay.
45  *
46  * Another related concern is the storage for sysc.  It used to be on the
47  * vcore's stack, but if an interrupt comes in before we use it, we trash the
48  * vcore's stack (and thus the storage for sysc!).  Instead, we put it on the
49  * stack of the user tf.  Moral: don't touch a vcore's stack with notifs
50  * enabled. */
51
52 /* Helper for writing the info we need later to the u_tf's stack.  Note, this
53  * could get fucked if the struct syscall isn't a multiple of 4-bytes.  Also,
54  * note this goes backwards, since memory reads up the stack. */
55 struct restart_helper {
56         uint32_t                                        notif_disab_loc;
57         uint32_t                                        notif_pend_loc;
58         struct syscall                          *sysc;
59         struct syscall                          local_sysc;     /* unused for now */
60         uint32_t                                        eax_save;
61         uint32_t                                        eflags;
62         uint32_t                                        eip;
63 };
64
65 /* Static syscall, used for self-notifying.  We never wait on it, and we
66  * actually might submit it multiple times in parallel on different cores!
67  * While this may seem dangerous, the kernel needs to be able to handle this
68  * scenario.  It's also important that we never wait on this, since for all but
69  * the first call, the DONE flag will be set.  (Set once, then never reset) */
70 extern struct syscall vc_entry; /* in x86/vcore.c */
71
72 static inline void pop_hw_tf(struct hw_trapframe *tf, uint32_t vcoreid)
73 {
74         struct restart_helper *rst;
75         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
76         if (!tf->tf_cs) { /* sysenter TF.  esp and eip are in other regs. */
77                 tf->tf_esp = tf->tf_regs.reg_ebp;
78                 tf->tf_eip = tf->tf_regs.reg_edx;
79         }
80         /* The stuff we need to write will be below the current stack of the utf */
81         rst = (struct restart_helper*)((void*)tf->tf_esp -
82                                        sizeof(struct restart_helper));
83         /* Fill in the info we'll need later */
84         rst->notif_disab_loc = (uint32_t)&vcpd->notif_disabled;
85         rst->notif_pend_loc = (uint32_t)&vcpd->notif_pending;
86         rst->sysc = &vc_entry;
87         rst->eax_save = 0;                      /* avoid bugs */
88         rst->eflags = tf->tf_eflags;
89         rst->eip = tf->tf_eip;
90
91         asm volatile ("movl %0,%%esp;        " /* jump esp to the utf */
92                       "popal;                " /* restore normal registers */
93                       "addl $0x24,%%esp;     " /* move to the esp slot in the tf */
94                       "popl %%esp;           " /* change to the utf's %esp */
95                       "subl $0x08,%%esp;     " /* move esp to below eax's slot */
96                       "pushl %%eax;          " /* save eax, will clobber soon */
97                                   "movl %2,%%eax;        " /* sizeof struct syscall */
98                                   "addl $0x0c,%%eax;     " /* more offset btw eax/notif_en_loc*/
99                       "subl %%eax,%%esp;     " /* move to notif_en_loc slot */
100                       "popl %%eax;           " /* load notif_disabled addr */
101                       "movb $0x00,(%%eax);   " /* enable notifications */
102                                   /* Need a wrmb() here so the write of enable_notif can't pass
103                                    * the read of notif_pending (racing with a potential
104                                    * cross-core call with proc_notify()). */
105                                   "lock addl $0,(%%esp); " /* LOCK is a CPU mb() */
106                                   /* From here down, we can get interrupted and restarted */
107                       "popl %%eax;           " /* get notif_pending status */
108                       "testb $0x01,(%%eax);  " /* test if a notif is pending */
109                       "jz 1f;                " /* if not pending, skip syscall */
110                                   /* Actual syscall.  Note we don't wait on the async call */
111                       "popl %%eax;           " /* &sysc, trap arg0 */
112                       "pushl %%edx;          " /* save edx, will be trap arg1 */
113                       "movl $0x1,%%edx;      " /* sending one async syscall: arg1 */
114                       "int %1;               " /* fire the syscall */
115                       "popl %%edx;           " /* restore regs after syscall */
116                       "jmp 2f;               " /* skip 1:, already popped */
117                                   "1: popl %%eax;        " /* discard &sysc (on non-sc path) */
118                       "2: addl %2,%%esp;     " /* jump over the sysc (both paths) */
119                       "popl %%eax;           " /* restore tf's %eax */
120                                   "popfl;                " /* restore utf's eflags */
121                       "ret;                  " /* return to the new PC */
122                       :
123                       : "g"(tf), "i"(T_SYSCALL), "i"(sizeof(struct syscall))
124                       : "memory");
125 }
126
127 static inline void pop_sw_tf(struct sw_trapframe *sw_tf, uint32_t vcoreid)
128 {
129         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
130
131         /* Restore callee-saved FPU state.  We need to clear exceptions before
132          * reloading the FP CW, in case the new CW unmasks any.  We also need to
133          * reset the tag word to clear out the stack.
134          *
135          * The main issue here is that while our context was saved in an
136          * ABI-complaint manner, we may be starting up on a somewhat random FPU
137          * state.  Having gibberish in registers isn't a big deal, but some of the
138          * FP environment settings could cause trouble.  If fnclex; emms isn't
139          * enough, we could also save/restore the entire FP env with fldenv, or do
140          * an fninit before fldcw. */
141         asm volatile ("ldmxcsr %0" : : "m"(sw_tf->tf_mxcsr));
142         asm volatile ("fnclex; emms; fldcw %0" : : "m"(sw_tf->tf_fpucw));
143         /* Basic plan: restore all regs, off ecx as the sw_tf.  Switch to the new
144          * stack, push the PC so we can pop it later.  Use eax and edx for the
145          * locations of sysc and vcpd.  Once on the new stack, we enable notifs,
146          * check if we missed one, and if so, self notify. */
147         asm volatile ("movl 0x00(%0),%%ebp;  " /* restore regs */
148                       "movl 0x04(%0),%%ebx;  "
149                       "movl 0x08(%0),%%esi;  "
150                       "movl 0x0c(%0),%%edi;  "
151                       "movl 0x10(%0),%%esp;  " /* jump to future stack */
152                       "pushl 0x14(%0);       " /* save PC for future ret */
153                       "movl %2,%%ecx;        " /* vcpd loc into ecx */
154                       "addl %4,%%ecx;        " /* notif_disabled loc into ecx */
155                       "movb $0x00,(%%ecx);   " /* enable notifications */
156                       /* Need a wrmb() here so the write of enable_notif can't pass
157                        * the read of notif_pending (racing with a potential
158                        * cross-core call with proc_notify()). */
159                       "lock addl $0,(%%esp); " /* LOCK is a CPU mb() */
160                       /* From here down, we can get interrupted and restarted */
161                       "movl %2,%%ecx;        " /* vcpd loc into ecx */
162                       "addl %5,%%ecx;        " /* notif_pending loc into ecx */
163                       "testb $0x01,(%%ecx);  " /* test if a notif is pending */
164                       "jz 1f;                " /* if not pending, skip syscall */
165                       /* Actual syscall.  Note we don't wait on the async call.
166                        * &sysc is already in eax (trap arg0). */
167                       "movl $0x1,%%edx;      " /* sending one async syscall: arg1 */
168                       "int %3;               " /* fire the syscall */
169                       "1: ret;               " /* retaddr was pushed earlier */
170                       :
171                       : "c"(sw_tf),
172                         "a"(&vc_entry),
173                         "d"(vcpd),
174                         "i"(T_SYSCALL),
175                         "i"(offsetof(struct preempt_data, notif_disabled)),
176                         "i"(offsetof(struct preempt_data, notif_pending))
177                       : "memory");
178 }
179
180 /* Pops a user context, reanabling notifications at the same time.  A Userspace
181  * scheduler can call this when transitioning off the transition stack.
182  *
183  * At some point in vcore context before calling this, you need to clear
184  * notif_pending (do this by calling handle_events()).  As a potential
185  * optimization, consider clearing the notif_pending flag / handle_events again
186  * (right before popping), right before calling this.  If notif_pending is not
187  * clear, this will self_notify this core, since it should be because we missed
188  * a notification message while notifs were disabled. */
189 static inline void pop_user_ctx(struct user_context *ctx, uint32_t vcoreid)
190 {
191         if (ctx->type == ROS_HW_CTX)
192                 pop_hw_tf(&ctx->tf.hw_tf, vcoreid);
193         else
194                 pop_sw_tf(&ctx->tf.sw_tf, vcoreid);
195 }
196
197 /* Like the regular pop_user_ctx, but this one doesn't check or clear
198  * notif_pending.  The only case where we use this is when an IRQ/notif
199  * interrupts a uthread that is in the process of disabling notifs. */
200 static inline void pop_user_ctx_raw(struct user_context *ctx, uint32_t vcoreid)
201 {
202         struct hw_trapframe *tf = &ctx->tf.hw_tf;
203         assert(ctx->type == ROS_HW_CTX);
204         struct restart_helper *rst;
205         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
206         if (!tf->tf_cs) { /* sysenter TF.  esp and eip are in other regs. */
207                 tf->tf_esp = tf->tf_regs.reg_ebp;
208                 tf->tf_eip = tf->tf_regs.reg_edx;
209         }
210         /* The stuff we need to write will be below the current stack of the utf */
211         rst = (struct restart_helper*)((void*)tf->tf_esp -
212                                        sizeof(struct restart_helper));
213         /* Fill in the info we'll need later */
214         rst->notif_disab_loc = (uint32_t)&vcpd->notif_disabled;
215         rst->eax_save = 0;                      /* avoid bugs */
216         rst->eflags = tf->tf_eflags;
217         rst->eip = tf->tf_eip;
218
219         asm volatile ("movl %0,%%esp;        " /* jump esp to the utf */
220                       "popal;                " /* restore normal registers */
221                       "addl $0x24,%%esp;     " /* move to the esp slot in the tf */
222                       "popl %%esp;           " /* change to the utf's %esp */
223                       "subl $0x08,%%esp;     " /* move esp to below eax's slot */
224                       "pushl %%eax;          " /* save eax, will clobber soon */
225                                   "movl %2,%%eax;        " /* sizeof struct syscall */
226                                   "addl $0x0c,%%eax;     " /* more offset btw eax/notif_en_loc*/
227                       "subl %%eax,%%esp;     " /* move to notif_en_loc slot */
228                       "popl %%eax;           " /* load notif_disabled addr */
229                       "movb $0x00,(%%eax);   " /* enable notifications */
230                                   /* Here's where we differ from the regular pop_user_ctx().
231                                    * We do the same pops/esp moves, just to keep things similar
232                                    * and simple, but don't do test, clear notif_pending, or
233                                    * call a syscall. */
234                                   /* From here down, we can get interrupted and restarted */
235                       "popl %%eax;           " /* get notif_pending status */
236                                   "popl %%eax;           " /* discard &sysc (on non-sc path) */
237                       "addl %2,%%esp;        " /* jump over the sysc (both paths) */
238                       "popl %%eax;           " /* restore tf's %eax */
239                                   "popfl;                " /* restore utf's eflags */
240                       "ret;                  " /* return to the new PC */
241                       :
242                       : "g"(tf), "i"(T_SYSCALL), "i"(sizeof(struct syscall))
243                       : "memory");
244 }
245
246 /* Save's a SW context, setting the PC to the end of this function.  We only
247  * save callee-saved registers (of the sysv abi).  The compiler knows to save
248  * the others via the input/clobber lists.
249  *
250  * Callers of this function need to have at least one
251  * 'calling-convention-compliant' function call between this and any floating
252  * point, so that the compiler saves any caller-saved FP before getting to
253  * here.
254  *
255  * To some extent, TLS is 'callee-saved', in that no one ever expects it to
256  * change.  We handle uthread TLS changes separately, since we often change to
257  * them early to set some variables.  Arguably we should do this different. */
258 static inline void save_user_ctx(struct user_context *ctx)
259 {
260         struct sw_trapframe *sw_tf = &ctx->tf.sw_tf;
261         long dummy;
262         ctx->type = ROS_SW_CTX;
263         asm volatile ("stmxcsr %0" : "=m"(sw_tf->tf_mxcsr));
264         asm volatile ("fnstcw %0" : "=m"(sw_tf->tf_fpucw));
265         /* Pretty simple: save all the regs, IAW the sys-v ABI */
266         asm volatile ("movl %%ebp,0x00(%0);   "
267                       "movl %%ebx,0x04(%0);   "
268                       "movl %%esi,0x08(%0);   "
269                       "movl %%edi,0x0c(%0);   "
270                       "movl %%esp,0x10(%0);   "
271                       "leal 1f,%%eax;         " /* get future eip */
272                       "movl %%eax,0x14(%0);   "
273                       "1:                     " /* where this tf will restart */
274                       : "=c"(dummy)     /* force clobber for ecx */
275                       : "c"(sw_tf)
276                       : "eax", "edx", "memory", "cc");
277 } __attribute__((always_inline, returns_twice))
278
279 /* The old version, kept around for testing */
280 static inline void save_user_ctx_hw(struct user_context *ctx)
281 {
282         struct hw_trapframe *tf = &ctx->tf.hw_tf;
283         ctx->type = ROS_HW_CTX;
284         memset(tf, 0, sizeof(struct hw_trapframe)); /* sanity */
285         /* set CS and make sure eflags is okay */
286         tf->tf_cs = GD_UT | 3;
287         tf->tf_eflags = 0x00000200; /* interrupts enabled.  bare minimum eflags. */
288         /* Save the regs and the future esp. */
289         asm volatile("movl %%esp,(%0);       " /* save esp in it's slot*/
290                      "pushl %%eax;           " /* temp save eax */
291                      "leal 1f,%%eax;         " /* get future eip */
292                      "movl %%eax,(%1);       " /* store future eip */
293                      "popl %%eax;            " /* restore eax */
294                      "movl %2,%%esp;         " /* move to the beginning of the tf */
295                      "addl $0x20,%%esp;      " /* move to after the push_regs */
296                      "pushal;                " /* save regs */
297                      "addl $0x44,%%esp;      " /* move to esp slot */
298                      "popl %%esp;            " /* restore esp */
299                      "1:                     " /* where this tf will restart */
300                      : 
301                      : "g"(&tf->tf_esp), "g"(&tf->tf_eip), "g"(tf)
302                      : "eax", "memory", "cc");
303 } __attribute__((always_inline, returns_twice))
304
305 static inline void init_user_ctx(struct user_context *ctx, uint32_t entry_pt,
306                                  uint32_t stack_top)
307 {
308         struct sw_trapframe *sw_tf = &ctx->tf.sw_tf;
309         ctx->type = ROS_SW_CTX;
310         /* No need to bother with setting the other GP registers; the called
311          * function won't care about their contents. */
312         sw_tf->tf_esp = stack_top;
313         sw_tf->tf_eip = entry_pt;
314         sw_tf->tf_mxcsr = 0x00001f80;   /* x86 default mxcsr */
315         sw_tf->tf_fpucw = 0x037f;               /* x86 default FP CW */
316 }
317
318 // this is how we get our thread id on entry.
319 #define __vcore_id_on_entry \
320 ({ \
321         register int temp asm ("ebx"); \
322         temp; \
323 })
324
325 /* For debugging. */
326 #include <stdio.h>
327 static void print_hw_tf(struct hw_trapframe *tf)
328 {
329         printf("[user] HW TRAP frame %08p\n", tf);
330         printf("  edi  0x%08x\n", tf->tf_regs.reg_edi);
331         printf("  esi  0x%08x\n", tf->tf_regs.reg_esi);
332         printf("  ebp  0x%08x\n", tf->tf_regs.reg_ebp);
333         printf("  oesp 0x%08x\n", tf->tf_regs.reg_oesp);
334         printf("  ebx  0x%08x\n", tf->tf_regs.reg_ebx);
335         printf("  edx  0x%08x\n", tf->tf_regs.reg_edx);
336         printf("  ecx  0x%08x\n", tf->tf_regs.reg_ecx);
337         printf("  eax  0x%08x\n", tf->tf_regs.reg_eax);
338         printf("  gs   0x----%04x\n", tf->tf_gs);
339         printf("  fs   0x----%04x\n", tf->tf_fs);
340         printf("  es   0x----%04x\n", tf->tf_es);
341         printf("  ds   0x----%04x\n", tf->tf_ds);
342         printf("  trap 0x%08x\n", tf->tf_trapno);
343         printf("  err  0x%08x\n", tf->tf_err);
344         printf("  eip  0x%08x\n", tf->tf_eip);
345         printf("  cs   0x----%04x\n", tf->tf_cs);
346         printf("  flag 0x%08x\n", tf->tf_eflags);
347         printf("  esp  0x%08x\n", tf->tf_esp);
348         printf("  ss   0x----%04x\n", tf->tf_ss);
349 }
350
351 static void print_sw_tf(struct sw_trapframe *sw_tf)
352 {
353         printf("[user] SW TRAP frame %08p\n", sw_tf);
354         printf("  ebp  0x%08x\n", sw_tf->tf_ebp);
355         printf("  ebx  0x%08x\n", sw_tf->tf_ebx);
356         printf("  esi  0x%08x\n", sw_tf->tf_esi);
357         printf("  edi  0x%08x\n", sw_tf->tf_edi);
358         printf("  esp  0x%08x\n", sw_tf->tf_esp);
359         printf("  eip  0x%08x\n", sw_tf->tf_eip);
360         printf(" mxcsr 0x%08x\n", sw_tf->tf_mxcsr);
361         printf(" fpucw 0x----%04x\n", sw_tf->tf_fpucw);
362 }
363
364 static void print_user_context(struct user_context *ctx)
365 {
366         if (ctx->type == ROS_HW_CTX)
367                 print_hw_tf(&ctx->tf.hw_tf);
368         else if (ctx->type == ROS_SW_CTX)
369                 print_sw_tf(&ctx->tf.sw_tf);
370         else
371                 printf("Unknown context type %d\n", ctx->type);
372 }
373
374 static bool has_refl_fault(struct user_context *ctx)
375 {
376         return ctx->tf.hw_tf.tf_padding3 == ROS_ARCH_REFL_ID;
377 }
378
379 static void clear_refl_fault(struct user_context *ctx)
380 {
381         ctx->tf.hw_tf.tf_padding3 = 0;
382 }
383
384 static unsigned int __arch_refl_get_nr(struct user_context *ctx)
385 {
386         return ctx->tf.hw_tf.tf_trapno;
387 }
388
389 static unsigned int __arch_refl_get_err(struct user_context *ctx)
390 {
391         return ctx->tf.hw_tf.tf_err;
392 }
393
394 static unsigned long __arch_refl_get_aux(struct user_context *ctx)
395 {
396         return ctx->tf.hw_tf.tf_regs.reg_oesp;
397 }
398
399 __END_DECLS
400
401 #endif /* PARLIB_ARCH_VCORE32_H */