blob: 53bf66985880169140636e502b7dc6dc0f38f1c9 [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'Connor1ca05b02010-01-03 17:43:37 -050027static inline u32 getcr0(void) {
Kevin O'Connora7fc5de2009-12-13 11:48:18 -050028 u32 cr0;
29 asm("movl %%cr0, %0" : "=r"(cr0));
30 return cr0;
31}
32static inline void sgdt(struct descloc_s *desc) {
33 asm("sgdtl %0" : "=m"(*desc));
34}
35static inline void lgdt(struct descloc_s *desc) {
36 asm("lgdtl %0" : : "m"(*desc) : "memory");
37}
38
39// Call a 32bit SeaBIOS function from a 16bit SeaBIOS function.
Kevin O'Connoraf9629b2010-11-25 09:17:31 -050040u32
41call32(void *func, u32 eax, u32 errret)
Kevin O'Connora7fc5de2009-12-13 11:48:18 -050042{
43 ASSERT16();
44 u32 cr0 = getcr0();
45 if (cr0 & CR0_PE)
46 // Called in 16bit protected mode?!
Kevin O'Connoraf9629b2010-11-25 09:17:31 -050047 return errret;
Kevin O'Connora7fc5de2009-12-13 11:48:18 -050048
49 // Backup cmos index register and disable nmi
50 u8 cmosindex = inb(PORT_CMOS_INDEX);
51 outb(cmosindex | NMI_DISABLE_BIT, PORT_CMOS_INDEX);
52 inb(PORT_CMOS_DATA);
53
54 // Backup fs/gs and gdt
55 u16 fs = GET_SEG(FS), gs = GET_SEG(GS);
56 struct descloc_s gdt;
57 sgdt(&gdt);
58
Kevin O'Connora7fc5de2009-12-13 11:48:18 -050059 u32 bkup_ss, bkup_esp;
60 asm volatile(
61 // Backup ss/esp / set esp to flat stack location
62 " movl %%ss, %0\n"
63 " movl %%esp, %1\n"
64 " shll $4, %0\n"
65 " addl %0, %%esp\n"
Kevin O'Connoraf9629b2010-11-25 09:17:31 -050066 " shrl $4, %0\n"
Kevin O'Connora7fc5de2009-12-13 11:48:18 -050067
Kevin O'Connorfb214dc2009-12-20 13:11:17 -050068 // Transition to 32bit mode, call func, return to 16bit
Kevin O'Connor4057f982010-11-25 08:52:50 -050069 " movl $(" __stringify(BUILD_BIOS_ADDR) " + 1f), %%edx\n"
Kevin O'Connora7fc5de2009-12-13 11:48:18 -050070 " jmp transition32\n"
71 " .code32\n"
Kevin O'Connoraf9629b2010-11-25 09:17:31 -050072 "1:calll *%3\n"
Kevin O'Connor4057f982010-11-25 08:52:50 -050073 " movl $2f, %%edx\n"
Kevin O'Connora7fc5de2009-12-13 11:48:18 -050074 " jmp transition16big\n"
75
76 // Restore ds/ss/esp
77 " .code16gcc\n"
78 "2:movl %0, %%ds\n"
79 " movl %0, %%ss\n"
80 " movl %1, %%esp\n"
Kevin O'Connoraf9629b2010-11-25 09:17:31 -050081 : "=&r" (bkup_ss), "=&r" (bkup_esp), "+a" (eax)
Kevin O'Connorbca07362010-03-20 20:41:38 -040082 : "r" (func)
Kevin O'Connoraf9629b2010-11-25 09:17:31 -050083 : "ecx", "edx", "cc", "memory");
Kevin O'Connora7fc5de2009-12-13 11:48:18 -050084
85 // Restore gdt and fs/gs
86 lgdt(&gdt);
87 SET_SEG(FS, fs);
88 SET_SEG(GS, gs);
89
90 // Restore cmos index register
91 outb(cmosindex, PORT_CMOS_INDEX);
92 inb(PORT_CMOS_DATA);
Kevin O'Connoraf9629b2010-11-25 09:17:31 -050093 return eax;
Kevin O'Connora7fc5de2009-12-13 11:48:18 -050094}
95
Kevin O'Connor9c447c32010-05-23 10:24:22 -040096// 16bit trampoline for enabling irqs from 32bit mode.
97ASM16(
98 " .global trampoline_checkirqs\n"
99 "trampoline_checkirqs:\n"
100 " rep ; nop\n"
101 " lretw"
102 );
103
104static void
105check_irqs(void)
106{
107 if (MODESEGMENT) {
108 asm volatile(
109 "sti\n"
110 "nop\n"
111 "rep ; nop\n"
112 "cli\n"
113 "cld\n"
114 : : :"memory");
115 return;
116 }
117 extern void trampoline_checkirqs();
118 struct bregs br;
119 br.flags = F_IF;
120 br.code.seg = SEG_BIOS;
121 br.code.offset = (u32)&trampoline_checkirqs;
122 call16big(&br);
123}
124
125// 16bit trampoline for waiting for an irq from 32bit mode.
126ASM16(
127 " .global trampoline_waitirq\n"
128 "trampoline_waitirq:\n"
129 " sti\n"
130 " hlt\n"
131 " lretw"
132 );
133
134// Wait for next irq to occur.
135void
136wait_irq(void)
137{
138 if (MODESEGMENT) {
139 asm volatile("sti ; hlt ; cli ; cld": : :"memory");
140 return;
141 }
142 if (CONFIG_THREADS && MainThread.next != &MainThread) {
143 // Threads still active - do a yield instead.
144 yield();
145 return;
146 }
147 extern void trampoline_waitirq();
148 struct bregs br;
149 br.flags = 0;
150 br.code.seg = SEG_BIOS;
151 br.code.offset = (u32)&trampoline_waitirq;
152 call16big(&br);
153}
154
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500155
156/****************************************************************
157 * Stack in EBDA
158 ****************************************************************/
159
160// Switch to the extra stack in ebda and call a function.
161inline u32
Kevin O'Connorbca07362010-03-20 20:41:38 -0400162stack_hop(u32 eax, u32 edx, void *func)
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500163{
164 ASSERT16();
165 u16 ebda_seg = get_ebda_seg(), bkup_ss;
166 u32 bkup_esp;
167 asm volatile(
168 // Backup current %ss/%esp values.
169 "movw %%ss, %w3\n"
170 "movl %%esp, %4\n"
171 // Copy ebda seg to %ds/%ss and set %esp
172 "movw %w6, %%ds\n"
173 "movw %w6, %%ss\n"
174 "movl %5, %%esp\n"
175 // Call func
Kevin O'Connorbca07362010-03-20 20:41:38 -0400176 "calll *%2\n"
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500177 // Restore segments and stack
178 "movw %w3, %%ds\n"
179 "movw %w3, %%ss\n"
180 "movl %4, %%esp"
Kevin O'Connorbca07362010-03-20 20:41:38 -0400181 : "+a" (eax), "+d" (edx), "+c" (func), "=&r" (bkup_ss), "=&r" (bkup_esp)
182 : "i" (EBDA_OFFSET_TOP_STACK), "r" (ebda_seg)
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500183 : "cc", "memory");
184 return eax;
185}
186
187
188/****************************************************************
189 * Threads
190 ****************************************************************/
191
192#define THREADSTACKSIZE 4096
Kevin O'Connorad901592009-12-13 11:25:25 -0500193int VAR16VISIBLE CanPreempt;
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500194
Kevin O'Connorad901592009-12-13 11:25:25 -0500195// Return the 'struct thread_info' for the currently running thread.
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500196struct thread_info *
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500197getCurThread(void)
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500198{
199 u32 esp = getesp();
200 if (esp <= BUILD_STACK_ADDR)
201 return &MainThread;
202 return (void*)ALIGN_DOWN(esp, THREADSTACKSIZE);
203}
204
Kevin O'Connorad901592009-12-13 11:25:25 -0500205// Switch to next thread stack.
206static void
207switch_next(struct thread_info *cur)
208{
209 struct thread_info *next = cur->next;
Kevin O'Connorfb214dc2009-12-20 13:11:17 -0500210 if (cur == next)
211 // Nothing to do.
212 return;
Kevin O'Connorad901592009-12-13 11:25:25 -0500213 asm volatile(
214 " pushl $1f\n" // store return pc
215 " pushl %%ebp\n" // backup %ebp
216 " movl %%esp, 4(%%eax)\n" // cur->stackpos = %esp
217 " movl 4(%%ecx), %%esp\n" // %esp = next->stackpos
218 " popl %%ebp\n" // restore %ebp
219 " retl\n" // restore pc
220 "1:\n"
221 : "+a"(cur), "+c"(next)
222 :
223 : "ebx", "edx", "esi", "edi", "cc", "memory");
224}
225
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500226// Briefly permit irqs to occur.
227void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500228yield(void)
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500229{
Kevin O'Connor52a300f2009-12-26 23:32:57 -0500230 if (MODESEGMENT || !CONFIG_THREADS) {
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500231 // Just directly check irqs.
232 check_irqs();
233 return;
234 }
235 struct thread_info *cur = getCurThread();
236 if (cur == &MainThread)
237 // Permit irqs to fire
238 check_irqs();
239
240 // Switch to the next thread
Kevin O'Connorad901592009-12-13 11:25:25 -0500241 switch_next(cur);
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500242}
243
244// Last thing called from a thread (called on "next" stack).
245static void
246__end_thread(struct thread_info *old)
247{
Kevin O'Connora7eb8fc2010-04-02 13:13:23 -0400248 old->next->pprev = old->pprev;
249 *old->pprev = old->next;
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500250 free(old);
251 dprintf(DEBUG_thread, "\\%08x/ End thread\n", (u32)old);
Kevin O'Connore438b0c2010-05-01 12:20:33 -0400252 if (MainThread.next == &MainThread)
253 dprintf(1, "All threads complete.\n");
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500254}
255
Kevin O'Connorad901592009-12-13 11:25:25 -0500256// Create a new thread and start executing 'func' in it.
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500257void
258run_thread(void (*func)(void*), void *data)
259{
Kevin O'Connor52a300f2009-12-26 23:32:57 -0500260 ASSERT32FLAT();
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500261 if (! CONFIG_THREADS)
262 goto fail;
263 struct thread_info *thread;
264 thread = memalign_tmphigh(THREADSTACKSIZE, THREADSTACKSIZE);
265 if (!thread)
266 goto fail;
267
268 thread->stackpos = (void*)thread + THREADSTACKSIZE;
269 struct thread_info *cur = getCurThread();
Kevin O'Connora7eb8fc2010-04-02 13:13:23 -0400270 thread->next = cur;
271 thread->pprev = cur->pprev;
272 cur->pprev = &thread->next;
273 *thread->pprev = thread;
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500274
275 dprintf(DEBUG_thread, "/%08x\\ Start thread\n", (u32)thread);
276 asm volatile(
277 // Start thread
278 " pushl $1f\n" // store return pc
279 " pushl %%ebp\n" // backup %ebp
280 " movl %%esp, 4(%%edx)\n" // cur->stackpos = %esp
281 " movl 4(%%ebx), %%esp\n" // %esp = thread->stackpos
282 " calll *%%ecx\n" // Call func
283
284 // End thread
285 " movl (%%ebx), %%ecx\n" // %ecx = thread->next
286 " movl 4(%%ecx), %%esp\n" // %esp = next->stackpos
287 " movl %%ebx, %%eax\n"
288 " calll %4\n" // call __end_thread(thread)
289 " popl %%ebp\n" // restore %ebp
290 " retl\n" // restore pc
291 "1:\n"
292 : "+a"(data), "+c"(func), "+b"(thread), "+d"(cur)
293 : "m"(*(u8*)__end_thread)
294 : "esi", "edi", "cc", "memory");
295 return;
296
297fail:
298 func(data);
299}
300
Kevin O'Connorad901592009-12-13 11:25:25 -0500301// Wait for all threads (other than the main thread) to complete.
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500302void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500303wait_threads(void)
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500304{
Kevin O'Connor52a300f2009-12-26 23:32:57 -0500305 ASSERT32FLAT();
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500306 if (! CONFIG_THREADS)
307 return;
308 while (MainThread.next != &MainThread)
309 yield();
310}
Kevin O'Connorad901592009-12-13 11:25:25 -0500311
Kevin O'Connore9086652010-02-27 13:49:47 -0500312void
313mutex_lock(struct mutex_s *mutex)
314{
315 ASSERT32FLAT();
316 if (! CONFIG_THREADS)
317 return;
318 while (mutex->isLocked)
319 yield();
320 mutex->isLocked = 1;
321}
322
323void
324mutex_unlock(struct mutex_s *mutex)
325{
326 ASSERT32FLAT();
327 if (! CONFIG_THREADS)
328 return;
329 mutex->isLocked = 0;
330}
331
Kevin O'Connorad901592009-12-13 11:25:25 -0500332
333/****************************************************************
334 * Thread preemption
335 ****************************************************************/
336
337static u32 PreemptCount;
338
339// Turn on RTC irqs and arrange for them to check the 32bit threads.
340void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500341start_preempt(void)
Kevin O'Connorad901592009-12-13 11:25:25 -0500342{
343 if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS)
344 return;
345 CanPreempt = 1;
346 PreemptCount = 0;
347 useRTC();
348}
349
350// Turn off RTC irqs / stop checking for thread execution.
351void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500352finish_preempt(void)
Kevin O'Connorad901592009-12-13 11:25:25 -0500353{
Kevin O'Connora7eb8fc2010-04-02 13:13:23 -0400354 if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS) {
355 yield();
Kevin O'Connorad901592009-12-13 11:25:25 -0500356 return;
Kevin O'Connora7eb8fc2010-04-02 13:13:23 -0400357 }
Kevin O'Connorad901592009-12-13 11:25:25 -0500358 CanPreempt = 0;
359 releaseRTC();
Kevin O'Connore438b0c2010-05-01 12:20:33 -0400360 dprintf(9, "Done preempt - %d checks\n", PreemptCount);
Kevin O'Connora7eb8fc2010-04-02 13:13:23 -0400361 yield();
Kevin O'Connorad901592009-12-13 11:25:25 -0500362}
363
Kevin O'Connord7eb27e2010-03-20 18:17:19 -0400364// Check if preemption is on, and wait for it to complete if so.
365int
366wait_preempt(void)
367{
368 if (MODESEGMENT || !CONFIG_THREADS || !CONFIG_THREAD_OPTIONROMS
369 || !CanPreempt)
370 return 0;
371 while (CanPreempt)
372 yield();
373 return 1;
374}
375
Kevin O'Connorad901592009-12-13 11:25:25 -0500376// Try to execute 32bit threads.
Kevin O'Connord1b4f962010-09-15 21:38:16 -0400377void VISIBLE32INIT
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500378yield_preempt(void)
Kevin O'Connorad901592009-12-13 11:25:25 -0500379{
380 PreemptCount++;
381 switch_next(&MainThread);
382}
Kevin O'Connorad901592009-12-13 11:25:25 -0500383
384// 16bit code that checks if threads are pending and executes them if so.
385void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500386check_preempt(void)
Kevin O'Connorad901592009-12-13 11:25:25 -0500387{
Kevin O'Connorad901592009-12-13 11:25:25 -0500388 if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS
389 || !GET_GLOBAL(CanPreempt)
Kevin O'Connor4d96edc2010-09-25 14:53:15 -0400390 || GET_FLATPTR(MainThread.next) == &MainThread)
Kevin O'Connorad901592009-12-13 11:25:25 -0500391 return;
Kevin O'Connorad901592009-12-13 11:25:25 -0500392
Kevin O'Connorf3fe3aa2010-12-05 12:38:33 -0500393 extern void _cfunc32flat_yield_preempt(void);
394 call32(_cfunc32flat_yield_preempt, 0, 0);
Kevin O'Connorad901592009-12-13 11:25:25 -0500395}