8f30f8772a1e5af065dc25c3a33e8bc4140639c6
[cake.git] / arch / all-unix / kernel / host_intr.c
1 /* theory of operation
2  *
3  * at the start, there's a single process. core_init() is called system
4  * initialisation. it starts a thread, the switcher thread.
5  *
6  * the main thread runs the "current" aros task. thats all. if left unchecked,
7  * it would run the same task forever. thats not much fun though, which is
8  * what the switcher thread is for.
9  *
10  * the switcher thread receives interrupts from a variety of sources (virtual
11  * hardware) including a timer thread (for normal task switch operation). when
12  * an interrupt occurs, the following happens (high level)
13  *
14  * - switcher signals main that its time for a context switch
15  * - main responds by storing the current context and then waiting
16  * - switcher saves the current context into the current aros task
17  * - switcher runs the scheduler with the current interrupt state
18  * - switcher loads the context for the scheduled task as the new current context
19  * - switcher signals main
20  * - main wakes up and jumps to the current context
21  * - switcher loops and waits for the next interrupt
22  */
23
24 #define DEBUG 1
25
26 #include <aros/system.h>
27 #include <exec/types.h>
28
29 #include <stddef.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <ucontext.h>
33 #include <pthread.h>
34 #include <semaphore.h>
35 #include <time.h>
36
37 #include <exec/lists.h>
38 #include <exec/execbase.h>
39
40 #include "kernel_intern.h"
41 #include "syscall.h"
42 #include "host_debug.h"
43
44 struct ExecBase **SysBasePtr;
45 struct KernelBase **KernelBasePtr;
46
47 int irq_enabled;
48 int in_supervisor;
49 int sleep_state;
50
51 void core_intr_disable(void) {
52     D(printf("[kernel] disabling interrupts\n"));
53     irq_enabled = 0;
54 }
55
56 void core_intr_enable(void) {
57     D(printf("[kernel] enabling interrupts\n"));
58     irq_enabled = 1;
59 }
60
61 int core_is_super(void) {
62     return in_supervisor;
63 }
64
65 pthread_t main_thread;
66 pthread_t switcher_thread;
67 pthread_t timer_thread;
68
69 unsigned long timer_period;
70
71 pthread_mutex_t irq_lock = PTHREAD_MUTEX_INITIALIZER;
72 pthread_cond_t irq_cond = PTHREAD_COND_INITIALIZER;
73 uint32_t irq_bits;
74
75 sem_t main_sem;
76 sem_t switcher_sem;
77
78 ucontext_t last_ctx;
79 ucontext_t next_ctx;
80
81 syscall_id_t syscall;
82
83 void core_syscall(syscall_id_t type) {
84     syscall = type;
85
86     pthread_mutex_lock(&irq_lock);
87     irq_bits |= 0x2;
88     pthread_mutex_unlock(&irq_lock);
89
90     pthread_cond_signal(&irq_cond);
91 }
92
93 static void *timer_entry(void *arg) {
94     struct timespec ts;
95
96     while (1) {
97         clock_gettime(CLOCK_REALTIME, &ts);
98         ts.tv_nsec += 1000000000 / timer_period;
99         if (ts.tv_nsec > 999999999) {
100             ts.tv_nsec -= 1000000000;
101             ts.tv_sec++;
102         }
103         clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &ts, NULL);
104
105         pthread_mutex_lock(&irq_lock);
106         irq_bits |= 0x1;
107         pthread_mutex_unlock(&irq_lock);
108
109         pthread_cond_signal(&irq_cond);
110     }
111 }
112
113 static void *switcher_entry(void *arg) {
114     uint32_t irq_bits_current;
115
116     irq_bits = 0;
117
118     sem_init(&main_sem, 0, 0);
119     sem_init(&switcher_sem, 0, 0);
120
121     while (1) {
122         /* wait for an interrupt */
123         pthread_mutex_lock(&irq_lock);
124         while (irq_bits == 0) pthread_cond_wait(&irq_cond, &irq_lock);
125
126         /* save the current interrupt bits and allow new interrupts to occur */
127         irq_bits_current = irq_bits;
128         irq_bits = 0;
129         pthread_mutex_unlock(&irq_lock);
130
131         D(printf("[kernel] interrupt received, irq bits are 0x%x\n", irq_bits_current));
132
133         /* tell the main task to stop and wait for its signal to proceed */
134         if (sleep_state != ss_SLEEPING) {
135             sem_post(&main_sem);
136             pthread_kill(main_thread, SIGUSR1);
137             sem_wait(&switcher_sem);
138         }
139
140         in_supervisor++;
141
142         if (irq_bits_current & 0x2) {
143             switch (syscall) {
144                 case sc_CAUSE:
145                     core_Cause(*SysBasePtr);
146                     break;
147
148                 case sc_DISPATCH:
149                     core_Dispatch(&last_ctx, &next_ctx);
150                     break;
151
152                 case sc_SWITCH:
153                     core_Switch(&last_ctx, &next_ctx);
154                     break;
155
156                 case sc_SCHEDULE:
157                     core_Schedule(&last_ctx, &next_ctx);
158                     break;
159             }
160         }
161
162         /* if interrupts are enabled, then its time to schedule a new task */
163         if (irq_enabled)
164             core_ExitInterrupt(&last_ctx, &next_ctx);
165
166         in_supervisor--;
167
168         /* if we're sleeping then we don't want to wake the main task just now */
169         if (sleep_state != ss_RUNNING)
170             sleep_state = ss_SLEEPING;
171
172         /* ready to go, give the main task a kick */
173         else
174             sem_post(&main_sem);
175     }
176
177     return NULL;
178 }
179
180 static void main_switch_handler(int signo, siginfo_t *si, void *vctx) {
181     /* make sure we were signalled by the switcher thread and not somewhere else */
182     if (sem_trywait(&main_sem) < 0)
183         return;
184
185     /* switcher thread is now waiting for us. save the current context somewhere it can get it */
186     memcpy(&last_ctx, vctx, sizeof(ucontext_t));
187
188     /* tell the switcher to proceed */
189     sem_post(&switcher_sem);
190
191     /* wait for it to run the scheduler and whatever else */
192     sem_wait(&main_sem);
193
194     /* switcher has given us the new context, jump to it */
195     setcontext(&next_ctx);
196 }
197
198 int core_init(unsigned long TimerPeriod, struct ExecBase **SysBasePointer, struct KernelBase **KernelBasePointer) {
199     struct sigaction sa;
200     pthread_attr_t thread_attrs;
201
202     return 0;
203
204     D(printf("[kernel] initialising interrupts and task switching\n"));
205
206     SysBasePtr = SysBasePointer;
207     KernelBasePtr = KernelBasePointer;
208
209     irq_enabled = 0;
210     in_supervisor = 0;
211     sleep_state = 0;
212
213     timer_period = TimerPeriod;
214
215     sa.sa_flags = SA_SIGINFO;
216     sa.sa_sigaction = main_switch_handler;
217     sigaction(SIGUSR1, &sa, NULL);
218
219     main_thread = pthread_self();
220
221     pthread_attr_init(&thread_attrs);
222     pthread_attr_setdetachstate(&thread_attrs, PTHREAD_CREATE_DETACHED);
223     pthread_create(&switcher_thread, &thread_attrs, switcher_entry, NULL);
224     pthread_create(&timer_thread, &thread_attrs, timer_entry, NULL);
225
226     D(printf("[kernel] threads started, switcher id %d, timer id %d\n", switcher_thread, timer_thread));
227
228     return 0;
229 }