Clear thread info on each boot.
[seabios.git] / src / util.c
1 // Misc utility functions.
2 //
3 // Copyright (C) 2008,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 "util.h" // call16
8 #include "bregs.h" // struct bregs
9 #include "farptr.h" // GET_FLATPTR
10 #include "biosvar.h" // get_ebda_seg
11
12 static inline u32 getesp() {
13     u32 esp;
14     asm("movl %%esp, %0" : "=rm"(esp));
15     return esp;
16 }
17
18
19 /****************************************************************
20  * 16bit calls
21  ****************************************************************/
22
23 // Call a function with a specified register state.  Note that on
24 // return, the interrupt enable/disable flag may be altered.
25 inline void
26 call16(struct bregs *callregs)
27 {
28     if (!MODE16 && getesp() > BUILD_STACK_ADDR)
29         panic("call16 with invalid stack\n");
30     asm volatile(
31 #if MODE16 == 1
32         "calll __call16\n"
33         "cli\n"
34         "cld"
35 #else
36         "calll __call16_from32"
37 #endif
38         : "+a" (callregs), "+m" (*callregs)
39         :
40         : "ebx", "ecx", "edx", "esi", "edi", "cc", "memory");
41 }
42
43 inline void
44 call16big(struct bregs *callregs)
45 {
46     ASSERT32();
47     if (getesp() > BUILD_STACK_ADDR)
48         panic("call16 with invalid stack\n");
49     asm volatile(
50         "calll __call16big_from32"
51         : "+a" (callregs), "+m" (*callregs)
52         :
53         : "ebx", "ecx", "edx", "esi", "edi", "cc", "memory");
54 }
55
56 inline void
57 __call16_int(struct bregs *callregs, u16 offset)
58 {
59     if (MODE16)
60         callregs->code.seg = GET_SEG(CS);
61     else
62         callregs->code.seg = SEG_BIOS;
63     callregs->code.offset = offset;
64     call16(callregs);
65 }
66
67 // Switch to the extra stack in ebda and call a function.
68 inline u32
69 stack_hop(u32 eax, u32 edx, u32 ecx, void *func)
70 {
71     ASSERT16();
72     u16 ebda_seg = get_ebda_seg(), bkup_ss;
73     u32 bkup_esp;
74     asm volatile(
75         // Backup current %ss/%esp values.
76         "movw %%ss, %w3\n"
77         "movl %%esp, %4\n"
78         // Copy ebda seg to %ds/%ss and set %esp
79         "movw %w6, %%ds\n"
80         "movw %w6, %%ss\n"
81         "movl %5, %%esp\n"
82         // Call func
83         "calll %7\n"
84         // Restore segments and stack
85         "movw %w3, %%ds\n"
86         "movw %w3, %%ss\n"
87         "movl %4, %%esp"
88         : "+a" (eax), "+d" (edx), "+c" (ecx), "=&r" (bkup_ss), "=&r" (bkup_esp)
89         : "i" (EBDA_OFFSET_TOP_STACK), "r" (ebda_seg), "m" (*(u8*)func)
90         : "cc", "memory");
91     return eax;
92 }
93
94 // 16bit trampoline for enabling irqs from 32bit mode.
95 ASM16(
96     "  .global trampoline_checkirqs\n"
97     "trampoline_checkirqs:\n"
98     "  rep ; nop\n"
99     "  lretw"
100     );
101
102 static void
103 check_irqs32()
104 {
105     extern void trampoline_checkirqs();
106     struct bregs br;
107     br.flags = F_IF;
108     br.code.seg = SEG_BIOS;
109     br.code.offset = (u32)&trampoline_checkirqs;
110     call16big(&br);
111 }
112
113 static void
114 check_irqs16()
115 {
116     asm volatile(
117         "sti\n"
118         "nop\n"
119         "rep ; nop\n"
120         "cli\n"
121         "cld\n"
122         : : :"memory");
123 }
124
125
126 /****************************************************************
127  * Threads
128  ****************************************************************/
129
130 #define THREADSTACKSIZE 4096
131
132 struct thread_info {
133     struct thread_info *next;
134     void *stackpos;
135 };
136
137 struct thread_info MainThread;
138
139 void
140 thread_setup()
141 {
142     MainThread.next = &MainThread;
143     MainThread.stackpos = NULL;
144 }
145
146 struct thread_info *
147 getCurThread()
148 {
149     u32 esp = getesp();
150     if (esp <= BUILD_STACK_ADDR)
151         return &MainThread;
152     return (void*)ALIGN_DOWN(esp, THREADSTACKSIZE);
153 }
154
155 // Briefly permit irqs to occur.
156 void
157 yield()
158 {
159     if (MODE16) {
160         // In 16bit mode, just directly check irqs.
161         check_irqs16();
162         return;
163     }
164     if (! CONFIG_THREADS) {
165         check_irqs32();
166         return;
167     }
168     struct thread_info *cur = getCurThread();
169     if (cur == &MainThread)
170         // Permit irqs to fire
171         check_irqs32();
172
173     // Switch to the next thread
174     struct thread_info *next = cur->next;
175     asm volatile(
176         "  pushl $1f\n"                 // store return pc
177         "  pushl %%ebp\n"               // backup %ebp
178         "  movl %%esp, 4(%%eax)\n"      // cur->stackpos = %esp
179         "  movl 4(%%ecx), %%esp\n"      // %esp = next->stackpos
180         "  popl %%ebp\n"                // restore %ebp
181         "  retl\n"                      // restore pc
182         "1:\n"
183         : "+a"(cur), "+c"(next)
184         :
185         : "ebx", "edx", "esi", "edi", "cc", "memory");
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     struct thread_info *pos = &MainThread;
193     while (pos->next != old)
194         pos = pos->next;
195     pos->next = old->next;
196     free(old);
197     dprintf(DEBUG_thread, "\\%08x/ End thread\n", (u32)old);
198 }
199
200 void
201 run_thread(void (*func)(void*), void *data)
202 {
203     ASSERT32();
204     if (! CONFIG_THREADS)
205         goto fail;
206     struct thread_info *thread;
207     thread = memalign_tmphigh(THREADSTACKSIZE, THREADSTACKSIZE);
208     if (!thread)
209         goto fail;
210
211     thread->stackpos = (void*)thread + THREADSTACKSIZE;
212     struct thread_info *cur = getCurThread();
213     thread->next = cur->next;
214     cur->next = thread;
215
216     dprintf(DEBUG_thread, "/%08x\\ Start thread\n", (u32)thread);
217     asm volatile(
218         // Start thread
219         "  pushl $1f\n"                 // store return pc
220         "  pushl %%ebp\n"               // backup %ebp
221         "  movl %%esp, 4(%%edx)\n"      // cur->stackpos = %esp
222         "  movl 4(%%ebx), %%esp\n"      // %esp = thread->stackpos
223         "  calll *%%ecx\n"              // Call func
224
225         // End thread
226         "  movl (%%ebx), %%ecx\n"       // %ecx = thread->next
227         "  movl 4(%%ecx), %%esp\n"      // %esp = next->stackpos
228         "  movl %%ebx, %%eax\n"
229         "  calll %4\n"                  // call __end_thread(thread)
230         "  popl %%ebp\n"                // restore %ebp
231         "  retl\n"                      // restore pc
232         "1:\n"
233         : "+a"(data), "+c"(func), "+b"(thread), "+d"(cur)
234         : "m"(*(u8*)__end_thread)
235         : "esi", "edi", "cc", "memory");
236     return;
237
238 fail:
239     func(data);
240 }
241
242 void
243 wait_threads()
244 {
245     ASSERT32();
246     if (! CONFIG_THREADS)
247         return;
248     while (MainThread.next != &MainThread)
249         yield();
250 }
251
252
253 /****************************************************************
254  * String ops
255  ****************************************************************/
256
257 // Sum the bytes in the specified area.
258 u8
259 checksum_far(u16 buf_seg, void *buf_far, u32 len)
260 {
261     SET_SEG(ES, buf_seg);
262     u32 i;
263     u8 sum = 0;
264     for (i=0; i<len; i++)
265         sum += GET_VAR(ES, ((u8*)buf_far)[i]);
266     return sum;
267 }
268
269 u8
270 checksum(void *buf, u32 len)
271 {
272     return checksum_far(GET_SEG(SS), buf, len);
273 }
274
275 size_t
276 strlen(const char *s)
277 {
278     if (__builtin_constant_p(s))
279         return __builtin_strlen(s);
280     const char *p = s;
281     while (*p)
282         p++;
283     return p-s;
284 }
285
286 // Compare two areas of memory.
287 int
288 memcmp(const void *s1, const void *s2, size_t n)
289 {
290     while (n) {
291         if (*(u8*)s1 != *(u8*)s2)
292             return *(u8*)s1 < *(u8*)s2 ? -1 : 1;
293         s1++;
294         s2++;
295         n--;
296     }
297     return 0;
298 }
299
300 // Compare two strings.
301 int
302 strcmp(const char *s1, const char *s2)
303 {
304     for (;;) {
305         if (*s1 != *s2)
306             return *s1 < *s2 ? -1 : 1;
307         if (! *s1)
308             return 0;
309         s1++;
310         s2++;
311     }
312 }
313
314 inline void
315 memset_far(u16 d_seg, void *d_far, u8 c, size_t len)
316 {
317     SET_SEG(ES, d_seg);
318     asm volatile(
319         "rep stosb %%es:(%%di)"
320         : "+c"(len), "+D"(d_far)
321         : "a"(c)
322         : "cc", "memory");
323 }
324
325 inline void
326 memset16_far(u16 d_seg, void *d_far, u16 c, size_t len)
327 {
328     len /= 2;
329     SET_SEG(ES, d_seg);
330     asm volatile(
331         "rep stosw %%es:(%%di)"
332         : "+c"(len), "+D"(d_far)
333         : "a"(c)
334         : "cc", "memory");
335 }
336
337 void *
338 memset(void *s, int c, size_t n)
339 {
340     while (n)
341         ((char *)s)[--n] = c;
342     return s;
343 }
344
345 inline void
346 memcpy_far(u16 d_seg, void *d_far, u16 s_seg, const void *s_far, size_t len)
347 {
348     SET_SEG(ES, d_seg);
349     u16 bkup_ds;
350     asm volatile(
351         "movw %%ds, %w0\n"
352         "movw %w4, %%ds\n"
353         "rep movsb (%%si),%%es:(%%di)\n"
354         "movw %w0, %%ds"
355         : "=&r"(bkup_ds), "+c"(len), "+S"(s_far), "+D"(d_far)
356         : "r"(s_seg)
357         : "cc", "memory");
358 }
359
360 void *
361 #undef memcpy
362 memcpy(void *d1, const void *s1, size_t len)
363 #if MODE16 == 0
364 #define memcpy __builtin_memcpy
365 #endif
366 {
367     SET_SEG(ES, GET_SEG(SS));
368     void *d = d1;
369     if (((u32)d1 | (u32)s1 | len) & 3) {
370         // non-aligned memcpy
371         asm volatile(
372             "rep movsb (%%esi),%%es:(%%edi)"
373             : "+c"(len), "+S"(s1), "+D"(d)
374             : : "cc", "memory");
375         return d1;
376     }
377     // Common case - use 4-byte copy
378     len /= 4;
379     asm volatile(
380         "rep movsl (%%esi),%%es:(%%edi)"
381         : "+c"(len), "+S"(s1), "+D"(d)
382         : : "cc", "memory");
383     return d1;
384 }
385
386 // Copy from memory mapped IO.  IO mem is very slow, so yield
387 // periodically.  'len' must be 4 byte aligned.
388 void
389 iomemcpy(void *d, const void *s, u32 len)
390 {
391     yield();
392     while (len) {
393         u32 copylen = len;
394         if (copylen > 1024)
395             copylen = 1024;
396         len -= copylen;
397         copylen /= 4;
398         asm volatile(
399             "rep movsl (%%esi),%%es:(%%edi)"
400             : "+c"(copylen), "+S"(s), "+D"(d)
401             : : "cc", "memory");
402         yield();
403     }
404 }
405
406 void *
407 memmove(void *d, const void *s, size_t len)
408 {
409     if (s >= d)
410         return memcpy(d, s, len);
411
412     d += len-1;
413     s += len-1;
414     while (len--) {
415         *(char*)d = *(char*)s;
416         d--;
417         s--;
418     }
419
420     return d;
421 }
422
423 // Copy a string - truncating it if necessary.
424 char *
425 strtcpy(char *dest, const char *src, size_t len)
426 {
427     char *d = dest;
428     while (len-- && *src != '\0')
429         *d++ = *src++;
430     *d = '\0';
431     return dest;
432 }
433
434
435 /****************************************************************
436  * Keyboard calls
437  ****************************************************************/
438
439 // Wait for 'usec' microseconds using (with irqs enabled) using int 1586.
440 void
441 biosusleep(u32 usec)
442 {
443     struct bregs br;
444     memset(&br, 0, sizeof(br));
445     br.flags = F_IF;
446     br.ah = 0x86;
447     br.cx = usec >> 16;
448     br.dx = usec;
449     call16_int(0x15, &br);
450 }
451
452 // See if a keystroke is pending in the keyboard buffer.
453 static int
454 check_for_keystroke()
455 {
456     struct bregs br;
457     memset(&br, 0, sizeof(br));
458     br.flags = F_IF;
459     br.ah = 1;
460     call16_int(0x16, &br);
461     return !(br.flags & F_ZF);
462 }
463
464 // Return a keystroke - waiting forever if necessary.
465 static int
466 get_raw_keystroke()
467 {
468     struct bregs br;
469     memset(&br, 0, sizeof(br));
470     br.flags = F_IF;
471     call16_int(0x16, &br);
472     return br.ah;
473 }
474
475 // Read a keystroke - waiting up to 'msec' milliseconds.
476 int
477 get_keystroke(int msec)
478 {
479     for (;;) {
480         if (check_for_keystroke())
481             return get_raw_keystroke();
482         if (msec <= 0)
483             return -1;
484         biosusleep(50*1000);
485         msec -= 50;
486     }
487 }