Add parlib/common.h
[akaros.git] / user / parlib / include / x86 / vcore64.h
1 #ifndef PARLIB_ARCH_VCORE64_H
2 #define PARLIB_ARCH_VCORE64_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 RSP -> |   u_thread's old stuff   | the future %rsp, tf->tf_rsp
27  *               |   new rip                | 0x08 below %rsp (one slot is 0x08)
28  *               |   rflags space           | 0x10 below
29  *               |   rdi save space         | 0x18 below
30  *               |   *sysc ptr to syscall   | 0x20 below
31  *               |   notif_pending_loc      | 0x28 below
32  *               |   notif_disabled_loc     | 0x30 below
33  *
34  * The important thing is that it can handle a notification after it enables
35  * notifications, and when it gets resumed it can ultimately run the new
36  * context.  Enough state is saved in the running context and stack to continue
37  * running.
38  *
39  * Related to that is whether or not our stack pointer is sufficiently far down
40  * so that restarting *this* code won't clobber shit we need later.  The way we
41  * do this is that we do any "stack jumping" before we enable interrupts/notifs.
42  * These jumps are when we directly modify rsp, specifically in the down
43  * direction (subtracts).  Adds would be okay.
44  *
45  * Another 64-bit concern is the red-zone.  The AMD64 ABI allows the use of
46  * space below the stack pointer by regular programs.  If we allowed this, we
47  * would clobber that space when we do our TF restarts, much like with OSs and
48  * IRQ handlers.  Thus we have the cross compiler automatically disabling the
49  * redzone (-mno-red-zone is a built-in option).
50  *
51  * When compared to the 32 bit code, notice we use rdi, instead of eax, for our
52  * work.  This is because rdi is the arg0 of a syscall.  Using it saves us some
53  * extra moves, since we need to pop the *sysc before saving any other
54  * registers. */
55
56 /* Helper for writing the info we need later to the u_tf's stack.  Also, note
57  * this goes backwards, since memory reads up the stack. */
58 struct restart_helper {
59         void                                            *notif_disab_loc;
60         void                                            *notif_pend_loc;
61         struct syscall                          *sysc;
62         uint64_t                                        rdi_save;
63         uint64_t                                        rflags;
64         uint64_t                                        rip;
65 };
66
67 /* Static syscall, used for self-notifying.  We never wait on it, and we
68  * actually might submit it multiple times in parallel on different cores!
69  * While this may seem dangerous, the kernel needs to be able to handle this
70  * scenario.  It's also important that we never wait on this, since for all but
71  * the first call, the DONE flag will be set.  (Set once, then never reset) */
72 extern struct syscall vc_entry; /* in x86/vcore.c */
73
74 static inline void pop_hw_tf(struct hw_trapframe *tf, uint32_t vcoreid)
75 {
76         struct restart_helper *rst;
77         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
78         /* The stuff we need to write will be below the current stack of the utf */
79         rst = (struct restart_helper*)((void*)tf->tf_rsp -
80                                        sizeof(struct restart_helper));
81         /* Fill in the info we'll need later */
82         rst->notif_disab_loc = &vcpd->notif_disabled;
83         rst->notif_pend_loc = &vcpd->notif_pending;
84         rst->sysc = &vc_entry;
85         rst->rdi_save = 0;                      /* avoid bugs */
86         rst->rflags = tf->tf_rflags;
87         rst->rip = tf->tf_rip;
88
89         asm volatile ("movq %0, %%rsp;       " /* jump rsp to the utf */
90                       "popq %%rax;           " /* restore registers */
91                       "popq %%rbx;           "
92                       "popq %%rcx;           "
93                       "popq %%rdx;           "
94                       "popq %%rbp;           "
95                       "popq %%rsi;           "
96                       "popq %%rdi;           "
97                       "popq %%r8;            "
98                       "popq %%r9;            "
99                       "popq %%r10;           "
100                       "popq %%r11;           "
101                       "popq %%r12;           "
102                       "popq %%r13;           "
103                       "popq %%r14;           "
104                       "popq %%r15;           "
105                       "addq $0x28, %%rsp;    " /* move to the rsp slot in the tf */
106                       "popq %%rsp;           " /* change to the utf's %rsp */
107                       "subq $0x10, %%rsp;    " /* move rsp to below rdi's slot */
108                       "pushq %%rdi;          " /* save rdi, will clobber soon */
109                       "subq $0x18, %%rsp;    " /* move to notif_dis_loc slot */
110                       "popq %%rdi;           " /* load notif_disabled addr */
111                       "movb $0x00, (%%rdi);  " /* enable notifications */
112                                   /* Need a wrmb() here so the write of enable_notif can't pass
113                                    * the read of notif_pending (racing with a potential
114                                    * cross-core call with proc_notify()). */
115                                   "lock addq $0, (%%rdi);" /* LOCK is a CPU mb() */
116                                   /* From here down, we can get interrupted and restarted */
117                       "popq %%rdi;           " /* get notif_pending status loc */
118                       "testb $0x01, (%%rdi); " /* test if a notif is pending */
119                       "jz 1f;                " /* if not pending, skip syscall */
120                                   /* Actual syscall.  Note we don't wait on the async call */
121                       "popq %%rdi;           " /* &sysc, trap arg0 */
122                       "pushq %%rsi;          " /* save rax, will be trap arg1 */
123                       "pushq %%rax;          " /* save rax, will be trap ret */
124                       "movq $0x1, %%rsi;     " /* sending one async syscall: arg1 */
125                       "int %1;               " /* fire the syscall */
126                       "popq %%rax;           " /* restore regs after syscall */
127                       "popq %%rsi;           "
128                       "jmp 2f;               " /* skip 1:, already popped */
129                                   "1: addq $0x08, %%rsp; " /* discard &sysc (on non-sc path) */
130                       "2: popq %%rdi;        " /* restore tf's %rdi (both paths) */
131                                   "popfq;                " /* restore utf's rflags */
132                       "ret;                  " /* return to the new PC */
133                       :
134                       : "g"(&tf->tf_rax), "i"(T_SYSCALL)
135                       : "memory");
136 }
137
138 static inline void pop_sw_tf(struct sw_trapframe *sw_tf, uint32_t vcoreid)
139 {
140         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
141         /* Restore callee-saved FPU state.  We need to clear exceptions before
142          * reloading the FP CW, in case the new CW unmasks any.  We also need to
143          * reset the tag word to clear out the stack.
144          *
145          * The main issue here is that while our context was saved in an
146          * ABI-complaint manner, we may be starting up on a somewhat random FPU
147          * state.  Having gibberish in registers isn't a big deal, but some of the
148          * FP environment settings could cause trouble.  If fnclex; emms isn't
149          * enough, we could also save/restore the entire FP env with fldenv, or do
150          * an fninit before fldcw. */
151         asm volatile ("ldmxcsr %0" : : "m"(sw_tf->tf_mxcsr));
152         asm volatile ("fnclex; emms; fldcw %0" : : "m"(sw_tf->tf_fpucw));
153         /* Basic plan: restore all regs, off rcx as the sw_tf.  Switch to the new
154          * stack, save the PC so we can jump to it later.  Use clobberably
155          * registers for the locations of sysc, notif_dis, and notif_pend. Once on
156          * the new stack, we enable notifs, check if we missed one, and if so, self
157          * notify.  Note the syscall clobbers rax. */
158         asm volatile ("movq 0x00(%0), %%rbx; " /* restore regs */
159                       "movq 0x08(%0), %%rbp; "
160                       "movq 0x10(%0), %%r12; "
161                       "movq 0x18(%0), %%r13; "
162                       "movq 0x20(%0), %%r14; "
163                       "movq 0x28(%0), %%r15; "
164                       "movq 0x30(%0), %%r8;  " /* save rip in r8 */
165                       "movq 0x38(%0), %%rsp; " /* jump to future stack */
166                       "movb $0x00, (%2);     " /* enable notifications */
167                       /* Need a wrmb() here so the write of enable_notif can't pass
168                        * the read of notif_pending (racing with a potential
169                        * cross-core call with proc_notify()). */
170                       "lock addq $0, (%2);   " /* LOCK is a CPU mb() */
171                       /* From here down, we can get interrupted and restarted */
172                       "testb $0x01, (%3);    " /* test if a notif is pending */
173                       "jz 1f;                " /* if not pending, skip syscall */
174                       /* Actual syscall.  Note we don't wait on the async call.
175                        * &vc_entry is already in rdi (trap arg0). */
176                       "movq $0x1, %%rsi;     " /* sending one async syscall: arg1 */
177                       "int %4;               " /* fire the syscall */
178                       "1: jmp *%%r8;         " /* ret saved earlier */
179                       :
180                       : "c"(&sw_tf->tf_rbx),
181                         "D"(&vc_entry),
182                         "S"(&vcpd->notif_disabled),
183                         "d"(&vcpd->notif_pending),
184                         "i"(T_SYSCALL)
185                       : "memory");
186 }
187
188 /* Pops a user context, reanabling notifications at the same time.  A Userspace
189  * scheduler can call this when transitioning off the transition stack.
190  *
191  * At some point in vcore context before calling this, you need to clear
192  * notif_pending (do this by calling handle_events()).  As a potential
193  * optimization, consider clearing the notif_pending flag / handle_events again
194  * (right before popping), right before calling this.  If notif_pending is not
195  * clear, this will self_notify this core, since it should be because we missed
196  * a notification message while notifs were disabled. */
197 static inline void pop_user_ctx(struct user_context *ctx, uint32_t vcoreid)
198 {
199         if (ctx->type == ROS_HW_CTX)
200                 pop_hw_tf(&ctx->tf.hw_tf, vcoreid);
201         else
202                 pop_sw_tf(&ctx->tf.sw_tf, vcoreid);
203 }
204
205 /* Like the regular pop_user_ctx, but this one doesn't check or clear
206  * notif_pending.  The only case where we use this is when an IRQ/notif
207  * interrupts a uthread that is in the process of disabling notifs. */
208 static inline void pop_user_ctx_raw(struct user_context *ctx, uint32_t vcoreid)
209 {
210         struct hw_trapframe *tf = &ctx->tf.hw_tf;
211         assert(ctx->type == ROS_HW_CTX);
212         struct restart_helper *rst;
213         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
214         /* The stuff we need to write will be below the current stack of the utf */
215         rst = (struct restart_helper*)((void*)tf->tf_rsp -
216                                        sizeof(struct restart_helper));
217         /* Fill in the info we'll need later */
218         rst->notif_disab_loc = &vcpd->notif_disabled;
219         rst->rdi_save = 0;                      /* avoid bugs */
220         rst->rflags = tf->tf_rflags;
221         rst->rip = tf->tf_rip;
222
223         asm volatile ("movq %0, %%rsp;       " /* jump esp to the utf */
224                       "popq %%rax;           " /* restore registers */
225                       "popq %%rbx;           "
226                       "popq %%rcx;           "
227                       "popq %%rdx;           "
228                       "popq %%rbp;           "
229                       "popq %%rsi;           "
230                       "popq %%rdi;           "
231                       "popq %%r8;            "
232                       "popq %%r9;            "
233                       "popq %%r10;           "
234                       "popq %%r11;           "
235                       "popq %%r12;           "
236                       "popq %%r13;           "
237                       "popq %%r14;           "
238                       "popq %%r15;           "
239                       "addq $0x28, %%rsp;    " /* move to the rsp slot in the tf */
240                       "popq %%rsp;           " /* change to the utf's %rsp */
241                       "subq $0x10, %%rsp;    " /* move rsp to below rdi's slot */
242                       "pushq %%rdi;          " /* save rdi, will clobber soon */
243                       "subq $0x18, %%rsp;    " /* move to notif_dis_loc slot */
244                       "popq %%rdi;           " /* load notif_disabled addr */
245                       "movb $0x00, (%%rdi);  " /* enable notifications */
246                                   /* Here's where we differ from the regular pop_user_ctx().
247                                    * We need to adjust rsp and whatnot, but don't do test,
248                                    * clear notif_pending, or call a syscall. */
249                                   /* From here down, we can get interrupted and restarted */
250                       "addq $0x10, %%rsp;    " /* move to rdi save slot */
251                       "popq %%rdi;           " /* restore tf's %rdi */
252                                   "popfq;                " /* restore utf's rflags */
253                       "ret;                  " /* return to the new PC */
254                       :
255                       : "g"(&tf->tf_rax)
256                       : "memory");
257 }
258
259 /* Save's a SW context, setting the PC to the end of this function.  We only
260  * save callee-saved registers (of the sysv abi).  The compiler knows to save
261  * the others via the input/clobber lists.
262  *
263  * Callers of this function need to have at least one
264  * 'calling-convention-compliant' function call between this and any floating
265  * point, so that the compiler saves any caller-saved FP before getting to
266  * here.
267  *
268  * To some extent, TLS is 'callee-saved', in that no one ever expects it to
269  * change.  We handle uthread TLS changes separately, since we often change to
270  * them early to set some variables.  Arguably we should do this different. */
271 static inline void save_user_ctx(struct user_context *ctx)
272 {
273         struct sw_trapframe *sw_tf = &ctx->tf.sw_tf;
274         long dummy;
275         ctx->type = ROS_SW_CTX;
276         asm volatile ("stmxcsr %0" : "=m"(sw_tf->tf_mxcsr));
277         asm volatile ("fnstcw %0" : "=m"(sw_tf->tf_fpucw));
278         /* Pretty simple: save all the regs, IAW the sys-v ABI */
279         asm volatile("mov %%rsp, 0x48(%0);   " /* save rsp in its slot*/
280                      "leaq 1f(%%rip), %%rax; " /* get future rip */
281                      "mov %%rax, 0x40(%0);   " /* save rip in its slot*/
282                      "mov %%r15, 0x38(%0);   "
283                      "mov %%r14, 0x30(%0);   "
284                      "mov %%r13, 0x28(%0);   "
285                      "mov %%r12, 0x20(%0);   "
286                      "mov %%rbp, 0x18(%0);   "
287                      "mov %%rbx, 0x10(%0);   "
288                      "1:                     " /* where this tf will restart */
289                      : "=D"(dummy) /* force clobber for rdi */
290                                  : "D"(sw_tf)
291                      : "rax", "rcx", "rdx", "rsi", "r8", "r9", "r10", "r11",
292                        "memory", "cc");
293 } __attribute__((always_inline, returns_twice))
294
295 /* The old version, kept around for testing */
296 /* Hasn't been used yet for 64 bit.  If you use this, it's worth checking to
297  * make sure rax isn't selected for 0, 1, or 2. (and we probably don't need to
298  * save rax in the beginning) */
299 static inline void save_user_ctx_hw(struct user_context *ctx)
300 {
301         struct hw_trapframe *tf = &ctx->tf.hw_tf;
302         ctx->type = ROS_HW_CTX;
303         memset(tf, 0, sizeof(struct hw_trapframe)); /* sanity */
304         /* set CS and make sure eflags is okay */
305         tf->tf_cs = GD_UT | 3;
306         tf->tf_rflags = 0x200; /* interrupts enabled.  bare minimum rflags. */
307         /* Save the regs and the future rsp. */
308         asm volatile("movq %%rsp, (%0);      " /* save rsp in it's slot*/
309                      "pushq %%rax;           " /* temp save rax */
310                      "leaq 1f, %%rax;        " /* get future rip */
311                      "movq %%rax, (%1);      " /* store future rip */
312                      "popq %%rax;            " /* restore rax */
313                      "movq %2, %%rsp;        " /* move to the rax slot of the tf */
314                      "addl $0x78,%%esp;      " /* move to just past r15 */
315                      "pushq %%r15;           " /* save regs */
316                      "pushq %%r14;           "
317                      "pushq %%r13;           "
318                      "pushq %%r12;           "
319                      "pushq %%r11;           "
320                      "pushq %%r10;           "
321                      "pushq %%r9;            "
322                      "pushq %%r8;            "
323                      "pushq %%rdi;           "
324                      "pushq %%rsi;           "
325                      "pushq %%rbp;           "
326                      "pushq %%rdx;           "
327                      "pushq %%rcx;           "
328                      "pushq %%rbx;           "
329                      "pushq %%rax;           "
330                      "addq $0xa0, %%rsp;     " /* move to rsp slot */
331                      "popq %%rsp;            " /* restore saved/original rsp */
332                      "1:                     " /* where this tf will restart */
333                      : 
334                      : "g"(&tf->tf_rsp), "g"(&tf->tf_rip), "g"(tf->tf_rax)
335                      : "rax", "memory", "cc");
336 } __attribute__((always_inline, returns_twice))
337
338 static inline void init_user_ctx(struct user_context *ctx, uintptr_t entry_pt,
339                                  uintptr_t stack_top)
340 {
341         struct sw_trapframe *sw_tf = &ctx->tf.sw_tf;
342         ctx->type = ROS_SW_CTX;
343         /* Stack pointers in a fresh stackframe need to be such that adding or
344          * subtracting 8 will result in 16 byte alignment (AMD64 ABI).  The reason
345          * is so that input arguments (on the stack) are 16 byte aligned.  The
346          * extra 8 bytes is the retaddr, pushed on the stack.  Compilers know they
347          * can subtract 8 to get 16 byte alignment for instructions like movaps. */
348         sw_tf->tf_rsp = ROUNDDOWN(stack_top, 16) - 8;
349         sw_tf->tf_rip = entry_pt;
350         sw_tf->tf_rbp = 0;      /* for potential backtraces */
351         /* No need to bother with setting the other GP registers; the called
352          * function won't care about their contents. */
353         sw_tf->tf_mxcsr = 0x00001f80;   /* x86 default mxcsr */
354         sw_tf->tf_fpucw = 0x037f;               /* x86 default FP CW */
355 }
356
357 // this is how we get our thread id on entry.
358 #define __vcore_id_on_entry \
359 ({ \
360         register int temp asm ("rbx"); \
361         temp; \
362 })
363
364 /* For debugging. */
365 #include <stdio.h>
366 static void print_hw_tf(struct hw_trapframe *hw_tf)
367 {
368         printf("[user] HW TRAP frame 0x%016x\n", hw_tf);
369         printf("  rax  0x%016lx\n",           hw_tf->tf_rax);
370         printf("  rbx  0x%016lx\n",           hw_tf->tf_rbx);
371         printf("  rcx  0x%016lx\n",           hw_tf->tf_rcx);
372         printf("  rdx  0x%016lx\n",           hw_tf->tf_rdx);
373         printf("  rbp  0x%016lx\n",           hw_tf->tf_rbp);
374         printf("  rsi  0x%016lx\n",           hw_tf->tf_rsi);
375         printf("  rdi  0x%016lx\n",           hw_tf->tf_rdi);
376         printf("  r8   0x%016lx\n",           hw_tf->tf_r8);
377         printf("  r9   0x%016lx\n",           hw_tf->tf_r9);
378         printf("  r10  0x%016lx\n",           hw_tf->tf_r10);
379         printf("  r11  0x%016lx\n",           hw_tf->tf_r11);
380         printf("  r12  0x%016lx\n",           hw_tf->tf_r12);
381         printf("  r13  0x%016lx\n",           hw_tf->tf_r13);
382         printf("  r14  0x%016lx\n",           hw_tf->tf_r14);
383         printf("  r15  0x%016lx\n",           hw_tf->tf_r15);
384         printf("  trap 0x%08x\n",             hw_tf->tf_trapno);
385         printf("  gsbs 0x%016lx\n",           hw_tf->tf_gsbase);
386         printf("  fsbs 0x%016lx\n",           hw_tf->tf_fsbase);
387         printf("  err  0x--------%08x\n",     hw_tf->tf_err);
388         printf("  rip  0x%016lx\n",           hw_tf->tf_rip);
389         printf("  cs   0x------------%04x\n", hw_tf->tf_cs);
390         printf("  flag 0x%016lx\n",           hw_tf->tf_rflags);
391         printf("  rsp  0x%016lx\n",           hw_tf->tf_rsp);
392         printf("  ss   0x------------%04x\n", hw_tf->tf_ss);
393 }
394
395 static void print_sw_tf(struct sw_trapframe *sw_tf)
396 {
397         printf("[user] SW TRAP frame 0x%016p\n", sw_tf);
398         printf("  rbx  0x%016lx\n",           sw_tf->tf_rbx);
399         printf("  rbp  0x%016lx\n",           sw_tf->tf_rbp);
400         printf("  r12  0x%016lx\n",           sw_tf->tf_r12);
401         printf("  r13  0x%016lx\n",           sw_tf->tf_r13);
402         printf("  r14  0x%016lx\n",           sw_tf->tf_r14);
403         printf("  r15  0x%016lx\n",           sw_tf->tf_r15);
404         printf("  gsbs 0x%016lx\n",           sw_tf->tf_gsbase);
405         printf("  fsbs 0x%016lx\n",           sw_tf->tf_fsbase);
406         printf("  rip  0x%016lx\n",           sw_tf->tf_rip);
407         printf("  rsp  0x%016lx\n",           sw_tf->tf_rsp);
408         printf(" mxcsr 0x%08x\n",             sw_tf->tf_mxcsr);
409         printf(" fpucw 0x%04x\n",             sw_tf->tf_fpucw);
410 }
411
412 static void print_user_context(struct user_context *ctx)
413 {
414         if (ctx->type == ROS_HW_CTX)
415                 print_hw_tf(&ctx->tf.hw_tf);
416         else if (ctx->type == ROS_SW_CTX)
417                 print_sw_tf(&ctx->tf.sw_tf);
418         else
419                 printf("Unknown context type %d\n", ctx->type);
420 }
421
422 static bool has_refl_fault(struct user_context *ctx)
423 {
424         return ctx->tf.hw_tf.tf_padding3 == ROS_ARCH_REFL_ID;
425 }
426
427 static void clear_refl_fault(struct user_context *ctx)
428 {
429         ctx->tf.hw_tf.tf_padding3 = 0;
430 }
431
432 static unsigned int __arch_refl_get_nr(struct user_context *ctx)
433 {
434         return ctx->tf.hw_tf.tf_trapno;
435 }
436
437 static unsigned int __arch_refl_get_err(struct user_context *ctx)
438 {
439         return ctx->tf.hw_tf.tf_err;
440 }
441
442 static unsigned long __arch_refl_get_aux(struct user_context *ctx)
443 {
444         return ((unsigned long)ctx->tf.hw_tf.tf_padding5 << 32) |
445                ctx->tf.hw_tf.tf_padding4;
446 }
447
448 __END_DECLS
449
450 #endif /* PARLIB_ARCH_VCORE64_H */