Fixed bug with setting parameters properly for the self_notify trap
[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 /* Pops an ROS kernel-provided TF, reanabling notifications at the same time.
11  * A Userspace scheduler can call this when transitioning off the transition
12  * stack.
13  *
14  * Make sure you clear the notif_pending flag, and then check the queue before
15  * calling this.  If notif_pending is not clear, this will self_notify this
16  * core, since it should be because we missed a notification message while
17  * notifs were disabled. 
18  *
19  * Basically, it sets up the future stack pointer to have extra stuff after it,
20  * and then it pops the registers, then pops the new context's stack
21  * pointer.  Then it uses the extra stuff (the new PC is on the stack, the
22  * location of notif_enabled, and a clobbered work register) to enable notifs,
23  * make sure notif IPIs weren't pending, restore the work reg, and then "ret".
24  *
25  * This is what the target notif_tf's stack will look like (growing down):
26  *
27  * Target ESP -> |   u_thread's old stuff   |
28  *               |   new eip                |
29  *               |   eax save space         |
30  *               |   vcoreid                |
31  *               |   notif_pending_loc      |
32  *               |   notif_enabled_loc      |
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 static inline void pop_ros_tf(struct user_trapframe *tf, uint32_t vcoreid)
39 {
40         struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
41         if (!tf->tf_cs) { /* sysenter TF.  esp and eip are in other regs. */
42                 tf->tf_esp = tf->tf_regs.reg_ebp;
43                 tf->tf_eip = tf->tf_regs.reg_edx;
44         }
45         asm volatile ("movl %2,-0x04(%1);    " /* push the PC */
46                       "movl %3,-0x0c(%1);    " /* room for eax, push vcoreid */
47                       "movl %4,-0x10(%1);    " /* push notif_pending loc */
48                       "movl %5,-0x14(%1);    " /* push notif_enabled loc */
49                       "movl %0,%%esp;        " /* pop the real tf */
50                       "popal;                " /* restore normal registers */
51                       "addl $0x20,%%esp;     " /* move to the eflags in the tf */
52                       "popfl;                " /* restore eflags */
53                       "popl %%esp;           " /* change to the new %esp */
54                       "subl $0x4,%%esp;      " /* move esp to the slot for eax */
55                       "pushl %%eax;          " /* save eax, will clobber soon */
56                       "subl $0xc,%%esp;      " /* move to notif_en_loc slot */
57                       "popl %%eax;           " /* load notif_enabaled addr */
58                       "movb $0x01,(%%eax);   " /* enable notifications */
59                       "popl %%eax;           " /* get notif_pending status */
60                       "pushfl;               " /* save eflags */
61                       "testb $0x01,(%%eax);  " /* test if a notif is pending */
62                       "jz 1f;                " /* if not pending, skip syscall */
63                       "popfl;                " /* restore eflags */
64                       "movb $0x00,(%%eax);   " /* clear pending */
65                       "pushl %%edx;          " /* save edx, syscall arg1 */
66                       "pushl %%ecx;          " /* save ecx, syscall arg2 */
67                       "pushl %%ebx;          " /* save ebx, syscall arg3 */
68                       "pushl %%esi;          " /* will be clobbered for errno */
69                       "addl $0x10,%%esp;     " /* move back over the 4 push's */
70                       "popl %%edx;           " /* vcoreid, arg1 */
71                       "subl $0x14,%%esp;     " /* jump back to after the 4 push's */
72                       "movl $0x0,%%ecx;      " /* send the null notif, arg2 */
73                       "movl $0x0,%%ebx;      " /* no u_ne message, arg3 */
74                       "movl %6,%%eax;        " /* syscall num */
75                       "int %7;               " /* fire the syscall */
76                       "popl %%esi;           " /* restore regs after syscall */
77                       "popl %%ebx;           "
78                       "popl %%ecx;           "
79                       "popl %%edx;           "
80                       "jmp 2f;               " /* skip 1:, already popped */
81                       "1: popfl;             " /* restore eflags */
82                       "2: popl %%eax;        " /* discard vcoreid */
83                       "popl %%eax;           " /* restore tf's %eax */
84                       "ret;                  " /* return to the new PC */
85                       :
86                       : "g"(tf), "r"(tf->tf_esp), "r"(tf->tf_eip), "r"(vcoreid),
87                         "r"(&vcpd->notif_pending), "r"(&vcpd->notif_enabled),
88                         "i"(SYS_self_notify), "i"(T_SYSCALL)
89                       : "memory");
90 }
91
92 /* Save the current context/registers into the given tf, setting the pc of the
93  * tf to the end of this function.  You only need to save that which you later
94  * restore with pop_ros_tf(). */
95 static inline void save_ros_tf(struct user_trapframe *tf)
96 {
97         memset(tf, 0, sizeof(struct user_trapframe)); /* sanity */
98         /* set CS and make sure eflags is okay */
99         tf->tf_cs = GD_UT | 3;
100         tf->tf_eflags = 0x00000200; /* interrupts enabled.  bare minimum eflags. */
101         /* Save the regs and the future esp. */
102         asm volatile("movl %%esp,(%0);       " /* save esp in it's slot*/
103                      "pushl %%eax;           " /* temp save eax */
104                      "leal 1f,%%eax;         " /* get future eip */
105                      "movl %%eax,(%1);       " /* store future eip */
106                      "popl %%eax;            " /* restore eax */
107                      "movl %2,%%esp;         " /* move to the beginning of the tf */
108                      "addl $0x20,%%esp;      " /* move to after the push_regs */
109                      "pushal;                " /* save regs */
110                      "addl $0x44,%%esp;      " /* move to esp slot */
111                      "popl %%esp;            " /* restore esp */
112                      "1:                     " /* where this tf will restart */
113                      : 
114                      : "g"(&tf->tf_esp), "g"(&tf->tf_eip), "g"(tf)
115                      : "eax", "memory", "cc");
116 }
117
118 /* This assumes a user_tf looks like a regular kernel trapframe */
119 static __inline void
120 init_user_tf(struct user_trapframe *u_tf, uint32_t entry_pt, uint32_t stack_top)
121 {
122         memset(u_tf, 0, sizeof(struct user_trapframe));
123         u_tf->tf_eip = entry_pt;
124         u_tf->tf_cs = GD_UT | 3;
125         u_tf->tf_esp = stack_top;
126 }
127
128 /* Reading from the LDT.  Could also use %gs, but that would require including
129  * half of libc's TLS header.  Sparc will probably ignore the vcoreid, so don't
130  * rely on it too much.  The intent of it is vcoreid is the caller's vcoreid,
131  * and that vcoreid might be in the TLS of the caller (it will be for transition
132  * stacks) and we could avoid a trap on x86 to sys_getvcoreid(). */
133 static inline void *get_tls_desc(uint32_t vcoreid)
134 {
135         return (void*)(__procdata.ldt[vcoreid].sd_base_31_24 << 24 |
136                        __procdata.ldt[vcoreid].sd_base_23_16 << 16 |
137                        __procdata.ldt[vcoreid].sd_base_15_0);
138 }
139
140 /* passing in the vcoreid, since it'll be in TLS of the caller */
141 static inline void set_tls_desc(void *tls_desc, uint32_t vcoreid)
142 {
143   /* Keep this technique in sync with sysdeps/ros/i386/tls.h */
144   segdesc_t tmp = SEG(STA_W, (uint32_t)tls_desc, 0xffffffff, 3);
145   __procdata.ldt[vcoreid] = tmp;
146
147   /* GS is still the same (should be!), but it needs to be reloaded to force a
148    * re-read of the LDT. */
149   uint32_t gs = (vcoreid << 3) | 0x07;
150   asm volatile("movl %0,%%gs" : : "r" (gs) : "memory");
151 }
152
153 // this is how we get our thread id on entry.
154 #define __vcore_id_on_entry \
155 ({ \
156         register int temp asm ("eax"); \
157         temp; \
158 })
159
160 // The actual vcore_self() function is a global symbol that invokes this routine.
161 static inline int
162 __vcore_id()
163 {
164         // TODO: use some kind of thread-local storage to speed this up!
165         return (int)ros_syscall(SYS_getvcoreid,0,0,0,0,0);
166 }
167
168 /* For debugging. */
169 #include <rstdio.h>
170 static __inline void print_trapframe(struct user_trapframe *tf)
171 {
172         printf("[user] TRAP frame\n");
173         printf("  edi  0x%08x\n", tf->tf_regs.reg_edi);
174         printf("  esi  0x%08x\n", tf->tf_regs.reg_esi);
175         printf("  ebp  0x%08x\n", tf->tf_regs.reg_ebp);
176         printf("  oesp 0x%08x\n", tf->tf_regs.reg_oesp);
177         printf("  ebx  0x%08x\n", tf->tf_regs.reg_ebx);
178         printf("  edx  0x%08x\n", tf->tf_regs.reg_edx);
179         printf("  ecx  0x%08x\n", tf->tf_regs.reg_ecx);
180         printf("  eax  0x%08x\n", tf->tf_regs.reg_eax);
181         printf("  gs   0x----%04x\n", tf->tf_gs);
182         printf("  fs   0x----%04x\n", tf->tf_fs);
183         printf("  es   0x----%04x\n", tf->tf_es);
184         printf("  ds   0x----%04x\n", tf->tf_ds);
185         printf("  trap 0x%08x\n", tf->tf_trapno);
186         printf("  err  0x%08x\n", tf->tf_err);
187         printf("  eip  0x%08x\n", tf->tf_eip);
188         printf("  cs   0x----%04x\n", tf->tf_cs);
189         printf("  flag 0x%08x\n", tf->tf_eflags);
190         printf("  esp  0x%08x\n", tf->tf_esp);
191         printf("  ss   0x----%04x\n", tf->tf_ss);
192 }
193
194 #endif /* PARLIB_ARCH_VCORE_H */