a6778414cce2e8675d39f89438f02bbe5f717ba7
[akaros.git] / user / parlib / x86 / vcore.c
1 #include <ros/syscall.h>
2 #include <parlib/vcore.h>
3 #include <parlib/stdio.h>
4 #include <stdlib.h>
5
6 /* Here's how the HW popping works:  It sets up the future stack pointer to
7  * have extra stuff after it, and then it pops the registers, then pops the new
8  * context's stack pointer.  Then it uses the extra stuff (the new PC is on the
9  * stack, the location of notif_disabled, and a clobbered work register) to
10  * enable notifs, make sure notif IPIs weren't pending, restore the work reg,
11  * and then "ret".
12  *
13  * This is what the target uthread's stack will look like (growing down):
14  *
15  * Target RSP -> |   u_thread's old stuff   | the future %rsp, tf->tf_rsp
16  *               |   new rip                | 0x08 below %rsp (one slot is 0x08)
17  *               |   rflags space           | 0x10 below
18  *               |   rdi save space         | 0x18 below
19  *               |   *sysc ptr to syscall   | 0x20 below
20  *               |   notif_pending_loc      | 0x28 below
21  *               |   notif_disabled_loc     | 0x30 below
22  *
23  * The important thing is that it can handle a notification after it enables
24  * notifications, and when it gets resumed it can ultimately run the new
25  * context.  Enough state is saved in the running context and stack to continue
26  * running.
27  *
28  * Related to that is whether or not our stack pointer is sufficiently far down
29  * so that restarting *this* code won't clobber shit we need later.  The way we
30  * do this is that we do any "stack jumping" before we enable interrupts/notifs.
31  * These jumps are when we directly modify rsp, specifically in the down
32  * direction (subtracts).  Adds would be okay.
33  *
34  * Another 64-bit concern is the red-zone.  The AMD64 ABI allows the use of
35  * space below the stack pointer by regular programs.  If we allowed this, we
36  * would clobber that space when we do our TF restarts, much like with OSs and
37  * IRQ handlers.  Thus we have the cross compiler automatically disabling the
38  * redzone (-mno-red-zone is a built-in option).
39  *
40  * When compared to the 32 bit code, notice we use rdi, instead of eax, for our
41  * work.  This is because rdi is the arg0 of a syscall.  Using it saves us some
42  * extra moves, since we need to pop the *sysc before saving any other
43  * registers. */
44
45 /* Helper for writing the info we need later to the u_tf's stack.  Also, note
46  * this goes backwards, since memory reads up the stack. */
47 struct restart_helper {
48         void                                            *notif_disab_loc;
49         void                                            *notif_pend_loc;
50         struct syscall                          *sysc;
51         uint64_t                                        rdi_save;
52         uint64_t                                        rflags;
53         uint64_t                                        rip;
54 };
55 /* Static syscall, used for self-notifying.  We never wait on it, and we
56  * actually might submit it multiple times in parallel on different cores!
57  * While this may seem dangerous, the kernel needs to be able to handle this
58  * scenario.  It's also important that we never wait on this, since for all but
59  * the first call, the DONE flag will be set.  (Set once, then never reset) */
60 struct syscall vc_entry = {
61         .num = SYS_vc_entry,
62         .err = 0,
63         .retval = 0,
64         .flags = 0,
65         .ev_q = 0,
66         .u_data = 0,
67         .arg0 = 0,
68         .arg1 = 0,
69         .arg2 = 0,
70         .arg3 = 0,
71         .arg4 = 0,
72         .arg5 = 0,
73 };
74
75 static void pop_hw_tf(struct hw_trapframe *tf, uint32_t vcoreid)
76 {
77         struct restart_helper *rst;
78         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
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_rsp -
82                                        sizeof(struct restart_helper));
83         /* Fill in the info we'll need later */
84         rst->notif_disab_loc = &vcpd->notif_disabled;
85         rst->notif_pend_loc = &vcpd->notif_pending;
86         rst->sysc = &vc_entry;
87         rst->rdi_save = 0;                      /* avoid bugs */
88         rst->rflags = tf->tf_rflags;
89         rst->rip = tf->tf_rip;
90
91         asm volatile ("movq %0, %%rsp;       " /* jump rsp to the utf */
92                       "popq %%rax;           " /* restore registers */
93                       "popq %%rbx;           "
94                       "popq %%rcx;           "
95                       "popq %%rdx;           "
96                       "popq %%rbp;           "
97                       "popq %%rsi;           "
98                       "popq %%rdi;           "
99                       "popq %%r8;            "
100                       "popq %%r9;            "
101                       "popq %%r10;           "
102                       "popq %%r11;           "
103                       "popq %%r12;           "
104                       "popq %%r13;           "
105                       "popq %%r14;           "
106                       "popq %%r15;           "
107                       "addq $0x28, %%rsp;    " /* move to the rsp slot in the tf */
108                       "popq %%rsp;           " /* change to the utf's %rsp */
109                       "subq $0x10, %%rsp;    " /* move rsp to below rdi's slot */
110                       "pushq %%rdi;          " /* save rdi, will clobber soon */
111                       "subq $0x18, %%rsp;    " /* move to notif_dis_loc slot */
112                       "popq %%rdi;           " /* load notif_disabled addr */
113                       "movb $0x00, (%%rdi);  " /* enable notifications */
114                                   /* Need a wrmb() here so the write of enable_notif can't pass
115                                    * the read of notif_pending (racing with a potential
116                                    * cross-core call with proc_notify()). */
117                                   "lock addq $0, (%%rdi);" /* LOCK is a CPU mb() */
118                                   /* From here down, we can get interrupted and restarted */
119                       "popq %%rdi;           " /* get notif_pending status loc */
120                       "testb $0x01, (%%rdi); " /* test if a notif is pending */
121                       "jz 1f;                " /* if not pending, skip syscall */
122                                   /* Actual syscall.  Note we don't wait on the async call */
123                       "popq %%rdi;           " /* &sysc, trap arg0 */
124                       "pushq %%rsi;          " /* save rax, will be trap arg1 */
125                       "pushq %%rax;          " /* save rax, will be trap ret */
126                       "movq $0x1, %%rsi;     " /* sending one async syscall: arg1 */
127                       "int %1;               " /* fire the syscall */
128                       "popq %%rax;           " /* restore regs after syscall */
129                       "popq %%rsi;           "
130                       "jmp 2f;               " /* skip 1:, already popped */
131                                   "1: addq $0x08, %%rsp; " /* discard &sysc (on non-sc path) */
132                       "2: popq %%rdi;        " /* restore tf's %rdi (both paths) */
133                                   "popfq;                " /* restore utf's rflags */
134                       "ret;                  " /* return to the new PC */
135                       :
136                       : "g"(&tf->tf_rax), "i"(T_SYSCALL)
137                       : "memory");
138 }
139
140 static void pop_sw_tf(struct sw_trapframe *sw_tf, uint32_t vcoreid)
141 {
142         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
143
144         /* Restore callee-saved FPU state.  We need to clear exceptions before
145          * reloading the FP CW, in case the new CW unmasks any.  We also need to
146          * reset the tag word to clear out the stack.
147          *
148          * The main issue here is that while our context was saved in an
149          * ABI-complaint manner, we may be starting up on a somewhat random FPU
150          * state.  Having gibberish in registers isn't a big deal, but some of the
151          * FP environment settings could cause trouble.  If fnclex; emms isn't
152          * enough, we could also save/restore the entire FP env with fldenv, or do
153          * an fninit before fldcw. */
154         asm volatile ("ldmxcsr %0" : : "m"(sw_tf->tf_mxcsr));
155         asm volatile ("fnclex; emms; fldcw %0" : : "m"(sw_tf->tf_fpucw));
156         /* Basic plan: restore all regs, off rcx as the sw_tf.  Switch to the new
157          * stack, save the PC so we can jump to it later.  Use clobberably
158          * registers for the locations of sysc, notif_dis, and notif_pend. Once on
159          * the new stack, we enable notifs, check if we missed one, and if so, self
160          * notify.  Note the syscall clobbers rax. */
161         asm volatile ("movq 0x00(%0), %%rbx; " /* restore regs */
162                       "movq 0x08(%0), %%rbp; "
163                       "movq 0x10(%0), %%r12; "
164                       "movq 0x18(%0), %%r13; "
165                       "movq 0x20(%0), %%r14; "
166                       "movq 0x28(%0), %%r15; "
167                       "movq 0x30(%0), %%r8;  " /* save rip in r8 */
168                       "movq 0x38(%0), %%rsp; " /* jump to future stack */
169                       "movb $0x00, (%2);     " /* enable notifications */
170                       /* Need a wrmb() here so the write of enable_notif can't pass
171                        * the read of notif_pending (racing with a potential
172                        * cross-core call with proc_notify()). */
173                       "lock addq $0, (%2);   " /* LOCK is a CPU mb() */
174                       /* From here down, we can get interrupted and restarted */
175                       "testb $0x01, (%3);    " /* test if a notif is pending */
176                       "jz 1f;                " /* if not pending, skip syscall */
177                       /* Actual syscall.  Note we don't wait on the async call.
178                        * &vc_entry is already in rdi (trap arg0). */
179                       "movq $0x1, %%rsi;     " /* sending one async syscall: arg1 */
180                       "int %4;               " /* fire the syscall */
181                       "1: jmp *%%r8;         " /* ret saved earlier */
182                       :
183                       : "c"(&sw_tf->tf_rbx),
184                         "D"(&vc_entry),
185                         "S"(&vcpd->notif_disabled),
186                         "d"(&vcpd->notif_pending),
187                         "i"(T_SYSCALL)
188                       : "memory");
189 }
190
191 /* Pops a user context, reanabling notifications at the same time.  A Userspace
192  * scheduler can call this when transitioning off the transition stack.
193  *
194  * At some point in vcore context before calling this, you need to clear
195  * notif_pending (do this by calling handle_events()).  As a potential
196  * optimization, consider clearing the notif_pending flag / handle_events again
197  * (right before popping), right before calling this.  If notif_pending is not
198  * clear, this will self_notify this core, since it should be because we missed
199  * a notification message while notifs were disabled. */
200 void pop_user_ctx(struct user_context *ctx, uint32_t vcoreid)
201 {
202         switch (ctx->type) {
203         case ROS_HW_CTX:
204                 pop_hw_tf(&ctx->tf.hw_tf, vcoreid);
205                 break;
206         case ROS_SW_CTX:
207                 pop_sw_tf(&ctx->tf.sw_tf, vcoreid);
208                 break;
209         case ROS_VM_CTX:
210                 ros_syscall(SYS_pop_ctx, ctx, 0, 0, 0, 0, 0);
211                 break;
212         }
213         assert(0);
214 }
215
216 /* Like the regular pop_user_ctx, but this one doesn't check or clear
217  * notif_pending.  The only case where we use this is when an IRQ/notif
218  * interrupts a uthread that is in the process of disabling notifs.
219  *
220  * If we need to support VM_CTXs here, we'll need to tell the kernel whether or
221  * not we want to enable_notifs (flag to SYS_pop_ctx).  The only use case for
222  * this is when disabling notifs.  Currently, a VM can't do this or do things
223  * like uthread_yield.  It doesn't have access to the vcore's or uthread's TLS
224  * to bootstrap any of that stuff. */
225 void pop_user_ctx_raw(struct user_context *ctx, uint32_t vcoreid)
226 {
227         struct hw_trapframe *tf = &ctx->tf.hw_tf;
228         struct restart_helper *rst;
229         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
230
231         assert(ctx->type == ROS_HW_CTX);
232         /* The stuff we need to write will be below the current stack of the utf */
233         rst = (struct restart_helper*)((void*)tf->tf_rsp -
234                                        sizeof(struct restart_helper));
235         /* Fill in the info we'll need later */
236         rst->notif_disab_loc = &vcpd->notif_disabled;
237         rst->rdi_save = 0;                      /* avoid bugs */
238         rst->rflags = tf->tf_rflags;
239         rst->rip = tf->tf_rip;
240
241         asm volatile ("movq %0, %%rsp;       " /* jump esp to the utf */
242                       "popq %%rax;           " /* restore registers */
243                       "popq %%rbx;           "
244                       "popq %%rcx;           "
245                       "popq %%rdx;           "
246                       "popq %%rbp;           "
247                       "popq %%rsi;           "
248                       "popq %%rdi;           "
249                       "popq %%r8;            "
250                       "popq %%r9;            "
251                       "popq %%r10;           "
252                       "popq %%r11;           "
253                       "popq %%r12;           "
254                       "popq %%r13;           "
255                       "popq %%r14;           "
256                       "popq %%r15;           "
257                       "addq $0x28, %%rsp;    " /* move to the rsp slot in the tf */
258                       "popq %%rsp;           " /* change to the utf's %rsp */
259                       "subq $0x10, %%rsp;    " /* move rsp to below rdi's slot */
260                       "pushq %%rdi;          " /* save rdi, will clobber soon */
261                       "subq $0x18, %%rsp;    " /* move to notif_dis_loc slot */
262                       "popq %%rdi;           " /* load notif_disabled addr */
263                       "movb $0x00, (%%rdi);  " /* enable notifications */
264                                   /* Here's where we differ from the regular pop_user_ctx().
265                                    * We need to adjust rsp and whatnot, but don't do test,
266                                    * clear notif_pending, or call a syscall. */
267                                   /* From here down, we can get interrupted and restarted */
268                       "addq $0x10, %%rsp;    " /* move to rdi save slot */
269                       "popq %%rdi;           " /* restore tf's %rdi */
270                                   "popfq;                " /* restore utf's rflags */
271                       "ret;                  " /* return to the new PC */
272                       :
273                       : "g"(&tf->tf_rax)
274                       : "memory");
275 }
276
277 void print_hw_tf(struct hw_trapframe *hw_tf)
278 {
279         printf("[user] HW TRAP frame 0x%016x\n", hw_tf);
280         printf("  rax  0x%016lx\n",           hw_tf->tf_rax);
281         printf("  rbx  0x%016lx\n",           hw_tf->tf_rbx);
282         printf("  rcx  0x%016lx\n",           hw_tf->tf_rcx);
283         printf("  rdx  0x%016lx\n",           hw_tf->tf_rdx);
284         printf("  rbp  0x%016lx\n",           hw_tf->tf_rbp);
285         printf("  rsi  0x%016lx\n",           hw_tf->tf_rsi);
286         printf("  rdi  0x%016lx\n",           hw_tf->tf_rdi);
287         printf("  r8   0x%016lx\n",           hw_tf->tf_r8);
288         printf("  r9   0x%016lx\n",           hw_tf->tf_r9);
289         printf("  r10  0x%016lx\n",           hw_tf->tf_r10);
290         printf("  r11  0x%016lx\n",           hw_tf->tf_r11);
291         printf("  r12  0x%016lx\n",           hw_tf->tf_r12);
292         printf("  r13  0x%016lx\n",           hw_tf->tf_r13);
293         printf("  r14  0x%016lx\n",           hw_tf->tf_r14);
294         printf("  r15  0x%016lx\n",           hw_tf->tf_r15);
295         printf("  trap 0x%08x\n",             hw_tf->tf_trapno);
296         printf("  gsbs 0x%016lx\n",           hw_tf->tf_gsbase);
297         printf("  fsbs 0x%016lx\n",           hw_tf->tf_fsbase);
298         printf("  err  0x--------%08x\n",     hw_tf->tf_err);
299         printf("  rip  0x%016lx\n",           hw_tf->tf_rip);
300         printf("  cs   0x------------%04x\n", hw_tf->tf_cs);
301         printf("  flag 0x%016lx\n",           hw_tf->tf_rflags);
302         printf("  rsp  0x%016lx\n",           hw_tf->tf_rsp);
303         printf("  ss   0x------------%04x\n", hw_tf->tf_ss);
304 }
305
306 void print_sw_tf(struct sw_trapframe *sw_tf)
307 {
308         printf("[user] SW TRAP frame 0x%016p\n", sw_tf);
309         printf("  rbx  0x%016lx\n",           sw_tf->tf_rbx);
310         printf("  rbp  0x%016lx\n",           sw_tf->tf_rbp);
311         printf("  r12  0x%016lx\n",           sw_tf->tf_r12);
312         printf("  r13  0x%016lx\n",           sw_tf->tf_r13);
313         printf("  r14  0x%016lx\n",           sw_tf->tf_r14);
314         printf("  r15  0x%016lx\n",           sw_tf->tf_r15);
315         printf("  gsbs 0x%016lx\n",           sw_tf->tf_gsbase);
316         printf("  fsbs 0x%016lx\n",           sw_tf->tf_fsbase);
317         printf("  rip  0x%016lx\n",           sw_tf->tf_rip);
318         printf("  rsp  0x%016lx\n",           sw_tf->tf_rsp);
319         printf(" mxcsr 0x%08x\n",             sw_tf->tf_mxcsr);
320         printf(" fpucw 0x%04x\n",             sw_tf->tf_fpucw);
321 }
322
323 void print_vm_tf(struct vm_trapframe *vm_tf)
324 {
325         printf("[user] VM Trapframe 0x%016x\n", vm_tf);
326         printf("  rax  0x%016lx\n",           vm_tf->tf_rax);
327         printf("  rbx  0x%016lx\n",           vm_tf->tf_rbx);
328         printf("  rcx  0x%016lx\n",           vm_tf->tf_rcx);
329         printf("  rdx  0x%016lx\n",           vm_tf->tf_rdx);
330         printf("  rbp  0x%016lx\n",           vm_tf->tf_rbp);
331         printf("  rsi  0x%016lx\n",           vm_tf->tf_rsi);
332         printf("  rdi  0x%016lx\n",           vm_tf->tf_rdi);
333         printf("  r8   0x%016lx\n",           vm_tf->tf_r8);
334         printf("  r9   0x%016lx\n",           vm_tf->tf_r9);
335         printf("  r10  0x%016lx\n",           vm_tf->tf_r10);
336         printf("  r11  0x%016lx\n",           vm_tf->tf_r11);
337         printf("  r12  0x%016lx\n",           vm_tf->tf_r12);
338         printf("  r13  0x%016lx\n",           vm_tf->tf_r13);
339         printf("  r14  0x%016lx\n",           vm_tf->tf_r14);
340         printf("  r15  0x%016lx\n",           vm_tf->tf_r15);
341         printf("  rip  0x%016lx\n",           vm_tf->tf_rip);
342         printf("  rflg 0x%016lx\n",           vm_tf->tf_rflags);
343         printf("  rsp  0x%016lx\n",           vm_tf->tf_rsp);
344         printf("  cr2  0x%016lx\n",           vm_tf->tf_cr2);
345         printf("  cr3  0x%016lx\n",           vm_tf->tf_cr3);
346         printf("Gpcore 0x%08x\n",             vm_tf->tf_guest_pcoreid);
347         printf("Flags  0x%08x\n",             vm_tf->tf_flags);
348         printf("Inject 0x%08x\n",             vm_tf->tf_trap_inject);
349         printf("ExitRs 0x%08x\n",             vm_tf->tf_exit_reason);
350         printf("ExitQl 0x%08x\n",             vm_tf->tf_exit_qual);
351         printf("Intr1  0x%016lx\n",           vm_tf->tf_intrinfo1);
352         printf("Intr2  0x%016lx\n",           vm_tf->tf_intrinfo2);
353         printf("GVA    0x%016lx\n",           vm_tf->tf_guest_va);
354         printf("GPA    0x%016lx\n",           vm_tf->tf_guest_pa);
355 }
356
357 void print_user_context(struct user_context *ctx)
358 {
359         switch (ctx->type) {
360         case ROS_HW_CTX:
361                 print_hw_tf(&ctx->tf.hw_tf);
362                 break;
363         case ROS_SW_CTX:
364                 print_sw_tf(&ctx->tf.sw_tf);
365                 break;
366         case ROS_VM_CTX:
367                 print_vm_tf(&ctx->tf.vm_tf);
368                 break;
369         default:
370                 printf("Unknown context type %d\n", ctx->type);
371         }
372 }
373
374 /* The second-lowest level function jumped to by the kernel on every vcore
375  * entry.  We get called from __kernel_vcore_entry.
376  *
377  * We should consider making it mandatory to set the tls_desc in the kernel. We
378  * wouldn't even need to pass the vcore id to user space at all if we did this.
379  * It would already be set in the preinstalled TLS as __vcore_id. */
380 void __attribute__((noreturn)) __kvc_entry_c(void)
381 {
382         /* The kernel sets the TLS desc for us, based on whatever is in VCPD.
383          *
384          * x86 32-bit TLS is pretty jacked up, so the kernel doesn't set the TLS
385          * desc for us.  it's a little more expensive to do it here, esp for
386          * amd64.  Can remove this when/if we overhaul 32 bit TLS. */
387         int id = __vcore_id_on_entry;
388
389         #ifndef __x86_64__
390         set_tls_desc(vcpd_of(id)->vcore_tls_desc);
391         #endif
392         /* Every time the vcore comes up, it must set that it is in vcore context.
393          * uthreads may share the same TLS as their vcore (when uthreads do not have
394          * their own TLS), and if a uthread was preempted, __vcore_context == FALSE,
395          * and that will continue to be true the next time the vcore pops up. */
396         __vcore_context = TRUE;
397         vcore_entry();
398         fprintf(stderr, "vcore_entry() should never return!\n");
399         abort();
400         __builtin_unreachable();
401 }