seperate function for getting the stack pointer. moving towards a platform-specific...
[cake.git] / arch / all-unix / kernel / host_intr.c
blob8f30f8772a1e5af065dc25c3a33e8bc4140639c6
1 /* theory of operation
3 * at the start, there's a single process. core_init() is called system
4 * initialisation. it starts a thread, the switcher thread.
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.
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)
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
24 #define DEBUG 1
26 #include <aros/system.h>
27 #include <exec/types.h>
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>
37 #include <exec/lists.h>
38 #include <exec/execbase.h>
40 #include "kernel_intern.h"
41 #include "syscall.h"
42 #include "host_debug.h"
44 struct ExecBase **SysBasePtr;
45 struct KernelBase **KernelBasePtr;
47 int irq_enabled;
48 int in_supervisor;
49 int sleep_state;
51 void core_intr_disable(void) {
52 D(printf("[kernel] disabling interrupts\n"));
53 irq_enabled = 0;
56 void core_intr_enable(void) {
57 D(printf("[kernel] enabling interrupts\n"));
58 irq_enabled = 1;
61 int core_is_super(void) {
62 return in_supervisor;
65 pthread_t main_thread;
66 pthread_t switcher_thread;
67 pthread_t timer_thread;
69 unsigned long timer_period;
71 pthread_mutex_t irq_lock = PTHREAD_MUTEX_INITIALIZER;
72 pthread_cond_t irq_cond = PTHREAD_COND_INITIALIZER;
73 uint32_t irq_bits;
75 sem_t main_sem;
76 sem_t switcher_sem;
78 ucontext_t last_ctx;
79 ucontext_t next_ctx;
81 syscall_id_t syscall;
83 void core_syscall(syscall_id_t type) {
84 syscall = type;
86 pthread_mutex_lock(&irq_lock);
87 irq_bits |= 0x2;
88 pthread_mutex_unlock(&irq_lock);
90 pthread_cond_signal(&irq_cond);
93 static void *timer_entry(void *arg) {
94 struct timespec ts;
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++;
103 clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &ts, NULL);
105 pthread_mutex_lock(&irq_lock);
106 irq_bits |= 0x1;
107 pthread_mutex_unlock(&irq_lock);
109 pthread_cond_signal(&irq_cond);
113 static void *switcher_entry(void *arg) {
114 uint32_t irq_bits_current;
116 irq_bits = 0;
118 sem_init(&main_sem, 0, 0);
119 sem_init(&switcher_sem, 0, 0);
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);
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);
131 D(printf("[kernel] interrupt received, irq bits are 0x%x\n", irq_bits_current));
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);
140 in_supervisor++;
142 if (irq_bits_current & 0x2) {
143 switch (syscall) {
144 case sc_CAUSE:
145 core_Cause(*SysBasePtr);
146 break;
148 case sc_DISPATCH:
149 core_Dispatch(&last_ctx, &next_ctx);
150 break;
152 case sc_SWITCH:
153 core_Switch(&last_ctx, &next_ctx);
154 break;
156 case sc_SCHEDULE:
157 core_Schedule(&last_ctx, &next_ctx);
158 break;
162 /* if interrupts are enabled, then its time to schedule a new task */
163 if (irq_enabled)
164 core_ExitInterrupt(&last_ctx, &next_ctx);
166 in_supervisor--;
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;
172 /* ready to go, give the main task a kick */
173 else
174 sem_post(&main_sem);
177 return NULL;
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;
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));
188 /* tell the switcher to proceed */
189 sem_post(&switcher_sem);
191 /* wait for it to run the scheduler and whatever else */
192 sem_wait(&main_sem);
194 /* switcher has given us the new context, jump to it */
195 setcontext(&next_ctx);
198 int core_init(unsigned long TimerPeriod, struct ExecBase **SysBasePointer, struct KernelBase **KernelBasePointer) {
199 struct sigaction sa;
200 pthread_attr_t thread_attrs;
202 return 0;
204 D(printf("[kernel] initialising interrupts and task switching\n"));
206 SysBasePtr = SysBasePointer;
207 KernelBasePtr = KernelBasePointer;
209 irq_enabled = 0;
210 in_supervisor = 0;
211 sleep_state = 0;
213 timer_period = TimerPeriod;
215 sa.sa_flags = SA_SIGINFO;
216 sa.sa_sigaction = main_switch_handler;
217 sigaction(SIGUSR1, &sa, NULL);
219 main_thread = pthread_self();
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);
226 D(printf("[kernel] threads started, switcher id %d, timer id %d\n", switcher_thread, timer_thread));
228 return 0;