Enhance experimental option rom "threading" - enable preemption.
[seabios.git] / src / stacks.c
1 // Code for manipulating stack locations.
2 //
3 // Copyright (C) 2009  Kevin O'Connor <kevin@koconnor.net>
4 //
5 // This file may be distributed under the terms of the GNU LGPLv3 license.
6
7 #include "biosvar.h" // get_ebda_seg
8 #include "util.h" // dprintf
9 #include "bregs.h" // CR0_PE
10
11
12 /****************************************************************
13  * Stack in EBDA
14  ****************************************************************/
15
16 // Switch to the extra stack in ebda and call a function.
17 inline u32
18 stack_hop(u32 eax, u32 edx, u32 ecx, void *func)
19 {
20     ASSERT16();
21     u16 ebda_seg = get_ebda_seg(), bkup_ss;
22     u32 bkup_esp;
23     asm volatile(
24         // Backup current %ss/%esp values.
25         "movw %%ss, %w3\n"
26         "movl %%esp, %4\n"
27         // Copy ebda seg to %ds/%ss and set %esp
28         "movw %w6, %%ds\n"
29         "movw %w6, %%ss\n"
30         "movl %5, %%esp\n"
31         // Call func
32         "calll %7\n"
33         // Restore segments and stack
34         "movw %w3, %%ds\n"
35         "movw %w3, %%ss\n"
36         "movl %4, %%esp"
37         : "+a" (eax), "+d" (edx), "+c" (ecx), "=&r" (bkup_ss), "=&r" (bkup_esp)
38         : "i" (EBDA_OFFSET_TOP_STACK), "r" (ebda_seg), "m" (*(u8*)func)
39         : "cc", "memory");
40     return eax;
41 }
42
43
44 /****************************************************************
45  * Threads
46  ****************************************************************/
47
48 #define THREADSTACKSIZE 4096
49
50 struct thread_info {
51     struct thread_info *next;
52     void *stackpos;
53 };
54
55 struct thread_info VAR16VISIBLE MainThread;
56 int VAR16VISIBLE CanPreempt;
57
58 void
59 thread_setup()
60 {
61     MainThread.next = &MainThread;
62     MainThread.stackpos = NULL;
63     CanPreempt = 0;
64 }
65
66 // Return the 'struct thread_info' for the currently running thread.
67 struct thread_info *
68 getCurThread()
69 {
70     u32 esp = getesp();
71     if (esp <= BUILD_STACK_ADDR)
72         return &MainThread;
73     return (void*)ALIGN_DOWN(esp, THREADSTACKSIZE);
74 }
75
76 // Switch to next thread stack.
77 static void
78 switch_next(struct thread_info *cur)
79 {
80     struct thread_info *next = cur->next;
81     asm volatile(
82         "  pushl $1f\n"                 // store return pc
83         "  pushl %%ebp\n"               // backup %ebp
84         "  movl %%esp, 4(%%eax)\n"      // cur->stackpos = %esp
85         "  movl 4(%%ecx), %%esp\n"      // %esp = next->stackpos
86         "  popl %%ebp\n"                // restore %ebp
87         "  retl\n"                      // restore pc
88         "1:\n"
89         : "+a"(cur), "+c"(next)
90         :
91         : "ebx", "edx", "esi", "edi", "cc", "memory");
92 }
93
94 // Briefly permit irqs to occur.
95 void
96 yield()
97 {
98     if (MODE16 || !CONFIG_THREADS) {
99         // Just directly check irqs.
100         check_irqs();
101         return;
102     }
103     struct thread_info *cur = getCurThread();
104     if (cur == &MainThread)
105         // Permit irqs to fire
106         check_irqs();
107
108     // Switch to the next thread
109     switch_next(cur);
110 }
111
112 // Last thing called from a thread (called on "next" stack).
113 static void
114 __end_thread(struct thread_info *old)
115 {
116     struct thread_info *pos = &MainThread;
117     while (pos->next != old)
118         pos = pos->next;
119     pos->next = old->next;
120     free(old);
121     dprintf(DEBUG_thread, "\\%08x/ End thread\n", (u32)old);
122 }
123
124 // Create a new thread and start executing 'func' in it.
125 void
126 run_thread(void (*func)(void*), void *data)
127 {
128     ASSERT32();
129     if (! CONFIG_THREADS)
130         goto fail;
131     struct thread_info *thread;
132     thread = memalign_tmphigh(THREADSTACKSIZE, THREADSTACKSIZE);
133     if (!thread)
134         goto fail;
135
136     thread->stackpos = (void*)thread + THREADSTACKSIZE;
137     struct thread_info *cur = getCurThread();
138     thread->next = cur->next;
139     cur->next = thread;
140
141     dprintf(DEBUG_thread, "/%08x\\ Start thread\n", (u32)thread);
142     asm volatile(
143         // Start thread
144         "  pushl $1f\n"                 // store return pc
145         "  pushl %%ebp\n"               // backup %ebp
146         "  movl %%esp, 4(%%edx)\n"      // cur->stackpos = %esp
147         "  movl 4(%%ebx), %%esp\n"      // %esp = thread->stackpos
148         "  calll *%%ecx\n"              // Call func
149
150         // End thread
151         "  movl (%%ebx), %%ecx\n"       // %ecx = thread->next
152         "  movl 4(%%ecx), %%esp\n"      // %esp = next->stackpos
153         "  movl %%ebx, %%eax\n"
154         "  calll %4\n"                  // call __end_thread(thread)
155         "  popl %%ebp\n"                // restore %ebp
156         "  retl\n"                      // restore pc
157         "1:\n"
158         : "+a"(data), "+c"(func), "+b"(thread), "+d"(cur)
159         : "m"(*(u8*)__end_thread)
160         : "esi", "edi", "cc", "memory");
161     return;
162
163 fail:
164     func(data);
165 }
166
167 // Wait for all threads (other than the main thread) to complete.
168 void
169 wait_threads()
170 {
171     ASSERT32();
172     if (! CONFIG_THREADS)
173         return;
174     while (MainThread.next != &MainThread)
175         yield();
176 }
177
178
179 /****************************************************************
180  * Thread preemption
181  ****************************************************************/
182
183 static u32 PreemptCount;
184
185 // Turn on RTC irqs and arrange for them to check the 32bit threads.
186 void
187 start_preempt()
188 {
189     if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS)
190         return;
191     CanPreempt = 1;
192     PreemptCount = 0;
193     useRTC();
194 }
195
196 // Turn off RTC irqs / stop checking for thread execution.
197 void
198 finish_preempt()
199 {
200     if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS)
201         return;
202     CanPreempt = 0;
203     releaseRTC();
204     dprintf(1, "Done preempt - %d checks\n", PreemptCount);
205 }
206
207 static inline u32 getcr0() {
208     u32 cr0;
209     asm("movl %%cr0, %0" : "=r"(cr0));
210     return cr0;
211 }
212 static inline void sgdt(struct descloc_s *desc) {
213     asm("sgdtl %0" : "=m"(*desc));
214 }
215 static inline void lgdt(struct descloc_s *desc) {
216     asm("lgdtl %0" : : "m"(*desc) : "memory");
217 }
218
219 #if !MODE16
220 // Try to execute 32bit threads.
221 void VISIBLE32
222 yield_preempt()
223 {
224     PreemptCount++;
225     switch_next(&MainThread);
226 }
227 #endif
228
229 // 16bit code that checks if threads are pending and executes them if so.
230 void
231 check_preempt()
232 {
233     ASSERT16();
234     if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS
235         || !GET_GLOBAL(CanPreempt)
236         || GET_GLOBAL(MainThread.next) == &MainThread)
237         return;
238     u32 cr0 = getcr0();
239     if (cr0 & CR0_PE)
240         // Called in 16bit protected mode?!
241         return;
242
243     // Backup cmos index register and disable nmi
244     u8 cmosindex = inb(PORT_CMOS_INDEX);
245     outb(cmosindex | NMI_DISABLE_BIT, PORT_CMOS_INDEX);
246     inb(PORT_CMOS_DATA);
247
248     // Backup fs/gs and gdt
249     u16 fs = GET_SEG(FS), gs = GET_SEG(GS);
250     struct descloc_s gdt;
251     sgdt(&gdt);
252
253     u32 bkup_ss, bkup_esp;
254     asm volatile(
255         // Backup ss/esp / set esp to flat stack location
256         "  movl %%ss, %0\n"
257         "  movl %%esp, %1\n"
258         "  shll $4, %0\n"
259         "  addl %0, %%esp\n"
260         "  movl %%ss, %0\n"
261
262         // Transition to 32bit mode, call yield_preempt, return to 16bit
263         "  pushl $(" __stringify(BUILD_BIOS_ADDR) " + 1f)\n"
264         "  jmp transition32\n"
265         "  .code32\n"
266         "1:calll (yield_preempt - " __stringify(BUILD_BIOS_ADDR) ")\n"
267         "  pushl $2f\n"
268         "  jmp transition16big\n"
269         "  .code16gcc\n"
270         "2:\n"
271
272         // Restore ds/ss/esp
273         "  movl %0, %%ds\n"
274         "  movl %0, %%ss\n"
275         "  movl %1, %%esp\n"
276         : "=&r" (bkup_ss), "=&r" (bkup_esp)
277         : : "eax", "ecx", "edx", "cc", "memory");
278
279     // Restore gdt and fs/gs
280     lgdt(&gdt);
281     SET_SEG(FS, fs);
282     SET_SEG(GS, gs);
283
284     // Restore cmos index register
285     outb(cmosindex, PORT_CMOS_INDEX);
286     inb(PORT_CMOS_DATA);
287 }