blob: 0371330adc8443ec2c80d0df233051c9b339299a [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
Kevin O'Connor46b82622012-05-13 12:10:30 -04007#include "biosvar.h" // GET_GLOBAL
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -05008#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/****************************************************************
Kevin O'Connor46b82622012-05-13 12:10:30 -0400152 * Extra 16bit stack
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500153 ****************************************************************/
154
Kevin O'Connor46b82622012-05-13 12:10:30 -0400155// Space for a stack for 16bit code.
156u8 ExtraStack[BUILD_EXTRA_STACK_SIZE+1] VARLOW __aligned(8);
157
158// Switch to the extra stack and call a function.
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500159inline 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();
Kevin O'Connor46b82622012-05-13 12:10:30 -0400163 u16 stack_seg = SEG_LOW, bkup_ss;
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500164 u32 bkup_esp;
165 asm volatile(
166 // Backup current %ss/%esp values.
167 "movw %%ss, %w3\n"
168 "movl %%esp, %4\n"
Kevin O'Connor46b82622012-05-13 12:10:30 -0400169 // Copy stack seg to %ds/%ss and set %esp
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500170 "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)
Kevin O'Connor46b82622012-05-13 12:10:30 -0400180 : "i" (&ExtraStack[BUILD_EXTRA_STACK_SIZE]), "r" (stack_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
Kevin O'Connorad901592009-12-13 11:25:25 -0500193// Return the 'struct thread_info' for the currently running thread.
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500194struct thread_info *
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500195getCurThread(void)
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500196{
197 u32 esp = getesp();
198 if (esp <= BUILD_STACK_ADDR)
199 return &MainThread;
200 return (void*)ALIGN_DOWN(esp, THREADSTACKSIZE);
201}
202
Kevin O'Connorad901592009-12-13 11:25:25 -0500203// Switch to next thread stack.
204static void
205switch_next(struct thread_info *cur)
206{
207 struct thread_info *next = cur->next;
Kevin O'Connorfb214dc2009-12-20 13:11:17 -0500208 if (cur == next)
209 // Nothing to do.
210 return;
Kevin O'Connorad901592009-12-13 11:25:25 -0500211 asm volatile(
212 " pushl $1f\n" // store return pc
213 " pushl %%ebp\n" // backup %ebp
214 " movl %%esp, 4(%%eax)\n" // cur->stackpos = %esp
215 " movl 4(%%ecx), %%esp\n" // %esp = next->stackpos
216 " popl %%ebp\n" // restore %ebp
217 " retl\n" // restore pc
218 "1:\n"
219 : "+a"(cur), "+c"(next)
220 :
221 : "ebx", "edx", "esi", "edi", "cc", "memory");
222}
223
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500224// Briefly permit irqs to occur.
225void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500226yield(void)
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500227{
Kevin O'Connor52a300f2009-12-26 23:32:57 -0500228 if (MODESEGMENT || !CONFIG_THREADS) {
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500229 // Just directly check irqs.
230 check_irqs();
231 return;
232 }
233 struct thread_info *cur = getCurThread();
234 if (cur == &MainThread)
235 // Permit irqs to fire
236 check_irqs();
237
238 // Switch to the next thread
Kevin O'Connorad901592009-12-13 11:25:25 -0500239 switch_next(cur);
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500240}
241
242// Last thing called from a thread (called on "next" stack).
243static void
244__end_thread(struct thread_info *old)
245{
Kevin O'Connora7eb8fc2010-04-02 13:13:23 -0400246 old->next->pprev = old->pprev;
247 *old->pprev = old->next;
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500248 free(old);
249 dprintf(DEBUG_thread, "\\%08x/ End thread\n", (u32)old);
Kevin O'Connore438b0c2010-05-01 12:20:33 -0400250 if (MainThread.next == &MainThread)
251 dprintf(1, "All threads complete.\n");
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500252}
253
Kevin O'Connorad901592009-12-13 11:25:25 -0500254// Create a new thread and start executing 'func' in it.
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500255void
256run_thread(void (*func)(void*), void *data)
257{
Kevin O'Connor52a300f2009-12-26 23:32:57 -0500258 ASSERT32FLAT();
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500259 if (! CONFIG_THREADS)
260 goto fail;
261 struct thread_info *thread;
262 thread = memalign_tmphigh(THREADSTACKSIZE, THREADSTACKSIZE);
263 if (!thread)
264 goto fail;
265
266 thread->stackpos = (void*)thread + THREADSTACKSIZE;
267 struct thread_info *cur = getCurThread();
Kevin O'Connora7eb8fc2010-04-02 13:13:23 -0400268 thread->next = cur;
269 thread->pprev = cur->pprev;
270 cur->pprev = &thread->next;
271 *thread->pprev = thread;
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500272
273 dprintf(DEBUG_thread, "/%08x\\ Start thread\n", (u32)thread);
274 asm volatile(
275 // Start thread
276 " pushl $1f\n" // store return pc
277 " pushl %%ebp\n" // backup %ebp
278 " movl %%esp, 4(%%edx)\n" // cur->stackpos = %esp
279 " movl 4(%%ebx), %%esp\n" // %esp = thread->stackpos
280 " calll *%%ecx\n" // Call func
281
282 // End thread
283 " movl (%%ebx), %%ecx\n" // %ecx = thread->next
284 " movl 4(%%ecx), %%esp\n" // %esp = next->stackpos
285 " movl %%ebx, %%eax\n"
286 " calll %4\n" // call __end_thread(thread)
287 " popl %%ebp\n" // restore %ebp
288 " retl\n" // restore pc
289 "1:\n"
290 : "+a"(data), "+c"(func), "+b"(thread), "+d"(cur)
291 : "m"(*(u8*)__end_thread)
292 : "esi", "edi", "cc", "memory");
293 return;
294
295fail:
296 func(data);
297}
298
Kevin O'Connorad901592009-12-13 11:25:25 -0500299// Wait for all threads (other than the main thread) to complete.
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500300void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500301wait_threads(void)
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500302{
Kevin O'Connor52a300f2009-12-26 23:32:57 -0500303 ASSERT32FLAT();
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500304 if (! CONFIG_THREADS)
305 return;
306 while (MainThread.next != &MainThread)
307 yield();
308}
Kevin O'Connorad901592009-12-13 11:25:25 -0500309
Kevin O'Connore9086652010-02-27 13:49:47 -0500310void
311mutex_lock(struct mutex_s *mutex)
312{
313 ASSERT32FLAT();
314 if (! CONFIG_THREADS)
315 return;
316 while (mutex->isLocked)
317 yield();
318 mutex->isLocked = 1;
319}
320
321void
322mutex_unlock(struct mutex_s *mutex)
323{
324 ASSERT32FLAT();
325 if (! CONFIG_THREADS)
326 return;
327 mutex->isLocked = 0;
328}
329
Kevin O'Connorad901592009-12-13 11:25:25 -0500330
331/****************************************************************
332 * Thread preemption
333 ****************************************************************/
334
335static u32 PreemptCount;
336
337// Turn on RTC irqs and arrange for them to check the 32bit threads.
338void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500339start_preempt(void)
Kevin O'Connorad901592009-12-13 11:25:25 -0500340{
341 if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS)
342 return;
343 CanPreempt = 1;
344 PreemptCount = 0;
345 useRTC();
346}
347
348// Turn off RTC irqs / stop checking for thread execution.
349void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500350finish_preempt(void)
Kevin O'Connorad901592009-12-13 11:25:25 -0500351{
Kevin O'Connora7eb8fc2010-04-02 13:13:23 -0400352 if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS) {
353 yield();
Kevin O'Connorad901592009-12-13 11:25:25 -0500354 return;
Kevin O'Connora7eb8fc2010-04-02 13:13:23 -0400355 }
Kevin O'Connorad901592009-12-13 11:25:25 -0500356 CanPreempt = 0;
357 releaseRTC();
Kevin O'Connore438b0c2010-05-01 12:20:33 -0400358 dprintf(9, "Done preempt - %d checks\n", PreemptCount);
Kevin O'Connora7eb8fc2010-04-02 13:13:23 -0400359 yield();
Kevin O'Connorad901592009-12-13 11:25:25 -0500360}
361
Kevin O'Connord7eb27e2010-03-20 18:17:19 -0400362// Check if preemption is on, and wait for it to complete if so.
363int
364wait_preempt(void)
365{
366 if (MODESEGMENT || !CONFIG_THREADS || !CONFIG_THREAD_OPTIONROMS
367 || !CanPreempt)
368 return 0;
369 while (CanPreempt)
370 yield();
371 return 1;
372}
373
Kevin O'Connorad901592009-12-13 11:25:25 -0500374// Try to execute 32bit threads.
Kevin O'Connord1b4f962010-09-15 21:38:16 -0400375void VISIBLE32INIT
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500376yield_preempt(void)
Kevin O'Connorad901592009-12-13 11:25:25 -0500377{
378 PreemptCount++;
379 switch_next(&MainThread);
380}
Kevin O'Connorad901592009-12-13 11:25:25 -0500381
382// 16bit code that checks if threads are pending and executes them if so.
383void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500384check_preempt(void)
Kevin O'Connorad901592009-12-13 11:25:25 -0500385{
Kevin O'Connorad901592009-12-13 11:25:25 -0500386 if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS
387 || !GET_GLOBAL(CanPreempt)
Kevin O'Connor4d96edc2010-09-25 14:53:15 -0400388 || GET_FLATPTR(MainThread.next) == &MainThread)
Kevin O'Connorad901592009-12-13 11:25:25 -0500389 return;
Kevin O'Connorad901592009-12-13 11:25:25 -0500390
Kevin O'Connorf3fe3aa2010-12-05 12:38:33 -0500391 extern void _cfunc32flat_yield_preempt(void);
392 call32(_cfunc32flat_yield_preempt, 0, 0);
Kevin O'Connorad901592009-12-13 11:25:25 -0500393}