86117ccb6b8f4ff395f22d81c35d60004dbd71bf
[tpg/acess2.git] / Kernel / arch / x86 / lib.c
1 /*
2  * AcessOS Microkernel Version
3  * lib.c
4  */
5 #include <acess.h>
6 #include <threads.h>
7
8 extern int      GetCPUNum(void);
9
10 // === CODE ===
11 /**
12  * \brief Determine if a short spinlock is locked
13  * \param Lock  Lock pointer
14  */
15 int IS_LOCKED(struct sShortSpinlock *Lock)
16 {
17         return !!Lock->Lock;
18 }
19
20 /**
21  * \brief Check if the current CPU has the lock
22  * \param Lock  Lock pointer
23  */
24 int CPU_HAS_LOCK(struct sShortSpinlock *Lock)
25 {
26         #if STACKED_LOCKS == 1
27         return Lock->Lock == GetCPUNum() + 1;
28         #elif STACKED_LOCKS == 2
29         return Lock->Lock == Proc_GetCurThread();
30         #else
31         return 0;
32         #endif
33 }
34
35 /**
36  * \brief Acquire a Short Spinlock
37  * \param Lock  Lock pointer
38  * 
39  * This type of mutex should only be used for very short sections of code,
40  * or in places where a Mutex_* would be overkill, such as appending
41  * an element to linked list (usually two assignement lines in C)
42  * 
43  * \note This type of lock halts interrupts, so ensure that no timing
44  * functions are called while it is held. As a matter of fact, spend as
45  * little time as possible with this lock held
46  * \note If \a STACKED_LOCKS is set, this type of spinlock can be nested
47  */
48 void SHORTLOCK(struct sShortSpinlock *Lock)
49 {
50          int    v = 1;
51         #if LOCK_DISABLE_INTS
52          int    IF;
53         #endif
54         #if STACKED_LOCKS == 1
55          int    cpu = GetCPUNum() + 1;
56         #elif STACKED_LOCKS == 2
57         void    *thread = Proc_GetCurThread();
58         #endif
59         
60         #if LOCK_DISABLE_INTS
61         // Save interrupt state
62         __ASM__ ("pushf;\n\tpop %0" : "=r"(IF));
63         IF &= 0x200;    // AND out all but the interrupt flag
64         #endif
65         
66         #if STACKED_LOCKS == 1
67         if( Lock->Lock == cpu ) {
68                 Lock->Depth ++;
69                 return ;
70         }
71         #elif STACKED_LOCKS == 2
72         if( Lock->Lock == thread ) {
73                 Lock->Depth ++;
74                 return ;
75         }
76         #endif
77         
78         // Wait for another CPU to release
79         while(v) {
80                 // CMPXCHG:
81                 //  If r/m32 == EAX, set ZF and set r/m32 = r32
82                 //  Else, clear ZF and set EAX = r/m32
83                 #if STACKED_LOCKS == 1
84                 __ASM__("lock cmpxchgl %2, (%3)"
85                         : "=a"(v)
86                         : "a"(0), "r"(cpu), "r"(&Lock->Lock)
87                         );
88                 #elif STACKED_LOCKS == 2
89                 __ASM__("lock cmpxchgl %2, (%3)"
90                         : "=a"(v)
91                         : "a"(0), "r"(thread), "r"(&Lock->Lock)
92                         );
93                 #else
94                 __ASM__("xchgl %%eax, (%%edi)":"=a"(v):"a"(1),"D"(&Lock->Lock));
95                 #endif
96                 
97                 #if LOCK_DISABLE_INTS
98                 if( v ) __ASM__("sti"); // Re-enable interrupts
99                 #endif
100         }
101         
102         #if LOCK_DISABLE_INTS
103         __ASM__("cli");
104         Lock->IF = IF;
105         #endif
106 }
107 /**
108  * \brief Release a short lock
109  * \param Lock  Lock pointer
110  */
111 void SHORTREL(struct sShortSpinlock *Lock)
112 {
113         #if STACKED_LOCKS
114         if( Lock->Depth ) {
115                 Lock->Depth --;
116                 return ;
117         }
118         #endif
119         
120         #if LOCK_DISABLE_INTS
121         // Lock->IF can change anytime once Lock->Lock is zeroed
122         if(Lock->IF) {
123                 Lock->Lock = 0;
124                 __ASM__ ("sti");
125         }
126         else {
127                 Lock->Lock = 0;
128         }
129         #else
130         Lock->Lock = 0;
131         #endif
132 }
133
134 // === IO Commands ===
135 void outb(Uint16 Port, Uint8 Data)
136 {
137         __asm__ __volatile__ ("outb %%al, %%dx"::"d"(Port),"a"(Data));
138 }
139 void outw(Uint16 Port, Uint16 Data)
140 {
141         __asm__ __volatile__ ("outw %%ax, %%dx"::"d"(Port),"a"(Data));
142 }
143 void outd(Uint16 Port, Uint32 Data)
144 {
145         __asm__ __volatile__ ("outl %%eax, %%dx"::"d"(Port),"a"(Data));
146 }
147 Uint8 inb(Uint16 Port)
148 {
149         Uint8   ret;
150         __asm__ __volatile__ ("inb %%dx, %%al":"=a"(ret):"d"(Port));
151         return ret;
152 }
153 Uint16 inw(Uint16 Port)
154 {
155         Uint16  ret;
156         __asm__ __volatile__ ("inw %%dx, %%ax":"=a"(ret):"d"(Port));
157         return ret;
158 }
159 Uint32 ind(Uint16 Port)
160 {
161         Uint32  ret;
162         __asm__ __volatile__ ("inl %%dx, %%eax":"=a"(ret):"d"(Port));
163         return ret;
164 }
165
166 /**
167  * \fn void *memset(void *Dest, int Val, size_t Num)
168  * \brief Do a byte granuality set of Dest
169  */
170 void *memset(void *Dest, int Val, size_t Num)
171 {
172         Uint32  val = Val&0xFF;
173         val |= val << 8;
174         val |= val << 16;
175         __asm__ __volatile__ (
176                 "rep stosl;\n\t"
177                 "mov %3, %%ecx;\n\t"
178                 "rep stosb"
179                 :: "D" (Dest), "a" (val), "c" (Num/4), "r" (Num&3));
180         return Dest;
181 }
182 /**
183  * \brief Set double words
184  */
185 void *memsetd(void *Dest, Uint32 Val, size_t Num)
186 {
187         __asm__ __volatile__ ("rep stosl" :: "D" (Dest), "a" (Val), "c" (Num));
188         return Dest;
189 }
190
191 /**
192  * \fn int memcmp(const void *m1, const void *m2, size_t Num)
193  * \brief Compare two pieces of memory
194  */
195 int memcmp(const void *m1, const void *m2, size_t Num)
196 {
197         if( Num == 0 )  return 0;       // No bytes are always identical
198         
199         while(Num--)
200         {
201                 if(*(Uint8*)m1 != *(Uint8*)m2)
202                         return *(Uint8*)m1 - *(Uint8*)m2;
203                 m1 ++;
204                 m2 ++;
205         }
206         return 0;
207 }
208
209 /**
210  * \fn void *memcpy(void *Dest, const void *Src, size_t Num)
211  * \brief Copy \a Num bytes from \a Src to \a Dest
212  */
213 void *memcpy(void *Dest, const void *Src, size_t Num)
214 {
215         if( ((Uint)Dest & 3) || ((Uint)Src & 3) )
216                 __asm__ __volatile__ ("rep movsb" :: "D" (Dest), "S" (Src), "c" (Num));
217         else {
218                 __asm__ __volatile__ (
219                         "rep movsl;\n\t"
220                         "mov %3, %%ecx;\n\t"
221                         "rep movsb"
222                         :: "D" (Dest), "S" (Src), "c" (Num/4), "r" (Num&3));
223         }
224         return Dest;
225 }
226 /**
227  * \fn void *memcpyd(void *Dest, const void *Src, size_t Num)
228  * \brief Copy \a Num DWORDs from \a Src to \a Dest
229  */
230 void *memcpyd(void *Dest, const void *Src, size_t Num)
231 {
232         __asm__ __volatile__ ("rep movsl" :: "D" (Dest), "S" (Src), "c" (Num));
233         return Dest;
234 }
235
236 /**
237  * \fn Uint64 __udivdi3(Uint64 Num, Uint64 Den)
238  * \brief Divide two 64-bit integers
239  */
240 Uint64 __udivdi3(Uint64 Num, Uint64 Den)
241 {
242         Uint64  P[2];
243         Uint64  q = 0;
244          int    i;
245         
246         if(Den == 0)    __asm__ __volatile__ ("int $0x0");
247         // Common speedups
248         if(Num <= 0xFFFFFFFF && Den <= 0xFFFFFFFF)
249                 return (Uint32)Num / (Uint32)Den;
250         if(Den == 1)    return Num;
251         if(Den == 2)    return Num >> 1;        // Speed Hacks
252         if(Den == 4)    return Num >> 2;        // Speed Hacks
253         if(Den == 8)    return Num >> 3;        // Speed Hacks
254         if(Den == 16)   return Num >> 4;        // Speed Hacks
255         if(Den == 32)   return Num >> 5;        // Speed Hacks
256         if(Den == 1024) return Num >> 10;       // Speed Hacks
257         if(Den == 2048) return Num >> 11;       // Speed Hacks
258         if(Den == 4096) return Num >> 12;
259         if(Num < Den)   return 0;
260         if(Num < Den*2) return 1;
261         if(Num == Den*2)        return 2;
262         
263         // Restoring division, from wikipedia
264         // http://en.wikipedia.org/wiki/Division_(digital)
265         P[0] = Num;     P[1] = 0;
266         for( i = 64; i--; )
267         {
268                 // P <<= 1;
269                 P[1] = (P[1] << 1) | (P[0] >> 63);
270                 P[0] = P[0] << 1;
271                 
272                 // P -= Den << 64
273                 P[1] -= Den;
274                 
275                 // P >= 0
276                 if( !(P[1] & (1ULL<<63)) ) {
277                         q |= (Uint64)1 << (63-i);
278                 }
279                 else {
280                         //q |= 0 << (63-i);
281                         P[1] += Den;
282                 }
283         }
284         
285         return q;
286 }
287
288 /**
289  * \fn Uint64 __umoddi3(Uint64 Num, Uint64 Den)
290  * \brief Get the modulus of two 64-bit integers
291  */
292 Uint64 __umoddi3(Uint64 Num, Uint64 Den)
293 {
294         if(Den == 0)    __asm__ __volatile__ ("int $0x0");      // Call Div by Zero Error
295         if(Den == 1)    return 0;       // Speed Hacks
296         if(Den == 2)    return Num & 1; // Speed Hacks
297         if(Den == 4)    return Num & 3; // Speed Hacks
298         if(Den == 8)    return Num & 7; // Speed Hacks
299         if(Den == 16)   return Num & 15;        // Speed Hacks
300         if(Den == 32)   return Num & 31;        // Speed Hacks
301         if(Den == 1024) return Num & 1023;      // Speed Hacks
302         if(Den == 2048) return Num & 2047;      // Speed Hacks
303         if(Den == 4096) return Num & 4095;      // Speed Hacks
304         
305         if(Num >> 32 == 0 && Den >> 32 == 0)
306                 return (Uint32)Num % (Uint32)Den;
307         
308         return Num - __udivdi3(Num, Den) * Den;
309 }
310
311 Uint16 LittleEndian16(Uint16 Val)
312 {
313         return Val;
314 }
315 Uint16 BigEndian16(Uint16 Val)
316 {
317         return ((Val&0xFF)<<8) | ((Val>>8)&0xFF);
318 }
319 Uint32 LittleEndian32(Uint32 Val)
320 {
321         return Val;
322 }
323 Uint32 BigEndian32(Uint32 Val)
324 {
325         return ((Val&0xFF)<<24) | ((Val&0xFF00)<<8) | ((Val>>8)&0xFF00) | ((Val>>24)&0xFF);
326 }
327
328 // --- EXPORTS ---
329 EXPORT(memcpy); EXPORT(memset);
330 EXPORT(memcmp);
331 //EXPORT(memcpyw);      EXPORT(memsetw);
332 EXPORT(memcpyd);        EXPORT(memsetd);
333 EXPORT(inb);    EXPORT(inw);    EXPORT(ind);
334 EXPORT(outb);   EXPORT(outw);   EXPORT(outd);
335 EXPORT(__udivdi3);      EXPORT(__umoddi3);
336
337 EXPORT(LittleEndian16); EXPORT(BigEndian16);
338 EXPORT(LittleEndian32); EXPORT(BigEndian32);
339
340 EXPORT(SHORTLOCK);
341 EXPORT(SHORTREL);
342 EXPORT(IS_LOCKED);

UCC git Repository :: git.ucc.asn.au