d71381fa134a1735a8d947b6a09a168983d4d7f9
[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
10
11 /****************************************************************
12  * Stack in EBDA
13  ****************************************************************/
14
15 // Switch to the extra stack in ebda and call a function.
16 inline u32
17 stack_hop(u32 eax, u32 edx, u32 ecx, void *func)
18 {
19     ASSERT16();
20     u16 ebda_seg = get_ebda_seg(), bkup_ss;
21     u32 bkup_esp;
22     asm volatile(
23         // Backup current %ss/%esp values.
24         "movw %%ss, %w3\n"
25         "movl %%esp, %4\n"
26         // Copy ebda seg to %ds/%ss and set %esp
27         "movw %w6, %%ds\n"
28         "movw %w6, %%ss\n"
29         "movl %5, %%esp\n"
30         // Call func
31         "calll %7\n"
32         // Restore segments and stack
33         "movw %w3, %%ds\n"
34         "movw %w3, %%ss\n"
35         "movl %4, %%esp"
36         : "+a" (eax), "+d" (edx), "+c" (ecx), "=&r" (bkup_ss), "=&r" (bkup_esp)
37         : "i" (EBDA_OFFSET_TOP_STACK), "r" (ebda_seg), "m" (*(u8*)func)
38         : "cc", "memory");
39     return eax;
40 }
41
42
43 /****************************************************************
44  * Threads
45  ****************************************************************/
46
47 #define THREADSTACKSIZE 4096
48
49 struct thread_info {
50     struct thread_info *next;
51     void *stackpos;
52 };
53
54 struct thread_info MainThread;
55
56 void
57 thread_setup()
58 {
59     MainThread.next = &MainThread;
60     MainThread.stackpos = NULL;
61 }
62
63 struct thread_info *
64 getCurThread()
65 {
66     u32 esp = getesp();
67     if (esp <= BUILD_STACK_ADDR)
68         return &MainThread;
69     return (void*)ALIGN_DOWN(esp, THREADSTACKSIZE);
70 }
71
72 // Briefly permit irqs to occur.
73 void
74 yield()
75 {
76     if (MODE16 || !CONFIG_THREADS) {
77         // Just directly check irqs.
78         check_irqs();
79         return;
80     }
81     struct thread_info *cur = getCurThread();
82     if (cur == &MainThread)
83         // Permit irqs to fire
84         check_irqs();
85
86     // Switch to the next thread
87     struct thread_info *next = cur->next;
88     asm volatile(
89         "  pushl $1f\n"                 // store return pc
90         "  pushl %%ebp\n"               // backup %ebp
91         "  movl %%esp, 4(%%eax)\n"      // cur->stackpos = %esp
92         "  movl 4(%%ecx), %%esp\n"      // %esp = next->stackpos
93         "  popl %%ebp\n"                // restore %ebp
94         "  retl\n"                      // restore pc
95         "1:\n"
96         : "+a"(cur), "+c"(next)
97         :
98         : "ebx", "edx", "esi", "edi", "cc", "memory");
99 }
100
101 // Last thing called from a thread (called on "next" stack).
102 static void
103 __end_thread(struct thread_info *old)
104 {
105     struct thread_info *pos = &MainThread;
106     while (pos->next != old)
107         pos = pos->next;
108     pos->next = old->next;
109     free(old);
110     dprintf(DEBUG_thread, "\\%08x/ End thread\n", (u32)old);
111 }
112
113 void
114 run_thread(void (*func)(void*), void *data)
115 {
116     ASSERT32();
117     if (! CONFIG_THREADS)
118         goto fail;
119     struct thread_info *thread;
120     thread = memalign_tmphigh(THREADSTACKSIZE, THREADSTACKSIZE);
121     if (!thread)
122         goto fail;
123
124     thread->stackpos = (void*)thread + THREADSTACKSIZE;
125     struct thread_info *cur = getCurThread();
126     thread->next = cur->next;
127     cur->next = thread;
128
129     dprintf(DEBUG_thread, "/%08x\\ Start thread\n", (u32)thread);
130     asm volatile(
131         // Start thread
132         "  pushl $1f\n"                 // store return pc
133         "  pushl %%ebp\n"               // backup %ebp
134         "  movl %%esp, 4(%%edx)\n"      // cur->stackpos = %esp
135         "  movl 4(%%ebx), %%esp\n"      // %esp = thread->stackpos
136         "  calll *%%ecx\n"              // Call func
137
138         // End thread
139         "  movl (%%ebx), %%ecx\n"       // %ecx = thread->next
140         "  movl 4(%%ecx), %%esp\n"      // %esp = next->stackpos
141         "  movl %%ebx, %%eax\n"
142         "  calll %4\n"                  // call __end_thread(thread)
143         "  popl %%ebp\n"                // restore %ebp
144         "  retl\n"                      // restore pc
145         "1:\n"
146         : "+a"(data), "+c"(func), "+b"(thread), "+d"(cur)
147         : "m"(*(u8*)__end_thread)
148         : "esi", "edi", "cc", "memory");
149     return;
150
151 fail:
152     func(data);
153 }
154
155 void
156 wait_threads()
157 {
158     ASSERT32();
159     if (! CONFIG_THREADS)
160         return;
161     while (MainThread.next != &MainThread)
162         yield();
163 }