1 #ifndef PARLIB_ARCH_VCORE_H
2 #define PARLIB_ARCH_VCORE_H
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>
10 extern __thread int __vcoreid;
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
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.
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_disabled, and a clobbered work register) to enable notifs,
25 * make sure notif IPIs weren't pending, restore the work reg, and then "ret".
27 * This is what the target notif_tf's stack will look like (growing down):
29 * Target ESP -> | u_thread's old stuff | the future %esp, tf->tf_esp
30 * | new eip | 0x04 below %esp (one slot is 0x04)
31 * | eflags space | 0x08 below
32 * | eax save space | 0x0c below
33 * | actual syscall | 0x10 below (0x30 space)
34 * | *sysc ptr to syscall | 0x40 below (0x10 + 0x30)
35 * | notif_pending_loc | 0x44 below (0x10 + 0x30)
36 * | notif_disabled_loc | 0x48 below (0x10 + 0x30)
38 * The important thing is that it can handle a notification after it enables
39 * notifications, and when it gets resumed it can ultimately run the new
40 * context. Enough state is saved in the running context and stack to continue
43 * Related to that is whether or not our stack pointer is sufficiently far down
44 * so that restarting *this* code won't clobber shit we need later. The way we
45 * do this is that we do any "stack jumping" after we enable interrupts/notifs.
46 * These jumps are when we directly modify esp, specifically in the down
47 * direction (subtracts). Adds would be okay.
49 * Another related concern is the storage for sysc. It used to be on the
50 * vcore's stack, but if an interrupt comes in before we use it, we trash the
51 * vcore's stack (and thus the storage for sysc!). Instead, we put it on the
52 * stack of the user tf. Moral: don't touch a vcore's stack with notifs
55 /* Helper for writing the info we need later to the u_tf's stack. Note, this
56 * could get fucked if the struct syscall isn't a multiple of 4-bytes. Also,
57 * note this goes backwards, since memory reads up the stack. */
58 struct restart_helper {
59 uint32_t notif_disab_loc;
60 uint32_t notif_pend_loc;
62 struct syscall local_sysc;
68 static inline void pop_ros_tf(struct user_trapframe *tf, uint32_t vcoreid)
70 struct restart_helper *rst;
71 struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
72 if (!tf->tf_cs) { /* sysenter TF. esp and eip are in other regs. */
73 tf->tf_esp = tf->tf_regs.reg_ebp;
74 tf->tf_eip = tf->tf_regs.reg_edx;
76 /* The stuff we need to write will be below the current stack of the utf */
77 rst = (struct restart_helper*)((void*)tf->tf_esp -
78 sizeof(struct restart_helper));
79 /* Fill in the info we'll need later */
80 rst->notif_disab_loc = (uint32_t)&vcpd->notif_disabled;
81 rst->notif_pend_loc = (uint32_t)&vcpd->notif_pending;
82 rst->sysc = &rst->local_sysc; /* point to the local one */
83 memset(rst->sysc, 0, sizeof(struct syscall));
84 /* Need to prep the async sysc in case we need to notify ourselves */
85 rst->sysc->num = SYS_self_notify;
86 rst->sysc->arg0 = vcoreid; /* arg 1 & 2 already = 0 (null notif, no u_ne)*/
87 rst->sysc->arg3 = TRUE; /* just a private VCPD notification */
88 rst->eax_save = 0; /* avoid bugs */
89 rst->eflags = tf->tf_eflags;
90 rst->eip = tf->tf_eip;
92 asm volatile ("movl %0,%%esp; " /* jump esp to the utf */
93 "popal; " /* restore normal registers */
94 "addl $0x24,%%esp; " /* move to the esp slot in the tf */
95 "popl %%esp; " /* change to the utf's %esp */
96 "subl $0x08,%%esp; " /* move esp to below eax's slot */
97 "pushl %%eax; " /* save eax, will clobber soon */
98 "movl %2,%%eax; " /* sizeof struct syscall */
99 "addl $0x0c,%%eax; " /* more offset btw eax/notif_en_loc*/
100 "subl %%eax,%%esp; " /* move to notif_en_loc slot */
101 "popl %%eax; " /* load notif_disabled addr */
102 "movb $0x00,(%%eax); " /* enable notifications */
103 /* Need a wrmb() here so the write of enable_notif can't pass
104 * the read of notif_pending (racing with a potential
105 * cross-core call with proc_notify()). */
106 "lock addl $0,(%%esp); " /* LOCK is a CPU mb() */
107 /* From here down, we can get interrupted and restarted */
108 "popl %%eax; " /* get notif_pending status */
109 "testb $0x01,(%%eax); " /* test if a notif is pending */
110 "jz 1f; " /* if not pending, skip syscall */
111 "movb $0x00,(%%eax); " /* clear pending */
112 /* Actual syscall. Note we don't wait on the async call */
113 "popl %%eax; " /* &sysc, trap arg0 */
114 "pushl %%edx; " /* save edx, will be trap arg1 */
115 "movl $0x1,%%edx; " /* sending one async syscall: arg1 */
116 "int %1; " /* fire the syscall */
117 "popl %%edx; " /* restore regs after syscall */
118 "jmp 2f; " /* skip 1:, already popped */
119 "1: popl %%eax; " /* discard &sysc (on non-sc path) */
120 "2: addl %2,%%esp; " /* jump over the sysc (both paths) */
121 "popl %%eax; " /* restore tf's %eax */
122 "popfl; " /* restore utf's eflags */
123 "ret; " /* return to the new PC */
125 : "g"(tf), "i"(T_SYSCALL), "i"(sizeof(struct syscall))
129 /* Like the regular pop_ros_tf, but this one doesn't check or clear
131 static inline void pop_ros_tf_raw(struct user_trapframe *tf, uint32_t vcoreid)
133 struct restart_helper *rst;
134 struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
135 if (!tf->tf_cs) { /* sysenter TF. esp and eip are in other regs. */
136 tf->tf_esp = tf->tf_regs.reg_ebp;
137 tf->tf_eip = tf->tf_regs.reg_edx;
139 /* The stuff we need to write will be below the current stack of the utf */
140 rst = (struct restart_helper*)((void*)tf->tf_esp -
141 sizeof(struct restart_helper));
142 /* Fill in the info we'll need later */
143 rst->notif_disab_loc = (uint32_t)&vcpd->notif_disabled;
144 rst->eax_save = 0; /* avoid bugs */
145 rst->eflags = tf->tf_eflags;
146 rst->eip = tf->tf_eip;
148 asm volatile ("movl %0,%%esp; " /* jump esp to the utf */
149 "popal; " /* restore normal registers */
150 "addl $0x24,%%esp; " /* move to the esp slot in the tf */
151 "popl %%esp; " /* change to the utf's %esp */
152 "subl $0x08,%%esp; " /* move esp to below eax's slot */
153 "pushl %%eax; " /* save eax, will clobber soon */
154 "movl %2,%%eax; " /* sizeof struct syscall */
155 "addl $0x0c,%%eax; " /* more offset btw eax/notif_en_loc*/
156 "subl %%eax,%%esp; " /* move to notif_en_loc slot */
157 "popl %%eax; " /* load notif_disabled addr */
158 "movb $0x00,(%%eax); " /* enable notifications */
159 /* Here's where we differ from the regular pop_ros_tf(). We
160 * do the same pops/esp moves, just to keep things similar
161 * and simple, but don't do test, clear notif_pending, or
163 /* From here down, we can get interrupted and restarted */
164 "popl %%eax; " /* get notif_pending status */
165 "popl %%eax; " /* discard &sysc (on non-sc path) */
166 "addl %2,%%esp; " /* jump over the sysc (both paths) */
167 "popl %%eax; " /* restore tf's %eax */
168 "popfl; " /* restore utf's eflags */
169 "ret; " /* return to the new PC */
171 : "g"(tf), "i"(T_SYSCALL), "i"(sizeof(struct syscall))
175 /* Save the current context/registers into the given tf, setting the pc of the
176 * tf to the end of this function. You only need to save that which you later
177 * restore with pop_ros_tf(). */
178 static inline void save_ros_tf(struct user_trapframe *tf)
180 memset(tf, 0, sizeof(struct user_trapframe)); /* sanity */
181 /* set CS and make sure eflags is okay */
182 tf->tf_cs = GD_UT | 3;
183 tf->tf_eflags = 0x00000200; /* interrupts enabled. bare minimum eflags. */
184 /* Save the regs and the future esp. */
185 asm volatile("movl %%esp,(%0); " /* save esp in it's slot*/
186 "pushl %%eax; " /* temp save eax */
187 "leal 1f,%%eax; " /* get future eip */
188 "movl %%eax,(%1); " /* store future eip */
189 "popl %%eax; " /* restore eax */
190 "movl %2,%%esp; " /* move to the beginning of the tf */
191 "addl $0x20,%%esp; " /* move to after the push_regs */
192 "pushal; " /* save regs */
193 "addl $0x44,%%esp; " /* move to esp slot */
194 "popl %%esp; " /* restore esp */
195 "1: " /* where this tf will restart */
197 : "g"(&tf->tf_esp), "g"(&tf->tf_eip), "g"(tf)
198 : "eax", "memory", "cc");
201 /* This assumes a user_tf looks like a regular kernel trapframe */
203 init_user_tf(struct user_trapframe *u_tf, uint32_t entry_pt, uint32_t stack_top)
205 memset(u_tf, 0, sizeof(struct user_trapframe));
206 u_tf->tf_eip = entry_pt;
207 u_tf->tf_cs = GD_UT | 3;
208 u_tf->tf_esp = stack_top;
211 /* Reading from the LDT. Could also use %gs, but that would require including
212 * half of libc's TLS header. Sparc will probably ignore the vcoreid, so don't
213 * rely on it too much. The intent of it is vcoreid is the caller's vcoreid,
214 * and that vcoreid might be in the TLS of the caller (it will be for transition
215 * stacks) and we could avoid a trap on x86 to sys_getvcoreid(). */
216 static inline void *get_tls_desc(uint32_t vcoreid)
218 return (void*)(__procdata.ldt[vcoreid].sd_base_31_24 << 24 |
219 __procdata.ldt[vcoreid].sd_base_23_16 << 16 |
220 __procdata.ldt[vcoreid].sd_base_15_0);
223 /* passing in the vcoreid, since it'll be in TLS of the caller */
224 static inline void set_tls_desc(void *tls_desc, uint32_t vcoreid)
226 /* Keep this technique in sync with sysdeps/ros/i386/tls.h */
227 segdesc_t tmp = SEG(STA_W, (uint32_t)tls_desc, 0xffffffff, 3);
228 __procdata.ldt[vcoreid] = tmp;
230 /* GS is still the same (should be!), but it needs to be reloaded to force a
231 * re-read of the LDT. */
232 uint32_t gs = (vcoreid << 3) | 0x07;
233 asm volatile("movl %0,%%gs" : : "r" (gs) : "memory");
238 // this is how we get our thread id on entry.
239 #define __vcore_id_on_entry \
241 register int temp asm ("eax"); \
247 static __inline void print_trapframe(struct user_trapframe *tf)
249 printf("[user] TRAP frame %08p\n", tf);
250 printf(" edi 0x%08x\n", tf->tf_regs.reg_edi);
251 printf(" esi 0x%08x\n", tf->tf_regs.reg_esi);
252 printf(" ebp 0x%08x\n", tf->tf_regs.reg_ebp);
253 printf(" oesp 0x%08x\n", tf->tf_regs.reg_oesp);
254 printf(" ebx 0x%08x\n", tf->tf_regs.reg_ebx);
255 printf(" edx 0x%08x\n", tf->tf_regs.reg_edx);
256 printf(" ecx 0x%08x\n", tf->tf_regs.reg_ecx);
257 printf(" eax 0x%08x\n", tf->tf_regs.reg_eax);
258 printf(" gs 0x----%04x\n", tf->tf_gs);
259 printf(" fs 0x----%04x\n", tf->tf_fs);
260 printf(" es 0x----%04x\n", tf->tf_es);
261 printf(" ds 0x----%04x\n", tf->tf_ds);
262 printf(" trap 0x%08x\n", tf->tf_trapno);
263 printf(" err 0x%08x\n", tf->tf_err);
264 printf(" eip 0x%08x\n", tf->tf_eip);
265 printf(" cs 0x----%04x\n", tf->tf_cs);
266 printf(" flag 0x%08x\n", tf->tf_eflags);
267 printf(" esp 0x%08x\n", tf->tf_esp);
268 printf(" ss 0x----%04x\n", tf->tf_ss);
271 #endif /* PARLIB_ARCH_VCORE_H */