blob: 78ad65c5ec287bf626bac313d088593182950540 [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'Connorad901592009-12-13 11:25:25 -05008#include "bregs.h" // CR0_PE
Kevin O'Connor8b7861c2013-09-15 02:29:06 -04009#include "hw/rtc.h" // rtc_use
Kevin O'Connor0039a942013-06-08 21:50:15 -040010#include "list.h" // hlist_node
Kevin O'Connor9dea5902013-09-14 20:23:54 -040011#include "malloc.h" // free
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040012#include "output.h" // dprintf
Kevin O'Connor3df600b2013-09-14 19:28:55 -040013#include "stacks.h" // struct mutex_s
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040014#include "util.h" // useRTC
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -050015
Kevin O'Connor7acaa3c2013-10-02 20:20:03 -040016#define MAIN_STACK_MAX (1024*1024)
17
Kevin O'Connore77c7052012-05-28 22:06:42 -040018
19/****************************************************************
20 * Extra 16bit stack
21 ****************************************************************/
22
23// Space for a stack for 16bit code.
24u8 ExtraStack[BUILD_EXTRA_STACK_SIZE+1] VARLOW __aligned(8);
Kevin O'Connorbf2e8c22012-05-28 12:59:58 -040025u8 *StackPos VARLOW;
26
27// Test if currently on the extra stack
28static inline int
29on_extra_stack(void)
30{
31 return MODE16 && GET_SEG(SS) == SEG_LOW && getesp() > (u32)ExtraStack;
32}
Kevin O'Connore77c7052012-05-28 22:06:42 -040033
34// Switch to the extra stack and call a function.
Kevin O'Connorecdc6552012-05-28 14:25:15 -040035u32
Kevin O'Connore77c7052012-05-28 22:06:42 -040036stack_hop(u32 eax, u32 edx, void *func)
37{
Kevin O'Connorbf2e8c22012-05-28 12:59:58 -040038 if (on_extra_stack())
39 return ((u32 (*)(u32, u32))func)(eax, edx);
Kevin O'Connore77c7052012-05-28 22:06:42 -040040 ASSERT16();
Kevin O'Connorbf2e8c22012-05-28 12:59:58 -040041 u16 stack_seg = SEG_LOW;
42 u32 bkup_ss, bkup_esp;
Kevin O'Connore77c7052012-05-28 22:06:42 -040043 asm volatile(
44 // Backup current %ss/%esp values.
45 "movw %%ss, %w3\n"
46 "movl %%esp, %4\n"
47 // Copy stack seg to %ds/%ss and set %esp
48 "movw %w6, %%ds\n"
49 "movw %w6, %%ss\n"
50 "movl %5, %%esp\n"
Kevin O'Connorbf2e8c22012-05-28 12:59:58 -040051 "pushl %3\n"
52 "pushl %4\n"
Kevin O'Connore77c7052012-05-28 22:06:42 -040053 // Call func
54 "calll *%2\n"
Kevin O'Connorbf2e8c22012-05-28 12:59:58 -040055 "popl %4\n"
56 "popl %3\n"
Kevin O'Connore77c7052012-05-28 22:06:42 -040057 // Restore segments and stack
58 "movw %w3, %%ds\n"
59 "movw %w3, %%ss\n"
60 "movl %4, %%esp"
61 : "+a" (eax), "+d" (edx), "+c" (func), "=&r" (bkup_ss), "=&r" (bkup_esp)
Kevin O'Connorbf2e8c22012-05-28 12:59:58 -040062 : "m" (StackPos), "r" (stack_seg)
63 : "cc", "memory");
64 return eax;
65}
66
67// Switch back to original caller's stack and call a function.
Kevin O'Connorecdc6552012-05-28 14:25:15 -040068u32
Kevin O'Connorbf2e8c22012-05-28 12:59:58 -040069stack_hop_back(u32 eax, u32 edx, void *func)
70{
71 if (!on_extra_stack())
72 return ((u32 (*)(u32, u32))func)(eax, edx);
73 ASSERT16();
74 u16 bkup_ss;
75 u32 bkup_stack_pos, temp;
76 asm volatile(
77 // Backup stack_pos and current %ss/%esp
78 "movl %6, %4\n"
79 "movw %%ss, %w3\n"
80 "movl %%esp, %6\n"
81 // Restore original callers' %ss/%esp
82 "movl -4(%4), %5\n"
83 "movl %5, %%ss\n"
Kevin O'Connor1297e5d2012-06-02 20:30:58 -040084 "movw %%ds:-8(%4), %%sp\n"
Kevin O'Connorbf2e8c22012-05-28 12:59:58 -040085 "movl %5, %%ds\n"
86 // Call func
87 "calll *%2\n"
88 // Restore %ss/%esp and stack_pos
89 "movw %w3, %%ds\n"
90 "movw %w3, %%ss\n"
91 "movl %6, %%esp\n"
92 "movl %4, %6"
93 : "+a" (eax), "+d" (edx), "+c" (func), "=&r" (bkup_ss)
94 , "=&r" (bkup_stack_pos), "=&r" (temp), "+m" (StackPos)
95 :
Kevin O'Connore77c7052012-05-28 22:06:42 -040096 : "cc", "memory");
97 return eax;
98}
Kevin O'Connor9c447c32010-05-23 10:24:22 -040099
100
101/****************************************************************
Kevin O'Connore77c7052012-05-28 22:06:42 -0400102 * 16bit / 32bit calling
Kevin O'Connor9c447c32010-05-23 10:24:22 -0400103 ****************************************************************/
104
Kevin O'Connora7fc5de2009-12-13 11:48:18 -0500105static inline void sgdt(struct descloc_s *desc) {
106 asm("sgdtl %0" : "=m"(*desc));
107}
108static inline void lgdt(struct descloc_s *desc) {
109 asm("lgdtl %0" : : "m"(*desc) : "memory");
110}
111
Kevin O'Connor7acaa3c2013-10-02 20:20:03 -0400112u16 StackSeg VARLOW;
113
Kevin O'Connora7fc5de2009-12-13 11:48:18 -0500114// Call a 32bit SeaBIOS function from a 16bit SeaBIOS function.
Kevin O'Connor533b6282011-07-16 13:13:12 -0400115u32 VISIBLE16
Kevin O'Connoraf9629b2010-11-25 09:17:31 -0500116call32(void *func, u32 eax, u32 errret)
Kevin O'Connora7fc5de2009-12-13 11:48:18 -0500117{
118 ASSERT16();
119 u32 cr0 = getcr0();
120 if (cr0 & CR0_PE)
121 // Called in 16bit protected mode?!
Kevin O'Connoraf9629b2010-11-25 09:17:31 -0500122 return errret;
Kevin O'Connora7fc5de2009-12-13 11:48:18 -0500123
124 // Backup cmos index register and disable nmi
125 u8 cmosindex = inb(PORT_CMOS_INDEX);
126 outb(cmosindex | NMI_DISABLE_BIT, PORT_CMOS_INDEX);
127 inb(PORT_CMOS_DATA);
128
129 // Backup fs/gs and gdt
130 u16 fs = GET_SEG(FS), gs = GET_SEG(GS);
131 struct descloc_s gdt;
132 sgdt(&gdt);
133
Kevin O'Connor7acaa3c2013-10-02 20:20:03 -0400134 u16 oldstackseg = GET_LOW(StackSeg);
135 SET_LOW(StackSeg, GET_SEG(SS));
Kevin O'Connora7fc5de2009-12-13 11:48:18 -0500136 u32 bkup_ss, bkup_esp;
137 asm volatile(
138 // Backup ss/esp / set esp to flat stack location
139 " movl %%ss, %0\n"
140 " movl %%esp, %1\n"
141 " shll $4, %0\n"
142 " addl %0, %%esp\n"
Kevin O'Connoraf9629b2010-11-25 09:17:31 -0500143 " shrl $4, %0\n"
Kevin O'Connora7fc5de2009-12-13 11:48:18 -0500144
Kevin O'Connorfb214dc2009-12-20 13:11:17 -0500145 // Transition to 32bit mode, call func, return to 16bit
Kevin O'Connor4057f982010-11-25 08:52:50 -0500146 " movl $(" __stringify(BUILD_BIOS_ADDR) " + 1f), %%edx\n"
Kevin O'Connora7fc5de2009-12-13 11:48:18 -0500147 " jmp transition32\n"
148 " .code32\n"
Kevin O'Connoraf9629b2010-11-25 09:17:31 -0500149 "1:calll *%3\n"
Kevin O'Connor4057f982010-11-25 08:52:50 -0500150 " movl $2f, %%edx\n"
Kevin O'Connora7fc5de2009-12-13 11:48:18 -0500151 " jmp transition16big\n"
152
153 // Restore ds/ss/esp
154 " .code16gcc\n"
155 "2:movl %0, %%ds\n"
156 " movl %0, %%ss\n"
157 " movl %1, %%esp\n"
Kevin O'Connoraf9629b2010-11-25 09:17:31 -0500158 : "=&r" (bkup_ss), "=&r" (bkup_esp), "+a" (eax)
Kevin O'Connorbca07362010-03-20 20:41:38 -0400159 : "r" (func)
Kevin O'Connoraf9629b2010-11-25 09:17:31 -0500160 : "ecx", "edx", "cc", "memory");
Kevin O'Connora7fc5de2009-12-13 11:48:18 -0500161
Kevin O'Connor7acaa3c2013-10-02 20:20:03 -0400162 SET_LOW(StackSeg, oldstackseg);
163
Kevin O'Connora7fc5de2009-12-13 11:48:18 -0500164 // Restore gdt and fs/gs
165 lgdt(&gdt);
166 SET_SEG(FS, fs);
167 SET_SEG(GS, gs);
168
169 // Restore cmos index register
170 outb(cmosindex, PORT_CMOS_INDEX);
171 inb(PORT_CMOS_DATA);
Kevin O'Connoraf9629b2010-11-25 09:17:31 -0500172 return eax;
Kevin O'Connora7fc5de2009-12-13 11:48:18 -0500173}
174
Kevin O'Connor2f898d52012-05-28 10:56:20 -0400175// Call a 16bit SeaBIOS function from a 32bit SeaBIOS function.
176static inline u32
Kevin O'Connor9e75b082013-09-29 19:58:09 -0400177call16(u32 eax, u32 edx, void *func)
Kevin O'Connore77c7052012-05-28 22:06:42 -0400178{
179 ASSERT32FLAT();
Kevin O'Connor7acaa3c2013-10-02 20:20:03 -0400180 if (getesp() > MAIN_STACK_MAX)
Kevin O'Connore77c7052012-05-28 22:06:42 -0400181 panic("call16 with invalid stack\n");
Kevin O'Connor9e75b082013-09-29 19:58:09 -0400182 extern u32 __call16(u32 eax, u32 edx, void *func);
183 return __call16(eax, edx, func - BUILD_BIOS_ADDR);
Kevin O'Connor2f898d52012-05-28 10:56:20 -0400184}
185
186static inline u32
Kevin O'Connor9e75b082013-09-29 19:58:09 -0400187call16big(u32 eax, u32 edx, void *func)
Kevin O'Connor2f898d52012-05-28 10:56:20 -0400188{
189 ASSERT32FLAT();
Kevin O'Connor7acaa3c2013-10-02 20:20:03 -0400190 if (getesp() > MAIN_STACK_MAX)
Kevin O'Connor2f898d52012-05-28 10:56:20 -0400191 panic("call16big with invalid stack\n");
Kevin O'Connor9e75b082013-09-29 19:58:09 -0400192 extern u32 __call16big(u32 eax, u32 edx, void *func);
193 return __call16big(eax, edx, func - BUILD_BIOS_ADDR);
Kevin O'Connor2f898d52012-05-28 10:56:20 -0400194}
195
Kevin O'Connorbfac2b52013-09-29 10:48:24 -0400196
197/****************************************************************
198 * External 16bit interface calling
199 ****************************************************************/
200
Kevin O'Connor2f898d52012-05-28 10:56:20 -0400201// Far call 16bit code with a specified register state.
202void VISIBLE16
Kevin O'Connor2d977562013-09-29 20:21:40 -0400203_farcall16(struct bregs *callregs, u16 callregseg)
Kevin O'Connor2f898d52012-05-28 10:56:20 -0400204{
205 ASSERT16();
Kevin O'Connorbfac2b52013-09-29 10:48:24 -0400206 if (on_extra_stack()) {
Kevin O'Connor2d977562013-09-29 20:21:40 -0400207 stack_hop_back((u32)callregs, callregseg, _farcall16);
Kevin O'Connorbfac2b52013-09-29 10:48:24 -0400208 return;
209 }
Kevin O'Connor2f898d52012-05-28 10:56:20 -0400210 asm volatile(
211 "calll __farcall16\n"
Kevin O'Connor2d977562013-09-29 20:21:40 -0400212 : "+a" (callregs), "+m" (*callregs), "+d" (callregseg)
213 :
214 : "ebx", "ecx", "esi", "edi", "cc", "memory");
Kevin O'Connore77c7052012-05-28 22:06:42 -0400215}
216
217inline void
Kevin O'Connor2f898d52012-05-28 10:56:20 -0400218farcall16(struct bregs *callregs)
219{
220 if (MODE16) {
Kevin O'Connor2d977562013-09-29 20:21:40 -0400221 _farcall16(callregs, GET_SEG(SS));
Kevin O'Connor2f898d52012-05-28 10:56:20 -0400222 return;
223 }
224 extern void _cfunc16__farcall16(void);
Kevin O'Connor7acaa3c2013-10-02 20:20:03 -0400225 call16((u32)callregs - StackSeg * 16, StackSeg, _cfunc16__farcall16);
Kevin O'Connor2f898d52012-05-28 10:56:20 -0400226}
227
228inline void
229farcall16big(struct bregs *callregs)
230{
231 extern void _cfunc16__farcall16(void);
Kevin O'Connor7acaa3c2013-10-02 20:20:03 -0400232 call16big((u32)callregs - StackSeg * 16, StackSeg, _cfunc16__farcall16);
Kevin O'Connor2f898d52012-05-28 10:56:20 -0400233}
234
235// Invoke a 16bit software interrupt.
236inline void
Kevin O'Connore77c7052012-05-28 22:06:42 -0400237__call16_int(struct bregs *callregs, u16 offset)
238{
239 if (MODESEGMENT)
240 callregs->code.seg = GET_SEG(CS);
241 else
242 callregs->code.seg = SEG_BIOS;
243 callregs->code.offset = offset;
244 farcall16(callregs);
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500245}
246
247
248/****************************************************************
249 * Threads
250 ****************************************************************/
251
Kevin O'Connore77c7052012-05-28 22:06:42 -0400252// Thread info - stored at bottom of each thread stack - don't change
253// without also updating the inline assembler below.
254struct thread_info {
Kevin O'Connore77c7052012-05-28 22:06:42 -0400255 void *stackpos;
Kevin O'Connor0039a942013-06-08 21:50:15 -0400256 struct hlist_node node;
Kevin O'Connore77c7052012-05-28 22:06:42 -0400257};
Kevin O'Connor89a2f962013-02-18 23:36:03 -0500258struct thread_info MainThread VARFSEG = {
Kevin O'Connor0039a942013-06-08 21:50:15 -0400259 NULL, { &MainThread.node, &MainThread.node.next }
Kevin O'Connore77c7052012-05-28 22:06:42 -0400260};
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500261#define THREADSTACKSIZE 4096
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500262
Kevin O'Connor2a8633f2013-06-08 22:11:07 -0400263// Check if any threads are running.
264static int
265have_threads(void)
266{
Kevin O'Connor0039a942013-06-08 21:50:15 -0400267 return (CONFIG_THREADS
268 && GET_FLATPTR(MainThread.node.next) != &MainThread.node);
Kevin O'Connor2a8633f2013-06-08 22:11:07 -0400269}
270
Kevin O'Connorad901592009-12-13 11:25:25 -0500271// Return the 'struct thread_info' for the currently running thread.
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500272struct thread_info *
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500273getCurThread(void)
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500274{
275 u32 esp = getesp();
Kevin O'Connor7acaa3c2013-10-02 20:20:03 -0400276 if (esp <= MAIN_STACK_MAX)
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500277 return &MainThread;
278 return (void*)ALIGN_DOWN(esp, THREADSTACKSIZE);
279}
280
Kevin O'Connorad901592009-12-13 11:25:25 -0500281// Switch to next thread stack.
282static void
283switch_next(struct thread_info *cur)
284{
Kevin O'Connor0039a942013-06-08 21:50:15 -0400285 struct thread_info *next = container_of(
286 cur->node.next, struct thread_info, node);
Kevin O'Connorfb214dc2009-12-20 13:11:17 -0500287 if (cur == next)
288 // Nothing to do.
289 return;
Kevin O'Connorad901592009-12-13 11:25:25 -0500290 asm volatile(
291 " pushl $1f\n" // store return pc
292 " pushl %%ebp\n" // backup %ebp
Kevin O'Connor0039a942013-06-08 21:50:15 -0400293 " movl %%esp, (%%eax)\n" // cur->stackpos = %esp
294 " movl (%%ecx), %%esp\n" // %esp = next->stackpos
Kevin O'Connorad901592009-12-13 11:25:25 -0500295 " popl %%ebp\n" // restore %ebp
296 " retl\n" // restore pc
297 "1:\n"
298 : "+a"(cur), "+c"(next)
299 :
300 : "ebx", "edx", "esi", "edi", "cc", "memory");
301}
302
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500303// Last thing called from a thread (called on "next" stack).
304static void
305__end_thread(struct thread_info *old)
306{
Kevin O'Connor0039a942013-06-08 21:50:15 -0400307 hlist_del(&old->node);
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500308 free(old);
309 dprintf(DEBUG_thread, "\\%08x/ End thread\n", (u32)old);
Kevin O'Connor2a8633f2013-06-08 22:11:07 -0400310 if (!have_threads())
Kevin O'Connore438b0c2010-05-01 12:20:33 -0400311 dprintf(1, "All threads complete.\n");
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500312}
313
Kevin O'Connorad901592009-12-13 11:25:25 -0500314// Create a new thread and start executing 'func' in it.
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500315void
316run_thread(void (*func)(void*), void *data)
317{
Kevin O'Connor52a300f2009-12-26 23:32:57 -0500318 ASSERT32FLAT();
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500319 if (! CONFIG_THREADS)
320 goto fail;
321 struct thread_info *thread;
322 thread = memalign_tmphigh(THREADSTACKSIZE, THREADSTACKSIZE);
323 if (!thread)
324 goto fail;
325
326 thread->stackpos = (void*)thread + THREADSTACKSIZE;
327 struct thread_info *cur = getCurThread();
Kevin O'Connor0039a942013-06-08 21:50:15 -0400328 hlist_add_after(&thread->node, &cur->node);
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500329
330 dprintf(DEBUG_thread, "/%08x\\ Start thread\n", (u32)thread);
331 asm volatile(
332 // Start thread
333 " pushl $1f\n" // store return pc
334 " pushl %%ebp\n" // backup %ebp
Kevin O'Connor0039a942013-06-08 21:50:15 -0400335 " movl %%esp, (%%edx)\n" // cur->stackpos = %esp
336 " movl (%%ebx), %%esp\n" // %esp = thread->stackpos
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500337 " calll *%%ecx\n" // Call func
338
339 // End thread
Kevin O'Connor0039a942013-06-08 21:50:15 -0400340 " movl 4(%%ebx), %%ecx\n" // %ecx = thread->node.next
341 " movl -4(%%ecx), %%esp\n" // %esp = next->stackpos
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500342 " movl %%ebx, %%eax\n"
343 " calll %4\n" // call __end_thread(thread)
344 " popl %%ebp\n" // restore %ebp
345 " retl\n" // restore pc
346 "1:\n"
347 : "+a"(data), "+c"(func), "+b"(thread), "+d"(cur)
348 : "m"(*(u8*)__end_thread)
349 : "esi", "edi", "cc", "memory");
350 return;
351
352fail:
353 func(data);
354}
355
Kevin O'Connor3cf301f2013-06-08 22:05:19 -0400356
357/****************************************************************
358 * Thread helpers
359 ****************************************************************/
360
361// Low-level irq enable.
362void VISIBLE16
363check_irqs(void)
364{
Kevin O'Connorbfac2b52013-09-29 10:48:24 -0400365 if (on_extra_stack()) {
366 stack_hop_back(0, 0, check_irqs);
367 return;
368 }
Kevin O'Connor3cf301f2013-06-08 22:05:19 -0400369 asm volatile("sti ; nop ; rep ; nop ; cli ; cld" : : :"memory");
370}
371
372// Briefly permit irqs to occur.
373void
374yield(void)
375{
376 if (MODESEGMENT) {
Kevin O'Connorbfac2b52013-09-29 10:48:24 -0400377 check_irqs();
Kevin O'Connor3cf301f2013-06-08 22:05:19 -0400378 return;
379 }
380 extern void _cfunc16_check_irqs(void);
381 if (!CONFIG_THREADS) {
Kevin O'Connor9e75b082013-09-29 19:58:09 -0400382 call16big(0, 0, _cfunc16_check_irqs);
Kevin O'Connor3cf301f2013-06-08 22:05:19 -0400383 return;
384 }
385 struct thread_info *cur = getCurThread();
386 if (cur == &MainThread)
387 // Permit irqs to fire
Kevin O'Connor9e75b082013-09-29 19:58:09 -0400388 call16big(0, 0, _cfunc16_check_irqs);
Kevin O'Connor3cf301f2013-06-08 22:05:19 -0400389
390 // Switch to the next thread
391 switch_next(cur);
392}
393
394void VISIBLE16
395wait_irq(void)
396{
Kevin O'Connorbfac2b52013-09-29 10:48:24 -0400397 if (on_extra_stack()) {
398 stack_hop_back(0, 0, wait_irq);
399 return;
400 }
Kevin O'Connor3cf301f2013-06-08 22:05:19 -0400401 asm volatile("sti ; hlt ; cli ; cld": : :"memory");
402}
403
404// Wait for next irq to occur.
405void
406yield_toirq(void)
407{
408 if (MODESEGMENT) {
Kevin O'Connorbfac2b52013-09-29 10:48:24 -0400409 wait_irq();
Kevin O'Connor3cf301f2013-06-08 22:05:19 -0400410 return;
411 }
Kevin O'Connor2a8633f2013-06-08 22:11:07 -0400412 if (have_threads()) {
Kevin O'Connor3cf301f2013-06-08 22:05:19 -0400413 // Threads still active - do a yield instead.
414 yield();
415 return;
416 }
417 extern void _cfunc16_wait_irq(void);
Kevin O'Connor9e75b082013-09-29 19:58:09 -0400418 call16big(0, 0, _cfunc16_wait_irq);
Kevin O'Connor3cf301f2013-06-08 22:05:19 -0400419}
420
Kevin O'Connorad901592009-12-13 11:25:25 -0500421// Wait for all threads (other than the main thread) to complete.
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500422void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500423wait_threads(void)
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500424{
Kevin O'Connor52a300f2009-12-26 23:32:57 -0500425 ASSERT32FLAT();
Kevin O'Connor2a8633f2013-06-08 22:11:07 -0400426 while (have_threads())
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500427 yield();
428}
Kevin O'Connorad901592009-12-13 11:25:25 -0500429
Kevin O'Connore9086652010-02-27 13:49:47 -0500430void
431mutex_lock(struct mutex_s *mutex)
432{
433 ASSERT32FLAT();
434 if (! CONFIG_THREADS)
435 return;
436 while (mutex->isLocked)
437 yield();
438 mutex->isLocked = 1;
439}
440
441void
442mutex_unlock(struct mutex_s *mutex)
443{
444 ASSERT32FLAT();
445 if (! CONFIG_THREADS)
446 return;
447 mutex->isLocked = 0;
448}
449
Kevin O'Connorad901592009-12-13 11:25:25 -0500450
451/****************************************************************
452 * Thread preemption
453 ****************************************************************/
454
Kevin O'Connor89a2f962013-02-18 23:36:03 -0500455int CanPreempt VARFSEG;
Kevin O'Connorad901592009-12-13 11:25:25 -0500456static u32 PreemptCount;
457
458// Turn on RTC irqs and arrange for them to check the 32bit threads.
459void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500460start_preempt(void)
Kevin O'Connorad901592009-12-13 11:25:25 -0500461{
Kevin O'Connorb5c7da62013-02-08 00:45:44 -0500462 if (! CONFIG_THREAD_OPTIONROMS)
Kevin O'Connorad901592009-12-13 11:25:25 -0500463 return;
464 CanPreempt = 1;
465 PreemptCount = 0;
Kevin O'Connor8b7861c2013-09-15 02:29:06 -0400466 rtc_use();
Kevin O'Connorad901592009-12-13 11:25:25 -0500467}
468
469// Turn off RTC irqs / stop checking for thread execution.
470void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500471finish_preempt(void)
Kevin O'Connorad901592009-12-13 11:25:25 -0500472{
Kevin O'Connorb5c7da62013-02-08 00:45:44 -0500473 if (! CONFIG_THREAD_OPTIONROMS) {
Kevin O'Connora7eb8fc2010-04-02 13:13:23 -0400474 yield();
Kevin O'Connorad901592009-12-13 11:25:25 -0500475 return;
Kevin O'Connora7eb8fc2010-04-02 13:13:23 -0400476 }
Kevin O'Connorad901592009-12-13 11:25:25 -0500477 CanPreempt = 0;
Kevin O'Connor8b7861c2013-09-15 02:29:06 -0400478 rtc_release();
Kevin O'Connore438b0c2010-05-01 12:20:33 -0400479 dprintf(9, "Done preempt - %d checks\n", PreemptCount);
Kevin O'Connora7eb8fc2010-04-02 13:13:23 -0400480 yield();
Kevin O'Connorad901592009-12-13 11:25:25 -0500481}
482
Kevin O'Connord7eb27e2010-03-20 18:17:19 -0400483// Check if preemption is on, and wait for it to complete if so.
484int
485wait_preempt(void)
486{
Kevin O'Connor2b0fb8c2013-08-07 23:03:47 -0400487 if (MODESEGMENT || !CONFIG_THREAD_OPTIONROMS || !CanPreempt
Kevin O'Connor7acaa3c2013-10-02 20:20:03 -0400488 || getesp() < MAIN_STACK_MAX)
Kevin O'Connord7eb27e2010-03-20 18:17:19 -0400489 return 0;
490 while (CanPreempt)
491 yield();
492 return 1;
493}
494
Kevin O'Connorad901592009-12-13 11:25:25 -0500495// Try to execute 32bit threads.
Kevin O'Connord1b4f962010-09-15 21:38:16 -0400496void VISIBLE32INIT
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500497yield_preempt(void)
Kevin O'Connorad901592009-12-13 11:25:25 -0500498{
499 PreemptCount++;
500 switch_next(&MainThread);
501}
Kevin O'Connorad901592009-12-13 11:25:25 -0500502
503// 16bit code that checks if threads are pending and executes them if so.
504void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500505check_preempt(void)
Kevin O'Connorad901592009-12-13 11:25:25 -0500506{
Kevin O'Connorf3fe3aa2010-12-05 12:38:33 -0500507 extern void _cfunc32flat_yield_preempt(void);
Kevin O'Connor2a8633f2013-06-08 22:11:07 -0400508 if (CONFIG_THREAD_OPTIONROMS && GET_GLOBAL(CanPreempt) && have_threads())
509 call32(_cfunc32flat_yield_preempt, 0, 0);
Kevin O'Connorad901592009-12-13 11:25:25 -0500510}