Implement memcpy_far and checksum_far, and replace _fl variants.
[seabios.git] / src / util.c
1 // Misc utility functions.
2 //
3 // Copyright (C) 2008  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" // usleep
8 #include "bregs.h" // struct bregs
9 #include "config.h" // SEG_BIOS
10 #include "farptr.h" // GET_FLATPTR
11 #include "biosvar.h" // get_ebda_seg
12
13 // Call a function with a specified register state.  Note that on
14 // return, the interrupt enable/disable flag may be altered.
15 inline void
16 call16(struct bregs *callregs)
17 {
18     asm volatile(
19 #if MODE16 == 1
20         "calll __call16\n"
21 #else
22         "calll __call16_from32\n"
23 #endif
24         : "+a" (callregs), "+m" (*callregs)
25         :
26         : "ebx", "ecx", "edx", "esi", "edi", "ebp", "cc", "memory");
27 }
28
29 inline void
30 call16big(struct bregs *callregs)
31 {
32     extern void __force_link_error__call16big_only_in_32bit_mode();
33     if (MODE16)
34         __force_link_error__call16big_only_in_32bit_mode();
35
36     asm volatile(
37         "calll __call16big_from32\n"
38         : "+a" (callregs), "+m" (*callregs)
39         :
40         : "ebx", "ecx", "edx", "esi", "edi", "ebp", "cc", "memory");
41 }
42
43 inline void
44 __call16_int(struct bregs *callregs, u16 offset)
45 {
46     callregs->cs = SEG_BIOS;
47     callregs->ip = offset;
48     call16(callregs);
49 }
50
51 inline void
52 call16_simpint(int nr, u32 *eax, u32 *flags)
53 {
54     extern void __force_link_error__call16_simpint_only_in_16bit_mode();
55     if (!MODE16)
56         __force_link_error__call16_simpint_only_in_16bit_mode();
57
58     asm volatile(
59         "stc\n"
60         "int %2\n"
61         "pushfl\n"
62         "popl %1\n"
63         "cld\n"
64         "cli\n"
65         : "+a"(*eax), "=r"(*flags)
66         : "i"(nr)
67         : "cc", "memory");
68 }
69
70 // Switch to the extra stack in ebda and call a function.
71 inline u32
72 stack_hop(u32 eax, u32 edx, u32 ecx, void *func)
73 {
74     extern void __force_link_error__stack_hop_only_in_16bit_mode();
75     if (!MODE16)
76         __force_link_error__stack_hop_only_in_16bit_mode();
77
78     u16 ebda_seg = get_ebda_seg(), bkup_ss;
79     u32 bkup_esp;
80     asm volatile(
81         // Backup current %ss/%esp values.
82         "movw %%ss, %w3\n"
83         "movl %%esp, %4\n"
84         // Copy ebda seg to %ds/%ss and set %esp
85         "movw %w6, %%ds\n"
86         "movw %w6, %%ss\n"
87         "movl %5, %%esp\n"
88         // Call func
89         "calll %7\n"
90         // Restore segments and stack
91         "movw %w3, %%ds\n"
92         "movw %w3, %%ss\n"
93         "movl %4, %%esp\n"
94         : "+a" (eax), "+d" (edx), "+c" (ecx), "=&r" (bkup_ss), "=&r" (bkup_esp)
95         : "i" (EBDA_OFFSET_TOP_STACK), "r" (ebda_seg), "m" (*(u8*)func)
96         : "cc", "memory");
97     return eax;
98 }
99
100 // Sum the bytes in the specified area.
101 u8
102 checksum_far(u16 buf_seg, u8 *buf_far, u32 len)
103 {
104     SET_SEG(ES, buf_seg);
105     u32 i;
106     u8 sum = 0;
107     for (i=0; i<len; i++)
108         sum += GET_VAR(ES, buf_far[i]);
109     return sum;
110 }
111
112 u8
113 checksum(u8 *buf, u32 len)
114 {
115     return checksum_far(GET_SEG(SS), buf, len);
116 }
117
118 void *
119 memset(void *s, int c, size_t n)
120 {
121     while (n)
122         ((char *)s)[--n] = c;
123     return s;
124 }
125
126 inline void
127 memcpy_far(u16 d_seg, void *d_far, u16 s_seg, const void *s_far, size_t len)
128 {
129     SET_SEG(ES, d_seg);
130     u16 bkup_ds;
131     asm volatile(
132         "movw %%ds, %w0\n"
133         "movw %w4, %%ds\n"
134         "rep movsb (%%si),%%es:(%%di)\n"
135         "movw %w0, %%ds\n"
136         : "=&r"(bkup_ds), "+c"(len), "+S"(s_far), "+D"(d_far)
137         : "r"(s_seg)
138         : "cc", "memory");
139 }
140
141 void *
142 memcpy(void *d1, const void *s1, size_t len)
143 {
144     u8 *d = (u8*)d1, *s = (u8*)s1;
145     while (len--)
146         *d++ = *s++;
147     return d1;
148 }
149
150 void *
151 memmove(void *d, const void *s, size_t len)
152 {
153     if (s >= d)
154         return memcpy(d, s, len);
155
156     d += len-1;
157     s += len-1;
158     while (len--) {
159         *(char*)d = *(char*)s;
160         d--;
161         s--;
162     }
163
164     return d;
165 }