x86: cleans up syscalls, fixes pop_ros_tf() (XCC)
[akaros.git] / user / 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_enabled, 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   |
30  *               |   new eip                |
31  *               |   eax save space         |
32  *               |   &sysc (async syscall)  |
33  *               |   notif_pending_loc      |
34  *               |   notif_enabled_loc      |
35  *
36  * The important thing is that it can handle a notification after it enables
37  * notifications, and when it gets resumed it can ultimately run the new
38  * context.  Enough state is saved in the running context and stack to continue
39  * running. */
40 #include <stdio.h>
41 static inline void pop_ros_tf(struct user_trapframe *tf, uint32_t vcoreid)
42 {
43         struct syscall sysc = {0};
44         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
45         /* need to prep the async sysc in case we need to notify ourselves */
46         sysc.num = SYS_self_notify;
47         sysc.arg0 = vcoreid;    /* arg1 and 2 already = 0 (null notif, no u_ne) */
48         if (!tf->tf_cs) { /* sysenter TF.  esp and eip are in other regs. */
49                 tf->tf_esp = tf->tf_regs.reg_ebp;
50                 tf->tf_eip = tf->tf_regs.reg_edx;
51         }
52
53         asm volatile ("movl %2,-0x04(%1);    " /* push the PC */
54                       "movl %3,-0x0c(%1);    " /* room for eax, push &sysc */
55                       "movl %4,-0x10(%1);    " /* push notif_pending loc */
56                       "movl %5,-0x14(%1);    " /* push notif_enabled loc */
57                       "movl %0,%%esp;        " /* pop the real tf */
58                       "popal;                " /* restore normal registers */
59                       "addl $0x20,%%esp;     " /* move to the eflags in the tf */
60                       "popfl;                " /* restore eflags */
61                       "popl %%esp;           " /* change to the new %esp */
62                       "subl $0x4,%%esp;      " /* move esp to the slot for eax */
63                       "pushl %%eax;          " /* save eax, will clobber soon */
64                       "subl $0xc,%%esp;      " /* move to notif_en_loc slot */
65                       "popl %%eax;           " /* load notif_enabaled addr */
66                       "movb $0x01,(%%eax);   " /* enable notifications */
67                       "popl %%eax;           " /* get notif_pending status */
68                       "pushfl;               " /* save eflags */
69                       "testb $0x01,(%%eax);  " /* test if a notif is pending */
70                       "jz 1f;                " /* if not pending, skip syscall */
71                       "popfl;                " /* restore eflags */
72                       "movb $0x00,(%%eax);   " /* clear pending */
73                                   /* Actual syscall.  Note we don't wait on the async call */
74                       "popl %%eax;           " /* &sysc, trap arg0 */
75                       "pushl %%edx;          " /* save edx, will be trap arg1 */
76                       "movl $0x1,%%edx;      " /* sending one async syscall: arg1 */
77                       "int %6;               " /* fire the syscall */
78                       "popl %%edx;           " /* restore regs after syscall */
79                       "jmp 2f;               " /* skip 1:, already popped */
80                       "1: popfl;             " /* restore eflags (on non-sc path) */
81                                   "popl %%eax;           " /* discard &sysc (on non-sc path) */
82                       "2: popl %%eax;        " /* restore tf's %eax (both paths) */
83                       "ret;                  " /* return to the new PC */
84                       :
85                       : "g"(tf), "r"(tf->tf_esp), "r"(tf->tf_eip), "r"(&sysc),
86                         "r"(&vcpd->notif_pending), "r"(&vcpd->notif_enabled),
87                         "i"(T_SYSCALL)
88                       : "memory");
89 }
90
91 /* Save the current context/registers into the given tf, setting the pc of the
92  * tf to the end of this function.  You only need to save that which you later
93  * restore with pop_ros_tf(). */
94 static inline void save_ros_tf(struct user_trapframe *tf)
95 {
96         memset(tf, 0, sizeof(struct user_trapframe)); /* sanity */
97         /* set CS and make sure eflags is okay */
98         tf->tf_cs = GD_UT | 3;
99         tf->tf_eflags = 0x00000200; /* interrupts enabled.  bare minimum eflags. */
100         /* Save the regs and the future esp. */
101         asm volatile("movl %%esp,(%0);       " /* save esp in it's slot*/
102                      "pushl %%eax;           " /* temp save eax */
103                      "leal 1f,%%eax;         " /* get future eip */
104                      "movl %%eax,(%1);       " /* store future eip */
105                      "popl %%eax;            " /* restore eax */
106                      "movl %2,%%esp;         " /* move to the beginning of the tf */
107                      "addl $0x20,%%esp;      " /* move to after the push_regs */
108                      "pushal;                " /* save regs */
109                      "addl $0x44,%%esp;      " /* move to esp slot */
110                      "popl %%esp;            " /* restore esp */
111                      "1:                     " /* where this tf will restart */
112                      : 
113                      : "g"(&tf->tf_esp), "g"(&tf->tf_eip), "g"(tf)
114                      : "eax", "memory", "cc");
115 }
116
117 /* This assumes a user_tf looks like a regular kernel trapframe */
118 static __inline void
119 init_user_tf(struct user_trapframe *u_tf, uint32_t entry_pt, uint32_t stack_top)
120 {
121         memset(u_tf, 0, sizeof(struct user_trapframe));
122         u_tf->tf_eip = entry_pt;
123         u_tf->tf_cs = GD_UT | 3;
124         u_tf->tf_esp = stack_top;
125 }
126
127 /* Reading from the LDT.  Could also use %gs, but that would require including
128  * half of libc's TLS header.  Sparc will probably ignore the vcoreid, so don't
129  * rely on it too much.  The intent of it is vcoreid is the caller's vcoreid,
130  * and that vcoreid might be in the TLS of the caller (it will be for transition
131  * stacks) and we could avoid a trap on x86 to sys_getvcoreid(). */
132 static inline void *get_tls_desc(uint32_t vcoreid)
133 {
134         return (void*)(__procdata.ldt[vcoreid].sd_base_31_24 << 24 |
135                        __procdata.ldt[vcoreid].sd_base_23_16 << 16 |
136                        __procdata.ldt[vcoreid].sd_base_15_0);
137 }
138
139 /* passing in the vcoreid, since it'll be in TLS of the caller */
140 static inline void set_tls_desc(void *tls_desc, uint32_t vcoreid)
141 {
142         /* Keep this technique in sync with sysdeps/ros/i386/tls.h */
143         segdesc_t tmp = SEG(STA_W, (uint32_t)tls_desc, 0xffffffff, 3);
144         __procdata.ldt[vcoreid] = tmp;
145
146         /* GS is still the same (should be!), but it needs to be reloaded to force a
147          * re-read of the LDT. */
148         uint32_t gs = (vcoreid << 3) | 0x07;
149         asm volatile("movl %0,%%gs" : : "r" (gs) : "memory");
150
151         __vcoreid = vcoreid;
152 }
153
154 // this is how we get our thread id on entry.
155 #define __vcore_id_on_entry \
156 ({ \
157         register int temp asm ("eax"); \
158         temp; \
159 })
160
161 /* For debugging. */
162 #include <rstdio.h>
163 static __inline void print_trapframe(struct user_trapframe *tf)
164 {
165         printf("[user] TRAP frame\n");
166         printf("  edi  0x%08x\n", tf->tf_regs.reg_edi);
167         printf("  esi  0x%08x\n", tf->tf_regs.reg_esi);
168         printf("  ebp  0x%08x\n", tf->tf_regs.reg_ebp);
169         printf("  oesp 0x%08x\n", tf->tf_regs.reg_oesp);
170         printf("  ebx  0x%08x\n", tf->tf_regs.reg_ebx);
171         printf("  edx  0x%08x\n", tf->tf_regs.reg_edx);
172         printf("  ecx  0x%08x\n", tf->tf_regs.reg_ecx);
173         printf("  eax  0x%08x\n", tf->tf_regs.reg_eax);
174         printf("  gs   0x----%04x\n", tf->tf_gs);
175         printf("  fs   0x----%04x\n", tf->tf_fs);
176         printf("  es   0x----%04x\n", tf->tf_es);
177         printf("  ds   0x----%04x\n", tf->tf_ds);
178         printf("  trap 0x%08x\n", tf->tf_trapno);
179         printf("  err  0x%08x\n", tf->tf_err);
180         printf("  eip  0x%08x\n", tf->tf_eip);
181         printf("  cs   0x----%04x\n", tf->tf_cs);
182         printf("  flag 0x%08x\n", tf->tf_eflags);
183         printf("  esp  0x%08x\n", tf->tf_esp);
184         printf("  ss   0x----%04x\n", tf->tf_ss);
185 }
186
187 #endif /* PARLIB_ARCH_VCORE_H */