blob: 17f1a4a81915b51fedee507aacc7ba0c8c9a60ad [file] [log] [blame]
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -05001// Code for manipulating stack locations.
2//
Kevin O'Connord1b4f962010-09-15 21:38:16 -04003// Copyright (C) 2009-2010 Kevin O'Connor <kevin@koconnor.net>
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -05004//
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};
Kevin O'Connor4d96edc2010-09-25 14:53:15 -040018struct thread_info VAR32FLATVISIBLE MainThread = {
19 &MainThread, NULL, &MainThread.next
20};
Kevin O'Connor9c447c32010-05-23 10:24:22 -040021
22
23/****************************************************************
24 * Low level helpers
25 ****************************************************************/
26
Kevin O'Connora7fc5de2009-12-13 11:48:18 -050027static inline void sgdt(struct descloc_s *desc) {
28 asm("sgdtl %0" : "=m"(*desc));
29}
30static inline void lgdt(struct descloc_s *desc) {
31 asm("lgdtl %0" : : "m"(*desc) : "memory");
32}
33
34// Call a 32bit SeaBIOS function from a 16bit SeaBIOS function.
Kevin O'Connor533b6282011-07-16 13:13:12 -040035u32 VISIBLE16
Kevin O'Connoraf9629b2010-11-25 09:17:31 -050036call32(void *func, u32 eax, u32 errret)
Kevin O'Connora7fc5de2009-12-13 11:48:18 -050037{
38 ASSERT16();
39 u32 cr0 = getcr0();
40 if (cr0 & CR0_PE)
41 // Called in 16bit protected mode?!
Kevin O'Connoraf9629b2010-11-25 09:17:31 -050042 return errret;
Kevin O'Connora7fc5de2009-12-13 11:48:18 -050043
44 // Backup cmos index register and disable nmi
45 u8 cmosindex = inb(PORT_CMOS_INDEX);
46 outb(cmosindex | NMI_DISABLE_BIT, PORT_CMOS_INDEX);
47 inb(PORT_CMOS_DATA);
48
49 // Backup fs/gs and gdt
50 u16 fs = GET_SEG(FS), gs = GET_SEG(GS);
51 struct descloc_s gdt;
52 sgdt(&gdt);
53
Kevin O'Connora7fc5de2009-12-13 11:48:18 -050054 u32 bkup_ss, bkup_esp;
55 asm volatile(
56 // Backup ss/esp / set esp to flat stack location
57 " movl %%ss, %0\n"
58 " movl %%esp, %1\n"
59 " shll $4, %0\n"
60 " addl %0, %%esp\n"
Kevin O'Connoraf9629b2010-11-25 09:17:31 -050061 " shrl $4, %0\n"
Kevin O'Connora7fc5de2009-12-13 11:48:18 -050062
Kevin O'Connorfb214dc2009-12-20 13:11:17 -050063 // Transition to 32bit mode, call func, return to 16bit
Kevin O'Connor4057f982010-11-25 08:52:50 -050064 " movl $(" __stringify(BUILD_BIOS_ADDR) " + 1f), %%edx\n"
Kevin O'Connora7fc5de2009-12-13 11:48:18 -050065 " jmp transition32\n"
66 " .code32\n"
Kevin O'Connoraf9629b2010-11-25 09:17:31 -050067 "1:calll *%3\n"
Kevin O'Connor4057f982010-11-25 08:52:50 -050068 " movl $2f, %%edx\n"
Kevin O'Connora7fc5de2009-12-13 11:48:18 -050069 " jmp transition16big\n"
70
71 // Restore ds/ss/esp
72 " .code16gcc\n"
73 "2:movl %0, %%ds\n"
74 " movl %0, %%ss\n"
75 " movl %1, %%esp\n"
Kevin O'Connoraf9629b2010-11-25 09:17:31 -050076 : "=&r" (bkup_ss), "=&r" (bkup_esp), "+a" (eax)
Kevin O'Connorbca07362010-03-20 20:41:38 -040077 : "r" (func)
Kevin O'Connoraf9629b2010-11-25 09:17:31 -050078 : "ecx", "edx", "cc", "memory");
Kevin O'Connora7fc5de2009-12-13 11:48:18 -050079
80 // Restore gdt and fs/gs
81 lgdt(&gdt);
82 SET_SEG(FS, fs);
83 SET_SEG(GS, gs);
84
85 // Restore cmos index register
86 outb(cmosindex, PORT_CMOS_INDEX);
87 inb(PORT_CMOS_DATA);
Kevin O'Connoraf9629b2010-11-25 09:17:31 -050088 return eax;
Kevin O'Connora7fc5de2009-12-13 11:48:18 -050089}
90
Kevin O'Connor9c447c32010-05-23 10:24:22 -040091// 16bit trampoline for enabling irqs from 32bit mode.
92ASM16(
93 " .global trampoline_checkirqs\n"
94 "trampoline_checkirqs:\n"
95 " rep ; nop\n"
96 " lretw"
97 );
98
99static void
100check_irqs(void)
101{
102 if (MODESEGMENT) {
103 asm volatile(
104 "sti\n"
105 "nop\n"
106 "rep ; nop\n"
107 "cli\n"
108 "cld\n"
109 : : :"memory");
110 return;
111 }
112 extern void trampoline_checkirqs();
113 struct bregs br;
114 br.flags = F_IF;
115 br.code.seg = SEG_BIOS;
116 br.code.offset = (u32)&trampoline_checkirqs;
117 call16big(&br);
118}
119
120// 16bit trampoline for waiting for an irq from 32bit mode.
121ASM16(
122 " .global trampoline_waitirq\n"
123 "trampoline_waitirq:\n"
124 " sti\n"
125 " hlt\n"
126 " lretw"
127 );
128
129// Wait for next irq to occur.
130void
131wait_irq(void)
132{
133 if (MODESEGMENT) {
134 asm volatile("sti ; hlt ; cli ; cld": : :"memory");
135 return;
136 }
137 if (CONFIG_THREADS && MainThread.next != &MainThread) {
138 // Threads still active - do a yield instead.
139 yield();
140 return;
141 }
142 extern void trampoline_waitirq();
143 struct bregs br;
144 br.flags = 0;
145 br.code.seg = SEG_BIOS;
146 br.code.offset = (u32)&trampoline_waitirq;
147 call16big(&br);
148}
149
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500150
151/****************************************************************
152 * Stack in EBDA
153 ****************************************************************/
154
155// Switch to the extra stack in ebda and call a function.
156inline u32
Kevin O'Connorbca07362010-03-20 20:41:38 -0400157stack_hop(u32 eax, u32 edx, void *func)
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500158{
159 ASSERT16();
160 u16 ebda_seg = get_ebda_seg(), bkup_ss;
161 u32 bkup_esp;
162 asm volatile(
163 // Backup current %ss/%esp values.
164 "movw %%ss, %w3\n"
165 "movl %%esp, %4\n"
166 // Copy ebda seg to %ds/%ss and set %esp
167 "movw %w6, %%ds\n"
168 "movw %w6, %%ss\n"
169 "movl %5, %%esp\n"
170 // Call func
Kevin O'Connorbca07362010-03-20 20:41:38 -0400171 "calll *%2\n"
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500172 // Restore segments and stack
173 "movw %w3, %%ds\n"
174 "movw %w3, %%ss\n"
175 "movl %4, %%esp"
Kevin O'Connorbca07362010-03-20 20:41:38 -0400176 : "+a" (eax), "+d" (edx), "+c" (func), "=&r" (bkup_ss), "=&r" (bkup_esp)
177 : "i" (EBDA_OFFSET_TOP_STACK), "r" (ebda_seg)
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500178 : "cc", "memory");
179 return eax;
180}
181
182
183/****************************************************************
184 * Threads
185 ****************************************************************/
186
187#define THREADSTACKSIZE 4096
Kevin O'Connorad901592009-12-13 11:25:25 -0500188int VAR16VISIBLE CanPreempt;
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500189
Kevin O'Connorad901592009-12-13 11:25:25 -0500190// Return the 'struct thread_info' for the currently running thread.
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500191struct thread_info *
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500192getCurThread(void)
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500193{
194 u32 esp = getesp();
195 if (esp <= BUILD_STACK_ADDR)
196 return &MainThread;
197 return (void*)ALIGN_DOWN(esp, THREADSTACKSIZE);
198}
199
Kevin O'Connorad901592009-12-13 11:25:25 -0500200// Switch to next thread stack.
201static void
202switch_next(struct thread_info *cur)
203{
204 struct thread_info *next = cur->next;
Kevin O'Connorfb214dc2009-12-20 13:11:17 -0500205 if (cur == next)
206 // Nothing to do.
207 return;
Kevin O'Connorad901592009-12-13 11:25:25 -0500208 asm volatile(
209 " pushl $1f\n" // store return pc
210 " pushl %%ebp\n" // backup %ebp
211 " movl %%esp, 4(%%eax)\n" // cur->stackpos = %esp
212 " movl 4(%%ecx), %%esp\n" // %esp = next->stackpos
213 " popl %%ebp\n" // restore %ebp
214 " retl\n" // restore pc
215 "1:\n"
216 : "+a"(cur), "+c"(next)
217 :
218 : "ebx", "edx", "esi", "edi", "cc", "memory");
219}
220
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500221// Briefly permit irqs to occur.
222void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500223yield(void)
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500224{
Kevin O'Connor52a300f2009-12-26 23:32:57 -0500225 if (MODESEGMENT || !CONFIG_THREADS) {
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500226 // Just directly check irqs.
227 check_irqs();
228 return;
229 }
230 struct thread_info *cur = getCurThread();
231 if (cur == &MainThread)
232 // Permit irqs to fire
233 check_irqs();
234
235 // Switch to the next thread
Kevin O'Connorad901592009-12-13 11:25:25 -0500236 switch_next(cur);
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500237}
238
239// Last thing called from a thread (called on "next" stack).
240static void
241__end_thread(struct thread_info *old)
242{
Kevin O'Connora7eb8fc2010-04-02 13:13:23 -0400243 old->next->pprev = old->pprev;
244 *old->pprev = old->next;
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500245 free(old);
246 dprintf(DEBUG_thread, "\\%08x/ End thread\n", (u32)old);
Kevin O'Connore438b0c2010-05-01 12:20:33 -0400247 if (MainThread.next == &MainThread)
248 dprintf(1, "All threads complete.\n");
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500249}
250
Kevin O'Connorad901592009-12-13 11:25:25 -0500251// Create a new thread and start executing 'func' in it.
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500252void
253run_thread(void (*func)(void*), void *data)
254{
Kevin O'Connor52a300f2009-12-26 23:32:57 -0500255 ASSERT32FLAT();
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500256 if (! CONFIG_THREADS)
257 goto fail;
258 struct thread_info *thread;
259 thread = memalign_tmphigh(THREADSTACKSIZE, THREADSTACKSIZE);
260 if (!thread)
261 goto fail;
262
263 thread->stackpos = (void*)thread + THREADSTACKSIZE;
264 struct thread_info *cur = getCurThread();
Kevin O'Connora7eb8fc2010-04-02 13:13:23 -0400265 thread->next = cur;
266 thread->pprev = cur->pprev;
267 cur->pprev = &thread->next;
268 *thread->pprev = thread;
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500269
270 dprintf(DEBUG_thread, "/%08x\\ Start thread\n", (u32)thread);
271 asm volatile(
272 // Start thread
273 " pushl $1f\n" // store return pc
274 " pushl %%ebp\n" // backup %ebp
275 " movl %%esp, 4(%%edx)\n" // cur->stackpos = %esp
276 " movl 4(%%ebx), %%esp\n" // %esp = thread->stackpos
277 " calll *%%ecx\n" // Call func
278
279 // End thread
280 " movl (%%ebx), %%ecx\n" // %ecx = thread->next
281 " movl 4(%%ecx), %%esp\n" // %esp = next->stackpos
282 " movl %%ebx, %%eax\n"
283 " calll %4\n" // call __end_thread(thread)
284 " popl %%ebp\n" // restore %ebp
285 " retl\n" // restore pc
286 "1:\n"
287 : "+a"(data), "+c"(func), "+b"(thread), "+d"(cur)
288 : "m"(*(u8*)__end_thread)
289 : "esi", "edi", "cc", "memory");
290 return;
291
292fail:
293 func(data);
294}
295
Kevin O'Connorad901592009-12-13 11:25:25 -0500296// Wait for all threads (other than the main thread) to complete.
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500297void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500298wait_threads(void)
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500299{
Kevin O'Connor52a300f2009-12-26 23:32:57 -0500300 ASSERT32FLAT();
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500301 if (! CONFIG_THREADS)
302 return;
303 while (MainThread.next != &MainThread)
304 yield();
305}
Kevin O'Connorad901592009-12-13 11:25:25 -0500306
Kevin O'Connore9086652010-02-27 13:49:47 -0500307void
308mutex_lock(struct mutex_s *mutex)
309{
310 ASSERT32FLAT();
311 if (! CONFIG_THREADS)
312 return;
313 while (mutex->isLocked)
314 yield();
315 mutex->isLocked = 1;
316}
317
318void
319mutex_unlock(struct mutex_s *mutex)
320{
321 ASSERT32FLAT();
322 if (! CONFIG_THREADS)
323 return;
324 mutex->isLocked = 0;
325}
326
Kevin O'Connorad901592009-12-13 11:25:25 -0500327
328/****************************************************************
329 * Thread preemption
330 ****************************************************************/
331
332static u32 PreemptCount;
333
334// Turn on RTC irqs and arrange for them to check the 32bit threads.
335void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500336start_preempt(void)
Kevin O'Connorad901592009-12-13 11:25:25 -0500337{
338 if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS)
339 return;
340 CanPreempt = 1;
341 PreemptCount = 0;
342 useRTC();
343}
344
345// Turn off RTC irqs / stop checking for thread execution.
346void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500347finish_preempt(void)
Kevin O'Connorad901592009-12-13 11:25:25 -0500348{
Kevin O'Connora7eb8fc2010-04-02 13:13:23 -0400349 if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS) {
350 yield();
Kevin O'Connorad901592009-12-13 11:25:25 -0500351 return;
Kevin O'Connora7eb8fc2010-04-02 13:13:23 -0400352 }
Kevin O'Connorad901592009-12-13 11:25:25 -0500353 CanPreempt = 0;
354 releaseRTC();
Kevin O'Connore438b0c2010-05-01 12:20:33 -0400355 dprintf(9, "Done preempt - %d checks\n", PreemptCount);
Kevin O'Connora7eb8fc2010-04-02 13:13:23 -0400356 yield();
Kevin O'Connorad901592009-12-13 11:25:25 -0500357}
358
Kevin O'Connord7eb27e2010-03-20 18:17:19 -0400359// Check if preemption is on, and wait for it to complete if so.
360int
361wait_preempt(void)
362{
363 if (MODESEGMENT || !CONFIG_THREADS || !CONFIG_THREAD_OPTIONROMS
364 || !CanPreempt)
365 return 0;
366 while (CanPreempt)
367 yield();
368 return 1;
369}
370
Kevin O'Connorad901592009-12-13 11:25:25 -0500371// Try to execute 32bit threads.
Kevin O'Connord1b4f962010-09-15 21:38:16 -0400372void VISIBLE32INIT
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500373yield_preempt(void)
Kevin O'Connorad901592009-12-13 11:25:25 -0500374{
375 PreemptCount++;
376 switch_next(&MainThread);
377}
Kevin O'Connorad901592009-12-13 11:25:25 -0500378
379// 16bit code that checks if threads are pending and executes them if so.
380void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500381check_preempt(void)
Kevin O'Connorad901592009-12-13 11:25:25 -0500382{
Kevin O'Connorad901592009-12-13 11:25:25 -0500383 if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS
384 || !GET_GLOBAL(CanPreempt)
Kevin O'Connor4d96edc2010-09-25 14:53:15 -0400385 || GET_FLATPTR(MainThread.next) == &MainThread)
Kevin O'Connorad901592009-12-13 11:25:25 -0500386 return;
Kevin O'Connorad901592009-12-13 11:25:25 -0500387
Kevin O'Connorf3fe3aa2010-12-05 12:38:33 -0500388 extern void _cfunc32flat_yield_preempt(void);
389 call32(_cfunc32flat_yield_preempt, 0, 0);
Kevin O'Connorad901592009-12-13 11:25:25 -0500390}