Switch to new stack when calling ATA function in 16bit mode.
[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 GPLv3 license.
6
7 #include "util.h" // usleep
8 #include "bregs.h" // struct bregs
9 #include "config.h" // SEG_BIOS
10 #include "farptr.h" // GET_FARPTR
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 // Switch to the extra stack in ebda and call a function.
52 inline u32
53 stack_hop(u32 eax, u32 edx, u32 ecx, void *func)
54 {
55     extern void __force_link_error__stack_hop_only_in_16bit_mode();
56     if (!MODE16)
57         __force_link_error__stack_hop_only_in_16bit_mode();
58
59     u32 ebda_seg = get_ebda_seg();
60     u32 tmp;
61     asm volatile(
62         // Backup current %ss value.
63         "movl %%ss, %4\n"
64         // Copy ebda seg to %ss and %ds
65         "movl %3, %%ss\n"
66         "movl %3, %%ds\n"
67         // Backup %esp and set it to new value
68         "movl %%esp, %3\n"
69         "movl %5, %%esp\n"
70         // Call func
71         "calll %6\n"
72         // Restore segments and stack
73         "movl %3, %%esp\n"
74         "movl %4, %%ss\n"
75         "movl %4, %%ds\n"
76         : "+a" (eax), "+d" (edx), "+c" (ecx), "+r" (ebda_seg), "=r" (tmp)
77         : "i" (EBDA_OFFSET_TOP_STACK), "m" (*(u8*)func)
78         : "cc", "memory");
79     return eax;
80 }
81
82 // Sum the bytes in the specified area.
83 u8
84 checksum(u8 *far_data, u32 len)
85 {
86     u32 i;
87     u8 sum = 0;
88     for (i=0; i<len; i++)
89         sum += GET_FARPTR(far_data[i]);
90     return sum;
91 }
92
93 void *
94 memset(void *s, int c, size_t n)
95 {
96     while (n)
97         ((char *)s)[--n] = c;
98     return s;
99 }
100
101 void *
102 memcpy_far(void *far_d1, const void *far_s1, size_t len)
103 {
104     u8 *d = far_d1;
105     u8 *s = (u8*)far_s1;
106
107     while (len--) {
108         SET_FARPTR(*d, GET_FARPTR(*s));
109         d++;
110         s++;
111     }
112
113     return far_d1;
114 }
115
116 void *
117 memcpy(void *d1, const void *s1, size_t len)
118 {
119     u8 *d = (u8*)d1, *s = (u8*)s1;
120     while (len--)
121         *d++ = *s++;
122     return d1;
123 }
124
125 void *
126 memmove(void *d, const void *s, size_t len)
127 {
128     if (s >= d)
129         return memcpy(d, s, len);
130
131     d += len-1;
132     s += len-1;
133     while (len--) {
134         *(char*)d = *(char*)s;
135         d--;
136         s--;
137     }
138
139     return d;
140 }