92d91a0602c6e19997101e73d2464b94b1b60e7e
[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 // Thread info - stored at bottom of each thread stack - don't change
120 // without also updating the inline assembler below.
121 struct thread_info {
122     struct thread_info *next;
123     void *stackpos;
124     struct thread_info **pprev;
125 };
126
127 struct thread_info VAR16VISIBLE MainThread;
128 int VAR16VISIBLE CanPreempt;
129
130 void
131 thread_setup(void)
132 {
133     MainThread.next = &MainThread;
134     MainThread.pprev = &MainThread.next;
135     MainThread.stackpos = NULL;
136     CanPreempt = 0;
137 }
138
139 // Return the 'struct thread_info' for the currently running thread.
140 struct thread_info *
141 getCurThread(void)
142 {
143     u32 esp = getesp();
144     if (esp <= BUILD_STACK_ADDR)
145         return &MainThread;
146     return (void*)ALIGN_DOWN(esp, THREADSTACKSIZE);
147 }
148
149 // Switch to next thread stack.
150 static void
151 switch_next(struct thread_info *cur)
152 {
153     struct thread_info *next = cur->next;
154     if (cur == next)
155         // Nothing to do.
156         return;
157     asm volatile(
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
164         "1:\n"
165         : "+a"(cur), "+c"(next)
166         :
167         : "ebx", "edx", "esi", "edi", "cc", "memory");
168 }
169
170 // Briefly permit irqs to occur.
171 void
172 yield(void)
173 {
174     if (MODESEGMENT || !CONFIG_THREADS) {
175         // Just directly check irqs.
176         check_irqs();
177         return;
178     }
179     struct thread_info *cur = getCurThread();
180     if (cur == &MainThread)
181         // Permit irqs to fire
182         check_irqs();
183
184     // Switch to the next thread
185     switch_next(cur);
186 }
187
188 // Last thing called from a thread (called on "next" stack).
189 static void
190 __end_thread(struct thread_info *old)
191 {
192     old->next->pprev = old->pprev;
193     *old->pprev = old->next;
194     free(old);
195     dprintf(DEBUG_thread, "\\%08x/ End thread\n", (u32)old);
196 }
197
198 // Create a new thread and start executing 'func' in it.
199 void
200 run_thread(void (*func)(void*), void *data)
201 {
202     ASSERT32FLAT();
203     if (! CONFIG_THREADS)
204         goto fail;
205     struct thread_info *thread;
206     thread = memalign_tmphigh(THREADSTACKSIZE, THREADSTACKSIZE);
207     if (!thread)
208         goto fail;
209
210     thread->stackpos = (void*)thread + THREADSTACKSIZE;
211     struct thread_info *cur = getCurThread();
212     thread->next = cur;
213     thread->pprev = cur->pprev;
214     cur->pprev = &thread->next;
215     *thread->pprev = thread;
216
217     dprintf(DEBUG_thread, "/%08x\\ Start thread\n", (u32)thread);
218     asm volatile(
219         // Start 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
225
226         // End thread
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
233         "1:\n"
234         : "+a"(data), "+c"(func), "+b"(thread), "+d"(cur)
235         : "m"(*(u8*)__end_thread)
236         : "esi", "edi", "cc", "memory");
237     return;
238
239 fail:
240     func(data);
241 }
242
243 // Wait for all threads (other than the main thread) to complete.
244 void
245 wait_threads(void)
246 {
247     ASSERT32FLAT();
248     if (! CONFIG_THREADS)
249         return;
250     while (MainThread.next != &MainThread)
251         yield();
252 }
253
254 void
255 mutex_lock(struct mutex_s *mutex)
256 {
257     ASSERT32FLAT();
258     if (! CONFIG_THREADS)
259         return;
260     while (mutex->isLocked)
261         yield();
262     mutex->isLocked = 1;
263 }
264
265 void
266 mutex_unlock(struct mutex_s *mutex)
267 {
268     ASSERT32FLAT();
269     if (! CONFIG_THREADS)
270         return;
271     mutex->isLocked = 0;
272 }
273
274
275 /****************************************************************
276  * Thread preemption
277  ****************************************************************/
278
279 static u32 PreemptCount;
280
281 // Turn on RTC irqs and arrange for them to check the 32bit threads.
282 void
283 start_preempt(void)
284 {
285     if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS)
286         return;
287     CanPreempt = 1;
288     PreemptCount = 0;
289     useRTC();
290 }
291
292 // Turn off RTC irqs / stop checking for thread execution.
293 void
294 finish_preempt(void)
295 {
296     if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS) {
297         yield();
298         return;
299     }
300     CanPreempt = 0;
301     releaseRTC();
302     dprintf(1, "Done preempt - %d checks\n", PreemptCount);
303     yield();
304 }
305
306 // Check if preemption is on, and wait for it to complete if so.
307 int
308 wait_preempt(void)
309 {
310     if (MODESEGMENT || !CONFIG_THREADS || !CONFIG_THREAD_OPTIONROMS
311         || !CanPreempt)
312         return 0;
313     while (CanPreempt)
314         yield();
315     return 1;
316 }
317
318 extern void yield_preempt(void);
319 #if MODESEGMENT == 0
320 // Try to execute 32bit threads.
321 void VISIBLE32FLAT
322 yield_preempt(void)
323 {
324     PreemptCount++;
325     switch_next(&MainThread);
326 }
327 #endif
328
329 // 16bit code that checks if threads are pending and executes them if so.
330 void
331 check_preempt(void)
332 {
333     if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS
334         || !GET_GLOBAL(CanPreempt)
335         || GET_GLOBAL(MainThread.next) == &MainThread)
336         return;
337
338     call32(yield_preempt);
339 }