User can set how long sys_block() sleeps
[akaros.git] / tests / syscall.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <parlib.h>
4 #include <event.h>
5 #include <vcore.h>
6 #include <rassert.h>
7 #include <ros/bcq.h>
8 #include <uthread.h>
9
10 static void handle_syscall(struct event_msg *ev_msg, unsigned int ev_type,
11                            bool overflow);
12 struct syscall sysc = {0};
13 struct event_queue *ev_q;
14 void *core0_tls = 0;
15
16 void ghetto_vcore_entry(void);
17 struct uthread *ghetto_init(void)
18 {
19         return malloc(sizeof(struct uthread));
20 }
21
22 struct schedule_ops ghetto_sched_ops = {
23         .sched_init = ghetto_init,
24         .sched_entry = ghetto_vcore_entry,
25 };
26 struct schedule_ops *sched_ops = &ghetto_sched_ops;
27
28 int main(int argc, char** argv)
29 {
30         int num_started, retval;
31         unsigned int ev_type;
32
33         /* register our syscall handler (2LS does this) */
34         ev_handlers[EV_SYSCALL] = handle_syscall;
35
36         printf("Trying to block\n");
37         /* Not doing anything else to it: no EVENT_IPI yet, etc. */
38         ev_q = get_big_event_q();
39         /* issue the diagnostic block syscall */
40         sysc.num = SYS_block;
41         sysc.arg0 = 5000;       /* 5ms */
42         sysc.ev_q = ev_q;
43         /* Trap */
44         num_started = __ros_arch_syscall((long)&sysc, 1);
45         if (!(atomic_read(&sysc.flags) & SC_DONE))
46                 printf("Not done, looping!\n");
47         #if 0
48         /* You could poll on this */
49         while (!(sysc.flags & SC_DONE))
50                 cpu_relax();
51         #endif
52         /* But let's check on events...  Spin til something happened, then handle
53          * events.  This method is just used for this testing code. */
54         while (!event_activity(ev_q->ev_mbox, ev_q->ev_flags))
55                 cpu_relax();
56         handle_event_q(ev_q);
57         /* by now, we should have run our handler */
58         /********************************************************/
59         /* Start MCP / IPI test */
60         printf("Switching to _M mode and testing an IPI-d ev_q\n");
61         printf("Our indirect ev_q is %08p\n", ev_q);
62
63 /* begin: stuff userspace needs to do before switching to multi-mode */
64         /* Note we don't need to set up event reception for any particular kevent.
65          * The ev_q in the syscall said to send an IPI to vcore 0 which means an
66          * EV_EVENT will be sent straight to vcore0. */
67         /* Need to save this somewhere that you can find it again when restarting
68          * core0 */
69         core0_tls = get_tls_desc(0);
70         /* Need to save our floating point state somewhere (like in the
71          * user_thread_tcb so it can be restarted too */
72         enable_notifs(0);
73 /* end: stuff userspace needs to do before switching to multi-mode */
74
75         retval = vcore_request(1);
76         if (retval < 0)
77                 printf("No cores granted, Rut Ro Raggy!\n");
78         /* now we're back in thread 0 on vcore 0 */
79         ev_q->ev_flags = EVENT_IPI;
80         ev_q->ev_vcore = 0;
81         sysc.u_data = (void*)1; /* using this to loop on */
82         /* issue the diagnostic blocking syscall */
83         sysc.num = SYS_block;
84         sysc.arg0 = 5000;       /* 5ms */
85         sysc.ev_q = ev_q;
86         num_started = __ros_arch_syscall((long)&sysc, 1);
87         /* have this thread "wait" */
88         if (!(atomic_read(&sysc.flags) & SC_DONE))
89                 printf("Not done, looping on a local variable!\n");
90         while (sysc.u_data)
91                 cpu_relax();
92         assert(atomic_read(&sysc.flags) & SC_DONE);
93         printf("Syscall unblocked, IPI broke me out of the loop.\n");
94
95         /* done */
96         put_big_event_q(ev_q);
97         printf("Syscall test exiting\n");
98         return 0;
99 }
100
101 static void handle_syscall(struct event_msg *ev_msg, unsigned int ev_type,
102                            bool overflow)
103 {
104         struct syscall *my_sysc;
105         if (!ev_msg)
106                 return;
107         my_sysc = ev_msg->ev_arg3;
108         printf("Handling syscall event for sysc %08p (%08p)\n",
109                my_sysc, &sysc);
110         /* our syscall should be done (we ought to check the msg pointer) */
111         if (atomic_read(&sysc.flags) & SC_DONE) 
112                 printf("Syscall is done, retval: %d\n", sysc.retval);
113         else
114                 printf("BUG! Syscall wasn't done!\n");
115         /* signal to thread 0 that the sysc is done, just to show this
116          * is getting done in vcore context. */
117         my_sysc->u_data = 0;
118 }
119
120 void ghetto_vcore_entry(void)
121 {
122         uint32_t vcoreid = vcore_id();
123         static bool first_time = TRUE;
124
125 /* begin: stuff userspace needs to do to handle notifications */
126
127         /* Restart vcore0's context. */
128         if (vcoreid == 0) {
129                 run_current_uthread();
130                 panic("should never see me!");
131         }       
132         /* unmask notifications once you can let go of the notif_tf and it is okay
133          * to clobber the transition stack.
134          * Check Documentation/processes.txt: 4.2.4.  In real code, you should be
135          * popping the tf of whatever user process you want (get off the x-stack) */
136         struct preempt_data *vcpd;
137         vcpd = &__procdata.vcore_preempt_data[vcoreid];
138         vcpd->notif_enabled = TRUE;
139         
140 /* end: stuff userspace needs to do to handle notifications */
141         /* if you have other vcores, they'll just chill here */
142         while(1);
143 }