blob: 408694844b852e2ae04cbb6c947541388394f669 [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'Connor10ad7992009-10-24 11:06:08 -040012
13/****************************************************************
14 * 16bit calls
15 ****************************************************************/
16
Kevin O'Connor9521e262008-07-04 13:04:29 -040017// Call a function with a specified register state. Note that on
18// return, the interrupt enable/disable flag may be altered.
19inline void
20call16(struct bregs *callregs)
21{
22 asm volatile(
Kevin O'Connor7ab798f2008-07-13 11:08:36 -040023#if MODE16 == 1
Kevin O'Connor9521e262008-07-04 13:04:29 -040024 "calll __call16\n"
Kevin O'Connorf094ba82009-04-13 19:30:27 -040025 "cli\n"
Kevin O'Connor5b199ac2009-05-06 23:23:01 -040026 "cld"
Kevin O'Connor9521e262008-07-04 13:04:29 -040027#else
Kevin O'Connor5b199ac2009-05-06 23:23:01 -040028 "calll __call16_from32"
Kevin O'Connor9521e262008-07-04 13:04:29 -040029#endif
30 : "+a" (callregs), "+m" (*callregs)
31 :
Kevin O'Connor9caf7862009-02-27 20:14:05 -050032 : "ebx", "ecx", "edx", "esi", "edi", "cc", "memory");
Kevin O'Connor9521e262008-07-04 13:04:29 -040033}
34
35inline void
Kevin O'Connor6e5b4a42008-12-06 13:03:52 -050036call16big(struct bregs *callregs)
37{
Kevin O'Connor0b60a062009-06-15 23:03:05 -040038 ASSERT32();
Kevin O'Connor6e5b4a42008-12-06 13:03:52 -050039 asm volatile(
Kevin O'Connor5b199ac2009-05-06 23:23:01 -040040 "calll __call16big_from32"
Kevin O'Connor6e5b4a42008-12-06 13:03:52 -050041 : "+a" (callregs), "+m" (*callregs)
42 :
Kevin O'Connor9caf7862009-02-27 20:14:05 -050043 : "ebx", "ecx", "edx", "esi", "edi", "cc", "memory");
Kevin O'Connor6e5b4a42008-12-06 13:03:52 -050044}
45
46inline void
Kevin O'Connor9521e262008-07-04 13:04:29 -040047__call16_int(struct bregs *callregs, u16 offset)
48{
Kevin O'Connor273e8ae2009-01-19 19:56:07 -050049 if (MODE16)
Kevin O'Connor9f985422009-09-09 11:34:39 -040050 callregs->code.seg = GET_SEG(CS);
Kevin O'Connor273e8ae2009-01-19 19:56:07 -050051 else
Kevin O'Connor9f985422009-09-09 11:34:39 -040052 callregs->code.seg = SEG_BIOS;
53 callregs->code.offset = offset;
Kevin O'Connor9521e262008-07-04 13:04:29 -040054 call16(callregs);
55}
Kevin O'Connora4d35762008-03-08 15:43:03 -050056
Kevin O'Connor7f343092009-01-01 18:31:11 -050057// Switch to the extra stack in ebda and call a function.
58inline u32
59stack_hop(u32 eax, u32 edx, u32 ecx, void *func)
60{
Kevin O'Connor0b60a062009-06-15 23:03:05 -040061 ASSERT16();
Kevin O'Connorfaab1b32009-01-19 13:29:36 -050062 u16 ebda_seg = get_ebda_seg(), bkup_ss;
63 u32 bkup_esp;
Kevin O'Connor7f343092009-01-01 18:31:11 -050064 asm volatile(
Kevin O'Connorfaab1b32009-01-19 13:29:36 -050065 // Backup current %ss/%esp values.
66 "movw %%ss, %w3\n"
67 "movl %%esp, %4\n"
68 // Copy ebda seg to %ds/%ss and set %esp
69 "movw %w6, %%ds\n"
70 "movw %w6, %%ss\n"
Kevin O'Connor7f343092009-01-01 18:31:11 -050071 "movl %5, %%esp\n"
72 // Call func
Kevin O'Connorfaab1b32009-01-19 13:29:36 -050073 "calll %7\n"
Kevin O'Connor7f343092009-01-01 18:31:11 -050074 // Restore segments and stack
Kevin O'Connorfaab1b32009-01-19 13:29:36 -050075 "movw %w3, %%ds\n"
76 "movw %w3, %%ss\n"
Kevin O'Connor5b199ac2009-05-06 23:23:01 -040077 "movl %4, %%esp"
Kevin O'Connorfaab1b32009-01-19 13:29:36 -050078 : "+a" (eax), "+d" (edx), "+c" (ecx), "=&r" (bkup_ss), "=&r" (bkup_esp)
79 : "i" (EBDA_OFFSET_TOP_STACK), "r" (ebda_seg), "m" (*(u8*)func)
Kevin O'Connor7f343092009-01-01 18:31:11 -050080 : "cc", "memory");
81 return eax;
82}
83
Kevin O'Connor10ad7992009-10-24 11:06:08 -040084// 16bit trampoline for enabling irqs from 32bit mode.
85ASM16(
86 " .global trampoline_yield\n"
87 "trampoline_yield:\n"
88 " rep ; nop\n"
89 " lretw"
90 );
91
92// Briefly permit irqs to occur.
93void
94yield()
95{
96 if (MODE16) {
97 asm volatile(
98 "sti\n"
99 "nop\n"
100 "rep ; nop\n"
101 "cli\n"
102 "cld\n"
103 : : :"memory");
104 return;
105 }
106 extern void trampoline_yield();
107 struct bregs br;
108 br.flags = F_IF;
109 br.code.seg = SEG_BIOS;
110 br.code.offset = (u32)&trampoline_yield;
111 call16big(&br);
112}
113
114
115/****************************************************************
116 * String ops
117 ****************************************************************/
118
Kevin O'Connor2e7ab8b2008-03-29 14:29:35 -0400119// Sum the bytes in the specified area.
120u8
Kevin O'Connor94fd47e2009-02-15 15:21:10 -0500121checksum_far(u16 buf_seg, void *buf_far, u32 len)
Kevin O'Connor2e7ab8b2008-03-29 14:29:35 -0400122{
Kevin O'Connor8b267cb2009-01-19 19:25:21 -0500123 SET_SEG(ES, buf_seg);
Kevin O'Connor2e7ab8b2008-03-29 14:29:35 -0400124 u32 i;
125 u8 sum = 0;
126 for (i=0; i<len; i++)
Kevin O'Connor94fd47e2009-02-15 15:21:10 -0500127 sum += GET_VAR(ES, ((u8*)buf_far)[i]);
Kevin O'Connor2e7ab8b2008-03-29 14:29:35 -0400128 return sum;
129}
130
Kevin O'Connor8b267cb2009-01-19 19:25:21 -0500131u8
Kevin O'Connor94fd47e2009-02-15 15:21:10 -0500132checksum(void *buf, u32 len)
Kevin O'Connor8b267cb2009-01-19 19:25:21 -0500133{
134 return checksum_far(GET_SEG(SS), buf, len);
135}
136
Kevin O'Connor67823442009-04-13 14:14:51 -0400137size_t
138strlen(const char *s)
139{
140 if (__builtin_constant_p(s))
141 return __builtin_strlen(s);
142 const char *p = s;
143 while (*p)
144 p++;
145 return p-s;
146}
147
148// Compare two areas of memory.
149int
Kevin O'Connor38d1a342009-04-18 16:59:47 -0400150memcmp(const void *s1, const void *s2, size_t n)
Kevin O'Connor67823442009-04-13 14:14:51 -0400151{
152 while (n) {
153 if (*(u8*)s1 != *(u8*)s2)
Kevin O'Connor38d1a342009-04-18 16:59:47 -0400154 return *(u8*)s1 < *(u8*)s2 ? -1 : 1;
Kevin O'Connor67823442009-04-13 14:14:51 -0400155 s1++;
156 s2++;
157 n--;
158 }
Kevin O'Connor38d1a342009-04-18 16:59:47 -0400159 return 0;
Kevin O'Connor67823442009-04-13 14:14:51 -0400160}
161
Kevin O'Connor4c0c85a2009-04-11 23:31:29 -0400162// Compare two strings.
163int
Kevin O'Connor38d1a342009-04-18 16:59:47 -0400164strcmp(const char *s1, const char *s2)
Kevin O'Connor4c0c85a2009-04-11 23:31:29 -0400165{
166 for (;;) {
167 if (*s1 != *s2)
Kevin O'Connor38d1a342009-04-18 16:59:47 -0400168 return *s1 < *s2 ? -1 : 1;
Kevin O'Connor4c0c85a2009-04-11 23:31:29 -0400169 if (! *s1)
Kevin O'Connor38d1a342009-04-18 16:59:47 -0400170 return 0;
Kevin O'Connor4c0c85a2009-04-11 23:31:29 -0400171 s1++;
172 s2++;
173 }
174}
175
Kevin O'Connor5b199ac2009-05-06 23:23:01 -0400176inline void
177memset_far(u16 d_seg, void *d_far, u8 c, size_t len)
178{
179 SET_SEG(ES, d_seg);
180 asm volatile(
181 "rep stosb %%es:(%%di)"
182 : "+c"(len), "+D"(d_far)
183 : "a"(c)
184 : "cc", "memory");
185}
186
187inline void
188memset16_far(u16 d_seg, void *d_far, u16 c, size_t len)
189{
190 len /= 2;
191 SET_SEG(ES, d_seg);
192 asm volatile(
193 "rep stosw %%es:(%%di)"
194 : "+c"(len), "+D"(d_far)
195 : "a"(c)
196 : "cc", "memory");
197}
198
Kevin O'Connor5e4235f2008-04-12 09:00:04 -0400199void *
Kevin O'Connor567e4e32008-04-05 11:37:51 -0400200memset(void *s, int c, size_t n)
201{
202 while (n)
203 ((char *)s)[--n] = c;
Kevin O'Connor5e4235f2008-04-12 09:00:04 -0400204 return s;
Kevin O'Connor567e4e32008-04-05 11:37:51 -0400205}
206
Kevin O'Connor8b267cb2009-01-19 19:25:21 -0500207inline void
208memcpy_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 -0400209{
Kevin O'Connor8b267cb2009-01-19 19:25:21 -0500210 SET_SEG(ES, d_seg);
211 u16 bkup_ds;
212 asm volatile(
213 "movw %%ds, %w0\n"
214 "movw %w4, %%ds\n"
215 "rep movsb (%%si),%%es:(%%di)\n"
Kevin O'Connor5b199ac2009-05-06 23:23:01 -0400216 "movw %w0, %%ds"
Kevin O'Connor8b267cb2009-01-19 19:25:21 -0500217 : "=&r"(bkup_ds), "+c"(len), "+S"(s_far), "+D"(d_far)
218 : "r"(s_seg)
219 : "cc", "memory");
Kevin O'Connor567e4e32008-04-05 11:37:51 -0400220}
221
Kevin O'Connor942d4952009-06-10 22:44:06 -0400222void *
223#undef memcpy
224memcpy(void *d1, const void *s1, size_t len)
Kevin O'Connor5a1d0fc2009-06-15 23:35:30 -0400225#if MODE16 == 0
Kevin O'Connor942d4952009-06-10 22:44:06 -0400226#define memcpy __builtin_memcpy
Kevin O'Connor5a1d0fc2009-06-15 23:35:30 -0400227#endif
Kevin O'Connor18b927e2008-08-29 21:14:36 -0400228{
Kevin O'Connor5a1d0fc2009-06-15 23:35:30 -0400229 SET_SEG(ES, GET_SEG(SS));
Kevin O'Connor942d4952009-06-10 22:44:06 -0400230 void *d = d1;
231 if (((u32)d1 | (u32)s1 | len) & 3) {
232 // non-aligned memcpy
233 asm volatile(
234 "rep movsb (%%esi),%%es:(%%edi)"
235 : "+c"(len), "+S"(s1), "+D"(d)
236 : : "cc", "memory");
237 return d1;
238 }
239 // Common case - use 4-byte copy
Kevin O'Connor5d7b3f62009-04-19 20:05:50 -0400240 len /= 4;
241 asm volatile(
Kevin O'Connor5b199ac2009-05-06 23:23:01 -0400242 "rep movsl (%%esi),%%es:(%%edi)"
Kevin O'Connor942d4952009-06-10 22:44:06 -0400243 : "+c"(len), "+S"(s1), "+D"(d)
Kevin O'Connor5d7b3f62009-04-19 20:05:50 -0400244 : : "cc", "memory");
Kevin O'Connor942d4952009-06-10 22:44:06 -0400245 return d1;
Kevin O'Connor18b927e2008-08-29 21:14:36 -0400246}
247
248void *
Kevin O'Connorc7812932008-06-08 23:08:12 -0400249memmove(void *d, const void *s, size_t len)
250{
251 if (s >= d)
252 return memcpy(d, s, len);
253
254 d += len-1;
255 s += len-1;
256 while (len--) {
257 *(char*)d = *(char*)s;
258 d--;
259 s--;
260 }
261
262 return d;
263}
Kevin O'Connor9f4e1d92009-02-08 15:44:08 -0500264
Kevin O'Connor71f036d2009-02-08 16:57:22 -0500265// Copy a string - truncating it if necessary.
266char *
267strtcpy(char *dest, const char *src, size_t len)
268{
269 char *d = dest;
270 while (len-- && *src != '\0')
271 *d++ = *src++;
272 *d = '\0';
273 return dest;
274}
275
Kevin O'Connor10ad7992009-10-24 11:06:08 -0400276
277/****************************************************************
278 * Keyboard calls
279 ****************************************************************/
280
281// Wait for 'usec' microseconds using (with irqs enabled) using int 1586.
Kevin O'Connor5ca4b952009-02-15 13:45:48 -0500282void
Kevin O'Connor10ad7992009-10-24 11:06:08 -0400283biosusleep(u32 usec)
Kevin O'Connor9f4e1d92009-02-08 15:44:08 -0500284{
285 struct bregs br;
286 memset(&br, 0, sizeof(br));
Kevin O'Connorf8e800d2009-09-24 21:01:16 -0400287 br.flags = F_IF;
Kevin O'Connor9f4e1d92009-02-08 15:44:08 -0500288 br.ah = 0x86;
289 br.cx = usec >> 16;
290 br.dx = usec;
291 call16_int(0x15, &br);
292}
293
294// See if a keystroke is pending in the keyboard buffer.
295static int
296check_for_keystroke()
297{
298 struct bregs br;
299 memset(&br, 0, sizeof(br));
Kevin O'Connorf8e800d2009-09-24 21:01:16 -0400300 br.flags = F_IF;
Kevin O'Connor9f4e1d92009-02-08 15:44:08 -0500301 br.ah = 1;
302 call16_int(0x16, &br);
303 return !(br.flags & F_ZF);
304}
305
306// Return a keystroke - waiting forever if necessary.
307static int
308get_raw_keystroke()
309{
310 struct bregs br;
311 memset(&br, 0, sizeof(br));
Kevin O'Connorf8e800d2009-09-24 21:01:16 -0400312 br.flags = F_IF;
Kevin O'Connor9f4e1d92009-02-08 15:44:08 -0500313 call16_int(0x16, &br);
314 return br.ah;
315}
316
317// Read a keystroke - waiting up to 'msec' milliseconds.
318int
319get_keystroke(int msec)
320{
321 for (;;) {
322 if (check_for_keystroke())
323 return get_raw_keystroke();
324 if (msec <= 0)
325 return -1;
Kevin O'Connor10ad7992009-10-24 11:06:08 -0400326 biosusleep(50*1000);
Kevin O'Connor9f4e1d92009-02-08 15:44:08 -0500327 msec -= 50;
328 }
329}