blob: 9c528f551877786803dafdf39de4f05f128110ee [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'Connor7acaa3c2013-10-02 20:20:03 -0400105u16 StackSeg VARLOW;
106
Kevin O'Connora7fc5de2009-12-13 11:48:18 -0500107// Call a 32bit SeaBIOS function from a 16bit SeaBIOS function.
Kevin O'Connor533b6282011-07-16 13:13:12 -0400108u32 VISIBLE16
Kevin O'Connoraf9629b2010-11-25 09:17:31 -0500109call32(void *func, u32 eax, u32 errret)
Kevin O'Connora7fc5de2009-12-13 11:48:18 -0500110{
111 ASSERT16();
112 u32 cr0 = getcr0();
113 if (cr0 & CR0_PE)
114 // Called in 16bit protected mode?!
Kevin O'Connoraf9629b2010-11-25 09:17:31 -0500115 return errret;
Kevin O'Connora7fc5de2009-12-13 11:48:18 -0500116
117 // Backup cmos index register and disable nmi
118 u8 cmosindex = inb(PORT_CMOS_INDEX);
119 outb(cmosindex | NMI_DISABLE_BIT, PORT_CMOS_INDEX);
120 inb(PORT_CMOS_DATA);
121
122 // Backup fs/gs and gdt
123 u16 fs = GET_SEG(FS), gs = GET_SEG(GS);
124 struct descloc_s gdt;
125 sgdt(&gdt);
126
Kevin O'Connor7acaa3c2013-10-02 20:20:03 -0400127 u16 oldstackseg = GET_LOW(StackSeg);
128 SET_LOW(StackSeg, GET_SEG(SS));
Kevin O'Connora7fc5de2009-12-13 11:48:18 -0500129 u32 bkup_ss, bkup_esp;
130 asm volatile(
131 // Backup ss/esp / set esp to flat stack location
132 " movl %%ss, %0\n"
133 " movl %%esp, %1\n"
134 " shll $4, %0\n"
135 " addl %0, %%esp\n"
Kevin O'Connoraf9629b2010-11-25 09:17:31 -0500136 " shrl $4, %0\n"
Kevin O'Connora7fc5de2009-12-13 11:48:18 -0500137
Kevin O'Connorfb214dc2009-12-20 13:11:17 -0500138 // Transition to 32bit mode, call func, return to 16bit
Kevin O'Connor4057f982010-11-25 08:52:50 -0500139 " movl $(" __stringify(BUILD_BIOS_ADDR) " + 1f), %%edx\n"
Kevin O'Connora7fc5de2009-12-13 11:48:18 -0500140 " jmp transition32\n"
141 " .code32\n"
Kevin O'Connoraf9629b2010-11-25 09:17:31 -0500142 "1:calll *%3\n"
Kevin O'Connor4057f982010-11-25 08:52:50 -0500143 " movl $2f, %%edx\n"
Kevin O'Connora7fc5de2009-12-13 11:48:18 -0500144 " jmp transition16big\n"
145
146 // Restore ds/ss/esp
147 " .code16gcc\n"
148 "2:movl %0, %%ds\n"
149 " movl %0, %%ss\n"
150 " movl %1, %%esp\n"
Kevin O'Connoraf9629b2010-11-25 09:17:31 -0500151 : "=&r" (bkup_ss), "=&r" (bkup_esp), "+a" (eax)
Kevin O'Connorbca07362010-03-20 20:41:38 -0400152 : "r" (func)
Kevin O'Connoraf9629b2010-11-25 09:17:31 -0500153 : "ecx", "edx", "cc", "memory");
Kevin O'Connora7fc5de2009-12-13 11:48:18 -0500154
Kevin O'Connor7acaa3c2013-10-02 20:20:03 -0400155 SET_LOW(StackSeg, oldstackseg);
156
Kevin O'Connora7fc5de2009-12-13 11:48:18 -0500157 // Restore gdt and fs/gs
158 lgdt(&gdt);
159 SET_SEG(FS, fs);
160 SET_SEG(GS, gs);
161
162 // Restore cmos index register
163 outb(cmosindex, PORT_CMOS_INDEX);
164 inb(PORT_CMOS_DATA);
Kevin O'Connoraf9629b2010-11-25 09:17:31 -0500165 return eax;
Kevin O'Connora7fc5de2009-12-13 11:48:18 -0500166}
167
Kevin O'Connor2f898d52012-05-28 10:56:20 -0400168// Call a 16bit SeaBIOS function from a 32bit SeaBIOS function.
169static inline u32
Kevin O'Connor9e75b082013-09-29 19:58:09 -0400170call16(u32 eax, u32 edx, void *func)
Kevin O'Connore77c7052012-05-28 22:06:42 -0400171{
172 ASSERT32FLAT();
Kevin O'Connor7acaa3c2013-10-02 20:20:03 -0400173 if (getesp() > MAIN_STACK_MAX)
Kevin O'Connore77c7052012-05-28 22:06:42 -0400174 panic("call16 with invalid stack\n");
Kevin O'Connor9e75b082013-09-29 19:58:09 -0400175 extern u32 __call16(u32 eax, u32 edx, void *func);
176 return __call16(eax, edx, func - BUILD_BIOS_ADDR);
Kevin O'Connor2f898d52012-05-28 10:56:20 -0400177}
178
179static inline u32
Kevin O'Connor9e75b082013-09-29 19:58:09 -0400180call16big(u32 eax, u32 edx, void *func)
Kevin O'Connor2f898d52012-05-28 10:56:20 -0400181{
182 ASSERT32FLAT();
Kevin O'Connor7acaa3c2013-10-02 20:20:03 -0400183 if (getesp() > MAIN_STACK_MAX)
Kevin O'Connor2f898d52012-05-28 10:56:20 -0400184 panic("call16big with invalid stack\n");
Kevin O'Connor9e75b082013-09-29 19:58:09 -0400185 extern u32 __call16big(u32 eax, u32 edx, void *func);
186 return __call16big(eax, edx, func - BUILD_BIOS_ADDR);
Kevin O'Connor2f898d52012-05-28 10:56:20 -0400187}
188
Kevin O'Connorbfac2b52013-09-29 10:48:24 -0400189
190/****************************************************************
191 * External 16bit interface calling
192 ****************************************************************/
193
Kevin O'Connor2f898d52012-05-28 10:56:20 -0400194// Far call 16bit code with a specified register state.
195void VISIBLE16
Kevin O'Connor2d977562013-09-29 20:21:40 -0400196_farcall16(struct bregs *callregs, u16 callregseg)
Kevin O'Connor2f898d52012-05-28 10:56:20 -0400197{
198 ASSERT16();
Kevin O'Connorbfac2b52013-09-29 10:48:24 -0400199 if (on_extra_stack()) {
Kevin O'Connor2d977562013-09-29 20:21:40 -0400200 stack_hop_back((u32)callregs, callregseg, _farcall16);
Kevin O'Connorbfac2b52013-09-29 10:48:24 -0400201 return;
202 }
Kevin O'Connor2f898d52012-05-28 10:56:20 -0400203 asm volatile(
204 "calll __farcall16\n"
Kevin O'Connor2d977562013-09-29 20:21:40 -0400205 : "+a" (callregs), "+m" (*callregs), "+d" (callregseg)
206 :
207 : "ebx", "ecx", "esi", "edi", "cc", "memory");
Kevin O'Connore77c7052012-05-28 22:06:42 -0400208}
209
210inline void
Kevin O'Connor2f898d52012-05-28 10:56:20 -0400211farcall16(struct bregs *callregs)
212{
213 if (MODE16) {
Kevin O'Connor2d977562013-09-29 20:21:40 -0400214 _farcall16(callregs, GET_SEG(SS));
Kevin O'Connor2f898d52012-05-28 10:56:20 -0400215 return;
216 }
217 extern void _cfunc16__farcall16(void);
Kevin O'Connor7acaa3c2013-10-02 20:20:03 -0400218 call16((u32)callregs - StackSeg * 16, StackSeg, _cfunc16__farcall16);
Kevin O'Connor2f898d52012-05-28 10:56:20 -0400219}
220
221inline void
222farcall16big(struct bregs *callregs)
223{
224 extern void _cfunc16__farcall16(void);
Kevin O'Connor7acaa3c2013-10-02 20:20:03 -0400225 call16big((u32)callregs - StackSeg * 16, StackSeg, _cfunc16__farcall16);
Kevin O'Connor2f898d52012-05-28 10:56:20 -0400226}
227
228// Invoke a 16bit software interrupt.
229inline void
Kevin O'Connore77c7052012-05-28 22:06:42 -0400230__call16_int(struct bregs *callregs, u16 offset)
231{
232 if (MODESEGMENT)
233 callregs->code.seg = GET_SEG(CS);
234 else
235 callregs->code.seg = SEG_BIOS;
236 callregs->code.offset = offset;
237 farcall16(callregs);
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500238}
239
240
241/****************************************************************
242 * Threads
243 ****************************************************************/
244
Kevin O'Connore77c7052012-05-28 22:06:42 -0400245// Thread info - stored at bottom of each thread stack - don't change
246// without also updating the inline assembler below.
247struct thread_info {
Kevin O'Connore77c7052012-05-28 22:06:42 -0400248 void *stackpos;
Kevin O'Connor0039a942013-06-08 21:50:15 -0400249 struct hlist_node node;
Kevin O'Connore77c7052012-05-28 22:06:42 -0400250};
Kevin O'Connor89a2f962013-02-18 23:36:03 -0500251struct thread_info MainThread VARFSEG = {
Kevin O'Connor0039a942013-06-08 21:50:15 -0400252 NULL, { &MainThread.node, &MainThread.node.next }
Kevin O'Connore77c7052012-05-28 22:06:42 -0400253};
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500254#define THREADSTACKSIZE 4096
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500255
Kevin O'Connor2a8633f2013-06-08 22:11:07 -0400256// Check if any threads are running.
257static int
258have_threads(void)
259{
Kevin O'Connor0039a942013-06-08 21:50:15 -0400260 return (CONFIG_THREADS
261 && GET_FLATPTR(MainThread.node.next) != &MainThread.node);
Kevin O'Connor2a8633f2013-06-08 22:11:07 -0400262}
263
Kevin O'Connorad901592009-12-13 11:25:25 -0500264// Return the 'struct thread_info' for the currently running thread.
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500265struct thread_info *
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500266getCurThread(void)
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500267{
268 u32 esp = getesp();
Kevin O'Connor7acaa3c2013-10-02 20:20:03 -0400269 if (esp <= MAIN_STACK_MAX)
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500270 return &MainThread;
271 return (void*)ALIGN_DOWN(esp, THREADSTACKSIZE);
272}
273
Kevin O'Connorad901592009-12-13 11:25:25 -0500274// Switch to next thread stack.
275static void
276switch_next(struct thread_info *cur)
277{
Kevin O'Connor0039a942013-06-08 21:50:15 -0400278 struct thread_info *next = container_of(
279 cur->node.next, struct thread_info, node);
Kevin O'Connorfb214dc2009-12-20 13:11:17 -0500280 if (cur == next)
281 // Nothing to do.
282 return;
Kevin O'Connorad901592009-12-13 11:25:25 -0500283 asm volatile(
284 " pushl $1f\n" // store return pc
285 " pushl %%ebp\n" // backup %ebp
Kevin O'Connor0039a942013-06-08 21:50:15 -0400286 " movl %%esp, (%%eax)\n" // cur->stackpos = %esp
287 " movl (%%ecx), %%esp\n" // %esp = next->stackpos
Kevin O'Connorad901592009-12-13 11:25:25 -0500288 " popl %%ebp\n" // restore %ebp
289 " retl\n" // restore pc
290 "1:\n"
291 : "+a"(cur), "+c"(next)
292 :
293 : "ebx", "edx", "esi", "edi", "cc", "memory");
294}
295
Kevin O'Connor48367e22013-12-22 22:44:08 -0500296// Last thing called from a thread (called on MainThread stack).
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500297static void
298__end_thread(struct thread_info *old)
299{
Kevin O'Connor0039a942013-06-08 21:50:15 -0400300 hlist_del(&old->node);
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500301 dprintf(DEBUG_thread, "\\%08x/ End thread\n", (u32)old);
Kevin O'Connor48367e22013-12-22 22:44:08 -0500302 free(old);
Kevin O'Connor2a8633f2013-06-08 22:11:07 -0400303 if (!have_threads())
Kevin O'Connore438b0c2010-05-01 12:20:33 -0400304 dprintf(1, "All threads complete.\n");
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500305}
306
Kevin O'Connorad901592009-12-13 11:25:25 -0500307// Create a new thread and start executing 'func' in it.
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500308void
309run_thread(void (*func)(void*), void *data)
310{
Kevin O'Connor52a300f2009-12-26 23:32:57 -0500311 ASSERT32FLAT();
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500312 if (! CONFIG_THREADS)
313 goto fail;
314 struct thread_info *thread;
315 thread = memalign_tmphigh(THREADSTACKSIZE, THREADSTACKSIZE);
316 if (!thread)
317 goto fail;
318
Kevin O'Connor48367e22013-12-22 22:44:08 -0500319 dprintf(DEBUG_thread, "/%08x\\ Start thread\n", (u32)thread);
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500320 thread->stackpos = (void*)thread + THREADSTACKSIZE;
321 struct thread_info *cur = getCurThread();
Kevin O'Connor0039a942013-06-08 21:50:15 -0400322 hlist_add_after(&thread->node, &cur->node);
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500323 asm volatile(
324 // Start thread
325 " pushl $1f\n" // store return pc
326 " pushl %%ebp\n" // backup %ebp
Kevin O'Connor0039a942013-06-08 21:50:15 -0400327 " movl %%esp, (%%edx)\n" // cur->stackpos = %esp
328 " movl (%%ebx), %%esp\n" // %esp = thread->stackpos
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500329 " calll *%%ecx\n" // Call func
330
331 // End thread
Kevin O'Connor48367e22013-12-22 22:44:08 -0500332 " movl %%ebx, %%eax\n" // %eax = thread
333 " movl 4(%%ebx), %%ebx\n" // %ebx = thread->node.next
334 " movl (%5), %%esp\n" // %esp = MainThread.stackpos
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500335 " calll %4\n" // call __end_thread(thread)
Kevin O'Connor48367e22013-12-22 22:44:08 -0500336 " movl -4(%%ebx), %%esp\n" // %esp = next->stackpos
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500337 " popl %%ebp\n" // restore %ebp
338 " retl\n" // restore pc
339 "1:\n"
340 : "+a"(data), "+c"(func), "+b"(thread), "+d"(cur)
Kevin O'Connor48367e22013-12-22 22:44:08 -0500341 : "m"(*(u8*)__end_thread), "m"(MainThread)
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500342 : "esi", "edi", "cc", "memory");
343 return;
344
345fail:
346 func(data);
347}
348
Kevin O'Connor3cf301f2013-06-08 22:05:19 -0400349
350/****************************************************************
351 * Thread helpers
352 ****************************************************************/
353
354// Low-level irq enable.
355void VISIBLE16
356check_irqs(void)
357{
Kevin O'Connorbfac2b52013-09-29 10:48:24 -0400358 if (on_extra_stack()) {
359 stack_hop_back(0, 0, check_irqs);
360 return;
361 }
Kevin O'Connor3cf301f2013-06-08 22:05:19 -0400362 asm volatile("sti ; nop ; rep ; nop ; cli ; cld" : : :"memory");
363}
364
365// Briefly permit irqs to occur.
366void
367yield(void)
368{
369 if (MODESEGMENT) {
Kevin O'Connorbfac2b52013-09-29 10:48:24 -0400370 check_irqs();
Kevin O'Connor3cf301f2013-06-08 22:05:19 -0400371 return;
372 }
373 extern void _cfunc16_check_irqs(void);
374 if (!CONFIG_THREADS) {
Kevin O'Connor9e75b082013-09-29 19:58:09 -0400375 call16big(0, 0, _cfunc16_check_irqs);
Kevin O'Connor3cf301f2013-06-08 22:05:19 -0400376 return;
377 }
378 struct thread_info *cur = getCurThread();
379 if (cur == &MainThread)
380 // Permit irqs to fire
Kevin O'Connor9e75b082013-09-29 19:58:09 -0400381 call16big(0, 0, _cfunc16_check_irqs);
Kevin O'Connor3cf301f2013-06-08 22:05:19 -0400382
383 // Switch to the next thread
384 switch_next(cur);
385}
386
387void VISIBLE16
388wait_irq(void)
389{
Kevin O'Connorbfac2b52013-09-29 10:48:24 -0400390 if (on_extra_stack()) {
391 stack_hop_back(0, 0, wait_irq);
392 return;
393 }
Kevin O'Connor3cf301f2013-06-08 22:05:19 -0400394 asm volatile("sti ; hlt ; cli ; cld": : :"memory");
395}
396
397// Wait for next irq to occur.
398void
399yield_toirq(void)
400{
401 if (MODESEGMENT) {
Kevin O'Connorbfac2b52013-09-29 10:48:24 -0400402 wait_irq();
Kevin O'Connor3cf301f2013-06-08 22:05:19 -0400403 return;
404 }
Kevin O'Connor2a8633f2013-06-08 22:11:07 -0400405 if (have_threads()) {
Kevin O'Connor3cf301f2013-06-08 22:05:19 -0400406 // Threads still active - do a yield instead.
407 yield();
408 return;
409 }
410 extern void _cfunc16_wait_irq(void);
Kevin O'Connor9e75b082013-09-29 19:58:09 -0400411 call16big(0, 0, _cfunc16_wait_irq);
Kevin O'Connor3cf301f2013-06-08 22:05:19 -0400412}
413
Kevin O'Connorad901592009-12-13 11:25:25 -0500414// Wait for all threads (other than the main thread) to complete.
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500415void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500416wait_threads(void)
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500417{
Kevin O'Connor52a300f2009-12-26 23:32:57 -0500418 ASSERT32FLAT();
Kevin O'Connor2a8633f2013-06-08 22:11:07 -0400419 while (have_threads())
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500420 yield();
421}
Kevin O'Connorad901592009-12-13 11:25:25 -0500422
Kevin O'Connore9086652010-02-27 13:49:47 -0500423void
424mutex_lock(struct mutex_s *mutex)
425{
426 ASSERT32FLAT();
427 if (! CONFIG_THREADS)
428 return;
429 while (mutex->isLocked)
430 yield();
431 mutex->isLocked = 1;
432}
433
434void
435mutex_unlock(struct mutex_s *mutex)
436{
437 ASSERT32FLAT();
438 if (! CONFIG_THREADS)
439 return;
440 mutex->isLocked = 0;
441}
442
Kevin O'Connorad901592009-12-13 11:25:25 -0500443
444/****************************************************************
445 * Thread preemption
446 ****************************************************************/
447
Kevin O'Connor89a2f962013-02-18 23:36:03 -0500448int CanPreempt VARFSEG;
Kevin O'Connorad901592009-12-13 11:25:25 -0500449static u32 PreemptCount;
450
451// Turn on RTC irqs and arrange for them to check the 32bit threads.
452void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500453start_preempt(void)
Kevin O'Connorad901592009-12-13 11:25:25 -0500454{
Kevin O'Connorb5c7da62013-02-08 00:45:44 -0500455 if (! CONFIG_THREAD_OPTIONROMS)
Kevin O'Connorad901592009-12-13 11:25:25 -0500456 return;
457 CanPreempt = 1;
458 PreemptCount = 0;
Kevin O'Connor8b7861c2013-09-15 02:29:06 -0400459 rtc_use();
Kevin O'Connorad901592009-12-13 11:25:25 -0500460}
461
462// Turn off RTC irqs / stop checking for thread execution.
463void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500464finish_preempt(void)
Kevin O'Connorad901592009-12-13 11:25:25 -0500465{
Kevin O'Connorb5c7da62013-02-08 00:45:44 -0500466 if (! CONFIG_THREAD_OPTIONROMS) {
Kevin O'Connora7eb8fc2010-04-02 13:13:23 -0400467 yield();
Kevin O'Connorad901592009-12-13 11:25:25 -0500468 return;
Kevin O'Connora7eb8fc2010-04-02 13:13:23 -0400469 }
Kevin O'Connorad901592009-12-13 11:25:25 -0500470 CanPreempt = 0;
Kevin O'Connor8b7861c2013-09-15 02:29:06 -0400471 rtc_release();
Kevin O'Connore438b0c2010-05-01 12:20:33 -0400472 dprintf(9, "Done preempt - %d checks\n", PreemptCount);
Kevin O'Connora7eb8fc2010-04-02 13:13:23 -0400473 yield();
Kevin O'Connorad901592009-12-13 11:25:25 -0500474}
475
Kevin O'Connord7eb27e2010-03-20 18:17:19 -0400476// Check if preemption is on, and wait for it to complete if so.
477int
478wait_preempt(void)
479{
Kevin O'Connor2b0fb8c2013-08-07 23:03:47 -0400480 if (MODESEGMENT || !CONFIG_THREAD_OPTIONROMS || !CanPreempt
Kevin O'Connor7acaa3c2013-10-02 20:20:03 -0400481 || getesp() < MAIN_STACK_MAX)
Kevin O'Connord7eb27e2010-03-20 18:17:19 -0400482 return 0;
483 while (CanPreempt)
484 yield();
485 return 1;
486}
487
Kevin O'Connorad901592009-12-13 11:25:25 -0500488// Try to execute 32bit threads.
Kevin O'Connord1b4f962010-09-15 21:38:16 -0400489void VISIBLE32INIT
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500490yield_preempt(void)
Kevin O'Connorad901592009-12-13 11:25:25 -0500491{
492 PreemptCount++;
493 switch_next(&MainThread);
494}
Kevin O'Connorad901592009-12-13 11:25:25 -0500495
496// 16bit code that checks if threads are pending and executes them if so.
497void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500498check_preempt(void)
Kevin O'Connorad901592009-12-13 11:25:25 -0500499{
Kevin O'Connorf3fe3aa2010-12-05 12:38:33 -0500500 extern void _cfunc32flat_yield_preempt(void);
Kevin O'Connor2a8633f2013-06-08 22:11:07 -0400501 if (CONFIG_THREAD_OPTIONROMS && GET_GLOBAL(CanPreempt) && have_threads())
502 call32(_cfunc32flat_yield_preempt, 0, 0);
Kevin O'Connorad901592009-12-13 11:25:25 -0500503}