Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


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