1 // Code for manipulating stack locations.
3 // Copyright (C) 2009 Kevin O'Connor <kevin@koconnor.net>
5 // This file may be distributed under the terms of the GNU LGPLv3 license.
7 #include "biosvar.h" // get_ebda_seg
8 #include "util.h" // dprintf
9 #include "bregs.h" // CR0_PE
12 /****************************************************************
14 ****************************************************************/
16 // Switch to the extra stack in ebda and call a function.
18 stack_hop(u32 eax, u32 edx, u32 ecx, void *func)
21 u16 ebda_seg = get_ebda_seg(), bkup_ss;
24 // Backup current %ss/%esp values.
27 // Copy ebda seg to %ds/%ss and set %esp
33 // Restore segments and stack
37 : "+a" (eax), "+d" (edx), "+c" (ecx), "=&r" (bkup_ss), "=&r" (bkup_esp)
38 : "i" (EBDA_OFFSET_TOP_STACK), "r" (ebda_seg), "m" (*(u8*)func)
44 /****************************************************************
46 ****************************************************************/
48 #define THREADSTACKSIZE 4096
51 struct thread_info *next;
55 struct thread_info VAR16VISIBLE MainThread;
56 int VAR16VISIBLE CanPreempt;
61 MainThread.next = &MainThread;
62 MainThread.stackpos = NULL;
66 // Return the 'struct thread_info' for the currently running thread.
71 if (esp <= BUILD_STACK_ADDR)
73 return (void*)ALIGN_DOWN(esp, THREADSTACKSIZE);
76 // Switch to next thread stack.
78 switch_next(struct thread_info *cur)
80 struct thread_info *next = cur->next;
82 " pushl $1f\n" // store return pc
83 " pushl %%ebp\n" // backup %ebp
84 " movl %%esp, 4(%%eax)\n" // cur->stackpos = %esp
85 " movl 4(%%ecx), %%esp\n" // %esp = next->stackpos
86 " popl %%ebp\n" // restore %ebp
87 " retl\n" // restore pc
89 : "+a"(cur), "+c"(next)
91 : "ebx", "edx", "esi", "edi", "cc", "memory");
94 // Briefly permit irqs to occur.
98 if (MODE16 || !CONFIG_THREADS) {
99 // Just directly check irqs.
103 struct thread_info *cur = getCurThread();
104 if (cur == &MainThread)
105 // Permit irqs to fire
108 // Switch to the next thread
112 // Last thing called from a thread (called on "next" stack).
114 __end_thread(struct thread_info *old)
116 struct thread_info *pos = &MainThread;
117 while (pos->next != old)
119 pos->next = old->next;
121 dprintf(DEBUG_thread, "\\%08x/ End thread\n", (u32)old);
124 // Create a new thread and start executing 'func' in it.
126 run_thread(void (*func)(void*), void *data)
129 if (! CONFIG_THREADS)
131 struct thread_info *thread;
132 thread = memalign_tmphigh(THREADSTACKSIZE, THREADSTACKSIZE);
136 thread->stackpos = (void*)thread + THREADSTACKSIZE;
137 struct thread_info *cur = getCurThread();
138 thread->next = cur->next;
141 dprintf(DEBUG_thread, "/%08x\\ Start thread\n", (u32)thread);
144 " pushl $1f\n" // store return pc
145 " pushl %%ebp\n" // backup %ebp
146 " movl %%esp, 4(%%edx)\n" // cur->stackpos = %esp
147 " movl 4(%%ebx), %%esp\n" // %esp = thread->stackpos
148 " calll *%%ecx\n" // Call func
151 " movl (%%ebx), %%ecx\n" // %ecx = thread->next
152 " movl 4(%%ecx), %%esp\n" // %esp = next->stackpos
153 " movl %%ebx, %%eax\n"
154 " calll %4\n" // call __end_thread(thread)
155 " popl %%ebp\n" // restore %ebp
156 " retl\n" // restore pc
158 : "+a"(data), "+c"(func), "+b"(thread), "+d"(cur)
159 : "m"(*(u8*)__end_thread)
160 : "esi", "edi", "cc", "memory");
167 // Wait for all threads (other than the main thread) to complete.
172 if (! CONFIG_THREADS)
174 while (MainThread.next != &MainThread)
179 /****************************************************************
181 ****************************************************************/
183 static u32 PreemptCount;
185 // Turn on RTC irqs and arrange for them to check the 32bit threads.
189 if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS)
196 // Turn off RTC irqs / stop checking for thread execution.
200 if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS)
204 dprintf(1, "Done preempt - %d checks\n", PreemptCount);
207 static inline u32 getcr0() {
209 asm("movl %%cr0, %0" : "=r"(cr0));
212 static inline void sgdt(struct descloc_s *desc) {
213 asm("sgdtl %0" : "=m"(*desc));
215 static inline void lgdt(struct descloc_s *desc) {
216 asm("lgdtl %0" : : "m"(*desc) : "memory");
220 // Try to execute 32bit threads.
225 switch_next(&MainThread);
229 // 16bit code that checks if threads are pending and executes them if so.
234 if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS
235 || !GET_GLOBAL(CanPreempt)
236 || GET_GLOBAL(MainThread.next) == &MainThread)
240 // Called in 16bit protected mode?!
243 // Backup cmos index register and disable nmi
244 u8 cmosindex = inb(PORT_CMOS_INDEX);
245 outb(cmosindex | NMI_DISABLE_BIT, PORT_CMOS_INDEX);
248 // Backup fs/gs and gdt
249 u16 fs = GET_SEG(FS), gs = GET_SEG(GS);
250 struct descloc_s gdt;
253 u32 bkup_ss, bkup_esp;
255 // Backup ss/esp / set esp to flat stack location
262 // Transition to 32bit mode, call yield_preempt, return to 16bit
263 " pushl $(" __stringify(BUILD_BIOS_ADDR) " + 1f)\n"
264 " jmp transition32\n"
266 "1:calll (yield_preempt - " __stringify(BUILD_BIOS_ADDR) ")\n"
268 " jmp transition16big\n"
276 : "=&r" (bkup_ss), "=&r" (bkup_esp)
277 : : "eax", "ecx", "edx", "cc", "memory");
279 // Restore gdt and fs/gs
284 // Restore cmos index register
285 outb(cmosindex, PORT_CMOS_INDEX);