blob: a35ca3de7717a3bafed840689e3fdea40e5c3611 [file] [log] [blame]
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -05001// 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
Kevin O'Connorad901592009-12-13 11:25:25 -05009#include "bregs.h" // CR0_PE
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -050010
Kevin O'Connor1ca05b02010-01-03 17:43:37 -050011static inline u32 getcr0(void) {
Kevin O'Connora7fc5de2009-12-13 11:48:18 -050012 u32 cr0;
13 asm("movl %%cr0, %0" : "=r"(cr0));
14 return cr0;
15}
16static inline void sgdt(struct descloc_s *desc) {
17 asm("sgdtl %0" : "=m"(*desc));
18}
19static 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.
24static inline int
25call32(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 func -= BUILD_BIOS_ADDR;
44 u32 bkup_ss, bkup_esp;
45 asm volatile(
46 // Backup ss/esp / set esp to flat stack location
47 " movl %%ss, %0\n"
48 " movl %%esp, %1\n"
49 " shll $4, %0\n"
50 " addl %0, %%esp\n"
51 " movl %%ss, %0\n"
52
Kevin O'Connorfb214dc2009-12-20 13:11:17 -050053 // Transition to 32bit mode, call func, return to 16bit
Kevin O'Connora7fc5de2009-12-13 11:48:18 -050054 " pushl $(" __stringify(BUILD_BIOS_ADDR) " + 1f)\n"
55 " jmp transition32\n"
56 " .code32\n"
57 "1:calll %2\n"
58 " pushl $2f\n"
59 " jmp transition16big\n"
60
61 // Restore ds/ss/esp
62 " .code16gcc\n"
63 "2:movl %0, %%ds\n"
64 " movl %0, %%ss\n"
65 " movl %1, %%esp\n"
66 : "=&r" (bkup_ss), "=&r" (bkup_esp)
67 : "m" (*(u8*)func)
68 : "eax", "ecx", "edx", "cc", "memory");
69
70 // Restore gdt and fs/gs
71 lgdt(&gdt);
72 SET_SEG(FS, fs);
73 SET_SEG(GS, gs);
74
75 // Restore cmos index register
76 outb(cmosindex, PORT_CMOS_INDEX);
77 inb(PORT_CMOS_DATA);
78 return 0;
79}
80
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -050081
82/****************************************************************
83 * Stack in EBDA
84 ****************************************************************/
85
86// Switch to the extra stack in ebda and call a function.
87inline u32
88stack_hop(u32 eax, u32 edx, u32 ecx, void *func)
89{
90 ASSERT16();
91 u16 ebda_seg = get_ebda_seg(), bkup_ss;
92 u32 bkup_esp;
93 asm volatile(
94 // Backup current %ss/%esp values.
95 "movw %%ss, %w3\n"
96 "movl %%esp, %4\n"
97 // Copy ebda seg to %ds/%ss and set %esp
98 "movw %w6, %%ds\n"
99 "movw %w6, %%ss\n"
100 "movl %5, %%esp\n"
101 // Call func
102 "calll %7\n"
103 // Restore segments and stack
104 "movw %w3, %%ds\n"
105 "movw %w3, %%ss\n"
106 "movl %4, %%esp"
107 : "+a" (eax), "+d" (edx), "+c" (ecx), "=&r" (bkup_ss), "=&r" (bkup_esp)
108 : "i" (EBDA_OFFSET_TOP_STACK), "r" (ebda_seg), "m" (*(u8*)func)
109 : "cc", "memory");
110 return eax;
111}
112
113
114/****************************************************************
115 * Threads
116 ****************************************************************/
117
118#define THREADSTACKSIZE 4096
119
120struct thread_info {
121 struct thread_info *next;
122 void *stackpos;
123};
124
Kevin O'Connorad901592009-12-13 11:25:25 -0500125struct thread_info VAR16VISIBLE MainThread;
126int VAR16VISIBLE CanPreempt;
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500127
128void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500129thread_setup(void)
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500130{
131 MainThread.next = &MainThread;
132 MainThread.stackpos = NULL;
Kevin O'Connorad901592009-12-13 11:25:25 -0500133 CanPreempt = 0;
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500134}
135
Kevin O'Connorad901592009-12-13 11:25:25 -0500136// Return the 'struct thread_info' for the currently running thread.
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500137struct thread_info *
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500138getCurThread(void)
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500139{
140 u32 esp = getesp();
141 if (esp <= BUILD_STACK_ADDR)
142 return &MainThread;
143 return (void*)ALIGN_DOWN(esp, THREADSTACKSIZE);
144}
145
Kevin O'Connorad901592009-12-13 11:25:25 -0500146// Switch to next thread stack.
147static void
148switch_next(struct thread_info *cur)
149{
150 struct thread_info *next = cur->next;
Kevin O'Connorfb214dc2009-12-20 13:11:17 -0500151 if (cur == next)
152 // Nothing to do.
153 return;
Kevin O'Connorad901592009-12-13 11:25:25 -0500154 asm volatile(
155 " pushl $1f\n" // store return pc
156 " pushl %%ebp\n" // backup %ebp
157 " movl %%esp, 4(%%eax)\n" // cur->stackpos = %esp
158 " movl 4(%%ecx), %%esp\n" // %esp = next->stackpos
159 " popl %%ebp\n" // restore %ebp
160 " retl\n" // restore pc
161 "1:\n"
162 : "+a"(cur), "+c"(next)
163 :
164 : "ebx", "edx", "esi", "edi", "cc", "memory");
165}
166
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500167// Briefly permit irqs to occur.
168void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500169yield(void)
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500170{
Kevin O'Connor52a300f2009-12-26 23:32:57 -0500171 if (MODESEGMENT || !CONFIG_THREADS) {
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500172 // Just directly check irqs.
173 check_irqs();
174 return;
175 }
176 struct thread_info *cur = getCurThread();
177 if (cur == &MainThread)
178 // Permit irqs to fire
179 check_irqs();
180
181 // Switch to the next thread
Kevin O'Connorad901592009-12-13 11:25:25 -0500182 switch_next(cur);
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500183}
184
185// Last thing called from a thread (called on "next" stack).
186static void
187__end_thread(struct thread_info *old)
188{
189 struct thread_info *pos = &MainThread;
190 while (pos->next != old)
191 pos = pos->next;
192 pos->next = old->next;
193 free(old);
194 dprintf(DEBUG_thread, "\\%08x/ End thread\n", (u32)old);
195}
196
Kevin O'Connorad901592009-12-13 11:25:25 -0500197// Create a new thread and start executing 'func' in it.
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500198void
199run_thread(void (*func)(void*), void *data)
200{
Kevin O'Connor52a300f2009-12-26 23:32:57 -0500201 ASSERT32FLAT();
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500202 if (! CONFIG_THREADS)
203 goto fail;
204 struct thread_info *thread;
205 thread = memalign_tmphigh(THREADSTACKSIZE, THREADSTACKSIZE);
206 if (!thread)
207 goto fail;
208
209 thread->stackpos = (void*)thread + THREADSTACKSIZE;
210 struct thread_info *cur = getCurThread();
211 thread->next = cur->next;
212 cur->next = thread;
213
214 dprintf(DEBUG_thread, "/%08x\\ Start thread\n", (u32)thread);
215 asm volatile(
216 // Start thread
217 " pushl $1f\n" // store return pc
218 " pushl %%ebp\n" // backup %ebp
219 " movl %%esp, 4(%%edx)\n" // cur->stackpos = %esp
220 " movl 4(%%ebx), %%esp\n" // %esp = thread->stackpos
221 " calll *%%ecx\n" // Call func
222
223 // End thread
224 " movl (%%ebx), %%ecx\n" // %ecx = thread->next
225 " movl 4(%%ecx), %%esp\n" // %esp = next->stackpos
226 " movl %%ebx, %%eax\n"
227 " calll %4\n" // call __end_thread(thread)
228 " popl %%ebp\n" // restore %ebp
229 " retl\n" // restore pc
230 "1:\n"
231 : "+a"(data), "+c"(func), "+b"(thread), "+d"(cur)
232 : "m"(*(u8*)__end_thread)
233 : "esi", "edi", "cc", "memory");
234 return;
235
236fail:
237 func(data);
238}
239
Kevin O'Connorad901592009-12-13 11:25:25 -0500240// Wait for all threads (other than the main thread) to complete.
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500241void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500242wait_threads(void)
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500243{
Kevin O'Connor52a300f2009-12-26 23:32:57 -0500244 ASSERT32FLAT();
Kevin O'Connor7cefbfa2009-12-10 21:35:49 -0500245 if (! CONFIG_THREADS)
246 return;
247 while (MainThread.next != &MainThread)
248 yield();
249}
Kevin O'Connorad901592009-12-13 11:25:25 -0500250
251
252/****************************************************************
253 * Thread preemption
254 ****************************************************************/
255
256static u32 PreemptCount;
257
258// Turn on RTC irqs and arrange for them to check the 32bit threads.
259void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500260start_preempt(void)
Kevin O'Connorad901592009-12-13 11:25:25 -0500261{
262 if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS)
263 return;
264 CanPreempt = 1;
265 PreemptCount = 0;
266 useRTC();
267}
268
269// Turn off RTC irqs / stop checking for thread execution.
270void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500271finish_preempt(void)
Kevin O'Connorad901592009-12-13 11:25:25 -0500272{
273 if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS)
274 return;
275 CanPreempt = 0;
276 releaseRTC();
277 dprintf(1, "Done preempt - %d checks\n", PreemptCount);
278}
279
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500280extern void yield_preempt(void);
Kevin O'Connor52a300f2009-12-26 23:32:57 -0500281#if MODESEGMENT == 0
Kevin O'Connorad901592009-12-13 11:25:25 -0500282// Try to execute 32bit threads.
Kevin O'Connor52a300f2009-12-26 23:32:57 -0500283void VISIBLE32FLAT
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500284yield_preempt(void)
Kevin O'Connorad901592009-12-13 11:25:25 -0500285{
286 PreemptCount++;
287 switch_next(&MainThread);
288}
289#endif
290
291// 16bit code that checks if threads are pending and executes them if so.
292void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500293check_preempt(void)
Kevin O'Connorad901592009-12-13 11:25:25 -0500294{
Kevin O'Connorad901592009-12-13 11:25:25 -0500295 if (! CONFIG_THREADS || ! CONFIG_THREAD_OPTIONROMS
296 || !GET_GLOBAL(CanPreempt)
297 || GET_GLOBAL(MainThread.next) == &MainThread)
298 return;
Kevin O'Connorad901592009-12-13 11:25:25 -0500299
Kevin O'Connora7fc5de2009-12-13 11:48:18 -0500300 call32(yield_preempt);
Kevin O'Connorad901592009-12-13 11:25:25 -0500301}