These changes are the raw update to qemu-2.6.
[kvmfornfv.git] / qemu / roms / seabios / src / stacks.c
1 // Code for manipulating stack locations.
2 //
3 // Copyright (C) 2009-2015  Kevin O'Connor <kevin@koconnor.net>
4 //
5 // This file may be distributed under the terms of the GNU LGPLv3 license.
6
7 #include "biosvar.h" // GET_GLOBAL
8 #include "bregs.h" // CR0_PE
9 #include "fw/paravirt.h" // PORT_SMI_CMD
10 #include "hw/rtc.h" // rtc_use
11 #include "list.h" // hlist_node
12 #include "malloc.h" // free
13 #include "output.h" // dprintf
14 #include "romfile.h" // romfile_loadint
15 #include "stacks.h" // struct mutex_s
16 #include "string.h" // memset
17 #include "util.h" // useRTC
18
19 #define MAIN_STACK_MAX (1024*1024)
20
21
22 /****************************************************************
23  * 16bit / 32bit calling
24  ****************************************************************/
25
26 struct {
27     u8 method;
28     u8 cmosindex;
29     u8 a20;
30     u16 ss, fs, gs;
31     u32 cr0;
32     struct descloc_s gdt;
33 } Call16Data VARLOW;
34
35 #define C16_BIG 1
36 #define C16_SMM 2
37
38 int HaveSmmCall32 VARFSEG;
39
40 // Backup state in preparation for call32
41 static int
42 call32_prep(u8 method)
43 {
44     if (!CONFIG_CALL32_SMM || method != C16_SMM) {
45         // Backup cr0
46         u32 cr0 = cr0_read();
47         if (cr0 & CR0_PE)
48             // Called in 16bit protected mode?!
49             return -1;
50         SET_LOW(Call16Data.cr0, cr0);
51
52         // Backup fs/gs and gdt
53         SET_LOW(Call16Data.fs, GET_SEG(FS));
54         SET_LOW(Call16Data.gs, GET_SEG(GS));
55         struct descloc_s gdt;
56         sgdt(&gdt);
57         SET_LOW(Call16Data.gdt.length, gdt.length);
58         SET_LOW(Call16Data.gdt.addr, gdt.addr);
59
60         // Enable a20 and backup its previous state
61         SET_LOW(Call16Data.a20, set_a20(1));
62     }
63
64     // Backup ss
65     SET_LOW(Call16Data.ss, GET_SEG(SS));
66
67     // Backup cmos index register and disable nmi
68     u8 cmosindex = inb(PORT_CMOS_INDEX);
69     outb(cmosindex | NMI_DISABLE_BIT, PORT_CMOS_INDEX);
70     inb(PORT_CMOS_DATA);
71     SET_LOW(Call16Data.cmosindex, cmosindex);
72
73     SET_LOW(Call16Data.method, method);
74     return 0;
75 }
76
77 // Restore state backed up during call32
78 static u8
79 call32_post(void)
80 {
81     u8 method = GET_LOW(Call16Data.method);
82     SET_LOW(Call16Data.method, 0);
83     SET_LOW(Call16Data.ss, 0);
84
85     if (!CONFIG_CALL32_SMM || method != C16_SMM) {
86         // Restore a20
87         set_a20(GET_LOW(Call16Data.a20));
88
89         // Restore gdt and fs/gs
90         struct descloc_s gdt;
91         gdt.length = GET_LOW(Call16Data.gdt.length);
92         gdt.addr = GET_LOW(Call16Data.gdt.addr);
93         lgdt(&gdt);
94         SET_SEG(FS, GET_LOW(Call16Data.fs));
95         SET_SEG(GS, GET_LOW(Call16Data.gs));
96
97         // Restore cr0
98         u32 cr0_caching = GET_LOW(Call16Data.cr0) & (CR0_CD|CR0_NW);
99         if (cr0_caching)
100             cr0_mask(CR0_CD|CR0_NW, cr0_caching);
101     }
102
103     // Restore cmos index register
104     outb(GET_LOW(Call16Data.cmosindex), PORT_CMOS_INDEX);
105     inb(PORT_CMOS_DATA);
106     return method;
107 }
108
109 // Force next call16() to restore to a pristine cpu environment state
110 static void
111 call16_override(int big)
112 {
113     ASSERT32FLAT();
114     if (getesp() > BUILD_STACK_ADDR)
115         panic("call16_override with invalid stack\n");
116     memset(&Call16Data, 0, sizeof(Call16Data));
117     if (big) {
118         Call16Data.method = C16_BIG;
119         Call16Data.a20 = 1;
120     } else {
121         Call16Data.a20 = !CONFIG_DISABLE_A20;
122     }
123 }
124
125 // 16bit handler code called from call16() / call16_smm()
126 u32 VISIBLE16
127 call16_helper(u32 eax, u32 edx, u32 (*func)(u32 eax, u32 edx))
128 {
129     u8 method = call32_post();
130     u32 ret = func(eax, edx);
131     call32_prep(method);
132     return ret;
133 }
134
135 #define ASM32_SWITCH16 "  .pushsection .text.32fseg." UNIQSEC "\n  .code16\n"
136 #define ASM32_BACK32   "  .popsection\n  .code32\n"
137 #define ASM16_SWITCH32 "  .code32\n"
138 #define ASM16_BACK16   "  .code16gcc\n"
139
140 // Call a SeaBIOS C function in 32bit mode using smm trampoline
141 static u32
142 call32_smm(void *func, u32 eax)
143 {
144     ASSERT16();
145     dprintf(9, "call32_smm %p %x\n", func, eax);
146     call32_prep(C16_SMM);
147     u32 bkup_esp;
148     asm volatile(
149         // Backup esp / set esp to flat stack location
150         "  movl %%esp, %0\n"
151         "  movl %%ss, %%eax\n"
152         "  shll $4, %%eax\n"
153         "  addl %%eax, %%esp\n"
154
155         // Transition to 32bit mode, call func, return to 16bit
156         "  movl $" __stringify(CALL32SMM_CMDID) ", %%eax\n"
157         "  movl $" __stringify(CALL32SMM_ENTERID) ", %%ecx\n"
158         "  movl $(" __stringify(BUILD_BIOS_ADDR) " + 1f), %%ebx\n"
159         "  outb %%al, $" __stringify(PORT_SMI_CMD) "\n"
160         "  rep; nop\n"
161         "  hlt\n"
162
163         ASM16_SWITCH32
164         "1:movl %1, %%eax\n"
165         "  calll *%2\n"
166         "  movl %%eax, %1\n"
167
168         "  movl $" __stringify(CALL32SMM_CMDID) ", %%eax\n"
169         "  movl $" __stringify(CALL32SMM_RETURNID) ", %%ecx\n"
170         "  movl $2f, %%ebx\n"
171         "  outb %%al, $" __stringify(PORT_SMI_CMD) "\n"
172         "  rep; nop\n"
173         "  hlt\n"
174
175         // Restore esp
176         ASM16_BACK16
177         "2:movl %0, %%esp\n"
178         : "=&r" (bkup_esp), "+r" (eax)
179         : "r" (func)
180         : "eax", "ecx", "edx", "ebx", "cc", "memory");
181     call32_post();
182
183     dprintf(9, "call32_smm done %p %x\n", func, eax);
184     return eax;
185 }
186
187 static u32
188 call16_smm(u32 eax, u32 edx, void *func)
189 {
190     ASSERT32FLAT();
191     if (!CONFIG_CALL32_SMM)
192         return eax;
193     func -= BUILD_BIOS_ADDR;
194     dprintf(9, "call16_smm %p %x %x\n", func, eax, edx);
195     u32 stackoffset = Call16Data.ss << 4;
196     asm volatile(
197         // Restore esp
198         "  subl %0, %%esp\n"
199
200         // Transition to 16bit mode, call func, return to 32bit
201         "  movl $" __stringify(CALL32SMM_CMDID) ", %%eax\n"
202         "  movl $" __stringify(CALL32SMM_RETURNID) ", %%ecx\n"
203         "  movl $(1f - " __stringify(BUILD_BIOS_ADDR) "), %%ebx\n"
204         "  outb %%al, $" __stringify(PORT_SMI_CMD) "\n"
205         "  rep; nop\n"
206         "  hlt\n"
207
208         ASM32_SWITCH16
209         "1:movl %1, %%eax\n"
210         "  movl %3, %%ecx\n"
211         "  calll _cfunc16_call16_helper\n"
212         "  movl %%eax, %1\n"
213
214         "  movl $" __stringify(CALL32SMM_CMDID) ", %%eax\n"
215         "  movl $" __stringify(CALL32SMM_ENTERID) ", %%ecx\n"
216         "  movl $2f, %%ebx\n"
217         "  outb %%al, $" __stringify(PORT_SMI_CMD) "\n"
218         "  rep; nop\n"
219         "  hlt\n"
220
221         // Set esp to flat stack location
222         ASM32_BACK32
223         "2:addl %0, %%esp\n"
224         : "+r" (stackoffset), "+r" (eax), "+d" (edx)
225         : "r" (func)
226         : "eax", "ecx", "ebx", "cc", "memory");
227     return eax;
228 }
229
230 // Call a 32bit SeaBIOS function from a 16bit SeaBIOS function.
231 u32 VISIBLE16
232 __call32(void *func, u32 eax, u32 errret)
233 {
234     ASSERT16();
235     if (CONFIG_CALL32_SMM && GET_GLOBAL(HaveSmmCall32))
236         return call32_smm(func, eax);
237     // Jump direclty to 32bit mode - this clobbers the 16bit segment
238     // selector registers.
239     int ret = call32_prep(C16_BIG);
240     if (ret)
241         return errret;
242     u32 bkup_ss, bkup_esp;
243     asm volatile(
244         // Backup ss/esp / set esp to flat stack location
245         "  movl %%ss, %0\n"
246         "  movl %%esp, %1\n"
247         "  shll $4, %0\n"
248         "  addl %0, %%esp\n"
249         "  shrl $4, %0\n"
250
251         // Transition to 32bit mode, call func, return to 16bit
252         "  movl $(" __stringify(BUILD_BIOS_ADDR) " + 1f), %%edx\n"
253         "  jmp transition32_nmi_off\n"
254         ASM16_SWITCH32
255         "1:calll *%3\n"
256         "  movl $2f, %%edx\n"
257         "  jmp transition16big\n"
258
259         // Restore ds/ss/esp
260         ASM16_BACK16
261         "2:movl %0, %%ds\n"
262         "  movl %0, %%ss\n"
263         "  movl %1, %%esp\n"
264         : "=&r" (bkup_ss), "=&r" (bkup_esp), "+a" (eax)
265         : "r" (func)
266         : "ecx", "edx", "cc", "memory");
267     call32_post();
268     return eax;
269 }
270
271 // Call a 16bit SeaBIOS function, restoring the mode from last call32().
272 static u32
273 call16(u32 eax, u32 edx, void *func)
274 {
275     ASSERT32FLAT();
276     if (getesp() > MAIN_STACK_MAX)
277         panic("call16 with invalid stack\n");
278     if (CONFIG_CALL32_SMM && Call16Data.method == C16_SMM)
279         return call16_smm(eax, edx, func);
280
281     extern void transition16big(void);
282     extern void transition16(void);
283     void *thunk = transition16;
284     if (Call16Data.method == C16_BIG || in_post())
285         thunk = transition16big;
286     func -= BUILD_BIOS_ADDR;
287     u32 stackseg = Call16Data.ss;
288     asm volatile(
289         // Transition to 16bit mode
290         "  movl $(1f - " __stringify(BUILD_BIOS_ADDR) "), %%edx\n"
291         "  jmp *%%ecx\n"
292         // Setup ss/esp and call func
293         ASM32_SWITCH16
294         "1:movl %2, %%ecx\n"
295         "  shll $4, %2\n"
296         "  movw %%cx, %%ss\n"
297         "  subl %2, %%esp\n"
298         "  movw %%cx, %%ds\n"
299         "  movl %4, %%edx\n"
300         "  movl %3, %%ecx\n"
301         "  calll _cfunc16_call16_helper\n"
302         // Return to 32bit and restore esp
303         "  movl $2f, %%edx\n"
304         "  jmp transition32_nmi_off\n"
305         ASM32_BACK32
306         "2:addl %2, %%esp\n"
307         : "+a" (eax), "+c"(thunk), "+r"(stackseg)
308         : "r" (func), "r" (edx)
309         : "edx", "cc", "memory");
310     return eax;
311 }
312
313
314 /****************************************************************
315  * Extra 16bit stack
316  ****************************************************************/
317
318 // Space for a stack for 16bit code.
319 u8 ExtraStack[BUILD_EXTRA_STACK_SIZE+1] VARLOW __aligned(8);
320 u8 *StackPos VARLOW;
321
322 // Test if currently on the extra stack
323 int
324 on_extra_stack(void)
325 {
326     return MODE16 && GET_SEG(SS) == SEG_LOW && getesp() > (u32)ExtraStack;
327 }
328
329 // Switch to the extra stack and call a function.
330 u32
331 __stack_hop(u32 eax, u32 edx, void *func)
332 {
333     if (on_extra_stack())
334         return ((u32 (*)(u32, u32))func)(eax, edx);
335     ASSERT16();
336     u16 stack_seg = SEG_LOW;
337     u32 bkup_ss, bkup_esp;
338     asm volatile(
339         // Backup current %ss/%esp values.
340         "movw %%ss, %w3\n"
341         "movl %%esp, %4\n"
342         // Copy stack seg to %ds/%ss and set %esp
343         "movw %w6, %%ds\n"
344         "movw %w6, %%ss\n"
345         "movl %5, %%esp\n"
346         "pushl %3\n"
347         "pushl %4\n"
348         // Call func
349         "calll *%2\n"
350         "popl %4\n"
351         "popl %3\n"
352         // Restore segments and stack
353         "movw %w3, %%ds\n"
354         "movw %w3, %%ss\n"
355         "movl %4, %%esp"
356         : "+a" (eax), "+d" (edx), "+c" (func), "=&r" (bkup_ss), "=&r" (bkup_esp)
357         : "m" (StackPos), "r" (stack_seg)
358         : "cc", "memory");
359     return eax;
360 }
361
362 // Switch back to original caller's stack and call a function.
363 u32
364 __stack_hop_back(u32 eax, u32 edx, void *func)
365 {
366     if (!MODESEGMENT)
367         return call16(eax, edx, func);
368     if (!MODE16 || !on_extra_stack())
369         return ((u32 (*)(u32, u32))func)(eax, edx);
370     ASSERT16();
371     u16 bkup_ss;
372     u32 bkup_stack_pos, temp;
373     asm volatile(
374         // Backup stack_pos and current %ss/%esp
375         "movl %6, %4\n"
376         "movw %%ss, %w3\n"
377         "movl %%esp, %6\n"
378         // Restore original callers' %ss/%esp
379         "movl -4(%4), %5\n"
380         "movl %5, %%ss\n"
381         "movw %%ds:-8(%4), %%sp\n"
382         "movl %5, %%ds\n"
383         // Call func
384         "calll *%2\n"
385         // Restore %ss/%esp and stack_pos
386         "movw %w3, %%ds\n"
387         "movw %w3, %%ss\n"
388         "movl %6, %%esp\n"
389         "movl %4, %6"
390         : "+a" (eax), "+d" (edx), "+c" (func), "=&r" (bkup_ss)
391           , "=&r" (bkup_stack_pos), "=&r" (temp), "+m" (StackPos)
392         :
393         : "cc", "memory");
394     return eax;
395 }
396
397
398 /****************************************************************
399  * External 16bit interface calling
400  ****************************************************************/
401
402 // Far call 16bit code with a specified register state.
403 void VISIBLE16
404 _farcall16(struct bregs *callregs, u16 callregseg)
405 {
406     if (need_hop_back()) {
407         stack_hop_back(_farcall16, callregs, callregseg);
408         return;
409     }
410     ASSERT16();
411     asm volatile(
412         "calll __farcall16\n"
413         : "+a" (callregs), "+m" (*callregs), "+d" (callregseg)
414         :
415         : "ebx", "ecx", "esi", "edi", "cc", "memory");
416 }
417
418 // Invoke external 16bit code.
419 void
420 farcall16(struct bregs *callregs)
421 {
422     call16_override(0);
423     _farcall16(callregs, 0);
424 }
425
426 // Invoke external 16bit code in "big real" mode.
427 void
428 farcall16big(struct bregs *callregs)
429 {
430     call16_override(1);
431     _farcall16(callregs, 0);
432 }
433
434 // Invoke a 16bit software interrupt.
435 void
436 __call16_int(struct bregs *callregs, u16 offset)
437 {
438     callregs->code.offset = offset;
439     if (!MODESEGMENT) {
440         callregs->code.seg = SEG_BIOS;
441         _farcall16((void*)callregs - Call16Data.ss * 16, Call16Data.ss);
442         return;
443     }
444     callregs->code.seg = GET_SEG(CS);
445     _farcall16(callregs, GET_SEG(SS));
446 }
447
448 // Reset the machine
449 void
450 reset(void)
451 {
452     extern void reset_vector(void) __noreturn;
453     if (!MODE16)
454         call16(0, 0, reset_vector);
455     reset_vector();
456 }
457
458
459 /****************************************************************
460  * Threads
461  ****************************************************************/
462
463 // Thread info - stored at bottom of each thread stack - don't change
464 // without also updating the inline assembler below.
465 struct thread_info {
466     void *stackpos;
467     struct hlist_node node;
468 };
469 struct thread_info MainThread VARFSEG = {
470     NULL, { &MainThread.node, &MainThread.node.next }
471 };
472 #define THREADSTACKSIZE 4096
473
474 // Check if any threads are running.
475 static int
476 have_threads(void)
477 {
478     return (CONFIG_THREADS
479             && GET_FLATPTR(MainThread.node.next) != &MainThread.node);
480 }
481
482 // Return the 'struct thread_info' for the currently running thread.
483 struct thread_info *
484 getCurThread(void)
485 {
486     u32 esp = getesp();
487     if (esp <= MAIN_STACK_MAX)
488         return &MainThread;
489     return (void*)ALIGN_DOWN(esp, THREADSTACKSIZE);
490 }
491
492 static u8 CanInterrupt, ThreadControl;
493
494 // Initialize the support for internal threads.
495 void
496 thread_setup(void)
497 {
498     CanInterrupt = 1;
499     if (! CONFIG_THREADS)
500         return;
501     ThreadControl = romfile_loadint("etc/threads", 1);
502 }
503
504 // Should hardware initialization threads run during optionrom execution.
505 int
506 threads_during_optionroms(void)
507 {
508     return CONFIG_THREADS && CONFIG_RTC_TIMER && ThreadControl == 2 && in_post();
509 }
510
511 // Switch to next thread stack.
512 static void
513 switch_next(struct thread_info *cur)
514 {
515     struct thread_info *next = container_of(
516         cur->node.next, struct thread_info, node);
517     if (cur == next)
518         // Nothing to do.
519         return;
520     asm volatile(
521         "  pushl $1f\n"                 // store return pc
522         "  pushl %%ebp\n"               // backup %ebp
523         "  movl %%esp, (%%eax)\n"       // cur->stackpos = %esp
524         "  movl (%%ecx), %%esp\n"       // %esp = next->stackpos
525         "  popl %%ebp\n"                // restore %ebp
526         "  retl\n"                      // restore pc
527         "1:\n"
528         : "+a"(cur), "+c"(next)
529         :
530         : "ebx", "edx", "esi", "edi", "cc", "memory");
531 }
532
533 // Last thing called from a thread (called on MainThread stack).
534 static void
535 __end_thread(struct thread_info *old)
536 {
537     hlist_del(&old->node);
538     dprintf(DEBUG_thread, "\\%08x/ End thread\n", (u32)old);
539     free(old);
540     if (!have_threads())
541         dprintf(1, "All threads complete.\n");
542 }
543
544 // Create a new thread and start executing 'func' in it.
545 void
546 run_thread(void (*func)(void*), void *data)
547 {
548     ASSERT32FLAT();
549     if (! CONFIG_THREADS || ! ThreadControl)
550         goto fail;
551     struct thread_info *thread;
552     thread = memalign_tmphigh(THREADSTACKSIZE, THREADSTACKSIZE);
553     if (!thread)
554         goto fail;
555
556     dprintf(DEBUG_thread, "/%08x\\ Start thread\n", (u32)thread);
557     thread->stackpos = (void*)thread + THREADSTACKSIZE;
558     struct thread_info *cur = getCurThread();
559     hlist_add_after(&thread->node, &cur->node);
560     asm volatile(
561         // Start thread
562         "  pushl $1f\n"                 // store return pc
563         "  pushl %%ebp\n"               // backup %ebp
564         "  movl %%esp, (%%edx)\n"       // cur->stackpos = %esp
565         "  movl (%%ebx), %%esp\n"       // %esp = thread->stackpos
566         "  calll *%%ecx\n"              // Call func
567
568         // End thread
569         "  movl %%ebx, %%eax\n"         // %eax = thread
570         "  movl 4(%%ebx), %%ebx\n"      // %ebx = thread->node.next
571         "  movl (%5), %%esp\n"          // %esp = MainThread.stackpos
572         "  calll %4\n"                  // call __end_thread(thread)
573         "  movl -4(%%ebx), %%esp\n"     // %esp = next->stackpos
574         "  popl %%ebp\n"                // restore %ebp
575         "  retl\n"                      // restore pc
576         "1:\n"
577         : "+a"(data), "+c"(func), "+b"(thread), "+d"(cur)
578         : "m"(*(u8*)__end_thread), "m"(MainThread)
579         : "esi", "edi", "cc", "memory");
580     return;
581
582 fail:
583     func(data);
584 }
585
586
587 /****************************************************************
588  * Thread helpers
589  ****************************************************************/
590
591 // Low-level irq enable.
592 void VISIBLE16
593 check_irqs(void)
594 {
595     if (!MODESEGMENT && !CanInterrupt) {
596         // Can't enable interrupts (PIC and/or IVT not yet setup)
597         cpu_relax();
598         return;
599     }
600     if (need_hop_back()) {
601         stack_hop_back(check_irqs, 0, 0);
602         return;
603     }
604     if (MODE16)
605         clock_poll_irq();
606     asm volatile("sti ; nop ; rep ; nop ; cli ; cld" : : :"memory");
607 }
608
609 // Briefly permit irqs to occur.
610 void
611 yield(void)
612 {
613     if (MODESEGMENT || !CONFIG_THREADS) {
614         check_irqs();
615         return;
616     }
617     struct thread_info *cur = getCurThread();
618     if (cur == &MainThread)
619         // Permit irqs to fire
620         check_irqs();
621
622     // Switch to the next thread
623     switch_next(cur);
624 }
625
626 void VISIBLE16
627 wait_irq(void)
628 {
629     if (need_hop_back()) {
630         stack_hop_back(wait_irq, 0, 0);
631         return;
632     }
633     asm volatile("sti ; hlt ; cli ; cld": : :"memory");
634 }
635
636 // Wait for next irq to occur.
637 void
638 yield_toirq(void)
639 {
640     if (!CONFIG_HARDWARE_IRQ
641         || (!MODESEGMENT && (have_threads() || !CanInterrupt))) {
642         // Threads still active or irqs not available - do a yield instead.
643         yield();
644         return;
645     }
646     wait_irq();
647 }
648
649 // Wait for all threads (other than the main thread) to complete.
650 void
651 wait_threads(void)
652 {
653     ASSERT32FLAT();
654     while (have_threads())
655         yield();
656 }
657
658 void
659 mutex_lock(struct mutex_s *mutex)
660 {
661     ASSERT32FLAT();
662     if (! CONFIG_THREADS)
663         return;
664     while (mutex->isLocked)
665         yield();
666     mutex->isLocked = 1;
667 }
668
669 void
670 mutex_unlock(struct mutex_s *mutex)
671 {
672     ASSERT32FLAT();
673     if (! CONFIG_THREADS)
674         return;
675     mutex->isLocked = 0;
676 }
677
678
679 /****************************************************************
680  * Thread preemption
681  ****************************************************************/
682
683 int CanPreempt VARFSEG;
684 static u32 PreemptCount;
685
686 // Turn on RTC irqs and arrange for them to check the 32bit threads.
687 void
688 start_preempt(void)
689 {
690     if (! threads_during_optionroms())
691         return;
692     CanPreempt = 1;
693     PreemptCount = 0;
694     rtc_use();
695 }
696
697 // Turn off RTC irqs / stop checking for thread execution.
698 void
699 finish_preempt(void)
700 {
701     if (! threads_during_optionroms()) {
702         yield();
703         return;
704     }
705     CanPreempt = 0;
706     rtc_release();
707     dprintf(9, "Done preempt - %d checks\n", PreemptCount);
708     yield();
709 }
710
711 // Check if preemption is on, and wait for it to complete if so.
712 int
713 wait_preempt(void)
714 {
715     if (MODESEGMENT || !CONFIG_THREADS || !CanPreempt
716         || getesp() < MAIN_STACK_MAX)
717         return 0;
718     while (CanPreempt)
719         yield();
720     return 1;
721 }
722
723 // Try to execute 32bit threads.
724 void VISIBLE32INIT
725 yield_preempt(void)
726 {
727     PreemptCount++;
728     switch_next(&MainThread);
729 }
730
731 // 16bit code that checks if threads are pending and executes them if so.
732 void
733 check_preempt(void)
734 {
735     if (CONFIG_THREADS && GET_GLOBAL(CanPreempt) && have_threads())
736         call32(yield_preempt, 0, 0);
737 }
738
739
740 /****************************************************************
741  * call32 helper
742  ****************************************************************/
743
744 struct call32_params_s {
745     void *func;
746     u32 eax, edx, ecx;
747 };
748
749 u32 VISIBLE32FLAT
750 call32_params_helper(struct call32_params_s *params)
751 {
752     return ((u32 (*)(u32, u32, u32))params->func)(
753         params->eax, params->edx, params->ecx);
754 }
755
756 u32
757 __call32_params(void *func, u32 eax, u32 edx, u32 ecx, u32 errret)
758 {
759     ASSERT16();
760     struct call32_params_s params = {func, eax, edx, ecx};
761     return call32(call32_params_helper, MAKE_FLATPTR(GET_SEG(SS), &params)
762                   , errret);
763 }