blob: 93da7b51525b8a8b65bb71056f684c4658e3faca [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'Connore77c7052012-05-28 22:06:42 -040011
12/****************************************************************
13 * Extra 16bit stack
14 ****************************************************************/
15
16// Space for a stack for 16bit code.
17u8 ExtraStack[BUILD_EXTRA_STACK_SIZE+1] VARLOW __aligned(8);
Kevin O'Connorbf2e8c22012-05-28 12:59:58 -040018u8 *StackPos VARLOW;
19
20// Test if currently on the extra stack
21static inline int
22on_extra_stack(void)
23{
24 return MODE16 && GET_SEG(SS) == SEG_LOW && getesp() > (u32)ExtraStack;
25}
Kevin O'Connore77c7052012-05-28 22:06:42 -040026
27// Switch to the extra stack and call a function.
Kevin O'Connorecdc6552012-05-28 14:25:15 -040028u32
Kevin O'Connore77c7052012-05-28 22:06:42 -040029stack_hop(u32 eax, u32 edx, void *func)
30{
Kevin O'Connorbf2e8c22012-05-28 12:59:58 -040031 if (on_extra_stack())
32 return ((u32 (*)(u32, u32))func)(eax, edx);
Kevin O'Connore77c7052012-05-28 22:06:42 -040033 ASSERT16();
Kevin O'Connorbf2e8c22012-05-28 12:59:58 -040034 u16 stack_seg = SEG_LOW;
35 u32 bkup_ss, bkup_esp;
Kevin O'Connore77c7052012-05-28 22:06:42 -040036 asm volatile(
37 // Backup current %ss/%esp values.
38 "movw %%ss, %w3\n"
39 "movl %%esp, %4\n"
40 // Copy stack seg to %ds/%ss and set %esp
41 "movw %w6, %%ds\n"
42 "movw %w6, %%ss\n"
43 "movl %5, %%esp\n"
Kevin O'Connorbf2e8c22012-05-28 12:59:58 -040044 "pushl %3\n"
45 "pushl %4\n"
Kevin O'Connore77c7052012-05-28 22:06:42 -040046 // Call func
47 "calll *%2\n"
Kevin O'Connorbf2e8c22012-05-28 12:59:58 -040048 "popl %4\n"
49 "popl %3\n"
Kevin O'Connore77c7052012-05-28 22:06:42 -040050 // Restore segments and stack
51 "movw %w3, %%ds\n"
52 "movw %w3, %%ss\n"
53 "movl %4, %%esp"
54 : "+a" (eax), "+d" (edx), "+c" (func), "=&r" (bkup_ss), "=&r" (bkup_esp)
Kevin O'Connorbf2e8c22012-05-28 12:59:58 -040055 : "m" (StackPos), "r" (stack_seg)
56 : "cc", "memory");
57 return eax;
58}
59
60// Switch back to original caller's stack and call a function.
Kevin O'Connorecdc6552012-05-28 14:25:15 -040061u32
Kevin O'Connorbf2e8c22012-05-28 12:59:58 -040062stack_hop_back(u32 eax, u32 edx, void *func)
63{
64 if (!on_extra_stack())
65 return ((u32 (*)(u32, u32))func)(eax, edx);
66 ASSERT16();
67 u16 bkup_ss;
68 u32 bkup_stack_pos, temp;
69 asm volatile(
70 // Backup stack_pos and current %ss/%esp
71 "movl %6, %4\n"
72 "movw %%ss, %w3\n"
73 "movl %%esp, %6\n"
74 // Restore original callers' %ss/%esp
75 "movl -4(%4), %5\n"
76 "movl %5, %%ss\n"
Kevin O'Connor1297e5d2012-06-02 20:30:58 -040077 "movw %%ds:-8(%4), %%sp\n"
Kevin O'Connorbf2e8c22012-05-28 12:59:58 -040078 "movl %5, %%ds\n"
79 // Call func
80 "calll *%2\n"
81 // Restore %ss/%esp and stack_pos
82 "movw %w3, %%ds\n"
83 "movw %w3, %%ss\n"
84 "movl %6, %%esp\n"
85 "movl %4, %6"
86 : "+a" (eax), "+d" (edx), "+c" (func), "=&r" (bkup_ss)
87 , "=&r" (bkup_stack_pos), "=&r" (temp), "+m" (StackPos)
88 :
Kevin O'Connore77c7052012-05-28 22:06:42 -040089 : "cc", "memory");
90 return eax;
91}
Kevin O'Connor9c447c32010-05-23 10:24:22 -040092
93
94/****************************************************************
Kevin O'Connore77c7052012-05-28 22:06:42 -040095 * 16bit / 32bit calling
Kevin O'Connor9c447c32010-05-23 10:24:22 -040096 ****************************************************************/
97
Kevin O'Connora7fc5de2009-12-13 11:48:18 -050098static inline void sgdt(struct descloc_s *desc) {
99 asm("sgdtl %0" : "=m"(*desc));
100}
101static inline void lgdt(struct descloc_s *desc) {
102 asm("lgdtl %0" : : "m"(*desc) : "memory");
103}
104
105// Call a 32bit SeaBIOS function from a 16bit SeaBIOS function.
Kevin O'Connor533b6282011-07-16 13:13:12 -0400106u32 VISIBLE16
Kevin O'Connoraf9629b2010-11-25 09:17:31 -0500107call32(void *func, u32 eax, u32 errret)
Kevin O'Connora7fc5de2009-12-13 11:48:18 -0500108{
109 ASSERT16();
110 u32 cr0 = getcr0();
111 if (cr0 & CR0_PE)
112 // Called in 16bit protected mode?!
Kevin O'Connoraf9629b2010-11-25 09:17:31 -0500113 return errret;
Kevin O'Connora7fc5de2009-12-13 11:48:18 -0500114
115 // Backup cmos index register and disable nmi
116 u8 cmosindex = inb(PORT_CMOS_INDEX);
117 outb(cmosindex | NMI_DISABLE_BIT, PORT_CMOS_INDEX);
118 inb(PORT_CMOS_DATA);
119
120 // Backup fs/gs and gdt
121 u16 fs = GET_SEG(FS), gs = GET_SEG(GS);
122 struct descloc_s gdt;
123 sgdt(&gdt);
124
Kevin O'Connora7fc5de2009-12-13 11:48:18 -0500125 u32 bkup_ss, bkup_esp;
126 asm volatile(
127 // Backup ss/esp / set esp to flat stack location
128 " movl %%ss, %0\n"
129 " movl %%esp, %1\n"
130 " shll $4, %0\n"
131 " addl %0, %%esp\n"
Kevin O'Connoraf9629b2010-11-25 09:17:31 -0500132 " shrl $4, %0\n"
Kevin O'Connora7fc5de2009-12-13 11:48:18 -0500133
Kevin O'Connorfb214dc2009-12-20 13:11:17 -0500134 // Transition to 32bit mode, call func, return to 16bit
Kevin O'Connor4057f982010-11-25 08:52:50 -0500135 " movl $(" __stringify(BUILD_BIOS_ADDR) " + 1f), %%edx\n"
Kevin O'Connora7fc5de2009-12-13 11:48:18 -0500136 " jmp transition32\n"
137 " .code32\n"
Kevin O'Connoraf9629b2010-11-25 09:17:31 -0500138 "1:calll *%3\n"
Kevin O'Connor4057f982010-11-25 08:52:50 -0500139 " movl $2f, %%edx\n"
Kevin O'Connora7fc5de2009-12-13 11:48:18 -0500140 " jmp transition16big\n"
141
142 // Restore ds/ss/esp
143 " .code16gcc\n"
144 "2:movl %0, %%ds\n"
145 " movl %0, %%ss\n"
146 " movl %1, %%esp\n"
Kevin O'Connoraf9629b2010-11-25 09:17:31 -0500147 : "=&r" (bkup_ss), "=&r" (bkup_esp), "+a" (eax)
Kevin O'Connorbca07362010-03-20 20:41:38 -0400148 : "r" (func)
Kevin O'Connoraf9629b2010-11-25 09:17:31 -0500149 : "ecx", "edx", "cc", "memory");
Kevin O'Connora7fc5de2009-12-13 11:48:18 -0500150
151 // Restore gdt and fs/gs
152 lgdt(&gdt);
153 SET_SEG(FS, fs);
154 SET_SEG(GS, gs);
155
156 // Restore cmos index register
157 outb(cmosindex, PORT_CMOS_INDEX);
158 inb(PORT_CMOS_DATA);
Kevin O'Connoraf9629b2010-11-25 09:17:31 -0500159 return eax;
Kevin O'Connora7fc5de2009-12-13 11:48:18 -0500160}
161
Kevin O'Connor2f898d52012-05-28 10:56:20 -0400162// Call a 16bit SeaBIOS function from a 32bit SeaBIOS function.
163static inline u32
164call16(u32 eax, void *func)
Kevin O'Connore77c7052012-05-28 22:06:42 -0400165{
166 ASSERT32FLAT();
167 if (getesp() > BUILD_STACK_ADDR)
168 panic("call16 with invalid stack\n");
169 asm volatile(
Kevin O'Connor2f898d52012-05-28 10:56:20 -0400170 "calll __call16"
171 : "+a" (eax)
172 : "b" ((u32)func - BUILD_BIOS_ADDR)
173 : "ecx", "edx", "cc", "memory");
174 return eax;
175}
176
177static inline u32
178call16big(u32 eax, void *func)
179{
180 ASSERT32FLAT();
181 if (getesp() > BUILD_STACK_ADDR)
182 panic("call16big with invalid stack\n");
183 asm volatile(
184 "calll __call16big"
185 : "+a" (eax)
186 : "b" ((u32)func - BUILD_BIOS_ADDR)
187 : "ecx", "edx", "cc", "memory");
188 return eax;
189}
190
191// Far call 16bit code with a specified register state.
192void VISIBLE16
193_farcall16(struct bregs *callregs)
194{
195 ASSERT16();
196 asm volatile(
197 "calll __farcall16\n"
Kevin O'Connore77c7052012-05-28 22:06:42 -0400198 : "+a" (callregs), "+m" (*callregs)
Kevin O'Connorbeeabd62012-05-28 13:59:07 -0400199 : "m" (__segment_ES)
Kevin O'Connore77c7052012-05-28 22:06:42 -0400200 : "ebx", "ecx", "edx", "esi", "edi", "cc", "memory");
201}
202
203inline void
Kevin O'Connor2f898d52012-05-28 10:56:20 -0400204farcall16(struct bregs *callregs)
205{
206 if (MODE16) {
Kevin O'Connorbeeabd62012-05-28 13:59:07 -0400207 SET_SEG(ES, GET_SEG(SS));
208 stack_hop_back((u32)callregs, 0, _farcall16);
Kevin O'Connor2f898d52012-05-28 10:56:20 -0400209 return;
210 }
211 extern void _cfunc16__farcall16(void);
212 call16((u32)callregs, _cfunc16__farcall16);
213}
214
215inline void
216farcall16big(struct bregs *callregs)
217{
218 extern void _cfunc16__farcall16(void);
219 call16big((u32)callregs, _cfunc16__farcall16);
220}
221
222// Invoke a 16bit software interrupt.
223inline void
Kevin O'Connore77c7052012-05-28 22:06:42 -0400224__call16_int(struct bregs *callregs, u16 offset)
225{
226 if (MODESEGMENT)
227 callregs->code.seg = GET_SEG(CS);
228 else
229 callregs->code.seg = SEG_BIOS;
230 callregs->code.offset = offset;
231 farcall16(callregs);
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500232}
233
234
235/****************************************************************
236 * Threads
237 ****************************************************************/
238
Kevin O'Connore77c7052012-05-28 22:06:42 -0400239// Thread info - stored at bottom of each thread stack - don't change
240// without also updating the inline assembler below.
241struct thread_info {
242 struct thread_info *next;
243 void *stackpos;
244 struct thread_info **pprev;
245};
246struct thread_info VAR32FLATVISIBLE MainThread = {
247 &MainThread, NULL, &MainThread.next
248};
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500249#define THREADSTACKSIZE 4096
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500250
Kevin O'Connorad901592009-12-13 11:25:25 -0500251// Return the 'struct thread_info' for the currently running thread.
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500252struct thread_info *
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500253getCurThread(void)
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500254{
255 u32 esp = getesp();
256 if (esp <= BUILD_STACK_ADDR)
257 return &MainThread;
258 return (void*)ALIGN_DOWN(esp, THREADSTACKSIZE);
259}
260
Kevin O'Connorad901592009-12-13 11:25:25 -0500261// Switch to next thread stack.
262static void
263switch_next(struct thread_info *cur)
264{
265 struct thread_info *next = cur->next;
Kevin O'Connorfb214dc2009-12-20 13:11:17 -0500266 if (cur == next)
267 // Nothing to do.
268 return;
Kevin O'Connorad901592009-12-13 11:25:25 -0500269 asm volatile(
270 " pushl $1f\n" // store return pc
271 " pushl %%ebp\n" // backup %ebp
272 " movl %%esp, 4(%%eax)\n" // cur->stackpos = %esp
273 " movl 4(%%ecx), %%esp\n" // %esp = next->stackpos
274 " popl %%ebp\n" // restore %ebp
275 " retl\n" // restore pc
276 "1:\n"
277 : "+a"(cur), "+c"(next)
278 :
279 : "ebx", "edx", "esi", "edi", "cc", "memory");
280}
281
Kevin O'Connor2f898d52012-05-28 10:56:20 -0400282// Low-level irq enable.
283void VISIBLE16
Kevin O'Connore77c7052012-05-28 22:06:42 -0400284check_irqs(void)
285{
Kevin O'Connor2f898d52012-05-28 10:56:20 -0400286 asm volatile("sti ; nop ; rep ; nop ; cli ; cld" : : :"memory");
Kevin O'Connore77c7052012-05-28 22:06:42 -0400287}
288
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500289// Briefly permit irqs to occur.
290void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500291yield(void)
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500292{
Kevin O'Connor2f898d52012-05-28 10:56:20 -0400293 if (MODESEGMENT) {
Kevin O'Connorbf2e8c22012-05-28 12:59:58 -0400294 stack_hop_back(0, 0, check_irqs);
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500295 return;
296 }
Kevin O'Connor2f898d52012-05-28 10:56:20 -0400297 extern void _cfunc16_check_irqs(void);
298 if (!CONFIG_THREADS) {
299 call16big(0, _cfunc16_check_irqs);
300 return;
301 }
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500302 struct thread_info *cur = getCurThread();
303 if (cur == &MainThread)
304 // Permit irqs to fire
Kevin O'Connor2f898d52012-05-28 10:56:20 -0400305 call16big(0, _cfunc16_check_irqs);
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500306
307 // Switch to the next thread
Kevin O'Connorad901592009-12-13 11:25:25 -0500308 switch_next(cur);
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500309}
310
Kevin O'Connor2f898d52012-05-28 10:56:20 -0400311void VISIBLE16
312wait_irq(void)
313{
314 asm volatile("sti ; hlt ; cli ; cld": : :"memory");
315}
Kevin O'Connore77c7052012-05-28 22:06:42 -0400316
317// Wait for next irq to occur.
318void
319yield_toirq(void)
320{
321 if (MODESEGMENT) {
Kevin O'Connorbf2e8c22012-05-28 12:59:58 -0400322 stack_hop_back(0, 0, wait_irq);
Kevin O'Connore77c7052012-05-28 22:06:42 -0400323 return;
324 }
325 if (CONFIG_THREADS && MainThread.next != &MainThread) {
326 // Threads still active - do a yield instead.
327 yield();
328 return;
329 }
Kevin O'Connor2f898d52012-05-28 10:56:20 -0400330 extern void _cfunc16_wait_irq(void);
331 call16big(0, _cfunc16_wait_irq);
Kevin O'Connore77c7052012-05-28 22:06:42 -0400332}
333
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500334// Last thing called from a thread (called on "next" stack).
335static void
336__end_thread(struct thread_info *old)
337{
Kevin O'Connora7eb8fc2010-04-02 13:13:23 -0400338 old->next->pprev = old->pprev;
339 *old->pprev = old->next;
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500340 free(old);
341 dprintf(DEBUG_thread, "\\%08x/ End thread\n", (u32)old);
Kevin O'Connore438b0c2010-05-01 12:20:33 -0400342 if (MainThread.next == &MainThread)
343 dprintf(1, "All threads complete.\n");
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500344}
345
Kevin O'Connorad901592009-12-13 11:25:25 -0500346// Create a new thread and start executing 'func' in it.
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500347void
348run_thread(void (*func)(void*), void *data)
349{
Kevin O'Connor52a300f2009-12-26 23:32:57 -0500350 ASSERT32FLAT();
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500351 if (! CONFIG_THREADS)
352 goto fail;
353 struct thread_info *thread;
354 thread = memalign_tmphigh(THREADSTACKSIZE, THREADSTACKSIZE);
355 if (!thread)
356 goto fail;
357
358 thread->stackpos = (void*)thread + THREADSTACKSIZE;
359 struct thread_info *cur = getCurThread();
Kevin O'Connora7eb8fc2010-04-02 13:13:23 -0400360 thread->next = cur;
361 thread->pprev = cur->pprev;
362 cur->pprev = &thread->next;
363 *thread->pprev = thread;
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500364
365 dprintf(DEBUG_thread, "/%08x\\ Start thread\n", (u32)thread);
366 asm volatile(
367 // Start thread
368 " pushl $1f\n" // store return pc
369 " pushl %%ebp\n" // backup %ebp
370 " movl %%esp, 4(%%edx)\n" // cur->stackpos = %esp
371 " movl 4(%%ebx), %%esp\n" // %esp = thread->stackpos
372 " calll *%%ecx\n" // Call func
373
374 // End thread
375 " movl (%%ebx), %%ecx\n" // %ecx = thread->next
376 " movl 4(%%ecx), %%esp\n" // %esp = next->stackpos
377 " movl %%ebx, %%eax\n"
378 " calll %4\n" // call __end_thread(thread)
379 " popl %%ebp\n" // restore %ebp
380 " retl\n" // restore pc
381 "1:\n"
382 : "+a"(data), "+c"(func), "+b"(thread), "+d"(cur)
383 : "m"(*(u8*)__end_thread)
384 : "esi", "edi", "cc", "memory");
385 return;
386
387fail:
388 func(data);
389}
390
Kevin O'Connorad901592009-12-13 11:25:25 -0500391// Wait for all threads (other than the main thread) to complete.
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500392void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500393wait_threads(void)
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500394{
Kevin O'Connor52a300f2009-12-26 23:32:57 -0500395 ASSERT32FLAT();
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500396 if (! CONFIG_THREADS)
397 return;
398 while (MainThread.next != &MainThread)
399 yield();
400}
Kevin O'Connorad901592009-12-13 11:25:25 -0500401
Kevin O'Connore9086652010-02-27 13:49:47 -0500402void
403mutex_lock(struct mutex_s *mutex)
404{
405 ASSERT32FLAT();
406 if (! CONFIG_THREADS)
407 return;
408 while (mutex->isLocked)
409 yield();
410 mutex->isLocked = 1;
411}
412
413void
414mutex_unlock(struct mutex_s *mutex)
415{
416 ASSERT32FLAT();
417 if (! CONFIG_THREADS)
418 return;
419 mutex->isLocked = 0;
420}
421
Kevin O'Connorad901592009-12-13 11:25:25 -0500422
423/****************************************************************
424 * Thread preemption
425 ****************************************************************/
426
Kevin O'Connore77c7052012-05-28 22:06:42 -0400427int VAR16VISIBLE CanPreempt;
Kevin O'Connorad901592009-12-13 11:25:25 -0500428static u32 PreemptCount;
429
430// Turn on RTC irqs and arrange for them to check the 32bit threads.
431void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500432start_preempt(void)
Kevin O'Connorad901592009-12-13 11:25:25 -0500433{
Kevin O'Connorb5c7da62013-02-08 00:45:44 -0500434 if (! CONFIG_THREAD_OPTIONROMS)
Kevin O'Connorad901592009-12-13 11:25:25 -0500435 return;
436 CanPreempt = 1;
437 PreemptCount = 0;
438 useRTC();
439}
440
441// Turn off RTC irqs / stop checking for thread execution.
442void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500443finish_preempt(void)
Kevin O'Connorad901592009-12-13 11:25:25 -0500444{
Kevin O'Connorb5c7da62013-02-08 00:45:44 -0500445 if (! CONFIG_THREAD_OPTIONROMS) {
Kevin O'Connora7eb8fc2010-04-02 13:13:23 -0400446 yield();
Kevin O'Connorad901592009-12-13 11:25:25 -0500447 return;
Kevin O'Connora7eb8fc2010-04-02 13:13:23 -0400448 }
Kevin O'Connorad901592009-12-13 11:25:25 -0500449 CanPreempt = 0;
450 releaseRTC();
Kevin O'Connore438b0c2010-05-01 12:20:33 -0400451 dprintf(9, "Done preempt - %d checks\n", PreemptCount);
Kevin O'Connora7eb8fc2010-04-02 13:13:23 -0400452 yield();
Kevin O'Connorad901592009-12-13 11:25:25 -0500453}
454
Kevin O'Connord7eb27e2010-03-20 18:17:19 -0400455// Check if preemption is on, and wait for it to complete if so.
456int
457wait_preempt(void)
458{
Kevin O'Connorb5c7da62013-02-08 00:45:44 -0500459 if (MODESEGMENT || !CONFIG_THREAD_OPTIONROMS || !CanPreempt)
Kevin O'Connord7eb27e2010-03-20 18:17:19 -0400460 return 0;
461 while (CanPreempt)
462 yield();
463 return 1;
464}
465
Kevin O'Connorad901592009-12-13 11:25:25 -0500466// Try to execute 32bit threads.
Kevin O'Connord1b4f962010-09-15 21:38:16 -0400467void VISIBLE32INIT
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500468yield_preempt(void)
Kevin O'Connorad901592009-12-13 11:25:25 -0500469{
470 PreemptCount++;
471 switch_next(&MainThread);
472}
Kevin O'Connorad901592009-12-13 11:25:25 -0500473
474// 16bit code that checks if threads are pending and executes them if so.
475void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500476check_preempt(void)
Kevin O'Connorad901592009-12-13 11:25:25 -0500477{
Kevin O'Connorb5c7da62013-02-08 00:45:44 -0500478 if (! CONFIG_THREAD_OPTIONROMS || !GET_GLOBAL(CanPreempt)
Kevin O'Connor4d96edc2010-09-25 14:53:15 -0400479 || GET_FLATPTR(MainThread.next) == &MainThread)
Kevin O'Connorad901592009-12-13 11:25:25 -0500480 return;
Kevin O'Connorad901592009-12-13 11:25:25 -0500481
Kevin O'Connorf3fe3aa2010-12-05 12:38:33 -0500482 extern void _cfunc32flat_yield_preempt(void);
483 call32(_cfunc32flat_yield_preempt, 0, 0);
Kevin O'Connorad901592009-12-13 11:25:25 -0500484}