blob: f5feeebeee22539462b98ee53bc8ac1a59dce1a8 [file] [log] [blame]
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -05001// 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
Kevin O'Connorad901592009-12-13 11:25:25 -05009#include "bregs.h" // CR0_PE
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -050010
Kevin O'Connor9c447c32010-05-23 10:24:22 -040011// Thread info - stored at bottom of each thread stack - don't change
12// without also updating the inline assembler below.
13struct thread_info {
14 struct thread_info *next;
15 void *stackpos;
16 struct thread_info **pprev;
17};
18struct thread_info VAR16VISIBLE MainThread;
19
20
21/****************************************************************
22 * Low level helpers
23 ****************************************************************/
24
Kevin O'Connor1ca05b02010-01-03 17:43:37 -050025static inline u32 getcr0(void) {
Kevin O'Connora7fc5de2009-12-13 11:48:18 -050026 u32 cr0;
27 asm("movl %%cr0, %0" : "=r"(cr0));
28 return cr0;
29}
30static inline void sgdt(struct descloc_s *desc) {
31 asm("sgdtl %0" : "=m"(*desc));
32}
33static inline void lgdt(struct descloc_s *desc) {
34 asm("lgdtl %0" : : "m"(*desc) : "memory");
35}
36
37// Call a 32bit SeaBIOS function from a 16bit SeaBIOS function.
38static inline int
39call32(void *func)
40{
41 ASSERT16();
42 u32 cr0 = getcr0();
43 if (cr0 & CR0_PE)
44 // Called in 16bit protected mode?!
45 return -1;
46
47 // Backup cmos index register and disable nmi
48 u8 cmosindex = inb(PORT_CMOS_INDEX);
49 outb(cmosindex | NMI_DISABLE_BIT, PORT_CMOS_INDEX);
50 inb(PORT_CMOS_DATA);
51
52 // Backup fs/gs and gdt
53 u16 fs = GET_SEG(FS), gs = GET_SEG(GS);
54 struct descloc_s gdt;
55 sgdt(&gdt);
56
Kevin O'Connora7fc5de2009-12-13 11:48:18 -050057 u32 bkup_ss, bkup_esp;
58 asm volatile(
59 // Backup ss/esp / set esp to flat stack location
60 " movl %%ss, %0\n"
61 " movl %%esp, %1\n"
62 " shll $4, %0\n"
63 " addl %0, %%esp\n"
64 " movl %%ss, %0\n"
65
Kevin O'Connorfb214dc2009-12-20 13:11:17 -050066 // Transition to 32bit mode, call func, return to 16bit
Kevin O'Connora7fc5de2009-12-13 11:48:18 -050067 " pushl $(" __stringify(BUILD_BIOS_ADDR) " + 1f)\n"
68 " jmp transition32\n"
69 " .code32\n"
Kevin O'Connorbca07362010-03-20 20:41:38 -040070 "1:calll *%2\n"
Kevin O'Connora7fc5de2009-12-13 11:48:18 -050071 " pushl $2f\n"
72 " jmp transition16big\n"
73
74 // Restore ds/ss/esp
75 " .code16gcc\n"
76 "2:movl %0, %%ds\n"
77 " movl %0, %%ss\n"
78 " movl %1, %%esp\n"
79 : "=&r" (bkup_ss), "=&r" (bkup_esp)
Kevin O'Connorbca07362010-03-20 20:41:38 -040080 : "r" (func)
Kevin O'Connora7fc5de2009-12-13 11:48:18 -050081 : "eax", "ecx", "edx", "cc", "memory");
82
83 // Restore gdt and fs/gs
84 lgdt(&gdt);
85 SET_SEG(FS, fs);
86 SET_SEG(GS, gs);
87
88 // Restore cmos index register
89 outb(cmosindex, PORT_CMOS_INDEX);
90 inb(PORT_CMOS_DATA);
91 return 0;
92}
93
Kevin O'Connor9c447c32010-05-23 10:24:22 -040094// 16bit trampoline for enabling irqs from 32bit mode.
95ASM16(
96 " .global trampoline_checkirqs\n"
97 "trampoline_checkirqs:\n"
98 " rep ; nop\n"
99 " lretw"
100 );
101
102static void
103check_irqs(void)
104{
105 if (MODESEGMENT) {
106 asm volatile(
107 "sti\n"
108 "nop\n"
109 "rep ; nop\n"
110 "cli\n"
111 "cld\n"
112 : : :"memory");
113 return;
114 }
115 extern void trampoline_checkirqs();
116 struct bregs br;
117 br.flags = F_IF;
118 br.code.seg = SEG_BIOS;
119 br.code.offset = (u32)&trampoline_checkirqs;
120 call16big(&br);
121}
122
123// 16bit trampoline for waiting for an irq from 32bit mode.
124ASM16(
125 " .global trampoline_waitirq\n"
126 "trampoline_waitirq:\n"
127 " sti\n"
128 " hlt\n"
129 " lretw"
130 );
131
132// Wait for next irq to occur.
133void
134wait_irq(void)
135{
136 if (MODESEGMENT) {
137 asm volatile("sti ; hlt ; cli ; cld": : :"memory");
138 return;
139 }
140 if (CONFIG_THREADS && MainThread.next != &MainThread) {
141 // Threads still active - do a yield instead.
142 yield();
143 return;
144 }
145 extern void trampoline_waitirq();
146 struct bregs br;
147 br.flags = 0;
148 br.code.seg = SEG_BIOS;
149 br.code.offset = (u32)&trampoline_waitirq;
150 call16big(&br);
151}
152
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500153
154/****************************************************************
155 * Stack in EBDA
156 ****************************************************************/
157
158// Switch to the extra stack in ebda and call a function.
159inline u32
Kevin O'Connorbca07362010-03-20 20:41:38 -0400160stack_hop(u32 eax, u32 edx, void *func)
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500161{
162 ASSERT16();
163 u16 ebda_seg = get_ebda_seg(), bkup_ss;
164 u32 bkup_esp;
165 asm volatile(
166 // Backup current %ss/%esp values.
167 "movw %%ss, %w3\n"
168 "movl %%esp, %4\n"
169 // Copy ebda seg to %ds/%ss and set %esp
170 "movw %w6, %%ds\n"
171 "movw %w6, %%ss\n"
172 "movl %5, %%esp\n"
173 // Call func
Kevin O'Connorbca07362010-03-20 20:41:38 -0400174 "calll *%2\n"
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500175 // Restore segments and stack
176 "movw %w3, %%ds\n"
177 "movw %w3, %%ss\n"
178 "movl %4, %%esp"
Kevin O'Connorbca07362010-03-20 20:41:38 -0400179 : "+a" (eax), "+d" (edx), "+c" (func), "=&r" (bkup_ss), "=&r" (bkup_esp)
180 : "i" (EBDA_OFFSET_TOP_STACK), "r" (ebda_seg)
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500181 : "cc", "memory");
182 return eax;
183}
184
185
186/****************************************************************
187 * Threads
188 ****************************************************************/
189
190#define THREADSTACKSIZE 4096
Kevin O'Connorad901592009-12-13 11:25:25 -0500191int VAR16VISIBLE CanPreempt;
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500192
193void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500194thread_setup(void)
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500195{
196 MainThread.next = &MainThread;
Kevin O'Connora7eb8fc2010-04-02 13:13:23 -0400197 MainThread.pprev = &MainThread.next;
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500198 MainThread.stackpos = NULL;
Kevin O'Connorad901592009-12-13 11:25:25 -0500199 CanPreempt = 0;
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500200}
201
Kevin O'Connorad901592009-12-13 11:25:25 -0500202// Return the 'struct thread_info' for the currently running thread.
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500203struct thread_info *
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500204getCurThread(void)
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500205{
206 u32 esp = getesp();
207 if (esp <= BUILD_STACK_ADDR)
208 return &MainThread;
209 return (void*)ALIGN_DOWN(esp, THREADSTACKSIZE);
210}
211
Kevin O'Connorad901592009-12-13 11:25:25 -0500212// Switch to next thread stack.
213static void
214switch_next(struct thread_info *cur)
215{
216 struct thread_info *next = cur->next;
Kevin O'Connorfb214dc2009-12-20 13:11:17 -0500217 if (cur == next)
218 // Nothing to do.
219 return;
Kevin O'Connorad901592009-12-13 11:25:25 -0500220 asm volatile(
221 " pushl $1f\n" // store return pc
222 " pushl %%ebp\n" // backup %ebp
223 " movl %%esp, 4(%%eax)\n" // cur->stackpos = %esp
224 " movl 4(%%ecx), %%esp\n" // %esp = next->stackpos
225 " popl %%ebp\n" // restore %ebp
226 " retl\n" // restore pc
227 "1:\n"
228 : "+a"(cur), "+c"(next)
229 :
230 : "ebx", "edx", "esi", "edi", "cc", "memory");
231}
232
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500233// Briefly permit irqs to occur.
234void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500235yield(void)
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500236{
Kevin O'Connor52a300f2009-12-26 23:32:57 -0500237 if (MODESEGMENT || !CONFIG_THREADS) {
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500238 // Just directly check irqs.
239 check_irqs();
240 return;
241 }
242 struct thread_info *cur = getCurThread();
243 if (cur == &MainThread)
244 // Permit irqs to fire
245 check_irqs();
246
247 // Switch to the next thread
Kevin O'Connorad901592009-12-13 11:25:25 -0500248 switch_next(cur);
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500249}
250
251// Last thing called from a thread (called on "next" stack).
252static void
253__end_thread(struct thread_info *old)
254{
Kevin O'Connora7eb8fc2010-04-02 13:13:23 -0400255 old->next->pprev = old->pprev;
256 *old->pprev = old->next;
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500257 free(old);
258 dprintf(DEBUG_thread, "\\%08x/ End thread\n", (u32)old);
Kevin O'Connore438b0c2010-05-01 12:20:33 -0400259 if (MainThread.next == &MainThread)
260 dprintf(1, "All threads complete.\n");
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500261}
262
Kevin O'Connorad901592009-12-13 11:25:25 -0500263// Create a new thread and start executing 'func' in it.
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500264void
265run_thread(void (*func)(void*), void *data)
266{
Kevin O'Connor52a300f2009-12-26 23:32:57 -0500267 ASSERT32FLAT();
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500268 if (! CONFIG_THREADS)
269 goto fail;
270 struct thread_info *thread;
271 thread = memalign_tmphigh(THREADSTACKSIZE, THREADSTACKSIZE);
272 if (!thread)
273 goto fail;
274
275 thread->stackpos = (void*)thread + THREADSTACKSIZE;
276 struct thread_info *cur = getCurThread();
Kevin O'Connora7eb8fc2010-04-02 13:13:23 -0400277 thread->next = cur;
278 thread->pprev = cur->pprev;
279 cur->pprev = &thread->next;
280 *thread->pprev = thread;
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500281
282 dprintf(DEBUG_thread, "/%08x\\ Start thread\n", (u32)thread);
283 asm volatile(
284 // Start thread
285 " pushl $1f\n" // store return pc
286 " pushl %%ebp\n" // backup %ebp
287 " movl %%esp, 4(%%edx)\n" // cur->stackpos = %esp
288 " movl 4(%%ebx), %%esp\n" // %esp = thread->stackpos
289 " calll *%%ecx\n" // Call func
290
291 // End thread
292 " movl (%%ebx), %%ecx\n" // %ecx = thread->next
293 " movl 4(%%ecx), %%esp\n" // %esp = next->stackpos
294 " movl %%ebx, %%eax\n"
295 " calll %4\n" // call __end_thread(thread)
296 " popl %%ebp\n" // restore %ebp
297 " retl\n" // restore pc
298 "1:\n"
299 : "+a"(data), "+c"(func), "+b"(thread), "+d"(cur)
300 : "m"(*(u8*)__end_thread)
301 : "esi", "edi", "cc", "memory");
302 return;
303
304fail:
305 func(data);
306}
307
Kevin O'Connorad901592009-12-13 11:25:25 -0500308// Wait for all threads (other than the main thread) to complete.
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500309void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500310wait_threads(void)
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500311{
Kevin O'Connor52a300f2009-12-26 23:32:57 -0500312 ASSERT32FLAT();
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500313 if (! CONFIG_THREADS)
314 return;
315 while (MainThread.next != &MainThread)
316 yield();
317}
Kevin O'Connorad901592009-12-13 11:25:25 -0500318
Kevin O'Connore9086652010-02-27 13:49:47 -0500319void
320mutex_lock(struct mutex_s *mutex)
321{
322 ASSERT32FLAT();
323 if (! CONFIG_THREADS)
324 return;
325 while (mutex->isLocked)
326 yield();
327 mutex->isLocked = 1;
328}
329
330void
331mutex_unlock(struct mutex_s *mutex)
332{
333 ASSERT32FLAT();
334 if (! CONFIG_THREADS)
335 return;
336 mutex->isLocked = 0;
337}
338
Kevin O'Connorad901592009-12-13 11:25:25 -0500339
340/****************************************************************
341 * Thread preemption
342 ****************************************************************/
343
344static u32 PreemptCount;
345
346// Turn on RTC irqs and arrange for them to check the 32bit threads.
347void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500348start_preempt(void)
Kevin O'Connorad901592009-12-13 11:25:25 -0500349{
350 if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS)
351 return;
352 CanPreempt = 1;
353 PreemptCount = 0;
354 useRTC();
355}
356
357// Turn off RTC irqs / stop checking for thread execution.
358void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500359finish_preempt(void)
Kevin O'Connorad901592009-12-13 11:25:25 -0500360{
Kevin O'Connora7eb8fc2010-04-02 13:13:23 -0400361 if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS) {
362 yield();
Kevin O'Connorad901592009-12-13 11:25:25 -0500363 return;
Kevin O'Connora7eb8fc2010-04-02 13:13:23 -0400364 }
Kevin O'Connorad901592009-12-13 11:25:25 -0500365 CanPreempt = 0;
366 releaseRTC();
Kevin O'Connore438b0c2010-05-01 12:20:33 -0400367 dprintf(9, "Done preempt - %d checks\n", PreemptCount);
Kevin O'Connora7eb8fc2010-04-02 13:13:23 -0400368 yield();
Kevin O'Connorad901592009-12-13 11:25:25 -0500369}
370
Kevin O'Connord7eb27e2010-03-20 18:17:19 -0400371// Check if preemption is on, and wait for it to complete if so.
372int
373wait_preempt(void)
374{
375 if (MODESEGMENT || !CONFIG_THREADS || !CONFIG_THREAD_OPTIONROMS
376 || !CanPreempt)
377 return 0;
378 while (CanPreempt)
379 yield();
380 return 1;
381}
382
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500383extern void yield_preempt(void);
Kevin O'Connor52a300f2009-12-26 23:32:57 -0500384#if MODESEGMENT == 0
Kevin O'Connorad901592009-12-13 11:25:25 -0500385// Try to execute 32bit threads.
Kevin O'Connor52a300f2009-12-26 23:32:57 -0500386void VISIBLE32FLAT
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500387yield_preempt(void)
Kevin O'Connorad901592009-12-13 11:25:25 -0500388{
389 PreemptCount++;
390 switch_next(&MainThread);
391}
392#endif
393
394// 16bit code that checks if threads are pending and executes them if so.
395void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500396check_preempt(void)
Kevin O'Connorad901592009-12-13 11:25:25 -0500397{
Kevin O'Connorad901592009-12-13 11:25:25 -0500398 if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS
399 || !GET_GLOBAL(CanPreempt)
400 || GET_GLOBAL(MainThread.next) == &MainThread)
401 return;
Kevin O'Connorad901592009-12-13 11:25:25 -0500402
Kevin O'Connora7fc5de2009-12-13 11:48:18 -0500403 call32(yield_preempt);
Kevin O'Connorad901592009-12-13 11:25:25 -0500404}