blob: c09b8515306623a51ea439b1e167b61f51d07ec7 [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
12// Call a function with a specified register state. Note that on
13// return, the interrupt enable/disable flag may be altered.
14inline void
15call16(struct bregs *callregs)
16{
17 asm volatile(
Kevin O'Connor7ab798f2008-07-13 11:08:36 -040018#if MODE16 == 1
Kevin O'Connor9521e262008-07-04 13:04:29 -040019 "calll __call16\n"
Kevin O'Connorf094ba82009-04-13 19:30:27 -040020 "cli\n"
Kevin O'Connor5b199ac2009-05-06 23:23:01 -040021 "cld"
Kevin O'Connor9521e262008-07-04 13:04:29 -040022#else
Kevin O'Connor5b199ac2009-05-06 23:23:01 -040023 "calll __call16_from32"
Kevin O'Connor9521e262008-07-04 13:04:29 -040024#endif
25 : "+a" (callregs), "+m" (*callregs)
26 :
Kevin O'Connor9caf7862009-02-27 20:14:05 -050027 : "ebx", "ecx", "edx", "esi", "edi", "cc", "memory");
Kevin O'Connor9521e262008-07-04 13:04:29 -040028}
29
30inline void
Kevin O'Connor6e5b4a42008-12-06 13:03:52 -050031call16big(struct bregs *callregs)
32{
Kevin O'Connor0b60a062009-06-15 23:03:05 -040033 ASSERT32();
Kevin O'Connor6e5b4a42008-12-06 13:03:52 -050034 asm volatile(
Kevin O'Connor5b199ac2009-05-06 23:23:01 -040035 "calll __call16big_from32"
Kevin O'Connor6e5b4a42008-12-06 13:03:52 -050036 : "+a" (callregs), "+m" (*callregs)
37 :
Kevin O'Connor9caf7862009-02-27 20:14:05 -050038 : "ebx", "ecx", "edx", "esi", "edi", "cc", "memory");
Kevin O'Connor6e5b4a42008-12-06 13:03:52 -050039}
40
41inline void
Kevin O'Connor9521e262008-07-04 13:04:29 -040042__call16_int(struct bregs *callregs, u16 offset)
43{
Kevin O'Connor273e8ae2009-01-19 19:56:07 -050044 if (MODE16)
Kevin O'Connor9f985422009-09-09 11:34:39 -040045 callregs->code.seg = GET_SEG(CS);
Kevin O'Connor273e8ae2009-01-19 19:56:07 -050046 else
Kevin O'Connor9f985422009-09-09 11:34:39 -040047 callregs->code.seg = SEG_BIOS;
48 callregs->code.offset = offset;
Kevin O'Connor9521e262008-07-04 13:04:29 -040049 call16(callregs);
50}
Kevin O'Connora4d35762008-03-08 15:43:03 -050051
Kevin O'Connor7f343092009-01-01 18:31:11 -050052// Switch to the extra stack in ebda and call a function.
53inline u32
54stack_hop(u32 eax, u32 edx, u32 ecx, void *func)
55{
Kevin O'Connor0b60a062009-06-15 23:03:05 -040056 ASSERT16();
Kevin O'Connorfaab1b32009-01-19 13:29:36 -050057 u16 ebda_seg = get_ebda_seg(), bkup_ss;
58 u32 bkup_esp;
Kevin O'Connor7f343092009-01-01 18:31:11 -050059 asm volatile(
Kevin O'Connorfaab1b32009-01-19 13:29:36 -050060 // Backup current %ss/%esp values.
61 "movw %%ss, %w3\n"
62 "movl %%esp, %4\n"
63 // Copy ebda seg to %ds/%ss and set %esp
64 "movw %w6, %%ds\n"
65 "movw %w6, %%ss\n"
Kevin O'Connor7f343092009-01-01 18:31:11 -050066 "movl %5, %%esp\n"
67 // Call func
Kevin O'Connorfaab1b32009-01-19 13:29:36 -050068 "calll %7\n"
Kevin O'Connor7f343092009-01-01 18:31:11 -050069 // Restore segments and stack
Kevin O'Connorfaab1b32009-01-19 13:29:36 -050070 "movw %w3, %%ds\n"
71 "movw %w3, %%ss\n"
Kevin O'Connor5b199ac2009-05-06 23:23:01 -040072 "movl %4, %%esp"
Kevin O'Connorfaab1b32009-01-19 13:29:36 -050073 : "+a" (eax), "+d" (edx), "+c" (ecx), "=&r" (bkup_ss), "=&r" (bkup_esp)
74 : "i" (EBDA_OFFSET_TOP_STACK), "r" (ebda_seg), "m" (*(u8*)func)
Kevin O'Connor7f343092009-01-01 18:31:11 -050075 : "cc", "memory");
76 return eax;
77}
78
Kevin O'Connor2e7ab8b2008-03-29 14:29:35 -040079// Sum the bytes in the specified area.
80u8
Kevin O'Connor94fd47e2009-02-15 15:21:10 -050081checksum_far(u16 buf_seg, void *buf_far, u32 len)
Kevin O'Connor2e7ab8b2008-03-29 14:29:35 -040082{
Kevin O'Connor8b267cb2009-01-19 19:25:21 -050083 SET_SEG(ES, buf_seg);
Kevin O'Connor2e7ab8b2008-03-29 14:29:35 -040084 u32 i;
85 u8 sum = 0;
86 for (i=0; i<len; i++)
Kevin O'Connor94fd47e2009-02-15 15:21:10 -050087 sum += GET_VAR(ES, ((u8*)buf_far)[i]);
Kevin O'Connor2e7ab8b2008-03-29 14:29:35 -040088 return sum;
89}
90
Kevin O'Connor8b267cb2009-01-19 19:25:21 -050091u8
Kevin O'Connor94fd47e2009-02-15 15:21:10 -050092checksum(void *buf, u32 len)
Kevin O'Connor8b267cb2009-01-19 19:25:21 -050093{
94 return checksum_far(GET_SEG(SS), buf, len);
95}
96
Kevin O'Connor67823442009-04-13 14:14:51 -040097size_t
98strlen(const char *s)
99{
100 if (__builtin_constant_p(s))
101 return __builtin_strlen(s);
102 const char *p = s;
103 while (*p)
104 p++;
105 return p-s;
106}
107
108// Compare two areas of memory.
109int
Kevin O'Connor38d1a342009-04-18 16:59:47 -0400110memcmp(const void *s1, const void *s2, size_t n)
Kevin O'Connor67823442009-04-13 14:14:51 -0400111{
112 while (n) {
113 if (*(u8*)s1 != *(u8*)s2)
Kevin O'Connor38d1a342009-04-18 16:59:47 -0400114 return *(u8*)s1 < *(u8*)s2 ? -1 : 1;
Kevin O'Connor67823442009-04-13 14:14:51 -0400115 s1++;
116 s2++;
117 n--;
118 }
Kevin O'Connor38d1a342009-04-18 16:59:47 -0400119 return 0;
Kevin O'Connor67823442009-04-13 14:14:51 -0400120}
121
Kevin O'Connor4c0c85a2009-04-11 23:31:29 -0400122// Compare two strings.
123int
Kevin O'Connor38d1a342009-04-18 16:59:47 -0400124strcmp(const char *s1, const char *s2)
Kevin O'Connor4c0c85a2009-04-11 23:31:29 -0400125{
126 for (;;) {
127 if (*s1 != *s2)
Kevin O'Connor38d1a342009-04-18 16:59:47 -0400128 return *s1 < *s2 ? -1 : 1;
Kevin O'Connor4c0c85a2009-04-11 23:31:29 -0400129 if (! *s1)
Kevin O'Connor38d1a342009-04-18 16:59:47 -0400130 return 0;
Kevin O'Connor4c0c85a2009-04-11 23:31:29 -0400131 s1++;
132 s2++;
133 }
134}
135
Kevin O'Connor5b199ac2009-05-06 23:23:01 -0400136inline void
137memset_far(u16 d_seg, void *d_far, u8 c, size_t len)
138{
139 SET_SEG(ES, d_seg);
140 asm volatile(
141 "rep stosb %%es:(%%di)"
142 : "+c"(len), "+D"(d_far)
143 : "a"(c)
144 : "cc", "memory");
145}
146
147inline void
148memset16_far(u16 d_seg, void *d_far, u16 c, size_t len)
149{
150 len /= 2;
151 SET_SEG(ES, d_seg);
152 asm volatile(
153 "rep stosw %%es:(%%di)"
154 : "+c"(len), "+D"(d_far)
155 : "a"(c)
156 : "cc", "memory");
157}
158
Kevin O'Connor5e4235f2008-04-12 09:00:04 -0400159void *
Kevin O'Connor567e4e32008-04-05 11:37:51 -0400160memset(void *s, int c, size_t n)
161{
162 while (n)
163 ((char *)s)[--n] = c;
Kevin O'Connor5e4235f2008-04-12 09:00:04 -0400164 return s;
Kevin O'Connor567e4e32008-04-05 11:37:51 -0400165}
166
Kevin O'Connor8b267cb2009-01-19 19:25:21 -0500167inline void
168memcpy_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 -0400169{
Kevin O'Connor8b267cb2009-01-19 19:25:21 -0500170 SET_SEG(ES, d_seg);
171 u16 bkup_ds;
172 asm volatile(
173 "movw %%ds, %w0\n"
174 "movw %w4, %%ds\n"
175 "rep movsb (%%si),%%es:(%%di)\n"
Kevin O'Connor5b199ac2009-05-06 23:23:01 -0400176 "movw %w0, %%ds"
Kevin O'Connor8b267cb2009-01-19 19:25:21 -0500177 : "=&r"(bkup_ds), "+c"(len), "+S"(s_far), "+D"(d_far)
178 : "r"(s_seg)
179 : "cc", "memory");
Kevin O'Connor567e4e32008-04-05 11:37:51 -0400180}
181
Kevin O'Connor942d4952009-06-10 22:44:06 -0400182void *
183#undef memcpy
184memcpy(void *d1, const void *s1, size_t len)
Kevin O'Connor5a1d0fc2009-06-15 23:35:30 -0400185#if MODE16 == 0
Kevin O'Connor942d4952009-06-10 22:44:06 -0400186#define memcpy __builtin_memcpy
Kevin O'Connor5a1d0fc2009-06-15 23:35:30 -0400187#endif
Kevin O'Connor18b927e2008-08-29 21:14:36 -0400188{
Kevin O'Connor5a1d0fc2009-06-15 23:35:30 -0400189 SET_SEG(ES, GET_SEG(SS));
Kevin O'Connor942d4952009-06-10 22:44:06 -0400190 void *d = d1;
191 if (((u32)d1 | (u32)s1 | len) & 3) {
192 // non-aligned memcpy
193 asm volatile(
194 "rep movsb (%%esi),%%es:(%%edi)"
195 : "+c"(len), "+S"(s1), "+D"(d)
196 : : "cc", "memory");
197 return d1;
198 }
199 // Common case - use 4-byte copy
Kevin O'Connor5d7b3f62009-04-19 20:05:50 -0400200 len /= 4;
201 asm volatile(
Kevin O'Connor5b199ac2009-05-06 23:23:01 -0400202 "rep movsl (%%esi),%%es:(%%edi)"
Kevin O'Connor942d4952009-06-10 22:44:06 -0400203 : "+c"(len), "+S"(s1), "+D"(d)
Kevin O'Connor5d7b3f62009-04-19 20:05:50 -0400204 : : "cc", "memory");
Kevin O'Connor942d4952009-06-10 22:44:06 -0400205 return d1;
Kevin O'Connor18b927e2008-08-29 21:14:36 -0400206}
207
208void *
Kevin O'Connorc7812932008-06-08 23:08:12 -0400209memmove(void *d, const void *s, size_t len)
210{
211 if (s >= d)
212 return memcpy(d, s, len);
213
214 d += len-1;
215 s += len-1;
216 while (len--) {
217 *(char*)d = *(char*)s;
218 d--;
219 s--;
220 }
221
222 return d;
223}
Kevin O'Connor9f4e1d92009-02-08 15:44:08 -0500224
Kevin O'Connor71f036d2009-02-08 16:57:22 -0500225// Copy a string - truncating it if necessary.
226char *
227strtcpy(char *dest, const char *src, size_t len)
228{
229 char *d = dest;
230 while (len-- && *src != '\0')
231 *d++ = *src++;
232 *d = '\0';
233 return dest;
234}
235
Kevin O'Connor9f4e1d92009-02-08 15:44:08 -0500236// Wait for 'usec' microseconds with irqs enabled.
Kevin O'Connor5ca4b952009-02-15 13:45:48 -0500237void
Kevin O'Connor9f4e1d92009-02-08 15:44:08 -0500238usleep(u32 usec)
239{
240 struct bregs br;
241 memset(&br, 0, sizeof(br));
Kevin O'Connorf8e800d2009-09-24 21:01:16 -0400242 br.flags = F_IF;
Kevin O'Connor9f4e1d92009-02-08 15:44:08 -0500243 br.ah = 0x86;
244 br.cx = usec >> 16;
245 br.dx = usec;
246 call16_int(0x15, &br);
247}
248
249// See if a keystroke is pending in the keyboard buffer.
250static int
251check_for_keystroke()
252{
253 struct bregs br;
254 memset(&br, 0, sizeof(br));
Kevin O'Connorf8e800d2009-09-24 21:01:16 -0400255 br.flags = F_IF;
Kevin O'Connor9f4e1d92009-02-08 15:44:08 -0500256 br.ah = 1;
257 call16_int(0x16, &br);
258 return !(br.flags & F_ZF);
259}
260
261// Return a keystroke - waiting forever if necessary.
262static int
263get_raw_keystroke()
264{
265 struct bregs br;
266 memset(&br, 0, sizeof(br));
Kevin O'Connorf8e800d2009-09-24 21:01:16 -0400267 br.flags = F_IF;
Kevin O'Connor9f4e1d92009-02-08 15:44:08 -0500268 call16_int(0x16, &br);
269 return br.ah;
270}
271
272// Read a keystroke - waiting up to 'msec' milliseconds.
273int
274get_keystroke(int msec)
275{
276 for (;;) {
277 if (check_for_keystroke())
278 return get_raw_keystroke();
279 if (msec <= 0)
280 return -1;
281 usleep(50*1000);
282 msec -= 50;
283 }
284}