blob: b1c116b044cee91eb9c6547da4978ebcfecb706a [file] [log] [blame]
Kevin O'Connor9521e262008-07-04 13:04:29 -04001// Misc utility functions.
2//
Kevin O'Connor5b199ac2009-05-06 23:23:01 -04003// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net>
Kevin O'Connor9521e262008-07-04 13:04:29 -04004//
Kevin O'Connorb1b7c2a2009-01-15 20:52:58 -05005// This file may be distributed under the terms of the GNU LGPLv3 license.
Kevin O'Connor9521e262008-07-04 13:04:29 -04006
Kevin O'Connor273e8ae2009-01-19 19:56:07 -05007#include "util.h" // call16
Kevin O'Connor9521e262008-07-04 13:04:29 -04008#include "bregs.h" // struct bregs
Kevin O'Connor35ae7262009-01-19 15:44:44 -05009#include "farptr.h" // GET_FLATPTR
Kevin O'Connor7f343092009-01-01 18:31:11 -050010#include "biosvar.h" // get_ebda_seg
Kevin O'Connor9521e262008-07-04 13:04:29 -040011
Kevin O'Connora5826b52009-10-24 17:57:29 -040012static inline u32 getesp() {
13 u32 esp;
14 asm("movl %%esp, %0" : "=rm"(esp));
15 return esp;
16}
17
Kevin O'Connor10ad7992009-10-24 11:06:08 -040018
19/****************************************************************
20 * 16bit calls
21 ****************************************************************/
22
Kevin O'Connor9521e262008-07-04 13:04:29 -040023// Call a function with a specified register state. Note that on
24// return, the interrupt enable/disable flag may be altered.
25inline void
26call16(struct bregs *callregs)
27{
Kevin O'Connora5826b52009-10-24 17:57:29 -040028 if (!MODE16 && getesp() > BUILD_STACK_ADDR)
29 panic("call16 with invalid stack\n");
Kevin O'Connor9521e262008-07-04 13:04:29 -040030 asm volatile(
Kevin O'Connor7ab798f2008-07-13 11:08:36 -040031#if MODE16 == 1
Kevin O'Connor9521e262008-07-04 13:04:29 -040032 "calll __call16\n"
Kevin O'Connorf094ba82009-04-13 19:30:27 -040033 "cli\n"
Kevin O'Connor5b199ac2009-05-06 23:23:01 -040034 "cld"
Kevin O'Connor9521e262008-07-04 13:04:29 -040035#else
Kevin O'Connor5b199ac2009-05-06 23:23:01 -040036 "calll __call16_from32"
Kevin O'Connor9521e262008-07-04 13:04:29 -040037#endif
38 : "+a" (callregs), "+m" (*callregs)
39 :
Kevin O'Connor9caf7862009-02-27 20:14:05 -050040 : "ebx", "ecx", "edx", "esi", "edi", "cc", "memory");
Kevin O'Connor9521e262008-07-04 13:04:29 -040041}
42
43inline void
Kevin O'Connor6e5b4a42008-12-06 13:03:52 -050044call16big(struct bregs *callregs)
45{
Kevin O'Connor0b60a062009-06-15 23:03:05 -040046 ASSERT32();
Kevin O'Connora5826b52009-10-24 17:57:29 -040047 if (getesp() > BUILD_STACK_ADDR)
48 panic("call16 with invalid stack\n");
Kevin O'Connor6e5b4a42008-12-06 13:03:52 -050049 asm volatile(
Kevin O'Connor5b199ac2009-05-06 23:23:01 -040050 "calll __call16big_from32"
Kevin O'Connor6e5b4a42008-12-06 13:03:52 -050051 : "+a" (callregs), "+m" (*callregs)
52 :
Kevin O'Connor9caf7862009-02-27 20:14:05 -050053 : "ebx", "ecx", "edx", "esi", "edi", "cc", "memory");
Kevin O'Connor6e5b4a42008-12-06 13:03:52 -050054}
55
56inline void
Kevin O'Connor9521e262008-07-04 13:04:29 -040057__call16_int(struct bregs *callregs, u16 offset)
58{
Kevin O'Connor273e8ae2009-01-19 19:56:07 -050059 if (MODE16)
Kevin O'Connor9f985422009-09-09 11:34:39 -040060 callregs->code.seg = GET_SEG(CS);
Kevin O'Connor273e8ae2009-01-19 19:56:07 -050061 else
Kevin O'Connor9f985422009-09-09 11:34:39 -040062 callregs->code.seg = SEG_BIOS;
63 callregs->code.offset = offset;
Kevin O'Connor9521e262008-07-04 13:04:29 -040064 call16(callregs);
65}
Kevin O'Connora4d35762008-03-08 15:43:03 -050066
Kevin O'Connor7f343092009-01-01 18:31:11 -050067// Switch to the extra stack in ebda and call a function.
68inline u32
69stack_hop(u32 eax, u32 edx, u32 ecx, void *func)
70{
Kevin O'Connor0b60a062009-06-15 23:03:05 -040071 ASSERT16();
Kevin O'Connorfaab1b32009-01-19 13:29:36 -050072 u16 ebda_seg = get_ebda_seg(), bkup_ss;
73 u32 bkup_esp;
Kevin O'Connor7f343092009-01-01 18:31:11 -050074 asm volatile(
Kevin O'Connorfaab1b32009-01-19 13:29:36 -050075 // 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"
Kevin O'Connor7f343092009-01-01 18:31:11 -050081 "movl %5, %%esp\n"
82 // Call func
Kevin O'Connorfaab1b32009-01-19 13:29:36 -050083 "calll %7\n"
Kevin O'Connor7f343092009-01-01 18:31:11 -050084 // Restore segments and stack
Kevin O'Connorfaab1b32009-01-19 13:29:36 -050085 "movw %w3, %%ds\n"
86 "movw %w3, %%ss\n"
Kevin O'Connor5b199ac2009-05-06 23:23:01 -040087 "movl %4, %%esp"
Kevin O'Connorfaab1b32009-01-19 13:29:36 -050088 : "+a" (eax), "+d" (edx), "+c" (ecx), "=&r" (bkup_ss), "=&r" (bkup_esp)
89 : "i" (EBDA_OFFSET_TOP_STACK), "r" (ebda_seg), "m" (*(u8*)func)
Kevin O'Connor7f343092009-01-01 18:31:11 -050090 : "cc", "memory");
91 return eax;
92}
93
Kevin O'Connor10ad7992009-10-24 11:06:08 -040094// 16bit trampoline for enabling irqs from 32bit mode.
95ASM16(
Kevin O'Connora5826b52009-10-24 17:57:29 -040096 " .global trampoline_checkirqs\n"
97 "trampoline_checkirqs:\n"
Kevin O'Connor10ad7992009-10-24 11:06:08 -040098 " rep ; nop\n"
99 " lretw"
100 );
101
Kevin O'Connora5826b52009-10-24 17:57:29 -0400102static void
103check_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
113static void
114check_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
132struct thread_info {
133 struct thread_info *next;
134 void *stackpos;
135};
136
Kevin O'Connore9a67bf2009-12-09 20:04:54 -0500137struct thread_info MainThread;
138
139void
140thread_setup()
141{
142 MainThread.next = &MainThread;
143 MainThread.stackpos = NULL;
144}
Kevin O'Connora5826b52009-10-24 17:57:29 -0400145
Kevin O'Connorc604f2f2009-10-24 19:56:11 -0400146struct thread_info *
Kevin O'Connora5826b52009-10-24 17:57:29 -0400147getCurThread()
148{
149 u32 esp = getesp();
150 if (esp <= BUILD_STACK_ADDR)
151 return &MainThread;
152 return (void*)ALIGN_DOWN(esp, THREADSTACKSIZE);
153}
154
Kevin O'Connor10ad7992009-10-24 11:06:08 -0400155// Briefly permit irqs to occur.
156void
157yield()
158{
159 if (MODE16) {
Kevin O'Connora5826b52009-10-24 17:57:29 -0400160 // In 16bit mode, just directly check irqs.
161 check_irqs16();
Kevin O'Connor10ad7992009-10-24 11:06:08 -0400162 return;
163 }
Kevin O'Connora5826b52009-10-24 17:57:29 -0400164 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).
189static 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);
Kevin O'Connorc604f2f2009-10-24 19:56:11 -0400197 dprintf(DEBUG_thread, "\\%08x/ End thread\n", (u32)old);
Kevin O'Connora5826b52009-10-24 17:57:29 -0400198}
199
200void
201run_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
Kevin O'Connorc604f2f2009-10-24 19:56:11 -0400216 dprintf(DEBUG_thread, "/%08x\\ Start thread\n", (u32)thread);
Kevin O'Connora5826b52009-10-24 17:57:29 -0400217 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
238fail:
239 func(data);
240}
241
242void
243wait_threads()
244{
245 ASSERT32();
246 if (! CONFIG_THREADS)
247 return;
248 while (MainThread.next != &MainThread)
249 yield();
Kevin O'Connor10ad7992009-10-24 11:06:08 -0400250}
251
252
253/****************************************************************
254 * String ops
255 ****************************************************************/
256
Kevin O'Connor2e7ab8b2008-03-29 14:29:35 -0400257// Sum the bytes in the specified area.
258u8
Kevin O'Connor94fd47e2009-02-15 15:21:10 -0500259checksum_far(u16 buf_seg, void *buf_far, u32 len)
Kevin O'Connor2e7ab8b2008-03-29 14:29:35 -0400260{
Kevin O'Connor8b267cb2009-01-19 19:25:21 -0500261 SET_SEG(ES, buf_seg);
Kevin O'Connor2e7ab8b2008-03-29 14:29:35 -0400262 u32 i;
263 u8 sum = 0;
264 for (i=0; i<len; i++)
Kevin O'Connor94fd47e2009-02-15 15:21:10 -0500265 sum += GET_VAR(ES, ((u8*)buf_far)[i]);
Kevin O'Connor2e7ab8b2008-03-29 14:29:35 -0400266 return sum;
267}
268
Kevin O'Connor8b267cb2009-01-19 19:25:21 -0500269u8
Kevin O'Connor94fd47e2009-02-15 15:21:10 -0500270checksum(void *buf, u32 len)
Kevin O'Connor8b267cb2009-01-19 19:25:21 -0500271{
272 return checksum_far(GET_SEG(SS), buf, len);
273}
274
Kevin O'Connor67823442009-04-13 14:14:51 -0400275size_t
276strlen(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.
287int
Kevin O'Connor38d1a342009-04-18 16:59:47 -0400288memcmp(const void *s1, const void *s2, size_t n)
Kevin O'Connor67823442009-04-13 14:14:51 -0400289{
290 while (n) {
291 if (*(u8*)s1 != *(u8*)s2)
Kevin O'Connor38d1a342009-04-18 16:59:47 -0400292 return *(u8*)s1 < *(u8*)s2 ? -1 : 1;
Kevin O'Connor67823442009-04-13 14:14:51 -0400293 s1++;
294 s2++;
295 n--;
296 }
Kevin O'Connor38d1a342009-04-18 16:59:47 -0400297 return 0;
Kevin O'Connor67823442009-04-13 14:14:51 -0400298}
299
Kevin O'Connor4c0c85a2009-04-11 23:31:29 -0400300// Compare two strings.
301int
Kevin O'Connor38d1a342009-04-18 16:59:47 -0400302strcmp(const char *s1, const char *s2)
Kevin O'Connor4c0c85a2009-04-11 23:31:29 -0400303{
304 for (;;) {
305 if (*s1 != *s2)
Kevin O'Connor38d1a342009-04-18 16:59:47 -0400306 return *s1 < *s2 ? -1 : 1;
Kevin O'Connor4c0c85a2009-04-11 23:31:29 -0400307 if (! *s1)
Kevin O'Connor38d1a342009-04-18 16:59:47 -0400308 return 0;
Kevin O'Connor4c0c85a2009-04-11 23:31:29 -0400309 s1++;
310 s2++;
311 }
312}
313
Kevin O'Connor5b199ac2009-05-06 23:23:01 -0400314inline void
315memset_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
325inline void
326memset16_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
Kevin O'Connor5e4235f2008-04-12 09:00:04 -0400337void *
Kevin O'Connor567e4e32008-04-05 11:37:51 -0400338memset(void *s, int c, size_t n)
339{
340 while (n)
341 ((char *)s)[--n] = c;
Kevin O'Connor5e4235f2008-04-12 09:00:04 -0400342 return s;
Kevin O'Connor567e4e32008-04-05 11:37:51 -0400343}
344
Kevin O'Connor8b267cb2009-01-19 19:25:21 -0500345inline void
346memcpy_far(u16 d_seg, void *d_far, u16 s_seg, const void *s_far, size_t len)
Kevin O'Connor567e4e32008-04-05 11:37:51 -0400347{
Kevin O'Connor8b267cb2009-01-19 19:25:21 -0500348 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"
Kevin O'Connor5b199ac2009-05-06 23:23:01 -0400354 "movw %w0, %%ds"
Kevin O'Connor8b267cb2009-01-19 19:25:21 -0500355 : "=&r"(bkup_ds), "+c"(len), "+S"(s_far), "+D"(d_far)
356 : "r"(s_seg)
357 : "cc", "memory");
Kevin O'Connor567e4e32008-04-05 11:37:51 -0400358}
359
Kevin O'Connor942d4952009-06-10 22:44:06 -0400360void *
361#undef memcpy
362memcpy(void *d1, const void *s1, size_t len)
Kevin O'Connor5a1d0fc2009-06-15 23:35:30 -0400363#if MODE16 == 0
Kevin O'Connor942d4952009-06-10 22:44:06 -0400364#define memcpy __builtin_memcpy
Kevin O'Connor5a1d0fc2009-06-15 23:35:30 -0400365#endif
Kevin O'Connor18b927e2008-08-29 21:14:36 -0400366{
Kevin O'Connor5a1d0fc2009-06-15 23:35:30 -0400367 SET_SEG(ES, GET_SEG(SS));
Kevin O'Connor942d4952009-06-10 22:44:06 -0400368 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
Kevin O'Connor5d7b3f62009-04-19 20:05:50 -0400378 len /= 4;
379 asm volatile(
Kevin O'Connor5b199ac2009-05-06 23:23:01 -0400380 "rep movsl (%%esi),%%es:(%%edi)"
Kevin O'Connor942d4952009-06-10 22:44:06 -0400381 : "+c"(len), "+S"(s1), "+D"(d)
Kevin O'Connor5d7b3f62009-04-19 20:05:50 -0400382 : : "cc", "memory");
Kevin O'Connor942d4952009-06-10 22:44:06 -0400383 return d1;
Kevin O'Connor18b927e2008-08-29 21:14:36 -0400384}
385
Kevin O'Connor34036962009-12-05 18:51:53 -0500386// Copy from memory mapped IO. IO mem is very slow, so yield
387// periodically. 'len' must be 4 byte aligned.
388void
389iomemcpy(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
Kevin O'Connor18b927e2008-08-29 21:14:36 -0400406void *
Kevin O'Connorc7812932008-06-08 23:08:12 -0400407memmove(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}
Kevin O'Connor9f4e1d92009-02-08 15:44:08 -0500422
Kevin O'Connor71f036d2009-02-08 16:57:22 -0500423// Copy a string - truncating it if necessary.
424char *
425strtcpy(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
Kevin O'Connor10ad7992009-10-24 11:06:08 -0400434
435/****************************************************************
436 * Keyboard calls
437 ****************************************************************/
438
439// Wait for 'usec' microseconds using (with irqs enabled) using int 1586.
Kevin O'Connor5ca4b952009-02-15 13:45:48 -0500440void
Kevin O'Connor10ad7992009-10-24 11:06:08 -0400441biosusleep(u32 usec)
Kevin O'Connor9f4e1d92009-02-08 15:44:08 -0500442{
443 struct bregs br;
444 memset(&br, 0, sizeof(br));
Kevin O'Connorf8e800d2009-09-24 21:01:16 -0400445 br.flags = F_IF;
Kevin O'Connor9f4e1d92009-02-08 15:44:08 -0500446 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.
453static int
454check_for_keystroke()
455{
456 struct bregs br;
457 memset(&br, 0, sizeof(br));
Kevin O'Connorf8e800d2009-09-24 21:01:16 -0400458 br.flags = F_IF;
Kevin O'Connor9f4e1d92009-02-08 15:44:08 -0500459 br.ah = 1;
460 call16_int(0x16, &br);
461 return !(br.flags & F_ZF);
462}
463
464// Return a keystroke - waiting forever if necessary.
465static int
466get_raw_keystroke()
467{
468 struct bregs br;
469 memset(&br, 0, sizeof(br));
Kevin O'Connorf8e800d2009-09-24 21:01:16 -0400470 br.flags = F_IF;
Kevin O'Connor9f4e1d92009-02-08 15:44:08 -0500471 call16_int(0x16, &br);
472 return br.ah;
473}
474
475// Read a keystroke - waiting up to 'msec' milliseconds.
476int
477get_keystroke(int msec)
478{
479 for (;;) {
480 if (check_for_keystroke())
481 return get_raw_keystroke();
482 if (msec <= 0)
483 return -1;
Kevin O'Connor10ad7992009-10-24 11:06:08 -0400484 biosusleep(50*1000);
Kevin O'Connor9f4e1d92009-02-08 15:44:08 -0500485 msec -= 50;
486 }
487}