Force use of indirect function calls in inline assembler.
[seabios.git] / src / stacks.c
1 // Code for manipulating stack locations.
2 //
3 // Copyright (C) 2009  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_ebda_seg
8 #include "util.h" // dprintf
9 #include "bregs.h" // CR0_PE
10
11 static inline u32 getcr0(void) {
12     u32 cr0;
13     asm("movl %%cr0, %0" : "=r"(cr0));
14     return cr0;
15 }
16 static inline void sgdt(struct descloc_s *desc) {
17     asm("sgdtl %0" : "=m"(*desc));
18 }
19 static inline void lgdt(struct descloc_s *desc) {
20     asm("lgdtl %0" : : "m"(*desc) : "memory");
21 }
22
23 // Call a 32bit SeaBIOS function from a 16bit SeaBIOS function.
24 static inline int
25 call32(void *func)
26 {
27     ASSERT16();
28     u32 cr0 = getcr0();
29     if (cr0 & CR0_PE)
30         // Called in 16bit protected mode?!
31         return -1;
32
33     // Backup cmos index register and disable nmi
34     u8 cmosindex = inb(PORT_CMOS_INDEX);
35     outb(cmosindex | NMI_DISABLE_BIT, PORT_CMOS_INDEX);
36     inb(PORT_CMOS_DATA);
37
38     // Backup fs/gs and gdt
39     u16 fs = GET_SEG(FS), gs = GET_SEG(GS);
40     struct descloc_s gdt;
41     sgdt(&gdt);
42
43     u32 bkup_ss, bkup_esp;
44     asm volatile(
45         // Backup ss/esp / set esp to flat stack location
46         "  movl %%ss, %0\n"
47         "  movl %%esp, %1\n"
48         "  shll $4, %0\n"
49         "  addl %0, %%esp\n"
50         "  movl %%ss, %0\n"
51
52         // Transition to 32bit mode, call func, return to 16bit
53         "  pushl $(" __stringify(BUILD_BIOS_ADDR) " + 1f)\n"
54         "  jmp transition32\n"
55         "  .code32\n"
56         "1:calll *%2\n"
57         "  pushl $2f\n"
58         "  jmp transition16big\n"
59
60         // Restore ds/ss/esp
61         "  .code16gcc\n"
62         "2:movl %0, %%ds\n"
63         "  movl %0, %%ss\n"
64         "  movl %1, %%esp\n"
65         : "=&r" (bkup_ss), "=&r" (bkup_esp)
66         : "r" (func)
67         : "eax", "ecx", "edx", "cc", "memory");
68
69     // Restore gdt and fs/gs
70     lgdt(&gdt);
71     SET_SEG(FS, fs);
72     SET_SEG(GS, gs);
73
74     // Restore cmos index register
75     outb(cmosindex, PORT_CMOS_INDEX);
76     inb(PORT_CMOS_DATA);
77     return 0;
78 }
79
80
81 /****************************************************************
82  * Stack in EBDA
83  ****************************************************************/
84
85 // Switch to the extra stack in ebda and call a function.
86 inline u32
87 stack_hop(u32 eax, u32 edx, void *func)
88 {
89     ASSERT16();
90     u16 ebda_seg = get_ebda_seg(), bkup_ss;
91     u32 bkup_esp;
92     asm volatile(
93         // Backup current %ss/%esp values.
94         "movw %%ss, %w3\n"
95         "movl %%esp, %4\n"
96         // Copy ebda seg to %ds/%ss and set %esp
97         "movw %w6, %%ds\n"
98         "movw %w6, %%ss\n"
99         "movl %5, %%esp\n"
100         // Call func
101         "calll *%2\n"
102         // Restore segments and stack
103         "movw %w3, %%ds\n"
104         "movw %w3, %%ss\n"
105         "movl %4, %%esp"
106         : "+a" (eax), "+d" (edx), "+c" (func), "=&r" (bkup_ss), "=&r" (bkup_esp)
107         : "i" (EBDA_OFFSET_TOP_STACK), "r" (ebda_seg)
108         : "cc", "memory");
109     return eax;
110 }
111
112
113 /****************************************************************
114  * Threads
115  ****************************************************************/
116
117 #define THREADSTACKSIZE 4096
118
119 struct thread_info {
120     struct thread_info *next;
121     void *stackpos;
122 };
123
124 struct thread_info VAR16VISIBLE MainThread;
125 int VAR16VISIBLE CanPreempt;
126
127 void
128 thread_setup(void)
129 {
130     MainThread.next = &MainThread;
131     MainThread.stackpos = NULL;
132     CanPreempt = 0;
133 }
134
135 // Return the 'struct thread_info' for the currently running thread.
136 struct thread_info *
137 getCurThread(void)
138 {
139     u32 esp = getesp();
140     if (esp <= BUILD_STACK_ADDR)
141         return &MainThread;
142     return (void*)ALIGN_DOWN(esp, THREADSTACKSIZE);
143 }
144
145 // Switch to next thread stack.
146 static void
147 switch_next(struct thread_info *cur)
148 {
149     struct thread_info *next = cur->next;
150     if (cur == next)
151         // Nothing to do.
152         return;
153     asm volatile(
154         "  pushl $1f\n"                 // store return pc
155         "  pushl %%ebp\n"               // backup %ebp
156         "  movl %%esp, 4(%%eax)\n"      // cur->stackpos = %esp
157         "  movl 4(%%ecx), %%esp\n"      // %esp = next->stackpos
158         "  popl %%ebp\n"                // restore %ebp
159         "  retl\n"                      // restore pc
160         "1:\n"
161         : "+a"(cur), "+c"(next)
162         :
163         : "ebx", "edx", "esi", "edi", "cc", "memory");
164 }
165
166 // Briefly permit irqs to occur.
167 void
168 yield(void)
169 {
170     if (MODESEGMENT || !CONFIG_THREADS) {
171         // Just directly check irqs.
172         check_irqs();
173         return;
174     }
175     struct thread_info *cur = getCurThread();
176     if (cur == &MainThread)
177         // Permit irqs to fire
178         check_irqs();
179
180     // Switch to the next thread
181     switch_next(cur);
182 }
183
184 // Last thing called from a thread (called on "next" stack).
185 static void
186 __end_thread(struct thread_info *old)
187 {
188     struct thread_info *pos = &MainThread;
189     while (pos->next != old)
190         pos = pos->next;
191     pos->next = old->next;
192     free(old);
193     dprintf(DEBUG_thread, "\\%08x/ End thread\n", (u32)old);
194 }
195
196 // Create a new thread and start executing 'func' in it.
197 void
198 run_thread(void (*func)(void*), void *data)
199 {
200     ASSERT32FLAT();
201     if (! CONFIG_THREADS)
202         goto fail;
203     struct thread_info *thread;
204     thread = memalign_tmphigh(THREADSTACKSIZE, THREADSTACKSIZE);
205     if (!thread)
206         goto fail;
207
208     thread->stackpos = (void*)thread + THREADSTACKSIZE;
209     struct thread_info *cur = getCurThread();
210     thread->next = cur->next;
211     cur->next = thread;
212
213     dprintf(DEBUG_thread, "/%08x\\ Start thread\n", (u32)thread);
214     asm volatile(
215         // Start thread
216         "  pushl $1f\n"                 // store return pc
217         "  pushl %%ebp\n"               // backup %ebp
218         "  movl %%esp, 4(%%edx)\n"      // cur->stackpos = %esp
219         "  movl 4(%%ebx), %%esp\n"      // %esp = thread->stackpos
220         "  calll *%%ecx\n"              // Call func
221
222         // End thread
223         "  movl (%%ebx), %%ecx\n"       // %ecx = thread->next
224         "  movl 4(%%ecx), %%esp\n"      // %esp = next->stackpos
225         "  movl %%ebx, %%eax\n"
226         "  calll %4\n"                  // call __end_thread(thread)
227         "  popl %%ebp\n"                // restore %ebp
228         "  retl\n"                      // restore pc
229         "1:\n"
230         : "+a"(data), "+c"(func), "+b"(thread), "+d"(cur)
231         : "m"(*(u8*)__end_thread)
232         : "esi", "edi", "cc", "memory");
233     return;
234
235 fail:
236     func(data);
237 }
238
239 // Wait for all threads (other than the main thread) to complete.
240 void
241 wait_threads(void)
242 {
243     ASSERT32FLAT();
244     if (! CONFIG_THREADS)
245         return;
246     while (MainThread.next != &MainThread)
247         yield();
248 }
249
250 void
251 mutex_lock(struct mutex_s *mutex)
252 {
253     ASSERT32FLAT();
254     if (! CONFIG_THREADS)
255         return;
256     while (mutex->isLocked)
257         yield();
258     mutex->isLocked = 1;
259 }
260
261 void
262 mutex_unlock(struct mutex_s *mutex)
263 {
264     ASSERT32FLAT();
265     if (! CONFIG_THREADS)
266         return;
267     mutex->isLocked = 0;
268 }
269
270
271 /****************************************************************
272  * Thread preemption
273  ****************************************************************/
274
275 static u32 PreemptCount;
276
277 // Turn on RTC irqs and arrange for them to check the 32bit threads.
278 void
279 start_preempt(void)
280 {
281     if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS)
282         return;
283     CanPreempt = 1;
284     PreemptCount = 0;
285     useRTC();
286 }
287
288 // Turn off RTC irqs / stop checking for thread execution.
289 void
290 finish_preempt(void)
291 {
292     if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS)
293         return;
294     CanPreempt = 0;
295     releaseRTC();
296     dprintf(1, "Done preempt - %d checks\n", PreemptCount);
297 }
298
299 // Check if preemption is on, and wait for it to complete if so.
300 int
301 wait_preempt(void)
302 {
303     if (MODESEGMENT || !CONFIG_THREADS || !CONFIG_THREAD_OPTIONROMS
304         || !CanPreempt)
305         return 0;
306     while (CanPreempt)
307         yield();
308     return 1;
309 }
310
311 extern void yield_preempt(void);
312 #if MODESEGMENT == 0
313 // Try to execute 32bit threads.
314 void VISIBLE32FLAT
315 yield_preempt(void)
316 {
317     PreemptCount++;
318     switch_next(&MainThread);
319 }
320 #endif
321
322 // 16bit code that checks if threads are pending and executes them if so.
323 void
324 check_preempt(void)
325 {
326     if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS
327         || !GET_GLOBAL(CanPreempt)
328         || GET_GLOBAL(MainThread.next) == &MainThread)
329         return;
330
331     call32(yield_preempt);
332 }