parlib: Trim some header includes
[akaros.git] / user / parlib / thread0_sched.c
1 /* Copyright (c) 2015 Google, Inc.
2  * Barret Rhoden <brho@cs.berkeley.edu>
3  * See LICENSE for details.
4  *
5  * thread0_sched: a basic scheduler for thread0, used by SCPs without a
6  * multithreaded 2LS linked in.
7  *
8  * This is closely coupled with uthread.c */
9
10 #include <ros/arch/membar.h>
11 #include <parlib/arch/atomic.h>
12 #include <parlib/parlib.h>
13 #include <parlib/vcore.h>
14 #include <parlib/uthread.h>
15 #include <parlib/event.h>
16 #include <parlib/arch/trap.h>
17 #include <parlib/ros_debug.h>
18 #include <stdlib.h>
19
20 static void thread0_sched_entry(void);
21 static void thread0_thread_blockon_sysc(struct uthread *uthread, void *sysc);
22 static void thread0_thread_refl_fault(struct uthread *uth,
23                                       struct user_context *ctx);
24 static void thread0_thread_runnable(struct uthread *uth);
25 static void thread0_thread_has_blocked(struct uthread *uth, uth_sync_t sync,
26                                        int flags);
27 static void thread0_thread_exited(struct uthread *uth);
28 static struct uthread *thread0_thread_create(void *(*func)(void *), void *arg);
29 static uth_sync_t thread0_sync_alloc(void);
30 static void thread0_sync_free(uth_sync_t);
31 static struct uthread *thread0_sync_get_next(uth_sync_t);
32 static bool thread0_sync_get_uth(uth_sync_t s, struct uthread *uth);
33
34 /* externed into uthread.c */
35 struct schedule_ops thread0_2ls_ops = {
36         .sched_entry = thread0_sched_entry,
37         .thread_blockon_sysc = thread0_thread_blockon_sysc,
38         .thread_refl_fault = thread0_thread_refl_fault,
39         .thread_runnable = thread0_thread_runnable,
40         .thread_paused = thread0_thread_runnable,
41         .thread_has_blocked = thread0_thread_has_blocked,
42         .thread_exited = thread0_thread_exited,
43         .thread_create = thread0_thread_create,
44         .sync_alloc = thread0_sync_alloc,
45         .sync_free = thread0_sync_free,
46         .sync_get_next = thread0_sync_get_next,
47         .sync_get_uth = thread0_sync_get_uth,
48 };
49
50 /* externed into uthread.c */
51 struct uthread *thread0_uth;
52
53 /* Our thread0 is actually allocated in uthread as just a struct uthread, so we
54  * don't actually attach this mgmt info to it.  But since we just have one
55  * thread, it doesn't matter. */
56 struct thread0_info {
57         bool                                            is_blocked;
58 };
59 static struct thread0_info thread0_info;
60 static struct event_queue *sysc_evq;
61
62 void thread0_handle_syscall(struct event_msg *ev_msg,
63                             unsigned int ev_type, void *data)
64 {
65         thread0_info.is_blocked = FALSE;
66 }
67
68 void thread0_lib_init(void)
69 {
70         memset(&thread0_info, 0, sizeof(thread0_info));
71         /* we don't care about the message, so don't bother with a UCQ */
72         sysc_evq = get_eventq(EV_MBOX_BITMAP);
73         sysc_evq->ev_flags = EVENT_INDIR | EVENT_WAKEUP;
74 }
75
76 /* Thread0 scheduler ops (for processes that haven't linked in a full 2LS) */
77 static void thread0_sched_entry(void)
78 {
79         /* TODO: support signal handling whenever we run a uthread */
80         if (current_uthread) {
81                 uthread_prep_pending_signals(current_uthread);
82                 run_current_uthread();
83                 assert(0);
84         }
85         while (1) {
86                 if (!thread0_info.is_blocked) {
87                         uthread_prep_pending_signals(thread0_uth);
88                         run_uthread(thread0_uth);
89                         assert(0);
90                 }
91                 sys_yield(FALSE);
92                 handle_events(0);
93         }
94 }
95
96 static void thread0_thread_blockon_sysc(struct uthread *uthread, void *arg)
97 {
98         struct syscall *sysc = (struct syscall*)arg;
99         thread0_thread_has_blocked(uthread, NULL, 0);
100         if (!register_evq(sysc, sysc_evq))
101                 thread0_thread_runnable(uthread);
102 }
103
104 static void refl_error(struct uthread *uth, unsigned int trap_nr,
105                        unsigned int err, unsigned long aux)
106 {
107         printf("Thread has unhandled fault: %d, err: %d, aux: %p\n",
108                trap_nr, err, aux);
109         /* Note that uthread.c already copied out our ctx into the uth
110          * struct */
111         print_user_context(&uth->u_ctx);
112         printf("Turn on printx to spew unhandled, malignant trap info\n");
113         exit(-1);
114 }
115
116 static bool handle_page_fault(struct uthread *uth, unsigned int err,
117                               unsigned long aux)
118 {
119         if (!(err & PF_VMR_BACKED))
120                 return FALSE;
121         syscall_async(&uth->local_sysc, SYS_populate_va, aux, 1);
122         __block_uthread_on_async_sysc(uth);
123         return TRUE;
124 }
125
126 static void thread0_thread_refl_fault(struct uthread *uth,
127                                       struct user_context *ctx)
128 {
129         unsigned int trap_nr = __arch_refl_get_nr(ctx);
130         unsigned int err = __arch_refl_get_err(ctx);
131         unsigned long aux = __arch_refl_get_aux(ctx);
132
133         assert(ctx->type == ROS_HW_CTX);
134         switch (trap_nr) {
135         case HW_TRAP_PAGE_FAULT:
136                 if (!handle_page_fault(uth, err, aux))
137                         refl_error(uth, trap_nr, err, aux);
138                 break;
139         default:
140                 refl_error(uth, trap_nr, err, aux);
141         }
142 }
143
144 static void thread0_thread_runnable(struct uthread *uth)
145 {
146         thread0_info.is_blocked = FALSE;
147 }
148
149 static void thread0_thread_has_blocked(struct uthread *uth, uth_sync_t sync,
150                                        int flags)
151 {
152         assert(!thread0_info.is_blocked);
153         thread0_info.is_blocked = TRUE;
154 }
155
156 /* Actually, a 2LS only needs to implement this if it calls
157  * uth_2ls_thread_exit().  Keep it here to catch bugs. */
158 static void thread0_thread_exited(struct uthread *uth)
159 {
160         assert(0);
161 }
162
163 static struct uthread *thread0_thread_create(void *(*func)(void *), void *arg)
164 {
165         panic("Thread0 sched asked to create more threads!");
166 }
167
168 static uth_sync_t thread0_sync_alloc(void)
169 {
170         return (void*)0xf00baa;
171 }
172
173 static void thread0_sync_free(uth_sync_t s)
174 {
175 }
176
177 static struct uthread *thread0_sync_get_next(uth_sync_t s)
178 {
179         if (thread0_info.is_blocked) {
180                 /* Note we don't clear is_blocked.  Runnable does that, which should be
181                  * called before the next get_next (since we have only one thread). */
182                 return thread0_uth;
183         } else {
184                 return NULL;
185         }
186 }
187
188 static bool thread0_sync_get_uth(uth_sync_t s, struct uthread *uth)
189 {
190         assert(uth == thread0_uth);
191         if (thread0_info.is_blocked) {
192                 /* Note we don't clear is_blocked.  Runnable does that. */
193                 return TRUE;
194         }
195         return FALSE;
196 }