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
11 static inline u32 getcr0(void) {
13 asm("movl %%cr0, %0" : "=r"(cr0));
16 static inline void sgdt(struct descloc_s *desc) {
17 asm("sgdtl %0" : "=m"(*desc));
19 static inline void lgdt(struct descloc_s *desc) {
20 asm("lgdtl %0" : : "m"(*desc) : "memory");
23 // Call a 32bit SeaBIOS function from a 16bit SeaBIOS function.
30 // Called in 16bit protected mode?!
33 // Backup cmos index register and disable nmi
34 u8 cmosindex = inb(PORT_CMOS_INDEX);
35 outb(cmosindex | NMI_DISABLE_BIT, PORT_CMOS_INDEX);
38 // Backup fs/gs and gdt
39 u16 fs = GET_SEG(FS), gs = GET_SEG(GS);
43 u32 bkup_ss, bkup_esp;
45 // Backup ss/esp / set esp to flat stack location
52 // Transition to 32bit mode, call func, return to 16bit
53 " pushl $(" __stringify(BUILD_BIOS_ADDR) " + 1f)\n"
58 " jmp transition16big\n"
65 : "=&r" (bkup_ss), "=&r" (bkup_esp)
67 : "eax", "ecx", "edx", "cc", "memory");
69 // Restore gdt and fs/gs
74 // Restore cmos index register
75 outb(cmosindex, PORT_CMOS_INDEX);
81 /****************************************************************
83 ****************************************************************/
85 // Switch to the extra stack in ebda and call a function.
87 stack_hop(u32 eax, u32 edx, void *func)
90 u16 ebda_seg = get_ebda_seg(), bkup_ss;
93 // Backup current %ss/%esp values.
96 // Copy ebda seg to %ds/%ss and set %esp
102 // Restore segments and stack
106 : "+a" (eax), "+d" (edx), "+c" (func), "=&r" (bkup_ss), "=&r" (bkup_esp)
107 : "i" (EBDA_OFFSET_TOP_STACK), "r" (ebda_seg)
113 /****************************************************************
115 ****************************************************************/
117 #define THREADSTACKSIZE 4096
119 // Thread info - stored at bottom of each thread stack - don't change
120 // without also updating the inline assembler below.
122 struct thread_info *next;
124 struct thread_info **pprev;
127 struct thread_info VAR16VISIBLE MainThread;
128 int VAR16VISIBLE CanPreempt;
133 MainThread.next = &MainThread;
134 MainThread.pprev = &MainThread.next;
135 MainThread.stackpos = NULL;
139 // Return the 'struct thread_info' for the currently running thread.
144 if (esp <= BUILD_STACK_ADDR)
146 return (void*)ALIGN_DOWN(esp, THREADSTACKSIZE);
149 // Switch to next thread stack.
151 switch_next(struct thread_info *cur)
153 struct thread_info *next = cur->next;
158 " pushl $1f\n" // store return pc
159 " pushl %%ebp\n" // backup %ebp
160 " movl %%esp, 4(%%eax)\n" // cur->stackpos = %esp
161 " movl 4(%%ecx), %%esp\n" // %esp = next->stackpos
162 " popl %%ebp\n" // restore %ebp
163 " retl\n" // restore pc
165 : "+a"(cur), "+c"(next)
167 : "ebx", "edx", "esi", "edi", "cc", "memory");
170 // Briefly permit irqs to occur.
174 if (MODESEGMENT || !CONFIG_THREADS) {
175 // Just directly check irqs.
179 struct thread_info *cur = getCurThread();
180 if (cur == &MainThread)
181 // Permit irqs to fire
184 // Switch to the next thread
188 // Last thing called from a thread (called on "next" stack).
190 __end_thread(struct thread_info *old)
192 old->next->pprev = old->pprev;
193 *old->pprev = old->next;
195 dprintf(DEBUG_thread, "\\%08x/ End thread\n", (u32)old);
198 // Create a new thread and start executing 'func' in it.
200 run_thread(void (*func)(void*), void *data)
203 if (! CONFIG_THREADS)
205 struct thread_info *thread;
206 thread = memalign_tmphigh(THREADSTACKSIZE, THREADSTACKSIZE);
210 thread->stackpos = (void*)thread + THREADSTACKSIZE;
211 struct thread_info *cur = getCurThread();
213 thread->pprev = cur->pprev;
214 cur->pprev = &thread->next;
215 *thread->pprev = thread;
217 dprintf(DEBUG_thread, "/%08x\\ Start thread\n", (u32)thread);
220 " pushl $1f\n" // store return pc
221 " pushl %%ebp\n" // backup %ebp
222 " movl %%esp, 4(%%edx)\n" // cur->stackpos = %esp
223 " movl 4(%%ebx), %%esp\n" // %esp = thread->stackpos
224 " calll *%%ecx\n" // Call func
227 " movl (%%ebx), %%ecx\n" // %ecx = thread->next
228 " movl 4(%%ecx), %%esp\n" // %esp = next->stackpos
229 " movl %%ebx, %%eax\n"
230 " calll %4\n" // call __end_thread(thread)
231 " popl %%ebp\n" // restore %ebp
232 " retl\n" // restore pc
234 : "+a"(data), "+c"(func), "+b"(thread), "+d"(cur)
235 : "m"(*(u8*)__end_thread)
236 : "esi", "edi", "cc", "memory");
243 // Wait for all threads (other than the main thread) to complete.
248 if (! CONFIG_THREADS)
250 while (MainThread.next != &MainThread)
255 mutex_lock(struct mutex_s *mutex)
258 if (! CONFIG_THREADS)
260 while (mutex->isLocked)
266 mutex_unlock(struct mutex_s *mutex)
269 if (! CONFIG_THREADS)
275 /****************************************************************
277 ****************************************************************/
279 static u32 PreemptCount;
281 // Turn on RTC irqs and arrange for them to check the 32bit threads.
285 if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS)
292 // Turn off RTC irqs / stop checking for thread execution.
296 if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS) {
302 dprintf(1, "Done preempt - %d checks\n", PreemptCount);
306 // Check if preemption is on, and wait for it to complete if so.
310 if (MODESEGMENT || !CONFIG_THREADS || !CONFIG_THREAD_OPTIONROMS
318 extern void yield_preempt(void);
320 // Try to execute 32bit threads.
325 switch_next(&MainThread);
329 // 16bit code that checks if threads are pending and executes them if so.
333 if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS
334 || !GET_GLOBAL(CanPreempt)
335 || GET_GLOBAL(MainThread.next) == &MainThread)
338 call32(yield_preempt);